tradeblocks-mcp 4.2.0 → 4.3.0

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 (47) hide show
  1. package/README.md +40 -1
  2. package/dist/{chunk-YRD62LDE.js → chunk-35RVWZGT.js} +4 -1
  3. package/dist/{chunk-4UU7IT2A.js.map → chunk-35RVWZGT.js.map} +1 -1
  4. package/dist/{chunk-4UU7IT2A.js → chunk-ENXBPJ7Y.js} +148 -6
  5. package/dist/chunk-ENXBPJ7Y.js.map +1 -0
  6. package/dist/{chunk-62GCLMQA.js → chunk-TLX7IBCM.js} +3 -6
  7. package/dist/chunk-TLX7IBCM.js.map +1 -0
  8. package/dist/{daily-logs-store-PXB53WEK.js → daily-logs-store-FZRIGYUX.js} +2 -2
  9. package/dist/{daily-logs-store-65YHFJ5F.js → daily-logs-store-MEZ5S3BR.js} +2 -2
  10. package/dist/market/provenance/index.js +1 -4
  11. package/dist/market/provenance/index.js.map +1 -1
  12. package/dist/{sync-JL7OPLCT.js → sync-EW33QZPD.js} +3 -3
  13. package/dist/test-exports.js +320 -279
  14. package/dist/test-exports.js.map +1 -1
  15. package/dist/{trades-store-UGZ62A7N.js → trades-store-3TOLBL2W.js} +2 -2
  16. package/dist/{trades-store-MOFJHRC2.js → trades-store-PNLPRYRJ.js} +2 -2
  17. package/package.json +1 -1
  18. package/server/{chunk-7N6HBOAY.js → chunk-PDSUY2X2.js} +148 -6
  19. package/server/chunk-PDSUY2X2.js.map +1 -0
  20. package/server/{chunk-J4GWE6JR.js → chunk-YIWLVX7Z.js} +3 -6
  21. package/server/chunk-YIWLVX7Z.js.map +1 -0
  22. package/server/{daily-logs-store-YAXASQN4.js → daily-logs-store-LRVO7COL.js} +2 -2
  23. package/server/index.js +993 -801
  24. package/server/index.js.map +1 -1
  25. package/server/{sync-ZV3QEGMO.js → sync-2POG6KA6.js} +3 -3
  26. package/server/{trades-store-MF7YYLCH.js → trades-store-MXUZLSF5.js} +2 -2
  27. package/src/db/dataset-schemas.ts +97 -23
  28. package/src/tools/market-data.ts +327 -245
  29. package/src/tools/profile-analysis.ts +18 -4
  30. package/src/tools/reports/fields.ts +16 -11
  31. package/src/tools/reports/helpers.ts +6 -2
  32. package/src/tools/reports/predictive.ts +15 -4
  33. package/src/tools/shared/field-analysis.ts +57 -14
  34. package/src/tools/shared/filter-objective.ts +73 -0
  35. package/src/utils/trade-analysis-fields.ts +19 -4
  36. package/dist/chunk-62GCLMQA.js.map +0 -1
  37. package/dist/chunk-YRD62LDE.js.map +0 -1
  38. package/server/chunk-7N6HBOAY.js.map +0 -1
  39. package/server/chunk-J4GWE6JR.js.map +0 -1
  40. /package/dist/{daily-logs-store-65YHFJ5F.js.map → daily-logs-store-FZRIGYUX.js.map} +0 -0
  41. /package/dist/{daily-logs-store-PXB53WEK.js.map → daily-logs-store-MEZ5S3BR.js.map} +0 -0
  42. /package/dist/{sync-JL7OPLCT.js.map → sync-EW33QZPD.js.map} +0 -0
  43. /package/dist/{trades-store-MOFJHRC2.js.map → trades-store-3TOLBL2W.js.map} +0 -0
  44. /package/dist/{trades-store-UGZ62A7N.js.map → trades-store-PNLPRYRJ.js.map} +0 -0
  45. /package/server/{daily-logs-store-YAXASQN4.js.map → daily-logs-store-LRVO7COL.js.map} +0 -0
  46. /package/server/{sync-ZV3QEGMO.js.map → sync-2POG6KA6.js.map} +0 -0
  47. /package/server/{trades-store-MF7YYLCH.js.map → trades-store-MXUZLSF5.js.map} +0 -0
@@ -9,11 +9,11 @@ import {
9
9
  syncAllBlocks,
10
10
  syncBlock,
11
11
  upsertSyncMetadata
12
- } from "./chunk-J4GWE6JR.js";
12
+ } from "./chunk-YIWLVX7Z.js";
13
13
  import "./chunk-76OEJGQX.js";
14
14
  import "./chunk-NLYLE5C6.js";
15
15
  import "./chunk-24O66TM5.js";
16
- import "./chunk-7N6HBOAY.js";
16
+ import "./chunk-PDSUY2X2.js";
17
17
  import "./chunk-2E3K452Q.js";
18
18
  import "./chunk-UT3JLF3M.js";
19
19
  export {
@@ -27,4 +27,4 @@ export {
27
27
  syncBlock,
28
28
  upsertSyncMetadata
29
29
  };
30
- //# sourceMappingURL=sync-ZV3QEGMO.js.map
30
+ //# sourceMappingURL=sync-2POG6KA6.js.map
@@ -12,7 +12,7 @@ import {
12
12
  getTradesPage,
13
13
  searchTrades,
14
14
  updateTradesForBlock
15
- } from "./chunk-7N6HBOAY.js";
15
+ } from "./chunk-PDSUY2X2.js";
16
16
  import "./chunk-2E3K452Q.js";
17
17
  import "./chunk-UT3JLF3M.js";
18
18
  export {
@@ -29,4 +29,4 @@ export {
29
29
  searchTrades,
30
30
  updateTradesForBlock
31
31
  };
32
- //# sourceMappingURL=trades-store-MF7YYLCH.js.map
32
+ //# sourceMappingURL=trades-store-MXUZLSF5.js.map
@@ -8,6 +8,16 @@ import {
8
8
  } from "@tradeblocks/lib";
9
9
  import { OPEN_KNOWN_FIELDS, CLOSE_KNOWN_FIELDS, STATIC_FIELDS } from "../utils/field-timing.ts";
10
10
  import { resolveTradeTicker } from "../utils/ticker.ts";
11
+ import { z } from "zod";
12
+
13
+ export const maxMissedSessionsSchema = z
14
+ .number()
15
+ .int()
16
+ .nonnegative()
17
+ .optional()
18
+ .describe(
19
+ "Opt-in maximum missed XNYS sessions for dataset values. No limit when omitted. Unsupported calendar ages (outside 2022–2030) are refused when a limit is set.",
20
+ );
11
21
 
12
22
  export async function ensureDatasetsSchema(conn: DuckDBConnection): Promise<void> {
13
23
  await conn.run("CREATE SCHEMA IF NOT EXISTS datasets");
@@ -127,7 +137,7 @@ export function datasetFields(entries: StoredDataset[]): AnalysisField[] {
127
137
  ? "numeric"
128
138
  : "categorical",
129
139
  timing: dataset.columnTiming![column],
130
- knownBy: dataset.knownBy ?? null,
140
+ knownBy: dataset.columnTiming![column] === "open" ? (dataset.knownBy ?? null) : null,
131
141
  source,
132
142
  })),
133
143
  );
@@ -149,31 +159,49 @@ export async function runtimeFieldCatalog(conn: DuckDBConnection): Promise<Analy
149
159
  ];
150
160
  }
151
161
 
152
- export function resolveTradeDatasets(entries: StoredDataset[], trade: Trade) {
162
+ export interface DatasetProvenance {
163
+ sourceTimestamp: string;
164
+ availableAt: string;
165
+ sessionAge: number | "unsupported";
166
+ }
167
+
168
+ export function resolveTradeDatasets(
169
+ entries: StoredDataset[],
170
+ trade: Trade,
171
+ maxMissedSessions?: number,
172
+ ) {
153
173
  const values: Record<string, number | string> = {};
154
- const provenance: Record<string, { sourceTimestamp: string; availableAt: string }> = {};
174
+ const provenance: Record<string, DatasetProvenance> = {};
175
+ const rejected: Record<string, DatasetProvenance> = {};
155
176
  const entryTime = combineDateAndTime(trade.dateOpened, trade.timeOpened);
156
177
  for (const { dataset, rows, ticker } of entries) {
157
178
  if (ticker && ticker !== resolveTradeTicker(trade)) continue;
158
179
  for (const column of dataset.columns) {
159
- const match = resolveDatasetColumnAtTime(dataset, rows, column, entryTime);
180
+ const match = resolveDatasetColumnAtTime(dataset, rows, column, entryTime, maxMissedSessions);
160
181
  if (!match) continue;
161
182
  const field = `${dataset.name}.${column}`;
162
- values[field] = match.value;
163
- provenance[field] = {
183
+ if (match.accepted) values[field] = match.value;
184
+ (match.accepted ? provenance : rejected)[field] = {
164
185
  sourceTimestamp: match.row.timestamp.toISOString(),
165
186
  availableAt: match.availableAt.toISOString(),
187
+ sessionAge: match.sessionAge,
166
188
  };
167
189
  }
168
190
  }
169
- return { values, provenance };
191
+ return { values, provenance, rejected };
170
192
  }
171
193
 
172
194
  export interface DatasetFieldAge {
173
195
  attachments: number;
174
- medianAgeDays: number;
175
- maxAgeDays: number;
196
+ medianAgeDays: number | null;
197
+ maxAgeDays: number | null;
176
198
  olderThan3Days: number;
199
+ sessionAgeStatus: "supported" | "unsupported";
200
+ medianAgeSessions: number | null;
201
+ maxAgeSessions: number | null;
202
+ staleAttachments: number;
203
+ unsupportedSessionAge: number;
204
+ rejectedAttachments: number;
177
205
  }
178
206
 
179
207
  const easternCalendar = new Intl.DateTimeFormat("en-US", {
@@ -194,34 +222,61 @@ function easternDayNumber(date: Date): number {
194
222
  );
195
223
  }
196
224
 
197
- /** Age is source-to-entry Eastern calendar days, not a freshness limit. */
225
+ /** Session ages come only from the shared resolver's selected source provenance. */
198
226
  export function summarizeDatasetAge(
199
227
  trades: Trade[],
200
- matches: { provenance: Record<string, { sourceTimestamp: string }> }[],
228
+ matches: {
229
+ provenance: Record<string, { sourceTimestamp: string; sessionAge?: number | "unsupported" }>;
230
+ rejected?: Record<string, DatasetProvenance>;
231
+ }[],
201
232
  ): Record<string, DatasetFieldAge> {
202
- const ages = new Map<string, number[]>();
233
+ const ages = new Map<
234
+ string,
235
+ { days: number[]; sessions: number[]; unsupported: number; rejected: number }
236
+ >();
203
237
  for (let i = 0; i < trades.length; i++) {
204
238
  const entryDay = easternDayNumber(
205
239
  combineDateAndTime(trades[i].dateOpened, trades[i].timeOpened),
206
240
  );
207
- for (const [field, provenance] of Object.entries(matches[i].provenance)) {
208
- const samples = ages.get(field) ?? [];
209
- samples.push(entryDay - easternDayNumber(new Date(provenance.sourceTimestamp)));
241
+ for (const [field, provenance] of Object.entries({
242
+ ...matches[i].provenance,
243
+ ...matches[i].rejected,
244
+ })) {
245
+ const samples = ages.get(field) ?? { days: [], sessions: [], unsupported: 0, rejected: 0 };
246
+ if (matches[i].rejected?.[field]) samples.rejected++;
247
+ else samples.days.push(entryDay - easternDayNumber(new Date(provenance.sourceTimestamp)));
248
+ if (typeof provenance.sessionAge === "number") samples.sessions.push(provenance.sessionAge);
249
+ else samples.unsupported++;
210
250
  ages.set(field, samples);
211
251
  }
212
252
  }
213
253
  return Object.fromEntries(
214
254
  [...ages].map(([field, samples]) => {
215
- samples.sort((a, b) => a - b);
216
- const middle = Math.floor(samples.length / 2);
255
+ const days = samples.days.sort((a, b) => a - b);
256
+ const sessions = samples.sessions.sort((a, b) => a - b);
257
+ const middle = Math.floor(days.length / 2);
258
+ const sessionMiddle = Math.floor(sessions.length / 2);
217
259
  return [
218
260
  field,
219
261
  {
220
- attachments: samples.length,
221
- medianAgeDays:
222
- samples.length % 2 ? samples[middle] : (samples[middle - 1] + samples[middle]) / 2,
223
- maxAgeDays: samples[samples.length - 1],
224
- olderThan3Days: samples.filter((age) => age > 3).length,
262
+ attachments: days.length,
263
+ medianAgeDays: !days.length
264
+ ? null
265
+ : days.length % 2
266
+ ? days[middle]
267
+ : (days[middle - 1] + days[middle]) / 2,
268
+ maxAgeDays: days.length ? days[days.length - 1] : null,
269
+ olderThan3Days: days.filter((age) => age > 3).length,
270
+ sessionAgeStatus: samples.unsupported ? ("unsupported" as const) : ("supported" as const),
271
+ medianAgeSessions: !sessions.length
272
+ ? null
273
+ : sessions.length % 2
274
+ ? sessions[sessionMiddle]
275
+ : (sessions[sessionMiddle - 1] + sessions[sessionMiddle]) / 2,
276
+ maxAgeSessions: sessions.length ? sessions[sessions.length - 1] : null,
277
+ staleAttachments: sessions.filter((age) => age >= 1).length,
278
+ unsupportedSessionAge: samples.unsupported,
279
+ rejectedAttachments: samples.rejected,
225
280
  },
226
281
  ];
227
282
  }),
@@ -232,5 +287,24 @@ export function datasetAgeOutput(summary: Record<string, DatasetFieldAge>, field
232
287
  const selected = Object.fromEntries(
233
288
  fields.filter((field) => summary[field]).map((field) => [field, summary[field]]),
234
289
  );
235
- return Object.keys(selected).length ? { datasetAge: selected } : {};
290
+ const datasetWarnings = Object.entries(selected).flatMap(([field, age]) => [
291
+ ...(age.staleAttachments
292
+ ? [
293
+ `${field}: ${age.staleAttachments} candidate attachments missed at least one expected XNYS session.`,
294
+ ]
295
+ : []),
296
+ ...(age.unsupportedSessionAge
297
+ ? [
298
+ `${field}: session age unsupported for ${age.unsupportedSessionAge} candidate attachments outside XNYS calendar 2022–2030.`,
299
+ ]
300
+ : []),
301
+ ...(age.rejectedAttachments
302
+ ? [
303
+ `${field}: ${age.rejectedAttachments} attachments refused by maxMissedSessions; counted as uncovered.`,
304
+ ]
305
+ : []),
306
+ ]);
307
+ return Object.keys(selected).length
308
+ ? { datasetAge: selected, ...(datasetWarnings.length ? { datasetWarnings } : {}) }
309
+ : {};
236
310
  }