/// <summary> /// Sets the <see cref="TimeSliceFactory"/> instance to use /// </summary> /// <param name="timeSliceFactory">Used to create the new <see cref="TimeSlice"/></param> public void SetTimeSliceFactory(TimeSliceFactory timeSliceFactory) { if (_timeSliceFactory != null) { throw new Exception("SubscriptionSynchronizer.SetTimeSliceFactory(): can only be called once"); } _timeSliceFactory = timeSliceFactory; }
/// <summary> /// Performs additional initialization steps after algorithm initialization /// </summary> protected virtual void PostInitialize() { SubscriptionSynchronizer.SubscriptionFinished += (sender, subscription) => { SubscriptionManager.RemoveSubscription(subscription.Configuration); Log.Debug("Synchronizer.SubscriptionFinished(): Finished subscription:" + $"{subscription.Configuration} at {FrontierTimeProvider.GetUtcNow()} UTC"); }; // this is set after the algorithm initializes _dateTimeZone = Algorithm.TimeZone; TimeSliceFactory = new TimeSliceFactory(_dateTimeZone); SubscriptionSynchronizer.SetTimeSliceFactory(TimeSliceFactory); }
private void PostInitialize() { _subscriptionSynchronizer.SubscriptionFinished += (sender, subscription) => { _subscriptionManager.RemoveSubscription(subscription.Configuration); Log.Debug("Synchronizer.SubscriptionFinished(): Finished subscription:" + $"{subscription.Configuration} at {FrontierTimeProvider.GetUtcNow()} UTC"); }; // this is set after the algorithm initializes _dateTimeZone = _algorithm.TimeZone; _timeSliceFactory = new TimeSliceFactory(_dateTimeZone); _subscriptionSynchronizer.SetTimeSliceFactory(_timeSliceFactory); if (!_liveMode) { // GetTimeProvider() will call GetInitialFrontierTime() which // will consume added subscriptions so we need to do this after initialization TimeProvider = GetTimeProvider(); _subscriptionSynchronizer.SetTimeProvider(TimeProvider); } }
/// <summary> /// Returns an enumerable which provides the data to stream to the algorithm /// </summary> public override IEnumerable <TimeSlice> StreamData(CancellationToken cancellationToken) { PostInitialize(); var shouldSendExtraEmptyPacket = false; var nextEmit = DateTime.MinValue; var lastLoopStart = DateTime.UtcNow; var enumerator = SubscriptionSynchronizer .Sync(SubscriptionManager.DataFeedSubscriptions, cancellationToken) .GetEnumerator(); var previousWasTimePulse = false; while (!cancellationToken.IsCancellationRequested) { var now = DateTime.UtcNow; if (!previousWasTimePulse) { if (!_newLiveDataEmitted.IsSet) { // if we just crossed into the next second let's loop again, we will flush any consolidator bar // else we will wait to be notified by the subscriptions or our scheduled event service every second if (lastLoopStart.Second == now.Second) { _realTimeScheduleEventService.ScheduleEvent(TimeSpan.FromMilliseconds(GetPulseDueTime(now)), now); _newLiveDataEmitted.Wait(); } } _newLiveDataEmitted.Reset(); } lastLoopStart = now; TimeSlice timeSlice; try { if (!enumerator.MoveNext()) { // the enumerator ended break; } timeSlice = enumerator.Current; } catch (Exception err) { Log.Error(err); // notify the algorithm about the error, so it can be reported to the user Algorithm.RunTimeError = err; Algorithm.Status = AlgorithmStatus.RuntimeError; shouldSendExtraEmptyPacket = true; break; } // check for cancellation if (timeSlice == null || cancellationToken.IsCancellationRequested) { break; } var frontierUtc = FrontierTimeProvider.GetUtcNow(); // emit on data or if we've elapsed a full second since last emit or there are security changes if (timeSlice.SecurityChanges != SecurityChanges.None || timeSlice.IsTimePulse || timeSlice.Data.Count != 0 || frontierUtc >= nextEmit) { previousWasTimePulse = timeSlice.IsTimePulse; yield return(timeSlice); // force emitting every second since the data feed is // the heartbeat of the application nextEmit = frontierUtc.RoundDown(Time.OneSecond).Add(Time.OneSecond); } } if (shouldSendExtraEmptyPacket) { // 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 = FrontierTimeProvider.GetUtcNow().RoundDown(Time.OneSecond); if (!cancellationToken.IsCancellationRequested) { var timeSlice = TimeSliceFactory.Create( nextEmit, new List <DataFeedPacket>(), SecurityChanges.None, new Dictionary <Universe, BaseDataCollection>()); yield return(timeSlice); } } enumerator.DisposeSafely(); Log.Trace("LiveSynchronizer.GetEnumerator(): Exited thread."); }
/// <summary> /// Returns an enumerable which provides the data to stream to the algorithm /// </summary> public override IEnumerable <TimeSlice> StreamData(CancellationToken cancellationToken) { PostInitialize(); var shouldSendExtraEmptyPacket = false; var nextEmit = DateTime.MinValue; while (!cancellationToken.IsCancellationRequested) { _newLiveDataEmitted.WaitOne(TimeSpan.FromMilliseconds(500)); TimeSlice timeSlice; try { timeSlice = SubscriptionSynchronizer.Sync(SubscriptionManager.DataFeedSubscriptions); } catch (Exception err) { Log.Error(err); // notify the algorithm about the error, so it can be reported to the user Algorithm.RunTimeError = err; Algorithm.Status = AlgorithmStatus.RuntimeError; shouldSendExtraEmptyPacket = true; break; } // check for cancellation if (cancellationToken.IsCancellationRequested) { break; } var frontierUtc = FrontierTimeProvider.GetUtcNow(); // emit on data or if we've elapsed a full second since last emit or there are security changes if (timeSlice.SecurityChanges != SecurityChanges.None || timeSlice.Data.Count != 0 || frontierUtc >= nextEmit) { yield return(timeSlice); // force emitting every second since the data feed is // the heartbeat of the application nextEmit = frontierUtc.RoundDown(Time.OneSecond).Add(Time.OneSecond); } } if (shouldSendExtraEmptyPacket) { // 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 = FrontierTimeProvider.GetUtcNow().RoundDown(Time.OneSecond); if (!cancellationToken.IsCancellationRequested) { var timeSlice = TimeSliceFactory.Create( nextEmit, new List <DataFeedPacket>(), SecurityChanges.None, new Dictionary <Universe, BaseDataCollection>()); yield return(timeSlice); } } Log.Trace("LiveSynchronizer.GetEnumerator(): Exited thread."); }