Why is my cbot lagging a few candles than tradingview?

Created at 24 Jan 2025, 16:17
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!
DU

duynguyenhl.bio

Joined 14.12.2024

Why is my cbot lagging a few candles than tradingview?
24 Jan 2025, 16:17


Hello,
I created a cBot (Full code below) based on an indicator on TradingView (Range Filter Buy and Sell 5min - @guikroth ver), but the signals on the cBot are delayed compared to those on TradingView. I have tried to figure it out but still can't understand the reason. Could anyone help me? Thank you!

Green lines are signal on TradingView, Yellow lines are signal on Cbot

 

using System;
using cAlgo.API;
using cAlgo.API.Indicators;

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class RangeFilterProBot : Robot
    {
        [Parameter("Sampling Period", DefaultValue = 50, MinValue = 10)]
        public int Period { get; set; }

        [Parameter("Range Multiplier", DefaultValue = 3.0, MinValue = 0.5)]
        public double Multiplier { get; set; }

        [Parameter("Volume (Lots)", DefaultValue = 0.1, MinValue = 0.01)]
        public double Volume { get; set; }

        [Parameter("Stop Loss (pips)", DefaultValue = 300)]
        public int StopLossInPips { get; set; }

        [Parameter("Take Profit (pips)", DefaultValue = 600)]
        public int TakeProfitInPips { get; set; }

        private IndicatorDataSeries _absPriceDiff;
        private ExponentialMovingAverage _ema1, _ema2;
        private double _prevFilter;
        private int _upward, _downward, _lastSignal;

        protected override void OnStart()
        {
            _absPriceDiff = CreateDataSeries();
            _ema1 = Indicators.ExponentialMovingAverage(_absPriceDiff, Period);
            _ema2 = Indicators.ExponentialMovingAverage(_ema1.Result, Period * 2 - 1);
            InitializeHistoricalData();
            Print("Bot is ready!");
        }

        // Initialize historical data for all closed candles
        private void InitializeHistoricalData()
        {
            for (int i = 1; i < Bars.Count; i++)
            {
                _absPriceDiff[i] = Math.Abs(Bars.ClosePrices[i] - Bars.ClosePrices[i - 1]);
            }
            _prevFilter = Bars.ClosePrices.Last(1); // Use previous candle's closing price
        }

        protected override void OnBar()
        {
            try
            {
                int currentIndex = Bars.Count - 1;
                int lastClosedIndex = currentIndex - 1; // Index of the LAST CLOSED candle

                // Skip if insufficient data for EMA stability
                if (lastClosedIndex < Period * 2)
                {
                    Print($"Initializing... ({lastClosedIndex}/{Period * 2})");
                    return;
                }

                // 1. Update price volatility for the CLOSED candle
                _absPriceDiff[lastClosedIndex] = Math.Abs(
                    Bars.ClosePrices[lastClosedIndex] - 
                    Bars.ClosePrices[lastClosedIndex - 1]
                );

                // 2. Calculate Smoothed Range from CLOSED candle data
                double smrng = _ema2.Result[lastClosedIndex] * Multiplier;

                // 3. Update the filter
                double price = Bars.ClosePrices[lastClosedIndex];
                double newFilter = price > _prevFilter 
                    ? Math.Max(_prevFilter, price - smrng) 
                    : Math.Min(_prevFilter, price + smrng);

                // 4. Determine trend direction
                _upward = newFilter > _prevFilter ? _upward + 1 : 0;
                _downward = newFilter < _prevFilter ? _downward + 1 : 0;

                // 5. Trading conditions (processed immediately after candle closes)
                bool shouldBuy = price > newFilter && _upward >= 1;
                bool shouldSell = price < newFilter && _downward >= 1;

                // 6. Execute trades immediately
                ExecuteTrades(shouldBuy, shouldSell, price, newFilter, lastClosedIndex);

                _prevFilter = newFilter;
            }
            catch (Exception ex)
            {
                Print($"Error: {ex.Message}");
            }
        }

        private void ExecuteTrades(bool buy, bool sell, double price, double filter, int barIndex)
        {
            if (buy && _lastSignal != 1)
            {
                ClosePositions(TradeType.Sell);
                OpenTrade(TradeType.Buy, price, filter, barIndex);
                _lastSignal = 1;
            }
            else if (sell && _lastSignal != -1)
            {
                ClosePositions(TradeType.Buy);
                OpenTrade(TradeType.Sell, price, filter, barIndex);
                _lastSignal = -1;
            }
        }

        private void OpenTrade(TradeType type, double price, double filter, int barIndex)
        {
            ExecuteMarketOrder(
                type,
                SymbolName,
                Symbol.QuantityToVolumeInUnits(Volume),
                $"RF_{type}",
                StopLossInPips,
                TakeProfitInPips
            );
            // Log the exact timestamp of the signal candle
            Print($"{(type == TradeType.Buy ? "▲ BUY" : "▼ SELL")} @ {price:F5} | Filter: {filter:F5} | Time: {Bars.OpenTimes[barIndex]}");
        }

        private void ClosePositions(TradeType typeToClose)
        {
            foreach (var pos in Positions.FindAll($"RF_{typeToClose}", SymbolName))
                ClosePosition(pos);
        }

        protected override void OnStop()
        {
            Print("Bot has stopped.");
        }
    }
}

@duynguyenhl.bio