← К списку статей
March 7, 2026
5 мин. чтения

Backtest-live parity: почему ваш бот торгует не так, как бэктест

Backtest-live parity: почему ваш бот торгует не так, как бэктест
#алготрейдинг
#бэктест
#live trading
#backtest-live parity
#execution
#NautilusTrader

Вы прогнали стратегию через бэктест. Sharpe 2.1, MaxDD -8%, PnL +67%. Запустили бота. Через месяц сравниваете: те же сигналы, тот же период — но live PnL на 40% ниже. Просадка в полтора раза глубже. Два трейда из десяти вообще не были исполнены.

Это не баг. Это backtest-live divergence — систематическое расхождение между результатами бэктеста и реальной торговли. Оно есть у всех. Вопрос только в том, знаете ли вы о нём и умеете ли его контролировать.

В этой статье — полная таксономия расхождений, архитектурные паттерны для их минимизации и практический чек-лист для мониторинга parity в продакшене.

Синдром «it worked in backtest»

Расхождение бэктеста и реальной торговли — идеальная кривая эквити против волатильных реальных результатов

Каждый алготрейдер проходит через этот цикл:

  1. Написал стратегию в Jupyter notebook
  2. Прогнал бэктест на историческом CSV — результаты отличные
  3. Переписал логику на бота (часто на другом языке или фреймворке)
  4. Запустил — результаты не совпадают
  5. Начал искать баг, не нашёл — «рынок изменился»

Проблема не в рынке. Проблема в том, что бэктест и бот — это два разных программных продукта, которые по-разному моделируют одну и ту же реальность. Расхождения неизбежны, но их можно систематизировать и минимизировать.

Таксономия расхождений

Taxonomy of backtest-live divergences

Все источники divergence делятся на четыре категории. Для каждой — оценка severity (от 1 до 5) и типичный вклад в расхождение PnL.

1. Data divergences (severity: 3/5)

Данные, которые видит бэктест, и данные, которые видит бот в реальном времени — это не одно и то же.

Timestamps. Биржи отдают свечи с разными правилами привязки к временной метке. Одна биржа помечает свечу началом периода, другая — концом. REST API может вернуть свечу с задержкой 1–3 секунды после фактического закрытия. Бэктест работает с «идеальными» timestamps из исторического файла.

OHLCV-агрегация. Исторические данные часто агрегируются провайдером иначе, чем это делает биржа в реальном времени. Разница в последнем знаке — но при пороговых сигналах (пересечение MA, пробитие уровня) это решает, войдёт стратегия в позицию или нет.

Gap и пропуски. Исторические данные обычно чистые — пропущенные свечи заполнены интерполяцией. В реальном времени WebSocket может отвалиться, и бот пропустит 30 секунд данных.

Типичный вклад в PnL divergence: 2–5% от итогового PnL за год.

2. Execution divergences (severity: 5/5)

Расхождения исполнения ордеров — визуализация проскальзывания, задержки и частичного исполнения

Самый опасный класс расхождений. Бэктест симулирует исполнение идеально — реальность далека от идеала.

Slippage. Бэктест заполняет ордер по цене close (или по цене сигнала). В реальности market-ордер исполняется по best bid/ask плюс проскальзывание, зависящее от объёма и ликвидности. Для позиции в $10K на среднеликвидном альткоине slippage может составлять 0.05–0.3%.

Формула кумулятивного slippage за NN сделок:

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

где sis_i — slippage ii-й сделки, зависящий от orderbook depth:

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

Latency. От момента генерации сигнала до исполнения ордера проходит время: вычисление сигнала (1–50 мс), отправка запроса (10–200 мс), матчинг на бирже (1–10 мс). В бэктесте latency = 0. В live — цена может уйти.

Partial fills. Бэктест предполагает, что 100% ордера исполняется мгновенно. В реальности limit-ордер может быть исполнен частично — или не исполнен вовсе, если цена развернулась. Для market-ордера на неликвидном рынке ордер «проскальзывает» по нескольким уровням стакана.

Queue priority. Limit-ордер, поставленный по цене best bid, не исполнится сразу — он встаёт в очередь за всеми ранее размещёнными ордерами на этом уровне. Бэктест, который считает «цена коснулась — ордер исполнен», систематически завышает fill rate.

Типичный вклад в PnL divergence: 10–30% от итогового PnL за год.

3. Logic divergences (severity: 4/5)

Это расхождения в самом коде стратегии между бэктестом и ботом.

Раздельные кодовые базы. Классический антипаттерн: backtests/strategy_a.py и bot/strategy_a.py — два отдельных файла, которые «делают одно и то же». После трёх месяцев правок они неизбежно расходятся. Кто-то добавил фильтр в бэктест и забыл продублировать в боте. Или наоборот — в боте поправили баг, который остался в бэктесте.

Разные фреймворки. Бэктест на pandas с vectorized операциями, бот на asyncio с event-driven логикой. Даже при идентичной стратегии edge cases обрабатываются по-разному: округление, порядок проверки условий, обработка NaN.

State management. Бэктест обычно stateless — проходит по массиву данных. Бот stateful — хранит позиции, балансы, историю ордеров. Рестарт бота, потеря state, десинхронизация с биржей — всё это источники расхождений.

Типичный вклад в PnL divergence: 5–20% от итогового PnL за год.

4. Cost divergences (severity: 3/5)

Расхождения в моделировании торговых издержек.

Funding rates. Большинство бэктестов perpetual futures не учитывают funding rates вообще. При leverage 10× и средней ставке 0.01% за 8 часов это 0.01%×3×365×10=109.5%0.01\% \times 3 \times 365 \times 10 = 109.5\% в год — больше, чем PnL большинства стратегий. Подробный разбор — в статье Funding rates убивают ваш leverage.

Комиссии. Maker/taker комиссии обычно моделируются, но часто с неправильной ставкой. VIP-уровни, BNB-скидки, rebates — всё это влияет на итоговый результат.

Spread. Бэктест по свечам не видит bid-ask spread. На минутной свече close = 3000, но в реальности bid = 2999.5 и ask = 3000.5. Каждая сделка «стоит» полспреда.

Типичный вклад в PnL divergence: 5–15% от итогового PnL за год.

Суммарный эффект

Все четыре категории действуют одновременно и, как правило, в одну сторону — против трейдера:

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

Суммарная divergence в 20–50% от бэктест-PnL — норма для непродуманной системы. При leverage эффект мультиплицируется.

Архитектурные паттерны для parity

Паттерн 1: Shared Core (извлечение общего ядра)

Архитектура Shared Core — единый модуль стратегии для бэктеста и live-торговли

Идея: выделить ядро стратегии — генерацию сигналов и логику исполнения — в отдельный модуль, который используется и бэктестом, и ботом. Различается только инфраструктура вокруг: источник данных и механизм отправки ордеров.

┌─────────────────────────────────────┐
│         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:
    """
    Ядро стратегии. Одинаковый код для бэктеста и live.
    Зависит только от данных, не от инфраструктуры.
    """
    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]:
        """
        Обработка новой свечи. Возвращает OrderRequest или None.
        Этот метод вызывается идентично из бэктеста и из бота.
        """
        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: Event-driven unification (подход NautilusTrader)

Event-driven архитектура с каскадным потоком событий — рыночные данные, сигналы, ордера, fills

NautilusTrader реализует parity через единое ядро NautilusKernel — Rust-native engine с детерминированным event-driven ядром и наносекундной резолюцией. Одна и та же реализация стратегии работает и в бэктесте, и в live-торговле.

Архитектура строится на паттерне ports and adapters (hexagonal architecture):

┌──────────────────────────────────┐
│        NautilusKernel            │
│  ┌───────────┐  ┌─────────────┐  │
│  │ Strategy   │  │ RiskEngine  │  │
│  │ (Python)   │  │ (Rust)      │  │
│  └─────┬─────┘  └──────┬──────┘  │
│        │               │         │
│  ┌─────┴───────────────┴──────┐  │
│  │      Message Bus (Rust)    │  │
│  └─────┬───────────────┬──────┘  │
└────────┼───────────────┼─────────┘
         │               │
   ┌─────┴─────┐   ┌─────┴──────┐
   │ Backtest   │   │ Live       │
   │ Adapter    │   │ Adapter    │
   │ FillModel  │   │ Exchange   │
   │ (L2 book)  │   │ Gateway    │
   └────────────┘   └────────────┘

Преимущества:

  • Deterministic replay. События обрабатываются в строго определённом порядке — результат бэктеста воспроизводим побитово.
  • Custom FillModel. L2-моделирование ордербука для каждого исполнения — slippage симулируется на основе реальной глубины стакана.
  • Производительность. До 5 миллионов строк/сек, обработка данных, не помещающихся в RAM.
  • Redis + PostgreSQL. Cache и message bus через Redis, persistence через PostgreSQL — одинаковая инфраструктура для бэктеста и live.

Паттерн 3: Strategy Interface (подход Freqtrade)

Freqtrade использует единый интерфейс IStrategy: один и тот же класс стратегии работает в бэктесте и в live. Разница только в persistence layer.


class IStrategy:
    """Единый интерфейс — реализация не знает, бэктест это или live."""

    def populate_indicators(self, dataframe, metadata):
        """Рассчитать индикаторы."""
        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):
        """Определить сигналы входа."""
        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):
        """Определить сигналы выхода."""
        dataframe.loc[
            (dataframe['fast_ma'] < dataframe['slow_ma']),
            'exit_long'
        ] = 1
        return dataframe

Freqtrade дополнительно предоставляет:

  • Hyperopt через Optuna — оптимизация параметров стратегии
  • --timeframe-detail — drill-down в более мелкий таймфрейм для уточнения fill (аналогично адаптивному drill-down)

Сравнение паттернов

Shared Core Event-driven (NautilusTrader) Strategy Interface (Freqtrade)
Сложность внедрения Низкая Высокая Средняя
Уровень parity Средний Максимальный Высокий
Fill simulation Отдельный FillModel L2 orderbook --timeframe-detail
Язык ядра Python Rust + Python Python
Подходит для Собственных движков Институциональной торговли Быстрого старта

Точность fill simulation

Fill simulation accuracy levels

Fill simulation — главный источник execution divergence. Три уровня точности:

Уровень 1: Naive (fill по цене close)

fill_price = candle['close']

Ошибка: не учитывает slippage, spread, partial fills. Систематически завышает PnL.

Уровень 2: Slippage model

def simulate_fill(order, candle, slippage_bps=5):
    """Fill с учётом slippage."""
    base_price = candle['close']
    slip = base_price * slippage_bps / 10000

    if order.side == 'buy':
        return base_price + slip  # Покупаем дороже
    else:
        return base_price - slip  # Продаём дешевле

Ошибка: фиксированный slippage не учитывает ликвидность и размер ордера. Лучше, чем naive, но всё ещё грубая модель.

Уровень 3: Adaptive drill-down с 1s/100ms данными

Лучший вариант: использовать реальные данные мелкой гранулярности для точного определения порядка заполнения SL/TP. Подробно описано в статье Adaptive drill-down: бэктест с переменной гранулярностью.

class RealisticFillModel:
    """
    Комбинированная модель fill: 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

Формула market impact (модель Альмгрена-Кристса, упрощённая):

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

где σ\sigma — волатильность, kk — коэффициент impact, VorderV_{order} — объём ордера, VmarketV_{market} — объём рынка за период.

Практический чек-лист parity

Голографический чек-лист валидации parity по категориям — данные, исполнение, тайминг, комиссии

Перед запуском бота в live проверьте каждый пункт:

Код:

  • Стратегия использует shared core (один модуль для бэктеста и live)
  • Нет дублирования логики сигналов в двух местах
  • Unit-тесты проверяют идентичность выходов core для одинаковых входов
  • Порядок проверки условий идентичен (SL перед TP? TP перед SL?)

Данные:

  • Timestamp-формат одинаковый (UTC, один и тот же провайдер)
  • OHLCV-агрегация использует одни правила
  • Обработка пропущенных свечей идентична
  • Нет look-ahead bias — бэктест не заглядывает в будущее

Исполнение:

  • Slippage model калиброван по реальным данным
  • Partial fills смоделированы (или хотя бы пессимистично оценены)
  • Limit-ордера имеют модель queue priority
  • Latency учтена (задержка 100–500 мс от сигнала до fill)

Costs:

  • Maker/taker комиссии включены с актуальной ставкой
  • Funding rates учтены для perpetual futures
  • Spread смоделирован (хотя бы средний)

Инфраструктура:

  • State persistence: бот восстанавливает позиции после рестарта
  • Reconnection logic: WebSocket переподключается без потери данных
  • Logging: все ордера и fills логируются для post-mortem анализа

Мониторинг divergence в продакшене

Parity — не разовая проверка, а непрерывный процесс. После запуска бота необходимо отслеживать расхождения в реальном времени.

Shadow mode (paper trading)

Shadow trading — live-данные и симулированные ордера работают параллельно

Запустите бота параллельно с бэктестом на тех же данных. Бот генерирует сигналы, но не отправляет ордера — только логирует. Одновременно бэктест обрабатывает те же данные. Сравните:

class DivergenceMonitor:
    """
    Сравнивает сигналы бэктеста и live-бота в реальном времени.
    """
    def __init__(self, tolerance_pct=0.5):
        self.tolerance = tolerance_pct / 100
        self.divergences = []

    def compare_signal(self, backtest_signal, live_signal, timestamp):
        """Сравнить сигнал бэктеста и live."""
        if backtest_signal is None and live_signal is None:
            return  # Оба молчат — 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):
        """Сравнить исполнение."""
        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):
        """Еженедельный отчёт о divergence."""
        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,
        }

Метрики для дашборда

Метрика Формула Порог тревоги
Signal match rate совпадениявсего сигналов\frac{\text{совпадения}}{\text{всего сигналов}} < 95%
Avg slippage 1Nsi\frac{1}{N}\sum s_i (bps) > 10 bps
Fill rate исполненоотправлено\frac{\text{исполнено}}{\text{отправлено}} < 90%
PnL divergence PnLlivePnLbtPnLbt\frac{PnL_{live} - PnL_{bt}}{PnL_{bt}} > 20%
Latency p99 99-й перцентиль signal-to-fill > 500 мс

Калибровка slippage model

Калибровка модели slippage — глубина стакана с кривой market impact

После накопления данных за 2–4 недели можно откалибровать slippage model бэктеста по реальным данным:

def calibrate_slippage(live_fills: list[dict]) -> dict:
    """
    Калибровка slippage model по реальным 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,
    }

Связь с другими инструментами

Backtest-live parity — не изолированная задача. Она пересекается с другими инструментами из серии «Бэктесты без иллюзий»:

  • Adaptive drill-down — повышает точность fill simulation, ключевой компонент execution parity.
  • Funding rates — если бэктест не моделирует funding, parity невозможна при leverage > 3×.
  • Parquet-кэш — предвычисленные таймфреймы и индикаторы гарантируют, что бэктест видит те же данные, что и бот. Эмуляция RunningCandleBuffer = реалтайм-обновление.
  • Polars vs Pandas — при переходе с pandas (бэктест) на Polars (live) нужно убедиться, что числовые результаты совпадают.
  • Walk-Forward — walk-forward на out-of-sample данных показывает, как стратегия деградирует — это ближе к live, чем in-sample бэктест.

Рекомендации

  1. Shared core — обязательно. Одна кодовая база для генерации сигналов — минимальное требование для parity. Два файла с одинаковой логикой — гарантированная divergence через месяц.

  2. Калибруйте fill model. Фиксированный slippage 5 bps — лучше, чем ничего. Slippage model, откалиброванный по реальным данным — значительно лучше.

  3. Используйте shadow mode первые 2–4 недели. Не торгуйте реальными деньгами, пока signal match rate не достигнет 95%+.

  4. Моделируйте funding rates. Для perpetual futures это не опционально — это обязательно. Funding может съесть весь PnL при leverage > 5×.

  5. Логируйте всё. Каждый сигнал, каждый ордер, каждый fill — с timestamps. Без логов post-mortem анализ невозможен.

  6. Автоматизируйте сравнение. Еженедельный отчёт DivergenceMonitor должен приходить автоматически. Не ждите, пока PnL уйдёт в минус.

  7. Пессимистичный бэктест по умолчанию. Лучше занизить ожидания в бэктесте и приятно удивиться в live, чем наоборот. Slippage model должен быть консервативным.

Заключение

Уровни зрелости торговой системы — от базового бэктеста до полного продакшена

Backtest-live parity — это не свойство системы, а процесс. Идеальной parity не существует: бэктест по определению — модель реальности, а модель всегда упрощает. Но разницу между «модель отличается на 5%» и «модель отличается на 50%» определяет архитектура.

Три уровня зрелости:

  1. Базовый. Shared core, фиксированный slippage, комиссии. Divergence: 10–20%.
  2. Продвинутый. Event-driven architecture, adaptive drill-down, funding model, shadow mode. Divergence: 5–10%.
  3. Институциональный. L2 orderbook simulation, калиброванный impact model, real-time divergence monitoring. Divergence: 2–5%.

Ваша задача — определить, на каком уровне вы находитесь, и понимать, какую divergence вы считаете приемлемой для вашего размера позиции и leverage.


Полезные ссылки

  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. López 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

Цитирование

@article{soloviov2026backtestliveparity,
  author = {Soloviov, Eugen},
  title = {Backtest-live parity: почему ваш бот торгует не так, как бэктест},
  year = {2026},
  url = {https://marketmaker.cc/ru/blog/post/backtest-live-parity},
  description = {Полная таксономия расхождений между бэктестом и live-торговлей: от slippage и partial fills до рассинхронизации кодовых баз. Архитектурные паттерны для достижения parity и чек-лист мониторинга в продакшене.}
}
Дисклеймер: Информация в этой статье предоставлена исключительно в образовательных и ознакомительных целях и не является финансовым, инвестиционным или торговым советом. Торговля криптовалютами сопряжена с высоким риском убытков.

MarketMaker.cc Team

Количественные исследования и стратегии

Обсудить в Telegram
Newsletter

Будьте в курсе событий

Подпишитесь на нашу рассылку, чтобы получать эксклюзивную аналитику по AI-трейдингу и обновления платформы.

Мы уважаем вашу конфиденциальность. Отписаться можно в любой момент.