@tradejs/node 2.0.6 → 2.0.8
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 +47 -1
- package/dist/ai.mjs +1 -1
- package/dist/backtest.js +486 -84
- package/dist/backtest.mjs +30 -5
- package/dist/{chunk-2DZCHRP6.mjs → chunk-76TEMQKX.mjs} +418 -80
- package/dist/{chunk-IUZML4RK.mjs → chunk-PD25CABK.mjs} +47 -1
- package/dist/cli.js +47 -1
- package/dist/cli.mjs +1 -1
- package/dist/strategies.d.mts +49 -1
- package/dist/strategies.d.ts +49 -1
- package/dist/strategies.js +541 -90
- package/dist/strategies.mjs +78 -8
- package/package.json +4 -4
package/dist/backtest.mjs
CHANGED
|
@@ -7,10 +7,10 @@ import {
|
|
|
7
7
|
enrichSignalWithBinanceMarketContext,
|
|
8
8
|
enrichSignalWithCoinMarketCapContext,
|
|
9
9
|
enrichSignalWithDerivativesContext
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-76TEMQKX.mjs";
|
|
11
11
|
import {
|
|
12
12
|
buildAiPayload
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-PD25CABK.mjs";
|
|
14
14
|
import {
|
|
15
15
|
getStrategyCreator
|
|
16
16
|
} from "./chunk-QVSMINLG.mjs";
|
|
@@ -23,6 +23,9 @@ import "./chunk-UV2HVMKZ.mjs";
|
|
|
23
23
|
export * from "@tradejs/core/backtest";
|
|
24
24
|
|
|
25
25
|
// src/testing.ts
|
|
26
|
+
import {
|
|
27
|
+
BACKTEST_WARNING_CODES
|
|
28
|
+
} from "@tradejs/types";
|
|
26
29
|
import { alignSortedCandlesByTimestamp } from "@tradejs/core/indicators";
|
|
27
30
|
import {
|
|
28
31
|
BACKTEST_EXECUTION_INTERVAL,
|
|
@@ -985,6 +988,16 @@ var resolveExecutionCosts = async (params) => {
|
|
|
985
988
|
var isBacktestEntryDelayControlCode = (value) => typeof value === "string" && value.startsWith("BACKTEST_ENTRY_DELAY_");
|
|
986
989
|
var CLOSED_RESULT_FLUSH_INTERVAL = 500;
|
|
987
990
|
var DEFAULT_STRATEGY_CANDLE_TIMEOUT_MS = 6e4;
|
|
991
|
+
var createBacktestWarningCounts = () => ({
|
|
992
|
+
[BACKTEST_WARNING_CODES.TAKE_PROFIT_CROSSED_BEFORE_ENTRY]: 0
|
|
993
|
+
});
|
|
994
|
+
var recordBacktestSignalWarning = (warningCounts, signal) => {
|
|
995
|
+
if (!signal || typeof signal === "string" || signal.orderStatus !== "failed" || signal.orderFailureReason !== BACKTEST_WARNING_CODES.TAKE_PROFIT_CROSSED_BEFORE_ENTRY) {
|
|
996
|
+
return;
|
|
997
|
+
}
|
|
998
|
+
const code = BACKTEST_WARNING_CODES.TAKE_PROFIT_CROSSED_BEFORE_ENTRY;
|
|
999
|
+
warningCounts[code] = (warningCounts[code] ?? 0) + 1;
|
|
1000
|
+
};
|
|
988
1001
|
var resolvePositiveInt = (value, fallback) => {
|
|
989
1002
|
const parsed = parseInt(String(value ?? ""), 10);
|
|
990
1003
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
@@ -1818,6 +1831,7 @@ var testing = async ({
|
|
|
1818
1831
|
);
|
|
1819
1832
|
const pendingMlPayloadBySignalId = /* @__PURE__ */ new Map();
|
|
1820
1833
|
const pendingAiRowBySignalId = /* @__PURE__ */ new Map();
|
|
1834
|
+
const warningCounts = createBacktestWarningCounts();
|
|
1821
1835
|
const replaySignalEvaluations = collectReplaySignalEvaluations ? [] : null;
|
|
1822
1836
|
const flushClosedResultsBatch = async () => {
|
|
1823
1837
|
if (!ml && !ai) return;
|
|
@@ -1862,6 +1876,7 @@ var testing = async ({
|
|
|
1862
1876
|
}
|
|
1863
1877
|
};
|
|
1864
1878
|
const processSignal = async (signal, candle) => {
|
|
1879
|
+
recordBacktestSignalWarning(warningCounts, signal);
|
|
1865
1880
|
if (isBacktestEntryDelayControlCode(signal)) {
|
|
1866
1881
|
return;
|
|
1867
1882
|
}
|
|
@@ -1970,10 +1985,14 @@ var testing = async ({
|
|
|
1970
1985
|
}
|
|
1971
1986
|
await withTimeout("flush closed results", flushClosedResultsBatch());
|
|
1972
1987
|
const result = await withTimeout("collect result", testConnector.getResult());
|
|
1973
|
-
|
|
1988
|
+
const resultWithWarnings = {
|
|
1974
1989
|
...result,
|
|
1990
|
+
warningCounts
|
|
1991
|
+
};
|
|
1992
|
+
return replaySignalEvaluations ? {
|
|
1993
|
+
...resultWithWarnings,
|
|
1975
1994
|
inlineReplaySignalEvaluations: replaySignalEvaluations
|
|
1976
|
-
} :
|
|
1995
|
+
} : resultWithWarnings;
|
|
1977
1996
|
};
|
|
1978
1997
|
var testingGroupInSharedCandleLoop = async (tests) => {
|
|
1979
1998
|
if (!canRunTestsInSharedCandleLoop(tests)) {
|
|
@@ -2227,6 +2246,7 @@ var testingGroupInSharedCandleLoop = async (tests) => {
|
|
|
2227
2246
|
testConnector,
|
|
2228
2247
|
pendingMlPayloadBySignalId: /* @__PURE__ */ new Map(),
|
|
2229
2248
|
pendingAiRowBySignalId: /* @__PURE__ */ new Map(),
|
|
2249
|
+
warningCounts: createBacktestWarningCounts(),
|
|
2230
2250
|
replaySignalEvaluations: collectReplaySignalEvaluations ? [] : null
|
|
2231
2251
|
});
|
|
2232
2252
|
}
|
|
@@ -2279,6 +2299,7 @@ var testingGroupInSharedCandleLoop = async (tests) => {
|
|
|
2279
2299
|
}
|
|
2280
2300
|
};
|
|
2281
2301
|
const processRunnerSignal = async (runner, signal, candle) => {
|
|
2302
|
+
recordBacktestSignalWarning(runner.warningCounts, signal);
|
|
2282
2303
|
if (isBacktestEntryDelayControlCode(signal)) {
|
|
2283
2304
|
return;
|
|
2284
2305
|
}
|
|
@@ -2420,8 +2441,12 @@ var testingGroupInSharedCandleLoop = async (tests) => {
|
|
|
2420
2441
|
test: runner.test,
|
|
2421
2442
|
result: runner.replaySignalEvaluations ? {
|
|
2422
2443
|
...result,
|
|
2444
|
+
warningCounts: runner.warningCounts,
|
|
2423
2445
|
inlineReplaySignalEvaluations: runner.replaySignalEvaluations
|
|
2424
|
-
} :
|
|
2446
|
+
} : {
|
|
2447
|
+
...result,
|
|
2448
|
+
warningCounts: runner.warningCounts
|
|
2449
|
+
}
|
|
2425
2450
|
});
|
|
2426
2451
|
}
|
|
2427
2452
|
return results;
|
|
@@ -1,10 +1,315 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getStrategyProfileMlAdapter
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-PD25CABK.mjs";
|
|
4
4
|
import {
|
|
5
5
|
getStrategyManifest
|
|
6
6
|
} from "./chunk-QVSMINLG.mjs";
|
|
7
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
|
+
|
|
8
313
|
// src/strategyHelpers/binanceMarketContext.ts
|
|
9
314
|
import {
|
|
10
315
|
getLatestMarketBreadth,
|
|
@@ -43,7 +348,6 @@ var signalIntervalToMarketInterval = (value) => {
|
|
|
43
348
|
return "15m";
|
|
44
349
|
};
|
|
45
350
|
var resolveMarketInterval = (signal, override) => override ?? signalIntervalToMarketInterval(signal.interval);
|
|
46
|
-
var resolveBreadthUniverse = () => (process.env.BINANCE_MARKET_CONTEXT_BREADTH_UNIVERSE || "binance_top30_usdt").trim().toLowerCase();
|
|
47
351
|
var getReferenceSymbols = () => {
|
|
48
352
|
const symbols = (process.env.BINANCE_MARKET_CONTEXT_REFERENCE_SYMBOLS || "BTCUSDT,ETHUSDT").split(",").map((item) => item.trim().toUpperCase()).filter(Boolean);
|
|
49
353
|
return symbols.length ? [...new Set(symbols)] : ["BTCUSDT", "ETHUSDT"];
|
|
@@ -116,13 +420,30 @@ var getCachedBreadth = ({
|
|
|
116
420
|
breadthCache.set(key, promise);
|
|
117
421
|
return promise;
|
|
118
422
|
};
|
|
423
|
+
var toMarketBreadthContext = (breadth, interval) => ({
|
|
424
|
+
source: "binance_klines",
|
|
425
|
+
universe: breadth.universe,
|
|
426
|
+
interval,
|
|
427
|
+
asOfTs: breadth.ts.getTime(),
|
|
428
|
+
ageMs: breadth.ageMs,
|
|
429
|
+
stale: breadth.stale,
|
|
430
|
+
symbolsCount: toFiniteNumberOrNull(breadth.symbolsCount),
|
|
431
|
+
advancers: toFiniteNumberOrNull(breadth.advancers),
|
|
432
|
+
decliners: toFiniteNumberOrNull(breadth.decliners),
|
|
433
|
+
unchanged: toFiniteNumberOrNull(breadth.unchanged),
|
|
434
|
+
advanceDeclineRatio: toFiniteNumberOrNull(breadth.advanceDeclineRatio),
|
|
435
|
+
pctAboveMa20: toFiniteNumberOrNull(breadth.pctAboveMa20),
|
|
436
|
+
pctAboveMa50: toFiniteNumberOrNull(breadth.pctAboveMa50),
|
|
437
|
+
equalWeightedReturn: toFiniteNumberOrNull(breadth.equalWeightedReturn),
|
|
438
|
+
volumeWeightedReturn: toFiniteNumberOrNull(breadth.volumeWeightedReturn),
|
|
439
|
+
dispersion: toFiniteNumberOrNull(breadth.dispersion)
|
|
440
|
+
});
|
|
119
441
|
var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
120
442
|
const {
|
|
121
443
|
signal,
|
|
122
444
|
env,
|
|
123
445
|
enabled = isBinanceMarketContextEnabled(env),
|
|
124
446
|
interval = resolveMarketInterval(signal, params.interval),
|
|
125
|
-
breadthUniverse = resolveBreadthUniverse(),
|
|
126
447
|
maxAgeMs = DEFAULT_MAX_AGE_BY_INTERVAL[interval]
|
|
127
448
|
} = params;
|
|
128
449
|
if (signal.universe === "tradfi" || !enabled || binanceMarketContextUnavailable || !hasBaseContext(signal)) {
|
|
@@ -131,26 +452,49 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
131
452
|
try {
|
|
132
453
|
const referenceSymbols = getReferenceSymbols();
|
|
133
454
|
const primaryReferenceSymbol = resolvePrimaryReferenceSymbol(signal.symbol);
|
|
134
|
-
const
|
|
455
|
+
const breadthUniverses = params.breadthUniverse ? [
|
|
456
|
+
{
|
|
457
|
+
key: "top30",
|
|
458
|
+
universe: params.breadthUniverse
|
|
459
|
+
}
|
|
460
|
+
] : getBinanceBreadthUniverses();
|
|
461
|
+
const [referenceRows, breadthRows] = await Promise.all([
|
|
135
462
|
getCachedReferenceRows({
|
|
136
463
|
referenceSymbols,
|
|
137
464
|
interval,
|
|
138
465
|
timestamp: signal.timestamp,
|
|
139
466
|
maxAgeMs
|
|
140
467
|
}),
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
468
|
+
Promise.all(
|
|
469
|
+
breadthUniverses.map(async ({ key, universe }) => ({
|
|
470
|
+
key,
|
|
471
|
+
breadth: await getCachedBreadth({
|
|
472
|
+
breadthUniverse: universe,
|
|
473
|
+
interval,
|
|
474
|
+
timestamp: signal.timestamp,
|
|
475
|
+
maxAgeMs
|
|
476
|
+
})
|
|
477
|
+
}))
|
|
478
|
+
)
|
|
147
479
|
]);
|
|
480
|
+
const availableBreadths = breadthRows.filter(
|
|
481
|
+
(row) => row.breadth != null
|
|
482
|
+
);
|
|
483
|
+
const marketBreadths = Object.fromEntries(
|
|
484
|
+
availableBreadths.map(({ key, breadth }) => [
|
|
485
|
+
key,
|
|
486
|
+
toMarketBreadthContext(breadth, interval)
|
|
487
|
+
])
|
|
488
|
+
);
|
|
489
|
+
const primaryBreadth = availableBreadths.find(
|
|
490
|
+
({ key }) => key === "top30"
|
|
491
|
+
)?.breadth;
|
|
148
492
|
const tradeFlowBySymbol = Object.fromEntries(
|
|
149
493
|
referenceRows.filter((row) => row.tradeFlow).map((row) => [row.symbol, row.tradeFlow])
|
|
150
494
|
);
|
|
151
495
|
const targetReferenceSymbol = signal.symbol.trim().toUpperCase();
|
|
152
496
|
const targetTradeFlow = tradeFlowBySymbol[targetReferenceSymbol];
|
|
153
|
-
if (!Object.keys(tradeFlowBySymbol).length && !
|
|
497
|
+
if (!Object.keys(tradeFlowBySymbol).length && !availableBreadths.length) {
|
|
154
498
|
return false;
|
|
155
499
|
}
|
|
156
500
|
const baseContext = signal.additionalIndicators.baseContext;
|
|
@@ -177,76 +521,65 @@ var enrichSignalWithBinanceMarketContext = async (params) => {
|
|
|
177
521
|
tradeFlowBySymbol
|
|
178
522
|
}
|
|
179
523
|
} : {},
|
|
180
|
-
...
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
ageMs: breadth.ageMs,
|
|
187
|
-
stale: breadth.stale,
|
|
188
|
-
symbolsCount: toFiniteNumberOrNull(breadth.symbolsCount),
|
|
189
|
-
advancers: toFiniteNumberOrNull(breadth.advancers),
|
|
190
|
-
decliners: toFiniteNumberOrNull(breadth.decliners),
|
|
191
|
-
unchanged: toFiniteNumberOrNull(breadth.unchanged),
|
|
192
|
-
advanceDeclineRatio: toFiniteNumberOrNull(
|
|
193
|
-
breadth.advanceDeclineRatio
|
|
194
|
-
),
|
|
195
|
-
pctAboveMa20: toFiniteNumberOrNull(breadth.pctAboveMa20),
|
|
196
|
-
pctAboveMa50: toFiniteNumberOrNull(breadth.pctAboveMa50),
|
|
197
|
-
equalWeightedReturn: toFiniteNumberOrNull(
|
|
198
|
-
breadth.equalWeightedReturn
|
|
199
|
-
),
|
|
200
|
-
volumeWeightedReturn: toFiniteNumberOrNull(
|
|
201
|
-
breadth.volumeWeightedReturn
|
|
524
|
+
...availableBreadths.length ? {
|
|
525
|
+
marketBreadths,
|
|
526
|
+
...primaryBreadth ? {
|
|
527
|
+
marketBreadth: toMarketBreadthContext(
|
|
528
|
+
primaryBreadth,
|
|
529
|
+
interval
|
|
202
530
|
),
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
531
|
+
btcAltRegime: {
|
|
532
|
+
source: "binance_klines",
|
|
533
|
+
universe: primaryBreadth.universe,
|
|
534
|
+
interval,
|
|
535
|
+
asOfTs: primaryBreadth.ts.getTime(),
|
|
536
|
+
ageMs: primaryBreadth.ageMs,
|
|
537
|
+
stale: primaryBreadth.stale,
|
|
538
|
+
btcReturn1h: toFiniteNumberOrNull(
|
|
539
|
+
primaryBreadth.btcReturn1h
|
|
540
|
+
),
|
|
541
|
+
btcReturn4h: toFiniteNumberOrNull(
|
|
542
|
+
primaryBreadth.btcReturn4h
|
|
543
|
+
),
|
|
544
|
+
btcReturn24h: toFiniteNumberOrNull(
|
|
545
|
+
primaryBreadth.btcReturn24h
|
|
546
|
+
),
|
|
547
|
+
altBasketReturn1h: toFiniteNumberOrNull(
|
|
548
|
+
primaryBreadth.altBasketReturn1h
|
|
549
|
+
),
|
|
550
|
+
altBasketReturn4h: toFiniteNumberOrNull(
|
|
551
|
+
primaryBreadth.altBasketReturn4h
|
|
552
|
+
),
|
|
553
|
+
altBasketReturn24h: toFiniteNumberOrNull(
|
|
554
|
+
primaryBreadth.altBasketReturn24h
|
|
555
|
+
),
|
|
556
|
+
btcVsAltReturn1h: toFiniteNumberOrNull(
|
|
557
|
+
primaryBreadth.btcVsAltReturn1h
|
|
558
|
+
),
|
|
559
|
+
btcVsAltReturn4h: toFiniteNumberOrNull(
|
|
560
|
+
primaryBreadth.btcVsAltReturn4h
|
|
561
|
+
),
|
|
562
|
+
btcVsAltReturn24h: toFiniteNumberOrNull(
|
|
563
|
+
primaryBreadth.btcVsAltReturn24h
|
|
564
|
+
),
|
|
565
|
+
btcTurnoverShare1h: toFiniteNumberOrNull(
|
|
566
|
+
primaryBreadth.btcTurnoverShare1h
|
|
567
|
+
),
|
|
568
|
+
btcTurnoverShare24h: toFiniteNumberOrNull(
|
|
569
|
+
primaryBreadth.btcTurnoverShare24h
|
|
570
|
+
),
|
|
571
|
+
btcTurnoverShareChange24h: toFiniteNumberOrNull(
|
|
572
|
+
primaryBreadth.btcTurnoverShareChange24h
|
|
573
|
+
),
|
|
574
|
+
altVolToBtcVol24h: toFiniteNumberOrNull(
|
|
575
|
+
primaryBreadth.altVolToBtcVol24h
|
|
576
|
+
),
|
|
577
|
+
altDispersion24h: toFiniteNumberOrNull(
|
|
578
|
+
primaryBreadth.altDispersion24h
|
|
579
|
+
),
|
|
580
|
+
regime: primaryBreadth.btcAltRegime ?? "unknown"
|
|
581
|
+
}
|
|
582
|
+
} : {}
|
|
250
583
|
} : {}
|
|
251
584
|
}
|
|
252
585
|
}
|
|
@@ -1041,6 +1374,11 @@ var buildMlPayload = (payload) => {
|
|
|
1041
1374
|
};
|
|
1042
1375
|
|
|
1043
1376
|
export {
|
|
1377
|
+
BINANCE_BREADTH_UNIVERSE_KEYS,
|
|
1378
|
+
buildBinanceBreadthUniverseSnapshot,
|
|
1379
|
+
getBinanceBreadthUniverseSnapshot,
|
|
1380
|
+
getBinanceBreadthUniverses,
|
|
1381
|
+
getPrimaryBinanceBreadthUniverse,
|
|
1044
1382
|
enrichSignalWithBinanceMarketContext,
|
|
1045
1383
|
enrichSignalWithCoinMarketCapContext,
|
|
1046
1384
|
enrichSignalWithDerivativesContext,
|