islandflow/services/compute/src/structures.ts
2026-01-28 21:04:24 -05:00

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()
};
};