@backtest-kit/sidekick 0.1.1 → 3.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.
Files changed (43) hide show
  1. package/README.md +120 -13
  2. package/content/config/source/timeframe_15m.pine +114 -0
  3. package/content/config/source/timeframe_4h.pine +57 -0
  4. package/content/config/symbol.config.cjs +460 -0
  5. package/content/docker/ollama/docker-compose.yaml +34 -0
  6. package/content/docker/ollama/watch.sh +2 -0
  7. package/content/scripts/cache/cache_candles.mjs +47 -0
  8. package/content/scripts/cache/cache_model.mjs +42 -0
  9. package/content/scripts/cache/validate_candles.mjs +46 -0
  10. package/content/scripts/run_timeframe_15m.mjs +77 -0
  11. package/content/scripts/run_timeframe_4h.mjs +68 -0
  12. package/package.json +2 -2
  13. package/scripts/init.mjs +40 -9
  14. package/src/classes/BacktestLowerStopOnBreakevenAction.mjs +18 -0
  15. package/src/classes/{PartialProfitTakingAction.mjs → BacktestPartialProfitTakingAction.mjs} +7 -2
  16. package/src/classes/BacktestPositionMonitorAction.mjs +57 -0
  17. package/src/config/params.mjs +1 -0
  18. package/src/config/setup.mjs +27 -1
  19. package/src/enum/ActionName.mjs +3 -1
  20. package/src/enum/FrameName.mjs +1 -0
  21. package/src/enum/RiskName.mjs +1 -1
  22. package/src/logic/action/backtest_lower_stop_on_breakeven.action.mjs +9 -0
  23. package/src/logic/action/backtest_partial_profit_taking.action.mjs +9 -0
  24. package/src/logic/action/backtest_position_monitor.action.mjs +9 -0
  25. package/src/logic/exchange/binance.exchange.mjs +12 -2
  26. package/src/logic/frame/feb_2024.frame.mjs +10 -0
  27. package/src/logic/index.mjs +5 -2
  28. package/src/logic/risk/sl_distance.risk.mjs +32 -0
  29. package/src/logic/risk/tp_distance.risk.mjs +5 -3
  30. package/src/logic/strategy/main.strategy.mjs +29 -10
  31. package/src/main/bootstrap.mjs +52 -0
  32. package/src/math/timeframe_15m.math.mjs +68 -0
  33. package/src/math/timeframe_4h.math.mjs +53 -0
  34. package/src/utils/getArgs.mjs +15 -23
  35. package/template/CLAUDE.mustache +421 -0
  36. package/template/README.mustache +232 -24
  37. package/template/env.mustache +1 -17
  38. package/template/jsconfig.json.mustache +1 -0
  39. package/template/package.mustache +8 -6
  40. package/src/func/market.func.mjs +0 -46
  41. package/src/logic/action/partial_profit_taking.action.mjs +0 -8
  42. package/src/logic/risk/rr_ratio.risk.mjs +0 -39
  43. /package/{types/backtest-kit.d.ts → template/types.mustache} +0 -0
@@ -1,48 +1,256 @@
1
1
  # {{PROJECT_NAME}}
2
2
 
3
- A trading bot project powered by [Backtest Kit](https://github.com/tripolskypetr/backtest-kit).
3
+ > Multi-timeframe crypto trading strategy built on [backtest-kit](https://libraries.io/npm/backtest-kit) with Pine Script indicators via [PineTS](https://github.com/QuantForgeOrg/PineTS) runtime. Uses a 4H trend filter + 15m signal generator to open long/short positions on Binance spot market with partial profit taking, breakeven trailing stops, and risk validation.
4
4
 
5
- ## 🚀 Getting Started
5
+ ![screenshot](https://raw.githubusercontent.com/tripolskypetr/backtest-kit/HEAD/assets/screenshots/screenshot8.png)
6
6
 
7
- ### Configure API Keys
7
+ [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/tripolskypetr/backtest-kit)
8
+ [![npm](https://img.shields.io/npm/v/backtest-kit.svg?style=flat-square)](https://npmjs.org/package/backtest-kit)
9
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue)]()
10
+ [![Build](https://github.com/tripolskypetr/backtest-kit/actions/workflows/webpack.yml/badge.svg)](https://github.com/tripolskypetr/backtest-kit/actions/workflows/webpack.yml)
8
11
 
9
- Edit `.env` file and add your API keys:
12
+ ## Features
13
+
14
+ - 📊 **Multi-timeframe analysis** — 4H daily trend filter (RSI + MACD + ADX) combined with 15m entry signals (EMA crossover + volume spike + momentum)
15
+ - 📜 **Pine Script indicators** — strategies written in TradingView Pine Script v5, executed locally via `@backtest-kit/pinets`
16
+ - 🛡️ **Risk management** — SL/TP distance validation, Kelly-optimized partial profit taking (33/33/34%), breakeven trailing stop
17
+ - 🔄 **Position lifecycle** — full monitoring with scheduled/opened/closed/cancelled event logging
18
+ - 🔌 **Binance integration** — OHLCV candles, order book depth, tick-precise price/quantity formatting via CCXT
19
+ - 🕐 **Historical frames** — predefined backtest periods covering bull runs, sharp drops, and sideways markets
20
+ - 🎨 **Web UI dashboard** — interactive charting via `@backtest-kit/ui`
21
+ - 💾 **Persistent storage** — crash-safe state with atomic persistence for both backtest and live modes
22
+
23
+ ## 🏗️ Architecture
24
+
25
+ ```
26
+ src/
27
+ ├── index.mjs # Entry point — loads config, logic, bootstrap
28
+ ├── main/bootstrap.mjs # Mode dispatcher (backtest / paper / live)
29
+ ├── config/
30
+ │ ├── setup.mjs # Logger, storage, notifications, UI server
31
+ │ ├── validate.mjs # Schema validation for all enums
32
+ │ ├── params.mjs # Environment variables (Ollama API key)
33
+ │ └── ccxt.mjs # Binance exchange singleton via CCXT
34
+ ├── logic/
35
+ │ ├── strategy/main.strategy.mjs # Main strategy — multi-TF signal logic
36
+ │ ├── exchange/binance.exchange.mjs # Exchange schema — candles, order book, formatting
37
+ │ ├── frame/*.frame.mjs # Backtest time frames (Feb 2024, Oct–Dec 2025)
38
+ │ ├── risk/sl_distance.risk.mjs # Stop-loss distance validation (≥0.2%)
39
+ │ ├── risk/tp_distance.risk.mjs # Take-profit distance validation (≥0.2%)
40
+ │ └── action/
41
+ │ ├── backtest_partial_profit_taking.action.mjs
42
+ │ ├── backtest_lower_stop_on_breakeven.action.mjs
43
+ │ └── backtest_position_monitor.action.mjs
44
+ ├── classes/
45
+ │ ├── BacktestPartialProfitTakingAction.mjs # Scale out at 3 TP levels
46
+ │ ├── BacktestLowerStopOnBreakevenAction.mjs # Trailing stop on breakeven
47
+ │ └── BacktestPositionMonitorAction.mjs # Position event logger
48
+ ├── math/
49
+ │ ├── timeframe_4h.math.mjs # 4H trend data — RSI, MACD, ADX, DI+/DI-
50
+ │ └── timeframe_15m.math.mjs # 15m signal data — EMA, ATR, volume, momentum
51
+ ├── enum/ # String constants for type-safe schema refs
52
+ │ ├── ExchangeName.mjs
53
+ │ ├── StrategyName.mjs
54
+ │ ├── FrameName.mjs
55
+ │ ├── RiskName.mjs
56
+ │ └── ActionName.mjs
57
+ └── utils/getArgs.mjs # CLI argument parser with defaults
58
+
59
+ config/source/
60
+ ├── timeframe_4h.pine # Pine Script v5 — Daily Trend Filter (RSI/MACD/ADX)
61
+ └── timeframe_15m.pine # Pine Script v5 — Signal Strategy (EMA/ATR/Volume)
62
+
63
+ scripts/
64
+ ├── run_timeframe_15m.mjs # Standalone 15m Pine Script runner — outputs signal markdown
65
+ ├── run_timeframe_4h.mjs # Standalone 4H Pine Script runner — outputs trend markdown
66
+ └── cache/
67
+ ├── cache_candles.mjs # Pre-download OHLCV candles (1m/15m/4h) for a date range
68
+ ├── validate_candles.mjs # Verify cached candle data integrity and completeness
69
+ └── cache_model.mjs # Pull Ollama LLM model (glm-4.7-flash:q4_K_M) with progress bar
70
+ ```
71
+
72
+ ## 💡 Strategy Overview
73
+
74
+ ### 🎯 4H Trend Filter (`timeframe_4h.pine`)
75
+
76
+ Determines the market regime using three indicators:
77
+
78
+ | Regime | Condition |
79
+ |--------|-----------|
80
+ | **AllowLong** | ADX > 25, MACD histogram > 0, DI+ > DI-, RSI > 50 |
81
+ | **AllowShort** | ADX > 25, MACD histogram < 0, DI- > DI+, RSI < 50 |
82
+ | **AllowBoth** | Strong trend but no clear bull/bear regime |
83
+ | **NoTrades** | ADX ≤ 25 (weak trend) |
84
+
85
+ ### ⚡ 15m Signal Generator (`timeframe_15m.pine`)
86
+
87
+ Generates entry signals with EMA crossover confirmed by volume and momentum:
88
+
89
+ - **Long**: EMA(5) crosses above EMA(13), RSI 40–65, price above EMA(50), volume spike (>1.5x MA), positive momentum
90
+ - **Short**: EMA(5) crosses below EMA(13), RSI 35–60, price below EMA(50), volume spike, negative momentum
91
+ - **SL/TP**: Static 2%/3% from entry price
92
+ - **Signal expiry**: 5 bars
93
+
94
+ ### 🛡️ Risk Filters
95
+
96
+ - Reject signals where SL distance < 0.2% (slippage protection)
97
+ - Reject signals where TP distance < 0.2% (slippage protection)
98
+ - Trend alignment: long signals rejected in bear regime, short signals rejected in bull regime
99
+
100
+ ### 💹 Position Management
101
+
102
+ - **Partial profit taking**: Scale out at 3 levels — 33% at TP3, 33% at TP2, 34% at TP1
103
+ - **Breakeven trailing stop**: When breakeven is reached, lower trailing stop by 3 points
104
+
105
+ ## 🕐 Backtest Frames
106
+
107
+ | Frame | Period | Market Note |
108
+ |-------|--------|-------------|
109
+ | `February2024` | Feb 1–29, 2024 | Bull run |
110
+ | `October2025` | Oct 1–31, 2025 | Sharp drop Oct 9–11 |
111
+ | `November2025` | Nov 1–30, 2025 | Sideways with downtrend |
112
+ | `December2025` | Dec 1–31, 2025 | Sideways, no clear direction |
113
+
114
+ ## 📦 Cache Utilities
115
+
116
+ Standalone scripts in `scripts/cache/` for preparing data before running backtests:
10
117
 
11
118
  ```bash
12
- # For live trading
13
- CC_BINANCE_API_KEY=your_api_key_here
14
- CC_BINANCE_API_SECRET=your_api_secret_here
119
+ # Download 1m, 15m, 4h candles for BTCUSDT (Feb 2024) from Binance
120
+ node scripts/cache/cache_candles.mjs
15
121
 
16
- # For AI strategies
17
- CC_OPENAI_API_KEY=your_openai_key_here
122
+ # Validate downloaded candle data — check for gaps and missing intervals
123
+ node scripts/cache/validate_candles.mjs
124
+
125
+ # Pull the Ollama LLM model (glm-4.7-flash:q4_K_M) with progress bar
126
+ node scripts/cache/cache_model.mjs
18
127
  ```
19
128
 
20
- ### Run the Bot
129
+ `cache_candles.mjs` and `validate_candles.mjs` register a temporary `ccxt-exchange` schema and call `warmCandles` / `checkCandles` from `backtest-kit` to populate and verify the local candle cache. `cache_model.mjs` pulls the `glm-4.7-flash:q4_K_M` model to a local Ollama instance at `http://127.0.0.1:11434`.
130
+
131
+ ## 🔬 Debug Scripts
132
+
133
+ Standalone scripts in `scripts/` for testing Pine Script indicators outside of backtest mode:
21
134
 
22
135
  ```bash
23
- npm start
136
+ # Run 15m signal strategy on BTCUSDT and print markdown output
137
+ node scripts/run_timeframe_15m.mjs
138
+
139
+ # Run 4H trend filter on BTCUSDT and print markdown output
140
+ node scripts/run_timeframe_4h.mjs
24
141
  ```
25
142
 
26
- ## 📚 Documentation
143
+ Both scripts register a temporary Binance exchange, execute the corresponding `.pine` file via `@backtest-kit/pinets`, and output a markdown table with all indicator values (RSI, EMA, MACD, ADX, volume, momentum, etc.) for a fixed historical timestamp. Useful for verifying indicator logic and signal schema mapping without running a full backtest.
27
144
 
28
- - [Backtest Kit Docs](https://backtest-kit.github.io/documents/example_02_first_backtest.html)
29
- - [GitHub Repository](https://github.com/tripolskypetr/backtest-kit)
145
+ ## 📂 Dump Directory Structure
30
146
 
31
- ## 📁 Project Structure
147
+ After running a backtest, all output is written to the `dump/` directory:
32
148
 
33
149
  ```
34
- {{PROJECT_NAME}}/
35
- ├── src/
36
- │ ├── index.mjs # Main entry point
37
- │ └── utils/ # Utility functions
38
- ├── .env # Environment variables
39
- ├── .gitignore # Git ignore rules
40
- └── package.json # Dependencies
150
+ dump/
151
+ ├── data/
152
+ │ ├── candle/{exchange}/{symbol}/{timeframe}/ # Cached OHLCV candles
153
+ └── {timestamp_ms}.json # One candle per file
154
+ ├── notification/backtest/ # Trading event notifications
155
+ │ │ └── {uuid}.json
156
+ └── storage/backtest/ # Signal/position state
157
+ │ └── {signalId}.json
158
+ ├── report/ # JSONL event logs
159
+ │ ├── backtest.jsonl
160
+ │ ├── performance.jsonl
161
+ │ ├── schedule.jsonl
162
+ │ ├── heat.jsonl
163
+ │ ├── breakeven.jsonl
164
+ │ └── partial.jsonl
165
+ └── ta/ # Technical analysis markdown
166
+ ├── math_15m/{signalId}.md
167
+ └── math_4h/{signalId}.md
41
168
  ```
42
169
 
43
- ## 🤝 Contributing
170
+ ### JSONL Reports (`dump/report/`)
171
+
172
+ Each JSONL file is an append-only event log — one JSON object per line with a `reportName`, `data`, and context metadata (`symbol`, `strategyName`, `exchangeName`, `frameName`, `timestamp`).
173
+
174
+ | File | Content |
175
+ |------|---------|
176
+ | `backtest.jsonl` | Strategy execution events (`idle`, `signal`, etc.) with `currentPrice` |
177
+ | `performance.jsonl` | Execution timing metrics (`metricType`, `duration`) |
178
+ | `schedule.jsonl` | Trade scheduling — `signalId`, `position`, `priceOpen`, SL/TP levels |
179
+ | `heat.jsonl` | Position closures — `closeReason` (`time_expired`, `stop_loss`, `take_profit`), `pnl` |
180
+ | `breakeven.jsonl` | Breakeven events — triggered when price reaches entry level |
181
+ | `partial.jsonl` | Partial profit taking — `level`, `partialExecuted`, `action` (`profit`/`loss`) |
182
+
183
+ ### Markdown TA Dumps (`dump/ta/`)
184
+
185
+ Generated by `dumpPlotData()` from `@backtest-kit/pinets`. Each file is a markdown table with all Pine Script indicator values for the corresponding timeframe:
186
+
187
+ - `dump/ta/math_15m/{signalId}.md` — 15m signal indicators (EMA, ATR, Volume, Momentum)
188
+ - `dump/ta/math_4h/{signalId}.md` — 4H trend filter indicators (RSI, MACD, ADX, DI+/DI-)
189
+
190
+ ## 📦 Installation
191
+
192
+ ```bash
193
+ npm install
194
+ ```
195
+
196
+ ## ⚙️ Configuration
197
+
198
+ Copy `.env.example` to `.env` and fill in the required values:
199
+
200
+ ```bash
201
+ cp .env.example .env
202
+ ```
203
+
204
+ ```env
205
+ # Ollama Configuration (for AI strategies)
206
+ CC_OLLAMA_API_KEY=your_ollama_key_here
207
+ ```
208
+
209
+ ## 🚀 Usage
210
+
211
+ ### 🧪 Run Backtest
212
+
213
+ ```bash
214
+ npm start
215
+ ```
216
+
217
+ This runs the default backtest with:
218
+ - **Symbol**: BTCUSDT
219
+ - **Exchange**: Binance
220
+ - **Strategy**: MainStrategy
221
+ - **Frame**: October 2025
222
+
223
+ ### 🔧 CLI Arguments
224
+
225
+ Override defaults via command-line flags (parsed by `backtest-kit`):
226
+
227
+ ```bash
228
+ node ./src/index.mjs --backtest --symbol ETHUSDT --frameName nov_2025_frame
229
+ ```
230
+
231
+ | Flag | Default | Description |
232
+ |------|---------|-------------|
233
+ | `--backtest` | — | Run in backtest mode |
234
+ | `--paper` | — | Run in paper trading mode (not yet implemented) |
235
+ | `--live` | — | Run in live trading mode (not yet implemented) |
236
+ | `--symbol` | `BTCUSDT` | Trading pair |
237
+ | `--strategyName` | `main_strategy` | Strategy to use |
238
+ | `--frameName` | `oct_2025_frame` | Backtest time frame |
239
+ | `--exchangeName` | `binance_exchange` | Exchange connector |
240
+
241
+ ## 📋 Dependencies
44
242
 
45
- Feel free to submit issues and pull requests!
243
+ | Package | Purpose |
244
+ |---------|---------|
245
+ | [backtest-kit](https://libraries.io/npm/backtest-kit) | Core backtesting/trading framework |
246
+ | [@backtest-kit/pinets](https://github.com/QuantForgeOrg/PineTS) | Pine Script v5 runtime for Node.js |
247
+ | [@backtest-kit/ui](https://libraries.io/npm/backtest-kit) | Interactive charting dashboard |
248
+ | [@backtest-kit/ollama](https://libraries.io/npm/backtest-kit) | LLM inference integration |
249
+ | [ccxt](https://github.com/ccxt/ccxt) | Binance exchange connectivity |
250
+ | [functools-kit](https://www.npmjs.com/package/functools-kit) | `singleshot`, `randomString` utilities |
251
+ | [pinolog](https://www.npmjs.com/package/pinolog) | File-based structured logging |
252
+ | [openai](https://www.npmjs.com/package/openai) | OpenAI API client |
253
+ | [ollama](https://www.npmjs.com/package/ollama) | Ollama local LLM client |
46
254
 
47
255
  ## 📜 License
48
256
 
@@ -1,18 +1,2 @@
1
- # Exchange API Keys (for live trading)
2
- # CC_BINANCE_API_KEY=your_api_key_here
3
- # CC_BINANCE_API_SECRET=your_api_secret_here
4
-
5
- # LLM API Keys (for AI strategies)
6
- # CC_OPENAI_API_KEY=your_openai_key_here
7
- # CC_ANTHROPIC_API_KEY=your_anthropic_key_here
8
- # CC_DEEPSEEK_API_KEY=your_deepseek_key_here
9
- # CC_GROK_API_KEY=your_grok_key_here
10
-
11
1
  # Ollama Configuration
12
- # CC_OLLAMA_API_KEY=your_ollama_key_here
13
- # CC_OLLAMA_BASE_URL=https://cloud.ollama.com
14
-
15
- # Trading Configuration
16
- # CC_PERCENT_SLIPPAGE=0.1
17
- # CC_PERCENT_FEE=0.1
18
- # CC_SCHEDULE_AWAIT_MINUTES=120
2
+ CC_OLLAMA_API_KEY=
@@ -4,6 +4,7 @@
4
4
  "moduleResolution": "node",
5
5
  "target": "ES2020",
6
6
  "checkJs": true,
7
+ "noImplicitAny": false,
7
8
  "allowSyntheticDefaultImports": true,
8
9
  "ignoreDeprecations": "6.0",
9
10
  "baseUrl": ".",
@@ -5,17 +5,19 @@
5
5
  "type": "module",
6
6
  "description": "Backtest Kit trading bot project",
7
7
  "scripts": {
8
- "start": "node ./src/index.mjs",
9
- "test": "echo \"Error: no test specified\" && exit 1"
8
+ "start": "node ./src/index.mjs --backtest",
9
+ "postinstall": "npm run postinstall:bun",
10
+ "postinstall:bun": "npm list -g bun || npm install -g bun"
10
11
  },
11
12
  "dependencies": {
12
- "@backtest-kit/ollama": "^0.1.0",
13
- "@backtest-kit/signals": "^0.1.0",
13
+ "@backtest-kit/ollama": "^3.0.6",
14
+ "@backtest-kit/pinets": "^3.0.11",
15
+ "@backtest-kit/ui": "^3.0.8",
14
16
  "@huggingface/inference": "^4.7.1",
15
17
  "@langchain/core": "^0.3.57",
16
18
  "@langchain/xai": "^0.0.2",
17
- "agent-swarm-kit": "^1.1.182",
18
- "backtest-kit": "^2.0.5",
19
+ "agent-swarm-kit": "^1.2.4",
20
+ "backtest-kit": "^3.0.16",
19
21
  "ccxt": "^4.4.41",
20
22
  "dotenv": "^16.4.7",
21
23
  "functools-kit": "^1.0.95",
@@ -1,46 +0,0 @@
1
- import {
2
- commitFifteenMinuteHistory,
3
- commitHourHistory,
4
- commitLongTermMath,
5
- commitMicroTermMath,
6
- commitOneMinuteHistory,
7
- commitShortTermMath,
8
- commitSwingTermMath,
9
- commitThirtyMinuteHistory,
10
- } from "@backtest-kit/signals";
11
- import { formatPrice, getAveragePrice, getDate } from "backtest-kit";
12
- import { str } from "functools-kit";
13
-
14
- const commitHistorySetup = async (symbol, history) => {
15
- // Candle histories across timeframes
16
- {
17
- await commitOneMinuteHistory(symbol, history);
18
- await commitFifteenMinuteHistory(symbol, history);
19
- await commitThirtyMinuteHistory(symbol, history);
20
- await commitHourHistory(symbol, history);
21
- }
22
-
23
- // Technical indicators across timeframes
24
- {
25
- await commitMicroTermMath(symbol, history);
26
- await commitShortTermMath(symbol, history);
27
- await commitSwingTermMath(symbol, history);
28
- await commitLongTermMath(symbol, history);
29
- }
30
-
31
- const displayName = await String(symbol).toUpperCase();
32
-
33
- const currentPrice = await getAveragePrice(symbol);
34
- const currentData = await getDate();
35
-
36
- await history.push({
37
- role: "system",
38
- content: str.newline(
39
- `Trading symbol: ${displayName}`,
40
- `Current price: ${await formatPrice(symbol, currentPrice)} USD`,
41
- `Current time: ${currentData.toISOString()}`
42
- ),
43
- });
44
- };
45
-
46
- export { commitHistorySetup };
@@ -1,8 +0,0 @@
1
- import { addActionSchema } from "backtest-kit";
2
- import ActionName from "../../enum/ActionName.mjs";
3
- import { PartialProfitTakingAction } from "../../classes/PartialProfitTakingAction.mjs";
4
-
5
- addActionSchema({
6
- actionName: ActionName.PartialProfitTakingAction,
7
- handler: PartialProfitTakingAction,
8
- });
@@ -1,39 +0,0 @@
1
- import { addRiskSchema } from "backtest-kit";
2
- import RiskName from "../../enum/RiskName.mjs";
3
-
4
- addRiskSchema({
5
- riskName: RiskName.RiskRewardRatioRisk,
6
- validations: [
7
- {
8
- validate: ({ pendingSignal, currentPrice }) => {
9
- const {
10
- priceOpen = currentPrice,
11
- priceTakeProfit,
12
- priceStopLoss,
13
- position,
14
- } = pendingSignal;
15
- if (!priceOpen) {
16
- return;
17
- }
18
- // Calculate reward (TP distance)
19
- const reward =
20
- position === "long"
21
- ? priceTakeProfit - priceOpen
22
- : priceOpen - priceTakeProfit;
23
- // Calculate risk (SL distance)
24
- const risk =
25
- position === "long"
26
- ? priceOpen - priceStopLoss
27
- : priceStopLoss - priceOpen;
28
- if (risk <= 0) {
29
- throw new Error("Invalid SL: risk must be positive");
30
- }
31
- const rrRatio = reward / risk;
32
- if (rrRatio < 2) {
33
- throw new Error(`RR ratio ${rrRatio.toFixed(2)} < 2:1`);
34
- }
35
- },
36
- note: "Risk-Reward ratio must be at least 1:2",
37
- },
38
- ],
39
- });