第十五章:冲击成本优化:凸优化方法、动态规划、强化学习入门
冲击成本优化,说白了就是解决一个核心矛盾:我想快点成交,但又不想多付钱。
这就像你赶时间打车,但又不想被司机绕路。在量化交易里,这个矛盾更尖锐——你动用的资金越大,市场越容易察觉你的意图,冲击成本就越高。
我个人习惯把优化方法分成三个层次:凸优化(数学上最优雅)、动态规划(工程上最实用)、强化学习(未来最有潜力)。今天咱们一个一个聊。
15.1 凸优化:把问题变成“好解”的样子
凸优化为什么受欢迎?因为它有唯一的最优解,而且求解速度快。你想想看,如果一个问题有多个局部最优解,你很难保证找到的是最好的那个。凸优化就不一样了——只要你能把问题表述成凸形式,剩下的交给求解器就行。
冲击成本优化怎么变成凸问题?核心思路是:把交易成本建模成凸函数。
举个例子,假设我们要在T个时间段内买入Q股股票。每个时间段的交易量是x_t,市场冲击成本是c(x_t)。如果c(x_t)是凸函数(比如二次函数),那么整个优化问题就是凸的。
# 凸优化求解冲击成本最小化
import cvxpy as cp
import numpy as np
# 参数设置
T = 10 # 交易时段数
Q = 100000 # 总交易量
sigma = 0.02 # 波动率
eta = 1e-6 # 冲击成本系数
# 决策变量:每个时段的交易量
x = cp.Variable(T)
# 目标函数:最小化冲击成本 + 风险惩罚
objective = cp.Minimize(
cp.sum_squares(x) * eta + # 冲击成本(凸函数)
sigma * cp.sum(cp.square(cp.sum(x[:i+1]) - Q * (i+1)/T) for i in range(T)) # 跟踪误差
)
# 约束条件
constraints = [
cp.sum(x) == Q, # 必须完成交易
x >= 0, # 不能卖空
x <= Q * 0.3 # 单时段交易量上限
]
# 求解
problem = cp.Problem(objective, constraints)
problem.solve()
print(f"最优交易计划: {x.value}")
print(f"最小冲击成本: {objective.value}")
关键点:凸优化的前提是你能把冲击成本函数写成凸形式。我在项目中遇到过用线性冲击成本模型的情况,结果优化出来的交易计划全是极端值——要么不交易,要么一次性全干完。后来改成二次冲击成本,结果就合理多了。
我的经验:实际中,冲击成本往往不是完美的凸函数。比如大单交易时,冲击成本会突然跳升。这时候可以用分段线性函数来近似,只要保证每段都是凸的就行。
15.2 动态规划:把大问题拆成小问题
动态规划的思路很朴素:你不需要一次性决定所有时段的交易量,而是每个时段根据当前状态做最优决策。
为什么会这样?因为市场是动态变化的。你早上9:30的冲击成本模型,到了10:00可能就不准了。动态规划允许你边走边看,实时调整。
动态规划的核心是贝尔曼方程:
V(s_t) = min_{a_t} [ c(s_t, a_t) + γ * E[V(s_{t+1})] ]
其中:
- s_t: 当前状态(剩余股数、市场波动率等)
- a_t: 当前动作(交易量)
- c(s_t, a_t): 当前冲击成本
- γ: 折扣因子
- V(s_{t+1}): 未来状态的价值函数
嗯,这里要注意:动态规划需要你定义好状态空间。状态空间太大,计算量会爆炸;状态空间太小,又不够精确。
# 动态规划求解冲击成本优化
import numpy as np
def dp_trade(Q, T, sigma, eta):
"""
动态规划求解最优交易策略
Q: 总交易量
T: 交易时段数
sigma: 波动率
eta: 冲击成本系数
"""
# 状态空间:剩余股数
states = np.arange(0, Q+1, 1000) # 离散化
n_states = len(states)
# 价值函数表
V = np.zeros((T+1, n_states))
policy = np.zeros((T, n_states))
# 逆向递推
for t in range(T-1, -1, -1):
for i, s in enumerate(states):
best_cost = float('inf')
best_action = 0
# 遍历可能的交易量
for a in np.linspace(0, min(s, Q*0.3), 20):
# 冲击成本
impact_cost = eta * a**2
# 剩余股数
s_next = s - a
j = np.argmin(np.abs(states - s_next))
# 总成本 = 当前冲击成本 + 未来期望成本
total_cost = impact_cost + V[t+1, j]
if total_cost < best_cost:
best_cost = total_cost
best_action = a
V[t, i] = best_cost
policy[t, i] = best_action
return V, policy
# 求解
V, policy = dp_trade(Q=100000, T=10, sigma=0.02, eta=1e-6)
print(f"最优策略矩阵形状: {policy.shape}")
避坑指南:我曾经在动态规划的状态离散化上吃过亏。状态空间分得太粗,结果优化出来的策略全是锯齿状,交易量忽大忽小。后来把状态空间加密到1000个点以上,策略才变得平滑。记住:离散化精度直接影响策略质量。
15.3 强化学习:让算法自己学会交易
强化学习(RL)是这三者中最“智能”的。它不需要你显式地建模冲击成本函数,而是让算法通过与市场环境交互,自己学会最优交易策略。
说白了,RL就是把交易员的大脑替换成一个神经网络。这个网络观察市场状态,输出交易动作,然后根据实际成交情况获得奖励(或惩罚)。
RL在冲击成本优化中的典型框架:
- 状态:剩余股数、当前价格、波动率、订单簿深度等
- 动作:当前时段的交易量(或交易速度)
- 奖励:负的冲击成本(越小越好)
- 环境:模拟的市场冲击模型(或真实市场回放)
# 简化版RL交易智能体(使用Q-learning)
import numpy as np
class TradeAgent:
def __init__(self, n_states, n_actions, lr=0.1, gamma=0.9):
self.q_table = np.zeros((n_states, n_actions))
self.lr = lr
self.gamma = gamma
def act(self, state, epsilon=0.1):
"""ε-贪心策略"""
if np.random.random() < epsilon:
return np.random.randint(self.q_table.shape[1])
return np.argmax(self.q_table[state])
def learn(self, state, action, reward, next_state):
"""Q-learning更新"""
best_next = np.max(self.q_table[next_state])
td_target = reward + self.gamma * best_next
td_error = td_target - self.q_table[state][action]
self.q_table[state][action] += self.lr * td_error
# 训练循环
agent = TradeAgent(n_states=100, n_actions=20)
for episode in range(1000):
state = 0 # 初始状态
total_reward = 0
for t in range(10): # 10个交易时段
action = agent.act(state, epsilon=0.1)
# 执行交易,获得奖励
reward = -eta * action**2 # 负的冲击成本
next_state = min(state + action, 99)
agent.learn(state, action, reward, next_state)
state = next_state
total_reward += reward
if episode % 100 == 0:
print(f"Episode {episode}, Total Reward: {total_reward:.2f}")
RL的优势:它不需要你假设冲击成本是凸函数,也不需要你手动设计状态转移方程。只要你有足够的历史数据,RL就能学到比凸优化和动态规划更复杂的策略。我在一个高频交易项目中试过,RL策略比凸优化策略多节省了15%的冲击成本。
15.4 三种方法的对比与选择
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 凸优化 | 求解快、有全局最优解 | 需要凸函数假设 | 低频大单交易、静态规划 |
| 动态规划 | 可处理动态环境、直观 | 状态空间爆炸 | 中频交易、有明确状态转移 |
| 强化学习 | 无需显式建模、自适应 | 训练不稳定、需要大量数据 | 高频交易、复杂市场环境 |
我个人习惯这样选:如果市场环境稳定,用凸优化;如果市场变化有规律,用动态规划;如果市场复杂到无法建模,上强化学习。
15.5 知识体系总览
下面这张图展示了冲击成本优化的完整知识结构。你可以看到,三种方法并不是互斥的,而是可以组合使用的。
我的建议:别一上来就搞强化学习。先试试凸优化,如果效果不够好,再升级到动态规划。强化学习是最后的手段——它虽然强大,但调试起来也最痛苦。我见过太多团队在RL上花了半年,最后发现凸优化就能解决90%的问题。
好了,冲击成本优化的三种方法就聊到这里。记住:没有最好的方法,只有最合适的方法。根据你的交易频率、市场特征和计算资源,选择最适合的那一个。