Bokeh 股票相关性仪表盘实战:深入解析 stocks 示例与 Bokeh Server 应用开发

发布时间:2026/9/14 10:54:26

Bokeh 股票相关性仪表盘实战:深入解析 stocks 示例与 Bokeh Server 应用开发 Bokeh 股票相关性仪表盘实战深入解析 stocks 示例与 Bokeh Server 应用开发【免费下载链接】bokehInteractive Data Visualization in the browser, from Python项目地址: https://gitcode.com/GitHub_Trending/bo/bokeh本文以 Bokeh 官方仓库中的stocks示例应用examples/server/app/stocks为主线完整讲解如何用 Bokeh Server 从零构建一个可交互的股票对比仪表盘包括启动命令、目录型应用的目录结构与加载机制、基于yfinance的行情数据加载、联动绘图与选择交互的实现以及app_hooks.py生命周期钩子的底层原理。读完本文你将掌握 Bokeh 服务器应用Bokeh Application的标准开发范式并可以直接复用这套“数据准备 回调联动 布局组合”的模式搭建自己的数据分析看板。一、stocks 示例是做什么的stocks是 Bokeh 仓库中一个典型的Bokeh Server 应用Bokeh Application用于创建“一个简单的股票相关性仪表盘a simple stocks correlation dashboard”。该应用的定位在其源码 examples/server/app/stocks/main.py 的模块 docstring 中写得很清楚Choose stocks to compare in the drop down widgets, and make selections on the plots to update the summary and histograms accordingly.即用户通过下拉框选择两支要对比的股票应用会在散点图上展示两支股票收益率的相关性用户还可以在图上**框选box select**数据点右侧的摘要统计describe()输出会随选择实时更新。整个应用由以下几部分交互构成两个Select下拉框用于选择股票默认候选池为AAPL、GOOG、INTC、NVDA、MSFT一张收益率相关性散点图corr支持box_select框选两条时间序列曲线图ts1、ts2分别显示两支股票的收盘价支持xbox_select横向框选一个PreText统计面板实时展示所选股票的收盘价与收益率的描述性统计describe()。在 Bokeh 官方的 Server 应用总览文档 examples/server/app/README.md 中stocks 被描述为 “Linked plots, summary statistics, and correlations for market data”即“联动绘图、摘要统计与市场数据相关性”这是学习 Bokeh 联动交互最合适的入门级示例之一。二、运行方式一条命令启动整个看板根据 examples/server/app/stocks/README.md启动该应用的命令非常简单先进入其父目录examples/server/app然后执行bokeh serve --show stocks其中bokeh serve是 Bokeh 自带的服务器命令用于托管 Bokeh 应用stocks是应用目录名即examples/server/app/stocks/目录Bokeh 会将其解析为一个目录型应用--show表示启动成功后自动在浏览器中打开新标签页。启动后浏览器地址为http://localhost:5006/stocks端口5006是 Bokeh Server 的默认端口/stocks是应用目录名对应的 URL 路径。关于bokeh serve的更多用法如指定端口--port、--allow-websocket-origin等可以执行bokeh serve --help查看。注意stocks 应用需要联网加载股票数据见下文app_hooks.py并且依赖第三方库yfinance运行前需要保证环境已安装yfinancepip install yfinance。除了 stocksexamples/server/app目录下还提供了clustering、crossfilter、movies、ohlc、selection_histogram、spectrogram等大量同类 Bokeh Server 应用运行方式完全一致例如bokeh serve --show selection_histogram.py直接指定单脚本文件适合对照学习。三、目录型应用的结构与加载机制stocks 是一个目录型应用Directory-style app其目录内只有三个文件examples/server/app/stocks/ ├── README.md # 应用说明与运行命令 ├── app_hooks.py # 服务端生命周期钩子数据预加载 └── main.py # 应用主体构建文档、绘图与回调这种目录结构是 Bokeh Server 的规范约定。从源码 src/bokeh/application/handlers/directory.py 可以看到DirectoryHandler在初始化时会在目录中查找main.py或main.ipynb作为主应用脚本两者同时存在时优先使用main.py见 directory.py#L132-L142查找目录下的app_hooks.py或server_lifecycle.py作为生命周期钩子模块两者不能同时存在否则会抛出ValueError见 directory.py#L149-L162若存在__init__.py则将其作为 Python 包运行使main.py可以通过包名共享模块级状态。也就是说对 Bokeh 而言一个目录只要有main.py就可以被bokeh serve直接当作应用启动app_hooks.py属于可选的“锦上添花”能力用于在服务端生命周期事件中执行数据准备逻辑。3.1 数据加载钩子app_hooks.pystocks 的 app_hooks.py 是理解“Bokeh 应用如何预加载数据”的最佳范例全文如下import yfinance as yf TICKERS [AAPL, GOOG, INTC, NVDA, MSFT] def on_server_loaded(server_context): print(\nLoading data from Yahoo finance..., end) for ticker in TICKERS: df yf.download(ticker, start2020-09-01, end2022-09-01) df[Returns] df[Close].diff() setattr(server_context, ticker, df) print(DONE!\n)关键点解读on_server_loaded(server_context)是 Bokeh 定义的服务端生命周期回调在服务器首次加载该应用时执行一次不是每次会话都执行。它的作用是把五支股票AAPL、GOOG、INTC、NVDA、MSFT在 2020-09-01 至 2022-09-01 期间的数据一次性下载到内存中并通过setattr(server_context, ticker, df)把每个DataFrame挂到server_context上供main.py直接读取。数据来源是 Yahoo Finance通过yfinance库并且额外计算了一列日收益率Returns Close.diff()收盘价的一阶差分。这种“在on_server_loaded中准备全局共享数据”的模式避免了每个浏览器会话都重复下载同一批数据属于 Bokeh Server 应用的标准优化手段。3.2 生命周期钩子的底层原理app_hooks.py之所以能被识别是因为 Bokeh 在DirectoryHandler中专门查找该文件名并交由ServerLifecycleHandler处理见 directory.py#L150-L160。而ServerLifecycleHandler在 src/bokeh/application/handlers/server_lifecycle.py 中实现它会读取钩子模块的__dict__提取其中定义的on_server_loaded、on_server_unloaded、on_session_created、on_session_destroyed四个可选回调见 server_lifecycle.py#L83-L97并用_check_callback校验其签名on_server_loaded必须接收server_context参数。真正的调用时机在 src/bokeh/application/application.py#L195-L203Application.on_server_loaded(server_context)会按顺序调用每个 handler的on_server_loaded。因此若你的应用有多个 handler例如同时挂载多个目录应用每个 handler 的钩子都会被执行on_server_unloaded在服务器干净退出前执行需要注意服务器常被信号直接杀死该回调在实践中不一定可靠。stocks 示例只用了on_server_loaded一个钩子把“下载五支股票数据”这件事与“构建界面”解耦让main.py保持纯粹的可视化逻辑——这是目录型应用推荐的职责划分。四、应用主体 main.py 逐段解析main.py 是应用的核心它完成“取数据 → 建图 → 挂回调 → 布局 → 提交文档”的完整流程。下面按逻辑分段解读。4.1 全局状态与数据读取from functools import lru_cache from bokeh.layouts import column, row from bokeh.models import ColumnDataSource, PreText, Select from bokeh.plotting import curdoc, figure DEFAULT_TICKERS [AAPL, GOOG, INTC, NVDA, MSFT] SERVER_CONTEXT curdoc().session_context.server_context def nix(val, lst): return [x for x in lst if x ! val] lru_cache def get_data(t1, t2): df1 getattr(SERVER_CONTEXT, t1) df2 getattr(SERVER_CONTEXT, t2) df1.columns [f{col[0]}_{col[1]} if isinstance(col, tuple) and col[1] ! else col[0] for col in df1.columns] df2.columns [f{col[0]}_{col[1]} if isinstance(col, tuple) and col[1] ! else col[0] for col in df2.columns] df1 df1.rename(columns{Close: fClose_{t1}, Returns: fReturns_{t1}}) df2 df2.rename(columns{Close: fClose_{t2}, Returns: fReturns_{t2}}) data df1.join(df2, howouter).dropna() data[t1] data[fClose_{t1}] data[t2] data[fClose_{t2}] data[t1_returns] data[fReturns_{t1}] data[t2_returns] data[fReturns_{t2}] return data这里有三处值得注意的实现细节通过session_context.server_context拿到全局数据SERVER_CONTEXT curdoc().session_context.server_context正是读取app_hooks.py中挂载数据的关键句柄。main.py每次执行都通过getattr(SERVER_CONTEXT, ticker)取回对应股票的DataFrame实现“数据加载一次、多处复用”。扁平化 MultiIndex 列名yfinance下载的数据列名是(字段, 股票代码)形式的元组MultiIndex所以这里把元组列名拼接成字段_代码字符串如Close_AAPL再统一重命名为Close_{t1}、Returns_{t1}等带股票后缀的列最后用join(howouter)按日期外连接、dropna()丢弃缺失行。lru_cache缓存get_data(t1, t2)用最近最少使用缓存缓存计算结果当用户反复切换股票组合时相同组合的 DataFrame 无需重复拼接属于典型的性能优化手段。4.2 两个数据源与三张图source ColumnDataSource(datadict(date[], t1[], t2[], t1_returns[], t2_returns[])) source_static ColumnDataSource(datadict(date[], t1[], t2[], t1_returns[], t2_returns[])) corr figure(width370, height350, min_border_left60, toolspan,wheel_zoom,box_select,reset, active_dragbox_select) corr.scatter(t1_returns, t2_returns, size3, sourcesource, selection_colororange, alpha0.8, nonselection_alpha0.1, selection_alpha0.5) ts1 figure(width900, height200, x_axis_typedatetime, toolspan,wheel_zoom,xbox_select,reset, active_dragxbox_select) ts1.line(Date, t1, sourcesource_static) ts1.scatter(Date, t1, size3, sourcesource, colorNone, selection_colororange) ts2 figure(width900, height200, x_axis_typedatetime, toolspan,wheel_zoom,xbox_select,reset, active_dragxbox_select) ts2.line(Date, t2, sourcesource_static) ts2.scatter(Date, t2, sourcesource, colorNone, selection_colororange) ts2.x_range ts1.x_range这段代码展示了 Bokeh 联动交互的经典设计两个ColumnDataSource分工明确source用于散点层承载选择状态source_static用于线层静态底图。选择交互发生在source上而底图线不参与选择因此框选不会改变线的外观。corr相关性散点图以t1_returns为 x 轴、t2_returns为 y 轴绘制散点active_dragbox_select将默认拖拽手势设为框选通过selection_colororange、selection_alpha0.5、nonselection_alpha0.1区分选中/未选中点这是 Bokeh 基于ColumnDataSource选择状态的典型视觉反馈。ts1/ts2时间序列图x_axis_typedatetime让 x 轴按时间刻度渲染ts2.x_range ts1.x_range将两张图的 x 轴范围共享实现缩放/平移时两张时序图联动——这是 Bokeh 中最常用的跨图联动手段之一。4.3 下拉框与回调联动stats PreText(text, width500) ticker1 Select(valueAAPL, optionsnix(GOOG, DEFAULT_TICKERS)) def ticker1_change(attrname, old, new): ticker2.options nix(new, DEFAULT_TICKERS) update() ticker1.on_change(value, ticker1_change) ticker2 Select(valueGOOG, optionsnix(AAPL, DEFAULT_TICKERS)) def ticker2_change(attrname, old, new): ticker1.options nix(new, DEFAULT_TICKERS) update() ticker2.on_change(value, ticker2_change)两个Select下拉框通过on_change(value, callback)注册回调实现互斥选择当用户在ticker1中选择了一支股票ticker1_change会立刻用nix即“从列表中剔除该项”把该股票从ticker2的候选项中移除保证两支股票不会相同然后调用update()刷新全图。回调签名(attrname, old, new)是 Bokeh 属性回调的固定形式分别表示变化的属性名、旧值、新值。4.4 更新与选择回调def update_stats(data, t1, t2): stats.text str(data[[fClose_{t1}, fClose_{t2}, fReturns_{t1}, fReturns_{t1}]].describe()) def update(selectedNone): t1, t2 ticker1.value, ticker2.value data get_data(t1, t2) source.data data source.selected.indices [] source_static.data data corr.title.text f{t1} returns vs. {t2} returns ts1.title.text, ts2.title.text t1, t2 update_stats(data, t1, t2) def selection_change(attrname, old, new): t1, t2 ticker1.value, ticker2.value data get_data(t1, t2) if selected : source.selected.indices: data data.iloc[selected, :] update_stats(data, t1, t2) source.selected.on_change(indices, selection_change)update()是核心刷新函数切换股票后重新取数、把新数据一次性写入source和source_static、清空旧选择source.selected.indices []、更新三张图的标题并刷新统计文本。注意source.data data是把整个DataFrame直接赋给ColumnDataSourceBokeh 会自动按列名匹配列。selection_change监听source.selected.indices即被框选中的行索引。if selected : source.selected.indices:使用了海象运算符当存在选中项时只对选中行做统计data.iloc[selected, :]实现“框选即看子集统计”。一个可以留意的细节从源码看update_stats中统计列列表的第四项写的是Returns_{t1}而不是Returns_{t2}见 main.py#L78-L79。这看起来像示例源码中的一处笔误重复引用了第一支股票的收益率列但不影响应用整体运行读者在自行复刻时可以改为Returns_{t2}以统计两支股票的收益率。4.5 布局与提交文档update() widgets column(row(ticker1, ticker2), stats) main_row row(corr, widgets) series column(ts1, ts2) curdoc().add_root(column(main_row, series)) curdoc().title Stocks最后一步是布局与文档提交用row/column组合布局上方一行是“散点图 右侧控件两个下拉框和统计面板”下方一列是两张时序图脚本末尾调用curdoc().add_root(...)把布局树挂到当前文档curdoc().title Stocks设置浏览器标签页标题update()在末尾被显式调用一次保证页面首次加载时立即渲染出默认股票AAPL vs GOOG的完整数据。这也解释了 Bokeh 目录型应用的本质main.py每次新会话都会执行一遍向curdoc()提交文档内容而app_hooks.py中的on_server_loaded只执行一次用于准备跨会话共享的数据。五、事件驱动模型小结一次完整的交互链路综合main.py与app_hooks.py一次典型交互用户在ticker1中把股票从 AAPL 换成 NVDA的完整链路如下Bokeh Server 启动DirectoryHandler加载app_hooks.py见 directory.py#L150-L162Application.on_server_loaded触发on_server_loaded(server_context)五支股票数据被下载并挂到server_contextapplication.py#L195-L203浏览器打开http://localhost:5006/stocks新会话执行main.pyget_data(AAPL, GOOG)经lru_cache取数并构建界面用户在下拉框选择 NVDAticker1_change回调触发ticker2.options剔除 NVDA随后update()重新取数此时get_data(NVDA, GOOG)命中缓存则直接返回、刷新三张图与统计用户在散点图上框选数据点source.selected.indices变化触发selection_change统计面板切换为选中子集的describe()输出。整条链路完全由 Bokeh Server 的事件回调机制驱动Python 回调运行在服务器端界面状态的变更通过 WebSocket 同步到浏览器端渲染无需编写任何前端 JavaScript。六、如何在此基础上扩展stocks 是一个极具可扩展性的模板读者可以基于它快速搭建自己的看板更换数据源把 app_hooks.py 中yf.download(...)替换为本地 CSV、数据库或自建接口只要最终在server_context上挂载“含日期、收盘价列的DataFrame”即可增加股票池修改DEFAULT_TICKERSmain.py与TICKERSapp_hooks.py为同一组代码即可扩充候选列表增加统计维度在get_data中追加计算列如滚动均值、涨跌幅并在update_stats的describe()之外补充自定义统计参考同目录示例examples/server/app下的 selection_histogram.py 演示了“选中/未选中点分别绘制直方图”的进阶选择交互ohlc 演示了流式更新streaming API非常适合与 stocks 对照学习。七、总结stocks虽然只是一个“简单的股票相关性仪表盘”但它浓缩了 Bokeh Server 应用开发的全部核心知识点目录型应用的组织方式main.py 可选app_hooks.py、服务端生命周期钩子on_server_loaded与跨会话数据共享server_context、ColumnDataSource驱动的选择状态可视化、on_change属性回调与selected.indices选择回调、跨图联动共享x_range以及row/column布局与curdoc()文档提交。掌握了这份示例你就掌握了用纯 Python 构建交互式数据看板的核心方法——这正是 Bokeh “从 Python 到浏览器交互式可视化”理念的最佳入门教材。【免费下载链接】bokehInteractive Data Visualization in the browser, from Python项目地址: https://gitcode.com/GitHub_Trending/bo/bokeh创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/9/14 10:54:26

Spring框架九大设计模式深度解析与实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/14 10:54:26

基于SpringBoot与深度学习的图书推荐系统实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/14 10:49:26

基于K-Means聚类的非线性抗干扰算法MATLAB实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/14 11:39:30

基于Java Swing的股票交易模拟系统:从订单队列到JDBC事务的完整实践

简介:面向数据结构课程设计而整理的Java实战项目,实现了一款基于Swing的股票交易模拟系统,覆盖行情实时展示、系统管理、账户管理、模拟交易、技术指标与策略分析等完整功能模块,适合计算机专业学生用于课程设计、毕业设计或Java …

2026/9/14 11:39:30

C#实现测绘后方交会坐标解算与精度验证

简介:本资源是一个基于C#开发的测绘专业后方交会计算程序,面向测绘工程学生、测量技术人员及C#初学者,解决未知点坐标解算这一典型测量问题。程序完整实现了数据输入、方位角转换、线性方程组求解、误差分析与结果输出等核心流程,…

2026/9/14 11:39:30

Three.js粒子特效实战:从核心原理到网页落地与性能优化

简介:基于Three.js的粒子特效网页设计源码,面向前端开发者和三维可视化初学者,演示如何用JavaScript与CSS构建动态粒子系统并集成3D模型,适用于个人网站、产品展示等需要增强视觉冲击力的场景。资源共41个文件,容量约6…

2026/9/14 11:39:30

Java并发核心:单例、阻塞队列、定时器与线程池全解析

Java并发进阶2:把单例、阻塞队列、定时器、线程池一次讲透并发编程这块,光看八股文和面试题是远远不够的,因为面试题告诉你的只是“结论”,而工程里的坑往往藏在“为什么”里。我见过不少人能背出线程池的七大参数,也能…

2026/9/14 11:39:30

专科生论文写作神器:8款实测有效的辅助工具推荐

1. 论文写作工具测评背景作为一名经历过毕业论文折磨的过来人,我深知专科生在论文写作过程中的痛点。从开题报告到文献综述,从格式排版到查重降重,每个环节都让没有科研经验的同学头疼不已。市面上号称能"一键生成论文"的工具层出不…

2026/9/14 11:34:29

Java SSM博客系统毕业设计实战:环境搭建、调试与高分答辩

简介:这是一套基于Java技术栈的高分毕业设计级博客系统,面向计算机专业本科生及Java初学者,适用于课程设计、期末大作业与毕设参考。系统采用SSM(SpringSpringMVCMyBatis)框架开发,后端以Java编写&#xff…

2026/9/14 2:17:50

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/14 0:03:22

KCF目标跟踪算法与OTB工程实现:毕业设计实战解析

简介:这是一份基于KCF核相关滤波算法、融合尺度池与抗遮挡处理的目标检测跟踪MATLAB完整源码,主要面向计算机相关专业准备毕业设计、课程设计或期末大作业的学生,也适合需要项目实战练习的初学者。源码在OTB数据集上完成验证,能够…

2026/9/14 0:03:22

语音情感识别实战:Keras实现LSTM、CNN、SVM与MLP多模型对比

简介:面向语音情感识别入门与进阶开发者,这份基于Keras的项目源码完整实现了LSTM、CNN、SVM、MLP四种模型,兼容Python3.8与Keras/TensorFlow2环境。压缩包内含49个文件,大小约70.31MB,主体包括Python脚本、yaml/json配…

2026/9/12 6:29:36

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/12 14:32:17

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/14 11:22:57

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码