Система сравнения крипто-торговых стратегий: метрики и бенчмаркинг

Трейдеры, активно использующие бэктестинг, часто сталкиваются с проблемой: после прогона десятков стратегий на исторических данных нет единого способа объективно их сравнить. Каждая стратегия выводит собственный набор метрик — одна показывает Sharpe ratio, другая только максимальную просадку. Выбор

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

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

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

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

Трейдеры, активно использующие бэктестинг, часто сталкиваются с проблемой: после прогона десятков стратегий на исторических данных нет единого способа объективно их сравнить. Каждая стратегия выводит собственный набор метрик — одна показывает Sharpe ratio, другая только максимальную просадку. Выбор лучшей превращается в гадание. Мы разработали систему, которая стандартизирует все результаты: приводит метрики к годовым значениям, рассчитывает дополнительные показатели (Sortino, Calmar, profit factor) и выводит сводную таблицу с ранжированием по каждому критерию. Благодаря единому классу StrategyResult все данные хранятся в одном месте, а компаратор автоматически строит рейтинг и анализирует корреляцию стратегий.

Стандартизация необходима, потому что метрики, рассчитанные на разных таймфреймах, несопоставимы. Sharpe на часовых свечах и на дневных даёт разные значения. Наш инструмент annualizes все метрики, как того требуют академические стандарты. Это позволяет сравнивать стратегии с разной частотой сделок и длительностью тестовых периодов.

Почему стандартизация метрик критична для бэктестинга?

Без единого формата сложно понять, какая стратегия действительно лучше. Sharpe ratio одной может быть рассчитан на дневных данных, другой — на часовых. Наша система приводит все метрики к годовым значениям, используя единый класс StrategyResult.

from dataclasses import dataclass import pandas as pd import numpy as np @dataclass class StrategyResult: name: str params: dict equity_curve: pd.Series trades: pd.DataFrame # Вычисленные метрики sharpe_ratio: float sortino_ratio: float calmar_ratio: float annual_return_pct: float max_drawdown_pct: float win_rate: float profit_factor: float total_trades: int avg_trade_duration_hours: float total_commission_pct: float 

Класс хранит все вычисленные метрики в одном месте. Это позволяет легко передавать результаты в компаратор.

Как мы оцениваем риск и доходность?

Каждая метрика отвечает на свой вопрос: Sharpe показывает избыточную доходность на единицу риска, Sortino учитывает только отрицательную волатильность, Calmar — отношение доходности к максимальной просадке. Мы рассчитываем их автоматически из equity curve.

Метрика Что показывает Формула (annualized)
Sharpe ratio Доходность за риск (mean_return - risk_free) / std_return * sqrt(252)
Sortino ratio Доходность за downside-риск (mean_return - risk_free) / downside_std * sqrt(252)
Calmar ratio Доходность к просадке annual_return / max_drawdown
Profit factor Выигрыш/проигрыш gross_profit / gross_loss
Win rate Доля прибыльных сделок wins / total_trades

Таблица помогает быстро понять, какая стратегия лучше по интересующему критерию.

Сравнение стратегий с бенчмарком

Мы добавляем Buy & Hold benchmark, чтобы оценить, превосходит ли стратегия пассивное инвестирование. Компаратор StrategyComparator ранжирует стратегии по каждой метрике и выводит общий рейтинг.

class StrategyComparator: def __init__(self, backtester, benchmark_data: pd.Series = None): self.backtester = backtester self.benchmark = benchmark_data # Buy & Hold BTC для сравнения def compare(self, strategies: list[dict], data: pd.DataFrame) -> ComparisonReport: results = [] for strategy_config in strategies: result = self.backtester.run( strategy_class=strategy_config['class'], params=strategy_config['params'], data=data, name=strategy_config['name'], ) results.append(result) if self.benchmark is not None: bh_return = (self.benchmark.iloc[-1] / self.benchmark.iloc[0] - 1) results.append(self._create_buyhold_result(self.benchmark)) return self.build_report(results) def build_report(self, results: list[StrategyResult]) -> ComparisonReport: comparison_df = pd.DataFrame([{ 'Strategy': r.name, 'Annual Return %': round(r.annual_return_pct, 2), 'Sharpe Ratio': round(r.sharpe_ratio, 3), 'Sortino Ratio': round(r.sortino_ratio, 3), 'Max Drawdown %': round(r.max_drawdown_pct, 2), 'Calmar Ratio': round(r.calmar_ratio, 3), 'Win Rate %': round(r.win_rate * 100, 1), 'Profit Factor': round(r.profit_factor, 2), 'Total Trades': r.total_trades, 'Avg Trade Hours': round(r.avg_trade_duration_hours, 1), 'Commission Drag %': round(r.total_commission_pct, 2), } for r in results]) rankings = self._compute_rankings(comparison_df) correlations = self._compute_correlations(results) return ComparisonReport( summary=comparison_df, rankings=rankings, correlations=correlations, strategies=results, ) def _compute_rankings(self, df: pd.DataFrame) -> pd.DataFrame: rankings = pd.DataFrame({'Strategy': df['Strategy']}) for metric, ascending in [ ('Annual Return %', False), ('Sharpe Ratio', False), ('Max Drawdown %', True), ('Profit Factor', False), ('Win Rate %', False), ]: if metric in df.columns: rankings[f'Rank: {metric}'] = df[metric].rank(ascending=ascending).astype(int) rank_cols = [c for c in rankings.columns if c.startswith('Rank:')] rankings['Overall Rank'] = rankings[rank_cols].mean(axis=1).rank().astype(int) return rankings.sort_values('Overall Rank') def _compute_correlations(self, results: list[StrategyResult]) -> pd.DataFrame: returns_dict = { r.name: r.equity_curve.pct_change().dropna() for r in results } returns_df = pd.DataFrame(returns_dict).dropna() return returns_df.corr() 

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

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

График equity curves, scatter plot риск-доходность и просадки дают наглядное представление. Пример кода с Plotly:

def plot_comparison(report: ComparisonReport): import plotly.graph_objects as go from plotly.subplots import make_subplots fig = make_subplots( rows=2, cols=2, subplot_titles=[ 'Equity Curves', 'Risk-Return Scatter', 'Monthly Returns Distribution', 'Drawdown Comparison', ] ) colors = ['#00C853', '#2196F3', '#FF9800', '#E91E63', '#9C27B0'] for i, strat in enumerate(report.strategies): color = colors[i % len(colors)] fig.add_trace(go.Scatter( x=strat.equity_curve.index, y=strat.equity_curve / strat.equity_curve.iloc[0] * 100, name=strat.name, line=dict(color=color), ), row=1, col=1) fig.add_trace(go.Scatter( x=[abs(strat.max_drawdown_pct)], y=[strat.annual_return_pct], mode='markers+text', marker=dict(size=12, color=color), text=[strat.name], textposition='top center', showlegend=False, ), row=1, col=2) rolling_max = strat.equity_curve.cummax() drawdown = (strat.equity_curve - rolling_max) / rolling_max * 100 fig.add_trace(go.Scatter( x=drawdown.index, y=drawdown, fill='tozeroy', name=strat.name, line=dict(color=color), showlegend=False, opacity=0.6, ), row=2, col=2) fig.update_layout( title='Strategy Comparison Report', height=900, template='plotly_dark', ) return fig 

Визуализация особенно полезна при презентации результатов команде или инвесторам.

Как выполняется сравнение: пошагово

  1. Загрузите исторические данные и список конфигураций стратегий.
  2. Запустите бэктестинг — каждая стратегия прогоняется на едином наборе данных.
  3. Система собирает equity curves и списки сделок, затем вычисляет все метрики.
  4. Компаратор формирует сводную таблицу, ранжирует стратегии и строит матрицу корреляции.
  5. Отчёт экспортируется в HTML с графиками Plotly.

Весь процесс занимает от нескольких минут до часа в зависимости от количества стратегий и объёма данных. На практике, при 20 стратегиях на годовом датасете, расчёт занимает менее 5 минут.

Пример сводки для трёх стратегий

Стратегия Annual Return % Sharpe Ratio Max Drawdown % Profit Factor
Momentum 34.2 1.85 -18.3 2.10
MeanRev 18.7 1.12 -25.1 1.45
Breakout 41.5 2.10 -22.7 2.55

По данным таблицы, Breakout показывает лучший Sharpe и доходность, но Momentum имеет меньшую просадку. Система ранжирования с учётом всех метрик отдаст предпочтение Breakout, но также укажет на высокую корреляцию между ним и Momentum (если она есть).

Как портфельная оптимизация улучшает Sharpe?

Если стратегии слабо коррелированы, их можно объединить в портфель. Мы реализовали оптимизацию по Sharpe ratio:

def analyze_portfolio_combination( strategy_returns: dict[str, pd.Series], target_sharpe: float = 2.0, ) -> dict: from scipy.optimize import minimize returns_df = pd.DataFrame(strategy_returns).dropna() mean_returns = returns_df.mean() cov_matrix = returns_df.cov() def neg_sharpe(weights): portfolio_return = np.dot(weights, mean_returns) * 252 portfolio_vol = np.sqrt(np.dot(weights.T, np.dot(cov_matrix * 252, weights))) return -portfolio_return / portfolio_vol if portfolio_vol > 0 else 999 n = len(strategy_returns) constraints = [{'type': 'eq', 'fun': lambda w: np.sum(w) - 1}] bounds = [(0, 1)] * n x0 = [1/n] * n result = minimize(neg_sharpe, x0, method='SLSQP', bounds=bounds, constraints=constraints) optimal_weights = dict(zip(strategy_returns.keys(), result.x)) combined_returns = sum(ret * w for ret, w in zip(returns_df.values.T, result.x)) combined_sharpe = -result.fun return { 'optimal_weights': optimal_weights, 'combined_sharpe': combined_sharpe, 'improvement_vs_best_single': combined_sharpe - max( ret.mean() / ret.std() * np.sqrt(252) for ret in strategy_returns.values() ), } 

На практике комбинация из двух-трёх некоррелированных стратегий улучшает Sharpe на 30–50% относительно лучшей одиночной. Наш опыт разработки таких систем насчитывает более 50 проектов.

Что входит в разработку системы сравнения?

Мы предоставляем:

  • Архитектуру и проектирование модуля сравнения
  • Реализацию на Python с использованием pandas, numpy, scipy
  • Интеграцию с существующим backtester
  • Генерацию отчёта в виде DataFrame или HTML
  • Визуализацию через Plotly
  • Документацию и примеры использования
  • Поддержку после внедрения

Срок разработки системы под ключ составляет от 2 до 4 недель, стоимость рассчитывается индивидуально в зависимости от сложности интеграции. Новая система экономит до 80% времени на анализе результатов ручным способом. Мы гарантируем, что все расчёты соответствуют академическим стандартам.

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

Типичные ошибки при сравнении стратегий

  • Look-ahead bias: использование будущих данных при расчёте метрик. Мы строго разделяем in-sample и out-of-sample.
  • Survivor bias: игнорирование умерших стратегий. Включаем все протестированные.
  • Неучёт комиссий: commission drag может съесть до 2% годовых. Наш backtester их учитывает.
  • Сравнение на разных периодах: все метрики annualized, поэтому периоды могут быть разными.