Files
2026-07-13 13:02:50 +08:00

214 lines
9.9 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 Moq;
using NUnit.Framework;
using QuantConnect.Brokerages;
using QuantConnect.Tests.Brokerages;
using QuantConnect.Orders;
using QuantConnect.Securities;
using QuantConnect.Orders.Fills;
using System.Collections.Generic;
using QuantConnect.Data.Market;
using Fasterflect;
using QuantConnect.Lean.Engine.TransactionHandlers;
using System;
using QuantConnect.Tests.Engine.BrokerageTransactionHandlerTests;
using QuantConnect.Brokerages.Backtesting;
using QuantConnect.Tests.Engine;
using System.Linq;
namespace QuantConnect.Tests.Common.Brokerages
{
[TestFixture, Parallelizable(ParallelScope.All)]
public class DefaultBrokerageModelTests
{
private readonly DefaultBrokerageModel _defaultBrokerageModel = new DefaultBrokerageModel();
[Test]
public void CanSubmitOrder_WhenMarketOnOpenOrderForFutures()
{
var order = GetMarketOnOpenOrder();
var future = TestsHelpers.GetSecurity(securityType: SecurityType.Future, symbol: Futures.Indices.SP500EMini, market: Market.CME);
var futureOption = TestsHelpers.GetSecurity(securityType: SecurityType.FutureOption, symbol: Futures.Indices.SP500EMini, market: Market.CME);
Assert.IsFalse(_defaultBrokerageModel.CanSubmitOrder(future, order, out _));
Assert.IsFalse(_defaultBrokerageModel.CanSubmitOrder(futureOption, order, out _));
}
[TestCase(SecurityType.Base)]
[TestCase(SecurityType.Equity)]
[TestCase(SecurityType.Option)]
[TestCase(SecurityType.Forex)]
[TestCase(SecurityType.Cfd)]
[TestCase(SecurityType.Crypto)]
[TestCase(SecurityType.Index)]
[TestCase(SecurityType.IndexOption)]
public void CanSubmitOrder_WhenMarketOnOpenOrderForOtherSecurityTypes(SecurityType securityType)
{
var order = GetMarketOnOpenOrder();
var security = TestsHelpers.GetSecurity(securityType: securityType, market: Market.USA);
Assert.IsTrue(_defaultBrokerageModel.CanSubmitOrder(security, order, out _));
}
[TestCase(SecurityType.Base, nameof(ImmediateFillModel))]
[TestCase(SecurityType.Equity, nameof(EquityFillModel))]
[TestCase(SecurityType.Option, nameof(ImmediateFillModel))]
[TestCase(SecurityType.Forex, nameof(ImmediateFillModel))]
[TestCase(SecurityType.Cfd, nameof(ImmediateFillModel))]
[TestCase(SecurityType.Crypto, nameof(ImmediateFillModel))]
[TestCase(SecurityType.Index, nameof(ImmediateFillModel))]
[TestCase(SecurityType.IndexOption, nameof(ImmediateFillModel))]
[TestCase(SecurityType.Future, nameof(FutureFillModel))]
[TestCase(SecurityType.FutureOption, nameof(FutureOptionFillModel))]
public void GetsCorrectFillModel(SecurityType securityType, string expectedFillModel)
{
var security = TestsHelpers.GetSecurity(securityType: securityType, market: Market.USA);
var fillModel = _defaultBrokerageModel.GetFillModel(security);
Assert.AreEqual(expectedFillModel, fillModel.GetType().Name);
}
[Test]
public void ApplySplitWorksAsExpected()
{
var orderTypes = new List<OrderType>()
{
OrderType.Limit,
OrderType.StopLimit,
OrderType.LimitIfTouched,
OrderType.TrailingStop
};
var algorithm = new BrokerageTransactionHandlerTests.TestAlgorithm
{
HistoryProvider = new BrokerageTransactionHandlerTests.EmptyHistoryProvider()
};
var transactionHandler = new BacktestingTransactionHandler();
using var backtestingBrokerage = new BacktestingBrokerage(algorithm);
transactionHandler.Initialize(algorithm, backtestingBrokerage, new TestResultHandler(Console.WriteLine));
try
{
algorithm.Transactions.SetOrderProcessor(transactionHandler);
algorithm.AddEquity("IBM");
var tickets = new List<OrderTicket>();
foreach (var type in orderTypes)
{
SubmitOrderRequest orderRequest = null;
switch (type)
{
case OrderType.Limit:
orderRequest = new SubmitOrderRequest(OrderType.Limit, SecurityType.Equity, Symbols.IBM, 100, 0, limitPrice: 8, 0,
DateTime.UtcNow, "");
break;
case OrderType.StopLimit:
orderRequest = new SubmitOrderRequest(OrderType.StopLimit, SecurityType.Equity, Symbols.IBM, 100, stopPrice: 10, 0, 0,
DateTime.UtcNow, "");
break;
case OrderType.LimitIfTouched:
orderRequest = new SubmitOrderRequest(OrderType.LimitIfTouched, SecurityType.Equity, Symbols.IBM, 100, 0, limitPrice: 14,
triggerPrice: 12, DateTime.UtcNow, "");
break;
case OrderType.TrailingStop:
orderRequest = new SubmitOrderRequest(OrderType.TrailingStop, SecurityType.Equity, Symbols.IBM, 100, stopPrice: 10, 0, 0,
trailingAmount: 0.5m, trailingAsPercentage: false, DateTime.UtcNow, "");
break;
}
algorithm.Transactions.AddOrder(orderRequest);
var ticket = new OrderTicket(algorithm.Transactions, orderRequest);
tickets.Add(ticket);
}
var split = new Split(Symbols.IBM, DateTime.UtcNow, 1, 0.5m, SplitType.SplitOccurred);
_defaultBrokerageModel.ApplySplit(tickets, split);
transactionHandler.ProcessSynchronousEvents();
foreach (var order in algorithm.Transactions.GetOrders())
{
Assert.AreEqual(200, order.Quantity);
var orderType = order.Type;
switch (orderType)
{
case OrderType.Limit:
Assert.AreEqual(4, order.GetPropertyValue("LimitPrice"));
break;
case OrderType.StopLimit:
Assert.AreEqual(5, order.GetPropertyValue("StopPrice"));
break;
case OrderType.LimitIfTouched:
Assert.AreEqual(6, order.GetPropertyValue("TriggerPrice"));
Assert.AreEqual(7, order.GetPropertyValue("LimitPrice"));
break;
case OrderType.TrailingStop:
Assert.AreEqual(5, order.GetPropertyValue("StopPrice"));
Assert.AreEqual(0.25m, order.GetPropertyValue("TrailingAmount"));
break;
}
}
}
finally
{
transactionHandler.Exit();
}
}
[Test]
public void AppliesSplitOnlyWhenTrailingStopOrderTrailingAmountIsNotPercentage([Values] bool trailingAsPercentage)
{
var algorithm = new BrokerageTransactionHandlerTests.TestAlgorithm
{
HistoryProvider = new BrokerageTransactionHandlerTests.EmptyHistoryProvider()
};
var transactionHandler = new BacktestingTransactionHandler();
using var backtestingBrokerage = new BacktestingBrokerage(algorithm);
transactionHandler.Initialize(algorithm, backtestingBrokerage, new TestResultHandler(Console.WriteLine));
try
{
algorithm.Transactions.SetOrderProcessor(transactionHandler);
algorithm.AddEquity("IBM");
var tickets = new List<OrderTicket>();
var orderTime = new DateTime(2023, 07, 21, 12, 0, 0);
var orderRequest = new SubmitOrderRequest(OrderType.TrailingStop, SecurityType.Equity, Symbols.IBM, 100, stopPrice: 10, 0, 0,
trailingAmount: 0.1m, trailingAsPercentage, orderTime, "");
algorithm.Transactions.AddOrder(orderRequest);
var ticket = new OrderTicket(algorithm.Transactions, orderRequest);
tickets.Add(ticket);
var split = new Split(Symbols.IBM, orderTime, 1, 0.5m, SplitType.SplitOccurred);
_defaultBrokerageModel.ApplySplit(tickets, split);
transactionHandler.ProcessSynchronousEvents();
var order = algorithm.Transactions.GetOrders().Single();
Assert.AreEqual(5, order.GetPropertyValue("StopPrice", Flags.Instance | Flags.Public));
Assert.AreEqual(trailingAsPercentage ? 0.1m : 0.05m, order.GetPropertyValue("TrailingAmount"));
}
finally
{
transactionHandler.Exit();
}
}
private static Order GetMarketOnOpenOrder()
{
var order = new Mock<Order>();
order.Setup(o => o.Type).Returns(OrderType.MarketOnOpen);
return order.Object;
}
}
}