Розробка кастомного фреймворку бектестування для нестандартних стратегій

Кастомний фреймворк [бектестування](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(self.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 потокобезпечний і перевірений роками.

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