Bid Ask Spread Tracker
Track bid-ask spreads in real time across multiple exchanges and trading pairs, analyzing quoted and effective spread patterns by time of day, volatility regime, and scheduled market events for optimal execution cost minimization and venue selection.
Bid-Ask Spread Tracker: A Comprehensive Guide
Definition and Importance of Bid-Ask Spread
The bid-ask spread is the difference between the highest price a buyer is willing to pay for an asset (the 'bid' price) and the lowest price a seller is willing to accept (the 'ask' or 'offer' price). It represents the cost of executing a transaction immediately and is a key measure of market liquidity.
Importance:
- Cost of Trading: The spread is a direct cost to traders. Buyers pay the ask price, sellers receive the bid price, and the difference goes to the market maker or exchange as profit.
- Market Liquidity: A narrow spread typically indicates a highly liquid market with many buyers and sellers, meaning orders can be filled quickly without significantly impacting the price. A wide spread suggests lower liquidity.
- Volatility Indicator: Spreads often widen during periods of high market volatility or uncertainty, as market makers demand greater compensation for the increased risk.
- Market Efficiency: Efficient markets tend to have narrower spreads as information is quickly disseminated and incorporated into prices.
- Profit for Market Makers: For market makers, the bid-ask spread is their primary source of profit. They aim to buy at the bid and sell at the ask, capturing the spread.
Types of Bid-Ask Spread
1. Nominal (Quoted) Spread
Definition: The nominal spread is the simplest form, representing the direct difference between the best ask and best bid prices available in the market at a given moment.
Formula:
Nominal Spread = Ask Price - Bid Price
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
# Creating data with variance in both price and spread
np.random.seed(42)
num_data_points = 50
timestamp = pd.to_datetime(pd.date_range(start='2023-01-01', periods=num_data_points, freq='1min'))
# Base linear trend plus random noise for variance
noise = np.random.normal(0, 0.2, num_data_points)
base_trend = np.linspace(100.00, 105.00, num_data_points) + noise
bid_prices = np.round(base_trend, 2)
# Introduce variance into the spread itself (centered around 0.9)
spread_variance = np.random.normal(0.9, 0.05, num_data_points)
ask_prices = np.round(bid_prices + spread_variance, 2)
data = pd.DataFrame({
'timestamp': timestamp,
'bid': bid_prices,
'ask': ask_prices
})
def calculate_nominal_spread(bid_price: float, ask_price: float) -> float:
return ask_price - bid_price
# Apply calculations
data['nominal_spread'] = data.apply(lambda row: calculate_nominal_spread(row['bid'], row['ask']), axis=1)
print("--- Data Generated with Price & Spread Variance ---")
display(data.head())--- Data Generated with Price & Spread Variance ---
| timestamp | bid | ask | nominal_spread | |
|---|---|---|---|---|
| 0 | 2023-01-01 00:00:00 | 100.10 | 101.02 | 0.92 |
| 1 | 2023-01-01 00:01:00 | 100.07 | 100.95 | 0.88 |
| 2 | 2023-01-01 00:02:00 | 100.33 | 101.20 | 0.87 |
| 3 | 2023-01-01 00:03:00 | 100.61 | 101.54 | 0.93 |
| 4 | 2023-01-01 00:04:00 | 100.36 | 101.31 | 0.95 |
2. Percentage Spread
Definition: The percentage spread normalizes the nominal spread by expressing it as a percentage of the midpoint price (average of bid and ask). This allows for easier comparison of liquidity across assets with different price levels.
Formula:
Midpoint Price = (Bid Price + Ask Price) / 2
Percentage Spread = (Nominal Spread / Midpoint Price) * 100
def calculate_percentage_spread(bid_price: float, ask_price: float) -> float:
midpoint_price = (bid_price + ask_price) / 2
nominal_spread = ask_price - bid_price
if midpoint_price == 0:
return 0.0
return (nominal_spread / midpoint_price) * 100
data['percentage_spread'] = data.apply(lambda row: calculate_percentage_spread(row['bid'], row['ask']), axis=1)
display(data.head())| timestamp | bid | ask | nominal_spread | percentage_spread | |
|---|---|---|---|---|---|
| 0 | 2023-01-01 00:00:00 | 100.10 | 101.02 | 0.92 | 0.914877 |
| 1 | 2023-01-01 00:01:00 | 100.07 | 100.95 | 0.88 | 0.875535 |
| 2 | 2023-01-01 00:02:00 | 100.33 | 101.20 | 0.87 | 0.863395 |
| 3 | 2023-01-01 00:03:00 | 100.61 | 101.54 | 0.93 | 0.920109 |
| 4 | 2023-01-01 00:04:00 | 100.36 | 101.31 | 0.95 | 0.942133 |
3. Effective Spread
Definition: The effective spread measures the actual cost of trading by comparing the trade execution price to the midpoint of the bid and ask prices at the time the order was placed. It accounts for orders that might be filled inside the quoted spread (price improvement) or outside due to market impact.
Formula:
- For a buy order:
Effective Spread = 2 * (Trade Price - Midpoint Price at Order Entry) - For a sell order:
Effective Spread = 2 * (Midpoint Price at Order Entry - Trade Price)
np.random.seed(43)
trade_prices = []
trade_types = []
for i in range(len(data)):
mid_price = (data.loc[i, 'bid'] + data.loc[i, 'ask']) / 2
if np.random.rand() > 0.5:
trade_type = 'buy'
trade_price = np.round(np.random.uniform(mid_price, data.loc[i, 'ask']), 2)
else:
trade_type = 'sell'
trade_price = np.round(np.random.uniform(data.loc[i, 'bid'], mid_price), 2)
trade_prices.append(trade_price)
trade_types.append(trade_type)
data['trade_price'] = trade_prices
data['trade_type'] = trade_types
def calculate_effective_spread(bid_price_at_order: float, ask_price_at_order: float, trade_price: float, trade_type: str) -> float:
midpoint_at_order = (bid_price_at_order + ask_price_at_order) / 2
if trade_type == 'buy':
return 2 * (trade_price - midpoint_at_order)
elif trade_type == 'sell':
return 2 * (midpoint_at_order - trade_price)
return 0.0
data['effective_spread'] = data.apply(lambda row: calculate_effective_spread(row['bid'], row['ask'], row['trade_price'], row['trade_type']), axis=1)
display(data.head())| timestamp | bid | ask | nominal_spread | percentage_spread | trade_price | trade_type | effective_spread | |
|---|---|---|---|---|---|---|---|---|
| 0 | 2023-01-01 00:00:00 | 100.10 | 101.02 | 0.92 | 0.914877 | 100.38 | sell | 0.36 |
| 1 | 2023-01-01 00:01:00 | 100.07 | 100.95 | 0.88 | 0.875535 | 100.18 | sell | 0.66 |
| 2 | 2023-01-01 00:02:00 | 100.33 | 101.20 | 0.87 | 0.863395 | 100.70 | sell | 0.13 |
| 3 | 2023-01-01 00:03:00 | 100.61 | 101.54 | 0.93 | 0.920109 | 101.33 | buy | 0.51 |
| 4 | 2023-01-01 00:04:00 | 100.36 | 101.31 | 0.95 | 0.942133 | 100.71 | sell | 0.25 |
np.random.seed(43)
# Mock trade data, assuming some price improvement or slippage
trade_prices = []
trade_types = []
for i in range(num_data_points):
mid_price = (data.loc[i, 'bid'] + data.loc[i, 'ask']) / 2
if np.random.rand() > 0.5: # Simulate a buy order
trade_type = 'buy'
# Trade price slightly above mid, sometimes slightly below ask
trade_price = np.round(np.random.uniform(mid_price, data.loc[i, 'ask']), 2)
else: # Simulate a sell order
trade_type = 'sell'
# Trade price slightly below mid, sometimes slightly above bid
trade_price = np.round(np.random.uniform(data.loc[i, 'bid'], mid_price), 2)
trade_prices.append(trade_price)
trade_types.append(trade_type)
data['trade_price'] = trade_prices
data['trade_type'] = trade_types
def calculate_effective_spread(bid_price_at_order: float, ask_price_at_order: float, trade_price: float, trade_type: str) -> float:
"""
Calculates the effective bid-ask spread.
Args:
bid_price_at_order (float): Bid price at the time the order was placed.
ask_price_at_order (float): Ask price at the time the order was placed.
trade_price (float): The actual execution price of the trade.
trade_type (str): Type of the trade, either 'buy' or 'sell'.
Returns:
float: The effective bid-ask spread.
"""
midpoint_at_order = (bid_price_at_order + ask_price_at_order) / 2
if trade_type == 'buy':
return 2 * (trade_price - midpoint_at_order)
elif trade_type == 'sell':
return 2 * (midpoint_at_order - trade_price)
else:
raise ValueError("Trade type must be 'buy' or 'sell'.")
# Apply the function to mock data
data['effective_spread'] = data.apply(lambda row:
calculate_effective_spread(row['bid'], row['ask'], row['trade_price'], row['trade_type']),
axis=1
)
print("--- Effective Spread Calculation ---")
display(data.head())
print("\nInterpretation: The effective spread provides a more accurate picture of transaction costs by comparing the actual trade price to the mid-price at the time of the order. A negative effective spread for a buy order would indicate a highly favorable execution (bought below mid-price).")--- Effective Spread Calculation ---
| timestamp | bid | ask | nominal_spread | percentage_spread | trade_price | trade_type | effective_spread | |
|---|---|---|---|---|---|---|---|---|
| 0 | 2023-01-01 00:00:00 | 101.49 | 101.69 | 0.20 | 0.196870 | 101.55 | sell | 0.08 |
| 1 | 2023-01-01 00:01:00 | 103.49 | 103.66 | 0.17 | 0.164132 | 103.51 | sell | 0.13 |
| 2 | 2023-01-01 00:02:00 | 105.49 | 105.68 | 0.19 | 0.179950 | 105.57 | sell | 0.03 |
| 3 | 2023-01-01 00:03:00 | 107.49 | 107.67 | 0.18 | 0.167317 | 107.63 | buy | 0.10 |
| 4 | 2023-01-01 00:04:00 | 109.48 | 109.62 | 0.14 | 0.127796 | 109.53 | sell | 0.04 |
Interpretation: The effective spread provides a more accurate picture of transaction costs by comparing the actual trade price to the mid-price at the time of the order. A negative effective spread for a buy order would indicate a highly favorable execution (bought below mid-price).
np.random.seed(44)
# Simulate post-trade midpoint price (e.g., 5 minutes after the trade)
# For simplicity, we'll shift the mid_price or add some noise
post_trade_mid_prices = []
for i in range(num_data_points):
current_mid = (data.loc[i, 'bid'] + data.loc[i, 'ask']) / 2
# Simulate a slight random walk for post-trade mid price
post_trade_mid = current_mid + np.random.uniform(-0.02, 0.02)
post_trade_mid_prices.append(np.round(post_trade_mid, 2))
data['post_trade_mid_price'] = post_trade_mid_prices
def calculate_realized_spread(trade_price: float, post_trade_mid_price: float, trade_type: str) -> float:
"""
Calculates the realized spread from the perspective of the trade initiator.
Args:
trade_price (float): The actual execution price of the trade.
post_trade_mid_price (float): The midpoint price a short period after the trade (e.g., T+5).
trade_type (str): Type of the trade, either 'buy' or 'sell'.
Returns:
float: The realized spread (from trader's perspective).
"""
if trade_type == 'buy':
return trade_price - post_trade_mid_price
elif trade_type == 'sell':
return post_trade_mid_price - trade_price
else:
raise ValueError("Trade type must be 'buy' or 'sell'.")
# Apply the function to mock data
data['realized_spread'] = data.apply(lambda row:
calculate_realized_spread(row['trade_price'], row['post_trade_mid_price'], row['trade_type']),
axis=1
)
print("--- Realized Spread Calculation ---")
display(data.head())
print("\nInterpretation: The realized spread quantifies the adverse selection component. A negative realized spread for a buy order indicates that the mid-price rose after your purchase, meaning you paid less relative to the future market price. A positive value means you paid more relative to the future market price.")--- Realized Spread Calculation ---
| timestamp | bid | ask | nominal_spread | percentage_spread | trade_price | trade_type | effective_spread | post_trade_mid_price | realized_spread | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 2023-01-01 00:00:00 | 101.49 | 101.69 | 0.20 | 0.196870 | 101.55 | sell | 0.08 | 101.60 | 0.05 |
| 1 | 2023-01-01 00:01:00 | 103.49 | 103.66 | 0.17 | 0.164132 | 103.51 | sell | 0.13 | 103.56 | 0.05 |
| 2 | 2023-01-01 00:02:00 | 105.49 | 105.68 | 0.19 | 0.179950 | 105.57 | sell | 0.03 | 105.59 | 0.02 |
| 3 | 2023-01-01 00:03:00 | 107.49 | 107.67 | 0.18 | 0.167317 | 107.63 | buy | 0.10 | 107.57 | 0.06 |
| 4 | 2023-01-01 00:04:00 | 109.48 | 109.62 | 0.14 | 0.127796 | 109.53 | sell | 0.04 | 109.54 | 0.01 |
Interpretation: The realized spread quantifies the adverse selection component. A negative realized spread for a buy order indicates that the mid-price rose after your purchase, meaning you paid less relative to the future market price. A positive value means you paid more relative to the future market price.
fig, axes = plt.subplots(1, 2, figsize=(16, 6))
# Plot 1: Distribution of Effective Spread
sns.histplot(data['effective_spread'], kde=True, ax=axes[0], color='blue', label='Effective Spread')
axes[0].axvline(data['effective_spread'].mean(), color='red', linestyle='--', label=f'Mean: {data["effective_spread"].mean():.4f}')
axes[0].set_title('Distribution of Effective Spread')
axes[0].set_xlabel('Effective Spread Value')
axes[0].set_ylabel('Frequency / Density')
axes[0].legend()
axes[0].grid(True, linestyle='--', alpha=0.7);
# Plot 2: Distribution of Realized Spread
sns.histplot(data['realized_spread'], kde=True, ax=axes[1], color='green', label='Realized Spread')
axes[1].axvline(data['realized_spread'].mean(), color='purple', linestyle='--', label=f'Mean: {data["realized_spread"].mean():.4f}')
axes[1].set_title('Distribution of Realized Spread')
axes[1].set_xlabel('Realized Spread Value')
axes[1].set_ylabel('Frequency / Density')
axes[1].legend()
axes[1].grid(True, linestyle='--', alpha=0.7);
plt.tight_layout()
plt.show()
print("Interpretation:")
print("The first histogram shows the distribution of the effective spread. A distribution centered near zero or slightly positive suggests efficient execution costs. A longer tail on the positive side indicates trades that incurred higher-than-expected costs relative to the mid-price at order entry. The mean effective spread helps quantify the average transaction cost incurred by traders.")
print("\nThe second histogram displays the distribution of the realized spread. This distribution's mean and shape reveal how often trades were subject to adverse selection (market moving against the trader post-trade) or benefited from favorable price movements. A mean near zero suggests market makers are generally able to capture the spread without significant losses to adverse selection, or that traders' profits/losses from post-trade price movements balance out. Negative values for a buyer's realized spread (meaning the post-trade mid-price was higher than their trade price) indicate a 'good' trade from the perspective of beating future market movement.")Interpretation: The first histogram shows the distribution of the effective spread. A distribution centered near zero or slightly positive suggests efficient execution costs. A longer tail on the positive side indicates trades that incurred higher-than-expected costs relative to the mid-price at order entry. The mean effective spread helps quantify the average transaction cost incurred by traders. The second histogram displays the distribution of the realized spread. This distribution's mean and shape reveal how often trades were subject to adverse selection (market moving against the trader post-trade) or benefited from favorable price movements. A mean near zero suggests market makers are generally able to capture the spread without significant losses to adverse selection, or that traders' profits/losses from post-trade price movements balance out. Negative values for a buyer's realized spread (meaning the post-trade mid-price was higher than their trade price) indicate a 'good' trade from the perspective of beating future market movement.
4. Realized Spread
Definition: The realized spread measures the profit or loss made by a market maker from a trade, considering how the midpoint price moves after the trade. It captures the impact of adverse selection (when a market maker trades with someone who has better information).
Formula:
- For a buy order executed by market maker (you sold):
Realized Spread = Midpoint Price T+5 - Trade Price - For a sell order executed by market maker (you bought):
Realized Spread = Trade Price - Midpoint Price T+5
(Note: T+5 typically refers to 5 minutes after the trade, but can be any short-term future point.)
For our purposes, we'll calculate it from the perspective of the trader who initiated the trade:
- For a buy order:
Realized Spread (Trader) = Trade Price - Midpoint Price T+5 - For a sell order:
Realized Spread (Trader) = Midpoint Price T+5 - Trade Price
A positive realized spread for the trader means the market moved favorably after their trade, effectively making their transaction cost lower than initially perceived. A negative value suggests the market moved against them, indicating a higher true cost.
np.random.seed(44)
post_trade_mid_prices = []
for i in range(len(data)):
current_mid = (data.loc[i, 'bid'] + data.loc[i, 'ask']) / 2
post_trade_mid = current_mid + np.random.uniform(-0.1, 0.1)
post_trade_mid_prices.append(np.round(post_trade_mid, 2))
data['post_trade_mid_price'] = post_trade_mid_prices
def calculate_realized_spread(trade_price: float, post_trade_mid_price: float, trade_type: str) -> float:
if trade_type == 'buy':
return trade_price - post_trade_mid_price
elif trade_type == 'sell':
return post_trade_mid_price - trade_price
return 0.0
data['realized_spread'] = data.apply(lambda row: calculate_realized_spread(row['trade_price'], row['post_trade_mid_price'], row['trade_type']), axis=1)
display(data.head())| timestamp | bid | ask | nominal_spread | percentage_spread | trade_price | trade_type | effective_spread | post_trade_mid_price | realized_spread | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 2023-01-01 00:00:00 | 100.10 | 101.02 | 0.92 | 0.914877 | 100.38 | sell | 0.36 | 100.63 | 0.25 |
| 1 | 2023-01-01 00:01:00 | 100.07 | 100.95 | 0.88 | 0.875535 | 100.18 | sell | 0.66 | 100.43 | 0.25 |
| 2 | 2023-01-01 00:02:00 | 100.33 | 101.20 | 0.87 | 0.863395 | 100.70 | sell | 0.13 | 100.81 | 0.11 |
| 3 | 2023-01-01 00:03:00 | 100.61 | 101.54 | 0.93 | 0.920109 | 101.33 | buy | 0.51 | 101.05 | 0.28 |
| 4 | 2023-01-01 00:04:00 | 100.36 | 101.31 | 0.95 | 0.942133 | 100.71 | sell | 0.25 | 100.81 | 0.10 |
Visualizations
1. Bid, Ask, Mid-Price, and Nominal Spread Over Time
This multi-panel plot illustrates the movement of bid, ask, and mid-prices, along with the nominal spread, providing an immediate visual understanding of price dynamics and liquidity changes.
data['mid_price'] = (data['bid'] + data['ask']) / 2
fig, axes = plt.subplots(3, 1, figsize=(15, 14), sharex=True)
# Panel 1
axes[0].plot(data['timestamp'], data['bid'], label='Bid Price', color='red', linewidth=1, marker='o', markersize=2)
axes[0].plot(data['timestamp'], data['ask'], label='Ask Price', color='green', linewidth=1, marker='x', markersize=2)
axes[0].plot(data['timestamp'], data['mid_price'], label='Mid Price', color='blue', linestyle='--')
axes[0].fill_between(data['timestamp'], data['bid'], data['ask'], color='yellow', alpha=0.2)
axes[0].set_title('Bid, Ask, and Mid Prices (Non-linear with variable spread)')
axes[0].set_ylabel('Price')
axes[0].legend(loc='upper left')
axes[0].grid(True, linestyle='--', alpha=0.5)
# Panel 2: Nominal Spread with Variance
axes[1].plot(data['timestamp'], data['nominal_spread'], label='Nominal Spread', color='purple', linewidth=1.5, marker='s', markersize=3)
axes[1].set_ylim(0.5, 1.3)
axes[1].set_title('Nominal Bid-Ask Spread (Variable around 0.9)')
axes[1].set_ylabel('Spread Value')
axes[1].legend()
axes[1].grid(True, linestyle='--', alpha=0.7)
# Panel 3
axes[2].plot(data['timestamp'], data['percentage_spread'], label='Percentage Spread (%)', color='brown')
axes[2].set_title('Percentage Bid-Ask Spread')
axes[2].set_xlabel('Time')
axes[2].set_ylabel('Spread (%)')
axes[2].legend()
axes[2].grid(True, linestyle='--', alpha=0.7)
plt.tight_layout()
plt.show()2. Distribution of Effective and Realized Spreads
This plot uses histograms and kernel density estimates (KDE) to show the distribution of effective and realized spreads. It helps in understanding the common transaction costs and the impact of post-trade price movements, including potential price improvement or adverse selection.
fig, axes = plt.subplots(1, 2, figsize=(16, 6))
# Plot 1: Distribution of Effective Spread
sns.histplot(data['effective_spread'], kde=True, ax=axes[0], color='blue', label='Effective Spread')
axes[0].axvline(data['effective_spread'].mean(), color='red', linestyle='--', label=f'Mean: {data["effective_spread"].mean():.4f}')
axes[0].set_title('Distribution of Effective Spread')
axes[0].set_xlabel('Effective Spread Value')
axes[0].set_ylabel('Frequency / Density')
axes[0].legend()
axes[0].grid(True, linestyle='--', alpha=0.7);
# Plot 2: Distribution of Realized Spread
sns.histplot(data['realized_spread'], kde=True, ax=axes[1], color='green', label='Realized Spread')
axes[1].axvline(data['realized_spread'].mean(), color='purple', linestyle='--', label=f'Mean: {data["realized_spread"].mean():.4f}')
axes[1].set_title('Distribution of Realized Spread')
axes[1].set_xlabel('Realized Spread Value')
axes[1].set_ylabel('Frequency / Density')
axes[1].legend()
axes[1].grid(True, linestyle='--', alpha=0.7);
plt.tight_layout()
plt.show()
print("Interpretation:")
print("The first histogram shows the distribution of the effective spread. A distribution centered near zero or slightly positive suggests efficient execution costs. A longer tail on the positive side indicates trades that incurred higher-than-expected costs relative to the mid-price at order entry. The mean effective spread helps quantify the average transaction cost incurred by traders.")
print("\nThe second histogram displays the distribution of the realized spread. This distribution's mean and shape reveal how often trades were subject to adverse selection (market moving against the trader post-trade) or benefited from favorable price movements. A mean near zero suggests market makers are generally able to capture the spread without significant losses to adverse selection, or that traders' profits/losses from post-trade price movements balance out. Negative values for a buyer's realized spread (meaning the post-trade mid-price was higher than their trade price) indicate a 'good' trade from the perspective of beating future market movement.")Interpretation: The first histogram shows the distribution of the effective spread. A distribution centered near zero or slightly positive suggests efficient execution costs. A longer tail on the positive side indicates trades that incurred higher-than-expected costs relative to the mid-price at order entry. The mean effective spread helps quantify the average transaction cost incurred by traders. The second histogram displays the distribution of the realized spread. This distribution's mean and shape reveal how often trades were subject to adverse selection (market moving against the trader post-trade) or benefited from favorable price movements. A mean near zero suggests market makers are generally able to capture the spread without significant losses to adverse selection, or that traders' profits/losses from post-trade price movements balance out. Negative values for a buyer's realized spread (meaning the post-trade mid-price was higher than their trade price) indicate a 'good' trade from the perspective of beating future market movement.
Conclusion
This notebook has provided a comprehensive overview of bid-ask spreads, their various types, and their significance in financial markets. We've explored the following key concepts:
-
Definition and Importance: The bid-ask spread is a fundamental measure of market liquidity and a direct cost of trading, impacting efficiency and profitability for both traders and market makers.
-
Types of Spreads:
- Nominal (Quoted) Spread: The basic difference between the highest bid and lowest ask, representing the instantaneous cost.
- Percentage Spread: Normalizes the nominal spread by expressing it as a percentage of the midpoint, allowing for cross-asset comparisons.
- Effective Spread: Measures the actual cost of a trade by comparing the execution price to the midpoint at the time of the order, accounting for price improvement or slippage.
- Realized Spread: Quantifies the profit/loss from a trade relative to a future midpoint price, reflecting the impact of adverse selection or favorable post-trade price movements for the trader.
-
Visualizations: We've used various plots to illustrate:
- The dynamic movement of bid, ask, and mid-prices over time.
- The behavior of nominal and percentage spreads, highlighting their variability.
- The distributions of effective and realized spreads, offering insights into transaction costs and the degree of adverse selection.
Understanding these different types of bid-ask spreads is crucial for traders to accurately assess transaction costs, evaluate execution quality, and make informed trading decisions. For market participants, these metrics provide valuable insights into market liquidity, efficiency, and the overall health of an asset's trading environment.