2、可视化页面搭建

可视化页面搭建

在这里插入图片描述

1、基础页

在基础页面主要涉及以下功能:

  • 1、展示初始投资资金
  • 2、设置时间模式
    • 2.1使用回测年数
    • 2.2使用回测开始结束时间
  • 3、回测结果设置
    在这里插入图片描述

1、展示初始投资资金

在这个方法中可以让用户自定义设置初始投资资金,以及搭建一个基础页面的基础类

# 继承两个类,一个处理时间模式设置,一个处理回测结果设置
class WidgetRunTT(WidgetTrad, WidgetTime):
    """基础设置界面:初始资金,回测开始,结束周期等"""

    def __init__(self):
        """初始化基础回测设置界面"""
        # 初始资金
        self.cash = widgets.BoundedIntText(
            value=1000000,
            min=10000,
            max=999999999,
            step=1,
            description='初始资金:',
            disabled=False
        )
        # 时间模式设置
        tm_box = self.init_time_mode_ui()
        # 回测结果设置
        metrics_box = self.init_metrics_ui()
		# 通过VBox功能将其展示到页面上
        self.widget = widgets.VBox([self.cash, tm_box, metrics_box])

基础类已经完成,但是其中的时间模式设置以及回测结果设置还没有。
在这里插入图片描述

2、时间模式设置(init_time_mode_ui)

class WidgetTime(object):
    """
        便于上层widgte使用self去获取设置,统一上层使用
        混入类:基础时间模式设置:
        1. 年数模式
        2. 开始结束模式
    """

    # noinspection PyProtectedMember
    def init_time_mode_ui(self):
        """构建基础 return widgets.VBox"""
        # 回测时间模式
        self.time_mode = widgets.RadioButtons(
            options={'使用回测年数': 0,
                     '使用回测开始结束日期': 1},
            value=0,
            description='时间模式:',
            disabled=False
        )
        self.time_mode.observe(self.on_time_mode_change, names='value')
        # 年数模式
        self.run_years = widgets.BoundedIntText(
            value=2,
            min=1,
            max=6,
            step=1,
            description='回测年数:',
            disabled=False
        )
        # 开始结束模式
        self.start = widgets.Text(
            value='2015-01-01',
            placeholder='年-月-日',
            description='开始日期:',
            disabled=False
        )
        self.end = widgets.Text(
            value='2019-04-26',
            placeholder='年-月-日',
            description='结束日期:',
            disabled=False
        )
        self.run_years.disabled = False
        self.start.disabled = True
        self.end.disabled = True

        return widgets.VBox([self.time_mode, self.run_years, self.start, self.end])

    def on_time_mode_change(self, change):
        """切换使用年数还是起始,结束时间做为回测参数"""
        if change['new'] == 0:
            self.run_years.disabled = False
            self.start.disabled = True
            self.end.disabled = True
        else:
            self.run_years.disabled = True
            self.start.disabled = False
            self.end.disabled = False

在这里插入图片描述

3、回测输出设置(init_metrics_ui)

class WidgetTrad(object):
    """
		回测输出设置:
        1. 输出度量对象:
            1. 只输出交易单:orders_pd
            2. 只输出行为单:action_pd
            3. 只输出资金单:capital_pd
            4. 同时输出交易单,行为单,资金单(orders_pd, action_pd, capital_pd)
        2. 输出交易单最大行列显示设置:
            1. 默认最大行显示50
            2. 默认最大列显示50
        3. 是否将交易单,行为单,资金单保存在本地output文件中
    """
    def init_metrics_ui(self):
        """构建基础 return widgets.VBox"""

        self.metrics_out_put = widgets.RadioButtons(
            options={'只输出交易单:orders_pd': 0,
                     '只输出行为单:action_pd': 1,
                     '只输出资金单:capital_pd': 2,
                     '输出交易单,行为单,资金单': 3},
            value=0,
            description='输出对象:',
            disabled=False
        )

        out_put_display_max_label1 = widgets.Label('输出显示最大行列数,最大100行,100列',
                                                   layout=widgets.Layout(width='300px', 																align_items='stretch'))
        out_put_display_max_label2 = widgets.Label('如需查看更多输出表单,请选择保存输出至文件',
                                                   layout=widgets.Layout(width='300px', align_items='stretch'))
        self.out_put_display_max_rows = widgets.IntSlider(
            value=50,
            min=1,
            max=100,
            step=1,
            description='行数',
            disabled=False,
            orientation='horizontal',
            readout=True,
            readout_format='d'
        )

        self.out_put_display_max_columns = widgets.IntSlider(
            value=50,
            min=1,
            max=100,
            step=1,
            description='列数',
            disabled=False,
            orientation='horizontal',
            readout=True,
            readout_format='d'
        )
        out_put_display = widgets.VBox([out_put_display_max_label1,
                                        out_put_display_max_label2,
                                        self.out_put_display_max_rows,
                                        self.out_put_display_max_columns])

        save_out_put_lable = widgets.Label('是否保存交易单,行为单,资金单到文件',
                                           layout=widgets.Layout(width='300px', align_items='stretch'))
        save_out_put_lable2 = widgets.Label('路径:{}'.format(os.path.join(settings.BASE_DIR, 'out_put')),
                                            layout=widgets.Layout(width='300px', align_items='stretch'))
        self.save_out_put = widgets.Checkbox(
            value=False,
            description='保存输出',
            disabled=False,
        )
        save_out_put = widgets.VBox([save_out_put_lable,
                                     save_out_put_lable2,
                                     self.save_out_put])

        accordion = widgets.Accordion()
        accordion.children = [widgets.VBox([self.metrics_out_put, out_put_display, save_out_put])]
        accordion.set_title(0, '回测度量结果设置')

        return accordion

在这里插入图片描述

在这里插入图片描述

2、股池页

涉及功能:

  • 1、股池区,用于存放选择的股票
    • 点击选中的股票可以将其删除
  • 2、选股区,选择需要回测股票
    • 点击选择的股票可以将其添加到股池区
  • 3、搜索区
    • 输入股票代码返回对应股票信息,可以选择添加进入股票池
      在这里插入图片描述

1、股池区

class StockPool(WidgetBase):
    """股票池选股ui界面"""

    # noinspection PyProtectedMember
    def __init__(self):
        """构建股票池选股ui界面"""
        label_layout = widgets.Layout(width='300px', align_items='stretch', justify_content='space-between')
        # 股票池多选框
        self.choice_symbols = widgets.SelectMultiple(
            description='股池:',
            disabled=False,
            layout=widgets.Layout(width='300px', align_items='stretch', justify_content='space-between')
        )
        self.choice_symbols.observe(self.choice_symbols_select, names='value')

        # 构建所有沙盒中的数据序列
        market_title = ['A股']  # 因为目前国内股票历史数据只有A股是免费获取,其他例如美股、港股需要花钱获取,所以先以A股进行测试
        cn_seed_symbol = [('{}:{}'.format(name[0], symbol))
                          for symbol, name in stock_list.get_stock_list(stock_list.K_SAND_BOX_CN).items()]
        self.market_dict = {'A股': cn_seed_symbol,}
        # 一个市场一个tab,tab中的symbol为沙盒中的symbol
        self.market_widget_tab = widgets.Tab()
        self.market_symbol_widget = []

        for title in market_title:
            market_symbol = widgets.SelectMultiple(
                options=self.market_dict[title],
                description=title,
                disabled=False
            )
            market_symbol.observe(self.on_already_select, names='value')
            self.market_symbol_widget.append(market_symbol)
            self.market_widget_tab.children = self.market_symbol_widget

            for ind, name in enumerate(market_title):
                self.market_widget_tab.set_title(ind, name)

            self.sc_box = WidgetSearchBox(self.on_already_select)()

        self.widget = widgets.VBox([self.choice_symbols, self.market_widget_tab,self.sc_box])

在这里插入图片描述

2、选股区

    def on_already_select(self, select):
        """搜索框或者内置沙盒symbol中点击放入股票池"""
        st_symbol = [symbol.split(':')[1] if symbol.find(':') > 0
                     else symbol for symbol in list(select['new'])]
        # 更新股票池中原有的symbol序列
        self.choice_symbols.options = list(set(st_symbol + list(self.choice_symbols.options)))

    def choice_symbols_select(self, select):
        """股票池中点击删除股票池中对应的symbol"""
        self.choice_symbols.options = list(set(self.choice_symbols.options) - set(select['new']))

在这里插入图片描述

3、搜索区

class WidgetSearchBox(WidgetBase):
    """搜索框ui界面"""

    # noinspection PyProtectedMember
    def __init__(self, search_result_callable):
        """构建股票池选股ui界面"""
        if not callable(search_result_callable):
            raise TypeError('search_result_select_func must callable!')
        # symbol搜索框构建
        self.search_bt = widgets.Button(description='搜索:', layout=widgets.Layout(height='10%', width='7%'))
        self.search_input = widgets.Text(
            value='',
            placeholder='请输入股票代码...',
            description='',
            disabled=False
        )
        # symbol搜索结果框
        self.search_result = widgets.SelectMultiple(
            options=[],
            description='搜索结果:',
            disabled=False,
            layout=widgets.Layout(width='300px', align_items='stretch', justify_content='space-between')
        )
        self.search_result.observe(search_result_callable, names='value')
        self.search_bt.on_click(self.do_search)

        # 搜索框 + 按钮 + 结果框 box拼接
        sc_hb = widgets.HBox([self.search_bt, self.search_input])
        self.widget = widgets.VBox([sc_hb, self.search_result])

    # noinspection PyUnusedLocal
    def do_search(self, bt):
        """搜索框搜索执行函数"""
        ts_code = self.search_input.value
        data = stock_list.read_data()  # data是通过tushare获取的股票信息列表
        if ts_code[0] == '0':
            ts_code += ".SZ"
        elif ts_code[0] == '6':
            ts_code += ".SH"
        code_data = data[data['ts_code'].isin([ts_code])]
        ts_name = code_data.name.values
        ts_code = ts_code[:6]
        for name in ts_name:
            self.search_result.options = [f'{name}:{ts_code}']

在这里插入图片描述

在这里插入图片描述

3、策略页

涉及功能:

  • 1、展示所有的策略
  • 2、选择交易策略
  • 3、展示已选中的策略

在这里插入图片描述

1、界面组件基类

class WidgetBase(object):
    """界面组件基类,限定最终widget为self.widget"""

    def __call__(self):
        return self.widget

    def display(self):
        """显示使用统一display"""
        display(self.widget)

在这里插入图片描述

2、策略组织类

class Strategy():
    """策略组织类"""

    def __init__(self, show_add_buy=True, add_button_style='default'):
        self.factor_dict = {}
        self.factor_wg_array = []
        # 策略候选池可x轴左右滚动
        self.factor_layout = widgets.Layout(overflow_x='scroll',
                                            # flex_direction='row',
                                            display='flex')
        self.selected_factors = widgets.SelectMultiple(
            options=[],
            description='已添加策略:',
            disabled=False,
            layout=widgets.Layout(width='100%', align_items='stretch')
        )

        # 已添加的全局策略可点击删除
        self.selected_factors.observe(self.remove_factor, names='value')
        # 全局策略改变通知接收序列
        self.selected_factors_obs = set()
        self.factor_box = None
        # # 默认不启动可滚动因子界面,因为对外的widget版本以及os操作系统不统一
        self.show_add_buy = show_add_buy
        self.add_button_style = add_button_style
        self._init_widget()

        if self.factor_box is None:
            raise RuntimeError('_init_widget must build factor_box!')
        self.widget = widgets.VBox([self.factor_box, self.selected_factors])


    def remove_factor(self, select):
        """点击从策略池中删除已选择的策略"""
        for st_key in list(select['new']):
            self.factor_dict.pop(st_key)
        self.selected_factors.options = list(self.factor_dict.keys())


    def add_factor(self, factor_dict, factor_desc_key, only_one=False):
        """根据具体策略提供的策略字典对象和策略描述构建上层策略序列"""
        print("你执行了添加策略")
        if factor_desc_key in self.factor_dict:
            msg = '{}策略已经添加过,重复添加!'.format(factor_desc_key)
        
            return
        if only_one:
            """
                非重复容器类型策略,如一个买入策略只能对应一个仓位管理策略
                大多数为可复容器类型策略,如可以有多个买入因子,多个卖出,
                多个选股因子
            """
            # 对基础类型不要使用clear等函数,py2低版本不支持
            self.factor_dict.clear()
            self.factor_dict = {}
        self.factor_dict[factor_desc_key] = factor_dict
        print(self.factor_dict.keys())
        self.selected_factors.options = list(self.factor_dict.keys())


    def _init_widget(self):
        self.sf_array = []
        self.sf_array.append(MeanReversion(self))
        self.sf_array.append(DoubleAverages(self))
        self.sf_array.append(TurtleStrategy(self))

        self.scroll_factor_box = False

        children = [sf() for sf in self.sf_array]

        self.factor_layout = widgets.Layout(overflow_x='scroll',
                                            flex_direction='row',
                                            display='flex')

        self.factor_box = widgets.Box(children=children,
                                      layout=self.factor_layout)

在这里插入图片描述

3、策略可视化基础类

class WidgetFactorBase(WidgetBase):
    """策略可视化基础类"""

    def __init__(self, wg_manager):
        self.wg_manager = wg_manager
        self.widget = None
        self.label_layout = widgets.Layout(width='300px', align_items='stretch')
        self.description_layout = widgets.Layout(height='150px')
        self.widget_layout = widgets.Layout(align_items='stretch', justify_content='space-between')

在这里插入图片描述

4、买入策略可视化基础类

class WidgetFactorBuyBase(WidgetFactorBase):
    """买入策略可视化基础类"""

    def __init__(self, wg_manager):
        super(WidgetFactorBuyBase, self).__init__(wg_manager)

        self.add = widgets.Button(description='添加为全局策略', layout=widgets.Layout(width='98%'),
                                  button_style='info')
        self.add.on_click(self.add_buy_factor)
        self._init_widget = getattr(self,'_init_widget')
        self._init_widget()

    def add_buy_factor(self, bt):
        """对应按钮添加策略,构建策略字典对象factor_dict以及唯一策略描述字符串factor_desc_key"""
        self.make_buy_factor_unique = getattr(self,'make_buy_factor_unique')
        factor_dict, factor_desc_key = self.make_buy_factor_unique()
        self.wg_manager.add_factor(factor_dict, factor_desc_key)

在这里插入图片描述

5、均值回归策略

class MeanReversion(WidgetFactorBuyBase):
    """
    均值回归策略:
    1. 均值回归:“跌下去的迟早要涨上来”
    2. 均值回归的理论基于以下观测:价格的波动一般会以它的均线为中心。也就是说,当标的价格由于波动而偏离移动均线时,它将调整并重新归于均线。
    3. 偏离程度:(MA-P)/MA
    4. 策略:在每个调仓日进行(每月调一次仓)
      4.1 计算池内股票的N日移动均线;
      4.2 计算池内所有股票价格与均线的偏离度;
      4.3 选取偏离度最高的num_stocks支股票并进行调仓。
    """

    def _init_widget(self):
        self.label_layout = widgets.Layout(width='300px', align_items='stretch')
        self.widget_layout = widgets.Layout(align_items='stretch', justify_content='space-between')
        self.description = widgets.Textarea(
            value='均值回归策略:\n'
                  '1. 均值回归:“跌下去的迟早要涨上来”'
                  '2. 均值回归的理论基于以下观测:价格的波动一般会以它的均线为中心。也就是说,当标的价格由于波动而偏离移动均线时,它将调整并重新归于均线。'
                  '3. 偏离程度:(MA-P)/MA'
                  '4. 策略:在每个调仓日进行(每月调一次仓)'
                  '4.1 计算池内股票的N日移动均线; '
                  '4.2 计算池内所有股票价格与均线的偏离度;'
                  '4.3 选取偏离度最高的num_stocks支股票并进行调仓',
            description='均值回归',
            disabled=False,
            layout=widgets.Layout(height='150px')
        )

        self.average_label = widgets.Label('默认30日均线,可手动调节数值', layout=self.label_layout)
        self.average_int = widgets.IntSlider(
            value=30,
            min=10,
            max=120,
            step=1,
            description='手动',
            disabled=False,
            orientation='horizontal',
            readout=True,
            readout_format='d'
        )
        self.auto_average = widgets.Checkbox(
            value=True,
            description='均线',
            disabled=False
        )

        def average_change(change):
            self.average_int.disabled = change['new']

        self.average_box = widgets.VBox([self.average_label, self.average_int, self.auto_average])

        self.position_label = widgets.Label('默认持仓5支,可手动调节', layout=self.label_layout)
        self.position_int = widgets.IntSlider(
            value=5,
            min=1,
            max=20,
            step=1,
            description='手动',
            disabled=False,
            orientation='horizontal',
            readout=True,
            readout_format='d'
        )
        self.auto_position = widgets.Checkbox(
            value=True,
            description='持仓',
            disabled=False,
        )

        def position_change(change):
            self.position_int.disabled = change['new']

        self.position_box = widgets.VBox([self.position_label, self.position_int, self.auto_position])
        self.widget =  widgets.VBox([self.description, self.average_box, self.position_box,self.add])


    def make_buy_factor_unique(self):
        """对应按钮添加双均线策略,构建策略字典对象factor_dict以及唯一策略描述字符串factor_desc_key"""
        factor_dict = { 'average_int': self.average_int.value,
                       'auto_average': self.auto_average.value, 'position_int': self.position_int.value}
        factor_desc_key = '使用均值回归策略进行回测,{}均线,持仓{}'.format(
            self.average_int.value, self.position_int.value)
        return factor_dict, factor_desc_key

在这里插入图片描述

6、双均线策略

class DoubleAverages(WidgetFactorBuyBase):
    """
    双均线策略:
    1. 预设两条均线:如一个ma=5,一个ma=60, 5的均线被称作快线,60的均线被称作慢线
    2. 择时买入策略中当快线上穿慢线(ma5上穿ma60)称为形成金叉买点信号,买入股票
    3. 自适应动态慢线,不需要输入慢线值,根据走势震荡套利空间,寻找合适的ma慢线
    4. 自适应动态快线,不需要输入快线值,根据慢线以及大盘走势,寻找合适的ma快线
    """
    def _init_widget(self):
        self.label_layout = widgets.Layout(width='300px', align_items='stretch')
        self.widget_layout = widgets.Layout(align_items='stretch', justify_content='space-between')
        self.description = widgets.Textarea(
            value='双均线策略:\n'
                  '1. 预设两条均线:如一个ma=5,一个ma=60, 5的均线被称作快线,60的均线被称作慢线'
                  '2. 择时买入策略中当快线上穿慢线(ma5上穿ma60)称为形成金叉买点信号,买入股票'
                  '3. 自适应动态慢线,不需要输入慢线值,根据走势震荡套利空间,寻找合适的ma慢线'
                  '4. 自适应动态快线,不需要输入快线值,根据慢线以及大盘走势,寻找合适的ma快线',
            description='双均线策略:',
            disabled=False,
            layout=widgets.Layout(height='150px')
        )

        self.slow_label = widgets.Label('默认动态慢线60日均线,可手动调节数值', layout=self.label_layout)
        self.slow_int = widgets.IntSlider(
            value=60,
            min=10,
            max=120,
            step=1,
            description='手动',
            disabled=False,
            orientation='horizontal',
            readout=True,
            readout_format='d'
        )
        self.auto_slow = widgets.Checkbox(
            value=True,
            description='动态慢线',
            disabled=False
        )

        def slow_change(change):
            self.slow_int.disabled = change['new']

        self.auto_slow.observe(slow_change, names='value')
        self.slow = widgets.VBox([self.auto_slow, self.slow_int])
        self.slow_box = widgets.VBox([self.slow_label, self.slow])

        self.fast_label = widgets.Label('默认动态快线5日均线,可手动调节数值', layout=self.label_layout)
        self.fast_int = widgets.IntSlider(
            value=5,
            min=1,
            max=90,
            step=1,
            description=u'手动',
            disabled=False,
            orientation='horizontal',
            readout=True,
            readout_format='d'
        )
        self.auto_fast = widgets.Checkbox(
            value=True,
            description='动态快线',
            disabled=False,
        )

        def fast_change(change):
            self.fast_int.disabled = change['new']

        self.auto_fast.observe(fast_change, names='value')
        self.fast = widgets.VBox([self.auto_fast, self.fast_int])
        self.fast_box = widgets.VBox([self.fast_label, self.fast])

        self.widget = widgets.VBox([self.description, self.slow_box, self.fast_box,self.add],  # border='solid 1px',
                                   layout=self.widget_layout)

    def make_buy_factor_unique(self):
        """对应按钮添加AbuDoubleMaBuy策略,构建策略字典对象factor_dict以及唯一策略描述字符串factor_desc_key"""
        slow_int = -1 if self.auto_slow.value else self.slow_int.value
        fast_int = -1 if self.auto_fast.value else self.fast_int.value

        factor_dict = {'slow': slow_int, 'fast': fast_int}
        factor_desc_key = '动态双均慢{}快{}买入'.format(
            '动态' if slow_int == -1 else slow_int, '动态' if fast_int == -1 else fast_int)
        return factor_dict, factor_desc_key

在这里插入图片描述

7、海龟策略

class TurtleStrategy(WidgetFactorBuyBase):
    """海龟策略"""
    def _init_widget(self):
        """构建AbuFactorBuyBreak策略参数界面"""
        self.label_layout = widgets.Layout(width='300px', align_items='stretch')
        self.widget_layout = widgets.Layout(align_items='stretch', justify_content='space-between')
        self.description = widgets.Textarea(
            value='海龟向上趋势突破买入策略:\n'
                  '趋势突破定义为当天收盘价格超过N天内的最高价,超过最高价格作为买入信号买入股票持有',
            description='海龟策略',
            disabled=False,
            layout=widgets.Layout(height='150px')
        )
        self.xd_label = widgets.Label('突破周期参数:比如21,30,42天....突破', layout=self.label_layout)
        self.xd = widgets.IntSlider(
            value=21,
            min=3,
            max=120,
            step=1,
            description='周期',
            disabled=False,
            orientation='horizontal',
            readout=True,
            readout_format='d'
        )
        self.xd_box = widgets.VBox([self.xd_label, self.xd])
        self.widget = widgets.VBox([self.description, self.xd_box,self.add],  # border='solid 1px',
                                   layout=self.widget_layout)

    def make_buy_factor_unique(self):
        """对应按钮添加AbuFactorBuyBreak策略,构建策略字典对象factor_dict以及唯一策略描述字符串factor_desc_key"""
        factor_dict = {'xd': self.xd.value}
        factor_desc_key = '海龟{}天趋势突破买入'.format(self.xd.value)
        return factor_dict, factor_desc_key

在这里插入图片描述

周期’,
disabled=False,
orientation=‘horizontal’,
readout=True,
readout_format=‘d’
)
self.xd_box = widgets.VBox([self.xd_label, self.xd])
self.widget = widgets.VBox([self.description, self.xd_box,self.add], # border=‘solid 1px’,
layout=self.widget_layout)

def make_buy_factor_unique(self):
    """对应按钮添加AbuFactorBuyBreak策略,构建策略字典对象factor_dict以及唯一策略描述字符串factor_desc_key"""
    factor_dict = {'xd': self.xd.value}
    factor_desc_key = '海龟{}天趋势突破买入'.format(self.xd.value)
    return factor_dict, factor_desc_key


![在这里插入图片描述](https://img-blog.csdnimg.cn/20200224210956631.gif)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值