High-Frequency Trading and Latency Arbitrage › Market Impact and Flash Crashes
Quantitative FinanceAll chapters High-Frequency Trading and Latency Arbitrage › Market Impact and Flash CrashesOne section · Sections in this chapter15 / 18 ↓
Reading tools Definitions & inline links
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
HFT has fundamentally changed market dynamics, with both positive and negative effects.
Positive Contributions
Positive contributions include:
-
Improved liquidity: HFT market makers provide continuous quotes across many securities and market conditions, reducing bid-ask spreads for all market participants. Since HFT became prevalent, spreads have declined dramatically. Academic research estimates that spreads for liquid securities have narrowed by 50% or more, with particularly active securities seeing even greater improvements.
-
Price efficiency: By quickly arbitraging price discrepancies, HFT helps ensure that the same security trades at similar prices across venues, improving market efficiency.
-
Lower transaction costs: Tighter spreads and improved price discovery have reduced trading costs for all market participants.
Negative Effects and Risks
Negative effects and risks include:
-
Flash crashes: On May 6, 2010, the Dow Jones Industrial Average dropped nearly 1,000 points (about 9%) in minutes before recovering. The event was triggered by a large sell order that destabilized HFT market makers, who withdrew liquidity en masse.
-
Liquidity illusion: HFT liquidity can disappear instantly during stress, leaving markets illiquid precisely when liquidity is most needed. The 2010 Flash Crash and subsequent events demonstrated this vulnerability.
-
Arms race costs: The resources devoted to achieving marginal speed improvements (microwave towers, submarine cables, FPGA development) represent significant social costs with debatable benefits.
def simulate_flash_crash(
n_periods: int = 200,
n_market_makers: int = 10,
initial_price: float = 100.0,
normal_volatility: float = 0.05,
shock_magnitude: float = 2.0, # Standard deviations
mm_withdrawal_threshold: float = 1.5, # Volatility multiple to withdraw
mm_risk_limit: float = 500, # Inventory limit before withdrawal
):
"""
Simulate a flash crash scenario with HFT market maker withdrawal.
Parameters:
-----------
n_periods : Number of time periods
n_market_makers : Number of HFT market makers
initial_price : Starting price
normal_volatility : Normal volatility per period (%)
shock_magnitude : Size of initial shock in std devs
mm_withdrawal_threshold : Volatility level triggering withdrawal
mm_risk_limit : Inventory level triggering withdrawal
"""
np.random.seed(42)
prices = [initial_price]
active_mms = [n_market_makers]
total_liquidity = [
n_market_makers * 1000
] # Shares available at best bid/ask
# Market maker states
mm_inventories = np.zeros(n_market_makers)
mm_active = np.ones(n_market_makers, dtype=bool)
recent_vol = normal_volatility
for t in range(1, n_periods):
# Large sell order at t=50
if t == 50:
shock = -shock_magnitude * normal_volatility
else:
shock = 0
# Normal random price movement
random_move = np.random.normal(0, normal_volatility)
# Price impact depends on available liquidity
liquidity_factor = total_liquidity[-1] / (n_market_makers * 1000)
price_impact = 1 / max(
liquidity_factor, 0.1
) # Higher impact with less liquidity
price_change = (random_move + shock) * price_impact
new_price = prices[-1] * (1 + price_change / 100)
# Update recent volatility
if t > 5:
recent_returns = np.diff(np.log(prices[-5:])) * 100
recent_vol = np.std(recent_returns)
# Market makers update their state
for i in range(n_market_makers):
if mm_active[i]:
# Accumulate inventory from the shock
if t == 50:
mm_inventories[i] += np.random.exponential(200)
# Check withdrawal conditions
vol_exceeded = (
recent_vol > mm_withdrawal_threshold * normal_volatility
)
inventory_exceeded = abs(mm_inventories[i]) > mm_risk_limit
if vol_exceeded or inventory_exceeded:
mm_active[i] = False
# Update liquidity
current_liquidity = mm_active.sum() * 1000
# If liquidity gets very low, allow some recovery
if t > 100 and recent_vol < normal_volatility * 1.2:
for i in range(n_market_makers):
if not mm_active[i] and np.random.random() < 0.1:
mm_active[i] = True
mm_inventories[i] = 0
prices.append(new_price)
active_mms.append(mm_active.sum())
total_liquidity.append(current_liquidity)
return {
"prices": np.array(prices),
"active_mms": np.array(active_mms),
"total_liquidity": np.array(total_liquidity),
"min_price": min(prices),
"max_drawdown": (initial_price - min(prices)) / initial_price * 100,
}
crash = simulate_flash_crash()Flash Crash Simulation ======================================== Initial price: $100.00 Minimum price: $97.49 Maximum drawdown: 2.5% Minimum active market makers: 0
The simulation triggers a severe liquidity event at period 50: all ten market makers withdraw and displayed liquidity falls from 10,000 shares to zero. The price response is much smaller than the accompanying liquidity collapse, with a minimum near $97.5 rather than a double-digit percentage crash. Apart from a brief return by two market makers around period 147, the simulated market does not recover its liquidity before the horizon ends. The result therefore illustrates synchronized withdrawal more clearly than it reproduces a full flash-crash price path.



The simulation illustrates the feedback loop that creates flash crashes: an initial shock causes price volatility, which triggers market maker risk limits, leading to liquidity withdrawal, which amplifies price movements further. The recovery occurs only when volatility subsides and market makers gradually return.