Разработка системы бэктестинга с учетом комиссий и проскальзывания

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1Все 1306 услуг
Разработка системы бэктестинга с учетом комиссий и проскальзывания
Средний
~3-5 дней
Часто задаваемые вопросы

Направления блокчейн-разработки

Этапы блокчейн-разработки

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1286
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1198
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    902
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1122
  • image_logo-advance_0.webp
    Разработка логотипа компании B2B Advance
    589
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    859

Разработка системы бэктестинга с учётом комиссий и проскальзывания

Бэктест без учёта комиссий и проскальзывания — оптимистичная сказка. Стратегия, показывающая 30% годовых в идеальных условиях, после добавления реальных торговых издержек может оказаться убыточной. Правильная модель издержек критически важна для честной оценки стратегий.

Структура торговых издержек

Комиссия биржи (Exchange Fee):

  • Maker fee: 0.01–0.1% (за limit orders, добавляющие ликвидность)
  • Taker fee: 0.05–0.5% (за market orders, забирающие ликвидность)
  • VIP скидки при больших объёмах

Проскальзывание (Slippage):

  • Market impact: крупный ордер двигает цену против вас
  • Bid-ask spread: разница между ценой покупки и продажи
  • Gap slippage: цена на открытии следующей свечи отличается от close предыдущей

Funding rate (для фьючерсов): платёж каждые 8 часов за удержание позиции.

Latency cost (косвенно): пока ордер дошёл до биржи, цена ушла.

Реализация модели комиссий

from dataclasses import dataclass
from decimal import Decimal
from enum import Enum

class OrderType(Enum):
    MARKET = "market"
    LIMIT = "limit"
    LIMIT_POST_ONLY = "limit_post_only"

@dataclass
class FeeSchedule:
    """Тарифная сетка биржи"""
    maker_fee: float
    taker_fee: float
    
    # VIP уровни (объём в USD за 30 дней → тарифы)
    vip_tiers: list[tuple[float, float, float]] = None  # (min_volume, maker, taker)

    def get_fee(self, order_type: OrderType, volume_30d: float = 0) -> float:
        if self.vip_tiers and volume_30d > 0:
            for min_vol, maker, taker in sorted(self.vip_tiers, reverse=True):
                if volume_30d >= min_vol:
                    return maker if order_type != OrderType.MARKET else taker

        if order_type == OrderType.MARKET:
            return self.taker_fee
        elif order_type == OrderType.LIMIT_POST_ONLY:
            return self.maker_fee
        else:
            return self.maker_fee  # limit orders обычно maker

# Тарифные сетки популярных бирж
BINANCE_SPOT = FeeSchedule(
    maker_fee=0.001, taker_fee=0.001,
    vip_tiers=[
        (1_000_000, 0.0009, 0.001),
        (5_000_000, 0.0008, 0.0009),
        (20_000_000, 0.0007, 0.0008),
    ]
)

BYBIT_PERP = FeeSchedule(maker_fee=-0.0001, taker_fee=0.0006)  # Bybit даёт rebate за maker

Модели проскальзывания

class SlippageModel:
    """Базовый класс для моделей проскальзывания"""

    def get_fill_price(self, order_price: float, bar, side: str) -> float:
        raise NotImplementedError


class FixedSlippage(SlippageModel):
    """Фиксированное проскальзывание в %"""
    def __init__(self, slippage_pct: float = 0.0005):
        self.slippage_pct = slippage_pct

    def get_fill_price(self, order_price: float, bar, side: str) -> float:
        if side == 'BUY':
            return order_price * (1 + self.slippage_pct)
        else:
            return order_price * (1 - self.slippage_pct)


class VolumeImpactSlippage(SlippageModel):
    """Проскальзывание, растущее с размером ордера относительно объёма"""
    def __init__(self, impact_factor: float = 0.1):
        self.impact_factor = impact_factor

    def get_fill_price(self, order_price: float, bar, side: str, order_size_usd: float = 0) -> float:
        # Линейный market impact: slippage = impact_factor × (order_size / bar_volume)
        bar_volume_usd = bar.volume * bar.close
        market_impact = self.impact_factor * order_size_usd / bar_volume_usd if bar_volume_usd > 0 else 0
        
        if side == 'BUY':
            return order_price * (1 + market_impact)
        else:
            return order_price * (1 - market_impact)


class BidAskSlippage(SlippageModel):
    """Учитываем bid-ask спред: market orders всегда исполняются через спред"""
    def __init__(self, typical_spread_pct: float = 0.0001):
        self.half_spread = typical_spread_pct / 2

    def get_fill_price(self, order_price: float, bar, side: str) -> float:
        # Для market order: покупаем по ask (mid + half_spread), продаём по bid (mid - half_spread)
        if side == 'BUY':
            return order_price * (1 + self.half_spread)
        else:
            return order_price * (1 - self.half_spread)

Funding Rate для фьючерсов

class FundingRateModel:
    """Учёт funding rate для вечных фьючерсов"""

    def __init__(self, funding_interval_hours: int = 8):
        self.interval = funding_interval_hours
        self.funding_history: dict[str, pd.Series] = {}  # symbol → historical rates

    def load_funding_history(self, symbol: str, rates: pd.Series):
        self.funding_history[symbol] = rates

    def calculate_funding_cost(
        self,
        symbol: str,
        position_value: float,
        from_ts: int,
        to_ts: int,
        position_side: str,
    ) -> float:
        """Рассчитываем суммарный funding за период"""
        if symbol not in self.funding_history:
            return 0.0

        rates = self.funding_history[symbol]

        # Фильтруем funding события в диапазоне
        mask = (rates.index >= from_ts) & (rates.index < to_ts)
        period_rates = rates[mask]

        total_funding = 0.0
        for rate in period_rates:
            # При position_side == 'LONG': платим если rate > 0, получаем если rate < 0
            if position_side == 'LONG':
                funding_payment = -position_value * rate
            else:
                funding_payment = position_value * rate
            total_funding += funding_payment

        return total_funding

Сравнение с/без издержек

def analyze_cost_impact(
    results_ideal: BacktestResult,
    results_realistic: BacktestResult,
) -> dict:
    """Сравниваем результаты с и без торговых издержек"""
    ideal_return = results_ideal.total_return_pct
    real_return = results_realistic.total_return_pct

    ideal_sharpe = results_ideal.metrics.sharpe_ratio
    real_sharpe = results_realistic.metrics.sharpe_ratio

    total_fees = results_realistic.total_commission_paid
    fee_drag_pct = total_fees / results_realistic.initial_capital * 100

    return {
        'ideal_annual_return': ideal_return,
        'real_annual_return': real_return,
        'return_drag_from_fees': ideal_return - real_return,
        'ideal_sharpe': ideal_sharpe,
        'real_sharpe': real_sharpe,
        'total_fees_paid': total_fees,
        'fee_drag_pct': fee_drag_pct,
        'fees_as_pct_of_gross_pnl': total_fees / max(results_ideal.gross_pnl, 0.01) * 100,
        'breakeven_win_rate_with_fees': results_realistic.breakeven_win_rate,
    }

Практические значения для крипто

Биржа/тип Maker fee Taker fee Типичное проскальзывание
Binance Spot 0.10% 0.10% 0.01–0.05%
Binance Futures 0.02% 0.05% 0.01–0.03%
Bybit Perp -0.01% (rebate) 0.06% 0.01–0.03%
Kraken Spot 0.16% 0.26% 0.02–0.10%
DEX (Uniswap) 0.30% (pool fee) 0.30% 0.05–0.50%

Для стратегий с высокой частотой сделок (десятки в день) комиссии критично важны. Стратегия с 100 сделками в месяц при 0.1% taker fee теряет 10% капитала в год только на комиссиях — это нужно окупать из P&L.