/* * 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.Linq.Expressions; using System.Globalization; using NodaTime; using NodaTime.TimeZones; using QuantConnect.Benchmarks; using QuantConnect.Brokerages; using QuantConnect.Data; using QuantConnect.Data.UniverseSelection; using QuantConnect.Interfaces; using QuantConnect.Notifications; using QuantConnect.Orders; using QuantConnect.Parameters; using QuantConnect.Scheduling; using QuantConnect.Securities; using QuantConnect.Securities.Cfd; using QuantConnect.Securities.Equity; using QuantConnect.Securities.Forex; using QuantConnect.Securities.IndexOption; using QuantConnect.Securities.Option; using QuantConnect.Statistics; using QuantConnect.Util; using QuantConnect.Data.Market; using QuantConnect.Data.Fundamental; using System.Collections.Concurrent; using QuantConnect.Securities.Future; using QuantConnect.Securities.Crypto; using QuantConnect.Algorithm.Framework.Alphas; using QuantConnect.Algorithm.Framework.Execution; using QuantConnect.Algorithm.Framework.Portfolio; using QuantConnect.Algorithm.Framework.Risk; using QuantConnect.Algorithm.Framework.Selection; using QuantConnect.Algorithm.Selection; using QuantConnect.Storage; using Index = QuantConnect.Securities.Index.Index; using QuantConnect.Securities.CryptoFuture; using QuantConnect.Algorithm.Framework.Alphas.Analysis; using QuantConnect.Algorithm.Framework.Portfolio.SignalExports; using Python.Runtime; using QuantConnect.Commands; using Newtonsoft.Json; using QuantConnect.Securities.Index; using QuantConnect.Api; using System.Threading.Tasks; namespace QuantConnect.Algorithm { /// /// QC Algorithm Base Class - Handle the basic requirements of a trading algorithm, /// allowing user to focus on event methods. The QCAlgorithm class implements Portfolio, /// Securities, Transactions and Data Subscription Management. /// public partial class QCAlgorithm : MarshalByRefObject, IAlgorithm { #region Documentation Attribute Categories const string AddingData = "Adding Data"; const string AlgorithmFramework = "Algorithm Framework"; const string Charting = "Charting"; const string ConsolidatingData = "Consolidating Data"; const string HandlingData = "Handling Data"; const string HistoricalData = "Historical Data"; const string Indicators = "Indicators"; const string LiveTrading = "Live Trading"; const string Logging = "Logging"; const string MachineLearning = "Machine Learning"; const string Modeling = "Modeling"; const string ParameterAndOptimization = "Parameter and Optimization"; const string ScheduledEvents = "Scheduled Events"; const string SecuritiesAndPortfolio = "Securities and Portfolio"; const string TradingAndOrders = "Trading and Orders"; const string Universes = "Universes"; const string StatisticsTag = "Statistics"; #endregion /// /// Maximum length of the name or tags of a backtest /// protected const int MaxNameAndTagsLength = 200; /// /// Maximum number of tags allowed for a backtest /// protected const int MaxTagsCount = 100; private readonly TimeKeeper _timeKeeper; private LocalTimeKeeper _localTimeKeeper; private string _name; private HashSet _tags; private bool _tagsLimitReachedLogSent; private bool _tagsCollectionTruncatedLogSent; private DateTime _start; private DateTime _startDate; //Default start and end dates. private DateTime _endDate; //Default end to yesterday private bool _locked; private bool _liveMode; private AlgorithmMode _algorithmMode; private DeploymentTarget _deploymentTarget; private string _algorithmId = ""; private ConcurrentQueue _debugMessages = new ConcurrentQueue(); private ConcurrentQueue _logMessages = new ConcurrentQueue(); private ConcurrentQueue _errorMessages = new ConcurrentQueue(); private IStatisticsService _statisticsService; private IBrokerageModel _brokerageModel; private bool _sentBroadcastCommandsDisabled; private readonly HashSet _oneTimeCommandErrors = new(); private readonly Dictionary> _registeredCommands = new(StringComparer.InvariantCultureIgnoreCase); //Error tracking to avoid message flooding: private string _previousDebugMessage = ""; private string _previousErrorMessage = ""; /// /// Gets the market hours database in use by this algorithm /// protected MarketHoursDatabase MarketHoursDatabase { get; } /// /// Gets the symbol properties database in use by this algorithm /// protected SymbolPropertiesDatabase SymbolPropertiesDatabase { get; } // used for calling through to void OnData(Slice) if no override specified private bool _checkedForOnDataSlice; private Action _onDataSlice; // flips to true when the user private bool _userSetSecurityInitializer; // warmup resolution variables private TimeSpan? _warmupTimeSpan; private int? _warmupBarCount; private Dictionary _parameters = new Dictionary(); private SecurityDefinitionSymbolResolver _securityDefinitionSymbolResolver; private SecurityDefinitionSymbolResolver SecurityDefinitionSymbolResolver { get { _securityDefinitionSymbolResolver ??= SecurityDefinitionSymbolResolver.GetInstance(); return _securityDefinitionSymbolResolver; } } private readonly HistoryRequestFactory _historyRequestFactory; private IApi _api; /// /// QCAlgorithm Base Class Constructor - Initialize the underlying QCAlgorithm components. /// QCAlgorithm manages the transactions, portfolio, charting and security subscriptions for the users algorithms. /// public QCAlgorithm() { Name = GetType().Name; Tags = new(); Status = AlgorithmStatus.Running; // AlgorithmManager will flip this when we're caught up with realtime IsWarmingUp = true; //Initialise the Algorithm Helper Classes: //- Note - ideally these wouldn't be here, but because of the DLL we need to make the classes shared across // the Worker & Algorithm, limiting ability to do anything else. //Initialise Start Date: _startDate = new DateTime(1998, 01, 01); // intialize our time keeper with only new york _timeKeeper = new TimeKeeper(_startDate, new[] { TimeZones.NewYork }); // set our local time zone _localTimeKeeper = _timeKeeper.GetLocalTimeKeeper(TimeZones.NewYork); //Initialise End Date: SetEndDate(DateTime.UtcNow.ConvertFromUtc(TimeZone)); // Set default algorithm mode as backtesting _algorithmMode = AlgorithmMode.Backtesting; // Set default deployment target as local _deploymentTarget = DeploymentTarget.LocalPlatform; Settings = new AlgorithmSettings(); DefaultOrderProperties = new OrderProperties(); //Initialise Data Manager SubscriptionManager = new SubscriptionManager(_timeKeeper); Securities = new SecurityManager(_timeKeeper); Transactions = new SecurityTransactionManager(this, Securities); Portfolio = new SecurityPortfolioManager(Securities, Transactions, Settings, DefaultOrderProperties); SignalExport = new SignalExportManager(this); BrokerageModel = new DefaultBrokerageModel(); RiskFreeInterestRateModel = new InterestRateProvider(); Notify = new NotificationManager(false); // Notification manager defaults to disabled. //Initialise to unlocked: _locked = false; // get exchange hours loaded from the market-hours-database.csv in /Data/market-hours MarketHoursDatabase = MarketHoursDatabase.FromDataFolder(); SymbolPropertiesDatabase = SymbolPropertiesDatabase.FromDataFolder(); // universe selection UniverseManager = new UniverseManager(); Universe = new UniverseDefinitions(this); UniverseSettings = new UniverseSettings(Resolution.Minute, Security.NullLeverage, true, false, TimeSpan.FromDays(1)); // initialize our scheduler, this acts as a liason to the real time handler Schedule = new ScheduleManager(Securities, TimeZone, MarketHoursDatabase); // initialize the trade builder SetTradeBuilder(new TradeBuilder(FillGroupingMethod.FillToFill, FillMatchingMethod.FIFO)); SecurityInitializer = new BrokerageModelSecurityInitializer(BrokerageModel, SecuritySeeder.Null); CandlestickPatterns = new CandlestickPatterns(this); // initialize trading calendar TradingCalendar = new TradingCalendar(Securities, MarketHoursDatabase); OptionChainProvider = new EmptyOptionChainProvider(); FutureChainProvider = new EmptyFutureChainProvider(); _historyRequestFactory = new HistoryRequestFactory(this); // set model defaults, universe selection set via PostInitialize SetAlpha(new NullAlphaModel()); SetPortfolioConstruction(new NullPortfolioConstructionModel()); SetExecution(new ImmediateExecutionModel()); SetRiskManagement(new NullRiskManagementModel()); SetUniverseSelection(new NullUniverseSelectionModel()); Insights = new InsightManager(this); } /// /// Event fired when the algorithm generates insights /// [DocumentationAttribute(AlgorithmFramework)] public event AlgorithmEvent InsightsGenerated; /// /// Security collection is an array of the security objects such as Equities and FOREX. Securities data /// manages the properties of tradeable assets such as price, open and close time and holdings information. /// [DocumentationAttribute(SecuritiesAndPortfolio)] public SecurityManager Securities { get; set; } /// /// Read-only dictionary containing all active securities. An active security is /// a security that is currently selected by the universe or has holdings or open orders. /// [DocumentationAttribute(SecuritiesAndPortfolio)] public IReadOnlyDictionary ActiveSecurities => UniverseManager.ActiveSecurities; /// /// Portfolio object provieds easy access to the underlying security-holding properties; summed together in a way to make them useful. /// This saves the user time by providing common portfolio requests in a single /// [DocumentationAttribute(SecuritiesAndPortfolio)] public SecurityPortfolioManager Portfolio { get; set; } /// /// Gets the account currency /// [DocumentationAttribute(SecuritiesAndPortfolio)] public string AccountCurrency => Portfolio.CashBook.AccountCurrency; /// /// Gets the time keeper instance /// public ITimeKeeper TimeKeeper => _timeKeeper; /// /// Generic Data Manager - Required for compiling all data feeds in order, and passing them into algorithm event methods. /// The subscription manager contains a list of the data feed's we're subscribed to and properties of each data feed. /// [DocumentationAttribute(HandlingData)] public SubscriptionManager SubscriptionManager { get; set; } /// /// SignalExport - Allows sending export signals to different 3rd party API's. For example, it allows to send signals /// to Collective2, CrunchDAO and Numerai API's /// [DocumentationAttribute(SecuritiesAndPortfolio)] public SignalExportManager SignalExport { get; } /// /// The project id associated with this algorithm if any /// public int ProjectId { get; set; } /// /// Gets the brokerage model - used to model interactions with specific brokerages. /// [DocumentationAttribute(Modeling)] public IBrokerageModel BrokerageModel { get { return _brokerageModel; } private set { _brokerageModel = value; try { BrokerageName = Brokerages.BrokerageModel.GetBrokerageName(_brokerageModel); } catch (ArgumentOutOfRangeException) { // The brokerage model might be a custom one which has not a corresponding BrokerageName BrokerageName = BrokerageName.Default; } } } /// /// Gets the brokerage name. /// [DocumentationAttribute(Modeling)] public BrokerageName BrokerageName { get; private set; } /// /// Gets the brokerage message handler used to decide what to do /// with each message sent from the brokerage /// [DocumentationAttribute(Modeling)] public IBrokerageMessageHandler BrokerageMessageHandler { get; set; } /// /// Gets the risk free interest rate model used to get the interest rates /// [DocumentationAttribute(Modeling)] public IRiskFreeInterestRateModel RiskFreeInterestRateModel { get; private set; } /// /// Notification Manager for Sending Live Runtime Notifications to users about important events. /// [DocumentationAttribute(LiveTrading)] public NotificationManager Notify { get; set; } /// /// Gets schedule manager for adding/removing scheduled events /// [DocumentationAttribute(ScheduledEvents)] public ScheduleManager Schedule { get; private set; } /// /// Gets or sets the current status of the algorithm /// [DocumentationAttribute(HandlingData)] public AlgorithmStatus Status { get; set; } /// /// Gets an instance that is to be used to initialize newly created securities. /// [DocumentationAttribute(AddingData)] public ISecurityInitializer SecurityInitializer { get; private set; } /// /// Gets the Trade Builder to generate trades from executions /// [DocumentationAttribute(TradingAndOrders)] public ITradeBuilder TradeBuilder { get; private set; } /// /// Gets an instance to access the candlestick pattern helper methods /// [DocumentationAttribute(Indicators)] public CandlestickPatterns CandlestickPatterns { get; private set; } /// /// Gets the date rules helper object to make specifying dates for events easier /// [DocumentationAttribute(ScheduledEvents)] public DateRules DateRules { get { return Schedule.DateRules; } } /// /// Gets the time rules helper object to make specifying times for events easier /// [DocumentationAttribute(ScheduledEvents)] public TimeRules TimeRules { get { return Schedule.TimeRules; } } /// /// Gets trading calendar populated with trading events /// [DocumentationAttribute(ScheduledEvents)] public TradingCalendar TradingCalendar { get; private set; } /// /// Gets the user settings for the algorithm /// [DocumentationAttribute(HandlingData)] public IAlgorithmSettings Settings { get; private set; } /// /// Gets the option chain provider, used to get the list of option contracts for an underlying symbol /// [DocumentationAttribute(AddingData)] [Obsolete("OptionChainProvider property is will soon be deprecated. " + "The new OptionChain() method should be used to fetch option chains, " + "which will contain additional data per contract, like daily price data, implied volatility and greeks.")] public IOptionChainProvider OptionChainProvider { get; private set; } /// /// Gets the future chain provider, used to get the list of future contracts for an underlying symbol /// [DocumentationAttribute(AddingData)] [Obsolete("FutureChainProvider property is will soon be deprecated. " + "The new FuturesChain() method should be used to fetch futures chains, " + "which will contain additional data per contract, like daily price data.")] public IFutureChainProvider FutureChainProvider { get; private set; } /// /// Gets the default order properties /// [DocumentationAttribute(TradingAndOrders)] public IOrderProperties DefaultOrderProperties { get; set; } /// /// Public name for the algorithm as automatically generated by the IDE. Intended for helping distinguish logs by noting /// the algorithm-id. /// /// [DocumentationAttribute(HandlingData)] public string Name { get { return _name; } set { if (_locked) { throw new InvalidOperationException("Cannot set algorithm name after it is initialized."); } if (!string.IsNullOrEmpty(value)) { _name = value.Truncate(MaxNameAndTagsLength); } } } /// /// A list of tags associated with the algorithm or the backtest, useful for categorization /// [DocumentationAttribute(HandlingData)] public HashSet Tags { get { return _tags; } set { if (value == null) { return; } var tags = value.Where(x => !string.IsNullOrEmpty(x?.Trim())).ToList(); if (tags.Count > MaxTagsCount && !_tagsCollectionTruncatedLogSent) { Log($"Warning: The tags collection cannot contain more than {MaxTagsCount} items. It will be truncated."); _tagsCollectionTruncatedLogSent = true; } _tags = tags.Take(MaxTagsCount).ToHashSet(tag => tag.Truncate(MaxNameAndTagsLength)); if (_locked) { TagsUpdated?.Invoke(this, Tags); } } } /// /// Event fired algorithm's name is changed /// [DocumentationAttribute(HandlingData)] public event AlgorithmEvent NameUpdated; /// /// Event fired when the tag collection is updated /// [DocumentationAttribute(HandlingData)] public event AlgorithmEvent> TagsUpdated; /// /// Read-only value for current time frontier of the algorithm in terms of the /// /// During backtesting this is primarily sourced from the data feed. During live trading the time is updated from the system clock. [DocumentationAttribute(HandlingData)] public DateTime Time { get { return _localTimeKeeper.LocalTime; } } /// /// Current date/time in UTC. /// [DocumentationAttribute(HandlingData)] public DateTime UtcTime { get { return _timeKeeper.UtcTime; } } /// /// Gets the time zone used for the property. The default value /// is /// [DocumentationAttribute(HandlingData)] public DateTimeZone TimeZone { get { return _localTimeKeeper.TimeZone; } } /// /// Value of the user set start-date from the backtest. /// /// This property is set with SetStartDate() and defaults to the earliest QuantConnect data available - Jan 1st 1998. It is ignored during live trading /// [DocumentationAttribute(HandlingData)] public DateTime StartDate => _startDate; /// /// Value of the user set start-date from the backtest. Controls the period of the backtest. /// /// This property is set with SetEndDate() and defaults to today. It is ignored during live trading. /// [DocumentationAttribute(HandlingData)] public DateTime EndDate { get { return _endDate; } } /// /// Algorithm Id for this backtest or live algorithm. /// /// A unique identifier for [DocumentationAttribute(HandlingData)] public string AlgorithmId { get { return _algorithmId; } } /// /// Boolean property indicating the algorithm is currently running in live mode. /// /// Intended for use where certain behaviors will be enabled while the algorithm is trading live: such as notification emails, or displaying runtime statistics. [DocumentationAttribute(LiveTrading)] public bool LiveMode { get { return _liveMode; } } /// /// Algorithm running mode. /// public AlgorithmMode AlgorithmMode { get { return _algorithmMode; } } /// /// Deployment target, either local or cloud. /// public DeploymentTarget DeploymentTarget { get { return _deploymentTarget; } } /// /// Storage for debugging messages before the event handler has passed control back to the Lean Engine. /// /// [DocumentationAttribute(Logging)] public ConcurrentQueue DebugMessages { get { return _debugMessages; } set { _debugMessages = value; } } /// /// Storage for log messages before the event handlers have passed control back to the Lean Engine. /// /// [DocumentationAttribute(Logging)] public ConcurrentQueue LogMessages { get { return _logMessages; } set { _logMessages = value; } } /// /// Gets the run time error from the algorithm, or null if none was encountered. /// [DocumentationAttribute(Logging)] public Exception RunTimeError { get; set; } /// /// List of error messages generated by the user's code calling the "Error" function. /// /// This method is best used within a try-catch bracket to handle any runtime errors from a user algorithm. /// [DocumentationAttribute(Logging)] public ConcurrentQueue ErrorMessages { get { return _errorMessages; } set { _errorMessages = value; } } /// /// Returns the current Slice object /// [DocumentationAttribute(HandlingData)] public Slice CurrentSlice { get; private set; } /// /// Gets the object store, used for persistence /// [DocumentationAttribute(HandlingData)] [DocumentationAttribute(MachineLearning)] public ObjectStore ObjectStore { get; private set; } /// /// The current statistics for the running algorithm. /// [DocumentationAttribute(StatisticsTag)] public StatisticsResults Statistics { get { return _statisticsService?.StatisticsResults() ?? new StatisticsResults(); } } /// /// Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized. /// /// /// /// [DocumentationAttribute(AlgorithmFramework)] [DocumentationAttribute(HandlingData)] public virtual void Initialize() { //Setup Required Data throw new NotImplementedException("Please override the Initialize() method"); } /// /// Called by setup handlers after Initialize and allows the algorithm a chance to organize /// the data gather in the Initialize method /// [DocumentationAttribute(AlgorithmFramework)] [DocumentationAttribute(HandlingData)] public virtual void PostInitialize() { if (_endDate < _startDate) { throw new ArgumentException("Please select an algorithm end date greater than start date."); } var portfolioConstructionModel = PortfolioConstruction as PortfolioConstructionModel; if (portfolioConstructionModel != null) { // only override default values if user set the algorithm setting if (Settings.RebalancePortfolioOnSecurityChanges.HasValue) { portfolioConstructionModel.RebalanceOnSecurityChanges = Settings.RebalancePortfolioOnSecurityChanges.Value; } if (Settings.RebalancePortfolioOnInsightChanges.HasValue) { portfolioConstructionModel.RebalanceOnInsightChanges = Settings.RebalancePortfolioOnInsightChanges.Value; } } else { if (Settings.RebalancePortfolioOnInsightChanges.HasValue || Settings.RebalancePortfolioOnSecurityChanges.HasValue) { Debug("Warning: rebalance portfolio settings are set but not supported by the current IPortfolioConstructionModel type: " + $"{PortfolioConstruction.GetType()}"); } } FrameworkPostInitialize(); // if the benchmark hasn't been set yet, load in the default from the brokerage model if (Benchmark == null) { Benchmark = BrokerageModel.GetBenchmark(Securities); } // Check benchmark timezone against algorithm timezone to warn for misaligned statistics if (Benchmark is SecurityBenchmark securityBenchmark) { // Only warn on algorithms subscribed to daily resolution as its statistics will suffer the most var subscription = SubscriptionManager.Subscriptions.OrderByDescending(x => x.Resolution).FirstOrDefault(); var benchmarkTimeZone = MarketHoursDatabase.GetDataTimeZone(securityBenchmark.Security.Symbol.ID.Market, securityBenchmark.Security.Symbol, securityBenchmark.Security.Type); if ((subscription?.Resolution == Resolution.Daily || UniverseSettings.Resolution == Resolution.Daily) && benchmarkTimeZone != TimeZone) { Log($"QCAlgorithm.PostInitialize(): Warning: Using a security benchmark of a different timezone ({benchmarkTimeZone})" + $" than the algorithm TimeZone ({TimeZone}) may lead to skewed and incorrect statistics. Use a higher resolution than daily to minimize."); } } if (TryGetWarmupHistoryStartTime(out var result)) { SetDateTime(result.ConvertToUtc(TimeZone)); } else { SetFinishedWarmingUp(); } if (Settings.DailyPreciseEndTime) { Debug("Accurate daily end-times now enabled by default. See more at https://qnt.co/3YHaWHL. To disable it and use legacy daily bars set self.settings.daily_precise_end_time = False."); } // perform end of time step checks, such as enforcing underlying securities are in raw data mode OnEndOfTimeStep(); } /// /// Called when the algorithm has completed initialization and warm up. /// [DocumentationAttribute(HandlingData)] public virtual void OnWarmupFinished() { } /// /// Gets the parameter with the specified name. If a parameter with the specified name does not exist, /// the given default value is returned if any, else null /// /// The name of the parameter to get /// The default value to return /// The value of the specified parameter, or defaultValue if not found or null if there's no default value [DocumentationAttribute(ParameterAndOptimization)] public string GetParameter(string name, string defaultValue = null) { return _parameters.TryGetValue(name, out var value) ? value : defaultValue; } /// /// Gets the parameter with the specified name parsed as an integer. If a parameter with the specified name does not exist, /// or the conversion is not possible, the given default value is returned /// /// The name of the parameter to get /// The default value to return /// The value of the specified parameter, or defaultValue if not found or null if there's no default value [DocumentationAttribute(ParameterAndOptimization)] public int GetParameter(string name, int defaultValue) { return _parameters.TryGetValue(name, out var strValue) && int.TryParse(strValue, out var value) ? value : defaultValue; } /// /// Gets the parameter with the specified name parsed as a double. If a parameter with the specified name does not exist, /// or the conversion is not possible, the given default value is returned /// /// The name of the parameter to get /// The default value to return /// The value of the specified parameter, or defaultValue if not found or null if there's no default value [DocumentationAttribute(ParameterAndOptimization)] public double GetParameter(string name, double defaultValue) { return _parameters.TryGetValue(name, out var strValue) && double.TryParse(strValue, NumberStyles.Any, CultureInfo.InvariantCulture, out var value) ? value : defaultValue; } /// /// Gets the parameter with the specified name parsed as a decimal. If a parameter with the specified name does not exist, /// or the conversion is not possible, the given default value is returned /// /// The name of the parameter to get /// The default value to return /// The value of the specified parameter, or defaultValue if not found or null if there's no default value [DocumentationAttribute(ParameterAndOptimization)] public decimal GetParameter(string name, decimal defaultValue) { return _parameters.TryGetValue(name, out var strValue) && decimal.TryParse(strValue, NumberStyles.Any, CultureInfo.InvariantCulture, out var value) ? value : defaultValue; } /// /// Gets a read-only dictionary with all current parameters /// [DocumentationAttribute(ParameterAndOptimization)] public IReadOnlyDictionary GetParameters() { return _parameters.ToReadOnlyDictionary(); } /// /// Sets the parameters from the dictionary /// /// Dictionary containing the parameter names to values [DocumentationAttribute(ParameterAndOptimization)] public void SetParameters(Dictionary parameters) { // save off a copy and try to apply the parameters _parameters = parameters.ToDictionary(); try { ParameterAttribute.ApplyAttributes(parameters, this); } catch (Exception err) { Error("Error applying parameter values: " + err.Message); } } /// /// Set the available data feeds in the /// /// The different each supports [DocumentationAttribute(HandlingData)] public void SetAvailableDataTypes(Dictionary> availableDataTypes) { if (availableDataTypes == null) { return; } foreach (var dataFeed in availableDataTypes) { SubscriptionManager.AvailableDataTypes[dataFeed.Key] = dataFeed.Value; } } /// /// Sets the security initializer, used to initialize/configure securities after creation. /// The initializer will be applied to all universes and manually added securities. /// /// The security initializer [DocumentationAttribute(AddingData)] [DocumentationAttribute(Modeling)] public void SetSecurityInitializer(ISecurityInitializer securityInitializer) { if (_locked) { throw new Exception("SetSecurityInitializer() cannot be called after algorithm initialization. " + "When you use the SetSecurityInitializer() method it will apply to all universes and manually added securities."); } if (_userSetSecurityInitializer) { Debug("Warning: SetSecurityInitializer() has already been called, existing security initializers in all universes will be overwritten."); } // this flag will prevent calls to SetBrokerageModel from overwriting this initializer _userSetSecurityInitializer = true; SecurityInitializer = securityInitializer; } /// /// Sets the security initializer function, used to initialize/configure securities after creation. /// The initializer will be applied to all universes and manually added securities. /// /// The security initializer function [Obsolete("This method is deprecated. Please use this overload: SetSecurityInitializer(Action securityInitializer)")] [DocumentationAttribute(AddingData)] [DocumentationAttribute(Modeling)] public void SetSecurityInitializer(Action securityInitializer) { SetSecurityInitializer(new FuncSecurityInitializer(security => securityInitializer(security, false))); } /// /// Sets the security initializer function, used to initialize/configure securities after creation. /// The initializer will be applied to all universes and manually added securities. /// /// The security initializer function [DocumentationAttribute(AddingData)] [DocumentationAttribute(Modeling)] public void SetSecurityInitializer(Action securityInitializer) { SetSecurityInitializer(new FuncSecurityInitializer(securityInitializer)); } /// /// Sets the option chain provider, used to get the list of option contracts for an underlying symbol /// /// The option chain provider [DocumentationAttribute(AddingData)] public void SetOptionChainProvider(IOptionChainProvider optionChainProvider) { OptionChainProvider = optionChainProvider; } /// /// Sets the future chain provider, used to get the list of future contracts for an underlying symbol /// /// The future chain provider [DocumentationAttribute(AddingData)] public void SetFutureChainProvider(IFutureChainProvider futureChainProvider) { FutureChainProvider = futureChainProvider; } /// /// Event - v3.0 DATA EVENT HANDLER: (Pattern) Basic template for user to override for receiving all subscription data in a single event /// /// /// TradeBars bars = slice.Bars; /// Ticks ticks = slice.Ticks; /// TradeBar spy = slice["SPY"]; /// List{Tick} aaplTicks = slice["AAPL"] /// Quandl oil = slice["OIL"] /// dynamic anySymbol = slice[symbol]; /// DataDictionary{Quandl} allQuandlData = slice.Get{Quand} /// Quandl oil = slice.Get{Quandl}("OIL") /// /// The current slice of data keyed by symbol string [DocumentationAttribute(HandlingData)] public virtual void OnData(Slice slice) { // as a default implementation, let's look for and call OnData(Slice) just in case a user forgot to use the override keyword if (!_checkedForOnDataSlice) { _checkedForOnDataSlice = true; var method = GetType().GetMethods() .Where(x => x.Name == "OnData") .Where(x => x.DeclaringType != typeof(QCAlgorithm)) .Where(x => x.GetParameters().Length == 1) .FirstOrDefault(x => x.GetParameters()[0].ParameterType == typeof(Slice)); if (method == null) { return; } var self = Expression.Constant(this); var parameter = Expression.Parameter(typeof(Slice), "data"); var call = Expression.Call(self, method, parameter); var lambda = Expression.Lambda>(call, parameter); _onDataSlice = lambda.Compile(); } // if we have it, then invoke it if (_onDataSlice != null) { _onDataSlice(slice); } } /// /// Event handler to be called when there's been a split event /// /// The current time slice splits [DocumentationAttribute(HandlingData)] public virtual void OnSplits(Splits splits) { } /// /// Event handler to be called when there's been a dividend event /// /// The current time slice dividends [DocumentationAttribute(HandlingData)] public virtual void OnDividends(Dividends dividends) { } /// /// Event handler to be called when there's been a delistings event /// /// The current time slice delistings [DocumentationAttribute(HandlingData)] public virtual void OnDelistings(Delistings delistings) { } /// /// Event handler to be called when there's been a symbol changed event /// /// The current time slice symbol changed events [DocumentationAttribute(HandlingData)] public virtual void OnSymbolChangedEvents(SymbolChangedEvents symbolsChanged) { } /// /// Event fired each time the we add/remove securities from the data feed /// /// Security additions/removals for this time step [DocumentationAttribute(AddingData)] [DocumentationAttribute(Universes)] public virtual void OnSecuritiesChanged(SecurityChanges changes) { } /// /// Margin call event handler. This method is called right before the margin call orders are placed in the market. /// /// The orders to be executed to bring this algorithm within margin limits [DocumentationAttribute(Modeling)] [DocumentationAttribute(TradingAndOrders)] public virtual void OnMarginCall(List requests) { } /// /// Margin call warning event handler. This method is called when Portfolio.MarginRemaining is under 5% of your Portfolio.TotalPortfolioValue /// [DocumentationAttribute(Modeling)] [DocumentationAttribute(TradingAndOrders)] public virtual void OnMarginCallWarning() { } /// /// End of a trading day event handler. This method is called at the end of the algorithm day (or multiple times if trading multiple assets). /// /// Method is called 10 minutes before closing to allow user to close out position. /// Deprecated because different assets have different market close times, /// and because Python does not support two methods with the same name [Obsolete("This method is deprecated and will be removed after August 2021. Please use this overload: OnEndOfDay(Symbol symbol)")] [DocumentationAttribute(HandlingData)] [StubsIgnore] public virtual void OnEndOfDay() { } /// /// End of a trading day event handler. This method is called at the end of the algorithm day (or multiple times if trading multiple assets). /// /// /// This method is left for backwards compatibility and is invoked via , if that method is /// override then this method will not be called without a called to base.OnEndOfDay(string) /// /// Asset symbol for this end of day event. Forex and equities have different closing hours. [DocumentationAttribute(HandlingData)] [StubsIgnore] public virtual void OnEndOfDay(string symbol) { } /// /// End of a trading day event handler. This method is called at the end of the algorithm day (or multiple times if trading multiple assets). /// /// Asset symbol for this end of day event. Forex and equities have different closing hours. [DocumentationAttribute(HandlingData)] [StubsAvoidImplicits] public virtual void OnEndOfDay(Symbol symbol) { OnEndOfDay(symbol.ToString()); } /// /// End of algorithm run event handler. This method is called at the end of a backtest or live trading operation. Intended for closing out logs. /// [DocumentationAttribute(HandlingData)] public virtual void OnEndOfAlgorithm() { } /// /// Order fill event handler. On an order fill update the resulting information is passed to this method. /// /// Order event details containing details of the events /// This method can be called asynchronously and so should only be used by seasoned C# experts. Ensure you use proper locks on thread-unsafe objects [DocumentationAttribute(TradingAndOrders)] public virtual void OnOrderEvent(OrderEvent orderEvent) { } /// /// Option assignment event handler. On an option assignment event for short legs the resulting information is passed to this method. /// /// Option exercise event details containing details of the assignment /// This method can be called asynchronously and so should only be used by seasoned C# experts. Ensure you use proper locks on thread-unsafe objects [DocumentationAttribute(TradingAndOrders)] public virtual void OnAssignmentOrderEvent(OrderEvent assignmentEvent) { } /// /// Brokerage message event handler. This method is called for all types of brokerage messages. /// [DocumentationAttribute(LiveTrading)] [DocumentationAttribute(Modeling)] [DocumentationAttribute(TradingAndOrders)] public virtual void OnBrokerageMessage(BrokerageMessageEvent messageEvent) { } /// /// Brokerage disconnected event handler. This method is called when the brokerage connection is lost. /// [DocumentationAttribute(LiveTrading)] public virtual void OnBrokerageDisconnect() { } /// /// Brokerage reconnected event handler. This method is called when the brokerage connection is restored after a disconnection. /// [DocumentationAttribute(LiveTrading)] public virtual void OnBrokerageReconnect() { } /// /// Update the internal algorithm time frontier. /// /// For internal use only to advance time. /// Current utc datetime. [DocumentationAttribute(HandlingData)] public void SetDateTime(DateTime frontier) { _timeKeeper.SetUtcDateTime(frontier); if (_locked && IsWarmingUp && Time >= _start) { SetFinishedWarmingUp(); } } /// /// Sets the time zone of the property in the algorithm /// /// The desired time zone [DocumentationAttribute(HandlingData)] public void SetTimeZone(string timeZone) { DateTimeZone tz; try { tz = DateTimeZoneProviders.Tzdb[timeZone]; } catch (DateTimeZoneNotFoundException) { throw new ArgumentException($"TimeZone with id '{timeZone}' was not found. For a complete list of time zones please visit: http://en.wikipedia.org/wiki/List_of_tz_database_time_zones"); } SetTimeZone(tz); } /// /// Sets the time zone of the property in the algorithm /// /// The desired time zone [DocumentationAttribute(HandlingData)] public void SetTimeZone(DateTimeZone timeZone) { if (_locked) { throw new InvalidOperationException("Algorithm.SetTimeZone(): Cannot change time zone after algorithm running."); } if (timeZone == null) throw new ArgumentNullException(nameof(timeZone)); _timeKeeper.AddTimeZone(timeZone); _localTimeKeeper = _timeKeeper.GetLocalTimeKeeper(timeZone); // the time rules need to know the default time zone as well TimeRules.SetDefaultTimeZone(timeZone); DateRules.SetDefaultTimeZone(timeZone); // In BackTest mode we reset the Algorithm time to reflect the new timezone // startDate is set by the user so we expect it to be for their timezone already // so there is no need to update it. if (!LiveMode) { _start = _startDate; SetDateTime(_startDate.ConvertToUtc(TimeZone)); } // In live mode we need to adjust startDate to reflect the new timezone // startDate is set by Lean to the default timezone (New York), so we must update it here else { SetLiveModeStartDate(); } } /// /// Sets the brokerage to emulate in backtesting or paper trading. /// This can be used for brokerages that have been implemented in LEAN /// /// The brokerage to emulate /// The account type (Cash or Margin) [DocumentationAttribute(Modeling)] public void SetBrokerageModel(BrokerageName brokerage, AccountType accountType = AccountType.Margin) { SetBrokerageModel(Brokerages.BrokerageModel.Create(Transactions, brokerage, accountType)); } /// /// Sets the brokerage to emulate in backtesting or paper trading. /// This can be used to set a custom brokerage model. /// /// The brokerage model to use [DocumentationAttribute(Modeling)] public void SetBrokerageModel(IBrokerageModel model) { BrokerageModel = model; if (!_userSetSecurityInitializer) { // purposefully use the direct setter vs Set method so we don't flip the switch :/ SecurityInitializer = new BrokerageModelSecurityInitializer(model, SecuritySeeder.Null); // update models on securities added earlier (before SetBrokerageModel is called) foreach (var kvp in Securities) { var security = kvp.Value; // save the existing leverage specified in AddSecurity, // if Leverage needs to be set in a SecurityInitializer, // SetSecurityInitializer must be called before SetBrokerageModel var leverage = security.Leverage; SecurityInitializer.Initialize(security); // restore the saved leverage security.SetLeverage(leverage); } } } /// /// Sets the implementation used to handle messages from the brokerage. /// The default implementation will forward messages to debug or error /// and when a occurs, the algorithm /// is stopped. /// /// The message handler to use [DocumentationAttribute(Modeling)] [DocumentationAttribute(Logging)] public void SetBrokerageMessageHandler(IBrokerageMessageHandler handler) { BrokerageMessageHandler = handler ?? throw new ArgumentNullException(nameof(handler)); } /// /// Sets the risk free interest rate model to be used in the algorithm /// /// The risk free interest rate model to use [DocumentationAttribute(Modeling)] public void SetRiskFreeInterestRateModel(IRiskFreeInterestRateModel model) { RiskFreeInterestRateModel = model ?? throw new ArgumentNullException(nameof(model)); } /// /// Sets the benchmark used for computing statistics of the algorithm to the specified symbol /// /// symbol to use as the benchmark /// Is the symbol an equity, forex, base, etc. Default SecurityType.Equity /// /// Must use symbol that is available to the trade engine in your data store(not strictly enforced) /// [Obsolete("Symbol implicit operator to string is provided for algorithm use only.")] [DocumentationAttribute(TradingAndOrders)] [DocumentationAttribute(SecuritiesAndPortfolio)] [DocumentationAttribute(Indicators)] public void SetBenchmark(SecurityType securityType, string symbol) { if (_locked) { throw new InvalidOperationException("Algorithm.SetBenchmark(): Cannot change Benchmark after algorithm initialized."); } var market = GetMarket(null, symbol, securityType, defaultMarket: Market.USA); var benchmarkSymbol = QuantConnect.Symbol.Create(symbol, securityType, market); SetBenchmark(benchmarkSymbol); } /// /// Sets the benchmark used for computing statistics of the algorithm to the specified ticker, defaulting to SecurityType.Equity /// if the ticker doesn't exist in the algorithm /// /// Ticker to use as the benchmark /// /// Overload to accept ticker without passing SecurityType. If ticker is in portfolio it will use that SecurityType, otherwise will default to SecurityType.Equity /// [DocumentationAttribute(TradingAndOrders)] [DocumentationAttribute(SecuritiesAndPortfolio)] [DocumentationAttribute(Indicators)] public void SetBenchmark(string ticker) { Symbol symbol; // Check the cache for the symbol if (!SymbolCache.TryGetSymbol(ticker, out symbol)) { // Check our securities for a symbol matched with this ticker symbol = Securities.FirstOrDefault(x => x.Key.Value == ticker).Key; // If we didn't find a symbol matching our ticker, create one. if (symbol == null) { Debug($"Warning: SetBenchmark({ticker}): no existing symbol found, benchmark security will be added with {SecurityType.Equity} type."); symbol = QuantConnect.Symbol.Create(ticker, SecurityType.Equity, Market.USA); } } // Send our symbol through SetBenchmark(symbol); } /// /// Sets the benchmark used for computing statistics of the algorithm to the specified symbol /// /// symbol to use as the benchmark [DocumentationAttribute(TradingAndOrders)] [DocumentationAttribute(SecuritiesAndPortfolio)] [DocumentationAttribute(Indicators)] public void SetBenchmark(Symbol symbol) { if (_locked) { throw new InvalidOperationException("Algorithm.SetBenchmark(): Cannot change Benchmark after algorithm initialized."); } // Create our security benchmark Benchmark = SecurityBenchmark.CreateInstance(Securities, symbol); } /// /// Sets the specified function as the benchmark, this function provides the value of /// the benchmark at each date/time requested /// /// The benchmark producing function [DocumentationAttribute(TradingAndOrders)] [DocumentationAttribute(SecuritiesAndPortfolio)] [DocumentationAttribute(Indicators)] public void SetBenchmark(Func benchmark) { if (_locked) { throw new InvalidOperationException("Algorithm.SetBenchmark(): Cannot change Benchmark after algorithm initialized."); } Benchmark = new FuncBenchmark(benchmark); } /// /// Benchmark /// /// Use Benchmark to override default symbol based benchmark, and create your own benchmark. For example a custom moving average benchmark /// [DocumentationAttribute(TradingAndOrders)] [DocumentationAttribute(SecuritiesAndPortfolio)] [DocumentationAttribute(Indicators)] public IBenchmark Benchmark { get; private set; } /// /// Sets name to the currently running backtest /// /// The name for the backtest public void SetName(string name) { Name = name; } /// /// Adds a tag to the algorithm /// /// The tag to add public void AddTag(string tag) { if (!string.IsNullOrEmpty(tag?.Trim())) { if (Tags.Count >= MaxTagsCount) { if (!_tagsLimitReachedLogSent) { Log($"Warning: AddTag({tag}): Unable to add tag. Tags are limited to a maximum of {MaxTagsCount}."); _tagsLimitReachedLogSent = true; } return; } // We'll only notify the tad update after the algorithm has been initialized if (Tags.Add(tag.Truncate(MaxNameAndTagsLength)) && _locked) { TagsUpdated?.Invoke(this, Tags); } } } /// /// Sets the tags for the algorithm /// /// The tags public void SetTags(HashSet tags) { Tags = tags; } /// /// Sets the account currency cash symbol this algorithm is to manage, as well as /// the starting cash in this currency if given /// /// Has to be called during before /// calling or adding any /// The account currency cash symbol to set /// The account currency starting cash to set [DocumentationAttribute(SecuritiesAndPortfolio)] public void SetAccountCurrency(string accountCurrency, decimal? startingCash = null) { if (_locked) { throw new InvalidOperationException("Algorithm.SetAccountCurrency(): " + "Cannot change AccountCurrency after algorithm initialized."); } if (startingCash == null) { Debug($"Changing account currency from {AccountCurrency} to {accountCurrency}..."); } else { Debug($"Changing account currency from {AccountCurrency} to {accountCurrency}, with a starting cash of {startingCash}..."); } Portfolio.SetAccountCurrency(accountCurrency, startingCash); } /// /// Set initial cash for the strategy while backtesting. During live mode this value is ignored /// and replaced with the actual cash of your brokerage account. /// /// Starting cash for the strategy backtest /// Alias of SetCash(decimal) [DocumentationAttribute(SecuritiesAndPortfolio)] public void SetCash(double startingCash) { SetCash((decimal)startingCash); } /// /// Set initial cash for the strategy while backtesting. During live mode this value is ignored /// and replaced with the actual cash of your brokerage account. /// /// Starting cash for the strategy backtest /// Alias of SetCash(decimal) [DocumentationAttribute(SecuritiesAndPortfolio)] public void SetCash(int startingCash) { SetCash((decimal)startingCash); } /// /// Set initial cash for the strategy while backtesting. During live mode this value is ignored /// and replaced with the actual cash of your brokerage account. /// /// Starting cash for the strategy backtest [DocumentationAttribute(SecuritiesAndPortfolio)] public void SetCash(decimal startingCash) { if (!_locked) { Portfolio.SetCash(startingCash); } else { throw new InvalidOperationException("Algorithm.SetCash(): Cannot change cash available after algorithm initialized."); } } /// /// Set the cash for the specified symbol /// /// The cash symbol to set /// Decimal cash value of portfolio /// The current conversion rate for the [DocumentationAttribute(SecuritiesAndPortfolio)] public void SetCash(string symbol, decimal startingCash, decimal conversionRate = 0) { if (!_locked) { Portfolio.SetCash(symbol, startingCash, conversionRate); } else { throw new InvalidOperationException("Algorithm.SetCash(): Cannot change cash available after algorithm initialized."); } } /// /// Set the start date for backtest. /// /// Int starting date 1-30 /// Int month starting date /// Int year starting date /// Wrapper for SetStartDate(DateTime). /// Must be less than end date. /// Ignored in live trading mode. /// [DocumentationAttribute(HandlingData)] public void SetStartDate(int year, int month, int day) { try { var start = new DateTime(year, month, day); // We really just want the date of the start, so it's 12am of the requested day (first moment of the day) start = start.Date; SetStartDate(start); } catch (Exception err) { throw new ArgumentException($"Date Invalid: {err.Message}"); } } /// /// Set the end date for a backtest run /// /// Int end date 1-30 /// Int month end date /// Int year end date /// Wrapper for SetEndDate(datetime). /// [DocumentationAttribute(HandlingData)] public void SetEndDate(int year, int month, int day) { try { var end = new DateTime(year, month, day); // we want the end date to be just before the next day (last moment of the day) end = end.Date.AddDays(1).Subtract(TimeSpan.FromTicks(1)); SetEndDate(end); } catch (Exception err) { throw new ArgumentException($"Date Invalid: {err.Message}"); } } /// /// Set the algorithm id (backtestId or live deployId for the algorithm). /// /// String Algorithm Id /// Intended for internal QC Lean Engine use only as a setter for AlgorithmId [DocumentationAttribute(HandlingData)] public void SetAlgorithmId(string algorithmId) { _algorithmId = algorithmId; } /// /// Set the start date for the backtest /// /// Datetime Start date for backtest /// Must be less than end date and within data available /// [DocumentationAttribute(HandlingData)] public void SetStartDate(DateTime start) { // no need to set this value in live mode, will be set using the current time. if (_liveMode) return; //Round down start = start.RoundDown(TimeSpan.FromDays(1)); //Validate the start date: //1. Check range; if (start < (new DateTime(1900, 01, 01))) { throw new ArgumentOutOfRangeException(nameof(start), "Please select a start date after January 1st, 1900."); } //2. Check future date var todayInAlgorithmTimeZone = DateTime.UtcNow.ConvertFromUtc(TimeZone).Date; if (start > todayInAlgorithmTimeZone) { throw new ArgumentOutOfRangeException(nameof(start), "Please select start date less than today"); } //3. Check not locked already: if (!_locked) { _start = _startDate = start; SetDateTime(_startDate.ConvertToUtc(TimeZone)); } else { throw new InvalidOperationException("Algorithm.SetStartDate(): Cannot change start date after algorithm initialized."); } } /// /// Set the end date for a backtest. /// /// Datetime value for end date /// Must be greater than the start date /// [DocumentationAttribute(HandlingData)] public void SetEndDate(DateTime end) { // no need to set this value in live mode, will be set using the current time. if (_liveMode) return; //1. Check not locked already: if (_locked) { throw new InvalidOperationException("Algorithm.SetEndDate(): Cannot change end date after algorithm initialized."); } //Validate: //2. Check Range: var yesterdayInAlgorithmTimeZone = DateTime.UtcNow.ConvertFromUtc(TimeZone).Date.AddDays(-1); if (end > yesterdayInAlgorithmTimeZone) { end = yesterdayInAlgorithmTimeZone; } //3. Make this at the very end of the requested date _endDate = end.RoundDown(TimeSpan.FromDays(1)).AddDays(1).AddTicks(-1); } /// /// Lock the algorithm initialization to avoid user modifiying cash and data stream subscriptions /// /// Intended for Internal QC Lean Engine use only to prevent accidental manipulation of important properties [DocumentationAttribute(AlgorithmFramework)] public void SetLocked() { _locked = true; // The algorithm is initialized, we can now send the initial name and tags updates NameUpdated?.Invoke(this, Name); TagsUpdated?.Invoke(this, Tags); } /// /// Gets whether or not this algorithm has been locked and fully initialized /// [DocumentationAttribute(AlgorithmFramework)] public bool GetLocked() { return _locked; } /// /// Set live mode state of the algorithm run: Public setter for the algorithm property LiveMode. /// [DocumentationAttribute(LiveTrading)] public void SetLiveMode(bool live) { if (!_locked) { _liveMode = live; Notify = new NotificationManager(live); TradeBuilder.SetLiveMode(live); Securities.SetLiveMode(live); Transactions.SetLiveMode(live); if (live) { SetLiveModeStartDate(); _algorithmMode = AlgorithmMode.Live; } } } /// /// Sets the algorithm running mode /// /// Algorithm mode public void SetAlgorithmMode(AlgorithmMode algorithmMode) { if (!_locked) { _algorithmMode = algorithmMode; SetLiveMode(_algorithmMode == AlgorithmMode.Live); } } /// /// Sets the algorithm deployment target /// /// Deployment target public void SetDeploymentTarget(DeploymentTarget deploymentTarget) { if (!_locked) { _deploymentTarget = deploymentTarget; } } /// /// Set the implementation to generate trades from executions and market price updates /// [DocumentationAttribute(TradingAndOrders)] public void SetTradeBuilder(ITradeBuilder tradeBuilder) { TradeBuilder = tradeBuilder; TradeBuilder.SetLiveMode(LiveMode); TradeBuilder.SetSecurityManager(Securities); } /// /// Add specified data to our data subscriptions. QuantConnect will funnel this data to the handle data routine. /// /// MarketType Type: Equity, Commodity, Future, FOREX or Crypto /// The security ticker /// Resolution of the Data Required /// When no data available on a tradebar, return the last data that was generated /// Use extended market hours data /// The contract mapping mode to use for the security /// The price scaling mode to use for the security [DocumentationAttribute(AddingData)] public Security AddSecurity(SecurityType securityType, string ticker, Resolution? resolution = null, bool fillForward = true, bool extendedMarketHours = false, DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null) { return AddSecurity(securityType, ticker, resolution, fillForward, Security.NullLeverage, extendedMarketHours, dataMappingMode, dataNormalizationMode); } /// /// Add specified data to required list. QC will funnel this data to the handle data routine. /// /// MarketType Type: Equity, Commodity, Future, FOREX or Crypto /// The security ticker /// Resolution of the Data Required /// When no data available on a tradebar, return the last data that was generated /// Custom leverage per security /// Use extended market hours data /// The contract mapping mode to use for the security /// The price scaling mode to use for the security /// AddSecurity(SecurityType securityType, Symbol symbol, Resolution resolution, bool fillForward, decimal leverage, bool extendedMarketHours) [DocumentationAttribute(AddingData)] public Security AddSecurity(SecurityType securityType, string ticker, Resolution? resolution, bool fillForward, decimal leverage, bool extendedMarketHours, DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null) { return AddSecurity(securityType, ticker, resolution, null, fillForward, leverage, extendedMarketHours, dataMappingMode, dataNormalizationMode); } /// /// Set a required SecurityType-symbol and resolution for algorithm /// /// MarketType Type: Equity, Commodity, Future, FOREX or Crypto /// The security ticker, e.g. AAPL /// Resolution of the MarketType required: MarketData, Second or Minute /// The market the requested security belongs to, such as 'usa' or 'fxcm' /// If true, returns the last available data even if none in that timeslice. /// leverage for this security /// Use extended market hours data /// The contract mapping mode to use for the security /// The price scaling mode to use for the security [DocumentationAttribute(AddingData)] public Security AddSecurity(SecurityType securityType, string ticker, Resolution? resolution, string market, bool fillForward, decimal leverage, bool extendedMarketHours, DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null) { // if AddSecurity method is called to add an option or a future, we delegate a call to respective methods if (securityType == SecurityType.Option) { return AddOption(ticker, resolution, market, fillForward, leverage); } if (securityType == SecurityType.Future) { return AddFuture(ticker, resolution, market, fillForward, leverage, extendedMarketHours, dataMappingMode, dataNormalizationMode); } try { market = GetMarket(market, ticker, securityType); Symbol symbol; if (!SymbolCache.TryGetSymbol(ticker, out symbol) || symbol.ID.Market != market || symbol.SecurityType != securityType) { symbol = QuantConnect.Symbol.Create(ticker, securityType, market); } return AddSecurity(symbol, resolution, fillForward, leverage, extendedMarketHours, dataMappingMode, dataNormalizationMode); } catch (Exception err) { Error("Algorithm.AddSecurity(): " + err); return null; } } /// /// Set a required SecurityType-symbol and resolution for algorithm /// /// The security Symbol /// Resolution of the MarketType required: MarketData, Second or Minute /// If true, returns the last available data even if none in that timeslice. /// leverage for this security /// Use extended market hours data /// The contract mapping mode to use for the security /// The price scaling mode to use for the security /// The continuous contract desired offset from the current front month. /// For example, 0 (default) will use the front month, 1 will use the back month contract /// The new Security that was added to the algorithm [DocumentationAttribute(AddingData)] public Security AddSecurity(Symbol symbol, Resolution? resolution = null, bool fillForward = true, decimal leverage = Security.NullLeverage, bool extendedMarketHours = false, DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null, int contractDepthOffset = 0) { // allow users to specify negative numbers, we get the abs of it var contractOffset = (uint)Math.Abs(contractDepthOffset); if (contractOffset > Futures.MaximumContractDepthOffset) { throw new ArgumentOutOfRangeException(nameof(contractDepthOffset), $"'contractDepthOffset' current maximum value is {Futures.MaximumContractDepthOffset}." + $" Front month (0) and only {Futures.MaximumContractDepthOffset} back month contracts are currently supported."); } var isCanonical = symbol.IsCanonical(); // Short-circuit to AddOptionContract because it will add the underlying if required if (!isCanonical && symbol.SecurityType.IsOption()) { return AddOptionContract(symbol, resolution, fillForward, leverage, extendedMarketHours); } var securityResolution = resolution; var securityFillForward = fillForward; if (isCanonical) { // canonical options and futures are daily only securityResolution = Resolution.Daily; securityFillForward = false; } var isFilteredSubscription = !isCanonical; List configs; // we pass dataNormalizationMode to SubscriptionManager.SubscriptionDataConfigService.Add conditionally, // so the default value for its argument is used when the it is null here. if (dataNormalizationMode.HasValue) { configs = SubscriptionManager.SubscriptionDataConfigService.Add(symbol, securityResolution, securityFillForward, extendedMarketHours, isFilteredSubscription, dataNormalizationMode: dataNormalizationMode.Value, contractDepthOffset: (uint)contractDepthOffset); } else { configs = SubscriptionManager.SubscriptionDataConfigService.Add(symbol, securityResolution, securityFillForward, extendedMarketHours, isFilteredSubscription, contractDepthOffset: (uint)contractDepthOffset); } var security = Securities.CreateSecurity(symbol, configs, leverage); if (isCanonical) { security.IsTradable = false; Securities.Add(security); // add this security to the user defined universe Universe universe; if (!UniverseManager.ContainsKey(symbol)) { var canonicalConfig = configs.First(); var universeSettingsResolution = resolution ?? UniverseSettings.Resolution; var settings = new UniverseSettings(universeSettingsResolution, leverage, fillForward, extendedMarketHours, UniverseSettings.MinimumTimeInUniverse) { Asynchronous = UniverseSettings.Asynchronous }; if (symbol.SecurityType.IsOption()) { universe = new OptionChainUniverse((Option)security, settings); } else { // add the expected configurations of the canonical symbol right away, will allow it to warmup and indicators register to them var dataTypes = SubscriptionManager.LookupSubscriptionConfigDataTypes(SecurityType.Future, GetResolution(symbol, resolution, null), isCanonical: false); var continuousUniverseSettings = new UniverseSettings(settings) { ExtendedMarketHours = extendedMarketHours, DataMappingMode = dataMappingMode ?? UniverseSettings.GetUniverseNormalizationModeOrDefault(symbol.SecurityType, symbol.ID.Market), DataNormalizationMode = dataNormalizationMode ?? UniverseSettings.GetUniverseNormalizationModeOrDefault(symbol.SecurityType), ContractDepthOffset = (int)contractOffset, SubscriptionDataTypes = dataTypes, Asynchronous = UniverseSettings.Asynchronous }; ContinuousContractUniverse.AddConfigurations(SubscriptionManager.SubscriptionDataConfigService, continuousUniverseSettings, security.Symbol); // let's add a MHDB entry for the continuous symbol using the associated security var continuousContractSymbol = ContinuousContractUniverse.CreateSymbol(security.Symbol); MarketHoursDatabase.SetEntry(continuousContractSymbol.ID.Market, continuousContractSymbol.ID.Symbol, continuousContractSymbol.ID.SecurityType, security.Exchange.Hours); AddUniverse(new ContinuousContractUniverse(security, continuousUniverseSettings, LiveMode, new SubscriptionDataConfig(canonicalConfig, symbol: continuousContractSymbol, // We can use any data type here, since we are not going to use the data. // We just don't want to use the FutureUniverse type because it will force disable extended market hours objectType: typeof(Tick), extendedHours: extendedMarketHours))); universe = new FuturesChainUniverse((Future)security, settings); } AddUniverse(universe); } return security; } return AddToUserDefinedUniverse(security, configs); } /// /// Creates and adds a new security to the algorithm /// /// The equity ticker symbol /// The of market data, Tick, Second, Minute, Hour, or Daily. Default is /// The equity's market, . Default value is null and looked up using BrokerageModel.DefaultMarkets in /// If true, returns the last available data even if none in that timeslice. Default is true /// The requested leverage for this equity. Default is set by /// True to send data during pre and post market sessions. Default is false /// The price scaling mode to use for the equity /// The new security [DocumentationAttribute(AddingData)] public Equity AddEquity(string ticker, Resolution? resolution = null, string market = null, bool fillForward = true, decimal leverage = Security.NullLeverage, bool extendedMarketHours = false, DataNormalizationMode? dataNormalizationMode = null) { return AddSecurity(SecurityType.Equity, ticker, resolution, market, fillForward, leverage, extendedMarketHours, normalizationMode: dataNormalizationMode); } /// /// Creates and adds a new equity security to the algorithm /// /// The underlying equity ticker /// The of market data, Tick, Second, Minute, Hour, or Daily. Default is /// The equity's market, . Default is value null and looked up using BrokerageModel.DefaultMarkets in /// If true, returns the last available data even if none in that timeslice. Default is true /// The requested leverage for this equity. Default is set by /// The new security [DocumentationAttribute(AddingData)] public Option AddOption(string underlying, Resolution? resolution = null, string market = null, bool fillForward = true, decimal leverage = Security.NullLeverage) { market = GetMarket(market, underlying, SecurityType.Option); var underlyingSymbol = QuantConnect.Symbol.Create(underlying, SecurityType.Equity, market); return AddOption(underlyingSymbol, resolution, market, fillForward, leverage); } /// /// Creates and adds a new security to the algorithm. /// This method can be used to add options with non-equity asset classes /// to the algorithm (e.g. Future Options). /// /// Underlying asset Symbol to use as the option's underlying /// The of market data, Tick, Second, Minute, Hour, or Daily. Default is /// The option's market, . Default value is null, but will be resolved using BrokerageModel.DefaultMarkets in /// If true, data will be provided to the algorithm every Second, Minute, Hour, or Day, while the asset is open and depending on the Resolution this option was configured to use. /// The requested leverage for the /// The new option security instance /// [DocumentationAttribute(AddingData)] public Option AddOption(Symbol underlying, Resolution? resolution = null, string market = null, bool fillForward = true, decimal leverage = Security.NullLeverage) { return AddOption(underlying, null, resolution, market, fillForward, leverage); } /// /// Creates and adds a new security to the algorithm. /// This method can be used to add options with non-equity asset classes /// to the algorithm (e.g. Future Options). /// /// Underlying asset Symbol to use as the option's underlying /// The target option ticker. This is useful when the option ticker does not match the underlying, e.g. SPX index and the SPXW weekly option. If null is provided will use underlying /// The of market data, Tick, Second, Minute, Hour, or Daily. Default is /// The option's market, . Default value is null, but will be resolved using BrokerageModel.DefaultMarkets in /// If true, data will be provided to the algorithm every Second, Minute, Hour, or Day, while the asset is open and depending on the Resolution this option was configured to use. /// The requested leverage for the /// The new option security instance /// [DocumentationAttribute(AddingData)] public Option AddOption(Symbol underlying, string targetOption, Resolution? resolution = null, string market = null, bool fillForward = true, decimal leverage = Security.NullLeverage) { var optionType = QuantConnect.Symbol.GetOptionTypeFromUnderlying(underlying); market = GetMarket(market, targetOption, optionType); Symbol canonicalSymbol; string alias; if (!string.IsNullOrEmpty(targetOption)) { alias = $"?{targetOption}"; } else { alias = $"?{underlying.Value}"; } if (!SymbolCache.TryGetSymbol(alias, out canonicalSymbol) || canonicalSymbol.ID.Market != market || !canonicalSymbol.SecurityType.IsOption()) { canonicalSymbol = QuantConnect.Symbol.CreateCanonicalOption(underlying, targetOption, market, alias); } return (Option)AddSecurity(canonicalSymbol, resolution, fillForward, leverage); } /// /// Creates and adds a new security to the algorithm /// /// The future ticker /// The of market data, Tick, Second, Minute, Hour, or Daily. Default is /// The futures market, . Default is value null and looked up using BrokerageModel.DefaultMarkets in /// If true, returns the last available data even if none in that timeslice. Default is true /// The requested leverage for this equity. Default is set by /// Use extended market hours data /// The contract mapping mode to use for the continuous future contract /// The price scaling mode to use for the continuous future contract /// The continuous future contract desired offset from the current front month. /// For example, 0 (default) will use the front month, 1 will use the back month contract /// The new security [DocumentationAttribute(AddingData)] public Future AddFuture(string ticker, Resolution? resolution = null, string market = null, bool fillForward = true, decimal leverage = Security.NullLeverage, bool extendedMarketHours = false, DataMappingMode? dataMappingMode = null, DataNormalizationMode? dataNormalizationMode = null, int contractDepthOffset = 0) { market = GetMarket(market, ticker, SecurityType.Future); Symbol canonicalSymbol; var alias = "/" + ticker; if (!SymbolCache.TryGetSymbol(alias, out canonicalSymbol) || canonicalSymbol.ID.Market != market || canonicalSymbol.SecurityType != SecurityType.Future) { canonicalSymbol = QuantConnect.Symbol.Create(ticker, SecurityType.Future, market, alias); } return (Future)AddSecurity(canonicalSymbol, resolution, fillForward, leverage, extendedMarketHours, dataMappingMode: dataMappingMode, dataNormalizationMode: dataNormalizationMode, contractDepthOffset: contractDepthOffset); } /// /// Creates and adds a new single contract to the algorithm /// /// The futures contract symbol /// The of market data, Tick, Second, Minute, Hour, or Daily. Default is /// If true, returns the last available data even if none in that timeslice. Default is true /// The requested leverage for this equity. Default is set by /// Use extended market hours data /// The new security [DocumentationAttribute(AddingData)] public Future AddFutureContract(Symbol symbol, Resolution? resolution = null, bool fillForward = true, decimal leverage = Security.NullLeverage, bool extendedMarketHours = false) { return (Future)AddSecurity(symbol, resolution, fillForward, leverage, extendedMarketHours); } /// /// Creates and adds a new Future Option contract to the algorithm. /// /// The canonical symbol (i.e. Symbol returned from ) /// Filter to apply to option contracts loaded as part of the universe /// The new security, containing a as its underlying. /// The symbol provided is not canonical. [DocumentationAttribute(AddingData)] public void AddFutureOption(Symbol symbol, Func optionFilter = null) { if (!symbol.IsCanonical()) { throw new ArgumentException("Symbol provided must be canonical (i.e. the Symbol returned from AddFuture(), not AddFutureContract()."); } AddUniverseOptions(symbol, optionFilter); } /// /// Adds a future option contract to the algorithm. /// /// Option contract Symbol /// Resolution of the option contract, i.e. the granularity of the data /// If true, this will fill in missing data points with the previous data point /// The leverage to apply to the option contract /// Use extended market hours data /// Option security /// Symbol is canonical (i.e. a generic Symbol returned from or ) [DocumentationAttribute(AddingData)] public Option AddFutureOptionContract(Symbol symbol, Resolution? resolution = null, bool fillForward = true, decimal leverage = Security.NullLeverage, bool extendedMarketHours = false) { if (symbol.IsCanonical()) { throw new ArgumentException("Expected non-canonical Symbol (i.e. a Symbol representing a specific Future contract"); } return AddOptionContract(symbol, resolution, fillForward, leverage, extendedMarketHours); } /// /// Creates and adds index options to the algorithm. /// /// The underlying ticker of the Index Option /// Resolution of the index option contracts, i.e. the granularity of the data /// The foreign exchange trading market, . Default value is null and looked up using BrokerageModel.DefaultMarkets in /// If true, this will fill in missing data points with the previous data point /// Canonical Option security [DocumentationAttribute(AddingData)] public IndexOption AddIndexOption(string underlying, Resolution? resolution = null, string market = null, bool fillForward = true) { return AddIndexOption(underlying, null, resolution, market, fillForward); } /// /// Creates and adds index options to the algorithm. /// /// The Symbol of the returned from /// Resolution of the index option contracts, i.e. the granularity of the data /// If true, this will fill in missing data points with the previous data point /// Canonical Option security [DocumentationAttribute(AddingData)] public IndexOption AddIndexOption(Symbol symbol, Resolution? resolution = null, bool fillForward = true) { return AddIndexOption(symbol, null, resolution, fillForward); } /// /// Creates and adds index options to the algorithm. /// /// The Symbol of the returned from /// The target option ticker. This is useful when the option ticker does not match the underlying, e.g. SPX index and the SPXW weekly option. If null is provided will use underlying /// Resolution of the index option contracts, i.e. the granularity of the data /// If true, this will fill in missing data points with the previous data point /// Canonical Option security [DocumentationAttribute(AddingData)] public IndexOption AddIndexOption(Symbol symbol, string targetOption, Resolution? resolution = null, bool fillForward = true) { if (symbol.SecurityType != SecurityType.Index) { throw new ArgumentException("Symbol provided must be of type SecurityType.Index"); } return (IndexOption)AddOption(symbol, targetOption, resolution, symbol.ID.Market, fillForward); } /// /// Creates and adds index options to the algorithm. /// /// The underlying ticker of the Index Option /// The target option ticker. This is useful when the option ticker does not match the underlying, e.g. SPX index and the SPXW weekly option. If null is provided will use underlying /// Resolution of the index option contracts, i.e. the granularity of the data /// The foreign exchange trading market, . Default value is null and looked up using BrokerageModel.DefaultMarkets in /// If true, this will fill in missing data points with the previous data point /// Canonical Option security [DocumentationAttribute(AddingData)] public IndexOption AddIndexOption(string underlying, string targetOption, Resolution? resolution = null, string market = null, bool fillForward = true) { return AddIndexOption( QuantConnect.Symbol.Create(underlying, SecurityType.Index, GetMarket(market, underlying, SecurityType.Index)), targetOption, resolution, fillForward); } /// /// Adds an index option contract to the algorithm. /// /// Symbol of the index option contract /// Resolution of the index option contract, i.e. the granularity of the data /// If true, this will fill in missing data points with the previous data point /// Index Option Contract /// The provided Symbol is not an Index Option [DocumentationAttribute(AddingData)] public IndexOption AddIndexOptionContract(Symbol symbol, Resolution? resolution = null, bool fillForward = true) { if (symbol.SecurityType != SecurityType.IndexOption || symbol.IsCanonical()) { throw new ArgumentException("Symbol provided must be non-canonical and of type SecurityType.IndexOption"); } return (IndexOption)AddOptionContract(symbol, resolution, fillForward); } /// /// Creates and adds a new single contract to the algorithm /// /// The option contract symbol /// The of market data, Tick, Second, Minute, Hour, or Daily. Default is /// If true, returns the last available data even if none in that timeslice. Default is true /// The requested leverage for this equity. Default is set by /// Use extended market hours data /// The new security [DocumentationAttribute(AddingData)] public Option AddOptionContract(Symbol symbol, Resolution? resolution = null, bool fillForward = true, decimal leverage = Security.NullLeverage, bool extendedMarketHours = false) { if (symbol == null || !symbol.SecurityType.IsOption() || symbol.Underlying == null) { throw new ArgumentException($"Unexpected option symbol {symbol}. " + $"Please provide a valid option contract with it's underlying symbol set."); } // add underlying if not present var underlying = symbol.Underlying; Security underlyingSecurity; List underlyingConfigs; if (!Securities.TryGetValue(underlying, out underlyingSecurity) || // The underlying might have been removed, let's see if there's already a subscription for it (!underlyingSecurity.IsTradable && SubscriptionManager.SubscriptionDataConfigService.GetSubscriptionDataConfigs(underlying).Count == 0)) { underlyingSecurity = AddSecurity(underlying, resolution, fillForward, leverage, extendedMarketHours); underlyingConfigs = SubscriptionManager.SubscriptionDataConfigService .GetSubscriptionDataConfigs(underlying); } else if (underlyingSecurity != null && underlyingSecurity.IsDelisted) { throw new ArgumentException($"The underlying {underlying.SecurityType} asset ({underlying.Value}) is delisted " + $"(current time is {Time})"); } else { underlyingConfigs = SubscriptionManager.SubscriptionDataConfigService .GetSubscriptionDataConfigs(underlying); var dataNormalizationMode = underlyingConfigs.DataNormalizationMode(); if (dataNormalizationMode != DataNormalizationMode.Raw && _locked) { // We check the "locked" flag here because during initialization we need to load existing open orders and holdings from brokerages. // There is no data streaming yet, so it is safe to change the data normalization mode to Raw. throw new ArgumentException($"The underlying {underlying.SecurityType} asset ({underlying.Value}) is set to " + $"{dataNormalizationMode}, please change this to DataNormalizationMode.Raw with the " + "SetDataNormalization() method" ); } } var configs = SubscriptionManager.SubscriptionDataConfigService.Add(symbol, resolution, fillForward, extendedMarketHours, dataNormalizationMode: DataNormalizationMode.Raw); var option = (Option)Securities.CreateSecurity(symbol, configs, leverage, underlying: underlyingSecurity); underlyingConfigs.SetDataNormalizationMode(DataNormalizationMode.Raw); // For backward compatibility we need to refresh the security DataNormalizationMode Property underlyingSecurity.RefreshDataNormalizationModeProperty(); Securities.Add(option); // get or create the universe var universeSymbol = OptionContractUniverse.CreateSymbol(symbol.ID.Market, symbol.Underlying.SecurityType); Universe universe; if (!UniverseManager.TryGetValue(universeSymbol, out universe)) { var settings = new UniverseSettings(UniverseSettings) { DataNormalizationMode = DataNormalizationMode.Raw, Resolution = underlyingConfigs.GetHighestResolution(), ExtendedMarketHours = extendedMarketHours }; universe = AddUniverse(new OptionContractUniverse(new SubscriptionDataConfig(configs.First(), // We can use any data type here, since we are not going to use the data. // We just don't want to use the OptionUniverse type because it will force disable extended market hours symbol: universeSymbol, objectType: typeof(Tick), extendedHours: extendedMarketHours), settings)); } // update the universe var optionUniverse = universe as OptionContractUniverse; if (optionUniverse != null) { foreach (var subscriptionDataConfig in configs.Concat(underlyingConfigs)) { optionUniverse.Add(subscriptionDataConfig); } } return option; } /// /// Creates and adds a new security to the algorithm /// /// The currency pair /// The of market data, Tick, Second, Minute, Hour, or Daily. Default is /// The foreign exchange trading market, . Default value is null and looked up using BrokerageModel.DefaultMarkets in /// If true, returns the last available data even if none in that timeslice. Default is true /// The requested leverage for this equity. Default is set by /// The new security [DocumentationAttribute(AddingData)] public Forex AddForex(string ticker, Resolution? resolution = null, string market = null, bool fillForward = true, decimal leverage = Security.NullLeverage) { return AddSecurity(SecurityType.Forex, ticker, resolution, market, fillForward, leverage, false); } /// /// Creates and adds a new security to the algorithm /// /// The currency pair /// The of market data, Tick, Second, Minute, Hour, or Daily. Default is /// The cfd trading market, . Default value is null and looked up using BrokerageModel.DefaultMarkets in /// If true, returns the last available data even if none in that timeslice. Default is true /// The requested leverage for this equity. Default is set by /// The new security [DocumentationAttribute(AddingData)] public Cfd AddCfd(string ticker, Resolution? resolution = null, string market = null, bool fillForward = true, decimal leverage = Security.NullLeverage) { return AddSecurity(SecurityType.Cfd, ticker, resolution, market, fillForward, leverage, false); } /// /// Creates and adds a new security to the algorithm /// /// The currency pair /// The of market data, Tick, Second, Minute, Hour, or Daily. Default is /// The index trading market, . Default value is null and looked up using BrokerageModel.DefaultMarkets in /// If true, returns the last available data even if none in that timeslice. Default is true /// The new security [DocumentationAttribute(AddingData)] public Index AddIndex(string ticker, Resolution? resolution = null, string market = null, bool fillForward = true) { var index = AddSecurity(SecurityType.Index, ticker, resolution, market, fillForward, 1, false); return index; } /// /// Creates and adds a new security to the algorithm /// /// The currency pair /// The of market data, Tick, Second, Minute, Hour, or Daily. Default is /// The cfd trading market, . Default value is null and looked up using BrokerageModel.DefaultMarkets in /// If true, returns the last available data even if none in that timeslice. Default is true /// The requested leverage for this equity. Default is set by /// The new security [DocumentationAttribute(AddingData)] public Crypto AddCrypto(string ticker, Resolution? resolution = null, string market = null, bool fillForward = true, decimal leverage = Security.NullLeverage) { return AddSecurity(SecurityType.Crypto, ticker, resolution, market, fillForward, leverage, false); } /// /// Creates and adds a new security to the algorithm /// /// The currency pair /// The of market data, Tick, Second, Minute, Hour, or Daily. Default is /// The cfd trading market, . Default value is null and looked up using BrokerageModel.DefaultMarkets in /// If true, returns the last available data even if none in that timeslice. Default is true /// The requested leverage for this equity. Default is set by /// The new security [DocumentationAttribute(AddingData)] public CryptoFuture AddCryptoFuture(string ticker, Resolution? resolution = null, string market = null, bool fillForward = true, decimal leverage = Security.NullLeverage) { return AddSecurity(SecurityType.CryptoFuture, ticker, resolution, market, fillForward, leverage, false); } /// /// Removes the security with the specified symbol. This will cancel all /// open orders and then liquidate any existing holdings /// /// The symbol of the security to be removed /// Sugar syntax for [DocumentationAttribute(AddingData)] public bool RemoveOptionContract(Symbol symbol) { return RemoveSecurity(symbol); } /// /// Removes the security with the specified symbol. This will cancel all /// open orders and then liquidate any existing holdings /// /// The symbol of the security to be removed [DocumentationAttribute(AddingData)] public bool RemoveSecurity(Symbol symbol) { Security security; if (!Securities.TryGetValue(symbol, out security)) { return false; } if (!IsWarmingUp) { // cancel open orders Transactions.CancelOpenOrders(security.Symbol); } // liquidate if invested if (security.Invested) { Liquidate(security.Symbol); } // Mark security as not tradable security.Reset(); if (symbol.IsCanonical()) { // remove underlying equity data if it's marked as internal foreach (var kvp in UniverseManager.Where(x => x.Value.Configuration.Symbol == symbol || x.Value.Configuration.Symbol == ContinuousContractUniverse.CreateSymbol(symbol))) { var universe = kvp.Value; // remove underlying if not used by other universes var otherUniverses = UniverseManager.Select(ukvp => ukvp.Value).Where(u => !ReferenceEquals(u, universe)).ToList(); if (symbol.HasUnderlying) { var underlying = Securities[symbol.Underlying]; if (!otherUniverses.Any(u => u.Members.ContainsKey(underlying.Symbol))) { RemoveSecurity(underlying.Symbol); } } // remove child securities (option contracts for option chain universes) if not used in other universes // we order the securities so that the removal is deterministic, it will liquidate any holdings foreach (var child in universe.Members.Values.OrderBy(security1 => security1.Symbol)) { if (!otherUniverses.Any(u => u.Members.ContainsKey(child.Symbol)) && !child.Symbol.IsCanonical()) { RemoveSecurity(child.Symbol); } } // finally, dispose and remove the canonical security from the universe manager UniverseManager.Remove(kvp.Key); _universeSelectionUniverses.Remove(security.Symbol); } } else { lock (_pendingUniverseAdditionsLock) { // we need to handle existing universes and pending to be added universes, that will be pushed // at the end of this time step see OnEndOfTimeStep() foreach (var universe in UniverseManager.Select(x => x.Value).OfType()) { universe.Remove(symbol); } // for existing universes we need to purge pending additions too, also handled at OnEndOfTimeStep() _pendingUserDefinedUniverseSecurityAdditions.RemoveAll(addition => addition.Security.Symbol == symbol); } } return true; } /// /// AddData a new user defined data source, requiring only the minimum config options. /// The data is added with a default time zone of NewYork (Eastern Daylight Savings Time) /// /// Key/Ticker for data /// Resolution of the data /// The new /// Generic type T must implement base data [DocumentationAttribute(AddingData)] public Security AddData(string ticker, Resolution? resolution = null) where T : IBaseData, new() { //Add this new generic data as a tradeable security: // Defaults:extended market hours" = true because we want events 24 hours, // fillforward = false because only want to trigger when there's new custom data. // leverage = 1 because no leverage on nonmarket data? return AddData(ticker, resolution, fillForward: false, leverage: 1m); } /// /// AddData a new user defined data source, requiring only the minimum config options. /// The data is added with a default time zone of NewYork (Eastern Daylight Savings Time) /// /// The underlying symbol for the custom data /// Resolution of the data /// The new /// Generic type T must implement base data [DocumentationAttribute(AddingData)] public Security AddData(Symbol underlying, Resolution? resolution = null) where T : IBaseData, new() { //Add this new generic data as a tradeable security: // Defaults:extended market hours" = true because we want events 24 hours, // fillforward = false because only want to trigger when there's new custom data. // leverage = 1 because no leverage on nonmarket data? return AddData(underlying, resolution, fillForward: false, leverage: 1m); } /// /// AddData a new user defined data source, requiring only the minimum config options. /// The data is added with a default time zone of NewYork (Eastern Daylight Savings Time) /// /// Key/Ticker for data /// Resolution of the Data Required /// When no data available on a tradebar, return the last data that was generated /// Custom leverage per security /// The new /// Generic type T must implement base data [DocumentationAttribute(AddingData)] public Security AddData(string ticker, Resolution? resolution, bool fillForward, decimal leverage = 1.0m) where T : IBaseData, new() { return AddData(ticker, resolution, null, fillForward, leverage); } /// /// AddData a new user defined data source, requiring only the minimum config options. /// The data is added with a default time zone of NewYork (Eastern Daylight Savings Time) /// /// The underlying symbol for the custom data /// Resolution of the Data Required /// When no data available on a tradebar, return the last data that was generated /// Custom leverage per security /// The new /// Generic type T must implement base data [DocumentationAttribute(AddingData)] public Security AddData(Symbol underlying, Resolution? resolution, bool fillForward, decimal leverage = 1.0m) where T : IBaseData, new() { return AddData(underlying, resolution, null, fillForward, leverage); } /// /// AddData a new user defined data source, requiring only the minimum config options. /// /// Key/Ticker for data /// Resolution of the Data Required /// Specifies the time zone of the raw data /// When no data available on a tradebar, return the last data that was generated /// Custom leverage per security /// The new /// Generic type T must implement base data [DocumentationAttribute(AddingData)] public Security AddData(string ticker, Resolution? resolution, DateTimeZone timeZone, bool fillForward = false, decimal leverage = 1.0m) where T : IBaseData, new() { return AddData(typeof(T), ticker, resolution, timeZone, fillForward, leverage); } /// /// AddData a new user defined data source, requiring only the minimum config options. /// /// The underlying symbol for the custom data /// Resolution of the Data Required /// Specifies the time zone of the raw data /// When no data available on a tradebar, return the last data that was generated /// Custom leverage per security /// The new /// Generic type T must implement base data [DocumentationAttribute(AddingData)] public Security AddData(Symbol underlying, Resolution? resolution, DateTimeZone timeZone, bool fillForward = false, decimal leverage = 1.0m) where T : IBaseData, new() { return AddData(typeof(T), underlying, resolution, timeZone, fillForward, leverage); } /// /// AddData a new user defined data source including symbol properties and exchange hours, /// all other vars are not required and will use defaults. /// /// Key/Ticker for data /// The properties of this new custom data /// The Exchange hours of this symbol /// Resolution of the Data Required /// When no data available on a tradebar, return the last data that was generated /// Custom leverage per security /// The new [DocumentationAttribute(AddingData)] public Security AddData(string ticker, SymbolProperties properties, SecurityExchangeHours exchangeHours, Resolution? resolution = null, bool fillForward = false, decimal leverage = 1.0m) where T : IBaseData, new() { // Get the right key for storage of base type symbols var key = SecurityIdentifier.GenerateBaseSymbol(typeof(T), ticker); // Set our database entries for this data type SetDatabaseEntries(key, properties, exchangeHours); // Then add the data return AddData(typeof(T), ticker, resolution, null, fillForward, leverage); } /// /// Send a debug message to the web console: /// /// Message to send to debug console /// /// [DocumentationAttribute(Logging)] public void Debug(string message) { if (!_liveMode && (string.IsNullOrEmpty(message) || _previousDebugMessage == message)) return; _debugMessages.Enqueue(message); _previousDebugMessage = message; } /// /// Send a debug message to the web console: /// /// Message to send to debug console /// /// [DocumentationAttribute(Logging)] public void Debug(int message) { Debug(message.ToStringInvariant()); } /// /// Send a debug message to the web console: /// /// Message to send to debug console /// /// [DocumentationAttribute(Logging)] public void Debug(double message) { Debug(message.ToStringInvariant()); } /// /// Send a debug message to the web console: /// /// Message to send to debug console /// /// [DocumentationAttribute(Logging)] public void Debug(decimal message) { Debug(message.ToStringInvariant()); } /// /// Added another method for logging if user guessed. /// /// String message to log. /// /// [DocumentationAttribute(Logging)] public void Log(string message) { if (!_liveMode && string.IsNullOrEmpty(message)) return; _logMessages.Enqueue(message); } /// /// Added another method for logging if user guessed. /// /// Int message to log. /// /// [DocumentationAttribute(Logging)] public void Log(int message) { Log(message.ToStringInvariant()); } /// /// Added another method for logging if user guessed. /// /// Double message to log. /// /// [DocumentationAttribute(Logging)] public void Log(double message) { Log(message.ToStringInvariant()); } /// /// Added another method for logging if user guessed. /// /// Decimal message to log. /// /// [DocumentationAttribute(Logging)] public void Log(decimal message) { Log(message.ToStringInvariant()); } /// /// Send a string error message to the Console. /// /// Message to display in errors grid /// /// [DocumentationAttribute(Logging)] public void Error(string message) { if (!_liveMode && (string.IsNullOrEmpty(message) || _previousErrorMessage == message)) return; _errorMessages.Enqueue(message); _previousErrorMessage = message; } /// /// Send a int error message to the Console. /// /// Message to display in errors grid /// /// [DocumentationAttribute(Logging)] public void Error(int message) { Error(message.ToStringInvariant()); } /// /// Send a double error message to the Console. /// /// Message to display in errors grid /// /// [DocumentationAttribute(Logging)] public void Error(double message) { Error(message.ToStringInvariant()); } /// /// Send a decimal error message to the Console. /// /// Message to display in errors grid /// /// [DocumentationAttribute(Logging)] public void Error(decimal message) { Error(message.ToStringInvariant()); } /// /// Send a string error message to the Console. /// /// Exception object captured from a try catch loop /// /// [DocumentationAttribute(Logging)] public void Error(Exception error) { var message = error.Message; if (!_liveMode && (string.IsNullOrEmpty(message) || _previousErrorMessage == message)) return; _errorMessages.Enqueue(message); _previousErrorMessage = message; } /// /// Terminate the algorithm after processing the current event handler. /// /// Exit message to display on quitting [DocumentationAttribute(Logging)] public void Quit(string message = "") { Debug("Quit(): " + message); Status = AlgorithmStatus.Stopped; } /// /// Set the Quit flag property of the algorithm. /// /// Intended for internal use by the QuantConnect Lean Engine only. /// Boolean quit state /// [DocumentationAttribute(Logging)] public void SetQuit(bool quit) { if (quit) { Status = AlgorithmStatus.Stopped; } } /// /// Converts the string 'ticker' symbol into a full object /// This requires that the string 'ticker' has been added to the algorithm /// /// The ticker symbol. This should be the ticker symbol /// as it was added to the algorithm /// The symbol object mapped to the specified ticker [DocumentationAttribute(AddingData)] [DocumentationAttribute(HandlingData)] public Symbol Symbol(string ticker) { return SymbolCache.GetSymbol(ticker); } /// /// For the given symbol will resolve the ticker it used at the current algorithm date /// /// The symbol to get the ticker for /// The mapped ticker for a symbol [DocumentationAttribute(AddingData)] [DocumentationAttribute(HandlingData)] public string Ticker(Symbol symbol) { return SecurityIdentifier.Ticker(symbol, Time); } /// /// Creates and adds a new to the algorithm /// [DocumentationAttribute(AddingData)] private T AddSecurity(SecurityType securityType, string ticker, Resolution? resolution, string market, bool fillForward, decimal leverage, bool extendedMarketHours, DataMappingMode? mappingMode = null, DataNormalizationMode? normalizationMode = null) where T : Security { market = GetMarket(market, ticker, securityType); Symbol symbol; if (!SymbolCache.TryGetSymbol(ticker, out symbol) || symbol.ID.Market != market || symbol.SecurityType != securityType) { symbol = QuantConnect.Symbol.Create(ticker, securityType, market); } var configs = SubscriptionManager.SubscriptionDataConfigService.Add(symbol, resolution, fillForward, extendedMarketHours, dataNormalizationMode: normalizationMode ?? UniverseSettings.DataNormalizationMode, dataMappingMode: mappingMode ?? UniverseSettings.DataMappingMode); var security = Securities.CreateSecurity(symbol, configs, leverage); return (T)AddToUserDefinedUniverse(security, configs); } /// /// Set the historical data provider /// /// Historical data provider [DocumentationAttribute(HistoricalData)] public void SetHistoryProvider(IHistoryProvider historyProvider) { if (historyProvider == null) { throw new ArgumentNullException(nameof(historyProvider), "Algorithm.SetHistoryProvider(): Historical data provider cannot be null."); } HistoryProvider = historyProvider; } /// /// Set the runtime error /// /// Represents error that occur during execution [DocumentationAttribute(HandlingData)] [DocumentationAttribute(LiveTrading)] public void SetRunTimeError(Exception exception) { if (exception == null) { throw new ArgumentNullException(nameof(exception), "Algorithm.SetRunTimeError(): Algorithm.RunTimeError cannot be set to null."); } RunTimeError = exception; } /// /// Set the state of a live deployment /// /// Live deployment status [DocumentationAttribute(LiveTrading)] public void SetStatus(AlgorithmStatus status) { Status = status; } /// /// Downloads the requested resource as a . /// The resource to download is specified as a containing the URI. /// /// A string containing the URI to download /// The requested resource as a [DocumentationAttribute(AddingData)] [DocumentationAttribute(MachineLearning)] public string Download(string address) => Download(address, Enumerable.Empty>()); /// /// Downloads the requested resource as a . /// The resource to download is specified as a containing the URI. /// /// A string containing the URI to download /// Defines header values to add to the request /// The requested resource as a [DocumentationAttribute(AddingData)] [DocumentationAttribute(MachineLearning)] public string Download(string address, IEnumerable> headers) => Download(address, headers, null, null); /// /// Downloads the requested resource as a . /// The resource to download is specified as a containing the URI. /// /// A string containing the URI to download /// Defines header values to add to the request /// The user name associated with the credentials /// The password for the user name associated with the credentials /// The requested resource as a [DocumentationAttribute(AddingData)] [DocumentationAttribute(MachineLearning)] public string Download(string address, IEnumerable> headers, string userName, string password) { return _api.Download(address, headers, userName, password); } /// /// Schedules the provided training code to execute immediately /// /// The training code to be invoked [DocumentationAttribute(MachineLearning)] [DocumentationAttribute(ScheduledEvents)] public ScheduledEvent Train(Action trainingCode) { return Schedule.TrainingNow(trainingCode); } /// /// Schedules the training code to run using the specified date and time rules /// /// Specifies what dates the event should run /// Specifies the times on those dates the event should run /// The training code to be invoked [DocumentationAttribute(MachineLearning)] [DocumentationAttribute(ScheduledEvents)] public ScheduledEvent Train(IDateRule dateRule, ITimeRule timeRule, Action trainingCode) { return Schedule.Training(dateRule, timeRule, trainingCode); } /// /// Event invocator for the event /// /// The collection of insights generaed at the current time step [DocumentationAttribute(AlgorithmFramework)] private void OnInsightsGenerated(Insight[] insights) { // debug printing of generated insights if (DebugMode) { Log($"{Time}: ALPHA: {string.Join(" | ", insights.Select(i => i.ToString()).OrderBy(i => i))}"); } Insights.AddRange(insights); InsightsGenerated?.Invoke(this, new GeneratedInsightsCollection(UtcTime, insights)); } /// /// Sets the current slice /// /// The Slice object [DocumentationAttribute(HandlingData)] public void SetCurrentSlice(Slice slice) { CurrentSlice = slice; } /// /// Provide the API for the algorithm. /// /// Initiated API [DocumentationAttribute(HandlingData)] public void SetApi(IApi api) { _api = api; } /// /// Sets the object store /// /// The object store [DocumentationAttribute(HandlingData)] [DocumentationAttribute(MachineLearning)] public void SetObjectStore(IObjectStore objectStore) { ObjectStore = new ObjectStore(objectStore); } /// /// Determines if the Symbol is shortable at the brokerage /// /// Symbol to check if shortable /// True if shortable [DocumentationAttribute(TradingAndOrders)] public bool Shortable(Symbol symbol) { return Shortable(symbol, 0); } /// /// Determines if the Symbol is shortable at the brokerage /// /// Symbol to check if shortable /// Order's quantity to check if it is currently shortable, taking into account current holdings and open orders /// Optionally the id of the order being updated. When updating an order /// we want to ignore it's submitted short quantity and use the new provided quantity to determine if we /// can perform the update /// True if the symbol can be shorted by the requested quantity [DocumentationAttribute(TradingAndOrders)] public bool Shortable(Symbol symbol, decimal shortQuantity, int? updateOrderId = null) { var security = Securities[symbol]; var shortableQuantity = security.ShortableProvider.ShortableQuantity(symbol, security.LocalTime); if (shortableQuantity == null) { return true; } var openOrderQuantity = Transactions.GetOpenOrdersRemainingQuantity( // if 'updateOrderId' was given, ignore that orders quantity order => order.Symbol == symbol && (!updateOrderId.HasValue || order.OrderId != updateOrderId.Value)); var portfolioQuantity = security.Holdings.Quantity; // We check portfolio and open orders beforehand to ensure that orderQuantity == 0 case does not return // a true result whenever we have no more shares left to short. if (portfolioQuantity + openOrderQuantity <= -shortableQuantity) { return false; } shortQuantity = -Math.Abs(shortQuantity); return portfolioQuantity + shortQuantity + openOrderQuantity >= -shortableQuantity; } /// /// Gets the quantity shortable for the given asset /// /// /// Quantity shortable for the given asset. Zero if not /// shortable, or a number greater than zero if shortable. /// [DocumentationAttribute(TradingAndOrders)] public long ShortableQuantity(Symbol symbol) { var security = Securities[symbol]; return security.ShortableProvider.ShortableQuantity(symbol, security.LocalTime) ?? 0; } /// /// Converts an ISIN identifier into a /// /// The International Securities Identification Number (ISIN) of an asset /// /// The date that the stock being looked up is/was traded at. /// The date is used to create a Symbol with the ticker set to the ticker the asset traded under on the trading date. /// /// Symbol corresponding to the ISIN. If no Symbol with a matching ISIN was found, returns null. [DocumentationAttribute(HandlingData)] [DocumentationAttribute(SecuritiesAndPortfolio)] public Symbol ISIN(string isin, DateTime? tradingDate = null) { return SecurityDefinitionSymbolResolver.ISIN(isin, GetVerifiedTradingDate(tradingDate)); } /// /// Converts a into an ISIN identifier /// /// The /// ISIN corresponding to the Symbol. If no matching ISIN is found, returns null. [DocumentationAttribute(HandlingData)] [DocumentationAttribute(SecuritiesAndPortfolio)] public string ISIN(Symbol symbol) { return SecurityDefinitionSymbolResolver.ISIN(symbol); } /// /// Converts a composite FIGI identifier into a /// /// The composite Financial Instrument Global Identifier (FIGI) of an asset /// /// The date that the stock being looked up is/was traded at. /// The date is used to create a Symbol with the ticker set to the ticker the asset traded under on the trading date. /// /// Symbol corresponding to the composite FIGI. If no Symbol with a matching composite FIGI was found, returns null. /// /// The composite FIGI differs from an exchange-level FIGI, in that it identifies /// an asset across all exchanges in a single country that the asset trades in. /// [DocumentationAttribute(HandlingData)] [DocumentationAttribute(SecuritiesAndPortfolio)] public Symbol CompositeFIGI(string compositeFigi, DateTime? tradingDate = null) { return SecurityDefinitionSymbolResolver.CompositeFIGI(compositeFigi, GetVerifiedTradingDate(tradingDate)); } /// /// Converts a into a composite FIGI identifier /// /// The /// Composite FIGI corresponding to the Symbol. If no matching composite FIGI is found, returns null. [DocumentationAttribute(HandlingData)] [DocumentationAttribute(SecuritiesAndPortfolio)] public string CompositeFIGI(Symbol symbol) { return SecurityDefinitionSymbolResolver.CompositeFIGI(symbol); } /// /// Converts a CUSIP identifier into a /// /// The CUSIP number of an asset /// /// The date that the stock being looked up is/was traded at. /// The date is used to create a Symbol with the ticker set to the ticker the asset traded under on the trading date. /// /// Symbol corresponding to the CUSIP. If no Symbol with a matching CUSIP was found, returns null. [DocumentationAttribute(HandlingData)] [DocumentationAttribute(SecuritiesAndPortfolio)] public Symbol CUSIP(string cusip, DateTime? tradingDate = null) { return SecurityDefinitionSymbolResolver.CUSIP(cusip, GetVerifiedTradingDate(tradingDate)); } /// /// Converts a into a CUSIP identifier /// /// The /// CUSIP corresponding to the Symbol. If no matching CUSIP is found, returns null. [DocumentationAttribute(HandlingData)] [DocumentationAttribute(SecuritiesAndPortfolio)] public string CUSIP(Symbol symbol) { return SecurityDefinitionSymbolResolver.CUSIP(symbol); } /// /// Converts a SEDOL identifier into a /// /// The SEDOL identifier of an asset /// /// The date that the stock being looked up is/was traded at. /// The date is used to create a Symbol with the ticker set to the ticker the asset traded under on the trading date. /// /// Symbol corresponding to the SEDOL. If no Symbol with a matching SEDOL was found, returns null. [DocumentationAttribute(HandlingData)] [DocumentationAttribute(SecuritiesAndPortfolio)] public Symbol SEDOL(string sedol, DateTime? tradingDate = null) { return SecurityDefinitionSymbolResolver.SEDOL(sedol, GetVerifiedTradingDate(tradingDate)); } /// /// Converts a into a SEDOL identifier /// /// The /// SEDOL corresponding to the Symbol. If no matching SEDOL is found, returns null. [DocumentationAttribute(HandlingData)] [DocumentationAttribute(SecuritiesAndPortfolio)] public string SEDOL(Symbol symbol) { return SecurityDefinitionSymbolResolver.SEDOL(symbol); } /// /// Converts a CIK identifier into array /// /// The CIK identifier of an asset /// /// The date that the stock being looked up is/was traded at. /// The date is used to create a Symbol with the ticker set to the ticker the asset traded under on the trading date. /// /// Symbols corresponding to the CIK. If no Symbol with a matching CIK was found, returns empty array. [DocumentationAttribute(HandlingData)] [DocumentationAttribute(SecuritiesAndPortfolio)] public Symbol[] CIK(int cik, DateTime? tradingDate = null) { return SecurityDefinitionSymbolResolver.CIK(cik, GetVerifiedTradingDate(tradingDate)); } /// /// Converts a into a CIK identifier /// /// The /// CIK corresponding to the Symbol. If no matching CIK is found, returns null. [DocumentationAttribute(HandlingData)] [DocumentationAttribute(SecuritiesAndPortfolio)] public int? CIK(Symbol symbol) { return SecurityDefinitionSymbolResolver.CIK(symbol); } /// /// Get the fundamental data for the requested symbol at the current time /// /// The /// The fundamental data for the Symbol [DocumentationAttribute(HandlingData)] [DocumentationAttribute(SecuritiesAndPortfolio)] public Fundamental Fundamentals(Symbol symbol) { return new Fundamental(Time, symbol) { EndTime = Time }; } /// /// Get the fundamental data for the requested symbols at the current time /// /// The /// The fundamental data for the symbols [DocumentationAttribute(HandlingData)] [DocumentationAttribute(SecuritiesAndPortfolio)] public List Fundamentals(List symbols) { return symbols.Select(symbol => Fundamentals(symbol)).ToList(); } /// /// Get the option chain for the specified symbol at the current time () /// /// /// The symbol for which the option chain is asked for. /// It can be either the canonical option or the underlying symbol. /// /// /// Whether to flatten the resulting data frame. Used from Python when accessing . /// See /// /// The option chain /// /// As of 2024/09/11, future options chain will not contain any additional data (e.g. daily price data, implied volatility and greeks), /// it will be populated with the contract symbol only. This is expected to change in the future. /// As of 2024/12/18, future options data will contain daily price data but not implied volatility and greeks. /// [DocumentationAttribute(AddingData)] public OptionChain OptionChain(Symbol symbol, bool flatten = false) { return OptionChains(new[] { symbol }, flatten).Values.SingleOrDefault() ?? new OptionChain(GetCanonicalOptionSymbol(symbol), Time.Date, flatten); } /// /// Get the option chains for the specified symbols at the current time () /// /// /// The symbols for which the option chain is asked for. /// It can be either the canonical options or the underlying symbols. /// /// /// Whether to flatten the resulting data frame. Used from Python when accessing . /// See /// /// The option chains [DocumentationAttribute(AddingData)] public OptionChains OptionChains(IEnumerable symbols, bool flatten = false) { var canonicalSymbols = symbols.Select(GetCanonicalOptionSymbol).ToList(); var optionChainsData = GetChainsData(canonicalSymbols); var chains = new OptionChains(Time.Date, flatten); foreach (var (symbol, contracts) in optionChainsData) { var symbolProperties = SymbolPropertiesDatabase.GetSymbolProperties(symbol.ID.Market, symbol, symbol.SecurityType, AccountCurrency); var optionChain = new OptionChain(symbol, GetTimeInExchangeTimeZone(symbol).Date, contracts, symbolProperties, flatten); chains.Add(symbol, optionChain); } return chains; } /// /// Get the futures chain for the specified symbol at the current time () /// /// /// The symbol for which the futures chain is asked for. /// It can be either the canonical future, a contract or an option symbol. /// /// /// Whether to flatten the resulting data frame. Used from Python when accessing . /// See /// /// The futures chain [DocumentationAttribute(AddingData)] public FuturesChain FutureChain(Symbol symbol, bool flatten = false) { return FuturesChain(symbol, flatten); } /// /// Get the futures chain for the specified symbol at the current time () /// /// /// The symbol for which the futures chain is asked for. /// It can be either the canonical future, a contract or an option symbol. /// /// /// Whether to flatten the resulting data frame. Used from Python when accessing . /// See /// /// The futures chain [DocumentationAttribute(AddingData)] public FuturesChain FuturesChain(Symbol symbol, bool flatten = false) { return FuturesChains(new[] { symbol }, flatten).Values.SingleOrDefault() ?? new FuturesChain(GetCanonicalFutureSymbol(symbol), Time.Date); } /// /// Get the futures chains for the specified symbols at the current time () /// /// /// The symbols for which the futures chains are asked for. /// It can be either the canonical future, a contract or an option symbol. /// /// /// Whether to flatten the resulting data frame. Used from Python when accessing . /// See /// /// The futures chains [DocumentationAttribute(AddingData)] public FuturesChains FutureChains(IEnumerable symbols, bool flatten = false) { return FuturesChains(symbols, flatten); } /// /// Get the futures chains for the specified symbols at the current time () /// /// /// The symbols for which the futures chains are asked for. /// It can be either the canonical future, a contract or an option symbol. /// /// /// Whether to flatten the resulting data frame. Used from Python when accessing . /// See /// /// The futures chains [DocumentationAttribute(AddingData)] public FuturesChains FuturesChains(IEnumerable symbols, bool flatten = false) { var canonicalSymbols = symbols.Select(GetCanonicalFutureSymbol).ToList(); var futureChainsData = GetChainsData(canonicalSymbols); var chains = new FuturesChains(Time.Date, flatten); if (futureChainsData != null) { foreach (var (symbol, contracts) in futureChainsData) { var chain = new FuturesChain(symbol, GetTimeInExchangeTimeZone(symbol).Date, contracts, flatten); chains.Add(symbol, chain); } } return chains; } /// /// Get an authenticated link to execute the given command instance /// /// The target command /// The authenticated link public string Link(object command) { var typeName = command.GetType().Name; if (command is Command || typeName.Contains("AnonymousType", StringComparison.InvariantCultureIgnoreCase)) { return CommandLink(typeName, command); } // this shouldn't happen but just in case throw new ArgumentException($"Unexpected command type: {typeName}"); } /// /// Register a command type to be used /// /// The command type public void AddCommand() where T : Command { _registeredCommands[typeof(T).Name] = (CallbackCommand command) => { var commandInstance = JsonConvert.DeserializeObject(command.Payload); return commandInstance.Run(this); }; } /// /// Broadcast a live command /// /// The target command /// public RestResponse BroadcastCommand(object command) { var typeName = command.GetType().Name; if (command is Command || typeName.Contains("AnonymousType", StringComparison.InvariantCultureIgnoreCase)) { var serialized = JsonConvert.SerializeObject(command); var payload = JsonConvert.DeserializeObject>(serialized); return SendBroadcast(typeName, payload); } // this shouldn't happen but just in case throw new ArgumentException($"Unexpected command type: {typeName}"); } /// /// Run a callback command instance /// /// The callback command instance /// The command result public CommandResultPacket RunCommand(CallbackCommand command) { bool? result = null; if (_registeredCommands.TryGetValue(command.Type, out var target)) { try { result = target.Invoke(command); } catch (Exception ex) { QuantConnect.Logging.Log.Error(ex); if (_oneTimeCommandErrors.Add(command.Type)) { Log($"Unexpected error running command '{command.Type}' error: '{ex.Message}'"); } } } else { if (_oneTimeCommandErrors.Add(command.Type)) { Log($"Detected unregistered command type '{command.Type}', will be ignored"); } } return new CommandResultPacket(command, result) { CommandName = command.Type }; } /// /// Generic untyped command call handler /// /// The associated data /// True if success, false otherwise. Returning null will disable command feedback public virtual bool? OnCommand(dynamic data) { return true; } /// /// Helper method to get a market for a given security type and ticker /// private string GetMarket(string market, string ticker, SecurityType securityType, string defaultMarket = null) { if (string.IsNullOrEmpty(market)) { if (securityType == SecurityType.Index && IndexSymbol.TryGetIndexMarket(ticker, out market)) { return market; } if (securityType == SecurityType.Future && SymbolPropertiesDatabase.TryGetMarket(ticker, securityType, out market)) { return market; } if (!BrokerageModel.DefaultMarkets.TryGetValue(securityType, out market)) { if (string.IsNullOrEmpty(defaultMarket)) { throw new KeyNotFoundException($"No default market set for security type: {securityType}"); } return defaultMarket; } } return market; } private string CommandLink(string typeName, object command) { var payload = new Dictionary { { "projectId", ProjectId }, { "command", command } }; if (_registeredCommands.ContainsKey(typeName)) { payload["command[$type]"] = typeName; } return Authentication.Link("live/commands/create", payload); } private RestResponse SendBroadcast(string typeName, Dictionary payload) { if (AlgorithmMode == AlgorithmMode.Backtesting) { if (!_sentBroadcastCommandsDisabled) { _sentBroadcastCommandsDisabled = true; Debug("Warning: sending broadcast commands is disabled in backtesting"); } return null; } if (_registeredCommands.ContainsKey(typeName)) { payload["$type"] = typeName; } return _api.BroadcastLiveCommand(Globals.OrganizationID, AlgorithmMode == AlgorithmMode.Live ? ProjectId : null, payload); } private static Symbol GetCanonicalOptionSymbol(Symbol symbol) { // We got the underlying if (symbol.SecurityType.HasOptions()) { return QuantConnect.Symbol.CreateCanonicalOption(symbol); } if (symbol.SecurityType.IsOption()) { return symbol.Canonical; } throw new ArgumentException($"The symbol {symbol} is not an option or an underlying symbol."); } private static Symbol GetCanonicalFutureSymbol(Symbol symbol) { // We got either a contract or the canonical itself if (symbol.SecurityType == SecurityType.Future) { return symbol.Canonical; } if (symbol.SecurityType == SecurityType.FutureOption) { return symbol.Underlying.Canonical; } throw new ArgumentException($"The symbol {symbol} is neither a future nor a future option symbol."); } /// /// Set the properties and exchange hours for a given key into our databases /// /// Key for database storage /// Properties to store /// Exchange hours to store private void SetDatabaseEntries(string key, SymbolProperties properties, SecurityExchangeHours exchangeHours) { // Add entries to our Symbol Properties DB and MarketHours DB SymbolPropertiesDatabase.SetEntry(Market.USA, key, SecurityType.Base, properties); MarketHoursDatabase.SetEntry(Market.USA, key, SecurityType.Base, exchangeHours); } /// /// Takes a date, and verifies that it is point-in-time. If null /// time is provided, algorithm time is returned instead. /// /// /// The trading date to verify that it is a point-in-time /// date, or before, relative to the algorithm's current trading date. /// /// The date provided if not null, otherwise the algorithm's current trading date /// /// The trading date provided is not null and it is after the algorithm's current trading date /// private DateTime GetVerifiedTradingDate(DateTime? tradingDate) { tradingDate ??= Time.Date; if (tradingDate > Time.Date) { throw new ArgumentException($"The trading date provided: \"{tradingDate:yyyy-MM-dd}\" is after the current algorithm's trading date: \"{Time:yyyy-MM-dd}\""); } return tradingDate.Value; } /// /// Helper method to set the start date during live trading /// private void SetLiveModeStartDate() { if (!LiveMode) { throw new InvalidOperationException("SetLiveModeStartDate should only be called during live trading!"); } _start = DateTime.UtcNow.ConvertFromUtc(TimeZone); // startDate is set relative to the algorithm's timezone. _startDate = _start.Date; _endDate = QuantConnect.Time.EndOfTime; } /// /// Sets the statistics service instance to be used by the algorithm /// /// The statistics service instance public void SetStatisticsService(IStatisticsService statisticsService) { if (_statisticsService == null) { _statisticsService = statisticsService; } } /// /// Makes a history request to get the option/future chain data for the specified symbols /// at the current algorithm time () /// private IEnumerable>> GetChainsData(IEnumerable canonicalSymbols) where T : BaseChainUniverseData { foreach (var symbol in canonicalSymbols) { // We will add a safety measure in case the universe file for the current time is not available: // we will use the latest available universe file within the last 3 trading dates. // This is useful in cases like live trading when the algorithm is deployed at a time of day when // the universe file is not available yet. var history = (DataDictionary)null; var periods = 1; while ((history == null || history.Count == 0) && periods <= 3) { history = History([symbol], periods++).FirstOrDefault(); } var chain = history != null && history.Count > 0 ? history.Values.Single().Cast() : Enumerable.Empty(); yield return KeyValuePair.Create(symbol, chain); } } /// /// Gets the current time in the exchange time zone for the given symbol /// private DateTime GetTimeInExchangeTimeZone(Symbol symbol) { var exchange = MarketHoursDatabase.GetExchangeHours(symbol.ID.Market, symbol, symbol.SecurityType); return UtcTime.ConvertFromUtc(exchange.TimeZone); } } }