Bot ignored code instructions and blown my account in commissions!
Bot ignored code instructions and blown my account in commissions!
08 Jan 2025, 22:56
On 17th Dec I had Ctrader bot (instance ID 122928) running on my live account with the following logic:
H4 timeframe
Calculate Z-score deviation between GBPUSD and EURUSD over 210 periods
On each tick: If Z>1.8 and there are no open trades then sell 20000 of GBPUSD. Set stop loss=60pips, take profit=290pips, max trade duration=6000minutes
Trade close triggers: if Z<=0.1 OR trade duration exceeded OR SL hit OR TP hit
This bot was started on 13th Dec and worked fine until 17/12/24 17:24:51. At that time it opened first trade and closed it ~700ms later after price moved by less than 1 pip and none of my trade close triggers hit. It then continued performing the same set of actions with trades lasting 0.3-5sec until all my account balance was spent on spread and commissions (40mins and 1166trades later).
The bot log has shown two messages for most trades: "trade duration exceeded" (not true as my trigger was set at 6000mins) and "technical error". - obviously this doesn't tell me much.
I suspect that the z-score did hit my trade entry trigger of 1.8 on that day but it should've opened the sell position once and stayed in it since none of the close triggers have been hit. Instead, it ignored the code logic and kept closing and reopening trades.
Ctrader interface doesn't provide any server execution logs so I have no idea what went wrong and if it was something I could've accounted for in my code. Or was it Calgo glitch? Can you review from your end and help me get to the root cause?
Here is the bot code:
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.Indicators;
using cAlgo.API.Internals;
using System.Linq;
using System;
//using System.Net;
namespace cAlgo
{
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class ZScoreTradingBot : Robot
{
private Z_score _zScoreIndicator;
[Parameter("Symbol 1", DefaultValue = "EURUSD.P")]
public string Symbol1 { get; set; }
[Parameter("Symbol 2", DefaultValue = "GBPUSD.P")]
public string Symbol2 { get; set; }
[Parameter("Lookback Period", DefaultValue = 30)]
public int LookbackPeriod { get; set; }
[Parameter("Order Volume1", DefaultValue = 100000)]
public int OrderVolume1 { get; set; }
[Parameter("Order Volume2", DefaultValue = 100000)]
public int OrderVolume2 { get; set; }
[Parameter("Z-Score Threshold", DefaultValue = 2.0)]
public double ZScoreThreshold { get; set; }
[Parameter("Stop Loss1 (pips)", DefaultValue = 40, MinValue = 1)]
public int StopLossPips1 { get; set; }
[Parameter("Stop Loss2 (pips)", DefaultValue = 40, MinValue = 1)]
public int StopLossPips2 { get; set; }
[Parameter("Take Profit1 (pips)", DefaultValue = 20, MinValue = 1)]
public int TakeProfitPips1 { get; set; }
[Parameter("Take Profit2 (pips)", DefaultValue = 20, MinValue = 1)]
public int TakeProfitPips2 { get; set; }
[Parameter("Label", DefaultValue = "PairsTradingBot")]
public string Label { get; set; }
[Parameter("Max Trade Duration (min)", DefaultValue = 60)]
public int MaxTradeDuration { get; set; }
private DateTime tradeEntryTime;
public Position[] BotPositions
{
get
{
return Positions.FindAll(Label);
}
}
protected override void OnStart()
{
//var result = System.Diagnostics.Debugger.Launch();
//if(result is false)
//{
//Print("Debugger launch failed");
//}
_zScoreIndicator = Indicators.GetIndicator<Z_score>(Symbol1, Symbol2, LookbackPeriod);
}
protected override void OnTick()
{
double zScore = _zScoreIndicator.ZScore.LastValue;
// Count the number of open positions
int openPositionCount = 0;
foreach (Position pos in Positions)
{
if (pos.TradeType == TradeType.Buy || pos.TradeType == TradeType.Sell)
{
openPositionCount++;
}
}
// Check if the open position count is zero
if (openPositionCount == 0)
{
if (zScore < -ZScoreThreshold)
{
Print(zScore);
ExecuteMarketOrder(TradeType.Buy, Symbol1, OrderVolume1, "Buy order", StopLossPips1,TakeProfitPips1, null);
//ExecuteMarketOrder(TradeType.Sell, Symbol2, OrderVolume2, "Sell order", StopLossPips2, TakeProfitPips2, null);
tradeEntryTime = Server.Time;
}
else if (zScore > ZScoreThreshold)
{
Print(zScore);
ExecuteMarketOrder(TradeType.Sell, Symbol1, OrderVolume1, "Sell order", StopLossPips1,TakeProfitPips1, null);
//ExecuteMarketOrder(TradeType.Buy, Symbol2, OrderVolume2, "Buy order", StopLossPips2, TakeProfitPips2, null);
tradeEntryTime = Server.Time;
}
}
// Check if Z-score has reverted to 0
if (openPositionCount >= 1)
{
if (zScore < 0.1 && zScore > -0.1 )
{
Print("z=",(zScore), " Closing all positions...");
CloseAllPositions();
}
else if ((Server.Time - tradeEntryTime).TotalMinutes > MaxTradeDuration)
{
Print("z=",(zScore), "Trade duration exceeded. Closing all positions...");
//Print("z=",(zScore));
CloseAllPositions();
}
}
void CloseAllPositions()
{
foreach (var pos in Positions)
{
ClosePosition(pos);
}
}
}
}
}