问题描述
解决方案
在 QMT (Quantitative Trading Platform) 中,获取某个因子在所有股票中的排名,主要有两种方式:
- 使用 API 直接获取(针对引用数据): 如果是系统内置的引用因子或扩展数据,可以使用
get_factor_rank或ext_data_rank。 - 使用 Pandas 进行计算(推荐,更灵活): 获取所有股票的因子值,然后利用 Python 的
pandas库进行排序和排名。这是量化策略中最常用的方法,因为它适用于任何自定义计算的因子。
以下是详细的说明和代码实现。
方法一:使用 Pandas 计算排名(推荐)
这种方法最通用。逻辑是:获取股票池 -> 获取因子数据 -> 转换为 DataFrame -> 使用 rank() 函数排名。
策略代码示例
# -*- coding: gbk -*-
import pandas as pd
def init(ContextInfo):
# 1. 设置股票池,这里以沪深300为例,如果需要全市场,可使用 '沪深A股'
# 注意:全市场股票数量较多,获取数据和计算耗时会增加
ContextInfo.index_code = '000300.SH'
ContextInfo.stock_list = ContextInfo.get_stock_list_in_sector('沪深300')
print(f"股票池数量: {len(ContextInfo.stock_list)}")
def handlebar(ContextInfo):
# 仅在最后一根K线运行,避免回测时每个bar都打印大量日志
if not ContextInfo.is_last_bar():
return
# 2. 获取当前时间
timetag = ContextInfo.get_bar_timetag(ContextInfo.barpos)
date_str = timetag_to_datetime(timetag, '%Y%m%d')
# 3. 定义要获取的因子
# 这里以 "市盈率(PE)" 为例,因子库表名为 Valuation_and_Market_Cap
factor_name = 'Valuation_and_Market_Cap.PE'
field_list = [factor_name]
# 4. 获取因子数据
# 参数:字段列表, 股票列表, 开始时间, 结束时间
# 当股票列表为多个,时间为1个点时,返回 DataFrame (index=代码, columns=字段)
df_factor = ContextInfo.get_factor_data(field_list, ContextInfo.stock_list, date_str, date_str)
# 检查数据是否为空
if df_factor.empty:
print(f"{date_str} 无因子数据")
return
# 5. 数据清洗与排名
# 剔除空值 (NaN)
df_factor.dropna(inplace=True)
# 使用 pandas 的 rank 函数进行排名
# ascending=True 表示升序(数值越小排名越靠前),False 表示降序
# method='min' 表示如果有相同数值,取最小排名
df_factor['rank_asc'] = df_factor[factor_name].rank(ascending=True, method='min')
# 6. 打印结果
print(f"--- {date_str} 因子排名示例 (PE 最小的前10名) ---")
# 按排名排序并取前10
top_10 = df_factor.sort_values('rank_asc').head(10)
print(top_10)
# 如果你想获取某只特定股票的排名,可以直接索引
target_stock = '600000.SH'
if target_stock in df_factor.index:
rank = df_factor.loc[target_stock, 'rank_asc']
val = df_factor.loc[target_stock, factor_name]
print(f"股票 {target_stock} 的 PE 为 {val}, 排名为: {rank}")
方法二:使用 API 获取引用数据的排名
如果你使用的是通过“扩展数据”功能导入的数据,或者特定的引用因子,可以使用以下 API。这种方法通常用于获取单只股票的排名数值,若要获取全市场列表,仍需循环调用(效率较低)。
1. get_factor_rank (获取引用因子排名)
用于获取引用的因子数据在所有品种中的排名。
- 用法:
get_factor_rank(factorname, stockcode, deviation, ContextInfo) - 参数:
factorname: 因子名stockcode: 股票代码deviation: K线偏移(0为当前)
2. ext_data_rank (获取扩展数据排名)
用于获取用户自定义并导入的“扩展数据”的排名。
- 用法:
ext_data_rank(extdataname, stockcode, deviation, ContextInfo) - 参数:
extdataname: 扩展数据名称(在QMT客户端数据管理中定义的名称)stockcode: 股票代码
代码片段示例:
# -*- coding: gbk -*-
def handlebar(ContextInfo):
# 获取浦发银行在 'my_custom_factor' 这个扩展数据中的排名
# 注意:前提是你已经在QMT客户端建立了名为 'my_custom_factor' 的扩展数据
rank = ext_data_rank('my_custom_factor', '600000.SH', 0, ContextInfo)
print(f"600000.SH 排名: {rank}")
总结
| 需求场景 | 推荐方法 | 优势 |
|---|---|---|
| 策略开发、选股 | 方法一 (Pandas) | 速度快(向量化计算),灵活性高,可同时处理排序、筛选、分层。 |
| 简单指标引用 | 方法二 (API) | 代码简短,直接获取单只股票结果。 |
Q&A
Q: get_factor_data 返回的数据格式是什么?
A: 当传入多个股票代码和单个时间点时,它返回一个 pandas.DataFrame,索引(Index)是股票代码,列(Columns)是因子名称。这非常适合直接进行 rank 操作。
Q: 如何处理因子数据中的 NaN (空值)?
A: 在排名之前,务必使用 df.dropna() 去除空值,或者使用 df.fillna() 填充默认值。否则 rank 函数可能会对 NaN 进行不符合预期的排序(默认 NaN 不参与排名或排在最后)。
Q: 如何实现行业内排名?
A: 可以先获取股票的行业分类数据,将其合并到 DataFrame 中,然后使用 df.groupby('行业')['因子'].rank() 进行分组排名。