Files
GifariKemalandClaude Opus 4.6 e8355b3f62 feat: add 5 dashboard features — dark mode, trade history, backtests, model insights, alerts
- Dark mode: class-based theme toggle with localStorage persistence and flash prevention
- Trade History (/trades): paginated table, stats cards, equity curve chart with DB API endpoints
- Backtest Viewer (/backtests): log parser for 35 backtest results, sidebar + detail + comparison tabs
- Model Insights: dashboard card + dialog showing feature importance, regime distribution, training history
- Alert/Signal Log (/alerts): signal stats, filterable table with execution tracking
- API: 8 new endpoints with psycopg2 DB connection pool
- Dark mode sweep across books page, about dialog, and all dashboard components
- Architecture docs rewritten with Mermaid diagrams (23 docs)
- README and FEATURES.md rewritten bilingual (Indonesian + English)
- main_live.py: write model_metrics.json on startup and retrain

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 05:46:54 +07:00

356 lines
9.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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:
```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
```
---
## 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
```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
```