You are currently viewing Options Liquidity and Market Execution

Options Liquidity and Market Execution

Description

Before any option trade, I spend a lot of time analyzing the market, identifying a perfect setup, calculating my edge, and then I enter the trade. My thesis plays out exactly as expected. The stock moves in my favor, and I feel right…But I lose money. How? Liquidity.

That 15-cent bid-ask spread I ignored ate your entire profit. The option I bought at the ask is now worth mid-market price, but I can only sell at the bid. I paid $2.20, it’s “worth” $2.50, but I can only get $2.10. I was right about direction and volatility, but execution costs destroyed my edge.

This is the silent killer of retail options traders. In this post, I’ll explore what makes an option liquid, why it matters more than most traders realize, and how to screen for tradable options using Python systematically. I’ll use SPY as an example and show you the exact criteria professional traders use before entering any position.

This isn’t about option pricing models or Greeks — it’s about the market microstructure that determines whether your theoretical edge becomes actual profit.

What Is Liquidity?

In one of the previous posts, I talked about the fingerprint of the option chain, the volume, and the open interest (Read it here: Vol and OI), and now we are going to use this information in a concrete trading setup.

Let me start again with the usual physics analogy, my first love. In physics, we distinguish between ideal systems (frictionless planes, perfect vacuums) and real systems (with friction, air resistance, energy loss). Options markets are the same. In the ideal case, you can buy or sell immediately at a fair market price with zero transaction costs. Then you have the reality, which seems complicate your plans. Every trade incurs costs through:

  • Bid-ask spread (immediate loss when entering)
  • Slippage (price moves against you and your entry price while filling the order)
  • Market impact (your order moves the price)

The liquidity determines how close you get to the ideal case, and you cannot ignore it!

When traders talk about liquidity, they’re usually referring to three closely connected elements. Volume tells you how actively an option is trading today and how efficiently prices incorporate new information. Open interest reveals whether meaningful positions already exist, signaling sustained market participation and reliable quoting. The bid-ask spread is where liquidity becomes tangible: tight spreads keep transaction costs low, while wide spreads quietly erode any edge. Crucially, these pillars must work together — high volume with a wide spread is still costly, and high open interest without trading activity often leads to stale, misleading prices.

Why Liquidity Matters More Than You Think

I would like you to consider the following 2 scenarios:

Scenario 1: Liquid option. Last price: $2.00, Bid: $1.95, Ask: $2.05. In this case, we have a $0.10 spread, a 5% of the last traded price. You buy at $2.05, the stock moves favorably, and the option is now worth $2.50 mid-market. You can sell at a bid of $2.45, making you a profit of $0.40. Let’s think of the spread as a transaction cost you have to pay for entering into a trade. That means your cost is $0.10.

Scenario 2: Illiquid option. Last price: $2.00, Bid: $1.70, Ask: $2.30. In this case, we have a $0.60 spread, a 3% of the last traded price. You buy at $2.30, the stock moves favorably by the same amount as in the previous case, and the option is now worth $2.50 mid-market. You can sell at a bid of $2.20, making you a loss of -$0.10.

Same market move. Same analysis. Opposite outcome. The transaction cost makes you an even more theoretical larger loss, reflecting a 26% price move to just break even on transaction costs. Your edge evaporated before the trade even started.

Professional Liquidity Criteria

Professional traders tend to be very disciplined when it comes to liquidity, relying on clear, quantitative thresholds rather than intuition.

Daily volume is typically expected to exceed 100–200 contracts, ensuring that the option is actively traded, prices reflect current information, and orders can be filled efficiently at posted quotes.

Open interest usually needs to be above 500–1,000 contracts, signaling an established market with multiple participants, consistent market-maker activity, and real depth on both sides of the book.

Finally, the bid-ask spread is kept below roughly 5–10% of the option price, so transaction costs remain controlled and do not overwhelm the expected edge. Importantly, these criteria are not interchangeable.

All three must be satisfied, because failing even one often turns an otherwise “good” trade into a costly one.

Python: Screening for Liquid Options

Let’s implement a systematic liquidity screen on SPY options. The first part of the code about retrieving the option chain from Yahoo Finance and how to filter around the strikes within ±10% of the current price can be found in the article Vol and OI.

Let’s apply the liquidity criteria to the chain.

# Define liquidity criteria
min_volume = 100
min_oi = 500
max_spread_pct = 0.10  # 10% max bid-ask spread

# Calculate bid-ask spread percentage
calls_filtered['spread'] = calls_filtered['ask'] - calls_filtered['bid']
calls_filtered['spread_pct'] = calls_filtered['spread'] / calls_filtered['lastPrice']

puts_filtered['spread'] = puts_filtered['ask'] - puts_filtered['bid']
puts_filtered['spread_pct'] = puts_filtered['spread'] / puts_filtered['lastPrice']

# Apply liquidity filters
liquid_calls = calls_filtered[
    (calls_filtered['volume'] >= min_volume) & 
    (calls_filtered['openInterest'] >= min_oi) & 
    (calls_filtered['spread_pct'] <= max_spread_pct)
]

liquid_puts = puts_filtered[
    (puts_filtered['volume'] >= min_volume) & 
    (puts_filtered['openInterest'] >= min_oi) & 
    (puts_filtered['spread_pct'] <= max_spread_pct)
]

print(f"LIQUIDITY SCREENING RESULTS")
print(f"Criteria:")
print(f"  Min Volume: {min_volume:,} contracts")
print(f"  Min Open Interest: {min_oi:,} contracts")
print(f"  Max Bid-Ask Spread: {max_spread_pct*100:.0f}% of price")

print(f"\nResults:")
print(f"  Liquid Calls: {len(liquid_calls)}/{len(calls_filtered)} ({len(liquid_calls)/len(calls_filtered)*100:.1f}%)")
print(f"  Liquid Puts: {len(liquid_puts)}/{len(puts_filtered)} ({len(liquid_puts)/len(puts_filtered)*100:.1f}%)")

# Calculate how many failed each criterion
calls_vol_fail = len(calls_filtered[calls_filtered['volume'] < min_volume])
calls_oi_fail = len(calls_filtered[calls_filtered['openInterest'] < min_oi])
calls_spread_fail = len(calls_filtered[calls_filtered['spread_pct'] > max_spread_pct])

print(f"\nCalls that FAILED criteria:")
print(f"  Volume too low: {calls_vol_fail}")
print(f"  OI too low: {calls_oi_fail}")
print(f"  Spread too wide: {calls_spread_fail}")

From this initial screener, one can visualize and analyze the 5 most liquid calls and puts and make the appropriate trade decision.

Real-World Example: Iron Condor Liquidity Check

Let’s say you want to sell an iron condor on SPY. Before entering, verify that all four legs pass liquidity criteria.

# Example: Proposed Iron Condor
# Sell 590/595 put spread, Sell 605/610 call spread

ic_strikes = [590, 595, 605, 610]

print(f"IRON CONDOR LIQUIDITY CHECK")
print(f"Proposed Structure: {ic_strikes[0]}/{ic_strikes[1]}/{ic_strikes[2]}/{ic_strikes[3]}")

all_liquid = True

for strike in ic_strikes:
    # Check calls
    if strike in calls_filtered['strike'].values:
        call_data = calls_filtered[calls_filtered['strike'] == strike].iloc[0]
        
        vol_pass = call_data['volume'] >= min_volume
        oi_pass = call_data['openInterest'] >= min_oi
        spread_pass = call_data['spread_pct'] <= max_spread_pct
        
        is_liquid = vol_pass and oi_pass and spread_pass
        
        print(f"\n${strike:.2f} Call:")
        print(f"  Volume: {call_data['volume']:,.0f} {'✓' if vol_pass else '✗'}")
        print(f"  OI: {call_data['openInterest']:,.0f} {'✓' if oi_pass else '✗'}")
        print(f"  Spread: {call_data['spread_pct']*100:.1f}% {'✓' if spread_pass else '✗'}")
        print(f"  → {'LIQUID' if is_liquid else 'ILLIQUID'}")
        
        if not is_liquid:
            all_liquid = False
    
    # Check puts
    if strike in puts_filtered['strike'].values:
        put_data = puts_filtered[puts_filtered['strike'] == strike].iloc[0]
        
        vol_pass = put_data['volume'] >= min_volume
        oi_pass = put_data['openInterest'] >= min_oi
        spread_pass = put_data['spread_pct'] <= max_spread_pct
        
        is_liquid = vol_pass and oi_pass and spread_pass
        
        print(f"\n${strike:.2f} Put:")
        print(f"  Volume: {put_data['volume']:,.0f} {'✓' if vol_pass else '✗'}")
        print(f"  OI: {put_data['openInterest']:,.0f} {'✓' if oi_pass else '✗'}")
        print(f"  Spread: {put_data['spread_pct']*100:.1f}% {'✓' if spread_pass else '✗'}")
        print(f"  → {'LIQUID' if is_liquid else 'ILLIQUID'}")
        
        if not is_liquid:
            all_liquid = False

if all_liquid:
    print(f"✓ ALL LEGS PASS - Iron Condor is tradable")
else:
    print(f"✗ SOME LEGS FAIL - Avoid this structure or widen strikes")
print(f"{'='*60}")

The liquidity thresholds above are well-suited for products like SPY and other heavily traded underlyings, but they’re not universal.
When dealing with mid-cap stocks or less active ETFs, it can be reasonable to relax the requirements slightly — for example, daily volume above 50 contracts, open interest above 250, and bid-ask spreads below roughly 15%.
For small-cap stocks, however, options markets are often so thin that transaction costs alone can overwhelm any expected return, making avoidance the more prudent choice.
Index options such as SPX or NDX follow a different logic: despite lower contract counts, their larger notional size and active institutional participation justify thresholds like volume above 50, open interest above 1,000, and spreads under 5%. In all cases, it’s essential to compute the full round-trip cost as a percentage of the option premium — if that friction exceeds 20%, only a powerful edge can justify the trade.

Final Thoughts

Liquidity is the foundation of profitable options trading. Before you analyze volatility, calculate Greeks, or build complex spreads, ask a simpler question: Can I actually trade this option at a fair price?

Most retail traders ignore liquidity until it’s too late. They focus on direction, timing, and strategy construction while overlooking the 15-30% haircut from wide spreads. Then they wonder why their win rate is 60%, but they still lose money.

Professional traders screen for liquidity first because execution quality determines whether the theoretical edge becomes realized profit. A perfect setup on an illiquid option is worse than a mediocre setup on a liquid one.

In the next post, we’ll explore Max Pain and Pin Risk — how concentrated open interest at specific strikes creates gravitational forces that pull prices toward those levels as expiration approaches. Liquidity screening tells you what you can trade. Max pain analysis tells you where prices are likely to drift.

This is the mindset behind The Quantitative Edge — systematic filters that eliminate bad trades before you ever enter them.

Statemi bene!