Add NBBO persistence, API/WS streaming, and UI context

This commit is contained in:
dirtydishes 2025-12-30 12:47:58 -05:00
parent 15fce370ef
commit fc7065792f
12 changed files with 768 additions and 46 deletions

View file

@ -4,6 +4,7 @@ import {
ClassifierHitEventSchema,
EquityPrintSchema,
FlowPacketSchema,
OptionNBBOSchema,
OptionPrintSchema
} from "@islandflow/types";
import type {
@ -11,6 +12,7 @@ import type {
ClassifierHitEvent,
EquityPrint,
FlowPacket,
OptionNBBO,
OptionPrint
} from "@islandflow/types";
import {
@ -18,6 +20,7 @@ import {
optionPrintsTableDDL,
OPTION_PRINTS_TABLE
} from "./option-prints";
import { normalizeOptionNBBO, optionNBBOTableDDL, OPTION_NBBO_TABLE } from "./option-nbbo";
import {
equityPrintsTableDDL,
EQUITY_PRINTS_TABLE,
@ -69,6 +72,14 @@ export const ensureOptionPrintsTable = async (
});
};
export const ensureOptionNBBOTable = async (
client: ClickHouseClient
): Promise<void> => {
await client.exec({
query: optionNBBOTableDDL()
});
};
export const ensureEquityPrintsTable = async (
client: ClickHouseClient
): Promise<void> => {
@ -111,6 +122,18 @@ export const insertOptionPrint = async (
});
};
export const insertOptionNBBO = async (
client: ClickHouseClient,
nbbo: OptionNBBO
): Promise<void> => {
const record = normalizeOptionNBBO(nbbo);
await client.insert({
table: OPTION_NBBO_TABLE,
values: [record],
format: "JSONEachRow"
});
};
export const insertEquityPrint = async (
client: ClickHouseClient,
print: EquityPrint
@ -213,6 +236,24 @@ const normalizeOptionRow = (row: unknown): unknown => {
return row;
};
const normalizeOptionNbboRow = (row: unknown): unknown => {
if (row && typeof row === "object") {
return normalizeNumericFields(row as Record<string, unknown>, [
"source_ts",
"ingest_ts",
"seq",
"ts",
"bid",
"ask",
"bidSize",
"askSize"
]);
}
return row;
};
const normalizeEquityRow = (row: unknown): unknown => {
if (row && typeof row === "object") {
const record = normalizeNumericFields(row as Record<string, unknown>, [
@ -307,6 +348,20 @@ export const fetchRecentOptionPrints = async (
return OptionPrintSchema.array().parse(rows.map(normalizeOptionRow));
};
export const fetchRecentOptionNBBO = async (
client: ClickHouseClient,
limit: number
): Promise<OptionNBBO[]> => {
const safeLimit = clampLimit(limit);
const result = await client.query({
query: `SELECT * FROM ${OPTION_NBBO_TABLE} ORDER BY ts DESC, seq DESC LIMIT ${safeLimit}`,
format: "JSONEachRow"
});
const rows = await result.json<unknown[]>();
return OptionNBBOSchema.array().parse(rows.map(normalizeOptionNbboRow));
};
export const fetchRecentEquityPrints = async (
client: ClickHouseClient,
limit: number
@ -394,6 +449,25 @@ export const fetchOptionPrintsAfter = async (
return OptionPrintSchema.array().parse(rows.map(normalizeOptionRow));
};
export const fetchOptionNBBOAfter = async (
client: ClickHouseClient,
afterTs: number,
afterSeq: number,
limit: number
): Promise<OptionNBBO[]> => {
const safeLimit = clampLimit(limit);
const safeAfterTs = clampCursor(afterTs);
const safeAfterSeq = clampCursor(afterSeq);
const result = await client.query({
query: `SELECT * FROM ${OPTION_NBBO_TABLE} WHERE (ts, seq) > (${safeAfterTs}, ${safeAfterSeq}) ORDER BY ts ASC, seq ASC LIMIT ${safeLimit}`,
format: "JSONEachRow"
});
const rows = await result.json<unknown[]>();
return OptionNBBOSchema.array().parse(rows.map(normalizeOptionNbboRow));
};
export const fetchEquityPrintsAfter = async (
client: ClickHouseClient,
afterTs: number,

View file

@ -4,3 +4,4 @@ export * from "./alerts";
export * from "./flow-packets";
export * from "./equity-prints";
export * from "./option-prints";
export * from "./option-nbbo";

View file

@ -0,0 +1,26 @@
import type { OptionNBBO } from "@islandflow/types";
export const OPTION_NBBO_TABLE = "option_nbbo";
export const optionNBBOTableDDL = (): string => {
return `
CREATE TABLE IF NOT EXISTS ${OPTION_NBBO_TABLE} (
source_ts UInt64,
ingest_ts UInt64,
seq UInt64,
trace_id String,
ts UInt64,
option_contract_id String,
bid Float64,
ask Float64,
bidSize UInt32,
askSize UInt32
)
ENGINE = MergeTree
ORDER BY (ts, option_contract_id)
`;
};
export const normalizeOptionNBBO = (nbbo: OptionNBBO): OptionNBBO => {
return nbbo;
};