Implement options snapshot tape table

This commit is contained in:
dirtydishes 2026-05-04 01:14:52 -04:00
parent 6abfff30d3
commit e78387130a
15 changed files with 904 additions and 128 deletions

View file

@ -0,0 +1,88 @@
import { describe, expect, it } from "bun:test";
import type { EquityQuote, OptionNBBO, OptionPrint, OptionsSignalConfig } from "@islandflow/types";
import { enrichOptionPrint, selectAtOrBefore } from "../src/enrichment";
const config: OptionsSignalConfig = {
mode: "all",
minNotional: 0,
etfMinNotional: 0,
bidSideMinNotional: 0,
midMinNotional: 0,
missingNbboMinNotional: 0,
largePrintMinSize: 1,
largePrintMinNotional: 0,
sweepMinNotional: 0,
autoKeepMinNotional: 100_000,
nbboMaxAgeMs: 1_500,
etfUnderlyings: new Set(["SPY"])
};
const print: OptionPrint = {
source_ts: 1_000,
ingest_ts: 1_000,
seq: 1,
trace_id: "print-1",
ts: 1_000,
option_contract_id: "SPY-2025-01-17-450-C",
price: 1.3,
size: 10,
exchange: "TEST"
};
const nbbo = (overrides: Partial<OptionNBBO> = {}): OptionNBBO => ({
source_ts: 990,
ingest_ts: 990,
seq: 1,
trace_id: "nbbo-1",
ts: 990,
option_contract_id: "SPY-2025-01-17-450-C",
bid: 1.2,
ask: 1.3,
bidSize: 20,
askSize: 30,
...overrides
});
const equityQuote = (overrides: Partial<EquityQuote> = {}): EquityQuote => ({
source_ts: 980,
ingest_ts: 980,
seq: 1,
trace_id: "eq-1",
ts: 980,
underlying_id: "SPY",
bid: 450,
ask: 450.1,
...overrides
});
describe("option print enrichment", () => {
it("attaches preserved NBBO context and mirrors nbbo_side", () => {
const enriched = enrichOptionPrint(print, nbbo(), null, config);
expect(enriched.execution_nbbo_bid).toBe(1.2);
expect(enriched.execution_nbbo_ask).toBe(1.3);
expect(enriched.execution_nbbo_mid).toBe(1.25);
expect(enriched.execution_nbbo_age_ms).toBe(10);
expect(enriched.execution_nbbo_side).toBe("A");
expect(enriched.nbbo_side).toBe(enriched.execution_nbbo_side);
});
it("attaches preserved underlying quote mid as spot", () => {
const enriched = enrichOptionPrint(print, null, equityQuote(), config);
expect(enriched.execution_underlying_spot).toBe(450.05);
expect(enriched.execution_underlying_mid).toBe(450.05);
expect(enriched.execution_underlying_source).toBe("equity_quote_mid");
expect(enriched.execution_underlying_age_ms).toBe(20);
});
it("selects context at or before the print timestamp only", () => {
const selected = selectAtOrBefore(
[nbbo({ ts: 900, seq: 1, bid: 1 }), nbbo({ ts: 1_001, seq: 2, bid: 2 })],
print.ts
);
expect(selected?.ts).toBe(900);
expect(selected?.bid).toBe(1);
});
});

View file

@ -1,5 +1,5 @@
import { describe, expect, it } from "bun:test";
import { buildSyntheticBurstForTest } from "../src/adapters/synthetic";
import { buildSyntheticBurstForTest, updateSyntheticIvForTest } from "../src/adapters/synthetic";
const totalBurstNotional = (burst: {
basePrice: number;
@ -24,3 +24,66 @@ describe("synthetic options burst sizing", () => {
expect(totalBurstNotional(burst)).toBeLessThanOrEqual(240_000);
});
});
describe("synthetic options IV model", () => {
it("increases under repeated same-contract ask buying", () => {
let state = updateSyntheticIvForTest(undefined, {
ts: 1_000,
placement: "A",
size: 100,
notional: 20_000,
dteDays: 1,
moneyness: 1.02
});
const firstIv = state.iv;
state = updateSyntheticIvForTest(state, {
ts: 1_100,
placement: "AA",
size: 300,
notional: 80_000,
dteDays: 1,
moneyness: 1.02
});
expect(state.iv).toBeGreaterThan(firstIv);
});
it("decays after inactivity", () => {
const active = updateSyntheticIvForTest(undefined, {
ts: 1_000,
placement: "AA",
size: 500,
notional: 120_000,
dteDays: 7,
moneyness: 1.1
});
const decayed = updateSyntheticIvForTest(active, {
ts: 181_000,
placement: "MID",
size: 10,
notional: 1_000,
dteDays: 7,
moneyness: 1.1
});
expect(decayed.iv).toBeLessThan(active.iv);
});
it("keeps IV within clamps", () => {
let state = undefined;
for (let i = 0; i < 80; i += 1) {
state = updateSyntheticIvForTest(state, {
ts: 1_000 + i * 10,
placement: "AA",
size: 10_000,
notional: 5_000_000,
dteDays: 0,
moneyness: 1.8
});
}
expect(state.iv).toBeGreaterThanOrEqual(0.05);
expect(state.iv).toBeLessThanOrEqual(2.5);
});
});