292 lines
7.2 KiB
Markdown
292 lines
7.2 KiB
Markdown
# Analisis Kelemahan Sistem Trading Bot
|
|||
|
|
|
||
|
|
## Tanggal Analisis: 6 Februari 2026
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 1. STOP LOSS - KELEMAHAN KRITIS
|
||
|
|
|
||
|
|
### 1.1 Tidak Ada Broker Stop Loss
|
||
|
|
**File:** `main_live.py` line 876
|
||
|
|
```python
|
||
|
|
result = self.mt5.send_order(
|
||
|
|
sl=0, # MASALAH: Tidak ada SL di broker!
|
||
|
|
tp=signal.take_profit,
|
||
|
|
)
|
||
|
|
```
|
||
|
|
|
||
|
|
**Risiko:**
|
||
|
|
- Gap weekend = loss unlimited
|
||
|
|
- Flash crash = posisi tidak terproteksi
|
||
|
|
- Disconnect internet = loss tidak terkontrol
|
||
|
|
|
||
|
|
**Solusi:**
|
||
|
|
```python
|
||
|
|
# Hitung emergency SL berdasarkan ATR
|
||
|
|
atr = df["atr"].tail(1).item()
|
||
|
|
emergency_sl = entry_price - (3.0 * atr) if direction == "BUY" else entry_price + (3.0 * atr)
|
||
|
|
|
||
|
|
result = self.mt5.send_order(
|
||
|
|
sl=emergency_sl, # BROKER-LEVEL PROTECTION
|
||
|
|
tp=signal.take_profit,
|
||
|
|
)
|
||
|
|
```
|
||
|
|
|
||
|
|
### 1.2 Smart Hold Terlalu Agresif
|
||
|
|
**File:** `smart_risk_manager.py` line 460-486
|
||
|
|
```python
|
||
|
|
# Tahan loss $15 selama 3 jam menunggu golden time
|
||
|
|
if loss_percent_of_max < 30 and hours_to_golden <= 3 and momentum > -50:
|
||
|
|
return False, None, f"SMART HOLD..."
|
||
|
|
```
|
||
|
|
|
||
|
|
**Risiko:**
|
||
|
|
- Loss $15 bisa jadi $30 dalam 3 jam
|
||
|
|
- Momentum -50 masih terlalu lemah sebagai threshold
|
||
|
|
|
||
|
|
**Solusi:**
|
||
|
|
- Kurangi max hold time ke 1 jam
|
||
|
|
- Naikkan momentum threshold ke -30
|
||
|
|
- Exit jika loss > 40% max (bukan 50%)
|
||
|
|
|
||
|
|
### 1.3 SL Berbasis Swing Terlalu Dekat
|
||
|
|
**File:** `smc_polars.py` line 639-640
|
||
|
|
```python
|
||
|
|
sl = last_swing_low if last_swing_low and last_swing_low < entry else entry * 0.995
|
||
|
|
# Entry 2000, fallback SL = 1990 (hanya 10 pips!)
|
||
|
|
```
|
||
|
|
|
||
|
|
**Risiko:**
|
||
|
|
- Volatilitas normal XAUUSD = 10-20 pips
|
||
|
|
- SL 10 pips = kena stop oleh noise
|
||
|
|
|
||
|
|
**Solusi:**
|
||
|
|
```python
|
||
|
|
# Minimum SL = 1.5 * ATR
|
||
|
|
atr = df["atr"].tail(1).item()
|
||
|
|
min_sl_distance = 1.5 * atr
|
||
|
|
|
||
|
|
if direction == "BUY":
|
||
|
|
swing_sl = last_swing_low
|
||
|
|
atr_sl = entry - min_sl_distance
|
||
|
|
sl = min(swing_sl, atr_sl) if swing_sl else atr_sl
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 2. TAKE PROFIT - KELEMAHAN
|
||
|
|
|
||
|
|
### 2.1 TP Fixed 2:1 RR
|
||
|
|
**File:** `smc_polars.py` line 643-644
|
||
|
|
```python
|
||
|
|
risk = entry - sl
|
||
|
|
tp = entry + (risk * 2)
|
||
|
|
```
|
||
|
|
|
||
|
|
**Masalah:**
|
||
|
|
- Tidak cek apakah TP di zona resistance
|
||
|
|
- TP bisa 100+ pips, tidak realistis
|
||
|
|
|
||
|
|
**Solusi:**
|
||
|
|
```python
|
||
|
|
# TP berdasarkan ATR dan struktur market
|
||
|
|
atr = df["atr"].tail(1).item()
|
||
|
|
max_tp_distance = 4.0 * atr # Maximum 4 ATR
|
||
|
|
|
||
|
|
# Cek resistance terdekat
|
||
|
|
nearest_resistance = find_nearest_resistance(df, entry)
|
||
|
|
|
||
|
|
# TP = minimum dari RR target atau resistance
|
||
|
|
rr_tp = entry + (risk * 2)
|
||
|
|
tp = min(rr_tp, entry + max_tp_distance)
|
||
|
|
if nearest_resistance and nearest_resistance < tp:
|
||
|
|
tp = nearest_resistance * 0.995 # Sedikit di bawah resistance
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2.2 Tidak Ada Partial Take Profit
|
||
|
|
**Solusi:**
|
||
|
|
```python
|
||
|
|
# Partial TP levels
|
||
|
|
tp_25 = entry + (risk * 0.5) # 25% posisi di 0.5 RR
|
||
|
|
tp_50 = entry + (risk * 1.0) # 25% posisi di 1.0 RR
|
||
|
|
tp_75 = entry + (risk * 1.5) # 25% posisi di 1.5 RR
|
||
|
|
tp_100 = entry + (risk * 2.0) # 25% posisi di 2.0 RR
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3. ENTRY TRADE - KELEMAHAN
|
||
|
|
|
||
|
|
### 3.1 ML Threshold 50% = Coin Flip
|
||
|
|
**File:** `main_live.py` line 723
|
||
|
|
```python
|
||
|
|
ml_min_threshold = 0.50
|
||
|
|
```
|
||
|
|
|
||
|
|
**Masalah:**
|
||
|
|
- 50% confidence = tidak lebih baik dari random
|
||
|
|
- Seharusnya dinamis per session
|
||
|
|
|
||
|
|
**Solusi:**
|
||
|
|
```python
|
||
|
|
# Dynamic threshold berdasarkan session
|
||
|
|
if session == "Sydney":
|
||
|
|
ml_min_threshold = 0.60 # Low liquidity = butuh confidence tinggi
|
||
|
|
elif session == "London-NY Overlap":
|
||
|
|
ml_min_threshold = 0.50 # High quality = threshold lebih rendah OK
|
||
|
|
else:
|
||
|
|
ml_min_threshold = 0.55 # Default
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3.2 Signal Key Reset Terus
|
||
|
|
**File:** `main_live.py` line 733
|
||
|
|
```python
|
||
|
|
signal_key = f"{smc_signal.signal_type}_{int(smc_signal.entry_price):.0f}"
|
||
|
|
# Entry price berubah setiap candle = signal key selalu baru!
|
||
|
|
```
|
||
|
|
|
||
|
|
**Solusi:**
|
||
|
|
```python
|
||
|
|
# Gunakan zone-based key, bukan exact price
|
||
|
|
zone_size = 5 # $5 zone
|
||
|
|
zone = int(smc_signal.entry_price / zone_size) * zone_size
|
||
|
|
signal_key = f"{smc_signal.signal_type}_{zone}"
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3.3 Pullback Filter Fixed $2
|
||
|
|
**File:** `main_live.py` line 673
|
||
|
|
```python
|
||
|
|
if momentum_direction == "UP" and short_momentum > 2: # Fixed $2
|
||
|
|
```
|
||
|
|
|
||
|
|
**Solusi:**
|
||
|
|
```python
|
||
|
|
# ATR-based threshold
|
||
|
|
atr = df["atr"].tail(1).item()
|
||
|
|
pullback_threshold = 0.5 * atr # 50% of ATR
|
||
|
|
|
||
|
|
if momentum_direction == "UP" and short_momentum > pullback_threshold:
|
||
|
|
return False, "SELL blocked: Price bouncing"
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 4. EXIT TRADE - KELEMAHAN
|
||
|
|
|
||
|
|
### 4.1 ML Reversal Butuh 75% Confidence
|
||
|
|
**File:** `smart_risk_manager.py` line 441
|
||
|
|
```python
|
||
|
|
if ml_confidence >= 0.75 and ml_is_reversal:
|
||
|
|
return True, ExitReason.TREND_REVERSAL
|
||
|
|
```
|
||
|
|
|
||
|
|
**Masalah:**
|
||
|
|
- Terlalu tinggi, sering sudah telat
|
||
|
|
- Harga sudah bergerak jauh saat ML 75%
|
||
|
|
|
||
|
|
**Solusi:**
|
||
|
|
```python
|
||
|
|
# Lower threshold dengan tambahan konfirmasi
|
||
|
|
if ml_confidence >= 0.65 and ml_is_reversal:
|
||
|
|
if momentum_score < -30: # Momentum juga negatif
|
||
|
|
return True, ExitReason.TREND_REVERSAL
|
||
|
|
```
|
||
|
|
|
||
|
|
### 4.2 Tidak Ada Time-Based Exit
|
||
|
|
**Solusi:**
|
||
|
|
```python
|
||
|
|
# Exit jika trade stuck terlalu lama
|
||
|
|
trade_duration = (datetime.now() - entry_time).total_seconds() / 3600 # hours
|
||
|
|
|
||
|
|
if trade_duration > 4 and abs(current_profit) < 5: # 4 jam tanpa progress
|
||
|
|
return True, ExitReason.TIMEOUT, "Trade stuck > 4 hours"
|
||
|
|
|
||
|
|
if trade_duration > 6: # Maximum 6 jam
|
||
|
|
return True, ExitReason.TIMEOUT, "Maximum duration reached"
|
||
|
|
```
|
||
|
|
|
||
|
|
### 4.3 Tidak Ada Breakeven Protection
|
||
|
|
**Solusi:**
|
||
|
|
```python
|
||
|
|
# Move to breakeven setelah profit tertentu
|
||
|
|
if current_profit >= 15: # $15 profit
|
||
|
|
if not breakeven_set:
|
||
|
|
move_sl_to_breakeven(ticket)
|
||
|
|
breakeven_set = True
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 5. BACKTEST vs LIVE - PERBEDAAN
|
||
|
|
|
||
|
|
### 5.1 Exit Timing Berbeda
|
||
|
|
| Aspek | Backtest | Live |
|
||
|
|
|-------|----------|------|
|
||
|
|
| Check interval | Per bar (15 min) | Per detik |
|
||
|
|
| ML reversal check | Setiap 5 bar | Setiap loop |
|
||
|
|
| Smart Hold | Tidak ada | Ada |
|
||
|
|
|
||
|
|
**Solusi:**
|
||
|
|
- Sinkronkan logic di `backtest_live_sync.py`
|
||
|
|
- Tambah Smart Hold logic ke backtest
|
||
|
|
- Gunakan bar-close sebagai trigger
|
||
|
|
|
||
|
|
### 5.2 Slippage Tidak Dihitung
|
||
|
|
```python
|
||
|
|
# Tambah slippage simulation
|
||
|
|
SLIPPAGE_PIPS = 0.5 # 0.5 pip slippage
|
||
|
|
|
||
|
|
def simulate_entry(entry_price, direction):
|
||
|
|
if direction == "BUY":
|
||
|
|
return entry_price + SLIPPAGE_PIPS * 0.1
|
||
|
|
else:
|
||
|
|
return entry_price - SLIPPAGE_PIPS * 0.1
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 6. PRIORITAS PERBAIKAN
|
||
|
|
|
||
|
|
| # | Item | Risiko | Effort | Prioritas |
|
||
|
|
|---|------|--------|--------|-----------|
|
||
|
|
| 1 | Broker SL | KRITIS | Low | **P0** |
|
||
|
|
| 2 | ATR-based SL | TINGGI | Medium | **P1** |
|
||
|
|
| 3 | Faster reversal exit | TINGGI | Low | **P1** |
|
||
|
|
| 4 | Time-based exit | SEDANG | Low | **P2** |
|
||
|
|
| 5 | Dynamic ML threshold | SEDANG | Low | **P2** |
|
||
|
|
| 6 | Partial TP | SEDANG | Medium | **P3** |
|
||
|
|
| 7 | Breakeven logic | SEDANG | Low | **P3** |
|
||
|
|
| 8 | Backtest sync | SEDANG | High | **P3** |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 7. SKENARIO TERBURUK
|
||
|
|
|
||
|
|
### Skenario 1: Weekend Gap
|
||
|
|
- Jumat: Posisi BUY di 2000, profit $10
|
||
|
|
- Weekend: Berita ekonomi buruk
|
||
|
|
- Senin: Market buka di 1950 (-50 pips = -$50)
|
||
|
|
- **Tanpa broker SL = loss unlimited**
|
||
|
|
|
||
|
|
### Skenario 2: Flash Crash
|
||
|
|
- Posisi aktif, harga normal
|
||
|
|
- Flash crash -2% dalam 1 menit
|
||
|
|
- Bot detect, tapi close gagal (broker overload)
|
||
|
|
- **Tanpa broker SL = loss unlimited**
|
||
|
|
|
||
|
|
### Skenario 3: Connection Lost
|
||
|
|
- Posisi aktif dengan profit $20
|
||
|
|
- Internet mati 2 jam
|
||
|
|
- Market reversal -$60
|
||
|
|
- **Tanpa broker SL = loss unlimited**
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 8. IMPLEMENTASI SEGERA
|
||
|
|
|
||
|
|
File yang perlu diubah:
|
||
|
|
1. `main_live.py` - Tambah broker SL
|
||
|
|
2. `smc_polars.py` - ATR-based SL
|
||
|
|
3. `smart_risk_manager.py` - Faster exit, time-based exit
|
||
|
|
4. `backtest_live_sync.py` - Sinkronkan dengan live
|