cBot failure in backtesting
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; }
}
}
}