🚀 AI 一键生成 qmt 策略代码

QMT 量化实战:PSY 心理线指标策略 Python 源码

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

问题描述

如何用 Python 实现 PSY 指标策略?

解决方案

这是一个关于量化策略设计与实现的问题。

PSY (心理线) 指标策略概述

PSY (Psychological Line) 是一种基于投资者心理预期的市场情绪指标。它通过计算一段时间内股价上涨天数占总天数的比例,来判断市场的超买或超卖状态。

计算公式:
$$PSY(N) = \frac{N日内上涨天数}{N} \times 100$$
通常 $N$ 取值为 12。

策略逻辑:

  1. 超卖(买入信号): 当 PSY 值低于某个下限(如 25),表示市场情绪过度悲观,可能出现反弹,执行买入。
  2. 超买(卖出信号): 当 PSY 值高于某个上限(如 75),表示市场情绪过度乐观,可能出现回调,执行卖出。

QMT Python 策略代码实现

以下代码展示了如何在 QMT 平台实现该策略。代码包含了数据获取、指标计算、交易信号判断及下单操作。

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

def init(ContextInfo):
    """
    初始化函数,设定策略参数和股票池
    """
    # 策略参数设置
    ContextInfo.N = 12           # PSY计算周期
    ContextInfo.buy_threshold = 25   # 买入阈值
    ContextInfo.sell_threshold = 75  # 卖出阈值
    ContextInfo.stock_code = '600000.SH' # 示例标的:浦发银行
    ContextInfo.account_id = '6000000000' # 请替换为您的真实资金账号
    
    # 设置股票池
    ContextInfo.set_universe([ContextInfo.stock_code])
    
    # 设置交易账号
    ContextInfo.set_account(ContextInfo.account_id)
    
    print(f"策略初始化完成,标的:{ContextInfo.stock_code},PSY周期:{ContextInfo.N}")

def handlebar(ContextInfo):
    """
    K线周期运行函数,每根K线执行一次
    """
    # 获取当前K线索引
    index = ContextInfo.barpos
    
    # 如果历史数据不足以计算PSY(需要N+1根K线来计算N次涨跌),则直接返回
    if index < ContextInfo.N:
        return

    # 获取当前K线的时间戳,用于截取数据
    timetag = ContextInfo.get_bar_timetag(index)
    # 将时间戳转换为字符串格式 'YYYYMMDDHHMMSS'
    end_time_str = timetag_to_datetime(timetag, '%Y%m%d%H%M%S')
    
    # 获取历史行情数据
    # 我们需要 N+1 个数据点来计算 N 个交易日的涨跌情况
    # count = N + 1
    data_df = ContextInfo.get_market_data_ex(
        fields=['close'], 
        stock_code=[ContextInfo.stock_code], 
        period='1d', 
        end_time=end_time_str, 
        count=ContextInfo.N + 1,
        dividend_type='front' # 前复权
    )
    
    # 检查数据是否获取成功
    if ContextInfo.stock_code not in data_df or data_df[ContextInfo.stock_code].empty:
        return
        
    # 提取收盘价序列
    close_series = data_df[ContextInfo.stock_code]['close']
    
    # 再次确认数据长度是否足够
    if len(close_series) < ContextInfo.N + 1:
        return

    # --- PSY 指标计算逻辑 ---
    # 计算每日涨跌:今日收盘 - 昨日收盘
    diff = close_series.diff()
    
    # 取最近 N 天的涨跌数据(去掉第一个NaN值)
    # diff序列的最后 N 个值即为我们要统计的区间
    recent_diff = diff.iloc[-ContextInfo.N:]
    
    # 统计上涨天数 (diff > 0)
    up_days = recent_diff[recent_diff > 0].count()
    
    # 计算 PSY 值
    psy_value = (up_days / ContextInfo.N) * 100
    
    # 打印调试信息(可选)
    # print(f"时间: {end_time_str}, PSY: {psy_value:.2f}, 收盘价: {close_series.iloc[-1]}")

    # --- 交易逻辑 ---
    
    # 获取当前持仓
    positions = get_trade_detail_data(ContextInfo.account_id, 'stock', 'position')
    current_vol = 0
    for pos in positions:
        if pos.m_strInstrumentID + '.' + pos.m_strExchangeID == ContextInfo.stock_code:
            current_vol = pos.m_nVolume
            break

    # 1. 买入信号:PSY < 超卖阈值 且 当前无持仓
    if psy_value < ContextInfo.buy_threshold and current_vol == 0:
        print(f"触发买入信号: PSY={psy_value:.2f} < {ContextInfo.buy_threshold}")
        # 全仓买入示例(实际请根据资金管理调整)
        # 这里简单演示买入 1000 股
        passorder(23, 1101, ContextInfo.account_id, ContextInfo.stock_code, 5, -1, 1000, ContextInfo)
    
    # 2. 卖出信号:PSY > 超买阈值 且 有持仓
    elif psy_value > ContextInfo.sell_threshold and current_vol > 0:
        print(f"触发卖出信号: PSY={psy_value:.2f} > {ContextInfo.sell_threshold}")
        # 卖出所有持仓
        passorder(24, 1101, ContextInfo.account_id, ContextInfo.stock_code, 5, -1, current_vol, ContextInfo)

代码关键点解析

  1. 数据获取 (get_market_data_ex):

    • 我们使用 count=ContextInfo.N + 1。这是因为计算 $N$ 日的涨跌需要 $N+1$ 个收盘价(第1天的涨跌需要第0天的数据作为参照)。
    • end_time 参数至关重要,它确保我们在回测时获取的是“当时”的数据,而不是未来的数据,避免未来函数。
  2. 指标计算:

    • 利用 Pandas 的 .diff() 方法快速计算价格差。
    • 利用布尔索引 recent_diff > 0.count() 快速统计上涨天数。
  3. 交易执行 (passorder):

    • 23 代表买入,24 代表卖出。
    • 1101 代表单股、单账号、普通、股/手方式下单。
    • 5 代表使用最新价(市价)下单,保证成交概率(回测中通常以收盘价成交)。
  4. 持仓检查:

    • 使用 get_trade_detail_data 获取当前持仓,避免重复买入或卖空(A股不支持直接卖空)。

如何运行

  1. 打开 QMT 客户端,进入【模型研究】或【策略编辑器】。
  2. 新建一个 Python 策略,将上述代码粘贴进去。
  3. 修改 ContextInfo.account_id 为你的模拟或实盘账号。
  4. 在右侧设置回测参数(如资金、费率等),选择回测周期(建议日线)。
  5. 点击【运行】或【回测】。

Q&A

Q: 为什么 PSY 指标有时候会钝化(长期停留在高位或低位)?
A: PSY 只是统计上涨概率,不考虑上涨幅度。如果股价连续微涨,PSY 会很高,但股价可能并没有暴涨。通常建议结合 VR(容量比率)或 ARBR 指标一起使用。

Q: 如何修改为多只股票轮动?
A: 可以在 init 中将 stock_code 设为一个列表,然后在 handlebar 中使用 for 循环遍历这个列表,分别获取数据和计算指标。

Q: get_market_data_ex 返回的数据结构是什么?
A: 当请求单只股票时,它返回一个字典,Key 是股票代码,Value 是一个 Pandas DataFrame。DataFrame 的 Index 是时间字符串,Columns 是你请求的字段(如 'close')。