PendingOrders.Filled event is not deterministic

Created at 07 Apr 2021, 23:58
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!
JE

JerryTrader

Joined 06.01.2021

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


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


@JerryTrader
Replies

amusleh
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.


@amusleh

JerryTrader
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...


@JerryTrader

amusleh
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.


@amusleh

JerryTrader
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 ?


@JerryTrader

amusleh
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.


@amusleh

JerryTrader
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 ?


@JerryTrader

amusleh
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.


@amusleh

JerryTrader
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.


@JerryTrader

amusleh
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.


@amusleh

JerryTrader
13 Apr 2021, 12:06

RE:

amusleh said:

Why you are trying to link OnTick with PendingOrders Filled event?

I'm not trying to link them, I'm trying to create a TradeCollection class that groups some PendingOrders and Positions together.
To keep it up to date, I was relying on the PendingOrders.Filled event to remove the order from one collection, and add the new Position to the other collection.

When you look at an API and find an event like PendingOrders.Filled, you expect that it can be used to know exactly when an order is filled.
The documentation doesn't mention anywhere that this event is called sequentially, and after the OnTick (which is partly wrong, since there is a bug depending on the backtesting speed).
 

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.

I wish I could. Usually, you do this by relying on events, which can't be done here.

 

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.

This is exactly my biggest problem here.
So how can you close a position in the OnTick, if you didn't received the PendingOrders Filled event (since it seems to be the only way to get the position for a triggered PendingOrder) ?
Relying on some text parsing in the order label is just a hack to fill a gap in the API.

 

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.

This is exactly what I do. And keeping my collection up to date is exactly what I'm trying to achieve.
But using OnTick and PendingOrders events "based on my preference" ... I wish I could but I'm facing the issues I'm trying to explain from the beginning :

- If I use OnTick, I can see that a PendingOrder is missing, but I can't find a way to get the Position yet
- If I use PendingOrders.Filled, I can get the Position for the triggered pending order, but it is too late : in the OnTick I already tried to close a pending order that doesn't exist anymore because it has been triggered.

 


@JerryTrader

PanagiotisCharalampous
13 Apr 2021, 15:33

Hi JerryTrader,

It seems you are misunderstanding how things work. Events triggered by trading actions on the server have nothing to do with ticks. They are separate concerns and handled independently. A pending order is a order that is kept on and managed by the server. When the order is triggered, then it is not an order anymore, since it has been sent to the LP for execution. At that moment it is removed from the PendingOrders list, hence you cannot apply any actions on it e.g. cancel it. But It might take some time for the order to be filled or rejected e.g. you could wait for 10 seconds. There is no guarantee to what will happen to the order e.g. if it will be filled or canceled. Hence there is a time span of uncertainty where neither a position exits nor an order, and we cannot know the length of this time span either. However all the rest of the activity on the trading platform has to continue as usual e.g. spot prices streamed. This is not a bug, this is how things work. If this causes problems to your cBot logic e.g. a pending order has been sent for execution but you have no position yet, then this problem needs to be handled at the level of the cBot, since it is specific to your solution. It is you who needs to decide what happens at that time of uncertainty and not the API. Thus a solution could be to develop your own custom collections of orders and positions and manage them as per your requirements e.g. remove pending orders only when filled. I think this is what Ahmad is trying to convey here.

Best Regards,

Panagiotis 

Join us on Telegram


@PanagiotisCharalampous

JerryTrader
13 Apr 2021, 15:56

RE:

Thanks Panagiotis for your complete answer. It is much more clear now.
I will have to think more about how I can develop this collection.

Would it be possible, on your side, to consider the feasibility of adding a way to find the Position given a PendingOrder (if this order has been filled, of course) ?
I don't know, something like an event that we subscribe on the PendingOrder, for example.

Or am I still misunderstanding something, and it doesn't make any sense to request such a feature ?

 

Anyways, a big thanks to you guys, Ahmad and Panagiotis.
By your precious support, you add a lot of value to this platform and API (that truly lacks documentation by the way).

Keep up the good work !


@JerryTrader