Markets: A Rigorous Course

Exercise · m05_roll.py

The Roll (1984) spread estimator

Model. The efficient price m_t is a random walk, m_t = m_{t-1} + sigma e_t. Each trade is a buy (q_t = +1) or a sell (q_t = -1), iid, independent of e. Buyers pay the ask, sellers receive the bid, so the trade price is

p_t = m_t + (s / 2) q_t.

Proposition 5.1: Cov(dp_t, dp_{t-1}) = -s^2 / 4 when P(q = +1) = 1/2, so s = 2 sqrt(-Cov). You will simulate the model, estimate s, and check the estimate against the truth you put in.

Check: cd exercises && .venv/bin/python -m pytest modules/M05-intraday-execution/tests

Tests (7)

  • test_simulation_shapes_and_bounce
  • test_autocov_matches_theory
  • test_autocov_definition
  • test_roll_recovers_spread
  • test_unbalanced_order_flow_biases_roll_down
  • test_positive_autocov_gives_nan
  • test_bounce_on_synthetic_daily_mid

Theory: M05 notes. Run with Ctrl/⌘+Enter.

not run yet

Not saved: sign up to keep your code
"""Exercise 5.1: the Roll (1984) spread estimator (notes.md §5.2, Proposition 5.1).

Model. The efficient price m_t is a random walk, m_t = m_{t-1} + sigma e_t.
Each trade is a buy (q_t = +1) or a sell (q_t = -1), iid, independent of e.
Buyers pay the ask, sellers receive the bid, so the trade price is

    p_t = m_t + (s / 2) q_t.

Proposition 5.1: Cov(dp_t, dp_{t-1}) = -s^2 / 4 when P(q = +1) = 1/2, so
s = 2 sqrt(-Cov). You will simulate the model, estimate s, and check the
estimate against the truth you put in.

Check:
    cd exercises && .venv/bin/python -m pytest modules/M05-intraday-execution/tests
"""

from __future__ import annotations

import numpy as np  # noqa: F401


def simulate_roll_prices(n: int, spread: float, sigma: float, p0: float = 100.0, seed: int = 0, p_buy: float = 0.5):
    """Simulate n trade prices from the Roll model.

    m_t = p0 + sum_{i<=t} sigma * e_i with e_i iid N(0, 1)
    q_t = +1 with probability p_buy, else -1 (iid, independent of e)
    p_t = m_t + (spread / 2) * q_t

    Use np.random.default_rng(seed). Returns (p, m, q), numpy arrays of length n.
    """
    raise NotImplementedError


def first_order_autocov(x: np.ndarray) -> float:
    """First-order sample autocovariance of the first differences of x.

    dx_t = x_t - x_{t-1} (K = len(x) - 1 values), dbar = mean(dx),
    gamma1 = (1 / (K - 1)) * sum_{t=2}^{K} (dx_t - dbar)(dx_{t-1} - dbar).
    """
    raise NotImplementedError


def roll_spread(prices: np.ndarray) -> float:
    """Roll estimator s_hat = 2 sqrt(-gamma1).

    If gamma1 >= 0 the estimator is undefined; return float('nan').
    """
    raise NotImplementedError


def roll_spread_theory(spread: float, p_buy: float = 0.5) -> float:
    """Population Cov(dp_t, dp_{t-1}) when P(q = +1) = p_buy.

    Derive it (problem 5.2): it equals -(s^2 / 4) * Var(q) with Var(q) = 4 p (1 - p).
    """
    raise NotImplementedError

No runs yet

The stub's functions raise NotImplementedError until you write them. Fill in one, press Run tests, and watch its tests turn green.

Reference solution

Available after your first run, with an account approved for code runs.

Create a free account

Reading is open to everyone. An account keeps your progress, answers, notes and code drafts, and gives you the study plan, the journals and the simulator. Running code needs an account approved for it.