问题描述
解决方案
在聚宽(JoinQuant)平台上,计算股票的流动性因子主要有两种方式:一种是直接获取聚宽因子库中已经计算好的风格因子(Liquidity),另一种是使用基础数据(如换手率、成交量)进行手动计算。
以下是详细的说明和代码实现。
1. 聚宽风格因子定义(Liquidity)
根据聚宽风险模型因子列表,标准的**流动性因子(Liquidity)**并非简单的换手率,而是由不同期限的换手率加权组合而成,具体计算公式如下:
$$ \text{Liquidity} = 0.35 \cdot \text{ST_M} + 0.35 \cdot \text{AST_Q} + 0.3 \cdot \text{AST_A} $$
其中:
- ST_M (share_turnover_monthly): 月换手率(过去21日换手率之和的自然对数)。
- AST_Q (average_share_turnover_quarterly): 季度平均月换手率(过去3个月平均换手率的自然对数)。
- AST_A (average_share_turnover_annual): 年度平均月换手率(过去12个月平均换手率的自然对数)。
2. 获取方式
方法一:直接调用因子库(推荐)
使用 jqfactor.get_factor_values 直接获取计算好的 liquidity 因子值。这是最方便、最标准的方法。
方法二:使用财务数据查询换手率
使用 get_fundamentals 查询 valuation.turnover_ratio(换手率),这是最基础的流动性指标。
方法三:手动计算(Amihud非流动性因子等)
利用 get_price 获取成交量和价格,自行编写公式计算(如 Amihud 指标 = |收益率| / 成交金额)。
3. 策略代码示例
以下代码展示了如何在策略中通过方法一(获取风格因子)和方法二(查询换手率)来获取流动性数据,并演示了如何根据流动性因子对股票进行排序。
# -*- coding: utf-8 -*-
from jqdata import *
from jqfactor import get_factor_values
def initialize(context):
# 设定沪深300作为基准
set_benchmark('000300.XSHG')
# 开启动态复权模式(真实价格)
set_option('use_real_price', True)
# 过滤掉order系列API产生的比error级别低的log
log.set_level('order', 'error')
# 每天开盘前运行
run_daily(before_market_open, time='09:00')
def before_market_open(context):
# 设定要查询的股票池,这里以沪深300为例
stock_list = get_index_stocks('000300.XSHG')
# 获取当前日期
date = context.previous_date
# --- 方法一:直接获取聚宽风格因子库中的流动性因子 (Liquidity) ---
# 注意:因子库数据通常在收盘后更新,所以使用 previous_date
factor_data = get_factor_values(
securities=stock_list,
factors=['liquidity'],
end_date=date,
count=1
)
# 获取流动性因子值 Series (索引为股票代码,值为因子值)
# liquidity因子值越大,代表流动性越好(基于换手率计算)
liquidity_series = factor_data['liquidity'].iloc[0]
# 对因子值进行排序(从大到小),取流动性最好的前5只
top_liquidity_stocks = liquidity_series.sort_values(ascending=False).head(5).index.tolist()
print("【方法一】基于聚宽风格因子(Liquidity)流动性最高的前5只股票:")
print(top_liquidity_stocks)
# --- 方法二:使用财务数据查询换手率 (Turnover Ratio) ---
# turnover_ratio: 换手率(%)
q = query(
valuation.code,
valuation.turnover_ratio
).filter(
valuation.code.in_(stock_list)
).order_by(
valuation.turnover_ratio.desc() # 按换手率降序排列
)
df_fundamentals = get_fundamentals(q, date=date)
# 取换手率最高的前5只
if not df_fundamentals.empty:
top_turnover_stocks = df_fundamentals['code'].head(5).tolist()
print("【方法二】基于换手率(Turnover Ratio)流动性最高的前5只股票:")
print(top_turnover_stocks)
# --- 方法三:手动计算 Amihud 非流动性因子 (ILLIQ) ---
# 公式:ILLIQ = mean( |Rt| / (Price_t * Volume_t) )
# 这里计算过去20天的平均值
# 注意:Amihud值越大,表示流动性越差(非流动性越高)
# 获取过去20天的价格和成交额数据
prices = get_price(stock_list, end_date=date, frequency='daily', fields=['close', 'money'], count=21, panel=False)
if not prices.empty:
# 计算每日收益率的绝对值
# pct_change计算收益率,abs取绝对值
# 注意:这里需要处理多只股票的数据,pandas操作需谨慎
# 为了方便计算,我们将数据转换为pivot table
close_df = prices.pivot(index='time', columns='code', values='close')
money_df = prices.pivot(index='time', columns='code', values='money')
# 计算收益率
ret_df = close_df.pct_change().abs()
# 计算每日 ILLIQ = |Ret| / Money
# 注意:money单位是元,数值很大,结果会很小,通常可以乘以 1e6 或 1e9 方便观察
daily_illiq = (ret_df / money_df) * 1e9
# 计算过去20天的均值 (去掉第一天因为pct_change产生的NaN)
avg_illiq = daily_illiq.iloc[1:].mean()
# 排序:Amihud值越小,流动性越好。所以我们按升序排列取前5
top_amihud_stocks = avg_illiq.sort_values(ascending=True).head(5).index.tolist()
print("【方法三】基于Amihud非流动性因子(ILLIQ)流动性最高的前5只股票:")
print(top_amihud_stocks)
def handle_data(context, data):
pass
总结
- Liquidity (风格因子):综合了月、季、年维度的换手率,经过了对数处理,是量化模型中常用的标准因子。使用
get_factor_values获取。 - Turnover Ratio (换手率):最直观的流动性指标,反映当天的交易活跃度。使用
get_fundamentals获取。 - Amihud (非流动性因子):衡量单位成交额带来的价格冲击,常用于学术研究和特定策略。需要通过
get_price获取原始行情数据自行计算。
Q&A
Q: 为什么使用 get_factor_values 获取的 liquidity 因子值有正有负?
A: 聚宽提供的风格因子通常经过了标准化(z-score)处理。0 表示市场平均水平,正值表示流动性高于平均水平,负值表示低于平均水平。
Q: valuation.turnover_ratio 的单位是什么?
A: 单位是百分比(%)。例如获取到的值为 1.5,代表换手率为 1.5%。
Q: 如何在回测中避免未来函数?
A: 在 get_fundamentals 或 get_factor_values 中,查询日期应使用 context.previous_date(前一个交易日)或者在 before_market_open 中查询。如果在盘中(handle_data)查询当日的换手率或因子,可能会用到收盘后才确定的数据(如全天成交量),从而导致未来函数。上述代码示例中使用了 context.previous_date 来确保数据安全。