53 lines
1.6 KiB
TypeScript
53 lines
1.6 KiB
TypeScript
import type { ParsedContract } from "./contracts";
|
|
|
|
export type ContractLeg = ParsedContract & {
|
|
contractId: string;
|
|
startTs: number;
|
|
endTs: number;
|
|
};
|
|
|
|
export type StructureSummary = {
|
|
type: string;
|
|
legs: number;
|
|
strikes: number;
|
|
strikeSpan: number;
|
|
rights: string;
|
|
contractIds: string[];
|
|
};
|
|
|
|
export const summarizeStructure = (legs: ContractLeg[]): StructureSummary | null => {
|
|
if (legs.length < 2) {
|
|
return null;
|
|
}
|
|
|
|
const strikes = Array.from(new Set(legs.map((leg) => leg.strike))).sort((a, b) => a - b);
|
|
const rights = new Set(legs.map((leg) => leg.right));
|
|
const expiries = new Set(legs.map((leg) => leg.expiry));
|
|
const strikeSpan = strikes.length >= 2 ? strikes[strikes.length - 1] - strikes[0] : 0;
|
|
|
|
let type = "multi_leg";
|
|
if (expiries.size === 1) {
|
|
if (rights.size === 2 && strikes.length === 1) {
|
|
type = "straddle";
|
|
} else if (rights.size === 2 && strikes.length >= 2) {
|
|
type = "strangle";
|
|
} else if (rights.size === 1 && strikes.length === 2) {
|
|
type = "vertical";
|
|
} else if (rights.size === 1 && strikes.length >= 3) {
|
|
type = "ladder";
|
|
}
|
|
} else if (rights.size === 1 && expiries.size === 2) {
|
|
// Conservative roll heuristic: same right, exactly two expiries within the burst window.
|
|
// We do not attempt to infer the exact strategy beyond roll-style behavior.
|
|
type = "roll";
|
|
}
|
|
|
|
return {
|
|
type,
|
|
legs: legs.length,
|
|
strikes: strikes.length,
|
|
strikeSpan,
|
|
rights: rights.size === 2 ? "C/P" : Array.from(rights)[0] ?? "",
|
|
contractIds: legs.map((leg) => leg.contractId).slice().sort()
|
|
};
|
|
};
|