@usherlabs/cex-broker 0.2.51 → 0.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 (144) hide show
  1. package/dist/commands/cli.js +580 -93
  2. package/dist/index.js +581 -94
  3. package/dist/index.js.map +15 -14
  4. package/package.json +5 -28
  5. package/dist/commands/cex-canonical-orderbook-export.d.ts +0 -23
  6. package/dist/commands/cex-canonical-orderbook-export.js +0 -34275
  7. package/dist/commands/cex-canonical-orderbook-export.js.map +0 -266
  8. package/dist/commands/market-data-vendor-backfill.d.ts +0 -24
  9. package/dist/commands/market-data-vendor-backfill.js +0 -34930
  10. package/dist/commands/market-data-vendor-backfill.js.map +0 -268
  11. package/dist/handlers/execute-action/context.d.ts +0 -50
  12. package/dist/handlers/execute-action/deposit.d.ts +0 -2
  13. package/dist/handlers/execute-action/handler.d.ts +0 -19
  14. package/dist/handlers/execute-action/index.d.ts +0 -10
  15. package/dist/handlers/execute-action/internal-transfer.d.ts +0 -2
  16. package/dist/handlers/execute-action/order-book-call.d.ts +0 -3
  17. package/dist/handlers/execute-action/orders.d.ts +0 -2
  18. package/dist/handlers/execute-action/pass-through.d.ts +0 -2
  19. package/dist/handlers/execute-action/perp-config.d.ts +0 -2
  20. package/dist/handlers/execute-action/registry.d.ts +0 -5
  21. package/dist/handlers/execute-action/treasury-call.d.ts +0 -2
  22. package/dist/handlers/execute-action/withdraw.d.ts +0 -2
  23. package/dist/handlers/subscribe/broker-lifecycle.d.ts +0 -13
  24. package/dist/handlers/subscribe/handler.d.ts +0 -20
  25. package/dist/handlers/subscribe/index.d.ts +0 -2
  26. package/dist/handlers/types.d.ts +0 -23
  27. package/dist/helpers/account-balance-archive-poller.d.ts +0 -19
  28. package/dist/helpers/auth.d.ts +0 -5
  29. package/dist/helpers/balance-update-archive-consumer.d.ts +0 -16
  30. package/dist/helpers/binance-user-data-normalization.d.ts +0 -3
  31. package/dist/helpers/binance-user-data-stream.d.ts +0 -58
  32. package/dist/helpers/broker-execution-archive/capture.d.ts +0 -41
  33. package/dist/helpers/broker-execution-archive/index.d.ts +0 -7
  34. package/dist/helpers/broker-execution-archive/journal-export.d.ts +0 -20
  35. package/dist/helpers/broker-execution-archive/loss-journal.d.ts +0 -36
  36. package/dist/helpers/broker-execution-archive/redact.d.ts +0 -7
  37. package/dist/helpers/broker-execution-archive/rows.d.ts +0 -134
  38. package/dist/helpers/broker-execution-archive/types.d.ts +0 -26
  39. package/dist/helpers/broker-execution-archive/withdrawal-observation-tracker.d.ts +0 -23
  40. package/dist/helpers/broker-execution-archive/writer.d.ts +0 -105
  41. package/dist/helpers/broker.d.ts +0 -38
  42. package/dist/helpers/canonical-orderbook-export/exact-selection.d.ts +0 -21
  43. package/dist/helpers/canonical-orderbook-export/exporter.d.ts +0 -26
  44. package/dist/helpers/canonical-orderbook-export/parquet-projection.d.ts +0 -19
  45. package/dist/helpers/constants.d.ts +0 -36
  46. package/dist/helpers/deposit-archive-poller.d.ts +0 -33
  47. package/dist/helpers/deposit.d.ts +0 -5
  48. package/dist/helpers/exchange-credentials.d.ts +0 -10
  49. package/dist/helpers/fill-archive-poller.d.ts +0 -42
  50. package/dist/helpers/grpc/broker.d.ts +0 -5
  51. package/dist/helpers/grpc/callbacks.d.ts +0 -9
  52. package/dist/helpers/grpc/payload.d.ts +0 -9
  53. package/dist/helpers/grpc/status.d.ts +0 -8
  54. package/dist/helpers/index.d.ts +0 -44
  55. package/dist/helpers/logger.d.ts +0 -13
  56. package/dist/helpers/market-data-archive/canonical-orderbook.d.ts +0 -20
  57. package/dist/helpers/market-data-archive/capture-context.d.ts +0 -53
  58. package/dist/helpers/market-data-archive/capture-contract.d.ts +0 -30
  59. package/dist/helpers/market-data-archive/capture.d.ts +0 -22
  60. package/dist/helpers/market-data-archive/index.d.ts +0 -13
  61. package/dist/helpers/market-data-archive/legacy-migration.d.ts +0 -37
  62. package/dist/helpers/market-data-archive/ohlcv-bar-tracker.d.ts +0 -11
  63. package/dist/helpers/market-data-archive/ohlcv-bootstrap.d.ts +0 -1
  64. package/dist/helpers/market-data-archive/ohlcv-history.d.ts +0 -8
  65. package/dist/helpers/market-data-archive/orderbook-depth.d.ts +0 -5
  66. package/dist/helpers/market-data-archive/orderbook-sampler.d.ts +0 -12
  67. package/dist/helpers/market-data-archive/parse-stream.d.ts +0 -26
  68. package/dist/helpers/market-data-archive/rows.d.ts +0 -14
  69. package/dist/helpers/market-data-archive/types.d.ts +0 -83
  70. package/dist/helpers/market-data-preparation/conformance-fixtures.d.ts +0 -128
  71. package/dist/helpers/market-data-preparation/contracts.d.ts +0 -2970
  72. package/dist/helpers/market-data-preparation/file-job.d.ts +0 -38
  73. package/dist/helpers/market-data-preparation/required-clock-qualification.d.ts +0 -43
  74. package/dist/helpers/market-data-preparation/source-tape-dependencies.d.ts +0 -19
  75. package/dist/helpers/market-data-preparation/source-tape-operation.d.ts +0 -109
  76. package/dist/helpers/market-data-source-forensics.d.ts +0 -400
  77. package/dist/helpers/market-data-vendor-backfill/archive-reader.d.ts +0 -26
  78. package/dist/helpers/market-data-vendor-backfill/batching.d.ts +0 -10
  79. package/dist/helpers/market-data-vendor-backfill/conformance-fixtures.d.ts +0 -78
  80. package/dist/helpers/market-data-vendor-backfill/contracts.d.ts +0 -358
  81. package/dist/helpers/market-data-vendor-backfill/core.d.ts +0 -101
  82. package/dist/helpers/market-data-vendor-backfill/cryptohftdata.d.ts +0 -118
  83. package/dist/helpers/market-data-vendor-backfill/forwarder-client.d.ts +0 -25
  84. package/dist/helpers/market-data-vendor-backfill/identity.d.ts +0 -10
  85. package/dist/helpers/market-data-vendor-backfill/manifests.d.ts +0 -61
  86. package/dist/helpers/market-data-vendor-backfill/promotion.d.ts +0 -10
  87. package/dist/helpers/market-data-vendor-backfill/qualification.d.ts +0 -20
  88. package/dist/helpers/market-data-vendor-backfill/selection.d.ts +0 -30
  89. package/dist/helpers/market-data-vendor-backfill/semantic-verification.d.ts +0 -25
  90. package/dist/helpers/market-type.d.ts +0 -16
  91. package/dist/helpers/order-activity-tracker.d.ts +0 -22
  92. package/dist/helpers/order-book.d.ts +0 -79
  93. package/dist/helpers/order-telemetry.d.ts +0 -50
  94. package/dist/helpers/otel.d.ts +0 -83
  95. package/dist/helpers/passive-order.d.ts +0 -8
  96. package/dist/helpers/public-market-data-feed/identity.d.ts +0 -12
  97. package/dist/helpers/public-market-data-feed/index.d.ts +0 -5
  98. package/dist/helpers/public-market-data-feed/orderbook-coalescing-evidence.d.ts +0 -79
  99. package/dist/helpers/public-market-data-feed/orderbook-profile.d.ts +0 -54
  100. package/dist/helpers/public-market-data-feed/subscriber-buffer.d.ts +0 -31
  101. package/dist/helpers/public-market-data-feed/supervisor.d.ts +0 -82
  102. package/dist/helpers/shared/errors.d.ts +0 -14
  103. package/dist/helpers/shared/guards.d.ts +0 -2
  104. package/dist/helpers/source-tape-sandbox.d.ts +0 -82
  105. package/dist/helpers/source-tape.d.ts +0 -165
  106. package/dist/helpers/stream-health-publisher.d.ts +0 -41
  107. package/dist/helpers/trace-context.d.ts +0 -8
  108. package/dist/helpers/transfer-network.d.ts +0 -12
  109. package/dist/helpers/travel-rule-deposit-reconciler.d.ts +0 -159
  110. package/dist/helpers/travel-rule.d.ts +0 -75
  111. package/dist/helpers/treasury-discovery.d.ts +0 -17
  112. package/dist/helpers/user-asset-archive-poller.d.ts +0 -19
  113. package/dist/helpers/user-data-stream-supervisor.d.ts +0 -27
  114. package/dist/helpers/verity.d.ts +0 -5
  115. package/dist/index.d.ts +0 -56
  116. package/dist/market-data-preparation/fixtures/conformance-v3.json +0 -621
  117. package/dist/market-data-preparation/policies/capability-policy.json +0 -39
  118. package/dist/market-data-preparation/policies/resource-policy.json +0 -16
  119. package/dist/market-data-preparation/policies/source-tape-capability-v1.json +0 -35
  120. package/dist/market-data-preparation/schema-manifest.json +0 -66
  121. package/dist/market-data-preparation/schemas/archive-selection-v1.schema.json +0 -224
  122. package/dist/market-data-preparation/schemas/backfill-request-v1.schema.json +0 -166
  123. package/dist/market-data-preparation/schemas/backfill-result-v2.schema.json +0 -212
  124. package/dist/market-data-preparation/schemas/canonical-orderbook-export-request-v1.schema.json +0 -61
  125. package/dist/market-data-preparation/schemas/canonical-orderbook-export-result-v2.schema.json +0 -252
  126. package/dist/market-data-preparation/schemas/order-book-depth-summary-parquet-projection-v1.schema.json +0 -256
  127. package/dist/market-data-preparation/schemas/order-book-levels-parquet-projection-v1.schema.json +0 -220
  128. package/dist/market-data-preparation/schemas/preparation-product-pin-v2.schema.json +0 -176
  129. package/dist/market-data-preparation/schemas/promotion-receipt-v1.schema.json +0 -175
  130. package/dist/market-data-preparation/schemas/required-clock-v1.schema.json +0 -47
  131. package/dist/market-data-preparation/schemas/source-forensics-ledger-v1.schema.json +0 -463
  132. package/dist/market-data-preparation/schemas/source-qualification-record-v1.schema.json +0 -394
  133. package/dist/market-data-preparation.d.ts +0 -11
  134. package/dist/market-data-preparation.js +0 -37488
  135. package/dist/market-data-preparation.js.map +0 -277
  136. package/dist/market-data-vendor-backfill.d.ts +0 -21
  137. package/dist/market-data-vendor-backfill.js +0 -22463
  138. package/dist/market-data-vendor-backfill.js.map +0 -199
  139. package/dist/proto/node.descriptor.d.ts +0 -127
  140. package/dist/proto-loader-options.d.ts +0 -7
  141. package/dist/proto-package-definition.d.ts +0 -2
  142. package/dist/schemas/action-payloads.d.ts +0 -88
  143. package/dist/server.d.ts +0 -10
  144. package/dist/types.d.ts +0 -100
@@ -315838,6 +315838,22 @@ import {
315838
315838
  import { basename, dirname } from "node:path";
315839
315839
  var EXPORT_CHUNK_BYTES = 8 * 1024 * 1024;
315840
315840
  var SHA256_RECEIPT_PATTERN = /^([0-9a-f]{64}) {2}(.+)\n$/;
315841
+ function sha256File(path) {
315842
+ const hash4 = createHash3("sha256");
315843
+ const fd2 = openSync(path, "r");
315844
+ try {
315845
+ const chunk = Buffer.alloc(EXPORT_CHUNK_BYTES);
315846
+ for (;; ) {
315847
+ const read = readSync(fd2, chunk, 0, chunk.length, null);
315848
+ if (read === 0)
315849
+ break;
315850
+ hash4.update(chunk.subarray(0, read));
315851
+ }
315852
+ } finally {
315853
+ closeSync(fd2);
315854
+ }
315855
+ return hash4.digest("hex");
315856
+ }
315841
315857
 
315842
315858
  class DeadLetterJournalExportError extends Error {
315843
315859
  constructor(message, options) {
@@ -315871,6 +315887,9 @@ function exportDeadLetterJournal(journalPath, exportPath) {
315871
315887
  if (match?.[1] === undefined || match[2] !== basename(exportPath)) {
315872
315888
  throw new Error("export receipt is malformed");
315873
315889
  }
315890
+ if (sha256File(exportPath) !== match[1]) {
315891
+ throw new Error("export digest does not match receipt");
315892
+ }
315874
315893
  } catch (error) {
315875
315894
  throw new DeadLetterJournalExportError("Dead-letter journal export target is incomplete or unverifiable", { cause: error });
315876
315895
  }
@@ -317211,6 +317230,16 @@ function withTimeout(promise, timeoutMs, label) {
317211
317230
  return Promise.race([promise, expiry]).finally(() => clearTimeout(timer));
317212
317231
  }
317213
317232
  var ALL_CURRENCIES_CODE = "*";
317233
+ var BINANCE_UNLOCK_PROGRESS_SOURCE = {
317234
+ venue: "binance",
317235
+ endpoint: "GET /sapi/v1/capital/deposit/hisrec",
317236
+ fields: {
317237
+ status: "info.status",
317238
+ confirmTimes: "info.confirmTimes",
317239
+ unlockConfirm: "info.unlockConfirm",
317240
+ completeTime: "info.completeTime"
317241
+ }
317242
+ };
317214
317243
  function depositTimestamp(record) {
317215
317244
  const observedAt = depositField(record, [
317216
317245
  "timestamp",
@@ -317223,12 +317252,185 @@ function depositTimestamp(record) {
317223
317252
  const timestamp = typeof observedAt === "number" ? observedAt : typeof observedAt === "string" ? Date.parse(observedAt) : Number.NaN;
317224
317253
  return Number.isFinite(timestamp) ? timestamp : undefined;
317225
317254
  }
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";
317255
+ function parseNonNegativeInteger(value) {
317256
+ if (typeof value === "number") {
317257
+ return Number.isSafeInteger(value) && value >= 0 ? value : undefined;
317258
+ }
317259
+ if (typeof value !== "string") {
317260
+ return;
317261
+ }
317262
+ const trimmed = value.trim();
317263
+ if (!/^\d+$/.test(trimmed)) {
317264
+ return;
317265
+ }
317266
+ const parsed = Number(trimmed);
317267
+ return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : undefined;
317268
+ }
317269
+ function parseConfirmTimes(value) {
317270
+ if (value === undefined || value === null) {
317271
+ return { reason: "missing_confirmTimes" };
317272
+ }
317273
+ let currentValue;
317274
+ let creditRequiredValue;
317275
+ if (typeof value === "string") {
317276
+ const match = /^(\d+)\s*\/\s*(\d+)$/.exec(value.trim());
317277
+ if (!match) {
317278
+ return { reason: "invalid_confirmTimes" };
317279
+ }
317280
+ currentValue = match[1];
317281
+ creditRequiredValue = match[2];
317282
+ } else {
317283
+ const record = asRecord(value);
317284
+ if (!record) {
317285
+ return { reason: "invalid_confirmTimes" };
317286
+ }
317287
+ currentValue = record.current;
317288
+ creditRequiredValue = record.credit_required;
317289
+ }
317290
+ const current = parseNonNegativeInteger(currentValue);
317291
+ const creditRequired = parseNonNegativeInteger(creditRequiredValue);
317292
+ if (current === undefined || creditRequired === undefined) {
317293
+ return { reason: "invalid_confirmTimes" };
317230
317294
  }
317231
- return normalizeCcxtTransactionForArchive(record).status;
317295
+ return { current, creditRequired, reason: null };
317296
+ }
317297
+ function parseOptionalNonNegativeInteger(value, fieldName) {
317298
+ if (value === undefined || value === null) {
317299
+ return { value: undefined, reason: null };
317300
+ }
317301
+ const parsed = parseNonNegativeInteger(value);
317302
+ return parsed === undefined ? { value: undefined, reason: `invalid_${fieldName}` } : { value: parsed, reason: null };
317303
+ }
317304
+ function parseBinanceUnlockProgress(record) {
317305
+ const info = asRecord(record.info);
317306
+ const nativeStatus = parseNonNegativeInteger(info?.status);
317307
+ const confirmations = parseConfirmTimes(info?.confirmTimes);
317308
+ const unlockRequired = parseOptionalNonNegativeInteger(info?.unlockConfirm, "unlockConfirm");
317309
+ const completeTime = parseOptionalNonNegativeInteger(info?.completeTime, "completeTime");
317310
+ let quality = "valid";
317311
+ let reason = null;
317312
+ if (nativeStatus === undefined) {
317313
+ quality = "unknown";
317314
+ reason = "missing_or_invalid_status";
317315
+ } else if (confirmations.reason !== null) {
317316
+ quality = "unknown";
317317
+ reason = confirmations.reason;
317318
+ } else if (unlockRequired.reason !== null) {
317319
+ quality = "unknown";
317320
+ reason = unlockRequired.reason;
317321
+ } else if (completeTime.reason !== null) {
317322
+ quality = "unknown";
317323
+ reason = completeTime.reason;
317324
+ }
317325
+ let state = "unknown";
317326
+ switch (nativeStatus) {
317327
+ case 0:
317328
+ state = "pending";
317329
+ break;
317330
+ case 1:
317331
+ if (quality === "valid" && confirmations.current !== undefined && unlockRequired.value !== undefined && confirmations.current >= unlockRequired.value) {
317332
+ state = "ok";
317333
+ } else if (quality === "valid") {
317334
+ state = "credited_not_withdrawable";
317335
+ }
317336
+ break;
317337
+ case 2:
317338
+ case 7:
317339
+ state = "failed";
317340
+ break;
317341
+ case 6:
317342
+ state = "credited_not_withdrawable";
317343
+ break;
317344
+ case 8:
317345
+ state = "pending";
317346
+ break;
317347
+ default:
317348
+ quality = "unknown";
317349
+ reason = "unsupported_status";
317350
+ break;
317351
+ }
317352
+ return {
317353
+ version: 1,
317354
+ state,
317355
+ progress_state: quality,
317356
+ reason,
317357
+ native_status: nativeStatus ?? null,
317358
+ current: confirmations.current ?? null,
317359
+ credit_required: confirmations.creditRequired ?? null,
317360
+ unlock_required: unlockRequired.value ?? null,
317361
+ complete_time: completeTime.value ?? null,
317362
+ observed_at: new Date().toISOString(),
317363
+ source: BINANCE_UNLOCK_PROGRESS_SOURCE
317364
+ };
317365
+ }
317366
+ function unlockProgressKey(progress) {
317367
+ return JSON.stringify({
317368
+ version: progress.version,
317369
+ state: progress.state,
317370
+ progress_state: progress.progress_state,
317371
+ reason: progress.reason,
317372
+ native_status: progress.native_status,
317373
+ current: progress.current,
317374
+ credit_required: progress.credit_required,
317375
+ unlock_required: progress.unlock_required,
317376
+ complete_time: progress.complete_time
317377
+ });
317378
+ }
317379
+ function progressWatermark(progress, previous) {
317380
+ if (progress.progress_state !== "valid" || progress.current === null || progress.credit_required === null || progress.unlock_required === null) {
317381
+ return previous;
317382
+ }
317383
+ return {
317384
+ current: Math.max(previous?.current ?? 0, progress.current),
317385
+ credit_required: progress.credit_required,
317386
+ unlock_required: progress.unlock_required
317387
+ };
317388
+ }
317389
+ function classifyBinanceDeposit(record, previous) {
317390
+ let progress = parseBinanceUnlockProgress(record);
317391
+ let highWatermark = previous?.highWatermark;
317392
+ 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)) {
317393
+ progress = {
317394
+ ...progress,
317395
+ state: "unknown",
317396
+ progress_state: "contradictory",
317397
+ reason: "confirmation_progress_regressed_or_requirement_changed"
317398
+ };
317399
+ } else {
317400
+ highWatermark = progressWatermark(progress, previous?.highWatermark);
317401
+ }
317402
+ return {
317403
+ archiveStatus: progress.state,
317404
+ holdCursor: progress.state === "pending" || progress.state === "credited_not_withdrawable" || progress.state === "unknown",
317405
+ unlockProgress: progress,
317406
+ progressKey: unlockProgressKey(progress),
317407
+ highWatermark
317408
+ };
317409
+ }
317410
+ function classifyDeposit(exchangeId, record, previous) {
317411
+ if (exchangeId?.toLowerCase() === "binance") {
317412
+ return classifyBinanceDeposit(record, previous);
317413
+ }
317414
+ const archiveStatus = normalizeCcxtTransactionForArchive(record).status ?? "";
317415
+ const status = normalizeDepositStatus(depositField(record, ["status", "state"]));
317416
+ return {
317417
+ archiveStatus,
317418
+ holdCursor: archiveStatus === "credited_not_withdrawable" || status === "pending",
317419
+ progressKey: JSON.stringify({ status: archiveStatus })
317420
+ };
317421
+ }
317422
+ function depositIdentity(input) {
317423
+ if (input.externalId === undefined && input.txid === undefined) {
317424
+ return;
317425
+ }
317426
+ return JSON.stringify({
317427
+ exchange: input.exchangeId,
317428
+ account: input.accountSelector,
317429
+ coin: input.coin === undefined ? "" : String(input.coin),
317430
+ network: input.network === undefined ? "" : String(input.network),
317431
+ external_id: input.externalId ?? "",
317432
+ txid: input.txid ?? ""
317433
+ });
317232
317434
  }
317233
317435
  function nextDepositCursor(deposits, currentSince, depositsLimit, exchangeId) {
317234
317436
  if (deposits.length >= depositsLimit) {
@@ -317242,9 +317444,8 @@ function nextDepositCursor(deposits, currentSince, depositsLimit, exchangeId) {
317242
317444
  return currentSince;
317243
317445
  }
317244
317446
  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";
317447
+ const classification = classifyDeposit(exchangeId, record);
317448
+ const isPending = classification.holdCursor;
317248
317449
  if (timestamp === undefined) {
317249
317450
  if (isPending) {
317250
317451
  return currentSince;
@@ -317364,7 +317565,8 @@ class DepositArchivePoller {
317364
317565
  if (!record) {
317365
317566
  continue;
317366
317567
  }
317367
- const assetSymbol = depositField(record, ["currency", "code", "asset"]);
317568
+ const info = asRecord(record.info);
317569
+ const assetSymbol = depositField(record, ["currency", "code", "asset"]) ?? info?.coin;
317368
317570
  const amount = depositField(record, ["amount"]);
317369
317571
  const address = depositField(record, [
317370
317572
  "address",
@@ -317373,8 +317575,21 @@ class DepositArchivePoller {
317373
317575
  "destination"
317374
317576
  ]);
317375
317577
  const txid = depositField(record, ["txid", "txId", "tx_hash", "txHash"]);
317376
- const network = depositField(record, ["network", "chain"]);
317377
- const archiveStatus = archivedDepositStatus(target.exchangeId, record);
317578
+ const network = depositField(record, ["network", "chain"]) ?? info?.network;
317579
+ const depositTxid = txid === undefined ? undefined : String(txid);
317580
+ const identity = depositIdentity({
317581
+ exchangeId: target.exchangeId,
317582
+ accountSelector: target.account.label,
317583
+ coin: assetSymbol,
317584
+ network,
317585
+ externalId: depositTxid,
317586
+ txid: depositTxid
317587
+ });
317588
+ const lastArchived = identity === undefined ? undefined : this.#lastArchivedByTarget.get(key)?.get(identity);
317589
+ const classification = classifyDeposit(target.exchangeId, record, lastArchived);
317590
+ if (lastArchived && lastArchived.status === classification.archiveStatus && lastArchived.progressKey === classification.progressKey) {
317591
+ continue;
317592
+ }
317378
317593
  const creditedAt = depositField(record, [
317379
317594
  "creditedAt",
317380
317595
  "credited_at",
@@ -317383,11 +317598,7 @@ class DepositArchivePoller {
317383
317598
  "timestamp",
317384
317599
  "datetime"
317385
317600
  ]);
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
- }
317601
+ const payload = classification.unlockProgress === undefined ? record : { ...record, unlock_progress: classification.unlockProgress };
317391
317602
  this.params.archiver.enqueue(buildTransferEventArchiveRow({
317392
317603
  tags: buildCommonArchiveTags({
317393
317604
  deploymentId: this.params.archiver.getDeploymentId(),
@@ -317398,25 +317609,27 @@ class DepositArchivePoller {
317398
317609
  transfer: {
317399
317610
  eventKind: "deposit",
317400
317611
  lifecycleAction: "observe_deposit",
317401
- status: archiveStatus,
317612
+ status: classification.archiveStatus,
317402
317613
  amount: amount === undefined ? undefined : String(amount),
317403
317614
  address: address === undefined ? undefined : String(address),
317404
317615
  network: network === undefined ? undefined : String(network),
317405
317616
  externalId: depositTxid,
317406
317617
  txid: depositTxid,
317407
317618
  exchangeTimestamp: normalizeTimestamp(creditedAt),
317408
- payload: record
317619
+ payload
317409
317620
  }
317410
317621
  }));
317411
- if (depositTxid !== undefined) {
317622
+ if (identity !== undefined) {
317412
317623
  let targetDeposits2 = this.#lastArchivedByTarget.get(key);
317413
317624
  if (!targetDeposits2) {
317414
317625
  targetDeposits2 = new Map;
317415
317626
  this.#lastArchivedByTarget.set(key, targetDeposits2);
317416
317627
  }
317417
- targetDeposits2.set(depositTxid, {
317418
- status: archiveStatus,
317419
- timestamp: depositTimestamp(record)
317628
+ targetDeposits2.set(identity, {
317629
+ status: classification.archiveStatus,
317630
+ timestamp: depositTimestamp(record),
317631
+ progressKey: classification.progressKey,
317632
+ highWatermark: classification.highWatermark
317420
317633
  });
317421
317634
  }
317422
317635
  archived += 1;
@@ -317584,10 +317797,7 @@ import { createHash as createHash4 } from "node:crypto";
317584
317797
  var MARKET_CAPTURE_SCHEMA_VERSION = "1.0.0";
317585
317798
  var CHECKSUM_ALGORITHM = "sha256-canonical-json-v1";
317586
317799
  var ARCHIVE_SOURCES = ["broker_read", "broker_write"];
317587
- var MARKET_ARCHIVE_SOURCES = [
317588
- ...ARCHIVE_SOURCES,
317589
- "external_backfill"
317590
- ];
317800
+ var MARKET_ARCHIVE_SOURCES = ARCHIVE_SOURCES;
317591
317801
  var CAPTURE_FEEDS = [
317592
317802
  "ORDERBOOK",
317593
317803
  "TICKER",
@@ -317601,15 +317811,12 @@ var SOURCE_MODES = [
317601
317811
  "broker_bootstrap_fetch_v1",
317602
317812
  "external_ccxt_fallback_v1",
317603
317813
  "external_hummingbot_fallback_v1",
317604
- "legacy_migration_v1",
317605
- "historical_vendor_orderbook_v1",
317606
- "vendor_historical_backfill_v1"
317814
+ "legacy_migration_v1"
317607
317815
  ];
317608
317816
  var RAW_CAPTURE_SCOPES = [
317609
317817
  "ccxt_normalized_object",
317610
317818
  "broker_visible_payload",
317611
- "exchange_wire_frame",
317612
- "vendor_normalized_dataset_file"
317819
+ "exchange_wire_frame"
317613
317820
  ];
317614
317821
  var CHECKSUM_FIELDS = new Set([
317615
317822
  "normalized_row_checksum",
@@ -317798,6 +318005,9 @@ function createRawCapture(context2, input) {
317798
318005
  checksumAlgorithm: context2.checksumAlgorithm
317799
318006
  };
317800
318007
  }
318008
+ function projectRawCapturePayload(rawCapture, redactedPayload) {
318009
+ return { ...rawCapture, redactedPayload };
318010
+ }
317801
318011
  function captureCoreFields(context2, rawCapture) {
317802
318012
  assertCaptureContext(context2);
317803
318013
  return {
@@ -318374,14 +318584,20 @@ function resolveConservativeOrderBookAcquisitionProfile(input) {
318374
318584
  id: `${exchange}:conservative:default`,
318375
318585
  upstreamLimit: undefined,
318376
318586
  guaranteedRetainedDepth: undefined,
318377
- coalescingSupported: false
318587
+ coalescingSupported: false,
318588
+ bidExhaustionEvidence: false,
318589
+ askExhaustionEvidence: false,
318590
+ exhaustionEvidenceValidated: true
318378
318591
  };
318379
318592
  }
318380
318593
  return {
318381
318594
  id: `${exchange}:conservative:limit:${input.requestedDepth}`,
318382
318595
  upstreamLimit: input.requestedDepth,
318383
318596
  guaranteedRetainedDepth: input.requestedDepth,
318384
- coalescingSupported: false
318597
+ coalescingSupported: false,
318598
+ bidExhaustionEvidence: false,
318599
+ askExhaustionEvidence: false,
318600
+ exhaustionEvidenceValidated: true
318385
318601
  };
318386
318602
  }
318387
318603
  function resolveOrderBookAcquisitionProfile(input) {
@@ -318393,7 +318609,10 @@ function resolveOrderBookAcquisitionProfile(input) {
318393
318609
  id: candidateProfileId,
318394
318610
  upstreamLimit: VERIFIED_PROFILE_DEPTH,
318395
318611
  guaranteedRetainedDepth: VERIFIED_PROFILE_DEPTH,
318396
- coalescingSupported: true
318612
+ coalescingSupported: true,
318613
+ bidExhaustionEvidence: false,
318614
+ askExhaustionEvidence: false,
318615
+ exhaustionEvidenceValidated: true
318397
318616
  };
318398
318617
  }
318399
318618
  return resolveConservativeOrderBookAcquisitionProfile(input);
@@ -318543,7 +318762,63 @@ class PublicFeedSubscriberBuffer {
318543
318762
  this.#bytes = 0;
318544
318763
  }
318545
318764
  }
318765
+ // src/helpers/market-data-archive/orderbook-depth.ts
318766
+ var DEFAULT_ORDERBOOK_ARCHIVE_DEPTH_LIMIT = 25;
318767
+ var MAX_ORDERBOOK_ARCHIVE_DEPTH_LIMIT = 500;
318768
+ var MAX_ORDERBOOK_MEASUREMENT_BANDS = 64;
318769
+ var MAX_ORDERBOOK_MEASUREMENT_BAND_BPS = 1e4;
318770
+ var DEFAULT_ORDERBOOK_MEASUREMENT_BANDS_BPS = [10, 25, 50, 100];
318771
+ function parseBoundedDepth(raw, field) {
318772
+ if (!/^[1-9]\d*$/.test(raw)) {
318773
+ throw new Error(`${field} must be an integer between 1 and 500`);
318774
+ }
318775
+ const parsed = Number(raw);
318776
+ if (!Number.isSafeInteger(parsed) || parsed > MAX_ORDERBOOK_ARCHIVE_DEPTH_LIMIT) {
318777
+ throw new Error(`${field} must be an integer between 1 and 500`);
318778
+ }
318779
+ return parsed;
318780
+ }
318781
+ function getOrderbookArchiveDepthLimit() {
318782
+ const raw = process.env.CEX_BROKER_ORDERBOOK_ARCHIVE_DEPTH_LIMIT?.trim();
318783
+ if (!raw) {
318784
+ return DEFAULT_ORDERBOOK_ARCHIVE_DEPTH_LIMIT;
318785
+ }
318786
+ return parseBoundedDepth(raw, "CEX_BROKER_ORDERBOOK_ARCHIVE_DEPTH_LIMIT");
318787
+ }
318788
+ function normalizeOrderbookMeasurementBandsBps(input) {
318789
+ const bands = input ?? DEFAULT_ORDERBOOK_MEASUREMENT_BANDS_BPS;
318790
+ if (bands.length === 0) {
318791
+ throw new Error("ORDERBOOK measurement bands must not be empty");
318792
+ }
318793
+ if (bands.length > MAX_ORDERBOOK_MEASUREMENT_BANDS) {
318794
+ throw new Error(`ORDERBOOK measurement bands must contain at most ${MAX_ORDERBOOK_MEASUREMENT_BANDS} entries`);
318795
+ }
318796
+ for (const band of bands) {
318797
+ if (!Number.isSafeInteger(band) || band <= 0 || band > MAX_ORDERBOOK_MEASUREMENT_BAND_BPS) {
318798
+ throw new Error("ORDERBOOK measurement bands must be integer basis points in 1..10000");
318799
+ }
318800
+ }
318801
+ const normalized = [...new Set(bands)].sort((left, right) => left - right);
318802
+ if (normalized.length > MAX_ORDERBOOK_MEASUREMENT_BANDS) {
318803
+ throw new Error(`ORDERBOOK measurement bands must contain at most ${MAX_ORDERBOOK_MEASUREMENT_BANDS} unique entries`);
318804
+ }
318805
+ return normalized;
318806
+ }
318807
+ function getOrderbookMeasurementBandsBps() {
318808
+ const raw = process.env.CEX_BROKER_ORDERBOOK_MEASUREMENT_BANDS_BPS?.trim();
318809
+ if (!raw) {
318810
+ return [...DEFAULT_ORDERBOOK_MEASUREMENT_BANDS_BPS];
318811
+ }
318812
+ const parts = raw.split(",").map((entry) => entry.trim());
318813
+ if (parts.some((entry) => !/^[1-9]\d*$/.test(entry))) {
318814
+ throw new Error("CEX_BROKER_ORDERBOOK_MEASUREMENT_BANDS_BPS must be a comma-separated list of positive integers");
318815
+ }
318816
+ return normalizeOrderbookMeasurementBandsBps(parts.map(Number));
318817
+ }
318818
+
318546
318819
  // src/helpers/market-data-archive/canonical-orderbook.ts
318820
+ var ORDERBOOK_SUMMARY_SCHEMA_VERSION = "2.0.0";
318821
+
318547
318822
  class OrderBookValidationError extends Error {
318548
318823
  reason;
318549
318824
  constructor(reason) {
@@ -318569,38 +318844,70 @@ function parseSequence(value) {
318569
318844
  }
318570
318845
  return parsed.toString(10);
318571
318846
  }
318572
- function validateSide(side, levels, depthLimit) {
318847
+ function validatePositiveInteger(value, field, maximum = Number.MAX_SAFE_INTEGER) {
318848
+ if (!Number.isSafeInteger(value) || value <= 0 || value > maximum) {
318849
+ throw new OrderBookValidationError(`${field} must be a positive integer${maximum === 500 ? " between 1 and 500" : ""}`);
318850
+ }
318851
+ }
318852
+ function validateSide(side, levels) {
318573
318853
  if (levels.length === 0) {
318574
318854
  throw new OrderBookValidationError(`${side} side is missing`);
318575
318855
  }
318576
- const retained = levels.slice(0, depthLimit);
318577
- for (let index2 = 0;index2 < retained.length; index2 += 1) {
318578
- const entry = retained[index2];
318856
+ const validated = [];
318857
+ for (let index2 = 0;index2 < levels.length; index2 += 1) {
318858
+ const entry = levels[index2];
318579
318859
  const price = entry?.[0];
318580
318860
  const amount = entry?.[1];
318581
318861
  if (!Array.isArray(entry) || entry.length < 2 || price === undefined || amount === undefined || !Number.isFinite(price) || !Number.isFinite(amount) || price <= 0 || amount <= 0) {
318582
318862
  throw new OrderBookValidationError(`${side} level ${index2} has a non-positive or non-finite price/amount`);
318583
318863
  }
318584
318864
  if (index2 > 0) {
318585
- const previous = retained[index2 - 1]?.[0];
318865
+ const previous = validated[index2 - 1]?.price;
318586
318866
  if (previous === undefined || (side === "bid" ? price >= previous : price <= previous)) {
318587
318867
  throw new OrderBookValidationError(`${side} levels are not strictly ${side === "bid" ? "descending" : "ascending"}`);
318588
318868
  }
318589
318869
  }
318870
+ validated.push({ price, amount });
318590
318871
  }
318591
- return retained.map(([price, amount]) => ({
318592
- price,
318593
- amount
318594
- }));
318872
+ return validated;
318595
318873
  }
318596
- function normalizedBands(input) {
318597
- const bands = input ?? [10, 25, 50, 100];
318598
- for (const band of bands) {
318599
- if (!Number.isFinite(band) || band <= 0 || !Number.isInteger(band)) {
318600
- throw new OrderBookValidationError("measurement bands must be positive integer basis points");
318874
+ function validateMetadata(metadata, observedBids, observedAsks) {
318875
+ if (!metadata.captureProfileId.trim()) {
318876
+ throw new OrderBookValidationError("capture_profile_id must not be empty");
318877
+ }
318878
+ validatePositiveInteger(metadata.effectiveCadenceMs, "effective_cadence_ms");
318879
+ if (metadata.requestedUpstreamDepth !== null) {
318880
+ validatePositiveInteger(metadata.requestedUpstreamDepth, "requested_upstream_depth", 500);
318881
+ }
318882
+ validatePositiveInteger(metadata.observedBidCount, "observed_bid_count");
318883
+ validatePositiveInteger(metadata.observedAskCount, "observed_ask_count");
318884
+ if (metadata.observedBidCount !== observedBids.length) {
318885
+ throw new OrderBookValidationError("observed_bid_count does not match the complete observation");
318886
+ }
318887
+ if (metadata.observedAskCount !== observedAsks.length) {
318888
+ throw new OrderBookValidationError("observed_ask_count does not match the complete observation");
318889
+ }
318890
+ const farthestBid = observedBids.at(-1)?.price;
318891
+ const farthestAsk = observedAsks.at(-1)?.price;
318892
+ if (!Number.isFinite(metadata.observedFarthestBid) || metadata.observedFarthestBid <= 0 || metadata.observedFarthestBid !== farthestBid) {
318893
+ throw new OrderBookValidationError("observed_farthest_bid does not match the complete observation");
318894
+ }
318895
+ if (!Number.isFinite(metadata.observedFarthestAsk) || metadata.observedFarthestAsk <= 0 || metadata.observedFarthestAsk !== farthestAsk) {
318896
+ throw new OrderBookValidationError("observed_farthest_ask does not match the complete observation");
318897
+ }
318898
+ for (const [side, evidence] of [
318899
+ ["bid", metadata.exhaustionEvidence.bid],
318900
+ ["ask", metadata.exhaustionEvidence.ask]
318901
+ ]) {
318902
+ if (evidence.validated !== true || typeof evidence.exhausted !== "boolean" || !evidence.source.trim()) {
318903
+ throw new OrderBookValidationError(`${side} exhaustion evidence must be validated and source-bound`);
318601
318904
  }
318602
318905
  }
318603
- return [...new Set(bands)].sort((left, right) => left - right);
318906
+ try {
318907
+ return normalizeOrderbookMeasurementBandsBps(metadata.measurementBandsBps);
318908
+ } catch (error) {
318909
+ throw new OrderBookValidationError(error instanceof Error ? error.message : "invalid measurement bands");
318910
+ }
318604
318911
  }
318605
318912
  function checksumRow(row) {
318606
318913
  return { ...row, normalized_row_checksum: sha256Canonical(row) };
@@ -318611,20 +318918,33 @@ function commonEvidenceFields(context2, rawCapture, input) {
318611
318918
  source_time_ms: input.eventTimeMs,
318612
318919
  received_time_ms: input.receivedTimeMs,
318613
318920
  snapshot_id: input.snapshotId,
318614
- construction_mode: input.constructionMode,
318921
+ construction_mode: "sampled_top_n_snapshot",
318615
318922
  gap_policy: "record_gap",
318616
318923
  depth_limit: input.depthLimit,
318617
318924
  sequence: input.sequence,
318618
318925
  exact_l2_reconstruction_complete: 0
318619
318926
  };
318620
318927
  }
318928
+ function sideBandEvidence(input) {
318929
+ const farthest = input.levels.at(-1)?.price;
318930
+ const depths = input.boundaries.map((boundary) => input.levels.filter(({ price }) => input.side === "bid" ? price >= boundary : price <= boundary).reduce((sum3, { amount }) => sum3 + amount, 0));
318931
+ const statuses = input.boundaries.map((boundary) => {
318932
+ const boundaryReached = input.side === "bid" ? farthest <= boundary : farthest >= boundary;
318933
+ return boundaryReached || input.exhausted ? "exact" : "censored";
318934
+ });
318935
+ return { depths, statuses };
318936
+ }
318621
318937
  function buildCanonicalOrderBookRows(input) {
318622
- if (!Number.isSafeInteger(input.depthLimit) || input.depthLimit <= 0 || input.depthLimit > 500) {
318623
- throw new OrderBookValidationError("depth limit must be an integer between 1 and 500");
318624
- }
318938
+ validatePositiveInteger(input.depthLimit, "depth limit", 500);
318625
318939
  if (input.context.feed !== "ORDERBOOK") {
318626
318940
  throw new OrderBookValidationError("capture context feed is not ORDERBOOK");
318627
318941
  }
318942
+ if (input.context.schemaVersion !== "1.0.0") {
318943
+ throw new OrderBookValidationError("ORDERBOOK raw and level capture schema must be 1.0.0");
318944
+ }
318945
+ if (!input.context.provenanceComplete) {
318946
+ throw new OrderBookValidationError("summary v2 requires complete live provenance");
318947
+ }
318628
318948
  if (input.constructionMode === "exact_l2_reconstruction") {
318629
318949
  throw new OrderBookValidationError("exact L2 requires a complete continuity proof and is unsupported");
318630
318950
  }
@@ -318639,13 +318959,18 @@ function buildCanonicalOrderBookRows(input) {
318639
318959
  if (receivedTimeMs < eventTimeMs) {
318640
318960
  throw new OrderBookValidationError("received timestamp precedes source timestamp");
318641
318961
  }
318642
- const bids = validateSide("bid", input.snapshot.bids, input.depthLimit);
318643
- const asks = validateSide("ask", input.snapshot.asks, input.depthLimit);
318644
- const bestBid = bids[0];
318645
- const bestAsk = asks[0];
318962
+ const observedBids = validateSide("bid", input.snapshot.bids);
318963
+ const observedAsks = validateSide("ask", input.snapshot.asks);
318964
+ const bestBid = observedBids[0];
318965
+ const bestAsk = observedAsks[0];
318646
318966
  if (bestBid.price >= bestAsk.price) {
318647
318967
  throw new OrderBookValidationError("book is crossed or locked");
318648
318968
  }
318969
+ const bands = validateMetadata(input.archiveMetadata, observedBids, observedAsks);
318970
+ const bids = observedBids.slice(0, input.depthLimit);
318971
+ const asks = observedAsks.slice(0, input.depthLimit);
318972
+ const retainedFarthestBid = bids.at(-1);
318973
+ const retainedFarthestAsk = asks.at(-1);
318649
318974
  const sequence = parseSequence(input.snapshot.sequence);
318650
318975
  const midPrice = (bestBid.price + bestAsk.price) / 2;
318651
318976
  const spread2 = bestAsk.price - bestBid.price;
@@ -318665,8 +318990,7 @@ function buildCanonicalOrderBookRows(input) {
318665
318990
  sequence,
318666
318991
  depthLimit: input.depthLimit,
318667
318992
  eventTimeMs,
318668
- receivedTimeMs,
318669
- constructionMode: input.constructionMode ?? "sampled_top_n_snapshot"
318993
+ receivedTimeMs
318670
318994
  });
318671
318995
  const levels = [
318672
318996
  ["bid", bids],
@@ -318684,17 +319008,34 @@ function buildCanonicalOrderBookRows(input) {
318684
319008
  });
318685
319009
  return { table: "market_data.cex_order_book_levels", row };
318686
319010
  }));
318687
- const bands = normalizedBands(input.measurementBandsBps);
318688
- const bidDepth = bands.map((band) => {
318689
- const minimumPrice = bestBid.price * (1 - band / 1e4);
318690
- return bids.filter(({ price }) => price >= minimumPrice).reduce((sum3, { amount }) => sum3 + amount, 0);
319011
+ const bidBoundaries = bands.map((band) => midPrice * (1 - band / 1e4));
319012
+ const askBoundaries = bands.map((band) => midPrice * (1 + band / 1e4));
319013
+ const bidEvidence = sideBandEvidence({
319014
+ levels: observedBids,
319015
+ boundaries: bidBoundaries,
319016
+ side: "bid",
319017
+ exhausted: input.archiveMetadata.exhaustionEvidence.bid.exhausted
318691
319018
  });
318692
- const askDepth = bands.map((band) => {
318693
- const maximumPrice = bestAsk.price * (1 + band / 1e4);
318694
- return asks.filter(({ price }) => price <= maximumPrice).reduce((sum3, { amount }) => sum3 + amount, 0);
319019
+ const askEvidence = sideBandEvidence({
319020
+ levels: observedAsks,
319021
+ boundaries: askBoundaries,
319022
+ side: "ask",
319023
+ exhausted: input.archiveMetadata.exhaustionEvidence.ask.exhausted
318695
319024
  });
318696
319025
  const summaryRow = checksumRow({
318697
319026
  ...common,
319027
+ schema_version: ORDERBOOK_SUMMARY_SCHEMA_VERSION,
319028
+ capture_profile_id: input.archiveMetadata.captureProfileId,
319029
+ effective_cadence_ms: input.archiveMetadata.effectiveCadenceMs,
319030
+ requested_upstream_depth: input.archiveMetadata.requestedUpstreamDepth,
319031
+ observed_bid_count: input.archiveMetadata.observedBidCount,
319032
+ observed_ask_count: input.archiveMetadata.observedAskCount,
319033
+ observed_farthest_bid: input.archiveMetadata.observedFarthestBid,
319034
+ observed_farthest_ask: input.archiveMetadata.observedFarthestAsk,
319035
+ retained_farthest_bid: retainedFarthestBid.price,
319036
+ retained_farthest_ask: retainedFarthestAsk.price,
319037
+ bid_exhausted: input.archiveMetadata.exhaustionEvidence.bid.exhausted ? 1 : 0,
319038
+ ask_exhausted: input.archiveMetadata.exhaustionEvidence.ask.exhausted ? 1 : 0,
318698
319039
  best_bid: bestBid.price,
318699
319040
  best_ask: bestAsk.price,
318700
319041
  best_bid_amount: bestBid.amount,
@@ -318706,8 +319047,12 @@ function buildCanonicalOrderBookRows(input) {
318706
319047
  bid_level_count: bids.length,
318707
319048
  ask_level_count: asks.length,
318708
319049
  measurement_bands_bps: bands,
318709
- bid_depth_by_band: bidDepth,
318710
- ask_depth_by_band: askDepth
319050
+ bid_boundary_price_by_band: bidBoundaries,
319051
+ ask_boundary_price_by_band: askBoundaries,
319052
+ bid_depth_by_band: bidEvidence.depths,
319053
+ ask_depth_by_band: askEvidence.depths,
319054
+ bid_status_by_band: bidEvidence.statuses,
319055
+ ask_status_by_band: askEvidence.statuses
318711
319056
  });
318712
319057
  return {
318713
319058
  snapshotId,
@@ -318837,21 +319182,6 @@ class OhlcvBarTracker {
318837
319182
  }
318838
319183
  }
318839
319184
 
318840
- // src/helpers/market-data-archive/orderbook-depth.ts
318841
- var DEFAULT_ORDERBOOK_ARCHIVE_DEPTH_LIMIT = 25;
318842
- var MAX_ORDERBOOK_ARCHIVE_DEPTH_LIMIT = 500;
318843
- function getOrderbookArchiveDepthLimit() {
318844
- const raw = process.env.CEX_BROKER_ORDERBOOK_ARCHIVE_DEPTH_LIMIT;
318845
- if (!raw) {
318846
- return DEFAULT_ORDERBOOK_ARCHIVE_DEPTH_LIMIT;
318847
- }
318848
- const parsed = Number.parseInt(raw, 10);
318849
- if (!Number.isFinite(parsed) || parsed <= 0) {
318850
- return DEFAULT_ORDERBOOK_ARCHIVE_DEPTH_LIMIT;
318851
- }
318852
- return Math.min(parsed, MAX_ORDERBOOK_ARCHIVE_DEPTH_LIMIT);
318853
- }
318854
-
318855
319185
  // src/helpers/market-data-archive/parse-stream.ts
318856
319186
  function isFiniteNumber2(value) {
318857
319187
  return typeof value === "number" && Number.isFinite(value);
@@ -318975,13 +319305,17 @@ function legacyMarketFields(context2, rawCapture) {
318975
319305
  function legacyDecimal8(value) {
318976
319306
  return value === undefined ? undefined : Number(value.toFixed(8));
318977
319307
  }
318978
- function buildCanonicalCexStreamEventRow(context2, rawCapture) {
319308
+ function buildCanonicalCexStreamEventRow(context2, rawCapture, options) {
319309
+ const payloadEncoding = options?.payloadEncoding ?? "canonical_json_v1";
319310
+ if (payloadEncoding === "orderbook_metadata_only_v1" && context2.feed !== "ORDERBOOK") {
319311
+ throw new Error("Metadata-only raw encoding requires an ORDERBOOK context");
319312
+ }
318979
319313
  const row = withNormalizedChecksum({
318980
319314
  ...captureCoreFields(context2, rawCapture),
318981
319315
  ...legacyMarketFields(context2, rawCapture),
318982
319316
  stream_type: context2.feed,
318983
319317
  event_time_ms: rawCapture.eventTimeMs,
318984
- payload_encoding: "canonical_json_v1",
319318
+ payload_encoding: payloadEncoding,
318985
319319
  payload_json: canonicalSerialize(rawCapture.redactedPayload)
318986
319320
  });
318987
319321
  return { table: "market_data.cex_stream_events", row };
@@ -319125,7 +319459,13 @@ function archiveOrderbookInBackground(archiver, otelMetrics, input, options) {
319125
319459
  queueMicrotask(() => {
319126
319460
  try {
319127
319461
  const context2 = resolveCaptureContext(archiver, input, "ORDERBOOK", options?.sourceMode ?? "broker_live_sampling_v1");
319128
- const rawCapture = createRawCapture(context2, {
319462
+ const depthLimit = options?.depthLimit ?? getOrderbookArchiveDepthLimit();
319463
+ const measurementBandsBps = normalizeOrderbookMeasurementBandsBps(input.archiveMetadata.measurementBandsBps);
319464
+ const archiveMetadata = {
319465
+ ...input.archiveMetadata,
319466
+ measurementBandsBps
319467
+ };
319468
+ const completeRawCapture = createRawCapture(context2, {
319129
319469
  payload: input.snapshot,
319130
319470
  eventTimeMs: input.snapshot.timestamp,
319131
319471
  receivedTimeMs: input.snapshot.receivedTimestamp,
@@ -319134,10 +319474,29 @@ function archiveOrderbookInBackground(archiver, otelMetrics, input, options) {
319134
319474
  const canonical = buildCanonicalOrderBookRows({
319135
319475
  context: context2,
319136
319476
  snapshot: input.snapshot,
319137
- rawCapture,
319138
- depthLimit: options?.depthLimit ?? getOrderbookArchiveDepthLimit()
319477
+ rawCapture: completeRawCapture,
319478
+ depthLimit,
319479
+ archiveMetadata
319139
319480
  });
319140
- archiver.enqueue(buildCanonicalCexStreamEventRow(context2, rawCapture));
319481
+ const rawMetadata = {
319482
+ capture_profile_id: archiveMetadata.captureProfileId,
319483
+ effective_cadence_ms: archiveMetadata.effectiveCadenceMs,
319484
+ requested_upstream_depth: archiveMetadata.requestedUpstreamDepth,
319485
+ archive_depth_limit: depthLimit,
319486
+ observed_bid_count: archiveMetadata.observedBidCount,
319487
+ observed_ask_count: archiveMetadata.observedAskCount,
319488
+ observed_farthest_bid: canonicalDecimal(archiveMetadata.observedFarthestBid),
319489
+ observed_farthest_ask: canonicalDecimal(archiveMetadata.observedFarthestAsk),
319490
+ bid_exhausted: archiveMetadata.exhaustionEvidence.bid.exhausted,
319491
+ ask_exhausted: archiveMetadata.exhaustionEvidence.ask.exhausted,
319492
+ retained_bid_count: canonical.levels.filter(({ row }) => row.side === "bid").length,
319493
+ retained_ask_count: canonical.levels.filter(({ row }) => row.side === "ask").length,
319494
+ measurement_bands_bps: measurementBandsBps
319495
+ };
319496
+ const storedRawCapture = projectRawCapturePayload(completeRawCapture, rawMetadata);
319497
+ archiver.enqueue(buildCanonicalCexStreamEventRow(context2, storedRawCapture, {
319498
+ payloadEncoding: "orderbook_metadata_only_v1"
319499
+ }));
319141
319500
  for (const row of canonical.levels)
319142
319501
  archiver.enqueue(row);
319143
319502
  archiver.enqueue(canonical.summary);
@@ -319259,6 +319618,9 @@ function archiveTickerInBackground(archiver, otelMetrics, input) {
319259
319618
  });
319260
319619
  }
319261
319620
  function archiveCexStreamEventInBackground(archiver, otelMetrics, input) {
319621
+ if (input.streamType === "ORDERBOOK") {
319622
+ throw new Error("Generic ORDERBOOK archive events are unsupported; use archiveOrderbookInBackground");
319623
+ }
319262
319624
  archiveMarketRowsInBackground(archiver, otelMetrics, "stream", input, input.streamType, () => [
319263
319625
  buildCexStreamEventRow({
319264
319626
  ...input,
@@ -319283,6 +319645,82 @@ function resolveOhlcvBootstrapLimit(optionValue) {
319283
319645
  }
319284
319646
  return Math.min(parsed, MAX_OHLCV_BOOTSTRAP_LIMIT);
319285
319647
  }
319648
+ // src/helpers/market-data-archive/summary-v2-conformance.ts
319649
+ var ORDERBOOK_SUMMARY_V2_SUPPORTED_VIEW_FIELDS = [
319650
+ ["source", "String", false],
319651
+ ["deployment_id", "String", false],
319652
+ ["capture_bundle_id", "String", false],
319653
+ ["exchange", "String", false],
319654
+ ["symbol", "String", false],
319655
+ ["trading_pair", "String", false],
319656
+ ["source_symbol", "String", false],
319657
+ ["asset_type", "String", false],
319658
+ ["feed", "String", false],
319659
+ ["provider", "String", false],
319660
+ ["source_mode", "String", false],
319661
+ ["source_time_ms", "UInt64", false],
319662
+ ["received_time_ms", "UInt64", false],
319663
+ ["raw_capture_id", "String", false],
319664
+ ["raw_capture_scope", "String", false],
319665
+ ["schema_version", "String", false],
319666
+ ["checksum_algorithm", "String", false],
319667
+ ["raw_checksum", "String", false],
319668
+ ["provenance_complete", "UInt8", false],
319669
+ ["snapshot_id", "String", false],
319670
+ ["construction_mode", "String", false],
319671
+ ["gap_policy", "String", false],
319672
+ ["depth_limit", "UInt16", false],
319673
+ ["sequence", "UInt64", true],
319674
+ ["exact_l2_reconstruction_complete", "UInt8", false],
319675
+ ["capture_profile_id", "String", false],
319676
+ ["effective_cadence_ms", "UInt32", false],
319677
+ ["requested_upstream_depth", "UInt16", true],
319678
+ ["observed_bid_count", "UInt32", false],
319679
+ ["observed_ask_count", "UInt32", false],
319680
+ ["observed_farthest_bid", "Decimal(38,18)", false],
319681
+ ["observed_farthest_ask", "Decimal(38,18)", false],
319682
+ ["retained_farthest_bid", "Decimal(38,18)", false],
319683
+ ["retained_farthest_ask", "Decimal(38,18)", false],
319684
+ ["bid_exhausted", "UInt8", false],
319685
+ ["ask_exhausted", "UInt8", false],
319686
+ ["best_bid", "Decimal(38,18)", false],
319687
+ ["best_ask", "Decimal(38,18)", false],
319688
+ ["best_bid_amount", "Decimal(38,18)", false],
319689
+ ["best_ask_amount", "Decimal(38,18)", false],
319690
+ ["mid_price", "Decimal(38,18)", false],
319691
+ ["spread", "Decimal(38,18)", false],
319692
+ ["spread_bps", "Float64", false],
319693
+ ["staleness_ms", "UInt64", false],
319694
+ ["bid_level_count", "UInt16", false],
319695
+ ["ask_level_count", "UInt16", false],
319696
+ ["measurement_bands_bps", "Array(UInt32)", false],
319697
+ ["bid_boundary_price_by_band", "Array(Decimal(38,18))", false],
319698
+ ["ask_boundary_price_by_band", "Array(Decimal(38,18))", false],
319699
+ ["bid_depth_by_band", "Array(Decimal(38,18))", false],
319700
+ ["ask_depth_by_band", "Array(Decimal(38,18))", false],
319701
+ ["bid_status_by_band", "Array(Enum8('exact'=1,'censored'=2))", false],
319702
+ ["ask_status_by_band", "Array(Enum8('exact'=1,'censored'=2))", false],
319703
+ ["normalized_row_checksum", "String", false]
319704
+ ];
319705
+ var ORDERBOOK_SUMMARY_V2_SUPPORTED_VIEW_FIELD_NAMES = ORDERBOOK_SUMMARY_V2_SUPPORTED_VIEW_FIELDS.map(([name]) => name);
319706
+ var DECIMAL_38_18_FIELDS = new Set([
319707
+ "observed_farthest_bid",
319708
+ "observed_farthest_ask",
319709
+ "retained_farthest_bid",
319710
+ "retained_farthest_ask",
319711
+ "best_bid",
319712
+ "best_ask",
319713
+ "best_bid_amount",
319714
+ "best_ask_amount",
319715
+ "mid_price",
319716
+ "spread"
319717
+ ]);
319718
+ var DECIMAL_38_18_ARRAY_FIELDS = new Set([
319719
+ "bid_boundary_price_by_band",
319720
+ "ask_boundary_price_by_band",
319721
+ "bid_depth_by_band",
319722
+ "ask_depth_by_band"
319723
+ ]);
319286
319724
  // src/helpers/order-book.ts
319287
319725
  var ORDER_BOOK_CALL_METHODS = {
319288
319726
  FETCH_CAPABILITY: "fetch_order_book_capability",
@@ -319634,7 +320072,7 @@ class PublicFeedWorker {
319634
320072
  if (this.options.profile) {
319635
320073
  this.options.metrics?.recordCounter("public_feed_acquisition_profiles_total", 1, {
319636
320074
  ...this.#metricLabels(),
319637
- profile: this.options.profile.id
320075
+ profile_class: this.options.profile.coalescingSupported ? "coalesced" : "conservative"
319638
320076
  });
319639
320077
  }
319640
320078
  this.#run = this.#watchLoop();
@@ -319702,7 +320140,34 @@ class PublicFeedWorker {
319702
320140
  depthLimit: retainedDepth,
319703
320141
  receivedTimestamp
319704
320142
  });
319705
- this.options.archiveSink.orderbook({ ...context2, snapshot }, { sampledOut: !this.#sampler.shouldEmit(receivedTimestamp) });
320143
+ const observedBidCount = snapshot.bids.length;
320144
+ const observedAskCount = snapshot.asks.length;
320145
+ this.options.archiveSink.orderbook({
320146
+ ...context2,
320147
+ snapshot,
320148
+ archiveMetadata: {
320149
+ captureProfileId: this.options.profile?.id ?? `${this.options.exchangeName}:conservative:default`,
320150
+ effectiveCadenceMs: getOrderbookIntervalMs(),
320151
+ requestedUpstreamDepth: this.options.profile?.upstreamLimit ?? null,
320152
+ observedBidCount,
320153
+ observedAskCount,
320154
+ observedFarthestBid: snapshot.bids.at(-1)?.[0] ?? Number.NaN,
320155
+ observedFarthestAsk: snapshot.asks.at(-1)?.[0] ?? Number.NaN,
320156
+ exhaustionEvidence: {
320157
+ bid: {
320158
+ exhausted: this.options.profile?.bidExhaustionEvidence ?? false,
320159
+ validated: this.options.profile?.exhaustionEvidenceValidated ?? true,
320160
+ source: this.options.profile?.id ?? "broker:conservative"
320161
+ },
320162
+ ask: {
320163
+ exhausted: this.options.profile?.askExhaustionEvidence ?? false,
320164
+ validated: this.options.profile?.exhaustionEvidenceValidated ?? true,
320165
+ source: this.options.profile?.id ?? "broker:conservative"
320166
+ }
320167
+ },
320168
+ measurementBandsBps: getOrderbookMeasurementBandsBps()
320169
+ }
320170
+ }, { sampledOut: !this.#sampler.shouldEmit(receivedTimestamp) });
319706
320171
  this.#recordCounter("public_feed_archive_decisions_total");
319707
320172
  this.options.observer?.archiveDecision?.(this.#observation());
319708
320173
  this.#fanout((subscriber) => this.#frame(projectOrderBookSnapshot(snapshot, subscriber.depthLimit), receivedTimestamp));
@@ -319875,7 +320340,7 @@ class PublicFeedWorker {
319875
320340
  }
319876
320341
  #metricLabels() {
319877
320342
  return {
319878
- exchange: this.options.exchangeName,
320343
+ exchange_bucket: this.options.exchangeOwned ? "dynamic" : "configured",
319879
320344
  feed: this.options.feed,
319880
320345
  market_type: this.options.marketType
319881
320346
  };
@@ -335242,7 +335707,29 @@ async function handleOrderBookCall(ctx) {
335242
335707
  assetType: "spot",
335243
335708
  accountSelector: ctx.selectedBrokerAccount?.label,
335244
335709
  deploymentId: ctx.brokerArchiver?.getDeploymentId() ?? "unarchived",
335245
- snapshot
335710
+ snapshot,
335711
+ archiveMetadata: {
335712
+ captureProfileId: `${orderBookPayload.exchange.trim().toLowerCase()}:current-snapshot:requested:${orderBookPayload.depthLimit}`,
335713
+ effectiveCadenceMs: 1,
335714
+ requestedUpstreamDepth: orderBookPayload.depthLimit,
335715
+ observedBidCount: snapshot.bids.length,
335716
+ observedAskCount: snapshot.asks.length,
335717
+ observedFarthestBid: snapshot.bids.at(-1)?.[0] ?? Number.NaN,
335718
+ observedFarthestAsk: snapshot.asks.at(-1)?.[0] ?? Number.NaN,
335719
+ exhaustionEvidence: {
335720
+ bid: {
335721
+ exhausted: false,
335722
+ validated: true,
335723
+ source: "broker:current-snapshot"
335724
+ },
335725
+ ask: {
335726
+ exhausted: false,
335727
+ validated: true,
335728
+ source: "broker:current-snapshot"
335729
+ }
335730
+ },
335731
+ measurementBandsBps: getOrderbookMeasurementBandsBps()
335732
+ }
335246
335733
  }, {
335247
335734
  sourceMode: "broker_current_snapshot_v1",
335248
335735
  depthLimit: orderBookPayload.depthLimit