Exercise · m01_mechanics.py
Call auctions, index arithmetic, corporate actions
not run yet
Not saved: sign up to keep your code"""Exercise 1.5: call auctions, index arithmetic, corporate actions.
Three pieces of exchange arithmetic that every price series you download has
already been through:
* the pre-open and closing call auctions pick one price that maximises
executable volume (notes.md §1.3, Proposition 1.1);
* a free-float index is a ratio of market values to a divisor, and the
divisor absorbs every basket change (notes.md §1.9, Proposition 1.4);
* splits, bonuses and dividends make raw price series jump on the ex-date;
adjusted series remove the jump with a multiplicative factor
(notes.md §1.10, Proposition 1.5).
Check:
cd exercises && .venv/bin/python -m pytest modules/M01-market-structure/tests
"""
from __future__ import annotations
import numpy as np # noqa: F401
import pandas as pd
# ----------------------------------------------------------------- call auction
def auction_schedule(buys, sells):
"""Cumulative demand and supply at every candidate price.
buys, sells: lists of (price, qty). price None means a market order, which
is willing to trade at any price.
Candidate prices are the distinct LIMIT prices on either side, ascending.
D(p) = market-buy qty + sum of buy-limit qty with limit >= p.
S(p) = market-sell qty + sum of sell-limit qty with limit <= p.
Returns (prices, demand, supply) as numpy float arrays of equal length.
"""
raise NotImplementedError
def equilibrium_price(buys, sells, ref_price: float):
"""Single-price call auction (notes.md §1.3).
Returns (price, volume, imbalance). Rules, applied in order:
1. maximise executable volume V(p) = min(D(p), S(p));
2. among ties, minimise the imbalance |D(p) - S(p)|;
3. among ties, minimise |p - ref_price| (ref = previous close);
4. if still tied, take the lower price.
If no positive volume is possible (book does not cross, or empty),
return (None, 0.0, None).
"""
raise NotImplementedError
# ----------------------------------------------------------------- index arithmetic
def free_float_mcap(prices, shares, iwf) -> float:
"""Free-float market capitalisation sum_i P_i * N_i * f_i.
P_i price, N_i shares outstanding, f_i investible weight factor in (0, 1].
"""
raise NotImplementedError
def index_level(prices, shares, iwf, divisor: float) -> float:
"""Index level I = sum_i P_i N_i f_i / D (notes.md eq. (1.6))."""
raise NotImplementedError
def new_divisor(old_divisor: float, mcap_before: float, mcap_after: float) -> float:
"""Divisor after a basket change (notes.md eq. (1.8)), chosen so the index level is unchanged:
mcap_before / old_divisor = mcap_after / new_divisor.
mcap_before and mcap_after are both evaluated at the SAME prices (the last
close before the change), before and after the change in constituents,
shares or IWFs.
"""
raise NotImplementedError
# ----------------------------------------------------------------- corporate actions
def split_factor(old_face: float, new_face: float) -> float:
"""Price adjustment factor for a split of face value old_face -> new_face.
A ₹10 -> ₹2 split multiplies the share count by 5, so pre-split prices are
multiplied by 2/10 = 0.2.
"""
raise NotImplementedError
def bonus_factor(new_shares: int, held_shares: int) -> float:
"""Factor for an a:b bonus (a new shares for every b held): b / (a + b).
A 1:1 bonus gives 0.5.
"""
raise NotImplementedError
def dividend_factor(cum_close: float, dividend: float) -> float:
"""Multiplicative dividend adjustment (P_cum - d) / P_cum (notes.md eq. (1.12)).
P_cum is the last close on which the share still carried the dividend
(the day before the ex-date).
"""
raise NotImplementedError
def adjusted_prices(close: pd.Series, actions: list[tuple[str, float]]) -> pd.Series:
"""Back-adjust a close series indexed by dates.
actions: list of (ex_date, factor). Every close strictly BEFORE ex_date is
multiplied by factor; factors compound when there are several actions.
Closes on or after the ex-date are unchanged. Return a new Series; do not
modify the input.
"""
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.