synchronization issue between the indicator and the cBot

Created at 11 Mar 2025, 18:56
How’s your experience with the cTrader Platform?
Your feedback is crucial to cTrader's development. Please take a few seconds to share your opinion and help us improve your trading experience. Thanks!
FA

Falcorest

Joined 01.10.2024

synchronization issue between the indicator and the cBot
11 Mar 2025, 18:56


Hello everyone,

I need your help. I'm sure it's something trivial, but I'm just starting out, so I hope you'll forgive me:

So, I tried to write the code for an indicator and then created a cBot with the same logic, but unfortunately, I'm having trouble managing the indication of the last candle to use for calculations.

The logic behind the indicator is very simple:
First, I calculate a channel made up of highs and lows over a specific period, excluding the last candle.

If a candle closes outside the channel, I consider it a breakout signal and open a position following the trend.

The problem is that while for the indicator using the index is sufficient, for the bot I'm using Bars.Count - 1, and this makes me unsure about which candle the bot is actually considering.

In fact, when running a simple backtest of the bot and adding the indicator to the chart, I see that sometimes the bot opens positions even when there are no signals.
I'll share both the indicator and bot codes, and I hope someone more experienced than me (it doesn't take much) can help me out.

Thank you very much!


 


using System;
using System.Linq;
using cAlgo.API;
using cAlgo.API.Collections;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;

namespace cAlgo
{
    [Indicator(IsOverlay = true, AccessRights = AccessRights.None)]
    public class BreakingBoundIndicator : Indicator
    {
        
        [Parameter("Channel Lenght", DefaultValue = 25)]
        public int ChLenght { get; set; }
        
        [Parameter("Skip Amount", DefaultValue = 1, MinValue = 1, Step = 1)]
        public int SkipAmount { get; set; } = 1;

        // OUTPUT
        [Output("UpBoundPlot", LineColor = "gray", PlotType = PlotType.Line, Thickness = 1)]
        public IndicatorDataSeries UpBoundPlot { get; set; }

        [Output("DownBoundPlot", LineColor = "gray", PlotType = PlotType.Line, Thickness = 1)]
        public IndicatorDataSeries DownBoundPlot { get; set; }

        private int lastCandle;
        
        private double upBound;
        private double downBound;

        protected override void Initialize()
        {
        
        }

        public override void Calculate(int index)
        {
            
            lastCandle = index;
            
            //Channel     
            upBound = Bars.HighPrices.TakeLast(ChLenght + 1).SkipLast(SkipAmount).Max();
            downBound = Bars.LowPrices.TakeLast(ChLenght + 1).SkipLast(SkipAmount).Min();

            bool breakUp = Bars.ClosePrices[lastCandle] > upBound;
            bool breakDown = Bars.ClosePrices[lastCandle] < downBound;
            

            if (breakUp)
            {
                Chart.DrawIcon("Buy" + lastCandle, ChartIconType.UpTriangle, lastCandle, Bars.LowPrices.Last(), Color.Green);
            }
        
            if (breakDown)
            {
                Chart.DrawIcon("Sell" + lastCandle, ChartIconType.DownTriangle, lastCandle, Bars.HighPrices.Last(), Color.Red);
            }  
            
            // Plot
            UpBoundPlot[lastCandle] = upBound;
            DownBoundPlot[lastCandle] = downBound;
                
        }
    }
}

 

using System;
using System.Linq;
using cAlgo.API;
using cAlgo.API.Collections;

namespace cAlgo
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class BreakingBoundsBot : Robot
    {
             
        [Parameter("Lots", DefaultValue = 0.5, Group = "Trade", Step = 0.05)]
        public double Quantity { get; set; }
        
        [Parameter("Take Profit (Pips)", DefaultValue = 20, Group = "Trade", Step = 1)]
        public double TakeProfitInPips { get; set; }

        [Parameter("Stop Loss (Pips)", DefaultValue = 18, Group = "Trade", Step = 1)]
        public double StopLossInPips { get; set; }
        
        [Parameter("Channel Lenght", DefaultValue = 25)]
        public int ChLenght { get; set; }
        
        [Parameter("Skip Amount", DefaultValue = 1, MinValue = 1, Step = 1)]
        public int SkipAmount { get; set; } = 1;

        private int lastCandle;
        private string LabelBuy = "BBB-Buy";
        private string LabelSell = "BBB-Sell";
        
        private double upBound;
        private double downBound;

        protected override void OnStart()
        {

        }


        protected override void OnBar()
        {  
            var volume = Symbol.QuantityToVolumeInUnits(Quantity);
            volume = Symbol.NormalizeVolumeInUnits(volume, RoundingMode.Down);
            
            lastCandle = Bars.Count - 1;
            
            //Channel     
            upBound = Bars.HighPrices.TakeLast(ChLenght + 1).SkipLast(SkipAmount).Max();
            downBound = Bars.LowPrices.TakeLast(ChLenght + 1).SkipLast(SkipAmount).Min();

            bool breakUp = Bars.ClosePrices[lastCandle] > upBound;
            bool breakDown = Bars.ClosePrices[lastCandle] < downBound;
            

            if (breakUp)
            {
                ExecuteMarketOrder(TradeType.Buy, SymbolName, volume, LabelBuy, StopLossInPips, TakeProfitInPips);
                Chart.DrawIcon("Buy" + lastCandle, ChartIconType.UpTriangle, lastCandle, Bars.LowPrices.Last(), Color.Green);
            }
        
            if (breakDown)
            {
                ExecuteMarketOrder(TradeType.Sell, SymbolName, volume, LabelSell, StopLossInPips, TakeProfitInPips);
                Chart.DrawIcon("Sell" + lastCandle, ChartIconType.DownTriangle, lastCandle, Bars.HighPrices.Last(), Color.Red);                
            }       
        
        }
    }
}
 

@Falcorest