You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 03:02:06