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