Sistem bot trading berbasis Python yang modular, real-time, dan production-ready untuk Bybit Perpetual Futures (CCXT Pro). Kode infrastruktur (engine) terpisah dari kode user (strategi) — kamu cukup edit strategy.py.
Bybit (WebSocket / REST, via CCXT Pro)
│
▼
engine/data_stream.py ← OrderBook + Funding Rate + Heartbeat
│ (dua timestamp: ts_event exchange / ts_init terima)
├─[DATA_MODE="tick"]──► engine/data_resampler.py ← Susun candle dari tick mentah
│
└─[DATA_MODE="kline"]─► engine/candle_stream.py ← Candle instan Bybit Kline WS
│
▼
strategy.py ← USER EDITS HERE ONLY
│
▼
engine/strategy_runtime.py ← Orkestrasi: signal → record → execute
│
▼
engine/execution.py ← Order (paper/live)
│ + in-flight guard + optimistic state
│ + precision (Decimal) + stale-data check
▼
engine/position_manager.py ← Realtime PnL + sync Bybit
│
▼
engine/trade_logger.py ← CSV logging (trades + equity)
│
▼
dashboard.py ← Streamlit monitoring
Modul infrastruktur ada di folder
engine/.main.pyotomatis menambahkanengine/kesys.path, jadi import tetap flat (import execution,import position_manager, dst).
pip install -r requirements.txtPython 3.12+ disarankan (diuji di 3.14).
Buat file .env di folder utama dan isi kunci API Anda. Didukung dua penamaan
(generik EXCHANGE_* direkomendasikan; BYBIT_* tetap kompatibel):
# Generik (rekomendasi)
EXCHANGE_API_KEY="api_key_live_kamu"
EXCHANGE_API_SECRET="secret_live_kamu"
EXCHANGE_DEMO_API_KEY="api_key_demo_kamu"
EXCHANGE_DEMO_API_SECRET="secret_demo_kamu"
# (Fallback lama — masih dibaca)
# BYBIT_LIVE_API_KEY / BYBIT_LIVE_API_SECRET
# BYBIT_DEMO_API_KEY / BYBIT_DEMO_API_SECRETLalu edit konfigurasi di config.py:
EXCHANGE = "bybit" # bybit, binance, okx, bitget, ...
SYMBOL = "XRPUSDT"
MODE = "demo" # "paper" | "demo" | "live"
# Mode pengambilan data candle:
# "kline" = Bybit native Kline WebSocket (RINGAN, sinkron TradingView)
# "tick" = Tick-by-tick resampler (BERAT, data granular penuh)
DATA_MODE = "kline"
# Resampling timeframe — edit di sini, otomatis berlaku di seluruh sistem
TIMEFRAMES = {
"1m": (60, 200), # 1 menit, simpan 200 candle
"15m": (900, 50), # 15 menit, simpan 50 candle
# "1h": (3600, 30), # aktifkan dengan hapus tanda #
}pip install pytest pytest-cov pytest-asyncio # sekali install saja
python -m pytest # ~376 test
python -m pytest --cov # plus coverage reportTest cover: PnL/money math, signal generation, engine orchestration, monitoring, order execution, precision, stale-data, + stress/concurrency (race & lockout) engine. Berguna setelah refactor untuk pastikan tidak ada regresi.
Catatan: 9 test di
tests/test_strategy_sqz.pysaat ini merah karenastrategy.pyaktif adalah varian MA-crossover (bukan Squeeze) — utang teknis lama, tidak terkait engine.
Pre-flight check berjalan otomatis saat python main.py startup (dan saat klik
strategy.py, dua level:
| Level | Contoh | Efek |
|---|---|---|
| Error Fatal | Syntax error, on_tick() tidak ada, time.sleep() di generate_signal |
Bot DIBLOKIR |
| Saran | HTTP request tiap tick, file I/O, data parsial | Bot tetap jalan, hanya diingatkan |
Validasi ini juga tersedia di halaman ⚙️ Control Panel — klik
▶️ Start Bot, sistem memvalidasi otomatis sebelum bot dinyalakan.
python main.py
main.pyotomatis menjalankan pre-flight check saat startup. Error fatal → bot berhenti + tampilkan pesan. Saran (warning) → ditampilkan tapi tidak menghentikan bot.
python -m streamlit run dashboard.pyBuka: http://localhost:8501
Jika Anda men-duplikasi folder bot untuk strategi/koin berbeda (mis. Bot_DOGE, Bot_BTC),
Anda TIDAK PERLU menjalankan banyak dashboard. Cukup buka 1 halaman Streamlit, lalu pada
kolom teks Sidebar masukkan path folder-folder bot (tiap path beda baris). Aplikasi membaca
rekaman mereka serentak dan menggambar 1 Portofolio Gabungan.
| Paper | Demo | Live | |
|---|---|---|---|
| API Key | Tidak perlu | DEMO key | LIVE key |
| Order | Simulasi lokal | Masuk server Bybit Demo | Uang nyata |
| PnL/Balance | Hitung lokal | Fetch dari Bybit tiap 2s | Fetch dari Bybit tiap 2s |
| Reset saldo | Restart bot | UI Bybit Demo | - |
| Cocok untuk | Dev & debug strategi | Validasi eksekusi API | Trading nyata |
- Login Bybit → Trade → Demo Trading
- Buat API Key: Account → API Management (System-generated)
- Isi
.env(EXCHANGE_DEMO_API_KEY/EXCHANGE_DEMO_API_SECRET) dan setMODE = "demo"diconfig.py.
Engine sudah melewati perbaikan korektnes + pengujian di luar happy-path. Bagian ini mendokumentasikan apa yang menjaga engine tetap konsisten (terutama di demo/live).
- In-flight order guard — hanya 1 order live in-flight pada satu waktu. Mencegah duplicate-fire: tick 100ms berikutnya tidak men-dispatch order kembar saat state belum sync. (Bug nyata yang dulu bikin posisi menumpuk 2×.)
- Optimistic state update — setelah fill terkonfirmasi,
position_managerdi-update SEKARANG (bukan nunggu sync ~2s), menutup window di mana strategi bisa fire ulang. Akunting uang tetap milik sync loop dari data exchange (sumber kebenaran). - No-retry pada desync
110017— kalau exchange bilang posisi sudah zero, engine clear state lokal & tidak retry (cegah spam + state ngawur). - Zero-qty guard — order yang membulat ke 0 (akibat harga/config) ditolak fail-fast sebelum dikirim ke exchange.
- Fixed-point precision (
engine/precision.py) — harga/qty/uang lewatDecimal, bukan float. Menutup bug sizing laten (mis. float0.3 / 0.1→ 0.2, kehilangan 1 step).make_qty()raise kalau hasil nol;round_money()menghapus drift di fee/PnL. - Stale-data 2 lapis — selain cek waktu-terima (deteksi disconnect), engine cek
ts_event(waktu exchange) untuk menolak trade saat feed lag (pesan masuk tapi data tua) — kasus yang luput dari cek waktu-terima. Lihatengine/data_stream.py(best_bid_ts_event,data_latency_ms()).
recap_run.py— observer independen: catat modal awal + posisi, snapshot tiap N detik dari Bybit langsung, lalu rekap (delta modal, deteksi stacking via ground-truth Bybit, scan log error). Contoh:python recap_run.py --minutes 60.verify_collector.py— observer truth: snapshot balance/posisi Bybit demo →logs/bybit_truth.jsonl, untuk cross-check klaim bot.tests/test_engine_stress.py— stress/concurrency: paksa race (10–100 order fire serempak) & buktikan guard mencegah dispatch kembar + tak ada lockout permanen.
Kamu HANYA perlu edit strategy.py.
Strategi bebas pakai Machine Learning atau indikator teknikal biasa — tidak wajib.
Yang penting, fungsi generate_signal() ada dan return format yang benar.
| Fungsi | Deskripsi |
|---|---|
generate_signal(data) |
SATU-SATUNYA fungsi wajib. Analisa data dan return sinyal. |
🎉 Sejak refactor
strategy_runtime: Anda TIDAK PERLU menulison_tick(),execute_trade(), ataumanage_position(). Engine otomatis panggilgenerate_signal()tiap tick → catat ke dashboard → forward keexecution.place_order()jika action ≠ hold.
{"action": "buy" | "sell" | "close" | "hold", "reason": "alasan (opsional)"}Pre-flight check menjalankan
generate_signal()dengan data dummy saat startup. Jika return formatnya salah atau ada error, bot tidak jalan dan tampilkan pesan error.
def generate_signal(data):
candles = list(data["candles"].values())[0] # ambil timeframe apapun
if len(candles) < 20:
return {"action": "hold", "reason": "Data belum cukup"}
closes = [c["close"] for c in candles]
sma_fast = sum(closes[-5:]) / 5
sma_slow = sum(closes[-20:]) / 20
if sma_fast > sma_slow:
return {"action": "buy", "reason": "SMA5 cross above SMA20"}
return {"action": "hold", "reason": "Tidak ada sinyal"}Itu saja. Tidak perlu import execution, bot_monitor, atau menulis on_tick. Engine handle semuanya.
Hanya kalau butuh kontrol penuh — mis. sync state custom dengan position_manager seperti di
strategy_ml.py (Random Forest + Triple Barrier). Jika on_tick() didefinisikan, engine bypass
orkestrasi default dan serahkan eksekusi ke Anda.
def on_tick(data):
import execution, bot_monitor
signal = generate_signal(data)
bot_monitor.record_tick(signal, data=data) # WAJIB di mode advanced
if signal["action"] != "hold":
execution.place_order(signal)Cari parameter strategi terbaik via grid search dengan validasi out-of-sample. Semua diatur
lewat tuning_config.py — tidak ada CLI prompt.
-
Deklarasikan parameter di atas file strategi (gaya MQL5 input):
BB_LENGTH = 20 KC_LENGTH = 20 STOP_LOSS_PCT = 0.008 TAKE_PROFIT_PCT = 0.020
-
Edit
tuning_config.py— set strategy file, timeframe, rentang data, mode optimasi, model posisi, dan range parameter:STRATEGY_FILE = "strategy" # nama file tanpa .py TIMEFRAME = "2h" # 1m, 5m, 15m, 30m, 1h, 2h, 4h, 1d DAYS_BACK = 90 # rentang data dari sekarang OPTIM_MODE = "balanced" # profit | drawdown | balanced POSITION_MODEL = "single" # single | multi PARAMS = { "BB_LENGTH": [15, 25, 3], # [start, end, jumlah] → [15, 20, 25] "KC_LENGTH": [15, 25, 3], "STOP_LOSS_PCT": [0.005, 0.015, 3], # float → [0.005, 0.01, 0.015] "TAKE_PROFIT_PCT": [0.010, 0.040, 4], }
-
Jalankan:
python hypertune.py
Program otomatis: download OHLC dari Bybit → save CSV ke
data/→ split 80% in-sample / 20% out-of-sample → grid search → validasi top 10 di OOS → print top 3 di CMD.
| Format | Contoh | Hasil |
|---|---|---|
Range [start, end, N] |
[10, 30, 5] |
[10, 15, 20, 25, 30] — linspace N nilai |
| Range float | [0.005, 0.020, 4] |
[0.005, 0.01, 0.015, 0.02] — auto-detect float |
| Discrete list | [True, False] |
[True, False] — apa pun yang bukan format range |
| 4 elemen+ | [10, 15, 20, 25] |
discrete, pakai apa adanya |
Auto-detect tipe: kalau start & end keduanya int → hasil int (dedup setelah round). Selain itu → float.
| Mode | Skor |
|---|---|
profit |
Total PnL (USDT) — cari profit terbesar |
drawdown |
-max_drawdown_pct — cari drawdown terkecil |
balanced |
Sharpe ratio (return/risk) — paling direkomendasikan |
| Mode | Cara kerja | Cocok untuk |
|---|---|---|
single |
1 posisi aktif. Strategy handle entry + exit (return close saat keluar). |
Tuning realistis untuk live |
multi |
Tiap buy/sell = trade independen. Simulator handle exit pakai STOP_LOSS_PCT & TAKE_PROFIT_PCT. |
Analisa kualitas sinyal mentah |
⚠️ Multi-position tidak realistis untuk bot ini (bot fisik selalu single-position viaposition_manager). Pakai single untuk tuning yang akan dieksekusi live.
Untuk tiap top 3: Parameter yang ditemukan + In-Sample (80%) & Out-of-Sample (20%)
(PnL USDT+%, Trades, Win Rate, Max DD, Profit Factor, Sharpe, Max Consecutive Wins/Losses).
CSV OHLC tersimpan di data/<SYMBOL>_<TF>_<startdate>_<enddate>.csv.
- Hasil hanya tampil di CMD — strategy file TIDAK diubah otomatis. Copy nilai best manual.
- Out-of-sample jelek padahal in-sample bagus = overfitting, jangan dipakai.
- Support strategi apa pun yang: ada di folder ini, expose
generate_signal(data), parameter dideklarasi di atas file, nama param diPARAMSmatch variabel di strategy file. Ganti strategi cukup ubahSTRATEGY_FILE, tidak perlu edithypertune.py.
| Key | Isi |
|---|---|
data["candles"]["1m"] |
List candle 1m tertutup |
data["current"]["1m"] |
Candle 1m live (update tiap tick) |
data["candles"]["15m"] |
List candle 15m tertutup |
data["best_bid"] / data["best_ask"] |
{"price": float, "qty": float} |
data["bid_ask_spread"] |
ask - bid |
data["orderbook_imbalance"] |
-1.0 (sell) to +1.0 (buy) |
data["volume_delta"] |
buy_vol - sell_vol (50 tick terakhir) |
data["funding_rate"] |
Funding rate saat ini |
data["latest_tick"] |
Tick terakhir {timestamp, price, qty, side} |
data["is_warmup"] |
True saat preload historis — strategi tidak boleh order |
Label
"1m","15m"dst sesuai key diTIMEFRAMESdalamconfig.py.
Saat startup, main.py mengambil candle historis via Bybit REST (/v5/market/kline) untuk semua
timeframe ≥ 1m dan mengisi buffer. Selama proses ini data["is_warmup"] = True — strategi
otomatis return hold. Setelah selesai, flag di-set False dan bot masuk mode live.
DATA_MODE = "kline" (Default) |
DATA_MODE = "tick" |
|
|---|---|---|
| Sumber | Bybit Kline WebSocket | Bybit publicTrade WebSocket |
| RAM | Ringan | Berat (ribuan tick/menit) |
| Sinkronisasi TV | 100% sama | Bisa beda (repainting) |
| Volume Delta | Tidak tersedia | Tersedia |
| Cocok untuk | SMA, BB, RSI, MACD | Footprint, Order Flow |
Setiap update orderbook menyimpan dua waktu: ts_event (kapan kejadian di exchange) dan
ts_init/*_updated_at (kapan kita terima). Latensi feed = ts_init - ts_event
(data_stream.data_latency_ms()). Dipakai untuk deteksi feed lag & monitoring.
BUY → entry @ ASK, exit @ BID
SELL → entry @ BID, exit @ ASK
Validasi pre-order: stale-data (2 lapis), VWAP slippage depth-aware, & liquidity check. Qty dibulatkan fixed-point (Decimal) ke step instrumen.
# LONG
unrealized_pnl = (bid_price - entry_price) * qty
# SHORT
unrealized_pnl = (entry_price - ask_price) * qty
equity = balance + unrealized_pnlMode paper: dihitung lokal tiap tick. Mode demo/live: di-sync dari Bybit tiap PNL_SYNC_INTERVAL
detik (default 2s); state posisi juga di-update optimistic setelah fill.
logs/heartbeat.json diupdate tiap detik. Dashboard baca file ini:
- ONLINE jika
time.time() - last_update < 5 - OFFLINE jika tidak diupdate > 5 detik
| File | Isi |
|---|---|
logs/trade_history.csv |
Trade: entry/exit price, qty, PnL, alasan (terisi di mode paper) |
logs/equity_curve.csv |
Snapshot balance/equity/unrealized |
logs/heartbeat.json |
Timestamp terakhir bot aktif |
logs/bot_health.json |
Metrics health monitor (watchdog, error rate) |
logs/bybit_truth.jsonl |
Snapshot kebenaran Bybit (dari verify_collector.py) |
logs/recap_*.md |
Laporan rekap run (dari recap_run.py) |
Catatan: di mode demo/live, trade dilog ke exchange (bukan
trade_history.csv). Untuk rekap demo/live pakairecap_run.py+ snapshot Bybit.
| Risiko | Mitigasi |
|---|---|
| Duplicate-fire | In-flight order guard + optimistic state update (lihat Reliability) |
| Sizing salah | Fixed-point precision (Decimal) + zero-qty guard |
| Data basi / feed lag | Stale-data 2 lapis (waktu-terima + ts_event exchange) |
| Latency | Gunakan VPS di region server exchange (mis. Singapore) |
| Slippage | SLIPPAGE_TOLERANCE + VWAP depth-aware check di config.py |
| Disconnect | Supervisor auto-restart task (exponential backoff) + CCXT Pro reconnect |
| Partial Fill | Deteksi + (opsional) cancel sisa via CANCEL_ON_PARTIAL |
| State desync | PnL sync loop (otoritatif dari Bybit) + clear pada reject 110017 |
# Install Python 3.12+
sudo apt update && sudo apt install python3.12 python3-pip -y
# Upload / clone project, lalu:
cd /your/project
pip install -r requirements.txt
# Jalankan bot di background
nohup python main.py > logs/bot.log 2>&1 &
# Jalankan dashboard
python -m streamlit run dashboard.pybot/
├── main.py ← Entry point (auto pre-flight; tambah engine/ ke sys.path)
├── config.py ← Konfigurasi semua parameter (EXCHANGE, SYMBOL, MODE, DATA_MODE)
├── strategy.py ← USER EDIT DI SINI (cukup tulis generate_signal)
├── strategy_ml.py ← Variant ML (Random Forest + Triple Barrier) — referensi advanced
├── dashboard.py ← Streamlit: Live Monitor (halaman utama)
├── hypertune.py ← 🔬 Hyperparameter tuning CLI (grid search + 80/20 split)
├── tuning_config.py ← 🔬 Konfigurasi tuning
├── recap_run.py ← 🔭 Observer: modal awal → rekap N menit (deteksi duplicate-fire)
├── verify_collector.py ← 🔭 Observer: snapshot Bybit truth → logs/bybit_truth.jsonl
├── requirements.txt
├── pytest.ini
├── .env ← API keys (EXCHANGE_* / BYBIT_*) — JANGAN commit
│
├── engine/ ← INFRASTRUKTUR (jarang disentuh user)
│ ├── data_stream.py ← WebSocket orderbook + funding + heartbeat (ts_event/ts_init)
│ ├── candle_stream.py ← Bybit Kline WebSocket (DATA_MODE="kline")
│ ├── data_resampler.py ← Tick → candle realtime (DATA_MODE="tick")
│ ├── strategy_runtime.py ← Orkestrator (signal → record → execute)
│ ├── execution.py ← Order paper/live + in-flight guard + optimistic state + precision
│ ├── position_manager.py ← PnL tracking + optimistic state + sync Bybit
│ ├── precision.py ← Fixed-point Decimal (make_price / make_qty / round_money)
│ ├── ccxt_client.py ← Wrapper CCXT Pro (init exchange, demo endpoint)
│ ├── trade_logger.py ← CSV logging (non-blocking, daemon thread)
│ ├── bot_monitor.py ← Health monitor: watchdog, error rate, data quality
│ ├── preflight_check.py ← Validator strategy.py (cek error + warning)
│ ├── backtest.py ← Engine backtest
│ └── ft_types.py ← Pusat definisi TypedDict (single source of truth)
│
├── pages/ ← Streamlit multi-page
│ ├── 1_backtest.py ← Simulasi backtest (Mode Colab & Live)
│ └── 2_control_panel.py ← Start/Stop bot + validasi strategy sebelum run
│
├── models/ ← Model ML (opsional — jika tidak ada, strategy jalan tanpa ML)
│ ├── trading_model_15m.pkl
│ └── trading_scaler_15m.pkl
│
├── tests/ ← pytest suite (~376 test) — `python -m pytest`
│ ├── test_execution.py ← Order fill, in-flight guard, optimistic, stale-data
│ ├── test_engine_stress.py ← Stress/concurrency (race & lockout) — engine
│ ├── test_precision.py ← Fixed-point precision (Decimal)
│ ├── test_position_manager.py, test_data_stream.py, test_strategy_ml.py, ... (dll)
│ └── conftest.py
│
├── docs/
│ ├── README.md ← (file ini)
│ └── penjelasan (1).txt
│
└── logs/ ← Auto-generated runtime
├── heartbeat.json ← Timestamp bot aktif
├── bot_health.json ← Health monitor metrics
├── trade_history.csv ← Trade (paper mode)
├── equity_curve.csv ← Snapshot balance/equity
├── bybit_truth.jsonl ← Snapshot kebenaran Bybit (verify_collector)
└── recap_*.md ← Laporan rekap (recap_run)
Engine inti memakai typing pragmatis (Mypy-compliant). Semua function signature & variabel
lokal dianotasi; struktur data kompleks didefinisikan sebagai TypedDict terpusat di
engine/ft_types.py.
| TypedDict | Dipakai oleh |
|---|---|
Candle |
data_resampler, candle_stream, strategy |
MarketDataSnapshot |
Return get_live_data() — kontrak antar modul |
Signal |
strategy → execution |
OrderResult |
execution |
PositionState |
position_manager |
PnlSummary |
position_manager → strategy → dashboard |
TradeLogRow / EquityLogRow |
trade_logger |
python -m mypy engine/ --ignore-missing-imports --no-strict-optionalTyping Level 3 (Pragmatic Strict): tidak pakai
mypy --strictatau Pydantic karena data WebSocket Bybit bersifat dynamic JSON yang tak bisa diverifikasi penuh saat compile time.
short: https://h1.nu/1pCx7