- XGBoost ML model with 37 features for market direction prediction - Smart Money Concepts (SMC): Order Blocks, FVG, BOS, CHoCH - HMM market regime detection (trending/ranging/volatile) - ATR-based stop loss with 1.5 ATR minimum distance - Broker-level SL protection with fallback - Time-based exit (max 6 hours per trade) - Session-aware trading optimized for London/NY overlap - Auto-retraining based on market conditions - Telegram notifications and web dashboard - Backtest results: 63.9% win rate, 2.64 profit factor, 4.83 Sharpe Backtest period: Jan 2025 - Feb 2026, 654 trades, $4,189 net P/L Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
404 lines
12 KiB
Markdown
404 lines
12 KiB
Markdown
# SMC Analyzer (Smart Money Concepts)
|
|
|
|
> **File:** `src/smc_polars.py`
|
|
> **Framework:** Pure Polars (vectorized, tanpa loop)
|
|
|
|
---
|
|
|
|
## Apa Itu SMC?
|
|
|
|
Smart Money Concepts adalah metode analisis berdasarkan **cara institusi besar (bank, hedge fund) trading**. SMC membaca **struktur pasar** dan **jejak uang besar** untuk menemukan zona entry yang presisi.
|
|
|
|
**Analogi:** SMC adalah **peta jalan** — menunjukkan zona penting, rambu lalu lintas, dan rute terbaik.
|
|
|
|
---
|
|
|
|
## 6 Konsep yang Diimplementasikan
|
|
|
|
| # | Konsep | Fungsi | Lines |
|
|
|---|--------|--------|-------|
|
|
| 1 | Swing Points | Puncak & lembah penting | 185-261 |
|
|
| 2 | Fair Value Gap (FVG) | Imbalance/gap harga | 84-183 |
|
|
| 3 | Order Block (OB) | Zona order institusi | 263-368 |
|
|
| 4 | Break of Structure (BOS) | Kelanjutan tren | 370-457 |
|
|
| 5 | Change of Character (CHoCH) | Pembalikan tren | 370-457 |
|
|
| 6 | Liquidity Zones | Kumpulan stop loss | 459-551 |
|
|
|
|
---
|
|
|
|
## 1. Swing Points (Fractal High/Low)
|
|
|
|
**Fungsi:** Mendeteksi puncak dan lembah penting di chart.
|
|
|
|
### Algoritma
|
|
|
|
```
|
|
Window: 2 x swing_length + 1 = 11 candle (default swing_length=5)
|
|
|
|
Swing High: High saat ini = Maximum dalam 11 candle
|
|
Swing Low: Low saat ini = Minimum dalam 11 candle
|
|
```
|
|
|
|
### Visualisasi
|
|
|
|
```
|
|
/\ <- Swing High (high = max 11 candle)
|
|
/ \
|
|
/ \
|
|
/ \
|
|
/ \/ <- Swing Low (low = min 11 candle)
|
|
/
|
|
```
|
|
|
|
### Output
|
|
| Kolom | Nilai | Keterangan |
|
|
|-------|-------|-----------|
|
|
| `swing_high` | 1 / 0 | 1 jika swing high |
|
|
| `swing_low` | -1 / 0 | -1 jika swing low |
|
|
| `swing_high_level` | float | Harga di swing high |
|
|
| `swing_low_level` | float | Harga di swing low |
|
|
| `last_swing_high` | float | Swing high terakhir (forward fill) |
|
|
| `last_swing_low` | float | Swing low terakhir (forward fill) |
|
|
|
|
---
|
|
|
|
## 2. Fair Value Gap (FVG)
|
|
|
|
**Fungsi:** Mendeteksi **imbalance/gap** di harga — zona yang belum "diisi" oleh pasar.
|
|
|
|
### Algoritma
|
|
|
|
```
|
|
Bullish FVG: Bearish FVG:
|
|
Candle T-2: ████ high Candle T-2: ████ low
|
|
| |
|
|
| GAP (celah) | GAP (celah)
|
|
| |
|
|
Candle T+1: ████ low Candle T+1: ████ high
|
|
|
|
Syarat Bullish: high[T-2] < low[T+1]
|
|
Syarat Bearish: low[T-2] > high[T+1]
|
|
```
|
|
|
|
### Zona FVG
|
|
|
|
```
|
|
Bullish FVG Zone:
|
|
Top = low[T+1] (batas atas gap)
|
|
Bottom = high[T-2] (batas bawah gap)
|
|
Mid = (top + bottom) / 2 (50% retracement)
|
|
```
|
|
|
|
### Output
|
|
| Kolom | Nilai | Keterangan |
|
|
|-------|-------|-----------|
|
|
| `fvg_signal` | 1 / -1 / 0 | Bullish / Bearish / Tidak ada |
|
|
| `fvg_top` | float | Batas atas gap |
|
|
| `fvg_bottom` | float | Batas bawah gap |
|
|
| `fvg_mid` | float | Titik tengah (target retracement) |
|
|
|
|
**Peran:** Zona entry ideal — harga cenderung **kembali mengisi gap** sebelum melanjutkan.
|
|
|
|
---
|
|
|
|
## 3. Order Block (OB)
|
|
|
|
**Fungsi:** Mendeteksi candle terakhir sebelum pergerakan besar — zona dimana institusi menaruh order.
|
|
|
|
### Algoritma
|
|
|
|
```
|
|
Bullish OB:
|
|
1. Temukan swing low
|
|
2. Lihat 10 candle ke belakang
|
|
3. Cari candle bearish terakhir (close < open)
|
|
4. Jika candle berikutnya close di atas high candle tersebut:
|
|
-> Candle itu = Bullish Order Block
|
|
|
|
Bearish OB:
|
|
1. Temukan swing high
|
|
2. Lihat 10 candle ke belakang
|
|
3. Cari candle bullish terakhir (close > open)
|
|
4. Jika candle berikutnya close di bawah low candle tersebut:
|
|
-> Candle itu = Bearish Order Block
|
|
```
|
|
|
|
### Visualisasi
|
|
|
|
```
|
|
Bullish OB: Bearish OB:
|
|
████ <- Bullish candle terakhir
|
|
████ <- Bearish candle terakhir sebelum jatuh
|
|
sebelum naik ═══════════════
|
|
═══════════════ ||| turun
|
|
||| naik
|
|
```
|
|
|
|
### Output
|
|
| Kolom | Nilai | Keterangan |
|
|
|-------|-------|-----------|
|
|
| `ob` | 1 / -1 / 0 | Bullish / Bearish / Tidak ada |
|
|
| `ob_top` | float | Batas atas zona OB |
|
|
| `ob_bottom` | float | Batas bawah zona OB |
|
|
| `ob_mitigated` | bool | True jika OB sudah dikunjungi ulang |
|
|
|
|
**Peran:** Zona support/resistance berdasarkan aksi institusi besar.
|
|
|
|
---
|
|
|
|
## 4. Break of Structure (BOS)
|
|
|
|
**Fungsi:** Mendeteksi **kelanjutan tren** — harga menembus swing point searah tren.
|
|
|
|
### Algoritma
|
|
|
|
```python
|
|
# Tren sudah BULLISH, lalu:
|
|
if close > last_swing_high:
|
|
bos = 1 # Bullish BOS — tren naik BERLANJUT
|
|
|
|
# Tren sudah BEARISH, lalu:
|
|
if close < last_swing_low:
|
|
bos = -1 # Bearish BOS — tren turun BERLANJUT
|
|
```
|
|
|
|
### Visualisasi
|
|
|
|
```
|
|
Bullish BOS:
|
|
SH1 SH2 (baru ditembus!)
|
|
/ \ / close >>>
|
|
/ \ /
|
|
/ SL1 -> BOS! Tren naik lanjut
|
|
|
|
Bearish BOS:
|
|
\ SH1
|
|
\ / \
|
|
\ / \ close <<<
|
|
SL1 SL2 (baru ditembus!) -> BOS! Tren turun lanjut
|
|
```
|
|
|
|
### Output
|
|
| Kolom | Nilai | Keterangan |
|
|
|-------|-------|-----------|
|
|
| `bos` | 1 / -1 / 0 | Bullish / Bearish / Tidak ada |
|
|
|
|
**Peran:** Konfirmasi bahwa **tren masih kuat** dan lanjut.
|
|
|
|
---
|
|
|
|
## 5. Change of Character (CHoCH)
|
|
|
|
**Fungsi:** Mendeteksi **pembalikan tren** — harga menembus swing point berlawanan tren.
|
|
|
|
### Algoritma
|
|
|
|
```python
|
|
# Tren sedang BEARISH, lalu:
|
|
if close > last_swing_high:
|
|
choch = 1 # Bullish CHoCH — REVERSAL naik!
|
|
|
|
# Tren sedang BULLISH, lalu:
|
|
if close < last_swing_low:
|
|
choch = -1 # Bearish CHoCH — REVERSAL turun!
|
|
```
|
|
|
|
### Visualisasi
|
|
|
|
```
|
|
Bearish CHoCH (tren naik -> balik turun):
|
|
SH <- gagal naik
|
|
/ \
|
|
/ \
|
|
/ close menembus SL >>> CHoCH! Reversal turun!
|
|
SL
|
|
|
|
Bullish CHoCH (tren turun -> balik naik):
|
|
SH
|
|
\ close menembus SH >>> CHoCH! Reversal naik!
|
|
\ /
|
|
\ /
|
|
SL <- gagal turun
|
|
```
|
|
|
|
### Output
|
|
| Kolom | Nilai | Keterangan |
|
|
|-------|-------|-----------|
|
|
| `choch` | 1 / -1 / 0 | Bullish / Bearish / Tidak ada |
|
|
| `market_structure` | 1 / -1 / 0 | Bullish / Bearish / Netral |
|
|
|
|
**Peran:** **Early warning** perubahan arah tren.
|
|
|
|
---
|
|
|
|
## 6. Liquidity Zones
|
|
|
|
**Fungsi:** Mendeteksi kumpulan stop loss (equal highs/lows) yang bisa "disapu" oleh institusi.
|
|
|
|
### Algoritma
|
|
|
|
```
|
|
1. Hitung rolling std & mean dari highs dan lows (window=20)
|
|
2. Coefficient of Variation = std / mean
|
|
3. Jika CV < 0.001 (0.1%):
|
|
-> Harga sangat mirip = cluster likuiditas
|
|
-> BSL (Buy Side Liquidity) = level high
|
|
-> SSL (Sell Side Liquidity) = level low
|
|
4. Deteksi sweep:
|
|
-> BSL sweep: High > BSL lalu close < BSL
|
|
-> SSL sweep: Low < SSL lalu close > SSL
|
|
```
|
|
|
|
### Visualisasi
|
|
|
|
```
|
|
Buy Side Liquidity (BSL): Sell Side Liquidity (SSL):
|
|
═══════ equal highs ═══════
|
|
████ ████ ████ ████ ████ ████ ████ ████
|
|
═══════ equal lows ═══════
|
|
^ Stop loss short sellers ^ Stop loss long traders
|
|
^ Institusi sweep ke atas ^ Institusi sweep ke bawah
|
|
```
|
|
|
|
### Output
|
|
| Kolom | Nilai | Keterangan |
|
|
|-------|-------|-----------|
|
|
| `bsl_level` | float | Level buy side liquidity |
|
|
| `ssl_level` | float | Level sell side liquidity |
|
|
| `liquidity_sweep` | "BSL" / "SSL" / None | Sweep terdeteksi |
|
|
|
|
---
|
|
|
|
## Signal Generation
|
|
|
|
### ATR-Based Dynamic SL/TP (v3 Update)
|
|
|
|
Sebelum menghitung SL dan TP, sistem mengambil nilai ATR untuk kalkulasi dinamis:
|
|
|
|
```python
|
|
# Line 631-634
|
|
atr = latest["atr"] # Dari Feature Engineering
|
|
min_sl_distance = 1.5 * atr # Minimum jarak SL = 1.5 ATR
|
|
max_tp_distance = 4.0 * atr # Maximum jarak TP = 4.0 ATR
|
|
|
|
# Fallback jika ATR tidak tersedia:
|
|
atr = current_close * 0.01 # 1% dari harga
|
|
```
|
|
|
|
### Kondisi Bullish Signal
|
|
|
|
```
|
|
IF (market_structure == BULLISH ATAU ada BOS/CHoCH bullish)
|
|
AND (ada FVG bullish ATAU Order Block bullish):
|
|
|
|
Entry = FVG bottom atau OB bottom
|
|
|
|
SL (v3 - ATR-based, lebih protektif):
|
|
swing_sl = last_swing_low (jika ada & di bawah entry)
|
|
atr_sl = entry - 1.5 * ATR
|
|
SL = MIN(swing_sl, atr_sl) <- pilih yang LEBIH JAUH
|
|
|
|
TP (v3 - dibatasi realistis):
|
|
risk = entry - SL
|
|
tp = entry + (risk * 2) <- minimum 2:1 RR
|
|
IF tp > entry + 4*ATR:
|
|
tp = entry + 4*ATR <- cap TP agar realistis
|
|
```
|
|
|
|
### Kondisi Bearish Signal
|
|
|
|
```
|
|
IF (market_structure == BEARISH ATAU ada BOS/CHoCH bearish)
|
|
AND (ada FVG bearish ATAU Order Block bearish):
|
|
|
|
Entry = FVG top atau OB top
|
|
|
|
SL (v3 - ATR-based, lebih protektif):
|
|
swing_sl = last_swing_high (jika ada & di atas entry)
|
|
atr_sl = entry + 1.5 * ATR
|
|
SL = MAX(swing_sl, atr_sl) <- pilih yang LEBIH JAUH
|
|
|
|
TP (v3 - dibatasi realistis):
|
|
risk = SL - entry
|
|
tp = entry - (risk * 2) <- minimum 2:1 RR
|
|
IF tp < entry - 4*ATR:
|
|
tp = entry - 4*ATR <- cap TP agar realistis
|
|
```
|
|
|
|
### Perbandingan SL/TP Lama vs Baru
|
|
|
|
```
|
|
┌────────────┬───────────────────────────┬──────────────────────────────┐
|
|
│ Komponen │ Sebelum (v2) │ Sesudah (v3) │
|
|
├────────────┼───────────────────────────┼──────────────────────────────┤
|
|
│ SL (BUY) │ swing_low atau │ MIN(swing_low, entry-1.5ATR) │
|
|
│ │ entry * 0.995 (bisa dekat)│ <- selalu cukup jauh │
|
|
├────────────┼───────────────────────────┼──────────────────────────────┤
|
|
│ SL (SELL) │ swing_high atau │ MAX(swing_high, entry+1.5ATR)│
|
|
│ │ entry * 1.005 (bisa dekat)│ <- selalu cukup jauh │
|
|
├────────────┼───────────────────────────┼──────────────────────────────┤
|
|
│ TP │ risk * 2 │ MIN(risk*2, 4*ATR) │
|
|
│ │ (bisa sangat jauh) │ <- dibatasi realistis │
|
|
└────────────┴───────────────────────────┴──────────────────────────────┘
|
|
```
|
|
|
|
### Sistem Confidence
|
|
|
|
```
|
|
Base confidence: 55%
|
|
+ BOS/CHoCH: +10%
|
|
+ FVG: +10%
|
|
+ Order Block: +10%
|
|
Maximum: 85%
|
|
```
|
|
|
|
### Output Signal
|
|
|
|
```python
|
|
SMCSignal:
|
|
signal_type: "BUY" / "SELL"
|
|
entry_price: float
|
|
stop_loss: float # ATR-based (min 1.5 ATR dari entry)
|
|
take_profit: float # 2:1 RR, capped di 4 ATR
|
|
confidence: 0.55 - 0.85
|
|
reason: "Bullish BOS + FVG + OB"
|
|
risk_reward: float # Minimum 2.0
|
|
```
|
|
|
|
---
|
|
|
|
## Konfigurasi
|
|
|
|
```python
|
|
SMCConfig:
|
|
swing_length: 5 # Window untuk deteksi swing (11 bar total)
|
|
fvg_min_gap_pips: 2.0 # Minimum ukuran FVG
|
|
ob_lookback: 10 # Berapa jauh cari OB ke belakang
|
|
bos_close_break: True # Harus close (bukan wick) yang break
|
|
```
|
|
|
|
---
|
|
|
|
## Integrasi dalam Pipeline
|
|
|
|
```
|
|
Data OHLCV
|
|
|
|
|
v
|
|
smc.calculate_all(df)
|
|
|--- calculate_fair_value_gaps()
|
|
|--- calculate_swing_points()
|
|
|--- calculate_order_blocks() <- butuh swing points
|
|
|--- calculate_structure_breaks() <- butuh swing points
|
|
|--- calculate_liquidity_zones()
|
|
|
|
|
v
|
|
smc.generate_signal(df)
|
|
|
|
|
v
|
|
SMCSignal (entry, SL, TP, confidence)
|
|
|
|
|
v
|
|
Dikombinasikan dengan XGBoost + HMM
|
|
```
|