Multi-symbol валидация: проверяйте стратегию на всех парах

MarketMaker.cc Team
Количественные исследования и стратегии

MarketMaker.cc Team
Количественные исследования и стратегии
Статья из серии «Бэктесты без иллюзий»
Вы оптимизировали стратегию на ETHUSDT. 25 месяцев данных, 12+ параметров. Бэктест показывает PnL +55%, 500 сделок, MaxDD -0.9%, позиция открыта 15% времени. Equity curve плавно растёт. Параметры прошли plateau analysis — оптимум выглядит широким. Walk-forward даёт WFER > 0.6. Monte Carlo bootstrap показывает положительный 5-й перцентиль.
Всё идеально. Кроме одного: вы протестировали стратегию на одном инструменте.
Запускаете тот же алгоритм с теми же параметрами на BTCUSDT — PnL +8%. На SOLUSDT — PnL -12%. На DOGEUSDT — PnL -34%. Стратегия, прошедшая все проверки на ETH, оказывается убыточной на большинстве других пар.
Это не баг. Это single-symbol trap — одна из самых распространённых и коварных форм overfitting в алготрейдинге.
Оптимизация стратегии на одном символе — это, по сути, подгонка под ценовую динамику конкретного актива. Даже если вы провели walk-forward, даже если bootstrap показывает широкие доверительные интервалы — все эти проверки выполнены внутри одного временного ряда.
Walk-forward проверяет робастность по времени: работают ли параметры на будущих данных того же инструмента. Monte Carlo проверяет робастность по порядку сделок: выдержит ли стратегия иную последовательность. Но ни один из этих методов не проверяет робастность по инструменту: работает ли стратегия на других активах с другими характеристиками.
Если стратегия прибыльна только на ETHUSDT — она захватила не рыночную неэффективность, а специфическую структуру ценового ряда ETH:
Всё это — не edge. Это curve fitting на уровне инструмента.

Не все криптовалюты одинаковы. Для осмысленной multi-symbol валидации необходимо понимать, что инструменты делятся на группы с принципиально разными характеристиками.
Высокая ликвидность, относительно низкая волатильность, институциональный поток. Корреляция с макро (S&P 500, DXY, ставки ФРС). Глубокие стаканы, узкие спреды, стабильные funding rates. Типичная дневная волатильность: 2-4%.
Умеренная ликвидность, повышенная волатильность. Движения часто обусловлены секторной динамикой (L1 vs L2, DeFi vs инфра). Funding rates более волатильны. Спреды шире. Типичная дневная волатильность: 4-6%.
Мем-коины и нарративные токены. Высокая волатильность, низкая корреляция с фундаментальными факторами. Движения определяются социальными сетями, листингами, нарративами. Тонкие стаканы на некоторых биржах. Типичная дневная волатильность: 6-10%.
Экстремальная волатильность, тонкие стаканы, риск манипуляций. Часто недостаточная история для полноценного бэктеста. Типичная дневная волатильность: 10-20%+.
| Характеристика | Tier 1 | Tier 2 | Tier 3 | Tier 4 |
|---|---|---|---|---|
| Дневная волатильность | 2-4% | 4-6% | 6-10% | 10-20%+ |
| Средний спред (perps) | 0.01-0.02% | 0.02-0.05% | 0.05-0.15% | 0.1-0.5%+ |
| Глубина стакана (top 5 bps) | $5-50M | $1-10M | $100K-2M | $10K-200K |
| Funding rate (средний abs) | 0.005-0.01% | 0.01-0.03% | 0.02-0.08% | 0.05-0.2%+ |
| Корреляция с BTC | 0.85-0.95 | 0.6-0.85 | 0.3-0.7 | 0.1-0.5 |
| Минимальная история | 5+ лет | 2-5 лет | 6 мес - 3 года | < 6 мес |
Каждый тир — это отдельный «мир» с собственной микроструктурой. Стратегия, настроенная на Tier 1, попадает в чужеродную среду при переходе на Tier 3.
Выберите символ для оптимизации — например, ETHUSDT. Проведите полный пайплайн: Optuna-оптимизация, plateau analysis, walk-forward. Зафиксируйте параметры.
Запустите стратегию с теми же параметрами на 5-10 символах из того же тира. Для Tier 1 это ограничено (BTC + ETH), но для Tier 2 и Tier 3 символов достаточно.
Запустите стратегию на 3-5 символах из каждого другого тира. Это самый жёсткий тест: если стратегия работает на ETHUSDT (Tier 1) и на DOGEUSDT (Tier 3), вероятность curve fitting минимальна.
Агрегируйте метрики по тирам и оцените cross-symbol robustness.
Для каждого символа фиксируем:
Стратегия проходит multi-symbol валидацию, если:
Рассмотрим конкретный пример. Три стратегии (Strategy A, Strategy B, Strategy C), оптимизированные на ETHUSDT, протестированы на 12 символах из четырёх тиров.
Параметры: PnL +55%, ~500 сделок, ~15% active time, MaxDD ~0.9%.
| Символ | Tier | PnL | MaxDD | N trades | Win rate | PnL/active day |
|---|---|---|---|---|---|---|
| ETHUSDT* | 1 | +55.2% | -0.9% | 491 | 52.1% | 0.48% |
| BTCUSDT | 1 | +31.4% | -1.8% | 478 | 50.8% | 0.27% |
| SOLUSDT | 2 | +22.7% | -3.1% | 512 | 49.2% | 0.18% |
| BNBUSDT | 2 | +18.3% | -2.7% | 467 | 48.9% | 0.16% |
| AVAXUSDT | 2 | +8.1% | -4.5% | 498 | 47.6% | 0.07% |
| ADAUSDT | 2 | -3.2% | -6.1% | 445 | 46.1% | -0.03% |
| DOGEUSDT | 3 | -12.8% | -9.4% | 531 | 44.3% | -0.10% |
| SHIBUSDT | 3 | -18.7% | -12.1% | 487 | 43.1% | -0.16% |
| PEPEUSDT | 3 | -24.3% | -14.8% | 556 | 42.7% | -0.18% |
| ARBUSDT | 3 | -7.4% | -7.2% | 419 | 45.8% | -0.07% |
| OPUSDT | 3 | -5.1% | -6.8% | 402 | 46.2% | -0.05% |
* — символ оптимизации
Результат по тирам:
| Tier | Символов | Прибыльных | Средний PnL | Средний MaxDD |
|---|---|---|---|---|
| Tier 1 | 2 | 2 (100%) | +43.3% | -1.4% |
| Tier 2 | 4 | 3 (75%) | +11.5% | -4.1% |
| Tier 3 | 5 | 0 (0%) | -13.7% | -10.1% |
Вердикт: Strategy A работает на Tier 1-2, но полностью провалена на Tier 3. Это типичная стратегия, настроенная под низковолатильную среду. Для портфеля из blue chips и large caps — допустимо. Для универсального использования — нет.
Параметры: PnL +25%, ~40 сделок, ~5% active time.
| Символ | Tier | PnL | MaxDD | N trades | Win rate |
|---|---|---|---|---|---|
| ETHUSDT* | 1 | +25.1% | -2.3% | 38 | 57.9% |
| BTCUSDT | 1 | +21.8% | -2.8% | 41 | 56.1% |
| SOLUSDT | 2 | +19.4% | -3.5% | 44 | 54.5% |
| BNBUSDT | 2 | +16.7% | -3.1% | 37 | 54.1% |
| AVAXUSDT | 2 | +12.3% | -4.2% | 42 | 52.4% |
| ADAUSDT | 2 | +8.9% | -4.8% | 39 | 51.3% |
| DOGEUSDT | 3 | +4.2% | -6.7% | 48 | 47.9% |
| SHIBUSDT | 3 | -1.3% | -8.4% | 45 | 46.7% |
| PEPEUSDT | 3 | -3.8% | -9.1% | 52 | 46.2% |
| ARBUSDT | 3 | +6.1% | -5.8% | 40 | 50.0% |
| OPUSDT | 3 | +3.7% | -6.2% | 38 | 50.0% |
Результат по тирам:
| Tier | Символов | Прибыльных | Средний PnL | Средний MaxDD |
|---|---|---|---|---|
| Tier 1 | 2 | 2 (100%) | +23.5% | -2.6% |
| Tier 2 | 4 | 4 (100%) | +14.3% | -3.9% |
| Tier 3 | 5 | 3 (60%) | +1.8% | -7.2% |
Вердикт: Strategy B прибыльна на 9 из 11 символов (82%). Средний PnL положительный во всех тирах. MaxDD растёт предсказуемо с тиром. Это робастная стратегия с реальным рыночным edge. Несмотря на более скромный PnL на оптимизационном символе (+25% vs +55%), Strategy B значительно надёжнее Strategy A.
Параметры: PnL +300%, ~400 сделок, ~45% active time, MaxDD ~17%.
| Символ | Tier | PnL | MaxDD | N trades | Win rate |
|---|---|---|---|---|---|
| ETHUSDT* | 1 | +301.2% | -17.1% | 418 | 53.8% |
| BTCUSDT | 1 | +42.7% | -28.4% | 395 | 48.6% |
| SOLUSDT | 2 | -18.3% | -41.2% | 456 | 44.1% |
| BNBUSDT | 2 | +12.1% | -33.7% | 387 | 46.8% |
| AVAXUSDT | 2 | -31.4% | -52.8% | 471 | 42.3% |
| ADAUSDT | 2 | -44.7% | -58.1% | 412 | 40.5% |
| DOGEUSDT | 3 | -67.2% | -74.3% | 528 | 38.1% |
| PEPEUSDT | 3 | -72.1% | -81.6% | 574 | 37.4% |
Вердикт: Strategy C — классический overfitting. +301% на ETHUSDT, но катастрофические убытки на большинстве других пар. MaxDD на Tier 3 превышает 70% — это уничтожение капитала. Стратегия захватила уникальные паттерны ETH, а не рыночную неэффективность. Отклонить.
Самая распространённая причина. Параметры стратегии настроены под конкретный уровень волатильности. Если стратегия использует порог входа в 2% — для ETH с дневной волатильностью 3% это разумный фильтр. Для DOGE с дневной волатильностью 8% — этот порог срабатывает слишком часто, генерируя массу ложных сигналов.
Аналогично, стоп-лосс на 1% адекватен для ETH, но для PEPE это нормальный «шум», и стоп выбивается десятки раз в день.
Стратегия предполагает мгновенное исполнение ордеров по текущей цене. На BTCUSDT с глубиной стакана 200K — ваш ордер на $10K сдвинет цену, и реальное исполнение будет хуже на 0.05-0.2%. За 500 сделок это 25-100% потерь только на slippage.
Каждый инструмент имеет свою микроструктуру:
Альткоины ведут себя по-разному в разных фазах рынка:
Стратегия, оптимизированная в одной фазе на одном символе, может быть оптимально настроена на лаг/лид именно этого символа относительно BTC — и этот лаг/лид изменится при смене режима.
Запускать стратегию с идентичными параметрами на всех символах — некорректно. Но полная переоптимизация на каждом символе обесценивает саму идею multi-symbol валидации (параметры станут «свои» для каждого символа).
Компромисс — нормализация параметров по волатильности:
import numpy as np
def scale_params_by_volatility(
base_params: dict,
optimization_symbol_vol: float,
target_symbol_vol: float,
vol_sensitive_params: list[str],
) -> dict:
"""
Масштабирование параметров стратегии по волатильности целевого символа.
Args:
base_params: параметры, оптимизированные на исходном символе
optimization_symbol_vol: дневная волатильность символа оптимизации
target_symbol_vol: дневная волатильность целевого символа
vol_sensitive_params: список параметров, чувствительных к волатильности
"""
vol_ratio = target_symbol_vol / optimization_symbol_vol
adjusted = base_params.copy()
for param in vol_sensitive_params:
if param in adjusted:
adjusted[param] = adjusted[param] * vol_ratio
return adjusted
base_params = {
"entry_threshold": 0.02, # 2% — порог входа
"stop_loss": 0.01, # 1% — стоп-лосс
"take_profit": 0.03, # 3% — тейк-профит
"trailing_stop": 0.008, # 0.8% — трейлинг-стоп
"atr_multiplier": 2.5, # множитель ATR (не масштабируем)
"rsi_period": 14, # период RSI (не масштабируем)
"ma_fast": 10, # быстрая MA (не масштабируем)
"ma_slow": 50, # медленная MA (не масштабируем)
}
vol_sensitive = ["entry_threshold", "stop_loss", "take_profit", "trailing_stop"]
eth_vol = 0.032 # 3.2%
doge_vol = 0.081 # 8.1%
doge_params = scale_params_by_volatility(
base_params, eth_vol, doge_vol, vol_sensitive
)
print("ETH params:", {k: f"{v:.4f}" for k, v in base_params.items() if k in vol_sensitive})
print("DOGE params:", {k: f"{v:.4f}" for k, v in doge_params.items() if k in vol_sensitive})
Вывод:
ETH params: {'entry_threshold': '0.0200', 'stop_loss': '0.0100', 'take_profit': '0.0300', 'trailing_stop': '0.0080'}
DOGE params: {'entry_threshold': '0.0506', 'stop_loss': '0.0253', 'take_profit': '0.0759', 'trailing_stop': '0.0203'}
Стоп-лосс увеличился с 1% до 2.53% — адекватно для дневной волатильности DOGE в 8.1%. Без масштабирования стоп на 1% выбивался бы по «шуму» десятки раз.
Важно: масштабируйте только ценовые пороги (входы, стопы, тейки). Периоды индикаторов (RSI, MA) и множители (ATR multiplier) обычно не масштабируются — они уже нормализованы по волатильности через сам индикатор.
Strict mode (без масштабирования): запуск с идентичными параметрами. Тест на абсолютную робастность. Если стратегия прибыльна — edge сильный.
Adaptive mode (с масштабированием): запуск с нормализованными параметрами. Тест на робастность логики стратегии, с допущением, что уровни волатильности различаются.
Рекомендуем проводить оба теста. Strict mode — для оценки «силы» edge. Adaptive mode — для практического применения.

Для количественной оценки multi-symbol робастности вводим составную метрику — Cross-Symbol Robustness Score (CSRS).
где:
где — ликвидность символа (средний дневной объём).
| Компонент | Вес | Обоснование |
|---|---|---|
| (доля прибыльных) | 0.35 | Самый важный: стратегия должна работать на большинстве |
| (средний PnL) | 0.25 | Абсолютная доходность |
| (кросс-тировая) | 0.25 | Бонус за универсальность |
| (штраф за дисперсию) | 0.15 | Штраф за нестабильность |
| CSRS | Интерпретация |
|---|---|
| > 0.7 | Отличная робастность. Стратегия работает на большинстве инструментов. |
| 0.5 — 0.7 | Хорошая робастность. Стратегия работает в своём тире и частично в других. |
| 0.3 — 0.5 | Пограничная. Стратегия работает на узком наборе символов. |
| < 0.3 | Низкая робастность. Вероятен curve fitting на уровне инструмента. |
import numpy as np
import pandas as pd
from dataclasses import dataclass, field
from typing import Callable, Optional
@dataclass
class SymbolResult:
"""Результат стратегии на одном символе."""
symbol: str
tier: int
pnl: float
max_dd: float
n_trades: int
win_rate: float
pnl_per_active_day: float
avg_daily_volume: float # ликвидность
@dataclass
class TierResult:
"""Агрегированный результат по тиру."""
tier: int
symbols: list[SymbolResult]
n_symbols: int
n_profitable: int
profit_ratio: float
avg_pnl: float
avg_max_dd: float
pnl_std: float
@dataclass
class MultiSymbolResult:
"""Полный результат multi-symbol валидации."""
symbol_results: list[SymbolResult]
tier_results: list[TierResult]
csrs: float
passed: bool
optimization_symbol: str
report: str
SYMBOL_TIERS = {
1: ["BTCUSDT", "ETHUSDT"],
2: ["SOLUSDT", "BNBUSDT", "ADAUSDT", "XRPUSDT", "AVAXUSDT"],
3: ["DOGEUSDT", "SHIBUSDT", "PEPEUSDT", "ARBUSDT", "OPUSDT"],
}
SYMBOL_VOLATILITY = {
"BTCUSDT": 0.028, "ETHUSDT": 0.032,
"SOLUSDT": 0.052, "BNBUSDT": 0.038, "ADAUSDT": 0.048,
"XRPUSDT": 0.045, "AVAXUSDT": 0.055,
"DOGEUSDT": 0.081, "SHIBUSDT": 0.092, "PEPEUSDT": 0.105,
"ARBUSDT": 0.068, "OPUSDT": 0.063,
}
SYMBOL_VOLUME = {
"BTCUSDT": 15e9, "ETHUSDT": 8e9,
"SOLUSDT": 2e9, "BNBUSDT": 1.5e9, "ADAUSDT": 800e6,
"XRPUSDT": 1.2e9, "AVAXUSDT": 500e6,
"DOGEUSDT": 1e9, "SHIBUSDT": 400e6, "PEPEUSDT": 600e6,
"ARBUSDT": 300e6, "OPUSDT": 250e6,
}
def run_multi_symbol_validation(
strategy_fn: Callable,
base_params: dict,
optimization_symbol: str,
data_loader: Callable,
vol_sensitive_params: list[str],
adaptive: bool = True,
csrs_weights: tuple = (0.35, 0.25, 0.25, 0.15),
min_profit_ratio: float = 0.6,
) -> MultiSymbolResult:
"""
Полный пайплайн multi-symbol валидации.
Args:
strategy_fn: функция стратегии (data, params) -> (pnl, max_dd, n_trades, win_rate, returns)
base_params: параметры, оптимизированные на optimization_symbol
optimization_symbol: символ оптимизации
data_loader: функция загрузки данных (symbol) -> np.ndarray
vol_sensitive_params: параметры для масштабирования по волатильности
adaptive: использовать масштабирование волатильности
csrs_weights: веса (w1, w2, w3, w4) для CSRS
min_profit_ratio: минимальная доля прибыльных символов в тире
"""
w1, w2, w3, w4 = csrs_weights
opt_vol = SYMBOL_VOLATILITY.get(optimization_symbol, 0.03)
symbol_results = []
for tier, symbols in SYMBOL_TIERS.items():
for symbol in symbols:
data = data_loader(symbol)
if data is None or len(data) < 100:
continue
if adaptive and symbol != optimization_symbol:
sym_vol = SYMBOL_VOLATILITY.get(symbol, 0.05)
params = scale_params_by_volatility(
base_params, opt_vol, sym_vol, vol_sensitive_params
)
else:
params = base_params.copy()
pnl, max_dd, n_trades, win_rate, returns = strategy_fn(data, params)
active_days = max(n_trades * 0.5, 1) # грубая оценка
pnl_per_day = pnl / active_days
symbol_results.append(SymbolResult(
symbol=symbol,
tier=tier,
pnl=pnl,
max_dd=max_dd,
n_trades=n_trades,
win_rate=win_rate,
pnl_per_active_day=pnl_per_day,
avg_daily_volume=SYMBOL_VOLUME.get(symbol, 1e6),
))
tier_results = []
tiers_present = sorted(set(r.tier for r in symbol_results))
for tier in tiers_present:
tier_symbols = [r for r in symbol_results if r.tier == tier]
n_profitable = sum(1 for r in tier_symbols if r.pnl > 0)
pnls = [r.pnl for r in tier_symbols]
tier_results.append(TierResult(
tier=tier,
symbols=tier_symbols,
n_symbols=len(tier_symbols),
n_profitable=n_profitable,
profit_ratio=n_profitable / len(tier_symbols) if tier_symbols else 0,
avg_pnl=np.mean(pnls),
avg_max_dd=np.mean([r.max_dd for r in tier_symbols]),
pnl_std=np.std(pnls),
))
all_pnls = [r.pnl for r in symbol_results]
all_volumes = [r.avg_daily_volume for r in symbol_results]
n_total = len(symbol_results)
n_profitable = sum(1 for r in symbol_results if r.pnl > 0)
r_profit = n_profitable / n_total if n_total > 0 else 0
total_vol = sum(all_volumes)
r_pnl_raw = sum(r.pnl * r.avg_daily_volume for r in symbol_results) / total_vol
r_pnl = 1 / (1 + np.exp(-r_pnl_raw * 5))
profitable_tiers = sum(1 for tr in tier_results if tr.avg_pnl > 0)
p_consistency = profitable_tiers / len(tier_results) if tier_results else 0
pnl_std = np.std(all_pnls) if len(all_pnls) > 1 else 0
pnl_mean = np.mean(all_pnls) if all_pnls else 0.01
p_variance = pnl_std / max(abs(pnl_mean), 0.01)
p_variance = min(p_variance, 5.0) # ограничиваем штраф
csrs = w1 * r_profit + w2 * r_pnl + w3 * p_consistency - w4 * (p_variance / 5.0)
csrs = max(0, min(1, csrs)) # clamp to [0, 1]
opt_tier = None
for tier, symbols in SYMBOL_TIERS.items():
if optimization_symbol in symbols:
opt_tier = tier
break
same_tier_result = next((tr for tr in tier_results if tr.tier == opt_tier), None)
passed = (
csrs >= 0.5
and (same_tier_result is None or same_tier_result.profit_ratio >= min_profit_ratio)
and np.mean(all_pnls) > 0
)
report = _generate_report(
symbol_results, tier_results, csrs, passed,
optimization_symbol, adaptive
)
return MultiSymbolResult(
symbol_results=symbol_results,
tier_results=tier_results,
csrs=csrs,
passed=passed,
optimization_symbol=optimization_symbol,
report=report,
)
def _generate_report(
symbol_results, tier_results, csrs, passed,
opt_symbol, adaptive
) -> str:
"""Генерация текстового отчёта."""
lines = []
lines.append("=" * 60)
lines.append("MULTI-SYMBOL VALIDATION REPORT")
lines.append(f"Optimization symbol: {opt_symbol}")
lines.append(f"Mode: {'adaptive' if adaptive else 'strict'}")
lines.append(f"CSRS: {csrs:.3f}")
lines.append(f"Passed: {'YES' if passed else 'NO'}")
lines.append("=" * 60)
for tr in tier_results:
lines.append(f"\n--- Tier {tr.tier} ---")
lines.append(f" Symbols: {tr.n_symbols}, Profitable: {tr.n_profitable} "
f"({tr.profit_ratio:.0%})")
lines.append(f" Avg PnL: {tr.avg_pnl:.2%}, Avg MaxDD: {tr.avg_max_dd:.2%}")
lines.append(f" PnL StdDev: {tr.pnl_std:.2%}")
for sr in tr.symbols:
marker = "*" if sr.symbol == opt_symbol else " "
status = "+" if sr.pnl > 0 else "-"
lines.append(
f" {marker} [{status}] {sr.symbol:12s} "
f"PnL={sr.pnl:+.2%} MaxDD={sr.max_dd:.2%} "
f"Trades={sr.n_trades:4d} WR={sr.win_rate:.1%}"
)
lines.append("\n" + "=" * 60)
return "\n".join(lines)
def my_strategy(data, params):
"""Ваша стратегия. Возвращает (pnl, max_dd, n_trades, win_rate, returns)."""
pass
def load_ohlcv(symbol):
"""Загрузка OHLCV данных для символа."""
pass
base_params = {
"entry_threshold": 0.02,
"stop_loss": 0.01,
"take_profit": 0.03,
"trailing_stop": 0.008,
"atr_multiplier": 2.5,
"rsi_period": 14,
"ma_fast": 10,
"ma_slow": 50,
}
result = run_multi_symbol_validation(
strategy_fn=my_strategy,
base_params=base_params,
optimization_symbol="ETHUSDT",
data_loader=load_ohlcv,
vol_sensitive_params=["entry_threshold", "stop_loss", "take_profit", "trailing_stop"],
adaptive=True,
)
print(result.report)
print(f"\nCSRS: {result.csrs:.3f}")
print(f"Passed: {result.passed}")
Не каждая стратегия должна работать на множестве инструментов. Есть легитимные случаи, когда single-symbol — нормальный подход:
Стратегия маркет-мейкинга (например, по модели Avellaneda-Stoikov) по определению привязана к конкретному стакану. Параметры зависят от конкретной микроструктуры: глубины, spread, queue position, fill rate. Тестирование на другом символе не имеет смысла — это другой стакан.
Funding rate арбитраж или кросс-биржевой арбитраж по определению привязан к конкретным парам инструментов. Валидация здесь — на других биржах с теми же парами, а не на других символах.
Если стратегия основана на специфическом свойстве одного актива (например, корреляция BTC с хэшрейтом или ETH с gas fees), multi-symbol валидация неприменима. Но таких стратегий — единицы.
Во всех остальных случаях — если стратегия основана на «общих» сигналах (MA crossover, RSI, momentum, mean reversion) — multi-symbol валидация обязательна. Если generic-стратегия работает только на одном символе, это не edge — это overfitting.
Multi-symbol валидация — это один из трёх ортогональных методов out-of-sample тестирования:
| Метод | Ось проверки | Что выявляет |
|---|---|---|
| Walk-Forward | Время | Переобучение на конкретном периоде |
| Multi-symbol | Инструмент | Переобучение на конкретном активе |
| Monte Carlo Bootstrap | Порядок сделок | Зависимость от конкретной последовательности |
Каждый метод проверяет робастность по своей оси. Стратегия может пройти walk-forward, но провалиться на multi-symbol (curve fitted к инструменту). Может пройти multi-symbol, но провалиться на Monte Carlo (зависит от удачного порядка сделок).
Максимальная защита от overfitting: использовать все три метода.
Полный пайплайн валидации:
Стратегия, прошедшая все шесть проверок, имеет минимальную вероятность оказаться артефактом overfitting.
Multi-symbol валидация раскрывает ещё один аспект: корреляции между символами. Если стратегия прибыльна на BTC и ETH, но убыточна на всех альткоинах — это информация о том, что edge привязан к высокой корреляции BTC-ETH. Подробный анализ корреляционных структур — в статье Корреляция сигналов и парный трейдинг.
Для портфелей стратегий multi-symbol результаты определяют, на каких инструментах стратегию стоит запускать. Strategy A из примера выше — только Tier 1-2. Strategy B — Tier 1-3. Это входные данные для каскадной оркестрации, где разные стратегии запускаются на разных инструментах в зависимости от их robustness profile.
Multi-symbol валидация — не опция, а обязательный шаг для любой стратегии, претендующей на обобщённый рыночный edge. Ключевые выводы:
Стратегия, работающая только на одном символе, скорее всего переобучена на специфику этого символа. Исключения: маркет-мейкинг, арбитраж, стратегии на уникальных свойствах актива.
Группировка по тирам обязательна. Нельзя сравнивать результат на BTC (Tier 1) с результатом на PEPE (Tier 3) без понимания разницы в волатильности, ликвидности и микроструктуре.
Adaptive parameter scaling — масштабирование порогов по волатильности — значительно повышает реалистичность multi-symbol тестирования.
CSRS > 0.5 — разумный минимальный порог. Стратегия должна быть прибыльна на >= 60% символов того же тира, а средний PnL по всем символам — положительный.
Walk-forward + Multi-symbol + Monte Carlo — три ортогональных оси валидации. Каждый метод ловит то, что пропускают другие. Используйте все три.
Стратегия с PnL +25% и CSRS 0.72 надёжнее стратегии с PnL +300% и CSRS 0.18. Первая зарабатывает на рыночной неэффективности. Вторая — на запоминании одного ценового ряда.
@article{soloviov2026multisymbolvalidation, author = {Soloviov, Eugen}, title = {Multi-symbol валидация: проверяйте стратегию на всех парах}, year = {2026}, url = {https://marketmaker.cc/ru/blog/post/multi-symbol-validation}, version = {0.1.0}, description = {Почему стратегия, оптимизированная на ETHUSDT, может провалиться на альткоинах. Как правильно тестировать по группам пар (blue chips, large caps, шиткоины) и какой cross-symbol robustness score считать достаточным.} }