Разработка системы бэктестинга арбитражных стратегий

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1 услугВсе 1306 услуг
Разработка системы бэктестинга арбитражных стратегий
Сложная
~1-2 недели
Часто задаваемые вопросы
Направления блокчейн-разработки
Этапы блокчейн-разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1221
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1163
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    855
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1062
  • image_logo-advance_0.png
    Разработка логотипа компании B2B Advance
    561
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    828

Разработка системы бэктестинга арбитражных стратегий

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

Типы арбитражных стратегий

Cross-Exchange Arbitrage — BTC стоит $43,200 на Binance и $43,250 на Kraken. Купить там, продать здесь. Проблема: пока исполняется вторая нога, цена может измениться.

Statistical Arbitrage (Pairs Trading) — BTC и ETH исторически движутся вместе. При расширении спреда — покупаем отставший, продаём обогнавший. Ставим на возврат к средней.

Triangular Arbitrage — внутри одной биржи: BTC/USDT → ETH/BTC → ETH/USDT → USDT. Если произведение курсов не равно 1 — есть прибыль.

Funding Rate Arbitrage — если funding rate на Binance Futures > 0, открываем spot long + futures short. Получаем funding, не имея рыночного риска.

Модель данных для cross-exchange арбитража

import pandas as pd
import numpy as np
from dataclasses import dataclass

@dataclass
class ArbitrageOpportunity:
    timestamp: int
    symbol: str
    buy_exchange: str
    sell_exchange: str
    buy_price: float     # best ask на buy exchange
    sell_price: float    # best bid на sell exchange
    gross_spread: float  # sell_price - buy_price
    spread_pct: float    # gross_spread / buy_price
    buy_commission: float
    sell_commission: float
    net_spread_pct: float  # spread_pct - buy_commission - sell_commission
    max_size_usd: float   # ограничен доступной ликвидностью

class CrossExchangeArbitrageBacktester:
    def __init__(
        self,
        commission_per_exchange: float = 0.001,
        slippage_per_exchange: float = 0.0005,
        min_profit_pct: float = 0.002,  # минимальная прибыль для входа
        transfer_fee_usd: float = 2.0,   # стоимость перевода между биржами
    ):
        self.commission = commission_per_exchange
        self.slippage = slippage_per_exchange
        self.min_profit = min_profit_pct
        self.transfer_fee = transfer_fee_usd

    def find_opportunities(
        self,
        exchange_data: dict[str, pd.DataFrame],  # exchange → OHLCV
        symbol: str,
    ) -> pd.DataFrame:
        """Находим арбитражные возможности в исторических данных"""
        opportunities = []

        # Синхронизируем данные по timestamp
        merged = self._merge_exchange_data(exchange_data)

        for timestamp, row in merged.iterrows():
            exchanges = list(exchange_data.keys())

            for i, buy_ex in enumerate(exchanges):
                for sell_ex in exchanges:
                    if buy_ex == sell_ex:
                        continue

                    buy_price = row[f'{buy_ex}_ask'] * (1 + self.slippage)
                    sell_price = row[f'{sell_ex}_bid'] * (1 - self.slippage)

                    gross_spread = sell_price - buy_price
                    spread_pct = gross_spread / buy_price
                    total_commission = self.commission * 2
                    net_spread = spread_pct - total_commission

                    if net_spread > self.min_profit:
                        max_size = min(
                            row[f'{buy_ex}_ask_size'] * buy_price,
                            row[f'{sell_ex}_bid_size'] * sell_price,
                            10_000,  # наш лимит на сделку
                        )

                        opportunities.append({
                            'timestamp': timestamp,
                            'buy_exchange': buy_ex,
                            'sell_exchange': sell_ex,
                            'buy_price': buy_price,
                            'sell_price': sell_price,
                            'net_spread_pct': net_spread,
                            'max_size_usd': max_size,
                            'estimated_profit': max_size * net_spread,
                        })

        return pd.DataFrame(opportunities)

Статистический арбитраж (Pairs Trading)

from scipy import stats

class PairsTradingBacktester:
    def __init__(self, window: int = 60, entry_z: float = 2.0, exit_z: float = 0.5):
        self.window = window
        self.entry_z = entry_z
        self.exit_z = exit_z

    def compute_spread(
        self,
        price_a: pd.Series,
        price_b: pd.Series,
    ) -> tuple[pd.Series, float]:
        """Вычисляем коинтегрированный спред"""
        # OLS: price_a = beta * price_b + alpha
        slope, intercept, r_value, _, _ = stats.linregress(price_b, price_a)

        # Проверка коинтеграции (ADF тест)
        from statsmodels.tsa.stattools import coint
        _, p_value, _ = coint(price_a, price_b)

        if p_value > 0.05:
            raise ValueError(f"Pairs not cointegrated (p-value={p_value:.3f})")

        spread = price_a - slope * price_b - intercept
        return spread, slope

    def run(
        self,
        prices_a: pd.Series,
        prices_b: pd.Series,
        symbol_a: str,
        symbol_b: str,
    ) -> BacktestResult:
        portfolio = Portfolio(initial_cash=100_000)
        trades = []

        for i in range(self.window, len(prices_a)):
            # Rolling window для расчёта статистики
            window_a = prices_a.iloc[i - self.window:i]
            window_b = prices_b.iloc[i - self.window:i]

            spread, beta = self.compute_spread(window_a, window_b)
            current_spread = prices_a.iloc[i] - beta * prices_b.iloc[i]

            # Z-score спреда
            spread_mean = spread.mean()
            spread_std = spread.std()
            if spread_std == 0:
                continue
            z_score = (current_spread - spread_mean) / spread_std

            # Торговые сигналы
            position = portfolio.get_position_net(symbol_a)

            if position == 0:
                if z_score > self.entry_z:
                    # Спред высокий: продаём A, покупаем B
                    portfolio.sell(symbol_a, prices_a.iloc[i])
                    portfolio.buy(symbol_b, prices_b.iloc[i], quantity_usd=50_000)

                elif z_score < -self.entry_z:
                    # Спред низкий: покупаем A, продаём B
                    portfolio.buy(symbol_a, prices_a.iloc[i], quantity_usd=50_000)
                    portfolio.sell(symbol_b, prices_b.iloc[i])

            elif abs(z_score) < self.exit_z:
                # Закрываем позицию
                portfolio.close_all()

        return BacktestResult(portfolio, trades)

Реалистичные допущения арбитражного бэктеста

Latency — между получением цены и исполнением ордера проходит время. На cross-exchange арбитраже это 10–100ms. За это время цена может уйти. Моделируем добавлением slippage к каждой ноге.

Partial fills — книга заявок показывает объём, но большой ордер "ест" несколько уровней. Моделируем volume-weighted average price (VWAP) исполнения.

Execution correlation — обе ноги арбитража должны исполниться почти одновременно. На практике одна нога может быть исполнена, другая — нет (leg risk). Моделируем через вероятность неисполнения второй ноги.

Funding constraints — для cross-exchange арбитража нужны средства на обеих биржах одновременно. Переводы занимают часы (BTC) до суток (fiat). Это ограничивает масштаб.

Арбитражный бэктест без учёта этих факторов даёт нереалистично оптимистичные результаты. Каждый из них может превратить прибыльную стратегию в убыточную.