EDITED: I found a solution regarding the error messages - it was a bug on IB's API. The code I show as an answer below should be useful for those looking for a clean solution to read positions, and also NAVs, from their accounts at IB.
The original question [SEE SOLUTION BELOW; LEAVING ORIGINAL QUESTION HERE FOR CONTEXT]: I'm trying to get all my accounts positions at Interactive Brokers [IB] using the firm's API. Their documentation, although extensive, is extremely confusing. Sample codes are full of unnecessary commands - I want something very streamlined.
I need help with:
- How to get the information "per account" [SOLVED]
- How to bring the variables to a DataFrame [SOLVED]
- How to avoid IB's API from printing a series of error messages [SOLVED]
The code so far:
from ibapi.client import EClient
from ibapi.wrapper import EWrapper
from ibapi.common import * #for TickerId type
import pandas as pd
class ib_class(EWrapper, EClient):
def __init__(self):
EClient.__init__(self, self)
self.all_positions = pd.DataFrame([], columns = ['Account','Symbol', 'Quantity', 'Average Cost'])
def position(self, account, contract, pos, avgCost):
index = str(account)+str(contract.symbol)
self.all_positions.loc[index]=account,contract.symbol,pos,avgCost
def error(self, reqId:TickerId, errorCode:int, errorString:str):
if reqId > -1:
print("Error. Id: " , reqId, " Code: " , errorCode , " Msg: " , errorString)
def positionEnd(self):
self.disconnect()
ib_api = ib_class()
ib_api.connect("127.0.0.1", 7496, 0)
ib_api.reqPositions()
current_positions = ib_api.all_positions
ib_api.run()
When I run the code above I get a series of (i) account numbers, (ii) the contract symbol, (iii) position and (iv) average cost. This, therefore, answers question #1. You might want to "print" these values to see how IB's API send you the information.
I was also able to define a DataFrame variable all_positions
that receives the information I was looking for. See the result below. Note that I had to create an "index" variable that is a unique identifier for each row of the DataFrame (as a combination of the account number and symbol). I didn't find a way to 'append' information to the DataFrame without this 'index' (any idea on how to do it would be welcome):
As for the last issue (the error messages):
Brian's suggestion of the "error" function (see below) got rid of the "-1" errors. But I still get the following:
unhandled exception in EReader thread Traceback (most recent call last): File "C:\Users\danil\Anaconda3\lib\site-packages\ibapi-9.76.1-py3.7.egg\ibapi\reader.py", line 34, in run data = self.conn.recvMsg() File "C:\Users\danil\Anaconda3\lib\site-packages\ibapi-9.76.1-py3.7.egg\ibapi\connection.py", line 99, in recvMsg buf = self._recvAllMsg() File "C:\Users\danil\Anaconda3\lib\site-packages\ibapi-9.76.1-py3.7.egg\ibapi\connection.py", line 119, in _recvAllMsg buf = self.socket.recv(4096) OSError: [WinError 10038] An operation was attempted on something that is not a socket