Real time callback different than historical quotes?

I was playing around with the APIs and found the real time quote callback through StockDataStream:subscribe_quotes can give very different price data for some symbols than what’s pulled from StockHistoricalDataClient:get_stock_quotes. The StockDataStream contains level 2 book data while the StockHistoricalDataClient is comparatively much closer to the real price. As an example, below is WYNN streaming vs. historical quotes for the same time period that you can see the difference from the highlights. Am I missing any filter or supposed to use specific exchange? Anyone has idea of how to get rid of the level 2 “noises”? Thanks!

1 Like

anyone to address this? :hot_face:

I’m trying to move my algos over to Alpaca and one went rogue during testing. I noticed the same thing as you. I’m wondering if this would be fixed by subscribe_trades rather than subscribe_quotes. But regardless, does this mean Alpaca is quoting 27.5 point spread on BRK.B? That should be a highly liquid stock with very minimal spread.

This can’t be accurate during market hours…

@JeremyWhittaker The answer is the same: if you’re using the free plan, you can only see the IEX book.

I guess I don’t know market depth enough to understand what is going on here. Are people really paying those prices on IEX? Or is the data trash?

@JeremyWhittaker The data is not trash, it shows the top of the book on the IEX exchange, which is only one of the many exchanges. If you see for example bid $305 - ask $330 on that single exchange that means there is someone, who is willing to buy that security for $305, and another someone, who’s willing to sell it for $330. All trades must happen within this range. But, crucially, other exchanges might have much better rates, e.g. someone on another exchange might be selling for $320.

Our unlimited plan provides you access to the best bid and ask across all exchanges. In the above example that could be for example $315 x $316.

I have the unlimited plan. I just didn’t want to trade this particular algo in my live account. This code was for testing. I’m going to modify my code to pull quotes from my live key instead of demo.

Regarding these prices. I find it hard to believe people are paying these drastically different prices. Something doesn’t seem right to me.

These are the spreads on BRK.B today. I find it hard to believe Berkshire baby B’s have a spread of 141 points on a stock that is worth $324. That’s a 43.5% spread.

Or look at any of these stocks. these spreads are absurd.

Please share the code how you’re calculating these.

I believe specifying SIP(as you previously suggested) fixed the code:
wss_client = CustomStockDataStream(LIVE_API_KEY, LIVE_SECRET_KEY, data_feed=‘sip’)

But without data_feed=‘sip’ that is how I generated those charts.

def store_minute_data(symbol, bid, ask):
    now = datetime.datetime.now()
    timestamp = now.replace(second=0, microsecond=0)

    if symbol not in minute_data:
        minute_data[symbol] = pd.DataFrame(columns=['timestamp', 'bid', 'ask'])

    # Update the data for the current minute
    if not minute_data[symbol].empty and minute_data[symbol].iloc[-1]['timestamp'] == timestamp:
        minute_data[symbol].iloc[-1] = [timestamp, bid, ask]
    else:
        new_data = pd.DataFrame({'timestamp': timestamp, 'bid': bid, 'ask': ask}, index=[0])
        minute_data[symbol] = pd.concat([minute_data[symbol], new_data], ignore_index=True)

    # Save the data to disk
    today_date = datetime.datetime.now().strftime("%Y-%m-%d")
    folder_path = 'data'
    if not os.path.exists(folder_path):
        os.makedirs(folder_path)
    file_path = f'{folder_path}/{symbol}_{today_date}_minute_data.pickle'

    if os.path.exists(file_path):
        with open(file_path, 'rb') as f:
            existing_data = pickle.load(f)
            combined_data = pd.concat([existing_data, minute_data[symbol]]).drop_duplicates(subset='timestamp', keep='last').reset_index(
                drop=True)
    else:
        combined_data = minute_data[symbol]

    with open(file_path, 'wb') as f:
        pickle.dump(combined_data, f)
async def quote_data_handler(data):
    if market_status_open == False:
        print(f'\033[33mMarket is closed, waiting for market to open in {time_to_market_open} seconds\033[0m')
        await asyncio.sleep(time_to_market_open)

    store_minute_data(data.symbol,  data.bid_price, data.ask_price)
    prices[data.symbol] = {'bid': data.bid_price, 'ask': data.ask_price}

    current_spread = get_alpaca_spread(data.symbol)
    # if current_spread is not None:
    #     print(f'{data.symbol}: Current spread: {current_spread:.2f} & Max Spread:{MAX_SPREAD}')
    # else:
    #     print(f'{data.symbol}: Current spread not available')
    if current_spread != None and current_spread > MAX_SPREAD:
        if data.symbol not in spread_too_high:
            spread_too_high.append(data.symbol)
            print(f'\033[33m{data.symbol}: Blacklisting, as the spread {current_spread} is higher than the max {MAX_SPREAD}\033[0m')
            # print(f'\033[33mThese symbols currently have a spread higher than {MAX_SPREAD}%\033[0m')
            # print(spread_too_high)
    elif current_spread != None and current_spread < MAX_SPREAD:
        if data.symbol in spread_too_high:
            spread_too_high.remove(data.symbol)
            print(f'\033[32m{data.symbol}: Removing from blacklist. Spread dropped below threshold\033[0m')
            # print(f'\033[33mThese symbols still have a spread higher than {MAX_SPREAD}%\033[0m')
            # print(spread_too_high)
async def monitor_stocks():
    if market_status_open == False:
        print(f'\033[33mMarket is closed, waiting for market to open in {time_to_market_open} seconds\033[0m')
        await asyncio.sleep(time_to_market_open)


    wss_client = CustomStockDataStream(LIVE_API_KEY, LIVE_SECRET_KEY, data_feed='sip')

    wss_client.subscribe_quotes(quote_data_handler, regressor_symbol)
    # print(f'Subscribing to {symbols}')
    for symbol in symbols_to_trade:
        wss_client.subscribe_quotes(quote_data_handler, symbol)

    while True:
        try:
            await wss_client.run()
        except (AttributeError, ConnectionClosedError) as e:
            if "resume_reading" in str(e) or isinstance(e, ConnectionClosedError):
                print("\033[31mEncountered websocket error, retrying connection in 5 seconds...\033[0m")
                await asyncio.sleep(5)
            else:
                raise
        except asyncio.exceptions.TimeoutError:
            print("\033[31mConnection timed out. Retrying in 10 seconds...\033[0m")
            await asyncio.sleep(10)
        except Exception as e:
            print(f"\033[31mUnexpected error. Retrying in 60 seconds: {e}\033[0m")
            await asyncio.sleep(60)