ib-api-reloaded / ib_async

Python sync/async framework for Interactive Brokers API (replaces ib_insync)
BSD 2-Clause "Simplified" License
223 stars 37 forks source link

Contract can't be hashed #4

Closed tsauliu closed 3 months ago

tsauliu commented 3 months ago

when placing orders, it returned "ValueError: Contract Contract(secType='STK', symbol='AAPL', exchange='SMART/AMEX', currency='USD') can't be hashed"

but i tried to do the same using the orignal ib_insync, i successfully put in the order. please look into it when you have time, thanks!

my code as follows:

stock = Stock(symbol='AAPL', exchange='SMART/AMEX', currency='USD') action = MarketOrder(action='buy', totalQuantity=1, tif='DAY') order = ib.placeOrder(stock, action)

artemloy commented 3 months ago

Experiencing the same issue

mattsta commented 3 months ago

Are we sure this is a new problem? We had various "can't be hashed" issues on the old repo too.

This one is probably caused by the hash requiring the contractId to be populated (so contracts must be qualified using qualifyContracts or qualifyContractsAsync before you can use them):

    def isHashable(self) -> bool:
        """
        See if this contract can be hashed by conId.

        Note: Bag contracts always get conId=28812380, so they're not hashable.
        """
        return bool(self.conId and self.conId != 28812380 and self.secType != "BAG")

I'll at least add some better error messages plus some automated tests around this later today.

In [1]: import ib_async

In [2]: s = ib_async.Stock(symbol='AAPL', exchange='SMART/AMEX', currency='USD')

In [3]: {s: "test"}
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[3], line 1
----> 1 {s: "test"}

File ~/repos/ibkr/ib_insync/ib_async/contract.py:156, in Contract.__hash__(self)
    154 def __hash__(self):
    155     if not self.isHashable():
--> 156         raise ValueError(f"Contract {self} can't be hashed")
    158     if self.secType == "CONTFUT":
    159         # CONTFUT gets the same conId as the front contract, invert it here
    160         h = -self.conId

ValueError: Contract Stock(symbol='AAPL', exchange='SMART/AMEX', currency='USD') can't be hashed

In [4]: s_qualified = ib_async.Stock(conId=999999999999999999, symbol='AAPL', exchange='SMART/AMEX', currency='USD')

In [5]: {s_qualified: "test"}
Out[5]: {Stock(conId=999999999999999999, symbol='AAPL', exchange='SMART/AMEX', currency='USD'): 'test'}
artemloy commented 3 months ago

Are we sure this is a new problem? We had various "can't be hashed" issues on the old repo too.

I've checked various "can't be hashed" issues on the old repo before commenting here. However, it really seems like smth new as the topic starter mentioned with old ib_insync everything works as it should. With ib_async I can not retrieve OHLCV, keep getting hash error.

import pandas as pd

#Symbols list
symbols = ['AMZN', 'TSLA']

#Method to get OHLCV
def get_OHLCV(symbol, endDateTime='', durationStr='30 D', barSizeSetting = '1 hour', whatToShow='TRADES', useRTH=False, formatDate=1):
    bars = ib.reqHistoricalData(symbol, endDateTime, durationStr, barSizeSetting, whatToShow, useRTH, formatDate)
    df = util.df(bars)
    df["date"] = df["date"].dt.tz_convert("America/New_York")
    df = df.drop(columns=['average', 'barCount'])
    df.set_index('date', inplace=True)
    #df = df.iloc[::-1]
    df.to_csv('{}.csv'.format(symbol.symbol))
    df = pd.read_csv('{}.csv'.format(symbol.symbol))
    df.columns = ['date','open', 'high', 'low', 'close', 'volume']
    df['date'] = pd.to_datetime(df['date'])
    df.set_index('date', inplace=True)
    print(f'Data for {symbol.symbol} downloaded OK with OLD')
    return df

for symbol_str in symbols:
    symbol = Stock(symbol_str, 'SMART', 'USD')
    df = get_OHLCV(symbol)
artemloy commented 3 months ago

@mattsta If we rely on this thread for example it's clear that the issue was resolved with some update in the library.

mattsta commented 3 months ago

Thanks for the details of how to reproduce it (I added part of your example as a new test case we can keep tracking going forward).

Fixed the error with a larger cleanup which I think looks better overall and is probably a little faster (now it's using a direct lookup table for each type instead of crawling through up to 8 if-else statements for each field).

Added fix as new version 1.0.1 and updated pypi package too.

artemloy commented 3 months ago

@mattsta Thank you for doing it all so fast.