当前位置:首页>python>三种完全不同的市场:用 Python 拆解市场如何定价恐惧

三种完全不同的市场:用 Python 拆解市场如何定价恐惧

  • 2026-10-11 07:17:53
三种完全不同的市场:用 Python 拆解市场如何定价恐惧

用 Python 揭秘均值回归策略:你的收益从何而来?

2026年重磅升级已全面落地!欢迎加入专注财经数据与量化投研的【数据科学实战】知识星球!您将获取持续更新的《财经数据宝典》与《量化投研宝典》,双典协同提供系统化指引;星球内含 500 篇以上独有高质量文章,深度覆盖策略开发、因子分析、风险管理等核心领域,内容基本每日更新;同步推出的「量化因子专题教程」系列(含完整可运行代码与实战案例),系统详解因子构建、回测与优化全流程,并实现日更迭代。我们持续扩充独家内容资源,全方位赋能您的投研效率与专业成长。无论您是量化新手还是资深研究者,这里都是助您少走弯路、事半功倍的理想伙伴,携手共探数据驱动的投资未来!

引言

2025 年 4 月 3 日,美国宣布对中国进口商品加征大规模关税,SPY(标普 500 ETF)当天暴跌 4.8%,次日再跌 6%。财经新闻的标题千篇一律:「地缘政治不确定性冲击市场」。

而在更早的 2024 年 8 月 5 日,日元套息交易(Yen Carry Trade)大规模平仓,SPY 单日下跌 3%,恐慌指数相关产品 VIXY 飙升。新闻标题还是那一句:「地缘政治不确定性冲击市场」。

两次事件被贴上了同一个标签。但如果你真的把数据拉出来看,会发现它们几乎没有任何共同点:关税冲击中黄金大涨,日元平仓时黄金反而下跌;日元平仓时债券被疯抢,关税冲击时债券却和股票一起被抛售。

同一个标签,完全不同的市场。

本文将带你用 Python 对三次地缘政治冲击事件做一次"法医式"拆解:什么资产先动、期权市场在现货下跌前定价了什么、新闻情绪又在说什么。这是一个非常好的量化金融 + 数据分析实战案例,适合正在学习 Python 的你。

实验设计:11 个资产组成的"指纹采集器"

分析覆盖三次事件:

  • • 2023 年 10 月 7 日:哈马斯袭击以色列(信心冲击)
  • • 2024 年 8 月 5 日:日元套息交易平仓 + 中东局势升级(流动性冲击)
  • • 2025 年 4 月 3 日:美中关税冲击(结构性冲击)

资产篮子围绕一个问题构建:哪些工具最能揭示市场如何"解读"一次冲击?

  • • 宽基股指 ETF(SPY、QQQ、IWM):看抛售的规模和市值结构
  • • 行业 ETF(XLE 能源、XLF 金融、ITA 国防、XLK 科技):看经济后果被定价在哪里
  • • 避险资产(GLD 黄金、TLT 长债、UUP 美元):最具诊断价值,避险资产相对股票的走法,揭示市场在表达哪一种恐惧
  • • VIXY:直接跟踪隐含波动率

这 11 个资产共同构成每次事件的"指纹"。数据通过行情数据服务商的历史 EOD(日终)API 获取,每个事件取前后各 30 天的窗口。

import requests
import
 pandas as pd

# 数据服务商的 API 地址和密钥(请替换为你自己使用的服务)

BASE_URL = '你的行情数据API地址'
api_key = '你的API密钥'

# 定义三次事件:日期、标签、冲击类型

events = {
    'oct7_attack'
: {
        'date'
: '2023-10-07',
        'label'
: '哈马斯袭击以色列(2023 年 10 月)',
        'shock_type'
: 'confidence',   # 信心冲击
    },
    'yen_carry_unwind'
: {
        'date'
: '2024-08-05',
        'label'
: '日元套息平仓(2024 年 8 月)',
        'shock_type'
: 'liquidity',    # 流动性冲击
    },
    'tariff_shock'
: {
        'date'
: '2025-04-03',
        'label'
: '美中关税冲击(2025 年 4 月)',
        'shock_type'
: 'structural',   # 结构性冲击
    }
}

# 11 个资产构成的观察篮子

assets = {
    'spy'
: 'SPY.US', 'qqq': 'QQQ.US', 'iwm': 'IWM.US',
    'xle'
: 'XLE.US', 'xlf': 'XLF.US', 'ita': 'ITA.US',
    'xlk'
: 'XLK.US', 'gld': 'GLD.US', 'tlt': 'TLT.US',
    'uup'
: 'UUP.US', 'vixy': 'VIXY.US'
}

def
 fetch_prices(ticker, start, end):
    """拉取单个资产的复权收盘价"""

    params = {'from': start, 'to': end,
              'api_token'
: api_key, 'fmt': 'json'}
    r = requests.get(f'{BASE_URL}/eod/{ticker}', params=params)
    df = pd.DataFrame(r.json())
    df['date'] = pd.to_datetime(df['date'])
    # 使用复权收盘价(adjusted_close),自动处理拆股和分红的影响

    df = df.set_index('date')[['adjusted_close']]
    df.columns = [ticker.split('.')[0].lower()]
    return
 df

def
 fetch_event_prices(event_date, lookback=30, lookahead=30):
    """以事件日为中心,取前后各 30 天的所有资产价格"""

    start = (pd.Timestamp(event_date) - pd.Timedelta(days=lookback)).strftime('%Y-%m-%d')
    end = (pd.Timestamp(event_date) + pd.Timedelta(days=lookahead)).strftime('%Y-%m-%d')
    frames = [fetch_prices(t, start, end) for t in assets.values()]
    return
 pd.concat(frames, axis=1)

# 为三次事件各生成一个 DataFrame

event_prices = {name: fetch_event_prices(e['date']) for name, e in events.items()}

小提示:这里使用复权收盘价(adjusted close)而不是普通收盘价,是为了消除拆股和分红造成的价格"假跳水"。这是做历史回测时最常见的易错点之一。

第一步:重定价排行榜——谁先动,动多大?

要横向比较三次事件,需要一个统一的度量方法。思路分三步:把所有资产价格在事件日归一化为 100;切出事件前后的窗口;按 T+1(事件次日)涨跌幅的绝对值排名,找出"最先重定价"的资产。

def normalize_to_event(df, event_date):
    """将所有资产价格在事件日归一化为 100,方便跨资产比较"""

    event_date = pd.Timestamp(event_date)
    # 事件日如果是周末,自动顺延到第一个交易日(锚定日)

    anchor = df.index[df.index >= event_date][0]
    normalized = df.div(df.loc[anchor]) * 100
    return
 normalized, anchor

def
 repricing_leaderboard(window_df, anchor):
    """计算事件后的累计收益,并按 T+1 涨跌幅绝对值排名"""

    anchor_idx = window_df.index.get_loc(anchor)
    post_event = window_df.iloc[anchor_idx:]
    # 相对锚定日的累计收益率(百分比)

    cumulative_returns = (post_event / post_event.iloc[0] - 1) * 100
    # 取 T+1 当天的变动幅度,按绝对值降序排列

    t1_moves = cumulative_returns.iloc[1].abs().sort_values(ascending=False)
    return
 cumulative_returns, t1_moves

三次事件的 T+1 排行榜结果非常有意思:

事件
T+1 最大变动(除 VIXY 外)
特征
哈马斯袭击
IWM 仅 1.13%,全场波澜不惊
VIXY 只动了 3.35%
日元套息平仓
TLT 大涨 2.24%,债券被疯抢
VIXY 飙升 20.52%
关税冲击
XLE 暴跌 9.20%,全行业 4%~9% 齐跌
TLT 只动 1.09%,债券失灵

VIXY 在三次事件中都排第一,这符合常识——波动率永远是重定价最快的。但剥开 VIXY 往下看,三张榜单完全不同。这本身就是关键信息:T+1 排行榜揭示了市场到底在为什么定价。

一个细节:10 月 7 日袭击发生在周六,第一个交易日是 10 月 9 日(周一),所以锚定日要顺延。处理事件研究时,"事件日不一定是交易日"是必须考虑的工程问题。

第二步:期权 IV 偏度——恐惧的"定价标签"

价格数据告诉你发生了什么,期权数据告诉你市场愿意花多少钱来防范它。

这里的偏度(Skew)指标定义很直观:

  • • 取虚值认沽期权(OTM Put,行权价为现货的 90%~97%)的平均隐含波动率
  • • 减去平值认购期权(ATM Call,行权价为现货的 97%~103%)的平均隐含波动率

当这个差值上升,说明市场在为下行保护支付溢价——这就是被量化了的恐惧。

def compute_skew(df, spot):
    """计算每日的 IV 偏度:虚值 Put 的平均 IV - 平值 Call 的平均 IV"""

    df = df.copy()
    # moneyness(价值状态)= 行权价 / 现货价

    df['moneyness'] = df['strike'] / spot

    for
 expiry in sorted(df['exp_date'].unique()):
        sub = df[df['exp_date'] == expiry]
        # 筛选虚值认沽:行权价在现货的 90%~97% 之间

        otm_puts = sub[(sub['type'] == 'put') & (sub['moneyness'].between(0.90, 0.97))]
        # 筛选平值认购:行权价在现货的 97%~103% 之间

        atm_calls = sub[(sub['type'] == 'call') & (sub['moneyness'].between(0.97, 1.03))]
        if
 otm_puts.empty or atm_calls.empty:
            continue


        daily_skew = []
        # 按交易日分组,逐日计算偏度

        for
 date, puts in otm_puts.groupby('tradetime'):
            calls = atm_calls[atm_calls['tradetime'] == date]
            if
 calls.empty:
                continue

            skew = puts['volatility'].mean() - calls['volatility'].mean()
            daily_skew.append({'date': date, 'skew': skew})

        if
 daily_skew:
            return
 pd.DataFrame(daily_skew).set_index('date').sort_index()
    return
 pd.DataFrame()

偏度数据揭示了本次分析中最关键的两个发现:

发现一:日元平仓前 6 天,期权市场已经在"买保险"。 2024 年 7 月 30 日,偏度飙升至 0.087,是整个事件前窗口的最高读数。而触发平仓的日本央行加息决议是 7 月 31 日才公布的——也就是说,期权市场在触发事件公开之前一天,就已经在为下行风险大举定价。有人(很可能是很多人)在新闻见报前就在抢购 SPY 的认沽保护。

发现二:关税冲击前一天,偏度居然是负的。 2025 年 4 月 2 日,偏度读数为 -0.023——平值 Call 比虚值 Put 还贵,期权市场不仅没在定价下跌,反而在定价上涨。第二天,SPY 就迎来了 2025 年最大单日跌幅。

对第二个发现有两种合理解释:一是市场整个 3 月都把关税当作谈判筹码来定价,到 4 月初集体认定"会达成协议";二是期权市场根本没有掌握信息——4 月 2 日宣布的关税比绝大多数参与者预期的更严厉、更直接。

无论哪种解释,结论都一样:偏度衡量的是参与者愿意为保护付多少钱。如果市场集体认定某个风险不值得定价,偏度就不会警告你。而这个集体判断,可能是错的。

第三步:复合压力指标——给信号上"双保险"

偏度信号有个弱点:它可能因为与地缘政治无关的原因而飙升。为了更稳健,再引入第二个独立信号:SPY 与 GLD 的 10 日滚动相关性。正常情况下,股票和黄金的相关性较弱或为负;当压力积累时,这种关系会瓦解。

两个信号先做 Z-score 标准化(消除量纲差异),相关性信号取反(相关性下降 = 压力上升),然后取平均得到复合分数,超过 1.0 即触发压力警报。

def build_composite(skew_df, event_prices_df):
    """构建复合压力指标:IV 偏度 + SPY/GLD 相关性瓦解"""

    prices = event_prices_df[['spy', 'gld']].copy()
    # 计算 SPY 与 GLD 的 10 日滚动相关性

    prices['corr'] = prices['spy'].rolling(10).corr(prices['gld'])

    def
 zscore(s):
        # Z-score 标准化:让两个信号在同一尺度上比较

        return
 (s - s.mean()) / s.std()

    skew_z = zscore(skew_df['skew'])
    # 相关性信号取反:相关性下降意味着压力上升

    corr_z = zscore(prices['corr'].dropna()) * -1

    combined = pd.concat(
        [skew_z.rename('skew_z'), corr_z.rename('corr_z')], axis=1)
    # 复合分数 = 两个标准化信号的均值

    combined['composite'] = combined.mean(axis=1)
    # 超过 1.0 触发压力警报

    combined['stress_flag'] = combined['composite'] > 1.0
    return
 combined

结果如何?三次事件中只有两天被打上压力标记:

  • • 2024 年 8 月 2 日——日元平仓崩盘(8 月 5 日)前的最后一个交易日
  • • 2025 年 3 月 31 日——关税崩盘(4 月 3 日)前 3 天

两次都是事前预警。但关税事件里也暴露了致命短板:4 月 2 日(崩盘前一天),偏度分量跌到 -2.824 的极端负值,把复合分数拽到了 -0.817——指标实际上在说"没有压力",紧接着就是全年最大单日跌幅。两个信号里只有相关性分量做对了,二中取一不是一个可靠的复合指标,这是该方法论的诚实边界。

第四步:新闻情绪——永远慢半拍的旁观者

最后一层数据是新闻情绪:基于财经新闻生成的每日标准化分数,范围从 -1(极度负面)到 +1(极度正面)。

结果令人警醒:

  • • 日元平仓事件:7 月 17 日至 31 日,情绪一直维持在 0.934~0.995 的"接近满分看多"区间。7 月 30 日偏度飙升那天,情绪还是 0.963。新闻圈毫无察觉,期权市场已经在行动。情绪直到 8 月 2 日才跌到 0.335——比偏度信号晚了 3 天。
  • • 关税冲击:SPY 暴跌 4.8% 的当天(4 月 3 日),新闻情绪是 +0.9365,强烈正面。新闻周期完全不知道发生了什么。

三种冲击的"指纹"对照表

把三次事件放在一起,市场行为的分类学就清晰了:

维度
信心冲击(哈马斯袭击)
流动性冲击(日元平仓)
结构性冲击(关税)
股票
几乎不动
全面下跌后较快恢复
全行业 4%~9% 深跌
黄金
小幅走高
先跌后涨
大涨且持续走强
债券(TLT)
小幅走高
大涨,避险首选
失灵,与股票同跌
本质
定价恐惧,但不定价经济损害
仓位机械性平仓,与基本面无关
重估公司在新经济体制下的价值
恢复速度
两周内基本回到原位
较快
未知

理解这三类指纹的逻辑:

  • • 信心冲击:黄金和债券同涨、股票稳住,是教科书式的避险。资金买的是"保险",不是在重估基本面。
  • • 流动性冲击:一切抛售都是机械性的——借入低息日元买高收益资产的交易者被迫同时平仓,资产间相关性瓦解,因为所有东西都因为同一个机械原因被卖出。
  • • 结构性冲击:债券提供不了保护,因为冲击本身动摇了财政和货币前景;黄金成为唯一没有交易对手风险的资产。

总结

三次事件在新闻里共用同一个标签,数据却给了它们三个截然不同的名字。这次分析有几条值得记住的结论:

  1. 1. T+1 重定价排行榜是一面照妖镜。剥开必然领涨的波动率产品,剩下的排名结构就能告诉你市场在为哪种恐惧定价。
  2. 2. 期权偏度领先于新闻情绪。在日元平仓事件中,偏度提前 6 天发出信号,新闻情绪晚了整整一周。结构化的市场信号携带着头条新闻里没有的信息。
  3. 3. 但偏度不是水晶球。在关税冲击中它彻底失灵——崩盘前一天还在定价上涨。当市场集体错误定价一个风险时,基于期权的信号只会忠实反映那个错误。偏度的沉默不等于安全。
  4. 4. 这个框架最有用的产出不是某个信号,而是一个问题:这是哪一种冲击?答案会改变接下来的一切——该不该抄底、避险资产选什么、恢复周期怎么估。

对学 Python 的同学来说,这也是一个很完整的数据分析工程范本:API 分页拉取、复权价处理、事件窗口切片、归一化、滚动相关性、Z-score 标准化、多信号合成——每一步都是量化分析中的高频基本功。

参考文章

加入专注于财经数据与量化投研的知识星球【数据科学实战】,获取本文完整研究解析、代码实现细节。

财经数据与量化投研知识社区

2026年全面升级已落地!【数据科学实战】知识星球核心权益如下:

  1. 1. 双典系统赋能:获赠《财经数据宝典》与《量化投研宝典》完整文档,凝练多年实战经验,构建系统化知识框架;
  2. 2. 量化因子日更教程(2026重磅新增):每日更新「量化因子专题教程」,配套完整可运行代码与实战案例,深度拆解因子构建、回测与优化全流程;
  3. 3. 量化文章专题教程库:500+篇星球独有高质量教程式文章,系统覆盖策略开发、因子研究、风险管理等核心领域,内容基本每日更新,并配套精选学习资料与实战参考;
  4. 4. 量化投研实战课程:赠送《AKQuant-入门及实战》《PyBroker-入门及实战》视频课程,手把手教学,快速掌握量化策略开发技能;
  5. 5. 财经数据支持:定期更新国内外财经数据,为策略研发提供精准、可靠的数据基础;
  6. 6. 顶尖学者与行业专家分享:年度邀请学术界博士与业界资深专家开展前沿论文精讲与实战案例分享,不少于4场,直击研究前沿与产业实践;
    专家直连答疑:与核心开发者及领域专家实时互动,高效解决投研实战难题;
  7. 7. 专业社群与专属福利:加入高质量交流社群,获取课程折扣及更多独家资源。

星球已沉淀丰富内容生态——涵盖量化文章专题教程库、因子日更系列、高频数据集、PyBroker实战课程、专家深度分享与实时答疑服务。无论您是初探量化的学习者,还是深耕领域的从业者,这里都是助您少走弯路、高效成长的理想平台。诚邀加入,共探数据驱动的投资未来!

好文推荐

1. 用 Python 打造股票预测系统:Transformer 模型教程(一)

2. 用 Python 打造股票预测系统:Transformer 模型教程(二)

3. 用 Python 打造股票预测系统:Transformer 模型教程(三)

4. 用 Python 打造股票预测系统:Transformer 模型教程(完结)

5. 揭秘隐马尔可夫模型:因子投资的制胜武器

6. YOLO 也能预测股市涨跌?计算机视觉在股票市场预测中的应用

7. 金融 AI 助手:FinGPT 让你轻松掌握市场分析

8. 量化交易秘籍:为什么专业交易员都在用对数收益率?

9. Python 量化投资利器:Ridge、Lasso 和 Elastic Net 回归详解

10. 掌握金融波动率模型:完整 Python 实现指南

好书推荐

最新文章

随机文章