ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Python量化实战:手把手计算A股与港股市场温度

Python量化实战:手把手计算A股与港股市场温度 近期行情波动加大身边不少朋友又开始频繁讨论“市场温度”这个词。有人把它理解为指数点位高低有人把它等同于成交量大小也有人直接看估值分位数。但真到落地分析时往往会发现指标很多、口径不一、数据难拿最后只能凭感觉拍脑袋。这篇文章不讨论具体个股也不做任何涨跌预测而是聚焦一个更基础的问题——如何用公开数据量化描述 A 股和港股市场的温度。我会从指标定义、数据获取、Python 计算、可视化到常见坑点完整拆解一套可复用的分析流程。无论你手里有没有量化经验只要能运行 Python 脚本都能跟着一步步做出来。1. 市场温度到底是什么1.1 从体感温度到市场温度天气里的“体感温度”是温度、湿度、风速共同作用的结果单纯的天气预报温度并不等于你身体感受到的冷暖。市场温度也是同样的道理单看指数涨跌并不能准确反映市场是冷是热。所谓市场温度本质上是对“市场情绪状态”和“估值性价比”的综合度量。它的用途不是预测明天的涨跌而是帮助投资者判断当前所处的环境市场处于高估值、高情绪状态时历史上往往对应较高的回调风险市场处于低估、冷清状态时往往对应长期配置价值的提升。当然所有历史统计规律都存在失效的可能市场温度只是一个参考工具不是交易信号。1.2 市场温度的组成维度一个完整的市场温度体系通常由三个维度构成维度代表指标说明估值维度市盈率、市净率、股息率判断“贵不贵”情绪维度成交额、换手率判断“热不热”资金维度两融余额、北向资金、南向资金判断“钱在不在进场”A 股和港股的规则不同同一个维度需要选择不一样的数据口径。比如港股的成交量容易受少数大市值股票影响单纯看成交额还不够需要结合换手率和卖空数据来综合判断。1.3 为什么需要独立计算很多财经 APP 上直接提供“估值温度计”之类的数据但这类产品往往口径不透明指数取样范围是什么历史区间从哪一年开始极值如何剔除这些都会对结果产生明显影响。自己动手计算的好处在于口径完全可控可以按自己的需求调整历史区间可以复现和回测而不是只看一个神秘分数便于把指标接入自己的投研流程。所以本文采用“公开数据中心 Python 计算”的路线实现一套可解释、可审计的市场温度计算流程。2. 环境准备与数据源选型2.1 软件环境本文的所有代码基于 Python 3.8使用 Jupyter Notebook 或 VS Code 均可。需要安装以下库pip install pandas numpy matplotlib requests openpyxl如果安装速度较慢可以指定国内镜像源pip install pandas numpy matplotlib requests openpyxl -i https://pypi.tuna.tsinghua.edu.cn/simple2.2 数据源选型说明计算市场温度第一步是拿到可靠的历史行情数据。常见的数据来源有数据源特点适合场景akshare免费、接口丰富但接口变动较频繁个人研究和学习baostock免费、稳定覆盖指数和个股日线个人量化研究Tushare Pro数据质量高部分接口需要积分有一定数据需求的开发者Wind、Choice专业终端费用高机构、专业投研考虑到教程的可复现性本文以baostock 的指数日线数据作为示例因为它接口相对稳定、注册免费、数据覆盖完整非常适合做历史分位数计算。如果你已经有其他数据源完全可以替换数据读取部分。baostock 需要用手机号注册获取账号密码具体流程不复杂登录后即可调用接口。以下代码会读取指定指数代码的历史日线数据。注意baostock 接口目前覆盖沪深指数和个股历史数据较为完善部分港股指数数据可能不完整。如果你需要分析港股建议使用 akshare 或专业数据源。3. 核心指标与计算原理3.1 市盈率分位数市盈率分位数是市场温度最核心的指标之一。它的含义是当前市盈率在历史区间内处于什么位置。计算方式收集指数近 N 年的每日市盈率统计历史区间内多少比例的交易日市盈率低于当前值这个比例就是分位数。分位数为 0.9意味着当前估值比历史上 90% 的时间都贵分位数为 0.1则说明当前估值处于历史低位。具体实现如下def calc_percentile(series, value): 计算某个值在历史序列中的百分位 if series.empty: return None return (series value).mean()3.2 成交额滚动分位数成交额反映市场活跃度但成交额的绝对值会随着时间推移和市场规模扩大而不断增加所以直接用绝对值判断冷暖并不合理。正确做法是计算滚动分位数比如“当前成交额在近 3 年中的位置”。这样可以在一定程度上消除市场规模扩张的长期趋势影响。3.3 温度合成方式不同维度的指标计量方式不同无法直接相加。常用的标准化方法有两种方法说明历史分位数映射每个指标先转成 0 到 1 的分位数然后加权平均Z-Score 标准化用远离均值的标准差倍数衡量适合更复杂的分析对于市场温度这个场景历史分位数映射更直观0-100 的温度值一目了然。合成温度的示例公式市场温度 估值温度 * 50% 情绪温度 * 30% 资金温度 * 20%权重可以根据自己的投资偏好和策略回测结果调整没有绝对正确的答案。4. 实战计算沪深 300 市场温度下面进入完整实战环节。我们以沪深 300 指数为例演示从数据获取到温度合成的完整流程。4.1 获取指数历史行情使用 baostock 获取沪深 300 近 10 年的日线数据。这里以本地账号登录为例代码中需要替换为自己的账号信息。import baostock as bs import pandas as pd import numpy as np # 登录 lg bs.login() # 获取沪深300指数日线数据 rs bs.query_history_k_data_plus( sh.000300, date,code,close,amount,turn,peTTM,pbMRQ, start_date2015-01-01, end_date2025-12-31, frequencyd, adjustflag2 ) data_list [] while (rs.error_code 0) rs.next(): data_list.append(rs.get_row_data()) df pd.DataFrame(data_list, columnsrs.fields) bs.logout() # 数据类型处理 df[date] pd.to_datetime(df[date]) df[close] pd.to_numeric(df[close], errorscoerce) df[amount] pd.to_numeric(df[amount], errorscoerce) df[turn] pd.to_numeric(df[turn], errorscoerce) df[peTTM] pd.to_numeric(df[peTTM], errorscoerce) df[pbMRQ] pd.to_numeric(df[pbMRQ], errorscoerce) df df.dropna(subset[close, peTTM]) print(df.tail())这段代码做了以下几件事登录 baostock查询沪深 300 的日线数据字段包括日期、收盘价、成交额、换手率、滚动市盈率、滚动市净率将字符串数据转换为数值类型删除关键字段为空的记录。4.2 计算单指标分位温度接下来定义一个通用函数把某个指标的历史序列转换成 0-100 的温度值。def indicator_temperature(df, indicator, windows750): indicator: 需要计算的指标列名 windows: 滚动窗口长度默认750个交易日约等于3年 df df.dropna(subset[indicator]).copy() # 按日期升序 df df.sort_values(date).reset_index(dropTrue) temp_list [] for i in range(len(df)): if i windows: # 数据不足时使用到当前为止所有历史数据 hist df[indicator].iloc[:i1] else: hist df[indicator].iloc[i-windows:i] current df[indicator].iloc[i] if hist.std() 0 or pd.isna(current): temp_list.append(np.nan) else: temp_list.append((hist current).mean() * 100) df[temp_ indicator] temp_list return df这个函数的逻辑很简单对每一天取出过去一段时间默认 750 个交易日约 3 年的指标历史值计算当前值在历史区间内的分位数乘以 100 得到温度。不过上面的循环效率较低数据量大的时候会很慢。更高效的做法是使用 pandas 的 rolling 方法。下面给出一个优化版本def indicator_temperature_fast(df, indicator, windows750): df df.dropna(subset[indicator]).sort_values(date).reset_index(dropTrue).copy() # 当前值低于历史值的比例使用滚动窗口 rolling_rank df[indicator].rolling(windowwindows, min_periods60).apply( lambda x: (x.iloc[-1] x).mean() * 100 ) df[temp_ indicator] rolling_rank return df这里使用了rolling().apply()pandas 会把每个窗口的数据传入函数x.iloc[-1]是当前值(x.iloc[-1] x)比较当前值是否大于窗口内每个历史值取平均就得到了分位数。4.3 合成综合温度估值和情绪指标加权合成得到综合市场温度。def compose_temperature(df): # 估值温度市盈率40% 市净率40% val_temp 0.5 * df[temp_peTTM] 0.5 * df[temp_pbMRQ] # 情绪温度换手率70% 成交额30% emo_temp 0.7 * df[temp_turn] 0.3 * df[temp_amount] # 综合温度估值60% 情绪40% df[temperature] 0.6 * val_temp 0.4 * emo_temp return df不同的指标权重会直接改变温度结果。这里给出的权重只是示例你可以根据自己熟悉的策略逻辑调整。4.4 运行完整流程将以上函数串起来形成完整脚本if __name__ __main__: # 1. 获取数据复用 4.1 节代码 # ... 假设已经得到 df 变量 # 2. 计算各个指标的滚动分位温度 df indicator_temperature_fast(df, peTTM, windows750) df indicator_temperature_fast(df, pbMRQ, windows750) df indicator_temperature_fast(df, turn, windows750) df indicator_temperature_fast(df, amount, windows750) # 3. 合成综合市场温度 df compose_temperature(df) # 4. 查看最近结果 result df[[date, close, temp_peTTM, temp_pbMRQ, temp_turn, temperature]].tail(10) print(result)预期输出是一张表格包含日期、收盘价、各指标温度值和综合温度值。温度值接近 100 表示过热接近 0 表示过冷。4.5 可视化温度曲线有了温度序列后可视化能帮助我们更直观地观察历史变化import matplotlib.pyplot as plt import matplotlib.dates as mdates plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False fig, ax plt.subplots(figsize(14, 7)) # 双轴图左轴为指数收盘价右轴为市场温度 ax.plot(df[date], df[close], color#2E86AB, label沪深300 收盘价, linewidth2) ax.set_ylabel(沪深300 收盘价, color#2E86AB) ax2 ax.twinx() ax2.plot(df[date], df[temperature], color#F06543, label市场温度, linewidth1.5, alpha0.8) ax2.axhline(y80, color#D62828, linestyle--, linewidth1, label过热线 80) ax2.axhline(y20, color#2A9D8F, linestyle--, linewidth1, label过冷线 20) ax2.set_ylabel(市场温度 (0-100), color#F06543) ax2.set_ylim(0, 100) plt.title(沪深300 市场温度走势近10年) plt.legend(locupper left) plt.grid(True, alpha0.3) plt.tight_layout() plt.show()图上可以明显看到温度攀升到高位后往往会跟随一段息从高位回落到低位后长期配置价值逐步显现。但需要反复强调这是历史统计关系不是必然规律。5. 扩展港股市场温度计算的关键差异港股与 A 股有本质差异直接照搬 A 股逻辑容易得出错误结论。5.1 数据口径差异港股指数中恒生指数和恒生科技指数成分股数量差异巨大港股成交量受少数权重股影响明显港股的卖空数据是重要情绪指标A 股则没有等量级数据。所以在计算港股温度时建议额外引入卖空比率作为情绪维度指标卖空成交额占市场总成交额的比例越高通常代表市场看空情绪越浓。5.2 历史区间选择A 股投资者普遍习惯看 3-5 年分位但港股结构变化较快尤其是恒生科技这类新经济指数历史超过 5 年的数据参考意义有限。建议对恒生指数采用 5 年窗口对恒生科技指数采用 3 年窗口。5.3 实战一份简化的港股温度计算框架def hk_market_temperature(df, val_colpe, emo_colsell_ratio, window750): 港股市场温度计算框架 val_col: 估值指标列 emo_col: 卖空比例列 df df.sort_values(date).reset_index(dropTrue) # 估值温度 df[temp_val] df[val_col].rolling(window, min_periods60).apply( lambda x: (x.iloc[-1] x).mean() * 100 ) # 情绪温度注意卖空比例越高情绪越冷所以用反向指标 df[temp_emo] 100 - df[emo_col].rolling(window, min_periods60).apply( lambda x: (x.iloc[-1] x).mean() * 100 ) # 综合温度 df[temperature] 0.6 * df[temp_val] 0.4 * df[temp_emo] return df需要注意的是这个函数只是一个计算框架不代表任何真实市场判断。实际使用时要根据数据源提供的字段名调整列名并根据自己的分析目标调整指标和权重。6. 常见问题与排查思路动手操作时你大概率会遇到下面这些问题。问题现象常见原因解决思路baostock 登录失败账号未注册或密码错误重新注册检查账号密码返回数据为空指数代码错误或日期范围超出覆盖范围核对指数代码缩小日期范围peTTM 列出现大量 NaN数据源部分日期未提供估值数据用dropna删除或向前填充建议删除温度计算出现连续 NaN滚动窗口min_periods设置过大把min_periods调小比如 60温度全部为 0 或 100窗口内数值相同检查是否选错了指标字段或使用了缩放后的数据画图中文乱码matplotlib 默认字体不支持中文设置plt.rcParams[font.sans-serif] [SimHei]运行速度极慢rolling().apply()循环方式不够高效检查数据量减少窗口宽度或改用 numba 加速6.1 数据源接口变动问题免费数据源接口变化很频繁这是无法避免的。如果今天运行代码提示找不到接口大概率是数据源升级了接口名称或参数。建议处理方式关注数据源官方文档更新记录将数据获取与指标计算代码解耦数据本地化保存为 CSV/Parquet 后后续分析不再依赖网络接口备份历史数据避免接口失效后数据丢失。6.2 历史分位数的统计陷阱滚动分位数计算有一个容易被忽略的陷阱前视偏差。如果在计算 t 日分位数时不小心用到了 t 日之后的数据统计结果就没有实时可用性。在示例代码中我使用了rolling(window, min_periods)pandas 的 rolling 默认窗口只包含当前值和之前的数据不包含未来数据这是安全的。但如果自己用循环方式实现要格外注意切片边界避免把未来数据切进来。7. 最佳实践与工程建议7.1 数据本地化与版本管理免费数据源的不稳定性决定了我们不能在每次分析时都实时拉取数据。建议把每日数据落盘本地df.to_csv(hs300_daily.csv, indexFalse, encodingutf-8-sig)后续分析时直接读取本地文件既快又稳定。如果数据量继续增大可以改用 SQLite 或 Parquet 存储。7.2 多市场统一框架A 股、港股、美股的数据结构不同但“温度计算”的逻辑是一样的。建议把程序拆成三层数据获取层只负责拉数据统一输出标准 DataFrame指标计算层只输入 DataFrame输出指标列温度合成层只做加权合成。这样后续新增市场时只需要写一个新的数据获取函数指标计算和温度合成代码可以复用。7.3 关注极端值历史分位数对极端值很敏感。如果某一天数据出现异常比如交易系统故障导致的成交额为 0会扭曲后续一段时间的分位数结果。建议在数据预处理时对明显异常值做剔除或 winsorization 处理。# 示例对成交额做上下1%分位的截断处理 lower df[amount].quantile(0.01) upper df[amount].quantile(0.99) df[amount_clean] df[amount].clip(lower, upper)但要注意是否截断、截断多少需要结合具体指标和业务逻辑不能一概而论。7.4 合规与免责最后必须强调本文所有代码和分析方法仅用于技术学习与研究不构成任何投资建议。市场温度是一个统计描述工具它可能是有效的参考指标也可能在某些市场环境下完全失效。任何投资决策都应该在合法合规的前提下结合个人风险承受能力独立判断并自行承担责任。使用任何第三方金融数据时也请先确认数据的使用条款和授权范围。8. 总结本文从“市场温度”这个概念出发梳理了它背后的估值、情绪、资金三大维度并用 Python 完整实现了从数据获取、指标计算、温度合成到可视化的整套流程。同时也针对港股市场的特殊性给出了指标口径和窗口选择的调整思路。你可以把这套代码直接拿去分析沪深 300、中证 500、恒生指数、恒生科技指数等不同市场的温度变化。更进一步可以考虑引入更多因子如行业换手率变化、期权隐含波动率、南向资金流向等构建更适合自己分析框架的多维温度模型。希望这篇教程能帮你的市场分析流程从“凭感觉”进化为“有数据、可复现、可检验”。如果你在实现过程中遇到数据或代码问题欢迎在评论区留言交流。
返回列表