diff --git a/chartspy/charts.py b/chartspy/charts.py
index a05cc40..dedb992 100644
--- a/chartspy/charts.py
+++ b/chartspy/charts.py
@@ -248,7 +248,7 @@ def json_type_convert(o: object):
return json_encoder.default(o)
-ECHARTS_JS_URL = "https://cdn.staticfile.org/echarts/5.3.2/echarts.min.js"
+ECHARTS_JS_URL = "https://cdn.staticfile.org/echarts/5.4.3/echarts.min.js"
ECHARTS_GL_JS_URL = "https://cdn.staticfile.org/echarts-gl/2.0.8/echarts-gl.min.js"
@@ -370,16 +370,19 @@ def timeline(echarts_dict: dict = {}, visual_map_options=None):
return Echarts(options=options)
- def overlap_series(self, other_chart_options: list = [], add_yaxis=False, add_yaxis_grid_index=0):
+ def overlap_series(self, other_chart_options: list = [], add_yaxis=False, add_yaxis_grid_index=0) -> "Echarts":
"""
叠加其他配置中的Series数据到现有配置,现有配置有多个坐标轴的,建议Series声明对应的axisIndex
- :param other_chart_options:要叠加的Echarts对象列表,或者options列表
+
+ :param other_chart_options: 要叠加的Echarts对象列表,或者options列表
:param add_yaxis: 是否增加一个Y轴
:param add_yaxis_grid_index: 0
:return:
"""
this_options = copy.deepcopy(self.options)
if add_yaxis:
+ if type(this_options['yAxis']).__name__=='dict':
+ this_options['yAxis'] = [this_options['yAxis']]
this_options['yAxis'].append({'scale': True, 'type': 'value', 'gridIndex': add_yaxis_grid_index})
if this_options["legend"]["data"] is None:
this_options["legend"]["data"] = []
@@ -406,9 +409,28 @@ def overlap_series(self, other_chart_options: list = [], add_yaxis=False, add_ya
this_options["visualMap"].extend(chart_option["visualMap"])
return Echarts(options=this_options, extra_js=self.extra_js, width=self.width, height=self.height)
+ def update_options(self, options: dict) -> "Echarts":
+ """
+ 更新options
+
+ :param options: 要更新的options,相同key进行递归覆盖
+ :return: 更新options后的Echarts
+ """
+ def _update_dict(a: dict, b: dict) -> dict:
+ for k, v in b.items():
+ if k in a.keys() and isinstance(a[k], dict) and isinstance(v, dict):
+ a[k] = _update_dict(a[k], v)
+ else:
+ a[k] = v
+ return a
+
+ self.options = _update_dict(self.options, options)
+ return self
+
def print_options(self, drop_data=False):
"""
格式化打印options 方便二次修改
+
:param drop_data: 是否过滤掉data,减小打印长度,方便粘贴
:return:
"""
@@ -421,7 +443,8 @@ def print_options(self, drop_data=False):
def dump_options(self):
"""
- 导出 js option字符串表示
+ 导出 js option字符串表示
+
:return:
"""
self.js_options = Tools.convert_dict_to_js(self.options)
@@ -430,6 +453,7 @@ def dump_options(self):
def render_notebook(self) -> Html:
"""
在jupyter notebook 环境输出
+
:return:
"""
self.js_options = Tools.convert_dict_to_js(self.options)
@@ -438,8 +462,8 @@ def render_notebook(self) -> Html:
html = f"""
@@ -487,6 +511,7 @@ def render_notebook(self) -> Html:
def render_jupyterlab(self) -> Html:
"""
在jupyterlab 环境输出
+
:return:
"""
self.js_options = Tools.convert_dict_to_js(self.options)
@@ -549,6 +574,7 @@ def render_jupyterlab(self) -> Html:
def render_html(self) -> str:
"""
渲染html字符串,可以用于 streamlit
+
:return:
"""
self.js_options = Tools.convert_dict_to_js(self.options)
@@ -588,8 +614,8 @@ def render_html(self) -> str:
@@ -609,6 +635,7 @@ def render_html(self) -> str:
def render_html_fragment(self):
"""
渲染html 片段,方便一个网页输出多个图表
+
:return:
"""
self.js_options = Tools.convert_dict_to_js(self.options)
@@ -656,6 +683,7 @@ def render_html_fragment(self):
def _repr_html_(self):
"""
jupyter 环境,直接输出
+
:return:
"""
self.js_options = Tools.convert_dict_to_js(self.options)
@@ -1279,11 +1307,12 @@ def __init__(self, df: pd.DataFrame, mas=[5, 10, 30, 60, 120, 250], main_indicat
height: str = "500px"):
"""
k线图
+
:param df: [open,high,low,close,volume,turnover,timestamp]
:param mas: [5, 10, 30, 60, 120, 250]
:param main_indicators: 主图显示的指标列表 MA,EMA,SMA,BOLL,SAR,BBI
- :param bottom_indicators:副图显示指标列表 VOL,MACD,KDJ,RSI,BIAS,BBAR,CCI,DMI,CR,PSY,DMA,TRIX,OBV,VR,WR,MTM,EMV,SAR,SMA,ROC,PVT,BBI,AO
- :param df_segments:[start_time,start_price,end_time,end_price]
+ :param bottom_indicators: 副图显示指标列表 VOL,MACD,KDJ,RSI,BIAS,BBAR,CCI,DMI,CR,PSY,DMA,TRIX,OBV,VR,WR,MTM,EMV,SAR,SMA,ROC,PVT,BBI,AO
+ :param df_segments: [start_time,start_price,end_time,end_price]
:param extra_js:
:param width:
:param height:
@@ -1314,6 +1343,7 @@ def __init__(self, df: pd.DataFrame, mas=[5, 10, 30, 60, 120, 250], main_indicat
def render_notebook(self) -> Html:
"""
在jupyter notebook 环境输出
+
:return:
"""
plot = self
@@ -1347,6 +1377,7 @@ def render_notebook(self) -> Html:
def render_jupyterlab(self) -> Html:
"""
在jupyterlab 环境输出
+
:return:
"""
plot = self
@@ -1378,6 +1409,7 @@ def render_jupyterlab(self) -> Html:
def render_html(self) -> str:
"""
渲染html字符串,可以用于 streamlit
+
:return:
"""
plot = self
@@ -1410,6 +1442,7 @@ def render_html(self) -> str:
def render_html_fragment(self):
"""
渲染html 片段,方便一个网页输出多个图表
+
:return:
"""
plot = self
@@ -1434,6 +1467,7 @@ def render_html_fragment(self):
def _repr_html_(self):
"""
jupyter 环境,直接输出
+
:return:
"""
plot = self
diff --git a/chartspy/echarts.py b/chartspy/echarts.py
index 552c4c9..45149e7 100644
--- a/chartspy/echarts.py
+++ b/chartspy/echarts.py
@@ -127,10 +127,11 @@ def timeline(echarts_dict: dict = {}, visual_map_options=None):
return Echarts(options=options)
- def overlap_series(self, other_chart_options: list = [], add_yaxis=False, add_yaxis_grid_index=0):
+ def overlap_series(self, other_chart_options: list = [], add_yaxis=False, add_yaxis_grid_index=0) -> "Echarts":
"""
叠加其他配置中的Series数据到现有配置,现有配置有多个坐标轴的,建议Series声明对应的axisIndex
- :param other_chart_options:要叠加的Echarts对象列表,或者options列表
+
+ :param other_chart_options: 要叠加的Echarts对象列表,或者options列表
:param add_yaxis: 是否增加一个Y轴
:param add_yaxis_grid_index: 0
:return:
@@ -165,9 +166,28 @@ def overlap_series(self, other_chart_options: list = [], add_yaxis=False, add_ya
this_options["visualMap"].extend(chart_option["visualMap"])
return Echarts(options=this_options, extra_js=self.extra_js, width=self.width, height=self.height)
+ def update_options(self, options: dict) -> "Echarts":
+ """
+ 更新options
+
+ :param options: 要更新的options,相同key进行递归覆盖
+ :return: 更新options后的Echarts
+ """
+ def _update_dict(a: dict, b: dict) -> dict:
+ for k, v in b.items():
+ if k in a.keys() and isinstance(a[k], dict) and isinstance(v, dict):
+ a[k] = _update_dict(a[k], v)
+ else:
+ a[k] = v
+ return a
+
+ self.options = _update_dict(self.options, options)
+ return self
+
def print_options(self, drop_data=False):
"""
格式化打印options 方便二次修改
+
:param drop_data: 是否过滤掉data,减小打印长度,方便粘贴
:return:
"""
@@ -180,7 +200,8 @@ def print_options(self, drop_data=False):
def dump_options(self):
"""
- 导出 js option字符串表示
+ 导出 js option字符串表示
+
:return:
"""
self.js_options = Tools.convert_dict_to_js(self.options)
@@ -189,6 +210,7 @@ def dump_options(self):
def render_notebook(self) -> Html:
"""
在jupyter notebook 环境输出
+
:return:
"""
self.js_options = Tools.convert_dict_to_js(self.options)
@@ -246,6 +268,7 @@ def render_notebook(self) -> Html:
def render_jupyterlab(self) -> Html:
"""
在jupyterlab 环境输出
+
:return:
"""
self.js_options = Tools.convert_dict_to_js(self.options)
@@ -308,6 +331,7 @@ def render_jupyterlab(self) -> Html:
def render_html(self) -> str:
"""
渲染html字符串,可以用于 streamlit
+
:return:
"""
self.js_options = Tools.convert_dict_to_js(self.options)
@@ -368,6 +392,7 @@ def render_html(self) -> str:
def render_html_fragment(self):
"""
渲染html 片段,方便一个网页输出多个图表
+
:return:
"""
self.js_options = Tools.convert_dict_to_js(self.options)
@@ -415,6 +440,7 @@ def render_html_fragment(self):
def _repr_html_(self):
"""
jupyter 环境,直接输出
+
:return:
"""
self.js_options = Tools.convert_dict_to_js(self.options)
diff --git a/chartspy/express/echarts.py b/chartspy/express/echarts.py
index 782a6e3..15ae7be 100644
--- a/chartspy/express/echarts.py
+++ b/chartspy/express/echarts.py
@@ -146,7 +146,6 @@ def scatter_echarts(data_frame: pd.DataFrame, x_field: str = None, y_field: str
"""
scatter chart
-
:param data_frame: 必填 DataFrame
:param x_field: 必填 x轴映射的列
:param y_field: 必填 y轴映射的列
@@ -326,6 +325,7 @@ def line_echarts(data_frame: pd.DataFrame, x_field: str = None, y_field: str = N
width: str = "100%", height: str = "500px",**kwargs) -> Echarts:
"""
绘制线图
+
:param data_frame: 必填 DataFrame
:param x_field: 必填 x轴映射的列
:param y_field: 必填 y轴映射的列
@@ -383,12 +383,12 @@ def bar_echarts(data_frame: pd.DataFrame, x_field: str = None, y_field: str = No
:param x_field: 必填 x轴映射的列
:param y_field: 必填 y轴映射的列
:param series_field: 选填 series 对应列
- :param stack:堆叠分组
+ :param stack: 堆叠分组
:param tooltip_trigger: axis item
:param title: 可选标题
:param width: 输出div的宽度 支持像素和百分比 比如800px/100%
:param height: 输出div的高度 支持像素和百分比 比如800px/100%
- :return:Echarts
+ :return: Echarts
"""
options = copy.deepcopy(ECHARTS_BASE_GRID_OPTIONS)
df = data_frame.copy()
@@ -482,6 +482,7 @@ def pie_echarts(data_frame: pd.DataFrame, name_field: str = None, value_field: s
width: str = "100%", height: str = "500px",**kwargs) -> Echarts:
"""
饼图
+
:param data_frame: 必填 DataFrame
:param name_field: name列名
:param value_field: value列名
@@ -555,6 +556,7 @@ def candlestick_echarts(data_frame: pd.DataFrame, time_field: str = 'time', open
right_padding: str = '3%',text_color:str='#000',**kwargs) -> Echarts:
"""
绘制K线
+
:param data_frame:
:param time_field: 时间列名, 如果指定的列不存在,使用index作为time
:param open_field: open列名
@@ -993,6 +995,7 @@ def calendar_heatmap_echarts(data_frame: pd.DataFrame, date_field: str = None, v
width: str = "100%", height: str = "300px",**kwargs) -> Echarts:
"""
日历热度图,显示日期热度
+
:param data_frame:
:param date_field: 日期列
:param value_field: 值列
@@ -1078,6 +1081,7 @@ def parallel_echarts(data_frame: pd.DataFrame, name_field: str = None, indicator
width: str = "100%", height: str = "500px",**kwargs) -> Echarts:
"""
平行坐标图,要求name列每行唯一 比如:显示每个报告期各财务指标
+
:param data_frame:
:param name_field: name列
:param indicator_field_list: 数据维度列list
@@ -1172,7 +1176,6 @@ def sankey_echarts(data_frame: pd.DataFrame, source_field: str = None, target_fi
width: str = "100%", height: str = "500px",**kwargs) -> Echarts:
"""
-
:param data_frame:
:param source_field: source列
:param target_field: target列
@@ -1369,6 +1372,7 @@ def mark_area_echarts(data_frame: pd.DataFrame, x1: str, y1: str, x2: str, y2: s
):
"""
在现有图表上叠加矩形,不能单独显示
+
:param data_frame:
:param x1: 左上方顶点x坐标对应列
:param y1: 左上方顶点y坐标对应列
@@ -1376,7 +1380,7 @@ def mark_area_echarts(data_frame: pd.DataFrame, x1: str, y1: str, x2: str, y2: s
:param y2: 右下方顶点y坐标对应列
:param label: 矩形标签文字对应列
:param title: 用于在legend显示,控制叠加矩形显示隐藏
- :param label_position:top / left / right / bottom / inside / insideLeft / insideRight / insideTop / insideBottom / insideTopLeft / insideBottomLeft / insideTopRight / insideBottomRight
+ :param label_position: top / left / right / bottom / inside / insideLeft / insideRight / insideTop / insideBottom / insideTopLeft / insideBottomLeft / insideTopRight / insideBottomRight
:param label_distance:
:param label_font_size:
:param label_font_color:
@@ -1419,12 +1423,13 @@ def mark_background_echarts(data_frame: pd.DataFrame, x1: str, x2: str, label: s
):
"""
在现有图表上叠加背景,不能单独显示
+
:param data_frame:
:param x1: 左上方顶点x坐标对应列
:param x2: 右下方顶点x坐标对应列
:param label: 矩形标签文字对应列
:param title: 用于在legend显示,控制叠加矩形显示隐藏
- :param label_position:top / left / right / bottom / inside / insideLeft / insideRight / insideTop / insideBottom / insideTopLeft / insideBottomLeft / insideTopRight / insideBottomRight
+ :param label_position: top / left / right / bottom / inside / insideLeft / insideRight / insideTop / insideBottom / insideTopLeft / insideBottomLeft / insideTopRight / insideBottomRight
:param label_distance:
:param label_font_size:
:param label_font_color:
@@ -1479,7 +1484,7 @@ def mark_segment_echarts(data_frame: pd.DataFrame, x1: str, y1: str, x2: str, y2
:param label: 矩形标签文字对应列
:param title: 用于在legend显示,控制叠加矩形显示隐藏
:param show_label: 是否显示label
- :param label_position:top / left / right / bottom / inside / insideLeft / insideRight / insideTop / insideBottom / insideTopLeft / insideBottomLeft / insideTopRight / insideBottomRight
+ :param label_position: top / left / right / bottom / inside / insideLeft / insideRight / insideTop / insideBottom / insideTopLeft / insideBottomLeft / insideTopRight / insideBottomRight
:param label_distance: 10
:param label_font_color:inherit
:param label_font_size:12
@@ -1527,16 +1532,17 @@ def mark_label_echarts(data_frame: pd.DataFrame, x: str, y: str, label: str, tit
label_font_color: str = 'inherit', label_background_color: str = "transparent",**kwargs):
"""
在现有图表上叠加标签,不能单独显示
+
:param data_frame:
:param x: 左上方顶点x坐标对应列
:param y: 左上方顶点y坐标对应列
:param label: 矩形标签文字对应列
:param title: 用于在legend显示,控制叠加矩形显示隐藏
- :param label_position:top / left / right / bottom / inside / insideLeft / insideRight / insideTop / insideBottom / insideTopLeft / insideBottomLeft / insideTopRight / insideBottomRight
+ :param label_position: top / left / right / bottom / inside / insideLeft / insideRight / insideTop / insideBottom / insideTopLeft / insideBottomLeft / insideTopRight / insideBottomRight
:param label_distance: 10
- :param label_font_color:inherit
- :param label_font_size:12
- :param label_background_color:transparent
+ :param label_font_color: inherit
+ :param label_font_size: 12
+ :param label_background_color: transparent
:return:
"""
options = copy.deepcopy(ECHARTS_BASE_OVERLAY_OPTIONS)
@@ -1569,6 +1575,7 @@ def mark_vertical_line_echarts(data_frame: pd.DataFrame, x: str, label: str, tit
label_font_color: str = 'inherit',**kwargs):
"""
在现有图表上叠加竖线,不能单独显示
+
:param data_frame:
:param x:
:param label:
@@ -1614,6 +1621,7 @@ def mark_horizontal_line_echarts(data_frame: pd.DataFrame, y: str, label: str, t
label_font_color: str = 'inherit',**kwargs):
"""
在现有图表上叠加横线,不能单独显示
+
:param data_frame:
:param y:
:param label:
@@ -1668,7 +1676,6 @@ def scatter3d_echarts(data_frame: pd.DataFrame, x_field: str = None, y_field: st
"""
3d 气泡图
-
:param data_frame:
:param x_field:
:param y_field:
@@ -1812,6 +1819,7 @@ def bar3d_echarts(data_frame: pd.DataFrame, x_field: str = None, y_field: str =
height: str = "500px",**kwargs):
"""
3d bar
+
:param data_frame:
:param x_field:
:param y_field:
@@ -1916,6 +1924,7 @@ def drawdown_echarts(data_frame: pd.DataFrame, time_field: str, value_field: str
height='500px',**kwargs) -> Echarts:
"""
回撤图
+
:param data_frame: pd.DataFrame
:param time_field: 时间列名
:param value_field: 价格列名
diff --git a/chartspy/klinecharts.py b/chartspy/klinecharts.py
index 320610d..d74d8e4 100644
--- a/chartspy/klinecharts.py
+++ b/chartspy/klinecharts.py
@@ -11,7 +11,7 @@
# language=jinja2
-def kline_chart_segment(plot):
+def kline_chart_segment(plot, inject_js: str=None):
parts = []
parts.append(
f"""var chart_{plot.plot_id} = klinecharts.init("{plot.plot_id}",
@@ -39,6 +39,8 @@ def kline_chart_segment(plot):
chart_{plot.plot_id}.createShape({{name: 'segment',points:[{{timestamp:{seg['start_time']},value:{seg['start_price']}}},{{timestamp:{seg['end_time']},value:{seg['end_price']}}}]}},"candle_pane")
""")
parts.append(f"""chart_{plot.plot_id}.applyNewData(data_{plot.plot_id})""")
+ if inject_js:
+ parts.append(inject_js)
return "\n".join(parts)
@@ -50,20 +52,25 @@ class KlineCharts(object):
def __init__(self, df: pd.DataFrame, mas=[5, 10, 30, 60, 120, 250], main_indicators=["MA"],
bottom_indicators=["VOL", "MACD"], df_segments: pd.DataFrame = None,
extra_js: str = "", width: str = "100%",
- height: str = "500px"):
+ height: str = "500px",
+ inject_klinecharts_js: str = None):
"""
k线图
+
:param df: [open,high,low,close,volume,turnover,timestamp]
:param mas: [5, 10, 30, 60, 120, 250]
:param main_indicators: 主图显示的指标列表 MA,EMA,SMA,BOLL,SAR,BBI
- :param bottom_indicators:副图显示指标列表 VOL,MACD,KDJ,RSI,BIAS,BBAR,CCI,DMI,CR,PSY,DMA,TRIX,OBV,VR,WR,MTM,EMV,SAR,SMA,ROC,PVT,BBI,AO
- :param df_segments:[start_time,start_price,end_time,end_price]
+ :param bottom_indicators: 副图显示指标列表 VOL,MACD,KDJ,RSI,BIAS,BBAR,CCI,DMI,CR,PSY,DMA,TRIX,OBV,VR,WR,MTM,EMV,SAR,SMA,ROC,PVT,BBI,AO
+ :param df_segments: [start_time,start_price,end_time,end_price]
:param extra_js:
:param width:
:param height:
+ :param inject_klinecharts_js: 注入 klinecharts 的定制化js代码\n
+ 以'{chart}'方式引用chart,如 {chart}.createIndicator(...)
"""
data = df.copy()
- data['timestamp'] = (pd.to_datetime(data['timestamp']) - pd.Timedelta(hours=8)).view("i8") // 10 ** 6
+ u = None if data['timestamp'].dtype != int else 'ms' if data['timestamp'].iloc[-1] > 1e10 else 's'
+ data['timestamp'] = (pd.to_datetime(data['timestamp'], unit=u) - pd.Timedelta(hours=8)).view("i8") // 10 ** 6
data = data.sort_values(by=['timestamp'])
if len(mas) > 0 and "MA" not in main_indicators:
main_indicators.append("MA")
@@ -84,10 +91,14 @@ def __init__(self, df: pd.DataFrame, mas=[5, 10, 30, 60, 120, 250], main_indicat
self.plot_id = "u" + uuid.uuid4().hex
self.js_url = KlineCharts_JS_URL
self.extra_js = extra_js
+ self.inject_js = inject_klinecharts_js
+ if self.inject_js is not None:
+ self.inject_js = self.inject_js.replace('{chart}', f'chart_{self.plot_id}')
def render_notebook(self) -> Html:
"""
在jupyter notebook 环境输出
+
:return:
"""
plot = self
@@ -111,7 +122,7 @@ def render_notebook(self) -> Html:
{plot.extra_js}
var data_{plot.plot_id} = {plot.data}
require(['klinecharts'], function (klinecharts) {{
- """ + kline_chart_segment(plot) + f"""
+ """ + kline_chart_segment(plot, self.inject_js) + f"""
}});
@@ -121,6 +132,7 @@ def render_notebook(self) -> Html:
def render_jupyterlab(self) -> Html:
"""
在jupyterlab 环境输出
+
:return:
"""
plot = self
@@ -143,7 +155,7 @@ def render_jupyterlab(self) -> Html:
script.src = "{plot.js_url}";
document.head.appendChild(script);
}}).then(() => {{
- """ + kline_chart_segment(plot) + f"""
+ """ + kline_chart_segment(plot, self.inject_js) + f"""
}});
"""
@@ -152,6 +164,7 @@ def render_jupyterlab(self) -> Html:
def render_html(self) -> str:
"""
渲染html字符串,可以用于 streamlit
+
:return:
"""
plot = self
@@ -173,7 +186,8 @@ def render_html(self) -> str: