erdewit / ib_insync

Python sync/async framework for Interactive Brokers API
BSD 2-Clause "Simplified" License
2.76k stars 726 forks source link

TWS returns wrong option prices same-day vs next day. #646

Closed Sebasvdr closed 10 months ago

Sebasvdr commented 10 months ago

I have been building an algo that will check my fills against the bid and ask price of a certain contract at the time that I receive an alert and at the time the order was submitted by the algo. 

However, when trying to fetch these prices the same day that the trade occurred, about half the fetched prices are correct and the other half are sometimes wildly off. When trying the same request the following day, all prices are correct.

Is there something I am doing wrong that prices would be different when requested for same-day vs requesting the price for the same contract for the same time, the next day?



Here is an example of what my script returns for the first same-day price fetch: Alert Time: 20230920 19:50:36, Contract: Option(symbol='SPX', lastTradeDateOrContractMonth='20230920', strike=4415.0, right='C', exchange='CBOE'), Alert Bid Price: 11.8, Alert Ask Price: 12.1 Fetched mid prices: Alert Time - 11.95, Script Run Time - 11.95 Executing update query: UPDATE AllTrades SET Alert Mid Price = 11.95, Submit Mid Price = 11.95 
 Here is the second pull that day, 30 minutes after market close:
 Alert Time: 20230920 19:50:36, Contract: Option(symbol='SPX', lastTradeDateOrContractMonth='20230920', strike=4415.0, right='C', exchange='CBOE'), Alert Bid Price: 21.5, Alert Ask Price: 22.1 Fetched mid prices: Alert Time - 21.8, Script Run Time - 21.8 Executing update query: UPDATE AllTrades SET Alert Mid Price = 21.8, Submit Mid Price = 21.8

And here is the same pull the following day before market open:
 Alert Time: 20230920 19:50:36, Contract: Option(symbol='SPX', lastTradeDateOrContractMonth='20230920', strike=4415.0, right='C', exchange='CBOE'), Alert Bid Price: 1.25, Alert Ask Price: 1.35 Fetched mid prices: Alert Time - 1.3, Script Run Time - 1.425 Executing update query: UPDATE AllTrades SET Alert Mid Price = 1.3, Submit Mid Price = 1.425

Here below is the function that fetched all 3 of these prices. I made sure the timezones are correctly set and the issue happens with any ticker, not just SPX although SPY and SPX are most often the ones to be wrong.

from ib_insync import *
import mysql.connector
import pandas as pd
from datetime import datetime, time
import signal
import pytz

# Initialize IBKR connection
ib = IB()

def fetch_market_price(new_trade):
    try:
        # Explicitly convert fields to the expected types
        symbol = str(new_trade['Symbol'])
        # Convert the datetime.date to a string and then replace dashes
        expiration_date_str = new_trade['Expiration Date'].strftime('%Y-%m-%d')
        lastTradeDateOrContractMonth = expiration_date_str.replace('-', '')
        right = str(new_trade['Right'])
        strike = float(new_trade['Strike Price'])    

        # Define the contract based on new_trade
        contract = Option(symbol=symbol, lastTradeDateOrContractMonth=lastTradeDateOrContractMonth, right=right, strike=strike, exchange='CBOE')

        # Initialize mid_price for both Alert Time and Script Run Time
        mid_price_alert_time = None
        mid_price_script_run_time = None

        # Fetch price based on Alert Time
        query_time_alert = pd.to_datetime(new_trade['Alert Time']).strftime('%Y%m%d %H:%M:%S')
        ticks_alert = ib.reqHistoricalTicks(
            contract,
            startDateTime=query_time_alert,
            endDateTime=query_time_alert,
            numberOfTicks=1,
            whatToShow='BID_ASK',
            useRth=False,
        )
        if len(ticks_alert) > 0:
            tick = ticks_alert[0]
            mid_price_alert_time = (tick.priceBid + tick.priceAsk) / 2 if tick.priceBid is not None and tick.priceAsk is not None else None
            print(f"Alert Time: {query_time_alert}, Contract: {contract}, Alert Bid Price: {tick.priceBid}, Alert Ask Price: {tick.priceAsk}")

        # Fetch price based on Script Run Time (if available)
        if 'Script Run Time' in new_trade and pd.notna(new_trade['Script Run Time']):
            query_time_script = pd.to_datetime(new_trade['Script Run Time']).strftime('%Y%m%d %H:%M:%S')
            ticks_script = ib.reqHistoricalTicks(
                contract,
                startDateTime=query_time_script,
                endDateTime=query_time_script,
                numberOfTicks=1,
                whatToShow='BID_ASK',
                useRth=False,
            )
            if len(ticks_script) > 0:
                tick = ticks_script[0]
                mid_price_script_run_time = (tick.priceBid + tick.priceAsk) / 2 if tick.priceBid is not None and tick.priceAsk is not None else None
        print(f"Fetched mid prices: Alert Time - {mid_price_alert_time}, Script Run Time - {mid_price_script_run_time}")

    except Exception as e:
        print(f"Error fetching market price: {e}")
        return None, None  # Set to 0 if an error occurs

    return mid_price_alert_time, mid_price_script_run_time

Thanks for any help!

erdewit commented 10 months ago

Questions about the market data that IB returns are better posed to the IB help desk.