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
|
@@ -95,9 +95,15 @@ export function validateColumnMapping(
|
|
|
95
95
|
|
|
96
96
|
/** Parse CSV content into rows with header mapping. Strips UTF-8 BOM. */
|
|
97
97
|
function parseCSV(content: string): { headers: string[]; rows: Record<string, string>[] } {
|
|
98
|
-
const lines = content
|
|
98
|
+
const lines = content
|
|
99
|
+
.replace(/^\uFEFF/, "")
|
|
100
|
+
.trim()
|
|
101
|
+
.split("\n");
|
|
99
102
|
if (lines.length < 2) return { headers: [], rows: [] };
|
|
100
|
-
const headers = lines[0]
|
|
103
|
+
const headers = lines[0]
|
|
104
|
+
.trim()
|
|
105
|
+
.split(",")
|
|
106
|
+
.map((h) => h.trim());
|
|
101
107
|
const rows: Record<string, string>[] = [];
|
|
102
108
|
for (let i = 1; i < lines.length; i++) {
|
|
103
109
|
const line = lines[i].trim();
|
|
@@ -123,7 +129,9 @@ function parseFlexibleDate(value: string): string | null {
|
|
|
123
129
|
if (!isNaN(numeric) && numeric > 1e8) {
|
|
124
130
|
return new Date(numeric * 1000).toLocaleDateString("en-CA", {
|
|
125
131
|
timeZone: "America/New_York",
|
|
126
|
-
year: "numeric",
|
|
132
|
+
year: "numeric",
|
|
133
|
+
month: "2-digit",
|
|
134
|
+
day: "2-digit",
|
|
127
135
|
});
|
|
128
136
|
}
|
|
129
137
|
if (/^\d{4}-\d{2}-\d{2}$/.test(value)) return value;
|
|
@@ -143,7 +151,9 @@ function parseFlexibleTime(value: string): string | null {
|
|
|
143
151
|
const d = new Date(numeric * 1000);
|
|
144
152
|
return d.toLocaleTimeString("en-US", {
|
|
145
153
|
timeZone: "America/New_York",
|
|
146
|
-
hour: "2-digit",
|
|
154
|
+
hour: "2-digit",
|
|
155
|
+
minute: "2-digit",
|
|
156
|
+
hour12: false,
|
|
147
157
|
});
|
|
148
158
|
}
|
|
149
159
|
if (/^\d{2}:\d{2}$/.test(value)) return value;
|
|
@@ -171,11 +181,17 @@ function applyColumnMapping(
|
|
|
171
181
|
const rawValue = row[sourceCol] ?? "";
|
|
172
182
|
if (schemaCol === "date") {
|
|
173
183
|
const parsed = parseFlexibleDate(rawValue);
|
|
174
|
-
if (parsed === null) {
|
|
184
|
+
if (parsed === null) {
|
|
185
|
+
hasNullDate = true;
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
175
188
|
mapped[schemaCol] = parsed;
|
|
176
189
|
} else if (schemaCol === "time") {
|
|
177
190
|
const parsed = parseFlexibleTime(rawValue);
|
|
178
|
-
if (parsed === null) {
|
|
191
|
+
if (parsed === null) {
|
|
192
|
+
hasNullDate = true;
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
179
195
|
mapped[schemaCol] = parsed;
|
|
180
196
|
} else {
|
|
181
197
|
if (rawValue === "" || rawValue === "NaN" || rawValue === "NA") {
|
|
@@ -190,7 +206,9 @@ function applyColumnMapping(
|
|
|
190
206
|
if (!("date" in mapped)) continue;
|
|
191
207
|
// Auto-extract time from a Unix-timestamp date column when `time` is not mapped.
|
|
192
208
|
if (!("time" in mapped)) {
|
|
193
|
-
const dateSourceCol = Object.entries(columnMapping).find(
|
|
209
|
+
const dateSourceCol = Object.entries(columnMapping).find(
|
|
210
|
+
([, schema]) => schema === "date",
|
|
211
|
+
)?.[0];
|
|
194
212
|
if (dateSourceCol) {
|
|
195
213
|
const rawDateValue = row[dateSourceCol] ?? "";
|
|
196
214
|
const numericDate = Number(rawDateValue);
|
|
@@ -211,10 +229,7 @@ function applyColumnMapping(
|
|
|
211
229
|
* Non-numeric values fall back to `0` so the spot store always receives well-
|
|
212
230
|
* typed numbers — invalid rows are filtered upstream.
|
|
213
231
|
*/
|
|
214
|
-
function coerceMappedRowToBar(
|
|
215
|
-
row: Record<string, unknown>,
|
|
216
|
-
ticker: string,
|
|
217
|
-
): BarRow | null {
|
|
232
|
+
function coerceMappedRowToBar(row: Record<string, unknown>, ticker: string): BarRow | null {
|
|
218
233
|
const date = typeof row.date === "string" ? row.date : null;
|
|
219
234
|
if (!date) return null;
|
|
220
235
|
const time = typeof row.time === "string" ? row.time : "09:30";
|
|
@@ -408,7 +423,11 @@ export async function importFromDatabase(
|
|
|
408
423
|
});
|
|
409
424
|
bars = parseDatabaseRowsToBars(rawRows, normalizedTicker, columnMapping);
|
|
410
425
|
} finally {
|
|
411
|
-
try {
|
|
426
|
+
try {
|
|
427
|
+
await conn.run(`DETACH ${EXT_ALIAS}`);
|
|
428
|
+
} catch {
|
|
429
|
+
/* best-effort */
|
|
430
|
+
}
|
|
412
431
|
}
|
|
413
432
|
|
|
414
433
|
if (bars.length === 0) {
|
|
@@ -18,16 +18,16 @@ import { ThetaDataProvider } from "./providers/thetadata.ts";
|
|
|
18
18
|
|
|
19
19
|
/** Normalized OHLCV bar — shared output type for all providers. */
|
|
20
20
|
export interface BarRow {
|
|
21
|
-
date: string;
|
|
21
|
+
date: string; // "YYYY-MM-DD" Eastern Time
|
|
22
22
|
open: number;
|
|
23
23
|
high: number;
|
|
24
24
|
low: number;
|
|
25
25
|
close: number;
|
|
26
26
|
volume: number;
|
|
27
|
-
ticker: string;
|
|
28
|
-
time?: string;
|
|
29
|
-
bid?: number;
|
|
30
|
-
ask?: number;
|
|
27
|
+
ticker: string; // Plain storage format (no prefix)
|
|
28
|
+
time?: string; // "HH:MM" ET — only set for intraday (minute/hour) bars
|
|
29
|
+
bid?: number; // Best bid — only set when provider supplies quote data
|
|
30
|
+
ask?: number; // Best ask — only set when provider supplies quote data
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
/** Asset classes supported by market data providers. */
|
|
@@ -56,7 +56,7 @@ export interface OptionContract {
|
|
|
56
56
|
underlying_price: number;
|
|
57
57
|
contract_type: "call" | "put";
|
|
58
58
|
strike: number;
|
|
59
|
-
expiration: string;
|
|
59
|
+
expiration: string; // "YYYY-MM-DD"
|
|
60
60
|
exercise_style: string;
|
|
61
61
|
delta: number | null;
|
|
62
62
|
gamma: number | null;
|
|
@@ -93,19 +93,19 @@ export interface FetchSnapshotResult {
|
|
|
93
93
|
/** Options for fetching historical option contract metadata (reference endpoint). */
|
|
94
94
|
export interface FetchContractListOptions {
|
|
95
95
|
underlying: string;
|
|
96
|
-
as_of: string;
|
|
97
|
-
expired?: boolean;
|
|
98
|
-
expiration_date_gte?: string;
|
|
99
|
-
expiration_date_lte?: string;
|
|
96
|
+
as_of: string; // "YYYY-MM-DD" -- historical date
|
|
97
|
+
expired?: boolean; // default true for historical contract lookup
|
|
98
|
+
expiration_date_gte?: string; // Only contracts expiring on or after this date
|
|
99
|
+
expiration_date_lte?: string; // Only contracts expiring on or before this date
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
/** Single contract reference record (no greeks/pricing -- metadata only). */
|
|
103
103
|
export interface ContractReference {
|
|
104
|
-
ticker: string;
|
|
104
|
+
ticker: string; // OCC ticker without O: prefix
|
|
105
105
|
contract_type: "call" | "put";
|
|
106
106
|
strike: number;
|
|
107
|
-
expiration: string;
|
|
108
|
-
exercise_style: string;
|
|
107
|
+
expiration: string; // "YYYY-MM-DD"
|
|
108
|
+
exercise_style: string; // "american" | "european"
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
/** Result from contract list reference endpoint. */
|
|
@@ -147,7 +147,7 @@ export interface DataAvailability {
|
|
|
147
147
|
|
|
148
148
|
/** Declares what data endpoints a provider supports. Used by the pipeline to build fetch plans. */
|
|
149
149
|
export interface ProviderCapabilities {
|
|
150
|
-
tradeBars: boolean;
|
|
150
|
+
tradeBars: boolean; // minute OHLC from trade aggregates
|
|
151
151
|
/**
|
|
152
152
|
* Strictly: "true NBBO bid/ask is available via this provider's dedicated
|
|
153
153
|
* quotes endpoint". Use this when you specifically need real bid/ask spreads.
|
|
@@ -160,12 +160,12 @@ export interface ProviderCapabilities {
|
|
|
160
160
|
* 'synth_close') for per-row provenance.
|
|
161
161
|
*/
|
|
162
162
|
quotes: boolean;
|
|
163
|
-
greeks: boolean;
|
|
164
|
-
flatFiles: boolean;
|
|
165
|
-
bulkByRoot: boolean;
|
|
166
|
-
perTicker: boolean;
|
|
167
|
-
minuteBars: boolean;
|
|
168
|
-
dailyBars: boolean;
|
|
163
|
+
greeks: boolean; // provider-computed greeks on contracts
|
|
164
|
+
flatFiles: boolean; // bulk S3/file download of historical data
|
|
165
|
+
bulkByRoot: boolean; // provider has an every-contract path for an underlying/root
|
|
166
|
+
perTicker: boolean; // one call per OCC ticker (Massive/Polygon pattern)
|
|
167
|
+
minuteBars: boolean; // minute-level resolution available
|
|
168
|
+
dailyBars: boolean; // daily-level resolution available
|
|
169
169
|
/** Earliest available data per asset class. Used by download tools and data pipelines to avoid requesting data that doesn't exist. */
|
|
170
170
|
dataAvailability?: {
|
|
171
171
|
option?: DataAvailability;
|
|
@@ -342,9 +342,7 @@ export function getProvider(): MarketDataProvider {
|
|
|
342
342
|
_cached = new ThetaDataProvider();
|
|
343
343
|
break;
|
|
344
344
|
default:
|
|
345
|
-
throw new Error(
|
|
346
|
-
`Unknown MARKET_DATA_PROVIDER: "${name}". Supported: massive, thetadata`
|
|
347
|
-
);
|
|
345
|
+
throw new Error(`Unknown MARKET_DATA_PROVIDER: "${name}". Supported: massive, thetadata`);
|
|
348
346
|
}
|
|
349
347
|
return _cached!;
|
|
350
348
|
}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
export type MassiveDataTier =
|
|
1
|
+
export type MassiveDataTier = "ohlc" | "trades" | "quotes";
|
|
2
2
|
|
|
3
|
-
export function resolveMassiveDataTier(
|
|
4
|
-
|
|
5
|
-
)
|
|
6
|
-
const tier = (env.MASSIVE_DATA_TIER ?? '').toLowerCase();
|
|
7
|
-
if (tier === 'quotes' || tier === 'trades' || tier === 'ohlc') {
|
|
3
|
+
export function resolveMassiveDataTier(env: NodeJS.ProcessEnv = process.env): MassiveDataTier {
|
|
4
|
+
const tier = (env.MASSIVE_DATA_TIER ?? "").toLowerCase();
|
|
5
|
+
if (tier === "quotes" || tier === "trades" || tier === "ohlc") {
|
|
8
6
|
return tier;
|
|
9
7
|
}
|
|
10
|
-
return
|
|
8
|
+
return "ohlc";
|
|
11
9
|
}
|
|
@@ -46,10 +46,7 @@ export function groupTickersByUnderlying(
|
|
|
46
46
|
* a row-level column. sourceGlob is interpolated raw; caller must pass a
|
|
47
47
|
* trusted, migrator-composed path.
|
|
48
48
|
*/
|
|
49
|
-
export function buildOptionChainSelectQuery(
|
|
50
|
-
sourceGlob: string,
|
|
51
|
-
underlying: string,
|
|
52
|
-
): string {
|
|
49
|
+
export function buildOptionChainSelectQuery(sourceGlob: string, underlying: string): string {
|
|
53
50
|
return `SELECT * EXCLUDE (underlying)
|
|
54
51
|
FROM read_parquet('${sourceGlob}')
|
|
55
52
|
WHERE underlying = '${underlying}'`;
|
|
@@ -59,10 +56,7 @@ export function buildOptionChainSelectQuery(
|
|
|
59
56
|
* Build the SELECT for option_quote_minutes rewrite. NO EXCLUDE — the body
|
|
60
57
|
* has no `underlying` column. Filters by root via regexp_extract.
|
|
61
58
|
*/
|
|
62
|
-
export function buildOptionQuoteSelectQuery(
|
|
63
|
-
sourceGlob: string,
|
|
64
|
-
roots: string[],
|
|
65
|
-
): string {
|
|
59
|
+
export function buildOptionQuoteSelectQuery(sourceGlob: string, roots: string[]): string {
|
|
66
60
|
if (roots.length === 0) {
|
|
67
61
|
throw new Error("buildOptionQuoteSelectQuery: roots must not be empty");
|
|
68
62
|
}
|
|
@@ -2,11 +2,7 @@ import { getSofrRateByKey } from "@tradeblocks/lib";
|
|
|
2
2
|
import type { ContractRow } from "./chain-loader.ts";
|
|
3
3
|
import { computeLegGreeks } from "./black-scholes.ts";
|
|
4
4
|
import { computeFractionalDte } from "./option-time.ts";
|
|
5
|
-
import {
|
|
6
|
-
getSharedIvSolverPool,
|
|
7
|
-
type IvSolveColumns,
|
|
8
|
-
type IvSolverPool,
|
|
9
|
-
} from "./iv-solver-pool.ts";
|
|
5
|
+
import { getSharedIvSolverPool, type IvSolveColumns, type IvSolverPool } from "./iv-solver-pool.ts";
|
|
10
6
|
|
|
11
7
|
export type QuoteGreeksSource = "massive" | "thetadata" | "computed";
|
|
12
8
|
export type QuoteGreeksMode = "auto" | "provider" | "compute";
|
|
@@ -79,20 +75,24 @@ function memoizedSofrRate(dateKey: string): number {
|
|
|
79
75
|
}
|
|
80
76
|
|
|
81
77
|
export function hasQuoteGreeks(row: QuoteGreekFields): boolean {
|
|
82
|
-
return
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
78
|
+
return (
|
|
79
|
+
isFiniteNumber(row.delta ?? null) &&
|
|
80
|
+
isFiniteNumber(row.gamma ?? null) &&
|
|
81
|
+
isFiniteNumber(row.theta ?? null) &&
|
|
82
|
+
isFiniteNumber(row.vega ?? null) &&
|
|
83
|
+
isFiniteNumber(row.iv ?? null)
|
|
84
|
+
);
|
|
87
85
|
}
|
|
88
86
|
|
|
89
87
|
function hasProviderFirstOrderGreeks(row: QuoteGreekFields): boolean {
|
|
90
|
-
return
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
88
|
+
return (
|
|
89
|
+
row.greeks_source === "thetadata" &&
|
|
90
|
+
isFiniteNumber(row.delta ?? null) &&
|
|
91
|
+
(row.gamma == null || isFiniteNumber(row.gamma)) &&
|
|
92
|
+
isFiniteNumber(row.theta ?? null) &&
|
|
93
|
+
isFiniteNumber(row.vega ?? null) &&
|
|
94
|
+
isFiniteNumber(row.iv ?? null)
|
|
95
|
+
);
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
function hasExistingQuoteGreeks(row: QuoteGreekFields): boolean {
|
|
@@ -100,9 +100,11 @@ function hasExistingQuoteGreeks(row: QuoteGreekFields): boolean {
|
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
function hasQuoteGreekProvenanceFields(row: QuoteGreekFields): boolean {
|
|
103
|
-
return
|
|
104
|
-
|
|
105
|
-
|
|
103
|
+
return (
|
|
104
|
+
row.rate_type === OPTION_QUOTE_GREEKS_RATE_TYPE &&
|
|
105
|
+
isFiniteNumber(row.rate_value ?? null) &&
|
|
106
|
+
row.gamma_source === OPTION_QUOTE_GREEKS_GAMMA_SOURCE
|
|
107
|
+
);
|
|
106
108
|
}
|
|
107
109
|
|
|
108
110
|
export function normalizeExistingQuoteGreeks(
|
|
@@ -114,9 +116,9 @@ export function normalizeExistingQuoteGreeks(
|
|
|
114
116
|
row.greeks_source = defaultSource;
|
|
115
117
|
}
|
|
116
118
|
if (
|
|
117
|
-
row.greeks_source === "computed"
|
|
118
|
-
|
|
119
|
-
|
|
119
|
+
row.greeks_source === "computed" &&
|
|
120
|
+
row.greeks_revision == null &&
|
|
121
|
+
hasQuoteGreekProvenanceFields(row)
|
|
120
122
|
) {
|
|
121
123
|
row.greeks_revision = OPTION_QUOTE_GREEKS_REVISION;
|
|
122
124
|
}
|
|
@@ -131,15 +133,7 @@ export function computeQuoteGreeks(params: {
|
|
|
131
133
|
expiration: string;
|
|
132
134
|
contractType: ContractRow["contract_type"];
|
|
133
135
|
}): QuoteGreekFields | null {
|
|
134
|
-
const {
|
|
135
|
-
optionPrice,
|
|
136
|
-
underlyingPrice,
|
|
137
|
-
strike,
|
|
138
|
-
date,
|
|
139
|
-
time,
|
|
140
|
-
expiration,
|
|
141
|
-
contractType,
|
|
142
|
-
} = params;
|
|
136
|
+
const { optionPrice, underlyingPrice, strike, date, time, expiration, contractType } = params;
|
|
143
137
|
if (!(optionPrice > 0) || !(underlyingPrice > 0) || !(strike > 0)) return null;
|
|
144
138
|
const dte = computeFractionalDte(date, time.slice(0, 5), expiration);
|
|
145
139
|
if (!(dte >= 0)) return null;
|
package/src/utils/option-time.ts
CHANGED
|
@@ -3,18 +3,14 @@ function parseDateUtc(date: string): Date {
|
|
|
3
3
|
}
|
|
4
4
|
|
|
5
5
|
function minutesFromTime(time: string): number {
|
|
6
|
-
const [hour, minute] = time.slice(0, 5).split(
|
|
6
|
+
const [hour, minute] = time.slice(0, 5).split(":").map(Number);
|
|
7
7
|
return hour * 60 + minute;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
export function computeFractionalDte(
|
|
11
|
-
date: string,
|
|
12
|
-
time: string,
|
|
13
|
-
expiration: string,
|
|
14
|
-
): number {
|
|
10
|
+
export function computeFractionalDte(date: string, time: string, expiration: string): number {
|
|
15
11
|
const startDay = parseDateUtc(date).getTime();
|
|
16
12
|
const expiryDay = parseDateUtc(expiration).getTime();
|
|
17
13
|
const dayDiff = Math.round((expiryDay - startDay) / 86_400_000);
|
|
18
|
-
const remainingMinutes = Math.max(
|
|
19
|
-
return Math.max(dayDiff +
|
|
14
|
+
const remainingMinutes = Math.max(16 * 60 - minutesFromTime(time), 0);
|
|
15
|
+
return Math.max(dayDiff + remainingMinutes / (24 * 60), 0);
|
|
20
16
|
}
|
|
@@ -98,10 +98,7 @@ export function formatPercent(value: number, decimals: number = 2): string {
|
|
|
98
98
|
/**
|
|
99
99
|
* Format a ratio with specified decimals
|
|
100
100
|
*/
|
|
101
|
-
export function formatRatio(
|
|
102
|
-
value: number | undefined,
|
|
103
|
-
decimals: number = 2
|
|
104
|
-
): string {
|
|
101
|
+
export function formatRatio(value: number | undefined, decimals: number = 2): string {
|
|
105
102
|
if (value === undefined || value === null || !isFinite(value)) {
|
|
106
103
|
return "N/A";
|
|
107
104
|
}
|
|
@@ -3,10 +3,7 @@ import {
|
|
|
3
3
|
type MarketDataProvider,
|
|
4
4
|
type ProviderCapabilities,
|
|
5
5
|
} from "./market-provider.ts";
|
|
6
|
-
import {
|
|
7
|
-
resolveMassiveDataTier,
|
|
8
|
-
type MassiveDataTier,
|
|
9
|
-
} from "./massive-tier.ts";
|
|
6
|
+
import { resolveMassiveDataTier, type MassiveDataTier } from "./massive-tier.ts";
|
|
10
7
|
|
|
11
8
|
export interface ResolvedProviderCapabilities extends ProviderCapabilities {
|
|
12
9
|
provider: MarketDataProvider;
|