🚀 AI 一键生成 qmt 策略代码

QMT 基本面量化:利用流动比率与速动比率进行选股

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

问题描述

如何根据流动比率选股?

解决方案

根据流动比率(Current Ratio)选股是一种基于基本面分析的策略。流动比率衡量的是企业偿还短期债务的能力,计算公式为:流动资产 / 流动负债。通常认为,流动比率越高,企业的短期偿债能力越强。

在 QMT 中,我们可以利用 多因子数据接口 (ContextInfo.get_factor_data) 来获取该指标。

策略逻辑

  1. 股票池:设定为沪深300成分股(示例)。
  2. 选股指标Solvency_and_Capital_Structure.CurrentRatio(偿债能力和资本结构 - 流动比率)。
  3. 筛选条件:选取流动比率大于 2.0 的股票(数值可调整)。
  4. 交易逻辑
    • 每月第一个交易日进行调仓。
    • 卖出不在新名单中的持仓股。
    • 买入满足条件的股票,资金等权分配。

QMT 策略代码实现

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

def init(ContextInfo):
    # 1. 设置策略参数
    ContextInfo.target_ratio = 2.0  # 流动比率阈值
    ContextInfo.holding_num = 20    # 最大持仓数量(可选,防止资金过于分散)
    
    # 2. 设置股票池 (这里以沪深300为例)
    ContextInfo.index_code = '000300.SH'
    ContextInfo.set_universe(ContextInfo.get_sector(ContextInfo.index_code))
    
    # 3. 设置账号 (回测模式下会自动使用虚拟账号,实盘需替换为真实账号)
    ContextInfo.account_id = 'test_account' 
    ContextInfo.account_type = 'STOCK'
    ContextInfo.set_account(ContextInfo.account_id)
    
    # 4. 初始化全局变量
    ContextInfo.rebalance_period = 'monthly' # 调仓周期
    ContextInfo.last_rebalance_month = -1

def handlebar(ContextInfo):
    # 获取当前K线的时间
    bar_index = ContextInfo.barpos
    current_time = ContextInfo.get_bar_timetag(bar_index)
    current_date_str = timetag_to_datetime(current_time, '%Y%m%d')
    current_month = int(current_date_str[4:6])
    
    # -------------------------------------------------------
    # 调仓逻辑控制:每月第一个交易日调仓
    # -------------------------------------------------------
    if ContextInfo.last_rebalance_month == current_month:
        return # 本月已调仓,跳过
    
    # 更新调仓月份标记
    ContextInfo.last_rebalance_month = current_month
    print(f'=== 开始调仓: {current_date_str} ===')

    # -------------------------------------------------------
    # 1. 获取数据
    # -------------------------------------------------------
    stock_list = ContextInfo.get_universe()
    
    # 因子名称:偿债能力和资本结构 - 流动比率
    factor_name = 'Solvency_and_Capital_Structure.CurrentRatio'
    
    # 获取因子数据
    # 注意:get_factor_data 返回的数据结构取决于传入的股票和时间数量
    # 传入多只股票、单日时间,返回 DataFrame (index=股票代码, columns=因子名)
    factor_data = ContextInfo.get_factor_data(
        [factor_name], 
        stock_list, 
        current_date_str, 
        current_date_str
    )
    
    # 数据清洗:去除空值
    if factor_data.empty:
        print("未获取到因子数据")
        return
        
    # -------------------------------------------------------
    # 2. 选股逻辑
    # -------------------------------------------------------
    # 筛选流动比率大于阈值的股票
    # 注意:factor_data 是一个 DataFrame,列名为因子全名
    if factor_name not in factor_data.columns:
        print(f"数据中缺少因子列: {factor_name}")
        return

    # 过滤掉 NaN 值
    valid_data = factor_data.dropna(subset=[factor_name])
    
    # 筛选大于阈值的股票
    selected_df = valid_data[valid_data[factor_name] > ContextInfo.target_ratio]
    
    # (可选) 排序:例如按流动比率从大到小排序,取前N只
    selected_df = selected_df.sort_values(by=factor_name, ascending=False)
    target_stocks = selected_df.head(ContextInfo.holding_num).index.tolist()
    
    print(f"选出股票数量: {len(target_stocks)}")
    
    # -------------------------------------------------------
    # 3. 交易执行
    # -------------------------------------------------------
    # 获取当前持仓
    positions = get_trade_detail_data(ContextInfo.account_id, ContextInfo.account_type, 'POSITION')
    holding_stocks = [obj.m_strInstrumentID for obj in positions if obj.m_nVolume > 0]
    
    # 卖出不在目标列表中的股票
    for stock in holding_stocks:
        if stock not in target_stocks:
            order_target_value(stock, 0, ContextInfo, ContextInfo.account_id)
            print(f"卖出: {stock}")
            
    # 买入目标股票
    if len(target_stocks) > 0:
        # 简单的等权分配资金 (注意:这里使用 order_target_percent 需要在回测设置中设置初始资金)
        # 也可以使用 order_target_value 根据总资产计算
        weight = 1.0 / len(target_stocks)
        for stock in target_stocks:
            order_target_percent(stock, weight, ContextInfo, ContextInfo.account_id)
            print(f"买入/调仓: {stock}, 目标权重: {weight:.2%}")

代码关键点解析

  1. 数据接口 (get_factor_data):

    • 我们使用了 Solvency_and_Capital_Structure.CurrentRatio 这个字段。
    • 该接口返回的是 pandas.DataFrame,索引是股票代码,列名是因子名称。
    • 注意:因子数据通常有滞后性(基于财报发布日期),QMT 的因子库会自动处理这点,获取的是当时可获得的最新财报数据。
  2. 时间控制:

    • 基本面数据变化频率较低(季度更新),因此不需要每日频繁交易。
    • 代码中通过 current_monthlast_rebalance_month 的对比,实现了月度调仓
  3. 交易函数:

    • 使用了 order_target_percent(目标比例下单)。这在调仓时非常方便,它会自动计算需要买入或卖出的股数,将仓位调整到指定的权重(例如 5%)。
    • 注意:在实盘中使用 order_target_percent 时,请确保 ContextInfo 能正确获取到账户的总资产数据,否则建议改用 order_target_valueorder_lots
  4. 容错处理:

    • 增加了 .dropna() 处理,防止因为某些股票停牌或未发布财报导致数据为空(NaN)而报错。

常见问题 Q&A

Q: 如何查看还有哪些财务因子可以使用?
A: 在 QMT 的帮助文档或“数据管理”界面中,查看“多因子数据”部分。例如,速动比率是 Solvency_and_Capital_Structure.QuickRatio,资产负债率是 Solvency_and_Capital_Structure.DebtsAssetRatio

Q: 为什么回测时没有交易?
A:

  1. 请检查回测时间段内是否有财报数据(因子数据需要下载补充)。
  2. 检查 ContextInfo.index_code 设置的指数成分股在回测区间内是否存在。
  3. 检查 ContextInfo.target_ratio 阈值是否设置过高,导致选不出股票。

Q: 如何结合其他指标(如市盈率 PE)?
A: 可以在 get_factor_data 的第一个参数列表中添加字段,例如 ['Solvency_and_Capital_Structure.CurrentRatio', 'Valuation_and_Market_Cap.PE'],然后在获取到的 DataFrame 中增加额外的筛选逻辑即可(例如 df[(df['...CurrentRatio'] > 2) & (df['...PE'] < 20)])。