涨跌家数指标:情绪线的起点
各位同学,今天我们来聊聊量化交易里最基础、也最直观的情绪指标——涨跌家数比。
说白了,这个指标就是数一数今天有多少股票涨了,多少跌了。然后算个比值。听起来简单吧?但就是这么一个简单的数字,背后藏着市场情绪的密码。
我个人习惯把这个指标叫做「市场呼吸灯」。为什么?因为它能直观反映市场是「喘得上气」还是「憋得慌」。牛市里涨的家数多,熊市里跌的家数多,这个道理谁都懂。但怎么把这个感觉变成可量化的信号,才是我们今天要解决的问题。
一、涨跌家数比的定义
先给个公式,大家一看就明白:
涨跌家数比 = 当日上涨家数 / 当日下跌家数
举个例子:今天沪深两市有3000只股票上涨,1500只下跌,那么涨跌比就是2.0。这意味着每2只股票上涨,对应1只股票下跌。市场情绪偏乐观。
如果反过来,1000只涨,3500只跌,比值就是0.286。嗯,这时候你该警惕了。
我在项目中遇到过一个问题:有些同学直接用「上涨家数 - 下跌家数」来做差值。我个人不建议这么做。为什么?因为差值受股票总数影响太大。十年前A股只有2000只股票,现在快5000只了。同样的差值,在不同时期代表的意义完全不同。用比值,天然做了归一化处理,更稳定。
二、数据获取与计算
好,理论讲完了,咱们直接上代码。我习惯用Tushare或者AKShare来获取数据。这里以AKShare为例,因为它免费且数据全。
import akshare as ak
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 获取全市场涨跌家数数据
def get_market_sentiment(date):
"""
获取指定日期的涨跌家数
"""
df = ak.stock_zh_a_spot_em()
# 计算涨跌家数
up_count = len(df[df['涨跌幅'] > 0])
down_count = len(df[df['涨跌幅'] < 0])
flat_count = len(df[df['涨跌幅'] == 0])
# 计算比值
if down_count > 0:
ratio = up_count / down_count
else:
ratio = np.inf # 极端情况,没有下跌的股票
return {
'date': date,
'up': up_count,
'down': down_count,
'flat': flat_count,
'ratio': ratio
}
# 测试一下
result = get_market_sentiment('2024-01-15')
print(f"上涨: {result['up']}, 下跌: {result['down']}, 涨跌比: {result['ratio']:.2f}")
这里有个细节要注意:平盘的家数怎么处理?我一般直接忽略,因为平盘的股票数量很少,对整体比值影响不大。但如果你做高频分析,可以考虑把平盘家数按0.5:0.5的比例分摊到涨跌两边。
三、构建基础情绪线
单日的涨跌比波动太大,直接看容易眼花。我建议做两个处理:
- 移动平均平滑:用5日或10日均线把毛刺去掉
- 归一化到0-100区间:方便和其他指标对比
来看代码实现:
def build_sentiment_line(start_date, end_date, window=5):
"""
构建基础情绪线
"""
# 获取日期序列
dates = pd.date_range(start_date, end_date, freq='B')
sentiment_data = []
for date in dates:
try:
result = get_market_sentiment(date.strftime('%Y-%m-%d'))
sentiment_data.append(result)
except:
continue
df = pd.DataFrame(sentiment_data)
# 计算移动平均
df['ma_ratio'] = df['ratio'].rolling(window=window).mean()
# 归一化处理(将比值映射到0-100)
min_ratio = df['ma_ratio'].min()
max_ratio = df['ma_ratio'].max()
df['sentiment_score'] = (df['ma_ratio'] - min_ratio) / (max_ratio - min_ratio) * 100
return df
# 生成情绪线
sentiment_df = build_sentiment_line('2023-01-01', '2024-01-01')
print(sentiment_df.tail())
你想想看,这个情绪线有什么意义?当sentiment_score低于20时,说明市场极度悲观,可能接近冰点。高于80时,说明市场极度亢奋,可能接近沸点。这就是我们后面要讲的「冰点-沸点」交易策略的基础。
四、核心逻辑框架
为了让大家更直观地理解整个体系,我画了一张流程图:
五、实战中的注意事项
讲完了理论和方法,我来分享几个实战中容易踩的坑:
💡 核心要点:
- 数据源一致性:不同数据源对「全市场」的定义不同。有的包含北交所,有的不包含。我建议固定使用一个数据源,不要混用。
- 新股处理:新股上市首日涨跌幅不受限制,会严重干扰比值。我一般会剔除上市不满20个交易日的股票。
- 极端值处理:当下跌家数为0时,比值会变成无穷大。这种情况虽然罕见,但代码里一定要做保护。
📌 我的个人习惯:
我会把涨跌比和成交量结合起来看。如果涨跌比很高但成交量萎缩,说明市场只是局部热点,不是全面上涨。这种时候要小心追高。
六、情绪线的可视化
最后,我们来看看怎么把情绪线画出来。好的可视化能让你一眼看出市场的情绪状态。
def plot_sentiment_line(df):
"""
绘制情绪线
"""
fig, ax = plt.subplots(figsize=(12, 6))
# 绘制情绪线
ax.plot(df['date'], df['sentiment_score'],
color='#3498db', linewidth=2, label='情绪指数')
# 绘制冰点/沸点区域
ax.axhspan(0, 20, alpha=0.2, color='red', label='冰点区域')
ax.axhspan(80, 100, alpha=0.2, color='green', label='沸点区域')
# 添加参考线
ax.axhline(y=50, color='gray', linestyle='--', alpha=0.5)
ax.set_title('市场情绪线(涨跌家数比)', fontsize=16)
ax.set_ylabel('情绪指数')
ax.legend()
plt.show()
# 调用函数
plot_sentiment_line(sentiment_df)
嗯,到这里,基础情绪线的构建就讲完了。你可能会问:这个指标单独用够不够?说实话,不够。它只是一个基础信号,需要和其他指标配合使用。但作为起点,它已经足够优秀了。
记住一句话:涨跌家数比是市场的体温计,不是诊断书。它能告诉你市场「发烧了」,但具体是什么病,还需要进一步检查。
下一节,我们会在这个基础上,加入成交量、涨跌停家数等维度,构建更立体的情绪指标体系。但今天,先把基础打牢。
📊 今日作业:
- 用AKShare获取最近30个交易日的涨跌家数数据
- 计算每日涨跌比,并用5日均线平滑
- 找出最近一个月内,情绪指数低于20和高于80的日期
- 对照这些日期的市场走势,看看冰点和沸点是否准确
公众号:蓝海数据掘金营,微信deep3321