Разработка системы forward-тестирования (paper trading)

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1 услугВсе 1306 услуг
Разработка системы forward-тестирования (paper trading)
Средняя
~3-5 рабочих дней
Часто задаваемые вопросы
Направления блокчейн-разработки
Этапы блокчейн-разработки
Последние работы
  • 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

Разработка системы forward-тестирования (paper trading)

Paper trading — торговля с виртуальными деньгами на реальных рыночных данных в реальном времени. Это мост между бэктестингом и live trading: стратегия работает с реальной инфраструктурой, реальными API биржи, реальными данными, но без реальных денег. Позволяет выявить проблемы, невидимые в бэктесте.

Что выявляет paper trading, чего не показывает бэктест

Latency issues — бэктест мгновенный, реальная система имеет задержки. Сигнал генерируется, но к моменту исполнения цена ушла.

API quirks — биржевые API имеют особенности: rate limits, неожиданное поведение при market volatility, расхождение real-time и historical data.

Software bugs — production-код содержит ошибки, невидимые при бэктестинге на pandas DataFrame.

Order management complexity — реальный management ордеров сложнее симуляции: partial fills, unexpected cancellations, margin calls.

Mental psychology — для manual/semi-manual стратегий: готов ли трейдер следовать сигналам в реальности?

Архитектура paper trading системы

import asyncio
from dataclasses import dataclass
from decimal import Decimal
import time

class PaperBroker:
    """
    Брокер для paper trading.
    Использует real-time данные биржи, но не отправляет реальные ордера.
    """
    def __init__(self, exchange_client, initial_balance: dict[str, Decimal]):
        self.exchange = exchange_client
        self.balance = dict(initial_balance)
        self.orders: dict[str, PaperOrder] = {}
        self.positions: dict[str, PaperPosition] = {}
        self.trade_history = []
        self.order_id_counter = 0

    async def place_order(
        self,
        symbol: str,
        side: str,
        order_type: str,
        quantity: Decimal,
        price: Decimal = None,
    ) -> PaperOrder:
        order_id = f"paper_{self.order_id_counter:06d}"
        self.order_id_counter += 1

        # Проверяем баланс
        if side == 'BUY':
            required_quote = quantity * (price or await self.get_market_price(symbol, 'ask'))
            quote_asset = symbol.split('/')[1]
            if self.balance.get(quote_asset, Decimal(0)) < required_quote:
                raise InsufficientFundsError(f"Need {required_quote} {quote_asset}")

        order = PaperOrder(
            id=order_id,
            symbol=symbol,
            side=side,
            type=order_type,
            quantity=quantity,
            price=price,
            status='OPEN',
            created_at=time.time(),
        )

        self.orders[order_id] = order

        # Для market orders — немедленное исполнение
        if order_type == 'MARKET':
            await self.execute_market_order(order)

        return order

    async def get_market_price(self, symbol: str, side: str) -> Decimal:
        """Получаем real-time цену с биржи"""
        orderbook = await self.exchange.fetch_order_book(symbol, limit=5)
        if side == 'ask':
            return Decimal(str(orderbook['asks'][0][0]))
        else:
            return Decimal(str(orderbook['bids'][0][0]))

    async def execute_market_order(self, order: PaperOrder):
        """Исполняем market order по текущей рыночной цене + slippage"""
        price = await self.get_market_price(order.symbol, 'ask' if order.side == 'BUY' else 'bid')

        # Применяем реалистичное проскальзывание
        slippage = Decimal('0.0005')
        if order.side == 'BUY':
            fill_price = price * (1 + slippage)
        else:
            fill_price = price * (1 - slippage)

        commission = order.quantity * fill_price * Decimal('0.001')

        await self.process_fill(order, fill_price, commission)

    async def process_fill(self, order: PaperOrder, fill_price: Decimal, commission: Decimal):
        base_asset, quote_asset = order.symbol.split('/')
        cost = order.quantity * fill_price

        if order.side == 'BUY':
            self.balance[quote_asset] = self.balance.get(quote_asset, Decimal(0)) - cost - commission
            self.balance[base_asset] = self.balance.get(base_asset, Decimal(0)) + order.quantity
        else:
            self.balance[base_asset] = self.balance.get(base_asset, Decimal(0)) - order.quantity
            self.balance[quote_asset] = self.balance.get(quote_asset, Decimal(0)) + cost - commission

        order.fill_price = fill_price
        order.status = 'FILLED'
        order.filled_at = time.time()

        # Записываем сделку
        self.trade_history.append({
            'timestamp': order.filled_at,
            'symbol': order.symbol,
            'side': order.side,
            'quantity': float(order.quantity),
            'price': float(fill_price),
            'commission': float(commission),
        })

    async def check_limit_orders(self):
        """Периодически проверяем pending limit orders"""
        while True:
            for order_id, order in list(self.orders.items()):
                if order.status != 'OPEN' or order.type != 'LIMIT':
                    continue

                current_price = await self.get_market_price(order.symbol, 'last')

                should_fill = (
                    (order.side == 'BUY' and current_price <= order.price) or
                    (order.side == 'SELL' and current_price >= order.price)
                )

                if should_fill:
                    commission = order.quantity * order.price * Decimal('0.0001')  # maker fee
                    await self.process_fill(order, order.price, commission)

            await asyncio.sleep(1)  # проверяем каждую секунду

Сравнение paper vs backtest результатов

class PaperVsBacktestComparator:
    def compare(self, paper_result: PaperTradingResult, backtest_result: BacktestResult) -> dict:
        """Сравниваем результаты paper trading с ожиданиями из бэктеста"""
        paper_sharpe = self.compute_sharpe(paper_result.equity_curve)
        expected_sharpe = backtest_result.metrics.sharpe_ratio

        # Execution quality
        slippage_analysis = self.analyze_slippage(paper_result.trades, backtest_result.trades)

        return {
            'expected_sharpe': expected_sharpe,
            'actual_sharpe': paper_sharpe,
            'sharpe_ratio': paper_sharpe / expected_sharpe if expected_sharpe > 0 else 0,
            'avg_slippage_pct': slippage_analysis['avg_slippage'],
            'latency_cost_pct': slippage_analysis['latency_cost'],
            'signal_timing_drift': slippage_analysis['timing_drift'],
            'ready_for_live': paper_sharpe >= expected_sharpe * 0.5,  # порог 50%
        }

Мониторинг paper trading

class PaperTradingMonitor:
    async def run_dashboard(self, broker: PaperBroker):
        """Реальное время dashboard для paper trading"""
        while True:
            portfolio_value = await broker.get_portfolio_value()
            pnl = portfolio_value - broker.initial_value
            pnl_pct = pnl / broker.initial_value * 100

            print(f"\r Portfolio: ${portfolio_value:,.2f} | P&L: {pnl_pct:+.2f}% | "
                  f"Trades: {len(broker.trade_history)} | "
                  f"Open Orders: {sum(1 for o in broker.orders.values() if o.status == 'OPEN')}",
                  end='', flush=True)

            await asyncio.sleep(5)

Минимальный период paper trading перед запуском в live — 2–4 недели для стратегий с частыми сделками, 2–3 месяца для долгосрочных. Если за этот период нет критических инцидентов и результаты близки к ожидаемым из бэктеста — стратегия готова к real trading с небольшим начальным капиталом.