问题描述
解决方案
在量化交易中,凯利公式(Kelly Criterion)是一种著名的资金管理方法,用于在已知胜率和盈亏比的情况下,计算出能使账户长期增长率最大化的单次交易仓位比例。
以下是关于凯利公式的原理说明以及如何在 PTrade 中实现该策略的代码。
1. 凯利公式原理
凯利公式的基本形式如下:
$$f^* = \frac{b \times p - q}{b}$$
或者简化为:
$$f^* = p - \frac{1 - p}{b}$$
其中:
- $f^*$:应投入的资金比例(仓位比例)。
- $p$:获胜概率(胜率,例如 0.55 代表 55%)。
- $q$:失败概率($1 - p$)。
- $b$:盈亏比(平均盈利金额 / 平均亏损金额)。
注意:
- 如果计算出的 $f^*$ 小于或等于 0,意味着该策略期望收益为负,不应进行交易。
- 半凯利(Half Kelly):由于全凯利公式在实际交易中波动极其剧烈,且假设胜率和赔率是固定的(实际市场中是动态的),通常建议使用计算结果的 50%(即半凯利) 或更小比例来降低风险。
2. PTrade 策略实现代码
下面的代码展示了如何在 PTrade 中定义一个函数来计算凯利仓位,并根据策略的历史回测数据(胜率和盈亏比)来动态调整下单金额。
import numpy as np
def initialize(context):
"""
策略初始化函数
"""
# 设置股票池,这里以恒生电子为例
g.security = '600570.SS'
set_universe(g.security)
# ---------------------------------------------------
# 策略参数设置 (这些数据通常来自历史回测报告)
# ---------------------------------------------------
# 历史胜率 (例如 45%)
g.win_rate = 0.45
# 历史盈亏比 (例如 盈利平均赚2000,亏损平均亏800,则为 2.5)
g.pnl_ratio = 2.5
# 凯利乘数 (风险控制系数)
# 1.0 为全凯利 (激进,风险大)
# 0.5 为半凯利 (常用,平衡收益与风险)
g.kelly_multiplier = 0.5
# 单只股票最大持仓限制 (防止凯利公式计算出过大仓位,例如超过80%)
g.max_position_pct = 0.8
# 记录是否持仓
g.has_position = False
def calculate_kelly_fraction(win_rate, pnl_ratio):
"""
计算凯利公式建议的仓位比例
f = (bp - q) / b
"""
# 失败概率
loss_rate = 1 - win_rate
# 避免除以零
if pnl_ratio == 0:
return 0.0
# 应用公式
f = (pnl_ratio * win_rate - loss_rate) / pnl_ratio
# 如果结果小于0,说明期望收益为负,不应交易
if f < 0:
return 0.0
return f
def handle_data(context, data):
"""
按频率运行的策略主函数
"""
security = g.security
# 获取当前最新价格
current_price = data[security]['close']
# 简单的均线策略作为买卖信号示例
# 获取过去20天收盘价
history = get_history(21, '1d', 'close', security, fq=None, include=False)
if len(history) < 20:
return
# 计算均线 (Python 3.5 兼容写法)
close_prices = history['close'].values
ma5 = close_prices[-5:].mean()
ma20 = close_prices[-20:].mean()
# 获取当前账户总资产 (现金 + 持仓市值)
total_value = context.portfolio.portfolio_value
# ---------------------------------------------------
# 信号生成与仓位管理
# ---------------------------------------------------
# 买入信号:5日线上穿20日线
if ma5 > ma20 and not g.has_position:
# 1. 计算凯利比例
raw_kelly = calculate_kelly_fraction(g.win_rate, g.pnl_ratio)
# 2. 应用风险乘数 (如半凯利)
target_pct = raw_kelly * g.kelly_multiplier
# 3. 限制最大仓位
if target_pct > g.max_position_pct:
target_pct = g.max_position_pct
log.info("触发买入信号。原始凯利比例: %.2f, 调整后目标仓位: %.2f" % (raw_kelly, target_pct))
if target_pct > 0:
# 4. 计算目标买入金额
target_value = total_value * target_pct
# 执行下单
order_target_value(security, target_value)
g.has_position = True
# 卖出信号:5日线下穿20日线
elif ma5 < ma20 and g.has_position:
log.info("触发卖出信号,清仓。")
order_target(security, 0)
g.has_position = False
3. 代码关键点解析
-
参数输入 (
g.win_rate,g.pnl_ratio):- 凯利公式依赖于准确的概率统计。在实盘或模拟盘中,这两个参数通常来源于长期的历史回测报告。
- 你也可以编写逻辑在
after_trading_end中动态统计已平仓的交易记录来实时更新这两个参数,但初期建议使用回测固定的稳健参数。
-
凯利乘数 (
g.kelly_multiplier):- 全凯利(Multiplier = 1.0)在数学上能带来最大增长,但在金融市场中,由于参数估计的不确定性和“肥尾”风险,全凯利往往会导致巨大的回撤甚至爆仓。
- 代码中默认设置为
0.5(半凯利),这是业界常用的做法,能保留大部分收益潜力同时显著降低波动。
-
最大仓位限制 (
g.max_position_pct):- 即使凯利公式建议满仓(100%),出于分散风险和流动性的考虑,通常也会设置一个硬性的上限(如 80%)。
-
资金计算:
- 使用
context.portfolio.portfolio_value获取当前账户总资产。 order_target_value函数用于将持仓调整到目标金额,非常适合基于比例的资金管理。
- 使用
4. 风险提示
- 参数敏感性:凯利公式对胜率和盈亏比非常敏感。如果历史数据过拟合,导致输入的胜率虚高,凯利公式会给出过于激进的仓位建议,导致亏损。
- 黑天鹅事件:凯利公式假设输赢是基于概率分布的,但无法预测极端的市场崩盘。因此,止损策略必须与凯利公式配合使用。
Q&A
Q1: 为什么计算出的凯利比例是负数?
A1: 当 $p \times b < q$(即期望收益为负)时,凯利公式的结果为负。这意味着根据当前的历史表现,该策略长期来看是亏损的,数学上建议的下注比例为 0(即不交易)。
Q2: 我可以在多只股票上同时使用凯利公式吗?
A2: 可以,但需要注意相关性。如果多只股票走势高度相关,同时对每只股票使用凯利公式计算出的仓位加总可能会导致总杠杆过高。在多资产组合中,通常需要使用矩阵形式的凯利公式或简单地将单资产凯利值除以资产数量。
Q3: 如何动态更新胜率和盈亏比?
A3: 你需要建立一个全局列表(如 g.trade_records),在每次平仓时记录盈亏金额。然后编写一个函数计算该列表正收益次数占比(胜率)和平均盈利/平均亏损比(盈亏比),并在 handle_data 中调用该函数更新 g.win_rate 和 g.pnl_ratio。