Komnomnomnom / swigibpy

Third party Interactive Brokers Python API generated from TWS C++ API using SWIG.
http://github.com/Komnomnomnom/swigibpy/
Other
157 stars 35 forks source link

std::bad_alloc when trying to get open Contract properties? #21

Closed troyshu closed 10 years ago

troyshu commented 10 years ago

First, let me just say thank you so much for developing swigibpy, it makes interfacing with the Interactive Brokers API using Python infinitely easier (and, combined with the pandas library, can make some pretty powerful trading algos).

I'm running into the C++ memory error std::bad_alloc when trying to access properties (such as symbol) for any open Contracts in my account.

The below is an example (based off of the provided placeorder_example.py): it successfully gets all open positions in my account (the freely accessible IB demo account number is used here), but for any position, when I try to access a property of the Contract (e.g. contract.symbol), it throws the std::bad_alloc error.

Do you know how I could go about resolving this issue? Thank you for your help in advance!

(Because this example uses the demo account, you might have to run this code during market hours and make some trades in the demo account first, because the demo account seems to reset itself nightly, and obviously the code doesn't do anything when there aren't any open positions. Demo account downloadable at https://www.interactivebrokers.com/en/?f=%2Fen%2Fpagemap%2Fpagemap_demo.php, button "Try Individual Demo")


import datetime as dt

import sys
from time import sleep

from swigibpy import EWrapper, EPosixClientSocket, Contract, Order, TagValue,\
        TagValueList

try:
    input = raw_input
except:
    pass

###

orderId = None

availableFunds = 0
netLiquidationValue = 0

currentPositions = []

class PlaceOrderExample(EWrapper):
    '''Callback object passed to TWS, these functions will be called directly
    by TWS.

    '''

    def openOrderEnd(self):
        '''Not relevant for our example'''
        pass

    def execDetails(self, id, contract, execution):
        '''Not relevant for our example'''
        pass

    def managedAccounts(self, openOrderEnd):
        '''Not relevant for our example'''
        pass

    ###############

    def nextValidId(self, validOrderId):
        '''Capture the next order id'''
        global orderId
        orderId = validOrderId

    def orderStatus(self, id, status, filled, remaining, avgFillPrice, permId,
            parentId, lastFilledPrice, clientId, whyHeld):

        print(("Order #%s - %s (filled %d, remaining %d, avgFillPrice %f,"
               "last fill price %f)") % (
                id, status, filled, remaining, avgFillPrice, lastFilledPrice))

    def openOrder(self, orderID, contract, order, orderState):

        print("Order opened for %s" % contract.symbol)

    ####account value
    def updateAccountValue(self, key, value, currency, accountName):
        global availableFunds
        global netLiquidationValue

        #get how much current available funds we have, also our net liquidation value
        if currency == 'USD':

            if key == 'AvailableFunds':
                availableFunds = float(value)
            elif key=='NetLiquidation':
                netLiquidationValue = float(value)

    def accountDownloadEnd(self,accountName):
        print 'account download ended for %s' % (accountName)
    def updateAccountTime(self,timestamp):
        print 'account information pulled at %s' % (timestamp)
    def updatePortfolio(self, contract, position, marketPrice, marketValue, averageCost, unrealizedPNL, realizedPNL, accoutName):
        #this only called when there are actually positions

        global currentPositions
        currentPositions.append((contract,position))

# Instantiate our callback object
callback = PlaceOrderExample()

# Instantiate a socket object, allowing us to call TWS directly. Pass our
# callback object so TWS can respond.
tws = EPosixClientSocket(callback)

# Connect to tws running on localhost
tws.eConnect("", 7496, 42)

#account updates
tws.reqAccountUpdates(True,'DU15068')

sleep(3)
print 'available funds: %s' % (availableFunds)
print 'net liquidation value: %s' % (netLiquidationValue)

for position in currentPositions:
    contract = position[0]
    quantity = position[1]

    print contract.symbol

print("******************* Press ENTER to quit when done *******************\n")
input()

print("\nDisconnecting...")
tws.eDisconnect()
Komnomnomnom commented 10 years ago

thanks for the detailed report. I can reproduce using the code you provided and will investigate.

Komnomnomnom commented 10 years ago

Looks like a problem accessing the contract object from separate threads.

The updatePortfolio method is called in the callback thread and it appends to the global currentPositions list. You then try to read from that in the main thread and kaboom. There is no problem accessing contract.symbol from within the callback thread in updatePortfolio.

troyshu commented 10 years ago

Awesome, you're absolutely right! Thank you again for your help.

Below is the code that successfully grabs each position symbol and quantity, in case any one is interested


import datetime as dt

import sys
from time import sleep

from swigibpy import EWrapper, EPosixClientSocket, Contract, Order, TagValue,\
        TagValueList

try:
    input = raw_input
except:
    pass

###

orderId = None

availableFunds = 0
netLiquidationValue = 0

currentPositions = {}

class PlaceOrderExample(EWrapper):
    '''Callback object passed to TWS, these functions will be called directly
    by TWS.

    '''

    def openOrderEnd(self):
        '''Not relevant for our example'''
        pass

    def execDetails(self, id, contract, execution):
        '''Not relevant for our example'''
        pass

    def managedAccounts(self, openOrderEnd):
        '''Not relevant for our example'''
        pass

    ###############

    def nextValidId(self, validOrderId):
        '''Capture the next order id'''
        global orderId
        orderId = validOrderId

    def orderStatus(self, id, status, filled, remaining, avgFillPrice, permId,
            parentId, lastFilledPrice, clientId, whyHeld):

        print(("Order #%s - %s (filled %d, remaining %d, avgFillPrice %f,"
               "last fill price %f)") % (
                id, status, filled, remaining, avgFillPrice, lastFilledPrice))

    def openOrder(self, orderID, contract, order, orderState):

        print("Order opened for %s" % contract.symbol)

    ####account value
    def updateAccountValue(self, key, value, currency, accountName):
        global availableFunds
        global netLiquidationValue

        #get how much current available funds we have, also our net liquidation value
        if currency == 'USD':

            if key == 'AvailableFunds':
                availableFunds = float(value)
            elif key=='NetLiquidation':
                netLiquidationValue = float(value)

    def accountDownloadEnd(self,accountName):
        print 'account download ended for %s' % (accountName)
    def updateAccountTime(self,timestamp):
        print 'account information pulled at %s' % (timestamp)
    def updatePortfolio(self, contract, position, marketPrice, marketValue, averageCost, unrealizedPNL, realizedPNL, accoutName):
        #this only called when there are actually positions

        global currentPositions
        symbol = contract.symbol
        currentPositions[symbol] = position

# Instantiate our callback object
callback = PlaceOrderExample()

# Instantiate a socket object, allowing us to call TWS directly. Pass our
# callback object so TWS can respond.
tws = EPosixClientSocket(callback)

# Connect to tws running on localhost
tws.eConnect("", 7496, 42)

#account updates
tws.reqAccountUpdates(True,'DU15068')

sleep(3)
print 'available funds: %s' % (availableFunds)
print 'net liquidation value: %s' % (netLiquidationValue)

print 'positions:'
for symbol in currentPositions:
    print "%s: %s" % (symbol,currentPositions[symbol])

print("******************* Press ENTER to quit when done *******************\n")
input()

print("\nDisconnecting...")
tws.eDisconnect()