@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.
package/dist/index.mjs CHANGED
@@ -1,25 +1,39 @@
1
1
  import {
2
- breakoutManifest
3
- } from "./chunk-3PN7ZSJJ.mjs";
2
+ breakoutManifest,
3
+ config as config2
4
+ } from "./chunk-37ZWRG3W.mjs";
4
5
  import {
5
- config,
6
+ config as config6,
6
7
  trendLineManifest
7
- } from "./chunk-MVIV5ZII.mjs";
8
+ } from "./chunk-TDUTYEGH.mjs";
8
9
  import {
10
+ config as config5,
11
+ trendShiftAiAdapter,
12
+ trendShiftManifest
13
+ } from "./chunk-SOVTOGY4.mjs";
14
+ import {
15
+ config as config4,
16
+ reverseTrendLineAiAdapter,
17
+ reverseTrendLineManifest
18
+ } from "./chunk-UK4YHD2E.mjs";
19
+ import {
20
+ config as config3,
9
21
  maStrategyAiAdapter,
10
22
  maStrategyManifest,
11
23
  maStrategyMlAdapter
12
- } from "./chunk-RDK2JK3K.mjs";
24
+ } from "./chunk-H4MHFD4B.mjs";
13
25
  import {
14
26
  adaptiveMomentumRibbonAiAdapter,
15
27
  adaptiveMomentumRibbonManifest,
16
- adaptiveMomentumRibbonMlAdapter
17
- } from "./chunk-RYEPHOGL.mjs";
28
+ adaptiveMomentumRibbonMlAdapter,
29
+ config
30
+ } from "./chunk-IMLNXICX.mjs";
18
31
  import {
32
+ config as config7,
19
33
  volumeDivergenceAiAdapter,
20
34
  volumeDivergenceManifest,
21
35
  volumeDivergenceMlAdapter
22
- } from "./chunk-ULLCAH5C.mjs";
36
+ } from "./chunk-QVWMBYYM.mjs";
23
37
  import "./chunk-HEBXNMVQ.mjs";
24
38
 
25
39
  // src/index.ts
@@ -40,48 +54,81 @@ var strategyEntries = [
40
54
  {
41
55
  manifest: breakoutManifest,
42
56
  creator: createLazyStrategyCreator(
43
- () => import("./strategy-Y4SOK6FF.mjs"),
57
+ () => import("./strategy-JQIJILHQ.mjs"),
44
58
  "BreakoutStrategyCreator"
45
59
  )
46
60
  },
47
61
  {
48
62
  manifest: trendLineManifest,
49
63
  creator: createLazyStrategyCreator(
50
- () => import("./strategy-UZBWST3G.mjs"),
64
+ () => import("./strategy-D7H5J3C4.mjs"),
51
65
  "TrendlineStrategyCreator"
52
66
  )
53
67
  },
68
+ {
69
+ manifest: trendShiftManifest,
70
+ creator: createLazyStrategyCreator(
71
+ () => import("./strategy-QEIPAPY4.mjs"),
72
+ "TrendShiftStrategyCreator"
73
+ )
74
+ },
75
+ {
76
+ manifest: reverseTrendLineManifest,
77
+ creator: createLazyStrategyCreator(
78
+ () => import("./strategy-HQIPCUOY.mjs"),
79
+ "ReverseTrendLineStrategyCreator"
80
+ )
81
+ },
54
82
  {
55
83
  manifest: maStrategyManifest,
56
84
  creator: createLazyStrategyCreator(
57
- () => import("./strategy-ZVNTA6UR.mjs"),
85
+ () => import("./strategy-ABIO65CR.mjs"),
58
86
  "MaStrategyCreator"
59
87
  )
60
88
  },
61
89
  {
62
90
  manifest: adaptiveMomentumRibbonManifest,
63
91
  creator: createLazyStrategyCreator(
64
- () => import("./strategy-UHRWSGZC.mjs"),
92
+ () => import("./strategy-XTUKPYPM.mjs"),
65
93
  "AdaptiveMomentumRibbonStrategyCreator"
66
94
  )
67
95
  },
68
96
  {
69
97
  manifest: volumeDivergenceManifest,
70
98
  creator: createLazyStrategyCreator(
71
- () => import("./strategy-M3BRWDRR.mjs"),
99
+ () => import("./strategy-WYN4FZ5S.mjs"),
72
100
  "VolumeDivergenceStrategyCreator"
73
101
  )
74
102
  }
75
103
  ];
104
+ var builtInStrategyDefaultConfigs = {
105
+ Breakout: config2,
106
+ TrendLine: config6,
107
+ TrendShift: config5,
108
+ ReverseTrendLine: config4,
109
+ MaStrategy: config3,
110
+ AdaptiveMomentumRibbon: config,
111
+ VolumeDivergence: config7
112
+ };
113
+ var getBuiltInStrategyDefaultConfig = (strategyName) => builtInStrategyDefaultConfigs[strategyName];
76
114
  var index_default = defineStrategyPlugin({ strategyEntries });
77
115
  export {
78
116
  adaptiveMomentumRibbonAiAdapter,
117
+ config as adaptiveMomentumRibbonDefaultConfig,
79
118
  adaptiveMomentumRibbonMlAdapter,
119
+ config2 as breakoutDefaultConfig,
80
120
  index_default as default,
121
+ getBuiltInStrategyDefaultConfig,
81
122
  maStrategyAiAdapter,
123
+ config3 as maStrategyDefaultConfig,
82
124
  maStrategyMlAdapter,
125
+ reverseTrendLineAiAdapter,
126
+ config4 as reverseTrendLineDefaultConfig,
83
127
  strategyEntries,
84
- config as trendLineDefaultConfig,
128
+ config6 as trendLineDefaultConfig,
129
+ trendShiftAiAdapter,
130
+ config5 as trendShiftDefaultConfig,
85
131
  volumeDivergenceAiAdapter,
132
+ config7 as volumeDivergenceDefaultConfig,
86
133
  volumeDivergenceMlAdapter
87
134
  };
@@ -1,44 +1,12 @@
1
1
  import {
2
+ config,
2
3
  maStrategyManifest
3
- } from "./chunk-RDK2JK3K.mjs";
4
+ } from "./chunk-H4MHFD4B.mjs";
4
5
  import "./chunk-HEBXNMVQ.mjs";
5
6
 
6
7
  // src/MaStrategy/strategy.ts
7
8
  import { createStrategyRuntime } from "@tradejs/node/strategies";
8
9
 
9
- // src/MaStrategy/config.ts
10
- var config = {
11
- ENV: "BACKTEST",
12
- INTERVAL: "15",
13
- MAKE_ORDERS: true,
14
- CLOSE_OPPOSITE_POSITIONS: false,
15
- BACKTEST_PRICE_MODE: "mid",
16
- AI_ENABLED: false,
17
- ML_ENABLED: false,
18
- ML_THRESHOLD: 0.1,
19
- MIN_AI_QUALITY: 3,
20
- FEE_PERCENT: 5e-3,
21
- MAX_LOSS_VALUE: 10,
22
- MAX_CORRELATION: 0.45,
23
- TRADE_COOLDOWN_MS: 0,
24
- MA_FAST: 21,
25
- MA_SLOW: 55,
26
- LONG: {
27
- enable: true,
28
- direction: "LONG",
29
- TP: 2,
30
- SL: 1,
31
- minRiskRatio: 1.5
32
- },
33
- SHORT: {
34
- enable: true,
35
- direction: "SHORT",
36
- TP: 2,
37
- SL: 1,
38
- minRiskRatio: 1.5
39
- }
40
- };
41
-
42
10
  // src/MaStrategy/core.ts
43
11
  import { round } from "@tradejs/core/math";
44
12
 
@@ -134,15 +102,7 @@ var detectCross = (maFast, maSlow) => {
134
102
  return null;
135
103
  };
136
104
  var createMaStrategyCore = async ({ config: config2, strategyApi, indicatorsState }) => {
137
- const {
138
- ENV,
139
- FEE_PERCENT,
140
- MAX_LOSS_VALUE,
141
- MAX_CORRELATION,
142
- TRADE_COOLDOWN_MS,
143
- LONG,
144
- SHORT
145
- } = config2;
105
+ const { FEE_PERCENT, MAX_LOSS_VALUE, TRADE_COOLDOWN_MS, LONG, SHORT } = config2;
146
106
  const lastTradeController = strategyApi.createLastTradeController({
147
107
  enabled: Number(TRADE_COOLDOWN_MS ?? 0) > 0,
148
108
  cooldownMs: Number(TRADE_COOLDOWN_MS ?? 0)
@@ -205,9 +165,6 @@ var createMaStrategyCore = async ({ config: config2, strategyApi, indicatorsStat
205
165
  return strategyApi.skip(`RISK_RATIO:${round(riskRatio)}`);
206
166
  }
207
167
  const correlation = indicatorsState.latestNumber("correlation");
208
- if (ENV !== "BACKTEST" && correlation != null && correlation >= MAX_CORRELATION) {
209
- return strategyApi.skip(`MAX_CORRELATION:${round(correlation)}`);
210
- }
211
168
  lastTradeController.markTrade(timestamp);
212
169
  return strategyApi.entry({
213
170
  code: cross.kind === "bullish" ? "MA_BULLISH_CROSS" : "MA_BEARISH_CROSS",
@@ -0,0 +1,348 @@
1
+ import {
2
+ buildTrendlineStructuralContext,
3
+ buildTrendlineTimingContext,
4
+ config,
5
+ trendLineManifest
6
+ } from "./chunk-TDUTYEGH.mjs";
7
+ import "./chunk-HEBXNMVQ.mjs";
8
+
9
+ // src/TrendLine/strategy.ts
10
+ import { createStrategyRuntime } from "@tradejs/node/strategies";
11
+
12
+ // src/TrendLine/core.ts
13
+ import { round as round2 } from "@tradejs/core/math";
14
+ import { createTrendlineEngine } from "@tradejs/core/indicators";
15
+
16
+ // src/TrendLine/filters.ts
17
+ import { diffRel } from "@tradejs/core/math";
18
+ import { ATR_PCT } from "@tradejs/indicators";
19
+ var MAX_CANDLE_VOLATILITY = 0.025;
20
+ var filterByVeryVolatility = (data) => {
21
+ const lastCandle = data[data.length - 1];
22
+ const prevCandle = data[data.length - 2];
23
+ const isVeryVolatility = diffRel(lastCandle.low, lastCandle.high) > MAX_CANDLE_VOLATILITY || diffRel(prevCandle.low, prevCandle.high) > MAX_CANDLE_VOLATILITY;
24
+ if (isVeryVolatility) {
25
+ return false;
26
+ }
27
+ return true;
28
+ };
29
+
30
+ // src/TrendLine/figures.ts
31
+ var buildTrendLineFigures = (bestLine) => ({
32
+ lines: [
33
+ {
34
+ id: bestLine.id,
35
+ kind: "trendline",
36
+ points: [...bestLine.points ?? []].sort(
37
+ (left, right) => left.timestamp - right.timestamp
38
+ ),
39
+ color: bestLine.mode === "lows" ? "#facc15" : "#fb923c",
40
+ width: 2,
41
+ style: "solid"
42
+ }
43
+ ],
44
+ points: [
45
+ {
46
+ id: `${bestLine.id}-points`,
47
+ kind: "trendline_points",
48
+ points: [...bestLine.points ?? [], ...bestLine.touches ?? []].sort(
49
+ (left, right) => left.timestamp - right.timestamp
50
+ ),
51
+ color: "#ef4444",
52
+ radius: 4
53
+ }
54
+ ]
55
+ });
56
+
57
+ // src/TrendLine/risk.ts
58
+ import { round } from "@tradejs/core/math";
59
+ var MIN_STOP_BUFFER_PCT = 0.15;
60
+ var LINE_BUFFER_ATR_FACTOR = 0.35;
61
+ var LINE_BUFFER_BASE_SL_FACTOR = 0.15;
62
+ var ATR_STOP_FLOOR_FACTOR = 0.8;
63
+ var MIN_STOP_LOSS_FACTOR = 0.75;
64
+ var MAX_STOP_LOSS_FACTOR = 2.25;
65
+ var clampNumber = (value, min, max) => Math.min(Math.max(value, min), max);
66
+ var getTimingStopFactor = (entryTiming) => {
67
+ if (entryTiming === "ready_retest") {
68
+ return 0.9;
69
+ }
70
+ if (entryTiming === "ready_follow_through") {
71
+ return 1.05;
72
+ }
73
+ return 1;
74
+ };
75
+ var getTimingTargetRiskRatio = ({
76
+ direction,
77
+ entryTiming
78
+ }) => {
79
+ if (direction === "LONG") {
80
+ if (entryTiming === "ready_retest") {
81
+ return 2.45;
82
+ }
83
+ if (entryTiming === "ready_follow_through") {
84
+ return 2.3;
85
+ }
86
+ return 2.6;
87
+ }
88
+ if (entryTiming === "ready_retest") {
89
+ return 2.3;
90
+ }
91
+ if (entryTiming === "ready_follow_through") {
92
+ return 2.15;
93
+ }
94
+ return 2.45;
95
+ };
96
+ var buildTrendlineRiskPlan = ({
97
+ direction,
98
+ modeConfig,
99
+ structuralContext,
100
+ timingContext
101
+ }) => {
102
+ const baseStopLossDelta = modeConfig.SL;
103
+ const atrPct = structuralContext.atrPct ?? baseStopLossDelta;
104
+ const priceVsLinePctAbs = structuralContext.priceVsLinePctAbs ?? 0;
105
+ const breakVsAtrRatio = structuralContext.breakVsAtrRatio ?? 0;
106
+ const touches = structuralContext.touches ?? 0;
107
+ const distance = structuralContext.distance ?? null;
108
+ const lineBufferPct = Math.max(
109
+ atrPct * LINE_BUFFER_ATR_FACTOR,
110
+ baseStopLossDelta * LINE_BUFFER_BASE_SL_FACTOR,
111
+ MIN_STOP_BUFFER_PCT
112
+ );
113
+ const lineInvalidationPct = priceVsLinePctAbs + lineBufferPct;
114
+ const volatilityFloorPct = Math.max(
115
+ atrPct * ATR_STOP_FLOOR_FACTOR,
116
+ baseStopLossDelta * MIN_STOP_LOSS_FACTOR
117
+ );
118
+ let stopLossDelta = Math.max(lineInvalidationPct, volatilityFloorPct);
119
+ if (touches >= 6) {
120
+ stopLossDelta *= 0.95;
121
+ } else if (touches > 0 && touches <= 4) {
122
+ stopLossDelta *= 1.05;
123
+ }
124
+ if (distance != null && distance >= 250) {
125
+ stopLossDelta *= 1.08;
126
+ } else if (distance != null && distance <= 120) {
127
+ stopLossDelta *= 0.95;
128
+ }
129
+ stopLossDelta *= getTimingStopFactor(timingContext.entryTiming);
130
+ if (direction === "SHORT") {
131
+ stopLossDelta *= 1.08;
132
+ }
133
+ if (breakVsAtrRatio >= 1.5) {
134
+ stopLossDelta *= 0.95;
135
+ }
136
+ stopLossDelta = clampNumber(
137
+ stopLossDelta,
138
+ baseStopLossDelta * MIN_STOP_LOSS_FACTOR,
139
+ baseStopLossDelta * MAX_STOP_LOSS_FACTOR
140
+ );
141
+ let targetRiskRatio = getTimingTargetRiskRatio({
142
+ direction,
143
+ entryTiming: timingContext.entryTiming
144
+ });
145
+ if (breakVsAtrRatio >= 1.25) {
146
+ targetRiskRatio += 0.2;
147
+ } else if (breakVsAtrRatio > 0 && breakVsAtrRatio < 0.75) {
148
+ targetRiskRatio -= 0.15;
149
+ }
150
+ if (touches >= 6) {
151
+ targetRiskRatio += 0.1;
152
+ }
153
+ if (distance != null && distance >= 120 && distance <= 350) {
154
+ targetRiskRatio += 0.1;
155
+ }
156
+ if (direction === "SHORT" && distance != null && distance > 450) {
157
+ targetRiskRatio -= 0.25;
158
+ }
159
+ if (direction === "LONG" && distance != null && distance > 500) {
160
+ targetRiskRatio -= 0.15;
161
+ }
162
+ const minTargetRiskRatio = modeConfig.minRiskRatio + 0.05;
163
+ const maxTargetRiskRatio = Math.max(modeConfig.TP / modeConfig.SL, minTargetRiskRatio) + 0.4;
164
+ targetRiskRatio = clampNumber(
165
+ targetRiskRatio,
166
+ minTargetRiskRatio,
167
+ maxTargetRiskRatio
168
+ );
169
+ return {
170
+ stopLossDelta: round(stopLossDelta, 3),
171
+ targetRiskRatio: round(targetRiskRatio, 2),
172
+ takeProfitDelta: round(stopLossDelta * targetRiskRatio, 3)
173
+ };
174
+ };
175
+
176
+ // src/TrendLine/core.ts
177
+ var buildTrendlineSignalSeed = ({
178
+ direction,
179
+ currentPrice,
180
+ indicators,
181
+ bestLine,
182
+ trendlineTiming
183
+ }) => ({
184
+ direction,
185
+ prices: { currentPrice },
186
+ indicators,
187
+ additionalIndicators: {
188
+ touches: bestLine.touches.length + 2,
189
+ distance: bestLine.distance,
190
+ trendLine: bestLine,
191
+ ...trendlineTiming ? { trendlineTiming } : {}
192
+ },
193
+ figures: {
194
+ trendLine: bestLine
195
+ }
196
+ });
197
+ var isOpenPosition = (position) => Boolean(
198
+ 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")
199
+ );
200
+ var isFailedBreakout = ({
201
+ direction,
202
+ priceVsLinePct
203
+ }) => {
204
+ if (priceVsLinePct == null) {
205
+ return false;
206
+ }
207
+ return direction === "LONG" ? priceVsLinePct < 0 : priceVsLinePct > 0;
208
+ };
209
+ var createTrendLineCore = async ({ config: config2, data: cachedData, strategyApi, indicatorsState }) => {
210
+ const { TRENDLINE, FEE_PERCENT, MAX_LOSS_VALUE, HIGHS, LOWS } = config2;
211
+ const lastTradeController = strategyApi.createLastTradeController();
212
+ const trendlineOptions = {
213
+ bestLines: 1,
214
+ capture: true,
215
+ ...TRENDLINE
216
+ };
217
+ const getLowsTrendlines = createTrendlineEngine(cachedData, {
218
+ mode: "lows",
219
+ ...trendlineOptions
220
+ });
221
+ const getHighsTrendlines = createTrendlineEngine(cachedData, {
222
+ mode: "highs",
223
+ ...trendlineOptions
224
+ });
225
+ return async (candle) => {
226
+ const lowsTrendlines = getLowsTrendlines.next(candle);
227
+ const highsTrendlines = getHighsTrendlines.next(candle);
228
+ indicatorsState.onBar();
229
+ const currentPosition = await strategyApi.getCurrentPosition();
230
+ if (isOpenPosition(currentPosition)) {
231
+ const { currentPrice: currentPrice2 } = await strategyApi.getMarketData();
232
+ const activeLine = currentPosition.direction === "LONG" ? highsTrendlines[0] : lowsTrendlines[0];
233
+ const activeModeConfig = currentPosition.direction === "LONG" ? HIGHS : LOWS;
234
+ if (activeLine) {
235
+ const indicators2 = indicatorsState.snapshot();
236
+ const manageSignalSeed = buildTrendlineSignalSeed({
237
+ direction: activeModeConfig.direction,
238
+ currentPrice: currentPrice2,
239
+ indicators: indicators2,
240
+ bestLine: activeLine
241
+ });
242
+ const structuralContext2 = buildTrendlineStructuralContext(manageSignalSeed);
243
+ if (isFailedBreakout({
244
+ direction: currentPosition.direction,
245
+ priceVsLinePct: structuralContext2.priceVsLinePct
246
+ })) {
247
+ return strategyApi.exit({
248
+ code: "TRENDLINE_FAILED_BREAKOUT_EXIT",
249
+ direction: currentPosition.direction
250
+ });
251
+ }
252
+ }
253
+ return strategyApi.skip("POSITION_EXISTS");
254
+ }
255
+ const bestLine = lowsTrendlines.length > 0 ? lowsTrendlines[0] : highsTrendlines[0];
256
+ if (!bestLine) {
257
+ return strategyApi.skip("NO_TRENDLINE");
258
+ }
259
+ if (lastTradeController.isInCooldown(candle.timestamp)) {
260
+ return strategyApi.skip("DEV_TRADE_COOLDOWN");
261
+ }
262
+ const modeConfig = bestLine.mode === "highs" ? HIGHS : LOWS;
263
+ const { direction, minRiskRatio, enable } = modeConfig;
264
+ if (!enable) {
265
+ return strategyApi.skip("STRATEGY_DISABLED");
266
+ }
267
+ const { fullData, timestamp, currentPrice } = await strategyApi.getMarketData();
268
+ if (!filterByVeryVolatility(fullData)) {
269
+ return strategyApi.skip("VERY_VOLATILITY");
270
+ }
271
+ const indicators = indicatorsState.snapshot();
272
+ const signalSeed = buildTrendlineSignalSeed({
273
+ direction,
274
+ currentPrice,
275
+ indicators,
276
+ bestLine
277
+ });
278
+ const structuralContext = buildTrendlineStructuralContext(signalSeed);
279
+ if (structuralContext.structuralHardBlockReasons.length > 0) {
280
+ return strategyApi.skip(
281
+ `TRENDLINE_STRUCTURE:${structuralContext.structuralHardBlockReasons[0]}`
282
+ );
283
+ }
284
+ const timingContext = buildTrendlineTimingContext({
285
+ signal: signalSeed,
286
+ candles: fullData,
287
+ structuralContext
288
+ });
289
+ if (!timingContext.entryReadyNow) {
290
+ const timingCode = timingContext.entryTiming === "stale_breakout" ? "STALE_BREAKOUT" : timingContext.entryTiming === "wait_retest_confirmation" ? "WAIT_RETEST_CONFIRMATION" : "WAIT_RETEST";
291
+ return strategyApi.skip(`TRENDLINE_TIMING:${timingCode}`);
292
+ }
293
+ const riskPlan = buildTrendlineRiskPlan({
294
+ direction,
295
+ modeConfig,
296
+ structuralContext,
297
+ timingContext
298
+ });
299
+ const { stopLossPrice, takeProfitPrice, riskRatio, qty } = strategyApi.getDirectionalTpSlPrices({
300
+ price: currentPrice,
301
+ direction,
302
+ takeProfitDelta: riskPlan.takeProfitDelta,
303
+ stopLossDelta: riskPlan.stopLossDelta,
304
+ unit: "percent",
305
+ maxLossValue: MAX_LOSS_VALUE,
306
+ feePercent: Number(FEE_PERCENT ?? 0)
307
+ });
308
+ if (!qty || !Number.isFinite(qty) || qty <= 0) {
309
+ return strategyApi.skip("INVALID_QTY");
310
+ }
311
+ if (riskRatio <= minRiskRatio) {
312
+ return strategyApi.skip(`RISK_RATIO:${round2(riskRatio)}`);
313
+ }
314
+ lastTradeController.markTrade(timestamp);
315
+ return strategyApi.entry({
316
+ code: "TRENDLINE_SIGNAL",
317
+ figures: {
318
+ ...buildTrendLineFigures(bestLine)
319
+ },
320
+ direction,
321
+ indicators,
322
+ additionalIndicators: buildTrendlineSignalSeed({
323
+ direction,
324
+ currentPrice,
325
+ indicators,
326
+ bestLine,
327
+ trendlineTiming: timingContext
328
+ }).additionalIndicators,
329
+ orderPlan: {
330
+ qty,
331
+ stopLossPrice,
332
+ takeProfits: [{ rate: 1, price: takeProfitPrice }]
333
+ }
334
+ });
335
+ };
336
+ };
337
+
338
+ // src/TrendLine/strategy.ts
339
+ var TrendlineStrategyCreator = createStrategyRuntime({
340
+ strategyName: "TrendLine",
341
+ defaults: config,
342
+ createCore: createTrendLineCore,
343
+ manifest: trendLineManifest,
344
+ strategyDirectory: __dirname
345
+ });
346
+ export {
347
+ TrendlineStrategyCreator
348
+ };