OnTick() is called without tick change

Created at 28 Sep 2020, 05:50
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!
KE

kebbo

Joined 28.09.2020

OnTick() is called without tick change
28 Sep 2020, 05:50


Hi Community,

i have been working with CTrader and its cbots for quite some time now, but until now i could always help myself as a silent reader.

But now I'm facing a problem that I don't understand and I need your help:

The following scenario:

I have programmed a bot that includes several instruments, currently 4 of them.
In the OnBar() method I get the last close price of the 5 minute chart of all 4 instruments.

The Bot runs in the M1 Chart, which means the Onbar method should be called 5 times!
This works great too!

But:
Sometimes it happens that not the last price is taken from each instrument, but the close price of the candle before?
I suppose that the "Reading Algo" is faster than "Making the Close Prices" in each instrument? Do you understand what I mean?

1st solution: So I tried to help myself with Thread.Sleep, so I pause for about 5 sec after each call of OnBar, so that all instruments have completed the current candle. But then the backtest does not work correctly.

2nd solution:
 
I also use the OnTick method to read the data only after about 10 ticks after barclose, so the algorithm has some time to update all instruments:


        protected override void OnBar()
        {
            bar_count++;
        }

        protected override void OnTick()
        {

            if (bar_count == bar_count1)
            {
                return;
            }

            if (tick_count < 10)
            {
                Print(tick_count);
                tick_count++;
                return;

            }

            bar_count1 = bar_count;
            tick_count = 0;

          ....bot logic.....
}

Problem here:

After starting the bot it works great, the ticks are counted up, then the rest is executed. But as soon as there is a new candle and the ticks should be counted up again (to get some time), the counting up happens within a second, even though there was no tick change at all?
Why?
Can you help me?

Thanks a lot!
kebbo


@kebbo
Replies

PanagiotisCharalampous
28 Sep 2020, 09:04

Hi kebbo,

It would be easier for us to help you if you posted the complete cBot code and explained how we can reproduce the problem. With the information I have, I can only suggest that you check for a change in the last bar's open time, to detect when the bar has changed.

Best Regards,

Panagiotis 

Join us on Telegram


@PanagiotisCharalampous

kebbo
29 Sep 2020, 17:47

RE:

PanagiotisCharalampous said:

Hi kebbo,

It would be easier for us to help you if you posted the complete cBot code and explained how we can reproduce the problem. With the information I have, I can only suggest that you check for a change in the last bar's open time, to detect when the bar has changed.

Best Regards,

Panagiotis 

Join us on Telegram

Hi Panagiotis,

thank you very much for your quick answer and help with my long text! Sorry for my late reaction.

You're right, it's hard to understand the problem without code. I have shortened the code, already in my own interest, so far that it is almost limited to the actual problem. The actual algorithm is much longer, but that is not what this is about.

What do I want to achieve, what do I expect?

The bot runs in the M1 chart and includes 3 more (+the instrument in which the bot runs) instruments. But the 3 added instruments run in the M5 chart.
Now I want my code to be executed every minute and I want to include the respective close price of the already completed 5 minutes candles in the calculation. Therefore I wrote my code in the OnBar method, the actual function (5x output per 5minute candle) also works, as expected!
However, I was surprised by the results and to find out what the error was, I added some print methods, which print the close prices every minute.

Now you can clearly see that the 3 close prices are printed every minute, but not always the CURRENT close price of the previous 5 minute candle, especially when a new candle starts. I think the bot is faster than updating the respective close price of each instrument, or not?

So my code is not usable and I was wondering how to solve the problem.
1. thread.sleep method. I don't like this solution, because the backtest is not fast.
2. I have also implemented the OnTick method (see the commented out section in my code below) and let some ticks pass until the close price is printed, so I can guarantee that in every instrument the candle is closed.
Here you can see that after the start of the bot, the ticks are counted correctly, but at the next minute all 10 ticks are printed one after the other, although there is no market movement and the initial error that the close price is not correct is present again...


my questions now :
1. Do you have an elegant solution for me, how I can achieve that the close price is correctly recorded when changing candles in the M5 chart?

2.Why are the ticks in my 2nd solution no longer counted correctly from the 2nd candle onwards?

 

 

Please run my bot on your system so that you can reproduce the error if necessary. Can you reproduce it?


Thanks for your help, have a nice day!
Kebbo

 

Code:

 

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

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class Testbot : Robot
    {
        [Parameter(DefaultValue = 0.0)]
        public double Parameter { get; set; }

        int bar_count = 0;
        int bar_count1 = 0;
        int tick_count = 0;

        Symbol EURUSD;
        Symbol GBPUSD;
        Symbol AUDUSD;

        Bars EURUSD_m5;
        Bars GBPUSD_m5;
        Bars AUDUSD_m5;


        protected override void OnBar()
        {
            bar_count++;
        }

        protected override void OnTick()
        {

            if (bar_count == bar_count1)
            {
                return;
            }

  /*   if (tick_count < 10)
            {
                Print(tick_count);
                tick_count++;
                return;

            }
*/
            bar_count1 = bar_count;
            tick_count = 0;


            EURUSD = Symbols.GetSymbol("EURUSD");
            GBPUSD = Symbols.GetSymbol("GBPUSD");
            AUDUSD = Symbols.GetSymbol("AUDUSD");
            EURUSD_m5 = MarketData.GetBars(TimeFrame.Minute5, "EURUSD");
            GBPUSD_m5 = MarketData.GetBars(TimeFrame.Minute5, "GBPUSD");
            AUDUSD_m5 = MarketData.GetBars(TimeFrame.Minute5, "AUDUSD");

            Print(EURUSD_m5.ClosePrices.Last(1) + "     " + GBPUSD_m5.ClosePrices.Last(1) + "     " + AUDUSD_m5.ClosePrices.Last(1));
        }


    }
}

 

 


@kebbo

PanagiotisCharalampous
30 Sep 2020, 08:48

Hi kebbo,

If I understood your intentions correctly, then the correct solution is the below

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

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class Testbot : Robot
    {
        [Parameter(DefaultValue = 0.0)]
        public double Parameter { get; set; }


        Bars EURUSD_m5;
        Bars GBPUSD_m5;
        Bars AUDUSD_m5;

        DateTime EURUSD_LastOpenTime;
        DateTime GBPUSD_LastOpenTime;
        DateTime AUDUSD_LastOpenTime;
        protected override void OnStart()
        {
            EURUSD_m5 = MarketData.GetBars(TimeFrame.Minute5, "EURUSD");
            GBPUSD_m5 = MarketData.GetBars(TimeFrame.Minute5, "GBPUSD");
            AUDUSD_m5 = MarketData.GetBars(TimeFrame.Minute5, "AUDUSD");
        }

        protected override void OnBar()
        {

        }

        protected override void OnTick()
        {
            if (EURUSD_m5.OpenTimes.LastValue != EURUSD_LastOpenTime)
            {
                Print(EURUSD_m5.ClosePrices.Last(1));
                EURUSD_LastOpenTime = EURUSD_m5.OpenTimes.LastValue;
            }
            if (GBPUSD_m5.OpenTimes.LastValue != GBPUSD_LastOpenTime)
            {
                Print(GBPUSD_m5.ClosePrices.Last(1));
                GBPUSD_LastOpenTime = GBPUSD_m5.OpenTimes.LastValue;
            }
            if (AUDUSD_m5.OpenTimes.LastValue != AUDUSD_LastOpenTime)
            {
                Print(AUDUSD_m5.ClosePrices.Last(1));
                AUDUSD_LastOpenTime = AUDUSD_m5.OpenTimes.LastValue;
            }
        }


    }
}

Best Regards,

Panagiotis 

Join us on Telegram


@PanagiotisCharalampous

kebbo
30 Sep 2020, 23:23

RE:

PanagiotisCharalampous said:

Hi kebbo,

If I understood your intentions correctly, then the correct solution is the below

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

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class Testbot : Robot
    {
        [Parameter(DefaultValue = 0.0)]
        public double Parameter { get; set; }


        Bars EURUSD_m5;
        Bars GBPUSD_m5;
        Bars AUDUSD_m5;

        DateTime EURUSD_LastOpenTime;
        DateTime GBPUSD_LastOpenTime;
        DateTime AUDUSD_LastOpenTime;
        protected override void OnStart()
        {
            EURUSD_m5 = MarketData.GetBars(TimeFrame.Minute5, "EURUSD");
            GBPUSD_m5 = MarketData.GetBars(TimeFrame.Minute5, "GBPUSD");
            AUDUSD_m5 = MarketData.GetBars(TimeFrame.Minute5, "AUDUSD");
        }

        protected override void OnBar()
        {

        }

        protected override void OnTick()
        {
            if (EURUSD_m5.OpenTimes.LastValue != EURUSD_LastOpenTime)
            {
                Print(EURUSD_m5.ClosePrices.Last(1));
                EURUSD_LastOpenTime = EURUSD_m5.OpenTimes.LastValue;
            }
            if (GBPUSD_m5.OpenTimes.LastValue != GBPUSD_LastOpenTime)
            {
                Print(GBPUSD_m5.ClosePrices.Last(1));
                GBPUSD_LastOpenTime = GBPUSD_m5.OpenTimes.LastValue;
            }
            if (AUDUSD_m5.OpenTimes.LastValue != AUDUSD_LastOpenTime)
            {
                Print(AUDUSD_m5.ClosePrices.Last(1));
                AUDUSD_LastOpenTime = AUDUSD_m5.OpenTimes.LastValue;
            }
        }


    }
}

Best Regards,

Panagiotis 

Join us on Telegram

Hi Panagiotis!

Thanks again for your quick and detailed answer.
I can understand your way of thinking and your code, but it does not exactly fulfill my wishes.
When I execute your code in the M1 chart, I only get the prices every 5 minutes, because the time data of the 5m charts are compared.
But I want to achieve that I can run the bot in the M1 and every minute an update of the prices comes in, no matter if a new candle is forming in the M5, M15 or any other chart.

I want to record the close price of the last completed candle every minute for further processing, no matter in which timeframe it was formed.

If you now use my code, you will notice that every minute the three prices are displayed, they should be 5x identical and then again different because there is a new candle. However, and this is the problem, sometimes the close prices of the last candle are not taken, but the previous one, because the 5M chart does not seem to have "updated" yet.

1.  for example, in EURUSD (where the bot is running) a new M1 candle was started, now the three added M5 charts are asked for their last close prices. But then the previous price is taken (from the candle before, which has to be index 2) and not the one that just closed the candle. Why? Is the bot faster than updating every chart?

2.how many lines of code can be "processed" per tick? If the market makes a jump of about 5 ticks, will the onTick method be called 5x completely? Even if the market is faster than the bot?

3. if you use my code above and add the commented out area, it will "save time" because the bot needs 10 ticks before it asks the close prices, that would work, but it only counts up cleanly once right after the start (every tick increases the counter), but from the second candle/call it jumps very fast to the needed 10 ticks although there was no price movement at all, why is that? Why are the ticks with this code not counted cleanly before the algorithm continues?


See the following protocol and note the times:

The first time 10 ticks movement was actually counted, this took about 6 sec. The next time the market was called by a new candle in the M1, the tick counter jumps up within 3 sec. even though the market did not move 10 ticks...

30/09/2020 19:53:09.258 | cBot "Testbot" was stopped for EURUSD, m1.
30/09/2020 19:53:04.743 | 1,1726 1,2917 0,7168
30/09/2020 19:53:04.712 | 9
30/09/2020 19:53:04.493 | 8
30/09/2020 19:53:04.493 | 7
30/09/2020 19:53:04.446 | 6
30/09/2020 19:53:01.821 | 5
30/09/2020 19:53:01.680 | 4
30/09/2020 19:53:01.555 | 3
30/09/2020 19:53:01.462 | 2
30/09/2020 19:53:01.430 | 1
30/09/2020 19:53:01.399 | 0
30/09/2020 19:52:09.462 | 1,1726 1,2917 0,7168
30/09/2020 19:52:08.712 | 9
30/09/2020 19:52:08.071 | 8
30/09/2020 19:52:07.853 | 7
30/09/2020 19:52:07.384 | 6
30/09/2020 19:52:06.931 | 5
30/09/2020 19:52:06.712 | 4
30/09/2020 19:52:05.775 | 3
30/09/2020 19:52:04.587 | 2
30/09/2020 19:52:03.884 | 1
30/09/2020 19:52:03.728 | 0
30/09/2020 19:51:48.274 | cBot "Testbot" was started successfully for EURUSD, m1.


Thanks for your help with this complex problem!

Regards,
kebbo

 

 

 

 

 

 


@kebbo

PanagiotisCharalampous
02 Oct 2020, 08:52

Hi kebbo,

My code shows you how to detect a bat change as soon as it happens. If you want to use it for other timeframes you need to adjust it accordingly. If the code does not enter the if statement, it means the bar has not been created yet and there is nothing you can do about it. Your code is a completely wrong way to solve the problem it and I suggest to forget about it since it does not take into consideration that bars from other symbols might have not been completed yet.

Regarding your questions

But then the previous price is taken (from the candle before, which has to be index 2) and not the one that just closed the candle. Why? Is the bot faster than updating every chart?

The fact that the candle closed in one symbol does not mean that it is closed for the rest, hence you should avoid this approach

 how many lines of code can be "processed" per tick? If the market makes a jump of about 5 ticks, will the onTick method be called 5x completely? Even if the market is faster than the bot?

This depends on your pc. If it does not manage to process incoming ticks before the next one arrives, then they will be ignored.

but from the second candle/call it jumps very fast to the needed 10 ticks although there was no price movement at all, why is that?

OnTick() is called whenever a bir or ask price is changed for any of the subscribed symbols. Hence it will not correspond to the movement of the chart.

Best Regards,

Panagiotis 

Join us on Telegram


@PanagiotisCharalampous

kebbo
03 Oct 2020, 21:44

RE:

PanagiotisCharalampous said:

OnTick() is called whenever a bir or ask price is changed for any of the subscribed symbols. Hence it will not correspond to the movement of the chart.

 

 

Hi Panagiotis,

This is a point I suspected! The OnTick method is also called when a tick change happens in one of the other instruments, right? Not only in relation to the chart where the bot was started... this would explain a lot, I wasn't aware of it....Thank you!

Thank you also very much for your enlightening words!

Now I have taken a step forward.

I am not (yet) a professional programmer and have recognized the described problem and tried to develop a solution for it with my knowledges, this is how this approach came into being. I noticed relatively quickly that this can't be the right way, because it didn't work ;).
I am now in the process of rewriting my code a bit and integrating your approach.

best regards

 


@kebbo