# coding: utf-8
"""
使用 pyecharts 定制绘图模块
"""
from pyecharts import options as opts
from pyecharts.charts import HeatMap, Kline, Line, Bar, Scatter, Grid, Boxplot
from pyecharts.commons.utils import JsCode
from typing import List
import numpy as np
from czsc.objects import Operate
from .ta import SMA, MACD
[docs]def heat_map(data: List[dict],
x_label: List[str] = None,
y_label: List[str] = None,
title: str = "热力图",
width: str = "900px",
height: str = "680px") -> HeatMap:
"""绘制热力图
:param data: 用于绘制热力图的数据,示例如下
[{'x': '0hour', 'y': '0day', 'heat': 11},
{'x': '0hour', 'y': '1day', 'heat': 40},
{'x': '0hour', 'y': '2day', 'heat': 38},
{'x': '0hour', 'y': '3day', 'heat': 36},
{'x': '0hour', 'y': '4day', 'heat': 11}]
:param x_label: x轴标签
:param y_label: y轴标签
:param title: 图表标题
:param width: 图表宽度
:param height: 图表高度
:return: 图表
"""
value = [[s['x'], s['y'], s['heat']] for s in data]
heat = [s['heat'] for s in data]
if not x_label:
x_label = sorted(list(set([s['x'] for s in data])))
if not y_label:
y_label = sorted(list(set([s['y'] for s in data])))
vis_map_opts = opts.VisualMapOpts(pos_left="90%", pos_top="20%", min_=min(heat), max_=max(heat))
title_opts = opts.TitleOpts(title=title)
init_opts = opts.InitOpts(page_title=title, width=width, height=height)
dz_inside = opts.DataZoomOpts(False, "inside", xaxis_index=[0], range_start=80, range_end=100)
dz_slider = opts.DataZoomOpts(True, "slider", xaxis_index=[0], pos_top="96%", pos_bottom="0%",
range_start=80, range_end=100)
legend_opts = opts.LegendOpts(is_show=False)
hm = HeatMap(init_opts=init_opts)
hm.add_xaxis(x_label)
hm.add_yaxis("heat", y_label, value, label_opts=opts.LabelOpts(is_show=True, position="inside"))
hm.set_global_opts(title_opts=title_opts, visualmap_opts=vis_map_opts, legend_opts=legend_opts,
xaxis_opts=opts.AxisOpts(grid_index=0), datazoom_opts=[dz_inside, dz_slider])
return hm
[docs]def kline_pro(kline: List[dict],
fx: List[dict] = [],
bi: List[dict] = [],
xd: List[dict] = [],
bs: List[dict] = [],
title: str = "缠中说禅K线分析",
t_seq: List[int] = [],
width: str = "1400px",
height: str = '580px') -> Grid:
"""绘制缠中说禅K线分析结果
:param kline: K线
:param fx: 分型识别结果
:param bi: 笔识别结果
{'dt': Timestamp('2020-11-26 00:00:00'),
'fx_mark': 'd',
'start_dt': Timestamp('2020-11-25 00:00:00'),
'end_dt': Timestamp('2020-11-27 00:00:00'),
'fx_high': 144.87,
'fx_low': 138.0,
'bi': 138.0}
:param xd: 线段识别结果
:param bs: 买卖点
:param title: 图表标题
:param t_seq: 均线系统
:param width: 图表宽度
:param height: 图表高度
:return: 用Grid组合好的图表
"""
# 配置项设置
# ------------------------------------------------------------------------------------------------------------------
bg_color = "#1f212d" # 背景
up_color = "#F9293E"
down_color = "#00aa3b"
init_opts = opts.InitOpts(bg_color=bg_color, width=width, height=height, animation_opts=opts.AnimationOpts(False))
title_opts = opts.TitleOpts(title=title, pos_top="1%",
title_textstyle_opts=opts.TextStyleOpts(color=up_color, font_size=20),
subtitle_textstyle_opts=opts.TextStyleOpts(color=down_color, font_size=12))
label_show_opts = opts.LabelOpts(is_show=True)
label_not_show_opts = opts.LabelOpts(is_show=False)
legend_not_show_opts = opts.LegendOpts(is_show=False)
red_item_style = opts.ItemStyleOpts(color=up_color)
green_item_style = opts.ItemStyleOpts(color=down_color)
k_style_opts = opts.ItemStyleOpts(color=up_color, color0=down_color, border_color=up_color,
border_color0=down_color, opacity=0.8)
legend_opts = opts.LegendOpts(is_show=True, pos_top="1%", pos_left="30%", item_width=14, item_height=8,
textstyle_opts=opts.TextStyleOpts(font_size=12, color="#0e99e2"))
brush_opts = opts.BrushOpts(tool_box=["rect", "polygon", "keep", "clear"],
x_axis_index="all", brush_link="all",
out_of_brush={"colorAlpha": 0.1}, brush_type="lineX")
axis_pointer_opts = opts.AxisPointerOpts(is_show=True, link=[{"xAxisIndex": "all"}])
dz_inside = opts.DataZoomOpts(False, "inside", xaxis_index=[0, 1, 2], range_start=80, range_end=100)
dz_slider = opts.DataZoomOpts(True, "slider", xaxis_index=[0, 1, 2], pos_top="96%",
pos_bottom="0%", range_start=80, range_end=100)
yaxis_opts = opts.AxisOpts(is_scale=True, min_="dataMin", max_="dataMax",
splitline_opts=opts.SplitLineOpts(is_show=False),
axislabel_opts=opts.LabelOpts(color="#c7c7c7", font_size=8, position="inside"))
grid0_xaxis_opts = opts.AxisOpts(type_="category", grid_index=0, axislabel_opts=label_not_show_opts,
split_number=20, min_="dataMin", max_="dataMax",
is_scale=True, boundary_gap=False,
splitline_opts=opts.SplitLineOpts(is_show=False),
axisline_opts=opts.AxisLineOpts(is_on_zero=False))
tool_tip_opts = opts.TooltipOpts(
trigger="axis",
axis_pointer_type="cross",
background_color="rgba(245, 245, 245, 0.8)",
border_width=1,
border_color="#ccc",
position=JsCode("""
function (pos, params, el, elRect, size) {
var obj = {top: 10};
obj[['left', 'right'][+(pos[0] < size.viewSize[0] / 2)]] = 30;
return obj;
}
"""),
textstyle_opts=opts.TextStyleOpts(color="#000"),
)
# 数据预处理
# ------------------------------------------------------------------------------------------------------------------
dts = [x['dt'] for x in kline]
# k_data = [[x['open'], x['close'], x['low'], x['high']] for x in kline]
k_data = [opts.CandleStickItem(name=i, value=[x['open'], x['close'], x['low'], x['high']])
for i, x in enumerate(kline)]
vol = []
for i, row in enumerate(kline):
item_style = red_item_style if row['close'] > row['open'] else green_item_style
bar = opts.BarItem(name=i, value=row['vol'], itemstyle_opts=item_style, label_opts=label_not_show_opts)
vol.append(bar)
close = np.array([x['close'] for x in kline], dtype=np.double)
diff, dea, macd = MACD(close)
macd_bar = []
for i, v in enumerate(macd.tolist()):
item_style = red_item_style if v > 0 else green_item_style
bar = opts.BarItem(name=i, value=round(v, 4), itemstyle_opts=item_style,
label_opts=label_not_show_opts)
macd_bar.append(bar)
diff = diff.round(4)
dea = dea.round(4)
# K 线主图
# ------------------------------------------------------------------------------------------------------------------
chart_k = Kline()
chart_k.add_xaxis(xaxis_data=dts)
chart_k.add_yaxis(series_name="Kline", y_axis=k_data, itemstyle_opts=k_style_opts)
chart_k.set_global_opts(
legend_opts=legend_opts,
datazoom_opts=[dz_inside, dz_slider],
yaxis_opts=yaxis_opts,
tooltip_opts=tool_tip_opts,
axispointer_opts=axis_pointer_opts,
brush_opts=brush_opts,
title_opts=title_opts,
xaxis_opts=grid0_xaxis_opts
)
# 加入买卖点 - 多头操作 - 空头操作
if bs:
long_opens = {'i': [], 'val': []}
long_exits = {'i': [], 'val': []}
short_opens = {'i': [], 'val': []}
short_exits = {'i': [], 'val': []}
for op in bs:
_dt = op['dt']
_price = round(op['price'], 4)
_info = f"{op['op_desc']} - 价格{_price}"
if op['op'] in [Operate.LO]:
long_opens['i'].append(_dt)
long_opens['val'].append([_price, _info])
if op['op'] in [Operate.LE]:
long_exits['i'].append(_dt)
long_exits['val'].append([_price, _info])
if op['op'] in [Operate.SO]:
short_opens['i'].append(_dt)
short_opens['val'].append([_price, _info])
if op['op'] in [Operate.SE]:
short_exits['i'].append(_dt)
short_exits['val'].append([_price, _info])
chart_lo = (
Scatter().add_xaxis(xaxis_data=long_opens['i']).add_yaxis(
series_name="多头操作",
y_axis=long_opens['val'],
symbol_size=25,
symbol='diamond',
label_opts=opts.LabelOpts(is_show=False),
itemstyle_opts=opts.ItemStyleOpts(color='#ff461f'),
tooltip_opts=opts.TooltipOpts(
textstyle_opts=opts.TextStyleOpts(font_size=12),
formatter=JsCode("function (params) {return params.value[2];}")
),
)
)
chart_le = (
Scatter().add_xaxis(xaxis_data=long_exits['i']).add_yaxis(
series_name="多头操作",
y_axis=long_exits['val'],
symbol_size=25,
symbol='diamond',
label_opts=opts.LabelOpts(is_show=False),
itemstyle_opts=opts.ItemStyleOpts(color='#afdd22'),
tooltip_opts=opts.TooltipOpts(
textstyle_opts=opts.TextStyleOpts(font_size=12),
formatter=JsCode("function (params) {return params.value[2];}")
),
)
)
chart_so = (
Scatter().add_xaxis(xaxis_data=short_opens['i']).add_yaxis(
series_name="空头订单",
y_axis=short_opens['val'],
symbol_size=25,
symbol='triangle',
label_opts=opts.LabelOpts(is_show=False),
itemstyle_opts=opts.ItemStyleOpts(color='#ff461f'),
tooltip_opts=opts.TooltipOpts(
textstyle_opts=opts.TextStyleOpts(font_size=12),
formatter=JsCode("function (params) {return params.value[2];}")
),
)
)
chart_se = (
Scatter().add_xaxis(xaxis_data=short_exits['i']).add_yaxis(
series_name="空头订单",
y_axis=short_exits['val'],
symbol_size=25,
symbol='triangle',
label_opts=opts.LabelOpts(is_show=False),
itemstyle_opts=opts.ItemStyleOpts(color='#afdd22'),
tooltip_opts=opts.TooltipOpts(
textstyle_opts=opts.TextStyleOpts(font_size=12),
formatter=JsCode("function (params) {return params.value[2];}")
),
)
)
chart_k = chart_k.overlap(chart_lo)
chart_k = chart_k.overlap(chart_le)
chart_k = chart_k.overlap(chart_so)
chart_k = chart_k.overlap(chart_se)
# 均线图
# ------------------------------------------------------------------------------------------------------------------
chart_ma = Line()
chart_ma.add_xaxis(xaxis_data=dts)
if not t_seq:
t_seq = [5, 13, 21]
ma_keys = dict()
for t in t_seq:
ma_keys[f"MA{t}"] = SMA(close, timeperiod=t)
for i, (name, ma) in enumerate(ma_keys.items()):
chart_ma.add_yaxis(series_name=name, y_axis=ma, is_smooth=True,
symbol_size=0, label_opts=label_not_show_opts,
linestyle_opts=opts.LineStyleOpts(opacity=0.8, width=1))
chart_ma.set_global_opts(xaxis_opts=grid0_xaxis_opts, legend_opts=legend_not_show_opts)
chart_k = chart_k.overlap(chart_ma)
# 缠论结果
# ------------------------------------------------------------------------------------------------------------------
if fx:
fx_dts = [x['dt'] for x in fx]
fx_val = [round(x['fx'], 2) for x in fx]
chart_fx = Line()
chart_fx.add_xaxis(fx_dts)
chart_fx.add_yaxis(series_name="FX", y_axis=fx_val,
symbol="circle", symbol_size=6, label_opts=label_show_opts,
itemstyle_opts=opts.ItemStyleOpts(color="rgba(152, 147, 193, 1.0)", ))
chart_fx.set_global_opts(xaxis_opts=grid0_xaxis_opts, legend_opts=legend_not_show_opts)
chart_k = chart_k.overlap(chart_fx)
if bi:
bi_dts = [x['dt'] for x in bi]
bi_val = [round(x['bi'], 2) for x in bi]
chart_bi = Line()
chart_bi.add_xaxis(bi_dts)
chart_bi.add_yaxis(series_name="BI", y_axis=bi_val,
symbol="diamond", symbol_size=10, label_opts=label_show_opts,
itemstyle_opts=opts.ItemStyleOpts(color="rgba(184, 117, 225, 1.0)", ),
linestyle_opts=opts.LineStyleOpts(width=1.5))
chart_bi.set_global_opts(xaxis_opts=grid0_xaxis_opts, legend_opts=legend_not_show_opts)
chart_k = chart_k.overlap(chart_bi)
if xd:
xd_dts = [x['dt'] for x in xd]
xd_val = [x['xd'] for x in xd]
chart_xd = Line()
chart_xd.add_xaxis(xd_dts)
chart_xd.add_yaxis(series_name="XD", y_axis=xd_val,
symbol="triangle", symbol_size=10,
itemstyle_opts=opts.ItemStyleOpts(color="rgba(37, 141, 54, 1.0)", ))
chart_xd.set_global_opts(xaxis_opts=grid0_xaxis_opts, legend_opts=legend_not_show_opts)
chart_k = chart_k.overlap(chart_xd)
# 成交量图
# ------------------------------------------------------------------------------------------------------------------
chart_vol = Bar()
chart_vol.add_xaxis(dts)
chart_vol.add_yaxis(series_name="Volume", y_axis=vol, bar_width='60%')
chart_vol.set_global_opts(
xaxis_opts=opts.AxisOpts(
type_="category",
grid_index=1,
boundary_gap=False,
axislabel_opts=opts.LabelOpts(is_show=True, font_size=8, color="#9b9da9"),
),
yaxis_opts=yaxis_opts, legend_opts=legend_not_show_opts,
)
# MACD图
# ------------------------------------------------------------------------------------------------------------------
chart_macd = Bar()
chart_macd.add_xaxis(dts)
chart_macd.add_yaxis(series_name="MACD", y_axis=macd_bar, bar_width='60%')
chart_macd.set_global_opts(
xaxis_opts=opts.AxisOpts(
type_="category",
grid_index=2,
axislabel_opts=opts.LabelOpts(is_show=False),
splitline_opts=opts.SplitLineOpts(is_show=False),
),
yaxis_opts=opts.AxisOpts(
grid_index=2,
split_number=4,
axisline_opts=opts.AxisLineOpts(is_on_zero=False),
axistick_opts=opts.AxisTickOpts(is_show=False),
splitline_opts=opts.SplitLineOpts(is_show=False),
axislabel_opts=opts.LabelOpts(is_show=True, color="#c7c7c7"),
),
legend_opts=opts.LegendOpts(is_show=False),
)
line = Line()
line.add_xaxis(dts)
line.add_yaxis(series_name="DIFF", y_axis=diff, label_opts=label_not_show_opts, is_symbol_show=False,
linestyle_opts=opts.LineStyleOpts(opacity=0.8, width=1.0, color="#da6ee8"))
line.add_yaxis(series_name="DEA", y_axis=dea, label_opts=label_not_show_opts, is_symbol_show=False,
linestyle_opts=opts.LineStyleOpts(opacity=0.8, width=1.0, color="#39afe6"))
chart_macd = chart_macd.overlap(line)
grid0_opts = opts.GridOpts(pos_left="0%", pos_right="1%", pos_top="12%", height="58%")
grid1_opts = opts.GridOpts(pos_left="0%", pos_right="1%", pos_top="74%", height="8%")
grid2_opts = opts.GridOpts(pos_left="0%", pos_right="1%", pos_top="86%", height="10%")
grid_chart = Grid(init_opts)
grid_chart.add(chart_k, grid_opts=grid0_opts)
grid_chart.add(chart_vol, grid_opts=grid1_opts)
grid_chart.add(chart_macd, grid_opts=grid2_opts)
return grid_chart
def box_plot(data: dict,
title: str = "箱线图",
width: str = "900px",
height: str = "680px") -> Boxplot:
"""
:param data: 数据
样例:
data = {
"expr 0": [960, 850, 830, 880],
"expr 1": [960, 850, 830, 880],
}
:param title:
:param width:
:param height:
:return:
"""
x_data = []
y_data = []
for k, v in data.items():
x_data.append(k)
y_data.append(v)
init_opts = opts.InitOpts(page_title=title, width=width, height=height)
chart = Boxplot(init_opts=init_opts)
chart.add_xaxis(xaxis_data=x_data)
chart.add_yaxis(series_name="", y_axis=y_data)
chart.set_global_opts(title_opts=opts.TitleOpts(pos_left="center", title=title),
tooltip_opts=opts.TooltipOpts(trigger="item", axis_pointer_type="shadow"),
xaxis_opts=opts.AxisOpts(
type_="category",
boundary_gap=True,
splitarea_opts=opts.SplitAreaOpts(is_show=False),
axislabel_opts=opts.LabelOpts(formatter="{value}"),
splitline_opts=opts.SplitLineOpts(is_show=False),
),
yaxis_opts=opts.AxisOpts(
type_="value",
name="",
splitarea_opts=opts.SplitAreaOpts(
is_show=True, areastyle_opts=opts.AreaStyleOpts(opacity=1)
)
))
return chart