← Maqolalarga qaytish
March 7, 2026
5 daqiqa o'qish

Backtest-live paritet: nima uchun botingiz backtestdan farqli savdo qiladi

#algotrading
#backtest
#live trading
#backtest-live parity
#execution
#NautilusTrader
🎯
Part 8 of 9 · Collection
Backtesting Without Fooling Yourself

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

Backtest va live savdo divergensiyasi — ideal equity egri chizig'i real volatil natijalarga qarama-qarshi

Har bir algotreyder ushbu tsikldan o'tadi:

  1. Jupyter notebook'da strategiya yozdi
  2. Tarixiy CSV bo'yicha backtest o'tkazdi — natijalar ajoyib
  3. Mantiqni bot sifatida qayta yozdi (ko'pincha boshqa til yoki freymvorkda)
  4. Ishga tushirdi — natijalar mos kelmaydi
  5. 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

Backtest-live 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)

Order bajarilish divergensiyasi — orderbook slippagei, latensiya va qisman bajarilish vizualizatsiyasi

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.

NN savdo bo'yicha kumulyativ slippage formulasi:

Slippagetotal=i=1Nsizei×si\text{Slippage}_{total} = \sum_{i=1}^{N} \text{size}_i \times s_i

bu yerda sis_iii-savdoning slippagei, orderbook chuqurligiga bog'liq:

sisizeiLiquidity(ti)×ks_i \approx \frac{\text{size}_i}{\text{Liquidity}(t_i)} \times k

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 0.01%×3×365×10=109.5%0.01\% \times 3 \times 365 \times 10 = 109.5\% 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:

PnLlivePnLbacktestΔdataΔexecutionΔlogicΔcosts\text{PnL}_{live} \approx \text{PnL}_{backtest} - \Delta_{data} - \Delta_{execution} - \Delta_{logic} - \Delta_{costs}

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)

Shared Core arxitekturasi — backtest va live savdo dvigatellarini ta'minlovchi yagona strategiya moduli

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)

Kaskadli event pipeline bilan event-driven savdo arxitekturasi — bozor ma'lumotlari, signallar, order'lar, bajarilishlar

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 aniqlik darajalari

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):

Δp=σkVorderVmarket\Delta p = \sigma \cdot k \cdot \sqrt{\frac{V_{order}}{V_{market}}}

bu yerda σ\sigma — volatillik, kk — ta'sir koeffitsienti, VorderV_{order} — order hajmi, VmarketV_{market} — davr uchun bozor hajmi.

Amaliy Paritet Cheklisti

Toifalar bo'yicha tashkillashtirilgan gologramma paritet tekshiruvi cheklisti — ma'lumotlar, bajarilish, vaqt, komissiyalar

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)

Shadow savdo rejimi — bir vaqtda ishlayotgan real bozor ma'lumotlari va simulyatsiya qilingan order'lar

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 matchestotal signals\frac{\text{matches}}{\text{total signals}} < 95%
O'rtacha slippage 1Nsi\frac{1}{N}\sum s_i (bps) > 10 bps
Bajarilish darajasi filledsent\frac{\text{filled}}{\text{sent}} < 90%
PnL divergensiyasi PnLlivePnLbtPnLbt\frac{PnL_{live} - PnL_{bt}}{PnL_{bt}} > 20%
Latensiya p99 99-foizli signal-dan-bajarilishgacha > 500 ms

Slippage Modelini Kalibrlash

Slippage modelini kalibrlash — kutilgan va real bajarilishlarni ko'rsatuvchi narx ta'siri egri chizig'i bilan orderbook chuqurligi

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

  1. Shared core majburiy. Signal generatsiyasi uchun yagona kod bazasi — paritet uchun minimal talab. Bir xil mantiqqa ega ikkita fayl bir oy ichida divergensiyani kafolatlaydi.

  2. Bajarilish modelini kalibrlang. Qat'iy 5 bps slippage hech narsadan yaxshiroq. Real ma'lumotlar bo'yicha kalibrlangan slippage modeli sezilarli darajada yaxshiroq.

  3. Birinchi 2-4 hafta uchun shadow rejimdan foydalaning. Signal moslik darajasi 95%+ ga yetguncha real pul bilan savdo qilmang.

  4. Funding stavkalarini modellashtiring. Perpetual fyucherslar uchun bu ixtiyoriy emas — majburiy. Funding 5x'dan yuqori leverage'da barcha PnL'ni yeb qo'yishi mumkin.

  5. Hamma narsani loglang. Har bir signal, har bir order, har bir bajarilish — vaqt belgisi bilan. Logsiz post-mortem tahlil imkonsiz.

  6. Solishtirishni avtomatlashtiring. Haftalik DivergenceMonitor hisoboti avtomatik kelishi kerak. PnL manfiy bo'lguncha kutmang.

  7. Standart bo'yicha pessimistik backtest. Backtestda kutishlarni kam baholab, live'da yoqimli hayratlanish, aksincha bo'lgandan yaxshiroq. Slippage modeli konservativ bo'lishi kerak.

Xulosa

Savdo tizimi yetuklik darajalari — asosiy backtestingdan to'liq production'gacha

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:

  1. Asosiy. Shared core, qat'iy slippage, komissiyalar. Divergensiya: 10-20%.
  2. Ilg'or. Event-driven arxitektura, adaptiv chuqurlashish, funding modeli, shadow rejim. Divergensiya: 5-10%.
  3. 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

  1. NautilusTrader — High-Performance Algorithmic Trading Platform
  2. Freqtrade — Free, open source crypto trading bot
  3. Almgren, R., Chriss, N. — Optimal Execution of Portfolio Transactions (2001)
  4. Lopez de Prado — Advances in Financial Machine Learning, Chapter 12: Backtesting
  5. Ernest Chan — Quantitative Trading: How to Build Your Own Algorithmic Trading Business
  6. Hexagonal Architecture (Ports and Adapters) — Alistair Cockburn
  7. 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.}
}
blog.disclaimer

Authors

Eugen Soloviov
Eugen Soloviov

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.

Newsletter

Bozordan bir qadam oldinda bo'ling

Sun'iy intellekt savdo tahlillari, bozor tahlili va platforma yangiliklari uchun bizning xabarnomaga obuna bo'ling.

Biz sizning maxfiyligingizni hurmat qilamiz. Istalgan vaqtda obunadan chiqishingiz mumkin.