@tradejs/strategies 1.0.5 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,547 @@
1
+ // src/AdaptiveMomentumRibbon/adapters/ai.ts
2
+ import { mapAiRuntimeFromConfig } from "@tradejs/core/strategies";
3
+ var ADAPTIVE_MOMENTUM_RIBBON_CONTEXT_PROMPT = `
4
+ AdaptiveMomentumRibbon addon:
5
+ - This is a momentum entry based on an oscillator zero-cross, not a trendline breakout and not a line-reversal setup.
6
+ - LONG appears when \`signalOsc\` crosses above 0 and the ribbon switches into \`activeBuy\`; SHORT is the mirror case.
7
+ - \`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.
8
+ - \`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\`.
9
+ - \`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.
10
+ - \`quality=5\` requires very clean momentum: correct channel side, strong \`signalOsc\`, sane invalidation distance, and no coin/BTC bias conflicts.
11
+ - If \`approvalAllowedNow=false\` or \`deterministicQuality<4\`, this is usually watch mode rather than a ready live approval.
12
+ `;
13
+ var ADAPTIVE_MOMENTUM_RIBBON_PAYLOAD_PROMPT = `
14
+ - \`payload.additionalIndicators.adaptiveMomentumRibbonContext\` contains a compact signal summary:
15
+ signalOsc / oscillatorStrength / channelState / channelExtensionPct / invalidationDistancePct / structuralRewardRiskRatio / coinBiasAligned / btcBiasAligned / deterministicQuality / approvalAllowedNow / structuralHardBlockReasons.
16
+ - Use this context as the primary strategy-specific interpretation instead of re-deriving it only from generic series.
17
+ `;
18
+ var toFiniteNumberOrNull = (value) => {
19
+ if (typeof value === "number" && Number.isFinite(value)) {
20
+ return value;
21
+ }
22
+ if (typeof value === "string" && value.trim()) {
23
+ const parsed = Number(value);
24
+ return Number.isFinite(parsed) ? parsed : null;
25
+ }
26
+ return null;
27
+ };
28
+ var getLastFiniteNumber = (value) => {
29
+ if (Array.isArray(value)) {
30
+ for (let i = value.length - 1; i >= 0; i -= 1) {
31
+ const item = toFiniteNumberOrNull(value[i]);
32
+ if (item != null) {
33
+ return item;
34
+ }
35
+ }
36
+ return null;
37
+ }
38
+ return toFiniteNumberOrNull(value);
39
+ };
40
+ var getBias = (fast, slow) => {
41
+ if (fast == null || slow == null) {
42
+ return null;
43
+ }
44
+ if (fast > slow) {
45
+ return "bullish";
46
+ }
47
+ if (fast < slow) {
48
+ return "bearish";
49
+ }
50
+ return null;
51
+ };
52
+ var getSignalDirection = (signal) => signal.direction === "LONG" || signal.direction === "SHORT" ? signal.direction : null;
53
+ var asBoolean = (value) => value === true || value === 1;
54
+ var getRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
55
+ var getPrimarySession = (signal, additionalIndicators) => {
56
+ const marketContext = getRecord(
57
+ additionalIndicators?.marketContext ?? signal.additionalIndicators?.marketContext
58
+ );
59
+ const tradingSession = getRecord(marketContext?.tradingSession);
60
+ const primarySession = tradingSession?.primarySession;
61
+ if (primarySession === "asia" || primarySession === "europe" || primarySession === "us" || primarySession === "off_hours") {
62
+ return primarySession;
63
+ }
64
+ const timestamp = toFiniteNumberOrNull(signal.timestamp);
65
+ if (timestamp == null) {
66
+ return null;
67
+ }
68
+ const date = new Date(timestamp);
69
+ const minuteUtc = date.getUTCHours() * 60 + date.getUTCMinutes();
70
+ const activeSessions = [
71
+ minuteUtc >= 0 && minuteUtc < 8 * 60 ? "asia" : null,
72
+ minuteUtc >= 7 * 60 && minuteUtc < 16 * 60 ? "europe" : null,
73
+ minuteUtc >= 13 * 60 && minuteUtc < 22 * 60 ? "us" : null
74
+ ].filter(
75
+ (session) => session != null
76
+ );
77
+ if (activeSessions.includes("us")) {
78
+ return "us";
79
+ }
80
+ if (activeSessions.includes("europe")) {
81
+ return "europe";
82
+ }
83
+ if (activeSessions.includes("asia")) {
84
+ return "asia";
85
+ }
86
+ return "off_hours";
87
+ };
88
+ var getAdaptiveMomentumRibbonSnapshot = (signal) => {
89
+ const additional = getRecord(signal.additionalIndicators);
90
+ const amr = additional?.amr;
91
+ return amr && typeof amr === "object" ? amr : {};
92
+ };
93
+ var getAdaptiveMomentumRibbonConfigSnapshot = (signal) => {
94
+ const additional = getRecord(signal.additionalIndicators);
95
+ return getRecord(additional?.amrConfigSnapshot);
96
+ };
97
+ var isAtLeast = (value, threshold) => value != null && value >= threshold;
98
+ var isInRange = (value, min, max) => value != null && value >= min && value <= max;
99
+ var getDirectionalInvalidationDistancePct = ({
100
+ signalDirection,
101
+ currentPrice,
102
+ invalidationLevel
103
+ }) => {
104
+ if (signalDirection == null || currentPrice == null || currentPrice <= 0 || invalidationLevel == null) {
105
+ return null;
106
+ }
107
+ if (signalDirection === "LONG") {
108
+ if (invalidationLevel >= currentPrice) {
109
+ return null;
110
+ }
111
+ return (currentPrice - invalidationLevel) / currentPrice * 100;
112
+ }
113
+ if (invalidationLevel <= currentPrice) {
114
+ return null;
115
+ }
116
+ return (invalidationLevel - currentPrice) / currentPrice * 100;
117
+ };
118
+ var getDirectionalRewardPct = ({
119
+ signalDirection,
120
+ currentPrice,
121
+ takeProfitPrice
122
+ }) => {
123
+ if (signalDirection == null || currentPrice == null || currentPrice <= 0 || takeProfitPrice == null) {
124
+ return null;
125
+ }
126
+ return signalDirection === "LONG" ? (takeProfitPrice - currentPrice) / currentPrice * 100 : (currentPrice - takeProfitPrice) / currentPrice * 100;
127
+ };
128
+ var getDirectionalChannelExtensionPct = ({
129
+ signalDirection,
130
+ currentPrice,
131
+ kcUpper,
132
+ kcLower
133
+ }) => {
134
+ if (signalDirection == null || currentPrice == null || currentPrice <= 0) {
135
+ return null;
136
+ }
137
+ if (signalDirection === "LONG") {
138
+ if (kcUpper == null || currentPrice <= kcUpper) {
139
+ return null;
140
+ }
141
+ return (currentPrice - kcUpper) / currentPrice * 100;
142
+ }
143
+ if (kcLower == null || currentPrice >= kcLower) {
144
+ return null;
145
+ }
146
+ return (kcLower - currentPrice) / currentPrice * 100;
147
+ };
148
+ var getChannelState = ({
149
+ signalDirection,
150
+ currentPrice,
151
+ kcMidline,
152
+ kcUpper,
153
+ kcLower
154
+ }) => {
155
+ if (signalDirection == null || currentPrice == null || kcMidline == null) {
156
+ return "unknown";
157
+ }
158
+ if (signalDirection === "LONG") {
159
+ if (kcUpper != null && currentPrice >= kcUpper) {
160
+ return "above_upper";
161
+ }
162
+ if (currentPrice >= kcMidline) {
163
+ return "inside_channel";
164
+ }
165
+ if (kcLower != null && currentPrice <= kcLower) {
166
+ return "below_lower";
167
+ }
168
+ return "below_midline";
169
+ }
170
+ if (kcLower != null && currentPrice <= kcLower) {
171
+ return "below_lower";
172
+ }
173
+ if (currentPrice <= kcMidline) {
174
+ return "inside_channel";
175
+ }
176
+ if (kcUpper != null && currentPrice >= kcUpper) {
177
+ return "above_upper";
178
+ }
179
+ return "above_midline";
180
+ };
181
+ var getRetestPrice = (context) => {
182
+ if (context.signalDirection === "LONG") {
183
+ if (context.kcMidline != null && context.channelState === "below_midline") {
184
+ return context.kcMidline;
185
+ }
186
+ return context.kcUpper ?? context.kcMidline ?? context.invalidationLevel;
187
+ }
188
+ if (context.signalDirection === "SHORT") {
189
+ if (context.kcMidline != null && context.channelState === "above_midline") {
190
+ return context.kcMidline;
191
+ }
192
+ return context.kcLower ?? context.kcMidline ?? context.invalidationLevel;
193
+ }
194
+ return null;
195
+ };
196
+ var getDeterministicAdaptiveMomentumRibbonQuality = (context) => {
197
+ if (context.hardBlockReasons.length > 0) {
198
+ return 2;
199
+ }
200
+ const biasConflictCount = Number(context.coinBiasAligned === false) + Number(context.btcBiasAligned === false);
201
+ const noBiasConflict = biasConflictCount === 0;
202
+ const oscillatorModerate = isAtLeast(context.oscillatorStrength, 0.3);
203
+ const oscillatorStrong = isAtLeast(context.oscillatorStrength, 0.55);
204
+ const oscillatorElite = isAtLeast(context.oscillatorStrength, 0.9);
205
+ const invalidationCompact = isInRange(
206
+ context.invalidationDistancePct,
207
+ 0.15,
208
+ 1.8
209
+ );
210
+ const invalidationTight = isInRange(
211
+ context.invalidationDistancePct,
212
+ 0.2,
213
+ 1.25
214
+ );
215
+ const structuralRrModerate = isAtLeast(
216
+ context.structuralRewardRiskRatio,
217
+ 1.2
218
+ );
219
+ const structuralRrStrong = isAtLeast(context.structuralRewardRiskRatio, 1.8);
220
+ const channelSupportive = context.channelBiasAligned === true;
221
+ const channelExpansion = context.signalDirection === "LONG" ? context.channelState === "above_upper" : context.channelState === "below_lower";
222
+ const channelExtensionStrong = isAtLeast(context.channelExtensionPct, 0.08);
223
+ const sessionAllowsApproval = context.sessionAllowsApproval !== false;
224
+ const slowestDetector = context.momentumPeriod === 48 && context.butterworthSmoothing === 6;
225
+ if (!sessionAllowsApproval) {
226
+ return 3;
227
+ }
228
+ if (channelSupportive && channelExpansion && channelExtensionStrong && oscillatorElite && invalidationTight && structuralRrStrong && noBiasConflict) {
229
+ return 5;
230
+ }
231
+ if (channelSupportive && channelExpansion && !slowestDetector && oscillatorModerate && invalidationCompact && structuralRrModerate && biasConflictCount < 2 && (biasConflictCount === 0 || oscillatorStrong)) {
232
+ return 4;
233
+ }
234
+ return 3;
235
+ };
236
+ var getHardBlockReasonText = (reason) => {
237
+ switch (reason) {
238
+ case "invalidated":
239
+ return "the signal is already invalidated relative to invalidationLevel";
240
+ case "inactive_signal_state":
241
+ return "the active ribbon state does not confirm the current direction";
242
+ case "oscillator_conflict":
243
+ return "signalOsc conflicts with the signal direction";
244
+ case "invalidation_wrong_side":
245
+ return "invalidationLevel is on the wrong side of the current price";
246
+ default:
247
+ return reason;
248
+ }
249
+ };
250
+ var buildAdaptiveMomentumRibbonContext = (signal, additionalIndicators) => {
251
+ const signalDirection = getSignalDirection(signal);
252
+ const snapshot = getAdaptiveMomentumRibbonSnapshot(signal);
253
+ const configSnapshot = getAdaptiveMomentumRibbonConfigSnapshot(signal);
254
+ const momentumPeriod = toFiniteNumberOrNull(configSnapshot?.momentumPeriod);
255
+ const butterworthSmoothing = toFiniteNumberOrNull(
256
+ configSnapshot?.butterworthSmoothing
257
+ );
258
+ const currentPrice = toFiniteNumberOrNull(signal.prices?.currentPrice);
259
+ const takeProfitPrice = toFiniteNumberOrNull(signal.prices?.takeProfitPrice);
260
+ const signalOsc = toFiniteNumberOrNull(snapshot.signalOsc);
261
+ const oscillatorStrength = signalOsc != null ? Math.abs(signalOsc) : null;
262
+ const kcMidline = toFiniteNumberOrNull(snapshot.kcMidline);
263
+ const kcUpper = toFiniteNumberOrNull(snapshot.kcUpper);
264
+ const kcLower = toFiniteNumberOrNull(snapshot.kcLower);
265
+ const invalidationLevel = toFiniteNumberOrNull(snapshot.invalidationLevel);
266
+ const entryLong = asBoolean(snapshot.entryLong);
267
+ const entryShort = asBoolean(snapshot.entryShort);
268
+ const activeBuy = asBoolean(snapshot.activeBuy);
269
+ const activeSell = asBoolean(snapshot.activeSell);
270
+ const invalidated = asBoolean(snapshot.invalidated);
271
+ const channelState = getChannelState({
272
+ signalDirection,
273
+ currentPrice,
274
+ kcMidline,
275
+ kcUpper,
276
+ kcLower
277
+ });
278
+ const channelBiasAligned = signalDirection === "LONG" ? kcMidline != null && currentPrice != null ? currentPrice >= kcMidline : null : signalDirection === "SHORT" ? kcMidline != null && currentPrice != null ? currentPrice <= kcMidline : null : null;
279
+ const channelExtensionPct = getDirectionalChannelExtensionPct({
280
+ signalDirection,
281
+ currentPrice,
282
+ kcUpper,
283
+ kcLower
284
+ });
285
+ const invalidationDistancePct = getDirectionalInvalidationDistancePct({
286
+ signalDirection,
287
+ currentPrice,
288
+ invalidationLevel
289
+ });
290
+ const rewardPct = getDirectionalRewardPct({
291
+ signalDirection,
292
+ currentPrice,
293
+ takeProfitPrice
294
+ });
295
+ const structuralRewardRiskRatio = rewardPct != null && invalidationDistancePct != null && invalidationDistancePct > 0 ? rewardPct / invalidationDistancePct : null;
296
+ const coinBias = getBias(
297
+ getLastFiniteNumber(signal.indicators?.maFast),
298
+ getLastFiniteNumber(signal.indicators?.maSlow)
299
+ );
300
+ const btcBias = getBias(
301
+ getLastFiniteNumber(signal.indicators?.btcMaFast),
302
+ getLastFiniteNumber(signal.indicators?.btcMaSlow)
303
+ );
304
+ const coinBiasAligned = signalDirection === "LONG" ? coinBias == null ? null : coinBias === "bullish" : signalDirection === "SHORT" ? coinBias == null ? null : coinBias === "bearish" : null;
305
+ const btcBiasAligned = signalDirection === "LONG" ? btcBias == null ? null : btcBias === "bullish" : signalDirection === "SHORT" ? btcBias == null ? null : btcBias === "bearish" : null;
306
+ const primarySession = getPrimarySession(signal, additionalIndicators);
307
+ const sessionAllowsApproval = primarySession == null ? null : primarySession === "off_hours";
308
+ const hardBlockReasons = [];
309
+ if (invalidated) {
310
+ hardBlockReasons.push("invalidated");
311
+ }
312
+ if (signalDirection === "LONG" && (!entryLong || !activeBuy) || signalDirection === "SHORT" && (!entryShort || !activeSell)) {
313
+ hardBlockReasons.push("inactive_signal_state");
314
+ }
315
+ if (signalDirection === "LONG" && !isAtLeast(signalOsc, Number.EPSILON) || signalDirection === "SHORT" && !(signalOsc != null && signalOsc < -Number.EPSILON)) {
316
+ hardBlockReasons.push("oscillator_conflict");
317
+ }
318
+ if (signalDirection != null && invalidationLevel != null && currentPrice != null && (signalDirection === "LONG" && invalidationLevel >= currentPrice || signalDirection === "SHORT" && invalidationLevel <= currentPrice)) {
319
+ hardBlockReasons.push("invalidation_wrong_side");
320
+ }
321
+ const deterministicQuality = getDeterministicAdaptiveMomentumRibbonQuality({
322
+ signalDirection,
323
+ momentumPeriod,
324
+ butterworthSmoothing,
325
+ entryLong,
326
+ entryShort,
327
+ activeBuy,
328
+ activeSell,
329
+ invalidated,
330
+ signalOsc,
331
+ oscillatorStrength,
332
+ kcMidline,
333
+ kcUpper,
334
+ kcLower,
335
+ invalidationLevel,
336
+ channelState,
337
+ channelBiasAligned,
338
+ channelExtensionPct,
339
+ invalidationDistancePct,
340
+ structuralRewardRiskRatio,
341
+ coinMaBias: coinBias,
342
+ btcMaBias: btcBias,
343
+ coinBiasAligned,
344
+ btcBiasAligned,
345
+ primarySession,
346
+ sessionAllowsApproval,
347
+ hardBlockReasons
348
+ });
349
+ return {
350
+ signalDirection,
351
+ momentumPeriod,
352
+ butterworthSmoothing,
353
+ entryLong,
354
+ entryShort,
355
+ activeBuy,
356
+ activeSell,
357
+ invalidated,
358
+ signalOsc,
359
+ oscillatorStrength,
360
+ kcMidline,
361
+ kcUpper,
362
+ kcLower,
363
+ invalidationLevel,
364
+ channelState,
365
+ channelBiasAligned,
366
+ channelExtensionPct,
367
+ invalidationDistancePct,
368
+ structuralRewardRiskRatio,
369
+ coinMaBias: coinBias,
370
+ btcMaBias: btcBias,
371
+ coinBiasAligned,
372
+ btcBiasAligned,
373
+ primarySession,
374
+ sessionAllowsApproval,
375
+ hardBlockReasons,
376
+ structuralHardBlockReasons: hardBlockReasons,
377
+ deterministicQuality,
378
+ approvalAllowedNow: deterministicQuality >= 4,
379
+ maxAllowedQuality: deterministicQuality
380
+ };
381
+ };
382
+ var getAdaptiveMomentumRibbonContextFromPayload = (payload, signal) => {
383
+ const additional = payload.additionalIndicators;
384
+ const context = additional?.adaptiveMomentumRibbonContext;
385
+ return context && typeof context === "object" ? context : buildAdaptiveMomentumRibbonContext(signal);
386
+ };
387
+ var clampQuality = (value, maxAllowedQuality) => {
388
+ const resolved = typeof value === "number" ? value : maxAllowedQuality;
389
+ return Math.max(1, Math.min(maxAllowedQuality, Math.round(resolved)));
390
+ };
391
+ var postProcessAnalysis = ({
392
+ signal,
393
+ payload,
394
+ analysis
395
+ }) => {
396
+ const context = getAdaptiveMomentumRibbonContextFromPayload(payload, signal);
397
+ const signalDirection = getSignalDirection(signal);
398
+ const requestedDirection = analysis.direction === signalDirection ? signalDirection : null;
399
+ const finalDirection = requestedDirection != null && context.approvalAllowedNow ? requestedDirection : null;
400
+ const finalQuality = clampQuality(
401
+ typeof analysis.quality === "number" ? analysis.quality : context.deterministicQuality,
402
+ context.maxAllowedQuality
403
+ );
404
+ const needRetest = finalDirection == null;
405
+ const retestPrice = needRetest ? getRetestPrice(context) : null;
406
+ if (finalDirection == null) {
407
+ return {
408
+ ...analysis,
409
+ direction: null,
410
+ quality: finalQuality,
411
+ needRetest: true,
412
+ retestPrice,
413
+ takeProfitPrice: null,
414
+ stopLossPrice: null,
415
+ 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."),
416
+ 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."),
417
+ 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.")
418
+ };
419
+ }
420
+ return {
421
+ ...analysis,
422
+ direction: finalDirection,
423
+ quality: finalQuality,
424
+ needRetest: false,
425
+ retestPrice: null,
426
+ takeProfitPrice: signal.prices?.takeProfitPrice ?? null,
427
+ stopLossPrice: signal.prices?.stopLossPrice ?? null
428
+ };
429
+ };
430
+ var adaptiveMomentumRibbonAiAdapter = {
431
+ buildPayload: ({ signal, basePayload }) => {
432
+ const additionalIndicators = getRecord(basePayload.additionalIndicators);
433
+ return {
434
+ ...basePayload,
435
+ additionalIndicators: {
436
+ ...additionalIndicators ?? {},
437
+ adaptiveMomentumRibbonContext: buildAdaptiveMomentumRibbonContext(
438
+ signal,
439
+ additionalIndicators
440
+ )
441
+ }
442
+ };
443
+ },
444
+ postProcessAnalysis,
445
+ buildSystemPromptAddon: () => `${ADAPTIVE_MOMENTUM_RIBBON_CONTEXT_PROMPT}
446
+ ${ADAPTIVE_MOMENTUM_RIBBON_PAYLOAD_PROMPT}`,
447
+ buildHumanPromptAddon: ({ signal, payload }) => {
448
+ const context = getAdaptiveMomentumRibbonContextFromPayload(
449
+ payload,
450
+ signal
451
+ );
452
+ return `
453
+
454
+ Additional AdaptiveMomentumRibbon context:
455
+ - momentumPeriod=${context.momentumPeriod ?? "n/a"}
456
+ - butterworthSmoothing=${context.butterworthSmoothing ?? "n/a"}
457
+ - signalOsc=${context.signalOsc?.toFixed?.(3) ?? "n/a"}
458
+ - oscillatorStrength=${context.oscillatorStrength?.toFixed?.(3) ?? "n/a"}
459
+ - channelState=${context.channelState}
460
+ - channelBiasAligned=${context.channelBiasAligned}
461
+ - channelExtensionPct=${context.channelExtensionPct?.toFixed?.(3) ?? "n/a"}%
462
+ - invalidationDistancePct=${context.invalidationDistancePct?.toFixed?.(3) ?? "n/a"}%
463
+ - structuralRewardRiskRatio=${context.structuralRewardRiskRatio?.toFixed?.(3) ?? "n/a"}
464
+ - coinBiasAligned=${context.coinBiasAligned}
465
+ - btcBiasAligned=${context.btcBiasAligned}
466
+ - primarySession=${context.primarySession ?? "n/a"}
467
+ - sessionAllowsApproval=${context.sessionAllowsApproval}
468
+ - deterministicQuality=${context.deterministicQuality}
469
+ - approvalAllowedNow=${context.approvalAllowedNow}
470
+ - hardBlockReasons=${context.hardBlockReasons.join(", ") || "none"}
471
+
472
+ Interpretation rules for AdaptiveMomentumRibbon:
473
+ - a zero-cross alone does not make quality high;
474
+ - pay attention to Keltner channel side, sane invalidation distance, and bias alignment;
475
+ - if \`signalOsc\` already conflicts with direction or the signal is invalidated, do not treat the entry as confirmed.
476
+ `;
477
+ },
478
+ mapEntryRuntimeFromConfig: (config2) => mapAiRuntimeFromConfig(
479
+ config2
480
+ )
481
+ };
482
+
483
+ // src/AdaptiveMomentumRibbon/adapters/ml.ts
484
+ import { mapMlRuntimeFromConfig } from "@tradejs/core/strategies";
485
+ var adaptiveMomentumRibbonMlAdapter = {
486
+ mapEntryRuntimeFromConfig: (config2) => mapMlRuntimeFromConfig(
487
+ config2
488
+ )
489
+ };
490
+
491
+ // src/AdaptiveMomentumRibbon/manifest.ts
492
+ var adaptiveMomentumRibbonManifest = {
493
+ name: "AdaptiveMomentumRibbon",
494
+ aiAdapter: adaptiveMomentumRibbonAiAdapter,
495
+ mlAdapter: adaptiveMomentumRibbonMlAdapter
496
+ };
497
+
498
+ // src/AdaptiveMomentumRibbon/config.ts
499
+ var config = {
500
+ ENV: "BACKTEST",
501
+ INTERVAL: "15",
502
+ MAKE_ORDERS: true,
503
+ CLOSE_OPPOSITE_POSITIONS: false,
504
+ BACKTEST_PRICE_MODE: "mid",
505
+ AI_ENABLED: false,
506
+ AI_MODE: "llm",
507
+ ML_ENABLED: false,
508
+ ML_THRESHOLD: 0.1,
509
+ MIN_AI_QUALITY: 3,
510
+ FEE_PERCENT: 5e-3,
511
+ MAX_LOSS_VALUE: 10,
512
+ AMR_LOOKBACK_BARS: 200,
513
+ AMR_MOMENTUM_PERIOD: 32,
514
+ AMR_BUTTERWORTH_SMOOTHING: 4,
515
+ AMR_WAIT_CLOSE: true,
516
+ AMR_CONFIRM_ON_NEXT_BAR: true,
517
+ AMR_MIN_SIGNAL_OSC_ABS: 0.55,
518
+ AMR_REQUIRE_KC_BIAS: true,
519
+ AMR_MIN_BARS_BETWEEN_SIGNALS: 12,
520
+ AMR_SHOW_INVALIDATION_LEVELS: true,
521
+ AMR_SHOW_KELTNER_CHANNEL: true,
522
+ AMR_KC_LENGTH: 20,
523
+ AMR_KC_MA_TYPE: "EMA",
524
+ AMR_ATR_LENGTH: 14,
525
+ AMR_ATR_MULTIPLIER: 2,
526
+ AMR_EXIT_ON_INVALIDATION: true,
527
+ AMR_LINE_PLOTS: ["kcMidline", "kcUpper", "kcLower", "invalidationLevel"],
528
+ LONG: {
529
+ enable: true,
530
+ direction: "LONG",
531
+ TP: 2,
532
+ SL: 1
533
+ },
534
+ SHORT: {
535
+ enable: true,
536
+ direction: "SHORT",
537
+ TP: 2,
538
+ SL: 1
539
+ }
540
+ };
541
+
542
+ export {
543
+ adaptiveMomentumRibbonAiAdapter,
544
+ adaptiveMomentumRibbonMlAdapter,
545
+ adaptiveMomentumRibbonManifest,
546
+ config
547
+ };