@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,399 @@
1
+ import {
2
+ buildReverseTrendlineStructuralContext,
3
+ buildReverseTrendlineTimingContext,
4
+ config,
5
+ reverseTrendLineManifest,
6
+ toFiniteNumberOrNull
7
+ } from "./chunk-UK4YHD2E.mjs";
8
+ import "./chunk-HEBXNMVQ.mjs";
9
+
10
+ // src/ReverseTrendLine/strategy.ts
11
+ import { createStrategyRuntime } from "@tradejs/node/strategies";
12
+
13
+ // src/ReverseTrendLine/core.ts
14
+ import { round as round2 } from "@tradejs/core/math";
15
+ import { createTrendlineEngine } from "@tradejs/core/indicators";
16
+
17
+ // src/ReverseTrendLine/filters.ts
18
+ import { diffRel } from "@tradejs/core/math";
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
+ if (!lastCandle || !prevCandle) {
24
+ return false;
25
+ }
26
+ const isVeryVolatility = diffRel(lastCandle.low, lastCandle.high) > MAX_CANDLE_VOLATILITY || diffRel(prevCandle.low, prevCandle.high) > MAX_CANDLE_VOLATILITY;
27
+ return !isVeryVolatility;
28
+ };
29
+
30
+ // src/ReverseTrendLine/figures.ts
31
+ var buildReverseTrendLineFigures = (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" ? "#22c55e" : "#f97316",
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/ReverseTrendLine/risk.ts
58
+ import { round } from "@tradejs/core/math";
59
+ var MIN_STOP_BUFFER_PCT = 0.1;
60
+ var LINE_BUFFER_ATR_FACTOR = 0.25;
61
+ var LINE_BUFFER_BASE_SL_FACTOR = 0.1;
62
+ var ATR_STOP_FLOOR_FACTOR = 0.65;
63
+ var MIN_STOP_LOSS_FACTOR = 0.8;
64
+ var MAX_STOP_LOSS_FACTOR = 2;
65
+ var clampNumber = (value, min, max) => Math.min(Math.max(value, min), max);
66
+ var getTimingStopFactor = (entryTiming) => {
67
+ if (entryTiming === "ready_follow_through") {
68
+ return 0.95;
69
+ }
70
+ return 1;
71
+ };
72
+ var getTimingTargetRiskRatio = ({
73
+ direction,
74
+ entryTiming
75
+ }) => {
76
+ if (direction === "LONG") {
77
+ return entryTiming === "ready_follow_through" ? 2.15 : 1.95;
78
+ }
79
+ return entryTiming === "ready_follow_through" ? 2.2 : 2;
80
+ };
81
+ var buildReverseTrendlineRiskPlan = ({
82
+ direction,
83
+ modeConfig,
84
+ structuralContext,
85
+ timingContext
86
+ }) => {
87
+ const baseStopLossDelta = modeConfig.SL;
88
+ const atrPct = structuralContext.atrPct ?? baseStopLossDelta;
89
+ const priceVsLinePctAbs = structuralContext.priceVsLinePctAbs ?? 0;
90
+ const rejectionStrengthPct = structuralContext.rejectionStrengthPct ?? 0;
91
+ const touches = structuralContext.touches ?? 0;
92
+ const distance = structuralContext.distance ?? null;
93
+ const lineBufferPct = Math.max(
94
+ atrPct * LINE_BUFFER_ATR_FACTOR,
95
+ baseStopLossDelta * LINE_BUFFER_BASE_SL_FACTOR,
96
+ MIN_STOP_BUFFER_PCT
97
+ );
98
+ const lineInvalidationPct = priceVsLinePctAbs + lineBufferPct;
99
+ const volatilityFloorPct = Math.max(
100
+ atrPct * ATR_STOP_FLOOR_FACTOR,
101
+ baseStopLossDelta * MIN_STOP_LOSS_FACTOR
102
+ );
103
+ let stopLossDelta = Math.max(lineInvalidationPct, volatilityFloorPct);
104
+ if (touches >= 6) {
105
+ stopLossDelta *= 0.95;
106
+ } else if (touches > 0 && touches <= 4) {
107
+ stopLossDelta *= 1.03;
108
+ }
109
+ if (distance != null && distance >= 400) {
110
+ stopLossDelta *= 1.05;
111
+ } else if (distance != null && distance <= 120) {
112
+ stopLossDelta *= 0.95;
113
+ }
114
+ if (rejectionStrengthPct >= 0.2) {
115
+ stopLossDelta *= 0.95;
116
+ }
117
+ stopLossDelta *= getTimingStopFactor(timingContext.entryTiming);
118
+ stopLossDelta = clampNumber(
119
+ stopLossDelta,
120
+ baseStopLossDelta * MIN_STOP_LOSS_FACTOR,
121
+ baseStopLossDelta * MAX_STOP_LOSS_FACTOR
122
+ );
123
+ let targetRiskRatio = getTimingTargetRiskRatio({
124
+ direction,
125
+ entryTiming: timingContext.entryTiming
126
+ });
127
+ if (touches >= 6) {
128
+ targetRiskRatio += 0.1;
129
+ }
130
+ if (distance != null && distance >= 120 && distance <= 350) {
131
+ targetRiskRatio += 0.05;
132
+ }
133
+ if (direction === "SHORT" && distance != null && distance > 500) {
134
+ targetRiskRatio -= 0.15;
135
+ }
136
+ if (direction === "LONG" && distance != null && distance > 500) {
137
+ targetRiskRatio -= 0.1;
138
+ }
139
+ const minTargetRiskRatio = modeConfig.minRiskRatio + 0.05;
140
+ const maxTargetRiskRatio = Math.max(modeConfig.TP / modeConfig.SL, minTargetRiskRatio) + 0.3;
141
+ targetRiskRatio = clampNumber(
142
+ targetRiskRatio,
143
+ minTargetRiskRatio,
144
+ maxTargetRiskRatio
145
+ );
146
+ return {
147
+ stopLossDelta: round(stopLossDelta, 3),
148
+ targetRiskRatio: round(targetRiskRatio, 2),
149
+ takeProfitDelta: round(stopLossDelta * targetRiskRatio, 3)
150
+ };
151
+ };
152
+
153
+ // src/ReverseTrendLine/core.ts
154
+ var buildReverseTrendlineSignalSeed = ({
155
+ direction,
156
+ currentPrice,
157
+ indicators,
158
+ bestLine,
159
+ currentCandle,
160
+ reverseTrendlineTiming
161
+ }) => ({
162
+ direction,
163
+ prices: { currentPrice },
164
+ indicators,
165
+ additionalIndicators: {
166
+ touches: Array.isArray(bestLine.touches) ? bestLine.touches.length + 2 : 2,
167
+ distance: bestLine.distance,
168
+ trendLine: bestLine,
169
+ ...currentCandle ? { currentCandle } : {},
170
+ ...reverseTrendlineTiming ? { reverseTrendlineTiming } : {}
171
+ },
172
+ figures: {
173
+ trendLine: bestLine
174
+ }
175
+ });
176
+ var isOpenPosition = (position) => Boolean(
177
+ 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")
178
+ );
179
+ var getLinePriceAtNow = (line, timestamp) => {
180
+ if (!line || !Array.isArray(line.points) || line.points.length === 0) {
181
+ return null;
182
+ }
183
+ const sortedPoints = [...line.points].sort(
184
+ (left, right) => left.timestamp - right.timestamp
185
+ );
186
+ const first = sortedPoints[0];
187
+ const last = sortedPoints[sortedPoints.length - 1];
188
+ if (first.timestamp === last.timestamp) {
189
+ return last.value;
190
+ }
191
+ const slope = (last.value - first.value) / (last.timestamp - first.timestamp);
192
+ return first.value + slope * (timestamp - first.timestamp);
193
+ };
194
+ var buildReverseTrendlineCandidateContext = ({
195
+ line,
196
+ candle,
197
+ direction
198
+ }) => {
199
+ const currentLinePrice = getLinePriceAtNow(line, candle.timestamp);
200
+ const priceVsLinePct = currentLinePrice != null && currentLinePrice !== 0 ? (candle.close - currentLinePrice) / currentLinePrice * 100 : null;
201
+ const priceVsLinePctAbs = priceVsLinePct == null ? null : Math.abs(priceVsLinePct);
202
+ const lineTouchedNow = currentLinePrice != null && candle.low <= currentLinePrice && candle.high >= currentLinePrice;
203
+ const failedBounceBreak = direction === "LONG" ? priceVsLinePct != null && priceVsLinePct <= -0.35 : priceVsLinePct != null && priceVsLinePct >= 0.35;
204
+ return {
205
+ currentLinePrice,
206
+ priceVsLinePctAbs,
207
+ lineTouchedNow,
208
+ failedBounceBreak,
209
+ distance: toFiniteNumberOrNull(line.distance)
210
+ };
211
+ };
212
+ var pickBestCandidateLine = ({
213
+ candle,
214
+ lines
215
+ }) => {
216
+ const ranked = lines.map(({ line, direction }) => {
217
+ const candidateContext = buildReverseTrendlineCandidateContext({
218
+ line,
219
+ candle,
220
+ direction
221
+ });
222
+ return { line, direction, candidateContext };
223
+ }).filter(({ candidateContext }) => candidateContext.currentLinePrice != null).sort((left, right) => {
224
+ const leftTouchRank = left.candidateContext.lineTouchedNow ? 0 : 1;
225
+ const rightTouchRank = right.candidateContext.lineTouchedNow ? 0 : 1;
226
+ if (leftTouchRank !== rightTouchRank) {
227
+ return leftTouchRank - rightTouchRank;
228
+ }
229
+ const leftDistance = left.candidateContext.priceVsLinePctAbs ?? Number.POSITIVE_INFINITY;
230
+ const rightDistance = right.candidateContext.priceVsLinePctAbs ?? Number.POSITIVE_INFINITY;
231
+ if (leftDistance !== rightDistance) {
232
+ return leftDistance - rightDistance;
233
+ }
234
+ return (left.candidateContext.distance ?? Number.POSITIVE_INFINITY) - (right.candidateContext.distance ?? Number.POSITIVE_INFINITY);
235
+ });
236
+ return ranked[0] ?? null;
237
+ };
238
+ var createReverseTrendLineCore = async ({ config: config2, data: cachedData, strategyApi, indicatorsState }) => {
239
+ const { TRENDLINE, FEE_PERCENT, MAX_LOSS_VALUE, HIGHS, LOWS } = config2;
240
+ const lastTradeController = strategyApi.createLastTradeController();
241
+ const trendlineOptions = {
242
+ bestLines: 1,
243
+ capture: true,
244
+ ...TRENDLINE
245
+ };
246
+ const getLowsTrendlines = createTrendlineEngine(cachedData, {
247
+ mode: "lows",
248
+ ...trendlineOptions
249
+ });
250
+ const getHighsTrendlines = createTrendlineEngine(cachedData, {
251
+ mode: "highs",
252
+ ...trendlineOptions
253
+ });
254
+ return async (candle) => {
255
+ const lowsTrendlines = getLowsTrendlines.next(candle);
256
+ const highsTrendlines = getHighsTrendlines.next(candle);
257
+ indicatorsState.onBar();
258
+ const currentPosition = await strategyApi.getCurrentPosition();
259
+ if (isOpenPosition(currentPosition)) {
260
+ const activeLine = currentPosition.direction === "LONG" ? lowsTrendlines[0] : highsTrendlines[0];
261
+ const activeLinePrice = getLinePriceAtNow(
262
+ activeLine ?? null,
263
+ candle.timestamp
264
+ );
265
+ const priceVsLinePct = activeLinePrice != null && activeLinePrice !== 0 ? (candle.close - activeLinePrice) / activeLinePrice * 100 : null;
266
+ const failedBounceBreak = currentPosition.direction === "LONG" ? priceVsLinePct != null && priceVsLinePct <= -0.35 : priceVsLinePct != null && priceVsLinePct >= 0.35;
267
+ if (failedBounceBreak) {
268
+ return strategyApi.exit({
269
+ code: "REVERSE_TRENDLINE_FAILED_BOUNCE_EXIT",
270
+ direction: currentPosition.direction
271
+ });
272
+ }
273
+ return strategyApi.skip("POSITION_EXISTS");
274
+ }
275
+ if (lastTradeController.isInCooldown(candle.timestamp)) {
276
+ return strategyApi.skip("DEV_TRADE_COOLDOWN");
277
+ }
278
+ const candidates = [];
279
+ if (LOWS.enable && lowsTrendlines.length > 0) {
280
+ candidates.push({ line: lowsTrendlines[0], direction: LOWS.direction });
281
+ }
282
+ if (HIGHS.enable && highsTrendlines.length > 0) {
283
+ candidates.push({ line: highsTrendlines[0], direction: HIGHS.direction });
284
+ }
285
+ if (candidates.length === 0) {
286
+ return strategyApi.skip("NO_TRENDLINE");
287
+ }
288
+ const bestCandidate = pickBestCandidateLine({
289
+ candle: {
290
+ timestamp: candle.timestamp,
291
+ open: candle.open,
292
+ close: candle.close,
293
+ high: candle.high,
294
+ low: candle.low
295
+ },
296
+ lines: candidates
297
+ });
298
+ if (!bestCandidate) {
299
+ return strategyApi.skip("NO_TRENDLINE");
300
+ }
301
+ const { line: bestLine, direction, candidateContext } = bestCandidate;
302
+ const modeConfig = direction === "LONG" ? LOWS : HIGHS;
303
+ const { minRiskRatio } = modeConfig;
304
+ if (candidateContext.failedBounceBreak) {
305
+ return strategyApi.skip(
306
+ "REVERSE_TRENDLINE_STRUCTURE:failed_bounce_break"
307
+ );
308
+ }
309
+ const { fullData, timestamp, currentPrice } = await strategyApi.getMarketData();
310
+ if (!filterByVeryVolatility(fullData)) {
311
+ return strategyApi.skip("VERY_VOLATILITY");
312
+ }
313
+ const indicators = indicatorsState.snapshot();
314
+ const signalSeed = buildReverseTrendlineSignalSeed({
315
+ direction,
316
+ currentPrice,
317
+ indicators,
318
+ bestLine,
319
+ currentCandle: {
320
+ timestamp: candle.timestamp,
321
+ open: candle.open,
322
+ close: candle.close,
323
+ high: candle.high,
324
+ low: candle.low
325
+ }
326
+ });
327
+ const structuralContext = buildReverseTrendlineStructuralContext(signalSeed);
328
+ const timingContext = buildReverseTrendlineTimingContext({
329
+ signal: signalSeed,
330
+ candles: fullData,
331
+ structuralContext
332
+ });
333
+ if (!timingContext.entryReadyNow) {
334
+ const timingCode = timingContext.entryTiming === "stale_reaction" ? "STALE_REACTION" : timingContext.entryTiming === "wait_reaction_confirmation" ? "WAIT_REACTION_CONFIRMATION" : "WAIT_TOUCH";
335
+ return strategyApi.skip(`REVERSE_TRENDLINE_TIMING:${timingCode}`);
336
+ }
337
+ const riskPlan = buildReverseTrendlineRiskPlan({
338
+ direction,
339
+ modeConfig,
340
+ structuralContext,
341
+ timingContext
342
+ });
343
+ const { stopLossPrice, takeProfitPrice, riskRatio, qty } = strategyApi.getDirectionalTpSlPrices({
344
+ price: currentPrice,
345
+ direction,
346
+ takeProfitDelta: riskPlan.takeProfitDelta,
347
+ stopLossDelta: riskPlan.stopLossDelta,
348
+ unit: "percent",
349
+ maxLossValue: MAX_LOSS_VALUE,
350
+ feePercent: Number(FEE_PERCENT ?? 0)
351
+ });
352
+ if (!qty || !Number.isFinite(qty) || qty <= 0) {
353
+ return strategyApi.skip("INVALID_QTY");
354
+ }
355
+ if (riskRatio <= minRiskRatio) {
356
+ return strategyApi.skip(`RISK_RATIO:${round2(riskRatio)}`);
357
+ }
358
+ lastTradeController.markTrade(timestamp);
359
+ return strategyApi.entry({
360
+ code: "REVERSE_TRENDLINE_SIGNAL",
361
+ figures: {
362
+ ...buildReverseTrendLineFigures(bestLine)
363
+ },
364
+ direction,
365
+ indicators,
366
+ additionalIndicators: buildReverseTrendlineSignalSeed({
367
+ direction,
368
+ currentPrice,
369
+ indicators,
370
+ bestLine,
371
+ currentCandle: {
372
+ timestamp: candle.timestamp,
373
+ open: candle.open,
374
+ close: candle.close,
375
+ high: candle.high,
376
+ low: candle.low
377
+ },
378
+ reverseTrendlineTiming: timingContext
379
+ }).additionalIndicators,
380
+ orderPlan: {
381
+ qty,
382
+ stopLossPrice,
383
+ takeProfits: [{ rate: 1, price: takeProfitPrice }]
384
+ }
385
+ });
386
+ };
387
+ };
388
+
389
+ // src/ReverseTrendLine/strategy.ts
390
+ var ReverseTrendLineStrategyCreator = createStrategyRuntime({
391
+ strategyName: "ReverseTrendLine",
392
+ defaults: config,
393
+ createCore: createReverseTrendLineCore,
394
+ manifest: reverseTrendLineManifest,
395
+ strategyDirectory: __dirname
396
+ });
397
+ export {
398
+ ReverseTrendLineStrategyCreator
399
+ };
@@ -1,6 +1,7 @@
1
1
  import {
2
- breakoutManifest
3
- } from "./chunk-3PN7ZSJJ.mjs";
2
+ breakoutManifest,
3
+ config
4
+ } from "./chunk-37ZWRG3W.mjs";
4
5
  import {
5
6
  __commonJS,
6
7
  __toESM
@@ -12,9 +13,9 @@ var require_lodash = __commonJS({
12
13
  "use strict";
13
14
  (function() {
14
15
  var undefined;
15
- var VERSION = "4.17.21";
16
+ var VERSION = "4.18.1";
16
17
  var LARGE_ARRAY_SIZE = 200;
17
- 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`";
18
+ 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`";
18
19
  var HASH_UNDEFINED = "__lodash_hash_undefined__";
19
20
  var MAX_MEMOIZE_SIZE = 500;
20
21
  var PLACEHOLDER = "__lodash_placeholder__";
@@ -1940,8 +1941,21 @@ var require_lodash = __commonJS({
1940
1941
  }
1941
1942
  function baseUnset(object, path) {
1942
1943
  path = castPath(path, object);
1943
- object = parent(object, path);
1944
- return object == null || delete object[toKey(last(path))];
1944
+ var index = -1, length = path.length;
1945
+ if (!length) {
1946
+ return true;
1947
+ }
1948
+ while (++index < length) {
1949
+ var key = toKey(path[index]);
1950
+ if (key === "__proto__" && !hasOwnProperty.call(object, "__proto__")) {
1951
+ return false;
1952
+ }
1953
+ if ((key === "constructor" || key === "prototype") && index < length - 1) {
1954
+ return false;
1955
+ }
1956
+ }
1957
+ var obj = parent(object, path);
1958
+ return obj == null || delete obj[toKey(last(path))];
1945
1959
  }
1946
1960
  function baseUpdate(object, path, updater, customizer) {
1947
1961
  return baseSet(object, path, updater(baseGet(object, path)), customizer);
@@ -3268,7 +3282,7 @@ var require_lodash = __commonJS({
3268
3282
  var index = -1, length = pairs == null ? 0 : pairs.length, result2 = {};
3269
3283
  while (++index < length) {
3270
3284
  var pair = pairs[index];
3271
- result2[pair[0]] = pair[1];
3285
+ baseAssignValue(result2, pair[0], pair[1]);
3272
3286
  }
3273
3287
  return result2;
3274
3288
  }
@@ -4652,8 +4666,13 @@ var require_lodash = __commonJS({
4652
4666
  options = undefined;
4653
4667
  }
4654
4668
  string = toString(string);
4655
- options = assignInWith({}, options, settings, customDefaultsAssignIn);
4656
- var imports = assignInWith({}, options.imports, settings.imports, customDefaultsAssignIn), importsKeys = keys(imports), importsValues = baseValues(imports, importsKeys);
4669
+ options = assignWith({}, options, settings, customDefaultsAssignIn);
4670
+ var imports = assignWith({}, options.imports, settings.imports, customDefaultsAssignIn), importsKeys = keys(imports), importsValues = baseValues(imports, importsKeys);
4671
+ arrayEach(importsKeys, function(key) {
4672
+ if (reForbiddenIdentifierChars.test(key)) {
4673
+ throw new Error(INVALID_TEMPL_IMPORTS_ERROR_TEXT);
4674
+ }
4675
+ });
4657
4676
  var isEscaping, isEvaluating, index = 0, interpolate = options.interpolate || reNoMatch, source = "__p += '";
4658
4677
  var reDelimiters = RegExp2(
4659
4678
  (options.escape || reNoMatch).source + "|" + interpolate.source + "|" + (interpolate === reInterpolate ? reEsTemplate : reNoMatch).source + "|" + (options.evaluate || reNoMatch).source + "|$",
@@ -5490,106 +5509,6 @@ var require_lodash = __commonJS({
5490
5509
  // src/Breakout/strategy.ts
5491
5510
  import { createStrategyRuntime } from "@tradejs/node/strategies";
5492
5511
 
5493
- // src/Breakout/config.ts
5494
- var config = {
5495
- ML_ENABLED: false,
5496
- MA_FAST: 49,
5497
- MA_MEDIUM: 49,
5498
- MA_SLOW: 99,
5499
- OBV_SMA: 10,
5500
- ATR: 14,
5501
- ATR_PCT_SHORT: 7,
5502
- ATR_PCT_LONG: 30,
5503
- BB: 20,
5504
- BB_STD: 2,
5505
- MACD_FAST: 12,
5506
- MACD_SLOW: 26,
5507
- MACD_SIGNAL: 9,
5508
- LEVEL_LOOKBACK: 20,
5509
- LEVEL_DELAY: 2,
5510
- ATR_PERIOD: 14,
5511
- BB_PERIOD: 20,
5512
- BB_STDDEV: 2,
5513
- LIMIT: 100,
5514
- ATR_OPEN: 0.5,
5515
- ATR_CLOSE: 1.5,
5516
- OBV_SMA_PERIOD: 10,
5517
- BREAKOUT_LOOKBACK_DELAY: 2,
5518
- BREAKOUT_LOOKBACK: 20,
5519
- REQUIRED_SCORE_LONG: 7,
5520
- REQUIRED_SCORE_SHORT: 7,
5521
- SIGNALS_LONG: {
5522
- VOLATILE: {
5523
- weight: 1,
5524
- required: true
5525
- },
5526
- SMA_UPTREND: {
5527
- weight: 1,
5528
- required: true
5529
- },
5530
- OBV_ABOVE_SMA: {
5531
- weight: 1,
5532
- required: true
5533
- },
5534
- PREV_HIGH_BREAKOUT: {
5535
- weight: 1,
5536
- required: false
5537
- },
5538
- CLOSE_ABOVE_UPPER_BB: {
5539
- weight: 1,
5540
- required: false
5541
- },
5542
- CLOSE_ABOVE_HIGH_LEVEL: {
5543
- weight: 1,
5544
- required: false
5545
- },
5546
- CLOSE_ABOVE_PREV_CLOSE: {
5547
- weight: 1,
5548
- required: false
5549
- }
5550
- },
5551
- SIGNALS_SHORT: {
5552
- VOLATILE: {
5553
- weight: 1,
5554
- required: true
5555
- },
5556
- SMA_DOWNTREND: {
5557
- weight: 1,
5558
- required: true
5559
- },
5560
- OBV_BELOW_SMA: {
5561
- weight: 1,
5562
- required: true
5563
- },
5564
- PREV_LOW_BREAKDOWN: {
5565
- weight: 1,
5566
- required: false
5567
- },
5568
- CLOSE_BELOW_LOWER_BB: {
5569
- weight: 1,
5570
- required: false
5571
- },
5572
- CLOSE_BELOW_LOW_LEVEL: {
5573
- weight: 1,
5574
- required: false
5575
- },
5576
- CLOSE_BELOW_PREV_CLOSE: {
5577
- weight: 1,
5578
- required: false
5579
- }
5580
- },
5581
- TP_LONG: [
5582
- { profit: 0.1, rate: 0.25 },
5583
- { profit: 0.15, rate: 0.5 }
5584
- ],
5585
- TP_SHORT: [
5586
- { profit: 0.05, rate: 0.25 },
5587
- { profit: 0.1, rate: 0.5 }
5588
- ],
5589
- SL_LONG: 0.06,
5590
- SL_SHORT: 0.03
5591
- };
5592
-
5593
5512
  // src/Breakout/core.ts
5594
5513
  var import_lodash = __toESM(require_lodash());
5595
5514