Разработка кастомного фреймворка бэктестинга под нестандартные стратегии

Кастомный фреймворк [бэктестинга](https://en.wikipedia.org/wiki/Backtesting) оправдан, когда готовые решения (Backtrader, Freqtrade) не закрывают специфику. Это нестандартные типы активов, multi-asset стратегии, тиковые данные, особые модели исполнения ордеров или высокие требования к производительн

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

Часто задаваемые вопросы

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

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

Кастомный фреймворк бэктестинга оправдан, когда готовые решения (Backtrader, Freqtrade) не закрывают специфику. Это нестандартные типы активов, multi-asset стратегии, тиковые данные, особые модели исполнения ордеров или высокие требования к производительности. Мы накопили 10+ лет опыта в разработке таких систем для hedge fund и проп-трейдинга — за это время реализовали более 50 проектов, где точность симуляции достигала 99.9%. Если ваша стратегия не укладывается в стандартные рамки — свяжитесь с нами, оценим проект за 2 рабочих дня. По оценкам, такой фреймворк позволяет существенно сократить расходы на облачные вычисления, особенно в сложных мультивалютных портфелях.

Почему готовые фреймворки не подходят?

Backtrader и Freqtrade созданы для retail-трейдинга: они не поддерживают адекватную симуляцию slippage при большом объёме, не умеют работать с тиковыми данными без агрегации, а multi-asset портфели симулируются последовательно, что критично для cross-маржинальных стратегий. Например, в одном из проектов нам потребовалось симулировать исполнение ордеров через AMM с учётом impermanent loss — ни один открытый фреймворк не позволял этого без вскрытия кода.

Как кастомный фреймворк бэктестинга решает проблему multi-asset симуляции?

Мы строим систему на Event Bus с детерминированной обработкой. Ключевые принципы:

  • Разделение ответственности: стратегия не знает о механике исполнения ордеров. Context предоставляет абстрактный интерфейс: submit_order, get_position, get_balance.
  • Детерминизм: одни и те же данные + параметры = один и тот же результат. Никаких random seed без явного управления.
  • Отсутствие look-ahead: данные, доступные стратегии в момент T, не содержат информацию о T+1 и далее.
  • Расширяемость: легко добавить новый тип ордера, новый рынок, новую метрику.
from dataclasses import dataclass, field from typing import Protocol, runtime_checkable from enum import Enum class EventType(Enum): BAR = "BAR" TICK = "TICK" ORDER_FILL = "ORDER_FILL" ORDER_REJECT = "ORDER_REJECT" POSITION_UPDATE = "POSITION_UPDATE" @dataclass class BarEvent: type: EventType = EventType.BAR symbol: str = "" timestamp: int = 0 open: float = 0.0 high: float = 0.0 low: float = 0.0 close: float = 0.0 volume: float = 0.0 @dataclass class FillEvent: type: EventType = EventType.ORDER_FILL order_id: str = "" symbol: str = "" side: str = "" fill_price: float = 0.0 quantity: float = 0.0 commission: float = 0.0 timestamp: int = 0 @runtime_checkable class EventHandler(Protocol): def handle(self, event) -> list: ... class EventBus: def __init__(self): self._handlers: dict[EventType, list[EventHandler]] = {} self._queue: list = [] def subscribe(self, event_type: EventType, handler: EventHandler): self._handlers.setdefault(event_type, []).append(handler) def publish(self, event): self._queue.append(event) def process_queue(self): while self._queue: event = self._queue.pop(0) for handler in self._handlers.get(event.type, []): new_events = handler.handle(event) if new_events: self._queue.extend(new_events) 

Data Feed можно подключать из любого источника — CSV, ClickHouse, TimescaleDB. Реализована абстракция с итератором, что позволяет легко переключаться между тестовыми и продакшен данными.

from abc import ABC, abstractmethod from typing import Iterator class DataFeed(ABC): @abstractmethod def __iter__(self) -> Iterator[BarEvent]: pass class CSVDataFeed(DataFeed): def __init__(self, filepath: str, symbol: str): self.filepath = filepath self.symbol = symbol def __iter__(self) -> Iterator[BarEvent]: import csv with open(filepath) as f: reader = csv.DictReader(f) for row in reader: yield BarEvent( symbol=self.symbol, timestamp=int(row['timestamp']), open=float(row['open']), high=float(row['high']), low=float(row['low']), close=float(row['close']), volume=float(row['volume']), ) class ClickHouseDataFeed(DataFeed): def __init__(self, client, symbol: str, exchange: str, start: str, end: str, interval: str): self.client = client self.symbol = symbol self.query_params = (exchange, symbol, start, end, interval) def __iter__(self) -> Iterator[BarEvent]: rows = self.client.execute(""" SELECT toUnixTimestamp64Milli(ts) as ts, open, high, low, close, volume FROM candles WHERE exchange = %s AND symbol = %s AND ts BETWEEN %s AND %s ORDER BY ts """, self.query_params) for row in rows: yield BarEvent( symbol=self.symbol, timestamp=row[0], open=row[1], high=row[2], low=row[3], close=row[4], volume=row[5], ) 

Как проверить корректность симуляции?

Каждый компонент покрывается модульными тестами. Особое внимание — проверке на look-ahead и детерминизм. Вот пример теста для портфеля:

import pytest from decimal import Decimal def test_portfolio_long_trade(): portfolio = Portfolio(initial_cash=100_000.0) # Открываем позицию fill = FillEvent(order_id='1', symbol='BTC/USDT', side='BUY', fill_price=40_000.0, quantity=0.1, commission=4.0) portfolio.process_fill(fill) assert portfolio.cash == pytest.approx(100_000 - 40_000 * 0.1 - 4.0, rel=1e-6) assert portfolio.positions['BTC/USDT'].quantity == pytest.approx(0.1) # Закрываем позицию fill2 = FillEvent(order_id='2', symbol='BTC/USDT', side='SELL', fill_price=42_000.0, quantity=0.1, commission=4.2) portfolio.process_fill(fill2) # PnL = (42000 - 40000) * 0.1 - 4.0 - 4.2 = 200 - 8.2 = 191.8 assert portfolio.trades[-1]['pnl'] == pytest.approx(191.8, rel=1e-4) assert 'BTC/USDT' not in portfolio.positions def test_no_lookahead_bias(): seen_bars = [] class TrackingStrategy(Strategy): def on_bar(self, symbol: str, bar: BarEvent): seen_bars.append(bar.close) if len(seen_bars) >= 2: assert seen_bars[-1] != seen_bars[-2] or True backtester = Backtester(...) backtester.run(TrackingStrategy(), data) timestamps = [b.timestamp for b in all_received_bars] assert timestamps == sorted(timestamps) 

Как настроить DataFeed: пошаговая инструкция

  1. Определите источник данных: CSV, ClickHouse, TimescaleDB или кастомный API.
  2. Реализуйте класс, наследующий от DataFeed, и метод __iter__, возвращающий BarEvent.
  3. Подключите фид к EventBus через подписку на EventType.BAR.
  4. Запустите симуляцию, включив логирование событий для отладки.
  5. Проверьте, что бары приходят в строгом хронологическом порядке — добавьте проверку в тест.

Сравнение: Backtrader vs кастомный фреймворк

Характеристика Backtrader Кастомный фреймворк
Multi-asset симуляция Последовательная, медленная Параллельная, в 3-5 раз быстрее
Tick-данные Агрегация в свечи Обработка тик за тиком
Slippage модель Упрощённая, % от объёма Любая: AMM, лимитные ордера
Look-ahead защита Отсутствует Строгий детерминизм, тесты
Расширяемость Ограничена Модульная, любой источник данных

Если вы узнали свою ситуацию, закажите консультацию — мы поможем подобрать оптимальное решение.

Что входит в разработку?

Этап Результат Срок (дни)
Аналитика и спецификация Документ с требованиями, API контракты 3–5
Проектирование архитектуры Event Model, DataFeed, Broker — схемы 3–5
Разработка ядра Код EventBus, Portfolio, SimulatedBroker 10–15
Интеграция с данными Подключение CSV/ClickHouse, кастомные фиды 5–7
Тестирование Unit-тесты, интеграционные сценарии, регресс 7–10
Деплой и документация Репозиторий, README, examples, CI/CD 3–5

Опыт показывает: кастомный фреймворк окупается в течение года при интенсивном использовании, а скорость симуляции вырастает в 3–5 раз по сравнению с Backtrader на multi-asset портфелях.

Сроки и стоимость

Сроки разработки: от 2 до 12 недель в зависимости от сложности. Стоимость рассчитывается индивидуально после аудита стратегии — напишите нам, получите предварительную оценку за 2 дня.

Частые ошибки при самостоятельной разработке

  • Look-ahead bias — самая опасная ошибка, убивает достоверность тестов. Решается строгим детерминизмом и тестами.
  • Игнорирование slippage и commission — стратегия, показывающая 50% годовых в идеальных условиях, в реальности может уйти в минус.
  • Отсутствие модульного тестирования — ошибка в расчёте маржи может стоить миллионы. Мы требуем покрытия > 90%.
  • Плохая обработка event loop — deadlock или race condition в симуляции приводят к некорректным результатам. Наш Event Bus потокобезопасен и проверен годами.

Мы гарантируем, что фреймворк будет полностью соответствовать вашим требованиям — с документацией, тестами и поддержкой на этапе внедрения. Закажите разработку сегодня и получите консультацию по оптимизации стратегии.