← Voltar aos artigos
March 10, 2026
5 min read

Validação multi-símbolo: teste sua estratégia em todos os pares

Validação multi-símbolo: teste sua estratégia em todos os pares
#algotrading
#backtest
#validation
#multi-symbol
#diversification
#crypto

Artigo da série "Backtests sem ilusões"

Você otimizou uma estratégia em ETHUSDT. 25 meses de dados, 12+ parâmetros. O backtest mostra PnL +55%, 500 trades, MaxDD -0,9%, posição aberta 15% do tempo. A curva de equity sobe suavemente. Os parâmetros passaram pela análise de platô — o ótimo parece amplo. O walk-forward rende um WFER > 0,6. O bootstrap de Monte Carlo mostra um 5º percentil positivo.

Tudo é perfeito. Exceto por uma coisa: você testou a estratégia em um único instrumento.

Você lança o mesmo algoritmo com os mesmos parâmetros em BTCUSDT — PnL +8%. Em SOLUSDT — PnL -12%. Em DOGEUSDT — PnL -34%. A estratégia que passou em todas as verificações no ETH se revela não lucrativa na maioria dos outros pares.

Isso não é um bug. É a armadilha do símbolo único — uma das formas mais comuns e traiçoeiras de overfitting no algotrading.

A armadilha do instrumento único

Single-symbol trap: one bright equity curve surrounded by failing strategies on other assets

Otimizar uma estratégia em um único símbolo é, essencialmente, ajustá-la à dinâmica de preços de um ativo específico. Mesmo que você tenha executado walk-forward, mesmo que o bootstrap mostre intervalos de confiança amplos — todas essas verificações foram realizadas dentro de uma única série temporal.

O walk-forward verifica a robustez ao longo do tempo: os parâmetros funcionam em dados futuros do mesmo instrumento. O Monte Carlo verifica a robustez em relação à ordem dos trades: a estratégia consegue suportar uma sequência diferente. Mas nenhum desses métodos verifica a robustez entre instrumentos: a estratégia funciona em outros ativos com características diferentes.

Se uma estratégia só é lucrativa em ETHUSDT, ela não capturou uma ineficiência de mercado, mas sim a estrutura específica da série de preços do ETH:

  • Padrões de candlestick característicos e únicos do ETH
  • Níveis de volatilidade específicos aos quais os limiares estão ajustados
  • As particularidades de liquidez e microestrutura desse par específico
  • Correlação com BTC, característica de um período determinado

Nada disso é um edge. É curve fitting no nível do instrumento.

Grupos de símbolos (tiers) no mercado cripto

Symbol tier characteristics matrix

Nem todas as criptomoedas são iguais. Para uma validação multi-símbolo significativa, é preciso entender que os instrumentos se dividem em grupos com características fundamentalmente diferentes.

Tier 1: Blue chips (BTC, ETH)

Alta liquidez, volatilidade relativamente baixa, fluxo institucional. Correlação com o macro (S&P 500, DXY, taxas do Fed). Livros de ordens profundos, spreads apertados, funding rates estáveis. Volatilidade diária típica: 2-4%.

Tier 2: Large caps (SOL, BNB, ADA, XRP, AVAX)

Liquidez moderada, volatilidade elevada. Os movimentos costumam ser impulsionados pela dinâmica setorial (L1 vs L2, DeFi vs infra). Os funding rates são mais voláteis. Os spreads são mais amplos. Volatilidade diária típica: 4-6%.

Tier 3: Mid caps (DOGE, SHIB, PEPE, ARB, OP)

Meme coins e tokens narrativos. Alta volatilidade, baixa correlação com fatores fundamentais. Os movimentos são determinados por redes sociais, listagens, narrativas. Livros de ordens rasos em algumas exchanges. Volatilidade diária típica: 6-10%.

Tier 4: Low caps (novas listagens)

Volatilidade extrema, livros de ordens rasos, risco de manipulação. Frequentemente com histórico insuficiente para um backtest completo. Volatilidade diária típica: 10-20%+.

Tabela resumo das características

Característica Tier 1 Tier 2 Tier 3 Tier 4
Volatilidade diária 2-4% 4-6% 6-10% 10-20%+
Spread médio (perps) 0.01-0.02% 0.02-0.05% 0.05-0.15% 0.1-0.5%+
Profundidade do book (top 5 bps) $5-50M $1-10M $100K-2M $10K-200K
Funding rate (média abs.) 0.005-0.01% 0.01-0.03% 0.02-0.08% 0.05-0.2%+
Correlação com BTC 0.85-0.95 0.6-0.85 0.3-0.7 0.1-0.5
Histórico mínimo 5+ anos 2-5 anos 6 meses - 3 anos < 6 meses

Cada tier é um "mundo" separado, com sua própria microestrutura. Uma estratégia ajustada para o Tier 1 entra em um ambiente estranho ao migrar para o Tier 3.

Metodologia de validação multi-símbolo

Multi-symbol validation methodology: optimize, test same tier, test other tiers, analyze results

Passo 1: Otimizar em um único símbolo

Escolha um símbolo para a otimização — por exemplo, ETHUSDT. Execute o pipeline completo: otimização com Optuna, análise de platô, walk-forward. Fixe os parâmetros.

Passo 2: Testar em símbolos do mesmo tier

Execute a estratégia com os mesmos parâmetros em 5-10 símbolos do mesmo tier. Para o Tier 1 isso é limitado (BTC + ETH), mas para o Tier 2 e o Tier 3 há símbolos suficientes.

Passo 3: Testar em símbolos de outros tiers

Execute a estratégia em 3-5 símbolos de cada outro tier. Este é o teste mais rigoroso: se a estratégia funciona em ETHUSDT (Tier 1) e em DOGEUSDT (Tier 3), a probabilidade de curve fitting é mínima.

Passo 4: Analisar resultados por grupo

Agregue as métricas por tier e avalie a robustez cross-symbol.

Métricas para cada símbolo

Para cada símbolo, registre:

  • PnL — retorno total
  • MaxDD — drawdown máximo
  • N trades — número de operações
  • Win rate — fração de trades lucrativos
  • PnL/dia ativo — retorno por unidade de tempo ativo (mais detalhes em PnL por tempo ativo)

Critérios de aprovação

Uma estratégia passa na validação multi-símbolo se:

  1. For lucrativa em >= 60% dos símbolos do mesmo tier
  2. O PnL médio do grupo for positivo
  3. O MaxDD não aumentar drasticamente (no máximo 2-3x em relação ao símbolo de otimização)
  4. Se a estratégia só for lucrativa no símbolo de otimização — rejeitar

Exemplo: três estratégias, três resultados

Three strategies compared: A (green, partial success), B (cyan, robust), C (red, overfitted)

Vamos considerar um exemplo concreto. Três estratégias (Estratégia A, Estratégia B, Estratégia C), otimizadas em ETHUSDT, testadas em 12 símbolos de quatro tiers.

Estratégia A (otimizada em ETHUSDT)

Parâmetros: PnL +55%, ~500 trades, ~15% de tempo ativo, MaxDD ~0,9%.

Símbolo Tier PnL MaxDD N trades Win rate PnL/dia ativo
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%

* — símbolo de otimização

Resultados por tier:

Tier Símbolos Lucrativos PnL médio MaxDD médio
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%

Veredito: a Estratégia A funciona nos Tiers 1-2, mas falha completamente no Tier 3. É uma estratégia típica ajustada para um ambiente de baixa volatilidade. Para uma carteira de blue chips e large caps — aceitável. Para uso universal — não.

Estratégia B (otimizada em ETHUSDT)

Parâmetros: PnL +25%, ~40 trades, ~5% de tempo ativo.

Símbolo 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%

Resultados por tier:

Tier Símbolos Lucrativos PnL médio MaxDD médio
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%

Veredito: a Estratégia B é lucrativa em 9 dos 11 símbolos (82%). O PnL médio é positivo em todos os tiers. O MaxDD cresce de forma previsível com o tier. É uma estratégia robusta com um edge de mercado real. Apesar do PnL mais modesto no símbolo de otimização (+25% versus +55%), a Estratégia B é significativamente mais confiável do que a Estratégia A.

Estratégia C (otimizada em ETHUSDT)

Parâmetros: PnL +300%, ~400 trades, ~45% de tempo ativo, MaxDD ~17%.

Símbolo 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%

Veredito: a Estratégia C é overfitting clássico. +301% em ETHUSDT, mas perdas catastróficas na maioria dos outros pares. O MaxDD no Tier 3 ultrapassa 70% — isso é destruição de capital. A estratégia capturou padrões únicos do ETH, não uma ineficiência de mercado. Rejeitada.

Por que estratégias falham em outros símbolos

Four factors that break strategies: volatility, liquidity, microstructure, and regime transitions

1. Desajuste de volatilidade

O motivo mais comum. Os parâmetros da estratégia estão ajustados a um nível específico de volatilidade. Se a estratégia usa um limiar de entrada de 2% — para o ETH, com 3% de volatilidade diária, este é um filtro razoável. Para o DOGE, com 8% de volatilidade diária, esse limiar dispara com frequência excessiva, gerando uma massa de sinais falsos.

Da mesma forma, um stop loss de 1% é adequado para o ETH, mas para o PEPE isso é "ruído" normal, e o stop é atingido dezenas de vezes por dia.

2. Diferenças de liquidez

A estratégia pressupõe execução instantânea de ordens ao preço atual. No BTCUSDT, com 50Mdeprofundidadedebookdentrode5bpsissoeˊrealista.NoARBUSDT,com50M de profundidade de book dentro de 5 bps — isso é realista. No ARBUSDT, com 200K de profundidade — sua ordem de $10K moverá o preço, e a execução real será 0,05-0,2% pior. Em 500 trades, isso significa 25-100% perdidos apenas com slippage.

3. Microestrutura de mercado

Cada instrumento tem sua própria microestrutura:

  • Funding rates: no BTC, o funding é consistentemente positivo em um mercado altista (+0,01% a cada 8 horas). Em meme coins, o funding pode saltar de -0,3% para +0,5%. Mais detalhes em Funding rates destroem sua alavancagem.
  • Spread: no Tier 1 o spread é de 0,01%, no Tier 4 — de 0,5%. Uma estratégia com take-profits pequenos não pode ser lucrativa quando o spread ultrapassa o tamanho do take.
  • Padrões de manipulação: wicks, spoofing, wash trading — manifestam-se de forma diferente em cada tier.

4. Sensibilidade ao regime

Os altcoins se comportam de maneira diferente em fases distintas do mercado:

  • Em uma tendência de alta, os altcoins superam o BTC (beta > 1)
  • Em uma tendência de baixa, os altcoins caem mais que o BTC
  • Em um mercado lateral, os altcoins podem se correlacionar com o BTC ou se mover conforme suas próprias narrativas

Uma estratégia otimizada em uma fase, em um símbolo, pode estar ajustada de forma ótima ao lag/lead desse símbolo específico em relação ao BTC — e esse lag/lead mudará quando o regime se alterar.

Escalonamento adaptativo de parâmetros

Adaptive parameter scaling: volatility ratio gauge with parameter sliders transforming from tight to wide thresholds

Executar uma estratégia com parâmetros idênticos em todos os símbolos é incorreto. Mas reotimizar completamente em cada símbolo anula o próprio propósito da validação multi-símbolo (os parâmetros se tornam "nativos" de cada símbolo).

O meio-termo é a normalização de parâmetros por volatilidade:

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:
    """
    Scale strategy parameters by target symbol volatility.

    Args:
        base_params: parameters optimized on the original symbol
        optimization_symbol_vol: daily volatility of the optimization symbol
        target_symbol_vol: daily volatility of the target symbol
        vol_sensitive_params: list of volatility-sensitive parameters
    """
    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% — entry threshold
    "stop_loss": 0.01,           # 1% — stop loss
    "take_profit": 0.03,         # 3% — take profit
    "trailing_stop": 0.008,      # 0.8% — trailing stop
    "atr_multiplier": 2.5,       # ATR multiplier (not scaled)
    "rsi_period": 14,            # RSI period (not scaled)
    "ma_fast": 10,               # fast MA (not scaled)
    "ma_slow": 50,               # slow MA (not scaled)
}

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})

Saída:

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'}

O stop loss aumentou de 1% para 2,53% — apropriado para a volatilidade diária de 8,1% do DOGE. Sem o escalonamento, um stop de 1% seria atingido por "ruído" dezenas de vezes.

Importante: escalone apenas os limiares de preço (entradas, stops, takes). Períodos de indicadores (RSI, MA) e multiplicadores (multiplicador ATR) normalmente não são escalonados — já estão normalizados por volatilidade através do próprio indicador.

Dois modos de validação

  1. Modo estrito (sem escalonamento): execução com parâmetros idênticos. Um teste de robustez absoluta. Se a estratégia for lucrativa — o edge é forte.

  2. Modo adaptativo (com escalonamento): execução com parâmetros normalizados. Um teste de robustez da lógica da estratégia, considerando que os níveis de volatilidade diferem.

Recomendamos executar ambos os testes. O modo estrito — para avaliar a "força" do edge. O modo adaptativo — para aplicação prática.

Pontuação de robustez cross-symbol

Multi-symbol cross-robustness radar

Para uma avaliação quantitativa da robustez multi-símbolo, introduzimos uma métrica composta — o Cross-Symbol Robustness Score (CSRS).

Fórmula

CSRS=w1Rprofit+w2Rpnl+w3Pconsistencyw4Pvariance\text{CSRS} = w_1 \cdot R_{profit} + w_2 \cdot R_{pnl} + w_3 \cdot P_{consistency} - w_4 \cdot P_{variance}

onde:

  • RprofitR_{profit} — fração de símbolos lucrativos:

Rprofit=NprofitableNtotalR_{profit} = \frac{N_{profitable}}{N_{total}}

  • RpnlR_{pnl} — PnL médio normalizado, ponderado por liquidez:

Rpnl=i=1NliPnLii=1NliR_{pnl} = \frac{\sum_{i=1}^{N} l_i \cdot \text{PnL}_i}{\sum_{i=1}^{N} l_i}

onde lil_i é a liquidez do símbolo ii (volume diário médio).

  • PconsistencyP_{consistency} — bônus por consistência entre tiers:

Pconsistency=Nprofitable_tiersNtotal_tiersP_{consistency} = \frac{N_{profitable\_tiers}}{N_{total\_tiers}}

  • PvarianceP_{variance} — penalidade por alta variância de PnL entre símbolos:

Pvariance=σ(PnL1,,PnLN)max(PnLˉ,0.01)P_{variance} = \frac{\sigma(\text{PnL}_1, \ldots, \text{PnL}_N)}{\max(|\bar{\text{PnL}}|, 0.01)}

Pesos padrão

Componente Peso Justificativa
w1w_1 (fração lucrativa) 0.35 O mais importante: a estratégia deve funcionar na maioria
w2w_2 (PnL médio) 0.25 Retornos absolutos
w3w_3 (cross-tier) 0.25 Bônus por universalidade
w4w_4 (penalidade de variância) 0.15 Penalidade por instabilidade

Interpretação do CSRS

CSRS Interpretação
> 0.7 Robustez excelente. A estratégia funciona na maioria dos instrumentos.
0.5 — 0.7 Boa robustez. A estratégia funciona em seu tier e parcialmente em outros.
0.3 — 0.5 Limítrofe. A estratégia funciona em um conjunto restrito de símbolos.
< 0.3 Baixa robustez. Curve fitting no nível do instrumento é provável.

Implementação completa: pipeline de validação multi-símbolo

Isometric 3D data processing pipeline for multi-symbol strategy validation

import numpy as np
import pandas as pd
from dataclasses import dataclass, field
from typing import Callable, Optional

@dataclass
class SymbolResult:
    """Strategy result on a single symbol."""
    symbol: str
    tier: int
    pnl: float
    max_dd: float
    n_trades: int
    win_rate: float
    pnl_per_active_day: float
    avg_daily_volume: float  # liquidity

@dataclass
class TierResult:
    """Aggregated result by tier."""
    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:
    """Full multi-symbol validation result."""
    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:
    """
    Full multi-symbol validation pipeline.

    Args:
        strategy_fn: strategy function (data, params) -> (pnl, max_dd, n_trades, win_rate, returns)
        base_params: parameters optimized on optimization_symbol
        optimization_symbol: optimization symbol
        data_loader: data loading function (symbol) -> np.ndarray
        vol_sensitive_params: parameters to scale by volatility
        adaptive: use volatility scaling
        csrs_weights: weights (w1, w2, w3, w4) for CSRS
        min_profit_ratio: minimum fraction of profitable symbols in a tier
    """
    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)  # rough estimate
            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)  # cap the penalty

    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:
    """Generate text report."""
    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)

Exemplo de uso do pipeline

def my_strategy(data, params):
    """Your strategy. Returns (pnl, max_dd, n_trades, win_rate, returns)."""
    pass

def load_ohlcv(symbol):
    """Load OHLCV data for a symbol."""
    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}")

Quando a validação de símbolo único é aceitável

Exceptions: market-making order book, cross-exchange arbitrage, and unique asset-specific correlation patterns

Nem toda estratégia precisa funcionar em múltiplos instrumentos. Existem casos legítimos em que o símbolo único é uma abordagem normal:

Market making em um book de ofertas específico

Uma estratégia de market making (por exemplo, usando o modelo Avellaneda-Stoikov) está, por definição, vinculada a um book de ofertas específico. Os parâmetros dependem da microestrutura específica: profundidade, spread, posição na fila, fill rate. Testar em outro símbolo não faz sentido — é um book de ofertas diferente.

Arbitragem entre pares específicos

A arbitragem de funding rate ou a arbitragem cross-exchange está, por definição, vinculada a pares de instrumentos específicos. Aqui a validação é feita em outras exchanges com os mesmos pares, não em símbolos diferentes.

Estratégias que usam explicitamente propriedades únicas de um ativo

Se uma estratégia se baseia em uma propriedade específica de um ativo (por exemplo, a correlação do BTC com o hashrate ou do ETH com as taxas de gas), a validação multi-símbolo não é aplicável. Mas esse tipo de estratégia é raro.

Em todos os outros casos — se a estratégia se baseia em sinais "genéricos" (cruzamento de médias móveis, RSI, momentum, reversão à média) — a validação multi-símbolo é obrigatória. Se uma estratégia genérica só funciona em um símbolo, isso não é um edge — é overfitting.

Relação com outros métodos de validação

Three orthogonal validation axes: time (walk-forward), instrument (multi-symbol), trade order (Monte Carlo)

A validação multi-símbolo é um dos três métodos ortogonais de teste out-of-sample:

Método Eixo de validação O que revela
Walk-Forward Tempo Overfitting em um período específico
Multi-símbolo Instrumento Overfitting em um ativo específico
Bootstrap de Monte Carlo Ordem dos trades Dependência de uma sequência específica

Cada método verifica a robustez ao longo de seu próprio eixo. Uma estratégia pode passar no walk-forward mas falhar no multi-símbolo (curve fitting no instrumento). Pode passar no multi-símbolo mas falhar no Monte Carlo (dependendo de uma ordem de trades favorável).

Proteção máxima contra overfitting: use os três métodos.

Pipeline de validação completo:

  1. Otimização de parâmetros em um único símbolo
  2. Análise de platô — verificação da estabilidade do ótimo
  3. Walk-forward — validação no eixo temporal (WFER > 0,5)
  4. Multi-símbolo — validação no eixo dos instrumentos (CSRS > 0,5)
  5. Bootstrap de Monte Carlo — intervalos de confiança (5º percentil > 0)
  6. Considerar os funding rates e a assimetria entre perda e lucro

Uma estratégia que passa nas seis verificações tem probabilidade mínima de ser um artefato de overfitting.

Extensões: correlação entre símbolos e estratégias em cascata

Cryptocurrency correlation network graph with cascade strategy allocation flows

A validação multi-símbolo revela mais um aspecto: as correlações entre símbolos. Se uma estratégia é lucrativa em BTC e ETH, mas não lucrativa em todos os altcoins — isso é informação de que o edge está vinculado à alta correlação BTC-ETH. Uma análise detalhada das estruturas de correlação está no artigo Correlação de sinais e pairs trading.

Para carteiras de estratégias, os resultados multi-símbolo determinam em quais instrumentos uma estratégia deve ser lançada. A Estratégia A do exemplo acima — apenas Tier 1-2. A Estratégia B — Tier 1-3. Esses são dados de entrada para a orquestração em cascata, na qual diferentes estratégias são lançadas em diferentes instrumentos de acordo com seu perfil de robustez.

Conclusão

A validação multi-símbolo não é opcional — é uma etapa obrigatória para qualquer estratégia que reivindique um edge de mercado generalizado. Principais conclusões:

  1. Uma estratégia que só funciona em um símbolo provavelmente está overfit às particularidades desse símbolo. Exceções: market making, arbitragem, estratégias baseadas em propriedades únicas de um ativo.

  2. O agrupamento por tiers é obrigatório. Não é possível comparar resultados em BTC (Tier 1) com resultados em PEPE (Tier 3) sem entender as diferenças em volatilidade, liquidez e microestrutura.

  3. O escalonamento adaptativo de parâmetros — a normalização de limiares por volatilidade — melhora significativamente o realismo dos testes multi-símbolo.

  4. CSRS > 0,5 — um limiar mínimo razoável. A estratégia deveria ser lucrativa em >= 60% dos símbolos do mesmo tier, e o PnL médio em todos os símbolos deveria ser positivo.

  5. Walk-forward + multi-símbolo + Monte Carlo — três eixos de validação ortogonais. Cada método captura o que os outros deixam passar. Use os três.

Uma estratégia com PnL +25% e CSRS 0,72 é mais confiável do que uma estratégia com PnL +300% e CSRS 0,18. A primeira ganha com uma ineficiência de mercado. A segunda ganha decorando uma única série de preços.


Links úteis

  1. Lopez de Prado, M. — Advances in Financial Machine Learning (Wiley)
  2. Pardo, R. — The Evaluation and Optimization of Trading Strategies (Wiley)
  3. Bailey, D.H. et al. — The Probability of Backtest Overfitting
  4. Aronson, D.R. — Evidence-Based Technical Analysis
  5. Kevin Davey — Building Winning Algorithmic Trading Systems (Wiley)
  6. Harvey, C.R. & Liu, Y. — Backtesting (2015)
  7. Chan, E. — Algorithmic Trading: Winning Strategies and Their Rationale (Wiley)
  8. Binance Research — Cryptocurrency Correlation Analysis
  9. NumPy — numpy.random.choice
  10. Pandas — DataFrame

Citação

@article{soloviov2026multisymbolvalidation,
  author = {Soloviov, Eugen},
  title = {Multi-Symbol Validation: Test Your Strategy on All Pairs},
  year = {2026},
  url = {https://marketmaker.cc/en/blog/post/multi-symbol-validation},
  version = {0.1.0},
  description = {Why a strategy optimized on ETHUSDT may fail on altcoins. How to properly test across pair groups (blue chips, large caps, shitcoins) and what cross-symbol robustness score to consider sufficient.}
}
blog.disclaimer

Authors

Eugen Soloviov
Eugen Soloviov

Trading-systems engineer

Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.

Newsletter

Fique à frente do mercado

Assine nossa newsletter para insights exclusivos sobre trading com IA, análises de mercado e atualizações da plataforma.

Respeitamos sua privacidade. Cancele a inscrição a qualquer momento.