本章介绍 QMT 内置 Python 的模型扩展、自定义指标和图表绘制功能。

自定义指标

创建自定义指标

def init(ContextInfo): ContextInfo.set_universe(['000001.SZ']) # 注册自定义指标 ContextInfo.bindBindbar('MY_MA', 'my_ma') def my_ma(ContextInfo, index_info): """自定义均线指标""" closes = ContextInfo.get_history_data('close', 20) if closes and len(closes) >= 20: ma = sum(closes[-20:]) / 20 index_info.set_value(ma)

指标参数

def init(ContextInfo): # 注册带参数的指标 ContextInfo.bindBindbar('MY_MACD', 'my_macd', params={'short': 12, 'long': 26, 'signal': 9}) def my_macd(ContextInfo, index_info): short = index_info.get_param('short') long_period = index_info.get_param('long') signal = index_info.get_param('signal') closes = ContextInfo.get_history_data('close', long_period + signal) if not closes or len(closes) < long_period + signal: return # 计算 MACD ema_short = calc_ema(closes, short) ema_long = calc_ema(closes, long_period) dif = ema_short - ema_long index_info.set_value(dif) def calc_ema(data, period): """计算指数移动平均""" multiplier = 2 / (period + 1) ema = data[0] for price in data[1:]: ema = price * multiplier + ema * (1 - multiplier) return ema

模型扩展

使用内置 talib 库

import talib def handlebar(ContextInfo): if not ContextInfo.is_last_bar(): return closes = ContextInfo.get_history_data('close', 50) if not closes or len(closes) < 50: return closes_array = np.array(closes) # 计算技术指标 ma20 = talib.SMA(closes_array, timeperiod=20) rsi = talib.RSI(closes_array, timeperiod=14) macd, signal, hist = talib.MACD(closes_array) print(f'MA20: {ma20[-1]}, RSI: {rsi[-1]}')

可用的 talib 指标

指标 函数 说明
SMA talib.SMA 简单移动平均
EMA talib.EMA 指数移动平均
RSI talib.RSI 相对强弱指标
MACD talib.MACD MACD 指标
KDJ talib.STOCH 随机指标
BOLL talib.BBANDS 布林带
ATR talib.ATR 真实波幅

自定义计算函数

import numpy as np def calc_bollinger(closes, period=20, num_std=2): """计算布林带""" closes = np.array(closes) ma = np.mean(closes[-period:]) std = np.std(closes[-period:]) upper = ma + num_std * std lower = ma - num_std * std return upper, ma, lower def calc_kdj(highs, lows, closes, period=9): """计算 KDJ 指标""" highs = np.array(highs[-period:]) lows = np.array(lows[-period:]) closes = np.array(closes[-period:]) highest = np.max(highs) lowest = np.min(lows) rsv = (closes[-1] - lowest) / (highest - lowest) * 100 return rsv

板块管理

获取板块列表

# 获取所有板块 sectors = ContextInfo.get_sector_list() for s in sectors: print(s)

获取板块成分

# 获取板块成分股 stocks = ContextInfo.get_stock_list_in_sector('沪深300') print(f'成分股数量: {len(stocks)}')

自定义板块

# 创建自定义板块 ContextInfo.add_sector('MY_STOCKS', ['000001.SZ', '600000.SH']) # 获取自定义板块成分 stocks = ContextInfo.get_stock_list_in_sector('MY_STOCKS')

图表绘制

基本绘图

import matplotlib matplotlib.use('Agg') # 使用非交互式后端 import matplotlib.pyplot as plt def draw_chart(closes, title='价格走势'): """绘制价格走势图""" fig, ax = plt.subplots(figsize=(10, 5)) ax.plot(closes, label='收盘价') ax.set_title(title) ax.set_xlabel('日期') ax.set_ylabel('价格') ax.legend() ax.grid(True) # 保存图片 fig.savefig('chart.png', dpi=150, bbox_inches='tight') plt.close(fig) print('图表已保存到 chart.png')

在策略中使用绘图

def handlebar(ContextInfo): if not ContextInfo.is_last_bar(): return closes = ContextInfo.get_history_data('close', 60) if not closes or len(closes) < 60: return # 每 20 天画一次图 if ContextInfo.barpos % 20 == 0: draw_chart(closes, f'{ContextInfo.stockcode} 价格走势')

绘制 K 线图

from matplotlib.patches import Rectangle def draw_candlestick(opens, highs, lows, closes, title='K线图'): """绘制 K 线图""" fig, ax = plt.subplots(figsize=(12, 6)) for i in range(len(closes)): # 判断涨跌 color = 'red' if closes[i] >= opens[i] else 'green' # 画实体 rect = Rectangle( (i, min(opens[i], closes[i])), 0.6, abs(closes[i] - opens[i]), facecolor=color, edgecolor=color, ) ax.add_patch(rect) # 画影线 ax.plot([i + 0.3, i + 0.3], [lows[i], highs[i]], color=color, linewidth=0.5) ax.set_title(title) ax.set_xlabel('交易日') ax.set_ylabel('价格') fig.savefig('candlestick.png', dpi=150, bbox_inches='tight') plt.close(fig)

数据导出

导出 CSV

import csv def export_to_csv(ContextInfo): """导出策略数据到 CSV""" closes = ContextInfo.get_history_data('close', 20) with open('output.csv', 'w', newline='', encoding='utf-8') as f: writer = csv.writer(f) writer.writerow(['日期', '收盘价']) for i, close in enumerate(closes): writer.writerow([i, close]) print('数据已导出到 output.csv')
提示

QMT 内置的 matplotlib 使用 Agg 后端,无法实时显示图表,只能保存为图片文件。