tradeblocks-mcp 3.0.2 → 3.3.1
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.
- package/README.md +85 -80
- package/dist/{chunk-LDKTV7GW.js → chunk-27S67XW3.js} +24 -52
- package/dist/chunk-27S67XW3.js.map +1 -0
- package/dist/{chunk-FGZH632F.js → chunk-2E63THNI.js} +13 -11
- package/dist/chunk-2E63THNI.js.map +1 -0
- package/dist/{chunk-QTTR7AAW.js → chunk-2I3S2ZLT.js} +325 -84
- package/dist/chunk-2I3S2ZLT.js.map +1 -0
- package/dist/{chunk-4BLCXNQ6.js → chunk-4HZLVUOZ.js} +179 -183
- package/dist/chunk-4HZLVUOZ.js.map +1 -0
- package/dist/{chunk-XXYOUIZY.js → chunk-NWNSKALN.js} +63 -80
- package/dist/chunk-NWNSKALN.js.map +1 -0
- package/dist/{chunk-PRAYH3RT.js → chunk-WNTYA7ER.js} +29 -12
- package/dist/chunk-WNTYA7ER.js.map +1 -0
- package/dist/{chunk-BKQ4PM4Y.js → chunk-YZA4RS76.js} +24 -18
- package/dist/chunk-YZA4RS76.js.map +1 -0
- package/dist/{chunk-W2PP3LEH.js → chunk-ZTJXC3LS.js} +17 -8
- package/dist/chunk-ZTJXC3LS.js.map +1 -0
- package/dist/daily-log-processor-MZW2XBZY.js +9 -0
- package/dist/{daily-logs-store-D3QC2XPV.js → daily-logs-store-3C3OTGPK.js} +2 -2
- package/dist/iv-solver-worker.js +9 -1
- package/dist/market-provider-6U33TQUT.js +16 -0
- package/dist/{proto-3HZTCWK4.js → proto-V23GIZG2.js} +2 -2
- package/dist/{sync-PO4IPCYV.js → sync-3UBC37VW.js} +7 -7
- package/dist/test-exports.js +876 -483
- package/dist/test-exports.js.map +1 -1
- package/dist/trade-processor-DKDCWGKD.js +9 -0
- package/dist/{trades-store-LOL3FQSF.js → trades-store-R3NL25T2.js} +2 -2
- package/package.json +2 -6
- package/server/{chunk-CSDVJPBB.js → chunk-536NSFED.js} +29 -12
- package/server/chunk-536NSFED.js.map +1 -0
- package/server/{chunk-WA5AAPCH.js → chunk-A3UKD64A.js} +17 -8
- package/server/chunk-A3UKD64A.js.map +1 -0
- package/server/{chunk-PNKG7RY7.js → chunk-OMUDNJBJ.js} +63 -80
- package/server/chunk-OMUDNJBJ.js.map +1 -0
- package/server/{chunk-72GKJE2U.js → chunk-RJDJHZ5Y.js} +934 -335
- package/server/chunk-RJDJHZ5Y.js.map +1 -0
- package/server/{chunk-NZO6PT64.js → chunk-RM4B2VKS.js} +167 -177
- package/server/chunk-RM4B2VKS.js.map +1 -0
- package/server/{chunk-5EBXHT6C.js → chunk-SY6GDNVI.js} +24 -18
- package/server/chunk-SY6GDNVI.js.map +1 -0
- package/server/{chunk-FBNDMCT5.js → chunk-T66KH2XH.js} +13 -11
- package/server/chunk-T66KH2XH.js.map +1 -0
- package/server/{chunk-NRFXAJF7.js → chunk-W5E7FHC4.js} +24 -52
- package/server/chunk-W5E7FHC4.js.map +1 -0
- package/server/{config-6IZXEFEX.js → config-DK7KOMNL.js} +1 -1
- package/server/config-DK7KOMNL.js.map +1 -0
- package/server/daily-log-processor-B2VPU2FQ.js +10 -0
- package/server/{daily-logs-store-NU7M4L5C.js → daily-logs-store-YFNXTB7J.js} +2 -2
- package/server/http-server.js +2 -5
- package/server/index.js +2497 -2029
- package/server/index.js.map +1 -1
- package/server/iv-solver-worker.js +9 -1
- package/server/market-provider-L56RFHAC.js +17 -0
- package/server/{proto-DWRZJO4E.js → proto-YZ4TLX2N.js} +2 -2
- package/server/{sync-QFI5L7S7.js → sync-CENKDN53.js} +7 -7
- package/server/trade-processor-3PI4LHZI.js +10 -0
- package/server/{trades-store-VY26MWER.js → trades-store-RTTEYMXH.js} +2 -2
- package/src/auth/clients-store.ts +4 -4
- package/src/auth/code-store.ts +1 -1
- package/src/auth/config.ts +11 -12
- package/src/auth/login-page.ts +8 -10
- package/src/auth/provider.ts +35 -30
- package/src/auth/token.ts +17 -15
- package/src/db/backtest-schemas.ts +38 -8
- package/src/db/connection.ts +107 -40
- package/src/db/data-root.ts +22 -0
- package/src/db/index.ts +37 -5
- package/src/db/json-adapters.ts +22 -31
- package/src/db/json-migration.ts +11 -15
- package/src/db/json-store.ts +5 -3
- package/src/db/market-datasets.ts +48 -15
- package/src/db/market-schemas.ts +11 -5
- package/src/db/market-views.ts +119 -32
- package/src/db/parquet-writer.ts +7 -7
- package/src/db/profile-schemas.ts +39 -22
- package/src/db/schemas.ts +2 -2
- package/src/http-server.ts +7 -12
- package/src/index.ts +8 -9
- package/src/market/ingestor/index.ts +1 -4
- package/src/market/ingestor/market-ingestor.ts +140 -67
- package/src/market/ingestor/types.ts +1 -3
- package/src/market/stores/chain-sql.ts +2 -8
- package/src/market/stores/chain-store.ts +1 -4
- package/src/market/stores/duckdb-chain-store.ts +3 -14
- package/src/market/stores/duckdb-enriched-store.ts +3 -13
- package/src/market/stores/duckdb-quote-store.ts +10 -27
- package/src/market/stores/duckdb-spot-store.ts +4 -20
- package/src/market/stores/enriched-sql.ts +3 -9
- package/src/market/stores/enriched-store.ts +2 -2
- package/src/market/stores/parquet-chain-store.ts +5 -22
- package/src/market/stores/parquet-enriched-store.ts +4 -16
- package/src/market/stores/parquet-oi-daily-store.ts +3 -14
- package/src/market/stores/parquet-quote-store.ts +9 -29
- package/src/market/stores/parquet-spot-store.ts +14 -33
- package/src/market/stores/quote-store.ts +2 -10
- package/src/market/stores/spot-sql.ts +3 -15
- package/src/market/stores/spot-store.ts +1 -1
- package/src/market/tickers/defaults.json +6 -6
- package/src/market/tickers/loader.ts +2 -7
- package/src/market/tickers/registry.ts +5 -15
- package/src/market/tickers/schemas.ts +2 -6
- package/src/models/strategy-profile.ts +39 -39
- package/src/sync/block-sync.ts +44 -65
- package/src/sync/index.ts +1 -4
- package/src/sync/metadata.ts +14 -20
- package/src/test-exports.ts +139 -123
- package/src/tools/analysis.ts +180 -159
- package/src/tools/batch-exit-analysis.ts +100 -59
- package/src/tools/blocks/analysis.ts +116 -101
- package/src/tools/blocks/comparison.ts +281 -364
- package/src/tools/blocks/core.ts +208 -253
- package/src/tools/blocks/health.ts +571 -563
- package/src/tools/blocks/index.ts +2 -0
- package/src/tools/blocks/paired-comparison.ts +425 -0
- package/src/tools/blocks/similarity.ts +316 -283
- package/src/tools/edge-decay.ts +124 -197
- package/src/tools/exit-analysis.ts +130 -77
- package/src/tools/greeks-attribution.ts +84 -35
- package/src/tools/guides.ts +4 -6
- package/src/tools/imports.ts +11 -14
- package/src/tools/market-data.ts +1067 -757
- package/src/tools/market-enrichment.ts +3 -3
- package/src/tools/market-fetch.ts +148 -67
- package/src/tools/market-imports.ts +12 -12
- package/src/tools/middleware/sync-middleware.ts +5 -6
- package/src/tools/performance.ts +185 -302
- package/src/tools/profile-analysis.ts +52 -66
- package/src/tools/profiles.ts +106 -69
- package/src/tools/regime-advisor.ts +20 -45
- package/src/tools/replay.ts +81 -77
- package/src/tools/reports/discrepancies.ts +298 -328
- package/src/tools/reports/fields.ts +7 -25
- package/src/tools/reports/helpers.ts +18 -49
- package/src/tools/reports/predictive.ts +27 -70
- package/src/tools/reports/slippage-trends.ts +315 -345
- package/src/tools/reports/slippage.ts +1 -4
- package/src/tools/reports/strategy-matches.ts +399 -441
- package/src/tools/schema.ts +43 -40
- package/src/tools/shared/filters.ts +3 -9
- package/src/tools/snapshot.ts +9 -30
- package/src/tools/sql.ts +15 -14
- package/src/tools/tickers.ts +1 -4
- package/src/utils/batch-exit-analysis.ts +31 -42
- package/src/utils/black-scholes.ts +39 -29
- package/src/utils/block-loader.ts +69 -83
- package/src/utils/calibration-probe.ts +3 -4
- package/src/utils/chain-loader.ts +3 -3
- package/src/utils/csv-discovery.ts +16 -22
- package/src/utils/data-quality.ts +24 -36
- package/src/utils/exit-triggers.ts +91 -96
- package/src/utils/field-timing.ts +94 -79
- package/src/utils/filter-predicates.ts +13 -9
- package/src/utils/flatfile-importer.ts +94 -64
- package/src/utils/greeks-decomposition.ts +152 -100
- package/src/utils/iv-solver-pool.ts +55 -25
- package/src/utils/iv-solver-worker.ts +5 -5
- package/src/utils/market-enricher.ts +528 -497
- package/src/utils/market-importer.ts +31 -12
- package/src/utils/market-provider.ts +21 -23
- package/src/utils/massive-tier.ts +5 -7
- package/src/utils/migrate-option-data-helpers.ts +2 -8
- package/src/utils/option-quote-greeks.ts +25 -31
- package/src/utils/option-time.ts +4 -8
- package/src/utils/output-formatter.ts +1 -4
- package/src/utils/provider-capabilities.ts +1 -4
- package/src/utils/providers/massive.ts +59 -93
- package/src/utils/providers/thetadata/backfill.ts +14 -23
- package/src/utils/providers/thetadata/client.ts +12 -8
- package/src/utils/providers/thetadata/decode.ts +2 -20
- package/src/utils/providers/thetadata/endpoints.ts +32 -40
- package/src/utils/providers/thetadata/join.ts +11 -10
- package/src/utils/providers/thetadata/proto.ts +12 -10
- package/src/utils/providers/thetadata/quote-mid-greeks.ts +5 -5
- package/src/utils/providers/thetadata.ts +11 -10
- package/src/utils/quote-enricher.ts +4 -4
- package/src/utils/quote-parquet-projection.ts +3 -11
- package/src/utils/sample-date-selector.ts +3 -5
- package/src/utils/schema-metadata.ts +102 -70
- package/src/utils/ticker.ts +5 -9
- package/src/utils/trade-replay.ts +77 -68
- package/dist/chunk-4BLCXNQ6.js.map +0 -1
- package/dist/chunk-BKQ4PM4Y.js.map +0 -1
- package/dist/chunk-FGZH632F.js.map +0 -1
- package/dist/chunk-LDKTV7GW.js.map +0 -1
- package/dist/chunk-PRAYH3RT.js.map +0 -1
- package/dist/chunk-QTTR7AAW.js.map +0 -1
- package/dist/chunk-W2PP3LEH.js.map +0 -1
- package/dist/chunk-XXYOUIZY.js.map +0 -1
- package/dist/daily-log-processor-6MWJ23JK.js +0 -9
- package/dist/market-provider-VDRJUEF2.js +0 -16
- package/dist/trade-processor-NHU4VWRX.js +0 -9
- package/manifest.json +0 -141
- package/server/chunk-5EBXHT6C.js.map +0 -1
- package/server/chunk-72GKJE2U.js.map +0 -1
- package/server/chunk-CSDVJPBB.js.map +0 -1
- package/server/chunk-FBNDMCT5.js.map +0 -1
- package/server/chunk-NRFXAJF7.js.map +0 -1
- package/server/chunk-NZO6PT64.js.map +0 -1
- package/server/chunk-PNKG7RY7.js.map +0 -1
- package/server/chunk-WA5AAPCH.js.map +0 -1
- package/server/config-6IZXEFEX.js.map +0 -1
- package/server/daily-log-processor-Y3PVSVBM.js +0 -10
- package/server/market-provider-VOYYVYWT.js +0 -17
- package/server/trade-processor-JWVS37KM.js +0 -10
- /package/dist/{daily-log-processor-6MWJ23JK.js.map → daily-log-processor-MZW2XBZY.js.map} +0 -0
- /package/dist/{daily-logs-store-D3QC2XPV.js.map → daily-logs-store-3C3OTGPK.js.map} +0 -0
- /package/dist/{market-provider-VDRJUEF2.js.map → market-provider-6U33TQUT.js.map} +0 -0
- /package/dist/{proto-3HZTCWK4.js.map → proto-V23GIZG2.js.map} +0 -0
- /package/dist/{sync-PO4IPCYV.js.map → sync-3UBC37VW.js.map} +0 -0
- /package/dist/{trade-processor-NHU4VWRX.js.map → trade-processor-DKDCWGKD.js.map} +0 -0
- /package/dist/{trades-store-LOL3FQSF.js.map → trades-store-R3NL25T2.js.map} +0 -0
- /package/server/{daily-log-processor-Y3PVSVBM.js.map → daily-log-processor-B2VPU2FQ.js.map} +0 -0
- /package/server/{daily-logs-store-NU7M4L5C.js.map → daily-logs-store-YFNXTB7J.js.map} +0 -0
- /package/server/{market-provider-VOYYVYWT.js.map → market-provider-L56RFHAC.js.map} +0 -0
- /package/server/{proto-DWRZJO4E.js.map → proto-YZ4TLX2N.js.map} +0 -0
- /package/server/{sync-QFI5L7S7.js.map → sync-CENKDN53.js.map} +0 -0
- /package/server/{trade-processor-JWVS37KM.js.map → trade-processor-3PI4LHZI.js.map} +0 -0
- /package/server/{trades-store-VY26MWER.js.map → trades-store-RTTEYMXH.js.map} +0 -0
|
@@ -15,23 +15,12 @@ import type { Trade } from "@tradeblocks/lib";
|
|
|
15
15
|
import { getConnection } from "../db/connection.ts";
|
|
16
16
|
import { getProfile, listProfiles } from "../db/profile-schemas.ts";
|
|
17
17
|
import { filterByStrategy } from "./shared/filters.ts";
|
|
18
|
-
import {
|
|
19
|
-
|
|
20
|
-
type MarketLookupKey,
|
|
21
|
-
} from "../utils/field-timing.ts";
|
|
22
|
-
import {
|
|
23
|
-
DEFAULT_MARKET_TICKER,
|
|
24
|
-
marketTickerDateKey,
|
|
25
|
-
resolveTradeTicker,
|
|
26
|
-
} from "../utils/ticker.ts";
|
|
18
|
+
import { buildLookaheadFreeQuery, type MarketLookupKey } from "../utils/field-timing.ts";
|
|
19
|
+
import { DEFAULT_MARKET_TICKER, marketTickerDateKey, resolveTradeTicker } from "../utils/ticker.ts";
|
|
27
20
|
import { computeSliceStats, type SliceStats } from "../utils/analysis-stats.ts";
|
|
28
21
|
import { buildFilterPredicate, type FilterPredicate } from "../utils/filter-predicates.ts";
|
|
29
22
|
import { withSyncedBlock } from "./middleware/sync-middleware.ts";
|
|
30
|
-
import {
|
|
31
|
-
upgradeToReadWrite,
|
|
32
|
-
downgradeToReadOnly,
|
|
33
|
-
getConnectionMode,
|
|
34
|
-
} from "../db/connection.ts";
|
|
23
|
+
import { upgradeToReadWrite, downgradeToReadOnly, getConnectionMode } from "../db/connection.ts";
|
|
35
24
|
import { syncAllBlocks } from "../sync/index.ts";
|
|
36
25
|
|
|
37
26
|
// =============================================================================
|
|
@@ -92,7 +81,7 @@ function resultToRecords(result: {
|
|
|
92
81
|
}
|
|
93
82
|
|
|
94
83
|
function recordsByTickerDate(
|
|
95
|
-
records: Record<string, unknown>[]
|
|
84
|
+
records: Record<string, unknown>[],
|
|
96
85
|
): Map<string, Record<string, unknown>> {
|
|
97
86
|
const mapped = new Map<string, Record<string, unknown>>();
|
|
98
87
|
for (const record of records) {
|
|
@@ -150,8 +139,8 @@ function getTimeBucket(timeOpened: string | undefined): string | null {
|
|
|
150
139
|
|
|
151
140
|
// morning: 09:30-11:00, midday: 11:00-14:00, afternoon: 14:00-16:00
|
|
152
141
|
if (totalMinutes < 570) return null; // before 09:30
|
|
153
|
-
if (totalMinutes < 660) return "morning";
|
|
154
|
-
if (totalMinutes < 840) return "midday";
|
|
142
|
+
if (totalMinutes < 660) return "morning"; // 09:30-11:00
|
|
143
|
+
if (totalMinutes < 840) return "midday"; // 11:00-14:00
|
|
155
144
|
if (totalMinutes <= 960) return "afternoon"; // 14:00-16:00
|
|
156
145
|
return null; // after 16:00
|
|
157
146
|
}
|
|
@@ -175,7 +164,7 @@ interface TradeWithMarket {
|
|
|
175
164
|
async function loadTradesAndMarket(
|
|
176
165
|
baseDir: string,
|
|
177
166
|
blockId: string,
|
|
178
|
-
strategyName: string
|
|
167
|
+
strategyName: string,
|
|
179
168
|
): Promise<{
|
|
180
169
|
matched: TradeWithMarket[];
|
|
181
170
|
unmatchedCount: number;
|
|
@@ -267,7 +256,7 @@ function createNumericBuckets(values: number[]): { label: string; min: number; m
|
|
|
267
256
|
*/
|
|
268
257
|
function findBucket(
|
|
269
258
|
value: number,
|
|
270
|
-
buckets: { label: string; min: number; max: number }[]
|
|
259
|
+
buckets: { label: string; min: number; max: number }[],
|
|
271
260
|
): string | null {
|
|
272
261
|
for (const bucket of buckets) {
|
|
273
262
|
if (value >= bucket.min && value <= bucket.max) return bucket.label;
|
|
@@ -291,7 +280,7 @@ export const analyzeStructureFitSchema = z.object({
|
|
|
291
280
|
|
|
292
281
|
export async function handleAnalyzeStructureFit(
|
|
293
282
|
input: z.infer<typeof analyzeStructureFitSchema>,
|
|
294
|
-
baseDir: string
|
|
283
|
+
baseDir: string,
|
|
295
284
|
): Promise<ReturnType<typeof createToolOutput>> {
|
|
296
285
|
const { blockId, strategyName } = input;
|
|
297
286
|
const minTrades = input.minTrades ?? 10;
|
|
@@ -302,7 +291,7 @@ export async function handleAnalyzeStructureFit(
|
|
|
302
291
|
if (!profile) {
|
|
303
292
|
return createToolOutput(
|
|
304
293
|
`No profile found for strategy '${strategyName}' in block '${blockId}'. Create one with profile_strategy first.`,
|
|
305
|
-
{ error: "profile_not_found" }
|
|
294
|
+
{ error: "profile_not_found" },
|
|
306
295
|
);
|
|
307
296
|
}
|
|
308
297
|
|
|
@@ -310,7 +299,7 @@ export async function handleAnalyzeStructureFit(
|
|
|
310
299
|
const { matched, unmatchedCount, allTrades } = await loadTradesAndMarket(
|
|
311
300
|
baseDir,
|
|
312
301
|
blockId,
|
|
313
|
-
strategyName
|
|
302
|
+
strategyName,
|
|
314
303
|
);
|
|
315
304
|
|
|
316
305
|
const warnings: string[] = [];
|
|
@@ -318,20 +307,20 @@ export async function handleAnalyzeStructureFit(
|
|
|
318
307
|
if (allTrades.length === 0) {
|
|
319
308
|
return createToolOutput(
|
|
320
309
|
`No trades found for strategy '${strategyName}' in block '${blockId}'.`,
|
|
321
|
-
{ error: "no_trades" }
|
|
310
|
+
{ error: "no_trades" },
|
|
322
311
|
);
|
|
323
312
|
}
|
|
324
313
|
|
|
325
314
|
if (unmatchedCount > 0) {
|
|
326
315
|
warnings.push(
|
|
327
|
-
`${unmatchedCount} of ${allTrades.length} trades had no matching market data and were excluded from market-based analysis
|
|
316
|
+
`${unmatchedCount} of ${allTrades.length} trades had no matching market data and were excluded from market-based analysis.`,
|
|
328
317
|
);
|
|
329
318
|
}
|
|
330
319
|
|
|
331
320
|
if (matched.length === 0) {
|
|
332
321
|
return createToolOutput(
|
|
333
322
|
`No trades could be matched to market data for strategy '${strategyName}'.`,
|
|
334
|
-
{ error: "no_market_match", warnings }
|
|
323
|
+
{ error: "no_market_match", warnings },
|
|
335
324
|
);
|
|
336
325
|
}
|
|
337
326
|
|
|
@@ -427,7 +416,7 @@ export async function handleAnalyzeStructureFit(
|
|
|
427
416
|
for (const [bucketLabel, stats] of Object.entries(bucketStats)) {
|
|
428
417
|
if (stats.tradeCount > 0 && stats.tradeCount < minTrades) {
|
|
429
418
|
warnings.push(
|
|
430
|
-
`${dimName}/${bucketLabel}: only ${stats.tradeCount} trades (< ${minTrades} threshold)
|
|
419
|
+
`${dimName}/${bucketLabel}: only ${stats.tradeCount} trades (< ${minTrades} threshold)`,
|
|
431
420
|
);
|
|
432
421
|
}
|
|
433
422
|
}
|
|
@@ -525,7 +514,7 @@ export const validateEntryFiltersSchema = z.object({
|
|
|
525
514
|
|
|
526
515
|
export async function handleValidateEntryFilters(
|
|
527
516
|
input: z.infer<typeof validateEntryFiltersSchema>,
|
|
528
|
-
baseDir: string
|
|
517
|
+
baseDir: string,
|
|
529
518
|
): Promise<ReturnType<typeof createToolOutput>> {
|
|
530
519
|
const { blockId, strategyName } = input;
|
|
531
520
|
const minTrades = input.minTrades ?? 10;
|
|
@@ -537,7 +526,7 @@ export async function handleValidateEntryFilters(
|
|
|
537
526
|
if (!profile) {
|
|
538
527
|
return createToolOutput(
|
|
539
528
|
`No profile found for strategy '${strategyName}' in block '${blockId}'. Create one with profile_strategy first.`,
|
|
540
|
-
{ error: "profile_not_found" }
|
|
529
|
+
{ error: "profile_not_found" },
|
|
541
530
|
);
|
|
542
531
|
}
|
|
543
532
|
|
|
@@ -545,7 +534,7 @@ export async function handleValidateEntryFilters(
|
|
|
545
534
|
if (!profile.entryFilters || profile.entryFilters.length === 0) {
|
|
546
535
|
return createToolOutput(
|
|
547
536
|
`Profile '${strategyName}' has no entry_filters defined. Add filters via profile_strategy to enable validation.`,
|
|
548
|
-
{ no_filters: true }
|
|
537
|
+
{ no_filters: true },
|
|
549
538
|
);
|
|
550
539
|
}
|
|
551
540
|
|
|
@@ -557,7 +546,7 @@ export async function handleValidateEntryFilters(
|
|
|
557
546
|
if (marketFilters.length === 0) {
|
|
558
547
|
return createToolOutput(
|
|
559
548
|
`Profile '${strategyName}' has ${allFilters.length} filter(s) but all are tagged source:'execution' (platform-level). No market-data filters to validate.`,
|
|
560
|
-
{ no_market_filters: true, execution_filters: executionFilters }
|
|
549
|
+
{ no_market_filters: true, execution_filters: executionFilters },
|
|
561
550
|
);
|
|
562
551
|
}
|
|
563
552
|
|
|
@@ -565,34 +554,34 @@ export async function handleValidateEntryFilters(
|
|
|
565
554
|
const { matched, unmatchedCount, allTrades } = await loadTradesAndMarket(
|
|
566
555
|
baseDir,
|
|
567
556
|
blockId,
|
|
568
|
-
strategyName
|
|
557
|
+
strategyName,
|
|
569
558
|
);
|
|
570
559
|
|
|
571
560
|
const warnings: string[] = [];
|
|
572
561
|
|
|
573
562
|
if (executionFilters.length > 0) {
|
|
574
563
|
warnings.push(
|
|
575
|
-
`${executionFilters.length} execution-level filter(s) skipped (not testable against market data): ${executionFilters.map((f) => f.description || f.field).join(", ")}
|
|
564
|
+
`${executionFilters.length} execution-level filter(s) skipped (not testable against market data): ${executionFilters.map((f) => f.description || f.field).join(", ")}`,
|
|
576
565
|
);
|
|
577
566
|
}
|
|
578
567
|
|
|
579
568
|
if (allTrades.length === 0) {
|
|
580
569
|
return createToolOutput(
|
|
581
570
|
`No trades found for strategy '${strategyName}' in block '${blockId}'.`,
|
|
582
|
-
{ error: "no_trades" }
|
|
571
|
+
{ error: "no_trades" },
|
|
583
572
|
);
|
|
584
573
|
}
|
|
585
574
|
|
|
586
575
|
if (unmatchedCount > 0) {
|
|
587
576
|
warnings.push(
|
|
588
|
-
`${unmatchedCount} of ${allTrades.length} trades had no matching market data and were excluded
|
|
577
|
+
`${unmatchedCount} of ${allTrades.length} trades had no matching market data and were excluded.`,
|
|
589
578
|
);
|
|
590
579
|
}
|
|
591
580
|
|
|
592
581
|
if (matched.length === 0) {
|
|
593
582
|
return createToolOutput(
|
|
594
583
|
`No trades could be matched to market data for strategy '${strategyName}'.`,
|
|
595
|
-
{ error: "no_market_match", warnings }
|
|
584
|
+
{ error: "no_market_match", warnings },
|
|
596
585
|
);
|
|
597
586
|
}
|
|
598
587
|
|
|
@@ -740,10 +729,7 @@ export async function handleValidateEntryFilters(
|
|
|
740
729
|
|
|
741
730
|
// Check per-filter: if entered performs worse than filtered_out, suggest removal
|
|
742
731
|
for (const [filterDesc, { entered, filtered_out }] of Object.entries(perFilter)) {
|
|
743
|
-
if (
|
|
744
|
-
entered.tradeCount >= minTrades &&
|
|
745
|
-
filtered_out.tradeCount >= minTrades
|
|
746
|
-
) {
|
|
732
|
+
if (entered.tradeCount >= minTrades && filtered_out.tradeCount >= minTrades) {
|
|
747
733
|
if (entered.avgPl < filtered_out.avgPl && filtered_out.avgPl > 0) {
|
|
748
734
|
profileUpdateHints.push({
|
|
749
735
|
field: filterDesc,
|
|
@@ -770,24 +756,25 @@ export async function handleValidateEntryFilters(
|
|
|
770
756
|
// Thin-data warnings
|
|
771
757
|
if (baseline.tradeCount > 0 && baseline.tradeCount < minTrades) {
|
|
772
758
|
warnings.push(
|
|
773
|
-
`Baseline (all filters): only ${baseline.tradeCount} trades (< ${minTrades} threshold)
|
|
759
|
+
`Baseline (all filters): only ${baseline.tradeCount} trades (< ${minTrades} threshold)`,
|
|
774
760
|
);
|
|
775
761
|
}
|
|
776
762
|
for (const [filterDesc, { entered, filtered_out }] of Object.entries(perFilter)) {
|
|
777
763
|
if (entered.tradeCount > 0 && entered.tradeCount < minTrades) {
|
|
778
764
|
warnings.push(
|
|
779
|
-
`${filterDesc} entered: only ${entered.tradeCount} trades (< ${minTrades} threshold)
|
|
765
|
+
`${filterDesc} entered: only ${entered.tradeCount} trades (< ${minTrades} threshold)`,
|
|
780
766
|
);
|
|
781
767
|
}
|
|
782
768
|
if (filtered_out.tradeCount > 0 && filtered_out.tradeCount < minTrades) {
|
|
783
769
|
warnings.push(
|
|
784
|
-
`${filterDesc} filtered_out: only ${filtered_out.tradeCount} trades (< ${minTrades} threshold)
|
|
770
|
+
`${filterDesc} filtered_out: only ${filtered_out.tradeCount} trades (< ${minTrades} threshold)`,
|
|
785
771
|
);
|
|
786
772
|
}
|
|
787
773
|
}
|
|
788
774
|
|
|
789
775
|
// Summary text
|
|
790
|
-
const execNote =
|
|
776
|
+
const execNote =
|
|
777
|
+
executionFilters.length > 0 ? ` (${executionFilters.length} execution filter(s) skipped)` : "";
|
|
791
778
|
const summaryText = `Filter validation for '${strategyName}': ${filters.length} market filter(s) analyzed across ${matched.length} trades${execNote}. Baseline (all market filters): ${baseline.tradeCount} trades, win rate ${baseline.winRate.toFixed(1)}%, avg P&L $${baseline.avgPl.toFixed(2)}. ${profileUpdateHints.length} update hint(s).`;
|
|
792
779
|
|
|
793
780
|
return createToolOutput(summaryText, {
|
|
@@ -798,7 +785,9 @@ export async function handleValidateEntryFilters(
|
|
|
798
785
|
single: ablationSingle,
|
|
799
786
|
pairs: ablationPairs,
|
|
800
787
|
},
|
|
801
|
-
execution_filters_skipped: executionFilters.map(
|
|
788
|
+
execution_filters_skipped: executionFilters.map(
|
|
789
|
+
(f) => f.description || `${f.field} ${f.operator} ${f.value}`,
|
|
790
|
+
),
|
|
802
791
|
profile_update_hints: profileUpdateHints,
|
|
803
792
|
warnings,
|
|
804
793
|
});
|
|
@@ -822,8 +811,11 @@ export const portfolioStructureMapSchema = z.object({
|
|
|
822
811
|
|
|
823
812
|
export async function handlePortfolioStructureMap(
|
|
824
813
|
input: z.infer<typeof portfolioStructureMapSchema>,
|
|
825
|
-
baseDir: string
|
|
826
|
-
): Promise<
|
|
814
|
+
baseDir: string,
|
|
815
|
+
): Promise<
|
|
816
|
+
| ReturnType<typeof createToolOutput>
|
|
817
|
+
| { content: Array<{ type: "text"; text: string }>; isError?: boolean }
|
|
818
|
+
> {
|
|
827
819
|
try {
|
|
828
820
|
const { blockId, minTrades } = portfolioStructureMapSchema.parse(input);
|
|
829
821
|
const conn = await getConnection(baseDir);
|
|
@@ -858,7 +850,9 @@ export async function handlePortfolioStructureMap(
|
|
|
858
850
|
try {
|
|
859
851
|
block = await loadBlock(baseDir, profile.blockId);
|
|
860
852
|
} catch {
|
|
861
|
-
warnings.push(
|
|
853
|
+
warnings.push(
|
|
854
|
+
`Could not load block '${profile.blockId}' for strategy '${profile.strategyName}'`,
|
|
855
|
+
);
|
|
862
856
|
continue;
|
|
863
857
|
}
|
|
864
858
|
|
|
@@ -871,7 +865,9 @@ export async function handlePortfolioStructureMap(
|
|
|
871
865
|
}
|
|
872
866
|
}
|
|
873
867
|
if (trades.length === 0) {
|
|
874
|
-
warnings.push(
|
|
868
|
+
warnings.push(
|
|
869
|
+
`No trades found for strategy '${profile.strategyName}' in block '${profile.blockId}'`,
|
|
870
|
+
);
|
|
875
871
|
continue;
|
|
876
872
|
}
|
|
877
873
|
|
|
@@ -927,11 +923,7 @@ export async function handlePortfolioStructureMap(
|
|
|
927
923
|
|
|
928
924
|
// Handle missing Trend_Direction
|
|
929
925
|
let trend: TrendLabel | null = null;
|
|
930
|
-
if (
|
|
931
|
-
trendRaw === null ||
|
|
932
|
-
trendRaw === undefined ||
|
|
933
|
-
trendRaw === ""
|
|
934
|
-
) {
|
|
926
|
+
if (trendRaw === null || trendRaw === undefined || trendRaw === "") {
|
|
935
927
|
unknownTrendCount++;
|
|
936
928
|
if (!unknownTrendPls.has(strategyName)) {
|
|
937
929
|
unknownTrendPls.set(strategyName, []);
|
|
@@ -1001,7 +993,7 @@ export async function handlePortfolioStructureMap(
|
|
|
1001
993
|
// Thin-data warning
|
|
1002
994
|
if (pls.length > 0 && pls.length < minTrades) {
|
|
1003
995
|
warnings.push(
|
|
1004
|
-
`Thin data: '${stratName}' has only ${pls.length} trades in ${regimeLabel}/${trend} (threshold: ${minTrades})
|
|
996
|
+
`Thin data: '${stratName}' has only ${pls.length} trades in ${regimeLabel}/${trend} (threshold: ${minTrades})`,
|
|
1005
997
|
);
|
|
1006
998
|
}
|
|
1007
999
|
}
|
|
@@ -1026,7 +1018,7 @@ export async function handlePortfolioStructureMap(
|
|
|
1026
1018
|
// Handle unknown trend trades
|
|
1027
1019
|
if (unknownTrendCount > 0) {
|
|
1028
1020
|
warnings.push(
|
|
1029
|
-
`${unknownTrendCount} trades had missing or unknown Trend_Direction. Consider running enrich_market_data to populate Trend_Direction
|
|
1021
|
+
`${unknownTrendCount} trades had missing or unknown Trend_Direction. Consider running enrich_market_data to populate Trend_Direction.`,
|
|
1030
1022
|
);
|
|
1031
1023
|
}
|
|
1032
1024
|
|
|
@@ -1034,10 +1026,7 @@ export async function handlePortfolioStructureMap(
|
|
|
1034
1026
|
const unknownTrendStats: Record<string, SliceStats> | undefined =
|
|
1035
1027
|
unknownTrendPls.size > 0
|
|
1036
1028
|
? Object.fromEntries(
|
|
1037
|
-
[...unknownTrendPls.entries()].map(([name, pls]) => [
|
|
1038
|
-
name,
|
|
1039
|
-
computeSliceStats(pls),
|
|
1040
|
-
])
|
|
1029
|
+
[...unknownTrendPls.entries()].map(([name, pls]) => [name, computeSliceStats(pls)]),
|
|
1041
1030
|
)
|
|
1042
1031
|
: undefined;
|
|
1043
1032
|
|
|
@@ -1086,10 +1075,7 @@ export async function handlePortfolioStructureMap(
|
|
|
1086
1075
|
* This includes portfolio_structure_map (from Plan 03) and
|
|
1087
1076
|
* analyze_structure_fit + validate_entry_filters (from Plan 02, if present).
|
|
1088
1077
|
*/
|
|
1089
|
-
export function registerProfileAnalysisTools(
|
|
1090
|
-
server: McpServer,
|
|
1091
|
-
baseDir: string
|
|
1092
|
-
): void {
|
|
1078
|
+
export function registerProfileAnalysisTools(server: McpServer, baseDir: string): void {
|
|
1093
1079
|
// portfolio_structure_map: optional blockId means we can't always use withSyncedBlock.
|
|
1094
1080
|
// When blockId is provided, sync that block. When omitted, sync all blocks.
|
|
1095
1081
|
server.registerTool(
|
|
@@ -1119,7 +1105,7 @@ export function registerProfileAnalysisTools(
|
|
|
1119
1105
|
}
|
|
1120
1106
|
|
|
1121
1107
|
return handlePortfolioStructureMap(input, baseDir);
|
|
1122
|
-
}
|
|
1108
|
+
},
|
|
1123
1109
|
);
|
|
1124
1110
|
|
|
1125
1111
|
// -------------------------------------------------------------------------
|
|
@@ -1137,7 +1123,7 @@ export function registerProfileAnalysisTools(
|
|
|
1137
1123
|
},
|
|
1138
1124
|
withSyncedBlock(baseDir, async (input, ctx) => {
|
|
1139
1125
|
return handleAnalyzeStructureFit(input, ctx.baseDir);
|
|
1140
|
-
})
|
|
1126
|
+
}),
|
|
1141
1127
|
);
|
|
1142
1128
|
|
|
1143
1129
|
// -------------------------------------------------------------------------
|
|
@@ -1155,6 +1141,6 @@ export function registerProfileAnalysisTools(
|
|
|
1155
1141
|
},
|
|
1156
1142
|
withSyncedBlock(baseDir, async (input, ctx) => {
|
|
1157
1143
|
return handleValidateEntryFilters(input, ctx.baseDir);
|
|
1158
|
-
})
|
|
1144
|
+
}),
|
|
1159
1145
|
);
|
|
1160
1146
|
}
|