大盘到底强不强?用 Python 做一个市场温度计

用户头像sh_*2176oo
2026-06-21 发布

大盘到底强不强?用 Python 做一个市场温度计

很多人判断市场强弱,习惯看指数:上证指数涨了,觉得行情不错;创业板跌了,觉得市场很差。

但指数有一个问题:它是少数权重股共同影响的结果。指数上涨,不代表大多数股票都在涨;指数下跌,也不代表没有结构性机会。

更好的办法是看市场广度,也就是回答几个更细的问题:

  1. 全市场有多少股票上涨?
  2. 有多少股票涨幅超过 3%?
  3. 有多少股票跌幅超过 3%?
  4. 多少股票站上 20 日均线?
  5. 成交额集中在少数股票,还是扩散到更多标的?

这篇文章用 AlphaFeed 写一个“市场温度计”:拉取全市场实时行情,再结合历史 K 线计算广度指标,输出一个简单但很有用的市场状态报告。

1. 为什么只看指数不够

假设某一天沪深 300 涨了 1%,你可能会觉得市场很强。

但真实情况可能有三种:

情况 市场含义
大多数股票上涨,指数也上涨 普涨,赚钱效应较好
少数权重股上涨,很多小票下跌 指数强,个股弱
指数小涨,但涨停家数很多 结构性行情活跃

如果只看指数,这三种情况会被混在一起。

所以做交易和研究时,我更喜欢看“市场内部结构”:上涨家数、下跌家数、强势股比例、弱势股比例、均线以上比例、成交额分布。

这些指标不神秘,Python 几十行代码就能算出来。关键是你需要一个能方便拿到全市场行情的数据源。

2. 用 AlphaFeed 获取全市场实时行情

安装:

pip install alphafeed pandas

初始化:

from alphafeed import AlphaFeed

af = AlphaFeed(api_key="your-api-key")

也可以用环境变量:

export ALPHAFEED_API_KEY="your-api-key"

然后代码里直接:

from alphafeed import AlphaFeed

af = AlphaFeed()

获取全部 A 股实时行情:

import pandas as pd
from alphafeed import AlphaFeed

af = AlphaFeed()

quotes = af.quotes.get(universes=["CN_Stock"], to_dataframe=True)

print(len(quotes))
print(quotes[["symbol", "last_price", "prev_close", "volume", "amount", "ext.name", "ext.change_pct"]].head())

AlphaFeed 支持的标的池包括:

标的池 含义
CN_Stock A 股
CN_ETF ETF
US_Stock 美股
HK_Stock 港股

这意味着你可以用同样的方法做 A 股市场温度计、ETF 市场温度计、美股观察面板或港股观察面板。

3. 计算最基础的市场广度

先计算上涨、下跌、平盘数量:

def calc_basic_breadth(quotes):
    df = quotes.copy()
    change_pct = df["ext.change_pct"]

    total = len(df)
    up = int((change_pct > 0).sum())
    down = int((change_pct < 0).sum())
    flat = total - up - down

    return {
        "total": total,
        "up": up,
        "down": down,
        "flat": flat,
        "up_ratio": up / total if total else 0,
        "down_ratio": down / total if total else 0,
    }

打印结果:

breadth = calc_basic_breadth(quotes)

print(f"全市场标的数: {breadth['total']}")
print(f"上涨家数: {breadth['up']} ({breadth['up_ratio']:.1%})")
print(f"下跌家数: {breadth['down']} ({breadth['down_ratio']:.1%})")
print(f"平盘家数: {breadth['flat']}")

这个指标很简单,但已经比只看指数多了一层信息。

比如指数涨 0.5%,但上涨家数只有 25%,那说明行情可能只是权重股在撑指数。反过来,指数跌 0.3%,但上涨家数超过 60%,说明市场内部可能并不弱。

4. 计算强势股和弱势股比例

再定义几个阈值:

def calc_strength_distribution(quotes):
    df = quotes.copy()
    change_pct = df["ext.change_pct"]

    return {
        "rise_gt_3pct": int((change_pct >= 0.03).sum()),
        "rise_gt_5pct": int((change_pct >= 0.05).sum()),
        "fall_gt_3pct": int((change_pct <= -0.03).sum()),
        "fall_gt_5pct": int((change_pct <= -0.05).sum()),
    }

这些指标可以粗略判断赚钱效应:

指标 含义
涨幅超 3% 家数 主动进攻力量
涨幅超 5% 家数 强势股扩散程度
跌幅超 3% 家数 风险释放范围
跌幅超 5% 家数 恐慌程度

很多时候,大盘指数涨跌不大,但强势股数量会提前变化。市场温度计的价值就在这里:它不是预测明天,而是更客观地描述今天。

5. 找出成交额最高的股票

成交额代表市场注意力。我们可以看资金主要集中在哪里:

top_amount = quotes.sort_values("amount", ascending=False).head(20)

print(
    top_amount[[
        "symbol",
        "ext.name",
        "last_price",
        "ext.change_pct",
        "amount",
    ]]
)

如果每天成交额前 20 名都是少数权重股,说明市场可能偏指数行情;如果成交额扩散到很多中小市值股票,说明市场活跃度可能更高。

当然,AlphaFeed 这里只提供行情数据,不替你判断行业主题。你可以把成交额榜导出后,再结合自己的行业分类或观察列表做进一步分析。

6. 加入 20 日均线以上比例

实时行情能告诉我们今天涨跌,但判断趋势还需要历史 K 线。

我们可以抽取成交额较高的一批股票,计算有多少站上 20 日均线:

active_symbols = (
    quotes.sort_values("amount", ascending=False)
    .head(300)["symbol"]
    .tolist()
)

dfs = af.klines.batch(
    active_symbols,
    period="1d",
    count=60,
    adjust="forward",
    to_dataframe=True,
    show_progress=True,
)

计算比例:

def calc_above_ma20_ratio(dfs):
    total = 0
    above = 0

    for symbol, df in dfs.items():
        df = df.sort_values("trade_date").copy()
        if len(df) < 20:
            continue

        ma20 = df["close"].rolling(20).mean().iloc[-1]
        close = df["close"].iloc[-1]

        if pd.isna(ma20):
            continue

        total += 1
        if close > ma20:
            above += 1

    return above / total if total else 0

使用:

above_ma20_ratio = calc_above_ma20_ratio(dfs)
print(f"活跃股票中站上20日线比例: {above_ma20_ratio:.1%}")

这个指标很适合做市场温度判断:

20 日线以上比例 粗略状态
70% 以上 市场较热
50% - 70% 偏强或震荡偏强
30% - 50% 震荡偏弱
30% 以下 市场较冷

阈值只是经验,不要机械使用。不同市场、不同时期都需要校准。

7. 输出一份市场温度报告

把上面的部分合起来:

import pandas as pd
from datetime import datetime
from alphafeed import AlphaFeed


def calc_basic_breadth(quotes):
    change_pct = quotes["ext.change_pct"]
    total = len(quotes)
    up = int((change_pct > 0).sum())
    down = int((change_pct < 0).sum())
    flat = total - up - down
    return total, up, down, flat


def calc_strength_distribution(quotes):
    change_pct = quotes["ext.change_pct"]
    return {
        "rise_gt_3pct": int((change_pct >= 0.03).sum()),
        "rise_gt_5pct": int((change_pct >= 0.05).sum()),
        "fall_gt_3pct": int((change_pct <= -0.03).sum()),
        "fall_gt_5pct": int((change_pct <= -0.05).sum()),
    }


def calc_above_ma20_ratio(dfs):
    total = 0
    above = 0
    for _, df in dfs.items():
        df = df.sort_values("trade_date").copy()
        if len(df) < 20:
            continue
        ma20 = df["close"].rolling(20).mean().iloc[-1]
        close = df["close"].iloc[-1]
        if pd.isna(ma20):
            continue
        total += 1
        above += int(close > ma20)
    return above / total if total else 0


def market_temperature_label(up_ratio, above_ma20_ratio, weak_count):
    if up_ratio >= 0.65 and above_ma20_ratio >= 0.6:
        return "偏热"
    if up_ratio >= 0.5 and above_ma20_ratio >= 0.45:
        return "偏强"
    if up_ratio <= 0.35 or weak_count > 500:
        return "偏冷"
    return "震荡"


def build_market_report(universe="CN_Stock"):
    af = AlphaFeed()

    quotes = af.quotes.get(universes=[universe], to_dataframe=True)
    quotes = quotes.dropna(subset=["ext.change_pct", "amount"])

    total, up, down, flat = calc_basic_breadth(quotes)
    dist = calc_strength_distribution(quotes)

    active_symbols = (
        quotes.sort_values("amount", ascending=False)
        .head(300)["symbol"]
        .tolist()
    )
    dfs = af.klines.batch(
        active_symbols,
        period="1d",
        count=60,
        adjust="forward",
        to_dataframe=True,
        show_progress=True,
    )
    above_ma20_ratio = calc_above_ma20_ratio(dfs)

    up_ratio = up / total if total else 0
    label = market_temperature_label(
        up_ratio,
        above_ma20_ratio,
        dist["fall_gt_3pct"],
    )

    top_amount = quotes.sort_values("amount", ascending=False).head(10)

    lines = []
    lines.append(f"# 市场温度计 {datetime.now():%Y-%m-%d %H:%M}")
    lines.append("")
    lines.append(f"- 标的池:{universe}")
    lines.append(f"- 市场状态:{label}")
    lines.append(f"- 上涨家数:{up} / {total} ({up_ratio:.1%})")
    lines.append(f"- 下跌家数:{down} / {total} ({down / total:.1%})")
    lines.append(f"- 平盘家数:{flat}")
    lines.append(f"- 涨幅超过 3%:{dist['rise_gt_3pct']}")
    lines.append(f"- 涨幅超过 5%:{dist['rise_gt_5pct']}")
    lines.append(f"- 跌幅超过 3%:{dist['fall_gt_3pct']}")
    lines.append(f"- 跌幅超过 5%:{dist['fall_gt_5pct']}")
    lines.append(f"- 活跃股票站上 20 日线比例:{above_ma20_ratio:.1%}")
    lines.append("")
    lines.append("## 成交额 Top 10")
    lines.append("")
    lines.append(top_amount[["symbol", "ext.name", "last_price", "ext.change_pct", "amount"]].to_markdown(index=False))
    lines.append("")
    lines.append("数据来源:AlphaFeed (https://alphafeed.org/)")

    return "\n".join(lines)


if __name__ == "__main__":
    report = build_market_report("CN_Stock")
    print(report)

    with open("market-temperature.md", "w", encoding="utf-8") as f:
        f.write(report)

如果需要 to_markdown,安装:

pip install tabulate

8. 这个报告怎么读

你可以每天收盘后生成一次报告,然后观察几个核心变化:

指标 观察重点
上涨家数比例 市场赚钱效应是否扩散
跌幅超过 3% 家数 风险释放是否扩大
活跃股站上 20 日线比例 趋势是否健康
成交额 Top 10 资金是否集中在少数权重股
市场状态标签 给自己一个快速摘要

不要把它当成买卖信号。它更像天气预报里的温度和湿度:告诉你环境如何,但不会替你决定今天穿哪件衣服。

交易决策仍然要结合你的策略、仓位、风险承受能力和交易计划。

9. 扩展到 ETF、美股、港股

如果你想看 ETF 市场:

report = build_market_report("CN_ETF")

如果你想看美股:

report = build_market_report("US_Stock")

如果你想看港股:

report = build_market_report("HK_Stock")

跨市场统一接口的好处在这里会非常明显。你的市场温度计逻辑不用重写,只要换一个标的池 ID,就可以观察不同市场。

当然,不同市场的交易制度、涨跌幅分布、成交结构都不一样,所以阈值不要照搬。比如 A 股和美股的单日波动结构不同,涨幅超过 3% 在不同市场里的含义也不同。

10. 可以继续做成一个 Dashboard

这篇文章输出的是 Markdown 报告。如果你想更进一步,可以做成网页看板:

  1. 用 Streamlit 显示市场温度;
  2. 用 Plotly 画上涨/下跌比例柱状图;
  3. 每 5 分钟刷新一次实时行情;
  4. 保存每日市场温度到 CSV;
  5. 画出市场温度历史曲线;
  6. 给极端冷/热状态加提醒。

你甚至可以让 AI 编程工具帮你改:

请基于这个 AlphaFeed 市场温度计脚本,帮我改成 Streamlit Dashboard:
1. 页面顶部显示市场状态标签;
2. 显示上涨/下跌/平盘数量;
3. 显示涨幅超过 3%、跌幅超过 3% 的数量;
4. 显示成交额 Top 20 表格;
5. 支持在侧边栏选择 CN_Stock、CN_ETF、US_Stock、HK_Stock。

这种需求非常适合 AlphaFeed + AI 编程:AlphaFeed 提供结构化数据,AI 帮你快速搭界面,你把注意力放在指标设计和结果解释上。

11. 为什么这是一个适合普通人的量化工具

很多人一听量化,就觉得一定要机器学习、深度学习、高频交易。其实不是。

对大多数个人投资者和研究者来说,更实用的量化工具往往是:

  1. 自动整理市场信息;
  2. 用固定规则减少主观情绪;
  3. 长期记录市场状态;
  4. 用数据验证自己的感觉;
  5. 在风险变大时及时降温。

市场温度计就是这样的工具。它不复杂,但每天都能用。

当你觉得“今天市场好像很强”时,可以看上涨家数比例;当你觉得“指数没跌多少,但账户很难受”时,可以看跌幅超过 3% 的家数;当你觉得“是不是该加仓”时,可以先看活跃股票站上 20 日线的比例。

它不能替你赚钱,但能帮你少一点拍脑袋。

结语

判断市场强弱,不应该只盯着指数涨跌。指数是结果,市场广度才更接近交易者真实体感。

用 AlphaFeed,你可以用几行 Python 拿到全市场实时行情,再结合历史 K 线计算自己的市场温度指标。这个过程没有玄学,也不需要复杂模型,核心就是把“感觉”变成“可重复观察的数据”。

如果你正在搭建自己的量化工作流,可以从这个市场温度计开始:每天生成一份报告,持续记录,慢慢你会对市场状态有更稳定的判断。

参考文献:

评论