Backtest-live paritet: nima uchun botingiz backtestdan farqli savdo qiladi
Siz strategiyani backtest orqali o'tkazdingiz. Sharpe 2.1, MaxDD -8%, PnL +67%. Botni ishga tushirdingiz. Bir oydan so'ng solishtirasiz: signallar bir xil, davr bir xil — lekin live PnL 40% past. Drawdown bir yarim marta chuqurroq. O'n savdodan ikkitasi umuman bajarilmagan.
Bu xato emas. Bu backtest-live divergensiya — backtest natijalari va real savdo o'rtasidagi tizimli nomuvofiqlik. Bu hamma botlarda mavjud. Yagona savol — siz bu haqda bilasizmi va uni nazorat qila olasizmi.
Ushbu maqolada divergensiyalarning to'liq taksonomiyasi, ularni minimallashtirish uchun arxitektura patternlari va production'da paritetni monitoring qilish uchun amaliy cheklist keltirilgan.
"Backtestda ishladi" sindromi

Har bir algotreyder ushbu tsikldan o'tadi:
- Jupyter notebook'da strategiya yozdi
- Tarixiy CSV bo'yicha backtest o'tkazdi — natijalar ajoyib
- Mantiqni bot sifatida qayta yozdi (ko'pincha boshqa til yoki freymvorkda)
- Ishga tushirdi — natijalar mos kelmaydi
- Xato qidira boshladi, topmadi — "bozor o'zgardi"
Muammo bozorda emas. Muammo shundaki, backtest va bot bir xil haqiqatni turlicha modellashtiradigan ikkita alohida dasturiy mahsulot. Divergensiyalar muqarrar, lekin ularni tizimlashtirish va minimallashtirish mumkin.
Divergensiyalar Taksonomiyasi

Divergensiyaning barcha manbalari to'rtta toifaga bo'linadi. Har biri uchun — jiddiylik reytingi (1 dan 5 gacha) va PnL divergensiyasiga odatiy hissa.
1. Ma'lumotlar divergensiyasi (jiddiylik: 3/5)
Backtest ko'radigan ma'lumotlar va bot real vaqtda ko'radigan ma'lumotlar bir xil narsa emas.
Vaqt belgilari (timestamps). Birjalar shamlarga vaqt belgisini turlicha qoidalar bilan beradi. Bir birja shamni davr boshi bilan belgilaydi, boshqasi — oxiri bilan. REST API real yopilishdan 1-3 soniya kechikish bilan shamni qaytarishi mumkin. Backtest tarixiy fayldan "ideal" vaqt belgilari bilan ishlaydi.
OHLCV agregatsiyasi. Tarixiy ma'lumotlar provayder tomonidan birja real vaqtda qiladigandan ko'pincha boshqacha agregatlanadi. Farq oxirgi raqamda — lekin chegara signallari bilan (MA kesishuvi, daraja buzilishi) bu strategiya pozitsiyaga kirish-kirmasligini belgilaydi.
Bo'shliqlar va yo'qolgan ma'lumotlar. Tarixiy ma'lumotlar odatda toza — yo'qolgan shamlar interpolyatsiya bilan to'ldiriladi. Real vaqtda WebSocket uzilishi mumkin, va bot 30 soniyalik ma'lumotni o'tkazib yuboradi.
PnL divergensiyasiga odatiy hissa: yillik PnL'ning 2-5%.
2. Bajarilish divergensiyasi (jiddiylik: 5/5)

Divergensiyalarning eng xavfli sinfi. Backtest bajarilishni mukammal simulyatsiya qiladi — haqiqat esa idealdan uzoq.
Slippage. Backtest order'ni yopilish narxida (yoki signal narxida) bajaradi. Haqiqatda market order eng yaxshi bid/ask plyus hajm va likvidlikka bog'liq slippage bilan bajariladi. O'rtacha likvidlikdagi altkoin bo'yicha $10K pozitsiya uchun slippage 0.05-0.3% bo'lishi mumkin.
savdo bo'yicha kumulyativ slippage formulasi:
bu yerda — -savdoning slippagei, orderbook chuqurligiga bog'liq:
Latensiya. Signal generatsiyasidan order bajarilishigacha vaqt o'tadi: signalni hisoblash (1-50 ms), so'rovni uzatish (10-200 ms), birjada matching (1-10 ms). Backtestda latensiya = 0. Live'da esa — narx harakatlanishi mumkin.
Qisman bajarilish. Backtest order'ning 100% zudlik bilan bajarilishini taxmin qiladi. Haqiqatda limit order qisman bajarilishi mumkin — yoki narx qaytsa umuman bajarilmasligi mumkin. Likvid bo'lmagan bozorda market order bir necha orderbook darajasi bo'ylab "sirg'anadi".
Navbat ustuvorligi. Eng yaxshi bid narxida qo'yilgan limit order darhol bajarilmaydi — u shu darajada avval qo'yilgan barcha order'lar orqasida navbatga turadi. "Narx tegdi = order bajarildi" deb hisoblovchi backtest bajarilish darajasini tizimli ravishda oshirib ko'rsatadi.
PnL divergensiyasiga odatiy hissa: yillik PnL'ning 10-30%.
3. Mantiq divergensiyasi (jiddiylik: 4/5)
Bu backtest va bot o'rtasidagi strategiya kodining o'zidagi divergensiyalar.
Alohida kod bazalari. Klassik anti-pattern: backtests/strategy_a.py va bot/strategy_a.py — "bir xil ishni bajaruvchi" ikkita alohida fayl. Uch oylik tahrirlardan so'ng ular muqarrar ravishda farqlanadi. Kimdir backtestga filtr qo'shdi va botga takrorlashni unutdi. Yoki aksincha — botda xato tuzatildi, lekin backtestda qoldi.
Turli freymvorklar. Backtest pandas'da vektorlashtirilgan operatsiyalar bilan, bot esa asyncio'da event-driven mantiq bilan. Bir xil strategiyada ham chegara holatlari turlicha ishlanadi: yaxlitlash, shartlarni tekshirish tartibi, NaN'ni qayta ishlash.
Holat boshqaruvi. Backtest odatda stateless — u ma'lumotlar massivini iteratsiya qiladi. Bot esa stateful — u pozitsiyalarni, balanslarni, order tarixini saqlaydi. Bot qayta ishga tushishi, holat yo'qolishi, birja bilan desinxronizatsiya — bularning barchasi divergensiya manbalaridir.
PnL divergensiyasiga odatiy hissa: yillik PnL'ning 5-20%.
4. Xarajatlar divergensiyasi (jiddiylik: 3/5)
Savdo xarajatlarini modellashtirishdagi divergensiyalar.
Funding stavkalari. Ko'pgina perpetual fyucherslar backtestlari funding stavkalarini umuman hisobga olmaydi. 10x leverage va o'rtacha 8 soatda 0.01% stavkada bu yiliga — ko'pgina strategiyalarning PnL'idan ko'proq. Batafsil tahlil Funding stavkalari sizning leverage'ingizni o'ldiradi maqolasida.
Komissiyalar. Maker/taker komissiyalari odatda modellashtiriladi, lekin ko'pincha noto'g'ri stavka bilan. VIP darajalar, BNB chegirmalari, rebate'lar — bularning barchasi yakuniy natijaga ta'sir qiladi.
Spred. Shamga asoslangan backtest bid-ask spredini ko'rmaydi. 1 daqiqalik shamda close = 3000, lekin haqiqatda bid = 2999.5 va ask = 3000.5. Har bir savdo spredning yarmiga "tushadi".
PnL divergensiyasiga odatiy hissa: yillik PnL'ning 5-15%.
Kumulyativ Effekt
Barcha to'rtta toifa bir vaqtda va, qoida tariqasida, bitta yo'nalishda — treyder zarariga ta'sir qiladi:
Yaxshilanmagan tizim uchun backtest PnL'idan 20-50% umumiy divergensiya normaldir. Leverage bilan effekt ko'paytiriladi.
Paritet uchun Arxitektura Patternlari
Pattern 1: Shared Core (umumiy yadroni ajratib olish)

G'oya: strategiya yadrosini — signal generatsiyasi va bajarish mantig'ini — ham backtest, ham bot tomonidan ishlatiladigan alohida modulga ajratib olish. Faqat atrofdagi infratuzilma farq qiladi: ma'lumotlar manbai va order yuborish mexanizmi.
┌─────────────────────────────────────┐
│ 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:
"""
Strategiya yadrosi. Backtest va live uchun bir xil kod.
Faqat ma'lumotlarga bog'liq, infratuzilmaga emas.
"""
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]:
"""
Yangi shamni qayta ishlash. OrderRequest yoki None qaytaradi.
Ushbu metod backtest va botdan bir xil chaqiriladi.
"""
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
Endi backtest va bot bir xil StrategyCore'dan foydalanadi:
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)
Asosiy qoida: StrategyCore ma'lumotlar qayerdan kelishini yoki order'lar qayerga yuborilishini bilmaydi. U OHLCV qabul qiladi va OrderRequest qaytaradi. Qolgan hamma narsa infratuzilma qatlamining mas'uliyatidir.
Pattern 2: Event-driven unifikatsiya (NautilusTrader yondashuvi)

NautilusTrader paritetni yagona NautilusKernel — deterministik event-driven yadro va nanosekund aniqligiga ega Rust-native dvigatel orqali amalga oshiradi. Bir xil strategiya implementatsiyasi ham backtestda, ham live savdoda ishlaydi.
Arxitektura ports and adapters patterni (geksagonal arxitektura) asosida qurilgan:
┌──────────────────────────────────┐
│ NautilusKernel │
│ ┌───────────┐ ┌─────────────┐ │
│ │ Strategy │ │ RiskEngine │ │
│ │ (Python) │ │ (Rust) │ │
│ └─────┬─────┘ └──────┬──────┘ │
│ │ │ │
│ ┌─────┴───────────────┴──────┐ │
│ │ Message Bus (Rust) │ │
│ └─────┬───────────────┬──────┘ │
└────────┼───────────────┼─────────┘
│ │
┌─────┴─────┐ ┌─────┴──────┐
│ Backtest │ │ Live │
│ Adapter │ │ Adapter │
│ FillModel │ │ Exchange │
│ (L2 book) │ │ Gateway │
└────────────┘ └────────────┘
Afzalliklari:
- Deterministik takror ijro (replay). Event'lar qat'iy belgilangan tartibda qayta ishlanadi — backtest natijasi bit darajasida takrorlanuvchan.
- Custom FillModel. Har bir bajarilish uchun L2 orderbook simulyatsiyasi — slippage real orderbook chuqurligi asosida modellashtiriladi.
- Unumdorlik. Sekundiga 5 million qatorgacha, RAM'ga sig'maydigan ma'lumotlarni qayta ishlash.
- Redis + PostgreSQL. Redis orqali kesh va message bus, PostgreSQL orqali persistentlik — backtest va live uchun bir xil infratuzilma.
Pattern 3: Strategy Interface (Freqtrade yondashuvi)
Freqtrade yagona IStrategy interfeysidan foydalanadi: bir xil strategiya klassi ham backtestda, ham live'da ishlaydi. Yagona farq — persistentlik qatlami.
class IStrategy:
"""Yagona interfeys — implementatsiya bu backtest yoki live ekanini bilmaydi."""
def populate_indicators(self, dataframe, metadata):
"""Indikatorlarni hisoblash."""
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):
"""Kirish signallarini aniqlash."""
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):
"""Chiqish signallarini aniqlash."""
dataframe.loc[
(dataframe['fast_ma'] < dataframe['slow_ma']),
'exit_long'
] = 1
return dataframe
Freqtrade qo'shimcha ravishda quyidagilarni taqdim etadi:
- Optuna orqali Hyperopt — strategiya parametrlarini optimallashtirish
--timeframe-detail— bajarilishni aniqlashtirish uchun mayda vaqt oralig'iga chuqurlashish (adaptiv chuqurlashishga o'xshash)
Patternlarni Solishtirish
| Shared Core | Event-driven (NautilusTrader) | Strategy Interface (Freqtrade) | |
|---|---|---|---|
| Implementatsiya murakkabligi | Past | Yuqori | O'rtacha |
| Paritet darajasi | O'rtacha | Maksimal | Yuqori |
| Bajarilish simulyatsiyasi | Alohida FillModel | L2 orderbook | --timeframe-detail |
| Yadro tili | Python | Rust + Python | Python |
| Mos keladi | Custom dvigatellar | Institutsional savdo | Tezkor boshlash |
Bajarilish Simulyatsiyasi Aniqligi

Bajarilish simulyatsiyasi bajarish divergensiyasining asosiy manbaidir. Aniqlikning uchta darajasi:
Daraja 1: Naiv (yopilish narxida bajarish)
fill_price = candle['close']
Xato: slippage, spred yoki qisman bajarilishni hisobga olmaydi. PnL'ni tizimli ravishda oshirib ko'rsatadi.
Daraja 2: Slippage modeli
def simulate_fill(order, candle, slippage_bps=5):
"""Slippage bilan bajarish."""
base_price = candle['close']
slip = base_price * slippage_bps / 10000
if order.side == 'buy':
return base_price + slip # Yuqoriroq narxda sotib olish
else:
return base_price - slip # Pastroq narxda sotish
Xato: qat'iy slippage likvidlik va order hajmini hisobga olmaydi. Naivdan yaxshiroq, lekin baribir qo'pol model.
Daraja 3: 1s/100ms ma'lumotlar bilan adaptiv chuqurlashish
Eng yaxshi variant: SL/TP bajarilish tartibini aniq belgilash uchun real mayda granulyarlikdagi ma'lumotlardan foydalanish. Batafsil Adaptiv chuqurlashish: o'zgaruvchan granulyarlik bilan backtest qilish maqolasida tasvirlangan.
class RealisticFillModel:
"""
Birlashtirilgan bajarilish modeli: slippage + spred + hajm ta'siri.
"""
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
Bozor ta'siri formulasi (soddalashtirilgan Almgren-Chriss modeli):
bu yerda — volatillik, — ta'sir koeffitsienti, — order hajmi, — davr uchun bozor hajmi.
Amaliy Paritet Cheklisti

Botni live'ga ishga tushirishdan oldin har bir bandni tekshiring:
Kod:
- Strategiya umumiy yadrodan foydalanadi (backtest va live uchun bitta modul)
- Signal mantig'i ikki joyda dublyor qilinmagan
- Unit testlar bir xil kirishlar uchun bir xil yadro chiqishlarini tekshiradi
- Shartlarni tekshirish tartibi bir xil (SL TP'dan oldinmi? TP SL'dan oldinmi?)
Ma'lumotlar:
- Vaqt belgisi formati bir xil (UTC, bir xil provayder)
- OHLCV agregatsiyasi bir xil qoidalardan foydalanadi
- Yo'qolgan shamlarni qayta ishlash bir xil
- Look-ahead bias yo'q — backtest kelajakka qaramaydi
Bajarilish:
- Slippage modeli real ma'lumotlar bo'yicha kalibrlangan
- Qisman bajarilishlar modellashtirilgan (yoki hech bo'lmaganda pessimistik baholangan)
- Limit order'larda navbat ustuvorligi modeli mavjud
- Latensiya hisobga olingan (signal'dan bajarilishgacha 100-500 ms kechikish)
Xarajatlar:
- Maker/taker komissiyalari joriy stavka bilan kiritilgan
- Perpetual fyucherslarda funding stavkalari hisobga olingan
- Spred modellashtirilgan (hech bo'lmaganda o'rtacha)
Infratuzilma:
- Holat persistentligi: bot qayta ishga tushgandan so'ng pozitsiyalarni tiklaydi
- Qayta ulanish mantig'i: WebSocket ma'lumot yo'qotmasdan qayta ulanadi
- Logging: barcha order'lar va bajarilishlar post-mortem tahlil uchun loglanadi
Production'da Divergensiyani Monitoring Qilish
Paritet bir martalik tekshiruv emas, balki uzluksiz jarayon. Botni ishga tushirgandan so'ng divergensiyalar real vaqtda kuzatilishi kerak.
Shadow rejim (paper trading)

Botni bir xil ma'lumotlar bo'yicha backtest bilan parallel ishga tushiring. Bot signallarni generatsiya qiladi, lekin order yubormaydi — faqat loglaydi. Bir vaqtda backtest bir xil ma'lumotlarni qayta ishlaydi. Solishtiring:
class DivergenceMonitor:
"""
Backtest va live bot signallarini real vaqtda solishtiradi.
"""
def __init__(self, tolerance_pct=0.5):
self.tolerance = tolerance_pct / 100
self.divergences = []
def compare_signal(self, backtest_signal, live_signal, timestamp):
"""Backtest va live signallarni solishtirish."""
if backtest_signal is None and live_signal is None:
return # Ikkalasi ham jim — 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):
"""Bajarilishni solishtirish."""
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):
"""Haftalik divergensiya hisoboti."""
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,
}
Dashboard Metrikalari
| Metrika | Formula | Signal chegarasi |
|---|---|---|
| Signal moslik darajasi | < 95% | |
| O'rtacha slippage | (bps) | > 10 bps |
| Bajarilish darajasi | < 90% | |
| PnL divergensiyasi | > 20% | |
| Latensiya p99 | 99-foizli signal-dan-bajarilishgacha | > 500 ms |
Slippage Modelini Kalibrlash

2-4 haftalik ma'lumot to'plangandan so'ng, backtest slippage modelini real ma'lumotlar bo'yicha kalibrlash mumkin:
def calibrate_slippage(live_fills: list[dict]) -> dict:
"""
Real bajarilishlar yordamida slippage modelini kalibrlash.
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,
}
Boshqa Vositalar bilan Bog'liqlik
Backtest-live paritet alohida vazifa emas. U "Illyuziyalarsiz Backtestlar" seriyasidagi boshqa vositalar bilan kesishadi:
- Adaptiv chuqurlashish — bajarilish simulyatsiyasi aniqligini yaxshilaydi, bu bajarish paritetining asosiy komponenti.
- Funding stavkalari — agar backtest fundingni modellashtirsa, 3x'dan yuqori leverage'da paritet imkonsiz.
- Parquet kesh — oldindan hisoblangan vaqt oraliqlari va indikatorlar backtest botning ko'rgani bilan bir xil ma'lumotni ko'rishini ta'minlaydi. RunningCandleBuffer emulyatsiyasi = real vaqtda yangilanish.
- Polars vs Pandas — pandas'dan (backtest) Polars'ga (live) o'tishda raqamli natijalarning mos kelishini ta'minlash kerak.
- Walk-Forward — out-of-sample ma'lumotlar bo'yicha walk-forward strategiyaning qanday degradatsiyalanishini ko'rsatadi — bu in-sample backtestga qaraganda live'ga yaqinroq.
Tavsiyalar
-
Shared core majburiy. Signal generatsiyasi uchun yagona kod bazasi — paritet uchun minimal talab. Bir xil mantiqqa ega ikkita fayl bir oy ichida divergensiyani kafolatlaydi.
-
Bajarilish modelini kalibrlang. Qat'iy 5 bps slippage hech narsadan yaxshiroq. Real ma'lumotlar bo'yicha kalibrlangan slippage modeli sezilarli darajada yaxshiroq.
-
Birinchi 2-4 hafta uchun shadow rejimdan foydalaning. Signal moslik darajasi 95%+ ga yetguncha real pul bilan savdo qilmang.
-
Funding stavkalarini modellashtiring. Perpetual fyucherslar uchun bu ixtiyoriy emas — majburiy. Funding 5x'dan yuqori leverage'da barcha PnL'ni yeb qo'yishi mumkin.
-
Hamma narsani loglang. Har bir signal, har bir order, har bir bajarilish — vaqt belgisi bilan. Logsiz post-mortem tahlil imkonsiz.
-
Solishtirishni avtomatlashtiring. Haftalik DivergenceMonitor hisoboti avtomatik kelishi kerak. PnL manfiy bo'lguncha kutmang.
-
Standart bo'yicha pessimistik backtest. Backtestda kutishlarni kam baholab, live'da yoqimli hayratlanish, aksincha bo'lgandan yaxshiroq. Slippage modeli konservativ bo'lishi kerak.
Xulosa

Backtest-live paritet tizimning xususiyati emas, balki jarayon. Mukammal paritet mavjud emas: backtest ta'rifi bo'yicha haqiqatning modeli, va model doim soddalashtiradi. Lekin "model 5% farq qiladi" va "model 50% farq qiladi" o'rtasidagi farq arxitektura bilan belgilanadi.
Yetuklikning uchta darajasi:
- Asosiy. Shared core, qat'iy slippage, komissiyalar. Divergensiya: 10-20%.
- Ilg'or. Event-driven arxitektura, adaptiv chuqurlashish, funding modeli, shadow rejim. Divergensiya: 5-10%.
- Institutsional. L2 orderbook simulyatsiyasi, kalibrlangan ta'sir modeli, real vaqtda divergensiya monitoring. Divergensiya: 2-5%.
Sizning vazifangiz — qaysi darajada ekanligingizni aniqlash va pozitsiya hajmi hamda leverage'ingiz uchun qanday divergensiyani maqbul deb hisoblashingizni tushunish.
Foydali Havolalar
- NautilusTrader — High-Performance Algorithmic Trading Platform
- Freqtrade — Free, open source crypto trading bot
- Almgren, R., Chriss, N. — Optimal Execution of Portfolio Transactions (2001)
- Lopez de Prado — Advances in Financial Machine Learning, Chapter 12: Backtesting
- Ernest Chan — Quantitative Trading: How to Build Your Own Algorithmic Trading Business
- Hexagonal Architecture (Ports and Adapters) — Alistair Cockburn
- Optuna — Hyperparameter Optimization Framework
Iqtibos
@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 = {Complete taxonomy of divergences between backtesting and live trading: from slippage and partial fills to codebase desynchronization. Architectural patterns for achieving parity and a production monitoring checklist.}
}
Authors
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.