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
@@ -25,10 +25,7 @@ import type { ReportingTrade } from "@tradeblocks/lib";
25
25
  /**
26
26
  * Register edge decay analysis MCP tools
27
27
  */
28
- export function registerEdgeDecayTools(
29
- server: McpServer,
30
- baseDir: string
31
- ): void {
28
+ export function registerEdgeDecayTools(server: McpServer, baseDir: string): void {
32
29
  // Tool 1: analyze_period_metrics
33
30
  server.registerTool(
34
31
  "analyze_period_metrics",
@@ -37,10 +34,7 @@ export function registerEdgeDecayTools(
37
34
  "Segment a block's trades by year, quarter, and month with per-period statistics, trend detection via linear regression, and worst consecutive losing month identification. Foundation for edge decay analysis.",
38
35
  inputSchema: z.object({
39
36
  blockId: z.string().describe("Block folder name"),
40
- strategy: z
41
- .string()
42
- .optional()
43
- .describe("Filter by strategy name (case-insensitive)"),
37
+ strategy: z.string().optional().describe("Filter by strategy name (case-insensitive)"),
44
38
  }),
45
39
  },
46
40
  withSyncedBlock(baseDir, async ({ blockId, strategy }) => {
@@ -104,7 +98,7 @@ export function registerEdgeDecayTools(
104
98
  isError: true as const,
105
99
  };
106
100
  }
107
- })
101
+ }),
108
102
  );
109
103
 
110
104
  // Tool 2: analyze_rolling_metrics
@@ -115,37 +109,30 @@ export function registerEdgeDecayTools(
115
109
  "Compute rolling window statistics, quarterly seasonal averages, and recent-vs-historical comparison with structural flags for a block's trades. Foundation for edge decay analysis.",
116
110
  inputSchema: z.object({
117
111
  blockId: z.string().describe("Block folder name"),
118
- strategy: z
119
- .string()
120
- .optional()
121
- .describe("Filter by strategy name (case-insensitive)"),
112
+ strategy: z.string().optional().describe("Filter by strategy name (case-insensitive)"),
122
113
  windowSize: z
123
114
  .number()
124
115
  .min(5)
125
116
  .optional()
126
117
  .describe(
127
- "Rolling window size in trades (default: auto-calculated based on trade count)"
118
+ "Rolling window size in trades (default: auto-calculated based on trade count)",
128
119
  ),
129
120
  recentWindowSize: z
130
121
  .number()
131
122
  .min(10)
132
123
  .optional()
133
- .describe(
134
- "Recent window size in trades for comparison (default: auto-calculated)"
135
- ),
124
+ .describe("Recent window size in trades for comparison (default: auto-calculated)"),
136
125
  recentWindowDays: z
137
126
  .number()
138
127
  .min(7)
139
128
  .optional()
140
- .describe(
141
- "Override: recent window as calendar days instead of trade count"
142
- ),
129
+ .describe("Override: recent window as calendar days instead of trade count"),
143
130
  includeSeries: z
144
131
  .boolean()
145
132
  .optional()
146
133
  .default(false)
147
134
  .describe(
148
- "Include full rolling series data points in output (default: false, saves tokens)"
135
+ "Include full rolling series data points in output (default: false, saves tokens)",
149
136
  ),
150
137
  }),
151
138
  },
@@ -190,27 +177,17 @@ export function registerEdgeDecayTools(
190
177
  const seriesLength = result.series.length;
191
178
  const flags = result.recentVsHistorical.structuralFlags;
192
179
  const flagCount = flags.length;
193
- const flagNames =
194
- flagCount > 0
195
- ? flags.map((f) => f.metric).join(", ")
196
- : "none";
180
+ const flagNames = flagCount > 0 ? flags.map((f) => f.metric).join(", ") : "none";
197
181
 
198
182
  const recentMetrics = result.recentVsHistorical.metrics;
199
- const recentWR =
200
- recentMetrics.find((m) => m.metric === "winRate")?.recentValue;
201
- const histWR =
202
- recentMetrics.find((m) => m.metric === "winRate")?.historicalValue;
203
- const recentPF =
204
- recentMetrics.find((m) => m.metric === "profitFactor")
205
- ?.recentValue;
206
- const histPF =
207
- recentMetrics.find((m) => m.metric === "profitFactor")
208
- ?.historicalValue;
183
+ const recentWR = recentMetrics.find((m) => m.metric === "winRate")?.recentValue;
184
+ const histWR = recentMetrics.find((m) => m.metric === "winRate")?.historicalValue;
185
+ const recentPF = recentMetrics.find((m) => m.metric === "profitFactor")?.recentValue;
186
+ const histPF = recentMetrics.find((m) => m.metric === "profitFactor")?.historicalValue;
209
187
 
210
188
  const fmtPct = (v: number | undefined) =>
211
189
  v !== undefined ? `${(v * 100).toFixed(1)}%` : "N/A";
212
- const fmtRatio = (v: number | undefined) =>
213
- v !== undefined ? v.toFixed(2) : "N/A";
190
+ const fmtRatio = (v: number | undefined) => (v !== undefined ? v.toFixed(2) : "N/A");
214
191
 
215
192
  const summary = `Rolling metrics for ${blockId}${strategy ? ` (${strategy})` : ""}: ${result.dataQuality.totalTrades} trades, window=${result.windowSize}\nRolling series: ${seriesLength} data points\nStructural flags: ${flagCount} (${flagNames})\nRecent vs historical: win rate ${fmtPct(recentWR)} vs ${fmtPct(histWR)}, PF ${fmtRatio(recentPF)} vs ${fmtRatio(histPF)}`;
216
193
 
@@ -236,8 +213,8 @@ export function registerEdgeDecayTools(
236
213
  isError: true as const,
237
214
  };
238
215
  }
239
- }
240
- )
216
+ },
217
+ ),
241
218
  );
242
219
 
243
220
  // Tool 3: analyze_regime_comparison
@@ -248,36 +225,28 @@ export function registerEdgeDecayTools(
248
225
  "Run dual Monte Carlo simulations comparing full trade history vs recent window to detect regime divergence. Compares P(Profit), expected return, Sharpe ratio, and median max drawdown between the two periods. Returns a composite divergence score (0 = aligned, higher = more divergent).",
249
226
  inputSchema: z.object({
250
227
  blockId: z.string().describe("Block folder name"),
251
- strategy: z
252
- .string()
253
- .optional()
254
- .describe("Filter by strategy name (case-insensitive)"),
228
+ strategy: z.string().optional().describe("Filter by strategy name (case-insensitive)"),
255
229
  recentWindowSize: z
256
230
  .number()
257
231
  .min(20)
258
232
  .optional()
259
233
  .describe(
260
- "Number of recent trades for the recent window simulation (default: auto-calculated, typically max(20% of trades, 200))"
234
+ "Number of recent trades for the recent window simulation (default: auto-calculated, typically max(20% of trades, 200))",
261
235
  ),
262
236
  numSimulations: z
263
237
  .number()
264
238
  .min(50)
265
239
  .max(10000)
266
240
  .optional()
267
- .describe(
268
- "Number of Monte Carlo simulation paths (default: 1000)"
269
- ),
241
+ .describe("Number of Monte Carlo simulation paths (default: 1000)"),
270
242
  simulationLength: z
271
243
  .number()
272
244
  .min(10)
273
245
  .optional()
274
246
  .describe(
275
- "Number of trades to project forward per simulation (default: recentWindowSize)"
247
+ "Number of trades to project forward per simulation (default: recentWindowSize)",
276
248
  ),
277
- randomSeed: z
278
- .number()
279
- .optional()
280
- .describe("Random seed for reproducibility (default: 42)"),
249
+ randomSeed: z.number().optional().describe("Random seed for reproducibility (default: 42)"),
281
250
  }),
282
251
  },
283
252
  withSyncedBlock(
@@ -320,16 +289,12 @@ export function registerEdgeDecayTools(
320
289
  });
321
290
 
322
291
  // Build summary
323
- const fullPProfit = (
324
- result.fullHistory.statistics.probabilityOfProfit * 100
325
- ).toFixed(1);
326
- const recentPProfit = (
327
- result.recentWindow.statistics.probabilityOfProfit * 100
328
- ).toFixed(1);
329
- const fullSharpe =
330
- result.fullHistory.statistics.meanSharpeRatio.toFixed(2);
331
- const recentSharpe =
332
- result.recentWindow.statistics.meanSharpeRatio.toFixed(2);
292
+ const fullPProfit = (result.fullHistory.statistics.probabilityOfProfit * 100).toFixed(1);
293
+ const recentPProfit = (result.recentWindow.statistics.probabilityOfProfit * 100).toFixed(
294
+ 1,
295
+ );
296
+ const fullSharpe = result.fullHistory.statistics.meanSharpeRatio.toFixed(2);
297
+ const recentSharpe = result.recentWindow.statistics.meanSharpeRatio.toFixed(2);
333
298
  const score = result.divergence.compositeScore.toFixed(2);
334
299
 
335
300
  const summary = `Regime comparison for ${blockId}${strategy ? ` (${strategy})` : ""}: ${result.fullHistory.tradeCount} full / ${result.recentWindow.tradeCount} recent trades\nP(Profit): ${fullPProfit}% (full) vs ${recentPProfit}% (recent) | Sharpe: ${fullSharpe} (full) vs ${recentSharpe} (recent)\nDivergence: score ${score}`;
@@ -341,36 +306,24 @@ export function registerEdgeDecayTools(
341
306
  tradeCount: result.fullHistory.tradeCount,
342
307
  dateRange: result.fullHistory.dateRange,
343
308
  statistics: {
344
- probabilityOfProfit:
345
- result.fullHistory.statistics.probabilityOfProfit,
346
- meanTotalReturn:
347
- result.fullHistory.statistics.meanTotalReturn,
348
- meanSharpeRatio:
349
- result.fullHistory.statistics.meanSharpeRatio,
350
- medianMaxDrawdown:
351
- result.fullHistory.statistics.medianMaxDrawdown,
352
- meanFinalValue:
353
- result.fullHistory.statistics.meanFinalValue,
354
- medianFinalValue:
355
- result.fullHistory.statistics.medianFinalValue,
309
+ probabilityOfProfit: result.fullHistory.statistics.probabilityOfProfit,
310
+ meanTotalReturn: result.fullHistory.statistics.meanTotalReturn,
311
+ meanSharpeRatio: result.fullHistory.statistics.meanSharpeRatio,
312
+ medianMaxDrawdown: result.fullHistory.statistics.medianMaxDrawdown,
313
+ meanFinalValue: result.fullHistory.statistics.meanFinalValue,
314
+ medianFinalValue: result.fullHistory.statistics.medianFinalValue,
356
315
  },
357
316
  },
358
317
  recentWindow: {
359
318
  tradeCount: result.recentWindow.tradeCount,
360
319
  dateRange: result.recentWindow.dateRange,
361
320
  statistics: {
362
- probabilityOfProfit:
363
- result.recentWindow.statistics.probabilityOfProfit,
364
- meanTotalReturn:
365
- result.recentWindow.statistics.meanTotalReturn,
366
- meanSharpeRatio:
367
- result.recentWindow.statistics.meanSharpeRatio,
368
- medianMaxDrawdown:
369
- result.recentWindow.statistics.medianMaxDrawdown,
370
- meanFinalValue:
371
- result.recentWindow.statistics.meanFinalValue,
372
- medianFinalValue:
373
- result.recentWindow.statistics.medianFinalValue,
321
+ probabilityOfProfit: result.recentWindow.statistics.probabilityOfProfit,
322
+ meanTotalReturn: result.recentWindow.statistics.meanTotalReturn,
323
+ meanSharpeRatio: result.recentWindow.statistics.meanSharpeRatio,
324
+ medianMaxDrawdown: result.recentWindow.statistics.medianMaxDrawdown,
325
+ meanFinalValue: result.recentWindow.statistics.meanFinalValue,
326
+ medianFinalValue: result.recentWindow.statistics.medianFinalValue,
374
327
  },
375
328
  },
376
329
  comparison: result.comparison,
@@ -390,8 +343,8 @@ export function registerEdgeDecayTools(
390
343
  isError: true as const,
391
344
  };
392
345
  }
393
- }
394
- )
346
+ },
347
+ ),
395
348
  );
396
349
 
397
350
  // Tool 4: analyze_walk_forward_degradation
@@ -402,24 +355,17 @@ export function registerEdgeDecayTools(
402
355
  "Run progressive walk-forward analysis to track whether out-of-sample performance is degrading relative to in-sample. Slides IS/OOS windows across trade history, computes efficiency ratios (OOS metric / IS metric) for Sharpe, win rate, and profit factor, detects trends via linear regression, and compares recent vs historical OOS efficiency.",
403
356
  inputSchema: z.object({
404
357
  blockId: z.string().describe("Block folder name"),
405
- strategy: z
406
- .string()
407
- .optional()
408
- .describe("Filter by strategy name (case-insensitive)"),
358
+ strategy: z.string().optional().describe("Filter by strategy name (case-insensitive)"),
409
359
  inSampleDays: z
410
360
  .number()
411
361
  .min(30)
412
362
  .optional()
413
- .describe(
414
- "In-sample window in calendar days (default: 365)"
415
- ),
363
+ .describe("In-sample window in calendar days (default: 365)"),
416
364
  outOfSampleDays: z
417
365
  .number()
418
366
  .min(7)
419
367
  .optional()
420
- .describe(
421
- "Out-of-sample window in calendar days (default: 90)"
422
- ),
368
+ .describe("Out-of-sample window in calendar days (default: 90)"),
423
369
  stepSizeDays: z
424
370
  .number()
425
371
  .min(7)
@@ -429,16 +375,12 @@ export function registerEdgeDecayTools(
429
375
  .number()
430
376
  .min(1)
431
377
  .optional()
432
- .describe(
433
- "Minimum trades for a period to be considered sufficient (default: 10)"
434
- ),
378
+ .describe("Minimum trades for a period to be considered sufficient (default: 10)"),
435
379
  recentPeriodCount: z
436
380
  .number()
437
381
  .min(1)
438
382
  .optional()
439
- .describe(
440
- "Number of recent WF periods for comparison (default: 3)"
441
- ),
383
+ .describe("Number of recent WF periods for comparison (default: 3)"),
442
384
  }),
443
385
  },
444
386
  withSyncedBlock(
@@ -485,8 +427,7 @@ export function registerEdgeDecayTools(
485
427
  // Build text summary
486
428
  const dq = result.dataQuality;
487
429
  const rvh = result.recentVsHistorical;
488
- const fmtVal = (v: number | null) =>
489
- v !== null ? v.toFixed(2) : "N/A";
430
+ const fmtVal = (v: number | null) => (v !== null ? v.toFixed(2) : "N/A");
490
431
 
491
432
  const summary = [
492
433
  `WF degradation for ${blockId}${strategy ? ` (${strategy})` : ""}: ${dq.totalTrades} trades, ${dq.totalPeriods} periods (${dq.sufficientPeriods} sufficient)`,
@@ -517,8 +458,8 @@ export function registerEdgeDecayTools(
517
458
  isError: true as const,
518
459
  };
519
460
  }
520
- }
521
- )
461
+ },
462
+ ),
522
463
  );
523
464
 
524
465
  // Tool 5: analyze_live_alignment
@@ -529,15 +470,12 @@ export function registerEdgeDecayTools(
529
470
  "Compare backtest trades against actual (reporting log) trades to assess live execution alignment. Computes direction agreement rate (% of days where both agree on win/loss), per-strategy execution efficiency (actual P/L as ratio of backtest P/L), and alignment trend over time via monthly regression. Returns graceful skip when no reporting log exists.",
530
471
  inputSchema: z.object({
531
472
  blockId: z.string().describe("Block folder name"),
532
- strategy: z
533
- .string()
534
- .optional()
535
- .describe("Filter by strategy name (case-insensitive)"),
473
+ strategy: z.string().optional().describe("Filter by strategy name (case-insensitive)"),
536
474
  scaling: z
537
475
  .enum(["raw", "perContract", "toReported"])
538
476
  .optional()
539
477
  .describe(
540
- "P/L scaling mode: raw (as-is), perContract (divide by contracts, default), toReported (scale backtest to actual contract count)"
478
+ "P/L scaling mode: raw (as-is), perContract (divide by contracts, default), toReported (scale backtest to actual contract count)",
541
479
  ),
542
480
  }),
543
481
  },
@@ -557,7 +495,7 @@ export function registerEdgeDecayTools(
557
495
  strategy: strategy ?? null,
558
496
  available: false,
559
497
  reason: "no reporting log",
560
- }
498
+ },
561
499
  );
562
500
  }
563
501
 
@@ -585,15 +523,12 @@ export function registerEdgeDecayTools(
585
523
  });
586
524
 
587
525
  if (!output.available) {
588
- return createToolOutput(
589
- `Live alignment for ${blockId}: skipped (${output.reason})`,
590
- {
591
- blockId,
592
- strategy: strategy ?? null,
593
- available: false,
594
- reason: output.reason,
595
- }
596
- );
526
+ return createToolOutput(`Live alignment for ${blockId}: skipped (${output.reason})`, {
527
+ blockId,
528
+ strategy: strategy ?? null,
529
+ available: false,
530
+ reason: output.reason,
531
+ });
597
532
  }
598
533
 
599
534
  const result = output;
@@ -603,8 +538,7 @@ export function registerEdgeDecayTools(
603
538
  const ee = result.executionEfficiency;
604
539
  const dq = result.dataQuality;
605
540
  const fmtPct = (v: number) => (v * 100).toFixed(1) + "%";
606
- const fmtVal = (v: number | null) =>
607
- v !== null ? v.toFixed(2) : "N/A";
541
+ const fmtVal = (v: number | null) => (v !== null ? v.toFixed(2) : "N/A");
608
542
 
609
543
  const summary = [
610
544
  `Live alignment for ${blockId}${strategy ? ` (${strategy})` : ""}: ${dq.backtestTradeCount} backtest, ${dq.actualTradeCount} actual, ${dq.matchedTradeCount} matched (${fmtPct(dq.matchRate)})`,
@@ -636,7 +570,7 @@ export function registerEdgeDecayTools(
636
570
  isError: true as const,
637
571
  };
638
572
  }
639
- })
573
+ }),
640
574
  );
641
575
 
642
576
  // Tool 6: analyze_edge_decay (unified)
@@ -651,95 +585,88 @@ export function registerEdgeDecayTools(
651
585
  "analyze_walk_forward_degradation, analyze_live_alignment) for detailed drill-down or custom parameters.",
652
586
  inputSchema: z.object({
653
587
  blockId: z.string().describe("Block folder name"),
654
- strategy: z
655
- .string()
656
- .optional()
657
- .describe("Filter by strategy name (case-insensitive)"),
588
+ strategy: z.string().optional().describe("Filter by strategy name (case-insensitive)"),
658
589
  recentWindow: z
659
590
  .number()
660
591
  .min(10)
661
592
  .optional()
662
593
  .describe(
663
- "Number of recent trades for comparison (default: auto-calculated as max(20% of trades, 200))"
594
+ "Number of recent trades for comparison (default: auto-calculated as max(20% of trades, 200))",
664
595
  ),
665
596
  }),
666
597
  },
667
- withSyncedBlock(
668
- baseDir,
669
- async ({ blockId, strategy, recentWindow }) => {
670
- try {
671
- const block = await loadBlock(baseDir, blockId);
672
- const trades = applyStrategyFilter(block.trades, strategy);
673
-
674
- if (trades.length === 0) {
675
- return {
676
- content: [
677
- {
678
- type: "text" as const,
679
- text: strategy
680
- ? `No trades found for strategy "${strategy}" in block "${blockId}".`
681
- : `No trades found in block "${blockId}".`,
682
- },
683
- ],
684
- isError: true as const,
685
- };
686
- }
687
-
688
- // Load reporting log -- graceful skip if missing
689
- let actualTrades: ReportingTrade[] | undefined;
690
- try {
691
- const raw = await loadReportingLog(baseDir, blockId);
692
- actualTrades = applyStrategyFilter(raw, strategy);
693
- } catch {
694
- actualTrades = undefined;
695
- }
696
-
697
- // Call pure synthesis engine
698
- const result = synthesizeEdgeDecay(trades, actualTrades, {
699
- recentWindow,
700
- });
701
-
702
- // Build text summary
703
- const s = result.summary;
704
- const fmtPct = (v: number | null) => v !== null ? (v * 100).toFixed(1) + "%" : "N/A";
705
- const fmtRatio = (v: number | null) =>
706
- v !== null ? v.toFixed(2) : "N/A";
707
-
708
- const lines = [
709
- `Edge decay analysis for ${blockId}${strategy ? ` (${strategy})` : ""}: ${s.totalTrades} trades, recent window=${s.recentWindow}`,
710
- `Win rate: ${fmtPct(s.recentWinRate)} recent vs ${fmtPct(s.historicalWinRate)} historical`,
711
- `Profit factor: ${fmtRatio(s.recentProfitFactor)} recent vs ${fmtRatio(s.historicalProfitFactor)} historical`,
712
- `Sharpe: ${fmtRatio(s.recentSharpe)} recent vs ${fmtRatio(s.historicalSharpe)} historical`,
713
- `Signals: ${result.metadata.signalsRun} run, ${result.metadata.signalsSkipped} skipped`,
714
- `Observations: ${s.observationCount} notable (${s.structuralFlagCount} structural flags)`,
715
- ];
716
-
717
- if (s.mcProbabilityOfProfit) {
718
- lines.push(
719
- `MC P(Profit): ${(s.mcProbabilityOfProfit.full * 100).toFixed(1)}% full vs ${(s.mcProbabilityOfProfit.recent * 100).toFixed(1)}% recent`
720
- );
721
- }
722
- if (s.liveDirectionAgreement !== null) {
723
- lines.push(
724
- `Live alignment: ${fmtPct(s.liveDirectionAgreement)} direction agreement, ${fmtRatio(s.liveExecutionEfficiency)} efficiency`
725
- );
726
- }
727
-
728
- const summaryText = lines.join("\n");
598
+ withSyncedBlock(baseDir, async ({ blockId, strategy, recentWindow }) => {
599
+ try {
600
+ const block = await loadBlock(baseDir, blockId);
601
+ const trades = applyStrategyFilter(block.trades, strategy);
729
602
 
730
- return createToolOutput(summaryText, result);
731
- } catch (error) {
603
+ if (trades.length === 0) {
732
604
  return {
733
605
  content: [
734
606
  {
735
607
  type: "text" as const,
736
- text: `Error analyzing edge decay: ${(error as Error).message}`,
608
+ text: strategy
609
+ ? `No trades found for strategy "${strategy}" in block "${blockId}".`
610
+ : `No trades found in block "${blockId}".`,
737
611
  },
738
612
  ],
739
613
  isError: true as const,
740
614
  };
741
615
  }
616
+
617
+ // Load reporting log -- graceful skip if missing
618
+ let actualTrades: ReportingTrade[] | undefined;
619
+ try {
620
+ const raw = await loadReportingLog(baseDir, blockId);
621
+ actualTrades = applyStrategyFilter(raw, strategy);
622
+ } catch {
623
+ actualTrades = undefined;
624
+ }
625
+
626
+ // Call pure synthesis engine
627
+ const result = synthesizeEdgeDecay(trades, actualTrades, {
628
+ recentWindow,
629
+ });
630
+
631
+ // Build text summary
632
+ const s = result.summary;
633
+ const fmtPct = (v: number | null) => (v !== null ? (v * 100).toFixed(1) + "%" : "N/A");
634
+ const fmtRatio = (v: number | null) => (v !== null ? v.toFixed(2) : "N/A");
635
+
636
+ const lines = [
637
+ `Edge decay analysis for ${blockId}${strategy ? ` (${strategy})` : ""}: ${s.totalTrades} trades, recent window=${s.recentWindow}`,
638
+ `Win rate: ${fmtPct(s.recentWinRate)} recent vs ${fmtPct(s.historicalWinRate)} historical`,
639
+ `Profit factor: ${fmtRatio(s.recentProfitFactor)} recent vs ${fmtRatio(s.historicalProfitFactor)} historical`,
640
+ `Sharpe: ${fmtRatio(s.recentSharpe)} recent vs ${fmtRatio(s.historicalSharpe)} historical`,
641
+ `Signals: ${result.metadata.signalsRun} run, ${result.metadata.signalsSkipped} skipped`,
642
+ `Observations: ${s.observationCount} notable (${s.structuralFlagCount} structural flags)`,
643
+ ];
644
+
645
+ if (s.mcProbabilityOfProfit) {
646
+ lines.push(
647
+ `MC P(Profit): ${(s.mcProbabilityOfProfit.full * 100).toFixed(1)}% full vs ${(s.mcProbabilityOfProfit.recent * 100).toFixed(1)}% recent`,
648
+ );
649
+ }
650
+ if (s.liveDirectionAgreement !== null) {
651
+ lines.push(
652
+ `Live alignment: ${fmtPct(s.liveDirectionAgreement)} direction agreement, ${fmtRatio(s.liveExecutionEfficiency)} efficiency`,
653
+ );
654
+ }
655
+
656
+ const summaryText = lines.join("\n");
657
+
658
+ return createToolOutput(summaryText, result);
659
+ } catch (error) {
660
+ return {
661
+ content: [
662
+ {
663
+ type: "text" as const,
664
+ text: `Error analyzing edge decay: ${(error as Error).message}`,
665
+ },
666
+ ],
667
+ isError: true as const,
668
+ };
742
669
  }
743
- )
670
+ }),
744
671
  );
745
672
  }