@clonegod/ttd-core 3.1.56 → 3.1.58
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/alert/bootstrap.d.ts +1 -0
- package/dist/alert/bootstrap.js +29 -0
- package/dist/alert/bus.d.ts +9 -0
- package/dist/alert/bus.js +13 -0
- package/dist/alert/index.d.ts +5 -3
- package/dist/alert/index.js +17 -3
- package/dist/alert/registry.d.ts +46 -0
- package/dist/alert/registry.js +164 -0
- package/dist/alert/runtime_state.d.ts +10 -0
- package/dist/alert/runtime_state.js +126 -0
- package/dist/alert/transport_analyze.d.ts +1 -0
- package/dist/alert/transport_analyze.js +14 -0
- package/dist/alert/types.d.ts +64 -35
- package/dist/alert/types.js +0 -9
- package/dist/analyze/index.js +1 -0
- package/dist/appconfig/EnvArgs.d.ts +0 -1
- package/dist/appconfig/env_registry.js +0 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/quote/depth_tier.d.ts +41 -0
- package/dist/quote/depth_tier.js +36 -0
- package/dist/quote/index.d.ts +1 -0
- package/dist/quote/index.js +1 -0
- package/dist/quote/on_quote_response.d.ts +14 -2
- package/dist/quote/on_quote_response.js +9 -19
- package/dist/quote/publish_quote_price.js +16 -1
- package/dist/quote/to_price_message.d.ts +2 -2
- package/dist/quote/to_price_message.js +27 -41
- package/dist/trade/abstract_tx_check.js +1 -1
- package/package.json +1 -1
- package/types/index.d.ts +80 -9
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function bootstrapAlertSystem(moduleName: string): Promise<void>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.bootstrapAlertSystem = bootstrapAlertSystem;
|
|
13
|
+
const runtime_state_1 = require("./runtime_state");
|
|
14
|
+
const transport_analyze_1 = require("./transport_analyze");
|
|
15
|
+
const index_1 = require("../index");
|
|
16
|
+
const index_2 = require("../index");
|
|
17
|
+
function bootstrapAlertSystem(moduleName) {
|
|
18
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
19
|
+
try {
|
|
20
|
+
(0, runtime_state_1.setAlertModule)(moduleName);
|
|
21
|
+
(0, runtime_state_1.initAlertEnvDisable)();
|
|
22
|
+
(0, transport_analyze_1.registerAnalyzeTransport)();
|
|
23
|
+
yield (0, runtime_state_1.initAlertRedisSync)((0, index_1.getRedisClient)('alert'));
|
|
24
|
+
}
|
|
25
|
+
catch (e) {
|
|
26
|
+
(0, index_2.log_warn)(`[alert] bootstrap failed: ${e.message}`);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
import { AlertEvent } from './types';
|
|
3
|
+
declare class AlertBus extends EventEmitter {
|
|
4
|
+
constructor();
|
|
5
|
+
emitAlert(event: AlertEvent): void;
|
|
6
|
+
onAlert(listener: (event: AlertEvent) => void): void;
|
|
7
|
+
}
|
|
8
|
+
export declare const alertBus: AlertBus;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.alertBus = void 0;
|
|
4
|
+
const events_1 = require("events");
|
|
5
|
+
class AlertBus extends events_1.EventEmitter {
|
|
6
|
+
constructor() {
|
|
7
|
+
super();
|
|
8
|
+
this.setMaxListeners(20);
|
|
9
|
+
}
|
|
10
|
+
emitAlert(event) { this.emit('alert', event); }
|
|
11
|
+
onAlert(listener) { this.on('alert', listener); }
|
|
12
|
+
}
|
|
13
|
+
exports.alertBus = new AlertBus();
|
package/dist/alert/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export * from './types';
|
|
2
|
-
export * from './
|
|
3
|
-
export * from './
|
|
4
|
-
export
|
|
2
|
+
export * from './bus';
|
|
3
|
+
export * from './registry';
|
|
4
|
+
export { setAlertModule, getAlertModule, initAlertEnvDisable, initAlertRedisSync, stopAlertRedisSync, setAlertEnabled, clearAlertEnabledOverride, isTypeEnabled, onAlertEnabledChange, } from './runtime_state';
|
|
5
|
+
export { registerAnalyzeTransport } from './transport_analyze';
|
|
6
|
+
export { bootstrapAlertSystem } from './bootstrap';
|
package/dist/alert/index.js
CHANGED
|
@@ -14,7 +14,21 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.bootstrapAlertSystem = exports.registerAnalyzeTransport = exports.onAlertEnabledChange = exports.isTypeEnabled = exports.clearAlertEnabledOverride = exports.setAlertEnabled = exports.stopAlertRedisSync = exports.initAlertRedisSync = exports.initAlertEnvDisable = exports.getAlertModule = exports.setAlertModule = void 0;
|
|
17
18
|
__exportStar(require("./types"), exports);
|
|
18
|
-
__exportStar(require("./
|
|
19
|
-
__exportStar(require("./
|
|
20
|
-
|
|
19
|
+
__exportStar(require("./bus"), exports);
|
|
20
|
+
__exportStar(require("./registry"), exports);
|
|
21
|
+
var runtime_state_1 = require("./runtime_state");
|
|
22
|
+
Object.defineProperty(exports, "setAlertModule", { enumerable: true, get: function () { return runtime_state_1.setAlertModule; } });
|
|
23
|
+
Object.defineProperty(exports, "getAlertModule", { enumerable: true, get: function () { return runtime_state_1.getAlertModule; } });
|
|
24
|
+
Object.defineProperty(exports, "initAlertEnvDisable", { enumerable: true, get: function () { return runtime_state_1.initAlertEnvDisable; } });
|
|
25
|
+
Object.defineProperty(exports, "initAlertRedisSync", { enumerable: true, get: function () { return runtime_state_1.initAlertRedisSync; } });
|
|
26
|
+
Object.defineProperty(exports, "stopAlertRedisSync", { enumerable: true, get: function () { return runtime_state_1.stopAlertRedisSync; } });
|
|
27
|
+
Object.defineProperty(exports, "setAlertEnabled", { enumerable: true, get: function () { return runtime_state_1.setAlertEnabled; } });
|
|
28
|
+
Object.defineProperty(exports, "clearAlertEnabledOverride", { enumerable: true, get: function () { return runtime_state_1.clearAlertEnabledOverride; } });
|
|
29
|
+
Object.defineProperty(exports, "isTypeEnabled", { enumerable: true, get: function () { return runtime_state_1.isTypeEnabled; } });
|
|
30
|
+
Object.defineProperty(exports, "onAlertEnabledChange", { enumerable: true, get: function () { return runtime_state_1.onAlertEnabledChange; } });
|
|
31
|
+
var transport_analyze_1 = require("./transport_analyze");
|
|
32
|
+
Object.defineProperty(exports, "registerAnalyzeTransport", { enumerable: true, get: function () { return transport_analyze_1.registerAnalyzeTransport; } });
|
|
33
|
+
var bootstrap_1 = require("./bootstrap");
|
|
34
|
+
Object.defineProperty(exports, "bootstrapAlertSystem", { enumerable: true, get: function () { return bootstrap_1.bootstrapAlertSystem; } });
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { AlertEvent, AlertSeverity, AlertTypeMeta, ThresholdSpec, WrapContext } from './types';
|
|
2
|
+
type BuildPartial = () => Partial<Omit<AlertEvent, 'type' | 'severity' | 'module'>>;
|
|
3
|
+
export declare class AlertTypeDef {
|
|
4
|
+
readonly name: string;
|
|
5
|
+
readonly defaultSeverity: AlertSeverity;
|
|
6
|
+
readonly meta: AlertTypeMeta;
|
|
7
|
+
private lastSignature;
|
|
8
|
+
private firedOnce;
|
|
9
|
+
private thresholdState;
|
|
10
|
+
constructor(name: string, defaultSeverity: AlertSeverity, meta: AlertTypeMeta);
|
|
11
|
+
get enabled(): boolean;
|
|
12
|
+
private emit;
|
|
13
|
+
report(partial: Partial<Omit<AlertEvent, 'type' | 'module'>>): void;
|
|
14
|
+
reportIfChanged(fingerprint: string, changed: boolean, build: BuildPartial): void;
|
|
15
|
+
reportThreshold(value: number, opts: ThresholdSpec): void;
|
|
16
|
+
assert(cond: boolean, build: BuildPartial): asserts cond;
|
|
17
|
+
wrap<T>(ctx: WrapContext, fn: () => Promise<T>): Promise<T>;
|
|
18
|
+
reportOnce(fingerprint: string, build: BuildPartial): void;
|
|
19
|
+
}
|
|
20
|
+
export declare const ALERT_TYPES: {
|
|
21
|
+
readonly QUOTE_FEE_CHAIN_DRIFT: AlertTypeDef;
|
|
22
|
+
readonly QUOTE_TIER_AMOUNT_ZERO: AlertTypeDef;
|
|
23
|
+
readonly QUOTE_ASK_LT_BID: AlertTypeDef;
|
|
24
|
+
readonly QUOTE_POOL_INIT_FAILED: AlertTypeDef;
|
|
25
|
+
readonly QUOTE_V2_STALL: AlertTypeDef;
|
|
26
|
+
readonly QUOTE_VERIFY_HIGH_DRIFT: AlertTypeDef;
|
|
27
|
+
readonly QUOTE_HOOK_FEE_CHANGE: AlertTypeDef;
|
|
28
|
+
readonly TRADE_NONCE_DESYNC: AlertTypeDef;
|
|
29
|
+
readonly TRADE_CALLER_LOW_BALANCE: AlertTypeDef;
|
|
30
|
+
readonly TRADE_TX_PENDING_TOO_LONG: AlertTypeDef;
|
|
31
|
+
readonly TRADE_BUILDER_REJECTED: AlertTypeDef;
|
|
32
|
+
readonly TRADE_RESULT_MISSING: AlertTypeDef;
|
|
33
|
+
readonly TRADE_VAULT_REVERT: AlertTypeDef;
|
|
34
|
+
readonly INFRA_RPC_UNHEALTHY: AlertTypeDef;
|
|
35
|
+
readonly INFRA_REDIS_DISCONNECTED: AlertTypeDef;
|
|
36
|
+
readonly INFRA_WS_RECONNECT_STORM: AlertTypeDef;
|
|
37
|
+
readonly INFRA_BLOCK_STREAM_LAG: AlertTypeDef;
|
|
38
|
+
};
|
|
39
|
+
export declare const ALERT_TYPES_BY_NAME: Map<string, AlertTypeDef>;
|
|
40
|
+
export declare function listAllAlertTypes(): Array<{
|
|
41
|
+
name: string;
|
|
42
|
+
defaultSeverity: AlertSeverity;
|
|
43
|
+
meta: AlertTypeMeta;
|
|
44
|
+
enabled: boolean;
|
|
45
|
+
}>;
|
|
46
|
+
export {};
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.ALERT_TYPES_BY_NAME = exports.ALERT_TYPES = exports.AlertTypeDef = void 0;
|
|
13
|
+
exports.listAllAlertTypes = listAllAlertTypes;
|
|
14
|
+
const bus_1 = require("./bus");
|
|
15
|
+
const runtime_state_1 = require("./runtime_state");
|
|
16
|
+
class AlertTypeDef {
|
|
17
|
+
constructor(name, defaultSeverity, meta) {
|
|
18
|
+
this.name = name;
|
|
19
|
+
this.defaultSeverity = defaultSeverity;
|
|
20
|
+
this.meta = meta;
|
|
21
|
+
this.lastSignature = new Map();
|
|
22
|
+
this.firedOnce = new Set();
|
|
23
|
+
this.thresholdState = new Map();
|
|
24
|
+
}
|
|
25
|
+
get enabled() {
|
|
26
|
+
return (0, runtime_state_1.isTypeEnabled)(this.name, this.meta.enable);
|
|
27
|
+
}
|
|
28
|
+
emit(partial) {
|
|
29
|
+
var _a;
|
|
30
|
+
if (!this.enabled)
|
|
31
|
+
return;
|
|
32
|
+
if (!partial.identity || !partial.title) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const event = Object.assign({ type: this.name, severity: partial.severity || this.defaultSeverity, module: (0, runtime_state_1.getAlertModule)(), ts: partial.ts || Date.now(), ttl_ms: (_a = partial.ttl_ms) !== null && _a !== void 0 ? _a : this.meta.ttl_ms }, partial);
|
|
36
|
+
try {
|
|
37
|
+
bus_1.alertBus.emitAlert(event);
|
|
38
|
+
}
|
|
39
|
+
catch (_) { }
|
|
40
|
+
}
|
|
41
|
+
report(partial) {
|
|
42
|
+
this.emit(partial);
|
|
43
|
+
}
|
|
44
|
+
reportIfChanged(fingerprint, changed, build) {
|
|
45
|
+
if (!this.enabled)
|
|
46
|
+
return;
|
|
47
|
+
if (!changed) {
|
|
48
|
+
if (this.lastSignature.has(fingerprint) && this.meta.auto_resolve_on_recheck) {
|
|
49
|
+
const partial = Object.assign(Object.assign({}, build()), { resolved: true });
|
|
50
|
+
this.lastSignature.delete(fingerprint);
|
|
51
|
+
this.emit(partial);
|
|
52
|
+
}
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const partial = build();
|
|
56
|
+
const sig = JSON.stringify(partial.detail || {});
|
|
57
|
+
if (this.lastSignature.get(fingerprint) === sig)
|
|
58
|
+
return;
|
|
59
|
+
this.lastSignature.set(fingerprint, sig);
|
|
60
|
+
this.emit(Object.assign(Object.assign({}, partial), { fingerprint }));
|
|
61
|
+
}
|
|
62
|
+
reportThreshold(value, opts) {
|
|
63
|
+
var _a;
|
|
64
|
+
if (!this.enabled)
|
|
65
|
+
return;
|
|
66
|
+
const fp = `${this.name}:${opts.identity}`;
|
|
67
|
+
const prev = this.thresholdState.get(fp) || 'ok';
|
|
68
|
+
const worse = opts.higher_is_worse
|
|
69
|
+
? (a, b) => a > b
|
|
70
|
+
: (a, b) => a < b;
|
|
71
|
+
let nextSev = 'ok';
|
|
72
|
+
if (worse(value, opts.thresholds.critical))
|
|
73
|
+
nextSev = 'critical';
|
|
74
|
+
else if (worse(value, opts.thresholds.warn))
|
|
75
|
+
nextSev = 'warn';
|
|
76
|
+
if (nextSev === prev)
|
|
77
|
+
return;
|
|
78
|
+
this.thresholdState.set(fp, nextSev);
|
|
79
|
+
const title = ((_a = opts.title) === null || _a === void 0 ? void 0 : _a.call(opts, value)) || `${this.meta.display}: ${value}`;
|
|
80
|
+
const baseDetail = Object.assign({ value, thresholds: opts.thresholds }, (opts.detail || {}));
|
|
81
|
+
if (nextSev === 'ok') {
|
|
82
|
+
this.emit({
|
|
83
|
+
identity: opts.identity, scope: opts.scope, chain_id: opts.chain_id,
|
|
84
|
+
title, detail: baseDetail, resolved: true,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
this.emit({
|
|
89
|
+
identity: opts.identity, scope: opts.scope, chain_id: opts.chain_id,
|
|
90
|
+
severity: nextSev, title, detail: baseDetail,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
assert(cond, build) {
|
|
95
|
+
if (cond)
|
|
96
|
+
return;
|
|
97
|
+
const partial = build();
|
|
98
|
+
this.emit(partial);
|
|
99
|
+
throw new Error(`[${this.name}] invariant violated: ${partial.title || ''}`);
|
|
100
|
+
}
|
|
101
|
+
wrap(ctx, fn) {
|
|
102
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
103
|
+
var _a;
|
|
104
|
+
try {
|
|
105
|
+
return yield fn();
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
const e = err;
|
|
109
|
+
this.emit({
|
|
110
|
+
severity: 'critical',
|
|
111
|
+
identity: ctx.identity,
|
|
112
|
+
scope: ctx.scope,
|
|
113
|
+
chain_id: ctx.chain_id,
|
|
114
|
+
title: ((_a = ctx.title) === null || _a === void 0 ? void 0 : _a.call(ctx, e)) || `${this.meta.display}: ${e.message}`,
|
|
115
|
+
detail: Object.assign({ error: e.message, stack: e.stack }, (ctx.detail || {})),
|
|
116
|
+
});
|
|
117
|
+
throw err;
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
reportOnce(fingerprint, build) {
|
|
122
|
+
if (!this.enabled)
|
|
123
|
+
return;
|
|
124
|
+
if (this.firedOnce.has(fingerprint))
|
|
125
|
+
return;
|
|
126
|
+
this.firedOnce.add(fingerprint);
|
|
127
|
+
this.emit(Object.assign(Object.assign({}, build()), { fingerprint }));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
exports.AlertTypeDef = AlertTypeDef;
|
|
131
|
+
function def(name, severity, meta) {
|
|
132
|
+
return new AlertTypeDef(name, severity, meta);
|
|
133
|
+
}
|
|
134
|
+
exports.ALERT_TYPES = {
|
|
135
|
+
QUOTE_FEE_CHAIN_DRIFT: def('quote.fee_chain_drift', 'warn', { display: 'Fee 配置与链上不一致', ttl_ms: 0, enable: true, auto_resolve_on_recheck: true }),
|
|
136
|
+
QUOTE_TIER_AMOUNT_ZERO: def('quote.tier_amount_zero', 'warn', { display: 'Tier 金额计算为 0', ttl_ms: 60000, enable: true }),
|
|
137
|
+
QUOTE_ASK_LT_BID: def('quote.ask_lt_bid', 'critical', { display: 'ask<bid invariant 违反', ttl_ms: 30000, enable: true }),
|
|
138
|
+
QUOTE_POOL_INIT_FAILED: def('quote.pool_init_failed', 'critical', { display: '池子初始化失败', ttl_ms: 0, enable: true }),
|
|
139
|
+
QUOTE_V2_STALL: def('quote.v2_stall', 'warn', { display: 'V2 事件流停摆(降级 V1)', ttl_ms: 60000, enable: true, auto_resolve_on_recheck: true }),
|
|
140
|
+
QUOTE_VERIFY_HIGH_DRIFT: def('quote.verify_high_drift', 'warn', { display: '报价反校验偏差超阈值', ttl_ms: 300000, enable: true }),
|
|
141
|
+
QUOTE_HOOK_FEE_CHANGE: def('quote.hook_fee_change', 'info', { display: 'Hook 动态 fee 变化', ttl_ms: 0, enable: true, kind: 'event' }),
|
|
142
|
+
TRADE_NONCE_DESYNC: def('trade.nonce_desync', 'critical', { display: 'Nonce 不同步', ttl_ms: 30000, enable: true, auto_resolve_on_recheck: true }),
|
|
143
|
+
TRADE_CALLER_LOW_BALANCE: def('trade.caller_low_balance', 'warn', { display: 'Caller 余额不足', ttl_ms: 300000, enable: true }),
|
|
144
|
+
TRADE_TX_PENDING_TOO_LONG: def('trade.tx_pending_too_long', 'warn', { display: '交易长时间未上链', ttl_ms: 60000, enable: true }),
|
|
145
|
+
TRADE_BUILDER_REJECTED: def('trade.builder_rejected', 'warn', { display: 'Bundle builder 拒绝', ttl_ms: 60000, enable: true }),
|
|
146
|
+
TRADE_RESULT_MISSING: def('trade.result_missing', 'critical', { display: 'TradeResult 事件缺失', ttl_ms: 30000, enable: true }),
|
|
147
|
+
TRADE_VAULT_REVERT: def('trade.vault_revert', 'critical', { display: 'Vault 执行 revert', ttl_ms: 30000, enable: true }),
|
|
148
|
+
INFRA_RPC_UNHEALTHY: def('infra.rpc_unhealthy', 'warn', { display: 'RPC provider 不健康', ttl_ms: 120000, enable: true, auto_resolve_on_recheck: true }),
|
|
149
|
+
INFRA_REDIS_DISCONNECTED: def('infra.redis_disconnected', 'critical', { display: 'Redis 连接断开', ttl_ms: 30000, enable: true, auto_resolve_on_recheck: true }),
|
|
150
|
+
INFRA_WS_RECONNECT_STORM: def('infra.ws_reconnect_storm', 'warn', { display: 'WS 频繁重连', ttl_ms: 60000, enable: true }),
|
|
151
|
+
INFRA_BLOCK_STREAM_LAG: def('infra.block_stream_lag', 'warn', { display: '区块流落后', ttl_ms: 60000, enable: true, auto_resolve_on_recheck: true }),
|
|
152
|
+
};
|
|
153
|
+
exports.ALERT_TYPES_BY_NAME = new Map();
|
|
154
|
+
for (const def of Object.values(exports.ALERT_TYPES)) {
|
|
155
|
+
exports.ALERT_TYPES_BY_NAME.set(def.name, def);
|
|
156
|
+
}
|
|
157
|
+
function listAllAlertTypes() {
|
|
158
|
+
return Object.values(exports.ALERT_TYPES).map(t => ({
|
|
159
|
+
name: t.name,
|
|
160
|
+
defaultSeverity: t.defaultSeverity,
|
|
161
|
+
meta: t.meta,
|
|
162
|
+
enabled: t.enabled,
|
|
163
|
+
}));
|
|
164
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { RedisClient } from '../redis';
|
|
2
|
+
export declare function setAlertModule(name: string): void;
|
|
3
|
+
export declare function getAlertModule(): string;
|
|
4
|
+
export declare function initAlertEnvDisable(): void;
|
|
5
|
+
export declare function initAlertRedisSync(redis: RedisClient): Promise<void>;
|
|
6
|
+
export declare function stopAlertRedisSync(): void;
|
|
7
|
+
export declare function setAlertEnabled(redis: RedisClient, type: string, enabled: boolean): Promise<void>;
|
|
8
|
+
export declare function clearAlertEnabledOverride(redis: RedisClient, type: string): Promise<void>;
|
|
9
|
+
export declare function isTypeEnabled(typeName: string, defaultEnable: boolean): boolean;
|
|
10
|
+
export declare function onAlertEnabledChange(cb: (type: string, enabled: boolean) => void): () => void;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.setAlertModule = setAlertModule;
|
|
13
|
+
exports.getAlertModule = getAlertModule;
|
|
14
|
+
exports.initAlertEnvDisable = initAlertEnvDisable;
|
|
15
|
+
exports.initAlertRedisSync = initAlertRedisSync;
|
|
16
|
+
exports.stopAlertRedisSync = stopAlertRedisSync;
|
|
17
|
+
exports.setAlertEnabled = setAlertEnabled;
|
|
18
|
+
exports.clearAlertEnabledOverride = clearAlertEnabledOverride;
|
|
19
|
+
exports.isTypeEnabled = isTypeEnabled;
|
|
20
|
+
exports.onAlertEnabledChange = onAlertEnabledChange;
|
|
21
|
+
const index_1 = require("../index");
|
|
22
|
+
const REDIS_HASH = 'alert:type_enabled';
|
|
23
|
+
const POLL_INTERVAL_MS = 60000;
|
|
24
|
+
let moduleName = process.env.MODULE_NAME || 'unknown';
|
|
25
|
+
const disabledByEnv = new Set();
|
|
26
|
+
const overrides = new Map();
|
|
27
|
+
const changeListeners = new Set();
|
|
28
|
+
let pollTimer = null;
|
|
29
|
+
let pollRedis = null;
|
|
30
|
+
function setAlertModule(name) {
|
|
31
|
+
moduleName = name;
|
|
32
|
+
}
|
|
33
|
+
function getAlertModule() {
|
|
34
|
+
return moduleName;
|
|
35
|
+
}
|
|
36
|
+
function initAlertEnvDisable() {
|
|
37
|
+
const raw = process.env.ALERT_DISABLED_TYPES || '';
|
|
38
|
+
raw.split(',').map(s => s.trim()).filter(Boolean).forEach(t => disabledByEnv.add(t));
|
|
39
|
+
if (disabledByEnv.size > 0) {
|
|
40
|
+
(0, index_1.log_info)(`[alert] disabled via env: ${[...disabledByEnv].join(',')}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function pollOnce(redis) {
|
|
44
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
45
|
+
try {
|
|
46
|
+
const map = yield redis.hgetall(REDIS_HASH);
|
|
47
|
+
const nextKeys = new Set(map ? Object.keys(map) : []);
|
|
48
|
+
if (map) {
|
|
49
|
+
for (const [type, val] of Object.entries(map)) {
|
|
50
|
+
const next = (val === '1' || val === 'true');
|
|
51
|
+
const prev = overrides.get(type);
|
|
52
|
+
if (prev !== next) {
|
|
53
|
+
overrides.set(type, next);
|
|
54
|
+
changeListeners.forEach(cb => { try {
|
|
55
|
+
cb(type, next);
|
|
56
|
+
}
|
|
57
|
+
catch (_) { } });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
for (const type of [...overrides.keys()]) {
|
|
62
|
+
if (!nextKeys.has(type)) {
|
|
63
|
+
overrides.delete(type);
|
|
64
|
+
changeListeners.forEach(cb => { try {
|
|
65
|
+
cb(type, null);
|
|
66
|
+
}
|
|
67
|
+
catch (_) { } });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch (e) {
|
|
72
|
+
(0, index_1.log_warn)(`[alert] redis poll failed: ${e.message}`);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
function initAlertRedisSync(redis) {
|
|
77
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
78
|
+
var _a;
|
|
79
|
+
pollRedis = redis;
|
|
80
|
+
yield pollOnce(redis);
|
|
81
|
+
(0, index_1.log_info)(`[alert] redis sync started (interval ${POLL_INTERVAL_MS}ms, ${overrides.size} overrides loaded)`);
|
|
82
|
+
if (pollTimer)
|
|
83
|
+
clearInterval(pollTimer);
|
|
84
|
+
pollTimer = setInterval(() => { if (pollRedis)
|
|
85
|
+
pollOnce(pollRedis); }, POLL_INTERVAL_MS);
|
|
86
|
+
(_a = pollTimer.unref) === null || _a === void 0 ? void 0 : _a.call(pollTimer);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
function stopAlertRedisSync() {
|
|
90
|
+
if (pollTimer) {
|
|
91
|
+
clearInterval(pollTimer);
|
|
92
|
+
pollTimer = null;
|
|
93
|
+
}
|
|
94
|
+
pollRedis = null;
|
|
95
|
+
}
|
|
96
|
+
function setAlertEnabled(redis, type, enabled) {
|
|
97
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
98
|
+
overrides.set(type, enabled);
|
|
99
|
+
yield redis.hset(REDIS_HASH, type, enabled ? '1' : '0');
|
|
100
|
+
changeListeners.forEach(cb => { try {
|
|
101
|
+
cb(type, enabled);
|
|
102
|
+
}
|
|
103
|
+
catch (_) { } });
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
function clearAlertEnabledOverride(redis, type) {
|
|
107
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
108
|
+
overrides.delete(type);
|
|
109
|
+
yield redis.hdel(REDIS_HASH, type);
|
|
110
|
+
changeListeners.forEach(cb => { try {
|
|
111
|
+
cb(type, null);
|
|
112
|
+
}
|
|
113
|
+
catch (_) { } });
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
function isTypeEnabled(typeName, defaultEnable) {
|
|
117
|
+
if (overrides.has(typeName))
|
|
118
|
+
return overrides.get(typeName);
|
|
119
|
+
if (disabledByEnv.has(typeName))
|
|
120
|
+
return false;
|
|
121
|
+
return defaultEnable;
|
|
122
|
+
}
|
|
123
|
+
function onAlertEnabledChange(cb) {
|
|
124
|
+
changeListeners.add(cb);
|
|
125
|
+
return () => changeListeners.delete(cb);
|
|
126
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function registerAnalyzeTransport(): void;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerAnalyzeTransport = registerAnalyzeTransport;
|
|
4
|
+
const bus_1 = require("./bus");
|
|
5
|
+
const analyze_1 = require("../analyze");
|
|
6
|
+
let registered = false;
|
|
7
|
+
function registerAnalyzeTransport() {
|
|
8
|
+
if (registered)
|
|
9
|
+
return;
|
|
10
|
+
registered = true;
|
|
11
|
+
bus_1.alertBus.onAlert((event) => {
|
|
12
|
+
(0, analyze_1.report_data_to_analyze)('Alert', event);
|
|
13
|
+
});
|
|
14
|
+
}
|
package/dist/alert/types.d.ts
CHANGED
|
@@ -1,40 +1,69 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
ERROR = "error",
|
|
6
|
-
CRITICAL = "critical"
|
|
7
|
-
}
|
|
8
|
-
export interface AlertCodeDef {
|
|
9
|
-
code: string;
|
|
1
|
+
export type AlertSeverity = 'info' | 'warn' | 'critical';
|
|
2
|
+
export type AlertStatus = 'active' | 'resolved' | 'acked' | 'expired';
|
|
3
|
+
export interface AlertEvent {
|
|
4
|
+
type: string;
|
|
10
5
|
severity: AlertSeverity;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
6
|
+
module: string;
|
|
7
|
+
chain_id?: string;
|
|
8
|
+
identity: string;
|
|
9
|
+
scope?: {
|
|
10
|
+
dex_id?: string;
|
|
11
|
+
pair?: string;
|
|
12
|
+
group_id?: string;
|
|
13
|
+
pool_address?: string;
|
|
14
|
+
caller_address?: string;
|
|
15
|
+
rpc_id?: string;
|
|
16
|
+
[k: string]: string | undefined;
|
|
17
17
|
};
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
message: string;
|
|
24
|
-
context?: Record<string, any>;
|
|
25
|
-
err?: {
|
|
26
|
-
name?: string;
|
|
27
|
-
message?: string;
|
|
28
|
-
stack?: string;
|
|
29
|
-
} | Error;
|
|
18
|
+
title: string;
|
|
19
|
+
detail?: Record<string, any>;
|
|
20
|
+
fingerprint?: string;
|
|
21
|
+
resolved?: boolean;
|
|
22
|
+
ttl_ms?: number;
|
|
30
23
|
ts?: number;
|
|
31
24
|
}
|
|
32
|
-
export interface AlertRecord
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
25
|
+
export interface AlertRecord {
|
|
26
|
+
fingerprint: string;
|
|
27
|
+
type: string;
|
|
28
|
+
severity: AlertSeverity;
|
|
29
|
+
module: string;
|
|
30
|
+
chain_id?: string;
|
|
31
|
+
identity: string;
|
|
32
|
+
scope?: AlertEvent['scope'];
|
|
33
|
+
title: string;
|
|
34
|
+
latest_detail?: Record<string, any>;
|
|
35
|
+
status: AlertStatus;
|
|
36
|
+
first_seen_ms: number;
|
|
37
|
+
last_seen_ms: number;
|
|
38
|
+
occurrences: number;
|
|
39
|
+
resolved_ms?: number;
|
|
40
|
+
acked_ms?: number;
|
|
41
|
+
acked_by?: string;
|
|
42
|
+
}
|
|
43
|
+
export interface AlertTypeMeta {
|
|
44
|
+
display: string;
|
|
45
|
+
kind?: 'state' | 'event';
|
|
46
|
+
ttl_ms: number;
|
|
47
|
+
enable: boolean;
|
|
48
|
+
auto_resolve_on_recheck?: boolean;
|
|
49
|
+
doc_url?: string;
|
|
50
|
+
}
|
|
51
|
+
export interface ThresholdSpec {
|
|
52
|
+
identity: string;
|
|
53
|
+
thresholds: {
|
|
54
|
+
warn: number;
|
|
55
|
+
critical: number;
|
|
56
|
+
};
|
|
57
|
+
higher_is_worse?: boolean;
|
|
58
|
+
scope?: AlertEvent['scope'];
|
|
59
|
+
chain_id?: string;
|
|
60
|
+
title?: (value: number) => string;
|
|
61
|
+
detail?: Record<string, any>;
|
|
62
|
+
}
|
|
63
|
+
export interface WrapContext {
|
|
64
|
+
identity: string;
|
|
65
|
+
scope?: AlertEvent['scope'];
|
|
66
|
+
chain_id?: string;
|
|
67
|
+
detail?: Record<string, any>;
|
|
68
|
+
title?: (err: Error) => string;
|
|
40
69
|
}
|
package/dist/alert/types.js
CHANGED
|
@@ -1,11 +1,2 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AlertSeverity = void 0;
|
|
4
|
-
var AlertSeverity;
|
|
5
|
-
(function (AlertSeverity) {
|
|
6
|
-
AlertSeverity["DEBUG"] = "debug";
|
|
7
|
-
AlertSeverity["INFO"] = "info";
|
|
8
|
-
AlertSeverity["WARN"] = "warn";
|
|
9
|
-
AlertSeverity["ERROR"] = "error";
|
|
10
|
-
AlertSeverity["CRITICAL"] = "critical";
|
|
11
|
-
})(AlertSeverity || (exports.AlertSeverity = AlertSeverity = {}));
|
package/dist/analyze/index.js
CHANGED
|
@@ -105,6 +105,7 @@ const WS_TYPES = new Set([
|
|
|
105
105
|
const HTTP_PATH = {
|
|
106
106
|
OrderMessageType: '/ingest/order',
|
|
107
107
|
TradeResultType: '/ingest/trade-result',
|
|
108
|
+
Alert: '/ingest/alert',
|
|
108
109
|
};
|
|
109
110
|
const report_data_to_analyze = (type, data) => {
|
|
110
111
|
const host = getAnalyzeHost();
|
|
@@ -27,7 +27,6 @@ export declare class EnvArgs {
|
|
|
27
27
|
quote_pool_name: string;
|
|
28
28
|
quote_pool_fee_rate: number;
|
|
29
29
|
tick_cache_force_full_refresh_interval: number;
|
|
30
|
-
depth_cex_bps: number;
|
|
31
30
|
trade_log_report_cex_url: string;
|
|
32
31
|
trade_log_report_cex_data_type: string;
|
|
33
32
|
token_price_refresh_interval_seconds: number;
|
|
@@ -36,7 +36,6 @@ registerEnvVars({
|
|
|
36
36
|
quote_pool_name: { env: 'QUOTE_POOL_NAME', type: 'string', default: '', desc: '指定池子名称' },
|
|
37
37
|
quote_pool_fee_rate: { env: 'QUOTE_POOL_FEE_RATE', type: 'number', default: 0, desc: '指定池子费率' },
|
|
38
38
|
tick_cache_force_full_refresh_interval: { env: 'TICK_CACHE_FORCE_FULL_REFRESH_INTERVAL', type: 'number', default: 60000, desc: 'tick 流动性强制全量刷新间隔(ms)' },
|
|
39
|
-
depth_cex_bps: { env: 'DEPTH_CEX_BPS', type: 'number', default: 20, desc: 'CEX 深度 bps(用于报价转换)' },
|
|
40
39
|
trade_log_report_cex_url: { env: 'TRADE_LOG_REPORT_CEX_URL', type: 'string', default: '', desc: '【使用方: ttd-core/src/index.ts save_trade_execution_logs】DEX 交易执行日志上报 cex 侧接收端 URL;为空则跳过上报' },
|
|
41
40
|
trade_log_report_cex_data_type: { env: 'TRADE_LOG_REPORT_CEX_DATA_TYPE', type: 'string', default: '', desc: '【使用方: ttd-core/src/index.ts save_trade_execution_logs】上报 payload.type,cex 侧用于路由/区分;为空则跳过上报' },
|
|
42
41
|
wallet_dir: { env: 'WALLET_DIR', type: 'string', default: '', desc: '钱包目录' },
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import fs from "fs";
|
|
|
4
4
|
import { StandardPoolInfoType, StandardTokenInfoType, TokenPriceWithAmountType, TradeResponseType, UniqueOrderbookIdType, WalletInfoType } from '../types';
|
|
5
5
|
import * as redis from './redis';
|
|
6
6
|
import * as cache from './cache';
|
|
7
|
+
export * from './alert';
|
|
7
8
|
export * from './analyze';
|
|
8
9
|
export * from './appconfig';
|
|
9
10
|
export * from './redis';
|
|
@@ -17,7 +18,7 @@ export * from './chains';
|
|
|
17
18
|
export * from './log_bus';
|
|
18
19
|
export * from './ws';
|
|
19
20
|
export * from './util';
|
|
20
|
-
export type { StandardTokenConfigType, StandardTokenInfoType, StandardPoolConfigType, StandardDexPoolConfigType, StandardPoolInfoType, StandardPairType, TradeServiceConfigType, TradeGroupType, TradePairType, TradePairDexPoolsType, TradeSettingsType, RpcInfoType, TradeStrategyType, TradeCommandLineArgs, TradeRuntimeType, TradeProcessInstanceType, TokenPriceWithAmountType, QuoteResultType, PriceMessageType, QuoteSourceType, UniqueOrderbookIdType, PriceType, QuoteTimeInfoType, OrderbookPriceType, Ladder, OrderMessageType, OrderSubmitResultType, OrderSubmitResponseType, StandardSwapDetailType, TokenBalChangeType, TokenBalanceChangeType, TradeResponseType, TradeResultType, TradeResultBalanceChangeType, TradeExecutionInfoType, TradeTimeFlowType, TradeBroadcastType, TradeGasFeeType, ServerInfoType, SendTxLogType, WalletInfoType, WalletTokenBalanceInfoType, RedisConfigChangeEventMessageType, RedisOrderEventMessageType, RedisQuoteEventMessageType, PreTradeEventType, ProviderComparisonType, ProviderArrivalType, OnTradeType, OnTradePriceType, OnTradeDepthType, OnTradeExecutionType, OnTradeResultType, AnalyzeDepthLevelType, AnalyzeTokenAmountType, PostTradeType, AnalyzeQuoteSnapshotType, AnalyzePoolEventType, AnalyzeEventSummaryType, AnalyzeSameBlockType, } from '../types';
|
|
21
|
+
export type { StandardTokenConfigType, StandardTokenInfoType, StandardPoolConfigType, StandardDexPoolConfigType, StandardPoolInfoType, StandardPairType, TradeServiceConfigType, TradeGroupType, TradePairType, TradePairDexPoolsType, TradeSettingsType, RpcInfoType, TradeStrategyType, TradeCommandLineArgs, TradeRuntimeType, TradeProcessInstanceType, TokenPriceWithAmountType, QuoteResultType, PriceMessageType, QuoteSourceType, UniqueOrderbookIdType, PriceType, QuoteTimeInfoType, OrderbookPriceType, Ladder, OrderMessageType, OrderSubmitResultType, OrderSubmitResponseType, StandardSwapDetailType, TokenBalChangeType, TokenBalanceChangeType, TradeResponseType, TradeResultType, TradeResultBalanceChangeType, TradeExecutionInfoType, TradeTimeFlowType, TradeBroadcastType, TradeGasFeeType, ServerInfoType, SendTxLogType, WalletInfoType, WalletTokenBalanceInfoType, RedisConfigChangeEventMessageType, RedisOrderEventMessageType, RedisQuoteEventMessageType, PreTradeEventType, ProviderComparisonType, ProviderArrivalType, OnTradeType, OnTradePriceType, OnTradeDepthType, OnTradeExecutionType, OnTradeResultType, AnalyzeDepthLevelType, AnalyzeTierLevelType, AnalyzeTokenAmountType, PostTradeType, AnalyzeQuoteSnapshotType, AnalyzePoolEventType, AnalyzeEventSummaryType, AnalyzeSameBlockType, } from '../types';
|
|
21
22
|
export declare const FAILED = "FAILED";
|
|
22
23
|
export declare const SUCCESS = "SUCCESS";
|
|
23
24
|
export declare const NOT_FOUND = "NOT_FOUND";
|
package/dist/index.js
CHANGED
|
@@ -87,6 +87,7 @@ const path_1 = __importDefault(require("path"));
|
|
|
87
87
|
const redis = __importStar(require("./redis"));
|
|
88
88
|
const cache = __importStar(require("./cache"));
|
|
89
89
|
const crypto_1 = require("./util/crypto");
|
|
90
|
+
__exportStar(require("./alert"), exports);
|
|
90
91
|
__exportStar(require("./analyze"), exports);
|
|
91
92
|
__exportStar(require("./appconfig"), exports);
|
|
92
93
|
__exportStar(require("./redis"), exports);
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export interface QuoteTier {
|
|
2
|
+
pct: number;
|
|
3
|
+
price: number;
|
|
4
|
+
amount: number;
|
|
5
|
+
amount_in: number;
|
|
6
|
+
amount_in_usd: number;
|
|
7
|
+
fee: number;
|
|
8
|
+
fee_usd: number;
|
|
9
|
+
tick_move?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface QuoteEntry {
|
|
12
|
+
price: number;
|
|
13
|
+
amount: number;
|
|
14
|
+
amount_in: number;
|
|
15
|
+
amount_in_usd: number;
|
|
16
|
+
fee: number;
|
|
17
|
+
fee_usd: number;
|
|
18
|
+
tick_move?: string;
|
|
19
|
+
tiers: QuoteTier[];
|
|
20
|
+
}
|
|
21
|
+
export interface QuoteDepthOutput {
|
|
22
|
+
mid_price: number;
|
|
23
|
+
fee_rate_bps: number;
|
|
24
|
+
ask: QuoteEntry;
|
|
25
|
+
bid: QuoteEntry;
|
|
26
|
+
}
|
|
27
|
+
export interface OrderbookTier {
|
|
28
|
+
pct: number;
|
|
29
|
+
price: number;
|
|
30
|
+
amount: number;
|
|
31
|
+
usdValue: number;
|
|
32
|
+
}
|
|
33
|
+
export interface OrderbookEntry {
|
|
34
|
+
price: number;
|
|
35
|
+
amount: number;
|
|
36
|
+
usdValue: number;
|
|
37
|
+
tiers: OrderbookTier[];
|
|
38
|
+
}
|
|
39
|
+
export declare function toOrderbookEntry(q: QuoteEntry): OrderbookEntry;
|
|
40
|
+
export declare const DEFAULT_TIER_PCT = 0.2;
|
|
41
|
+
export declare function getDepthPricePctLevels(): number[];
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_TIER_PCT = void 0;
|
|
4
|
+
exports.toOrderbookEntry = toOrderbookEntry;
|
|
5
|
+
exports.getDepthPricePctLevels = getDepthPricePctLevels;
|
|
6
|
+
function toOrderbookEntry(q) {
|
|
7
|
+
return {
|
|
8
|
+
price: q.price,
|
|
9
|
+
amount: q.amount,
|
|
10
|
+
usdValue: q.amount_in_usd,
|
|
11
|
+
tiers: q.tiers.map(t => ({ pct: t.pct, price: t.price, amount: t.amount, usdValue: t.amount_in_usd })),
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
const PCT_LEVEL_DEFAULT = [0.1, 0.2, 0.3, 0.5, 1.0];
|
|
15
|
+
const PCT_LEVEL_CAP = 1.0;
|
|
16
|
+
exports.DEFAULT_TIER_PCT = 0.2;
|
|
17
|
+
let _pctLevelsCache;
|
|
18
|
+
function getDepthPricePctLevels() {
|
|
19
|
+
if (_pctLevelsCache)
|
|
20
|
+
return _pctLevelsCache;
|
|
21
|
+
const raw = process.env.DEPTH_PRICE_PCT;
|
|
22
|
+
let levels;
|
|
23
|
+
if (raw) {
|
|
24
|
+
levels = raw.split(',').map(s => parseFloat(s.trim())).filter(n => !isNaN(n) && n > 0 && n <= PCT_LEVEL_CAP);
|
|
25
|
+
if (levels.length === 0)
|
|
26
|
+
levels = [...PCT_LEVEL_DEFAULT];
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
levels = [...PCT_LEVEL_DEFAULT];
|
|
30
|
+
}
|
|
31
|
+
if (!levels.includes(exports.DEFAULT_TIER_PCT))
|
|
32
|
+
levels.push(exports.DEFAULT_TIER_PCT);
|
|
33
|
+
levels = Array.from(new Set(levels)).sort((a, b) => a - b);
|
|
34
|
+
_pctLevelsCache = levels;
|
|
35
|
+
return levels;
|
|
36
|
+
}
|
package/dist/quote/index.d.ts
CHANGED
package/dist/quote/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { StandardPoolInfoType, QuoteResultType } from "../../types";
|
|
2
2
|
import { AppConfig } from "../appconfig";
|
|
3
|
+
import { QuoteDepthOutput } from "./depth_tier";
|
|
3
4
|
export interface PoolDepthData {
|
|
4
5
|
ask: Record<number, {
|
|
5
6
|
amount: number;
|
|
@@ -27,7 +28,7 @@ export interface QuoteResponseOptions {
|
|
|
27
28
|
txid: string;
|
|
28
29
|
pushPriceMessage?: boolean;
|
|
29
30
|
source?: string;
|
|
30
|
-
depth?:
|
|
31
|
+
depth?: QuoteDepthOutput;
|
|
31
32
|
}
|
|
32
33
|
export declare function on_quote_response(opts: QuoteResponseOptions): void;
|
|
33
34
|
export declare function report_quote_candidate(opts: {
|
|
@@ -38,5 +39,16 @@ export declare function report_quote_candidate(opts: {
|
|
|
38
39
|
block_number: number;
|
|
39
40
|
ask_price: number | string;
|
|
40
41
|
bid_price: number | string;
|
|
42
|
+
ask_tiers?: Array<{
|
|
43
|
+
pct: number;
|
|
44
|
+
price: number;
|
|
45
|
+
amount: number;
|
|
46
|
+
amount_in: number;
|
|
47
|
+
}>;
|
|
48
|
+
bid_tiers?: Array<{
|
|
49
|
+
pct: number;
|
|
50
|
+
price: number;
|
|
51
|
+
amount: number;
|
|
52
|
+
amount_in: number;
|
|
53
|
+
}>;
|
|
41
54
|
}): void;
|
|
42
|
-
export declare function on_quote_respose(appConfig: AppConfig, pool_info: StandardPoolInfoType, quote_amount_usd: number, _execution_price: number, stream_time: number, quote_start_time: number, slot_info: string, result: [QuoteResultType, QuoteResultType, number], txid: string, push_price_message?: boolean, source?: string, depth?: PoolDepthData): void;
|
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.on_quote_response = on_quote_response;
|
|
4
4
|
exports.report_quote_candidate = report_quote_candidate;
|
|
5
|
-
exports.on_quote_respose = on_quote_respose;
|
|
6
5
|
const analyze_1 = require("../analyze");
|
|
7
6
|
const log_quote_price_1 = require("./log_quote_price");
|
|
8
7
|
const publish_quote_price_1 = require("./publish_quote_price");
|
|
9
8
|
const to_price_message_1 = require("./to_price_message");
|
|
10
9
|
function on_quote_response(opts) {
|
|
10
|
+
var _a, _b;
|
|
11
11
|
const { appConfig, poolInfo, quoteAmountUsd, streamTime, quoteStartTime, blockNumber, quotes, txid, pushPriceMessage = true, source = '', depth, } = opts;
|
|
12
12
|
const quoteEndTime = Date.now();
|
|
13
|
-
const quoteAskPrice = Number(quotes[0].price);
|
|
14
|
-
const quoteBidPrice = Number(quotes[1].price);
|
|
15
13
|
const priceTime = Date.now();
|
|
14
|
+
const quoteAskPrice = Number((_a = quotes[0]) === null || _a === void 0 ? void 0 : _a.price) || 0;
|
|
15
|
+
const quoteBidPrice = Number((_b = quotes[1]) === null || _b === void 0 ? void 0 : _b.price) || 0;
|
|
16
16
|
const time = {
|
|
17
17
|
block_time: 0,
|
|
18
18
|
stream_time: streamTime,
|
|
@@ -33,7 +33,10 @@ function on_quote_response(opts) {
|
|
|
33
33
|
price_id: price_msg.price_id,
|
|
34
34
|
unique_orderbook_id: price_msg.unique_orderbook_id,
|
|
35
35
|
pair: price_msg.pair,
|
|
36
|
-
depth,
|
|
36
|
+
mid_price: depth.mid_price,
|
|
37
|
+
fee_rate_bps: depth.fee_rate_bps,
|
|
38
|
+
ask: depth.ask,
|
|
39
|
+
bid: depth.bid,
|
|
37
40
|
});
|
|
38
41
|
}
|
|
39
42
|
})
|
|
@@ -52,21 +55,8 @@ function report_quote_candidate(opts) {
|
|
|
52
55
|
block_number: opts.block_number,
|
|
53
56
|
ask_price: Number(opts.ask_price) || 0,
|
|
54
57
|
bid_price: Number(opts.bid_price) || 0,
|
|
58
|
+
ask_tiers: opts.ask_tiers,
|
|
59
|
+
bid_tiers: opts.bid_tiers,
|
|
55
60
|
computed_at: Date.now(),
|
|
56
61
|
});
|
|
57
62
|
}
|
|
58
|
-
function on_quote_respose(appConfig, pool_info, quote_amount_usd, _execution_price, stream_time, quote_start_time, slot_info, result, txid, push_price_message = true, source = "", depth) {
|
|
59
|
-
on_quote_response({
|
|
60
|
-
appConfig,
|
|
61
|
-
poolInfo: pool_info,
|
|
62
|
-
quoteAmountUsd: quote_amount_usd,
|
|
63
|
-
streamTime: stream_time,
|
|
64
|
-
quoteStartTime: quote_start_time,
|
|
65
|
-
blockNumber: parseInt(slot_info, 10) || 0,
|
|
66
|
-
quotes: [result[0], result[1]],
|
|
67
|
-
txid,
|
|
68
|
-
pushPriceMessage: push_price_message,
|
|
69
|
-
source,
|
|
70
|
-
depth,
|
|
71
|
-
});
|
|
72
|
-
}
|
|
@@ -1,15 +1,30 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
3
|
+
var t = {};
|
|
4
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
5
|
+
t[p] = s[p];
|
|
6
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
7
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
8
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
9
|
+
t[p[i]] = s[p[i]];
|
|
10
|
+
}
|
|
11
|
+
return t;
|
|
12
|
+
};
|
|
2
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
14
|
exports.publish_quote_price = void 0;
|
|
4
15
|
const __1 = require("..");
|
|
16
|
+
const depth_tier_1 = require("./depth_tier");
|
|
5
17
|
const publish_quote_price = (appConfig, price_msg) => {
|
|
6
18
|
const chain_id = appConfig.env_args.chain_id;
|
|
7
19
|
let price_id = price_msg.price_id;
|
|
20
|
+
const { fee_rate_bps, mid_price } = price_msg, rest = __rest(price_msg, ["fee_rate_bps", "mid_price"]);
|
|
21
|
+
const isTierEntry = (e) => e && 'tiers' in e && Array.isArray(e.tiers);
|
|
22
|
+
const orderbook_msg = Object.assign(Object.assign({}, rest), { ask: isTierEntry(price_msg.ask) ? (0, depth_tier_1.toOrderbookEntry)(price_msg.ask) : price_msg.ask, bid: isTierEntry(price_msg.bid) ? (0, depth_tier_1.toOrderbookEntry)(price_msg.bid) : price_msg.bid });
|
|
8
23
|
let event = {
|
|
9
24
|
event_type: __1.REDIS_EVENT_TYPE_QUOTE.NEW_QUOTE_PRICE,
|
|
10
25
|
event_time: Date.now(),
|
|
11
26
|
chain_id,
|
|
12
|
-
data:
|
|
27
|
+
data: orderbook_msg,
|
|
13
28
|
};
|
|
14
29
|
appConfig.arb_cache.redis_event_publisher.publish_quote_price_event(chain_id, event);
|
|
15
30
|
appConfig.arb_cache.cache_price_message(price_msg)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { StandardPoolInfoType, QuoteTimeInfoType, PriceMessageType } from '../../types';
|
|
2
2
|
import { AppConfig } from '../appconfig';
|
|
3
|
-
import {
|
|
3
|
+
import { QuoteDepthOutput } from './depth_tier';
|
|
4
4
|
export declare const get_quote_token_decimals: (pool_info: StandardPoolInfoType) => number;
|
|
5
|
-
export declare function to_price_message(appConfig: AppConfig, quote_amount_usd: number, tx_price: number, quote_ask_price: number, quote_bid_price: number, pool_info: StandardPoolInfoType, time: QuoteTimeInfoType, slot
|
|
5
|
+
export declare function to_price_message(appConfig: AppConfig, quote_amount_usd: number, tx_price: number, quote_ask_price: number, quote_bid_price: number, pool_info: StandardPoolInfoType, time: QuoteTimeInfoType, slot: string | undefined, source: string, depth?: QuoteDepthOutput): Promise<PriceMessageType>;
|
|
6
6
|
export declare function normalize_pair_name(priceMessage: PriceMessageType): void;
|
|
@@ -8,15 +8,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
-
};
|
|
14
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
12
|
exports.get_quote_token_decimals = void 0;
|
|
16
13
|
exports.to_price_message = to_price_message;
|
|
17
14
|
exports.normalize_pair_name = normalize_pair_name;
|
|
18
|
-
const decimal_js_1 = __importDefault(require("decimal.js"));
|
|
19
|
-
const core_env_1 = require("../appconfig/core_env");
|
|
20
15
|
const uuid_1 = require("uuid");
|
|
21
16
|
const __1 = require("..");
|
|
22
17
|
const get_quote_token_decimals = (pool_info) => {
|
|
@@ -31,43 +26,40 @@ const get_quote_token_decimals = (pool_info) => {
|
|
|
31
26
|
return decimals;
|
|
32
27
|
};
|
|
33
28
|
exports.get_quote_token_decimals = get_quote_token_decimals;
|
|
34
|
-
function to_price_message(
|
|
35
|
-
return __awaiter(this,
|
|
36
|
-
var _a, _b;
|
|
29
|
+
function to_price_message(appConfig, quote_amount_usd, tx_price, quote_ask_price, quote_bid_price, pool_info, time, slot, source, depth) {
|
|
30
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
37
31
|
let { pool_address, tokenA, tokenB } = pool_info;
|
|
38
32
|
let { dex_id, pool_name, fee_rate, is_reverse_token } = yield appConfig.arb_cache.get_one_pool_info(pool_address);
|
|
39
33
|
let _dex_id = dex_id;
|
|
40
34
|
let price_id = (0, uuid_1.v4)().replace(/-/gi, '');
|
|
41
35
|
let chain_id = appConfig.env_args.chain_id;
|
|
42
36
|
let unique_orderbook_id = (0, __1.format_unique_orderbook_id)(chain_id, _dex_id, pool_address, fee_rate);
|
|
43
|
-
time.price_time =
|
|
44
|
-
let
|
|
45
|
-
let
|
|
46
|
-
let
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
bidData.depth_bps = cexBps;
|
|
62
|
-
bidData.depth_amount = bidDepth ? bidDepth.amount.toFixed(6) : '0';
|
|
63
|
-
bidData.depth_usd = bidDepth ? bidDepth.amountUsd.toFixed(2) : '0';
|
|
64
|
-
let price_message = {
|
|
37
|
+
time.price_time = Date.now();
|
|
38
|
+
let ask;
|
|
39
|
+
let bid;
|
|
40
|
+
let mid_price;
|
|
41
|
+
let fee_rate_bps;
|
|
42
|
+
if (depth) {
|
|
43
|
+
ask = quote_ask_price > 0 ? Object.assign(Object.assign({}, depth.ask), { price: quote_ask_price.toFixed(12) }) : depth.ask;
|
|
44
|
+
bid = quote_bid_price > 0 ? Object.assign(Object.assign({}, depth.bid), { price: quote_bid_price.toFixed(12) }) : depth.bid;
|
|
45
|
+
mid_price = depth.mid_price;
|
|
46
|
+
fee_rate_bps = depth.fee_rate_bps;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
ask = { price: quote_ask_price.toFixed(12), quantity: quote_amount_usd };
|
|
50
|
+
bid = { price: quote_bid_price.toFixed(12), quantity: quote_amount_usd };
|
|
51
|
+
mid_price = (quote_ask_price + quote_bid_price) / 2;
|
|
52
|
+
fee_rate_bps = fee_rate;
|
|
53
|
+
}
|
|
54
|
+
const price_message = {
|
|
65
55
|
chain_id,
|
|
66
56
|
dex_id,
|
|
67
57
|
price_id,
|
|
68
58
|
pool_id: pool_address,
|
|
69
59
|
pool_name,
|
|
70
60
|
fee_rate,
|
|
61
|
+
fee_rate_bps,
|
|
62
|
+
mid_price,
|
|
71
63
|
pair: pool_name,
|
|
72
64
|
unique_orderbook_id,
|
|
73
65
|
is_reverse_token,
|
|
@@ -87,25 +79,19 @@ function to_price_message(appConfig_1, quote_amount_usd_1, tx_price_1, quote_ask
|
|
|
87
79
|
name: tokenB.name,
|
|
88
80
|
enable: true,
|
|
89
81
|
},
|
|
90
|
-
tx_price: tx_price
|
|
82
|
+
tx_price: tx_price ? tx_price.toFixed(12) : undefined,
|
|
91
83
|
quote_amount_usd,
|
|
92
|
-
ask
|
|
93
|
-
bid
|
|
84
|
+
ask,
|
|
85
|
+
bid,
|
|
94
86
|
time,
|
|
95
|
-
slot,
|
|
87
|
+
slot: slot !== null && slot !== void 0 ? slot : '',
|
|
96
88
|
blockNumber: slot ? parseInt(slot, 10) || undefined : undefined,
|
|
97
|
-
source: source !== null && source !== void 0 ? source :
|
|
89
|
+
source: source !== null && source !== void 0 ? source : '',
|
|
98
90
|
};
|
|
99
91
|
normalize_pair_name(price_message);
|
|
100
92
|
return price_message;
|
|
101
93
|
});
|
|
102
94
|
}
|
|
103
|
-
const getAskPrice = (price, feeRate) => {
|
|
104
|
-
return price.mul(1 + feeRate).toFixed(12);
|
|
105
|
-
};
|
|
106
|
-
const getBidPrice = (price, feeRate) => {
|
|
107
|
-
return price.mul(1 - feeRate).toFixed(12);
|
|
108
|
-
};
|
|
109
95
|
function normalize_pair_name(priceMessage) {
|
|
110
96
|
let { pool_name, is_reverse_token } = priceMessage;
|
|
111
97
|
if (is_reverse_token) {
|
|
@@ -59,7 +59,7 @@ class AbstractTransactionResultCheck {
|
|
|
59
59
|
let { pool_name, is_reverse_token } = this.pool_info;
|
|
60
60
|
let { chain_id, dex_id, unique_orderbook_id, pair, price_id, time: quote_time, ask, bid } = this.price_msg;
|
|
61
61
|
let { group_id, unique_order_msg_id, order_send_time, order_recv_time, order_submit_time, aToB } = this.order_msg;
|
|
62
|
-
let order_price = aToB ? bid.price : ask.price;
|
|
62
|
+
let order_price = String(aToB ? bid.price : ask.price);
|
|
63
63
|
let server_info = (0, __1.getServerInfo)();
|
|
64
64
|
let order_end_time = Date.now();
|
|
65
65
|
let start_time = order_recv_time;
|
package/package.json
CHANGED
package/types/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { QuoteEntry } from '../src/quote/depth_tier'
|
|
2
|
+
|
|
1
3
|
/*********************************************************************
|
|
2
4
|
* CONFIG - TOKEN CONFIG TYPE *
|
|
3
5
|
* *******************************************************************/
|
|
@@ -259,7 +261,12 @@ export interface PriceMessageType {
|
|
|
259
261
|
dex_id: string
|
|
260
262
|
pool_id: string
|
|
261
263
|
pool_name: string
|
|
264
|
+
/** @deprecated 使用 fee_rate_bps;该字段在 W5 清理时移除 */
|
|
262
265
|
fee_rate: number
|
|
266
|
+
/** 池子 effective fee(bps,例如 25 表示 0.25%);动态协议为最新观测值 */
|
|
267
|
+
fee_rate_bps: number
|
|
268
|
+
/** 池子当前 mid price(无冲击、无费的中间价;CLMM 从 sqrtPriceX96 反算,AMM 从 reserves 比值) */
|
|
269
|
+
mid_price: number
|
|
263
270
|
pair: string // 标准化的币对名称
|
|
264
271
|
unique_orderbook_id: string // ${chain_id}_${dex_id}_${pool_id}_${fee_rate}
|
|
265
272
|
is_reverse_token: boolean // 是否反转币对进行交易(按USDT,SOL,TRX等作为计价币)
|
|
@@ -267,9 +274,16 @@ export interface PriceMessageType {
|
|
|
267
274
|
tokenB?: StandardTokenInfoType
|
|
268
275
|
price_id: string // 价格消息的唯一id
|
|
269
276
|
tx_price?: string // 链上交易的执行价格
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
277
|
+
/** @deprecated W5 清理时移除;新模型不再用 USD 名义额驱动询价 */
|
|
278
|
+
quote_amount_usd: number
|
|
279
|
+
/**
|
|
280
|
+
* 卖方报价(双形态:V2 走 QuoteEntry tier 化、V1/V3 走 PriceType scalar)
|
|
281
|
+
*
|
|
282
|
+
* 下游用 `'tiers' in ask` 运行时判别 + `source` 后缀(`:v2` vs `:v1`/`:v3`)双保险
|
|
283
|
+
*/
|
|
284
|
+
ask: QuoteEntry | PriceType
|
|
285
|
+
/** 买方报价:同上,两种形态共存 */
|
|
286
|
+
bid: QuoteEntry | PriceType
|
|
273
287
|
time: QuoteTimeInfoType // quote 耗时数据
|
|
274
288
|
slot: string // 询价生成的价格,所对应的slot (account, vaultA, vaultB)
|
|
275
289
|
blockNumber?: number // 报价基于的区块号
|
|
@@ -290,10 +304,13 @@ export interface UniqueOrderbookIdType {
|
|
|
290
304
|
fee_rate: string
|
|
291
305
|
}
|
|
292
306
|
|
|
307
|
+
/**
|
|
308
|
+
* @deprecated 使用 QuoteEntry (from src/quote/depth_tier)。W5 清理时移除。
|
|
309
|
+
* 旧的"单价 + 动态 depth_{bps}_*"扁平结构。
|
|
310
|
+
*/
|
|
293
311
|
export interface PriceType {
|
|
294
312
|
price: string
|
|
295
|
-
quantity: number
|
|
296
|
-
/** 动态深度字段:depth_{bps} = token 数量, depth_{bps}_usd = USD 估值 */
|
|
313
|
+
quantity: number
|
|
297
314
|
[key: string]: any
|
|
298
315
|
}
|
|
299
316
|
|
|
@@ -312,21 +329,37 @@ export interface QuoteTimeInfoType {
|
|
|
312
329
|
export interface OrderbookPriceType {
|
|
313
330
|
chain_id: string
|
|
314
331
|
pair: string
|
|
315
|
-
|
|
332
|
+
/** @deprecated W5 删除:新模型不再用 USD 名义额驱动询价。保留只为兼容旧消费者 */
|
|
333
|
+
quote_amount_usd?: number
|
|
316
334
|
asks: Ladder[]
|
|
317
335
|
bids: Ladder[]
|
|
318
336
|
}
|
|
319
337
|
|
|
338
|
+
/**
|
|
339
|
+
* Orderbook 对外 HTTP 响应里的单档报价行(向 CEX 暴露)。
|
|
340
|
+
*
|
|
341
|
+
* 与内部 PriceMessage.ask 的对应关系:
|
|
342
|
+
* - 顶层 price/amount 镜像 tiers[pct=0.2](默认主档)
|
|
343
|
+
* - tiers 数组按 pct 升序,每档含 {pct, price, amount}
|
|
344
|
+
* - 不含 amount_in/fee/tick_move 等内部字段(已在 publish 时投影掉)
|
|
345
|
+
*/
|
|
320
346
|
export interface Ladder {
|
|
321
347
|
unique_orderbook_id: string
|
|
348
|
+
|
|
349
|
+
/** 默认主档(0.2%)目标价;字符串以保持 wire 兼容性 */
|
|
322
350
|
price: string
|
|
323
|
-
|
|
351
|
+
/** 默认主档(0.2%)可成交 base token 数量 */
|
|
352
|
+
qty: string
|
|
353
|
+
/** 默认主档(0.2%)该档位 USD 估值 */
|
|
354
|
+
usd_value: string
|
|
355
|
+
/** 多档完整数据(按 pct 升序:0.1, 0.2, 0.3, 0.5, 1.0) */
|
|
356
|
+
tiers: { pct: number; price: string; qty: string; usd_value: string }[]
|
|
324
357
|
|
|
325
358
|
// 询价生成的价格,所对应的slot (account, vaultA, vaultB)
|
|
326
359
|
slot: string
|
|
327
360
|
|
|
328
361
|
// 区块号
|
|
329
|
-
|
|
362
|
+
block_number?: string
|
|
330
363
|
|
|
331
364
|
// 价格来源: "rpc:v1" | "{provider_id}:v2" | "{provider_id}:v3"
|
|
332
365
|
source?: string
|
|
@@ -852,14 +885,36 @@ export interface OnTradePriceType {
|
|
|
852
885
|
quote_bid: string
|
|
853
886
|
cex_price: string
|
|
854
887
|
exec_price: string
|
|
855
|
-
/**
|
|
888
|
+
/**
|
|
889
|
+
* 报价 vs 成交偏差 (bps), 正=成交比报价差
|
|
890
|
+
* - tier 模式:用按 actualAmount 匹配的 tier price 作为参考价(更准)
|
|
891
|
+
* - scalar 模式:用 ask/bid 标量价
|
|
892
|
+
*/
|
|
856
893
|
quote_exec_deviation_bps: number
|
|
857
894
|
/** CEX vs DEX 价差 (bps) */
|
|
858
895
|
cex_dex_spread_bps: number
|
|
896
|
+
|
|
897
|
+
// ===== W3 新增:tier 模式偏差细分(旧消费者忽略可继续工作) =====
|
|
898
|
+
/** 实际成交量命中的 tier 偏移百分比(如 0.2 表示 0.2%) */
|
|
899
|
+
matched_tier_pct?: number
|
|
900
|
+
/**
|
|
901
|
+
* tier 匹配模式:
|
|
902
|
+
* - 'exact': 落在某档 amount_in 上
|
|
903
|
+
* - 'interpolated': 在两档之间线性插值
|
|
904
|
+
* - 'extrapolated_low': 小于最低档(用最低档兜底)
|
|
905
|
+
* - 'extrapolated_high': 大于最高档(用最高档兜底;提示成交量超出深度)
|
|
906
|
+
* - 'single_tier': V1 兜底仅 1 档可用
|
|
907
|
+
*/
|
|
908
|
+
matched_tier_mode?: string
|
|
909
|
+
/** 纯价格漂移 (bps) = tier 匹配价 vs 成交价 —— 隔离了"大小不匹配"带来的伪偏差 */
|
|
910
|
+
price_drift_bps?: number
|
|
859
911
|
}
|
|
860
912
|
|
|
861
913
|
export interface OnTradeDepthType {
|
|
914
|
+
/** @deprecated 旧 bps-keyed 结构;新代码用 tiers 数组 */
|
|
862
915
|
levels: Record<number, AnalyzeDepthLevelType>
|
|
916
|
+
/** W3 新增:按 pct 升序的多档数据 */
|
|
917
|
+
tiers?: AnalyzeTierLevelType[]
|
|
863
918
|
trade_size_usd: number
|
|
864
919
|
/** 交易量消耗了多少可用深度 (%) */
|
|
865
920
|
depth_utilization_pct: number
|
|
@@ -876,6 +931,20 @@ export interface AnalyzeDepthLevelType {
|
|
|
876
931
|
amount_in_usd: number
|
|
877
932
|
}
|
|
878
933
|
|
|
934
|
+
/**
|
|
935
|
+
* W3 新增:tier 化深度档位
|
|
936
|
+
*/
|
|
937
|
+
export interface AnalyzeTierLevelType {
|
|
938
|
+
pct: number
|
|
939
|
+
target_price: number
|
|
940
|
+
amount: number
|
|
941
|
+
amount_in: number
|
|
942
|
+
amount_in_usd: number
|
|
943
|
+
fee: number
|
|
944
|
+
fee_usd: number
|
|
945
|
+
tick_move?: string
|
|
946
|
+
}
|
|
947
|
+
|
|
879
948
|
export interface OnTradeExecutionType {
|
|
880
949
|
// Quote 阶段(来自 PriceMessage.time)
|
|
881
950
|
block_time: number // 链上出块时间
|
|
@@ -958,6 +1027,8 @@ export interface AnalyzeQuoteSnapshotType {
|
|
|
958
1027
|
bid: string
|
|
959
1028
|
block_number: number
|
|
960
1029
|
source_txid: string
|
|
1030
|
+
/** 报价来源标签: 'rpc:v1' | '{provider}:v2' | '{provider}:v3' */
|
|
1031
|
+
source?: string
|
|
961
1032
|
stream_recv_time: number
|
|
962
1033
|
price_ready_time: number
|
|
963
1034
|
}
|