问题描述
解决方案
在PTrade中,当股票发生除权除息(分红、送股、配股等)时,股价会自然回落。如果您使用默认的“不复权”数据计算均线,历史的高价和除权后的低价混合会导致均线出现非市场因素的“跳空”下跌,从而产生错误的交易信号。
要修正这个问题,需要在获取历史数据时使用 “前复权”(Forward Adjustment)。
核心解决方案
在调用 get_history 或 get_price 函数时,设置参数 fq='pre'。
fq=None(默认):不复权。真实记录当时的成交价格,除权日会出现缺口。fq='pre'(前复权):以当前价格为基准,向下调整历史价格。这是策略回测和实盘中最常用的模式,因为它能保持K线形态的连续性,且最新价格与盘面实时价格一致。fq='post'(后复权):以历史上市价格为基准,向上调整当前价格。通常用于计算长期累计收益,但不适合实盘交易逻辑(因为计算出的价格与盘口挂单价格不一致)。
策略代码示例
以下是一个完整的双均线策略示例,展示了如何正确使用 fq='pre' 来获取平滑的均线数据。
def initialize(context):
"""
初始化函数
"""
# 设置我们要操作的股票,以恒生电子为例
g.security = '600570.SS'
set_universe(g.security)
# 设定均线周期
g.short_window = 5
g.long_window = 10
def handle_data(context, data):
"""
盘中运行函数
"""
security = g.security
# ------------------------------------------------------------------
# 核心修正点:获取历史数据时,务必添加 fq='pre' 参数
# ------------------------------------------------------------------
# 获取过去15天的收盘价(多取几天以防停牌或计算余量)
# 如果不加 fq='pre',一旦遇到除权除息,ma_long 的计算结果将瞬间暴跌
history_data = get_history(count=15, frequency='1d', field='close',
security_list=security, fq='pre', include=False)
# 提取收盘价数组
close_prices = history_data['close']
# 确保数据长度足够计算长周期均线
if len(close_prices) < g.long_window:
return
# 计算短期均线 (5日)
ma_short = close_prices[-g.short_window:].mean()
# 计算长期均线 (10日)
ma_long = close_prices[-g.long_window:].mean()
# 获取当前最新价格
current_price = data[security]['close']
# 获取当前持仓
position = get_position(security)
# 打印日志方便观察复权后的均线数值
# log.info("日期:%s, 5日均线:%.2f, 10日均线:%.2f, 现价:%.2f" % (context.blotter.current_dt, ma_short, ma_long, current_price))
# 简单的金叉死叉逻辑
# 金叉:短均线上穿长均线,且无持仓 -> 买入
if ma_short > ma_long and position.amount == 0:
log.info("金叉出现 - 买入 %s" % security)
order_target_value(security, context.portfolio.cash)
# 死叉:短均线下穿长均线,且有持仓 -> 卖出
elif ma_short < ma_long and position.amount > 0:
log.info("死叉出现 - 卖出 %s" % security)
order_target(security, 0)
常见问题 Q&A
Q: 为什么实盘交易推荐用前复权(pre)而不是后复权(post)?
A: 后复权会将当前股价向上调整(例如某股票现价10元,后复权后可能是500元)。如果您在策略中使用 limit_price(限价单)或判断当前价格是否触发止损,使用后复权计算出的500元去和交易所实时的10元做对比,会导致严重的逻辑错误。前复权则保证了历史K线形态连续,同时当前价格就是交易所的实时价格。
Q: get_price 函数也支持复权吗?
A: 支持。get_price 的参数用法与 get_history 完全一致,同样设置 fq='pre' 即可。
Q: 如果我只做日内Tick级别的交易,需要复权吗?
A: 如果您的策略只依赖当天的Tick数据(如 get_snapshot 或 tick_data),不需要复权,因为当天之内没有除权缺口。但如果您在Tick策略中需要参考过去几天的日线均线作为趋势判断,那么获取日线数据的那部分代码必须复权。
Q: 为什么有时候使用了 fq='pre' 数据还是不对?
A: 请检查 count 数量。如果 count 设置得太小(例如刚好等于均线周期),且刚好遇到停牌,可能会导致有效数据不足。建议 get_history 的 count 设置得比所需均线周期稍大一些(如计算10日均线,取15或20条数据),利用切片(如 [-10:])来计算,这样更稳健。