🚀 AI 一键生成 qmt 策略代码

QMT 绘图教程:如何在 K 线图上用箭头标记买入和卖出点?

qmt | 发布时间: 2025/12/16
以下内容由 EasyQuant 生成。

问题描述

如何在策略回测的K线图上,用向上的箭头表示买入,向下的箭头表示卖出?

解决方案

在 QMT 的 Python 策略回测中,要在 K 线图上绘制买卖信号(箭头),最常用且灵活的方法是使用 ContextInfo.draw_text() 函数。

虽然 QMT 提供了 draw_icon 函数,但根据文档其支持的图标类型有限(主要是矩形和椭圆)。因此,使用 Unicode 箭头字符(如 '↑', '↓') 或文字(如 'B', 'S')配合 draw_text 是实现该需求的最佳方式。

实现原理

  1. 买入信号:在触发买入条件的 K 线 最低价(Low) 下方绘制向上的箭头 '↑''Buy'
  2. 卖出信号:在触发卖出条件的 K 线 最高价(High) 上方绘制向下的箭头 '↓''Sell'

完整策略代码示例

以下是一个完整的双均线策略示例。它不仅包含交易逻辑,还包含了在图表上绘制买卖箭头的代码。

# -*- coding: gbk -*-
import pandas as pd

def init(ContextInfo):
    # 设置股票池,这里以平安银行为例
    ContextInfo.set_universe(['000001.SZ'])
    # 设置策略运行账号(回测模式下使用虚拟账号)
    ContextInfo.set_account('account_id')
    # 设置基准
    ContextInfo.benchmark = '000300.SH'

def handlebar(ContextInfo):
    # 获取当前 K 线的位置索引
    index = ContextInfo.barpos
    # 获取当前主图的股票代码
    stock_code = ContextInfo.get_universe()[0]
    
    # 获取历史行情数据(取过去 30 根 K 线用于计算均线)
    # 注意:为了计算 MA10,我们需要至少 10 根数据,这里取 30 根以防万一
    data = ContextInfo.get_market_data(
        ['close', 'high', 'low'], 
        stock_code=[stock_code], 
        count=30, 
        period=ContextInfo.period, 
        dividend_type='front'
    )
    
    # 如果数据不足,直接返回
    if data.empty or len(data) < 20:
        return

    # 提取收盘价、最高价、最低价序列
    close_list = data['close']
    high_list = data['high']
    low_list = data['low']
    
    # 获取当前 K 线的价格数据
    current_close = close_list.iloc[-1]
    current_high = high_list.iloc[-1]
    current_low = low_list.iloc[-1]
    
    # 计算 5 日均线和 10 日均线
    ma5 = close_list.rolling(window=5).mean()
    ma10 = close_list.rolling(window=10).mean()
    
    # 获取当前和上一根 K 线的均线值
    ma5_curr = ma5.iloc[-1]
    ma5_prev = ma5.iloc[-2]
    ma10_curr = ma10.iloc[-1]
    ma10_prev = ma10.iloc[-2]
    
    # --- 交易信号逻辑 ---
    
    # 金叉:5日线上穿10日线
    buy_condition = ma5_prev < ma10_prev and ma5_curr > ma10_curr
    
    # 死叉:5日线下穿10日线
    sell_condition = ma5_prev > ma10_prev and ma5_curr < ma10_curr
    
    # --- 绘图逻辑 (核心部分) ---
    
    # 1. 绘制买入箭头 (↑)
    # condition: 触发条件
    # position: 绘制位置,设在最低价下方一点点 (例如最低价的 99%),避免遮挡 K 线
    # text: 显示的文本,这里使用 Unicode 箭头
    ContextInfo.draw_text(buy_condition, current_low * 0.99, '↑')
    
    # 2. 绘制卖出箭头 (↓)
    # position: 绘制位置,设在最高价上方一点点 (例如最高价的 101%)
    ContextInfo.draw_text(sell_condition, current_high * 1.01, '↓')
    
    # --- 执行交易 ---
    
    # 如果不是最后一根 K 线(即在历史回测阶段),执行下单
    # 注意:ContextInfo.is_last_bar() 在回测的历史 K 线上返回 False,在实盘最新 K 线返回 True
    # 这里为了演示回测效果,我们在所有 K 线上都尝试触发
    
    if buy_condition:
        # 买入 100 股
        order_shares(stock_code, 100, 'fix', current_close, ContextInfo, ContextInfo.get_account(0))
        # 可以在日志中打印
        print(f"{ContextInfo.get_bar_timetag(index)}: 买入信号 - 价格 {current_close}")
        
    if sell_condition:
        # 卖出 100 股
        order_shares(stock_code, -100, 'fix', current_close, ContextInfo, ContextInfo.get_account(0))
        print(f"{ContextInfo.get_bar_timetag(index)}: 卖出信号 - 价格 {current_close}")

代码详解

  1. ContextInfo.draw_text(condition, position, text):

    • condition: 布尔值(True/False)。只有当 buy_conditionsell_conditionTrue 时,文字才会被绘制出来。
    • position: 这是一个价格数值(Y轴坐标)。
      • 对于买入,我们通常设置在 current_low * 0.99(最低价下方),这样箭头看起来是托着 K 线的。
      • 对于卖出,我们通常设置在 current_high * 1.01(最高价上方),这样箭头看起来是压着 K 线的。
    • text: 这里使用了 '↑''↓'。你也可以将其替换为 'B' (Buy) 和 'S' (Sell),或者中文 '买''卖'
  2. 数据获取: 使用了 get_market_data 获取 Pandas DataFrame 格式的数据,方便直接计算均线。

  3. 运行效果: 编译并运行回测后,在 K 线主图上,你会看到金叉位置下方有向上的箭头,死叉位置上方有向下的箭头。

Q&A

Q: 可以改变箭头的颜色吗?
A: ContextInfo.draw_text 函数本身在 API 文档中没有提供直接的颜色参数。它通常跟随系统默认的文本颜色(通常是显眼的颜色)。如果需要绘制带颜色的线条或柱状图,可以使用 ContextInfo.paint,但 paint 无法绘制箭头形状。

Q: 为什么箭头有时候会挡住 K 线?
A: 这是因为 position 参数设置得离 K 线太近。你可以调整乘数,例如将 0.99 改为 0.98,或者将 1.01 改为 1.02,以此增加箭头与 K 线之间的距离。

Q: 这个绘图函数在实盘中有效吗?
A: 有效。在实盘运行模式下,当新的 K 线生成并触发信号时,图表上也会实时画出相应的标记。