SmartQuant Discussion

Automated Quantitative Strategy Development, SmartQuant Product Discussion and Technical Support Forums
It is currently Tue Dec 10, 2024 9:13 am

All times are UTC + 3 hours




Post new topic Reply to topic  [ 14 posts ] 
Author Message
PostPosted: Tue Dec 03, 2019 12:49 am 
Offline

Joined: Mon Oct 04, 2010 5:13 pm
Posts: 363
Hi,

What is the correct usage of the EmitCancelReject call? For example - sometimes I can send Cancel of an order that has already been filled. In this case my provider plugin sees that the order is already filled and therefore cannot be cancelled. Naturally I send back the EmitCancelReject. But I'm not sure regarding the order.Status. Should I put there Filled or Rejected? When I set there Filled a weird problem occurs - it causes an additional call of OnOrderFilled in Strategy.

Thanks in advance
E


Top
 Profile  
 
PostPosted: Tue Dec 03, 2019 10:36 am 
Offline

Joined: Wed May 05, 2010 9:49 pm
Posts: 583
Hi,
for filled order(filled status already in framework):
Code:
            ExecutionReport report = new ExecutionReport(order);

            report.dateTime = framework.Clock.DateTime;
            report.execType = ExecType.ExecCancelReject;
            report.ordStatus = OrderStatus.Filled;
            report.cumQty = cumQty; //all for filled order
            report.leavesQty = leavesQty; //0 for filled order
            report.text = rejectText;
           
            EmitExecutionReport(report, queued);


Test strategy:
Code:
    public class MyStrategy : InstrumentStrategy
    {
      Order order;
      bool testCommandSend;
      
      public MyStrategy(Framework framework, string name)
            : base(framework, name)
        {
        }

      protected override void OnTrade(SmartQuant.Instrument instrument, SmartQuant.Trade trade)
      {
         if (order == null)   
            order = Buy(instrument, 100, "test order");
         else if (!testCommandSend)
         {
            testCommandSend = true;
            
            Cancel(order);
         }
      }   
    }


OrderManager:
Attachment:
CancelReject.png
CancelReject.png [ 19.5 KiB | Viewed 49487 times ]


Top
 Profile  
 
PostPosted: Tue Dec 03, 2019 10:49 am 
Offline

Joined: Mon Oct 04, 2010 5:13 pm
Posts: 363
Thanks for the quick answer. Not sure I understood it.
Here is my situation - I have my own user execution provider that connects to my broker. I wrote the provider plugin myself.

Sometimes my strategy sends Cancel order immediately after Market order. The Cance() method in my provider checks if the order has already been filled and if it is, I issue the EmitCancelReject. Naturally the order status is Filled here. So I discovered that once EmitCancelReject is called, the strategy gets additional (unexpected) call to OnOrderFilled. I'm looking for a way to prevent that OnOrderFilled, because I have already received it for that order.
Can it be that when the framework gets OnCancelReject it again checks the order status and, since it is "Filled" you call the OnOrderFilled again?

Thanks
E


Top
 Profile  
 
PostPosted: Tue Dec 03, 2019 11:06 am 
Offline

Joined: Wed May 05, 2010 9:49 pm
Posts: 583
OrderManager sends OnOrderFilled only when report.ExecType = ExecType.ExecTrade,
check this. In your case, report should have ExecType.ExecCancelReject


Top
 Profile  
 
PostPosted: Tue Dec 03, 2019 11:30 am 
Offline

Joined: Mon Oct 04, 2010 5:13 pm
Posts: 363
Thanks


Top
 Profile  
 
PostPosted: Tue Dec 03, 2019 12:02 pm 
Offline

Joined: Mon Oct 04, 2010 5:13 pm
Posts: 363
Short question. Do I understand it correctly that EmitExecutionReport is a part of the UserProvider and was introduced relatively recently? Below is a disassembly of UserProvider that I use.

Code:
#region Assembly OpenQuant.API, Version=1.0.4700.23555, Culture=neutral, PublicKeyToken=null
// C:\Program Files\SmartQuant Ltd\OpenQuant\Bin\OpenQuant.API.dll
#endregion

using System;

namespace OpenQuant.API.Plugins
{
    public class UserProvider
    {
        protected byte id;
        protected string name;
        protected string description;
        protected string url;
        protected bool isConnected;

        protected UserProvider();

        protected virtual bool IsConnected { get; }

        protected virtual void Cancel(Order order);
        protected virtual void CancelHistoricalData(HistoricalDataRequest request);
        protected virtual void Connect();
        protected virtual void Disconnect();
        protected void EmitAccepted(Order order);
        protected void EmitCancelled(Order order);
        protected void EmitCancelReject(Order order, OrderStatus status, string message);
        protected void EmitConnected();
        protected void EmitDisconnected();
        protected void EmitError(string message);
        protected void EmitError(int id, int code, string message);
        protected void EmitFilled(Order order, double price, int quantity, CommissionType commissionType, double commission);
        protected void EmitFilled(Order order, double price, int quantity);
        protected void EmitHistoricalDataCancelled(HistoricalDataRequest request);
        protected void EmitHistoricalDataCompleted(HistoricalDataRequest request);
        protected void EmitHistoricalDataError(HistoricalDataRequest request, string message);
        protected void EmitNewBar(Instrument instrument, BarType barType, long barSize, DateTime beginDateTime, DateTime endDateTime, double open, double high, double low, double close, long volume);
        protected void EmitNewBarOpen(Instrument instrument, BarType barType, long barSize, DateTime beginDateTime, DateTime endDateTime, double open, double high, double low, double close, long volume);
        protected void EmitNewBarSlice(long barSize);
        protected void EmitNewHistoricalBar(HistoricalDataRequest request, DateTime datetime, double open, double high, double low, double close, long volume);
        protected void EmitNewHistoricalQuote(HistoricalDataRequest request, DateTime datetime, double bid, int bidSize, double ask, int askSize);
        protected void EmitNewHistoricalTrade(HistoricalDataRequest request, DateTime datetime, double price, int size);
        protected void EmitNewOrderBookUpdate(Instrument instrument, DateTime time, BidAsk side, OrderBookAction action, double price, int size, int position);
        protected void EmitNewQuote(Instrument instrument, DateTime time, double bid, int bidSize, double ask, int askSize);
        protected void EmitNewQuote(Instrument instrument, DateTime time, byte providerId, double bid, int bidSize, double ask, int askSize);
        protected void EmitNewTrade(Instrument instrument, DateTime time, byte providerId, double price, int size);
        protected void EmitNewTrade(Instrument instrument, DateTime time, double price, int size);
        protected void EmitPendingCancel(Order order);
        protected void EmitPendingReplace(Order order);
        protected void EmitRejected(Order order, string message);
        protected void EmitReplaced(Order order);
        protected void EmitReplaceReject(Order order, OrderStatus status, string message);
        protected virtual BrokerInfo GetBrokerInfo();
        [Obsolete("Use Replace(Order,double,double,double) method.", false)]
        protected virtual void Replace(Order order);
        protected virtual void Replace(Order order, double newQty, double newPrice, double newStopPrice);
        protected virtual void RequestHistoricalData(HistoricalDataRequest request);
        protected virtual void Send(Order order);
        protected virtual void Shutdown();
        protected virtual void Subscribe(Instrument instrument);
        protected virtual void Unsubscribe(Instrument instrument);
    }
}


Top
 Profile  
 
PostPosted: Tue Dec 03, 2019 1:41 pm 
Offline

Joined: Wed May 05, 2010 9:49 pm
Posts: 583
Wait, did you mention OpenQuant, not OpenQuant2014?
I need to look again.


Top
 Profile  
 
PostPosted: Tue Dec 03, 2019 1:43 pm 
Offline

Joined: Mon Oct 04, 2010 5:13 pm
Posts: 363
Yes, not 2014. It is a quite old one


Top
 Profile  
 
PostPosted: Tue Dec 03, 2019 4:22 pm 
Offline

Joined: Wed May 05, 2010 9:49 pm
Posts: 583
I tested simple provider, this code works well:

Code:
      protected override void Send(Order order)
      {
         if (!IsConnected)
         {
            EmitError("Not connected.");

            return;
         }

         base.EmitAccepted(order);
         base.EmitFilled(order, order.Price, (int)order.Qty);
         base.EmitCancelReject(order, order.Status, "test reject message");
      }


I am not sure that this code fully reproduces your situation, in this case, please provide more detailed information for reproduction. You can do it privately.


Top
 Profile  
 
PostPosted: Tue Dec 03, 2019 6:07 pm 
Offline

Joined: Mon Oct 04, 2010 5:13 pm
Posts: 363
Thanks. Will try soon. The only difference with my code is that I send the EmitCancelReject from another context - i.e. from the Cancel() not from the SendOrder

Thanks
E


Top
 Profile  
 
PostPosted: Tue Dec 03, 2019 6:23 pm 
Offline

Joined: Mon Oct 04, 2010 5:13 pm
Posts: 363
So I rechecked my code. The only difference is that I call that EmitCancelReject from the Cancel() method in provider. Then I get 2 consequent calls to
Code:
        public override void OnOrderStatusChanged(Order order)
        {
            Logger.Instance.Log(LogLevel.INFO, Instrument.Symbol + " order status has been changed. Order id: " + order.OrderID + " Current status is: " + order.Status + " Odered side is: " + order.Side + " Order quantity is: " + order.Qty + ". Order price is: " + order.Price + ". Stop Price:" + order.StopPrice + " group: " + order.OCAGroup);
}


Here is my log to prove that:

2019-12-03 00:40:31.9760|INFO|IBProviderLogger|OrderStatusChanged for Filled order id 60962 filled 1 remaining 0 avg price 3120
2019-12-03 00:40:31.9916|INFO|IBProviderLogger|OrderStatusChanged for Filled order id 60962 filled 1 remaining 0 avg price 3120

You see - it happen almost immediately but with slight delay. If I remove my call to EmitCancelReject then I have only one log of those above.

I very much appreciate your help on this. But I understand that I'm running a very old version of OQ so please don't waste too much of your time on this. Just curious if there is anything that can be done.

Thanks
E


Top
 Profile  
 
PostPosted: Tue Dec 03, 2019 6:26 pm 
Offline

Joined: Mon Oct 04, 2010 5:13 pm
Posts: 363
I've found a workaround not in the provider but in the strategy. I'll keep a track of filled order and will ignore next fill.

Thanks a lot
Appreciated!


Top
 Profile  
 
PostPosted: Wed Dec 04, 2019 10:23 am 
Offline

Joined: Wed May 05, 2010 9:49 pm
Posts: 583
Well, I'm glad to hear that.
I tested strategy with cancellation and OrderStatusChanged has not fired twice:
Attachment:
orderstatuschanged.png
orderstatuschanged.png [ 20.07 KiB | Viewed 49483 times ]


Perhaps recent versions of OQ have something for this.

Attachment:
CancelReject2.png
CancelReject2.png [ 59.59 KiB | Viewed 49483 times ]


Top
 Profile  
 
PostPosted: Wed Dec 04, 2019 10:46 am 
Offline

Joined: Mon Oct 04, 2010 5:13 pm
Posts: 363
Thanks a lot for your efforts again. Really appreciated.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 14 posts ] 

All times are UTC + 3 hours


Who is online

Users browsing this forum: No registered users and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
cron
Powered by phpBB® Forum Software © phpBB Group