PendingOrders.Filled event is not deterministic

JerryTrader since: 06 Jan 2021;

  07 Apr 2021, 23:58
PendingOrders.Filled event is not deterministic

Hi all,

I'm developing a cBot, and it has a pretty basic function, nothing fancy:
I maintain a list of PendingOrder and Position to create a set of trades, so that when a condition is met, I can cancel/close all the orders/positions related to this set.

To keep this list updated, I subscribe to PendingOrders.Filled event, to remove the args.PendingOrder.Id from the list of pending orders, and add the args.Position.Id to the list of positions.

While backtesting my bot in non visual mode, it crashes.
I tested it visually, and it starts to crash at speed 1000x (up to 500x is fine).

What happens is that when the backtest runs too fast, the PendingOrders.Filled event is raised after the OnTick method is called.
In the OnTick method, when my condition is met, I want to :
- cancel all the pending orders of my list
- close all the positions of my list

But since the PendingOrders.Filled is called after the OnTick, my list is not up to date, and I'm ending with a "Technical Error".

The issue can be reproduced 100% of the time on the EURUSD M5 chart at 30/04/2020 00:34:00 (with m1 bars from the server).

Here is a cBot reproducing the issue:

using cAlgo.API;

namespace cAlgo
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FileSystem)]
    public class RaceConditionTests : Robot
    {
        private bool _cancelled = false;

        protected override void OnStart()
        {
            base.OnStart();

            PendingOrders.Filled += PendingOrders_Filled;

            var result = PlaceLimitOrder(TradeType.Buy, SymbolName, 10000, 1.08644);
            Print(string.Format("Limit order #{0} placed", result.PendingOrder.Id));
        }

        private void PendingOrders_Filled(PendingOrderFilledEventArgs obj)
        {
            Print(string.Format("PendingOrders.Filled event received. (Order #{0} filled. Position is #{1})", obj.PendingOrder.Id, obj.Position.Id));
        }

        protected override void OnTick()
        {
            base.OnTick();
            
            if (Ask < 1.0864 && !_cancelled)
            {
                _cancelled = true;
                Print("OnTick when condition is met");
                Print(string.Format("Cancel {0} pending orders", PendingOrders.Count));
                Print(string.Format("Close {0} positions", Positions.Count));
            }
        }
    }
}

Here are the screenshots:

Working at 500x

Not working at 1000x

My bot is working fine in live, and visual backtest 500x, but I can't deliver a cBot to my client without backtesting it.
It is really a blocking bug, and I don't think that I'm doing something fancy here.


Can you please provide a hotfix or a workaround for this bug ?

Thanks for your support,
Jerry

amusleh's avatar

amusleh since: 01 Mar 2021;

  09 Apr 2021, 14:07

Hi,

It calls the OnTick event whenever a new tick arrives and it has precedence over invoking events, that's how it works on real live market and the back tester simulates the same condition.

There is an issue while back testing in slower speed and it should not invoke PendingOrders.Filled event before calling OnTick, we will investigate it and if there was any bug it will be fixed in future releases.

Community Developer | Spotware

JerryTrader since: 06 Jan 2021;

  09 Apr 2021, 19:59

Hi amusleh, 
Thank you for your support.

If it is working in different ways depending on the backtesting speed, it is necessarily a bug.

I don't know how it is done under the hood, but in my opinion, it makes more sense to raise PendingOrders.Filled event first.
Otherwise, you end up in strange state where in the OnTick, you want to close a pending order, but this one doesn't exist anymore, because it has already been filled, but you're not yet aware of this.

I don't know how I can workaround this...

amusleh's avatar

amusleh since: 01 Mar 2021;

  09 Apr 2021, 21:59
RE:

JerryTrader said:

Hi amusleh, 
Thank you for your support.

If it is working in different ways depending on the backtesting speed, it is necessarily a bug.

I don't know how it is done under the hood, but in my opinion, it makes more sense to raise PendingOrders.Filled event first.
Otherwise, you end up in strange state where in the OnTick, you want to close a pending order, but this one doesn't exist anymore, because it has already been filled, but you're not yet aware of this.

I don't know how I can workaround this...

These types of issues can happen while trading on a live market, noting is guaranteed, you should take the TradeResult after each trade operation call and check if it was successful or not.

Raising OnTick has precedence over anything else, and you can also check if Bid/ask reached your pending order target price or not inside OnTick.

Community Developer | Spotware

JerryTrader since: 06 Jan 2021;

  10 Apr 2021, 17:28

amusleh said:

These types of issues can happen while trading on a live market, noting is guaranteed, you should take the TradeResult after each trade operation call and check if it was successful or not.

Yes, I check the TradeResult after each trade operation call, but then ... I don't know what to do with it ...
If I want to cancel a PendingOrder that doesn't exist anymore, I receive a TechnicalError (and not an EntityNotFound as I would expect), but I have no more information about what happened, and I can't find any way to "guess" that the order just get filled, but the event has not been raised yet, and that this order has now turned to a Position (so I don't have the Position ID).

Raising OnTick has precedence over anything else, and you can also check if Bid/ask reached your pending order target price or not inside OnTick.

Ok, I could check that to anticipate the PendingOrders.Filled event (which makes the event obsolete if I have to manually check the conditions of all my PendingOrders and increase my CPU usage). Why not, if I can workaround this bug ...

But then, if I find that the Bid/Ask reached my pending order, how can I find the corresponding position ID ? I don't see any ways to find it except from waiting for the PendingOrders.Filled event. Maybe I missed it ?

amusleh's avatar

amusleh since: 01 Mar 2021;

  10 Apr 2021, 19:45
RE:

JerryTrader said:

amusleh said:

These types of issues can happen while trading on a live market, noting is guaranteed, you should take the TradeResult after each trade operation call and check if it was successful or not.

Yes, I check the TradeResult after each trade operation call, but then ... I don't know what to do with it ...
If I want to cancel a PendingOrder that doesn't exist anymore, I receive a TechnicalError (and not an EntityNotFound as I would expect), but I have no more information about what happened, and I can't find any way to "guess" that the order just get filled, but the event has not been raised yet, and that this order has now turned to a Position (so I don't have the Position ID).

Raising OnTick has precedence over anything else, and you can also check if Bid/ask reached your pending order target price or not inside OnTick.

Ok, I could check that to anticipate the PendingOrders.Filled event (which makes the event obsolete if I have to manually check the conditions of all my PendingOrders and increase my CPU usage). Why not, if I can workaround this bug ...

But then, if I find that the Bid/Ask reached my pending order, how can I find the corresponding position ID ? I don't see any ways to find it except from waiting for the PendingOrders.Filled event. Maybe I missed it ?

Hi,

PendingOrders collection does change before invoking the PendingOrders.Filled event, if you check the PendingOrders collection inside OnTick it always give you to correct number of pending orders, there are ways to track your pending orders, like using the order label or comment and then you can check the comment/label of positions to find the matching position for your order.

Community Developer | Spotware

JerryTrader since: 06 Jan 2021;

  10 Apr 2021, 20:17

Ok, 

So there's no way, through the API, to "map" a PendingOrder to a Position, like we have in the PendingOrderFilledEventArgs class.
Thanks for the tip, using the order label or comment is feasible. It is risky to count on some text that the user can modify, though.

It looks like the API is missing something here.
Maybe you could add a "Position" property to the PendingOrder interface, that would be null if the PendingOrder is not filled, and that returns the Position when filled ?
So that, in the OnTick, we could retrieve the Position from the PendingOrder.

What do you think ?

amusleh's avatar

amusleh since: 01 Mar 2021;

  10 Apr 2021, 22:43
RE:

JerryTrader said:

Ok, 

So there's no way, through the API, to "map" a PendingOrder to a Position, like we have in the PendingOrderFilledEventArgs class.
Thanks for the tip, using the order label or comment is feasible. It is risky to count on some text that the user can modify, though.

It looks like the API is missing something here.
Maybe you could add a "Position" property to the PendingOrder interface, that would be null if the PendingOrder is not filled, and that returns the Position when filled ?
So that, in the OnTick, we could retrieve the Position from the PendingOrder.

What do you think ?

Hi,

User can't change the order label or comment, and there is no reason to add a Position property to pending order, you can use the PendingOrder.Filled event.

Your case is exceptional, you want to get notified as soon as possible when a pending order if filled, so that's why the PendingOrder Filled event might not be useful for you but it works fine most of the time.

As I said, OnTick has precedence over PendingOrder filled event, we want to notify the user as soon as possible when a new tick is arrived.

Community Developer | Spotware

JerryTrader since: 06 Jan 2021;

  12 Apr 2021, 11:32

Hi, 

First, I would like to apologize if you are offensed, because I feel like you're defensive.
I don't say that cTrader nor the API is bad, but in my opinion, this is a bug that needs to be fixed, and that shows some lacks from the API (using the order label might work but it is just a "hack" to workaround a lack/bug).

My only goal here is to go forward and help as much as I can.

amusleh said:

User can't change the order label or comment, and there is no reason to add a Position property to pending order, you can use the PendingOrder.Filled event.

There is no way of knowing which PendingOrder turned into which Position, except from listening to an non deterministic event that "sometimes" is called before, "sometimes" is called after the OnTick.
Without talking about the event raise bug, there is a relation between a PendingOrder and a Position, and this is already a reason to let the API user to retrieve an order from a position and vice versa.

amusleh said:

Your case is exceptional, you want to get notified as soon as possible when a pending order if filled, so that's why the PendingOrder Filled event might not be useful for you but it works fine most of the time.

I don't think that maintaining a list of PendingOrders and Positions is something exceptional.

amusleh said:

As I said, OnTick has precedence over PendingOrder filled event, we want to notify the user as soon as possible when a new tick is arrived.

I understand that. But in the other hand, if we take the example of an ObservableCollection, your collection is never modified before you get the event.
This is the only way you can be guaranteed that your collection is up to date.

amusleh's avatar

amusleh since: 01 Mar 2021;

  13 Apr 2021, 09:59

I'm not defensive, I'm trying to explain how the Automate API works.

As I said OnTick has precedence and we are not going to change this behavior.

Why you are trying to link OnTick with PendingOrders Filled event? there is no link between these two and nor with other methods like OnBar or OnTimer.

Right now all cBot methods are called sequentially by one thread, in future we might change this and use multiple threads to call different methods at the same time.

Code your cBot in a way that it doesn't rely on the sequence that these methods will be called, because in future it can change.

PendingOrders Filled event is where you get the position for triggered Pending Order, but in what sequence it will be called is not guaranteed and we call it as soon as possible.

If its very important for you, then you can create you own observable collections of pending orders and positions, use OnTick and Pending Orders events to keep your collections up to date based on your preference, you can create a library with your own classes and use it on your bots.

Community Developer | Spotware