src/lib/pricing/accrual.ts

30 lines
import { overnightRate360, TRF_DISTRIBUTION_RATIO } from "./instruments";
import { settlementFraction } from "./time";

/**
 * TRF accrual since launch, index points (Eurex Circular 086/21, Att. 3):
 *
 *   Accrual_t = Σ_{τ ∈ (launch, t]} ( Div_τ − S_{τ−1} · L(τ−1, τ) · Δ_SSP(τ−1, τ) )
 *
 * Simplifications: the historical funding rate is flat and, with no spot history, the
 * index is held at today's level, so the daily funding sum telescopes to
 * S · L · Δ_SSP(launch, t). Distributions are the gross dividends going ex in (launch, t].
 */
export function trfAccrual(p: {
  launch: Date;
  valuation: Date;
  spot: number;
  /** Historical €STR, continuously compounded ACT/360. */
  rate: number;
  dividends: { date: Date; gross: number }[];
}): { distributions: number; funding: number; accrual: number } {
  if (p.valuation.getTime() <= p.launch.getTime()) return { distributions: 0, funding: 0, accrual: 0 };
  const distributions =
    TRF_DISTRIBUTION_RATIO *
    p.dividends
      .filter((d) => d.date.getTime() > p.launch.getTime() && d.date.getTime() <= p.valuation.getTime())
      .reduce((acc, d) => acc + d.gross, 0);
  const funding = p.spot * overnightRate360(p.rate) * settlementFraction(p.launch, p.valuation);
  return { distributions, funding, accrual: distributions - funding };
}