问题描述
解决方案
这是一个基于聚宽(JoinQuant)平台的双均线(Dual Moving Average)策略代码。
策略逻辑说明
- 标的:选取了一组具有代表性的股票(如平安银行、万科A、贵州茅台、招商银行)作为股票池。
- 指标:
- 短期均线:5日均线(MA5)
- 长期均线:20日均线(MA20)
- 交易信号:
- 金叉买入:当短期均线大于长期均线,且当前无持仓时,买入。
- 死叉卖出:当短期均线小于长期均线,且当前有持仓时,卖出。
- 资金管理:采用等权重分配资金,即总资产除以股票池数量作为每只股票的目标持仓金额。
策略代码
# -*- coding: utf-8 -*-
# 导入聚宽函数库
import jqdata
def initialize(context):
"""
初始化函数,设定基准、手续费、股票池及技术参数
"""
# 设定沪深300作为基准
set_benchmark('000300.XSHG')
# 开启动态复权模式(真实价格)
set_option('use_real_price', True)
# 过滤掉order系列API产生的比error级别低的log
log.set_level('order', 'error')
# 设定股票类每笔交易时的手续费
# 买入时佣金万分之三,卖出时佣金万分之三加千分之一印花税, 每笔交易佣金最低扣5块钱
set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')
# --- 策略参数设置 ---
# 设定短期均线窗口
g.short_window = 5
# 设定长期均线窗口
g.long_window = 20
# 设定要操作的股票池 (这里示例选取了几个不同行业的龙头股)
g.security_list = [
'000001.XSHE', # 平安银行
'000002.XSHE', # 万科A
'600519.XSHG', # 贵州茅台
'600036.XSHG' # 招商银行
]
# 每天开盘时运行策略
run_daily(market_open, time='open')
def market_open(context):
"""
每日交易逻辑
"""
# 获取当前持仓的股票列表
current_positions = context.portfolio.positions
# 计算每只股票应该分配的资金 (总资产 / 股票池数量)
# 这种方式是简单的等权重分配
target_value_per_stock = context.portfolio.total_value / len(g.security_list)
for security in g.security_list:
# 获取股票过去 g.long_window 天的收盘价数据
# 注意:在按天回测中,time='open'时获取的是昨天及以前的数据,避免未来函数
hist_data = attribute_history(security, g.long_window, '1d', ['close'])
# 如果数据长度不足(例如新股上市时间太短),则跳过
if len(hist_data) < g.long_window:
continue
# 获取当前标的的信息(用于判断是否停牌)
current_data = get_current_data()[security]
if current_data.paused:
continue
# --- 计算均线 ---
# 收盘价序列
close_prices = hist_data['close']
# 计算短期均线 (取最后 short_window 天的平均值)
ma_short = close_prices[-g.short_window:].mean()
# 计算长期均线 (取所有 long_window 天的平均值)
ma_long = close_prices.mean()
# --- 交易逻辑 ---
# 获取该股票当前的持仓数量
current_amount = context.portfolio.positions[security].total_amount
# 1. 金叉:短期均线 > 长期均线
if ma_short > ma_long:
# 如果当前没有持仓,或者持仓价值远低于目标价值,则买入/加仓
# 这里使用 order_target_value 自动调整仓位到目标金额
if current_amount == 0:
log.info("金叉买入: %s (MA5:%.2f, MA20:%.2f)" % (security, ma_short, ma_long))
order_target_value(security, target_value_per_stock)
# 2. 死叉:短期均线 < 长期均线
elif ma_short < ma_long:
# 如果当前有持仓,则清仓卖出
if current_amount > 0:
log.info("死叉卖出: %s (MA5:%.2f, MA20:%.2f)" % (security, ma_short, ma_long))
order_target(security, 0)
代码关键点解析
attribute_history: 这是获取历史数据的核心函数。在run_daily(..., time='open')模式下,它获取的是截止到昨天收盘的数据。这是为了在今天开盘时根据昨天的指标进行决策,避免使用今天的收盘价(未来数据)来决定今天的开盘交易。order_target_value: 这是一个非常实用的下单函数。它会根据当前股价自动计算需要买入或卖出多少股,使该股票的持仓市值达到你设定的target_value_per_stock。get_current_data: 用于检查股票是否停牌(paused),避免对停牌股票下单导致报错或无效订单。- 资金管理: 代码中使用了
context.portfolio.total_value / len(g.security_list)来计算每只股票的仓位。这意味着策略会尽量保持每只股票占用相同的资金比例。
如何使用
- 登录聚宽官网。
- 进入“我的策略” -> “新建策略”。
- 将上述代码复制粘贴到代码编辑区。
- 点击“编译运行”进行回测。
Q&A
Q: 如何修改均线的天数?
A: 修改 initialize 函数中的 g.short_window (短期) 和 g.long_window (长期) 变量的值即可。例如改为 10日和 60日均线。
Q: 如何更换股票池?
A: 修改 g.security_list 列表。你可以手动输入股票代码,也可以使用 get_index_stocks('000300.XSHG') 来获取沪深300的所有成分股(注意:如果股票池很大,回测速度会变慢)。
Q: 为什么使用 order_target_value 而不是 order?
A: order 是按股数下单,而 order_target_value 是按目标市值下单。在多股票策略中,按市值分配资金(例如每只股票买1万元)比按股数分配(例如每只股票买100股)更能平衡风险,因为不同股票的单价差异很大。
Q: 策略是在盘中实时成交吗?
A: 本代码设置的是 run_daily(market_open, time='open'),意味着回测引擎会在每天开盘时(09:30)根据昨天的收盘数据计算信号,并以开盘价撮合成交。这是日线级别策略最常用的回测方式。