Table of Contents

Custom type of candle

S# allows you to expand the possibilities of building candles, giving the opportunity to work with arbitrary candle types. This is useful when you want to work with candles that are not currently supported by S#. Below is a plan of action for the addition of tick candles (candles, which are generated by the number of trades).

The implementation of tick candles

  1. First, you need to create your own candle type. The type must inherit from the Candle class:

    Caution

    Tick candles are supported by S# as standard and this step is presented only as an example.

    /// <summary>
    /// Time-frame candle
    /// </summary>
    public class TickCandle : Candle
    {
        /// <summary>
        /// The candle parameter
        /// </summary>
        public override object Arg
        {
            get
            {
                return this.TradeCount;
            }
            set
            {
                this.TradeCount = (int) value;
            }
        }
        /// <summary>
        /// Maximum tick count
        /// </summary>
        public int TradeCount { get; set; }
    }
    
  2. Additionally, you need to create your own candle message type. More about Messages. The type must inherit from the CandleMessage class:

    /// <summary>
    /// Tick candle
    /// </summary>
    public class TickCandleMessage : CandleMessage
    {
     public TickCandleMessage()
     	: base(MessageTypes.CandleTick)
     {
     }
     /// <summary>
     /// Maximum tick count
     /// </summary>
     public int MaxTradeCount { get; set; }
     /// <summary>
     /// Clone <see cref="TickCandleMessage"/>.
     /// </summary>
     /// <returns>Clone</returns>
     public override Message Clone()
     {
     	return CopyTo(new TickCandleMessage
     	{
     		MaxTradeCount = MaxTradeCount
     	});
     }
     /// <summary>
     /// The candle parameter
     /// </summary>
     public override object Arg
     {
     	get => MaxTradeCount;
     	set => MaxTradeCount = (int)value;
     }
    }
    
    1. Next, you need to create a candle builder for a new candle type. To do this, you need to create the CandleBuilder<TCandleMessage>. implementation. The CandleBuilder<TCandleMessage>.ProcessValue(StockSharp.Algo.Candles.Compression.ICandleBuilderSubscription subscription, StockSharp.Algo.Candles.Compression.ICandleBuilderValueTransform transform ) method will receive a value of the ICandleBuilderValueTransform. type. Depending on the settings, it can contain data about both the TickCandleBuilderValueTransform, tick trade and the QuoteCandleBuilderValueTransform.

    The CandleBuilder<TCandleMessage>.ProcessValue(StockSharp.Algo.Candles.Compression.ICandleBuilderSubscription subscription, StockSharp.Algo.Candles.Compression.ICandleBuilderValueTransform transform ) method shall return either a new candle (if new data led to the candle generation), or update the passed one (if the data is not enough to create a new candle). If the CandleBuilder<TCandleMessage>.ProcessValue(StockSharp.Algo.Candles.Compression.ICandleBuilderSubscription subscription, StockSharp.Algo.Candles.Compression.ICandleBuilderValueTransform transform ) method returns a new candle, CandleBuilder<TCandleMessage> calls it again, passing the same ICandleBuilderValueTransform. value to the method. The method will be called until CandleBuilder<TCandleMessage>.ProcessValue(StockSharp.Algo.Candles.Compression.ICandleBuilderSubscription subscription, StockSharp.Algo.Candles.Compression.ICandleBuilderValueTransform transform ) returns the passed candle. This is done for those cases when for one ICandleBuilderValueTransform input value can be generated several candles:

    /// <summary>
    /// The builder of candles of <see cref="T:StockSharp.Algo.Candles.TickCandle" />.
    /// </summary>
    public class TickCandleBuilder : CandleBuilder<TickCandleMessage>
    {
        /// <summary>
        /// Create <see cref="T:StockSharp.Algo.Candles.Compression.TickCandleBuilder" />.
        /// </summary>
        public TickCandleBuilder()
        {
        }
        /// <summary>
        /// Create <see cref="T:StockSharp.Algo.Candles.Compression.TickCandleBuilder" />.
        /// </summary>
        public TickCandleBuilder()
        {
        }
        /// <summary>
        /// To create a new candle
        /// </summary>
        /// <param name="series">Candles series</param>
        /// <param name="transform">Data with which a new candle should be created</param>
        /// <returns>Created candle</returns>
        protected override TickCandle CreateCandle(CandleSeries series, ICandleBuilderValueTransform transform)
        {
            var candle = new TickCandleMessage
            {
                TradeCount = (int)series.Arg,
                OpenTime = transform.Time,
                CloseTime = transform.Time
            };
            return this.FirstInitCandle(series, candle, transform);
        }
        /// <summary>
        /// To get time ranges for which this source of passed candles series has data
        /// </summary>
        /// <param name="series">Candles series</param>
        /// <returns>Time ranges.</returns>
        public override IEnumerable<Range<DateTime>> GetSupportedRanges(CandleSeries series)
        {
            IEnumerable<Range<DateTime>> supportedRanges = base.GetSupportedRanges(series);
            if (!supportedRanges.IsEmpty<Range<DateTime>>())
            {
                if (!(series.Arg is int))
                {
                    throw new ArgumentException();
                }
                if (((int) series.Arg) <= 0)
                {
                    throw new ArgumentOutOfRangeException();
                }
            }
            return supportedRanges;
        }
        /// <summary>
        /// Whether the candle is created before data adding
        /// </summary>
        /// <param name="series">Candles series.</param>
        /// <param name="candle">Candle</param>
        /// <param name="transform">Data by which it is decided to end the current candle creation.</param>
        /// <returns>True, if the candle should be finished. Otherwise, false.</returns>
        protected override bool IsCandleFinishedBeforeChange(CandleSeries series, TickCandleMessage candle, ICandleBuilderValueTransform transform)
        {
            return candle.TotalTicks != null && candle.TotalTicks.Value >= candle.MaxTradeCount;
        }
        /// <summary>
        /// To update the candle data.
        /// </summary>
        /// <param name="series">Candles series.</param>
        /// <param name="candle">Candle.</param>
        /// <param name="transform">Data.</param>
        protected override void UpdateCandle(CandleSeries series, TickCandleMessage candle, ICandleBuilderValueTransform transform)
        {
     	base.UpdateCandle(series, candle, transform);
     	candle.TotalTicks++;
        }
    }
    
  3. Then, you need to get the CandleBuilderProvider from the connection, and add to it: TickCandleBuilder:

    Caution

    TickCandleBuilder, as a candle source, is normally present in CandleBuilderProvider. This step is presented only as an example.

    private Connector _connector;
    ...
    _connector.Adapter.CandleBuilderProvider.Register(new TickCandleBuilder());
    
  4. Create a CandleSeries and request data on it:

    var series = new CandleSeries(typeof(TickCandle), _security, 1000);
    ...
    _connector.SubscribeCandles(series);