import { expiryFromLabel, thirdFriday, utc, type Expiry } from "../pricing/time";
export interface DividendPoint {
/** Contract label, e.g. "Dec27". */
label: string;
/** First day of the window: the day after the previous expiry. */
windowStart: Date;
/** Ex-date = pay date of the bucket: the window's expiry. */
date: Date;
/** Gross dividend in index points going ex within the window. */
gross: number;
}
export interface MarketData {
valuationDate: Date;
spot: number;
/** €STR, continuously compounded, ACT/360. */
rate: number;
/** EQL spread over €STR for discounting option premia, decimal. */
eqlSpread: number;
dividends: DividendPoint[];
}
export interface RunQuote {
expiry: Expiry;
bid: number;
offer: number;
}
/**
* Dividend schedule from the brief, as dividend-futures-style windows (not cumulative):
*
* Sep26 previous expiry (Jun26) + 1 → Sep26 expiry
* Dec26 Sep26 expiry + 1 → Dec26 expiry
* Dec27… previous Dec expiry + 1 → this Dec expiry (yearly)
*
* Each window's amount goes ex — and is paid — on the window's expiry.
*/
const WINDOWS: [label: string, start: Date, end: Date, gross: number][] = [
["Sep26", thirdFriday(2026, 5), thirdFriday(2026, 8), 0.33],
["Dec26", thirdFriday(2026, 8), thirdFriday(2026, 11), 30.25],
["Dec27", thirdFriday(2026, 11), thirdFriday(2027, 11), 218.25],
["Dec28", thirdFriday(2027, 11), thirdFriday(2028, 11), 409.25],
["Dec29", thirdFriday(2028, 11), thirdFriday(2029, 11), 596],
["Dec30", thirdFriday(2029, 11), thirdFriday(2030, 11), 777],
];
const nextDay = (d: Date) => new Date(d.getTime() + 86_400_000);
export const SX5E_DIVIDENDS: DividendPoint[] = WINDOWS.map(([label, prevExpiry, expiry, gross]) => ({
label,
windowStart: nextDay(prevExpiry),
date: expiry,
gross,
}));
const VALUATION_DATE = utc(2026, 7, 11);
/**
* TRF launch date for the accrual (simplified: the brief's valuation date, so the accrual
* starts at 0). The valuation date can't be set before it.
*/
export const TRF_LAUNCH_DATE = VALUATION_DATE;
/** Historical €STR for the accrual since launch: flat, continuously compounded ACT/360. */
export const HISTORICAL_ESTR = 0.02;
export const DEFAULT_ALL_IN = 0.87;
export const SX5E_TRF_RUN: RunQuote[] = [
{ expiry: expiryFromLabel("Dec26"), bid: 61.5, offer: 63.5 },
{ expiry: expiryFromLabel("Dec27"), bid: 83.5, offer: 85 },
{ expiry: expiryFromLabel("Dec28"), bid: 89, offer: 91.5 },
{ expiry: expiryFromLabel("Dec29"), bid: 94, offer: 96 },
{ expiry: expiryFromLabel("Dec30"), bid: 97, offer: 101 },
];
export const EXPIRIES: Expiry[] = SX5E_TRF_RUN.map((q) => q.expiry);
/**
* "SX5E TRF vs 19.255 basis" — meaning still open. It is not the Dec26 TRF basis at the
* real spot (that is 14.90 at the 62.5bp mid, with settlement lags).
*/
export const TRF_REFERENCE_BASIS = 19.255;
export const SX5E_MARKET: MarketData = {
valuationDate: VALUATION_DATE,
spot: 6551.22, // SX5E close, 11-Aug-2026
rate: 0.02, // PLACEHOLDER — flat; replace with the €STR curve
eqlSpread: 0.005, // flat
dividends: SX5E_DIVIDENDS,
};