Pariti backtest-live: mengapa bot anda berdagang berbeza daripada backtest
Anda menjalankan strategi melalui backtest. Sharpe 2.1, MaxDD -8%, PnL +67%. Anda melancarkan bot. Sebulan kemudian anda bandingkan: isyarat yang sama, tempoh yang sama — tetapi PnL langsung 40% lebih rendah. Drawdown satu setengah kali lebih dalam. Dua daripada sepuluh dagangan langsung tidak dilaksanakan.
Ini bukan pepijat. Ini adalah perbezaan backtest-live — perbezaan sistematik antara keputusan backtest dan dagangan sebenar. Semua orang mengalaminya. Soalan sebenarnya adalah sama ada anda mengetahuinya dan sama ada anda boleh mengawalnya.
Artikel ini menyediakan taksonomi lengkap perbezaan, corak seni bina untuk meminimumkannya, dan senarai semak praktikal untuk memantau pariti dalam pengeluaran.
Sindrom "ia berjaya dalam backtest"

Setiap pedagang algo melalui kitaran ini:
- Menulis strategi dalam notebook Jupyter
- Menjalankan backtest pada CSV sejarah — hasilnya bagus
- Menulis semula logik sebagai bot (sering dalam bahasa atau rangka kerja yang berbeza)
- Dilancarkan — keputusan tidak sepadan
- Mula mencari pepijat, tidak menjumpainya — "pasaran telah berubah"
Masalahnya bukan pasaran. Masalahnya ialah backtest dan bot adalah dua produk perisian yang berbeza yang memodelkan realiti yang sama secara berbeza. Perbezaan tidak dapat dielakkan, tetapi ia boleh disistematikkan dan diminimumkan.
Taksonomi Perbezaan

Semua sumber perbezaan jatuh ke dalam empat kategori. Untuk setiap satu — penilaian keterukan (dari 1 hingga 5) dan sumbangan tipikal kepada perbezaan PnL.
1. Perbezaan data (keterukan: 3/5)
Data yang dilihat backtest dan data yang dilihat bot secara masa nyata tidak sama.
Cap waktu. Bursa menyampaikan lilin dengan peraturan berbeza untuk penugasan cap waktu. Satu bursa menanda lilin dengan permulaan tempoh, yang lain dengan penghujung. REST API mungkin mengembalikan lilin dengan kelewatan 1-3 saat selepas penutupan sebenar. Backtest berfungsi dengan cap waktu "ideal" dari fail sejarah.
Pengagregatan OHLCV. Data sejarah sering diagregatkan oleh pembekal secara berbeza daripada yang dilakukan bursa dalam masa nyata. Perbezaannya ada pada digit terakhir — tetapi dengan isyarat ambang (persilangan MA, pecah paras) ini menentukan sama ada strategi memasuki posisi atau tidak.
Jurang dan data hilang. Data sejarah biasanya bersih — lilin yang hilang diisi dengan interpolasi. Dalam masa nyata, WebSocket mungkin putus, dan bot terlepas 30 saat data.
Sumbangan tipikal kepada perbezaan PnL: 2-5% daripada PnL tahunan.
2. Perbezaan pelaksanaan (keterukan: 5/5)

Kelas perbezaan yang paling berbahaya. Backtest mensimulasikan pelaksanaan dengan sempurna — realiti jauh dari ideal.
Slippage. Backtest mengisi pesanan pada harga penutupan (atau harga isyarat). Dalam realiti, pesanan pasaran dilaksanakan pada bid/ask terbaik ditambah slippage yang bergantung pada volum dan kecairan. Untuk posisi $10K pada altcoin kecairan sederhana, slippage boleh menjadi 0.05-0.3%.
Formula untuk slippage kumulatif bagi dagangan:
di mana adalah slippage dagangan ke-, bergantung pada kedalaman buku pesanan:
Kependaman. Dari saat isyarat dijana hingga pelaksanaan pesanan, masa berlalu: pengiraan isyarat (1-50 ms), penghantaran permintaan (10-200 ms), pemadanan di bursa (1-10 ms). Dalam backtest, kependaman = 0. Dalam keadaan langsung — harga boleh bergerak.
Pengisian separa. Backtest mengandaikan 100% pesanan diisi serta-merta. Dalam realiti, pesanan had mungkin diisi sebahagiannya — atau tidak diisi langsung jika harga berbalik. Untuk pesanan pasaran di pasaran tidak cair, pesanan "tergelincir" melalui pelbagai aras buku pesanan.
Keutamaan giliran. Pesanan had yang diletakkan pada harga bid terbaik tidak akan diisi serta-merta — ia beratur di belakang semua pesanan yang diletakkan sebelumnya pada aras tersebut. Backtest yang menganggap "harga disentuh = pesanan diisi" secara sistematik melebih-lebihkan kadar pengisian.
Sumbangan tipikal kepada perbezaan PnL: 10-30% daripada PnL tahunan.
3. Perbezaan logik (keterukan: 4/5)
Ini adalah perbezaan dalam kod strategi itu sendiri antara backtest dan bot.
Pangkalan kod berasingan. Anti-corak klasik: backtests/strategy_a.py dan bot/strategy_a.py — dua fail berasingan yang "melakukan perkara yang sama." Selepas tiga bulan suntingan, ia pasti akan berbeza. Seseorang menambah penapis dalam backtest dan terlupa untuk mereplikasinya dalam bot. Atau sebaliknya — pepijat diperbaiki dalam bot tetapi kekal dalam backtest.
Rangka kerja yang berbeza. Backtest pada pandas dengan operasi vektor, bot pada asyncio dengan logik dipacu peristiwa. Walaupun dengan strategi yang sama, kes tepi dikendalikan secara berbeza: pembundaran, susunan pemeriksaan syarat, pengendalian NaN.
Pengurusan keadaan. Backtest biasanya tanpa keadaan — ia berulang ke atas susunan data. Bot mempunyai keadaan — ia menyimpan posisi, baki, sejarah pesanan. Mulakan semula bot, kehilangan keadaan, penyahsegerakan dengan bursa — semua ini adalah sumber perbezaan.
Sumbangan tipikal kepada perbezaan PnL: 5-20% daripada PnL tahunan.
4. Perbezaan kos (keterukan: 3/5)
Perbezaan dalam pemodelan kos dagangan.
Kadar pembiayaan. Kebanyakan backtest niaga hadapan kekal tidak mengambil kira kadar pembiayaan langsung. Pada leverage 10x dan kadar purata 0.01% setiap 8 jam, ini adalah setahun — lebih daripada PnL kebanyakan strategi. Analisis terperinci ada dalam artikel Kadar pembiayaan membunuh leverage anda.
Komisen. Komisen maker/taker biasanya dimodelkan tetapi sering dengan kadar yang salah. Tahap VIP, diskaun BNB, rebat — semua ini mempengaruhi keputusan akhir.
Spread. Backtest berasaskan lilin tidak melihat spread bid-ask. Pada lilin 1 minit, penutupan = 3000, tetapi dalam realiti bid = 2999.5 dan ask = 3000.5. Setiap dagangan "berharga" separuh spread.
Sumbangan tipikal kepada perbezaan PnL: 5-15% daripada PnL tahunan.
Kesan Kumulatif
Keempat-empat kategori bertindak serentak dan, secara umum, dalam satu arah — menentang pedagang:
Jumlah perbezaan sebanyak 20-50% daripada PnL backtest adalah normal untuk sistem yang tidak diperhalusi. Dengan leverage, kesannya berganda.
Corak Seni Bina untuk Pariti
Corak 1: Teras Bersama (mengekstrak teras bersama)

Ideanya: ekstrak teras strategi — logik penjanaan isyarat dan pelaksanaan — ke dalam modul berasingan yang digunakan oleh kedua-dua backtest dan bot. Hanya infrastruktur sekeliling yang berbeza: sumber data dan mekanisme penyerahan pesanan.
┌─────────────────────────────────────┐
│ strategy_core.py │
│ ┌─────────────┐ ┌───────────────┐ │
│ │ SignalEngine │ │ OrderManager │ │
│ └──────┬──────┘ └──────┬────────┘ │
│ │ │ │
│ generate_signal() create_order()│
└─────────┬───────────────┬───────────┘
│ │
┌─────┴─────┐ ┌─────┴──────┐
│ Backtest │ │ Live │
│ DataFeed │ │ DataFeed │
│ FillModel │ │ Exchange │
└────────────┘ └────────────┘
from dataclasses import dataclass
from typing import Optional
import numpy as np
@dataclass
class Signal:
side: str # 'long' | 'short'
entry_price: float
sl_price: float
tp_price: float
size: float
timestamp: int
@dataclass
class OrderRequest:
side: str
order_type: str # 'market' | 'limit'
price: float
size: float
class StrategyCore:
"""
Teras strategi. Kod yang sama untuk backtest dan live.
Bergantung hanya pada data, bukan pada infrastruktur.
"""
def __init__(self, params: dict):
self.fast_period = params.get('fast_ma', 20)
self.slow_period = params.get('slow_ma', 50)
self.sl_pct = params.get('sl_pct', 0.02)
self.tp_pct = params.get('tp_pct', 0.04)
self.position: Optional[Signal] = None
self._closes: list[float] = []
def on_candle(self, timestamp: int, o: float, h: float,
l: float, c: float, v: float) -> Optional[OrderRequest]:
"""
Proses lilin baru. Mengembalikan OrderRequest atau None.
Kaedah ini dipanggil secara sama dari backtest dan bot.
"""
self._closes.append(c)
if len(self._closes) < self.slow_period:
return None
fast_ma = np.mean(self._closes[-self.fast_period:])
slow_ma = np.mean(self._closes[-self.slow_period:])
if self.position is not None:
exit_order = self._check_exit(h, l, c)
if exit_order:
self.position = None
return exit_order
if self.position is None:
if fast_ma > slow_ma and self._prev_fast_ma <= self._prev_slow_ma:
self.position = Signal(
side='long', entry_price=c,
sl_price=c * (1 - self.sl_pct),
tp_price=c * (1 + self.tp_pct),
size=1.0, timestamp=timestamp,
)
return OrderRequest('buy', 'market', c, 1.0)
self._prev_fast_ma = fast_ma
self._prev_slow_ma = slow_ma
return None
def _check_exit(self, high: float, low: float,
close: float) -> Optional[OrderRequest]:
pos = self.position
if pos.side == 'long':
if low <= pos.sl_price:
return OrderRequest('sell', 'market', pos.sl_price, pos.size)
if high >= pos.tp_price:
return OrderRequest('sell', 'market', pos.tp_price, pos.size)
return None
Kini backtest dan bot menggunakan StrategyCore yang sama:
from strategy_core import StrategyCore
def run_backtest(candles, params, fill_model):
core = StrategyCore(params)
trades = []
for candle in candles:
order = core.on_candle(
candle['timestamp'], candle['open'], candle['high'],
candle['low'], candle['close'], candle['volume'],
)
if order:
fill_price = fill_model.simulate_fill(order, candle)
trades.append({'price': fill_price, 'side': order.side})
return trades
from strategy_core import StrategyCore
async def run_live(exchange, symbol, params):
core = StrategyCore(params)
async for candle in exchange.stream_candles(symbol, '1m'):
order = core.on_candle(
candle['timestamp'], candle['open'], candle['high'],
candle['low'], candle['close'], candle['volume'],
)
if order:
await exchange.place_order(symbol, order.side,
order.order_type, order.size)
Peraturan utama: StrategyCore tidak mengetahui dari mana data datang atau ke mana pesanan dihantar. Ia menerima OHLCV dan mengembalikan OrderRequest. Selebihnya adalah tanggungjawab lapisan infrastruktur.
Corak 2: Penyatuan dipacu peristiwa (pendekatan NautilusTrader)

NautilusTrader melaksanakan pariti melalui NautilusKernel yang bersatu — enjin asli Rust dengan teras dipacu peristiwa deterministik dan resolusi nanosaat. Pelaksanaan strategi yang sama berfungsi dalam kedua-dua backtest dan dagangan langsung.
Seni bina dibina atas corak port dan penyesuai (seni bina heksagon):
┌──────────────────────────────────┐
│ NautilusKernel │
│ ┌───────────┐ ┌─────────────┐ │
│ │ Strategy │ │ RiskEngine │ │
│ │ (Python) │ │ (Rust) │ │
│ └─────┬─────┘ └──────┬──────┘ │
│ │ │ │
│ ┌─────┴───────────────┴──────┐ │
│ │ Message Bus (Rust) │ │
│ └─────┬───────────────┬──────┘ │
└────────┼───────────────┼─────────┘
│ │
┌─────┴─────┐ ┌─────┴──────┐
│ Backtest │ │ Live │
│ Adapter │ │ Adapter │
│ FillModel │ │ Exchange │
│ (L2 book) │ │ Gateway │
└────────────┘ └────────────┘
Kelebihan:
- Ulangan deterministik. Peristiwa diproses dalam susunan yang ditentukan dengan ketat — keputusan backtest boleh direproduksi bit demi bit.
- FillModel tersuai. Simulasi buku pesanan L2 untuk setiap pelaksanaan — slippage disimulasikan berdasarkan kedalaman buku pesanan sebenar.
- Prestasi. Sehingga 5 juta baris/saat, memproses data yang tidak muat dalam RAM.
- Redis + PostgreSQL. Cache dan bas mesej melalui Redis, kegigihan melalui PostgreSQL — infrastruktur yang sama untuk backtest dan live.
Corak 3: Antara Muka Strategi (pendekatan Freqtrade)
Freqtrade menggunakan antara muka IStrategy yang bersatu: kelas strategi yang sama berfungsi dalam kedua-dua backtest dan live. Satu-satunya perbezaan adalah lapisan kegigihan.
class IStrategy:
"""Antara muka bersatu — pelaksanaan tidak tahu sama ada ini backtest atau live."""
def populate_indicators(self, dataframe, metadata):
"""Kira penunjuk."""
dataframe['fast_ma'] = dataframe['close'].rolling(20).mean()
dataframe['slow_ma'] = dataframe['close'].rolling(50).mean()
return dataframe
def populate_entry_trend(self, dataframe, metadata):
"""Tentukan isyarat masuk."""
dataframe.loc[
(dataframe['fast_ma'] > dataframe['slow_ma']) &
(dataframe['fast_ma'].shift(1) <= dataframe['slow_ma'].shift(1)),
'enter_long'
] = 1
return dataframe
def populate_exit_trend(self, dataframe, metadata):
"""Tentukan isyarat keluar."""
dataframe.loc[
(dataframe['fast_ma'] < dataframe['slow_ma']),
'exit_long'
] = 1
return dataframe
Freqtrade turut menyediakan:
- Hyperopt melalui Optuna — pengoptimuman parameter strategi
--timeframe-detail— gerudi ke jangka masa yang lebih halus untuk penghalusan pengisian (serupa dengan gerudi adaptif)
Perbandingan Corak
| Teras Bersama | Dipacu peristiwa (NautilusTrader) | Antara Muka Strategi (Freqtrade) | |
|---|---|---|---|
| Kerumitan pelaksanaan | Rendah | Tinggi | Sederhana |
| Tahap pariti | Sederhana | Maksimum | Tinggi |
| Simulasi pengisian | FillModel berasingan | Buku pesanan L2 | --timeframe-detail |
| Bahasa teras | Python | Rust + Python | Python |
| Sesuai untuk | Enjin tersuai | Dagangan institusi | Permulaan pantas |
Ketepatan Simulasi Pengisian

Simulasi pengisian adalah sumber utama perbezaan pelaksanaan. Tiga tahap ketepatan:
Tahap 1: Naif (pengisian pada harga penutupan)
fill_price = candle['close']
Ralat: tidak mengambil kira slippage, spread, atau pengisian separa. Secara sistematik melebih-lebihkan PnL.
Tahap 2: Model slippage
def simulate_fill(order, candle, slippage_bps=5):
"""Pengisian dengan slippage."""
base_price = candle['close']
slip = base_price * slippage_bps / 10000
if order.side == 'buy':
return base_price + slip # Beli pada harga lebih tinggi
else:
return base_price - slip # Jual pada harga lebih rendah
Ralat: slippage tetap tidak mengambil kira kecairan dan saiz pesanan. Lebih baik daripada naif, tetapi masih model kasar.
Tahap 3: Gerudi adaptif dengan data 1s/100ms
Pilihan terbaik: gunakan data keberbutiran halus sebenar untuk penentuan tepat susunan pengisian SL/TP. Dihuraikan secara terperinci dalam artikel Gerudi adaptif: backtesting dengan keberbutiran berubah.
class RealisticFillModel:
"""
Model pengisian gabungan: slippage + spread + impak volum.
"""
def __init__(self, avg_spread_bps=3, impact_coeff=0.1):
self.avg_spread_bps = avg_spread_bps
self.impact_coeff = impact_coeff
def simulate_fill(self, order, candle, order_size_usd):
base_price = candle['close']
spread_cost = base_price * self.avg_spread_bps / 20000
candle_volume_usd = candle['volume'] * candle['close']
participation_rate = order_size_usd / max(candle_volume_usd, 1)
impact = base_price * self.impact_coeff * np.sqrt(participation_rate)
if order.side == 'buy':
return base_price + spread_cost + impact
else:
return base_price - spread_cost - impact
Formula impak pasaran (model Almgren-Chriss yang dipermudahkan):
di mana adalah turun naik, adalah pekali impak, adalah volum pesanan, dan adalah volum pasaran untuk tempoh tersebut.
Senarai Semak Pariti Praktikal

Sebelum melancarkan bot secara langsung, sahkan setiap item:
Kod:
- Strategi menggunakan teras bersama (satu modul untuk backtest dan live)
- Tiada pendua logik isyarat di dua tempat
- Ujian unit mengesahkan output teras yang sama untuk input yang sama
- Susunan pemeriksaan syarat adalah sama (SL sebelum TP? TP sebelum SL?)
Data:
- Format cap waktu adalah sama (UTC, pembekal yang sama)
- Pengagregatan OHLCV menggunakan peraturan yang sama
- Pengendalian lilin yang hilang adalah sama
- Tiada berat sebelah melihat ke hadapan — backtest tidak mengintip masa hadapan
Pelaksanaan:
- Model slippage dikalibrasi pada data sebenar
- Pengisian separa dimodelkan (atau sekurang-kurangnya dianggarkan secara pesimistik)
- Pesanan had mempunyai model keutamaan giliran
- Kependaman diambil kira (kelewatan 100-500 ms dari isyarat ke pengisian)
Kos:
- Komisen maker/taker dimasukkan dengan kadar semasa
- Kadar pembiayaan diambil kira dengan niaga hadapan kekal
- Spread dimodelkan (sekurang-kurangnya purata)
Infrastruktur:
- Kegigihan keadaan: bot memulihkan posisi selepas dimulakan semula
- Logik penyambungan semula: WebSocket menyambung semula tanpa kehilangan data
- Pengelogan: semua pesanan dan pengisian dilog untuk analisis pasca-mortem
Memantau Perbezaan dalam Pengeluaran
Pariti bukan pemeriksaan sekali sahaja tetapi proses berterusan. Selepas melancarkan bot, perbezaan mesti dijejaki dalam masa nyata.
Mod bayangan (dagangan kertas)

Jalankan bot selari dengan backtest pada data yang sama. Bot menjana isyarat tetapi tidak menghantar pesanan — ia hanya mencatat log. Pada masa yang sama, backtest memproses data yang sama. Bandingkan:
class DivergenceMonitor:
"""
Membandingkan isyarat backtest dan bot langsung dalam masa nyata.
"""
def __init__(self, tolerance_pct=0.5):
self.tolerance = tolerance_pct / 100
self.divergences = []
def compare_signal(self, backtest_signal, live_signal, timestamp):
"""Bandingkan isyarat backtest dan live."""
if backtest_signal is None and live_signal is None:
return # Kedua-dua senyap — OK
if (backtest_signal is None) != (live_signal is None):
self.divergences.append({
'timestamp': timestamp,
'type': 'signal_mismatch',
'backtest': backtest_signal,
'live': live_signal,
'severity': 'HIGH',
})
return
price_diff = abs(
backtest_signal.entry_price - live_signal.entry_price
) / backtest_signal.entry_price
if price_diff > self.tolerance:
self.divergences.append({
'timestamp': timestamp,
'type': 'price_divergence',
'diff_pct': price_diff * 100,
'severity': 'MEDIUM',
})
def compare_fill(self, backtest_fill, live_fill, timestamp):
"""Bandingkan pelaksanaan."""
if backtest_fill and live_fill:
slippage = (live_fill['price'] - backtest_fill['price']
) / backtest_fill['price']
self.divergences.append({
'timestamp': timestamp,
'type': 'fill_divergence',
'slippage_bps': slippage * 10000,
'severity': 'LOW' if abs(slippage) < 0.001 else 'MEDIUM',
})
def report(self):
"""Laporan perbezaan mingguan."""
from collections import Counter
severity_counts = Counter(d['severity'] for d in self.divergences)
return {
'total_divergences': len(self.divergences),
'by_severity': dict(severity_counts),
'avg_slippage_bps': np.mean([
d['slippage_bps'] for d in self.divergences
if d['type'] == 'fill_divergence'
]) if any(d['type'] == 'fill_divergence'
for d in self.divergences) else 0,
}
Metrik Papan Pemuka
| Metrik | Formula | Had Amaran |
|---|---|---|
| Kadar padanan isyarat | < 95% | |
| Slippage purata | (bps) | > 10 bps |
| Kadar pengisian | < 90% | |
| Perbezaan PnL | > 20% | |
| Kependaman p99 | Persentil ke-99 isyarat-ke-pengisian | > 500 ms |
Kalibrasi Model Slippage

Selepas mengumpul data selama 2-4 minggu, anda boleh mengkalibrasi model slippage backtest pada data sebenar:
def calibrate_slippage(live_fills: list[dict]) -> dict:
"""
Kalibrasi model slippage menggunakan pengisian sebenar.
live_fills: [{'expected_price': ..., 'actual_price': ..., 'size_usd': ..., 'volume_usd': ...}]
"""
slippages = []
participation_rates = []
for fill in live_fills:
slip = abs(fill['actual_price'] - fill['expected_price']
) / fill['expected_price']
part = fill['size_usd'] / max(fill['volume_usd'], 1)
slippages.append(slip)
participation_rates.append(part)
slippages = np.array(slippages)
participation_rates = np.array(participation_rates)
from scipy.optimize import curve_fit
def model(x, k, base):
return k * np.sqrt(x) + base
popt, _ = curve_fit(model, participation_rates, slippages,
p0=[0.1, 0.0001])
return {
'impact_coeff': popt[0],
'base_slippage': popt[1],
'mean_slippage_bps': np.mean(slippages) * 10000,
'p95_slippage_bps': np.percentile(slippages, 95) * 10000,
}
Hubungan dengan Alat Lain
Pariti backtest-live bukan tugas yang terpencil. Ia bersilang dengan alat lain dari siri "Backtest Tanpa Ilusi":
- Gerudi adaptif — meningkatkan ketepatan simulasi pengisian, komponen utama pariti pelaksanaan.
- Kadar pembiayaan — jika backtest tidak memodelkan pembiayaan, pariti mustahil pada leverage > 3x.
- Cache Parquet — jangka masa dan penunjuk yang dikira terlebih dahulu memastikan backtest melihat data yang sama seperti bot. Emulasi RunningCandleBuffer = kemas kini masa nyata.
- Polars vs Pandas — apabila beralih dari pandas (backtest) ke Polars (live), anda perlu memastikan keputusan berangka sepadan.
- Walk-Forward — walk-forward pada data luar sampel menunjukkan bagaimana strategi merosot — ini lebih hampir kepada live daripada backtest dalam sampel.
Cadangan
-
Teras bersama adalah wajib. Pangkalan kod tunggal untuk penjanaan isyarat adalah keperluan minimum untuk pariti. Dua fail dengan logik yang sama menjamin perbezaan dalam masa sebulan.
-
Kalibrasi model pengisian. Slippage tetap 5 bps lebih baik daripada tiada. Model slippage yang dikalibrasi pada data sebenar jauh lebih baik.
-
Gunakan mod bayangan selama 2-4 minggu pertama. Jangan berdagang dengan wang sebenar sehingga kadar padanan isyarat mencapai 95%+.
-
Modelkan kadar pembiayaan. Untuk niaga hadapan kekal, ini bukan pilihan — ia wajib. Pembiayaan boleh menghabiskan semua PnL pada leverage > 5x.
-
Log semua perkara. Setiap isyarat, setiap pesanan, setiap pengisian — dengan cap waktu. Tanpa log, analisis pasca-mortem mustahil.
-
Automatikkan perbandingan. Laporan DivergenceMonitor mingguan harus tiba secara automatik. Jangan tunggu sehingga PnL menjadi negatif.
-
Backtest pesimistik secara lalai. Lebih baik merendah-rendahkan jangkaan dalam backtest dan terkejut senang dalam live daripada sebaliknya. Model slippage hendaklah konservatif.
Kesimpulan

Pariti backtest-live bukan sifat sistem tetapi proses. Pariti sempurna tidak wujud: backtest secara definisinya adalah model realiti, dan model sentiasa menyederhanakan. Tetapi perbezaan antara "model berbeza sebanyak 5%" dan "model berbeza sebanyak 50%" ditentukan oleh seni bina.
Tiga tahap kematangan:
- Asas. Teras bersama, slippage tetap, komisen. Perbezaan: 10-20%.
- Lanjutan. Seni bina dipacu peristiwa, gerudi adaptif, model pembiayaan, mod bayangan. Perbezaan: 5-10%.
- Institusi. Simulasi buku pesanan L2, model impak yang dikalibrasi, pemantauan perbezaan masa nyata. Perbezaan: 2-5%.
Tugas anda adalah menentukan tahap anda berada dan memahami perbezaan yang anda anggap boleh diterima untuk saiz posisi dan leverage anda.
Pautan Berguna
- NautilusTrader — Platform Dagangan Algoritmik Berprestasi Tinggi
- Freqtrade — Bot dagangan kripto percuma, sumber terbuka
- Almgren, R., Chriss, N. — Optimal Execution of Portfolio Transactions (2001)
- Lopez de Prado — Advances in Financial Machine Learning, Bab 12: Backtesting
- Ernest Chan — Quantitative Trading: How to Build Your Own Algorithmic Trading Business
- Hexagonal Architecture (Ports and Adapters) — Alistair Cockburn
- Optuna — Rangka Kerja Pengoptimuman Hiperparameter
Rujukan
@article{soloviov2026backtestliveparity,
author = {Soloviov, Eugen},
title = {Backtest-live parity: why your bot trades differently from the backtest},
year = {2026},
url = {https://marketmaker.cc/ru/blog/post/backtest-live-parity},
description = {Taksonomi lengkap perbezaan antara backtesting dan dagangan langsung: daripada slippage dan pengisian separa hingga penyahsegerakan pangkalan kod. Corak seni bina untuk mencapai pariti dan senarai semak pemantauan pengeluaran.}
}
Pengarang
Trading-systems engineer
Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.