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.
Files changed (214) hide show
  1. package/README.md +82 -68
  2. package/dist/{chunk-LDKTV7GW.js → chunk-27S67XW3.js} +24 -52
  3. package/dist/chunk-27S67XW3.js.map +1 -0
  4. package/dist/{chunk-FGZH632F.js → chunk-2E63THNI.js} +13 -11
  5. package/dist/chunk-2E63THNI.js.map +1 -0
  6. package/dist/{chunk-BKQ4PM4Y.js → chunk-6S37CXUA.js} +11 -13
  7. package/dist/chunk-6S37CXUA.js.map +1 -0
  8. package/dist/{chunk-PRAYH3RT.js → chunk-AP7IHUUR.js} +29 -12
  9. package/dist/chunk-AP7IHUUR.js.map +1 -0
  10. package/dist/{chunk-QTTR7AAW.js → chunk-C6LL746C.js} +67 -84
  11. package/dist/{chunk-QTTR7AAW.js.map → chunk-C6LL746C.js.map} +1 -1
  12. package/dist/{chunk-W2PP3LEH.js → chunk-SEUZYQGQ.js} +17 -8
  13. package/dist/chunk-SEUZYQGQ.js.map +1 -0
  14. package/dist/{chunk-4BLCXNQ6.js → chunk-UBUC5A66.js} +105 -114
  15. package/dist/chunk-UBUC5A66.js.map +1 -0
  16. package/dist/{chunk-XXYOUIZY.js → chunk-VDU25Z6X.js} +63 -80
  17. package/dist/chunk-VDU25Z6X.js.map +1 -0
  18. package/dist/daily-log-processor-BY3ISY6K.js +9 -0
  19. package/dist/{daily-logs-store-D3QC2XPV.js → daily-logs-store-3C3OTGPK.js} +2 -2
  20. package/dist/iv-solver-worker.js +9 -1
  21. package/dist/market-provider-H3ARIVZ4.js +16 -0
  22. package/dist/{proto-3HZTCWK4.js → proto-V23GIZG2.js} +2 -2
  23. package/dist/{sync-PO4IPCYV.js → sync-EVLKB3ZJ.js} +7 -7
  24. package/dist/test-exports.js +358 -254
  25. package/dist/test-exports.js.map +1 -1
  26. package/dist/trade-processor-EYA3I3XB.js +9 -0
  27. package/dist/{trades-store-LOL3FQSF.js → trades-store-R3NL25T2.js} +2 -2
  28. package/manifest.json +3 -11
  29. package/package.json +1 -1
  30. package/server/{chunk-NZO6PT64.js → chunk-4P7D7YZP.js} +102 -113
  31. package/server/chunk-4P7D7YZP.js.map +1 -0
  32. package/server/{chunk-72GKJE2U.js → chunk-BOPHW5M6.js} +521 -235
  33. package/server/chunk-BOPHW5M6.js.map +1 -0
  34. package/server/{chunk-WA5AAPCH.js → chunk-GH2552SE.js} +17 -8
  35. package/server/chunk-GH2552SE.js.map +1 -0
  36. package/server/{chunk-5EBXHT6C.js → chunk-OBYKFW2B.js} +11 -13
  37. package/server/chunk-OBYKFW2B.js.map +1 -0
  38. package/server/{chunk-FBNDMCT5.js → chunk-T66KH2XH.js} +13 -11
  39. package/server/chunk-T66KH2XH.js.map +1 -0
  40. package/server/{chunk-NRFXAJF7.js → chunk-W5E7FHC4.js} +24 -52
  41. package/server/chunk-W5E7FHC4.js.map +1 -0
  42. package/server/{chunk-CSDVJPBB.js → chunk-YUCOAJ4Z.js} +29 -12
  43. package/server/chunk-YUCOAJ4Z.js.map +1 -0
  44. package/server/{chunk-PNKG7RY7.js → chunk-ZBJCF4ZG.js} +63 -80
  45. package/server/chunk-ZBJCF4ZG.js.map +1 -0
  46. package/server/{config-6IZXEFEX.js → config-DK7KOMNL.js} +1 -1
  47. package/server/config-DK7KOMNL.js.map +1 -0
  48. package/server/daily-log-processor-ENEUT22A.js +10 -0
  49. package/server/{daily-logs-store-NU7M4L5C.js → daily-logs-store-YFNXTB7J.js} +2 -2
  50. package/server/http-server.js +2 -5
  51. package/server/index.js +1771 -1538
  52. package/server/index.js.map +1 -1
  53. package/server/iv-solver-worker.js +9 -1
  54. package/server/market-provider-B437HKLW.js +17 -0
  55. package/server/{proto-DWRZJO4E.js → proto-YZ4TLX2N.js} +2 -2
  56. package/server/{sync-QFI5L7S7.js → sync-V25UQJA3.js} +7 -7
  57. package/server/trade-processor-L3PIQ5TG.js +10 -0
  58. package/server/{trades-store-VY26MWER.js → trades-store-RTTEYMXH.js} +2 -2
  59. package/src/auth/clients-store.ts +4 -4
  60. package/src/auth/code-store.ts +1 -1
  61. package/src/auth/config.ts +11 -12
  62. package/src/auth/login-page.ts +8 -10
  63. package/src/auth/provider.ts +35 -30
  64. package/src/auth/token.ts +17 -15
  65. package/src/db/backtest-schemas.ts +12 -4
  66. package/src/db/connection.ts +107 -40
  67. package/src/db/index.ts +37 -5
  68. package/src/db/json-adapters.ts +22 -31
  69. package/src/db/json-migration.ts +11 -15
  70. package/src/db/json-store.ts +5 -3
  71. package/src/db/market-datasets.ts +48 -15
  72. package/src/db/market-schemas.ts +11 -5
  73. package/src/db/market-views.ts +119 -32
  74. package/src/db/parquet-writer.ts +7 -7
  75. package/src/db/profile-schemas.ts +39 -22
  76. package/src/db/schemas.ts +2 -2
  77. package/src/http-server.ts +7 -12
  78. package/src/index.ts +8 -9
  79. package/src/market/ingestor/index.ts +1 -4
  80. package/src/market/ingestor/market-ingestor.ts +140 -67
  81. package/src/market/ingestor/types.ts +1 -3
  82. package/src/market/stores/chain-sql.ts +2 -8
  83. package/src/market/stores/chain-store.ts +1 -4
  84. package/src/market/stores/duckdb-chain-store.ts +3 -14
  85. package/src/market/stores/duckdb-enriched-store.ts +3 -13
  86. package/src/market/stores/duckdb-quote-store.ts +10 -27
  87. package/src/market/stores/duckdb-spot-store.ts +4 -20
  88. package/src/market/stores/enriched-sql.ts +3 -9
  89. package/src/market/stores/enriched-store.ts +2 -2
  90. package/src/market/stores/parquet-chain-store.ts +5 -22
  91. package/src/market/stores/parquet-enriched-store.ts +4 -16
  92. package/src/market/stores/parquet-oi-daily-store.ts +3 -14
  93. package/src/market/stores/parquet-quote-store.ts +9 -29
  94. package/src/market/stores/parquet-spot-store.ts +14 -33
  95. package/src/market/stores/quote-store.ts +2 -10
  96. package/src/market/stores/spot-sql.ts +3 -15
  97. package/src/market/stores/spot-store.ts +1 -1
  98. package/src/market/tickers/defaults.json +6 -6
  99. package/src/market/tickers/loader.ts +2 -7
  100. package/src/market/tickers/registry.ts +5 -15
  101. package/src/market/tickers/schemas.ts +2 -6
  102. package/src/models/strategy-profile.ts +39 -39
  103. package/src/sync/block-sync.ts +44 -65
  104. package/src/sync/index.ts +1 -4
  105. package/src/sync/metadata.ts +14 -20
  106. package/src/test-exports.ts +127 -123
  107. package/src/tools/analysis.ts +180 -159
  108. package/src/tools/batch-exit-analysis.ts +100 -59
  109. package/src/tools/blocks/analysis.ts +116 -101
  110. package/src/tools/blocks/comparison.ts +281 -364
  111. package/src/tools/blocks/core.ts +208 -253
  112. package/src/tools/blocks/health.ts +571 -563
  113. package/src/tools/blocks/similarity.ts +316 -283
  114. package/src/tools/edge-decay.ts +124 -197
  115. package/src/tools/exit-analysis.ts +130 -77
  116. package/src/tools/greeks-attribution.ts +84 -35
  117. package/src/tools/guides.ts +4 -6
  118. package/src/tools/imports.ts +9 -12
  119. package/src/tools/market-data.ts +1067 -757
  120. package/src/tools/market-enrichment.ts +3 -3
  121. package/src/tools/market-fetch.ts +148 -67
  122. package/src/tools/market-imports.ts +12 -12
  123. package/src/tools/middleware/sync-middleware.ts +5 -6
  124. package/src/tools/performance.ts +185 -302
  125. package/src/tools/profile-analysis.ts +52 -66
  126. package/src/tools/profiles.ts +106 -69
  127. package/src/tools/regime-advisor.ts +20 -45
  128. package/src/tools/replay.ts +81 -77
  129. package/src/tools/reports/discrepancies.ts +298 -328
  130. package/src/tools/reports/fields.ts +7 -25
  131. package/src/tools/reports/helpers.ts +18 -49
  132. package/src/tools/reports/predictive.ts +27 -70
  133. package/src/tools/reports/slippage-trends.ts +315 -345
  134. package/src/tools/reports/slippage.ts +1 -4
  135. package/src/tools/reports/strategy-matches.ts +399 -441
  136. package/src/tools/schema.ts +43 -40
  137. package/src/tools/shared/filters.ts +3 -9
  138. package/src/tools/snapshot.ts +9 -30
  139. package/src/tools/sql.ts +15 -14
  140. package/src/tools/tickers.ts +1 -4
  141. package/src/utils/batch-exit-analysis.ts +31 -42
  142. package/src/utils/black-scholes.ts +39 -29
  143. package/src/utils/block-loader.ts +68 -82
  144. package/src/utils/calibration-probe.ts +1 -2
  145. package/src/utils/chain-loader.ts +3 -3
  146. package/src/utils/csv-discovery.ts +16 -22
  147. package/src/utils/data-quality.ts +24 -36
  148. package/src/utils/exit-triggers.ts +91 -96
  149. package/src/utils/field-timing.ts +94 -79
  150. package/src/utils/filter-predicates.ts +13 -9
  151. package/src/utils/flatfile-importer.ts +94 -64
  152. package/src/utils/greeks-decomposition.ts +152 -100
  153. package/src/utils/iv-solver-pool.ts +55 -25
  154. package/src/utils/iv-solver-worker.ts +5 -5
  155. package/src/utils/market-enricher.ts +528 -497
  156. package/src/utils/market-importer.ts +31 -12
  157. package/src/utils/market-provider.ts +21 -23
  158. package/src/utils/massive-tier.ts +5 -7
  159. package/src/utils/migrate-option-data-helpers.ts +2 -8
  160. package/src/utils/option-quote-greeks.ts +25 -31
  161. package/src/utils/option-time.ts +4 -8
  162. package/src/utils/output-formatter.ts +1 -4
  163. package/src/utils/provider-capabilities.ts +1 -4
  164. package/src/utils/providers/massive.ts +59 -93
  165. package/src/utils/providers/thetadata/backfill.ts +14 -23
  166. package/src/utils/providers/thetadata/client.ts +12 -8
  167. package/src/utils/providers/thetadata/decode.ts +2 -20
  168. package/src/utils/providers/thetadata/endpoints.ts +32 -40
  169. package/src/utils/providers/thetadata/join.ts +11 -10
  170. package/src/utils/providers/thetadata/proto.ts +12 -10
  171. package/src/utils/providers/thetadata/quote-mid-greeks.ts +5 -5
  172. package/src/utils/providers/thetadata.ts +11 -10
  173. package/src/utils/quote-enricher.ts +4 -4
  174. package/src/utils/quote-parquet-projection.ts +3 -11
  175. package/src/utils/sample-date-selector.ts +3 -5
  176. package/src/utils/schema-metadata.ts +102 -70
  177. package/src/utils/ticker.ts +5 -9
  178. package/src/utils/trade-replay.ts +77 -68
  179. package/dist/chunk-4BLCXNQ6.js.map +0 -1
  180. package/dist/chunk-BKQ4PM4Y.js.map +0 -1
  181. package/dist/chunk-FGZH632F.js.map +0 -1
  182. package/dist/chunk-LDKTV7GW.js.map +0 -1
  183. package/dist/chunk-PRAYH3RT.js.map +0 -1
  184. package/dist/chunk-W2PP3LEH.js.map +0 -1
  185. package/dist/chunk-XXYOUIZY.js.map +0 -1
  186. package/dist/daily-log-processor-6MWJ23JK.js +0 -9
  187. package/dist/market-provider-VDRJUEF2.js +0 -16
  188. package/dist/trade-processor-NHU4VWRX.js +0 -9
  189. package/server/chunk-5EBXHT6C.js.map +0 -1
  190. package/server/chunk-72GKJE2U.js.map +0 -1
  191. package/server/chunk-CSDVJPBB.js.map +0 -1
  192. package/server/chunk-FBNDMCT5.js.map +0 -1
  193. package/server/chunk-NRFXAJF7.js.map +0 -1
  194. package/server/chunk-NZO6PT64.js.map +0 -1
  195. package/server/chunk-PNKG7RY7.js.map +0 -1
  196. package/server/chunk-WA5AAPCH.js.map +0 -1
  197. package/server/config-6IZXEFEX.js.map +0 -1
  198. package/server/daily-log-processor-Y3PVSVBM.js +0 -10
  199. package/server/market-provider-VOYYVYWT.js +0 -17
  200. package/server/trade-processor-JWVS37KM.js +0 -10
  201. /package/dist/{daily-log-processor-6MWJ23JK.js.map → daily-log-processor-BY3ISY6K.js.map} +0 -0
  202. /package/dist/{daily-logs-store-D3QC2XPV.js.map → daily-logs-store-3C3OTGPK.js.map} +0 -0
  203. /package/dist/{market-provider-VDRJUEF2.js.map → market-provider-H3ARIVZ4.js.map} +0 -0
  204. /package/dist/{proto-3HZTCWK4.js.map → proto-V23GIZG2.js.map} +0 -0
  205. /package/dist/{sync-PO4IPCYV.js.map → sync-EVLKB3ZJ.js.map} +0 -0
  206. /package/dist/{trade-processor-NHU4VWRX.js.map → trade-processor-EYA3I3XB.js.map} +0 -0
  207. /package/dist/{trades-store-LOL3FQSF.js.map → trades-store-R3NL25T2.js.map} +0 -0
  208. /package/server/{daily-log-processor-Y3PVSVBM.js.map → daily-log-processor-ENEUT22A.js.map} +0 -0
  209. /package/server/{daily-logs-store-NU7M4L5C.js.map → daily-logs-store-YFNXTB7J.js.map} +0 -0
  210. /package/server/{market-provider-VOYYVYWT.js.map → market-provider-B437HKLW.js.map} +0 -0
  211. /package/server/{proto-DWRZJO4E.js.map → proto-YZ4TLX2N.js.map} +0 -0
  212. /package/server/{sync-QFI5L7S7.js.map → sync-V25UQJA3.js.map} +0 -0
  213. /package/server/{trade-processor-JWVS37KM.js.map → trade-processor-L3PIQ5TG.js.map} +0 -0
  214. /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; // join enriched_context (VIX family cross-ticker fields)
22
- includeOhlcv?: boolean; // join spot daily for OHLCV (avoids double-storing OHLCV)
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
- `tickers=${occUnion.size} rows=${rows.length} queryMs=${Date.now() - queryStart}`,
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) && b.open > 0 &&
59
- Number.isFinite(b.high) && b.high > 0 &&
60
- Number.isFinite(b.low) && b.low > 0 &&
61
- Number.isFinite(b.close)&& b.close> 0,
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
- ticker: string,
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", "roots": ["SPX", "SPXW", "SPXQ"] },
5
- { "underlying": "QQQ", "roots": ["QQQ", "QQQX"] },
6
- { "underlying": "VIX", "roots": ["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", "roots": ["ES"] },
10
- { "underlying": "NDX", "roots": ["NDX", "NDXP"] },
11
- { "underlying": "RUT", "roots": ["RUT", "RUTW"] }
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
  });