|
| 1 | +--- |
| 2 | +title: Bid-ask spread |
| 3 | +sidebar_label: Bid-ask spread |
| 4 | +description: Calculate bid-ask spread metrics for transaction cost analysis and liquidity measurement |
| 5 | +--- |
| 6 | + |
| 7 | +The bid-ask spread is the difference between the best ask (lowest sell price) and best bid (highest buy price). It represents the cost of immediately executing a round-trip trade and is a key measure of market liquidity. |
| 8 | + |
| 9 | +## Problem |
| 10 | + |
| 11 | +You want to measure market liquidity and transaction costs. Narrow spreads indicate liquid markets with low trading costs, while wide spreads suggest illiquidity or market stress. |
| 12 | + |
| 13 | +## Solution |
| 14 | + |
| 15 | +```questdb-sql demo title="Calculate bid-ask spread metrics" |
| 16 | +DECLARE |
| 17 | + @symbol := 'EURUSD', |
| 18 | + @lookback := '$now - 1h..$now' |
| 19 | +
|
| 20 | +SELECT |
| 21 | + timestamp, |
| 22 | + symbol, |
| 23 | + round(bid_price, 5) AS bid, |
| 24 | + round(ask_price, 5) AS ask, |
| 25 | + round(ask_price - bid_price, 6) AS spread_absolute, |
| 26 | + round((ask_price - bid_price) / ((bid_price + ask_price) / 2) * 10000, 2) AS spread_bps, |
| 27 | + round((bid_price + ask_price) / 2, 5) AS mid_price |
| 28 | +FROM core_price |
| 29 | +WHERE symbol = @symbol |
| 30 | + AND timestamp IN @lookback |
| 31 | +ORDER BY timestamp; |
| 32 | +``` |
| 33 | + |
| 34 | +The query calculates: |
| 35 | +- **Absolute spread**: ask - bid |
| 36 | +- **Spread in basis points**: spread / mid_price × 10,000 |
| 37 | +- **Mid price**: (bid + ask) / 2 |
| 38 | + |
| 39 | +## Aggregated spread analysis |
| 40 | + |
| 41 | +```questdb-sql demo title="Average spread by time period" |
| 42 | +DECLARE |
| 43 | + @symbol := 'EURUSD', |
| 44 | + @lookback := '$now - 1d..$now' |
| 45 | +
|
| 46 | +SELECT |
| 47 | + timestamp, |
| 48 | + symbol, |
| 49 | + round(avg((ask_price - bid_price) / ((bid_price + ask_price) / 2) * 10000), 2) AS avg_spread_bps, |
| 50 | + round(min((ask_price - bid_price) / ((bid_price + ask_price) / 2) * 10000), 2) AS min_spread_bps, |
| 51 | + round(max((ask_price - bid_price) / ((bid_price + ask_price) / 2) * 10000), 2) AS max_spread_bps, |
| 52 | + count() AS quote_count |
| 53 | +FROM core_price |
| 54 | +WHERE symbol = @symbol |
| 55 | + AND timestamp IN @lookback |
| 56 | +SAMPLE BY 1h |
| 57 | +ORDER BY timestamp; |
| 58 | +``` |
| 59 | + |
| 60 | +## Interpreting results |
| 61 | + |
| 62 | +- **Tight spread**: Highly liquid market conditions and lower immediate transaction costs |
| 63 | +- **Wide spread**: Illiquid or volatile period, higher transaction costs |
| 64 | +- **Spread widening**: Often precedes or accompanies volatility |
| 65 | +- **Intraday patterns**: Spreads typically widen during off-hours and narrow during active sessions |
| 66 | + |
| 67 | +:::note Spread conventions (venue-dependent) |
| 68 | +Typical spreads vary a lot by venue, instrument, and session: |
| 69 | + |
| 70 | +- FX majors: often around sub-pip to a few pips in liquid hours (roughly sub-1 to several bps) |
| 71 | +- FX minors/emerging pairs: typically wider than majors |
| 72 | +- Crypto: can range from tight to very wide depending on pair and exchange |
| 73 | +- Equities: often quoted in ticks/cents rather than bps |
| 74 | + |
| 75 | +Treat these as rough guidelines, not fixed thresholds. |
| 76 | +::: |
| 77 | + |
| 78 | +:::info Related documentation |
| 79 | +- [SAMPLE BY](/docs/query/sql/sample-by/) |
| 80 | +- [Aggregation functions](/docs/query/functions/aggregation/) |
| 81 | +::: |
0 commit comments