From 5accfd6be3f9932461d306a67eaf253466424adc Mon Sep 17 00:00:00 2001 From: JosueNina <36119850+JosueNina@users.noreply.github.com> Date: Tue, 14 Jan 2025 13:17:55 -0500 Subject: [PATCH 1/7] Update secondsTimeStep value (#8528) --- .../DataFeeds/LiveTradingDataFeedTests.cs | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/Tests/Engine/DataFeeds/LiveTradingDataFeedTests.cs b/Tests/Engine/DataFeeds/LiveTradingDataFeedTests.cs index f9e8ddec3cdf..25dc5acb9807 100644 --- a/Tests/Engine/DataFeeds/LiveTradingDataFeedTests.cs +++ b/Tests/Engine/DataFeeds/LiveTradingDataFeedTests.cs @@ -102,7 +102,7 @@ public void EmitsStreamedDailyData(bool strictEndTimes) var dqh = new TestDataQueueHandler { - DataPerSymbol = new Dictionary> + DataPerSymbol = new Dictionary> { { symbol, new List { new TradeBar(_algorithm.StartDate, symbol, 1, 5, 1, 3, 100, Time.OneDay) } @@ -271,7 +271,8 @@ public void LiveChainSelection(SecurityType securityType, Resolution resolution, // allow time for the exchange to pick up the selection point Thread.Sleep(50); - ConsumeBridge(feed, TimeSpan.FromSeconds(5), true, ts => { + ConsumeBridge(feed, TimeSpan.FromSeconds(5), true, ts => + { if (selectionHappened == 2) { // we got what we wanted shortcut unit test @@ -436,7 +437,7 @@ IEnumerable Filter(IEnumerable fundamentals) var dataPoint = fundamentals.Take(1); selectionDataTime.Add(dataPoint.First().EndTime); return dataPoint.Select(x => x.Symbol); - }; + } _algorithm.UniverseSettings.Resolution = Resolution.Daily; var universe = _algorithm.AddUniverse(Filter); @@ -493,7 +494,7 @@ IEnumerable CoarseFilter(IEnumerable coarse) selectionTime = _algorithm.UtcTime; selectedSymbols = coarse.Select(x => x.Symbol).ToList(); return selectedSymbols; - }; + } _algorithm.UniverseSettings.Resolution = Resolution.Daily; var universe = _algorithm.AddUniverse(CoarseFilter); @@ -930,7 +931,7 @@ public void FutureLiveHoldingsFutureMapping() } }, endDate: _startDate.AddDays(10), - secondsTimeStep: 60 * 60 * 24); + secondsTimeStep: 60 * 60 * 8); Assert.IsTrue(assertedHoldings); Assert.AreEqual(4, securityChanges); @@ -1006,7 +1007,7 @@ public void WarmupFutureSelection(bool useWarmupResolution) var countLive = 0; ConsumeBridge(feed, TimeSpan.FromSeconds(5), true, ts => { - if(ts.UniverseData?.Count > 0) + if (ts.UniverseData?.Count > 0) { Assert.IsNotEmpty(ts.UniverseData.Select(x => x.Value.FilteredContracts)); if (_algorithm.IsWarmingUp) @@ -1946,7 +1947,7 @@ public void DelistedEventEmitted() { foreach (var delisting in ts.Slice.Delistings) { - if(delisting.Key != Symbols.SPY_C_192_Feb19_2016) + if (delisting.Key != Symbols.SPY_C_192_Feb19_2016) { throw new RegressionTestException($"Unexpected delisting for symbol {delisting.Key}"); } @@ -2071,7 +2072,7 @@ public void CustomUniverseFineFundamentalDataGetsPipedCorrectly() }, secondsTimeStep: 60 * 60, alwaysInvoke: true, sendUniverseData: true, - endDate:_startDate.AddDays(10)); + endDate: _startDate.AddDays(10)); Assert.IsNotNull(securityChanges); Assert.IsTrue(securityChanges.AddedSecurities.Single().Symbol.Value == "AAPL"); @@ -2212,7 +2213,7 @@ public void FineCoarseFundamentalDataGetsPipedCorrectly(int numberOfUniverses) // we got what we wanted shortcut unit test _manualTimeProvider.SetCurrentTimeUtc(Time.EndOfTime); } - }, sendUniverseData: true, alwaysInvoke: true, secondsTimeStep: 3600, endDate: _startDate.AddDays(10)); + }, sendUniverseData: true, alwaysInvoke: true, secondsTimeStep: 1200, endDate: _startDate.AddDays(10)); Assert.IsTrue(receivedFundamentalsData); for (var i = 0; i < numberOfUniverses; i++) @@ -2313,7 +2314,7 @@ public void ConstituentsUniverse() _manualTimeProvider.SetCurrentTimeUtc(Time.EndOfTime); } } - }, secondsTimeStep: 60 * 60 * 3, // 3 hour time step + }, secondsTimeStep: 60 * 60, alwaysInvoke: true, endDate: endDate); @@ -2330,7 +2331,8 @@ public void ThrowingDataQueueHandlerRuntimeError() _algorithm.AddEquity("SPY"); _algorithm.OnEndOfTimeStep(); - ConsumeBridge(feed, TimeSpan.FromSeconds(2), ts => { + ConsumeBridge(feed, TimeSpan.FromSeconds(2), ts => + { if (_algorithm.Status == AlgorithmStatus.RuntimeError) { _manualTimeProvider.SetCurrentTimeUtc(Time.EndOfTime); @@ -2604,7 +2606,7 @@ public void LiveSplitHandling(bool warmup) { Assert.AreEqual(warmup, _algorithm.IsWarmingUp); - if(split.Type == SplitType.SplitOccurred) + if (split.Type == SplitType.SplitOccurred) { emittedSplit = true; // we got what we wanted shortcut unit test @@ -4114,7 +4116,7 @@ internal class TestableLiveTradingDataFeed : LiveTradingDataFeed public TestableLiveTradingDataFeed(IAlgorithmSettings settings, IDataQueueHandler dataQueueHandler = null) { DataQueueHandler = dataQueueHandler; - TestDataQueueHandlerManager = new (new[] { DataQueueHandler }, settings); + TestDataQueueHandlerManager = new(new[] { DataQueueHandler }, settings); } protected override BaseDataExchange GetBaseDataExchange() From cda769df7442e8610848360d303657178135c472 Mon Sep 17 00:00:00 2001 From: Martin-Molinero Date: Tue, 14 Jan 2025 17:08:36 -0300 Subject: [PATCH 2/7] Add Nikkei 225 index support (#8529) - Add Osaka exchange for nikkei index. Adding regression test - Improve default market resolution --- .../HSIFutureHourRegressionAlgorithm.cs | 2 +- .../NikkeiIndexRegressionAlgorithm.cs | 122 ++++++++++++++++ Algorithm/QCAlgorithm.cs | 84 +++++------ Common/Currencies.cs | 7 +- Common/Market.cs | 8 +- Common/Securities/Index/IndexSymbol.cs | 27 +++- Data/index/ose/minute/n225/20210114_trade.zip | Bin 0 -> 4775 bytes Data/market-hours/market-hours-database.json | 138 ++++++++++++++++++ .../symbol-properties-database.csv | 2 + run_benchmarks.py | 5 +- 10 files changed, 338 insertions(+), 57 deletions(-) create mode 100644 Algorithm.CSharp/NikkeiIndexRegressionAlgorithm.cs create mode 100644 Data/index/ose/minute/n225/20210114_trade.zip diff --git a/Algorithm.CSharp/HSIFutureHourRegressionAlgorithm.cs b/Algorithm.CSharp/HSIFutureHourRegressionAlgorithm.cs index b7daa659a3e1..657ced59c790 100644 --- a/Algorithm.CSharp/HSIFutureHourRegressionAlgorithm.cs +++ b/Algorithm.CSharp/HSIFutureHourRegressionAlgorithm.cs @@ -52,7 +52,7 @@ public override void Initialize() SetTimeZone(TimeZones.HongKong); UniverseSettings.Resolution = Resolution; - _index = AddIndex("HSI", Resolution, market: Market.HKFE).Symbol; + _index = AddIndex("HSI", Resolution).Symbol; var future = AddFuture(Futures.Indices.HangSeng, Resolution); future.SetFilter(TimeSpan.Zero, TimeSpan.FromDays(182)); _futureSymbol = future.Symbol; diff --git a/Algorithm.CSharp/NikkeiIndexRegressionAlgorithm.cs b/Algorithm.CSharp/NikkeiIndexRegressionAlgorithm.cs new file mode 100644 index 000000000000..633b38934160 --- /dev/null +++ b/Algorithm.CSharp/NikkeiIndexRegressionAlgorithm.cs @@ -0,0 +1,122 @@ +/* + * 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.Indicators; +using QuantConnect.Interfaces; +using QuantConnect.Securities; +using System.Collections.Generic; + +namespace QuantConnect.Algorithm.CSharp +{ + /// + /// Example algorithm using nikkei index + /// + public class NikkeiIndexRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition + { + private Security _security; + private ExponentialMovingAverage _emaSlow; + private ExponentialMovingAverage _emaFast; + + /// + /// Initialize your algorithm and add desired assets. + /// + public override void Initialize() + { + SetStartDate(2021, 1, 14); + SetEndDate(2021, 1, 15); + SetCash(1000000); + + _security = AddIndex("N225"); + + var symbol = _security.Symbol; + _emaSlow = EMA(symbol, 80); + _emaFast = EMA(symbol, 200); + + Settings.DailyPreciseEndTime = true; + } + + public override void OnEndOfAlgorithm() + { + if (!_emaSlow.IsReady || !_emaFast.IsReady) + { + throw new RegressionTestException("Indicators are not ready!"); + } + if (!_security.HasData) + { + throw new RegressionTestException("Security does not have data!"); + } + } + + /// + /// 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 virtual bool CanRunLocally { get; } = true; + + /// + /// This is used by the regression test system to indicate which languages this algorithm is written in. + /// + public virtual List Languages { get; } = new() { Language.CSharp }; + + /// + /// Data Points count of all timeslices of algorithm + /// + public virtual long DataPoints => 443; + + /// + /// Data Points count of the algorithm history + /// + public virtual 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 virtual Dictionary ExpectedStatistics => new Dictionary + { + {"Total Orders", "0"}, + {"Average Win", "0%"}, + {"Average Loss", "0%"}, + {"Compounding Annual Return", "0%"}, + {"Drawdown", "0%"}, + {"Expectancy", "0"}, + {"Start Equity", "1000000"}, + {"End Equity", "1000000"}, + {"Net Profit", "0%"}, + {"Sharpe Ratio", "0"}, + {"Sortino Ratio", "0"}, + {"Probabilistic Sharpe Ratio", "0%"}, + {"Loss Rate", "0%"}, + {"Win Rate", "0%"}, + {"Profit-Loss Ratio", "0"}, + {"Alpha", "0"}, + {"Beta", "0"}, + {"Annual Standard Deviation", "0"}, + {"Annual Variance", "0"}, + {"Information Ratio", "0"}, + {"Tracking Error", "0"}, + {"Treynor Ratio", "0"}, + {"Total Fees", "$0.00"}, + {"Estimated Strategy Capacity", "$0"}, + {"Lowest Capacity Asset", ""}, + {"Portfolio Turnover", "0%"}, + {"OrderListHash", "d41d8cd98f00b204e9800998ecf8427e"} + }; + } +} diff --git a/Algorithm/QCAlgorithm.cs b/Algorithm/QCAlgorithm.cs index 280eb60d2ae8..0224eb60d098 100644 --- a/Algorithm/QCAlgorithm.cs +++ b/Algorithm/QCAlgorithm.cs @@ -56,6 +56,7 @@ using Python.Runtime; using QuantConnect.Commands; using Newtonsoft.Json; +using QuantConnect.Securities.Index; namespace QuantConnect.Algorithm { @@ -1378,11 +1379,7 @@ public void SetBenchmark(SecurityType securityType, string symbol) throw new InvalidOperationException("Algorithm.SetBenchmark(): Cannot change Benchmark after algorithm initialized."); } - string market; - if (!BrokerageModel.DefaultMarkets.TryGetValue(securityType, out market)) - { - market = Market.USA; - } + var market = GetMarket(null, symbol, securityType, defaultMarket: Market.USA); var benchmarkSymbol = QuantConnect.Symbol.Create(symbol, securityType, market); SetBenchmark(benchmarkSymbol); @@ -1886,13 +1883,7 @@ public Security AddSecurity(SecurityType securityType, string ticker, Resolution try { - if (market == null) - { - if (!BrokerageModel.DefaultMarkets.TryGetValue(securityType, out market)) - { - throw new KeyNotFoundException($"No default market set for security type: {securityType}"); - } - } + market = GetMarket(market, ticker, securityType); Symbol symbol; if (!SymbolCache.TryGetSymbol(ticker, out symbol) || @@ -2068,13 +2059,7 @@ public Equity AddEquity(string ticker, Resolution? resolution = null, string mar [DocumentationAttribute(AddingData)] public Option AddOption(string underlying, Resolution? resolution = null, string market = null, bool fillForward = true, decimal leverage = Security.NullLeverage) { - if (market == null) - { - if (!BrokerageModel.DefaultMarkets.TryGetValue(SecurityType.Option, out market)) - { - throw new KeyNotFoundException($"No default market set for security type: {SecurityType.Option}"); - } - } + market = GetMarket(market, underlying, SecurityType.Option); var underlyingSymbol = QuantConnect.Symbol.Create(underlying, SecurityType.Equity, market); return AddOption(underlyingSymbol, resolution, market, fillForward, leverage); @@ -2117,13 +2102,7 @@ public Option AddOption(Symbol underlying, string targetOption, Resolution? reso { var optionType = QuantConnect.Symbol.GetOptionTypeFromUnderlying(underlying); - if (market == null) - { - if (!BrokerageModel.DefaultMarkets.TryGetValue(optionType, out market)) - { - throw new KeyNotFoundException($"No default market set for security type: {optionType}"); - } - } + market = GetMarket(market, targetOption, optionType); Symbol canonicalSymbol; @@ -2165,14 +2144,7 @@ public Future AddFuture(string ticker, Resolution? resolution = null, string mar bool fillForward = true, decimal leverage = Security.NullLeverage, bool extendedMarketHours = false, DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null, int contractDepthOffset = 0) { - if (market == null) - { - if (!SymbolPropertiesDatabase.TryGetMarket(ticker, SecurityType.Future, out market) - && !BrokerageModel.DefaultMarkets.TryGetValue(SecurityType.Future, out market)) - { - throw new KeyNotFoundException($"No default market set for security type: {SecurityType.Future}"); - } - } + market = GetMarket(market, ticker, SecurityType.Future); Symbol canonicalSymbol; var alias = "/" + ticker; @@ -2301,13 +2273,8 @@ public IndexOption AddIndexOption(Symbol symbol, string targetOption, Resolution [DocumentationAttribute(AddingData)] public IndexOption AddIndexOption(string underlying, string targetOption, Resolution? resolution = null, string market = null, bool fillForward = true) { - if (market == null && !BrokerageModel.DefaultMarkets.TryGetValue(SecurityType.Index, out market)) - { - throw new KeyNotFoundException($"No default market set for underlying security type: {SecurityType.Index}"); - } - return AddIndexOption( - QuantConnect.Symbol.Create(underlying, SecurityType.Index, market), + QuantConnect.Symbol.Create(underlying, SecurityType.Index, GetMarket(market, underlying, SecurityType.Index)), targetOption, resolution, fillForward); } @@ -2946,13 +2913,7 @@ private T AddSecurity(SecurityType securityType, string ticker, Resolution? r DataMappingMode? mappingMode = null, DataNormalizationMode? normalizationMode = null) where T : Security { - if (market == null) - { - if (!BrokerageModel.DefaultMarkets.TryGetValue(securityType, out market)) - { - throw new Exception("No default market set for security type: " + securityType); - } - } + market = GetMarket(market, ticker, securityType); Symbol symbol; if (!SymbolCache.TryGetSymbol(ticker, out symbol) || @@ -3489,6 +3450,35 @@ public CommandResultPacket RunCommand(CallbackCommand command) return true; } + /// + /// Helper method to get a market for a given security type and ticker + /// + private string GetMarket(string market, string ticker, SecurityType securityType, string defaultMarket = null) + { + if (string.IsNullOrEmpty(market)) + { + if (securityType == SecurityType.Index && IndexSymbol.TryGetIndexMarket(ticker, out market)) + { + return market; + } + + if (securityType == SecurityType.Future && SymbolPropertiesDatabase.TryGetMarket(ticker, securityType, out market)) + { + return market; + } + + if (!BrokerageModel.DefaultMarkets.TryGetValue(securityType, out market)) + { + if (string.IsNullOrEmpty(defaultMarket)) + { + throw new KeyNotFoundException($"No default market set for security type: {securityType}"); + } + return defaultMarket; + } + } + return market; + } + private string CommandLink(string typeName, object command) { var payload = new Dictionary { { "projectId", ProjectId }, { "command", command } }; diff --git a/Common/Currencies.cs b/Common/Currencies.cs index eab212a6ea8c..fcbc1efd6702 100644 --- a/Common/Currencies.cs +++ b/Common/Currencies.cs @@ -66,6 +66,11 @@ public static class Currencies /// public const string HKD = "HKD"; + /// + /// JPY (Japanese yen) currency string + /// + public const string JPY = "JPY"; + /// /// Null currency used when a real one is not required /// @@ -81,7 +86,7 @@ public static class Currencies { {USD, "$"}, {GBP, "₤"}, - {"JPY", "¥"}, + {JPY, "¥"}, {EUR, "€"}, {"NZD", "$"}, {"AUD", "$"}, diff --git a/Common/Market.cs b/Common/Market.cs index 82670df584c0..b2a1692cbd68 100644 --- a/Common/Market.cs +++ b/Common/Market.cs @@ -69,7 +69,8 @@ public static class Market Tuple.Create(Bybit, 37), Tuple.Create(Coinbase, 38), Tuple.Create(InteractiveBrokers, 39), - Tuple.Create(EUREX, 40) + Tuple.Create(EUREX, 40), + Tuple.Create(OSE, 41) }; static Market() @@ -169,6 +170,11 @@ static Market() /// public const string HKFE = "hkfe"; + /// + /// Osaka Stock Exchange + /// + public const string OSE = "ose"; + /// /// London International Financial Futures and Options Exchange /// diff --git a/Common/Securities/Index/IndexSymbol.cs b/Common/Securities/Index/IndexSymbol.cs index 132e4f1ec506..a55e77e536a2 100644 --- a/Common/Securities/Index/IndexSymbol.cs +++ b/Common/Securities/Index/IndexSymbol.cs @@ -13,6 +13,7 @@ * limitations under the License. */ +using System; using System.Collections.Generic; namespace QuantConnect.Securities.Index @@ -22,27 +23,41 @@ namespace QuantConnect.Securities.Index /// public static class IndexSymbol { - private static readonly Dictionary _indexMarket = new Dictionary + private static readonly Dictionary _indexExchange = new(StringComparer.InvariantCultureIgnoreCase) { { "SPX", Market.CBOE }, { "NDX", "NASDAQ" }, { "VIX", Market.CBOE }, { "SPXW", Market.CBOE }, { "NQX", "NASDAQ" }, - { "VIXW", Market.CBOE }, - { "HSI", Market.HKFE } + { "VIXW", Market.CBOE } + }; + + private static readonly Dictionary _indexMarket = new(StringComparer.InvariantCultureIgnoreCase) + { + { "HSI", Market.HKFE }, + { "N225", Market.OSE } }; /// /// Gets the actual exchange the index lives on /// - /// The market of the index + /// Useful for live trading + /// The exchange of the index public static string GetIndexExchange(Symbol symbol) { - string market; - return _indexMarket.TryGetValue(symbol.Value, out market) + return _indexExchange.TryGetValue(symbol.Value, out var market) ? market : symbol.ID.Market; } + + /// + /// Gets the lean market for this index ticker + /// + /// The market of the index + public static bool TryGetIndexMarket(string ticker, out string market) + { + return _indexMarket.TryGetValue(ticker, out market); + } } } diff --git a/Data/index/ose/minute/n225/20210114_trade.zip b/Data/index/ose/minute/n225/20210114_trade.zip new file mode 100644 index 0000000000000000000000000000000000000000..8edad96366ac527c24cfee809309a6ef51b7633f GIT binary patch literal 4775 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 literal 0 HcmV?d00001 diff --git a/Data/market-hours/market-hours-database.json b/Data/market-hours/market-hours-database.json index a18855d3acd5..ca0956353325 100644 --- a/Data/market-hours/market-hours-database.json +++ b/Data/market-hours/market-hours-database.json @@ -123903,6 +123903,144 @@ "12/31/2025": "12:00:00" }, "lateOpens": {} + }, + "Index-ose-[*]": { + "dataTimeZone": "Asia/Tokyo", + "exchangeTimeZone": "Asia/Tokyo", + "sunday": [], + "monday": [ + { + "start": "09:00:00", + "end": "11:30:00", + "state": "market" + }, + { + "start": "12:30:00", + "end": "15:30:00", + "state": "market" + } + ], + "tuesday": [ + { + "start": "09:00:00", + "end": "11:30:00", + "state": "market" + }, + { + "start": "12:30:00", + "end": "15:30:00", + "state": "market" + } + ], + "wednesday": [ + { + "start": "09:00:00", + "end": "11:30:00", + "state": "market" + }, + { + "start": "12:30:00", + "end": "15:30:00", + "state": "market" + } + ], + "thursday": [ + { + "start": "09:00:00", + "end": "11:30:00", + "state": "market" + }, + { + "start": "12:30:00", + "end": "15:30:00", + "state": "market" + } + ], + "friday": [ + { + "start": "09:00:00", + "end": "11:30:00", + "state": "market" + }, + { + "start": "12:30:00", + "end": "15:30:00", + "state": "market" + } + ], + "saturday": [], + "holidays": [], + "earlyCloses": {}, + "lateOpens": {} + }, + "Index-ose-N225": { + "dataTimeZone": "Asia/Tokyo", + "exchangeTimeZone": "Asia/Tokyo", + "sunday": [], + "monday": [ + { + "start": "09:00:00", + "end": "11:30:00", + "state": "market" + }, + { + "start": "12:30:00", + "end": "15:30:00", + "state": "market" + } + ], + "tuesday": [ + { + "start": "09:00:00", + "end": "11:30:00", + "state": "market" + }, + { + "start": "12:30:00", + "end": "15:30:00", + "state": "market" + } + ], + "wednesday": [ + { + "start": "09:00:00", + "end": "11:30:00", + "state": "market" + }, + { + "start": "12:30:00", + "end": "15:30:00", + "state": "market" + } + ], + "thursday": [ + { + "start": "09:00:00", + "end": "11:30:00", + "state": "market" + }, + { + "start": "12:30:00", + "end": "15:30:00", + "state": "market" + } + ], + "friday": [ + { + "start": "09:00:00", + "end": "11:30:00", + "state": "market" + }, + { + "start": "12:30:00", + "end": "15:30:00", + "state": "market" + } + ], + "saturday": [], + "holidays": [], + "earlyCloses": {}, + "lateOpens": {} } } } \ No newline at end of file diff --git a/Data/symbol-properties/symbol-properties-database.csv b/Data/symbol-properties/symbol-properties-database.csv index b8be25c4f13c..947fb46b2dd6 100644 --- a/Data/symbol-properties/symbol-properties-database.csv +++ b/Data/symbol-properties/symbol-properties-database.csv @@ -407,6 +407,8 @@ india,SENSEX,future,BSE S&P Sensex Index,INR,0.05,25,1.0 hkfe,HSI,future,Hang Seng Index,HKD,50,1,1.0 hkfe,[*],index,,HKD,1,0.01,1 +ose,[*],index,,JPY,1,0.01,1 + # Futures options -- will default to Futures contract specs in case no entry exists cbot,OZB,futureoption,U.S. Treasury Bond American Futures Options,USD,1000.0,0.015625,1.0 cbot,OZC,futureoption,Corn American Futures Options,USD,5000.0,0.00125,1.0,,,100 diff --git a/run_benchmarks.py b/run_benchmarks.py index 7c48fb6bb62c..a029d0e7f363 100644 --- a/run_benchmarks.py +++ b/run_benchmarks.py @@ -30,7 +30,7 @@ dataPointsPerSecond = [] benchmarkLengths = [] - for x in range(1, 2): + for x in range(1, 3): subprocess.run(["dotnet", "./QuantConnect.Lean.Launcher.dll", "--data-folder " + dataPath, @@ -42,6 +42,9 @@ cwd="./Launcher/bin/Release", stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + if x == 1: + # skip first run + continue algorithmLogs = os.path.join("./Launcher/bin/Release", algorithmName + "-log.txt") file = open(algorithmLogs, 'r') From 2d4ca756707251ff89ec9bed9d431b258b5f3125 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Tue, 14 Jan 2025 17:23:08 -0400 Subject: [PATCH 3/7] Add Russell 2000 (RUT) market hours and symbol properties entries (#8530) --- Data/market-hours/market-hours-database.json | 42 +++++++++++++++++++ .../symbol-properties-database.csv | 2 + 2 files changed, 44 insertions(+) diff --git a/Data/market-hours/market-hours-database.json b/Data/market-hours/market-hours-database.json index ca0956353325..b42cc4551035 100644 --- a/Data/market-hours/market-hours-database.json +++ b/Data/market-hours/market-hours-database.json @@ -109425,6 +109425,48 @@ "12/24/2024": "12:00:00" } }, + "Index-usa-RUT": { + "dataTimeZone": "America/New_York", + "exchangeTimeZone": "America/Chicago", + "sunday": [], + "monday": [ + { + "start": "08:30:00", + "end": "15:00:00", + "state": "market" + } + ], + "tuesday": [ + { + "start": "08:30:00", + "end": "15:00:00", + "state": "market" + } + ], + "wednesday": [ + { + "start": "08:30:00", + "end": "15:00:00", + "state": "market" + } + ], + "thursday": [ + { + "start": "08:30:00", + "end": "15:00:00", + "state": "market" + } + ], + "friday": [ + { + "start": "08:30:00", + "end": "15:00:00", + "state": "market" + } + ], + "saturday": [], + "holidays": [] + }, "IndexOption-usa-[*]": { "dataTimeZone": "America/New_York", "exchangeTimeZone": "America/Chicago", diff --git a/Data/symbol-properties/symbol-properties-database.csv b/Data/symbol-properties/symbol-properties-database.csv index 947fb46b2dd6..9bf404cbcb3c 100644 --- a/Data/symbol-properties/symbol-properties-database.csv +++ b/Data/symbol-properties/symbol-properties-database.csv @@ -200,6 +200,8 @@ usa,[*],index,,USD,1,0.01,1 usa,[*],indexoption,,USD,100,0.05,1 usa,NQX,indexoption,Nasdaq-100 Reduced-Value Index Options,USD,100,0.05,1,,,,5 +usa,RUT,index,,USD,1,0.001,1 + # for backwards compatibility, order here is important for futures, since we could have the same symbol for more than 1 market in which case Lean will use the first cfe,VX,future,VIX futures ,USD,1000.0,0.05,1.0 cbot,2YY,future,Micro 2-Year Yield Futures,USD,1000,0.001,1 From 58b7a9d22a9bf52084bc1869d6abb7a101b50e99 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Wed, 15 Jan 2025 09:36:20 -0400 Subject: [PATCH 4/7] Add FTSE Russell Exchange support (#8531) * Add FTSE Russell Exchange support * Cleanup --- Common/Securities/Index/IndexSymbol.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Common/Securities/Index/IndexSymbol.cs b/Common/Securities/Index/IndexSymbol.cs index a55e77e536a2..4019cdbb682f 100644 --- a/Common/Securities/Index/IndexSymbol.cs +++ b/Common/Securities/Index/IndexSymbol.cs @@ -30,7 +30,8 @@ public static class IndexSymbol { "VIX", Market.CBOE }, { "SPXW", Market.CBOE }, { "NQX", "NASDAQ" }, - { "VIXW", Market.CBOE } + { "VIXW", Market.CBOE }, + { "RUT", "RUSSELL" } }; private static readonly Dictionary _indexMarket = new(StringComparer.InvariantCultureIgnoreCase) From 1cb8fcf2d9537f590ac30545dd80413312b83012 Mon Sep 17 00:00:00 2001 From: Ashutosh <62750665+ashutoshrana171@users.noreply.github.com> Date: Wed, 15 Jan 2025 09:29:37 -0500 Subject: [PATCH 5/7] Update mhdb to add holidays for osaka exchange (#8533) * Update mhdb to add holidays for osaka exchange * Minor MHDB cleanup --------- Co-authored-by: Martin Molinero --- Data/market-hours/market-hours-database.json | 320 ++++++++++--------- 1 file changed, 169 insertions(+), 151 deletions(-) diff --git a/Data/market-hours/market-hours-database.json b/Data/market-hours/market-hours-database.json index b42cc4551035..928aef522d0a 100644 --- a/Data/market-hours/market-hours-database.json +++ b/Data/market-hours/market-hours-database.json @@ -123794,156 +123794,8 @@ "state": "premarket" } ], - "holidays": [ - "1/2/2017", - "1/30/2017", - "1/31/2017", - "4/4/2017", - "4/14/2017", - "4/17/2017", - "5/1/2017", - "5/3/2017", - "5/30/2017", - "10/2/2017", - "10/5/2017", - "12/25/2017", - "12/26/2017", - "1/1/2018", - "2/16/2018", - "2/19/2018", - "3/30/2018", - "4/2/2018", - "4/5/2018", - "5/1/2018", - "5/22/2018", - "6/18/2018", - "7/2/2018", - "9/25/2018", - "10/1/2018", - "10/17/2018", - "12/25/2018", - "12/26/2018", - "1/1/2019", - "2/5/2019", - "2/6/2019", - "2/7/2019", - "4/5/2019", - "4/19/2019", - "4/22/2019", - "5/1/2019", - "5/13/2019", - "6/7/2019", - "7/1/2019", - "10/1/2019", - "10/7/2019", - "12/25/2019", - "12/26/2019", - "1/1/2020", - "1/27/2020", - "1/28/2020", - "4/10/2020", - "4/13/2020", - "4/30/2020", - "5/1/2020", - "6/25/2020", - "7/1/2020", - "10/1/2020", - "10/2/2020", - "10/26/2020", - "12/25/2020", - "1/1/2021", - "2/12/2021", - "2/15/2021", - "4/2/2021", - "4/5/2021", - "4/6/2021", - "5/19/2021", - "6/14/2021", - "7/1/2021", - "9/22/2021", - "10/1/2021", - "10/14/2021", - "12/27/2021", - "2/1/2022", - "2/2/2022", - "2/3/2022", - "4/5/2022", - "4/15/2022", - "4/18/2022", - "5/2/2022", - "5/9/2022", - "6/3/2022", - "7/1/2022", - "9/12/2022", - "10/4/2022", - "12/26/2022", - "12/27/2022", - "1/2/2023", - "1/23/2023", - "1/24/2023", - "1/25/2023", - "4/5/2023", - "4/7/2023", - "4/10/2023", - "5/1/2023", - "5/26/2023", - "6/22/2023", - "10/2/2023", - "10/23/2023", - "12/25/2023", - "12/26/2023", - "1/1/2024", - "2/12/2024", - "2/13/2024", - "3/29/2024", - "4/1/2024", - "4/4/2024", - "5/1/2024", - "5/15/2024", - "6/10/2024", - "7/1/2024", - "9/18/2024", - "10/1/2024", - "10/11/2024", - "12/25/2024", - "12/26/2024", - "1/1/2025", - "1/29/2025", - "1/30/2025", - "1/31/2025", - "4/4/2025", - "4/18/2025", - "4/21/2025", - "5/1/2025", - "5/5/2025", - "7/1/2025", - "10/1/2025", - "10/7/2025", - "10/29/2025", - "12/25/2025", - "12/26/2025" - ], - "earlyCloses": { - "2/15/2018": "12:00:00", - "12/24/2018": "12:00:00", - "12/31/2018": "12:00:00", - "2/4/2019": "12:00:00", - "12/24/2019": "12:00:00", - "12/31/2019": "12:00:00", - "1/24/2020": "12:00:00", - "12/24/2020": "12:00:00", - "12/31/2020": "12:00:00", - "2/11/2021": "12:00:00", - "12/24/2021": "12:00:00", - "12/31/2021": "12:00:00", - "1/31/2022": "12:00:00", - "2/9/2024": "12:00:00", - "12/24/2024": "12:00:00", - "12/31/2024": "12:00:00", - "1/28/2025": "12:00:00", - "12/24/2025": "12:00:00", - "12/31/2025": "12:00:00" - }, + "holidays": [], + "earlyCloses": {}, "lateOpens": {} }, "Index-ose-[*]": { @@ -124011,7 +123863,173 @@ } ], "saturday": [], - "holidays": [], + "holidays": [ + "01/01/2016", + "01/11/2016", + "02/11/2016", + "03/21/2016", + "04/29/2016", + "05/03/2016", + "05/04/2016", + "05/05/2016", + "07/18/2016", + "08/11/2016", + "09/19/2016", + "09/22/2016", + "10/10/2016", + "11/03/2016", + "11/23/2016", + "12/23/2016", + "12/31/2016", + "01/02/2017", + "01/03/2017", + "01/09/2017", + "03/20/2017", + "05/03/2017", + "05/04/2017", + "05/05/2017", + "07/17/2017", + "08/11/2017", + "09/18/2017", + "10/09/2017", + "11/03/2017", + "11/23/2017", + "01/01/2018", + "01/02/2018", + "01/03/2018", + "01/08/2018", + "02/12/2018", + "03/21/2018", + "04/30/2018", + "05/03/2018", + "05/04/2018", + "07/16/2018", + "09/17/2018", + "09/24/2018", + "10/08/2018", + "11/23/2018", + "12/24/2018", + "12/31/2018", + "01/01/2019", + "01/02/2019", + "01/03/2019", + "01/14/2019", + "02/11/2019", + "03/21/2019", + "04/29/2019", + "04/30/2019", + "05/01/2019", + "05/02/2019", + "05/03/2019", + "05/06/2019", + "07/15/2019", + "08/12/2019", + "09/16/2019", + "09/23/2019", + "10/14/2019", + "10/22/2019", + "11/04/2019", + "12/31/2019", + "01/01/2020", + "01/02/2020", + "01/03/2020", + "01/13/2020", + "02/11/2020", + "02/24/2020", + "03/20/2020", + "04/29/2020", + "05/04/2020", + "05/05/2020", + "05/06/2020", + "07/23/2020", + "07/24/2020", + "08/10/2020", + "09/21/2020", + "09/22/2020", + "11/03/2020", + "11/23/2020", + "12/31/2020", + "01/01/2021", + "01/11/2021", + "02/11/2021", + "02/23/2021", + "04/29/2021", + "05/03/2021", + "05/04/2021", + "05/05/2021", + "07/22/2021", + "07/23/2021", + "08/09/2021", + "09/20/2021", + "09/23/2021", + "11/03/2021", + "11/23/2021", + "12/31/2021", + "01/03/2022", + "01/10/2022", + "02/11/2022", + "02/23/2022", + "03/21/2022", + "04/29/2022", + "05/03/2022", + "05/04/2022", + "05/05/2022", + "07/18/2022", + "08/11/2022", + "09/19/2022", + "10/10/2022", + "11/03/2022", + "11/23/2022", + "01/02/2023", + "01/03/2023", + "01/09/2023", + "02/23/2023", + "03/21/2023", + "05/03/2023", + "05/04/2023", + "05/05/2023", + "07/17/2023", + "08/11/2023", + "09/18/2023", + "10/09/2023", + "11/03/2023", + "11/23/2023", + "01/01/2024", + "01/02/2024", + "01/03/2024", + "01/08/2024", + "02/12/2024", + "02/23/2024", + "03/20/2024", + "04/29/2024", + "05/03/2024", + "05/06/2024", + "07/15/2024", + "08/12/2024", + "09/16/2024", + "09/23/2024", + "10/14/2024", + "11/04/2024", + "12/31/2024", + "01/01/2025", + "01/02/2025", + "01/03/2025", + "01/13/2025", + "02/11/2025", + "02/24/2025", + "03/20/2025", + "04/29/2025", + "05/05/2025", + "05/06/2025", + "07/21/2025", + "08/11/2025", + "09/15/2025", + "09/23/2025", + "10/13/2025", + "11/03/2025", + "11/24/2025", + "12/31/2025" + ], "earlyCloses": {}, "lateOpens": {} }, From c35d6ee1944ad0d8c530fe6d1f1dd1d334619708 Mon Sep 17 00:00:00 2001 From: Martin-Molinero Date: Wed, 15 Jan 2025 15:04:15 -0300 Subject: [PATCH 6/7] Fix HSI future expiration function (#8534) * Fix HSI future expiration function - Fix HSI futures expiration function. Adding unit tests reproducing issue * Minor update to sample test data --- .../Future/FuturesExpiryFunctions.cs | 10 ++++--- Data/future/hkfe/daily/hsi_quote.zip | Bin 3449 -> 3449 bytes Data/future/hkfe/daily/hsi_trade.zip | Bin 1462 -> 1462 bytes Data/future/hkfe/hour/hsi_quote.zip | Bin 5026 -> 5026 bytes Data/future/hkfe/hour/hsi_trade.zip | Bin 3473 -> 3473 bytes .../Futures/FuturesExpiryFunctionsTests.cs | 27 +++++++++++++----- 6 files changed, 26 insertions(+), 11 deletions(-) diff --git a/Common/Securities/Future/FuturesExpiryFunctions.cs b/Common/Securities/Future/FuturesExpiryFunctions.cs index 2aec92aa32f3..2726abd455ca 100644 --- a/Common/Securities/Future/FuturesExpiryFunctions.cs +++ b/Common/Securities/Future/FuturesExpiryFunctions.cs @@ -912,16 +912,18 @@ public static Func FuturesExpiryFunction(Symbol symbol) // HSI Index Futures:https://www.hkex.com.hk/Products/Listed-Derivatives/Equity-Index/Hang-Seng-Index-(HSI)/Hang-Seng-Index-Futures?sc_lang=en#&product=HSI {Symbol.Create(Futures.Indices.HangSeng, SecurityType.Future, Market.HKFE), (time => { - // Short-dated Futures: Spot, next calendar month & next two calendar quarter months; and Long-dated Futures: the following 5 December months + // Short-dated Futures: + // Spot, next three calendar month & next three calendar quarter months; and + // Long-dated Futures: + // The three months of June and December plus the next three months of December // The Business Day immediately preceding the last Business Day of the Contract Month var lastDay = new DateTime(time.Year, time.Month, DateTime.DaysInMonth(time.Year, time.Month)); var holidays = FuturesExpiryUtilityFunctions.GetHolidays(Market.HKFE, Futures.Indices.HangSeng); - var lastBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDaysIfHoliday(lastDay, -1, holidays); - var priorBusinessDay = lastBusinessDay.AddDays(-1); + var lastBusinessDay = FuturesExpiryUtilityFunctions.NthLastBusinessDay(time, 1, holidays); + var priorBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDays(lastBusinessDay, -1, holidays); - priorBusinessDay = FuturesExpiryUtilityFunctions.AddBusinessDaysIfHoliday(priorBusinessDay, -1, holidays); return priorBusinessDay.Add(new TimeSpan(16, 0, 0)); }) }, diff --git a/Data/future/hkfe/daily/hsi_quote.zip b/Data/future/hkfe/daily/hsi_quote.zip index 8fdf5a5846f103207b0869bccab09ae8483655be..70cbd9c3ced3335e44e9c24ff6953d18ebadb93b 100644 GIT binary patch delta 121 zcmew<^;2qs?&Nt)YV6;?wWkB|=F?0^+2Ne&TuT`z7c*&3)a7KfnB2%CI(aV>Ty|rW zB-7;iJQ|Zzc!YsQ$WM;pB-2EuK$zIZsH5yK_GUGXrHqrA tcr_-c@Cd_A(3$MTBgI_E#4u4-e{u{j=j8c38ceoalTY$kPEO?&0RXq(B(4Ae diff --git a/Data/future/hkfe/daily/hsi_trade.zip b/Data/future/hkfe/daily/hsi_trade.zip index 3b8f60fc1ebaf4912084254ca89b38ea629887a8..b40f2b73125746c977ff4b822aaca3f902dfb941 100644 GIT binary patch delta 104 zcmdnSy^VYFE+#WZi^(6EWY|A{YflH_%_7XHj1!pxVf>9zii|M!<|alf#>w`q8j}~Y v2y+?68yOfH8yXr}OjZO6HM7Vu?`33|D62pDGK(S8Q>Mvmtd^4(v5Eiy$XO;; delta 110 zcmdnSy^VYFUdCYd{EE7CCZF2P;!KK+?1@m0J+l?#L?#nP%gKtYqI|pz>_8RiNf616 zQK^iR@33f0UdSRm`4yx5WHVMnCJn~PnXHyTVXn!|EON}dm>4F?N`kdZOrFCk1pxhi B9}EBh diff --git a/Data/future/hkfe/hour/hsi_quote.zip b/Data/future/hkfe/hour/hsi_quote.zip index 240f6bcccfe93e6cda55fe051c5921c17e3fea6d..e928bec03e50f30e675d951f1e743869da436a9b 100644 GIT binary patch delta 133 zcmZ3azDRxYcD7)4$sg_MK)hLyorew1QQ>uDoycUuXfc_QSAv^=V=YJt2ycvvXPbON zNMmxZknrRuZ1R(pgbkU6*%&52=h0_pWng3lDxI7xq{Xy`dvd>!6xaaK$@_&kMOfJw QfPfhYH5nNgl7&D#09_y?C;$Ke delta 134 zcmZ3azDRxY8g4U2%gF`8qI|pz?ED*R(?R%;ckO02o_My2Oo8mm5dOv}9yWGXs9Y|a zBkN>sVU5YXLc&~z@kR!Q#)gJQAYZD*U@FOqZv%XtSlJkW PfEfri85tOYctAV=ihLqU diff --git a/Tests/Common/Securities/Futures/FuturesExpiryFunctionsTests.cs b/Tests/Common/Securities/Futures/FuturesExpiryFunctionsTests.cs index d52e8967d9bc..06d60694e0dd 100644 --- a/Tests/Common/Securities/Futures/FuturesExpiryFunctionsTests.cs +++ b/Tests/Common/Securities/Futures/FuturesExpiryFunctionsTests.cs @@ -69,16 +69,29 @@ public void Init() } } - [Test] - public void HSIFutures() + // last day and previous are holidays + [TestCase("20250101", "20250127")] + // normal case + [TestCase("20250201", "20250227")] + [TestCase("20250301", "20250328")] + [TestCase("20250401", "20250429")] + [TestCase("20250501", "20250529")] + [TestCase("20250601", "20250627")] + [TestCase("20250701", "20250730")] + [TestCase("20250801", "20250828")] + [TestCase("20250901", "20250929")] + [TestCase("20251001", "20251030")] + [TestCase("20251101", "20251127")] + [TestCase("20251201", "20251230")] + public void HSIFutures(string input, string expectedStr) { + var date = Time.ParseDate(input); + var expected = Time.ParseDate(expectedStr); + var canonical = Symbol.Create("HSI", SecurityType.Future, Market.HKFE); var expiration = FuturesExpiryFunctions.FuturesExpiryDictionary[canonical]; - - // last day and previous are holidays - Assert.AreEqual(new DateTime(2025, 1, 27, 16, 0, 0), expiration(new DateTime(2025, 1, 1))); - // normal case - Assert.AreEqual(new DateTime(2025, 2, 27, 16, 0, 0), expiration(new DateTime(2025, 2, 1))); + var result = expiration(date); + Assert.AreEqual(expected, result.Date); } [Test] From 41f9d1bd3ac6279333dc7811191626a3faff0168 Mon Sep 17 00:00:00 2001 From: Jhonathan Abreu Date: Wed, 15 Jan 2025 16:49:55 -0400 Subject: [PATCH 7/7] Support Collective2 white-label API (#8536) * Support Collective2 white-labe api usage * Make Collective2SignalExport Destination public and assignable * Minor comment change --- ...tive2SignalExportDemonstrationAlgorithm.cs | 14 +++++++---- ...tive2SignalExportDemonstrationAlgorithm.py | 4 +++ .../SignalExports/Collective2SignalExport.cs | 25 +++++++++++-------- 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/Algorithm.CSharp/Collective2SignalExportDemonstrationAlgorithm.cs b/Algorithm.CSharp/Collective2SignalExportDemonstrationAlgorithm.cs index d556e7e32d35..9b59b1072ae0 100644 --- a/Algorithm.CSharp/Collective2SignalExportDemonstrationAlgorithm.cs +++ b/Algorithm.CSharp/Collective2SignalExportDemonstrationAlgorithm.cs @@ -49,13 +49,13 @@ public class Collective2SignalExportDemonstrationAlgorithm : QCAlgorithm, IRegre private bool _firstCall = true; private PortfolioTarget[] _targets = new PortfolioTarget[4]; - + /// /// Symbols accepted by Collective2. Collective2 accepts stock, /// future, forex and US stock option symbols /// private List _symbols = new() - { + { QuantConnect.Symbol.Create("SPY", SecurityType.Equity, Market.USA, null, null), QuantConnect.Symbol.Create("EURUSD", SecurityType.Forex, Market.Oanda, null, null), QuantConnect.Symbol.CreateFuture("ES", Market.CME, new DateTime(2023, 12, 15), null), @@ -95,15 +95,19 @@ public override void Initialize() // Initialize this flag, to check when the ema indicators crosses between themselves _emaFastIsNotSet = true; - // Set Collective2 signal export provider + // Set Collective2 signal export provider. + // If using the Collective2 white-label API, you can specify it in the constructor with the optional parameter `useWhiteLabelApi`: + // e.g. new Collective2SignalExport(_collective2ApiKey, _collective2SystemId, useWhiteLabelApi: true) + // The API url can also be overridden by setting the Destination property: + // e.g. new Collective2SignalExport(_collective2ApiKey, _collective2SystemId) { Destination = new Uri("your url") } SignalExport.AddSignalExportProviders(new Collective2SignalExport(_collective2ApiKey, _collective2SystemId)); SetWarmUp(100); } /// - /// Reduce the quantity of holdings for SPY or increase it, depending the case, - /// when the EMA's indicators crosses between themselves, then send a signal to + /// Reduce the quantity of holdings for SPY or increase it, depending the case, + /// when the EMA's indicators crosses between themselves, then send a signal to /// Collective2 API /// /// diff --git a/Algorithm.Python/Collective2SignalExportDemonstrationAlgorithm.py b/Algorithm.Python/Collective2SignalExportDemonstrationAlgorithm.py index 10a1b6b2c2c0..4a670cecb50d 100644 --- a/Algorithm.Python/Collective2SignalExportDemonstrationAlgorithm.py +++ b/Algorithm.Python/Collective2SignalExportDemonstrationAlgorithm.py @@ -61,6 +61,10 @@ def initialize(self): # Collective2 System ID: This value is found beside the system's name (strategy's name) on the main system page self.collective2_system_id = 0 + # If using the Collective2 white-label API, you can specify it in the constructor with the optional parameter `use_white_label_api`: + # e.g. Collective2SignalExport(self.collective2_apikey, self.collective2_system_id, use_white_label_api=True) + # The API url can also be overridden by setting the Destination property: + # e.g. Collective2SignalExport(self.collective2_apikey, self.collective2_system_id) { Destination = new Uri("your url") } self.signal_export.add_signal_export_providers(Collective2SignalExport(self.collective2_apikey, self.collective2_system_id)) self.first_call = True diff --git a/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs b/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs index cc496ce0bf44..71da9770d39d 100644 --- a/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs +++ b/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs @@ -41,11 +41,6 @@ public class Collective2SignalExport : BaseSignalExport /// private readonly int _systemId; - /// - /// Collective2 API endpoint - /// - private readonly Uri _destination; - /// /// Algorithm being ran /// @@ -56,6 +51,11 @@ public class Collective2SignalExport : BaseSignalExport /// private bool _isZeroPriceWarningPrinted; + /// + /// Collective2 API endpoint + /// + public Uri Destination { get; set; } + /// /// The name of this signal export /// @@ -83,18 +83,21 @@ public class Collective2SignalExport : BaseSignalExport /// /// API key provided by Collective2 /// Trading system's ID number - public Collective2SignalExport(string apiKey, int systemId) + /// Whether to use the white-label API instead of the general one + public Collective2SignalExport(string apiKey, int systemId, bool useWhiteLabelApi = false) { _apiKey = apiKey; _systemId = systemId; - _destination = new Uri("https://api4-general.collective2.com/Strategies/SetDesiredPositions"); + Destination = new Uri(useWhiteLabelApi + ? "https://api4-wl.collective2.com/Strategies/SetDesiredPositions" + : "https://api4-general.collective2.com/Strategies/SetDesiredPositions"); } /// /// Creates a JSON message with the desired positions using the expected /// Collective2 API format and then sends it /// - /// A list of holdings from the portfolio + /// A list of holdings from the portfolio /// expected to be sent to Collective2 API and the algorithm being ran /// True if the positions were sent correctly and Collective2 sent no errors, false otherwise public override bool Send(SignalExportTargetParameters parameters) @@ -120,7 +123,7 @@ public override bool Send(SignalExportTargetParameters parameters) /// /// Converts a list of targets to a list of Collective2 positions /// - /// A list of targets from the portfolio + /// A list of targets from the portfolio /// expected to be sent to Collective2 API and the algorithm being ran /// A list of Collective2 positions /// True if the given targets could be converted to a Collective2Position list, false otherwise @@ -266,7 +269,7 @@ private bool SendPositions(string message) HttpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _apiKey); //Send the message - using HttpResponseMessage response = HttpClient.PostAsync(_destination, httpMessage).Result; + using HttpResponseMessage response = HttpClient.PostAsync(Destination, httpMessage).Result; //Parse it var responseObject = response.Content.ReadFromJsonAsync().Result; @@ -333,7 +336,7 @@ private class ResponseStatus { /* Example: - "ResponseStatus": + "ResponseStatus": { "ErrorCode": ""401", "Message": ""Unauthorized",