@usherlabs/cex-broker 0.2.52 → 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 +364 -71
  2. package/dist/index.js +365 -72
  3. package/dist/index.js.map +14 -13
  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 -66
  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
  }
@@ -317778,10 +317797,7 @@ import { createHash as createHash4 } from "node:crypto";
317778
317797
  var MARKET_CAPTURE_SCHEMA_VERSION = "1.0.0";
317779
317798
  var CHECKSUM_ALGORITHM = "sha256-canonical-json-v1";
317780
317799
  var ARCHIVE_SOURCES = ["broker_read", "broker_write"];
317781
- var MARKET_ARCHIVE_SOURCES = [
317782
- ...ARCHIVE_SOURCES,
317783
- "external_backfill"
317784
- ];
317800
+ var MARKET_ARCHIVE_SOURCES = ARCHIVE_SOURCES;
317785
317801
  var CAPTURE_FEEDS = [
317786
317802
  "ORDERBOOK",
317787
317803
  "TICKER",
@@ -317795,15 +317811,12 @@ var SOURCE_MODES = [
317795
317811
  "broker_bootstrap_fetch_v1",
317796
317812
  "external_ccxt_fallback_v1",
317797
317813
  "external_hummingbot_fallback_v1",
317798
- "legacy_migration_v1",
317799
- "historical_vendor_orderbook_v1",
317800
- "vendor_historical_backfill_v1"
317814
+ "legacy_migration_v1"
317801
317815
  ];
317802
317816
  var RAW_CAPTURE_SCOPES = [
317803
317817
  "ccxt_normalized_object",
317804
317818
  "broker_visible_payload",
317805
- "exchange_wire_frame",
317806
- "vendor_normalized_dataset_file"
317819
+ "exchange_wire_frame"
317807
317820
  ];
317808
317821
  var CHECKSUM_FIELDS = new Set([
317809
317822
  "normalized_row_checksum",
@@ -317992,6 +318005,9 @@ function createRawCapture(context2, input) {
317992
318005
  checksumAlgorithm: context2.checksumAlgorithm
317993
318006
  };
317994
318007
  }
318008
+ function projectRawCapturePayload(rawCapture, redactedPayload) {
318009
+ return { ...rawCapture, redactedPayload };
318010
+ }
317995
318011
  function captureCoreFields(context2, rawCapture) {
317996
318012
  assertCaptureContext(context2);
317997
318013
  return {
@@ -318568,14 +318584,20 @@ function resolveConservativeOrderBookAcquisitionProfile(input) {
318568
318584
  id: `${exchange}:conservative:default`,
318569
318585
  upstreamLimit: undefined,
318570
318586
  guaranteedRetainedDepth: undefined,
318571
- coalescingSupported: false
318587
+ coalescingSupported: false,
318588
+ bidExhaustionEvidence: false,
318589
+ askExhaustionEvidence: false,
318590
+ exhaustionEvidenceValidated: true
318572
318591
  };
318573
318592
  }
318574
318593
  return {
318575
318594
  id: `${exchange}:conservative:limit:${input.requestedDepth}`,
318576
318595
  upstreamLimit: input.requestedDepth,
318577
318596
  guaranteedRetainedDepth: input.requestedDepth,
318578
- coalescingSupported: false
318597
+ coalescingSupported: false,
318598
+ bidExhaustionEvidence: false,
318599
+ askExhaustionEvidence: false,
318600
+ exhaustionEvidenceValidated: true
318579
318601
  };
318580
318602
  }
318581
318603
  function resolveOrderBookAcquisitionProfile(input) {
@@ -318587,7 +318609,10 @@ function resolveOrderBookAcquisitionProfile(input) {
318587
318609
  id: candidateProfileId,
318588
318610
  upstreamLimit: VERIFIED_PROFILE_DEPTH,
318589
318611
  guaranteedRetainedDepth: VERIFIED_PROFILE_DEPTH,
318590
- coalescingSupported: true
318612
+ coalescingSupported: true,
318613
+ bidExhaustionEvidence: false,
318614
+ askExhaustionEvidence: false,
318615
+ exhaustionEvidenceValidated: true
318591
318616
  };
318592
318617
  }
318593
318618
  return resolveConservativeOrderBookAcquisitionProfile(input);
@@ -318737,7 +318762,63 @@ class PublicFeedSubscriberBuffer {
318737
318762
  this.#bytes = 0;
318738
318763
  }
318739
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
+
318740
318819
  // src/helpers/market-data-archive/canonical-orderbook.ts
318820
+ var ORDERBOOK_SUMMARY_SCHEMA_VERSION = "2.0.0";
318821
+
318741
318822
  class OrderBookValidationError extends Error {
318742
318823
  reason;
318743
318824
  constructor(reason) {
@@ -318763,38 +318844,70 @@ function parseSequence(value) {
318763
318844
  }
318764
318845
  return parsed.toString(10);
318765
318846
  }
318766
- 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) {
318767
318853
  if (levels.length === 0) {
318768
318854
  throw new OrderBookValidationError(`${side} side is missing`);
318769
318855
  }
318770
- const retained = levels.slice(0, depthLimit);
318771
- for (let index2 = 0;index2 < retained.length; index2 += 1) {
318772
- const entry = retained[index2];
318856
+ const validated = [];
318857
+ for (let index2 = 0;index2 < levels.length; index2 += 1) {
318858
+ const entry = levels[index2];
318773
318859
  const price = entry?.[0];
318774
318860
  const amount = entry?.[1];
318775
318861
  if (!Array.isArray(entry) || entry.length < 2 || price === undefined || amount === undefined || !Number.isFinite(price) || !Number.isFinite(amount) || price <= 0 || amount <= 0) {
318776
318862
  throw new OrderBookValidationError(`${side} level ${index2} has a non-positive or non-finite price/amount`);
318777
318863
  }
318778
318864
  if (index2 > 0) {
318779
- const previous = retained[index2 - 1]?.[0];
318865
+ const previous = validated[index2 - 1]?.price;
318780
318866
  if (previous === undefined || (side === "bid" ? price >= previous : price <= previous)) {
318781
318867
  throw new OrderBookValidationError(`${side} levels are not strictly ${side === "bid" ? "descending" : "ascending"}`);
318782
318868
  }
318783
318869
  }
318870
+ validated.push({ price, amount });
318784
318871
  }
318785
- return retained.map(([price, amount]) => ({
318786
- price,
318787
- amount
318788
- }));
318872
+ return validated;
318789
318873
  }
318790
- function normalizedBands(input) {
318791
- const bands = input ?? [10, 25, 50, 100];
318792
- for (const band of bands) {
318793
- if (!Number.isFinite(band) || band <= 0 || !Number.isInteger(band)) {
318794
- 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`);
318795
318904
  }
318796
318905
  }
318797
- 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
+ }
318798
318911
  }
318799
318912
  function checksumRow(row) {
318800
318913
  return { ...row, normalized_row_checksum: sha256Canonical(row) };
@@ -318805,20 +318918,33 @@ function commonEvidenceFields(context2, rawCapture, input) {
318805
318918
  source_time_ms: input.eventTimeMs,
318806
318919
  received_time_ms: input.receivedTimeMs,
318807
318920
  snapshot_id: input.snapshotId,
318808
- construction_mode: input.constructionMode,
318921
+ construction_mode: "sampled_top_n_snapshot",
318809
318922
  gap_policy: "record_gap",
318810
318923
  depth_limit: input.depthLimit,
318811
318924
  sequence: input.sequence,
318812
318925
  exact_l2_reconstruction_complete: 0
318813
318926
  };
318814
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
+ }
318815
318937
  function buildCanonicalOrderBookRows(input) {
318816
- if (!Number.isSafeInteger(input.depthLimit) || input.depthLimit <= 0 || input.depthLimit > 500) {
318817
- throw new OrderBookValidationError("depth limit must be an integer between 1 and 500");
318818
- }
318938
+ validatePositiveInteger(input.depthLimit, "depth limit", 500);
318819
318939
  if (input.context.feed !== "ORDERBOOK") {
318820
318940
  throw new OrderBookValidationError("capture context feed is not ORDERBOOK");
318821
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
+ }
318822
318948
  if (input.constructionMode === "exact_l2_reconstruction") {
318823
318949
  throw new OrderBookValidationError("exact L2 requires a complete continuity proof and is unsupported");
318824
318950
  }
@@ -318833,13 +318959,18 @@ function buildCanonicalOrderBookRows(input) {
318833
318959
  if (receivedTimeMs < eventTimeMs) {
318834
318960
  throw new OrderBookValidationError("received timestamp precedes source timestamp");
318835
318961
  }
318836
- const bids = validateSide("bid", input.snapshot.bids, input.depthLimit);
318837
- const asks = validateSide("ask", input.snapshot.asks, input.depthLimit);
318838
- const bestBid = bids[0];
318839
- 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];
318840
318966
  if (bestBid.price >= bestAsk.price) {
318841
318967
  throw new OrderBookValidationError("book is crossed or locked");
318842
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);
318843
318974
  const sequence = parseSequence(input.snapshot.sequence);
318844
318975
  const midPrice = (bestBid.price + bestAsk.price) / 2;
318845
318976
  const spread2 = bestAsk.price - bestBid.price;
@@ -318859,8 +318990,7 @@ function buildCanonicalOrderBookRows(input) {
318859
318990
  sequence,
318860
318991
  depthLimit: input.depthLimit,
318861
318992
  eventTimeMs,
318862
- receivedTimeMs,
318863
- constructionMode: input.constructionMode ?? "sampled_top_n_snapshot"
318993
+ receivedTimeMs
318864
318994
  });
318865
318995
  const levels = [
318866
318996
  ["bid", bids],
@@ -318878,17 +319008,34 @@ function buildCanonicalOrderBookRows(input) {
318878
319008
  });
318879
319009
  return { table: "market_data.cex_order_book_levels", row };
318880
319010
  }));
318881
- const bands = normalizedBands(input.measurementBandsBps);
318882
- const bidDepth = bands.map((band) => {
318883
- const minimumPrice = bestBid.price * (1 - band / 1e4);
318884
- 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
318885
319018
  });
318886
- const askDepth = bands.map((band) => {
318887
- const maximumPrice = bestAsk.price * (1 + band / 1e4);
318888
- 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
318889
319024
  });
318890
319025
  const summaryRow = checksumRow({
318891
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,
318892
319039
  best_bid: bestBid.price,
318893
319040
  best_ask: bestAsk.price,
318894
319041
  best_bid_amount: bestBid.amount,
@@ -318900,8 +319047,12 @@ function buildCanonicalOrderBookRows(input) {
318900
319047
  bid_level_count: bids.length,
318901
319048
  ask_level_count: asks.length,
318902
319049
  measurement_bands_bps: bands,
318903
- bid_depth_by_band: bidDepth,
318904
- 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
318905
319056
  });
318906
319057
  return {
318907
319058
  snapshotId,
@@ -319031,21 +319182,6 @@ class OhlcvBarTracker {
319031
319182
  }
319032
319183
  }
319033
319184
 
319034
- // src/helpers/market-data-archive/orderbook-depth.ts
319035
- var DEFAULT_ORDERBOOK_ARCHIVE_DEPTH_LIMIT = 25;
319036
- var MAX_ORDERBOOK_ARCHIVE_DEPTH_LIMIT = 500;
319037
- function getOrderbookArchiveDepthLimit() {
319038
- const raw = process.env.CEX_BROKER_ORDERBOOK_ARCHIVE_DEPTH_LIMIT;
319039
- if (!raw) {
319040
- return DEFAULT_ORDERBOOK_ARCHIVE_DEPTH_LIMIT;
319041
- }
319042
- const parsed = Number.parseInt(raw, 10);
319043
- if (!Number.isFinite(parsed) || parsed <= 0) {
319044
- return DEFAULT_ORDERBOOK_ARCHIVE_DEPTH_LIMIT;
319045
- }
319046
- return Math.min(parsed, MAX_ORDERBOOK_ARCHIVE_DEPTH_LIMIT);
319047
- }
319048
-
319049
319185
  // src/helpers/market-data-archive/parse-stream.ts
319050
319186
  function isFiniteNumber2(value) {
319051
319187
  return typeof value === "number" && Number.isFinite(value);
@@ -319169,13 +319305,17 @@ function legacyMarketFields(context2, rawCapture) {
319169
319305
  function legacyDecimal8(value) {
319170
319306
  return value === undefined ? undefined : Number(value.toFixed(8));
319171
319307
  }
319172
- 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
+ }
319173
319313
  const row = withNormalizedChecksum({
319174
319314
  ...captureCoreFields(context2, rawCapture),
319175
319315
  ...legacyMarketFields(context2, rawCapture),
319176
319316
  stream_type: context2.feed,
319177
319317
  event_time_ms: rawCapture.eventTimeMs,
319178
- payload_encoding: "canonical_json_v1",
319318
+ payload_encoding: payloadEncoding,
319179
319319
  payload_json: canonicalSerialize(rawCapture.redactedPayload)
319180
319320
  });
319181
319321
  return { table: "market_data.cex_stream_events", row };
@@ -319319,7 +319459,13 @@ function archiveOrderbookInBackground(archiver, otelMetrics, input, options) {
319319
319459
  queueMicrotask(() => {
319320
319460
  try {
319321
319461
  const context2 = resolveCaptureContext(archiver, input, "ORDERBOOK", options?.sourceMode ?? "broker_live_sampling_v1");
319322
- 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, {
319323
319469
  payload: input.snapshot,
319324
319470
  eventTimeMs: input.snapshot.timestamp,
319325
319471
  receivedTimeMs: input.snapshot.receivedTimestamp,
@@ -319328,10 +319474,29 @@ function archiveOrderbookInBackground(archiver, otelMetrics, input, options) {
319328
319474
  const canonical = buildCanonicalOrderBookRows({
319329
319475
  context: context2,
319330
319476
  snapshot: input.snapshot,
319331
- rawCapture,
319332
- depthLimit: options?.depthLimit ?? getOrderbookArchiveDepthLimit()
319477
+ rawCapture: completeRawCapture,
319478
+ depthLimit,
319479
+ archiveMetadata
319333
319480
  });
319334
- 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
+ }));
319335
319500
  for (const row of canonical.levels)
319336
319501
  archiver.enqueue(row);
319337
319502
  archiver.enqueue(canonical.summary);
@@ -319453,6 +319618,9 @@ function archiveTickerInBackground(archiver, otelMetrics, input) {
319453
319618
  });
319454
319619
  }
319455
319620
  function archiveCexStreamEventInBackground(archiver, otelMetrics, input) {
319621
+ if (input.streamType === "ORDERBOOK") {
319622
+ throw new Error("Generic ORDERBOOK archive events are unsupported; use archiveOrderbookInBackground");
319623
+ }
319456
319624
  archiveMarketRowsInBackground(archiver, otelMetrics, "stream", input, input.streamType, () => [
319457
319625
  buildCexStreamEventRow({
319458
319626
  ...input,
@@ -319477,6 +319645,82 @@ function resolveOhlcvBootstrapLimit(optionValue) {
319477
319645
  }
319478
319646
  return Math.min(parsed, MAX_OHLCV_BOOTSTRAP_LIMIT);
319479
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
+ ]);
319480
319724
  // src/helpers/order-book.ts
319481
319725
  var ORDER_BOOK_CALL_METHODS = {
319482
319726
  FETCH_CAPABILITY: "fetch_order_book_capability",
@@ -319828,7 +320072,7 @@ class PublicFeedWorker {
319828
320072
  if (this.options.profile) {
319829
320073
  this.options.metrics?.recordCounter("public_feed_acquisition_profiles_total", 1, {
319830
320074
  ...this.#metricLabels(),
319831
- profile: this.options.profile.id
320075
+ profile_class: this.options.profile.coalescingSupported ? "coalesced" : "conservative"
319832
320076
  });
319833
320077
  }
319834
320078
  this.#run = this.#watchLoop();
@@ -319896,7 +320140,34 @@ class PublicFeedWorker {
319896
320140
  depthLimit: retainedDepth,
319897
320141
  receivedTimestamp
319898
320142
  });
319899
- 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) });
319900
320171
  this.#recordCounter("public_feed_archive_decisions_total");
319901
320172
  this.options.observer?.archiveDecision?.(this.#observation());
319902
320173
  this.#fanout((subscriber) => this.#frame(projectOrderBookSnapshot(snapshot, subscriber.depthLimit), receivedTimestamp));
@@ -320069,7 +320340,7 @@ class PublicFeedWorker {
320069
320340
  }
320070
320341
  #metricLabels() {
320071
320342
  return {
320072
- exchange: this.options.exchangeName,
320343
+ exchange_bucket: this.options.exchangeOwned ? "dynamic" : "configured",
320073
320344
  feed: this.options.feed,
320074
320345
  market_type: this.options.marketType
320075
320346
  };
@@ -335436,7 +335707,29 @@ async function handleOrderBookCall(ctx) {
335436
335707
  assetType: "spot",
335437
335708
  accountSelector: ctx.selectedBrokerAccount?.label,
335438
335709
  deploymentId: ctx.brokerArchiver?.getDeploymentId() ?? "unarchived",
335439
- 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
+ }
335440
335733
  }, {
335441
335734
  sourceMode: "broker_current_snapshot_v1",
335442
335735
  depthLimit: orderBookPayload.depthLimit