tradeblocks-mcp 4.0.0 → 4.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +36 -15
  2. package/dist/{chunk-YQMQ3PI4.js → chunk-4UU7IT2A.js} +112 -1
  3. package/dist/chunk-4UU7IT2A.js.map +1 -0
  4. package/dist/{chunk-HQQNVMYF.js → chunk-FQKKA5WL.js} +63 -31
  5. package/dist/chunk-FQKKA5WL.js.map +1 -0
  6. package/dist/{chunk-XF3F3S7D.js → chunk-YRD62LDE.js} +1 -1
  7. package/dist/{chunk-XF3F3S7D.js.map → chunk-YRD62LDE.js.map} +1 -1
  8. package/dist/{daily-logs-store-LRZBF2UA.js → daily-logs-store-65YHFJ5F.js} +2 -2
  9. package/dist/{daily-logs-store-UNX5DAT7.js → daily-logs-store-PXB53WEK.js} +2 -2
  10. package/dist/market/provenance/index.js +30 -10
  11. package/dist/market/provenance/index.js.map +1 -1
  12. package/dist/{sync-I2S5TFQS.js → sync-VK2VEYHM.js} +3 -3
  13. package/dist/test-exports.js +559 -146
  14. package/dist/test-exports.js.map +1 -1
  15. package/dist/{trades-store-O4ETTDGU.js → trades-store-MOFJHRC2.js} +2 -2
  16. package/dist/{trades-store-P6QZQMAD.js → trades-store-UGZ62A7N.js} +2 -2
  17. package/package.json +2 -2
  18. package/server/{chunk-3XKPTACI.js → chunk-7N6HBOAY.js} +129 -1
  19. package/server/chunk-7N6HBOAY.js.map +1 -0
  20. package/server/{chunk-JI2QLM6W.js → chunk-JSQFHCJO.js} +3406 -2978
  21. package/server/chunk-JSQFHCJO.js.map +1 -0
  22. package/server/{daily-logs-store-EMWH6MR4.js → daily-logs-store-YAXASQN4.js} +2 -2
  23. package/server/index.js +1893 -900
  24. package/server/index.js.map +1 -1
  25. package/server/{sync-NNHKLGNZ.js → sync-3W2RJUZ6.js} +3 -3
  26. package/server/{trades-store-3QYMUVA5.js → trades-store-MF7YYLCH.js} +2 -2
  27. package/src/db/dataset-schemas.ts +236 -0
  28. package/src/db/parquet-writer.ts +31 -1
  29. package/src/index.ts +3 -1
  30. package/src/market/ingestor/market-ingestor.ts +100 -29
  31. package/src/market/ingestor/types.ts +3 -0
  32. package/src/market/stores/parquet-quote-store.ts +19 -2
  33. package/src/prompts.ts +2 -2
  34. package/src/tools/analysis.ts +107 -6
  35. package/src/tools/blocks/analysis.ts +322 -209
  36. package/src/tools/datasets.ts +147 -0
  37. package/src/tools/market-data.ts +51 -5
  38. package/src/tools/market-fetch.ts +8 -6
  39. package/src/tools/middleware/strict-arguments.ts +1 -1
  40. package/src/tools/profile-analysis.ts +203 -77
  41. package/src/tools/reports/fields.ts +8 -3
  42. package/src/tools/reports/helpers.ts +19 -0
  43. package/src/tools/reports/predictive.ts +31 -45
  44. package/src/tools/schema.ts +9 -0
  45. package/src/tools/shared/field-analysis.ts +171 -0
  46. package/src/utils/filter-predicates.ts +48 -19
  47. package/src/utils/market-provider.ts +17 -2
  48. package/src/utils/providers/massive.ts +151 -31
  49. package/src/utils/threshold-sweep.ts +35 -0
  50. package/src/utils/trade-analysis-fields.ts +72 -0
  51. package/dist/chunk-HQQNVMYF.js.map +0 -1
  52. package/dist/chunk-YQMQ3PI4.js.map +0 -1
  53. package/server/chunk-3XKPTACI.js.map +0 -1
  54. package/server/chunk-JI2QLM6W.js.map +0 -1
  55. /package/dist/{daily-logs-store-LRZBF2UA.js.map → daily-logs-store-65YHFJ5F.js.map} +0 -0
  56. /package/dist/{daily-logs-store-UNX5DAT7.js.map → daily-logs-store-PXB53WEK.js.map} +0 -0
  57. /package/dist/{sync-I2S5TFQS.js.map → sync-VK2VEYHM.js.map} +0 -0
  58. /package/dist/{trades-store-O4ETTDGU.js.map → trades-store-MOFJHRC2.js.map} +0 -0
  59. /package/dist/{trades-store-P6QZQMAD.js.map → trades-store-UGZ62A7N.js.map} +0 -0
  60. /package/server/{daily-logs-store-EMWH6MR4.js.map → daily-logs-store-YAXASQN4.js.map} +0 -0
  61. /package/server/{sync-NNHKLGNZ.js.map → sync-3W2RJUZ6.js.map} +0 -0
  62. /package/server/{trades-store-3QYMUVA5.js.map → trades-store-MF7YYLCH.js.map} +0 -0
package/README.md CHANGED
@@ -510,21 +510,35 @@ such as `calculatedAt`, sync times, and market-feed timestamps, remain ISO times
510
510
 
511
511
  ### Market Data Tools
512
512
 
513
- | Tool | Description |
514
- | ---------------------------- | ---------------------------------------------------------------------------------- |
515
- | `import_market_csv` | Import market data CSV with column mapping |
516
- | `import_from_database` | Import from external DuckDB databases |
517
- | `import_flat_file` | Import a local Parquet or CSV flat file for a ticker/timespan |
518
- | `fetch_bars` | Fetch daily or intraday OHLCV bars from configured provider |
519
- | `fetch_quotes` | Fetch option minute quotes from configured provider |
520
- | `fetch_chain` | Fetch option chain snapshot for an underlying on a given date |
521
- | `compute_vix_context` | Compute cross-ticker VIX regime fields for a date range |
522
- | `refresh_market_data` | Composite daily refresh: fetch bars, auto-fire VIX context, return coverage report |
523
- | `enrich_market_data` | Compute ~40 derived indicators from raw OHLCV |
524
- | `enrich_trades` | Enrich trades with market context (lookahead-free) |
525
- | `analyze_regime_performance` | Analyze P&L by market regime |
526
- | `suggest_filters` | Suggest trade filters based on market conditions |
527
- | `calculate_orb` | Opening range breakout analysis from intraday bars |
513
+ | Tool | Description |
514
+ | ---------------------------- | ----------------------------------------------------------------------------------- |
515
+ | `import_market_csv` | Import market data CSV with column mapping |
516
+ | `import_from_database` | Import from external DuckDB databases |
517
+ | `import_flat_file` | Import a local Parquet or CSV flat file for a ticker/timespan |
518
+ | `fetch_bars` | Fetch daily or intraday OHLCV bars from configured provider |
519
+ | `fetch_quotes` | Fetch option minute quotes from configured provider |
520
+ | `fetch_chain` | Fetch option chain snapshot for an underlying on a given date |
521
+ | `compute_vix_context` | Compute cross-ticker VIX regime fields for a date range |
522
+ | `refresh_market_data` | Composite daily refresh: fetch bars, auto-fire VIX context, return coverage report |
523
+ | `enrich_market_data` | Compute ~40 derived indicators from raw OHLCV |
524
+ | `enrich_trades` | Enrich trades with causal market/dataset values, provenance and custom fields |
525
+ | `analyze_regime_performance` | Analyze P&L by presets or any registered field (numeric bins or categorical values) |
526
+ | `suggest_filters` | Default market filters or optional fields with generic threshold/exclusion sweeps |
527
+ | `calculate_orb` | Opening range breakout analysis from intraday bars |
528
+ | `import_daily_dataset` | Import or replace a user Static Dataset with per-column timing |
529
+ | `list_daily_datasets` | List user datasets, fields, timing and coverage |
530
+ | `delete_daily_dataset` | Remove a dataset and its registered fields |
531
+ | `list_analysis_fields` | Discover built-in and dataset fields at runtime |
532
+
533
+ Since 4.1.0, user daily datasets use `<dataset>.<column>` field names in profile filters,
534
+ predictive reports, regime analysis and threshold sweeps. `import_daily_dataset` takes
535
+ `name`, exactly one of inline `content` or `csvPath` under the server's data root, and optional
536
+ `ticker`, `columnTiming` and `knownBy` (`HH:mm` ET). Re-import replaces the named dataset
537
+ atomically, without changing market data or requiring provider credentials. Date-only close
538
+ values are available the following calendar day at 00:00 ET; date-only open values require
539
+ `knownBy`; timestamped rows are as-of. Sparse columns carry forward their latest available
540
+ observation without a freshness cutoff. See the [dataset tools reference](../../docs/mcp-tools.md#market-analysis)
541
+ and [4.1.0 behavior changes](../../releases/v4.1.0.md#behavior-changes).
528
542
 
529
543
  ### Strategy Profile Tools
530
544
 
@@ -538,6 +552,13 @@ such as `calculatedAt`, sync times, and market-feed timestamps, remain ISO times
538
552
  | `validate_entry_filters` | Test each entry filter's contribution to edge |
539
553
  | `portfolio_structure_map` | Regime x structure coverage matrix across strategies |
540
554
 
555
+ `validate_entry_filters` names unknown market fields with reasons in
556
+ `unresolvable_filters`, warnings, and its summary; they are excluded from ablation
557
+ so known filters remain analyzable. If all market filters are unknown, it returns
558
+ `error: "no_resolvable_filters"`. `analyze_structure_fit` reports the same exclusions
559
+ and keeps its remaining dimensions. Known fields with missing daily values retain
560
+ their existing missing-data treatment.
561
+
541
562
  ### Import Tools
542
563
 
543
564
  | Tool | Description |
@@ -5327,6 +5327,115 @@ async function getTradesPage(blockId, offset, limit) {
5327
5327
  // ../lib/utils/trade-frequency.ts
5328
5328
  var MS_PER_DAY = 1e3 * 60 * 60 * 24;
5329
5329
 
5330
+ // ../lib/calculations/static-dataset-matcher.ts
5331
+ function getDatasetValueAvailableAt(dataset, row, column) {
5332
+ if (!dataset.columns.includes(column)) throw new Error(`Unknown dataset column: ${column}`);
5333
+ if (dataset.timingMode !== "declared") return new Date(row.timestamp);
5334
+ const timing = dataset.columnTiming?.[column];
5335
+ if (timing !== "close" && timing !== "open")
5336
+ throw new Error(`Declare open or close timing for ${column}`);
5337
+ if (!(row.dateOnly ?? dataset.dateOnly)) return new Date(row.timestamp);
5338
+ const day = getDateOnly(new Date(row.timestamp));
5339
+ if (timing === "open") {
5340
+ if (!dataset.knownBy || !/^([01]\d|2[0-3]):[0-5]\d$/.test(dataset.knownBy)) {
5341
+ throw new Error(`Date-only open column ${column} requires a known-by time (ET)`);
5342
+ }
5343
+ return combineDateAndTime(/* @__PURE__ */ new Date(`${day}T00:00:00Z`), dataset.knownBy);
5344
+ }
5345
+ const next = /* @__PURE__ */ new Date(`${day}T00:00:00Z`);
5346
+ next.setUTCDate(next.getUTCDate() + 1);
5347
+ return combineDateAndTime(next, "00:00");
5348
+ }
5349
+ var availabilityIndexes = /* @__PURE__ */ new WeakMap();
5350
+ function resolveDatasetColumnAtTime(dataset, rows, column, entryTime) {
5351
+ if (!dataset.columns.includes(column)) throw new Error(`Unknown dataset column: ${column}`);
5352
+ let rowIndexes = availabilityIndexes.get(dataset);
5353
+ if (!rowIndexes) {
5354
+ rowIndexes = /* @__PURE__ */ new WeakMap();
5355
+ availabilityIndexes.set(dataset, rowIndexes);
5356
+ }
5357
+ let columns = rowIndexes.get(rows);
5358
+ if (!columns) {
5359
+ columns = /* @__PURE__ */ new Map();
5360
+ rowIndexes.set(rows, columns);
5361
+ }
5362
+ let index = columns.get(column);
5363
+ if (!index) {
5364
+ index = rows.filter((row) => {
5365
+ const value = row.values[column];
5366
+ return value !== void 0 && value !== null && (typeof value !== "string" || value.trim() !== "");
5367
+ }).map((row) => ({
5368
+ row,
5369
+ time: getDatasetValueAvailableAt(dataset, row, column).getTime()
5370
+ })).sort((a, b) => a.time - b.time);
5371
+ columns.set(column, index);
5372
+ }
5373
+ let left = 0;
5374
+ let right = index.length;
5375
+ const time = entryTime.getTime();
5376
+ while (left < right) {
5377
+ const mid = Math.floor((left + right) / 2);
5378
+ if (index[mid].time <= time) left = mid + 1;
5379
+ else right = mid;
5380
+ }
5381
+ if (!left) return null;
5382
+ const match = index[left - 1];
5383
+ return { value: match.row.values[column], row: match.row, availableAt: new Date(match.time) };
5384
+ }
5385
+ function combineDateAndTime(dateOpened, timeOpened) {
5386
+ const d = new Date(dateOpened);
5387
+ const isUtcMidnight = d.getUTCHours() === 0 && d.getUTCMinutes() === 0 && d.getUTCSeconds() === 0 && d.getUTCMilliseconds() === 0;
5388
+ let year;
5389
+ let month;
5390
+ let day;
5391
+ if (isUtcMidnight) {
5392
+ year = d.getUTCFullYear();
5393
+ month = d.getUTCMonth();
5394
+ day = d.getUTCDate();
5395
+ } else {
5396
+ year = d.getFullYear();
5397
+ month = d.getMonth();
5398
+ day = d.getDate();
5399
+ }
5400
+ let hours = 0;
5401
+ let minutes = 0;
5402
+ let seconds = 0;
5403
+ const timeParts = timeOpened.split(":");
5404
+ if (timeParts.length >= 2) {
5405
+ hours = parseInt(timeParts[0], 10) || 0;
5406
+ minutes = parseInt(timeParts[1], 10) || 0;
5407
+ seconds = timeParts.length > 2 ? parseInt(timeParts[2], 10) || 0 : 0;
5408
+ }
5409
+ const utcDate = Date.UTC(year, month, day, hours, minutes, seconds, 0);
5410
+ const testDate = new Date(utcDate);
5411
+ const etOffset = getEasternTimeOffset(testDate);
5412
+ return new Date(utcDate - etOffset * 60 * 1e3);
5413
+ }
5414
+ function getEasternTimeOffset(date) {
5415
+ const formatter = new Intl.DateTimeFormat("en-US", {
5416
+ timeZone: "America/New_York",
5417
+ timeZoneName: "shortOffset"
5418
+ });
5419
+ const parts = formatter.formatToParts(date);
5420
+ const tzPart = parts.find((p) => p.type === "timeZoneName");
5421
+ if (tzPart) {
5422
+ const match = tzPart.value.match(/GMT([+-]\d+)/);
5423
+ if (match) {
5424
+ return parseInt(match[1], 10) * 60;
5425
+ }
5426
+ }
5427
+ return -300;
5428
+ }
5429
+ function getDateOnly(date) {
5430
+ const formatter = new Intl.DateTimeFormat("en-CA", {
5431
+ timeZone: "America/New_York",
5432
+ year: "numeric",
5433
+ month: "2-digit",
5434
+ day: "2-digit"
5435
+ });
5436
+ return formatter.format(date);
5437
+ }
5438
+
5330
5439
  // ../lib/db/index.ts
5331
5440
  var DB_NAME = "TradeBlocksDB";
5332
5441
  var DB_VERSION = 7;
@@ -5805,6 +5914,8 @@ export {
5805
5914
  getNetPl,
5806
5915
  rebuildEquityCurve,
5807
5916
  formatDateKey,
5917
+ resolveDatasetColumnAtTime,
5918
+ combineDateAndTime,
5808
5919
  REPORTING_TRADE_COLUMN_ALIASES,
5809
5920
  addDailyLogEntries,
5810
5921
  getDailyLogsByBlock,
@@ -5830,4 +5941,4 @@ export {
5830
5941
  searchTrades,
5831
5942
  getTradesPage
5832
5943
  };
5833
- //# sourceMappingURL=chunk-YQMQ3PI4.js.map
5944
+ //# sourceMappingURL=chunk-4UU7IT2A.js.map