17 using System.Collections.Generic;
19 using System.Linq.Expressions;
20 using System.Globalization;
22 using NodaTime.TimeZones;
39 using System.Collections.Concurrent;
63 #region Documentation Attribute Categories
64 const string AddingData =
"Adding Data";
65 const string AlgorithmFramework =
"Algorithm Framework";
66 const string Charting =
"Charting";
67 const string ConsolidatingData =
"Consolidating Data";
68 const string HandlingData =
"Handling Data";
69 const string HistoricalData =
"Historical Data";
70 const string Indicators =
"Indicators";
71 const string LiveTrading =
"Live Trading";
72 const string Logging =
"Logging";
73 const string MachineLearning =
"Machine Learning";
74 const string Modeling =
"Modeling";
75 const string ParameterAndOptimization =
"Parameter and Optimization";
76 const string ScheduledEvents =
"Scheduled Events";
77 const string SecuritiesAndPortfolio =
"Securities and Portfolio";
78 const string TradingAndOrders =
"Trading and Orders";
79 const string Universes =
"Universes";
85 private DateTime _start;
86 private DateTime _startDate;
87 private DateTime _endDate;
89 private bool _liveMode;
90 private string _algorithmId =
"";
91 private ConcurrentQueue<string> _debugMessages =
new ConcurrentQueue<string>();
92 private ConcurrentQueue<string> _logMessages =
new ConcurrentQueue<string>();
93 private ConcurrentQueue<string> _errorMessages =
new ConcurrentQueue<string>();
96 private string _previousDebugMessage =
"";
97 private string _previousErrorMessage =
"";
110 private bool _checkedForOnDataSlice;
111 private Action<Slice> _onDataSlice;
114 private bool _userSetSecurityInitializer;
117 private TimeSpan? _warmupTimeSpan;
118 private int? _warmupBarCount;
119 private Dictionary<string, string> _parameters =
new Dictionary<string, string>();
132 Name = GetType().Name;
143 _startDate =
new DateTime(1998, 01, 01);
438 get {
return _localTimeKeeper.
LocalTime; }
447 get {
return _timeKeeper.UtcTime; }
457 get {
return _localTimeKeeper.
TimeZone; }
517 return _debugMessages;
521 _debugMessages = value;
538 _logMessages = value;
558 return _errorMessages;
562 _errorMessages = value;
590 throw new NotImplementedException(
"Please override the Initialize() method");
601 if (_endDate < _startDate)
603 throw new ArgumentException(
"Please select an algorithm end date greater than start date.");
607 if (portfolioConstructionModel !=
null)
617 portfolioConstructionModel.RebalanceOnInsightChanges
626 Debug(
"Warning: rebalance portfolio settings are set but not supported by the current IPortfolioConstructionModel type: " +
627 $
"{PortfolioConstruction.GetType()}");
645 securityBenchmark.Security.Symbol, securityBenchmark.Security.Type);
648 Log($
"QCAlgorithm.PostInitialize(): Warning: Using a security benchmark of a different timezone ({benchmarkTimeZone})" +
649 $
" than the algorithm TimeZone ({TimeZone}) may lead to skewed and incorrect statistics. Use a higher resolution than daily to minimize.");
653 if(TryGetWarmupHistoryStartTime(out var result))
684 return _parameters.TryGetValue(name, out var value) ? value : defaultValue;
697 return _parameters.TryGetValue(name, out var strValue) &&
int.TryParse(strValue, out var value) ? value : defaultValue;
710 return _parameters.TryGetValue(name, out var strValue) &&
711 double.TryParse(strValue, NumberStyles.Any, CultureInfo.InvariantCulture, out var value) ? value : defaultValue;
724 return _parameters.TryGetValue(name, out var strValue) &&
725 decimal.TryParse(strValue, NumberStyles.Any, CultureInfo.InvariantCulture, out var value) ? value : defaultValue;
734 return _parameters.ToReadOnlyDictionary();
745 _parameters = parameters.ToDictionary();
750 catch (Exception err)
752 Error(
"Error applying parameter values: " + err.Message);
763 if (availableDataTypes ==
null)
768 foreach (var dataFeed
in availableDataTypes)
785 throw new Exception(
"SetSecurityInitializer() cannot be called after algorithm initialization. " +
786 "When you use the SetSecurityInitializer() method it will apply to all universes and manually added securities.");
789 if (_userSetSecurityInitializer)
791 Debug(
"Warning: SetSecurityInitializer() has already been called, existing security initializers in all universes will be overwritten.");
795 _userSetSecurityInitializer =
true;
804 [Obsolete(
"This method is deprecated. Please use this overload: SetSecurityInitializer(Action<Security> securityInitializer)")]
862 if (!_checkedForOnDataSlice)
864 _checkedForOnDataSlice =
true;
866 var method = GetType().GetMethods()
867 .Where(x => x.Name ==
"OnData")
868 .Where(x => x.DeclaringType != typeof(
QCAlgorithm))
869 .Where(x => x.GetParameters().Length == 1)
870 .FirstOrDefault(x => x.GetParameters()[0].ParameterType == typeof (
Slice));
877 var
self = Expression.Constant(
this);
878 var parameter = Expression.Parameter(typeof (
Slice),
"data");
879 var call = Expression.Call(
self, method, parameter);
880 var lambda = Expression.Lambda<Action<Slice>>(call, parameter);
881 _onDataSlice = lambda.Compile();
884 if (_onDataSlice !=
null)
991 [Obsolete(
"This method is deprecated and will be removed after August 2021. Please use this overload: OnEndOfDay(Symbol symbol)")]
1089 _timeKeeper.SetUtcDateTime(frontier);
1106 tz = DateTimeZoneProviders.Tzdb[timeZone];
1108 catch (DateTimeZoneNotFoundException)
1110 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");
1125 throw new InvalidOperationException(
"Algorithm.SetTimeZone(): Cannot change time zone after algorithm running.");
1128 if (timeZone ==
null)
throw new ArgumentNullException(
"timeZone");
1129 _timeKeeper.AddTimeZone(timeZone);
1130 _localTimeKeeper = _timeKeeper.GetLocalTimeKeeper(timeZone);
1141 _start = _startDate;
1148 SetLiveModeStartDate();
1173 if (!_userSetSecurityInitializer)
1181 var security = kvp.Value;
1186 var leverage = security.Leverage;
1191 security.SetLeverage(leverage);
1218 [Obsolete(
"Symbol implicit operator to string is provided for algorithm use only.")]
1226 throw new InvalidOperationException(
"Algorithm.SetBenchmark(): Cannot change Benchmark after algorithm initialized.");
1230 if (!
BrokerageModel.DefaultMarkets.TryGetValue(securityType, out market))
1258 symbol =
Securities.FirstOrDefault(x => x.Key.Value == ticker).Key;
1263 Debug($
"Warning: SetBenchmark({ticker}): no existing symbol found, benchmark security will be added with {SecurityType.Equity} type.");
1283 throw new InvalidOperationException(
"Algorithm.SetBenchmark(): Cannot change Benchmark after algorithm initialized.");
1302 throw new InvalidOperationException(
"Algorithm.SetBenchmark(): Cannot change Benchmark after algorithm initialized.");
1335 throw new InvalidOperationException(
"Algorithm.SetAccountCurrency(): " +
1336 "Cannot change AccountCurrency after algorithm initialized.");
1339 if (startingCash ==
null)
1341 Debug($
"Changing account currency from {AccountCurrency} to {accountCurrency}...");
1345 Debug($
"Changing account currency from {AccountCurrency} to {accountCurrency}, with a starting cash of {startingCash}...");
1360 SetCash((decimal)startingCash);
1372 SetCash((decimal)startingCash);
1389 throw new InvalidOperationException(
"Algorithm.SetCash(): Cannot change cash available after algorithm initialized.");
1400 public void SetCash(
string symbol, decimal startingCash, decimal conversionRate = 0)
1408 throw new InvalidOperationException(
"Algorithm.SetCash(): Cannot change cash available after algorithm initialized.");
1427 var start =
new DateTime(year, month, day);
1434 catch (Exception err)
1436 throw new ArgumentException($
"Date Invalid: {err.Message}");
1453 var end =
new DateTime(year, month, day);
1456 end = end.Date.AddDays(1).Subtract(TimeSpan.FromTicks(1));
1460 catch (Exception err)
1462 throw new ArgumentException($
"Date Invalid: {err.Message}");
1474 _algorithmId = algorithmId;
1487 if (_liveMode)
return;
1490 start = start.RoundDown(TimeSpan.FromDays(1));
1494 if (start < (
new DateTime(1900, 01, 01)))
1496 throw new ArgumentOutOfRangeException(nameof(start),
"Please select a start date after January 1st, 1900.");
1500 var todayInAlgorithmTimeZone = DateTime.UtcNow.ConvertFromUtc(
TimeZone).Date;
1501 if (start > todayInAlgorithmTimeZone)
1503 throw new ArgumentOutOfRangeException(nameof(start),
"Please select start date less than today");
1509 _start = _startDate = start;
1514 throw new InvalidOperationException(
"Algorithm.SetStartDate(): Cannot change start date after algorithm initialized.");
1528 if (_liveMode)
return;
1533 throw new InvalidOperationException(
"Algorithm.SetEndDate(): Cannot change end date after algorithm initialized.");
1538 var yesterdayInAlgorithmTimeZone = DateTime.UtcNow.ConvertFromUtc(
TimeZone).Date.AddDays(-1);
1539 if (end > yesterdayInAlgorithmTimeZone)
1541 end = yesterdayInAlgorithmTimeZone;
1545 _endDate = end.RoundDown(TimeSpan.FromDays(1)).AddDays(1).AddTicks(-1);
1582 SetLiveModeStartDate();
1611 return AddSecurity(securityType, ticker, resolution, fillForward,
Security.
NullLeverage, extendedMarketHours, dataMappingMode, dataNormalizationMode);
1630 return AddSecurity(securityType, ticker, resolution,
null, fillForward, leverage, extendedMarketHours, dataMappingMode, dataNormalizationMode);
1652 return AddOption(ticker, resolution, market, fillForward, leverage);
1657 return AddFuture(ticker, resolution, market, fillForward, leverage, extendedMarketHours, dataMappingMode, dataNormalizationMode);
1664 if (!
BrokerageModel.DefaultMarkets.TryGetValue(securityType, out market))
1666 throw new KeyNotFoundException($
"No default market set for security type: {securityType}");
1672 symbol.ID.Market != market ||
1673 symbol.SecurityType != securityType)
1678 return AddSecurity(symbol, resolution, fillForward, leverage, extendedMarketHours, dataMappingMode, dataNormalizationMode);
1680 catch (Exception err)
1682 Error(
"Algorithm.AddSecurity(): " + err);
1705 var contractOffset = (uint)Math.Abs(contractDepthOffset);
1708 throw new ArgumentOutOfRangeException(nameof(contractDepthOffset), $
"'contractDepthOffset' current maximum value is {Futures.MaximumContractDepthOffset}." +
1709 $
" Front month (0) and only {Futures.MaximumContractDepthOffset} back month contracts are currently supported.");
1717 return AddOptionContract(symbol, resolution, fillForward, leverage, extendedMarketHours);
1720 var isFilteredSubscription = !isCanonical;
1721 List<SubscriptionDataConfig> configs;
1724 if (dataNormalizationMode.HasValue)
1729 extendedMarketHours,
1730 isFilteredSubscription,
1731 dataNormalizationMode: dataNormalizationMode.
Value,
1732 contractDepthOffset: (uint)contractDepthOffset);
1739 extendedMarketHours,
1740 isFilteredSubscription,
1741 contractDepthOffset: (uint)contractDepthOffset);
1755 var canonicalConfig = configs.First();
1765 GetResolution(symbol, resolution), isCanonical:
false);
1768 ExtendedMarketHours = extendedMarketHours,
1771 ContractDepthOffset = (int)contractOffset,
1772 SubscriptionDataTypes = dataTypes,
1779 continuousContractSymbol.ID.Symbol,
1780 continuousContractSymbol.ID.SecurityType,
1781 security.Exchange.Hours);
1792 return AddToUserDefinedUniverse(security, configs);
1810 return AddSecurity<Equity>(
SecurityType.Equity, ticker, resolution, market, fillForward, leverage, extendedMarketHours, normalizationMode: dataNormalizationMode);
1829 throw new KeyNotFoundException($
"No default market set for security type: {SecurityType.Option}");
1834 return AddOption(underlyingSymbol, resolution, market, fillForward, leverage);
1852 return AddOption(underlying,
null, resolution, market, fillForward, leverage);
1875 if (!
BrokerageModel.DefaultMarkets.TryGetValue(optionType, out market))
1877 throw new KeyNotFoundException($
"No default market set for security type: {optionType}");
1884 if (!
string.IsNullOrEmpty(targetOption))
1886 alias = $
"?{targetOption}";
1890 alias = $
"?{underlying.Value}";
1893 canonicalSymbol.ID.Market != market ||
1894 !canonicalSymbol.SecurityType.IsOption())
1918 bool fillForward =
true, decimal leverage =
Security.
NullLeverage,
bool extendedMarketHours =
false,
1926 throw new KeyNotFoundException($
"No default market set for security type: {SecurityType.Future}");
1931 var alias =
"/" + ticker;
1933 canonicalSymbol.ID.Market != market ||
1939 return (
Future)
AddSecurity(canonicalSymbol, resolution, fillForward, leverage, extendedMarketHours, dataMappingMode: dataMappingMode,
1940 dataNormalizationMode: dataNormalizationMode, contractDepthOffset: contractDepthOffset);
1956 return (
Future)
AddSecurity(symbol, resolution, fillForward, leverage, extendedMarketHours);
1971 throw new ArgumentException(
"Symbol provided must be canonical (i.e. the Symbol returned from AddFuture(), not AddFutureContract().");
1993 throw new ArgumentException(
"Expected non-canonical Symbol (i.e. a Symbol representing a specific Future contract");
1996 return AddOptionContract(symbol, resolution, fillForward, leverage, extendedMarketHours);
2042 throw new ArgumentException(
"Symbol provided must be of type SecurityType.Index");
2045 return AddOption(symbol, targetOption, resolution, symbol.
ID.
Market, fillForward);
2061 throw new ArgumentException(
"Symbol provided must be of type SecurityType.IndexOption");
2082 throw new ArgumentException($
"Unexpected option symbol {symbol}. " +
2083 $
"Please provide a valid option contract with it's underlying symbol set.");
2089 List<SubscriptionDataConfig> underlyingConfigs;
2092 underlyingSecurity =
AddSecurity(underlying, resolution, fillForward, leverage, extendedMarketHours);
2101 var dataNormalizationMode = underlyingConfigs.DataNormalizationMode();
2106 throw new ArgumentException($
"The underlying {underlying.SecurityType} asset ({underlying.Value}) is set to " +
2107 $
"{dataNormalizationMode}, please change this to DataNormalizationMode.Raw with the " +
2108 "SetDataNormalization() method"
2119 underlyingSecurity.RefreshDataNormalizationModeProperty();
2130 Resolution = underlyingConfigs.GetHighestResolution(),
2131 ExtendedMarketHours = extendedMarketHours
2133 lock (_pendingUniverseAdditionsLock)
2135 universe = _pendingUniverseAdditions.FirstOrDefault(u => u.Configuration.Symbol == universeSymbol)
2142 if (optionUniverse !=
null)
2144 foreach (var subscriptionDataConfig
in configs.Concat(underlyingConfigs))
2146 optionUniverse.
Add(subscriptionDataConfig);
2165 return AddSecurity<Forex>(
SecurityType.Forex, ticker, resolution, market, fillForward, leverage,
false);
2180 return AddSecurity<Cfd>(
SecurityType.Cfd, ticker, resolution, market, fillForward, leverage,
false);
2193 public Index
AddIndex(
string ticker,
Resolution? resolution =
null,
string market =
null,
bool fillForward =
true)
2195 var index = AddSecurity<Index>(
SecurityType.Index, ticker, resolution, market, fillForward, 1,
false);
2211 return AddSecurity<Crypto>(
SecurityType.Crypto, ticker, resolution, market, fillForward, leverage,
false);
2226 return AddSecurity<CryptoFuture>(
SecurityType.CryptoFuture, ticker, resolution, market, fillForward, leverage,
false);
2262 if (security.Invested)
2268 security.Cache.Reset();
2271 security.IsTradable =
false;
2275 foreach (var kvp
in UniverseManager.Where(x => x.Value.Configuration.Symbol == symbol
2278 var universe = kvp.Value;
2280 var otherUniverses =
UniverseManager.Select(ukvp => ukvp.Value).Where(u => !ReferenceEquals(u, universe)).ToList();
2284 if (!otherUniverses.Any(u => u.Members.ContainsKey(underlying.Symbol)))
2292 foreach (var child
in universe.Members.Values.OrderBy(security1 => security1.Symbol))
2294 if (!otherUniverses.Any(u => u.Members.ContainsKey(child.Symbol)) && !child.Symbol.IsCanonical())
2302 _userAddedUniverses.Remove(kvp.Key);
2307 lock (_pendingUniverseAdditionsLock)
2312 .Concat(_pendingUniverseAdditions)
2315 universe.Remove(symbol);
2318 _pendingUserDefinedUniverseSecurityAdditions.RemoveAll(addition => addition.Security.Symbol == symbol);
2341 return AddData<T>(ticker, resolution, fillForward:
false, leverage: 1m);
2360 return AddData<T>(underlying, resolution, fillForward:
false, leverage: 1m);
2378 return AddData<T>(ticker, resolution,
null, fillForward, leverage);
2395 return AddData<T>(underlying, resolution,
null, fillForward, leverage);
2412 return AddData(typeof(T), ticker, resolution, timeZone, fillForward, leverage);
2429 return AddData(typeof(T), underlying, resolution, timeZone, fillForward, leverage);
2451 SetDatabaseEntries(key, properties, exchangeHours);
2454 return AddData(typeof(T), ticker, resolution,
null, fillForward, leverage);
2466 if (!_liveMode && (message ==
"" || _previousDebugMessage == message))
return;
2467 _debugMessages.Enqueue(message);
2468 _previousDebugMessage = message;
2480 Debug(message.ToStringInvariant());
2492 Debug(message.ToStringInvariant());
2504 Debug(message.ToStringInvariant());
2514 public void Log(
string message)
2516 if (!_liveMode && message ==
"")
return;
2517 _logMessages.Enqueue(message);
2529 Log(message.ToStringInvariant());
2539 public void Log(
double message)
2541 Log(message.ToStringInvariant());
2551 public void Log(decimal message)
2553 Log(message.ToStringInvariant());
2565 if (!_liveMode && (message ==
"" || _previousErrorMessage == message))
return;
2566 _errorMessages.Enqueue(message);
2567 _previousErrorMessage = message;
2579 Error(message.ToStringInvariant());
2591 Error(message.ToStringInvariant());
2603 Error(message.ToStringInvariant());
2615 var message = error.Message;
2616 if (!_liveMode && (message ==
"" || _previousErrorMessage == message))
return;
2617 _errorMessages.Enqueue(message);
2618 _previousErrorMessage = message;
2626 public void Quit(
string message =
"")
2628 Debug(
"Quit(): " + message);
2677 private T AddSecurity<T>(
SecurityType securityType,
string ticker,
Resolution? resolution,
string market,
bool fillForward, decimal leverage,
bool extendedMarketHours,
2683 if (!
BrokerageModel.DefaultMarkets.TryGetValue(securityType, out market))
2685 throw new Exception(
"No default market set for security type: " + securityType);
2690 if (!SymbolCache.TryGetSymbol(ticker, out symbol) ||
2691 symbol.ID.Market != market ||
2692 symbol.SecurityType != securityType)
2702 return (T) AddToUserDefinedUniverse(security, configs);
2709 [DocumentationAttribute(HistoricalData)]
2712 if (historyProvider ==
null)
2714 throw new ArgumentNullException(nameof(historyProvider),
"Algorithm.SetHistoryProvider(): Historical data provider cannot be null.");
2727 if (exception ==
null)
2729 throw new ArgumentNullException(nameof(exception),
"Algorithm.SetRunTimeError(): Algorithm.RunTimeError cannot be set to null.");
2753 public string Download(
string address) =>
Download(address, Enumerable.Empty<KeyValuePair<string, string>>());
2764 public string Download(
string address, IEnumerable<KeyValuePair<string, string>> headers) =>
Download(address, headers,
null,
null);
2777 public string Download(
string address, IEnumerable<KeyValuePair<string, string>> headers,
string userName,
string password)
2779 return _api.
Download(address, headers, userName, password);
2812 private void OnInsightsGenerated(
Insight[] insights)
2817 Log($
"{Time}: ALPHA: {string.Join(" |
", insights.Select(i => i.ToString()).OrderBy(i => i))}");
2829 [DocumentationAttribute(HandlingData)]
2877 var shortableQuantity =
Securities[symbol].ShortableProvider.ShortableQuantity(symbol,
Time);
2878 if (shortableQuantity ==
null)
2887 if (portfolioQuantity + openOrderQuantity <= -shortableQuantity)
2892 shortQuantity = -Math.Abs(shortQuantity);
2893 return portfolioQuantity + shortQuantity + openOrderQuantity >= -shortableQuantity;
2906 return Securities[symbol].ShortableProvider.ShortableQuantity(symbol,
Time) ?? 0;
2922 return _securityDefinitionSymbolResolver.
ISIN(isin, GetVerifiedTradingDate(tradingDate));
2934 return _securityDefinitionSymbolResolver.
ISIN(symbol);
2954 return _securityDefinitionSymbolResolver.
CompositeFIGI(compositeFigi, GetVerifiedTradingDate(tradingDate));
2966 return _securityDefinitionSymbolResolver.
CompositeFIGI(symbol);
2982 return _securityDefinitionSymbolResolver.
CUSIP(cusip, GetVerifiedTradingDate(tradingDate));
2994 return _securityDefinitionSymbolResolver.
CUSIP(symbol);
3010 return _securityDefinitionSymbolResolver.
SEDOL(sedol, GetVerifiedTradingDate(tradingDate));
3022 return _securityDefinitionSymbolResolver.
SEDOL(symbol);
3050 private DateTime GetVerifiedTradingDate(DateTime? tradingDate)
3052 tradingDate ??=
Time.Date;
3053 if (tradingDate >
Time.Date)
3055 throw new ArgumentException($
"The trading date provided: \"{tradingDate:yyyy-MM-dd}\" is after the current algorithm's trading date: \"{Time:yyyy-MM-dd}\"");
3058 return tradingDate.Value;
3064 private void SetLiveModeStartDate()
3068 throw new InvalidOperationException(
"SetLiveModeStartDate should only be called during live trading!");
3070 _start = DateTime.UtcNow.ConvertFromUtc(
TimeZone);
3072 _startDate = _start.Date;
3079 private bool IsAlreadyPending(
Symbol symbol)
3081 lock (_pendingUniverseAdditionsLock)
3083 return _pendingUniverseAdditions.Any(u => u.Configuration.Symbol == symbol);