@tradejs/node 1.0.8 → 1.0.10
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.d.mts +4 -1
- package/dist/ai.d.ts +4 -1
- package/dist/ai.js +601 -93
- package/dist/ai.mjs +5 -3
- package/dist/backtest.d.mts +26 -2
- package/dist/backtest.d.ts +26 -2
- package/dist/backtest.js +3368 -485
- package/dist/backtest.mjs +1839 -269
- package/dist/chunk-37VNDZVX.mjs +1040 -0
- package/dist/chunk-IUZML4RK.mjs +1136 -0
- package/dist/{chunk-WGOYR6AB.mjs → chunk-QVSMINLG.mjs} +1 -1
- package/dist/{chunk-JMDYEKIO.mjs → chunk-V3YMKE4I.mjs} +1 -1
- package/dist/{chunk-JU77QVJ3.mjs → chunk-WS5DYEVZ.mjs} +59 -5
- package/dist/cli.d.mts +13 -3
- package/dist/cli.d.ts +13 -3
- package/dist/cli.js +1016 -234
- package/dist/cli.mjs +338 -65
- package/dist/connectors.js +59 -5
- package/dist/connectors.mjs +2 -2
- package/dist/registry.js +59 -5
- package/dist/registry.mjs +2 -2
- package/dist/strategies.d.mts +29 -8
- package/dist/strategies.d.ts +29 -8
- package/dist/strategies.js +3417 -1285
- package/dist/strategies.mjs +892 -112
- package/package.json +6 -6
- package/dist/chunk-2JKX3DM7.mjs +0 -619
- package/dist/chunk-JRRG3YQG.mjs +0 -154
package/dist/ai.js
CHANGED
|
@@ -37,6 +37,7 @@ __export(ai_exports, {
|
|
|
37
37
|
buildAiPayload: () => buildAiPayload,
|
|
38
38
|
buildAiPrompts: () => buildAiPrompts,
|
|
39
39
|
buildAiSystemPrompt: () => buildAiSystemPrompt,
|
|
40
|
+
buildCompactAiIndicatorsSnapshot: () => buildCompactAiIndicatorsSnapshot,
|
|
40
41
|
ensureAiStrategyPluginsLoaded: () => ensureAiStrategyPluginsLoaded,
|
|
41
42
|
getDeterministicAiGateContext: () => getDeterministicAiGateContext,
|
|
42
43
|
getOpenRouterModelKwargs: () => getOpenRouterModelKwargs,
|
|
@@ -52,6 +53,10 @@ var import_userSettings = require("@tradejs/infra/userSettings");
|
|
|
52
53
|
|
|
53
54
|
// src/aiShared.ts
|
|
54
55
|
var MAX_AI_SERIES_POINTS = 5;
|
|
56
|
+
var COMPACT_INDICATORS_SNAPSHOT_SYMBOL = /* @__PURE__ */ Symbol.for(
|
|
57
|
+
"tradejs.indicators.compactSnapshot"
|
|
58
|
+
);
|
|
59
|
+
var COMPACT_INDICATORS_SNAPSHOT_KEY = "__tradejsCompactIndicatorsSnapshot";
|
|
55
60
|
var trimSeriesDeep = (value) => {
|
|
56
61
|
if (Array.isArray(value)) {
|
|
57
62
|
const trimmed = value.slice(-MAX_AI_SERIES_POINTS);
|
|
@@ -73,13 +78,15 @@ var trimSeriesDeep = (value) => {
|
|
|
73
78
|
}
|
|
74
79
|
return value;
|
|
75
80
|
};
|
|
81
|
+
var buildCompactAiIndicatorsSnapshot = (value) => {
|
|
82
|
+
const compactSnapshot = value && typeof value === "object" ? value[COMPACT_INDICATORS_SNAPSHOT_SYMBOL] ?? value[COMPACT_INDICATORS_SNAPSHOT_KEY] : void 0;
|
|
83
|
+
if (typeof compactSnapshot === "function") {
|
|
84
|
+
return compactSnapshot({ limit: MAX_AI_SERIES_POINTS });
|
|
85
|
+
}
|
|
86
|
+
return trimSeriesDeep(value);
|
|
87
|
+
};
|
|
76
88
|
|
|
77
89
|
// src/aiMarketContext.ts
|
|
78
|
-
var SESSION_WINDOWS = [
|
|
79
|
-
{ name: "asia", startMinuteUtc: 0, endMinuteUtc: 8 * 60 },
|
|
80
|
-
{ name: "europe", startMinuteUtc: 7 * 60, endMinuteUtc: 16 * 60 },
|
|
81
|
-
{ name: "us", startMinuteUtc: 13 * 60, endMinuteUtc: 22 * 60 }
|
|
82
|
-
];
|
|
83
90
|
var toRecord = (value) => {
|
|
84
91
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
85
92
|
return null;
|
|
@@ -90,92 +97,485 @@ var toFiniteNumber = (value) => {
|
|
|
90
97
|
const numeric = typeof value === "number" ? value : typeof value === "string" && value.trim() ? Number(value) : Number.NaN;
|
|
91
98
|
return Number.isFinite(numeric) ? numeric : null;
|
|
92
99
|
};
|
|
93
|
-
var getLastFiniteNumber = (value) => {
|
|
94
|
-
const numeric = toFiniteNumber(value);
|
|
95
|
-
if (numeric != null) {
|
|
96
|
-
return numeric;
|
|
97
|
-
}
|
|
98
|
-
if (!Array.isArray(value)) {
|
|
99
|
-
return null;
|
|
100
|
-
}
|
|
101
|
-
for (let i = value.length - 1; i >= 0; i -= 1) {
|
|
102
|
-
const nested = getLastFiniteNumber(value[i]);
|
|
103
|
-
if (nested != null) {
|
|
104
|
-
return nested;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
return null;
|
|
108
|
-
};
|
|
109
100
|
var roundTo = (value, decimals) => {
|
|
110
101
|
const factor = 10 ** decimals;
|
|
111
102
|
return Math.round(value * factor) / factor;
|
|
112
103
|
};
|
|
113
|
-
var isInsideSession = (minuteUtc, startMinuteUtc, endMinuteUtc) => startMinuteUtc <= endMinuteUtc ? minuteUtc >= startMinuteUtc && minuteUtc < endMinuteUtc : minuteUtc >= startMinuteUtc || minuteUtc < endMinuteUtc;
|
|
114
|
-
var buildTradingSessionContext = (timestamp) => {
|
|
115
|
-
const date = new Date(timestamp);
|
|
116
|
-
const utcHour = date.getUTCHours();
|
|
117
|
-
const utcMinute = date.getUTCMinutes();
|
|
118
|
-
const minuteUtc = utcHour * 60 + utcMinute;
|
|
119
|
-
const activeSessions = SESSION_WINDOWS.filter(
|
|
120
|
-
(session) => isInsideSession(minuteUtc, session.startMinuteUtc, session.endMinuteUtc)
|
|
121
|
-
).map((session) => session.name);
|
|
122
|
-
const primarySession = activeSessions.includes("us") ? "us" : activeSessions.includes("europe") ? "europe" : activeSessions.includes("asia") ? "asia" : "off_hours";
|
|
123
|
-
return {
|
|
124
|
-
timezone: "UTC",
|
|
125
|
-
utcHour,
|
|
126
|
-
utcMinute,
|
|
127
|
-
primarySession,
|
|
128
|
-
activeSessions,
|
|
129
|
-
isOverlap: activeSessions.length > 1,
|
|
130
|
-
overlap: activeSessions.length > 1 ? `${activeSessions.join("_")}_overlap` : null
|
|
131
|
-
};
|
|
132
|
-
};
|
|
133
104
|
var buildMissingSpreadContext = () => ({
|
|
134
|
-
source: "
|
|
135
|
-
indicatorKey: "payload.
|
|
105
|
+
source: "payload.additionalIndicators.baseContext.relative.execution.venueSpread",
|
|
106
|
+
indicatorKey: "payload.additionalIndicators.baseContext.relative.execution.venueSpread",
|
|
136
107
|
available: false,
|
|
137
108
|
value: null,
|
|
109
|
+
zScore: null,
|
|
138
110
|
bps: null,
|
|
139
111
|
absBps: null,
|
|
140
112
|
bias: null,
|
|
141
113
|
severity: null
|
|
142
114
|
});
|
|
143
|
-
var
|
|
115
|
+
var buildSpreadContextFromSignal = (signal) => {
|
|
116
|
+
const baseContext = toRecord(signal.additionalIndicators?.baseContext);
|
|
117
|
+
const relative = toRecord(baseContext?.relative);
|
|
118
|
+
const execution = toRecord(relative?.execution);
|
|
119
|
+
const spread = toFiniteNumber(execution?.venueSpread);
|
|
120
|
+
const zScore = toFiniteNumber(execution?.venueSpreadZScore);
|
|
121
|
+
if (spread == null) {
|
|
122
|
+
return buildMissingSpreadContext();
|
|
123
|
+
}
|
|
144
124
|
const value = roundTo(spread, 8);
|
|
145
125
|
const bps = roundTo(value * 1e4, 2);
|
|
146
126
|
const absBps = Math.abs(bps);
|
|
147
|
-
const bias =
|
|
127
|
+
const bias = absBps < 1 ? "flat" : bps > 0 ? "coinbase_premium" : "binance_premium";
|
|
148
128
|
const severity = absBps >= 20 ? "wide" : absBps >= 5 ? "elevated" : "normal";
|
|
149
129
|
return {
|
|
150
|
-
source: "
|
|
151
|
-
indicatorKey: "payload.
|
|
130
|
+
source: "payload.additionalIndicators.baseContext.relative.execution.venueSpread",
|
|
131
|
+
indicatorKey: "payload.additionalIndicators.baseContext.relative.execution.venueSpread",
|
|
152
132
|
available: true,
|
|
153
133
|
value,
|
|
134
|
+
zScore,
|
|
154
135
|
bps,
|
|
155
136
|
absBps,
|
|
156
137
|
bias,
|
|
157
138
|
severity
|
|
158
139
|
};
|
|
159
140
|
};
|
|
160
|
-
var
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
141
|
+
var buildTrueDeltaContextFromSignal = (signal) => {
|
|
142
|
+
const baseContext = toRecord(signal.additionalIndicators?.baseContext);
|
|
143
|
+
const participation = toRecord(baseContext?.participation);
|
|
144
|
+
const delta = toRecord(participation?.delta);
|
|
145
|
+
const source = String(delta?.source ?? "");
|
|
146
|
+
const isTrueDeltaSource = source === "kline_taker_volume" || source === "agg_trades" || source === "trades";
|
|
147
|
+
if (!delta || !isTrueDeltaSource) {
|
|
148
|
+
return {
|
|
149
|
+
source: source || null,
|
|
150
|
+
available: false,
|
|
151
|
+
buyPressurePct: null,
|
|
152
|
+
buyVolume: null,
|
|
153
|
+
sellVolume: null,
|
|
154
|
+
netDelta: null,
|
|
155
|
+
deltaPct: null,
|
|
156
|
+
signedVolumeZScore: null
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
source,
|
|
161
|
+
available: true,
|
|
162
|
+
buyPressurePct: toFiniteNumber(delta.buyPressurePct),
|
|
163
|
+
buyVolume: toFiniteNumber(delta.buyVolume),
|
|
164
|
+
sellVolume: toFiniteNumber(delta.sellVolume),
|
|
165
|
+
netDelta: toFiniteNumber(delta.netDelta),
|
|
166
|
+
deltaPct: toFiniteNumber(delta.deltaPct),
|
|
167
|
+
signedVolumeZScore: toFiniteNumber(delta.signedVolumeZScore)
|
|
168
|
+
};
|
|
169
|
+
};
|
|
170
|
+
var buildTradeFlowContextFromSignal = (signal) => {
|
|
171
|
+
const baseContext = toRecord(signal.additionalIndicators?.baseContext);
|
|
172
|
+
const participation = toRecord(baseContext?.participation);
|
|
173
|
+
const tradeFlow = toRecord(participation?.tradeFlow);
|
|
174
|
+
if (!tradeFlow) {
|
|
175
|
+
return {
|
|
176
|
+
source: null,
|
|
177
|
+
available: false,
|
|
178
|
+
interval: null,
|
|
179
|
+
stale: null,
|
|
180
|
+
trades: null,
|
|
181
|
+
buyPressurePct: null,
|
|
182
|
+
netBaseDelta: null,
|
|
183
|
+
netQuoteDelta: null
|
|
184
|
+
};
|
|
164
185
|
}
|
|
165
|
-
return
|
|
186
|
+
return {
|
|
187
|
+
source: String(tradeFlow.source ?? ""),
|
|
188
|
+
available: true,
|
|
189
|
+
interval: String(tradeFlow.interval ?? ""),
|
|
190
|
+
stale: typeof tradeFlow.stale === "boolean" ? tradeFlow.stale : null,
|
|
191
|
+
trades: toFiniteNumber(tradeFlow.trades),
|
|
192
|
+
buyPressurePct: toFiniteNumber(tradeFlow.buyPressurePct),
|
|
193
|
+
netBaseDelta: toFiniteNumber(tradeFlow.netBaseDelta),
|
|
194
|
+
netQuoteDelta: toFiniteNumber(tradeFlow.netQuoteDelta)
|
|
195
|
+
};
|
|
166
196
|
};
|
|
167
|
-
var
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
|
|
197
|
+
var buildMarketBreadthContextFromSignal = (signal) => {
|
|
198
|
+
const baseContext = toRecord(signal.additionalIndicators?.baseContext);
|
|
199
|
+
const relative = toRecord(baseContext?.relative);
|
|
200
|
+
const breadth = toRecord(relative?.marketBreadth);
|
|
201
|
+
if (!breadth) {
|
|
202
|
+
return {
|
|
203
|
+
source: null,
|
|
204
|
+
available: false,
|
|
205
|
+
universe: null,
|
|
206
|
+
interval: null,
|
|
207
|
+
stale: null,
|
|
208
|
+
symbolsCount: null,
|
|
209
|
+
advanceDeclineRatio: null,
|
|
210
|
+
pctAboveMa20: null,
|
|
211
|
+
pctAboveMa50: null,
|
|
212
|
+
equalWeightedReturn: null,
|
|
213
|
+
volumeWeightedReturn: null,
|
|
214
|
+
dispersion: null
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
source: String(breadth.source ?? ""),
|
|
219
|
+
available: true,
|
|
220
|
+
universe: String(breadth.universe ?? ""),
|
|
221
|
+
interval: String(breadth.interval ?? ""),
|
|
222
|
+
stale: typeof breadth.stale === "boolean" ? breadth.stale : null,
|
|
223
|
+
symbolsCount: toFiniteNumber(breadth.symbolsCount),
|
|
224
|
+
advanceDeclineRatio: toFiniteNumber(breadth.advanceDeclineRatio),
|
|
225
|
+
pctAboveMa20: toFiniteNumber(breadth.pctAboveMa20),
|
|
226
|
+
pctAboveMa50: toFiniteNumber(breadth.pctAboveMa50),
|
|
227
|
+
equalWeightedReturn: toFiniteNumber(breadth.equalWeightedReturn),
|
|
228
|
+
volumeWeightedReturn: toFiniteNumber(breadth.volumeWeightedReturn),
|
|
229
|
+
dispersion: toFiniteNumber(breadth.dispersion)
|
|
230
|
+
};
|
|
231
|
+
};
|
|
232
|
+
var buildTargetVsBtcContextFromSignal = (signal) => {
|
|
233
|
+
const baseContext = toRecord(signal.additionalIndicators?.baseContext);
|
|
234
|
+
const relative = toRecord(baseContext?.relative);
|
|
235
|
+
const targetVsBtc = toRecord(relative?.targetVsBtc);
|
|
236
|
+
if (!targetVsBtc) {
|
|
237
|
+
return {
|
|
238
|
+
source: null,
|
|
239
|
+
available: false,
|
|
240
|
+
ratioReturn1h: null,
|
|
241
|
+
ratioReturn4h: null,
|
|
242
|
+
ratioReturn24h: null,
|
|
243
|
+
alphaVsBtc1h: null,
|
|
244
|
+
alphaVsBtc4h: null,
|
|
245
|
+
alphaVsBtc24h: null,
|
|
246
|
+
betaToBtc20: null,
|
|
247
|
+
correlationToBtc20: null,
|
|
248
|
+
ratioTrend: null
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
return {
|
|
252
|
+
source: String(targetVsBtc.source ?? ""),
|
|
253
|
+
available: true,
|
|
254
|
+
ratioReturn1h: toFiniteNumber(targetVsBtc.ratioReturn1h),
|
|
255
|
+
ratioReturn4h: toFiniteNumber(targetVsBtc.ratioReturn4h),
|
|
256
|
+
ratioReturn24h: toFiniteNumber(targetVsBtc.ratioReturn24h),
|
|
257
|
+
alphaVsBtc1h: toFiniteNumber(targetVsBtc.alphaVsBtc1h),
|
|
258
|
+
alphaVsBtc4h: toFiniteNumber(targetVsBtc.alphaVsBtc4h),
|
|
259
|
+
alphaVsBtc24h: toFiniteNumber(targetVsBtc.alphaVsBtc24h),
|
|
260
|
+
betaToBtc20: toFiniteNumber(targetVsBtc.betaToBtc20),
|
|
261
|
+
correlationToBtc20: toFiniteNumber(targetVsBtc.correlationToBtc20),
|
|
262
|
+
ratioTrend: typeof targetVsBtc.ratioTrend === "string" ? targetVsBtc.ratioTrend : null
|
|
263
|
+
};
|
|
264
|
+
};
|
|
265
|
+
var buildBtcAltRegimeContextFromSignal = (signal) => {
|
|
266
|
+
const baseContext = toRecord(signal.additionalIndicators?.baseContext);
|
|
267
|
+
const relative = toRecord(baseContext?.relative);
|
|
268
|
+
const btcAltRegime = toRecord(relative?.btcAltRegime);
|
|
269
|
+
if (!btcAltRegime) {
|
|
270
|
+
return {
|
|
271
|
+
source: null,
|
|
272
|
+
available: false,
|
|
273
|
+
universe: null,
|
|
274
|
+
interval: null,
|
|
275
|
+
stale: null,
|
|
276
|
+
regime: null,
|
|
277
|
+
btcReturn24h: null,
|
|
278
|
+
altBasketReturn24h: null,
|
|
279
|
+
btcVsAltReturn24h: null,
|
|
280
|
+
btcTurnoverShare24h: null,
|
|
281
|
+
btcTurnoverShareChange24h: null,
|
|
282
|
+
altVolToBtcVol24h: null,
|
|
283
|
+
altDispersion24h: null
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
return {
|
|
287
|
+
source: String(btcAltRegime.source ?? ""),
|
|
288
|
+
available: true,
|
|
289
|
+
universe: String(btcAltRegime.universe ?? ""),
|
|
290
|
+
interval: String(btcAltRegime.interval ?? ""),
|
|
291
|
+
stale: typeof btcAltRegime.stale === "boolean" ? btcAltRegime.stale : null,
|
|
292
|
+
regime: typeof btcAltRegime.regime === "string" ? btcAltRegime.regime : null,
|
|
293
|
+
btcReturn24h: toFiniteNumber(btcAltRegime.btcReturn24h),
|
|
294
|
+
altBasketReturn24h: toFiniteNumber(btcAltRegime.altBasketReturn24h),
|
|
295
|
+
btcVsAltReturn24h: toFiniteNumber(btcAltRegime.btcVsAltReturn24h),
|
|
296
|
+
btcTurnoverShare24h: toFiniteNumber(btcAltRegime.btcTurnoverShare24h),
|
|
297
|
+
btcTurnoverShareChange24h: toFiniteNumber(
|
|
298
|
+
btcAltRegime.btcTurnoverShareChange24h
|
|
299
|
+
),
|
|
300
|
+
altVolToBtcVol24h: toFiniteNumber(btcAltRegime.altVolToBtcVol24h),
|
|
301
|
+
altDispersion24h: toFiniteNumber(btcAltRegime.altDispersion24h)
|
|
302
|
+
};
|
|
303
|
+
};
|
|
304
|
+
var buildCmcGlobalContextFromSignal = (signal) => {
|
|
305
|
+
const baseContext = toRecord(signal.additionalIndicators?.baseContext);
|
|
306
|
+
const relative = toRecord(baseContext?.relative);
|
|
307
|
+
const cmcGlobal = toRecord(relative?.cmcGlobal);
|
|
308
|
+
if (!cmcGlobal) {
|
|
309
|
+
return {
|
|
310
|
+
source: null,
|
|
311
|
+
available: false,
|
|
312
|
+
interval: null,
|
|
313
|
+
asOfTs: null,
|
|
314
|
+
stale: null,
|
|
315
|
+
totalMarketCapUsd: null,
|
|
316
|
+
totalVolumeUsd: null,
|
|
317
|
+
totalVolumeReportedUsd: null,
|
|
318
|
+
altMarketCapUsd: null,
|
|
319
|
+
altVolumeUsd: null,
|
|
320
|
+
altVolumeReportedUsd: null,
|
|
321
|
+
btcDominancePct: null,
|
|
322
|
+
ethDominancePct: null,
|
|
323
|
+
btcDominanceChange24hPct: null,
|
|
324
|
+
ethDominanceChange24hPct: null,
|
|
325
|
+
altMarketCapChange24hPct: null,
|
|
326
|
+
altVolumeChange24hPct: null,
|
|
327
|
+
activeCryptocurrencies: null,
|
|
328
|
+
activeExchanges: null,
|
|
329
|
+
activeMarketPairs: null,
|
|
330
|
+
altLiquidityRegime: null
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
return {
|
|
334
|
+
source: String(cmcGlobal.source ?? ""),
|
|
335
|
+
available: true,
|
|
336
|
+
interval: typeof cmcGlobal.interval === "string" ? cmcGlobal.interval : null,
|
|
337
|
+
asOfTs: toFiniteNumber(cmcGlobal.asOfTs),
|
|
338
|
+
stale: typeof cmcGlobal.stale === "boolean" ? cmcGlobal.stale : null,
|
|
339
|
+
totalMarketCapUsd: toFiniteNumber(cmcGlobal.totalMarketCapUsd),
|
|
340
|
+
totalVolumeUsd: toFiniteNumber(cmcGlobal.totalVolumeUsd),
|
|
341
|
+
totalVolumeReportedUsd: toFiniteNumber(cmcGlobal.totalVolumeReportedUsd),
|
|
342
|
+
altMarketCapUsd: toFiniteNumber(cmcGlobal.altMarketCapUsd),
|
|
343
|
+
altVolumeUsd: toFiniteNumber(cmcGlobal.altVolumeUsd),
|
|
344
|
+
altVolumeReportedUsd: toFiniteNumber(cmcGlobal.altVolumeReportedUsd),
|
|
345
|
+
btcDominancePct: toFiniteNumber(cmcGlobal.btcDominancePct),
|
|
346
|
+
ethDominancePct: toFiniteNumber(cmcGlobal.ethDominancePct),
|
|
347
|
+
btcDominanceChange24hPct: toFiniteNumber(
|
|
348
|
+
cmcGlobal.btcDominanceChange24hPct
|
|
349
|
+
),
|
|
350
|
+
ethDominanceChange24hPct: toFiniteNumber(
|
|
351
|
+
cmcGlobal.ethDominanceChange24hPct
|
|
352
|
+
),
|
|
353
|
+
altMarketCapChange24hPct: toFiniteNumber(
|
|
354
|
+
cmcGlobal.altMarketCapChange24hPct
|
|
355
|
+
),
|
|
356
|
+
altVolumeChange24hPct: toFiniteNumber(cmcGlobal.altVolumeChange24hPct),
|
|
357
|
+
activeCryptocurrencies: toFiniteNumber(cmcGlobal.activeCryptocurrencies),
|
|
358
|
+
activeExchanges: toFiniteNumber(cmcGlobal.activeExchanges),
|
|
359
|
+
activeMarketPairs: toFiniteNumber(cmcGlobal.activeMarketPairs),
|
|
360
|
+
altLiquidityRegime: typeof cmcGlobal.altLiquidityRegime === "string" ? cmcGlobal.altLiquidityRegime : null
|
|
361
|
+
};
|
|
362
|
+
};
|
|
363
|
+
var buildCmcReferenceAssetsContextFromSignal = (signal) => {
|
|
364
|
+
const baseContext = toRecord(signal.additionalIndicators?.baseContext);
|
|
365
|
+
const relative = toRecord(baseContext?.relative);
|
|
366
|
+
const cmcReferenceAssets = toRecord(relative?.cmcReferenceAssets);
|
|
367
|
+
if (!cmcReferenceAssets) {
|
|
368
|
+
return {
|
|
369
|
+
source: null,
|
|
370
|
+
available: false,
|
|
371
|
+
interval: null,
|
|
372
|
+
asOfTs: null,
|
|
373
|
+
stale: null,
|
|
374
|
+
btcMarketCapUsd: null,
|
|
375
|
+
ethMarketCapUsd: null,
|
|
376
|
+
btcVolumeUsd: null,
|
|
377
|
+
ethVolumeUsd: null,
|
|
378
|
+
btcVolumeToMarketCap: null,
|
|
379
|
+
ethVolumeToMarketCap: null,
|
|
380
|
+
ethBtcMarketCapRatio: null,
|
|
381
|
+
ethBtcMarketCapRatioChange24hPct: null,
|
|
382
|
+
ethVsBtcVolumeRatio: null,
|
|
383
|
+
referenceLiquidityRegime: null
|
|
384
|
+
};
|
|
385
|
+
}
|
|
173
386
|
return {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
387
|
+
source: String(cmcReferenceAssets.source ?? ""),
|
|
388
|
+
available: true,
|
|
389
|
+
interval: typeof cmcReferenceAssets.interval === "string" ? cmcReferenceAssets.interval : null,
|
|
390
|
+
asOfTs: toFiniteNumber(cmcReferenceAssets.asOfTs),
|
|
391
|
+
stale: typeof cmcReferenceAssets.stale === "boolean" ? cmcReferenceAssets.stale : null,
|
|
392
|
+
btcMarketCapUsd: toFiniteNumber(cmcReferenceAssets.btcMarketCapUsd),
|
|
393
|
+
ethMarketCapUsd: toFiniteNumber(cmcReferenceAssets.ethMarketCapUsd),
|
|
394
|
+
btcVolumeUsd: toFiniteNumber(cmcReferenceAssets.btcVolumeUsd),
|
|
395
|
+
ethVolumeUsd: toFiniteNumber(cmcReferenceAssets.ethVolumeUsd),
|
|
396
|
+
btcVolumeToMarketCap: toFiniteNumber(
|
|
397
|
+
cmcReferenceAssets.btcVolumeToMarketCap
|
|
398
|
+
),
|
|
399
|
+
ethVolumeToMarketCap: toFiniteNumber(
|
|
400
|
+
cmcReferenceAssets.ethVolumeToMarketCap
|
|
401
|
+
),
|
|
402
|
+
ethBtcMarketCapRatio: toFiniteNumber(
|
|
403
|
+
cmcReferenceAssets.ethBtcMarketCapRatio
|
|
404
|
+
),
|
|
405
|
+
ethBtcMarketCapRatioChange24hPct: toFiniteNumber(
|
|
406
|
+
cmcReferenceAssets.ethBtcMarketCapRatioChange24hPct
|
|
407
|
+
),
|
|
408
|
+
ethVsBtcVolumeRatio: toFiniteNumber(cmcReferenceAssets.ethVsBtcVolumeRatio),
|
|
409
|
+
referenceLiquidityRegime: typeof cmcReferenceAssets.referenceLiquidityRegime === "string" ? cmcReferenceAssets.referenceLiquidityRegime : null
|
|
177
410
|
};
|
|
178
411
|
};
|
|
412
|
+
var buildCmcExchangeLiquidityContextFromSignal = (signal) => {
|
|
413
|
+
const baseContext = toRecord(signal.additionalIndicators?.baseContext);
|
|
414
|
+
const relative = toRecord(baseContext?.relative);
|
|
415
|
+
const cmcExchangeLiquidity = toRecord(relative?.cmcExchangeLiquidity);
|
|
416
|
+
if (!cmcExchangeLiquidity) {
|
|
417
|
+
return {
|
|
418
|
+
source: null,
|
|
419
|
+
available: false,
|
|
420
|
+
interval: null,
|
|
421
|
+
asOfTs: null,
|
|
422
|
+
stale: null,
|
|
423
|
+
exchangesCount: null,
|
|
424
|
+
totalVolumeUsd: null,
|
|
425
|
+
totalVolumeChange24hPct: null,
|
|
426
|
+
binanceVolumeUsd: null,
|
|
427
|
+
binanceVolumeShare: null,
|
|
428
|
+
topExchangeVolumeShare: null,
|
|
429
|
+
liquidityRegime: null
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
return {
|
|
433
|
+
source: String(cmcExchangeLiquidity.source ?? ""),
|
|
434
|
+
available: true,
|
|
435
|
+
interval: typeof cmcExchangeLiquidity.interval === "string" ? cmcExchangeLiquidity.interval : null,
|
|
436
|
+
asOfTs: toFiniteNumber(cmcExchangeLiquidity.asOfTs),
|
|
437
|
+
stale: typeof cmcExchangeLiquidity.stale === "boolean" ? cmcExchangeLiquidity.stale : null,
|
|
438
|
+
exchangesCount: toFiniteNumber(cmcExchangeLiquidity.exchangesCount),
|
|
439
|
+
totalVolumeUsd: toFiniteNumber(cmcExchangeLiquidity.totalVolumeUsd),
|
|
440
|
+
totalVolumeChange24hPct: toFiniteNumber(
|
|
441
|
+
cmcExchangeLiquidity.totalVolumeChange24hPct
|
|
442
|
+
),
|
|
443
|
+
binanceVolumeUsd: toFiniteNumber(cmcExchangeLiquidity.binanceVolumeUsd),
|
|
444
|
+
binanceVolumeShare: toFiniteNumber(cmcExchangeLiquidity.binanceVolumeShare),
|
|
445
|
+
topExchangeVolumeShare: toFiniteNumber(
|
|
446
|
+
cmcExchangeLiquidity.topExchangeVolumeShare
|
|
447
|
+
),
|
|
448
|
+
liquidityRegime: typeof cmcExchangeLiquidity.liquidityRegime === "string" ? cmcExchangeLiquidity.liquidityRegime : null
|
|
449
|
+
};
|
|
450
|
+
};
|
|
451
|
+
var buildCmcFearGreedContextFromSignal = (signal) => {
|
|
452
|
+
const baseContext = toRecord(signal.additionalIndicators?.baseContext);
|
|
453
|
+
const relative = toRecord(baseContext?.relative);
|
|
454
|
+
const cmcFearGreed = toRecord(relative?.cmcFearGreed);
|
|
455
|
+
if (!cmcFearGreed) {
|
|
456
|
+
return {
|
|
457
|
+
source: null,
|
|
458
|
+
available: false,
|
|
459
|
+
interval: null,
|
|
460
|
+
asOfTs: null,
|
|
461
|
+
stale: null,
|
|
462
|
+
value: null,
|
|
463
|
+
valueChange24h: null,
|
|
464
|
+
valueChange7d: null,
|
|
465
|
+
classification: null,
|
|
466
|
+
sentimentRegime: null
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
return {
|
|
470
|
+
source: String(cmcFearGreed.source ?? ""),
|
|
471
|
+
available: true,
|
|
472
|
+
interval: typeof cmcFearGreed.interval === "string" ? cmcFearGreed.interval : null,
|
|
473
|
+
asOfTs: toFiniteNumber(cmcFearGreed.asOfTs),
|
|
474
|
+
stale: typeof cmcFearGreed.stale === "boolean" ? cmcFearGreed.stale : null,
|
|
475
|
+
value: toFiniteNumber(cmcFearGreed.value),
|
|
476
|
+
valueChange24h: toFiniteNumber(cmcFearGreed.valueChange24h),
|
|
477
|
+
valueChange7d: toFiniteNumber(cmcFearGreed.valueChange7d),
|
|
478
|
+
classification: typeof cmcFearGreed.classification === "string" ? cmcFearGreed.classification : null,
|
|
479
|
+
sentimentRegime: typeof cmcFearGreed.sentimentRegime === "string" ? cmcFearGreed.sentimentRegime : null
|
|
480
|
+
};
|
|
481
|
+
};
|
|
482
|
+
var buildCmcIndexesContextFromSignal = (signal) => {
|
|
483
|
+
const baseContext = toRecord(signal.additionalIndicators?.baseContext);
|
|
484
|
+
const relative = toRecord(baseContext?.relative);
|
|
485
|
+
const cmcIndexes = toRecord(relative?.cmcIndexes);
|
|
486
|
+
if (!cmcIndexes) {
|
|
487
|
+
return {
|
|
488
|
+
source: null,
|
|
489
|
+
available: false,
|
|
490
|
+
interval: null,
|
|
491
|
+
asOfTs: null,
|
|
492
|
+
stale: null,
|
|
493
|
+
cmc100Value: null,
|
|
494
|
+
cmc100Change24hPct: null,
|
|
495
|
+
cmc100TopConstituentSymbol: null,
|
|
496
|
+
cmc100TopConstituentWeightPct: null,
|
|
497
|
+
cmc20Value: null,
|
|
498
|
+
cmc20Change24hPct: null,
|
|
499
|
+
cmc20TopConstituentSymbol: null,
|
|
500
|
+
cmc20TopConstituentWeightPct: null,
|
|
501
|
+
cmc20ToCmc100Ratio: null,
|
|
502
|
+
cmc20ToCmc100RatioChange24hPct: null,
|
|
503
|
+
indexRegime: null
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
return {
|
|
507
|
+
source: String(cmcIndexes.source ?? ""),
|
|
508
|
+
available: true,
|
|
509
|
+
interval: typeof cmcIndexes.interval === "string" ? cmcIndexes.interval : null,
|
|
510
|
+
asOfTs: toFiniteNumber(cmcIndexes.asOfTs),
|
|
511
|
+
stale: typeof cmcIndexes.stale === "boolean" ? cmcIndexes.stale : null,
|
|
512
|
+
cmc100Value: toFiniteNumber(cmcIndexes.cmc100Value),
|
|
513
|
+
cmc100Change24hPct: toFiniteNumber(cmcIndexes.cmc100Change24hPct),
|
|
514
|
+
cmc100TopConstituentSymbol: typeof cmcIndexes.cmc100TopConstituentSymbol === "string" ? cmcIndexes.cmc100TopConstituentSymbol : null,
|
|
515
|
+
cmc100TopConstituentWeightPct: toFiniteNumber(
|
|
516
|
+
cmcIndexes.cmc100TopConstituentWeightPct
|
|
517
|
+
),
|
|
518
|
+
cmc20Value: toFiniteNumber(cmcIndexes.cmc20Value),
|
|
519
|
+
cmc20Change24hPct: toFiniteNumber(cmcIndexes.cmc20Change24hPct),
|
|
520
|
+
cmc20TopConstituentSymbol: typeof cmcIndexes.cmc20TopConstituentSymbol === "string" ? cmcIndexes.cmc20TopConstituentSymbol : null,
|
|
521
|
+
cmc20TopConstituentWeightPct: toFiniteNumber(
|
|
522
|
+
cmcIndexes.cmc20TopConstituentWeightPct
|
|
523
|
+
),
|
|
524
|
+
cmc20ToCmc100Ratio: toFiniteNumber(cmcIndexes.cmc20ToCmc100Ratio),
|
|
525
|
+
cmc20ToCmc100RatioChange24hPct: toFiniteNumber(
|
|
526
|
+
cmcIndexes.cmc20ToCmc100RatioChange24hPct
|
|
527
|
+
),
|
|
528
|
+
indexRegime: typeof cmcIndexes.indexRegime === "string" ? cmcIndexes.indexRegime : null
|
|
529
|
+
};
|
|
530
|
+
};
|
|
531
|
+
var buildReferenceTradeFlowContextFromSignal = (signal) => {
|
|
532
|
+
const baseContext = toRecord(signal.additionalIndicators?.baseContext);
|
|
533
|
+
const relative = toRecord(baseContext?.relative);
|
|
534
|
+
const refs = toRecord(relative?.referenceTradeFlow);
|
|
535
|
+
const primaryReferenceSymbol = typeof refs?.primaryReferenceSymbol === "string" ? refs.primaryReferenceSymbol : null;
|
|
536
|
+
const tradeFlowBySymbol = toRecord(refs?.tradeFlowBySymbol);
|
|
537
|
+
const primaryTradeFlow = primaryReferenceSymbol != null ? toRecord(tradeFlowBySymbol?.[primaryReferenceSymbol]) : null;
|
|
538
|
+
if (!refs) {
|
|
539
|
+
return {
|
|
540
|
+
source: null,
|
|
541
|
+
available: false,
|
|
542
|
+
primaryReferenceSymbol: null,
|
|
543
|
+
referenceSymbols: [],
|
|
544
|
+
primaryTradeFlowBuyPressurePct: null,
|
|
545
|
+
primaryTradeFlowStale: null
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
return {
|
|
549
|
+
source: String(refs.source ?? ""),
|
|
550
|
+
available: true,
|
|
551
|
+
primaryReferenceSymbol,
|
|
552
|
+
referenceSymbols: Array.isArray(refs.referenceSymbols) ? refs.referenceSymbols.map(String) : [],
|
|
553
|
+
primaryTradeFlowBuyPressurePct: toFiniteNumber(
|
|
554
|
+
primaryTradeFlow?.buyPressurePct
|
|
555
|
+
),
|
|
556
|
+
primaryTradeFlowStale: typeof primaryTradeFlow?.stale === "boolean" ? primaryTradeFlow.stale : null
|
|
557
|
+
};
|
|
558
|
+
};
|
|
559
|
+
var buildAiMarketContext = (signal) => ({
|
|
560
|
+
execution: {
|
|
561
|
+
binanceCoinbaseSpread: buildSpreadContextFromSignal(signal)
|
|
562
|
+
},
|
|
563
|
+
participation: {
|
|
564
|
+
trueDelta: buildTrueDeltaContextFromSignal(signal),
|
|
565
|
+
tradeFlow: buildTradeFlowContextFromSignal(signal)
|
|
566
|
+
},
|
|
567
|
+
relative: {
|
|
568
|
+
marketBreadth: buildMarketBreadthContextFromSignal(signal),
|
|
569
|
+
targetVsBtc: buildTargetVsBtcContextFromSignal(signal),
|
|
570
|
+
btcAltRegime: buildBtcAltRegimeContextFromSignal(signal),
|
|
571
|
+
cmcGlobal: buildCmcGlobalContextFromSignal(signal),
|
|
572
|
+
cmcReferenceAssets: buildCmcReferenceAssetsContextFromSignal(signal),
|
|
573
|
+
cmcExchangeLiquidity: buildCmcExchangeLiquidityContextFromSignal(signal),
|
|
574
|
+
cmcFearGreed: buildCmcFearGreedContextFromSignal(signal),
|
|
575
|
+
cmcIndexes: buildCmcIndexesContextFromSignal(signal),
|
|
576
|
+
referenceTradeFlow: buildReferenceTradeFlowContextFromSignal(signal)
|
|
577
|
+
}
|
|
578
|
+
});
|
|
179
579
|
|
|
180
580
|
// src/strategy/manifests.ts
|
|
181
581
|
var import_indicators = require("@tradejs/core/indicators");
|
|
@@ -184,7 +584,6 @@ var import_logger2 = require("@tradejs/infra/logger");
|
|
|
184
584
|
// src/tradejsConfig.ts
|
|
185
585
|
var import_fs = __toESM(require("fs"));
|
|
186
586
|
var import_path = __toESM(require("path"));
|
|
187
|
-
var import_module = require("module");
|
|
188
587
|
var import_url = require("url");
|
|
189
588
|
var import_config = require("@tradejs/core/config");
|
|
190
589
|
var import_logger = require("@tradejs/infra/logger");
|
|
@@ -200,6 +599,7 @@ var cachedByCwd = /* @__PURE__ */ new Map();
|
|
|
200
599
|
var announcedConfigFile = /* @__PURE__ */ new Set();
|
|
201
600
|
var tsNodeRegistered = false;
|
|
202
601
|
var tsconfigPathsRegisteredByCwd = /* @__PURE__ */ new Set();
|
|
602
|
+
var tsconfigPathMatchersByCwd = /* @__PURE__ */ new Map();
|
|
203
603
|
var getTradejsProjectCwd = (cwd) => {
|
|
204
604
|
const explicit = String(cwd ?? "").trim();
|
|
205
605
|
if (explicit) {
|
|
@@ -229,7 +629,14 @@ var normalizeConfig = (rawConfig) => {
|
|
|
229
629
|
...hooks ? { hooks } : {}
|
|
230
630
|
};
|
|
231
631
|
};
|
|
232
|
-
var
|
|
632
|
+
var getNodeCreateRequire = () => {
|
|
633
|
+
const builtinModule = process.getBuiltinModule?.("module");
|
|
634
|
+
if (typeof builtinModule?.createRequire === "function") {
|
|
635
|
+
return builtinModule.createRequire;
|
|
636
|
+
}
|
|
637
|
+
throw new TypeError("module.createRequire is not available");
|
|
638
|
+
};
|
|
639
|
+
var getRequireFn = (cwd = getTradejsProjectCwd()) => getNodeCreateRequire()(import_path.default.join(import_path.default.resolve(cwd), "__tradejs_loader__.js"));
|
|
233
640
|
var ensureTsNodeRegistered = async () => {
|
|
234
641
|
if (tsNodeRegistered) {
|
|
235
642
|
return;
|
|
@@ -239,8 +646,8 @@ var ensureTsNodeRegistered = async () => {
|
|
|
239
646
|
tsNode.register?.({
|
|
240
647
|
transpileOnly: true,
|
|
241
648
|
compilerOptions: {
|
|
242
|
-
module: "
|
|
243
|
-
moduleResolution: "
|
|
649
|
+
module: "Node16",
|
|
650
|
+
moduleResolution: "node16"
|
|
244
651
|
}
|
|
245
652
|
});
|
|
246
653
|
tsNodeRegistered = true;
|
|
@@ -267,6 +674,42 @@ var ensureTsconfigPathsRegistered = async (cwd = getTradejsProjectCwd()) => {
|
|
|
267
674
|
});
|
|
268
675
|
tsconfigPathsRegisteredByCwd.add(projectRoot);
|
|
269
676
|
};
|
|
677
|
+
var resolveTsconfigPathModule = async (moduleName, cwd = getTradejsProjectCwd()) => {
|
|
678
|
+
const projectRoot = getTradejsProjectCwd(cwd);
|
|
679
|
+
const cachedMatcher = tsconfigPathMatchersByCwd.get(projectRoot);
|
|
680
|
+
if (cachedMatcher) {
|
|
681
|
+
const resolved2 = cachedMatcher(moduleName);
|
|
682
|
+
return resolved2 || null;
|
|
683
|
+
}
|
|
684
|
+
const tsconfigPathsModule = await import("tsconfig-paths");
|
|
685
|
+
const loadConfig = tsconfigPathsModule.loadConfig;
|
|
686
|
+
const createMatchPath = tsconfigPathsModule.createMatchPath;
|
|
687
|
+
if (typeof loadConfig !== "function" || typeof createMatchPath !== "function") {
|
|
688
|
+
return null;
|
|
689
|
+
}
|
|
690
|
+
const loadedConfig = loadConfig(projectRoot);
|
|
691
|
+
if (loadedConfig.resultType !== "success") {
|
|
692
|
+
return null;
|
|
693
|
+
}
|
|
694
|
+
const matchPath = createMatchPath(
|
|
695
|
+
loadedConfig.absoluteBaseUrl,
|
|
696
|
+
loadedConfig.paths
|
|
697
|
+
);
|
|
698
|
+
const matcher = (requestedModule) => matchPath(requestedModule, void 0, import_fs.default.existsSync, [
|
|
699
|
+
".ts",
|
|
700
|
+
".tsx",
|
|
701
|
+
".mts",
|
|
702
|
+
".cts",
|
|
703
|
+
".js",
|
|
704
|
+
".jsx",
|
|
705
|
+
".mjs",
|
|
706
|
+
".cjs",
|
|
707
|
+
".json"
|
|
708
|
+
]) || "";
|
|
709
|
+
tsconfigPathMatchersByCwd.set(projectRoot, matcher);
|
|
710
|
+
const resolved = matcher(moduleName);
|
|
711
|
+
return resolved || null;
|
|
712
|
+
};
|
|
270
713
|
var toImportSpecifier = (moduleName) => {
|
|
271
714
|
if (moduleName.startsWith("file://")) {
|
|
272
715
|
return moduleName;
|
|
@@ -325,7 +768,18 @@ var importTradejsModule = async (moduleName, cwd = getTradejsProjectCwd()) => {
|
|
|
325
768
|
}
|
|
326
769
|
if (isBareModuleSpecifier(normalized)) {
|
|
327
770
|
await ensureTsconfigPathsRegistered(cwd);
|
|
328
|
-
|
|
771
|
+
try {
|
|
772
|
+
return requireFn(normalized);
|
|
773
|
+
} catch (error) {
|
|
774
|
+
const resolvedByTsconfig = await resolveTsconfigPathModule(
|
|
775
|
+
normalized,
|
|
776
|
+
cwd
|
|
777
|
+
);
|
|
778
|
+
if (resolvedByTsconfig && resolvedByTsconfig !== normalized) {
|
|
779
|
+
return requireFn(resolvedByTsconfig);
|
|
780
|
+
}
|
|
781
|
+
throw error;
|
|
782
|
+
}
|
|
329
783
|
}
|
|
330
784
|
try {
|
|
331
785
|
return await import(
|
|
@@ -595,6 +1049,9 @@ var strategies = new Proxy(
|
|
|
595
1049
|
}
|
|
596
1050
|
);
|
|
597
1051
|
|
|
1052
|
+
// src/strategy/policyProfiles.ts
|
|
1053
|
+
var getStrategyProfileAiAdapter = (manifest, profileId) => manifest?.policyProfiles?.find(({ id }) => id === profileId)?.aiAdapter ?? manifest?.aiAdapter;
|
|
1054
|
+
|
|
598
1055
|
// src/strategyAdapters/ai.ts
|
|
599
1056
|
var toRecord2 = (value) => {
|
|
600
1057
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
@@ -622,13 +1079,13 @@ var buildBaseAiPayload = (signal) => {
|
|
|
622
1079
|
}
|
|
623
1080
|
},
|
|
624
1081
|
figures: trimSeriesDeep(signal.figures ?? {}),
|
|
625
|
-
indicators:
|
|
1082
|
+
indicators: buildCompactAiIndicatorsSnapshot(signal.indicators),
|
|
626
1083
|
additionalIndicators: trimSeriesDeep(additionalIndicators)
|
|
627
1084
|
};
|
|
628
1085
|
};
|
|
629
1086
|
var defaultAiAdapter = {};
|
|
630
|
-
var getStrategyAiAdapter = (strategy) => getStrategyManifest(strategy)
|
|
631
|
-
var getSignalAiAdapter = (signal) => getStrategyAiAdapter(signal.strategy);
|
|
1087
|
+
var getStrategyAiAdapter = (strategy, profileId) => getStrategyProfileAiAdapter(getStrategyManifest(strategy), profileId) ?? defaultAiAdapter;
|
|
1088
|
+
var getSignalAiAdapter = (signal) => getStrategyAiAdapter(signal.strategy, signal.policyProfileId);
|
|
632
1089
|
var buildAiPayloadByStrategy = (signal) => {
|
|
633
1090
|
const basePayload = buildBaseAiPayload(signal);
|
|
634
1091
|
const adapter = getSignalAiAdapter(signal);
|
|
@@ -715,7 +1172,7 @@ var getDeterministicQuality = (gateContext) => {
|
|
|
715
1172
|
if (Number.isFinite(maxAllowedQuality)) {
|
|
716
1173
|
return Math.max(1, Math.min(5, Math.round(maxAllowedQuality)));
|
|
717
1174
|
}
|
|
718
|
-
return Array.isArray(gateContext?.structuralHardBlockReasons) && gateContext.structuralHardBlockReasons.length > 0 ? 2 : 3;
|
|
1175
|
+
return Array.isArray(gateContext?.approvalBlockReasons) && gateContext.approvalBlockReasons.length > 0 || Array.isArray(gateContext?.structuralHardBlockReasons) && gateContext.structuralHardBlockReasons.length > 0 ? 2 : 3;
|
|
719
1176
|
};
|
|
720
1177
|
var buildAiSystemPrompt = (signal) => `
|
|
721
1178
|
You are an internal market-structure classifier for an already computed system signal.
|
|
@@ -779,24 +1236,44 @@ Input payload structure:
|
|
|
779
1236
|
- payload.figures:
|
|
780
1237
|
strategy-specific figures or geometry when available. Fields vary by strategy.
|
|
781
1238
|
- payload.indicators:
|
|
782
|
-
indicator dictionaries and series for the coin and BTC; all series are already trimmed to the latest 5 values.
|
|
1239
|
+
historical indicator dictionaries and series for the coin and BTC; all series are already trimmed to the latest 5 values. Treat this block as recent-history transport, not as the primary source of the current shared context.
|
|
783
1240
|
- payload.additionalIndicators:
|
|
784
|
-
strategy-specific summary/context fields
|
|
785
|
-
|
|
1241
|
+
strategy-specific summary/context fields plus the canonical current shared context snapshot.
|
|
1242
|
+
This is not noise; it contains derived fields deliberately passed by the strategy to help the decision.
|
|
1243
|
+
Examples: baseContext, helperFlags, structureContext, volatilitySummary.
|
|
1244
|
+
Always inspect \`payload.additionalIndicators.baseContext\` first for the current shared state:
|
|
1245
|
+
\u2022 \`baseContext.raw\`: current MA, ATR, BB, OBV, price stats, levels, BTC correlation.
|
|
1246
|
+
\u2022 \`baseContext.regime\`: derived trend / volatility / momentum / session regime fields.
|
|
1247
|
+
\u2022 \`baseContext.structure\`: local range position, breakout freshness/quality, level-touch counts, rejection wick context.
|
|
1248
|
+
\u2022 \`baseContext.participation\`: volume/turnover participation, effort-vs-result context, and Binance aggTrades trade-flow when available.
|
|
1249
|
+
\u2022 \`baseContext.relative\`: BTC/ETH relative-strength, benchmark MA bias context, Binance alt-basket breadth, and CoinMarketCap historical global/exchange/index context when available.
|
|
1250
|
+
\u2022 \`baseContext.derivatives\`: Coinalyze-aligned derivatives summary when available.
|
|
1251
|
+
\u2022 \`baseContext.mtf\`: compact multi-timeframe summary plus only the latest few candles for each timeframe.
|
|
1252
|
+
\u2022 \`baseContext.gateFeatures\`: direction-aware, normalized fields derived from baseContext; prefer \`setup\`, \`scores\`, \`confirmations\`, \`conflicts\`, \`risk\`, and \`decisionHints\` for quick gate checks before inspecting raw nested context.
|
|
786
1253
|
Always inspect \`payload.additionalIndicators.marketContext\` when present:
|
|
787
|
-
\u2022 \`marketContext.
|
|
788
|
-
\u2022 \`marketContext.
|
|
1254
|
+
\u2022 \`marketContext.execution.binanceCoinbaseSpread\`: AI-friendly BTC spread view projected from \`payload.additionalIndicators.baseContext.relative.execution.venueSpread\`; \`value=(Coinbase-Binance)/Binance\`, \`bps=value*10000\`.
|
|
1255
|
+
\u2022 \`marketContext.participation.trueDelta\`: Binance taker buy/sell volume delta from kline payload when \`source=kline_taker_volume\`; otherwise absent/unavailable.
|
|
1256
|
+
\u2022 \`marketContext.participation.tradeFlow\`: Binance aggTrades buy/sell pressure buckets when available.
|
|
1257
|
+
\u2022 \`marketContext.relative.marketBreadth\`: equal/volume-weighted alt-basket return, advance/decline ratio, and MA breadth for the configured Binance breadth universe.
|
|
1258
|
+
\u2022 \`marketContext.relative.targetVsBtc\`: target/BTC ratio returns, alpha, beta, and short-window correlation; use it to decide whether the target is leading or lagging BTC in the signal direction.
|
|
1259
|
+
\u2022 \`marketContext.relative.btcAltRegime\`: Binance-derived BTC-vs-alt basket regime, BTC/alt 24h returns, BTC turnover share, and alt dispersion; use it as a broad alt-market risk pocket.
|
|
1260
|
+
\u2022 \`marketContext.relative.cmcGlobal\`: historical CoinMarketCap global market metrics: total/alt market cap, total/alt volume, BTC/ETH dominance and 24h changes, active markets, \`interval\`, and \`altLiquidityRegime\`.
|
|
1261
|
+
\u2022 \`marketContext.relative.cmcReferenceAssets\`: historical CoinMarketCap BTC/ETH market-cap and volume context, ETH/BTC market-cap ratio, ETH-vs-BTC volume ratio, \`interval\`, and \`referenceLiquidityRegime\`.
|
|
1262
|
+
\u2022 \`marketContext.relative.cmcExchangeLiquidity\`: historical CoinMarketCap major-exchange liquidity aggregate: total volume, 24h volume change, Binance share, concentration, and \`liquidityRegime\`.
|
|
1263
|
+
\u2022 \`marketContext.relative.cmcFearGreed\`: historical daily CoinMarketCap Fear & Greed sentiment index: value, classification, 24h/7d value changes, and \`sentimentRegime\`.
|
|
1264
|
+
\u2022 \`marketContext.relative.cmcIndexes\`: historical daily CoinMarketCap CMC100/CMC20 index values, 24h changes, top constituents, CMC20/CMC100 ratio, and \`indexRegime\`.
|
|
1265
|
+
\u2022 \`marketContext.relative.referenceTradeFlow\`: BTC/ETH reference trade-flow summary used for broad market pressure when the target symbol itself is not BTC/ETH.
|
|
789
1266
|
If those fields exist, use them as a more explicit hint instead of trying to re-derive the same idea from raw lines or points.
|
|
790
|
-
If \`
|
|
1267
|
+
If \`baseContext.derivatives\` exists, its top-level \`summary\` and \`intervals\` are the primary BTCUSDT Coinalyze benchmark context for the time of the signal. \`secondaryReferenceSymbol\` identifies the ETHUSDT secondary benchmark, and \`referenceContexts\` contains BTCUSDT/ETHUSDT plus configured extra reference symbols such as BNBUSDT/SOLUSDT/TRXUSDT/XRPUSDT. If \`targetContext\` or \`targetDerived\` exists, those fields are the Coinalyze context for the actual target coin; use them as target-specific positioning evidence, but do not infer target-coin derivatives when they are absent.
|
|
791
1268
|
Key patterns:
|
|
792
|
-
\u2022
|
|
793
|
-
\u2022
|
|
794
|
-
\u2022 strategy service keys are possible as well, for example \`
|
|
1269
|
+
\u2022 current shared state: prefer \`payload.additionalIndicators.baseContext\`
|
|
1270
|
+
\u2022 recent historical series: \`payload.indicators\`
|
|
1271
|
+
\u2022 strategy service keys are possible as well, for example \`touches\`, \`distance\`, timing flags, and other setup-specific summaries
|
|
795
1272
|
|
|
796
1273
|
How to analyze, in order:
|
|
797
1274
|
1. Start with price structure and the setup geometry or context in \`payload.figures\`. This has higher priority than indicators.
|
|
798
|
-
2. Then use \`payload.additionalIndicators\`
|
|
799
|
-
3. Then assess confirmation or conflict from the current coin
|
|
1275
|
+
2. Then use \`payload.additionalIndicators.baseContext\` and other explicit strategy-specific context fields.
|
|
1276
|
+
3. Then assess confirmation or conflict from the current shared state and recent coin indicator history.
|
|
800
1277
|
4. Then evaluate BTC context.
|
|
801
1278
|
5. Only after that choose \`direction\`, \`quality\`, and whether an extra confirmation level is required.
|
|
802
1279
|
6. If strong conflicts exist, reduce quality or set direction to \`null\`.
|
|
@@ -805,13 +1282,24 @@ Explicit conflict rules:
|
|
|
805
1282
|
- If the figure or price structure is invalid or doubtful, indicators must not rescue the setup.
|
|
806
1283
|
- If strategy-specific helper fields explicitly say the signal is not confirmed yet, lacks margin, or requires waiting, do not overstate quality.
|
|
807
1284
|
- If the structure is acceptable but BTC or key indicators noticeably conflict, quality is usually \`<= 3\`.
|
|
808
|
-
- If \`
|
|
809
|
-
- If \`
|
|
810
|
-
- If \`
|
|
811
|
-
- If \`
|
|
812
|
-
-
|
|
813
|
-
- If \`marketContext.binanceCoinbaseSpread.available=true\` and \`severity=elevated/wide\`, treat it as cross-exchange divergence or BTC liquidity risk. Do not use the spread as a standalone long/short signal, but reduce confidence or require more confirmation when the rest of the structure is weak or BTC context conflicts.
|
|
814
|
-
- If \`marketContext.binanceCoinbaseSpread\` is missing or \`available=false\`, do not infer anything from Binance/Coinbase spread and do not penalize the signal just because it is absent.
|
|
1285
|
+
- If \`baseContext.derivatives.referenceContexts\` exists, check \`primaryReferenceSymbol\` first as the BTC benchmark, then compare \`secondaryReferenceSymbol\`/ETHUSDT and any target-specific \`targetDerived\`. If \`targetDerived\` exists, compare it to the primary reference instead of treating reference pressure as the target coin's own pressure.
|
|
1286
|
+
- If top-level \`baseContext.derivatives.summary.riskFlags\` contains \`crowded_long\` for a LONG or \`crowded_short\` for a SHORT, treat that as broad-market crowded positioning. If \`targetDerived.riskFlags\` contains the same directional crowding, treat that as target-specific crowded positioning.
|
|
1287
|
+
- If top-level \`baseContext.derivatives.summary.directionAligned=false\`, explicitly mention the broad-market derivatives conflict in \`confirmations\` or \`qualityReason\`. If \`targetDerived.directionAligned=false\`, explicitly mention the target-specific derivatives conflict.
|
|
1288
|
+
- If \`baseContext.derivatives\` is absent, stale, or \`missing_derivatives\`, do not infer Coinalyze conclusions and do not penalize the signal just because that data is missing.
|
|
1289
|
+
- Use \`baseContext.regime.session\` directly as the canonical session/liquidity regime: asia is often thinner, europe/us are more active, and overlaps can amplify both momentum and noise. Do not reject a signal solely because of session, but mention clear session support or conflict in \`confirmations\` or \`qualityReason\`.
|
|
1290
|
+
- If \`marketContext.execution.binanceCoinbaseSpread.available=true\` and \`severity=elevated/wide\`, treat it as cross-exchange divergence or BTC liquidity risk. Do not use the spread as a standalone long/short signal, but reduce confidence or require more confirmation when the rest of the structure is weak or BTC context conflicts.
|
|
1291
|
+
- If \`marketContext.execution.binanceCoinbaseSpread\` is missing or \`available=false\`, do not infer anything from Binance/Coinbase spread and do not penalize the signal just because it is absent.
|
|
1292
|
+
- If \`marketContext.participation.trueDelta.available=true\`, use it as better participation evidence than OHLCV-derived proxy delta; still do not let delta override invalid price structure.
|
|
1293
|
+
- If \`marketContext.participation.tradeFlow.available=true\` and \`stale=false\`, use it as direct lower-timeframe participation evidence. Treat stale or missing tradeFlow as absent, not as negative evidence.
|
|
1294
|
+
- If \`marketContext.relative.marketBreadth.available=true\` and \`stale=false\`, use it as broad alt-market support/conflict. Breadth is contextual; do not let it override the target symbol structure.
|
|
1295
|
+
- If \`marketContext.relative.targetVsBtc.available=true\`, treat positive target/BTC ratio trend as support for alt LONGs and negative ratio trend as support for alt SHORTs; ignore it when the target structure is stronger and clearly explains the setup.
|
|
1296
|
+
- If \`marketContext.relative.btcAltRegime.available=true\` and \`stale=false\`, treat \`alt_lead\`/\`risk_on\` as broad support for alt LONGs and \`btc_lead\`/\`risk_off\` as pressure against alt LONGs or support for cautious alt SHORTs. Do not use it as a standalone entry reason.
|
|
1297
|
+
- If \`marketContext.relative.cmcGlobal.available=true\` and \`stale=false\`, use falling alt market cap/volume or rising BTC dominance as broad risk pressure for alt LONGs. Treat missing CMC history as absent context, not a bearish signal.
|
|
1298
|
+
- If \`marketContext.relative.cmcReferenceAssets.available=true\` and \`stale=false\`, use \`eth_led\` as broad support for ETH/high-beta alt strength and \`btc_led\`/\`thin\` as broad caution. Do not describe BTC/ETH reference history as target-symbol flow.
|
|
1299
|
+
- If \`marketContext.relative.cmcExchangeLiquidity.available=true\` and \`stale=false\`, treat \`contracting\`, \`thin\`, or \`concentrated\` as broad liquidity risk; \`expanding\` or \`balanced\` supports cleaner execution context but is not a standalone entry reason.
|
|
1300
|
+
- If \`marketContext.relative.cmcFearGreed.available=true\` and \`stale=false\`, use \`risk_on\` as broad support for LONGs and \`risk_off\`/\`capitulation\` as broad pressure. Treat \`euphoric\` as overheating/chase caution, not as standalone SHORT proof.
|
|
1301
|
+
- If \`marketContext.relative.cmcIndexes.available=true\` and \`stale=false\`, use \`top20_led\` as broad support for mega-cap leadership, \`large_cap_led\` as broader CMC100 participation, and \`risk_off\` as broad pressure. Do not use CMC index history as a standalone entry reason.
|
|
1302
|
+
- If \`marketContext.relative.referenceTradeFlow.available=true\`, treat BTC/ETH trade-flow as broad market context only. For alt symbols, do not describe it as the target coin's own flow.
|
|
815
1303
|
- If the current signal is not confirmed (\`direction=null\`), name the main reason briefly in \`comment\`.
|
|
816
1304
|
If you use the structured fields, include the main reason in \`qualityReason\` or \`triggerInvalidation\`.
|
|
817
1305
|
|
|
@@ -861,7 +1349,7 @@ var getDeterministicAiGateContext = (payload) => {
|
|
|
861
1349
|
...Object.values(additionalIndicators ?? {}).map(asRecord)
|
|
862
1350
|
].filter((value) => Boolean(value));
|
|
863
1351
|
return candidates.find(
|
|
864
|
-
(candidate) => Array.isArray(candidate.structuralHardBlockReasons) || typeof candidate.approvalAllowedNow === "boolean"
|
|
1352
|
+
(candidate) => Array.isArray(candidate.approvalBlockReasons) || Array.isArray(candidate.riskAnnotations) || Array.isArray(candidate.structuralHardBlockReasons) || typeof candidate.approvalAllowedNow === "boolean"
|
|
865
1353
|
) ?? null;
|
|
866
1354
|
};
|
|
867
1355
|
var buildAiHumanPrompt = (signal, payload = buildAiPayload(signal)) => `
|
|
@@ -872,6 +1360,18 @@ Trade payload:
|
|
|
872
1360
|
${JSON.stringify(payload)}
|
|
873
1361
|
${buildAiHumanPromptAddonByStrategy(signal, payload)}
|
|
874
1362
|
`;
|
|
1363
|
+
var getAiInvocationError = (error) => {
|
|
1364
|
+
const details = error instanceof Error && error.message.trim() ? error.message.trim() : String(error);
|
|
1365
|
+
const isEmptyCompletion = error instanceof TypeError && /Cannot read properties of undefined \(reading ['"]message['"]\)/.test(
|
|
1366
|
+
details
|
|
1367
|
+
);
|
|
1368
|
+
const wrapped = new Error(
|
|
1369
|
+
isEmptyCompletion ? "AI provider returned an empty chat completion" : `AI model invocation failed: ${details}`
|
|
1370
|
+
);
|
|
1371
|
+
wrapped.cause = error;
|
|
1372
|
+
return wrapped;
|
|
1373
|
+
};
|
|
1374
|
+
var isEmptyResponseContent = (content) => typeof content === "string" ? content.trim().length === 0 : Object.keys(content).length === 0;
|
|
875
1375
|
var DEFAULT_AI_MODEL = "openai/gpt-5-mini";
|
|
876
1376
|
var userSettingsCache = /* @__PURE__ */ new Map();
|
|
877
1377
|
var aiModelCache = /* @__PURE__ */ new Map();
|
|
@@ -1003,10 +1503,17 @@ var runAiPrompt = async ({ systemPrompt, humanPrompt }, options = {}) => {
|
|
|
1003
1503
|
]
|
|
1004
1504
|
})
|
|
1005
1505
|
);
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
)
|
|
1506
|
+
let response;
|
|
1507
|
+
try {
|
|
1508
|
+
response = await model.invoke(messages);
|
|
1509
|
+
} catch (error) {
|
|
1510
|
+
throw getAiInvocationError(error);
|
|
1511
|
+
}
|
|
1512
|
+
const responseContent = normalizeResponseContent(response?.content);
|
|
1513
|
+
if (isEmptyResponseContent(responseContent)) {
|
|
1514
|
+
throw new Error("AI provider returned an empty chat completion");
|
|
1515
|
+
}
|
|
1516
|
+
const parsed = parseAIResponse(responseContent);
|
|
1010
1517
|
const normalized = normalizeAnalysis(parsed);
|
|
1011
1518
|
if (!options.signal) {
|
|
1012
1519
|
return normalized;
|
|
@@ -1064,6 +1571,7 @@ var askAI = async (signal, options = {}) => {
|
|
|
1064
1571
|
buildAiPayload,
|
|
1065
1572
|
buildAiPrompts,
|
|
1066
1573
|
buildAiSystemPrompt,
|
|
1574
|
+
buildCompactAiIndicatorsSnapshot,
|
|
1067
1575
|
ensureAiStrategyPluginsLoaded,
|
|
1068
1576
|
getDeterministicAiGateContext,
|
|
1069
1577
|
getOpenRouterModelKwargs,
|