"無幻覺回測"系列文章
你在ETHUSDT上優化了策略。25個月的資料,12+個參數。回測顯示PnL +55%,500筆交易,MaxDD -0.9%,持倉時間佔15%。權益曲線平滑上升。參數通過了plateau分析——最優解看起來範圍很寬。Walk-forward給出WFER > 0.6。蒙特卡洛自助法顯示第5百分位為正。
一切看起來完美。除了一點:你只在一個品種上測試了策略。
用相同的演算法和相同的參數在BTCUSDT上執行——PnL +8%。在SOLUSDT上——PnL -12%。在DOGEUSDT上——PnL -34%。一個通過了所有ETH檢驗的策略,在大多數其他交易對上都是虧損的。
這不是bug。這是單品種陷阱(single-symbol trap)——演算法交易中最常見且最隱蔽的過擬合形式之一。
單一品種的陷阱

在單一品種上最佳化策略,本質上就是對特定資產的價格動態進行擬合。即使你進行了walk-forward,即使自助法顯示了寬置信區間——所有這些檢驗都是在同一個時間序列內部完成的。
Walk-forward檢驗的是時間維度的魯棒性:參數在同一品種的未來資料上是否有效。蒙特卡洛檢驗的是交易順序維度的魯棒性:策略能否承受不同的交易序列。但這兩種方法都無法檢驗品種維度的魯棒性:策略在具有不同特徵的其他資產上是否有效。
如果策略只在ETHUSDT上盈利——它捕獲的不是市場低效性,而是ETH價格序列的特定結構:
- ETH獨有的K線形態
- 針對特定波動率水平調整的閾值
- 該交易對特有的流動性和微觀結構
- 特定時期內與BTC的相關性
這些都不是edge,而是品種層面的曲線擬合。
加密市場的品種分組(tiers)

並非所有加密貨幣都是相同的。要進行有意義的多品種驗證,必須理解品種按具有根本不同特徵的組別進行劃分。
Tier 1:藍籌(BTC、ETH)
高流動性,相對較低的波動率,機構資金流。與宏觀因素相關(S&P 500、DXY、美聯儲利率)。深度訂單簿,窄價差,穩定的資金費率。典型日波動率:2-4%。
Tier 2:大市值(SOL、BNB、ADA、XRP、AVAX)
中等流動性,較高波動率。行情通常由板塊動態驅動(L1 vs L2,DeFi vs 基礎設施)。資金費率波動更大。價差更寬。典型日波動率:4-6%。
Tier 3:中等市值(DOGE、SHIB、PEPE、ARB、OP)
Meme幣和敘事型代幣。高波動率,與基本面因素的相關性低。行情由社交媒體、上線公告、敘事驅動。部分交易所訂單簿較薄。典型日波動率:6-10%。
Tier 4:小市值(新上線代幣)
極端波動率,薄訂單簿,存在操縱風險。通常沒有足夠的歷史資料進行完整回測。典型日波動率:10-20%+。
特徵彙總表
| 特徵 | Tier 1 | Tier 2 | Tier 3 | Tier 4 |
|---|---|---|---|---|
| 日波動率 | 2-4% | 4-6% | 6-10% | 10-20%+ |
| 平均價差(永續合約) | 0.01-0.02% | 0.02-0.05% | 0.05-0.15% | 0.1-0.5%+ |
| 訂單簿深度(前5 bps) | $5-50M | $1-10M | $100K-2M | $10K-200K |
| 資金費率(平均絕對值) | 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都是一個擁有獨特微觀結構的"世界"。針對Tier 1調整的策略,在切換到Tier 3時會進入一個完全陌生的環境。
多品種驗證方法論

步驟1:在單一品種上最佳化
選擇一個品種進行最佳化——例如ETHUSDT。執行完整的流程:Optuna最佳化、plateau分析、walk-forward。鎖定參數。
步驟2:在同tier品種上測試
用相同的參數在同tier的5-10個品種上執行策略。對於Tier 1來說選擇有限(BTC + ETH),但Tier 2和Tier 3有足夠多的品種。
步驟3:在其他tier品種上測試
在每個其他tier的3-5個品種上執行策略。這是最嚴格的測試:如果策略在ETHUSDT(Tier 1)和DOGEUSDT(Tier 3)上都有效,曲線擬合的可能性極低。
步驟4:按組分析結果
按tier彙總指標並評估cross-symbol魯棒性。
每個品種的指標
對每個品種記錄:
- PnL — 最終收益率
- MaxDD — 最大回撤
- N trades — 交易次數
- Win rate — 盈利交易比例
- PnL/active day — 每單位活躍時間的收益率(詳見按活躍時間計算PnL)
通過標準
策略通過多品種驗證的條件:
- 在同tier >= 60%的品種上盈利
- 該組的平均PnL為正
- MaxDD不會急劇增加(相對於最佳化品種不超過2-3倍)
- 如果策略僅在最佳化品種上盈利——拒絕
示例:三個策略,三種結果

來看一個具體示例。三個策略(Strategy A、Strategy B、Strategy C),在ETHUSDT上最佳化,在四個tier的12個品種上測試。
Strategy A(在ETHUSDT上最佳化)
參數:PnL +55%,約500筆交易,約15%活躍時間,MaxDD約0.9%。
| 品種 | Tier | PnL | MaxDD | 交易數 | 勝率 | PnL/活躍日 |
|---|---|---|---|---|---|---|
| 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結果:
| 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上完全失敗。這是典型的針對低波動率環境調整的策略。用於藍籌和大市值組合——可以接受。用於通用目的——不行。
Strategy B(在ETHUSDT上最佳化)
參數:PnL +25%,約40筆交易,約5%活躍時間。
| 品種 | Tier | PnL | MaxDD | 交易數 | 勝率 |
|---|---|---|---|---|---|
| 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結果:
| 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在11個品種中有9個盈利(82%)。所有tier的平均PnL均為正。MaxDD隨tier級別可預測地增長。這是一個具有真實市場edge的魯棒策略。儘管在最佳化品種上的PnL較為溫和(+25% vs +55%),Strategy B遠比Strategy A可靠。
Strategy C(在ETHUSDT上最佳化)
參數:PnL +300%,約400筆交易,約45%活躍時間,MaxDD約17%。
| 品種 | Tier | PnL | MaxDD | 交易數 | 勝率 |
|---|---|---|---|---|---|
| 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是典型的過擬合。在ETHUSDT上+301%,但在大多數其他交易對上出現災難性虧損。Tier 3的MaxDD超過70%——這意味著資金毀滅。策略捕獲了ETH獨有的模式,而非市場低效性。拒絕。
策略在其他品種上失效的原因

1. 波動率不匹配
最常見的原因。策略參數針對特定的波動率水平進行了調整。如果策略使用2%的入場閾值——對於日波動率3%的ETH來說,這是合理的過濾器。但對於日波動率8%的DOGE來說——這個閾值觸發過於頻繁,產生大量虛假訊號。
同樣,1%的止損對ETH來說是合理的,但對PEPE來說這只是正常的"噪音",止損會在一天內被觸發數十次。
2. 流動性差異
策略假設訂單按當前價格即時成交。在訂單簿深度為200K的ARBUSDT上——你$10K的訂單會推動價格,實際成交價會差0.05-0.2%。500筆交易下來,僅滑點就會造成25-100%的損失。
3. 市場微觀結構
每個品種都有自己的微觀結構:
- 資金費率: 在牛市中,BTC的資金費率穩定為正(每8小時+0.01%)。Meme幣的資金費率可能在-0.3%到+0.5%之間劇烈波動。詳見資金費率如何吞噬你的槓桿。
- 價差: Tier 1的價差為0.01%,Tier 4為0.5%。止盈較小的策略在價差超過止盈幅度時無法盈利。
- 操縱模式: 插針、欺騙掛單、刷量交易——在每個tier中表現不同。
4. 市場狀態敏感性
山寨幣在不同市場階段表現各異:
- 在牛市中,山寨幣跑贏BTC(beta > 1)
- 在熊市中,山寨幣跌幅超過BTC
- 在橫盤中,山寨幣可能與BTC相關,也可能按自身敘事波動
在某一階段對某一品種最佳化的策略,可能恰好針對該品種相對於BTC的領先/滯後關係進行了最優調整——而這種領先/滯後關係在市場狀態切換時會改變。
自適應參數縮放

用完全相同的參數在所有品種上執行策略是不正確的。但對每個品種進行完全重新最佳化會使多品種驗證本身失去意義(參數會變成每個品種"自己的")。
折中方案是按波動率歸一化參數:
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%——對於日波動率8.1%的DOGE來說是合理的。不進行縮放的話,1%的止損會因為"噪音"被觸發數十次。
重要: 只縮放價格閾值(入場、止損、止盈)。指標週期(RSI、MA)和乘數(ATR multiplier)通常不需要縮放——它們通過指標本身已經按波動率歸一化了。
兩種驗證模式
-
嚴格模式(不縮放): 使用完全相同的參數執行。測試絕對魯棒性。如果策略仍然盈利——edge很強。
-
自適應模式(帶縮放): 使用歸一化後的參數執行。測試策略邏輯的魯棒性,允許波動率水平存在差異。
建議兩種測試都進行。嚴格模式——評估edge的"強度"。自適應模式——用於實際應用。
Cross-symbol魯棒性評分

為了量化評估多品種魯棒性,我們引入一個複合指標——Cross-Symbol Robustness Score (CSRS)。
公式
其中:
- — 盈利品種佔比:
- — 歸一化加權平均PnL(按流動性加權):
其中 為品種 的流動性(平均日成交量)。
- — 跨tier一致性獎勵:
- — 品種間PnL高方差懲罰:
預設權重
| 組成部分 | 權重 | 依據 |
|---|---|---|
| (盈利佔比) | 0.35 | 最重要:策略必須在大多數品種上有效 |
| (平均PnL) | 0.25 | 絕對收益率 |
| (跨tier一致性) | 0.25 | 通用性獎勵 |
| (方差懲罰) | 0.15 | 不穩定性懲罰 |
CSRS解讀
| CSRS | 解讀 |
|---|---|
| > 0.7 | 優秀的魯棒性。策略在大多數品種上有效。 |
| 0.5 — 0.7 | 良好的魯棒性。策略在自身tier和部分其他tier上有效。 |
| 0.3 — 0.5 | 臨界水平。策略僅在少數品種上有效。 |
| < 0.3 | 低魯棒性。可能存在品種層面的曲線擬合。 |
完整實現:多品種驗證流水線

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汇总的结果。"""
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:
"""多品种验证的完整结果。"""
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:
"""
完整的多品种验证流水线。
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: CSRS的权重 (w1, w2, w3, w4)
min_profit_ratio: 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) # 粗略估计
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)) # 限制在[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}")
何時可以接受單品種驗證

並非每個策略都必須在多個品種上有效。在以下合理情況下,單品種驗證是正常的做法:
特定訂單簿上的做市
做市策略(例如基於Avellaneda-Stoikov模型)天然繫結到特定訂單簿。參數依賴於具體的微觀結構:深度、價差、佇列位置、成交率。在其他品種上測試沒有意義——那是另一個訂單簿。
特定交易對之間的套利
資金費率套利或跨交易所套利天然繫結到特定的品種對。這裡的驗證應該在相同交易對的不同交易所上進行,而非在不同品種上。
明確利用資產獨有屬性的策略
如果策略基於某個資產的特定屬性(例如BTC與算力的相關性,或ETH與gas費的相關性),多品種驗證不適用。但這類策略非常少見。
在所有其他情況下——如果策略基於"通用"訊號(MA交叉、RSI、動量、均值迴歸)——多品種驗證是必須的。如果通用策略只在一個品種上有效,這不是edge——這是過擬合。
與其他驗證方法的關係

多品種驗證是三種正交的樣本外測試方法之一:
| 方法 | 檢驗維度 | 發現的問題 |
|---|---|---|
| Walk-Forward | 時間 | 對特定時期的過擬合 |
| 多品種驗證 | 品種 | 對特定資產的過擬合 |
| 蒙特卡洛自助法 | 交易順序 | 對特定交易序列的依賴 |
每種方法沿各自的維度檢驗魯棒性。策略可能通過walk-forward,但在多品種驗證上失敗(對品種的曲線擬合)。可能通過多品種驗證,但在蒙特卡洛上失敗(依賴於特定的交易順序)。
最大程度防止過擬合:三種方法全部使用。
完整驗證流水線:
- 在單一品種上最佳化參數
- Plateau分析——檢驗最優解的穩定性
- Walk-forward——時間維度檢驗(WFER > 0.5)
- 多品種驗證——品種維度檢驗(CSRS > 0.5)
- 蒙特卡洛自助法——置信區間(第5百分位 > 0)
- 考慮資金費率和虧損不對稱性
通過全部六項檢驗的策略,成為過擬合產物的機率最小。
擴充:品種相關性與級聯策略

多品種驗證還揭示了另一個維度:品種間的相關性。如果策略在BTC和ETH上盈利,但在所有山寨幣上虧損——這說明edge繫結在BTC-ETH的高相關性上。關於相關性結構的詳細分析,請參閱訊號相關性與配對交易。
對於策略組合,多品種結果決定了應該在哪些品種上執行策略。上述示例中的Strategy A——僅限Tier 1-2。Strategy B——Tier 1-3。這些是級聯編排的輸入資料,其中不同策略根據其魯棒性特徵在不同品種上執行。
結論
多品種驗證不是可選項,而是任何聲稱具有泛化市場edge的策略的必要步驟。核心要點:
-
只在一個品種上有效的策略,很可能對該品種的特性過擬合了。 例外情況:做市、套利、基於資產獨有屬性的策略。
-
按tier分組是必須的。 不能在不理解波動率、流動性和微觀結構差異的情況下,將BTC(Tier 1)的結果與PEPE(Tier 3)的結果進行比較。
-
自適應參數縮放——按波動率縮放閾值——顯著提高了多品種測試的現實性。
-
CSRS > 0.5——合理的最低門檻。策略應在同tier >= 60%的品種上盈利,且所有品種的平均PnL為正。
-
Walk-forward + 多品種驗證 + 蒙特卡洛——三個正交的驗證維度。每種方法能捕獲其他方法遺漏的問題。三者都要使用。
PnL +25%且CSRS 0.72的策略,比PnL +300%且CSRS 0.18的策略更可靠。前者從市場低效性中獲利。後者只是記住了一條價格序列。
參考連結
- Lopez de Prado, M. — Advances in Financial Machine Learning (Wiley)
- Pardo, R. — The Evaluation and Optimization of Trading Strategies (Wiley)
- Bailey, D.H. et al. — The Probability of Backtest Overfitting
- Aronson, D.R. — Evidence-Based Technical Analysis
- Kevin Davey — Building Winning Algorithmic Trading Systems (Wiley)
- Harvey, C.R. & Liu, Y. — Backtesting (2015)
- Chan, E. — Algorithmic Trading: Winning Strategies and Their Rationale (Wiley)
- Binance Research — Cryptocurrency Correlation Analysis
- NumPy — numpy.random.choice
- Pandas — DataFrame
引用
@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上优化的策略可能在山寨币上失败。如何正确按交易对分组(蓝筹、大市值、垃圾币)进行测试,以及什么水平的cross-symbol robustness score才算足够。}
}
Authors
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.