e30535f5e9
## What changed ### backtest_gpu.py (new — ~1,300 lines) Standalone GPU-accelerated backtester completely separate from backtest.py. Computes all indicator series in one vectorised O(N) pass over the full price history, then replays positions with a sequential state machine. Key design: - VectorIndicators: pandas ewm/rolling (C-optimised) for all 7 indicators - CuPy GPU backend with automatic NumPy fallback (no hard dependency) - Two-phase API: prepare_arrays() once, score_and_run() many times - --optimize flag: 36-combo weight sweep (1 baseline + 7 ablations + 28 multiplier runs) — calls prepare_arrays() once, replays all combos in seconds - Full CLI: --tf, --local, --start/--end, --regime-filter, --regime-tf, --session-filter, --optimize, --journal, --journal-csv O(N) vs O(N^2): backtest.py recomputes all indicators per bar (O(N^2)); backtest_gpu.py computes each indicator series once and reads bar T in O(1). On 100,000 H1 bars: ~4 seconds vs >10 minutes. ### data_provider.py Added load_local_csv() method — reads MetaTrader tab-delimited CSV exports from market_data/ directory. Format: Time|Open|High|Low|Close|Volume|Spread (spread column dropped on load, timestamps localised to UTC). Applies the same _validate_and_clean() pipeline as the Twelve Data path. LOCAL_CSV_MAP maps each Timeframe enum to its filename. market_data/ files added (MetaTrader exports, UTC): USDJPY_M15.csv 100,000 bars 2022-05-04 to 2026-05-08 USDJPY_H1.csv 100,000 bars 2010-04-23 to 2026-05-08 USDJPY_H4.csv 25,855 bars 2010-04-22 to 2026-05-08 USDJPY_D1.csv 5,020 bars 2010-04-22 to 2026-05-08 16 years of H1/H4/D1 vs ~6 weeks of H1 on Twelve Data free plan. Use --local for all backtests; Twelve Data reserved for live signal feed. ### engine.py — optimised indicator weights 36-combo sweep on H4 2022-2026 found MACD and EMA trend are the dominant signals; RSI and Bollinger are net drag on this trending pair. Old → new weights: ema_trend: 1.2 → 3.6 (×3 — strongest directional signal) macd: 1.1 → 3.3 (×3 — ablating it is the single worst move: -42%) stochastic: 0.8 → 1.6 (×2 — clean timing, 40% WR at this level) adx: 1.0 → 0.5 (÷2 — partially redundant with EMA stack) ichimoku: 1.1 → 0.55 (÷2 — lags too much at H4, hurts in ranging) rsi: 0.3 → 0.0 (ablated — net drag across all test windows) bollinger: 0.9 → 0.0 (ablated — counter-trend noise on trending pair) RSI and Bollinger are still computed and displayed in live output for reference; weight=0 means they contribute nothing to the composite score. Combined result vs baseline (H4 2022-2026, local data): Return: +7.12% → +21.30% (+14.2pp) Max DD: -9.05% → -8.52% (tighter — gains did not come at expense of risk) Sharpe: 0.23 → 0.47 (2x improvement) Sortino: 1.38 → 1.91 Win Rate: 36.3% → 38.1% ### backtest.py Added 16-thread CPU parallelism for signal pre-computation via ThreadPoolExecutor. _precompute_all_signals() dispatches each bar to a worker; pandas/NumPy C extensions release the GIL enabling true parallel execution. Results are collected and replayed in the main loop, replacing the per-bar recompute. Also added --regime-tf CLI flag so regime filter timeframe can be specified independently of the primary TF. ### signal_aggregator.py EMA200 distance gate: confidence *= 0.55 when price is within 0.5% of EMA200 (chop zone) or beyond 1.5% (overextended). Sweet spot is 0.5-1.5% from EMA200 where trending momentum is cleanest. Strongest single filter found: lifted cumulative return +13.88% to +35.50% in ablation window. ### ENGINE_LOGIC.md (new) Full reference document covering: architecture, data pipeline, all indicator logic, composite scoring formula, optimised weights with rationale, regime classification, EMA200 gate, MTF alignment, grade system, backtesting results, filter catalogue, and configuration reference. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
451 lines
15 KiB
Markdown
451 lines
15 KiB
Markdown
# ForexBot2 — Engine Logic & Design Reference
|
||
|
||
> USD/JPY signal intelligence platform.
|
||
> Last updated: 2026-05-08
|
||
|
||
---
|
||
|
||
## Table of Contents
|
||
|
||
1. [Architecture Overview](#1-architecture-overview)
|
||
2. [Data Pipeline](#2-data-pipeline)
|
||
3. [Indicator Engine (`engine.py`)](#3-indicator-engine)
|
||
4. [Signal Aggregator (`signal_aggregator.py`)](#4-signal-aggregator)
|
||
5. [Composite Scoring Formula](#5-composite-scoring-formula)
|
||
6. [Indicator Weights — Optimised](#6-indicator-weights--optimised)
|
||
7. [Market Regime Classification](#7-market-regime-classification)
|
||
8. [EMA200 Distance Gate](#8-ema200-distance-gate)
|
||
9. [Multi-Timeframe Alignment](#9-multi-timeframe-alignment)
|
||
10. [Signal Grade System](#10-signal-grade-system)
|
||
11. [GPU Backtester (`backtest_gpu.py`)](#11-gpu-backtester)
|
||
12. [Backtesting Results Summary](#12-backtesting-results-summary)
|
||
13. [Filter Catalogue](#13-filter-catalogue)
|
||
14. [Configuration Reference](#14-configuration-reference)
|
||
|
||
---
|
||
|
||
## 1. Architecture Overview
|
||
|
||
```
|
||
market_data/ (local CSV)
|
||
│
|
||
▼
|
||
ForexDataProvider ← also pulls live via Twelve Data SDK
|
||
│
|
||
▼
|
||
TechnicalEngine ← computes all indicators bar-by-bar
|
||
│
|
||
▼
|
||
SignalAggregator ← MTF blending, EMA200 gate, grading
|
||
│
|
||
├──▶ Live output (main.py)
|
||
└──▶ GPUBacktester ← vectorised O(N) replay over full history
|
||
```
|
||
|
||
**Key design principle:** No single indicator is trusted alone. The composite score is a confidence-weighted, regime-adjusted blend of up to 7 indicators across 3 timeframes. A signal only fires when direction, strength, and confidence all agree.
|
||
|
||
---
|
||
|
||
## 2. Data Pipeline
|
||
|
||
### Live mode
|
||
`ForexDataProvider.fetch_ohlcv()` calls the Twelve Data SDK with in-memory caching (5-min TTL). Fetches primary TF + two confirmation TFs + optional regime TF.
|
||
|
||
### Backtest mode
|
||
`ForexDataProvider.load_local_csv()` reads MetaTrader tab-delimited exports from `market_data/`:
|
||
|
||
| File | TF | Rows | Date Range |
|
||
|------|----|------|------------|
|
||
| USDJPY_M15.csv | M15 | 100,000 | 2022-05-04 → 2026-05-08 |
|
||
| USDJPY_H1.csv | H1 | 100,000 | 2010-04-23 → 2026-05-08 |
|
||
| USDJPY_H4.csv | H4 | 25,855 | 2010-04-22 → 2026-05-08 |
|
||
| USDJPY_D1.csv | D1 | 5,020 | 2010-04-22 → 2026-05-08 |
|
||
|
||
CSV format: tab-delimited, columns `Time | Open | High | Low | Close | Volume | Spread`. Spread column is dropped on load. Timestamps are UTC.
|
||
|
||
**Data validation** (applied to both sources):
|
||
- Drop NaN OHLC rows; forward-fill up to 3 bars
|
||
- Drop future timestamps (>5 min ahead of UTC now)
|
||
- Fix OHLC inconsistencies (High < Low etc.)
|
||
- Remove outlier bars where `|return| > 10 × rolling_std`
|
||
|
||
---
|
||
|
||
## 3. Indicator Engine
|
||
|
||
`engine.py → TechnicalEngine.analyze(df, symbol, timeframe)`
|
||
|
||
All indicators are computed on a single OHLCV DataFrame. Each returns an `IndicatorSignal` with:
|
||
- `value` — raw numeric reading
|
||
- `signal` — `SignalStrength` enum: STRONG_BUY(+2) / BUY(+1) / NEUTRAL(0) / SELL(-1) / STRONG_SELL(-2)
|
||
- `confidence` — 0–100%
|
||
- `weight` — contribution to composite score (0 = excluded)
|
||
|
||
### Indicators computed
|
||
|
||
| Name | Key | Wilder smoothing? | Notes |
|
||
|------|-----|:-----------------:|-------|
|
||
| EMA Alignment | `ema_trend` | No | 4-EMA stack: 9/21/50/200; normalised to -1..+1 |
|
||
| RSI | `rsi` | Yes (`alpha=1/14`) | Overbought/oversold + momentum zones |
|
||
| MACD | `macd` | No | 12/26/9; crossover = STRONG signal |
|
||
| Bollinger Bands | `bollinger` | No | 20-period; position within band + squeeze detect |
|
||
| Stochastic | `stochastic` | No | 14/3; K+D both OB/OS required for signal |
|
||
| ADX | `adx` | Yes | DI+/DI- direction + ADX strength gate (≥25) |
|
||
| ATR | `atr` | Yes | Context only — sets SL/TP distance; weight=0 |
|
||
| Ichimoku | `ichimoku` | No | Price vs cloud (+2/-2) + TK cross (+1/-1) + cloud colour (+1/-1) |
|
||
| Divergence | `divergence` | — | RSI/MACD price vs indicator swing divergence; weight=1.5 |
|
||
| Candlestick | `candlestick` | — | **Disabled** (ablation: net -3.4%) |
|
||
| Pivot Points | `pivot` | — | **Disabled** (ablation: net -9.5%) |
|
||
| Volume | `volume` | — | Context filter; weight=0 |
|
||
|
||
**Disabled indicators** are controlled by `TechnicalConfig.disabled_indicators` (default: `["pivot", "candlestick"]`).
|
||
|
||
---
|
||
|
||
## 4. Signal Aggregator
|
||
|
||
`signal_aggregator.py → SignalAggregator.analyze()`
|
||
|
||
Orchestrates three timeframes (primary + 2 confirmation), blends their scores, applies gates, and emits a final graded signal.
|
||
|
||
### Flow
|
||
|
||
```
|
||
H1 (primary) ──┐
|
||
M15 (confirm) ──┼──▶ per-TF score ──▶ MTF blend ──▶ EMA200 gate ──▶ grade ──▶ output
|
||
H4 (confirm) ──┘
|
||
```
|
||
|
||
### MTF alignment (no lookahead)
|
||
|
||
Lower-frequency bars are aligned to the primary TF index by forward-filling at **close times**:
|
||
|
||
```
|
||
secondary close time = secondary bar open + secondary TF minutes
|
||
primary close time = primary bar open + primary TF minutes
|
||
|
||
For each primary bar T: use the most recent secondary close ≤ T_close
|
||
```
|
||
|
||
This ensures no future information leaks into the signal.
|
||
|
||
---
|
||
|
||
## 5. Composite Scoring Formula
|
||
|
||
Mirrors `engine._compute_composite` exactly:
|
||
|
||
```
|
||
composite_score = clip(
|
||
(Σ signal_i × weight_i × regime_mult_i × (confidence_i / 100))
|
||
────────────────────────────────────────────────────────────────── × 50,
|
||
(Σ weight_i × regime_mult_i)
|
||
-100, +100
|
||
)
|
||
|
||
avg_confidence = Σ(confidence_i × weight_i) / Σ weight_i
|
||
```
|
||
|
||
- `signal_i` ∈ {-2, -1, 0, +1, +2}
|
||
- `weight_i` — per-indicator base weight (see §6)
|
||
- `regime_mult_i` — boosted/dampened by market regime (see §7)
|
||
- Indicators with `weight == 0` are excluded entirely
|
||
|
||
---
|
||
|
||
## 6. Indicator Weights — Optimised
|
||
|
||
Optimised 2026-05-08 via 36-combo sweep (ablation + ×0.5/1.5/2.0/3.0 per indicator) on H4 data 2022–2026. Combined result: **+21.30% return / -8.52% max DD / Sharpe 0.47** vs baseline +7.12% / -9.05% DD / Sharpe 0.23.
|
||
|
||
| Indicator | Weight | Change from original | Reason |
|
||
|-----------|-------:|---------------------|--------|
|
||
| `ema_trend` | **3.6** | was 1.2 (×3) | Strongest directional signal on trending pair |
|
||
| `macd` | **3.3** | was 1.1 (×3) | Most impactful single indicator; ablation = worst result |
|
||
| `stochastic` | **1.6** | was 0.8 (×2) | Clean timing signal; 40% WR at ×2 |
|
||
| `adx` | **0.5** | was 1.0 (÷2) | Partially redundant with EMA stack |
|
||
| `ichimoku` | **0.55** | was 1.1 (÷2) | Lags too much at H4; hurts on ranging periods |
|
||
| `rsi` | **0.0** | was 0.3 (ablated) | Net drag across all test windows |
|
||
| `bollinger` | **0.0** | was 0.9 (ablated) | Counter-trend noise on trending pair |
|
||
| `divergence` | 1.5 | unchanged | Strong reversal signal; not in sweep scope |
|
||
| `atr` | 0.0 | unchanged | Context only; sets SL/TP |
|
||
|
||
> **Note:** RSI and Bollinger are still **computed and displayed** in live output for reference, but their weight=0 means they contribute nothing to the composite score.
|
||
|
||
### Regime weight multipliers
|
||
|
||
Applied on top of base weights:
|
||
|
||
| Indicator | Trending mult | Ranging mult |
|
||
|-----------|:-------------:|:------------:|
|
||
| ema_trend | ×1.5 | ×0.6 |
|
||
| adx | ×1.3 | ×0.5 |
|
||
| macd | ×1.2 | ×1.0 |
|
||
| ichimoku | ×1.3 | ×0.2 |
|
||
| rsi | ×0.7 | ×1.4 |
|
||
| bollinger | ×0.6 | ×1.4 |
|
||
| stochastic | ×1.0 | ×1.3 |
|
||
|
||
In trending regimes: EMA trend, MACD, and Ichimoku are boosted further.
|
||
In ranging regimes: RSI and Bollinger would be boosted — but since both are weight=0, ranging regime currently relies entirely on stochastic + reduced EMA/ADX.
|
||
|
||
---
|
||
|
||
## 7. Market Regime Classification
|
||
|
||
`engine._classify_regime()` — runs after all indicators are computed:
|
||
|
||
```
|
||
if ADX ≥ 25:
|
||
if EMA signal is BUY or STRONG_BUY → trending_up
|
||
if EMA signal is SELL or STRONG_SELL → trending_down
|
||
else → trending
|
||
else:
|
||
→ ranging
|
||
```
|
||
|
||
Regime feeds directly into the weight multipliers applied in `_compute_composite`.
|
||
|
||
---
|
||
|
||
## 8. EMA200 Distance Gate
|
||
|
||
**Strongest single filter discovered** — implemented in `signal_aggregator.py`.
|
||
|
||
```
|
||
ema_dist = |close - EMA200| / EMA200 × 100 (percent)
|
||
|
||
if ema_dist < 0.5% or ema_dist > 1.5%:
|
||
confidence *= 0.55 ← damped
|
||
else: ← sweet spot
|
||
no change
|
||
```
|
||
|
||
**Rationale:** Price within 0.5% of EMA200 is in a high-chop zone where the 200 EMA acts as contested support/resistance. Price more than 1.5% away has often already run far and is prone to reversion. The 0.5–1.5% band is where trending momentum is cleanest.
|
||
|
||
**Impact measured:** Lifted cumulative return +13.88% → +35.50% in the 2024-trend ablation window.
|
||
|
||
---
|
||
|
||
## 9. Multi-Timeframe Alignment
|
||
|
||
Primary TF: **H1** (default). Confirmation TFs: **M15** and **H4**.
|
||
|
||
### MTF blending (when 2 of 3 TFs agree)
|
||
|
||
```
|
||
mtf_agree = (bull_count ≥ 2) OR (bear_count ≥ 2)
|
||
|
||
if mtf_agree:
|
||
score = 0.6 × primary_score + 0.4 × mean(all_scores)
|
||
confidence = min(confidence + 15, 95)
|
||
else:
|
||
confidence = confidence × 0.7 ← penalised for disagreement
|
||
```
|
||
|
||
### MTF agreement bonus in grade assignment
|
||
|
||
`mtf_agree=True` is required for grades A and A+.
|
||
|
||
---
|
||
|
||
## 10. Signal Grade System
|
||
|
||
| Grade | Conditions |
|
||
|-------|-----------|
|
||
| **A+** | `|score| ≥ 50` AND `conf ≥ 75%` AND `mtf_agree` |
|
||
| **A** | `|score| ≥ 35` AND `conf ≥ 65%` AND `mtf_agree` |
|
||
| **B** | `|score| ≥ 25` AND `conf ≥ 55%` |
|
||
| **C** | `|score| ≥ 15` AND `conf ≥ 45%` |
|
||
| **D** | `|score| ≥ 10` |
|
||
| **F** | below all thresholds |
|
||
|
||
Default minimum grade for trade entry: **B** (`BacktestConfig.min_trade_grade`).
|
||
|
||
### Signal integer thresholds (for trade direction)
|
||
|
||
| Signal | Condition |
|
||
|--------|-----------|
|
||
| STRONG_BUY (+2) | score ≥ +40 AND conf ≥ 65% |
|
||
| BUY (+1) | score ≥ +15 AND conf ≥ 50% |
|
||
| STRONG_SELL (-2) | score ≤ -40 AND conf ≥ 65% |
|
||
| SELL (-1) | score ≤ -15 AND conf ≥ 50% |
|
||
| NEUTRAL | otherwise |
|
||
|
||
---
|
||
|
||
## 11. GPU Backtester
|
||
|
||
`backtest_gpu.py` — completely separate from `backtest.py`.
|
||
|
||
### Why O(N) instead of O(N²)
|
||
|
||
`backtest.py` slices the DataFrame and recomputes all indicators fresh each bar → N × O(N) = **O(N²)**.
|
||
`backtest_gpu.py` computes every indicator series end-to-end once, then reads bar T's value in O(1) → **O(N) total**.
|
||
|
||
On 100,000 H1 bars: ~4 seconds vs >10 minutes.
|
||
|
||
### Two-phase architecture
|
||
|
||
```
|
||
Phase 1 — prepare_arrays(dfs)
|
||
VectorIndicators.compute_all() ← pandas ewm/rolling, C-optimised
|
||
Align MTF signals to primary TF ← forward-fill at close times
|
||
Compute regime ADX series ← if regime filter enabled
|
||
Returns reusable bundle dict
|
||
|
||
Phase 2 — score_and_run(bundle, weights, disabled)
|
||
compute_bar_scores() ← CuPy GPU or NumPy CPU
|
||
compute_final_scores() ← MTF blend + EMA200 gate (vectorised)
|
||
compute_grades() ← vectorised grade assignment
|
||
run_state_machine() ← sequential position management (O(N))
|
||
```
|
||
|
||
`prepare_arrays()` is called once; `score_and_run()` can be called many times with different weights — this is what the `--optimize` sweep uses.
|
||
|
||
### Optimization sweep (`--optimize`)
|
||
|
||
36 combinations tested per run:
|
||
- **1** baseline (default weights)
|
||
- **7** ablation runs (each indicator disabled one at a time)
|
||
- **28** weight multiplier runs (each indicator at ×0.5, ×1.5, ×2.0, ×3.0)
|
||
|
||
Indicator arrays computed once → all 36 replays run in seconds.
|
||
|
||
### Trade execution model
|
||
|
||
```
|
||
Entry: next bar Open ± (spread/2 + slippage) [default: 0.5 pip slippage]
|
||
SL: entry ± ATR × atr_sl_multiplier [default: 1.5×]
|
||
TP: entry ± SL_dist × rr_ratio [default: 2.0× = 1:2 R:R]
|
||
Risk: 1% of current equity per trade
|
||
```
|
||
|
||
If SL and TP both touch within the same bar: SL assumed to hit first (`sl_first_on_tie=True`).
|
||
|
||
### CLI flags
|
||
|
||
```
|
||
python backtest_gpu.py --tf H4 --local # local CSV, H4
|
||
python backtest_gpu.py --tf H1 --local --start 2022-01-01
|
||
python backtest_gpu.py --tf H4 --local --optimize # 36-combo weight sweep
|
||
python backtest_gpu.py --regime-filter --regime-tf D1
|
||
python backtest_gpu.py --session-filter # UTC 07-16 only
|
||
python backtest_gpu.py --journal --journal-csv out.csv
|
||
```
|
||
|
||
---
|
||
|
||
## 12. Backtesting Results Summary
|
||
|
||
### H4 — local CSV 2022–2026 (optimised weights)
|
||
|
||
| Metric | Value |
|
||
|--------|-------|
|
||
| Trades | 160 |
|
||
| Win Rate | 38.1% |
|
||
| Profit Factor | 1.19 |
|
||
| Total Return | +21.30% |
|
||
| Max Drawdown | -8.52% |
|
||
| Sharpe | 0.47 |
|
||
| Sortino | 1.91 |
|
||
|
||
### D1 — Twelve Data 2007–2026 (optimised weights)
|
||
|
||
| Metric | Value |
|
||
|--------|-------|
|
||
| Trades | 62 |
|
||
| Win Rate | 40.3% |
|
||
| Profit Factor | 1.27 |
|
||
| Total Return | +10.51% |
|
||
| Max Drawdown | -6.84% |
|
||
| Sortino | 7.74 |
|
||
|
||
### Optimisation sweep delta (H4 2022–2026)
|
||
|
||
| | Baseline | Optimised |
|
||
|--|----------|-----------|
|
||
| Return | +7.12% | +21.30% |
|
||
| Max DD | -9.05% | -8.52% |
|
||
| Sharpe | 0.23 | 0.47 |
|
||
| Win Rate | 36.3% | 38.1% |
|
||
|
||
---
|
||
|
||
## 13. Filter Catalogue
|
||
|
||
### Regime filter (`--regime-filter`)
|
||
|
||
Gates trade entry on ADX of a **higher** timeframe than the primary. Default: H4 ADX gate when trading H1. Redundant if regime TF == primary TF.
|
||
|
||
```
|
||
regime TF ADX ≥ min_adx (default 25) → entry allowed
|
||
```
|
||
|
||
Sensible defaults: H1→H4, H4→D1, D1→D1.
|
||
|
||
### Session filter (`--session-filter`)
|
||
|
||
Only trade during UTC 07:00–16:00 (London + NY overlap).
|
||
2026 diagnostic: 07-15 UTC = 47% WR / +127 pips; off-hours = 0% WR / -571 pips.
|
||
|
||
### Auto-session toggle (`auto_session_filter`)
|
||
|
||
Reactive version of session filter. Activates automatically when mean P&L over last N closed trades drops below threshold:
|
||
|
||
```
|
||
if mean(pnl_pct[-10:]) < -0.3%:
|
||
session_filter = ON ← restrict to liquid hours
|
||
else:
|
||
session_filter = OFF ← all hours allowed
|
||
```
|
||
|
||
### EMA200 distance gate
|
||
|
||
Always active. See §8.
|
||
|
||
### Signal grade gate (`min_trade_grade`)
|
||
|
||
Default: **B**. Only trades with grade ≥ B are entered. Raise to A for fewer, higher-quality trades.
|
||
|
||
### Min absolute score (`min_abs_score`)
|
||
|
||
Default: **0** (no extra gate). Set to e.g. 15 to skip weak directional scores.
|
||
|
||
---
|
||
|
||
## 14. Configuration Reference
|
||
|
||
All knobs live in `config.py → PlatformConfig`.
|
||
|
||
```python
|
||
# Indicator parameters
|
||
TechnicalConfig.ema_fast = 9
|
||
TechnicalConfig.ema_medium = 21
|
||
TechnicalConfig.ema_slow = 50
|
||
TechnicalConfig.ema_trend = 200
|
||
TechnicalConfig.rsi_period = 14
|
||
TechnicalConfig.macd_fast / slow / signal = 12/26/9
|
||
TechnicalConfig.bb_period = 20
|
||
TechnicalConfig.atr_period = 14
|
||
TechnicalConfig.stoch_k / d = 14/3
|
||
TechnicalConfig.adx_period = 14
|
||
TechnicalConfig.adx_strong_trend = 25.0
|
||
TechnicalConfig.ichimoku_tenkan / kijun / senkou_b = 9/26/52
|
||
TechnicalConfig.disabled_indicators = ["pivot", "candlestick"]
|
||
|
||
# Signal scoring
|
||
SignalConfig.min_confidence = 55.0
|
||
SignalConfig.atr_sl_multiplier = 1.5
|
||
SignalConfig.default_rr_ratio = 2.0
|
||
SignalConfig.default_risk_pct = 1.0
|
||
|
||
# Backtest
|
||
BacktestConfig.initial_balance = 10_000
|
||
BacktestConfig.risk_per_trade_pct = 1.0
|
||
BacktestConfig.min_trade_grade = "B"
|
||
BacktestConfig.regime_filter = False
|
||
BacktestConfig.session_filter = False
|
||
BacktestConfig.auto_session_filter = False
|
||
BacktestConfig.cache_dir = ".cache"
|
||
```
|