Renko/Range Backtest

Created at 20 Dec 2019, 17:18
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!
afhacker's avatar

afhacker

Joined 15.10.2015

Renko/Range Backtest
20 Dec 2019, 17:18


Hello,

I want to share the code that I use for backtesting Renko/Range bots on cTrader, follow the below instruction:

  1. Clone the "cAlgo.API.Extensions.Series" repo, and build it (You have to update the cAlgo.dll file reference to one located on your system before build)
  2. Reference the library on your Indicator/cBot
  3. Use the sample below to implement it on your cBot code
  4. For backtesting you have to use Tick data, it will not work on bars data
using cAlgo.API;
using cAlgo.API.Extensions.Enums;
using cAlgo.API.Extensions.Models;
using cAlgo.API.Extensions.Series;
using cAlgo.API.Internals;

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]
    public class RenkoBot : Robot
    {
        // Use RangeBars for Range bars
        private RenkoBars _renkoBars;

        [Parameter("Size (Pips)", DefaultValue = 10)]
        public double RankoSizeInPips { get; set; }

        protected override void OnStart()
        {
            _renkoBars = new RenkoBars(RankoSizeInPips, Symbol, this);

            _renkoBars.OnBar += RenkoBars_OnBar;
        }

        protected override void OnTick()
        {
            // The Renko/Range market series OnTick method must be called on each new upcoming ticks
            _renkoBars.OnTick();
        }

        // This method is called on new Renko/Range bars, you should use this method instead of OnBar method of your Robot
        private void RenkoBars_OnBar(object sender, OhlcBar newBar, OhlcBar oldBar)
        {
            var positionsToClose = Positions.FindAll("RenkoBot");

            if (oldBar.Type == BarType.Bullish)
            {
                foreach (var position in positionsToClose)
                {
                    if (position.TradeType == TradeType.Buy) continue;

                    ClosePosition(position);
                }

                ExecuteMarketOrder(TradeType.Buy, Symbol.Name, 1000, "RenkoBot", 10, 20);
            }
            else if (oldBar.Type == BarType.Bearish)
            {
                foreach (var position in positionsToClose)
                {
                    if (position.TradeType == TradeType.Sell) continue;

                    ClosePosition(position);
                }

                ExecuteMarketOrder(TradeType.Sell, Symbol.Name, 1000, "RenkoBot", 10, 20);
            }
        }
    }
}

 


@afhacker
Replies

afhacker
20 Dec 2019, 17:30

It only works if you use Tick data for backtesting, and the time frame is not important it will give you the same result on all time frames.


@afhacker

lukasztrader
29 Dec 2019, 23:11 ( Updated at: 21 Dec 2023, 09:21 )

Dear AlgoDeveloper,

I appreciate very much your input! I see that not only me is waiting for renko backtesting functionality in cTrader.

I tested your great work.

I tested on EURUSD and WTI. For both backtesting I set t1 (1 tick) and in backtesting settings I chose that Data: "Tick data from Server (accurate)".

In both tests, not once the code went into the 'bear' condition, the only log I received was only "BULL".:

            if (newBar.Type == BarType.Bullish)
            {
                Print("      BULL");
                //ExecuteMarketOrder(TradeType.Buy, Symbol.Name, 1000, string.Empty, 10, 20);
            }
            else if (newBar.Type == BarType.Bearish)
            {
                Print("BEAR");                
                //ExecuteMarketOrder(TradeType.Sell, Symbol.Name, 1000, string.Empty, 10, 20);
            }

Can you reproduce it at your environment and give us hint how to proceed? I am using cTrader 3.6


@lukasztrader

afhacker
02 Jan 2020, 12:57 ( Updated at: 21 Dec 2023, 09:21 )

RE:

lukasz.iskier said:

Dear AlgoDeveloper,

I appreciate very much your input! I see that not only me is waiting for renko backtesting functionality in cTrader.

I tested your great work.

I tested on EURUSD and WTI. For both backtesting I set t1 (1 tick) and in backtesting settings I chose that Data: "Tick data from Server (accurate)".

In both tests, not once the code went into the 'bear' condition, the only log I received was only "BULL".:

            if (newBar.Type == BarType.Bullish)
            {
                Print("      BULL");
                //ExecuteMarketOrder(TradeType.Buy, Symbol.Name, 1000, string.Empty, 10, 20);
            }
            else if (newBar.Type == BarType.Bearish)
            {
                Print("BEAR");                
                //ExecuteMarketOrder(TradeType.Sell, Symbol.Name, 1000, string.Empty, 10, 20);
            }

Can you reproduce it at your environment and give us hint how to proceed? I am using cTrader 3.6

Hi,

I fixed the issue, please clone the latest version of the library from Github.


@afhacker

lukasztrader
03 Jan 2020, 00:01

Hi,

I cloned the newest version (there is a missing reference for cargo.dll, but I changed that manually).

Now, there is no bar detection - I received no logs in the console and none transaction was made.

I guess something went wrong with new change.


@lukasztrader

afhacker
07 Jan 2020, 23:55 ( Updated at: 21 Dec 2023, 09:21 )

RE:

lukasz.iskier said:

Hi,

I cloned the newest version (there is a missing reference for cargo.dll, but I changed that manually).

Now, there is no bar detection - I received no logs in the console and none transaction was made.

I guess something went wrong with new change.

I just tested it and it works fine, are you you are calling the onTick method of Renko series on each new tick?

This is the code I used:

using cAlgo.API;
using cAlgo.API.Extensions.Enums;
using cAlgo.API.Extensions.Models;
using cAlgo.API.Extensions.Series;
using cAlgo.API.Internals;
using cAlgo.API.Indicators;

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]
    public class RenkoBot : Robot
    {
        // Use RangeMarketSeries for Range bars
        private RenkoMarketSeries _renkoSeries;

        private MovingAverage _ma;

        [Parameter("Size (Pips)", DefaultValue = 10)]
        public double RankoSizeInPips { get; set; }

        protected override void OnStart()
        {
            _renkoSeries = new RenkoMarketSeries(RankoSizeInPips, Symbol, this);

            _renkoSeries.OnBar += RenkoSeries_OnBar;

            // You can initialize cTrader indicators 
            _ma = Indicators.MovingAverage(_renkoSeries.Close, 10, MovingAverageType.Exponential);
        }

        protected override void OnTick()
        {
            // The Renko/Range market series OnTick method must be called on each new up coming ticks
            _renkoSeries.OnTick();
        }

        // This method is called on new Renko/Range bars, you should use this method instead of OnBar method of your Robot
        private void RenkoSeries_OnBar(object sender, OhlcBar newBar, OhlcBar oldBar)
        {
            Print(oldBar.Type);
        }
    }
}

And Result:


@afhacker

liwik10297
08 Jan 2020, 20:30 ( Updated at: 21 Dec 2023, 09:21 )

Hi, I've tested the following code, but the indicator doesn't work properly. It returns NaN values for about 100 bars (the number of bars depend on the time frame selected).

The code:

using cAlgo.API;
using cAlgo.API.Extensions.Enums;
using cAlgo.API.Extensions.Models;
using cAlgo.API.Extensions.Series;
using cAlgo.API.Internals;
using cAlgo.API.Indicators;

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]
    public class RenkoBot : Robot
    {
        // Use RangeMarketSeries for Range bars
        private RenkoMarketSeries _renkoSeries;

        private MovingAverage _ma;

        [Parameter("Size (Pips)", DefaultValue = 10)]
        public double RankoSizeInPips { get; set; }

        
        protected override void OnStart()
        {
            _renkoSeries = new RenkoMarketSeries(RankoSizeInPips, Symbol, this);

            _renkoSeries.OnBar += RenkoSeries_OnBar;
            
            // You can initialize cTrader indicators 
            _ma = Indicators.MovingAverage(_renkoSeries.Close, 10, MovingAverageType.Exponential);
        }

        protected override void OnTick()
        {
            // The Renko/Range market series OnTick method must be called on each new up coming ticks
            _renkoSeries.OnTick();
            
        }

        // This method is called on new Renko/Range bars, you should use this method instead of OnBar method of your Robot
        private void RenkoSeries_OnBar(object sender, OhlcBar newBar, OhlcBar oldBar)
        {

            Print("Renko: ", _renkoSeries.Close.Last(1));
            Print("MA: ", ma.Result.Last(1));
            Print("Count", _renkoSeries.Close.Count);

        }
    }
}


@liwik10297

lukasztrader
11 Jan 2020, 16:00

I compared backtest Renko size 10 and real chart and the bricks and the dates are not the same. Why?

What impact does have the setting of the instance's to 1hour or 1tick?

The size of the brick size is set to 10, the backtesting setting for data is set to "Tick data from Server (accurate)".

I see that there are different values for:

var prevOpenPrice = MarketSeries.Open.Last(1);

Example:

GBPJPY, h1, m1, t1 - the renko values for prevOpenPrice are different. What is the reason for that?


@lukasztrader

afhacker
13 Jan 2020, 15:23 ( Updated at: 21 Dec 2023, 09:21 )

RE:

lukasz.iskier said:

I compared backtest Renko size 10 and real chart and the bricks and the dates are not the same. Why?

What impact does have the setting of the instance's to 1hour or 1tick?

The size of the brick size is set to 10, the backtesting setting for data is set to "Tick data from Server (accurate)".

I see that there are different values for:

var prevOpenPrice = MarketSeries.Open.Last(1);

Example:

GBPJPY, h1, m1, t1 - the renko values for prevOpenPrice are different. What is the reason for that?

Hi,

It differs because the counting starts from different points of time, the cTrader Renko chart is calculated on their servers and then fed to client platforms.


@afhacker

lukasztrader
13 Jan 2020, 20:17

Ok, thanks.

How about those NaN values for indicators from mine screens and liwik10297 ? Do you plan to release any fix for that? Do you plan to make possible to visualize renko bricks for backtesting?


@lukasztrader

lukasztrader
25 Jan 2020, 20:28

Dear AlgoDeveloper,

any update for this update? Did you try to reproduce issue with NaN values?


@lukasztrader

scotpip
09 Mar 2020, 18:59

I was planning to do something like this myself so it's good to see a starting point. I'd be looking to add other Renko bar types and perhaps some JS charting.

If a community member can get this working, it does make you wonder why the promised native feature from the cTrader team has been delayed for so long...

I'm new to cTrader, so before I invest time with this, do people find it runs over a decent period of data? I've hit a crashing bug simply trying to backtest tick data against an empty algo, which has rather shaken my faith in the platform...

 


@scotpip

... Deleted by UFO ...

ctid2407925
06 Sep 2020, 04:16

RE:

Hi, is it possible to have the .dll, i was able to clone the repository but i was not able to build it i do not have visual studio. Thanks

afhacker said:

Hello,

I want to share the code that I use for backtesting Renko/Range bots on cTrader, follow the below instruction:

  1. Clone the "cAlgo.API.Extensions.Series" repo, and build it
  2. Reference the library on your Indicator/cBot
  3. Use the sample below to implement it on your cBot code. 
using cAlgo.API;
using cAlgo.API.Extensions.Enums;
using cAlgo.API.Extensions.Models;
using cAlgo.API.Extensions.Series;
using cAlgo.API.Internals;
using cAlgo.API.Indicators;

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]
    public class RenkoBot : Robot
    {
        // Use RangeMarketSeries for Range bars
        private RenkoMarketSeries _renkoSeries;

        private MovingAverage _ma;

        [Parameter("Size (Pips)", DefaultValue = 10)]
        public double RankoSizeInPips { get; set; }

        protected override void OnStart()
        {
            _renkoSeries = new RenkoMarketSeries(RankoSizeInPips, Symbol, this);

            _renkoSeries.OnBar += RenkoSeries_OnBar;

            // You can initialize cTrader indicators 
            _ma = Indicators.MovingAverage(_renkoSeries.Close, 10, MovingAverageType.Exponential);
        }

        protected override void OnTick()
        {
            // The Renko/Range market series OnTick method must be called on each new up coming ticks
            _renkoSeries.OnTick();
        }

        // This method is called on new Renko/Range bars, you should use this method instead of OnBar method of your Robot
        private void RenkoSeries_OnBar(object sender, OhlcBar newBar, OhlcBar oldBar)
        {
            if (newBar.Type == BarType.Bullish)
            {
                ExecuteMarketOrder(TradeType.Buy, Symbol.Name, 1000, string.Empty, 10, 20);
            }
            else if (newBar.Type == BarType.Bearish)
            {
                ExecuteMarketOrder(TradeType.Sell, Symbol.Name, 1000, string.Empty, 10, 20);
            }
        }
    }
}

 

 


@ctid2407925

arthur.albert990
30 Sep 2020, 01:34

RE: Unable to Build it.

afhacker said:

Hello,

I want to share the code that I use for backtesting Renko/Range bots on cTrader, follow the below instruction:

  1. Clone the "cAlgo.API.Extensions.Series" repo, and build it
  2. Reference the library on your Indicator/cBot
  3. Use the sample below to implement it on your cBot code. 
using cAlgo.API;
using cAlgo.API.Extensions.Enums;
using cAlgo.API.Extensions.Models;
using cAlgo.API.Extensions.Series;
using cAlgo.API.Internals;
using cAlgo.API.Indicators;

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]
    public class RenkoBot : Robot
    {
        // Use RangeMarketSeries for Range bars
        private RenkoMarketSeries _renkoSeries;

        private MovingAverage _ma;

        [Parameter("Size (Pips)", DefaultValue = 10)]
        public double RankoSizeInPips { get; set; }

        protected override void OnStart()
        {
            _renkoSeries = new RenkoMarketSeries(RankoSizeInPips, Symbol, this);

            _renkoSeries.OnBar += RenkoSeries_OnBar;

            // You can initialize cTrader indicators 
            _ma = Indicators.MovingAverage(_renkoSeries.Close, 10, MovingAverageType.Exponential);
        }

        protected override void OnTick()
        {
            // The Renko/Range market series OnTick method must be called on each new up coming ticks
            _renkoSeries.OnTick();
        }

        // This method is called on new Renko/Range bars, you should use this method instead of OnBar method of your Robot
        private void RenkoSeries_OnBar(object sender, OhlcBar newBar, OhlcBar oldBar)
        {
            if (newBar.Type == BarType.Bullish)
            {
                ExecuteMarketOrder(TradeType.Buy, Symbol.Name, 1000, string.Empty, 10, 20);
            }
            else if (newBar.Type == BarType.Bearish)
            {
                ExecuteMarketOrder(TradeType.Sell, Symbol.Name, 1000, string.Empty, 10, 20);
            }
        }
    }
}

 


Hello, i'm unable to build the github files, is it possible to upload the DLL file? Thank you.


@arthur.albert990

dave.anderson.consulting
26 Feb 2021, 21:59 ( Updated at: 26 Feb 2021, 22:00 )

RE: Unable to Build it.

The only dll used is the cAlgo.API.dll and that is from cTrader itself. The afhacker's code makes use of this dll that you already have on your cTrader installation.

The only problem is that some parts of the afhacker's code is not fully implemented yet. Therefore, you might get different error messages depending on which piece of code you are using.

He is aware of this but had no time to fix it.(see: https://github.com/afhacker/cAlgo.API.Extensions.Series/issues/2 )

But honestly, not being able to back test Renko bars is actually something Spotware / cTrader has to fix.

To me Renko is a half baked feature in cTrader.

Until the time of writing this post no one has managed to have a working workaround to backtest Renko bars on cTrader.

 


@dave.anderson.consulting

bonedrak
14 Mar 2022, 05:20

can this be used for indicators as well as cbots?

is this possible using "private void RenkoSeries_OnBar(object sender, OhlcBar newBar, OhlcBar oldBar)" instead of "calculate(int index)"

 


@bonedrak

amusleh
14 Mar 2022, 08:29

RE: can this be used for indicators as well as cbots?

bonedrak said:

is this possible using "private void RenkoSeries_OnBar(object sender, OhlcBar newBar, OhlcBar oldBar)" instead of "calculate(int index)"

 

Hi,

You should use RenkoSeries OnBar only in backtesting environment and the Calculate method on live mode.


@amusleh

bonedrak
14 Mar 2022, 08:47

RE: RE: can this be used for indicators as well as cbots?

amusleh said:

bonedrak said:

is this possible using "private void RenkoSeries_OnBar(object sender, OhlcBar newBar, OhlcBar oldBar)" instead of "calculate(int index)"

 

Hi,

You should use RenkoSeries OnBar only in backtesting environment and the Calculate method on live mode.

I want to use a custom indicator in backtesting a cbot, but it wont allow me because it says the time frames are different, for the cbot  (which uses your renko bars) and the indicator (which uses "calculate(int index)").  So my question is, can an indicator be built that uses your library and renko bars? or will it only work on cbots?


@bonedrak

amusleh
14 Mar 2022, 08:50

RE: RE: RE: can this be used for indicators as well as cbots?

bonedrak said:

amusleh said:

bonedrak said:

is this possible using "private void RenkoSeries_OnBar(object sender, OhlcBar newBar, OhlcBar oldBar)" instead of "calculate(int index)"

 

Hi,

You should use RenkoSeries OnBar only in backtesting environment and the Calculate method on live mode.

I want to use a custom indicator in backtesting a cbot, but it wont allow me because it says the time frames are different, for the cbot  (which uses your renko bars) and the indicator (which uses "calculate(int index)").  So my question is, can an indicator be built that uses your library and renko bars? or will it only work on cbots?

Hi,

You can't feed a custom indicator the Renko generated bars, it's not possible.

The solution is to create a class for your indicator inside cBot project, pass it the Renko generated bars data and then call it's OnBar method whenever you receive a bar from Renko library.

It's little complicated but it's possible.


@amusleh

bonedrak
14 Mar 2022, 10:03

RE: RE: RE: RE: can this be used for indicators as well as cbots?

amusleh said:

bonedrak said:

amusleh said:

bonedrak said:

is this possible using "private void RenkoSeries_OnBar(object sender, OhlcBar newBar, OhlcBar oldBar)" instead of "calculate(int index)"

 

Hi,

You should use RenkoSeries OnBar only in backtesting environment and the Calculate method on live mode.

I want to use a custom indicator in backtesting a cbot, but it wont allow me because it says the time frames are different, for the cbot  (which uses your renko bars) and the indicator (which uses "calculate(int index)").  So my question is, can an indicator be built that uses your library and renko bars? or will it only work on cbots?

Hi,

You can't feed a custom indicator the Renko generated bars, it's not possible.

The solution is to create a class for your indicator inside cBot project, pass it the Renko generated bars data and then call it's OnBar method whenever you receive a bar from Renko library.

It's little complicated but it's possible.

Thanks,

is it possible to show me an example of how to that, or to give an outline of the parts needed?


@bonedrak

arminvandam1980
15 Apr 2022, 15:22

RE: RE: RE: RE: RE: can this be used for indicators as well as cbots?

I did it. But it is very troublesome when you have complex indicator. I've explained it here how to do it. https://github.com/afhacker/cAlgo.API.Extensions.Series/issues/4 ​

Although I find cTrader to be more user-friendly than NinjaTrader, I test my ideas in NinjaTrader. There you can also use C# and also perform backtesting on various non-timebased charts. If you're a C# developer, you'd better spend a week on NinjaTrader learning it. After that, testing your ideas on non-time-based charts will be much faster.
In the past, I've wasted too much time bypassing Renko/Range backtest shortcomings of cTrader. Don't make the same mistake I did.
You can use NinjaTrader for free with a Demo account.
I am not suggesting that you should start using NinjaTrader. I'm just saying that backtesting on non-time-based charts is possible there. If you search this forum you will find that backtesting/optimization on non-timebased chart is something Spotware cannot and/or does not want to implement. This has been requested for years but is always postponed to future versions.

 


@arminvandam1980

olivierdmz
12 Aug 2023, 12:51

Add

public event Action<BarClosedEventArgs> BarClosed;

just after

public event Action<BarOpenedEventArgs> BarOpened;

in https://github.com/amusleh-spotware-com/cAlgo.API.Extensions.Series/blob/master/cAlgo.API.Extensions.Series/IndicatorBars.cs


@olivierdmz