问题描述
解决方案
在量化交易中,根据市场风险自动调整仓位大小是资金管理的核心。最经典且有效的方法是使用 ATR(平均真实波幅) 指标来衡量波动率风险,并据此计算仓位。
核心逻辑:基于波动率的仓位管理
这种方法的核心思想是:当市场波动剧烈(风险高)时,降低仓位;当市场波动平稳(风险低)时,增加仓位。 从而保证每笔交易承担的风险金额是固定的(例如总资金的 1%)。
计算公式:
- 单笔风险金额 = 总资产 $\times$ 单笔风险敞口比例(如 1%)
- 止损幅度 = $n \times ATR$(通常取 2 倍或 3 倍 ATR)
- 买入数量 = 单笔风险金额 $\div$ 止损幅度
PTrade 策略代码实现
以下是一个完整的 PTrade 策略示例。该策略会计算标的股票的 ATR 指标,根据当前的波动率自动计算应该买入多少股,确保每次交易的潜在亏损控制在总资金的 1% 以内。
import talib
import numpy as np
def initialize(context):
"""
初始化函数
"""
# 设定要操作的股票,这里以恒生电子为例
g.security = '600570.SS'
set_universe(g.security)
# 策略参数设置
g.risk_ratio = 0.01 # 每次交易愿意承担的风险比例(占总资金的1%)
g.atr_period = 14 # ATR指标的计算周期
g.atr_multiplier = 2.0 # 止损宽度系数(风险=ATR * 系数)
# 设置交易费用(回测用)
set_commission(commission_ratio=0.0003, min_commission=5.0)
def handle_data(context, data):
"""
盘中运行函数,每日或每分钟调用
"""
security = g.security
# 1. 获取历史行情数据用于计算ATR
# 多取一些数据以保证指标计算的稳定性
count = g.atr_period + 10
# 获取最高价、最低价、收盘价
hist = get_history(count, frequency='1d', field=['high', 'low', 'close'], security_list=security, fq='pre')
if len(hist['close']) < count:
log.info("历史数据不足,跳过本次计算")
return
# 将数据转换为numpy数组,供talib使用
high_prices = np.array(hist['high'])
low_prices = np.array(hist['low'])
close_prices = np.array(hist['close'])
# 2. 计算ATR指标 (Average True Range)
atr_values = talib.ATR(high_prices, low_prices, close_prices, timeperiod=g.atr_period)
current_atr = atr_values[-1]
# 如果ATR无效或为0,防止除零错误
if np.isnan(current_atr) or current_atr <= 0:
log.warning("ATR计算异常: %s" % current_atr)
return
# 3. 计算仓位大小
# 获取当前账户总资产
total_value = context.portfolio.portfolio_value
# 获取当前标的价格
current_price = data[security]['close']
# 计算单笔交易允许的最大亏损金额 (例如 100万 * 1% = 1万)
risk_money = total_value * g.risk_ratio
# 计算每股的风险金额 (例如 2 * ATR)
# 这意味着如果价格向不利方向波动 2个ATR,我们就止损,此时亏损额正好等于 risk_money
risk_per_share = current_atr * g.atr_multiplier
# 计算理论买入股数 = 允许亏损总额 / 每股风险金额
target_shares = int(risk_money / risk_per_share)
# 向下取整到100的倍数(A股一手为100股)
target_shares = int(target_shares / 100) * 100
# 4. 执行交易逻辑
# 获取当前持仓
position = get_position(security)
# 简单的均线策略作为买卖信号示例:
# 计算短期和长期均线
ma5 = talib.SMA(close_prices, timeperiod=5)[-1]
ma10 = talib.SMA(close_prices, timeperiod=10)[-1]
# 打印日志方便调试
log.info("当前价格: %.2f, ATR: %.2f, 理论仓位: %d" % (current_price, current_atr, target_shares))
# 金叉买入
if ma5 > ma10 and position.amount == 0:
if target_shares >= 100:
# 检查现金是否足够
cash = context.portfolio.cash
cost = target_shares * current_price
if cash >= cost:
log.info("买入信号触发 - 波动率调整仓位: 买入 %d 股" % target_shares)
order(security, target_shares)
else:
log.warning("资金不足,无法买入理论仓位")
# 死叉卖出
elif ma5 < ma10 and position.amount > 0:
log.info("卖出信号触发 - 清仓")
order_target(security, 0)
代码关键点解析
-
引入 TA-Lib 库:
PTrade 支持talib库,这是计算技术指标最高效的方式。我们使用talib.ATR来计算平均真实波幅。 -
风险金额计算 (
risk_money):
total_value * g.risk_ratio决定了你这一笔交易最多愿意亏多少钱。例如你有 100 万资金,设定风险比例为 1%,那么你这笔交易的底线是亏损不超过 1 万。 -
每股风险计算 (
risk_per_share):
current_atr * g.atr_multiplier定义了每股的止损宽度。- 如果某股票 ATR 很大(波动剧烈),
risk_per_share就会变大。 - 如果某股票 ATR 很小(波动平缓),
risk_per_share就会变小。
- 如果某股票 ATR 很大(波动剧烈),
-
仓位调整结果:
根据公式买入数量 = 风险金额 / 每股风险:- 波动大 -> 分母大 -> 买入数量少(自动降低风险)。
- 波动小 -> 分母小 -> 买入数量多(在安全范围内放大收益)。
常见问题 Q&A
Q: 为什么使用 ATR 而不是标准差?
A: ATR 考虑了跳空高开或低开的情况(True Range),比单纯基于收盘价的标准差更能反映真实的日内和隔日波动风险,特别适合用于确定止损幅度和仓位大小。
Q: g.atr_multiplier 设置为多少合适?
A: 通常设置为 2 到 3。这意味着你预留了 2-3 倍的日平均波动幅度作为缓冲。如果设置太小(如 0.5),很容易被正常的市场噪音震荡出局;设置太大则会导致仓位过小,资金利用率不足。
Q: 这个策略可以直接用于实盘吗?
A: 该代码是一个框架示例。在实盘前,你需要:
- 完善买卖信号逻辑(示例中仅使用了简单的 MA5/MA10 交叉)。
- 添加止损逻辑(一旦买入,当价格下跌超过
g.atr_multiplier * ATR时应触发止损,以闭环风险控制)。 - 考虑多只股票的资金分配问题(如果同时操作多只股票,需要防止总仓位超过 100%)。
Q: 如何在 PTrade 中查看 ATR 的数值是否正确?
A: 你可以在 handle_data 中使用 log.info(current_atr) 打印数值,并与同花顺或通达信软件中的 ATR 指标(参数保持一致,如 14 日)进行对比。注意复权方式(fq='pre')可能会导致数值与未复权图表有差异。