Exercise · m07_mtm.py
Daily mark-to-market, margin calls, and futures versus forward prices
not run yet
Not saved: sign up to keep your code"""M07 exercise 3: daily mark-to-market, margin calls, and futures versus forward prices.
Implement each function, then run from the repo root:
cd exercises && .venv/bin/python -m pytest modules/M07-forwards-futures/tests/test_m07_mtm.py
Read notes §7.2 and §7.4 first. Use numpy/pandas only.
"""
from __future__ import annotations
import math
import numpy as np
import pandas as pd
def daily_mtm(settles, entry: float, qty: float, side: int = 1) -> np.ndarray:
"""Daily variation-margin cash flows of a futures position (eq. 7.6).
settles: daily settlement prices F_1..F_n (position opened at `entry` on day 1, held through day n).
Flow on day 1: side*qty*(F_1 - entry); on day t > 1: side*qty*(F_t - F_{t-1}).
side = +1 long, -1 short. Return a numpy array of length n.
"""
raise NotImplementedError
def margin_account(settles, entry: float, qty: float, side: int, initial_margin: float,
maintenance_margin: float) -> pd.DataFrame:
"""Simulate a margin account with a variation-margin call rule (Hull's convention, §7.2.2).
Day 0: deposit initial_margin. For each day t = 1..n:
balance_pre_t = balance_{t-1} + mtm_t
call_t = initial_margin - balance_pre_t if balance_pre_t < maintenance_margin, else 0
balance_t = balance_pre_t + call_t (excess is never withdrawn)
Return a DataFrame with index 1..n and columns
['settle', 'mtm', 'balance_pre', 'call', 'balance'].
Indian practice is the special case maintenance_margin == initial_margin.
"""
raise NotImplementedError
def return_on_margin(entry: float, exit_price: float, qty: float, margin: float, side: int = 1) -> float:
"""Return on margin (eq. 7.12): side * (exit_price - entry) * qty / margin."""
raise NotImplementedError
def tailed_holdings(times, r: float, T: float) -> np.ndarray:
"""Holdings that make daily-settled futures replicate a forward (Prop. 7.5).
times: t_0 < t_1 < ... < t_n = T in years. Over (t_i, t_{i+1}] hold
delta_i = exp(-r (T - t_{i+1})) contracts. Return an array of length n.
"""
raise NotImplementedError
def futures_terminal_value(fut_path, times, r: float, T: float, holdings) -> float:
"""Value at T of a futures strategy whose daily gains are invested (losses financed) at r until T:
V_T = sum_{i=0}^{n-1} holdings_i * (F_{i+1} - F_i) * exp(r (T - t_{i+1})).
fut_path has length n+1 (F_0..F_n), times has length n+1, holdings length n.
"""
raise NotImplementedError
def _vasicek_B_integrals(kappa: float, T: float) -> tuple[float, float]:
"""Return (I1, I2) = (int_0^T B(s,T) ds, int_0^T B(s,T)^2 ds), B(s,T) = (1 - e^{-kappa (T-s)}) / kappa.
Closed forms (Problem 7.7): with B0 = B(0,T),
I1 = (T - B0) / kappa
I2 = (T - 2 B0 + (1 - e^{-2 kappa T}) / (2 kappa)) / kappa^2
"""
raise NotImplementedError
def futures_forward_ratio_analytic(sigma: float, kappa: float, eta: float, rho: float, T: float) -> float:
"""F_futures / F_forward under Vasicek rates dr = kappa(theta - r)dt + eta dW_r and
dS/S = r dt + sigma dW_S under Q, d<W_S, W_r> = rho dt (Prop. 7.6, Problem 7.7):
ratio = exp(eta^2 * I2 + rho * sigma * eta * I1).
"""
raise NotImplementedError
def futures_forward_mc(S0: float, sigma: float, r0: float, kappa: float, theta: float, eta: float,
rho: float, T: float, n_steps: int = 100, n_paths: int = 40_000,
seed: int = 0) -> dict:
"""Monte Carlo futures and forward prices under stochastic (Vasicek) rates (§7.2.4).
rng = np.random.default_rng(seed); dt = T/n_steps. Per step, draw z1 then an
independent z, set z2 = rho z1 + sqrt(1-rho^2) z, and update (in this order)
ln S += (r - sigma^2/2) dt + sigma sqrt(dt) z1
ln D -= r dt
r += kappa (theta - r) dt + eta sqrt(dt) z2
starting from S0, D = 1, r = r0. Then
futures = mean(S_T); forward = mean(D_T S_T) / mean(D_T); diff = futures - forward
se_diff = std(psi, ddof=1)/sqrt(n_paths) with the delta-method influence function
psi = S - D S / mean(D) + mean(D S) D / mean(D)^2
P0T = mean(D_T)
Return dict(futures=..., forward=..., diff=..., se_diff=..., P0T=...).
"""
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.