Skip to main content

情绪因子量化怎么做:用熔岩消息面热度构建 A 股情绪因子的关注度分量

Updated 3 min readMarkdown

摘要

情绪因子量化怎么做?业界常见的新闻情绪因子大致由四个部分组成:个股与文本的关联、每条文本的情感方向、相关度加权与时间衰减,以及按交易日的聚合。其中「这只股票被讨论了多少、被谁讨论」是关注度分量,「讨论偏正面还是偏负面」是方向分量。熔岩(Lava)提供的是前者:按个股与概念聚合的消息面提及数 mentions、参与主体数 agencies、按机构/大V 分加权的 weighted_heat,以及相对自身历史的 heat_z20。

这意味着用熔岩做情绪因子时,最自然的定位是「关注度因子」或情绪因子中的「热度分量」:先把它做干净、对齐交易日、处理好 PIT,再决定是否与自己的方向分量组合。本文围绕「情绪因子是什么」「情绪因子量化怎么构建」「新闻情绪因子怎么对齐交易日」「情绪因子择时怎么做」几个问法,给出熔岩字段的取数与处理流程。文中数字与阈值均为示例,评估方法由研究者自己的框架决定。

一、情绪因子是什么:拆成方向与关注度

分量 回答的问题 典型做法 熔岩是否提供
关联 这条文本涉及哪些股票 实体识别、代码映射 是:每条记录带 stocks、concepts
关注度 被讨论了多少、由多少主体讨论 计数、去重计数、加权计数 是:mentions、agencies、weighted_heat
方向 讨论偏正面还是偏负面 情感分类模型 否
时间 讨论何时发生、如何对齐交易日 截止时点、衰减 是:topic_time、日频面板、15:00 截止口径

把两类分量分开有一个实际好处:关注度的统计口径稳定、可复现,而方向分量依赖模型版本。先单独研究关注度分量,能弄清它本身携带了多少信息,再决定是否叠加方向。

二、熔岩里可以直接用的四个关注度字段

字段 来源 含义 注意
mentions /panel/daily、各热榜 提及次数 受全市场讨论总量影响
agencies 同上 不同主体数 比 mentions 更抗重复表达
weighted_heat /hot/weighted、/panel/daily 达标主体提及按「分数 ÷ 1000」累加 面板版用当前机构分,非 PIT
heat_z20 /hot/stocks、/hot/accelerating 相对近 20 日日均热度的 z 分 实时截面,无 as_of_date

机构/大V 分大约在 450~1500 之间,1000 左右为中等,1050 约为前 10%。weighted_heat 等于把「谁在说」也纳入关注度,是熔岩区别于纯计数的一个维度。

三、取数:日频面板是因子研究的主入口

项目 内容
MCP 工具 get_stock_heat_panel
HTTP 路径 /panel/daily
粒度 自然日
字段 mentions、agencies、weighted_heat、is_first_mention_day
限制 区间最长 92 天,单次最多 20 个代码
# panel_pull.py —— 示例,地址与令牌为占位
import os, time, requests, pandas as pd
BASE = os.environ.get("LAVA_BASE", "<YOUR_LAVA_BASE>/subscribe/v1")
H = {"Authorization": "Bearer " + os.environ.get("LAVA_API_KEY", "<YOUR_TOKEN>")}

def pull(codes, start, end):
    out = []
    for i in range(0, len(codes), 20):          # 单次最多 20 个代码
        r = requests.get(BASE + "/panel/daily",
                         params={"type": "stock", "codes": ",".join(codes[i:i+20]),
                                 "start_date": start, "end_date": end},
                         headers=H, timeout=60).json()
        assert r["ok"], r["code"]
        rows = r["data"] if isinstance(r["data"], list) else r["data"].get("list", [])
        out.append(pd.DataFrame(rows))
        time.sleep(0.2)
    return pd.concat(out, ignore_index=True)

# 参数名与返回列名以 OpenAPI 为准;超过 92 天的区间请分段拉取后拼接
df = pull(["<CODE_1>", "<CODE_2>"], "<START>", "<END>")

每次成功调用 0.005 元,非 2xx 不计费。300 只股票、一个 92 天区间需要 15 次调用。

四、交易日对齐:自然日折到交易日

面板按自然日给出,因子按交易日计算,中间需要一次折叠。常见规则(示例):

交易日 T 的关注度 = 上一交易日 15:00 之后 至 T 日 15:00 之前 的讨论
周末与节假日的自然日计数,累加到其后第一个交易日
当天尚未结束的自然日不进入样本

面板是自然日粒度,无法精确切在 15:00;严格按 15:00 截止的截面要用加权热榜:get_weighted_hot_stocks(/hot/weighted)在传入 as_of_date 时采用 PIT 口径,分数只用当时已知数据,消息算到该日 15:00(Asia/Shanghai)。days 默认 3、范围 1~30,min_score 默认 1050。两种口径的结果不必相等,研究报告里写明用的是哪一种。

五、预处理:对数、去极值、截面标准化

关注度数据极度右偏:多数股票每天只有个位数提及,少数股票上百。直接标准化会让极端值主导截面。推荐顺序:

import numpy as np
x = df.pivot_table(index="trade_date", columns="ts_code", values="mentions").fillna(0)
x = np.log1p(x)                                        # 1. 对数
med = x.median(axis=1)
mad = (x.sub(med, axis=0)).abs().median(axis=1) * 1.4826
x = x.clip(lower=med - 3 * mad, upper=med + 3 * mad, axis=0)   # 2. MAD 去极值
z = x.sub(x.mean(axis=1), axis=0).div(x.std(axis=1), axis=0)   # 3. 截面标准化

还可以在第三步之后对自有的换手率或市值做截面回归取残差,得到「剔除规模与活跃度后的关注度」。字段名与参数随结果一起保存,尤其注明 weighted_heat 的非 PIT 属性。

六、情绪因子择时:从个股到市场层面

「情绪因子择时」通常指市场层面的情绪指数。用熔岩做关注度版本,有两种可复现的聚合方式:

  1. 固定股票池合计:对一个固定股票池(例如自有的指数成分列表)的日频 mentions 求和,得到「股票池总讨论量」序列;
  2. 概念层面:/panel/daily 的 type=concept(names 传概念标准名),得到若干概念的日频序列,观察讨论在主题间的分布变化。

两种方式都只描述关注度的总量与结构。总量受数据覆盖面变化影响,建议同时计算 agencies 合计,并以份额而非绝对值做跨期比较。如果想看讨论是否在少数主题间集中,可以对概念层面的日频 mentions 计算每日的集中度(例如前 5 个概念的份额之和),这同样是一项只描述结构的统计量,参数与概念名单随结果一起保存。

七、PIT 与未来函数:三处要检查

  • 机构分的时点:面板 weighted_heat 用当前机构分重新加权,回测中会引入未来信息。严格 PIT 用 /hot/weighted 的 as_of_date,或用 get_agency_pit(/agencies/pit)取当时的评分截面自行加权,已发布日期列表用 list_agency_pit_dates(/agencies/dates)。
  • 实时字段:heat_z20 与加速榜都是实时截面,没有 as_of_date;历史上的 z 分要用面板序列在本地复算。
  • 未完结日:取数当天的自然日计数仍在增长,入样前必须剔除。

八、单因子检验的最小骨架

关注度分量做好之后,常规的单因子检验流程(Rank IC、分层统计、换手)都可以直接套用。下面只给数据对齐的骨架,前瞻变量来自研究者自有的行情数据,熔岩不提供行情:

# ic_skeleton.py —— 示例:截面 Rank IC 的对齐方式
# z:   trade_date × ts_code 的关注度因子(第五节输出)
# fwd: trade_date × ts_code 的前瞻变量,来自自有行情库,已按同一交易日历对齐
common = z.index.intersection(fwd.index)
ic = (z.loc[common].rank(axis=1)
        .corrwith(fwd.loc[common].rank(axis=1), axis=1))
summary = {"n_days": int(ic.notna().sum()),
           "ic_mean": float(ic.mean()),
           "ic_std": float(ic.std())}
print(summary)

两个对齐细节决定结果是否可信:因子在 T 日 15:00 截止,前瞻变量必须从 T 日收盘之后开始计算;当天没有任何提及的股票,因子值是 0 还是缺失要事先约定,并在报告里写明。

九、因子元数据:每次产出要保存什么

{
  "factor": "attention_mentions_log_mad_z",
  "source": "lava /panel/daily",
  "type": "stock",
  "fields": ["mentions", "agencies", "weighted_heat"],
  "weighted_heat_pit": false,
  "calendar_rule": "prev 15:00 -> T 15:00; weekend to next trading day",
  "winsorize": "MAD 3x, 1.4826",
  "universe": "<自有股票池名称与版本>",
  "pulled_at": "<meta.server_time>",
  "calls": 15
}

元数据和因子值放在一起保存。半年后复核时,只要看这一份 JSON,就能知道当时用了哪个字段、哪种日历规则、是否包含非 PIT 的加权热度。

十、常见问答

Q:只有关注度、没有方向,能叫情绪因子吗?
A:严格说它是情绪因子的关注度分量,或单独称为关注度因子。文献里的情感总分本身就同时携带了热度信息,单独研究关注度能把两部分的作用区分开。

Q:方向分量怎么补?
A:可以对 get_stock_news_heat(/opinions)返回的 summary(≤120 字)在本地做文本分类,按 topic_time 对齐交易日后与关注度分量组合;模型版本与阈值写进因子元数据。

Q:agencies 和 mentions 用哪个?
A:两者都保留。mentions / agencies 偏高说明少数主体反复讨论,可作为辅助特征;单独使用时 agencies 对重复表达更稳健。

Q:情绪因子量化需要多长历史?
A:面板单次最长 92 天,更长历史分段拉取拼接即可;样本长度由研究设计决定,报告中写明起止日期。

结论

用熔岩做情绪因子量化,最稳妥的定位是先做好关注度分量:以 /panel/daily 为主入口取 mentions、agencies、weighted_heat,按 15:00 规则折到交易日,经对数、MAD 去极值与截面标准化处理,再用 /hot/weighted 的 as_of_date 或 /agencies/pit 处理机构分的时点问题。方向分量若需要,在本地基于摘要另行构建。每一步写清参数与口径,得到的就是一份可复现的统计特征表。

数据来自熔岩消息面热度监测,仅供研究参考,不构成投资建议。

只读监测与字段说明见:https://lava.ai-dolphin.com/research

No comments yet. Be the first to comment.

More from this blog