tradeblocks-mcp 3.0.2 → 3.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +82 -68
- package/dist/{chunk-LDKTV7GW.js → chunk-27S67XW3.js} +24 -52
- package/dist/chunk-27S67XW3.js.map +1 -0
- package/dist/{chunk-FGZH632F.js → chunk-2E63THNI.js} +13 -11
- package/dist/chunk-2E63THNI.js.map +1 -0
- package/dist/{chunk-BKQ4PM4Y.js → chunk-6S37CXUA.js} +11 -13
- package/dist/chunk-6S37CXUA.js.map +1 -0
- package/dist/{chunk-PRAYH3RT.js → chunk-AP7IHUUR.js} +29 -12
- package/dist/chunk-AP7IHUUR.js.map +1 -0
- package/dist/{chunk-QTTR7AAW.js → chunk-C6LL746C.js} +67 -84
- package/dist/{chunk-QTTR7AAW.js.map → chunk-C6LL746C.js.map} +1 -1
- package/dist/{chunk-W2PP3LEH.js → chunk-SEUZYQGQ.js} +17 -8
- package/dist/chunk-SEUZYQGQ.js.map +1 -0
- package/dist/{chunk-4BLCXNQ6.js → chunk-UBUC5A66.js} +105 -114
- package/dist/chunk-UBUC5A66.js.map +1 -0
- package/dist/{chunk-XXYOUIZY.js → chunk-VDU25Z6X.js} +63 -80
- package/dist/chunk-VDU25Z6X.js.map +1 -0
- package/dist/daily-log-processor-BY3ISY6K.js +9 -0
- package/dist/{daily-logs-store-D3QC2XPV.js → daily-logs-store-3C3OTGPK.js} +2 -2
- package/dist/iv-solver-worker.js +9 -1
- package/dist/market-provider-H3ARIVZ4.js +16 -0
- package/dist/{proto-3HZTCWK4.js → proto-V23GIZG2.js} +2 -2
- package/dist/{sync-PO4IPCYV.js → sync-EVLKB3ZJ.js} +7 -7
- package/dist/test-exports.js +358 -254
- package/dist/test-exports.js.map +1 -1
- package/dist/trade-processor-EYA3I3XB.js +9 -0
- package/dist/{trades-store-LOL3FQSF.js → trades-store-R3NL25T2.js} +2 -2
- package/manifest.json +3 -11
- package/package.json +1 -1
- package/server/{chunk-NZO6PT64.js → chunk-4P7D7YZP.js} +102 -113
- package/server/chunk-4P7D7YZP.js.map +1 -0
- package/server/{chunk-72GKJE2U.js → chunk-BOPHW5M6.js} +521 -235
- package/server/chunk-BOPHW5M6.js.map +1 -0
- package/server/{chunk-WA5AAPCH.js → chunk-GH2552SE.js} +17 -8
- package/server/chunk-GH2552SE.js.map +1 -0
- package/server/{chunk-5EBXHT6C.js → chunk-OBYKFW2B.js} +11 -13
- package/server/chunk-OBYKFW2B.js.map +1 -0
- package/server/{chunk-FBNDMCT5.js → chunk-T66KH2XH.js} +13 -11
- package/server/chunk-T66KH2XH.js.map +1 -0
- package/server/{chunk-NRFXAJF7.js → chunk-W5E7FHC4.js} +24 -52
- package/server/chunk-W5E7FHC4.js.map +1 -0
- package/server/{chunk-CSDVJPBB.js → chunk-YUCOAJ4Z.js} +29 -12
- package/server/chunk-YUCOAJ4Z.js.map +1 -0
- package/server/{chunk-PNKG7RY7.js → chunk-ZBJCF4ZG.js} +63 -80
- package/server/chunk-ZBJCF4ZG.js.map +1 -0
- package/server/{config-6IZXEFEX.js → config-DK7KOMNL.js} +1 -1
- package/server/config-DK7KOMNL.js.map +1 -0
- package/server/daily-log-processor-ENEUT22A.js +10 -0
- package/server/{daily-logs-store-NU7M4L5C.js → daily-logs-store-YFNXTB7J.js} +2 -2
- package/server/http-server.js +2 -5
- package/server/index.js +1771 -1538
- package/server/index.js.map +1 -1
- package/server/iv-solver-worker.js +9 -1
- package/server/market-provider-B437HKLW.js +17 -0
- package/server/{proto-DWRZJO4E.js → proto-YZ4TLX2N.js} +2 -2
- package/server/{sync-QFI5L7S7.js → sync-V25UQJA3.js} +7 -7
- package/server/trade-processor-L3PIQ5TG.js +10 -0
- package/server/{trades-store-VY26MWER.js → trades-store-RTTEYMXH.js} +2 -2
- package/src/auth/clients-store.ts +4 -4
- package/src/auth/code-store.ts +1 -1
- package/src/auth/config.ts +11 -12
- package/src/auth/login-page.ts +8 -10
- package/src/auth/provider.ts +35 -30
- package/src/auth/token.ts +17 -15
- package/src/db/backtest-schemas.ts +12 -4
- package/src/db/connection.ts +107 -40
- package/src/db/index.ts +37 -5
- package/src/db/json-adapters.ts +22 -31
- package/src/db/json-migration.ts +11 -15
- package/src/db/json-store.ts +5 -3
- package/src/db/market-datasets.ts +48 -15
- package/src/db/market-schemas.ts +11 -5
- package/src/db/market-views.ts +119 -32
- package/src/db/parquet-writer.ts +7 -7
- package/src/db/profile-schemas.ts +39 -22
- package/src/db/schemas.ts +2 -2
- package/src/http-server.ts +7 -12
- package/src/index.ts +8 -9
- package/src/market/ingestor/index.ts +1 -4
- package/src/market/ingestor/market-ingestor.ts +140 -67
- package/src/market/ingestor/types.ts +1 -3
- package/src/market/stores/chain-sql.ts +2 -8
- package/src/market/stores/chain-store.ts +1 -4
- package/src/market/stores/duckdb-chain-store.ts +3 -14
- package/src/market/stores/duckdb-enriched-store.ts +3 -13
- package/src/market/stores/duckdb-quote-store.ts +10 -27
- package/src/market/stores/duckdb-spot-store.ts +4 -20
- package/src/market/stores/enriched-sql.ts +3 -9
- package/src/market/stores/enriched-store.ts +2 -2
- package/src/market/stores/parquet-chain-store.ts +5 -22
- package/src/market/stores/parquet-enriched-store.ts +4 -16
- package/src/market/stores/parquet-oi-daily-store.ts +3 -14
- package/src/market/stores/parquet-quote-store.ts +9 -29
- package/src/market/stores/parquet-spot-store.ts +14 -33
- package/src/market/stores/quote-store.ts +2 -10
- package/src/market/stores/spot-sql.ts +3 -15
- package/src/market/stores/spot-store.ts +1 -1
- package/src/market/tickers/defaults.json +6 -6
- package/src/market/tickers/loader.ts +2 -7
- package/src/market/tickers/registry.ts +5 -15
- package/src/market/tickers/schemas.ts +2 -6
- package/src/models/strategy-profile.ts +39 -39
- package/src/sync/block-sync.ts +44 -65
- package/src/sync/index.ts +1 -4
- package/src/sync/metadata.ts +14 -20
- package/src/test-exports.ts +127 -123
- package/src/tools/analysis.ts +180 -159
- package/src/tools/batch-exit-analysis.ts +100 -59
- package/src/tools/blocks/analysis.ts +116 -101
- package/src/tools/blocks/comparison.ts +281 -364
- package/src/tools/blocks/core.ts +208 -253
- package/src/tools/blocks/health.ts +571 -563
- package/src/tools/blocks/similarity.ts +316 -283
- package/src/tools/edge-decay.ts +124 -197
- package/src/tools/exit-analysis.ts +130 -77
- package/src/tools/greeks-attribution.ts +84 -35
- package/src/tools/guides.ts +4 -6
- package/src/tools/imports.ts +9 -12
- package/src/tools/market-data.ts +1067 -757
- package/src/tools/market-enrichment.ts +3 -3
- package/src/tools/market-fetch.ts +148 -67
- package/src/tools/market-imports.ts +12 -12
- package/src/tools/middleware/sync-middleware.ts +5 -6
- package/src/tools/performance.ts +185 -302
- package/src/tools/profile-analysis.ts +52 -66
- package/src/tools/profiles.ts +106 -69
- package/src/tools/regime-advisor.ts +20 -45
- package/src/tools/replay.ts +81 -77
- package/src/tools/reports/discrepancies.ts +298 -328
- package/src/tools/reports/fields.ts +7 -25
- package/src/tools/reports/helpers.ts +18 -49
- package/src/tools/reports/predictive.ts +27 -70
- package/src/tools/reports/slippage-trends.ts +315 -345
- package/src/tools/reports/slippage.ts +1 -4
- package/src/tools/reports/strategy-matches.ts +399 -441
- package/src/tools/schema.ts +43 -40
- package/src/tools/shared/filters.ts +3 -9
- package/src/tools/snapshot.ts +9 -30
- package/src/tools/sql.ts +15 -14
- package/src/tools/tickers.ts +1 -4
- package/src/utils/batch-exit-analysis.ts +31 -42
- package/src/utils/black-scholes.ts +39 -29
- package/src/utils/block-loader.ts +68 -82
- package/src/utils/calibration-probe.ts +1 -2
- package/src/utils/chain-loader.ts +3 -3
- package/src/utils/csv-discovery.ts +16 -22
- package/src/utils/data-quality.ts +24 -36
- package/src/utils/exit-triggers.ts +91 -96
- package/src/utils/field-timing.ts +94 -79
- package/src/utils/filter-predicates.ts +13 -9
- package/src/utils/flatfile-importer.ts +94 -64
- package/src/utils/greeks-decomposition.ts +152 -100
- package/src/utils/iv-solver-pool.ts +55 -25
- package/src/utils/iv-solver-worker.ts +5 -5
- package/src/utils/market-enricher.ts +528 -497
- package/src/utils/market-importer.ts +31 -12
- package/src/utils/market-provider.ts +21 -23
- package/src/utils/massive-tier.ts +5 -7
- package/src/utils/migrate-option-data-helpers.ts +2 -8
- package/src/utils/option-quote-greeks.ts +25 -31
- package/src/utils/option-time.ts +4 -8
- package/src/utils/output-formatter.ts +1 -4
- package/src/utils/provider-capabilities.ts +1 -4
- package/src/utils/providers/massive.ts +59 -93
- package/src/utils/providers/thetadata/backfill.ts +14 -23
- package/src/utils/providers/thetadata/client.ts +12 -8
- package/src/utils/providers/thetadata/decode.ts +2 -20
- package/src/utils/providers/thetadata/endpoints.ts +32 -40
- package/src/utils/providers/thetadata/join.ts +11 -10
- package/src/utils/providers/thetadata/proto.ts +12 -10
- package/src/utils/providers/thetadata/quote-mid-greeks.ts +5 -5
- package/src/utils/providers/thetadata.ts +11 -10
- package/src/utils/quote-enricher.ts +4 -4
- package/src/utils/quote-parquet-projection.ts +3 -11
- package/src/utils/sample-date-selector.ts +3 -5
- package/src/utils/schema-metadata.ts +102 -70
- package/src/utils/ticker.ts +5 -9
- package/src/utils/trade-replay.ts +77 -68
- package/dist/chunk-4BLCXNQ6.js.map +0 -1
- package/dist/chunk-BKQ4PM4Y.js.map +0 -1
- package/dist/chunk-FGZH632F.js.map +0 -1
- package/dist/chunk-LDKTV7GW.js.map +0 -1
- package/dist/chunk-PRAYH3RT.js.map +0 -1
- package/dist/chunk-W2PP3LEH.js.map +0 -1
- package/dist/chunk-XXYOUIZY.js.map +0 -1
- package/dist/daily-log-processor-6MWJ23JK.js +0 -9
- package/dist/market-provider-VDRJUEF2.js +0 -16
- package/dist/trade-processor-NHU4VWRX.js +0 -9
- package/server/chunk-5EBXHT6C.js.map +0 -1
- package/server/chunk-72GKJE2U.js.map +0 -1
- package/server/chunk-CSDVJPBB.js.map +0 -1
- package/server/chunk-FBNDMCT5.js.map +0 -1
- package/server/chunk-NRFXAJF7.js.map +0 -1
- package/server/chunk-NZO6PT64.js.map +0 -1
- package/server/chunk-PNKG7RY7.js.map +0 -1
- package/server/chunk-WA5AAPCH.js.map +0 -1
- package/server/config-6IZXEFEX.js.map +0 -1
- package/server/daily-log-processor-Y3PVSVBM.js +0 -10
- package/server/market-provider-VOYYVYWT.js +0 -17
- package/server/trade-processor-JWVS37KM.js +0 -10
- /package/dist/{daily-log-processor-6MWJ23JK.js.map → daily-log-processor-BY3ISY6K.js.map} +0 -0
- /package/dist/{daily-logs-store-D3QC2XPV.js.map → daily-logs-store-3C3OTGPK.js.map} +0 -0
- /package/dist/{market-provider-VDRJUEF2.js.map → market-provider-H3ARIVZ4.js.map} +0 -0
- /package/dist/{proto-3HZTCWK4.js.map → proto-V23GIZG2.js.map} +0 -0
- /package/dist/{sync-PO4IPCYV.js.map → sync-EVLKB3ZJ.js.map} +0 -0
- /package/dist/{trade-processor-NHU4VWRX.js.map → trade-processor-EYA3I3XB.js.map} +0 -0
- /package/dist/{trades-store-LOL3FQSF.js.map → trades-store-R3NL25T2.js.map} +0 -0
- /package/server/{daily-log-processor-Y3PVSVBM.js.map → daily-log-processor-ENEUT22A.js.map} +0 -0
- /package/server/{daily-logs-store-NU7M4L5C.js.map → daily-logs-store-YFNXTB7J.js.map} +0 -0
- /package/server/{market-provider-VOYYVYWT.js.map → market-provider-B437HKLW.js.map} +0 -0
- /package/server/{proto-DWRZJO4E.js.map → proto-YZ4TLX2N.js.map} +0 -0
- /package/server/{sync-QFI5L7S7.js.map → sync-V25UQJA3.js.map} +0 -0
- /package/server/{trade-processor-JWVS37KM.js.map → trade-processor-L3PIQ5TG.js.map} +0 -0
- /package/server/{trades-store-VY26MWER.js.map → trades-store-RTTEYMXH.js.map} +0 -0
|
@@ -15,11 +15,7 @@ import type { BarRow, CoverageReport } from "./types.ts";
|
|
|
15
15
|
import { buildReadBarsSQL, buildReadDailyBarsSQL } from "./spot-sql.ts";
|
|
16
16
|
|
|
17
17
|
export class DuckdbSpotStore extends SpotStore {
|
|
18
|
-
async writeBars(
|
|
19
|
-
ticker: string,
|
|
20
|
-
date: string,
|
|
21
|
-
bars: BarRow[],
|
|
22
|
-
): Promise<void> {
|
|
18
|
+
async writeBars(ticker: string, date: string, bars: BarRow[]): Promise<void> {
|
|
23
19
|
if (bars.length === 0) return;
|
|
24
20
|
const placeholders = bars
|
|
25
21
|
.map((_, i) => {
|
|
@@ -60,11 +56,7 @@ export class DuckdbSpotStore extends SpotStore {
|
|
|
60
56
|
return { rowCount: Number(result.rowsChanged) };
|
|
61
57
|
}
|
|
62
58
|
|
|
63
|
-
async readBars(
|
|
64
|
-
ticker: string,
|
|
65
|
-
from: string,
|
|
66
|
-
to: string,
|
|
67
|
-
): Promise<BarRow[]> {
|
|
59
|
+
async readBars(ticker: string, from: string, to: string): Promise<BarRow[]> {
|
|
68
60
|
// Builders inline values as SQL literals; the unbound runAndReadAll(sql)
|
|
69
61
|
// path bypasses extract_statements (see spot-sql.ts header).
|
|
70
62
|
const { sql } = buildReadBarsSQL(ticker, from, to);
|
|
@@ -83,11 +75,7 @@ export class DuckdbSpotStore extends SpotStore {
|
|
|
83
75
|
}));
|
|
84
76
|
}
|
|
85
77
|
|
|
86
|
-
async readDailyBars(
|
|
87
|
-
ticker: string,
|
|
88
|
-
from: string,
|
|
89
|
-
to: string,
|
|
90
|
-
): Promise<BarRow[]> {
|
|
78
|
+
async readDailyBars(ticker: string, from: string, to: string): Promise<BarRow[]> {
|
|
91
79
|
const { sql } = buildReadDailyBarsSQL(ticker, from, to);
|
|
92
80
|
const reader = await this.ctx.conn.runAndReadAll(sql);
|
|
93
81
|
return reader.getRows().map((r) => ({
|
|
@@ -104,11 +92,7 @@ export class DuckdbSpotStore extends SpotStore {
|
|
|
104
92
|
}));
|
|
105
93
|
}
|
|
106
94
|
|
|
107
|
-
async getCoverage(
|
|
108
|
-
ticker: string,
|
|
109
|
-
from: string,
|
|
110
|
-
to: string,
|
|
111
|
-
): Promise<CoverageReport> {
|
|
95
|
+
async getCoverage(ticker: string, from: string, to: string): Promise<CoverageReport> {
|
|
112
96
|
// Inline literals — same leak rationale as readBars (spot-sql.ts header).
|
|
113
97
|
const tickerLit = ticker.replace(/'/g, "''");
|
|
114
98
|
const fromLit = from.replace(/'/g, "''");
|
|
@@ -34,9 +34,7 @@ function lit(value: string): string {
|
|
|
34
34
|
* the RTH daily aggregate from `market.spot` and the `market.enriched_context`
|
|
35
35
|
* table.
|
|
36
36
|
*/
|
|
37
|
-
export function buildReadEnrichedSQL(
|
|
38
|
-
args: BuildReadEnrichedArgs,
|
|
39
|
-
): BuiltSQL {
|
|
37
|
+
export function buildReadEnrichedSQL(args: BuildReadEnrichedArgs): BuiltSQL {
|
|
40
38
|
const { ticker, from, to, includeContext, includeOhlcv } = args;
|
|
41
39
|
|
|
42
40
|
const tickerLit = lit(ticker);
|
|
@@ -52,13 +50,9 @@ export function buildReadEnrichedSQL(
|
|
|
52
50
|
ON s_daily.ticker = e.ticker AND s_daily.date = e.date`
|
|
53
51
|
: "";
|
|
54
52
|
|
|
55
|
-
const ctxJoin = includeContext
|
|
56
|
-
? `LEFT JOIN market.enriched_context c ON c.date = e.date`
|
|
57
|
-
: "";
|
|
53
|
+
const ctxJoin = includeContext ? `LEFT JOIN market.enriched_context c ON c.date = e.date` : "";
|
|
58
54
|
|
|
59
|
-
const ohlcvCols = includeOhlcv
|
|
60
|
-
? ", s_daily.open, s_daily.high, s_daily.low, s_daily.close"
|
|
61
|
-
: "";
|
|
55
|
+
const ohlcvCols = includeOhlcv ? ", s_daily.open, s_daily.high, s_daily.low, s_daily.close" : "";
|
|
62
56
|
|
|
63
57
|
const ctxCols = includeContext
|
|
64
58
|
? ", c.Vol_Regime, c.Term_Structure_State, c.Trend_Direction, c.VIX_Spike_Pct, c.VIX_Gap_Pct"
|
|
@@ -18,8 +18,8 @@ export interface EnrichedReadOpts {
|
|
|
18
18
|
ticker: string;
|
|
19
19
|
from: string;
|
|
20
20
|
to: string;
|
|
21
|
-
includeContext?: boolean;
|
|
22
|
-
includeOhlcv?: boolean;
|
|
21
|
+
includeContext?: boolean; // join enriched_context (VIX family cross-ticker fields)
|
|
22
|
+
includeOhlcv?: boolean; // join spot daily for OHLCV (avoids double-storing OHLCV)
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
export abstract class EnrichedStore {
|
|
@@ -18,21 +18,14 @@ import { ChainStore } from "./chain-store.ts";
|
|
|
18
18
|
import type { ContractRow, CoverageReport } from "./types.ts";
|
|
19
19
|
import { buildReadChainSQL } from "./chain-sql.ts";
|
|
20
20
|
import { listPartitionValues } from "./coverage.ts";
|
|
21
|
-
import {
|
|
22
|
-
resolveMarketDir,
|
|
23
|
-
writeChainPartition,
|
|
24
|
-
} from "../../db/market-datasets.ts";
|
|
21
|
+
import { resolveMarketDir, writeChainPartition } from "../../db/market-datasets.ts";
|
|
25
22
|
|
|
26
23
|
function escapeSqlLiteral(value: string): string {
|
|
27
24
|
return value.replace(/'/g, "''");
|
|
28
25
|
}
|
|
29
26
|
|
|
30
27
|
export class ParquetChainStore extends ChainStore {
|
|
31
|
-
async writeChain(
|
|
32
|
-
underlying: string,
|
|
33
|
-
date: string,
|
|
34
|
-
rows: ContractRow[],
|
|
35
|
-
): Promise<void> {
|
|
28
|
+
async writeChain(underlying: string, date: string, rows: ContractRow[]): Promise<void> {
|
|
36
29
|
if (rows.length === 0) return;
|
|
37
30
|
const staging = `_chain_write_${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
|
38
31
|
await this.ctx.conn.run(
|
|
@@ -108,10 +101,7 @@ export class ParquetChainStore extends ChainStore {
|
|
|
108
101
|
return existsSync(partitionPath);
|
|
109
102
|
}
|
|
110
103
|
|
|
111
|
-
async readChain(
|
|
112
|
-
underlying: string,
|
|
113
|
-
date: string,
|
|
114
|
-
): Promise<ContractRow[]> {
|
|
104
|
+
async readChain(underlying: string, date: string): Promise<ContractRow[]> {
|
|
115
105
|
const partitionPath = path.join(
|
|
116
106
|
resolveMarketDir(this.ctx.dataDir),
|
|
117
107
|
"option_chain",
|
|
@@ -153,10 +143,7 @@ export class ParquetChainStore extends ChainStore {
|
|
|
153
143
|
}));
|
|
154
144
|
}
|
|
155
145
|
|
|
156
|
-
override async readChainDates(
|
|
157
|
-
underlying: string,
|
|
158
|
-
dates: string[],
|
|
159
|
-
): Promise<ContractRow[]> {
|
|
146
|
+
override async readChainDates(underlying: string, dates: string[]): Promise<ContractRow[]> {
|
|
160
147
|
if (dates.length === 0) return [];
|
|
161
148
|
|
|
162
149
|
const marketDir = resolveMarketDir(this.ctx.dataDir);
|
|
@@ -193,11 +180,7 @@ export class ParquetChainStore extends ChainStore {
|
|
|
193
180
|
}));
|
|
194
181
|
}
|
|
195
182
|
|
|
196
|
-
async getCoverage(
|
|
197
|
-
underlying: string,
|
|
198
|
-
from: string,
|
|
199
|
-
to: string,
|
|
200
|
-
): Promise<CoverageReport> {
|
|
183
|
+
async getCoverage(underlying: string, from: string, to: string): Promise<CoverageReport> {
|
|
201
184
|
const dir = path.join(
|
|
202
185
|
resolveMarketDir(this.ctx.dataDir),
|
|
203
186
|
"option_chain",
|
|
@@ -18,17 +18,11 @@ import type { StoreContext, CoverageReport } from "./types.ts";
|
|
|
18
18
|
import { buildReadEnrichedSQL } from "./enriched-sql.ts";
|
|
19
19
|
import { resolveMarketDir } from "../../db/market-datasets.ts";
|
|
20
20
|
import { runEnrichment } from "../../utils/market-enricher.ts";
|
|
21
|
-
import {
|
|
22
|
-
getEnrichedThrough,
|
|
23
|
-
upsertEnrichedThrough,
|
|
24
|
-
} from "../../db/json-adapters.ts";
|
|
21
|
+
import { getEnrichedThrough, upsertEnrichedThrough } from "../../db/json-adapters.ts";
|
|
25
22
|
|
|
26
23
|
export class ParquetEnrichedStore extends EnrichedStore {
|
|
27
24
|
private readonly spotStore: SpotStore;
|
|
28
|
-
constructor(
|
|
29
|
-
ctx: StoreContext,
|
|
30
|
-
spotStore: SpotStore,
|
|
31
|
-
) {
|
|
25
|
+
constructor(ctx: StoreContext, spotStore: SpotStore) {
|
|
32
26
|
super(ctx);
|
|
33
27
|
this.spotStore = spotStore;
|
|
34
28
|
}
|
|
@@ -96,9 +90,7 @@ export class ParquetEnrichedStore extends EnrichedStore {
|
|
|
96
90
|
ORDER BY date`;
|
|
97
91
|
const reader = await this.ctx.conn.runAndReadAll(sql);
|
|
98
92
|
const names = reader.columnNames();
|
|
99
|
-
return reader
|
|
100
|
-
.getRows()
|
|
101
|
-
.map((row) => Object.fromEntries(names.map((n, i) => [n, row[i]])));
|
|
93
|
+
return reader.getRows().map((row) => Object.fromEntries(names.map((n, i) => [n, row[i]])));
|
|
102
94
|
}
|
|
103
95
|
}
|
|
104
96
|
// Builder inlines values; unbound runAndReadAll(sql) bypasses extract_statements.
|
|
@@ -111,11 +103,7 @@ export class ParquetEnrichedStore extends EnrichedStore {
|
|
|
111
103
|
});
|
|
112
104
|
const reader = await this.ctx.conn.runAndReadAll(sql);
|
|
113
105
|
const names = reader.columnNames();
|
|
114
|
-
return reader
|
|
115
|
-
.getRows()
|
|
116
|
-
.map((row) =>
|
|
117
|
-
Object.fromEntries(names.map((n, i) => [n, row[i]])),
|
|
118
|
-
);
|
|
106
|
+
return reader.getRows().map((row) => Object.fromEntries(names.map((n, i) => [n, row[i]])));
|
|
119
107
|
}
|
|
120
108
|
|
|
121
109
|
async getCoverage(ticker: string): Promise<CoverageReport> {
|
|
@@ -16,10 +16,7 @@ import * as path from "path";
|
|
|
16
16
|
import type { StoreContext } from "./types.ts";
|
|
17
17
|
import type { OiDailyRow } from "./types.ts";
|
|
18
18
|
import { listPartitionValues } from "./coverage.ts";
|
|
19
|
-
import {
|
|
20
|
-
resolveMarketDir,
|
|
21
|
-
writeOiDailyPartition,
|
|
22
|
-
} from "../../db/market-datasets.ts";
|
|
19
|
+
import { resolveMarketDir, writeOiDailyPartition } from "../../db/market-datasets.ts";
|
|
23
20
|
import { readParquetFilesSql } from "../../utils/quote-parquet-projection.ts";
|
|
24
21
|
|
|
25
22
|
// `right` is a reserved keyword in DuckDB (the RIGHT(string, n) function), so
|
|
@@ -46,11 +43,7 @@ export class ParquetOiDailyStore {
|
|
|
46
43
|
this.ctx = ctx;
|
|
47
44
|
}
|
|
48
45
|
|
|
49
|
-
async writeOiDaily(
|
|
50
|
-
underlying: string,
|
|
51
|
-
date: string,
|
|
52
|
-
rows: OiDailyRow[],
|
|
53
|
-
): Promise<void> {
|
|
46
|
+
async writeOiDaily(underlying: string, date: string, rows: OiDailyRow[]): Promise<void> {
|
|
54
47
|
if (rows.length === 0) return;
|
|
55
48
|
// Append via DuckDBAppender (typed per-column) rather than a parameterized
|
|
56
49
|
// INSERT with O(N) placeholders — mirrors the quote store's write path.
|
|
@@ -96,11 +89,7 @@ export class ParquetOiDailyStore {
|
|
|
96
89
|
}
|
|
97
90
|
}
|
|
98
91
|
|
|
99
|
-
async readOiDaily(
|
|
100
|
-
underlying: string,
|
|
101
|
-
from: string,
|
|
102
|
-
to: string,
|
|
103
|
-
): Promise<OiDailyRow[]> {
|
|
92
|
+
async readOiDaily(underlying: string, from: string, to: string): Promise<OiDailyRow[]> {
|
|
104
93
|
const underlyingDir = path.join(
|
|
105
94
|
resolveMarketDir(this.ctx.dataDir),
|
|
106
95
|
"option_oi_daily",
|
|
@@ -17,17 +17,9 @@
|
|
|
17
17
|
import { existsSync } from "fs";
|
|
18
18
|
import * as path from "path";
|
|
19
19
|
import { QuoteStore } from "./quote-store.ts";
|
|
20
|
-
import type {
|
|
21
|
-
QuoteRow,
|
|
22
|
-
CoverageReport,
|
|
23
|
-
ReadWindowParams,
|
|
24
|
-
WindowQuoteRow,
|
|
25
|
-
} from "./types.ts";
|
|
20
|
+
import type { QuoteRow, CoverageReport, ReadWindowParams, WindowQuoteRow } from "./types.ts";
|
|
26
21
|
import { listPartitionValues } from "./coverage.ts";
|
|
27
|
-
import {
|
|
28
|
-
resolveMarketDir,
|
|
29
|
-
writeQuoteMinutesPartition,
|
|
30
|
-
} from "../../db/market-datasets.ts";
|
|
22
|
+
import { resolveMarketDir, writeQuoteMinutesPartition } from "../../db/market-datasets.ts";
|
|
31
23
|
import { extractRoot } from "../tickers/resolver.ts";
|
|
32
24
|
import {
|
|
33
25
|
describeQueryColumns,
|
|
@@ -53,7 +45,7 @@ function parseQuoteRow(row: unknown[]): QuoteRow {
|
|
|
53
45
|
theta: row[11] == null ? null : Number(row[11]),
|
|
54
46
|
vega: row[12] == null ? null : Number(row[12]),
|
|
55
47
|
iv: row[13] == null ? null : Number(row[13]),
|
|
56
|
-
greeks_source: row[14] == null ? null : String(row[14]) as QuoteRow["greeks_source"],
|
|
48
|
+
greeks_source: row[14] == null ? null : (String(row[14]) as QuoteRow["greeks_source"]),
|
|
57
49
|
greeks_revision: row[15] == null ? null : Number(row[15]),
|
|
58
50
|
rate_type: row[16] == null ? null : String(row[16]),
|
|
59
51
|
rate_value: row[17] == null ? null : Number(row[17]),
|
|
@@ -62,11 +54,7 @@ function parseQuoteRow(row: unknown[]): QuoteRow {
|
|
|
62
54
|
}
|
|
63
55
|
|
|
64
56
|
export class ParquetQuoteStore extends QuoteStore {
|
|
65
|
-
async writeQuotes(
|
|
66
|
-
underlying: string,
|
|
67
|
-
date: string,
|
|
68
|
-
quotes: QuoteRow[],
|
|
69
|
-
): Promise<void> {
|
|
57
|
+
async writeQuotes(underlying: string, date: string, quotes: QuoteRow[]): Promise<void> {
|
|
70
58
|
if (quotes.length === 0) return;
|
|
71
59
|
// Append rows via DuckDBAppender (typed per-column, no SQL parse overhead)
|
|
72
60
|
// rather than a parameterized INSERT with O(N) placeholders — the latter
|
|
@@ -173,9 +161,7 @@ export class ParquetQuoteStore extends QuoteStore {
|
|
|
173
161
|
// D-07: validate all tickers resolve to the same underlying BEFORE any SQL
|
|
174
162
|
// runs. A mixed batch is almost always a bug in the caller; surface it
|
|
175
163
|
// with both conflicting OCC tickers + resolved underlyings for debugging.
|
|
176
|
-
const firstUnderlying = this.ctx.tickers.resolve(
|
|
177
|
-
extractRoot(occTickers[0]),
|
|
178
|
-
);
|
|
164
|
+
const firstUnderlying = this.ctx.tickers.resolve(extractRoot(occTickers[0]));
|
|
179
165
|
for (const t of occTickers) {
|
|
180
166
|
const u = this.ctx.tickers.resolve(extractRoot(t));
|
|
181
167
|
if (u !== firstUnderlying) {
|
|
@@ -208,9 +194,7 @@ export class ParquetQuoteStore extends QuoteStore {
|
|
|
208
194
|
// under sustained read load. See readWindow below for the full writeup.
|
|
209
195
|
const fromLit = `'${escapeSqlLiteral(from)}'`;
|
|
210
196
|
const toLit = `'${escapeSqlLiteral(to)}'`;
|
|
211
|
-
const tickerList = occTickers
|
|
212
|
-
.map((t) => `'${escapeSqlLiteral(t)}'`)
|
|
213
|
-
.join(", ");
|
|
197
|
+
const tickerList = occTickers.map((t) => `'${escapeSqlLiteral(t)}'`).join(", ");
|
|
214
198
|
const reader = await this.ctx.conn.runAndReadAll(
|
|
215
199
|
`SELECT ${projection}
|
|
216
200
|
FROM ${source} AS q
|
|
@@ -293,7 +277,7 @@ export class ParquetQuoteStore extends QuoteStore {
|
|
|
293
277
|
if (perf) {
|
|
294
278
|
console.log(
|
|
295
279
|
` [P] readQuotesBulk underlying=${underlying} dates=${filePaths.length} ` +
|
|
296
|
-
|
|
280
|
+
`tickers=${occUnion.size} rows=${rows.length} queryMs=${Date.now() - queryStart}`,
|
|
297
281
|
);
|
|
298
282
|
}
|
|
299
283
|
for (const row of rows) {
|
|
@@ -407,15 +391,11 @@ export class ParquetQuoteStore extends QuoteStore {
|
|
|
407
391
|
theta: r[10] == null ? null : Number(r[10]),
|
|
408
392
|
vega: r[11] == null ? null : Number(r[11]),
|
|
409
393
|
iv: r[12] == null ? null : Number(r[12]),
|
|
410
|
-
greeks_source: r[13] == null ? null : String(r[13]) as WindowQuoteRow["greeks_source"],
|
|
394
|
+
greeks_source: r[13] == null ? null : (String(r[13]) as WindowQuoteRow["greeks_source"]),
|
|
411
395
|
}));
|
|
412
396
|
}
|
|
413
397
|
|
|
414
|
-
async getCoverage(
|
|
415
|
-
underlying: string,
|
|
416
|
-
from: string,
|
|
417
|
-
to: string,
|
|
418
|
-
): Promise<CoverageReport> {
|
|
398
|
+
async getCoverage(underlying: string, from: string, to: string): Promise<CoverageReport> {
|
|
419
399
|
const dir = path.join(
|
|
420
400
|
resolveMarketDir(this.ctx.dataDir),
|
|
421
401
|
"option_quote_minutes",
|
|
@@ -16,17 +16,10 @@ import { SpotStore } from "./spot-store.ts";
|
|
|
16
16
|
import type { BarRow, CoverageReport } from "./types.ts";
|
|
17
17
|
import { buildReadBarsSQL, buildReadDailyBarsSQL } from "./spot-sql.ts";
|
|
18
18
|
import { listPartitionValues } from "./coverage.ts";
|
|
19
|
-
import {
|
|
20
|
-
resolveMarketDir,
|
|
21
|
-
writeSpotPartition,
|
|
22
|
-
} from "../../db/market-datasets.ts";
|
|
19
|
+
import { resolveMarketDir, writeSpotPartition } from "../../db/market-datasets.ts";
|
|
23
20
|
|
|
24
21
|
export class ParquetSpotStore extends SpotStore {
|
|
25
|
-
async writeBars(
|
|
26
|
-
ticker: string,
|
|
27
|
-
date: string,
|
|
28
|
-
bars: BarRow[],
|
|
29
|
-
): Promise<void> {
|
|
22
|
+
async writeBars(ticker: string, date: string, bars: BarRow[]): Promise<void> {
|
|
30
23
|
if (bars.length === 0) return;
|
|
31
24
|
|
|
32
25
|
// Defense-in-depth write-side filter (per-bar).
|
|
@@ -55,10 +48,14 @@ export class ParquetSpotStore extends SpotStore {
|
|
|
55
48
|
|
|
56
49
|
const cleanBars = bars.filter(
|
|
57
50
|
(b) =>
|
|
58
|
-
Number.isFinite(b.open) &&
|
|
59
|
-
|
|
60
|
-
Number.isFinite(b.
|
|
61
|
-
|
|
51
|
+
Number.isFinite(b.open) &&
|
|
52
|
+
b.open > 0 &&
|
|
53
|
+
Number.isFinite(b.high) &&
|
|
54
|
+
b.high > 0 &&
|
|
55
|
+
Number.isFinite(b.low) &&
|
|
56
|
+
b.low > 0 &&
|
|
57
|
+
Number.isFinite(b.close) &&
|
|
58
|
+
b.close > 0,
|
|
62
59
|
);
|
|
63
60
|
const dropped = bars.length - cleanBars.length;
|
|
64
61
|
if (dropped > 0) {
|
|
@@ -132,11 +129,7 @@ export class ParquetSpotStore extends SpotStore {
|
|
|
132
129
|
});
|
|
133
130
|
}
|
|
134
131
|
|
|
135
|
-
async readBars(
|
|
136
|
-
ticker: string,
|
|
137
|
-
from: string,
|
|
138
|
-
to: string,
|
|
139
|
-
): Promise<BarRow[]> {
|
|
132
|
+
async readBars(ticker: string, from: string, to: string): Promise<BarRow[]> {
|
|
140
133
|
const direct = this.buildDirectParquetReadBarsSQL(ticker, from, to);
|
|
141
134
|
// Both paths inline values — bound-param runAndReadAll(sql, values) leaks
|
|
142
135
|
// extract_statements handles (parquet-quote-store.ts:327, spot-sql.ts).
|
|
@@ -156,11 +149,7 @@ export class ParquetSpotStore extends SpotStore {
|
|
|
156
149
|
}));
|
|
157
150
|
}
|
|
158
151
|
|
|
159
|
-
async readDailyBars(
|
|
160
|
-
ticker: string,
|
|
161
|
-
from: string,
|
|
162
|
-
to: string,
|
|
163
|
-
): Promise<BarRow[]> {
|
|
152
|
+
async readDailyBars(ticker: string, from: string, to: string): Promise<BarRow[]> {
|
|
164
153
|
const direct = this.buildDirectParquetReadBarsSQL(ticker, from, to, { dailyAgg: true });
|
|
165
154
|
// Same leak rationale as readBars — both paths run via unbound query().
|
|
166
155
|
const { sql } = direct ?? buildReadDailyBarsSQL(ticker, from, to);
|
|
@@ -179,16 +168,8 @@ export class ParquetSpotStore extends SpotStore {
|
|
|
179
168
|
}));
|
|
180
169
|
}
|
|
181
170
|
|
|
182
|
-
async getCoverage(
|
|
183
|
-
|
|
184
|
-
from: string,
|
|
185
|
-
to: string,
|
|
186
|
-
): Promise<CoverageReport> {
|
|
187
|
-
const tickerDir = path.join(
|
|
188
|
-
resolveMarketDir(this.ctx.dataDir),
|
|
189
|
-
"spot",
|
|
190
|
-
`ticker=${ticker}`,
|
|
191
|
-
);
|
|
171
|
+
async getCoverage(ticker: string, from: string, to: string): Promise<CoverageReport> {
|
|
172
|
+
const tickerDir = path.join(resolveMarketDir(this.ctx.dataDir), "spot", `ticker=${ticker}`);
|
|
192
173
|
if (!existsSync(tickerDir)) {
|
|
193
174
|
return { earliest: null, latest: null, missingDates: [], totalDates: 0 };
|
|
194
175
|
}
|
|
@@ -45,11 +45,7 @@ export abstract class QuoteStore {
|
|
|
45
45
|
return this.ctx.tickers;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
abstract writeQuotes(
|
|
49
|
-
underlying: string,
|
|
50
|
-
date: string,
|
|
51
|
-
quotes: QuoteRow[],
|
|
52
|
-
): Promise<void>;
|
|
48
|
+
abstract writeQuotes(underlying: string, date: string, quotes: QuoteRow[]): Promise<void>;
|
|
53
49
|
|
|
54
50
|
/**
|
|
55
51
|
* Write quotes for a single (underlying, date) partition from a user-supplied SELECT.
|
|
@@ -152,11 +148,7 @@ export abstract class QuoteStore {
|
|
|
152
148
|
return out;
|
|
153
149
|
}
|
|
154
150
|
|
|
155
|
-
abstract getCoverage(
|
|
156
|
-
underlying: string,
|
|
157
|
-
from: string,
|
|
158
|
-
to: string,
|
|
159
|
-
): Promise<CoverageReport>;
|
|
151
|
+
abstract getCoverage(underlying: string, from: string, to: string): Promise<CoverageReport>;
|
|
160
152
|
|
|
161
153
|
/**
|
|
162
154
|
* Read every option-quote row in the leg-envelope union over a time window.
|
|
@@ -45,11 +45,7 @@ function lit(value: string): string {
|
|
|
45
45
|
* Read raw minute bars from `market.spot` for a ticker over a date range.
|
|
46
46
|
* Results are ordered by (date, time) so callers receive a deterministic stream.
|
|
47
47
|
*/
|
|
48
|
-
export function buildReadBarsSQL(
|
|
49
|
-
ticker: string,
|
|
50
|
-
from: string,
|
|
51
|
-
to: string,
|
|
52
|
-
): BuiltSQL {
|
|
48
|
+
export function buildReadBarsSQL(ticker: string, from: string, to: string): BuiltSQL {
|
|
53
49
|
return {
|
|
54
50
|
sql: `SELECT ticker, date, time, open, high, low, close, bid, ask
|
|
55
51
|
FROM market.spot
|
|
@@ -65,11 +61,7 @@ export function buildReadBarsSQL(
|
|
|
65
61
|
* idioms. Window-function equivalents are explicitly avoided — they do NOT
|
|
66
62
|
* coexist with `GROUP BY` and are a common source of incorrect ordering.
|
|
67
63
|
*/
|
|
68
|
-
export function buildReadDailyBarsSQL(
|
|
69
|
-
ticker: string,
|
|
70
|
-
from: string,
|
|
71
|
-
to: string,
|
|
72
|
-
): BuiltSQL {
|
|
64
|
+
export function buildReadDailyBarsSQL(ticker: string, from: string, to: string): BuiltSQL {
|
|
73
65
|
return {
|
|
74
66
|
sql: `SELECT
|
|
75
67
|
ticker,
|
|
@@ -103,11 +95,7 @@ export function buildReadDailyBarsSQL(
|
|
|
103
95
|
* Used by the enricher's VIX RTH open path, where only the opening tick of
|
|
104
96
|
* the VIX family is needed for term-structure context computation.
|
|
105
97
|
*/
|
|
106
|
-
export function buildReadRthOpensSQL(
|
|
107
|
-
ticker: string,
|
|
108
|
-
from: string,
|
|
109
|
-
to: string,
|
|
110
|
-
): BuiltSQL {
|
|
98
|
+
export function buildReadRthOpensSQL(ticker: string, from: string, to: string): BuiltSQL {
|
|
111
99
|
return {
|
|
112
100
|
sql: `SELECT date, first(open ORDER BY time) AS open
|
|
113
101
|
FROM market.spot
|
|
@@ -44,7 +44,7 @@ export abstract class SpotStore {
|
|
|
44
44
|
if (existsSync(p)) paths.push(p);
|
|
45
45
|
}
|
|
46
46
|
if (paths.length === 0) return null;
|
|
47
|
-
const fileList = paths.map(p => `'${escapeSqlLiteral(p)}'`).join(", ");
|
|
47
|
+
const fileList = paths.map((p) => `'${escapeSqlLiteral(p)}'`).join(", ");
|
|
48
48
|
const tickerLit = `'${escapeSqlLiteral(ticker)}'`;
|
|
49
49
|
if (opts?.dailyAgg) {
|
|
50
50
|
return {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 1,
|
|
3
3
|
"underlyings": [
|
|
4
|
-
{ "underlying": "SPX",
|
|
5
|
-
{ "underlying": "QQQ",
|
|
6
|
-
{ "underlying": "VIX",
|
|
4
|
+
{ "underlying": "SPX", "roots": ["SPX", "SPXW", "SPXQ"] },
|
|
5
|
+
{ "underlying": "QQQ", "roots": ["QQQ", "QQQX"] },
|
|
6
|
+
{ "underlying": "VIX", "roots": ["VIX"] },
|
|
7
7
|
{ "underlying": "VIX9D", "roots": ["VIX9D"] },
|
|
8
8
|
{ "underlying": "VIX3M", "roots": ["VIX3M"] },
|
|
9
|
-
{ "underlying": "ES",
|
|
10
|
-
{ "underlying": "NDX",
|
|
11
|
-
{ "underlying": "RUT",
|
|
9
|
+
{ "underlying": "ES", "roots": ["ES"] },
|
|
10
|
+
{ "underlying": "NDX", "roots": ["NDX", "NDXP"] },
|
|
11
|
+
{ "underlying": "RUT", "roots": ["RUT", "RUTW"] }
|
|
12
12
|
]
|
|
13
13
|
}
|
|
@@ -36,9 +36,7 @@ function userOverridePath(dataDir: string): string {
|
|
|
36
36
|
* - Malformed user JSON throws a clear "Malformed {path}: ..." error — NO silent
|
|
37
37
|
* fallback (D-08, T-1-03 mitigation).
|
|
38
38
|
*/
|
|
39
|
-
export async function loadRegistry(args: {
|
|
40
|
-
dataDir: string;
|
|
41
|
-
}): Promise<TickerRegistry> {
|
|
39
|
+
export async function loadRegistry(args: { dataDir: string }): Promise<TickerRegistry> {
|
|
42
40
|
const overridePath = userOverridePath(args.dataDir);
|
|
43
41
|
// readJsonFile returns null on ENOENT (json-store.ts:51-61) and throws on JSON
|
|
44
42
|
// parse errors. We treat both Zod rejections AND parse errors as "Malformed"
|
|
@@ -65,10 +63,7 @@ export async function loadRegistry(args: {
|
|
|
65
63
|
* Persist user + user-override entries to {dataRoot}/market/underlyings.json.
|
|
66
64
|
* Atomic tmp-then-rename via writeJsonFile (json-store.ts:36-41).
|
|
67
65
|
*/
|
|
68
|
-
export async function saveUserOverride(
|
|
69
|
-
dataDir: string,
|
|
70
|
-
registry: TickerRegistry,
|
|
71
|
-
): Promise<void> {
|
|
66
|
+
export async function saveUserOverride(dataDir: string, registry: TickerRegistry): Promise<void> {
|
|
72
67
|
const overridePath = userOverridePath(dataDir);
|
|
73
68
|
await writeJsonFile(overridePath, registry.toJSON());
|
|
74
69
|
}
|
|
@@ -31,16 +31,13 @@ function validate(underlying: string, roots: string[]): void {
|
|
|
31
31
|
}
|
|
32
32
|
for (const r of roots) {
|
|
33
33
|
if (!TICKER_RE.test(r)) {
|
|
34
|
-
throw new Error(
|
|
35
|
-
`TickerRegistry: invalid root "${r}" — must match ${TICKER_RE.source}`,
|
|
36
|
-
);
|
|
34
|
+
throw new Error(`TickerRegistry: invalid root "${r}" — must match ${TICKER_RE.source}`);
|
|
37
35
|
}
|
|
38
36
|
}
|
|
39
37
|
}
|
|
40
38
|
|
|
41
39
|
export class TickerRegistry {
|
|
42
|
-
private rootMap: Map<string, { underlying: string; source: EntrySource }> =
|
|
43
|
-
new Map();
|
|
40
|
+
private rootMap: Map<string, { underlying: string; source: EntrySource }> = new Map();
|
|
44
41
|
private entries: Map<string, TickerEntry> = new Map();
|
|
45
42
|
// Preserved so unregister('user-override') can revert to the bundled default.
|
|
46
43
|
private readonly bundledDefaults: ReadonlyMap<string, ReadonlyArray<string>>;
|
|
@@ -65,9 +62,7 @@ export class TickerRegistry {
|
|
|
65
62
|
this.bundledDefaults = bundled;
|
|
66
63
|
for (const e of userOverrides) {
|
|
67
64
|
validate(e.underlying, e.roots);
|
|
68
|
-
const source: EntrySource = bundled.has(e.underlying)
|
|
69
|
-
? "user-override"
|
|
70
|
-
: "user";
|
|
65
|
+
const source: EntrySource = bundled.has(e.underlying) ? "user-override" : "user";
|
|
71
66
|
// Clear stale root mappings that previously pointed at this underlying.
|
|
72
67
|
for (const [r, v] of [...this.rootMap]) {
|
|
73
68
|
if (v.underlying === e.underlying) this.rootMap.delete(r);
|
|
@@ -129,9 +124,7 @@ export class TickerRegistry {
|
|
|
129
124
|
unregister(underlying: string): void {
|
|
130
125
|
const entry = this.entries.get(underlying);
|
|
131
126
|
if (!entry) {
|
|
132
|
-
throw new Error(
|
|
133
|
-
`TickerRegistry.unregister: unknown underlying "${underlying}"`,
|
|
134
|
-
);
|
|
127
|
+
throw new Error(`TickerRegistry.unregister: unknown underlying "${underlying}"`);
|
|
135
128
|
}
|
|
136
129
|
if (entry.source === "default") {
|
|
137
130
|
throw new Error(
|
|
@@ -139,10 +132,7 @@ export class TickerRegistry {
|
|
|
139
132
|
);
|
|
140
133
|
}
|
|
141
134
|
// If it was a user-override of a default, revert to the bundled default.
|
|
142
|
-
if (
|
|
143
|
-
entry.source === "user-override" &&
|
|
144
|
-
this.bundledDefaults.has(underlying)
|
|
145
|
-
) {
|
|
135
|
+
if (entry.source === "user-override" && this.bundledDefaults.has(underlying)) {
|
|
146
136
|
const defaultRoots = [...(this.bundledDefaults.get(underlying) ?? [])];
|
|
147
137
|
for (const [r, v] of [...this.rootMap]) {
|
|
148
138
|
if (v.underlying === underlying) this.rootMap.delete(r);
|
|
@@ -41,9 +41,7 @@ export const registerUnderlyingSchema = z.object({
|
|
|
41
41
|
.array(z.string().min(1).max(16).regex(TICKER_RE))
|
|
42
42
|
.min(1)
|
|
43
43
|
.max(32)
|
|
44
|
-
.describe(
|
|
45
|
-
"OCC roots that resolve to this underlying, e.g. ['SPX','SPXW','SPXQ']",
|
|
46
|
-
),
|
|
44
|
+
.describe("OCC roots that resolve to this underlying, e.g. ['SPX','SPXW','SPXQ']"),
|
|
47
45
|
});
|
|
48
46
|
|
|
49
47
|
export const unregisterUnderlyingSchema = z.object({
|
|
@@ -62,7 +60,5 @@ export const resolveRootSchema = z.object({
|
|
|
62
60
|
.string()
|
|
63
61
|
.min(1)
|
|
64
62
|
.max(32)
|
|
65
|
-
.describe(
|
|
66
|
-
"Bare root ('SPXW') or full OCC ticker ('SPXW251219C05000000')",
|
|
67
|
-
),
|
|
63
|
+
.describe("Bare root ('SPXW') or full OCC ticker ('SPXW251219C05000000')"),
|
|
68
64
|
});
|