@tradejs/strategies 1.0.6 → 1.0.9
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/chunk-37ZWRG3W.mjs +128 -0
- package/dist/chunk-H4MHFD4B.mjs +62 -0
- package/dist/{chunk-MOBKL73M.mjs → chunk-IMLNXICX.mjs} +183 -41
- package/dist/{chunk-XMRB45ZO.mjs → chunk-QVWMBYYM.mjs} +92 -45
- package/dist/chunk-SOVTOGY4.mjs +163 -0
- package/dist/{chunk-H2TU2YMA.mjs → chunk-TDUTYEGH.mjs} +111 -76
- package/dist/{chunk-GNQJ5TVU.mjs → chunk-UK4YHD2E.mjs} +34 -38
- package/dist/index.d.mts +294 -13
- package/dist/index.d.ts +294 -13
- package/dist/index.js +1825 -906
- package/dist/index.mjs +51 -18
- package/dist/{strategy-FYNNJDOH.mjs → strategy-ABIO65CR.mjs} +2 -33
- package/dist/{strategy-AFIGEHDS.mjs → strategy-D7H5J3C4.mjs} +1 -71
- package/dist/{strategy-LC2FSFVN.mjs → strategy-HQIPCUOY.mjs} +1 -72
- package/dist/{strategy-Y4SOK6FF.mjs → strategy-JQIJILHQ.mjs} +28 -109
- package/dist/strategy-QEIPAPY4.mjs +373 -0
- package/dist/{strategy-6TS2NFSC.mjs → strategy-WYN4FZ5S.mjs} +2 -125
- package/dist/{strategy-OI4WRB3S.mjs → strategy-XTUKPYPM.mjs} +466 -120
- package/package.json +5 -5
- package/dist/chunk-3PN7ZSJJ.mjs +0 -27
- package/dist/chunk-RDK2JK3K.mjs +0 -28
package/dist/index.js
CHANGED
|
@@ -34,25 +34,25 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
34
34
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
35
35
|
|
|
36
36
|
// src/AdaptiveMomentumRibbon/adapters/ai.ts
|
|
37
|
-
var import_strategies, ADAPTIVE_MOMENTUM_RIBBON_CONTEXT_PROMPT, ADAPTIVE_MOMENTUM_RIBBON_PAYLOAD_PROMPT, toFiniteNumberOrNull, getLastFiniteNumber, getBias, getSignalDirection, asBoolean, getAdaptiveMomentumRibbonSnapshot, isAtLeast, isInRange, getDirectionalInvalidationDistancePct, getDirectionalRewardPct, getChannelState, getRetestPrice, getDeterministicAdaptiveMomentumRibbonQuality, getHardBlockReasonText, buildAdaptiveMomentumRibbonContext, getAdaptiveMomentumRibbonContextFromPayload, clampQuality, postProcessAnalysis, adaptiveMomentumRibbonAiAdapter;
|
|
37
|
+
var import_strategies, ADAPTIVE_MOMENTUM_RIBBON_CONTEXT_PROMPT, ADAPTIVE_MOMENTUM_RIBBON_PAYLOAD_PROMPT, toFiniteNumberOrNull, getLastFiniteNumber, getBias, getSignalDirection, asBoolean, getRecord, getPrimarySession, getAdaptiveMomentumRibbonSnapshot, getAdaptiveMomentumRibbonConfigSnapshot, isAtLeast, isInRange, getDirectionalInvalidationDistancePct, getDirectionalRewardPct, getDirectionalChannelExtensionPct, getChannelState, getRetestPrice, getDeterministicAdaptiveMomentumRibbonQuality, getHardBlockReasonText, buildAdaptiveMomentumRibbonContext, getAdaptiveMomentumRibbonContextFromPayload, clampQuality, postProcessAnalysis, adaptiveMomentumRibbonAiAdapter;
|
|
38
38
|
var init_ai = __esm({
|
|
39
39
|
"src/AdaptiveMomentumRibbon/adapters/ai.ts"() {
|
|
40
40
|
"use strict";
|
|
41
41
|
import_strategies = require("@tradejs/core/strategies");
|
|
42
42
|
ADAPTIVE_MOMENTUM_RIBBON_CONTEXT_PROMPT = `
|
|
43
|
-
|
|
44
|
-
-
|
|
45
|
-
- LONG
|
|
46
|
-
- invalidationLevel
|
|
47
|
-
- channelState
|
|
48
|
-
- invalidationDistancePct
|
|
49
|
-
-
|
|
50
|
-
-
|
|
43
|
+
AdaptiveMomentumRibbon addon:
|
|
44
|
+
- This is a momentum entry based on an oscillator zero-cross, not a trendline breakout and not a line-reversal setup.
|
|
45
|
+
- LONG appears when \`signalOsc\` crosses above 0 and the ribbon switches into \`activeBuy\`; SHORT is the mirror case.
|
|
46
|
+
- \`invalidationLevel\` is the structural invalidation level on the signal bar. If \`invalidated=true\` or \`invalidationLevel\` sits on the wrong side of the current price, do not treat the setup as confirmed.
|
|
47
|
+
- \`channelState\` and \`channelBiasAligned\` describe where price sits relative to the Keltner Channel. For LONG it is a negative sign if price is still below \`kcMidline\`; for SHORT it is a negative sign if price is above \`kcMidline\`.
|
|
48
|
+
- \`invalidationDistancePct\` and \`structuralRewardRiskRatio\` describe how compact the structure is. Do not overstate quality when invalidation is too wide or reward/risk versus invalidation is weak.
|
|
49
|
+
- \`quality=5\` requires very clean momentum: correct channel side, strong \`signalOsc\`, sane invalidation distance, and no coin/BTC bias conflicts.
|
|
50
|
+
- If \`approvalAllowedNow=false\` or \`deterministicQuality<4\`, this is usually watch mode rather than a ready live approval.
|
|
51
51
|
`;
|
|
52
52
|
ADAPTIVE_MOMENTUM_RIBBON_PAYLOAD_PROMPT = `
|
|
53
|
-
-
|
|
54
|
-
signalOsc / oscillatorStrength / channelState / invalidationDistancePct / structuralRewardRiskRatio / coinBiasAligned / btcBiasAligned / deterministicQuality / approvalAllowedNow / structuralHardBlockReasons.
|
|
55
|
-
-
|
|
53
|
+
- \`payload.additionalIndicators.adaptiveMomentumRibbonContext\` contains a compact signal summary:
|
|
54
|
+
signalOsc / oscillatorStrength / channelState / channelExtensionPct / invalidationDistancePct / structuralRewardRiskRatio / coinBiasAligned / btcBiasAligned / deterministicQuality / approvalAllowedNow / structuralHardBlockReasons.
|
|
55
|
+
- Use this context as the primary strategy-specific interpretation instead of re-deriving it only from generic series.
|
|
56
56
|
`;
|
|
57
57
|
toFiniteNumberOrNull = (value) => {
|
|
58
58
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
@@ -90,11 +90,49 @@ var init_ai = __esm({
|
|
|
90
90
|
};
|
|
91
91
|
getSignalDirection = (signal) => signal.direction === "LONG" || signal.direction === "SHORT" ? signal.direction : null;
|
|
92
92
|
asBoolean = (value) => value === true || value === 1;
|
|
93
|
+
getRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
94
|
+
getPrimarySession = (signal, additionalIndicators) => {
|
|
95
|
+
const marketContext = getRecord(
|
|
96
|
+
additionalIndicators?.marketContext ?? signal.additionalIndicators?.marketContext
|
|
97
|
+
);
|
|
98
|
+
const tradingSession = getRecord(marketContext?.tradingSession);
|
|
99
|
+
const primarySession = tradingSession?.primarySession;
|
|
100
|
+
if (primarySession === "asia" || primarySession === "europe" || primarySession === "us" || primarySession === "off_hours") {
|
|
101
|
+
return primarySession;
|
|
102
|
+
}
|
|
103
|
+
const timestamp = toFiniteNumberOrNull(signal.timestamp);
|
|
104
|
+
if (timestamp == null) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
const date = new Date(timestamp);
|
|
108
|
+
const minuteUtc = date.getUTCHours() * 60 + date.getUTCMinutes();
|
|
109
|
+
const activeSessions = [
|
|
110
|
+
minuteUtc >= 0 && minuteUtc < 8 * 60 ? "asia" : null,
|
|
111
|
+
minuteUtc >= 7 * 60 && minuteUtc < 16 * 60 ? "europe" : null,
|
|
112
|
+
minuteUtc >= 13 * 60 && minuteUtc < 22 * 60 ? "us" : null
|
|
113
|
+
].filter(
|
|
114
|
+
(session) => session != null
|
|
115
|
+
);
|
|
116
|
+
if (activeSessions.includes("us")) {
|
|
117
|
+
return "us";
|
|
118
|
+
}
|
|
119
|
+
if (activeSessions.includes("europe")) {
|
|
120
|
+
return "europe";
|
|
121
|
+
}
|
|
122
|
+
if (activeSessions.includes("asia")) {
|
|
123
|
+
return "asia";
|
|
124
|
+
}
|
|
125
|
+
return "off_hours";
|
|
126
|
+
};
|
|
93
127
|
getAdaptiveMomentumRibbonSnapshot = (signal) => {
|
|
94
|
-
const additional = signal.additionalIndicators;
|
|
128
|
+
const additional = getRecord(signal.additionalIndicators);
|
|
95
129
|
const amr = additional?.amr;
|
|
96
130
|
return amr && typeof amr === "object" ? amr : {};
|
|
97
131
|
};
|
|
132
|
+
getAdaptiveMomentumRibbonConfigSnapshot = (signal) => {
|
|
133
|
+
const additional = getRecord(signal.additionalIndicators);
|
|
134
|
+
return getRecord(additional?.amrConfigSnapshot);
|
|
135
|
+
};
|
|
98
136
|
isAtLeast = (value, threshold) => value != null && value >= threshold;
|
|
99
137
|
isInRange = (value, min, max) => value != null && value >= min && value <= max;
|
|
100
138
|
getDirectionalInvalidationDistancePct = ({
|
|
@@ -126,6 +164,26 @@ var init_ai = __esm({
|
|
|
126
164
|
}
|
|
127
165
|
return signalDirection === "LONG" ? (takeProfitPrice - currentPrice) / currentPrice * 100 : (currentPrice - takeProfitPrice) / currentPrice * 100;
|
|
128
166
|
};
|
|
167
|
+
getDirectionalChannelExtensionPct = ({
|
|
168
|
+
signalDirection,
|
|
169
|
+
currentPrice,
|
|
170
|
+
kcUpper,
|
|
171
|
+
kcLower
|
|
172
|
+
}) => {
|
|
173
|
+
if (signalDirection == null || currentPrice == null || currentPrice <= 0) {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
if (signalDirection === "LONG") {
|
|
177
|
+
if (kcUpper == null || currentPrice <= kcUpper) {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
return (currentPrice - kcUpper) / currentPrice * 100;
|
|
181
|
+
}
|
|
182
|
+
if (kcLower == null || currentPrice >= kcLower) {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
return (kcLower - currentPrice) / currentPrice * 100;
|
|
186
|
+
};
|
|
129
187
|
getChannelState = ({
|
|
130
188
|
signalDirection,
|
|
131
189
|
currentPrice,
|
|
@@ -178,8 +236,8 @@ var init_ai = __esm({
|
|
|
178
236
|
if (context.hardBlockReasons.length > 0) {
|
|
179
237
|
return 2;
|
|
180
238
|
}
|
|
181
|
-
const noBiasConflict = context.coinBiasAligned === true && context.btcBiasAligned === true;
|
|
182
239
|
const biasConflictCount = Number(context.coinBiasAligned === false) + Number(context.btcBiasAligned === false);
|
|
240
|
+
const noBiasConflict = biasConflictCount === 0;
|
|
183
241
|
const oscillatorModerate = isAtLeast(context.oscillatorStrength, 0.3);
|
|
184
242
|
const oscillatorStrong = isAtLeast(context.oscillatorStrength, 0.55);
|
|
185
243
|
const oscillatorElite = isAtLeast(context.oscillatorStrength, 0.9);
|
|
@@ -200,11 +258,16 @@ var init_ai = __esm({
|
|
|
200
258
|
const structuralRrStrong = isAtLeast(context.structuralRewardRiskRatio, 1.8);
|
|
201
259
|
const channelSupportive = context.channelBiasAligned === true;
|
|
202
260
|
const channelExpansion = context.signalDirection === "LONG" ? context.channelState === "above_upper" : context.channelState === "below_lower";
|
|
203
|
-
const
|
|
204
|
-
|
|
261
|
+
const channelExtensionStrong = isAtLeast(context.channelExtensionPct, 0.08);
|
|
262
|
+
const sessionAllowsApproval = context.sessionAllowsApproval !== false;
|
|
263
|
+
const slowestDetector = context.momentumPeriod === 48 && context.butterworthSmoothing === 6;
|
|
264
|
+
if (!sessionAllowsApproval) {
|
|
265
|
+
return 3;
|
|
266
|
+
}
|
|
267
|
+
if (channelSupportive && channelExpansion && channelExtensionStrong && oscillatorElite && invalidationTight && structuralRrStrong && noBiasConflict) {
|
|
205
268
|
return 5;
|
|
206
269
|
}
|
|
207
|
-
if (channelSupportive &&
|
|
270
|
+
if (channelSupportive && channelExpansion && !slowestDetector && oscillatorModerate && invalidationCompact && structuralRrModerate && biasConflictCount < 2 && (biasConflictCount === 0 || oscillatorStrong)) {
|
|
208
271
|
return 4;
|
|
209
272
|
}
|
|
210
273
|
return 3;
|
|
@@ -212,20 +275,25 @@ var init_ai = __esm({
|
|
|
212
275
|
getHardBlockReasonText = (reason) => {
|
|
213
276
|
switch (reason) {
|
|
214
277
|
case "invalidated":
|
|
215
|
-
return "
|
|
278
|
+
return "the signal is already invalidated relative to invalidationLevel";
|
|
216
279
|
case "inactive_signal_state":
|
|
217
|
-
return "active ribbon state
|
|
280
|
+
return "the active ribbon state does not confirm the current direction";
|
|
218
281
|
case "oscillator_conflict":
|
|
219
|
-
return "signalOsc
|
|
282
|
+
return "signalOsc conflicts with the signal direction";
|
|
220
283
|
case "invalidation_wrong_side":
|
|
221
|
-
return "invalidationLevel
|
|
284
|
+
return "invalidationLevel is on the wrong side of the current price";
|
|
222
285
|
default:
|
|
223
286
|
return reason;
|
|
224
287
|
}
|
|
225
288
|
};
|
|
226
|
-
buildAdaptiveMomentumRibbonContext = (signal) => {
|
|
289
|
+
buildAdaptiveMomentumRibbonContext = (signal, additionalIndicators) => {
|
|
227
290
|
const signalDirection = getSignalDirection(signal);
|
|
228
291
|
const snapshot = getAdaptiveMomentumRibbonSnapshot(signal);
|
|
292
|
+
const configSnapshot = getAdaptiveMomentumRibbonConfigSnapshot(signal);
|
|
293
|
+
const momentumPeriod = toFiniteNumberOrNull(configSnapshot?.momentumPeriod);
|
|
294
|
+
const butterworthSmoothing = toFiniteNumberOrNull(
|
|
295
|
+
configSnapshot?.butterworthSmoothing
|
|
296
|
+
);
|
|
229
297
|
const currentPrice = toFiniteNumberOrNull(signal.prices?.currentPrice);
|
|
230
298
|
const takeProfitPrice = toFiniteNumberOrNull(signal.prices?.takeProfitPrice);
|
|
231
299
|
const signalOsc = toFiniteNumberOrNull(snapshot.signalOsc);
|
|
@@ -247,6 +315,12 @@ var init_ai = __esm({
|
|
|
247
315
|
kcLower
|
|
248
316
|
});
|
|
249
317
|
const channelBiasAligned = signalDirection === "LONG" ? kcMidline != null && currentPrice != null ? currentPrice >= kcMidline : null : signalDirection === "SHORT" ? kcMidline != null && currentPrice != null ? currentPrice <= kcMidline : null : null;
|
|
318
|
+
const channelExtensionPct = getDirectionalChannelExtensionPct({
|
|
319
|
+
signalDirection,
|
|
320
|
+
currentPrice,
|
|
321
|
+
kcUpper,
|
|
322
|
+
kcLower
|
|
323
|
+
});
|
|
250
324
|
const invalidationDistancePct = getDirectionalInvalidationDistancePct({
|
|
251
325
|
signalDirection,
|
|
252
326
|
currentPrice,
|
|
@@ -268,6 +342,8 @@ var init_ai = __esm({
|
|
|
268
342
|
);
|
|
269
343
|
const coinBiasAligned = signalDirection === "LONG" ? coinBias == null ? null : coinBias === "bullish" : signalDirection === "SHORT" ? coinBias == null ? null : coinBias === "bearish" : null;
|
|
270
344
|
const btcBiasAligned = signalDirection === "LONG" ? btcBias == null ? null : btcBias === "bullish" : signalDirection === "SHORT" ? btcBias == null ? null : btcBias === "bearish" : null;
|
|
345
|
+
const primarySession = getPrimarySession(signal, additionalIndicators);
|
|
346
|
+
const sessionAllowsApproval = primarySession == null ? null : primarySession === "off_hours";
|
|
271
347
|
const hardBlockReasons = [];
|
|
272
348
|
if (invalidated) {
|
|
273
349
|
hardBlockReasons.push("invalidated");
|
|
@@ -283,6 +359,8 @@ var init_ai = __esm({
|
|
|
283
359
|
}
|
|
284
360
|
const deterministicQuality = getDeterministicAdaptiveMomentumRibbonQuality({
|
|
285
361
|
signalDirection,
|
|
362
|
+
momentumPeriod,
|
|
363
|
+
butterworthSmoothing,
|
|
286
364
|
entryLong,
|
|
287
365
|
entryShort,
|
|
288
366
|
activeBuy,
|
|
@@ -296,16 +374,21 @@ var init_ai = __esm({
|
|
|
296
374
|
invalidationLevel,
|
|
297
375
|
channelState,
|
|
298
376
|
channelBiasAligned,
|
|
377
|
+
channelExtensionPct,
|
|
299
378
|
invalidationDistancePct,
|
|
300
379
|
structuralRewardRiskRatio,
|
|
301
380
|
coinMaBias: coinBias,
|
|
302
381
|
btcMaBias: btcBias,
|
|
303
382
|
coinBiasAligned,
|
|
304
383
|
btcBiasAligned,
|
|
384
|
+
primarySession,
|
|
385
|
+
sessionAllowsApproval,
|
|
305
386
|
hardBlockReasons
|
|
306
387
|
});
|
|
307
388
|
return {
|
|
308
389
|
signalDirection,
|
|
390
|
+
momentumPeriod,
|
|
391
|
+
butterworthSmoothing,
|
|
309
392
|
entryLong,
|
|
310
393
|
entryShort,
|
|
311
394
|
activeBuy,
|
|
@@ -319,12 +402,15 @@ var init_ai = __esm({
|
|
|
319
402
|
invalidationLevel,
|
|
320
403
|
channelState,
|
|
321
404
|
channelBiasAligned,
|
|
405
|
+
channelExtensionPct,
|
|
322
406
|
invalidationDistancePct,
|
|
323
407
|
structuralRewardRiskRatio,
|
|
324
408
|
coinMaBias: coinBias,
|
|
325
409
|
btcMaBias: btcBias,
|
|
326
410
|
coinBiasAligned,
|
|
327
411
|
btcBiasAligned,
|
|
412
|
+
primarySession,
|
|
413
|
+
sessionAllowsApproval,
|
|
328
414
|
hardBlockReasons,
|
|
329
415
|
structuralHardBlockReasons: hardBlockReasons,
|
|
330
416
|
deterministicQuality,
|
|
@@ -365,9 +451,9 @@ var init_ai = __esm({
|
|
|
365
451
|
retestPrice,
|
|
366
452
|
takeProfitPrice: null,
|
|
367
453
|
stopLossPrice: null,
|
|
368
|
-
qualityReason: analysis.qualityReason || (context.hardBlockReasons.length > 0 ? `AdaptiveMomentumRibbon guardrail: ${context.hardBlockReasons.map(getHardBlockReasonText).join("; ")}.` : "AdaptiveMomentumRibbon
|
|
369
|
-
triggerInvalidation: analysis.triggerInvalidation || (retestPrice != null ?
|
|
370
|
-
comment: analysis.comment || (context.hardBlockReasons.length > 0 ? `AdaptiveMomentumRibbon
|
|
454
|
+
qualityReason: analysis.qualityReason || (context.hardBlockReasons.length > 0 ? `AdaptiveMomentumRibbon guardrail: ${context.hardBlockReasons.map(getHardBlockReasonText).join("; ")}.` : "AdaptiveMomentumRibbon keeps the setup in watch mode until momentum confirmation becomes cleaner."),
|
|
455
|
+
triggerInvalidation: analysis.triggerInvalidation || (retestPrice != null ? `Wait for confirmation relative to level ${retestPrice}.` : "Wait for cleaner momentum confirmation and better price positioning inside the Keltner channel."),
|
|
456
|
+
comment: analysis.comment || (context.hardBlockReasons.length > 0 ? `AdaptiveMomentumRibbon rejected: ${context.hardBlockReasons.map(getHardBlockReasonText).join("; ")}.` : "AdaptiveMomentumRibbon keeps the signal in watch mode until continuation confirmation becomes cleaner.")
|
|
371
457
|
};
|
|
372
458
|
}
|
|
373
459
|
return {
|
|
@@ -381,13 +467,19 @@ var init_ai = __esm({
|
|
|
381
467
|
};
|
|
382
468
|
};
|
|
383
469
|
adaptiveMomentumRibbonAiAdapter = {
|
|
384
|
-
buildPayload: ({ signal, basePayload }) =>
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
...basePayload
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
470
|
+
buildPayload: ({ signal, basePayload }) => {
|
|
471
|
+
const additionalIndicators = getRecord(basePayload.additionalIndicators);
|
|
472
|
+
return {
|
|
473
|
+
...basePayload,
|
|
474
|
+
additionalIndicators: {
|
|
475
|
+
...additionalIndicators ?? {},
|
|
476
|
+
adaptiveMomentumRibbonContext: buildAdaptiveMomentumRibbonContext(
|
|
477
|
+
signal,
|
|
478
|
+
additionalIndicators
|
|
479
|
+
)
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
},
|
|
391
483
|
postProcessAnalysis,
|
|
392
484
|
buildSystemPromptAddon: () => `${ADAPTIVE_MOMENTUM_RIBBON_CONTEXT_PROMPT}
|
|
393
485
|
${ADAPTIVE_MOMENTUM_RIBBON_PAYLOAD_PROMPT}`,
|
|
@@ -398,27 +490,32 @@ ${ADAPTIVE_MOMENTUM_RIBBON_PAYLOAD_PROMPT}`,
|
|
|
398
490
|
);
|
|
399
491
|
return `
|
|
400
492
|
|
|
401
|
-
|
|
493
|
+
Additional AdaptiveMomentumRibbon context:
|
|
494
|
+
- momentumPeriod=${context.momentumPeriod ?? "n/a"}
|
|
495
|
+
- butterworthSmoothing=${context.butterworthSmoothing ?? "n/a"}
|
|
402
496
|
- signalOsc=${context.signalOsc?.toFixed?.(3) ?? "n/a"}
|
|
403
497
|
- oscillatorStrength=${context.oscillatorStrength?.toFixed?.(3) ?? "n/a"}
|
|
404
498
|
- channelState=${context.channelState}
|
|
405
499
|
- channelBiasAligned=${context.channelBiasAligned}
|
|
500
|
+
- channelExtensionPct=${context.channelExtensionPct?.toFixed?.(3) ?? "n/a"}%
|
|
406
501
|
- invalidationDistancePct=${context.invalidationDistancePct?.toFixed?.(3) ?? "n/a"}%
|
|
407
502
|
- structuralRewardRiskRatio=${context.structuralRewardRiskRatio?.toFixed?.(3) ?? "n/a"}
|
|
408
503
|
- coinBiasAligned=${context.coinBiasAligned}
|
|
409
504
|
- btcBiasAligned=${context.btcBiasAligned}
|
|
505
|
+
- primarySession=${context.primarySession ?? "n/a"}
|
|
506
|
+
- sessionAllowsApproval=${context.sessionAllowsApproval}
|
|
410
507
|
- deterministicQuality=${context.deterministicQuality}
|
|
411
508
|
- approvalAllowedNow=${context.approvalAllowedNow}
|
|
412
509
|
- hardBlockReasons=${context.hardBlockReasons.join(", ") || "none"}
|
|
413
510
|
|
|
414
|
-
|
|
415
|
-
- zero-cross
|
|
416
|
-
-
|
|
417
|
-
-
|
|
511
|
+
Interpretation rules for AdaptiveMomentumRibbon:
|
|
512
|
+
- a zero-cross alone does not make quality high;
|
|
513
|
+
- pay attention to Keltner channel side, sane invalidation distance, and bias alignment;
|
|
514
|
+
- if \`signalOsc\` already conflicts with direction or the signal is invalidated, do not treat the entry as confirmed.
|
|
418
515
|
`;
|
|
419
516
|
},
|
|
420
|
-
mapEntryRuntimeFromConfig: (
|
|
421
|
-
|
|
517
|
+
mapEntryRuntimeFromConfig: (config8) => (0, import_strategies.mapAiRuntimeFromConfig)(
|
|
518
|
+
config8
|
|
422
519
|
)
|
|
423
520
|
};
|
|
424
521
|
}
|
|
@@ -431,8 +528,8 @@ var init_ml = __esm({
|
|
|
431
528
|
"use strict";
|
|
432
529
|
import_strategies2 = require("@tradejs/core/strategies");
|
|
433
530
|
adaptiveMomentumRibbonMlAdapter = {
|
|
434
|
-
mapEntryRuntimeFromConfig: (
|
|
435
|
-
|
|
531
|
+
mapEntryRuntimeFromConfig: (config8) => (0, import_strategies2.mapMlRuntimeFromConfig)(
|
|
532
|
+
config8
|
|
436
533
|
)
|
|
437
534
|
};
|
|
438
535
|
}
|
|
@@ -469,7 +566,7 @@ var init_ml2 = __esm({
|
|
|
469
566
|
"use strict";
|
|
470
567
|
import_strategies3 = require("@tradejs/core/strategies");
|
|
471
568
|
breakoutMlAdapter = {
|
|
472
|
-
mapEntryRuntimeFromConfig: (
|
|
569
|
+
mapEntryRuntimeFromConfig: (config8) => (0, import_strategies3.mapMlRuntimeFromConfig)(config8)
|
|
473
570
|
};
|
|
474
571
|
}
|
|
475
572
|
});
|
|
@@ -504,8 +601,8 @@ var init_ai3 = __esm({
|
|
|
504
601
|
"use strict";
|
|
505
602
|
import_strategies4 = require("@tradejs/core/strategies");
|
|
506
603
|
maStrategyAiAdapter = {
|
|
507
|
-
mapEntryRuntimeFromConfig: (
|
|
508
|
-
|
|
604
|
+
mapEntryRuntimeFromConfig: (config8) => (0, import_strategies4.mapAiRuntimeFromConfig)(
|
|
605
|
+
config8
|
|
509
606
|
)
|
|
510
607
|
};
|
|
511
608
|
}
|
|
@@ -518,8 +615,8 @@ var init_ml3 = __esm({
|
|
|
518
615
|
"use strict";
|
|
519
616
|
import_strategies5 = require("@tradejs/core/strategies");
|
|
520
617
|
maStrategyMlAdapter = {
|
|
521
|
-
mapEntryRuntimeFromConfig: (
|
|
522
|
-
|
|
618
|
+
mapEntryRuntimeFromConfig: (config8) => (0, import_strategies5.mapMlRuntimeFromConfig)(
|
|
619
|
+
config8
|
|
523
620
|
)
|
|
524
621
|
};
|
|
525
622
|
}
|
|
@@ -540,18 +637,6 @@ var init_manifest3 = __esm({
|
|
|
540
637
|
}
|
|
541
638
|
});
|
|
542
639
|
|
|
543
|
-
// src/ReverseTrendLine/hooks.ts
|
|
544
|
-
var import_strategies6, reverseTrendLineBeforePlaceOrderHook;
|
|
545
|
-
var init_hooks = __esm({
|
|
546
|
-
"src/ReverseTrendLine/hooks.ts"() {
|
|
547
|
-
"use strict";
|
|
548
|
-
import_strategies6 = require("@tradejs/node/strategies");
|
|
549
|
-
reverseTrendLineBeforePlaceOrderHook = (0, import_strategies6.createCloseOppositeBeforePlaceOrderHook)({
|
|
550
|
-
isEnabled: (config7) => Boolean(config7.CLOSE_OPPOSITE_POSITIONS)
|
|
551
|
-
});
|
|
552
|
-
}
|
|
553
|
-
});
|
|
554
|
-
|
|
555
640
|
// src/ReverseTrendLine/guardrails.ts
|
|
556
641
|
var REVERSE_TRENDLINE_NEAR_LINE_PCT, REVERSE_TRENDLINE_FAILED_BOUNCE_PCT, REVERSE_TRENDLINE_TIMING_WINDOW, MIN_REJECTION_WICK_PCT, MIN_REJECTION_STRENGTH_PCT, FOLLOW_THROUGH_STRENGTH_PCT, toFiniteNumberOrNull2, getLastFiniteNumber2, getBias2, getSpreadPct, getTrendLineFromPayload, deriveDirectionFromMode, getSortedTrendLinePoints, buildTrendLineEvaluator, getCurrentCandle, getLineTouched, getCloseOnBounceSide, getFailedBounceBreak, getBodyAligned, getRejectionWickPct, getRejectionStrengthPct, getRejectionBar, buildReverseTrendlineStructuralContext, buildReverseTrendlineTimingContext;
|
|
557
642
|
var init_guardrails = __esm({
|
|
@@ -943,30 +1028,30 @@ var init_guardrails = __esm({
|
|
|
943
1028
|
});
|
|
944
1029
|
|
|
945
1030
|
// src/ReverseTrendLine/adapters/ai.ts
|
|
946
|
-
var
|
|
1031
|
+
var import_strategies6, REVERSE_TRENDLINE_CONTEXT_PROMPT, REVERSE_TRENDLINE_PAYLOAD_PROMPT, getReverseTrendlineBiasConflictState, getDeterministicReverseTrendlineQuality, getDeterministicReverseTrendlineRejectionScore, buildReverseTrendlineAiContext, getReverseTrendlineContextFromPayload, getHardBlockReasonText2, reverseTrendLineAiAdapter;
|
|
947
1032
|
var init_ai4 = __esm({
|
|
948
1033
|
"src/ReverseTrendLine/adapters/ai.ts"() {
|
|
949
1034
|
"use strict";
|
|
950
|
-
|
|
1035
|
+
import_strategies6 = require("@tradejs/core/strategies");
|
|
951
1036
|
init_guardrails();
|
|
952
1037
|
REVERSE_TRENDLINE_CONTEXT_PROMPT = `
|
|
953
|
-
|
|
954
|
-
-
|
|
955
|
-
-
|
|
956
|
-
-
|
|
957
|
-
-
|
|
958
|
-
-
|
|
959
|
-
-
|
|
960
|
-
-
|
|
961
|
-
-
|
|
962
|
-
-
|
|
963
|
-
-
|
|
964
|
-
-
|
|
965
|
-
-
|
|
1038
|
+
ReverseTrendLine addon:
|
|
1039
|
+
- This is a trendline bounce strategy, not a breakout strategy.
|
|
1040
|
+
- For LONG on a support line (\`trendline.mode="lows"\`), you need a touch or false break of the line followed by a close back above it.
|
|
1041
|
+
- For SHORT on a resistance line (\`trendline.mode="highs"\`), you need a touch or false break of the line followed by a close back below it.
|
|
1042
|
+
- If price has already broken through the line with conviction in the opposite direction, this is not a bounce setup: use \`direction=null\` and \`quality <= 2\`.
|
|
1043
|
+
- For bounce setups, prioritize candle reaction at the line, rejection wick quality, a close on the correct side, and next-bar follow-through.
|
|
1044
|
+
- If \`payload.additionalIndicators.reverseTrendlineContext.failedBounceBreak=true\`, do not treat the signal as structurally confirmed.
|
|
1045
|
+
- If \`payload.additionalIndicators.reverseTrendlineContext.entryTiming\` is not \`ready_rejection\` or \`ready_follow_through\`, quality is usually \`<= 3\`.
|
|
1046
|
+
- Baseline deterministic approval for same-bar rejection is intentionally strict:
|
|
1047
|
+
- a strong conflict-only rejection may qualify for \`quality=4\`;
|
|
1048
|
+
- some same-bar rejections with \`conflictState=none\` or \`both\` may reach \`quality=4\` only with a very strong deterministic rejection score.
|
|
1049
|
+
- For SHORT bounce setups with \`btc_bias_conflict\`, do not overstate quality; those cases usually stay in watch mode unless the structural confirmation is much stronger.
|
|
1050
|
+
- If \`deterministicRejectionScore\` is low or medium, do not assign \`quality=4\` just because the candle visually resembles a rejection.
|
|
966
1051
|
`;
|
|
967
1052
|
REVERSE_TRENDLINE_PAYLOAD_PROMPT = `
|
|
968
|
-
-
|
|
969
|
-
-
|
|
1053
|
+
- \`payload.figures.trendline\` contains the line geometry.
|
|
1054
|
+
- \`payload.additionalIndicators.reverseTrendlineContext\` contains a compact bounce summary: direction, price distance to the line, whether the line was touched, whether there was a rejection candle, rejection strength, timing stage, bias conflicts, and \`deterministicRejectionScore\`.
|
|
970
1055
|
`;
|
|
971
1056
|
getReverseTrendlineBiasConflictState = (context) => {
|
|
972
1057
|
const coinConflict = context.coinBiasAligned === false;
|
|
@@ -1007,12 +1092,16 @@ var init_ai4 = __esm({
|
|
|
1007
1092
|
if (quality4FollowThrough) {
|
|
1008
1093
|
return 4;
|
|
1009
1094
|
}
|
|
1010
|
-
const quality4ConflictRejection = context.entryTiming === "ready_rejection" && conflictOnly && rejectionStrengthPct >= 0.45 && touches >= 5 && !(context.signalDirection === "SHORT" && biasConflictState === "btc_only");
|
|
1095
|
+
const quality4ConflictRejection = context.entryTiming === "ready_rejection" && conflictOnly && rejectionStrengthPct >= 0.45 && touches >= 5 && !(context.signalDirection === "SHORT" && biasConflictState === "coin_only" && distance <= 180 && rejectionWickPct <= 0.45) && !(context.signalDirection === "SHORT" && biasConflictState === "btc_only");
|
|
1011
1096
|
if (quality4ConflictRejection) {
|
|
1012
1097
|
return 4;
|
|
1013
1098
|
}
|
|
1014
1099
|
const rejectionScore = getDeterministicReverseTrendlineRejectionScore(context);
|
|
1015
|
-
const
|
|
1100
|
+
const quality4EliteShortBtcOnlyRejection = context.entryTiming === "ready_rejection" && context.signalDirection === "SHORT" && biasConflictState === "btc_only" && rejectionScore != null && rejectionScore >= 5 && rejectionWickPct >= 0.6 && touches >= 5 && distance <= 200;
|
|
1101
|
+
if (quality4EliteShortBtcOnlyRejection) {
|
|
1102
|
+
return 4;
|
|
1103
|
+
}
|
|
1104
|
+
const quality4ScoredRejection = context.entryTiming === "ready_rejection" && (biasConflictState === "none" || biasConflictState === "both") && rejectionScore != null && rejectionScore >= 7 && !(context.signalDirection === "SHORT" && biasConflictState === "none" && distance <= 150 && (rejectionWickPct >= 0.7 || rejectionStrengthPct >= 1.3));
|
|
1016
1105
|
if (quality4ScoredRejection) {
|
|
1017
1106
|
return 4;
|
|
1018
1107
|
}
|
|
@@ -1099,11 +1188,11 @@ var init_ai4 = __esm({
|
|
|
1099
1188
|
getHardBlockReasonText2 = (reason) => {
|
|
1100
1189
|
switch (reason) {
|
|
1101
1190
|
case "failed_bounce_break":
|
|
1102
|
-
return "
|
|
1191
|
+
return "price broke through the line against the intended bounce";
|
|
1103
1192
|
case "coin_bias_conflict":
|
|
1104
|
-
return "bias
|
|
1193
|
+
return "coin bias conflicts with the bounce direction";
|
|
1105
1194
|
case "btc_bias_conflict":
|
|
1106
|
-
return "BTC
|
|
1195
|
+
return "BTC context conflicts with the bounce direction";
|
|
1107
1196
|
default:
|
|
1108
1197
|
return reason;
|
|
1109
1198
|
}
|
|
@@ -1142,9 +1231,9 @@ var init_ai4 = __esm({
|
|
|
1142
1231
|
retestPrice: context.currentLinePrice ?? null,
|
|
1143
1232
|
takeProfitPrice: null,
|
|
1144
1233
|
stopLossPrice: null,
|
|
1145
|
-
qualityReason: context.hardBlockReasons.length > 0 ? `ReverseTrendLine guardrail: ${context.hardBlockReasons.map(getHardBlockReasonText2).join("; ")}.` : "ReverseTrendLine deterministic quality
|
|
1146
|
-
triggerInvalidation: context.hardBlockReasons.length > 0 ?
|
|
1147
|
-
comment: context.hardBlockReasons.length > 0 ? `ReverseTrendLine guardrail
|
|
1234
|
+
qualityReason: context.hardBlockReasons.length > 0 ? `ReverseTrendLine guardrail: ${context.hardBlockReasons.map(getHardBlockReasonText2).join("; ")}.` : "ReverseTrendLine deterministic quality requires either a strong conflict-only rejection or a confirmed aligned follow-through for a bounce.",
|
|
1235
|
+
triggerInvalidation: context.hardBlockReasons.length > 0 ? `Wait for a new bounce setup: ${context.hardBlockReasons.map(getHardBlockReasonText2).join("; ")}.` : "Wait for a line touch, a rejection candle, and a close held on the correct side of the line.",
|
|
1236
|
+
comment: context.hardBlockReasons.length > 0 ? `ReverseTrendLine guardrail blocked the entry: ${context.hardBlockReasons.map(getHardBlockReasonText2).join("; ")}.` : "ReverseTrendLine keeps the setup in watch mode until the bounce is confirmed."
|
|
1148
1237
|
};
|
|
1149
1238
|
},
|
|
1150
1239
|
buildSystemPromptAddon: () => `${REVERSE_TRENDLINE_CONTEXT_PROMPT}
|
|
@@ -1153,7 +1242,7 @@ ${REVERSE_TRENDLINE_PAYLOAD_PROMPT}`,
|
|
|
1153
1242
|
const context = getReverseTrendlineContextFromPayload(payload, signal);
|
|
1154
1243
|
return `
|
|
1155
1244
|
|
|
1156
|
-
|
|
1245
|
+
Additional ReverseTrendLine context:
|
|
1157
1246
|
- entryTiming=${context.entryTiming}
|
|
1158
1247
|
- lineTouchedNow=${context.lineTouchedNow}
|
|
1159
1248
|
- closeOnBounceSide=${context.closeOnBounceSide}
|
|
@@ -1168,15 +1257,15 @@ ${REVERSE_TRENDLINE_PAYLOAD_PROMPT}`,
|
|
|
1168
1257
|
- approvalAllowedNow=${context.approvalAllowedNow}
|
|
1169
1258
|
- hardBlockReasons=${context.hardBlockReasons.join(", ") || "none"}
|
|
1170
1259
|
|
|
1171
|
-
|
|
1172
|
-
-
|
|
1173
|
-
-
|
|
1174
|
-
-
|
|
1175
|
-
-
|
|
1260
|
+
Interpretation rules for ReverseTrendLine:
|
|
1261
|
+
- look for structural confirmation of a reaction from the line, not a breakout through the line;
|
|
1262
|
+
- if \`failedBounceBreak=true\` is already present, do not treat the signal as confirmed;
|
|
1263
|
+
- if the setup is still in \`wait_touch\`, \`wait_reaction_confirmation\`, or \`stale_reaction\`, do not overstate quality;
|
|
1264
|
+
- if \`deterministicRejectionScore\` is high, use it only as an extra signal together with the proper bounce context, not as a replacement for structure.
|
|
1176
1265
|
`;
|
|
1177
1266
|
},
|
|
1178
|
-
mapEntryRuntimeFromConfig: (
|
|
1179
|
-
|
|
1267
|
+
mapEntryRuntimeFromConfig: (config8) => (0, import_strategies6.mapAiRuntimeFromConfig)(
|
|
1268
|
+
config8
|
|
1180
1269
|
)
|
|
1181
1270
|
};
|
|
1182
1271
|
}
|
|
@@ -1187,18 +1276,135 @@ var reverseTrendLineManifest;
|
|
|
1187
1276
|
var init_manifest4 = __esm({
|
|
1188
1277
|
"src/ReverseTrendLine/manifest.ts"() {
|
|
1189
1278
|
"use strict";
|
|
1190
|
-
init_hooks();
|
|
1191
1279
|
init_ai4();
|
|
1192
1280
|
reverseTrendLineManifest = {
|
|
1193
1281
|
name: "ReverseTrendLine",
|
|
1194
|
-
hooks: {
|
|
1195
|
-
beforePlaceOrder: reverseTrendLineBeforePlaceOrderHook
|
|
1196
|
-
},
|
|
1197
1282
|
aiAdapter: reverseTrendLineAiAdapter
|
|
1198
1283
|
};
|
|
1199
1284
|
}
|
|
1200
1285
|
});
|
|
1201
1286
|
|
|
1287
|
+
// src/TrendShift/adapters/ai.ts
|
|
1288
|
+
var import_strategies7, getTrendShiftContext, reasonText, trendShiftAiAdapter;
|
|
1289
|
+
var init_ai5 = __esm({
|
|
1290
|
+
"src/TrendShift/adapters/ai.ts"() {
|
|
1291
|
+
"use strict";
|
|
1292
|
+
import_strategies7 = require("@tradejs/core/strategies");
|
|
1293
|
+
getTrendShiftContext = (payload) => {
|
|
1294
|
+
const additional = payload.additionalIndicators;
|
|
1295
|
+
const raw = additional?.trendShiftContext ?? {};
|
|
1296
|
+
const hardBlockReasons = [];
|
|
1297
|
+
if (!raw.confirmedFlip) {
|
|
1298
|
+
hardBlockReasons.push("unconfirmed_flip");
|
|
1299
|
+
}
|
|
1300
|
+
if (!raw.flipDistanceOk) {
|
|
1301
|
+
hardBlockReasons.push("weak_flip_distance");
|
|
1302
|
+
}
|
|
1303
|
+
if (raw.coinBiasAligned === false) {
|
|
1304
|
+
hardBlockReasons.push("coin_bias_conflict");
|
|
1305
|
+
}
|
|
1306
|
+
const slopeAbs = Math.abs(raw.avgSlopePct ?? 0);
|
|
1307
|
+
const distanceAtrRatio = raw.distanceAtrRatio ?? 0;
|
|
1308
|
+
const closeVsAvgPctAbs = Math.abs(raw.closeVsAvgPct ?? 0);
|
|
1309
|
+
let deterministicQuality = 3;
|
|
1310
|
+
if (hardBlockReasons.length > 0) {
|
|
1311
|
+
deterministicQuality = raw.confirmedFlip ? 2 : 1;
|
|
1312
|
+
} else if (distanceAtrRatio >= 0.8 && slopeAbs >= 0.09 && closeVsAvgPctAbs >= 0.12) {
|
|
1313
|
+
deterministicQuality = 5;
|
|
1314
|
+
} else if (distanceAtrRatio >= 0.45 && slopeAbs >= 0.04 && closeVsAvgPctAbs >= 0.05) {
|
|
1315
|
+
deterministicQuality = 4;
|
|
1316
|
+
}
|
|
1317
|
+
return {
|
|
1318
|
+
...raw,
|
|
1319
|
+
deterministicQuality,
|
|
1320
|
+
approvalAllowedNow: deterministicQuality >= 4,
|
|
1321
|
+
hardBlockReasons
|
|
1322
|
+
};
|
|
1323
|
+
};
|
|
1324
|
+
reasonText = (reason) => {
|
|
1325
|
+
switch (reason) {
|
|
1326
|
+
case "unconfirmed_flip":
|
|
1327
|
+
return "the internal flip is not confirmed yet";
|
|
1328
|
+
case "weak_flip_distance":
|
|
1329
|
+
return "price moved away from the adaptive average too weakly";
|
|
1330
|
+
case "coin_bias_conflict":
|
|
1331
|
+
return "coin MA bias conflicts with the flip direction";
|
|
1332
|
+
default:
|
|
1333
|
+
return reason;
|
|
1334
|
+
}
|
|
1335
|
+
};
|
|
1336
|
+
trendShiftAiAdapter = {
|
|
1337
|
+
buildPayload: ({ signal, basePayload }) => ({
|
|
1338
|
+
...basePayload,
|
|
1339
|
+
additionalIndicators: {
|
|
1340
|
+
...basePayload.additionalIndicators,
|
|
1341
|
+
trendShiftContext: signal.additionalIndicators?.trendShiftContext
|
|
1342
|
+
}
|
|
1343
|
+
}),
|
|
1344
|
+
postProcessAnalysis: ({ payload, analysis }) => {
|
|
1345
|
+
const context = getTrendShiftContext(payload);
|
|
1346
|
+
const requestedDirection = analysis.direction === "LONG" || analysis.direction === "SHORT" ? analysis.direction : context.signalDirection;
|
|
1347
|
+
if (context.approvalAllowedNow === true && requestedDirection != null) {
|
|
1348
|
+
return {
|
|
1349
|
+
...analysis,
|
|
1350
|
+
direction: requestedDirection,
|
|
1351
|
+
quality: context.deterministicQuality,
|
|
1352
|
+
approved: true
|
|
1353
|
+
};
|
|
1354
|
+
}
|
|
1355
|
+
return {
|
|
1356
|
+
...analysis,
|
|
1357
|
+
direction: null,
|
|
1358
|
+
quality: context.deterministicQuality,
|
|
1359
|
+
approved: false,
|
|
1360
|
+
rejectReason: context.hardBlockReasons.length > 0 ? context.hardBlockReasons.map(reasonText).join("; ") : "the flip still does not look strong enough for live approval"
|
|
1361
|
+
};
|
|
1362
|
+
},
|
|
1363
|
+
buildHumanPromptAddon: ({ payload }) => {
|
|
1364
|
+
const context = getTrendShiftContext(payload);
|
|
1365
|
+
return `
|
|
1366
|
+
Additional TrendShift context:
|
|
1367
|
+
- signalDirection=${context.signalDirection ?? "n/a"}
|
|
1368
|
+
- confirmedFlip=${String(context.confirmedFlip)}
|
|
1369
|
+
- bullFlip=${String(context.bullFlip)}
|
|
1370
|
+
- bearFlip=${String(context.bearFlip)}
|
|
1371
|
+
- flipDistanceOk=${String(context.flipDistanceOk)}
|
|
1372
|
+
- closeVsAvgPct=${String(context.closeVsAvgPct ?? "n/a")}
|
|
1373
|
+
- bandWidthPct=${String(context.bandWidthPct ?? "n/a")}
|
|
1374
|
+
- avgSlopePct=${String(context.avgSlopePct ?? "n/a")}
|
|
1375
|
+
- distanceAtrRatio=${String(context.distanceAtrRatio ?? "n/a")}
|
|
1376
|
+
- coinBias=${context.coinBias ?? "n/a"}
|
|
1377
|
+
- coinBiasAligned=${String(context.coinBiasAligned)}
|
|
1378
|
+
- deterministicQuality=${context.deterministicQuality}
|
|
1379
|
+
- approvalAllowedNow=${context.approvalAllowedNow}
|
|
1380
|
+
- hardBlockReasons=${JSON.stringify(context.hardBlockReasons)}
|
|
1381
|
+
|
|
1382
|
+
Interpretation rules for TrendShift:
|
|
1383
|
+
- This is a trend-state flip strategy, not a forecast of future impulse.
|
|
1384
|
+
- If approvalAllowedNow=false, do not describe the signal as a fully confirmed live entry.
|
|
1385
|
+
- If hardBlockReasons is not empty, explain exactly what is still missing for confirmation.
|
|
1386
|
+
`.trim();
|
|
1387
|
+
},
|
|
1388
|
+
mapEntryRuntimeFromConfig: (config8) => (0, import_strategies7.mapAiRuntimeFromConfig)(
|
|
1389
|
+
config8
|
|
1390
|
+
)
|
|
1391
|
+
};
|
|
1392
|
+
}
|
|
1393
|
+
});
|
|
1394
|
+
|
|
1395
|
+
// src/TrendShift/manifest.ts
|
|
1396
|
+
var trendShiftManifest;
|
|
1397
|
+
var init_manifest5 = __esm({
|
|
1398
|
+
"src/TrendShift/manifest.ts"() {
|
|
1399
|
+
"use strict";
|
|
1400
|
+
init_ai5();
|
|
1401
|
+
trendShiftManifest = {
|
|
1402
|
+
name: "TrendShift",
|
|
1403
|
+
aiAdapter: trendShiftAiAdapter
|
|
1404
|
+
};
|
|
1405
|
+
}
|
|
1406
|
+
});
|
|
1407
|
+
|
|
1202
1408
|
// src/TrendLine/guardrails.ts
|
|
1203
1409
|
var TRENDLINE_CLEAR_BREAK_PCT, TRENDLINE_TIMING_WINDOW, WEAK_CLEAN_BREAK_ATR_RATIO_MAX, COMPRESSED_CLEAN_BREAK_ATR_RATIO_MAX, COMPRESSED_CLEAN_BREAK_DISTANCE_MAX, COMPRESSED_CLEAN_BREAK_TOUCHES_MIN, WEAK_LONG_FAR_BREAK_ATR_RATIO_MAX, WEAK_LONG_FAR_BREAK_DISTANCE_MIN, WEAK_LONG_FAR_BREAK_BTC_SPREAD_MAX, toFiniteNumberOrNull3, getLastFiniteNumber3, getFiniteTailNumbers, getBias3, getSpreadPct2, getTrendLineFromPayload2, getSortedTrendLinePoints2, buildTrendLineEvaluator2, getBreakoutSide, getClearBreakAtPct, getLineSlopeDirection, buildTrendlineStructuralContext, buildTrendlineTimingContext;
|
|
1204
1410
|
var init_guardrails2 = __esm({
|
|
@@ -1542,35 +1748,49 @@ var init_guardrails2 = __esm({
|
|
|
1542
1748
|
|
|
1543
1749
|
// src/TrendLine/adapters/ai.ts
|
|
1544
1750
|
var import_strategies8, TRENDLINE_CONTEXT_PROMPT, TRENDLINE_PAYLOAD_PROMPT, buildTrendlineContext, formatPromptNumber, getHardBlockReasonText3, mergeShortText, getDeterministicTrendlineQuality, getDeterministicTrendlineQualityReason, getTrendlineContextFromPayload, trendLineAiAdapter;
|
|
1545
|
-
var
|
|
1751
|
+
var init_ai6 = __esm({
|
|
1546
1752
|
"src/TrendLine/adapters/ai.ts"() {
|
|
1547
1753
|
"use strict";
|
|
1548
1754
|
import_strategies8 = require("@tradejs/core/strategies");
|
|
1549
1755
|
init_guardrails2();
|
|
1550
1756
|
TRENDLINE_CONTEXT_PROMPT = `
|
|
1551
|
-
|
|
1552
|
-
-
|
|
1553
|
-
-
|
|
1554
|
-
-
|
|
1555
|
-
-
|
|
1556
|
-
-
|
|
1557
|
-
-
|
|
1558
|
-
-
|
|
1559
|
-
-
|
|
1560
|
-
-
|
|
1561
|
-
-
|
|
1562
|
-
-
|
|
1563
|
-
-
|
|
1564
|
-
-
|
|
1565
|
-
-
|
|
1566
|
-
-
|
|
1757
|
+
TrendLine addon:
|
|
1758
|
+
- This setup is based on breakout or reaction around a trendline. 'payload.figures.trendline' contains the line geometry, and 'payload.additionalIndicators.trendlineContext' contains a compact summary of price location versus the line.
|
|
1759
|
+
- For TrendLine, geometry and price structure have higher priority than indicator confirmation.
|
|
1760
|
+
- Touches strengthen a line but do not confirm the signal by themselves. Without a confirmed breakout or retest, do not raise quality only because there were many touches.
|
|
1761
|
+
- For SHORT on rising support ('trendline.mode="lows"'), you usually need either a clear move below the line or a retest from below with rejection. If price remains above the line or directly on it, use 'direction=null' and usually 'quality <= 2'.
|
|
1762
|
+
- For LONG on descending resistance ('trendline.mode="highs"'), the mirror logic applies: you usually need a move above the line or a retest from above. If price remains below the line or directly on it, use 'direction=null' and usually 'quality <= 2'.
|
|
1763
|
+
- If 'payload.additionalIndicators.trendlineContext.nearLineNoise=true', do not treat that as a confirmed breakout. Quality is usually '<= 2-3', and a retest or confirmation is still needed.
|
|
1764
|
+
- If 'payload.additionalIndicators.trendlineContext.coinBiasAligned=false' or 'btcBiasAligned=false', treat it as a direct conflict with the signal direction. In that case, the signal is usually not confirmed unless the structural edge is exceptional.
|
|
1765
|
+
- If 'payload.additionalIndicators.derivativesContext' exists, use it as Coinalyze-based breakout confirmation or conflict: open interest should support the move, funding should not be extremely crowded against the entry quality, and liquidation spikes can indicate flush, squeeze, or exhaustion.
|
|
1766
|
+
- If 'derivativesContext.summary.riskFlags' contains 'oi_not_confirming', treat it as a direct sign that open interest does not confirm the breakout yet. Without very strong follow-through, do not elevate the signal to immediate entry.
|
|
1767
|
+
- For SHORT during 'off_hours' or session overlap, require cleaner structural follow-through than during normal hours; those windows are noisier and less suitable for immediate approval.
|
|
1768
|
+
- If 'payload.additionalIndicators.trendlineContext.clearBreak=false' and price is still near the line, do not describe it as a clean breakout.
|
|
1769
|
+
- If 'clearBreak=true' but 'trendlineContext.weakCleanBreak=true', treat it as a formally valid but too-weak breakout: structure has been touched, but displacement margin is still limited. This usually calls for follow-through or retest, not immediate confirmation.
|
|
1770
|
+
- If 'clearBreak=true' but 'trendlineContext.compressedCleanBreak=true', treat it as a compressed breakout after a cluster of close touches on a short line. Even with a formal line exit, this still usually calls for follow-through or retest rather than immediate confirmation.
|
|
1771
|
+
- If 'clearBreak=true', 'trendlineContext.breakVsAtrRatio < 0.5', and 'trendlineContext.weakBtcLedBreak=true', treat it as a weak BTC-led break without enough coin-specific follow-through. This usually calls for retest or extra confirmation rather than immediate confirmation.
|
|
1772
|
+
- For LONG on descending resistance, if the line is very long, the move above it is still modest, and BTC only weakly supports the break, treat it as an early breakout without sufficient follow-through. That usually needs retest or confirmation rather than immediate confirmation.
|
|
1773
|
+
- For TrendLine, quality 4-5 is only allowed when all of the following are true at once: 'clearBreak=true', 'nearLineNoise=false', 'coinBiasAligned=true', and 'btcBiasAligned=true'. If any one of these conditions is not met, do not set quality above 3.
|
|
1774
|
+
- Rare exception: if 'trendlineContext.aggressivePreBreakPressure=true', this is an aggressive pre-break-pressure setup. In that case 'quality=4' is allowed even without 'clearBreak', but only as early structural confirmation and only when coin/BTC bias is not conflicting.
|
|
1775
|
+
- Another rare exception: if 'trendlineContext.strongNearBreakPressure=true', this is a mature line with pressure already building in the signal direction and very strong aligned pressure from both the coin and BTC. In that case 'quality=4' is allowed even when 'nearLineNoise=true', but only as early structural confirmation on strong structure.
|
|
1567
1776
|
`;
|
|
1568
1777
|
TRENDLINE_PAYLOAD_PROMPT = `
|
|
1569
|
-
-
|
|
1570
|
-
-
|
|
1571
|
-
-
|
|
1778
|
+
- 'payload.figures.trendline' contains the full trendline geometry without trimming so touches and structure can be evaluated.
|
|
1779
|
+
- 'payload.additionalIndicators.trendlineContext' contains 'mode / touches / distance / currentLinePrice / priceVsLinePct / priceVsLineSide / clearBreak / nearLineNoise / coinMaBias / btcMaBias / maxAllowedQuality / approvalAllowedNow / hardBlockReasons'.
|
|
1780
|
+
- It also includes 'atrPct / breakVsAtrRatio / coinMaSpreadPct / btcMaSpreadPct / aggressivePreBreakPressure / strongNearBreakPressure / weakCleanBreak / compressedCleanBreak / weakBtcLedBreak / weakLongFarBreak'.
|
|
1781
|
+
- If 'payload.additionalIndicators.derivativesContext' exists, it contains Coinalyze-derived open interest, funding, and liquidation fields for the signal moment; do not treat 'stale' or 'missing_derivatives' as confirmation or conflict.
|
|
1572
1782
|
`;
|
|
1573
1783
|
buildTrendlineContext = (signal) => {
|
|
1784
|
+
const marketContext = signal.additionalIndicators && typeof signal.additionalIndicators.marketContext === "object" && signal.additionalIndicators.marketContext && !Array.isArray(signal.additionalIndicators.marketContext) ? signal.additionalIndicators.marketContext : null;
|
|
1785
|
+
const tradingSession = marketContext && typeof marketContext.tradingSession === "object" && marketContext.tradingSession && !Array.isArray(marketContext.tradingSession) ? marketContext.tradingSession : null;
|
|
1786
|
+
const sessionPrimary = typeof tradingSession?.primarySession === "string" ? tradingSession.primarySession : null;
|
|
1787
|
+
const sessionIsOverlap = tradingSession?.isOverlap === true;
|
|
1788
|
+
const derivativesContext = signal.additionalIndicators && typeof signal.additionalIndicators.derivativesContext === "object" && signal.additionalIndicators.derivativesContext && !Array.isArray(signal.additionalIndicators.derivativesContext) ? signal.additionalIndicators.derivativesContext : null;
|
|
1789
|
+
const derivativesSummary = derivativesContext && typeof derivativesContext.summary === "object" && derivativesContext.summary && !Array.isArray(derivativesContext.summary) ? derivativesContext.summary : null;
|
|
1790
|
+
const derivativesRiskFlags = Array.isArray(derivativesSummary?.riskFlags) ? derivativesSummary.riskFlags.filter(
|
|
1791
|
+
(flag) => typeof flag === "string" && flag.length > 0
|
|
1792
|
+
) : [];
|
|
1793
|
+
const oiNotConfirming = derivativesRiskFlags.includes("oi_not_confirming");
|
|
1574
1794
|
const structural = buildTrendlineStructuralContext(signal);
|
|
1575
1795
|
const trendLine = getTrendLineFromPayload2(signal);
|
|
1576
1796
|
const coinMaFast = getLastFiniteNumber3(signal.indicators?.maFast);
|
|
@@ -1592,6 +1812,12 @@ var init_ai5 = __esm({
|
|
|
1592
1812
|
if (weakBtcLedBreak) {
|
|
1593
1813
|
hardBlockReasons.push("weak_btc_led_break");
|
|
1594
1814
|
}
|
|
1815
|
+
if (oiNotConfirming && !hardBlockReasons.includes("oi_not_confirming")) {
|
|
1816
|
+
hardBlockReasons.push("oi_not_confirming");
|
|
1817
|
+
}
|
|
1818
|
+
if (structural.signalDirection === "SHORT" && (entryTiming === "ready_follow_through" || entryTiming === "ready_retest") && (sessionPrimary === "off_hours" || sessionIsOverlap) && !hardBlockReasons.includes("short_session_risk")) {
|
|
1819
|
+
hardBlockReasons.push("short_session_risk");
|
|
1820
|
+
}
|
|
1595
1821
|
const deterministicQuality = getDeterministicTrendlineQuality({
|
|
1596
1822
|
signalDirection: structural.signalDirection,
|
|
1597
1823
|
clearBreak: structural.clearBreak,
|
|
@@ -1620,6 +1846,10 @@ var init_ai5 = __esm({
|
|
|
1620
1846
|
aggressivePreBreakPressure,
|
|
1621
1847
|
strongNearBreakPressure,
|
|
1622
1848
|
weakBtcLedBreak,
|
|
1849
|
+
sessionPrimary,
|
|
1850
|
+
sessionIsOverlap,
|
|
1851
|
+
derivativesRiskFlags,
|
|
1852
|
+
oiNotConfirming,
|
|
1623
1853
|
deterministicQuality,
|
|
1624
1854
|
maxAllowedQuality,
|
|
1625
1855
|
approvalAllowedNow,
|
|
@@ -1635,21 +1865,25 @@ var init_ai5 = __esm({
|
|
|
1635
1865
|
getHardBlockReasonText3 = (reason) => {
|
|
1636
1866
|
switch (reason) {
|
|
1637
1867
|
case "no_clear_break":
|
|
1638
|
-
return "
|
|
1868
|
+
return "there is no clean breakout of the line";
|
|
1639
1869
|
case "near_line_noise":
|
|
1640
|
-
return "
|
|
1870
|
+
return "price is too close to the line and looks like noise";
|
|
1641
1871
|
case "coin_bias_conflict":
|
|
1642
|
-
return "bias
|
|
1872
|
+
return "coin bias conflicts with the direction";
|
|
1643
1873
|
case "btc_bias_conflict":
|
|
1644
|
-
return "BTC
|
|
1874
|
+
return "BTC context conflicts with the direction";
|
|
1645
1875
|
case "weak_clean_break":
|
|
1646
|
-
return "
|
|
1876
|
+
return "the formal breakout exists, but displacement is still too weak relative to ATR";
|
|
1647
1877
|
case "compressed_clean_break":
|
|
1648
|
-
return "
|
|
1878
|
+
return "the breakout looks too compressed: clustered close touches on a short line without enough follow-through";
|
|
1649
1879
|
case "weak_btc_led_break":
|
|
1650
|
-
return "
|
|
1880
|
+
return "the breakout is too small relative to ATR and looks more like a BTC-led move without enough coin follow-through";
|
|
1651
1881
|
case "weak_long_far_break":
|
|
1652
|
-
return "
|
|
1882
|
+
return "for LONG, the breakout of the very long line is still too modest and BTC support is too weak";
|
|
1883
|
+
case "oi_not_confirming":
|
|
1884
|
+
return "open interest does not confirm the move, so the breakout still looks unconfirmed on derivatives context";
|
|
1885
|
+
case "short_session_risk":
|
|
1886
|
+
return "for SHORT, the current session is too noisy or thin (off-hours or overlap), so clearer follow-through is required";
|
|
1653
1887
|
default:
|
|
1654
1888
|
return reason;
|
|
1655
1889
|
}
|
|
@@ -1684,7 +1918,8 @@ var init_ai5 = __esm({
|
|
|
1684
1918
|
const shortLineStrengthQuality4 = breakVsAtrRatio >= 0.6 && priceVsLinePctAbs >= 0.65 && touches >= 6 && distance < 120 && btcMaSpreadPct >= 0.75;
|
|
1685
1919
|
const matureLineQuality4 = breakVsAtrRatio >= 0.8 && priceVsLinePctAbs >= 0.7 && touches >= 5 && distance < 350 && btcMaSpreadPct >= 0.4;
|
|
1686
1920
|
const extendedHighConvictionQuality4 = breakVsAtrRatio >= 0.75 && priceVsLinePctAbs >= 0.65 && touches >= 5 && distance < 600 && btcMaSpreadPct >= 0.9;
|
|
1687
|
-
|
|
1921
|
+
const alignedRecentFollowThroughQuality4 = (entryTiming === "ready_follow_through" || entryTiming === "ready_retest") && breakVsAtrRatio >= 0.58 && breakVsAtrRatio <= 0.72 && priceVsLinePctAbs >= 0.48 && priceVsLinePctAbs <= 0.7 && touches >= 5 && distance <= 420 && btcMaSpreadPct >= 0.45 && coinMaSpreadPct >= 0.25;
|
|
1922
|
+
return compactBreakoutQuality4 || shortLineStrengthQuality4 || matureLineQuality4 || extendedHighConvictionQuality4 || alignedRecentFollowThroughQuality4 ? 4 : 3;
|
|
1688
1923
|
}
|
|
1689
1924
|
const quality5 = breakVsAtrRatio >= 5 && priceVsLinePctAbs >= 10 && touches >= 5 && distance >= 240 && distance <= 400 && btcMaSpreadPct <= -1;
|
|
1690
1925
|
if (quality5) {
|
|
@@ -1692,19 +1927,23 @@ var init_ai5 = __esm({
|
|
|
1692
1927
|
}
|
|
1693
1928
|
const quality4 = breakVsAtrRatio >= 1 && breakVsAtrRatio < 2.5 && priceVsLinePctAbs >= 1 && touches >= 5 && distance < 300 && btcMaSpreadPct <= -0.5;
|
|
1694
1929
|
const strongReadyBreakoutQuality4 = entryTiming === "ready_breakout" && breakVsAtrRatio >= 2 && priceVsLinePctAbs >= 1.8 && touches >= 5 && btcMaSpreadPct <= -1 && (coinMaSpreadPct <= -1 || breakVsAtrRatio >= 3);
|
|
1695
|
-
|
|
1930
|
+
const moderateReadyBreakoutQuality4 = entryTiming === "ready_breakout" && breakVsAtrRatio >= 0.65 && breakVsAtrRatio <= 1.2 && priceVsLinePctAbs >= 0.65 && priceVsLinePctAbs <= 1 && touches >= 5 && distance >= 150 && distance <= 320 && btcMaSpreadPct <= -0.05 && coinMaSpreadPct <= -0.25;
|
|
1931
|
+
if ((quality4 || moderateReadyBreakoutQuality4) && entryTiming === "ready_breakout") {
|
|
1932
|
+
return 4;
|
|
1933
|
+
}
|
|
1934
|
+
return strongReadyBreakoutQuality4 ? 4 : 3;
|
|
1696
1935
|
};
|
|
1697
1936
|
getDeterministicTrendlineQualityReason = (trendlineContext) => {
|
|
1698
1937
|
if (trendlineContext.hardBlockReasons.length > 0) {
|
|
1699
|
-
return `TrendLine guardrail:
|
|
1938
|
+
return `TrendLine guardrail: entry is blocked because ${trendlineContext.hardBlockReasons.map(getHardBlockReasonText3).join("; ")}.`;
|
|
1700
1939
|
}
|
|
1701
1940
|
if (trendlineContext.signalDirection === "LONG") {
|
|
1702
|
-
return "TrendLine deterministic quality:
|
|
1941
|
+
return "TrendLine deterministic quality: the breakout exists, but LONG still lacks enough displacement, BTC support, or a compact enough line for immediate entry.";
|
|
1703
1942
|
}
|
|
1704
1943
|
if (trendlineContext.signalDirection === "SHORT") {
|
|
1705
|
-
return "TrendLine deterministic quality:
|
|
1944
|
+
return "TrendLine deterministic quality: the breakout exists, but SHORT still lacks enough bearish displacement or follow-through, so entry is still too early.";
|
|
1706
1945
|
}
|
|
1707
|
-
return "TrendLine deterministic quality:
|
|
1946
|
+
return "TrendLine deterministic quality: the structure is still not strong enough for entry right now.";
|
|
1708
1947
|
};
|
|
1709
1948
|
getTrendlineContextFromPayload = (payload, signal) => {
|
|
1710
1949
|
const additional = payload.additionalIndicators;
|
|
@@ -1713,25 +1952,35 @@ var init_ai5 = __esm({
|
|
|
1713
1952
|
};
|
|
1714
1953
|
trendLineAiAdapter = {
|
|
1715
1954
|
// Shared builder trims nested series/figures; TrendLine keeps trendline geometry untrimmed on purpose.
|
|
1716
|
-
buildPayload: ({ signal, basePayload }) =>
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
...basePayload.
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1955
|
+
buildPayload: ({ signal, basePayload }) => {
|
|
1956
|
+
const mergedAdditionalIndicators = {
|
|
1957
|
+
...signal.additionalIndicators ?? {},
|
|
1958
|
+
...basePayload.additionalIndicators ?? {}
|
|
1959
|
+
};
|
|
1960
|
+
const trendlineContext = buildTrendlineContext({
|
|
1961
|
+
...signal,
|
|
1962
|
+
additionalIndicators: mergedAdditionalIndicators
|
|
1963
|
+
});
|
|
1964
|
+
return {
|
|
1965
|
+
...basePayload,
|
|
1966
|
+
figures: {
|
|
1967
|
+
...basePayload.figures,
|
|
1968
|
+
// Keep raw line geometry available exactly where the shared prompt expects it.
|
|
1969
|
+
trendline: getTrendLineFromPayload2(signal)
|
|
1970
|
+
},
|
|
1971
|
+
additionalIndicators: {
|
|
1972
|
+
...mergedAdditionalIndicators,
|
|
1973
|
+
trendlineContext
|
|
1974
|
+
}
|
|
1975
|
+
};
|
|
1976
|
+
},
|
|
1728
1977
|
postProcessAnalysis: ({ signal, payload, analysis }) => {
|
|
1729
1978
|
const trendlineContext = getTrendlineContextFromPayload(payload, signal);
|
|
1730
1979
|
const quality = trendlineContext.deterministicQuality;
|
|
1731
1980
|
const signalDirection = signal.direction === "LONG" || signal.direction === "SHORT" ? signal.direction : null;
|
|
1732
1981
|
if ((trendlineContext.aggressivePreBreakPressure === true || trendlineContext.strongNearBreakPressure === true) && signalDirection != null) {
|
|
1733
|
-
const fallbackReason = trendlineContext.strongNearBreakPressure === true ? "TrendLine strong near-break pressure:
|
|
1734
|
-
const fallbackComment = trendlineContext.strongNearBreakPressure === true ? "TrendLine strong near-break pressure:
|
|
1982
|
+
const fallbackReason = trendlineContext.strongNearBreakPressure === true ? "TrendLine strong near-break pressure: a mature line is already compressing in the trade direction, so early entry is allowed by strategy code." : "TrendLine aggressive pre-break pressure: early structural confirmation is allowed under strong bearish pressure.";
|
|
1983
|
+
const fallbackComment = trendlineContext.strongNearBreakPressure === true ? "TrendLine strong near-break pressure: early entry is allowed by strategy code." : "TrendLine aggressive pre-break pressure: early entry is allowed by strategy code.";
|
|
1735
1984
|
return {
|
|
1736
1985
|
...analysis,
|
|
1737
1986
|
direction: signalDirection,
|
|
@@ -1762,17 +2011,17 @@ var init_ai5 = __esm({
|
|
|
1762
2011
|
const retestPrice = trendlineContext.currentLinePrice ?? analysis.retestPrice ?? null;
|
|
1763
2012
|
const qualityReason = mergeShortText(
|
|
1764
2013
|
getDeterministicTrendlineQualityReason(trendlineContext),
|
|
1765
|
-
"TrendLine guardrail:
|
|
2014
|
+
"TrendLine guardrail: entry is blocked until the structure is confirmed.",
|
|
1766
2015
|
400
|
|
1767
2016
|
);
|
|
1768
2017
|
const triggerInvalidation = mergeShortText(
|
|
1769
|
-
trendlineContext.hardBlockReasons.length > 0 ?
|
|
1770
|
-
"
|
|
2018
|
+
trendlineContext.hardBlockReasons.length > 0 ? `Wait for a clean breakout or retest of the line and resolve the conflicts: ${trendlineContext.hardBlockReasons.map(getHardBlockReasonText3).join("; ")}.` : "Wait for stronger breakout follow-through or a line retest confirmed by both the coin and BTC.",
|
|
2019
|
+
"Wait for a clean line breakout or retest plus confirmation from the coin and BTC.",
|
|
1771
2020
|
400
|
|
1772
2021
|
);
|
|
1773
2022
|
const comment = mergeShortText(
|
|
1774
|
-
trendlineContext.hardBlockReasons.length > 0 ? `TrendLine guardrail
|
|
1775
|
-
"TrendLine guardrail
|
|
2023
|
+
trendlineContext.hardBlockReasons.length > 0 ? `TrendLine guardrail blocked the entry: ${trendlineContext.hardBlockReasons.map(getHardBlockReasonText3).join("; ")}.` : "TrendLine deterministic quality downgraded the entry to watch or reject until stronger structure appears.",
|
|
2024
|
+
"TrendLine guardrail blocked the entry until the structure is confirmed.",
|
|
1776
2025
|
1024
|
|
1777
2026
|
);
|
|
1778
2027
|
return {
|
|
@@ -1785,12 +2034,12 @@ var init_ai5 = __esm({
|
|
|
1785
2034
|
stopLossPrice: null,
|
|
1786
2035
|
setup: mergeShortText(
|
|
1787
2036
|
analysis.setup ?? "",
|
|
1788
|
-
"
|
|
2037
|
+
"There is no confirmed trendline breakout or retest for entry right now.",
|
|
1789
2038
|
400
|
|
1790
2039
|
),
|
|
1791
2040
|
retestPlan: mergeShortText(
|
|
1792
2041
|
analysis.retestPlan ?? "",
|
|
1793
|
-
"
|
|
2042
|
+
"Wait for a return to the line and a reaction in the trade direction before a new entry.",
|
|
1794
2043
|
400
|
|
1795
2044
|
),
|
|
1796
2045
|
qualityReason,
|
|
@@ -1810,7 +2059,7 @@ ${TRENDLINE_PAYLOAD_PROMPT}
|
|
|
1810
2059
|
}
|
|
1811
2060
|
return `
|
|
1812
2061
|
|
|
1813
|
-
|
|
2062
|
+
Additional TrendLine context:
|
|
1814
2063
|
- trendline.mode=${trendlineContext.mode ?? "n/a"}
|
|
1815
2064
|
- trendline.touches=${formatPromptNumber(trendlineContext.touches, 0)}
|
|
1816
2065
|
- trendline.distance=${formatPromptNumber(trendlineContext.distance, 0)}
|
|
@@ -1844,22 +2093,22 @@ ${TRENDLINE_PAYLOAD_PROMPT}
|
|
|
1844
2093
|
- btc.maSpreadPct=${formatPromptNumber(trendlineContext.btcMaSpreadPct, 3)}%
|
|
1845
2094
|
- btc.biasAligned=${String(trendlineContext.btcBiasAligned)}
|
|
1846
2095
|
|
|
1847
|
-
|
|
1848
|
-
- SHORT
|
|
1849
|
-
- LONG
|
|
1850
|
-
-
|
|
1851
|
-
-
|
|
1852
|
-
-
|
|
1853
|
-
-
|
|
1854
|
-
-
|
|
1855
|
-
-
|
|
1856
|
-
-
|
|
1857
|
-
-
|
|
1858
|
-
-
|
|
2096
|
+
Interpretation rules for TrendLine:
|
|
2097
|
+
- SHORT from a 'lows' line is confirmed only by a clear move below the line or a retest from below with rejection.
|
|
2098
|
+
- LONG from a 'highs' line is confirmed only by a clear move above the line or a retest from above with rejection.
|
|
2099
|
+
- If 'trendline.nearLineNoise=true' or any bias alignment is false, it is better to return 'direction=null' and quality 1-3 than to describe the signal as confirmed without margin.
|
|
2100
|
+
- If 'trendline.weakCleanBreak=true', the formal breakout exists but is too weak in displacement; it needs follow-through or retest rather than quality 4-5.
|
|
2101
|
+
- If 'trendline.compressedCleanBreak=true', the breakout exists formally, but the line is too short and compressed after clustered touches; this usually needs follow-through or retest rather than immediate confirmation.
|
|
2102
|
+
- If 'trendline.weakBtcLedBreak=true', treat it as a small breakout driven more by BTC than by the coin itself; this usually calls for retest and quality 1-3.
|
|
2103
|
+
- If 'clearBreak=false' or any alignment is false, do not raise quality above 3.
|
|
2104
|
+
- If 'trendline.aggressivePreBreakPressure=true', an early SHORT before a clear breakout may be considered only as an exception: quality can be at most 4, and the explanation must clearly state that the structural confirmation is still aggressive.
|
|
2105
|
+
- If 'trendline.strongNearBreakPressure=true', an early SHORT may be considered when pressure is already strong on the correct side of the line even if the breakout still falls short of the 'clearBreak' threshold: quality can be at most 4.
|
|
2106
|
+
- The strategy deterministically normalizes final quality to 'trendline.deterministicQuality'; your job is to explain the decision within that frame, not to argue with the tier.
|
|
2107
|
+
- If 'trendline.approvalAllowedNow=false', do not describe the signal as fully confirmed right now; explain what is still missing for confirmation.
|
|
1859
2108
|
`;
|
|
1860
2109
|
},
|
|
1861
|
-
mapEntryRuntimeFromConfig: (
|
|
1862
|
-
|
|
2110
|
+
mapEntryRuntimeFromConfig: (config8) => (0, import_strategies8.mapAiRuntimeFromConfig)(
|
|
2111
|
+
config8
|
|
1863
2112
|
)
|
|
1864
2113
|
};
|
|
1865
2114
|
}
|
|
@@ -1898,40 +2147,24 @@ var init_ml4 = __esm({
|
|
|
1898
2147
|
normalizeStrategyConfig: (strategyConfig) => {
|
|
1899
2148
|
return toTrendLineMlStrategyConfig(strategyConfig);
|
|
1900
2149
|
},
|
|
1901
|
-
mapEntryRuntimeFromConfig: (
|
|
2150
|
+
mapEntryRuntimeFromConfig: (config8) => (0, import_strategies9.mapMlRuntimeFromConfig)(config8, {
|
|
1902
2151
|
strategyConfig: toTrendLineMlStrategyConfig(
|
|
1903
|
-
|
|
2152
|
+
config8
|
|
1904
2153
|
)
|
|
1905
2154
|
})
|
|
1906
2155
|
};
|
|
1907
2156
|
}
|
|
1908
2157
|
});
|
|
1909
2158
|
|
|
1910
|
-
// src/TrendLine/hooks.ts
|
|
1911
|
-
var import_strategies10, trendLineBeforePlaceOrderHook;
|
|
1912
|
-
var init_hooks2 = __esm({
|
|
1913
|
-
"src/TrendLine/hooks.ts"() {
|
|
1914
|
-
"use strict";
|
|
1915
|
-
import_strategies10 = require("@tradejs/node/strategies");
|
|
1916
|
-
trendLineBeforePlaceOrderHook = (0, import_strategies10.createCloseOppositeBeforePlaceOrderHook)({
|
|
1917
|
-
isEnabled: (config7) => Boolean(config7.CLOSE_OPPOSITE_POSITIONS)
|
|
1918
|
-
});
|
|
1919
|
-
}
|
|
1920
|
-
});
|
|
1921
|
-
|
|
1922
2159
|
// src/TrendLine/manifest.ts
|
|
1923
2160
|
var trendLineManifest;
|
|
1924
|
-
var
|
|
2161
|
+
var init_manifest6 = __esm({
|
|
1925
2162
|
"src/TrendLine/manifest.ts"() {
|
|
1926
2163
|
"use strict";
|
|
1927
|
-
|
|
2164
|
+
init_ai6();
|
|
1928
2165
|
init_ml4();
|
|
1929
|
-
init_hooks2();
|
|
1930
2166
|
trendLineManifest = {
|
|
1931
2167
|
name: "TrendLine",
|
|
1932
|
-
hooks: {
|
|
1933
|
-
beforePlaceOrder: trendLineBeforePlaceOrderHook
|
|
1934
|
-
},
|
|
1935
2168
|
aiAdapter: trendLineAiAdapter,
|
|
1936
2169
|
mlAdapter: trendLineMlAdapter
|
|
1937
2170
|
};
|
|
@@ -2108,33 +2341,35 @@ var init_setup = __esm({
|
|
|
2108
2341
|
});
|
|
2109
2342
|
|
|
2110
2343
|
// src/VolumeDivergence/adapters/ai.ts
|
|
2111
|
-
var
|
|
2112
|
-
var
|
|
2344
|
+
var import_strategies10, VOLUME_DIVERGENCE_CONTEXT_PROMPT, VOLUME_DIVERGENCE_PAYLOAD_PROMPT, toFiniteNumberOrNull4, getLastFiniteNumber4, getBias4, getSignalDirection2, getDivergenceSummary, getVolumeDivergenceSetupSummary, getVolumeDivergenceThresholdSummary, isAtLeast2, isAtMost, isInRange2, getConfirmationDistancePct, buildHardBlockReasons, getLongQ4Demotion, getLongDeterministicQuality, getShortDeterministicQuality, getVolumeDivergenceContext, getVolumeDivergenceContextFromPayload, clampQuality2, getHardBlockReasonText4, buildGuardrailReason, postProcessAnalysis2, volumeDivergenceAiAdapter;
|
|
2345
|
+
var init_ai7 = __esm({
|
|
2113
2346
|
"src/VolumeDivergence/adapters/ai.ts"() {
|
|
2114
2347
|
"use strict";
|
|
2115
|
-
|
|
2348
|
+
import_strategies10 = require("@tradejs/core/strategies");
|
|
2116
2349
|
init_setup();
|
|
2117
2350
|
VOLUME_DIVERGENCE_CONTEXT_PROMPT = `
|
|
2118
|
-
|
|
2119
|
-
-
|
|
2120
|
-
- Bullish divergence
|
|
2121
|
-
- Bearish divergence
|
|
2122
|
-
-
|
|
2123
|
-
-
|
|
2124
|
-
-
|
|
2125
|
-
-
|
|
2126
|
-
-
|
|
2127
|
-
-
|
|
2128
|
-
-
|
|
2129
|
-
-
|
|
2130
|
-
-
|
|
2131
|
-
- confirmationDistancePct
|
|
2132
|
-
- additionalIndicators.deltaAtPivot
|
|
2351
|
+
VolumeDivergence addon:
|
|
2352
|
+
- This is a reversal setup built on price and normalized-volume divergence, not a breakout strategy.
|
|
2353
|
+
- Bullish divergence means price makes a lower low while volume makes a higher low.
|
|
2354
|
+
- Bearish divergence means price makes a higher high while volume makes a lower high.
|
|
2355
|
+
- For a bullish signal, do not overstate quality if price still failed to bounce meaningfully away from the current pivot low or failed to reclaim enough structure.
|
|
2356
|
+
- For a bearish signal, mirror that logic: do not overstate quality if price failed to move down meaningfully away from the current pivot high.
|
|
2357
|
+
- If \`payload.additionalIndicators.volumeDivergenceContext.confirmationReady=false\`, this is usually not a fully confirmed reversal yet; quality is often \`<= 4\` and a retest or confirmation is often still needed.
|
|
2358
|
+
- For live approval, treat \`confirmationReady\` as much more important than \`structureAdvanced\`; structure advance alone does not mean the reversal is entry-ready.
|
|
2359
|
+
- For a reversal setup, do not automatically reward quality just because the coin or BTC MA bias already matches the signal direction.
|
|
2360
|
+
- For LONG with \`entryTiming=structure_advance\`, avoid \`quality=5\`; that is an intermediate phase, not a fully confirmed reversal.
|
|
2361
|
+
- Be stricter for SHORT than for LONG: a bearish reversal should require cleaner follow-through, and bias or delta conflict should reduce quality more aggressively.
|
|
2362
|
+
- If \`deltaAtPivot\` conflicts with the reversal direction or the coin/BTC bias conflicts with the signal, do not overstate quality just because divergence exists.
|
|
2363
|
+
- Use \`divergenceAmplitudeAtrRatio\`, \`reclaimPct\`, and \`confirmationCandleQuality\` as explicit setup features describing how meaningful the divergence is relative to ATR, how much structure price reclaimed, and how strong the confirmation candle was.
|
|
2364
|
+
- \`confirmationDistancePct\` tells you how far price moved beyond the confirmation level; do not overstate quality when confirmation exists only marginally.
|
|
2365
|
+
- \`additionalIndicators.deltaAtPivot\` is a proxy net-volume value on the pivot candle, not true lower-timeframe TradingView volume delta.
|
|
2366
|
+
- If \`payload.additionalIndicators.derivativesContext\` exists, use Coinalyze-derived open interest, funding, and liquidations as positioning context: a liquidation flush can strengthen reversal odds, while crowded positioning against the trade or stale or missing data should not mechanically increase quality.
|
|
2133
2367
|
`;
|
|
2134
2368
|
VOLUME_DIVERGENCE_PAYLOAD_PROMPT = `
|
|
2135
|
-
-
|
|
2369
|
+
- \`payload.additionalIndicators.volumeDivergenceContext\` contains a compact divergence-strength summary:
|
|
2136
2370
|
divergenceKind / confirmationPrice / confirmationReady / structureAdvanced / reboundFromPivotPct / confirmationDistancePct / priceDisplacementPct / divergenceAmplitudeAtrRatio / reclaimPct / confirmationCandleQuality / volumeDivergenceStrength / deltaAligned / coinBiasAligned / btcBiasAligned / deterministicQuality / approvalAllowedNow / structuralHardBlockReasons / maxAllowedQuality.
|
|
2137
|
-
-
|
|
2371
|
+
- Use this context as the explicit strategy-specific summary instead of trying to derive the same conclusion again only from generic candles.
|
|
2372
|
+
- If \`payload.additionalIndicators.derivativesContext\` exists, it is a Coinalyze-derived summary of derivatives state at signal time; \`stale\` or \`missing_derivatives\` means that Coinalyze context must not be used.
|
|
2138
2373
|
`;
|
|
2139
2374
|
toFiniteNumberOrNull4 = (value) => {
|
|
2140
2375
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
@@ -2571,13 +2806,13 @@ var init_ai6 = __esm({
|
|
|
2571
2806
|
getHardBlockReasonText4 = (reason) => {
|
|
2572
2807
|
switch (reason) {
|
|
2573
2808
|
case "no_rebound_from_pivot":
|
|
2574
|
-
return "
|
|
2809
|
+
return "price failed to move away from the pivot in the reversal direction";
|
|
2575
2810
|
case "weak_divergence_amplitude":
|
|
2576
|
-
return "
|
|
2811
|
+
return "divergence amplitude is too small relative to ATR";
|
|
2577
2812
|
case "weak_reclaim":
|
|
2578
|
-
return "
|
|
2813
|
+
return "price reclaimed too little structure after the pivot";
|
|
2579
2814
|
case "weak_confirmation_candle":
|
|
2580
|
-
return "confirmation candle
|
|
2815
|
+
return "confirmation candle is too weak";
|
|
2581
2816
|
default:
|
|
2582
2817
|
return reason;
|
|
2583
2818
|
}
|
|
@@ -2587,9 +2822,9 @@ var init_ai6 = __esm({
|
|
|
2587
2822
|
return `VolumeDivergence guardrail: ${context.hardBlockReasons.map(getHardBlockReasonText4).join("; ")}.`;
|
|
2588
2823
|
}
|
|
2589
2824
|
if (!context.confirmationReady && context.entryTiming == null) {
|
|
2590
|
-
return "VolumeDivergence guardrail: reversal
|
|
2825
|
+
return "VolumeDivergence guardrail: reversal is visible, but the confirmation level has not been cleared yet.";
|
|
2591
2826
|
}
|
|
2592
|
-
return "VolumeDivergence guardrail: quality
|
|
2827
|
+
return "VolumeDivergence guardrail: quality is limited by confirmation state and the strength of reversal away from the pivot.";
|
|
2593
2828
|
};
|
|
2594
2829
|
postProcessAnalysis2 = ({
|
|
2595
2830
|
signal,
|
|
@@ -2617,8 +2852,8 @@ var init_ai6 = __esm({
|
|
|
2617
2852
|
takeProfitPrice: null,
|
|
2618
2853
|
stopLossPrice: null,
|
|
2619
2854
|
qualityReason: analysis.qualityReason || buildGuardrailReason(context),
|
|
2620
|
-
triggerInvalidation: analysis.triggerInvalidation || (context.confirmationPrice != null ?
|
|
2621
|
-
comment: analysis.comment || (context.hardBlockReasons.length > 0 ? `VolumeDivergence
|
|
2855
|
+
triggerInvalidation: analysis.triggerInvalidation || (context.confirmationPrice != null ? `Wait for reversal confirmation relative to level ${context.confirmationPrice}.` : "Wait for a confirmed reversal after the pivot."),
|
|
2856
|
+
comment: analysis.comment || (context.hardBlockReasons.length > 0 ? `VolumeDivergence rejected: ${context.hardBlockReasons.map(getHardBlockReasonText4).join("; ")}.` : "VolumeDivergence remains in watch mode until the reversal is confirmed.")
|
|
2622
2857
|
};
|
|
2623
2858
|
}
|
|
2624
2859
|
return {
|
|
@@ -2646,7 +2881,7 @@ ${VOLUME_DIVERGENCE_PAYLOAD_PROMPT}`,
|
|
|
2646
2881
|
const context = getVolumeDivergenceContextFromPayload(payload, signal);
|
|
2647
2882
|
return `
|
|
2648
2883
|
|
|
2649
|
-
|
|
2884
|
+
Additional VolumeDivergence context:
|
|
2650
2885
|
- divergenceKind=${context.divergenceKind ?? "n/a"}
|
|
2651
2886
|
- confirmationPrice=${context.confirmationPrice ?? "n/a"}
|
|
2652
2887
|
- confirmationReady=${context.confirmationReady}
|
|
@@ -2673,26 +2908,26 @@ ${VOLUME_DIVERGENCE_PAYLOAD_PROMPT}`,
|
|
|
2673
2908
|
- maxAllowedQuality=${context.maxAllowedQuality}
|
|
2674
2909
|
- hardBlockReasons=${context.hardBlockReasons.join(", ") || "none"}
|
|
2675
2910
|
|
|
2676
|
-
|
|
2677
|
-
-
|
|
2678
|
-
- confirmationReady=false
|
|
2679
|
-
-
|
|
2680
|
-
-
|
|
2911
|
+
Interpretation rules for VolumeDivergence:
|
|
2912
|
+
- first evaluate whether there is a real reversal away from the pivot, not just divergence on paper;
|
|
2913
|
+
- \`confirmationReady=false\` usually means the reversal is not fully confirmed yet;
|
|
2914
|
+
- if price did not bounce away from the current pivot in the signal direction, do not treat the setup as confirmed;
|
|
2915
|
+
- delta or bias conflict should reduce quality, not be ignored.
|
|
2681
2916
|
`;
|
|
2682
2917
|
},
|
|
2683
|
-
mapEntryRuntimeFromConfig: (
|
|
2684
|
-
|
|
2918
|
+
mapEntryRuntimeFromConfig: (config8) => (0, import_strategies10.mapAiRuntimeFromConfig)(
|
|
2919
|
+
config8
|
|
2685
2920
|
)
|
|
2686
2921
|
};
|
|
2687
2922
|
}
|
|
2688
2923
|
});
|
|
2689
2924
|
|
|
2690
2925
|
// src/VolumeDivergence/adapters/ml.ts
|
|
2691
|
-
var
|
|
2926
|
+
var import_strategies11, toVolumeDivergenceMlStrategyConfig, volumeDivergenceMlAdapter;
|
|
2692
2927
|
var init_ml5 = __esm({
|
|
2693
2928
|
"src/VolumeDivergence/adapters/ml.ts"() {
|
|
2694
2929
|
"use strict";
|
|
2695
|
-
|
|
2930
|
+
import_strategies11 = require("@tradejs/core/strategies");
|
|
2696
2931
|
toVolumeDivergenceMlStrategyConfig = (input) => {
|
|
2697
2932
|
if (!input) return void 0;
|
|
2698
2933
|
return {
|
|
@@ -2712,50 +2947,34 @@ var init_ml5 = __esm({
|
|
|
2712
2947
|
normalizeStrategyConfig: (strategyConfig) => {
|
|
2713
2948
|
return toVolumeDivergenceMlStrategyConfig(strategyConfig);
|
|
2714
2949
|
},
|
|
2715
|
-
mapEntryRuntimeFromConfig: (
|
|
2950
|
+
mapEntryRuntimeFromConfig: (config8) => (0, import_strategies11.mapMlRuntimeFromConfig)(config8, {
|
|
2716
2951
|
strategyConfig: toVolumeDivergenceMlStrategyConfig(
|
|
2717
|
-
|
|
2952
|
+
config8
|
|
2718
2953
|
)
|
|
2719
2954
|
})
|
|
2720
2955
|
};
|
|
2721
2956
|
}
|
|
2722
2957
|
});
|
|
2723
2958
|
|
|
2724
|
-
// src/VolumeDivergence/hooks.ts
|
|
2725
|
-
var import_strategies13, volumeDivergenceBeforePlaceOrderHook;
|
|
2726
|
-
var init_hooks3 = __esm({
|
|
2727
|
-
"src/VolumeDivergence/hooks.ts"() {
|
|
2728
|
-
"use strict";
|
|
2729
|
-
import_strategies13 = require("@tradejs/node/strategies");
|
|
2730
|
-
volumeDivergenceBeforePlaceOrderHook = (0, import_strategies13.createCloseOppositeBeforePlaceOrderHook)({
|
|
2731
|
-
isEnabled: (config7) => Boolean(config7.CLOSE_OPPOSITE_POSITIONS)
|
|
2732
|
-
});
|
|
2733
|
-
}
|
|
2734
|
-
});
|
|
2735
|
-
|
|
2736
2959
|
// src/VolumeDivergence/manifest.ts
|
|
2737
2960
|
var volumeDivergenceManifest;
|
|
2738
|
-
var
|
|
2961
|
+
var init_manifest7 = __esm({
|
|
2739
2962
|
"src/VolumeDivergence/manifest.ts"() {
|
|
2740
2963
|
"use strict";
|
|
2741
|
-
|
|
2964
|
+
init_ai7();
|
|
2742
2965
|
init_ml5();
|
|
2743
|
-
init_hooks3();
|
|
2744
2966
|
volumeDivergenceManifest = {
|
|
2745
2967
|
name: "VolumeDivergence",
|
|
2746
|
-
hooks: {
|
|
2747
|
-
beforePlaceOrder: volumeDivergenceBeforePlaceOrderHook
|
|
2748
|
-
},
|
|
2749
2968
|
aiAdapter: volumeDivergenceAiAdapter,
|
|
2750
2969
|
mlAdapter: volumeDivergenceMlAdapter
|
|
2751
2970
|
};
|
|
2752
2971
|
}
|
|
2753
2972
|
});
|
|
2754
2973
|
|
|
2755
|
-
// src/
|
|
2974
|
+
// src/AdaptiveMomentumRibbon/config.ts
|
|
2756
2975
|
var config;
|
|
2757
2976
|
var init_config = __esm({
|
|
2758
|
-
"src/
|
|
2977
|
+
"src/AdaptiveMomentumRibbon/config.ts"() {
|
|
2759
2978
|
"use strict";
|
|
2760
2979
|
config = {
|
|
2761
2980
|
ENV: "BACKTEST",
|
|
@@ -2764,110 +2983,54 @@ var init_config = __esm({
|
|
|
2764
2983
|
CLOSE_OPPOSITE_POSITIONS: false,
|
|
2765
2984
|
BACKTEST_PRICE_MODE: "mid",
|
|
2766
2985
|
AI_ENABLED: false,
|
|
2986
|
+
AI_MODE: "llm",
|
|
2767
2987
|
ML_ENABLED: false,
|
|
2768
2988
|
ML_THRESHOLD: 0.1,
|
|
2769
2989
|
MIN_AI_QUALITY: 3,
|
|
2770
2990
|
FEE_PERCENT: 5e-3,
|
|
2771
2991
|
MAX_LOSS_VALUE: 10,
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
epsilon: 3e-3,
|
|
2790
|
-
epsilonOffset: 4e-3
|
|
2791
|
-
},
|
|
2792
|
-
HIGHS: {
|
|
2992
|
+
AMR_LOOKBACK_BARS: 200,
|
|
2993
|
+
AMR_MOMENTUM_PERIOD: 32,
|
|
2994
|
+
AMR_BUTTERWORTH_SMOOTHING: 4,
|
|
2995
|
+
AMR_WAIT_CLOSE: true,
|
|
2996
|
+
AMR_CONFIRM_ON_NEXT_BAR: true,
|
|
2997
|
+
AMR_MIN_SIGNAL_OSC_ABS: 0.55,
|
|
2998
|
+
AMR_REQUIRE_KC_BIAS: true,
|
|
2999
|
+
AMR_MIN_BARS_BETWEEN_SIGNALS: 12,
|
|
3000
|
+
AMR_SHOW_INVALIDATION_LEVELS: true,
|
|
3001
|
+
AMR_SHOW_KELTNER_CHANNEL: true,
|
|
3002
|
+
AMR_KC_LENGTH: 20,
|
|
3003
|
+
AMR_KC_MA_TYPE: "EMA",
|
|
3004
|
+
AMR_ATR_LENGTH: 14,
|
|
3005
|
+
AMR_ATR_MULTIPLIER: 2,
|
|
3006
|
+
AMR_EXIT_ON_INVALIDATION: true,
|
|
3007
|
+
AMR_LINE_PLOTS: ["kcMidline", "kcUpper", "kcLower", "invalidationLevel"],
|
|
3008
|
+
LONG: {
|
|
2793
3009
|
enable: true,
|
|
2794
3010
|
direction: "LONG",
|
|
2795
|
-
TP:
|
|
2796
|
-
SL: 1
|
|
2797
|
-
minRiskRatio: 2
|
|
3011
|
+
TP: 2,
|
|
3012
|
+
SL: 1
|
|
2798
3013
|
},
|
|
2799
|
-
|
|
3014
|
+
SHORT: {
|
|
2800
3015
|
enable: true,
|
|
2801
3016
|
direction: "SHORT",
|
|
2802
|
-
TP:
|
|
2803
|
-
SL: 1
|
|
2804
|
-
minRiskRatio: 2
|
|
3017
|
+
TP: 2,
|
|
3018
|
+
SL: 1
|
|
2805
3019
|
}
|
|
2806
3020
|
};
|
|
2807
3021
|
}
|
|
2808
3022
|
});
|
|
2809
3023
|
|
|
2810
|
-
// src/
|
|
3024
|
+
// src/Breakout/config.ts
|
|
2811
3025
|
var config2;
|
|
2812
3026
|
var init_config2 = __esm({
|
|
2813
|
-
"src/
|
|
3027
|
+
"src/Breakout/config.ts"() {
|
|
2814
3028
|
"use strict";
|
|
2815
3029
|
config2 = {
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
MAKE_ORDERS: true,
|
|
2819
|
-
CLOSE_OPPOSITE_POSITIONS: false,
|
|
2820
|
-
BACKTEST_PRICE_MODE: "mid",
|
|
2821
|
-
AI_ENABLED: false,
|
|
2822
|
-
MIN_AI_QUALITY: 3,
|
|
2823
|
-
FEE_PERCENT: 5e-3,
|
|
2824
|
-
MAX_LOSS_VALUE: 10,
|
|
2825
|
-
MA_FAST: 14,
|
|
3030
|
+
ML_ENABLED: false,
|
|
3031
|
+
MA_FAST: 49,
|
|
2826
3032
|
MA_MEDIUM: 49,
|
|
2827
|
-
MA_SLOW:
|
|
2828
|
-
OBV_SMA: 10,
|
|
2829
|
-
ATR: 14,
|
|
2830
|
-
ATR_PCT_SHORT: 7,
|
|
2831
|
-
ATR_PCT_LONG: 30,
|
|
2832
|
-
BB: 20,
|
|
2833
|
-
BB_STD: 2,
|
|
2834
|
-
MACD_FAST: 12,
|
|
2835
|
-
MACD_SLOW: 26,
|
|
2836
|
-
MACD_SIGNAL: 9,
|
|
2837
|
-
TRENDLINE: {
|
|
2838
|
-
minTouches: 4,
|
|
2839
|
-
offset: 3,
|
|
2840
|
-
epsilon: 3e-3,
|
|
2841
|
-
epsilonOffset: 4e-3
|
|
2842
|
-
},
|
|
2843
|
-
HIGHS: {
|
|
2844
|
-
enable: true,
|
|
2845
|
-
direction: "SHORT",
|
|
2846
|
-
TP: 3.2,
|
|
2847
|
-
SL: 1.1,
|
|
2848
|
-
minRiskRatio: 1.6
|
|
2849
|
-
},
|
|
2850
|
-
LOWS: {
|
|
2851
|
-
enable: true,
|
|
2852
|
-
direction: "LONG",
|
|
2853
|
-
TP: 3.2,
|
|
2854
|
-
SL: 1.1,
|
|
2855
|
-
minRiskRatio: 1.6
|
|
2856
|
-
}
|
|
2857
|
-
};
|
|
2858
|
-
}
|
|
2859
|
-
});
|
|
2860
|
-
|
|
2861
|
-
// src/Breakout/config.ts
|
|
2862
|
-
var config3;
|
|
2863
|
-
var init_config3 = __esm({
|
|
2864
|
-
"src/Breakout/config.ts"() {
|
|
2865
|
-
"use strict";
|
|
2866
|
-
config3 = {
|
|
2867
|
-
ML_ENABLED: false,
|
|
2868
|
-
MA_FAST: 49,
|
|
2869
|
-
MA_MEDIUM: 49,
|
|
2870
|
-
MA_SLOW: 99,
|
|
3033
|
+
MA_SLOW: 99,
|
|
2871
3034
|
OBV_SMA: 10,
|
|
2872
3035
|
ATR: 14,
|
|
2873
3036
|
ATR_PCT_SHORT: 7,
|
|
@@ -2964,15 +3127,277 @@ var init_config3 = __esm({
|
|
|
2964
3127
|
}
|
|
2965
3128
|
});
|
|
2966
3129
|
|
|
3130
|
+
// src/MaStrategy/config.ts
|
|
3131
|
+
var config3;
|
|
3132
|
+
var init_config3 = __esm({
|
|
3133
|
+
"src/MaStrategy/config.ts"() {
|
|
3134
|
+
"use strict";
|
|
3135
|
+
config3 = {
|
|
3136
|
+
ENV: "BACKTEST",
|
|
3137
|
+
INTERVAL: "15",
|
|
3138
|
+
MAKE_ORDERS: true,
|
|
3139
|
+
CLOSE_OPPOSITE_POSITIONS: false,
|
|
3140
|
+
BACKTEST_PRICE_MODE: "mid",
|
|
3141
|
+
AI_ENABLED: false,
|
|
3142
|
+
AI_MODE: "llm",
|
|
3143
|
+
ML_ENABLED: false,
|
|
3144
|
+
ML_THRESHOLD: 0.1,
|
|
3145
|
+
MIN_AI_QUALITY: 3,
|
|
3146
|
+
FEE_PERCENT: 5e-3,
|
|
3147
|
+
MAX_LOSS_VALUE: 10,
|
|
3148
|
+
TRADE_COOLDOWN_MS: 0,
|
|
3149
|
+
MA_FAST: 21,
|
|
3150
|
+
MA_SLOW: 55,
|
|
3151
|
+
LONG: {
|
|
3152
|
+
enable: true,
|
|
3153
|
+
direction: "LONG",
|
|
3154
|
+
TP: 2,
|
|
3155
|
+
SL: 1,
|
|
3156
|
+
minRiskRatio: 1.5
|
|
3157
|
+
},
|
|
3158
|
+
SHORT: {
|
|
3159
|
+
enable: true,
|
|
3160
|
+
direction: "SHORT",
|
|
3161
|
+
TP: 2,
|
|
3162
|
+
SL: 1,
|
|
3163
|
+
minRiskRatio: 1.5
|
|
3164
|
+
}
|
|
3165
|
+
};
|
|
3166
|
+
}
|
|
3167
|
+
});
|
|
3168
|
+
|
|
3169
|
+
// src/ReverseTrendLine/config.ts
|
|
3170
|
+
var config4;
|
|
3171
|
+
var init_config4 = __esm({
|
|
3172
|
+
"src/ReverseTrendLine/config.ts"() {
|
|
3173
|
+
"use strict";
|
|
3174
|
+
config4 = {
|
|
3175
|
+
ENV: "BACKTEST",
|
|
3176
|
+
INTERVAL: "15",
|
|
3177
|
+
MAKE_ORDERS: true,
|
|
3178
|
+
CLOSE_OPPOSITE_POSITIONS: false,
|
|
3179
|
+
BACKTEST_PRICE_MODE: "mid",
|
|
3180
|
+
AI_ENABLED: false,
|
|
3181
|
+
AI_MODE: "llm",
|
|
3182
|
+
MIN_AI_QUALITY: 3,
|
|
3183
|
+
FEE_PERCENT: 5e-3,
|
|
3184
|
+
MAX_LOSS_VALUE: 10,
|
|
3185
|
+
MA_FAST: 14,
|
|
3186
|
+
MA_MEDIUM: 49,
|
|
3187
|
+
MA_SLOW: 50,
|
|
3188
|
+
OBV_SMA: 10,
|
|
3189
|
+
ATR: 14,
|
|
3190
|
+
ATR_PCT_SHORT: 7,
|
|
3191
|
+
ATR_PCT_LONG: 30,
|
|
3192
|
+
BB: 20,
|
|
3193
|
+
BB_STD: 2,
|
|
3194
|
+
MACD_FAST: 12,
|
|
3195
|
+
MACD_SLOW: 26,
|
|
3196
|
+
MACD_SIGNAL: 9,
|
|
3197
|
+
TRENDLINE: {
|
|
3198
|
+
minTouches: 4,
|
|
3199
|
+
offset: 3,
|
|
3200
|
+
epsilon: 3e-3,
|
|
3201
|
+
epsilonOffset: 4e-3
|
|
3202
|
+
},
|
|
3203
|
+
HIGHS: {
|
|
3204
|
+
enable: true,
|
|
3205
|
+
direction: "SHORT",
|
|
3206
|
+
TP: 3.2,
|
|
3207
|
+
SL: 1.1,
|
|
3208
|
+
minRiskRatio: 1.6
|
|
3209
|
+
},
|
|
3210
|
+
LOWS: {
|
|
3211
|
+
enable: true,
|
|
3212
|
+
direction: "LONG",
|
|
3213
|
+
TP: 3.2,
|
|
3214
|
+
SL: 1.1,
|
|
3215
|
+
minRiskRatio: 1.6
|
|
3216
|
+
}
|
|
3217
|
+
};
|
|
3218
|
+
}
|
|
3219
|
+
});
|
|
3220
|
+
|
|
3221
|
+
// src/TrendShift/config.ts
|
|
3222
|
+
var config5;
|
|
3223
|
+
var init_config5 = __esm({
|
|
3224
|
+
"src/TrendShift/config.ts"() {
|
|
3225
|
+
"use strict";
|
|
3226
|
+
config5 = {
|
|
3227
|
+
ENV: "BACKTEST",
|
|
3228
|
+
INTERVAL: "15",
|
|
3229
|
+
MAKE_ORDERS: true,
|
|
3230
|
+
CLOSE_OPPOSITE_POSITIONS: false,
|
|
3231
|
+
BACKTEST_PRICE_MODE: "mid",
|
|
3232
|
+
AI_ENABLED: false,
|
|
3233
|
+
AI_MODE: "llm",
|
|
3234
|
+
ML_ENABLED: false,
|
|
3235
|
+
ML_THRESHOLD: 0.1,
|
|
3236
|
+
MIN_AI_QUALITY: 3,
|
|
3237
|
+
FEE_PERCENT: 5e-3,
|
|
3238
|
+
MAX_LOSS_VALUE: 10,
|
|
3239
|
+
MA_FAST: 14,
|
|
3240
|
+
MA_MEDIUM: 49,
|
|
3241
|
+
MA_SLOW: 50,
|
|
3242
|
+
OBV_SMA: 10,
|
|
3243
|
+
ATR: 14,
|
|
3244
|
+
ATR_PCT_SHORT: 7,
|
|
3245
|
+
ATR_PCT_LONG: 30,
|
|
3246
|
+
BB: 20,
|
|
3247
|
+
BB_STD: 2,
|
|
3248
|
+
MACD_FAST: 12,
|
|
3249
|
+
MACD_SLOW: 26,
|
|
3250
|
+
MACD_SIGNAL: 9,
|
|
3251
|
+
TRENDSHIFT_MULTIPLICATIVE_FACTOR: 4,
|
|
3252
|
+
TRENDSHIFT_SLOPE: 12,
|
|
3253
|
+
TRENDSHIFT_ATR_LENGTH: 150,
|
|
3254
|
+
TRENDSHIFT_WIDTH_PCT: 75,
|
|
3255
|
+
TRENDSHIFT_CONFIRM_FLIP_WITH_CLOSE: true,
|
|
3256
|
+
TRENDSHIFT_MIN_FLIP_DISTANCE_ATR: 0.15,
|
|
3257
|
+
TRENDSHIFT_EXIT_ON_OPPOSITE_FLIP: true,
|
|
3258
|
+
TRENDSHIFT_MAX_FIGURE_POINTS: 180,
|
|
3259
|
+
LONG: {
|
|
3260
|
+
enable: true,
|
|
3261
|
+
direction: "LONG",
|
|
3262
|
+
TP: 2.8,
|
|
3263
|
+
SL: 1.1,
|
|
3264
|
+
minRiskRatio: 1.6
|
|
3265
|
+
},
|
|
3266
|
+
SHORT: {
|
|
3267
|
+
enable: true,
|
|
3268
|
+
direction: "SHORT",
|
|
3269
|
+
TP: 2.8,
|
|
3270
|
+
SL: 1.1,
|
|
3271
|
+
minRiskRatio: 1.6
|
|
3272
|
+
}
|
|
3273
|
+
};
|
|
3274
|
+
}
|
|
3275
|
+
});
|
|
3276
|
+
|
|
3277
|
+
// src/TrendLine/config.ts
|
|
3278
|
+
var config6;
|
|
3279
|
+
var init_config6 = __esm({
|
|
3280
|
+
"src/TrendLine/config.ts"() {
|
|
3281
|
+
"use strict";
|
|
3282
|
+
config6 = {
|
|
3283
|
+
ENV: "BACKTEST",
|
|
3284
|
+
INTERVAL: "15",
|
|
3285
|
+
MAKE_ORDERS: true,
|
|
3286
|
+
CLOSE_OPPOSITE_POSITIONS: false,
|
|
3287
|
+
BACKTEST_PRICE_MODE: "mid",
|
|
3288
|
+
AI_ENABLED: false,
|
|
3289
|
+
AI_MODE: "llm",
|
|
3290
|
+
ML_ENABLED: false,
|
|
3291
|
+
ML_THRESHOLD: 0.1,
|
|
3292
|
+
MIN_AI_QUALITY: 3,
|
|
3293
|
+
FEE_PERCENT: 5e-3,
|
|
3294
|
+
MAX_LOSS_VALUE: 10,
|
|
3295
|
+
MA_FAST: 14,
|
|
3296
|
+
MA_MEDIUM: 49,
|
|
3297
|
+
MA_SLOW: 50,
|
|
3298
|
+
OBV_SMA: 10,
|
|
3299
|
+
ATR: 14,
|
|
3300
|
+
ATR_PCT_SHORT: 7,
|
|
3301
|
+
ATR_PCT_LONG: 30,
|
|
3302
|
+
BB: 20,
|
|
3303
|
+
BB_STD: 2,
|
|
3304
|
+
MACD_FAST: 12,
|
|
3305
|
+
MACD_SLOW: 26,
|
|
3306
|
+
MACD_SIGNAL: 9,
|
|
3307
|
+
LEVEL_LOOKBACK: 20,
|
|
3308
|
+
LEVEL_DELAY: 2,
|
|
3309
|
+
TRENDLINE: {
|
|
3310
|
+
minTouches: 4,
|
|
3311
|
+
offset: 3,
|
|
3312
|
+
epsilon: 3e-3,
|
|
3313
|
+
epsilonOffset: 4e-3
|
|
3314
|
+
},
|
|
3315
|
+
HIGHS: {
|
|
3316
|
+
enable: true,
|
|
3317
|
+
direction: "LONG",
|
|
3318
|
+
TP: 4,
|
|
3319
|
+
SL: 1.3,
|
|
3320
|
+
minRiskRatio: 2
|
|
3321
|
+
},
|
|
3322
|
+
LOWS: {
|
|
3323
|
+
enable: true,
|
|
3324
|
+
direction: "SHORT",
|
|
3325
|
+
TP: 4,
|
|
3326
|
+
SL: 1.3,
|
|
3327
|
+
minRiskRatio: 2
|
|
3328
|
+
}
|
|
3329
|
+
};
|
|
3330
|
+
}
|
|
3331
|
+
});
|
|
3332
|
+
|
|
3333
|
+
// src/VolumeDivergence/config.ts
|
|
3334
|
+
var config7;
|
|
3335
|
+
var init_config7 = __esm({
|
|
3336
|
+
"src/VolumeDivergence/config.ts"() {
|
|
3337
|
+
"use strict";
|
|
3338
|
+
config7 = {
|
|
3339
|
+
ENV: "BACKTEST",
|
|
3340
|
+
INTERVAL: "15",
|
|
3341
|
+
MAKE_ORDERS: true,
|
|
3342
|
+
CLOSE_OPPOSITE_POSITIONS: false,
|
|
3343
|
+
BACKTEST_PRICE_MODE: "mid",
|
|
3344
|
+
AI_ENABLED: false,
|
|
3345
|
+
AI_MODE: "llm",
|
|
3346
|
+
ML_ENABLED: false,
|
|
3347
|
+
ML_THRESHOLD: 0.1,
|
|
3348
|
+
MIN_AI_QUALITY: 3,
|
|
3349
|
+
FEE_PERCENT: 5e-3,
|
|
3350
|
+
MAX_LOSS_VALUE: 10,
|
|
3351
|
+
MA_FAST: 14,
|
|
3352
|
+
MA_MEDIUM: 49,
|
|
3353
|
+
MA_SLOW: 50,
|
|
3354
|
+
OBV_SMA: 10,
|
|
3355
|
+
ATR: 14,
|
|
3356
|
+
ATR_PCT_SHORT: 7,
|
|
3357
|
+
ATR_PCT_LONG: 30,
|
|
3358
|
+
BB: 20,
|
|
3359
|
+
BB_STD: 2,
|
|
3360
|
+
MACD_FAST: 12,
|
|
3361
|
+
MACD_SLOW: 26,
|
|
3362
|
+
MACD_SIGNAL: 9,
|
|
3363
|
+
LEVEL_LOOKBACK: 20,
|
|
3364
|
+
LEVEL_DELAY: 2,
|
|
3365
|
+
NORMALIZATION_LENGTH: 100,
|
|
3366
|
+
PIVOT_LOOKBACK_LEFT: 8,
|
|
3367
|
+
PIVOT_LOOKBACK_RIGHT: 3,
|
|
3368
|
+
MIN_BARS_BETWEEN_PIVOTS: 4,
|
|
3369
|
+
MAX_BARS_BETWEEN_PIVOTS: 36,
|
|
3370
|
+
ALLOW_STRUCTURE_ADVANCE_ENTRY: false,
|
|
3371
|
+
MIN_DIVERGENCE_AMPLITUDE_ATR_RATIO: 0.35,
|
|
3372
|
+
MIN_RECLAIM_PCT: 105,
|
|
3373
|
+
MIN_CONFIRMATION_CANDLE_QUALITY: 0.58,
|
|
3374
|
+
BULLISH: {
|
|
3375
|
+
enable: true,
|
|
3376
|
+
direction: "LONG",
|
|
3377
|
+
TP: 4,
|
|
3378
|
+
SL: 1.3,
|
|
3379
|
+
minRiskRatio: 2
|
|
3380
|
+
},
|
|
3381
|
+
BEARISH: {
|
|
3382
|
+
enable: true,
|
|
3383
|
+
direction: "SHORT",
|
|
3384
|
+
TP: 4,
|
|
3385
|
+
SL: 1.3,
|
|
3386
|
+
minRiskRatio: 2
|
|
3387
|
+
}
|
|
3388
|
+
};
|
|
3389
|
+
}
|
|
3390
|
+
});
|
|
3391
|
+
|
|
2967
3392
|
// ../../node_modules/lodash/lodash.js
|
|
2968
3393
|
var require_lodash = __commonJS({
|
|
2969
3394
|
"../../node_modules/lodash/lodash.js"(exports2, module2) {
|
|
2970
3395
|
"use strict";
|
|
2971
3396
|
(function() {
|
|
2972
3397
|
var undefined2;
|
|
2973
|
-
var VERSION = "4.
|
|
3398
|
+
var VERSION = "4.18.1";
|
|
2974
3399
|
var LARGE_ARRAY_SIZE = 200;
|
|
2975
|
-
var CORE_ERROR_TEXT = "Unsupported core-js use. Try https://npms.io/search?q=ponyfill.", FUNC_ERROR_TEXT = "Expected a function", INVALID_TEMPL_VAR_ERROR_TEXT = "Invalid `variable` option passed into `_.template`";
|
|
3400
|
+
var CORE_ERROR_TEXT = "Unsupported core-js use. Try https://npms.io/search?q=ponyfill.", FUNC_ERROR_TEXT = "Expected a function", INVALID_TEMPL_VAR_ERROR_TEXT = "Invalid `variable` option passed into `_.template`", INVALID_TEMPL_IMPORTS_ERROR_TEXT = "Invalid `imports` option passed into `_.template`";
|
|
2976
3401
|
var HASH_UNDEFINED = "__lodash_hash_undefined__";
|
|
2977
3402
|
var MAX_MEMOIZE_SIZE = 500;
|
|
2978
3403
|
var PLACEHOLDER = "__lodash_placeholder__";
|
|
@@ -4898,9 +5323,22 @@ var require_lodash = __commonJS({
|
|
|
4898
5323
|
}
|
|
4899
5324
|
function baseUnset(object, path) {
|
|
4900
5325
|
path = castPath(path, object);
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
5326
|
+
var index = -1, length = path.length;
|
|
5327
|
+
if (!length) {
|
|
5328
|
+
return true;
|
|
5329
|
+
}
|
|
5330
|
+
while (++index < length) {
|
|
5331
|
+
var key = toKey(path[index]);
|
|
5332
|
+
if (key === "__proto__" && !hasOwnProperty.call(object, "__proto__")) {
|
|
5333
|
+
return false;
|
|
5334
|
+
}
|
|
5335
|
+
if ((key === "constructor" || key === "prototype") && index < length - 1) {
|
|
5336
|
+
return false;
|
|
5337
|
+
}
|
|
5338
|
+
}
|
|
5339
|
+
var obj = parent(object, path);
|
|
5340
|
+
return obj == null || delete obj[toKey(last(path))];
|
|
5341
|
+
}
|
|
4904
5342
|
function baseUpdate(object, path, updater, customizer) {
|
|
4905
5343
|
return baseSet(object, path, updater(baseGet(object, path)), customizer);
|
|
4906
5344
|
}
|
|
@@ -5381,14 +5819,14 @@ var require_lodash = __commonJS({
|
|
|
5381
5819
|
if (step && typeof step != "number" && isIterateeCall(start, end, step)) {
|
|
5382
5820
|
end = step = undefined2;
|
|
5383
5821
|
}
|
|
5384
|
-
start =
|
|
5822
|
+
start = toFinite2(start);
|
|
5385
5823
|
if (end === undefined2) {
|
|
5386
5824
|
end = start;
|
|
5387
5825
|
start = 0;
|
|
5388
5826
|
} else {
|
|
5389
|
-
end =
|
|
5827
|
+
end = toFinite2(end);
|
|
5390
5828
|
}
|
|
5391
|
-
step = step === undefined2 ? start < end ? 1 : -1 :
|
|
5829
|
+
step = step === undefined2 ? start < end ? 1 : -1 : toFinite2(step);
|
|
5392
5830
|
return baseRange(start, end, step, fromRight);
|
|
5393
5831
|
};
|
|
5394
5832
|
}
|
|
@@ -6226,7 +6664,7 @@ var require_lodash = __commonJS({
|
|
|
6226
6664
|
var index = -1, length = pairs == null ? 0 : pairs.length, result2 = {};
|
|
6227
6665
|
while (++index < length) {
|
|
6228
6666
|
var pair = pairs[index];
|
|
6229
|
-
result2
|
|
6667
|
+
baseAssignValue(result2, pair[0], pair[1]);
|
|
6230
6668
|
}
|
|
6231
6669
|
return result2;
|
|
6232
6670
|
}
|
|
@@ -7161,7 +7599,7 @@ var require_lodash = __commonJS({
|
|
|
7161
7599
|
var tag = getTag(value), func = tag == mapTag ? mapToArray : tag == setTag ? setToArray : values;
|
|
7162
7600
|
return func(value);
|
|
7163
7601
|
}
|
|
7164
|
-
function
|
|
7602
|
+
function toFinite2(value) {
|
|
7165
7603
|
if (!value) {
|
|
7166
7604
|
return value === 0 ? value : 0;
|
|
7167
7605
|
}
|
|
@@ -7173,7 +7611,7 @@ var require_lodash = __commonJS({
|
|
|
7173
7611
|
return value === value ? value : 0;
|
|
7174
7612
|
}
|
|
7175
7613
|
function toInteger(value) {
|
|
7176
|
-
var result2 =
|
|
7614
|
+
var result2 = toFinite2(value), remainder = result2 % 1;
|
|
7177
7615
|
return result2 === result2 ? remainder ? result2 - remainder : result2 : 0;
|
|
7178
7616
|
}
|
|
7179
7617
|
function toLength(value) {
|
|
@@ -7452,12 +7890,12 @@ var require_lodash = __commonJS({
|
|
|
7452
7890
|
return baseClamp(toNumber(number), lower, upper);
|
|
7453
7891
|
}
|
|
7454
7892
|
function inRange(number, start, end) {
|
|
7455
|
-
start =
|
|
7893
|
+
start = toFinite2(start);
|
|
7456
7894
|
if (end === undefined2) {
|
|
7457
7895
|
end = start;
|
|
7458
7896
|
start = 0;
|
|
7459
7897
|
} else {
|
|
7460
|
-
end =
|
|
7898
|
+
end = toFinite2(end);
|
|
7461
7899
|
}
|
|
7462
7900
|
number = toNumber(number);
|
|
7463
7901
|
return baseInRange(number, start, end);
|
|
@@ -7479,12 +7917,12 @@ var require_lodash = __commonJS({
|
|
|
7479
7917
|
lower = 0;
|
|
7480
7918
|
upper = 1;
|
|
7481
7919
|
} else {
|
|
7482
|
-
lower =
|
|
7920
|
+
lower = toFinite2(lower);
|
|
7483
7921
|
if (upper === undefined2) {
|
|
7484
7922
|
upper = lower;
|
|
7485
7923
|
lower = 0;
|
|
7486
7924
|
} else {
|
|
7487
|
-
upper =
|
|
7925
|
+
upper = toFinite2(upper);
|
|
7488
7926
|
}
|
|
7489
7927
|
}
|
|
7490
7928
|
if (lower > upper) {
|
|
@@ -7610,8 +8048,13 @@ var require_lodash = __commonJS({
|
|
|
7610
8048
|
options = undefined2;
|
|
7611
8049
|
}
|
|
7612
8050
|
string = toString(string);
|
|
7613
|
-
options =
|
|
7614
|
-
var imports =
|
|
8051
|
+
options = assignWith({}, options, settings, customDefaultsAssignIn);
|
|
8052
|
+
var imports = assignWith({}, options.imports, settings.imports, customDefaultsAssignIn), importsKeys = keys(imports), importsValues = baseValues(imports, importsKeys);
|
|
8053
|
+
arrayEach(importsKeys, function(key) {
|
|
8054
|
+
if (reForbiddenIdentifierChars.test(key)) {
|
|
8055
|
+
throw new Error2(INVALID_TEMPL_IMPORTS_ERROR_TEXT);
|
|
8056
|
+
}
|
|
8057
|
+
});
|
|
7615
8058
|
var isEscaping, isEvaluating, index = 0, interpolate = options.interpolate || reNoMatch, source = "__p += '";
|
|
7616
8059
|
var reDelimiters = RegExp2(
|
|
7617
8060
|
(options.escape || reNoMatch).source + "|" + interpolate.source + "|" + (interpolate === reInterpolate ? reEsTemplate : reNoMatch).source + "|" + (options.evaluate || reNoMatch).source + "|$",
|
|
@@ -7944,7 +8387,7 @@ var require_lodash = __commonJS({
|
|
|
7944
8387
|
var multiply = createMathOperation(function(multiplier, multiplicand) {
|
|
7945
8388
|
return multiplier * multiplicand;
|
|
7946
8389
|
}, 1);
|
|
7947
|
-
var
|
|
8390
|
+
var round8 = createRound("round");
|
|
7948
8391
|
var subtract = createMathOperation(function(minuend, subtrahend) {
|
|
7949
8392
|
return minuend - subtrahend;
|
|
7950
8393
|
}, 0);
|
|
@@ -8222,7 +8665,7 @@ var require_lodash = __commonJS({
|
|
|
8222
8665
|
lodash.repeat = repeat;
|
|
8223
8666
|
lodash.replace = replace;
|
|
8224
8667
|
lodash.result = result;
|
|
8225
|
-
lodash.round =
|
|
8668
|
+
lodash.round = round8;
|
|
8226
8669
|
lodash.runInContext = runInContext2;
|
|
8227
8670
|
lodash.sample = sample;
|
|
8228
8671
|
lodash.size = size;
|
|
@@ -8241,7 +8684,7 @@ var require_lodash = __commonJS({
|
|
|
8241
8684
|
lodash.sumBy = sumBy;
|
|
8242
8685
|
lodash.template = template;
|
|
8243
8686
|
lodash.times = times;
|
|
8244
|
-
lodash.toFinite =
|
|
8687
|
+
lodash.toFinite = toFinite2;
|
|
8245
8688
|
lodash.toInteger = toInteger;
|
|
8246
8689
|
lodash.toLength = toLength;
|
|
8247
8690
|
lodash.toLower = toLower;
|
|
@@ -8461,7 +8904,7 @@ var init_core = __esm({
|
|
|
8461
8904
|
"use strict";
|
|
8462
8905
|
import_lodash = __toESM(require_lodash());
|
|
8463
8906
|
init_figures();
|
|
8464
|
-
getSignals = (
|
|
8907
|
+
getSignals = (config8, indicators) => {
|
|
8465
8908
|
const {
|
|
8466
8909
|
candle,
|
|
8467
8910
|
prevCandle,
|
|
@@ -8486,7 +8929,7 @@ var init_core = __esm({
|
|
|
8486
8929
|
const closeBelowLowerBB = candle.close < bb.lower;
|
|
8487
8930
|
const closeBelowLowLevel = candle.close < lowLevel;
|
|
8488
8931
|
const closeBelowPrevClose = candle.close < prevCandle.close;
|
|
8489
|
-
const atrThreshold = atr *
|
|
8932
|
+
const atrThreshold = atr * config8.ATR_OPEN;
|
|
8490
8933
|
const trueRange = Math.max(
|
|
8491
8934
|
candle.high - candle.low,
|
|
8492
8935
|
Math.abs(candle.high - prevCandle.close),
|
|
@@ -8509,9 +8952,9 @@ var init_core = __esm({
|
|
|
8509
8952
|
["CLOSE_BELOW_PREV_CLOSE" /* CLOSE_BELOW_PREV_CLOSE */]: closeBelowPrevClose
|
|
8510
8953
|
};
|
|
8511
8954
|
};
|
|
8512
|
-
checkSignals = (
|
|
8955
|
+
checkSignals = (config8, minScore, signals) => {
|
|
8513
8956
|
let score = 0;
|
|
8514
|
-
for (const [signal, rules] of Object.entries(
|
|
8957
|
+
for (const [signal, rules] of Object.entries(config8)) {
|
|
8515
8958
|
if (rules.required && !signals[signal]) {
|
|
8516
8959
|
return false;
|
|
8517
8960
|
}
|
|
@@ -8521,7 +8964,7 @@ var init_core = __esm({
|
|
|
8521
8964
|
}
|
|
8522
8965
|
return score >= minScore;
|
|
8523
8966
|
};
|
|
8524
|
-
createBreakoutCore = async ({ config:
|
|
8967
|
+
createBreakoutCore = async ({ config: config8, strategyApi }) => {
|
|
8525
8968
|
return async (candle, btcCandle) => {
|
|
8526
8969
|
if (import_lodash.default.isEmpty(candle)) {
|
|
8527
8970
|
return strategyApi.skip("NO_DATA");
|
|
@@ -8536,8 +8979,8 @@ var init_core = __esm({
|
|
|
8536
8979
|
const { currentPrice, timestamp } = await strategyApi.getMarketData();
|
|
8537
8980
|
const position = await strategyApi.getCurrentPosition();
|
|
8538
8981
|
const positionExists = await strategyApi.isCurrentPositionExists();
|
|
8539
|
-
const qty =
|
|
8540
|
-
const signals = getSignals(
|
|
8982
|
+
const qty = config8.LIMIT / currentPrice;
|
|
8983
|
+
const signals = getSignals(config8, {
|
|
8541
8984
|
...indicatorValues,
|
|
8542
8985
|
prevCandle: indicatorValues.prevCandle,
|
|
8543
8986
|
highLevel: indicatorValues.highLevel,
|
|
@@ -8548,13 +8991,13 @@ var init_core = __esm({
|
|
|
8548
8991
|
}
|
|
8549
8992
|
});
|
|
8550
8993
|
const shouldOpenLong = checkSignals(
|
|
8551
|
-
|
|
8552
|
-
|
|
8994
|
+
config8.SIGNALS_LONG,
|
|
8995
|
+
config8.REQUIRED_SCORE_LONG,
|
|
8553
8996
|
signals
|
|
8554
8997
|
);
|
|
8555
8998
|
const shouldOpenShort = checkSignals(
|
|
8556
|
-
|
|
8557
|
-
|
|
8999
|
+
config8.SIGNALS_SHORT,
|
|
9000
|
+
config8.REQUIRED_SCORE_SHORT,
|
|
8558
9001
|
signals
|
|
8559
9002
|
);
|
|
8560
9003
|
if (!positionExists || !position) {
|
|
@@ -8562,8 +9005,8 @@ var init_core = __esm({
|
|
|
8562
9005
|
const { stopLossPrice, takeProfitPrice } = strategyApi.getDirectionalTpSlPrices({
|
|
8563
9006
|
price: currentPrice,
|
|
8564
9007
|
direction: "LONG",
|
|
8565
|
-
takeProfitDelta:
|
|
8566
|
-
stopLossDelta:
|
|
9008
|
+
takeProfitDelta: config8.TP_LONG?.[0]?.profit ?? 0,
|
|
9009
|
+
stopLossDelta: config8.SL_LONG,
|
|
8567
9010
|
unit: "ratio"
|
|
8568
9011
|
});
|
|
8569
9012
|
return strategyApi.entry({
|
|
@@ -8588,7 +9031,7 @@ var init_core = __esm({
|
|
|
8588
9031
|
orderPlan: {
|
|
8589
9032
|
qty,
|
|
8590
9033
|
stopLossPrice,
|
|
8591
|
-
takeProfits:
|
|
9034
|
+
takeProfits: config8.TP_LONG.length > 0 ? config8.TP_LONG.map(({ rate, profit }) => ({
|
|
8592
9035
|
rate,
|
|
8593
9036
|
price: currentPrice * (1 + profit)
|
|
8594
9037
|
})) : [{ rate: 1, price: takeProfitPrice }]
|
|
@@ -8599,8 +9042,8 @@ var init_core = __esm({
|
|
|
8599
9042
|
const { stopLossPrice, takeProfitPrice } = strategyApi.getDirectionalTpSlPrices({
|
|
8600
9043
|
price: currentPrice,
|
|
8601
9044
|
direction: "SHORT",
|
|
8602
|
-
takeProfitDelta:
|
|
8603
|
-
stopLossDelta:
|
|
9045
|
+
takeProfitDelta: config8.TP_SHORT?.[0]?.profit ?? 0,
|
|
9046
|
+
stopLossDelta: config8.SL_SHORT,
|
|
8604
9047
|
unit: "ratio"
|
|
8605
9048
|
});
|
|
8606
9049
|
return strategyApi.entry({
|
|
@@ -8625,7 +9068,7 @@ var init_core = __esm({
|
|
|
8625
9068
|
orderPlan: {
|
|
8626
9069
|
qty,
|
|
8627
9070
|
stopLossPrice,
|
|
8628
|
-
takeProfits:
|
|
9071
|
+
takeProfits: config8.TP_SHORT.length > 0 ? config8.TP_SHORT.map(({ rate, profit }) => ({
|
|
8629
9072
|
rate,
|
|
8630
9073
|
price: currentPrice * (1 - profit)
|
|
8631
9074
|
})) : [{ rate: 1, price: takeProfitPrice }]
|
|
@@ -8662,17 +9105,17 @@ var strategy_exports = {};
|
|
|
8662
9105
|
__export(strategy_exports, {
|
|
8663
9106
|
BreakoutStrategyCreator: () => BreakoutStrategyCreator
|
|
8664
9107
|
});
|
|
8665
|
-
var
|
|
9108
|
+
var import_strategies12, BreakoutStrategyCreator;
|
|
8666
9109
|
var init_strategy = __esm({
|
|
8667
9110
|
"src/Breakout/strategy.ts"() {
|
|
8668
9111
|
"use strict";
|
|
8669
|
-
|
|
8670
|
-
|
|
9112
|
+
import_strategies12 = require("@tradejs/node/strategies");
|
|
9113
|
+
init_config2();
|
|
8671
9114
|
init_core();
|
|
8672
9115
|
init_manifest2();
|
|
8673
|
-
BreakoutStrategyCreator = (0,
|
|
9116
|
+
BreakoutStrategyCreator = (0, import_strategies12.createStrategyRuntime)({
|
|
8674
9117
|
strategyName: "Breakout",
|
|
8675
|
-
defaults:
|
|
9118
|
+
defaults: config2,
|
|
8676
9119
|
createCore: createBreakoutCore,
|
|
8677
9120
|
manifest: breakoutManifest,
|
|
8678
9121
|
strategyDirectory: __dirname
|
|
@@ -8867,7 +9310,7 @@ var init_risk = __esm({
|
|
|
8867
9310
|
});
|
|
8868
9311
|
|
|
8869
9312
|
// src/TrendLine/core.ts
|
|
8870
|
-
var import_math3, import_indicators2,
|
|
9313
|
+
var import_math3, import_indicators2, buildTrendlineSignalSeed, isOpenPosition, isFailedBreakout, createTrendLineCore;
|
|
8871
9314
|
var init_core2 = __esm({
|
|
8872
9315
|
"src/TrendLine/core.ts"() {
|
|
8873
9316
|
"use strict";
|
|
@@ -8877,7 +9320,6 @@ var init_core2 = __esm({
|
|
|
8877
9320
|
init_figures2();
|
|
8878
9321
|
init_guardrails2();
|
|
8879
9322
|
init_risk();
|
|
8880
|
-
BREAK_EVEN_TRIGGER_RISK_MULTIPLIER = 0.5;
|
|
8881
9323
|
buildTrendlineSignalSeed = ({
|
|
8882
9324
|
direction,
|
|
8883
9325
|
currentPrice,
|
|
@@ -8901,51 +9343,6 @@ var init_core2 = __esm({
|
|
|
8901
9343
|
isOpenPosition = (position) => Boolean(
|
|
8902
9344
|
position && typeof position.price === "number" && Number.isFinite(position.price) && typeof position.qty === "number" && Number.isFinite(position.qty) && position.qty > 0 && (position.direction === "LONG" || position.direction === "SHORT")
|
|
8903
9345
|
);
|
|
8904
|
-
getFavorableMovePct = ({
|
|
8905
|
-
direction,
|
|
8906
|
-
entryPrice,
|
|
8907
|
-
currentPrice
|
|
8908
|
-
}) => {
|
|
8909
|
-
if (!Number.isFinite(entryPrice) || !Number.isFinite(currentPrice) || entryPrice <= 0) {
|
|
8910
|
-
return null;
|
|
8911
|
-
}
|
|
8912
|
-
return direction === "LONG" ? (currentPrice - entryPrice) / entryPrice * 100 : (entryPrice - currentPrice) / entryPrice * 100;
|
|
8913
|
-
};
|
|
8914
|
-
getPositionStopLossPrice = (position) => {
|
|
8915
|
-
if (!position || typeof position !== "object") {
|
|
8916
|
-
return null;
|
|
8917
|
-
}
|
|
8918
|
-
const slPrice = Number(
|
|
8919
|
-
position.slPrice ?? Number.NaN
|
|
8920
|
-
);
|
|
8921
|
-
if (Number.isFinite(slPrice)) {
|
|
8922
|
-
return slPrice;
|
|
8923
|
-
}
|
|
8924
|
-
const signalStopLossPrice = Number(
|
|
8925
|
-
position.signal?.prices?.stopLossPrice ?? Number.NaN
|
|
8926
|
-
);
|
|
8927
|
-
return Number.isFinite(signalStopLossPrice) ? signalStopLossPrice : null;
|
|
8928
|
-
};
|
|
8929
|
-
getPositionRiskPct = ({
|
|
8930
|
-
direction,
|
|
8931
|
-
entryPrice,
|
|
8932
|
-
stopLossPrice
|
|
8933
|
-
}) => {
|
|
8934
|
-
if (stopLossPrice == null || !Number.isFinite(entryPrice) || !Number.isFinite(stopLossPrice) || entryPrice <= 0) {
|
|
8935
|
-
return null;
|
|
8936
|
-
}
|
|
8937
|
-
return direction === "LONG" ? (entryPrice - stopLossPrice) / entryPrice * 100 : (stopLossPrice - entryPrice) / entryPrice * 100;
|
|
8938
|
-
};
|
|
8939
|
-
isBreakEvenStopAlreadyApplied = ({
|
|
8940
|
-
direction,
|
|
8941
|
-
entryPrice,
|
|
8942
|
-
stopLossPrice
|
|
8943
|
-
}) => {
|
|
8944
|
-
if (stopLossPrice == null || !Number.isFinite(entryPrice) || !Number.isFinite(stopLossPrice)) {
|
|
8945
|
-
return false;
|
|
8946
|
-
}
|
|
8947
|
-
return direction === "LONG" ? stopLossPrice >= entryPrice : stopLossPrice <= entryPrice;
|
|
8948
|
-
};
|
|
8949
9346
|
isFailedBreakout = ({
|
|
8950
9347
|
direction,
|
|
8951
9348
|
priceVsLinePct
|
|
@@ -8955,8 +9352,8 @@ var init_core2 = __esm({
|
|
|
8955
9352
|
}
|
|
8956
9353
|
return direction === "LONG" ? priceVsLinePct < 0 : priceVsLinePct > 0;
|
|
8957
9354
|
};
|
|
8958
|
-
createTrendLineCore = async ({ config:
|
|
8959
|
-
const { TRENDLINE, FEE_PERCENT, MAX_LOSS_VALUE, HIGHS, LOWS } =
|
|
9355
|
+
createTrendLineCore = async ({ config: config8, data: cachedData, strategyApi, indicatorsState }) => {
|
|
9356
|
+
const { TRENDLINE, FEE_PERCENT, MAX_LOSS_VALUE, HIGHS, LOWS } = config8;
|
|
8960
9357
|
const lastTradeController = strategyApi.createLastTradeController();
|
|
8961
9358
|
const trendlineOptions = {
|
|
8962
9359
|
bestLines: 1,
|
|
@@ -8980,17 +9377,6 @@ var init_core2 = __esm({
|
|
|
8980
9377
|
const { currentPrice: currentPrice2 } = await strategyApi.getMarketData();
|
|
8981
9378
|
const activeLine = currentPosition.direction === "LONG" ? highsTrendlines[0] : lowsTrendlines[0];
|
|
8982
9379
|
const activeModeConfig = currentPosition.direction === "LONG" ? HIGHS : LOWS;
|
|
8983
|
-
const currentStopLossPrice = getPositionStopLossPrice(currentPosition);
|
|
8984
|
-
const favorableMovePct = getFavorableMovePct({
|
|
8985
|
-
direction: currentPosition.direction,
|
|
8986
|
-
entryPrice: currentPosition.price,
|
|
8987
|
-
currentPrice: currentPrice2
|
|
8988
|
-
});
|
|
8989
|
-
const currentPositionRiskPct = getPositionRiskPct({
|
|
8990
|
-
direction: currentPosition.direction,
|
|
8991
|
-
entryPrice: currentPosition.price,
|
|
8992
|
-
stopLossPrice: currentStopLossPrice
|
|
8993
|
-
});
|
|
8994
9380
|
if (activeLine) {
|
|
8995
9381
|
const indicators2 = indicatorsState.snapshot();
|
|
8996
9382
|
const manageSignalSeed = buildTrendlineSignalSeed({
|
|
@@ -9010,19 +9396,6 @@ var init_core2 = __esm({
|
|
|
9010
9396
|
});
|
|
9011
9397
|
}
|
|
9012
9398
|
}
|
|
9013
|
-
if (!isBreakEvenStopAlreadyApplied({
|
|
9014
|
-
direction: currentPosition.direction,
|
|
9015
|
-
entryPrice: currentPosition.price,
|
|
9016
|
-
stopLossPrice: currentStopLossPrice
|
|
9017
|
-
}) && favorableMovePct != null && favorableMovePct >= (currentPositionRiskPct ?? activeModeConfig.SL) * BREAK_EVEN_TRIGGER_RISK_MULTIPLIER) {
|
|
9018
|
-
return strategyApi.protect({
|
|
9019
|
-
code: "TRENDLINE_MOVE_STOP_TO_BREAK_EVEN",
|
|
9020
|
-
protectPlan: {
|
|
9021
|
-
direction: currentPosition.direction,
|
|
9022
|
-
stopLossPrice: currentPosition.price
|
|
9023
|
-
}
|
|
9024
|
-
});
|
|
9025
|
-
}
|
|
9026
9399
|
return strategyApi.skip("POSITION_EXISTS");
|
|
9027
9400
|
}
|
|
9028
9401
|
const bestLine = lowsTrendlines.length > 0 ? lowsTrendlines[0] : highsTrendlines[0];
|
|
@@ -9115,17 +9488,17 @@ var strategy_exports2 = {};
|
|
|
9115
9488
|
__export(strategy_exports2, {
|
|
9116
9489
|
TrendlineStrategyCreator: () => TrendlineStrategyCreator
|
|
9117
9490
|
});
|
|
9118
|
-
var
|
|
9491
|
+
var import_strategies13, TrendlineStrategyCreator;
|
|
9119
9492
|
var init_strategy2 = __esm({
|
|
9120
9493
|
"src/TrendLine/strategy.ts"() {
|
|
9121
9494
|
"use strict";
|
|
9122
|
-
|
|
9123
|
-
|
|
9495
|
+
import_strategies13 = require("@tradejs/node/strategies");
|
|
9496
|
+
init_config6();
|
|
9124
9497
|
init_core2();
|
|
9125
|
-
|
|
9126
|
-
TrendlineStrategyCreator = (0,
|
|
9498
|
+
init_manifest6();
|
|
9499
|
+
TrendlineStrategyCreator = (0, import_strategies13.createStrategyRuntime)({
|
|
9127
9500
|
strategyName: "TrendLine",
|
|
9128
|
-
defaults:
|
|
9501
|
+
defaults: config6,
|
|
9129
9502
|
createCore: createTrendLineCore,
|
|
9130
9503
|
manifest: trendLineManifest,
|
|
9131
9504
|
strategyDirectory: __dirname
|
|
@@ -9133,12 +9506,406 @@ var init_strategy2 = __esm({
|
|
|
9133
9506
|
}
|
|
9134
9507
|
});
|
|
9135
9508
|
|
|
9509
|
+
// src/TrendShift/engine.ts
|
|
9510
|
+
var clampPositive, calculateTrueRange, updateAtrState, trimSeries, asDirection, buildTrendShiftSignalContext, getConfigNumbers, createTrendShiftEngine;
|
|
9511
|
+
var init_engine = __esm({
|
|
9512
|
+
"src/TrendShift/engine.ts"() {
|
|
9513
|
+
"use strict";
|
|
9514
|
+
clampPositive = (value, fallback) => Number.isFinite(value) && value > 0 ? value : fallback;
|
|
9515
|
+
calculateTrueRange = (candle, prevClose) => {
|
|
9516
|
+
const high = Number(candle.high);
|
|
9517
|
+
const low = Number(candle.low);
|
|
9518
|
+
const close = Number(candle.close);
|
|
9519
|
+
if (!Number.isFinite(high) || !Number.isFinite(low) || !Number.isFinite(close)) {
|
|
9520
|
+
return 0;
|
|
9521
|
+
}
|
|
9522
|
+
if (prevClose == null || !Number.isFinite(prevClose)) {
|
|
9523
|
+
return Math.max(high - low, 0);
|
|
9524
|
+
}
|
|
9525
|
+
return Math.max(
|
|
9526
|
+
high - low,
|
|
9527
|
+
Math.abs(high - prevClose),
|
|
9528
|
+
Math.abs(low - prevClose)
|
|
9529
|
+
);
|
|
9530
|
+
};
|
|
9531
|
+
updateAtrState = ({
|
|
9532
|
+
atrState,
|
|
9533
|
+
tr,
|
|
9534
|
+
period
|
|
9535
|
+
}) => {
|
|
9536
|
+
const safeTr = Number.isFinite(tr) ? Math.max(tr, 0) : 0;
|
|
9537
|
+
const safePeriod = Math.max(1, Math.floor(period));
|
|
9538
|
+
if (atrState.value == null) {
|
|
9539
|
+
return {
|
|
9540
|
+
value: safeTr,
|
|
9541
|
+
count: 1
|
|
9542
|
+
};
|
|
9543
|
+
}
|
|
9544
|
+
if (atrState.count < safePeriod) {
|
|
9545
|
+
const nextCount = atrState.count + 1;
|
|
9546
|
+
return {
|
|
9547
|
+
value: (atrState.value * atrState.count + safeTr) / nextCount,
|
|
9548
|
+
count: nextCount
|
|
9549
|
+
};
|
|
9550
|
+
}
|
|
9551
|
+
return {
|
|
9552
|
+
value: (atrState.value * (safePeriod - 1) + safeTr) / safePeriod,
|
|
9553
|
+
count: atrState.count + 1
|
|
9554
|
+
};
|
|
9555
|
+
};
|
|
9556
|
+
trimSeries = (series, maxPoints) => series.length <= maxPoints ? series : series.slice(series.length - maxPoints);
|
|
9557
|
+
asDirection = (trendState) => trendState === 1 ? "LONG" : "SHORT";
|
|
9558
|
+
buildTrendShiftSignalContext = ({
|
|
9559
|
+
snapshot,
|
|
9560
|
+
indicators
|
|
9561
|
+
}) => {
|
|
9562
|
+
const maFastSeries = Array.isArray(indicators?.maFast) ? indicators?.maFast : [];
|
|
9563
|
+
const maSlowSeries = Array.isArray(indicators?.maSlow) ? indicators?.maSlow : [];
|
|
9564
|
+
const maFast = maFastSeries[maFastSeries.length - 1];
|
|
9565
|
+
const maSlow = maSlowSeries[maSlowSeries.length - 1];
|
|
9566
|
+
const coinBias = Number.isFinite(maFast) && Number.isFinite(maSlow) ? maFast > maSlow ? "bullish" : maFast < maSlow ? "bearish" : "neutral" : "unknown";
|
|
9567
|
+
const signalDirection = snapshot.bullFlip ? "LONG" : snapshot.bearFlip ? "SHORT" : asDirection(snapshot.trendState);
|
|
9568
|
+
const coinBiasAligned = coinBias === "unknown" || coinBias === "neutral" ? null : signalDirection === "LONG" ? coinBias === "bullish" : coinBias === "bearish";
|
|
9569
|
+
return {
|
|
9570
|
+
signalDirection,
|
|
9571
|
+
trendState: snapshot.trendState,
|
|
9572
|
+
rawTrend: snapshot.rawTrend,
|
|
9573
|
+
confirmedFlip: snapshot.bullFlip || snapshot.bearFlip,
|
|
9574
|
+
bullFlip: snapshot.bullFlip,
|
|
9575
|
+
bearFlip: snapshot.bearFlip,
|
|
9576
|
+
bullFlipRaw: snapshot.bullFlipRaw,
|
|
9577
|
+
bearFlipRaw: snapshot.bearFlipRaw,
|
|
9578
|
+
flipDistanceOk: snapshot.flipDistanceOk,
|
|
9579
|
+
closeVsAvgPct: snapshot.closeVsAvgPct,
|
|
9580
|
+
bandWidthPct: snapshot.bandWidthPct,
|
|
9581
|
+
avgSlopePct: snapshot.avgSlopePct,
|
|
9582
|
+
distanceAtrRatio: snapshot.distanceAtrRatio,
|
|
9583
|
+
adaptiveAtr: snapshot.adaptiveAtr,
|
|
9584
|
+
avg: snapshot.avg,
|
|
9585
|
+
upper: snapshot.upper,
|
|
9586
|
+
lower: snapshot.lower,
|
|
9587
|
+
hold: snapshot.hold,
|
|
9588
|
+
currentPrice: snapshot.close,
|
|
9589
|
+
coinMaFast: Number.isFinite(maFast) ? maFast : null,
|
|
9590
|
+
coinMaSlow: Number.isFinite(maSlow) ? maSlow : null,
|
|
9591
|
+
coinBias,
|
|
9592
|
+
coinBiasAligned
|
|
9593
|
+
};
|
|
9594
|
+
};
|
|
9595
|
+
getConfigNumbers = (config8) => ({
|
|
9596
|
+
mult: clampPositive(config8.TRENDSHIFT_MULTIPLICATIVE_FACTOR, 4),
|
|
9597
|
+
slope: clampPositive(config8.TRENDSHIFT_SLOPE, 12),
|
|
9598
|
+
atrLength: Math.max(1, Math.floor(config8.TRENDSHIFT_ATR_LENGTH ?? 150)),
|
|
9599
|
+
widthPct: clampPositive(config8.TRENDSHIFT_WIDTH_PCT, 75) / 100,
|
|
9600
|
+
minFlipAtr: Math.max(0, Number(config8.TRENDSHIFT_MIN_FLIP_DISTANCE_ATR ?? 0)),
|
|
9601
|
+
confirmFlipWithClose: Boolean(config8.TRENDSHIFT_CONFIRM_FLIP_WITH_CLOSE),
|
|
9602
|
+
maxFigurePoints: Math.max(
|
|
9603
|
+
20,
|
|
9604
|
+
Math.floor(config8.TRENDSHIFT_MAX_FIGURE_POINTS ?? 180)
|
|
9605
|
+
)
|
|
9606
|
+
});
|
|
9607
|
+
createTrendShiftEngine = ({
|
|
9608
|
+
config: config8,
|
|
9609
|
+
initialCandles = []
|
|
9610
|
+
}) => {
|
|
9611
|
+
const {
|
|
9612
|
+
mult,
|
|
9613
|
+
slope,
|
|
9614
|
+
atrLength,
|
|
9615
|
+
widthPct,
|
|
9616
|
+
minFlipAtr,
|
|
9617
|
+
confirmFlipWithClose,
|
|
9618
|
+
maxFigurePoints
|
|
9619
|
+
} = getConfigNumbers(config8);
|
|
9620
|
+
const state = {
|
|
9621
|
+
atrState: {
|
|
9622
|
+
value: null,
|
|
9623
|
+
count: 0
|
|
9624
|
+
},
|
|
9625
|
+
avg: null,
|
|
9626
|
+
hold: null,
|
|
9627
|
+
rawTrend: 1,
|
|
9628
|
+
trendState: 1,
|
|
9629
|
+
prevClose: null,
|
|
9630
|
+
series: {
|
|
9631
|
+
avg: [],
|
|
9632
|
+
upper: [],
|
|
9633
|
+
lower: []
|
|
9634
|
+
},
|
|
9635
|
+
snapshot: null
|
|
9636
|
+
};
|
|
9637
|
+
const apply = (candle) => {
|
|
9638
|
+
const close = Number(candle.close);
|
|
9639
|
+
if (!Number.isFinite(close)) {
|
|
9640
|
+
return {
|
|
9641
|
+
snapshot: state.snapshot,
|
|
9642
|
+
series: state.series
|
|
9643
|
+
};
|
|
9644
|
+
}
|
|
9645
|
+
const tr = calculateTrueRange(candle, state.prevClose);
|
|
9646
|
+
state.atrState = updateAtrState({
|
|
9647
|
+
atrState: state.atrState,
|
|
9648
|
+
tr,
|
|
9649
|
+
period: atrLength
|
|
9650
|
+
});
|
|
9651
|
+
const adaptiveAtr = Math.max((state.atrState.value ?? tr) * mult, 1e-9);
|
|
9652
|
+
const prevAvg = state.avg ?? close;
|
|
9653
|
+
const prevHold = state.hold ?? adaptiveAtr;
|
|
9654
|
+
const prevRawTrend = state.rawTrend;
|
|
9655
|
+
const prevTrendState = state.trendState;
|
|
9656
|
+
const avg = Math.abs(close - prevAvg) > adaptiveAtr ? (close + prevAvg) / 2 : prevAvg + prevRawTrend * (prevHold / mult / Math.max(slope, 1));
|
|
9657
|
+
const rawTrend = avg > prevAvg ? 1 : avg < prevAvg ? -1 : prevRawTrend;
|
|
9658
|
+
const hold = rawTrend !== prevRawTrend ? adaptiveAtr : prevHold + (adaptiveAtr - prevHold) / Math.max(slope, 1);
|
|
9659
|
+
const upper = avg + widthPct * hold;
|
|
9660
|
+
const lower = avg - widthPct * hold;
|
|
9661
|
+
const closeDistance = Math.abs(close - avg);
|
|
9662
|
+
const flipDistanceOk = closeDistance >= adaptiveAtr * minFlipAtr;
|
|
9663
|
+
const bullFlipRaw = rawTrend === 1 && prevTrendState !== 1;
|
|
9664
|
+
const bearFlipRaw = rawTrend === -1 && prevTrendState !== -1;
|
|
9665
|
+
const bullFlip = bullFlipRaw && flipDistanceOk && (!confirmFlipWithClose || close > avg);
|
|
9666
|
+
const bearFlip = bearFlipRaw && flipDistanceOk && (!confirmFlipWithClose || close < avg);
|
|
9667
|
+
const trendState = bullFlip ? 1 : bearFlip ? -1 : prevTrendState;
|
|
9668
|
+
const closeVsAvgPct = avg !== 0 ? (close - avg) / avg * 100 : 0;
|
|
9669
|
+
const bandWidthPct = avg !== 0 ? (upper - lower) / avg * 100 : 0;
|
|
9670
|
+
const avgSlopePct = prevAvg !== 0 ? (avg - prevAvg) / prevAvg * 100 : 0;
|
|
9671
|
+
const distanceAtrRatio = adaptiveAtr > 0 ? closeDistance / adaptiveAtr : 0;
|
|
9672
|
+
const labelOffset = hold * 0.2;
|
|
9673
|
+
state.avg = avg;
|
|
9674
|
+
state.hold = hold;
|
|
9675
|
+
state.rawTrend = rawTrend;
|
|
9676
|
+
state.trendState = trendState;
|
|
9677
|
+
state.prevClose = close;
|
|
9678
|
+
state.series.avg = trimSeries(
|
|
9679
|
+
[...state.series.avg, { timestamp: candle.timestamp, value: avg }],
|
|
9680
|
+
maxFigurePoints
|
|
9681
|
+
);
|
|
9682
|
+
state.series.upper = trimSeries(
|
|
9683
|
+
[...state.series.upper, { timestamp: candle.timestamp, value: upper }],
|
|
9684
|
+
maxFigurePoints
|
|
9685
|
+
);
|
|
9686
|
+
state.series.lower = trimSeries(
|
|
9687
|
+
[...state.series.lower, { timestamp: candle.timestamp, value: lower }],
|
|
9688
|
+
maxFigurePoints
|
|
9689
|
+
);
|
|
9690
|
+
state.snapshot = {
|
|
9691
|
+
avg,
|
|
9692
|
+
upper,
|
|
9693
|
+
lower,
|
|
9694
|
+
hold,
|
|
9695
|
+
adaptiveAtr,
|
|
9696
|
+
rawTrend,
|
|
9697
|
+
trendState,
|
|
9698
|
+
bullFlipRaw,
|
|
9699
|
+
bearFlipRaw,
|
|
9700
|
+
bullFlip,
|
|
9701
|
+
bearFlip,
|
|
9702
|
+
flipDistanceOk,
|
|
9703
|
+
closeVsAvgPct,
|
|
9704
|
+
bandWidthPct,
|
|
9705
|
+
avgSlopePct,
|
|
9706
|
+
distanceAtrRatio,
|
|
9707
|
+
labelOffset,
|
|
9708
|
+
timestamp: candle.timestamp,
|
|
9709
|
+
close
|
|
9710
|
+
};
|
|
9711
|
+
return {
|
|
9712
|
+
snapshot: state.snapshot,
|
|
9713
|
+
series: state.series
|
|
9714
|
+
};
|
|
9715
|
+
};
|
|
9716
|
+
for (const candle of initialCandles) {
|
|
9717
|
+
apply(candle);
|
|
9718
|
+
}
|
|
9719
|
+
return {
|
|
9720
|
+
next: apply,
|
|
9721
|
+
getState: () => ({
|
|
9722
|
+
snapshot: state.snapshot,
|
|
9723
|
+
series: state.series
|
|
9724
|
+
})
|
|
9725
|
+
};
|
|
9726
|
+
};
|
|
9727
|
+
}
|
|
9728
|
+
});
|
|
9729
|
+
|
|
9730
|
+
// src/TrendShift/figures.ts
|
|
9731
|
+
var buildTrendShiftFigures;
|
|
9732
|
+
var init_figures3 = __esm({
|
|
9733
|
+
"src/TrendShift/figures.ts"() {
|
|
9734
|
+
"use strict";
|
|
9735
|
+
buildTrendShiftFigures = ({
|
|
9736
|
+
series,
|
|
9737
|
+
direction,
|
|
9738
|
+
entryTimestamp,
|
|
9739
|
+
entryPrice
|
|
9740
|
+
}) => {
|
|
9741
|
+
const trendColor = direction === "LONG" ? "#00b894" : "#d63031";
|
|
9742
|
+
const lines = [
|
|
9743
|
+
{
|
|
9744
|
+
id: "trendshift-upper",
|
|
9745
|
+
kind: "trendshift_upper",
|
|
9746
|
+
points: series.upper,
|
|
9747
|
+
color: trendColor,
|
|
9748
|
+
width: 1,
|
|
9749
|
+
style: "dashed"
|
|
9750
|
+
},
|
|
9751
|
+
{
|
|
9752
|
+
id: "trendshift-avg",
|
|
9753
|
+
kind: "trendshift_avg",
|
|
9754
|
+
points: series.avg,
|
|
9755
|
+
color: trendColor,
|
|
9756
|
+
width: 2,
|
|
9757
|
+
style: "solid"
|
|
9758
|
+
},
|
|
9759
|
+
{
|
|
9760
|
+
id: "trendshift-lower",
|
|
9761
|
+
kind: "trendshift_lower",
|
|
9762
|
+
points: series.lower,
|
|
9763
|
+
color: trendColor,
|
|
9764
|
+
width: 1,
|
|
9765
|
+
style: "dashed"
|
|
9766
|
+
}
|
|
9767
|
+
].filter((line) => Array.isArray(line.points) && line.points.length > 0);
|
|
9768
|
+
const points = [
|
|
9769
|
+
{
|
|
9770
|
+
id: `trendshift-entry-${entryTimestamp}`,
|
|
9771
|
+
kind: "trendshift_entry",
|
|
9772
|
+
points: [{ timestamp: entryTimestamp, value: entryPrice }],
|
|
9773
|
+
color: trendColor,
|
|
9774
|
+
radius: 4
|
|
9775
|
+
}
|
|
9776
|
+
];
|
|
9777
|
+
return { lines, points };
|
|
9778
|
+
};
|
|
9779
|
+
}
|
|
9780
|
+
});
|
|
9781
|
+
|
|
9782
|
+
// src/TrendShift/core.ts
|
|
9783
|
+
var import_math4, isOpenPosition2, createTrendShiftCore;
|
|
9784
|
+
var init_core3 = __esm({
|
|
9785
|
+
"src/TrendShift/core.ts"() {
|
|
9786
|
+
"use strict";
|
|
9787
|
+
import_math4 = require("@tradejs/core/math");
|
|
9788
|
+
init_engine();
|
|
9789
|
+
init_figures3();
|
|
9790
|
+
isOpenPosition2 = (position) => Boolean(
|
|
9791
|
+
position && typeof position.price === "number" && Number.isFinite(position.price) && typeof position.qty === "number" && Number.isFinite(position.qty) && position.qty > 0 && (position.direction === "LONG" || position.direction === "SHORT")
|
|
9792
|
+
);
|
|
9793
|
+
createTrendShiftCore = async ({ config: config8, data: initialData, strategyApi, indicatorsState }) => {
|
|
9794
|
+
const engine = createTrendShiftEngine({
|
|
9795
|
+
config: config8,
|
|
9796
|
+
initialCandles: initialData
|
|
9797
|
+
});
|
|
9798
|
+
const lastTradeController = strategyApi.createLastTradeController();
|
|
9799
|
+
return async (candle) => {
|
|
9800
|
+
indicatorsState.onBar();
|
|
9801
|
+
const runtimeState = engine.next(candle);
|
|
9802
|
+
const snapshot = runtimeState.snapshot;
|
|
9803
|
+
if (!snapshot) {
|
|
9804
|
+
return strategyApi.skip("WAIT_DATA");
|
|
9805
|
+
}
|
|
9806
|
+
const position = await strategyApi.getCurrentPosition();
|
|
9807
|
+
if (isOpenPosition2(position)) {
|
|
9808
|
+
const oppositeBullExit = position.direction === "SHORT" && snapshot.bullFlip;
|
|
9809
|
+
const oppositeBearExit = position.direction === "LONG" && snapshot.bearFlip;
|
|
9810
|
+
if (Boolean(config8.TRENDSHIFT_EXIT_ON_OPPOSITE_FLIP) && (oppositeBullExit || oppositeBearExit)) {
|
|
9811
|
+
return strategyApi.exit({
|
|
9812
|
+
code: "TRENDSHIFT_OPPOSITE_FLIP_EXIT",
|
|
9813
|
+
direction: position.direction
|
|
9814
|
+
});
|
|
9815
|
+
}
|
|
9816
|
+
return strategyApi.skip("POSITION_EXISTS");
|
|
9817
|
+
}
|
|
9818
|
+
if (lastTradeController.isInCooldown(candle.timestamp)) {
|
|
9819
|
+
return strategyApi.skip("DEV_TRADE_COOLDOWN");
|
|
9820
|
+
}
|
|
9821
|
+
const isBullEntry = snapshot.bullFlip;
|
|
9822
|
+
const isBearEntry = snapshot.bearFlip;
|
|
9823
|
+
if (!isBullEntry && !isBearEntry) {
|
|
9824
|
+
return strategyApi.skip("NO_SIGNAL");
|
|
9825
|
+
}
|
|
9826
|
+
const modeConfig = isBullEntry ? config8.LONG : config8.SHORT;
|
|
9827
|
+
if (!modeConfig.enable) {
|
|
9828
|
+
return strategyApi.skip("STRATEGY_DISABLED");
|
|
9829
|
+
}
|
|
9830
|
+
const indicators = indicatorsState.snapshot();
|
|
9831
|
+
const { timestamp, currentPrice } = await strategyApi.getMarketData();
|
|
9832
|
+
const direction = modeConfig.direction;
|
|
9833
|
+
const signalContext = buildTrendShiftSignalContext({
|
|
9834
|
+
snapshot: {
|
|
9835
|
+
...snapshot,
|
|
9836
|
+
close: currentPrice
|
|
9837
|
+
},
|
|
9838
|
+
indicators
|
|
9839
|
+
});
|
|
9840
|
+
const { stopLossPrice, takeProfitPrice, riskRatio, qty } = strategyApi.getDirectionalTpSlPrices({
|
|
9841
|
+
price: currentPrice,
|
|
9842
|
+
direction,
|
|
9843
|
+
takeProfitDelta: modeConfig.TP,
|
|
9844
|
+
stopLossDelta: modeConfig.SL,
|
|
9845
|
+
unit: "percent",
|
|
9846
|
+
maxLossValue: config8.MAX_LOSS_VALUE,
|
|
9847
|
+
feePercent: Number(config8.FEE_PERCENT ?? 0)
|
|
9848
|
+
});
|
|
9849
|
+
if (!qty || !Number.isFinite(qty) || qty <= 0) {
|
|
9850
|
+
return strategyApi.skip("INVALID_QTY");
|
|
9851
|
+
}
|
|
9852
|
+
if (riskRatio <= modeConfig.minRiskRatio) {
|
|
9853
|
+
return strategyApi.skip(`RISK_RATIO:${(0, import_math4.round)(riskRatio)}`);
|
|
9854
|
+
}
|
|
9855
|
+
lastTradeController.markTrade(timestamp);
|
|
9856
|
+
return strategyApi.entry({
|
|
9857
|
+
code: isBullEntry ? "TRENDSHIFT_BULLISH_FLIP" : "TRENDSHIFT_BEARISH_FLIP",
|
|
9858
|
+
direction,
|
|
9859
|
+
indicators,
|
|
9860
|
+
additionalIndicators: {
|
|
9861
|
+
trendShiftContext: signalContext
|
|
9862
|
+
},
|
|
9863
|
+
figures: buildTrendShiftFigures({
|
|
9864
|
+
series: runtimeState.series,
|
|
9865
|
+
direction,
|
|
9866
|
+
entryTimestamp: timestamp,
|
|
9867
|
+
entryPrice: currentPrice
|
|
9868
|
+
}),
|
|
9869
|
+
orderPlan: {
|
|
9870
|
+
qty,
|
|
9871
|
+
stopLossPrice,
|
|
9872
|
+
takeProfits: [{ rate: 1, price: takeProfitPrice }]
|
|
9873
|
+
}
|
|
9874
|
+
});
|
|
9875
|
+
};
|
|
9876
|
+
};
|
|
9877
|
+
}
|
|
9878
|
+
});
|
|
9879
|
+
|
|
9880
|
+
// src/TrendShift/strategy.ts
|
|
9881
|
+
var strategy_exports3 = {};
|
|
9882
|
+
__export(strategy_exports3, {
|
|
9883
|
+
TrendShiftStrategyCreator: () => TrendShiftStrategyCreator
|
|
9884
|
+
});
|
|
9885
|
+
var import_strategies14, TrendShiftStrategyCreator;
|
|
9886
|
+
var init_strategy3 = __esm({
|
|
9887
|
+
"src/TrendShift/strategy.ts"() {
|
|
9888
|
+
"use strict";
|
|
9889
|
+
import_strategies14 = require("@tradejs/node/strategies");
|
|
9890
|
+
init_config5();
|
|
9891
|
+
init_core3();
|
|
9892
|
+
init_manifest5();
|
|
9893
|
+
TrendShiftStrategyCreator = (0, import_strategies14.createStrategyRuntime)({
|
|
9894
|
+
strategyName: "TrendShift",
|
|
9895
|
+
defaults: config5,
|
|
9896
|
+
createCore: createTrendShiftCore,
|
|
9897
|
+
manifest: trendShiftManifest,
|
|
9898
|
+
strategyDirectory: __dirname
|
|
9899
|
+
});
|
|
9900
|
+
}
|
|
9901
|
+
});
|
|
9902
|
+
|
|
9136
9903
|
// src/ReverseTrendLine/filters.ts
|
|
9137
|
-
var
|
|
9904
|
+
var import_math5, MAX_CANDLE_VOLATILITY2, filterByVeryVolatility2;
|
|
9138
9905
|
var init_filters2 = __esm({
|
|
9139
9906
|
"src/ReverseTrendLine/filters.ts"() {
|
|
9140
9907
|
"use strict";
|
|
9141
|
-
|
|
9908
|
+
import_math5 = require("@tradejs/core/math");
|
|
9142
9909
|
MAX_CANDLE_VOLATILITY2 = 0.025;
|
|
9143
9910
|
filterByVeryVolatility2 = (data) => {
|
|
9144
9911
|
const lastCandle = data[data.length - 1];
|
|
@@ -9146,7 +9913,7 @@ var init_filters2 = __esm({
|
|
|
9146
9913
|
if (!lastCandle || !prevCandle) {
|
|
9147
9914
|
return false;
|
|
9148
9915
|
}
|
|
9149
|
-
const isVeryVolatility = (0,
|
|
9916
|
+
const isVeryVolatility = (0, import_math5.diffRel)(lastCandle.low, lastCandle.high) > MAX_CANDLE_VOLATILITY2 || (0, import_math5.diffRel)(prevCandle.low, prevCandle.high) > MAX_CANDLE_VOLATILITY2;
|
|
9150
9917
|
return !isVeryVolatility;
|
|
9151
9918
|
};
|
|
9152
9919
|
}
|
|
@@ -9154,7 +9921,7 @@ var init_filters2 = __esm({
|
|
|
9154
9921
|
|
|
9155
9922
|
// src/ReverseTrendLine/figures.ts
|
|
9156
9923
|
var buildReverseTrendLineFigures;
|
|
9157
|
-
var
|
|
9924
|
+
var init_figures4 = __esm({
|
|
9158
9925
|
"src/ReverseTrendLine/figures.ts"() {
|
|
9159
9926
|
"use strict";
|
|
9160
9927
|
buildReverseTrendLineFigures = (bestLine) => ({
|
|
@@ -9186,11 +9953,11 @@ var init_figures3 = __esm({
|
|
|
9186
9953
|
});
|
|
9187
9954
|
|
|
9188
9955
|
// src/ReverseTrendLine/risk.ts
|
|
9189
|
-
var
|
|
9956
|
+
var import_math6, MIN_STOP_BUFFER_PCT2, LINE_BUFFER_ATR_FACTOR2, LINE_BUFFER_BASE_SL_FACTOR2, ATR_STOP_FLOOR_FACTOR2, MIN_STOP_LOSS_FACTOR2, MAX_STOP_LOSS_FACTOR2, clampNumber2, getTimingStopFactor2, getTimingTargetRiskRatio2, buildReverseTrendlineRiskPlan;
|
|
9190
9957
|
var init_risk2 = __esm({
|
|
9191
9958
|
"src/ReverseTrendLine/risk.ts"() {
|
|
9192
9959
|
"use strict";
|
|
9193
|
-
|
|
9960
|
+
import_math6 = require("@tradejs/core/math");
|
|
9194
9961
|
MIN_STOP_BUFFER_PCT2 = 0.1;
|
|
9195
9962
|
LINE_BUFFER_ATR_FACTOR2 = 0.25;
|
|
9196
9963
|
LINE_BUFFER_BASE_SL_FACTOR2 = 0.1;
|
|
@@ -9279,26 +10046,25 @@ var init_risk2 = __esm({
|
|
|
9279
10046
|
maxTargetRiskRatio
|
|
9280
10047
|
);
|
|
9281
10048
|
return {
|
|
9282
|
-
stopLossDelta: (0,
|
|
9283
|
-
targetRiskRatio: (0,
|
|
9284
|
-
takeProfitDelta: (0,
|
|
10049
|
+
stopLossDelta: (0, import_math6.round)(stopLossDelta, 3),
|
|
10050
|
+
targetRiskRatio: (0, import_math6.round)(targetRiskRatio, 2),
|
|
10051
|
+
takeProfitDelta: (0, import_math6.round)(stopLossDelta * targetRiskRatio, 3)
|
|
9285
10052
|
};
|
|
9286
10053
|
};
|
|
9287
10054
|
}
|
|
9288
10055
|
});
|
|
9289
10056
|
|
|
9290
10057
|
// src/ReverseTrendLine/core.ts
|
|
9291
|
-
var
|
|
9292
|
-
var
|
|
10058
|
+
var import_math7, import_indicators3, buildReverseTrendlineSignalSeed, isOpenPosition3, getLinePriceAtNow, buildReverseTrendlineCandidateContext, pickBestCandidateLine, createReverseTrendLineCore;
|
|
10059
|
+
var init_core4 = __esm({
|
|
9293
10060
|
"src/ReverseTrendLine/core.ts"() {
|
|
9294
10061
|
"use strict";
|
|
9295
|
-
|
|
10062
|
+
import_math7 = require("@tradejs/core/math");
|
|
9296
10063
|
import_indicators3 = require("@tradejs/core/indicators");
|
|
9297
10064
|
init_filters2();
|
|
9298
|
-
|
|
10065
|
+
init_figures4();
|
|
9299
10066
|
init_guardrails();
|
|
9300
10067
|
init_risk2();
|
|
9301
|
-
BREAK_EVEN_TRIGGER_RISK_MULTIPLIER2 = 0.5;
|
|
9302
10068
|
buildReverseTrendlineSignalSeed = ({
|
|
9303
10069
|
direction,
|
|
9304
10070
|
currentPrice,
|
|
@@ -9321,54 +10087,9 @@ var init_core3 = __esm({
|
|
|
9321
10087
|
trendLine: bestLine
|
|
9322
10088
|
}
|
|
9323
10089
|
});
|
|
9324
|
-
|
|
10090
|
+
isOpenPosition3 = (position) => Boolean(
|
|
9325
10091
|
position && typeof position.price === "number" && Number.isFinite(position.price) && typeof position.qty === "number" && Number.isFinite(position.qty) && position.qty > 0 && (position.direction === "LONG" || position.direction === "SHORT")
|
|
9326
10092
|
);
|
|
9327
|
-
getFavorableMovePct2 = ({
|
|
9328
|
-
direction,
|
|
9329
|
-
entryPrice,
|
|
9330
|
-
currentPrice
|
|
9331
|
-
}) => {
|
|
9332
|
-
if (!Number.isFinite(entryPrice) || !Number.isFinite(currentPrice) || entryPrice <= 0) {
|
|
9333
|
-
return null;
|
|
9334
|
-
}
|
|
9335
|
-
return direction === "LONG" ? (currentPrice - entryPrice) / entryPrice * 100 : (entryPrice - currentPrice) / entryPrice * 100;
|
|
9336
|
-
};
|
|
9337
|
-
getPositionStopLossPrice2 = (position) => {
|
|
9338
|
-
if (!position || typeof position !== "object") {
|
|
9339
|
-
return null;
|
|
9340
|
-
}
|
|
9341
|
-
const slPrice = Number(
|
|
9342
|
-
position.slPrice ?? Number.NaN
|
|
9343
|
-
);
|
|
9344
|
-
if (Number.isFinite(slPrice)) {
|
|
9345
|
-
return slPrice;
|
|
9346
|
-
}
|
|
9347
|
-
const signalStopLossPrice = Number(
|
|
9348
|
-
position.signal?.prices?.stopLossPrice ?? Number.NaN
|
|
9349
|
-
);
|
|
9350
|
-
return Number.isFinite(signalStopLossPrice) ? signalStopLossPrice : null;
|
|
9351
|
-
};
|
|
9352
|
-
getPositionRiskPct2 = ({
|
|
9353
|
-
direction,
|
|
9354
|
-
entryPrice,
|
|
9355
|
-
stopLossPrice
|
|
9356
|
-
}) => {
|
|
9357
|
-
if (stopLossPrice == null || !Number.isFinite(entryPrice) || !Number.isFinite(stopLossPrice) || entryPrice <= 0) {
|
|
9358
|
-
return null;
|
|
9359
|
-
}
|
|
9360
|
-
return direction === "LONG" ? (entryPrice - stopLossPrice) / entryPrice * 100 : (stopLossPrice - entryPrice) / entryPrice * 100;
|
|
9361
|
-
};
|
|
9362
|
-
isBreakEvenStopAlreadyApplied2 = ({
|
|
9363
|
-
direction,
|
|
9364
|
-
entryPrice,
|
|
9365
|
-
stopLossPrice
|
|
9366
|
-
}) => {
|
|
9367
|
-
if (stopLossPrice == null || !Number.isFinite(entryPrice) || !Number.isFinite(stopLossPrice)) {
|
|
9368
|
-
return false;
|
|
9369
|
-
}
|
|
9370
|
-
return direction === "LONG" ? stopLossPrice >= entryPrice : stopLossPrice <= entryPrice;
|
|
9371
|
-
};
|
|
9372
10093
|
getLinePriceAtNow = (line, timestamp) => {
|
|
9373
10094
|
if (!line || !Array.isArray(line.points) || line.points.length === 0) {
|
|
9374
10095
|
return null;
|
|
@@ -9428,8 +10149,8 @@ var init_core3 = __esm({
|
|
|
9428
10149
|
});
|
|
9429
10150
|
return ranked[0] ?? null;
|
|
9430
10151
|
};
|
|
9431
|
-
createReverseTrendLineCore = async ({ config:
|
|
9432
|
-
const { TRENDLINE, FEE_PERCENT, MAX_LOSS_VALUE, HIGHS, LOWS } =
|
|
10152
|
+
createReverseTrendLineCore = async ({ config: config8, data: cachedData, strategyApi, indicatorsState }) => {
|
|
10153
|
+
const { TRENDLINE, FEE_PERCENT, MAX_LOSS_VALUE, HIGHS, LOWS } = config8;
|
|
9433
10154
|
const lastTradeController = strategyApi.createLastTradeController();
|
|
9434
10155
|
const trendlineOptions = {
|
|
9435
10156
|
bestLines: 1,
|
|
@@ -9449,9 +10170,8 @@ var init_core3 = __esm({
|
|
|
9449
10170
|
const highsTrendlines = getHighsTrendlines.next(candle);
|
|
9450
10171
|
indicatorsState.onBar();
|
|
9451
10172
|
const currentPosition = await strategyApi.getCurrentPosition();
|
|
9452
|
-
if (
|
|
10173
|
+
if (isOpenPosition3(currentPosition)) {
|
|
9453
10174
|
const activeLine = currentPosition.direction === "LONG" ? lowsTrendlines[0] : highsTrendlines[0];
|
|
9454
|
-
const activeModeConfig = currentPosition.direction === "LONG" ? LOWS : HIGHS;
|
|
9455
10175
|
const activeLinePrice = getLinePriceAtNow(
|
|
9456
10176
|
activeLine ?? null,
|
|
9457
10177
|
candle.timestamp
|
|
@@ -9464,30 +10184,6 @@ var init_core3 = __esm({
|
|
|
9464
10184
|
direction: currentPosition.direction
|
|
9465
10185
|
});
|
|
9466
10186
|
}
|
|
9467
|
-
const favorableMovePct = getFavorableMovePct2({
|
|
9468
|
-
direction: currentPosition.direction,
|
|
9469
|
-
entryPrice: currentPosition.price,
|
|
9470
|
-
currentPrice: candle.close
|
|
9471
|
-
});
|
|
9472
|
-
const currentStopLossPrice = getPositionStopLossPrice2(currentPosition);
|
|
9473
|
-
const currentPositionRiskPct = getPositionRiskPct2({
|
|
9474
|
-
direction: currentPosition.direction,
|
|
9475
|
-
entryPrice: currentPosition.price,
|
|
9476
|
-
stopLossPrice: currentStopLossPrice
|
|
9477
|
-
});
|
|
9478
|
-
if (!isBreakEvenStopAlreadyApplied2({
|
|
9479
|
-
direction: currentPosition.direction,
|
|
9480
|
-
entryPrice: currentPosition.price,
|
|
9481
|
-
stopLossPrice: currentStopLossPrice
|
|
9482
|
-
}) && favorableMovePct != null && favorableMovePct >= (currentPositionRiskPct ?? activeModeConfig.SL) * BREAK_EVEN_TRIGGER_RISK_MULTIPLIER2) {
|
|
9483
|
-
return strategyApi.protect({
|
|
9484
|
-
code: "REVERSE_TRENDLINE_MOVE_STOP_TO_BREAK_EVEN",
|
|
9485
|
-
protectPlan: {
|
|
9486
|
-
direction: currentPosition.direction,
|
|
9487
|
-
stopLossPrice: currentPosition.price
|
|
9488
|
-
}
|
|
9489
|
-
});
|
|
9490
|
-
}
|
|
9491
10187
|
return strategyApi.skip("POSITION_EXISTS");
|
|
9492
10188
|
}
|
|
9493
10189
|
if (lastTradeController.isInCooldown(candle.timestamp)) {
|
|
@@ -9571,7 +10267,7 @@ var init_core3 = __esm({
|
|
|
9571
10267
|
return strategyApi.skip("INVALID_QTY");
|
|
9572
10268
|
}
|
|
9573
10269
|
if (riskRatio <= minRiskRatio) {
|
|
9574
|
-
return strategyApi.skip(`RISK_RATIO:${(0,
|
|
10270
|
+
return strategyApi.skip(`RISK_RATIO:${(0, import_math7.round)(riskRatio)}`);
|
|
9575
10271
|
}
|
|
9576
10272
|
lastTradeController.markTrade(timestamp);
|
|
9577
10273
|
return strategyApi.entry({
|
|
@@ -9607,21 +10303,21 @@ var init_core3 = __esm({
|
|
|
9607
10303
|
});
|
|
9608
10304
|
|
|
9609
10305
|
// src/ReverseTrendLine/strategy.ts
|
|
9610
|
-
var
|
|
9611
|
-
__export(
|
|
10306
|
+
var strategy_exports4 = {};
|
|
10307
|
+
__export(strategy_exports4, {
|
|
9612
10308
|
ReverseTrendLineStrategyCreator: () => ReverseTrendLineStrategyCreator
|
|
9613
10309
|
});
|
|
9614
|
-
var
|
|
9615
|
-
var
|
|
10310
|
+
var import_strategies15, ReverseTrendLineStrategyCreator;
|
|
10311
|
+
var init_strategy4 = __esm({
|
|
9616
10312
|
"src/ReverseTrendLine/strategy.ts"() {
|
|
9617
10313
|
"use strict";
|
|
9618
|
-
|
|
9619
|
-
|
|
9620
|
-
|
|
10314
|
+
import_strategies15 = require("@tradejs/node/strategies");
|
|
10315
|
+
init_config4();
|
|
10316
|
+
init_core4();
|
|
9621
10317
|
init_manifest4();
|
|
9622
|
-
ReverseTrendLineStrategyCreator = (0,
|
|
10318
|
+
ReverseTrendLineStrategyCreator = (0, import_strategies15.createStrategyRuntime)({
|
|
9623
10319
|
strategyName: "ReverseTrendLine",
|
|
9624
|
-
defaults:
|
|
10320
|
+
defaults: config4,
|
|
9625
10321
|
createCore: createReverseTrendLineCore,
|
|
9626
10322
|
manifest: reverseTrendLineManifest,
|
|
9627
10323
|
strategyDirectory: __dirname
|
|
@@ -9629,47 +10325,9 @@ var init_strategy3 = __esm({
|
|
|
9629
10325
|
}
|
|
9630
10326
|
});
|
|
9631
10327
|
|
|
9632
|
-
// src/MaStrategy/config.ts
|
|
9633
|
-
var config4;
|
|
9634
|
-
var init_config4 = __esm({
|
|
9635
|
-
"src/MaStrategy/config.ts"() {
|
|
9636
|
-
"use strict";
|
|
9637
|
-
config4 = {
|
|
9638
|
-
ENV: "BACKTEST",
|
|
9639
|
-
INTERVAL: "15",
|
|
9640
|
-
MAKE_ORDERS: true,
|
|
9641
|
-
CLOSE_OPPOSITE_POSITIONS: false,
|
|
9642
|
-
BACKTEST_PRICE_MODE: "mid",
|
|
9643
|
-
AI_ENABLED: false,
|
|
9644
|
-
ML_ENABLED: false,
|
|
9645
|
-
ML_THRESHOLD: 0.1,
|
|
9646
|
-
MIN_AI_QUALITY: 3,
|
|
9647
|
-
FEE_PERCENT: 5e-3,
|
|
9648
|
-
MAX_LOSS_VALUE: 10,
|
|
9649
|
-
TRADE_COOLDOWN_MS: 0,
|
|
9650
|
-
MA_FAST: 21,
|
|
9651
|
-
MA_SLOW: 55,
|
|
9652
|
-
LONG: {
|
|
9653
|
-
enable: true,
|
|
9654
|
-
direction: "LONG",
|
|
9655
|
-
TP: 2,
|
|
9656
|
-
SL: 1,
|
|
9657
|
-
minRiskRatio: 1.5
|
|
9658
|
-
},
|
|
9659
|
-
SHORT: {
|
|
9660
|
-
enable: true,
|
|
9661
|
-
direction: "SHORT",
|
|
9662
|
-
TP: 2,
|
|
9663
|
-
SL: 1,
|
|
9664
|
-
minRiskRatio: 1.5
|
|
9665
|
-
}
|
|
9666
|
-
};
|
|
9667
|
-
}
|
|
9668
|
-
});
|
|
9669
|
-
|
|
9670
10328
|
// src/MaStrategy/figures.ts
|
|
9671
10329
|
var toLinePoints, buildMaStrategyFigures;
|
|
9672
|
-
var
|
|
10330
|
+
var init_figures5 = __esm({
|
|
9673
10331
|
"src/MaStrategy/figures.ts"() {
|
|
9674
10332
|
"use strict";
|
|
9675
10333
|
toLinePoints = (candles, values, limit = 120) => {
|
|
@@ -9732,12 +10390,12 @@ var init_figures4 = __esm({
|
|
|
9732
10390
|
});
|
|
9733
10391
|
|
|
9734
10392
|
// src/MaStrategy/core.ts
|
|
9735
|
-
var
|
|
9736
|
-
var
|
|
10393
|
+
var import_math8, isFiniteNumber, detectCross, createMaStrategyCore;
|
|
10394
|
+
var init_core5 = __esm({
|
|
9737
10395
|
"src/MaStrategy/core.ts"() {
|
|
9738
10396
|
"use strict";
|
|
9739
|
-
|
|
9740
|
-
|
|
10397
|
+
import_math8 = require("@tradejs/core/math");
|
|
10398
|
+
init_figures5();
|
|
9741
10399
|
isFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value);
|
|
9742
10400
|
detectCross = (maFast, maSlow) => {
|
|
9743
10401
|
if (maFast.length < 2 || maSlow.length < 2) {
|
|
@@ -9770,8 +10428,8 @@ var init_core4 = __esm({
|
|
|
9770
10428
|
}
|
|
9771
10429
|
return null;
|
|
9772
10430
|
};
|
|
9773
|
-
createMaStrategyCore = async ({ config:
|
|
9774
|
-
const { FEE_PERCENT, MAX_LOSS_VALUE, TRADE_COOLDOWN_MS, LONG, SHORT } =
|
|
10431
|
+
createMaStrategyCore = async ({ config: config8, strategyApi, indicatorsState }) => {
|
|
10432
|
+
const { FEE_PERCENT, MAX_LOSS_VALUE, TRADE_COOLDOWN_MS, LONG, SHORT } = config8;
|
|
9775
10433
|
const lastTradeController = strategyApi.createLastTradeController({
|
|
9776
10434
|
enabled: Number(TRADE_COOLDOWN_MS ?? 0) > 0,
|
|
9777
10435
|
cooldownMs: Number(TRADE_COOLDOWN_MS ?? 0)
|
|
@@ -9831,7 +10489,7 @@ var init_core4 = __esm({
|
|
|
9831
10489
|
return strategyApi.skip("INVALID_QTY");
|
|
9832
10490
|
}
|
|
9833
10491
|
if (riskRatio <= modeConfig.minRiskRatio) {
|
|
9834
|
-
return strategyApi.skip(`RISK_RATIO:${(0,
|
|
10492
|
+
return strategyApi.skip(`RISK_RATIO:${(0, import_math8.round)(riskRatio)}`);
|
|
9835
10493
|
}
|
|
9836
10494
|
const correlation = indicatorsState.latestNumber("correlation");
|
|
9837
10495
|
lastTradeController.markTrade(timestamp);
|
|
@@ -9868,21 +10526,21 @@ var init_core4 = __esm({
|
|
|
9868
10526
|
});
|
|
9869
10527
|
|
|
9870
10528
|
// src/MaStrategy/strategy.ts
|
|
9871
|
-
var
|
|
9872
|
-
__export(
|
|
10529
|
+
var strategy_exports5 = {};
|
|
10530
|
+
__export(strategy_exports5, {
|
|
9873
10531
|
MaStrategyCreator: () => MaStrategyCreator
|
|
9874
10532
|
});
|
|
9875
|
-
var
|
|
9876
|
-
var
|
|
10533
|
+
var import_strategies16, MaStrategyCreator;
|
|
10534
|
+
var init_strategy5 = __esm({
|
|
9877
10535
|
"src/MaStrategy/strategy.ts"() {
|
|
9878
10536
|
"use strict";
|
|
9879
|
-
|
|
9880
|
-
|
|
9881
|
-
|
|
10537
|
+
import_strategies16 = require("@tradejs/node/strategies");
|
|
10538
|
+
init_config3();
|
|
10539
|
+
init_core5();
|
|
9882
10540
|
init_manifest3();
|
|
9883
|
-
MaStrategyCreator = (0,
|
|
10541
|
+
MaStrategyCreator = (0, import_strategies16.createStrategyRuntime)({
|
|
9884
10542
|
strategyName: "MaStrategy",
|
|
9885
|
-
defaults:
|
|
10543
|
+
defaults: config3,
|
|
9886
10544
|
createCore: createMaStrategyCore,
|
|
9887
10545
|
manifest: maStrategyManifest,
|
|
9888
10546
|
strategyDirectory: __dirname
|
|
@@ -9890,51 +10548,6 @@ var init_strategy4 = __esm({
|
|
|
9890
10548
|
}
|
|
9891
10549
|
});
|
|
9892
10550
|
|
|
9893
|
-
// src/AdaptiveMomentumRibbon/config.ts
|
|
9894
|
-
var config5;
|
|
9895
|
-
var init_config5 = __esm({
|
|
9896
|
-
"src/AdaptiveMomentumRibbon/config.ts"() {
|
|
9897
|
-
"use strict";
|
|
9898
|
-
config5 = {
|
|
9899
|
-
ENV: "BACKTEST",
|
|
9900
|
-
INTERVAL: "15",
|
|
9901
|
-
MAKE_ORDERS: true,
|
|
9902
|
-
CLOSE_OPPOSITE_POSITIONS: false,
|
|
9903
|
-
BACKTEST_PRICE_MODE: "mid",
|
|
9904
|
-
AI_ENABLED: false,
|
|
9905
|
-
ML_ENABLED: false,
|
|
9906
|
-
ML_THRESHOLD: 0.1,
|
|
9907
|
-
MIN_AI_QUALITY: 3,
|
|
9908
|
-
FEE_PERCENT: 5e-3,
|
|
9909
|
-
MAX_LOSS_VALUE: 10,
|
|
9910
|
-
AMR_LOOKBACK_BARS: 400,
|
|
9911
|
-
AMR_MOMENTUM_PERIOD: 20,
|
|
9912
|
-
AMR_BUTTERWORTH_SMOOTHING: 3,
|
|
9913
|
-
AMR_WAIT_CLOSE: true,
|
|
9914
|
-
AMR_SHOW_INVALIDATION_LEVELS: true,
|
|
9915
|
-
AMR_SHOW_KELTNER_CHANNEL: true,
|
|
9916
|
-
AMR_KC_LENGTH: 20,
|
|
9917
|
-
AMR_KC_MA_TYPE: "EMA",
|
|
9918
|
-
AMR_ATR_LENGTH: 14,
|
|
9919
|
-
AMR_ATR_MULTIPLIER: 2,
|
|
9920
|
-
AMR_EXIT_ON_INVALIDATION: true,
|
|
9921
|
-
AMR_LINE_PLOTS: ["kcMidline", "kcUpper", "kcLower", "invalidationLevel"],
|
|
9922
|
-
LONG: {
|
|
9923
|
-
enable: true,
|
|
9924
|
-
direction: "LONG",
|
|
9925
|
-
TP: 2,
|
|
9926
|
-
SL: 1
|
|
9927
|
-
},
|
|
9928
|
-
SHORT: {
|
|
9929
|
-
enable: true,
|
|
9930
|
-
direction: "SHORT",
|
|
9931
|
-
TP: 2,
|
|
9932
|
-
SL: 1
|
|
9933
|
-
}
|
|
9934
|
-
};
|
|
9935
|
-
}
|
|
9936
|
-
});
|
|
9937
|
-
|
|
9938
10551
|
// ../../node_modules/logform/format.js
|
|
9939
10552
|
var require_format = __commonJS({
|
|
9940
10553
|
"../../node_modules/logform/format.js"(exports2, module2) {
|
|
@@ -10834,9 +11447,9 @@ var require_levels = __commonJS({
|
|
|
10834
11447
|
"../../node_modules/logform/levels.js"(exports2, module2) {
|
|
10835
11448
|
"use strict";
|
|
10836
11449
|
var { Colorizer } = require_colorize();
|
|
10837
|
-
module2.exports = (
|
|
10838
|
-
Colorizer.addColors(
|
|
10839
|
-
return
|
|
11450
|
+
module2.exports = (config8) => {
|
|
11451
|
+
Colorizer.addColors(config8.colors || config8);
|
|
11452
|
+
return config8;
|
|
10840
11453
|
};
|
|
10841
11454
|
}
|
|
10842
11455
|
});
|
|
@@ -20090,7 +20703,7 @@ var require_logger = __commonJS({
|
|
|
20090
20703
|
var LegacyTransportStream = require_legacy();
|
|
20091
20704
|
var Profiler = require_profiler();
|
|
20092
20705
|
var { warn } = require_common();
|
|
20093
|
-
var
|
|
20706
|
+
var config8 = require_config2();
|
|
20094
20707
|
var formatRegExp = /%[scdjifoO%]/g;
|
|
20095
20708
|
var Logger = class extends Transform {
|
|
20096
20709
|
/**
|
|
@@ -20152,7 +20765,7 @@ var require_logger = __commonJS({
|
|
|
20152
20765
|
this.silent = silent;
|
|
20153
20766
|
this.format = format3 || this.format || require_json()();
|
|
20154
20767
|
this.defaultMeta = defaultMeta || null;
|
|
20155
|
-
this.levels = levels || this.levels ||
|
|
20768
|
+
this.levels = levels || this.levels || config8.npm.levels;
|
|
20156
20769
|
this.level = level;
|
|
20157
20770
|
if (this.exceptions) {
|
|
20158
20771
|
this.exceptions.unhandle();
|
|
@@ -20602,14 +21215,14 @@ var require_create_logger = __commonJS({
|
|
|
20602
21215
|
"../../node_modules/winston/lib/winston/create-logger.js"(exports2, module2) {
|
|
20603
21216
|
"use strict";
|
|
20604
21217
|
var { LEVEL } = require_triple_beam();
|
|
20605
|
-
var
|
|
21218
|
+
var config8 = require_config2();
|
|
20606
21219
|
var Logger = require_logger();
|
|
20607
21220
|
var debug = require_node2()("winston:create-logger");
|
|
20608
21221
|
function isLevelEnabledFunctionName(level) {
|
|
20609
21222
|
return "is" + level.charAt(0).toUpperCase() + level.slice(1) + "Enabled";
|
|
20610
21223
|
}
|
|
20611
21224
|
module2.exports = function(opts = {}) {
|
|
20612
|
-
opts.levels = opts.levels ||
|
|
21225
|
+
opts.levels = opts.levels || config8.npm.levels;
|
|
20613
21226
|
class DerivedLogger extends Logger {
|
|
20614
21227
|
/**
|
|
20615
21228
|
* Create a new class derived logger for which the levels can be attached to
|
|
@@ -20881,12 +21494,447 @@ var init_logger = __esm({
|
|
|
20881
21494
|
}
|
|
20882
21495
|
});
|
|
20883
21496
|
|
|
21497
|
+
// src/AdaptiveMomentumRibbon/engine.ts
|
|
21498
|
+
var import_math9, PI, MAX_PLOT_POINTS, toFinite, percentileNearestRank, stdev, pushAndTrim, createMovingAverageState, updateMovingAverage, createAtrState, updateAtr, createButterworthState, updateButterworth, pushPlotPoint, evaluateAdaptiveMomentumRibbon;
|
|
21499
|
+
var init_engine2 = __esm({
|
|
21500
|
+
"src/AdaptiveMomentumRibbon/engine.ts"() {
|
|
21501
|
+
"use strict";
|
|
21502
|
+
import_math9 = require("@tradejs/core/math");
|
|
21503
|
+
PI = 3.14159265359;
|
|
21504
|
+
MAX_PLOT_POINTS = 240;
|
|
21505
|
+
toFinite = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
21506
|
+
percentileNearestRank = (values, percent) => {
|
|
21507
|
+
if (!values.length) {
|
|
21508
|
+
return null;
|
|
21509
|
+
}
|
|
21510
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
21511
|
+
const rank = Math.max(
|
|
21512
|
+
0,
|
|
21513
|
+
Math.min(sorted.length - 1, Math.ceil(percent / 100 * sorted.length) - 1)
|
|
21514
|
+
);
|
|
21515
|
+
return sorted[rank] ?? null;
|
|
21516
|
+
};
|
|
21517
|
+
stdev = (values) => {
|
|
21518
|
+
if (!values.length) {
|
|
21519
|
+
return null;
|
|
21520
|
+
}
|
|
21521
|
+
const mean = values.reduce((sum, value) => sum + value, 0) / values.length;
|
|
21522
|
+
const variance = values.reduce((sum, value) => sum + (value - mean) ** 2, 0) / values.length;
|
|
21523
|
+
return Math.sqrt(variance);
|
|
21524
|
+
};
|
|
21525
|
+
pushAndTrim = (values, value, limit) => {
|
|
21526
|
+
values.push(value);
|
|
21527
|
+
if (values.length > limit) {
|
|
21528
|
+
values.splice(0, values.length - limit);
|
|
21529
|
+
}
|
|
21530
|
+
};
|
|
21531
|
+
createMovingAverageState = (type, length) => {
|
|
21532
|
+
switch (type) {
|
|
21533
|
+
case "SMA":
|
|
21534
|
+
return {
|
|
21535
|
+
type,
|
|
21536
|
+
length,
|
|
21537
|
+
window: [],
|
|
21538
|
+
sum: 0
|
|
21539
|
+
};
|
|
21540
|
+
case "EMA":
|
|
21541
|
+
return {
|
|
21542
|
+
type,
|
|
21543
|
+
length,
|
|
21544
|
+
seedWindow: [],
|
|
21545
|
+
value: null
|
|
21546
|
+
};
|
|
21547
|
+
case "SMMA (RMA)":
|
|
21548
|
+
return {
|
|
21549
|
+
type,
|
|
21550
|
+
length,
|
|
21551
|
+
seedWindow: [],
|
|
21552
|
+
value: null
|
|
21553
|
+
};
|
|
21554
|
+
case "WMA":
|
|
21555
|
+
return {
|
|
21556
|
+
type,
|
|
21557
|
+
length,
|
|
21558
|
+
window: []
|
|
21559
|
+
};
|
|
21560
|
+
case "VWMA":
|
|
21561
|
+
return {
|
|
21562
|
+
type,
|
|
21563
|
+
length,
|
|
21564
|
+
priceWindow: [],
|
|
21565
|
+
volumeWindow: [],
|
|
21566
|
+
weightedSum: 0,
|
|
21567
|
+
volumeSum: 0
|
|
21568
|
+
};
|
|
21569
|
+
}
|
|
21570
|
+
};
|
|
21571
|
+
updateMovingAverage = (state, price, volume) => {
|
|
21572
|
+
switch (state.type) {
|
|
21573
|
+
case "SMA": {
|
|
21574
|
+
state.window.push(price);
|
|
21575
|
+
state.sum += price;
|
|
21576
|
+
if (state.window.length > state.length) {
|
|
21577
|
+
state.sum -= state.window.shift() ?? 0;
|
|
21578
|
+
}
|
|
21579
|
+
if (state.window.length < state.length) {
|
|
21580
|
+
return null;
|
|
21581
|
+
}
|
|
21582
|
+
return state.sum / state.length;
|
|
21583
|
+
}
|
|
21584
|
+
case "EMA": {
|
|
21585
|
+
if (state.value == null) {
|
|
21586
|
+
state.seedWindow.push(price);
|
|
21587
|
+
if (state.seedWindow.length < state.length) {
|
|
21588
|
+
return null;
|
|
21589
|
+
}
|
|
21590
|
+
if (state.seedWindow.length === state.length) {
|
|
21591
|
+
state.value = state.seedWindow.reduce((sum, item) => sum + item, 0) / state.length;
|
|
21592
|
+
return state.value;
|
|
21593
|
+
}
|
|
21594
|
+
}
|
|
21595
|
+
const alpha = 2 / (state.length + 1);
|
|
21596
|
+
state.value = price * alpha + (state.value ?? price) * (1 - alpha);
|
|
21597
|
+
return state.value;
|
|
21598
|
+
}
|
|
21599
|
+
case "SMMA (RMA)": {
|
|
21600
|
+
if (state.value == null) {
|
|
21601
|
+
state.seedWindow.push(price);
|
|
21602
|
+
if (state.seedWindow.length < state.length) {
|
|
21603
|
+
return null;
|
|
21604
|
+
}
|
|
21605
|
+
if (state.seedWindow.length === state.length) {
|
|
21606
|
+
state.value = state.seedWindow.reduce((sum, item) => sum + item, 0) / state.length;
|
|
21607
|
+
return state.value;
|
|
21608
|
+
}
|
|
21609
|
+
}
|
|
21610
|
+
state.value = ((state.value ?? price) * (state.length - 1) + price) / state.length;
|
|
21611
|
+
return state.value;
|
|
21612
|
+
}
|
|
21613
|
+
case "WMA": {
|
|
21614
|
+
state.window.push(price);
|
|
21615
|
+
if (state.window.length > state.length) {
|
|
21616
|
+
state.window.shift();
|
|
21617
|
+
}
|
|
21618
|
+
if (state.window.length < state.length) {
|
|
21619
|
+
return null;
|
|
21620
|
+
}
|
|
21621
|
+
let weightedSum = 0;
|
|
21622
|
+
let weightSum = 0;
|
|
21623
|
+
for (let index = 0; index < state.window.length; index += 1) {
|
|
21624
|
+
const weight = index + 1;
|
|
21625
|
+
weightedSum += state.window[index] * weight;
|
|
21626
|
+
weightSum += weight;
|
|
21627
|
+
}
|
|
21628
|
+
return weightSum > 0 ? weightedSum / weightSum : null;
|
|
21629
|
+
}
|
|
21630
|
+
case "VWMA": {
|
|
21631
|
+
state.priceWindow.push(price);
|
|
21632
|
+
state.volumeWindow.push(volume);
|
|
21633
|
+
state.weightedSum += price * volume;
|
|
21634
|
+
state.volumeSum += volume;
|
|
21635
|
+
if (state.priceWindow.length > state.length) {
|
|
21636
|
+
const removedPrice = state.priceWindow.shift() ?? 0;
|
|
21637
|
+
const removedVolume = state.volumeWindow.shift() ?? 0;
|
|
21638
|
+
state.weightedSum -= removedPrice * removedVolume;
|
|
21639
|
+
state.volumeSum -= removedVolume;
|
|
21640
|
+
}
|
|
21641
|
+
if (state.priceWindow.length < state.length || state.volumeSum <= 0) {
|
|
21642
|
+
return null;
|
|
21643
|
+
}
|
|
21644
|
+
return state.weightedSum / state.volumeSum;
|
|
21645
|
+
}
|
|
21646
|
+
}
|
|
21647
|
+
};
|
|
21648
|
+
createAtrState = (length) => ({
|
|
21649
|
+
length,
|
|
21650
|
+
previousClose: null,
|
|
21651
|
+
trSeedWindow: [],
|
|
21652
|
+
atrValue: null
|
|
21653
|
+
});
|
|
21654
|
+
updateAtr = (state, candle) => {
|
|
21655
|
+
const previousClose = state.previousClose;
|
|
21656
|
+
const tr = previousClose == null ? candle.high - candle.low : Math.max(
|
|
21657
|
+
candle.high - candle.low,
|
|
21658
|
+
Math.abs(candle.high - previousClose),
|
|
21659
|
+
Math.abs(candle.low - previousClose)
|
|
21660
|
+
);
|
|
21661
|
+
state.previousClose = candle.close;
|
|
21662
|
+
if (state.atrValue == null) {
|
|
21663
|
+
state.trSeedWindow.push(tr);
|
|
21664
|
+
if (state.trSeedWindow.length < state.length) {
|
|
21665
|
+
return null;
|
|
21666
|
+
}
|
|
21667
|
+
if (state.trSeedWindow.length === state.length) {
|
|
21668
|
+
state.atrValue = state.trSeedWindow.reduce((sum, value) => sum + value, 0) / state.length;
|
|
21669
|
+
return state.atrValue;
|
|
21670
|
+
}
|
|
21671
|
+
}
|
|
21672
|
+
state.atrValue = ((state.atrValue ?? tr) * (state.length - 1) + tr) / state.length;
|
|
21673
|
+
return state.atrValue;
|
|
21674
|
+
};
|
|
21675
|
+
createButterworthState = (length) => ({
|
|
21676
|
+
length,
|
|
21677
|
+
prev1: null,
|
|
21678
|
+
prev2: null
|
|
21679
|
+
});
|
|
21680
|
+
updateButterworth = (state, source) => {
|
|
21681
|
+
if (source == null) {
|
|
21682
|
+
return null;
|
|
21683
|
+
}
|
|
21684
|
+
const safeLength = Math.max(state.length, 1);
|
|
21685
|
+
const a = Math.exp(-Math.sqrt(2) * PI / safeLength);
|
|
21686
|
+
const b = 2 * a * Math.cos(Math.sqrt(2) * PI / safeLength);
|
|
21687
|
+
const c2 = b;
|
|
21688
|
+
const c3 = -(a * a);
|
|
21689
|
+
const c1 = 1 - c2 - c3;
|
|
21690
|
+
if (state.prev1 == null || state.prev2 == null) {
|
|
21691
|
+
state.prev1 = source;
|
|
21692
|
+
state.prev2 = source;
|
|
21693
|
+
return source;
|
|
21694
|
+
}
|
|
21695
|
+
const result = c1 * source + c2 * state.prev1 + c3 * state.prev2;
|
|
21696
|
+
state.prev2 = state.prev1;
|
|
21697
|
+
state.prev1 = result;
|
|
21698
|
+
return result;
|
|
21699
|
+
};
|
|
21700
|
+
pushPlotPoint = (plotSeries, plotName, candle, value) => {
|
|
21701
|
+
if (value == null) {
|
|
21702
|
+
return;
|
|
21703
|
+
}
|
|
21704
|
+
const points = plotSeries[plotName] ?? [];
|
|
21705
|
+
points.push({
|
|
21706
|
+
time: candle.timestamp,
|
|
21707
|
+
value
|
|
21708
|
+
});
|
|
21709
|
+
if (points.length > MAX_PLOT_POINTS) {
|
|
21710
|
+
points.splice(0, points.length - MAX_PLOT_POINTS);
|
|
21711
|
+
}
|
|
21712
|
+
plotSeries[plotName] = points;
|
|
21713
|
+
};
|
|
21714
|
+
evaluateAdaptiveMomentumRibbon = ({
|
|
21715
|
+
candles,
|
|
21716
|
+
config: config8,
|
|
21717
|
+
linePlots
|
|
21718
|
+
}) => {
|
|
21719
|
+
const momentumPeriod = (0, import_math9.asPositiveInt)(config8.AMR_MOMENTUM_PERIOD, 20);
|
|
21720
|
+
const smoothingLength = (0, import_math9.asPositiveInt)(config8.AMR_BUTTERWORTH_SMOOTHING, 3);
|
|
21721
|
+
const waitClose = Boolean(config8.AMR_WAIT_CLOSE);
|
|
21722
|
+
const confirmOnNextBar = Boolean(config8.AMR_CONFIRM_ON_NEXT_BAR);
|
|
21723
|
+
const minSignalOscAbs = (0, import_math9.asPositiveNumber)(config8.AMR_MIN_SIGNAL_OSC_ABS, 0.55);
|
|
21724
|
+
const requireKcBias = Boolean(config8.AMR_REQUIRE_KC_BIAS);
|
|
21725
|
+
const minBarsBetweenSignals = (0, import_math9.asPositiveInt)(
|
|
21726
|
+
config8.AMR_MIN_BARS_BETWEEN_SIGNALS,
|
|
21727
|
+
12
|
|
21728
|
+
);
|
|
21729
|
+
const showInvalidationLevels = Boolean(config8.AMR_SHOW_INVALIDATION_LEVELS);
|
|
21730
|
+
const showKeltnerChannel = Boolean(config8.AMR_SHOW_KELTNER_CHANNEL);
|
|
21731
|
+
const kcLength = (0, import_math9.asPositiveInt)(config8.AMR_KC_LENGTH, 20);
|
|
21732
|
+
const kcMaType = config8.AMR_KC_MA_TYPE === "SMA" || config8.AMR_KC_MA_TYPE === "EMA" || config8.AMR_KC_MA_TYPE === "SMMA (RMA)" || config8.AMR_KC_MA_TYPE === "WMA" || config8.AMR_KC_MA_TYPE === "VWMA" ? config8.AMR_KC_MA_TYPE : "EMA";
|
|
21733
|
+
const atrLength = (0, import_math9.asPositiveInt)(config8.AMR_ATR_LENGTH, 14);
|
|
21734
|
+
const atrMultiplier = (0, import_math9.asPositiveNumber)(config8.AMR_ATR_MULTIPLIER, 2);
|
|
21735
|
+
const sourceWindow = [];
|
|
21736
|
+
const deviationWindow = [];
|
|
21737
|
+
const maState = createMovingAverageState(kcMaType, kcLength);
|
|
21738
|
+
const atrState = createAtrState(atrLength);
|
|
21739
|
+
const butterworthState = createButterworthState(smoothingLength);
|
|
21740
|
+
const plotSeries = {};
|
|
21741
|
+
let previousSignalOsc = null;
|
|
21742
|
+
let lastAcceptedSignalIndex = null;
|
|
21743
|
+
let pendingSignal = null;
|
|
21744
|
+
let invalidationLevel = null;
|
|
21745
|
+
let activeBuy = false;
|
|
21746
|
+
let activeSell = false;
|
|
21747
|
+
let lastSnapshot = {
|
|
21748
|
+
entryLong: false,
|
|
21749
|
+
entryShort: false,
|
|
21750
|
+
invalidated: false,
|
|
21751
|
+
activeBuy: false,
|
|
21752
|
+
activeSell: false,
|
|
21753
|
+
signalOsc: null,
|
|
21754
|
+
kcMidline: null,
|
|
21755
|
+
kcUpper: null,
|
|
21756
|
+
kcLower: null,
|
|
21757
|
+
invalidationLevel: null,
|
|
21758
|
+
lineValues: Object.fromEntries(
|
|
21759
|
+
linePlots.map((plotName) => [plotName, null])
|
|
21760
|
+
)
|
|
21761
|
+
};
|
|
21762
|
+
for (let index = 0; index < candles.length; index += 1) {
|
|
21763
|
+
const candle = candles[index];
|
|
21764
|
+
const previousCandle = index > 0 ? candles[index - 1] : null;
|
|
21765
|
+
const kcMidline = updateMovingAverage(
|
|
21766
|
+
maState,
|
|
21767
|
+
candle.close,
|
|
21768
|
+
Number(candle.volume ?? 0)
|
|
21769
|
+
);
|
|
21770
|
+
const atrValue = updateAtr(atrState, candle);
|
|
21771
|
+
const kcUpper = kcMidline != null && atrValue != null ? kcMidline + atrMultiplier * atrValue : null;
|
|
21772
|
+
const kcLower = kcMidline != null && atrValue != null ? kcMidline - atrMultiplier * atrValue : null;
|
|
21773
|
+
const sourceCandle = waitClose ? previousCandle : candle;
|
|
21774
|
+
const sourceClose = sourceCandle?.close ?? null;
|
|
21775
|
+
let signalOsc = null;
|
|
21776
|
+
let entryLong = false;
|
|
21777
|
+
let entryShort = false;
|
|
21778
|
+
if (sourceClose != null) {
|
|
21779
|
+
pushAndTrim(sourceWindow, sourceClose, momentumPeriod);
|
|
21780
|
+
if (sourceWindow.length >= momentumPeriod) {
|
|
21781
|
+
const medianValue = percentileNearestRank(sourceWindow, 50);
|
|
21782
|
+
const deviation = medianValue != null ? sourceClose - medianValue : null;
|
|
21783
|
+
if (deviation != null) {
|
|
21784
|
+
pushAndTrim(deviationWindow, deviation, momentumPeriod);
|
|
21785
|
+
}
|
|
21786
|
+
if (deviation != null && deviationWindow.length >= momentumPeriod) {
|
|
21787
|
+
const absoluteDeviationWindow = deviationWindow.map(
|
|
21788
|
+
(value) => Math.abs(value)
|
|
21789
|
+
);
|
|
21790
|
+
const medDeviation = percentileNearestRank(
|
|
21791
|
+
absoluteDeviationWindow,
|
|
21792
|
+
50
|
|
21793
|
+
);
|
|
21794
|
+
const scale = medDeviation === 0 ? stdev(sourceWindow) : medDeviation != null ? medDeviation * 1.4826 : null;
|
|
21795
|
+
const rawOsc = scale != null && scale !== 0 ? deviation / scale : 0;
|
|
21796
|
+
signalOsc = updateButterworth(butterworthState, rawOsc);
|
|
21797
|
+
}
|
|
21798
|
+
}
|
|
21799
|
+
}
|
|
21800
|
+
if (signalOsc != null && previousSignalOsc != null) {
|
|
21801
|
+
const rawEntryLong = previousSignalOsc <= 0 && signalOsc > 0;
|
|
21802
|
+
const rawEntryShort = previousSignalOsc >= 0 && signalOsc < 0;
|
|
21803
|
+
const strongEnough = Math.abs(signalOsc) >= minSignalOscAbs;
|
|
21804
|
+
const spacingOk = lastAcceptedSignalIndex == null || index - lastAcceptedSignalIndex >= minBarsBetweenSignals;
|
|
21805
|
+
const longKcBiasOk = !requireKcBias || kcMidline != null && candle.close > kcMidline;
|
|
21806
|
+
const shortKcBiasOk = !requireKcBias || kcMidline != null && candle.close < kcMidline;
|
|
21807
|
+
if (confirmOnNextBar) {
|
|
21808
|
+
if (pendingSignal?.direction === "LONG") {
|
|
21809
|
+
const pendingStillValid = pendingSignal.invalidationLevel == null || candle.low >= pendingSignal.invalidationLevel;
|
|
21810
|
+
const confirmed = pendingStillValid && signalOsc > 0 && strongEnough && longKcBiasOk;
|
|
21811
|
+
if (confirmed) {
|
|
21812
|
+
entryLong = true;
|
|
21813
|
+
invalidationLevel = pendingSignal.invalidationLevel;
|
|
21814
|
+
lastAcceptedSignalIndex = index;
|
|
21815
|
+
}
|
|
21816
|
+
pendingSignal = null;
|
|
21817
|
+
} else if (pendingSignal?.direction === "SHORT") {
|
|
21818
|
+
const pendingStillValid = pendingSignal.invalidationLevel == null || candle.high <= pendingSignal.invalidationLevel;
|
|
21819
|
+
const confirmed = pendingStillValid && signalOsc < 0 && strongEnough && shortKcBiasOk;
|
|
21820
|
+
if (confirmed) {
|
|
21821
|
+
entryShort = true;
|
|
21822
|
+
invalidationLevel = pendingSignal.invalidationLevel;
|
|
21823
|
+
lastAcceptedSignalIndex = index;
|
|
21824
|
+
}
|
|
21825
|
+
pendingSignal = null;
|
|
21826
|
+
}
|
|
21827
|
+
if (!entryLong && !entryShort && spacingOk) {
|
|
21828
|
+
if (rawEntryLong && strongEnough && longKcBiasOk && sourceCandle) {
|
|
21829
|
+
pendingSignal = {
|
|
21830
|
+
direction: "LONG",
|
|
21831
|
+
invalidationLevel: sourceCandle.low
|
|
21832
|
+
};
|
|
21833
|
+
} else if (rawEntryShort && strongEnough && shortKcBiasOk && sourceCandle) {
|
|
21834
|
+
pendingSignal = {
|
|
21835
|
+
direction: "SHORT",
|
|
21836
|
+
invalidationLevel: sourceCandle.high
|
|
21837
|
+
};
|
|
21838
|
+
}
|
|
21839
|
+
}
|
|
21840
|
+
} else {
|
|
21841
|
+
entryLong = rawEntryLong && strongEnough && spacingOk && longKcBiasOk;
|
|
21842
|
+
entryShort = rawEntryShort && strongEnough && spacingOk && shortKcBiasOk;
|
|
21843
|
+
}
|
|
21844
|
+
}
|
|
21845
|
+
if (signalOsc != null) {
|
|
21846
|
+
previousSignalOsc = signalOsc;
|
|
21847
|
+
}
|
|
21848
|
+
if (entryLong && sourceCandle) {
|
|
21849
|
+
if (invalidationLevel == null) {
|
|
21850
|
+
invalidationLevel = sourceCandle.low;
|
|
21851
|
+
}
|
|
21852
|
+
activeBuy = true;
|
|
21853
|
+
activeSell = false;
|
|
21854
|
+
}
|
|
21855
|
+
if (entryShort && sourceCandle) {
|
|
21856
|
+
if (invalidationLevel == null) {
|
|
21857
|
+
invalidationLevel = sourceCandle.high;
|
|
21858
|
+
}
|
|
21859
|
+
activeSell = true;
|
|
21860
|
+
activeBuy = false;
|
|
21861
|
+
}
|
|
21862
|
+
const checkCandle = waitClose ? previousCandle : candle;
|
|
21863
|
+
let invalidated = false;
|
|
21864
|
+
if (activeBuy && checkCandle && invalidationLevel != null && checkCandle.low < invalidationLevel) {
|
|
21865
|
+
invalidated = true;
|
|
21866
|
+
}
|
|
21867
|
+
if (activeSell && checkCandle && invalidationLevel != null && checkCandle.high > invalidationLevel) {
|
|
21868
|
+
invalidated = true;
|
|
21869
|
+
}
|
|
21870
|
+
if (invalidated) {
|
|
21871
|
+
activeBuy = false;
|
|
21872
|
+
activeSell = false;
|
|
21873
|
+
}
|
|
21874
|
+
const displayedKcMidline = showKeltnerChannel ? kcMidline : null;
|
|
21875
|
+
const displayedKcUpper = showKeltnerChannel ? kcUpper : null;
|
|
21876
|
+
const displayedKcLower = showKeltnerChannel ? kcLower : null;
|
|
21877
|
+
const displayedInvalidationLevel = showInvalidationLevels ? invalidationLevel : null;
|
|
21878
|
+
pushPlotPoint(plotSeries, "signalOsc", candle, signalOsc);
|
|
21879
|
+
pushPlotPoint(plotSeries, "kcMidline", candle, displayedKcMidline);
|
|
21880
|
+
pushPlotPoint(plotSeries, "kcUpper", candle, displayedKcUpper);
|
|
21881
|
+
pushPlotPoint(plotSeries, "kcLower", candle, displayedKcLower);
|
|
21882
|
+
pushPlotPoint(
|
|
21883
|
+
plotSeries,
|
|
21884
|
+
"invalidationLevel",
|
|
21885
|
+
candle,
|
|
21886
|
+
displayedInvalidationLevel
|
|
21887
|
+
);
|
|
21888
|
+
const currentLineValues = {};
|
|
21889
|
+
for (const plotName of linePlots) {
|
|
21890
|
+
switch (plotName) {
|
|
21891
|
+
case "signalOsc":
|
|
21892
|
+
currentLineValues[plotName] = signalOsc;
|
|
21893
|
+
break;
|
|
21894
|
+
case "kcMidline":
|
|
21895
|
+
currentLineValues[plotName] = displayedKcMidline;
|
|
21896
|
+
break;
|
|
21897
|
+
case "kcUpper":
|
|
21898
|
+
currentLineValues[plotName] = displayedKcUpper;
|
|
21899
|
+
break;
|
|
21900
|
+
case "kcLower":
|
|
21901
|
+
currentLineValues[plotName] = displayedKcLower;
|
|
21902
|
+
break;
|
|
21903
|
+
case "invalidationLevel":
|
|
21904
|
+
currentLineValues[plotName] = displayedInvalidationLevel;
|
|
21905
|
+
break;
|
|
21906
|
+
default:
|
|
21907
|
+
currentLineValues[plotName] = null;
|
|
21908
|
+
break;
|
|
21909
|
+
}
|
|
21910
|
+
}
|
|
21911
|
+
lastSnapshot = {
|
|
21912
|
+
entryLong,
|
|
21913
|
+
entryShort,
|
|
21914
|
+
invalidated,
|
|
21915
|
+
activeBuy,
|
|
21916
|
+
activeSell,
|
|
21917
|
+
signalOsc: toFinite(signalOsc),
|
|
21918
|
+
kcMidline: toFinite(displayedKcMidline),
|
|
21919
|
+
kcUpper: toFinite(displayedKcUpper),
|
|
21920
|
+
kcLower: toFinite(displayedKcLower),
|
|
21921
|
+
invalidationLevel: toFinite(displayedInvalidationLevel),
|
|
21922
|
+
lineValues: currentLineValues
|
|
21923
|
+
};
|
|
21924
|
+
}
|
|
21925
|
+
return {
|
|
21926
|
+
snapshot: lastSnapshot,
|
|
21927
|
+
plotSeries
|
|
21928
|
+
};
|
|
21929
|
+
};
|
|
21930
|
+
}
|
|
21931
|
+
});
|
|
21932
|
+
|
|
20884
21933
|
// src/AdaptiveMomentumRibbon/figures.ts
|
|
20885
|
-
var
|
|
20886
|
-
var
|
|
21934
|
+
var DEFAULT_COLORS, LINE_STYLE_BY_PLOT, toFigurePoints, buildAdaptiveMomentumRibbonFigures;
|
|
21935
|
+
var init_figures6 = __esm({
|
|
20887
21936
|
"src/AdaptiveMomentumRibbon/figures.ts"() {
|
|
20888
21937
|
"use strict";
|
|
20889
|
-
import_pine = require("@tradejs/node/pine");
|
|
20890
21938
|
DEFAULT_COLORS = ["#2962ff", "#f23645", "#089981", "#f59e0b"];
|
|
20891
21939
|
LINE_STYLE_BY_PLOT = {
|
|
20892
21940
|
kcMidline: {
|
|
@@ -20915,18 +21963,18 @@ var init_figures5 = __esm({
|
|
|
20915
21963
|
const points = [];
|
|
20916
21964
|
for (let i = start; i < series.length; i += 1) {
|
|
20917
21965
|
const item = series[i];
|
|
20918
|
-
|
|
20919
|
-
|
|
20920
|
-
|
|
21966
|
+
if (!Number.isFinite(item?.time) || !Number.isFinite(item?.value)) {
|
|
21967
|
+
continue;
|
|
21968
|
+
}
|
|
20921
21969
|
points.push({
|
|
20922
|
-
timestamp,
|
|
20923
|
-
value
|
|
21970
|
+
timestamp: item.time,
|
|
21971
|
+
value: item.value
|
|
20924
21972
|
});
|
|
20925
21973
|
}
|
|
20926
21974
|
return points;
|
|
20927
21975
|
};
|
|
20928
21976
|
buildAdaptiveMomentumRibbonFigures = ({
|
|
20929
|
-
|
|
21977
|
+
plotSeries,
|
|
20930
21978
|
linePlots,
|
|
20931
21979
|
direction,
|
|
20932
21980
|
entryTimestamp,
|
|
@@ -20934,7 +21982,7 @@ var init_figures5 = __esm({
|
|
|
20934
21982
|
maxPoints = 180
|
|
20935
21983
|
}) => {
|
|
20936
21984
|
const lines = linePlots.map((plotName, index) => {
|
|
20937
|
-
const series =
|
|
21985
|
+
const series = plotSeries[plotName] ?? [];
|
|
20938
21986
|
const points = toFigurePoints(series, maxPoints);
|
|
20939
21987
|
if (!points.length) {
|
|
20940
21988
|
return null;
|
|
@@ -20969,70 +22017,25 @@ var init_figures5 = __esm({
|
|
|
20969
22017
|
});
|
|
20970
22018
|
|
|
20971
22019
|
// src/AdaptiveMomentumRibbon/core.ts
|
|
20972
|
-
var
|
|
20973
|
-
var
|
|
22020
|
+
var import_math10, resolveLinePlots, createAdaptiveMomentumRibbonCore;
|
|
22021
|
+
var init_core6 = __esm({
|
|
20974
22022
|
"src/AdaptiveMomentumRibbon/core.ts"() {
|
|
20975
22023
|
"use strict";
|
|
20976
|
-
|
|
20977
|
-
import_math8 = require("@tradejs/core/math");
|
|
22024
|
+
import_math10 = require("@tradejs/core/math");
|
|
20978
22025
|
init_logger();
|
|
20979
|
-
|
|
20980
|
-
|
|
20981
|
-
AMR_BOOLEAN_PLOTS = [
|
|
20982
|
-
"entryLong",
|
|
20983
|
-
"entryShort",
|
|
20984
|
-
"invalidated",
|
|
20985
|
-
"activeBuy",
|
|
20986
|
-
"activeSell"
|
|
20987
|
-
];
|
|
20988
|
-
AMR_NUMBER_PLOTS = [
|
|
20989
|
-
"signalOsc",
|
|
20990
|
-
"kcMidline",
|
|
20991
|
-
"kcUpper",
|
|
20992
|
-
"kcLower",
|
|
20993
|
-
"invalidationLevel"
|
|
20994
|
-
];
|
|
20995
|
-
asKcMaType = (value) => {
|
|
20996
|
-
if (value === "SMA" || value === "EMA" || value === "SMMA (RMA)" || value === "WMA" || value === "VWMA") {
|
|
20997
|
-
return value;
|
|
20998
|
-
}
|
|
20999
|
-
return "EMA";
|
|
21000
|
-
};
|
|
21001
|
-
resolveAmrInputs = (config7) => ({
|
|
21002
|
-
"Momentum Period": (0, import_math8.asPositiveInt)(config7.AMR_MOMENTUM_PERIOD, 20),
|
|
21003
|
-
"Butterworth Smoothing": (0, import_math8.asPositiveInt)(config7.AMR_BUTTERWORTH_SMOOTHING, 3),
|
|
21004
|
-
"Confirm Signals on Bar Close": Boolean(config7.AMR_WAIT_CLOSE),
|
|
21005
|
-
"Show Invalidation Levels": Boolean(config7.AMR_SHOW_INVALIDATION_LEVELS),
|
|
21006
|
-
"Show Keltner Channel": Boolean(config7.AMR_SHOW_KELTNER_CHANNEL),
|
|
21007
|
-
"KC Length": (0, import_math8.asPositiveInt)(config7.AMR_KC_LENGTH, 20),
|
|
21008
|
-
"KC MA Type": asKcMaType(config7.AMR_KC_MA_TYPE),
|
|
21009
|
-
"ATR Length": (0, import_math8.asPositiveInt)(config7.AMR_ATR_LENGTH, 14),
|
|
21010
|
-
"ATR Multiplier": (0, import_math8.asPositiveNumber)(config7.AMR_ATR_MULTIPLIER, 2)
|
|
21011
|
-
});
|
|
22026
|
+
init_engine2();
|
|
22027
|
+
init_figures6();
|
|
21012
22028
|
resolveLinePlots = (value) => {
|
|
21013
22029
|
if (!Array.isArray(value)) {
|
|
21014
22030
|
return [];
|
|
21015
22031
|
}
|
|
21016
22032
|
return value.map((item) => String(item ?? "").trim()).filter((item) => item.length > 0);
|
|
21017
22033
|
};
|
|
21018
|
-
|
|
21019
|
-
|
|
21020
|
-
|
|
21021
|
-
|
|
21022
|
-
lineValues: (0, import_pine2.getLatestPineNumberPlotValues)(pineContext, linePlots)
|
|
21023
|
-
};
|
|
21024
|
-
};
|
|
21025
|
-
createAdaptiveMomentumRibbonCore = async ({ config: config7, symbol, loadPineScriptFile, strategyApi }) => {
|
|
21026
|
-
const script = loadPineScriptFile(AMR_PINE_FILE_NAME);
|
|
21027
|
-
const { LONG, SHORT, AMR_EXIT_ON_INVALIDATION, MAX_LOSS_VALUE, FEE_PERCENT } = config7;
|
|
21028
|
-
const linePlots = resolveLinePlots(config7.AMR_LINE_PLOTS);
|
|
21029
|
-
const lookbackBars = (0, import_math8.asPositiveInt)(config7.AMR_LOOKBACK_BARS, 0);
|
|
21030
|
-
const pineInputs = resolveAmrInputs(config7);
|
|
21031
|
-
const timeframe = String(config7.INTERVAL ?? "15");
|
|
22034
|
+
createAdaptiveMomentumRibbonCore = async ({ config: config8, symbol, strategyApi }) => {
|
|
22035
|
+
const { LONG, SHORT, AMR_EXIT_ON_INVALIDATION, MAX_LOSS_VALUE, FEE_PERCENT } = config8;
|
|
22036
|
+
const linePlots = resolveLinePlots(config8.AMR_LINE_PLOTS);
|
|
22037
|
+
const lookbackBars = (0, import_math10.asPositiveInt)(config8.AMR_LOOKBACK_BARS, 0);
|
|
21032
22038
|
return async () => {
|
|
21033
|
-
if (!script) {
|
|
21034
|
-
return strategyApi.skip("AMR_SCRIPT_EMPTY");
|
|
21035
|
-
}
|
|
21036
22039
|
const { fullData, currentPrice, timestamp } = await strategyApi.getMarketData();
|
|
21037
22040
|
if (fullData.length < 2) {
|
|
21038
22041
|
return strategyApi.skip("WAIT_DATA");
|
|
@@ -21042,26 +22045,24 @@ var init_core5 = __esm({
|
|
|
21042
22045
|
position && typeof position.qty === "number" && position.qty > 0
|
|
21043
22046
|
);
|
|
21044
22047
|
const candles = lookbackBars > 0 ? fullData.slice(-lookbackBars) : fullData;
|
|
21045
|
-
let
|
|
22048
|
+
let evaluation;
|
|
21046
22049
|
try {
|
|
21047
|
-
|
|
22050
|
+
evaluation = evaluateAdaptiveMomentumRibbon({
|
|
21048
22051
|
candles,
|
|
21049
|
-
|
|
21050
|
-
|
|
21051
|
-
timeframe,
|
|
21052
|
-
inputs: pineInputs
|
|
22052
|
+
config: config8,
|
|
22053
|
+
linePlots
|
|
21053
22054
|
});
|
|
21054
22055
|
} catch (error) {
|
|
21055
22056
|
if (typeof globalThis.setImmediate === "function") {
|
|
21056
22057
|
logger.warn(
|
|
21057
|
-
"AdaptiveMomentumRibbon
|
|
22058
|
+
"AdaptiveMomentumRibbon evaluation failed for %s: %s",
|
|
21058
22059
|
symbol,
|
|
21059
22060
|
String(error)
|
|
21060
22061
|
);
|
|
21061
22062
|
}
|
|
21062
|
-
return strategyApi.skip("
|
|
22063
|
+
return strategyApi.skip("AMR_EVALUATION_FAILED");
|
|
21063
22064
|
}
|
|
21064
|
-
const amr =
|
|
22065
|
+
const { snapshot: amr, plotSeries } = evaluation;
|
|
21065
22066
|
if (amr.entryLong && amr.entryShort) {
|
|
21066
22067
|
return strategyApi.skip("AMR_SIGNAL_CONFLICT");
|
|
21067
22068
|
}
|
|
@@ -21113,7 +22114,7 @@ var init_core5 = __esm({
|
|
|
21113
22114
|
code: amr.entryLong ? "AMR_ENTRY_LONG" : "AMR_ENTRY_SHORT",
|
|
21114
22115
|
direction: modeConfig.direction,
|
|
21115
22116
|
figures: buildAdaptiveMomentumRibbonFigures({
|
|
21116
|
-
|
|
22117
|
+
plotSeries,
|
|
21117
22118
|
linePlots,
|
|
21118
22119
|
direction: modeConfig.direction,
|
|
21119
22120
|
entryTimestamp: timestamp,
|
|
@@ -21123,18 +22124,28 @@ var init_core5 = __esm({
|
|
|
21123
22124
|
amr,
|
|
21124
22125
|
amrSignalTiming: {
|
|
21125
22126
|
entryTiming: "zero_cross",
|
|
21126
|
-
waitClose: Boolean(
|
|
22127
|
+
waitClose: Boolean(config8.AMR_WAIT_CLOSE),
|
|
22128
|
+
confirmOnNextBar: Boolean(config8.AMR_CONFIRM_ON_NEXT_BAR),
|
|
21127
22129
|
lookbackBars
|
|
21128
22130
|
},
|
|
21129
22131
|
amrConfigSnapshot: {
|
|
21130
|
-
momentumPeriod: (0,
|
|
21131
|
-
butterworthSmoothing: (0,
|
|
21132
|
-
|
|
22132
|
+
momentumPeriod: (0, import_math10.asPositiveInt)(config8.AMR_MOMENTUM_PERIOD, 20),
|
|
22133
|
+
butterworthSmoothing: (0, import_math10.asPositiveInt)(
|
|
22134
|
+
config8.AMR_BUTTERWORTH_SMOOTHING,
|
|
21133
22135
|
3
|
|
21134
22136
|
),
|
|
21135
|
-
|
|
21136
|
-
|
|
21137
|
-
|
|
22137
|
+
minSignalOscAbs: (0, import_math10.asPositiveNumber)(
|
|
22138
|
+
config8.AMR_MIN_SIGNAL_OSC_ABS,
|
|
22139
|
+
0.55
|
|
22140
|
+
),
|
|
22141
|
+
requireKcBias: Boolean(config8.AMR_REQUIRE_KC_BIAS),
|
|
22142
|
+
minBarsBetweenSignals: (0, import_math10.asPositiveInt)(
|
|
22143
|
+
config8.AMR_MIN_BARS_BETWEEN_SIGNALS,
|
|
22144
|
+
12
|
|
22145
|
+
),
|
|
22146
|
+
kcLength: (0, import_math10.asPositiveInt)(config8.AMR_KC_LENGTH, 20),
|
|
22147
|
+
atrLength: (0, import_math10.asPositiveInt)(config8.AMR_ATR_LENGTH, 14),
|
|
22148
|
+
atrMultiplier: (0, import_math10.asPositiveNumber)(config8.AMR_ATR_MULTIPLIER, 2)
|
|
21138
22149
|
}
|
|
21139
22150
|
},
|
|
21140
22151
|
orderPlan: {
|
|
@@ -21149,21 +22160,21 @@ var init_core5 = __esm({
|
|
|
21149
22160
|
});
|
|
21150
22161
|
|
|
21151
22162
|
// src/AdaptiveMomentumRibbon/strategy.ts
|
|
21152
|
-
var
|
|
21153
|
-
__export(
|
|
22163
|
+
var strategy_exports6 = {};
|
|
22164
|
+
__export(strategy_exports6, {
|
|
21154
22165
|
AdaptiveMomentumRibbonStrategyCreator: () => AdaptiveMomentumRibbonStrategyCreator
|
|
21155
22166
|
});
|
|
21156
|
-
var
|
|
21157
|
-
var
|
|
22167
|
+
var import_strategies17, AdaptiveMomentumRibbonStrategyCreator;
|
|
22168
|
+
var init_strategy6 = __esm({
|
|
21158
22169
|
"src/AdaptiveMomentumRibbon/strategy.ts"() {
|
|
21159
22170
|
"use strict";
|
|
21160
|
-
|
|
21161
|
-
|
|
21162
|
-
|
|
22171
|
+
import_strategies17 = require("@tradejs/node/strategies");
|
|
22172
|
+
init_config();
|
|
22173
|
+
init_core6();
|
|
21163
22174
|
init_manifest();
|
|
21164
|
-
AdaptiveMomentumRibbonStrategyCreator = (0,
|
|
22175
|
+
AdaptiveMomentumRibbonStrategyCreator = (0, import_strategies17.createStrategyRuntime)({
|
|
21165
22176
|
strategyName: "AdaptiveMomentumRibbon",
|
|
21166
|
-
defaults:
|
|
22177
|
+
defaults: config,
|
|
21167
22178
|
createCore: createAdaptiveMomentumRibbonCore,
|
|
21168
22179
|
manifest: adaptiveMomentumRibbonManifest,
|
|
21169
22180
|
strategyDirectory: __dirname
|
|
@@ -21171,67 +22182,9 @@ var init_strategy5 = __esm({
|
|
|
21171
22182
|
}
|
|
21172
22183
|
});
|
|
21173
22184
|
|
|
21174
|
-
// src/VolumeDivergence/config.ts
|
|
21175
|
-
var config6;
|
|
21176
|
-
var init_config6 = __esm({
|
|
21177
|
-
"src/VolumeDivergence/config.ts"() {
|
|
21178
|
-
"use strict";
|
|
21179
|
-
config6 = {
|
|
21180
|
-
ENV: "BACKTEST",
|
|
21181
|
-
INTERVAL: "15",
|
|
21182
|
-
MAKE_ORDERS: true,
|
|
21183
|
-
CLOSE_OPPOSITE_POSITIONS: false,
|
|
21184
|
-
BACKTEST_PRICE_MODE: "mid",
|
|
21185
|
-
AI_ENABLED: false,
|
|
21186
|
-
ML_ENABLED: false,
|
|
21187
|
-
ML_THRESHOLD: 0.1,
|
|
21188
|
-
MIN_AI_QUALITY: 3,
|
|
21189
|
-
FEE_PERCENT: 5e-3,
|
|
21190
|
-
MAX_LOSS_VALUE: 10,
|
|
21191
|
-
MA_FAST: 14,
|
|
21192
|
-
MA_MEDIUM: 49,
|
|
21193
|
-
MA_SLOW: 50,
|
|
21194
|
-
OBV_SMA: 10,
|
|
21195
|
-
ATR: 14,
|
|
21196
|
-
ATR_PCT_SHORT: 7,
|
|
21197
|
-
ATR_PCT_LONG: 30,
|
|
21198
|
-
BB: 20,
|
|
21199
|
-
BB_STD: 2,
|
|
21200
|
-
MACD_FAST: 12,
|
|
21201
|
-
MACD_SLOW: 26,
|
|
21202
|
-
MACD_SIGNAL: 9,
|
|
21203
|
-
LEVEL_LOOKBACK: 20,
|
|
21204
|
-
LEVEL_DELAY: 2,
|
|
21205
|
-
NORMALIZATION_LENGTH: 100,
|
|
21206
|
-
PIVOT_LOOKBACK_LEFT: 8,
|
|
21207
|
-
PIVOT_LOOKBACK_RIGHT: 3,
|
|
21208
|
-
MIN_BARS_BETWEEN_PIVOTS: 4,
|
|
21209
|
-
MAX_BARS_BETWEEN_PIVOTS: 36,
|
|
21210
|
-
ALLOW_STRUCTURE_ADVANCE_ENTRY: false,
|
|
21211
|
-
MIN_DIVERGENCE_AMPLITUDE_ATR_RATIO: 0.35,
|
|
21212
|
-
MIN_RECLAIM_PCT: 105,
|
|
21213
|
-
MIN_CONFIRMATION_CANDLE_QUALITY: 0.58,
|
|
21214
|
-
BULLISH: {
|
|
21215
|
-
enable: true,
|
|
21216
|
-
direction: "LONG",
|
|
21217
|
-
TP: 4,
|
|
21218
|
-
SL: 1.3,
|
|
21219
|
-
minRiskRatio: 2
|
|
21220
|
-
},
|
|
21221
|
-
BEARISH: {
|
|
21222
|
-
enable: true,
|
|
21223
|
-
direction: "SHORT",
|
|
21224
|
-
TP: 4,
|
|
21225
|
-
SL: 1.3,
|
|
21226
|
-
minRiskRatio: 2
|
|
21227
|
-
}
|
|
21228
|
-
};
|
|
21229
|
-
}
|
|
21230
|
-
});
|
|
21231
|
-
|
|
21232
22185
|
// src/VolumeDivergence/figures.ts
|
|
21233
22186
|
var buildVolumeDivergenceFigures;
|
|
21234
|
-
var
|
|
22187
|
+
var init_figures7 = __esm({
|
|
21235
22188
|
"src/VolumeDivergence/figures.ts"() {
|
|
21236
22189
|
"use strict";
|
|
21237
22190
|
buildVolumeDivergenceFigures = ({
|
|
@@ -21286,64 +22239,18 @@ var init_figures6 = __esm({
|
|
|
21286
22239
|
});
|
|
21287
22240
|
|
|
21288
22241
|
// src/VolumeDivergence/core.ts
|
|
21289
|
-
var
|
|
21290
|
-
var
|
|
22242
|
+
var import_math11, isFiniteNumber2, clamp2, isOpenPosition4, compactQueue, rebaseQueue, rebaseConfirmedPivots, appendNormalizedVolumes, isPivotHigh, candleDeltaProxy, appendConfirmedPivotIndices, findLatestDivergence, getRequiredHistorySize, buildPendingDivergenceCandidate, updatePendingCandidateProgress, resolvePendingEntryTiming, findCandleIndexByTimestamp, getModeConfigByKind, buildEntryPayloadFromPendingCandidate, createVolumeDivergenceCore;
|
|
22243
|
+
var init_core7 = __esm({
|
|
21291
22244
|
"src/VolumeDivergence/core.ts"() {
|
|
21292
22245
|
"use strict";
|
|
21293
|
-
|
|
21294
|
-
|
|
22246
|
+
import_math11 = require("@tradejs/core/math");
|
|
22247
|
+
init_figures7();
|
|
21295
22248
|
init_setup();
|
|
21296
|
-
BREAK_EVEN_TRIGGER_RISK_MULTIPLIER3 = 0.5;
|
|
21297
22249
|
isFiniteNumber2 = (value) => typeof value === "number" && Number.isFinite(value);
|
|
21298
22250
|
clamp2 = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
21299
|
-
|
|
22251
|
+
isOpenPosition4 = (position) => Boolean(
|
|
21300
22252
|
position && typeof position.price === "number" && Number.isFinite(position.price) && typeof position.qty === "number" && Number.isFinite(position.qty) && position.qty > 0 && (position.direction === "LONG" || position.direction === "SHORT")
|
|
21301
22253
|
);
|
|
21302
|
-
getFavorableMovePct3 = ({
|
|
21303
|
-
direction,
|
|
21304
|
-
entryPrice,
|
|
21305
|
-
currentPrice
|
|
21306
|
-
}) => {
|
|
21307
|
-
if (!Number.isFinite(entryPrice) || !Number.isFinite(currentPrice) || entryPrice <= 0) {
|
|
21308
|
-
return null;
|
|
21309
|
-
}
|
|
21310
|
-
return direction === "LONG" ? (currentPrice - entryPrice) / entryPrice * 100 : (entryPrice - currentPrice) / entryPrice * 100;
|
|
21311
|
-
};
|
|
21312
|
-
getPositionStopLossPrice3 = (position) => {
|
|
21313
|
-
if (!position || typeof position !== "object") {
|
|
21314
|
-
return null;
|
|
21315
|
-
}
|
|
21316
|
-
const slPrice = Number(
|
|
21317
|
-
position.slPrice ?? Number.NaN
|
|
21318
|
-
);
|
|
21319
|
-
if (Number.isFinite(slPrice)) {
|
|
21320
|
-
return slPrice;
|
|
21321
|
-
}
|
|
21322
|
-
const signalStopLossPrice = Number(
|
|
21323
|
-
position.signal?.prices?.stopLossPrice ?? Number.NaN
|
|
21324
|
-
);
|
|
21325
|
-
return Number.isFinite(signalStopLossPrice) ? signalStopLossPrice : null;
|
|
21326
|
-
};
|
|
21327
|
-
getPositionRiskPct3 = ({
|
|
21328
|
-
direction,
|
|
21329
|
-
entryPrice,
|
|
21330
|
-
stopLossPrice
|
|
21331
|
-
}) => {
|
|
21332
|
-
if (stopLossPrice == null || !Number.isFinite(entryPrice) || !Number.isFinite(stopLossPrice) || entryPrice <= 0) {
|
|
21333
|
-
return null;
|
|
21334
|
-
}
|
|
21335
|
-
return direction === "LONG" ? (entryPrice - stopLossPrice) / entryPrice * 100 : (stopLossPrice - entryPrice) / entryPrice * 100;
|
|
21336
|
-
};
|
|
21337
|
-
isBreakEvenStopAlreadyApplied3 = ({
|
|
21338
|
-
direction,
|
|
21339
|
-
entryPrice,
|
|
21340
|
-
stopLossPrice
|
|
21341
|
-
}) => {
|
|
21342
|
-
if (stopLossPrice == null || !Number.isFinite(entryPrice) || !Number.isFinite(stopLossPrice)) {
|
|
21343
|
-
return false;
|
|
21344
|
-
}
|
|
21345
|
-
return direction === "LONG" ? stopLossPrice >= entryPrice : stopLossPrice <= entryPrice;
|
|
21346
|
-
};
|
|
21347
22254
|
compactQueue = (queue) => {
|
|
21348
22255
|
if (queue.start <= 1024 || queue.start * 2 <= queue.indices.length) {
|
|
21349
22256
|
return;
|
|
@@ -21653,7 +22560,7 @@ var init_core6 = __esm({
|
|
|
21653
22560
|
}
|
|
21654
22561
|
};
|
|
21655
22562
|
};
|
|
21656
|
-
createVolumeDivergenceCore = async ({ config:
|
|
22563
|
+
createVolumeDivergenceCore = async ({ config: config8, strategyApi, indicatorsState, data: initialData }) => {
|
|
21657
22564
|
const {
|
|
21658
22565
|
NORMALIZATION_LENGTH,
|
|
21659
22566
|
PIVOT_LOOKBACK_LEFT,
|
|
@@ -21668,7 +22575,7 @@ var init_core6 = __esm({
|
|
|
21668
22575
|
MAX_LOSS_VALUE,
|
|
21669
22576
|
BULLISH,
|
|
21670
22577
|
BEARISH
|
|
21671
|
-
} =
|
|
22578
|
+
} = config8;
|
|
21672
22579
|
const entryThresholds = getVolumeDivergenceEntryThresholds({
|
|
21673
22580
|
ALLOW_STRUCTURE_ADVANCE_ENTRY,
|
|
21674
22581
|
MIN_DIVERGENCE_AMPLITUDE_ATR_RATIO,
|
|
@@ -21743,33 +22650,7 @@ var init_core6 = __esm({
|
|
|
21743
22650
|
indicatorsState.onBar();
|
|
21744
22651
|
const timestamp = Number(candle.timestamp);
|
|
21745
22652
|
const currentPosition = await strategyApi.getCurrentPosition();
|
|
21746
|
-
if (
|
|
21747
|
-
const { currentPrice: currentPrice2 } = await strategyApi.getMarketData();
|
|
21748
|
-
const activeModeConfig = currentPosition.direction === "LONG" ? BULLISH : BEARISH;
|
|
21749
|
-
const currentStopLossPrice = getPositionStopLossPrice3(currentPosition);
|
|
21750
|
-
const favorableMovePct = getFavorableMovePct3({
|
|
21751
|
-
direction: currentPosition.direction,
|
|
21752
|
-
entryPrice: currentPosition.price,
|
|
21753
|
-
currentPrice: currentPrice2
|
|
21754
|
-
});
|
|
21755
|
-
const currentPositionRiskPct = getPositionRiskPct3({
|
|
21756
|
-
direction: currentPosition.direction,
|
|
21757
|
-
entryPrice: currentPosition.price,
|
|
21758
|
-
stopLossPrice: currentStopLossPrice
|
|
21759
|
-
});
|
|
21760
|
-
if (!isBreakEvenStopAlreadyApplied3({
|
|
21761
|
-
direction: currentPosition.direction,
|
|
21762
|
-
entryPrice: currentPosition.price,
|
|
21763
|
-
stopLossPrice: currentStopLossPrice
|
|
21764
|
-
}) && favorableMovePct != null && favorableMovePct >= (currentPositionRiskPct ?? activeModeConfig.SL) * BREAK_EVEN_TRIGGER_RISK_MULTIPLIER3) {
|
|
21765
|
-
return strategyApi.protect({
|
|
21766
|
-
code: "VOLUME_DIVERGENCE_MOVE_STOP_TO_BREAK_EVEN",
|
|
21767
|
-
protectPlan: {
|
|
21768
|
-
direction: currentPosition.direction,
|
|
21769
|
-
stopLossPrice: currentPosition.price
|
|
21770
|
-
}
|
|
21771
|
-
});
|
|
21772
|
-
}
|
|
22653
|
+
if (isOpenPosition4(currentPosition)) {
|
|
21773
22654
|
return strategyApi.skip("POSITION_EXISTS");
|
|
21774
22655
|
}
|
|
21775
22656
|
if (candleWindow.length < PIVOT_LOOKBACK_LEFT + PIVOT_LOOKBACK_RIGHT + 2) {
|
|
@@ -21813,7 +22694,7 @@ var init_core6 = __esm({
|
|
|
21813
22694
|
previousPivotLow: nextPendingCandidate.previousPivotLow,
|
|
21814
22695
|
currentPivotHigh: nextPendingCandidate.currentPivotHigh,
|
|
21815
22696
|
previousPivotHigh: nextPendingCandidate.previousPivotHigh,
|
|
21816
|
-
atrPeriod:
|
|
22697
|
+
atrPeriod: config8.ATR
|
|
21817
22698
|
});
|
|
21818
22699
|
if (detectionSetupFeatures.divergenceAmplitudeAtrRatio != null && detectionSetupFeatures.divergenceAmplitudeAtrRatio < entryThresholds.minDivergenceAmplitudeAtrRatio) {
|
|
21819
22700
|
return strategyApi.skip("WEAK_DIVERGENCE_AMPLITUDE_ATR");
|
|
@@ -21854,7 +22735,7 @@ var init_core6 = __esm({
|
|
|
21854
22735
|
previousPivotLow: pendingCandidate.previousPivotLow,
|
|
21855
22736
|
currentPivotHigh: pendingCandidate.currentPivotHigh,
|
|
21856
22737
|
previousPivotHigh: pendingCandidate.previousPivotHigh,
|
|
21857
|
-
atrPeriod:
|
|
22738
|
+
atrPeriod: config8.ATR
|
|
21858
22739
|
});
|
|
21859
22740
|
if (setupFeatures.reclaimPct != null && setupFeatures.reclaimPct < entryThresholds.minReclaimPct) {
|
|
21860
22741
|
return strategyApi.skip("WAIT_CONFIRMATION_RECLAIM");
|
|
@@ -21875,7 +22756,7 @@ var init_core6 = __esm({
|
|
|
21875
22756
|
return strategyApi.skip("INVALID_QTY");
|
|
21876
22757
|
}
|
|
21877
22758
|
if (riskRatio <= modeConfig.minRiskRatio) {
|
|
21878
|
-
return strategyApi.skip(`RISK_RATIO:${(0,
|
|
22759
|
+
return strategyApi.skip(`RISK_RATIO:${(0, import_math11.round)(riskRatio)}`);
|
|
21879
22760
|
}
|
|
21880
22761
|
const indicators = indicatorsState.snapshot();
|
|
21881
22762
|
const entryPayload = buildEntryPayloadFromPendingCandidate({
|
|
@@ -21905,21 +22786,21 @@ var init_core6 = __esm({
|
|
|
21905
22786
|
});
|
|
21906
22787
|
|
|
21907
22788
|
// src/VolumeDivergence/strategy.ts
|
|
21908
|
-
var
|
|
21909
|
-
__export(
|
|
22789
|
+
var strategy_exports7 = {};
|
|
22790
|
+
__export(strategy_exports7, {
|
|
21910
22791
|
VolumeDivergenceStrategyCreator: () => VolumeDivergenceStrategyCreator
|
|
21911
22792
|
});
|
|
21912
|
-
var
|
|
21913
|
-
var
|
|
22793
|
+
var import_strategies18, VolumeDivergenceStrategyCreator;
|
|
22794
|
+
var init_strategy7 = __esm({
|
|
21914
22795
|
"src/VolumeDivergence/strategy.ts"() {
|
|
21915
22796
|
"use strict";
|
|
21916
|
-
|
|
21917
|
-
|
|
21918
|
-
|
|
21919
|
-
|
|
21920
|
-
VolumeDivergenceStrategyCreator = (0,
|
|
22797
|
+
import_strategies18 = require("@tradejs/node/strategies");
|
|
22798
|
+
init_config7();
|
|
22799
|
+
init_core7();
|
|
22800
|
+
init_manifest7();
|
|
22801
|
+
VolumeDivergenceStrategyCreator = (0, import_strategies18.createStrategyRuntime)({
|
|
21921
22802
|
strategyName: "VolumeDivergence",
|
|
21922
|
-
defaults:
|
|
22803
|
+
defaults: config7,
|
|
21923
22804
|
createCore: createVolumeDivergenceCore,
|
|
21924
22805
|
manifest: volumeDivergenceManifest,
|
|
21925
22806
|
strategyDirectory: __dirname
|
|
@@ -21931,33 +22812,47 @@ var init_strategy6 = __esm({
|
|
|
21931
22812
|
var index_exports = {};
|
|
21932
22813
|
__export(index_exports, {
|
|
21933
22814
|
adaptiveMomentumRibbonAiAdapter: () => adaptiveMomentumRibbonAiAdapter,
|
|
22815
|
+
adaptiveMomentumRibbonDefaultConfig: () => config,
|
|
21934
22816
|
adaptiveMomentumRibbonMlAdapter: () => adaptiveMomentumRibbonMlAdapter,
|
|
22817
|
+
breakoutDefaultConfig: () => config2,
|
|
21935
22818
|
default: () => index_default,
|
|
22819
|
+
getBuiltInStrategyDefaultConfig: () => getBuiltInStrategyDefaultConfig,
|
|
21936
22820
|
maStrategyAiAdapter: () => maStrategyAiAdapter,
|
|
22821
|
+
maStrategyDefaultConfig: () => config3,
|
|
21937
22822
|
maStrategyMlAdapter: () => maStrategyMlAdapter,
|
|
21938
22823
|
reverseTrendLineAiAdapter: () => reverseTrendLineAiAdapter,
|
|
21939
|
-
reverseTrendLineDefaultConfig: () =>
|
|
22824
|
+
reverseTrendLineDefaultConfig: () => config4,
|
|
21940
22825
|
strategyEntries: () => strategyEntries,
|
|
21941
|
-
trendLineDefaultConfig: () =>
|
|
22826
|
+
trendLineDefaultConfig: () => config6,
|
|
22827
|
+
trendShiftAiAdapter: () => trendShiftAiAdapter,
|
|
22828
|
+
trendShiftDefaultConfig: () => config5,
|
|
21942
22829
|
volumeDivergenceAiAdapter: () => volumeDivergenceAiAdapter,
|
|
22830
|
+
volumeDivergenceDefaultConfig: () => config7,
|
|
21943
22831
|
volumeDivergenceMlAdapter: () => volumeDivergenceMlAdapter
|
|
21944
22832
|
});
|
|
21945
22833
|
module.exports = __toCommonJS(index_exports);
|
|
21946
|
-
var
|
|
22834
|
+
var import_config8 = require("@tradejs/core/config");
|
|
21947
22835
|
init_manifest();
|
|
21948
22836
|
init_manifest2();
|
|
21949
22837
|
init_manifest3();
|
|
21950
22838
|
init_manifest4();
|
|
21951
22839
|
init_manifest5();
|
|
21952
22840
|
init_manifest6();
|
|
22841
|
+
init_manifest7();
|
|
21953
22842
|
init_config();
|
|
21954
22843
|
init_config2();
|
|
22844
|
+
init_config3();
|
|
22845
|
+
init_config4();
|
|
22846
|
+
init_config5();
|
|
22847
|
+
init_config6();
|
|
22848
|
+
init_config7();
|
|
21955
22849
|
init_ai();
|
|
21956
22850
|
init_ml();
|
|
21957
22851
|
init_ai3();
|
|
21958
22852
|
init_ml3();
|
|
21959
22853
|
init_ai4();
|
|
21960
|
-
|
|
22854
|
+
init_ai5();
|
|
22855
|
+
init_ai7();
|
|
21961
22856
|
init_ml5();
|
|
21962
22857
|
var createLazyStrategyCreator = (loader, exportName) => {
|
|
21963
22858
|
return async (params) => {
|
|
@@ -21987,46 +22882,70 @@ var strategyEntries = [
|
|
|
21987
22882
|
)
|
|
21988
22883
|
},
|
|
21989
22884
|
{
|
|
21990
|
-
manifest:
|
|
22885
|
+
manifest: trendShiftManifest,
|
|
21991
22886
|
creator: createLazyStrategyCreator(
|
|
21992
22887
|
() => Promise.resolve().then(() => (init_strategy3(), strategy_exports3)),
|
|
22888
|
+
"TrendShiftStrategyCreator"
|
|
22889
|
+
)
|
|
22890
|
+
},
|
|
22891
|
+
{
|
|
22892
|
+
manifest: reverseTrendLineManifest,
|
|
22893
|
+
creator: createLazyStrategyCreator(
|
|
22894
|
+
() => Promise.resolve().then(() => (init_strategy4(), strategy_exports4)),
|
|
21993
22895
|
"ReverseTrendLineStrategyCreator"
|
|
21994
22896
|
)
|
|
21995
22897
|
},
|
|
21996
22898
|
{
|
|
21997
22899
|
manifest: maStrategyManifest,
|
|
21998
22900
|
creator: createLazyStrategyCreator(
|
|
21999
|
-
() => Promise.resolve().then(() => (
|
|
22901
|
+
() => Promise.resolve().then(() => (init_strategy5(), strategy_exports5)),
|
|
22000
22902
|
"MaStrategyCreator"
|
|
22001
22903
|
)
|
|
22002
22904
|
},
|
|
22003
22905
|
{
|
|
22004
22906
|
manifest: adaptiveMomentumRibbonManifest,
|
|
22005
22907
|
creator: createLazyStrategyCreator(
|
|
22006
|
-
() => Promise.resolve().then(() => (
|
|
22908
|
+
() => Promise.resolve().then(() => (init_strategy6(), strategy_exports6)),
|
|
22007
22909
|
"AdaptiveMomentumRibbonStrategyCreator"
|
|
22008
22910
|
)
|
|
22009
22911
|
},
|
|
22010
22912
|
{
|
|
22011
22913
|
manifest: volumeDivergenceManifest,
|
|
22012
22914
|
creator: createLazyStrategyCreator(
|
|
22013
|
-
() => Promise.resolve().then(() => (
|
|
22915
|
+
() => Promise.resolve().then(() => (init_strategy7(), strategy_exports7)),
|
|
22014
22916
|
"VolumeDivergenceStrategyCreator"
|
|
22015
22917
|
)
|
|
22016
22918
|
}
|
|
22017
22919
|
];
|
|
22018
|
-
var
|
|
22920
|
+
var builtInStrategyDefaultConfigs = {
|
|
22921
|
+
Breakout: config2,
|
|
22922
|
+
TrendLine: config6,
|
|
22923
|
+
TrendShift: config5,
|
|
22924
|
+
ReverseTrendLine: config4,
|
|
22925
|
+
MaStrategy: config3,
|
|
22926
|
+
AdaptiveMomentumRibbon: config,
|
|
22927
|
+
VolumeDivergence: config7
|
|
22928
|
+
};
|
|
22929
|
+
var getBuiltInStrategyDefaultConfig = (strategyName) => builtInStrategyDefaultConfigs[strategyName];
|
|
22930
|
+
var index_default = (0, import_config8.defineStrategyPlugin)({ strategyEntries });
|
|
22019
22931
|
// Annotate the CommonJS export names for ESM import in node:
|
|
22020
22932
|
0 && (module.exports = {
|
|
22021
22933
|
adaptiveMomentumRibbonAiAdapter,
|
|
22934
|
+
adaptiveMomentumRibbonDefaultConfig,
|
|
22022
22935
|
adaptiveMomentumRibbonMlAdapter,
|
|
22936
|
+
breakoutDefaultConfig,
|
|
22937
|
+
getBuiltInStrategyDefaultConfig,
|
|
22023
22938
|
maStrategyAiAdapter,
|
|
22939
|
+
maStrategyDefaultConfig,
|
|
22024
22940
|
maStrategyMlAdapter,
|
|
22025
22941
|
reverseTrendLineAiAdapter,
|
|
22026
22942
|
reverseTrendLineDefaultConfig,
|
|
22027
22943
|
strategyEntries,
|
|
22028
22944
|
trendLineDefaultConfig,
|
|
22945
|
+
trendShiftAiAdapter,
|
|
22946
|
+
trendShiftDefaultConfig,
|
|
22029
22947
|
volumeDivergenceAiAdapter,
|
|
22948
|
+
volumeDivergenceDefaultConfig,
|
|
22030
22949
|
volumeDivergenceMlAdapter
|
|
22031
22950
|
});
|
|
22032
22951
|
/*! Bundled license information:
|