问题描述
解决方案
编写基于枢轴点(Pivot Points)的日内交易策略,核心在于利用前一个交易日的最高价(High)、最低价(Low)和收盘价(Close)来计算当天的关键支撑位和阻力位。
由于A股市场实行T+1制度,纯粹的日内回转交易(当天买当天卖)通常需要通过股指期货(如IF、IC)或持有底仓的股票来实现。为了演示最标准的日内策略逻辑,以下代码将基于股指期货(IF - 沪深300主力合约)编写,因为期货支持T+0交易,适合日内策略。
策略逻辑
- 计算枢轴点(盘前计算):
- Pivot (P) = (昨日最高价 + 昨日最低价 + 昨日收盘价) / 3
- 阻力位 1 (R1) = 2 $\times$ P - 昨日最低价
- 支撑位 1 (S1) = 2 $\times$ P - 昨日最高价
- 交易信号(盘中运行):
- 做多信号:当价格突破 R1 时,视为多头强势,开多单。
- 做空信号:当价格跌破 S1 时,视为空头强势,开空单。
- 风控与平仓:
- 日内平仓:每日收盘前(如14:55)强制平掉所有仓位,不持仓过夜。
- 反转平仓:如果持有空单但价格突破R1,则平空开多;反之亦然。
JoinQuant 策略代码实现
# -*- coding: utf-8 -*-
from jqdata import *
def initialize(context):
"""
初始化函数,设定基准、手续费、账户类型等
"""
# 设定沪深300作为基准
set_benchmark('000300.XSHG')
# 开启动态复权模式(真实价格)
set_option('use_real_price', True)
# 过滤掉order系列API产生的比error级别低的log
log.set_level('order', 'error')
# 重要:因为是日内T+0策略,我们需要配置期货账户
# 初始资金设为 500,000 元
set_subportfolios([SubPortfolioConfig(cash=500000, type='index_futures')])
# 设置期货交易手续费(以中金所为例,平今仓较贵,此处仅为示例设置)
set_order_cost(OrderCost(open_commission=0.000023,
close_commission=0.000023,
close_today_commission=0.0023), type='index_futures')
# 定义全局变量
g.underlying_symbol = 'IF' # 交易标的:沪深300股指期货
g.future = None # 存储当前主力合约代码
# 存储枢轴点数据
g.P = 0
g.R1 = 0
g.S1 = 0
# 定时运行函数
# 1. 开盘前计算枢轴点
run_daily(before_market_open, time='09:00')
# 2. 盘中每分钟检测信号
run_daily(market_open, time='every_bar')
# 3. 收盘前强制平仓
run_daily(close_all_positions, time='14:55')
def before_market_open(context):
"""
开盘前运行:获取主力合约,计算 Pivot Points
"""
# 获取当前日期的主力合约代码
g.future = get_dominant_future(g.underlying_symbol)
# 获取前一个交易日的日线数据(High, Low, Close)
# count=1, unit='1d' 获取的是昨天的数据
h_data = attribute_history(g.future, 1, '1d', ['high', 'low', 'close'], skip_paused=True)
if len(h_data) > 0:
high = h_data['high'][-1]
low = h_data['low'][-1]
close = h_data['close'][-1]
# 计算 Pivot Points
g.P = (high + low + close) / 3
g.R1 = 2 * g.P - low
g.S1 = 2 * g.P - high
# 记录日志
log.info(f"主力合约: {g.future}")
log.info(f"昨日数据 - H:{high}, L:{low}, C:{close}")
log.info(f"今日枢轴 - P:{g.P:.2f}, R1:{g.R1:.2f}, S1:{g.S1:.2f}")
def market_open(context):
"""
盘中运行:根据价格与 R1/S1 的关系进行交易
"""
if not g.future:
return
# 获取当前最新价格
current_data = get_current_data()
if g.future not in current_data:
return
current_price = current_data[g.future].last_price
# 获取当前持仓情况
long_pos = context.portfolio.long_positions[g.future].total_amount
short_pos = context.portfolio.short_positions[g.future].total_amount
# 资金管理:每次使用约 80% 的可用资金
available_cash = context.portfolio.available_cash
# --- 交易逻辑 ---
# 1. 突破 R1:看多
if current_price > g.R1:
# 如果持有空单,先平空
if short_pos > 0:
order_target(g.future, 0, side='short')
log.info(f"价格突破 R1 ({g.R1}),平空单")
# 如果没有多单,开多单
if long_pos == 0:
# 注意:期货开仓需要指定 side='long'
order_value(g.future, available_cash * 0.8, side='long')
log.info(f"价格突破 R1 ({g.R1}),开多单,价格: {current_price}")
# 2. 跌破 S1:看空
elif current_price < g.S1:
# 如果持有多单,先平多
if long_pos > 0:
order_target(g.future, 0, side='long')
log.info(f"价格跌破 S1 ({g.S1}),平多单")
# 如果没有空单,开空单
if short_pos == 0:
# 注意:期货开仓需要指定 side='short'
order_value(g.future, available_cash * 0.8, side='short')
log.info(f"价格跌破 S1 ({g.S1}),开空单,价格: {current_price}")
# 3. (可选) 止损逻辑:例如价格回到 P 点平仓
# 这里为了简化,暂不添加复杂的止损,仅依赖反转信号和尾盘平仓
def close_all_positions(context):
"""
收盘前强制平仓,确保日内交易不持仓过夜
"""
if g.future:
long_pos = context.portfolio.long_positions[g.future].total_amount
short_pos = context.portfolio.short_positions[g.future].total_amount
if long_pos > 0:
order_target(g.future, 0, side='long')
log.info("尾盘强制平多")
if short_pos > 0:
order_target(g.future, 0, side='short')
log.info("尾盘强制平空")
代码关键点解析
- 账户配置 (
set_subportfolios):- 因为是日内策略,必须使用期货账户(
type='index_futures')才能实现T+0交易(当天开仓当天平仓)。如果是股票账户,买入后当天无法卖出。
- 因为是日内策略,必须使用期货账户(
- 数据获取 (
attribute_history):- 在
before_market_open中调用attribute_history(..., 1, '1d', ...)。在开盘前(09:00),获取过去1天的数据实际上就是昨天的收盘数据,这是计算枢轴点的标准方法。
- 在
- 主力合约 (
get_dominant_future):- 期货合约会随时间换月,使用
get_dominant_future可以自动跟踪流动性最好的主力合约。
- 期货合约会随时间换月,使用
- 交易执行 (
order_value/order_target):- 期货交易必须指定
side参数('long'为做多,'short'为做空)。 - 平仓时通常使用
order_target(..., 0, side=...)将仓位调整为0。
- 期货交易必须指定
- 日内风控:
- 通过
run_daily(close_all_positions, time='14:55')确保在收盘前5分钟平掉所有仓位,避免隔夜风险。
- 通过
常见问题 Q&A
Q: 这个策略可以直接用于A股股票吗?
A: 不能直接用于开新仓。 A股是T+1制度,今天买入的股票明天才能卖出。如果你想在股票上应用此策略,需要你有“底仓”(即昨天已经持有的股票),通过“高抛低吸”的方式变相实现T+0。
Q: 为什么使用 attribute_history 而不是 get_price?
A: 在 initialize 或 before_market_open 等回测框架函数中,attribute_history 会自动根据回测的当前时间获取历史数据,能有效避免未来函数,且语法更简洁。
Q: 如何增加 R2, S2, R3, S3 等更多支撑阻力位?
A: 你可以在 before_market_open 中添加公式:
- $R2 = P + (High - Low)$
- $S2 = P - (High - Low)$
然后在market_open中增加相应的判断逻辑(例如突破R2加仓,或者在R2处止盈)。