1"""Corporate-action policy (docs/DATA_CARD.md §adjustments).23- Splits: applied explicitly from the instrument master (`/instruments/{symbol}`: action_type=split, ex_date, value);4 for a bar in session d < ex_date, prices /= ratio and volume *= ratio for every split with ex_date > d. This equals the5 server-side `adjustment=split` series (verified in tests/integration/test_live_api.py on real rows).6- Cash dividends are NOT applied to intraday returns: a dividend adjustment rescales all history before the ex-date and7 therefore makes any windowed return depend on the end of the download range (window-dependent artifact). Intraday8 log returns within a session are invariant to the dividend factor; the only affected quantity is the overnight gap9 feature across an ex-dividend date, which we leave raw and flag.10- Mergers / spin-offs: not available from the vendor; a symbol disappears (delisted suffix) and the series ends.11"""12from __future__ import annotations1314import numpy as np15import pandas as pd16import polars as pl171819def split_factor_by_session(session_dates: np.ndarray, splits: list[dict]) -> np.ndarray:20 """Cumulative split ratio applying to each session date (product of ratios with ex_date > session)."""21 sd = pd.to_datetime(pd.Series(session_dates)).to_numpy().astype("datetime64[D]")22 factor = np.ones(len(sd), dtype=np.float64)23 for s in splits:24 if s.get("action_type") != "split":25 continue26 ex = np.datetime64(str(s["ex_date"]), "D")27 ratio = float(s["value"])28 if ratio <= 0:29 continue30 factor[sd < ex] *= ratio31 return factor323334def apply_splits(bars: pl.DataFrame, splits: list[dict]) -> pl.DataFrame:35 """Return split-adjusted copy (raw columns kept as *_raw)."""36 f = split_factor_by_session(bars["session_date"].to_numpy(), splits)37 out = bars.with_columns(pl.Series("split_factor", f))38 return out.with_columns([39 (pl.col(c) / pl.col("split_factor")).alias(c) for c in ("open", "high", "low", "close")40 ] + [(pl.col("volume") * pl.col("split_factor")).alias("volume")]).with_columns(41 pl.lit("split").alias("adjustment_type"))424344def ex_dividend_sessions(actions: list[dict]) -> set[str]:45 return {str(a["ex_date"]) for a in actions if a.get("action_type") == "dividend"}46