第30章:课程总结与项目实战:构建一个完整的做市策略回测与优化系统
终于到了最后一章。说实话,写到这里我挺感慨的。从第一行代码开始,我们一步步搭建了做市策略的整个知识体系。这一章,咱们不聊新概念,而是把之前所有东西串起来,亲手构建一个完整的回测与优化系统。
我个人习惯,在项目收尾时一定要做一次“全景复盘”。你想想看,做市策略的核心无非三件事:报价逻辑、库存管理、风险控制。这三件事拧在一起,才是一个能跑的系统。
系统架构总览
先看整体结构。我画了一张图,帮你快速建立全局视角:
这张图我反复改了好几版。你会发现,数据在最底层,策略在中间,回测优化在最上层。为什么这么排?因为数据是地基,策略是骨架,回测是检验手段。我在项目中遇到过不少团队,一上来就调参数,结果数据没清洗干净,全白干。
核心模块实现
咱们拆开来看。每个模块我都给出关键代码,你直接拿去用。
1. 数据加载与预处理
数据质量决定回测质量。我习惯用Level2 Tick数据,但如果没有,1分钟Bar也能凑合。
import pandas as pd
import numpy as np
def load_market_data(file_path, freq='1min'):
"""
加载行情数据,支持Tick和Bar两种格式
"""
df = pd.read_csv(file_path, parse_dates=['timestamp'])
df.set_index('timestamp', inplace=True)
# 重采样到指定频率
if freq != 'tick':
df = df.resample(freq).agg({
'bid_price1': 'last',
'ask_price1': 'last',
'bid_volume1': 'sum',
'ask_volume1': 'sum',
'last_price': 'last',
'volume': 'sum'
}).fillna(method='ffill')
return df
fillna(method='ffill') 是你的好朋友。
2. 做市策略引擎
这是核心。我实现了一个带库存管理的做市策略,支持动态调整价差。
class MarketMakingStrategy:
def __init__(self, spread=0.001, position_limit=1000, rebalance_threshold=0.5):
self.spread = spread # 价差宽度(百分比)
self.position_limit = position_limit # 最大持仓
self.rebalance_threshold = rebalance_threshold # 再平衡阈值
self.position = 0 # 当前持仓
self.cash = 1000000 # 初始资金
def generate_quotes(self, mid_price, inventory):
"""
根据当前库存调整报价
"""
# 库存偏移量:持仓越多,报价越偏向卖出
inventory_skew = inventory / self.position_limit
adjusted_spread = self.spread * (1 + abs(inventory_skew))
bid_price = mid_price * (1 - adjusted_spread / 2 - inventory_skew * 0.0001)
ask_price = mid_price * (1 + adjusted_spread / 2 - inventory_skew * 0.0001)
return bid_price, ask_price
def check_rebalance(self):
"""
检查是否需要再平衡
"""
return abs(self.position) / self.position_limit > self.rebalance_threshold
说白了,这个策略的核心逻辑就是:库存多了就压低买价、抬高卖价,让市场帮你把库存消化掉。我刚开始做的时候,总想着靠预测方向赚钱,后来发现做市商赚的是流动性差价,不是方向性收益。
3. 模拟撮合引擎
回测的关键在于撮合逻辑。不能太理想化,否则结果失真。
class SimulatedExchange:
def __init__(self, slippage=0.0001):
self.slippage = slippage # 滑点
self.trades = []
def match_order(self, quote_bid, quote_ask, market_bid, market_ask, volume):
"""
模拟撮合:市价单与做市报价的交互
"""
# 买单成交条件:做市卖价 <= 市场买价
buy_volume = 0
if quote_ask <= market_bid:
buy_volume = volume
fill_price = quote_ask * (1 + self.slippage)
# 卖单成交条件:做市买价 >= 市场卖价
sell_volume = 0
if quote_bid >= market_ask:
sell_volume = volume
fill_price = quote_bid * (1 - self.slippage)
return buy_volume, sell_volume, fill_price
参数优化:从手动到自动
参数优化是回测的终极目标。我提供两种方法:网格搜索和贝叶斯优化。
网格搜索
简单粗暴,适合参数少的情况。
def grid_search(strategy_class, data, param_grid):
best_params = None
best_sharpe = -np.inf
for spread in param_grid['spread']:
for limit in param_grid['position_limit']:
strategy = strategy_class(spread=spread, position_limit=limit)
result = backtest(strategy, data)
sharpe = result['sharpe_ratio']
if sharpe > best_sharpe:
best_sharpe = sharpe
best_params = {'spread': spread, 'position_limit': limit}
return best_params, best_sharpe
贝叶斯优化
参数多的时候,网格搜索太慢了。贝叶斯优化能更快找到最优区域。
from skopt import gp_minimize
def objective(params):
spread, limit, threshold = params
strategy = MarketMakingStrategy(
spread=spread,
position_limit=int(limit),
rebalance_threshold=threshold
)
result = backtest(strategy, data)
return -result['sharpe_ratio'] # 最小化负夏普
res = gp_minimize(
objective,
dimensions=[(0.0005, 0.005), (500, 5000), (0.3, 0.8)],
n_calls=50,
random_state=42
)
print(f"最优参数:价差={res.x[0]:.4f}, 持仓限制={int(res.x[1])}, 再平衡阈值={res.x[2]:.2f}")
绩效评估指标
回测完了,怎么判断好坏?我一般看这几个指标:
| 指标 | 计算公式 | 说明 |
|---|---|---|
| 夏普比率 | (年化收益 - 无风险利率) / 年化波动率 | 风险调整后收益,做市策略通常 > 2 |
| 最大回撤 | max(峰值 - 谷值) / 峰值 | 做市策略应 < 5%,否则风控有问题 |
| 胜率 | 盈利交易数 / 总交易数 | 做市策略胜率通常较高,但单笔盈利小 |
| 库存周转率 | 总交易量 / 平均库存 | 越高说明流动性提供越活跃 |
你想想看,如果一个做市策略夏普比率不到1,最大回撤超过10%,那还不如去买ETF。我见过最离谱的一个策略,回测夏普3.5,实盘两周爆仓——后来发现是回测时没算手续费。
完整回测流程
最后,我把所有模块串起来,形成一个完整的回测流程:
def run_full_backtest(config):
# 1. 加载数据
data = load_market_data(config['data_path'], config['freq'])
# 2. 初始化策略和交易所
strategy = MarketMakingStrategy(
spread=config['spread'],
position_limit=config['position_limit'],
rebalance_threshold=config['rebalance_threshold']
)
exchange = SimulatedExchange(slippage=config['slippage'])
# 3. 回测主循环
for timestamp, row in data.iterrows():
mid_price = (row['bid_price1'] + row['ask_price1']) / 2
bid, ask = strategy.generate_quotes(mid_price, strategy.position)
buy_vol, sell_vol, fill_price = exchange.match_order(
bid, ask, row['bid_price1'], row['ask_price1'], config['trade_volume']
)
# 更新持仓和资金
strategy.position += buy_vol - sell_vol
strategy.cash -= buy_vol * fill_price
strategy.cash += sell_vol * fill_price
# 检查再平衡
if strategy.check_rebalance():
# 执行再平衡逻辑(略)
pass
# 4. 计算绩效
result = calculate_performance(strategy, data)
return result
核心要点:
- 数据质量 > 策略复杂度。先花70%时间清洗数据。
- 回测必须包含手续费、滑点、冲击成本。
- 参数优化要避免过拟合,建议用滚动窗口验证。
- 库存管理是做市策略的生命线,别忽视。
嗯,到这里,整个课程的内容就全部结束了。从第一行的K线基础,到最后一行的完整回测系统,我们走过了30章。说实话,做市策略这条路没有终点,市场在变,参数要调,系统要迭代。但只要你掌握了这套方法论,无论市场怎么变,你都能快速适应。
最后送你一句话:回测是过去,实盘是未来。别让回测的幻觉,毁了实盘的信心。