Бэктест-лайв паритеті: боттың бэктестен неге басқаша сауда жасайды
Сіз стратегияны бэктест арқылы өткіздіңіз. Sharpe 2.1, MaxDD -8%, PnL +67%. Ботты іске қостыңыз. Бір айдан кейін салыстырасыз: сол сигналдар, сол кезең — бірақ нақты PnL 40%-ға төмен. Просадка бір жарым есе тереңірек. Он мәмілеге екеуі мүлдем орындалмады.
Бұл баг емес. Бұл — бэктест-лайв алшақтығы, яғни бэктест нәтижелері мен нақты саудадағы жүйелі алшақтық. Бұл барлығында бар. Жалғыз сұрақ — сіз бұны білесіз бе және оны бақылай аласыз ба.
Бұл мақала алшақтықтардың толық таксономиясын, оларды азайтуға арналған архитектуралық үлгілерді және өндірісте паритетті бақылауға арналған практикалық чеклистті ұсынады.
"бэктесте жұмыс істеді" синдромы

Әрбір algotrader мына циклден өтеді:
- Jupyter notebook-та стратегия жазды
- Тарихи CSV бойынша бэктест жүргізді — нәтижелер керемет
- Логиканы бот түрінде қайта жазды (жиі басқа тілде немесе фреймворкта)
- Іске қосты — нәтижелер сәйкес келмейді
- Багты іздеді, таппады — "нарық өзгерді"
Мәселе нарықта емес. Мәселе — бэктест пен бот екі түрлі бағдарламалық өнім, олар бір реалдылықты әртүрлі модельдейді. Алшақтықтар сөзсіз, бірақ оларды жүйелеуге және азайтуға болады.
Алшақтықтар таксономиясы

Алшақтықтың барлық көздері төрт санатқа бөлінеді. Әрқайсысы үшін — ауырлық дәрежесі (1-ден 5-ке дейін) және PnL алшақтығына тән үлес.
1. Деректер алшақтығы (ауырлығы: 3/5)
Бэктест көретін деректер мен бот нақты уақытта көретін деректер бірдей емес.
Уақыт белгілері. Биржалар кэндлдерді уақыт белгісін тағайындаудың әртүрлі ережелерімен жеткізеді. Бір биржа кэндлді кезеңнің басымен, екіншісі соңымен белгілейді. REST API нақты жабылудан 1-3 секунд кейін кэндл қайтаруы мүмкін. Бэктест тарихи файлдан алынған "идеалды" уақыт белгілерімен жұмыс істейді.
OHLCV агрегациясы. Тарихи деректерді провайдер жиі биржа нақты уақытта жасайтын тәсілден өзгеше агрегаттайды. Айырмашылық соңғы санда — бірақ шекті сигналдарда (MA қиылысуы, деңгей бұзылуы) бұл стратегия позицияға кіру-кірмеуін анықтайды.
Олқылықтар мен жетіспейтін деректер. Тарихи деректер әдетте таза — жоқ кэндлдер интерполяциямен толтырылады. Нақты уақытта WebSocket үзілуі мүмкін, бот 30 секунд деректерін жіберіп алады.
PnL алшақтығына тән үлес: жылдық PnL-дің 2-5%.
2. Орындау алшақтығы (ауырлығы: 5/5)

Алшақтықтың ең қауіпті класы. Бэктест орындауды тамаша модельдейді — реалдылық идеалдан алыс.
Слиппедж. Бэктест бұйрықты жабылу бағасымен (немесе сигнал бағасымен) толтырады. Шын мәнінде, нарықтық бұйрық көлем мен өтімділікке байланысты слиппеджі бар ең жақсы bid/ask бойынша орындалады. Орташа өтімділігі бар альткоинде $10K позиция үшін слиппедж 0.05-0.3% болуы мүмкін.
мәміле бойынша жинақталған слиппедж формуласы:
мұндағы — ордербук тереңдігіне байланысты -ші мәміленің слиппеджі:
Кідіріс. Сигнал жасалған сәттен бұйрықты орындауға дейін уақыт өтеді: сигналды есептеу (1-50 мс), сұранысты жіберу (10-200 мс), биржада сәйкестендіру (1-10 мс). Бэктесте кідіріс = 0. Лайвта баға өзгеруі мүмкін.
Ішінара орындау. Бэктест бұйрықтың 100%-ы бірден толтырылады деп есептейді. Шын мәнінде, лимитті бұйрық ішінара толтырылуы мүмкін — немесе баға кері бағытта жүрсе мүлдем толтырылмайды. Өтімсіз нарықтағы нарықтық бұйрық үшін ол бірнеше ордербук деңгейлерінен "сырғанайды".
Кезек басымдығы. Ең жақсы bid бағасына қойылған лимитті бұйрық бірден толтырылмайды — ол осы деңгейде бұрын қойылған барлық бұйрықтардың артынан кезекке тұрады. "Баға тиді = бұйрық толтырылды" деп есептейтін бэктест толтыру мөлшерін жүйелі түрде асыра бағалайды.
PnL алшақтығына тән үлес: жылдық PnL-дің 10-30%.
3. Логика алшақтығы (ауырлығы: 4/5)
Бұл бэктест пен бот арасындағы стратегия кодының өзіндегі алшақтықтар.
Бөлек кодтық базалар. Классикалық антиүлгі: backtests/strategy_a.py және bot/strategy_a.py — "бір нәрсе жасайтын" екі бөлек файл. Үш ай өзгертулерден кейін олар сөзсіз алшақтайды. Біреу бэктестке фильтр қосып, ботта оны қайталауды ұмытып кетті. Немесе керісінше — ботта баг түзетілді, бірақ бэктесте қалды.
Әртүрлі фреймворктар. Векторланған операциялары бар pandas-та бэктест, оқиғаға негізделген логикасы бар asyncio-да бот. Тіпті бірдей стратегияда шекаралық жағдайлар әртүрлі өңделеді: дөңгелектеу, шарттарды тексеру реті, NaN-ды өңдеу.
Күй басқару. Бэктест әдетте күйсіз — ол деректер массиві бойынша итерациялайды. Бот күйлі — ол позицияларды, баланстарды, бұйрықтар тарихын сақтайды. Ботты қайта іске қосу, күйдің жоғалуы, биржамен десинхронизация — бұлардың барлығы алшақтық көздері.
PnL алшақтығына тән үлес: жылдық PnL-дің 5-20%.
4. Шығындар алшақтығы (ауырлығы: 3/5)
Сауда шығындарын модельдеудегі алшақтықтар.
Фандинг мөлшерлемелері. Мәңгілік фьючерстердің көпшілігінің бэктестері фандинг мөлшерлемелерін мүлдем есепке алмайды. 10x левередж және 8 сағат сайын орташа 0.01% мөлшерлемесі кезінде бұл жылына құрайды — стратегиялардың көпшілігінің PnL-нен көп. Толық талдау Фандинг мөлшерлемелері левереджіңізді өлтіреді мақаласында.
Комиссиялар. Maker/taker комиссиялары әдетте модельденеді, бірақ жиі қате мөлшерлемемен. VIP деңгейлер, BNB жеңілдіктері, rebate-тер — бұлардың барлығы соңғы нәтижеге әсер етеді.
Спред. Кэндлге негізделген бэктест bid-ask спредін көрмейді. 1 минуттық кэндлде close = 3000, бірақ шындығында bid = 2999.5 және ask = 3000.5. Әрбір мәміле спредтің жартысы "тұрады".
PnL алшақтығына тән үлес: жылдық PnL-дің 5-15%.
Жинақталған әсер
Барлық төрт санат бір мезгілде және әдетте бір бағытта — трейдерге қарсы әрекет етеді:
Тазаланбаған жүйе үшін бэктест PnL-нен 20-50% жалпы алшақтық қалыпты жағдай. Левереджбен әсер еселенеді.
Паритет үшін архитектуралық үлгілер
1-үлгі: Shared Core (ортақ ядроны бөлектеу)

Идея: стратегия ядросын — сигнал генерациясы мен орындау логикасын — бэктест те, бот та пайдаланатын бөлек модульге бөлектеу. Тек айналасындағы инфрақұрылым өзгеше: деректер көзі және бұйрықтарды жіберу механизмі.
┌─────────────────────────────────────┐
│ 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:
"""
Strategy core. Identical code for backtest and live.
Depends only on data, not on infrastructure.
"""
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]:
"""
Process a new candle. Returns an OrderRequest or None.
This method is called identically from the backtest and the 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
Енді бэктест пен бот бір StrategyCore-ды пайдаланады:
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)
Негізгі ереже: StrategyCore деректер қайдан келетінін немесе бұйрықтар қайда жіберілетінін білмейді. Ол OHLCV алады және OrderRequest қайтарады. Қалғанының бәрі инфрақұрылым қабатының жауапкершілігі.
2-үлгі: Оқиғаға негізделген біріздендіру (NautilusTrader тәсілі)

NautilusTrader паритетті біріздендірілген NautilusKernel арқылы жүзеге асырады — детерминистік оқиғаға негізделген ядросы және наносекундтық ажыратымдылығы бар Rust-native қозғалтқыш. Бір стратегия іске асыруы бэктесте де, нақты саудада да жұмыс істейді.
Архитектура порттар мен адаптерлер үлгісіне (гексагональды архитектура) негізделген:
┌──────────────────────────────────┐
│ NautilusKernel │
│ ┌───────────┐ ┌─────────────┐ │
│ │ Strategy │ │ RiskEngine │ │
│ │ (Python) │ │ (Rust) │ │
│ └─────┬─────┘ └──────┬──────┘ │
│ │ │ │
│ ┌─────┴───────────────┴──────┐ │
│ │ Message Bus (Rust) │ │
│ └─────┬───────────────┬──────┘ │
└────────┼───────────────┼─────────┘
│ │
┌─────┴─────┐ ┌─────┴──────┐
│ Backtest │ │ Live │
│ Adapter │ │ Adapter │
│ FillModel │ │ Exchange │
│ (L2 book) │ │ Gateway │
└────────────┘ └────────────┘
Артықшылықтары:
- Детерминистік replay. Оқиғалар қатаң анықталған ретпен өңделеді — бэктест нәтижесі бит-биттіклей қайта жаңғыртылады.
- Custom FillModel. Әрбір орындау үшін L2 ордербук симуляциясы — слиппедж нақты ордербук тереңдігі негізінде модельденеді.
- Өнімділік. Секундына 5 миллионға дейін жол, RAM-ға сыймайтын деректерді өңдеу.
- Redis + PostgreSQL. Redis арқылы кэш пен хабарлар шинасы, PostgreSQL арқылы персистенттілік — бэктест пен лайв үшін бірдей инфрақұрылым.
3-үлгі: Strategy Interface (Freqtrade тәсілі)
Freqtrade біріздендірілген IStrategy интерфейсін пайдаланады: бір стратегия класы бэктесте де, лайвта да жұмыс істейді. Жалғыз айырмашылық — персистенттілік қабаты.
class IStrategy:
"""Unified interface — the implementation does not know if this is a backtest or live."""
def populate_indicators(self, dataframe, metadata):
"""Compute indicators."""
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):
"""Determine entry signals."""
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):
"""Determine exit signals."""
dataframe.loc[
(dataframe['fast_ma'] < dataframe['slow_ma']),
'exit_long'
] = 1
return dataframe
Freqtrade қосымша ұсынады:
- Optuna арқылы Hyperopt — стратегия параметрлерін оңтайландыру
--timeframe-detail— толтыруды нақтылау үшін неғұрлым нәзік уақыт кезеңіне тереңдету (адаптивті drill-down-ге ұқсас)
Үлгілерді салыстыру
| Shared Core | Оқиғаға негізделген (NautilusTrader) | Strategy Interface (Freqtrade) | |
|---|---|---|---|
| Іске асыру күрделілігі | Төмен | Жоғары | Орташа |
| Паритет деңгейі | Орташа | Максималды | Жоғары |
| Толтыру симуляциясы | Бөлек FillModel | L2 ордербук | --timeframe-detail |
| Ядро тілі | Python | Rust + Python | Python |
| Қолайлы | Custom қозғалтқыштар | Институционалды сауда | Жылдам бастау |
Толтыру симуляциясының дәлдігі

Толтыру симуляциясы орындау алшақтығының негізгі көзі. Дәлдіктің үш деңгейі:
1-деңгей: Наивті (жабылу бағасымен толтыру)
fill_price = candle['close']
Қате: слиппеджді, спредті немесе ішінара толтыруды есепке алмайды. PnL-ды жүйелі түрде асыра бағалайды.
2-деңгей: Слиппедж моделі
def simulate_fill(order, candle, slippage_bps=5):
"""Fill with slippage."""
base_price = candle['close']
slip = base_price * slippage_bps / 10000
if order.side == 'buy':
return base_price + slip # Buy at a higher price
else:
return base_price - slip # Sell at a lower price
Қате: тұрақты слиппедж өтімділік пен бұйрық көлемін есепке алмайды. Наивтіден жақсырақ, бірақ бәрібір дөрекі модель.
3-деңгей: 1с/100мс деректерімен адаптивті drill-down
Ең жақсы нұсқа: SL/TP толтыру ретін дәл анықтау үшін нақты нәзік грануляциялы деректерді пайдалану. Толығырақ Адаптивті drill-down: айнымалы грануляциямен бэктестинг мақаласында сипатталған.
class RealisticFillModel:
"""
Combined fill model: slippage + spread + volume impact.
"""
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
Нарықтық әсер формуласы (жеңілдетілген Almgren-Chriss моделі):
мұндағы — құбылмалылық, — әсер коэффициенті, — бұйрық көлемі, ал — кезең үшін нарық көлемі.
Практикалық паритет чеклисті

Ботты лайвқа іске қоспас бұрын, әр тармақты тексеріңіз:
Код:
- Стратегия shared core пайдаланады (бэктест пен лайв үшін бір модуль)
- Сигнал логикасы екі жерде қайталанбайды
- Юнит-тесттер бірдей кірістер үшін ядроның бірдей шығуын тексереді
- Шарттарды тексеру реті бірдей (SL TP-ден бұрын ба? TP SL-ден бұрын ба?)
Деректер:
- Уақыт белгісінің форматы бірдей (UTC, бірдей провайдер)
- OHLCV агрегациясы бірдей ережелерді қолданады
- Жоқ кэндлдерді өңдеу бірдей
- Look-ahead bias жоқ — бэктест болашаққа үңілмейді
Орындау:
- Слиппедж моделі нақты деректерде калибрленген
- Ішінара толтырулар модельденген (немесе кем дегенде пессимистік бағаланған)
- Лимитті бұйрықтарда кезек басымдық моделі бар
- Кідіріс есепке алынған (сигналдан толтыруға дейін 100-500 мс кідіріс)
Шығындар:
- Maker/taker комиссиялары ағымдағы мөлшерлемемен қосылған
- Мәңгілік фьючерстермен фандинг мөлшерлемелері есепке алынған
- Спред модельденген (кем дегенде орташа)
Инфрақұрылым:
- Күй персистенттілігі: бот қайта іске қосудан кейін позицияларды қалпына келтіреді
- Қайта қосылу логикасы: WebSocket деректер жоғалтусыз қайта қосылады
- Логтау: пост-мортем талдау үшін барлық бұйрықтар мен толтырулар логталады
Өндірісте алшақтықты бақылау
Паритет — бір реттік тексеру емес, үздіксіз процесс. Ботты іске қосқаннан кейін алшақтықтарды нақты уақытта бақылау қажет.
Shadow режимі (paper trading)

Ботты бэктестпен параллель, бірдей деректер бойынша жүргізіңіз. Бот сигналдар генерациялайды, бірақ бұйрықтар жібермейді — тек логтайды. Сонымен қатар бэктест бірдей деректерді өңдейді. Салыстырыңыз:
class DivergenceMonitor:
"""
Compares backtest and live bot signals in real time.
"""
def __init__(self, tolerance_pct=0.5):
self.tolerance = tolerance_pct / 100
self.divergences = []
def compare_signal(self, backtest_signal, live_signal, timestamp):
"""Compare backtest and live signals."""
if backtest_signal is None and live_signal is None:
return # Both silent — 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):
"""Compare execution."""
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):
"""Weekly divergence report."""
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,
}
Дашборд метрикалары
| Метрика | Формула | Ескерту шегі |
|---|---|---|
| Сигнал сәйкестік деңгейі | < 95% | |
| Орташа слиппедж | (bps) | > 10 bps |
| Толтыру деңгейі | < 90% | |
| PnL алшақтығы | > 20% | |
| Кідіріс p99 | Сигналдан толтыруға дейінгі 99-персентиль | > 500 мс |
Слиппедж моделін калибрлеу

2-4 апта бойы деректер жинақтағаннан кейін, сіз бэктест слиппедж моделін нақты деректерге калибрлей аласыз:
def calibrate_slippage(live_fills: list[dict]) -> dict:
"""
Calibrate slippage model using real fills.
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,
}
Басқа құралдармен байланыс
Бэктест-лайв паритеті оқшауланған міндет емес. Ол "Backtests Without Illusions" сериясының басқа құралдарымен қиылысады:
- Адаптивті drill-down — толтыру симуляциясының дәлдігін жақсартады, бұл орындау паритетінің негізгі құрамдас бөлігі.
- Фандинг мөлшерлемелері — егер бэктест фандингті модельдемесе, 3x-тен жоғары левередж кезінде паритет мүмкін емес.
- Parquet кэш — алдын ала есептелген уақыт кезеңдері мен индикаторлар бэктестің ботпен бірдей деректерді көруін қамтамасыз етеді. RunningCandleBuffer эмуляциясы = нақты уақыттағы жаңарту.
- Polars vs Pandas — pandas-тан (бэктест) Polars-қа (лайв) ауысқанда, сандық нәтижелердің сәйкес келуін қамтамасыз ету қажет.
- Walk-Forward — үлгіден тыс деректердегі walk-forward стратегияның қалай нашарлайтынын көрсетеді — бұл in-sample бэктестке қарағанда лайвқа жақынырақ.
Ұсыныстар
-
Shared core міндетті. Сигнал генерациясы үшін жалғыз кодтық база — паритеттің минималды талабы. Бірдей логикасы бар екі файл бір ай ішінде алшақтықты кепілдейді.
-
Толтыру моделін калибрлеңіз. Тұрақты 5 bps слиппедж ештеңеден жақсы. Нақты деректерге калибрленген слиппедж моделі айтарлықтай жақсырақ.
-
Алғашқы 2-4 апта бойы shadow режимін пайдаланыңыз. Сигнал сәйкестік деңгейі 95%+ жеткенше нақты ақшамен сауда жасамаңыз.
-
Фандинг мөлшерлемелерін модельдеңіз. Мәңгілік фьючерстер үшін бұл опция емес — бұл міндетті. Фандинг 5x-тен жоғары левередж кезінде барлық PnL-ды жеп қоюы мүмкін.
-
Барлығын логтаңыз. Әрбір сигнал, әрбір бұйрық, әрбір толтыру — уақыт белгілерімен. Логтарсыз пост-мортем талдау мүмкін емес.
-
Салыстыруды автоматтандырыңыз. Апталық DivergenceMonitor есебі автоматты түрде келуі керек. PnL теріс болғанша күтпеңіз.
-
Әдепкі бойынша пессимистік бэктест. Бэктесте күтуді төмен бағалап, лайвта жағымды таңғалдыру керісінше болғаннан жақсырақ. Слиппедж моделі консервативті болуы керек.
Қорытынды

Бэктест-лайв паритеті жүйенің қасиеті емес, ол — процесс. Мінсіз паритет жоқ: бэктест анықтамасы бойынша реалдылықтың моделі, ал модель әрқашан жеңілдетеді. Бірақ "модель 5%-ға өзгеше" мен "модель 50%-ға өзгеше" арасындағы айырмашылықты архитектура анықтайды.
Жетілудің үш деңгейі:
- Негізгі. Shared core, тұрақты слиппедж, комиссиялар. Алшақтық: 10-20%.
- Жетілдірілген. Оқиғаға негізделген архитектура, адаптивті drill-down, фандинг моделі, shadow режимі. Алшақтық: 5-10%.
- Институционалды. L2 ордербук симуляциясы, калибрленген әсер моделі, нақты уақыттағы алшақтық мониторингі. Алшақтық: 2-5%.
Сіздің міндетіңіз — қай деңгейде екеніңізді анықтау және позиция көлеміңіз бен левереджіңіз үшін қандай алшақтықты қолайлы деп санайтыныңызды түсіну.
Пайдалы сілтемелер
- 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
Дәйексөз
@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.