@tradejs/node 2.0.18 → 2.0.20
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/ai.js +4763 -168
- package/dist/ai.mjs +2 -3
- package/dist/backtest.js +4117 -1326
- package/dist/backtest.mjs +660 -850
- package/dist/chunk-4AVFJMQL.mjs +6054 -0
- package/dist/chunk-Y6FXYEAI.mjs +10 -0
- package/dist/cli.js +5537 -6403
- package/dist/cli.mjs +5 -11
- package/dist/connectors.mjs +1 -1
- package/dist/constants.mjs +1 -1
- package/dist/pine.mjs +122 -13
- package/dist/registry.js +5732 -48
- package/dist/registry.mjs +2 -2
- package/dist/strategies.d.mts +2 -2
- package/dist/strategies.d.ts +2 -2
- package/dist/strategies.js +2296 -7843
- package/dist/strategies.mjs +46 -2547
- package/package.json +6 -4
- package/dist/chunk-3BP3ETAD.mjs +0 -2134
- package/dist/chunk-GPR56UYQ.mjs +0 -5518
- package/dist/chunk-IQKMII6L.mjs +0 -137
- package/dist/chunk-QVSMINLG.mjs +0 -246
- package/dist/chunk-UV2HVMKZ.mjs +0 -41
- package/dist/chunk-WK5EUCX5.mjs +0 -1191
package/dist/chunk-3BP3ETAD.mjs
DELETED
|
@@ -1,2134 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getStrategyProfileMlAdapter
|
|
3
|
-
} from "./chunk-WK5EUCX5.mjs";
|
|
4
|
-
import {
|
|
5
|
-
getStrategyManifest
|
|
6
|
-
} from "./chunk-QVSMINLG.mjs";
|
|
7
|
-
|
|
8
|
-
// src/binanceBreadthUniverses.ts
|
|
9
|
-
import { createHash } from "crypto";
|
|
10
|
-
|
|
11
|
-
// src/config/binanceBreadthUniverses.json
|
|
12
|
-
var binanceBreadthUniverses_default = {
|
|
13
|
-
schemaVersion: 1,
|
|
14
|
-
updatedAt: "2026-07-24T15:28:27.234Z",
|
|
15
|
-
source: "binance_spot_usdt_turnover24h",
|
|
16
|
-
fingerprint: "43df006e5510bb99",
|
|
17
|
-
universes: {
|
|
18
|
-
top5: {
|
|
19
|
-
size: 5,
|
|
20
|
-
fingerprint: "ba5b73456b18",
|
|
21
|
-
symbols: ["ETHUSDT", "USD1USDT", "SOLUSDT", "OPNUSDT", "VANAUSDT"]
|
|
22
|
-
},
|
|
23
|
-
top10: {
|
|
24
|
-
size: 10,
|
|
25
|
-
fingerprint: "370eb5af9778",
|
|
26
|
-
symbols: [
|
|
27
|
-
"ETHUSDT",
|
|
28
|
-
"USD1USDT",
|
|
29
|
-
"SOLUSDT",
|
|
30
|
-
"OPNUSDT",
|
|
31
|
-
"VANAUSDT",
|
|
32
|
-
"DEXEUSDT",
|
|
33
|
-
"BANKUSDT",
|
|
34
|
-
"ZECUSDT",
|
|
35
|
-
"XRPUSDT",
|
|
36
|
-
"RLUSDUSDT"
|
|
37
|
-
]
|
|
38
|
-
},
|
|
39
|
-
top30: {
|
|
40
|
-
size: 30,
|
|
41
|
-
fingerprint: "685a7e7f5fce",
|
|
42
|
-
symbols: [
|
|
43
|
-
"ETHUSDT",
|
|
44
|
-
"USD1USDT",
|
|
45
|
-
"SOLUSDT",
|
|
46
|
-
"OPNUSDT",
|
|
47
|
-
"VANAUSDT",
|
|
48
|
-
"DEXEUSDT",
|
|
49
|
-
"BANKUSDT",
|
|
50
|
-
"ZECUSDT",
|
|
51
|
-
"XRPUSDT",
|
|
52
|
-
"RLUSDUSDT",
|
|
53
|
-
"BNBUSDT",
|
|
54
|
-
"DOGEUSDT",
|
|
55
|
-
"REUSDT",
|
|
56
|
-
"RIFUSDT",
|
|
57
|
-
"ZAMAUSDT",
|
|
58
|
-
"TRXUSDT",
|
|
59
|
-
"EURUSDT",
|
|
60
|
-
"AEROUSDT",
|
|
61
|
-
"UUSDT",
|
|
62
|
-
"SUIUSDT",
|
|
63
|
-
"XAUTUSDT",
|
|
64
|
-
"NEARUSDT",
|
|
65
|
-
"LTCUSDT",
|
|
66
|
-
"SPCXBUSDT",
|
|
67
|
-
"LAUSDT",
|
|
68
|
-
"AVAXUSDT",
|
|
69
|
-
"WLDUSDT",
|
|
70
|
-
"ONDOUSDT",
|
|
71
|
-
"XLMUSDT",
|
|
72
|
-
"UNIUSDT"
|
|
73
|
-
]
|
|
74
|
-
},
|
|
75
|
-
top50: {
|
|
76
|
-
size: 50,
|
|
77
|
-
fingerprint: "cb9725e24141",
|
|
78
|
-
symbols: [
|
|
79
|
-
"ETHUSDT",
|
|
80
|
-
"USD1USDT",
|
|
81
|
-
"SOLUSDT",
|
|
82
|
-
"OPNUSDT",
|
|
83
|
-
"VANAUSDT",
|
|
84
|
-
"DEXEUSDT",
|
|
85
|
-
"BANKUSDT",
|
|
86
|
-
"ZECUSDT",
|
|
87
|
-
"XRPUSDT",
|
|
88
|
-
"RLUSDUSDT",
|
|
89
|
-
"BNBUSDT",
|
|
90
|
-
"DOGEUSDT",
|
|
91
|
-
"REUSDT",
|
|
92
|
-
"RIFUSDT",
|
|
93
|
-
"ZAMAUSDT",
|
|
94
|
-
"TRXUSDT",
|
|
95
|
-
"EURUSDT",
|
|
96
|
-
"AEROUSDT",
|
|
97
|
-
"UUSDT",
|
|
98
|
-
"SUIUSDT",
|
|
99
|
-
"XAUTUSDT",
|
|
100
|
-
"NEARUSDT",
|
|
101
|
-
"LTCUSDT",
|
|
102
|
-
"SPCXBUSDT",
|
|
103
|
-
"LAUSDT",
|
|
104
|
-
"AVAXUSDT",
|
|
105
|
-
"WLDUSDT",
|
|
106
|
-
"ONDOUSDT",
|
|
107
|
-
"XLMUSDT",
|
|
108
|
-
"UNIUSDT",
|
|
109
|
-
"ADAUSDT",
|
|
110
|
-
"SNDKBUSDT",
|
|
111
|
-
"KAITOUSDT",
|
|
112
|
-
"UTKUSDT",
|
|
113
|
-
"PEPEUSDT",
|
|
114
|
-
"WLFIUSDT",
|
|
115
|
-
"PAXGUSDT",
|
|
116
|
-
"ENAUSDT",
|
|
117
|
-
"TAOUSDT",
|
|
118
|
-
"AAVEUSDT",
|
|
119
|
-
"CRCLBUSDT",
|
|
120
|
-
"LINKUSDT",
|
|
121
|
-
"TONUSDT",
|
|
122
|
-
"ALLOUSDT",
|
|
123
|
-
"PUMPUSDT",
|
|
124
|
-
"INJUSDT",
|
|
125
|
-
"HOMEUSDT",
|
|
126
|
-
"MUBUSDT",
|
|
127
|
-
"TSLABUSDT",
|
|
128
|
-
"GRAMUSDT"
|
|
129
|
-
]
|
|
130
|
-
},
|
|
131
|
-
top100: {
|
|
132
|
-
size: 100,
|
|
133
|
-
fingerprint: "f397883a81cf",
|
|
134
|
-
symbols: [
|
|
135
|
-
"ETHUSDT",
|
|
136
|
-
"USD1USDT",
|
|
137
|
-
"SOLUSDT",
|
|
138
|
-
"OPNUSDT",
|
|
139
|
-
"VANAUSDT",
|
|
140
|
-
"DEXEUSDT",
|
|
141
|
-
"BANKUSDT",
|
|
142
|
-
"ZECUSDT",
|
|
143
|
-
"XRPUSDT",
|
|
144
|
-
"RLUSDUSDT",
|
|
145
|
-
"BNBUSDT",
|
|
146
|
-
"DOGEUSDT",
|
|
147
|
-
"REUSDT",
|
|
148
|
-
"RIFUSDT",
|
|
149
|
-
"ZAMAUSDT",
|
|
150
|
-
"TRXUSDT",
|
|
151
|
-
"EURUSDT",
|
|
152
|
-
"AEROUSDT",
|
|
153
|
-
"UUSDT",
|
|
154
|
-
"SUIUSDT",
|
|
155
|
-
"XAUTUSDT",
|
|
156
|
-
"NEARUSDT",
|
|
157
|
-
"LTCUSDT",
|
|
158
|
-
"SPCXBUSDT",
|
|
159
|
-
"LAUSDT",
|
|
160
|
-
"AVAXUSDT",
|
|
161
|
-
"WLDUSDT",
|
|
162
|
-
"ONDOUSDT",
|
|
163
|
-
"XLMUSDT",
|
|
164
|
-
"UNIUSDT",
|
|
165
|
-
"ADAUSDT",
|
|
166
|
-
"SNDKBUSDT",
|
|
167
|
-
"KAITOUSDT",
|
|
168
|
-
"UTKUSDT",
|
|
169
|
-
"PEPEUSDT",
|
|
170
|
-
"WLFIUSDT",
|
|
171
|
-
"PAXGUSDT",
|
|
172
|
-
"ENAUSDT",
|
|
173
|
-
"TAOUSDT",
|
|
174
|
-
"AAVEUSDT",
|
|
175
|
-
"CRCLBUSDT",
|
|
176
|
-
"LINKUSDT",
|
|
177
|
-
"TONUSDT",
|
|
178
|
-
"ALLOUSDT",
|
|
179
|
-
"PUMPUSDT",
|
|
180
|
-
"INJUSDT",
|
|
181
|
-
"HOMEUSDT",
|
|
182
|
-
"MUBUSDT",
|
|
183
|
-
"TSLABUSDT",
|
|
184
|
-
"GRAMUSDT",
|
|
185
|
-
"\u5E01\u5B89\u4EBA\u751FUSDT",
|
|
186
|
-
"SYNUSDT",
|
|
187
|
-
"TRUMPUSDT",
|
|
188
|
-
"PROMUSDT",
|
|
189
|
-
"XPLUSDT",
|
|
190
|
-
"HBARUSDT",
|
|
191
|
-
"ERAUSDT",
|
|
192
|
-
"BCHUSDT",
|
|
193
|
-
"SOXLBUSDT",
|
|
194
|
-
"DOTUSDT",
|
|
195
|
-
"EPICUSDT",
|
|
196
|
-
"KITEUSDT",
|
|
197
|
-
"NFPUSDT",
|
|
198
|
-
"KGSTUSDT",
|
|
199
|
-
"FILUSDT",
|
|
200
|
-
"FETUSDT",
|
|
201
|
-
"KORUBUSDT",
|
|
202
|
-
"ARBUSDT",
|
|
203
|
-
"STXUSDT",
|
|
204
|
-
"HEIUSDT",
|
|
205
|
-
"BARDUSDT",
|
|
206
|
-
"DASHUSDT",
|
|
207
|
-
"ASTERUSDT",
|
|
208
|
-
"VANRYUSDT",
|
|
209
|
-
"INTCBUSDT",
|
|
210
|
-
"ETCUSDT",
|
|
211
|
-
"EIGENUSDT",
|
|
212
|
-
"PENGUUSDT",
|
|
213
|
-
"LRCUSDT",
|
|
214
|
-
"VIRTUALUSDT",
|
|
215
|
-
"POLUSDT",
|
|
216
|
-
"ICPUSDT",
|
|
217
|
-
"ZBTUSDT",
|
|
218
|
-
"SKHYBUSDT",
|
|
219
|
-
"SHIBUSDT",
|
|
220
|
-
"LDOUSDT",
|
|
221
|
-
"APTUSDT",
|
|
222
|
-
"JTOUSDT",
|
|
223
|
-
"NOMUSDT",
|
|
224
|
-
"ZROUSDT",
|
|
225
|
-
"OPUSDT",
|
|
226
|
-
"BONKUSDT",
|
|
227
|
-
"SKLUSDT",
|
|
228
|
-
"HEMIUSDT",
|
|
229
|
-
"TNSRUSDT",
|
|
230
|
-
"INTWBUSDT",
|
|
231
|
-
"BIOUSDT",
|
|
232
|
-
"TIAUSDT",
|
|
233
|
-
"ORDIUSDT",
|
|
234
|
-
"XUSDUSDT"
|
|
235
|
-
]
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
};
|
|
239
|
-
|
|
240
|
-
// src/binanceBreadthUniverses.ts
|
|
241
|
-
var BINANCE_BREADTH_UNIVERSE_KEYS = [
|
|
242
|
-
"top5",
|
|
243
|
-
"top10",
|
|
244
|
-
"top30",
|
|
245
|
-
"top50",
|
|
246
|
-
"top100"
|
|
247
|
-
];
|
|
248
|
-
var fingerprint = (value, length) => createHash("sha256").update(JSON.stringify(value)).digest("hex").slice(0, length);
|
|
249
|
-
var normalizeSymbols = (symbols) => symbols.map((symbol) => symbol.trim().toUpperCase()).filter(Boolean);
|
|
250
|
-
var buildBinanceBreadthUniverseSnapshot = ({
|
|
251
|
-
rankedSymbols,
|
|
252
|
-
updatedAt = (/* @__PURE__ */ new Date()).toISOString()
|
|
253
|
-
}) => {
|
|
254
|
-
const uniqueSymbols = [...new Set(normalizeSymbols(rankedSymbols))];
|
|
255
|
-
if (uniqueSymbols.length < 100) {
|
|
256
|
-
throw new Error(
|
|
257
|
-
`Binance breadth snapshot requires at least 100 symbols, got ${uniqueSymbols.length}`
|
|
258
|
-
);
|
|
259
|
-
}
|
|
260
|
-
const universes = Object.fromEntries(
|
|
261
|
-
BINANCE_BREADTH_UNIVERSE_KEYS.map((key) => {
|
|
262
|
-
const size = Number(key.slice(3));
|
|
263
|
-
const symbols = uniqueSymbols.slice(0, size);
|
|
264
|
-
return [
|
|
265
|
-
key,
|
|
266
|
-
{
|
|
267
|
-
size,
|
|
268
|
-
fingerprint: fingerprint(symbols, 12),
|
|
269
|
-
symbols
|
|
270
|
-
}
|
|
271
|
-
];
|
|
272
|
-
})
|
|
273
|
-
);
|
|
274
|
-
return {
|
|
275
|
-
schemaVersion: 1,
|
|
276
|
-
updatedAt,
|
|
277
|
-
source: "binance_spot_usdt_turnover24h",
|
|
278
|
-
fingerprint: fingerprint(universes, 16),
|
|
279
|
-
universes
|
|
280
|
-
};
|
|
281
|
-
};
|
|
282
|
-
var validateSnapshot = (value) => {
|
|
283
|
-
const rebuilt = buildBinanceBreadthUniverseSnapshot({
|
|
284
|
-
rankedSymbols: value.universes.top100.symbols,
|
|
285
|
-
updatedAt: value.updatedAt
|
|
286
|
-
});
|
|
287
|
-
if (value.schemaVersion !== 1 || value.source !== rebuilt.source || value.fingerprint !== rebuilt.fingerprint) {
|
|
288
|
-
throw new Error("Invalid Binance breadth universe snapshot fingerprint");
|
|
289
|
-
}
|
|
290
|
-
for (const key of BINANCE_BREADTH_UNIVERSE_KEYS) {
|
|
291
|
-
const actual = value.universes[key];
|
|
292
|
-
const expected = rebuilt.universes[key];
|
|
293
|
-
if (actual.size !== expected.size || actual.fingerprint !== expected.fingerprint || JSON.stringify(actual.symbols) !== JSON.stringify(expected.symbols)) {
|
|
294
|
-
throw new Error(`Invalid Binance breadth universe snapshot: ${key}`);
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
return value;
|
|
298
|
-
};
|
|
299
|
-
var snapshot = validateSnapshot(binanceBreadthUniverses_default);
|
|
300
|
-
var getBinanceBreadthUniverseSnapshot = () => snapshot;
|
|
301
|
-
var getBinanceBreadthUniverses = () => BINANCE_BREADTH_UNIVERSE_KEYS.map((key) => {
|
|
302
|
-
const definition = snapshot.universes[key];
|
|
303
|
-
return {
|
|
304
|
-
key,
|
|
305
|
-
size: definition.size,
|
|
306
|
-
fingerprint: definition.fingerprint,
|
|
307
|
-
universe: `binance_${key}_usdt_${definition.fingerprint}`,
|
|
308
|
-
symbols: [...definition.symbols]
|
|
309
|
-
};
|
|
310
|
-
});
|
|
311
|
-
var getPrimaryBinanceBreadthUniverse = () => getBinanceBreadthUniverses().find(({ key }) => key === "top30");
|
|
312
|
-
|
|
313
|
-
// src/strategyHelpers/binanceMarketContext.ts
|
|
314
|
-
import {
|
|
315
|
-
getLatestMarketBreadth,
|
|
316
|
-
getLatestMarketTradeFlow
|
|
317
|
-
} from "@tradejs/infra/timescale";
|
|
318
|
-
import { logger } from "@tradejs/infra/logger";
|
|
319
|
-
import { refreshSignalBaseContextGateFeatures } from "@tradejs/core/strategies";
|
|
320
|
-
|
|
321
|
-
// src/strategyHelpers/marketContextErrors.ts
|
|
322
|
-
var MARKET_CONTEXT_CANCELLATION_ERROR_NAMES = /* @__PURE__ */ new Set([
|
|
323
|
-
"AbortError",
|
|
324
|
-
"TimescaleQueryTimeoutError"
|
|
325
|
-
]);
|
|
326
|
-
var isMarketContextCancellationError = (error, abortSignal) => abortSignal?.aborted === true || error instanceof Error && MARKET_CONTEXT_CANCELLATION_ERROR_NAMES.has(error.name);
|
|
327
|
-
|
|
328
|
-
// src/strategyHelpers/binanceMarketContext.ts
|
|
329
|
-
var DEFAULT_MAX_AGE_BY_INTERVAL = {
|
|
330
|
-
"1m": 3 * 6e4,
|
|
331
|
-
"5m": 10 * 6e4,
|
|
332
|
-
"15m": 30 * 6e4,
|
|
333
|
-
"1h": 2 * 60 * 6e4
|
|
334
|
-
};
|
|
335
|
-
var binanceMarketContextUnavailable = false;
|
|
336
|
-
var referenceRowsCache = /* @__PURE__ */ new Map();
|
|
337
|
-
var breadthCache = /* @__PURE__ */ new Map();
|
|
338
|
-
var parseEnabledFlag = (value, env) => {
|
|
339
|
-
const normalized = String(value ?? "").trim().toLowerCase();
|
|
340
|
-
if (!normalized)
|
|
341
|
-
return env === "BACKTEST" || env === "CRON" || env === "PARITY";
|
|
342
|
-
if (["1", "true", "yes", "on"].includes(normalized)) return true;
|
|
343
|
-
if (["0", "false", "no", "off"].includes(normalized)) return false;
|
|
344
|
-
if (normalized === "backtest") return env === "BACKTEST";
|
|
345
|
-
if (normalized === "live") return env !== "BACKTEST";
|
|
346
|
-
return false;
|
|
347
|
-
};
|
|
348
|
-
var toFiniteNumberOrNull = (value) => {
|
|
349
|
-
const numeric = typeof value === "number" ? value : Number(value);
|
|
350
|
-
return Number.isFinite(numeric) ? numeric : null;
|
|
351
|
-
};
|
|
352
|
-
var signalIntervalToMarketInterval = (value) => {
|
|
353
|
-
const normalized = String(value).trim().toLowerCase();
|
|
354
|
-
if (normalized === "1" || normalized === "1m") return "1m";
|
|
355
|
-
if (normalized === "5" || normalized === "5m") return "5m";
|
|
356
|
-
if (normalized === "60" || normalized === "1h") return "1h";
|
|
357
|
-
return "15m";
|
|
358
|
-
};
|
|
359
|
-
var resolveMarketInterval = (signal, override) => override ?? signalIntervalToMarketInterval(signal.interval);
|
|
360
|
-
var getReferenceSymbols = () => {
|
|
361
|
-
const symbols = (process.env.BINANCE_MARKET_CONTEXT_REFERENCE_SYMBOLS || "BTCUSDT,ETHUSDT").split(",").map((item) => item.trim().toUpperCase()).filter(Boolean);
|
|
362
|
-
return symbols.length ? [...new Set(symbols)] : ["BTCUSDT", "ETHUSDT"];
|
|
363
|
-
};
|
|
364
|
-
var resolvePrimaryReferenceSymbol = (signalSymbol) => {
|
|
365
|
-
const symbol = signalSymbol.trim().toUpperCase();
|
|
366
|
-
const referenceSymbols = getReferenceSymbols();
|
|
367
|
-
return referenceSymbols.includes(symbol) ? symbol : referenceSymbols[0];
|
|
368
|
-
};
|
|
369
|
-
var hasBaseContext = (signal) => Boolean(
|
|
370
|
-
signal.additionalIndicators?.baseContext && typeof signal.additionalIndicators.baseContext === "object" && !Array.isArray(signal.additionalIndicators.baseContext)
|
|
371
|
-
);
|
|
372
|
-
var isBinanceMarketContextEnabled = (env) => parseEnabledFlag(process.env.BINANCE_MARKET_CONTEXT_ENABLED, env);
|
|
373
|
-
var toTradeFlowContext = (row, interval) => row ? {
|
|
374
|
-
source: "binance_agg_trades",
|
|
375
|
-
interval,
|
|
376
|
-
asOfTs: row.ts.getTime(),
|
|
377
|
-
ageMs: row.ageMs,
|
|
378
|
-
stale: row.stale,
|
|
379
|
-
trades: toFiniteNumberOrNull(row.trades),
|
|
380
|
-
buyPressurePct: toFiniteNumberOrNull(row.buyPressurePct),
|
|
381
|
-
buyBaseVolume: toFiniteNumberOrNull(row.buyBaseVolume),
|
|
382
|
-
sellBaseVolume: toFiniteNumberOrNull(row.sellBaseVolume),
|
|
383
|
-
buyQuoteVolume: toFiniteNumberOrNull(row.buyQuoteVolume),
|
|
384
|
-
sellQuoteVolume: toFiniteNumberOrNull(row.sellQuoteVolume),
|
|
385
|
-
netBaseDelta: toFiniteNumberOrNull(row.netBaseDelta),
|
|
386
|
-
netQuoteDelta: toFiniteNumberOrNull(row.netQuoteDelta)
|
|
387
|
-
} : null;
|
|
388
|
-
var getCachedReferenceRows = ({
|
|
389
|
-
referenceSymbols,
|
|
390
|
-
interval,
|
|
391
|
-
timestamp,
|
|
392
|
-
maxAgeMs,
|
|
393
|
-
abortSignal
|
|
394
|
-
}) => {
|
|
395
|
-
const key = `${referenceSymbols.join(",")}:${interval}:${timestamp}:${maxAgeMs}`;
|
|
396
|
-
const cached = referenceRowsCache.get(key);
|
|
397
|
-
if (cached) return cached;
|
|
398
|
-
const promise = Promise.all(
|
|
399
|
-
referenceSymbols.map(async (symbol) => {
|
|
400
|
-
const tradeFlow = await getLatestMarketTradeFlow({
|
|
401
|
-
symbol,
|
|
402
|
-
interval,
|
|
403
|
-
atMs: timestamp,
|
|
404
|
-
maxAgeMs,
|
|
405
|
-
...abortSignal ? { signal: abortSignal } : {}
|
|
406
|
-
});
|
|
407
|
-
return {
|
|
408
|
-
symbol,
|
|
409
|
-
tradeFlow: toTradeFlowContext(tradeFlow, interval)
|
|
410
|
-
};
|
|
411
|
-
})
|
|
412
|
-
);
|
|
413
|
-
referenceRowsCache.set(key, promise);
|
|
414
|
-
void promise.catch(() => referenceRowsCache.delete(key));
|
|
415
|
-
return promise;
|
|
416
|
-
};
|
|
417
|
-
var getCachedBreadth = ({
|
|
418
|
-
breadthUniverse,
|
|
419
|
-
interval,
|
|
420
|
-
timestamp,
|
|
421
|
-
maxAgeMs,
|
|
422
|
-
abortSignal
|
|
423
|
-
}) => {
|
|
424
|
-
const key = `${breadthUniverse}:${interval}:${timestamp}:${maxAgeMs}`;
|
|
425
|
-
const cached = breadthCache.get(key);
|
|
426
|
-
if (cached) return cached;
|
|
427
|
-
const promise = getLatestMarketBreadth({
|
|
428
|
-
universe: breadthUniverse,
|
|
429
|
-
interval,
|
|
430
|
-
atMs: timestamp,
|
|
431
|
-
maxAgeMs,
|
|
432
|
-
...abortSignal ? { signal: abortSignal } : {}
|
|
433
|
-
});
|
|
434
|
-
breadthCache.set(key, promise);
|
|
435
|
-
void promise.catch(() => breadthCache.delete(key));
|
|
436
|
-
return promise;
|
|
437
|
-
};
|
|
438
|
-
var toMarketBreadthContext = (breadth, interval) => ({
|
|
439
|
-
source: "binance_klines",
|
|
440
|
-
universe: breadth.universe,
|
|
441
|
-
interval,
|
|
442
|
-
asOfTs: breadth.ts.getTime(),
|
|
443
|
-
ageMs: breadth.ageMs,
|
|
444
|
-
stale: breadth.stale,
|
|
445
|
-
symbolsCount: toFiniteNumberOrNull(breadth.symbolsCount),
|
|
446
|
-
advancers: toFiniteNumberOrNull(breadth.advancers),
|
|
447
|
-
decliners: toFiniteNumberOrNull(breadth.decliners),
|
|
448
|
-
unchanged: toFiniteNumberOrNull(breadth.unchanged),
|
|
449
|
-
advanceDeclineRatio: toFiniteNumberOrNull(breadth.advanceDeclineRatio),
|
|
450
|
-
pctAboveMa20: toFiniteNumberOrNull(breadth.pctAboveMa20),
|
|
451
|
-
pctAboveMa50: toFiniteNumberOrNull(breadth.pctAboveMa50),
|
|
452
|
-
equalWeightedReturn: toFiniteNumberOrNull(breadth.equalWeightedReturn),
|
|
453
|
-
volumeWeightedReturn: toFiniteNumberOrNull(breadth.volumeWeightedReturn),
|
|
454
|
-
dispersion: toFiniteNumberOrNull(breadth.dispersion)
|
|
455
|
-
});
|
|
456
|
-
var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
457
|
-
const {
|
|
458
|
-
signal,
|
|
459
|
-
env,
|
|
460
|
-
enabled = isBinanceMarketContextEnabled(env),
|
|
461
|
-
interval = resolveMarketInterval(signal, params.interval),
|
|
462
|
-
maxAgeMs = DEFAULT_MAX_AGE_BY_INTERVAL[interval]
|
|
463
|
-
} = params;
|
|
464
|
-
if (signal.universe === "tradfi" || !enabled || binanceMarketContextUnavailable || !hasBaseContext(signal)) {
|
|
465
|
-
return false;
|
|
466
|
-
}
|
|
467
|
-
try {
|
|
468
|
-
const referenceSymbols = getReferenceSymbols();
|
|
469
|
-
const primaryReferenceSymbol = resolvePrimaryReferenceSymbol(signal.symbol);
|
|
470
|
-
const breadthUniverses = params.breadthUniverse ? [
|
|
471
|
-
{
|
|
472
|
-
key: "top30",
|
|
473
|
-
universe: params.breadthUniverse
|
|
474
|
-
}
|
|
475
|
-
] : getBinanceBreadthUniverses();
|
|
476
|
-
const [referenceRows, breadthRows] = await Promise.all([
|
|
477
|
-
getCachedReferenceRows({
|
|
478
|
-
referenceSymbols,
|
|
479
|
-
interval,
|
|
480
|
-
timestamp: signal.timestamp,
|
|
481
|
-
maxAgeMs,
|
|
482
|
-
abortSignal: params.abortSignal
|
|
483
|
-
}),
|
|
484
|
-
Promise.all(
|
|
485
|
-
breadthUniverses.map(async ({ key, universe }) => ({
|
|
486
|
-
key,
|
|
487
|
-
breadth: await getCachedBreadth({
|
|
488
|
-
breadthUniverse: universe,
|
|
489
|
-
interval,
|
|
490
|
-
timestamp: signal.timestamp,
|
|
491
|
-
maxAgeMs,
|
|
492
|
-
abortSignal: params.abortSignal
|
|
493
|
-
})
|
|
494
|
-
}))
|
|
495
|
-
)
|
|
496
|
-
]);
|
|
497
|
-
const availableBreadths = breadthRows.filter(
|
|
498
|
-
(row) => row.breadth != null
|
|
499
|
-
);
|
|
500
|
-
const marketBreadths = Object.fromEntries(
|
|
501
|
-
availableBreadths.map(({ key, breadth }) => [
|
|
502
|
-
key,
|
|
503
|
-
toMarketBreadthContext(breadth, interval)
|
|
504
|
-
])
|
|
505
|
-
);
|
|
506
|
-
const primaryBreadth = availableBreadths.find(
|
|
507
|
-
({ key }) => key === "top30"
|
|
508
|
-
)?.breadth;
|
|
509
|
-
const tradeFlowBySymbol = Object.fromEntries(
|
|
510
|
-
referenceRows.filter((row) => row.tradeFlow).map((row) => [row.symbol, row.tradeFlow])
|
|
511
|
-
);
|
|
512
|
-
const targetReferenceSymbol = signal.symbol.trim().toUpperCase();
|
|
513
|
-
const targetTradeFlow = tradeFlowBySymbol[targetReferenceSymbol];
|
|
514
|
-
if (!Object.keys(tradeFlowBySymbol).length && !availableBreadths.length) {
|
|
515
|
-
return false;
|
|
516
|
-
}
|
|
517
|
-
const baseContext = signal.additionalIndicators.baseContext;
|
|
518
|
-
signal.additionalIndicators = {
|
|
519
|
-
...signal.additionalIndicators,
|
|
520
|
-
baseContext: {
|
|
521
|
-
...baseContext,
|
|
522
|
-
participation: {
|
|
523
|
-
...baseContext.participation,
|
|
524
|
-
...targetTradeFlow ? {
|
|
525
|
-
tradeFlow: targetTradeFlow
|
|
526
|
-
} : {}
|
|
527
|
-
},
|
|
528
|
-
relative: {
|
|
529
|
-
...baseContext.relative,
|
|
530
|
-
execution: {
|
|
531
|
-
...baseContext.relative.execution
|
|
532
|
-
},
|
|
533
|
-
...Object.keys(tradeFlowBySymbol).length ? {
|
|
534
|
-
referenceTradeFlow: {
|
|
535
|
-
source: "binance_reference_market",
|
|
536
|
-
primaryReferenceSymbol,
|
|
537
|
-
referenceSymbols,
|
|
538
|
-
tradeFlowBySymbol
|
|
539
|
-
}
|
|
540
|
-
} : {},
|
|
541
|
-
...availableBreadths.length ? {
|
|
542
|
-
marketBreadths,
|
|
543
|
-
...primaryBreadth ? {
|
|
544
|
-
marketBreadth: toMarketBreadthContext(
|
|
545
|
-
primaryBreadth,
|
|
546
|
-
interval
|
|
547
|
-
),
|
|
548
|
-
btcAltRegime: {
|
|
549
|
-
source: "binance_klines",
|
|
550
|
-
universe: primaryBreadth.universe,
|
|
551
|
-
interval,
|
|
552
|
-
asOfTs: primaryBreadth.ts.getTime(),
|
|
553
|
-
ageMs: primaryBreadth.ageMs,
|
|
554
|
-
stale: primaryBreadth.stale,
|
|
555
|
-
btcReturn1h: toFiniteNumberOrNull(
|
|
556
|
-
primaryBreadth.btcReturn1h
|
|
557
|
-
),
|
|
558
|
-
btcReturn4h: toFiniteNumberOrNull(
|
|
559
|
-
primaryBreadth.btcReturn4h
|
|
560
|
-
),
|
|
561
|
-
btcReturn24h: toFiniteNumberOrNull(
|
|
562
|
-
primaryBreadth.btcReturn24h
|
|
563
|
-
),
|
|
564
|
-
altBasketReturn1h: toFiniteNumberOrNull(
|
|
565
|
-
primaryBreadth.altBasketReturn1h
|
|
566
|
-
),
|
|
567
|
-
altBasketReturn4h: toFiniteNumberOrNull(
|
|
568
|
-
primaryBreadth.altBasketReturn4h
|
|
569
|
-
),
|
|
570
|
-
altBasketReturn24h: toFiniteNumberOrNull(
|
|
571
|
-
primaryBreadth.altBasketReturn24h
|
|
572
|
-
),
|
|
573
|
-
btcVsAltReturn1h: toFiniteNumberOrNull(
|
|
574
|
-
primaryBreadth.btcVsAltReturn1h
|
|
575
|
-
),
|
|
576
|
-
btcVsAltReturn4h: toFiniteNumberOrNull(
|
|
577
|
-
primaryBreadth.btcVsAltReturn4h
|
|
578
|
-
),
|
|
579
|
-
btcVsAltReturn24h: toFiniteNumberOrNull(
|
|
580
|
-
primaryBreadth.btcVsAltReturn24h
|
|
581
|
-
),
|
|
582
|
-
btcTurnoverShare1h: toFiniteNumberOrNull(
|
|
583
|
-
primaryBreadth.btcTurnoverShare1h
|
|
584
|
-
),
|
|
585
|
-
btcTurnoverShare24h: toFiniteNumberOrNull(
|
|
586
|
-
primaryBreadth.btcTurnoverShare24h
|
|
587
|
-
),
|
|
588
|
-
btcTurnoverShareChange24h: toFiniteNumberOrNull(
|
|
589
|
-
primaryBreadth.btcTurnoverShareChange24h
|
|
590
|
-
),
|
|
591
|
-
altVolToBtcVol24h: toFiniteNumberOrNull(
|
|
592
|
-
primaryBreadth.altVolToBtcVol24h
|
|
593
|
-
),
|
|
594
|
-
altDispersion24h: toFiniteNumberOrNull(
|
|
595
|
-
primaryBreadth.altDispersion24h
|
|
596
|
-
),
|
|
597
|
-
regime: primaryBreadth.btcAltRegime ?? "unknown"
|
|
598
|
-
}
|
|
599
|
-
} : {}
|
|
600
|
-
} : {}
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
};
|
|
604
|
-
refreshSignalBaseContextGateFeatures(signal);
|
|
605
|
-
return true;
|
|
606
|
-
} catch (error) {
|
|
607
|
-
if (isMarketContextCancellationError(error, params.abortSignal)) {
|
|
608
|
-
throw error;
|
|
609
|
-
}
|
|
610
|
-
binanceMarketContextUnavailable = true;
|
|
611
|
-
logger.warn(
|
|
612
|
-
"Binance market context disabled after Timescale read failure: %s",
|
|
613
|
-
String(error)
|
|
614
|
-
);
|
|
615
|
-
return false;
|
|
616
|
-
}
|
|
617
|
-
};
|
|
618
|
-
|
|
619
|
-
// src/strategyHelpers/coinMarketCapContext.ts
|
|
620
|
-
import { refreshSignalBaseContextGateFeatures as refreshSignalBaseContextGateFeatures2 } from "@tradejs/core/strategies";
|
|
621
|
-
import { logger as logger2 } from "@tradejs/infra/logger";
|
|
622
|
-
import {
|
|
623
|
-
getLatestMarketCmcExchangeLiquidityContext,
|
|
624
|
-
getLatestMarketCmcFearGreedContext,
|
|
625
|
-
getLatestMarketCmcIndexContexts,
|
|
626
|
-
getLatestMarketGlobalContext,
|
|
627
|
-
getLatestMarketReferenceAssetContexts
|
|
628
|
-
} from "@tradejs/infra/timescale";
|
|
629
|
-
var DEFAULT_MAX_AGE_MS = 48 * 60 * 6e4;
|
|
630
|
-
var SOURCE_GLOBAL_DAILY = "coinmarketcap_global";
|
|
631
|
-
var SOURCE_REFERENCE = "coinmarketcap_reference_asset";
|
|
632
|
-
var SOURCE_EXCHANGE_LIQUIDITY = "coinmarketcap_exchange_liquidity";
|
|
633
|
-
var SOURCE_FEAR_GREED = "coinmarketcap_fear_greed";
|
|
634
|
-
var SOURCE_INDEX = "coinmarketcap_index";
|
|
635
|
-
var DAY_MS = 864e5;
|
|
636
|
-
var coinMarketCapContextUnavailable = false;
|
|
637
|
-
var globalContextCache = /* @__PURE__ */ new Map();
|
|
638
|
-
var referenceContextCache = /* @__PURE__ */ new Map();
|
|
639
|
-
var exchangeLiquidityContextCache = /* @__PURE__ */ new Map();
|
|
640
|
-
var fearGreedContextCache = /* @__PURE__ */ new Map();
|
|
641
|
-
var indexContextCache = /* @__PURE__ */ new Map();
|
|
642
|
-
var parseEnabledFlag2 = (value, env) => {
|
|
643
|
-
const normalized = String(value ?? "").trim().toLowerCase();
|
|
644
|
-
if (!normalized) {
|
|
645
|
-
return env === "BACKTEST" || env === "PARITY" || env === "CRON";
|
|
646
|
-
}
|
|
647
|
-
if (["1", "true", "yes", "on"].includes(normalized)) return true;
|
|
648
|
-
if (["0", "false", "no", "off"].includes(normalized)) return false;
|
|
649
|
-
if (normalized === "backtest") return env === "BACKTEST";
|
|
650
|
-
if (normalized === "live") return env !== "BACKTEST";
|
|
651
|
-
return false;
|
|
652
|
-
};
|
|
653
|
-
var asInt = (value, fallback) => {
|
|
654
|
-
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
655
|
-
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
656
|
-
};
|
|
657
|
-
var toFiniteNumberOrNull2 = (value) => {
|
|
658
|
-
const numeric = typeof value === "number" ? value : typeof value === "string" && value.trim() ? Number(value) : Number.NaN;
|
|
659
|
-
return Number.isFinite(numeric) ? numeric : null;
|
|
660
|
-
};
|
|
661
|
-
var safeDivide = (numerator, denominator) => numerator != null && denominator != null && denominator > 0 ? numerator / denominator : null;
|
|
662
|
-
var hasBaseContext2 = (signal) => Boolean(
|
|
663
|
-
signal.additionalIndicators?.baseContext && typeof signal.additionalIndicators.baseContext === "object" && !Array.isArray(signal.additionalIndicators.baseContext)
|
|
664
|
-
);
|
|
665
|
-
var resolveMaxAgeMs = () => asInt(process.env.COINMARKETCAP_CONTEXT_MAX_AGE_MS, DEFAULT_MAX_AGE_MS);
|
|
666
|
-
var toAltLiquidityRegime = ({
|
|
667
|
-
stale,
|
|
668
|
-
btcDominanceChange24hPct,
|
|
669
|
-
altMarketCapChange24hPct,
|
|
670
|
-
altVolumeChange24hPct
|
|
671
|
-
}) => {
|
|
672
|
-
if (stale) return "unknown";
|
|
673
|
-
if (altMarketCapChange24hPct != null && altMarketCapChange24hPct <= -0.03 || altVolumeChange24hPct != null && altVolumeChange24hPct <= -0.15) {
|
|
674
|
-
return "risk_off";
|
|
675
|
-
}
|
|
676
|
-
if (btcDominanceChange24hPct != null && btcDominanceChange24hPct >= 0.25) {
|
|
677
|
-
return "btc_favored";
|
|
678
|
-
}
|
|
679
|
-
if (btcDominanceChange24hPct != null && btcDominanceChange24hPct <= -0.25 && (altMarketCapChange24hPct == null || altMarketCapChange24hPct >= 0)) {
|
|
680
|
-
return "alt_friendly";
|
|
681
|
-
}
|
|
682
|
-
return "neutral";
|
|
683
|
-
};
|
|
684
|
-
var toReferenceLiquidityRegime = ({
|
|
685
|
-
stale,
|
|
686
|
-
ethBtcMarketCapRatioChange24hPct,
|
|
687
|
-
ethVsBtcVolumeRatio
|
|
688
|
-
}) => {
|
|
689
|
-
if (stale) return "unknown";
|
|
690
|
-
if (ethVsBtcVolumeRatio != null && ethVsBtcVolumeRatio < 0.15) return "thin";
|
|
691
|
-
if (ethBtcMarketCapRatioChange24hPct != null && ethBtcMarketCapRatioChange24hPct >= 0.01) {
|
|
692
|
-
return "eth_led";
|
|
693
|
-
}
|
|
694
|
-
if (ethBtcMarketCapRatioChange24hPct != null && ethBtcMarketCapRatioChange24hPct <= -0.01) {
|
|
695
|
-
return "btc_led";
|
|
696
|
-
}
|
|
697
|
-
return "balanced";
|
|
698
|
-
};
|
|
699
|
-
var toExchangeLiquidityRegime = ({
|
|
700
|
-
stale,
|
|
701
|
-
totalVolumeChange24hPct,
|
|
702
|
-
fallback
|
|
703
|
-
}) => {
|
|
704
|
-
if (stale) return "unknown";
|
|
705
|
-
if (totalVolumeChange24hPct != null && totalVolumeChange24hPct >= 0.15) {
|
|
706
|
-
return "expanding";
|
|
707
|
-
}
|
|
708
|
-
if (totalVolumeChange24hPct != null && totalVolumeChange24hPct <= -0.15) {
|
|
709
|
-
return "contracting";
|
|
710
|
-
}
|
|
711
|
-
return fallback;
|
|
712
|
-
};
|
|
713
|
-
var toIndexRegime = ({
|
|
714
|
-
stale,
|
|
715
|
-
cmc100Change24hPct,
|
|
716
|
-
cmc20Change24hPct,
|
|
717
|
-
cmc20ToCmc100RatioChange24hPct
|
|
718
|
-
}) => {
|
|
719
|
-
if (stale) return "unknown";
|
|
720
|
-
if (cmc100Change24hPct == null && cmc20Change24hPct == null) {
|
|
721
|
-
return "unknown";
|
|
722
|
-
}
|
|
723
|
-
if ((cmc100Change24hPct ?? 0) <= -0.02 && (cmc20Change24hPct ?? 0) <= -0.02) {
|
|
724
|
-
return "risk_off";
|
|
725
|
-
}
|
|
726
|
-
if ((cmc20ToCmc100RatioChange24hPct ?? 0) >= 5e-3) {
|
|
727
|
-
return "top20_led";
|
|
728
|
-
}
|
|
729
|
-
if ((cmc20ToCmc100RatioChange24hPct ?? 0) <= -5e-3) {
|
|
730
|
-
return "large_cap_led";
|
|
731
|
-
}
|
|
732
|
-
return "balanced";
|
|
733
|
-
};
|
|
734
|
-
var getCachedGlobalContext = ({
|
|
735
|
-
timestamp,
|
|
736
|
-
maxAgeMs,
|
|
737
|
-
abortSignal
|
|
738
|
-
}) => {
|
|
739
|
-
const key = `${SOURCE_GLOBAL_DAILY}:${timestamp}:${maxAgeMs}`;
|
|
740
|
-
const cached = globalContextCache.get(key);
|
|
741
|
-
if (cached) return cached;
|
|
742
|
-
const promise = getLatestMarketGlobalContext({
|
|
743
|
-
source: SOURCE_GLOBAL_DAILY,
|
|
744
|
-
atMs: timestamp,
|
|
745
|
-
maxAgeMs,
|
|
746
|
-
...abortSignal ? { signal: abortSignal } : {}
|
|
747
|
-
});
|
|
748
|
-
globalContextCache.set(key, promise);
|
|
749
|
-
void promise.catch(() => globalContextCache.delete(key));
|
|
750
|
-
return promise;
|
|
751
|
-
};
|
|
752
|
-
var getCachedReferenceContexts = ({
|
|
753
|
-
timestamp,
|
|
754
|
-
maxAgeMs,
|
|
755
|
-
abortSignal
|
|
756
|
-
}) => {
|
|
757
|
-
const key = `${SOURCE_REFERENCE}:1d:${timestamp}:${maxAgeMs}`;
|
|
758
|
-
const cached = referenceContextCache.get(key);
|
|
759
|
-
if (cached) return cached;
|
|
760
|
-
const promise = getLatestMarketReferenceAssetContexts({
|
|
761
|
-
source: SOURCE_REFERENCE,
|
|
762
|
-
symbols: ["BTCUSDT", "ETHUSDT"],
|
|
763
|
-
interval: "1d",
|
|
764
|
-
atMs: timestamp,
|
|
765
|
-
maxAgeMs,
|
|
766
|
-
...abortSignal ? { signal: abortSignal } : {}
|
|
767
|
-
});
|
|
768
|
-
referenceContextCache.set(key, promise);
|
|
769
|
-
void promise.catch(() => referenceContextCache.delete(key));
|
|
770
|
-
return promise;
|
|
771
|
-
};
|
|
772
|
-
var getCachedExchangeLiquidityContext = ({
|
|
773
|
-
timestamp,
|
|
774
|
-
maxAgeMs,
|
|
775
|
-
abortSignal
|
|
776
|
-
}) => {
|
|
777
|
-
const key = `${SOURCE_EXCHANGE_LIQUIDITY}:1d:${timestamp}:${maxAgeMs}`;
|
|
778
|
-
const cached = exchangeLiquidityContextCache.get(key);
|
|
779
|
-
if (cached) return cached;
|
|
780
|
-
const promise = getLatestMarketCmcExchangeLiquidityContext({
|
|
781
|
-
source: SOURCE_EXCHANGE_LIQUIDITY,
|
|
782
|
-
interval: "1d",
|
|
783
|
-
atMs: timestamp,
|
|
784
|
-
maxAgeMs,
|
|
785
|
-
...abortSignal ? { signal: abortSignal } : {}
|
|
786
|
-
});
|
|
787
|
-
exchangeLiquidityContextCache.set(key, promise);
|
|
788
|
-
void promise.catch(() => exchangeLiquidityContextCache.delete(key));
|
|
789
|
-
return promise;
|
|
790
|
-
};
|
|
791
|
-
var getCachedFearGreedContext = ({
|
|
792
|
-
timestamp,
|
|
793
|
-
maxAgeMs,
|
|
794
|
-
abortSignal
|
|
795
|
-
}) => {
|
|
796
|
-
const key = `${SOURCE_FEAR_GREED}:1d:${timestamp}:${maxAgeMs}`;
|
|
797
|
-
const cached = fearGreedContextCache.get(key);
|
|
798
|
-
if (cached) return cached;
|
|
799
|
-
const promise = getLatestMarketCmcFearGreedContext({
|
|
800
|
-
source: SOURCE_FEAR_GREED,
|
|
801
|
-
interval: "1d",
|
|
802
|
-
atMs: timestamp,
|
|
803
|
-
maxAgeMs,
|
|
804
|
-
...abortSignal ? { signal: abortSignal } : {}
|
|
805
|
-
});
|
|
806
|
-
fearGreedContextCache.set(key, promise);
|
|
807
|
-
void promise.catch(() => fearGreedContextCache.delete(key));
|
|
808
|
-
return promise;
|
|
809
|
-
};
|
|
810
|
-
var getCachedIndexContexts = ({
|
|
811
|
-
timestamp,
|
|
812
|
-
maxAgeMs,
|
|
813
|
-
abortSignal
|
|
814
|
-
}) => {
|
|
815
|
-
const key = `${SOURCE_INDEX}:1d:${timestamp}:${maxAgeMs}`;
|
|
816
|
-
const cached = indexContextCache.get(key);
|
|
817
|
-
if (cached) return cached;
|
|
818
|
-
const promise = getLatestMarketCmcIndexContexts({
|
|
819
|
-
source: SOURCE_INDEX,
|
|
820
|
-
indexSlugs: ["cmc100", "cmc20"],
|
|
821
|
-
interval: "1d",
|
|
822
|
-
atMs: timestamp,
|
|
823
|
-
maxAgeMs,
|
|
824
|
-
...abortSignal ? { signal: abortSignal } : {}
|
|
825
|
-
});
|
|
826
|
-
indexContextCache.set(key, promise);
|
|
827
|
-
void promise.catch(() => indexContextCache.delete(key));
|
|
828
|
-
return promise;
|
|
829
|
-
};
|
|
830
|
-
var isCoinMarketCapContextEnabled = (env) => parseEnabledFlag2(process.env.COINMARKETCAP_CONTEXT_ENABLED, env);
|
|
831
|
-
var enrichSignalWithCoinMarketCapContext = async (params) => {
|
|
832
|
-
const {
|
|
833
|
-
signal,
|
|
834
|
-
env,
|
|
835
|
-
enabled = isCoinMarketCapContextEnabled(env),
|
|
836
|
-
maxAgeMs = resolveMaxAgeMs()
|
|
837
|
-
} = params;
|
|
838
|
-
if (signal.universe === "tradfi" || !enabled || coinMarketCapContextUnavailable || !hasBaseContext2(signal)) {
|
|
839
|
-
return false;
|
|
840
|
-
}
|
|
841
|
-
try {
|
|
842
|
-
const [
|
|
843
|
-
globalDailyRow,
|
|
844
|
-
dailyReferences,
|
|
845
|
-
previousDailyReferences,
|
|
846
|
-
exchangeLiquidityRow,
|
|
847
|
-
fearGreedRow,
|
|
848
|
-
indexRows
|
|
849
|
-
] = await Promise.all([
|
|
850
|
-
getCachedGlobalContext({
|
|
851
|
-
timestamp: signal.timestamp,
|
|
852
|
-
maxAgeMs,
|
|
853
|
-
abortSignal: params.abortSignal
|
|
854
|
-
}),
|
|
855
|
-
getCachedReferenceContexts({
|
|
856
|
-
timestamp: signal.timestamp,
|
|
857
|
-
maxAgeMs,
|
|
858
|
-
abortSignal: params.abortSignal
|
|
859
|
-
}),
|
|
860
|
-
getCachedReferenceContexts({
|
|
861
|
-
timestamp: signal.timestamp - DAY_MS,
|
|
862
|
-
maxAgeMs: maxAgeMs + DAY_MS,
|
|
863
|
-
abortSignal: params.abortSignal
|
|
864
|
-
}),
|
|
865
|
-
getCachedExchangeLiquidityContext({
|
|
866
|
-
timestamp: signal.timestamp,
|
|
867
|
-
maxAgeMs,
|
|
868
|
-
abortSignal: params.abortSignal
|
|
869
|
-
}),
|
|
870
|
-
getCachedFearGreedContext({
|
|
871
|
-
timestamp: signal.timestamp,
|
|
872
|
-
maxAgeMs,
|
|
873
|
-
abortSignal: params.abortSignal
|
|
874
|
-
}),
|
|
875
|
-
getCachedIndexContexts({
|
|
876
|
-
timestamp: signal.timestamp,
|
|
877
|
-
maxAgeMs,
|
|
878
|
-
abortSignal: params.abortSignal
|
|
879
|
-
})
|
|
880
|
-
]);
|
|
881
|
-
const globalRow = globalDailyRow;
|
|
882
|
-
const references = dailyReferences;
|
|
883
|
-
const previousReferences = previousDailyReferences;
|
|
884
|
-
if (!globalRow && !references.size && !exchangeLiquidityRow && !fearGreedRow && !indexRows.size) {
|
|
885
|
-
return false;
|
|
886
|
-
}
|
|
887
|
-
const btcRow = references.get("BTCUSDT") ?? null;
|
|
888
|
-
const ethRow = references.get("ETHUSDT") ?? null;
|
|
889
|
-
const previousBtcRow = previousReferences.get("BTCUSDT") ?? null;
|
|
890
|
-
const previousEthRow = previousReferences.get("ETHUSDT") ?? null;
|
|
891
|
-
const btcMarketCapUsd = toFiniteNumberOrNull2(btcRow?.marketCapUsd);
|
|
892
|
-
const ethMarketCapUsd = toFiniteNumberOrNull2(ethRow?.marketCapUsd);
|
|
893
|
-
const previousBtcMarketCapUsd = toFiniteNumberOrNull2(
|
|
894
|
-
previousBtcRow?.marketCapUsd
|
|
895
|
-
);
|
|
896
|
-
const previousEthMarketCapUsd = toFiniteNumberOrNull2(
|
|
897
|
-
previousEthRow?.marketCapUsd
|
|
898
|
-
);
|
|
899
|
-
const ethBtcMarketCapRatio = safeDivide(ethMarketCapUsd, btcMarketCapUsd);
|
|
900
|
-
const previousEthBtcMarketCapRatio = safeDivide(
|
|
901
|
-
previousEthMarketCapUsd,
|
|
902
|
-
previousBtcMarketCapUsd
|
|
903
|
-
);
|
|
904
|
-
const ethBtcMarketCapRatioChange24hPct = ethBtcMarketCapRatio != null && previousEthBtcMarketCapRatio != null && previousEthBtcMarketCapRatio > 0 ? (ethBtcMarketCapRatio - previousEthBtcMarketCapRatio) / previousEthBtcMarketCapRatio : null;
|
|
905
|
-
const btcVolumeUsd = toFiniteNumberOrNull2(btcRow?.volumeUsd);
|
|
906
|
-
const ethVolumeUsd = toFiniteNumberOrNull2(ethRow?.volumeUsd);
|
|
907
|
-
const referenceStale = btcRow?.stale === true || ethRow?.stale === true || !btcRow || !ethRow;
|
|
908
|
-
const btcDominanceChange24hPct = toFiniteNumberOrNull2(
|
|
909
|
-
globalRow?.btcDominanceChange24hPct
|
|
910
|
-
);
|
|
911
|
-
const altMarketCapChange24hPct = toFiniteNumberOrNull2(
|
|
912
|
-
globalRow?.altMarketCapChange24hPct
|
|
913
|
-
);
|
|
914
|
-
const altVolumeChange24hPct = toFiniteNumberOrNull2(
|
|
915
|
-
globalRow?.altVolumeChange24hPct
|
|
916
|
-
);
|
|
917
|
-
const altLiquidityRegime = globalRow ? toAltLiquidityRegime({
|
|
918
|
-
stale: globalRow.stale,
|
|
919
|
-
btcDominanceChange24hPct,
|
|
920
|
-
altMarketCapChange24hPct,
|
|
921
|
-
altVolumeChange24hPct
|
|
922
|
-
}) : "unknown";
|
|
923
|
-
const exchangeLiquidityRegime = exchangeLiquidityRow ? toExchangeLiquidityRegime({
|
|
924
|
-
stale: exchangeLiquidityRow.stale,
|
|
925
|
-
totalVolumeChange24hPct: toFiniteNumberOrNull2(
|
|
926
|
-
exchangeLiquidityRow.totalVolumeChange24hPct
|
|
927
|
-
),
|
|
928
|
-
fallback: exchangeLiquidityRow.liquidityRegime ?? "unknown"
|
|
929
|
-
}) : "unknown";
|
|
930
|
-
const cmc100Row = indexRows.get("cmc100") ?? null;
|
|
931
|
-
const cmc20Row = indexRows.get("cmc20") ?? null;
|
|
932
|
-
const cmc100Value = toFiniteNumberOrNull2(cmc100Row?.value);
|
|
933
|
-
const cmc20Value = toFiniteNumberOrNull2(cmc20Row?.value);
|
|
934
|
-
const cmc100Change24hPct = toFiniteNumberOrNull2(
|
|
935
|
-
cmc100Row?.valueChange24hPct
|
|
936
|
-
);
|
|
937
|
-
const cmc20Change24hPct = toFiniteNumberOrNull2(cmc20Row?.valueChange24hPct);
|
|
938
|
-
const cmc20ToCmc100Ratio = safeDivide(cmc20Value, cmc100Value);
|
|
939
|
-
const cmc20ToCmc100RatioChange24hPct = cmc20Change24hPct != null && cmc100Change24hPct != null ? (1 + cmc20Change24hPct) / (1 + cmc100Change24hPct) - 1 : null;
|
|
940
|
-
const indexStale = cmc100Row?.stale === true || cmc20Row?.stale === true || !cmc100Row || !cmc20Row;
|
|
941
|
-
const indexRegime = toIndexRegime({
|
|
942
|
-
stale: indexStale,
|
|
943
|
-
cmc100Change24hPct,
|
|
944
|
-
cmc20Change24hPct,
|
|
945
|
-
cmc20ToCmc100RatioChange24hPct
|
|
946
|
-
});
|
|
947
|
-
const baseContext = signal.additionalIndicators.baseContext;
|
|
948
|
-
signal.additionalIndicators = {
|
|
949
|
-
...signal.additionalIndicators,
|
|
950
|
-
baseContext: {
|
|
951
|
-
...baseContext,
|
|
952
|
-
relative: {
|
|
953
|
-
...baseContext.relative,
|
|
954
|
-
...globalRow ? {
|
|
955
|
-
cmcGlobal: {
|
|
956
|
-
source: globalRow.source,
|
|
957
|
-
interval: "1d",
|
|
958
|
-
asOfTs: globalRow.ts.getTime(),
|
|
959
|
-
ageMs: globalRow.ageMs,
|
|
960
|
-
stale: globalRow.stale,
|
|
961
|
-
totalMarketCapUsd: toFiniteNumberOrNull2(
|
|
962
|
-
globalRow.totalMarketCapUsd
|
|
963
|
-
),
|
|
964
|
-
totalVolumeUsd: toFiniteNumberOrNull2(
|
|
965
|
-
globalRow.totalVolumeUsd
|
|
966
|
-
),
|
|
967
|
-
totalVolumeReportedUsd: toFiniteNumberOrNull2(
|
|
968
|
-
globalRow.totalVolumeReportedUsd
|
|
969
|
-
),
|
|
970
|
-
altMarketCapUsd: toFiniteNumberOrNull2(
|
|
971
|
-
globalRow.altMarketCapUsd
|
|
972
|
-
),
|
|
973
|
-
altVolumeUsd: toFiniteNumberOrNull2(globalRow.altVolumeUsd),
|
|
974
|
-
altVolumeReportedUsd: toFiniteNumberOrNull2(
|
|
975
|
-
globalRow.altVolumeReportedUsd
|
|
976
|
-
),
|
|
977
|
-
btcDominancePct: toFiniteNumberOrNull2(
|
|
978
|
-
globalRow.btcDominancePct
|
|
979
|
-
),
|
|
980
|
-
ethDominancePct: toFiniteNumberOrNull2(
|
|
981
|
-
globalRow.ethDominancePct
|
|
982
|
-
),
|
|
983
|
-
btcDominanceChange24hPct,
|
|
984
|
-
ethDominanceChange24hPct: toFiniteNumberOrNull2(
|
|
985
|
-
globalRow.ethDominanceChange24hPct
|
|
986
|
-
),
|
|
987
|
-
altMarketCapChange24hPct,
|
|
988
|
-
altVolumeChange24hPct,
|
|
989
|
-
activeCryptocurrencies: toFiniteNumberOrNull2(
|
|
990
|
-
globalRow.activeCryptocurrencies
|
|
991
|
-
),
|
|
992
|
-
activeExchanges: toFiniteNumberOrNull2(
|
|
993
|
-
globalRow.activeExchanges
|
|
994
|
-
),
|
|
995
|
-
activeMarketPairs: toFiniteNumberOrNull2(
|
|
996
|
-
globalRow.activeMarketPairs
|
|
997
|
-
),
|
|
998
|
-
altLiquidityRegime
|
|
999
|
-
}
|
|
1000
|
-
} : {},
|
|
1001
|
-
...btcRow || ethRow ? {
|
|
1002
|
-
cmcReferenceAssets: {
|
|
1003
|
-
source: SOURCE_REFERENCE,
|
|
1004
|
-
interval: "1d",
|
|
1005
|
-
asOfTs: Math.max(
|
|
1006
|
-
btcRow?.ts.getTime() ?? 0,
|
|
1007
|
-
ethRow?.ts.getTime() ?? 0
|
|
1008
|
-
),
|
|
1009
|
-
ageMs: btcRow?.ageMs != null && ethRow?.ageMs != null ? Math.max(btcRow.ageMs, ethRow.ageMs) : btcRow?.ageMs ?? ethRow?.ageMs ?? null,
|
|
1010
|
-
stale: referenceStale,
|
|
1011
|
-
btcMarketCapUsd,
|
|
1012
|
-
ethMarketCapUsd,
|
|
1013
|
-
btcVolumeUsd,
|
|
1014
|
-
ethVolumeUsd,
|
|
1015
|
-
btcVolumeToMarketCap: safeDivide(
|
|
1016
|
-
btcVolumeUsd,
|
|
1017
|
-
btcMarketCapUsd
|
|
1018
|
-
),
|
|
1019
|
-
ethVolumeToMarketCap: safeDivide(
|
|
1020
|
-
ethVolumeUsd,
|
|
1021
|
-
ethMarketCapUsd
|
|
1022
|
-
),
|
|
1023
|
-
ethBtcMarketCapRatio,
|
|
1024
|
-
ethBtcMarketCapRatioChange24hPct,
|
|
1025
|
-
ethVsBtcVolumeRatio: safeDivide(ethVolumeUsd, btcVolumeUsd),
|
|
1026
|
-
referenceLiquidityRegime: toReferenceLiquidityRegime({
|
|
1027
|
-
stale: referenceStale,
|
|
1028
|
-
ethBtcMarketCapRatioChange24hPct,
|
|
1029
|
-
ethVsBtcVolumeRatio: safeDivide(ethVolumeUsd, btcVolumeUsd)
|
|
1030
|
-
})
|
|
1031
|
-
}
|
|
1032
|
-
} : {},
|
|
1033
|
-
...exchangeLiquidityRow ? {
|
|
1034
|
-
cmcExchangeLiquidity: {
|
|
1035
|
-
source: SOURCE_EXCHANGE_LIQUIDITY,
|
|
1036
|
-
interval: exchangeLiquidityRow.interval,
|
|
1037
|
-
asOfTs: exchangeLiquidityRow.ts.getTime(),
|
|
1038
|
-
ageMs: exchangeLiquidityRow.ageMs,
|
|
1039
|
-
stale: exchangeLiquidityRow.stale,
|
|
1040
|
-
exchangesCount: toFiniteNumberOrNull2(
|
|
1041
|
-
exchangeLiquidityRow.exchangesCount
|
|
1042
|
-
),
|
|
1043
|
-
totalVolumeUsd: toFiniteNumberOrNull2(
|
|
1044
|
-
exchangeLiquidityRow.totalVolumeUsd
|
|
1045
|
-
),
|
|
1046
|
-
totalVolumeChange24hPct: toFiniteNumberOrNull2(
|
|
1047
|
-
exchangeLiquidityRow.totalVolumeChange24hPct
|
|
1048
|
-
),
|
|
1049
|
-
binanceVolumeUsd: toFiniteNumberOrNull2(
|
|
1050
|
-
exchangeLiquidityRow.binanceVolumeUsd
|
|
1051
|
-
),
|
|
1052
|
-
binanceVolumeShare: toFiniteNumberOrNull2(
|
|
1053
|
-
exchangeLiquidityRow.binanceVolumeShare
|
|
1054
|
-
),
|
|
1055
|
-
topExchangeVolumeShare: toFiniteNumberOrNull2(
|
|
1056
|
-
exchangeLiquidityRow.topExchangeVolumeShare
|
|
1057
|
-
),
|
|
1058
|
-
liquidityRegime: exchangeLiquidityRegime
|
|
1059
|
-
}
|
|
1060
|
-
} : {},
|
|
1061
|
-
...fearGreedRow ? {
|
|
1062
|
-
cmcFearGreed: {
|
|
1063
|
-
source: SOURCE_FEAR_GREED,
|
|
1064
|
-
interval: "1d",
|
|
1065
|
-
asOfTs: fearGreedRow.ts.getTime(),
|
|
1066
|
-
ageMs: fearGreedRow.ageMs,
|
|
1067
|
-
stale: fearGreedRow.stale,
|
|
1068
|
-
value: toFiniteNumberOrNull2(fearGreedRow.value),
|
|
1069
|
-
valueChange24h: toFiniteNumberOrNull2(
|
|
1070
|
-
fearGreedRow.valueChange24h
|
|
1071
|
-
),
|
|
1072
|
-
valueChange7d: toFiniteNumberOrNull2(
|
|
1073
|
-
fearGreedRow.valueChange7d
|
|
1074
|
-
),
|
|
1075
|
-
classification: fearGreedRow.classification ?? "Unknown",
|
|
1076
|
-
sentimentRegime: fearGreedRow.sentimentRegime ?? "unknown"
|
|
1077
|
-
}
|
|
1078
|
-
} : {},
|
|
1079
|
-
...cmc100Row || cmc20Row ? {
|
|
1080
|
-
cmcIndexes: {
|
|
1081
|
-
source: SOURCE_INDEX,
|
|
1082
|
-
interval: "1d",
|
|
1083
|
-
asOfTs: Math.max(
|
|
1084
|
-
cmc100Row?.ts.getTime() ?? 0,
|
|
1085
|
-
cmc20Row?.ts.getTime() ?? 0
|
|
1086
|
-
),
|
|
1087
|
-
ageMs: cmc100Row?.ageMs != null && cmc20Row?.ageMs != null ? Math.max(cmc100Row.ageMs, cmc20Row.ageMs) : cmc100Row?.ageMs ?? cmc20Row?.ageMs ?? null,
|
|
1088
|
-
stale: indexStale,
|
|
1089
|
-
cmc100Value,
|
|
1090
|
-
cmc100Change24hPct,
|
|
1091
|
-
cmc100TopConstituentSymbol: cmc100Row?.topConstituentSymbol ?? null,
|
|
1092
|
-
cmc100TopConstituentWeightPct: toFiniteNumberOrNull2(
|
|
1093
|
-
cmc100Row?.topConstituentWeightPct
|
|
1094
|
-
),
|
|
1095
|
-
cmc20Value,
|
|
1096
|
-
cmc20Change24hPct,
|
|
1097
|
-
cmc20TopConstituentSymbol: cmc20Row?.topConstituentSymbol ?? null,
|
|
1098
|
-
cmc20TopConstituentWeightPct: toFiniteNumberOrNull2(
|
|
1099
|
-
cmc20Row?.topConstituentWeightPct
|
|
1100
|
-
),
|
|
1101
|
-
cmc20ToCmc100Ratio,
|
|
1102
|
-
cmc20ToCmc100RatioChange24hPct,
|
|
1103
|
-
indexRegime
|
|
1104
|
-
}
|
|
1105
|
-
} : {}
|
|
1106
|
-
}
|
|
1107
|
-
}
|
|
1108
|
-
};
|
|
1109
|
-
refreshSignalBaseContextGateFeatures2(signal);
|
|
1110
|
-
return true;
|
|
1111
|
-
} catch (error) {
|
|
1112
|
-
if (isMarketContextCancellationError(error, params.abortSignal)) {
|
|
1113
|
-
throw error;
|
|
1114
|
-
}
|
|
1115
|
-
coinMarketCapContextUnavailable = true;
|
|
1116
|
-
logger2.warn(
|
|
1117
|
-
"CoinMarketCap context disabled after Timescale read failure: %s",
|
|
1118
|
-
String(error)
|
|
1119
|
-
);
|
|
1120
|
-
return false;
|
|
1121
|
-
}
|
|
1122
|
-
};
|
|
1123
|
-
|
|
1124
|
-
// src/hyperliquidWhaleUniverse.ts
|
|
1125
|
-
import { createHash as createHash2 } from "crypto";
|
|
1126
|
-
|
|
1127
|
-
// src/config/hyperliquidPerpUniverse.json
|
|
1128
|
-
var hyperliquidPerpUniverse_default = {
|
|
1129
|
-
schemaVersion: 1,
|
|
1130
|
-
updatedAt: "2026-08-04T11:46:43.476Z",
|
|
1131
|
-
source: "hyperliquid_main_perp_day_notional_volume",
|
|
1132
|
-
fingerprint: "58f7e024b19158dc",
|
|
1133
|
-
size: 30,
|
|
1134
|
-
symbols: [
|
|
1135
|
-
"BTC",
|
|
1136
|
-
"ETH",
|
|
1137
|
-
"HYPE",
|
|
1138
|
-
"SOL",
|
|
1139
|
-
"ZEC",
|
|
1140
|
-
"PUMP",
|
|
1141
|
-
"XRP",
|
|
1142
|
-
"CASHCAT",
|
|
1143
|
-
"WLD",
|
|
1144
|
-
"GRAM",
|
|
1145
|
-
"UNI",
|
|
1146
|
-
"ADA",
|
|
1147
|
-
"LIT",
|
|
1148
|
-
"ENA",
|
|
1149
|
-
"NEAR",
|
|
1150
|
-
"kPEPE",
|
|
1151
|
-
"FARTCOIN",
|
|
1152
|
-
"KAITO",
|
|
1153
|
-
"AAVE",
|
|
1154
|
-
"SUI",
|
|
1155
|
-
"ONDO",
|
|
1156
|
-
"AVAX",
|
|
1157
|
-
"DOGE",
|
|
1158
|
-
"VVV",
|
|
1159
|
-
"BNB",
|
|
1160
|
-
"ALGO",
|
|
1161
|
-
"XMR",
|
|
1162
|
-
"MEGA",
|
|
1163
|
-
"LINK",
|
|
1164
|
-
"TAO"
|
|
1165
|
-
]
|
|
1166
|
-
};
|
|
1167
|
-
|
|
1168
|
-
// src/config/hyperliquidWhales.json
|
|
1169
|
-
var hyperliquidWhales_default = {
|
|
1170
|
-
schemaVersion: 2,
|
|
1171
|
-
updatedAt: "2026-08-04T15:10:21.316Z",
|
|
1172
|
-
source: "hyperliquid_structural_fills_snapshot",
|
|
1173
|
-
selection: {
|
|
1174
|
-
calibrationFrom: "2026-07-28T00:00:00.000Z",
|
|
1175
|
-
calibrationTo: "2026-08-04T00:00:00.000Z",
|
|
1176
|
-
effectiveFrom: "2026-08-04T00:00:00.000Z",
|
|
1177
|
-
candidateLimit: 400,
|
|
1178
|
-
minimumAccountValueUsd: 5e5,
|
|
1179
|
-
minimumActiveDays: 1,
|
|
1180
|
-
maximumRawFillsPerDay: 20,
|
|
1181
|
-
maximumDirectionalExecutionsPerDay: 10,
|
|
1182
|
-
minimumMedianNotionalUsd: 0,
|
|
1183
|
-
minimumMedianInterExecutionMinutes: 0,
|
|
1184
|
-
minimumTop30NotionalShare: 1e-3,
|
|
1185
|
-
minimumTurnoverToEquity: 0.05,
|
|
1186
|
-
maximumTurnoverToEquity: 5,
|
|
1187
|
-
score: "log1p(accountValue)*log1p(medianExecutionNotional)*log1p(max(1,medianInterExecutionMinutes))*sqrt(activeDays)*top30Share/sqrt(1+rawFillsPerDay)",
|
|
1188
|
-
forbiddenSelectionMetrics: ["pnl", "roi", "winRate", "closedPnl"]
|
|
1189
|
-
},
|
|
1190
|
-
fingerprint: "77a6e9d41222efdb",
|
|
1191
|
-
size: 100,
|
|
1192
|
-
addresses: [
|
|
1193
|
-
"0xacb66d1996501bcc82a0e7819c9e041599f5efbd",
|
|
1194
|
-
"0x90dbb196cacf24bd212b377a0dd62eaad5e8151b",
|
|
1195
|
-
"0xde5b8c8e713f7c29ec72c22a326d61e5ac76e402",
|
|
1196
|
-
"0x22edc38749b59c31c9319eae7fc62143ac2fcc6c",
|
|
1197
|
-
"0x4bc9a771cd2c96cb2a4501f2c049402b4e7e11b3",
|
|
1198
|
-
"0x9a0825ca6c4c577a1202a8fae3b8f044c1d5711d",
|
|
1199
|
-
"0xb6ca5d1ed0ca4bf8444c9dee2d068a9f4c5e2e92",
|
|
1200
|
-
"0xc3610d02548cdb1a30ede87c5dc0d2ffe5b9b99f",
|
|
1201
|
-
"0x48c6aaa647ad5197e85797f8eb95d7b9ce1ec5b6",
|
|
1202
|
-
"0x8c1ab015da7e5319649afca4a551924f89eff883",
|
|
1203
|
-
"0x64bddab5f13a99576fca6747f0aa205422c5f708",
|
|
1204
|
-
"0x98785f2a43fe32f972476b86ed5a5cfaf5240f00",
|
|
1205
|
-
"0x9836e286fbf4f71998827d4bd6ea3b470c26457e",
|
|
1206
|
-
"0x8f8d2d2565bfb10608a7ce64b48e2aea7875a344",
|
|
1207
|
-
"0xe6df4ed7a6ccb8426cbc87bdcdd3cc6d0e6c0690",
|
|
1208
|
-
"0xab04d040dec929a204298d44701476968b238b69",
|
|
1209
|
-
"0x870f718a84843abc3e22412a9b4dc2924466d448",
|
|
1210
|
-
"0x12c206d3e2f0d0059420c54d33fe604ebbecc230",
|
|
1211
|
-
"0x7d9e91a8a47eb553cbfd323e7633c0c213327124",
|
|
1212
|
-
"0x2057d4f2b6ea957b63371980775b0a5505a438f0",
|
|
1213
|
-
"0xead5b7d86c681c036c59cd00a0390541061c69f2",
|
|
1214
|
-
"0x3005fade4c0df5e1cd187d7062da359416f0eb8e",
|
|
1215
|
-
"0xe83b5e3644b50e802a25b34b38b91a061a0d3ca8",
|
|
1216
|
-
"0x525232cb6ed5030d2b052b36bfc0baec96986ee3",
|
|
1217
|
-
"0xa251520154ca342f0b1d702bf5a56f78c982405b",
|
|
1218
|
-
"0xa9184bb57fd75c7c63e4c074d6319eceac6be53b",
|
|
1219
|
-
"0xa20fb0c9e04063eec5be286e9269028d966646fa",
|
|
1220
|
-
"0x169ba96d963e77a87dbe58922ecdcdd0f0cf3315",
|
|
1221
|
-
"0x115849ce84370f25cadcf0d348510d73837e1aa5",
|
|
1222
|
-
"0x37b81ab9e3ab04b9e3738e4891621205aaa31fd5",
|
|
1223
|
-
"0x90f57d90a7dd3114312d4c31803ad5732baf5935",
|
|
1224
|
-
"0xad227f63d34e7251c1d0ab65e64eeea07aee4e44",
|
|
1225
|
-
"0xd38e2b0b3c85ae138532f9fe1a1d5f5455866b82",
|
|
1226
|
-
"0xf5f4c3aa62a0f903a8529905031b1536d1cf16f8",
|
|
1227
|
-
"0x75632aaf83120eae9b0618f8f50773ce347de2ad",
|
|
1228
|
-
"0x3927af597c09774f91ef84bf765ded4109e9f8ea",
|
|
1229
|
-
"0xf1863bd6d60a8f4b5f583ec54009af6a044877bf",
|
|
1230
|
-
"0xe7b34f28c54f85533896f8827edc92edac28b17f",
|
|
1231
|
-
"0x1cda20f9f6a23c209d54940af3ef63b96c9b2c31",
|
|
1232
|
-
"0x9e55b0e43b7f7f3909af841d326b2dadfda9392e",
|
|
1233
|
-
"0x610b8dd6b392d010c2b6ad8d121dfc09524cb613",
|
|
1234
|
-
"0x1d639a688b78f3d7e57166e59d9a8886b1bfb5a5",
|
|
1235
|
-
"0x34d3a17095ef7def39a489d63c7c916e673c9ca2",
|
|
1236
|
-
"0x5d0bde4017530ed43370e332a5d30194941f73c6",
|
|
1237
|
-
"0xaa7afc250041e2c56a06d3b7a5295bd01929e11b",
|
|
1238
|
-
"0x325edd95bb016c36027ce3b9f7595af7094a9564",
|
|
1239
|
-
"0x47f5a6397b79deaaca5c456503e7c1a49357b942",
|
|
1240
|
-
"0xf5e7ffe523d0491709958490b4877e1d9de6c6f7",
|
|
1241
|
-
"0x64702807a1f0f9888ea22b858099d7f481227b55",
|
|
1242
|
-
"0xd08a4557671ca465e7bba41110a93148c90f9931",
|
|
1243
|
-
"0x0423b4b2acb4f219ae4a270abd5dde2360e455bb",
|
|
1244
|
-
"0x7b29f8c0ea709b96b72214b3de5d85c3d9570794",
|
|
1245
|
-
"0xeb8ebad162a9ce4cb7e2325c92b960b9df63365c",
|
|
1246
|
-
"0x939f95036d2e7b6d7419ec072bf9d967352204d2",
|
|
1247
|
-
"0x2490aa2b48022e857d44e738911787c8e2296aed",
|
|
1248
|
-
"0x7622f628135378f1b8d1876952de9bab80245f1c",
|
|
1249
|
-
"0x9d677028e0e592d48fc8a8ddc910693301a4a450",
|
|
1250
|
-
"0x85e410e4c535cb1709dbd36468f132037a5b2087",
|
|
1251
|
-
"0xd6f52e2ab58a678eced4a4d1693757b0677631c8",
|
|
1252
|
-
"0x24d8969e2bd6aabb6c4dfe042ef8b2cd8085217e",
|
|
1253
|
-
"0xda51323fe9800c8365646ad5c7ade0dd17fdc167",
|
|
1254
|
-
"0x53f8f390fd4f70941c5d160a964f6893c8dbceff",
|
|
1255
|
-
"0x72dae3560e7f4c59a305e2e739d6bd6777f738b5",
|
|
1256
|
-
"0x36f5e448c9cf0032551e58919cf4eabefcd742c1",
|
|
1257
|
-
"0x5b43b7162694de766c164202df0f0c716e4292ab",
|
|
1258
|
-
"0xae551d73161bac3315c5ade0e2d499a44ebe2236",
|
|
1259
|
-
"0x4eca8d62e07b3f3e78a8ae8725d4666da324c5a8",
|
|
1260
|
-
"0x40a632026febd007b0e4431a29170507f7aa3896",
|
|
1261
|
-
"0xd453e91f692eb5437cf345e09545827576e3225d",
|
|
1262
|
-
"0xbe2c69eb3d7b8ee35cbe3a28293472be31179d75",
|
|
1263
|
-
"0xfdf891f2b214a4c9374d26595ec6d4080262e381",
|
|
1264
|
-
"0xfd97600ac44b3c4e20ac1a5f23e3b18d10fa5912",
|
|
1265
|
-
"0x98e073b579fd483eac8f10d5bd0b32c8c3bbd7e0",
|
|
1266
|
-
"0xb56f19e917546a60d658110a611cf62a6877b517",
|
|
1267
|
-
"0xbd334a55db91617545e8d1b09941e036ad8e4200",
|
|
1268
|
-
"0x9f1920d0cbb63ed03376a1e09fd2851d601234c8",
|
|
1269
|
-
"0xe9cef9fd872a03cedf9d3e6e90309782b3724ea6",
|
|
1270
|
-
"0xa43682a1f129e4faf0152175cc03c44e4957e8cf",
|
|
1271
|
-
"0x6fbb33de5f39c690d2decb95b9ab46ed0c83e3bc",
|
|
1272
|
-
"0x81d4163289cf61a0eaaac4862cb88ef0f4215c29",
|
|
1273
|
-
"0xd248d2f09bfbe04e67fc7fea08828d6ad6d95b6d",
|
|
1274
|
-
"0x01ffcc7285866e5cee14c8e807cb2e8d5f61079f",
|
|
1275
|
-
"0xc69ae428f6049e78d445f053d2c1df879c59b34c",
|
|
1276
|
-
"0xaab4dfe6d735c4ac46217216fe883a39fbfe8284",
|
|
1277
|
-
"0xe912cac1a6641004a8803687ee7699227fdb0550",
|
|
1278
|
-
"0x5fdbf83c3d95d525704f61401db68cbec1b2a775",
|
|
1279
|
-
"0xebd7c4b3677a4480fb5f69ac2f51c51d66a19990",
|
|
1280
|
-
"0xe4d1fa7e6141f9131345117bdb8b5701bca67662",
|
|
1281
|
-
"0x685feceec46dd4e5c9b5b726f5d7550fd0eda526",
|
|
1282
|
-
"0x3ea8b09da72972e953541500721803a187526da6",
|
|
1283
|
-
"0xdbe0780a5c5a0e3b345101bbc01ac2218196d6d5",
|
|
1284
|
-
"0x18cd4597e06b7fe0a8cd33dda499121b3a145a8b",
|
|
1285
|
-
"0xb8243756956e722bff5c763971b562ac551bfbd2",
|
|
1286
|
-
"0xe0bf8c96795d2df0b61b3b17e7cfd7d3ba13d4be",
|
|
1287
|
-
"0x218a65e21eddeece7a9df38c6bbdd89f692b7da2",
|
|
1288
|
-
"0x53b63a30a688beb53b5dc7bd731c661d678c555c",
|
|
1289
|
-
"0xfce053a5e461683454bf37ad66d20344c0e3f4c0",
|
|
1290
|
-
"0x3e516cf3c9d4f29fae6c1324c2414dc872fc9c09",
|
|
1291
|
-
"0xe5f1486dc84706d2509885fe6bbc46d21ba81286",
|
|
1292
|
-
"0x38042d713af713f0275b00640a83654db91ea543"
|
|
1293
|
-
]
|
|
1294
|
-
};
|
|
1295
|
-
|
|
1296
|
-
// src/hyperliquidWhaleUniverse.ts
|
|
1297
|
-
var fingerprint2 = (value) => createHash2("sha256").update(JSON.stringify(value)).digest("hex").slice(0, 16);
|
|
1298
|
-
var validateUnique = (values, label) => {
|
|
1299
|
-
if (new Set(values).size !== values.length) {
|
|
1300
|
-
throw new Error(`Hyperliquid ${label} snapshot contains duplicates`);
|
|
1301
|
-
}
|
|
1302
|
-
};
|
|
1303
|
-
var validatePerpSnapshot = (value) => {
|
|
1304
|
-
const symbols = value.symbols.map((symbol) => symbol.trim()).filter(Boolean);
|
|
1305
|
-
validateUnique(symbols, "perp universe");
|
|
1306
|
-
if (value.schemaVersion !== 1 || value.source !== "hyperliquid_main_perp_day_notional_volume" || value.size !== 30 || symbols.length !== 30 || value.fingerprint !== fingerprint2(symbols)) {
|
|
1307
|
-
throw new Error("Invalid Hyperliquid perp universe snapshot");
|
|
1308
|
-
}
|
|
1309
|
-
return value;
|
|
1310
|
-
};
|
|
1311
|
-
var validateWhaleSnapshot = (value) => {
|
|
1312
|
-
const addresses = value.addresses.map((address) => address.toLowerCase());
|
|
1313
|
-
validateUnique(addresses, "whale registry");
|
|
1314
|
-
if (value.schemaVersion !== 2 || value.source !== "hyperliquid_structural_fills_snapshot" || value.size !== 100 || addresses.length !== 100 || addresses.some((address) => !/^0x[0-9a-f]{40}$/.test(address)) || value.fingerprint !== fingerprint2(addresses)) {
|
|
1315
|
-
throw new Error("Invalid Hyperliquid whale registry snapshot");
|
|
1316
|
-
}
|
|
1317
|
-
return value;
|
|
1318
|
-
};
|
|
1319
|
-
var perpSnapshot = validatePerpSnapshot(hyperliquidPerpUniverse_default);
|
|
1320
|
-
var whaleSnapshot = validateWhaleSnapshot(hyperliquidWhales_default);
|
|
1321
|
-
var perpSymbolSet = new Set(perpSnapshot.symbols);
|
|
1322
|
-
var whaleAddressSet = new Set(whaleSnapshot.addresses);
|
|
1323
|
-
var getHyperliquidPerpUniverseSnapshot = () => perpSnapshot;
|
|
1324
|
-
var getHyperliquidWhaleRegistrySnapshot = () => whaleSnapshot;
|
|
1325
|
-
var getHyperliquidPerpSymbols = () => [...perpSnapshot.symbols];
|
|
1326
|
-
var getHyperliquidWhaleAddresses = () => [...whaleSnapshot.addresses];
|
|
1327
|
-
var isTrackedHyperliquidPerp = (symbol) => perpSymbolSet.has(symbol.trim());
|
|
1328
|
-
var isTrackedHyperliquidWhale = (address) => whaleAddressSet.has(address.trim().toLowerCase());
|
|
1329
|
-
var SIGNAL_TO_HYPERLIQUID_ALIASES = {
|
|
1330
|
-
PEPE: "kPEPE",
|
|
1331
|
-
"1000PEPE": "kPEPE",
|
|
1332
|
-
PUMPFUN: "PUMP",
|
|
1333
|
-
SHIB: "kSHIB",
|
|
1334
|
-
BONK: "kBONK"
|
|
1335
|
-
};
|
|
1336
|
-
var resolveHyperliquidPerpFromSignalSymbol = (symbol) => {
|
|
1337
|
-
const normalized = symbol.trim().toUpperCase().replace(/(?:USDT|USDC)$/, "");
|
|
1338
|
-
const candidate = SIGNAL_TO_HYPERLIQUID_ALIASES[normalized] ?? normalized;
|
|
1339
|
-
return perpSymbolSet.has(candidate) ? candidate : null;
|
|
1340
|
-
};
|
|
1341
|
-
|
|
1342
|
-
// src/strategyHelpers/hyperliquidWhaleContext.ts
|
|
1343
|
-
import { intervalToMs } from "@tradejs/core/data";
|
|
1344
|
-
import { refreshSignalBaseContextGateFeatures as refreshSignalBaseContextGateFeatures3 } from "@tradejs/core/strategies";
|
|
1345
|
-
import {
|
|
1346
|
-
getHyperliquidWhaleCoverageSeriesRows,
|
|
1347
|
-
getHyperliquidWhaleFlowAggregate,
|
|
1348
|
-
getHyperliquidWhaleFlowSeriesRows
|
|
1349
|
-
} from "@tradejs/infra/timescale";
|
|
1350
|
-
import { logger as logger3 } from "@tradejs/infra/logger";
|
|
1351
|
-
var MAX_CACHE_ENTRIES = 2048;
|
|
1352
|
-
var MAX_COVERAGE_SERIES_CACHE_ENTRIES = 20;
|
|
1353
|
-
var MAX_FLOW_SERIES_CACHE_ENTRIES = 64;
|
|
1354
|
-
var SERIES_CHUNK_MS = 90 * 24 * 60 * 6e4;
|
|
1355
|
-
var MAX_SERIES_LOOKBACK_MS = 60 * 6e4;
|
|
1356
|
-
var DEFAULT_MIN_COVERAGE_PCT = 0.8;
|
|
1357
|
-
var INTERVAL_MS = {
|
|
1358
|
-
"1m": 6e4,
|
|
1359
|
-
"5m": 5 * 6e4,
|
|
1360
|
-
"15m": 15 * 6e4,
|
|
1361
|
-
"1h": 60 * 6e4
|
|
1362
|
-
};
|
|
1363
|
-
var DEFAULT_MAX_AGE_BY_INTERVAL2 = {
|
|
1364
|
-
"1m": 2 * 6e4,
|
|
1365
|
-
"5m": 10 * 6e4,
|
|
1366
|
-
"15m": 30 * 6e4,
|
|
1367
|
-
"1h": 2 * 60 * 6e4
|
|
1368
|
-
};
|
|
1369
|
-
var hyperliquidWhaleContextUnavailable = false;
|
|
1370
|
-
var contextCache = /* @__PURE__ */ new Map();
|
|
1371
|
-
var coverageSeriesCache = /* @__PURE__ */ new Map();
|
|
1372
|
-
var flowSeriesCache = /* @__PURE__ */ new Map();
|
|
1373
|
-
var parseEnabledFlag3 = (value, env) => {
|
|
1374
|
-
const normalized = String(value ?? "").trim().toLowerCase();
|
|
1375
|
-
if (!normalized) return env !== "TEST";
|
|
1376
|
-
if (["1", "true", "yes", "on"].includes(normalized)) return true;
|
|
1377
|
-
if (["0", "false", "no", "off"].includes(normalized)) return false;
|
|
1378
|
-
if (normalized === "backtest") return env === "BACKTEST";
|
|
1379
|
-
if (normalized === "live") return env !== "BACKTEST";
|
|
1380
|
-
return false;
|
|
1381
|
-
};
|
|
1382
|
-
var getMinimumCoveragePct = () => {
|
|
1383
|
-
const parsed = Number(process.env.HYPERLIQUID_WHALE_MIN_COVERAGE_PCT);
|
|
1384
|
-
return Number.isFinite(parsed) ? Math.max(0, Math.min(1, parsed)) : DEFAULT_MIN_COVERAGE_PCT;
|
|
1385
|
-
};
|
|
1386
|
-
var signalIntervalToMarketInterval2 = (value) => {
|
|
1387
|
-
const normalized = String(value).trim().toLowerCase();
|
|
1388
|
-
if (normalized === "1" || normalized === "1m") return "1m";
|
|
1389
|
-
if (normalized === "5" || normalized === "5m") return "5m";
|
|
1390
|
-
if (normalized === "60" || normalized === "1h") return "1h";
|
|
1391
|
-
return "15m";
|
|
1392
|
-
};
|
|
1393
|
-
var hasBaseContext3 = (signal) => Boolean(
|
|
1394
|
-
signal.additionalIndicators?.baseContext && typeof signal.additionalIndicators.baseContext === "object" && !Array.isArray(signal.additionalIndicators.baseContext)
|
|
1395
|
-
);
|
|
1396
|
-
var setBoundedCache = (key, value) => {
|
|
1397
|
-
if (contextCache.size >= MAX_CACHE_ENTRIES) {
|
|
1398
|
-
const oldestKey = contextCache.keys().next().value;
|
|
1399
|
-
if (oldestKey != null) contextCache.delete(oldestKey);
|
|
1400
|
-
}
|
|
1401
|
-
contextCache.set(key, value);
|
|
1402
|
-
void value.catch(() => contextCache.delete(key));
|
|
1403
|
-
};
|
|
1404
|
-
var setBoundedSeriesCache = (cache, maxEntries, key, value) => {
|
|
1405
|
-
if (cache.size >= maxEntries) {
|
|
1406
|
-
const oldestKey = cache.keys().next().value;
|
|
1407
|
-
if (oldestKey != null) cache.delete(oldestKey);
|
|
1408
|
-
}
|
|
1409
|
-
cache.set(key, value);
|
|
1410
|
-
void value.catch(() => cache.delete(key));
|
|
1411
|
-
};
|
|
1412
|
-
var uniqueAddressCount = (rows, field) => {
|
|
1413
|
-
const addresses = /* @__PURE__ */ new Set();
|
|
1414
|
-
for (const row of rows) {
|
|
1415
|
-
for (const address of row[field]) addresses.add(address);
|
|
1416
|
-
}
|
|
1417
|
-
return addresses.size;
|
|
1418
|
-
};
|
|
1419
|
-
var sumFlowField = (rows, field) => rows.reduce((sum, row) => sum + row[field], 0);
|
|
1420
|
-
var sliceSeriesWindow = (rows, fromMs, toMs) => {
|
|
1421
|
-
const lowerBound = (timestamp) => {
|
|
1422
|
-
let low = 0;
|
|
1423
|
-
let high = rows.length;
|
|
1424
|
-
while (low < high) {
|
|
1425
|
-
const middle = low + Math.floor((high - low) / 2);
|
|
1426
|
-
if (rows[middle].ts.getTime() < timestamp) low = middle + 1;
|
|
1427
|
-
else high = middle;
|
|
1428
|
-
}
|
|
1429
|
-
return low;
|
|
1430
|
-
};
|
|
1431
|
-
return rows.slice(lowerBound(fromMs), lowerBound(toMs));
|
|
1432
|
-
};
|
|
1433
|
-
var aggregateHyperliquidWhaleFlowSeries = (params) => {
|
|
1434
|
-
const intervalMs = INTERVAL_MS[params.interval];
|
|
1435
|
-
const windowStartMs = params.decisionTimeMs - intervalMs;
|
|
1436
|
-
const coverageRows = sliceSeriesWindow(
|
|
1437
|
-
params.coverageRows,
|
|
1438
|
-
windowStartMs,
|
|
1439
|
-
params.decisionTimeMs
|
|
1440
|
-
);
|
|
1441
|
-
const expectedBuckets = Math.ceil(intervalMs / 6e4);
|
|
1442
|
-
if (coverageRows.length !== expectedBuckets || coverageRows.some((row) => row.coveredWhales <= 0)) {
|
|
1443
|
-
return null;
|
|
1444
|
-
}
|
|
1445
|
-
const flowRows = sliceSeriesWindow(
|
|
1446
|
-
params.flowRows,
|
|
1447
|
-
windowStartMs,
|
|
1448
|
-
params.decisionTimeMs
|
|
1449
|
-
);
|
|
1450
|
-
const asOfTs = new Date(
|
|
1451
|
-
Math.max(...coverageRows.map((row) => row.ts.getTime()))
|
|
1452
|
-
);
|
|
1453
|
-
const coveredWhales = Math.min(
|
|
1454
|
-
...coverageRows.map((row) => row.coveredWhales)
|
|
1455
|
-
);
|
|
1456
|
-
const expectedWhales = Math.max(
|
|
1457
|
-
...coverageRows.map((row) => row.expectedWhales)
|
|
1458
|
-
);
|
|
1459
|
-
const coveragePct = Math.min(...coverageRows.map((row) => row.coveragePct));
|
|
1460
|
-
const trades = sumFlowField(flowRows, "trades");
|
|
1461
|
-
const whaleSides = sumFlowField(flowRows, "whaleSides");
|
|
1462
|
-
const buyNotionalUsd = sumFlowField(flowRows, "buyNotionalUsd");
|
|
1463
|
-
const sellNotionalUsd = sumFlowField(flowRows, "sellNotionalUsd");
|
|
1464
|
-
const positionAwareWhaleSides = sumFlowField(
|
|
1465
|
-
flowRows,
|
|
1466
|
-
"positionAwareWhaleSides"
|
|
1467
|
-
);
|
|
1468
|
-
const longEntryNotionalUsd = sumFlowField(flowRows, "longEntryNotionalUsd");
|
|
1469
|
-
const shortEntryNotionalUsd = sumFlowField(flowRows, "shortEntryNotionalUsd");
|
|
1470
|
-
const longExitNotionalUsd = sumFlowField(flowRows, "longExitNotionalUsd");
|
|
1471
|
-
const shortExitNotionalUsd = sumFlowField(flowRows, "shortExitNotionalUsd");
|
|
1472
|
-
const totalNotionalUsd = buyNotionalUsd + sellNotionalUsd;
|
|
1473
|
-
const totalEntryNotionalUsd = longEntryNotionalUsd + shortEntryNotionalUsd;
|
|
1474
|
-
const ageMs = params.decisionTimeMs - (asOfTs.getTime() + 6e4);
|
|
1475
|
-
return {
|
|
1476
|
-
symbol: params.symbol,
|
|
1477
|
-
interval: params.interval,
|
|
1478
|
-
asOfTs,
|
|
1479
|
-
windowEndTs: new Date(params.decisionTimeMs),
|
|
1480
|
-
trades,
|
|
1481
|
-
whaleSides,
|
|
1482
|
-
uniqueWhales: uniqueAddressCount(flowRows, "whaleAddresses"),
|
|
1483
|
-
coveredWhales,
|
|
1484
|
-
expectedWhales,
|
|
1485
|
-
coveragePct,
|
|
1486
|
-
buyNotionalUsd,
|
|
1487
|
-
sellNotionalUsd,
|
|
1488
|
-
netNotionalUsd: buyNotionalUsd - sellNotionalUsd,
|
|
1489
|
-
buySharePct: totalNotionalUsd > 0 ? buyNotionalUsd / totalNotionalUsd : null,
|
|
1490
|
-
positionAwareWhaleSides,
|
|
1491
|
-
positionAwarePct: whaleSides > 0 ? positionAwareWhaleSides / whaleSides : 0,
|
|
1492
|
-
longEntryWhales: uniqueAddressCount(flowRows, "longEntryWhaleAddresses"),
|
|
1493
|
-
shortEntryWhales: uniqueAddressCount(flowRows, "shortEntryWhaleAddresses"),
|
|
1494
|
-
longExitWhales: uniqueAddressCount(flowRows, "longExitWhaleAddresses"),
|
|
1495
|
-
shortExitWhales: uniqueAddressCount(flowRows, "shortExitWhaleAddresses"),
|
|
1496
|
-
longEntryNotionalUsd,
|
|
1497
|
-
shortEntryNotionalUsd,
|
|
1498
|
-
longExitNotionalUsd,
|
|
1499
|
-
shortExitNotionalUsd,
|
|
1500
|
-
entryNetNotionalUsd: longEntryNotionalUsd - shortEntryNotionalUsd,
|
|
1501
|
-
entryLongSharePct: totalEntryNotionalUsd > 0 ? longEntryNotionalUsd / totalEntryNotionalUsd : null,
|
|
1502
|
-
universeFingerprint: params.universeFingerprint,
|
|
1503
|
-
whaleRegistryFingerprint: params.whaleRegistryFingerprint,
|
|
1504
|
-
source: positionAwareWhaleSides > 0 ? "hyperliquid_user_fills" : null,
|
|
1505
|
-
ageMs,
|
|
1506
|
-
stale: ageMs < 0 || (params.maxAgeMs != null && Number.isFinite(params.maxAgeMs) ? ageMs > params.maxAgeMs : false)
|
|
1507
|
-
};
|
|
1508
|
-
};
|
|
1509
|
-
var loadHyperliquidWhaleFlowAggregateFromSeries = async (params) => {
|
|
1510
|
-
const chunkStartMs = Math.floor(params.decisionTimeMs / SERIES_CHUNK_MS) * SERIES_CHUNK_MS;
|
|
1511
|
-
const fromMs = chunkStartMs - MAX_SERIES_LOOKBACK_MS;
|
|
1512
|
-
const toMs = chunkStartMs + SERIES_CHUNK_MS;
|
|
1513
|
-
const sharedKey = [
|
|
1514
|
-
fromMs,
|
|
1515
|
-
toMs,
|
|
1516
|
-
params.universeFingerprint,
|
|
1517
|
-
params.whaleRegistryFingerprint
|
|
1518
|
-
].join(":");
|
|
1519
|
-
let pendingCoverage = coverageSeriesCache.get(sharedKey);
|
|
1520
|
-
if (!pendingCoverage) {
|
|
1521
|
-
pendingCoverage = getHyperliquidWhaleCoverageSeriesRows({
|
|
1522
|
-
fromMs,
|
|
1523
|
-
toMs,
|
|
1524
|
-
universeFingerprint: params.universeFingerprint,
|
|
1525
|
-
whaleRegistryFingerprint: params.whaleRegistryFingerprint,
|
|
1526
|
-
...params.abortSignal ? { signal: params.abortSignal } : {}
|
|
1527
|
-
});
|
|
1528
|
-
setBoundedSeriesCache(
|
|
1529
|
-
coverageSeriesCache,
|
|
1530
|
-
MAX_COVERAGE_SERIES_CACHE_ENTRIES,
|
|
1531
|
-
sharedKey,
|
|
1532
|
-
pendingCoverage
|
|
1533
|
-
);
|
|
1534
|
-
}
|
|
1535
|
-
const flowKey = `${params.symbol}:${sharedKey}`;
|
|
1536
|
-
let pendingFlow = flowSeriesCache.get(flowKey);
|
|
1537
|
-
if (!pendingFlow) {
|
|
1538
|
-
pendingFlow = getHyperliquidWhaleFlowSeriesRows({
|
|
1539
|
-
symbol: params.symbol,
|
|
1540
|
-
fromMs,
|
|
1541
|
-
toMs,
|
|
1542
|
-
universeFingerprint: params.universeFingerprint,
|
|
1543
|
-
whaleRegistryFingerprint: params.whaleRegistryFingerprint,
|
|
1544
|
-
...params.abortSignal ? { signal: params.abortSignal } : {}
|
|
1545
|
-
});
|
|
1546
|
-
setBoundedSeriesCache(
|
|
1547
|
-
flowSeriesCache,
|
|
1548
|
-
MAX_FLOW_SERIES_CACHE_ENTRIES,
|
|
1549
|
-
flowKey,
|
|
1550
|
-
pendingFlow
|
|
1551
|
-
);
|
|
1552
|
-
}
|
|
1553
|
-
const [coverageRows, flowRows] = await Promise.all([
|
|
1554
|
-
pendingCoverage,
|
|
1555
|
-
pendingFlow
|
|
1556
|
-
]);
|
|
1557
|
-
return aggregateHyperliquidWhaleFlowSeries({
|
|
1558
|
-
...params,
|
|
1559
|
-
coverageRows,
|
|
1560
|
-
flowRows
|
|
1561
|
-
});
|
|
1562
|
-
};
|
|
1563
|
-
var toBaseHyperliquidWhaleFlowContext = (row, minimumCoveragePct) => ({
|
|
1564
|
-
source: row.positionAwareWhaleSides > 0 ? "hyperliquid_user_fills" : "hyperliquid_trades",
|
|
1565
|
-
interval: row.interval,
|
|
1566
|
-
asOfTs: row.asOfTs.getTime(),
|
|
1567
|
-
windowEndTs: row.windowEndTs.getTime(),
|
|
1568
|
-
ageMs: row.ageMs,
|
|
1569
|
-
stale: row.stale,
|
|
1570
|
-
symbol: row.symbol,
|
|
1571
|
-
trades: row.trades,
|
|
1572
|
-
whaleSides: row.whaleSides,
|
|
1573
|
-
uniqueWhales: row.uniqueWhales,
|
|
1574
|
-
coveredWhales: row.coveredWhales,
|
|
1575
|
-
expectedWhales: row.expectedWhales,
|
|
1576
|
-
coveragePct: row.coveragePct,
|
|
1577
|
-
coverageSufficient: row.coveragePct >= minimumCoveragePct,
|
|
1578
|
-
buyNotionalUsd: row.buyNotionalUsd,
|
|
1579
|
-
sellNotionalUsd: row.sellNotionalUsd,
|
|
1580
|
-
netNotionalUsd: row.netNotionalUsd,
|
|
1581
|
-
buySharePct: row.buySharePct,
|
|
1582
|
-
positionAwareWhaleSides: row.positionAwareWhaleSides,
|
|
1583
|
-
positionAwarePct: row.positionAwarePct,
|
|
1584
|
-
longEntryWhales: row.longEntryWhales,
|
|
1585
|
-
shortEntryWhales: row.shortEntryWhales,
|
|
1586
|
-
longExitWhales: row.longExitWhales,
|
|
1587
|
-
shortExitWhales: row.shortExitWhales,
|
|
1588
|
-
longEntryNotionalUsd: row.longEntryNotionalUsd,
|
|
1589
|
-
shortEntryNotionalUsd: row.shortEntryNotionalUsd,
|
|
1590
|
-
longExitNotionalUsd: row.longExitNotionalUsd,
|
|
1591
|
-
shortExitNotionalUsd: row.shortExitNotionalUsd,
|
|
1592
|
-
entryNetNotionalUsd: row.entryNetNotionalUsd,
|
|
1593
|
-
entryLongSharePct: row.entryLongSharePct,
|
|
1594
|
-
universeFingerprint: row.universeFingerprint,
|
|
1595
|
-
whaleRegistryFingerprint: row.whaleRegistryFingerprint
|
|
1596
|
-
});
|
|
1597
|
-
var isHyperliquidWhaleContextEnabled = (env) => parseEnabledFlag3(process.env.HYPERLIQUID_WHALE_CONTEXT_ENABLED, env);
|
|
1598
|
-
var loadHyperliquidWhaleFlowContext = async (params) => {
|
|
1599
|
-
if (!(params.enabled ?? isHyperliquidWhaleContextEnabled(params.env)) || hyperliquidWhaleContextUnavailable || params.interval == null) {
|
|
1600
|
-
return null;
|
|
1601
|
-
}
|
|
1602
|
-
const symbol = resolveHyperliquidPerpFromSignalSymbol(params.symbol);
|
|
1603
|
-
if (!symbol) return null;
|
|
1604
|
-
const interval = params.marketInterval ?? signalIntervalToMarketInterval2(params.interval);
|
|
1605
|
-
const decisionTimeMs = params.timestamp + intervalToMs(params.interval);
|
|
1606
|
-
const maxAgeMs = params.maxAgeMs ?? DEFAULT_MAX_AGE_BY_INTERVAL2[interval];
|
|
1607
|
-
const universe = getHyperliquidPerpUniverseSnapshot();
|
|
1608
|
-
const whales = getHyperliquidWhaleRegistrySnapshot();
|
|
1609
|
-
const minimumCoveragePct = getMinimumCoveragePct();
|
|
1610
|
-
const cacheKey = [
|
|
1611
|
-
symbol,
|
|
1612
|
-
interval,
|
|
1613
|
-
decisionTimeMs,
|
|
1614
|
-
maxAgeMs,
|
|
1615
|
-
universe.fingerprint,
|
|
1616
|
-
whales.fingerprint,
|
|
1617
|
-
minimumCoveragePct
|
|
1618
|
-
].join(":");
|
|
1619
|
-
try {
|
|
1620
|
-
let row;
|
|
1621
|
-
if (params.useSeriesCache) {
|
|
1622
|
-
row = await loadHyperliquidWhaleFlowAggregateFromSeries({
|
|
1623
|
-
symbol,
|
|
1624
|
-
interval,
|
|
1625
|
-
decisionTimeMs,
|
|
1626
|
-
maxAgeMs,
|
|
1627
|
-
universeFingerprint: universe.fingerprint,
|
|
1628
|
-
whaleRegistryFingerprint: whales.fingerprint,
|
|
1629
|
-
abortSignal: params.abortSignal
|
|
1630
|
-
});
|
|
1631
|
-
} else {
|
|
1632
|
-
let pending = contextCache.get(cacheKey);
|
|
1633
|
-
if (!pending) {
|
|
1634
|
-
pending = getHyperliquidWhaleFlowAggregate({
|
|
1635
|
-
symbol,
|
|
1636
|
-
interval,
|
|
1637
|
-
decisionTimeMs,
|
|
1638
|
-
maxAgeMs,
|
|
1639
|
-
universeFingerprint: universe.fingerprint,
|
|
1640
|
-
whaleRegistryFingerprint: whales.fingerprint,
|
|
1641
|
-
...params.abortSignal ? { signal: params.abortSignal } : {}
|
|
1642
|
-
});
|
|
1643
|
-
setBoundedCache(cacheKey, pending);
|
|
1644
|
-
}
|
|
1645
|
-
row = await pending;
|
|
1646
|
-
}
|
|
1647
|
-
if (!row) return null;
|
|
1648
|
-
return toBaseHyperliquidWhaleFlowContext(row, minimumCoveragePct);
|
|
1649
|
-
} catch (error) {
|
|
1650
|
-
if (isMarketContextCancellationError(error, params.abortSignal)) {
|
|
1651
|
-
throw error;
|
|
1652
|
-
}
|
|
1653
|
-
hyperliquidWhaleContextUnavailable = true;
|
|
1654
|
-
logger3.warn(
|
|
1655
|
-
"Hyperliquid whale context disabled after Timescale read failure: %s",
|
|
1656
|
-
String(error)
|
|
1657
|
-
);
|
|
1658
|
-
return null;
|
|
1659
|
-
}
|
|
1660
|
-
};
|
|
1661
|
-
var enrichSignalWithHyperliquidWhaleContext = async (params) => {
|
|
1662
|
-
const { signal, env } = params;
|
|
1663
|
-
if (signal.universe === "tradfi" || signal.interval == null || !hasBaseContext3(signal)) {
|
|
1664
|
-
return false;
|
|
1665
|
-
}
|
|
1666
|
-
const hyperliquidWhales = await loadHyperliquidWhaleFlowContext({
|
|
1667
|
-
symbol: signal.symbol,
|
|
1668
|
-
interval: signal.interval,
|
|
1669
|
-
timestamp: signal.timestamp,
|
|
1670
|
-
env,
|
|
1671
|
-
enabled: params.enabled,
|
|
1672
|
-
marketInterval: params.interval,
|
|
1673
|
-
maxAgeMs: params.maxAgeMs,
|
|
1674
|
-
abortSignal: params.abortSignal
|
|
1675
|
-
});
|
|
1676
|
-
if (!hyperliquidWhales) return false;
|
|
1677
|
-
const baseContext = signal.additionalIndicators.baseContext;
|
|
1678
|
-
signal.additionalIndicators = {
|
|
1679
|
-
...signal.additionalIndicators,
|
|
1680
|
-
baseContext: {
|
|
1681
|
-
...baseContext,
|
|
1682
|
-
participation: {
|
|
1683
|
-
...baseContext.participation,
|
|
1684
|
-
hyperliquidWhales
|
|
1685
|
-
}
|
|
1686
|
-
}
|
|
1687
|
-
};
|
|
1688
|
-
refreshSignalBaseContextGateFeatures3(signal);
|
|
1689
|
-
return true;
|
|
1690
|
-
};
|
|
1691
|
-
|
|
1692
|
-
// src/strategyHelpers/marketContextStages.ts
|
|
1693
|
-
import { logger as logger5 } from "@tradejs/infra/logger";
|
|
1694
|
-
|
|
1695
|
-
// src/strategyHelpers/derivativesContext.ts
|
|
1696
|
-
import {
|
|
1697
|
-
buildDerivativesContext,
|
|
1698
|
-
buildCoinalyzeHourlyRowsWithFallback,
|
|
1699
|
-
getLastClosedDerivativesBarStartMs
|
|
1700
|
-
} from "@tradejs/core/indicators";
|
|
1701
|
-
import { intervalToMs as intervalToMs2 } from "@tradejs/core/data";
|
|
1702
|
-
import { refreshSignalBaseContextGateFeatures as refreshSignalBaseContextGateFeatures4 } from "@tradejs/core/strategies";
|
|
1703
|
-
import {
|
|
1704
|
-
DERIVATIVES_CONTEXT_BASE_REFERENCE_SYMBOLS,
|
|
1705
|
-
resolveDerivativesContextReferenceSymbols
|
|
1706
|
-
} from "@tradejs/core/constants";
|
|
1707
|
-
import { getDerivativesWindow } from "@tradejs/infra/timescale";
|
|
1708
|
-
import { logger as logger4 } from "@tradejs/infra/logger";
|
|
1709
|
-
var STORED_INTERVALS = ["15m", "1h"];
|
|
1710
|
-
var CONTEXT_INTERVALS = ["15m", "1h"];
|
|
1711
|
-
var DEFAULT_LOOKBACK_HOURS = 48;
|
|
1712
|
-
var PRIMARY_DERIVATIVES_REFERENCE_SYMBOL = DERIVATIVES_CONTEXT_BASE_REFERENCE_SYMBOLS[0];
|
|
1713
|
-
var SECONDARY_DERIVATIVES_REFERENCE_SYMBOL = DERIVATIVES_CONTEXT_BASE_REFERENCE_SYMBOLS[1];
|
|
1714
|
-
var derivativesContextUnavailable = false;
|
|
1715
|
-
var parseEnabledFlag4 = (value, env) => {
|
|
1716
|
-
const normalized = String(value ?? "").trim().toLowerCase();
|
|
1717
|
-
if (!normalized) return true;
|
|
1718
|
-
if (["1", "true", "yes", "on"].includes(normalized)) return true;
|
|
1719
|
-
if (normalized === "backtest") return env === "BACKTEST";
|
|
1720
|
-
if (normalized === "live") return env !== "BACKTEST";
|
|
1721
|
-
return false;
|
|
1722
|
-
};
|
|
1723
|
-
var parseBooleanFlag = (value, fallback = false) => {
|
|
1724
|
-
const normalized = String(value ?? "").trim().toLowerCase();
|
|
1725
|
-
if (!normalized) return fallback;
|
|
1726
|
-
if (["1", "true", "yes", "on"].includes(normalized)) return true;
|
|
1727
|
-
if (["0", "false", "no", "off"].includes(normalized)) return false;
|
|
1728
|
-
return fallback;
|
|
1729
|
-
};
|
|
1730
|
-
var parseLookbackMs = () => {
|
|
1731
|
-
const hours = Number(process.env.DERIVATIVES_CONTEXT_LOOKBACK_HOURS);
|
|
1732
|
-
const normalizedHours = Number.isFinite(hours) && hours > 0 ? hours : DEFAULT_LOOKBACK_HOURS;
|
|
1733
|
-
return normalizedHours * 60 * 60 * 1e3;
|
|
1734
|
-
};
|
|
1735
|
-
var withHourlyFallbackRows = (rowsByInterval) => ({
|
|
1736
|
-
"15m": rowsByInterval["15m"] ?? [],
|
|
1737
|
-
"1h": buildCoinalyzeHourlyRowsWithFallback({
|
|
1738
|
-
rows15m: rowsByInterval["15m"],
|
|
1739
|
-
fallbackRows1h: rowsByInterval["1h"]
|
|
1740
|
-
})
|
|
1741
|
-
});
|
|
1742
|
-
var getDerivativesContextReferenceSymbols = () => [
|
|
1743
|
-
...resolveDerivativesContextReferenceSymbols(
|
|
1744
|
-
process.env.DERIVATIVES_CONTEXT_EXTRA_REFERENCE_SYMBOLS
|
|
1745
|
-
)
|
|
1746
|
-
];
|
|
1747
|
-
var normalizeSymbol = (symbol) => String(symbol || "").trim().toUpperCase();
|
|
1748
|
-
var getSignalPriceChangePct1h = (signal) => {
|
|
1749
|
-
const baseContext = signal.additionalIndicators?.baseContext;
|
|
1750
|
-
if (!baseContext || typeof baseContext !== "object" || Array.isArray(baseContext)) {
|
|
1751
|
-
return null;
|
|
1752
|
-
}
|
|
1753
|
-
const raw = typeof baseContext.raw === "object" && baseContext.raw && !Array.isArray(baseContext.raw) ? baseContext.raw : null;
|
|
1754
|
-
const price = raw && typeof raw.price === "object" && raw.price && !Array.isArray(raw.price) ? raw.price : null;
|
|
1755
|
-
const value = price?.price1hPct;
|
|
1756
|
-
const numeric = typeof value === "number" ? value : Number(value);
|
|
1757
|
-
return Number.isFinite(numeric) ? numeric : null;
|
|
1758
|
-
};
|
|
1759
|
-
var resolvePrimaryReferenceSymbol2 = () => PRIMARY_DERIVATIVES_REFERENCE_SYMBOL;
|
|
1760
|
-
var resolveSecondaryReferenceSymbol = () => SECONDARY_DERIVATIVES_REFERENCE_SYMBOL;
|
|
1761
|
-
var getPrimaryIntervalContext = (context) => context?.intervals["15m"] ?? context?.intervals["1h"] ?? null;
|
|
1762
|
-
var hasDerivativesSymbolData = (context) => Object.keys(context.intervals).length > 0 && !context.summary.riskFlags.includes("missing_derivatives");
|
|
1763
|
-
var toFiniteNumberOrNull3 = (value) => {
|
|
1764
|
-
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
1765
|
-
if (typeof value === "string" && value.trim()) {
|
|
1766
|
-
const parsed = Number(value);
|
|
1767
|
-
return Number.isFinite(parsed) ? parsed : null;
|
|
1768
|
-
}
|
|
1769
|
-
return null;
|
|
1770
|
-
};
|
|
1771
|
-
var roundNullable = (value, digits = 4) => {
|
|
1772
|
-
if (value == null || !Number.isFinite(value)) return null;
|
|
1773
|
-
const multiplier = 10 ** digits;
|
|
1774
|
-
return Math.round(value * multiplier) / multiplier;
|
|
1775
|
-
};
|
|
1776
|
-
var deltaNullable = (targetValue, referenceValue) => {
|
|
1777
|
-
const target = toFiniteNumberOrNull3(targetValue);
|
|
1778
|
-
const reference = toFiniteNumberOrNull3(referenceValue);
|
|
1779
|
-
return target == null || reference == null ? null : roundNullable(target - reference);
|
|
1780
|
-
};
|
|
1781
|
-
var buildTargetDerivedContext = (params) => {
|
|
1782
|
-
const { targetContext, primaryReferenceContext } = params;
|
|
1783
|
-
const targetPrimary = getPrimaryIntervalContext(targetContext);
|
|
1784
|
-
const referencePrimary = getPrimaryIntervalContext(primaryReferenceContext);
|
|
1785
|
-
const targetDirectionAligned = targetContext.summary.directionAligned;
|
|
1786
|
-
const referenceDirectionAligned = primaryReferenceContext?.summary.directionAligned ?? null;
|
|
1787
|
-
return {
|
|
1788
|
-
available: hasDerivativesSymbolData(targetContext),
|
|
1789
|
-
stale: targetContext.summary.riskFlags.includes("stale_derivatives") || targetPrimary?.stale === true ? true : targetPrimary == null ? null : false,
|
|
1790
|
-
sourceSymbol: targetContext.symbol,
|
|
1791
|
-
referenceSymbol: primaryReferenceContext?.symbol ?? null,
|
|
1792
|
-
directionAligned: targetDirectionAligned,
|
|
1793
|
-
referenceDirectionAligned,
|
|
1794
|
-
pressure: targetContext.summary.pressure ?? null,
|
|
1795
|
-
referencePressure: primaryReferenceContext?.summary.pressure ?? null,
|
|
1796
|
-
riskFlags: targetContext.summary.riskFlags,
|
|
1797
|
-
oiChangePct1h: targetPrimary?.oiChangePct1h ?? null,
|
|
1798
|
-
oiAcceleration: targetContext.summary.oiAcceleration ?? null,
|
|
1799
|
-
fundingRate: targetPrimary?.fundingRate ?? null,
|
|
1800
|
-
fundingZScore: targetPrimary?.fundingZScore ?? null,
|
|
1801
|
-
fundingChange1h: targetContext.summary.fundingChange1h ?? null,
|
|
1802
|
-
liqSpikeRatio: targetPrimary?.liqSpikeRatio ?? null,
|
|
1803
|
-
liqImbalance: targetPrimary?.liqImbalance ?? null,
|
|
1804
|
-
targetVsPrimaryOiChangePct1hDelta: deltaNullable(
|
|
1805
|
-
targetPrimary?.oiChangePct1h,
|
|
1806
|
-
referencePrimary?.oiChangePct1h
|
|
1807
|
-
),
|
|
1808
|
-
targetVsPrimaryFundingZScoreDelta: deltaNullable(
|
|
1809
|
-
targetPrimary?.fundingZScore,
|
|
1810
|
-
referencePrimary?.fundingZScore
|
|
1811
|
-
),
|
|
1812
|
-
targetReferenceConflict: targetDirectionAligned == null || referenceDirectionAligned == null ? null : targetDirectionAligned !== referenceDirectionAligned
|
|
1813
|
-
};
|
|
1814
|
-
};
|
|
1815
|
-
var buildReferenceDerivativesContext = (params) => {
|
|
1816
|
-
const {
|
|
1817
|
-
targetSymbol,
|
|
1818
|
-
primaryReferenceSymbol,
|
|
1819
|
-
secondaryReferenceSymbol,
|
|
1820
|
-
referenceSymbols,
|
|
1821
|
-
referenceContexts,
|
|
1822
|
-
targetContext
|
|
1823
|
-
} = params;
|
|
1824
|
-
const primaryContext = referenceContexts[primaryReferenceSymbol] ?? referenceContexts[referenceSymbols[0]];
|
|
1825
|
-
if (!primaryContext) {
|
|
1826
|
-
throw new Error("No derivatives reference contexts built");
|
|
1827
|
-
}
|
|
1828
|
-
const referenceSymbolsMetadata = [
|
|
1829
|
-
.../* @__PURE__ */ new Set([
|
|
1830
|
-
primaryReferenceSymbol,
|
|
1831
|
-
secondaryReferenceSymbol,
|
|
1832
|
-
...referenceSymbols,
|
|
1833
|
-
...Object.keys(referenceContexts)
|
|
1834
|
-
])
|
|
1835
|
-
];
|
|
1836
|
-
const targetDerived = targetContext && hasDerivativesSymbolData(targetContext) ? buildTargetDerivedContext({
|
|
1837
|
-
targetContext,
|
|
1838
|
-
primaryReferenceContext: primaryContext
|
|
1839
|
-
}) : void 0;
|
|
1840
|
-
return {
|
|
1841
|
-
...primaryContext,
|
|
1842
|
-
targetSymbol,
|
|
1843
|
-
primaryReferenceSymbol: primaryContext.symbol,
|
|
1844
|
-
secondaryReferenceSymbol: referenceContexts[secondaryReferenceSymbol]?.symbol ?? secondaryReferenceSymbol,
|
|
1845
|
-
referenceSymbols: referenceSymbolsMetadata,
|
|
1846
|
-
referenceContexts,
|
|
1847
|
-
...targetContext && targetDerived ? {
|
|
1848
|
-
targetContext,
|
|
1849
|
-
targetDerived
|
|
1850
|
-
} : {}
|
|
1851
|
-
};
|
|
1852
|
-
};
|
|
1853
|
-
var isDerivativesContextEnabled = (env) => parseEnabledFlag4(process.env.DERIVATIVES_CONTEXT_ENABLED, env);
|
|
1854
|
-
var isDerivativesTargetContextEnabled = () => parseBooleanFlag(process.env.DERIVATIVES_CONTEXT_TARGET_ENABLED, false);
|
|
1855
|
-
var enrichSignalWithDerivativesContext = async (params) => {
|
|
1856
|
-
const { signal, env, enabled = isDerivativesContextEnabled(env) } = params;
|
|
1857
|
-
if (signal.universe === "tradfi" || !enabled || derivativesContextUnavailable) {
|
|
1858
|
-
return false;
|
|
1859
|
-
}
|
|
1860
|
-
try {
|
|
1861
|
-
const referenceSymbols = getDerivativesContextReferenceSymbols();
|
|
1862
|
-
const targetSymbol = normalizeSymbol(signal.symbol);
|
|
1863
|
-
const lookbackMs = parseLookbackMs();
|
|
1864
|
-
const decisionTimeMs = signal.timestamp + intervalToMs2(signal.interval);
|
|
1865
|
-
const derivativesEndMs = getLastClosedDerivativesBarStartMs(
|
|
1866
|
-
decisionTimeMs,
|
|
1867
|
-
"15m"
|
|
1868
|
-
);
|
|
1869
|
-
const contexts = await Promise.all(
|
|
1870
|
-
referenceSymbols.map(async (symbol) => {
|
|
1871
|
-
const rowsByInterval = await getDerivativesWindow({
|
|
1872
|
-
symbol,
|
|
1873
|
-
intervals: STORED_INTERVALS,
|
|
1874
|
-
endMs: derivativesEndMs,
|
|
1875
|
-
lookbackMs,
|
|
1876
|
-
...params.abortSignal ? { signal: params.abortSignal } : {}
|
|
1877
|
-
});
|
|
1878
|
-
return [
|
|
1879
|
-
symbol,
|
|
1880
|
-
buildDerivativesContext({
|
|
1881
|
-
symbol,
|
|
1882
|
-
direction: signal.direction,
|
|
1883
|
-
timestamp: derivativesEndMs,
|
|
1884
|
-
rowsByInterval: withHourlyFallbackRows(rowsByInterval),
|
|
1885
|
-
priceChangePct1h: getSignalPriceChangePct1h(signal),
|
|
1886
|
-
intervals: CONTEXT_INTERVALS
|
|
1887
|
-
})
|
|
1888
|
-
];
|
|
1889
|
-
})
|
|
1890
|
-
);
|
|
1891
|
-
const referenceContexts = Object.fromEntries(contexts);
|
|
1892
|
-
const primaryReferenceSymbol = resolvePrimaryReferenceSymbol2();
|
|
1893
|
-
const secondaryReferenceSymbol = resolveSecondaryReferenceSymbol();
|
|
1894
|
-
const targetContextEnabled = isDerivativesTargetContextEnabled();
|
|
1895
|
-
const referenceTargetContext = targetContextEnabled && targetSymbol !== primaryReferenceSymbol ? referenceContexts[targetSymbol] : void 0;
|
|
1896
|
-
const shouldFetchTargetContext = targetContextEnabled && targetSymbol.length > 0 && !referenceSymbols.some(
|
|
1897
|
-
(referenceSymbol) => referenceSymbol === targetSymbol
|
|
1898
|
-
);
|
|
1899
|
-
const fetchedTargetContext = shouldFetchTargetContext ? await (async () => {
|
|
1900
|
-
const rowsByInterval = await getDerivativesWindow({
|
|
1901
|
-
symbol: targetSymbol,
|
|
1902
|
-
intervals: STORED_INTERVALS,
|
|
1903
|
-
endMs: derivativesEndMs,
|
|
1904
|
-
lookbackMs,
|
|
1905
|
-
...params.abortSignal ? { signal: params.abortSignal } : {}
|
|
1906
|
-
});
|
|
1907
|
-
const context = buildDerivativesContext({
|
|
1908
|
-
symbol: targetSymbol,
|
|
1909
|
-
direction: signal.direction,
|
|
1910
|
-
timestamp: derivativesEndMs,
|
|
1911
|
-
rowsByInterval: withHourlyFallbackRows(rowsByInterval),
|
|
1912
|
-
priceChangePct1h: getSignalPriceChangePct1h(signal),
|
|
1913
|
-
intervals: CONTEXT_INTERVALS
|
|
1914
|
-
});
|
|
1915
|
-
return hasDerivativesSymbolData(context) ? context : void 0;
|
|
1916
|
-
})() : void 0;
|
|
1917
|
-
const targetContext = referenceTargetContext && hasDerivativesSymbolData(referenceTargetContext) ? referenceTargetContext : fetchedTargetContext;
|
|
1918
|
-
const derivativesContext = buildReferenceDerivativesContext({
|
|
1919
|
-
targetSymbol: targetSymbol || signal.symbol,
|
|
1920
|
-
primaryReferenceSymbol,
|
|
1921
|
-
secondaryReferenceSymbol,
|
|
1922
|
-
referenceSymbols,
|
|
1923
|
-
referenceContexts,
|
|
1924
|
-
targetContext
|
|
1925
|
-
});
|
|
1926
|
-
signal.additionalIndicators = {
|
|
1927
|
-
...signal.additionalIndicators ?? {},
|
|
1928
|
-
baseContext: signal.additionalIndicators?.baseContext && typeof signal.additionalIndicators.baseContext === "object" && !Array.isArray(signal.additionalIndicators.baseContext) ? {
|
|
1929
|
-
...signal.additionalIndicators.baseContext,
|
|
1930
|
-
derivatives: derivativesContext
|
|
1931
|
-
} : signal.additionalIndicators?.baseContext
|
|
1932
|
-
};
|
|
1933
|
-
refreshSignalBaseContextGateFeatures4(signal);
|
|
1934
|
-
return true;
|
|
1935
|
-
} catch (error) {
|
|
1936
|
-
if (isMarketContextCancellationError(error, params.abortSignal)) {
|
|
1937
|
-
throw error;
|
|
1938
|
-
}
|
|
1939
|
-
derivativesContextUnavailable = true;
|
|
1940
|
-
logger4.warn(
|
|
1941
|
-
"Derivatives context disabled after Timescale read failure: %s",
|
|
1942
|
-
String(error)
|
|
1943
|
-
);
|
|
1944
|
-
return false;
|
|
1945
|
-
}
|
|
1946
|
-
};
|
|
1947
|
-
|
|
1948
|
-
// src/strategyHelpers/marketContextStages.ts
|
|
1949
|
-
var STAGE_ENV_KEYS = {
|
|
1950
|
-
binance: "BINANCE_MARKET_CONTEXT_STAGE_TIMEOUT_MS",
|
|
1951
|
-
coinmarketcap: "COINMARKETCAP_CONTEXT_STAGE_TIMEOUT_MS",
|
|
1952
|
-
derivatives: "DERIVATIVES_CONTEXT_STAGE_TIMEOUT_MS",
|
|
1953
|
-
hyperliquidWhales: "HYPERLIQUID_WHALE_CONTEXT_STAGE_TIMEOUT_MS"
|
|
1954
|
-
};
|
|
1955
|
-
var parsePositiveInt = (value) => {
|
|
1956
|
-
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
1957
|
-
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
1958
|
-
};
|
|
1959
|
-
var resolveMarketContextStageTimeoutMs = (stage) => parsePositiveInt(process.env[STAGE_ENV_KEYS[stage]]) ?? parsePositiveInt(process.env.MARKET_CONTEXT_STAGE_TIMEOUT_MS) ?? 35e3;
|
|
1960
|
-
var runMarketContextStage = async ({
|
|
1961
|
-
stage,
|
|
1962
|
-
parentSignal,
|
|
1963
|
-
operation,
|
|
1964
|
-
onStart,
|
|
1965
|
-
onComplete
|
|
1966
|
-
}) => {
|
|
1967
|
-
const controller = new AbortController();
|
|
1968
|
-
const timeoutMs = resolveMarketContextStageTimeoutMs(stage);
|
|
1969
|
-
const startedAt = Date.now();
|
|
1970
|
-
const onParentAbort = () => controller.abort(parentSignal?.reason);
|
|
1971
|
-
parentSignal?.addEventListener("abort", onParentAbort, { once: true });
|
|
1972
|
-
if (parentSignal?.aborted) onParentAbort();
|
|
1973
|
-
onStart?.(stage);
|
|
1974
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
1975
|
-
timer.unref?.();
|
|
1976
|
-
try {
|
|
1977
|
-
let available = false;
|
|
1978
|
-
try {
|
|
1979
|
-
available = await operation(controller.signal);
|
|
1980
|
-
} catch (error) {
|
|
1981
|
-
if (isMarketContextCancellationError(error)) {
|
|
1982
|
-
controller.abort(error);
|
|
1983
|
-
}
|
|
1984
|
-
if (!controller.signal.aborted) throw error;
|
|
1985
|
-
}
|
|
1986
|
-
const status = controller.signal.aborted ? "timed_out" : available ? "available" : "absent";
|
|
1987
|
-
const result = {
|
|
1988
|
-
stage,
|
|
1989
|
-
status,
|
|
1990
|
-
elapsedMs: Date.now() - startedAt
|
|
1991
|
-
};
|
|
1992
|
-
if (status === "timed_out") {
|
|
1993
|
-
logger5.warn(
|
|
1994
|
-
"Market context stage timed out: %s after %sms",
|
|
1995
|
-
stage,
|
|
1996
|
-
result.elapsedMs
|
|
1997
|
-
);
|
|
1998
|
-
}
|
|
1999
|
-
onComplete?.(result);
|
|
2000
|
-
return result;
|
|
2001
|
-
} finally {
|
|
2002
|
-
clearTimeout(timer);
|
|
2003
|
-
parentSignal?.removeEventListener("abort", onParentAbort);
|
|
2004
|
-
}
|
|
2005
|
-
};
|
|
2006
|
-
var enrichSignalWithMarketContextStages = async ({
|
|
2007
|
-
signal,
|
|
2008
|
-
env,
|
|
2009
|
-
coinMarketCapEnabled,
|
|
2010
|
-
includeHyperliquidWhales = true,
|
|
2011
|
-
abortSignal,
|
|
2012
|
-
onStageStart,
|
|
2013
|
-
onStageComplete
|
|
2014
|
-
}) => {
|
|
2015
|
-
const stages = [
|
|
2016
|
-
{
|
|
2017
|
-
stage: "binance",
|
|
2018
|
-
operation: (stageSignal) => enrichSignalWithBinanceMarketContext({
|
|
2019
|
-
signal,
|
|
2020
|
-
env,
|
|
2021
|
-
abortSignal: stageSignal
|
|
2022
|
-
})
|
|
2023
|
-
},
|
|
2024
|
-
{
|
|
2025
|
-
stage: "coinmarketcap",
|
|
2026
|
-
operation: (stageSignal) => enrichSignalWithCoinMarketCapContext({
|
|
2027
|
-
signal,
|
|
2028
|
-
env,
|
|
2029
|
-
enabled: coinMarketCapEnabled,
|
|
2030
|
-
abortSignal: stageSignal
|
|
2031
|
-
})
|
|
2032
|
-
},
|
|
2033
|
-
{
|
|
2034
|
-
stage: "derivatives",
|
|
2035
|
-
operation: (stageSignal) => enrichSignalWithDerivativesContext({
|
|
2036
|
-
signal,
|
|
2037
|
-
env,
|
|
2038
|
-
abortSignal: stageSignal
|
|
2039
|
-
})
|
|
2040
|
-
}
|
|
2041
|
-
];
|
|
2042
|
-
if (includeHyperliquidWhales) {
|
|
2043
|
-
stages.push({
|
|
2044
|
-
stage: "hyperliquidWhales",
|
|
2045
|
-
operation: (stageSignal) => enrichSignalWithHyperliquidWhaleContext({
|
|
2046
|
-
signal,
|
|
2047
|
-
env,
|
|
2048
|
-
abortSignal: stageSignal
|
|
2049
|
-
})
|
|
2050
|
-
});
|
|
2051
|
-
}
|
|
2052
|
-
const results = [];
|
|
2053
|
-
for (const stage of stages) {
|
|
2054
|
-
if (abortSignal?.aborted) break;
|
|
2055
|
-
results.push(
|
|
2056
|
-
await runMarketContextStage({
|
|
2057
|
-
...stage,
|
|
2058
|
-
parentSignal: abortSignal,
|
|
2059
|
-
onStart: onStageStart,
|
|
2060
|
-
onComplete: onStageComplete
|
|
2061
|
-
})
|
|
2062
|
-
);
|
|
2063
|
-
}
|
|
2064
|
-
return results;
|
|
2065
|
-
};
|
|
2066
|
-
|
|
2067
|
-
// src/strategyAdapters/ml.ts
|
|
2068
|
-
var defaultMlAdapter = {
|
|
2069
|
-
normalizeStrategyConfig: (strategyConfig) => strategyConfig
|
|
2070
|
-
};
|
|
2071
|
-
var getStrategyMlAdapter = (strategy, profileId) => {
|
|
2072
|
-
const strategyAdapter = getStrategyProfileMlAdapter(
|
|
2073
|
-
getStrategyManifest(strategy),
|
|
2074
|
-
profileId
|
|
2075
|
-
);
|
|
2076
|
-
if (!strategyAdapter) return defaultMlAdapter;
|
|
2077
|
-
return {
|
|
2078
|
-
...defaultMlAdapter,
|
|
2079
|
-
...strategyAdapter
|
|
2080
|
-
};
|
|
2081
|
-
};
|
|
2082
|
-
|
|
2083
|
-
// src/mlPayload.ts
|
|
2084
|
-
var normalizeStrategyConfig = (strategyConfig, strategyName, profileId) => {
|
|
2085
|
-
return getStrategyMlAdapter(
|
|
2086
|
-
strategyName,
|
|
2087
|
-
profileId
|
|
2088
|
-
).normalizeStrategyConfig?.(strategyConfig);
|
|
2089
|
-
};
|
|
2090
|
-
var buildMlPayload = (payload) => {
|
|
2091
|
-
const strategyName = payload.signal?.strategy ?? payload.context?.strategyName;
|
|
2092
|
-
const profileId = payload.signal?.policyProfileId;
|
|
2093
|
-
const mlAdapter = getStrategyMlAdapter(strategyName, profileId);
|
|
2094
|
-
const normalizedSignal = mlAdapter.normalizeSignal?.(payload.signal) ?? payload.signal;
|
|
2095
|
-
const nextSignal = {
|
|
2096
|
-
...normalizedSignal,
|
|
2097
|
-
indicators: {
|
|
2098
|
-
...normalizedSignal?.indicators ?? {}
|
|
2099
|
-
}
|
|
2100
|
-
};
|
|
2101
|
-
const nextContext = payload.context ? {
|
|
2102
|
-
...payload.context,
|
|
2103
|
-
strategyConfig: normalizeStrategyConfig(
|
|
2104
|
-
payload.context.strategyConfig,
|
|
2105
|
-
strategyName,
|
|
2106
|
-
profileId
|
|
2107
|
-
)
|
|
2108
|
-
} : void 0;
|
|
2109
|
-
return {
|
|
2110
|
-
signal: nextSignal,
|
|
2111
|
-
context: nextContext
|
|
2112
|
-
};
|
|
2113
|
-
};
|
|
2114
|
-
|
|
2115
|
-
export {
|
|
2116
|
-
BINANCE_BREADTH_UNIVERSE_KEYS,
|
|
2117
|
-
buildBinanceBreadthUniverseSnapshot,
|
|
2118
|
-
getBinanceBreadthUniverseSnapshot,
|
|
2119
|
-
getBinanceBreadthUniverses,
|
|
2120
|
-
getPrimaryBinanceBreadthUniverse,
|
|
2121
|
-
enrichSignalWithBinanceMarketContext,
|
|
2122
|
-
enrichSignalWithCoinMarketCapContext,
|
|
2123
|
-
getHyperliquidPerpUniverseSnapshot,
|
|
2124
|
-
getHyperliquidWhaleRegistrySnapshot,
|
|
2125
|
-
getHyperliquidPerpSymbols,
|
|
2126
|
-
getHyperliquidWhaleAddresses,
|
|
2127
|
-
isTrackedHyperliquidPerp,
|
|
2128
|
-
isTrackedHyperliquidWhale,
|
|
2129
|
-
resolveHyperliquidPerpFromSignalSymbol,
|
|
2130
|
-
loadHyperliquidWhaleFlowContext,
|
|
2131
|
-
enrichSignalWithHyperliquidWhaleContext,
|
|
2132
|
-
enrichSignalWithMarketContextStages,
|
|
2133
|
-
buildMlPayload
|
|
2134
|
-
};
|