// this event fires whenever we have changes to our universe public override void OnSecuritiesChanged(SecurityChanges changes) { _changes = changes; Log($"OnSecuritiesChanged({UtcTime:o}):: {changes}"); }
// this event fires whenever we have changes to our universe public override void OnSecuritiesChanged(SecurityChanges changes) { _changes = changes; if (changes.AddedSecurities.Count > 0) { Debug("Securities added: " + string.Join(",", changes.AddedSecurities.Select(x => x.Symbol.Value))); } if (changes.RemovedSecurities.Count > 0) { Debug("Securities removed: " + string.Join(",", changes.RemovedSecurities.Select(x => x.Symbol.Value))); } }
/// <summary> /// Primary entry point. /// </summary> public void Run() { IsActive = true; // we want to emit to the bridge minimally once a second since the data feed is // the heartbeat of the application, so this value will contain a second after // the last emit time, and if we pass this time, we'll emit even with no data var nextEmit = DateTime.MinValue; try { while (!_cancellationTokenSource.IsCancellationRequested) { // perform sleeps to wake up on the second? _frontierUtc = _timeProvider.GetUtcNow(); _frontierTimeProvider.SetCurrentTime(_frontierUtc); var data = new List <DataFeedPacket>(); foreach (var subscription in Subscriptions) { var config = subscription.Configuration; var packet = new DataFeedPacket(subscription.Security, config); // dequeue data that is time stamped at or before this frontier while (subscription.MoveNext() && subscription.Current != null) { var clone = subscription.Current.Clone(subscription.Current.IsFillForward); clone.Time = clone.Time.RoundDownInTimeZone(config.Increment, config.ExchangeTimeZone, config.DataTimeZone); packet.Add(clone); } // if we have data, add it to be added to the bridge if (packet.Count > 0) { data.Add(packet); } // we have new universe data to select based on if (subscription.IsUniverseSelectionSubscription && packet.Count > 0) { var universe = subscription.Universe; // always wait for other thread to sync up if (!_bridge.WaitHandle.WaitOne(Timeout.Infinite, _cancellationTokenSource.Token)) { break; } // assume that if the first item is a base data collection then the enumerator handled the aggregation, // otherwise, load all the the data into a new collection instance var collection = packet.Data[0] as BaseDataCollection ?? new BaseDataCollection(_frontierUtc, config.Symbol, packet.Data); _changes += _universeSelection.ApplyUniverseSelection(universe, _frontierUtc, collection); } } // check for cancellation if (_cancellationTokenSource.IsCancellationRequested) { return; } // emit on data or if we've elapsed a full second since last emit if (data.Count != 0 || _frontierUtc >= nextEmit) { _bridge.Add(TimeSlice.Create(_frontierUtc, _algorithm.TimeZone, _algorithm.Portfolio.CashBook, data, _changes), _cancellationTokenSource.Token); // force emitting every second nextEmit = _frontierUtc.RoundDown(Time.OneSecond).Add(Time.OneSecond); } // reset our security changes _changes = SecurityChanges.None; // take a short nap Thread.Sleep(1); } } catch (Exception err) { Log.Error(err); _algorithm.RunTimeError = err; _algorithm.Status = AlgorithmStatus.RuntimeError; // send last empty packet list before terminating, // so the algorithm manager has a chance to detect the runtime error // and exit showing the correct error instead of a timeout nextEmit = _frontierUtc.RoundDown(Time.OneSecond).Add(Time.OneSecond); if (!_cancellationTokenSource.IsCancellationRequested) { _bridge.Add( TimeSlice.Create(nextEmit, _algorithm.TimeZone, _algorithm.Portfolio.CashBook, new List <DataFeedPacket>(), SecurityChanges.None), _cancellationTokenSource.Token); } } Log.Trace("LiveTradingDataFeed.Run(): Exited thread."); IsActive = false; }
// this event fires whenever we have changes to our universe public override void OnSecuritiesChanged(SecurityChanges changes) { _changes = changes; }
public void OnSecuritiesChanged(SecurityChanges changes) { }
/// <summary> /// Creates a new <see cref="TimeSlice"/> for the specified time using the specified data /// </summary> /// <param name="utcDateTime">The UTC frontier date time</param> /// <param name="data">The data in this <see cref="TimeSlice"/></param> /// <param name="changes">The new changes that are seen in this time slice as a result of universe selection</param> /// <param name="universeData"></param> /// <returns>A new <see cref="TimeSlice"/> containing the specified data</returns> public TimeSlice Create(DateTime utcDateTime, List <DataFeedPacket> data, SecurityChanges changes, Dictionary <Universe, BaseDataCollection> universeData) { int count = 0; var security = new List <UpdateData <ISecurityPrice> >(data.Count); List <UpdateData <ISecurityPrice> > custom = null; var consolidator = new List <UpdateData <SubscriptionDataConfig> >(data.Count); var allDataForAlgorithm = new List <BaseData>(data.Count); var optionUnderlyingUpdates = new Dictionary <Symbol, BaseData>(); Split split; Dividend dividend; Delisting delisting; SymbolChangedEvent symbolChange; // we need to be able to reference the slice being created in order to define the // evaluation of option price models, so we define a 'future' that can be referenced // in the option price model evaluation delegates for each contract Slice slice = null; var sliceFuture = new Lazy <Slice>(() => slice); var algorithmTime = utcDateTime.ConvertFromUtc(_timeZone); TradeBars tradeBars = null; QuoteBars quoteBars = null; Ticks ticks = null; Splits splits = null; Dividends dividends = null; Delistings delistings = null; OptionChains optionChains = null; FuturesChains futuresChains = null; SymbolChangedEvents symbolChanges = null; UpdateEmptyCollections(algorithmTime); if (universeData.Count > 0) { // count universe data foreach (var kvp in universeData) { count += kvp.Value.Data.Count; } } // ensure we read equity data before option data, so we can set the current underlying price foreach (var packet in data) { // filter out packets for removed subscriptions if (packet.IsSubscriptionRemoved) { continue; } var list = packet.Data; var symbol = packet.Configuration.Symbol; if (list.Count == 0) { continue; } // keep count of all data points if (list.Count == 1 && list[0] is BaseDataCollection) { var baseDataCollectionCount = ((BaseDataCollection)list[0]).Data.Count; if (baseDataCollectionCount == 0) { continue; } count += baseDataCollectionCount; } else { count += list.Count; } if (!packet.Configuration.IsInternalFeed && packet.Configuration.IsCustomData) { if (custom == null) { custom = new List <UpdateData <ISecurityPrice> >(1); } // This is all the custom data custom.Add(new UpdateData <ISecurityPrice>(packet.Security, packet.Configuration.Type, list, packet.Configuration.IsInternalFeed)); } var securityUpdate = new List <BaseData>(list.Count); var consolidatorUpdate = new List <BaseData>(list.Count); var containsFillForwardData = false; for (var i = 0; i < list.Count; i++) { var baseData = list[i]; if (!packet.Configuration.IsInternalFeed) { // this is all the data that goes into the algorithm allDataForAlgorithm.Add(baseData); } containsFillForwardData |= baseData.IsFillForward; // don't add internal feed data to ticks/bars objects if (baseData.DataType != MarketDataType.Auxiliary) { var tick = baseData as Tick; if (!packet.Configuration.IsInternalFeed) { // populate data dictionaries switch (baseData.DataType) { case MarketDataType.Tick: if (ticks == null) { ticks = new Ticks(algorithmTime); } ticks.Add(baseData.Symbol, (Tick)baseData); break; case MarketDataType.TradeBar: if (tradeBars == null) { tradeBars = new TradeBars(algorithmTime); } var newTradeBar = (TradeBar)baseData; TradeBar existingTradeBar; // if we have an existing bar keep the highest resolution one // e.g Hour and Minute resolution subscriptions for the same symbol // see CustomUniverseWithBenchmarkRegressionAlgorithm if (!tradeBars.TryGetValue(baseData.Symbol, out existingTradeBar) || existingTradeBar.Period > newTradeBar.Period) { tradeBars[baseData.Symbol] = newTradeBar; } break; case MarketDataType.QuoteBar: if (quoteBars == null) { quoteBars = new QuoteBars(algorithmTime); } var newQuoteBar = (QuoteBar)baseData; QuoteBar existingQuoteBar; // if we have an existing bar keep the highest resolution one // e.g Hour and Minute resolution subscriptions for the same symbol // see CustomUniverseWithBenchmarkRegressionAlgorithm if (!quoteBars.TryGetValue(baseData.Symbol, out existingQuoteBar) || existingQuoteBar.Period > newQuoteBar.Period) { quoteBars[baseData.Symbol] = newQuoteBar; } break; case MarketDataType.OptionChain: if (optionChains == null) { optionChains = new OptionChains(algorithmTime); } optionChains[baseData.Symbol] = (OptionChain)baseData; break; case MarketDataType.FuturesChain: if (futuresChains == null) { futuresChains = new FuturesChains(algorithmTime); } futuresChains[baseData.Symbol] = (FuturesChain)baseData; break; } // special handling of options data to build the option chain if (symbol.SecurityType == SecurityType.Option) { if (optionChains == null) { optionChains = new OptionChains(algorithmTime); } if (baseData.DataType == MarketDataType.OptionChain) { optionChains[baseData.Symbol] = (OptionChain)baseData; } else if (!HandleOptionData(algorithmTime, baseData, optionChains, packet.Security, sliceFuture, optionUnderlyingUpdates)) { continue; } } // special handling of futures data to build the futures chain if (symbol.SecurityType == SecurityType.Future) { if (futuresChains == null) { futuresChains = new FuturesChains(algorithmTime); } if (baseData.DataType == MarketDataType.FuturesChain) { futuresChains[baseData.Symbol] = (FuturesChain)baseData; } else if (!HandleFuturesData(algorithmTime, baseData, futuresChains, packet.Security)) { continue; } } // this is data used to update consolidators // do not add it if it is a Suspicious tick if (tick == null || !tick.Suspicious) { consolidatorUpdate.Add(baseData); } } // this is the data used set market prices // do not add it if it is a Suspicious tick if (tick != null && tick.Suspicious) { continue; } securityUpdate.Add(baseData); // option underlying security update if (!packet.Configuration.IsInternalFeed && symbol.SecurityType == SecurityType.Equity) { optionUnderlyingUpdates[symbol] = baseData; } } else if (!packet.Configuration.IsInternalFeed) { // include checks for various aux types so we don't have to construct the dictionaries in Slice if ((delisting = baseData as Delisting) != null) { if (delistings == null) { delistings = new Delistings(algorithmTime); } delistings[symbol] = delisting; } else if ((dividend = baseData as Dividend) != null) { if (dividends == null) { dividends = new Dividends(algorithmTime); } dividends[symbol] = dividend; } else if ((split = baseData as Split) != null) { if (splits == null) { splits = new Splits(algorithmTime); } splits[symbol] = split; } else if ((symbolChange = baseData as SymbolChangedEvent) != null) { if (symbolChanges == null) { symbolChanges = new SymbolChangedEvents(algorithmTime); } // symbol changes is keyed by the requested symbol symbolChanges[packet.Configuration.Symbol] = symbolChange; } } } if (securityUpdate.Count > 0) { security.Add(new UpdateData <ISecurityPrice>(packet.Security, packet.Configuration.Type, securityUpdate, packet.Configuration.IsInternalFeed, containsFillForwardData)); } if (consolidatorUpdate.Count > 0) { consolidator.Add(new UpdateData <SubscriptionDataConfig>(packet.Configuration, packet.Configuration.Type, consolidatorUpdate, packet.Configuration.IsInternalFeed, containsFillForwardData)); } } slice = new Slice(algorithmTime, allDataForAlgorithm, tradeBars ?? _emptyTradeBars, quoteBars ?? _emptyQuoteBars, ticks ?? _emptyTicks, optionChains ?? _emptyOptionChains, futuresChains ?? _emptyFuturesChains, splits ?? _emptySplits, dividends ?? _emptyDividends, delistings ?? _emptyDelistings, symbolChanges ?? _emptySymbolChangedEvents, allDataForAlgorithm.Count > 0); return(new TimeSlice(utcDateTime, count, slice, data, security, consolidator, custom ?? _emptyCustom, changes, universeData)); }
/// <summary> /// Event fired each time the we add/remove securities from the data feed /// </summary> /// <param name="changes">Object containing AddedSecurities and RemovedSecurities</param> public override void OnSecuritiesChanged(SecurityChanges changes) { _changes = changes; Log(Time + " " + changes); }
/// <summary> /// Adds and removes the security changes to/from the collection /// </summary> /// <param name="securities">The securities collection to be updated with the changes</param> /// <param name="changes">The changes to be applied to the securities collection</param> public static void UpdateCollection(ICollection <Security> securities, SecurityChanges changes) { Update(changes, securities.Add, removed => securities.Remove(removed)); }
/// <summary> /// Adds and removes the security changes to/from the collection /// </summary> /// <param name="securities">The securities collection to be updated with the changes</param> /// <param name="changes">The changes to be applied to the securities collection</param> /// <param name="valueFactory">Delegate used to create instances of <typeparamref name="TValue"/> from a <see cref="Security"/> object</param> public static void UpdateCollection <TValue>(ICollection <TValue> securities, SecurityChanges changes, Func <Security, TValue> valueFactory) { Update(changes, added => securities.Add(valueFactory(added)), removed => securities.Remove(valueFactory(removed))); }
/// <summary> /// Initializes a new <see cref="TimeSlice"/> containing the specified data /// </summary> public TimeSlice(DateTime time, int dataPointCount, Slice slice, List <KeyValuePair <Security, List <BaseData> > > data, List <KeyValuePair <Cash, BaseData> > cashBookUpdateData, List <KeyValuePair <Security, BaseData> > securitiesUpdateData, List <KeyValuePair <SubscriptionDataConfig, List <BaseData> > > consolidatorUpdateData, List <KeyValuePair <Security, List <BaseData> > > customData, SecurityChanges securityChanges) { Time = time; Data = data; Slice = slice; CustomData = customData; DataPointCount = dataPointCount; CashBookUpdateData = cashBookUpdateData; SecuritiesUpdateData = securitiesUpdateData; ConsolidatorUpdateData = consolidatorUpdateData; SecurityChanges = securityChanges; }
/// <summary> /// Creates a new <see cref="TimeSlice"/> for the specified time using the specified data /// </summary> /// <param name="algorithm">The algorithm we're creating <see cref="TimeSlice"/> instances for</param> /// <param name="utcDateTime">The UTC frontier date time</param> /// <param name="data">The data in this <see cref="TimeSlice"/></param> /// <param name="changes">The new changes that are seen in this time slice as a result of universe selection</param> /// <returns>A new <see cref="TimeSlice"/> containing the specified data</returns> public static TimeSlice Create(IAlgorithm algorithm, DateTime utcDateTime, List <KeyValuePair <Security, List <BaseData> > > data, SecurityChanges changes) { int count = 0; var security = new List <KeyValuePair <Security, BaseData> >(); var custom = new List <KeyValuePair <Security, List <BaseData> > >(); var consolidator = new List <KeyValuePair <SubscriptionDataConfig, List <BaseData> > >(); var allDataForAlgorithm = new List <BaseData>(data.Count); var cash = new List <KeyValuePair <Cash, BaseData> >(algorithm.Portfolio.CashBook.Count); var cashSecurities = new HashSet <string>(); foreach (var cashItem in algorithm.Portfolio.CashBook.Values) { cashSecurities.Add(cashItem.SecuritySymbol); } Split split; Dividend dividend; Delisting delisting; var algorithmTime = utcDateTime.ConvertFromUtc(algorithm.TimeZone); var tradeBars = new TradeBars(algorithmTime); var ticks = new Ticks(algorithmTime); var splits = new Splits(algorithmTime); var dividends = new Dividends(algorithmTime); var delistings = new Delistings(algorithmTime); foreach (var kvp in data) { var list = kvp.Value; var symbol = kvp.Key.Symbol; // keep count of all data points count += list.Count; BaseData update = null; var consolidatorUpdate = new List <BaseData>(list.Count); for (int i = list.Count - 1; i > -1; i--) { var baseData = list[i]; if (!kvp.Key.SubscriptionDataConfig.IsInternalFeed) { // this is all the data that goes into the algorithm allDataForAlgorithm.Add(baseData); } if (kvp.Key.IsDynamicallyLoadedData) { // this is all the custom data custom.Add(kvp); } if (baseData.DataType != MarketDataType.Auxiliary) { // populate ticks and tradebars dictionaries with no aux data if (baseData.DataType == MarketDataType.Tick) { List <Tick> ticksList; if (!ticks.TryGetValue(symbol, out ticksList)) { ticksList = new List <Tick> { (Tick)baseData }; ticks[symbol] = ticksList; } ticksList.Add((Tick)baseData); } else if (baseData.DataType == MarketDataType.TradeBar) { tradeBars[symbol] = (TradeBar)baseData; } // this is data used to update consolidators consolidatorUpdate.Add(baseData); if (update == null) { // this is the data used set market prices update = baseData; } } // include checks for various aux types so we don't have to construct the dictionaries in Slice else if ((delisting = baseData as Delisting) != null) { delistings[symbol] = delisting; } else if ((dividend = baseData as Dividend) != null) { dividends[symbol] = dividend; } else if ((split = baseData as Split) != null) { splits[symbol] = split; } } // check for 'cash securities' if we found valid update data for this symbol // and we need this data to update cash conversion rates, long term we should // have Cash hold onto it's security, then he can update himself, or rather, just // patch through calls to conversion rate to compue it on the fly using Security.Price if (update != null && cashSecurities.Contains(kvp.Key.Symbol)) { foreach (var cashKvp in algorithm.Portfolio.CashBook) { if (cashKvp.Value.SecuritySymbol == kvp.Key.Symbol) { cash.Add(new KeyValuePair <Cash, BaseData>(cashKvp.Value, update)); } } } security.Add(new KeyValuePair <Security, BaseData>(kvp.Key, update)); consolidator.Add(new KeyValuePair <SubscriptionDataConfig, List <BaseData> >(kvp.Key.SubscriptionDataConfig, consolidatorUpdate)); } var slice = new Slice(utcDateTime.ConvertFromUtc(algorithm.TimeZone), allDataForAlgorithm, tradeBars, ticks, splits, dividends, delistings); return(new TimeSlice(utcDateTime, count, slice, data, cash, security, consolidator, custom, changes)); }
/// <summary> /// Ensures that we have a data feed to convert this currency into the base currency. /// This will add a <see cref="SubscriptionDataConfig"/> and create a <see cref="Security"/> at the lowest resolution if one is not found. /// </summary> /// <param name="securities">The security manager</param> /// <param name="subscriptions">The subscription manager used for searching and adding subscriptions</param> /// <param name="marketMap">The market map that decides which market the new security should be in</param> /// <param name="changes">Will be used to consume <see cref="SecurityChanges.AddedSecurities"/></param> /// <param name="securityService">Will be used to create required new <see cref="Security"/></param> /// <param name="accountCurrency">The account currency</param> /// <param name="defaultResolution">The default resolution to use for the internal subscriptions</param> /// <returns>Returns the added <see cref="SubscriptionDataConfig"/>, otherwise null</returns> public SubscriptionDataConfig EnsureCurrencyDataFeed(SecurityManager securities, SubscriptionManager subscriptions, IReadOnlyDictionary <SecurityType, string> marketMap, SecurityChanges changes, ISecurityService securityService, string accountCurrency, Resolution defaultResolution = Resolution.Minute ) { // this gets called every time we add securities using universe selection, // so must of the time we've already resolved the value and don't need to again if (ConversionRateSecurity != null) { return(null); } if (Symbol == accountCurrency) { ConversionRateSecurity = null; _isBaseCurrency = true; ConversionRate = 1.0m; return(null); } // we require a security that converts this into the base currency string normal = Symbol + accountCurrency; string invert = accountCurrency + Symbol; var securitiesToSearch = securities.Select(kvp => kvp.Value) .Concat(changes.AddedSecurities) .Where(s => s.Type == SecurityType.Forex || s.Type == SecurityType.Cfd || s.Type == SecurityType.Crypto); foreach (var security in securitiesToSearch) { if (security.Symbol.Value == normal) { ConversionRateSecurity = security; return(null); } if (security.Symbol.Value == invert) { ConversionRateSecurity = security; _invertRealTimePrice = true; return(null); } } // if we've made it here we didn't find a security, so we'll need to add one // Create a SecurityType to Market mapping with the markets from SecurityManager members var markets = securities.Select(x => x.Key) .GroupBy(x => x.SecurityType) .ToDictionary(x => x.Key, y => y.Select(symbol => symbol.ID.Market).ToHashSet()); if (markets.ContainsKey(SecurityType.Cfd) && !markets.ContainsKey(SecurityType.Forex)) { markets.Add(SecurityType.Forex, markets[SecurityType.Cfd]); } if (markets.ContainsKey(SecurityType.Forex) && !markets.ContainsKey(SecurityType.Cfd)) { markets.Add(SecurityType.Cfd, markets[SecurityType.Forex]); } var forexEntries = GetAvailableSymbolPropertiesDatabaseEntries(SecurityType.Forex, marketMap, markets); var cfdEntries = GetAvailableSymbolPropertiesDatabaseEntries(SecurityType.Cfd, marketMap, markets); var cryptoEntries = GetAvailableSymbolPropertiesDatabaseEntries(SecurityType.Crypto, marketMap, markets); var potentialEntries = forexEntries .Concat(cfdEntries) .Concat(cryptoEntries) .ToList(); if (!potentialEntries.Any(x => Symbol == x.Key.Symbol.Substring(0, x.Key.Symbol.Length - x.Value.QuoteCurrency.Length) || Symbol == x.Value.QuoteCurrency)) { // currency not found in any tradeable pair Log.Error($"No tradeable pair was found for currency {Symbol}, conversion rate to account currency ({accountCurrency}) will be set to zero."); ConversionRateSecurity = null; ConversionRate = 0m; return(null); } var potentials = potentialEntries .Select(x => QuantConnect.Symbol.Create(x.Key.Symbol, x.Key.SecurityType, x.Key.Market)); var minimumResolution = subscriptions.Subscriptions.Select(x => x.Resolution).DefaultIfEmpty(defaultResolution).Min(); foreach (var symbol in potentials) { if (symbol.Value == normal || symbol.Value == invert) { _invertRealTimePrice = symbol.Value == invert; var securityType = symbol.ID.SecurityType; // use the first subscription defined in the subscription manager var type = subscriptions.LookupSubscriptionConfigDataTypes(securityType, minimumResolution, false).First(); var objectType = type.Item1; var tickType = type.Item2; // set this as an internal feed so that the data doesn't get sent into the algorithm's OnData events var config = subscriptions.SubscriptionDataConfigService.Add(symbol, minimumResolution, fillForward: true, extendedMarketHours: false, isInternalFeed: true, subscriptionDataTypes: new List <Tuple <Type, TickType> > { new Tuple <Type, TickType>(objectType, tickType) }).First(); var security = securityService.CreateSecurity(symbol, config, addToSymbolCache: false); ConversionRateSecurity = security; securities.Add(config.Symbol, security); Log.Trace($"Cash.EnsureCurrencyDataFeed(): Adding {symbol.Value} for cash {Symbol} currency feed"); return(config); } } // Special case for crypto markets without direct pairs (They wont be found by the above) // This allows us to add cash for "StableCoins" that are 1-1 with our account currency without needing a conversion security. // Check out the StableCoinsWithoutPairs static var for those that are missing their 1-1 conversion pairs if (Currencies.StableCoinsWithoutPairs.Contains(QuantConnect.Symbol.Create(normal, SecurityType.Crypto, marketMap[SecurityType.Crypto]))) { ConversionRateSecurity = null; ConversionRate = 1.0m; return(null); } // if this still hasn't been set then it's an error condition throw new ArgumentException($"In order to maintain cash in {Symbol} you are required to add a " + $"subscription for Forex pair {Symbol}{accountCurrency} or {accountCurrency}{Symbol}" ); }
/// <summary> /// Checks the current subscriptions and adds necessary currency pair feeds to provide real time conversion data /// </summary> public void EnsureCurrencyDataFeeds(SecurityChanges securityChanges) { _currencySubscriptionDataConfigManager.EnsureCurrencySubscriptionDataConfigs(securityChanges); }
/// <summary> /// Securities changed, detects if we've got new additions to the universe /// so that we don't try to trade every loop /// </summary> public void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges changes) { _hasAdded = changes.AddedSecurities.Count != 0; }
/// <summary> /// Main routine for datafeed analysis. /// </summary> /// <remarks>This is a hot-thread and should be kept extremely lean. Modify with caution.</remarks> public void Run() { var frontier = DateTime.MaxValue; try { // compute initial frontier time frontier = GetInitialFrontierTime(); Log.Trace(string.Format("FileSystemDataFeed.Run(): Begin: {0} UTC", frontier)); // continue to loop over each subscription, enqueuing data in time order while (!_cancellationTokenSource.IsCancellationRequested) { // each time step reset our security changes _changes = SecurityChanges.None; var earlyBirdTicks = long.MaxValue; var data = new List <KeyValuePair <Security, List <BaseData> > >(); // we union subscriptions with itself so if subscriptions changes on the first // iteration we will pick up those changes in the union call, this is used in // universe selection. an alternative is to extract this into a method and check // to see if changes != SecurityChanges.None, and re-run all subscriptions again, // This was added as quick fix due to an issue found in universe selection regression alg foreach (var subscription in Subscriptions.Union(Subscriptions)) { if (subscription.EndOfStream) { // remove finished subscriptions Subscription sub; _subscriptions.TryRemove(new SymbolSecurityType(subscription), out sub); continue; } var cache = new KeyValuePair <Security, List <BaseData> >(subscription.Security, new List <BaseData>()); data.Add(cache); var configuration = subscription.Configuration; var offsetProvider = subscription.OffsetProvider; var currentOffsetTicks = offsetProvider.GetOffsetTicks(frontier); while (subscription.Current.EndTime.Ticks - currentOffsetTicks <= frontier.Ticks) { // we want bars rounded using their subscription times, we make a clone // so we don't interfere with the enumerator's internal logic var clone = subscription.Current.Clone(subscription.Current.IsFillForward); clone.Time = clone.Time.ExchangeRoundDown(configuration.Increment, subscription.Security.Exchange.Hours, configuration.ExtendedMarketHours); cache.Value.Add(clone); if (!subscription.MoveNext()) { Log.Trace("FileSystemDataFeed.Run(): Finished subscription: " + subscription.Security.Symbol + " at " + frontier + " UTC"); break; } } // we have new universe data to select based on if (subscription.IsUniverseSelectionSubscription && cache.Value.Count > 0) { var universe = subscription.Universe; // always wait for other thread if (!Bridge.Wait(Timeout.Infinite, _cancellationTokenSource.Token)) { break; } OnUniverseSelection(universe, frontier, configuration, cache.Value); } if (subscription.Current != null) { // take the earliest between the next piece of data or the next tz discontinuity earlyBirdTicks = Math.Min(earlyBirdTicks, Math.Min(subscription.Current.EndTime.Ticks - currentOffsetTicks, offsetProvider.GetNextDiscontinuity())); } } if (earlyBirdTicks == long.MaxValue) { if (_changes == SecurityChanges.None) { // there's no more data to pull off, we're done break; } } // enqueue our next time slice and set the frontier for the next Bridge.Add(TimeSlice.Create(frontier, _algorithm.TimeZone, _algorithm.Portfolio.CashBook, data, _changes), _cancellationTokenSource.Token); // never go backwards in time, so take the max between early birds and the current frontier frontier = new DateTime(Math.Max(earlyBirdTicks, frontier.Ticks), DateTimeKind.Utc); } if (!_cancellationTokenSource.IsCancellationRequested) { Bridge.CompleteAdding(); } } catch (Exception err) { Log.Error("FileSystemDataFeed.Run(): Encountered an error: " + err.Message); if (!_cancellationTokenSource.IsCancellationRequested) { Bridge.CompleteAdding(); _cancellationTokenSource.Cancel(); } } finally { Log.Trace(string.Format("FileSystemDataFeed.Run(): Data Feed Completed at {0} UTC", frontier)); //Close up all streams: foreach (var subscription in Subscriptions) { subscription.Dispose(); } Log.Trace("FileSystemDataFeed.Run(): Ending Thread... "); IsActive = false; } }
/// <summary> /// Launch the algorithm manager to run this strategy /// </summary> /// <param name="job">Algorithm job</param> /// <param name="algorithm">Algorithm instance</param> /// <param name="synchronizer">Instance which implements <see cref="ISynchronizer"/>. Used to stream the data</param> /// <param name="transactions">Transaction manager object</param> /// <param name="results">Result handler object</param> /// <param name="realtime">Realtime processing object</param> /// <param name="leanManager">ILeanManager implementation that is updated periodically with the IAlgorithm instance</param> /// <param name="alphas">Alpha handler used to process algorithm generated insights</param> /// <param name="token">Cancellation token</param> /// <remarks>Modify with caution</remarks> public void Run(AlgorithmNodePacket job, IAlgorithm algorithm, ISynchronizer synchronizer, ITransactionHandler transactions, IResultHandler results, IRealTimeHandler realtime, ILeanManager leanManager, IAlphaHandler alphas, CancellationToken token) { //Initialize: DataPoints = 0; _algorithm = algorithm; var backtestMode = (job.Type == PacketType.BacktestNode); var methodInvokers = new Dictionary <Type, MethodInvoker>(); var marginCallFrequency = TimeSpan.FromMinutes(5); var nextMarginCallTime = DateTime.MinValue; var settlementScanFrequency = TimeSpan.FromMinutes(30); var nextSettlementScanTime = DateTime.MinValue; var time = algorithm.StartDate.Date; var pendingDelistings = new List <Delisting>(); var splitWarnings = new List <Split>(); //Initialize Properties: AlgorithmId = job.AlgorithmId; //Create the method accessors to push generic types into algorithm: Find all OnData events: // Algorithm 2.0 data accessors var hasOnDataTradeBars = AddMethodInvoker <TradeBars>(algorithm, methodInvokers); var hasOnDataQuoteBars = AddMethodInvoker <QuoteBars>(algorithm, methodInvokers); var hasOnDataOptionChains = AddMethodInvoker <OptionChains>(algorithm, methodInvokers); var hasOnDataTicks = AddMethodInvoker <Ticks>(algorithm, methodInvokers); // dividend and split events var hasOnDataDividends = AddMethodInvoker <Dividends>(algorithm, methodInvokers); var hasOnDataSplits = AddMethodInvoker <Splits>(algorithm, methodInvokers); var hasOnDataDelistings = AddMethodInvoker <Delistings>(algorithm, methodInvokers); var hasOnDataSymbolChangedEvents = AddMethodInvoker <SymbolChangedEvents>(algorithm, methodInvokers); //Go through the subscription types and create invokers to trigger the event handlers for each custom type: foreach (var config in algorithm.SubscriptionManager.Subscriptions) { //If type is a custom feed, check for a dedicated event handler if (config.IsCustomData) { //Get the matching method for this event handler - e.g. public void OnData(Quandl data) { .. } var genericMethod = (algorithm.GetType()).GetMethod("OnData", new[] { config.Type }); //If we already have this Type-handler then don't add it to invokers again. if (methodInvokers.ContainsKey(config.Type)) { continue; } if (genericMethod != null) { methodInvokers.Add(config.Type, genericMethod.DelegateForCallMethod()); } } } // Schedule a daily event for sampling at midnight every night algorithm.Schedule.On("Daily Sampling", algorithm.Schedule.DateRules.EveryDay(), algorithm.Schedule.TimeRules.Midnight, () => { results.Sample(algorithm.UtcTime); }); //Loop over the queues: get a data collection, then pass them all into relevent methods in the algorithm. Log.Trace($"AlgorithmManager.Run(): Begin DataStream - Start: {algorithm.StartDate} Stop: {algorithm.EndDate} Time: {algorithm.Time} Warmup: {algorithm.IsWarmingUp}"); foreach (var timeSlice in Stream(algorithm, synchronizer, results, token)) { // reset our timer on each loop TimeLimit.StartNewTimeStep(); //Check this backtest is still running: if (_algorithm.Status != AlgorithmStatus.Running && _algorithm.RunTimeError == null) { Log.Error($"AlgorithmManager.Run(): Algorithm state changed to {_algorithm.Status} at {timeSlice.Time.ToStringInvariant()}"); break; } //Execute with TimeLimit Monitor: if (token.IsCancellationRequested) { Log.Error($"AlgorithmManager.Run(): CancellationRequestion at {timeSlice.Time.ToStringInvariant()}"); return; } // Update the ILeanManager leanManager.Update(); time = timeSlice.Time; DataPoints += timeSlice.DataPointCount; if (backtestMode && algorithm.Portfolio.TotalPortfolioValue <= 0) { var logMessage = "AlgorithmManager.Run(): Portfolio value is less than or equal to zero, stopping algorithm."; Log.Error(logMessage); results.SystemDebugMessage(logMessage); break; } // If backtesting/warmup, we need to check if there are realtime events in the past // which didn't fire because at the scheduled times there was no data (i.e. markets closed) // and fire them with the correct date/time. realtime.ScanPastEvents(time); //Set the algorithm and real time handler's time algorithm.SetDateTime(time); // the time pulse are just to advance algorithm time, lets shortcut the loop here if (timeSlice.IsTimePulse) { continue; } // Update the current slice before firing scheduled events or any other task algorithm.SetCurrentSlice(timeSlice.Slice); if (timeSlice.Slice.SymbolChangedEvents.Count != 0) { if (hasOnDataSymbolChangedEvents) { methodInvokers[typeof(SymbolChangedEvents)](algorithm, timeSlice.Slice.SymbolChangedEvents); } foreach (var symbol in timeSlice.Slice.SymbolChangedEvents.Keys) { // cancel all orders for the old symbol foreach (var ticket in transactions.GetOpenOrderTickets(x => x.Symbol == symbol)) { ticket.Cancel("Open order cancelled on symbol changed event"); } } } if (timeSlice.SecurityChanges != SecurityChanges.None) { foreach (var security in timeSlice.SecurityChanges.AddedSecurities) { security.IsTradable = true; // uses TryAdd, so don't need to worry about duplicates here algorithm.Securities.Add(security); } var activeSecurities = algorithm.UniverseManager.ActiveSecurities; foreach (var security in timeSlice.SecurityChanges.RemovedSecurities) { if (!activeSecurities.ContainsKey(security.Symbol)) { security.IsTradable = false; } } leanManager.OnSecuritiesChanged(timeSlice.SecurityChanges); realtime.OnSecuritiesChanged(timeSlice.SecurityChanges); results.OnSecuritiesChanged(timeSlice.SecurityChanges); } //Update the securities properties: first before calling user code to avoid issues with data foreach (var update in timeSlice.SecuritiesUpdateData) { var security = update.Target; security.Update(update.Data, update.DataType, update.ContainsFillForwardData); if (!update.IsInternalConfig) { // Send market price updates to the TradeBuilder algorithm.TradeBuilder.SetMarketPrice(security.Symbol, security.Price); } } //Update the securities properties with any universe data if (timeSlice.UniverseData.Count > 0) { foreach (var kvp in timeSlice.UniverseData) { foreach (var data in kvp.Value.Data) { Security security; if (algorithm.Securities.TryGetValue(data.Symbol, out security)) { security.Cache.StoreData(new[] { data }, data.GetType()); } } } } // poke each cash object to update from the recent security data foreach (var cash in algorithm.Portfolio.CashBook.Values.Where(x => x.CurrencyConversion != null)) { cash.Update(); } // security prices got updated algorithm.Portfolio.InvalidateTotalPortfolioValue(); // process fill models on the updated data before entering algorithm, applies to all non-market orders transactions.ProcessSynchronousEvents(); // fire real time events after we've updated based on the new data realtime.SetTime(timeSlice.Time); // process split warnings for options ProcessSplitSymbols(algorithm, splitWarnings, pendingDelistings); //Check if the user's signalled Quit: loop over data until day changes. if (_algorithm.Status != AlgorithmStatus.Running && _algorithm.RunTimeError == null) { Log.Error($"AlgorithmManager.Run(): Algorithm state changed to {_algorithm.Status} at {timeSlice.Time.ToStringInvariant()}"); break; } if (algorithm.RunTimeError != null) { Log.Error($"AlgorithmManager.Run(): Stopping, encountered a runtime error at {algorithm.UtcTime} UTC."); return; } // perform margin calls, in live mode we can also use realtime to emit these if (time >= nextMarginCallTime || (_liveMode && nextMarginCallTime > DateTime.UtcNow)) { // determine if there are possible margin call orders to be executed bool issueMarginCallWarning; var marginCallOrders = algorithm.Portfolio.MarginCallModel.GetMarginCallOrders(out issueMarginCallWarning); if (marginCallOrders.Count != 0) { var executingMarginCall = false; try { // tell the algorithm we're about to issue the margin call algorithm.OnMarginCall(marginCallOrders); executingMarginCall = true; // execute the margin call orders var executedTickets = algorithm.Portfolio.MarginCallModel.ExecuteMarginCall(marginCallOrders); foreach (var ticket in executedTickets) { algorithm.Error($"{algorithm.Time.ToStringInvariant()} - Executed MarginCallOrder: {ticket.Symbol} - " + $"Quantity: {ticket.Quantity.ToStringInvariant()} @ {ticket.AverageFillPrice.ToStringInvariant()}" ); } } catch (Exception err) { algorithm.SetRuntimeError(err, executingMarginCall ? "Portfolio.MarginCallModel.ExecuteMarginCall" : "OnMarginCall"); return; } } // we didn't perform a margin call, but got the warning flag back, so issue the warning to the algorithm else if (issueMarginCallWarning) { try { algorithm.OnMarginCallWarning(); } catch (Exception err) { algorithm.SetRuntimeError(err, "OnMarginCallWarning"); return; } } nextMarginCallTime = time + marginCallFrequency; } // perform check for settlement of unsettled funds if (time >= nextSettlementScanTime || (_liveMode && nextSettlementScanTime > DateTime.UtcNow)) { algorithm.Portfolio.ScanForCashSettlement(algorithm.UtcTime); nextSettlementScanTime = time + settlementScanFrequency; } // before we call any events, let the algorithm know about universe changes if (timeSlice.SecurityChanges != SecurityChanges.None) { try { var algorithmSecurityChanges = new SecurityChanges(timeSlice.SecurityChanges) { // by default for user code we want to filter out custom securities FilterCustomSecurities = true, // by default for user code we want to filter out internal securities FilterInternalSecurities = true }; algorithm.OnSecuritiesChanged(algorithmSecurityChanges); algorithm.OnFrameworkSecuritiesChanged(algorithmSecurityChanges); } catch (Exception err) { algorithm.SetRuntimeError(err, "OnSecuritiesChanged"); return; } } // apply dividends foreach (var dividend in timeSlice.Slice.Dividends.Values) { Log.Debug($"AlgorithmManager.Run(): {algorithm.Time}: Applying Dividend: {dividend}"); Security security = null; if (_liveMode && algorithm.Securities.TryGetValue(dividend.Symbol, out security)) { Log.Trace($"AlgorithmManager.Run(): {algorithm.Time}: Pre-Dividend: {dividend}. " + $"Security Holdings: {security.Holdings.Quantity} Account Currency Holdings: " + $"{algorithm.Portfolio.CashBook[algorithm.AccountCurrency].Amount}"); } var mode = algorithm.SubscriptionManager.SubscriptionDataConfigService .GetSubscriptionDataConfigs(dividend.Symbol) .DataNormalizationMode(); // apply the dividend event to the portfolio algorithm.Portfolio.ApplyDividend(dividend, _liveMode, mode); if (_liveMode && security != null) { Log.Trace($"AlgorithmManager.Run(): {algorithm.Time}: Post-Dividend: {dividend}. Security " + $"Holdings: {security.Holdings.Quantity} Account Currency Holdings: " + $"{algorithm.Portfolio.CashBook[algorithm.AccountCurrency].Amount}"); } } // apply splits foreach (var split in timeSlice.Slice.Splits.Values) { try { // only process split occurred events (ignore warnings) if (split.Type != SplitType.SplitOccurred) { continue; } Log.Debug($"AlgorithmManager.Run(): {algorithm.Time}: Applying Split for {split.Symbol}"); Security security = null; if (_liveMode && algorithm.Securities.TryGetValue(split.Symbol, out security)) { Log.Trace($"AlgorithmManager.Run(): {algorithm.Time}: Pre-Split for {split}. Security Price: {security.Price} Holdings: {security.Holdings.Quantity}"); } var mode = algorithm.SubscriptionManager.SubscriptionDataConfigService .GetSubscriptionDataConfigs(split.Symbol) .DataNormalizationMode(); // apply the split event to the portfolio algorithm.Portfolio.ApplySplit(split, _liveMode, mode); if (_liveMode && security != null) { Log.Trace($"AlgorithmManager.Run(): {algorithm.Time}: Post-Split for {split}. Security Price: {security.Price} Holdings: {security.Holdings.Quantity}"); } // apply the split to open orders as well in raw mode, all other modes are split adjusted if (_liveMode || mode == DataNormalizationMode.Raw) { // in live mode we always want to have our order match the order at the brokerage, so apply the split to the orders var openOrders = transactions.GetOpenOrderTickets(ticket => ticket.Symbol == split.Symbol); algorithm.BrokerageModel.ApplySplit(openOrders.ToList(), split); } } catch (Exception err) { algorithm.SetRuntimeError(err, "Split event"); return; } } //Update registered consolidators for this symbol index try { if (timeSlice.ConsolidatorUpdateData.Count > 0) { var timeKeeper = algorithm.TimeKeeper; foreach (var update in timeSlice.ConsolidatorUpdateData) { var localTime = timeKeeper.GetLocalTimeKeeper(update.Target.ExchangeTimeZone).LocalTime; var consolidators = update.Target.Consolidators; foreach (var consolidator in consolidators) { foreach (var dataPoint in update.Data) { // only push data into consolidators on the native, subscribed to resolution if (EndTimeIsInNativeResolution(update.Target, dataPoint.EndTime)) { consolidator.Update(dataPoint); } } // scan for time after we've pumped all the data through for this consolidator consolidator.Scan(localTime); } } } } catch (Exception err) { algorithm.SetRuntimeError(err, "Consolidators update"); return; } // fire custom event handlers foreach (var update in timeSlice.CustomData) { MethodInvoker methodInvoker; if (!methodInvokers.TryGetValue(update.DataType, out methodInvoker)) { continue; } try { foreach (var dataPoint in update.Data) { if (update.DataType.IsInstanceOfType(dataPoint)) { methodInvoker(algorithm, dataPoint); } } } catch (Exception err) { algorithm.SetRuntimeError(err, "Custom Data"); return; } } try { // fire off the dividend and split events before pricing events if (hasOnDataDividends && timeSlice.Slice.Dividends.Count != 0) { methodInvokers[typeof(Dividends)](algorithm, timeSlice.Slice.Dividends); } if (hasOnDataSplits && timeSlice.Slice.Splits.Count != 0) { methodInvokers[typeof(Splits)](algorithm, timeSlice.Slice.Splits); } if (hasOnDataDelistings && timeSlice.Slice.Delistings.Count != 0) { methodInvokers[typeof(Delistings)](algorithm, timeSlice.Slice.Delistings); } } catch (Exception err) { algorithm.SetRuntimeError(err, "Dividends/Splits/Delistings"); return; } // Only track pending delistings in non-live mode. if (!algorithm.LiveMode) { // Keep this up to date even though we don't process delistings here anymore foreach (var delisting in timeSlice.Slice.Delistings.Values) { if (delisting.Type == DelistingType.Warning) { // Store our delistings warnings because they are still used by ProcessSplitSymbols above pendingDelistings.Add(delisting); } else { // If we have an actual delisting event, remove it from pending delistings var index = pendingDelistings.FindIndex(x => x.Symbol == delisting.Symbol); if (index != -1) { pendingDelistings.RemoveAt(index); } } } } // run split logic after firing split events HandleSplitSymbols(timeSlice.Slice.Splits, splitWarnings); //After we've fired all other events in this second, fire the pricing events: try { if (hasOnDataTradeBars && timeSlice.Slice.Bars.Count > 0) { methodInvokers[typeof(TradeBars)](algorithm, timeSlice.Slice.Bars); } if (hasOnDataQuoteBars && timeSlice.Slice.QuoteBars.Count > 0) { methodInvokers[typeof(QuoteBars)](algorithm, timeSlice.Slice.QuoteBars); } if (hasOnDataOptionChains && timeSlice.Slice.OptionChains.Count > 0) { methodInvokers[typeof(OptionChains)](algorithm, timeSlice.Slice.OptionChains); } if (hasOnDataTicks && timeSlice.Slice.Ticks.Count > 0) { methodInvokers[typeof(Ticks)](algorithm, timeSlice.Slice.Ticks); } } catch (Exception err) { algorithm.SetRuntimeError(err, "methodInvokers"); return; } try { if (timeSlice.Slice.HasData) { // EVENT HANDLER v3.0 -- all data in a single event algorithm.OnData(timeSlice.Slice); } // always turn the crank on this method to ensure universe selection models function properly on day changes w/out data algorithm.OnFrameworkData(timeSlice.Slice); } catch (Exception err) { algorithm.SetRuntimeError(err, "OnData"); return; } //If its the historical/paper trading models, wait until market orders have been "filled" // Manually trigger the event handler to prevent thread switch. transactions.ProcessSynchronousEvents(); // sample alpha charts now that we've updated time/price information and after transactions // are processed so that insights closed because of new order based insights get updated alphas.ProcessSynchronousEvents(); // send the alpha statistics to the result handler for storage/transmit with the result packets results.SetAlphaRuntimeStatistics(alphas.RuntimeStatistics); // Process any required events of the results handler such as sampling assets, equity, or stock prices. results.ProcessSynchronousEvents(); // poke the algorithm at the end of each time step algorithm.OnEndOfTimeStep(); } // End of ForEach feed.Bridge.GetConsumingEnumerable // stop timing the loops TimeLimit.StopEnforcingTimeLimit(); //Stream over:: Send the final packet and fire final events: Log.Trace("AlgorithmManager.Run(): Firing On End Of Algorithm..."); try { algorithm.OnEndOfAlgorithm(); } catch (Exception err) { algorithm.SetRuntimeError(err, "OnEndOfAlgorithm"); return; } // final processing now that the algorithm has completed alphas.ProcessSynchronousEvents(); // send the final alpha statistics to the result handler for storage/transmit with the result packets results.SetAlphaRuntimeStatistics(alphas.RuntimeStatistics); // Process any required events of the results handler such as sampling assets, equity, or stock prices. results.ProcessSynchronousEvents(forceProcess: true); //Liquidate Holdings for Calculations: if (_algorithm.Status == AlgorithmStatus.Liquidated && _liveMode) { Log.Trace("AlgorithmManager.Run(): Liquidating algorithm holdings..."); algorithm.Liquidate(); results.LogMessage("Algorithm Liquidated"); results.SendStatusUpdate(AlgorithmStatus.Liquidated); } //Manually stopped the algorithm if (_algorithm.Status == AlgorithmStatus.Stopped) { Log.Trace("AlgorithmManager.Run(): Stopping algorithm..."); results.LogMessage("Algorithm Stopped"); results.SendStatusUpdate(AlgorithmStatus.Stopped); } //Backtest deleted. if (_algorithm.Status == AlgorithmStatus.Deleted) { Log.Trace("AlgorithmManager.Run(): Deleting algorithm..."); results.DebugMessage("Algorithm Id:(" + job.AlgorithmId + ") Deleted by request."); results.SendStatusUpdate(AlgorithmStatus.Deleted); } //Algorithm finished, send regardless of commands: results.SendStatusUpdate(AlgorithmStatus.Completed); SetStatus(AlgorithmStatus.Completed); //Take final samples: results.Sample(time); } // End of Run();
public override void OnSecuritiesChanged(QCAlgorithm algorithm, SecurityChanges changes) { }
/// <summary> /// Creates a new <see cref="TimeSlice"/> for the specified time using the specified data /// </summary> /// <param name="utcDateTime">The UTC frontier date time</param> /// <param name="algorithmTimeZone">The algorithm's time zone, required for computing algorithm and slice time</param> /// <param name="cashBook">The algorithm's cash book, required for generating cash update pairs</param> /// <param name="data">The data in this <see cref="TimeSlice"/></param> /// <param name="changes">The new changes that are seen in this time slice as a result of universe selection</param> /// <returns>A new <see cref="TimeSlice"/> containing the specified data</returns> public static TimeSlice Create(DateTime utcDateTime, DateTimeZone algorithmTimeZone, CashBook cashBook, List <DataFeedPacket> data, SecurityChanges changes) { int count = 0; var security = new List <KeyValuePair <Security, BaseData> >(); var custom = new List <KeyValuePair <Security, IEnumerable <BaseData> > >(); var consolidator = new List <KeyValuePair <SubscriptionDataConfig, List <BaseData> > >(); var allDataForAlgorithm = new List <BaseData>(data.Count); var cash = new List <KeyValuePair <Cash, BaseData> >(cashBook.Count); var cashSecurities = new HashSet <Symbol>(); foreach (var cashItem in cashBook.Values) { cashSecurities.Add(cashItem.SecuritySymbol); } Split split; Dividend dividend; Delisting delisting; SymbolChangedEvent symbolChange; var algorithmTime = utcDateTime.ConvertFromUtc(algorithmTimeZone); var tradeBars = new TradeBars(algorithmTime); var ticks = new Ticks(algorithmTime); var splits = new Splits(algorithmTime); var dividends = new Dividends(algorithmTime); var delistings = new Delistings(algorithmTime); var symbolChanges = new SymbolChangedEvents(algorithmTime); foreach (var packet in data) { var list = packet.Data; var symbol = packet.Security.Symbol; // keep count of all data points if (list.Count == 1 && list[0] is BaseDataCollection) { count += ((BaseDataCollection)list[0]).Data.Count; } else { count += list.Count; } BaseData update = null; var consolidatorUpdate = new List <BaseData>(list.Count); for (int i = 0; i < list.Count; i++) { var baseData = list[i]; if (!packet.Security.SubscriptionDataConfig.IsInternalFeed) { // this is all the data that goes into the algorithm allDataForAlgorithm.Add(baseData); if (packet.Security.SubscriptionDataConfig.IsCustomData) { // this is all the custom data custom.Add(new KeyValuePair <Security, IEnumerable <BaseData> >(packet.Security, list)); } } // don't add internal feed data to ticks/bars objects if (baseData.DataType != MarketDataType.Auxiliary) { if (!packet.Security.SubscriptionDataConfig.IsInternalFeed) { PopulateDataDictionaries(baseData, ticks, tradeBars); // this is data used to update consolidators consolidatorUpdate.Add(baseData); } // this is the data used set market prices update = baseData; } // include checks for various aux types so we don't have to construct the dictionaries in Slice else if ((delisting = baseData as Delisting) != null) { delistings[symbol] = delisting; } else if ((dividend = baseData as Dividend) != null) { dividends[symbol] = dividend; } else if ((split = baseData as Split) != null) { splits[symbol] = split; } else if ((symbolChange = baseData as SymbolChangedEvent) != null) { // symbol changes is keyed by the requested symbol symbolChanges[packet.Security.SubscriptionDataConfig.Symbol] = symbolChange; } } // check for 'cash securities' if we found valid update data for this symbol // and we need this data to update cash conversion rates, long term we should // have Cash hold onto it's security, then he can update himself, or rather, just // patch through calls to conversion rate to compue it on the fly using Security.Price if (update != null && cashSecurities.Contains(packet.Security.Symbol)) { foreach (var cashKvp in cashBook) { if (cashKvp.Value.SecuritySymbol == packet.Security.Symbol) { cash.Add(new KeyValuePair <Cash, BaseData>(cashKvp.Value, update)); } } } security.Add(new KeyValuePair <Security, BaseData>(packet.Security, update)); consolidator.Add(new KeyValuePair <SubscriptionDataConfig, List <BaseData> >(packet.Security.SubscriptionDataConfig, consolidatorUpdate)); } var slice = new Slice(algorithmTime, allDataForAlgorithm, tradeBars, ticks, splits, dividends, delistings, symbolChanges, allDataForAlgorithm.Count > 0); return(new TimeSlice(utcDateTime, count, slice, data, cash, security, consolidator, custom, changes)); }
/// <summary> /// Creates a new <see cref="TimeSlice"/> for the specified time using the specified data /// </summary> /// <param name="utcDateTime">The UTC frontier date time</param> /// <param name="algorithmTimeZone">The algorithm's time zone, required for computing algorithm and slice time</param> /// <param name="cashBook">The algorithm's cash book, required for generating cash update pairs</param> /// <param name="data">The data in this <see cref="TimeSlice"/></param> /// <param name="changes">The new changes that are seen in this time slice as a result of universe selection</param> /// <returns>A new <see cref="TimeSlice"/> containing the specified data</returns> public static TimeSlice Create(DateTime utcDateTime, DateTimeZone algorithmTimeZone, CashBook cashBook, List <DataFeedPacket> data, SecurityChanges changes) { int count = 0; var security = new List <UpdateData <Security> >(); var custom = new List <UpdateData <Security> >(); var consolidator = new List <UpdateData <SubscriptionDataConfig> >(); var allDataForAlgorithm = new List <BaseData>(data.Count); var optionUnderlyingUpdates = new Dictionary <Symbol, BaseData>(); Split split; Dividend dividend; Delisting delisting; SymbolChangedEvent symbolChange; // we need to be able to reference the slice being created in order to define the // evaluation of option price models, so we define a 'future' that can be referenced // in the option price model evaluation delegates for each contract Slice slice = null; var sliceFuture = new Lazy <Slice>(() => slice); var algorithmTime = utcDateTime.ConvertFromUtc(algorithmTimeZone); var tradeBars = new TradeBars(algorithmTime); var quoteBars = new QuoteBars(algorithmTime); var ticks = new Ticks(algorithmTime); var splits = new Splits(algorithmTime); var dividends = new Dividends(algorithmTime); var delistings = new Delistings(algorithmTime); var optionChains = new OptionChains(algorithmTime); var futuresChains = new FuturesChains(algorithmTime); var symbolChanges = new SymbolChangedEvents(algorithmTime); // ensure we read equity data before option data, so we can set the current underlying price foreach (var packet in data) { var list = packet.Data; var symbol = packet.Security.Symbol; if (list.Count == 0) { continue; } // keep count of all data points if (list.Count == 1 && list[0] is BaseDataCollection) { var baseDataCollectionCount = ((BaseDataCollection)list[0]).Data.Count; if (baseDataCollectionCount == 0) { continue; } count += baseDataCollectionCount; } else { count += list.Count; } if (!packet.Configuration.IsInternalFeed && packet.Configuration.IsCustomData) { // This is all the custom data custom.Add(new UpdateData <Security>(packet.Security, packet.Configuration.Type, list)); } var securityUpdate = new List <BaseData>(list.Count); var consolidatorUpdate = new List <BaseData>(list.Count); for (int i = 0; i < list.Count; i++) { var baseData = list[i]; if (!packet.Configuration.IsInternalFeed) { // this is all the data that goes into the algorithm allDataForAlgorithm.Add(baseData); } // don't add internal feed data to ticks/bars objects if (baseData.DataType != MarketDataType.Auxiliary) { if (!packet.Configuration.IsInternalFeed) { PopulateDataDictionaries(baseData, ticks, tradeBars, quoteBars, optionChains, futuresChains); // special handling of options data to build the option chain if (packet.Security.Type == SecurityType.Option) { if (baseData.DataType == MarketDataType.OptionChain) { optionChains[baseData.Symbol] = (OptionChain)baseData; } else if (!HandleOptionData(algorithmTime, baseData, optionChains, packet.Security, sliceFuture, optionUnderlyingUpdates)) { continue; } } // special handling of futures data to build the futures chain if (packet.Security.Type == SecurityType.Future) { if (baseData.DataType == MarketDataType.FuturesChain) { futuresChains[baseData.Symbol] = (FuturesChain)baseData; } else if (!HandleFuturesData(algorithmTime, baseData, futuresChains, packet.Security)) { continue; } } // this is data used to update consolidators consolidatorUpdate.Add(baseData); } // this is the data used set market prices // do not add it if it is a Suspicious tick var tick = baseData as Tick; if (tick != null && tick.Suspicious) { continue; } securityUpdate.Add(baseData); // option underlying security update if (packet.Security.Symbol.SecurityType == SecurityType.Equity) { optionUnderlyingUpdates[packet.Security.Symbol] = baseData; } } // include checks for various aux types so we don't have to construct the dictionaries in Slice else if ((delisting = baseData as Delisting) != null) { delistings[symbol] = delisting; } else if ((dividend = baseData as Dividend) != null) { dividends[symbol] = dividend; } else if ((split = baseData as Split) != null) { splits[symbol] = split; } else if ((symbolChange = baseData as SymbolChangedEvent) != null) { // symbol changes is keyed by the requested symbol symbolChanges[packet.Configuration.Symbol] = symbolChange; } } if (securityUpdate.Count > 0) { security.Add(new UpdateData <Security>(packet.Security, packet.Configuration.Type, securityUpdate)); } if (consolidatorUpdate.Count > 0) { consolidator.Add(new UpdateData <SubscriptionDataConfig>(packet.Configuration, packet.Configuration.Type, consolidatorUpdate)); } } slice = new Slice(algorithmTime, allDataForAlgorithm, tradeBars, quoteBars, ticks, optionChains, futuresChains, splits, dividends, delistings, symbolChanges, allDataForAlgorithm.Count > 0); return(new TimeSlice(utcDateTime, count, slice, data, security, consolidator, custom, changes)); }
public void OrdersAreSubmittedWhenRequiredForTargetsToExecute( Language language, double[] historicalPrices, decimal lastVolume, int expectedOrdersSubmitted, decimal expectedTotalQuantity) { var actualOrdersSubmitted = new List <SubmitOrderRequest>(); var time = new DateTime(2018, 8, 2, 16, 0, 0); var historyProvider = new Mock <IHistoryProvider>(); historyProvider.Setup(m => m.GetHistory(It.IsAny <IEnumerable <HistoryRequest> >(), It.IsAny <DateTimeZone>())) .Returns(historicalPrices.Select((x, i) => new Slice(time.AddMinutes(i), new List <BaseData> { new TradeBar { Time = time.AddMinutes(i), Symbol = Symbols.AAPL, Open = Convert.ToDecimal(x), High = Convert.ToDecimal(x), Low = Convert.ToDecimal(x), Close = Convert.ToDecimal(x), Volume = 100m } }))); var algorithm = new QCAlgorithm(); algorithm.SubscriptionManager.SetDataManager(new DataManagerStub(algorithm)); algorithm.SetPandasConverter(); algorithm.SetHistoryProvider(historyProvider.Object); algorithm.SetDateTime(time.AddMinutes(5)); var security = algorithm.AddEquity(Symbols.AAPL.Value); security.SetMarketPrice(new TradeBar { Value = 250, Volume = lastVolume }); algorithm.SetFinishedWarmingUp(); var orderProcessor = new Mock <IOrderProcessor>(); orderProcessor.Setup(m => m.Process(It.IsAny <SubmitOrderRequest>())) .Returns((SubmitOrderRequest request) => new OrderTicket(algorithm.Transactions, request)) .Callback((SubmitOrderRequest request) => actualOrdersSubmitted.Add(request)); orderProcessor.Setup(m => m.GetOpenOrders(It.IsAny <Func <Order, bool> >())) .Returns(new List <Order>()); algorithm.Transactions.SetOrderProcessor(orderProcessor.Object); var model = GetExecutionModel(language); algorithm.SetExecution(model); var changes = new SecurityChanges(new[] { security }, Enumerable.Empty <Security>()); model.OnSecuritiesChanged(algorithm, changes); algorithm.History(new List <Symbol> { security.Symbol }, historicalPrices.Length, Resolution.Minute) .PushThroughConsolidators(symbol => algorithm.Securities[symbol].Subscriptions.Single(s => s.TickType == LeanData.GetCommonTickType(SecurityType.Equity)).Consolidators.First()); var targets = new IPortfolioTarget[] { new PortfolioTarget(security.Symbol, 10) }; model.Execute(algorithm, targets); Assert.AreEqual(expectedOrdersSubmitted, actualOrdersSubmitted.Count); Assert.AreEqual(expectedTotalQuantity, actualOrdersSubmitted.Sum(x => x.Quantity)); if (actualOrdersSubmitted.Count == 1) { var request = actualOrdersSubmitted[0]; Assert.AreEqual(expectedTotalQuantity, request.Quantity); Assert.AreEqual(algorithm.UtcTime, request.Time); } }
/// <summary> /// Event fired each time the we add/remove securities from the data feed /// </summary> /// <param name="algorithm">The algorithm instance that experienced the change in securities</param> /// <param name="changes">The security additions and removals from the algorithm</param> public override void OnSecuritiesChanged(QCAlgorithmFramework algorithm, SecurityChanges changes) { // Get removed symbol and invalidate them in the insight collection _removedSymbols = changes.RemovedSecurities.Select(x => x.Symbol).ToList(); _insightCollection.Clear(_removedSymbols.ToArray()); }
/// <summary> /// Event fired each time the we add/remove securities from the data feed /// </summary> /// <param name="changes"></param> public override void OnSecuritiesChanged(SecurityChanges changes) { // each time our securities change we'll be notified here _changes = changes; }
/// <summary> /// Checks the current subscriptions and adds necessary currency pair feeds to provide real time conversion data /// </summary> public void EnsureCurrencyDataFeeds(SecurityChanges securityChanges) { _currencySubscriptionDataConfigManager.EnsureCurrencySubscriptionDataConfigs(securityChanges, _algorithm.BrokerageModel); }
/// <summary> /// Primary entry point. /// </summary> public void Run() { IsActive = true; // we want to emit to the bridge minimally once a second since the data feed is // the heartbeat of the application, so this value will contain a second after // the last emit time, and if we pass this time, we'll emit even with no data var nextEmit = DateTime.MinValue; try { while (!_cancellationTokenSource.IsCancellationRequested) { // perform sleeps to wake up on the second? _frontierUtc = _timeProvider.GetUtcNow(); _frontierTimeProvider.SetCurrentTime(_frontierUtc); var data = new List <KeyValuePair <Security, List <BaseData> > >(); foreach (var kvp in _subscriptions) { var subscription = kvp.Value; var cache = new KeyValuePair <Security, List <BaseData> >(subscription.Security, new List <BaseData>()); // dequeue data that is time stamped at or before this frontier while (subscription.MoveNext() && subscription.Current != null) { cache.Value.Add(subscription.Current); } // if we have data, add it to be added to the bridge if (cache.Value.Count > 0) { data.Add(cache); } // we have new universe data to select based on if (subscription.IsUniverseSelectionSubscription && cache.Value.Count > 0) { var universe = subscription.Universe; // always wait for other thread to sync up if (!_bridge.WaitHandle.WaitOne(Timeout.Infinite, _cancellationTokenSource.Token)) { break; } // assume that if the first item is a base data collection then the enumerator handled the aggregation, // otherwise, load all the the data into a new collection instance var collection = cache.Value[0] as BaseDataCollection ?? new BaseDataCollection(_frontierUtc, subscription.Configuration.Symbol, cache.Value); _changes += _universeSelection.ApplyUniverseSelection(universe, _frontierUtc, collection); } } // check for cancellation if (_cancellationTokenSource.IsCancellationRequested) { return; } // emit on data or if we've elapsed a full second since last emit if (data.Count != 0 || _frontierUtc >= nextEmit) { _bridge.Add(TimeSlice.Create(_frontierUtc, _algorithm.TimeZone, _algorithm.Portfolio.CashBook, data, _changes), _cancellationTokenSource.Token); // force emitting every second nextEmit = _frontierUtc.RoundDown(Time.OneSecond).Add(Time.OneSecond); } // reset our security changes _changes = SecurityChanges.None; // take a short nap Thread.Sleep(1); } } catch (Exception err) { Log.Error(err); _algorithm.RunTimeError = err; } Log.Trace("LiveTradingDataFeed.Run(): Exited thread."); IsActive = false; }
public void AddAlphaModel(Language language) { IAlphaModel model; IAlphaModel model2 = null; IAlphaModel model3 = null; if (!TryCreateModel(language, out model) || !TryCreateModel(language, out model2) || !TryCreateModel(language, out model3)) { Assert.Ignore($"Ignore {GetType().Name}: Could not create {language} model."); } // Set the alpha model _algorithm.SetAlpha(model); _algorithm.AddAlpha(model2); _algorithm.AddAlpha(model3); _algorithm.SetUniverseSelection(new ManualUniverseSelectionModel()); var changes = new SecurityChanges(AddedSecurities, RemovedSecurities); _algorithm.OnFrameworkSecuritiesChanged(changes); var actualInsights = new List <Insight>(); _algorithm.InsightsGenerated += (s, e) => actualInsights.AddRange(e.Insights); var expectedInsights = ExpectedInsights().ToList(); var consolidators = _algorithm.Securities.SelectMany(kvp => kvp.Value.Subscriptions).SelectMany(x => x.Consolidators); var slices = CreateSlices(); foreach (var slice in slices.ToList()) { _algorithm.SetDateTime(slice.Time); foreach (var symbol in slice.Keys) { var data = slice[symbol]; _algorithm.Securities[symbol].SetMarketPrice(data); foreach (var consolidator in consolidators) { consolidator.Update(data); } } _algorithm.OnFrameworkData(slice); } Assert.AreEqual(expectedInsights.Count * 3, actualInsights.Count); for (var i = 0; i < actualInsights.Count; i = i + 3) { var expected = expectedInsights[i / 3]; for (int j = i; j < 3; j++) { var actual = actualInsights[j]; Assert.AreEqual(expected.Symbol, actual.Symbol); Assert.AreEqual(expected.Type, actual.Type); Assert.AreEqual(expected.Direction, actual.Direction); Assert.AreEqual(expected.Period, actual.Period); Assert.AreEqual(expected.Magnitude, actual.Magnitude); Assert.AreEqual(expected.Confidence, actual.Confidence); } } }
public void OnSecuritiesChanged(SecurityChanges changes) { Algo.OnSecuritiesChanged(changes); }
//Data Event Handler: New data arrives here. "TradeBars" type is a dictionary of strings so you can access it by symbol. public void OnData(TradeBars data) { // if we have no changes, do nothing if (_changes == SecurityChanges.None) return; // liquidate removed securities foreach (var security in _changes.RemovedSecurities) { if (security.Invested) { Liquidate(security.Symbol); Debug("Liquidated Stock: " + security.Symbol.Value); } } // we want 50% allocation in each security in our universe foreach (var security in _changes.AddedSecurities) { SetHoldings(security.Symbol, 0.5m); Debug("Purchased Stock: " + security.Symbol.Value); } _changes = SecurityChanges.None; }
public void OnFrameworkSecuritiesChanged(SecurityChanges changes) { Algo.OnFrameworkSecuritiesChanged(changes); }
public override void OnSecuritiesChanged(SecurityChanges changes) { _optionCount += changes.AddedSecurities.Count(security => security.Symbol.SecurityType == SecurityType.Option); Log($"{GetStatusLog()} CHANGES: {changes}"); }
public override void OnSecuritiesChanged(SecurityChanges changes) { Debug($"{Time}-{changes}"); }
/// <summary> /// Event fired each time the we add/remove securities from the data feed /// </summary> /// <param name="algorithm">The algorithm instance that experienced the change in securities</param> /// <param name="changes">The security additions and removals from the algorithm</param> public void OnSecuritiesChanged(QCAlgorithmFramework algorithm, SecurityChanges changes) { }
/// <summary> /// Ensures that we have a data feed to convert this currency into the base currency. /// This will add a <see cref="SubscriptionDataConfig"/> and create a <see cref="Security"/> at the lowest resolution if one is not found. /// </summary> /// <param name="securities">The security manager</param> /// <param name="subscriptions">The subscription manager used for searching and adding subscriptions</param> /// <param name="marketMap">The market map that decides which market the new security should be in</param> /// <param name="changes">Will be used to consume <see cref="SecurityChanges.AddedSecurities"/></param> /// <param name="securityService">Will be used to create required new <see cref="Security"/></param> /// <param name="accountCurrency">The account currency</param> /// <param name="defaultResolution">The default resolution to use for the internal subscriptions</param> /// <returns>Returns the added <see cref="SubscriptionDataConfig"/>, otherwise null</returns> public List <SubscriptionDataConfig> EnsureCurrencyDataFeed(SecurityManager securities, SubscriptionManager subscriptions, IReadOnlyDictionary <SecurityType, string> marketMap, SecurityChanges changes, ISecurityService securityService, string accountCurrency, Resolution defaultResolution = Resolution.Minute ) { // this gets called every time we add securities using universe selection, // so must of the time we've already resolved the value and don't need to again if (CurrencyConversion != null) { return(null); } if (Symbol == accountCurrency) { _isBaseCurrency = true; CurrencyConversion = null; ConversionRate = 1.0m; return(null); } // existing securities var securitiesToSearch = securities.Select(kvp => kvp.Value) .Concat(changes.AddedSecurities) .Where(s => s.Type == SecurityType.Forex || s.Type == SecurityType.Cfd || s.Type == SecurityType.Crypto); // Create a SecurityType to Market mapping with the markets from SecurityManager members var markets = securities.Select(x => x.Key) .GroupBy(x => x.SecurityType) .ToDictionary(x => x.Key, y => y.Select(symbol => symbol.ID.Market).ToHashSet()); if (markets.ContainsKey(SecurityType.Cfd) && !markets.ContainsKey(SecurityType.Forex)) { markets.Add(SecurityType.Forex, markets[SecurityType.Cfd]); } if (markets.ContainsKey(SecurityType.Forex) && !markets.ContainsKey(SecurityType.Cfd)) { markets.Add(SecurityType.Cfd, markets[SecurityType.Forex]); } var forexEntries = GetAvailableSymbolPropertiesDatabaseEntries(SecurityType.Forex, marketMap, markets); var cfdEntries = GetAvailableSymbolPropertiesDatabaseEntries(SecurityType.Cfd, marketMap, markets); var cryptoEntries = GetAvailableSymbolPropertiesDatabaseEntries(SecurityType.Crypto, marketMap, markets); var potentialEntries = forexEntries .Concat(cfdEntries) .Concat(cryptoEntries) .ToList(); if (!potentialEntries.Any(x => Symbol == x.Key.Symbol.Substring(0, x.Key.Symbol.Length - x.Value.QuoteCurrency.Length) || Symbol == x.Value.QuoteCurrency)) { // currency not found in any tradeable pair Log.Error($"No tradeable pair was found for currency {Symbol}, conversion rate to account currency ({accountCurrency}) will be set to zero."); CurrencyConversion = null; ConversionRate = 0m; return(null); } // Special case for crypto markets without direct pairs (They wont be found by the above) // This allows us to add cash for "StableCoins" that are 1-1 with our account currency without needing a conversion security. // Check out the StableCoinsWithoutPairs static var for those that are missing their 1-1 conversion pairs if (marketMap.TryGetValue(SecurityType.Crypto, out var market) && (Currencies.IsStableCoinWithoutPair(Symbol + accountCurrency, market) || Currencies.IsStableCoinWithoutPair(accountCurrency + Symbol, market))) { CurrencyConversion = null; ConversionRate = 1.0m; return(null); } var requiredSecurities = new List <SubscriptionDataConfig>(); var potentials = potentialEntries .Select(x => QuantConnect.Symbol.Create(x.Key.Symbol, x.Key.SecurityType, x.Key.Market)); var minimumResolution = subscriptions.Subscriptions.Select(x => x.Resolution).DefaultIfEmpty(defaultResolution).Min(); var makeNewSecurity = new Func <Symbol, Security>(symbol => { var securityType = symbol.ID.SecurityType; // use the first subscription defined in the subscription manager var type = subscriptions.LookupSubscriptionConfigDataTypes(securityType, minimumResolution, false).First(); var objectType = type.Item1; var tickType = type.Item2; // set this as an internal feed so that the data doesn't get sent into the algorithm's OnData events var config = subscriptions.SubscriptionDataConfigService.Add(symbol, minimumResolution, fillForward: true, extendedMarketHours: false, isInternalFeed: true, subscriptionDataTypes: new List <Tuple <Type, TickType> > { new Tuple <Type, TickType>(objectType, tickType) }).First(); var newSecurity = securityService.CreateSecurity(symbol, config, addToSymbolCache: false); Log.Trace($"Cash.EnsureCurrencyDataFeed(): Adding {symbol.Value} for cash {Symbol} currency feed"); securities.Add(symbol, newSecurity); requiredSecurities.Add(config); return(newSecurity); }); CurrencyConversion = SecurityCurrencyConversion.LinearSearch(Symbol, accountCurrency, securitiesToSearch.ToList(), potentials, makeNewSecurity); return(requiredSecurities); }