/* * 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 System.Collections.Generic; using System.Linq; using QuantConnect.Data; using QuantConnect.Data.Consolidators; using QuantConnect.Data.Market; using QuantConnect.Indicators; using QuantConnect.Interfaces; using QuantConnect.Securities; namespace QuantConnect.Algorithm.CSharp { /// /// Regression algorithm reproducing data type bugs in the Consolidate API. Related to GH 4205. /// public class ConsolidateRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition { private List _consolidationCounts; private List _smas; private List _lastSmaUpdates; private int _customDataConsolidator; private Symbol _symbol; /// /// Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized. /// public override void Initialize() { SetStartDate(2013, 10, 08); SetEndDate(2013, 10, 20); var SP500 = QuantConnect.Symbol.Create(Futures.Indices.SP500EMini, SecurityType.Future, Market.CME); _symbol = FutureChainProvider.GetFutureContractList(SP500, StartDate).First(); AddFutureContract(_symbol); _consolidationCounts = Enumerable.Repeat(0, 9).ToList(); _smas = _consolidationCounts.Select(_ => new SimpleMovingAverage(10)).ToList(); _lastSmaUpdates = _consolidationCounts.Select(_ => DateTime.MinValue).ToList(); Consolidate(_symbol, time => new CalendarInfo(time.RoundDown(TimeSpan.FromDays(1)), TimeSpan.FromDays(1)), bar => UpdateQuoteBar(bar, 0)); Consolidate(_symbol, time => new CalendarInfo(time.RoundDown(TimeSpan.FromDays(1)), TimeSpan.FromDays(1)), TickType.Quote, bar => UpdateQuoteBar(bar, 1)); Consolidate(_symbol, TimeSpan.FromDays(1), bar => UpdateQuoteBar(bar, 2)); Consolidate(_symbol, Resolution.Daily, TickType.Quote, (Action)(bar => UpdateQuoteBar(bar, 3))); Consolidate(_symbol, TimeSpan.FromDays(1), bar => UpdateTradeBar(bar, 4)); Consolidate(_symbol, TimeSpan.FromDays(1), bar => UpdateTradeBar(bar, 5)); // custom data var symbol = AddData("BTC", Resolution.Minute).Symbol; Consolidate(symbol, TimeSpan.FromDays(1), bar => _customDataConsolidator++); try { Consolidate(symbol, TimeSpan.FromDays(1), bar => { UpdateQuoteBar(bar, -1); }); throw new Exception($"Expected {nameof(ArgumentException)} to be thrown"); } catch (ArgumentException) { // will try to use BaseDataConsolidator for which input is TradeBars not QuoteBars } // Test using abstract T types, through defining a 'BaseData' handler Consolidate(_symbol, Resolution.Daily, null, (Action)(bar => UpdateBar(bar, 6))); Consolidate(_symbol, TimeSpan.FromDays(1), null, (Action)(bar => UpdateBar(bar, 7))); Consolidate(_symbol, TimeSpan.FromDays(1), (Action)(bar => UpdateBar(bar, 8))); } private void UpdateBar(BaseData tradeBar, int position) { if (!(tradeBar is TradeBar)) { throw new Exception("Expected a TradeBar"); } _consolidationCounts[position]++; _smas[position].Update(tradeBar.EndTime, tradeBar.Value); _lastSmaUpdates[position] = tradeBar.EndTime; } private void UpdateTradeBar(TradeBar tradeBar, int position) { _consolidationCounts[position]++; _smas[position].Update(tradeBar.EndTime, tradeBar.High); _lastSmaUpdates[position] = tradeBar.EndTime; } private void UpdateQuoteBar(QuoteBar quoteBar, int position) { _consolidationCounts[position]++; _smas[position].Update(quoteBar.EndTime, quoteBar.High); _lastSmaUpdates[position] = quoteBar.EndTime; } public override void OnEndOfAlgorithm() { var expectedConsolidations = 8; if (_consolidationCounts.Any(i => i != expectedConsolidations) || _customDataConsolidator == 0) { throw new Exception("Unexpected consolidation count"); } for (var i = 0; i < _smas.Count; i++) { if (_smas[i].Samples != expectedConsolidations) { throw new Exception($"Expected {expectedConsolidations} samples in each SMA but found {_smas[i].Samples} in SMA in index {i}"); } if (_smas[i].Current.Time != _lastSmaUpdates[i]) { throw new Exception($"Expected SMA in index {i} to have been last updated at {_lastSmaUpdates[i]} but was {_smas[i].Current.Time}"); } } } /// /// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here. /// /// Slice object keyed by symbol containing the stock data public override void OnData(Slice data) { if (!Portfolio.Invested) { SetHoldings(_symbol, 0.5); } } /// /// 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 Language[] Languages { get; } = { Language.CSharp, Language.Python }; /// /// Data Points count of all timeslices of algorithm /// public long DataPoints => 12244; /// /// Data Points count of the algorithm history /// public int AlgorithmHistoryDataPoints => 0; /// /// 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 Trades", "1"}, {"Average Win", "0%"}, {"Average Loss", "0%"}, {"Compounding Annual Return", "6636.699%"}, {"Drawdown", "15.900%"}, {"Expectancy", "0"}, {"Net Profit", "16.178%"}, {"Sharpe Ratio", "640.32"}, {"Probabilistic Sharpe Ratio", "99.824%"}, {"Loss Rate", "0%"}, {"Win Rate", "0%"}, {"Profit-Loss Ratio", "0"}, {"Alpha", "636.128"}, {"Beta", "5.924"}, {"Annual Standard Deviation", "1.012"}, {"Annual Variance", "1.024"}, {"Information Ratio", "696.123"}, {"Tracking Error", "0.928"}, {"Treynor Ratio", "109.405"}, {"Total Fees", "$23.65"}, {"Estimated Strategy Capacity", "$210000000.00"}, {"Lowest Capacity Asset", "ES VMKLFZIH2MTD"}, {"Portfolio Turnover", "81.19%"}, {"OrderListHash", "dd38e7b94027d20942a5aa9ac31a9a7f"} }; } }