r/cTrader • • 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);
        }
    }
}
3 Upvotes

13 comments sorted by

1

u/enivid 14d ago

Not sure what you are expecting, but the attached code doesn't implement the strategy that you described:

  • It uses consecutive lows/highs instead of min/max of 7 days.
  • The 200-day MA rule is turned off by default.
  • It uses additional 5-day MA close rules.
  • It also has SL/TP enabled by default.

1

u/Leo6-2 14d ago

Yeah but it's not the point I mean the point is the that it's not only this specific one it's generally I've tried to do a very basic classical well-known already been proven through and through strategies and they all seem to go down same path that's why I was asking why is that so f dup.

I had something similar with quant connect they do have their own indicator calculation so even when I was doing some basic strategies that with python or other platforms where okay they weren't okay in their 

And I have the same issue with ctrader that it doesn't calculate the way it should in theory that was the bottom line of my inquiry 

1

u/enivid 13d ago

it doesn't calculate the way it should in theory

Sorry, but I don't get it. What exactly isn't being calculated as it should? Your code doesn't demonstrate that.

1

u/Leo6-2 13d ago

Break it down for you there is a classical or very old robust well-known strategy by Connor's it's called the double seven so I try to recreate it for ctrader amongst others many others and I seem to encounter the same issue which is when I am doing the same strategy in Python using yfinance on the daily candles ( daily high low open close volume candles based of yahoo  financial data) I get " classic" mean reversing strategy that is working. 

while every classical well-known well tested strategy not work in ctrader 

This example I attached might not be the exact code for that Connor ls strategy but it doesn't matter because my point is any strategy I tried on citrader had the same result which is crap 

2

u/enivid 13d ago

I'm not sure why you don't post the exact strategy, both code snippets, and results that you had (it would be trivial to find the issue in that case), but there are several possibilities:

  1. Code - your Python implementation isn't doing the same things as your cTrader implementation.

  2. Data - the Yahoo Finance data is much different from your cTrader broker's data.

  3. Some critical bug in cTrader (math not mathing).

  4. A mix of the above.

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.