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
@@ -80,7 +80,7 @@ import {
80
80
  writeParquetPartition,
81
81
  writeQuoteMinutesPartition,
82
82
  writeSpotPartition
83
- } from "./chunk-4BLCXNQ6.js";
83
+ } from "./chunk-UBUC5A66.js";
84
84
  import {
85
85
  BACHELIER_DTE_THRESHOLD,
86
86
  IvSolverPool,
@@ -156,17 +156,17 @@ import {
156
156
  stockHistoryOhlc,
157
157
  thetaTimestampToEtMinute,
158
158
  toMassiveTicker
159
- } from "./chunk-XXYOUIZY.js";
159
+ } from "./chunk-VDU25Z6X.js";
160
160
  import {
161
161
  PortfolioStatsCalculator,
162
162
  calculateCorrelationMatrix,
163
163
  performTailRiskAnalysis
164
- } from "./chunk-QTTR7AAW.js";
165
- import "./chunk-PRAYH3RT.js";
166
- import "./chunk-W2PP3LEH.js";
167
- import "./chunk-BKQ4PM4Y.js";
168
- import "./chunk-LDKTV7GW.js";
169
- import "./chunk-FGZH632F.js";
164
+ } from "./chunk-C6LL746C.js";
165
+ import "./chunk-AP7IHUUR.js";
166
+ import "./chunk-SEUZYQGQ.js";
167
+ import "./chunk-6S37CXUA.js";
168
+ import "./chunk-27S67XW3.js";
169
+ import "./chunk-2E63THNI.js";
170
170
  import "./chunk-5WRI5ZAA.js";
171
171
 
172
172
  // src/utils/trading-dates.ts
@@ -189,9 +189,7 @@ function yesterdayET(now = /* @__PURE__ */ new Date()) {
189
189
  // src/tools/shared/filters.ts
190
190
  function filterByStrategy(trades, strategy) {
191
191
  if (!strategy) return trades;
192
- return trades.filter(
193
- (t) => t.strategy.toLowerCase() === strategy.toLowerCase()
194
- );
192
+ return trades.filter((t) => t.strategy.toLowerCase() === strategy.toLowerCase());
195
193
  }
196
194
  var DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
197
195
  function validateDateParam(date) {
@@ -798,10 +796,22 @@ var VIX_OHLCV_MAPPINGS = [
798
796
  { alias: "VIX_Low", tableAlias: "vix", sourceCol: "low", ticker: "VIX", timing: "close" },
799
797
  // VIX9D
800
798
  { alias: "VIX9D_Open", tableAlias: "vix9d", sourceCol: "open", ticker: "VIX9D", timing: "open" },
801
- { alias: "VIX9D_Close", tableAlias: "vix9d", sourceCol: "close", ticker: "VIX9D", timing: "close" },
799
+ {
800
+ alias: "VIX9D_Close",
801
+ tableAlias: "vix9d",
802
+ sourceCol: "close",
803
+ ticker: "VIX9D",
804
+ timing: "close"
805
+ },
802
806
  // VIX3M
803
807
  { alias: "VIX3M_Open", tableAlias: "vix3m", sourceCol: "open", ticker: "VIX3M", timing: "open" },
804
- { alias: "VIX3M_Close", tableAlias: "vix3m", sourceCol: "close", ticker: "VIX3M", timing: "close" }
808
+ {
809
+ alias: "VIX3M_Close",
810
+ tableAlias: "vix3m",
811
+ sourceCol: "close",
812
+ ticker: "VIX3M",
813
+ timing: "close"
814
+ }
805
815
  ];
806
816
  var VIX_ENRICHED_MAPPINGS = [
807
817
  { alias: "VIX_IVR", tableAlias: "evix", sourceCol: "ivr", ticker: "VIX", timing: "close" },
@@ -811,10 +821,7 @@ var VIX_ENRICHED_MAPPINGS = [
811
821
  { alias: "VIX3M_IVR", tableAlias: "evix3m", sourceCol: "ivr", ticker: "VIX3M", timing: "close" },
812
822
  { alias: "VIX3M_IVP", tableAlias: "evix3m", sourceCol: "ivp", ticker: "VIX3M", timing: "close" }
813
823
  ];
814
- var VIX_ALL_MAPPINGS = [
815
- ...VIX_OHLCV_MAPPINGS,
816
- ...VIX_ENRICHED_MAPPINGS
817
- ];
824
+ var VIX_ALL_MAPPINGS = [...VIX_OHLCV_MAPPINGS, ...VIX_ENRICHED_MAPPINGS];
818
825
  var VIX_TICKER_ALIASES = [...new Set(VIX_OHLCV_MAPPINGS.map((m) => m.tableAlias))];
819
826
  var VIX_TICKER_FOR_ALIAS = Object.fromEntries([
820
827
  ...VIX_OHLCV_MAPPINGS.map((m) => [m.tableAlias, m.ticker]),
@@ -851,9 +858,7 @@ var CLOSE_KNOWN_FIELDS = /* @__PURE__ */ new Set([
851
858
  ...DAILY_CLOSE_FIELDS,
852
859
  ...CONTEXT_CLOSE_FIELDS
853
860
  ]);
854
- var STATIC_FIELDS = /* @__PURE__ */ new Set([
855
- ...DAILY_STATIC_FIELDS
856
- ]);
861
+ var STATIC_FIELDS = /* @__PURE__ */ new Set([...DAILY_STATIC_FIELDS]);
857
862
  function buildVixJoinClause(tickerAliases, baseAlias = "d") {
858
863
  return tickerAliases.flatMap((alias) => {
859
864
  const ticker = VIX_TICKER_FOR_ALIAS[alias];
@@ -978,7 +983,11 @@ function buildOutcomeQuery(tradeDatesOrKeys) {
978
983
  if (typeof tradeDatesOrKeys[0] === "string") {
979
984
  return buildOutcomeQueryForDates(tradeDatesOrKeys, vixCloseCols, derivedCloseCols);
980
985
  }
981
- return buildOutcomeQueryForKeys(tradeDatesOrKeys, vixCloseCols, derivedCloseCols);
986
+ return buildOutcomeQueryForKeys(
987
+ tradeDatesOrKeys,
988
+ vixCloseCols,
989
+ derivedCloseCols
990
+ );
982
991
  }
983
992
  function buildTargetCloseCols(eAlias, sAlias) {
984
993
  return [...DAILY_CLOSE_FIELDS].map((f) => `${OHLCV_COLS.has(f) ? sAlias : eAlias}."${f}"`).join(", ");
@@ -1098,11 +1107,11 @@ function scoreDataQuality(input) {
1098
1107
  async function queryCoverage(stores, underlying, fromDate, toDate) {
1099
1108
  const spotCov = await stores.spot.getCoverage(underlying, fromDate, toDate);
1100
1109
  const quoteCov = await stores.quote.getCoverage(underlying, fromDate, toDate);
1101
- const dates = enumerateCoveredDates(spotCov.earliest, spotCov.latest).concat(enumerateCoveredDates(quoteCov.earliest, quoteCov.latest));
1102
- const uniqueDates = [...new Set(dates)].sort();
1103
- const quoteDateSet = new Set(
1110
+ const dates = enumerateCoveredDates(spotCov.earliest, spotCov.latest).concat(
1104
1111
  enumerateCoveredDates(quoteCov.earliest, quoteCov.latest)
1105
1112
  );
1113
+ const uniqueDates = [...new Set(dates)].sort();
1114
+ const quoteDateSet = new Set(enumerateCoveredDates(quoteCov.earliest, quoteCov.latest));
1106
1115
  if (uniqueDates.length === 0) {
1107
1116
  return {
1108
1117
  totalBars: 0,
@@ -1168,9 +1177,7 @@ function formatCoverageReport(tickerPattern, coverage) {
1168
1177
  }
1169
1178
  if (current) groups.push(current);
1170
1179
  for (const group of groups) {
1171
- const avgBars = group.barCounts.length > 0 ? Math.round(
1172
- group.barCounts.reduce((s, v) => s + v, 0) / group.barCounts.length
1173
- ) : 0;
1180
+ const avgBars = group.barCounts.length > 0 ? Math.round(group.barCounts.reduce((s, v) => s + v, 0) / group.barCounts.length) : 0;
1174
1181
  const rangeStr = group.fromDate === group.toDate ? group.fromDate : `${group.fromDate} to ${group.toDate}`;
1175
1182
  let detail;
1176
1183
  if (group.density === "dense") {
@@ -1445,7 +1452,9 @@ async function setupParquetWorkingTables(conn, dataDir) {
1445
1452
  const enrichedTickerGlob = path.join(enrichedDir, "ticker=*", "data.parquet");
1446
1453
  const enrichedContextPath = path.join(enrichedDir, "context", "data.parquet");
1447
1454
  if (existsSync(dailyPath)) {
1448
- await conn.run(`CREATE TEMP TABLE "${dailyTable}" AS SELECT * FROM read_parquet('${dailyPath}')`);
1455
+ await conn.run(
1456
+ `CREATE TEMP TABLE "${dailyTable}" AS SELECT * FROM read_parquet('${dailyPath}')`
1457
+ );
1449
1458
  await alignDailyWorkingTableColumns(conn, dailyTable);
1450
1459
  } else if (hasEnrichedTickerFiles(enrichedDir)) {
1451
1460
  await conn.run(
@@ -1460,9 +1469,7 @@ async function setupParquetWorkingTables(conn, dataDir) {
1460
1469
  }
1461
1470
  await alignDailyWorkingTableColumns(conn, dailyTable);
1462
1471
  } else {
1463
- await conn.run(
1464
- `CREATE TEMP TABLE "${dailyTable}" AS SELECT * FROM market.enriched WHERE 1=0`
1465
- );
1472
+ await conn.run(`CREATE TEMP TABLE "${dailyTable}" AS SELECT * FROM market.enriched WHERE 1=0`);
1466
1473
  for (const ohlcv of ["open", "high", "low", "close"]) {
1467
1474
  try {
1468
1475
  await conn.run(`ALTER TABLE "${dailyTable}" ADD COLUMN "${ohlcv}" DOUBLE`);
@@ -1485,7 +1492,9 @@ async function setupParquetWorkingTables(conn, dataDir) {
1485
1492
  } catch {
1486
1493
  }
1487
1494
  if (existsSync(dateContextPath)) {
1488
- await conn.run(`CREATE TEMP TABLE "${dateContextTable}" AS SELECT * FROM read_parquet('${dateContextPath}')`);
1495
+ await conn.run(
1496
+ `CREATE TEMP TABLE "${dateContextTable}" AS SELECT * FROM read_parquet('${dateContextPath}')`
1497
+ );
1489
1498
  } else if (existsSync(enrichedContextPath)) {
1490
1499
  await conn.run(
1491
1500
  `CREATE TEMP TABLE "${dateContextTable}" AS SELECT * FROM read_parquet('${enrichedContextPath}')`
@@ -1496,7 +1505,9 @@ async function setupParquetWorkingTables(conn, dataDir) {
1496
1505
  Trend_Direction VARCHAR, VIX_Spike_Pct DOUBLE, VIX_Gap_Pct DOUBLE
1497
1506
  )`);
1498
1507
  }
1499
- await conn.run(`CREATE UNIQUE INDEX "idx_${dateContextTable}_date" ON "${dateContextTable}"(date)`);
1508
+ await conn.run(
1509
+ `CREATE UNIQUE INDEX "idx_${dateContextTable}_date" ON "${dateContextTable}"(date)`
1510
+ );
1500
1511
  await conn.run(`CREATE UNIQUE INDEX "idx_${dailyTable}_pk" ON "${dailyTable}"(ticker, date)`);
1501
1512
  return { dailyTable, dateContextTable };
1502
1513
  }
@@ -1523,12 +1534,7 @@ async function flushEnrichedToParquet(conn, dataDir, ticker, tables) {
1523
1534
  targetPath: tickerFile,
1524
1535
  selectQuery: `SELECT ticker, date, ${enrichedColList} FROM "${tables.dailyTable}" WHERE ticker = '${ticker}' ORDER BY date`
1525
1536
  });
1526
- const contextFile = path.join(
1527
- resolveMarketDir(dataDir),
1528
- "enriched",
1529
- "context",
1530
- "data.parquet"
1531
- );
1537
+ const contextFile = path.join(resolveMarketDir(dataDir), "enriched", "context", "data.parquet");
1532
1538
  await writeParquetAtomic(conn, {
1533
1539
  targetPath: contextFile,
1534
1540
  selectQuery: `SELECT * FROM "${tables.dateContextTable}" ORDER BY date`
@@ -1564,7 +1570,9 @@ async function runTier2(conn, targets, spotStore) {
1564
1570
  const BATCH_SIZE = 500;
1565
1571
  for (let start = 0; start < bars.length; start += BATCH_SIZE) {
1566
1572
  const batch = bars.slice(start, start + BATCH_SIZE);
1567
- const placeholders = batch.map((_, i) => `($${i * 6 + 1},$${i * 6 + 2},$${i * 6 + 3},$${i * 6 + 4},$${i * 6 + 5},$${i * 6 + 6})`).join(",");
1573
+ const placeholders = batch.map(
1574
+ (_, i) => `($${i * 6 + 1},$${i * 6 + 2},$${i * 6 + 3},$${i * 6 + 4},$${i * 6 + 5},$${i * 6 + 6})`
1575
+ ).join(",");
1568
1576
  const params = batch.flatMap((b) => [b.ticker, b.date, b.open, b.high, b.low, b.close]);
1569
1577
  await conn.run(
1570
1578
  `INSERT INTO "${vixTempTable}" VALUES ${placeholders}`,
@@ -1696,12 +1704,21 @@ async function runTier2(conn, targets, spotStore) {
1696
1704
  };
1697
1705
  });
1698
1706
  const enrichedContext = computeVIXDerivedFields(contextRows);
1699
- const derivedCols = ["date", "Vol_Regime", "Term_Structure_State", "Trend_Direction", "VIX_Spike_Pct", "VIX_Gap_Pct"];
1707
+ const derivedCols = [
1708
+ "date",
1709
+ "Vol_Regime",
1710
+ "Term_Structure_State",
1711
+ "Trend_Direction",
1712
+ "VIX_Spike_Pct",
1713
+ "VIX_Gap_Pct"
1714
+ ];
1700
1715
  const BATCH_SIZE = 500;
1701
1716
  for (let start = 0; start < enrichedContext.length; start += BATCH_SIZE) {
1702
1717
  const batch = enrichedContext.slice(start, start + BATCH_SIZE);
1703
1718
  const placeholders = batch.map((_, rowIdx) => {
1704
- const params2 = derivedCols.map((__, colIdx) => `$${rowIdx * derivedCols.length + colIdx + 1}`);
1719
+ const params2 = derivedCols.map(
1720
+ (__, colIdx) => `$${rowIdx * derivedCols.length + colIdx + 1}`
1721
+ );
1705
1722
  return `(${params2.join(", ")})`;
1706
1723
  }).join(", ");
1707
1724
  const sql = `INSERT OR REPLACE INTO ${dateContextTarget} (${derivedCols.join(", ")}) VALUES ${placeholders}`;
@@ -1735,10 +1752,9 @@ async function hasTier3Data(conn, ticker, spotStore) {
1735
1752
  const cov = await spotStore.getCoverage(ticker, "1970-01-01", "9999-12-31");
1736
1753
  return cov.totalDates > 0;
1737
1754
  }
1738
- const r = await conn.runAndReadAll(
1739
- `SELECT COUNT(*) FROM market.spot WHERE ticker = $1 LIMIT 1`,
1740
- [ticker]
1741
- );
1755
+ const r = await conn.runAndReadAll(`SELECT COUNT(*) FROM market.spot WHERE ticker = $1 LIMIT 1`, [
1756
+ ticker
1757
+ ]);
1742
1758
  return Number(r.getRows()[0]?.[0] ?? 0) > 0;
1743
1759
  }
1744
1760
  async function runContextEnrichment(conn, targets) {
@@ -1778,7 +1794,10 @@ async function runEnrichment(conn, ticker, opts = {}, io) {
1778
1794
  fetchParams.push(lookbackStart);
1779
1795
  }
1780
1796
  fetchSql += ` ORDER BY date ASC`;
1781
- const rawReader = await conn.runAndReadAll(fetchSql, fetchParams);
1797
+ const rawReader = await conn.runAndReadAll(
1798
+ fetchSql,
1799
+ fetchParams
1800
+ );
1782
1801
  rawRows = rawReader.getRows();
1783
1802
  }
1784
1803
  if (rawRows.length === 0) {
@@ -1995,7 +2014,8 @@ function computeIntradayTimingFields(bars) {
1995
2014
  const highInAfternoon = highTime >= 12;
1996
2015
  const lowInAfternoon = lowTime >= 12;
1997
2016
  let reversalType = 0;
1998
- if (highInMorning && lowInAfternoon) reversalType = 1;
2017
+ if (highInMorning && lowInAfternoon)
2018
+ reversalType = 1;
1999
2019
  else if (lowInMorning && highInAfternoon) reversalType = -1;
2000
2020
  const openingBars = bars.filter((b) => hhmmToDecimalHours(b.time) < 10);
2001
2021
  let openingDriveStrength = 0;
@@ -2020,7 +2040,14 @@ function computeIntradayTimingFields(bars) {
2020
2040
  intradayRealizedVol = barStdDev * Math.sqrt(bars.length * 252);
2021
2041
  }
2022
2042
  }
2023
- return { highTime, lowTime, highBeforeLow, reversalType, openingDriveStrength, intradayRealizedVol };
2043
+ return {
2044
+ highTime,
2045
+ lowTime,
2046
+ highBeforeLow,
2047
+ reversalType,
2048
+ openingDriveStrength,
2049
+ intradayRealizedVol
2050
+ };
2024
2051
  }
2025
2052
  async function runTier3(conn, ticker, dates, dailyTarget = "market.enriched", spotStore) {
2026
2053
  const hasData = await hasTier3Data(conn, ticker, spotStore);
@@ -2086,7 +2113,14 @@ async function runTier3(conn, ticker, dates, dailyTarget = "market.enriched", sp
2086
2113
  reason: "intraday data exists but no bars overlap with enrichment date range"
2087
2114
  };
2088
2115
  }
2089
- const tier3Cols = ["High_Time", "Low_Time", "High_Before_Low", "Reversal_Type", "Opening_Drive_Strength", "Intraday_Realized_Vol"];
2116
+ const tier3Cols = [
2117
+ "High_Time",
2118
+ "Low_Time",
2119
+ "High_Before_Low",
2120
+ "Reversal_Type",
2121
+ "Opening_Drive_Strength",
2122
+ "Intraday_Realized_Vol"
2123
+ ];
2090
2124
  const enrichedRows = [];
2091
2125
  for (const [dateStr, bars] of barsByDate) {
2092
2126
  const timing = computeIntradayTimingFields(bars);
@@ -2205,7 +2239,9 @@ function applyColumnMapping(rows, columnMapping, ticker) {
2205
2239
  if (hasNullDate) continue;
2206
2240
  if (!("date" in mapped)) continue;
2207
2241
  if (!("time" in mapped)) {
2208
- const dateSourceCol = Object.entries(columnMapping).find(([, schema]) => schema === "date")?.[0];
2242
+ const dateSourceCol = Object.entries(columnMapping).find(
2243
+ ([, schema]) => schema === "date"
2244
+ )?.[0];
2209
2245
  if (dateSourceCol) {
2210
2246
  const rawDateValue = row[dateSourceCol] ?? "";
2211
2247
  const numericDate = Number(rawDateValue);
@@ -2599,9 +2635,13 @@ var profileStrategySchema = z.object({
2599
2635
  operator: z.string().describe("Comparison operator: >, <, >=, <=, ==, between, in"),
2600
2636
  value: z.union([z.string(), z.number(), z.array(z.union([z.string(), z.number()]))]).describe("Filter value or array for between/in operators"),
2601
2637
  description: z.string().optional().describe("Human-readable description of this filter"),
2602
- source: z.enum(["market", "execution"]).optional().describe("Filter source: 'market' = testable against market data columns, 'execution' = platform-level (time windows, leg ratios). Defaults to 'market'. Execution filters are documented but skipped during validate_entry_filters analysis.")
2638
+ source: z.enum(["market", "execution"]).optional().describe(
2639
+ "Filter source: 'market' = testable against market data columns, 'execution' = platform-level (time windows, leg ratios). Defaults to 'market'. Execution filters are documented but skipped during validate_entry_filters analysis."
2640
+ )
2603
2641
  })
2604
- ).default([]).describe("Entry condition filters. Tag each with source: 'market' (testable in analysis) or 'execution' (OO/platform-level, skipped in analysis)."),
2642
+ ).default([]).describe(
2643
+ "Entry condition filters. Tag each with source: 'market' (testable in analysis) or 'execution' (OO/platform-level, skipped in analysis)."
2644
+ ),
2605
2645
  exitRules: z.array(
2606
2646
  z.object({
2607
2647
  type: z.string().describe("Rule type: stop_loss, profit_target, time_exit, conditional"),
@@ -2616,7 +2656,9 @@ var profileStrategySchema = z.object({
2616
2656
  slippage: z.number().optional().describe("Per-rule slippage override")
2617
2657
  })
2618
2658
  ).default([]).describe("Exit rules and triggers"),
2619
- expectedRegimes: z.array(z.enum(["very_low", "low", "below_avg", "above_avg", "high", "extreme"])).default([]).describe("VIX-based vol regimes this strategy targets. very_low=VIX<13, low=13-16, below_avg=16-20, above_avg=20-25, high=25-30, extreme=30+"),
2659
+ expectedRegimes: z.array(z.enum(["very_low", "low", "below_avg", "above_avg", "high", "extreme"])).default([]).describe(
2660
+ "VIX-based vol regimes this strategy targets. very_low=VIX<13, low=13-16, below_avg=16-20, above_avg=20-25, high=25-30, extreme=30+"
2661
+ ),
2620
2662
  keyMetrics: z.object({
2621
2663
  expectedWinRate: z.number().optional().describe("Expected win rate (0-1)"),
2622
2664
  targetPremium: z.number().optional().describe("Target premium collected ($)"),
@@ -2633,7 +2675,9 @@ var profileStrategySchema = z.object({
2633
2675
  backtestAllocationPct: z.number().optional().describe("Allocation % used in backtest"),
2634
2676
  liveAllocationPct: z.number().optional().describe("Allocation % used in live portfolio"),
2635
2677
  maxContractsPerTrade: z.number().optional().describe("Per-entry contract cap (distinct from maxContracts hard cap)")
2636
- }).optional().describe("Position sizing rules. Per-block \u2014 same strategy in backtest vs portfolio may have different sizing."),
2678
+ }).optional().describe(
2679
+ "Position sizing rules. Per-block \u2014 same strategy in backtest vs portfolio may have different sizing."
2680
+ ),
2637
2681
  underlying: z.string().optional().describe("Underlying symbol: SPX, QQQ, etc."),
2638
2682
  reEntry: z.boolean().optional().describe("Strategy supports re-entry on same day"),
2639
2683
  capProfits: z.boolean().optional().describe("Profits are capped by structure"),
@@ -2657,30 +2701,33 @@ async function handleProfileStrategy(input, baseDir) {
2657
2701
  await upgradeToReadWrite(baseDir);
2658
2702
  try {
2659
2703
  const conn = await getConnection(baseDir);
2660
- const stored = await upsertProfile(conn, {
2661
- blockId: input.blockId,
2662
- strategyName: input.strategyName,
2663
- structureType: input.structureType,
2664
- greeksBias: input.greeksBias,
2665
- thesis: input.thesis,
2666
- legs: input.legs,
2667
- entryFilters: input.entryFilters,
2668
- exitRules: input.exitRules,
2669
- expectedRegimes: input.expectedRegimes,
2670
- keyMetrics: input.keyMetrics,
2671
- positionSizing: input.positionSizing,
2672
- underlying: input.underlying,
2673
- reEntry: input.reEntry,
2674
- capProfits: input.capProfits,
2675
- capLosses: input.capLosses,
2676
- requireTwoPricesPT: input.requireTwoPricesPT,
2677
- closeOnCompletion: input.closeOnCompletion,
2678
- ignoreMarginReq: input.ignoreMarginReq
2679
- }, baseDir);
2680
- return createToolOutput(
2681
- `Profile saved: ${input.strategyName} for block ${input.blockId}`,
2682
- { profile: stored }
2704
+ const stored = await upsertProfile(
2705
+ conn,
2706
+ {
2707
+ blockId: input.blockId,
2708
+ strategyName: input.strategyName,
2709
+ structureType: input.structureType,
2710
+ greeksBias: input.greeksBias,
2711
+ thesis: input.thesis,
2712
+ legs: input.legs,
2713
+ entryFilters: input.entryFilters,
2714
+ exitRules: input.exitRules,
2715
+ expectedRegimes: input.expectedRegimes,
2716
+ keyMetrics: input.keyMetrics,
2717
+ positionSizing: input.positionSizing,
2718
+ underlying: input.underlying,
2719
+ reEntry: input.reEntry,
2720
+ capProfits: input.capProfits,
2721
+ capLosses: input.capLosses,
2722
+ requireTwoPricesPT: input.requireTwoPricesPT,
2723
+ closeOnCompletion: input.closeOnCompletion,
2724
+ ignoreMarginReq: input.ignoreMarginReq
2725
+ },
2726
+ baseDir
2683
2727
  );
2728
+ return createToolOutput(`Profile saved: ${input.strategyName} for block ${input.blockId}`, {
2729
+ profile: stored
2730
+ });
2684
2731
  } finally {
2685
2732
  await downgradeToReadOnly(baseDir);
2686
2733
  }
@@ -2694,10 +2741,7 @@ async function handleGetStrategyProfile(input, baseDir) {
2694
2741
  { profile: null }
2695
2742
  );
2696
2743
  }
2697
- return createToolOutput(
2698
- `Profile: ${input.strategyName} in block ${input.blockId}`,
2699
- { profile }
2700
- );
2744
+ return createToolOutput(`Profile: ${input.strategyName} in block ${input.blockId}`, { profile });
2701
2745
  }
2702
2746
  async function handleListProfiles(input, baseDir) {
2703
2747
  const conn = await getConnection(baseDir);
@@ -3286,7 +3330,9 @@ async function handleValidateEntryFilters(input, baseDir) {
3286
3330
  single: ablationSingle,
3287
3331
  pairs: ablationPairs
3288
3332
  },
3289
- execution_filters_skipped: executionFilters.map((f) => f.description || `${f.field} ${f.operator} ${f.value}`),
3333
+ execution_filters_skipped: executionFilters.map(
3334
+ (f) => f.description || `${f.field} ${f.operator} ${f.value}`
3335
+ ),
3290
3336
  profile_update_hints: profileUpdateHints,
3291
3337
  warnings
3292
3338
  });
@@ -3317,7 +3363,9 @@ async function handlePortfolioStructureMap(input, baseDir) {
3317
3363
  try {
3318
3364
  block = await loadBlock(baseDir, profile.blockId);
3319
3365
  } catch {
3320
- warnings.push(`Could not load block '${profile.blockId}' for strategy '${profile.strategyName}'`);
3366
+ warnings.push(
3367
+ `Could not load block '${profile.blockId}' for strategy '${profile.strategyName}'`
3368
+ );
3321
3369
  continue;
3322
3370
  }
3323
3371
  let trades = filterByStrategy(block.trades, profile.strategyName);
@@ -3328,7 +3376,9 @@ async function handlePortfolioStructureMap(input, baseDir) {
3328
3376
  }
3329
3377
  }
3330
3378
  if (trades.length === 0) {
3331
- warnings.push(`No trades found for strategy '${profile.strategyName}' in block '${profile.blockId}'`);
3379
+ warnings.push(
3380
+ `No trades found for strategy '${profile.strategyName}' in block '${profile.blockId}'`
3381
+ );
3332
3382
  continue;
3333
3383
  }
3334
3384
  const tradeKeys = uniqueTradeLookupKeys(trades);
@@ -3446,10 +3496,7 @@ async function handlePortfolioStructureMap(input, baseDir) {
3446
3496
  );
3447
3497
  }
3448
3498
  const unknownTrendStats = unknownTrendPls.size > 0 ? Object.fromEntries(
3449
- [...unknownTrendPls.entries()].map(([name, pls]) => [
3450
- name,
3451
- computeSliceStats(pls)
3452
- ])
3499
+ [...unknownTrendPls.entries()].map(([name, pls]) => [name, computeSliceStats(pls)])
3453
3500
  ) : void 0;
3454
3501
  const coverageSummary = {
3455
3502
  totalCells: 18,
@@ -3551,12 +3598,8 @@ var VOL_REGIME_LABELS2 = {
3551
3598
  6: "extreme"
3552
3599
  };
3553
3600
  var regimeAllocationAdvisorSchema = z3.object({
3554
- blockId: z3.string().optional().describe(
3555
- "Block ID to analyze. When omitted, aggregate across all profiled strategies."
3556
- ),
3557
- minTrades: z3.number().optional().default(5).describe(
3558
- "Minimum trades per regime cell for reliable stats (default: 5)"
3559
- )
3601
+ blockId: z3.string().optional().describe("Block ID to analyze. When omitted, aggregate across all profiled strategies."),
3602
+ minTrades: z3.number().optional().default(5).describe("Minimum trades per regime cell for reliable stats (default: 5)")
3560
3603
  });
3561
3604
  async function handleRegimeAllocationAdvisor(input, baseDir) {
3562
3605
  const minTrades = input.minTrades ?? 5;
@@ -3648,9 +3691,7 @@ async function handleRegimeAllocationAdvisor(input, baseDir) {
3648
3691
  if (!regimeAggPls[label]) regimeAggPls[label] = [];
3649
3692
  regimeAggPls[label].push(trade.pl);
3650
3693
  }
3651
- const expectedSet = new Set(
3652
- profile.expectedRegimes.map((r) => r.toLowerCase())
3653
- );
3694
+ const expectedSet = new Set(profile.expectedRegimes.map((r) => r.toLowerCase()));
3654
3695
  const regimePerformance = {};
3655
3696
  for (const [label, pls] of Object.entries(regimePls)) {
3656
3697
  const stats = computeSliceStats(pls);
@@ -3787,9 +3828,7 @@ var replayTradeSchema = z4.object({
3787
3828
  ).optional().describe("Explicit leg definitions for hypothetical replay"),
3788
3829
  // Mode B: Tradelog replay
3789
3830
  block_id: z4.string().optional().describe("Block ID to load trade from"),
3790
- trade_index: z4.number().optional().describe(
3791
- "0-based index of trade in block's tradelog (ordered by date_opened)"
3792
- ),
3831
+ trade_index: z4.number().optional().describe("0-based index of trade in block's tradelog (ordered by date_opened)"),
3793
3832
  // Common fields
3794
3833
  open_date: z4.string().optional().describe(
3795
3834
  "Trade open date YYYY-MM-DD (required for hypothetical mode, auto-resolved for tradelog mode)"
@@ -3836,22 +3875,13 @@ function resolveOODateRange(parsedLegs, tradeYear, tradeOpenDate) {
3836
3875
  return { from: tradeOpenDate, to: maxDate };
3837
3876
  }
3838
3877
  async function handleReplayTrade(params, baseDir, stores, injectedConn) {
3839
- const {
3840
- legs: inputLegs,
3841
- block_id,
3842
- trade_index,
3843
- multiplier,
3844
- close_at,
3845
- skip_quotes
3846
- } = params;
3878
+ const { legs: inputLegs, block_id, trade_index, multiplier, close_at, skip_quotes } = params;
3847
3879
  let { open_date, close_date } = params;
3848
3880
  let tradeCloseTimestamp;
3849
3881
  let replayLegs;
3850
3882
  if (inputLegs && inputLegs.length > 0) {
3851
3883
  if (!open_date || !close_date) {
3852
- throw new Error(
3853
- "open_date and close_date are required for hypothetical replay mode"
3854
- );
3884
+ throw new Error("open_date and close_date are required for hypothetical replay mode");
3855
3885
  }
3856
3886
  replayLegs = inputLegs.map((leg) => ({
3857
3887
  occTicker: buildOccTicker(leg.ticker, leg.expiry, leg.type, leg.strike),
@@ -3870,9 +3900,7 @@ async function handleReplayTrade(params, baseDir, stores, injectedConn) {
3870
3900
  );
3871
3901
  const rows = result.getRows();
3872
3902
  if (rows.length === 0) {
3873
- throw new Error(
3874
- `No trade found at index ${trade_index} in block "${block_id}"`
3875
- );
3903
+ throw new Error(`No trade found at index ${trade_index} in block "${block_id}"`);
3876
3904
  }
3877
3905
  const row = rows[0];
3878
3906
  const legsStr = String(row[0] ?? "");
@@ -3981,11 +4009,7 @@ async function handleReplayTrade(params, baseDir, stores, injectedConn) {
3981
4009
  );
3982
4010
  if (underlyingBars.length === 0) {
3983
4011
  try {
3984
- underlyingBars = await stores.spot.readDailyBars(
3985
- underlyingTicker,
3986
- open_date,
3987
- close_date
3988
- );
4012
+ underlyingBars = await stores.spot.readDailyBars(underlyingTicker, open_date, close_date);
3989
4013
  } catch {
3990
4014
  }
3991
4015
  }
@@ -4112,9 +4136,7 @@ var getOptionSnapshotSchema = z5.object({
4112
4136
  expiration_date_gte: z5.string().optional().describe("Earliest expiration date (YYYY-MM-DD)"),
4113
4137
  expiration_date_lte: z5.string().optional().describe("Latest expiration date (YYYY-MM-DD)"),
4114
4138
  contract_type: z5.enum(["call", "put"]).optional().describe("Filter by call or put"),
4115
- limit: z5.number().optional().default(50).describe(
4116
- "Max contracts to return (default 50, use higher for full chain)"
4117
- )
4139
+ limit: z5.number().optional().default(50).describe("Max contracts to return (default 50, use higher for full chain)")
4118
4140
  });
4119
4141
  async function handleGetOptionSnapshot(params) {
4120
4142
  try {
@@ -4231,7 +4253,9 @@ function numericalDecomposition(config, totalPnlChange, stepCount) {
4231
4253
  ...f,
4232
4254
  pctOfTotal: totalAbsSum > 0 ? Math.abs(f.totalPnl) / totalAbsSum * 100 : 0
4233
4255
  }));
4234
- const summaryParts = factors.map((f) => `${f.factor} ${f.totalPnl.toFixed(2)} (${f.pctOfTotal.toFixed(0)}%)`);
4256
+ const summaryParts = factors.map(
4257
+ (f) => `${f.factor} ${f.totalPnl.toFixed(2)} (${f.pctOfTotal.toFixed(0)}%)`
4258
+ );
4235
4259
  const summary = `P&L of ${totalPnlChange.toFixed(2)} (numerical): ${summaryParts.join(", ")}`;
4236
4260
  return {
4237
4261
  factors,
@@ -4266,7 +4290,15 @@ function priceOption(type, S, K, dte, r, q, iv) {
4266
4290
  return bsPrice(bsType, S, K, T, r, q, iv);
4267
4291
  }
4268
4292
  function decomposeGreeks(config) {
4269
- const { pnlPath, legs, underlyingPrices, legGroups, legPricingInputs, riskFreeRate, dividendYield } = config;
4293
+ const {
4294
+ pnlPath,
4295
+ legs,
4296
+ underlyingPrices,
4297
+ legGroups,
4298
+ legPricingInputs,
4299
+ riskFreeRate,
4300
+ dividendYield
4301
+ } = config;
4270
4302
  if (pnlPath.length <= 1) {
4271
4303
  const emptyFactors = [
4272
4304
  { factor: "delta", totalPnl: 0, pctOfTotal: 0, steps: [] },
@@ -4426,7 +4458,9 @@ function decomposeGreeks(config) {
4426
4458
  const summaryParts = factors.filter((f) => f.factor !== "residual").map((f) => `${f.factor} ${f.totalPnl.toFixed(2)} (${f.pctOfTotal.toFixed(0)}%)`);
4427
4459
  const residualFactor = factors.find((f) => f.factor === "residual");
4428
4460
  if (residualFactor && Math.abs(residualFactor.totalPnl) > 0.01) {
4429
- summaryParts.push(`residual ${residualFactor.totalPnl.toFixed(2)} (${residualFactor.pctOfTotal.toFixed(0)}%)`);
4461
+ summaryParts.push(
4462
+ `residual ${residualFactor.totalPnl.toFixed(2)} (${residualFactor.pctOfTotal.toFixed(0)}%)`
4463
+ );
4430
4464
  }
4431
4465
  const summary = `P&L of ${totalPnlChange.toFixed(2)} (${methodLabel}): ${summaryParts.join(", ")}`;
4432
4466
  const warning = residualPct > 0.5 ? `Residual ${(residualPct * 100).toFixed(0)}% \u2014 attribution limited for some legs.` : null;
@@ -4558,7 +4592,7 @@ function evaluateTrigger(trigger, pnlPath, legs) {
4558
4592
  switch (type) {
4559
4593
  case "profitTarget": {
4560
4594
  if (trigger.unit === "percent" && trigger.entryCost == null) break;
4561
- const requiredHits = trigger.requiredHits ?? 1;
4595
+ const requiredHits = trigger.requiredHits ?? 2;
4562
4596
  const dollarThresholdPT = trigger.unit === "percent" ? threshold * Math.abs(trigger.entryCost) : threshold;
4563
4597
  if (pnl >= dollarThresholdPT) {
4564
4598
  if (point.allLegsSync !== false) profitTargetHits++;
@@ -4940,11 +4974,13 @@ var triggerConfigSchema = z6.object({
4940
4974
  openDate: z6.string().optional(),
4941
4975
  clockTime: z6.string().optional(),
4942
4976
  trailAmount: z6.number().optional(),
4943
- steps: z6.array(z6.object({
4944
- armAt: z6.number(),
4945
- stopAt: z6.number(),
4946
- closeAllocationPct: z6.number().min(0).max(1).optional().describe("Fraction of REMAINING position to close at this milestone (0-1)")
4947
- })).optional(),
4977
+ steps: z6.array(
4978
+ z6.object({
4979
+ armAt: z6.number(),
4980
+ stopAt: z6.number(),
4981
+ closeAllocationPct: z6.number().min(0).max(1).optional().describe("Fraction of REMAINING position to close at this milestone (0-1)")
4982
+ })
4983
+ ).optional(),
4948
4984
  spreadWidth: z6.number().optional(),
4949
4985
  contracts: z6.number().optional(),
4950
4986
  legIndex: z6.number().optional().describe("0-based leg index for perLegDelta \u2014 targets specific leg"),
@@ -4969,11 +5005,13 @@ var analyzeExitTriggersSchema = z6.object({
4969
5005
  multiplier: z6.number().default(100),
4970
5006
  triggers: z6.array(triggerConfigSchema).describe("Exit triggers to evaluate against the P&L path"),
4971
5007
  actual_exit_timestamp: z6.string().optional().describe("Actual exit time for comparison (format: YYYY-MM-DD HH:MM)"),
4972
- leg_groups: z6.array(z6.object({
4973
- label: z6.string(),
4974
- leg_indices: z6.array(z6.number()),
4975
- triggers: z6.array(triggerConfigSchema)
4976
- })).optional().describe("Per-leg-group exit triggers for multi-structure strategies"),
5008
+ leg_groups: z6.array(
5009
+ z6.object({
5010
+ label: z6.string(),
5011
+ leg_indices: z6.array(z6.number()),
5012
+ triggers: z6.array(triggerConfigSchema)
5013
+ })
5014
+ ).optional().describe("Per-leg-group exit triggers for multi-structure strategies"),
4977
5015
  format: z6.enum(["summary", "full"]).default("summary").describe("'summary' omits per-step trigger states, 'full' includes all fire events")
4978
5016
  });
4979
5017
  var decomposeGreeksSchema = z6.object({
@@ -4984,10 +5022,12 @@ var decomposeGreeksSchema = z6.object({
4984
5022
  open_date: z6.string().optional(),
4985
5023
  close_date: z6.string().optional(),
4986
5024
  multiplier: z6.number().default(100),
4987
- leg_groups: z6.array(z6.object({
4988
- label: z6.string(),
4989
- leg_indices: z6.array(z6.number())
4990
- })).optional().describe("Leg grouping for per-group vega attribution (e.g., front_month vs back_month)"),
5025
+ leg_groups: z6.array(
5026
+ z6.object({
5027
+ label: z6.string(),
5028
+ leg_indices: z6.array(z6.number())
5029
+ })
5030
+ ).optional().describe("Leg grouping for per-group vega attribution (e.g., front_month vs back_month)"),
4991
5031
  format: z6.enum(["summary", "full"]).default("summary").describe("'summary' shows ranked factors, 'full' includes per-step contributions"),
4992
5032
  skip_quotes: z6.boolean().default(false).describe("Skip NBBO quote enrichment for option bars. Faster, but lower precision.")
4993
5033
  });
@@ -5012,7 +5052,8 @@ async function fetchPriceMap(stores, ticker, from, to) {
5012
5052
  }
5013
5053
  }
5014
5054
  for (const b of bars) {
5015
- if (!Number.isFinite(b.open) || b.open <= 0 || !Number.isFinite(b.high) || b.high <= 0 || !Number.isFinite(b.low) || b.low <= 0 || !Number.isFinite(b.close) || b.close <= 0) continue;
5055
+ if (!Number.isFinite(b.open) || b.open <= 0 || !Number.isFinite(b.high) || b.high <= 0 || !Number.isFinite(b.low) || b.low <= 0 || !Number.isFinite(b.close) || b.close <= 0)
5056
+ continue;
5016
5057
  const ts = `${b.date} ${b.time ?? ""}`.trim();
5017
5058
  map.set(ts, markPrice(b));
5018
5059
  }
@@ -5083,12 +5124,7 @@ async function handleAnalyzeExitTriggers(params, baseDir, stores, injectedConn)
5083
5124
  vix9dPrices = await fetchPriceMap(stores, "VIX9D", firstDate, lastDate);
5084
5125
  }
5085
5126
  if (needsUnderlying) {
5086
- underlyingPrices = await fetchPriceMap(
5087
- stores,
5088
- underlyingTicker,
5089
- firstDate,
5090
- lastDate
5091
- );
5127
+ underlyingPrices = await fetchPriceMap(stores, underlyingTicker, firstDate, lastDate);
5092
5128
  }
5093
5129
  const exitTriggers = triggers.map((t) => ({
5094
5130
  type: t.type,
@@ -5458,11 +5494,13 @@ var triggerConfigSchema2 = z7.object({
5458
5494
  openDate: z7.string().optional(),
5459
5495
  clockTime: z7.string().optional(),
5460
5496
  trailAmount: z7.number().optional(),
5461
- steps: z7.array(z7.object({
5462
- armAt: z7.number(),
5463
- stopAt: z7.number(),
5464
- closeAllocationPct: z7.number().min(0).max(1).optional().describe("Fraction of REMAINING position to close at this milestone (0-1)")
5465
- })).optional(),
5497
+ steps: z7.array(
5498
+ z7.object({
5499
+ armAt: z7.number(),
5500
+ stopAt: z7.number(),
5501
+ closeAllocationPct: z7.number().min(0).max(1).optional().describe("Fraction of REMAINING position to close at this milestone (0-1)")
5502
+ })
5503
+ ).optional(),
5466
5504
  spreadWidth: z7.number().optional(),
5467
5505
  contracts: z7.number().optional(),
5468
5506
  legIndex: z7.number().optional().describe("0-based leg index for perLegDelta \u2014 targets specific leg"),
@@ -5477,17 +5515,23 @@ var batchExitAnalysisSchema = z7.object({
5477
5515
  to: z7.string().optional().describe("End date YYYY-MM-DD")
5478
5516
  }).optional().describe("Filter trades by date range"),
5479
5517
  candidate_policy: z7.array(triggerConfigSchema2).describe("Candidate exit policy triggers to evaluate -- same schema as analyze_exit_triggers"),
5480
- leg_groups: z7.array(z7.object({
5481
- label: z7.string(),
5482
- leg_indices: z7.array(z7.number()),
5483
- triggers: z7.array(triggerConfigSchema2)
5484
- })).optional().describe("Per-leg-group exit triggers for multi-structure strategies"),
5485
- baseline_mode: z7.enum(["actual", "holdToEnd"]).default("actual").describe("'actual' compares candidate vs trade's actual P&L; 'holdToEnd' compares vs last replay timestamp"),
5518
+ leg_groups: z7.array(
5519
+ z7.object({
5520
+ label: z7.string(),
5521
+ leg_indices: z7.array(z7.number()),
5522
+ triggers: z7.array(triggerConfigSchema2)
5523
+ })
5524
+ ).optional().describe("Per-leg-group exit triggers for multi-structure strategies"),
5525
+ baseline_mode: z7.enum(["actual", "holdToEnd"]).default("actual").describe(
5526
+ "'actual' compares candidate vs trade's actual P&L; 'holdToEnd' compares vs last replay timestamp"
5527
+ ),
5486
5528
  limit: z7.number().min(1).max(200).default(50).describe("Max trades to analyze. Most recent trades selected"),
5487
5529
  min_pl: z7.number().optional().describe("Only include trades with actual P&L >= this value"),
5488
5530
  max_pl: z7.number().optional().describe("Only include trades with actual P&L <= this value"),
5489
5531
  multiplier: z7.number().default(100).describe("Contract multiplier (default 100)"),
5490
- format: z7.enum(["summary", "full"]).default("summary").describe("'summary' returns aggregate stats + trigger attribution; 'full' adds per-trade breakdown")
5532
+ format: z7.enum(["summary", "full"]).default("summary").describe(
5533
+ "'summary' returns aggregate stats + trigger attribution; 'full' adds per-trade breakdown"
5534
+ )
5491
5535
  });
5492
5536
  async function handleBatchExitAnalysis(params, baseDir, stores, injectedConn) {
5493
5537
  const {
@@ -5650,9 +5694,7 @@ async function handleBatchExitAnalysis(params, baseDir, stores, injectedConn) {
5650
5694
  if (profile) {
5651
5695
  result.profileContext = {
5652
5696
  structureType: profile.structureType,
5653
- exitRules: profile.exitRules.map(
5654
- (r) => r.description ?? `${r.type} ${r.trigger}`
5655
- )
5697
+ exitRules: profile.exitRules.map((r) => r.description ?? `${r.type} ${r.trigger}`)
5656
5698
  };
5657
5699
  }
5658
5700
  } catch {
@@ -5813,13 +5855,7 @@ function filterChain(contracts, filter) {
5813
5855
  import { open, mkdir } from "fs/promises";
5814
5856
  import { dirname, join as join2 } from "path";
5815
5857
  function backfillManifestPath(dataRoot, runId) {
5816
- return join2(
5817
- dataRoot,
5818
- "market",
5819
- "_manifests",
5820
- "thetadata-mdds-backfill",
5821
- `${runId}.ndjson`
5822
- );
5858
+ return join2(dataRoot, "market", "_manifests", "thetadata-mdds-backfill", `${runId}.ndjson`);
5823
5859
  }
5824
5860
  function backfillPartitionPath(dataRoot, underlying, date) {
5825
5861
  return join2(
@@ -6129,8 +6165,12 @@ function tradingDays(from, to) {
6129
6165
  var getGreeksAttributionSchema = z9.object({
6130
6166
  block_id: z9.string().describe("Block ID to analyze"),
6131
6167
  mode: z9.enum(["summary", "instance"]).default("summary").describe("summary: block-level attribution. instance: single trade time-series."),
6132
- trade_index: z9.number().int().min(0).optional().describe("Trade index (required for instance mode). Use get_block_info to find trade indices."),
6133
- skip_quotes: z9.boolean().default(true).describe("Use cached bar data only (fast). Set false to fetch NBBO quotes for higher precision."),
6168
+ trade_index: z9.number().int().min(0).optional().describe(
6169
+ "Trade index (required for instance mode). Use get_block_info to find trade indices."
6170
+ ),
6171
+ skip_quotes: z9.boolean().default(true).describe(
6172
+ "Use cached bar data only (fast). Set false to fetch NBBO quotes for higher precision."
6173
+ ),
6134
6174
  detailed: z9.boolean().default(false).describe("false: 5 factors (delta, gamma, theta, vega, residual). true: adds charm, vanna."),
6135
6175
  strategy: z9.string().optional().describe("Filter to trades matching this strategy name (case-insensitive).")
6136
6176
  });
@@ -6138,7 +6178,16 @@ var COLLAPSE_MAP = {
6138
6178
  charm: "delta",
6139
6179
  vanna: "vega"
6140
6180
  };
6141
- var FACTOR_ORDER = ["theta", "vega", "delta", "gamma", "residual", "time_and_vol", "charm", "vanna"];
6181
+ var FACTOR_ORDER = [
6182
+ "theta",
6183
+ "vega",
6184
+ "delta",
6185
+ "gamma",
6186
+ "residual",
6187
+ "time_and_vol",
6188
+ "charm",
6189
+ "vanna"
6190
+ ];
6142
6191
  function collapseFactors(factors, detailed) {
6143
6192
  const totals = /* @__PURE__ */ new Map();
6144
6193
  for (const f of factors) {
@@ -6188,9 +6237,25 @@ async function handleGetGreeksAttribution(params, baseDir, stores, injectedConn)
6188
6237
  if (trade_index == null) {
6189
6238
  throw new Error("trade_index is required for instance mode");
6190
6239
  }
6191
- return handleInstanceMode(block_id, trade_index, skip_quotes, detailed, baseDir, stores, injectedConn);
6240
+ return handleInstanceMode(
6241
+ block_id,
6242
+ trade_index,
6243
+ skip_quotes,
6244
+ detailed,
6245
+ baseDir,
6246
+ stores,
6247
+ injectedConn
6248
+ );
6192
6249
  }
6193
- return handleSummaryMode(block_id, skip_quotes, detailed, strategy, baseDir, stores, injectedConn);
6250
+ return handleSummaryMode(
6251
+ block_id,
6252
+ skip_quotes,
6253
+ detailed,
6254
+ strategy,
6255
+ baseDir,
6256
+ stores,
6257
+ injectedConn
6258
+ );
6194
6259
  }
6195
6260
  async function handleSummaryMode(block_id, skip_quotes, detailed, strategy, baseDir, stores, injectedConn) {
6196
6261
  const conn = injectedConn ?? await getConnection(baseDir);
@@ -6242,7 +6307,10 @@ async function handleSummaryMode(block_id, skip_quotes, detailed, strategy, base
6242
6307
  injectedConn
6243
6308
  ).then((result) => {
6244
6309
  for (const factor of result.factors) {
6245
- accumulated.set(factor.factor, (accumulated.get(factor.factor) ?? 0) + factor.totalPnl);
6310
+ accumulated.set(
6311
+ factor.factor,
6312
+ (accumulated.get(factor.factor) ?? 0) + factor.totalPnl
6313
+ );
6246
6314
  }
6247
6315
  actualTotalPnl += trade.actualPl;
6248
6316
  markTotalPnl += result.totalPnlChange;
@@ -6345,10 +6413,20 @@ async function handleInstanceMode(block_id, trade_index, skip_quotes, detailed,
6345
6413
  for (let i = 0; i <= stepCount; i++) {
6346
6414
  const entry = {
6347
6415
  date: getStepDate(i),
6348
- delta: getStepValue(factorSteps, "delta", i, detailed ? 0 : factorSteps.get("charm")?.[i] ?? 0),
6416
+ delta: getStepValue(
6417
+ factorSteps,
6418
+ "delta",
6419
+ i,
6420
+ detailed ? 0 : factorSteps.get("charm")?.[i] ?? 0
6421
+ ),
6349
6422
  gamma: getStepValue(factorSteps, "gamma", i, 0),
6350
6423
  theta: getStepValue(factorSteps, "theta", i, 0),
6351
- vega: getStepValue(factorSteps, "vega", i, detailed ? 0 : factorSteps.get("vanna")?.[i] ?? 0),
6424
+ vega: getStepValue(
6425
+ factorSteps,
6426
+ "vega",
6427
+ i,
6428
+ detailed ? 0 : factorSteps.get("vanna")?.[i] ?? 0
6429
+ ),
6352
6430
  residual: getStepValue(factorSteps, "residual", i, 0)
6353
6431
  };
6354
6432
  if (factorSteps.has("time_and_vol")) {
@@ -6815,11 +6893,7 @@ var ParquetSpotStore = class extends SpotStore {
6815
6893
  }));
6816
6894
  }
6817
6895
  async getCoverage(ticker, from, to) {
6818
- const tickerDir = path5.join(
6819
- resolveMarketDir(this.ctx.dataDir),
6820
- "spot",
6821
- `ticker=${ticker}`
6822
- );
6896
+ const tickerDir = path5.join(resolveMarketDir(this.ctx.dataDir), "spot", `ticker=${ticker}`);
6823
6897
  if (!existsSync4(tickerDir)) {
6824
6898
  return { earliest: null, latest: null, missingDates: [], totalDates: 0 };
6825
6899
  }
@@ -7044,9 +7118,7 @@ var ParquetEnrichedStore = class extends EnrichedStore {
7044
7118
  });
7045
7119
  const reader = await this.ctx.conn.runAndReadAll(sql);
7046
7120
  const names = reader.columnNames();
7047
- return reader.getRows().map(
7048
- (row) => Object.fromEntries(names.map((n, i) => [n, row[i]]))
7049
- );
7121
+ return reader.getRows().map((row) => Object.fromEntries(names.map((n, i) => [n, row[i]])));
7050
7122
  }
7051
7123
  async getCoverage(ticker) {
7052
7124
  const filePath = path6.join(
@@ -7119,9 +7191,7 @@ var DuckdbEnrichedStore = class extends EnrichedStore {
7119
7191
  });
7120
7192
  const reader = await this.ctx.conn.runAndReadAll(sql);
7121
7193
  const names = reader.columnNames();
7122
- return reader.getRows().map(
7123
- (row) => Object.fromEntries(names.map((n, i) => [n, row[i]]))
7124
- );
7194
+ return reader.getRows().map((row) => Object.fromEntries(names.map((n, i) => [n, row[i]])));
7125
7195
  }
7126
7196
  async getCoverage(ticker) {
7127
7197
  const tickerLit = ticker.replace(/'/g, "''");
@@ -7474,9 +7544,7 @@ var ParquetQuoteStore = class extends QuoteStore {
7474
7544
  }
7475
7545
  async readQuotes(occTickers, from, to) {
7476
7546
  if (occTickers.length === 0) return /* @__PURE__ */ new Map();
7477
- const firstUnderlying = this.ctx.tickers.resolve(
7478
- extractRoot(occTickers[0])
7479
- );
7547
+ const firstUnderlying = this.ctx.tickers.resolve(extractRoot(occTickers[0]));
7480
7548
  for (const t of occTickers) {
7481
7549
  const u = this.ctx.tickers.resolve(extractRoot(t));
7482
7550
  if (u !== firstUnderlying) {
@@ -7791,9 +7859,7 @@ var DuckdbQuoteStore = class extends QuoteStore {
7791
7859
  }
7792
7860
  async readQuotes(occTickers, from, to) {
7793
7861
  if (occTickers.length === 0) return /* @__PURE__ */ new Map();
7794
- const firstUnderlying = this.ctx.tickers.resolve(
7795
- extractRoot(occTickers[0])
7796
- );
7862
+ const firstUnderlying = this.ctx.tickers.resolve(extractRoot(occTickers[0]));
7797
7863
  for (const t of occTickers) {
7798
7864
  const u = this.ctx.tickers.resolve(extractRoot(t));
7799
7865
  if (u !== firstUnderlying) {
@@ -8084,18 +8150,14 @@ var UnderlyingsFileSchema = z10.object({
8084
8150
  });
8085
8151
  var registerUnderlyingSchema = z10.object({
8086
8152
  underlying: z10.string().min(1).max(16).regex(TICKER_RE).describe("Canonical underlying symbol, e.g. SPX"),
8087
- roots: z10.array(z10.string().min(1).max(16).regex(TICKER_RE)).min(1).max(32).describe(
8088
- "OCC roots that resolve to this underlying, e.g. ['SPX','SPXW','SPXQ']"
8089
- )
8153
+ roots: z10.array(z10.string().min(1).max(16).regex(TICKER_RE)).min(1).max(32).describe("OCC roots that resolve to this underlying, e.g. ['SPX','SPXW','SPXQ']")
8090
8154
  });
8091
8155
  var unregisterUnderlyingSchema = z10.object({
8092
8156
  underlying: z10.string().min(1).max(16).regex(TICKER_RE).describe("Underlying to remove. Bundled defaults cannot be removed.")
8093
8157
  });
8094
8158
  var listUnderlyingsSchema = z10.object({});
8095
8159
  var resolveRootSchema = z10.object({
8096
- input: z10.string().min(1).max(32).describe(
8097
- "Bare root ('SPXW') or full OCC ticker ('SPXW251219C05000000')"
8098
- )
8160
+ input: z10.string().min(1).max(32).describe("Bare root ('SPXW') or full OCC ticker ('SPXW251219C05000000')")
8099
8161
  });
8100
8162
 
8101
8163
  // src/market/tickers/registry.ts
@@ -8107,9 +8169,7 @@ function validate(underlying, roots) {
8107
8169
  }
8108
8170
  for (const r of roots) {
8109
8171
  if (!TICKER_RE.test(r)) {
8110
- throw new Error(
8111
- `TickerRegistry: invalid root "${r}" \u2014 must match ${TICKER_RE.source}`
8112
- );
8172
+ throw new Error(`TickerRegistry: invalid root "${r}" \u2014 must match ${TICKER_RE.source}`);
8113
8173
  }
8114
8174
  }
8115
8175
  }
@@ -8192,9 +8252,7 @@ var TickerRegistry = class {
8192
8252
  unregister(underlying) {
8193
8253
  const entry = this.entries.get(underlying);
8194
8254
  if (!entry) {
8195
- throw new Error(
8196
- `TickerRegistry.unregister: unknown underlying "${underlying}"`
8197
- );
8255
+ throw new Error(`TickerRegistry.unregister: unknown underlying "${underlying}"`);
8198
8256
  }
8199
8257
  if (entry.source === "default") {
8200
8258
  throw new Error(
@@ -8482,7 +8540,13 @@ var MarketIngestor = class {
8482
8540
  assetClass
8483
8541
  });
8484
8542
  } catch (error) {
8485
- const mapped = this.mapProviderFailure(provider, "bars", normalizedTicker, error, assetClass);
8543
+ const mapped = this.mapProviderFailure(
8544
+ provider,
8545
+ "bars",
8546
+ normalizedTicker,
8547
+ error,
8548
+ assetClass
8549
+ );
8486
8550
  if (mapped) return mapped;
8487
8551
  throw error;
8488
8552
  }
@@ -8735,11 +8799,7 @@ var MarketIngestor = class {
8735
8799
  if (mapped) return mapped;
8736
8800
  throw error;
8737
8801
  }
8738
- const written = await this.writeQuotesForTicker(
8739
- provider,
8740
- ticker,
8741
- quotes
8742
- );
8802
+ const written = await this.writeQuotesForTicker(provider, ticker, quotes);
8743
8803
  totalRows += written.rowsWritten;
8744
8804
  if (written.minDate && (!minDate || written.minDate < minDate)) minDate = written.minDate;
8745
8805
  if (written.maxDate && (!maxDate || written.maxDate > maxDate)) maxDate = written.maxDate;
@@ -8775,12 +8835,7 @@ var MarketIngestor = class {
8775
8835
  for (const underlying of underlyings) {
8776
8836
  const upperUnderlying = underlying.toUpperCase();
8777
8837
  for (const date of dates) {
8778
- const drain = await this.drainBulkQuotes(
8779
- provider,
8780
- upperUnderlying,
8781
- date,
8782
- onProgress
8783
- );
8838
+ const drain = await this.drainBulkQuotes(provider, upperUnderlying, date, onProgress);
8784
8839
  if (drain.rowsWritten > 0) {
8785
8840
  totalRows += drain.rowsWritten;
8786
8841
  if (!minDate || date < minDate) minDate = date;
@@ -8840,7 +8895,11 @@ var MarketIngestor = class {
8840
8895
  totalContracts: info.totalContracts
8841
8896
  });
8842
8897
  } : void 0;
8843
- const stream = provider.fetchBulkQuotes({ underlying: upperUnderlying, date, onGroupComplete });
8898
+ const stream = provider.fetchBulkQuotes({
8899
+ underlying: upperUnderlying,
8900
+ date,
8901
+ onGroupComplete
8902
+ });
8844
8903
  for await (const chunk of stream) {
8845
8904
  for (const row of chunk) {
8846
8905
  const root = extractRoot(row.ticker);
@@ -8891,15 +8950,12 @@ var MarketIngestor = class {
8891
8950
  );
8892
8951
  } catch (error) {
8893
8952
  const message = error instanceof Error ? error.message : String(error);
8894
- console.warn(
8895
- "[drainBulkQuotes] enrichQuoteRows failed; skipping batch",
8896
- {
8897
- underlying: resolvedUnderlying,
8898
- date,
8899
- rows: rows.length,
8900
- error: message
8901
- }
8902
- );
8953
+ console.warn("[drainBulkQuotes] enrichQuoteRows failed; skipping batch", {
8954
+ underlying: resolvedUnderlying,
8955
+ date,
8956
+ rows: rows.length,
8957
+ error: message
8958
+ });
8903
8959
  skipped.push({
8904
8960
  underlying: resolvedUnderlying,
8905
8961
  date,
@@ -8986,16 +9042,13 @@ var MarketIngestor = class {
8986
9042
  );
8987
9043
  } catch (error) {
8988
9044
  const message = error instanceof Error ? error.message : String(error);
8989
- console.warn(
8990
- "[writeQuotesForTicker] enrichQuoteRows failed; skipping batch",
8991
- {
8992
- underlying,
8993
- date,
8994
- ticker,
8995
- rows: rows.length,
8996
- error: message
8997
- }
8998
- );
9045
+ console.warn("[writeQuotesForTicker] enrichQuoteRows failed; skipping batch", {
9046
+ underlying,
9047
+ date,
9048
+ ticker,
9049
+ rows: rows.length,
9050
+ error: message
9051
+ });
8999
9052
  skipped.push({
9000
9053
  underlying,
9001
9054
  date,
@@ -9062,7 +9115,12 @@ var MarketIngestor = class {
9062
9115
  for (const underlying of opts.underlyings) {
9063
9116
  const upperUnderlying = underlying.toUpperCase();
9064
9117
  const assetClass = this.detectAssetClass(upperUnderlying);
9065
- const unsupported = this.preflightProviderSupport(provider, "chain", upperUnderlying, assetClass);
9118
+ const unsupported = this.preflightProviderSupport(
9119
+ provider,
9120
+ "chain",
9121
+ upperUnderlying,
9122
+ assetClass
9123
+ );
9066
9124
  if (unsupported) return unsupported;
9067
9125
  const dates = this.enumerateDates(opts.from, opts.to);
9068
9126
  for (const date of dates) {
@@ -9074,7 +9132,13 @@ var MarketIngestor = class {
9074
9132
  expired: true
9075
9133
  });
9076
9134
  } catch (error) {
9077
- const mapped = this.mapProviderFailure(provider, "chain", upperUnderlying, error, assetClass);
9135
+ const mapped = this.mapProviderFailure(
9136
+ provider,
9137
+ "chain",
9138
+ upperUnderlying,
9139
+ error,
9140
+ assetClass
9141
+ );
9078
9142
  if (mapped) return mapped;
9079
9143
  throw error;
9080
9144
  }
@@ -9222,43 +9286,75 @@ var MarketIngestor = class {
9222
9286
  case "spot_bars": {
9223
9287
  const ticker = opts.partition.ticker;
9224
9288
  if (!ticker) {
9225
- return { status: "error", rowsWritten: 0, error: "partition.ticker is required for datasetType='spot_bars'" };
9289
+ return {
9290
+ status: "error",
9291
+ rowsWritten: 0,
9292
+ error: "partition.ticker is required for datasetType='spot_bars'"
9293
+ };
9226
9294
  }
9227
9295
  const { rowCount } = await this.deps.stores.spot.writeFromSelect(
9228
9296
  { ticker: ticker.toUpperCase(), date: partitionDate },
9229
9297
  opts.selectSql
9230
9298
  );
9231
- return { status: "ok", rowsWritten: rowCount, dateRange: { from: partitionDate, to: partitionDate } };
9299
+ return {
9300
+ status: "ok",
9301
+ rowsWritten: rowCount,
9302
+ dateRange: { from: partitionDate, to: partitionDate }
9303
+ };
9232
9304
  }
9233
9305
  case "option_quotes": {
9234
9306
  const underlying = opts.partition.underlying;
9235
9307
  if (!underlying) {
9236
- return { status: "error", rowsWritten: 0, error: "partition.underlying is required for datasetType='option_quotes'" };
9308
+ return {
9309
+ status: "error",
9310
+ rowsWritten: 0,
9311
+ error: "partition.underlying is required for datasetType='option_quotes'"
9312
+ };
9237
9313
  }
9238
9314
  const { rowCount } = await this.deps.stores.quote.writeFromSelect(
9239
9315
  { underlying: underlying.toUpperCase(), date: partitionDate },
9240
9316
  opts.selectSql
9241
9317
  );
9242
- return { status: "ok", rowsWritten: rowCount, dateRange: { from: partitionDate, to: partitionDate } };
9318
+ return {
9319
+ status: "ok",
9320
+ rowsWritten: rowCount,
9321
+ dateRange: { from: partitionDate, to: partitionDate }
9322
+ };
9243
9323
  }
9244
9324
  case "option_chain": {
9245
9325
  const underlying = opts.partition.underlying;
9246
9326
  if (!underlying) {
9247
- return { status: "error", rowsWritten: 0, error: "partition.underlying is required for datasetType='option_chain'" };
9327
+ return {
9328
+ status: "error",
9329
+ rowsWritten: 0,
9330
+ error: "partition.underlying is required for datasetType='option_chain'"
9331
+ };
9248
9332
  }
9249
9333
  const { rowCount } = await this.deps.stores.chain.writeFromSelect(
9250
9334
  { underlying: underlying.toUpperCase(), date: partitionDate },
9251
9335
  opts.selectSql
9252
9336
  );
9253
- return { status: "ok", rowsWritten: rowCount, dateRange: { from: partitionDate, to: partitionDate } };
9337
+ return {
9338
+ status: "ok",
9339
+ rowsWritten: rowCount,
9340
+ dateRange: { from: partitionDate, to: partitionDate }
9341
+ };
9254
9342
  }
9255
9343
  default: {
9256
9344
  const _exhaustive = opts.datasetType;
9257
- return { status: "error", rowsWritten: 0, error: `Unknown datasetType: ${String(_exhaustive)}` };
9345
+ return {
9346
+ status: "error",
9347
+ rowsWritten: 0,
9348
+ error: `Unknown datasetType: ${String(_exhaustive)}`
9349
+ };
9258
9350
  }
9259
9351
  }
9260
9352
  } catch (err) {
9261
- return { status: "error", rowsWritten: 0, error: err instanceof Error ? err.message : String(err) };
9353
+ return {
9354
+ status: "error",
9355
+ rowsWritten: 0,
9356
+ error: err instanceof Error ? err.message : String(err)
9357
+ };
9262
9358
  }
9263
9359
  }
9264
9360
  async computeVixContext(opts) {
@@ -9348,7 +9444,11 @@ var MarketIngestor = class {
9348
9444
  const coverage = {};
9349
9445
  for (const ticker of opts.spotTickers) {
9350
9446
  try {
9351
- const cov = await this.deps.stores.spot.getCoverage(ticker.toUpperCase(), opts.asOf, opts.asOf);
9447
+ const cov = await this.deps.stores.spot.getCoverage(
9448
+ ticker.toUpperCase(),
9449
+ opts.asOf,
9450
+ opts.asOf
9451
+ );
9352
9452
  coverage[ticker] = {
9353
9453
  totalDates: cov.totalDates,
9354
9454
  dateRange: cov.earliest && cov.latest ? { from: cov.earliest, to: cov.latest } : void 0
@@ -9372,7 +9472,13 @@ var MarketIngestor = class {
9372
9472
  else status = "ok";
9373
9473
  return {
9374
9474
  status,
9375
- perOperation: { spot: spotResults, chain: chainResults, quotes: quoteResults, openInterest: openInterestResults, vixContext },
9475
+ perOperation: {
9476
+ spot: spotResults,
9477
+ chain: chainResults,
9478
+ quotes: quoteResults,
9479
+ openInterest: openInterestResults,
9480
+ vixContext
9481
+ },
9376
9482
  coverage,
9377
9483
  errors,
9378
9484
  ...aggregateSkipped.length > 0 ? { skipped: aggregateSkipped } : {}
@@ -9455,9 +9561,7 @@ function selectVerificationSampleDates(fromDate = "2022-01-01", toDate = (/* @__
9455
9561
  ...PHASE_5_KNOWN_EVENTS.map((s) => s.date),
9456
9562
  ...PHASE_5_STRUCTURAL_DATES.map((s) => s.date)
9457
9563
  ]);
9458
- const candidates = enumerateWeekdays(fromDate, toDate).filter(
9459
- (d) => !selectedSet.has(d)
9460
- );
9564
+ const candidates = enumerateWeekdays(fromDate, toDate).filter((d) => !selectedSet.has(d));
9461
9565
  const random = [];
9462
9566
  const pool = [...candidates];
9463
9567
  for (let i = 0; i < randomCount && pool.length > 0; i++) {