Ibpy Getting Portfolio Information: Interactive Broker, Python
Solution 1:
Adding another answer given the length and major changes. Given I was working on some piece of code I answered another question with it and with a small adaptation can be used for the portfolio too.
Code:
from __future__ import (absolute_import, division, print_function,)
# unicode_literals)import collections
import sys
if sys.version_info.major == 2:
import Queue as queue
import itertools
map = itertools.imap
else: # >= 3import queue
import ib.opt
import ib.ext.Contract
classIbManager(object):
def__init__(self, timeout=20, **kwargs):
self.q = queue.Queue()
self.timeout = 20
self.con = ib.opt.ibConnection(**kwargs)
self.con.registerAll(self.watcher)
self.msgs = {
ib.opt.message.error: self.errors,
ib.opt.message.updatePortfolio: self.acct_update,
ib.opt.message.accountDownloadEnd: self.acct_update,
}
# Skip the registered ones plus noisy ones from acctUpdate
self.skipmsgs = tuple(self.msgs.keys()) + (
ib.opt.message.updateAccountValue,
ib.opt.message.updateAccountTime)
for msgtype, handler in self.msgs.items():
self.con.register(handler, msgtype)
self.con.connect()
defwatcher(self, msg):
ifisinstance(msg, ib.opt.message.error):
if msg.errorCode > 2000: # informative messageprint('-' * 10, msg)
elifnotisinstance(msg, self.skipmsgs):
print('-' * 10, msg)
deferrors(self, msg):
if msg.idisNone: # something is very wrong in the connection to tws
self.q.put((True, -1, 'Lost Connection to TWS'))
elif msg.errorCode < 1000:
self.q.put((True, msg.errorCode, msg.errorMsg))
defacct_update(self, msg):
self.q.put((False, -1, msg))
defget_account_update(self):
self.con.reqAccountUpdates(True, 'D999999')
portfolio = list()
whileTrue:
try:
err, mid, msg = self.q.get(block=True, timeout=self.timeout)
except queue.Empty:
err, mid, msg = True, -1, "Timeout receiving information"breakifisinstance(msg, ib.opt.message.accountDownloadEnd):
breakifisinstance(msg, ib.opt.message.updatePortfolio):
c = msg.contract
ticker = '%s-%s-%s' % (c.m_symbol, c.m_secType, c.m_exchange)
entry = collections.OrderedDict(msg.items())
# Don't do this if contract object needs to be referenced later
entry['contract'] = ticker # replace object with the ticker
portfolio.append(entry)
# return list of contract details, followed by:# last return code (False means no error / True Error)# last error code or None if no error# last error message or None if no error# last error messagereturn portfolio, err, mid, msg
ibm = IbManager(clientId=5001)
portfolio, err, errid, errmsg = ibm.get_account_update()
if portfolio:
print(','.join(portfolio[0].keys()))
for p in portfolio:
print(','.join(map(str, p.values())))
sys.exit(0) # Ensure ib thread is terminated
Result
Server Version: 76
TWS Time at connection:20160113 00:15:29 CET
---------- <managedAccountsaccountsList=D999999>
---------- <nextValidIdorderId=1>
---------- <errorid=-1,errorCode=2104,errorMsg=MarketdatafarmconnectionisOK:usfuture>
---------- <errorid=-1,errorCode=2104,errorMsg=MarketdatafarmconnectionisOK:eufarm>
---------- <errorid=-1,errorCode=2104,errorMsg=MarketdatafarmconnectionisOK:cashfarm>
---------- <errorid=-1,errorCode=2104,errorMsg=MarketdatafarmconnectionisOK:usfarm.us>
---------- <errorid=-1,errorCode=2106,errorMsg=HMDSdatafarmconnectionisOK:ushmds.us>
---------- <errorid=-1,errorCode=2106,errorMsg=HMDSdatafarmconnectionisOK:ilhmds>
---------- <errorid=-1,errorCode=2106,errorMsg=HMDSdatafarmconnectionisOK:cashhmds>
---------- <errorid=-1,errorCode=2106,errorMsg=HMDSdatafarmconnectionisOK:ethmds>
contract,position,marketPrice,marketValue,averageCost,unrealizedPNL,realizedPNL,accountName
IBM-STK-,10,25.0,250.0,210.0,40.0,0.0,D999999
The last 2 lines can be imported directly to (for example) Excel. Or given it's a list of dictionaries (what's getting printed out) it can be further manipulated in an script.
Solution 2:
The "columns" requirement is somehow vague, given a list already fulfills the requirement if each entry in the list is a list itself (and each index position in the sublists always contains the same field)
The message you receive is sent to the callback you have registered with tws. Each of the "dumped" fields can be accessed with "dot" notation or through dict-like methods like "keys", "values" and "items"
The major challenge is the contract: IB gives access to a large amount of exchanges and different trading assets. The "contract" object (and information in the IB Backend) seems to reflect an effort to provide a uniform/unified access to everything and at the same time shows they had to build upon existing infrastructure.
You mention "stock ticker" so I guess you'll probably be happy with something like: IBM-STK-SMART with the "ib" industry standard notation (the 2nd field indicates it is a stock and the 3rd that IB will use SMART routing for orders and price updated)
Let's go for a list of lists:
defacct_update(self, msg):
# Assume the function is a method in a class with access to a member# 'portfolio' which is a listifisinstance(msg, ib.opt.message.updatePortfolio):
c = msg.contract
ticker = '%s-%s-%s' % (contract.m_symbol, c.m_secType, c.m_exchange)
entry = [ticker]
entry.extend(msg.values)
self.portfolio.append(entry)
Unfortunately the "keys" method in the ibpy
messages is not a classmethod
, but the names are actually __slots__
. In the class holding the acct_update
method you coud do the following:
classMyClass(object):
portfields = ['ticker'] + ib.opt.message.updatePortfolio.__slots__
If rather than accessing the fields in a list by index you prefer the names of the fields already provided by ibpy, you can also make a dict of dicts
def__init__(self, ...)self.portfolio = collections.OrderedDict()
defacct_update(self, msg):
# Assume the function is a method in a class with access to a member# 'portfolio' which is a listif isinstance(msg, ib.opt.message.updatePortfolio):
c = msg.contract
ticker = '%s-%s-%s' % (contract.m_symbol, c.m_secType, c.m_exchange)
self.portfolio[ticker] = collections.OrderedDict(msg.items())
Which would allow you to get the latest ticker information by name and access the fields by name.
If you need to keep a per ticker history
def__init__(self, ...)self.portfolio = collections.defaultdict(list)
defacct_update(self, msg):
# Assume the function is a method in a class with access to a member# 'portfolio' which is a listif isinstance(msg, ib.opt.message.updatePortfolio):
c = msg.contract
ticker = '%s-%s-%s' % (contract.m_symbol, c.m_secType, c.m_exchange)
self.portfolio[ticker].extend(msg.values())
You could store the "items" rather than the values and later access the tuples if needed.
Solution 3:
Besides using ibpy, I will also import IBWrapper which can be downloaded from Github: https://github.com/anthonyng2/ib
import pandas as pd
import numpy as np
import time
from IBWrapper import IBWrapper, contract
from ib.ext.EClientSocket import EClientSocket
accountName = "Your Account ID"
callback = IBWrapper() # Instantiate IBWrapper. callback
tws = EClientSocket(callback) # Instantiate EClientSocket and return data to callback
host = ""
port = 4002# It is for default port no. in demo account
clientId = 25
tws.eConnect(host, port, clientId) # connect to TWS
create = contract() # Instantiate contract class
callback.initiate_variables()
tws.reqAccountUpdates(1, accountName)
time.sleep(2)
They are your updated account value and portfolio summary:
accvalue = pd.DataFrame(callback.update_AccountValue, columns = ['key', 'value', 'currency', 'accountName']) #[:199]
portfolio = pd.DataFrame(callback.update_Portfolio, columns=['Contract ID','Currency', 'Expiry','Include Expired','Local Symbol','Multiplier','Primary Exchange','Right',
'Security Type','Strike','Symbol','Trading Class','Position','Market Price','Market Value',
'Average Cost', 'Unrealised PnL', 'Realised PnL', 'Account Name'])
callback.update_AccountTime
print("AccountValue: \n" + str(accvalue))
print("portfolio: \n" + str(portfolio))
It is your updated Position Summary:
# Position Summary
tws.reqPositions()
time.sleep(2)
dat = pd.DataFrame(callback.update_Position,
columns=['Account','Contract ID','Currency','Exchange','Expiry',
'Include Expired','Local Symbol','Multiplier','Right',
'Security Type','Strike','Symbol','Trading Class',
'Position','Average Cost'])
dat[dat["Account"] == accountName]
print("Position Summary: \n" + str(dat))
Solution 4:
I wrote the following code to allow me to read my positions and NAVs directly from Interactive Brokers API.
# Interactive Brokers functions to import datadefread_positions(): #read all accounts positions and return DataFrame with informationfrom ibapi.client import EClient
from ibapi.wrapper import EWrapper
from ibapi.common import TickerId
import pandas as pd
classib_class(EWrapper, EClient):
def__init__(self):
EClient.__init__(self, self)
self.all_positions = pd.DataFrame([], columns = ['Account','Symbol', 'Quantity', 'Average Cost'])
defposition(self, account, contract, pos, avgCost):
index = str(account)+str(contract.symbol)
self.all_positions.loc[index]=account,contract.symbol,pos,avgCost
deferror(self, reqId:TickerId, errorCode:int, errorString:str):
if reqId > -1:
print("Error. Id: " , reqId, " Code: " , errorCode , " Msg: " , errorString)
defpositionEnd(self):
super().positionEnd()
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()
return(current_positions)
defread_navs(): #read all accounts NAVsfrom ibapi.client import EClient
from ibapi.wrapper import EWrapper
from ibapi.common import TickerId
import pandas as pd
classib_class(EWrapper, EClient):
def__init__(self):
EClient.__init__(self, self)
self.all_accounts = pd.DataFrame([], columns = ['reqId','Account', 'Tag', 'Value' , 'Currency'])
defaccountSummary(self, reqId, account, tag, value, currency):
if tag == 'NetLiquidationByCurrency':
index = str(account)
self.all_accounts.loc[index]=reqId, account, tag, value, currency
deferror(self, reqId:TickerId, errorCode:int, errorString:str):
if reqId > -1:
print("Error. Id: " , reqId, " Code: " , errorCode , " Msg: " , errorString)
defaccountSummaryEnd(self, reqId:int):
super().accountSummaryEnd(reqId)
self.disconnect()
ib_api = ib_class()
ib_api.connect("127.0.0.1", 7496, 0)
ib_api.reqAccountSummary(9001,"All","$LEDGER")
current_nav = ib_api.all_accounts
ib_api.run()
return(current_nav)
To test the code I saved it on a .py file named IB_API and run the following:
import IB_API
print("Testing IB's API as an imported library:")
all_positions = IB_API.read_positions()
all_navs = IB_API.read_navs()
print("Test ended")
I'm still trying to avoid an error [WinError 10038] that says "An operation was attempted on something that is not a socket" - please see my question on the topic
Post a Comment for "Ibpy Getting Portfolio Information: Interactive Broker, Python"