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.
- package/README.md +82 -68
- 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-BKQ4PM4Y.js → chunk-6S37CXUA.js} +11 -13
- package/dist/chunk-6S37CXUA.js.map +1 -0
- package/dist/{chunk-PRAYH3RT.js → chunk-AP7IHUUR.js} +29 -12
- package/dist/chunk-AP7IHUUR.js.map +1 -0
- package/dist/{chunk-QTTR7AAW.js → chunk-C6LL746C.js} +67 -84
- package/dist/{chunk-QTTR7AAW.js.map → chunk-C6LL746C.js.map} +1 -1
- package/dist/{chunk-W2PP3LEH.js → chunk-SEUZYQGQ.js} +17 -8
- package/dist/chunk-SEUZYQGQ.js.map +1 -0
- package/dist/{chunk-4BLCXNQ6.js → chunk-UBUC5A66.js} +105 -114
- package/dist/chunk-UBUC5A66.js.map +1 -0
- package/dist/{chunk-XXYOUIZY.js → chunk-VDU25Z6X.js} +63 -80
- package/dist/chunk-VDU25Z6X.js.map +1 -0
- package/dist/daily-log-processor-BY3ISY6K.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-H3ARIVZ4.js +16 -0
- package/dist/{proto-3HZTCWK4.js → proto-V23GIZG2.js} +2 -2
- package/dist/{sync-PO4IPCYV.js → sync-EVLKB3ZJ.js} +7 -7
- package/dist/test-exports.js +358 -254
- package/dist/test-exports.js.map +1 -1
- package/dist/trade-processor-EYA3I3XB.js +9 -0
- package/dist/{trades-store-LOL3FQSF.js → trades-store-R3NL25T2.js} +2 -2
- package/manifest.json +3 -11
- package/package.json +1 -1
- package/server/{chunk-NZO6PT64.js → chunk-4P7D7YZP.js} +102 -113
- package/server/chunk-4P7D7YZP.js.map +1 -0
- package/server/{chunk-72GKJE2U.js → chunk-BOPHW5M6.js} +521 -235
- package/server/chunk-BOPHW5M6.js.map +1 -0
- package/server/{chunk-WA5AAPCH.js → chunk-GH2552SE.js} +17 -8
- package/server/chunk-GH2552SE.js.map +1 -0
- package/server/{chunk-5EBXHT6C.js → chunk-OBYKFW2B.js} +11 -13
- package/server/chunk-OBYKFW2B.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/{chunk-CSDVJPBB.js → chunk-YUCOAJ4Z.js} +29 -12
- package/server/chunk-YUCOAJ4Z.js.map +1 -0
- package/server/{chunk-PNKG7RY7.js → chunk-ZBJCF4ZG.js} +63 -80
- package/server/chunk-ZBJCF4ZG.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-ENEUT22A.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 +1771 -1538
- package/server/index.js.map +1 -1
- package/server/iv-solver-worker.js +9 -1
- package/server/market-provider-B437HKLW.js +17 -0
- package/server/{proto-DWRZJO4E.js → proto-YZ4TLX2N.js} +2 -2
- package/server/{sync-QFI5L7S7.js → sync-V25UQJA3.js} +7 -7
- package/server/trade-processor-L3PIQ5TG.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 +12 -4
- package/src/db/connection.ts +107 -40
- 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 +127 -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/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 +9 -12
- 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 +68 -82
- package/src/utils/calibration-probe.ts +1 -2
- 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-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/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-BY3ISY6K.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-H3ARIVZ4.js.map} +0 -0
- /package/dist/{proto-3HZTCWK4.js.map → proto-V23GIZG2.js.map} +0 -0
- /package/dist/{sync-PO4IPCYV.js.map → sync-EVLKB3ZJ.js.map} +0 -0
- /package/dist/{trade-processor-NHU4VWRX.js.map → trade-processor-EYA3I3XB.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-ENEUT22A.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-B437HKLW.js.map} +0 -0
- /package/server/{proto-DWRZJO4E.js.map → proto-YZ4TLX2N.js.map} +0 -0
- /package/server/{sync-QFI5L7S7.js.map → sync-V25UQJA3.js.map} +0 -0
- /package/server/{trade-processor-JWVS37KM.js.map → trade-processor-L3PIQ5TG.js.map} +0 -0
- /package/server/{trades-store-VY26MWER.js.map → trades-store-RTTEYMXH.js.map} +0 -0
|
@@ -23,10 +23,7 @@ import { withSyncedBlock } from "../middleware/sync-middleware.ts";
|
|
|
23
23
|
/**
|
|
24
24
|
* Register the analyze_slippage_trends tool
|
|
25
25
|
*/
|
|
26
|
-
export function registerSlippageTrendsTool(
|
|
27
|
-
server: McpServer,
|
|
28
|
-
baseDir: string
|
|
29
|
-
): void {
|
|
26
|
+
export function registerSlippageTrendsTool(server: McpServer, baseDir: string): void {
|
|
30
27
|
server.registerTool(
|
|
31
28
|
"analyze_slippage_trends",
|
|
32
29
|
{
|
|
@@ -34,10 +31,7 @@ export function registerSlippageTrendsTool(
|
|
|
34
31
|
"Analyze slippage trends over time with statistical significance testing. Detects improvement/degradation patterns using linear regression on time-aggregated slippage data. Provides slope, R-squared, and p-value. Requires both tradelog.csv (backtest) and reportinglog.csv (actual). Limitation: Trade matching uses minute precision; if multiple trades share the same date+strategy+minute, matching is order-dependent.",
|
|
35
32
|
inputSchema: z.object({
|
|
36
33
|
blockId: z.string().describe("Block folder name"),
|
|
37
|
-
strategy: z
|
|
38
|
-
.string()
|
|
39
|
-
.optional()
|
|
40
|
-
.describe("Filter to specific strategy name"),
|
|
34
|
+
strategy: z.string().optional().describe("Filter to specific strategy name"),
|
|
41
35
|
dateRange: z
|
|
42
36
|
.object({
|
|
43
37
|
from: z.string().optional().describe("Start date YYYY-MM-DD"),
|
|
@@ -56,9 +50,7 @@ export function registerSlippageTrendsTool(
|
|
|
56
50
|
includeTimeSeries: z
|
|
57
51
|
.boolean()
|
|
58
52
|
.default(false)
|
|
59
|
-
.describe(
|
|
60
|
-
"Include raw time series data points in output (for charting)"
|
|
61
|
-
),
|
|
53
|
+
.describe("Include raw time series data points in output (for charting)"),
|
|
62
54
|
correlationMethod: z
|
|
63
55
|
.enum(["pearson", "kendall"])
|
|
64
56
|
.default("pearson")
|
|
@@ -84,376 +76,354 @@ export function registerSlippageTrendsTool(
|
|
|
84
76
|
}) => {
|
|
85
77
|
try {
|
|
86
78
|
const block = await loadBlock(baseDir, blockId);
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
// Apply filters
|
|
106
|
-
backtestTrades = applyStrategyFilter(backtestTrades, strategy);
|
|
107
|
-
actualTrades = applyStrategyFilter(actualTrades, strategy);
|
|
108
|
-
backtestTrades = applyDateRangeFilter(backtestTrades, dateRange);
|
|
109
|
-
actualTrades = applyDateRangeFilter(actualTrades, dateRange);
|
|
110
|
-
|
|
111
|
-
if (backtestTrades.length === 0) {
|
|
112
|
-
return {
|
|
113
|
-
content: [
|
|
114
|
-
{
|
|
115
|
-
type: "text",
|
|
116
|
-
text: "No backtest trades found in tradelog.csv matching filters.",
|
|
117
|
-
},
|
|
118
|
-
],
|
|
119
|
-
isError: true,
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if (actualTrades.length === 0) {
|
|
124
|
-
return {
|
|
125
|
-
content: [
|
|
126
|
-
{
|
|
127
|
-
type: "text",
|
|
128
|
-
text: "No actual trades found in reportinglog.csv matching filters.",
|
|
129
|
-
},
|
|
130
|
-
],
|
|
131
|
-
isError: true,
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Match trades
|
|
136
|
-
const { matchedTrades } = matchTrades(
|
|
137
|
-
backtestTrades,
|
|
138
|
-
actualTrades,
|
|
139
|
-
scaling
|
|
140
|
-
);
|
|
141
|
-
|
|
142
|
-
if (matchedTrades.length === 0) {
|
|
143
|
-
return {
|
|
144
|
-
content: [
|
|
145
|
-
{
|
|
146
|
-
type: "text",
|
|
147
|
-
text: "No matching trades found between backtest and actual data. Cannot perform trend analysis.",
|
|
148
|
-
},
|
|
149
|
-
],
|
|
150
|
-
isError: true,
|
|
151
|
-
};
|
|
152
|
-
}
|
|
79
|
+
let backtestTrades = block.trades;
|
|
80
|
+
|
|
81
|
+
// Load reporting log (actual trades)
|
|
82
|
+
let actualTrades: ReportingTrade[];
|
|
83
|
+
try {
|
|
84
|
+
actualTrades = await loadReportingLog(baseDir, blockId);
|
|
85
|
+
} catch {
|
|
86
|
+
return {
|
|
87
|
+
content: [
|
|
88
|
+
{
|
|
89
|
+
type: "text",
|
|
90
|
+
text: `No reportinglog.csv found in block "${blockId}". This tool requires both tradelog.csv (backtest) and reportinglog.csv (actual).`,
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
isError: true,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
153
96
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
// Period slippage interface
|
|
162
|
-
interface PeriodSlippage {
|
|
163
|
-
period: string;
|
|
164
|
-
totalSlippage: number;
|
|
165
|
-
avgSlippage: number;
|
|
166
|
-
tradeCount: number;
|
|
167
|
-
avgMagnitude: number;
|
|
168
|
-
}
|
|
97
|
+
// Apply filters
|
|
98
|
+
backtestTrades = applyStrategyFilter(backtestTrades, strategy);
|
|
99
|
+
actualTrades = applyStrategyFilter(actualTrades, strategy);
|
|
100
|
+
backtestTrades = applyDateRangeFilter(backtestTrades, dateRange);
|
|
101
|
+
actualTrades = applyDateRangeFilter(actualTrades, dateRange);
|
|
169
102
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
for (const trade of trades) {
|
|
180
|
-
const periodKey = getPeriodKey(trade.date);
|
|
181
|
-
const existing = periodMap.get(periodKey) || {
|
|
182
|
-
slippages: [],
|
|
183
|
-
count: 0,
|
|
103
|
+
if (backtestTrades.length === 0) {
|
|
104
|
+
return {
|
|
105
|
+
content: [
|
|
106
|
+
{
|
|
107
|
+
type: "text",
|
|
108
|
+
text: "No backtest trades found in tradelog.csv matching filters.",
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
isError: true,
|
|
184
112
|
};
|
|
185
|
-
existing.slippages.push(trade.totalSlippage);
|
|
186
|
-
existing.count++;
|
|
187
|
-
periodMap.set(periodKey, existing);
|
|
188
113
|
}
|
|
189
114
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
totalSlippage,
|
|
201
|
-
avgSlippage,
|
|
202
|
-
tradeCount: data.count,
|
|
203
|
-
avgMagnitude,
|
|
204
|
-
});
|
|
115
|
+
if (actualTrades.length === 0) {
|
|
116
|
+
return {
|
|
117
|
+
content: [
|
|
118
|
+
{
|
|
119
|
+
type: "text",
|
|
120
|
+
text: "No actual trades found in reportinglog.csv matching filters.",
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
isError: true,
|
|
124
|
+
};
|
|
205
125
|
}
|
|
206
126
|
|
|
207
|
-
//
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
return periods;
|
|
211
|
-
};
|
|
127
|
+
// Match trades
|
|
128
|
+
const { matchedTrades } = matchTrades(backtestTrades, actualTrades, scaling);
|
|
212
129
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
130
|
+
if (matchedTrades.length === 0) {
|
|
131
|
+
return {
|
|
132
|
+
content: [
|
|
133
|
+
{
|
|
134
|
+
type: "text",
|
|
135
|
+
text: "No matching trades found between backtest and actual data. Cannot perform trend analysis.",
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
isError: true,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
221
141
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
142
|
+
// Helper to get period key based on granularity
|
|
143
|
+
const getPeriodKey = (dateStr: string): string => {
|
|
144
|
+
if (granularity === "daily") return dateStr;
|
|
145
|
+
if (granularity === "weekly") return getIsoWeekKey(dateStr);
|
|
146
|
+
return getMonthKey(dateStr);
|
|
147
|
+
};
|
|
226
148
|
|
|
227
|
-
//
|
|
228
|
-
|
|
149
|
+
// Period slippage interface
|
|
150
|
+
interface PeriodSlippage {
|
|
151
|
+
period: string;
|
|
152
|
+
totalSlippage: number;
|
|
153
|
+
avgSlippage: number;
|
|
154
|
+
tradeCount: number;
|
|
155
|
+
avgMagnitude: number;
|
|
156
|
+
}
|
|
229
157
|
|
|
230
|
-
//
|
|
231
|
-
const
|
|
232
|
-
|
|
158
|
+
// Aggregate matched trades by period
|
|
159
|
+
const aggregateByPeriod = (trades: MatchedTradeData[]): PeriodSlippage[] => {
|
|
160
|
+
const periodMap = new Map<string, { slippages: number[]; count: number }>();
|
|
161
|
+
|
|
162
|
+
for (const trade of trades) {
|
|
163
|
+
const periodKey = getPeriodKey(trade.date);
|
|
164
|
+
const existing = periodMap.get(periodKey) || {
|
|
165
|
+
slippages: [],
|
|
166
|
+
count: 0,
|
|
167
|
+
};
|
|
168
|
+
existing.slippages.push(trade.totalSlippage);
|
|
169
|
+
existing.count++;
|
|
170
|
+
periodMap.set(periodKey, existing);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const periods: PeriodSlippage[] = [];
|
|
174
|
+
for (const [period, data] of periodMap) {
|
|
175
|
+
const totalSlippage = data.slippages.reduce((sum, s) => sum + s, 0);
|
|
176
|
+
const avgSlippage = totalSlippage / data.count;
|
|
177
|
+
const avgMagnitude =
|
|
178
|
+
data.slippages.reduce((sum, s) => sum + Math.abs(s), 0) / data.count;
|
|
179
|
+
|
|
180
|
+
periods.push({
|
|
181
|
+
period,
|
|
182
|
+
totalSlippage,
|
|
183
|
+
avgSlippage,
|
|
184
|
+
tradeCount: data.count,
|
|
185
|
+
avgMagnitude,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Sort by period chronologically
|
|
190
|
+
periods.sort((a, b) => a.period.localeCompare(b.period));
|
|
191
|
+
|
|
192
|
+
return periods;
|
|
193
|
+
};
|
|
233
194
|
|
|
234
|
-
//
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
195
|
+
// Trend result interface
|
|
196
|
+
interface TrendResult {
|
|
197
|
+
slope: number;
|
|
198
|
+
intercept: number;
|
|
199
|
+
rSquared: number;
|
|
200
|
+
pValue: number;
|
|
201
|
+
stderr: number;
|
|
240
202
|
}
|
|
241
|
-
const slope = sumX2 > 0 ? sumXY / sumX2 : 0;
|
|
242
|
-
const intercept = meanY - slope * meanX;
|
|
243
|
-
|
|
244
|
-
// R-squared = 1 - SSres/SStot
|
|
245
|
-
const predicted = x.map((xi) => slope * xi + intercept);
|
|
246
|
-
const ssRes = y.reduce(
|
|
247
|
-
(sum, yi, i) => sum + (yi - predicted[i]) ** 2,
|
|
248
|
-
0
|
|
249
|
-
);
|
|
250
|
-
const ssTot = y.reduce((sum, yi) => sum + (yi - meanY) ** 2, 0);
|
|
251
|
-
const rSquared = ssTot > 0 ? 1 - ssRes / ssTot : 0;
|
|
252
203
|
|
|
253
|
-
//
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
204
|
+
// Linear regression with statistics
|
|
205
|
+
const linearRegression = (y: number[]): TrendResult | null => {
|
|
206
|
+
const n = y.length;
|
|
207
|
+
if (n < 2) return null;
|
|
208
|
+
|
|
209
|
+
// X values are period indices (0, 1, 2, ...)
|
|
210
|
+
const x = y.map((_, i) => i);
|
|
211
|
+
|
|
212
|
+
// Calculate means
|
|
213
|
+
const meanX = x.reduce((a, b) => a + b, 0) / n;
|
|
214
|
+
const meanY = y.reduce((a, b) => a + b, 0) / n;
|
|
215
|
+
|
|
216
|
+
// OLS: slope = sum((xi-meanX)(yi-meanY)) / sum((xi-meanX)^2)
|
|
217
|
+
let sumXY = 0;
|
|
218
|
+
let sumX2 = 0;
|
|
219
|
+
for (let i = 0; i < n; i++) {
|
|
220
|
+
sumXY += (x[i] - meanX) * (y[i] - meanY);
|
|
221
|
+
sumX2 += (x[i] - meanX) ** 2;
|
|
222
|
+
}
|
|
223
|
+
const slope = sumX2 > 0 ? sumXY / sumX2 : 0;
|
|
224
|
+
const intercept = meanY - slope * meanX;
|
|
225
|
+
|
|
226
|
+
// R-squared = 1 - SSres/SStot
|
|
227
|
+
const predicted = x.map((xi) => slope * xi + intercept);
|
|
228
|
+
const ssRes = y.reduce((sum, yi, i) => sum + (yi - predicted[i]) ** 2, 0);
|
|
229
|
+
const ssTot = y.reduce((sum, yi) => sum + (yi - meanY) ** 2, 0);
|
|
230
|
+
const rSquared = ssTot > 0 ? 1 - ssRes / ssTot : 0;
|
|
231
|
+
|
|
232
|
+
// Standard error and t-statistic for p-value
|
|
233
|
+
const mse = n > 2 ? ssRes / (n - 2) : 0;
|
|
234
|
+
const stderr = sumX2 > 0 ? Math.sqrt(mse / sumX2) : 0;
|
|
235
|
+
const tStat = stderr > 0 ? slope / stderr : 0;
|
|
236
|
+
|
|
237
|
+
// Two-tailed p-value using normal approximation
|
|
238
|
+
const pValue = 2 * (1 - normalCDF(Math.abs(tStat)));
|
|
239
|
+
|
|
240
|
+
return {
|
|
241
|
+
slope: Math.round(slope * 10000) / 10000,
|
|
242
|
+
intercept: Math.round(intercept * 100) / 100,
|
|
243
|
+
rSquared: Math.round(rSquared * 10000) / 10000,
|
|
244
|
+
pValue: Math.round(pValue * 10000) / 10000,
|
|
245
|
+
stderr: Math.round(stderr * 10000) / 10000,
|
|
246
|
+
};
|
|
247
|
+
};
|
|
257
248
|
|
|
258
|
-
//
|
|
259
|
-
const
|
|
249
|
+
// Aggregate all matched trades by period
|
|
250
|
+
const periodSlippages = aggregateByPeriod(matchedTrades);
|
|
260
251
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
stderr: Math.round(stderr * 10000) / 10000,
|
|
252
|
+
// Calculate date range from matched trades
|
|
253
|
+
const dates = matchedTrades.map((t) => t.date).sort();
|
|
254
|
+
const dateRangeResult = {
|
|
255
|
+
from: dates[0],
|
|
256
|
+
to: dates[dates.length - 1],
|
|
267
257
|
};
|
|
268
|
-
};
|
|
269
|
-
|
|
270
|
-
// Aggregate all matched trades by period
|
|
271
|
-
const periodSlippages = aggregateByPeriod(matchedTrades);
|
|
272
|
-
|
|
273
|
-
// Calculate date range from matched trades
|
|
274
|
-
const dates = matchedTrades.map((t) => t.date).sort();
|
|
275
|
-
const dateRangeResult = {
|
|
276
|
-
from: dates[0],
|
|
277
|
-
to: dates[dates.length - 1],
|
|
278
|
-
};
|
|
279
|
-
|
|
280
|
-
// Calculate summary statistics
|
|
281
|
-
const totalSlippage = matchedTrades.reduce(
|
|
282
|
-
(sum, t) => sum + t.totalSlippage,
|
|
283
|
-
0
|
|
284
|
-
);
|
|
285
|
-
const avgSlippagePerTrade = totalSlippage / matchedTrades.length;
|
|
286
|
-
const avgSlippagePerPeriod =
|
|
287
|
-
periodSlippages.length > 0
|
|
288
|
-
? periodSlippages.reduce((sum, p) => sum + p.totalSlippage, 0) /
|
|
289
|
-
periodSlippages.length
|
|
290
|
-
: 0;
|
|
291
|
-
|
|
292
|
-
// Calculate block-level trend (only if enough samples)
|
|
293
|
-
const periodAvgSlippages = periodSlippages.map((p) => p.avgSlippage);
|
|
294
|
-
const blockTrend =
|
|
295
|
-
matchedTrades.length >= minSamples
|
|
296
|
-
? linearRegression(periodAvgSlippages)
|
|
297
|
-
: null;
|
|
298
|
-
|
|
299
|
-
// Per-strategy breakdown
|
|
300
|
-
const byStrategy = new Map<string, MatchedTradeData[]>();
|
|
301
|
-
for (const trade of matchedTrades) {
|
|
302
|
-
const existing = byStrategy.get(trade.strategy) ?? [];
|
|
303
|
-
existing.push(trade);
|
|
304
|
-
byStrategy.set(trade.strategy, existing);
|
|
305
|
-
}
|
|
306
258
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
matchedTrades
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
259
|
+
// Calculate summary statistics
|
|
260
|
+
const totalSlippage = matchedTrades.reduce((sum, t) => sum + t.totalSlippage, 0);
|
|
261
|
+
const avgSlippagePerTrade = totalSlippage / matchedTrades.length;
|
|
262
|
+
const avgSlippagePerPeriod =
|
|
263
|
+
periodSlippages.length > 0
|
|
264
|
+
? periodSlippages.reduce((sum, p) => sum + p.totalSlippage, 0) /
|
|
265
|
+
periodSlippages.length
|
|
266
|
+
: 0;
|
|
267
|
+
|
|
268
|
+
// Calculate block-level trend (only if enough samples)
|
|
269
|
+
const periodAvgSlippages = periodSlippages.map((p) => p.avgSlippage);
|
|
270
|
+
const blockTrend =
|
|
271
|
+
matchedTrades.length >= minSamples ? linearRegression(periodAvgSlippages) : null;
|
|
272
|
+
|
|
273
|
+
// Per-strategy breakdown
|
|
274
|
+
const byStrategy = new Map<string, MatchedTradeData[]>();
|
|
275
|
+
for (const trade of matchedTrades) {
|
|
276
|
+
const existing = byStrategy.get(trade.strategy) ?? [];
|
|
277
|
+
existing.push(trade);
|
|
278
|
+
byStrategy.set(trade.strategy, existing);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const perStrategy: Array<{
|
|
282
|
+
strategy: string;
|
|
283
|
+
matchedTrades: number;
|
|
284
|
+
periodsAnalyzed: number;
|
|
285
|
+
totalSlippage: number;
|
|
286
|
+
trend: TrendResult | null;
|
|
287
|
+
}> = [];
|
|
288
|
+
|
|
289
|
+
for (const [strategyName, trades] of byStrategy) {
|
|
290
|
+
if (trades.length < minSamples) {
|
|
291
|
+
perStrategy.push({
|
|
292
|
+
strategy: strategyName,
|
|
293
|
+
matchedTrades: trades.length,
|
|
294
|
+
periodsAnalyzed: 0,
|
|
295
|
+
totalSlippage: trades.reduce((sum, t) => sum + t.totalSlippage, 0),
|
|
296
|
+
trend: null,
|
|
297
|
+
});
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const strategyPeriods = aggregateByPeriod(trades);
|
|
302
|
+
const strategyTrend =
|
|
303
|
+
strategyPeriods.length >= 2
|
|
304
|
+
? linearRegression(strategyPeriods.map((p) => p.avgSlippage))
|
|
305
|
+
: null;
|
|
314
306
|
|
|
315
|
-
for (const [strategyName, trades] of byStrategy) {
|
|
316
|
-
if (trades.length < minSamples) {
|
|
317
307
|
perStrategy.push({
|
|
318
308
|
strategy: strategyName,
|
|
319
309
|
matchedTrades: trades.length,
|
|
320
|
-
periodsAnalyzed:
|
|
310
|
+
periodsAnalyzed: strategyPeriods.length,
|
|
321
311
|
totalSlippage: trades.reduce((sum, t) => sum + t.totalSlippage, 0),
|
|
322
|
-
trend:
|
|
312
|
+
trend: strategyTrend,
|
|
323
313
|
});
|
|
324
|
-
continue;
|
|
325
314
|
}
|
|
326
315
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
strategyPeriods.length >= 2
|
|
330
|
-
? linearRegression(strategyPeriods.map((p) => p.avgSlippage))
|
|
331
|
-
: null;
|
|
332
|
-
|
|
333
|
-
perStrategy.push({
|
|
334
|
-
strategy: strategyName,
|
|
335
|
-
matchedTrades: trades.length,
|
|
336
|
-
periodsAnalyzed: strategyPeriods.length,
|
|
337
|
-
totalSlippage: trades.reduce((sum, t) => sum + t.totalSlippage, 0),
|
|
338
|
-
trend: strategyTrend,
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
// Sort by absolute total slippage descending
|
|
343
|
-
perStrategy.sort(
|
|
344
|
-
(a, b) => Math.abs(b.totalSlippage) - Math.abs(a.totalSlippage)
|
|
345
|
-
);
|
|
346
|
-
|
|
347
|
-
// External factor correlation (VIX)
|
|
348
|
-
interface ExternalFactorResult {
|
|
349
|
-
factor: string;
|
|
350
|
-
coefficient: number;
|
|
351
|
-
sampleSize: number;
|
|
352
|
-
}
|
|
316
|
+
// Sort by absolute total slippage descending
|
|
317
|
+
perStrategy.sort((a, b) => Math.abs(b.totalSlippage) - Math.abs(a.totalSlippage));
|
|
353
318
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
);
|
|
319
|
+
// External factor correlation (VIX)
|
|
320
|
+
interface ExternalFactorResult {
|
|
321
|
+
factor: string;
|
|
322
|
+
coefficient: number;
|
|
323
|
+
sampleSize: number;
|
|
324
|
+
}
|
|
361
325
|
|
|
362
|
-
|
|
363
|
-
const vixValues = vixTrades.map((t) => t.openingVix!);
|
|
364
|
-
const slippageValues = vixTrades.map((t) => t.totalSlippage);
|
|
326
|
+
let externalFactors: { method: string; results: ExternalFactorResult[] } | undefined;
|
|
365
327
|
|
|
366
|
-
const
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
: kendallTau(slippageValues, vixValues);
|
|
328
|
+
const vixTrades = matchedTrades.filter(
|
|
329
|
+
(t) => t.openingVix !== undefined && t.openingVix !== null,
|
|
330
|
+
);
|
|
370
331
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
332
|
+
if (vixTrades.length >= minSamples) {
|
|
333
|
+
const vixValues = vixTrades.map((t) => t.openingVix!);
|
|
334
|
+
const slippageValues = vixTrades.map((t) => t.totalSlippage);
|
|
335
|
+
|
|
336
|
+
const coefficient =
|
|
337
|
+
correlationMethod === "pearson"
|
|
338
|
+
? pearsonCorrelation(slippageValues, vixValues)
|
|
339
|
+
: kendallTau(slippageValues, vixValues);
|
|
340
|
+
|
|
341
|
+
// Only include if meaningful (|r| >= 0.1)
|
|
342
|
+
if (Math.abs(coefficient) >= 0.1) {
|
|
343
|
+
externalFactors = {
|
|
344
|
+
method: correlationMethod,
|
|
345
|
+
results: [
|
|
346
|
+
{
|
|
347
|
+
factor: "openingVix",
|
|
348
|
+
coefficient: Math.round(coefficient * 10000) / 10000,
|
|
349
|
+
sampleSize: vixTrades.length,
|
|
350
|
+
},
|
|
351
|
+
],
|
|
352
|
+
};
|
|
353
|
+
}
|
|
383
354
|
}
|
|
384
|
-
}
|
|
385
355
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
356
|
+
// Build summary text
|
|
357
|
+
const summaryParts = [
|
|
358
|
+
`Slippage trends (${granularity}): ${periodSlippages.length} periods, ${matchedTrades.length} trades`,
|
|
359
|
+
`Total: ${formatCurrency(totalSlippage)}`,
|
|
360
|
+
];
|
|
391
361
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
362
|
+
if (blockTrend) {
|
|
363
|
+
summaryParts.push(
|
|
364
|
+
`Trend: slope=${blockTrend.slope} (p=${blockTrend.pValue.toFixed(3)})`,
|
|
365
|
+
);
|
|
366
|
+
}
|
|
397
367
|
|
|
398
|
-
|
|
368
|
+
const summary = summaryParts.join(" | ");
|
|
399
369
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
370
|
+
// Build structured output
|
|
371
|
+
const structuredData: {
|
|
372
|
+
blockId: string;
|
|
373
|
+
filters: {
|
|
374
|
+
strategy: string | null;
|
|
375
|
+
dateRange: { from?: string; to?: string } | null;
|
|
376
|
+
};
|
|
377
|
+
scaling: string;
|
|
378
|
+
granularity: string;
|
|
379
|
+
dateRange: { from: string; to: string };
|
|
380
|
+
summary: {
|
|
381
|
+
matchedTrades: number;
|
|
382
|
+
periodsAnalyzed: number;
|
|
383
|
+
totalSlippage: number;
|
|
384
|
+
avgSlippagePerTrade: number;
|
|
385
|
+
avgSlippagePerPeriod: number;
|
|
386
|
+
};
|
|
387
|
+
trend: TrendResult | null;
|
|
388
|
+
timeSeries?: PeriodSlippage[];
|
|
389
|
+
perStrategy: typeof perStrategy;
|
|
390
|
+
externalFactors?: typeof externalFactors;
|
|
391
|
+
} = {
|
|
392
|
+
blockId,
|
|
393
|
+
filters: {
|
|
394
|
+
strategy: strategy ?? null,
|
|
395
|
+
dateRange: dateRange ?? null,
|
|
396
|
+
},
|
|
397
|
+
scaling,
|
|
398
|
+
granularity,
|
|
399
|
+
dateRange: dateRangeResult,
|
|
400
|
+
summary: {
|
|
401
|
+
matchedTrades: matchedTrades.length,
|
|
402
|
+
periodsAnalyzed: periodSlippages.length,
|
|
403
|
+
totalSlippage: Math.round(totalSlippage * 100) / 100,
|
|
404
|
+
avgSlippagePerTrade: Math.round(avgSlippagePerTrade * 100) / 100,
|
|
405
|
+
avgSlippagePerPeriod: Math.round(avgSlippagePerPeriod * 100) / 100,
|
|
406
|
+
},
|
|
407
|
+
trend: blockTrend,
|
|
408
|
+
perStrategy,
|
|
416
409
|
};
|
|
417
|
-
trend: TrendResult | null;
|
|
418
|
-
timeSeries?: PeriodSlippage[];
|
|
419
|
-
perStrategy: typeof perStrategy;
|
|
420
|
-
externalFactors?: typeof externalFactors;
|
|
421
|
-
} = {
|
|
422
|
-
blockId,
|
|
423
|
-
filters: {
|
|
424
|
-
strategy: strategy ?? null,
|
|
425
|
-
dateRange: dateRange ?? null,
|
|
426
|
-
},
|
|
427
|
-
scaling,
|
|
428
|
-
granularity,
|
|
429
|
-
dateRange: dateRangeResult,
|
|
430
|
-
summary: {
|
|
431
|
-
matchedTrades: matchedTrades.length,
|
|
432
|
-
periodsAnalyzed: periodSlippages.length,
|
|
433
|
-
totalSlippage: Math.round(totalSlippage * 100) / 100,
|
|
434
|
-
avgSlippagePerTrade: Math.round(avgSlippagePerTrade * 100) / 100,
|
|
435
|
-
avgSlippagePerPeriod: Math.round(avgSlippagePerPeriod * 100) / 100,
|
|
436
|
-
},
|
|
437
|
-
trend: blockTrend,
|
|
438
|
-
perStrategy,
|
|
439
|
-
};
|
|
440
|
-
|
|
441
|
-
// Add optional time series data
|
|
442
|
-
if (includeTimeSeries) {
|
|
443
|
-
structuredData.timeSeries = periodSlippages.map((p) => ({
|
|
444
|
-
...p,
|
|
445
|
-
totalSlippage: Math.round(p.totalSlippage * 100) / 100,
|
|
446
|
-
avgSlippage: Math.round(p.avgSlippage * 100) / 100,
|
|
447
|
-
avgMagnitude: Math.round(p.avgMagnitude * 100) / 100,
|
|
448
|
-
}));
|
|
449
|
-
}
|
|
450
410
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
411
|
+
// Add optional time series data
|
|
412
|
+
if (includeTimeSeries) {
|
|
413
|
+
structuredData.timeSeries = periodSlippages.map((p) => ({
|
|
414
|
+
...p,
|
|
415
|
+
totalSlippage: Math.round(p.totalSlippage * 100) / 100,
|
|
416
|
+
avgSlippage: Math.round(p.avgSlippage * 100) / 100,
|
|
417
|
+
avgMagnitude: Math.round(p.avgMagnitude * 100) / 100,
|
|
418
|
+
}));
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Add external factors if available
|
|
422
|
+
if (externalFactors) {
|
|
423
|
+
structuredData.externalFactors = externalFactors;
|
|
424
|
+
}
|
|
455
425
|
|
|
456
|
-
|
|
426
|
+
return createToolOutput(summary, structuredData);
|
|
457
427
|
} catch (error) {
|
|
458
428
|
return {
|
|
459
429
|
content: [
|
|
@@ -465,7 +435,7 @@ export function registerSlippageTrendsTool(
|
|
|
465
435
|
isError: true,
|
|
466
436
|
};
|
|
467
437
|
}
|
|
468
|
-
}
|
|
469
|
-
)
|
|
438
|
+
},
|
|
439
|
+
),
|
|
470
440
|
);
|
|
471
441
|
}
|