softwarespartan / IB4m

Interactive Brokers API for Matlab
GNU General Public License v2.0
62 stars 21 forks source link

How to create an Error object for handling errors return from IB #57

Open ecpgieicg opened 5 years ago

ecpgieicg commented 5 years ago

My task at hand is to detect the earliest available date for a given option contract and given data type (ie. TRADES, BID_ASK). A method to intelligently disambiguate errors returned from IB server will solve the task at hand. So I am also going to pose the question in the example of requesting option data but also ask more generally about com.tws.Error.

Let me start with a data request.

Assuming the IB4m path is in Matlab search path, the .jar file is in javapath, for example, with the following commands, and the connection to IB has been established.

javaaddpath(fullfile(pwd,'Jar','TWS973.jar'));
session = TWS.Session.getInstance();
session.eClientSocket.eConnect('127.0.0.1',7497,2);

I know that the AMD Jul 33.5 Call contract started to be available on June 25, 2019. The followings establish the correct contract object to said instrument.

>> [buf,lh] = TWS.initBufferForEvent(TWS.Events.CONTRACTDETAILS);
>> contract = com.ib.client.Contract();
>> contract.symbol("AMD");contract.secType("OPT");contract.exchange("SMART");contract.currency("USD");contract.lastTradeDateOrContractMonth("20190719");contract.strike(33.5);contract.right("Call");
>> session.eClientSocket.reqContractDetails(0,contract); pause(0.5);
>> contract.conid(buf.get().data.iterator().next().contractDetails.conid)

Now requesting for data exclusively from before June 25 will result in a com.tws.Error return, no data, but no particular syntax error in Matlab. (Just spelling out the obvious: Not only there is no data returned from IB server. The local cache object for historical data, if initialized, will also stay empty. Thus, this case of no available data is indistinguishable from the case of data has yet to returned unless the error return is also examined.)

>> [buf,lh] = TWS.initBufferForEvent(TWS.Events.ERROR);
>> session.eClientSocket.reqHistoricalData(105,contract,'20190623 16:00:00','3 D','1 min','TRADES',1,1,false,[]); pause(0.5);
105 162 Historical Market Data Service error message:HMDS query returned no data: AMD   190719C00033500@SMART Trades

What is the best practice to detect the error of "returned no data"?

I am thinking about creating an Error object identical to the above and use the embedded equals() method of the com.tws.Error class.

>> buf.get().data.equals(buf.get().data)
ans =
  logical
   1

My ad hoc alternative will be -- the naive way -- to extract the string from the error object and process the string from the toString() method. But the resultant string -- I suspect -- will not be stable. It can change from version to version. A change could be included in a small update and this method of classifying error by its toString() result would require update as well. Also at the moment I don't really know the string format of exception and error message with only string either. Not withstanding the above, when one uses this method, the incorrect identification of error type won't necessarily show.

Is there a way to create an Error object locally that will match the returned Error object? (And preferably, based on just the error code and some syntax rule and not based on the string description.)

giovannetti87 commented 2 years ago

I have perhaps a much more trivial issue. I would like to collect error messages into a workable cell, just like we do with buffers of prices etc. I can see errors popping up in my console as the script unfolds as a result of the listener in Session (please correct me), but I don't understand whether to get these errors into a cell I also have to define a specific event listener. Any help very much appreciated :)

softwarespartan commented 2 years ago

TWS.Session object sets up the callback to display each error event on the command window.

% set up listener to pipe the error messages to the command window
this.errorListenerHandle = event.listener(                          ...
    TWS.Events.getInstance  , ...
    TWS.Events.ERROR        , ...
    @(s,e)disp(e.event.data)  ...
);

This sets up callback to listen for TWS.Events.ERROR events and the function to perform for each event e is disp(e.event.data)

giovannetti87 commented 2 years ago

Hi Abel,

Thanks a lot for the timely response. Since I already have errors popping out with no code other than TWS.Session.getInstance(), does it mean that this code already includes the error listener/printer? And, also, if I want to collect the error string in a cell, how should I do?

thanks a lot for your time

softwarespartan commented 2 years ago

yeah, TWS.Session creates a listener for errors. It's possible to create many listeners for same event. Each listener will get a copy of the event. Therefore, the TWS.session defined callback prints the to the command window for the user and then you can create another listener and do whatever you want :)

Check out the IB API docs

https://interactivebrokers.github.io/tws-api/error_handling.html

To access the error message for this event would be e.data.errorMsg

giovannetti87 commented 2 years ago

Hi Abel, great! So, I generated a variable using assignin('base', 'xyzvariab', e.event.data) and I confirm I can store the error message. However, it seems like xyzvariab (which matlab codes as "error" variable) does not have any structure but only string. Is that right? thanks again :)

softwarespartan commented 2 years ago

I guess I'm not sure why store the event as a Matlab variable? It's already in the work space.

Why not just use event.data.errorMsg directly?

giovannetti87 commented 2 years ago

Hi Abel, I guess I already lost enough of my face to discard any residual face saving concern, but I can't locate the "event" in the work space. I do have the "this" structure file related to your example above, but if you're referring to call "this.data.errorMsg" it does not work either.

I am sure you're having a good laugh at reading my message

softwarespartan commented 2 years ago

No worries :) All good

So I would definitely read up on event processing in MATLAB

https://www.mathworks.com/help/matlab/matlab_oop/events-and-listeners-concepts.html

The even processing happens on a background thread (event dispatch thread or EDT)

Notice that using the circular buffer has the same effect of saying "hey just put this event in a basket in my workspace"

If you create a new variable for every event, you could easily have thousands are hundreds of thousands of variables created in your work space (not good).

You could simply add each event to the end of an array or cell-array. But, notice that the array could grow very large and potentially eat up all the system memory keeping. After a few hours many events are very old and not relevant any more so they can be discarded.

The nice thing about the circular buffer is that it has a limit on the number of events that can occupy main workspace. And, the circular buffer says "just keep the N most recent event" and automatically deletes older events.

On the other hand, if you have some UI component that shows, for example bid/ask, if you get an event for new bid price there is no reason to put that in a circular buffer just to go update the UI element to display the bid. Just have the call back update the UI element directly and discard the event. If there is another function that wants to do something else on bid update, just create a different callback and keep things separate.

giovannetti87 commented 2 years ago

Hi @softwarespartan thanks a lot for the patience and incredible work :) I confirm I'm reading/studying. I will also try to implement the profit function you kindly introduced as soon as I have time/skill to carry forward my scripts