cBot failure in backtesting

Created at 14 Jun 2024, 10:37
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!
CT

ctid7981400

Joined 14.06.2024

cBot failure in backtesting
14 Jun 2024, 10:37


My cbot doesn't seem able to go beyond 2 trades. Can anyone help review the code and show me where I'm going wrong please?

Here's the code:
 // Calculate pips - now sl and tp are being set correctly. But only 3 trades!!!



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

namespace cAlgo
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class CustomBot : Robot
    {
        [Parameter("Lookback Period", DefaultValue = 100)]
        public int LookbackPeriod { get; set; }

        [Parameter("Lot Size", DefaultValue = 10000)]
        public double LotSize { get; set; }

        [Parameter("Enable SAR Strategy", DefaultValue = true)]
        public bool EnableSARStrategy { get; set; }

        [Parameter("Enable ADX Strategy", DefaultValue = true)]
        public bool EnableADXStrategy { get; set; }

        private bool sarEntryTriggered;
        private bool adxEntryTriggered;

        protected override void OnStart()
        {
            Print("Bot started.");
        }

        protected override void OnBar()
        {
            var symbol = Symbol.Code;
            var series = MarketData.GetSeries(symbol, TimeFrame);

            if (series == null || series.Close.Count < LookbackPeriod)
            {
                Print($"Not enough data for {symbol} {TimeFrame}.");
                return;
            }

            Print($"Checking {symbol} {TimeFrame}.");

            var openPosition = Positions.Find(symbol, Symbol);

            if (openPosition != null)
            {
                Print($"Open Position Found: Type: {openPosition.TradeType}, Entry Price: {openPosition.EntryPrice}, Current Price: {MarketSeries.Close.Last(1)}, Stop Loss: {openPosition.StopLoss}, Take Profit: {openPosition.TakeProfit}");
                ModifyOpenPositions(symbol, openPosition);
                return; // Skip further processing until the position is closed
            }

            Print("No open position found.");

            if (HasPendingOrders(symbol))
            {
                CancelPendingOrders(symbol); // Cancel pending orders
            }

            int index = series.Close.Count - 1;

            var currentBar = GetCandle(series, index);
            var previousBar = GetCandle(series, index - 1);
            var previousBar1 = GetCandle(series, index - 2);

            var atr = Indicators.AverageTrueRange(series, 8, MovingAverageType.Simple);
            var sar = Indicators.ParabolicSAR(series, 0.03, 0.13);
            var adx = Indicators.DirectionalMovementSystem(series, 2);

            double atrValue = atr.Result.Last(1);
            double sarValue = sar.Result.Last(1);
            double sarValue1 = sar.Result.Last(2);

            double multiplier = (adx.DIPlus.Last(1) >= adx.DIMinus.Last(1)) ? 1 : -1;
            double adxValue = adx.ADX.Last(1) * multiplier;

            double multiplier1 = (adx.DIPlus.Last(2) >= adx.DIMinus.Last(2)) ? 1 : -1;
            double adxValue1 = adx.ADX.Last(2) * multiplier1;

            string sarState = (sarValue > currentBar.Close) ? "0" : "1";
            string sarState1 = (sarValue1 > previousBar.Close) ? "0" : "1";
            string sarSwitch = (sarState != sarState1) ? sarState : "-";

            string adxState = (adxValue >= 0) ? "1" : "0";
            string adxState1 = (adxValue1 >= 0) ? "1" : "0";
            string adxSwitch = (adxState != adxState1) ? adxState : "-";

            Print($"Current Bar: Time: {currentBar.OpenTime}, Open: {currentBar.Open}, High: {currentBar.High}, Low: {currentBar.Low}, Close: {currentBar.Close}, Volume: {currentBar.Volume}");
            Print($"SAR Values: Current: {sarValue}, Previous: {sarValue1}, SAR State: {sarState}, SAR Switch: {sarSwitch}");
            Print($"ADX Values: Current: {adxValue}, Previous: {adxValue1}, ADX State: {adxState}, ADX Switch: {adxSwitch}");

            if (sarSwitch != "-" || adxSwitch != "-")
            {
                CheckForNewEntrySignals(symbol, currentBar, sarState, sarSwitch, atrValue, adxState, adxSwitch);
            }
        }

        private void CheckForNewEntrySignals(string symbol, Candle currentBar, string sarState, string sarSwitch, double atrValue, string adxState, string adxSwitch)
        {
            if (EnableSARStrategy && !sarEntryTriggered && sarSwitch != "-")
            {
                double entryPrice = (sarSwitch == "1") ? currentBar.High + 0.33 * atrValue : currentBar.Low - 0.33 * atrValue;
                double stopLossPrice = (sarSwitch == "1") ? entryPrice - 2 * atrValue : entryPrice + 2 * atrValue;
                double takeProfitPrice = (sarSwitch == "1") ? entryPrice + 4 * atrValue : entryPrice - 4 * atrValue;

                var tradeType = (sarSwitch == "1") ? TradeType.Buy : TradeType.Sell;
                Print($"Attempting to place SAR entry order: Type: {tradeType}, Symbol: {symbol}, EntryPrice: {entryPrice}, StopLoss: {stopLossPrice}, TakeProfit: {takeProfitPrice}");

                // Calculate pips
                double stopLossPips = Math.Abs((entryPrice - stopLossPrice) / Symbol.PipSize);
                double takeProfitPips = Math.Abs((takeProfitPrice - entryPrice) / Symbol.PipSize);

                // Place stop order
                PlaceStopOrder(tradeType, symbol, VolumeInUnits, entryPrice, "SAR Entry", stopLossPips, takeProfitPips, DateTime.MaxValue);
                
                sarEntryTriggered = true;
            }

            if (EnableADXStrategy && !adxEntryTriggered && adxSwitch != "-")
            {
                double entryPrice = (adxSwitch == "1") ? currentBar.High + 0.33 * atrValue : currentBar.Low - 0.33 * atrValue;
                double stopLossPrice = (adxSwitch == "1") ? entryPrice - 2 * atrValue : entryPrice + 2 * atrValue;
                double takeProfitPrice = (adxSwitch == "1") ? entryPrice + 4 * atrValue : entryPrice - 4 * atrValue;

                var tradeType = (adxSwitch == "1") ? TradeType.Buy : TradeType.Sell;
                Print($"Attempting to place ADX entry order: Type: {tradeType}, Symbol: {symbol}, EntryPrice: {entryPrice}, StopLoss: {stopLossPrice}, TakeProfit: {takeProfitPrice}");

                // Calculate pips
                double stopLossPips = Math.Abs((entryPrice - stopLossPrice) / Symbol.PipSize);
                double takeProfitPips = Math.Abs((takeProfitPrice - entryPrice) / Symbol.PipSize);

                // Place stop order
                PlaceStopOrder(tradeType, symbol, VolumeInUnits, entryPrice, "ADX Entry", stopLossPips, takeProfitPips, DateTime.MaxValue);
                
                adxEntryTriggered = true;
            }
        }

        private bool HasPendingOrders(string symbol)
        {
            foreach (var pendingOrder in PendingOrders)
            {
                if (pendingOrder.SymbolCode == symbol)
                {
                    return true;
                }
            }
            return false;
        }

        private void CancelPendingOrders(string symbol)
        {
            foreach (var pendingOrder in PendingOrders)
            {
                if (pendingOrder.SymbolCode == symbol)
                {
                    Print($"Canceling pending order: ID: {pendingOrder.Id}, Type: {pendingOrder.TradeType}, Symbol: {pendingOrder.SymbolCode}, Label: {pendingOrder.Label}");
                    CancelPendingOrder(pendingOrder);
                    sarEntryTriggered = false; // Reset flag after cancelling pending order
                    adxEntryTriggered = false; // Reset flag after cancelling pending order
                }
            }
        }

 /*       private void ModifyOpenPositions(string symbol, Position position)
        {
            double atrValue = GetATRValue(symbol);
            double newStopLoss = position.TradeType == TradeType.Buy
                ? MarketSeries.Low.Last(1) - 0.33 * atrValue
                : MarketSeries.High.Last(1) + 0.33 * atrValue;

            if (position.StopLoss != newStopLoss)
            {
                Print($"Modifying position {position.Label}. New Stop Loss: {newStopLoss}");
                ModifyPosition(position, newStopLoss, position.TakeProfit);
            }
        }
*/

private void ModifyOpenPositions(string symbol, Position position)
{
    double atrValue = GetATRValue(symbol);
    double newStopLoss = position.TradeType == TradeType.Buy
        ? MarketSeries.Low.Last(1) - 0.33 * atrValue
        : MarketSeries.High.Last(1) + 0.33 * atrValue;

    if (position.StopLoss != newStopLoss)
    {
        Print($"Modifying position {position.Label}. New Stop Loss: {newStopLoss}");
        ModifyPosition(position, newStopLoss, position.TakeProfit);

        // Reset entry triggered flag if position is modified
        if (position.VolumeInUnits  == 0) // Check if position is closed
        {
            sarEntryTriggered = false;
            adxEntryTriggered = false;
        }
    }
}


        private Candle GetCandle(MarketSeries series, int index)
        {
            return new Candle
            {
                OpenTime = series.OpenTime[index],
                Open = series.Open[index],
                High = series.High[index],
                Low = series.Low[index],
                Close = series.Close[index],
                Volume = series.TickVolume[index]
            };
        }

        private double VolumeInUnits => Symbol.QuantityToVolumeInUnits(LotSize);

        private double GetATRValue(string symbol)
       
        {
            var series = MarketData.GetSeries(symbol, TimeFrame);
            var atr = Indicators.AverageTrueRange(series, 8, MovingAverageType.Simple);
            return atr.Result.Last(1);
        }

        public class Candle
        {
            public DateTime OpenTime { get; set; }
            public double Open { get; set; }
            public double High { get; set; }
            public double Low { get; set; }
            public double Close { get; set; }
            public double Volume { get; set; }
        }
    }
}


@ctid7981400