如何在Python的Bokeh/Plotly中绘制多轴多水平柱状图(市场轮廓图)
市场轮廓/成交量轮廓图实现方案
需求说明
需要绘制市场轮廓/成交量轮廓图:
- X轴为时间(
time字段),每个时间点对应一组水平柱状图 - Y轴为价格区间(
bins字段),每个区间对应一根水平柱 - 柱的长度由
amt字段决定,可进一步区分buy_amt和sell_amt的颜色
示例数据及预处理代码:
import pandas as pd data = [ {'time': pd.Timestamp('2023-03-07 23:01:00'), 'bins': pd.Interval(22085.286, 22088.925, closed='right'), 'amt': 8.013, 'buy_amt': 3.05, 'sell_amt': 4.963}, {'time': pd.Timestamp('2023-03-07 23:01:00'), 'bins': pd.Interval(22088.925, 22092.55, closed='right'), 'amt': 11.579, 'buy_amt': 0.106, 'sell_amt': 11.473}, {'time': pd.Timestamp('2023-03-07 23:01:00'), 'bins': pd.Interval(22092.55, 22096.175, closed='right'), 'amt': 0.678, 'buy_amt': 0.002, 'sell_amt': 0.676}, {'time': pd.Timestamp('2023-03-07 23:01:00'), 'bins': pd.Interval(22096.175, 22099.8, closed='right'), 'amt': 5.72300, 'buy_amt': 1.44899, 'sell_amt': 4.274}, {'time': pd.Timestamp('2023-03-07 23:02:00'), 'bins': pd.Interval(22071.686, 22076.3, closed='right'), 'amt': 14.968, 'buy_amt': 3.885, 'sell_amt': 11.08299}, {'time': pd.Timestamp('2023-03-07 23:02:00'), 'bins': pd.Interval(22076.3, 22080.9, closed='right'), 'amt': 0.1440, 'buy_amt': 0.001, 'sell_amt': 0.14300}, {'time': pd.Timestamp('2023-03-07 23:02:00'), 'bins': pd.Interval(22080.9, 22085.5, closed='right'), 'amt': 19.137, 'buy_amt': 4.259, 'sell_amt': 14.878}, {'time': pd.Timestamp('2023-03-07 23:03:00'), 'bins': pd.Interval(22070.691, 22073.8, closed='right'), 'amt': 14.03099, 'buy_amt': 9.532, 'sell_amt': 4.499}, {'time': pd.Timestamp('2023-03-07 23:03:00'), 'bins': pd.Interval(22076.9, 22080.0, closed='right'), 'amt': 5.91, 'buy_amt': 5.91, 'sell_amt': 0.0}, {'time': pd.Timestamp('2023-03-07 23:04:00'), 'bins': pd.Interval(22079.984, 22083.975, closed='right'), 'amt': 0.776, 'buy_amt': 0.68, 'sell_amt': 0.096}, {'time': pd.Timestamp('2023-03-07 23:04:00'), 'bins': pd.Interval(22083.975, 22087.95, closed='right'), 'amt': 6.27199, 'buy_amt': 5.067, 'sell_amt': 1.205}, {'time': pd.Timestamp('2023-03-07 23:04:00'), 'bins': pd.Interval(22087.95, 22091.925, closed='right'), 'amt': 2.156, 'buy_amt': 0.47600, 'sell_amt': 1.6800}, {'time': pd.Timestamp('2023-03-07 23:04:00'), 'bins': pd.Interval(22091.925, 22095.9, closed='right'), 'amt': 7.481, 'buy_amt': 4.755, 'sell_amt': 2.726}, {'time': pd.Timestamp('2023-03-07 23:05:00'), 'bins': pd.Interval(22076.491, 22078.85, closed='right'), 'amt': 26.618, 'buy_amt': 0.3080, 'sell_amt': 26.31}, {'time': pd.Timestamp('2023-03-07 23:05:00'), 'bins': pd.Interval(22081.2, 22083.55, closed='right'), 'amt': 0.196, 'buy_amt': 0.137, 'sell_amt': 0.059}, {'time': pd.Timestamp('2023-03-07 23:05:00'), 'bins': pd.Interval(22083.55, 22085.9, closed='right'), 'amt': 7.582, 'buy_amt': 3.691, 'sell_amt': 3.891} ] df = pd.DataFrame(data) # 预处理:提取区间中点作为Y坐标,给每个时间点分配离散X位置 df['y_val'] = df['bins'].apply(lambda x: (x.left + x.right)/2) time_unique = df['time'].unique() time_map = {t:i for i,t in enumerate(time_unique)} df['x_pos'] = df['time'].map(time_map)
一、Bokeh实现方案
核心思路:将时间映射为X轴离散位置,拆分买卖量做堆叠显示,添加交互hover工具。
from bokeh.plotting import figure, show from bokeh.models import ColumnDataSource, FactorRange, HoverTool source = ColumnDataSource(df) p = figure( width=1200, height=600, x_range=FactorRange(factors=[str(t)[:16] for t in time_unique]), y_axis_label='价格区间', x_axis_label='时间', title='市场轮廓图', tools='pan,wheel_zoom,box_zoom,reset,save' ) # 绘制卖出量 p.hbar( y='y_val', left='x_pos', right='x_pos + sell_amt', height=1.5, color='#1f77b4', source=source, legend_label='卖出量' ) # 绘制买入量(堆叠在卖出量左侧) p.hbar( y='y_val', left='x_pos', right='x_pos + buy_amt', height=1.5, color='#ff4b5c', source=source, legend_label='买入量' ) # 添加hover提示 hover = HoverTool( tooltips=[ ('时间', '@time{%Y-%m-%d %H:%M:%S}'), ('价格区间', '@bins'), ('总成交量', '@amt'), ('买入量', '@buy_amt'), ('卖出量', '@sell_amt') ], formatters={'@time': 'datetime'} ) p.add_tools(hover) # 样式调整 p.legend.location = 'top_right' p.xaxis.major_label_orientation = 1.2 show(p)
二、Plotly实现方案
利用Plotly的水平柱状图和堆叠模式,快速实现可视化并支持交互。
import plotly.express as px import plotly.graph_objects as go df['bin_label'] = df['bins'].apply(str) fig = go.Figure() # 添加买入量痕迹 fig.add_trace(go.Bar( x=df['buy_amt'], y=df['bin_label'], name='买入量', marker_color='#ff4b5c', base=df['x_pos'], hovertemplate='时间: %{customdata[0]}<br>价格区间: %{customdata[1]}<br>买入量: %{x}<br>总成交量: %{customdata[2]}', customdata=df[['time', 'bin_label', 'amt']].values )) # 添加卖出量痕迹 fig.add_trace(go.Bar( x=df['sell_amt'], y=df['bin_label'], name='卖出量', marker_color='#1f77b4', base=df['x_pos'] + df['buy_amt'], hovertemplate='时间: %{customdata[0]}<br>价格区间: %{customdata[1]}<br>卖出量: %{x}<br>总成交量: %{customdata[2]}', customdata=df[['time', 'bin_label', 'amt']].values )) # 调整轴标签和布局 fig.update_xaxes( tickvals=list(time_map.values()), ticktext=[str(t)[:16] for t in time_unique], title='时间' ) fig.update_yaxes(title='价格区间') fig.update_layout( width=1200, height=600, barmode='stack', title='市场轮廓图', legend=dict(orientation='h', yanchor='bottom', y=1.02, xanchor='right', x=1) ) fig.show()
关键优化点
- 时间轴处理:将时间转换为离散X位置,避免datetime轴与成交量长度的逻辑冲突
- 堆叠显示:拆分买卖量分别绘制,清晰展示多空分布
- 交互增强:添加hover工具显示完整数据维度,提升图表可读性
内容的提问来源于stack exchange,提问作者Dimasik
相关产品推荐
相关产品推荐

