问题描述
解决方案
这是一个基于 QMT 平台编写的 K 线形态识别策略。该策略利用 QMT 内置的 TA-Lib 库(技术分析库)来自动识别 锤头线 (Hammer)、启明之星 (Morning Star) 和 黄昏之星 (Evening Star),并根据识别结果进行买卖操作。
策略逻辑说明
- 数据获取:使用
ContextInfo.get_market_data_ex获取股票的历史 OHLC(开高低收)数据。 - 形态识别:调用
talib库的函数:CDLHAMMER:识别锤头线(看涨信号)。CDLMORNINGSTAR:识别启明之星(底部反转,看涨信号)。CDLEVENINGSTAR:识别黄昏之星(顶部反转,看跌信号)。
- 交易执行:
- 买入条件:当识别出“锤头线”或“启明之星”形态时,且当前无持仓,执行买入。
- 卖出条件:当识别出“黄昏之星”形态时,且当前有持仓,执行清仓。
策略代码
# -*- coding: gbk -*-
import pandas as pd
import numpy as np
import talib
import time
def init(ContextInfo):
"""
策略初始化函数
"""
# 设置资金账号 (请替换为您自己的资金账号)
ContextInfo.accID = '6000000000'
ContextInfo.set_account(ContextInfo.accID)
# 设置股票池 (示例:平安银行,贵州茅台)
ContextInfo.stock_list = ['000001.SZ', '600519.SH']
ContextInfo.set_universe(ContextInfo.stock_list)
# 设置策略参数
ContextInfo.lookback = 20 # 获取历史K线数量,形态识别通常需要前几根K线
ContextInfo.position_ratio = 0.2 # 每只股票买入占总资金的比例
print("策略初始化完成,监控形态:锤头线、启明之星、黄昏之星")
def handlebar(ContextInfo):
"""
K线周期运行函数
"""
# 获取当前正在处理的K线索引
index = ContextInfo.barpos
# 获取当前时间戳
realtime = ContextInfo.get_bar_timetag(index)
# 遍历股票池
for stock in ContextInfo.stock_list:
# 1. 获取历史行情数据
# 注意:形态识别至少需要一定数量的历史数据,这里取 lookback 数量
# subscribe=True 确保实盘时数据订阅更新
data_map = ContextInfo.get_market_data_ex(
['open', 'high', 'low', 'close'],
[stock],
period=ContextInfo.period,
count=ContextInfo.lookback,
subscribe=True
)
if stock not in data_map:
continue
df = data_map[stock]
# 数据长度校验,防止数据不足导致报错
if len(df) < 10:
continue
# 2. 准备 TA-Lib 需要的数据格式 (numpy array, float类型)
# 注意:TA-Lib计算是基于序列的,我们需要传入完整的序列
open_prices = df['open'].values.astype(float)
high_prices = df['high'].values.astype(float)
low_prices = df['low'].values.astype(float)
close_prices = df['close'].values.astype(float)
# 3. 调用 TA-Lib 进行形态识别
# 结果说明:100 表示看涨形态,-100 表示看跌形态,0 表示无形态
# 识别锤头线 (Hammer) - 看涨
hammer = talib.CDLHAMMER(open_prices, high_prices, low_prices, close_prices)
# 识别启明之星 (Morning Star) - 看涨
morning_star = talib.CDLMORNINGSTAR(open_prices, high_prices, low_prices, close_prices)
# 识别黄昏之星 (Evening Star) - 看跌
evening_star = talib.CDLEVENINGSTAR(open_prices, high_prices, low_prices, close_prices)
# 获取最新一根K线的形态结果 (即当前 bar 的结果)
# 注意:在回测中,df的最后一行是当前bar;在实盘中,如果bar未走完,信号可能会闪烁
# 建议实盘使用上一根K线确认信号,或者在当前K线即将收盘时确认
current_hammer = hammer[-1]
current_morning = morning_star[-1]
current_evening = evening_star[-1]
# 4. 获取当前持仓信息
positions = get_trade_detail_data(ContextInfo.accID, 'stock', 'position')
holding_vol = 0
for pos in positions:
if pos.m_strInstrumentID + '.' + pos.m_strExchangeID == stock:
holding_vol = pos.m_nVolume
break
# 5. 交易逻辑判断
# --- 买入逻辑 ---
# 如果出现 锤头线(100) 或者 启明之星(100)
if (current_hammer == 100 or current_morning == 100):
if holding_vol == 0:
print(f"[{stock}] 发现看涨形态 (锤头线/启明之星) - 时间: {timetag_to_datetime(realtime, '%Y-%m-%d %H:%M:%S')}")
# 目标市值下单:调整仓位到总资产的 20%
order_target_percent(stock, ContextInfo.position_ratio, ContextInfo, ContextInfo.accID)
# 在图表上标记
ContextInfo.draw_text(True, low_prices[-1], '买入')
# --- 卖出逻辑 ---
# 如果出现 黄昏之星(-100)
elif (current_evening == -100):
if holding_vol > 0:
print(f"[{stock}] 发现看跌形态 (黄昏之星) - 时间: {timetag_to_datetime(realtime, '%Y-%m-%d %H:%M:%S')}")
# 清仓
order_target_percent(stock, 0, ContextInfo, ContextInfo.accID)
# 在图表上标记
ContextInfo.draw_text(True, high_prices[-1], '卖出')
代码关键点解析
import talib: QMT 自带了talib库,这是量化领域最标准的形态识别库,计算效率极高。- 数据转换:
talib的函数通常要求输入numpy.ndarray类型的浮点数数组。代码中使用了.values.astype(float)将 pandas Series 转换为符合要求的格式。 - 信号判断:
talib的形态函数返回值通常为整数序列。100: 表示识别出标准的看涨形态。-100: 表示识别出标准的看跌形态。0: 表示未识别出形态。
order_target_percent: 使用目标比例下单函数。这比指定股数下单更方便,它会自动计算需要买入多少股才能达到设定的仓位比例(例如 20%),或者卖出多少股才能清仓(设为 0)。
如何在 QMT 中运行
- 打开 QMT 客户端,进入【模型研究】或【策略编辑器】。
- 新建一个 Python 策略。
- 将上述代码完整复制粘贴进去。
- 修改
ContextInfo.accID为你实际的资金账号。 - 点击【编译】检查无误。
- 在左侧选择回测区间和周期(建议选择 日线,因为形态学在日线级别准确率相对较高),点击【运行】或【回测】。
常见问题 Q&A
Q: 为什么实盘时信号会闪烁(一会有一会没有)?
A: 在实盘中,handlebar 会在每个 Tick(分笔数据)到来时运行。如果当前 K 线还没有收盘(例如日线还在交易中),形态可能会随着最新价的变化而成立或消失。
- 解决方法:可以在代码中判断
ContextInfo.is_last_bar()并且只在 K 线即将结束的时间点(如 14:55)下单,或者逻辑改为判断hammer[-2](即以前一根已收盘的 K 线作为信号)。
Q: 可以识别其他形态吗?
A: 可以。talib 支持几十种形态,例如:
- 红三兵:
talib.CDL3WHITESOLDIERS - 三只乌鸦:
talib.CDL3BLACKCROWS - 十字星:
talib.CDLDOJI
只需将代码中的函数名替换即可。
Q: 为什么回测没有交易?
A:
- 请检查回测时间段内是否真的出现了该形态(形态出现频率并不高)。
- 请检查
ContextInfo.accID是否设置正确(回测模式下通常不需要真实账号,但代码逻辑中获取持仓依赖账号)。 - 请确保数据已经下载补充完整(在数据管理中下载日线数据)。