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
@@ -8,11 +8,7 @@ import {
8
8
  OPTION_QUOTE_GREEKS_RATE_TYPE,
9
9
  OPTION_QUOTE_GREEKS_REVISION,
10
10
  } from "../../option-quote-greeks.ts";
11
- import type {
12
- ThetaFirstOrderGreekRow,
13
- ThetaQuoteRow,
14
- ThetaRight,
15
- } from "./types.ts";
11
+ import type { ThetaFirstOrderGreekRow, ThetaQuoteRow, ThetaRight } from "./types.ts";
16
12
 
17
13
  export interface ThetaJoinedQuoteRow extends BulkQuoteRow {
18
14
  greeks_revision?: number | null;
@@ -76,10 +72,12 @@ type CompleteThetaFirstOrderGreekRow = ThetaFirstOrderGreekRow & {
76
72
  function hasProviderFirstOrderGreeks(
77
73
  row: ThetaFirstOrderGreekRow,
78
74
  ): row is CompleteThetaFirstOrderGreekRow {
79
- return isFiniteNumber(row.delta)
80
- && isFiniteNumber(row.theta)
81
- && isFiniteNumber(row.vega)
82
- && isFiniteNumber(row.iv);
75
+ return (
76
+ isFiniteNumber(row.delta) &&
77
+ isFiniteNumber(row.theta) &&
78
+ isFiniteNumber(row.vega) &&
79
+ isFiniteNumber(row.iv)
80
+ );
83
81
  }
84
82
 
85
83
  function splitTimestamp(timestamp: string): { date: string; time: string } {
@@ -87,7 +85,10 @@ function splitTimestamp(timestamp: string): { date: string; time: string } {
87
85
  return { date, time: time.slice(0, 5) };
88
86
  }
89
87
 
90
- function computeGamma(row: ThetaQuoteRow, greek: ThetaFirstOrderGreekRow): {
88
+ function computeGamma(
89
+ row: ThetaQuoteRow,
90
+ greek: ThetaFirstOrderGreekRow,
91
+ ): {
91
92
  gamma: number | null;
92
93
  rateValue: number | null;
93
94
  } {
@@ -9,16 +9,18 @@ import type { Root } from "protobufjs";
9
9
  const moduleDir = dirname(fileURLToPath(import.meta.url));
10
10
 
11
11
  function protoCandidates(): string[] {
12
- return Array.from(new Set([
13
- resolve(moduleDir, "mdds.proto"),
14
- resolve(moduleDir, "../../../mdds.proto"),
15
- resolve(moduleDir, "../../../../server/mdds.proto"),
16
- resolve(moduleDir, "../../../../dist/mdds.proto"),
17
- resolve(process.cwd(), "packages/mcp-server/src/utils/providers/thetadata/mdds.proto"),
18
- resolve(process.cwd(), "src/utils/providers/thetadata/mdds.proto"),
19
- resolve(process.cwd(), "server/mdds.proto"),
20
- resolve(process.cwd(), "dist/mdds.proto"),
21
- ]));
12
+ return Array.from(
13
+ new Set([
14
+ resolve(moduleDir, "mdds.proto"),
15
+ resolve(moduleDir, "../../../mdds.proto"),
16
+ resolve(moduleDir, "../../../../server/mdds.proto"),
17
+ resolve(moduleDir, "../../../../dist/mdds.proto"),
18
+ resolve(process.cwd(), "packages/mcp-server/src/utils/providers/thetadata/mdds.proto"),
19
+ resolve(process.cwd(), "src/utils/providers/thetadata/mdds.proto"),
20
+ resolve(process.cwd(), "server/mdds.proto"),
21
+ resolve(process.cwd(), "dist/mdds.proto"),
22
+ ]),
23
+ );
22
24
  }
23
25
 
24
26
  export function resolveMddsProtoPath(): string {
@@ -53,11 +53,11 @@ export function computeThetaQuoteMidGreekRow(params: {
53
53
  OPTION_QUOTE_MID_GREEKS_DIVIDEND_YIELD,
54
54
  );
55
55
  if (
56
- !isFiniteNumber(greeks.delta)
57
- || !isFiniteNumber(greeks.gamma)
58
- || !isFiniteNumber(greeks.theta)
59
- || !isFiniteNumber(greeks.vega)
60
- || !isFiniteNumber(greeks.iv)
56
+ !isFiniteNumber(greeks.delta) ||
57
+ !isFiniteNumber(greeks.gamma) ||
58
+ !isFiniteNumber(greeks.theta) ||
59
+ !isFiniteNumber(greeks.vega) ||
60
+ !isFiniteNumber(greeks.iv)
61
61
  ) {
62
62
  return null;
63
63
  }
@@ -185,10 +185,12 @@ function isGreekForBulkContract(
185
185
  right: ThetaRight,
186
186
  contract: ThetaContractListRow,
187
187
  ): boolean {
188
- return row.symbol.toUpperCase() === root.toUpperCase()
189
- && row.expiration === contract.expiration
190
- && formatStrike(row.strike) === formatStrike(contract.strike)
191
- && row.right === right;
188
+ return (
189
+ row.symbol.toUpperCase() === root.toUpperCase() &&
190
+ row.expiration === contract.expiration &&
191
+ formatStrike(row.strike) === formatStrike(contract.strike) &&
192
+ row.right === right
193
+ );
192
194
  }
193
195
 
194
196
  function toMinuteQuote(row: BulkQuoteRow & { greeks_revision?: number | null }): MinuteQuote {
@@ -524,13 +526,12 @@ export class ThetaDataProvider implements MarketDataProvider {
524
526
  date,
525
527
  interval: "1m",
526
528
  } as const;
527
- const [quotes, bandGreeks]: [ThetaQuoteRow[], ThetaFirstOrderGreekRow[]] =
528
- await Promise.all([
529
- this.quoteEndpoint(this.client, request),
530
- this.fetchBulkGreekBand(root, contract.expiration, date, greekBandCache),
531
- ]);
529
+ const [quotes, bandGreeks]: [ThetaQuoteRow[], ThetaFirstOrderGreekRow[]] = await Promise.all([
530
+ this.quoteEndpoint(this.client, request),
531
+ this.fetchBulkGreekBand(root, contract.expiration, date, greekBandCache),
532
+ ]);
532
533
  const contractBandGreeks = bandGreeks.filter((row) =>
533
- isGreekForBulkContract(row, root, right, contract)
534
+ isGreekForBulkContract(row, root, right, contract),
534
535
  );
535
536
  const bandJoined = joinThetaQuotesAndFirstOrderGreeks({
536
537
  quotes,
@@ -39,7 +39,7 @@ export interface EnrichmentPlanItem {
39
39
  */
40
40
  export interface QuoteEnrichmentResult {
41
41
  tickersProcessed: number;
42
- tickersSkipped: number; // already dense
42
+ tickersSkipped: number; // already dense
43
43
  rowsWritten: number;
44
44
  errors: Array<{ ticker: string; date: string; error: string }>;
45
45
  unsupportedReason?: string;
@@ -47,7 +47,7 @@ export interface QuoteEnrichmentResult {
47
47
 
48
48
  export interface EnrichmentPlanInput {
49
49
  tickers: Array<{ ticker: string; fromDate: string; toDate: string }>;
50
- existingCoverage: Map<string, number>; // "ticker:date" → barCount
50
+ existingCoverage: Map<string, number>; // "ticker:date" → barCount
51
51
  providerSupportsQuotes: boolean;
52
52
  }
53
53
 
@@ -108,8 +108,8 @@ export function buildEnrichmentPlan(input: EnrichmentPlanInput): EnrichmentPlanI
108
108
  */
109
109
  function expandDateRange(fromDate: string, toDate: string): string[] {
110
110
  const dates: string[] = [];
111
- const from = new Date(fromDate + 'T00:00:00Z');
112
- const to = new Date(toDate + 'T00:00:00Z');
111
+ const from = new Date(fromDate + "T00:00:00Z");
112
+ const to = new Date(toDate + "T00:00:00Z");
113
113
  const current = new Date(from);
114
114
  while (current <= to) {
115
115
  dates.push(current.toISOString().slice(0, 10));
@@ -105,14 +105,9 @@ export function quoteParquetGreekProjection(
105
105
  * Read path (`quoteParquetGreekProjection` above) stays untouched so
106
106
  * existing DOUBLE partitions continue to read without widening cost.
107
107
  */
108
- export function quoteParquetGreekWriteProjection(
109
- columns: ParquetColumnSet,
110
- alias = "q",
111
- ): string {
108
+ export function quoteParquetGreekWriteProjection(columns: ParquetColumnSet, alias = "q"): string {
112
109
  const castReal = (name: string) =>
113
- hasColumn(columns, name)
114
- ? `CAST(${alias}.${name} AS REAL)`
115
- : `NULL::REAL`;
110
+ hasColumn(columns, name) ? `CAST(${alias}.${name} AS REAL)` : `NULL::REAL`;
116
111
  return [
117
112
  `${castReal("delta")} AS delta`,
118
113
  `${castReal("gamma")} AS gamma`,
@@ -127,10 +122,7 @@ export function quoteParquetGreekWriteProjection(
127
122
  ].join(",\n ");
128
123
  }
129
124
 
130
- export function quoteParquetCanonicalProjection(
131
- columns: ParquetColumnSet,
132
- alias = "q",
133
- ): string {
125
+ export function quoteParquetCanonicalProjection(columns: ParquetColumnSet, alias = "q"): string {
134
126
  return [
135
127
  `${quoteParquetColumnExpr(columns, alias, "underlying", "VARCHAR")} AS underlying`,
136
128
  `${quoteParquetColumnExpr(columns, alias, "date", "VARCHAR")} AS date`,
@@ -118,9 +118,7 @@ export function selectVerificationSampleDates(
118
118
  ...PHASE_5_KNOWN_EVENTS.map((s) => s.date),
119
119
  ...PHASE_5_STRUCTURAL_DATES.map((s) => s.date),
120
120
  ]);
121
- const candidates = enumerateWeekdays(fromDate, toDate).filter(
122
- (d) => !selectedSet.has(d),
123
- );
121
+ const candidates = enumerateWeekdays(fromDate, toDate).filter((d) => !selectedSet.has(d));
124
122
 
125
123
  const random: SampleDate[] = [];
126
124
  const pool = [...candidates];
@@ -129,7 +127,7 @@ export function selectVerificationSampleDates(
129
127
  random.push({ date: pool.splice(idx, 1)[0], category: "random" });
130
128
  }
131
129
 
132
- return [...PHASE_5_KNOWN_EVENTS, ...PHASE_5_STRUCTURAL_DATES, ...random].sort(
133
- (a, b) => a.date.localeCompare(b.date),
130
+ return [...PHASE_5_KNOWN_EVENTS, ...PHASE_5_STRUCTURAL_DATES, ...random].sort((a, b) =>
131
+ a.date.localeCompare(b.date),
134
132
  );
135
133
  }
@@ -30,7 +30,7 @@ export interface ColumnDescription {
30
30
  * - 'static': Calendar/metadata facts known before the day (Day_of_Week, Month, Is_Opex)
31
31
  * Only applicable to market.enriched and market.enriched_context columns. Omit for non-market tables.
32
32
  */
33
- timing?: 'open' | 'close' | 'static';
33
+ timing?: "open" | "close" | "static";
34
34
  }
35
35
 
36
36
  export interface TableDescription {
@@ -164,7 +164,8 @@ export const SCHEMA_DESCRIPTIONS: SchemaMetadata = {
164
164
  hypothesis: true,
165
165
  },
166
166
  legs: {
167
- description: "Option legs description with strikes (e.g., 'SPY 450P/445P') - compare with trade_data.legs to identify strike differences",
167
+ description:
168
+ "Option legs description with strikes (e.g., 'SPY 450P/445P') - compare with trade_data.legs to identify strike differences",
168
169
  hypothesis: true,
169
170
  },
170
171
  initial_premium: {
@@ -221,174 +222,182 @@ export const SCHEMA_DESCRIPTIONS: SchemaMetadata = {
221
222
  hypothesis: true,
222
223
  },
223
224
  date: {
224
- description: "Trading date (VARCHAR, format YYYY-MM-DD). Composite primary key with ticker.",
225
+ description:
226
+ "Trading date (VARCHAR, format YYYY-MM-DD). Composite primary key with ticker.",
225
227
  hypothesis: true,
226
228
  },
227
229
  // Raw OHLCV
228
230
  open: {
229
231
  description: "Underlying open price",
230
232
  hypothesis: false,
231
- timing: 'open',
233
+ timing: "open",
232
234
  },
233
235
  high: {
234
236
  description: "Underlying high price",
235
237
  hypothesis: false,
236
- timing: 'close',
238
+ timing: "close",
237
239
  },
238
240
  low: {
239
241
  description: "Underlying low price",
240
242
  hypothesis: false,
241
- timing: 'close',
243
+ timing: "close",
242
244
  },
243
245
  close: {
244
246
  description: "Underlying close price",
245
247
  hypothesis: false,
246
- timing: 'close',
248
+ timing: "close",
247
249
  },
248
250
  Prior_Close: {
249
251
  description: "Previous day's close price",
250
252
  hypothesis: false,
251
- timing: 'open',
253
+ timing: "open",
252
254
  },
253
255
  // Tier 1 enrichment — open-known
254
256
  Gap_Pct: {
255
257
  description: "Overnight gap percentage ((Open - Prior_Close) / Prior_Close * 100)",
256
258
  hypothesis: true,
257
- timing: 'open',
259
+ timing: "open",
258
260
  },
259
261
  Prev_Return_Pct: {
260
262
  description: "Previous day's total return percentage (prior close to prior close)",
261
263
  hypothesis: true,
262
- timing: 'open',
264
+ timing: "open",
263
265
  },
264
266
  Prior_Range_vs_ATR: {
265
- description: "Prior trading day's (high - low) / ATR ratio, measures prior day's range relative to average true range",
267
+ description:
268
+ "Prior trading day's (high - low) / ATR ratio, measures prior day's range relative to average true range",
266
269
  hypothesis: true,
267
- timing: 'open',
270
+ timing: "open",
268
271
  },
269
272
  // Tier 1 enrichment — close-derived
270
273
  ATR_Pct: {
271
274
  description: "Average True Range as percentage of price (14-day Wilder smoothing)",
272
275
  hypothesis: true,
273
- timing: 'close',
276
+ timing: "close",
274
277
  },
275
278
  RSI_14: {
276
279
  description: "14-day RSI (0-100, >70 overbought, <30 oversold)",
277
280
  hypothesis: true,
278
- timing: 'close',
281
+ timing: "close",
279
282
  },
280
283
  Price_vs_EMA21_Pct: {
281
284
  description: "Price vs 21-day EMA as percentage ((close - EMA21) / EMA21 * 100)",
282
285
  hypothesis: true,
283
- timing: 'close',
286
+ timing: "close",
284
287
  },
285
288
  Price_vs_SMA50_Pct: {
286
289
  description: "Price vs 50-day SMA as percentage ((close - SMA50) / SMA50 * 100)",
287
290
  hypothesis: true,
288
- timing: 'close',
291
+ timing: "close",
289
292
  },
290
293
  Realized_Vol_5D: {
291
294
  description: "5-day realized volatility (annualized standard deviation of log returns)",
292
295
  hypothesis: true,
293
- timing: 'close',
296
+ timing: "close",
294
297
  },
295
298
  Realized_Vol_20D: {
296
- description: "20-day realized volatility (annualized standard deviation of log returns)",
299
+ description:
300
+ "20-day realized volatility (annualized standard deviation of log returns)",
297
301
  hypothesis: true,
298
- timing: 'close',
302
+ timing: "close",
299
303
  },
300
304
  Return_5D: {
301
305
  description: "5-day cumulative return percentage",
302
306
  hypothesis: true,
303
- timing: 'close',
307
+ timing: "close",
304
308
  },
305
309
  Return_20D: {
306
310
  description: "20-day cumulative return percentage",
307
311
  hypothesis: true,
308
- timing: 'close',
312
+ timing: "close",
309
313
  },
310
314
  Intraday_Range_Pct: {
311
315
  description: "Intraday range as percentage ((High - Low) / Open * 100)",
312
316
  hypothesis: true,
313
- timing: 'close',
317
+ timing: "close",
314
318
  },
315
319
  Intraday_Return_Pct: {
316
320
  description: "Open to close return percentage ((Close - Open) / Open * 100)",
317
321
  hypothesis: true,
318
- timing: 'close',
322
+ timing: "close",
319
323
  },
320
324
  Close_Position_In_Range: {
321
325
  description: "Where close is in day's range (0 = low, 1 = high)",
322
326
  hypothesis: true,
323
- timing: 'close',
327
+ timing: "close",
324
328
  },
325
329
  Gap_Filled: {
326
330
  description: "Whether overnight gap was filled (1 = yes, 0 = no)",
327
331
  hypothesis: true,
328
- timing: 'close',
332
+ timing: "close",
329
333
  },
330
334
  Consecutive_Days: {
331
335
  description: "Consecutive up/down days (positive=up, negative=down)",
332
336
  hypothesis: true,
333
- timing: 'close',
337
+ timing: "close",
334
338
  },
335
339
  // Tier 3 intraday timing (columns exist in schema, enrichment deferred)
336
340
  High_Time: {
337
341
  description: "Time of day high as decimal hours (e.g., 10.5 = 10:30 AM ET)",
338
342
  hypothesis: true,
339
- timing: 'close',
343
+ timing: "close",
340
344
  },
341
345
  Low_Time: {
342
346
  description: "Time of day low as decimal hours (e.g., 14.25 = 2:15 PM ET)",
343
347
  hypothesis: true,
344
- timing: 'close',
348
+ timing: "close",
345
349
  },
346
350
  High_Before_Low: {
347
351
  description: "Did high occur before low? (1=yes, 0=no)",
348
352
  hypothesis: true,
349
- timing: 'close',
353
+ timing: "close",
350
354
  },
351
355
  Reversal_Type: {
352
- description: "Reversal pattern type (1=morning reversal up, -1=morning reversal down, 0=trend day)",
356
+ description:
357
+ "Reversal pattern type (1=morning reversal up, -1=morning reversal down, 0=trend day)",
353
358
  hypothesis: true,
354
- timing: 'close',
359
+ timing: "close",
355
360
  },
356
361
  Opening_Drive_Strength: {
357
- description: "First-30-min range / full-day range ratio (0-1); higher = strong opening drive",
362
+ description:
363
+ "First-30-min range / full-day range ratio (0-1); higher = strong opening drive",
358
364
  hypothesis: true,
359
- timing: 'close',
365
+ timing: "close",
360
366
  },
361
367
  Intraday_Realized_Vol: {
362
- description: "Annualized realized volatility from intraday bar returns (decimal, e.g., 0.15 = 15%)",
368
+ description:
369
+ "Annualized realized volatility from intraday bar returns (decimal, e.g., 0.15 = 15%)",
363
370
  hypothesis: true,
364
- timing: 'close',
371
+ timing: "close",
365
372
  },
366
373
  // Calendar fields — static
367
374
  Day_of_Week: {
368
375
  description: "Day of week (2=Monday through 6=Friday)",
369
376
  hypothesis: true,
370
- timing: 'static',
377
+ timing: "static",
371
378
  },
372
379
  Month: {
373
380
  description: "Month number (1-12)",
374
381
  hypothesis: true,
375
- timing: 'static',
382
+ timing: "static",
376
383
  },
377
384
  Is_Opex: {
378
385
  description: "Options expiration day flag (1=opex, 0=not)",
379
386
  hypothesis: true,
380
- timing: 'static',
387
+ timing: "static",
381
388
  },
382
389
  // VIX-family ticker IVR/IVP (populated for VIX, VIX9D, VIX3M, etc.)
383
390
  ivr: {
384
- description: "Implied Volatility Rank (252-day): where current close sits in range (0=min, 100=max). Populated for VIX-family tickers only.",
391
+ description:
392
+ "Implied Volatility Rank (252-day): where current close sits in range (0=min, 100=max). Populated for VIX-family tickers only.",
385
393
  hypothesis: true,
386
- timing: 'close',
394
+ timing: "close",
387
395
  },
388
396
  ivp: {
389
- description: "Implied Volatility Percentile (252-day): percentage of prior 251 trading days where close was at or below current level (0-100). Populated for VIX-family tickers only.",
397
+ description:
398
+ "Implied Volatility Percentile (252-day): percentage of prior 251 trading days where close was at or below current level (0-100). Populated for VIX-family tickers only.",
390
399
  hypothesis: true,
391
- timing: 'close',
400
+ timing: "close",
392
401
  },
393
402
  },
394
403
  },
@@ -402,29 +411,33 @@ export const SCHEMA_DESCRIPTIONS: SchemaMetadata = {
402
411
  hypothesis: true,
403
412
  },
404
413
  Vol_Regime: {
405
- description: "Volatility regime classification based on VIX close (1=very low <10, 2=low 10-15, 3=normal 15-20, 4=elevated 20-25, 5=high 25-30, 6=extreme >30)",
414
+ description:
415
+ "Volatility regime classification based on VIX close (1=very low <10, 2=low 10-15, 3=normal 15-20, 4=elevated 20-25, 5=high 25-30, 6=extreme >30)",
406
416
  hypothesis: true,
407
- timing: 'close',
417
+ timing: "close",
408
418
  },
409
419
  Term_Structure_State: {
410
- description: "VIX term structure state based on VIX9D/VIX ratio (-1=backwardation/inverted, 0=flat, 1=contango/normal). NULL when VIX9D data is absent.",
420
+ description:
421
+ "VIX term structure state based on VIX9D/VIX ratio (-1=backwardation/inverted, 0=flat, 1=contango/normal). NULL when VIX9D data is absent.",
411
422
  hypothesis: true,
412
- timing: 'close',
423
+ timing: "close",
413
424
  },
414
425
  Trend_Direction: {
415
- description: "Trend direction classification based on 20-day return: up (>1%), down (<-1%), flat (-1% to 1%). NULL if Return_20D unavailable.",
426
+ description:
427
+ "Trend direction classification based on 20-day return: up (>1%), down (<-1%), flat (-1% to 1%). NULL if Return_20D unavailable.",
416
428
  hypothesis: true,
417
- timing: 'close',
429
+ timing: "close",
418
430
  },
419
431
  VIX_Spike_Pct: {
420
432
  description: "VIX spike from open to high as percentage",
421
433
  hypothesis: true,
422
- timing: 'close',
434
+ timing: "close",
423
435
  },
424
436
  VIX_Gap_Pct: {
425
- description: "VIX overnight gap percentage ((VIX_Open - prior VIX_Close) / prior VIX_Close * 100)",
437
+ description:
438
+ "VIX overnight gap percentage ((VIX_Open - prior VIX_Close) / prior VIX_Close * 100)",
426
439
  hypothesis: true,
427
- timing: 'open',
440
+ timing: "open",
428
441
  },
429
442
  },
430
443
  },
@@ -434,15 +447,18 @@ export const SCHEMA_DESCRIPTIONS: SchemaMetadata = {
434
447
  keyColumns: ["ticker", "date", "time"],
435
448
  columns: {
436
449
  ticker: {
437
- description: "Underlying ticker symbol (part of composite primary key with date and time).",
450
+ description:
451
+ "Underlying ticker symbol (part of composite primary key with date and time).",
438
452
  hypothesis: true,
439
453
  },
440
454
  date: {
441
- description: "Trading date (VARCHAR, format YYYY-MM-DD). Part of composite primary key.",
455
+ description:
456
+ "Trading date (VARCHAR, format YYYY-MM-DD). Part of composite primary key.",
442
457
  hypothesis: true,
443
458
  },
444
459
  time: {
445
- description: "Bar time in HH:MM Eastern Time format (e.g., '09:30', '10:00'). Part of composite primary key.",
460
+ description:
461
+ "Bar time in HH:MM Eastern Time format (e.g., '09:30', '10:00'). Part of composite primary key.",
446
462
  hypothesis: false,
447
463
  },
448
464
  open: {
@@ -587,19 +603,23 @@ export const SCHEMA_DESCRIPTIONS: SchemaMetadata = {
587
603
  hypothesis: false,
588
604
  },
589
605
  delta: {
590
- description: "Option delta for the minute when available from provider data or computed fallback.",
606
+ description:
607
+ "Option delta for the minute when available from provider data or computed fallback.",
591
608
  hypothesis: true,
592
609
  },
593
610
  gamma: {
594
- description: "Option gamma for the minute when available from provider data or computed fallback.",
611
+ description:
612
+ "Option gamma for the minute when available from provider data or computed fallback.",
595
613
  hypothesis: false,
596
614
  },
597
615
  theta: {
598
- description: "Option theta for the minute when available from provider data or computed fallback.",
616
+ description:
617
+ "Option theta for the minute when available from provider data or computed fallback.",
599
618
  hypothesis: false,
600
619
  },
601
620
  vega: {
602
- description: "Option vega (per 1% IV move) for the minute when available from provider data or computed fallback.",
621
+ description:
622
+ "Option vega (per 1% IV move) for the minute when available from provider data or computed fallback.",
603
623
  hypothesis: false,
604
624
  },
605
625
  iv: {
@@ -607,15 +627,18 @@ export const SCHEMA_DESCRIPTIONS: SchemaMetadata = {
607
627
  hypothesis: true,
608
628
  },
609
629
  greeks_source: {
610
- description: "Origin of the stored minute greeks: provider-native or computed fallback.",
630
+ description:
631
+ "Origin of the stored minute greeks: provider-native or computed fallback.",
611
632
  hypothesis: false,
612
633
  },
613
634
  greeks_revision: {
614
- description: "Computation revision for stored computed greeks; null for provider-native values.",
635
+ description:
636
+ "Computation revision for stored computed greeks; null for provider-native values.",
615
637
  hypothesis: false,
616
638
  },
617
639
  rate_type: {
618
- description: "Interest-rate curve or rate label used by the provider or computed-greeks path.",
640
+ description:
641
+ "Interest-rate curve or rate label used by the provider or computed-greeks path.",
619
642
  hypothesis: false,
620
643
  },
621
644
  rate_value: {
@@ -623,7 +646,8 @@ export const SCHEMA_DESCRIPTIONS: SchemaMetadata = {
623
646
  hypothesis: false,
624
647
  },
625
648
  gamma_source: {
626
- description: "Provenance label for the stored gamma value when it differs from the broader greeks source.",
649
+ description:
650
+ "Provenance label for the stored gamma value when it differs from the broader greeks source.",
627
651
  hypothesis: false,
628
652
  },
629
653
  },
@@ -716,7 +740,8 @@ ORDER BY t.date_opened`,
716
740
  ],
717
741
  joins: [
718
742
  {
719
- description: "Trade P&L with market context (lag-aware: multi-table JOIN before LAG for correctness)",
743
+ description:
744
+ "Trade P&L with market context (lag-aware: multi-table JOIN before LAG for correctness)",
720
745
  sql: `WITH joined AS (
721
746
  SELECT d.ticker, d.date,
722
747
  d.Gap_Pct, d.Prior_Close, d.Prev_Return_Pct,
@@ -748,7 +773,8 @@ WHERE t.block_id = 'my-block'
748
773
  ORDER BY t.date_opened DESC`,
749
774
  },
750
775
  {
751
- description: "Trades with ORB context (opening range breakout from minute bars in market.spot)",
776
+ description:
777
+ "Trades with ORB context (opening range breakout from minute bars in market.spot)",
752
778
  sql: `WITH orb_range AS (
753
779
  SELECT ticker, date,
754
780
  MAX(high) AS ORB_High,
@@ -775,7 +801,8 @@ WHERE t.block_id = 'my-block'
775
801
  ORDER BY t.date_opened`,
776
802
  },
777
803
  {
778
- description: "VIX intraday data for a specific date (VIX bars are in market.spot with ticker='VIX')",
804
+ description:
805
+ "VIX intraday data for a specific date (VIX bars are in market.spot with ticker='VIX')",
779
806
  sql: `SELECT time, open, high, low, close
780
807
  FROM market.spot
781
808
  WHERE ticker = 'VIX'
@@ -783,7 +810,8 @@ WHERE ticker = 'VIX'
783
810
  ORDER BY time`,
784
811
  },
785
812
  {
786
- description: "Trades on reversal days (lag-aware: Reversal_Type uses prior trading day via LAG)",
813
+ description:
814
+ "Trades on reversal days (lag-aware: Reversal_Type uses prior trading day via LAG)",
787
815
  sql: `WITH joined AS (
788
816
  SELECT d.ticker, d.date,
789
817
  d.High_Before_Low, d.Reversal_Type
@@ -805,7 +833,8 @@ WHERE m.prev_Reversal_Type != 0
805
833
  AND t.block_id = 'my-block'`,
806
834
  },
807
835
  {
808
- description: "Enrich trades with market data (lag-aware: use enrich_trades tool for full enrichment)",
836
+ description:
837
+ "Enrich trades with market data (lag-aware: use enrich_trades tool for full enrichment)",
809
838
  sql: `WITH joined AS (
810
839
  SELECT d.ticker, d.date,
811
840
  d.Gap_Pct, d.Prior_Close,
@@ -833,7 +862,8 @@ WHERE t.block_id = 'my-block'`,
833
862
  ],
834
863
  hypothesis: [
835
864
  {
836
- description: "Win rate by VIX regime (lag-aware: uses prior day's Vol_Regime from market.enriched_context)",
865
+ description:
866
+ "Win rate by VIX regime (lag-aware: uses prior day's Vol_Regime from market.enriched_context)",
837
867
  sql: `WITH joined AS (
838
868
  SELECT d.ticker, d.date, cd.Vol_Regime
839
869
  FROM market.enriched d
@@ -872,7 +902,8 @@ GROUP BY d.Day_of_Week
872
902
  ORDER BY d.Day_of_Week`,
873
903
  },
874
904
  {
875
- description: "Performance by VIX term structure (lag-aware: uses prior day's Term_Structure_State from market.enriched_context)",
905
+ description:
906
+ "Performance by VIX term structure (lag-aware: uses prior day's Term_Structure_State from market.enriched_context)",
876
907
  sql: `WITH joined AS (
877
908
  SELECT d.ticker, d.date, cd.Term_Structure_State
878
909
  FROM market.enriched d
@@ -899,7 +930,8 @@ WHERE t.block_id = 'my-block'
899
930
  GROUP BY term_structure`,
900
931
  },
901
932
  {
902
- description: "Aggregate by VIX buckets (lag-aware: uses prior day's VIX close from market.spot_daily ticker='VIX')",
933
+ description:
934
+ "Aggregate by VIX buckets (lag-aware: uses prior day's VIX close from market.spot_daily ticker='VIX')",
903
935
  sql: `WITH joined AS (
904
936
  SELECT d.ticker, d.date, vix_s.close AS VIX_Close
905
937
  FROM market.enriched d