【Python量化系统工程化 #10】脚本写完只有自己看?Streamlit看板把脚本变成团队5分钟能用的产品

系列定位:让"写一个能稳定跑半年的量化脚本"这件事,从散落的经验贴变成可复制的流水线。


痛点:脚本跑通了,但只有你自己能看到结果

回顾前 9 篇:数据存好了(#01-#03)、并发拉快了(#04)、重试容错了(#05-#06)、定时调度了(#07)、守护进程了(#08)、日志结构化了(#09)。整套流水线能稳定跑了。

但问题来了:结果只有你能看。

  • 你给同事发一个 CSV,他打开看到 8000 行数字,不知道哪里是重点
  • 老板想看今日行情概览,你截图发微信,每天截一次
  • 团队成员想查某只股票的历史 K 线,只能让你跑一次脚本

你缺的不是更好的脚本,是一个让脚本变成"产品"的界面。


本文你将得到什么

  1. Streamlit 是什么、为什么适合量化场景
  2. 5 分钟搭建多 Tab 看板:K 线数据 / 实时行情 / 日志查看
  3. 股票代码筛选与自动刷新
  4. 从 JSON 结构化日志(#09)直接读取并展示
  5. 部署到团队可访问的服务器

一、Streamlit 是什么

Streamlit 是一个 Python Web 应用框架,专为数据科学和机器学习设计。核心卖点:

  • 纯 Python:不需要写 HTML/CSS/JS,一个 .py 文件就是一个完整 Web 应用
  • 自动刷新:用户改参数,页面自动重算
  • 内置组件:表格、图表、滑块、下拉框、Tab 布局全部一行代码
  • 免费开源:pip install streamlit 即装即用

对比 Flask/Django:

框架 需要写 适合场景 上手时间
Flask/Django HTML 模板 + 路由 + CSS 完整 Web 产品 数天
Streamlit 纯 Python 脚本 数据看板 / 内部工具 5 分钟

量化场景特别适合:你的数据是 DataFrame,Streamlit 原生支持 st.dataframe(df)、st.line_chart(df) 一行展示。


二、5 分钟搭一个多 Tab 看板

完整代码

import streamlit as st
import pandas as pd
import time
import json
import os

--- 数据加载函数 ---

def load_stock_history(code, period="d"):
"""拉取股票历史 K 线,返回 DataFrame。"""
from mairui import Client
# 演示证书,请换成你自己的正式证书
client = Client("LICENCE-66D8-9F96-0C7F0FBCD073")
data = client.stock_history(code=code, period=period, dividend="n")
if isinstance(data, list):
df = pd.DataFrame(data)
elif hasattr(data, "iloc"):
df = data
else:
df = pd.DataFrame(data)
return df

def load_realtime_quote(codes):
"""批量拉取实时行情快照。"""
from mairui import Client
client = Client("LICENCE-66D8-9F96-0C7F0FBCD073")
code_str = ",".join(codes)
data = client.stock_ssjy_more(stock_codes=code_str)
return data

def parse_json_logs(log_dir):
"""读取 JSON 结构化日志,返回 DataFrame。"""
log_file = os.path.join(log_dir, "quant.log")
if not os.path.isfile(log_file):
return pd.DataFrame()
records = []
with open(log_file, "r", encoding="utf-8") as f:
for line in f:
line = line.strip()
if not line:
continue
try:
records.append(json.loads(line))
except json.JSONDecodeError:
continue
return pd.DataFrame(records) if records else pd.DataFrame()

--- Streamlit 应用 ---

st.set_page_config(page_title="量化数据看板", layout="wide")

st.title("量化数据看板")
st.caption("基于 mairui SDK | 数据源:A股行情 API")

侧边栏参数

with st.sidebar:
st.header("参数设置")
code = st.text_input("股票代码", value="000001")
period = st.selectbox("K线周期", ["d", "w", "m", "y"],
format_func=lambda x: {"d": "日线", "w": "周线",
"m": "月线", "y": "年线"}[x])
auto_refresh = st.checkbox("自动刷新(30秒)", value=False)

Tab 布局

tab1, tab2, tab3 = st.tabs(["K 线数据", "实时行情", "日志查看"])

Tab 1: K 线数据

with tab1:
st.subheader(f"{code} {period} K 线数据")
try:
df = load_stock_history(code, period)
if df is not None and len(df) > 0:
st.dataframe(df.tail(20), use_container_width=True)
col1, col2, col3 = st.columns(3)
with col1:
st.metric("总条数", f"{len(df):,}")
with col2:
st.metric("起始日期", str(df.iloc[0].get("t", "N/A")))
with col3:
st.metric("结束日期", str(df.iloc[-1].get("t", "N/A")))
if "c" in df.columns:
st.line_chart(df.set_index("t")["c"].tail(60))
else:
st.warning("未获取到数据,请检查股票代码或证书权限")
except Exception as e:
st.error(f"加载失败: {type(e).name}: {e}")

Tab 2: 实时行情

with tab2:
st.subheader("实时行情快照")
try:
data = load_realtime_quote(["000001", "600519", "000002"])
if data:
st.dataframe(data, use_container_width=True)
else:
st.info("暂无实时数据(演示证书可能无实时权限,请换成正式证书)")
except Exception as e:
st.error(f"实时行情加载失败: {type(e).name}: {e}")

Tab 3: 日志查看

with tab3:
st.subheader("结构化日志查看")
log_dir = os.path.join(os.path.dirname(file), "logs")
df_logs = parse_json_logs(log_dir)
if len(df_logs) > 0:
levels = st.multiselect("日志级别", df_logs["level"].unique())
if levels:
df_logs = df_logs[df_logs["level"].isin(levels)]
st.dataframe(df_logs, use_container_width=True)
else:
st.info("暂无日志文件")

自动刷新

if auto_refresh:
time.sleep(30)
st.rerun()

运行方式

pip install streamlit mairui pandas
streamlit run app.py --server.port 8501

浏览器自动打开 http://localhost:8501 ,你就有了一个完整的 Web 看板。


三、实测输出

K 线数据加载

000001: 8696 条日K线
首行: t=1991-01-07 o=65.74 c=65.74
末行: t=2026-08-20 o=11.2 c=11.4
600519: 6061 条日K线
首行: t=2001-08-27 o=34.51 c=35.55
末行: t=2026-08-20 o=1299.8 c=1291.5
000002: 8696 条日K线
首行: t=1991-01-07 o=15.23 c=15.23
末行: t=2026-08-20 o=3.07 c=3.14

JSON 日志解析(接 #09)

日志条数: 18
字段: ['ts', 'level', 'module', 'line', 'msg', 'api_name', 'record_count',
'stock_code', 'duration_ms', 'error_type']
级别统计:
INFO: 10 条
ERROR: 4 条
WARNING: 2 条
DEBUG: 2 条


四、看板功能详解

  1. 多 Tab 布局

    tab1, tab2, tab3 = st.tabs(["K 线数据", "实时行情", "日志查看"])

一行代码创建三个 Tab 页。用户点击 Tab 名切换,互不干扰。

  1. 侧边栏参数

    with st.sidebar:
    code = st.text_input("股票代码", value="000001")
    period = st.selectbox("K线周期", ["d", "w", "m", "y"])
    auto_refresh = st.checkbox("自动刷新(30秒)", value=False)

用户在侧边栏输入股票代码,主区域自动刷新数据——不需要刷新按钮。

  1. 数据表格 + 图表

    st.dataframe(df.tail(20)) # 表格(最近 20 条)
    st.line_chart(df.set_index("t")["c"].tail(60)) # 收盘价折线图

Streamlit 原生支持 pandas DataFrame,表格自动带排序/分页,图表自动用 Altair 渲染。

  1. 指标卡片

    col1, col2, col3 = st.columns(3)
    with col1:
    st.metric("总条数", f"{len(df):,}")
    with col2:
    st.metric("起始日期", str(df.iloc[0].get("t", "N/A")))
    with col3:
    st.metric("结束日期", str(df.iloc[-1].get("t", "N/A")))

三列指标卡片,一眼看到总条数和日期范围。

  1. 日志 Tab(接 #09)

    df_logs = parse_json_logs(log_dir)
    levels = st.multiselect("日志级别", df_logs["level"].unique())
    if levels:
    df_logs = df_logs[df_logs["level"].isin(levels)]
    st.dataframe(df_logs)

直接读取 #09 写的 JSON 结构化日志,用 multiselect 做级别过滤——前一篇的输出就是这一篇的输入,工程链路完整打通。

  1. 自动刷新

    if auto_refresh:
    time.sleep(30)
    st.rerun()

勾选后每 30 秒自动重新执行脚本,数据自动更新。适合盘中盯盘场景。


五、部署到团队可访问的服务器

本地跑起来只是第一步。让团队访问,需要部署到服务器:

方式一:直接运行(最简单)

streamlit run app.py --server.port 8501 --server.headless true

方式二:用 systemd 守护(接 #08)

/etc/systemd/system/quant-dashboard.service

[Unit]
Description=Quant Dashboard
After=network.target

[Service]
Type=simple
User=quant
WorkingDirectory=/home/quant/dashboard
Environment=MAIRUI_LICENCE=<你的证书>
ExecStart=/home/quant/venv/bin/streamlit run app.py --server.port 8501 --server.headless true
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

sudo systemctl enable --now quant-dashboard
# 团队访问 http://<服务器IP>:8501

生产环境建议加 Nginx 反向代理 + HTTPS + Basic Auth,这里不展开。


六、Streamlit vs 其他看板方案

方案 上手速度 灵活度 适合场景
Streamlit 5 分钟 中 数据看板 / 内部工具
Dash (Plotly) 半天 高 复杂交互看板
Grafana 配置式 中 监控指标看板
Flask + ECharts 数天 极高 定制 Web 产品

量化场景推荐 Streamlit:80% 的需求(看数据、改参数、刷图表)5 分钟搞定;只有当你需要用户上传文件、多用户权限管理时才需要上 Dash/Flask。


常见坑

坑 解决
stock_history 返回 list 不是 DataFrame pd.DataFrame(data) 转一下
看板打开白屏 检查终端报错;常见是证书缺失或网络超时
每次操作都重新拉数据 用 st.session_state 缓存,或 @st.cache_data
部署后别人访问不了 检查防火墙端口、--server.headless true
自动刷新卡死 st.rerun() 会重跑整个脚本,重操作不要勾自动刷新


小结

  • Streamlit 把脚本变产品:一个 .py 文件 = 一个 Web 应用,无需 HTML/CSS
  • 多 Tab 布局:K 线数据 / 实时行情 / 日志查看三页一屏
  • 工程链路打通:#09 的 JSON 日志 → #10 的日志 Tab,前一篇的输出是这一篇的输入
  • 部署:streamlit run + systemd 守护(#08),团队浏览器直接访问

系列完结

本系列 10 篇到此完结。回顾完整工程链路:

篇号 主题 核心能力

01 存储格式对比 CSV/SQLite/Parquet/MySQL 选型

02 增量更新 只拉新数据,省额度

03 分区表+索引 千万级 K 线零等待

04 并发批量 速度提升 26 倍

05 重试+退避 网络抖动不崩盘

06 进度条+checkpoint 崩了能续跑

07 定时调度 schedule/cron/Airflow

08 systemd 守护 开机自启+异常重启

09 日志轮转+结构化 排错秒级定位

10 Streamlit 看板 脚本变产品

从"拉数据"到"看数据",从"手动跑"到"自动跑+团队看"——这就是工程化的全链路。


代码与文档

本文配套代码已开源在 GitHub:https://github.com/MaiRuiApi

安装 SDK:

pip install mairui streamlit pandas

证书获取:访问麦蕊官网申请免费版或付费版 API 证书。


免责声明:本文仅供技术学习交流,不构成任何投资建议。股市有风险,入市需谨慎。

posted @ 2026-09-19 13:10  量化分析码农  阅读(9)  评论(0)    收藏  举报