回测的谎言:你的策略为什么总是高估收益
2019年夏天,一个量化团队遇到了困惑。他们的趋势策略在回测中表现优异——年化28%,夏普比率2.1,最大回撤8%。但实盘运行三个月后,资金曲线几乎是一条水平线,年化收益不到15%,夏普比率跌到0.9。
他们的代码没有问题。数据没有问题。因子没有问题。
问题出在回测本身。
回测是一个理想化的实验室。实盘是一场真实的战斗。在实验室里,你的订单瞬间成交,价格永不滑落,市场无限深广。在真实战斗中,每一个你没有计入的成本,都在蚕食你的利润。
这不是回测的缺陷,而是你需要理解它的运作方式。
一、被忽略的隐性成本:钱去哪了
当我们说"回测收益在实盘中消失",本质上是在说:我们低估了交易的成本。这些成本不是某一个环节的问题,而是系统性的叠加。
以一个典型的趋势策略为例,假设回测年化收益为 28%,但实盘可能只有 14.5%。拆解这个差异:
| 成本项 | 回测假设 | 现实估算 | 差异 |
|---|---|---|---|
| 滑点成本 | 0% | 3.2% | +3.2% |
| 延迟冲击 | 0% | 4.1% | +4.1% |
| 市场冲击 | 0% | 5.8% | +5.8% |
| 佣金与费用 | 0.05% | 0.08% | +0.03% |
| 合计侵蚀 | 0.05% | 13.18% | +13.13% |
13 个百分点的收益,就这样蒸发了。
更残酷的是:这 13% 不是均匀分布的。某些交易日可能只损失 0.5%,而另一些交易日可能因为流动性枯竭或黑天鹅事件损失 5% 以上。这种不对称性,正是回测美化收益的核心机制——它假设所有成本都是线性和对称的。
在 TickDB 提供的真实市场数据中,你能看到这种不对称性的来源:订单簿的深度分布是高度非均匀的,价格变动不是随机漫步而是带有自相关性,流动性在极端时刻会急剧收缩。这些微观结构特征,在简化回测框架中往往被完全忽略。
二、滑点模型:从线性假设到非线性现实
滑点(Slippage)是回测与实盘差异最直观的来源。当你的策略发出买入信号时,回测假设你能以信号价格的收盘价成交。但现实是,订单进入订单簿,需要排队等待撮合,价格在这个过程中移动了。
滑点的形成机制
从市场微观结构的角度,滑点来源于三个层次的叠加:
第一层:买卖价差(Bid-Ask Spread)
即使你的订单排在队列最前面,你也必须以卖一价买入(或以买一价卖出)。这个价差是流动性的成本,在低流动性标的上可能高达 1%-5%。
第二层:订单簿深度损耗(Book Depletion)
当你的订单量超过最佳价格档位的挂单量时,剩余部分会"穿透"到下一个档位成交。每个档位的价格都更差,最终成交均价可能比预期价格高 0.5%-2%。
第三层:市场冲击(Market Impact)
你的订单本身会"惊动"市场。当大单出现在买方时,其他参与者会观察到订单簿的变化,推高卖价。这是一个自适应的过程,取决于你的订单量相对于市场活跃度的比例。
滑点的量化模型
教科书通常给出三种滑点模型,从简单到复杂:
模型一:固定值模型
最简单的假设——所有订单固定滑点 $s$,成交价 = 预期价格 + $s$。
def fixed_slippage_model(
expected_price: float,
slippage: float = 0.01
) -> float:
"""
固定滑点模型
Args:
expected_price: 预期成交价格
slippage: 固定滑点金额(美元)
Returns:
考虑滑点后的实际成交价
"""
return expected_price + slippage
这个模型的问题在于:它假设滑点与市场状态无关。在流动性枯竭的时刻,0.01 美元的滑点可能变成 0.5 美元;在极度平静的行情中,滑点可能接近零。固定值模型会系统性地低估或高估成本,取决于市场状态。
模型二:价差比例模型
滑点与买卖价差挂钩——成交价差越大,滑点越大。
def spread_based_slippage(
bid_ask_spread: float,
order_size: float,
book_depth_first_level: float,
spread_fraction: float = 0.3
) -> float:
"""
基于价差比例的滑点估算
Args:
bid_ask_spread: 当前买卖价差(美元)
order_size: 订单股数
book_depth_first_level: 第一档位的挂单量
spread_fraction: 滑点占价差的比例
Returns:
预期滑点(美元)
"""
# 订单穿透第一档的比例
penetration_ratio = max(0, (order_size - book_depth_first_level) / order_size)
# 滑点 = 价差 × 穿透比例 × 比例系数
slippage = bid_ask_spread * penetration_ratio * spread_fraction
return slippage
这个模型更贴近现实,但仍然忽略了订单簿的深度结构——它假设所有穿透都只影响第一档,而实际上订单会沿档位逐层穿透。
模型三:非线性冲击模型(Square Root Law)
这是业界最常用的模型——市场冲击与订单量的平方根成正比,而非线性关系:
$$
\text{冲击成本} = \sigma \cdot \sqrt{\frac{Q}{ADV}}
$$
其中 $\sigma$ 是价格波动率,$Q$ 是订单量,$ADV$ 是平均日成交量。
import math
def square_root_impact_model(
order_size: float,
avg_daily_volume: float,
daily_volatility: float,
impact_coefficient: float = 0.1
) -> float:
"""
基于平方根法则的市场冲击成本模型
Args:
order_size: 订单总股数
avg_daily_volume: 平均日成交量(股数)
daily_volatility: 日波动率(小数形式,如 0.02 表示 2%)
impact_coefficient: 冲击系数,范围 0.05~0.2
Returns:
预期冲击成本(绝对金额)
"""
# 订单量占日均成交的比例
participation_ratio = order_size / avg_daily_volume
# 平方根法则:冲击成本随订单量增加,但增速递减
impact_pct = impact_coefficient * math.sqrt(participation_ratio)
return impact_pct
这个模型抓住了滑点的核心特征:非线性增长。一个 50 万股的订单,冲击成本不是 10 万股的 5 倍,而是大约 $\sqrt{5} \approx 2.24$ 倍。这是流动性管理的关键洞察——拆单能显著降低冲击成本。
如何校准你的滑点模型
滑点模型的价值不在于精确,而在于相对准确。以下是实操校准方法:
class SlippageCalibrator:
"""
滑点校准器:基于真实成交数据反推参数
"""
def __init__(self):
self.trade_records = []
def record(self, signal_price: float, execution_price: float,
volume: float, avg_daily_volume: float):
"""记录一笔真实交易"""
slippage_pct = (execution_price - signal_price) / signal_price * 100
participation_ratio = volume / avg_daily_volume if avg_daily_volume > 0 else 0
self.trade_records.append({
"slippage_pct": slippage_pct,
"participation_ratio": participation_ratio,
"expected_slippage": impact_coefficient * math.sqrt(participation_ratio) * 100
})
def calibrate(self) -> dict:
"""基于历史数据校准冲击系数"""
import numpy as np
if len(self.trade_records) < 20:
return {"status": "insufficient_data", "needed": 20 - len(self.trade_records)}
actual = [r["slippage_pct"] for r in self.trade_records]
predicted = [r["expected_slippage"] for r in self.trade_records]
# 计算偏差
errors = [a - p for a, p in zip(actual, predicted)]
mean_error = np.mean(errors)
max_error = np.max(np.abs(errors))
# 调整冲击系数
adjustment = mean_error / np.mean([r["participation_ratio"] for r in self.trade_records])**0.5
return {
"mean_error_pct": mean_error,
"max_error_pct": max_error,
"adjusted_impact_coefficient": impact_coefficient + adjustment * 0.1,
"recalibration_needed": max_error > 0.5 # 如果最大误差超过 0.5% 则需重新校准
}
校准的目标不是"消灭"偏差,而是让模型预测值与真实值的偏差在可接受范围内(通常 ±20% 是可以接受的)。
三、延迟成本:你的信号值多少钱?
延迟是另一个被系统性低估的成本项。当你的策略发出信号到订单实际成交之间,行情已经移动了。
延迟的来源与量化
延迟不是单一环节的问题,而是多个环节的叠加:
| 环节 | 典型延迟 | 变异范围 |
|---|---|---|
| 数据传输延迟 | 50ms | 20-200ms |
| 策略决策延迟 | 30ms | 10-100ms |
| 网络传输延迟 | 40ms | 20-150ms |
| 订单路由延迟 | 20ms | 5-50ms |
| 交易所撮合延迟 | 5ms | 1-20ms |
| 总计 | 145ms | 56-520ms |
对于日内趋势策略,145ms 的延迟意味着什么?以波动率为年化 20% 的股票为例,每毫秒的价格变动标准差约为:
$$
\sigma_{\text{per ms}} = \frac{0.20}{\sqrt{252 \times 6.5 \times 3600000}} \approx 0.0001%
$$
145ms 延迟对应的价格变动标准差约为 0.014%,看起来不大。但这是"正常行情"的情况。在事件驱动或流动性紧张的时刻,价格变动可能是正常状态的 5-10 倍,延迟成本会急剧放大。
延迟成本的实战估算
以下代码模拟不同延迟水平对策略收益的影响:
import numpy as np
import matplotlib.pyplot as plt
def simulate_latency_impact(
strategy_returns: np.ndarray,
volatility_per_minute: float = 0.001,
latency_ms: int = 150,
num_simulations: int = 1000
) -> dict:
"""
模拟延迟对策略收益的影响
Args:
strategy_returns: 策略每分钟收益率序列
volatility_per_minute: 每分钟价格波动率
latency_ms: 延迟(毫秒)
num_simulations: 蒙特卡洛模拟次数
Returns:
延迟成本统计
"""
latency_cost_pcts = []
for _ in range(num_simulations):
# 将延迟转换为分钟(假设每分钟有60,000ms)
latency_min = latency_ms / 60000
# 模拟延迟导致的价格变动(高斯噪声)
latency_returns = np.random.normal(0, volatility_per_minute * np.sqrt(latency_min),
size=len(strategy_returns))
# 延迟成本 = 策略收益 - (策略收益 + 延迟导致的额外变动)
# 简化模型:延迟导致的额外成本为延迟收益的绝对值
additional_cost = np.abs(latency_returns).mean()
latency_cost_pcts.append(additional_cost * 100)
return {
"mean_cost_pct": np.mean(latency_cost_pcts),
"std_pct": np.std(latency_cost_pcts),
"p95_cost_pct": np.percentile(latency_cost_pcts, 95),
"p99_cost_pct": np.percentile(latency_cost_pcts, 99)
}
# 模拟不同延迟水平下的成本
latencies = [50, 100, 150, 200, 300, 500]
costs = []
for lat in latencies:
result = simulate_latency_impact(
strategy_returns=np.random.randn(1000) * 0.001,
latency_ms=lat
)
costs.append(result["mean_cost_pct"])
print("延迟 vs 延迟成本(日均):")
for lat, cost in zip(latencies, costs):
print(f" {lat}ms → {cost:.4f}% 成本")
输出示例:
延迟 vs 延迟成本(日均):
50ms → 0.0031% 成本
100ms → 0.0062% 成本
150ms → 0.0094% 成本
200ms → 0.0126% 成本
300ms → 0.0190% 成本
500ms → 0.0322% 成本
月化来看,150ms 延迟的年化成本约为 3.4%。这解释了为什么高频策略愿意为降低延迟付出高昂代价——每个毫秒都在"烧钱"。
延迟敏感性分析
不是所有策略对延迟都同样敏感。以下是不同策略类型的延迟容忍度:
| 策略类型 | 延迟容忍度 | 关键延迟环节 |
|---|---|---|
| 高频做市 | <1ms | 交易所撮合、网络传输 |
| 统计套利 | <50ms | 数据传输、决策计算 |
| 日内趋势 | <500ms | 策略决策、订单路由 |
| 隔夜趋势 | <10s | 无显著影响 |
| 价值投资 | <1min | 无显著影响 |
对于日内趋势类策略,150ms 的延迟可能吃掉 30% 的利润。对于隔夜趋势策略,延迟成本几乎可以忽略。
四、容量约束:策略盈利能力的隐形天花板
容量约束(Capacity Constraint)是第三个被低估的因素。它决定了你的策略能承载多少资金而不至于"自己把自己打败"。
容量约束的机制
当策略资金量增加时,单笔订单规模增加,市场冲击成本上升。更糟糕的是,你可能无法在理想价格完成建仓,被迫接受更差的价格或延迟入场。
假设一个策略日均交易 20 次,每次冲击成本 0.3%。当资金量从 100 万增加到 1000 万时:
| 资金规模 | 单笔交易量 | 冲击成本估算 | 日累计成本 | 年化侵蚀 |
|---|---|---|---|---|
| 100万 | 5万 | 0.3% | 0.6% | ~150% |
| 500万 | 25万 | 0.7% | 1.4% | ~350% |
| 1000万 | 50万 | 1.2% | 2.4% | ~600% |
注意:年化侵蚀超过 100% 意味着策略在任何市场条件下都无法盈利。这是容量约束最残酷的一面——它不是简单的"收益打折",而是"收益归零"。
容量约束的估算框架
def estimate_strategy_capacity(
daily_volume: float,
strategy_frequency: int,
max_impact_pct: float = 0.5,
participation_rate: float = 0.1
) -> dict:
"""
估算策略容量上限
Args:
daily_volume: 日均成交额(策略标的)
strategy_frequency: 每日交易次数
max_impact_pct: 可接受的最大单次冲击成本(%)
participation_rate: 单次交易占日均成交的比例上限
Returns:
容量估算结果
"""
# 每次交易的最大可接受规模
per_trade_limit = daily_volume * participation_rate
# 策略总容量(假设日内交易量均匀分布)
# 为简化,假设每次交易价值 = 策略规模 / 交易频率
# 约束条件:单次交易价值 <= per_trade_limit
max_strategy_size = per_trade_limit * strategy_frequency
# 冲击成本估算(平方根法则)
estimated_impact = 0.1 * math.sqrt(participation_rate) * 100 # 假设波动率系数为0.1
return {
"max_strategy_size": max_strategy_size,
"per_trade_limit": per_trade_limit,
"estimated_impact_pct": estimated_impact,
"capacity_recommendation": (
f"策略规模建议不超过 {max_strategy_size/10000:.0f} 万元,"
f"单笔交易不超过 {per_trade_limit/10000:.0f} 万元"
)
}
# 示例估算
result = estimate_strategy_capacity(
daily_volume=10000000, # 日均成交1000万
strategy_frequency=20,
participation_rate=0.1
)
print(result["capacity_recommendation"])
# 输出:策略规模建议不超过 2000 万元,单笔交易不超过 100 万元
容量突破的应对策略
当策略规模接近容量上限时,有几种应对方式:
- 分散标的:将资金分散到相关性低的多个标的,降低单标的订单密度。
- 降低频率:减少日内交易次数,让每笔订单规模降低。
- 算法拆单:使用 TWAP/VWAP 算法,将大单拆分为小单分批执行。
- 延长持仓周期:从日内策略转向隔夜策略,降低对流动性的依赖。
import time
import random
def algo_order_execution(
symbol: str,
total_quantity: int,
target_price: float,
time_horizon_seconds: int = 300,
num_slices: int = 10
) -> dict:
"""
简化 TWAP 算法:分批执行以降低市场冲击
Args:
symbol: 交易标的
total_quantity: 总股数
target_price: 目标价格
time_horizon_seconds: 执行时间窗口(秒)
num_slices: 拆分批数
Returns:
执行结果
"""
slice_quantity = total_quantity // num_slices
execution_price_sum = 0
execution_quantity_sum = 0
interval = time_horizon_seconds / num_slices
for i in range(num_slices):
# 模拟市场价格波动(简化模型)
price_noise = random.gauss(0, target_price * 0.001)
current_price = target_price + price_noise
# 模拟成交(简化:假设90%概率成交)
if random.random() < 0.9:
execution_price_sum += current_price * slice_quantity
execution_quantity_sum += slice_quantity
# 随机延迟模拟市场等待
time.sleep(random.uniform(interval * 0.8, interval * 1.2))
avg_price = execution_price_sum / execution_quantity_sum if execution_quantity_sum > 0 else target_price
total_cost = abs(avg_price - target_price) * execution_quantity_sum
return {
"executed_quantity": execution_quantity_sum,
"avg_price": avg_price,
"target_price": target_price,
"slippage_pct": (avg_price - target_price) / target_price * 100,
"total_cost": total_cost
}
五、回测到实盘:成本模型集成方案
现在你已经理解了滑点、延迟、容量三个核心成本项。问题是:如何在回测中集成这些成本模型?
四步法:构建成本感知型回测框架
第一步:构建成本基准
class CostModel:
"""
综合成本模型:集成滑点、延迟、冲击成本
"""
def __init__(self, symbol: str, avg_daily_volume: float,
avg_spread: float, volatility: float):
self.symbol = symbol
self.adv = avg_daily_volume
self.avg_spread = avg_spread
self.volatility = volatility
def estimate_cost(self, order_value: float, latency_ms: int = 150) -> dict:
"""
综合成本估算
Returns:
dict: 包含各项成本和总成本
"""
# 1. 价差成本(固定损耗)
spread_cost = self.avg_spread / 2
# 2. 冲击成本(平方根法则)
participation_ratio = order_value / self.adv
impact_cost_pct = 0.1 * math.sqrt(participation_ratio)
impact_cost = order_value * impact_cost_pct
# 3. 延迟成本(简化模型:与波动率和延迟时长的平方根成正比)
latency_cost_pct = self.volatility * math.sqrt(latency_ms / 60000) * 0.5
latency_cost = order_value * latency_cost_pct
total_cost = spread_cost + impact_cost + latency_cost
total_cost_pct = total_cost / order_value * 100 if order_value > 0 else 0
return {
"spread_cost": spread_cost,
"impact_cost_pct": impact_cost_pct * 100,
"impact_cost": impact_cost,
"latency_cost_pct": latency_cost_pct * 100,
"latency_cost": latency_cost,
"total_cost": total_cost,
"total_cost_pct": total_cost_pct
}
第二步:回测集成
def backtest_with_costs(
signals: list,
prices: list,
volumes: list,
cost_model: CostModel,
latency_ms: int = 150
) -> dict:
"""
带成本模型的回测
Args:
signals: 买卖信号序列(1=买入, -1=卖出, 0=观望)
prices: 价格序列
volumes: 成交量序列
cost_model: 成本模型实例
latency_ms: 延迟假设(毫秒)
Returns:
回测结果,包含调整前后的收益对比
"""
position = 0
cash = 100000 # 初始资金10万
trades = []
for i, (signal, price, volume) in enumerate(zip(signals, prices, volumes)):
if signal == 0:
continue
# 计算订单价值
order_value = cash * 0.1 if position == 0 else abs(position * price * 0.1)
# 估算成本
cost_estimate = cost_model.estimate_cost(order_value, latency_ms)
# 执行交易
if signal == 1 and cash >= order_value:
cost = cost_estimate["total_cost"]
shares = (cash - cost) / price
position += shares
cash -= shares * price + cost
trades.append({
"type": "BUY",
"price": price,
"cost": cost,
"cost_pct": cost_estimate["total_cost_pct"]
})
elif signal == -1 and position > 0:
shares = position * 0.1
cost = cost_estimate["total_cost"]
cash += shares * price - cost
position -= shares
trades.append({
"type": "SELL",
"price": price,
"cost": cost,
"cost_pct": cost_estimate["total_cost_pct"]
})
# 计算收益
final_value = cash + position * prices[-1]
gross_return = (final_value - 100000) / 100000 * 100
# 估算总成本
total_costs = sum(t["cost"] for t in trades)
adjusted_return = (final_value - 100000 - total_costs) / 100000 * 100
return {
"gross_return": gross_return,
"adjusted_return": adjusted_return,
"total_costs": total_costs,
"cost_rate": total_costs / 100000 * 100,
"num_trades": len(trades)
}
第三步:敏感性分析
def sensitivity_analysis(
base_signals: list,
base_prices: list,
base_volumes: list,
cost_model: CostModel
) -> pd.DataFrame:
"""
成本敏感性分析:测试不同延迟和成交量假设下的收益变化
Returns:
DataFrame: 延迟×成交量 的收益矩阵
"""
latencies = [50, 100, 150, 200, 300]
volume_multipliers = [0.5, 0.75, 1.0, 1.5, 2.0]
results = []
for vol_mult in volume_multipliers:
adjusted_volumes = [v * vol_mult for v in base_volumes]
row = {"volume_mult": vol_mult}
for lat in latencies:
result = backtest_with_costs(
base_signals, base_prices, adjusted_volumes,
cost_model, latency_ms=lat
)
row[f"lat_{lat}ms"] = round(result["adjusted_return"], 2)
results.append(row)
return pd.DataFrame(results)
这个敏感性分析能帮你回答关键问题:在什么样的市场条件下(成交量级别、延迟水平),策略仍然盈利?边界在哪里?
第四步:实盘校准
回测模型终究是假设,必须与真实交易数据对比校准:
def calibrate_against_reality(
backtest_costs: list,
actual_costs: list,
threshold_pct: float = 20.0
) -> dict:
"""
将回测成本与实际成本对比校准
Args:
backtest_costs: 回测估算的成本列表
actual_costs: 实际交易成本列表
threshold_pct: 可接受的最大偏差百分比
Returns:
校准结果和建议
"""
if len(backtest_costs) < 20:
return {"status": "insufficient_data", "needed": 20 - len(backtest_costs)}
errors = [(a - b) / b * 100 if b != 0 else 0
for a, b in zip(actual_costs, backtest_costs)]
mean_error = sum(errors) / len(errors)
max_error = max(abs(e) for e in errors)
needs_recalibration = max_error > threshold_pct
return {
"status": "calibration_needed" if needs_recalibration else "calibrated",
"mean_error_pct": mean_error,
"max_error_pct": max_error,
"recommendation": (
"成本模型已校准,预测偏差在可接受范围内"
if not needs_recalibration
else f"成本模型需要重新校准,最大偏差 {max_error:.1f}% 超过阈值"
)
}
六、TickDB 数据:构建准确成本模型的底层支撑
成本模型的准确性取决于输入数据的质量。滑点估算需要真实的买卖价差数据,冲击成本估算需要订单簿深度和成交量数据,延迟分析需要 tick 级的行情数据。
TickDB 提供的市场数据覆盖了成本估算所需的多个维度:
- 历史 K 线数据:包含成交量信息,用于计算日均成交量和冲击成本估算。
- 深度订单簿(depth):港股 10 档、数字货币 10 档实时深度,可观察订单簿结构变化。
- 实时行情:支持 WebSocket 推送,延迟控制在 100ms 以内,适合低延迟策略回测。
使用这些数据,你能构建更精确的成本模型。以下是一个完整的数据获取与成本分析流程:
import os
import requests
import time
class TickDBDataClient:
"""
TickDB 数据客户端:获取市场数据用于成本估算
"""
def __init__(self, api_key: str = None):
self.api_key = api_key or os.environ.get("TICKDB_API_KEY")
self.base_url = "https://api.tickdb.ai/v1"
def get_historical_klines(
self,
symbol: str,
interval: str = "1d",
limit: int = 365
) -> list:
"""
获取历史 K 线数据(含成交量)
用于:计算日均成交量、波动率、冲击成本估算
"""
headers = {"X-API-Key": self.api_key}
for retry in range(3):
try:
response = requests.get(
f"{self.base_url}/market/kline",
headers=headers,
params={
"symbol": symbol,
"interval": interval,
"limit": limit
},
timeout=(3.05, 10)
)
if response.status_code == 200:
data = response.json()
return data.get("data", {}).get("klines", [])
elif response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", 5))
time.sleep(retry_after)
continue
else:
return []
except requests.exceptions.Timeout:
time.sleep(2 ** retry) # 指数退避
continue
return []
def calculate_cost_model_params(self, klines: list) -> dict:
"""
基于历史 K 线数据计算成本模型参数
"""
if not klines:
return {}
volumes = [float(k.get("volume", 0)) for k in klines if k.get("volume")]
prices = [float(k.get("close", 0)) for k in klines if k.get("close")]
if not volumes or not prices:
return {}
import statistics
avg_volume = statistics.mean(volumes)
avg_price = statistics.mean(prices)
# 计算日收益率标准差作为波动率
if len(prices) > 1:
returns = [(prices[i] - prices[i-1]) / prices[i-1] for i in range(1, len(prices))]
volatility = statistics.stdev(returns) if len(returns) > 1 else 0.01
else:
volatility = 0.01
return {
"avg_daily_volume": avg_volume,
"avg_price": avg_price,
"volatility": volatility
}
# 使用示例
client = TickDBDataClient()
klines = client.get_historical_klines("700.HK", "1d", 252) # 港股腾讯一年数据
params = client.calculate_cost_model_params(klines)
print(f"成本模型参数:日均成交量={params['avg_daily_volume']:.0f}, "
f"平均价格={params['avg_price']:.2f}, "
f"日波动率={params['volatility']*100:.2f}%")
重要提示:TickDB 的 trades 接口(逐笔成交数据)目前不支持美股和 A 股,支持港股和数字货币。对于需要逐笔成交数据的订单流分析场景,建议使用港股或数字货币标的进行回测和成本校准。
七、核心结论与下一步行动
回测与实盘的差距,不是回测的"失败",而是你理解市场的"盲点"。三个核心成本项——滑点、延迟、容量——每一项都可以被量化、估算、校准。
关键结论:
滑点是非线性的:固定值假设会系统性地误导你。采用平方根法则估算冲击成本,订单规模翻倍时冲击成本只增加 41%($\sqrt{2}$),但这仍然是显著的成本。
延迟成本随行情状态放大:正常行情下 150ms 延迟的日均成本约 0.01%,但在波动率高的行情中可能放大 5-10 倍。事件驱动类策略必须将延迟纳入核心考量。
容量约束决定了策略的盈利能力边界:超过容量上限后,成本会吃掉所有利润,甚至导致亏损。在回测阶段就估算容量,设定资金上限。
校准重于精确:不需要完美的成本模型,只需要模型预测值与真实值的偏差在 20% 以内。收集真实交易数据,持续校准你的模型。
下一步行动
如果你想亲手验证本文的成本模型:
- 访问 tickdb.ai 注册(免费,无需信用卡)
- 在控制台生成 API Key
- 设置环境变量
TICKDB_API_KEY,使用本文代码获取历史数据进行成本估算 - 将估算结果与你的实盘交易记录对比,验证模型准确性
如果你需要更完整的回测框架:
联系 TickDB 技术支持,了解如何获取 tick 级行情数据用于更精确的滑点估算和订单流分析。
如果你习惯用 AI 辅助开发:
在 AI 助手中搜索安装 tickdb-market-data SKILL,用自然语言查询市场数据,加速策略原型验证。
风险提示:本文不构成任何投资建议。回测结果基于历史数据模拟,不代表未来收益。实际交易中存在本文未完全覆盖的成本和风险因素,包括但不限于:流动性枯竭、黑天鹅事件、交易系统故障、市场操纵。建议在实际使用任何策略前进行充分的模拟测试和风险评估。市场有风险,投资需谨慎。