问题描述
解决方案
Altman Z-Score(阿特曼Z分模型)是一种通过财务比率来评估公司破产风险的经典模型。在聚宽(JoinQuant)平台上,我们可以通过 get_fundamentals 函数获取财务数据(资产负债表、利润表)和市值数据来计算该指标。
Altman Z-Score 公式
标准公式(适用于上市公司):
$$Z = 1.2X_1 + 1.4X_2 + 3.3X_3 + 0.6X_4 + 1.0X_5$$
其中:
- $X_1 = \text{营运资本} / \text{总资产} = (\text{流动资产} - \text{流动负债}) / \text{总资产}$
- $X_2 = \text{留存收益} / \text{总资产}$
- $X_3 = \text{息税前利润 (EBIT)} / \text{总资产}$
- $X_4 = \text{股权市场价值} / \text{总负债}$
- $X_5 = \text{销售收入} / \text{总资产}$
代码实现
以下是一个完整的策略代码示例,包含一个计算 Z-Score 的通用函数。该代码可以在聚宽的回测或研究环境中运行。
注意单位换算:聚宽的财务报表数据(如总资产)单位通常为元,而市值数据(valuation.market_cap)单位为亿元。在计算 $X_4$ 时必须统一单位。
# -*- coding: utf-8 -*-
from jqdata import *
import pandas as pd
def initialize(context):
# 设定基准
set_benchmark('000300.XSHG')
# 开启动态复权模式(真实价格)
set_option('use_real_price', True)
# 过滤掉order系列API产生的比error级别低的log
log.set_level('order', 'error')
# 每天运行一次计算
run_daily(calculate_and_print_z_score, 'open')
def calculate_and_print_z_score(context):
"""
计算并打印股票池的 Altman Z-Score
"""
# 示例:选取沪深300成分股的前10只作为演示
security_list = get_index_stocks('000300.XSHG')[:10]
# 获取计算所需的财务和市值数据
# 注意:为了避免未来函数,get_fundamentals 在回测中默认获取昨日及之前的数据
q = query(
valuation.code,
valuation.market_cap, # 市值 (亿元)
balance.total_assets, # 总资产 (元)
balance.total_liability, # 总负债 (元)
balance.total_current_assets, # 流动资产 (元)
balance.total_current_liability, # 流动负债 (元)
balance.retained_profit, # 留存收益 (元)
income.EBIT, # 息税前利润 (元)
income.operating_revenue # 营业收入 (元)
).filter(
valuation.code.in_(security_list)
)
df = get_fundamentals(q, date=context.previous_date)
if df is None or len(df) == 0:
log.info("未获取到财务数据")
return
# --- 开始计算 Z-Score ---
# 1. 计算中间变量 (处理除数为0的情况,虽然财务数据通常不为0,但需防范)
# 营运资本 = 流动资产 - 流动负债
working_capital = df['total_current_assets'] - df['total_current_liability']
# X1: 营运资本 / 总资产
df['X1'] = working_capital / df['total_assets']
# X2: 留存收益 / 总资产
df['X2'] = df['retained_profit'] / df['total_assets']
# X3: EBIT / 总资产
df['X3'] = df['EBIT'] / df['total_assets']
# X4: 股权市场价值 / 总负债
# 注意:market_cap 单位是亿元,需要乘以 10^8 转换为元
df['X4'] = (df['market_cap'] * 100000000) / df['total_liability']
# X5: 销售收入 / 总资产
df['X5'] = df['operating_revenue'] / df['total_assets']
# 2. 计算最终 Z-Score
# Z = 1.2*X1 + 1.4*X2 + 3.3*X3 + 0.6*X4 + 1.0*X5
df['Z_Score'] = 1.2 * df['X1'] + 1.4 * df['X2'] + 3.3 * df['X3'] + 0.6 * df['X4'] + 1.0 * df['X5']
# --- 结果展示与分析 ---
log.info("Altman Z-Score 计算结果 (前5只):")
for index, row in df.head(5).iterrows():
code = row['code']
z_score = row['Z_Score']
# 简单的风险评级判断
risk_level = ""
if z_score > 2.99:
risk_level = "安全区 (Safe)"
elif 1.81 < z_score <= 2.99:
risk_level = "灰色区域 (Grey Zone)"
else:
risk_level = "危险区 (Distress)"
log.info(f"股票: {code}, Z-Score: {z_score:.4f}, 评级: {risk_level}")
# 可以在此处添加交易逻辑,例如卖出 Z-Score < 1.81 的股票
# ...
结果解读
Altman Z-Score 的经典判别标准如下:
- Z > 2.99:安全区。公司财务状况良好,破产风险极低。
- 1.81 < Z ≤ 2.99:灰色区域。财务状况不稳定,需要密切关注。
- Z ≤ 1.81:危险区。公司面临较大的财务困境,破产可能性较高。
关键点说明
- 数据单位:代码中特别处理了
valuation.market_cap,将其从“亿元”转换为“元”,以匹配资产负债表中的数据单位。 - 数据获取:使用了
get_fundamentals获取基本面数据。在回测中,为了避免未来函数,通常使用context.previous_date或默认日期(即回测日期的前一天可见的数据)。 - 适用范围:该模型最初是针对制造业上市公司设计的。对于非制造业(如服务业)或新兴行业,参数权重可能需要调整(例如 Altman Z'' 模型),但上述代码使用的是最通用的原始版本。
Q&A
Q1: 为什么计算出的 Z-Score 有些非常高或非常低?
A1: 某些行业(如轻资产行业或科技股)可能因为总资产较低而导致 X3、X5 偏高,或者因为市值极高导致 X4 极大。Z-Score 最适合用于传统的重资产制造业评估。对于银行、券商等金融行业,由于其资产负债结构的特殊性,Z-Score 通常不适用。
Q2: 如何在研究环境(Notebook)中使用这段代码?
A2: 在研究环境中,只需将 get_fundamentals 中的 date 参数指定为具体的日期字符串(如 '2023-01-01'),并移除 context 相关的调用即可直接运行数据查询和计算逻辑。
Q3: 代码中的 EBIT 是如何获取的?
A3: 聚宽的 income 表中直接提供了 EBIT(息税前利润)字段。如果某些早期数据缺失该字段,可以用 利润总额 + 利息费用 来近似替代。