@usherlabs/cex-broker 0.2.49 → 0.2.51
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/cex-canonical-orderbook-export.js +7747 -883
- package/dist/commands/cex-canonical-orderbook-export.js.map +41 -10
- package/dist/commands/cli.js +127 -34
- package/dist/commands/market-data-vendor-backfill.js +8284 -6391
- package/dist/commands/market-data-vendor-backfill.js.map +16 -15
- package/dist/helpers/canonical-orderbook-export/parquet-projection.d.ts +19 -0
- package/dist/helpers/market-data-archive/canonical-orderbook.d.ts +1 -1
- package/dist/helpers/market-data-preparation/conformance-fixtures.d.ts +114 -2
- package/dist/helpers/market-data-preparation/contracts.d.ts +1298 -18
- package/dist/helpers/market-data-preparation/required-clock-qualification.d.ts +43 -0
- package/dist/helpers/market-data-preparation/source-tape-dependencies.d.ts +19 -0
- package/dist/helpers/market-data-preparation/source-tape-operation.d.ts +109 -0
- package/dist/helpers/market-data-source-forensics.d.ts +400 -0
- package/dist/helpers/market-data-vendor-backfill/conformance-fixtures.d.ts +3 -5
- package/dist/helpers/market-data-vendor-backfill/contracts.d.ts +21 -56
- package/dist/helpers/market-data-vendor-backfill/core.d.ts +1 -0
- package/dist/helpers/market-data-vendor-backfill/cryptohftdata.d.ts +45 -5
- package/dist/helpers/market-data-vendor-backfill/manifests.d.ts +31 -1083
- package/dist/helpers/market-data-vendor-backfill/promotion.d.ts +1 -0
- package/dist/helpers/source-tape-sandbox.d.ts +82 -0
- package/dist/helpers/source-tape.d.ts +165 -0
- package/dist/helpers/trace-context.d.ts +8 -0
- package/dist/index.js +128 -35
- package/dist/index.js.map +7 -6
- package/dist/market-data-preparation/fixtures/{conformance-v2.json → conformance-v3.json} +257 -60
- package/dist/market-data-preparation/policies/capability-policy.json +15 -4
- package/dist/market-data-preparation/policies/resource-policy.json +3 -3
- package/dist/market-data-preparation/policies/source-tape-capability-v1.json +35 -0
- package/dist/market-data-preparation/schema-manifest.json +30 -10
- package/dist/market-data-preparation/schemas/canonical-orderbook-export-request-v1.schema.json +5 -1
- package/dist/market-data-preparation/schemas/{canonical-orderbook-export-result-v1.schema.json → canonical-orderbook-export-result-v2.schema.json} +20 -6
- package/dist/market-data-preparation/schemas/order-book-depth-summary-parquet-projection-v1.schema.json +256 -0
- package/dist/market-data-preparation/schemas/order-book-levels-parquet-projection-v1.schema.json +220 -0
- package/dist/market-data-preparation/schemas/{preparation-product-pin-v1.schema.json → preparation-product-pin-v2.schema.json} +59 -9
- package/dist/market-data-preparation/schemas/promotion-receipt-v1.schema.json +5 -1
- package/dist/market-data-preparation/schemas/source-forensics-ledger-v1.schema.json +463 -0
- package/dist/market-data-preparation/schemas/source-qualification-record-v1.schema.json +394 -0
- package/dist/market-data-preparation.d.ts +7 -0
- package/dist/market-data-preparation.js +35443 -25203
- package/dist/market-data-preparation.js.map +118 -78
- package/dist/market-data-vendor-backfill.d.ts +8 -5
- package/dist/market-data-vendor-backfill.js +19444 -15553
- package/dist/market-data-vendor-backfill.js.map +65 -60
- package/package.json +1 -5
- package/dist/market-data-preparation/policies/capability-policy-v1.json +0 -28
- package/dist/market-data-vendor-backfill/fixtures/conformance-v1.json +0 -448
- package/dist/market-data-vendor-backfill/policies/capability-policy-v2.json +0 -28
- package/dist/market-data-vendor-backfill/policies/capability-policy.json +0 -28
- package/dist/market-data-vendor-backfill/policies/resource-policy.json +0 -16
- package/dist/market-data-vendor-backfill/schema-manifest.json +0 -31
- package/dist/market-data-vendor-backfill/schemas/archive-selection.schema.json +0 -224
- package/dist/market-data-vendor-backfill/schemas/promotion-receipt.schema.json +0 -171
- package/dist/market-data-vendor-backfill/schemas/request.schema.json +0 -166
- package/dist/market-data-vendor-backfill/schemas/required-clock.schema.json +0 -47
- package/dist/market-data-vendor-backfill/schemas/result.schema.json +0 -198
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type BackfillArchiveRow, type PromotionReceipt, type PromotionReceiptWire } from "./contracts";
|
|
2
2
|
export type StablePromotionReceipt = Omit<PromotionReceipt, "receiptId" | "verificationTimeMs">;
|
|
3
|
+
export declare function promotionReceiptMatchesCurrentPolicies(receipt: PromotionReceiptWire): boolean;
|
|
3
4
|
export declare function promotionReceiptId(receipt: StablePromotionReceipt): string;
|
|
4
5
|
type UnfinalizedPromotionReceiptWire = Omit<PromotionReceiptWire, "receipt_id" | "promotion_identity_sha256"> & Partial<Pick<PromotionReceiptWire, "receipt_id" | "promotion_identity_sha256">>;
|
|
5
6
|
export declare function finalizePromotionReceipt(receipt: UnfinalizedPromotionReceiptWire): PromotionReceiptWire;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { ArchiveQueryClient } from "./market-data-vendor-backfill/archive-reader";
|
|
2
|
+
import type { ArchiveSelectionWire, ForwarderBatch, MarketDataVendorBackfillRequest, ProviderObjectEvidence } from "./market-data-vendor-backfill/contracts";
|
|
3
|
+
import type { CandidateVerification } from "./market-data-vendor-backfill/core";
|
|
4
|
+
import { SOURCE_TAPE_CONSTRUCTION_MODE, SOURCE_TAPE_SANDBOX_TARGET, type SourceTapeArchiveSinkResult } from "./source-tape";
|
|
5
|
+
export declare function verifySourceTapeArchive(input: {
|
|
6
|
+
request: MarketDataVendorBackfillRequest;
|
|
7
|
+
sinkResult: SourceTapeArchiveSinkResult;
|
|
8
|
+
client: ArchiveQueryClient;
|
|
9
|
+
}): Promise<CandidateVerification>;
|
|
10
|
+
export type SourceTapeExportArtifact = {
|
|
11
|
+
file_name: string;
|
|
12
|
+
rows: number;
|
|
13
|
+
bytes: number;
|
|
14
|
+
sha256: string;
|
|
15
|
+
projection_schema_id: string;
|
|
16
|
+
projection_schema_sha256: string;
|
|
17
|
+
};
|
|
18
|
+
export type SourceTapeExportResult = {
|
|
19
|
+
promotionReceiptIds: string[];
|
|
20
|
+
levels: SourceTapeExportArtifact;
|
|
21
|
+
summary: SourceTapeExportArtifact;
|
|
22
|
+
};
|
|
23
|
+
export type SourceTapeSandboxEvidence = {
|
|
24
|
+
schema_id: "market-data-source-tape-sandbox-evidence/v1";
|
|
25
|
+
archive_target: typeof SOURCE_TAPE_SANDBOX_TARGET;
|
|
26
|
+
construction_mode: typeof SOURCE_TAPE_CONSTRUCTION_MODE;
|
|
27
|
+
normal_archive_path: true;
|
|
28
|
+
tape_capability: {
|
|
29
|
+
policy_id: string;
|
|
30
|
+
policy_sha256: string;
|
|
31
|
+
};
|
|
32
|
+
state_count: number;
|
|
33
|
+
promotion: {
|
|
34
|
+
receipt_id: string;
|
|
35
|
+
promotion_identity_sha256: string;
|
|
36
|
+
qualification_event_id: string;
|
|
37
|
+
};
|
|
38
|
+
selection: {
|
|
39
|
+
selection_sha256: string;
|
|
40
|
+
receipt_ids: string[];
|
|
41
|
+
};
|
|
42
|
+
export: {
|
|
43
|
+
levels: SourceTapeExportArtifact;
|
|
44
|
+
summary: SourceTapeExportArtifact;
|
|
45
|
+
};
|
|
46
|
+
projection_schema_pins: readonly {
|
|
47
|
+
schema_id: string;
|
|
48
|
+
schema_sha256: string;
|
|
49
|
+
}[];
|
|
50
|
+
};
|
|
51
|
+
export declare function promoteAndExportSourceTapeSandbox(input: {
|
|
52
|
+
request: MarketDataVendorBackfillRequest;
|
|
53
|
+
sinkResult: SourceTapeArchiveSinkResult;
|
|
54
|
+
verification: CandidateVerification;
|
|
55
|
+
datasetObjects: ProviderObjectEvidence[];
|
|
56
|
+
vendorSemanticDigest: string;
|
|
57
|
+
verifiedAt: string;
|
|
58
|
+
forwarder: {
|
|
59
|
+
submit(batch: ForwarderBatch): Promise<{
|
|
60
|
+
ok: boolean;
|
|
61
|
+
inserted: number;
|
|
62
|
+
}>;
|
|
63
|
+
};
|
|
64
|
+
archive: {
|
|
65
|
+
resolveSelection(request: MarketDataVendorBackfillRequest): Promise<ArchiveSelectionWire>;
|
|
66
|
+
};
|
|
67
|
+
exporter: {
|
|
68
|
+
export(request: {
|
|
69
|
+
schema_id: "https://schemas.usher.so/cex-canonical-orderbook-export-request/v1";
|
|
70
|
+
request_id: string;
|
|
71
|
+
target: {
|
|
72
|
+
environment: string;
|
|
73
|
+
cluster: string;
|
|
74
|
+
};
|
|
75
|
+
selection: ArchiveSelectionWire;
|
|
76
|
+
depth: 100;
|
|
77
|
+
construction_mode: typeof SOURCE_TAPE_CONSTRUCTION_MODE;
|
|
78
|
+
canonical_schema_version: string;
|
|
79
|
+
checksum_algorithm: "sha256-canonical-json-v1";
|
|
80
|
+
}): Promise<SourceTapeExportResult>;
|
|
81
|
+
};
|
|
82
|
+
}): Promise<SourceTapeSandboxEvidence>;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import type { BackfillArchiveRow, ForwarderBatch } from "./market-data-vendor-backfill/contracts";
|
|
2
|
+
import { type PolicyNeutralCryptoHftBookState, type PolicyNeutralTapeSink } from "./market-data-vendor-backfill/cryptohftdata";
|
|
3
|
+
export declare const SOURCE_TAPE_CAPABILITY: Readonly<{
|
|
4
|
+
policy_id: string;
|
|
5
|
+
provider: string;
|
|
6
|
+
adapter_version: string;
|
|
7
|
+
acquisition_policy_id: string;
|
|
8
|
+
exchange: string;
|
|
9
|
+
market_type: string;
|
|
10
|
+
feed: string;
|
|
11
|
+
trading_pairs: string[];
|
|
12
|
+
construction_mode: string;
|
|
13
|
+
canonical_schema_id: string;
|
|
14
|
+
depth: number;
|
|
15
|
+
source_window_scan: string;
|
|
16
|
+
provider_object_inventory: string;
|
|
17
|
+
state_emission: string;
|
|
18
|
+
state_window: string;
|
|
19
|
+
archive_target: {
|
|
20
|
+
environment: string;
|
|
21
|
+
cluster: string;
|
|
22
|
+
};
|
|
23
|
+
archive_path: string;
|
|
24
|
+
submission_bounds: {
|
|
25
|
+
max_states_per_yield: number;
|
|
26
|
+
max_rows_per_batch: number;
|
|
27
|
+
max_bytes_per_batch: number;
|
|
28
|
+
max_in_flight: number;
|
|
29
|
+
};
|
|
30
|
+
clickhouse_image: string;
|
|
31
|
+
clickhouse_server_version: string;
|
|
32
|
+
projection_schema_ids: string[];
|
|
33
|
+
policy_sha256: string;
|
|
34
|
+
}>;
|
|
35
|
+
export declare const SOURCE_TAPE_CAPABILITY_ID: string;
|
|
36
|
+
export declare const SOURCE_TAPE_CONSTRUCTION_MODE: "policy_neutral_top_n_state_change_tape/v1";
|
|
37
|
+
export declare const SOURCE_TAPE_DEPTH: 100;
|
|
38
|
+
export declare const SOURCE_TAPE_MAX_STATES_PER_YIELD: 4;
|
|
39
|
+
export declare const SOURCE_TAPE_MAX_BATCH_ROWS = 1000;
|
|
40
|
+
export declare const SOURCE_TAPE_MAX_BATCH_BYTES: number;
|
|
41
|
+
export declare const SOURCE_TAPE_MAX_IN_FLIGHT: 1;
|
|
42
|
+
export declare const SOURCE_TAPE_SANDBOX_TARGET: Readonly<{
|
|
43
|
+
environment: "sandbox";
|
|
44
|
+
cluster: "cex-archive-local";
|
|
45
|
+
}>;
|
|
46
|
+
export declare const SOURCE_TAPE_PROJECTION_PINS: readonly Readonly<{
|
|
47
|
+
schema_id: string;
|
|
48
|
+
schema_sha256: string;
|
|
49
|
+
}>[];
|
|
50
|
+
export type SourceTapeProviderObjectInventory = {
|
|
51
|
+
expected_identities: string[];
|
|
52
|
+
observed_identities: string[];
|
|
53
|
+
complete: boolean;
|
|
54
|
+
};
|
|
55
|
+
export declare function sourceTapeCaptureBundleId(input: {
|
|
56
|
+
idempotencyKey: string;
|
|
57
|
+
tradingPair: "ARB-USDC" | "ARB-USDT";
|
|
58
|
+
window: {
|
|
59
|
+
startTimeMs: number;
|
|
60
|
+
endTimeMs: number;
|
|
61
|
+
};
|
|
62
|
+
expectedObjectIdentities: readonly string[];
|
|
63
|
+
}): string;
|
|
64
|
+
/**
|
|
65
|
+
* `production` is the stable forwarder mutation-authorization class. It is not
|
|
66
|
+
* an environment assertion: source-tape qualification remains sandbox-local.
|
|
67
|
+
*/
|
|
68
|
+
export declare function assertSourceTapeSandboxAuthorization(input: {
|
|
69
|
+
requestAuthorizationId: string;
|
|
70
|
+
requestTarget: {
|
|
71
|
+
environment: string;
|
|
72
|
+
cluster: string;
|
|
73
|
+
};
|
|
74
|
+
preflight: {
|
|
75
|
+
authorizationId: string;
|
|
76
|
+
scope: "production";
|
|
77
|
+
environment: string;
|
|
78
|
+
cluster: string;
|
|
79
|
+
credentialValidated: boolean;
|
|
80
|
+
};
|
|
81
|
+
}): void;
|
|
82
|
+
export type SourceTapeArchiveSinkResult = {
|
|
83
|
+
capture_bundle_id: string;
|
|
84
|
+
state_count: number;
|
|
85
|
+
level_row_count: number;
|
|
86
|
+
summary_row_count: number;
|
|
87
|
+
forwarder_batch_count: number;
|
|
88
|
+
forwarder_batch_identity_sha256: string;
|
|
89
|
+
initializer: {
|
|
90
|
+
canonical_snapshot_id: string;
|
|
91
|
+
source_time_ms: number;
|
|
92
|
+
sequence: string;
|
|
93
|
+
semantic_stream_position: 0;
|
|
94
|
+
};
|
|
95
|
+
expected_semantic_digest: string;
|
|
96
|
+
first_state_descriptor_sha256: string;
|
|
97
|
+
last_state_descriptor_sha256: string;
|
|
98
|
+
provider_object_inventory_complete: true;
|
|
99
|
+
max_states_per_yield: typeof SOURCE_TAPE_MAX_STATES_PER_YIELD;
|
|
100
|
+
max_batch_rows: typeof SOURCE_TAPE_MAX_BATCH_ROWS;
|
|
101
|
+
max_batch_bytes: typeof SOURCE_TAPE_MAX_BATCH_BYTES;
|
|
102
|
+
max_in_flight_submissions: typeof SOURCE_TAPE_MAX_IN_FLIGHT;
|
|
103
|
+
};
|
|
104
|
+
export type SourceTapeSemanticStateDescriptor = {
|
|
105
|
+
state_kind: "initialization" | "change";
|
|
106
|
+
semantic_stream_position: number;
|
|
107
|
+
canonical_snapshot_id: string;
|
|
108
|
+
source_time_ms: number;
|
|
109
|
+
sequence: string;
|
|
110
|
+
canonical_rows_sha256: string;
|
|
111
|
+
};
|
|
112
|
+
export declare class SourceTapeSemanticDigestAccumulator {
|
|
113
|
+
private readonly hash;
|
|
114
|
+
private count;
|
|
115
|
+
constructor();
|
|
116
|
+
append(descriptor: SourceTapeSemanticStateDescriptor): void;
|
|
117
|
+
digest(): string;
|
|
118
|
+
}
|
|
119
|
+
export declare function sourceTapeSemanticStateDescriptor(input: {
|
|
120
|
+
stateKind: "initialization" | "change";
|
|
121
|
+
semanticStreamPosition: number;
|
|
122
|
+
rows: readonly BackfillArchiveRow[];
|
|
123
|
+
}): SourceTapeSemanticStateDescriptor;
|
|
124
|
+
export type SourceTapeArchiveSink = PolicyNeutralTapeSink & {
|
|
125
|
+
result(): SourceTapeArchiveSinkResult;
|
|
126
|
+
};
|
|
127
|
+
/**
|
|
128
|
+
* Streams canonical tape rows to the existing archive-forwarder admission
|
|
129
|
+
* tables. Promotion, selection and exporter-v2 finalization remain later
|
|
130
|
+
* gates; this sink never creates a receipt or a success manifest by itself.
|
|
131
|
+
*/
|
|
132
|
+
export declare function createSourceTapeArchiveSink(input: {
|
|
133
|
+
captureBundleId: string;
|
|
134
|
+
tradingPair: "ARB-USDC" | "ARB-USDT";
|
|
135
|
+
window: {
|
|
136
|
+
startTimeMs: number;
|
|
137
|
+
endTimeMs: number;
|
|
138
|
+
};
|
|
139
|
+
forwarder: {
|
|
140
|
+
submit(batch: ForwarderBatch): Promise<{
|
|
141
|
+
ok: boolean;
|
|
142
|
+
inserted: number;
|
|
143
|
+
}>;
|
|
144
|
+
};
|
|
145
|
+
}): SourceTapeArchiveSink;
|
|
146
|
+
export declare function providerObjectInventoryIsComplete(inventory: SourceTapeProviderObjectInventory): boolean;
|
|
147
|
+
export declare function evaluateSourceTapeEligibility(input: {
|
|
148
|
+
source_enumeration_eligible: boolean;
|
|
149
|
+
provider_object_inventory: SourceTapeProviderObjectInventory;
|
|
150
|
+
tape_complete: boolean;
|
|
151
|
+
capability_id: string;
|
|
152
|
+
capability_sha256: string;
|
|
153
|
+
construction_mode: string;
|
|
154
|
+
projection_schema_pins: readonly {
|
|
155
|
+
schema_id: string;
|
|
156
|
+
schema_sha256: string;
|
|
157
|
+
}[];
|
|
158
|
+
artifact_sha256s: readonly string[];
|
|
159
|
+
tape_manifest_sha256: string;
|
|
160
|
+
}): boolean;
|
|
161
|
+
export declare function normalizeSourceTapeStates(input: {
|
|
162
|
+
tape: readonly PolicyNeutralCryptoHftBookState[];
|
|
163
|
+
capture_bundle_id: string;
|
|
164
|
+
trading_pair: "ARB-USDC" | "ARB-USDT";
|
|
165
|
+
}): BackfillArchiveRow[];
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Metadata } from "@grpc/grpc-js";
|
|
2
|
+
/** gRPC metadata key for lightweight operational correlation with callers. */
|
|
3
|
+
export declare const TRACE_METADATA_KEY = "x-trace-id";
|
|
4
|
+
/**
|
|
5
|
+
* Returns a validated caller correlation ID without mutating inbound metadata.
|
|
6
|
+
* Invalid values are deliberately discarded before they can reach telemetry.
|
|
7
|
+
*/
|
|
8
|
+
export declare function extractTraceId(metadata: Metadata): string | undefined;
|
package/dist/index.js
CHANGED
|
@@ -294125,7 +294125,7 @@ function commonEvidenceFields(context2, rawCapture, input) {
|
|
|
294125
294125
|
source_time_ms: input.eventTimeMs,
|
|
294126
294126
|
received_time_ms: input.receivedTimeMs,
|
|
294127
294127
|
snapshot_id: input.snapshotId,
|
|
294128
|
-
construction_mode:
|
|
294128
|
+
construction_mode: input.constructionMode,
|
|
294129
294129
|
gap_policy: "record_gap",
|
|
294130
294130
|
depth_limit: input.depthLimit,
|
|
294131
294131
|
sequence: input.sequence,
|
|
@@ -294179,7 +294179,8 @@ function buildCanonicalOrderBookRows(input) {
|
|
|
294179
294179
|
sequence,
|
|
294180
294180
|
depthLimit: input.depthLimit,
|
|
294181
294181
|
eventTimeMs,
|
|
294182
|
-
receivedTimeMs
|
|
294182
|
+
receivedTimeMs,
|
|
294183
|
+
constructionMode: input.constructionMode ?? "sampled_top_n_snapshot"
|
|
294183
294184
|
});
|
|
294184
294185
|
const levels = [
|
|
294185
294186
|
["bid", bids],
|
|
@@ -310668,6 +310669,24 @@ function selectBrokerAccountForCex(normalizedCex, brokers, metadata) {
|
|
|
310668
310669
|
return selectBrokerAccount(brokers[normalizedCex], metadata) ?? undefined;
|
|
310669
310670
|
}
|
|
310670
310671
|
|
|
310672
|
+
// src/helpers/trace-context.ts
|
|
310673
|
+
var TRACE_METADATA_KEY = "x-trace-id";
|
|
310674
|
+
var MAX_TRACE_METADATA_LENGTH = 128;
|
|
310675
|
+
var OTEL_TRACE_ID_PATTERN = /^[0-9a-f]{32}$/;
|
|
310676
|
+
var UUID_V4_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
310677
|
+
var ZERO_OTEL_TRACE_ID = "00000000000000000000000000000000";
|
|
310678
|
+
function extractTraceId(metadata) {
|
|
310679
|
+
const raw = metadata.get(TRACE_METADATA_KEY)[0];
|
|
310680
|
+
if (typeof raw !== "string" || raw.length > MAX_TRACE_METADATA_LENGTH) {
|
|
310681
|
+
return;
|
|
310682
|
+
}
|
|
310683
|
+
const traceId = raw.trim();
|
|
310684
|
+
if (OTEL_TRACE_ID_PATTERN.test(traceId) && traceId !== ZERO_OTEL_TRACE_ID || UUID_V4_PATTERN.test(traceId)) {
|
|
310685
|
+
return traceId;
|
|
310686
|
+
}
|
|
310687
|
+
return;
|
|
310688
|
+
}
|
|
310689
|
+
|
|
310671
310690
|
// src/handlers/execute-action/order-book-call.ts
|
|
310672
310691
|
import * as grpc4 from "@grpc/grpc-js";
|
|
310673
310692
|
async function handleOrderBookCall(ctx) {
|
|
@@ -311971,6 +311990,15 @@ function isPublicMarketDataAction(action, payload) {
|
|
|
311971
311990
|
return false;
|
|
311972
311991
|
return isOrderBookCallMethod(payload?.method ?? payload?.functionName);
|
|
311973
311992
|
}
|
|
311993
|
+
function grpcStatusName(error48) {
|
|
311994
|
+
if (!error48) {
|
|
311995
|
+
return "OK";
|
|
311996
|
+
}
|
|
311997
|
+
if (typeof error48.code !== "number") {
|
|
311998
|
+
return "UNKNOWN";
|
|
311999
|
+
}
|
|
312000
|
+
return grpc12.status[error48.code] ?? "UNKNOWN";
|
|
312001
|
+
}
|
|
311974
312002
|
function createExecuteActionHandler(deps) {
|
|
311975
312003
|
const {
|
|
311976
312004
|
policy,
|
|
@@ -311987,12 +312015,28 @@ function createExecuteActionHandler(deps) {
|
|
|
311987
312015
|
const startTime = Date.now();
|
|
311988
312016
|
const { action: rawAction, cex: cex3, symbol: symbol2 } = call.request;
|
|
311989
312017
|
const action = resolveAction(rawAction);
|
|
312018
|
+
const actionName = getActionName(action);
|
|
312019
|
+
const operationalCex = cex3?.trim().toLowerCase() || "unknown";
|
|
312020
|
+
const traceId = extractTraceId(call.metadata);
|
|
312021
|
+
const traceFields = traceId === undefined ? {} : { trace_id: traceId };
|
|
311990
312022
|
let actionCompleted = false;
|
|
311991
312023
|
const wrappedCallback = (error48, value) => {
|
|
311992
312024
|
if (!actionCompleted) {
|
|
311993
312025
|
actionCompleted = true;
|
|
311994
312026
|
const latency = Date.now() - startTime;
|
|
311995
|
-
const
|
|
312027
|
+
const terminalFields = {
|
|
312028
|
+
action: actionName,
|
|
312029
|
+
cex: operationalCex,
|
|
312030
|
+
latency_ms: latency,
|
|
312031
|
+
outcome: error48 ? "error" : "success",
|
|
312032
|
+
grpc_status: grpcStatusName(error48),
|
|
312033
|
+
...traceFields
|
|
312034
|
+
};
|
|
312035
|
+
if (error48) {
|
|
312036
|
+
log.withMetadata(terminalFields).error("ExecuteAction failed");
|
|
312037
|
+
} else {
|
|
312038
|
+
log.withMetadata(terminalFields).info("ExecuteAction completed");
|
|
312039
|
+
}
|
|
311996
312040
|
otelMetrics?.recordHistogram("execute_action_duration_ms", latency, {
|
|
311997
312041
|
action: actionName,
|
|
311998
312042
|
cex: cex3 || "unknown"
|
|
@@ -312013,9 +312057,13 @@ function createExecuteActionHandler(deps) {
|
|
|
312013
312057
|
callback(error48, value);
|
|
312014
312058
|
};
|
|
312015
312059
|
try {
|
|
312016
|
-
log.
|
|
312060
|
+
log.withMetadata({
|
|
312061
|
+
action: actionName,
|
|
312062
|
+
cex: operationalCex,
|
|
312063
|
+
...traceFields
|
|
312064
|
+
}).info("ExecuteAction started");
|
|
312017
312065
|
otelMetrics?.recordCounter("execute_action_requests_total", 1, {
|
|
312018
|
-
action:
|
|
312066
|
+
action: actionName,
|
|
312019
312067
|
cex: cex3 || "unknown"
|
|
312020
312068
|
});
|
|
312021
312069
|
if (!authenticateRequest(call, whitelistIps)) {
|
|
@@ -312253,6 +312301,9 @@ async function writeSubscribeError(call, isClosed, frame) {
|
|
|
312253
312301
|
call.end();
|
|
312254
312302
|
}
|
|
312255
312303
|
}
|
|
312304
|
+
function grpcStatusName2(status14) {
|
|
312305
|
+
return typeof status14 === "number" ? grpc13.status[status14] ?? "UNKNOWN" : "UNKNOWN";
|
|
312306
|
+
}
|
|
312256
312307
|
function getBinanceEventMarketId(event) {
|
|
312257
312308
|
const value = event.s;
|
|
312258
312309
|
return typeof value === "string" ? value : null;
|
|
@@ -312385,6 +312436,24 @@ function createSubscribeHandler(deps) {
|
|
|
312385
312436
|
});
|
|
312386
312437
|
return async (call) => {
|
|
312387
312438
|
const subscribeStartTime = Date.now();
|
|
312439
|
+
const request = call.request;
|
|
312440
|
+
const { cex: cex3, symbol: symbol2, type: type2 } = request;
|
|
312441
|
+
const metadata = call.metadata;
|
|
312442
|
+
const traceId = extractTraceId(metadata);
|
|
312443
|
+
const traceFields = traceId === undefined ? {} : { trace_id: traceId };
|
|
312444
|
+
const operationalCex = cex3?.trim().toLowerCase() || "unknown";
|
|
312445
|
+
const operationalSymbol = symbol2?.trim() || "unknown";
|
|
312446
|
+
const subscriptionType2 = resolveSubscriptionType(type2);
|
|
312447
|
+
const subscriptionTypeName = getSubscriptionTypeName(subscriptionType2);
|
|
312448
|
+
const operationalFields = {
|
|
312449
|
+
cex: operationalCex,
|
|
312450
|
+
symbol: operationalSymbol,
|
|
312451
|
+
subscription_type: subscriptionTypeName,
|
|
312452
|
+
...traceFields
|
|
312453
|
+
};
|
|
312454
|
+
let terminalLogged = false;
|
|
312455
|
+
let terminalOutcome = "completed";
|
|
312456
|
+
let terminalGrpcStatus = "OK";
|
|
312388
312457
|
let streamClosed = false;
|
|
312389
312458
|
let ownedBroker = null;
|
|
312390
312459
|
let ownedBrokerClosePromise;
|
|
@@ -312407,12 +312476,41 @@ function createSubscribeHandler(deps) {
|
|
|
312407
312476
|
const closeOwnedBrokerOnCallEnd = () => {
|
|
312408
312477
|
closeOwnedBroker();
|
|
312409
312478
|
};
|
|
312410
|
-
|
|
312479
|
+
const logTerminalOutcome = (outcome) => {
|
|
312480
|
+
if (terminalLogged) {
|
|
312481
|
+
return;
|
|
312482
|
+
}
|
|
312483
|
+
terminalLogged = true;
|
|
312484
|
+
const fields = {
|
|
312485
|
+
...operationalFields,
|
|
312486
|
+
duration_ms: Date.now() - subscribeStartTime,
|
|
312487
|
+
outcome,
|
|
312488
|
+
grpc_status: outcome === "cancelled" ? "CANCELLED" : terminalGrpcStatus
|
|
312489
|
+
};
|
|
312490
|
+
if (outcome === "error") {
|
|
312491
|
+
log.withMetadata(fields).error("Subscribe failed");
|
|
312492
|
+
} else if (outcome === "cancelled") {
|
|
312493
|
+
log.withMetadata(fields).info("Subscribe cancelled");
|
|
312494
|
+
} else {
|
|
312495
|
+
log.withMetadata(fields).info("Subscribe ended");
|
|
312496
|
+
}
|
|
312497
|
+
};
|
|
312498
|
+
const writeTerminalError = async (frame, status14 = grpc13.status.UNKNOWN) => {
|
|
312499
|
+
terminalOutcome = "error";
|
|
312500
|
+
terminalGrpcStatus = grpcStatusName2(status14);
|
|
312501
|
+
await writeSubscribeError(call, isStreamClosed, frame);
|
|
312502
|
+
};
|
|
312503
|
+
log.withMetadata(operationalFields).info("Subscribe started");
|
|
312504
|
+
call.once("cancelled", () => {
|
|
312505
|
+
terminalGrpcStatus = "CANCELLED";
|
|
312506
|
+
markStreamClosed();
|
|
312507
|
+
logTerminalOutcome("cancelled");
|
|
312508
|
+
});
|
|
312411
312509
|
call.once("cancelled", closeOwnedBrokerOnCallEnd);
|
|
312412
312510
|
call.once("error", closeOwnedBrokerOnCallEnd);
|
|
312413
312511
|
call.once("end", () => {
|
|
312414
312512
|
markStreamClosed();
|
|
312415
|
-
|
|
312513
|
+
logTerminalOutcome(terminalOutcome);
|
|
312416
312514
|
const duration3 = Date.now() - subscribeStartTime;
|
|
312417
312515
|
otelMetrics?.recordHistogram("subscribe_duration_ms", duration3, {
|
|
312418
312516
|
cex: call.request?.cex || "unknown",
|
|
@@ -312421,12 +312519,16 @@ function createSubscribeHandler(deps) {
|
|
|
312421
312519
|
});
|
|
312422
312520
|
call.once("error", (error48) => {
|
|
312423
312521
|
markStreamClosed();
|
|
312424
|
-
|
|
312522
|
+
terminalOutcome = "error";
|
|
312523
|
+
terminalGrpcStatus = grpcStatusName2(typeof error48 === "object" && error48 !== null && "code" in error48 ? error48.code : undefined);
|
|
312524
|
+
logTerminalOutcome(call.cancelled ? "cancelled" : "error");
|
|
312425
312525
|
otelMetrics?.recordCounter("subscribe_errors_total", 1, {
|
|
312426
312526
|
error_type: error48 instanceof Error ? error48.message : "unknown"
|
|
312427
312527
|
});
|
|
312428
312528
|
});
|
|
312429
312529
|
if (!authenticateRequest(call, whitelistIps)) {
|
|
312530
|
+
terminalOutcome = "error";
|
|
312531
|
+
terminalGrpcStatus = "PERMISSION_DENIED";
|
|
312430
312532
|
otelMetrics?.recordCounter("subscribe_errors_total", 1, {
|
|
312431
312533
|
error_type: "permission_denied"
|
|
312432
312534
|
});
|
|
@@ -312437,32 +312539,22 @@ function createSubscribeHandler(deps) {
|
|
|
312437
312539
|
call.destroy(new Error("Access denied: Unauthorized IP"));
|
|
312438
312540
|
return;
|
|
312439
312541
|
}
|
|
312440
|
-
const metadata = call.metadata;
|
|
312441
|
-
let subscriptionType2 = SubscriptionType.ORDERBOOK;
|
|
312442
312542
|
try {
|
|
312443
|
-
const
|
|
312444
|
-
const { cex: cex3, symbol: symbol2, type: type2, options } = request;
|
|
312445
|
-
subscriptionType2 = resolveSubscriptionType(type2);
|
|
312446
|
-
log.info(`Request - Subscribe:`, {
|
|
312447
|
-
cex: request.cex,
|
|
312448
|
-
symbol: request.symbol,
|
|
312449
|
-
type: subscriptionType2
|
|
312450
|
-
});
|
|
312451
|
-
const subscriptionTypeName = getSubscriptionTypeName(subscriptionType2);
|
|
312543
|
+
const { options } = request;
|
|
312452
312544
|
otelMetrics?.recordCounter("subscribe_requests_total", 1, {
|
|
312453
312545
|
cex: cex3 || "unknown",
|
|
312454
312546
|
symbol: symbol2 || "unknown",
|
|
312455
312547
|
type: subscriptionTypeName
|
|
312456
312548
|
});
|
|
312457
312549
|
if (!cex3 || !symbol2) {
|
|
312458
|
-
await
|
|
312550
|
+
await writeTerminalError({
|
|
312459
312551
|
data: JSON.stringify({
|
|
312460
312552
|
error: "cex, symbol, and type are required"
|
|
312461
312553
|
}),
|
|
312462
312554
|
timestamp: Date.now(),
|
|
312463
312555
|
symbol: symbol2 || "",
|
|
312464
312556
|
type: subscriptionType2
|
|
312465
|
-
});
|
|
312557
|
+
}, grpc13.status.INVALID_ARGUMENT);
|
|
312466
312558
|
return;
|
|
312467
312559
|
}
|
|
312468
312560
|
if (isPublicMarketDataSubscription(subscriptionType2)) {
|
|
@@ -312491,7 +312583,7 @@ function createSubscribeHandler(deps) {
|
|
|
312491
312583
|
} catch (error48) {
|
|
312492
312584
|
const message = getErrorMessage(error48);
|
|
312493
312585
|
if (!isStreamClosed()) {
|
|
312494
|
-
await
|
|
312586
|
+
await writeTerminalError({
|
|
312495
312587
|
data: JSON.stringify({ error: message }),
|
|
312496
312588
|
timestamp: Date.now(),
|
|
312497
312589
|
symbol: symbol2,
|
|
@@ -312512,14 +312604,14 @@ function createSubscribeHandler(deps) {
|
|
|
312512
312604
|
const selectedBroker = selectedBrokerAccount?.exchange ?? createBroker(normalizedCex, metadata);
|
|
312513
312605
|
const broker = selectedBroker ?? createPublicBroker(normalizedCex);
|
|
312514
312606
|
if (!broker) {
|
|
312515
|
-
await
|
|
312607
|
+
await writeTerminalError({
|
|
312516
312608
|
data: JSON.stringify({
|
|
312517
312609
|
error: "Exchange not registered and no API metadata found"
|
|
312518
312610
|
}),
|
|
312519
312611
|
timestamp: Date.now(),
|
|
312520
312612
|
symbol: symbol2,
|
|
312521
312613
|
type: subscriptionType2
|
|
312522
|
-
});
|
|
312614
|
+
}, grpc13.status.NOT_FOUND);
|
|
312523
312615
|
return;
|
|
312524
312616
|
}
|
|
312525
312617
|
if (!selectedBrokerAccount) {
|
|
@@ -312547,28 +312639,28 @@ function createSubscribeHandler(deps) {
|
|
|
312547
312639
|
if (isBinanceSpotAccountSubscription(normalizedCex, subscriptionType2, options?.marketType)) {
|
|
312548
312640
|
const accountBroker = selectedBrokerAccount?.exchange ?? selectedBroker;
|
|
312549
312641
|
if (!accountBroker) {
|
|
312550
|
-
await
|
|
312642
|
+
await writeTerminalError({
|
|
312551
312643
|
data: JSON.stringify({
|
|
312552
312644
|
error: "Binance account subscriptions require API credentials"
|
|
312553
312645
|
}),
|
|
312554
312646
|
timestamp: Date.now(),
|
|
312555
312647
|
symbol: resolvedSymbol,
|
|
312556
312648
|
type: subscriptionType2
|
|
312557
|
-
});
|
|
312649
|
+
}, grpc13.status.FAILED_PRECONDITION);
|
|
312558
312650
|
return;
|
|
312559
312651
|
}
|
|
312560
312652
|
const marketId = subscriptionType2 === SubscriptionType.ORDERS ? await getBinanceMarketId(accountBroker, resolvedSymbol) : undefined;
|
|
312561
312653
|
let userDataSource;
|
|
312562
312654
|
if (selectedBrokerAccount) {
|
|
312563
312655
|
if (!userDataStreamSupervisor) {
|
|
312564
|
-
await
|
|
312656
|
+
await writeTerminalError({
|
|
312565
312657
|
data: JSON.stringify({
|
|
312566
312658
|
error: "Configured account user-data supervisor is unavailable"
|
|
312567
312659
|
}),
|
|
312568
312660
|
timestamp: Date.now(),
|
|
312569
312661
|
symbol: resolvedSymbol,
|
|
312570
312662
|
type: subscriptionType2
|
|
312571
|
-
});
|
|
312663
|
+
}, grpc13.status.FAILED_PRECONDITION);
|
|
312572
312664
|
return;
|
|
312573
312665
|
}
|
|
312574
312666
|
userDataSource = userDataStreamSupervisor.subscribe({
|
|
@@ -312591,7 +312683,7 @@ function createSubscribeHandler(deps) {
|
|
|
312591
312683
|
} catch (error48) {
|
|
312592
312684
|
const message = getErrorMessage(error48);
|
|
312593
312685
|
log.error(`Error fetching balance for ${cex3}:`, error48);
|
|
312594
|
-
await
|
|
312686
|
+
await writeTerminalError({
|
|
312595
312687
|
data: JSON.stringify({
|
|
312596
312688
|
error: `Failed to fetch balance: ${message}`
|
|
312597
312689
|
}),
|
|
@@ -312610,7 +312702,7 @@ function createSubscribeHandler(deps) {
|
|
|
312610
312702
|
} catch (error48) {
|
|
312611
312703
|
log.error(`Error fetching orders for ${resolvedSymbol} on ${cex3}:`, error48);
|
|
312612
312704
|
const message = getErrorMessage(error48);
|
|
312613
|
-
await
|
|
312705
|
+
await writeTerminalError({
|
|
312614
312706
|
data: JSON.stringify({
|
|
312615
312707
|
error: `Failed to fetch orders: ${message}`
|
|
312616
312708
|
}),
|
|
@@ -312621,26 +312713,27 @@ function createSubscribeHandler(deps) {
|
|
|
312621
312713
|
}
|
|
312622
312714
|
break;
|
|
312623
312715
|
default:
|
|
312624
|
-
await
|
|
312716
|
+
await writeTerminalError({
|
|
312625
312717
|
data: JSON.stringify({ error: "Invalid subscription type" }),
|
|
312626
312718
|
timestamp: Date.now(),
|
|
312627
312719
|
symbol: symbol2,
|
|
312628
312720
|
type: subscriptionType2
|
|
312629
|
-
});
|
|
312721
|
+
}, grpc13.status.INVALID_ARGUMENT);
|
|
312630
312722
|
}
|
|
312631
312723
|
} catch (error48) {
|
|
312632
312724
|
log.error("Error in Subscribe stream:", error48);
|
|
312633
312725
|
const message = getErrorMessage(error48);
|
|
312634
|
-
await
|
|
312726
|
+
await writeTerminalError({
|
|
312635
312727
|
data: JSON.stringify({ error: `Internal server error: ${message}` }),
|
|
312636
312728
|
timestamp: Date.now(),
|
|
312637
312729
|
symbol: "",
|
|
312638
312730
|
type: subscriptionType2
|
|
312639
|
-
});
|
|
312731
|
+
}, grpc13.status.INTERNAL);
|
|
312640
312732
|
} finally {
|
|
312641
312733
|
call.off("cancelled", closeOwnedBrokerOnCallEnd);
|
|
312642
312734
|
call.off("error", closeOwnedBrokerOnCallEnd);
|
|
312643
312735
|
await closeOwnedBroker();
|
|
312736
|
+
logTerminalOutcome(call.cancelled ? "cancelled" : terminalOutcome);
|
|
312644
312737
|
}
|
|
312645
312738
|
};
|
|
312646
312739
|
}
|
|
@@ -313252,4 +313345,4 @@ export {
|
|
|
313252
313345
|
CEXBroker as default
|
|
313253
313346
|
};
|
|
313254
313347
|
|
|
313255
|
-
//# debugId=
|
|
313348
|
+
//# debugId=298884FD1F08284464756E2164756E21
|