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
@@ -14,12 +14,7 @@
14
14
  import { z } from "zod";
15
15
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
16
16
  import { getConnection, upgradeToReadWrite, downgradeToReadOnly } from "../db/connection.ts";
17
- import {
18
- upsertProfile,
19
- getProfile,
20
- listProfiles,
21
- deleteProfile,
22
- } from "../db/profile-schemas.ts";
17
+ import { upsertProfile, getProfile, listProfiles, deleteProfile } from "../db/profile-schemas.ts";
23
18
  import { createToolOutput } from "../utils/output-formatter.ts";
24
19
  import { withSyncedBlock } from "./middleware/sync-middleware.ts";
25
20
 
@@ -34,13 +29,13 @@ export const profileStrategySchema = z.object({
34
29
  .string()
35
30
  .describe(
36
31
  "Option structure type: iron_condor, calendar_spread, double_calendar, vertical_spread, " +
37
- "butterfly, reverse_iron_condor, short_put_spread, short_call_spread, straddle, strangle, etc."
32
+ "butterfly, reverse_iron_condor, short_put_spread, short_call_spread, straddle, strangle, etc.",
38
33
  ),
39
34
  greeksBias: z
40
35
  .string()
41
36
  .describe(
42
37
  "Primary greeks exposure: theta_positive, vega_negative, delta_neutral, delta_positive, " +
43
- "delta_negative, gamma_scalp, etc."
38
+ "delta_negative, gamma_scalp, etc.",
44
39
  ),
45
40
  thesis: z.string().default("").describe("Free-text description of the strategy thesis"),
46
41
  legs: z
@@ -50,9 +45,12 @@ export const profileStrategySchema = z.object({
50
45
  strike: z.string().describe("Strike selection: ATM, 5-delta, 30-delta, etc."),
51
46
  expiry: z.string().describe("Expiry selection: same-day, weekly, 45-DTE, etc."),
52
47
  quantity: z.number().describe("Quantity (positive=long, negative=short)"),
53
- strikeMethod: z.enum(['delta', 'dollar_price', 'offset', 'percentage']).optional().describe("How strike is selected"),
48
+ strikeMethod: z
49
+ .enum(["delta", "dollar_price", "offset", "percentage"])
50
+ .optional()
51
+ .describe("How strike is selected"),
54
52
  strikeValue: z.number().optional().describe("Numeric strike value (e.g., 25 for 25-delta)"),
55
- })
53
+ }),
56
54
  )
57
55
  .default([])
58
56
  .describe("Structured leg descriptions"),
@@ -65,32 +63,52 @@ export const profileStrategySchema = z.object({
65
63
  .union([z.string(), z.number(), z.array(z.union([z.string(), z.number()]))])
66
64
  .describe("Filter value or array for between/in operators"),
67
65
  description: z.string().optional().describe("Human-readable description of this filter"),
68
- 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."),
69
- })
66
+ source: z
67
+ .enum(["market", "execution"])
68
+ .optional()
69
+ .describe(
70
+ "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.",
71
+ ),
72
+ }),
70
73
  )
71
74
  .default([])
72
- .describe("Entry condition filters. Tag each with source: 'market' (testable in analysis) or 'execution' (OO/platform-level, skipped in analysis)."),
75
+ .describe(
76
+ "Entry condition filters. Tag each with source: 'market' (testable in analysis) or 'execution' (OO/platform-level, skipped in analysis).",
77
+ ),
73
78
  exitRules: z
74
79
  .array(
75
80
  z.object({
76
81
  type: z.string().describe("Rule type: stop_loss, profit_target, time_exit, conditional"),
77
- trigger: z.string().describe("Trigger condition: '200% of credit', '50% of max profit', '15:00 ET'"),
82
+ trigger: z
83
+ .string()
84
+ .describe("Trigger condition: '200% of credit', '50% of max profit', '15:00 ET'"),
78
85
  description: z.string().optional().describe("Human-readable description"),
79
- stopLossType: z.enum(['percentage', 'dollar', 'sl_ratio', 'debit_percentage']).optional().describe("Stop loss calculation method"),
86
+ stopLossType: z
87
+ .enum(["percentage", "dollar", "sl_ratio", "debit_percentage"])
88
+ .optional()
89
+ .describe("Stop loss calculation method"),
80
90
  stopLossValue: z.number().optional().describe("Stop loss numeric value"),
81
- monitoring: z.object({
82
- granularity: z.enum(['intra_minute', 'candle_close', 'end_of_bar']).optional().describe("Price check frequency"),
83
- priceSource: z.enum(['nbbo', 'mid', 'last']).optional().describe("Which price to use"),
84
- }).optional().describe("Monitoring configuration for this rule"),
91
+ monitoring: z
92
+ .object({
93
+ granularity: z
94
+ .enum(["intra_minute", "candle_close", "end_of_bar"])
95
+ .optional()
96
+ .describe("Price check frequency"),
97
+ priceSource: z.enum(["nbbo", "mid", "last"]).optional().describe("Which price to use"),
98
+ })
99
+ .optional()
100
+ .describe("Monitoring configuration for this rule"),
85
101
  slippage: z.number().optional().describe("Per-rule slippage override"),
86
- })
102
+ }),
87
103
  )
88
104
  .default([])
89
105
  .describe("Exit rules and triggers"),
90
106
  expectedRegimes: z
91
107
  .array(z.enum(["very_low", "low", "below_avg", "above_avg", "high", "extreme"]))
92
108
  .default([])
93
- .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+"),
109
+ .describe(
110
+ "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+",
111
+ ),
94
112
  keyMetrics: z
95
113
  .object({
96
114
  expectedWinRate: z.number().optional().describe("Expected win rate (0-1)"),
@@ -103,24 +121,37 @@ export const profileStrategySchema = z.object({
103
121
  .describe("Performance benchmarks and strategy-specific metrics"),
104
122
  positionSizing: z
105
123
  .object({
106
- method: z.string().describe("Sizing method: pct_of_portfolio, fixed_contracts, fixed_dollar, discretionary"),
107
- allocationPct: z.number().optional().describe("Portfolio allocation percentage (e.g., 2 for 2%)"),
124
+ method: z
125
+ .string()
126
+ .describe("Sizing method: pct_of_portfolio, fixed_contracts, fixed_dollar, discretionary"),
127
+ allocationPct: z
128
+ .number()
129
+ .optional()
130
+ .describe("Portfolio allocation percentage (e.g., 2 for 2%)"),
108
131
  maxContracts: z.number().optional().describe("Maximum contracts per trade"),
109
132
  maxAllocationDollar: z.number().optional().describe("Maximum dollar allocation per trade"),
110
133
  maxOpenPositions: z.number().optional().describe("Maximum concurrent open positions"),
111
134
  description: z.string().optional().describe("Free-text sizing notes"),
112
135
  backtestAllocationPct: z.number().optional().describe("Allocation % used in backtest"),
113
136
  liveAllocationPct: z.number().optional().describe("Allocation % used in live portfolio"),
114
- maxContractsPerTrade: z.number().optional().describe("Per-entry contract cap (distinct from maxContracts hard cap)"),
137
+ maxContractsPerTrade: z
138
+ .number()
139
+ .optional()
140
+ .describe("Per-entry contract cap (distinct from maxContracts hard cap)"),
115
141
  })
116
142
  .optional()
117
- .describe("Position sizing rules. Per-block — same strategy in backtest vs portfolio may have different sizing."),
143
+ .describe(
144
+ "Position sizing rules. Per-block — same strategy in backtest vs portfolio may have different sizing.",
145
+ ),
118
146
  underlying: z.string().optional().describe("Underlying symbol: SPX, QQQ, etc."),
119
147
  reEntry: z.boolean().optional().describe("Strategy supports re-entry on same day"),
120
148
  capProfits: z.boolean().optional().describe("Profits are capped by structure"),
121
149
  capLosses: z.boolean().optional().describe("Losses are capped by structure"),
122
150
  requireTwoPricesPT: z.boolean().optional().describe("Profit target requires two prices"),
123
- closeOnCompletion: z.boolean().optional().describe("Close entire position when any leg hits target"),
151
+ closeOnCompletion: z
152
+ .boolean()
153
+ .optional()
154
+ .describe("Close entire position when any leg hits target"),
124
155
  ignoreMarginReq: z.boolean().optional().describe("Strategy ignores standard margin requirements"),
125
156
  });
126
157
 
@@ -130,7 +161,10 @@ export const getStrategyProfileSchema = z.object({
130
161
  });
131
162
 
132
163
  export const listProfilesSchema = z.object({
133
- blockId: z.string().optional().describe("Optional block ID filter. Omit to list all profiles across all blocks."),
164
+ blockId: z
165
+ .string()
166
+ .optional()
167
+ .describe("Optional block ID filter. Omit to list all profiles across all blocks."),
134
168
  });
135
169
 
136
170
  export const deleteProfileSchema = z.object({
@@ -147,35 +181,38 @@ export const deleteProfileSchema = z.object({
147
181
  */
148
182
  export async function handleProfileStrategy(
149
183
  input: z.infer<typeof profileStrategySchema>,
150
- baseDir: string
184
+ baseDir: string,
151
185
  ): Promise<ReturnType<typeof createToolOutput>> {
152
186
  await upgradeToReadWrite(baseDir);
153
187
  try {
154
188
  const conn = await getConnection(baseDir);
155
- const stored = await upsertProfile(conn, {
156
- blockId: input.blockId,
157
- strategyName: input.strategyName,
158
- structureType: input.structureType,
159
- greeksBias: input.greeksBias,
160
- thesis: input.thesis,
161
- legs: input.legs,
162
- entryFilters: input.entryFilters,
163
- exitRules: input.exitRules,
164
- expectedRegimes: input.expectedRegimes,
165
- keyMetrics: input.keyMetrics,
166
- positionSizing: input.positionSizing,
167
- underlying: input.underlying,
168
- reEntry: input.reEntry,
169
- capProfits: input.capProfits,
170
- capLosses: input.capLosses,
171
- requireTwoPricesPT: input.requireTwoPricesPT,
172
- closeOnCompletion: input.closeOnCompletion,
173
- ignoreMarginReq: input.ignoreMarginReq,
174
- }, baseDir);
175
- return createToolOutput(
176
- `Profile saved: ${input.strategyName} for block ${input.blockId}`,
177
- { profile: stored }
189
+ const stored = await upsertProfile(
190
+ conn,
191
+ {
192
+ blockId: input.blockId,
193
+ strategyName: input.strategyName,
194
+ structureType: input.structureType,
195
+ greeksBias: input.greeksBias,
196
+ thesis: input.thesis,
197
+ legs: input.legs,
198
+ entryFilters: input.entryFilters,
199
+ exitRules: input.exitRules,
200
+ expectedRegimes: input.expectedRegimes,
201
+ keyMetrics: input.keyMetrics,
202
+ positionSizing: input.positionSizing,
203
+ underlying: input.underlying,
204
+ reEntry: input.reEntry,
205
+ capProfits: input.capProfits,
206
+ capLosses: input.capLosses,
207
+ requireTwoPricesPT: input.requireTwoPricesPT,
208
+ closeOnCompletion: input.closeOnCompletion,
209
+ ignoreMarginReq: input.ignoreMarginReq,
210
+ },
211
+ baseDir,
178
212
  );
213
+ return createToolOutput(`Profile saved: ${input.strategyName} for block ${input.blockId}`, {
214
+ profile: stored,
215
+ });
179
216
  } finally {
180
217
  await downgradeToReadOnly(baseDir);
181
218
  }
@@ -186,20 +223,17 @@ export async function handleProfileStrategy(
186
223
  */
187
224
  export async function handleGetStrategyProfile(
188
225
  input: z.infer<typeof getStrategyProfileSchema>,
189
- baseDir: string
226
+ baseDir: string,
190
227
  ): Promise<ReturnType<typeof createToolOutput>> {
191
228
  const conn = await getConnection(baseDir);
192
229
  const profile = await getProfile(conn, input.blockId, input.strategyName, baseDir);
193
230
  if (!profile) {
194
231
  return createToolOutput(
195
232
  `No profile found for strategy '${input.strategyName}' in block '${input.blockId}'`,
196
- { profile: null }
233
+ { profile: null },
197
234
  );
198
235
  }
199
- return createToolOutput(
200
- `Profile: ${input.strategyName} in block ${input.blockId}`,
201
- { profile }
202
- );
236
+ return createToolOutput(`Profile: ${input.strategyName} in block ${input.blockId}`, { profile });
203
237
  }
204
238
 
205
239
  /**
@@ -207,7 +241,7 @@ export async function handleGetStrategyProfile(
207
241
  */
208
242
  export async function handleListProfiles(
209
243
  input: z.infer<typeof listProfilesSchema>,
210
- baseDir: string
244
+ baseDir: string,
211
245
  ): Promise<ReturnType<typeof createToolOutput>> {
212
246
  const conn = await getConnection(baseDir);
213
247
  const profiles = await listProfiles(conn, input.blockId, baseDir);
@@ -222,7 +256,7 @@ export async function handleListProfiles(
222
256
  }));
223
257
  return createToolOutput(
224
258
  `Found ${profiles.length} profile(s)${input.blockId ? ` for block ${input.blockId}` : ""}`,
225
- { count: profiles.length, profiles: summaryRows }
259
+ { count: profiles.length, profiles: summaryRows },
226
260
  );
227
261
  }
228
262
 
@@ -231,7 +265,7 @@ export async function handleListProfiles(
231
265
  */
232
266
  export async function handleDeleteProfile(
233
267
  input: z.infer<typeof deleteProfileSchema>,
234
- baseDir: string
268
+ baseDir: string,
235
269
  ): Promise<ReturnType<typeof createToolOutput>> {
236
270
  await upgradeToReadWrite(baseDir);
237
271
  try {
@@ -240,12 +274,12 @@ export async function handleDeleteProfile(
240
274
  if (deleted) {
241
275
  return createToolOutput(
242
276
  `Deleted profile: ${input.strategyName} from block ${input.blockId}`,
243
- { deleted: true }
277
+ { deleted: true },
244
278
  );
245
279
  }
246
280
  return createToolOutput(
247
281
  `No profile found for strategy '${input.strategyName}' in block '${input.blockId}' — nothing to delete`,
248
- { deleted: false }
282
+ { deleted: false },
249
283
  );
250
284
  } finally {
251
285
  await downgradeToReadOnly(baseDir);
@@ -280,7 +314,7 @@ export function registerProfileTools(server: McpServer, baseDir: string): void {
280
314
  },
281
315
  withSyncedBlock(baseDir, async (input, ctx) => {
282
316
  return handleProfileStrategy(input, ctx.baseDir);
283
- })
317
+ }),
284
318
  );
285
319
 
286
320
  // -------------------------------------------------------------------------
@@ -296,7 +330,7 @@ export function registerProfileTools(server: McpServer, baseDir: string): void {
296
330
  },
297
331
  withSyncedBlock(baseDir, async (input, ctx) => {
298
332
  return handleGetStrategyProfile(input, ctx.baseDir);
299
- })
333
+ }),
300
334
  );
301
335
 
302
336
  // -------------------------------------------------------------------------
@@ -315,13 +349,16 @@ export function registerProfileTools(server: McpServer, baseDir: string): void {
315
349
  // list_profiles has optional blockId — when provided, sync the block first;
316
350
  // when omitted, query directly without sync (no block to validate).
317
351
  if (input.blockId) {
318
- const syncedHandler = withSyncedBlock(baseDir, async (syncInput: { blockId: string }, ctx) => {
319
- return handleListProfiles({ blockId: syncInput.blockId }, ctx.baseDir);
320
- });
352
+ const syncedHandler = withSyncedBlock(
353
+ baseDir,
354
+ async (syncInput: { blockId: string }, ctx) => {
355
+ return handleListProfiles({ blockId: syncInput.blockId }, ctx.baseDir);
356
+ },
357
+ );
321
358
  return syncedHandler({ blockId: input.blockId });
322
359
  }
323
360
  return handleListProfiles(input, baseDir);
324
- }
361
+ },
325
362
  );
326
363
 
327
364
  // -------------------------------------------------------------------------
@@ -337,6 +374,6 @@ export function registerProfileTools(server: McpServer, baseDir: string): void {
337
374
  },
338
375
  withSyncedBlock(baseDir, async (input, ctx) => {
339
376
  return handleDeleteProfile(input, ctx.baseDir);
340
- })
377
+ }),
341
378
  );
342
379
  }
@@ -14,21 +14,10 @@ import type { Trade } from "@tradeblocks/lib";
14
14
  import { getConnection } from "../db/connection.ts";
15
15
  import { listProfiles } from "../db/profile-schemas.ts";
16
16
  import { filterByStrategy } from "./shared/filters.ts";
17
- import {
18
- buildLookaheadFreeQuery,
19
- type MarketLookupKey,
20
- } from "../utils/field-timing.ts";
21
- import {
22
- DEFAULT_MARKET_TICKER,
23
- marketTickerDateKey,
24
- resolveTradeTicker,
25
- } from "../utils/ticker.ts";
17
+ import { buildLookaheadFreeQuery, type MarketLookupKey } from "../utils/field-timing.ts";
18
+ import { DEFAULT_MARKET_TICKER, marketTickerDateKey, resolveTradeTicker } from "../utils/ticker.ts";
26
19
  import { computeSliceStats, type SliceStats } from "../utils/analysis-stats.ts";
27
- import {
28
- upgradeToReadWrite,
29
- downgradeToReadOnly,
30
- getConnectionMode,
31
- } from "../db/connection.ts";
20
+ import { upgradeToReadWrite, downgradeToReadOnly, getConnectionMode } from "../db/connection.ts";
32
21
  import { syncAllBlocks } from "../sync/index.ts";
33
22
 
34
23
  // =============================================================================
@@ -86,7 +75,7 @@ function resultToRecords(result: {
86
75
  }
87
76
 
88
77
  function recordsByTickerDate(
89
- records: Record<string, unknown>[]
78
+ records: Record<string, unknown>[],
90
79
  ): Map<string, Record<string, unknown>> {
91
80
  const mapped = new Map<string, Record<string, unknown>>();
92
81
  for (const record of records) {
@@ -144,16 +133,12 @@ export const regimeAllocationAdvisorSchema = z.object({
144
133
  blockId: z
145
134
  .string()
146
135
  .optional()
147
- .describe(
148
- "Block ID to analyze. When omitted, aggregate across all profiled strategies."
149
- ),
136
+ .describe("Block ID to analyze. When omitted, aggregate across all profiled strategies."),
150
137
  minTrades: z
151
138
  .number()
152
139
  .optional()
153
140
  .default(5)
154
- .describe(
155
- "Minimum trades per regime cell for reliable stats (default: 5)"
156
- ),
141
+ .describe("Minimum trades per regime cell for reliable stats (default: 5)"),
157
142
  });
158
143
 
159
144
  // =============================================================================
@@ -162,7 +147,7 @@ export const regimeAllocationAdvisorSchema = z.object({
162
147
 
163
148
  export async function handleRegimeAllocationAdvisor(
164
149
  input: z.infer<typeof regimeAllocationAdvisorSchema>,
165
- baseDir: string
150
+ baseDir: string,
166
151
  ): Promise<ReturnType<typeof createToolOutput>> {
167
152
  const minTrades = input.minTrades ?? 5;
168
153
  const warnings: string[] = [];
@@ -177,7 +162,7 @@ export async function handleRegimeAllocationAdvisor(
177
162
  input.blockId
178
163
  ? `No strategy profiles found for block '${input.blockId}'. Use profile_strategy to create profiles first.`
179
164
  : "No strategy profiles found. Use profile_strategy to create profiles first.",
180
- { error: "no_profiles" }
165
+ { error: "no_profiles" },
181
166
  );
182
167
  }
183
168
 
@@ -206,7 +191,7 @@ export async function handleRegimeAllocationAdvisor(
206
191
  if (!profile.expectedRegimes || profile.expectedRegimes.length === 0) {
207
192
  skippedNoRegimes++;
208
193
  profileUpgradeHints.push(
209
- `Strategy '${profile.strategyName}' (block: ${profile.blockId}) has no expectedRegimes. Add via profile_strategy.`
194
+ `Strategy '${profile.strategyName}' (block: ${profile.blockId}) has no expectedRegimes. Add via profile_strategy.`,
210
195
  );
211
196
  continue;
212
197
  }
@@ -217,7 +202,7 @@ export async function handleRegimeAllocationAdvisor(
217
202
  block = await loadBlock(baseDir, profile.blockId);
218
203
  } catch {
219
204
  warnings.push(
220
- `Could not load block '${profile.blockId}' for strategy '${profile.strategyName}'. Skipped.`
205
+ `Could not load block '${profile.blockId}' for strategy '${profile.strategyName}'. Skipped.`,
221
206
  );
222
207
  continue;
223
208
  }
@@ -233,7 +218,7 @@ export async function handleRegimeAllocationAdvisor(
233
218
 
234
219
  if (trades.length === 0) {
235
220
  warnings.push(
236
- `No trades found for strategy '${profile.strategyName}' in block '${profile.blockId}'. Skipped.`
221
+ `No trades found for strategy '${profile.strategyName}' in block '${profile.blockId}'. Skipped.`,
237
222
  );
238
223
  continue;
239
224
  }
@@ -267,14 +252,14 @@ export async function handleRegimeAllocationAdvisor(
267
252
  if (matched.length === 0) {
268
253
  skippedNoMarket++;
269
254
  warnings.push(
270
- `No market data matched for strategy '${profile.strategyName}' (${trades.length} trades). Import and enrich market data first.`
255
+ `No market data matched for strategy '${profile.strategyName}' (${trades.length} trades). Import and enrich market data first.`,
271
256
  );
272
257
  continue;
273
258
  }
274
259
 
275
260
  if (unmatchedCount > 0) {
276
261
  warnings.push(
277
- `Strategy '${profile.strategyName}': ${unmatchedCount} of ${trades.length} trades had no market data match.`
262
+ `Strategy '${profile.strategyName}': ${unmatchedCount} of ${trades.length} trades had no market data match.`,
278
263
  );
279
264
  }
280
265
 
@@ -297,9 +282,7 @@ export async function handleRegimeAllocationAdvisor(
297
282
  }
298
283
 
299
284
  // Build per-regime comparison
300
- const expectedSet = new Set(
301
- profile.expectedRegimes.map((r) => r.toLowerCase())
302
- );
285
+ const expectedSet = new Set(profile.expectedRegimes.map((r) => r.toLowerCase()));
303
286
  const regimePerformance: Record<string, RegimeCell> = {};
304
287
 
305
288
  for (const [label, pls] of Object.entries(regimePls)) {
@@ -345,8 +328,7 @@ export async function handleRegimeAllocationAdvisor(
345
328
 
346
329
  // Allocation from position sizing
347
330
  const allocationPct =
348
- profile.positionSizing?.liveAllocationPct ??
349
- profile.positionSizing?.allocationPct;
331
+ profile.positionSizing?.liveAllocationPct ?? profile.positionSizing?.allocationPct;
350
332
 
351
333
  strategies.push({
352
334
  strategyName: profile.strategyName,
@@ -362,7 +344,7 @@ export async function handleRegimeAllocationAdvisor(
362
344
  });
363
345
  } catch (err) {
364
346
  warnings.push(
365
- `Error processing strategy '${profile.strategyName}' (block: ${profile.blockId}): ${(err as Error).message}`
347
+ `Error processing strategy '${profile.strategyName}' (block: ${profile.blockId}): ${(err as Error).message}`,
366
348
  );
367
349
  }
368
350
  }
@@ -400,12 +382,8 @@ export async function handleRegimeAllocationAdvisor(
400
382
  const summaryText =
401
383
  `Regime allocation advisor: ${strategies.length}/${profiles.length} strategies analyzed. ` +
402
384
  `${allThesisViolations.length} thesis violation(s), ${allHiddenEdges.length} hidden edge(s). ` +
403
- (skippedNoRegimes > 0
404
- ? `${skippedNoRegimes} skipped (no expectedRegimes). `
405
- : "") +
406
- (skippedNoMarket > 0
407
- ? `${skippedNoMarket} skipped (no market data). `
408
- : "");
385
+ (skippedNoRegimes > 0 ? `${skippedNoRegimes} skipped (no expectedRegimes). ` : "") +
386
+ (skippedNoMarket > 0 ? `${skippedNoMarket} skipped (no market data). ` : "");
409
387
 
410
388
  return createToolOutput(summaryText, {
411
389
  strategies,
@@ -420,10 +398,7 @@ export async function handleRegimeAllocationAdvisor(
420
398
  // Registration
421
399
  // =============================================================================
422
400
 
423
- export function registerRegimeAdvisorTools(
424
- server: McpServer,
425
- baseDir: string
426
- ): void {
401
+ export function registerRegimeAdvisorTools(server: McpServer, baseDir: string): void {
427
402
  server.registerTool(
428
403
  "regime_allocation_advisor",
429
404
  {
@@ -450,6 +425,6 @@ export function registerRegimeAdvisorTools(
450
425
  }
451
426
  }
452
427
  return handleRegimeAllocationAdvisor(input, baseDir);
453
- }
428
+ },
454
429
  );
455
430
  }