问题描述
解决方案
这是一个基于**布林带(Bollinger Bands)**的日内均值回归策略。
策略逻辑
- 核心思想:价格总是围绕均值波动。当价格偏离均值过大(突破上轨或下轨)时,认为价格会回归均值。
- 标的:选择 股指期货(如 IF9999.CCFX)。
- 原因:A股股票实行T+1制度,无法当天买入当天卖出,且融券做空困难。期货支持T+0和双向交易,最适合日内均值回归策略。
- 指标计算:
- 中轨:过去 N 分钟的收盘价移动平均线(MA)。
- 上轨:中轨 + K倍标准差。
- 下轨:中轨 - K倍标准差。
- 交易信号:
- 开多:价格跌破下轨(超卖)。
- 开空:价格突破上轨(超买)。
- 平仓:价格回归到中轨附近。
- 风控:每日收盘前(14:55)强制平仓,不持仓过夜。
策略代码
# -*- coding: utf-8 -*-
from jqdata import *
import numpy as np
def initialize(context):
"""
初始化函数,设定基准、标的、参数等
"""
# 设定沪深300作为基准
set_benchmark('000300.XSHG')
# 开启动态复权模式(真实价格)
set_option('use_real_price', True)
# 过滤掉order系列API产生的比error级别低的log
log.set_level('order', 'error')
# 设定操作标的:沪深300股指期货主力合约
# 注意:日内回测建议使用期货,因为股票是T+1,无法日内回转
g.security = 'IF9999.CCFX'
# 策略参数
g.n = 20 # 均线窗口长度(20分钟)
g.k = 2.0 # 标准差倍数
g.open_num = 1 # 每次开仓手数
# 设定账户类型为期货账户,并给予初始资金
set_subportfolios([SubPortfolioConfig(cash=context.portfolio.starting_cash, type='futures')])
# 设定期货保证金比例(根据实际情况调整,这里设为15%)
set_option('futures_margin_rate', 0.15)
# 运行频率:每分钟运行一次
run_daily(trade_func, time='every_bar')
# 每日收盘前强制平仓,确保日内策略不留隔夜仓
run_daily(close_all, time='14:55')
def trade_func(context):
"""
交易主逻辑,每分钟执行一次
"""
# 获取当前时间
current_time = context.current_dt.time()
# 避免在刚开盘数据不足时或收盘前最后几分钟交易
if str(current_time) < '09:30:00' or str(current_time) > '14:50:00':
return
security = g.security
# 获取过去 g.n 分钟的收盘价数据
# include_now=True 表示包含当前这一分钟的实时bar(回测中)
# 如果是实盘,建议根据需求调整,通常使用过去N个已完成的bar
data = get_bars(security, count=g.n, unit='1m', fields=['close'], include_now=False)
if data is None or len(data) < g.n:
return
close_prices = data['close']
# 计算布林带
mid_line = close_prices.mean() # 中轨:均线
std = close_prices.std() # 标准差
up_line = mid_line + g.k * std # 上轨
low_line = mid_line - g.k * std # 下轨
# 获取当前最新价格
current_data = get_current_data()
current_price = current_data[security].last_price
# 获取当前持仓情况
# long_positions: 多单仓位
# short_positions: 空单仓位
long_pos = context.portfolio.long_positions[security]
short_pos = context.portfolio.short_positions[security]
# ---------------------------------------------------
# 交易逻辑
# ---------------------------------------------------
# 1. 平仓逻辑 (回归均值即平仓)
# 如果持有多单,且价格回归到中轨上方,平多
if long_pos.total_amount > 0 and current_price >= mid_line:
order_target(security, 0, side='long')
log.info("价格回归中轨,平多单。当前价: %.2f, 中轨: %.2f" % (current_price, mid_line))
# 如果持有空单,且价格回归到中轨下方,平空
if short_pos.total_amount > 0 and current_price <= mid_line:
order_target(security, 0, side='short')
log.info("价格回归中轨,平空单。当前价: %.2f, 中轨: %.2f" % (current_price, mid_line))
# 2. 开仓逻辑 (突破轨道即逆势开仓)
# 如果没有持仓
if long_pos.total_amount == 0 and short_pos.total_amount == 0:
# 价格跌破下轨,看多(均值回归),开多单
if current_price < low_line:
order(security, g.open_num, side='long')
log.info("价格跌破下轨,开多单。当前价: %.2f, 下轨: %.2f" % (current_price, low_line))
# 价格突破上轨,看空(均值回归),开空单
elif current_price > up_line:
order(security, g.open_num, side='short')
log.info("价格突破上轨,开空单。当前价: %.2f, 上轨: %.2f" % (current_price, up_line))
def close_all(context):
"""
收盘前强制平仓函数
"""
security = g.security
long_pos = context.portfolio.long_positions[security]
short_pos = context.portfolio.short_positions[security]
if long_pos.total_amount > 0:
order_target(security, 0, side='long')
log.info("收盘强制平多单")
if short_pos.total_amount > 0:
order_target(security, 0, side='short')
log.info("收盘强制平空单")
代码关键点解析
-
标的选择 (
IF9999.CCFX):- 代码中使用了沪深300股指期货主力合约。这是因为日内策略要求T+0(当天开仓当天平),而A股股票是T+1。如果你必须使用股票,需要你有底仓(即昨天已经买入该股票),通过做T的方式实现日内交易。
-
数据获取 (
get_bars):- 使用
get_bars获取过去20分钟的K线数据。 include_now=False表示我们使用过去20个已完成的分钟Bar来计算指标,避免当前分钟价格波动导致信号闪烁。
- 使用
-
布林带计算:
- 利用
numpy和pandas的特性,直接对获取的数组进行.mean()和.std()计算,效率较高。
- 利用
-
交易指令 (
order/order_target):- 期货交易需要指定
side参数。side='long'操作多单,side='short'操作空单。 order_target(security, 0, side='...')用于将仓位调整为0,即平仓。
- 期货交易需要指定
-
强制平仓 (
close_all):- 日内策略最重要的是风险控制。我们在
14:55触发强制平仓,防止因忘记平仓而持有隔夜风险。
- 日内策略最重要的是风险控制。我们在
如何在聚宽上运行
- 登录聚宽官网,进入“我的策略” -> “新建策略”。
- 将上述代码复制粘贴到代码编辑区。
- 设置回测参数:
- 开始/结束日期:建议选择近期的一个月进行测试(分钟级回测速度较慢)。
- 频率:必须选择 分钟。
- 初始资金:建议 500,000 或以上(股指期货一手保证金较高)。
- 点击“编译运行”或“运行回测”。
常见问题 Q&A
Q: 为什么不用股票做这个策略?
A: 股票是T+1交易,如果你上午买入,下午即使触发了卖出信号也无法卖出。除非你使用“底仓做T”的逻辑(即持有老股,买入新股,卖出老股),但这会使代码逻辑复杂很多。
Q: 为什么回测没有交易?
A: 可能是资金不足。股指期货一手合约价值较高(例如IF合约价值约100万,保证金15%约需15万)。如果初始资金设置太少(如1万),会导致下单失败。请检查日志中的 Order Rejected 信息。
Q: 如何调整策略的敏感度?
A: 修改 g.n(周期)和 g.k(标准差倍数)。
- 减小
g.k(如改为1.5):交易更频繁,但假信号可能增加。 - 增大
g.k(如改为2.5):交易更少,信号更可靠,但可能错过机会。