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
|
@@ -61,14 +61,14 @@ export function registerMarketEnrichmentTools(
|
|
|
61
61
|
ticker: z
|
|
62
62
|
.string()
|
|
63
63
|
.describe(
|
|
64
|
-
"Ticker symbol to enrich (e.g., 'SPX', 'QQQ'). Must match an existing ticker in market.spot_daily / market.enriched."
|
|
64
|
+
"Ticker symbol to enrich (e.g., 'SPX', 'QQQ'). Must match an existing ticker in market.spot_daily / market.enriched.",
|
|
65
65
|
),
|
|
66
66
|
force_full: z
|
|
67
67
|
.boolean()
|
|
68
68
|
.default(false)
|
|
69
69
|
.describe(
|
|
70
70
|
"Currently a no-op against the store-backed compute path. " +
|
|
71
|
-
|
|
71
|
+
"Originally cleared the enriched_through watermark and recomputed all rows from scratch.",
|
|
72
72
|
),
|
|
73
73
|
}),
|
|
74
74
|
},
|
|
@@ -115,6 +115,6 @@ export function registerMarketEnrichmentTools(
|
|
|
115
115
|
} finally {
|
|
116
116
|
await downgradeToReadOnly(baseDir);
|
|
117
117
|
}
|
|
118
|
-
}
|
|
118
|
+
},
|
|
119
119
|
);
|
|
120
120
|
}
|
|
@@ -29,13 +29,21 @@ export function registerMarketFetchTools(
|
|
|
29
29
|
tickers: z.array(z.string()).min(1).describe("Ticker symbols (e.g., ['SPX','VIX'])"),
|
|
30
30
|
from: z.string().describe("Start date YYYY-MM-DD"),
|
|
31
31
|
to: z.string().describe("End date YYYY-MM-DD"),
|
|
32
|
-
timespan: z
|
|
32
|
+
timespan: z
|
|
33
|
+
.enum(["1d", "1m", "5m", "15m", "1h"])
|
|
34
|
+
.default("1d")
|
|
33
35
|
.describe("Bar granularity. '1d' = daily; others = intraday."),
|
|
34
|
-
provider: z
|
|
36
|
+
provider: z
|
|
37
|
+
.enum(["massive", "thetadata"])
|
|
38
|
+
.optional()
|
|
35
39
|
.describe("Optional per-call provider override. Defaults to env MARKET_DATA_PROVIDER."),
|
|
36
|
-
skip_enrichment: z
|
|
40
|
+
skip_enrichment: z
|
|
41
|
+
.boolean()
|
|
42
|
+
.default(false)
|
|
37
43
|
.describe("Skip per-ticker enrichment (RSI/ATR/etc.) after write."),
|
|
38
|
-
dry_run: z
|
|
44
|
+
dry_run: z
|
|
45
|
+
.boolean()
|
|
46
|
+
.default(false)
|
|
39
47
|
.describe("Validate inputs and preview without writing."),
|
|
40
48
|
}),
|
|
41
49
|
},
|
|
@@ -73,10 +81,20 @@ export function registerMarketFetchTools(
|
|
|
73
81
|
"Massive falls back automatically on Developer/lower plans; ThetaData (both modes) returns true NBBO. " +
|
|
74
82
|
"Returns status='unsupported' with a clear error only when the active provider lacks the requested mode (e.g., bulk-by-underlying on Massive).",
|
|
75
83
|
inputSchema: z.object({
|
|
76
|
-
tickers: z
|
|
77
|
-
.
|
|
78
|
-
|
|
79
|
-
.
|
|
84
|
+
tickers: z
|
|
85
|
+
.array(z.string())
|
|
86
|
+
.min(1)
|
|
87
|
+
.optional()
|
|
88
|
+
.describe(
|
|
89
|
+
"Option tickers in OCC format (e.g., 'SPXW260321C05800000'). No O: prefix. Mutually exclusive with 'underlyings'.",
|
|
90
|
+
),
|
|
91
|
+
underlyings: z
|
|
92
|
+
.array(z.string())
|
|
93
|
+
.min(1)
|
|
94
|
+
.optional()
|
|
95
|
+
.describe(
|
|
96
|
+
"Underlying symbols (e.g. ['SPX']). Bulk mode — returns every contract's minute quotes per date. Mutually exclusive with 'tickers'.",
|
|
97
|
+
),
|
|
80
98
|
from: z.string().describe("Start date YYYY-MM-DD"),
|
|
81
99
|
to: z.string().describe("End date YYYY-MM-DD"),
|
|
82
100
|
provider: z.enum(["massive", "thetadata"]).optional(),
|
|
@@ -97,13 +115,14 @@ export function registerMarketFetchTools(
|
|
|
97
115
|
let progress = 0;
|
|
98
116
|
onProgress = async (event) => {
|
|
99
117
|
progress++;
|
|
100
|
-
const message =
|
|
101
|
-
|
|
102
|
-
`${event.
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
118
|
+
const message =
|
|
119
|
+
event.kind === "group"
|
|
120
|
+
? `${event.underlying} ${event.root}/${event.right} ${event.date} ` +
|
|
121
|
+
`${event.phase === "checkpoint" ? "checkpoint" : "done"} (${event.status})` +
|
|
122
|
+
(event.totalContracts != null
|
|
123
|
+
? ` ${event.completedContracts ?? 0}/${event.totalContracts} contracts`
|
|
124
|
+
: "")
|
|
125
|
+
: `${event.underlying} ${event.date} flushed — ${event.rowsWritten} rows`;
|
|
107
126
|
try {
|
|
108
127
|
await extra.sendNotification({
|
|
109
128
|
method: "notifications/progress",
|
|
@@ -116,9 +135,17 @@ export function registerMarketFetchTools(
|
|
|
116
135
|
}
|
|
117
136
|
|
|
118
137
|
const result = await ingestor.ingestQuotes({
|
|
119
|
-
tickers,
|
|
138
|
+
tickers,
|
|
139
|
+
underlyings,
|
|
140
|
+
from,
|
|
141
|
+
to,
|
|
142
|
+
provider,
|
|
143
|
+
dryRun: dry_run,
|
|
144
|
+
onProgress,
|
|
120
145
|
});
|
|
121
|
-
const mode = underlyings
|
|
146
|
+
const mode = underlyings
|
|
147
|
+
? `bulk (${underlyings.join(",")})`
|
|
148
|
+
: `per-ticker (${tickers?.length ?? 0})`;
|
|
122
149
|
// `partial` means some batches were logged-and-skipped. The structured
|
|
123
150
|
// `skipped[]` array on the result body carries the details; we surface
|
|
124
151
|
// the count in the summary so console callers see it.
|
|
@@ -127,10 +154,13 @@ export function registerMarketFetchTools(
|
|
|
127
154
|
? ` — ${result.skipped.length} batch(es) skipped (see result.skipped[])`
|
|
128
155
|
: "";
|
|
129
156
|
const summary =
|
|
130
|
-
result.status === "unsupported"
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
157
|
+
result.status === "unsupported"
|
|
158
|
+
? `Unsupported: ${result.error}`
|
|
159
|
+
: result.status === "error"
|
|
160
|
+
? `Error: ${result.error}`
|
|
161
|
+
: dry_run
|
|
162
|
+
? `[DRY RUN] Would fetch quotes: ${mode}`
|
|
163
|
+
: `${result.status}: wrote ${result.rowsWritten} quote rows (${mode})${partialSuffix}`;
|
|
134
164
|
return createToolOutput(summary, result);
|
|
135
165
|
} finally {
|
|
136
166
|
await downgradeToReadOnly(baseDir);
|
|
@@ -147,7 +177,10 @@ export function registerMarketFetchTools(
|
|
|
147
177
|
"Writes contract metadata to market.option_chain. Capability-gated on provider.fetchContractList. " +
|
|
148
178
|
"Use for pre-populating the contract universe before running backtests or enriching quotes.",
|
|
149
179
|
inputSchema: z.object({
|
|
150
|
-
underlyings: z
|
|
180
|
+
underlyings: z
|
|
181
|
+
.array(z.string())
|
|
182
|
+
.min(1)
|
|
183
|
+
.describe("Underlying tickers (e.g., ['SPX','QQQ'])"),
|
|
151
184
|
from: z.string().describe("Start date YYYY-MM-DD"),
|
|
152
185
|
to: z.string().describe("End date YYYY-MM-DD"),
|
|
153
186
|
provider: z.enum(["massive", "thetadata"]).optional(),
|
|
@@ -157,11 +190,19 @@ export function registerMarketFetchTools(
|
|
|
157
190
|
async ({ underlyings, from, to, provider, dry_run }) => {
|
|
158
191
|
await upgradeToReadWrite(baseDir);
|
|
159
192
|
try {
|
|
160
|
-
const result = await ingestor.ingestChain({
|
|
193
|
+
const result = await ingestor.ingestChain({
|
|
194
|
+
underlyings,
|
|
195
|
+
from,
|
|
196
|
+
to,
|
|
197
|
+
provider,
|
|
198
|
+
dryRun: dry_run,
|
|
199
|
+
});
|
|
161
200
|
const summary =
|
|
162
|
-
result.status === "unsupported"
|
|
163
|
-
|
|
164
|
-
|
|
201
|
+
result.status === "unsupported"
|
|
202
|
+
? `Unsupported: ${result.error}`
|
|
203
|
+
: dry_run
|
|
204
|
+
? `[DRY RUN] Would fetch chain for ${underlyings.join(", ")}`
|
|
205
|
+
: `${result.status}: wrote ${result.rowsWritten} contract rows`;
|
|
165
206
|
return createToolOutput(summary, result);
|
|
166
207
|
} finally {
|
|
167
208
|
await downgradeToReadOnly(baseDir);
|
|
@@ -186,23 +227,34 @@ export function registerMarketFetchTools(
|
|
|
186
227
|
" option_chain → market.option_chain — partition {underlying, date}\n\n" +
|
|
187
228
|
"The SELECT must output the target store's canonical columns in order. Writes are single-partition: every row must belong to the named partition. Works in both Parquet and DuckDB modes — the store handles mode routing.",
|
|
188
229
|
inputSchema: z.object({
|
|
189
|
-
file_path: z
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
230
|
+
file_path: z
|
|
231
|
+
.string()
|
|
232
|
+
.describe(
|
|
233
|
+
"Absolute path to a local file DuckDB can read. The SELECT references this path via read_parquet('<file_path>') / read_csv('<file_path>') / read_json('<file_path>'). Provider downloads (e.g., Massive rclone output) or any user-supplied file are all valid sources.",
|
|
234
|
+
),
|
|
235
|
+
dataset_type: z
|
|
236
|
+
.enum(["spot_bars", "option_quotes", "option_chain"])
|
|
237
|
+
.describe(
|
|
238
|
+
"Target store. Determines which writeFromSelect is invoked and which partition keys are required.",
|
|
239
|
+
),
|
|
240
|
+
select_sql: z
|
|
241
|
+
.string()
|
|
242
|
+
.describe(
|
|
243
|
+
"SELECT (or WITH ... SELECT) that produces the target store's canonical columns. " +
|
|
244
|
+
"spot_bars columns: (ticker, date, time, open, high, low, close, bid, ask). " +
|
|
245
|
+
"option_quote_minutes columns: (underlying, date, ticker, time, bid, ask, mid, last_updated_ns, source, delta, gamma, theta, vega, iv, greeks_source, greeks_revision, rate_type, rate_value, gamma_source); missing greeks/provenance columns are filled as null. " +
|
|
246
|
+
"option_chain columns: (underlying, date, ticker, contract_type, strike, expiration, dte, exercise_style).",
|
|
247
|
+
),
|
|
248
|
+
partition: z
|
|
249
|
+
.object({
|
|
250
|
+
ticker: z.string().optional().describe("Required for dataset_type='spot_bars'."),
|
|
251
|
+
underlying: z
|
|
252
|
+
.string()
|
|
253
|
+
.optional()
|
|
254
|
+
.describe("Required for dataset_type='option_quotes' | 'option_chain'."),
|
|
255
|
+
date: z.string().describe("Partition date YYYY-MM-DD."),
|
|
256
|
+
})
|
|
257
|
+
.describe("Single-partition target. Keys depend on dataset_type."),
|
|
206
258
|
dry_run: z.boolean().default(false),
|
|
207
259
|
}),
|
|
208
260
|
},
|
|
@@ -217,10 +269,13 @@ export function registerMarketFetchTools(
|
|
|
217
269
|
dryRun: dry_run,
|
|
218
270
|
});
|
|
219
271
|
const summary =
|
|
220
|
-
result.status === "error"
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
272
|
+
result.status === "error"
|
|
273
|
+
? `Error: ${result.error}`
|
|
274
|
+
: result.status === "unsupported"
|
|
275
|
+
? `Unsupported: ${result.error}`
|
|
276
|
+
: dry_run
|
|
277
|
+
? `[DRY RUN] Would write ${dataset_type} rows from ${file_path}`
|
|
278
|
+
: `${result.status}: wrote ${result.rowsWritten} rows to ${dataset_type}`;
|
|
224
279
|
return createToolOutput(summary, result);
|
|
225
280
|
} finally {
|
|
226
281
|
await downgradeToReadOnly(baseDir);
|
|
@@ -269,21 +324,44 @@ export function registerMarketFetchTools(
|
|
|
269
324
|
inputSchema: z.object({
|
|
270
325
|
asOf: z.string().describe("Target date YYYY-MM-DD. Typically yesterday."),
|
|
271
326
|
spot_tickers: z.array(z.string()).min(1).describe("Tickers to fetch daily bars for"),
|
|
272
|
-
chain_underlyings: z
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
"Underlyings to fetch
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
327
|
+
chain_underlyings: z
|
|
328
|
+
.array(z.string())
|
|
329
|
+
.optional()
|
|
330
|
+
.describe("Underlyings to fetch option chains for"),
|
|
331
|
+
quote_tickers: z
|
|
332
|
+
.array(z.string())
|
|
333
|
+
.optional()
|
|
334
|
+
.describe("Option tickers to fetch minute quotes for"),
|
|
335
|
+
quote_underlyings: z
|
|
336
|
+
.array(z.string())
|
|
337
|
+
.optional()
|
|
338
|
+
.describe(
|
|
339
|
+
"Underlyings to fetch minute quotes for via the bulk-by-underlying path (e.g., ['SPX']). " +
|
|
340
|
+
"Complementary with quote_tickers — pass whichever matches the target provider's capability. " +
|
|
341
|
+
"ThetaData supports bulk mode; Massive is per-ticker only. Both may be passed in one call — " +
|
|
342
|
+
"they dispatch as independent ingestQuotes invocations.",
|
|
343
|
+
),
|
|
344
|
+
compute_vix_context: z
|
|
345
|
+
.boolean()
|
|
346
|
+
.default(true)
|
|
347
|
+
.describe(
|
|
348
|
+
"If true and any VIX-family ticker is in spot_tickers, runs compute_vix_context at the end. Default true.",
|
|
349
|
+
),
|
|
283
350
|
provider: z.enum(["massive", "thetadata"]).optional(),
|
|
284
351
|
}),
|
|
285
352
|
},
|
|
286
|
-
async (
|
|
353
|
+
async (
|
|
354
|
+
{
|
|
355
|
+
asOf,
|
|
356
|
+
spot_tickers,
|
|
357
|
+
chain_underlyings,
|
|
358
|
+
quote_tickers,
|
|
359
|
+
quote_underlyings,
|
|
360
|
+
compute_vix_context,
|
|
361
|
+
provider,
|
|
362
|
+
},
|
|
363
|
+
extra,
|
|
364
|
+
) => {
|
|
287
365
|
await upgradeToReadWrite(baseDir);
|
|
288
366
|
try {
|
|
289
367
|
// Bulk-underlying quote fetches are the only step in this composite
|
|
@@ -300,13 +378,14 @@ export function registerMarketFetchTools(
|
|
|
300
378
|
let progress = 0;
|
|
301
379
|
onProgress = async (event) => {
|
|
302
380
|
progress++;
|
|
303
|
-
const message =
|
|
304
|
-
|
|
305
|
-
`${event.
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
381
|
+
const message =
|
|
382
|
+
event.kind === "group"
|
|
383
|
+
? `${event.underlying} ${event.root}/${event.right} ${event.date} ` +
|
|
384
|
+
`${event.phase === "checkpoint" ? "checkpoint" : "done"} (${event.status})` +
|
|
385
|
+
(event.totalContracts != null
|
|
386
|
+
? ` ${event.completedContracts ?? 0}/${event.totalContracts} contracts`
|
|
387
|
+
: "")
|
|
388
|
+
: `${event.underlying} ${event.date} flushed — ${event.rowsWritten} rows`;
|
|
310
389
|
try {
|
|
311
390
|
await extra.sendNotification({
|
|
312
391
|
method: "notifications/progress",
|
|
@@ -334,7 +413,9 @@ export function registerMarketFetchTools(
|
|
|
334
413
|
...result.perOperation.quotes,
|
|
335
414
|
...(result.perOperation.vixContext ? [result.perOperation.vixContext] : []),
|
|
336
415
|
];
|
|
337
|
-
const unsupportedCount = operationResults.filter(
|
|
416
|
+
const unsupportedCount = operationResults.filter(
|
|
417
|
+
(item) => item.status === "unsupported",
|
|
418
|
+
).length;
|
|
338
419
|
const skippedOpCount = operationResults.filter((item) => item.status === "skipped").length;
|
|
339
420
|
// `partial` surfaces enrichQuoteRows batch-skip details. The structured
|
|
340
421
|
// skipped[] array lives on the refresh result body; we surface the count
|
|
@@ -27,10 +27,7 @@ import * as os from "os";
|
|
|
27
27
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
28
28
|
import { getConnection, upgradeToReadWrite, downgradeToReadOnly } from "../db/connection.ts";
|
|
29
29
|
import { createToolOutput } from "../utils/output-formatter.ts";
|
|
30
|
-
import {
|
|
31
|
-
parseCsvToBars,
|
|
32
|
-
parseDatabaseRowsToBars,
|
|
33
|
-
} from "../utils/market-importer.ts";
|
|
30
|
+
import { parseCsvToBars, parseDatabaseRowsToBars } from "../utils/market-importer.ts";
|
|
34
31
|
import { normalizeTicker } from "../utils/ticker.ts";
|
|
35
32
|
import type { MarketStores } from "../market/stores/index.ts";
|
|
36
33
|
import type { BarRow } from "../market/stores/types.ts";
|
|
@@ -121,8 +118,8 @@ export function registerMarketImportTools(
|
|
|
121
118
|
.record(z.string(), z.string())
|
|
122
119
|
.describe(
|
|
123
120
|
"Maps CSV column names (keys) to schema column names (values). " +
|
|
124
|
-
|
|
125
|
-
|
|
121
|
+
"Required: date (or unix time), open, high, low, close. " +
|
|
122
|
+
"Time auto-extracted from the date column when it carries a Unix timestamp.",
|
|
126
123
|
),
|
|
127
124
|
dry_run: z
|
|
128
125
|
.boolean()
|
|
@@ -133,7 +130,7 @@ export function registerMarketImportTools(
|
|
|
133
130
|
.default(false)
|
|
134
131
|
.describe(
|
|
135
132
|
"If true, skips EnrichedStore.compute() (and computeContext for VIX-family) " +
|
|
136
|
-
|
|
133
|
+
"after the spot write. Re-run enrich_market_data later to populate derived fields.",
|
|
137
134
|
),
|
|
138
135
|
}),
|
|
139
136
|
},
|
|
@@ -246,7 +243,7 @@ export function registerMarketImportTools(
|
|
|
246
243
|
.string()
|
|
247
244
|
.describe(
|
|
248
245
|
"DuckDB SELECT query to execute against the external database. " +
|
|
249
|
-
|
|
246
|
+
"Must reference tables with the alias 'ext_import_source'.",
|
|
250
247
|
),
|
|
251
248
|
ticker: z
|
|
252
249
|
.string()
|
|
@@ -257,8 +254,8 @@ export function registerMarketImportTools(
|
|
|
257
254
|
.record(z.string(), z.string())
|
|
258
255
|
.describe(
|
|
259
256
|
"Maps query column names (keys) to schema column names (values). " +
|
|
260
|
-
|
|
261
|
-
|
|
257
|
+
"Required: date (or unix time), open, high, low, close. " +
|
|
258
|
+
"Time auto-extracted from the date column when it carries a Unix timestamp.",
|
|
262
259
|
),
|
|
263
260
|
dry_run: z
|
|
264
261
|
.boolean()
|
|
@@ -309,7 +306,11 @@ export function registerMarketImportTools(
|
|
|
309
306
|
} finally {
|
|
310
307
|
// Always DETACH regardless of success — non-fatal if the ATTACH never
|
|
311
308
|
// succeeded (e.g. invalid db_path) so the original error surfaces.
|
|
312
|
-
try {
|
|
309
|
+
try {
|
|
310
|
+
await conn.run(`DETACH ${EXT_ALIAS}`);
|
|
311
|
+
} catch {
|
|
312
|
+
/* best-effort */
|
|
313
|
+
}
|
|
313
314
|
}
|
|
314
315
|
|
|
315
316
|
if (bars.length === 0) {
|
|
@@ -379,5 +380,4 @@ export function registerMarketImportTools(
|
|
|
379
380
|
}
|
|
380
381
|
},
|
|
381
382
|
);
|
|
382
|
-
|
|
383
383
|
}
|
|
@@ -48,7 +48,7 @@ export interface FullSyncContext {
|
|
|
48
48
|
*/
|
|
49
49
|
export function withSyncedBlock<TInput extends { blockId: string }, TOutput>(
|
|
50
50
|
baseDir: string,
|
|
51
|
-
handler: (input: TInput, ctx: SingleBlockContext) => Promise<TOutput
|
|
51
|
+
handler: (input: TInput, ctx: SingleBlockContext) => Promise<TOutput>,
|
|
52
52
|
): (input: TInput) => Promise<TOutput | ToolError> {
|
|
53
53
|
return async (input: TInput) => {
|
|
54
54
|
// NOTE: single-shot RW upgrade + silent RO fallback. withFullSync uses a bounded
|
|
@@ -106,13 +106,12 @@ export function withSyncedBlocks<
|
|
|
106
106
|
TOutput,
|
|
107
107
|
>(
|
|
108
108
|
baseDir: string,
|
|
109
|
-
handler: (input: TInput, ctx: MultiBlockContext) => Promise<TOutput
|
|
109
|
+
handler: (input: TInput, ctx: MultiBlockContext) => Promise<TOutput>,
|
|
110
110
|
): (input: TInput) => Promise<TOutput | ToolError> {
|
|
111
111
|
return async (input: TInput) => {
|
|
112
112
|
// Collect block IDs from various input patterns
|
|
113
113
|
const blockIds: string[] =
|
|
114
|
-
input.blockIds ??
|
|
115
|
-
[input.blockIdA, input.blockIdB].filter((id): id is string => !!id);
|
|
114
|
+
input.blockIds ?? [input.blockIdA, input.blockIdB].filter((id): id is string => !!id);
|
|
116
115
|
|
|
117
116
|
const syncResults = new Map<string, BlockSyncResult>();
|
|
118
117
|
|
|
@@ -171,7 +170,7 @@ export function withSyncedBlocks<
|
|
|
171
170
|
*/
|
|
172
171
|
export function withFullSync<TInput, TOutput>(
|
|
173
172
|
baseDir: string,
|
|
174
|
-
handler: (input: TInput, ctx: FullSyncContext) => Promise<TOutput
|
|
173
|
+
handler: (input: TInput, ctx: FullSyncContext) => Promise<TOutput>,
|
|
175
174
|
): (input: TInput) => Promise<TOutput> {
|
|
176
175
|
return async (input: TInput) => {
|
|
177
176
|
// Outer retry loop: papers over short RW-lock races where another session is
|
|
@@ -209,7 +208,7 @@ export function withFullSync<TInput, TOutput>(
|
|
|
209
208
|
// tell that sync was skipped and data may be stale.
|
|
210
209
|
console.warn(
|
|
211
210
|
`[sync-middleware] sync skipped: could not acquire write lock after ${attempts} retries; ` +
|
|
212
|
-
`downstream data may be stale until next call succeeds
|
|
211
|
+
`downstream data may be stale until next call succeeds.`,
|
|
213
212
|
);
|
|
214
213
|
blockSyncResult = {
|
|
215
214
|
blocksProcessed: 0,
|