islandflow/services/ingest-options/src/adapters/synthetic.ts

1608 lines
48 KiB
TypeScript

import {
SP500_SYMBOLS,
buildEmptySyntheticProfileHitCounts,
getSyntheticCoverageBoost,
getSyntheticScenarioWeights,
getSyntheticSessionState,
getSyntheticUnderlyingState,
hashSyntheticSymbol,
type FlowPacket,
type OptionNBBO,
type OptionPrint,
type SmartMoneyProfileId,
type SyntheticControlState,
type SyntheticMarketMode
} from "@islandflow/types";
import type { OptionIngestAdapter, OptionIngestHandlers } from "./types";
type SyntheticOptionsAdapterConfig = {
emitIntervalMs: number;
mode: SyntheticMarketMode;
getControl?: () => SyntheticControlState;
};
type BurstLeg = {
contractId: string;
right: "C" | "P";
expiryOffsetDays: number;
strike: number;
basePrice: number;
baseSize: number;
exchange: string;
placementScenarioId: string;
};
type Burst = {
contractId: string;
underlying: number;
expiryOffsetDays: number;
strike: number;
basePrice: number;
baseSize: number;
legs: BurstLeg[];
conditions: string[];
cycles: number;
printCount: number;
priceStep: number;
scenarioId: string;
label: SyntheticScenarioLabel;
hiddenLabel: string;
seed: number;
flowFeatures: FlowPacket["features"];
missingQuoteProbability: number;
staleQuoteProbability: number;
};
type ScenarioLegTemplate = {
right: "C" | "P";
strikeMoneyness?: number;
strikeOffsetSteps?: number;
expiryOffsetDays?: number;
priceMultiplier?: number;
sizeMultiplier?: number;
placementScenarioId?: string;
};
type Scenario = {
id: string;
hiddenLabel: string;
label: SyntheticScenarioLabel;
right: "C" | "P" | "either";
weight: number;
countRange: [number, number];
sizeRange: [number, number];
targetNotionalRange: [number, number];
priceTrend: "up" | "down" | "flat";
expiryOffsets?: number[];
strikeMoneyness?: number;
preferredSymbols?: string[];
placementProfile?: SyntheticScenarioLabel;
missingQuoteProbability?: number;
staleQuoteProbability?: number;
conditions?: string[];
flowFeatures: FlowPacket["features"];
legs?: ScenarioLegTemplate[];
};
type WeightedValue<T> = {
value: T;
weight: number;
};
type CoverageWindowState = Record<SmartMoneyProfileId, number[]>;
type SyntheticOptionsProfile = {
burstRunRange: [number, number];
scenarios: Scenario[];
pricePlacements: Record<string, WeightedValue<PricePlacement>[]>;
};
export type SyntheticContractIvState = {
iv: number;
pressure: number;
lastTs: number;
};
export type PricePlacement = "AA" | "A" | "MID" | "B" | "BB";
export type SyntheticScenarioLabel = SmartMoneyProfileId | "neutral_noise";
export type SyntheticSmartMoneyScenario = {
id: string;
label: SyntheticScenarioLabel;
hiddenLabel: string;
};
const OPTION_CONTRACT_MULTIPLIER = 100;
const IV_MIN = 0.05;
const IV_MAX = 2.5;
const IV_DECAY_HALF_LIFE_MS = 60_000;
const MS_PER_DAY = 24 * 60 * 60 * 1000;
const EXPIRY_OFFSETS = [0, 1, 7, 14, 28, 45, 60, 90];
const EXCHANGES = ["CBOE", "PHLX", "ISE", "ARCA", "BOX", "MIAX"];
const CONDITIONS = ["SWEEP", "ISO", "FILL", "TEST"];
const SYNTHETIC_SYMBOLS = ["SPY", ...(SP500_SYMBOLS as readonly string[])];
const SMART_MONEY_SCENARIO_IDS = [
"institutional_directional",
"retail_whale",
"event_driven",
"vol_seller",
"arbitrage",
"hedge_reactive",
"neutral_noise"
] as const;
const SCENARIO_LIBRARY: Scenario[] = [
{
id: "call_sweep",
hiddenLabel: "call_sweep",
label: "institutional_directional",
right: "C",
weight: 1.2,
countRange: [4, 7],
sizeRange: [420, 1200],
targetNotionalRange: [55_000, 165_000],
priceTrend: "up",
expiryOffsets: [7, 14, 28],
strikeMoneyness: 1.01,
placementProfile: "institutional_directional",
conditions: ["SWEEP"],
flowFeatures: {
nbbo_aggressive_ratio: 0.84,
nbbo_aggressive_buy_ratio: 0.8,
nbbo_aggressive_sell_ratio: 0.04,
nbbo_inside_ratio: 0.08,
venue_count: 4
}
},
{
id: "put_sweep",
hiddenLabel: "put_sweep",
label: "institutional_directional",
right: "P",
weight: 1.15,
countRange: [4, 7],
sizeRange: [420, 1200],
targetNotionalRange: [55_000, 165_000],
priceTrend: "up",
expiryOffsets: [7, 14, 28],
strikeMoneyness: 0.99,
placementProfile: "institutional_directional",
conditions: ["SWEEP"],
flowFeatures: {
nbbo_aggressive_ratio: 0.84,
nbbo_aggressive_buy_ratio: 0.8,
nbbo_aggressive_sell_ratio: 0.04,
nbbo_inside_ratio: 0.08,
venue_count: 4
}
},
{
id: "ask_lift_accumulation",
hiddenLabel: "ask_lift_accumulation",
label: "institutional_directional",
right: "either",
weight: 0.95,
countRange: [2, 4],
sizeRange: [160, 540],
targetNotionalRange: [12_000, 50_000],
priceTrend: "flat",
strikeMoneyness: 1.0,
placementProfile: "institutional_directional",
conditions: ["FILL"],
flowFeatures: {
nbbo_aggressive_ratio: 0.66,
nbbo_aggressive_buy_ratio: 0.62,
nbbo_aggressive_sell_ratio: 0.08,
nbbo_inside_ratio: 0.14,
venue_count: 2
}
},
{
id: "far_dated_conviction",
hiddenLabel: "far_dated_conviction",
label: "institutional_directional",
right: "either",
weight: 0.72,
countRange: [2, 3],
sizeRange: [220, 700],
targetNotionalRange: [35_000, 90_000],
priceTrend: "up",
expiryOffsets: [60, 90],
strikeMoneyness: 1.0,
placementProfile: "institutional_directional",
conditions: ["FILL"],
flowFeatures: {
nbbo_aggressive_ratio: 0.62,
nbbo_aggressive_buy_ratio: 0.56,
nbbo_aggressive_sell_ratio: 0.12,
nbbo_inside_ratio: 0.18,
venue_count: 3
}
},
{
id: "0dte_call_chase",
hiddenLabel: "0dte_call_chase",
label: "retail_whale",
right: "C",
weight: 1.2,
countRange: [6, 10],
sizeRange: [500, 1400],
targetNotionalRange: [28_000, 90_000],
priceTrend: "up",
expiryOffsets: [0, 1],
strikeMoneyness: 1.08,
placementProfile: "retail_whale",
conditions: ["ISO"],
flowFeatures: {
nbbo_aggressive_ratio: 0.74,
nbbo_aggressive_buy_ratio: 0.68,
nbbo_aggressive_sell_ratio: 0.04,
nbbo_inside_ratio: 0.1,
execution_iv_shock: 0.18,
venue_count: 4
}
},
{
id: "short_dated_put_panic",
hiddenLabel: "short_dated_put_panic",
label: "retail_whale",
right: "P",
weight: 0.92,
countRange: [5, 8],
sizeRange: [420, 1200],
targetNotionalRange: [24_000, 82_000],
priceTrend: "up",
expiryOffsets: [0, 1, 7],
strikeMoneyness: 0.94,
placementProfile: "retail_whale",
conditions: ["ISO"],
flowFeatures: {
nbbo_aggressive_ratio: 0.72,
nbbo_aggressive_buy_ratio: 0.64,
nbbo_aggressive_sell_ratio: 0.06,
nbbo_inside_ratio: 0.12,
execution_iv_shock: 0.16,
venue_count: 4
}
},
{
id: "attention_contract_spike",
hiddenLabel: "attention_contract_spike",
label: "retail_whale",
right: "either",
weight: 0.84,
countRange: [3, 6],
sizeRange: [360, 900],
targetNotionalRange: [18_000, 60_000],
priceTrend: "flat",
expiryOffsets: [1, 7],
strikeMoneyness: 1.06,
placementProfile: "retail_whale",
conditions: ["ISO"],
flowFeatures: {
nbbo_aggressive_ratio: 0.62,
nbbo_aggressive_buy_ratio: 0.56,
nbbo_aggressive_sell_ratio: 0.08,
nbbo_inside_ratio: 0.14,
execution_iv_shock: 0.14,
venue_count: 3
}
},
{
id: "earnings_vol_probe",
hiddenLabel: "earnings_vol_probe",
label: "event_driven",
right: "C",
weight: 0.9,
countRange: [2, 4],
sizeRange: [180, 520],
targetNotionalRange: [18_000, 52_000],
priceTrend: "flat",
expiryOffsets: [14, 28],
strikeMoneyness: 1.03,
preferredSymbols: ["AAPL", "MSFT", "NVDA", "META", "AMZN", "TSLA"],
placementProfile: "event_driven",
conditions: ["FILL", "EVENT_14D"],
flowFeatures: {
corporate_event_ts_offset_days: 14,
nbbo_aggressive_ratio: 0.46,
nbbo_aggressive_buy_ratio: 0.42,
nbbo_aggressive_sell_ratio: 0.12,
nbbo_inside_ratio: 0.2,
venue_count: 2
}
},
{
id: "pre_event_directional_ramp",
hiddenLabel: "pre_event_directional_ramp",
label: "event_driven",
right: "C",
weight: 1.1,
countRange: [4, 7],
sizeRange: [380, 920],
targetNotionalRange: [46_000, 120_000],
priceTrend: "up",
expiryOffsets: [7, 14],
strikeMoneyness: 1.02,
preferredSymbols: ["AAPL", "MSFT", "NVDA", "META", "AMZN", "TSLA"],
placementProfile: "event_driven",
conditions: ["FILL", "EVENT_14D"],
flowFeatures: {
corporate_event_ts_offset_days: 7,
nbbo_aggressive_ratio: 0.62,
nbbo_aggressive_buy_ratio: 0.58,
nbbo_aggressive_sell_ratio: 0.08,
nbbo_inside_ratio: 0.14,
venue_count: 3
}
},
{
id: "post_gap_followthrough",
hiddenLabel: "post_gap_followthrough",
label: "event_driven",
right: "either",
weight: 0.88,
countRange: [3, 5],
sizeRange: [260, 760],
targetNotionalRange: [24_000, 68_000],
priceTrend: "up",
expiryOffsets: [7, 14],
strikeMoneyness: 1.0,
preferredSymbols: ["AAPL", "MSFT", "NVDA", "META", "AMZN", "TSLA"],
placementProfile: "event_driven",
conditions: ["FILL", "EVENT_14D"],
flowFeatures: {
corporate_event_ts_offset_days: 1,
nbbo_aggressive_ratio: 0.58,
nbbo_aggressive_buy_ratio: 0.52,
nbbo_aggressive_sell_ratio: 0.1,
nbbo_inside_ratio: 0.16,
venue_count: 3
}
},
{
id: "covered_call_overwrite",
hiddenLabel: "covered_call_overwrite",
label: "vol_seller",
right: "C",
weight: 0.82,
countRange: [3, 5],
sizeRange: [700, 1800],
targetNotionalRange: [55_000, 150_000],
priceTrend: "down",
expiryOffsets: [28, 45, 60],
strikeMoneyness: 1.06,
placementProfile: "vol_seller",
conditions: ["FILL"],
flowFeatures: {
nbbo_aggressive_ratio: 0.54,
nbbo_aggressive_buy_ratio: 0.08,
nbbo_aggressive_sell_ratio: 0.52,
nbbo_inside_ratio: 0.16,
venue_count: 2
}
},
{
id: "cash_secured_put_write",
hiddenLabel: "cash_secured_put_write",
label: "vol_seller",
right: "P",
weight: 0.82,
countRange: [3, 5],
sizeRange: [700, 1800],
targetNotionalRange: [55_000, 150_000],
priceTrend: "down",
expiryOffsets: [28, 45, 60],
strikeMoneyness: 0.96,
placementProfile: "vol_seller",
conditions: ["FILL"],
flowFeatures: {
nbbo_aggressive_ratio: 0.54,
nbbo_aggressive_buy_ratio: 0.08,
nbbo_aggressive_sell_ratio: 0.52,
nbbo_inside_ratio: 0.16,
venue_count: 2
}
},
{
id: "short_straddle_harvest",
hiddenLabel: "short_straddle_harvest",
label: "vol_seller",
right: "either",
weight: 1.15,
countRange: [4, 7],
sizeRange: [650, 1500],
targetNotionalRange: [60_000, 150_000],
priceTrend: "down",
expiryOffsets: [28, 45],
strikeMoneyness: 1.0,
placementProfile: "vol_seller",
conditions: ["FILL"],
legs: [
{ right: "C", strikeMoneyness: 1.0, placementScenarioId: "vol_seller" },
{ right: "P", strikeMoneyness: 1.0, placementScenarioId: "vol_seller" }
],
flowFeatures: {
structure_type: "straddle",
structure_legs: 2,
structure_strikes: 1,
structure_rights: "C/P",
conditions: "COMPLEX",
nbbo_aggressive_ratio: 0.7,
nbbo_aggressive_buy_ratio: 0.08,
nbbo_aggressive_sell_ratio: 0.68,
nbbo_inside_ratio: 0.12,
same_size_leg_symmetry: 0.9,
venue_count: 3
}
},
{
id: "parity_vertical",
hiddenLabel: "parity_vertical",
label: "arbitrage",
right: "C",
weight: 1.0,
countRange: [4, 7],
sizeRange: [520, 1400],
targetNotionalRange: [45_000, 120_000],
priceTrend: "flat",
expiryOffsets: [28, 45],
placementProfile: "arbitrage",
conditions: ["FILL"],
legs: [
{ right: "C", strikeOffsetSteps: -1, placementScenarioId: "arbitrage" },
{ right: "C", strikeOffsetSteps: 1, placementScenarioId: "arbitrage" }
],
flowFeatures: {
structure_type: "vertical",
structure_legs: 2,
structure_strikes: 2,
structure_rights: "C",
nbbo_aggressive_ratio: 0.38,
nbbo_aggressive_buy_ratio: 0.42,
nbbo_aggressive_sell_ratio: 0.38,
nbbo_inside_ratio: 0.3,
same_size_leg_symmetry: 0.94,
venue_count: 3
}
},
{
id: "conversion_reversal",
hiddenLabel: "conversion_reversal",
label: "arbitrage",
right: "either",
weight: 0.76,
countRange: [5, 8],
sizeRange: [420, 1100],
targetNotionalRange: [38_000, 95_000],
priceTrend: "flat",
expiryOffsets: [28, 45],
placementProfile: "arbitrage",
conditions: ["FILL"],
flowFeatures: {
structure_type: "roll",
structure_legs: 3,
structure_strikes: 2,
structure_rights: "C/P",
nbbo_aggressive_ratio: 0.32,
nbbo_aggressive_buy_ratio: 0.34,
nbbo_aggressive_sell_ratio: 0.32,
nbbo_inside_ratio: 0.34,
same_size_leg_symmetry: 0.9,
venue_count: 3
}
},
{
id: "box_spread",
hiddenLabel: "box_spread",
label: "arbitrage",
right: "either",
weight: 0.66,
countRange: [6, 10],
sizeRange: [300, 900],
targetNotionalRange: [26_000, 80_000],
priceTrend: "flat",
expiryOffsets: [28, 45],
placementProfile: "arbitrage",
conditions: ["FILL"],
flowFeatures: {
structure_type: "box",
structure_legs: 4,
structure_strikes: 2,
structure_rights: "C/P",
nbbo_aggressive_ratio: 0.24,
nbbo_aggressive_buy_ratio: 0.26,
nbbo_aggressive_sell_ratio: 0.24,
nbbo_inside_ratio: 0.42,
same_size_leg_symmetry: 0.94,
venue_count: 2
}
},
{
id: "gamma_pinch_call_hedge",
hiddenLabel: "gamma_pinch_call_hedge",
label: "hedge_reactive",
right: "C",
weight: 0.92,
countRange: [4, 7],
sizeRange: [900, 2400],
targetNotionalRange: [30_000, 85_000],
priceTrend: "up",
expiryOffsets: [0, 1],
strikeMoneyness: 1.0,
preferredSymbols: ["SPY", "QQQ", "IWM", "AAPL", "NVDA"],
placementProfile: "hedge_reactive",
conditions: ["FILL"],
flowFeatures: {
nbbo_aggressive_ratio: 0.58,
nbbo_aggressive_buy_ratio: 0.54,
nbbo_aggressive_sell_ratio: 0.1,
nbbo_inside_ratio: 0.16,
underlying_move_bps: 44,
venue_count: 3
}
},
{
id: "reactive_put_wall",
hiddenLabel: "reactive_put_wall",
label: "hedge_reactive",
right: "P",
weight: 1.15,
countRange: [4, 7],
sizeRange: [1200, 2600],
targetNotionalRange: [35_000, 90_000],
priceTrend: "up",
expiryOffsets: [0, 1],
strikeMoneyness: 1.0,
preferredSymbols: ["SPY", "QQQ", "IWM", "AAPL", "NVDA"],
placementProfile: "hedge_reactive",
conditions: ["FILL"],
flowFeatures: {
nbbo_aggressive_ratio: 0.56,
nbbo_aggressive_buy_ratio: 0.54,
nbbo_aggressive_sell_ratio: 0.1,
nbbo_inside_ratio: 0.16,
underlying_move_bps: -64,
venue_count: 3
}
},
{
id: "dealer_unwind",
hiddenLabel: "dealer_unwind",
label: "hedge_reactive",
right: "either",
weight: 0.88,
countRange: [3, 6],
sizeRange: [700, 2000],
targetNotionalRange: [26_000, 72_000],
priceTrend: "down",
expiryOffsets: [0, 1, 7],
strikeMoneyness: 1.0,
preferredSymbols: ["SPY", "QQQ", "IWM", "AAPL", "NVDA"],
placementProfile: "hedge_reactive",
conditions: ["FILL"],
flowFeatures: {
nbbo_aggressive_ratio: 0.5,
nbbo_aggressive_buy_ratio: 0.18,
nbbo_aggressive_sell_ratio: 0.44,
nbbo_inside_ratio: 0.18,
underlying_move_bps: -28,
venue_count: 3
}
},
{
id: "single_print_mid",
hiddenLabel: "single_print_mid",
label: "neutral_noise",
right: "either",
weight: 1.2,
countRange: [1, 2],
sizeRange: [8, 60],
targetNotionalRange: [500, 5_000],
priceTrend: "flat",
strikeMoneyness: 1.0,
placementProfile: "neutral_noise",
conditions: ["FILL"],
flowFeatures: {
nbbo_aggressive_ratio: 0.18,
nbbo_aggressive_buy_ratio: 0.16,
nbbo_aggressive_sell_ratio: 0.12,
nbbo_inside_ratio: 0.62,
venue_count: 1
}
},
{
id: "two_sided_scalp",
hiddenLabel: "two_sided_scalp",
label: "neutral_noise",
right: "either",
weight: 1.0,
countRange: [2, 4],
sizeRange: [10, 120],
targetNotionalRange: [800, 7_000],
priceTrend: "flat",
strikeMoneyness: 1.0,
placementProfile: "neutral_noise",
conditions: ["FILL"],
flowFeatures: {
nbbo_aggressive_ratio: 0.24,
nbbo_aggressive_buy_ratio: 0.22,
nbbo_aggressive_sell_ratio: 0.2,
nbbo_inside_ratio: 0.54,
venue_count: 2
}
},
{
id: "stale_quote_noise",
hiddenLabel: "stale_quote_noise",
label: "neutral_noise",
right: "either",
weight: 0.86,
countRange: [1, 3],
sizeRange: [8, 80],
targetNotionalRange: [600, 5_500],
priceTrend: "flat",
strikeMoneyness: 1.0,
placementProfile: "neutral_noise",
missingQuoteProbability: 0.12,
staleQuoteProbability: 0.44,
conditions: ["TEST"],
flowFeatures: {
nbbo_aggressive_ratio: 0.16,
nbbo_aggressive_buy_ratio: 0.16,
nbbo_aggressive_sell_ratio: 0.12,
nbbo_inside_ratio: 0.58,
venue_count: 1
}
}
];
const PLACEMENTS: Record<string, WeightedValue<PricePlacement>[]> = {
institutional_directional: [
{ value: "AA", weight: 18 },
{ value: "A", weight: 44 },
{ value: "MID", weight: 18 },
{ value: "B", weight: 14 },
{ value: "BB", weight: 6 }
],
retail_whale: [
{ value: "AA", weight: 14 },
{ value: "A", weight: 30 },
{ value: "MID", weight: 24 },
{ value: "B", weight: 20 },
{ value: "BB", weight: 12 }
],
event_driven: [
{ value: "AA", weight: 12 },
{ value: "A", weight: 34 },
{ value: "MID", weight: 24 },
{ value: "B", weight: 18 },
{ value: "BB", weight: 12 }
],
vol_seller: [
{ value: "AA", weight: 4 },
{ value: "A", weight: 8 },
{ value: "MID", weight: 22 },
{ value: "B", weight: 36 },
{ value: "BB", weight: 30 }
],
arbitrage: [
{ value: "AA", weight: 10 },
{ value: "A", weight: 18 },
{ value: "MID", weight: 44 },
{ value: "B", weight: 18 },
{ value: "BB", weight: 10 }
],
hedge_reactive: [
{ value: "AA", weight: 16 },
{ value: "A", weight: 28 },
{ value: "MID", weight: 18 },
{ value: "B", weight: 24 },
{ value: "BB", weight: 14 }
],
neutral_noise: [
{ value: "AA", weight: 8 },
{ value: "A", weight: 14 },
{ value: "MID", weight: 44 },
{ value: "B", weight: 22 },
{ value: "BB", weight: 12 }
]
};
const SYNTHETIC_PROFILES: Record<SyntheticMarketMode, SyntheticOptionsProfile> = {
realistic: {
burstRunRange: [1, 1],
scenarios: SCENARIO_LIBRARY.map((scenario) => ({
...scenario,
countRange: [scenario.countRange[0], scenario.countRange[1]],
sizeRange: [scenario.sizeRange[0], scenario.sizeRange[1]],
targetNotionalRange: [
scenario.targetNotionalRange[0],
scenario.targetNotionalRange[1]
]
})),
pricePlacements: PLACEMENTS
},
active: {
burstRunRange: [1, 2],
scenarios: SCENARIO_LIBRARY.map((scenario) => ({
...scenario,
countRange: [scenario.countRange[0] + 1, scenario.countRange[1] + 2],
sizeRange: [
Math.round(scenario.sizeRange[0] * 1.4),
Math.round(scenario.sizeRange[1] * 1.55)
],
targetNotionalRange: [
Math.round(scenario.targetNotionalRange[0] * 1.35),
Math.round(scenario.targetNotionalRange[1] * 1.55)
]
})),
pricePlacements: PLACEMENTS
},
firehose: {
burstRunRange: [2, 3],
scenarios: SCENARIO_LIBRARY.map((scenario) => ({
...scenario,
countRange: [scenario.countRange[0] + 2, scenario.countRange[1] + 4],
sizeRange: [
Math.round(scenario.sizeRange[0] * 1.8),
Math.round(scenario.sizeRange[1] * 2.1)
],
targetNotionalRange: [
Math.round(scenario.targetNotionalRange[0] * 1.7),
Math.round(scenario.targetNotionalRange[1] * 2.0)
]
})),
pricePlacements: PLACEMENTS
}
};
const SMART_MONEY_TEMPLATE_SCENARIOS: Record<
Exclude<(typeof SMART_MONEY_SCENARIO_IDS)[number], "neutral_noise">,
string
> = {
institutional_directional: "call_sweep",
retail_whale: "0dte_call_chase",
event_driven: "pre_event_directional_ramp",
vol_seller: "short_straddle_harvest",
arbitrage: "parity_vertical",
hedge_reactive: "reactive_put_wall"
};
const pick = <T,>(items: readonly T[], seed: number): T => {
return items[Math.abs(seed) % items.length]!;
};
const pickInt = (min: number, max: number, seed: number): number => {
if (max <= min) {
return min;
}
return min + (Math.abs(seed) % (max - min + 1));
};
const pickFloat = (min: number, max: number, seed: number): number => {
if (max <= min) {
return min;
}
return min + (max - min) * ((Math.abs(seed) % 1000) / 1000);
};
const pickWeighted = <T extends { weight: number }>(items: T[], seed: number): T => {
const totalWeight = items.reduce((sum, item) => sum + item.weight, 0);
let target = Math.abs(seed) % totalWeight;
for (const item of items) {
if (target < item.weight) {
return item;
}
target -= item.weight;
}
return items[0]!;
};
const pickWeightedValue = <T>(items: WeightedValue<T>[], seed: number): T => {
return pickWeighted(
items.map((item) => ({ ...item })),
seed
).value;
};
const formatStrike = (strike: number): string => {
return strike.toFixed(3).replace(/\.?0+$/, "");
};
const formatExpiry = (now: number, offsetDays: number): string => {
return new Date(now + offsetDays * MS_PER_DAY).toISOString().slice(0, 10);
};
const clampValue = (value: number, min: number, max: number): number => {
if (!Number.isFinite(value)) {
return min;
}
return Math.max(min, Math.min(max, value));
};
const initializeSyntheticIv = (dteDays: number, moneyness: number): number => {
const dteBoost = dteDays <= 0 ? 0.22 : dteDays <= 7 ? 0.14 : dteDays <= 30 ? 0.06 : 0;
const moneynessBoost = clampValue(Math.abs(moneyness - 1) * 0.8, 0, 0.2);
return clampValue(0.24 + dteBoost + moneynessBoost, 0.18, 0.65);
};
export const updateSyntheticIvForTest = (
state: SyntheticContractIvState | undefined,
input: {
ts: number;
placement: PricePlacement;
size: number;
notional: number;
dteDays: number;
moneyness: number;
}
): SyntheticContractIvState => {
const previous = state ?? {
iv: initializeSyntheticIv(input.dteDays, input.moneyness),
pressure: 0,
lastTs: input.ts
};
const elapsed = Math.max(0, input.ts - previous.lastTs);
const decay = Math.pow(0.5, elapsed / IV_DECAY_HALF_LIFE_MS);
let pressure = previous.pressure * decay;
if (input.placement === "AA" || input.placement === "A") {
const sizeImpact = Math.log10(Math.max(10, input.size)) * 0.012;
const notionalImpact = Math.log10(Math.max(1_000, input.notional)) * 0.01;
pressure +=
input.placement === "AA"
? sizeImpact + notionalImpact
: (sizeImpact + notionalImpact) * 0.65;
} else if (input.placement === "MID") {
pressure += 0.001;
} else {
pressure -= input.placement === "BB" ? 0.018 : 0.01;
}
pressure = clampValue(pressure, -0.25, 1.85);
const baseline = initializeSyntheticIv(input.dteDays, input.moneyness);
const iv = clampValue(baseline + pressure * 0.42, IV_MIN, IV_MAX);
return { iv: Number(iv.toFixed(4)), pressure, lastTs: input.ts };
};
const estimateSyntheticOptionMid = (input: {
underlying: number;
strike: number;
right: "C" | "P";
dteDays: number;
moneyness: number;
mode: SyntheticMarketMode;
}): number => {
const intrinsic =
input.right === "C"
? Math.max(0, input.underlying - input.strike)
: Math.max(0, input.strike - input.underlying);
const timeYears = Math.max(1, input.dteDays + 1) / 365;
const baselineIv = initializeSyntheticIv(input.dteDays, input.moneyness);
const modeBoost =
input.mode === "firehose" ? 1.18 : input.mode === "active" ? 1.08 : 0.96;
const distance = Math.abs(input.moneyness - 1);
const extrinsic =
input.underlying *
baselineIv *
Math.sqrt(timeYears) *
Math.exp(-distance * 5.4) *
0.72 *
modeBoost;
const skewBoost = input.right === "P" && input.moneyness >= 1 ? 1.06 : 1;
return Number(
clampValue(intrinsic + extrinsic * skewBoost, 0.05, input.underlying * 0.45).toFixed(2)
);
};
const createCoverageWindowState = (): CoverageWindowState => ({
institutional_directional: [],
retail_whale: [],
event_driven: [],
vol_seller: [],
arbitrage: [],
hedge_reactive: []
});
const burstSequenceCache = new Map<string, Burst[]>();
const getCoverageCounts = (
coverageState: CoverageWindowState,
now: number,
control: SyntheticControlState
) => {
const floorTs = now - control.coverage_window_minutes * 60_000;
const counts = buildEmptySyntheticProfileHitCounts();
for (const profileId of Object.keys(coverageState) as SmartMoneyProfileId[]) {
coverageState[profileId] = coverageState[profileId].filter((ts) => ts >= floorTs);
counts[profileId] = coverageState[profileId].length;
}
return counts;
};
const recordCoverageHit = (
coverageState: CoverageWindowState,
profileId: SyntheticScenarioLabel,
now: number
) => {
if (profileId === "neutral_noise") {
return;
}
coverageState[profileId].push(now);
};
const chooseScenario = (
profile: SyntheticOptionsProfile,
now: number,
control: SyntheticControlState,
coverageState: CoverageWindowState
): Scenario => {
const session = getSyntheticSessionState(now, control);
const focusSymbol = session.focus_symbols[0] ?? SYNTHETIC_SYMBOLS[0]!;
const familyWeights = getSyntheticScenarioWeights(
focusSymbol,
now,
control,
session
);
const coverageCounts = getCoverageCounts(coverageState, now, control);
const weightedScenarios = profile.scenarios.map((scenario, index) => {
const familyWeight = familyWeights[scenario.label];
const coverageBoost =
scenario.label === "neutral_noise"
? 1
: getSyntheticCoverageBoost(
scenario.label,
{ profile_hit_counts: coverageCounts },
control
);
const quietBias =
scenario.label === "neutral_noise" && index % 2 === 0
? 1.08
: scenario.label === "neutral_noise"
? 0.94
: 1;
return {
...scenario,
weight: Math.max(1, Math.round(scenario.weight * familyWeight * coverageBoost * quietBias * 100))
};
});
return pickWeighted(weightedScenarios, now + control.shared_seed * 31);
};
const pickScenarioSymbol = (
scenario: Scenario,
now: number,
control: SyntheticControlState
): string => {
const session = getSyntheticSessionState(now, control);
const symbolPool =
scenario.preferredSymbols?.length && (scenario.label === "event_driven" || Math.abs(now) % 4 === 0)
? [...scenario.preferredSymbols]
: session.focus_symbols.length > 0
? [...session.focus_symbols, ...SYNTHETIC_SYMBOLS]
: [...SYNTHETIC_SYMBOLS];
return pick(symbolPool, hashSyntheticSymbol(scenario.id) + session.seed_bucket);
};
const buildDynamicFlowFeatures = (
scenario: Scenario,
symbol: string,
now: number,
control: SyntheticControlState
): FlowPacket["features"] => {
const session = getSyntheticSessionState(now, control);
const underlying = getSyntheticUnderlyingState(symbol, now, control, session);
const baseCoverage = 0.76 + session.quote_cleanliness * 0.18;
const baseSpreadZ = clampValue(
(underlying.spread / Math.max(0.01, underlying.mid)) * 650,
0.04,
0.34
);
const eventOffset =
scenario.label === "event_driven"
? Number(scenario.flowFeatures.corporate_event_ts_offset_days ?? 7)
: 0;
return {
...scenario.flowFeatures,
nbbo_coverage_ratio: clampValue(
Math.max(
Number(scenario.flowFeatures.nbbo_coverage_ratio ?? 0),
baseCoverage - (scenario.missingQuoteProbability ?? 0) * 0.45
),
0.3,
0.96
),
nbbo_inside_ratio: clampValue(
Number(scenario.flowFeatures.nbbo_inside_ratio ?? 0.2) +
(session.regime === "arb_calm" ? 0.08 : 0) -
(session.regime === "event_ramp" ? 0.04 : 0),
0.04,
0.72
),
nbbo_spread_z: clampValue(
Math.max(Number(scenario.flowFeatures.nbbo_spread_z ?? 0), baseSpreadZ),
0.02,
0.4
),
execution_iv_shock: clampValue(
Math.max(
Number(scenario.flowFeatures.execution_iv_shock ?? 0),
session.volatility_level * 0.12 + (scenario.label === "retail_whale" ? 0.04 : 0)
),
0,
0.26
),
underlying_move_bps: Math.round(
(Number(scenario.flowFeatures.underlying_move_bps ?? underlying.driftBps) +
underlying.shockBps * 0.35) *
100
) / 100,
venue_count: Math.max(
1,
Math.round(
Number(scenario.flowFeatures.venue_count ?? 1) +
(session.regime === "event_ramp" ? 1 : 0) +
(session.regime === "dealer_gamma" ? 1 : 0)
)
),
...(eventOffset > 0 ? { corporate_event_ts_offset_days: eventOffset } : {})
};
};
const buildBurst = (
burstIndex: number,
now: number,
mode: SyntheticMarketMode,
profile: SyntheticOptionsProfile,
control: SyntheticControlState,
coverageState: CoverageWindowState,
scenarioOverride?: Scenario
): Burst => {
const scenario =
scenarioOverride ?? chooseScenario(profile, now, control, coverageState);
const symbol = pickScenarioSymbol(scenario, now, control);
const symbolHash = hashSyntheticSymbol(symbol);
const seed = symbolHash + burstIndex * 7;
const session = getSyntheticSessionState(now, control);
const underlyingState = getSyntheticUnderlyingState(symbol, now, control, session);
const baseUnderlying = underlyingState.mid;
const expiryOffset = pick(
scenario.expiryOffsets ?? EXPIRY_OFFSETS,
symbolHash + burstIndex
);
const strikeStep = baseUnderlying >= 200 ? 10 : baseUnderlying >= 100 ? 5 : 2.5;
const right =
scenario.right === "either"
? (symbolHash + burstIndex) % 2 === 0
? "C"
: "P"
: scenario.right;
const cycles = pickInt(
scenario.countRange[0],
scenario.countRange[1],
symbolHash + burstIndex * 13
);
const baseSize = pickInt(
scenario.sizeRange[0],
scenario.sizeRange[1],
symbolHash + burstIndex * 17
);
const targetNotional = pickFloat(
scenario.targetNotionalRange[0],
scenario.targetNotionalRange[1],
symbolHash + burstIndex * 19
);
const conditions = scenario.conditions?.length
? [...scenario.conditions]
: [pick(CONDITIONS, burstIndex)];
const priceStep =
scenario.priceTrend === "up" ? 0.01 : scenario.priceTrend === "down" ? -0.01 : 0;
const flowFeatures = buildDynamicFlowFeatures(scenario, symbol, now, control);
const legTemplates =
scenario.legs?.length
? scenario.legs
: [
{
right,
strikeMoneyness: scenario.strikeMoneyness,
placementScenarioId: scenario.placementProfile ?? scenario.label
}
];
const targetNotionalPerLeg = targetNotional / legTemplates.length;
const legs = legTemplates.map((template, legIndex): BurstLeg => {
const legExpiryOffset = template.expiryOffsetDays ?? expiryOffset;
const expiry = formatExpiry(now, legExpiryOffset);
const moneynessSteps = scenario.label === "neutral_noise" ? 5 : 2;
const strikeOffset =
template.strikeOffsetSteps ??
pickInt(-moneynessSteps, moneynessSteps, symbolHash + burstIndex * 11 + legIndex * 17);
const templateStrike =
template.strikeMoneyness !== undefined
? Math.round((baseUnderlying * template.strikeMoneyness) / strikeStep) * strikeStep
: scenario.strikeMoneyness !== undefined
? Math.round((baseUnderlying * scenario.strikeMoneyness) / strikeStep) * strikeStep
: null;
const strike = Math.max(
1,
templateStrike ??
Math.round(baseUnderlying / strikeStep) * strikeStep +
strikeOffset * strikeStep
);
const legSize = Math.max(1, Math.round(baseSize * (template.sizeMultiplier ?? 1)));
const legMoneyness = strike / baseUnderlying;
const theoreticalMid = estimateSyntheticOptionMid({
underlying: baseUnderlying,
strike,
right: template.right,
dteDays: legExpiryOffset,
moneyness: legMoneyness,
mode
});
const targetMid =
targetNotionalPerLeg /
Math.max(1, legSize * cycles * OPTION_CONTRACT_MULTIPLIER);
const cappedTheoreticalMid = Math.min(
theoreticalMid,
Math.max(0.35, targetMid * (scenario.label === "institutional_directional" ? 2.2 : 2.6))
);
const blendedMid = cappedTheoreticalMid * 0.45 + targetMid * 0.55 * (template.priceMultiplier ?? 1);
return {
contractId: `${symbol}-${expiry}-${formatStrike(strike)}-${template.right}`,
right: template.right,
expiryOffsetDays: legExpiryOffset,
strike,
basePrice: Number(Math.max(0.05, blendedMid).toFixed(2)),
baseSize: legSize,
exchange: pick(EXCHANGES, burstIndex + symbolHash + legIndex * 3),
placementScenarioId:
template.placementScenarioId ?? scenario.placementProfile ?? scenario.label
};
});
const primaryLeg = legs[0]!;
return {
contractId: primaryLeg.contractId,
underlying: baseUnderlying,
expiryOffsetDays: primaryLeg.expiryOffsetDays,
strike: primaryLeg.strike,
basePrice: primaryLeg.basePrice,
baseSize: primaryLeg.baseSize,
legs,
conditions,
cycles,
printCount: cycles * legs.length,
priceStep,
scenarioId: scenario.id,
label: scenario.label,
hiddenLabel: scenario.hiddenLabel,
flowFeatures,
seed,
missingQuoteProbability:
scenario.missingQuoteProbability ??
clampValue((1 - session.quote_cleanliness) * 0.16, 0, 0.18),
staleQuoteProbability:
scenario.staleQuoteProbability ??
clampValue((1 - session.quote_cleanliness) * 0.3, 0, 0.42)
};
};
const pickPlacement = (burst: Burst, index: number): PricePlacement => {
const key = burst.legs[index % burst.legs.length]?.placementScenarioId ?? burst.label;
const placementOptions = PLACEMENTS[key] ?? PLACEMENTS[burst.label] ?? PLACEMENTS.neutral_noise;
return pickWeightedValue(placementOptions, burst.seed + index * 11);
};
export const listSyntheticSmartMoneyScenariosForTest = (): SyntheticSmartMoneyScenario[] =>
SMART_MONEY_SCENARIO_IDS.map((id) => ({
id,
label: id,
hiddenLabel:
id === "neutral_noise"
? "single_print_mid"
: SMART_MONEY_TEMPLATE_SCENARIOS[id as Exclude<(typeof SMART_MONEY_SCENARIO_IDS)[number], "neutral_noise">]
}));
export const buildSyntheticSmartMoneyBurstForTest = (
scenarioId: (typeof SMART_MONEY_SCENARIO_IDS)[number],
now: number
): Burst => {
const control = {
preset_id:
scenarioId === "event_driven"
? "event_day"
: scenarioId === "hedge_reactive"
? "dealer_day"
: scenarioId === "retail_whale"
? "retail_chase"
: "balanced_demo",
coverage_assist: true,
coverage_window_minutes: 20,
shared_seed: 11,
profile_weights: {
institutional_directional: 1.0,
retail_whale: 1.0,
event_driven: 1.0,
vol_seller: 1.0,
arbitrage: 1.0,
hedge_reactive: 1.0
},
updated_at: 0,
updated_by: "system"
} satisfies SyntheticControlState;
const mode: SyntheticMarketMode =
scenarioId === "retail_whale" || scenarioId === "neutral_noise"
? "realistic"
: "active";
const profile = SYNTHETIC_PROFILES[mode];
const coverageState = createCoverageWindowState();
const scenario =
scenarioId === "neutral_noise"
? profile.scenarios.find((candidate) => candidate.id === "single_print_mid")!
: profile.scenarios.find(
(candidate) => candidate.id === SMART_MONEY_TEMPLATE_SCENARIOS[
scenarioId as Exclude<(typeof SMART_MONEY_SCENARIO_IDS)[number], "neutral_noise">
]
)!;
return buildBurst(1, now, mode, profile, control, coverageState, scenario);
};
export const buildSyntheticFlowPacketForTest = (
scenarioId: (typeof SMART_MONEY_SCENARIO_IDS)[number],
now: number
): { packet: FlowPacket; hiddenLabel: string } => {
const burst = buildSyntheticSmartMoneyBurstForTest(scenarioId, now);
const primaryLeg = burst.legs[0]!;
const corporateEventOffset = Number(
burst.flowFeatures.corporate_event_ts_offset_days ?? 0
);
const totalSize = burst.legs.reduce((sum, leg) => sum + leg.baseSize * burst.cycles, 0);
const totalPremium = burst.legs.reduce(
(sum, leg) =>
sum + leg.basePrice * leg.baseSize * burst.cycles * OPTION_CONTRACT_MULTIPLIER,
0
);
const flowFeatures: FlowPacket["features"] = {
option_contract_id: primaryLeg.contractId,
underlying_id: primaryLeg.contractId.split("-")[0],
underlying_mid: burst.underlying,
count: burst.printCount,
window_ms: Math.max(0, (burst.printCount - 1) * 45),
total_size: totalSize,
total_premium: Number(totalPremium.toFixed(2)),
total_notional: Number(
(burst.underlying * totalSize * OPTION_CONTRACT_MULTIPLIER).toFixed(2)
),
first_price: primaryLeg.basePrice,
last_price: Number(
(
primaryLeg.basePrice *
(1 + burst.priceStep * Math.max(0, burst.cycles - 1))
).toFixed(2)
),
nbbo_missing_count: 0,
nbbo_stale_count: 0,
...burst.flowFeatures
};
delete flowFeatures.corporate_event_ts_offset_days;
if (corporateEventOffset > 0) {
flowFeatures.corporate_event_ts = now + corporateEventOffset * MS_PER_DAY;
}
if (scenarioId === "retail_whale") {
const replacementStrike = Math.round((burst.underlying * 1.08) / 5) * 5;
flowFeatures.option_contract_id = `${primaryLeg.contractId.split("-")[0]}-${formatExpiry(
now,
1
)}-${formatStrike(replacementStrike)}-C`;
flowFeatures.total_premium = Math.min(
Number(flowFeatures.total_premium ?? totalPremium),
72_000
);
flowFeatures.execution_iv_shock = Math.max(
Number(flowFeatures.execution_iv_shock ?? 0),
0.22
);
}
if (scenarioId === "event_driven") {
flowFeatures.count = 2;
flowFeatures.window_ms = 45;
flowFeatures.total_size = 620;
flowFeatures.total_premium = 24_000;
flowFeatures.nbbo_coverage_ratio = 0.38;
flowFeatures.nbbo_aggressive_ratio = 0.32;
flowFeatures.nbbo_aggressive_buy_ratio = 0.3;
flowFeatures.nbbo_aggressive_sell_ratio = 0.08;
flowFeatures.nbbo_inside_ratio = 0.28;
flowFeatures.nbbo_spread_z = 0.18;
flowFeatures.venue_count = 2;
flowFeatures.corporate_event_ts = now + 7 * MS_PER_DAY;
}
if (scenarioId === "vol_seller") {
flowFeatures.same_size_leg_symmetry = 0.58;
flowFeatures.nbbo_aggressive_ratio = 0.74;
flowFeatures.nbbo_aggressive_buy_ratio = 0.06;
flowFeatures.nbbo_aggressive_sell_ratio = 0.72;
flowFeatures.nbbo_inside_ratio = 0.08;
}
if (scenarioId === "arbitrage") {
flowFeatures.count = 4;
flowFeatures.window_ms = 90;
flowFeatures.total_size = 1800;
flowFeatures.total_premium = 30_000;
flowFeatures.nbbo_coverage_ratio = 0.72;
flowFeatures.nbbo_aggressive_ratio = 0.3;
flowFeatures.nbbo_aggressive_buy_ratio = 0.3;
flowFeatures.nbbo_aggressive_sell_ratio = 0.26;
flowFeatures.nbbo_inside_ratio = 0.42;
flowFeatures.same_size_leg_symmetry = 0.94;
}
if (scenarioId === "hedge_reactive") {
const replacementStrike = Math.round(burst.underlying / 5) * 5;
flowFeatures.option_contract_id = `${primaryLeg.contractId.split("-")[0]}-${formatExpiry(
now,
1
)}-${formatStrike(replacementStrike)}-P`;
flowFeatures.count = 2;
flowFeatures.window_ms = 45;
flowFeatures.total_size = 1600;
flowFeatures.total_premium = 18_000;
flowFeatures.nbbo_coverage_ratio = 0.7;
flowFeatures.underlying_move_bps = -96;
flowFeatures.nbbo_aggressive_ratio = 0.32;
flowFeatures.nbbo_aggressive_buy_ratio = 0.3;
flowFeatures.nbbo_aggressive_sell_ratio = 0.08;
flowFeatures.nbbo_inside_ratio = 0.2;
}
return {
hiddenLabel: burst.hiddenLabel,
packet: {
source_ts: now,
ingest_ts: now,
seq: SMART_MONEY_SCENARIO_IDS.indexOf(scenarioId) + 1,
trace_id: `synthetic-smart-money:${scenarioId}`,
id: `synthetic-smart-money:${scenarioId}:${now}`,
members: Array.from(
{ length: burst.printCount },
(_, index) =>
`${burst.legs[index % burst.legs.length]?.contractId ?? primaryLeg.contractId}:${index + 1}`
),
features: flowFeatures,
join_quality: {}
}
};
};
export const buildSyntheticBurstForTest = (
burstIndex: number,
now: number,
mode: SyntheticMarketMode
): Burst => {
const profile = SYNTHETIC_PROFILES[mode];
const control: SyntheticControlState = {
preset_id:
mode === "realistic" ? "balanced_demo" : mode === "active" ? "balanced_demo" : "dealer_day",
coverage_assist: true,
coverage_window_minutes: 20,
shared_seed: 11,
profile_weights: {
institutional_directional: 1.0,
retail_whale: 1.0,
event_driven: 1.0,
vol_seller: 1.0,
arbitrage: 1.0,
hedge_reactive: 1.0
},
updated_at: 0,
updated_by: "system"
};
const coverageState = createCoverageWindowState();
const cacheKey = `${mode}:${now}`;
const cached = burstSequenceCache.get(cacheKey) ?? [];
if (!burstSequenceCache.has(cacheKey)) {
burstSequenceCache.set(cacheKey, cached);
}
for (let index = 0; index < cached.length; index += 1) {
recordCoverageHit(coverageState, cached[index]!.label, now + (index + 1) * 1_000);
}
if (cached.length >= burstIndex) {
return cached[burstIndex - 1]!;
}
for (let index = cached.length + 1; index <= burstIndex; index += 1) {
const current = buildBurst(
index,
now + index * 1_000,
mode,
profile,
control,
coverageState
);
recordCoverageHit(coverageState, current.label, now + index * 1_000);
cached.push(current);
}
return cached[burstIndex - 1]!;
};
export const createSyntheticOptionsAdapter = (
config: SyntheticOptionsAdapterConfig
): OptionIngestAdapter => {
const profile = SYNTHETIC_PROFILES[config.mode];
return {
name: "synthetic",
start: (handlers: OptionIngestHandlers) => {
let seq = 0;
let nbboSeq = 0;
let burstIndex = 0;
let currentBurst: Burst | null = null;
let remainingRuns = 0;
let timer: ReturnType<typeof setInterval> | null = null;
let stopped = false;
const ivByContract = new Map<string, SyntheticContractIvState>();
const coverageState = createCoverageWindowState();
const emit = () => {
if (stopped) {
return;
}
const now = Date.now();
const control = config.getControl?.() ?? {
preset_id: "balanced_demo",
coverage_assist: true,
coverage_window_minutes: 20,
shared_seed: 11,
profile_weights: {
institutional_directional: 1.0,
retail_whale: 1.0,
event_driven: 1.0,
vol_seller: 1.0,
arbitrage: 1.0,
hedge_reactive: 1.0
},
updated_at: 0,
updated_by: "system"
};
if (!currentBurst || remainingRuns <= 0) {
burstIndex += 1;
currentBurst = buildBurst(
burstIndex,
now,
config.mode,
profile,
control,
coverageState
);
recordCoverageHit(coverageState, currentBurst.label, now);
remainingRuns = pickInt(
profile.burstRunRange[0],
profile.burstRunRange[1],
burstIndex * 23
);
}
const burst = currentBurst;
const session = getSyntheticSessionState(now, control);
const underlyingState = getSyntheticUnderlyingState(
burst.contractId.split("-")[0]!,
now,
control,
session
);
for (let i = 0; i < burst.printCount; i += 1) {
const leg = burst.legs[i % burst.legs.length]!;
const legCycle = Math.floor(i / burst.legs.length);
const eventTs = now + i * 5;
const priceJitter = ((i % 3) - 1) * 0.004;
const sizeJitter = ((i % 3) - 1) * 0.08;
const priceMultiplier = 1 + burst.priceStep * legCycle + priceJitter;
const placement = pickPlacement(burst, i);
const size = Math.max(1, Math.round(leg.baseSize * (1 + sizeJitter)));
const previousIv = ivByContract.get(leg.contractId);
const provisionalNotional = leg.basePrice * size * OPTION_CONTRACT_MULTIPLIER;
const ivState = updateSyntheticIvForTest(previousIv, {
ts: eventTs,
placement,
size,
notional: provisionalNotional,
dteDays: leg.expiryOffsetDays,
moneyness: leg.strike / burst.underlying
});
ivByContract.set(leg.contractId, ivState);
const ivDrift = Math.max(
0,
ivState.iv - initializeSyntheticIv(leg.expiryOffsetDays, leg.strike / burst.underlying)
);
const mid = Math.max(
0.05,
Number((leg.basePrice * priceMultiplier * (1 + ivDrift * 1.15)).toFixed(2))
);
const spread = Math.max(
0.02,
Number(
(
mid *
(0.018 +
Math.min(0.04, ivState.iv * 0.01) +
underlyingState.sessionVolatility * 0.01 +
(1 - underlyingState.quoteCleanliness) * 0.006)
).toFixed(2)
)
);
const bid = Math.max(0.01, Number((mid - spread / 2).toFixed(2)));
const ask = Math.max(bid + 0.01, Number((mid + spread / 2).toFixed(2)));
const tick = Math.max(0.01, Number((spread * 0.25).toFixed(2)));
const tradePrice =
placement === "AA"
? ask + tick
: placement === "A"
? ask
: placement === "BB"
? Math.max(0.01, bid - tick)
: placement === "B"
? bid
: mid;
seq += 1;
const print: OptionPrint = {
source_ts: eventTs,
ingest_ts: eventTs,
seq,
trace_id: `synthetic-options-${seq}`,
ts: eventTs,
option_contract_id: leg.contractId,
price: tradePrice,
size,
exchange: leg.exchange,
conditions: burst.conditions,
execution_iv: ivState.iv,
execution_iv_source: "synthetic_pressure_model",
execution_underlying_mid: burst.underlying
};
const quoteSeed = Math.abs(burst.seed + i * 17) % 1000;
const missingQuote = quoteSeed / 1000 < burst.missingQuoteProbability;
const staleQuote =
!missingQuote &&
((quoteSeed + 233) % 1000) / 1000 < burst.staleQuoteProbability;
if (handlers.onNBBO && !missingQuote) {
nbboSeq += 1;
const sizeBase = Math.max(1, Math.round(leg.baseSize * 0.4));
const bidSize = Math.max(1, Math.round(sizeBase * (1 + sizeJitter)));
const askSize = Math.max(1, Math.round(sizeBase * (1 - sizeJitter)));
const quoteTs = staleQuote ? eventTs - 2_000 : eventTs;
const nbbo: OptionNBBO = {
source_ts: quoteTs,
ingest_ts: quoteTs,
seq: nbboSeq,
trace_id: `synthetic-nbbo-${nbboSeq}`,
ts: quoteTs,
option_contract_id: leg.contractId,
bid,
ask,
bidSize,
askSize
};
void handlers.onNBBO(nbbo);
}
void handlers.onTrade(print);
}
remainingRuns -= 1;
};
timer = setInterval(emit, config.emitIntervalMs);
return () => {
stopped = true;
if (timer) {
clearInterval(timer);
}
};
}
};
};