@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
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// src/Breakout/adapters/ai.ts
|
|
2
|
+
var breakoutAiAdapter = {};
|
|
3
|
+
|
|
4
|
+
// src/Breakout/adapters/ml.ts
|
|
5
|
+
import { mapMlRuntimeFromConfig } from "@tradejs/core/strategies";
|
|
6
|
+
var breakoutMlAdapter = {
|
|
7
|
+
mapEntryRuntimeFromConfig: (config2) => mapMlRuntimeFromConfig(config2)
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
// src/Breakout/manifest.ts
|
|
11
|
+
var breakoutManifest = {
|
|
12
|
+
name: "Breakout",
|
|
13
|
+
entryRuntimeDefaults: {
|
|
14
|
+
ai: {
|
|
15
|
+
enabled: false
|
|
16
|
+
},
|
|
17
|
+
ml: {
|
|
18
|
+
enabled: false
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
aiAdapter: breakoutAiAdapter,
|
|
22
|
+
mlAdapter: breakoutMlAdapter
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// src/Breakout/config.ts
|
|
26
|
+
var config = {
|
|
27
|
+
ML_ENABLED: false,
|
|
28
|
+
MA_FAST: 49,
|
|
29
|
+
MA_MEDIUM: 49,
|
|
30
|
+
MA_SLOW: 99,
|
|
31
|
+
OBV_SMA: 10,
|
|
32
|
+
ATR: 14,
|
|
33
|
+
ATR_PCT_SHORT: 7,
|
|
34
|
+
ATR_PCT_LONG: 30,
|
|
35
|
+
BB: 20,
|
|
36
|
+
BB_STD: 2,
|
|
37
|
+
MACD_FAST: 12,
|
|
38
|
+
MACD_SLOW: 26,
|
|
39
|
+
MACD_SIGNAL: 9,
|
|
40
|
+
LEVEL_LOOKBACK: 20,
|
|
41
|
+
LEVEL_DELAY: 2,
|
|
42
|
+
ATR_PERIOD: 14,
|
|
43
|
+
BB_PERIOD: 20,
|
|
44
|
+
BB_STDDEV: 2,
|
|
45
|
+
LIMIT: 100,
|
|
46
|
+
ATR_OPEN: 0.5,
|
|
47
|
+
ATR_CLOSE: 1.5,
|
|
48
|
+
OBV_SMA_PERIOD: 10,
|
|
49
|
+
BREAKOUT_LOOKBACK_DELAY: 2,
|
|
50
|
+
BREAKOUT_LOOKBACK: 20,
|
|
51
|
+
REQUIRED_SCORE_LONG: 7,
|
|
52
|
+
REQUIRED_SCORE_SHORT: 7,
|
|
53
|
+
SIGNALS_LONG: {
|
|
54
|
+
VOLATILE: {
|
|
55
|
+
weight: 1,
|
|
56
|
+
required: true
|
|
57
|
+
},
|
|
58
|
+
SMA_UPTREND: {
|
|
59
|
+
weight: 1,
|
|
60
|
+
required: true
|
|
61
|
+
},
|
|
62
|
+
OBV_ABOVE_SMA: {
|
|
63
|
+
weight: 1,
|
|
64
|
+
required: true
|
|
65
|
+
},
|
|
66
|
+
PREV_HIGH_BREAKOUT: {
|
|
67
|
+
weight: 1,
|
|
68
|
+
required: false
|
|
69
|
+
},
|
|
70
|
+
CLOSE_ABOVE_UPPER_BB: {
|
|
71
|
+
weight: 1,
|
|
72
|
+
required: false
|
|
73
|
+
},
|
|
74
|
+
CLOSE_ABOVE_HIGH_LEVEL: {
|
|
75
|
+
weight: 1,
|
|
76
|
+
required: false
|
|
77
|
+
},
|
|
78
|
+
CLOSE_ABOVE_PREV_CLOSE: {
|
|
79
|
+
weight: 1,
|
|
80
|
+
required: false
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
SIGNALS_SHORT: {
|
|
84
|
+
VOLATILE: {
|
|
85
|
+
weight: 1,
|
|
86
|
+
required: true
|
|
87
|
+
},
|
|
88
|
+
SMA_DOWNTREND: {
|
|
89
|
+
weight: 1,
|
|
90
|
+
required: true
|
|
91
|
+
},
|
|
92
|
+
OBV_BELOW_SMA: {
|
|
93
|
+
weight: 1,
|
|
94
|
+
required: true
|
|
95
|
+
},
|
|
96
|
+
PREV_LOW_BREAKDOWN: {
|
|
97
|
+
weight: 1,
|
|
98
|
+
required: false
|
|
99
|
+
},
|
|
100
|
+
CLOSE_BELOW_LOWER_BB: {
|
|
101
|
+
weight: 1,
|
|
102
|
+
required: false
|
|
103
|
+
},
|
|
104
|
+
CLOSE_BELOW_LOW_LEVEL: {
|
|
105
|
+
weight: 1,
|
|
106
|
+
required: false
|
|
107
|
+
},
|
|
108
|
+
CLOSE_BELOW_PREV_CLOSE: {
|
|
109
|
+
weight: 1,
|
|
110
|
+
required: false
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
TP_LONG: [
|
|
114
|
+
{ profit: 0.1, rate: 0.25 },
|
|
115
|
+
{ profit: 0.15, rate: 0.5 }
|
|
116
|
+
],
|
|
117
|
+
TP_SHORT: [
|
|
118
|
+
{ profit: 0.05, rate: 0.25 },
|
|
119
|
+
{ profit: 0.1, rate: 0.5 }
|
|
120
|
+
],
|
|
121
|
+
SL_LONG: 0.06,
|
|
122
|
+
SL_SHORT: 0.03
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export {
|
|
126
|
+
breakoutManifest,
|
|
127
|
+
config
|
|
128
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// src/MaStrategy/adapters/ai.ts
|
|
2
|
+
import { mapAiRuntimeFromConfig } from "@tradejs/core/strategies";
|
|
3
|
+
var maStrategyAiAdapter = {
|
|
4
|
+
mapEntryRuntimeFromConfig: (config2) => mapAiRuntimeFromConfig(
|
|
5
|
+
config2
|
|
6
|
+
)
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
// src/MaStrategy/adapters/ml.ts
|
|
10
|
+
import { mapMlRuntimeFromConfig } from "@tradejs/core/strategies";
|
|
11
|
+
var maStrategyMlAdapter = {
|
|
12
|
+
mapEntryRuntimeFromConfig: (config2) => mapMlRuntimeFromConfig(
|
|
13
|
+
config2
|
|
14
|
+
)
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// src/MaStrategy/manifest.ts
|
|
18
|
+
var maStrategyManifest = {
|
|
19
|
+
name: "MaStrategy",
|
|
20
|
+
aiAdapter: maStrategyAiAdapter,
|
|
21
|
+
mlAdapter: maStrategyMlAdapter
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// src/MaStrategy/config.ts
|
|
25
|
+
var config = {
|
|
26
|
+
ENV: "BACKTEST",
|
|
27
|
+
INTERVAL: "15",
|
|
28
|
+
MAKE_ORDERS: true,
|
|
29
|
+
CLOSE_OPPOSITE_POSITIONS: false,
|
|
30
|
+
BACKTEST_PRICE_MODE: "mid",
|
|
31
|
+
AI_ENABLED: false,
|
|
32
|
+
AI_MODE: "llm",
|
|
33
|
+
ML_ENABLED: false,
|
|
34
|
+
ML_THRESHOLD: 0.1,
|
|
35
|
+
MIN_AI_QUALITY: 3,
|
|
36
|
+
FEE_PERCENT: 5e-3,
|
|
37
|
+
MAX_LOSS_VALUE: 10,
|
|
38
|
+
TRADE_COOLDOWN_MS: 0,
|
|
39
|
+
MA_FAST: 21,
|
|
40
|
+
MA_SLOW: 55,
|
|
41
|
+
LONG: {
|
|
42
|
+
enable: true,
|
|
43
|
+
direction: "LONG",
|
|
44
|
+
TP: 2,
|
|
45
|
+
SL: 1,
|
|
46
|
+
minRiskRatio: 1.5
|
|
47
|
+
},
|
|
48
|
+
SHORT: {
|
|
49
|
+
enable: true,
|
|
50
|
+
direction: "SHORT",
|
|
51
|
+
TP: 2,
|
|
52
|
+
SL: 1,
|
|
53
|
+
minRiskRatio: 1.5
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export {
|
|
58
|
+
maStrategyAiAdapter,
|
|
59
|
+
maStrategyMlAdapter,
|
|
60
|
+
maStrategyManifest,
|
|
61
|
+
config
|
|
62
|
+
};
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
// src/AdaptiveMomentumRibbon/adapters/ai.ts
|
|
2
2
|
import { mapAiRuntimeFromConfig } from "@tradejs/core/strategies";
|
|
3
3
|
var ADAPTIVE_MOMENTUM_RIBBON_CONTEXT_PROMPT = `
|
|
4
|
-
|
|
5
|
-
-
|
|
6
|
-
- LONG
|
|
7
|
-
- invalidationLevel
|
|
8
|
-
- channelState
|
|
9
|
-
- invalidationDistancePct
|
|
10
|
-
-
|
|
11
|
-
-
|
|
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
12
|
`;
|
|
13
13
|
var ADAPTIVE_MOMENTUM_RIBBON_PAYLOAD_PROMPT = `
|
|
14
|
-
-
|
|
15
|
-
signalOsc / oscillatorStrength / channelState / invalidationDistancePct / structuralRewardRiskRatio / coinBiasAligned / btcBiasAligned / deterministicQuality / approvalAllowedNow / structuralHardBlockReasons.
|
|
16
|
-
-
|
|
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
17
|
`;
|
|
18
18
|
var toFiniteNumberOrNull = (value) => {
|
|
19
19
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
@@ -51,11 +51,49 @@ var getBias = (fast, slow) => {
|
|
|
51
51
|
};
|
|
52
52
|
var getSignalDirection = (signal) => signal.direction === "LONG" || signal.direction === "SHORT" ? signal.direction : null;
|
|
53
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
|
+
};
|
|
54
88
|
var getAdaptiveMomentumRibbonSnapshot = (signal) => {
|
|
55
|
-
const additional = signal.additionalIndicators;
|
|
89
|
+
const additional = getRecord(signal.additionalIndicators);
|
|
56
90
|
const amr = additional?.amr;
|
|
57
91
|
return amr && typeof amr === "object" ? amr : {};
|
|
58
92
|
};
|
|
93
|
+
var getAdaptiveMomentumRibbonConfigSnapshot = (signal) => {
|
|
94
|
+
const additional = getRecord(signal.additionalIndicators);
|
|
95
|
+
return getRecord(additional?.amrConfigSnapshot);
|
|
96
|
+
};
|
|
59
97
|
var isAtLeast = (value, threshold) => value != null && value >= threshold;
|
|
60
98
|
var isInRange = (value, min, max) => value != null && value >= min && value <= max;
|
|
61
99
|
var getDirectionalInvalidationDistancePct = ({
|
|
@@ -87,6 +125,26 @@ var getDirectionalRewardPct = ({
|
|
|
87
125
|
}
|
|
88
126
|
return signalDirection === "LONG" ? (takeProfitPrice - currentPrice) / currentPrice * 100 : (currentPrice - takeProfitPrice) / currentPrice * 100;
|
|
89
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
|
+
};
|
|
90
148
|
var getChannelState = ({
|
|
91
149
|
signalDirection,
|
|
92
150
|
currentPrice,
|
|
@@ -139,8 +197,8 @@ var getDeterministicAdaptiveMomentumRibbonQuality = (context) => {
|
|
|
139
197
|
if (context.hardBlockReasons.length > 0) {
|
|
140
198
|
return 2;
|
|
141
199
|
}
|
|
142
|
-
const noBiasConflict = context.coinBiasAligned === true && context.btcBiasAligned === true;
|
|
143
200
|
const biasConflictCount = Number(context.coinBiasAligned === false) + Number(context.btcBiasAligned === false);
|
|
201
|
+
const noBiasConflict = biasConflictCount === 0;
|
|
144
202
|
const oscillatorModerate = isAtLeast(context.oscillatorStrength, 0.3);
|
|
145
203
|
const oscillatorStrong = isAtLeast(context.oscillatorStrength, 0.55);
|
|
146
204
|
const oscillatorElite = isAtLeast(context.oscillatorStrength, 0.9);
|
|
@@ -161,11 +219,16 @@ var getDeterministicAdaptiveMomentumRibbonQuality = (context) => {
|
|
|
161
219
|
const structuralRrStrong = isAtLeast(context.structuralRewardRiskRatio, 1.8);
|
|
162
220
|
const channelSupportive = context.channelBiasAligned === true;
|
|
163
221
|
const channelExpansion = context.signalDirection === "LONG" ? context.channelState === "above_upper" : context.channelState === "below_lower";
|
|
164
|
-
const
|
|
165
|
-
|
|
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) {
|
|
166
229
|
return 5;
|
|
167
230
|
}
|
|
168
|
-
if (channelSupportive &&
|
|
231
|
+
if (channelSupportive && channelExpansion && !slowestDetector && oscillatorModerate && invalidationCompact && structuralRrModerate && biasConflictCount < 2 && (biasConflictCount === 0 || oscillatorStrong)) {
|
|
169
232
|
return 4;
|
|
170
233
|
}
|
|
171
234
|
return 3;
|
|
@@ -173,20 +236,25 @@ var getDeterministicAdaptiveMomentumRibbonQuality = (context) => {
|
|
|
173
236
|
var getHardBlockReasonText = (reason) => {
|
|
174
237
|
switch (reason) {
|
|
175
238
|
case "invalidated":
|
|
176
|
-
return "
|
|
239
|
+
return "the signal is already invalidated relative to invalidationLevel";
|
|
177
240
|
case "inactive_signal_state":
|
|
178
|
-
return "active ribbon state
|
|
241
|
+
return "the active ribbon state does not confirm the current direction";
|
|
179
242
|
case "oscillator_conflict":
|
|
180
|
-
return "signalOsc
|
|
243
|
+
return "signalOsc conflicts with the signal direction";
|
|
181
244
|
case "invalidation_wrong_side":
|
|
182
|
-
return "invalidationLevel
|
|
245
|
+
return "invalidationLevel is on the wrong side of the current price";
|
|
183
246
|
default:
|
|
184
247
|
return reason;
|
|
185
248
|
}
|
|
186
249
|
};
|
|
187
|
-
var buildAdaptiveMomentumRibbonContext = (signal) => {
|
|
250
|
+
var buildAdaptiveMomentumRibbonContext = (signal, additionalIndicators) => {
|
|
188
251
|
const signalDirection = getSignalDirection(signal);
|
|
189
252
|
const snapshot = getAdaptiveMomentumRibbonSnapshot(signal);
|
|
253
|
+
const configSnapshot = getAdaptiveMomentumRibbonConfigSnapshot(signal);
|
|
254
|
+
const momentumPeriod = toFiniteNumberOrNull(configSnapshot?.momentumPeriod);
|
|
255
|
+
const butterworthSmoothing = toFiniteNumberOrNull(
|
|
256
|
+
configSnapshot?.butterworthSmoothing
|
|
257
|
+
);
|
|
190
258
|
const currentPrice = toFiniteNumberOrNull(signal.prices?.currentPrice);
|
|
191
259
|
const takeProfitPrice = toFiniteNumberOrNull(signal.prices?.takeProfitPrice);
|
|
192
260
|
const signalOsc = toFiniteNumberOrNull(snapshot.signalOsc);
|
|
@@ -208,6 +276,12 @@ var buildAdaptiveMomentumRibbonContext = (signal) => {
|
|
|
208
276
|
kcLower
|
|
209
277
|
});
|
|
210
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
|
+
});
|
|
211
285
|
const invalidationDistancePct = getDirectionalInvalidationDistancePct({
|
|
212
286
|
signalDirection,
|
|
213
287
|
currentPrice,
|
|
@@ -229,6 +303,8 @@ var buildAdaptiveMomentumRibbonContext = (signal) => {
|
|
|
229
303
|
);
|
|
230
304
|
const coinBiasAligned = signalDirection === "LONG" ? coinBias == null ? null : coinBias === "bullish" : signalDirection === "SHORT" ? coinBias == null ? null : coinBias === "bearish" : null;
|
|
231
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";
|
|
232
308
|
const hardBlockReasons = [];
|
|
233
309
|
if (invalidated) {
|
|
234
310
|
hardBlockReasons.push("invalidated");
|
|
@@ -244,6 +320,8 @@ var buildAdaptiveMomentumRibbonContext = (signal) => {
|
|
|
244
320
|
}
|
|
245
321
|
const deterministicQuality = getDeterministicAdaptiveMomentumRibbonQuality({
|
|
246
322
|
signalDirection,
|
|
323
|
+
momentumPeriod,
|
|
324
|
+
butterworthSmoothing,
|
|
247
325
|
entryLong,
|
|
248
326
|
entryShort,
|
|
249
327
|
activeBuy,
|
|
@@ -257,16 +335,21 @@ var buildAdaptiveMomentumRibbonContext = (signal) => {
|
|
|
257
335
|
invalidationLevel,
|
|
258
336
|
channelState,
|
|
259
337
|
channelBiasAligned,
|
|
338
|
+
channelExtensionPct,
|
|
260
339
|
invalidationDistancePct,
|
|
261
340
|
structuralRewardRiskRatio,
|
|
262
341
|
coinMaBias: coinBias,
|
|
263
342
|
btcMaBias: btcBias,
|
|
264
343
|
coinBiasAligned,
|
|
265
344
|
btcBiasAligned,
|
|
345
|
+
primarySession,
|
|
346
|
+
sessionAllowsApproval,
|
|
266
347
|
hardBlockReasons
|
|
267
348
|
});
|
|
268
349
|
return {
|
|
269
350
|
signalDirection,
|
|
351
|
+
momentumPeriod,
|
|
352
|
+
butterworthSmoothing,
|
|
270
353
|
entryLong,
|
|
271
354
|
entryShort,
|
|
272
355
|
activeBuy,
|
|
@@ -280,12 +363,15 @@ var buildAdaptiveMomentumRibbonContext = (signal) => {
|
|
|
280
363
|
invalidationLevel,
|
|
281
364
|
channelState,
|
|
282
365
|
channelBiasAligned,
|
|
366
|
+
channelExtensionPct,
|
|
283
367
|
invalidationDistancePct,
|
|
284
368
|
structuralRewardRiskRatio,
|
|
285
369
|
coinMaBias: coinBias,
|
|
286
370
|
btcMaBias: btcBias,
|
|
287
371
|
coinBiasAligned,
|
|
288
372
|
btcBiasAligned,
|
|
373
|
+
primarySession,
|
|
374
|
+
sessionAllowsApproval,
|
|
289
375
|
hardBlockReasons,
|
|
290
376
|
structuralHardBlockReasons: hardBlockReasons,
|
|
291
377
|
deterministicQuality,
|
|
@@ -326,9 +412,9 @@ var postProcessAnalysis = ({
|
|
|
326
412
|
retestPrice,
|
|
327
413
|
takeProfitPrice: null,
|
|
328
414
|
stopLossPrice: null,
|
|
329
|
-
qualityReason: analysis.qualityReason || (context.hardBlockReasons.length > 0 ? `AdaptiveMomentumRibbon guardrail: ${context.hardBlockReasons.map(getHardBlockReasonText).join("; ")}.` : "AdaptiveMomentumRibbon
|
|
330
|
-
triggerInvalidation: analysis.triggerInvalidation || (retestPrice != null ?
|
|
331
|
-
comment: analysis.comment || (context.hardBlockReasons.length > 0 ? `AdaptiveMomentumRibbon
|
|
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.")
|
|
332
418
|
};
|
|
333
419
|
}
|
|
334
420
|
return {
|
|
@@ -342,13 +428,19 @@ var postProcessAnalysis = ({
|
|
|
342
428
|
};
|
|
343
429
|
};
|
|
344
430
|
var adaptiveMomentumRibbonAiAdapter = {
|
|
345
|
-
buildPayload: ({ signal, basePayload }) =>
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
...basePayload
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
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
|
+
},
|
|
352
444
|
postProcessAnalysis,
|
|
353
445
|
buildSystemPromptAddon: () => `${ADAPTIVE_MOMENTUM_RIBBON_CONTEXT_PROMPT}
|
|
354
446
|
${ADAPTIVE_MOMENTUM_RIBBON_PAYLOAD_PROMPT}`,
|
|
@@ -359,35 +451,40 @@ ${ADAPTIVE_MOMENTUM_RIBBON_PAYLOAD_PROMPT}`,
|
|
|
359
451
|
);
|
|
360
452
|
return `
|
|
361
453
|
|
|
362
|
-
|
|
454
|
+
Additional AdaptiveMomentumRibbon context:
|
|
455
|
+
- momentumPeriod=${context.momentumPeriod ?? "n/a"}
|
|
456
|
+
- butterworthSmoothing=${context.butterworthSmoothing ?? "n/a"}
|
|
363
457
|
- signalOsc=${context.signalOsc?.toFixed?.(3) ?? "n/a"}
|
|
364
458
|
- oscillatorStrength=${context.oscillatorStrength?.toFixed?.(3) ?? "n/a"}
|
|
365
459
|
- channelState=${context.channelState}
|
|
366
460
|
- channelBiasAligned=${context.channelBiasAligned}
|
|
461
|
+
- channelExtensionPct=${context.channelExtensionPct?.toFixed?.(3) ?? "n/a"}%
|
|
367
462
|
- invalidationDistancePct=${context.invalidationDistancePct?.toFixed?.(3) ?? "n/a"}%
|
|
368
463
|
- structuralRewardRiskRatio=${context.structuralRewardRiskRatio?.toFixed?.(3) ?? "n/a"}
|
|
369
464
|
- coinBiasAligned=${context.coinBiasAligned}
|
|
370
465
|
- btcBiasAligned=${context.btcBiasAligned}
|
|
466
|
+
- primarySession=${context.primarySession ?? "n/a"}
|
|
467
|
+
- sessionAllowsApproval=${context.sessionAllowsApproval}
|
|
371
468
|
- deterministicQuality=${context.deterministicQuality}
|
|
372
469
|
- approvalAllowedNow=${context.approvalAllowedNow}
|
|
373
470
|
- hardBlockReasons=${context.hardBlockReasons.join(", ") || "none"}
|
|
374
471
|
|
|
375
|
-
|
|
376
|
-
- zero-cross
|
|
377
|
-
-
|
|
378
|
-
-
|
|
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.
|
|
379
476
|
`;
|
|
380
477
|
},
|
|
381
|
-
mapEntryRuntimeFromConfig: (
|
|
382
|
-
|
|
478
|
+
mapEntryRuntimeFromConfig: (config2) => mapAiRuntimeFromConfig(
|
|
479
|
+
config2
|
|
383
480
|
)
|
|
384
481
|
};
|
|
385
482
|
|
|
386
483
|
// src/AdaptiveMomentumRibbon/adapters/ml.ts
|
|
387
484
|
import { mapMlRuntimeFromConfig } from "@tradejs/core/strategies";
|
|
388
485
|
var adaptiveMomentumRibbonMlAdapter = {
|
|
389
|
-
mapEntryRuntimeFromConfig: (
|
|
390
|
-
|
|
486
|
+
mapEntryRuntimeFromConfig: (config2) => mapMlRuntimeFromConfig(
|
|
487
|
+
config2
|
|
391
488
|
)
|
|
392
489
|
};
|
|
393
490
|
|
|
@@ -398,8 +495,53 @@ var adaptiveMomentumRibbonManifest = {
|
|
|
398
495
|
mlAdapter: adaptiveMomentumRibbonMlAdapter
|
|
399
496
|
};
|
|
400
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
|
+
|
|
401
542
|
export {
|
|
402
543
|
adaptiveMomentumRibbonAiAdapter,
|
|
403
544
|
adaptiveMomentumRibbonMlAdapter,
|
|
404
|
-
adaptiveMomentumRibbonManifest
|
|
545
|
+
adaptiveMomentumRibbonManifest,
|
|
546
|
+
config
|
|
405
547
|
};
|