LAZY LOADING - the structure

Created at 22 Feb 2022, 09:15
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!
BJ

BJORNBERNAU

Joined 21.07.2020

LAZY LOADING - the structure
22 Feb 2022, 09:15


Hello 

I would like to ask responsible of the CTRADER to make a clear description on the "lazy loading" concept and how it is solved. There are not really any clear descriptions on the problem and how it is solved, e.g. is the lazy loading to be understood as Lazy Initialization class of C#? 

There are references saying we should 'explicitly' define an indicator signal "calling" the 'calculate method'. But calling the calculate method is intrinsic in a working indicator program. So this is not clear. 

Therefore, what is suggested here is that you give us a clear structure on exactly how the signal is made explicit, to 'avoid' lazy loading, as opposed to when it's actually going to be lazy. 

Sincerely, 

Björn Bernau 

 


@BJORNBERNAU
Replies

amusleh
23 Feb 2022, 09:03 ( Updated at: 23 Feb 2022, 09:04 )

Hi,

The lazy loading of indicators means that the indicator code is not called/executed unless you access it's data.

By data I mean indicator Outputs / IndicatorDataSeries, if your indicator has no Output then you have to explicitly call the indicator Calculate method.

It's an implementation detail, and it's very rare to face any issue with indicator lazy loading unless you use an indicator without an Output.


@amusleh

BJORNBERNAU
24 Feb 2022, 11:55 ( Updated at: 21 Dec 2023, 09:22 )

RE:

amusleh said:

Hi,

The lazy loading of indicators means that the indicator code is not called/executed unless you access it's data.

By data I mean indicator Outputs / IndicatorDataSeries, if your indicator has no Output then you have to explicitly call the indicator Calculate method.

It's an implementation detail, and it's very rare to face any issue with indicator lazy loading unless you use an indicator without an Output.

 

 

 

 

Hello Amusleh and thank you for your feedback.

Your points are partly spot on and have been further developed. The deterministic signal is enhanced by time specific restrictions as by Panagiotis such as

private DateTime _lastBarOpenTime;

if (Bars.OpenTimes[index] != _lastBarOpenTime)
{
_lastBarOpenTime = Bars.OpenTimes[index];
}
 

if (high == 1)
{
HIGHstartCountIndexZERO += 1;
}

cTDN Forum - LAZY LOADING REVISITED .... (ctrader.com)

This basically produces working signals from the robot, sell (purple) and liquidation (green signal), stoploss set at 50.

 

Basically…

Because, at the 422 signal, sell is liquidated in the robot shown here, after 36.4 pips, i.e. no stoploss

with a signal that is non-existent in the indicator as show below

 

After this the sell signal is reversed, so that liquidation now is sell, as seen in

This is later, reversed, and again reversed and repeated in another non-existant indicator signal

 

 

THE CODE

using System;
using cAlgo.API;
using cAlgo.API.Internals;
using cAlgo.API.Indicators;
using cAlgo.Indicators;

namespace cAlgo
{
    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class SELL_LIQ_RED_CTDN_PANAGIOTIS : Indicator
    {

        [Output("White", PlotType = PlotType.Points, LineColor = "#FFFFFF", Thickness = 4)]
        public IndicatorDataSeries White { get; set; }

        ////  SELL SIGNAL  ////

        [Output("sell_ONE", PlotType = PlotType.Points, LineColor = "#B511F7", Thickness = 4)]
        public IndicatorDataSeries sell_ONE { get; set; }

        [Output("liq_ONE", PlotType = PlotType.Points, LineColor = "#FF7CFC00", Thickness = 4)]
        public IndicatorDataSeries liq_ONE { get; set; }

        ////   BOLLINGER    ////

        [Parameter("Source")]
        public DataSeries Source { get; set; }

        [Parameter("BandPeriods", DefaultValue = 16)]
        public int BandPeriod { get; set; }

        [Parameter("Std", DefaultValue = 1.6)]
        public double std { get; set; }

        [Parameter("MAType")]
        public MovingAverageType MAType { get; set; }

        private BollingerBands boll;
        private bool isBuy;

        protected override void Initialize()
        {
            boll = Indicators.BollingerBands(Source, BandPeriod, std, MAType);
        }

        private int HIGHstartCountIndexZERO = 0;
        private double d = double.NaN;
        private DateTime _lastBarOpenTime;
        
        int mm = 0;

        public override void Calculate(int index)
        {
           

            var high = 0;

            double high0 = Bars.HighPrices[index - 0];
            double high1 = Bars.HighPrices[index - 1];
            double high2 = Bars.HighPrices[index - 2];
            double high3 = Bars.HighPrices[index - 3];
            double high4 = Bars.HighPrices[index - 4];
            double close0 = Bars.ClosePrices[index - 0];

            var BBT2 = boll.Top[index - 2];

            mm += 1;


            if (Bars.OpenTimes[index] != _lastBarOpenTime)
            {

                _lastBarOpenTime = Bars.OpenTimes[index];


                if (high == 0)
                {
                    sell_ONE[index - 0] = d;
                }

                if (high == 0)
                {
                    liq_ONE[index - 0] = d;
                }

                if (high4 <= high2)
                    if (high3 <= high2)
                        if (high2 > BBT2)
                            if (high2 >= high1)
                                if (high2 >= close0)
                                {
                                    high = 1;
                                    White[index - 0] = Bars.HighPrices[index - 0] + 0.0002;


                                }

                if (high == 1)
                {
                    HIGHstartCountIndexZERO += 1;
                }

                //      if (!isBuy)
                if (high == 1)
                    if (HIGHstartCountIndexZERO % 2 != 0)
                    {
                        sell_ONE[index - 0] = Bars.HighPrices[index - 0];
                        //   Print(index, "    SELL   ", sell_ONE);

                        string litt_LIMIT = Convert.ToString((index + 0));
                        var Lindex = Chart.DrawText("litt_LIMIT" + mm, litt_LIMIT, index - 2, sell_ONE[index] + 0.005, Color.DimGray);
                    }


                //   if (isBuy)
                if (high == 1)
                    if (HIGHstartCountIndexZERO % 2 == 0)
                    {
                        liq_ONE[index - 0] = Bars.HighPrices[index - 0];
                        //     Print(index, "    LIQ     ", liq_ONE);

                        string litt_LIMIT2 = Convert.ToString((index + 0));
                        var Lindex2 = Chart.DrawText("litt_LIMIT" + mm, litt_LIMIT2, index - 2, liq_ONE[index] + 0.005, Color.DimGray);
                    }

            }
        }
    }
}

 

The lazy loading should be overcome by directly addressing the Calculate method in Robot as below by

 

            var a = S2.sell_ONE.Last(0);
            var b = S2.liq_ONE.Last(0);
 

ROBOT // to simplify runs in the robot it is made in EURUSD between 13 jan - 21 febr 2022 

 

using System;
using System.Linq;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
using cAlgo.Indicators;

namespace cAlgo
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class SELL_LIQ_220220_panagiotis : Robot
    {

        ////////////////////////////////////
        /////   TRADING PARAMETERS    //////
        ////////////////////////////////////

        [Parameter("Volume", DefaultValue = 1000)]
        public int Volume { get; set; }

        [Parameter("StopLoss", DefaultValue = 50)]
        public int StopLoss { get; set; }

        [Parameter("LIMIT", DefaultValue = 200)]
        public int LIMIT { get; set; }


        ////////////////////////////
        ////   INITIAL PARAM    //// 

        [Parameter("Source")]
        public DataSeries Source { get; set; }

        [Parameter("BandPeriods", DefaultValue = 16)]
        public int BandPeriod { get; set; }

        [Parameter("Std", DefaultValue = 1.6)]
        public double std { get; set; }

        [Parameter("MAType")]
        public MovingAverageType MAType { get; set; }


        //////////////////////////////
        ////       LITTERALS      //// 

        [Parameter("littFACTOR", DefaultValue = 5)]
        public double littFACTOR { get; set; }

        private SELL_LIQ_RED_CTDN_PANAGIOTIS S2;

        protected override void OnStart()
        {
            S2 = Indicators.GetIndicator<SELL_LIQ_RED_CTDN_PANAGIOTIS>(Source, BandPeriod, std, MAType);
        }

        double d = double.NaN;


        int m = 0;
        int n = 0;
        protected override void OnBar()
        {

            var a = S2.sell_ONE.Last(0);
            var b = S2.liq_ONE.Last(0);


            //    m += 1;
            //  string POS = "sell_pos " + m;

            if (!double.IsNaN(S2.sell_ONE.Last(0)) == true)
            {
                m += 1;
                string POS = "sell_pos " + m;

                ExecuteMarketOrder(TradeType.Sell, Symbol.Name, 1000, POS, StopLoss, LIMIT);
                Print("SELL    ", Bars.Count, "      ", S2.sell_ONE, "    ", POS);
            }


            // var POS = Positions.FindAll(posit);

            if (!double.IsNaN(S2.liq_ONE.Last(0)) == true)

                foreach (var position in Positions)
                {

                    n += 1;
                    string LIQ = "liq " + n;

                    ClosePosition(position);
                    Print("LIQ    ", Bars.Count, "      ", S2.liq_ONE, "    ", LIQ);
                }

        }
    }
}

 

 

 


@BJORNBERNAU

amusleh
25 Feb 2022, 09:29

Hi,

Can you tell exactly me what's the issue?


@amusleh

BJORNBERNAU
25 Feb 2022, 09:39

RE:

amusleh said:

Hi,

Can you tell exactly me what's the issue?

Thank you Amusleh! 

The signals work and are relayed to the robot, mostly in a correct fashion. But now, there are apparently some 'ghost' signals instead. Sell signals are purple and liquidation signals green. 

  1. Second image above after signal 422 brown box - a correctly executed sell signal - the position is liquidated despite no liquidation signal, nor a stop-loss, as seen from the indicator that shows none, next image, after signal 1576 -  blue box.
  2. In addition, after this, the sell and liquidation signals are reversed, seen again in next image, signal 449. 

These two issues are now the most important problems. 

Thank you! 

Björn 


@BJORNBERNAU

PanagiotisCharalampous
25 Feb 2022, 09:52

Hi Björn,

The problem is probably caused by the fact that you are checking the value of the open candle. This can cause trades to open but the signal to disappear until the candle closes. To avoid such issues, you should be checking the values of closed candles i.e. use .Last(1) instead of .Last(0).

Best Regards,

Panagiotis 

Join us on Telegram and Facebook

 


@PanagiotisCharalampous

BJORNBERNAU
25 Feb 2022, 09:56

RE:

PanagiotisCharalampous said:

Hi Björn,

The problem is probably caused by the fact that you are checking the value of the open candle. This can cause trades to open but the signal to disappear until the candle closes. To avoid such issues, you should be checking the values of closed candles i.e. use .Last(1) instead of .Last(0).

Best Regards,

Panagiotis 

Join us on Telegram and Facebook

 

 

Thank you Panagiotis! 

No, I'm sorry to say that that just delays the positions, same issue, the code

  protected override void OnBar()
        {

            var a = S2.sell_ONE.Last(1);
            var b = S2.liq_ONE.Last(1);


            //    m += 1;
            //  string POS = "sell_pos " + m;

            if (!double.IsNaN(S2.sell_ONE.Last(1)) == true)
            {
                m += 1;
                string POS = "sell_pos " + m;

                ExecuteMarketOrder(TradeType.Sell, Symbol.Name, 1000, POS, StopLoss, LIMIT);
                Print("SELL    ", Bars.Count, "      ", S2.sell_ONE, "    ", POS);
            }


            // var POS = Positions.FindAll(posit);

            if (!double.IsNaN(S2.liq_ONE.Last(1)) == true)

                foreach (var position in Positions)
                {

                    n += 1;
                    string LIQ = "liq " + n;

                    ClosePosition(position);
                    Print("LIQ    ", Bars.Count, "      ", S2.liq_ONE, "    ", LIQ);
                }

        }


@BJORNBERNAU

BJORNBERNAU
25 Feb 2022, 09:57

RE: RE:

BJORNBERNAU said:

PanagiotisCharalampous said:

Hi Björn,

The problem is probably caused by the fact that you are checking the value of the open candle. This can cause trades to open but the signal to disappear until the candle closes. To avoid such issues, you should be checking the values of closed candles i.e. use .Last(1) instead of .Last(0).

Best Regards,

Panagiotis 

Join us on Telegram and Facebook

 

 

Thank you Panagiotis! 

No, I'm sorry to say that that just delays the positions, same issue, the code

  protected override void OnBar()
        {

            var a = S2.sell_ONE.Last(1);
            var b = S2.liq_ONE.Last(1);


            //    m += 1;
            //  string POS = "sell_pos " + m;

            if (!double.IsNaN(S2.sell_ONE.Last(1)) == true)
            {
                m += 1;
                string POS = "sell_pos " + m;

                ExecuteMarketOrder(TradeType.Sell, Symbol.Name, 1000, POS, StopLoss, LIMIT);
                Print("SELL    ", Bars.Count, "      ", S2.sell_ONE, "    ", POS);
            }


            // var POS = Positions.FindAll(posit);

            if (!double.IsNaN(S2.liq_ONE.Last(1)) == true)

                foreach (var position in Positions)
                {

                    n += 1;
                    string LIQ = "liq " + n;

                    ClosePosition(position);
                    Print("LIQ    ", Bars.Count, "      ", S2.liq_ONE, "    ", LIQ);
                }

        }


@BJORNBERNAU

BJORNBERNAU
25 Feb 2022, 10:03 ( Updated at: 21 Dec 2023, 09:22 )

RE: RE: RE:

BJORNBERNAU said:

BJORNBERNAU said:

PanagiotisCharalampous said:

Hi Björn,

The problem is probably caused by the fact that you are checking the value of the open candle. This can cause trades to open but the signal to disappear until the candle closes. To avoid such issues, you should be checking the values of closed candles i.e. use .Last(1) instead of .Last(0).

Best Regards,

Panagiotis 

Join us on Telegram and Facebook

 

 

Thank you Panagiotis! 

No, I'm sorry to say that that just delays the positions, same issue, the code

  protected override void OnBar()
        {

            var a = S2.sell_ONE.Last(1);
            var b = S2.liq_ONE.Last(1);


            //    m += 1;
            //  string POS = "sell_pos " + m;

            if (!double.IsNaN(S2.sell_ONE.Last(1)) == true)
            {
                m += 1;
                string POS = "sell_pos " + m;

                ExecuteMarketOrder(TradeType.Sell, Symbol.Name, 1000, POS, StopLoss, LIMIT);
                Print("SELL    ", Bars.Count, "      ", S2.sell_ONE, "    ", POS);
            }


            // var POS = Positions.FindAll(posit);

            if (!double.IsNaN(S2.liq_ONE.Last(1)) == true)

                foreach (var position in Positions)
                {

                    n += 1;
                    string LIQ = "liq " + n;

                    ClosePosition(position);
                    Print("LIQ    ", Bars.Count, "      ", S2.liq_ONE, "    ", LIQ);
                }

        }

Increasing stop loss to 100 produces that same two issues. 

Stop-out at 52.9 without signal and reversal of sell/liquidation execution by the robot. 


@BJORNBERNAU

PanagiotisCharalampous
25 Feb 2022, 10:04

Hi Björn,

In order to help you  further, you need to provide us with the cBot parameters and dates so that we can reproduce the trades.

Best Regards,

Panagiotis 

Join us on Telegram and Facebook


@PanagiotisCharalampous

BJORNBERNAU
25 Feb 2022, 10:27

RE:

PanagiotisCharalampous said:

Hi Björn,

In order to help you  further, you need to provide us with the cBot parameters and dates so that we can reproduce the trades.

Best Regards,

Panagiotis 

Join us on Telegram and Facebook

Thank you! 

Actually I did so above and it is 1 hr

 

ROBOT // to simplify runs in the robot it is made in EURUSD between 13 jan - 21 febr 2022 


@BJORNBERNAU

PanagiotisCharalampous
25 Feb 2022, 11:16

Hi Bjorn,

If you run the cBot using visual backtesting, you will see that the trade is correct based on the signal on the chart

The problem here lies in your indicator's logic. You set a value to the output series when the signal is valid, but you don't set it back to NaN when the signal is invalidated within the same candle. This causes discrepancies between signals calculated in real time and signals calculated for historical bars, because real time bars are calculated. You need to fix this. This problem does not appear with the indicator I provided here.

Best Regards,

Panagiotis 

Join us on Telegram and Facebook


@PanagiotisCharalampous

BJORNBERNAU
25 Feb 2022, 11:34 ( Updated at: 21 Dec 2023, 09:22 )

RE:

PanagiotisCharalampous said:

Hi Bjorn,

If you run the cBot using visual backtesting, you will see that the trade is correct based on the signal on the chart

The problem here lies in your indicator's logic. You set a value to the output series when the signal is valid, but you don't set it back to NaN when the signal is invalidated within the same candle. This causes discrepancies between signals calculated in real time and signals calculated for historical bars, because real time bars are calculated. You need to fix this. This problem does not appear with the indicator I provided here.

Best Regards,

Panagiotis 

Join us on Telegram and Facebook

Okay, thank you, I'll look into this! 


@BJORNBERNAU

BJORNBERNAU
25 Feb 2022, 12:28 ( Updated at: 21 Dec 2023, 09:22 )

RE: RE:

BJORNBERNAU said:

PanagiotisCharalampous said:

Hi Bjorn,

If you run the cBot using visual backtesting, you will see that the trade is correct based on the signal on the chart

The problem here lies in your indicator's logic. You set a value to the output series when the signal is valid, but you don't set it back to NaN when the signal is invalidated within the same candle. This causes discrepancies between signals calculated in real time and signals calculated for historical bars, because real time bars are calculated. You need to fix this. This problem does not appear with the indicator I provided here.

Best Regards,

Panagiotis 

Join us on Telegram and Facebook

Okay, thank you, I'll look into this! 

I've answered in the link for the code you're referring to. 


@BJORNBERNAU