第二十五课:曲面数据可视化——3D曲面图、热力图、动态仪表盘、异常标记

做期权做市这么多年,我越来越觉得:曲面可视化不是花架子,是保命符

你想想看,每天盯着几百个期权合约的报价,脑子里要同时处理期限结构、偏斜程度、波动率水平……光靠看数字,迟早要出问题。我见过太多交易员,明明曲面已经扭曲得不成样子了,还在那儿傻傻地报双边价。

所以这一章,咱们就聊聊怎么把曲面数据“画”出来。不是那种花里胡哨的PPT图表,而是真正能帮你做决策的实战工具。

一、3D曲面图:一眼看穿结构

3D曲面图,说白了就是把波动率曲面“立”起来看。X轴是剩余期限,Y轴是行权价(或者Delta),Z轴是隐含波动率。

我个人习惯用 plotly 来做,交互性好,能旋转、缩放。你看下面这个例子:

import plotly.graph_objects as go
import numpy as np

# 模拟曲面数据
tenors = np.array([0.1, 0.2, 0.5, 1.0, 2.0])  # 剩余期限
strikes = np.linspace(0.8, 1.2, 10)  # 行权价(归一化)
T, K = np.meshgrid(tenors, strikes)

# 模拟波动率曲面(带偏斜)
vol_surface = 0.2 + 0.1 * (K - 1.0) + 0.05 * np.sqrt(T)

fig = go.Figure(data=[go.Surface(z=vol_surface, x=T, y=K)])
fig.update_layout(
    title='期权隐含波动率曲面',
    scene=dict(
        xaxis_title='剩余期限 (年)',
        yaxis_title='行权价 (归一化)',
        zaxis_title='隐含波动率'
    )
)
fig.show()

嗯,这里要注意:别用matplotlib的3D图。我在项目中遇到过,matplotlib的3D渲染在数据点多的时候会卡成PPT,而且没法交互。plotly的WebGL渲染才是做市场景下的正确选择。

实战技巧:把鼠标悬停在曲面上,plotly会自动显示该点的具体数值。我经常用它来快速定位“异常凸起”——比如某个期限的波动率突然比周围高出一截,那八成是有人在大额交易。

二、热力图:快速扫描异常区域

3D图虽然直观,但有个问题:看久了眼睛累。而且如果你要同时监控多个品种,3D图占屏幕空间太大。

这时候热力图就派上用场了。说白了就是把曲面压扁,用颜色深浅表示波动率高低。

import seaborn as sns
import matplotlib.pyplot as plt

# 用上面的数据
plt.figure(figsize=(10, 6))
sns.heatmap(vol_surface, 
            xticklabels=np.round(tenors, 2), 
            yticklabels=np.round(strikes, 2),
            cmap='RdYlGn_r',  # 红绿配色,红色高波动
            annot=True, fmt='.3f')
plt.title('波动率曲面热力图')
plt.xlabel('剩余期限')
plt.ylabel('行权价')
plt.show()

你看,热力图有个天然优势:异常点会像黑夜里的萤火虫一样显眼。比如某个格子突然变成深红色,而周围都是绿色,那这个点十有八九有问题。

避坑指南:我曾经因为热力图的颜色映射没调好,漏掉了一个重要的套利信号。建议用 RdYlGn_r 或者 coolwarm 这类发散型色板,中间色对应正常值,两端对应极端值。千万别用彩虹色,那玩意儿看着好看,但人眼对颜色的感知是非线性的,容易误判。

三、动态仪表盘:实时监控的利器

做市交易,说白了就是跟时间赛跑。静态图表只能看历史,而我们需要的是实时监控

我个人习惯用 dash 或者 streamlit 搭一个动态仪表盘。核心逻辑是这样的:

  1. 数据流接入:从交易所或者做市系统实时拉取期权报价
  2. 曲面重建:每500ms重新计算一次波动率曲面
  3. 可视化更新:自动刷新3D图和热力图
  4. 异常检测:当曲面出现统计上的异常时,自动标记并报警

这里给一个streamlit的简单示例:

import streamlit as st
import plotly.graph_objects as go
import numpy as np
import time

st.set_page_config(layout='wide')
st.title('波动率曲面实时监控')

# 占位符
chart_placeholder = st.empty()

while True:
    # 模拟实时数据更新
    tenors = np.array([0.1, 0.2, 0.5, 1.0, 2.0])
    strikes = np.linspace(0.8, 1.2, 10)
    T, K = np.meshgrid(tenors, strikes)
    
    # 加入随机扰动模拟实时变化
    noise = np.random.normal(0, 0.005, T.shape)
    vol_surface = 0.2 + 0.1 * (K - 1.0) + 0.05 * np.sqrt(T) + noise
    
    fig = go.Figure(data=[go.Surface(z=vol_surface, x=T, y=K)])
    fig.update_layout(
        title=f'实时波动率曲面 - {time.strftime("%H:%M:%S")}',
        scene=dict(
            xaxis_title='剩余期限',
            yaxis_title='行权价',
            zaxis_title='隐含波动率'
        )
    )
    
    chart_placeholder.plotly_chart(fig, use_container_width=True)
    time.sleep(0.5)
核心要点:动态仪表盘的关键不是“画得好看”,而是“反应够快”。我建议把刷新频率控制在200-500ms,太慢了跟不上市场变化,太快了CPU扛不住。另外,一定要加一个“暂停”按钮——有时候你需要定格分析某个瞬间的曲面形态。

四、异常标记:让问题自己跳出来

做市最怕什么?怕曲面出现结构性扭曲而你却没发现

比如,某个深度虚值看涨期权的波动率突然飙升,而周围合约都很正常。这可能是有人在赌某个极端事件,也可能是数据错误。不管是哪种,你都得第一时间知道。

我常用的异常检测方法有几种:

  • 局部离群因子(LOF):检测曲面上某个点是否明显偏离周围邻居
  • Z-score阈值法:计算每个点的波动率与曲面均值的偏差,超过3个标准差就标记
  • 时间序列对比:对比当前曲面与过去N分钟的曲面,变化超过阈值就报警

在可视化上,我习惯用散点图叠加到曲面上:

# 假设我们已经检测到了异常点
anomaly_points = [(0.5, 1.1, 0.35), (1.0, 0.85, 0.28)]  # (期限, 行权价, 波动率)

fig.add_trace(go.Scatter3d(
    x=[p[0] for p in anomaly_points],
    y=[p[1] for p in anomaly_points],
    z=[p[2] for p in anomaly_points],
    mode='markers',
    marker=dict(size=10, color='red', symbol='x'),
    name='异常点'
))

你看,红色叉号在曲面上特别扎眼。我一般还会配合声音报警——一旦检测到异常,系统会“叮”一声,然后自动把异常区域的截图发到我的手机上。

一个小经验:别把所有异常都报警。我刚开始做的时候,阈值设得太低,结果一天报警几百次,最后直接免疫了。后来我把阈值调到3.5个标准差,并且要求异常必须持续超过3秒才报警,这才消停。记住:报警的目的是让你行动,不是让你焦虑

五、知识体系总览

说了这么多,咱们来梳理一下。曲面可视化其实就干三件事:看结构、找异常、盯动态。下面这张图能帮你快速理解:

曲面数据可视化 3D曲面图 看整体结构 热力图 快速扫描异常 动态仪表盘 实时监控 异常标记 自动报警 实时数据流 交易所/做市系统 四种可视化手段协同工作,构成完整的曲面监控体系

嗯,这张图其实就概括了咱们这一章的核心。3D图看宏观结构,热力图做快速扫描,动态仪表盘保证实时性,异常标记帮你抓住关键问题。四者缺一不可。

最后说一句:工具再好,也得靠人用。我见过有人把仪表盘做得跟NASA控制中心似的,结果自己从来不看。可视化不是用来炫耀的,是用来帮你赚钱的。记住这一点,你的曲面可视化才能真正发挥作用。


公众号:蓝海资料掘金营,微信deep3321