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>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
b2dc2dacd7
commit
e8355b3f62
@@ -1,4 +1,4 @@
|
||||
# MT5 Connector — Jembatan ke MetaTrader 5
|
||||
# *MT5 Connector* — Jembatan ke *MetaTrader* 5
|
||||
|
||||
> **File:** `src/mt5_connector.py`
|
||||
> **Class:** `MT5Connector`, `MT5SimulationConnector`
|
||||
@@ -6,11 +6,11 @@
|
||||
|
||||
---
|
||||
|
||||
## Apa Itu MT5 Connector?
|
||||
## Apa Itu *MT5 Connector*?
|
||||
|
||||
MT5 Connector adalah **jembatan komunikasi** antara bot AI dan terminal MetaTrader 5. Semua interaksi dengan broker — ambil data harga, kirim order, cek posisi — dilakukan melalui modul ini.
|
||||
*MT5 Connector* adalah **jembatan komunikasi** antara bot AI dan terminal *MetaTrader* 5. Semua interaksi dengan broker — ambil data harga, kirim order, cek posisi — dilakukan melalui modul ini.
|
||||
|
||||
**Analogi:** MT5 Connector seperti **penerjemah di bandara** — menerjemahkan perintah bot (Python) ke bahasa yang dipahami broker (MT5 API), dan sebaliknya.
|
||||
**Analogi:** *MT5 Connector* seperti **penerjemah di bandara** — menerjemahkan perintah bot (Python) ke bahasa yang dipahami broker (MT5 API), dan sebaliknya.
|
||||
|
||||
---
|
||||
|
||||
@@ -21,41 +21,76 @@ MT5 Connector adalah **jembatan komunikasi** antara bot AI dan terminal MetaTrad
|
||||
| `connect()` | Koneksi ke MT5 terminal | `bool` |
|
||||
| `disconnect()` | Putus koneksi | - |
|
||||
| `reconnect()` | Reconnect otomatis | `bool` |
|
||||
| `ensure_connected()` | Cek & auto-reconnect | `bool` |
|
||||
| `ensure_connected()` | Cek & *auto-reconnect* | `bool` |
|
||||
| `get_market_data()` | Ambil data OHLCV | `pl.DataFrame` |
|
||||
| `get_tick()` | Ambil harga real-time | `TickData` |
|
||||
| `send_order()` | Kirim order BUY/SELL | `OrderResult` |
|
||||
| `close_position()` | Tutup posisi | `OrderResult` |
|
||||
| `get_open_positions()` | Cek posisi terbuka | `pl.DataFrame` |
|
||||
| `get_symbol_info()` | Info simbol (spread, dll) | `Dict` |
|
||||
| `get_multi_timeframe_data()` | Ambil data multi-timeframe | `Dict[str, pl.DataFrame]` |
|
||||
|
||||
---
|
||||
|
||||
## Koneksi & Auto-Reconnect
|
||||
## Koneksi & *Auto-Reconnect*
|
||||
|
||||
```
|
||||
connect(max_retries=3)
|
||||
|
|
||||
v
|
||||
Shutdown koneksi lama (jika ada)
|
||||
|
|
||||
v
|
||||
mt5.initialize(login, password, server)
|
||||
|
|
||||
v
|
||||
Tunggu 2 detik (stabilisasi terminal)
|
||||
|
|
||||
v
|
||||
Verifikasi: terminal_info() != None?
|
||||
|
|
||||
├── Ya → Cek terminal.connected?
|
||||
│ ├── Ya → ✅ Connected!
|
||||
│ └── Tidak → Tunggu 3 detik → Retry
|
||||
│
|
||||
└── Tidak → Exponential backoff (2s, 4s, 8s) → Retry
|
||||
### Connection Flow
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A([Start connect]) --> B[Shutdown koneksi lama]
|
||||
B --> C[mt5.initialize\nlogin, password, server]
|
||||
C --> D{Initialize\nberhasil?}
|
||||
D -- Ya --> E[Tunggu 2 detik\nstabilisasi terminal]
|
||||
D -- Tidak --> K{Attempt\n< max_retries?}
|
||||
E --> F{terminal_info\n!= None?}
|
||||
F -- Ya --> G{terminal\n.connected?}
|
||||
F -- Tidak --> J[Shutdown & retry]
|
||||
G -- Ya --> H[Set _connected = True\nAmbil account_info\nSelect symbol XAUUSD]
|
||||
G -- Tidak --> I[Tunggu 3 detik\nCek ulang terminal]
|
||||
I --> G2{Masih belum\nconnected?}
|
||||
G2 -- Ya --> J
|
||||
G2 -- Tidak --> H
|
||||
H --> Z([Connected!])
|
||||
J --> K
|
||||
K -- Ya --> L[Exponential backoff\n2s, 4s, 8s]
|
||||
L --> B
|
||||
K -- Tidak --> M([ConnectionError\nRaise exception])
|
||||
|
||||
style A fill:#4CAF50,color:#fff
|
||||
style Z fill:#4CAF50,color:#fff
|
||||
style M fill:#f44336,color:#fff
|
||||
style H fill:#2196F3,color:#fff
|
||||
```
|
||||
|
||||
### Auto-Reconnect
|
||||
### Mekanisme *Auto-Reconnect*
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A([ensure_connected\ndipanggil]) --> B{Flag\n_connected?}
|
||||
B -- False --> C[reconnect]
|
||||
B -- True --> D[Cek mt5.account_info]
|
||||
D --> E{Info\nvalid?}
|
||||
E -- Ya --> F([Tetap connected\nReset attempt counter])
|
||||
E -- Tidak --> G[Set _connected = False\nIncrement attempt]
|
||||
G --> H{Attempt >\nmax 5?}
|
||||
H -- Ya --> I{Sudah lewat\n60 detik cooldown?}
|
||||
I -- Ya --> J[Reset attempt = 0]
|
||||
I -- Tidak --> K([Return False\nMasih dalam cooldown])
|
||||
J --> C
|
||||
H -- Tidak --> C
|
||||
C --> L{reconnect\nberhasil?}
|
||||
L -- Ya --> M([Reconnected!\nReset attempt counter])
|
||||
L -- Tidak --> N([Return False])
|
||||
|
||||
style A fill:#FF9800,color:#fff
|
||||
style F fill:#4CAF50,color:#fff
|
||||
style M fill:#4CAF50,color:#fff
|
||||
style K fill:#f44336,color:#fff
|
||||
style N fill:#f44336,color:#fff
|
||||
```
|
||||
|
||||
**Detail *Auto-Reconnect*:**
|
||||
|
||||
```python
|
||||
ensure_connected():
|
||||
@@ -69,12 +104,14 @@ ensure_connected():
|
||||
"""
|
||||
```
|
||||
|
||||
Mekanisme *exponential backoff* memastikan bot tidak membombardir server broker dengan request berulang. Setiap kali koneksi gagal, waktu tunggu berlipat ganda (2s, 4s, 8s). Setelah 5 kali gagal berturut-turut, bot masuk fase *cooldown* selama 60 detik sebelum mencoba lagi.
|
||||
|
||||
---
|
||||
|
||||
## Data Fetching (Polars Native)
|
||||
|
||||
```python
|
||||
get_market_data(symbol="XAUUSD", timeframe="M15", count=200)
|
||||
get_market_data(symbol="XAUUSD", timeframe="M15", count=1000, max_retries=3)
|
||||
```
|
||||
|
||||
**Proses:**
|
||||
@@ -83,6 +120,12 @@ get_market_data(symbol="XAUUSD", timeframe="M15", count=200)
|
||||
MT5 Terminal
|
||||
|
|
||||
v
|
||||
ensure_connected() → auto-reconnect jika putus
|
||||
|
|
||||
v
|
||||
mt5.symbol_select() → pastikan simbol aktif di Market Watch
|
||||
|
|
||||
v
|
||||
mt5.copy_rates_from_pos() → numpy structured array
|
||||
|
|
||||
v
|
||||
@@ -99,6 +142,8 @@ Cast types:
|
||||
Return pl.DataFrame
|
||||
```
|
||||
|
||||
> **Catatan penting:** Data dikonversi langsung dari NumPy structured array ke Polars DataFrame. Tidak ada konversi perantara via Pandas. Ini adalah optimisasi kritis untuk performa — menjaga target **< 50ms per loop**.
|
||||
|
||||
**Kolom output:**
|
||||
|
||||
| Kolom | Tipe | Keterangan |
|
||||
@@ -130,19 +175,40 @@ send_order(
|
||||
)
|
||||
```
|
||||
|
||||
**Retry Logic:**
|
||||
### Order Execution Flow dengan *Retry* Logic
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A([send_order\ndipanggil]) --> B[Ambil tick data\nmt5.symbol_info_tick]
|
||||
B --> C{Tick\nvalid?}
|
||||
C -- Tidak --> D([Return: Failed\nNo tick data])
|
||||
C -- Ya --> E[Tentukan harga\nBUY → ask / SELL → bid]
|
||||
E --> F[Build request:\naction, symbol, volume,\ntype, price, SL, TP,\ndeviation, magic]
|
||||
F --> G[mt5.order_send]
|
||||
G --> H{Result\n== None?}
|
||||
H -- Ya --> I[Log error]
|
||||
I --> P
|
||||
H -- Tidak --> J{RETCODE?}
|
||||
J -- 10009 DONE --> K([Order berhasil!\nReturn OrderResult])
|
||||
J -- "10013-10016\nINVALID" --> L([Return: Failed\nNon-retryable error])
|
||||
J -- 10027\nTRADE_DISABLED --> M([Raise RuntimeError\nAutoTrading off])
|
||||
J -- "10004 REQUOTE\n10006 REJECT\nlainnya" --> N[Log warning\nTunggu 0.5 detik]
|
||||
N --> P{Attempt\n< max_retries?}
|
||||
P -- Ya --> Q[Refresh harga\nUlangi order]
|
||||
Q --> G
|
||||
P -- Tidak --> R([Return: Failed\nMax retries exceeded])
|
||||
|
||||
style A fill:#FF9800,color:#fff
|
||||
style K fill:#4CAF50,color:#fff
|
||||
style D fill:#f44336,color:#fff
|
||||
style L fill:#f44336,color:#fff
|
||||
style M fill:#f44336,color:#fff
|
||||
style R fill:#f44336,color:#fff
|
||||
```
|
||||
Kirim order
|
||||
|
|
||||
├── RETCODE 10009 (DONE) → ✅ Success
|
||||
|
|
||||
├── RETCODE 10013-10016 (INVALID) → ❌ Non-retryable
|
||||
|
|
||||
├── RETCODE 10027 (TRADE DISABLED) → ❌ Raise error
|
||||
|
|
||||
└── RETCODE lain (requote/reject) → 🔄 Retry (max 3x)
|
||||
```
|
||||
|
||||
Parameter `deviation` mengontrol toleransi *slippage* maksimum dalam poin. Jika harga bergeser melebihi batas ini saat eksekusi, broker akan menolak order (REQUOTE) dan bot akan melakukan *retry* otomatis dengan harga terbaru.
|
||||
|
||||
**Close Position** juga menggunakan logika *retry* yang sama — setiap attempt mengambil ulang harga terbaru untuk memastikan akurasi.
|
||||
|
||||
---
|
||||
|
||||
@@ -154,9 +220,10 @@ Kirim order
|
||||
| `M5` | TIMEFRAME_M5 | 5 menit |
|
||||
| `M15` | TIMEFRAME_M15 | **Utama** (execution) |
|
||||
| `M30` | TIMEFRAME_M30 | 30 menit |
|
||||
| `H1` | TIMEFRAME_H1 | 1 jam |
|
||||
| `H1` | TIMEFRAME_H1 | 1 jam (EMA20 filter) |
|
||||
| `H4` | TIMEFRAME_H4 | Trend analysis |
|
||||
| `D1` | TIMEFRAME_D1 | 1 hari |
|
||||
| `W1` | TIMEFRAME_W1 | 1 minggu |
|
||||
|
||||
---
|
||||
|
||||
@@ -165,19 +232,24 @@ Kirim order
|
||||
| Code | Nama | Aksi |
|
||||
|------|------|------|
|
||||
| 10009 | DONE | Order berhasil |
|
||||
| 10004 | REQUOTE | Retry |
|
||||
| 10006 | REJECT | Retry |
|
||||
| 10004 | REQUOTE | *Retry* — harga berubah |
|
||||
| 10006 | REJECT | *Retry* — ditolak server |
|
||||
| 10013 | INVALID | Stop, order salah |
|
||||
| 10014 | INVALID_VOLUME | Stop, lot salah |
|
||||
| 10015 | INVALID_PRICE | Stop, harga salah |
|
||||
| 10016 | INVALID_STOPS | Stop, SL/TP salah |
|
||||
| 10027 | TRADE_DISABLED | AutoTrading off |
|
||||
| -10001 | COMMON_ERROR | Reconnect |
|
||||
| -10002 | INVALID_PARAMS | Reconnect |
|
||||
| -10003 | NO_CONNECTION | Reconnect |
|
||||
| -10004 | NO_IPC | Reconnect |
|
||||
| -1 | TERMINAL_CALL_FAILED | Reconnect |
|
||||
|
||||
Error code -10003, -10004, -10001, -10002, dan -1 termasuk dalam `CONNECTION_ERRORS` dan secara otomatis memicu mekanisme *auto-reconnect*.
|
||||
|
||||
---
|
||||
|
||||
## Simulation Mode
|
||||
## *Simulation Mode*
|
||||
|
||||
```python
|
||||
class MT5SimulationConnector(MT5Connector):
|
||||
@@ -187,9 +259,14 @@ class MT5SimulationConnector(MT5Connector):
|
||||
- connect() selalu berhasil
|
||||
- get_market_data() generate data sintetis (random walk)
|
||||
- Base price XAUUSD: $2000
|
||||
- Berguna untuk development & unit testing
|
||||
"""
|
||||
```
|
||||
|
||||
*Simulation mode* memungkinkan pengembangan dan testing tanpa perlu koneksi ke terminal *MetaTrader* yang sebenarnya. Connector ini menghasilkan data OHLCV sintetis menggunakan random walk dari harga dasar $2000.
|
||||
|
||||
> **Catatan:** *Simulation mode* secara otomatis aktif jika library MetaTrader5 tidak terinstal di environment.
|
||||
|
||||
---
|
||||
|
||||
## Konfigurasi Koneksi
|
||||
@@ -203,3 +280,22 @@ MT5Connector(
|
||||
timeout=60000, # 60 detik timeout
|
||||
)
|
||||
```
|
||||
|
||||
### Context Manager Support
|
||||
|
||||
*MT5 Connector* mendukung penggunaan sebagai context manager:
|
||||
|
||||
```python
|
||||
with MT5Connector(login, password, server) as mt5_conn:
|
||||
data = mt5_conn.get_market_data("XAUUSD", "M15")
|
||||
# Otomatis disconnect saat keluar blok
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Catatan Teknis
|
||||
|
||||
- **Polars, bukan Pandas:** Semua konversi data dari MT5 menggunakan Polars secara langsung. Tidak ada *connection pooling* atau konversi via Pandas.
|
||||
- **Thread Safety:** *MT5 Connector* berjalan di satu thread utama. Library MT5 Python API tidak thread-safe, jadi semua operasi dilakukan secara sekuensial.
|
||||
- **Password Security:** Password disimpan dengan prefix `_` (`self._password`) sebagai konvensi private attribute.
|
||||
- **Symbol Pre-selection:** Setelah koneksi berhasil, simbol XAUUSD otomatis di-select di Market Watch untuk memastikan data siap diambil.
|
||||
|
||||
Reference in New Issue
Block a user