用 Streamlit + QuantDash 10分钟拼装一个跨市场持仓风险与相关性诊断面板

无论你是多市场股票的价值投资者,还是活跃的量化多策略交易者,当你持有横跨 A 股(如贵州茅台)、港股(如腾讯控股)、美股(如特斯拉、苹果)的跨市场组合时,都会面临一个痛点:

"当系统性风险来临时,我持有的这些资产,它们的相关性到底有多高?"

传统的券商客户端通常只能看单只股票的行情,而专业的机构风控终端(如彭博、Wind)不仅昂贵,而且对个人开发者极不友好。

今天,我们将利用极其轻量化的 Python 动态 Web 框架 Streamlit ,配合 QuantDash SDK ,用最干净的逻辑链条拼装一个跨市场持仓相关性与风险波动度实时诊断面板

这个面板无需你编写任何 HTML/CSS,全部由纯 Python 构建。通过在本地运行,即可一键查看持仓的动态滚动相关性矩阵(Correlation Matrix)以及年化波动率(Volatility)诊断。

1. 环境准备

在终端中一键安装所需的轻量级依赖:

bash 复制代码
pip install streamlit pandas numpy plotly quantdash
2. 看板核心代码 (app.py)

新建一个名为 app.py 的文件,将以下无 Bug 生产级代码复制进去:

python 复制代码
import streamlit as st
import pandas as pd
import numpy as np
import plotly.express as px
from quantdash import QuantDash

# 页面基础配置 (高颜值宽屏布局)
st.set_page_config(page_title="多市场资产相关性诊断面板", layout="wide")

st.title("📊 跨市场持仓相关性与风险诊断系统")
st.markdown("通过 QuantDash 精准数据流,快速评估 A/H/US 跨境持仓在特定时间窗口内的资产协方差与波动率风险。")

# 侧边栏配置区
st.sidebar.header("⚙️ 诊断参数设置")

# 用户可在此动态配置多市场自选股
ticker_input = st.sidebar.text_input(
    "输入持仓标的 (用英文逗号分隔):",
    value="600519.SH, 00700.HK, AAPL.US, TSLA.US, NVDA.US"
)

lookback_days = st.sidebar.slider(
    "分析历史窗口 (天):",
    min_value=60,
    max_value=730,
    value=252
)

# API Token 输入
api_key = st.sidebar.text_input("QuantDash API Token:", type="password", value="")

if not api_key:
    st.info("💡 请在左侧侧边栏输入您的 QuantDash API Token 以开始数据诊断。")
    st.stop()

# 初始化 QuantDash 客户端
qd = QuantDash(api_key=api_key)

# 缓存数据拉取过程,避免重复渲染时频繁调用 API 浪费频控额度
@st.cache_data(ttl=3600)
def load_historical_returns(symbols_list, days):
    prices_dict = {}
    for sym in symbols_list:
        sym = sym.strip()
        if not sym:
            continue
        try:
            # 抓取日 K 线
            df = qd.klines.get(symbol=sym, period="1d", adjust="qfq", to_dataframe=True)
            if df is not None and not df.empty:
                df["date"] = pd.to_datetime(df["date"])
                df.set_index("date", inplace=True)
                # 仅截取指定窗口长度
                df_sorted = df.sort_index().tail(days)
                prices_dict[sym] = df_sorted["close"]
        except Exception as e:
            st.error(f"无法获取标的 {sym} 的行情数据: {e}")
            
    if not prices_dict:
        return pd.DataFrame()
        
    prices_df = pd.DataFrame(prices_dict).ffill().bfill()
    # 计算每日收益率
    returns_df = prices_df.pct_change().dropna()
    return returns_df

# 解析用户输入的标的
symbols = [s.strip() for s in ticker_input.split(",") if s.strip()]

if symbols:
    with st.spinner("正在从 QuantDash 安全调取全球市场行情数据..."):
        returns_df = load_historical_returns(symbols, lookback_days)
        
    if not returns_df.empty:
        # 布局:上方为基础指标,下方为图表
        col1, col2 = st.columns([1, 2])
        
        with col1:
            st.subheader("📈 资产风险概览")
            # 计算年化波动率 (假设每年 252 个交易日)
            vol_series = returns_df.std() * np.sqrt(252) * 100
            vol_df = pd.DataFrame({
                "资产代码": vol_series.index,
                "年化波动率 (%)": vol_series.values
            }).round(2)
            
            st.dataframe(vol_df, use_container_width=True)
            
            # 提示说明
            st.info(
                "💡 **指标解释**:\n"
                "- **年化波动率**:代表该资产的历史价格波动剧烈程度。数值越高,日内或跨日振幅越大。\n"
                "- **相关性系数**:接近 1 代表同涨同跌,接近 -1 代表负相关,接近 0 代表资产走势完全独立。"
            )
            
        with col2:
            st.subheader("🔥 资产相关性矩阵热力图")
            # 计算皮尔逊相关系数
            corr_matrix = returns_df.corr()
            
            # 绘制高颜值 Plotly 热力图
            fig = px.imshow(
                corr_matrix,
                text_auto=".2f",
                aspect="auto",
                color_continuous_scale="RdBu_r",  # 红蓝色调,红代表强正相关,蓝代表强负相关
                range_color=[-1, 1],
                labels=dict(color="相关性系数")
            )
            fig.update_layout(
                margin=dict(l=20, r=20, t=20, b=20),
                paper_bgcolor="rgba(0,0,0,0)",
                plot_bgcolor="rgba(0,0,0,0)"
            )
            st.plotly_chart(fig, use_container_width=True)
            
        # 绘制资产走势图 (归一化价格以便对比)
        st.subheader("📊 持仓历史走势对比 (归一化至 100)")
        cum_returns = (1 + returns_df).cumprod() * 100
        cum_returns.reset_index(inplace=True)
        melted_df = cum_returns.melt(id_vars="date", var_name="Asset", value_name="Price")
        
        fig_line = px.line(
            melted_df, 
            x="date", 
            y="Price", 
            color="Asset",
            labels={"date": "日期", "Price": "归一化净值 (基期=100)"}
        )
        fig_line.update_layout(hovermode="x unified")
        st.plotly_chart(fig_line, use_container_width=True)
        
    else:
        st.warning("未能成功拉取任何标的数据,请检查代码格式是否符合规范(如 600519.SH 或 AAPL.US)。")
3. 运行本地看板

在命令行运行以下命令,即可在浏览器中自动弹出你的私人跨市场风控大屏:

bash 复制代码
streamlit run app.py
本地开发的"小技巧"
  1. 缓存威力(@st.cache_data):在 Streamlit 这种响应式框架中,用户的每一次滑动或点击都会重新执行整个 Python 脚本。我们通过引入 @st.cache_data,将已下载的日 K 线结果缓存本地,避免了重复向 QuantDash 接口发送多余的网络请求,确保了 UI 极高的流畅度,同时保护了 API 频控额度。

  2. 免去对齐烦恼 :在计算相关性矩阵(returns_df.corr())时,若数据源时间轴存在断档或日期不对齐,Pandas 会产生大量的 NaN。由于 QuantDash 提供的跨市场数据格式天然高度一致,在通过简短的 .ffill().bfill() 简单清洗后,即可实现无缝的矩阵计算[2](https://www.google.com/url?sa=E&q=https%3A%2F%2Fvertexaisearch.cloud.google.com%2Fgrounding-api-redirect%2FAUZIYQGWHMRUdL6BiPLoNoZlxPzAtsK6ZhXlHJc4YTmZI5qbwBAzbC9XtsgMh8NZesdXoVcVldFLvqX_KJu6bZybmaQf_2wxajPn08rrBC8fhh2pAzQN0Gpx0C3TfgdZ_wH-aeI7OPVwba9xOlWdfjslIf5adqqpLiwhfg%3D%3D "2")


💡 给开发者的建议

数据清理与接口断线是阻碍量化实盘落地最无趣、也最消耗精力的事情。如果你厌倦了反复修补开源爬虫、被各种限流和积分墙折磨,想给自己的策略来一次"干净、无污染的真身检验"[3](https://www.google.com/url?sa=E&q=https%3A%2F%2Fvertexaisearch.cloud.google.com%2Fgrounding-api-redirect%2FAUZIYQHbdgc07zKibRmBEmAfEJbrr2710JfbVwnqH19tviAbq4DgceMurnmVrbRGmREfj3M42gfzGGpCAI_uFLA3RC-03gFhu2z7WA-1sIK7Yjmk3pWVkG33ZcCauDXWS-6bqxE%3D "3")

你可以先在本地或 Jupyter 中运行:

bash 复制代码
pip install quantdash

然后访问 QuantDash后台管理 获取免信用卡的免费 API Key 开启测试。你会发现,一份设计规整、格式统一、直接输出前复权 Pandas DataFrame 的专业行情流,能为你省下多少深夜调试 Bug 的时间[1](https://www.google.com/url?sa=E&q=https%3A%2F%2Fvertexaisearch.cloud.google.com%2Fgrounding-api-redirect%2FAUZIYQFdxDmzKU78dDPPj1RcOjJObg4RPKJ0Ub66pldRh1Jv533_60WafK4tv50-qnZBkX-twWu0Ya1NU8oM2sLbz1u7q1puI1qr3jAdCvdW1cJY5XqqrsctDezUNeBkfxhXUhkNhtybOF4bc6bWCsfxuV6ybI-dydM%3D "1")[2](https://www.google.com/url?sa=E&q=https%3A%2F%2Fvertexaisearch.cloud.google.com%2Fgrounding-api-redirect%2FAUZIYQGWHMRUdL6BiPLoNoZlxPzAtsK6ZhXlHJc4YTmZI5qbwBAzbC9XtsgMh8NZesdXoVcVldFLvqX_KJu6bZybmaQf_2wxajPn08rrBC8fhh2pAzQN0Gpx0C3TfgdZ_wH-aeI7OPVwba9xOlWdfjslIf5adqqpLiwhfg%3D%3D "2")[3](https://www.google.com/url?sa=E&q=https%3A%2F%2Fvertexaisearch.cloud.google.com%2Fgrounding-api-redirect%2FAUZIYQHbdgc07zKibRmBEmAfEJbrr2710JfbVwnqH19tviAbq4DgceMurnmVrbRGmREfj3M42gfzGGpCAI_uFLA3RC-03gFhu2z7WA-1sIK7Yjmk3pWVkG33ZcCauDXWS-6bqxE%3D "3")

相关链接 :

相关推荐
倒流时光三十年9 小时前
Logback 系列(3):三大核心组件 Logger / Appender / Encoder
java·开发语言·logback
Darkwanderor10 小时前
动、静态库相关内容的详细介绍
linux·c语言·开发语言·c++
文祐10 小时前
C++类之虚函数表没有虚继承的菱形继承
开发语言·c++·算法
sycmancia10 小时前
Qt——另一种创建线程的方式
开发语言·qt
至乐活着10 小时前
深入Python内存管理与垃圾回收:从引用计数到分代回收的实战解析
python·性能优化·内存管理·垃圾回收·引用计数
m0_5474866611 小时前
《Python数据分析与挖掘》全套PPT课件
python·数据挖掘·数据分析
你怎么知道我是队长11 小时前
JavaScript 函数 this 全解
开发语言·javascript·ecmascript
L-影11 小时前
单体项目结构
java·开发语言
㱘郳11 小时前
飞机大战Pygame存档
python·pygame
孬甭_11 小时前
C++ string类
开发语言·c++