@usherlabs/cex-broker 0.2.50 → 0.2.52

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 (56) hide show
  1. package/dist/commands/cex-canonical-orderbook-export.js +7750 -920
  2. package/dist/commands/cex-canonical-orderbook-export.js.map +41 -10
  3. package/dist/commands/cli.js +219 -24
  4. package/dist/commands/market-data-vendor-backfill.js +8297 -6460
  5. package/dist/commands/market-data-vendor-backfill.js.map +16 -15
  6. package/dist/helpers/canonical-orderbook-export/parquet-projection.d.ts +19 -0
  7. package/dist/helpers/deposit-archive-poller.d.ts +33 -0
  8. package/dist/helpers/market-data-archive/canonical-orderbook.d.ts +1 -1
  9. package/dist/helpers/market-data-preparation/conformance-fixtures.d.ts +114 -2
  10. package/dist/helpers/market-data-preparation/contracts.d.ts +1298 -18
  11. package/dist/helpers/market-data-preparation/required-clock-qualification.d.ts +43 -0
  12. package/dist/helpers/market-data-preparation/source-tape-dependencies.d.ts +19 -0
  13. package/dist/helpers/market-data-preparation/source-tape-operation.d.ts +109 -0
  14. package/dist/helpers/market-data-source-forensics.d.ts +400 -0
  15. package/dist/helpers/market-data-vendor-backfill/conformance-fixtures.d.ts +3 -5
  16. package/dist/helpers/market-data-vendor-backfill/contracts.d.ts +21 -56
  17. package/dist/helpers/market-data-vendor-backfill/core.d.ts +1 -0
  18. package/dist/helpers/market-data-vendor-backfill/cryptohftdata.d.ts +44 -5
  19. package/dist/helpers/market-data-vendor-backfill/manifests.d.ts +33 -1112
  20. package/dist/helpers/market-data-vendor-backfill/promotion.d.ts +1 -0
  21. package/dist/helpers/source-tape-sandbox.d.ts +82 -0
  22. package/dist/helpers/source-tape.d.ts +165 -0
  23. package/dist/index.js +220 -25
  24. package/dist/index.js.map +4 -4
  25. package/dist/market-data-preparation/fixtures/{conformance-v2.json → conformance-v3.json} +253 -56
  26. package/dist/market-data-preparation/policies/capability-policy.json +4 -4
  27. package/dist/market-data-preparation/policies/source-tape-capability-v1.json +35 -0
  28. package/dist/market-data-preparation/schema-manifest.json +30 -10
  29. package/dist/market-data-preparation/schemas/canonical-orderbook-export-request-v1.schema.json +5 -1
  30. package/dist/market-data-preparation/schemas/{canonical-orderbook-export-result-v1.schema.json → canonical-orderbook-export-result-v2.schema.json} +20 -6
  31. package/dist/market-data-preparation/schemas/order-book-depth-summary-parquet-projection-v1.schema.json +256 -0
  32. package/dist/market-data-preparation/schemas/order-book-levels-parquet-projection-v1.schema.json +220 -0
  33. package/dist/market-data-preparation/schemas/{preparation-product-pin-v1.schema.json → preparation-product-pin-v2.schema.json} +59 -9
  34. package/dist/market-data-preparation/schemas/promotion-receipt-v1.schema.json +5 -1
  35. package/dist/market-data-preparation/schemas/source-forensics-ledger-v1.schema.json +463 -0
  36. package/dist/market-data-preparation/schemas/source-qualification-record-v1.schema.json +394 -0
  37. package/dist/market-data-preparation.d.ts +7 -0
  38. package/dist/market-data-preparation.js +35448 -25240
  39. package/dist/market-data-preparation.js.map +118 -78
  40. package/dist/market-data-vendor-backfill.d.ts +7 -4
  41. package/dist/market-data-vendor-backfill.js +19338 -15503
  42. package/dist/market-data-vendor-backfill.js.map +65 -60
  43. package/package.json +1 -5
  44. package/dist/market-data-preparation/policies/capability-policy-v1.json +0 -28
  45. package/dist/market-data-preparation/policies/resource-policy-v1.json +0 -16
  46. package/dist/market-data-vendor-backfill/fixtures/conformance-v1.json +0 -448
  47. package/dist/market-data-vendor-backfill/policies/capability-policy-v2.json +0 -39
  48. package/dist/market-data-vendor-backfill/policies/capability-policy.json +0 -28
  49. package/dist/market-data-vendor-backfill/policies/resource-policy-v2.json +0 -16
  50. package/dist/market-data-vendor-backfill/policies/resource-policy.json +0 -16
  51. package/dist/market-data-vendor-backfill/schema-manifest.json +0 -31
  52. package/dist/market-data-vendor-backfill/schemas/archive-selection.schema.json +0 -224
  53. package/dist/market-data-vendor-backfill/schemas/promotion-receipt.schema.json +0 -171
  54. package/dist/market-data-vendor-backfill/schemas/request.schema.json +0 -166
  55. package/dist/market-data-vendor-backfill/schemas/required-clock.schema.json +0 -47
  56. package/dist/market-data-vendor-backfill/schemas/result.schema.json +0 -198
@@ -317211,6 +317211,16 @@ function withTimeout(promise, timeoutMs, label) {
317211
317211
  return Promise.race([promise, expiry]).finally(() => clearTimeout(timer));
317212
317212
  }
317213
317213
  var ALL_CURRENCIES_CODE = "*";
317214
+ var BINANCE_UNLOCK_PROGRESS_SOURCE = {
317215
+ venue: "binance",
317216
+ endpoint: "GET /sapi/v1/capital/deposit/hisrec",
317217
+ fields: {
317218
+ status: "info.status",
317219
+ confirmTimes: "info.confirmTimes",
317220
+ unlockConfirm: "info.unlockConfirm",
317221
+ completeTime: "info.completeTime"
317222
+ }
317223
+ };
317214
317224
  function depositTimestamp(record) {
317215
317225
  const observedAt = depositField(record, [
317216
317226
  "timestamp",
@@ -317223,12 +317233,185 @@ function depositTimestamp(record) {
317223
317233
  const timestamp = typeof observedAt === "number" ? observedAt : typeof observedAt === "string" ? Date.parse(observedAt) : Number.NaN;
317224
317234
  return Number.isFinite(timestamp) ? timestamp : undefined;
317225
317235
  }
317226
- function archivedDepositStatus(exchangeId, record) {
317227
- const rawStatus = asRecord(record.info)?.status;
317228
- if (exchangeId?.toLowerCase() === "binance" && String(rawStatus ?? "").trim() === "6") {
317229
- return "credited_not_withdrawable";
317236
+ function parseNonNegativeInteger(value) {
317237
+ if (typeof value === "number") {
317238
+ return Number.isSafeInteger(value) && value >= 0 ? value : undefined;
317239
+ }
317240
+ if (typeof value !== "string") {
317241
+ return;
317242
+ }
317243
+ const trimmed = value.trim();
317244
+ if (!/^\d+$/.test(trimmed)) {
317245
+ return;
317246
+ }
317247
+ const parsed = Number(trimmed);
317248
+ return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : undefined;
317249
+ }
317250
+ function parseConfirmTimes(value) {
317251
+ if (value === undefined || value === null) {
317252
+ return { reason: "missing_confirmTimes" };
317253
+ }
317254
+ let currentValue;
317255
+ let creditRequiredValue;
317256
+ if (typeof value === "string") {
317257
+ const match = /^(\d+)\s*\/\s*(\d+)$/.exec(value.trim());
317258
+ if (!match) {
317259
+ return { reason: "invalid_confirmTimes" };
317260
+ }
317261
+ currentValue = match[1];
317262
+ creditRequiredValue = match[2];
317263
+ } else {
317264
+ const record = asRecord(value);
317265
+ if (!record) {
317266
+ return { reason: "invalid_confirmTimes" };
317267
+ }
317268
+ currentValue = record.current;
317269
+ creditRequiredValue = record.credit_required;
317270
+ }
317271
+ const current = parseNonNegativeInteger(currentValue);
317272
+ const creditRequired = parseNonNegativeInteger(creditRequiredValue);
317273
+ if (current === undefined || creditRequired === undefined) {
317274
+ return { reason: "invalid_confirmTimes" };
317275
+ }
317276
+ return { current, creditRequired, reason: null };
317277
+ }
317278
+ function parseOptionalNonNegativeInteger(value, fieldName) {
317279
+ if (value === undefined || value === null) {
317280
+ return { value: undefined, reason: null };
317281
+ }
317282
+ const parsed = parseNonNegativeInteger(value);
317283
+ return parsed === undefined ? { value: undefined, reason: `invalid_${fieldName}` } : { value: parsed, reason: null };
317284
+ }
317285
+ function parseBinanceUnlockProgress(record) {
317286
+ const info = asRecord(record.info);
317287
+ const nativeStatus = parseNonNegativeInteger(info?.status);
317288
+ const confirmations = parseConfirmTimes(info?.confirmTimes);
317289
+ const unlockRequired = parseOptionalNonNegativeInteger(info?.unlockConfirm, "unlockConfirm");
317290
+ const completeTime = parseOptionalNonNegativeInteger(info?.completeTime, "completeTime");
317291
+ let quality = "valid";
317292
+ let reason = null;
317293
+ if (nativeStatus === undefined) {
317294
+ quality = "unknown";
317295
+ reason = "missing_or_invalid_status";
317296
+ } else if (confirmations.reason !== null) {
317297
+ quality = "unknown";
317298
+ reason = confirmations.reason;
317299
+ } else if (unlockRequired.reason !== null) {
317300
+ quality = "unknown";
317301
+ reason = unlockRequired.reason;
317302
+ } else if (completeTime.reason !== null) {
317303
+ quality = "unknown";
317304
+ reason = completeTime.reason;
317305
+ }
317306
+ let state = "unknown";
317307
+ switch (nativeStatus) {
317308
+ case 0:
317309
+ state = "pending";
317310
+ break;
317311
+ case 1:
317312
+ if (quality === "valid" && confirmations.current !== undefined && unlockRequired.value !== undefined && confirmations.current >= unlockRequired.value) {
317313
+ state = "ok";
317314
+ } else if (quality === "valid") {
317315
+ state = "credited_not_withdrawable";
317316
+ }
317317
+ break;
317318
+ case 2:
317319
+ case 7:
317320
+ state = "failed";
317321
+ break;
317322
+ case 6:
317323
+ state = "credited_not_withdrawable";
317324
+ break;
317325
+ case 8:
317326
+ state = "pending";
317327
+ break;
317328
+ default:
317329
+ quality = "unknown";
317330
+ reason = "unsupported_status";
317331
+ break;
317332
+ }
317333
+ return {
317334
+ version: 1,
317335
+ state,
317336
+ progress_state: quality,
317337
+ reason,
317338
+ native_status: nativeStatus ?? null,
317339
+ current: confirmations.current ?? null,
317340
+ credit_required: confirmations.creditRequired ?? null,
317341
+ unlock_required: unlockRequired.value ?? null,
317342
+ complete_time: completeTime.value ?? null,
317343
+ observed_at: new Date().toISOString(),
317344
+ source: BINANCE_UNLOCK_PROGRESS_SOURCE
317345
+ };
317346
+ }
317347
+ function unlockProgressKey(progress) {
317348
+ return JSON.stringify({
317349
+ version: progress.version,
317350
+ state: progress.state,
317351
+ progress_state: progress.progress_state,
317352
+ reason: progress.reason,
317353
+ native_status: progress.native_status,
317354
+ current: progress.current,
317355
+ credit_required: progress.credit_required,
317356
+ unlock_required: progress.unlock_required,
317357
+ complete_time: progress.complete_time
317358
+ });
317359
+ }
317360
+ function progressWatermark(progress, previous) {
317361
+ if (progress.progress_state !== "valid" || progress.current === null || progress.credit_required === null || progress.unlock_required === null) {
317362
+ return previous;
317363
+ }
317364
+ return {
317365
+ current: Math.max(previous?.current ?? 0, progress.current),
317366
+ credit_required: progress.credit_required,
317367
+ unlock_required: progress.unlock_required
317368
+ };
317369
+ }
317370
+ function classifyBinanceDeposit(record, previous) {
317371
+ let progress = parseBinanceUnlockProgress(record);
317372
+ let highWatermark = previous?.highWatermark;
317373
+ if (progress.progress_state === "valid" && progress.current !== null && progress.credit_required !== null && progress.unlock_required !== null && previous?.highWatermark !== undefined && (progress.current < previous.highWatermark.current || progress.credit_required !== previous.highWatermark.credit_required || progress.unlock_required !== previous.highWatermark.unlock_required)) {
317374
+ progress = {
317375
+ ...progress,
317376
+ state: "unknown",
317377
+ progress_state: "contradictory",
317378
+ reason: "confirmation_progress_regressed_or_requirement_changed"
317379
+ };
317380
+ } else {
317381
+ highWatermark = progressWatermark(progress, previous?.highWatermark);
317382
+ }
317383
+ return {
317384
+ archiveStatus: progress.state,
317385
+ holdCursor: progress.state === "pending" || progress.state === "credited_not_withdrawable" || progress.state === "unknown",
317386
+ unlockProgress: progress,
317387
+ progressKey: unlockProgressKey(progress),
317388
+ highWatermark
317389
+ };
317390
+ }
317391
+ function classifyDeposit(exchangeId, record, previous) {
317392
+ if (exchangeId?.toLowerCase() === "binance") {
317393
+ return classifyBinanceDeposit(record, previous);
317394
+ }
317395
+ const archiveStatus = normalizeCcxtTransactionForArchive(record).status ?? "";
317396
+ const status = normalizeDepositStatus(depositField(record, ["status", "state"]));
317397
+ return {
317398
+ archiveStatus,
317399
+ holdCursor: archiveStatus === "credited_not_withdrawable" || status === "pending",
317400
+ progressKey: JSON.stringify({ status: archiveStatus })
317401
+ };
317402
+ }
317403
+ function depositIdentity(input) {
317404
+ if (input.externalId === undefined && input.txid === undefined) {
317405
+ return;
317230
317406
  }
317231
- return normalizeCcxtTransactionForArchive(record).status;
317407
+ return JSON.stringify({
317408
+ exchange: input.exchangeId,
317409
+ account: input.accountSelector,
317410
+ coin: input.coin === undefined ? "" : String(input.coin),
317411
+ network: input.network === undefined ? "" : String(input.network),
317412
+ external_id: input.externalId ?? "",
317413
+ txid: input.txid ?? ""
317414
+ });
317232
317415
  }
317233
317416
  function nextDepositCursor(deposits, currentSince, depositsLimit, exchangeId) {
317234
317417
  if (deposits.length >= depositsLimit) {
@@ -317242,9 +317425,8 @@ function nextDepositCursor(deposits, currentSince, depositsLimit, exchangeId) {
317242
317425
  return currentSince;
317243
317426
  }
317244
317427
  const timestamp = depositTimestamp(record);
317245
- const archiveStatus = archivedDepositStatus(exchangeId, record);
317246
- const status = normalizeDepositStatus(depositField(record, ["status", "state"]));
317247
- const isPending = archiveStatus === "credited_not_withdrawable" || status === "pending";
317428
+ const classification = classifyDeposit(exchangeId, record);
317429
+ const isPending = classification.holdCursor;
317248
317430
  if (timestamp === undefined) {
317249
317431
  if (isPending) {
317250
317432
  return currentSince;
@@ -317364,7 +317546,8 @@ class DepositArchivePoller {
317364
317546
  if (!record) {
317365
317547
  continue;
317366
317548
  }
317367
- const assetSymbol = depositField(record, ["currency", "code", "asset"]);
317549
+ const info = asRecord(record.info);
317550
+ const assetSymbol = depositField(record, ["currency", "code", "asset"]) ?? info?.coin;
317368
317551
  const amount = depositField(record, ["amount"]);
317369
317552
  const address = depositField(record, [
317370
317553
  "address",
@@ -317373,8 +317556,21 @@ class DepositArchivePoller {
317373
317556
  "destination"
317374
317557
  ]);
317375
317558
  const txid = depositField(record, ["txid", "txId", "tx_hash", "txHash"]);
317376
- const network = depositField(record, ["network", "chain"]);
317377
- const archiveStatus = archivedDepositStatus(target.exchangeId, record);
317559
+ const network = depositField(record, ["network", "chain"]) ?? info?.network;
317560
+ const depositTxid = txid === undefined ? undefined : String(txid);
317561
+ const identity = depositIdentity({
317562
+ exchangeId: target.exchangeId,
317563
+ accountSelector: target.account.label,
317564
+ coin: assetSymbol,
317565
+ network,
317566
+ externalId: depositTxid,
317567
+ txid: depositTxid
317568
+ });
317569
+ const lastArchived = identity === undefined ? undefined : this.#lastArchivedByTarget.get(key)?.get(identity);
317570
+ const classification = classifyDeposit(target.exchangeId, record, lastArchived);
317571
+ if (lastArchived && lastArchived.status === classification.archiveStatus && lastArchived.progressKey === classification.progressKey) {
317572
+ continue;
317573
+ }
317378
317574
  const creditedAt = depositField(record, [
317379
317575
  "creditedAt",
317380
317576
  "credited_at",
@@ -317383,11 +317579,7 @@ class DepositArchivePoller {
317383
317579
  "timestamp",
317384
317580
  "datetime"
317385
317581
  ]);
317386
- const depositTxid = txid === undefined ? undefined : String(txid);
317387
- const lastArchived = depositTxid === undefined ? undefined : this.#lastArchivedByTarget.get(key)?.get(depositTxid);
317388
- if (lastArchived && lastArchived.status === archiveStatus) {
317389
- continue;
317390
- }
317582
+ const payload = classification.unlockProgress === undefined ? record : { ...record, unlock_progress: classification.unlockProgress };
317391
317583
  this.params.archiver.enqueue(buildTransferEventArchiveRow({
317392
317584
  tags: buildCommonArchiveTags({
317393
317585
  deploymentId: this.params.archiver.getDeploymentId(),
@@ -317398,25 +317590,27 @@ class DepositArchivePoller {
317398
317590
  transfer: {
317399
317591
  eventKind: "deposit",
317400
317592
  lifecycleAction: "observe_deposit",
317401
- status: archiveStatus,
317593
+ status: classification.archiveStatus,
317402
317594
  amount: amount === undefined ? undefined : String(amount),
317403
317595
  address: address === undefined ? undefined : String(address),
317404
317596
  network: network === undefined ? undefined : String(network),
317405
317597
  externalId: depositTxid,
317406
317598
  txid: depositTxid,
317407
317599
  exchangeTimestamp: normalizeTimestamp(creditedAt),
317408
- payload: record
317600
+ payload
317409
317601
  }
317410
317602
  }));
317411
- if (depositTxid !== undefined) {
317603
+ if (identity !== undefined) {
317412
317604
  let targetDeposits2 = this.#lastArchivedByTarget.get(key);
317413
317605
  if (!targetDeposits2) {
317414
317606
  targetDeposits2 = new Map;
317415
317607
  this.#lastArchivedByTarget.set(key, targetDeposits2);
317416
317608
  }
317417
- targetDeposits2.set(depositTxid, {
317418
- status: archiveStatus,
317419
- timestamp: depositTimestamp(record)
317609
+ targetDeposits2.set(identity, {
317610
+ status: classification.archiveStatus,
317611
+ timestamp: depositTimestamp(record),
317612
+ progressKey: classification.progressKey,
317613
+ highWatermark: classification.highWatermark
317420
317614
  });
317421
317615
  }
317422
317616
  archived += 1;
@@ -318611,7 +318805,7 @@ function commonEvidenceFields(context2, rawCapture, input) {
318611
318805
  source_time_ms: input.eventTimeMs,
318612
318806
  received_time_ms: input.receivedTimeMs,
318613
318807
  snapshot_id: input.snapshotId,
318614
- construction_mode: "sampled_top_n_snapshot",
318808
+ construction_mode: input.constructionMode,
318615
318809
  gap_policy: "record_gap",
318616
318810
  depth_limit: input.depthLimit,
318617
318811
  sequence: input.sequence,
@@ -318665,7 +318859,8 @@ function buildCanonicalOrderBookRows(input) {
318665
318859
  sequence,
318666
318860
  depthLimit: input.depthLimit,
318667
318861
  eventTimeMs,
318668
- receivedTimeMs
318862
+ receivedTimeMs,
318863
+ constructionMode: input.constructionMode ?? "sampled_top_n_snapshot"
318669
318864
  });
318670
318865
  const levels = [
318671
318866
  ["bid", bids],