MarketMaker.cc Team
量化研究与策略
MarketMaker.cc Team
量化研究与策略
你在10个加密货币交易对上启动策略:BTC/USDT、ETH/USDT、SOL/USDT、AVAX/USDT,再加上另外六个山寨币。逻辑看似无懈可击:如果策略在单个交易对上5%的时间处于活跃状态,那么在10个交易对上,至少有一个应该在 的时间内处于活跃状态。利用率提高四倍。
实际上,利用率仅为15-16%,而非40%。你的10个交易对表现得如同3个。资金闲置,fill_efficiency下降,投资组合的有效收益率比预期低三倍。
原因在于信号相关性。而在加密货币中,它高得惊人。
在传统金融中,分散化之所以有效,是因为苹果股票和石油ETF对不同因素做出反应。在加密货币市场中,一切都不同。
BTC是主导因素。当比特币下跌5%时,ETH下跌6-8%,SOL下跌8-12%,山寨币下跌10-20%。加密货币市场的日收益率相关性持续高于0.6,在恐慌时期接近1.0。
但对于我们——算法交易者——来说,重要的不是价格相关性,而是信号相关性。如果策略基于动量,并且BTC触发了入场信号,ETH和SOL很可能在同一分钟内触发类似信号。所有交易对同时做多,同时退出。十个仓位——但本质上只是一次押注。
假设策略在 个交易对中的每一个上,有 比例的时间处于活跃状态。如果信号完全独立,至少有一个交易对活跃的概率为:
对于策略B(,):
但信号并非独立的。加密货币同步运动——这意味着信号以集群的形式出现和消失。
直觉是这样的:如果10个交易对是相关的,它们携带的信息不是来自10个独立来源,而是大约3-4个。我们通过effective_N来形式化:
其中 是相关因子,反映了信号的平均成对相关性。当 时,交易对完全独立;当 时,它们完全相同。
对于加密货币交易对,典型的 。则:
不是40%,而是15.6%。相差2.5倍。Fill efficiency相应下降,整个投资组合的有效收益率也随之下降(参见按活跃时间计算的PnL)。

加密货币市场具有明显的因子结构。BTC解释了大多数山寨币日收益率60-80%的方差。这通过PCA(主成分分析)可以清楚地看到:
import numpy as np
from sklearn.decomposition import PCA
def analyze_crypto_factor_structure(returns_matrix: np.ndarray, pair_names: list) -> dict:
"""
加密货币收益率因子结构的PCA分析。
Args:
returns_matrix: 收益率矩阵 [n_days x n_pairs]
pair_names: 交易对名称列表
"""
pca = PCA()
pca.fit(returns_matrix)
explained = pca.explained_variance_ratio_
cumulative = np.cumsum(explained)
print("Factor structure:")
for i, (var, cum) in enumerate(zip(explained[:5], cumulative[:5])):
print(f" PC{i+1}: {var:.1%} variance (cumulative: {cum:.1%})")
loadings = pca.components_[0]
print("\nPC1 loadings (BTC factor):")
for name, load in sorted(zip(pair_names, loadings), key=lambda x: -abs(x[1])):
print(f" {name}: {load:.3f}")
return {
"explained_variance": explained,
"n_effective_factors": int(np.searchsorted(cumulative, 0.90)) + 1,
"pc1_loadings": dict(zip(pair_names, loadings)),
}
10个加密货币交易对投资组合的典型结果:
| 成分 | 解释方差 | 累计方差 |
|---|---|---|
| PC1 (BTC) | 65% | 65% |
| PC2 | 12% | 77% |
| PC3 | 8% | 85% |
| PC4 | 5% | 90% |
| PC5-PC10 | 10% | 100% |
四个因子解释了90%的方差。10个交易对中,"独立"的不超过4个。
这里有一个重要的细微差别。价格相关性和信号相关性是不同的概念。BTC和ETH的价格相关性为0.85,但特定策略的信号相关性可能是0.95或0.50——取决于入场逻辑。
示例:基于RSI超买/超卖的策略。BTC上的RSI穿过30(超卖)——做多入场。同一时刻ETH也可能处于超卖状态(信号相关性0.90)。也可能不是,如果ETH下跌更慢(信号相关性0.40)。
正确的方法是测量信号本身的相关性,而不是价格序列:
import numpy as np
from itertools import combinations
def signal_correlation_matrix(
signals: dict, # {pair: np.array of 0/1 per minute}
method: str = "pearson",
) -> np.ndarray:
"""
计算信号相关矩阵(二值:0 = 无仓位,1 = 有仓位)。
Args:
signals: 字典 {pair_name: binary_signal_array}
method: 相关性方法 ("pearson", "jaccard")
"""
pairs = sorted(signals.keys())
n = len(pairs)
corr_matrix = np.ones((n, n))
for i, j in combinations(range(n), 2):
s_i = signals[pairs[i]]
s_j = signals[pairs[j]]
if method == "pearson":
corr = np.corrcoef(s_i, s_j)[0, 1]
elif method == "jaccard":
intersection = np.sum(s_i & s_j)
union = np.sum(s_i | s_j)
corr = intersection / union if union > 0 else 0
else:
raise ValueError(f"Unknown method: {method}")
corr_matrix[i, j] = corr
corr_matrix[j, i] = corr
return corr_matrix, pairs
def estimate_correlation_factor(corr_matrix: np.ndarray) -> float:
"""
从信号相关矩阵估算correlation_factor。
correlation_factor = 1 + (N-1) * mean_pairwise_correlation
当相关性为0 → C_f = 1(全部独立)。
当相关性为1 → C_f = N(全部相同)。
"""
n = corr_matrix.shape[0]
upper_triangle = corr_matrix[np.triu_indices(n, k=1)]
mean_corr = np.mean(upper_triangle)
correlation_factor = 1 + (n - 1) * mean_corr
return correlation_factor
相关性不是静态的。在平静时期,BTC和山寨币可能会分化——ETH因以太坊新闻上涨,SOL因Solana新闻上涨。在危机中,一切坍缩为一个因子:风险偏好/风险规避。
def rolling_correlation_factor(
signals: dict,
window_days: int = 30,
step_days: int = 7,
) -> list:
"""
滚动correlation_factor,用于检测市场状态变化。
"""
pairs = sorted(signals.keys())
minutes_per_day = 1440
window = window_days * minutes_per_day
step = step_days * minutes_per_day
total_minutes = len(signals[pairs[0]])
results = []
for start in range(0, total_minutes - window, step):
end = start + window
window_signals = {p: signals[p][start:end] for p in pairs}
corr_matrix, _ = signal_correlation_matrix(window_signals)
cf = estimate_correlation_factor(corr_matrix)
results.append({
"start_minute": start,
"end_minute": end,
"correlation_factor": cf,
"effective_n": len(pairs) / cf,
})
return results
10个加密货币交易对的典型情况:
| 市场状态 | 平均信号相关性 | ||
|---|---|---|---|
| 横盘(低波动率) | 0.15-0.25 | 2.4-3.3 | 3.0-4.2 |
| 上升趋势 | 0.25-0.40 | 3.3-4.6 | 2.2-3.0 |
| 下降趋势 | 0.30-0.50 | 3.7-5.5 | 1.8-2.7 |
| 恐慌(崩盘) | 0.60-0.90 | 6.4-9.1 | 1.1-1.6 |
恐慌期间,10个交易对压缩至1-2个有效交易对。恰恰在最需要分散化的时候,它消失了。这是经典"危机中相关性趋向1"的加密货币版本。
effective_N的概念借鉴自统计学中的有效样本量,后者考虑了观测值的自相关性。对于我们的目的:
其中 是信号的平均成对相关性。简化表示:
性质:
实际中有三种方法:
1. 从信号相关矩阵(精确)。
在所有交易对上运行策略,获得二值信号(每分钟0/1),构建相关矩阵,使用上述公式计算 。
2. 从价格收益率的PCA(近似)。
如果信号强烈依赖于价格动态(动量、均值回归),可以将 估算为解释90%方差的PCA成分数量。
3. 从资产类别启发式(粗略)。
| 资产类别 | 典型 |
|---|---|
| 加密货币(前10名) | 2.5-4.0 |
| 加密货币(含DeFi/模因币) | 2.0-3.0 |
| 外汇(主要货币对) | 1.5-2.5 |
| 股票(单一行业) | 2.0-3.5 |
| 股票(跨行业) | 1.2-1.8 |
对于包含BTC、ETH、SOL、AVAX、MATIC、DOGE、DOT、LINK、UNI、ATOM的加密货币投资组合,安全估计为 。
考虑相关性的基础公式:
不同策略和交易对数量的对照表():
| 策略 | (交易时间) | 5对 () | 10对 () | 20对 () | 50对 () |
|---|---|---|---|---|---|
| 策略B | 5% | 8.2% | 15.6% | 29.1% | 58.0% |
| 策略A | 15% | 23.6% | 41.8% | 65.9% | 92.8% |
| 策略C | 45% | 67.1% | 89.0% | 98.8% | ~100% |
对于活跃度为5%的策略B,需要50个交易对才能在至少一半的时间内拥有至少一个活跃仓位。而且这还没有考虑到50个加密货币交易对的相关性比10个更强。
真正的编排器同时管理多个插槽。如果你有5个插槽和10个交易对,利用率的计算方式不同:
def estimate_fill_efficiency(
trading_time_pct: float,
n_pairs: int,
correlation_factor: float = 3.0,
max_slots: int = 1,
) -> dict:
"""
多插槽编排器fill efficiency的解析估算。
Args:
trading_time_pct: 单个策略在单个交易对上的活跃时间比例
n_pairs: 交易对数量
correlation_factor: 信号相关系数
max_slots: 最大同时仓位数
Returns:
包含利用率指标的dict
"""
effective_n = n_pairs / correlation_factor
p_at_least_one = 1 - (1 - trading_time_pct) ** effective_n
expected_active = effective_n * trading_time_pct
utilization = min(expected_active, max_slots) / max_slots
fill_efficiency = min(p_at_least_one, utilization)
return {
"effective_n": effective_n,
"p_at_least_one": p_at_least_one,
"expected_active": expected_active,
"utilization": utilization,
"fill_efficiency": fill_efficiency,
}
configs = [
("策略B,10对,1插槽", 0.05, 10, 3.0, 1),
("策略B,10对,3插槽", 0.05, 10, 3.0, 3),
("策略B,30对,1插槽", 0.05, 30, 3.0, 1),
("策略A,10对,1插槽", 0.15, 10, 3.0, 1),
("策略C,10对,1插槽", 0.45, 10, 3.0, 1),
("策略C,10对,5插槽", 0.45, 10, 3.0, 5),
]
for name, p, n, cf, slots in configs:
result = estimate_fill_efficiency(p, n, cf, slots)
print(f"{name}:")
print(f" N_eff = {result['effective_n']:.1f}")
print(f" P(≥1 active) = {result['p_at_least_one']:.1%}")
print(f" E[active] = {result['expected_active']:.2f}")
print(f" fill_efficiency = {result['fill_efficiency']:.1%}")
print()
预期输出:
策略B,10对,1插槽:
N_eff = 3.3
P(≥1 active) = 15.6%
E[active] = 0.17
fill_efficiency = 15.6%
策略B,10对,3插槽:
N_eff = 3.3
P(≥1 active) = 15.6%
E[active] = 0.17
fill_efficiency = 5.6%
策略B,30对,1插槽:
N_eff = 10.0
P(≥1 active) = 40.1%
E[active] = 0.50
fill_efficiency = 40.1%
策略A,10对,1插槽:
N_eff = 3.3
P(≥1 active) = 41.8%
E[active] = 0.50
fill_efficiency = 41.8%
策略C,10对,1插槽:
N_eff = 3.3
P(≥1 active) = 89.0%
E[active] = 1.50
fill_efficiency = 89.0%
策略C,10对,5插槽:
N_eff = 3.3
P(≥1 active) = 89.0%
E[active] = 1.50
fill_efficiency = 30.0%
请注意:策略B在3个插槽和10个交易对下,fill_efficiency仅为5.6%。当预期活跃交易对数量仅为0.17时,三个插槽毫无意义。插槽应按预期负载成比例分配。
解析模型是近似值。要获得精确估算,需要在真实信号上进行模拟:
import numpy as np
def simulate_fill_efficiency(
all_signals: dict, # {(strategy, pair): [(entry_min, exit_min), ...]}
max_slots: int = 10,
test_period_minutes: int = 750 * 24 * 60, # 750天
priority_fn=None, # 仓位选择的优先级函数
) -> dict:
"""
模拟编排器的实际插槽负载。
对于每一分钟:计算有多少交易对想要建仓,
以及实际占用了多少插槽(考虑限制)。
Args:
all_signals: 按交易对和策略分类的信号
max_slots: 最大同时仓位数
test_period_minutes: 测试期长度(分钟)
priority_fn: 如果为None — FIFO;否则 — 排名函数
"""
demand_timeline = np.zeros(test_period_minutes, dtype=np.int32)
capped_timeline = np.zeros(test_period_minutes, dtype=np.int32)
for signals in all_signals.values():
for entry_min, exit_min in signals:
if entry_min < test_period_minutes:
end = min(exit_min, test_period_minutes)
demand_timeline[entry_min:end] += 1
capped_timeline = np.minimum(demand_timeline, max_slots)
total_demand = np.sum(demand_timeline)
total_filled = np.sum(capped_timeline)
time_with_any_active = np.sum(demand_timeline > 0)
fill_efficiency = np.mean(capped_timeline) / max_slots
demand_fill_ratio = total_filled / total_demand if total_demand > 0 else 0
time_utilization = time_with_any_active / test_period_minutes
slot_distribution = {}
for s in range(max_slots + 1):
slot_distribution[s] = np.mean(capped_timeline == s)
return {
"fill_efficiency": fill_efficiency,
"demand_fill_ratio": demand_fill_ratio,
"time_utilization": time_utilization,
"avg_demand": np.mean(demand_timeline),
"avg_filled": np.mean(capped_timeline),
"slot_distribution": slot_distribution,
"overflow_pct": np.mean(demand_timeline > max_slots),
}
在真实数据上的模拟通常显示的利用率甚至低于解析估算,因为它考虑了信号的时间聚集性:所有交易对在一个集群中同时入场,造成溢出,然后全部沉默,造成空白。

核心问题:在什么 值时,增加一个交易对不再明显提高fill_efficiency?
import numpy as np
def diminishing_returns_analysis(
trading_time_pct: float,
correlation_factor: float = 3.0,
max_pairs: int = 100,
target_utilization: float = 0.80,
) -> dict:
"""
增加新交易对的边际递减分析。
"""
results = []
target_n = None
for n in range(1, max_pairs + 1):
n_eff = n / correlation_factor
p_active = 1 - (1 - trading_time_pct) ** n_eff
marginal = 0
if n > 1:
prev_eff = (n - 1) / correlation_factor
prev_p = 1 - (1 - trading_time_pct) ** prev_eff
marginal = p_active - prev_p
results.append({
"n_pairs": n,
"n_effective": n_eff,
"p_at_least_one": p_active,
"marginal_gain": marginal,
})
if target_n is None and p_active >= target_utilization:
target_n = n
return {
"results": results,
"target_n_for_utilization": target_n,
}
analysis_b = diminishing_returns_analysis(0.05, correlation_factor=3.0, target_utilization=0.80)
print(f"策略B:达到80% P(≥1)需要{analysis_b['target_n_for_utilization']}个交易对")
for r in analysis_b["results"]:
if r["n_pairs"] in [1, 3, 5, 10, 20, 30, 50, 80]:
print(f" N={r['n_pairs']:3d}: N_eff={r['n_effective']:.1f}, "
f"P(≥1)={r['p_at_least_one']:.1%}, "
f"marginal={r['marginal_gain']:.2%}")
策略B的结果(,):
| 交易对 | 边际增益 | ||
|---|---|---|---|
| 1 | 0.3 | 1.7% | — |
| 3 | 1.0 | 5.0% | +1.7% |
| 5 | 1.7 | 8.2% | +1.6% |
| 10 | 3.3 | 15.6% | +1.4% |
| 20 | 6.7 | 29.1% | +1.1% |
| 30 | 10.0 | 40.1% | +0.9% |
| 50 | 16.7 | 58.0% | +0.6% |
| 80 | 26.7 | 74.5% | +0.4% |
对于策略B,即使有100个交易对也无法达到80%的单插槽利用率(需要约96个交易对)。这是一个根本性限制:交易时间为5%的策略不适合单插槽运行——它需要多策略的投资组合方法。
对于策略A(,):
| 交易对 | 边际增益 | ||
|---|---|---|---|
| 5 | 1.7 | 23.6% | — |
| 10 | 3.3 | 41.8% | +3.3% |
| 20 | 6.7 | 65.9% | +2.1% |
| 30 | 10.0 | 80.3% | +1.2% |
策略A在约30个交易对时达到80%利用率。第30个交易对的边际增益仅为+1.2%。
对于策略C(,):
| 交易对 | ||
|---|---|---|
| 3 | 1.0 | 45.0% |
| 5 | 1.7 | 67.1% |
| 10 | 3.3 | 89.0% |
| 15 | 5.0 | 95.0% |
交易时间为45%的策略C在仅10个交易对时就达到了90%利用率。增加更多没有意义。
还有另一个限制交易对数量的因素:edge衰减。在BTC/USDT上开发和优化的策略,在流动性较低的山寨币上可能表现更差。
衰减原因:
def edge_decay_analysis(
strategy_results: dict, # {pair: {"pnl_per_day": float, "n_trades": int}}
min_trades: int = 30,
) -> list:
"""
按edge排名交易对,考虑衰减。
"""
ranked = []
for pair, metrics in strategy_results.items():
if metrics["n_trades"] < min_trades:
continue
ranked.append({
"pair": pair,
"pnl_per_day": metrics["pnl_per_day"],
"n_trades": metrics["n_trades"],
"sharpe": metrics.get("sharpe", 0),
})
ranked.sort(key=lambda x: x["pnl_per_day"], reverse=True)
cumulative_pnl = []
running_sum = 0
for i, r in enumerate(ranked):
running_sum += r["pnl_per_day"]
avg = running_sum / (i + 1)
cumulative_pnl.append({
"n_pairs": i + 1,
"last_added": r["pair"],
"last_pnl_per_day": r["pnl_per_day"],
"avg_pnl_per_day": avg,
})
return cumulative_pnl
典型情况:
| # 交易对 | 最后添加的 | 最后一个的PnL/天 | 平均PnL/天 |
|---|---|---|---|
| 1 | BTC/USDT | 0.89% | 0.89% |
| 2 | ETH/USDT | 0.82% | 0.86% |
| 3 | SOL/USDT | 0.71% | 0.81% |
| 5 | AVAX/USDT | 0.55% | 0.73% |
| 8 | DOT/USDT | 0.31% | 0.61% |
| 10 | DOGE/USDT | 0.12% | 0.53% |
添加第10个交易对降低了投资组合的平均PnL/天。到第8个交易对时,edge已经是最好的一半。需要在fill_efficiency(随交易对数量增长)和平均edge(下降)之间取得平衡。
我们将fill_efficiency和edge衰减合并为一个指标——预期投资组合每日PnL:
def optimal_pairs_count(
pair_edges: list, # PnL/天按降序排列:[0.89, 0.82, 0.71, ...]
trading_time_pct: float,
correlation_factor: float = 3.0,
max_slots: int = 1,
) -> dict:
"""
寻找最大化投资组合PnL的最优交易对数量。
"""
best_n = 0
best_score = 0
results = []
for n in range(1, len(pair_edges) + 1):
avg_edge = np.mean(pair_edges[:n])
n_eff = n / correlation_factor
p_active = 1 - (1 - trading_time_pct) ** n_eff
expected_active = n_eff * trading_time_pct
utilization = min(expected_active, max_slots) / max_slots
fill_eff = min(p_active, utilization)
portfolio_score = avg_edge * fill_eff * 365
results.append({
"n_pairs": n,
"avg_edge": avg_edge,
"fill_efficiency": fill_eff,
"portfolio_annualized": portfolio_score,
})
if portfolio_score > best_score:
best_score = portfolio_score
best_n = n
return {
"optimal_n": best_n,
"optimal_score": best_score,
"results": results,
}
edges = [0.89, 0.82, 0.71, 0.65, 0.55, 0.48, 0.40, 0.31, 0.22, 0.12,
0.08, 0.05, 0.02, -0.01, -0.05]
opt = optimal_pairs_count(edges, trading_time_pct=0.15, correlation_factor=3.0)
print(f"最优交易对数量:{opt['optimal_n']}")
print(f"年化投资组合收益:{opt['optimal_score']:.1f}%")
for r in opt["results"]:
print(f" N={r['n_pairs']:2d}: avg_edge={r['avg_edge']:.2f}%, "
f"fill_eff={r['fill_efficiency']:.1%}, "
f"portfolio={r['portfolio_annualized']:.1f}%")
最优值通常位于添加新交易对的边际fill_efficiency不再能补偿平均edge下降的点。对于典型的加密货币投资组合:
一个悖论:交易时间最少的策略从最多的交易对中获益,但fill_efficiency仍然很低。解决方案不是更多交易对,而是与其他策略组合(参见组合策略)。
如果无法无限增加交易对数量,可以降低 ——即增加信号多样性。
BTC、ETH、BNB高度相关。但UNI(DEX)、AAVE(借贷)、CRV(稳定币)可能有自己的驱动因素。添加DeFi代币可将平均 从0.35降至0.20-0.25:
不是10个交易对配一个策略——而是5个交易对配两个不同策略。如果策略基于不同原理(动量 vs. 均值回归),它们的信号可能负相关:
这是获得 的唯一方法——使用信号负相关的策略。
资金费率套利和现货交易具有不同的相关结构。将套利策略添加到投资组合中可显著降低整体 ,因为套利从本质上利用的是差异而非一致性。
典型代表:策略B(5%时间,750天38笔交易)。
典型代表:策略A(15%时间,750天418笔交易)。
典型代表:策略C(45%时间)。
| 参数 | 策略B (5%) | 策略A (15%) | 策略C (45%) |
|---|---|---|---|
| 建议 | 10-15 | 6-10 | 4-6 |
| 时的 | 3.3-5.0 | 2.0-3.3 | 1.3-2.0 |
| Fill eff.(1插槽) | 15-23% | 32-42% | 77-89% |
| 需要组合? | 必须 | 建议 | 不需要 |
| 瓶颈 | 信号太少 | 平衡 | 溢出 |
本文是"没有幻觉的回测"系列的第十一篇。信号相关性直接影响前几篇文章中的指标:
按活跃时间计算的PnL: fill_efficiency是有效收益率公式中的关键乘数。如果你忽略相关性而高估了fill_efficiency,你的投资组合PnL预测将过于乐观。
资金费率: 高相关性下,仓位同时开启——资金成本随插槽数量线性增长。溢出 + 资金费率 = 加速消耗资本。
资金费率套利: 套利策略是天然的分散化工具,可降低投资组合的 。其信号与动量和均值回归策略弱相关。
组合策略(下一篇文章):如何构建具有不同 和 的策略投资组合,以实现90%以上的利用率。级联编排在分配优先级时考虑信号相关性。
加密货币中的分散化不在于交易对的数量。10个相关交易对产生的效果相当于3-4个独立交易对。恐慌期间更少。
四个要点:
计算effective_N,而不是N。 对于加密货币交易对 。十个交易对约为3.3个有效交易对。根据 而非 来规划fill_efficiency。
测量信号相关性,而非价格相关性。 价格相关性是代理指标,不是替代品。在所有交易对上运行策略,计算二值信号的相关矩阵。
考虑edge衰减。 更多交易对意味着更低的平均PnL/天。最优点在于新交易对的边际fill_efficiency仍能补偿edge下降的位置。
降低 ,而不是增加 。 在相同交易对上组合不同策略比在更多交易对上使用单一策略更有效。跨策略分散化可以实现 。
相关因子是决定你的利用率和收益率预测是否现实的隐藏变量。忽略它意味着在幻觉之上构建投资组合。
@article{soloviov2026signalcorrelation, author = {Soloviov, Eugen}, title = {信号相关性:需要监控多少个交易对}, year = {2026}, url = {https://marketmaker.cc/ru/blog/post/signal-correlation-pairs}, version = {0.1.0}, description = {为什么10个加密货币交易对无法提供10倍分散化,如何通过correlation\_factor计算effective\_N,以及实际需要监控多少个交易对才能达到80-90\%的编排器插槽利用率。} }