From d03cd6e36322d6d34d730140f45c41c12ed815dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Andr=C3=A9s=20Marino=20Rojas?= <47573394+Marinovsky@users.noreply.github.com> Date: Mon, 17 Feb 2025 13:47:36 -0300 Subject: [PATCH 01/13] Initial draft of the solution --- .../SignalExports/Collective2SignalExport.cs | 197 +++++++++++++++++- .../Portfolio/SignalExportTargetTests.cs | 68 ++++-- 2 files changed, 239 insertions(+), 26 deletions(-) diff --git a/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs b/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs index b86f5946c351..bdbd72392284 100644 --- a/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs +++ b/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs @@ -15,6 +15,7 @@ using Newtonsoft.Json; using QuantConnect.Interfaces; +using QuantConnect.Securities.Forex; using QuantConnect.Util; using System; using System.Collections.Generic; @@ -157,10 +158,15 @@ protected bool ConvertHoldingsToCollective2(SignalExportTargetParameters paramet positions.Add(new Collective2Position { - C2Symbol = new C2Symbol + ExchangeSymbol = new ExchangeSymbol { - FullSymbol = symbol, - SymbolType = typeOfSymbol, + Symbol = GetSymbol(target.Symbol), + Currency = parameters.Algorithm.AccountCurrency, + SecurityExchange = GetMICExchangeCode(target.Symbol), + SecurityType = GetSecurityTypeAcronym(target.Symbol.SecurityType), + MaturityMonthYear = GetMaturityMonthYear(target.Symbol), + PutOrCall = GetPutOrCallValue(target.Symbol), + StrikePrice = GetStrikePrice(target.Symbol) }, Quantity = ConvertPercentageToQuantity(_algorithm, target), }); @@ -332,6 +338,128 @@ private class DesiredPositionResponse public List CanceledSignals { get; set; } = new List(); } + /// + /// Returns the given symbol in the expected C2 format + /// + private string GetSymbol(Symbol symbol) + { + if (symbol.SecurityType == SecurityType.Forex) + { + var forex = _algorithm.Securities[symbol] as Forex; + return $"{forex.BaseCurrency.Symbol}/{forex.QuoteCurrency.Symbol}"; + } + else if (symbol.SecurityType == SecurityType.Option) + { + return symbol.Underlying.Value; + } + else + { + return symbol.ID.Symbol; + } + } + + private string GetMICExchangeCode(Symbol symbol) + { + if (symbol.SecurityType == SecurityType.Equity || symbol.SecurityType == SecurityType.Option) + { + return "DEFAULT"; + } + + switch (symbol.ID.Market) + { + case "fxcm": + return "FXCM"; + case "india": + return "XNSE"; + case "hkfe": + return "XHKF"; + case "ose": + return "XOSE"; + case "nyseliffe": + return "XNLI"; + case "cme": + return "XCME"; + case "eurex": + return "XEUR"; + case "ice": + return "IEPA"; + case "cboe": + return "XCBO"; + case "cfe": + return "XCBF"; + case "cbot": + return "XCBT"; + case "comex": + return "XCEC"; + case "nymex": + return "XNYM"; + case "sgx": + return "XSES"; + default: + _algorithm.Debug($"The market of the symbol {symbol.ID.Symbol} was unexpected: {symbol.ID.Market}"); + return "DEFAULT"; + } + } + + /// + /// Returns the given security type in the format C2 expects + /// + private string GetSecurityTypeAcronym(SecurityType securityType) + { + switch (securityType) + { + case SecurityType.Equity: + return "CS"; + case SecurityType.Future: + return "FUT"; + case SecurityType.Option: + return "OPT"; + case SecurityType.Forex: + return "FOR"; + default: + _algorithm.Error($"Unexpected security type found: {securityType}. Collective2 just accepts: Equity, Future, Option and Stock"); + return null; + } + } + + /// + /// Returns the expiration date in the format C2 expects + /// + private string GetMaturityMonthYear(Symbol symbol) + { + if (symbol.SecurityType == SecurityType.Equity || symbol.SecurityType == SecurityType.Forex) return null; + + return $"{symbol.ID.Date:yyyyMMdd}"; + } + + private int? GetPutOrCallValue(Symbol symbol) + { + if (symbol.SecurityType == SecurityType.Option) + { + switch (symbol.ID.OptionRight) + { + case OptionRight.Put: + return 0; + case OptionRight.Call: + return 1; + } + } + + return null; + } + + private decimal? GetStrikePrice(Symbol symbol) + { + if (symbol.SecurityType == SecurityType.Option) + { + return symbol.ID.StrikePrice; + } + else + { + return null; + } + } + /// /// The C2 ResponseStatus object /// @@ -393,14 +521,14 @@ protected class Collective2Position /// /// Position symbol /// - [JsonProperty(PropertyName = "C2Symbol")] - public C2Symbol C2Symbol { get; set; } + [JsonProperty(PropertyName = "exchangeSymbol")] + public ExchangeSymbol ExchangeSymbol { get; set; } /// /// Number of shares/contracts of the given symbol. Positive quantites are long positions /// and negative short positions. /// - [JsonProperty(PropertyName = "Quantity")] + [JsonProperty(PropertyName = "quantity")] public decimal Quantity { get; set; } // number of shares, not % of the portfolio } @@ -422,5 +550,62 @@ protected class C2Symbol [JsonProperty(PropertyName = "SymbolType")] public string SymbolType { get; set; } } + + /// + /// The Collective2 symbol + /// + protected class ExchangeSymbol + { + /// + /// The exchange root symbol e.g. AAPL + /// + [JsonProperty(PropertyName = "symbol")] + public string Symbol { get; set; } + + /// + /// The 3-character ISO instrument currency. E.g. 'USD' + /// + [JsonProperty(PropertyName = "currency")] + public string Currency { get; set; } + + /// + /// The MIC Exchange code e.g. DEFAULT (for stocks & options), + /// XCME, XEUR, XICE, XLIF, XNYB, XNYM, XASX, XCBF, XCBT, XCEC, + /// XKBT, XSES. See details at http://www.iso15022.org/MIC/homepageMIC.htm + /// + [JsonProperty(PropertyName = "securityExchange")] + public string SecurityExchange { get; set; } + + + /// + /// The SecurityType e.g. 'CS'(Common Stock), 'FUT' (Future), 'OPT' (Option), 'FOR' (Forex) + /// + [JsonProperty(PropertyName = "securityType")] + public string SecurityType { get; set; } + + /// + /// The MaturityMonthYear e.g. '202103' (March 2021), or if the contract requires a day: '20210521' (May 21, 2021) + /// + [JsonProperty(PropertyName = "maturityMonthYear")] + public string MaturityMonthYear { get; set; } + + /// + /// The Option PutOrCall e.g. 0 = Put, 1 = Call + /// + [JsonProperty(PropertyName = "putOrCall")] + public int? PutOrCall { get; set; } + + /// + /// The ISO Option Strike Price. Zero means none + /// + [JsonProperty(PropertyName = "strikePrice")] + public decimal? StrikePrice { get; set; } + + /// + /// The multiplier to apply to the Exchange price to get the C2-formatted price. Default is 1 + /// + [JsonProperty(PropertyName = "priceMultiplier")] + public decimal PriceMultiplier { get; set; } = 1; + } } } diff --git a/Tests/Algorithm/Framework/Portfolio/SignalExportTargetTests.cs b/Tests/Algorithm/Framework/Portfolio/SignalExportTargetTests.cs index d6ba4f6c181a..c4647bb7dc4c 100644 --- a/Tests/Algorithm/Framework/Portfolio/SignalExportTargetTests.cs +++ b/Tests/Algorithm/Framework/Portfolio/SignalExportTargetTests.cs @@ -30,35 +30,23 @@ namespace QuantConnect.Tests.Algorithm.Framework.Portfolio [TestFixture] public class SignalExportTargetTests { - [Test] - public void SendsTargetsToCollective2Appropiately() + [TestCaseSource(nameof(SendsTargetsToCollective2AppropiatelyTestCases))] + public void SendsTargetsToCollective2Appropiately(string currency, Symbol symbol, decimal quantity, string expectedMessage) { - var symbols = new List() - { - Symbols.SPY, - Symbols.EURUSD, - Symbols.Future_ESZ18_Dec2018, - Symbols.SPY_C_192_Feb19_2016 - }; - - var targetList = new List() - { - new PortfolioTarget(Symbols.SPY, (decimal)0.2), - new PortfolioTarget(Symbols.EURUSD, (decimal)0.3), - new PortfolioTarget(Symbols.Future_ESZ18_Dec2018, (decimal)0.2), - new PortfolioTarget(Symbols.SPY_C_192_Feb19_2016, (decimal)0.3) - }; + var targetList = new List() { new(symbol, quantity) }; var algorithm = new AlgorithmStub(); - AddSymbols(symbols, algorithm); + algorithm.SetDateTime(new DateTime(2016, 02, 16, 11, 53, 30)); + algorithm.Portfolio.SetAccountCurrency(currency); + var security = algorithm.AddSecurity(symbol); + security.SetMarketPrice(new Tick { Value = 100 }); + algorithm.Portfolio.SetCash(50000); using var manager = new Collective2SignalExportHandler("", 0); var message = manager.GetMessageSent(new SignalExportTargetParameters { Targets = targetList, Algorithm = algorithm }); - var expectedMessage = @"{""StrategyId"":0,""Positions"":[{""C2Symbol"":{""FullSymbol"":""SPY"",""SymbolType"":""stock""},""Quantity"":99.0},{""C2Symbol"":{""FullSymbol"":""EURUSD"",""SymbolType"":""forex""},""Quantity"":149.0},{""C2Symbol"":{""FullSymbol"":""@ESZ8"",""SymbolType"":""future""},""Quantity"":2.0},{""C2Symbol"":{""FullSymbol"":""SPY1619B192"",""SymbolType"":""option""},""Quantity"":1.0}]}"; - Assert.AreEqual(expectedMessage, message); } @@ -388,6 +376,46 @@ public void SignalExportManagerReturnsFalseWhenNegativeTotalPortfolioValue() Assert.IsFalse(signalExportManagerHandler.GetPortfolioTargets(out _)); } + private static object[] SendsTargetsToCollective2AppropiatelyTestCases = + { + new object[] { "USD", Symbols.SPY, 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""SPY"",""currency"":""USD"",""securityExchange"":""DEFAULT"",""securityType"":""CS"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":99.0}]}" }, + new object[] { "USD", Symbols.EURUSD, 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""EUR/USD"",""currency"":""USD"",""securityExchange"":""DEFAULT"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":99.0}]}" }, + new object[] { "USD", Symbols.Future_ESZ18_Dec2018, 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""ES"",""currency"":""USD"",""securityExchange"":""XCME"",""securityType"":""FUT"",""maturityMonthYear"":""20181221"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":2.0}]}" }, + new object[] { "USD", Symbols.Future_CLF19_Jan2019, 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""CL"",""currency"":""USD"",""securityExchange"":""XNYM"",""securityType"":""FUT"",""maturityMonthYear"":""20181219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":1.0}]}" }, + new object[] { "USD", Symbols.Fut_SPY_Feb19_2016, 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""ES"",""currency"":""USD"",""securityExchange"":""XCME"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":2.0}]}" }, + new object[] { "USD", Symbols.SPY_C_192_Feb19_2016, 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""SPY"",""currency"":""USD"",""securityExchange"":""DEFAULT"",""securityType"":""OPT"",""maturityMonthYear"":""20160219"",""putOrCall"":1,""strikePrice"":192.0,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbols.SPY_P_192_Feb19_2016, 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""SPY"",""currency"":""USD"",""securityExchange"":""DEFAULT"",""securityType"":""OPT"",""maturityMonthYear"":""20160219"",""putOrCall"":0,""strikePrice"":192.0,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbol.Create("EURUSD", SecurityType.Forex, Market.FXCM), 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""EUR/USD"",""currency"":""USD"",""securityExchange"":""FXCM"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbol.CreateFuture("NIFTY", Market.India, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NIFTY"",""currency"":""USD"",""securityExchange"":""XNSE"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbol.CreateFuture("HSI", Market.HKFE, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""HSI"",""currency"":""USD"",""securityExchange"":""XHKF"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbol.CreateFuture("ZG", Market.NYSELIFFE, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""ZG"",""currency"":""USD"",""securityExchange"":""XNLI"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbol.CreateFuture("FESX", Market.EUREX, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""FESX"",""currency"":""USD"",""securityExchange"":""XEUR"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbol.CreateFuture("KC", Market.ICE, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""KC"",""currency"":""USD"",""securityExchange"":""IEPA"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbol.CreateFuture("VIX", Market.CFE, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""VIX"",""currency"":""USD"",""securityExchange"":""XCBF"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbol.CreateFuture("ZC", Market.CBOT, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""ZC"",""currency"":""USD"",""securityExchange"":""XCBT"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbol.CreateFuture("GC", Market.COMEX, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""GC"",""currency"":""USD"",""securityExchange"":""XCEC"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbol.CreateFuture("CL", Market.NYMEX, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""CL"",""currency"":""USD"",""securityExchange"":""XNYM"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbol.CreateFuture("NK", Market.SGX, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NK"",""currency"":""USD"",""securityExchange"":""XSES"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbols.SPY, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""SPY"",""currency"":""EUR"",""securityExchange"":""DEFAULT"",""securityType"":""CS"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbols.EURUSD, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""EUR/USD"",""currency"":""EUR"",""securityExchange"":""DEFAULT"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbols.Future_ESZ18_Dec2018, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""ES"",""currency"":""EUR"",""securityExchange"":""XCME"",""securityType"":""FUT"",""maturityMonthYear"":""20181221"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbols.Future_CLF19_Jan2019, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""CL"",""currency"":""EUR"",""securityExchange"":""XNYM"",""securityType"":""FUT"",""maturityMonthYear"":""20181219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbols.Fut_SPY_Feb19_2016, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""ES"",""currency"":""EUR"",""securityExchange"":""XCME"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbols.SPY_C_192_Feb19_2016, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""SPY"",""currency"":""EUR"",""securityExchange"":""DEFAULT"",""securityType"":""OPT"",""maturityMonthYear"":""20160219"",""putOrCall"":1,""strikePrice"":192.0,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbols.SPY_P_192_Feb19_2016, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""SPY"",""currency"":""EUR"",""securityExchange"":""DEFAULT"",""securityType"":""OPT"",""maturityMonthYear"":""20160219"",""putOrCall"":0,""strikePrice"":192.0,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbol.Create("EURUSD", SecurityType.Forex, Market.FXCM), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""EUR/USD"",""currency"":""EUR"",""securityExchange"":""FXCM"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbol.CreateFuture("NIFTY", Market.India, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NIFTY"",""currency"":""EUR"",""securityExchange"":""XNSE"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbol.CreateFuture("HSI", Market.HKFE, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""HSI"",""currency"":""EUR"",""securityExchange"":""XHKF"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbol.CreateFuture("ZG", Market.NYSELIFFE, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""ZG"",""currency"":""EUR"",""securityExchange"":""XNLI"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbol.CreateFuture("FESX", Market.EUREX, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""FESX"",""currency"":""EUR"",""securityExchange"":""XEUR"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbol.CreateFuture("KC", Market.ICE, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""KC"",""currency"":""EUR"",""securityExchange"":""IEPA"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbol.CreateFuture("VIX", Market.CFE, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""VIX"",""currency"":""EUR"",""securityExchange"":""XCBF"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbol.CreateFuture("ZC", Market.CBOT, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""ZC"",""currency"":""EUR"",""securityExchange"":""XCBT"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbol.CreateFuture("GC", Market.COMEX, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""GC"",""currency"":""EUR"",""securityExchange"":""XCEC"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbol.CreateFuture("CL", Market.NYMEX, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""CL"",""currency"":""EUR"",""securityExchange"":""XNYM"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbol.CreateFuture("NK", Market.SGX, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NK"",""currency"":""EUR"",""securityExchange"":""XSES"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + }; + private static void AddSymbols(List symbols, QCAlgorithm algorithm) { algorithm.SetDateTime(new DateTime(2016, 02, 16, 11, 53, 30)); From c7d41c875679e47027cadbb6c66fd93f9ef3df43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Andr=C3=A9s=20Marino=20Rojas?= <47573394+Marinovsky@users.noreply.github.com> Date: Mon, 17 Feb 2025 13:57:48 -0300 Subject: [PATCH 02/13] Remove old format --- .../SignalExports/Collective2SignalExport.cs | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs b/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs index bdbd72392284..d67e98ed7ea9 100644 --- a/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs +++ b/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs @@ -532,25 +532,6 @@ protected class Collective2Position public decimal Quantity { get; set; } // number of shares, not % of the portfolio } - /// - /// The Collective2 symbol - /// - protected class C2Symbol - { - /// - /// The The full native C2 symbol e.g. BSRR2121Q22.5 - /// - [JsonProperty(PropertyName = "FullSymbol")] - public string FullSymbol { get; set; } - - - /// - /// The type of instrument. e.g. 'stock', 'option', 'future', 'forex' - /// - [JsonProperty(PropertyName = "SymbolType")] - public string SymbolType { get; set; } - } - /// /// The Collective2 symbol /// From b82f6bae4f242ed38fa2ff485fc5d7554c548eb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Andr=C3=A9s=20Marino=20Rojas?= <47573394+Marinovsky@users.noreply.github.com> Date: Mon, 17 Feb 2025 20:38:52 -0300 Subject: [PATCH 03/13] Address requested changes --- .../SignalExports/Collective2SignalExport.cs | 74 +++---------------- 1 file changed, 11 insertions(+), 63 deletions(-) diff --git a/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs b/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs index d67e98ed7ea9..1dce2df30a44 100644 --- a/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs +++ b/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs @@ -141,24 +141,9 @@ protected bool ConvertHoldingsToCollective2(SignalExportTargetParameters paramet return false; } - if (!ConvertTypeOfSymbol(target.Symbol, out string typeOfSymbol)) - { - return false; - } - - var symbol = _algorithm.Ticker(target.Symbol); - if (target.Symbol.SecurityType == SecurityType.Future) - { - symbol = $"@{SymbolRepresentation.GenerateFutureTicker(target.Symbol.ID.Symbol, target.Symbol.ID.Date, doubleDigitsYear: false, includeExpirationDate: false)}"; - } - else if (target.Symbol.SecurityType.IsOption()) - { - symbol = SymbolRepresentation.GenerateOptionTicker(target.Symbol); - } - positions.Add(new Collective2Position { - ExchangeSymbol = new ExchangeSymbol + ExchangeSymbol = new C2ExchangeSymbol { Symbol = GetSymbol(target.Symbol), Currency = parameters.Algorithm.AccountCurrency, @@ -175,46 +160,6 @@ protected bool ConvertHoldingsToCollective2(SignalExportTargetParameters paramet return true; } - /// - /// Classifies a symbol type into the possible symbol types values defined - /// by Collective2 API. - /// - /// Symbol of the desired position - /// The type of the symbol according to Collective2 API - /// True if the symbol's type is supported by Collective2, false otherwise - private bool ConvertTypeOfSymbol(Symbol targetSymbol, out string typeOfSymbol) - { - switch (targetSymbol.SecurityType) - { - case SecurityType.Equity: - typeOfSymbol = "stock"; - break; - case SecurityType.Option: - typeOfSymbol = "option"; - break; - case SecurityType.Future: - typeOfSymbol = "future"; - break; - case SecurityType.Forex: - typeOfSymbol = "forex"; - break; - case SecurityType.IndexOption: - typeOfSymbol = "option"; - break; - default: - typeOfSymbol = "NotImplemented"; - break; - } - - if (typeOfSymbol == "NotImplemented") - { - _algorithm.Error($"{targetSymbol.SecurityType} security type is not supported by Collective2."); - return false; - } - - return true; - } - /// /// Converts a given percentage of a position into the number of shares of it /// @@ -348,7 +293,7 @@ private string GetSymbol(Symbol symbol) var forex = _algorithm.Securities[symbol] as Forex; return $"{forex.BaseCurrency.Symbol}/{forex.QuoteCurrency.Symbol}"; } - else if (symbol.SecurityType == SecurityType.Option) + else if (symbol.SecurityType.IsOption()) { return symbol.Underlying.Value; } @@ -360,7 +305,7 @@ private string GetSymbol(Symbol symbol) private string GetMICExchangeCode(Symbol symbol) { - if (symbol.SecurityType == SecurityType.Equity || symbol.SecurityType == SecurityType.Option) + if (symbol.SecurityType == SecurityType.Equity || symbol.SecurityType.IsOption()) { return "DEFAULT"; } @@ -411,8 +356,11 @@ private string GetSecurityTypeAcronym(SecurityType securityType) case SecurityType.Equity: return "CS"; case SecurityType.Future: + case SecurityType.CryptoFuture: return "FUT"; case SecurityType.Option: + case SecurityType.FutureOption: + case SecurityType.IndexOption: return "OPT"; case SecurityType.Forex: return "FOR"; @@ -429,12 +377,12 @@ private string GetMaturityMonthYear(Symbol symbol) { if (symbol.SecurityType == SecurityType.Equity || symbol.SecurityType == SecurityType.Forex) return null; - return $"{symbol.ID.Date:yyyyMMdd}"; + return $"{symbol.GetDelistingDate():yyyyMMdd}"; } private int? GetPutOrCallValue(Symbol symbol) { - if (symbol.SecurityType == SecurityType.Option) + if (symbol.SecurityType.IsOption()) { switch (symbol.ID.OptionRight) { @@ -450,7 +398,7 @@ private string GetMaturityMonthYear(Symbol symbol) private decimal? GetStrikePrice(Symbol symbol) { - if (symbol.SecurityType == SecurityType.Option) + if (symbol.SecurityType.IsOption()) { return symbol.ID.StrikePrice; } @@ -522,7 +470,7 @@ protected class Collective2Position /// Position symbol /// [JsonProperty(PropertyName = "exchangeSymbol")] - public ExchangeSymbol ExchangeSymbol { get; set; } + public C2ExchangeSymbol ExchangeSymbol { get; set; } /// /// Number of shares/contracts of the given symbol. Positive quantites are long positions @@ -535,7 +483,7 @@ protected class Collective2Position /// /// The Collective2 symbol /// - protected class ExchangeSymbol + protected class C2ExchangeSymbol { /// /// The exchange root symbol e.g. AAPL From e1657a02d49b89a032a4c0a52f9d34041a61321c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Andr=C3=A9s=20Marino=20Rojas?= <47573394+Marinovsky@users.noreply.github.com> Date: Tue, 18 Feb 2025 12:17:20 -0300 Subject: [PATCH 04/13] Add Index option test case --- .../Portfolio/SignalExports/Collective2SignalExport.cs | 4 +--- .../Algorithm/Framework/Portfolio/SignalExportTargetTests.cs | 1 + 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs b/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs index 1dce2df30a44..f57cfd2d94c9 100644 --- a/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs +++ b/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs @@ -356,16 +356,14 @@ private string GetSecurityTypeAcronym(SecurityType securityType) case SecurityType.Equity: return "CS"; case SecurityType.Future: - case SecurityType.CryptoFuture: return "FUT"; case SecurityType.Option: - case SecurityType.FutureOption: case SecurityType.IndexOption: return "OPT"; case SecurityType.Forex: return "FOR"; default: - _algorithm.Error($"Unexpected security type found: {securityType}. Collective2 just accepts: Equity, Future, Option and Stock"); + _algorithm.Error($"Unexpected security type found: {securityType}. Collective2 just accepts: Equity, Future, Option, Index Option and Stock"); return null; } } diff --git a/Tests/Algorithm/Framework/Portfolio/SignalExportTargetTests.cs b/Tests/Algorithm/Framework/Portfolio/SignalExportTargetTests.cs index c4647bb7dc4c..19768c6ea19f 100644 --- a/Tests/Algorithm/Framework/Portfolio/SignalExportTargetTests.cs +++ b/Tests/Algorithm/Framework/Portfolio/SignalExportTargetTests.cs @@ -386,6 +386,7 @@ public void SignalExportManagerReturnsFalseWhenNegativeTotalPortfolioValue() new object[] { "USD", Symbols.SPY_C_192_Feb19_2016, 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""SPY"",""currency"":""USD"",""securityExchange"":""DEFAULT"",""securityType"":""OPT"",""maturityMonthYear"":""20160219"",""putOrCall"":1,""strikePrice"":192.0,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "USD", Symbols.SPY_P_192_Feb19_2016, 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""SPY"",""currency"":""USD"",""securityExchange"":""DEFAULT"",""securityType"":""OPT"",""maturityMonthYear"":""20160219"",""putOrCall"":0,""strikePrice"":192.0,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "USD", Symbol.Create("EURUSD", SecurityType.Forex, Market.FXCM), 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""EUR/USD"",""currency"":""USD"",""securityExchange"":""FXCM"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbol.Create("NQX", SecurityType.IndexOption, Market.USA), 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NQX"",""currency"":""USD"",""securityExchange"":""DEFAULT"",""securityType"":""OPT"",""maturityMonthYear"":""20501231"",""putOrCall"":1,""strikePrice"":0.0,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "USD", Symbol.CreateFuture("NIFTY", Market.India, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NIFTY"",""currency"":""USD"",""securityExchange"":""XNSE"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "USD", Symbol.CreateFuture("HSI", Market.HKFE, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""HSI"",""currency"":""USD"",""securityExchange"":""XHKF"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "USD", Symbol.CreateFuture("ZG", Market.NYSELIFFE, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""ZG"",""currency"":""USD"",""securityExchange"":""XNLI"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, From 33e5947908af1737821113f0ddc0e5a661ff5ac2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Andr=C3=A9s=20Marino=20Rojas?= <47573394+Marinovsky@users.noreply.github.com> Date: Tue, 18 Feb 2025 12:39:22 -0300 Subject: [PATCH 05/13] nit changes --- .../Portfolio/SignalExports/Collective2SignalExport.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs b/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs index f57cfd2d94c9..8c74c59eaf21 100644 --- a/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs +++ b/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs @@ -141,6 +141,9 @@ protected bool ConvertHoldingsToCollective2(SignalExportTargetParameters paramet return false; } + var securityType = GetSecurityTypeAcronym(target.Symbol.SecurityType); + if (securityType == null) return false; + positions.Add(new Collective2Position { ExchangeSymbol = new C2ExchangeSymbol @@ -148,7 +151,7 @@ protected bool ConvertHoldingsToCollective2(SignalExportTargetParameters paramet Symbol = GetSymbol(target.Symbol), Currency = parameters.Algorithm.AccountCurrency, SecurityExchange = GetMICExchangeCode(target.Symbol), - SecurityType = GetSecurityTypeAcronym(target.Symbol.SecurityType), + SecurityType = securityType, MaturityMonthYear = GetMaturityMonthYear(target.Symbol), PutOrCall = GetPutOrCallValue(target.Symbol), StrikePrice = GetStrikePrice(target.Symbol) @@ -341,7 +344,7 @@ private string GetMICExchangeCode(Symbol symbol) case "sgx": return "XSES"; default: - _algorithm.Debug($"The market of the symbol {symbol.ID.Symbol} was unexpected: {symbol.ID.Market}"); + _algorithm.Debug($"The market of the symbol {symbol.ID.Symbol} was unexpected: {symbol.ID.Market}. Using 'DEFAULT' as market"); return "DEFAULT"; } } From 82ee546032cc39af1bd5906b824a8f3133cdc9aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Andr=C3=A9s=20Marino=20Rojas?= <47573394+Marinovsky@users.noreply.github.com> Date: Wed, 19 Feb 2025 13:08:43 -0300 Subject: [PATCH 06/13] Address Martin reviews, improve unit tests and solve 8577 --- .../SignalExports/Collective2SignalExport.cs | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs b/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs index 8c74c59eaf21..104930d19015 100644 --- a/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs +++ b/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs @@ -144,6 +144,9 @@ protected bool ConvertHoldingsToCollective2(SignalExportTargetParameters paramet var securityType = GetSecurityTypeAcronym(target.Symbol.SecurityType); if (securityType == null) return false; + var maturityMonthYear = GetMaturityMonthYear(target.Symbol); + if (maturityMonthYear?.Length == 0) return false; + positions.Add(new Collective2Position { ExchangeSymbol = new C2ExchangeSymbol @@ -152,7 +155,7 @@ protected bool ConvertHoldingsToCollective2(SignalExportTargetParameters paramet Currency = parameters.Algorithm.AccountCurrency, SecurityExchange = GetMICExchangeCode(target.Symbol), SecurityType = securityType, - MaturityMonthYear = GetMaturityMonthYear(target.Symbol), + MaturityMonthYear = maturityMonthYear, PutOrCall = GetPutOrCallValue(target.Symbol), StrikePrice = GetStrikePrice(target.Symbol) }, @@ -291,10 +294,9 @@ private class DesiredPositionResponse /// private string GetSymbol(Symbol symbol) { - if (symbol.SecurityType == SecurityType.Forex) + if (symbol.SecurityType == SecurityType.Forex && CurrencyPairUtil.TryDecomposeCurrencyPair(symbol, out var baseCurrency, out var quoteCurrency)) { - var forex = _algorithm.Securities[symbol] as Forex; - return $"{forex.BaseCurrency.Symbol}/{forex.QuoteCurrency.Symbol}"; + return $"{baseCurrency}/{quoteCurrency}"; } else if (symbol.SecurityType.IsOption()) { @@ -376,9 +378,18 @@ private string GetSecurityTypeAcronym(SecurityType securityType) /// private string GetMaturityMonthYear(Symbol symbol) { - if (symbol.SecurityType == SecurityType.Equity || symbol.SecurityType == SecurityType.Forex) return null; + var delistingDate = symbol.GetDelistingDate(); + if (delistingDate < DateTime.UtcNow) + { + _algorithm.Error($"Instrument {symbol} has already expired. Its delisting date was: {delistingDate}. No signal will be sent to Collective2."); + return string.Empty; + } + else if (delistingDate == Time.EndOfTime) + { + return null; + } - return $"{symbol.GetDelistingDate():yyyyMMdd}"; + return $"{delistingDate:yyyyMMdd}"; } private int? GetPutOrCallValue(Symbol symbol) From db211c050106c0290eac8f09675c7104358a9686 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Andr=C3=A9s=20Marino=20Rojas?= <47573394+Marinovsky@users.noreply.github.com> Date: Wed, 19 Feb 2025 13:09:17 -0300 Subject: [PATCH 07/13] Add unit tests --- .../Portfolio/SignalExportTargetTests.cs | 68 ++++++++++--------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/Tests/Algorithm/Framework/Portfolio/SignalExportTargetTests.cs b/Tests/Algorithm/Framework/Portfolio/SignalExportTargetTests.cs index 19768c6ea19f..85885a90db37 100644 --- a/Tests/Algorithm/Framework/Portfolio/SignalExportTargetTests.cs +++ b/Tests/Algorithm/Framework/Portfolio/SignalExportTargetTests.cs @@ -379,42 +379,46 @@ public void SignalExportManagerReturnsFalseWhenNegativeTotalPortfolioValue() private static object[] SendsTargetsToCollective2AppropiatelyTestCases = { new object[] { "USD", Symbols.SPY, 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""SPY"",""currency"":""USD"",""securityExchange"":""DEFAULT"",""securityType"":""CS"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":99.0}]}" }, - new object[] { "USD", Symbols.EURUSD, 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""EUR/USD"",""currency"":""USD"",""securityExchange"":""DEFAULT"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":99.0}]}" }, - new object[] { "USD", Symbols.Future_ESZ18_Dec2018, 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""ES"",""currency"":""USD"",""securityExchange"":""XCME"",""securityType"":""FUT"",""maturityMonthYear"":""20181221"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":2.0}]}" }, - new object[] { "USD", Symbols.Future_CLF19_Jan2019, 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""CL"",""currency"":""USD"",""securityExchange"":""XNYM"",""securityType"":""FUT"",""maturityMonthYear"":""20181219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":1.0}]}" }, - new object[] { "USD", Symbols.Fut_SPY_Feb19_2016, 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""ES"",""currency"":""USD"",""securityExchange"":""XCME"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":2.0}]}" }, - new object[] { "USD", Symbols.SPY_C_192_Feb19_2016, 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""SPY"",""currency"":""USD"",""securityExchange"":""DEFAULT"",""securityType"":""OPT"",""maturityMonthYear"":""20160219"",""putOrCall"":1,""strikePrice"":192.0,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "USD", Symbols.SPY_P_192_Feb19_2016, 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""SPY"",""currency"":""USD"",""securityExchange"":""DEFAULT"",""securityType"":""OPT"",""maturityMonthYear"":""20160219"",""putOrCall"":0,""strikePrice"":192.0,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbols.EURUSD, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""EUR/USD"",""currency"":""USD"",""securityExchange"":""DEFAULT"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbols.EURGBP, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""EUR/GBP"",""currency"":""USD"",""securityExchange"":""DEFAULT"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbols.GBPJPY, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""GBP/JPY"",""currency"":""USD"",""securityExchange"":""DEFAULT"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbols.GBPUSD, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""GBP/USD"",""currency"":""USD"",""securityExchange"":""DEFAULT"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbols.USDJPY, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""USD/JPY"",""currency"":""USD"",""securityExchange"":""DEFAULT"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbols.CreateOptionSymbol("SPY", OptionRight.Call, 192m, new DateTime(2016, 02, 19)), 0.2m, @"{""StrategyId"":0,""Positions"":[]}" }, + new object[] { "USD", Symbols.CreateOptionSymbol("SPY", OptionRight.Call, 192m, new DateTime(2056, 02, 19)), 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""SPY"",""currency"":""USD"",""securityExchange"":""DEFAULT"",""securityType"":""OPT"",""maturityMonthYear"":""20560218"",""putOrCall"":1,""strikePrice"":192.0,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbols.CreateOptionSymbol("SPY", OptionRight.Put, 192m, new DateTime(2056, 02, 19)), 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""SPY"",""currency"":""USD"",""securityExchange"":""DEFAULT"",""securityType"":""OPT"",""maturityMonthYear"":""20560218"",""putOrCall"":0,""strikePrice"":192.0,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "USD", Symbol.Create("EURUSD", SecurityType.Forex, Market.FXCM), 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""EUR/USD"",""currency"":""USD"",""securityExchange"":""FXCM"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "USD", Symbol.Create("NQX", SecurityType.IndexOption, Market.USA), 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NQX"",""currency"":""USD"",""securityExchange"":""DEFAULT"",""securityType"":""OPT"",""maturityMonthYear"":""20501231"",""putOrCall"":1,""strikePrice"":0.0,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "USD", Symbol.CreateFuture("NIFTY", Market.India, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NIFTY"",""currency"":""USD"",""securityExchange"":""XNSE"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "USD", Symbol.CreateFuture("HSI", Market.HKFE, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""HSI"",""currency"":""USD"",""securityExchange"":""XHKF"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "USD", Symbol.CreateFuture("ZG", Market.NYSELIFFE, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""ZG"",""currency"":""USD"",""securityExchange"":""XNLI"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "USD", Symbol.CreateFuture("FESX", Market.EUREX, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""FESX"",""currency"":""USD"",""securityExchange"":""XEUR"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "USD", Symbol.CreateFuture("KC", Market.ICE, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""KC"",""currency"":""USD"",""securityExchange"":""IEPA"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "USD", Symbol.CreateFuture("VIX", Market.CFE, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""VIX"",""currency"":""USD"",""securityExchange"":""XCBF"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "USD", Symbol.CreateFuture("ZC", Market.CBOT, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""ZC"",""currency"":""USD"",""securityExchange"":""XCBT"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "USD", Symbol.CreateFuture("GC", Market.COMEX, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""GC"",""currency"":""USD"",""securityExchange"":""XCEC"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "USD", Symbol.CreateFuture("CL", Market.NYMEX, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""CL"",""currency"":""USD"",""securityExchange"":""XNYM"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "USD", Symbol.CreateFuture("NK", Market.SGX, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NK"",""currency"":""USD"",""securityExchange"":""XSES"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbol.Create("NQX", SecurityType.IndexOption, Market.USA), 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NQX"",""currency"":""USD"",""securityExchange"":""DEFAULT"",""securityType"":""OPT"",""maturityMonthYear"":null,""putOrCall"":1,""strikePrice"":0.0,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbol.CreateFuture("NIFTY", Market.India, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NIFTY"",""currency"":""USD"",""securityExchange"":""XNSE"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbol.CreateFuture("HSI", Market.HKFE, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""HSI"",""currency"":""USD"",""securityExchange"":""XHKF"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbol.CreateFuture("ZG", Market.NYSELIFFE, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""ZG"",""currency"":""USD"",""securityExchange"":""XNLI"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbol.CreateFuture("FESX", Market.EUREX, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""FESX"",""currency"":""USD"",""securityExchange"":""XEUR"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbol.CreateFuture("KC", Market.ICE, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""KC"",""currency"":""USD"",""securityExchange"":""IEPA"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbol.CreateFuture("VIX", Market.CFE, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""VIX"",""currency"":""USD"",""securityExchange"":""XCBF"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbol.CreateFuture("ZC", Market.CBOT, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""ZC"",""currency"":""USD"",""securityExchange"":""XCBT"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbol.CreateFuture("GC", Market.COMEX, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""GC"",""currency"":""USD"",""securityExchange"":""XCEC"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbol.CreateFuture("CL", Market.NYMEX, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""CL"",""currency"":""USD"",""securityExchange"":""XNYM"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbol.CreateFuture("NK", Market.SGX, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NK"",""currency"":""USD"",""securityExchange"":""XSES"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "EUR", Symbols.SPY, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""SPY"",""currency"":""EUR"",""securityExchange"":""DEFAULT"",""securityType"":""CS"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "EUR", Symbols.EURUSD, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""EUR/USD"",""currency"":""EUR"",""securityExchange"":""DEFAULT"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "EUR", Symbols.Future_ESZ18_Dec2018, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""ES"",""currency"":""EUR"",""securityExchange"":""XCME"",""securityType"":""FUT"",""maturityMonthYear"":""20181221"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "EUR", Symbols.Future_CLF19_Jan2019, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""CL"",""currency"":""EUR"",""securityExchange"":""XNYM"",""securityType"":""FUT"",""maturityMonthYear"":""20181219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "EUR", Symbols.Fut_SPY_Feb19_2016, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""ES"",""currency"":""EUR"",""securityExchange"":""XCME"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "EUR", Symbols.SPY_C_192_Feb19_2016, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""SPY"",""currency"":""EUR"",""securityExchange"":""DEFAULT"",""securityType"":""OPT"",""maturityMonthYear"":""20160219"",""putOrCall"":1,""strikePrice"":192.0,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "EUR", Symbols.SPY_P_192_Feb19_2016, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""SPY"",""currency"":""EUR"",""securityExchange"":""DEFAULT"",""securityType"":""OPT"",""maturityMonthYear"":""20160219"",""putOrCall"":0,""strikePrice"":192.0,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbols.EURGBP, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""EUR/GBP"",""currency"":""EUR"",""securityExchange"":""DEFAULT"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbols.GBPJPY, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""GBP/JPY"",""currency"":""EUR"",""securityExchange"":""DEFAULT"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbols.GBPUSD, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""GBP/USD"",""currency"":""EUR"",""securityExchange"":""DEFAULT"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbols.USDJPY, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""USD/JPY"",""currency"":""EUR"",""securityExchange"":""DEFAULT"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbols.CreateOptionSymbol("SPY", OptionRight.Call, 192m, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""SPY"",""currency"":""EUR"",""securityExchange"":""DEFAULT"",""securityType"":""OPT"",""maturityMonthYear"":""20560218"",""putOrCall"":1,""strikePrice"":192.0,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbols.CreateOptionSymbol("SPY", OptionRight.Put, 192m, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""SPY"",""currency"":""EUR"",""securityExchange"":""DEFAULT"",""securityType"":""OPT"",""maturityMonthYear"":""20560218"",""putOrCall"":0,""strikePrice"":192.0,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbols.CreateOptionSymbol("SPY", OptionRight.Call, 192m, new DateTime(2016, 02, 19)), 0.2m, @"{""StrategyId"":0,""Positions"":[]}" }, new object[] { "EUR", Symbol.Create("EURUSD", SecurityType.Forex, Market.FXCM), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""EUR/USD"",""currency"":""EUR"",""securityExchange"":""FXCM"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "EUR", Symbol.CreateFuture("NIFTY", Market.India, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NIFTY"",""currency"":""EUR"",""securityExchange"":""XNSE"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "EUR", Symbol.CreateFuture("HSI", Market.HKFE, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""HSI"",""currency"":""EUR"",""securityExchange"":""XHKF"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "EUR", Symbol.CreateFuture("ZG", Market.NYSELIFFE, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""ZG"",""currency"":""EUR"",""securityExchange"":""XNLI"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "EUR", Symbol.CreateFuture("FESX", Market.EUREX, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""FESX"",""currency"":""EUR"",""securityExchange"":""XEUR"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "EUR", Symbol.CreateFuture("KC", Market.ICE, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""KC"",""currency"":""EUR"",""securityExchange"":""IEPA"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "EUR", Symbol.CreateFuture("VIX", Market.CFE, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""VIX"",""currency"":""EUR"",""securityExchange"":""XCBF"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "EUR", Symbol.CreateFuture("ZC", Market.CBOT, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""ZC"",""currency"":""EUR"",""securityExchange"":""XCBT"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "EUR", Symbol.CreateFuture("GC", Market.COMEX, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""GC"",""currency"":""EUR"",""securityExchange"":""XCEC"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "EUR", Symbol.CreateFuture("CL", Market.NYMEX, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""CL"",""currency"":""EUR"",""securityExchange"":""XNYM"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "EUR", Symbol.CreateFuture("NK", Market.SGX, new DateTime(2016, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NK"",""currency"":""EUR"",""securityExchange"":""XSES"",""securityType"":""FUT"",""maturityMonthYear"":""20160219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbol.CreateFuture("NIFTY", Market.India, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NIFTY"",""currency"":""EUR"",""securityExchange"":""XNSE"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbol.CreateFuture("HSI", Market.HKFE, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""HSI"",""currency"":""EUR"",""securityExchange"":""XHKF"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbol.CreateFuture("ZG", Market.NYSELIFFE, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""ZG"",""currency"":""EUR"",""securityExchange"":""XNLI"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbol.CreateFuture("FESX", Market.EUREX, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""FESX"",""currency"":""EUR"",""securityExchange"":""XEUR"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbol.CreateFuture("KC", Market.ICE, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""KC"",""currency"":""EUR"",""securityExchange"":""IEPA"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbol.CreateFuture("VIX", Market.CFE, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""VIX"",""currency"":""EUR"",""securityExchange"":""XCBF"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbol.CreateFuture("ZC", Market.CBOT, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""ZC"",""currency"":""EUR"",""securityExchange"":""XCBT"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbol.CreateFuture("GC", Market.COMEX, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""GC"",""currency"":""EUR"",""securityExchange"":""XCEC"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbol.CreateFuture("CL", Market.NYMEX, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""CL"",""currency"":""EUR"",""securityExchange"":""XNYM"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbol.CreateFuture("NK", Market.SGX, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NK"",""currency"":""EUR"",""securityExchange"":""XSES"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, }; private static void AddSymbols(List symbols, QCAlgorithm algorithm) From 26a78e2d9e287f84dd0e1f922dcf6f48d8b89129 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Andr=C3=A9s=20Marino=20Rojas?= <47573394+Marinovsky@users.noreply.github.com> Date: Wed, 19 Feb 2025 16:56:31 -0300 Subject: [PATCH 08/13] Nit change --- .../Portfolio/SignalExports/Collective2SignalExport.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs b/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs index 104930d19015..39e08a3aa170 100644 --- a/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs +++ b/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs @@ -15,7 +15,6 @@ using Newtonsoft.Json; using QuantConnect.Interfaces; -using QuantConnect.Securities.Forex; using QuantConnect.Util; using System; using System.Collections.Generic; @@ -379,12 +378,12 @@ private string GetSecurityTypeAcronym(SecurityType securityType) private string GetMaturityMonthYear(Symbol symbol) { var delistingDate = symbol.GetDelistingDate(); - if (delistingDate < DateTime.UtcNow) + if (delistingDate < DateTime.UtcNow) // The given symbol has already expired { _algorithm.Error($"Instrument {symbol} has already expired. Its delisting date was: {delistingDate}. No signal will be sent to Collective2."); return string.Empty; } - else if (delistingDate == Time.EndOfTime) + else if (delistingDate == Time.EndOfTime) // The given symbol is equity or forex { return null; } From 1b71e3da42cc9151331a9c3955a38e9eacd090c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Andr=C3=A9s=20Marino=20Rojas?= <47573394+Marinovsky@users.noreply.github.com> Date: Fri, 21 Feb 2025 12:19:52 -0300 Subject: [PATCH 09/13] Address requested changes --- .../SignalExports/Collective2SignalExport.cs | 44 +++++++++++++------ .../Portfolio/SignalExportTargetTests.cs | 16 +++---- 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs b/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs index 39e08a3aa170..00d037217673 100644 --- a/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs +++ b/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Net.Http; using System.Net.Http.Json; using System.Text; @@ -131,7 +132,8 @@ protected bool ConvertHoldingsToCollective2(SignalExportTargetParameters paramet { _algorithm = parameters.Algorithm; var targets = parameters.Targets; - positions = new List(); + var utcNow = DateTime.UtcNow; + positions = []; foreach (var target in targets) { if (target == null) @@ -141,10 +143,16 @@ protected bool ConvertHoldingsToCollective2(SignalExportTargetParameters paramet } var securityType = GetSecurityTypeAcronym(target.Symbol.SecurityType); - if (securityType == null) return false; + if (securityType == null) + { + return false; + } - var maturityMonthYear = GetMaturityMonthYear(target.Symbol); - if (maturityMonthYear?.Length == 0) return false; + var maturityMonthYear = GetMaturityMonthYear(target.Symbol, utcNow); + if (maturityMonthYear?.Length == 0) + { + return false; + } positions.Add(new Collective2Position { @@ -293,7 +301,7 @@ private class DesiredPositionResponse /// private string GetSymbol(Symbol symbol) { - if (symbol.SecurityType == SecurityType.Forex && CurrencyPairUtil.TryDecomposeCurrencyPair(symbol, out var baseCurrency, out var quoteCurrency)) + if (CurrencyPairUtil.TryDecomposeCurrencyPair(symbol, out var baseCurrency, out var quoteCurrency)) { return $"{baseCurrency}/{quoteCurrency}"; } @@ -322,12 +330,8 @@ private string GetMICExchangeCode(Symbol symbol) return "XNSE"; case "hkfe": return "XHKF"; - case "ose": - return "XOSE"; case "nyseliffe": return "XNLI"; - case "cme": - return "XCME"; case "eurex": return "XEUR"; case "ice": @@ -344,6 +348,9 @@ private string GetMICExchangeCode(Symbol symbol) return "XNYM"; case "sgx": return "XSES"; + case "ose": + case "cme": + return "X" + symbol.ID.Market.ToUpper(); default: _algorithm.Debug($"The market of the symbol {symbol.ID.Symbol} was unexpected: {symbol.ID.Market}. Using 'DEFAULT' as market"); return "DEFAULT"; @@ -375,17 +382,26 @@ private string GetSecurityTypeAcronym(SecurityType securityType) /// /// Returns the expiration date in the format C2 expects /// - private string GetMaturityMonthYear(Symbol symbol) + private string GetMaturityMonthYear(Symbol symbol, DateTime utcTime) { var delistingDate = symbol.GetDelistingDate(); - if (delistingDate < DateTime.UtcNow) // The given symbol has already expired + if (delistingDate == Time.EndOfTime) // The given symbol is equity or forex { - _algorithm.Error($"Instrument {symbol} has already expired. Its delisting date was: {delistingDate}. No signal will be sent to Collective2."); + return null; + } + + var exchangeTimeZone = _algorithm.Securities[symbol].Subscriptions.First()?.ExchangeTimeZone; + if (exchangeTimeZone == null) + { + _algorithm.Error($"No subscription was found for symbol {exchangeTimeZone}. No signal will be sent to Collective2."); return string.Empty; } - else if (delistingDate == Time.EndOfTime) // The given symbol is equity or forex + + delistingDate = delistingDate.ConvertToUtc(exchangeTimeZone); + if (delistingDate < utcTime.Date) // The given symbol has already expired { - return null; + _algorithm.Error($"Instrument {symbol} has already expired. Its delisting date was: {delistingDate}. No signal will be sent to Collective2."); + return string.Empty; } return $"{delistingDate:yyyyMMdd}"; diff --git a/Tests/Algorithm/Framework/Portfolio/SignalExportTargetTests.cs b/Tests/Algorithm/Framework/Portfolio/SignalExportTargetTests.cs index 85885a90db37..5bb988c190e5 100644 --- a/Tests/Algorithm/Framework/Portfolio/SignalExportTargetTests.cs +++ b/Tests/Algorithm/Framework/Portfolio/SignalExportTargetTests.cs @@ -389,16 +389,16 @@ public void SignalExportManagerReturnsFalseWhenNegativeTotalPortfolioValue() new object[] { "USD", Symbols.CreateOptionSymbol("SPY", OptionRight.Put, 192m, new DateTime(2056, 02, 19)), 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""SPY"",""currency"":""USD"",""securityExchange"":""DEFAULT"",""securityType"":""OPT"",""maturityMonthYear"":""20560218"",""putOrCall"":0,""strikePrice"":192.0,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "USD", Symbol.Create("EURUSD", SecurityType.Forex, Market.FXCM), 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""EUR/USD"",""currency"":""USD"",""securityExchange"":""FXCM"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "USD", Symbol.Create("NQX", SecurityType.IndexOption, Market.USA), 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NQX"",""currency"":""USD"",""securityExchange"":""DEFAULT"",""securityType"":""OPT"",""maturityMonthYear"":null,""putOrCall"":1,""strikePrice"":0.0,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "USD", Symbol.CreateFuture("NIFTY", Market.India, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NIFTY"",""currency"":""USD"",""securityExchange"":""XNSE"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "USD", Symbol.CreateFuture("HSI", Market.HKFE, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""HSI"",""currency"":""USD"",""securityExchange"":""XHKF"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbol.CreateFuture("NIFTY", Market.India, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NIFTY"",""currency"":""USD"",""securityExchange"":""XNSE"",""securityType"":""FUT"",""maturityMonthYear"":""20560218"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbol.CreateFuture("HSI", Market.HKFE, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""HSI"",""currency"":""USD"",""securityExchange"":""XHKF"",""securityType"":""FUT"",""maturityMonthYear"":""20560218"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "USD", Symbol.CreateFuture("ZG", Market.NYSELIFFE, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""ZG"",""currency"":""USD"",""securityExchange"":""XNLI"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "USD", Symbol.CreateFuture("FESX", Market.EUREX, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""FESX"",""currency"":""USD"",""securityExchange"":""XEUR"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbol.CreateFuture("FESX", Market.EUREX, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""FESX"",""currency"":""USD"",""securityExchange"":""XEUR"",""securityType"":""FUT"",""maturityMonthYear"":""20560218"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "USD", Symbol.CreateFuture("KC", Market.ICE, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""KC"",""currency"":""USD"",""securityExchange"":""IEPA"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "USD", Symbol.CreateFuture("VIX", Market.CFE, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""VIX"",""currency"":""USD"",""securityExchange"":""XCBF"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "USD", Symbol.CreateFuture("ZC", Market.CBOT, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""ZC"",""currency"":""USD"",""securityExchange"":""XCBT"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "USD", Symbol.CreateFuture("GC", Market.COMEX, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""GC"",""currency"":""USD"",""securityExchange"":""XCEC"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "USD", Symbol.CreateFuture("CL", Market.NYMEX, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""CL"",""currency"":""USD"",""securityExchange"":""XNYM"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "USD", Symbol.CreateFuture("NK", Market.SGX, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NK"",""currency"":""USD"",""securityExchange"":""XSES"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbol.CreateFuture("NK", Market.SGX, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NK"",""currency"":""USD"",""securityExchange"":""XSES"",""securityType"":""FUT"",""maturityMonthYear"":""20560218"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "EUR", Symbols.SPY, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""SPY"",""currency"":""EUR"",""securityExchange"":""DEFAULT"",""securityType"":""CS"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "EUR", Symbols.EURUSD, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""EUR/USD"",""currency"":""EUR"",""securityExchange"":""DEFAULT"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "EUR", Symbols.EURGBP, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""EUR/GBP"",""currency"":""EUR"",""securityExchange"":""DEFAULT"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, @@ -409,16 +409,16 @@ public void SignalExportManagerReturnsFalseWhenNegativeTotalPortfolioValue() new object[] { "EUR", Symbols.CreateOptionSymbol("SPY", OptionRight.Put, 192m, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""SPY"",""currency"":""EUR"",""securityExchange"":""DEFAULT"",""securityType"":""OPT"",""maturityMonthYear"":""20560218"",""putOrCall"":0,""strikePrice"":192.0,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "EUR", Symbols.CreateOptionSymbol("SPY", OptionRight.Call, 192m, new DateTime(2016, 02, 19)), 0.2m, @"{""StrategyId"":0,""Positions"":[]}" }, new object[] { "EUR", Symbol.Create("EURUSD", SecurityType.Forex, Market.FXCM), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""EUR/USD"",""currency"":""EUR"",""securityExchange"":""FXCM"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "EUR", Symbol.CreateFuture("NIFTY", Market.India, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NIFTY"",""currency"":""EUR"",""securityExchange"":""XNSE"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "EUR", Symbol.CreateFuture("HSI", Market.HKFE, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""HSI"",""currency"":""EUR"",""securityExchange"":""XHKF"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbol.CreateFuture("NIFTY", Market.India, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NIFTY"",""currency"":""EUR"",""securityExchange"":""XNSE"",""securityType"":""FUT"",""maturityMonthYear"":""20560218"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbol.CreateFuture("HSI", Market.HKFE, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""HSI"",""currency"":""EUR"",""securityExchange"":""XHKF"",""securityType"":""FUT"",""maturityMonthYear"":""20560218"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "EUR", Symbol.CreateFuture("ZG", Market.NYSELIFFE, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""ZG"",""currency"":""EUR"",""securityExchange"":""XNLI"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "EUR", Symbol.CreateFuture("FESX", Market.EUREX, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""FESX"",""currency"":""EUR"",""securityExchange"":""XEUR"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbol.CreateFuture("FESX", Market.EUREX, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""FESX"",""currency"":""EUR"",""securityExchange"":""XEUR"",""securityType"":""FUT"",""maturityMonthYear"":""20560218"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "EUR", Symbol.CreateFuture("KC", Market.ICE, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""KC"",""currency"":""EUR"",""securityExchange"":""IEPA"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "EUR", Symbol.CreateFuture("VIX", Market.CFE, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""VIX"",""currency"":""EUR"",""securityExchange"":""XCBF"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "EUR", Symbol.CreateFuture("ZC", Market.CBOT, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""ZC"",""currency"":""EUR"",""securityExchange"":""XCBT"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "EUR", Symbol.CreateFuture("GC", Market.COMEX, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""GC"",""currency"":""EUR"",""securityExchange"":""XCEC"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "EUR", Symbol.CreateFuture("CL", Market.NYMEX, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""CL"",""currency"":""EUR"",""securityExchange"":""XNYM"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "EUR", Symbol.CreateFuture("NK", Market.SGX, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NK"",""currency"":""EUR"",""securityExchange"":""XSES"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbol.CreateFuture("NK", Market.SGX, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NK"",""currency"":""EUR"",""securityExchange"":""XSES"",""securityType"":""FUT"",""maturityMonthYear"":""20560218"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, }; private static void AddSymbols(List symbols, QCAlgorithm algorithm) From 02cdff1eda180c399be7038e948a867cfaefc052 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Andr=C3=A9s=20Marino=20Rojas?= <47573394+Marinovsky@users.noreply.github.com> Date: Sun, 23 Feb 2025 18:32:53 -0300 Subject: [PATCH 10/13] Address Martin reviews --- .../SignalExports/Collective2SignalExport.cs | 34 ++++++++++--------- .../Portfolio/SignalExportTargetTests.cs | 4 +-- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs b/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs index 00d037217673..def1ccd8dc3b 100644 --- a/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs +++ b/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs @@ -32,6 +32,12 @@ namespace QuantConnect.Algorithm.Framework.Portfolio.SignalExports /// public class Collective2SignalExport : BaseSignalExport { + /// + /// Hashset of symbols whose market is unknown but have already been seen by + /// this signal export manager + /// + private HashSet _unknownMarketSymbols; + /// /// API key provided by Collective2 /// @@ -87,6 +93,7 @@ public class Collective2SignalExport : BaseSignalExport /// Whether to use the white-label API instead of the general one public Collective2SignalExport(string apiKey, int systemId, bool useWhiteLabelApi = false) { + _unknownMarketSymbols = new HashSet(); _apiKey = apiKey; _systemId = systemId; Destination = new Uri(useWhiteLabelApi @@ -132,7 +139,7 @@ protected bool ConvertHoldingsToCollective2(SignalExportTargetParameters paramet { _algorithm = parameters.Algorithm; var targets = parameters.Targets; - var utcNow = DateTime.UtcNow; + var utcNow = _algorithm.UtcTime; positions = []; foreach (var target in targets) { @@ -145,13 +152,13 @@ protected bool ConvertHoldingsToCollective2(SignalExportTargetParameters paramet var securityType = GetSecurityTypeAcronym(target.Symbol.SecurityType); if (securityType == null) { - return false; + continue; } var maturityMonthYear = GetMaturityMonthYear(target.Symbol, utcNow); if (maturityMonthYear?.Length == 0) { - return false; + continue; } positions.Add(new Collective2Position @@ -317,15 +324,13 @@ private string GetSymbol(Symbol symbol) private string GetMICExchangeCode(Symbol symbol) { - if (symbol.SecurityType == SecurityType.Equity || symbol.SecurityType.IsOption()) + if (_unknownMarketSymbols.Contains(symbol.Value) || symbol.SecurityType == SecurityType.Equity || symbol.SecurityType.IsOption()) { return "DEFAULT"; } switch (symbol.ID.Market) { - case "fxcm": - return "FXCM"; case "india": return "XNSE"; case "hkfe": @@ -348,11 +353,14 @@ private string GetMICExchangeCode(Symbol symbol) return "XNYM"; case "sgx": return "XSES"; + case "fxcm": + return symbol.ID.Market.ToUpper(); case "ose": case "cme": return "X" + symbol.ID.Market.ToUpper(); default: - _algorithm.Debug($"The market of the symbol {symbol.ID.Symbol} was unexpected: {symbol.ID.Market}. Using 'DEFAULT' as market"); + _unknownMarketSymbols.Add(symbol.Value); + _algorithm.Debug($"The market of the symbol {symbol.Value} was unexpected: {symbol.ID.Market}. Using 'DEFAULT' as market"); return "DEFAULT"; } } @@ -390,17 +398,11 @@ private string GetMaturityMonthYear(Symbol symbol, DateTime utcTime) return null; } - var exchangeTimeZone = _algorithm.Securities[symbol].Subscriptions.First()?.ExchangeTimeZone; - if (exchangeTimeZone == null) - { - _algorithm.Error($"No subscription was found for symbol {exchangeTimeZone}. No signal will be sent to Collective2."); - return string.Empty; - } - + var exchangeTimeZone = _algorithm.Securities[symbol].Exchange.TimeZone; delistingDate = delistingDate.ConvertToUtc(exchangeTimeZone); - if (delistingDate < utcTime.Date) // The given symbol has already expired + if (delistingDate < utcTime) // The given symbol has already expired { - _algorithm.Error($"Instrument {symbol} has already expired. Its delisting date was: {delistingDate}. No signal will be sent to Collective2."); + _algorithm.Error($"Instrument {symbol} has already expired. Its delisting date was: {delistingDate}. This signal won't be sent to Collective2."); return string.Empty; } diff --git a/Tests/Algorithm/Framework/Portfolio/SignalExportTargetTests.cs b/Tests/Algorithm/Framework/Portfolio/SignalExportTargetTests.cs index 5bb988c190e5..e1790cbf1e71 100644 --- a/Tests/Algorithm/Framework/Portfolio/SignalExportTargetTests.cs +++ b/Tests/Algorithm/Framework/Portfolio/SignalExportTargetTests.cs @@ -384,7 +384,7 @@ public void SignalExportManagerReturnsFalseWhenNegativeTotalPortfolioValue() new object[] { "USD", Symbols.GBPJPY, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""GBP/JPY"",""currency"":""USD"",""securityExchange"":""DEFAULT"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "USD", Symbols.GBPUSD, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""GBP/USD"",""currency"":""USD"",""securityExchange"":""DEFAULT"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "USD", Symbols.USDJPY, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""USD/JPY"",""currency"":""USD"",""securityExchange"":""DEFAULT"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "USD", Symbols.CreateOptionSymbol("SPY", OptionRight.Call, 192m, new DateTime(2016, 02, 19)), 0.2m, @"{""StrategyId"":0,""Positions"":[]}" }, + new object[] { "USD", Symbols.CreateOptionSymbol("SPY", OptionRight.Call, 192m, new DateTime(2016, 02, 16)), 0.2m, @"{""StrategyId"":0,""Positions"":[]}" }, new object[] { "USD", Symbols.CreateOptionSymbol("SPY", OptionRight.Call, 192m, new DateTime(2056, 02, 19)), 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""SPY"",""currency"":""USD"",""securityExchange"":""DEFAULT"",""securityType"":""OPT"",""maturityMonthYear"":""20560218"",""putOrCall"":1,""strikePrice"":192.0,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "USD", Symbols.CreateOptionSymbol("SPY", OptionRight.Put, 192m, new DateTime(2056, 02, 19)), 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""SPY"",""currency"":""USD"",""securityExchange"":""DEFAULT"",""securityType"":""OPT"",""maturityMonthYear"":""20560218"",""putOrCall"":0,""strikePrice"":192.0,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "USD", Symbol.Create("EURUSD", SecurityType.Forex, Market.FXCM), 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""EUR/USD"",""currency"":""USD"",""securityExchange"":""FXCM"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, @@ -407,7 +407,7 @@ public void SignalExportManagerReturnsFalseWhenNegativeTotalPortfolioValue() new object[] { "EUR", Symbols.USDJPY, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""USD/JPY"",""currency"":""EUR"",""securityExchange"":""DEFAULT"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "EUR", Symbols.CreateOptionSymbol("SPY", OptionRight.Call, 192m, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""SPY"",""currency"":""EUR"",""securityExchange"":""DEFAULT"",""securityType"":""OPT"",""maturityMonthYear"":""20560218"",""putOrCall"":1,""strikePrice"":192.0,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "EUR", Symbols.CreateOptionSymbol("SPY", OptionRight.Put, 192m, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""SPY"",""currency"":""EUR"",""securityExchange"":""DEFAULT"",""securityType"":""OPT"",""maturityMonthYear"":""20560218"",""putOrCall"":0,""strikePrice"":192.0,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "EUR", Symbols.CreateOptionSymbol("SPY", OptionRight.Call, 192m, new DateTime(2016, 02, 19)), 0.2m, @"{""StrategyId"":0,""Positions"":[]}" }, + new object[] { "EUR", Symbols.CreateOptionSymbol("SPY", OptionRight.Call, 192m, new DateTime(2016, 02, 16)), 0.2m, @"{""StrategyId"":0,""Positions"":[]}" }, new object[] { "EUR", Symbol.Create("EURUSD", SecurityType.Forex, Market.FXCM), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""EUR/USD"",""currency"":""EUR"",""securityExchange"":""FXCM"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "EUR", Symbol.CreateFuture("NIFTY", Market.India, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NIFTY"",""currency"":""EUR"",""securityExchange"":""XNSE"",""securityType"":""FUT"",""maturityMonthYear"":""20560218"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "EUR", Symbol.CreateFuture("HSI", Market.HKFE, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""HSI"",""currency"":""EUR"",""securityExchange"":""XHKF"",""securityType"":""FUT"",""maturityMonthYear"":""20560218"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, From 1433d3445d1cd4724905d683bdf0e06a46c30cb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Andr=C3=A9s=20Marino=20Rojas?= <47573394+Marinovsky@users.noreply.github.com> Date: Sun, 23 Feb 2025 18:54:50 -0300 Subject: [PATCH 11/13] Nit changes and more unit tests --- .../SignalExports/Collective2SignalExport.cs | 2 +- .../Portfolio/SignalExportTargetTests.cs | 27 +++++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs b/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs index def1ccd8dc3b..12154e0b5274 100644 --- a/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs +++ b/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs @@ -400,7 +400,7 @@ private string GetMaturityMonthYear(Symbol symbol, DateTime utcTime) var exchangeTimeZone = _algorithm.Securities[symbol].Exchange.TimeZone; delistingDate = delistingDate.ConvertToUtc(exchangeTimeZone); - if (delistingDate < utcTime) // The given symbol has already expired + if (delistingDate < utcTime.Date) // The given symbol has already expired { _algorithm.Error($"Instrument {symbol} has already expired. Its delisting date was: {delistingDate}. This signal won't be sent to Collective2."); return string.Empty; diff --git a/Tests/Algorithm/Framework/Portfolio/SignalExportTargetTests.cs b/Tests/Algorithm/Framework/Portfolio/SignalExportTargetTests.cs index e1790cbf1e71..ada4f2d2d597 100644 --- a/Tests/Algorithm/Framework/Portfolio/SignalExportTargetTests.cs +++ b/Tests/Algorithm/Framework/Portfolio/SignalExportTargetTests.cs @@ -50,6 +50,29 @@ public void SendsTargetsToCollective2Appropiately(string currency, Symbol symbol Assert.AreEqual(expectedMessage, message); } + [Test] + public void Collective2SignalExportManagerDoesNotLogMoreThanOnceWhenUnknownMarket() + { + var targetList = new List() { new(Symbols.EURUSD, 1) }; + + var algorithm = new AlgorithmStub(); + algorithm.SetDateTime(new DateTime(2016, 02, 16, 11, 53, 30)); + algorithm.Portfolio.SetAccountCurrency("USD"); + var security = algorithm.AddSecurity(Symbols.EURUSD); + security.SetMarketPrice(new Tick { Value = 100 }); + + algorithm.Portfolio.SetCash(50000); + + using var manager = new Collective2SignalExportHandler("", 0); + + for (int count = 0; count < 100; count++) + { + manager.GetMessageSent(new SignalExportTargetParameters { Targets = targetList, Algorithm = algorithm }); + } + + Assert.AreEqual(1, algorithm.DebugMessages.Count); + } + [Test] public void Collective2ConvertsPercentageToQuantityAppropiately() { @@ -384,7 +407,7 @@ public void SignalExportManagerReturnsFalseWhenNegativeTotalPortfolioValue() new object[] { "USD", Symbols.GBPJPY, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""GBP/JPY"",""currency"":""USD"",""securityExchange"":""DEFAULT"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "USD", Symbols.GBPUSD, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""GBP/USD"",""currency"":""USD"",""securityExchange"":""DEFAULT"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "USD", Symbols.USDJPY, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""USD/JPY"",""currency"":""USD"",""securityExchange"":""DEFAULT"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "USD", Symbols.CreateOptionSymbol("SPY", OptionRight.Call, 192m, new DateTime(2016, 02, 16)), 0.2m, @"{""StrategyId"":0,""Positions"":[]}" }, + new object[] { "USD", Symbols.CreateOptionSymbol("SPY", OptionRight.Call, 192m, new DateTime(2016, 02, 15)), 0.2m, @"{""StrategyId"":0,""Positions"":[]}" }, new object[] { "USD", Symbols.CreateOptionSymbol("SPY", OptionRight.Call, 192m, new DateTime(2056, 02, 19)), 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""SPY"",""currency"":""USD"",""securityExchange"":""DEFAULT"",""securityType"":""OPT"",""maturityMonthYear"":""20560218"",""putOrCall"":1,""strikePrice"":192.0,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "USD", Symbols.CreateOptionSymbol("SPY", OptionRight.Put, 192m, new DateTime(2056, 02, 19)), 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""SPY"",""currency"":""USD"",""securityExchange"":""DEFAULT"",""securityType"":""OPT"",""maturityMonthYear"":""20560218"",""putOrCall"":0,""strikePrice"":192.0,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "USD", Symbol.Create("EURUSD", SecurityType.Forex, Market.FXCM), 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""EUR/USD"",""currency"":""USD"",""securityExchange"":""FXCM"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, @@ -407,7 +430,7 @@ public void SignalExportManagerReturnsFalseWhenNegativeTotalPortfolioValue() new object[] { "EUR", Symbols.USDJPY, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""USD/JPY"",""currency"":""EUR"",""securityExchange"":""DEFAULT"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "EUR", Symbols.CreateOptionSymbol("SPY", OptionRight.Call, 192m, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""SPY"",""currency"":""EUR"",""securityExchange"":""DEFAULT"",""securityType"":""OPT"",""maturityMonthYear"":""20560218"",""putOrCall"":1,""strikePrice"":192.0,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "EUR", Symbols.CreateOptionSymbol("SPY", OptionRight.Put, 192m, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""SPY"",""currency"":""EUR"",""securityExchange"":""DEFAULT"",""securityType"":""OPT"",""maturityMonthYear"":""20560218"",""putOrCall"":0,""strikePrice"":192.0,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "EUR", Symbols.CreateOptionSymbol("SPY", OptionRight.Call, 192m, new DateTime(2016, 02, 16)), 0.2m, @"{""StrategyId"":0,""Positions"":[]}" }, + new object[] { "EUR", Symbols.CreateOptionSymbol("SPY", OptionRight.Call, 192m, new DateTime(2016, 02, 15)), 0.2m, @"{""StrategyId"":0,""Positions"":[]}" }, new object[] { "EUR", Symbol.Create("EURUSD", SecurityType.Forex, Market.FXCM), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""EUR/USD"",""currency"":""EUR"",""securityExchange"":""FXCM"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "EUR", Symbol.CreateFuture("NIFTY", Market.India, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NIFTY"",""currency"":""EUR"",""securityExchange"":""XNSE"",""securityType"":""FUT"",""maturityMonthYear"":""20560218"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "EUR", Symbol.CreateFuture("HSI", Market.HKFE, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""HSI"",""currency"":""EUR"",""securityExchange"":""XHKF"",""securityType"":""FUT"",""maturityMonthYear"":""20560218"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, From 2f383c339013ff1e4484b84afcbdced89f7818d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Andr=C3=A9s=20Marino=20Rojas?= <47573394+Marinovsky@users.noreply.github.com> Date: Sun, 23 Feb 2025 19:00:22 -0300 Subject: [PATCH 12/13] Nit change --- .../Framework/Portfolio/SignalExports/Collective2SignalExport.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs b/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs index 12154e0b5274..d8c505c22d6e 100644 --- a/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs +++ b/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs @@ -19,7 +19,6 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Linq; using System.Net.Http; using System.Net.Http.Json; using System.Text; From 0044cf75121c9f1784a48d2428db0d3dda4023d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Andr=C3=A9s=20Marino=20Rojas?= <47573394+Marinovsky@users.noreply.github.com> Date: Mon, 24 Feb 2025 11:07:51 -0300 Subject: [PATCH 13/13] Address requested changes --- .../SignalExports/Collective2SignalExport.cs | 9 +++------ .../Portfolio/SignalExportTargetTests.cs | 16 ++++++++-------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs b/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs index d8c505c22d6e..c922eeeb0600 100644 --- a/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs +++ b/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs @@ -138,7 +138,6 @@ protected bool ConvertHoldingsToCollective2(SignalExportTargetParameters paramet { _algorithm = parameters.Algorithm; var targets = parameters.Targets; - var utcNow = _algorithm.UtcTime; positions = []; foreach (var target in targets) { @@ -154,7 +153,7 @@ protected bool ConvertHoldingsToCollective2(SignalExportTargetParameters paramet continue; } - var maturityMonthYear = GetMaturityMonthYear(target.Symbol, utcNow); + var maturityMonthYear = GetMaturityMonthYear(target.Symbol); if (maturityMonthYear?.Length == 0) { continue; @@ -389,7 +388,7 @@ private string GetSecurityTypeAcronym(SecurityType securityType) /// /// Returns the expiration date in the format C2 expects /// - private string GetMaturityMonthYear(Symbol symbol, DateTime utcTime) + private string GetMaturityMonthYear(Symbol symbol) { var delistingDate = symbol.GetDelistingDate(); if (delistingDate == Time.EndOfTime) // The given symbol is equity or forex @@ -397,9 +396,7 @@ private string GetMaturityMonthYear(Symbol symbol, DateTime utcTime) return null; } - var exchangeTimeZone = _algorithm.Securities[symbol].Exchange.TimeZone; - delistingDate = delistingDate.ConvertToUtc(exchangeTimeZone); - if (delistingDate < utcTime.Date) // The given symbol has already expired + if (delistingDate < _algorithm.Securities[symbol].LocalTime) // The given symbol has already expired { _algorithm.Error($"Instrument {symbol} has already expired. Its delisting date was: {delistingDate}. This signal won't be sent to Collective2."); return string.Empty; diff --git a/Tests/Algorithm/Framework/Portfolio/SignalExportTargetTests.cs b/Tests/Algorithm/Framework/Portfolio/SignalExportTargetTests.cs index ada4f2d2d597..8d6d65e744c9 100644 --- a/Tests/Algorithm/Framework/Portfolio/SignalExportTargetTests.cs +++ b/Tests/Algorithm/Framework/Portfolio/SignalExportTargetTests.cs @@ -412,16 +412,16 @@ public void SignalExportManagerReturnsFalseWhenNegativeTotalPortfolioValue() new object[] { "USD", Symbols.CreateOptionSymbol("SPY", OptionRight.Put, 192m, new DateTime(2056, 02, 19)), 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""SPY"",""currency"":""USD"",""securityExchange"":""DEFAULT"",""securityType"":""OPT"",""maturityMonthYear"":""20560218"",""putOrCall"":0,""strikePrice"":192.0,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "USD", Symbol.Create("EURUSD", SecurityType.Forex, Market.FXCM), 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""EUR/USD"",""currency"":""USD"",""securityExchange"":""FXCM"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "USD", Symbol.Create("NQX", SecurityType.IndexOption, Market.USA), 0.2m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NQX"",""currency"":""USD"",""securityExchange"":""DEFAULT"",""securityType"":""OPT"",""maturityMonthYear"":null,""putOrCall"":1,""strikePrice"":0.0,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "USD", Symbol.CreateFuture("NIFTY", Market.India, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NIFTY"",""currency"":""USD"",""securityExchange"":""XNSE"",""securityType"":""FUT"",""maturityMonthYear"":""20560218"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "USD", Symbol.CreateFuture("HSI", Market.HKFE, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""HSI"",""currency"":""USD"",""securityExchange"":""XHKF"",""securityType"":""FUT"",""maturityMonthYear"":""20560218"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbol.CreateFuture("NIFTY", Market.India, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NIFTY"",""currency"":""USD"",""securityExchange"":""XNSE"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbol.CreateFuture("HSI", Market.HKFE, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""HSI"",""currency"":""USD"",""securityExchange"":""XHKF"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "USD", Symbol.CreateFuture("ZG", Market.NYSELIFFE, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""ZG"",""currency"":""USD"",""securityExchange"":""XNLI"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "USD", Symbol.CreateFuture("FESX", Market.EUREX, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""FESX"",""currency"":""USD"",""securityExchange"":""XEUR"",""securityType"":""FUT"",""maturityMonthYear"":""20560218"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbol.CreateFuture("FESX", Market.EUREX, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""FESX"",""currency"":""USD"",""securityExchange"":""XEUR"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "USD", Symbol.CreateFuture("KC", Market.ICE, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""KC"",""currency"":""USD"",""securityExchange"":""IEPA"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "USD", Symbol.CreateFuture("VIX", Market.CFE, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""VIX"",""currency"":""USD"",""securityExchange"":""XCBF"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "USD", Symbol.CreateFuture("ZC", Market.CBOT, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""ZC"",""currency"":""USD"",""securityExchange"":""XCBT"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "USD", Symbol.CreateFuture("GC", Market.COMEX, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""GC"",""currency"":""USD"",""securityExchange"":""XCEC"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "USD", Symbol.CreateFuture("CL", Market.NYMEX, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""CL"",""currency"":""USD"",""securityExchange"":""XNYM"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "USD", Symbol.CreateFuture("NK", Market.SGX, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NK"",""currency"":""USD"",""securityExchange"":""XSES"",""securityType"":""FUT"",""maturityMonthYear"":""20560218"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "USD", Symbol.CreateFuture("NK", Market.SGX, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NK"",""currency"":""USD"",""securityExchange"":""XSES"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "EUR", Symbols.SPY, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""SPY"",""currency"":""EUR"",""securityExchange"":""DEFAULT"",""securityType"":""CS"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "EUR", Symbols.EURUSD, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""EUR/USD"",""currency"":""EUR"",""securityExchange"":""DEFAULT"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "EUR", Symbols.EURGBP, 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""EUR/GBP"",""currency"":""EUR"",""securityExchange"":""DEFAULT"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, @@ -432,16 +432,16 @@ public void SignalExportManagerReturnsFalseWhenNegativeTotalPortfolioValue() new object[] { "EUR", Symbols.CreateOptionSymbol("SPY", OptionRight.Put, 192m, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""SPY"",""currency"":""EUR"",""securityExchange"":""DEFAULT"",""securityType"":""OPT"",""maturityMonthYear"":""20560218"",""putOrCall"":0,""strikePrice"":192.0,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "EUR", Symbols.CreateOptionSymbol("SPY", OptionRight.Call, 192m, new DateTime(2016, 02, 15)), 0.2m, @"{""StrategyId"":0,""Positions"":[]}" }, new object[] { "EUR", Symbol.Create("EURUSD", SecurityType.Forex, Market.FXCM), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""EUR/USD"",""currency"":""EUR"",""securityExchange"":""FXCM"",""securityType"":""FOR"",""maturityMonthYear"":null,""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "EUR", Symbol.CreateFuture("NIFTY", Market.India, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NIFTY"",""currency"":""EUR"",""securityExchange"":""XNSE"",""securityType"":""FUT"",""maturityMonthYear"":""20560218"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "EUR", Symbol.CreateFuture("HSI", Market.HKFE, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""HSI"",""currency"":""EUR"",""securityExchange"":""XHKF"",""securityType"":""FUT"",""maturityMonthYear"":""20560218"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbol.CreateFuture("NIFTY", Market.India, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NIFTY"",""currency"":""EUR"",""securityExchange"":""XNSE"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbol.CreateFuture("HSI", Market.HKFE, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""HSI"",""currency"":""EUR"",""securityExchange"":""XHKF"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "EUR", Symbol.CreateFuture("ZG", Market.NYSELIFFE, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""ZG"",""currency"":""EUR"",""securityExchange"":""XNLI"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "EUR", Symbol.CreateFuture("FESX", Market.EUREX, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""FESX"",""currency"":""EUR"",""securityExchange"":""XEUR"",""securityType"":""FUT"",""maturityMonthYear"":""20560218"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbol.CreateFuture("FESX", Market.EUREX, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""FESX"",""currency"":""EUR"",""securityExchange"":""XEUR"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "EUR", Symbol.CreateFuture("KC", Market.ICE, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""KC"",""currency"":""EUR"",""securityExchange"":""IEPA"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "EUR", Symbol.CreateFuture("VIX", Market.CFE, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""VIX"",""currency"":""EUR"",""securityExchange"":""XCBF"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "EUR", Symbol.CreateFuture("ZC", Market.CBOT, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""ZC"",""currency"":""EUR"",""securityExchange"":""XCBT"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "EUR", Symbol.CreateFuture("GC", Market.COMEX, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""GC"",""currency"":""EUR"",""securityExchange"":""XCEC"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, new object[] { "EUR", Symbol.CreateFuture("CL", Market.NYMEX, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""CL"",""currency"":""EUR"",""securityExchange"":""XNYM"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, - new object[] { "EUR", Symbol.CreateFuture("NK", Market.SGX, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NK"",""currency"":""EUR"",""securityExchange"":""XSES"",""securityType"":""FUT"",""maturityMonthYear"":""20560218"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, + new object[] { "EUR", Symbol.CreateFuture("NK", Market.SGX, new DateTime(2056, 02, 19)), 0m, @"{""StrategyId"":0,""Positions"":[{""exchangeSymbol"":{""symbol"":""NK"",""currency"":""EUR"",""securityExchange"":""XSES"",""securityType"":""FUT"",""maturityMonthYear"":""20560219"",""putOrCall"":null,""strikePrice"":null,""priceMultiplier"":1.0},""quantity"":0.0}]}" }, }; private static void AddSymbols(List symbols, QCAlgorithm algorithm)