2026-02-06 09:01:35 +07:00
# Backtest — Engine Simulasi Live-Sync
> **File:** `backtests/backtest_live_sync.py`
> **Class:** `LiveSyncBacktest`
> **Prinsip:** 100% identik dengan `main_live.py`
---
## Apa Itu Backtest?
Backtest adalah sistem **simulasi trading pada data historis** yang logikanya 100% disinkronkan dengan trading live. Tujuannya menguji strategi sebelum dipakai uang sungguhan dan memvalidasi perubahan kode.
**Analogi:** Backtest seperti **simulator penerbangan** — pilot (bot) berlatih di kondisi realistis tanpa risiko jatuh. Setiap instrumen, prosedur, dan respons sama persis dengan pesawat asli.
---
## Prinsip Sinkronisasi
```
ATURAN UTAMA: Backtest HARUS identik dengan live.
Setiap perubahan di main_live.py → HARUS di-mirror di backtest_live_sync.py
Yang disinkronkan:
├── ML Model: XGBoost dengan fitur yang sama
├── SMC Analyzer: Swing length & OB lookback sama
├── Regime Detection: HMM MarketRegimeDetector
├── Session Filter: Golden Time 19:00-23:00 WIB
├── Signal Logic: Semua filter entry
├── Position Sizing: Berdasarkan ML confidence tier
├── Trade Cooldown: 300 detik (5 menit)
└── Exit Logic: TP, ML reversal, max loss, time-based
```
---
## Komponen yang Dimuat
```python
# Sama persis dengan main_live.py
self . smc = SMCAnalyzer ( swing_length = config . smc . swing_length , ob_lookback = config . smc . ob_lookback )
self . features = FeatureEngineer ()
self . regime_detector = MarketRegimeDetector ( model_path = "models/hmm_regime.pkl" )
self . ml_model = TradingModel ( model_path = "models/xgboost_model.pkl" )
self . dynamic_confidence = create_dynamic_confidence ()
```
---
## Entry Logic (Sama dengan Live)
Semua filter entry di-replikasi:
2026-02-09 05:46:54 +07:00
```mermaid
flowchart TD
START["Untuk setiap bar dalam data historis"] --> F1{"1. COOLDOWN\n>= 20 bar dari trade terakhir?"}
F1 -->|YES| F2{"2. SESSION\nBukan Off Hours 04:00-06:00?"}
F1 -->|NO| SKIP["SKIP"]
F2 -->|YES| F3{"3. GOLDEN TIME\nHanya 19:00-23:00? (opsional)"}
F2 -->|NO| SKIP
F3 -->|YES| F4{"4. REGIME\nBukan CRISIS?"}
F3 -->|NO| SKIP
F4 -->|YES| F5{"5. SMC SIGNAL\nAda signal?"}
F4 -->|NO| SKIP
F5 -->|YES| F6{"6. DYNAMIC CONFIDENCE\nBukan AVOID?"}
F5 -->|NO| SKIP
F6 -->|YES| F7{"7. ML THRESHOLD\nConfidence >= 50-65%?"}
F6 -->|NO| SKIP
F7 -->|YES| F8{"8. ML AGREEMENT\nTidak strongly disagree?"}
F7 -->|NO| SKIP
F8 -->|YES| F9{"9. SIGNAL CONFIRMATION\n2x berturut?"}
F8 -->|NO| SKIP
F9 -->|YES| F10{"10. PULLBACK FILTER\nMomentum tidak berlawanan?"}
F9 -->|NO| SKIP
F10 -->|YES| EXEC["EXECUTE SIMULATED TRADE"]
F10 -->|NO| SKIP
2026-02-06 09:01:35 +07:00
```
---
## Session Mapping
```python
# Sama dengan session_filter.py
if 6 <= hour < 15 : # Sydney-Tokyo → lot 0.5x
if 15 <= hour < 16 : # Tokyo-London Overlap → lot 0.75x
if 16 <= hour < 19 : # London Early → lot 0.8x
if 19 <= hour < 24 : # London-NY (Golden) → lot 1.0x ← TERBAIK
if 0 <= hour < 4 : # NY Session → lot 0.9x
if 4 <= hour < 6 : # Off Hours → SKIP
```
---
## Exit Logic (5 Kondisi)
Untuk setiap bar setelah entry (max 100 bar):
### EXIT 1: Take Profit
```
IF harga hit TP level:
BUY: high >= take_profit
SELL: low <= take_profit
-> EXIT dengan profit penuh
```
### EXIT 2: Maximum Loss
```
IF current_profit < -$50 (max_loss_per_trade):
-> EXIT, potong kerugian
```
### EXIT 3: Time-Based (Synced dengan Live v3)
```
IF 16+ bar (4 jam) DAN profit < $5:
a) profit >= $0 → EXIT (breakeven setelah 4 jam)
b) profit > -$15 → EXIT (loss kecil, daripada stuck)
IF 24+ bar (6 jam):
-> FORCE EXIT (apapun profitnya)
```
**Visualisasi:**
```
Bar: 0 5 10 15 16 20 24
|-----|-----|-----|-----|-----|-----|
entry | |
| |
4h check: 6h FORCE EXIT
profit<$5?
Ya -> exit
```
### EXIT 4: ML Reversal
```
Setiap 5 bar, cek prediksi ML:
IF direction BUY DAN ML bilang SELL dengan confidence > 65%:
-> EXIT (ML mendeteksi reversal)
IF direction SELL DAN ML bilang BUY dengan confidence > 65%:
-> EXIT (ML mendeteksi reversal)
```
### EXIT 5: Trend Reversal (Momentum)
```
Setelah 10+ bar, cek momentum 5 bar terakhir:
IF BUY DAN momentum < -$5 DAN current_profit < -$10:
-> EXIT (tren berbalik + sudah rugi)
IF SELL DAN momentum > +$5 DAN current_profit < -$10:
-> EXIT (tren berbalik + sudah rugi)
```
---
## Lot Sizing
```python
# Berdasarkan ML confidence tier (sama dengan live)
if ml_confidence >= 0.65 :
lot_size = 0.02 # High confidence → lot lebih besar
elif ml_confidence >= 0.55 :
lot_size = 0.01 # Medium confidence → lot standar
else :
lot_size = 0.01 # Low confidence → lot minimum
# Apply session multiplier
lot_size = max ( 0.01 , lot_size * session_lot_multiplier )
```
---
## Pullback Filter
```
Sama persis dengan main_live.py:
Untuk signal SELL, block jika:
- Harga naik > $2 dalam 3 candle terakhir
- MACD histogram rising + harga naik
- Harga di atas EMA9 dan masih naik
Untuk signal BUY, block jika:
- Harga turun > $2 dalam 3 candle terakhir
- MACD histogram falling + harga turun
- Harga di bawah EMA9 dan masih turun
Exception (tetap boleh entry):
- Konsolidasi (pergerakan < $1.50)
- Momentum searah signal
```
---
## Metrik Performa
| Metrik | Rumus | Keterangan |
|--------|-------|------------|
| **Win Rate** | Wins / Total × 100% | Persentase trade profit |
| **Profit Factor** | Gross Profit / Gross Loss | > 1.0 = profitable |
| **Expectancy** | (WR × Avg Win) - (LR × Avg Loss) | Rata-rata per trade |
| **Max Drawdown** | (Peak - Trough) / Peak × 100% | Penurunan terbesar |
| **Sharpe Ratio** | (Avg Return / Std Dev) × √252 | Risk-adjusted return |
| **Net P/L** | Total Profit - Total Loss | Keuntungan bersih |
---
## Threshold Tuning
Mode `--tune` menguji beberapa ML threshold secara otomatis:
```python
ml_thresholds = [ 0.50 , 0.52 , 0.55 , 0.58 , 0.60 , 0.65 ]
# Untuk setiap threshold:
# 1. Jalankan full backtest
# 2. Catat: trades, win rate, net P/L, profit factor, drawdown
# 3. Ranking berdasarkan net P/L
# Output:
# ML Thresh Trades Win Rate Net P/L PF DD
# --------------------------------------------------------
# 55% 145 64.8% $1,250.00 1.85 3.2%
# 52% 178 62.1% $1,100.00 1.72 4.1%
# 60% 112 67.0% $ 980.00 1.95 2.8%
# ...
```
---
## Cara Penggunaan
```bash
# Backtest standar dengan threshold default (55%)
python backtests/backtest_live_sync.py
# Backtest dengan threshold custom
python backtests/backtest_live_sync.py --threshold 0.60
# Hanya golden time
python backtests/backtest_live_sync.py --golden-only
# Threshold tuning (cari optimal)
python backtests/backtest_live_sync.py --tune
# Simpan hasil ke CSV
python backtests/backtest_live_sync.py --save
```
---
## Output Backtest
### Laporan Performa
```
==================================================================
BACKTEST RESULTS
==================================================================
Configuration:
ML Threshold: 55%
Signal Confirmation: 2 consecutive
Pullback Filter: Enabled
Golden Time Only: False
Performance:
Total Trades: 145
Wins: 94
Losses: 51
Win Rate: 64.8%
Profit/Loss:
Total Profit: $2,850.00
Total Loss: $1,600.00
Net P/L: $1,250.00
Profit Factor: 1.78
Risk Metrics:
Max Drawdown: 3.2% ($160.00)
Avg Win: $30.32
Avg Loss: $31.37
Expectancy: $8.62
Sharpe Ratio: 1.45
```
### Breakdown Exit Reason
```
Exit Reasons:
take_profit: 72 (49.7%)
timeout: 35 (24.1%)
ml_reversal: 18 (12.4%)
max_loss: 12 (8.3%)
trend_reversal: 8 (5.5%)
```
### Breakdown Session
```
Session Performance:
London-NY Overlap (Golden): 65 trades, 69.2% WR, $820.00
NY Session: 32 trades, 62.5% WR, $280.00
London Early: 28 trades, 60.7% WR, $120.00
Sydney-Tokyo: 20 trades, 55.0% WR, $30.00
```
---
## File Output
```
backtests/results/
├── backtest_20250206_143000.csv # Detail semua trade
│ ├── ticket, entry_time, exit_time
│ ├── direction, entry_price, exit_price
│ ├── stop_loss, take_profit, lot_size
│ ├── profit_usd, profit_pips, result
│ ├── exit_reason, ml_confidence, smc_confidence
│ └── regime, session, signal_reason
│
└── backtest_20250206_143000_summary.csv # Ringkasan metrik
├── total_trades, wins, losses, win_rate
├── total_profit, total_loss, net_pnl
├── profit_factor, avg_win, avg_loss
└── max_drawdown, expectancy, sharpe_ratio
```
---
## Data Flow
2026-02-09 05:46:54 +07:00
```mermaid
flowchart TD
A["MT5 Connected"] --> B["Fetch 50.000 bar M15 XAUUSD"]
B --> C["FeatureEngineer.calculate_all() → 40+ fitur\nSMCAnalyzer.calculate_all() → Struktur pasar\nRegimeDetector.predict() → Regime label"]
C --> D["Filter: Jan 2025 - Now"]
D --> E["Loop setiap bar"]
E --> E1["Entry check (14 filter)"]
E --> E2["Simulate exit (5 kondisi)"]
E --> E3["Record trade result"]
E --> E4["Update statistics"]
E1 --> F["Print laporan + Save CSV"]
E2 --> F
E3 --> F
E4 --> F
2026-02-06 09:01:35 +07:00
```