/* * 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 QuantConnect.Brokerages; using QuantConnect.Data; using QuantConnect.Interfaces; using QuantConnect.Orders; using QuantConnect.Securities; using QuantConnect.Util; using System; using System.Collections.Generic; namespace QuantConnect.Algorithm.CSharp { /// /// Regression algorithm to test we can specify a custom brokerage model, and override some of its methods /// public class CustomBrokerageModelRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition { private OrderTicket _spyTicket; private OrderTicket _aigTicket; private bool _updateRequestSubmitted; public override void Initialize() { SetStartDate(2013, 10, 7); SetEndDate(2013, 10, 11); SetBrokerageModel(new CustomBrokerageModel()); AddEquity("SPY", Resolution.Daily); AddEquity("AIG", Resolution.Daily); _updateRequestSubmitted = false; if (BrokerageModel.DefaultMarkets[SecurityType.Equity] != Market.USA) { throw new RegressionTestException($"The default market for Equity should be {Market.USA}"); } if (BrokerageModel.DefaultMarkets[SecurityType.Crypto] != Market.Binance) { throw new RegressionTestException($"The default market for Crypto should be {Market.Binance}"); } } public override void OnData(Slice slice) { if (!Portfolio.Invested) { MarketOrder("SPY", 100.0); _aigTicket = MarketOrder("AIG", 100.0); } } public override void OnOrderEvent(OrderEvent orderEvent) { var ticket = Transactions.GetOrderTicket(orderEvent.OrderId); if (_updateRequestSubmitted == false) { var updateOrderFields = new UpdateOrderFields(); updateOrderFields.Quantity = ticket.Quantity + 10; ticket.Update(updateOrderFields); _spyTicket = ticket; _updateRequestSubmitted = true; } } public override void OnEndOfAlgorithm() { var submitExpectedMessage = "BrokerageModel declared unable to submit order: [2] Information - Code: - Symbol AIG can not be submitted"; if (_aigTicket.SubmitRequest.Response.ErrorMessage != submitExpectedMessage) { throw new RegressionTestException($"Order with ID: {_aigTicket.OrderId} should not have submitted symbol AIG"); } var updateExpectedMessage = "OrderID: 1 Information - Code: - This order can not be updated"; if (_spyTicket.UpdateRequests[0].Response.ErrorMessage != updateExpectedMessage) { throw new RegressionTestException($"Order with ID: {_spyTicket.OrderId} should have been updated"); } } class CustomBrokerageModel : DefaultBrokerageModel { private static readonly IReadOnlyDictionary _defaultMarketMap = new Dictionary { {SecurityType.Equity, Market.USA}, {SecurityType.Crypto, Market.Binance } }.ToReadOnlyDictionary(); public override IReadOnlyDictionary DefaultMarkets => _defaultMarketMap; public override bool CanSubmitOrder(Security security, Order order, out BrokerageMessageEvent message) { if (security.Symbol.Value == "AIG") { message = new BrokerageMessageEvent(BrokerageMessageType.Information, "", "Symbol AIG can not be submitted"); return false; } message = null; return true; } public override bool CanUpdateOrder(Security security, Order order, UpdateOrderRequest request, out BrokerageMessageEvent message) { message = new BrokerageMessageEvent(BrokerageMessageType.Information, "", "This order can not be updated"); return false; } } /// /// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm. /// public bool CanRunLocally { get; } = true; /// /// This is used by the regression test system to indicate which languages this algorithm is written in. /// public List Languages { get; } = new() { Language.CSharp, Language.Python }; /// /// Data Points count of all timeslices of algorithm /// public long DataPoints => 53; /// /// Data Points count of the algorithm history /// public int AlgorithmHistoryDataPoints => 0; /// /// Final status of the algorithm /// public AlgorithmStatus AlgorithmStatus => AlgorithmStatus.Completed; /// /// This is used by the regression test system to indicate what the expected statistics are from running the algorithm /// public Dictionary ExpectedStatistics => new Dictionary { {"Total Orders", "2"}, {"Average Win", "0%"}, {"Average Loss", "0%"}, {"Compounding Annual Return", "21.133%"}, {"Drawdown", "0.200%"}, {"Expectancy", "0"}, {"Start Equity", "100000"}, {"End Equity", "100245.42"}, {"Net Profit", "0.245%"}, {"Sharpe Ratio", "4.962"}, {"Sortino Ratio", "0"}, {"Probabilistic Sharpe Ratio", "66.956%"}, {"Loss Rate", "0%"}, {"Win Rate", "0%"}, {"Profit-Loss Ratio", "0"}, {"Alpha", "-0.126"}, {"Beta", "0.145"}, {"Annual Standard Deviation", "0.032"}, {"Annual Variance", "0.001"}, {"Information Ratio", "-9.54"}, {"Tracking Error", "0.19"}, {"Treynor Ratio", "1.104"}, {"Total Fees", "$1.00"}, {"Estimated Strategy Capacity", "$5300000000.00"}, {"Lowest Capacity Asset", "SPY R735QTJ8XC9X"}, {"Portfolio Turnover", "2.90%"}, {"Drawdown Recovery", "2"}, {"OrderListHash", "4e8e27d7546eced2ef3512fcc840a634"} }; } }