예제 #1
0
        private static PnFCandleMessage CreateCandle(ICandleBuilderSubscription subscription, DataType buildFrom, PnFArg pnfArg, decimal openPrice, decimal highPrice, decimal lowPrice, decimal closePrice, decimal price, decimal?volume, Sides?side, DateTimeOffset time, decimal?oi)
        {
            var candle = new PnFCandleMessage
            {
                SecurityId = subscription.Message.SecurityId,
                PnFArg     = pnfArg,
                BuildFrom  = buildFrom,

                OpenPrice  = openPrice,
                ClosePrice = closePrice,
                HighPrice  = highPrice,
                LowPrice   = lowPrice,
                OpenVolume = volume,
                //CloseVolume = volume,
                HighVolume = volume,
                LowVolume  = volume,
                OpenTime   = time,
                //CloseTime = time,
                HighTime = time,
                LowTime  = time,
                State    = CandleStates.Active,
            };

            if (subscription.Message.IsCalcVolumeProfile)
            {
                var levels = new List <CandlePriceLevel>();
                subscription.VolumeProfile = new VolumeProfileBuilder(levels);
                candle.PriceLevels         = levels;
            }

            UpdateCandle(candle, price, volume, time, side, oi, subscription.VolumeProfile);

            return(candle);
        }
예제 #2
0
        /// <summary>
        /// To process the new data.
        /// </summary>
        /// <param name="subscription">Subscription.</param>
        /// <param name="transform">The data source transformation.</param>
        /// <returns>A new candles changes.</returns>
        protected virtual IEnumerable <TCandleMessage> OnProcess(ICandleBuilderSubscription subscription, ICandleBuilderValueTransform transform)
        {
            if (subscription is null)
            {
                throw new ArgumentNullException(nameof(subscription));
            }

            if (transform == null)
            {
                throw new ArgumentNullException(nameof(transform));
            }

            var currentCandle = (TCandleMessage)subscription.CurrentCandle;
            var volumeProfile = subscription.VolumeProfile;

            var candle = ProcessValue(subscription, transform);

            if (candle == null)
            {
                // skip the value that cannot be processed
                yield break;
            }

            if (candle == currentCandle)
            {
                if (subscription.Message.IsCalcVolumeProfile)
                {
                    if (volumeProfile == null)
                    {
                        throw new InvalidOperationException();
                    }

                    volumeProfile.Update(transform);
                }

                //candle.State = CandleStates.Changed;
                yield return(candle);
            }
            else
            {
                if (currentCandle != null)
                {
                    currentCandle.State = CandleStates.Finished;
                    yield return(currentCandle);
                }

                if (subscription.Message.IsCalcVolumeProfile)
                {
                    var levels = new List <CandlePriceLevel>();

                    subscription.VolumeProfile = volumeProfile = new VolumeProfileBuilder(levels);
                    volumeProfile.Update(transform);

                    candle.PriceLevels = levels;
                }

                candle.State = CandleStates.Active;
                yield return(candle);
            }
        }
예제 #3
0
        /// <inheritdoc />
        protected override HeikinAshiCandleMessage CreateCandle(ICandleBuilderSubscription subscription, ICandleBuilderValueTransform transform)
        {
            var timeFrame = subscription.Message.GetTimeFrame();

            var board  = subscription.Message.IsRegularTradingHours ? ExchangeInfoProvider.GetOrCreateBoard(subscription.Message.SecurityId.BoardCode) : ExchangeBoard.Associated;
            var bounds = timeFrame.GetCandleBounds(transform.Time, board, board.WorkingTime);

            if (transform.Time < bounds.Min)
            {
                return(null);
            }

            var openTime = bounds.Min;

            var candle = FirstInitCandle(subscription, new HeikinAshiCandleMessage
            {
                TimeFrame = timeFrame,
                OpenTime  = openTime,
                HighTime  = openTime,
                LowTime   = openTime,
                CloseTime = openTime,
            }, transform);

            var currentCandle = subscription.CurrentCandle;

            if (currentCandle != null)
            {
                candle.OpenPrice = (currentCandle.OpenPrice + currentCandle.ClosePrice) / 2M;
            }

            return(candle);
        }
예제 #4
0
        private static RenkoCandleMessage CreateCandle(ICandleBuilderSubscription subscription, DataType buildFrom, Unit boxSize, decimal openPrice, decimal renkoStep, decimal price, decimal?volume, Sides?side, DateTimeOffset time, decimal?oi)
        {
            var candle = new RenkoCandleMessage
            {
                SecurityId = subscription.Message.SecurityId,
                BoxSize    = boxSize,
                BuildFrom  = buildFrom,

                OpenPrice  = openPrice,
                ClosePrice = openPrice + renkoStep,
                //HighPrice = openPrice + renkoStep,
                //LowPrice = openPrice,
                OpenVolume     = volume,
                CloseVolume    = volume,
                HighVolume     = volume,
                LowVolume      = volume,
                OpenTime       = time,
                CloseTime      = time,
                HighTime       = time,
                LowTime        = time,
                RelativeVolume = side == null ? null : (side == Sides.Buy ? volume : -volume),
                TotalTicks     = 1,
                State          = CandleStates.Active,
                OpenInterest   = oi,
            };

            if (volume != null)
            {
                candle.TotalPrice  += price * volume.Value;
                candle.TotalVolume += volume.Value;
            }

            if (renkoStep > 0)
            {
                candle.HighPrice = candle.ClosePrice;
                candle.LowPrice  = candle.OpenPrice;
            }
            else
            {
                candle.HighPrice = candle.OpenPrice;
                candle.LowPrice  = candle.ClosePrice;
            }

            if (subscription.Message.IsCalcVolumeProfile)
            {
                var levels = new List <CandlePriceLevel>();

                subscription.VolumeProfile = new VolumeProfileBuilder(levels);
                subscription.VolumeProfile.Update(price, volume, side);

                candle.PriceLevels = levels;
            }

            return(candle);
        }
예제 #5
0
 /// <inheritdoc />
 public IEnumerable <CandleMessage> Process(ICandleBuilderSubscription subscription, ICandleBuilderValueTransform transform)
 {
     if (IsTimeValid(subscription.Message, transform.Time))
     {
         foreach (var candle in OnProcess(subscription, transform))
         {
             subscription.CurrentCandle = candle;
             yield return(candle);
         }
     }
 }
예제 #6
0
        /// <inheritdoc />
        protected override TickCandleMessage CreateCandle(ICandleBuilderSubscription subscription, ICandleBuilderValueTransform transform)
        {
            var time = transform.Time;

            return(FirstInitCandle(subscription, new TickCandleMessage
            {
                MaxTradeCount = subscription.Message.GetArg <int>(),
                OpenTime = time,
                CloseTime = time,
                HighTime = time,
                LowTime = time,
            }, transform));
        }
예제 #7
0
        /// <inheritdoc />
        protected override RangeCandleMessage CreateCandle(ICandleBuilderSubscription subscription, ICandleBuilderValueTransform transform)
        {
            var time = transform.Time;

            return(FirstInitCandle(subscription, new RangeCandleMessage
            {
                PriceRange = subscription.Message.GetArg <Unit>(),
                OpenTime = time,
                CloseTime = time,
                HighTime = time,
                LowTime = time,
            }, transform));
        }
예제 #8
0
        /// <summary>
        /// To process the new data.
        /// </summary>
        /// <param name="subscription">Subscription.</param>
        /// <param name="transform">The data source transformation.</param>
        /// <returns>A new candle. If there is not necessary to create a new candle, then <see cref="ICandleBuilderSubscription.CurrentCandle" /> is returned. If it is impossible to create a new candle (<paramref name="transform" /> cannot be applied to candles), then <see langword="null" /> is returned.</returns>
        protected virtual TCandleMessage ProcessValue(ICandleBuilderSubscription subscription, ICandleBuilderValueTransform transform)
        {
            var currentCandle = (TCandleMessage)subscription.CurrentCandle;

            if (currentCandle == null || IsCandleFinishedBeforeChange(subscription, currentCandle, transform))
            {
                currentCandle = CreateCandle(subscription, transform);
                this.AddDebugLog("NewCandle {0} ForValue {1}", currentCandle, transform);
                return(currentCandle);
            }

            UpdateCandle(subscription, currentCandle, transform);

            // TODO performance
            //this.AddDebugLog("UpdatedCandle {0} ForValue {1}", currentCandle, value);

            return(currentCandle);
        }
예제 #9
0
        /// <summary>
        /// To fill in the initial candle settings.
        /// </summary>
        /// <param name="subscription">Subscription.</param>
        /// <param name="candle">Candle.</param>
        /// <param name="transform">The data source transformation.</param>
        /// <returns>Candle.</returns>
        protected virtual TCandleMessage FirstInitCandle(ICandleBuilderSubscription subscription, TCandleMessage candle, ICandleBuilderValueTransform transform)
        {
            if (candle == null)
            {
                throw new ArgumentNullException(nameof(candle));
            }

            if (transform == null)
            {
                throw new ArgumentNullException(nameof(transform));
            }

            var price  = transform.Price;
            var volume = transform.Volume;

            candle.BuildFrom  = transform.BuildFrom;
            candle.SecurityId = subscription.Message.SecurityId;

            candle.OpenPrice  = price;
            candle.ClosePrice = price;
            candle.LowPrice   = price;
            candle.HighPrice  = price;
            //candle.TotalPrice = price;

            candle.OpenVolume  = volume;
            candle.CloseVolume = volume;
            candle.LowVolume   = volume;
            candle.HighVolume  = volume;

            if (volume != null)
            {
                candle.TotalVolume = volume.Value;
            }

            candle.OpenInterest = transform.OpenInterest;
            candle.PriceLevels  = transform.PriceLevels;

            candle.TotalTicks = 1;

            return(candle);
        }
예제 #10
0
        /// <inheritdoc />
        protected override void UpdateCandle(ICandleBuilderSubscription subscription, HeikinAshiCandleMessage candle, ICandleBuilderValueTransform transform)
        {
            base.UpdateCandle(subscription, candle, transform);

            candle.ClosePrice = (candle.OpenPrice + candle.HighPrice + candle.LowPrice + candle.ClosePrice) / 4M;
        }
예제 #11
0
        /// <inheritdoc />
        protected override IEnumerable <RenkoCandleMessage> OnProcess(ICandleBuilderSubscription subscription, ICandleBuilderValueTransform transform)
        {
            var currentRenkoCandle = (RenkoCandleMessage)subscription.CurrentCandle;

            var price     = transform.Price;
            var volume    = transform.Volume;
            var time      = transform.Time;
            var side      = transform.Side;
            var oi        = transform.OpenInterest;
            var buildFrom = transform.BuildFrom;

            var boxSize   = subscription.Message.GetArg <Unit>();
            var renkoStep = (decimal)(1 * boxSize);

            if (currentRenkoCandle == null)
            {
                var openPrice = price.Floor(renkoStep);

                currentRenkoCandle = CreateCandle(subscription, buildFrom, boxSize, openPrice, renkoStep, price, volume, side, time, oi);
                yield return(currentRenkoCandle);
            }
            else
            {
                if (currentRenkoCandle.LowPrice <= price && price <= currentRenkoCandle.HighPrice)
                {
                    currentRenkoCandle.TotalTicks++;

                    if (volume != null)
                    {
                        currentRenkoCandle.TotalVolume += volume.Value;
                        currentRenkoCandle.TotalPrice  += volume.Value * price;

                        currentRenkoCandle.RelativeVolume += side == Sides.Buy ? volume : -volume;
                    }

                    currentRenkoCandle.CloseVolume = volume;
                    currentRenkoCandle.CloseTime   = time;

                    subscription.VolumeProfile?.Update(price, volume, side);

                    currentRenkoCandle.OpenInterest = oi;

                    yield return(currentRenkoCandle);
                }
                else
                {
                    currentRenkoCandle.State = CandleStates.Finished;
                    yield return(currentRenkoCandle);

                    int     times;
                    bool    isUp;
                    decimal openPrice;

                    if (price < currentRenkoCandle.LowPrice)
                    {
                        times     = (int)((currentRenkoCandle.LowPrice - price) / renkoStep) + 1;
                        isUp      = false;
                        openPrice = currentRenkoCandle.LowPrice;
                    }
                    else
                    {
                        times     = (int)((price - currentRenkoCandle.HighPrice) / renkoStep) + 1;
                        isUp      = true;
                        openPrice = currentRenkoCandle.HighPrice;
                    }

                    for (var i = 0; i < times; i++)
                    {
                        if (isUp)
                        {
                            currentRenkoCandle = CreateCandle(subscription, buildFrom, boxSize, openPrice, renkoStep, price, volume, side, time, oi);
                            yield return(currentRenkoCandle);

                            openPrice += renkoStep;
                        }
                        else
                        {
                            currentRenkoCandle = CreateCandle(subscription, buildFrom, boxSize, openPrice, -renkoStep, price, volume, side, time, oi);
                            yield return(currentRenkoCandle);

                            openPrice -= renkoStep;
                        }

                        currentRenkoCandle.State = CandleStates.Finished;
                    }

                    currentRenkoCandle.State = CandleStates.Active;
                }
            }
        }
예제 #12
0
 /// <summary>
 /// To create a new candle.
 /// </summary>
 /// <param name="subscription">Subscription.</param>
 /// <param name="transform">The data source transformation.</param>
 /// <returns>Created candle.</returns>
 protected virtual TCandleMessage CreateCandle(ICandleBuilderSubscription subscription, ICandleBuilderValueTransform transform)
 => throw new NotSupportedException(LocalizedStrings.Str637);
예제 #13
0
        /// <inheritdoc />
        protected override IEnumerable <PnFCandleMessage> OnProcess(ICandleBuilderSubscription subscription, ICandleBuilderValueTransform transform)
        {
            var currentPnFCandle = (PnFCandleMessage)subscription.CurrentCandle;

            var price     = transform.Price;
            var volume    = transform.Volume;
            var time      = transform.Time;
            var side      = transform.Side;
            var oi        = transform.OpenInterest;
            var buildFrom = transform.BuildFrom;

            var pnf     = subscription.Message.GetArg <PnFArg>();
            var pnfStep = (decimal)(1 * pnf.BoxSize);

            if (currentPnFCandle == null)
            {
                var openPrice = price.Floor(pnfStep);
                var highPrice = openPrice + pnfStep;

                currentPnFCandle = CreateCandle(subscription, buildFrom, pnf, openPrice, highPrice, openPrice, highPrice, price, volume, side, time, oi);
                yield return(currentPnFCandle);
            }
            else
            {
                if (currentPnFCandle.LowPrice <= price && price <= currentPnFCandle.HighPrice)
                {
                    UpdateCandle(currentPnFCandle, price, volume, time, side, oi, subscription.VolumeProfile);
                    yield return(currentPnFCandle);
                }
                else
                {
                    var isX = currentPnFCandle.OpenPrice < currentPnFCandle.ClosePrice;

                    if (isX)
                    {
                        if (price > currentPnFCandle.HighPrice)
                        {
                            currentPnFCandle.HighPrice = currentPnFCandle.ClosePrice = price.Floor(pnfStep) + pnfStep;
                            UpdateCandle(currentPnFCandle, price, volume, time, side, oi, subscription.VolumeProfile);
                            yield return(currentPnFCandle);
                        }
                        else if (price < (currentPnFCandle.HighPrice - pnfStep * pnf.ReversalAmount))
                        {
                            currentPnFCandle.State = CandleStates.Finished;
                            yield return(currentPnFCandle);

                            var highPrice = currentPnFCandle.HighPrice - pnfStep;
                            var lowPrice  = price.Floor(pnfStep);

                            currentPnFCandle = CreateCandle(subscription, buildFrom, pnf, highPrice, highPrice, lowPrice, lowPrice, price, volume, side, time, oi);
                            yield return(currentPnFCandle);
                        }
                        else
                        {
                            UpdateCandle(currentPnFCandle, price, volume, time, side, oi, subscription.VolumeProfile);
                            yield return(currentPnFCandle);
                        }
                    }
                    else
                    {
                        if (price < currentPnFCandle.LowPrice)
                        {
                            currentPnFCandle.LowPrice = currentPnFCandle.ClosePrice = price.Floor(pnfStep);
                            UpdateCandle(currentPnFCandle, price, volume, time, side, oi, subscription.VolumeProfile);
                            yield return(currentPnFCandle);
                        }
                        else if (price > (currentPnFCandle.LowPrice + pnfStep * pnf.ReversalAmount))
                        {
                            currentPnFCandle.State = CandleStates.Finished;
                            yield return(currentPnFCandle);

                            var highPrice = price.Floor(pnfStep) + pnfStep;
                            var lowPrice  = currentPnFCandle.LowPrice + pnfStep;

                            currentPnFCandle = CreateCandle(subscription, buildFrom, pnf, lowPrice, highPrice, lowPrice, highPrice, price, volume, side, time, oi);
                            yield return(currentPnFCandle);
                        }
                        else
                        {
                            UpdateCandle(currentPnFCandle, price, volume, time, side, oi, subscription.VolumeProfile);
                            yield return(currentPnFCandle);
                        }
                    }
                }
            }
        }
예제 #14
0
 /// <inheritdoc />
 protected override bool IsCandleFinishedBeforeChange(ICandleBuilderSubscription subscription, RangeCandleMessage candle, ICandleBuilderValueTransform transform)
 {
     return((decimal)(candle.LowPrice + candle.PriceRange) <= candle.HighPrice);
 }
예제 #15
0
 /// <inheritdoc />
 protected override bool IsCandleFinishedBeforeChange(ICandleBuilderSubscription subscription, VolumeCandleMessage candle, ICandleBuilderValueTransform transform)
 {
     return(candle.TotalVolume >= candle.Volume);
 }
예제 #16
0
 /// <inheritdoc />
 protected override bool IsCandleFinishedBeforeChange(ICandleBuilderSubscription subscription, TickCandleMessage candle, ICandleBuilderValueTransform transform)
 {
     return(candle.TotalTicks != null && candle.TotalTicks.Value >= candle.MaxTradeCount);
 }
예제 #17
0
 /// <inheritdoc />
 protected override bool IsCandleFinishedBeforeChange(ICandleBuilderSubscription subscription, HeikinAshiCandleMessage candle, ICandleBuilderValueTransform transform)
 {
     return(transform.Time < candle.OpenTime || (candle.OpenTime + candle.TimeFrame) <= transform.Time);
 }
예제 #18
0
        /// <summary>
        /// To update the candle data.
        /// </summary>
        /// <param name="subscription">Subscription.</param>
        /// <param name="candle">Candle.</param>
        /// <param name="transform">The data source transformation.</param>
        protected virtual void UpdateCandle(ICandleBuilderSubscription subscription, TCandleMessage candle, ICandleBuilderValueTransform transform)
        {
            if (candle == null)
            {
                throw new ArgumentNullException(nameof(candle));
            }

            if (transform == null)
            {
                throw new ArgumentNullException(nameof(transform));
            }

            var price  = transform.Price;
            var time   = transform.Time;
            var volume = transform.Volume;

            if (price < candle.LowPrice)
            {
                candle.LowPrice  = price;
                candle.LowTime   = time;
                candle.LowVolume = volume;
            }

            if (price > candle.HighPrice)
            {
                candle.HighPrice  = price;
                candle.HighTime   = time;
                candle.HighVolume = volume;
            }

            candle.ClosePrice = price;

            if (volume != null)
            {
                var v = volume.Value;

                candle.TotalPrice += price * v;

                candle.CloseVolume  = v;
                candle.TotalVolume += v;

                var dir = transform.Side;
                if (dir != null)
                {
                    candle.RelativeVolume = (candle.RelativeVolume ?? 0) + (dir.Value == Sides.Buy ? v : -v);
                }
            }

            candle.CloseTime = time;

            candle.OpenInterest = transform.OpenInterest;

            if (transform.PriceLevels != null)
            {
                if (candle.PriceLevels == null)
                {
                    candle.PriceLevels = transform.PriceLevels;
                }
                else
                {
                    var dict = candle.PriceLevels.ToDictionary(l => l.Price);

                    foreach (var level in transform.PriceLevels)
                    {
                        if (dict.TryGetValue(level.Price, out var currLevel))
                        {
                            currLevel.BuyCount    += level.BuyCount;
                            currLevel.SellCount   += level.SellCount;
                            currLevel.BuyVolume   += level.BuyVolume;
                            currLevel.SellVolume  += level.SellVolume;
                            currLevel.TotalVolume += level.TotalVolume;

                            if (level.BuyVolumes != null)
                            {
                                if (currLevel.BuyVolumes == null)
                                {
                                    currLevel.BuyVolumes = level.BuyVolumes.ToArray();
                                }
                                else
                                {
                                    currLevel.BuyVolumes = currLevel.BuyVolumes.Concat(level.BuyVolumes).ToArray();
                                }
                            }

                            if (currLevel.SellVolumes != null && level.SellVolumes != null)
                            {
                                if (currLevel.SellVolumes == null)
                                {
                                    currLevel.SellVolumes = level.SellVolumes.ToArray();
                                }
                                else
                                {
                                    currLevel.SellVolumes = currLevel.SellVolumes.Concat(level.SellVolumes).ToArray();
                                }
                            }
                        }
                        else
                        {
                            dict.Add(level.Price, level);
                        }
                    }

                    candle.PriceLevels = dict.Values.ToArray();
                }
            }

            if (candle.TotalTicks != null)
            {
                candle.TotalTicks++;
            }
            else
            {
                candle.TotalTicks = 1;
            }
        }
예제 #19
0
 /// <summary>
 /// Whether the candle is created before data adding.
 /// </summary>
 /// <param name="subscription">Subscription.</param>
 /// <param name="candle">Candle.</param>
 /// <param name="transform">The data source transformation.</param>
 /// <returns><see langword="true" /> if the candle should be finished. Otherwise, <see langword="false" />.</returns>
 protected virtual bool IsCandleFinishedBeforeChange(ICandleBuilderSubscription subscription, TCandleMessage candle, ICandleBuilderValueTransform transform)
 => false;