← Terug naar artikelen
March 10, 2026
5 min leestijd

Multi-symbol validatie: test je strategie op alle paren

Multi-symbol validatie: test je strategie op alle paren
#algotrading
#backtest
#validation
#multi-symbol
#diversification
#crypto

Artikel uit de reeks "Backtests zonder illusies"

Je hebt een strategie geoptimaliseerd op ETHUSDT. 25 maanden aan data, 12+ parameters. De backtest laat een PnL van +55%, 500 trades, MaxDD van -0,9% zien, positie 15% van de tijd open. De equity-curve stijgt gestaag. De parameters zijn geslaagd voor de plateau-analyse — het optimum ziet er breed uit. Walk-forward levert een WFER > 0,6 op. Monte Carlo bootstrap toont een positief 5e percentiel.

Alles is perfect. Op één ding na: je hebt de strategie op één enkel instrument getest.

Je start hetzelfde algoritme met dezelfde parameters op BTCUSDT — PnL +8%. Op SOLUSDT — PnL -12%. Op DOGEUSDT — PnL -34%. De strategie die alle controles op ETH doorstond, blijkt op de meeste andere paren onrendabel.

Dit is geen bug. Dit is de single-symbol-val — een van de meest voorkomende en verraderlijke vormen van overfitting in algotrading.

De val van het enkele instrument

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

Een strategie optimaliseren op één enkel symbool komt er in feite op neer dat je haar aanpast aan de koersdynamiek van een specifiek activum. Zelfs als je walk-forward hebt uitgevoerd, zelfs als bootstrap brede betrouwbaarheidsintervallen toont — al deze controles werden uitgevoerd binnen één enkele tijdreeks.

Walk-forward controleert robuustheid over tijd: werken de parameters op toekomstige data van hetzelfde instrument. Monte Carlo controleert robuustheid over de volgorde van trades: kan de strategie een andere sequentie doorstaan. Maar geen van deze methoden controleert robuustheid over instrumenten: werkt de strategie op andere activa met andere kenmerken.

Als een strategie alleen op ETHUSDT winstgevend is, heeft ze geen marktinefficiëntie gevangen, maar de specifieke structuur van de prijsreeks van ETH:

  • Kenmerkende candlestick-patronen die uniek zijn voor ETH
  • Specifieke volatiliteitsniveaus waarop de drempels zijn afgestemd
  • De liquiditeits- en microstructuurspecifieke kenmerken van dit ene paar
  • Correlatie met BTC, kenmerkend voor een bepaalde periode

Niets hiervan is een edge. Dit is curve fitting op instrumentniveau.

Symbolengroepen (tiers) in de cryptomarkt

Symbol tier characteristics matrix

Niet alle cryptovaluta zijn gelijk. Voor een zinvolle multi-symbol validatie moet je begrijpen dat instrumenten worden onderverdeeld in groepen met fundamenteel verschillende kenmerken.

Tier 1: Blue chips (BTC, ETH)

Hoge liquiditeit, relatief lage volatiliteit, institutionele flow. Correlatie met macro (S&P 500, DXY, Fed-rentes). Diepe orderboeken, krappe spreads, stabiele funding rates. Typische dagelijkse volatiliteit: 2-4%.

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

Matige liquiditeit, verhoogde volatiliteit. Bewegingen worden vaak gedreven door sectordynamiek (L1 vs L2, DeFi vs infra). Funding rates zijn volatieler. Spreads zijn breder. Typische dagelijkse volatiliteit: 4-6%.

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

Meme coins en narratieve tokens. Hoge volatiliteit, lage correlatie met fundamentele factoren. Bewegingen worden bepaald door social media, listings, narratieven. Dunne orderboeken op sommige exchanges. Typische dagelijkse volatiliteit: 6-10%.

Tier 4: Low caps (nieuwe listings)

Extreme volatiliteit, dunne orderboeken, manipulatierisico. Vaak onvoldoende historie voor een volledige backtest. Typische dagelijkse volatiliteit: 10-20%+.

Samenvattende tabel van kenmerken

Kenmerk Tier 1 Tier 2 Tier 3 Tier 4
Dagelijkse volatiliteit 2-4% 4-6% 6-10% 10-20%+
Gemiddelde spread (perps) 0.01-0.02% 0.02-0.05% 0.05-0.15% 0.1-0.5%+
Orderboekdiepte (top 5 bps) $5-50M $1-10M $100K-2M $10K-200K
Funding rate (gem. abs.) 0.005-0.01% 0.01-0.03% 0.02-0.08% 0.05-0.2%+
Correlatie met BTC 0.85-0.95 0.6-0.85 0.3-0.7 0.1-0.5
Minimale historie 5+ jaar 2-5 jaar 6 mnd - 3 jaar < 6 mnd

Elke tier is een eigen "wereld" met haar eigen microstructuur. Een strategie afgestemd op Tier 1 komt in een vreemde omgeving terecht bij verplaatsing naar Tier 3.

Methodologie voor multi-symbol validatie

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

Stap 1: Optimaliseren op één symbool

Kies een symbool voor optimalisatie — bijvoorbeeld ETHUSDT. Voer de volledige pipeline uit: Optuna-optimalisatie, plateau-analyse, walk-forward. Zet de parameters vast.

Stap 2: Testen op symbolen uit dezelfde tier

Voer de strategie uit met dezelfde parameters op 5-10 symbolen uit dezelfde tier. Voor Tier 1 is dit beperkt (BTC + ETH), maar voor Tier 2 en Tier 3 zijn er genoeg symbolen.

Stap 3: Testen op symbolen uit andere tiers

Voer de strategie uit op 3-5 symbolen uit elke andere tier. Dit is de zwaarste test: als de strategie werkt op ETHUSDT (Tier 1) en op DOGEUSDT (Tier 3), is de kans op curve fitting minimaal.

Stap 4: Resultaten per groep analyseren

Aggregeer de metrics per tier en beoordeel de cross-symbol robuustheid.

Metrics voor elk symbool

Registreer voor elk symbool:

  • PnL — totaalrendement
  • MaxDD — maximale drawdown
  • N trades — aantal trades
  • Win rate — aandeel winstgevende trades
  • PnL/actieve dag — rendement per eenheid actieve tijd (meer details in PnL per actieve tijd)

Slagingscriteria

Een strategie slaagt voor multi-symbol validatie als:

  1. Winstgevend op >= 60% van de symbolen in dezelfde tier
  2. De gemiddelde PnL over de groep positief is
  3. MaxDD niet drastisch toeneemt (niet meer dan 2-3x ten opzichte van het optimalisatiesymbool)
  4. Als de strategie ALLEEN op het optimalisatiesymbool winstgevend is — afwijzen

Voorbeeld: drie strategieën, drie uitkomsten

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

Laten we een concreet voorbeeld bekijken. Drie strategieën (Strategie A, Strategie B, Strategie C), geoptimaliseerd op ETHUSDT, getest op 12 symbolen verspreid over vier tiers.

Strategie A (geoptimaliseerd op ETHUSDT)

Parameters: PnL +55%, ~500 trades, ~15% actieve tijd, MaxDD ~0,9%.

Symbool Tier PnL MaxDD N trades Win rate PnL/actieve dag
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%

* — optimalisatiesymbool

Resultaten per tier:

Tier Symbolen Winstgevend Gem. PnL Gem. 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%

Verdict: Strategie A werkt op Tier 1-2, maar faalt volledig op Tier 3. Dit is een typische strategie afgestemd op een omgeving met lage volatiliteit. Voor een portfolio van blue chips en large caps — acceptabel. Voor universeel gebruik — nee.

Strategie B (geoptimaliseerd op ETHUSDT)

Parameters: PnL +25%, ~40 trades, ~5% actieve tijd.

Symbool 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%

Resultaten per tier:

Tier Symbolen Winstgevend Gem. PnL Gem. 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%

Verdict: Strategie B is winstgevend op 9 van de 11 symbolen (82%). De gemiddelde PnL is positief over alle tiers. MaxDD groeit voorspelbaar met de tier. Dit is een robuuste strategie met een echte marktedge. Ondanks de bescheidener PnL op het optimalisatiesymbool (+25% versus +55%) is Strategie B aanzienlijk betrouwbaarder dan Strategie A.

Strategie C (geoptimaliseerd op ETHUSDT)

Parameters: PnL +300%, ~400 trades, ~45% actieve tijd, MaxDD ~17%.

Symbool 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%

Verdict: Strategie C is klassieke overfitting. +301% op ETHUSDT, maar catastrofale verliezen op de meeste andere paren. MaxDD op Tier 3 overschrijdt 70% — dit is kapitaalvernietiging. De strategie ving unieke patronen van ETH, geen marktinefficiëntie. Afwijzen.

Waarom strategieën falen op andere symbolen

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

1. Volatiliteitsmismatch

De meest voorkomende reden. De strategieparameters zijn afgestemd op een specifiek volatiliteitsniveau. Als de strategie een entreedrempel van 2% gebruikt — is dit voor ETH met 3% dagelijkse volatiliteit een redelijk filter. Voor DOGE met 8% dagelijkse volatiliteit triggert deze drempel te vaak, wat een massa valse signalen genereert.

Evenzo is een stop loss van 1% adequaat voor ETH, maar voor PEPE is dit normale "ruis", en de stop wordt tientallen keren per dag geraakt.

2. Liquiditeitsverschillen

De strategie gaat uit van directe orderuitvoering tegen de huidige prijs. Bij BTCUSDT met 50Morderboekdieptebinnen5bpsisditrealistisch.BijARBUSDTmet50M orderboekdiepte binnen 5 bps — is dit realistisch. Bij ARBUSDT met 200K diepte — zal jouw order van $10K de prijs bewegen, en de daadwerkelijke uitvoering zal 0,05-0,2% slechter zijn. Over 500 trades betekent dit 25-100% verlies alleen al door slippage.

3. Marktmicrostructuur

Elk instrument heeft zijn eigen microstructuur:

  • Funding rates: bij BTC is de funding consistent positief in een bullmarkt (+0,01% elke 8 uur). Bij meme coins kan de funding springen van -0,3% naar +0,5%. Meer details in Funding rates vreten je leverage op.
  • Spread: op Tier 1 is de spread 0,01%, op Tier 4 — 0,5%. Een strategie met kleine take-profits kan niet winstgevend zijn wanneer de spread de take-omvang overschrijdt.
  • Manipulatiepatronen: wicks, spoofing, wash trading — manifesteren zich anders op elke tier.

4. Regimegevoeligheid

Altcoins gedragen zich anders in verschillende marktfasen:

  • In een bulltrend presteren altcoins beter dan BTC (bèta > 1)
  • In een beertrend vallen altcoins harder dan BTC
  • In een zijwaartse markt kunnen altcoins correleren met BTC of bewegen op basis van hun eigen narratieven

Een strategie geoptimaliseerd in één fase op één symbool kan optimaal zijn afgestemd op de lag/lead van precies dat symbool ten opzichte van BTC — en deze lag/lead zal veranderen wanneer het regime verschuift.

Adaptieve parameterschaling

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

Een strategie met identieke parameters op alle symbolen uitvoeren is onjuist. Maar een volledige heroptimalisatie op elk symbool druist in tegen het hele doel van multi-symbol validatie (parameters worden "inheems" aan elk symbool).

Het compromis is parameternormalisatie op basis van volatiliteit:

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

Uitvoer:

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

De stop loss steeg van 1% naar 2,53% — passend bij de dagelijkse volatiliteit van 8,1% van DOGE. Zonder schaling zou een stop van 1% tientallen keren geraakt worden door "ruis".

Belangrijk: schaal alleen prijsdrempels (entries, stops, takes). Indicatorperiodes (RSI, MA) en multipliers (ATR-multiplier) worden meestal niet geschaald — ze zijn al genormaliseerd op volatiliteit via de indicator zelf.

Twee validatiemodi

  1. Strikte modus (zonder schaling): uitvoeren met identieke parameters. Een test van absolute robuustheid. Als de strategie winstgevend is — is de edge sterk.

  2. Adaptieve modus (met schaling): uitvoeren met genormaliseerde parameters. Een test van de robuustheid van de strategielogica, rekening houdend met het feit dat volatiliteitsniveaus verschillen.

We raden aan beide tests uit te voeren. Strikte modus — om de "kracht" van de edge te beoordelen. Adaptieve modus — voor praktische toepassing.

Cross-symbol robuustheidsscore

Multi-symbol cross-robustness radar

Voor een kwantitatieve beoordeling van multi-symbol robuustheid introduceren we een samengestelde metric — de Cross-Symbol Robustness Score (CSRS).

Formule

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}

waarbij:

  • RprofitR_{profit} — aandeel winstgevende symbolen:

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

  • RpnlR_{pnl} — genormaliseerde, op liquiditeit gewogen gemiddelde PnL:

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

waarbij lil_i de liquiditeit is van symbool ii (gemiddeld dagelijks volume).

  • PconsistencyP_{consistency} — bonus voor cross-tier consistentie:

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

  • PvarianceP_{variance} — boete voor hoge PnL-variantie tussen symbolen:

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

Standaardgewichten

Component Gewicht Motivatie
w1w_1 (winstgevend aandeel) 0.35 Het belangrijkst: strategie moet werken voor de meerderheid
w2w_2 (gem. PnL) 0.25 Absolute rendementen
w3w_3 (cross-tier) 0.25 Bonus voor universaliteit
w4w_4 (variantieboete) 0.15 Boete voor instabiliteit

CSRS-interpretatie

CSRS Interpretatie
> 0.7 Uitstekende robuustheid. Strategie werkt op de meeste instrumenten.
0.5 — 0.7 Goede robuustheid. Strategie werkt binnen haar tier en gedeeltelijk bij andere.
0.3 — 0.5 Grensgeval. Strategie werkt op een smalle set symbolen.
< 0.3 Lage robuustheid. Curve fitting op instrumentniveau is waarschijnlijk.

Volledige implementatie: multi-symbol validatiepipeline

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)

Voorbeeld van pipelinegebruik

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

Wanneer single-symbol validatie acceptabel is

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

Niet elke strategie hoeft op meerdere instrumenten te werken. Er zijn legitieme gevallen waarin single-symbol een normale aanpak is:

Market making op een specifiek orderboek

Een market-making strategie (bijvoorbeeld met het Avellaneda-Stoikov-model) is per definitie gebonden aan een specifiek orderboek. Parameters hangen af van de specifieke microstructuur: diepte, spread, wachtrijpositie, fill rate. Testen op een ander symbool is zinloos — het is een ander orderboek.

Arbitrage tussen specifieke paren

Funding rate arbitrage of cross-exchange arbitrage is per definitie gebonden aan specifieke instrumentparen. Validatie vindt hier plaats op andere exchanges met dezelfde paren, niet op andere symbolen.

Strategieën die expliciet unieke eigenschappen van een activum gebruiken

Als een strategie is gebaseerd op een specifieke eigenschap van één activum (bijvoorbeeld de correlatie van BTC met de hashrate of van ETH met gasfees), is multi-symbol validatie niet van toepassing. Maar dergelijke strategieën zijn zeldzaam.

In alle andere gevallen — als de strategie gebaseerd is op "generieke" signalen (MA-crossover, RSI, momentum, mean reversion) — is multi-symbol validatie verplicht. Als een generieke strategie alleen op één symbool werkt, is dat geen edge — het is overfitting.

Relatie met andere validatiemethoden

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

Multi-symbol validatie is een van drie orthogonale methoden van out-of-sample testen:

Methode Validatie-as Wat het onthult
Walk-Forward Tijd Overfitting op een specifieke periode
Multi-symbol Instrument Overfitting op een specifiek activum
Monte Carlo bootstrap Volgorde van trades Afhankelijkheid van een specifieke sequentie

Elke methode controleert robuustheid langs haar eigen as. Een strategie kan slagen voor walk-forward maar falen op multi-symbol (curve fitting op het instrument). Ze kan slagen voor multi-symbol maar falen op Monte Carlo (afhankelijk van een gelukkige tradevolgorde).

Maximale bescherming tegen overfitting: gebruik alle drie de methoden.

Volledige validatiepipeline:

  1. Optimalisatie van parameters op één symbool
  2. Plateau-analyse — controle van optimum-stabiliteit
  3. Walk-forward — validatie langs de tijdas (WFER > 0,5)
  4. Multi-symbol — validatie langs de instrumentas (CSRS > 0,5)
  5. Monte Carlo bootstrap — betrouwbaarheidsintervallen (5e percentiel > 0)
  6. Rekening houden met funding rates en verlies-winst asymmetrie

Een strategie die alle zes controles doorstaat, heeft een minimale kans om een overfitting-artefact te zijn.

Uitbreidingen: symboolcorrelatie en cascade-strategieën

Cryptocurrency correlation network graph with cascade strategy allocation flows

Multi-symbol validatie onthult nog een ander aspect: correlaties tussen symbolen. Als een strategie winstgevend is op BTC en ETH maar onrendabel op alle altcoins — is dit informatie dat de edge gebonden is aan de hoge BTC-ETH-correlatie. Gedetailleerde analyse van correlatiestructuren — in het artikel Signaalcorrelatie en pairs trading.

Voor strategieportfolio's bepalen multi-symbol resultaten op welke instrumenten een strategie moet worden gelanceerd. Strategie A uit het voorbeeld hierboven — alleen Tier 1-2. Strategie B — Tier 1-3. Dit is invoerdata voor cascade-orchestratie, waarbij verschillende strategieën op verschillende instrumenten worden gelanceerd afhankelijk van hun robuustheidsprofiel.

Conclusie

Multi-symbol validatie is niet optioneel — het is een verplichte stap voor elke strategie die een gegeneraliseerde marktedge claimt. Belangrijkste conclusies:

  1. Een strategie die alleen op één symbool werkt, is hoogstwaarschijnlijk overfit op de specifieke kenmerken van dat symbool. Uitzonderingen: market making, arbitrage, strategieën gebaseerd op unieke eigenschappen van een activum.

  2. Tier-groepering is verplicht. Je kunt resultaten op BTC (Tier 1) niet vergelijken met resultaten op PEPE (Tier 3) zonder de verschillen in volatiliteit, liquiditeit en microstructuur te begrijpen.

  3. Adaptieve parameterschaling — drempelnormalisatie op basis van volatiliteit — verbetert het realisme van multi-symbol testen aanzienlijk.

  4. CSRS > 0,5 — een redelijke minimumdrempel. De strategie zou winstgevend moeten zijn op >= 60% van de symbolen in dezelfde tier, en de gemiddelde PnL over alle symbolen zou positief moeten zijn.

  5. Walk-forward + multi-symbol + Monte Carlo — drie orthogonale validatie-assen. Elke methode vangt wat de andere missen. Gebruik alle drie.

Een strategie met PnL +25% en CSRS 0,72 is betrouwbaarder dan een strategie met PnL +300% en CSRS 0,18. De eerste verdient aan marktinefficiëntie. De tweede verdient aan het uit het hoofd leren van één enkele prijsreeks.


Nuttige links

  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

Citatie

@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.}
}
Disclaimer: De informatie in dit artikel is uitsluitend bedoeld voor educatieve en informatieve doeleinden en vormt geen financieel, beleggings- of handelsadvies. Het handelen in cryptovaluta brengt een aanzienlijk risico op verlies met zich mee.

Auteurs

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

Blijf de markt voor

Abonneer je op onze nieuwsbrief voor exclusieve AI-handelsinzichten, marktanalyses en platformupdates.

We respecteren je privacy. Je kunt je op elk moment afmelden.