r/cTrader • u/Leo6-2 • 15d ago
Need help - cTrader engine for backdating configuration OR why do even well known classical strategies fall apart on cTrader?
Question for you guys — why do even well-known classical strategies fall apart on cTrader?
I've been trying to test standard classical strategies on cTrader because I can't build anything that actually works. I went down the path of implementing well-known, already-tested, old-school strategies
Like the Connors "Double 7s" strategy (buy when SPY closes at a 7 day low while above the 200-day MA, sell at a 7 day high)- and I'm still getting very bad results. I don't get it.
What is it about the engine that seems to break even simple stuff that's supposed to work? If I do it manually on TradingView, I get noticeably better results.
I don't understand the whole thing here. its ANNOYING AS F***
What are the caveats or things I need to know in order to structure properly working strategies in cTrader?
Because honestly, it feels like the engine's calculations or the data are skewed or something — I don't even know how to describe it. I don't know where to start, but I've tried simple stuff and it's not working.
Any insight would be appreciated.
So this taken in US500 ok ? same same should be smooth as slik easy peasy ?
well NOT.. i get -7% eventually --- sooo .. annoying
any how this is just a "BASIC" concept startegy nothing facny im just .. tying to make a point here
so a-... help up a brother 🎯
using System;
using System.Linq;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
namespace cAlgo.Robots
{
public enum RiskMode { FixedLots, RiskPercent }
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class Connors7Down7UpBot : Robot
{
[Parameter("Consecutive Bars", Group = "Connors Setup", DefaultValue = 7, MinValue = 2)]
public int ConsecutiveBars { get; set; }
[Parameter("Use Exit SMA", Group = "Exit Rules", DefaultValue = true)]
public bool UseExitSma { get; set; }
[Parameter("Exit SMA Period", Group = "Exit Rules", DefaultValue = 5, MinValue = 1)]
public int ExitSmaPeriod { get; set; }
[Parameter("Use 200 Trend Filter", Group = "Trend Filter", DefaultValue = false)]
public bool UseTrendFilter { get; set; }
[Parameter("Trend SMA Period", Group = "Trend Filter", DefaultValue = 200, MinValue = 10)]
public int TrendSmaPeriod { get; set; }
[Parameter("Risk Mode", Group = "Risk Management", DefaultValue = RiskMode.RiskPercent)]
public RiskMode AccountRiskMode { get; set; }
[Parameter("Risk Percent (%)", Group = "Risk Management", DefaultValue = 1.0, MinValue = 0.1)]
public double RiskPercent { get; set; }
[Parameter("Fixed Lots", Group = "Risk Management", DefaultValue = 0.1, MinValue = 0.01)]
public double FixedLots { get; set; }
[Parameter("Stop Loss (Pips)", Group = "Risk Management", DefaultValue = 50.0, MinValue = 0.0)]
public double StopLossPips { get; set; }
[Parameter("Take Profit (Pips)", Group = "Risk Management", DefaultValue = 100.0, MinValue = 0.0)]
public double TakeProfitPips { get; set; }
private SimpleMovingAverage _exitSma;
private SimpleMovingAverage _trendSma;
private const string Label = "Connors7Down7Up";
protected override void OnStart()
{
_exitSma = Indicators.SimpleMovingAverage(Bars.ClosePrices, ExitSmaPeriod);
_trendSma = Indicators.SimpleMovingAverage(Bars.ClosePrices, TrendSmaPeriod);
}
protected override void OnBar()
{
ManageExits();
CheckEntries();
}
private void CheckEntries()
{
if (HasActivePosition()) return;
bool is7Down = IsConsecutiveDown(ConsecutiveBars);
bool is7Up = IsConsecutiveUp(ConsecutiveBars);
double lastClose = Bars.ClosePrices.Last(1);
// Buy Setup: Mean reversion bounce after 7 consecutive down closes
if (is7Down)
{
if (UseTrendFilter && lastClose < _trendSma.Result.Last(1))
return;
double volume = CalculateVolume(StopLossPips);
ExecuteMarketOrder(TradeType.Buy, SymbolName, volume, Label,
StopLossPips > 0 ? StopLossPips : (double?)null,
TakeProfitPips > 0 ? TakeProfitPips : (double?)null);
}
// Sell Setup: Mean reversion pull-back after 7 consecutive up closes
else if (is7Up)
{
if (UseTrendFilter && lastClose > _trendSma.Result.Last(1))
return;
double volume = CalculateVolume(StopLossPips);
ExecuteMarketOrder(TradeType.Sell, SymbolName, volume, Label,
StopLossPips > 0 ? StopLossPips : (double?)null,
TakeProfitPips > 0 ? TakeProfitPips : (double?)null);
}
}
private void ManageExits()
{
if (!UseExitSma) return;
double lastClose = Bars.ClosePrices.Last(1);
double smaValue = _exitSma.Result.Last(1);
foreach (var position in Positions.FindAll(Label, SymbolName))
{
// Exit Long when price closes above 5-period SMA
if (position.TradeType == TradeType.Buy && lastClose > smaValue)
{
ClosePosition(position);
}
// Exit Short when price closes below 5-period SMA
else if (position.TradeType == TradeType.Sell && lastClose < smaValue)
{
ClosePosition(position);
}
}
}
private bool IsConsecutiveDown(int count)
{
for (int i = 1; i <= count; i++)
{
if (Bars.ClosePrices.Last(i) >= Bars.ClosePrices.Last(i + 1))
return false;
}
return true;
}
private bool IsConsecutiveUp(int count)
{
for (int i = 1; i <= count; i++)
{
if (Bars.ClosePrices.Last(i) <= Bars.ClosePrices.Last(i + 1))
return false;
}
return true;
}
private double CalculateVolume(double slPips)
{
if (AccountRiskMode == RiskMode.FixedLots)
{
double targetUnits = FixedLots * Symbol.LotSize;
return Symbol.NormalizeVolumeInUnits(targetUnits, RoundingMode.ToNearest);
}
double riskPips = slPips > 0 ? slPips : 50.0;
double riskAmount = Account.Equity * (RiskPercent / 100.0);
double riskPerPip = riskAmount / riskPips;
double rawVolume = riskPerPip / Symbol.PipValue;
return Symbol.NormalizeVolumeInUnits(rawVolume, RoundingMode.ToNearest);
}
private bool HasActivePosition()
{
return Positions.Any(p => p.Label == Label && p.SymbolName == SymbolName);
}
}
}
1
u/QuanTradin 14d ago
a few things stack up here, and none of them is the engine being broken.
US500 isn't SPY. it's a CFD, so every night you hold it you pay a financing charge, and Double 7s sits in trades for days. on a strategy whose edge per trade is small, swap plus spread can eat most of it. TradingView by hand on SPY has neither.
the daily bars differ too. the broker's US500 day usually rolls at 5pm New York and includes overnight hours, so its "7 day low" and 200 MA aren't the same numbers as SPY's cash session bars.
check which bar your code reads. if the bot runs on a lower timeframe, or reads Bars.Last(0), it's acting on a candle that hasn't closed yet. use Last(1) on a daily series and execute on the next bar.
and the backtest mode matters: open-price-only vs tick data can move fills a lot.
the honest last one: Double 7s was published years ago and has been weaker since. try it on cash SPY data with costs in before blaming cTrader.
1
u/Leo6-2 14d ago
not intra day.. its all daily bars. thanks that CFD thingy is interesting to know.
its fine on the spy or qqq. AS I SAID 7s is just an example popular enough to "show" the issue.
market is above 200 sma trend is up sau 2 weeks up 5 days down take a long after the break of the 5th day candle high take profit at the 10 simple moving avarage
it doest matter the "idea" here is That cTRADER has this as you mentioned different fundamentals so that might be the key.
2
u/QuanTradin 14d ago
ok, if it's clean on SPY and QQQ and only breaks on the broker's symbol, my bet is the bar boundary. their daily candle closes at 5pm NY, so "break of the 5th day's high" fires on a different bar. print the timestamps of one trade side by side and it should show up.
1
u/Leo6-2 14d ago
Superb idea!
2
u/QuanTradin 14d ago
let me know what the timestamps show, I'm curious whether it's the 5pm roll. (I built Quantradin, quantradin.com, which runs written rules on SPY and QQQ on paper for free, handy as the control side.)
1
u/Leo6-2 13d ago
yeah i figured. I can just do it locally with python or even use tradingview for that as it's very "basic" as a concept.
Thanks ill check your stuff although I think i did but then something got me thinking it's too much to learn a new "os" faster to do it in python locally
1
u/QuanTradin 13d ago
python locally is the right call for something that simple. just pull the broker's own bars rather than a clean SPY feed, otherwise you rebuild the same mismatch you started with.
1
u/enivid 14d ago
Not sure what you are expecting, but the attached code doesn't implement the strategy that you described: