问题描述
解决方案
这是一个基于 QMT 平台 API 编写的 Parabolic SAR (抛物线转向指标) 交易策略。
策略逻辑说明
- 指标原理:Parabolic SAR (Stop and Reverse) 是一种趋势跟踪指标。
- 当股价位于 SAR 点位 上方 时,视为多头趋势(看涨)。
- 当股价位于 SAR 点位 下方 时,视为空头趋势(看跌)。
- 交易信号:
- 买入信号:收盘价从下方突破 SAR 值(金叉)。
- 卖出信号:收盘价从上方跌破 SAR 值(死叉)。
- 参数设置:
- 加速因子 (AF):初始 0.02,步长 0.02。
- 最大加速因子 (Max AF):0.2。
策略代码
# -*- coding: gbk -*-
import pandas as pd
import numpy as np
import talib
def init(ContextInfo):
"""
策略初始化函数
"""
# 设置资金账号 (请替换为您真实的资金账号)
ContextInfo.accID = '6000000000'
ContextInfo.set_account(ContextInfo.accID)
# 设置标的股票 (示例:浦发银行)
ContextInfo.stock_code = '600000.SH'
ContextInfo.set_universe([ContextInfo.stock_code])
# 策略参数设置
ContextInfo.sar_acceleration = 0.02 # 加速因子初始值和步长
ContextInfo.sar_maximum = 0.2 # 加速因子最大值
# 每次交易数量
ContextInfo.trade_vol = 100
print("SAR 策略初始化完成")
def handlebar(ContextInfo):
"""
K线周期运行函数
"""
# 获取当前图表的当前K线索引
index = ContextInfo.barpos
# 获取当前时间戳
realtime = ContextInfo.get_bar_timetag(index)
# 获取标的代码
stock = ContextInfo.stock_code
# 获取历史行情数据 (High, Low, Close)
# 为了计算 SAR,我们需要一定长度的历史数据,这里取 100 根 K 线
# 使用 get_market_data_ex 接口
data_map = ContextInfo.get_market_data_ex(
['high', 'low', 'close'],
[stock],
period=ContextInfo.period,
count=100,
dividend_type='front' # 前复权
)
if stock not in data_map:
return
df = data_map[stock]
# 确保数据量足够计算指标
if len(df) < 50:
return
# 提取 High 和 Low 用于计算 SAR
high_prices = df['high'].values
low_prices = df['low'].values
close_prices = df['close'].values
# 使用 talib 计算 SAR 指标
# 注意:talib 接收 numpy array
sar_values = talib.SAR(
high_prices,
low_prices,
acceleration=ContextInfo.sar_acceleration,
maximum=ContextInfo.sar_maximum
)
# 获取当前和上一根K线的数据
current_close = close_prices[-1]
current_sar = sar_values[-1]
prev_close = close_prices[-2]
prev_sar = sar_values[-2]
# 在图表上绘制 SAR 指标 (方便回测观察)
ContextInfo.paint('SAR', current_sar, -1, 0, 'yellow')
# ----------------------------------------------------------------
# 交易逻辑判断
# ----------------------------------------------------------------
# 只有在最后一根K线或者是回测模式下才执行交易逻辑
if not ContextInfo.is_last_bar():
return
# 获取当前持仓
position = get_position(ContextInfo, stock)
# 信号 1: 买入 (价格上穿 SAR)
# 逻辑: 上一根K线收盘价在SAR下方,当前K线收盘价在SAR上方
buy_signal = (prev_close < prev_sar) and (current_close > current_sar)
# 信号 2: 卖出 (价格下穿 SAR)
# 逻辑: 上一根K线收盘价在SAR上方,当前K线收盘价在SAR下方
sell_signal = (prev_close > prev_sar) and (current_close < current_sar)
# 执行买入
if buy_signal:
if position == 0:
print(f"[{stock}] 触发买入信号: 价格({current_close}) 上穿 SAR({current_sar})")
# 23: 买入, 1101: 单股单账号普通交易
passorder(23, 1101, ContextInfo.accID, stock, 5, -1, ContextInfo.trade_vol, ContextInfo)
# 执行卖出
elif sell_signal:
if position > 0:
print(f"[{stock}] 触发卖出信号: 价格({current_close}) 下穿 SAR({current_sar})")
# 24: 卖出, 1101: 单股单账号普通交易
passorder(24, 1101, ContextInfo.accID, stock, 5, -1, position, ContextInfo)
def get_position(ContextInfo, stock_code):
"""
辅助函数:获取指定股票的持仓数量
"""
# 获取持仓对象列表
# 'STOCK' 代表股票账户, 'POSITION' 代表持仓
positions = get_trade_detail_data(ContextInfo.accID, 'STOCK', 'POSITION')
for pos in positions:
if pos.m_strInstrumentID + '.' + pos.m_strExchangeID == stock_code:
return pos.m_nVolume
return 0
代码关键点解析
-
数据获取 (
get_market_data_ex):- SAR 指标计算依赖于最高价 (
high) 和最低价 (low)。 - 我们获取了最近 100 根 K 线的数据,以确保
talib.SAR函数有足够的数据进行收敛计算。 - 使用了
dividend_type='front'(前复权),这在回测和实盘中对于技术指标计算非常重要,可以避免分红派息导致的价格跳空影响指标准确性。
- SAR 指标计算依赖于最高价 (
-
指标计算 (
talib.SAR):- 利用 QMT 内置的
talib库进行计算,效率高且准确。 - 传入参数
acceleration(加速因子) 和maximum(最大值) 是 SAR 的核心参数。
- 利用 QMT 内置的
-
信号逻辑:
- 上穿 (金叉):
prev_close < prev_sar且current_close > current_sar。这意味着趋势由跌转涨。 - 下穿 (死叉):
prev_close > prev_sar且current_close < current_sar。这意味着趋势由涨转跌。
- 上穿 (金叉):
-
绘图 (
ContextInfo.paint):- 代码中加入了
ContextInfo.paint函数,这样在回测或模型运行时,主图上会画出黄色的 SAR 点位,方便您直观验证策略信号是否正确。
- 代码中加入了
-
交易执行 (
passorder):- 买入使用
opType=23,卖出使用opType=24。 - 下单价格类型
prType=5(最新价),价格填-1表示跟随最新价。 - 在下单前调用了自定义的
get_position函数检查持仓,避免在没有持仓时卖出(虽然系统会报错,但逻辑上处理更好)。
- 买入使用
如何使用
- 打开 QMT 客户端,进入【模型研究】或【策略编辑器】。
- 新建一个 Python 策略。
- 将上述代码完整复制粘贴进去。
- 修改
init函数中的ContextInfo.accID为您的真实或模拟资金账号。 - 点击【编译】检查无误后,选择一个股票(如 600000.SH)和周期(如日线),点击【运行】或【回测】。
Q&A
Q: 为什么 SAR 指标在盘整震荡行情中表现不佳?
A: SAR 是典型的趋势跟踪指标。在明显的单边上涨或下跌趋势中,它能很好地锁住利润。但在横盘震荡时,价格频繁上下穿过 SAR 点位,会导致频繁发出买卖信号(来回止损),从而产生连续的小额亏损。通常建议结合 ADX (平均趋向指标) 来判断当前是否有趋势,只有在趋势明显时才使用 SAR。
Q: 如何修改 SAR 的灵敏度?
A: 可以调整 init 函数中的 ContextInfo.sar_acceleration (加速因子)。
- 增大该值(例如 0.03):SAR 点位会更紧密地跟随价格,反应更灵敏,但假信号也会增加。
- 减小该值(例如 0.01):SAR 点位跟随较慢,更能容忍价格波动,适合捕捉大趋势,但信号会有滞后。
Q: passorder 函数中的 1101 是什么意思?
A: 1101 是 orderType(下单方式)的代码,代表:单股、单账号、普通、股/手方式下单。这是最常用的股票交易下单模式。如果您需要按金额下单(例如买入 1 万元),可以使用 1102。