Multi-currency backtest

Created at 17 Dec 2019, 13:23
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!
V.

v.kredov

Joined 17.12.2019

Multi-currency backtest
17 Dec 2019, 13:23


As a test of the new multi-currency backtest functionality, I am writing a robot that calculates the correlation of two instruments.

using System;
using cAlgo.API;
using cAlgo.API.Internals;
using cAlgo.API.Indicators;
using cAlgo.Indicators;
using System.Threading;
using System.Collections.Generic;

namespace cAlgo
{
    [Indicator(IsOverlay = false, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None, ScalePrecision = 5, AutoRescale = true)]
    public class Correlation : Indicator
    {
        [Parameter("Master Currency", DefaultValue = "EURUSD")]
        public string masterName { get; set; }

        [Parameter("Slave Currency", DefaultValue = "AUDUSD")]
        public string slaveName { get; set; }

        [Parameter("Periods", DefaultValue = 120)]
        public int Period { get; set; }

        [Output("Correlation", LineColor = "Red")]
        public IndicatorDataSeries Result { get; set; }

        private Bars masterBars;
        private Bars slaveBars;
        private Symbol masterSymbol;
        private Symbol slaveSymbol;

        protected override void Initialize()
        {

            masterSymbol = Symbols.GetSymbol(masterName);
            slaveSymbol = Symbols.GetSymbol(slaveName);

            masterBars = MarketData.GetBars(Bars.TimeFrame, masterName);
            slaveBars = MarketData.GetBars(Bars.TimeFrame, slaveName);

            while (masterBars.Count < Period)
            {
                var loadedCount = masterBars.LoadMoreHistory();
                if (loadedCount == 0)
                    break;
            }

            while (slaveBars.Count < Period)
            {
                var loadedCount = slaveBars.LoadMoreHistory();
                if (loadedCount == 0)
                    break;
            }
        }

        public override void Calculate(int index)
        {
            if (index < Period + 1)
            {
                return;
            }

            double masterSum = 0;
            double masterMedian = 0;
            double slaveSum = 0;
            double slaveMedian = 0;
            double upPartSum = 0;
            double masterDeltaSqrtSum = 0;
            double slaveDeltaSqrtSum = 0;

            for (int i = 0; i < Period; i++)
            {
                masterSum = masterSum + masterBars[index - i].Close;
                slaveSum = slaveSum + slaveBars[index - i].Close;
            }

            masterMedian = masterSum / Period;
            slaveMedian = slaveSum / Period;

            for (int i = 0; i < Period; i++)
            {
                upPartSum = upPartSum + (masterBars[index - i].Close - masterMedian) * (slaveBars[index - i].Close - slaveMedian);
                masterDeltaSqrtSum = masterDeltaSqrtSum + Math.Pow(masterBars[index - i].Close - masterMedian, 2);
                slaveDeltaSqrtSum = slaveDeltaSqrtSum + Math.Pow(slaveBars[index - i].Close - slaveMedian, 2);
            }

            Result[index] = upPartSum / Math.Sqrt(masterDeltaSqrtSum * slaveDeltaSqrtSum);
        }
    }
}

On the backtest itself, the indicator does not work.

 


@v.kredov
Replies

v.kredov
17 Dec 2019, 13:41 ( Updated at: 21 Dec 2023, 09:21 )

On the live chart, this error often appears when adding an indicator. When the number of indicator values ​​is not equal to the number of tool candles, despite the fact that this behavior always occurs when loading the history of the tool by scrolling the chart back.


@v.kredov

PanagiotisCharalampous
17 Dec 2019, 15:44

Hi v,kredov,

The way you handle indices between different data series is wrong. An index in calculate is not the same index in a series retrieved by GetBars(). See below a corrected version

using System;
using cAlgo.API;
using cAlgo.API.Internals;
using cAlgo.API.Indicators;
using cAlgo.Indicators;
using System.Threading;
using System.Collections.Generic;

namespace cAlgo
{
    [Indicator(IsOverlay = false, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None, ScalePrecision = 5, AutoRescale = true)]
    public class Correlation : Indicator
    {
        [Parameter("Master Currency", DefaultValue = "EURUSD")]
        public string masterName { get; set; }

        [Parameter("Slave Currency", DefaultValue = "AUDUSD")]
        public string slaveName { get; set; }

        [Parameter("Periods", DefaultValue = 120)]
        public int Period { get; set; }

        [Output("Correlation", LineColor = "Red")]
        public IndicatorDataSeries Result { get; set; }

        private Bars masterBars;
        private Bars slaveBars;
        private Symbol masterSymbol;
        private Symbol slaveSymbol;

        protected override void Initialize()
        {

            masterSymbol = Symbols.GetSymbol(masterName);
            slaveSymbol = Symbols.GetSymbol(slaveName);

            masterBars = MarketData.GetBars(Bars.TimeFrame, masterName);
            slaveBars = MarketData.GetBars(Bars.TimeFrame, slaveName);

            while (masterBars.Count < Period)
            {
                var loadedCount = masterBars.LoadMoreHistory();
                if (loadedCount == 0)
                    break;
            }

            while (slaveBars.Count < Period)
            {
                var loadedCount = slaveBars.LoadMoreHistory();
                if (loadedCount == 0)
                    break;
            }
        }

        public override void Calculate(int index)
        {
            if (index < Period + 1)
            {
                return;
            }
            
            double masterSum = 0;
            double masterMedian = 0;
            double slaveSum = 0;
            double slaveMedian = 0;
            double upPartSum = 0;
            double masterDeltaSqrtSum = 0;
            double slaveDeltaSqrtSum = 0;

            for (int i = 0; i < Period; i++)
            {
                var masterBarsIndex = masterBars.OpenTimes.GetIndexByTime(Bars.OpenTimes[index]) - i;
                if (!double.IsNaN(masterBarsIndex))
                    masterSum = masterSum + masterBars[masterBarsIndex].Close;
                var slaveBarsIndex = slaveBars.OpenTimes.GetIndexByTime(Bars.OpenTimes[index]) - i;
                if (!double.IsNaN(slaveBarsIndex) && slaveBarsIndex >= 0 && slaveBars.Count > slaveBarsIndex)
                    slaveSum = slaveBars[slaveBarsIndex].Close;
            }

            masterMedian = masterSum / Period;
            slaveMedian = slaveSum / Period;

            for (int i = 0; i < Period; i++)
            {
                upPartSum = upPartSum + (masterBars[masterBars.OpenTimes.GetIndexByTime(Bars.OpenTimes[index]) - i].Close - masterMedian) * (slaveBars[slaveBars.OpenTimes.GetIndexByTime(Bars.OpenTimes[index]) - i].Close - slaveMedian);
                masterDeltaSqrtSum = masterDeltaSqrtSum + Math.Pow(masterBars[masterBars.OpenTimes.GetIndexByTime(Bars.OpenTimes[index]) - i].Close - masterMedian, 2);
                slaveDeltaSqrtSum = slaveDeltaSqrtSum + Math.Pow(slaveBars[slaveBars.OpenTimes.GetIndexByTime(Bars.OpenTimes[index]) - i].Close - slaveMedian, 2);
            }

            Result[index] = upPartSum / Math.Sqrt(masterDeltaSqrtSum * slaveDeltaSqrtSum);
        }
    }
}

Best Regards,

Panagiotis


@PanagiotisCharalampous