17 using System.Collections.Generic;
19 using System.Linq.Expressions;
20 using System.Globalization;
22 using NodaTime.TimeZones;
41 using System.Collections.Concurrent;
65 #region Documentation Attribute Categories
66 const string AddingData =
"Adding Data";
67 const string AlgorithmFramework =
"Algorithm Framework";
68 const string Charting =
"Charting";
69 const string ConsolidatingData =
"Consolidating Data";
70 const string HandlingData =
"Handling Data";
71 const string HistoricalData =
"Historical Data";
72 const string Indicators =
"Indicators";
73 const string LiveTrading =
"Live Trading";
74 const string Logging =
"Logging";
75 const string MachineLearning =
"Machine Learning";
76 const string Modeling =
"Modeling";
77 const string ParameterAndOptimization =
"Parameter and Optimization";
78 const string ScheduledEvents =
"Scheduled Events";
79 const string SecuritiesAndPortfolio =
"Securities and Portfolio";
80 const string TradingAndOrders =
"Trading and Orders";
81 const string Universes =
"Universes";
82 const string StatisticsTag =
"Statistics";
99 private HashSet<string> _tags;
100 private bool _tagsLimitReachedLogSent;
101 private bool _tagsCollectionTruncatedLogSent;
102 private DateTime _start;
103 private DateTime _startDate;
104 private DateTime _endDate;
105 private bool _locked;
106 private bool _liveMode;
109 private string _algorithmId =
"";
110 private ConcurrentQueue<string> _debugMessages =
new ConcurrentQueue<string>();
111 private ConcurrentQueue<string> _logMessages =
new ConcurrentQueue<string>();
112 private ConcurrentQueue<string> _errorMessages =
new ConcurrentQueue<string>();
117 private string _previousDebugMessage =
"";
118 private string _previousErrorMessage =
"";
131 private bool _checkedForOnDataSlice;
132 private Action<Slice> _onDataSlice;
135 private bool _userSetSecurityInitializer;
138 private TimeSpan? _warmupTimeSpan;
139 private int? _warmupBarCount;
140 private Dictionary<string, string> _parameters =
new Dictionary<string, string>();
153 Name = GetType().Name;
165 _startDate =
new DateTime(1998, 01, 01);
319 return _brokerageModel;
323 _brokerageModel = value;
326 BrokerageName = Brokerages.BrokerageModel.GetBrokerageName(_brokerageModel);
328 catch (ArgumentOutOfRangeException)
499 throw new InvalidOperationException(
"Cannot set algorithm name after it is initialized.");
502 if (!
string.IsNullOrEmpty(value))
513 public HashSet<string>
Tags
526 var tags = value.Where(x => !
string.IsNullOrEmpty(x?.Trim())).ToList();
528 if (tags.Count >
MaxTagsCount && !_tagsCollectionTruncatedLogSent)
530 Log($
"Warning: The tags collection cannot contain more than {MaxTagsCount} items. It will be truncated.");
531 _tagsCollectionTruncatedLogSent =
true;
561 get {
return _localTimeKeeper.
LocalTime; }
570 get {
return _timeKeeper.UtcTime; }
580 get {
return _localTimeKeeper.
TimeZone; }
638 return _algorithmMode;
649 return _deploymentTarget;
662 return _debugMessages;
666 _debugMessages = value;
683 _logMessages = value;
703 return _errorMessages;
707 _errorMessages = value;
747 throw new NotImplementedException(
"Please override the Initialize() method");
758 if (_endDate < _startDate)
760 throw new ArgumentException(
"Please select an algorithm end date greater than start date.");
764 if (portfolioConstructionModel !=
null)
774 portfolioConstructionModel.RebalanceOnInsightChanges
783 Debug(
"Warning: rebalance portfolio settings are set but not supported by the current IPortfolioConstructionModel type: " +
784 $
"{PortfolioConstruction.GetType()}");
802 securityBenchmark.Security.Symbol, securityBenchmark.Security.Type);
805 Log($
"QCAlgorithm.PostInitialize(): Warning: Using a security benchmark of a different timezone ({benchmarkTimeZone})" +
806 $
" than the algorithm TimeZone ({TimeZone}) may lead to skewed and incorrect statistics. Use a higher resolution than daily to minimize.");
810 if (TryGetWarmupHistoryStartTime(out var result))
841 return _parameters.TryGetValue(name, out var value) ? value : defaultValue;
854 return _parameters.TryGetValue(name, out var strValue) &&
int.TryParse(strValue, out var value) ? value : defaultValue;
867 return _parameters.TryGetValue(name, out var strValue) &&
868 double.TryParse(strValue, NumberStyles.Any, CultureInfo.InvariantCulture, out var value) ? value : defaultValue;
881 return _parameters.TryGetValue(name, out var strValue) &&
882 decimal.TryParse(strValue, NumberStyles.Any, CultureInfo.InvariantCulture, out var value) ? value : defaultValue;
891 return _parameters.ToReadOnlyDictionary();
902 _parameters = parameters.ToDictionary();
907 catch (Exception err)
909 Error(
"Error applying parameter values: " + err.Message);
920 if (availableDataTypes ==
null)
925 foreach (var dataFeed
in availableDataTypes)
942 throw new Exception(
"SetSecurityInitializer() cannot be called after algorithm initialization. " +
943 "When you use the SetSecurityInitializer() method it will apply to all universes and manually added securities.");
946 if (_userSetSecurityInitializer)
948 Debug(
"Warning: SetSecurityInitializer() has already been called, existing security initializers in all universes will be overwritten.");
952 _userSetSecurityInitializer =
true;
961 [Obsolete(
"This method is deprecated. Please use this overload: SetSecurityInitializer(Action<Security> securityInitializer)")]
1019 if (!_checkedForOnDataSlice)
1021 _checkedForOnDataSlice =
true;
1023 var method = GetType().GetMethods()
1024 .Where(x => x.Name ==
"OnData")
1025 .Where(x => x.DeclaringType != typeof(
QCAlgorithm))
1026 .Where(x => x.GetParameters().Length == 1)
1027 .FirstOrDefault(x => x.GetParameters()[0].ParameterType == typeof(
Slice));
1034 var
self = Expression.Constant(
this);
1035 var parameter = Expression.Parameter(typeof(
Slice),
"data");
1036 var call = Expression.Call(
self, method, parameter);
1037 var lambda = Expression.Lambda<Action<Slice>>(call, parameter);
1038 _onDataSlice = lambda.Compile();
1041 if (_onDataSlice !=
null)
1043 _onDataSlice(slice);
1118 [Obsolete(
"This method is deprecated and will be removed after August 2021. Please use this overload: OnEndOfDay(Symbol symbol)")]
1216 _timeKeeper.SetUtcDateTime(frontier);
1233 tz = DateTimeZoneProviders.Tzdb[timeZone];
1235 catch (DateTimeZoneNotFoundException)
1237 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");
1252 throw new InvalidOperationException(
"Algorithm.SetTimeZone(): Cannot change time zone after algorithm running.");
1255 if (timeZone ==
null)
throw new ArgumentNullException(nameof(timeZone));
1256 _timeKeeper.AddTimeZone(timeZone);
1257 _localTimeKeeper = _timeKeeper.GetLocalTimeKeeper(timeZone);
1268 _start = _startDate;
1275 SetLiveModeStartDate();
1300 if (!_userSetSecurityInitializer)
1308 var security = kvp.Value;
1313 var leverage = security.Leverage;
1318 security.SetLeverage(leverage);
1355 [Obsolete(
"Symbol implicit operator to string is provided for algorithm use only.")]
1363 throw new InvalidOperationException(
"Algorithm.SetBenchmark(): Cannot change Benchmark after algorithm initialized.");
1367 if (!
BrokerageModel.DefaultMarkets.TryGetValue(securityType, out market))
1395 symbol =
Securities.FirstOrDefault(x => x.Key.Value == ticker).Key;
1400 Debug($
"Warning: SetBenchmark({ticker}): no existing symbol found, benchmark security will be added with {SecurityType.Equity} type.");
1420 throw new InvalidOperationException(
"Algorithm.SetBenchmark(): Cannot change Benchmark after algorithm initialized.");
1439 throw new InvalidOperationException(
"Algorithm.SetBenchmark(): Cannot change Benchmark after algorithm initialized.");
1474 if (!
string.IsNullOrEmpty(tag?.Trim()))
1478 if (!_tagsLimitReachedLogSent)
1480 Log($
"Warning: AddTag({tag}): Unable to add tag. Tags are limited to a maximum of {MaxTagsCount}.");
1481 _tagsLimitReachedLogSent =
true;
1516 throw new InvalidOperationException(
"Algorithm.SetAccountCurrency(): " +
1517 "Cannot change AccountCurrency after algorithm initialized.");
1520 if (startingCash ==
null)
1522 Debug($
"Changing account currency from {AccountCurrency} to {accountCurrency}...");
1526 Debug($
"Changing account currency from {AccountCurrency} to {accountCurrency}, with a starting cash of {startingCash}...");
1541 SetCash((decimal)startingCash);
1553 SetCash((decimal)startingCash);
1570 throw new InvalidOperationException(
"Algorithm.SetCash(): Cannot change cash available after algorithm initialized.");
1581 public void SetCash(
string symbol, decimal startingCash, decimal conversionRate = 0)
1589 throw new InvalidOperationException(
"Algorithm.SetCash(): Cannot change cash available after algorithm initialized.");
1608 var start =
new DateTime(year, month, day);
1615 catch (Exception err)
1617 throw new ArgumentException($
"Date Invalid: {err.Message}");
1634 var end =
new DateTime(year, month, day);
1637 end = end.Date.AddDays(1).Subtract(TimeSpan.FromTicks(1));
1641 catch (Exception err)
1643 throw new ArgumentException($
"Date Invalid: {err.Message}");
1655 _algorithmId = algorithmId;
1668 if (_liveMode)
return;
1671 start = start.RoundDown(TimeSpan.FromDays(1));
1675 if (start < (
new DateTime(1900, 01, 01)))
1677 throw new ArgumentOutOfRangeException(nameof(start),
"Please select a start date after January 1st, 1900.");
1681 var todayInAlgorithmTimeZone = DateTime.UtcNow.ConvertFromUtc(
TimeZone).Date;
1682 if (start > todayInAlgorithmTimeZone)
1684 throw new ArgumentOutOfRangeException(nameof(start),
"Please select start date less than today");
1690 _start = _startDate = start;
1695 throw new InvalidOperationException(
"Algorithm.SetStartDate(): Cannot change start date after algorithm initialized.");
1709 if (_liveMode)
return;
1714 throw new InvalidOperationException(
"Algorithm.SetEndDate(): Cannot change end date after algorithm initialized.");
1719 var yesterdayInAlgorithmTimeZone = DateTime.UtcNow.ConvertFromUtc(
TimeZone).Date.AddDays(-1);
1720 if (end > yesterdayInAlgorithmTimeZone)
1722 end = yesterdayInAlgorithmTimeZone;
1726 _endDate = end.RoundDown(TimeSpan.FromDays(1)).AddDays(1).AddTicks(-1);
1767 SetLiveModeStartDate();
1781 _algorithmMode = algorithmMode;
1794 _deploymentTarget = deploymentTarget;
1823 return AddSecurity(securityType, ticker, resolution, fillForward,
Security.
NullLeverage, extendedMarketHours, dataMappingMode, dataNormalizationMode);
1842 return AddSecurity(securityType, ticker, resolution,
null, fillForward, leverage, extendedMarketHours, dataMappingMode, dataNormalizationMode);
1864 return AddOption(ticker, resolution, market, fillForward, leverage);
1869 return AddFuture(ticker, resolution, market, fillForward, leverage, extendedMarketHours, dataMappingMode, dataNormalizationMode);
1876 if (!
BrokerageModel.DefaultMarkets.TryGetValue(securityType, out market))
1878 throw new KeyNotFoundException($
"No default market set for security type: {securityType}");
1884 symbol.ID.Market != market ||
1885 symbol.SecurityType != securityType)
1890 return AddSecurity(symbol, resolution, fillForward, leverage, extendedMarketHours, dataMappingMode, dataNormalizationMode);
1892 catch (Exception err)
1894 Error(
"Algorithm.AddSecurity(): " + err);
1917 var contractOffset = (uint)Math.Abs(contractDepthOffset);
1920 throw new ArgumentOutOfRangeException(nameof(contractDepthOffset), $
"'contractDepthOffset' current maximum value is {Futures.MaximumContractDepthOffset}." +
1921 $
" Front month (0) and only {Futures.MaximumContractDepthOffset} back month contracts are currently supported.");
1929 return AddOptionContract(symbol, resolution, fillForward, leverage, extendedMarketHours);
1932 var isFilteredSubscription = !isCanonical;
1933 List<SubscriptionDataConfig> configs;
1936 if (dataNormalizationMode.HasValue)
1941 extendedMarketHours,
1942 isFilteredSubscription,
1943 dataNormalizationMode: dataNormalizationMode.
Value,
1944 contractDepthOffset: (uint)contractDepthOffset);
1951 extendedMarketHours,
1952 isFilteredSubscription,
1953 contractDepthOffset: (uint)contractDepthOffset);
1967 var canonicalConfig = configs.First();
1980 GetResolution(symbol, resolution,
null), isCanonical:
false);
1983 ExtendedMarketHours = extendedMarketHours,
1986 ContractDepthOffset = (int)contractOffset,
1987 SubscriptionDataTypes = dataTypes,
1995 continuousContractSymbol.ID.Symbol,
1996 continuousContractSymbol.ID.SecurityType,
1997 security.Exchange.Hours);
2008 return AddToUserDefinedUniverse(security, configs);
2026 return AddSecurity<Equity>(
SecurityType.Equity, ticker, resolution, market, fillForward, leverage, extendedMarketHours, normalizationMode: dataNormalizationMode);
2045 throw new KeyNotFoundException($
"No default market set for security type: {SecurityType.Option}");
2050 return AddOption(underlyingSymbol, resolution, market, fillForward, leverage);
2068 return AddOption(underlying,
null, resolution, market, fillForward, leverage);
2092 if (!
BrokerageModel.DefaultMarkets.TryGetValue(optionType, out market))
2094 throw new KeyNotFoundException($
"No default market set for security type: {optionType}");
2101 if (!
string.IsNullOrEmpty(targetOption))
2103 alias = $
"?{targetOption}";
2107 alias = $
"?{underlying.Value}";
2110 canonicalSymbol.ID.Market != market ||
2111 !canonicalSymbol.SecurityType.IsOption())
2135 bool fillForward =
true, decimal leverage =
Security.
NullLeverage,
bool extendedMarketHours =
false,
2143 throw new KeyNotFoundException($
"No default market set for security type: {SecurityType.Future}");
2148 var alias =
"/" + ticker;
2150 canonicalSymbol.ID.Market != market ||
2156 return (
Future)
AddSecurity(canonicalSymbol, resolution, fillForward, leverage, extendedMarketHours, dataMappingMode: dataMappingMode,
2157 dataNormalizationMode: dataNormalizationMode, contractDepthOffset: contractDepthOffset);
2173 return (
Future)
AddSecurity(symbol, resolution, fillForward, leverage, extendedMarketHours);
2188 throw new ArgumentException(
"Symbol provided must be canonical (i.e. the Symbol returned from AddFuture(), not AddFutureContract().");
2210 throw new ArgumentException(
"Expected non-canonical Symbol (i.e. a Symbol representing a specific Future contract");
2213 return AddOptionContract(symbol, resolution, fillForward, leverage, extendedMarketHours);
2259 throw new ArgumentException(
"Symbol provided must be of type SecurityType.Index");
2262 return AddOption(symbol, targetOption, resolution, symbol.
ID.
Market, fillForward);
2278 throw new ArgumentException(
"Symbol provided must be of type SecurityType.IndexOption");
2299 throw new ArgumentException($
"Unexpected option symbol {symbol}. " +
2300 $
"Please provide a valid option contract with it's underlying symbol set.");
2306 List<SubscriptionDataConfig> underlyingConfigs;
2311 underlyingSecurity =
AddSecurity(underlying, resolution, fillForward, leverage, extendedMarketHours);
2315 else if (underlyingSecurity !=
null && underlyingSecurity.IsDelisted)
2317 throw new ArgumentException($
"The underlying {underlying.SecurityType} asset ({underlying.Value}) is delisted " +
2318 $
"(current time is {Time})");
2325 var dataNormalizationMode = underlyingConfigs.DataNormalizationMode();
2330 throw new ArgumentException($
"The underlying {underlying.SecurityType} asset ({underlying.Value}) is set to " +
2331 $
"{dataNormalizationMode}, please change this to DataNormalizationMode.Raw with the " +
2332 "SetDataNormalization() method"
2343 underlyingSecurity.RefreshDataNormalizationModeProperty();
2355 Resolution = underlyingConfigs.GetHighestResolution(),
2356 ExtendedMarketHours = extendedMarketHours
2363 if (optionUniverse !=
null)
2365 foreach (var subscriptionDataConfig
in configs.Concat(underlyingConfigs))
2367 optionUniverse.
Add(subscriptionDataConfig);
2386 return AddSecurity<Forex>(
SecurityType.Forex, ticker, resolution, market, fillForward, leverage,
false);
2401 return AddSecurity<Cfd>(
SecurityType.Cfd, ticker, resolution, market, fillForward, leverage,
false);
2414 public Index
AddIndex(
string ticker,
Resolution? resolution =
null,
string market =
null,
bool fillForward =
true)
2416 var index = AddSecurity<Index>(
SecurityType.Index, ticker, resolution, market, fillForward, 1,
false);
2432 return AddSecurity<Crypto>(
SecurityType.Crypto, ticker, resolution, market, fillForward, leverage,
false);
2447 return AddSecurity<CryptoFuture>(
SecurityType.CryptoFuture, ticker, resolution, market, fillForward, leverage,
false);
2483 if (security.Invested)
2489 security.Cache.Reset();
2492 security.IsTradable =
false;
2496 foreach (var kvp
in UniverseManager.Where(x => x.Value.Configuration.Symbol == symbol
2499 var universe = kvp.Value;
2501 var otherUniverses =
UniverseManager.Select(ukvp => ukvp.Value).Where(u => !ReferenceEquals(u, universe)).ToList();
2505 if (!otherUniverses.Any(u => u.Members.ContainsKey(underlying.Symbol)))
2513 foreach (var child
in universe.Members.Values.OrderBy(security1 => security1.Symbol))
2515 if (!otherUniverses.Any(u => u.Members.ContainsKey(child.Symbol)) && !child.Symbol.IsCanonical())
2523 _universeSelectionUniverses.Remove(security.Symbol);
2528 lock (_pendingUniverseAdditionsLock)
2534 universe.Remove(symbol);
2537 _pendingUserDefinedUniverseSecurityAdditions.RemoveAll(addition => addition.Security.Symbol == symbol);
2560 return AddData<T>(ticker, resolution, fillForward:
false, leverage: 1m);
2579 return AddData<T>(underlying, resolution, fillForward:
false, leverage: 1m);
2597 return AddData<T>(ticker, resolution,
null, fillForward, leverage);
2614 return AddData<T>(underlying, resolution,
null, fillForward, leverage);
2631 return AddData(typeof(
T), ticker, resolution, timeZone, fillForward, leverage);
2648 return AddData(typeof(
T), underlying, resolution, timeZone, fillForward, leverage);
2670 SetDatabaseEntries(key, properties, exchangeHours);
2673 return AddData(typeof(
T), ticker, resolution,
null, fillForward, leverage);
2685 if (!_liveMode && (
string.IsNullOrEmpty(message) || _previousDebugMessage == message))
return;
2686 _debugMessages.Enqueue(message);
2687 _previousDebugMessage = message;
2699 Debug(message.ToStringInvariant());
2711 Debug(message.ToStringInvariant());
2723 Debug(message.ToStringInvariant());
2733 public void Log(
string message)
2735 if (!_liveMode &&
string.IsNullOrEmpty(message))
return;
2736 _logMessages.Enqueue(message);
2748 Log(message.ToStringInvariant());
2758 public void Log(
double message)
2760 Log(message.ToStringInvariant());
2770 public void Log(decimal message)
2772 Log(message.ToStringInvariant());
2784 if (!_liveMode && (
string.IsNullOrEmpty(message) || _previousErrorMessage == message))
return;
2785 _errorMessages.Enqueue(message);
2786 _previousErrorMessage = message;
2798 Error(message.ToStringInvariant());
2810 Error(message.ToStringInvariant());
2822 Error(message.ToStringInvariant());
2834 var message = error.Message;
2835 if (!_liveMode && (
string.IsNullOrEmpty(message) || _previousErrorMessage == message))
return;
2836 _errorMessages.Enqueue(message);
2837 _previousErrorMessage = message;
2845 public void Quit(
string message =
"")
2847 Debug(
"Quit(): " + message);
2896 private T AddSecurity<T>(
SecurityType securityType,
string ticker,
Resolution? resolution,
string market,
bool fillForward, decimal leverage,
bool extendedMarketHours,
2902 if (!
BrokerageModel.DefaultMarkets.TryGetValue(securityType, out market))
2904 throw new Exception(
"No default market set for security type: " + securityType);
2909 if (!SymbolCache.TryGetSymbol(ticker, out symbol) ||
2910 symbol.ID.Market != market ||
2911 symbol.SecurityType != securityType)
2921 return (
T)AddToUserDefinedUniverse(security, configs);
2928 [DocumentationAttribute(HistoricalData)]
2931 if (historyProvider ==
null)
2933 throw new ArgumentNullException(nameof(historyProvider),
"Algorithm.SetHistoryProvider(): Historical data provider cannot be null.");
2946 if (exception ==
null)
2948 throw new ArgumentNullException(nameof(exception),
"Algorithm.SetRunTimeError(): Algorithm.RunTimeError cannot be set to null.");
2972 public string Download(
string address) =>
Download(address, Enumerable.Empty<KeyValuePair<string, string>>());
2983 public string Download(
string address, IEnumerable<KeyValuePair<string, string>> headers) =>
Download(address, headers,
null,
null);
2996 public string Download(
string address, IEnumerable<KeyValuePair<string, string>> headers,
string userName,
string password)
2998 return _api.
Download(address, headers, userName, password);
3009 return Schedule.TrainingNow(trainingCode);
3022 return Schedule.Training(dateRule, timeRule, trainingCode);
3030 private void OnInsightsGenerated(
Insight[] insights)
3035 Log($
"{Time}: ALPHA: {string.Join(" |
", insights.Select(i => i.ToString()).OrderBy(i => i))}");
3047 [DocumentationAttribute(HandlingData)]
3099 var shortableQuantity = security.ShortableProvider.ShortableQuantity(symbol, security.LocalTime);
3100 if (shortableQuantity ==
null)
3107 order => order.Symbol == symbol && (!updateOrderId.HasValue || order.OrderId != updateOrderId.Value));
3109 var portfolioQuantity = security.Holdings.Quantity;
3112 if (portfolioQuantity + openOrderQuantity <= -shortableQuantity)
3117 shortQuantity = -Math.Abs(shortQuantity);
3118 return portfolioQuantity + shortQuantity + openOrderQuantity >= -shortableQuantity;
3132 return security.ShortableProvider.ShortableQuantity(symbol, security.LocalTime) ?? 0;
3148 return _securityDefinitionSymbolResolver.
ISIN(isin, GetVerifiedTradingDate(tradingDate));
3160 return _securityDefinitionSymbolResolver.
ISIN(symbol);
3180 return _securityDefinitionSymbolResolver.
CompositeFIGI(compositeFigi, GetVerifiedTradingDate(tradingDate));
3192 return _securityDefinitionSymbolResolver.
CompositeFIGI(symbol);
3208 return _securityDefinitionSymbolResolver.
CUSIP(cusip, GetVerifiedTradingDate(tradingDate));
3220 return _securityDefinitionSymbolResolver.
CUSIP(symbol);
3236 return _securityDefinitionSymbolResolver.
SEDOL(sedol, GetVerifiedTradingDate(tradingDate));
3248 return _securityDefinitionSymbolResolver.
SEDOL(symbol);
3264 return _securityDefinitionSymbolResolver.
CIK(cik, GetVerifiedTradingDate(tradingDate));
3276 return _securityDefinitionSymbolResolver.
CIK(symbol);
3300 return symbols.Select(symbol =>
Fundamentals(symbol)).ToList();
3328 private DateTime GetVerifiedTradingDate(DateTime? tradingDate)
3330 tradingDate ??=
Time.Date;
3331 if (tradingDate >
Time.Date)
3333 throw new ArgumentException($
"The trading date provided: \"{tradingDate:yyyy-MM-dd}\" is after the current algorithm's trading date: \"{Time:yyyy-MM-dd}\"");
3336 return tradingDate.Value;
3342 private void SetLiveModeStartDate()
3346 throw new InvalidOperationException(
"SetLiveModeStartDate should only be called during live trading!");
3348 _start = DateTime.UtcNow.ConvertFromUtc(
TimeZone);
3350 _startDate = _start.Date;
3360 if (_statisticsService ==
null)
3362 _statisticsService = statisticsService;