3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
这是一个基于 QMT 平台 Python API 编写的 CR 指标(能量指标) 交易策略。
CR 指标(Energy of Market)用于判断买卖气势的强弱。本策略采用经典的 CR 与其均线交叉 的逻辑:
# -*- coding: gbk -*-
import pandas as pd
import numpy as np
def init(ContextInfo):
"""
策略初始化函数
"""
# 设置策略运行的股票列表(示例:浦发银行)
ContextInfo.stock_code = '600000.SH'
ContextInfo.set_universe([ContextInfo.stock_code])
# 设置资金账号(请替换为您的真实资金账号)
ContextInfo.account_id = 'YOUR_ACCOUNT_ID'
ContextInfo.account_type = 'STOCK' # 股票账号
ContextInfo.set_account(ContextInfo.account_id)
# 策略参数设置
ContextInfo.N = 26 # CR指标的计算周期
ContextInfo.M = 10 # CR均线的计算周期
ContextInfo.period = '1d' # 运行周期:日线
# 买卖阈值设置
ContextInfo.buy_threshold = 150 # 买入时CR不应过高
ContextInfo.sell_threshold = 300 # 止盈/超买阈值
def get_cr_indicator(ContextInfo, stock_code):
"""
计算CR指标及其均线
"""
# 获取足够的历史数据,长度需要覆盖 N + M + 缓冲
count = ContextInfo.N + ContextInfo.M + 20
# 使用 get_market_data_ex 获取数据
# 注意:CR计算需要 High, Low
data_dict = ContextInfo.get_market_data_ex(
['high', 'low', 'close'],
[stock_code],
period=ContextInfo.period,
count=count,
dividend_type='front' # 前复权
)
if stock_code not in data_dict:
return None, None
df = data_dict[stock_code]
if len(df) < ContextInfo.N + 1:
return None, None
# 1. 计算中间价 MID = (昨日最高 + 昨日最低) / 2
# shift(1) 表示取前一行的数据
df['ref_high'] = df['high'].shift(1)
df['ref_low'] = df['low'].shift(1)
df['mid_price'] = (df['ref_high'] + df['ref_low']) / 2
# 2. 计算上升值和下跌值
# 上升值 = max(0, 今日最高 - 昨日中间价)
df['up_strength'] = (df['high'] - df['mid_price']).clip(lower=0)
# 下跌值 = max(0, 昨日中间价 - 今日最低)
df['down_strength'] = (df['mid_price'] - df['low']).clip(lower=0)
# 3. 计算 CR = N日上升和 / N日下跌和 * 100
# rolling(N).sum() 计算滚动求和
df['p1_sum'] = df['up_strength'].rolling(window=ContextInfo.N).sum()
df['p2_sum'] = df['down_strength'].rolling(window=ContextInfo.N).sum()
# 处理分母为0的情况,避免报错
df['cr'] = np.where(df['p2_sum'] == 0, 0, (df['p1_sum'] / df['p2_sum']) * 100)
# 4. 计算 CR 的均线
df['cr_ma'] = df['cr'].rolling(window=ContextInfo.M).mean()
# 返回最后两行数据用于判断交叉
return df.iloc[-2], df.iloc[-1]
def handlebar(ContextInfo):
"""
K线周期回调函数
"""
# 获取当前正在处理的K线索引
index = ContextInfo.barpos
# 获取当前时间
realtime = ContextInfo.get_bar_timetag(index)
stock = ContextInfo.stock_code
# 计算指标
prev_bar, curr_bar = get_cr_indicator(ContextInfo, stock)
if prev_bar is None or curr_bar is None:
return # 数据不足,跳过
# 提取当前和上一周期的 CR 及 MA 值
cr_curr = curr_bar['cr']
cr_prev = prev_bar['cr']
ma_curr = curr_bar['cr_ma']
ma_prev = prev_bar['cr_ma']
# 检查是否包含无效值(NaN)
if np.isnan(cr_curr) or np.isnan(ma_curr) or np.isnan(cr_prev):
return
# 获取当前持仓
positions = ContextInfo.get_trade_detail_data(ContextInfo.account_id, ContextInfo.account_type, 'POSITION')
holding_vol = 0
for pos in positions:
if pos.m_strInstrumentID == stock:
holding_vol = pos.m_nVolume
break
# --- 交易逻辑 ---
# 1. 买入条件:
# (1) CR 上穿 MA (金叉)
# (2) CR 当前值小于设定的安全阈值 (避免高位接盘)
is_golden_cross = (cr_prev <= ma_prev) and (cr_curr > ma_curr)
is_safe_zone = cr_curr < ContextInfo.buy_threshold
if is_golden_cross and is_safe_zone:
if holding_vol == 0:
# 全仓买入 (目标仓位调整为 100%)
print(f"[{ContextInfo.barpos}] 金叉买入信号: CR={cr_curr:.2f}, MA={ma_curr:.2f}")
order_target_percent(stock, 1.0, ContextInfo, ContextInfo.account_id)
# 2. 卖出条件:
# (1) CR 下穿 MA (死叉)
# (2) 或者 CR 超过超买阈值 (止盈风控)
is_death_cross = (cr_prev >= ma_prev) and (cr_curr < ma_curr)
is_overbought = cr_curr > ContextInfo.sell_threshold
if (is_death_cross or is_overbought) and holding_vol > 0:
# 清仓卖出 (目标仓位调整为 0%)
reason = "死叉" if is_death_cross else "超买"
print(f"[{ContextInfo.barpos}] {reason}卖出信号: CR={cr_curr:.2f}, MA={ma_curr:.2f}")
order_target_percent(stock, 0.0, ContextInfo, ContextInfo.account_id)
# 可视化输出 (仅在回测模式下有效,画在副图)
ContextInfo.paint('CR', cr_curr, -1, 0, 'red')
ContextInfo.paint('CR_MA', ma_curr, -1, 0, 'yellow')
数据获取 (get_market_data_ex):
get_market_data_ex 接口,这是 QMT 推荐的高效数据接口。high, low, close 字段。虽然 CR 计算主要用 High 和 Low,但 Close 通常用于复权计算和后续可能的扩展。count 设置为 N + M + 20,确保有足够的历史数据来计算移动平均线,避免计算初期出现 NaN(空值)。CR 指标计算 (get_cr_indicator):
pandas 的矢量化运算,避免了低效的循环。shift(1) 用于获取昨日数据。clip(lower=0) 替代了 max(x, 0) 的逻辑,处理负值情况。rolling(window=N).sum() 用于计算 N 日内的累加和。交易执行 (order_target_percent):
order_target_percent 函数。这是一个高级交易函数,它会自动计算需要买入或卖出的数量,将该股票的持仓调整到账户总资产的指定比例(这里是 1.0 即 100% 买入,0.0 即清仓)。passorder,这个函数在编写策略逻辑时更简洁,不需要手动计算手数。绘图 (paint):
ContextInfo.paint,这样在回测界面的副图中可以看到 CR 和 CR_MA 的走势,方便验证策略信号是否正确。init 函数中的 ContextInfo.account_id 为您的实际资金账号(如果是回测,系统会自动分配虚拟账号,可不改)。Q: 为什么 CR 指标要用昨日的中间价而不是今日的开盘价?
A: CR 指标的设计初衷是认为昨日的中间价((最高+最低)/2)是一个比较客观的均衡点。用今日最高/最低价与昨日均衡点比较,更能反映出今日多空双方相对于昨日均衡点的强弱程度。
Q: 代码中的 ContextInfo.buy_threshold = 150 是什么意思?
A: 这是一个风控条件。CR 指标如果数值过高(例如超过 200 或 300),说明市场已经过热,此时即使出现金叉也可能是诱多。限制买入阈值可以过滤掉高位风险。
Q: 如何将此策略应用到多只股票?
A: 在 init 函数中,将 ContextInfo.stock_code 改为一个列表,例如 ['600000.SH', '000001.SZ']。然后将 handlebar 函数中的逻辑放入一个 for stock in ContextInfo.get_universe(): 的循环中即可。