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
package/src/tools/blocks/core.ts
CHANGED
|
@@ -6,28 +6,22 @@
|
|
|
6
6
|
|
|
7
7
|
import { z } from "zod";
|
|
8
8
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
9
|
-
import {
|
|
10
|
-
loadBlock,
|
|
11
|
-
listBlocks,
|
|
12
|
-
loadReportingLog,
|
|
13
|
-
} from "../../utils/block-loader.ts";
|
|
9
|
+
import { loadBlock, listBlocks, loadReportingLog } from "../../utils/block-loader.ts";
|
|
14
10
|
import {
|
|
15
11
|
createToolOutput,
|
|
16
12
|
formatCurrency,
|
|
17
13
|
formatPercent,
|
|
18
14
|
formatRatio,
|
|
19
15
|
} from "../../utils/output-formatter.ts";
|
|
20
|
-
import {
|
|
21
|
-
PortfolioStatsCalculator,
|
|
22
|
-
calculateDailyExposure,
|
|
23
|
-
} from "@tradeblocks/lib";
|
|
16
|
+
import { PortfolioStatsCalculator, calculateDailyExposure } from "@tradeblocks/lib";
|
|
24
17
|
import type { Trade, PeakExposure, EquityCurvePoint } from "@tradeblocks/lib";
|
|
25
18
|
import { resolveTradeTicker } from "../../utils/ticker.ts";
|
|
26
|
-
import { filterByStrategy, filterByDateRange, filterDailyLogsByDateRange } from "../shared/filters.ts";
|
|
27
19
|
import {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
20
|
+
filterByStrategy,
|
|
21
|
+
filterByDateRange,
|
|
22
|
+
filterDailyLogsByDateRange,
|
|
23
|
+
} from "../shared/filters.ts";
|
|
24
|
+
import { withSyncedBlock, withFullSync } from "../middleware/sync-middleware.ts";
|
|
31
25
|
|
|
32
26
|
/**
|
|
33
27
|
* Calculate peak daily exposure using the shared sweep-line algorithm.
|
|
@@ -35,7 +29,7 @@ import {
|
|
|
35
29
|
*/
|
|
36
30
|
function calculatePeakExposure(
|
|
37
31
|
trades: Trade[],
|
|
38
|
-
initialCapital: number
|
|
32
|
+
initialCapital: number,
|
|
39
33
|
): {
|
|
40
34
|
peakByDollars: PeakExposure | null;
|
|
41
35
|
peakByPercent: PeakExposure | null;
|
|
@@ -43,8 +37,7 @@ function calculatePeakExposure(
|
|
|
43
37
|
// Build equity curve from trades - P&L is realized on close date
|
|
44
38
|
const closedTrades = trades.filter((t) => t.dateClosed);
|
|
45
39
|
const sortedByClose = [...closedTrades].sort(
|
|
46
|
-
(a, b) =>
|
|
47
|
-
new Date(a.dateClosed!).getTime() - new Date(b.dateClosed!).getTime()
|
|
40
|
+
(a, b) => new Date(a.dateClosed!).getTime() - new Date(b.dateClosed!).getTime(),
|
|
48
41
|
);
|
|
49
42
|
|
|
50
43
|
const equityCurve: EquityCurvePoint[] = [];
|
|
@@ -70,10 +63,7 @@ function calculatePeakExposure(
|
|
|
70
63
|
/**
|
|
71
64
|
* Register core block tools
|
|
72
65
|
*/
|
|
73
|
-
export function registerCoreBlockTools(
|
|
74
|
-
server: McpServer,
|
|
75
|
-
baseDir: string
|
|
76
|
-
): void {
|
|
66
|
+
export function registerCoreBlockTools(server: McpServer, baseDir: string): void {
|
|
77
67
|
const calculator = new PortfolioStatsCalculator();
|
|
78
68
|
|
|
79
69
|
// Tool 1: list_blocks (formerly list_backtests)
|
|
@@ -87,16 +77,11 @@ export function registerCoreBlockTools(
|
|
|
87
77
|
.enum(["name", "tradeCount", "netPl", "dateRange"])
|
|
88
78
|
.default("name")
|
|
89
79
|
.describe("Sort results by field (default: name)"),
|
|
90
|
-
sortOrder: z
|
|
91
|
-
.enum(["asc", "desc"])
|
|
92
|
-
.default("asc")
|
|
93
|
-
.describe("Sort direction (default: asc)"),
|
|
80
|
+
sortOrder: z.enum(["asc", "desc"]).default("asc").describe("Sort direction (default: asc)"),
|
|
94
81
|
containsStrategy: z
|
|
95
82
|
.string()
|
|
96
83
|
.optional()
|
|
97
|
-
.describe(
|
|
98
|
-
"Filter to blocks containing this strategy name (case-insensitive)"
|
|
99
|
-
),
|
|
84
|
+
.describe("Filter to blocks containing this strategy name (case-insensitive)"),
|
|
100
85
|
minTrades: z
|
|
101
86
|
.number()
|
|
102
87
|
.min(1)
|
|
@@ -105,14 +90,12 @@ export function registerCoreBlockTools(
|
|
|
105
90
|
hasDailyLog: z
|
|
106
91
|
.boolean()
|
|
107
92
|
.optional()
|
|
108
|
-
.describe(
|
|
109
|
-
"Filter to blocks with (true) or without (false) daily log data"
|
|
110
|
-
),
|
|
93
|
+
.describe("Filter to blocks with (true) or without (false) daily log data"),
|
|
111
94
|
hasReportingLog: z
|
|
112
95
|
.boolean()
|
|
113
96
|
.optional()
|
|
114
97
|
.describe(
|
|
115
|
-
"Filter to blocks with (true) or without (false) reporting log data (actual trade execution)"
|
|
98
|
+
"Filter to blocks with (true) or without (false) reporting log data (actual trade execution)",
|
|
116
99
|
),
|
|
117
100
|
limit: z
|
|
118
101
|
.number()
|
|
@@ -125,109 +108,99 @@ export function registerCoreBlockTools(
|
|
|
125
108
|
withFullSync(
|
|
126
109
|
baseDir,
|
|
127
110
|
async (
|
|
128
|
-
{
|
|
129
|
-
|
|
130
|
-
sortOrder,
|
|
131
|
-
containsStrategy,
|
|
132
|
-
minTrades,
|
|
133
|
-
hasDailyLog,
|
|
134
|
-
hasReportingLog,
|
|
135
|
-
limit,
|
|
136
|
-
},
|
|
137
|
-
{ blockSyncResult: syncResult }
|
|
111
|
+
{ sortBy, sortOrder, containsStrategy, minTrades, hasDailyLog, hasReportingLog, limit },
|
|
112
|
+
{ blockSyncResult: syncResult },
|
|
138
113
|
) => {
|
|
139
114
|
try {
|
|
140
115
|
let blocks = await listBlocks(baseDir);
|
|
141
116
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// Sort blocks based on parameters
|
|
160
|
-
const multiplier = sortOrder === "asc" ? 1 : -1;
|
|
161
|
-
blocks = [...blocks].sort((a, b) => {
|
|
162
|
-
switch (sortBy) {
|
|
163
|
-
case "tradeCount":
|
|
164
|
-
return (a.tradeCount - b.tradeCount) * multiplier;
|
|
165
|
-
case "netPl":
|
|
166
|
-
return ((a.netPl ?? 0) - (b.netPl ?? 0)) * multiplier;
|
|
167
|
-
case "dateRange": {
|
|
168
|
-
const aTime = a.dateRange.end?.getTime() ?? 0;
|
|
169
|
-
const bTime = b.dateRange.end?.getTime() ?? 0;
|
|
170
|
-
return (aTime - bTime) * multiplier;
|
|
171
|
-
}
|
|
172
|
-
case "name":
|
|
173
|
-
default:
|
|
174
|
-
return a.name.localeCompare(b.name) * multiplier;
|
|
117
|
+
// Apply filters
|
|
118
|
+
if (containsStrategy) {
|
|
119
|
+
const strategyLower = containsStrategy.toLowerCase();
|
|
120
|
+
blocks = blocks.filter((b) =>
|
|
121
|
+
b.strategies.some((s) => s.toLowerCase().includes(strategyLower)),
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
if (minTrades !== undefined) {
|
|
125
|
+
blocks = blocks.filter((b) => b.tradeCount >= minTrades);
|
|
126
|
+
}
|
|
127
|
+
if (hasDailyLog !== undefined) {
|
|
128
|
+
blocks = blocks.filter((b) => b.hasDailyLog === hasDailyLog);
|
|
129
|
+
}
|
|
130
|
+
if (hasReportingLog !== undefined) {
|
|
131
|
+
blocks = blocks.filter((b) => b.hasReportingLog === hasReportingLog);
|
|
175
132
|
}
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
// Apply limit
|
|
179
|
-
const totalBeforeLimit = blocks.length;
|
|
180
|
-
if (limit !== undefined && limit < blocks.length) {
|
|
181
|
-
blocks = blocks.slice(0, limit);
|
|
182
|
-
}
|
|
183
133
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
(b) =>
|
|
187
|
-
|
|
188
|
-
|
|
134
|
+
// Sort blocks based on parameters
|
|
135
|
+
const multiplier = sortOrder === "asc" ? 1 : -1;
|
|
136
|
+
blocks = [...blocks].sort((a, b) => {
|
|
137
|
+
switch (sortBy) {
|
|
138
|
+
case "tradeCount":
|
|
139
|
+
return (a.tradeCount - b.tradeCount) * multiplier;
|
|
140
|
+
case "netPl":
|
|
141
|
+
return ((a.netPl ?? 0) - (b.netPl ?? 0)) * multiplier;
|
|
142
|
+
case "dateRange": {
|
|
143
|
+
const aTime = a.dateRange.end?.getTime() ?? 0;
|
|
144
|
+
const bTime = b.dateRange.end?.getTime() ?? 0;
|
|
145
|
+
return (aTime - bTime) * multiplier;
|
|
146
|
+
}
|
|
147
|
+
case "name":
|
|
148
|
+
default:
|
|
149
|
+
return a.name.localeCompare(b.name) * multiplier;
|
|
150
|
+
}
|
|
151
|
+
});
|
|
189
152
|
|
|
190
|
-
|
|
191
|
-
|
|
153
|
+
// Apply limit
|
|
154
|
+
const totalBeforeLimit = blocks.length;
|
|
155
|
+
if (limit !== undefined && limit < blocks.length) {
|
|
156
|
+
blocks = blocks.slice(0, limit);
|
|
157
|
+
}
|
|
192
158
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
start: b.dateRange.start?.toISOString() ?? null,
|
|
211
|
-
end: b.dateRange.end?.toISOString() ?? null,
|
|
159
|
+
// Brief summary for user display
|
|
160
|
+
const blocksWithReporting = blocks.filter((b) => b.hasReportingLog).length;
|
|
161
|
+
const summary = `Found ${blocks.length} block(s)${totalBeforeLimit > blocks.length ? ` (showing ${blocks.length} of ${totalBeforeLimit})` : ""}${blocksWithReporting > 0 ? `, ${blocksWithReporting} with reporting logs` : ""}`;
|
|
162
|
+
|
|
163
|
+
// Collect sync errors
|
|
164
|
+
const syncErrors = [...syncResult.errors];
|
|
165
|
+
|
|
166
|
+
// Build structured data for Claude reasoning
|
|
167
|
+
const structuredData = {
|
|
168
|
+
options: {
|
|
169
|
+
sortBy,
|
|
170
|
+
sortOrder,
|
|
171
|
+
containsStrategy: containsStrategy ?? null,
|
|
172
|
+
minTrades: minTrades ?? null,
|
|
173
|
+
hasDailyLog: hasDailyLog ?? null,
|
|
174
|
+
hasReportingLog: hasReportingLog ?? null,
|
|
175
|
+
limit: limit ?? null,
|
|
212
176
|
},
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
177
|
+
totalMatching: totalBeforeLimit,
|
|
178
|
+
blocks: blocks.map((b) => ({
|
|
179
|
+
id: b.blockId,
|
|
180
|
+
name: b.name,
|
|
181
|
+
tradeCount: b.tradeCount,
|
|
182
|
+
dateRange: {
|
|
183
|
+
start: b.dateRange.start?.toISOString() ?? null,
|
|
184
|
+
end: b.dateRange.end?.toISOString() ?? null,
|
|
185
|
+
},
|
|
186
|
+
strategies: b.strategies,
|
|
187
|
+
totalPl: b.totalPl,
|
|
188
|
+
netPl: b.netPl,
|
|
189
|
+
hasDailyLog: b.hasDailyLog,
|
|
190
|
+
hasReportingLog: b.hasReportingLog,
|
|
191
|
+
reportingLog: b.reportingLog ?? null,
|
|
192
|
+
})),
|
|
193
|
+
count: blocks.length,
|
|
194
|
+
// Add sync info (informational for Claude)
|
|
195
|
+
syncInfo: {
|
|
196
|
+
blocksProcessed: syncResult.blocksProcessed,
|
|
197
|
+
blocksSynced: syncResult.blocksSynced,
|
|
198
|
+
blocksUnchanged: syncResult.blocksUnchanged,
|
|
199
|
+
blocksDeleted: syncResult.blocksDeleted,
|
|
200
|
+
},
|
|
201
|
+
// Add sync errors if any occurred
|
|
202
|
+
...(syncErrors.length > 0 ? { syncErrors } : {}),
|
|
203
|
+
};
|
|
231
204
|
|
|
232
205
|
return createToolOutput(summary, structuredData);
|
|
233
206
|
} catch (error) {
|
|
@@ -241,8 +214,8 @@ export function registerCoreBlockTools(
|
|
|
241
214
|
isError: true,
|
|
242
215
|
};
|
|
243
216
|
}
|
|
244
|
-
}
|
|
245
|
-
)
|
|
217
|
+
},
|
|
218
|
+
),
|
|
246
219
|
);
|
|
247
220
|
|
|
248
221
|
// Tool 2: get_block_info
|
|
@@ -252,9 +225,7 @@ export function registerCoreBlockTools(
|
|
|
252
225
|
description:
|
|
253
226
|
"Get detailed metadata for a block including available strategies, date range, and daily log status. Use blockId from list_blocks.",
|
|
254
227
|
inputSchema: z.object({
|
|
255
|
-
blockId: z
|
|
256
|
-
.string()
|
|
257
|
-
.describe("Block ID from list_blocks (e.g., 'main-port')"),
|
|
228
|
+
blockId: z.string().describe("Block ID from list_blocks (e.g., 'main-port')"),
|
|
258
229
|
}),
|
|
259
230
|
},
|
|
260
231
|
withSyncedBlock(baseDir, async ({ blockId }) => {
|
|
@@ -263,9 +234,7 @@ export function registerCoreBlockTools(
|
|
|
263
234
|
const trades = block.trades;
|
|
264
235
|
const dailyLogs = block.dailyLogs;
|
|
265
236
|
|
|
266
|
-
const strategies = Array.from(
|
|
267
|
-
new Set(trades.map((t) => t.strategy))
|
|
268
|
-
).sort();
|
|
237
|
+
const strategies = Array.from(new Set(trades.map((t) => t.strategy))).sort();
|
|
269
238
|
const dates = trades.map((t) => new Date(t.dateOpened).getTime());
|
|
270
239
|
const dateRange = {
|
|
271
240
|
start: dates.length > 0 ? new Date(Math.min(...dates)) : null,
|
|
@@ -299,7 +268,7 @@ export function registerCoreBlockTools(
|
|
|
299
268
|
isError: true,
|
|
300
269
|
};
|
|
301
270
|
}
|
|
302
|
-
})
|
|
271
|
+
}),
|
|
303
272
|
);
|
|
304
273
|
|
|
305
274
|
// Tool 2b: get_reporting_log_stats
|
|
@@ -347,13 +316,16 @@ export function registerCoreBlockTools(
|
|
|
347
316
|
strategyTrades.get(key)!.push(trade);
|
|
348
317
|
}
|
|
349
318
|
|
|
350
|
-
const byStrategy: Record<
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
319
|
+
const byStrategy: Record<
|
|
320
|
+
string,
|
|
321
|
+
{
|
|
322
|
+
tradeCount: number;
|
|
323
|
+
winRate: number;
|
|
324
|
+
totalPL: number;
|
|
325
|
+
avgPL: number;
|
|
326
|
+
contractCount: number;
|
|
327
|
+
}
|
|
328
|
+
> = {};
|
|
357
329
|
|
|
358
330
|
for (const [strategy, strategyTradeList] of strategyTrades) {
|
|
359
331
|
const tradeCount = strategyTradeList.length;
|
|
@@ -361,9 +333,7 @@ export function registerCoreBlockTools(
|
|
|
361
333
|
const winRate = tradeCount > 0 ? winningTrades / tradeCount : 0;
|
|
362
334
|
const totalPL = strategyTradeList.reduce((sum, t) => sum + t.pl, 0);
|
|
363
335
|
const avgPL = tradeCount > 0 ? totalPL / tradeCount : 0;
|
|
364
|
-
const contractCount = strategyTradeList.reduce(
|
|
365
|
-
(sum, t) => sum + t.numContracts, 0
|
|
366
|
-
);
|
|
336
|
+
const contractCount = strategyTradeList.reduce((sum, t) => sum + t.numContracts, 0);
|
|
367
337
|
byStrategy[strategy] = { tradeCount, winRate, totalPL, avgPL, contractCount };
|
|
368
338
|
}
|
|
369
339
|
|
|
@@ -404,7 +374,7 @@ export function registerCoreBlockTools(
|
|
|
404
374
|
isError: true,
|
|
405
375
|
};
|
|
406
376
|
}
|
|
407
|
-
})
|
|
377
|
+
}),
|
|
408
378
|
);
|
|
409
379
|
|
|
410
380
|
// Tool 3: get_statistics
|
|
@@ -414,23 +384,13 @@ export function registerCoreBlockTools(
|
|
|
414
384
|
description:
|
|
415
385
|
"Get comprehensive portfolio statistics: win rate, Sharpe ratio, max drawdown, P&L metrics, and more. Use blockId from list_blocks. Optionally filter by strategy, ticker, or date range.",
|
|
416
386
|
inputSchema: z.object({
|
|
417
|
-
blockId: z
|
|
418
|
-
|
|
419
|
-
.describe("Block ID from list_blocks (e.g., 'main-port')"),
|
|
420
|
-
strategy: z
|
|
421
|
-
.string()
|
|
422
|
-
.optional()
|
|
423
|
-
.describe("Filter by strategy name (case-insensitive)"),
|
|
387
|
+
blockId: z.string().describe("Block ID from list_blocks (e.g., 'main-port')"),
|
|
388
|
+
strategy: z.string().optional().describe("Filter by strategy name (case-insensitive)"),
|
|
424
389
|
tickerFilter: z
|
|
425
390
|
.string()
|
|
426
391
|
.optional()
|
|
427
|
-
.describe(
|
|
428
|
-
|
|
429
|
-
),
|
|
430
|
-
startDate: z
|
|
431
|
-
.string()
|
|
432
|
-
.optional()
|
|
433
|
-
.describe("Start date filter (YYYY-MM-DD)"),
|
|
392
|
+
.describe("Filter trades by underlying ticker symbol (e.g., 'SPY', 'AAPL')"),
|
|
393
|
+
startDate: z.string().optional().describe("Start date filter (YYYY-MM-DD)"),
|
|
434
394
|
endDate: z.string().optional().describe("End date filter (YYYY-MM-DD)"),
|
|
435
395
|
}),
|
|
436
396
|
},
|
|
@@ -439,105 +399,101 @@ export function registerCoreBlockTools(
|
|
|
439
399
|
async ({ blockId, strategy, tickerFilter, startDate, endDate }, { syncResult }) => {
|
|
440
400
|
try {
|
|
441
401
|
const block = await loadBlock(baseDir, blockId);
|
|
442
|
-
|
|
443
|
-
|
|
402
|
+
let trades = block.trades;
|
|
403
|
+
const dailyLogs = block.dailyLogs;
|
|
444
404
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
405
|
+
// Apply filters
|
|
406
|
+
trades = filterByStrategy(trades, strategy);
|
|
407
|
+
trades = filterByDateRange(trades, startDate, endDate);
|
|
448
408
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
);
|
|
455
|
-
}
|
|
409
|
+
// Apply ticker filter (supports both explicit ticker columns and legs-derived symbols)
|
|
410
|
+
if (tickerFilter) {
|
|
411
|
+
const tickerLower = tickerFilter.toLowerCase();
|
|
412
|
+
trades = trades.filter((t) => resolveTradeTicker(t).toLowerCase() === tickerLower);
|
|
413
|
+
}
|
|
456
414
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
415
|
+
if (trades.length === 0) {
|
|
416
|
+
return {
|
|
417
|
+
content: [
|
|
418
|
+
{
|
|
419
|
+
type: "text",
|
|
420
|
+
text: `No trades found matching the specified filters.`,
|
|
421
|
+
},
|
|
422
|
+
],
|
|
423
|
+
};
|
|
424
|
+
}
|
|
467
425
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
426
|
+
// Filter daily logs by date range when date filters are provided
|
|
427
|
+
// Only applies when not strategy-filtered (daily logs represent full portfolio)
|
|
428
|
+
const isStrategyFiltered = !!strategy;
|
|
429
|
+
let filteredDailyLogs = dailyLogs;
|
|
430
|
+
if (!isStrategyFiltered && (startDate || endDate) && dailyLogs) {
|
|
431
|
+
filteredDailyLogs = filterDailyLogsByDateRange(dailyLogs, startDate, endDate);
|
|
432
|
+
}
|
|
475
433
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
434
|
+
// When strategy filter is applied, we MUST use trade-based calculations
|
|
435
|
+
// because daily logs represent the FULL portfolio, not per-strategy
|
|
436
|
+
const stats = calculator.calculatePortfolioStats(
|
|
437
|
+
trades,
|
|
438
|
+
isStrategyFiltered ? undefined : filteredDailyLogs,
|
|
439
|
+
isStrategyFiltered,
|
|
440
|
+
);
|
|
483
441
|
|
|
484
|
-
|
|
485
|
-
|
|
442
|
+
// Calculate peak daily exposure
|
|
443
|
+
const peakExposure = calculatePeakExposure(trades, stats.initialCapital);
|
|
486
444
|
|
|
487
|
-
|
|
488
|
-
|
|
445
|
+
// Brief summary for user display
|
|
446
|
+
const summary = `Stats: ${blockId}${strategy ? ` (${strategy})` : ""} | ${stats.totalTrades} trades | Win: ${formatPercent(stats.winRate * 100)} | Net P&L: ${formatCurrency(stats.netPl)} | Sharpe: ${formatRatio(stats.sharpeRatio)}`;
|
|
489
447
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
: {}),
|
|
540
|
-
};
|
|
448
|
+
// Build structured data for Claude reasoning - include full PortfolioStats
|
|
449
|
+
const structuredData = {
|
|
450
|
+
blockId,
|
|
451
|
+
filters: {
|
|
452
|
+
strategy: strategy ?? null,
|
|
453
|
+
tickerFilter: tickerFilter ?? null,
|
|
454
|
+
startDate: startDate ?? null,
|
|
455
|
+
endDate: endDate ?? null,
|
|
456
|
+
},
|
|
457
|
+
stats: {
|
|
458
|
+
totalTrades: stats.totalTrades,
|
|
459
|
+
winningTrades: stats.winningTrades,
|
|
460
|
+
losingTrades: stats.losingTrades,
|
|
461
|
+
breakEvenTrades: stats.breakEvenTrades,
|
|
462
|
+
winRate: stats.winRate,
|
|
463
|
+
totalPl: stats.totalPl,
|
|
464
|
+
netPl: stats.netPl,
|
|
465
|
+
totalCommissions: stats.totalCommissions,
|
|
466
|
+
avgWin: stats.avgWin,
|
|
467
|
+
avgLoss: stats.avgLoss,
|
|
468
|
+
maxWin: stats.maxWin,
|
|
469
|
+
maxLoss: stats.maxLoss,
|
|
470
|
+
profitFactor: stats.profitFactor,
|
|
471
|
+
sharpeRatio: stats.sharpeRatio,
|
|
472
|
+
sortinoRatio: stats.sortinoRatio,
|
|
473
|
+
calmarRatio: stats.calmarRatio,
|
|
474
|
+
maxDrawdown: stats.maxDrawdown,
|
|
475
|
+
timeInDrawdown: stats.timeInDrawdown,
|
|
476
|
+
kellyPercentage: stats.kellyPercentage,
|
|
477
|
+
cagr: stats.cagr,
|
|
478
|
+
initialCapital: stats.initialCapital,
|
|
479
|
+
avgDailyPl: stats.avgDailyPl,
|
|
480
|
+
maxWinStreak: stats.maxWinStreak,
|
|
481
|
+
maxLossStreak: stats.maxLossStreak,
|
|
482
|
+
currentStreak: stats.currentStreak,
|
|
483
|
+
monthlyWinRate: stats.monthlyWinRate,
|
|
484
|
+
weeklyWinRate: stats.weeklyWinRate,
|
|
485
|
+
},
|
|
486
|
+
peakExposure: {
|
|
487
|
+
byDollars: peakExposure.peakByDollars,
|
|
488
|
+
byPercent: peakExposure.peakByPercent,
|
|
489
|
+
},
|
|
490
|
+
// Add sync info if sync occurred
|
|
491
|
+
...(syncResult.status === "synced"
|
|
492
|
+
? { syncInfo: { status: "synced", tradeCount: syncResult.tradeCount } }
|
|
493
|
+
: {}),
|
|
494
|
+
// Add sync warning if sync errored (continuing with potentially stale data)
|
|
495
|
+
...(syncResult.status === "error" ? { syncWarning: syncResult.error } : {}),
|
|
496
|
+
};
|
|
541
497
|
|
|
542
498
|
return createToolOutput(summary, structuredData);
|
|
543
499
|
} catch (error) {
|
|
@@ -551,8 +507,7 @@ export function registerCoreBlockTools(
|
|
|
551
507
|
isError: true,
|
|
552
508
|
};
|
|
553
509
|
}
|
|
554
|
-
}
|
|
555
|
-
)
|
|
510
|
+
},
|
|
511
|
+
),
|
|
556
512
|
);
|
|
557
|
-
|
|
558
513
|
}
|