diff --git a/Algorithm.CSharp/RegressionTests/Collective2IndexOptionAlgorithm.cs b/Algorithm.CSharp/RegressionTests/Collective2IndexOptionAlgorithm.cs index 7ee87b88bba4..03fd9ef93d24 100644 --- a/Algorithm.CSharp/RegressionTests/Collective2IndexOptionAlgorithm.cs +++ b/Algorithm.CSharp/RegressionTests/Collective2IndexOptionAlgorithm.cs @@ -1,23 +1,45 @@ +/* + * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. + * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + using System; using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using QuantConnect.Algorithm.Framework.Portfolio.SignalExports; using QuantConnect.Data; using QuantConnect.Indicators; using QuantConnect.Interfaces; -using QuantConnect.Securities.Option; namespace QuantConnect.Algorithm.CSharp.RegressionTests { public class Collective2IndexOptionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition { - private const string _collective2ApiKey = "8923ABC5-F221-4458-AF97-7CECF5BE3106"; - private const int _collective2SystemId = 145772785; + /// + /// Collective2 APIv4 KEY: This value is provided by Collective2 in your account section (See https://collective2.com/account-info) + /// See API documentation at https://trade.collective2.com/c2-api + /// + private const string _collective2ApiKey = "YOUR APIV4 KEY"; + + + /// + /// Collective2 System ID: This value is found beside the system's name (strategy's name) on the main system page + /// + private const int _collective2SystemId = 0; private ExponentialMovingAverage _fast; private Symbol _spxw; private Symbol _spxwOption; + private Symbol _symbol; public override void Initialize() { @@ -32,14 +54,13 @@ public override void Initialize() Market.USA, OptionStyle.European, OptionRight.Call, - 3200m, - new DateTime(2021, 1, 15)); + 3800m, + new DateTime(2021, 1, 04)); - var foo = AddIndexOptionContract(_spxwOption, Resolution.Minute); + _symbol = AddIndexOptionContract(_spxwOption, Resolution.Minute).Symbol; - _fast = EMA("SPXW", 10, Resolution.Minute); + _fast = EMA("SPXW", 3, Resolution.Minute); - // Configurar Collective2 var test = new Collective2SignalExport(_collective2ApiKey, _collective2SystemId); SignalExport.AddSignalExportProviders(test); SetWarmUp(100); @@ -47,22 +68,19 @@ public override void Initialize() public override void OnData(Slice slice) { - //SetHoldings("SPXW", 0.1); - //var chain = slice.OptionChains[_spxwOption]; - var test = Portfolio; if (!Portfolio[_spxw].Invested) { MarketOrder(_spxw, -1); SignalExport.SetTargetPortfolioFromPortfolio(); } } - public AlgorithmStatus AlgorithmStatus => AlgorithmStatus.RuntimeError; + public AlgorithmStatus AlgorithmStatus => AlgorithmStatus.Completed; public bool CanRunLocally { get; } = true; public virtual List Languages { get; } = new() { Language.CSharp }; - public long DataPoints => 80; + public long DataPoints => 493; public int AlgorithmHistoryDataPoints => 0; @@ -87,8 +105,8 @@ public override void OnData(Slice slice) {"Beta", "0"}, {"Annual Standard Deviation", "0"}, {"Annual Variance", "0"}, - {"Information Ratio", "-9.604"}, - {"Tracking Error", "0.097"}, + {"Information Ratio", "-5.208"}, + {"Tracking Error", "0.103"}, {"Treynor Ratio", "0"}, {"Total Fees", "$0.00"}, {"Estimated Strategy Capacity", "$0"}, diff --git a/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs b/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs index 17ddba237758..e2aa7bb7108d 100644 --- a/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs +++ b/Common/Algorithm/Framework/Portfolio/SignalExports/Collective2SignalExport.cs @@ -145,10 +145,6 @@ protected bool ConvertHoldingsToCollective2(SignalExportTargetParameters paramet else if (target.Symbol.SecurityType.IsOption()) { symbol = SymbolRepresentation.GenerateOptionTicker(target.Symbol); - if (target.Symbol.SecurityType == SecurityType.IndexOption) - { - symbol = target.Symbol.Canonical.Value.Replace("?", string.Empty); - } } positions.Add(new Collective2Position diff --git a/Common/Algorithm/Framework/Portfolio/SignalExports/SignalExportManager.cs b/Common/Algorithm/Framework/Portfolio/SignalExports/SignalExportManager.cs index a298d6ff0d55..1a9c18576206 100644 --- a/Common/Algorithm/Framework/Portfolio/SignalExports/SignalExportManager.cs +++ b/Common/Algorithm/Framework/Portfolio/SignalExports/SignalExportManager.cs @@ -109,7 +109,7 @@ protected bool GetPortfolioTargets(out PortfolioTarget[] targets) /// True if the portfolio targets could be sent to the different signal export providers successfully, false otherwise public bool SetTargetPortfolio(params PortfolioTarget[] portfolioTargets) { - if (_algorithm.LiveMode) + if (!_algorithm.LiveMode) { if (!_isLiveWarningModeLog) { @@ -156,12 +156,8 @@ private IEnumerable GetPortfolioTargets(decimal totalPortfolioV continue; } - //var marginParameters = MaintenanceMarginParameters.ForQuantityAtCurrentPrice(security, holding.Quantity); var marginParameters = new InitialMarginParameters(security, holding.Quantity); var adjustedPercent = security.BuyingPowerModel.GetInitialMarginRequirement(marginParameters) / totalPortfolioValue; - //test - //adjustedPercent = 0.3m; - // See PortfolioTarget.Percent: // we normalize the target buying power by the leverage so we work in the land of margin diff --git a/Common/SymbolRepresentation.cs b/Common/SymbolRepresentation.cs index 1619bdd05edc..2c5752092dee 100644 --- a/Common/SymbolRepresentation.cs +++ b/Common/SymbolRepresentation.cs @@ -44,7 +44,7 @@ public class FutureTickerProperties /// /// Underlying name /// - public string Underlying { get; set; } + public string Underlying { get; set; } /// /// Short expiration year @@ -167,8 +167,7 @@ public static Symbol ParseFutureSymbol(string ticker, int? futureYear = null) if (!SymbolPropertiesDatabase.FromDataFolder().TryGetMarket(underlying, SecurityType.Future, out var market)) { - Log.Debug($@"SymbolRepresentation.ParseFutureSymbol(): { - Messages.SymbolRepresentation.FailedToGetMarketForTickerAndUnderlying(ticker, underlying)}"); + Log.Debug($@"SymbolRepresentation.ParseFutureSymbol(): {Messages.SymbolRepresentation.FailedToGetMarketForTickerAndUnderlying(ticker, underlying)}"); return null; } @@ -269,7 +268,8 @@ public static string GenerateFutureTicker(string underlying, DateTime expiration month = expirationMonth.Month; year = doubleDigitsYear ? expirationMonth.Year % 100 : expirationMonth.Year % 10; } - else { + else + { // These futures expire in the month before or in the contract month month += contractMonthDelta; @@ -389,7 +389,7 @@ public static Symbol ParseOptionTickerOSI(string ticker, SecurityType securityTy // let it fallback to it's default handling, which include mapping optionTicker = null; } - else if(securityType == SecurityType.IndexOption) + else if (securityType == SecurityType.IndexOption) { underlyingSid = SecurityIdentifier.GenerateIndex(OptionSymbol.MapToUnderlying(optionTicker, securityType), market); underlyingSymbolValue = underlyingSid.Symbol; @@ -478,6 +478,10 @@ public static bool TryDecomposeOptionTickerOSI(string ticker, SecurityType secur /// The option ticker public static string GenerateOptionTicker(Symbol symbol) { + if (symbol.SecurityType == SecurityType.IndexOption) + { + return symbol.Canonical.Value.Replace("?", string.Empty); + } var letter = _optionSymbology.Where(x => x.Value.Item2 == symbol.ID.OptionRight && x.Value.Item1 == symbol.ID.Date.Month).Select(x => x.Key).Single(); var twoYearDigit = symbol.ID.Date.ToString("yy"); return $"{SecurityIdentifier.Ticker(symbol.Underlying, symbol.ID.Date)}{twoYearDigit}{symbol.ID.Date.Day:00}{letter}{symbol.ID.StrikePrice.ToStringInvariant()}"; @@ -590,10 +594,10 @@ public static OptionTickerProperties ParseOptionTickerIQFeed(string ticker) /// Tickers from live trading may not provide the four-digit year. private static int GetExpirationYear(int? futureYear, FutureTickerProperties parsed) { - if(futureYear.HasValue) + if (futureYear.HasValue) { var referenceYear = 1900 + parsed.ExpirationYearShort; - while(referenceYear < futureYear.Value) + while (referenceYear < futureYear.Value) { referenceYear += 10; } diff --git a/Tests/Common/SymbolRepresentationTests.cs b/Tests/Common/SymbolRepresentationTests.cs index e38f09332e46..a2844643340e 100644 --- a/Tests/Common/SymbolRepresentationTests.cs +++ b/Tests/Common/SymbolRepresentationTests.cs @@ -155,6 +155,15 @@ public void ParseInvalidFuturesTickers() Assert.AreEqual(result, null); } + [Test] + public void GenerateOptionTickerWithIndexOptionReturnsCorrectTicker() + { + var expected = "AAPL"; + var symbol = Symbol.Create("AAPL", SecurityType.IndexOption, Market.USA, null, null); + var result = SymbolRepresentation.GenerateOptionTicker(symbol); + Assert.AreEqual(expected, result); + } + [TestCase(Futures.Energy.ArgusLLSvsWTIArgusTradeMonth, 2017, 1, 29, "AE529G7", false)] // Previous month [TestCase(Futures.Energy.ArgusPropaneSaudiAramco, 2017, 1, 29, "A9N29G7", false)] // Previous month [TestCase(Futures.Energy.BrentCrude, 2017, 1, 29, "B29H7", false)] // Second prior month @@ -287,7 +296,7 @@ public void GenerateFutureSymbolFromTickerExpiringBefore2000(string ticker) [TestCase("PROPANE_NON_LDH_MONT_BELVIEU", QuantConnect.Securities.Futures.Energy.PropaneNonLDHMontBelvieu)] [TestCase("ARGUS_PROPANE_FAR_EAST_INDEX_BALMO", QuantConnect.Securities.Futures.Energy.ArgusPropaneFarEastIndexBALMO)] [TestCase("GASOLINE", QuantConnect.Securities.Futures.Energy.Gasoline)] - [TestCase("NATURAL_GAS",QuantConnect.Securities.Futures.Energy.NaturalGas)] + [TestCase("NATURAL_GAS", QuantConnect.Securities.Futures.Energy.NaturalGas)] public void FutureEnergySymbolsWorkInPythonWithPEP8(string FutureEnergyName, string expectedFutureEnergyValue) { using (Py.GIL()) diff --git a/Tests/RegressionTestMessageHandler.cs b/Tests/RegressionTestMessageHandler.cs index 0cb7e4dc9c39..83e22a1ccf84 100644 --- a/Tests/RegressionTestMessageHandler.cs +++ b/Tests/RegressionTestMessageHandler.cs @@ -28,7 +28,7 @@ namespace QuantConnect.Tests /// public class RegressionTestMessageHandler : QuantConnect.Messaging.Messaging { - private static readonly bool _updateRegressionStatistics = Config.GetBool("regression-update-statistics", true); + private static readonly bool _updateRegressionStatistics = Config.GetBool("regression-update-statistics", false); private AlgorithmNodePacket _job; private AlgorithmManager _algorithmManager;