问题描述
解决方案
这是一个基于PTrade平台的简单期货均线策略。
策略逻辑
- 标的:设定一个期货合约(例如螺纹钢
rb2410.XSGE)。 - 指标:使用两条均线,短期均线(5日)和长期均线(10日)。
- 开仓(做多):当短期均线上穿长期均线(金叉),且当前无持仓时,买入开仓。
- 平仓:当短期均线下穿长期均线(死叉),且当前有多头持仓时,卖出平仓。
策略代码
def initialize(context):
"""
初始化函数,设置全局变量和股票池
"""
# 设置要操作的期货合约代码,这里以螺纹钢2410为例
# 注意:实盘或回测时请确保合约代码在有效期内
g.security = 'rb2410.XSGE'
# 设置股票池(虽然是期货,也需要设置universe以便获取数据)
set_universe(g.security)
# 设定均线周期
g.short_window = 5 # 短期均线周期
g.long_window = 10 # 长期均线周期
# 设定交易数量(手)
g.trade_amount = 1
def handle_data(context, data):
"""
盘中运行函数,每个周期(如每天或每分钟)运行一次
"""
# 获取历史收盘价数据
# 获取长度为 长周期 + 2,确保有足够的数据计算均线和前一时刻的值
# count建议设置稍大一些,避免计算指标时数据不足
hist = get_history(count=g.long_window + 5, frequency='1d', field='close', security_list=g.security)
# 如果数据获取失败或长度不足,则直接返回
if hist is None or len(hist) < g.long_window + 2:
log.info("数据不足,跳过本次计算")
return
# 获取收盘价序列
close_prices = hist['close']
# 计算短期均线 (MA5)
ma_short = close_prices.rolling(window=g.short_window).mean()
# 计算长期均线 (MA10)
ma_long = close_prices.rolling(window=g.long_window).mean()
# 获取当前时刻(最新)的均线值
current_ma_short = ma_short.iloc[-1]
current_ma_long = ma_long.iloc[-1]
# 获取上一时刻的均线值(用于判断交叉)
prev_ma_short = ma_short.iloc[-2]
prev_ma_long = ma_long.iloc[-2]
# 获取当前持仓信息
position = get_position(g.security)
# 多头持仓数量
long_amount = position.long_amount
# --- 交易逻辑 ---
# 判断金叉:短期均线 上穿 长期均线
# 逻辑:上一时刻短线<=长线,且当前时刻短线>长线
golden_cross = (prev_ma_short <= prev_ma_long) and (current_ma_short > current_ma_long)
# 判断死叉:短期均线 下穿 长期均线
# 逻辑:上一时刻短线>=长线,且当前时刻短线<长线
death_cross = (prev_ma_short >= prev_ma_long) and (current_ma_short < current_ma_long)
# 执行交易
if golden_cross:
# 如果金叉且当前没有多头持仓,则买入开仓
if long_amount == 0:
log.info("金叉出现 (MA5: %.2f, MA10: %.2f),买入开仓 %s" % (current_ma_short, current_ma_long, g.security))
# 买入开仓
buy_open(g.security, g.trade_amount)
elif death_cross:
# 如果死叉且当前有多头持仓,则卖出平仓
if long_amount > 0:
log.info("死叉出现 (MA5: %.2f, MA10: %.2f),卖出平仓 %s" % (current_ma_short, current_ma_long, g.security))
# 卖出平仓
# 注意:上期所品种(如螺纹钢XSGE)区分平今和平昨,此处使用默认设置
# 若需优先平今仓,可设置 close_today=True
sell_close(g.security, long_amount)
代码关键点解析
-
get_history:- 用于获取K线数据。这里我们获取了比长期均线周期稍多一点的数据(
g.long_window + 5),是为了确保计算滚动平均值(rolling mean)时前面有足够的数据填充,避免计算结果为NaN。 frequency='1d'表示日线级别回测。如果需要分钟级别,可改为'1m'等。
- 用于获取K线数据。这里我们获取了比长期均线周期稍多一点的数据(
-
均线计算:
- 使用了
pandas的rolling(window=...).mean()方法,这是计算移动平均线最标准、最高效的方法。
- 使用了
-
交叉判断:
- 仅仅判断
当前短线 > 当前长线是不够的,这只能说明当前处于多头排列。 - 为了捕捉“交叉”动作,必须对比“上一时刻”和“当前时刻”的状态变化。
- 仅仅判断
-
期货特有API:
buy_open(合约, 数量): 买入开仓(做多)。sell_close(合约, 数量): 卖出平仓(平多头)。get_position(合约).long_amount: 获取多头持仓数量。期货持仓分为多头(long)和空头(short),与股票不同。
-
Python兼容性:
- 代码使用了
%格式化字符串(如"MA5: %.2f" % value),这是为了兼容 Python 3.5 及更早版本,避免使用 f-string 导致报错。
- 代码使用了
Q&A
Q: 如何将策略改为分钟级别运行?
A: 在回测设置中将频率选为“分钟”,并将代码中 get_history 的 frequency 参数从 '1d' 改为 '1m' 或 '5m' 等即可。
Q: 如果我想做空(死叉开空单,金叉平空单)怎么改?
A: 需要使用 sell_open (卖出开仓) 和 buy_close (买入平仓)。同时通过 position.short_amount 获取空头持仓。
Q: 为什么代码里没有 import pandas?
A: PTrade 策略环境通常预置了 pandas、numpy 等常用库,且 get_history 返回的本身就是类似 DataFrame 的结构,可以直接调用 rolling 方法。如果报错,可以在文件开头添加 import pandas as pd。
Q: 上期所(XSGE)的平今仓和平昨仓有什么区别?
A: 上期所(如螺纹钢、铜)对平今仓(平掉当天开的仓位)和平昨仓(平掉昨天以前的仓位)的手续费规定可能不同。PTrade 的 sell_close 函数有一个 close_today 参数。默认为 False(优先平昨),如果设为 True 则仅平今仓。对于简单的策略,默认设置通常足够。