Files
my_wiki/raw/量化/abuquant-src/abupy/WidgetBu/ABuWGTLTool.py
T

369 lines
16 KiB
Python

# -*- encoding:utf-8 -*-
"""量化技术分析工具图形可视化"""
from __future__ import print_function
from __future__ import absolute_import
from __future__ import division
import logging
from contextlib import contextmanager
from IPython.display import display
import ipywidgets as widgets
from ..TLineBu.ABuTL import EShiftDistanceHow, ESkeletonHow
from ..TLineBu.ABuTLExecute import calc_pair_speed
from ..TLineBu.ABuTLJump import calc_jump, calc_jump_line, calc_jump_line_weight
from ..TLineBu.ABuTLGolden import calc_golden
from ..UtilBu import ABuProgress
from ..WidgetBu.ABuWGToolBase import WidgetToolBase, single_fetch_symbol_analyse
__author__ = '阿布'
__weixin__ = 'abu_quant'
# noinspection PyUnusedLocal
class WidgetTLTool(WidgetToolBase):
"""技术分析界面"""
@contextmanager
def _init_tip_label_with_step_x(self, callback_analyse, analyse_name, with_step_x=True):
"""step_x需要的地方比较多,统一构建,外部接收赋予名字"""
if not callable(callback_analyse):
raise TabError('callback_analyse must callable!')
tip_label = widgets.Label(self.map_tip_target_label(n_target=1), layout=self.label_layout)
widget_list = [tip_label]
step_x = None
if with_step_x:
step_x_label = widgets.Label(u'时间步长控制参数step_x,默认1.0',
layout=self.label_layout)
step_x = widgets.FloatSlider(
value=1.0,
min=0.1,
max=2.6,
step=0.1,
description=u'步长',
disabled=False,
orientation='horizontal',
readout=True,
readout_format='.1f',
)
# 返回给需要的ui,命名独有的step_x
yield widget_list, step_x
if with_step_x:
# noinspection PyUnboundLocalVariable
step_x_box = widgets.VBox([step_x_label, step_x])
# noinspection PyTypeChecker
widget_list.append(step_x_box)
analyse_bt = widgets.Button(description=analyse_name, layout=widgets.Layout(width='98%'),
button_style='info')
analyse_bt.on_click(callback_analyse)
widget_list.append(analyse_bt)
def init_rs_ui(self):
"""阻力支撑分析ui"""
with self._init_tip_label_with_step_x(
self._rs_line_analyse, u'支撑阻力线分析', with_step_x=False) as (widget_list, _):
self.rs_mode = widgets.RadioButtons(
options={u'只分析支撑线': 0, u'只分析阻力线': 1, u'支撑线和阻力线': 2},
value=0,
description=u'分析模式:',
disabled=False
)
widget_list.append(self.rs_mode)
self.only_last = widgets.RadioButtons(
options={u'最近的阻力线和支撑线': True, u'所有的阻力线和支撑线': False},
value=True,
description=u'最近的阻力线和支撑线',
disabled=False
)
widget_list.append(self.only_last)
return widgets.VBox(widget_list, # border='solid 1px',
layout=self.tool_layout)
def init_jump_ui(self):
"""跳空分析ui"""
with self._init_tip_label_with_step_x(
self._jump_line_analyse, u'跳空技术分析', with_step_x=False) as (widget_list, _):
self.jump_mode = widgets.RadioButtons(
options={u'跳空统计分析': 0, u'跳空缺口筛选': 1, u'缺口时间加权筛选': 2},
value=0,
description=u'分析模式:',
disabled=False
)
widget_list.append(self.jump_mode)
power_threshold_label = widgets.Label(u'缺口能量阀值,默认2.0(只对缺口筛选生效)',
layout=self.label_layout)
self.power_threshold = widgets.FloatSlider(
value=2.0,
min=1.5,
max=3.5,
step=0.1,
description=u'能量',
disabled=False,
orientation='horizontal',
readout=True,
readout_format='.1f',
)
power_threshold_box = widgets.VBox([power_threshold_label, self.power_threshold])
widget_list.append(power_threshold_box)
jump_diff_factor_label = widgets.Label(u'设置调节跳空阀值的大小',
layout=self.label_layout)
self.jump_diff_factor = widgets.FloatSlider(
value=1.0,
min=0.1,
max=5.0,
step=0.1,
description=u'阀值',
disabled=False,
orientation='horizontal',
readout=True,
readout_format='.1f',
)
jump_diff_factor_box = widgets.VBox([jump_diff_factor_label, self.jump_diff_factor])
widget_list.append(jump_diff_factor_box)
return widgets.VBox(widget_list,
# border='solid 1px',
layout=self.tool_layout)
def init_shift_distance_ui(self):
"""位移路程比ui"""
with self._init_tip_label_with_step_x(
self._shift_distance_analyse, u'位移路程分析') as (widget_list, step_x):
self.shift_distance_step_x = step_x
self.shift_distance_mode = widgets.RadioButtons(
options={u'序列最后的元素做为路程基础': 0, u'极限值做为路程的计算基础': 1,
u'序列sum+极值做为路程计算基础': 2},
value=0,
description=u'路程模式:',
disabled=False
)
widget_list.append(self.shift_distance_mode)
return widgets.VBox(widget_list,
# border='solid 1px',
layout=self.tool_layout)
def init_regress_ui(self):
"""线性拟合ui"""
with self._init_tip_label_with_step_x(
self._regress_analyse, u'线性拟合分析') as (widget_list, step_x):
self.regress_step_x = step_x
self.regress_mode_description = widgets.Textarea(
value=u'1. 技术线最少拟合次数:\n'
u'检测至少多少次拟合曲线可以代表原始曲线y的走势,'
u'通过度量始y值和均线y_roll_mean的距离和原始y值和拟合回归的趋势曲线y_fit的距离的方法,默认使用metrics_rmse\n'
u'2. 技术线最优拟合次数:\n'
u'寻找多少次多项式拟合回归的趋势曲线可以完美的代表原始曲线y的走势\n'
u'3. 可视化技术线拟合曲线:\n'
u'通过步长参数在子金融序列中进行走势拟合,形成拟合曲线及上下拟合通道曲线,返回三条拟合曲线,组成拟合通道',
disabled=False,
layout=self.description_layout
)
widget_list.append(self.regress_mode_description)
self.regress_mode = widgets.RadioButtons(
options={u'技术线最少拟合次数': 0, u'技术线最优拟合次数': 1,
u'可视化技术线拟合曲线': 2},
value=0,
description=u'拟合模式:',
disabled=False
)
widget_list.append(self.regress_mode)
return widgets.VBox(widget_list,
# border='solid 1px',
layout=self.tool_layout)
def init_golden_line_ui(self):
"""黄金分割ui"""
with self._init_tip_label_with_step_x(
self._golden_line_analyse, u'黄金分割分析', with_step_x=False) as (widget_list, _):
self.golden_line_mode = widgets.RadioButtons(
options={u'可视化黄金分隔带': 0, u'可视化黄金分隔带+关键比例': 1,
u'可视化关键比例': 2},
value=0,
description=u'分隔模式:',
disabled=False
)
widget_list.append(self.golden_line_mode)
pt_tip_label = widgets.Label(u'比例设置仅对\'可视化关键比例\'生效', layout=self.label_layout)
self.pt_range = widgets.FloatRangeSlider(
value=[0.2, 0.8],
min=0.1,
max=0.9,
step=0.1,
description=u'比例设置:',
disabled=False,
continuous_update=False,
orientation='horizontal',
readout=True,
readout_format='.1f',
)
pt_box = widgets.VBox([pt_tip_label, self.pt_range])
widget_list.append(pt_box)
return widgets.VBox(widget_list,
# border='solid 1px',
layout=self.tool_layout)
def init_skeleton_ui(self):
"""价格骨架ui"""
with self._init_tip_label_with_step_x(
self._skeleton_analyse, u'价格骨架分析') as (widget_list, step_x):
self.skeleton_step_x = step_x
self.skeleton_mode = widgets.RadioButtons(
options={u'骨架通道点位值': 200, u'三角骨架点位值': 100, u'最小值骨架点位值': 0,
u'最大值骨架点位值': 1, u'平均值骨架点位值': 2, u'中位数骨架点位值': 3,
u'最后元素骨架点位值': 4},
value=200,
description=u'骨架模式:',
disabled=False
)
widget_list.append(self.skeleton_mode)
return widgets.VBox(widget_list,
# border='solid 1px',
layout=self.tool_layout)
def init_pair_speed_ui(self):
"""趋势敏感速度分析ui"""
with self._init_tip_label_with_step_x(
self._pair_speed_analyse, u'趋势敏感速度分析', with_step_x=False) as (widget_list, _):
self.pair_speed_mode = widgets.RadioButtons(
options={u'对比收盘敏感速度': 'close', u'对比涨跌敏感速度': 'p_change',
u'对比最高敏感速度': 'high', u'对比最低敏感速度': 'low'},
value='close',
description=u'对比模式:',
disabled=False
)
widget_list.append(self.pair_speed_mode)
resample_tip_label = widgets.Label(u'趋势敏感速度计算重采样周期', layout=self.label_layout)
self.pair_resample = widgets.IntSlider(
value=5,
min=3,
max=10,
step=1,
description=u'重采样',
disabled=False,
orientation='horizontal',
readout=True,
readout_format='d'
)
resample_box = widgets.VBox([resample_tip_label, self.pair_resample])
widget_list.append(resample_box)
return widgets.VBox(widget_list,
# border='solid 1px',
layout=self.tool_layout)
def __init__(self, tool_set):
"""初始化技术分析界面"""
super(WidgetTLTool, self).__init__(tool_set)
rs_box = self.init_rs_ui()
jump_box = self.init_jump_ui()
pair_speed = self.init_pair_speed_ui()
shift_distance = self.init_shift_distance_ui()
regress = self.init_regress_ui()
golden = self.init_golden_line_ui()
skeleton = self.init_skeleton_ui()
children = [rs_box, jump_box, pair_speed, shift_distance, regress, golden, skeleton]
if self.scroll_factor_box:
tl_box = widgets.Box(children,
layout=self.scroll_widget_layout)
# 需要再套一层VBox,不然外部的tab显示有问题
self.widget = widgets.VBox([tl_box])
else:
# 一行显示两个,2个为一组,组装sub_children_group序列,
sub_children_group = self._sub_children(children, len(children) / self._sub_children_group_cnt)
sub_children_box = [widgets.HBox(sub_children) for sub_children in sub_children_group]
self.widget = widgets.VBox(sub_children_box)
def _pair_speed_analyse(self, bt):
"""趋势变化敏感速度分析action"""
ABuProgress.clear_output()
symbol1, symbol2 = self._choice_symbol_pair()
start, end, n_folds = self._start_end_n_fold()
speed_key = self.pair_speed_mode.value
resample = self.pair_resample.value
symbol1_speed, symbol2_speed, corr = calc_pair_speed(symbol1, symbol2, resample=resample, speed_key=speed_key,
start=start, end=end, n_folds=n_folds, show=True)
if symbol1_speed is None:
self.info_change_set_mode('{} and {}'.format(symbol1, symbol2))
else:
logging.info(u'{}趋势变化敏感速度{}'.format(symbol1, symbol1_speed))
logging.info(u'{}趋势变化敏感速度{}'.format(symbol2, symbol2_speed))
logging.info(u'{}{}相关度{}'.format(symbol2, symbol2, corr))
logging.info(u'{}{}趋势相关敏感速度差{}'.format(symbol1, symbol2, (symbol1_speed - symbol2_speed) * corr))
@single_fetch_symbol_analyse
def _jump_line_analyse(self, kl, kl_tl, bt):
"""跳空缺口分析action"""
# print('正在分析跳空缺口,请稍后...')
if self.jump_mode.value == 0:
jumps = calc_jump(kl, jump_diff_factor=self.jump_diff_factor.value)
elif self.jump_mode.value == 1:
jumps = calc_jump_line(kl, power_threshold=self.power_threshold.value,
jump_diff_factor=self.jump_diff_factor.value)
else:
# 暂时固定加权比例为(0.5, 0.5)
jumps = calc_jump_line_weight(kl, sw=(0.5, 0.5), power_threshold=self.power_threshold.value,
jump_diff_factor=self.jump_diff_factor.value)
display(jumps)
@single_fetch_symbol_analyse
def _rs_line_analyse(self, kl, kl_tl, bt):
"""支撑阻力线分析action"""
if self.rs_mode.value == 0:
# 只绘制支撑线
kl_tl.show_support_trend(only_last=self.only_last.value, show=True, show_step=False)
elif self.rs_mode.value == 1:
# 只绘制阻力线
kl_tl.show_resistance_trend(only_last=self.only_last.value, show=True, show_step=False)
else:
# 支撑线和阻力线都绘制
kl_tl.show_support_resistance_trend(only_last=self.only_last.value, show=True, show_step=False)
@single_fetch_symbol_analyse
def _shift_distance_analyse(self, kl, kl_tl, bt):
"""位移路程比分析action"""
kl_tl.show_shift_distance(how=EShiftDistanceHow(self.shift_distance_mode.value),
step_x=self.shift_distance_step_x.value)
@single_fetch_symbol_analyse
def _regress_analyse(self, kl, kl_tl, bt):
"""走势线性回归分析action"""
if self.regress_mode.value == 0:
kl_tl.show_least_valid_poly()
elif self.regress_mode.value == 1:
kl_tl.show_best_poly()
else:
kl_tl.show_regress_trend_channel(step_x=self.regress_step_x.value)
@single_fetch_symbol_analyse
def _golden_line_analyse(self, kl, kl_tl, bt):
"""走势黄金分割分析action"""
if self.golden_line_mode.value == 0:
kl_tl.show_golden()
elif self.golden_line_mode.value == 1:
calc_golden(kl)
else:
kl_tl.show_percents(self.pt_range.value)
@single_fetch_symbol_analyse
def _skeleton_analyse(self, kl, kl_tl, bt):
"""走势骨架分析action"""
step_x = self.skeleton_step_x.value
skeleton_mode = self.skeleton_mode.value
if skeleton_mode == 200:
kl_tl.show_skeleton_channel(step_x=step_x)
else:
kl_tl.show_skeleton(how=ESkeletonHow(skeleton_mode), step_x=step_x)