🚀 AI 一键生成 qmt 策略代码

如何在 QMT 平台实现风险平价 (Risk Parity) 投资组合策略?

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

问题描述

请帮我用 Python 实现一个包含股票和债券的风险平价投资组合策略。

解决方案

这是一个基于 QMT 平台的股票债券风险平价(Risk Parity)策略实现。

策略逻辑说明

  1. 核心思想:风险平价策略的核心不是让资产金额相等,而是让资产对组合的风险贡献相等。通常波动率高的资产(如股票)配置权重低,波动率低的资产(如债券)配置权重高。
  2. 标的选择
    • 股票端:使用 510300.SH (沪深300ETF) 代表权益类资产。
    • 债券端:使用 511010.SH (国债ETF) 代表债券类资产。
  3. 计算方法
    • 获取过去 N 天(如 60 天)的历史收盘价。
    • 计算日收益率序列的标准差(即波动率)。
    • 权重计算公式:$w_i = \frac{1/\sigma_i}{\sum (1/\sigma_j)}$。
  4. 调仓机制:按月调仓(每月第一个交易日进行再平衡)。

QMT 策略代码

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

def init(ContextInfo):
    """
    初始化函数,设定策略参数和标的
    """
    # 1. 设定标的:股票ETF(沪深300) 和 债券ETF(国债ETF)
    # 注意:实盘请确保这两个标的在您的交易权限内,且有足够的流动性
    ContextInfo.stock_code = '510300.SH' 
    ContextInfo.bond_code = '511010.SH'
    ContextInfo.assets = [ContextInfo.stock_code, ContextInfo.bond_code]
    
    # 2. 设定资金账号 (请修改为您自己的资金账号)
    ContextInfo.account_id = 'YOUR_ACCOUNT_ID' 
    ContextInfo.set_account(ContextInfo.account_id)
    
    # 3. 策略参数
    ContextInfo.lookback_window = 60  # 回看窗口,用于计算波动率 (天)
    ContextInfo.rebalance_period = 'monthly' # 调仓周期
    
    # 4. 记录上一次调仓的月份,用于判断是否跨月
    ContextInfo.last_month = -1
    
    print("风险平价策略初始化完成")

def handlebar(ContextInfo):
    """
    K线周期运行函数
    """
    # 获取当前K线的时间
    bar_index = ContextInfo.barpos
    current_timetag = ContextInfo.get_bar_timetag(bar_index)
    # 将时间戳转换为 datetime 对象以便提取月份
    current_date_str = timetag_to_datetime(current_timetag, '%Y%m%d')
    current_date = datetime.datetime.strptime(current_date_str, '%Y%m%d')
    current_month = current_date.month

    # 判断是否需要调仓:
    # 如果是回测的第一根K线,或者当前月份与上一次记录的月份不同,则触发调仓
    is_rebalance_time = False
    if ContextInfo.last_month == -1:
        is_rebalance_time = True
    elif current_month != ContextInfo.last_month:
        is_rebalance_time = True
    
    # 更新月份记录
    ContextInfo.last_month = current_month

    # 执行调仓逻辑
    if is_rebalance_time:
        print(f"日期: {current_date_str}, 触发调仓...")
        rebalance(ContextInfo)

def rebalance(ContextInfo):
    """
    计算风险平价权重并执行调仓
    """
    # 1. 获取历史数据
    # 使用 get_market_data_ex 获取多只股票的历史数据
    # count 设置为 lookback_window + 1 是为了计算收益率时不会少数据
    market_data = ContextInfo.get_market_data_ex(
        ['close'], 
        ContextInfo.assets, 
        period='1d', 
        count=ContextInfo.lookback_window + 1,
        dividend_type='front', # 前复权
        subscribe=True
    )
    
    volatilities = {}
    
    # 2. 计算波动率
    for code in ContextInfo.assets:
        df = market_data.get(code)
        
        # 数据校验
        if df is None or df.empty or len(df) < 20:
            print(f"警告: {code} 数据不足,跳过本次调仓")
            return

        # 计算日收益率
        returns = df['close'].pct_change().dropna()
        
        # 计算标准差 (波动率)
        vol = returns.std()
        
        # 防止波动率为0导致除零错误
        if vol == 0:
            vol = 0.0001
            
        volatilities[code] = vol
        print(f"标的: {code}, 历史波动率: {vol:.6f}")

    # 3. 计算风险平价权重
    # 倒数波动率
    inv_vols = {k: 1.0 / v for k, v in volatilities.items()}
    sum_inv_vol = sum(inv_vols.values())
    
    # 归一化权重
    target_weights = {k: v / sum_inv_vol for k, v in inv_vols.items()}
    
    print("目标权重计算结果:", target_weights)

    # 4. 执行交易
    for code, weight in target_weights.items():
        # 使用 order_target_percent 自动调整仓位到目标比例
        # 注意:实盘中 order_target_percent 需要配合正确的账号类型和资产查询才能精确运作
        # 回测模式下该函数非常准确
        order_target_percent(code, weight, ContextInfo, ContextInfo.account_id)
        print(f"下单: 调整 {code} 仓位至 {weight*100:.2f}%")

代码关键点解析

  1. # -*- coding: gbk -*-:

    • QMT 的 Python 编辑器默认编码通常是 GBK,加上这行代码可以防止中文注释乱码或运行报错。
  2. get_market_data_ex:

    • 这是 QMT 获取历史行情推荐使用的接口。
    • dividend_type='front':计算策略收益率和波动率时,必须使用前复权数据,否则分红派息造成的缺口会被误判为大幅波动。
  3. 波动率倒数加权:

    • 代码中 inv_vols = {k: 1.0 / v ...} 实现了风险平价的核心逻辑。如果股票波动率是债券的 3 倍,那么债券的配置权重将大约是股票的 3 倍,从而使得两者对组合的风险贡献接近。
  4. order_target_percent:

    • 这是一个非常方便的交易函数,它会自动计算当前持仓与目标持仓的差额,并进行买入或卖出操作。例如,如果目标权重是 20%,当前持仓是 0%,它会买入 20%;如果当前是 30%,它会卖出 10%。

使用建议

  1. 回测设置:

    • 在 QMT 界面点击“模型回测”。
    • 主图指标:随便选一个,例如 510300.SH
    • 周期:选择 日线 (1d)
    • 初始资金:建议设置 100万 以上,因为债券ETF价格通常较高或波动小,资金太少可能无法精确分配仓位。
  2. 实盘注意:

    • 请务必将 ContextInfo.account_id 修改为您真实的资金账号。
    • 实盘运行时,建议将 handlebar 中的打印语句保留,以便在日志中监控调仓行为。
    • ETF 交易通常有最小单位(100股/份),order_target_percent 会自动向下取整到整手。

Q&A

Q: 为什么选择 ETF 而不是具体的股票和国债?
A: ETF(交易型开放式指数基金)具有流动性好、费率低、自动分散个股风险的特点,非常适合作为资产配置策略的底层工具。

Q: 如何调整调仓频率?
A: 代码中通过 current_month != ContextInfo.last_month 实现了月度调仓。如果需要周度调仓,可以利用 ContextInfo.get_bar_timetag 获取时间后,计算周数变化。

Q: 波动率计算窗口(Lookback Window)设为多少合适?
A: 代码中默认为 60 天(约一个季度)。窗口越短,对近期市场变化越敏感,但换手率会变高;窗口越长,权重变化越平滑。通常 60-120 天是比较常用的参数。