Пример #1
0
        /// <summary>
        /// Setups a new <see cref="Subscription"/> which will consume a blocking <see cref="EnqueueableEnumerator{T}"/>
        /// that will be feed by a worker task
        /// </summary>
        /// <param name="request">The subscription data request</param>
        /// <param name="enumerator">The data enumerator stack</param>
        /// <param name="lowerThreshold">The lower threshold for the worker task, for which the consumer will trigger the worker
        /// if it has stopped <see cref="EnqueueableEnumerator{T}.TriggerProducer"/></param>
        /// <param name="upperThreshold">The upper threshold for the worker task, after which it will stop producing until requested
        /// by the consumer <see cref="EnqueueableEnumerator{T}.TriggerProducer"/></param>
        /// <returns>A new subscription instance ready to consume</returns>
        public static Subscription CreateAndScheduleWorker(
            SubscriptionRequest request,
            IEnumerator <BaseData> enumerator,
            int lowerThreshold,
            int upperThreshold)
        {
            var exchangeHours          = request.Security.Exchange.Hours;
            var enqueueable            = new EnqueueableEnumerator <SubscriptionData>(true);
            var timeZoneOffsetProvider = new TimeZoneOffsetProvider(request.Security.Exchange.TimeZone, request.StartTimeUtc, request.EndTimeUtc);
            var subscription           = new Subscription(request, enqueueable, timeZoneOffsetProvider);

            Action produce = () =>
            {
                var count = 0;
                while (enumerator.MoveNext())
                {
                    // subscription has been removed, no need to continue enumerating
                    if (enqueueable.HasFinished)
                    {
                        enumerator.Dispose();
                        return;
                    }

                    var subscriptionData = SubscriptionData.Create(subscription.Configuration, exchangeHours, subscription.OffsetProvider, enumerator.Current);

                    // drop the data into the back of the enqueueable
                    enqueueable.Enqueue(subscriptionData);

                    count++;

                    // stop executing if we have more data than the upper threshold in the enqueueable, we don't want to fill the ram
                    if (count > upperThreshold)
                    {
                        // we use local count for the outside if, for performance, and adjust here
                        count = enqueueable.Count;
                        if (count > upperThreshold)
                        {
                            // we will be re scheduled to run by the consumer, see EnqueueableEnumerator
                            return;
                        }
                    }
                }

                // we made it here because MoveNext returned false, stop the enqueueable
                enqueueable.Stop();
                // we have to dispose of the enumerator
                enumerator.Dispose();
            };

            enqueueable.SetProducer(produce, lowerThreshold);

            return(subscription);
        }
Пример #2
0
        private void ScheduleEnumerator(Subscription subscription, IEnumerator <BaseData> enumerator, EnqueueableEnumerator <SubscriptionData> enqueueable,
                                        int lowerThreshold, int upperThreshold, SecurityExchangeHours exchangeHours, int firstLoopCount = 5)
        {
            Action produce = () =>
            {
                var count = 0;
                while (enumerator.MoveNext())
                {
                    // subscription has been removed, no need to continue enumerating
                    if (enqueueable.HasFinished)
                    {
                        enumerator.Dispose();
                        return;
                    }

                    var subscriptionData = SubscriptionData.Create(subscription.Configuration, exchangeHours, subscription.OffsetProvider, enumerator.Current);

                    // drop the data into the back of the enqueueable
                    enqueueable.Enqueue(subscriptionData);

                    count++;

                    // stop executing if we have more data than the upper threshold in the enqueueable
                    if (count > upperThreshold)
                    {
                        // we use local count for the outside if, for performance, and adjust here
                        count = enqueueable.Count;
                        if (count > upperThreshold)
                        {
                            return;
                        }
                    }
                }

                // we made it here because MoveNext returned false, stop the enqueueable
                enqueueable.Stop();
            };

            enqueueable.SetProducer(produce, lowerThreshold);
        }
Пример #3
0
        /// <summary>
        /// Setups a new <see cref="Subscription"/> which will consume a blocking <see cref="EnqueueableEnumerator{T}"/>
        /// that will be feed by a worker task
        /// </summary>
        /// <param name="request">The subscription data request</param>
        /// <param name="enumerator">The data enumerator stack</param>
        /// <param name="firstLoopLimit">The first loop data point count for which the worker will stop</param>
        /// <returns>A new subscription instance ready to consume</returns>
        public static Subscription CreateAndScheduleWorker(
            SubscriptionRequest request,
            IEnumerator <BaseData> enumerator,
            int firstLoopLimit = 50)
        {
            var upperThreshold = GetUpperThreshold(request.Configuration.Resolution);
            var lowerThreshold = GetLowerThreshold(request.Configuration.Resolution);

            if (request.Configuration.Type == typeof(CoarseFundamental))
            {
                // the lower threshold will be when we start the worker again, if he is stopped
                lowerThreshold = 200;
                // the upper threshold will stop the worker from loading more data. This is roughly 1 GB
                upperThreshold = 500;
            }

            var exchangeHours          = request.Security.Exchange.Hours;
            var enqueueable            = new EnqueueableEnumerator <SubscriptionData>(true);
            var timeZoneOffsetProvider = new TimeZoneOffsetProvider(request.Security.Exchange.TimeZone, request.StartTimeUtc, request.EndTimeUtc);
            var subscription           = new Subscription(request, enqueueable, timeZoneOffsetProvider);

            // The first loop of a backtest can load hundreds of subscription feeds, resulting in long delays while the thresholds
            // for the buffer are reached. For the first loop start up with just 50 items in the buffer.
            var    firstLoop = Ref.Create(true);
            Action produce   = () =>
            {
                try
                {
                    var count = 0;
                    while (enumerator.MoveNext())
                    {
                        // subscription has been removed, no need to continue enumerating
                        if (enqueueable.HasFinished)
                        {
                            enumerator.DisposeSafely();
                            return;
                        }

                        var subscriptionData = SubscriptionData.Create(subscription.Configuration, exchangeHours,
                                                                       subscription.OffsetProvider, enumerator.Current);

                        // drop the data into the back of the enqueueable
                        enqueueable.Enqueue(subscriptionData);

                        count++;

                        // stop executing if we have more data than the upper threshold in the enqueueable, we don't want to fill the ram
                        if (count > upperThreshold || count > firstLoopLimit && firstLoop.Value)
                        {
                            // we use local count for the outside if, for performance, and adjust here
                            count = enqueueable.Count;
                            if (count > upperThreshold || firstLoop.Value)
                            {
                                firstLoop.Value = false;
                                // we will be re scheduled to run by the consumer, see EnqueueableEnumerator

                                // if the consumer is already waiting for us wake him up, he will rescheduled us if required
                                enqueueable.CancellationTokenSource.Cancel();
                                return;
                            }
                        }
                    }
                }
                catch (Exception exception)
                {
                    Log.Error(exception, $"Subscription worker task exception {request.Configuration}.");
                }

                // we made it here because MoveNext returned false or we exploded, stop the enqueueable
                enqueueable.Stop();
                // we have to dispose of the enumerator
                enumerator.DisposeSafely();
            };

            enqueueable.SetProducer(produce, lowerThreshold);

            return(subscription);
        }