Paper trade buy filling 10%+ higher than the current stock price?

I’m testing a couple algos on some paper accounts and they used to hover somewhere a little bit in the green most days. Recently, they’ve just been tanking. Like I’d lose 10% - 20% every day. The weird thing is, I didn’t change my algos much at all.

I got to looking at it a little bit and there seems to be a pattern where, when my algorithm ‘buys’ a stock (not crypto) and then the Alpaca dashboard will show my ‘average entry price’ as being significantly higher than what the stock is actually priced at. For example, it had identified a stock around $12.58 (according to yfinance (I may switch to the alpaca price API, but that’s TBD)). It placed the (paper) order, and it was ‘filled’ at over $14! That subsequently immediately triggers my stop loss algo which then ‘fills’ at the current price of $12.58 (or very close). A little bit later, I’ll look at the stock data for the particular minute that my paper order was placed, and the ‘high’ isn’t anywhere close to the $14+ that my order was filled at. What’s up with that? I can handle a little bit of discrepancy here and there as that’s simply part of the game, but a 10%+ deviation is not what I was expecting. Thoughts?

Also thoughts on yfinance vs Alpaca for getting ‘real-time’ pricing info? I realize stock quotes are, by nature, never completely up to date, but I would prefer the fasted updates while maintaining accurate information. Also, my time units are ‘seconds’, so I’m not talking milliseconds, but even a couple seconds is important for me. Worth mentioning I’m still using the free tier although I’m not inherently opposed to paying for the upgraded tier if it’ll give me significantly better real-time data.

@kcducttaper
You asked about Alpaca order fills in the paper environment. The fills one gets using the paper environment simulate very closely (if not exactly) the fills one would expect in live trading. Orders fill at the current quote. Buy orders fill at the ‘ask’ (ie the lowest price anyone is willing to sell at) and sell orders fill at the ‘bid’ (ie the highest price anyone is willing to buy at). This is also the execution one will generally get in live trading. One difference however is paper trading does not take liquidity into account. It will fill an order for 1 share exactly the same as an order for 1,000,000 shares. However, if one is trading ‘reasonable’ quantities with fairly liquid symbols this generally isn’t an issue, but something to be aware of.

Note that the order fill price, in paper and live trading, has no direct relationship to the “current price” one sees posted (including on Alpaca). Why? That price is simply the price of the last trade. It happened in the past and moreover, one doesn’t know anything about the trade/order. Many brokers (including alpaca) support ‘advanced order types’ such as “fill at VWAP” or “fill at quote midpoint”. These orders however will generally not be $0 commission (so not comparable).

Below is some code (using the alpaca-py SDK) to easily fetch the expected fill price of an order. One can check the actual fill price this way and verify it’s as expected.

!pip install -q alpaca-py
import pandas as pd

from alpaca.data import StockHistoricalDataClient
from alpaca.data.requests import StockQuotesRequest

ALPACA_API_KEY = 'xxxxx'
ALPACA_API_SECRET = 'xxxxx'

data_client = StockHistoricalDataClient(ALPACA_API_KEY, ALPACA_API_SECRET)

# enter the order info
order_symbol = 'EEM'
order_side = 'buy' # buy or sell
order_fill_time = pd.to_datetime("2025-10-01 19:52:12.349Z")
actual_fill_price = 53.85

# get quotes 10 seconds before the order fill time
# convert to a dataframe	
quotes = data_client.get_stock_quotes(StockQuotesRequest(
                                symbol_or_symbols=order_symbol,
                                start=order_fill_time - pd.Timedelta(seconds=10),
                                end=order_fill_time,
                                feed='sip'
                                )).df.reset_index('symbol')

# get the current quote as-of the fill time
current_quote = quotes.asof(order_fill_time)

# get the expected fill price. ask if a buy / bid if a sell
expected_fill_price = current_quote.ask_price if order_side=="buy" else current_quote.bid_price

# print expected and actual
print(f'expected_fill_price {expected_fill_price}')
print(f'actual_fill_price {actual_fill_price}')

There is no mystery how paper orders fill. Buy orders fill at the current ask. Sell orders fill at the current bid. Hope that helps.

1 Like

Takes a minute to wrap my head around, but I think that makes sense! Seems a little strange to me that the nearest ask would be so much higher than the last price, but I guess that can happen in low volume stocks. Appreciate the very thorough explanation and the code snippet! I may have to include some of that in my algo to ‘test’ the actual fill price.

Thoughts on yfinance vs alpaca vs paid alpaca for ‘real time’ symbol prices?