Files
quantconnect--lean/Tests/Python/PythonMemoryLeakTests.cs
T
2026-07-13 13:02:50 +08:00

110 lines
3.6 KiB
C#

/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using Python.Runtime;
using NUnit.Framework;
using System.Threading;
using QuantConnect.Orders;
using QuantConnect.Logging;
using QuantConnect.Securities;
using QuantConnect.Orders.Fees;
using QuantConnect.Tests.Common.Securities;
namespace QuantConnect.Tests.Python
{
[TestFixture]
public class PythonMemoryLeakTests
{
private Security _security;
private static DateTime orderDateTime;
[SetUp]
public void SetUp()
{
_security = SecurityTests.GetSecurity();
orderDateTime = new DateTime(2017, 2, 2, 13, 0, 0);
var reference = orderDateTime;
var referenceUtc = reference.ConvertToUtc(TimeZones.NewYork);
var timeKeeper = new TimeKeeper(referenceUtc);
_security.SetLocalTimeKeeper(timeKeeper.GetLocalTimeKeeper(TimeZones.NewYork));
}
[Test]
public void DoesNotLeak()
{
using (Py.GIL())
{
var module = PyModule.FromString(Guid.NewGuid().ToString(),
@"
from AlgorithmImports import *
def jose(security):
security.SetFeeModel(MakerTakerModel())
class MakerTakerModel(FeeModel):
def __init__(self, maker = -.0016, taker = .003):
self.maker = maker
self.taker = taker
def GetOrderFee(self, parameters: OrderFeeParameters) -> OrderFee:
qty = parameters.Order.Quantity
ord_type = parameters.Order.Type
# fee_in_usd = .0008
# make_ps = -.0016 #Rebate
# take_ps = .003
if ord_type in [OrderType.Market, OrderType.StopMarket]:
fee_usd = self.taker * qty
else:
fee_usd = self.maker * qty
return OrderFee(CashAmount(fee_usd, 'USD'))");
module.GetAttr("jose").Invoke(_security.ToPython());
var parameters = new OrderFeeParameters(_security, new MarketOrder(_security.Symbol, 1, orderDateTime));
// warmup
var result = _security.FeeModel.GetOrderFee(parameters);
Assert.IsNotNull(result);
// let the system stabilize
Thread.Sleep(1000);
var start = GC.GetTotalMemory(true);
for (var i = 0; i < 50000; i++)
{
result = _security.FeeModel.GetOrderFee(parameters);
Assert.IsNotNull(result);
if (i % 10000 == 0)
{
Log.Debug($"Memory: {GC.GetTotalMemory(true)}");
}
}
Thread.Sleep(1000);
var end = GC.GetTotalMemory(true);
var message = $"Start: {start}. End {end}. Variation {((end - start) / (decimal)start * 100).RoundToSignificantDigits(2)}%";
Log.Debug(message);
// 5% noise, leak was >10%
Assert.LessOrEqual(end, start * 1.05, message);
}
}
}
}