Fix contract-focused options tape hydration

This commit is contained in:
dirtydishes 2026-05-07 23:37:32 -04:00
parent 73e25ddf70
commit b73e62bdba
8 changed files with 657 additions and 171 deletions

View file

@ -4,19 +4,23 @@ import {
NAV_ITEMS,
appendHistoryTail,
buildDefaultFlowFilters,
buildOptionTapeQueryParams,
classifierToneForFamily,
composeTapeItems,
deriveAlertDirection,
countActiveFlowFilterGroups,
filterOptionTapeItems,
findAnchorRestoreIndex,
formatCompactUsd,
formatOptionContractLabel,
flushPausableTapeData,
getEffectiveOptionPrintFilters,
getAlertWindowAnchorTs,
getHotChannelFeedStatus,
getScopedLiveAutoHydrationChannels,
getLiveHistoryRetentionCap,
getOptionTableSnapshot,
getOptionScope,
getLiveFeedStatus,
getLiveManifest,
getRouteFeatures,
@ -30,6 +34,7 @@ import {
shouldIncludeEquitiesForDarkUnderlyingFallback,
shouldShowEquitiesSilentFeedWarning,
selectPrimaryClassifierHit,
shouldClearOptionFocusSeed,
smartMoneyProfileLabel,
smartMoneyToneForProfile,
statusLabel,
@ -42,6 +47,25 @@ const makeItem = (traceId: string, seq: number, ts: number) => ({
ts
});
const makeOptionPrint = (overrides: Record<string, unknown> = {}) =>
({
trace_id: "opt-1",
seq: 1,
ts: 1_000,
source_ts: 1_000,
ingest_ts: 1_001,
option_contract_id: "AAPL-2025-01-17-200-C",
underlying_id: "AAPL",
option_type: "call",
nbbo_side: "A",
notional: 250_000,
signal_pass: true,
price: 1,
size: 10,
exchange: "X",
...overrides
}) as any;
const makeAlert = (overrides: Record<string, unknown> = {}) =>
({
trace_id: "alert-1",
@ -125,6 +149,31 @@ describe("live manifest", () => {
expect(equitiesSubscription?.underlying_ids).toEqual(["AAPL"]);
});
it("drops option-print filters for contract-focused options subscriptions but keeps flow filters", () => {
const filters = {
...buildDefaultFlowFilters(),
minNotional: 500_000,
optionTypes: ["put"] as const
};
const manifest = getLiveManifest(
"/tape",
"AAPL",
60000,
filters,
{
underlying_ids: ["AAPL"],
option_contract_id: "AAPL-2025-01-17-200-C"
},
{ underlying_ids: ["AAPL"] },
undefined
);
const optionsSubscription = manifest.find((subscription) => subscription.channel === "options");
const flowSubscription = manifest.find((subscription) => subscription.channel === "flow");
expect(optionsSubscription?.filters).toBeUndefined();
expect(flowSubscription?.filters).toBe(filters);
});
it("scopes /signals subscriptions to signals channels only", () => {
const channels = getLiveManifest("/signals", "SPY", 60000, buildDefaultFlowFilters()).map(
(subscription) => subscription.channel
@ -154,6 +203,130 @@ describe("live manifest", () => {
});
});
describe("contract-focused option helpers", () => {
it("uses the focused contract underlying for option scope even when ticker input differs", () => {
expect(
getOptionScope(["MSFT"], "AAPL", {
kind: "option-contract",
contractId: "AAPL-2025-01-17-200-C",
underlyingId: "AAPL"
})
).toEqual({
underlying_ids: ["AAPL"],
option_contract_id: "AAPL-2025-01-17-200-C"
});
});
it("ignores broad flow filters for focused contract options", () => {
const filters = {
...buildDefaultFlowFilters(),
minNotional: 500_000
};
const items = [
makeOptionPrint({
trace_id: "focused-low",
option_contract_id: "AAPL-2025-01-17-200-C",
notional: 100_000,
signal_pass: false
}),
makeOptionPrint({
trace_id: "focused-high",
seq: 2,
ts: 2_000,
option_contract_id: "AAPL-2025-01-17-200-C",
notional: 750_000
}),
makeOptionPrint({
trace_id: "other-contract",
seq: 3,
ts: 3_000,
option_contract_id: "MSFT-2025-01-17-300-C",
underlying_id: "MSFT",
notional: 900_000
})
];
expect(
filterOptionTapeItems(
items,
getEffectiveOptionPrintFilters(filters, true),
{
kind: "option-contract",
contractId: "AAPL-2025-01-17-200-C",
underlyingId: "AAPL"
},
new Set(["MSFT"]),
"AAPL"
).map((item) => item.trace_id)
).toEqual(["focused-low", "focused-high"]);
});
it("includes option_contract_id and drops broad filters in focused replay query params", () => {
const filters = {
...buildDefaultFlowFilters(),
minNotional: 500_000,
optionTypes: ["put"] as const
};
expect(
buildOptionTapeQueryParams(getEffectiveOptionPrintFilters(filters, true), {
underlying_ids: ["AAPL"],
option_contract_id: "AAPL-2025-01-17-200-C"
})
).toEqual({
underlying_ids: "AAPL",
option_contract_id: "AAPL-2025-01-17-200-C"
});
});
it("keeps the focus seed until the matching scoped subscription has loaded it", () => {
const seedItem = makeOptionPrint({
trace_id: "focused-seed",
option_contract_id: "AAPL-2025-01-17-200-C"
});
const seed = {
scopeKey: "option-contract:AAPL-2025-01-17-200-C",
subscriptionKey: getLiveSubscriptionKey({
channel: "options",
underlying_ids: ["AAPL"],
option_contract_id: "AAPL-2025-01-17-200-C"
}),
items: [seedItem]
};
expect(
shouldClearOptionFocusSeed(
seed,
"option-contract:AAPL-2025-01-17-200-C",
getLiveSubscriptionKey({
channel: "options",
filters: {
...buildDefaultFlowFilters(),
minNotional: 500_000
},
underlying_ids: ["AAPL"]
}),
[makeOptionPrint({ trace_id: "broad-old" })],
[]
)
).toBe(false);
expect(
shouldClearOptionFocusSeed(
seed,
"option-contract:AAPL-2025-01-17-200-C",
getLiveSubscriptionKey({
channel: "options",
underlying_ids: ["AAPL"],
option_contract_id: "AAPL-2025-01-17-200-C"
}),
[seedItem],
[]
)
).toBe(true);
});
});
describe("route feature map", () => {
it("maps /tape to tape panes and dependencies", () => {
const features = getRouteFeatures("/tape");

View file

@ -350,9 +350,17 @@ type SelectedInstrument =
type TapeFocusSeed<T> = {
scopeKey: string;
subscriptionKey?: string;
items: T[];
};
type OptionScope = Pick<
Extract<LiveSubscription, { channel: "options" }>,
"underlying_ids" | "option_contract_id"
>;
type EquityScope = Pick<Extract<LiveSubscription, { channel: "equities" }>, "underlying_ids">;
const formatIntervalLabel = (intervalMs: number): string => {
const match = CANDLE_INTERVALS.find((interval) => interval.ms === intervalMs);
if (match) {
@ -1956,6 +1964,13 @@ const useTape = <T extends SortableItem & { seq: number }>(
const replaySourceKey = config.replaySourceKey ?? null;
const onReplaySourceKey = config.onReplaySourceKey;
const queryParams = config.queryParams;
const queryKey = useMemo(
() =>
JSON.stringify(
Object.entries(queryParams ?? {}).sort(([left], [right]) => left.localeCompare(right))
),
[queryParams]
);
const hotWindowLimit = config.hotWindowLimit ?? LIVE_HOT_WINDOW;
const [status, setStatus] = useState<WsStatus>("connecting");
const [items, setItems] = useState<T[]>([]);
@ -2046,7 +2061,7 @@ const useTape = <T extends SortableItem & { seq: number }>(
pendingRef.current = [];
pendingCountRef.current = 0;
cancelFlush();
}, [mode, replaySourceKey, cancelFlush]);
}, [mode, replaySourceKey, queryKey, cancelFlush]);
useEffect(() => {
if (mode !== "replay" || !latestPath) {
@ -2091,7 +2106,7 @@ const useTape = <T extends SortableItem & { seq: number }>(
return () => {
active = false;
};
}, [mode, latestPath, getItemTs, replaySourceKey, queryParams]);
}, [mode, latestPath, getItemTs, replaySourceKey, queryKey, queryParams]);
useEffect(() => {
if (mode !== "live" || config.liveEnabled === false) {
@ -2242,9 +2257,14 @@ const useTape = <T extends SortableItem & { seq: number }>(
}
}
if (onReplaySourceKey && sourcePrefix && replaySourceNotifiedRef.current !== sourcePrefix) {
replaySourceNotifiedRef.current = sourcePrefix;
onReplaySourceKey(sourcePrefix);
if (onReplaySourceKey) {
if (sourcePrefix && replaySourceNotifiedRef.current !== sourcePrefix) {
replaySourceNotifiedRef.current = sourcePrefix;
onReplaySourceKey(sourcePrefix);
} else if (!sourcePrefix && replaySourceNotifiedRef.current !== null) {
replaySourceNotifiedRef.current = null;
onReplaySourceKey(null);
}
}
const filtered = sourcePrefix
@ -2330,6 +2350,7 @@ const useTape = <T extends SortableItem & { seq: number }>(
getReplayKey,
replaySourceKey,
onReplaySourceKey,
queryKey,
queryParams
]);
@ -2784,6 +2805,99 @@ const appendOptionFlowFilters = (params: URLSearchParams, filters: OptionFlowFil
}
};
const appendOptionScopeParams = (
params: URLSearchParams,
optionScope: OptionScope | undefined
): void => {
if (optionScope?.underlying_ids?.length) {
params.set("underlying_ids", optionScope.underlying_ids.join(","));
}
if (optionScope?.option_contract_id) {
params.set("option_contract_id", optionScope.option_contract_id);
}
};
export const getEffectiveOptionPrintFilters = (
flowFilters: OptionFlowFilters,
isOptionContractFocused: boolean
): OptionFlowFilters | undefined => {
return isOptionContractFocused ? undefined : flowFilters;
};
export const getOptionScope = (
activeTickers: string[],
instrumentUnderlying: string | null,
selectedInstrument: SelectedInstrument
): OptionScope => ({
underlying_ids:
selectedInstrument?.kind === "option-contract"
? instrumentUnderlying
? [instrumentUnderlying]
: undefined
: activeTickers.length > 0
? activeTickers
: instrumentUnderlying
? [instrumentUnderlying]
: undefined,
option_contract_id:
selectedInstrument?.kind === "option-contract" ? selectedInstrument.contractId : undefined
});
export const buildOptionTapeQueryParams = (
filters: OptionFlowFilters | undefined,
optionScope: OptionScope | undefined
): Record<string, string | undefined> => {
const params = new URLSearchParams();
appendOptionFlowFilters(params, filters);
appendOptionScopeParams(params, optionScope);
return Object.fromEntries(params.entries());
};
export const filterOptionTapeItems = (
items: OptionPrint[],
filters: OptionFlowFilters | undefined,
selectedInstrument: SelectedInstrument,
tickerSet: Set<string>,
instrumentUnderlying: string | null
): OptionPrint[] => {
return items.filter((print) => {
const contractId = normalizeContractId(print.option_contract_id);
if (selectedInstrument?.kind === "option-contract") {
return contractId === selectedInstrument.contractId;
}
if (!matchesOptionPrintFilters(print, filters)) {
return false;
}
const underlying = extractUnderlying(contractId);
if (tickerSet.size === 0) {
return !instrumentUnderlying || underlying === instrumentUnderlying;
}
return Boolean(underlying) && tickerSet.has(underlying.toUpperCase());
});
};
export const shouldClearOptionFocusSeed = (
seed: TapeFocusSeed<OptionPrint> | null,
optionFocusScopeKey: string | null,
currentOptionSubscriptionKey: string | null,
liveItems: OptionPrint[],
historyItems: OptionPrint[]
): boolean => {
if (!seed) {
return false;
}
if (seed.scopeKey !== optionFocusScopeKey) {
return true;
}
if (seed.subscriptionKey && seed.subscriptionKey !== currentOptionSubscriptionKey) {
return false;
}
const liveKeys = new Set(
composeTapeItems([], liveItems, historyItems).map((item) => getTapeItemKey(item))
);
return seed.items.every((item) => liveKeys.has(getTapeItemKey(item)));
};
const appendLiveScopeParams = (params: URLSearchParams, subscription: LiveSubscription): void => {
if ((subscription.channel === "options" || subscription.channel === "equities") && subscription.underlying_ids?.length) {
params.set("underlying_ids", subscription.underlying_ids.join(","));
@ -2810,8 +2924,9 @@ export const getLiveManifest = (
chartTicker: string,
chartIntervalMs: number,
flowFilters: OptionFlowFilters,
optionScope?: Pick<Extract<LiveSubscription, { channel: "options" }>, "underlying_ids" | "option_contract_id">,
equityScope?: Pick<Extract<LiveSubscription, { channel: "equities" }>, "underlying_ids">
optionScope?: OptionScope,
equityScope?: EquityScope,
optionPrintFilters?: OptionFlowFilters
): LiveSubscription[] => {
const features = getRouteFeatures(pathname);
const subscriptions: LiveSubscription[] = [];
@ -2819,7 +2934,10 @@ export const getLiveManifest = (
if (features.options) {
subscriptions.push({
channel: "options",
filters: flowFilters,
filters:
optionScope?.option_contract_id && optionPrintFilters === undefined
? undefined
: optionPrintFilters ?? flowFilters,
...optionScope,
snapshot_limit: LIVE_HOT_WINDOW_OPTIONS
});
@ -2868,11 +2986,7 @@ export const getLiveManifest = (
const useLiveSession = (
enabled: boolean,
pathname: string,
chartTicker: string,
chartIntervalMs: number,
flowFilters: OptionFlowFilters,
optionScope?: Pick<Extract<LiveSubscription, { channel: "options" }>, "underlying_ids" | "option_contract_id">,
equityScope?: Pick<Extract<LiveSubscription, { channel: "equities" }>, "underlying_ids">
manifest: LiveSubscription[]
): LiveSessionState => {
const [status, setStatus] = useState<WsStatus>(enabled ? "connecting" : "disconnected");
const [connectedAt, setConnectedAt] = useState<number | null>(null);
@ -2938,11 +3052,6 @@ const useLiveSession = (
const lastEventAtRef = useRef<number | null>(null);
const subscribedKeysRef = useRef<Set<string>>(new Set());
const subscribedMapRef = useRef<Map<string, LiveSubscription>>(new Map());
const manifest = useMemo(
() => getLiveManifest(pathname, chartTicker.toUpperCase(), chartIntervalMs, flowFilters, optionScope, equityScope),
[pathname, chartTicker, chartIntervalMs, flowFilters, optionScope, equityScope]
);
const replaceArrayState = <T,>(
setter: Dispatch<SetStateAction<T[]>>,
ref: { current: T[] },
@ -4857,20 +4966,21 @@ const useTerminalState = () => {
}, [filterInput]);
const tickerSet = useMemo(() => new Set(activeTickers), [activeTickers]);
const instrumentUnderlying = selectedInstrument?.underlyingId.toUpperCase() ?? null;
const isOptionContractFocused = selectedInstrument?.kind === "option-contract";
const focusedOptionContractId =
selectedInstrument?.kind === "option-contract" ? selectedInstrument.contractId : null;
const optionFocusScopeKey =
selectedInstrument?.kind === "option-contract"
? `option-contract:${selectedInstrument.contractId}`
: null;
focusedOptionContractId ? `option-contract:${focusedOptionContractId}` : null;
const equityFocusScopeKey =
selectedInstrument?.kind === "equity"
? `equity:${selectedInstrument.underlyingId.toUpperCase()}`
: null;
const effectiveOptionPrintFilters = useMemo(
() => getEffectiveOptionPrintFilters(flowFilters, isOptionContractFocused),
[flowFilters, isOptionContractFocused]
);
const optionScope = useMemo(
() => ({
underlying_ids: activeTickers.length > 0 ? activeTickers : instrumentUnderlying ? [instrumentUnderlying] : undefined,
option_contract_id:
selectedInstrument?.kind === "option-contract" ? selectedInstrument.contractId : undefined
}),
() => getOptionScope(activeTickers, instrumentUnderlying, selectedInstrument),
[activeTickers, instrumentUnderlying, selectedInstrument]
);
const equityScope = useMemo(
@ -4895,14 +5005,39 @@ const useTerminalState = () => {
? `Contract: ${display.ticker} ${display.expiration} ${display.strike}`
: `Contract: ${selectedInstrument.contractId}`;
}, [selectedInstrument]);
const liveSession = useLiveSession(
mode === "live",
pathname,
chartTicker,
chartIntervalMs,
flowFilters,
optionScope,
equityScope
const liveManifest = useMemo(
() =>
getLiveManifest(
pathname,
chartTicker.toUpperCase(),
chartIntervalMs,
flowFilters,
optionScope,
equityScope,
effectiveOptionPrintFilters
),
[
pathname,
chartTicker,
chartIntervalMs,
flowFilters,
optionScope,
equityScope,
effectiveOptionPrintFilters
]
);
const liveSession = useLiveSession(mode === "live", pathname, liveManifest);
const currentOptionSubscription = useMemo(
() =>
liveManifest.find(
(subscription): subscription is Extract<LiveSubscription, { channel: "options" }> =>
subscription.channel === "options"
) ?? null,
[liveManifest]
);
const currentOptionSubscriptionKey = useMemo(
() => (currentOptionSubscription ? getLiveSubscriptionKey(currentOptionSubscription) : null),
[currentOptionSubscription]
);
const equitiesLiveSubscriptionActive = routeFeatures.equities;
@ -4966,18 +5101,8 @@ const useTerminalState = () => {
);
const disableReplayGrouping = useCallback(() => null, []);
const optionQueryParams = useMemo<Record<string, string | undefined>>(
() => ({
view: flowFilters.view ?? "signal",
security:
flowFilters.securityTypes?.length === 1 ? flowFilters.securityTypes[0] : undefined,
side: flowFilters.nbboSides?.length ? flowFilters.nbboSides.join(",") : undefined,
type: flowFilters.optionTypes?.length ? flowFilters.optionTypes.join(",") : undefined,
min_notional:
typeof flowFilters.minNotional === "number"
? String(flowFilters.minNotional)
: undefined
}),
[flowFilters]
() => buildOptionTapeQueryParams(effectiveOptionPrintFilters, optionScope),
[effectiveOptionPrintFilters, optionScope]
);
const options = useTape<OptionPrint>({
@ -4992,9 +5117,10 @@ const useTerminalState = () => {
pollMs: mode === "replay" ? 200 : undefined,
captureScroll: optionsAnchor.capture,
onNewItems: optionsScroll.onNewItems,
getReplayKey: extractReplaySource,
onReplaySourceKey: handleReplaySource,
queryParams: optionQueryParams
getReplayKey: isOptionContractFocused ? disableReplayGrouping : extractReplaySource,
onReplaySourceKey: isOptionContractFocused ? undefined : handleReplaySource,
queryParams: optionQueryParams,
replaySourceKey: isOptionContractFocused ? null : replaySource
});
const equities = useTape<EquityPrint>({
@ -5010,6 +5136,12 @@ const useTerminalState = () => {
onNewItems: equitiesScroll.onNewItems
});
useEffect(() => {
if (isOptionContractFocused && replaySource !== null) {
setReplaySource(null);
}
}, [isOptionContractFocused, replaySource]);
const equityJoins = useTape<EquityPrintJoin>({
mode,
liveEnabled: false,
@ -5922,25 +6054,20 @@ const useTerminalState = () => {
);
const filteredOptions = useMemo(() => {
return optionsFeed.items.filter((print) => {
if (!matchesOptionPrintFilters(print, flowFilters)) {
return false;
}
if (
selectedInstrument?.kind === "option-contract" &&
normalizeContractId(print.option_contract_id) !== selectedInstrument.contractId
) {
return false;
}
if (tickerSet.size === 0) {
return (
!instrumentUnderlying ||
extractUnderlying(normalizeContractId(print.option_contract_id)) === instrumentUnderlying
);
}
return matchesTicker(extractUnderlying(normalizeContractId(print.option_contract_id)));
});
}, [flowFilters, optionsFeed.items, matchesTicker, tickerSet, selectedInstrument, instrumentUnderlying]);
return filterOptionTapeItems(
optionsFeed.items,
effectiveOptionPrintFilters,
selectedInstrument,
tickerSet,
instrumentUnderlying
);
}, [
effectiveOptionPrintFilters,
instrumentUnderlying,
optionsFeed.items,
selectedInstrument,
tickerSet
]);
const filteredEquities = useMemo(() => {
if (tickerSet.size === 0) {
@ -5956,16 +6083,24 @@ const useTerminalState = () => {
if (!optionFocusSeed) {
return;
}
if (optionFocusSeed.scopeKey !== optionFocusScopeKey) {
setOptionFocusSeed(null);
return;
}
const composedBaseItems = composeTapeItems([], liveOptions.liveItems ?? [], liveOptions.historyItems ?? []);
const liveKeys = new Set(composedBaseItems.map((item) => getTapeItemKey(item)));
if (optionFocusSeed.items.every((item) => liveKeys.has(getTapeItemKey(item)))) {
if (
shouldClearOptionFocusSeed(
optionFocusSeed,
optionFocusScopeKey,
currentOptionSubscriptionKey,
liveOptions.liveItems ?? [],
liveOptions.historyItems ?? []
)
) {
setOptionFocusSeed(null);
}
}, [liveOptions.historyItems, liveOptions.liveItems, optionFocusScopeKey, optionFocusSeed]);
}, [
currentOptionSubscriptionKey,
liveOptions.historyItems,
liveOptions.liveItems,
optionFocusScopeKey,
optionFocusSeed
]);
useEffect(() => {
if (!equityFocusSeed) {
@ -5988,15 +6123,21 @@ const useTerminalState = () => {
const parsed = parseOptionContractId(contractId);
const underlyingId = (print.underlying_id ?? parsed?.root ?? extractUnderlying(contractId)).toUpperCase();
const scopeKey = `option-contract:${contractId}`;
const subscriptionKey = getLiveSubscriptionKey({
channel: "options",
underlying_ids: [underlyingId],
option_contract_id: contractId
});
const seedItems = composeTapeItems(
[print],
filteredOptions.filter((candidate) => normalizeContractId(candidate.option_contract_id) === contractId),
[]
);
setOptionFocusSeed({ scopeKey, items: seedItems });
setOptionFocusSeed({ scopeKey, subscriptionKey, items: seedItems });
bumpTapeDebugMetric("focusSeedRowCount", seedItems.length);
logTapeDebug("option focus seed captured", {
contract_id: contractId,
subscription_key: subscriptionKey,
row_count: seedItems.length
});
setSelectedInstrument({