@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
package/dist/index.js CHANGED
@@ -291352,6 +291352,22 @@ import {
291352
291352
  import { basename, dirname } from "node:path";
291353
291353
  var EXPORT_CHUNK_BYTES = 8 * 1024 * 1024;
291354
291354
  var SHA256_RECEIPT_PATTERN = /^([0-9a-f]{64}) {2}(.+)\n$/;
291355
+ function sha256File(path) {
291356
+ const hash4 = createHash3("sha256");
291357
+ const fd2 = openSync(path, "r");
291358
+ try {
291359
+ const chunk = Buffer.alloc(EXPORT_CHUNK_BYTES);
291360
+ for (;; ) {
291361
+ const read = readSync(fd2, chunk, 0, chunk.length, null);
291362
+ if (read === 0)
291363
+ break;
291364
+ hash4.update(chunk.subarray(0, read));
291365
+ }
291366
+ } finally {
291367
+ closeSync(fd2);
291368
+ }
291369
+ return hash4.digest("hex");
291370
+ }
291355
291371
 
291356
291372
  class DeadLetterJournalExportError extends Error {
291357
291373
  constructor(message, options) {
@@ -291385,6 +291401,9 @@ function exportDeadLetterJournal(journalPath, exportPath) {
291385
291401
  if (match?.[1] === undefined || match[2] !== basename(exportPath)) {
291386
291402
  throw new Error("export receipt is malformed");
291387
291403
  }
291404
+ if (sha256File(exportPath) !== match[1]) {
291405
+ throw new Error("export digest does not match receipt");
291406
+ }
291388
291407
  } catch (error) {
291389
291408
  throw new DeadLetterJournalExportError("Dead-letter journal export target is incomplete or unverifiable", { cause: error });
291390
291409
  }
@@ -292725,6 +292744,16 @@ function withTimeout(promise, timeoutMs, label) {
292725
292744
  return Promise.race([promise, expiry]).finally(() => clearTimeout(timer));
292726
292745
  }
292727
292746
  var ALL_CURRENCIES_CODE = "*";
292747
+ var BINANCE_UNLOCK_PROGRESS_SOURCE = {
292748
+ venue: "binance",
292749
+ endpoint: "GET /sapi/v1/capital/deposit/hisrec",
292750
+ fields: {
292751
+ status: "info.status",
292752
+ confirmTimes: "info.confirmTimes",
292753
+ unlockConfirm: "info.unlockConfirm",
292754
+ completeTime: "info.completeTime"
292755
+ }
292756
+ };
292728
292757
  function depositTimestamp(record) {
292729
292758
  const observedAt = depositField(record, [
292730
292759
  "timestamp",
@@ -292737,12 +292766,185 @@ function depositTimestamp(record) {
292737
292766
  const timestamp = typeof observedAt === "number" ? observedAt : typeof observedAt === "string" ? Date.parse(observedAt) : Number.NaN;
292738
292767
  return Number.isFinite(timestamp) ? timestamp : undefined;
292739
292768
  }
292740
- function archivedDepositStatus(exchangeId, record) {
292741
- const rawStatus = asRecord(record.info)?.status;
292742
- if (exchangeId?.toLowerCase() === "binance" && String(rawStatus ?? "").trim() === "6") {
292743
- return "credited_not_withdrawable";
292769
+ function parseNonNegativeInteger(value) {
292770
+ if (typeof value === "number") {
292771
+ return Number.isSafeInteger(value) && value >= 0 ? value : undefined;
292772
+ }
292773
+ if (typeof value !== "string") {
292774
+ return;
292775
+ }
292776
+ const trimmed = value.trim();
292777
+ if (!/^\d+$/.test(trimmed)) {
292778
+ return;
292779
+ }
292780
+ const parsed = Number(trimmed);
292781
+ return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : undefined;
292782
+ }
292783
+ function parseConfirmTimes(value) {
292784
+ if (value === undefined || value === null) {
292785
+ return { reason: "missing_confirmTimes" };
292786
+ }
292787
+ let currentValue;
292788
+ let creditRequiredValue;
292789
+ if (typeof value === "string") {
292790
+ const match = /^(\d+)\s*\/\s*(\d+)$/.exec(value.trim());
292791
+ if (!match) {
292792
+ return { reason: "invalid_confirmTimes" };
292793
+ }
292794
+ currentValue = match[1];
292795
+ creditRequiredValue = match[2];
292796
+ } else {
292797
+ const record = asRecord(value);
292798
+ if (!record) {
292799
+ return { reason: "invalid_confirmTimes" };
292800
+ }
292801
+ currentValue = record.current;
292802
+ creditRequiredValue = record.credit_required;
292803
+ }
292804
+ const current = parseNonNegativeInteger(currentValue);
292805
+ const creditRequired = parseNonNegativeInteger(creditRequiredValue);
292806
+ if (current === undefined || creditRequired === undefined) {
292807
+ return { reason: "invalid_confirmTimes" };
292744
292808
  }
292745
- return normalizeCcxtTransactionForArchive(record).status;
292809
+ return { current, creditRequired, reason: null };
292810
+ }
292811
+ function parseOptionalNonNegativeInteger(value, fieldName) {
292812
+ if (value === undefined || value === null) {
292813
+ return { value: undefined, reason: null };
292814
+ }
292815
+ const parsed = parseNonNegativeInteger(value);
292816
+ return parsed === undefined ? { value: undefined, reason: `invalid_${fieldName}` } : { value: parsed, reason: null };
292817
+ }
292818
+ function parseBinanceUnlockProgress(record) {
292819
+ const info = asRecord(record.info);
292820
+ const nativeStatus = parseNonNegativeInteger(info?.status);
292821
+ const confirmations = parseConfirmTimes(info?.confirmTimes);
292822
+ const unlockRequired = parseOptionalNonNegativeInteger(info?.unlockConfirm, "unlockConfirm");
292823
+ const completeTime = parseOptionalNonNegativeInteger(info?.completeTime, "completeTime");
292824
+ let quality = "valid";
292825
+ let reason = null;
292826
+ if (nativeStatus === undefined) {
292827
+ quality = "unknown";
292828
+ reason = "missing_or_invalid_status";
292829
+ } else if (confirmations.reason !== null) {
292830
+ quality = "unknown";
292831
+ reason = confirmations.reason;
292832
+ } else if (unlockRequired.reason !== null) {
292833
+ quality = "unknown";
292834
+ reason = unlockRequired.reason;
292835
+ } else if (completeTime.reason !== null) {
292836
+ quality = "unknown";
292837
+ reason = completeTime.reason;
292838
+ }
292839
+ let state = "unknown";
292840
+ switch (nativeStatus) {
292841
+ case 0:
292842
+ state = "pending";
292843
+ break;
292844
+ case 1:
292845
+ if (quality === "valid" && confirmations.current !== undefined && unlockRequired.value !== undefined && confirmations.current >= unlockRequired.value) {
292846
+ state = "ok";
292847
+ } else if (quality === "valid") {
292848
+ state = "credited_not_withdrawable";
292849
+ }
292850
+ break;
292851
+ case 2:
292852
+ case 7:
292853
+ state = "failed";
292854
+ break;
292855
+ case 6:
292856
+ state = "credited_not_withdrawable";
292857
+ break;
292858
+ case 8:
292859
+ state = "pending";
292860
+ break;
292861
+ default:
292862
+ quality = "unknown";
292863
+ reason = "unsupported_status";
292864
+ break;
292865
+ }
292866
+ return {
292867
+ version: 1,
292868
+ state,
292869
+ progress_state: quality,
292870
+ reason,
292871
+ native_status: nativeStatus ?? null,
292872
+ current: confirmations.current ?? null,
292873
+ credit_required: confirmations.creditRequired ?? null,
292874
+ unlock_required: unlockRequired.value ?? null,
292875
+ complete_time: completeTime.value ?? null,
292876
+ observed_at: new Date().toISOString(),
292877
+ source: BINANCE_UNLOCK_PROGRESS_SOURCE
292878
+ };
292879
+ }
292880
+ function unlockProgressKey(progress) {
292881
+ return JSON.stringify({
292882
+ version: progress.version,
292883
+ state: progress.state,
292884
+ progress_state: progress.progress_state,
292885
+ reason: progress.reason,
292886
+ native_status: progress.native_status,
292887
+ current: progress.current,
292888
+ credit_required: progress.credit_required,
292889
+ unlock_required: progress.unlock_required,
292890
+ complete_time: progress.complete_time
292891
+ });
292892
+ }
292893
+ function progressWatermark(progress, previous) {
292894
+ if (progress.progress_state !== "valid" || progress.current === null || progress.credit_required === null || progress.unlock_required === null) {
292895
+ return previous;
292896
+ }
292897
+ return {
292898
+ current: Math.max(previous?.current ?? 0, progress.current),
292899
+ credit_required: progress.credit_required,
292900
+ unlock_required: progress.unlock_required
292901
+ };
292902
+ }
292903
+ function classifyBinanceDeposit(record, previous) {
292904
+ let progress = parseBinanceUnlockProgress(record);
292905
+ let highWatermark = previous?.highWatermark;
292906
+ 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)) {
292907
+ progress = {
292908
+ ...progress,
292909
+ state: "unknown",
292910
+ progress_state: "contradictory",
292911
+ reason: "confirmation_progress_regressed_or_requirement_changed"
292912
+ };
292913
+ } else {
292914
+ highWatermark = progressWatermark(progress, previous?.highWatermark);
292915
+ }
292916
+ return {
292917
+ archiveStatus: progress.state,
292918
+ holdCursor: progress.state === "pending" || progress.state === "credited_not_withdrawable" || progress.state === "unknown",
292919
+ unlockProgress: progress,
292920
+ progressKey: unlockProgressKey(progress),
292921
+ highWatermark
292922
+ };
292923
+ }
292924
+ function classifyDeposit(exchangeId, record, previous) {
292925
+ if (exchangeId?.toLowerCase() === "binance") {
292926
+ return classifyBinanceDeposit(record, previous);
292927
+ }
292928
+ const archiveStatus = normalizeCcxtTransactionForArchive(record).status ?? "";
292929
+ const status = normalizeDepositStatus(depositField(record, ["status", "state"]));
292930
+ return {
292931
+ archiveStatus,
292932
+ holdCursor: archiveStatus === "credited_not_withdrawable" || status === "pending",
292933
+ progressKey: JSON.stringify({ status: archiveStatus })
292934
+ };
292935
+ }
292936
+ function depositIdentity(input) {
292937
+ if (input.externalId === undefined && input.txid === undefined) {
292938
+ return;
292939
+ }
292940
+ return JSON.stringify({
292941
+ exchange: input.exchangeId,
292942
+ account: input.accountSelector,
292943
+ coin: input.coin === undefined ? "" : String(input.coin),
292944
+ network: input.network === undefined ? "" : String(input.network),
292945
+ external_id: input.externalId ?? "",
292946
+ txid: input.txid ?? ""
292947
+ });
292746
292948
  }
292747
292949
  function nextDepositCursor(deposits, currentSince, depositsLimit, exchangeId) {
292748
292950
  if (deposits.length >= depositsLimit) {
@@ -292756,9 +292958,8 @@ function nextDepositCursor(deposits, currentSince, depositsLimit, exchangeId) {
292756
292958
  return currentSince;
292757
292959
  }
292758
292960
  const timestamp = depositTimestamp(record);
292759
- const archiveStatus = archivedDepositStatus(exchangeId, record);
292760
- const status = normalizeDepositStatus(depositField(record, ["status", "state"]));
292761
- const isPending = archiveStatus === "credited_not_withdrawable" || status === "pending";
292961
+ const classification = classifyDeposit(exchangeId, record);
292962
+ const isPending = classification.holdCursor;
292762
292963
  if (timestamp === undefined) {
292763
292964
  if (isPending) {
292764
292965
  return currentSince;
@@ -292878,7 +293079,8 @@ class DepositArchivePoller {
292878
293079
  if (!record) {
292879
293080
  continue;
292880
293081
  }
292881
- const assetSymbol = depositField(record, ["currency", "code", "asset"]);
293082
+ const info = asRecord(record.info);
293083
+ const assetSymbol = depositField(record, ["currency", "code", "asset"]) ?? info?.coin;
292882
293084
  const amount = depositField(record, ["amount"]);
292883
293085
  const address = depositField(record, [
292884
293086
  "address",
@@ -292887,8 +293089,21 @@ class DepositArchivePoller {
292887
293089
  "destination"
292888
293090
  ]);
292889
293091
  const txid = depositField(record, ["txid", "txId", "tx_hash", "txHash"]);
292890
- const network = depositField(record, ["network", "chain"]);
292891
- const archiveStatus = archivedDepositStatus(target.exchangeId, record);
293092
+ const network = depositField(record, ["network", "chain"]) ?? info?.network;
293093
+ const depositTxid = txid === undefined ? undefined : String(txid);
293094
+ const identity = depositIdentity({
293095
+ exchangeId: target.exchangeId,
293096
+ accountSelector: target.account.label,
293097
+ coin: assetSymbol,
293098
+ network,
293099
+ externalId: depositTxid,
293100
+ txid: depositTxid
293101
+ });
293102
+ const lastArchived = identity === undefined ? undefined : this.#lastArchivedByTarget.get(key)?.get(identity);
293103
+ const classification = classifyDeposit(target.exchangeId, record, lastArchived);
293104
+ if (lastArchived && lastArchived.status === classification.archiveStatus && lastArchived.progressKey === classification.progressKey) {
293105
+ continue;
293106
+ }
292892
293107
  const creditedAt = depositField(record, [
292893
293108
  "creditedAt",
292894
293109
  "credited_at",
@@ -292897,11 +293112,7 @@ class DepositArchivePoller {
292897
293112
  "timestamp",
292898
293113
  "datetime"
292899
293114
  ]);
292900
- const depositTxid = txid === undefined ? undefined : String(txid);
292901
- const lastArchived = depositTxid === undefined ? undefined : this.#lastArchivedByTarget.get(key)?.get(depositTxid);
292902
- if (lastArchived && lastArchived.status === archiveStatus) {
292903
- continue;
292904
- }
293115
+ const payload = classification.unlockProgress === undefined ? record : { ...record, unlock_progress: classification.unlockProgress };
292905
293116
  this.params.archiver.enqueue(buildTransferEventArchiveRow({
292906
293117
  tags: buildCommonArchiveTags({
292907
293118
  deploymentId: this.params.archiver.getDeploymentId(),
@@ -292912,25 +293123,27 @@ class DepositArchivePoller {
292912
293123
  transfer: {
292913
293124
  eventKind: "deposit",
292914
293125
  lifecycleAction: "observe_deposit",
292915
- status: archiveStatus,
293126
+ status: classification.archiveStatus,
292916
293127
  amount: amount === undefined ? undefined : String(amount),
292917
293128
  address: address === undefined ? undefined : String(address),
292918
293129
  network: network === undefined ? undefined : String(network),
292919
293130
  externalId: depositTxid,
292920
293131
  txid: depositTxid,
292921
293132
  exchangeTimestamp: normalizeTimestamp(creditedAt),
292922
- payload: record
293133
+ payload
292923
293134
  }
292924
293135
  }));
292925
- if (depositTxid !== undefined) {
293136
+ if (identity !== undefined) {
292926
293137
  let targetDeposits2 = this.#lastArchivedByTarget.get(key);
292927
293138
  if (!targetDeposits2) {
292928
293139
  targetDeposits2 = new Map;
292929
293140
  this.#lastArchivedByTarget.set(key, targetDeposits2);
292930
293141
  }
292931
- targetDeposits2.set(depositTxid, {
292932
- status: archiveStatus,
292933
- timestamp: depositTimestamp(record)
293142
+ targetDeposits2.set(identity, {
293143
+ status: classification.archiveStatus,
293144
+ timestamp: depositTimestamp(record),
293145
+ progressKey: classification.progressKey,
293146
+ highWatermark: classification.highWatermark
292934
293147
  });
292935
293148
  }
292936
293149
  archived += 1;
@@ -293098,10 +293311,7 @@ import { createHash as createHash4 } from "node:crypto";
293098
293311
  var MARKET_CAPTURE_SCHEMA_VERSION = "1.0.0";
293099
293312
  var CHECKSUM_ALGORITHM = "sha256-canonical-json-v1";
293100
293313
  var ARCHIVE_SOURCES = ["broker_read", "broker_write"];
293101
- var MARKET_ARCHIVE_SOURCES = [
293102
- ...ARCHIVE_SOURCES,
293103
- "external_backfill"
293104
- ];
293314
+ var MARKET_ARCHIVE_SOURCES = ARCHIVE_SOURCES;
293105
293315
  var CAPTURE_FEEDS = [
293106
293316
  "ORDERBOOK",
293107
293317
  "TICKER",
@@ -293115,15 +293325,12 @@ var SOURCE_MODES = [
293115
293325
  "broker_bootstrap_fetch_v1",
293116
293326
  "external_ccxt_fallback_v1",
293117
293327
  "external_hummingbot_fallback_v1",
293118
- "legacy_migration_v1",
293119
- "historical_vendor_orderbook_v1",
293120
- "vendor_historical_backfill_v1"
293328
+ "legacy_migration_v1"
293121
293329
  ];
293122
293330
  var RAW_CAPTURE_SCOPES = [
293123
293331
  "ccxt_normalized_object",
293124
293332
  "broker_visible_payload",
293125
- "exchange_wire_frame",
293126
- "vendor_normalized_dataset_file"
293333
+ "exchange_wire_frame"
293127
293334
  ];
293128
293335
  var CHECKSUM_FIELDS = new Set([
293129
293336
  "normalized_row_checksum",
@@ -293312,6 +293519,9 @@ function createRawCapture(context2, input) {
293312
293519
  checksumAlgorithm: context2.checksumAlgorithm
293313
293520
  };
293314
293521
  }
293522
+ function projectRawCapturePayload(rawCapture, redactedPayload) {
293523
+ return { ...rawCapture, redactedPayload };
293524
+ }
293315
293525
  function captureCoreFields(context2, rawCapture) {
293316
293526
  assertCaptureContext(context2);
293317
293527
  return {
@@ -293888,14 +294098,20 @@ function resolveConservativeOrderBookAcquisitionProfile(input) {
293888
294098
  id: `${exchange}:conservative:default`,
293889
294099
  upstreamLimit: undefined,
293890
294100
  guaranteedRetainedDepth: undefined,
293891
- coalescingSupported: false
294101
+ coalescingSupported: false,
294102
+ bidExhaustionEvidence: false,
294103
+ askExhaustionEvidence: false,
294104
+ exhaustionEvidenceValidated: true
293892
294105
  };
293893
294106
  }
293894
294107
  return {
293895
294108
  id: `${exchange}:conservative:limit:${input.requestedDepth}`,
293896
294109
  upstreamLimit: input.requestedDepth,
293897
294110
  guaranteedRetainedDepth: input.requestedDepth,
293898
- coalescingSupported: false
294111
+ coalescingSupported: false,
294112
+ bidExhaustionEvidence: false,
294113
+ askExhaustionEvidence: false,
294114
+ exhaustionEvidenceValidated: true
293899
294115
  };
293900
294116
  }
293901
294117
  function resolveOrderBookAcquisitionProfile(input) {
@@ -293907,7 +294123,10 @@ function resolveOrderBookAcquisitionProfile(input) {
293907
294123
  id: candidateProfileId,
293908
294124
  upstreamLimit: VERIFIED_PROFILE_DEPTH,
293909
294125
  guaranteedRetainedDepth: VERIFIED_PROFILE_DEPTH,
293910
- coalescingSupported: true
294126
+ coalescingSupported: true,
294127
+ bidExhaustionEvidence: false,
294128
+ askExhaustionEvidence: false,
294129
+ exhaustionEvidenceValidated: true
293911
294130
  };
293912
294131
  }
293913
294132
  return resolveConservativeOrderBookAcquisitionProfile(input);
@@ -294057,7 +294276,63 @@ class PublicFeedSubscriberBuffer {
294057
294276
  this.#bytes = 0;
294058
294277
  }
294059
294278
  }
294279
+ // src/helpers/market-data-archive/orderbook-depth.ts
294280
+ var DEFAULT_ORDERBOOK_ARCHIVE_DEPTH_LIMIT = 25;
294281
+ var MAX_ORDERBOOK_ARCHIVE_DEPTH_LIMIT = 500;
294282
+ var MAX_ORDERBOOK_MEASUREMENT_BANDS = 64;
294283
+ var MAX_ORDERBOOK_MEASUREMENT_BAND_BPS = 1e4;
294284
+ var DEFAULT_ORDERBOOK_MEASUREMENT_BANDS_BPS = [10, 25, 50, 100];
294285
+ function parseBoundedDepth(raw, field) {
294286
+ if (!/^[1-9]\d*$/.test(raw)) {
294287
+ throw new Error(`${field} must be an integer between 1 and 500`);
294288
+ }
294289
+ const parsed = Number(raw);
294290
+ if (!Number.isSafeInteger(parsed) || parsed > MAX_ORDERBOOK_ARCHIVE_DEPTH_LIMIT) {
294291
+ throw new Error(`${field} must be an integer between 1 and 500`);
294292
+ }
294293
+ return parsed;
294294
+ }
294295
+ function getOrderbookArchiveDepthLimit() {
294296
+ const raw = process.env.CEX_BROKER_ORDERBOOK_ARCHIVE_DEPTH_LIMIT?.trim();
294297
+ if (!raw) {
294298
+ return DEFAULT_ORDERBOOK_ARCHIVE_DEPTH_LIMIT;
294299
+ }
294300
+ return parseBoundedDepth(raw, "CEX_BROKER_ORDERBOOK_ARCHIVE_DEPTH_LIMIT");
294301
+ }
294302
+ function normalizeOrderbookMeasurementBandsBps(input) {
294303
+ const bands = input ?? DEFAULT_ORDERBOOK_MEASUREMENT_BANDS_BPS;
294304
+ if (bands.length === 0) {
294305
+ throw new Error("ORDERBOOK measurement bands must not be empty");
294306
+ }
294307
+ if (bands.length > MAX_ORDERBOOK_MEASUREMENT_BANDS) {
294308
+ throw new Error(`ORDERBOOK measurement bands must contain at most ${MAX_ORDERBOOK_MEASUREMENT_BANDS} entries`);
294309
+ }
294310
+ for (const band of bands) {
294311
+ if (!Number.isSafeInteger(band) || band <= 0 || band > MAX_ORDERBOOK_MEASUREMENT_BAND_BPS) {
294312
+ throw new Error("ORDERBOOK measurement bands must be integer basis points in 1..10000");
294313
+ }
294314
+ }
294315
+ const normalized = [...new Set(bands)].sort((left, right) => left - right);
294316
+ if (normalized.length > MAX_ORDERBOOK_MEASUREMENT_BANDS) {
294317
+ throw new Error(`ORDERBOOK measurement bands must contain at most ${MAX_ORDERBOOK_MEASUREMENT_BANDS} unique entries`);
294318
+ }
294319
+ return normalized;
294320
+ }
294321
+ function getOrderbookMeasurementBandsBps() {
294322
+ const raw = process.env.CEX_BROKER_ORDERBOOK_MEASUREMENT_BANDS_BPS?.trim();
294323
+ if (!raw) {
294324
+ return [...DEFAULT_ORDERBOOK_MEASUREMENT_BANDS_BPS];
294325
+ }
294326
+ const parts = raw.split(",").map((entry) => entry.trim());
294327
+ if (parts.some((entry) => !/^[1-9]\d*$/.test(entry))) {
294328
+ throw new Error("CEX_BROKER_ORDERBOOK_MEASUREMENT_BANDS_BPS must be a comma-separated list of positive integers");
294329
+ }
294330
+ return normalizeOrderbookMeasurementBandsBps(parts.map(Number));
294331
+ }
294332
+
294060
294333
  // src/helpers/market-data-archive/canonical-orderbook.ts
294334
+ var ORDERBOOK_SUMMARY_SCHEMA_VERSION = "2.0.0";
294335
+
294061
294336
  class OrderBookValidationError extends Error {
294062
294337
  reason;
294063
294338
  constructor(reason) {
@@ -294083,38 +294358,70 @@ function parseSequence(value) {
294083
294358
  }
294084
294359
  return parsed.toString(10);
294085
294360
  }
294086
- function validateSide(side, levels, depthLimit) {
294361
+ function validatePositiveInteger(value, field, maximum = Number.MAX_SAFE_INTEGER) {
294362
+ if (!Number.isSafeInteger(value) || value <= 0 || value > maximum) {
294363
+ throw new OrderBookValidationError(`${field} must be a positive integer${maximum === 500 ? " between 1 and 500" : ""}`);
294364
+ }
294365
+ }
294366
+ function validateSide(side, levels) {
294087
294367
  if (levels.length === 0) {
294088
294368
  throw new OrderBookValidationError(`${side} side is missing`);
294089
294369
  }
294090
- const retained = levels.slice(0, depthLimit);
294091
- for (let index2 = 0;index2 < retained.length; index2 += 1) {
294092
- const entry = retained[index2];
294370
+ const validated = [];
294371
+ for (let index2 = 0;index2 < levels.length; index2 += 1) {
294372
+ const entry = levels[index2];
294093
294373
  const price = entry?.[0];
294094
294374
  const amount = entry?.[1];
294095
294375
  if (!Array.isArray(entry) || entry.length < 2 || price === undefined || amount === undefined || !Number.isFinite(price) || !Number.isFinite(amount) || price <= 0 || amount <= 0) {
294096
294376
  throw new OrderBookValidationError(`${side} level ${index2} has a non-positive or non-finite price/amount`);
294097
294377
  }
294098
294378
  if (index2 > 0) {
294099
- const previous = retained[index2 - 1]?.[0];
294379
+ const previous = validated[index2 - 1]?.price;
294100
294380
  if (previous === undefined || (side === "bid" ? price >= previous : price <= previous)) {
294101
294381
  throw new OrderBookValidationError(`${side} levels are not strictly ${side === "bid" ? "descending" : "ascending"}`);
294102
294382
  }
294103
294383
  }
294384
+ validated.push({ price, amount });
294104
294385
  }
294105
- return retained.map(([price, amount]) => ({
294106
- price,
294107
- amount
294108
- }));
294386
+ return validated;
294109
294387
  }
294110
- function normalizedBands(input) {
294111
- const bands = input ?? [10, 25, 50, 100];
294112
- for (const band of bands) {
294113
- if (!Number.isFinite(band) || band <= 0 || !Number.isInteger(band)) {
294114
- throw new OrderBookValidationError("measurement bands must be positive integer basis points");
294388
+ function validateMetadata(metadata, observedBids, observedAsks) {
294389
+ if (!metadata.captureProfileId.trim()) {
294390
+ throw new OrderBookValidationError("capture_profile_id must not be empty");
294391
+ }
294392
+ validatePositiveInteger(metadata.effectiveCadenceMs, "effective_cadence_ms");
294393
+ if (metadata.requestedUpstreamDepth !== null) {
294394
+ validatePositiveInteger(metadata.requestedUpstreamDepth, "requested_upstream_depth", 500);
294395
+ }
294396
+ validatePositiveInteger(metadata.observedBidCount, "observed_bid_count");
294397
+ validatePositiveInteger(metadata.observedAskCount, "observed_ask_count");
294398
+ if (metadata.observedBidCount !== observedBids.length) {
294399
+ throw new OrderBookValidationError("observed_bid_count does not match the complete observation");
294400
+ }
294401
+ if (metadata.observedAskCount !== observedAsks.length) {
294402
+ throw new OrderBookValidationError("observed_ask_count does not match the complete observation");
294403
+ }
294404
+ const farthestBid = observedBids.at(-1)?.price;
294405
+ const farthestAsk = observedAsks.at(-1)?.price;
294406
+ if (!Number.isFinite(metadata.observedFarthestBid) || metadata.observedFarthestBid <= 0 || metadata.observedFarthestBid !== farthestBid) {
294407
+ throw new OrderBookValidationError("observed_farthest_bid does not match the complete observation");
294408
+ }
294409
+ if (!Number.isFinite(metadata.observedFarthestAsk) || metadata.observedFarthestAsk <= 0 || metadata.observedFarthestAsk !== farthestAsk) {
294410
+ throw new OrderBookValidationError("observed_farthest_ask does not match the complete observation");
294411
+ }
294412
+ for (const [side, evidence] of [
294413
+ ["bid", metadata.exhaustionEvidence.bid],
294414
+ ["ask", metadata.exhaustionEvidence.ask]
294415
+ ]) {
294416
+ if (evidence.validated !== true || typeof evidence.exhausted !== "boolean" || !evidence.source.trim()) {
294417
+ throw new OrderBookValidationError(`${side} exhaustion evidence must be validated and source-bound`);
294115
294418
  }
294116
294419
  }
294117
- return [...new Set(bands)].sort((left, right) => left - right);
294420
+ try {
294421
+ return normalizeOrderbookMeasurementBandsBps(metadata.measurementBandsBps);
294422
+ } catch (error) {
294423
+ throw new OrderBookValidationError(error instanceof Error ? error.message : "invalid measurement bands");
294424
+ }
294118
294425
  }
294119
294426
  function checksumRow(row) {
294120
294427
  return { ...row, normalized_row_checksum: sha256Canonical(row) };
@@ -294125,20 +294432,33 @@ function commonEvidenceFields(context2, rawCapture, input) {
294125
294432
  source_time_ms: input.eventTimeMs,
294126
294433
  received_time_ms: input.receivedTimeMs,
294127
294434
  snapshot_id: input.snapshotId,
294128
- construction_mode: input.constructionMode,
294435
+ construction_mode: "sampled_top_n_snapshot",
294129
294436
  gap_policy: "record_gap",
294130
294437
  depth_limit: input.depthLimit,
294131
294438
  sequence: input.sequence,
294132
294439
  exact_l2_reconstruction_complete: 0
294133
294440
  };
294134
294441
  }
294442
+ function sideBandEvidence(input) {
294443
+ const farthest = input.levels.at(-1)?.price;
294444
+ const depths = input.boundaries.map((boundary) => input.levels.filter(({ price }) => input.side === "bid" ? price >= boundary : price <= boundary).reduce((sum3, { amount }) => sum3 + amount, 0));
294445
+ const statuses = input.boundaries.map((boundary) => {
294446
+ const boundaryReached = input.side === "bid" ? farthest <= boundary : farthest >= boundary;
294447
+ return boundaryReached || input.exhausted ? "exact" : "censored";
294448
+ });
294449
+ return { depths, statuses };
294450
+ }
294135
294451
  function buildCanonicalOrderBookRows(input) {
294136
- if (!Number.isSafeInteger(input.depthLimit) || input.depthLimit <= 0 || input.depthLimit > 500) {
294137
- throw new OrderBookValidationError("depth limit must be an integer between 1 and 500");
294138
- }
294452
+ validatePositiveInteger(input.depthLimit, "depth limit", 500);
294139
294453
  if (input.context.feed !== "ORDERBOOK") {
294140
294454
  throw new OrderBookValidationError("capture context feed is not ORDERBOOK");
294141
294455
  }
294456
+ if (input.context.schemaVersion !== "1.0.0") {
294457
+ throw new OrderBookValidationError("ORDERBOOK raw and level capture schema must be 1.0.0");
294458
+ }
294459
+ if (!input.context.provenanceComplete) {
294460
+ throw new OrderBookValidationError("summary v2 requires complete live provenance");
294461
+ }
294142
294462
  if (input.constructionMode === "exact_l2_reconstruction") {
294143
294463
  throw new OrderBookValidationError("exact L2 requires a complete continuity proof and is unsupported");
294144
294464
  }
@@ -294153,13 +294473,18 @@ function buildCanonicalOrderBookRows(input) {
294153
294473
  if (receivedTimeMs < eventTimeMs) {
294154
294474
  throw new OrderBookValidationError("received timestamp precedes source timestamp");
294155
294475
  }
294156
- const bids = validateSide("bid", input.snapshot.bids, input.depthLimit);
294157
- const asks = validateSide("ask", input.snapshot.asks, input.depthLimit);
294158
- const bestBid = bids[0];
294159
- const bestAsk = asks[0];
294476
+ const observedBids = validateSide("bid", input.snapshot.bids);
294477
+ const observedAsks = validateSide("ask", input.snapshot.asks);
294478
+ const bestBid = observedBids[0];
294479
+ const bestAsk = observedAsks[0];
294160
294480
  if (bestBid.price >= bestAsk.price) {
294161
294481
  throw new OrderBookValidationError("book is crossed or locked");
294162
294482
  }
294483
+ const bands = validateMetadata(input.archiveMetadata, observedBids, observedAsks);
294484
+ const bids = observedBids.slice(0, input.depthLimit);
294485
+ const asks = observedAsks.slice(0, input.depthLimit);
294486
+ const retainedFarthestBid = bids.at(-1);
294487
+ const retainedFarthestAsk = asks.at(-1);
294163
294488
  const sequence = parseSequence(input.snapshot.sequence);
294164
294489
  const midPrice = (bestBid.price + bestAsk.price) / 2;
294165
294490
  const spread2 = bestAsk.price - bestBid.price;
@@ -294179,8 +294504,7 @@ function buildCanonicalOrderBookRows(input) {
294179
294504
  sequence,
294180
294505
  depthLimit: input.depthLimit,
294181
294506
  eventTimeMs,
294182
- receivedTimeMs,
294183
- constructionMode: input.constructionMode ?? "sampled_top_n_snapshot"
294507
+ receivedTimeMs
294184
294508
  });
294185
294509
  const levels = [
294186
294510
  ["bid", bids],
@@ -294198,17 +294522,34 @@ function buildCanonicalOrderBookRows(input) {
294198
294522
  });
294199
294523
  return { table: "market_data.cex_order_book_levels", row };
294200
294524
  }));
294201
- const bands = normalizedBands(input.measurementBandsBps);
294202
- const bidDepth = bands.map((band) => {
294203
- const minimumPrice = bestBid.price * (1 - band / 1e4);
294204
- return bids.filter(({ price }) => price >= minimumPrice).reduce((sum3, { amount }) => sum3 + amount, 0);
294525
+ const bidBoundaries = bands.map((band) => midPrice * (1 - band / 1e4));
294526
+ const askBoundaries = bands.map((band) => midPrice * (1 + band / 1e4));
294527
+ const bidEvidence = sideBandEvidence({
294528
+ levels: observedBids,
294529
+ boundaries: bidBoundaries,
294530
+ side: "bid",
294531
+ exhausted: input.archiveMetadata.exhaustionEvidence.bid.exhausted
294205
294532
  });
294206
- const askDepth = bands.map((band) => {
294207
- const maximumPrice = bestAsk.price * (1 + band / 1e4);
294208
- return asks.filter(({ price }) => price <= maximumPrice).reduce((sum3, { amount }) => sum3 + amount, 0);
294533
+ const askEvidence = sideBandEvidence({
294534
+ levels: observedAsks,
294535
+ boundaries: askBoundaries,
294536
+ side: "ask",
294537
+ exhausted: input.archiveMetadata.exhaustionEvidence.ask.exhausted
294209
294538
  });
294210
294539
  const summaryRow = checksumRow({
294211
294540
  ...common,
294541
+ schema_version: ORDERBOOK_SUMMARY_SCHEMA_VERSION,
294542
+ capture_profile_id: input.archiveMetadata.captureProfileId,
294543
+ effective_cadence_ms: input.archiveMetadata.effectiveCadenceMs,
294544
+ requested_upstream_depth: input.archiveMetadata.requestedUpstreamDepth,
294545
+ observed_bid_count: input.archiveMetadata.observedBidCount,
294546
+ observed_ask_count: input.archiveMetadata.observedAskCount,
294547
+ observed_farthest_bid: input.archiveMetadata.observedFarthestBid,
294548
+ observed_farthest_ask: input.archiveMetadata.observedFarthestAsk,
294549
+ retained_farthest_bid: retainedFarthestBid.price,
294550
+ retained_farthest_ask: retainedFarthestAsk.price,
294551
+ bid_exhausted: input.archiveMetadata.exhaustionEvidence.bid.exhausted ? 1 : 0,
294552
+ ask_exhausted: input.archiveMetadata.exhaustionEvidence.ask.exhausted ? 1 : 0,
294212
294553
  best_bid: bestBid.price,
294213
294554
  best_ask: bestAsk.price,
294214
294555
  best_bid_amount: bestBid.amount,
@@ -294220,8 +294561,12 @@ function buildCanonicalOrderBookRows(input) {
294220
294561
  bid_level_count: bids.length,
294221
294562
  ask_level_count: asks.length,
294222
294563
  measurement_bands_bps: bands,
294223
- bid_depth_by_band: bidDepth,
294224
- ask_depth_by_band: askDepth
294564
+ bid_boundary_price_by_band: bidBoundaries,
294565
+ ask_boundary_price_by_band: askBoundaries,
294566
+ bid_depth_by_band: bidEvidence.depths,
294567
+ ask_depth_by_band: askEvidence.depths,
294568
+ bid_status_by_band: bidEvidence.statuses,
294569
+ ask_status_by_band: askEvidence.statuses
294225
294570
  });
294226
294571
  return {
294227
294572
  snapshotId,
@@ -294351,21 +294696,6 @@ class OhlcvBarTracker {
294351
294696
  }
294352
294697
  }
294353
294698
 
294354
- // src/helpers/market-data-archive/orderbook-depth.ts
294355
- var DEFAULT_ORDERBOOK_ARCHIVE_DEPTH_LIMIT = 25;
294356
- var MAX_ORDERBOOK_ARCHIVE_DEPTH_LIMIT = 500;
294357
- function getOrderbookArchiveDepthLimit() {
294358
- const raw = process.env.CEX_BROKER_ORDERBOOK_ARCHIVE_DEPTH_LIMIT;
294359
- if (!raw) {
294360
- return DEFAULT_ORDERBOOK_ARCHIVE_DEPTH_LIMIT;
294361
- }
294362
- const parsed = Number.parseInt(raw, 10);
294363
- if (!Number.isFinite(parsed) || parsed <= 0) {
294364
- return DEFAULT_ORDERBOOK_ARCHIVE_DEPTH_LIMIT;
294365
- }
294366
- return Math.min(parsed, MAX_ORDERBOOK_ARCHIVE_DEPTH_LIMIT);
294367
- }
294368
-
294369
294699
  // src/helpers/market-data-archive/parse-stream.ts
294370
294700
  function isFiniteNumber2(value) {
294371
294701
  return typeof value === "number" && Number.isFinite(value);
@@ -294489,13 +294819,17 @@ function legacyMarketFields(context2, rawCapture) {
294489
294819
  function legacyDecimal8(value) {
294490
294820
  return value === undefined ? undefined : Number(value.toFixed(8));
294491
294821
  }
294492
- function buildCanonicalCexStreamEventRow(context2, rawCapture) {
294822
+ function buildCanonicalCexStreamEventRow(context2, rawCapture, options) {
294823
+ const payloadEncoding = options?.payloadEncoding ?? "canonical_json_v1";
294824
+ if (payloadEncoding === "orderbook_metadata_only_v1" && context2.feed !== "ORDERBOOK") {
294825
+ throw new Error("Metadata-only raw encoding requires an ORDERBOOK context");
294826
+ }
294493
294827
  const row = withNormalizedChecksum({
294494
294828
  ...captureCoreFields(context2, rawCapture),
294495
294829
  ...legacyMarketFields(context2, rawCapture),
294496
294830
  stream_type: context2.feed,
294497
294831
  event_time_ms: rawCapture.eventTimeMs,
294498
- payload_encoding: "canonical_json_v1",
294832
+ payload_encoding: payloadEncoding,
294499
294833
  payload_json: canonicalSerialize(rawCapture.redactedPayload)
294500
294834
  });
294501
294835
  return { table: "market_data.cex_stream_events", row };
@@ -294639,7 +294973,13 @@ function archiveOrderbookInBackground(archiver, otelMetrics, input, options) {
294639
294973
  queueMicrotask(() => {
294640
294974
  try {
294641
294975
  const context2 = resolveCaptureContext(archiver, input, "ORDERBOOK", options?.sourceMode ?? "broker_live_sampling_v1");
294642
- const rawCapture = createRawCapture(context2, {
294976
+ const depthLimit = options?.depthLimit ?? getOrderbookArchiveDepthLimit();
294977
+ const measurementBandsBps = normalizeOrderbookMeasurementBandsBps(input.archiveMetadata.measurementBandsBps);
294978
+ const archiveMetadata = {
294979
+ ...input.archiveMetadata,
294980
+ measurementBandsBps
294981
+ };
294982
+ const completeRawCapture = createRawCapture(context2, {
294643
294983
  payload: input.snapshot,
294644
294984
  eventTimeMs: input.snapshot.timestamp,
294645
294985
  receivedTimeMs: input.snapshot.receivedTimestamp,
@@ -294648,10 +294988,29 @@ function archiveOrderbookInBackground(archiver, otelMetrics, input, options) {
294648
294988
  const canonical = buildCanonicalOrderBookRows({
294649
294989
  context: context2,
294650
294990
  snapshot: input.snapshot,
294651
- rawCapture,
294652
- depthLimit: options?.depthLimit ?? getOrderbookArchiveDepthLimit()
294991
+ rawCapture: completeRawCapture,
294992
+ depthLimit,
294993
+ archiveMetadata
294653
294994
  });
294654
- archiver.enqueue(buildCanonicalCexStreamEventRow(context2, rawCapture));
294995
+ const rawMetadata = {
294996
+ capture_profile_id: archiveMetadata.captureProfileId,
294997
+ effective_cadence_ms: archiveMetadata.effectiveCadenceMs,
294998
+ requested_upstream_depth: archiveMetadata.requestedUpstreamDepth,
294999
+ archive_depth_limit: depthLimit,
295000
+ observed_bid_count: archiveMetadata.observedBidCount,
295001
+ observed_ask_count: archiveMetadata.observedAskCount,
295002
+ observed_farthest_bid: canonicalDecimal(archiveMetadata.observedFarthestBid),
295003
+ observed_farthest_ask: canonicalDecimal(archiveMetadata.observedFarthestAsk),
295004
+ bid_exhausted: archiveMetadata.exhaustionEvidence.bid.exhausted,
295005
+ ask_exhausted: archiveMetadata.exhaustionEvidence.ask.exhausted,
295006
+ retained_bid_count: canonical.levels.filter(({ row }) => row.side === "bid").length,
295007
+ retained_ask_count: canonical.levels.filter(({ row }) => row.side === "ask").length,
295008
+ measurement_bands_bps: measurementBandsBps
295009
+ };
295010
+ const storedRawCapture = projectRawCapturePayload(completeRawCapture, rawMetadata);
295011
+ archiver.enqueue(buildCanonicalCexStreamEventRow(context2, storedRawCapture, {
295012
+ payloadEncoding: "orderbook_metadata_only_v1"
295013
+ }));
294655
295014
  for (const row of canonical.levels)
294656
295015
  archiver.enqueue(row);
294657
295016
  archiver.enqueue(canonical.summary);
@@ -294773,6 +295132,9 @@ function archiveTickerInBackground(archiver, otelMetrics, input) {
294773
295132
  });
294774
295133
  }
294775
295134
  function archiveCexStreamEventInBackground(archiver, otelMetrics, input) {
295135
+ if (input.streamType === "ORDERBOOK") {
295136
+ throw new Error("Generic ORDERBOOK archive events are unsupported; use archiveOrderbookInBackground");
295137
+ }
294776
295138
  archiveMarketRowsInBackground(archiver, otelMetrics, "stream", input, input.streamType, () => [
294777
295139
  buildCexStreamEventRow({
294778
295140
  ...input,
@@ -294797,6 +295159,82 @@ function resolveOhlcvBootstrapLimit(optionValue) {
294797
295159
  }
294798
295160
  return Math.min(parsed, MAX_OHLCV_BOOTSTRAP_LIMIT);
294799
295161
  }
295162
+ // src/helpers/market-data-archive/summary-v2-conformance.ts
295163
+ var ORDERBOOK_SUMMARY_V2_SUPPORTED_VIEW_FIELDS = [
295164
+ ["source", "String", false],
295165
+ ["deployment_id", "String", false],
295166
+ ["capture_bundle_id", "String", false],
295167
+ ["exchange", "String", false],
295168
+ ["symbol", "String", false],
295169
+ ["trading_pair", "String", false],
295170
+ ["source_symbol", "String", false],
295171
+ ["asset_type", "String", false],
295172
+ ["feed", "String", false],
295173
+ ["provider", "String", false],
295174
+ ["source_mode", "String", false],
295175
+ ["source_time_ms", "UInt64", false],
295176
+ ["received_time_ms", "UInt64", false],
295177
+ ["raw_capture_id", "String", false],
295178
+ ["raw_capture_scope", "String", false],
295179
+ ["schema_version", "String", false],
295180
+ ["checksum_algorithm", "String", false],
295181
+ ["raw_checksum", "String", false],
295182
+ ["provenance_complete", "UInt8", false],
295183
+ ["snapshot_id", "String", false],
295184
+ ["construction_mode", "String", false],
295185
+ ["gap_policy", "String", false],
295186
+ ["depth_limit", "UInt16", false],
295187
+ ["sequence", "UInt64", true],
295188
+ ["exact_l2_reconstruction_complete", "UInt8", false],
295189
+ ["capture_profile_id", "String", false],
295190
+ ["effective_cadence_ms", "UInt32", false],
295191
+ ["requested_upstream_depth", "UInt16", true],
295192
+ ["observed_bid_count", "UInt32", false],
295193
+ ["observed_ask_count", "UInt32", false],
295194
+ ["observed_farthest_bid", "Decimal(38,18)", false],
295195
+ ["observed_farthest_ask", "Decimal(38,18)", false],
295196
+ ["retained_farthest_bid", "Decimal(38,18)", false],
295197
+ ["retained_farthest_ask", "Decimal(38,18)", false],
295198
+ ["bid_exhausted", "UInt8", false],
295199
+ ["ask_exhausted", "UInt8", false],
295200
+ ["best_bid", "Decimal(38,18)", false],
295201
+ ["best_ask", "Decimal(38,18)", false],
295202
+ ["best_bid_amount", "Decimal(38,18)", false],
295203
+ ["best_ask_amount", "Decimal(38,18)", false],
295204
+ ["mid_price", "Decimal(38,18)", false],
295205
+ ["spread", "Decimal(38,18)", false],
295206
+ ["spread_bps", "Float64", false],
295207
+ ["staleness_ms", "UInt64", false],
295208
+ ["bid_level_count", "UInt16", false],
295209
+ ["ask_level_count", "UInt16", false],
295210
+ ["measurement_bands_bps", "Array(UInt32)", false],
295211
+ ["bid_boundary_price_by_band", "Array(Decimal(38,18))", false],
295212
+ ["ask_boundary_price_by_band", "Array(Decimal(38,18))", false],
295213
+ ["bid_depth_by_band", "Array(Decimal(38,18))", false],
295214
+ ["ask_depth_by_band", "Array(Decimal(38,18))", false],
295215
+ ["bid_status_by_band", "Array(Enum8('exact'=1,'censored'=2))", false],
295216
+ ["ask_status_by_band", "Array(Enum8('exact'=1,'censored'=2))", false],
295217
+ ["normalized_row_checksum", "String", false]
295218
+ ];
295219
+ var ORDERBOOK_SUMMARY_V2_SUPPORTED_VIEW_FIELD_NAMES = ORDERBOOK_SUMMARY_V2_SUPPORTED_VIEW_FIELDS.map(([name]) => name);
295220
+ var DECIMAL_38_18_FIELDS = new Set([
295221
+ "observed_farthest_bid",
295222
+ "observed_farthest_ask",
295223
+ "retained_farthest_bid",
295224
+ "retained_farthest_ask",
295225
+ "best_bid",
295226
+ "best_ask",
295227
+ "best_bid_amount",
295228
+ "best_ask_amount",
295229
+ "mid_price",
295230
+ "spread"
295231
+ ]);
295232
+ var DECIMAL_38_18_ARRAY_FIELDS = new Set([
295233
+ "bid_boundary_price_by_band",
295234
+ "ask_boundary_price_by_band",
295235
+ "bid_depth_by_band",
295236
+ "ask_depth_by_band"
295237
+ ]);
294800
295238
  // src/helpers/order-book.ts
294801
295239
  var ORDER_BOOK_CALL_METHODS = {
294802
295240
  FETCH_CAPABILITY: "fetch_order_book_capability",
@@ -295148,7 +295586,7 @@ class PublicFeedWorker {
295148
295586
  if (this.options.profile) {
295149
295587
  this.options.metrics?.recordCounter("public_feed_acquisition_profiles_total", 1, {
295150
295588
  ...this.#metricLabels(),
295151
- profile: this.options.profile.id
295589
+ profile_class: this.options.profile.coalescingSupported ? "coalesced" : "conservative"
295152
295590
  });
295153
295591
  }
295154
295592
  this.#run = this.#watchLoop();
@@ -295216,7 +295654,34 @@ class PublicFeedWorker {
295216
295654
  depthLimit: retainedDepth,
295217
295655
  receivedTimestamp
295218
295656
  });
295219
- this.options.archiveSink.orderbook({ ...context2, snapshot }, { sampledOut: !this.#sampler.shouldEmit(receivedTimestamp) });
295657
+ const observedBidCount = snapshot.bids.length;
295658
+ const observedAskCount = snapshot.asks.length;
295659
+ this.options.archiveSink.orderbook({
295660
+ ...context2,
295661
+ snapshot,
295662
+ archiveMetadata: {
295663
+ captureProfileId: this.options.profile?.id ?? `${this.options.exchangeName}:conservative:default`,
295664
+ effectiveCadenceMs: getOrderbookIntervalMs(),
295665
+ requestedUpstreamDepth: this.options.profile?.upstreamLimit ?? null,
295666
+ observedBidCount,
295667
+ observedAskCount,
295668
+ observedFarthestBid: snapshot.bids.at(-1)?.[0] ?? Number.NaN,
295669
+ observedFarthestAsk: snapshot.asks.at(-1)?.[0] ?? Number.NaN,
295670
+ exhaustionEvidence: {
295671
+ bid: {
295672
+ exhausted: this.options.profile?.bidExhaustionEvidence ?? false,
295673
+ validated: this.options.profile?.exhaustionEvidenceValidated ?? true,
295674
+ source: this.options.profile?.id ?? "broker:conservative"
295675
+ },
295676
+ ask: {
295677
+ exhausted: this.options.profile?.askExhaustionEvidence ?? false,
295678
+ validated: this.options.profile?.exhaustionEvidenceValidated ?? true,
295679
+ source: this.options.profile?.id ?? "broker:conservative"
295680
+ }
295681
+ },
295682
+ measurementBandsBps: getOrderbookMeasurementBandsBps()
295683
+ }
295684
+ }, { sampledOut: !this.#sampler.shouldEmit(receivedTimestamp) });
295220
295685
  this.#recordCounter("public_feed_archive_decisions_total");
295221
295686
  this.options.observer?.archiveDecision?.(this.#observation());
295222
295687
  this.#fanout((subscriber) => this.#frame(projectOrderBookSnapshot(snapshot, subscriber.depthLimit), receivedTimestamp));
@@ -295389,7 +295854,7 @@ class PublicFeedWorker {
295389
295854
  }
295390
295855
  #metricLabels() {
295391
295856
  return {
295392
- exchange: this.options.exchangeName,
295857
+ exchange_bucket: this.options.exchangeOwned ? "dynamic" : "configured",
295393
295858
  feed: this.options.feed,
295394
295859
  market_type: this.options.marketType
295395
295860
  };
@@ -310756,7 +311221,29 @@ async function handleOrderBookCall(ctx) {
310756
311221
  assetType: "spot",
310757
311222
  accountSelector: ctx.selectedBrokerAccount?.label,
310758
311223
  deploymentId: ctx.brokerArchiver?.getDeploymentId() ?? "unarchived",
310759
- snapshot
311224
+ snapshot,
311225
+ archiveMetadata: {
311226
+ captureProfileId: `${orderBookPayload.exchange.trim().toLowerCase()}:current-snapshot:requested:${orderBookPayload.depthLimit}`,
311227
+ effectiveCadenceMs: 1,
311228
+ requestedUpstreamDepth: orderBookPayload.depthLimit,
311229
+ observedBidCount: snapshot.bids.length,
311230
+ observedAskCount: snapshot.asks.length,
311231
+ observedFarthestBid: snapshot.bids.at(-1)?.[0] ?? Number.NaN,
311232
+ observedFarthestAsk: snapshot.asks.at(-1)?.[0] ?? Number.NaN,
311233
+ exhaustionEvidence: {
311234
+ bid: {
311235
+ exhausted: false,
311236
+ validated: true,
311237
+ source: "broker:current-snapshot"
311238
+ },
311239
+ ask: {
311240
+ exhausted: false,
311241
+ validated: true,
311242
+ source: "broker:current-snapshot"
311243
+ }
311244
+ },
311245
+ measurementBandsBps: getOrderbookMeasurementBandsBps()
311246
+ }
310760
311247
  }, {
310761
311248
  sourceMode: "broker_current_snapshot_v1",
310762
311249
  depthLimit: orderBookPayload.depthLimit
@@ -313345,4 +313832,4 @@ export {
313345
313832
  CEXBroker as default
313346
313833
  };
313347
313834
 
313348
- //# debugId=298884FD1F08284464756E2164756E21
313835
+ //# debugId=46EFCE207D029E0F64756E2164756E21