public void YieldsFutureDataAtCorrectTime() { var currentTime = new DateTime(2015, 10, 13); var timeProvider = new ManualTimeProvider(currentTime); var underlying = new List<Tick> { new Tick {Time = currentTime.AddSeconds(10)} }; var offsetProvider = new TimeZoneOffsetProvider(DateTimeZone.Utc, new DateTime(2015, 1, 1), new DateTime(2016, 1, 1)); var frontierAware = new FrontierAwareEnumerator(underlying.GetEnumerator(), timeProvider, offsetProvider); for (int i = 0; i < 10; i++) { timeProvider.AdvanceSeconds(1); Assert.IsTrue(frontierAware.MoveNext()); if (i < 9) { Assert.IsNull(frontierAware.Current); } else { Assert.IsNotNull(frontierAware.Current); Assert.AreEqual(underlying[0], frontierAware.Current); } } }
public void ReturnsTrueWhenNextDataIsAheadOfFrontier() { var currentTime = new DateTime(2015, 10, 13); var timeProvider = new ManualTimeProvider(currentTime); var underlying = new List<Tick> { new Tick {Time = currentTime.AddSeconds(1)} }; var offsetProvider = new TimeZoneOffsetProvider(DateTimeZone.Utc, new DateTime(2015, 1, 1), new DateTime(2016, 1, 1)); var frontierAware = new FrontierAwareEnumerator(underlying.GetEnumerator(), timeProvider, offsetProvider); Assert.IsTrue(frontierAware.MoveNext()); Assert.IsNull(frontierAware.Current); }
public void AggregatesTicksIntoSecondBars() { var timeProvider = new ManualTimeProvider(TimeZones.NewYork); var enumerator = new TradeBarBuilderEnumerator(Time.OneSecond, TimeZones.NewYork, timeProvider); // noon new york time var currentTime = new DateTime(2015, 10, 08, 12, 0, 0); timeProvider.SetCurrentTime(currentTime); // add some ticks var ticks = new List<Tick> { new Tick(currentTime, "SPY", 199.55m, 199, 200) {Quantity = 10}, new Tick(currentTime, "SPY", 199.56m, 199.21m, 200.02m) {Quantity = 5}, new Tick(currentTime, "SPY", 199.53m, 198.77m, 199.75m) {Quantity = 20}, new Tick(currentTime, "SPY", 198.77m, 199.75m) {Quantity = 0}, new Tick(currentTime, "SPY", 199.73m, 198.77m, 199.75m) {Quantity = 20}, new Tick(currentTime, "SPY", 198.77m, 199.75m) {Quantity = 0}, }; foreach (var tick in ticks) { enumerator.ProcessData(tick); } // even though no data is here, it will still return true Assert.IsTrue(enumerator.MoveNext()); Assert.IsNull(enumerator.Current); // advance a second currentTime = currentTime.AddSeconds(1); timeProvider.SetCurrentTime(currentTime); Assert.IsTrue(enumerator.MoveNext()); Assert.IsNotNull(enumerator.Current); // in the spirit of not duplicating the above code 5 times (OHLCV, we'll assert these ere as well) var bar = (TradeBar)enumerator.Current; Assert.AreEqual(currentTime.AddSeconds(-1), bar.Time); Assert.AreEqual(currentTime, bar.EndTime); Assert.AreEqual("SPY", bar.Symbol.Value); Assert.AreEqual(ticks.First().LastPrice, bar.Open); Assert.AreEqual(ticks.Max(x => x.LastPrice), bar.High); Assert.AreEqual(ticks.Min(x => x.LastPrice), bar.Low); Assert.AreEqual(ticks.Last().LastPrice, bar.Close); Assert.AreEqual(ticks.Sum(x => x.Quantity), bar.Volume); }
public void FastForwardsOldDataAllowsEquals() { var start = new DateTime(2015, 10, 10, 13, 0, 0); var data = new List<Tick> { new Tick {Time = start.AddMinutes(-1)}, new Tick {Time = start.AddSeconds(-1)}, new Tick {Time = start.AddSeconds(0)}, new Tick {Time = start.AddSeconds(1)}, }; var timeProvider = new ManualTimeProvider(start, TimeZones.Utc); var fastForward = new FastForwardEnumerator(data.GetEnumerator(), timeProvider, TimeZones.Utc, TimeSpan.FromSeconds(1)); Assert.IsTrue(fastForward.MoveNext()); Assert.AreEqual(start.AddSeconds(-1), fastForward.Current.Time); }
public void YieldsDataWhenFrontierPasses() { var currentTime = new DateTime(2015, 10, 13); var timeProvider = new ManualTimeProvider(currentTime); var underlying = new List<Tick> { new Tick {Time = currentTime.AddSeconds(1)} }; var offsetProvider = new TimeZoneOffsetProvider(DateTimeZone.Utc, new DateTime(2015, 1, 1), new DateTime(2016, 1, 1)); var frontierAware = new FrontierAwareEnumerator(underlying.GetEnumerator(), timeProvider, offsetProvider); timeProvider.AdvanceSeconds(1); Assert.IsTrue(frontierAware.MoveNext()); Assert.IsNotNull(frontierAware.Current); Assert.AreEqual(underlying[0], frontierAware.Current); }
public void LimitsBasedOnTimeBetweenCalls() { var currentTime = new DateTime(2015, 10, 10, 13, 6, 0); var timeProvider = new ManualTimeProvider(currentTime, TimeZones.Utc); var data = Enumerable.Range(0, 100).Select(x => new Tick {Symbol = CreateSymbol(x)}).GetEnumerator(); var rateLimit = new RateLimitEnumerator(data, timeProvider, Time.OneSecond); Assert.IsTrue(rateLimit.MoveNext()); while (rateLimit.MoveNext() && rateLimit.Current == null) { timeProvider.AdvanceSeconds(0.1); } var delta = (timeProvider.GetUtcNow() - currentTime).TotalSeconds; Assert.AreEqual(1, delta); Assert.AreEqual("1", data.Current.Symbol.Value); }
/// <summary> /// Initializes the data feed for the specified job and algorithm /// </summary> public void Initialize(IAlgorithm algorithm, AlgorithmNodePacket job, IResultHandler resultHandler, IMapFileProvider mapFileProvider, IFactorFileProvider factorFileProvider) { if (!(job is LiveNodePacket)) { throw new ArgumentException("The LiveTradingDataFeed requires a LiveNodePacket."); } _cancellationTokenSource = new CancellationTokenSource(); _algorithm = algorithm; _job = (LiveNodePacket) job; _resultHandler = resultHandler; _timeProvider = GetTimeProvider(); _dataQueueHandler = GetDataQueueHandler(); _frontierTimeProvider = new ManualTimeProvider(_timeProvider.GetUtcNow()); _customExchange = new BaseDataExchange("CustomDataExchange") {SleepInterval = 10}; // sleep is controlled on this exchange via the GetNextTicksEnumerator _exchange = new BaseDataExchange("DataQueueExchange"){SleepInterval = 0}; _exchange.AddEnumerator(DataQueueHandlerSymbol, GetNextTicksEnumerator()); _subscriptions = new SubscriptionCollection(); _bridge = new BusyBlockingCollection<TimeSlice>(); _universeSelection = new UniverseSelection(this, algorithm, job.Controls); // run the exchanges Task.Run(() => _exchange.Start(_cancellationTokenSource.Token)); Task.Run(() => _customExchange.Start(_cancellationTokenSource.Token)); // this value will be modified via calls to AddSubscription/RemoveSubscription var ffres = Time.OneMinute; _fillForwardResolution = Ref.Create(() => ffres, v => ffres = v); // wire ourselves up to receive notifications when universes are added/removed var start = _timeProvider.GetUtcNow(); algorithm.UniverseManager.CollectionChanged += (sender, args) => { switch (args.Action) { case NotifyCollectionChangedAction.Add: foreach (var universe in args.NewItems.OfType<Universe>()) { var config = universe.Configuration; var marketHoursDatabase = MarketHoursDatabase.FromDataFolder(); var exchangeHours = marketHoursDatabase.GetExchangeHours(config); Security security; if (!_algorithm.Securities.TryGetValue(config.Symbol, out security)) { // create a canonical security object security = new Security(exchangeHours, config, _algorithm.Portfolio.CashBook[CashBook.AccountCurrency], SymbolProperties.GetDefault(CashBook.AccountCurrency)); } AddSubscription(new SubscriptionRequest(true, universe, security, config, start, Time.EndOfTime)); // Not sure if this is needed but left here because of this: // https://github.com/QuantConnect/Lean/commit/029d70bde6ca83a1eb0c667bb5cc4444bea05678 UpdateFillForwardResolution(); } break; case NotifyCollectionChangedAction.Remove: foreach (var universe in args.OldItems.OfType<Universe>()) { RemoveSubscription(universe.Configuration); } break; default: throw new NotImplementedException("The specified action is not implemented: " + args.Action); } }; }
public void HandlesRestApi() { var resolution = Resolution.Second; var algorithm = new AlgorithmStub(); algorithm.AddData<RestApiBaseData>("RestApi", resolution); var symbol = SymbolCache.GetSymbol("RestApi"); FuncDataQueueHandler dqgh; var timeProvider = new ManualTimeProvider(new DateTime(2015, 10, 10, 16, 36, 0)); var feed = RunDataFeed(algorithm, out dqgh, null); var count = 0; var receivedData = false; var timeZone = algorithm.Securities[symbol].Exchange.TimeZone; RestApiBaseData last = null; var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5)); foreach (var ts in feed) { //timeProvider.AdvanceSeconds(0.5); if (!ts.Slice.ContainsKey(symbol)) return; count++; receivedData = true; var data = (RestApiBaseData)ts.Slice[symbol]; var time = data.EndTime.ConvertToUtc(timeZone); Console.WriteLine(DateTime.UtcNow + ": Data time: " + time.ConvertFromUtc(TimeZones.NewYork) + Environment.NewLine); if (last != null) { Assert.AreEqual(last.EndTime, data.EndTime.Subtract(resolution.ToTimeSpan())); } last = data; } // even though we're doing 10 seconds, give a little // leeway for slow internet traffic Assert.That(count, Is.GreaterThanOrEqualTo(8)); Assert.IsTrue(receivedData); Assert.That(RestApiBaseData.ReaderCount, Is.LessThanOrEqualTo(30)); // we poll at 10x frequency Console.WriteLine("Count: " + count + " ReaderCount: " + RestApiBaseData.ReaderCount); }
/// <summary> /// Initializes the data feed for the specified job and algorithm /// </summary> public void Initialize(IAlgorithm algorithm, AlgorithmNodePacket job, IResultHandler resultHandler) { if (!(job is LiveNodePacket)) { throw new ArgumentException("The LiveTradingDataFeed requires a LiveNodePacket."); } _cancellationTokenSource = new CancellationTokenSource(); _algorithm = algorithm; _job = (LiveNodePacket) job; _resultHandler = resultHandler; _timeProvider = GetTimeProvider(); _dataQueueHandler = GetDataQueueHandler(); _frontierTimeProvider = new ManualTimeProvider(_timeProvider.GetUtcNow()); _customExchange = new BaseDataExchange("CustomDataExchange") {SleepInterval = 10}; // sleep is controlled on this exchange via the GetNextTicksEnumerator _exchange = new BaseDataExchange("DataQueueExchange", GetNextTicksEnumerator()){SleepInterval = 0}; _subscriptions = new ConcurrentDictionary<SymbolSecurityType, Subscription>(); Bridge = new BusyBlockingCollection<TimeSlice>(); // run the exchanges _exchange.Start(); _customExchange.Start(); // find the minimum resolution, ignoring ticks _fillForwardResolution = algorithm.SubscriptionManager.Subscriptions .Where(x => x.Resolution != Resolution.Tick) .Select(x => x.Resolution) .Union(algorithm.Universes.Select(x => x.SubscriptionSettings.Resolution)) .DefaultIfEmpty(algorithm.UniverseSettings.Resolution) .Min(); // add user defined subscriptions var start = _timeProvider.GetUtcNow(); foreach (var kvp in _algorithm.Securities.OrderBy(x => x.Key.ToString())) { var security = kvp.Value; AddSubscription(security, start, Time.EndOfTime, true); } // add universe subscriptions foreach (var universe in _algorithm.Universes) { var subscription = CreateUniverseSubscription(universe, start, Time.EndOfTime); _subscriptions[new SymbolSecurityType(subscription)] = subscription; } }
/// <summary> /// Initializes the data feed for the specified job and algorithm /// </summary> public void Initialize(IAlgorithm algorithm, AlgorithmNodePacket job, IResultHandler resultHandler, IMapFileProvider mapFileProvider) { if (!(job is LiveNodePacket)) { throw new ArgumentException("The LiveTradingDataFeed requires a LiveNodePacket."); } if (algorithm.SubscriptionManager.Subscriptions.Count == 0 && algorithm.Universes.IsNullOrEmpty()) { throw new Exception("No subscriptions registered and no universe defined."); } _cancellationTokenSource = new CancellationTokenSource(); _algorithm = algorithm; _job = (LiveNodePacket) job; _resultHandler = resultHandler; _timeProvider = GetTimeProvider(); _dataQueueHandler = GetDataQueueHandler(); _frontierTimeProvider = new ManualTimeProvider(_timeProvider.GetUtcNow()); _customExchange = new BaseDataExchange("CustomDataExchange") {SleepInterval = 10}; // sleep is controlled on this exchange via the GetNextTicksEnumerator _exchange = new BaseDataExchange("DataQueueExchange", GetNextTicksEnumerator()){SleepInterval = 0}; _subscriptions = new ConcurrentDictionary<Symbol, Subscription>(); Bridge = new BusyBlockingCollection<TimeSlice>(); // run the exchanges _exchange.Start(); _customExchange.Start(); // this value will be modified via calls to AddSubscription/RemoveSubscription var ffres = Time.OneSecond; _fillForwardResolution = Ref.Create(() => ffres, v => ffres = v); ffres = ResolveFillForwardResolution(algorithm); // add subscriptions var start = _timeProvider.GetUtcNow(); foreach (var universe in _algorithm.Universes) { var subscription = CreateUniverseSubscription(universe, start, Time.EndOfTime); _subscriptions[subscription.Security.Symbol] = subscription; } }
/// <summary> /// Initializes the data feed for the specified job and algorithm /// </summary> public void Initialize(IAlgorithm algorithm, AlgorithmNodePacket job, IResultHandler resultHandler, IMapFileProvider mapFileProvider, IFactorFileProvider factorFileProvider) { if (!(job is LiveNodePacket)) { throw new ArgumentException("The LiveTradingDataFeed requires a LiveNodePacket."); } _cancellationTokenSource = new CancellationTokenSource(); _algorithm = algorithm; _job = (LiveNodePacket) job; _resultHandler = resultHandler; _timeProvider = GetTimeProvider(); _dataQueueHandler = GetDataQueueHandler(); _frontierTimeProvider = new ManualTimeProvider(_timeProvider.GetUtcNow()); _customExchange = new BaseDataExchange("CustomDataExchange") {SleepInterval = 10}; // sleep is controlled on this exchange via the GetNextTicksEnumerator _exchange = new BaseDataExchange("DataQueueExchange"){SleepInterval = 0}; _exchange.AddEnumerator(DataQueueHandlerSymbol, GetNextTicksEnumerator()); _subscriptions = new ConcurrentDictionary<Symbol, List<Subscription>>(); _bridge = new BusyBlockingCollection<TimeSlice>(); _universeSelection = new UniverseSelection(this, algorithm, job.Controls); // run the exchanges Task.Run(() => _exchange.Start(_cancellationTokenSource.Token)); Task.Run(() => _customExchange.Start(_cancellationTokenSource.Token)); // this value will be modified via calls to AddSubscription/RemoveSubscription var ffres = Time.OneMinute; _fillForwardResolution = Ref.Create(() => ffres, v => ffres = v); // wire ourselves up to receive notifications when universes are added/removed var start = _timeProvider.GetUtcNow(); algorithm.UniverseManager.CollectionChanged += (sender, args) => { switch (args.Action) { case NotifyCollectionChangedAction.Add: foreach (var universe in args.NewItems.OfType<Universe>()) { _subscriptions.Add(universe.Configuration.Symbol, CreateUniverseSubscription(universe, start, Time.EndOfTime)); UpdateFillForwardResolution(); } break; case NotifyCollectionChangedAction.Remove: foreach (var universe in args.OldItems.OfType<Universe>()) { RemoveSubscription(universe.Configuration.Symbol); } break; default: throw new NotImplementedException("The specified action is not implemented: " + args.Action); } }; }
public void FillsForwardOnNulls() { var reference = new DateTime(2015, 10, 08); var period = Time.OneSecond; var underlying = new List<BaseData> { // 0 seconds new TradeBar(reference, Symbols.SPY, 10, 20, 5, 15, 123456, period), // 1 seconds null, // 3 seconds new TradeBar(reference.AddSeconds(2), Symbols.SPY, 100, 200, 50, 150, 1234560, period), null, null, null, null }; var timeProvider = new ManualTimeProvider(TimeZones.NewYork); timeProvider.SetCurrentTime(reference); var exchange = new SecurityExchange(SecurityExchangeHours.AlwaysOpen(TimeZones.NewYork)); var fillForward = new LiveFillForwardEnumerator(timeProvider, underlying.GetEnumerator(), exchange, Ref.Create(Time.OneSecond), false, Time.EndOfTime, Time.OneSecond); // first point is always emitted Assert.IsTrue(fillForward.MoveNext()); Assert.AreEqual(underlying[0], fillForward.Current); // stepping again without advancing time does nothing, but we'll still // return true as per IEnumerator contract Assert.IsTrue(fillForward.MoveNext()); Assert.IsNull(fillForward.Current); timeProvider.SetCurrentTime(reference.AddSeconds(1)); // non-null next will fill forward in between Assert.IsTrue(fillForward.MoveNext()); Assert.AreEqual(underlying[0].EndTime, fillForward.Current.Time); Assert.AreEqual(underlying[0].Value, fillForward.Current.Value); Assert.IsTrue(fillForward.Current.IsFillForward); // even without stepping the time this will advance since non-null data is ready Assert.IsTrue(fillForward.MoveNext()); Assert.AreEqual(underlying[2], fillForward.Current); // step ahead into null data territory timeProvider.SetCurrentTime(reference.AddSeconds(4)); Assert.IsTrue(fillForward.MoveNext()); Assert.AreEqual(underlying[2].Value, fillForward.Current.Value); Assert.AreEqual(timeProvider.GetUtcNow().ConvertFromUtc(TimeZones.NewYork), fillForward.Current.EndTime); Assert.IsTrue(fillForward.Current.IsFillForward); Assert.IsTrue(fillForward.MoveNext()); Assert.IsNull(fillForward.Current); timeProvider.SetCurrentTime(reference.AddSeconds(5)); Assert.IsTrue(fillForward.MoveNext()); Assert.AreEqual(underlying[2].Value, fillForward.Current.Value); Assert.AreEqual(timeProvider.GetUtcNow().ConvertFromUtc(TimeZones.NewYork), fillForward.Current.EndTime); Assert.IsTrue(fillForward.Current.IsFillForward); timeProvider.SetCurrentTime(reference.AddSeconds(6)); Assert.IsTrue(fillForward.MoveNext()); Assert.AreEqual(underlying[2].Value, fillForward.Current.Value); Assert.AreEqual(timeProvider.GetUtcNow().ConvertFromUtc(TimeZones.NewYork), fillForward.Current.EndTime); Assert.IsTrue(fillForward.Current.IsFillForward); }
public void CreatesNewBarWhenBarSizeElapses() { var timeProvider = new ManualTimeProvider(); var enumerator = new TradeBarBuilderEnumerator(Time.OneSecond, TimeZones.Utc, timeProvider); // noon new york time var startTime = new DateTime(2015, 10, 08, 12, 0, 0); timeProvider.SetCurrentTime(startTime); enumerator.ProcessData(new Tick{Time = startTime}); Assert.IsTrue(enumerator.MoveNext()); Assert.IsNull(enumerator.Current); timeProvider.AdvanceSeconds(0.99); enumerator.ProcessData(new Tick {Time = timeProvider.GetUtcNow()}); Assert.IsTrue(enumerator.MoveNext()); Assert.IsNull(enumerator.Current); timeProvider.SetCurrentTime(startTime.AddSeconds(1)); // the second just ticked over, so it shouldn't include this tick when we move next enumerator.ProcessData(new Tick {Time = timeProvider.GetUtcNow(), Quantity = 1}); Assert.IsTrue(enumerator.MoveNext()); Assert.IsNotNull(enumerator.Current); Assert.AreEqual(0, ((TradeBar)enumerator.Current).Volume); }
/// <summary> /// Initializes the data feed for the specified job and algorithm /// </summary> public void Initialize(IAlgorithm algorithm, AlgorithmNodePacket job, IResultHandler resultHandler, IMapFileProvider mapFileProvider, IFactorFileProvider factorFileProvider, IDataProvider dataProvider) { if (!(job is LiveNodePacket)) { throw new ArgumentException("The LiveTradingDataFeed requires a LiveNodePacket."); } _cancellationTokenSource = new CancellationTokenSource(); _algorithm = algorithm; _job = (LiveNodePacket)job; _resultHandler = resultHandler; _timeProvider = GetTimeProvider(); _dataQueueHandler = GetDataQueueHandler(); _dataProvider = dataProvider; _dataCacheProvider = new SingleEntryDataCacheProvider(dataProvider); _frontierTimeProvider = new ManualTimeProvider(_timeProvider.GetUtcNow()); _customExchange = new BaseDataExchange("CustomDataExchange") { SleepInterval = 10 }; // sleep is controlled on this exchange via the GetNextTicksEnumerator _exchange = new BaseDataExchange("DataQueueExchange") { SleepInterval = 0 }; _exchange.AddEnumerator(DataQueueHandlerSymbol, GetNextTicksEnumerator()); _subscriptions = new SubscriptionCollection(); _bridge = new BusyBlockingCollection <TimeSlice>(); _universeSelection = new UniverseSelection(this, algorithm, job.Controls); // run the exchanges Task.Run(() => _exchange.Start(_cancellationTokenSource.Token)); Task.Run(() => _customExchange.Start(_cancellationTokenSource.Token)); // this value will be modified via calls to AddSubscription/RemoveSubscription var ffres = Time.OneMinute; _fillForwardResolution = Ref.Create(() => ffres, v => ffres = v); // wire ourselves up to receive notifications when universes are added/removed var start = _timeProvider.GetUtcNow(); algorithm.UniverseManager.CollectionChanged += (sender, args) => { switch (args.Action) { case NotifyCollectionChangedAction.Add: foreach (var universe in args.NewItems.OfType <Universe>()) { var config = universe.Configuration; var marketHoursDatabase = MarketHoursDatabase.FromDataFolder(); var exchangeHours = marketHoursDatabase.GetExchangeHours(config); Security security; if (!_algorithm.Securities.TryGetValue(config.Symbol, out security)) { // create a canonical security object security = new Security(exchangeHours, config, _algorithm.Portfolio.CashBook[CashBook.AccountCurrency], SymbolProperties.GetDefault(CashBook.AccountCurrency)); } AddSubscription(new SubscriptionRequest(true, universe, security, config, start, Time.EndOfTime)); // Not sure if this is needed but left here because of this: // https://github.com/QuantConnect/Lean/commit/029d70bde6ca83a1eb0c667bb5cc4444bea05678 UpdateFillForwardResolution(); } break; case NotifyCollectionChangedAction.Remove: foreach (var universe in args.OldItems.OfType <Universe>()) { RemoveSubscription(universe.Configuration); } break; default: throw new NotImplementedException("The specified action is not implemented: " + args.Action); } }; }
/// <summary> /// Initializes the data feed for the specified job and algorithm /// </summary> public void Initialize(IAlgorithm algorithm, AlgorithmNodePacket job, IResultHandler resultHandler, IMapFileProvider mapFileProvider, IFactorFileProvider factorFileProvider) { if (!(job is LiveNodePacket)) { throw new ArgumentException("The LiveTradingDataFeed requires a LiveNodePacket."); } _cancellationTokenSource = new CancellationTokenSource(); _algorithm = algorithm; _job = (LiveNodePacket)job; _resultHandler = resultHandler; _timeProvider = GetTimeProvider(); _dataQueueHandler = GetDataQueueHandler(); _frontierTimeProvider = new ManualTimeProvider(_timeProvider.GetUtcNow()); _customExchange = new BaseDataExchange("CustomDataExchange") { SleepInterval = 10 }; // sleep is controlled on this exchange via the GetNextTicksEnumerator _exchange = new BaseDataExchange("DataQueueExchange") { SleepInterval = 0 }; _exchange.AddEnumerator(DataQueueHandlerSymbol, GetNextTicksEnumerator()); _subscriptions = new ConcurrentDictionary <Symbol, Subscription>(); _bridge = new BusyBlockingCollection <TimeSlice>(); _universeSelection = new UniverseSelection(this, algorithm, job.Controls); // run the exchanges Task.Run(() => _exchange.Start(_cancellationTokenSource.Token)); Task.Run(() => _customExchange.Start(_cancellationTokenSource.Token)); // this value will be modified via calls to AddSubscription/RemoveSubscription var ffres = Time.OneSecond; _fillForwardResolution = Ref.Create(() => ffres, v => ffres = v); ffres = ResolveFillForwardResolution(algorithm); // wire ourselves up to receive notifications when universes are added/removed var start = _timeProvider.GetUtcNow(); algorithm.UniverseManager.CollectionChanged += (sender, args) => { switch (args.Action) { case NotifyCollectionChangedAction.Add: foreach (var universe in args.NewItems.OfType <Universe>()) { _subscriptions[universe.Configuration.Symbol] = CreateUniverseSubscription(universe, start, Time.EndOfTime); } break; case NotifyCollectionChangedAction.Remove: foreach (var universe in args.OldItems.OfType <Universe>()) { Subscription subscription; if (_subscriptions.TryGetValue(universe.Configuration.Symbol, out subscription)) { RemoveSubscription(subscription); } } break; default: throw new NotImplementedException("The specified action is not implemented: " + args.Action); } }; }