@tradejs/strategies 1.0.0

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,252 @@
1
+ import {
2
+ maStrategyManifest
3
+ } from "./chunk-RDK2JK3K.mjs";
4
+ import "./chunk-HEBXNMVQ.mjs";
5
+
6
+ // src/MaStrategy/strategy.ts
7
+ import { createStrategyRuntime } from "@tradejs/node/strategies";
8
+
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
+ // src/MaStrategy/core.ts
43
+ import { round } from "@tradejs/core/math";
44
+
45
+ // src/MaStrategy/figures.ts
46
+ var toLinePoints = (candles, values, limit = 120) => {
47
+ const points = [];
48
+ const seriesOffset = Math.max(0, candles.length - values.length);
49
+ const start = Math.max(0, values.length - limit);
50
+ for (let i = start; i < values.length; i += 1) {
51
+ const candle = candles[seriesOffset + i];
52
+ const value = values[i];
53
+ if (!candle || !Number.isFinite(value)) continue;
54
+ points.push({
55
+ timestamp: candle.timestamp,
56
+ value
57
+ });
58
+ }
59
+ return points;
60
+ };
61
+ var buildMaStrategyFigures = ({
62
+ fullData,
63
+ maFast,
64
+ maSlow,
65
+ crossTimestamp,
66
+ crossPrice,
67
+ crossKind
68
+ }) => ({
69
+ lines: [
70
+ {
71
+ id: "ma-fast",
72
+ kind: "ma_fast",
73
+ points: toLinePoints(fullData, maFast),
74
+ color: "#22d3ee",
75
+ width: 2,
76
+ style: "solid"
77
+ },
78
+ {
79
+ id: "ma-slow",
80
+ kind: "ma_slow",
81
+ points: toLinePoints(fullData, maSlow),
82
+ color: "#f59e0b",
83
+ width: 2,
84
+ style: "solid"
85
+ }
86
+ ],
87
+ points: [
88
+ {
89
+ id: `ma-cross-${crossTimestamp}`,
90
+ kind: "ma_cross",
91
+ points: [
92
+ {
93
+ timestamp: crossTimestamp,
94
+ value: crossPrice
95
+ }
96
+ ],
97
+ color: crossKind === "bullish" ? "#22c55e" : "#ef4444",
98
+ radius: 4
99
+ }
100
+ ]
101
+ });
102
+
103
+ // src/MaStrategy/core.ts
104
+ var isFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value);
105
+ var detectCross = (maFast, maSlow) => {
106
+ if (maFast.length < 2 || maSlow.length < 2) {
107
+ return null;
108
+ }
109
+ const maFastPrev = maFast[maFast.length - 2];
110
+ const maFastCurrent = maFast[maFast.length - 1];
111
+ const maSlowPrev = maSlow[maSlow.length - 2];
112
+ const maSlowCurrent = maSlow[maSlow.length - 1];
113
+ if (!isFiniteNumber(maFastPrev) || !isFiniteNumber(maFastCurrent) || !isFiniteNumber(maSlowPrev) || !isFiniteNumber(maSlowCurrent)) {
114
+ return null;
115
+ }
116
+ if (maFastPrev <= maSlowPrev && maFastCurrent > maSlowCurrent) {
117
+ return {
118
+ kind: "bullish",
119
+ maFastPrev,
120
+ maFastCurrent,
121
+ maSlowPrev,
122
+ maSlowCurrent
123
+ };
124
+ }
125
+ if (maFastPrev >= maSlowPrev && maFastCurrent < maSlowCurrent) {
126
+ return {
127
+ kind: "bearish",
128
+ maFastPrev,
129
+ maFastCurrent,
130
+ maSlowPrev,
131
+ maSlowCurrent
132
+ };
133
+ }
134
+ return null;
135
+ };
136
+ 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;
146
+ const lastTradeController = strategyApi.createLastTradeController({
147
+ enabled: Number(TRADE_COOLDOWN_MS ?? 0) > 0,
148
+ cooldownMs: Number(TRADE_COOLDOWN_MS ?? 0)
149
+ });
150
+ return async () => {
151
+ indicatorsState.onBar();
152
+ const indicators = indicatorsState.snapshot();
153
+ if (!indicators) {
154
+ return strategyApi.skip("NO_INDICATORS");
155
+ }
156
+ const maFast = Array.isArray(indicators.maFast) ? indicators.maFast : [];
157
+ const maSlow = Array.isArray(indicators.maSlow) ? indicators.maSlow : [];
158
+ if (maFast.length < 2 || maSlow.length < 2) {
159
+ return strategyApi.skip("WAIT_MA_DATA");
160
+ }
161
+ const cross = detectCross(maFast, maSlow);
162
+ const position = await strategyApi.getCurrentPosition();
163
+ const positionExists = Boolean(
164
+ position && typeof position.qty === "number" && position.qty > 0
165
+ );
166
+ if (positionExists && position) {
167
+ if (position.direction === "LONG" && cross?.kind === "bearish" || position.direction === "SHORT" && cross?.kind === "bullish") {
168
+ const { currentPrice: currentPrice2, timestamp: timestamp2 } = await strategyApi.getMarketData();
169
+ return {
170
+ kind: "exit",
171
+ code: "CLOSE_BY_OPPOSITE_MA_CROSS",
172
+ closePlan: {
173
+ price: currentPrice2,
174
+ timestamp: timestamp2,
175
+ direction: position.direction
176
+ }
177
+ };
178
+ }
179
+ return strategyApi.skip("POSITION_HELD");
180
+ }
181
+ if (!cross) {
182
+ return strategyApi.skip("NO_CROSS");
183
+ }
184
+ const modeConfig = cross.kind === "bullish" ? LONG : SHORT;
185
+ if (!modeConfig.enable) {
186
+ return strategyApi.skip("STRATEGY_DISABLED");
187
+ }
188
+ const { fullData, timestamp, currentPrice } = await strategyApi.getMarketData();
189
+ if (lastTradeController.isInCooldown(timestamp)) {
190
+ return strategyApi.skip("TRADE_COOLDOWN");
191
+ }
192
+ const { stopLossPrice, takeProfitPrice, riskRatio, qty } = strategyApi.getDirectionalTpSlPrices({
193
+ price: currentPrice,
194
+ direction: modeConfig.direction,
195
+ takeProfitDelta: modeConfig.TP,
196
+ stopLossDelta: modeConfig.SL,
197
+ unit: "percent",
198
+ maxLossValue: MAX_LOSS_VALUE,
199
+ feePercent: Number(FEE_PERCENT ?? 0)
200
+ });
201
+ if (!qty || !Number.isFinite(qty) || qty <= 0) {
202
+ return strategyApi.skip("INVALID_QTY");
203
+ }
204
+ if (riskRatio <= modeConfig.minRiskRatio) {
205
+ return strategyApi.skip(`RISK_RATIO:${round(riskRatio)}`);
206
+ }
207
+ const correlation = indicatorsState.latestNumber("correlation");
208
+ if (ENV !== "BACKTEST" && correlation != null && correlation >= MAX_CORRELATION) {
209
+ return strategyApi.skip(`MAX_CORRELATION:${round(correlation)}`);
210
+ }
211
+ lastTradeController.markTrade(timestamp);
212
+ return strategyApi.entry({
213
+ code: cross.kind === "bullish" ? "MA_BULLISH_CROSS" : "MA_BEARISH_CROSS",
214
+ direction: modeConfig.direction,
215
+ figures: buildMaStrategyFigures({
216
+ fullData,
217
+ maFast,
218
+ maSlow,
219
+ crossTimestamp: timestamp,
220
+ crossPrice: currentPrice,
221
+ crossKind: cross.kind
222
+ }),
223
+ indicators,
224
+ additionalIndicators: {
225
+ crossKind: cross.kind,
226
+ maFastPrev: cross.maFastPrev,
227
+ maFastCurrent: cross.maFastCurrent,
228
+ maSlowPrev: cross.maSlowPrev,
229
+ maSlowCurrent: cross.maSlowCurrent,
230
+ maGap: cross.maFastCurrent - cross.maSlowCurrent,
231
+ correlation
232
+ },
233
+ orderPlan: {
234
+ qty,
235
+ stopLossPrice,
236
+ takeProfits: [{ rate: 1, price: takeProfitPrice }]
237
+ }
238
+ });
239
+ };
240
+ };
241
+
242
+ // src/MaStrategy/strategy.ts
243
+ var MaStrategyCreator = createStrategyRuntime({
244
+ strategyName: "MaStrategy",
245
+ defaults: config,
246
+ createCore: createMaStrategyCore,
247
+ manifest: maStrategyManifest,
248
+ strategyDirectory: __dirname
249
+ });
250
+ export {
251
+ MaStrategyCreator
252
+ };
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@tradejs/strategies",
3
+ "version": "1.0.0",
4
+ "description": "Built-in TradeJS strategy plugin catalog.",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.mjs",
15
+ "require": "./dist/index.js"
16
+ }
17
+ },
18
+ "dependencies": {
19
+ "@tradejs/core": "^1.0.0",
20
+ "@tradejs/indicators": "^1.0.0",
21
+ "@tradejs/node": "^1.0.0",
22
+ "@tradejs/types": "^1.0.0"
23
+ },
24
+ "devDependencies": {
25
+ "tsup": "^8.5.1",
26
+ "typescript": "^5.1"
27
+ },
28
+ "scripts": {
29
+ "build": "tsup",
30
+ "typecheck": "tsc -p ./tsconfig.json --noEmit"
31
+ },
32
+ "license": "MIT",
33
+ "author": "aleksnick (https://github.com/aleksnick)"
34
+ }