AlgoTradeKit 0.7.2__tar.gz → 0.7.3__tar.gz
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.
- {algotradekit-0.7.2 → algotradekit-0.7.3}/CHANGELOG.md +59 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/PKG-INFO +49 -2
- {algotradekit-0.7.2 → algotradekit-0.7.3}/README.md +48 -1
- {algotradekit-0.7.2 → algotradekit-0.7.3}/pyproject.toml +1 -1
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/__init__.py +1 -1
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/simulate/__init__.py +2 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/simulate/_config.py +52 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/simulate/_engine.py +199 -56
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/simulate/_position.py +23 -6
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/simulate/_runner.py +9 -6
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/strategy/_types.py +24 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/tests/test_simulate.py +352 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/tests/test_strategy.py +21 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/.github/workflows/publish.yml +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/.gitignore +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/LICENSE +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/data/__init__.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/data/_utils.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/data/collector.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/data/converter.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/data/normalizer.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/data/sources/__init__.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/data/sources/base.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/data/sources/binance.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/data/storage/__init__.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/data/storage/csv_handler.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/indicator/__init__.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/indicator/_base.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/indicator/atr.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/indicator/ichimoku.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/indicator/ma.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/indicator/macd.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/indicator/rsi.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/report/__init__.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/report/_builder.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/report/_display.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/report/_server.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/report/static/report.html +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/simulate/_lot.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/simulate/_report.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/simulate/_session.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/strategy/__init__.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/strategy/_base.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/strategy/builtin/__init__.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/strategy/builtin/macd.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/visual/__init__.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/visual/chart.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/visual/indicator_renderer.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/visual/models.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/visual/server.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/src/AlgoTradeKit/visual/static/index.html +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/tests/test_data.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/tests/test_data_converter.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/tests/test_data_normalizer.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/tests/test_indicator.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/tests/test_report.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/tests/test_visual.py +0 -0
- {algotradekit-0.7.2 → algotradekit-0.7.3}/tests/test_visual__0_4_1.py +0 -0
|
@@ -7,6 +7,65 @@ Versioning: [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [0.7.3] — 2026-06-21
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
#### `strategy` module
|
|
15
|
+
- **`Signal.risk_multiplier`** — new optional field (default `1.0`) that scales
|
|
16
|
+
the size/risk a single signal receives relative to the run's global
|
|
17
|
+
`SimulateConfig` sizing (`risk_percent` / `fixed_amount` / `fixed_lot`).
|
|
18
|
+
Lets a strategy vary risk per signal instead of using one fixed sizing
|
|
19
|
+
rule for every trade in a backtest — e.g. half size on a tentative
|
|
20
|
+
"limit" entry vs. full size on a confirmed one, or splitting one trade
|
|
21
|
+
idea into several sub-positions whose sizes sum to one risk unit
|
|
22
|
+
(three signals at `1/3` each, targeting 1R/2R/3R, to emulate scaling out
|
|
23
|
+
of a position). Default `1.0` is fully backward compatible.
|
|
24
|
+
|
|
25
|
+
#### `simulate` module
|
|
26
|
+
- **`SimulateConfig.tp_level_close_fractions`** — optional list, parallel to
|
|
27
|
+
`tp_levels`, that turns on **true partial closes** for `tp_mode="multi_rr"`.
|
|
28
|
+
Each entry is the fraction of the position's *original* size to realise
|
|
29
|
+
when the corresponding level is reached, instead of only moving the SL.
|
|
30
|
+
`None` (default) preserves the exact pre-0.7.3 behaviour (SL-only
|
|
31
|
+
advancement, single full close at the final level). When set:
|
|
32
|
+
- fractions summing to `1.0` give a full progressive scale-out
|
|
33
|
+
(e.g. `[1/3, 1/3, 1/3]`) — profit is banked level by level instead of
|
|
34
|
+
all-or-nothing at the end.
|
|
35
|
+
- all-zero fractions give a "let it run" position: the SL just walks
|
|
36
|
+
through every level and the full size stays open, trailing at the
|
|
37
|
+
last level's price, until eventually stopped out.
|
|
38
|
+
- any mix is valid — partially bank some levels, let others purely
|
|
39
|
+
advance the SL.
|
|
40
|
+
|
|
41
|
+
Each partial close produces its own `ClosedTrade` sharing the position's
|
|
42
|
+
`trade_id`, so `SimulateReport` statistics (win rate, profit factor,
|
|
43
|
+
equity curve) count every realised slice on its own line, the same way
|
|
44
|
+
a real scaled-out position would appear on a broker statement.
|
|
45
|
+
Commission (pre-charged once, in full, at entry) is split proportionally
|
|
46
|
+
across every slice so the total charged across one position's full
|
|
47
|
+
lifetime never changes.
|
|
48
|
+
- **`CLOSE_REASON_TP_PARTIAL`** is now exported from `AlgoTradeKit.simulate`.
|
|
49
|
+
It existed on `ClosedTrade` since an earlier release (with `is_tp`
|
|
50
|
+
already wired to recognise it) but no code path had ever produced it
|
|
51
|
+
until `tp_level_close_fractions` above.
|
|
52
|
+
- `run_multi()` gained the identical partial-close handling as the
|
|
53
|
+
single-pair `Simulate` engine, so shared-wallet multi-symbol portfolios
|
|
54
|
+
behave the same way; `run_batch()` needed no changes (it only wraps
|
|
55
|
+
`Simulate(cfg).run()`).
|
|
56
|
+
|
|
57
|
+
### Tests
|
|
58
|
+
- `tests/test_strategy.py` — `TestSignal` extended with 5 tests for
|
|
59
|
+
`risk_multiplier` (default, custom value, `<= 0` validation, positional-
|
|
60
|
+
construction backward compatibility).
|
|
61
|
+
- `tests/test_simulate.py` — new `TestEngineRiskMultiplier` (4 tests) and
|
|
62
|
+
`TestEngineMultiRRPartialClose` (8 tests, including a hand-derived PnL
|
|
63
|
+
ground truth and an end-to-end wallet-conservation check) and
|
|
64
|
+
`TestSimulateConfigPartialTPValidation` (8 tests). All 171 pre-existing
|
|
65
|
+
tests in these two files continue to pass unmodified.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
10
69
|
## [0.7.2] — 2026-06-20
|
|
11
70
|
|
|
12
71
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: AlgoTradeKit
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.3
|
|
4
4
|
Summary: Algorithmic Trading Toolkit — data collection, indicators, strategy, simulation, and live trading
|
|
5
5
|
Project-URL: Homepage, https://github.com/AmirMohammadBazdar/AlgoTradeKit
|
|
6
6
|
Project-URL: Repository, https://github.com/AmirMohammadBazdar/AlgoTradeKit
|
|
@@ -308,12 +308,48 @@ report = Simulate(config).run(strategy_result)
|
|
|
308
308
|
| `tp_mode` | `"signal"` | `"signal"/"fixed_rr"/"multi_rr"/"none"` |
|
|
309
309
|
| `tp_rr` | 2.0 | R:R ratio for `fixed_rr` mode |
|
|
310
310
|
| `tp_levels` | [1,2,3] | R levels for `multi_rr` mode |
|
|
311
|
+
| `tp_level_close_fractions` | `None` | **v0.7.3** Fraction of original size to realise at each `tp_levels` entry (`multi_rr` only) — see below |
|
|
311
312
|
| `sl_mode` | `"signal"` | `"signal"` or `"trailing"` |
|
|
312
313
|
| `risk_free_enabled` | False | Move SL to break-even at `risk_free_at_rr` |
|
|
313
314
|
| `show_chart` | **False** | **v0.7.0** Open candle chart after run |
|
|
314
315
|
| `report_mode` | **`"none"`** | **v0.7.0** Post-run report rendering |
|
|
315
316
|
| `report_save_path` | `"report.html"` | **v0.7.0** HTML save path |
|
|
316
317
|
|
|
318
|
+
### Per-Signal Sizing & Partial Take-Profit (v0.7.3)
|
|
319
|
+
|
|
320
|
+
`Signal.risk_multiplier` scales one signal's size relative to the run's
|
|
321
|
+
sizing config — useful for varying risk by context, or for splitting one
|
|
322
|
+
trade idea into several sub-positions whose sizes sum to one risk unit:
|
|
323
|
+
|
|
324
|
+
```python
|
|
325
|
+
# Three signals at the same candle, each risking 1/3 of a normal trade,
|
|
326
|
+
# targeting 1R / 2R / 3R — equivalent to scaling out of one position.
|
|
327
|
+
for i, tp_r in enumerate([1, 2, 3], start=1):
|
|
328
|
+
signals.append(Signal(
|
|
329
|
+
direction="long", entry_price=entry, stop_loss=sl,
|
|
330
|
+
take_profit=entry + tp_r * (entry - sl),
|
|
331
|
+
timestamp=ts, candle_index=idx, timeframe="1h",
|
|
332
|
+
risk_multiplier=1 / 3,
|
|
333
|
+
))
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
`SimulateConfig.tp_level_close_fractions` turns `multi_rr` into a true
|
|
337
|
+
scale-out — each level realises a real, partial close instead of only
|
|
338
|
+
moving the SL:
|
|
339
|
+
|
|
340
|
+
```python
|
|
341
|
+
config = SimulateConfig(
|
|
342
|
+
...,
|
|
343
|
+
tp_mode="multi_rr",
|
|
344
|
+
tp_levels=[1.0, 2.0, 3.0],
|
|
345
|
+
tp_level_close_fractions=[1 / 3, 1 / 3, 1 / 3], # bank 1/3 at each level
|
|
346
|
+
)
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
Set every fraction to `0.0` instead to get the opposite pattern — SL walks
|
|
350
|
+
through every level but nothing closes until the position is eventually
|
|
351
|
+
stopped out, letting winners run with a trailing stop and no fixed target.
|
|
352
|
+
|
|
317
353
|
### Batch Sweep
|
|
318
354
|
|
|
319
355
|
```python
|
|
@@ -562,9 +598,20 @@ Two browser tabs open:
|
|
|
562
598
|
|---|---|---|
|
|
563
599
|
| `TP_MODE_SIGNAL` | `"signal"` | Use Signal's take_profit |
|
|
564
600
|
| `TP_MODE_FIXED_RR` | `"fixed_rr"` | entry ± `tp_rr` × SL distance |
|
|
565
|
-
| `TP_MODE_MULTI_RR` | `"multi_rr"` | Multiple levels, SL trails |
|
|
601
|
+
| `TP_MODE_MULTI_RR` | `"multi_rr"` | Multiple levels, SL trails (and optionally partially closes — see `tp_level_close_fractions` *(v0.7.3)*) |
|
|
566
602
|
| `TP_MODE_NONE` | `"none"` | No TP |
|
|
567
603
|
|
|
604
|
+
### Close Reason Constants
|
|
605
|
+
|
|
606
|
+
| Constant | Value | Meaning |
|
|
607
|
+
|---|---|---|
|
|
608
|
+
| `CLOSE_REASON_SL` | `"sl"` | Stop loss hit |
|
|
609
|
+
| `CLOSE_REASON_TP` | `"tp"` | Take profit hit — nothing left open afterwards |
|
|
610
|
+
| `CLOSE_REASON_TP_PARTIAL` | `"tp_rr"` | **v0.7.3** Intermediate `multi_rr` level partially realised — position still open with reduced size |
|
|
611
|
+
| `CLOSE_REASON_RF` | `"rf"` | Risk-free / trailed SL hit |
|
|
612
|
+
| `CLOSE_REASON_FC` | `"force_close"` | Closed by an `ExitSignal` |
|
|
613
|
+
| `CLOSE_REASON_EOD` | `"end_of_data"` | Still open when data ran out |
|
|
614
|
+
|
|
568
615
|
### SL Mode Constants
|
|
569
616
|
|
|
570
617
|
| Constant | Value | Behaviour |
|
|
@@ -252,12 +252,48 @@ report = Simulate(config).run(strategy_result)
|
|
|
252
252
|
| `tp_mode` | `"signal"` | `"signal"/"fixed_rr"/"multi_rr"/"none"` |
|
|
253
253
|
| `tp_rr` | 2.0 | R:R ratio for `fixed_rr` mode |
|
|
254
254
|
| `tp_levels` | [1,2,3] | R levels for `multi_rr` mode |
|
|
255
|
+
| `tp_level_close_fractions` | `None` | **v0.7.3** Fraction of original size to realise at each `tp_levels` entry (`multi_rr` only) — see below |
|
|
255
256
|
| `sl_mode` | `"signal"` | `"signal"` or `"trailing"` |
|
|
256
257
|
| `risk_free_enabled` | False | Move SL to break-even at `risk_free_at_rr` |
|
|
257
258
|
| `show_chart` | **False** | **v0.7.0** Open candle chart after run |
|
|
258
259
|
| `report_mode` | **`"none"`** | **v0.7.0** Post-run report rendering |
|
|
259
260
|
| `report_save_path` | `"report.html"` | **v0.7.0** HTML save path |
|
|
260
261
|
|
|
262
|
+
### Per-Signal Sizing & Partial Take-Profit (v0.7.3)
|
|
263
|
+
|
|
264
|
+
`Signal.risk_multiplier` scales one signal's size relative to the run's
|
|
265
|
+
sizing config — useful for varying risk by context, or for splitting one
|
|
266
|
+
trade idea into several sub-positions whose sizes sum to one risk unit:
|
|
267
|
+
|
|
268
|
+
```python
|
|
269
|
+
# Three signals at the same candle, each risking 1/3 of a normal trade,
|
|
270
|
+
# targeting 1R / 2R / 3R — equivalent to scaling out of one position.
|
|
271
|
+
for i, tp_r in enumerate([1, 2, 3], start=1):
|
|
272
|
+
signals.append(Signal(
|
|
273
|
+
direction="long", entry_price=entry, stop_loss=sl,
|
|
274
|
+
take_profit=entry + tp_r * (entry - sl),
|
|
275
|
+
timestamp=ts, candle_index=idx, timeframe="1h",
|
|
276
|
+
risk_multiplier=1 / 3,
|
|
277
|
+
))
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
`SimulateConfig.tp_level_close_fractions` turns `multi_rr` into a true
|
|
281
|
+
scale-out — each level realises a real, partial close instead of only
|
|
282
|
+
moving the SL:
|
|
283
|
+
|
|
284
|
+
```python
|
|
285
|
+
config = SimulateConfig(
|
|
286
|
+
...,
|
|
287
|
+
tp_mode="multi_rr",
|
|
288
|
+
tp_levels=[1.0, 2.0, 3.0],
|
|
289
|
+
tp_level_close_fractions=[1 / 3, 1 / 3, 1 / 3], # bank 1/3 at each level
|
|
290
|
+
)
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
Set every fraction to `0.0` instead to get the opposite pattern — SL walks
|
|
294
|
+
through every level but nothing closes until the position is eventually
|
|
295
|
+
stopped out, letting winners run with a trailing stop and no fixed target.
|
|
296
|
+
|
|
261
297
|
### Batch Sweep
|
|
262
298
|
|
|
263
299
|
```python
|
|
@@ -506,9 +542,20 @@ Two browser tabs open:
|
|
|
506
542
|
|---|---|---|
|
|
507
543
|
| `TP_MODE_SIGNAL` | `"signal"` | Use Signal's take_profit |
|
|
508
544
|
| `TP_MODE_FIXED_RR` | `"fixed_rr"` | entry ± `tp_rr` × SL distance |
|
|
509
|
-
| `TP_MODE_MULTI_RR` | `"multi_rr"` | Multiple levels, SL trails |
|
|
545
|
+
| `TP_MODE_MULTI_RR` | `"multi_rr"` | Multiple levels, SL trails (and optionally partially closes — see `tp_level_close_fractions` *(v0.7.3)*) |
|
|
510
546
|
| `TP_MODE_NONE` | `"none"` | No TP |
|
|
511
547
|
|
|
548
|
+
### Close Reason Constants
|
|
549
|
+
|
|
550
|
+
| Constant | Value | Meaning |
|
|
551
|
+
|---|---|---|
|
|
552
|
+
| `CLOSE_REASON_SL` | `"sl"` | Stop loss hit |
|
|
553
|
+
| `CLOSE_REASON_TP` | `"tp"` | Take profit hit — nothing left open afterwards |
|
|
554
|
+
| `CLOSE_REASON_TP_PARTIAL` | `"tp_rr"` | **v0.7.3** Intermediate `multi_rr` level partially realised — position still open with reduced size |
|
|
555
|
+
| `CLOSE_REASON_RF` | `"rf"` | Risk-free / trailed SL hit |
|
|
556
|
+
| `CLOSE_REASON_FC` | `"force_close"` | Closed by an `ExitSignal` |
|
|
557
|
+
| `CLOSE_REASON_EOD` | `"end_of_data"` | Still open when data ran out |
|
|
558
|
+
|
|
512
559
|
### SL Mode Constants
|
|
513
560
|
|
|
514
561
|
| Constant | Value | Behaviour |
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "AlgoTradeKit"
|
|
7
|
-
version = "0.7.
|
|
7
|
+
version = "0.7.3"
|
|
8
8
|
description = "Algorithmic Trading Toolkit — data collection, indicators, strategy, simulation, and live trading"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { file = "LICENSE" }
|
|
@@ -99,6 +99,7 @@ from ._position import (
|
|
|
99
99
|
CLOSE_REASON_RF,
|
|
100
100
|
CLOSE_REASON_SL,
|
|
101
101
|
CLOSE_REASON_TP,
|
|
102
|
+
CLOSE_REASON_TP_PARTIAL,
|
|
102
103
|
ClosedTrade,
|
|
103
104
|
)
|
|
104
105
|
from ._report import (
|
|
@@ -139,6 +140,7 @@ __all__ = [
|
|
|
139
140
|
"ClosedTrade",
|
|
140
141
|
"CLOSE_REASON_SL",
|
|
141
142
|
"CLOSE_REASON_TP",
|
|
143
|
+
"CLOSE_REASON_TP_PARTIAL",
|
|
142
144
|
"CLOSE_REASON_RF",
|
|
143
145
|
"CLOSE_REASON_FC",
|
|
144
146
|
"CLOSE_REASON_EOD",
|
|
@@ -121,6 +121,37 @@ class SimulateConfig:
|
|
|
121
121
|
For ``"multi_rr"`` mode: list of R-multiples at which TP levels sit
|
|
122
122
|
(default: ``[1.0, 2.0, 3.0]``). After each level is hit, SL moves
|
|
123
123
|
to the previous level (0.0 = entry, 1.0 = first TP, etc.).
|
|
124
|
+
tp_level_close_fractions : list[float] | None
|
|
125
|
+
For ``"multi_rr"`` mode only. Optional list, same length as
|
|
126
|
+
``tp_levels``, giving the fraction of the position's **original**
|
|
127
|
+
size to actually realise (partially close) when each corresponding
|
|
128
|
+
level is reached. Each value must be in ``[0.0, 1.0]`` and the
|
|
129
|
+
values must sum to ``<= 1.0``.
|
|
130
|
+
|
|
131
|
+
``None`` (default) preserves the exact pre-v0.7.3 behaviour: no
|
|
132
|
+
level closes anything — the position's SL simply walks forward
|
|
133
|
+
through the levels, and only the *final* level fully closes the
|
|
134
|
+
position. This default is unchanged for full backward compatibility.
|
|
135
|
+
|
|
136
|
+
When provided, every level (including the last) closes **only**
|
|
137
|
+
the fraction you specify — there is no automatic "close everything"
|
|
138
|
+
special-case for the final level anymore. Two common patterns:
|
|
139
|
+
|
|
140
|
+
* **Scale out completely** — fractions summing to ``1.0``
|
|
141
|
+
(e.g. ``[1/3, 1/3, 1/3]`` for ``tp_levels=[1, 2, 3]``) realises
|
|
142
|
+
profit progressively and leaves nothing open after the last level.
|
|
143
|
+
* **Trail forever, never auto-realise** — all-zero fractions
|
|
144
|
+
(e.g. ``[0.0] * 20`` for ``tp_levels=[1, 2, ..., 20]``) never
|
|
145
|
+
partially closes anything; the SL just keeps stepping up through
|
|
146
|
+
every level and the full size stays open, trailing at the last
|
|
147
|
+
level's price, until it is eventually stopped out, force-closed,
|
|
148
|
+
or hits end-of-data.
|
|
149
|
+
|
|
150
|
+
Each partial close produces its own ``ClosedTrade`` (sharing the
|
|
151
|
+
same ``trade_id`` as the rest of that position's closes), so
|
|
152
|
+
``SimulateReport`` statistics count each realised slice separately
|
|
153
|
+
— matching how a real scaled-out position would show up on a
|
|
154
|
+
broker statement.
|
|
124
155
|
sl_mode : str
|
|
125
156
|
Stop-loss mode: ``"signal"`` (use SL from Signal) or ``"trailing"``
|
|
126
157
|
(default: ``"signal"``).
|
|
@@ -199,6 +230,7 @@ class SimulateConfig:
|
|
|
199
230
|
tp_mode: str = TP_MODE_SIGNAL
|
|
200
231
|
tp_rr: float = 2.0
|
|
201
232
|
tp_levels: list[float] = field(default_factory=lambda: [1.0, 2.0, 3.0])
|
|
233
|
+
tp_level_close_fractions: list[float] | None = None
|
|
202
234
|
|
|
203
235
|
# ------------------------------------------------------------------
|
|
204
236
|
# Stop-loss
|
|
@@ -291,6 +323,26 @@ class SimulateConfig:
|
|
|
291
323
|
raise ValueError(
|
|
292
324
|
"SimulateConfig.tp_levels must be a non-empty list when tp_mode='multi_rr'."
|
|
293
325
|
)
|
|
326
|
+
if self.tp_level_close_fractions is not None:
|
|
327
|
+
if self.tp_mode != TP_MODE_MULTI_RR:
|
|
328
|
+
raise ValueError(
|
|
329
|
+
"SimulateConfig.tp_level_close_fractions requires tp_mode='multi_rr'."
|
|
330
|
+
)
|
|
331
|
+
if len(self.tp_level_close_fractions) != len(self.tp_levels):
|
|
332
|
+
raise ValueError(
|
|
333
|
+
"SimulateConfig.tp_level_close_fractions must be the same length as "
|
|
334
|
+
f"tp_levels ({len(self.tp_levels)}), got "
|
|
335
|
+
f"{len(self.tp_level_close_fractions)}."
|
|
336
|
+
)
|
|
337
|
+
if any(not (0.0 <= f <= 1.0) for f in self.tp_level_close_fractions):
|
|
338
|
+
raise ValueError(
|
|
339
|
+
"SimulateConfig.tp_level_close_fractions values must all be in [0.0, 1.0]."
|
|
340
|
+
)
|
|
341
|
+
if sum(self.tp_level_close_fractions) > 1.0 + 1e-9:
|
|
342
|
+
raise ValueError(
|
|
343
|
+
"SimulateConfig.tp_level_close_fractions must sum to <= 1.0, got "
|
|
344
|
+
f"{sum(self.tp_level_close_fractions)!r}."
|
|
345
|
+
)
|
|
294
346
|
if not self.config_id:
|
|
295
347
|
self.config_id = self._auto_id()
|
|
296
348
|
if self.report_mode not in (
|