@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/chunk-37ZWRG3W.mjs +128 -0
- package/dist/chunk-H4MHFD4B.mjs +62 -0
- package/dist/chunk-IMLNXICX.mjs +547 -0
- package/dist/chunk-QVWMBYYM.mjs +836 -0
- package/dist/chunk-SOVTOGY4.mjs +163 -0
- package/dist/chunk-TDUTYEGH.mjs +797 -0
- package/dist/chunk-UK4YHD2E.mjs +683 -0
- package/dist/index.d.mts +325 -4
- package/dist/index.d.ts +325 -4
- package/dist/index.js +5689 -1195
- package/dist/index.mjs +61 -14
- package/dist/{strategy-ZVNTA6UR.mjs → strategy-ABIO65CR.mjs} +3 -46
- package/dist/strategy-D7H5J3C4.mjs +348 -0
- package/dist/strategy-HQIPCUOY.mjs +399 -0
- package/dist/{strategy-Y4SOK6FF.mjs → strategy-JQIJILHQ.mjs} +28 -109
- package/dist/strategy-QEIPAPY4.mjs +373 -0
- package/dist/strategy-WYN4FZ5S.mjs +613 -0
- package/dist/{strategy-UHRWSGZC.mjs → strategy-XTUKPYPM.mjs} +484 -128
- package/package.json +5 -5
- package/dist/chunk-3PN7ZSJJ.mjs +0 -27
- package/dist/chunk-MVIV5ZII.mjs +0 -137
- package/dist/chunk-RDK2JK3K.mjs +0 -28
- package/dist/chunk-RYEPHOGL.mjs +0 -28
- package/dist/chunk-ULLCAH5C.mjs +0 -67
- package/dist/strategy-M3BRWDRR.mjs +0 -377
- package/dist/strategy-UZBWST3G.mjs +0 -156
|
@@ -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
|
-
|
|
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.
|
|
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
|
-
|
|
1944
|
-
|
|
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
|
|
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 =
|
|
4656
|
-
var imports =
|
|
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
|
|