Skip to content Skip to sidebar Skip to footer

Ibpy Getting Portfolio Information: Interactive Broker, Python

I have successfully written the code to extract the information from demo version of TWS regarding my positions using the code: tws_conn = conn.Connection.create(port=7497, cli

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"