def update_pattern_data_by_index_for_daily_period(self,
                                                   index: str,
                                                   start_after=''):
     print('Update pattern data for index: {}'.format(index))
     self.sys_config.init_detection_process_for_automated_pattern_update()
     self.sys_config.data_provider.use_index(index)
     if start_after != '':
         self.sys_config.data_provider.start_after(start_after)
     pattern_controller = PatternDetectionController(self.sys_config)
     pattern_controller.run_pattern_detector()
 def update_trade_records(self,
                          mean: int,
                          sma_number: int,
                          trade_strategies: dict = None,
                          pattern_end_date=str):
     if pattern_end_date == '':
         pattern_end_date = str(MyDate.adjust_by_days(
             None, -90))  # only the ones which are 3 month back
     self.sys_config.init_detection_process_for_automated_trade_update(
         mean, sma_number)
     if trade_strategies is None:
         trade_strategies = {
             BT.BREAKOUT: [
                 TSTR.LIMIT, TSTR.LIMIT_FIX, TSTR.TRAILING_STOP,
                 TSTR.TRAILING_STEPPED_STOP
             ]
         }
     for pattern_type in FT.get_long_trade_able_types():
         where_clause = "pattern_type = '{}' and period = 'DAILY' and trade_type in ('', 'long')".format(
             pattern_type)
         if pattern_end_date != '':
             where_clause += " AND {} > '{}'".format(
                 DC.PATTERN_END_DT, pattern_end_date)
         df_pattern_for_pattern_type = self.db_stock.get_pattern_records_as_dataframe(
             where_clause)
         pattern_id_dict = {
             row[DC.ID]: row[DC.TRADE_TYPE]
             for index, row in df_pattern_for_pattern_type.iterrows()
         }
         # pattern_id_list = ['20_1_1_LTCUSD_20_2016-11-02_00:00_2016-12-04_00:00']
         counter = 0
         for pattern_id, trade_type in pattern_id_dict.items():
             counter += 1
             run_detail = '{} ({:03d} of {:03d}): {}'.format(
                 pattern_type, counter, len(pattern_id_dict), pattern_id)
             result_dict = self.db_stock.get_missing_trade_strategies_for_pattern_id(
                 pattern_id, trade_strategies, mean, sma_number)
             for buy_trigger, trade_strategy_list in result_dict.items():
                 if len(trade_strategy_list) == 0:
                     print('{}: OK'.format(run_detail))
                 else:
                     print('{}: processing...'.format(run_detail))
                     self.sys_config.config.pattern_ids_to_find = [
                         pattern_id
                     ]
                     self.sys_config.exchange_config.trade_strategy_dict = {
                         buy_trigger: trade_strategy_list
                     }
                     pattern_controller = PatternDetectionController(
                         self.sys_config)
                     pattern_controller.run_pattern_detector()
             if trade_type == '':
                 self.db_stock.update_trade_type_for_pattern(
                     pattern_id, TRT.LONG)
 def __init__(self, app: Dash, sys_config: SystemConfiguration,
              trade_handler_online: PatternTradeHandler):
     MyPatternDashBaseTab.__init__(self, app, sys_config)
     self.sys_config = self.__get_adjusted_sys_config_copy__(sys_config)
     self.exchange_config = self.sys_config.exchange_config
     self._pattern_controller = PatternDetectionController(self.sys_config)
     self._recommender_table = RecommenderTable(
         self.sys_config.db_stock, self.sys_config.data_provider)
     self._active_manage_button_clicks = 0
     self._refresh_button_clicks = 0
     self._trade_handler_online = trade_handler_online
     self._dd_handler = RecommenderTabDropDownHandler()
Esempio n. 4
0
    def run_test_case(self, tc: TradeTestCase):
        self.exchange_config.trade_strategy_dict = {tc.buy_trigger: [tc.trade_strategy]}
        self.sys_config.config.pattern_type_list = [tc.pattern_type]
        self.sys_config.data_provider.use_own_dic({tc.symbol: tc.symbol})
        self.sys_config.data_provider.and_clause = tc.and_clause
        self.sys_config.config.with_trade_part = False if tc.buy_trigger == BT.TOUCH_POINT else True

        pattern_controller = PatternDetectionController(self.sys_config)
        detector = pattern_controller.get_detector_for_dash(self.sys_config, tc.symbol, tc.and_clause)
        pattern_list = detector.get_pattern_list_for_buy_trigger(tc.buy_trigger)
        trade_handler = PatternTradeHandler(self.sys_config)  # we need a new one for each
        trade_handler.add_pattern_list_for_trade(pattern_list)
        self.__print_frame_information__(tc.buy_trigger, tc.trade_strategy, tc.test_process)
        for wave_tick in tc.wave_tick_list:
            trade_handler.check_actual_trades(wave_tick)  # wave_tick
        trade_handler.enforce_sell_at_end(tc.wave_tick_list[-1])
        self.__print_frame_information__(tc.buy_trigger, tc.trade_strategy)
 def __init__(self, app: Dash, sys_config: SystemConfiguration,
              trade_handler_online: PatternTradeHandler):
     MyPatternDashBaseTab.__init__(self, app, sys_config)
     self.sys_config = self.__get_adjusted_sys_config_copy__(sys_config)
     self.exchange_config = self.sys_config.exchange_config
     self._table_rows = []
     self._reset_portfolio_selection_clicks = 0
     self._refresh_portfolio_selection_clicks = 0
     self._active_manage_button_clicks = 0
     self._selected_indicator = INDI.NONE
     self._selected_row_index = -1
     self._selected_row = None
     self._selected_ticker_id = ''
     self._selected_ticker_id_management = ''
     self._pattern_controller = PatternDetectionController(self.sys_config)
     self._trade_handler_online = trade_handler_online
     self._dd_handler = PortfolioTabDropDownHandler()
     self._selected_ticker_count_dict = {}
Esempio n. 6
0
 def __init__(self, app: Dash, sys_config: SystemConfiguration, trade_handler_online: PatternTradeHandler):
     MyPatternDashBaseTab.__init__(self, app, sys_config)
     self.bitfinex_config = self.sys_config.exchange_config
     self.trade_handler_online = trade_handler_online
     self.sys_config_second = sys_config.get_semi_deep_copy()
     self.sys_config_fibonacci = self.sys_config.get_semi_deep_copy()
     self._pattern_controller = PatternDetectionController(self.sys_config, TP.ONLINE)
     self.detector = None
     self._index_options = self.sys_config.index_config.get_indices_as_options()
     self._ticker_options = []
     self._ticker_selected = ''
     self.__fill_ticker_options__()
     self._dd_handler = PatternTabDropDownHandler(self._index_options, self._ticker_options)
     self._time_stamp_next_refresh = None
     self._graph_first_data_provider_api = None
     self._graph_second_data_provider_api = None
     self._graph_first_cache = MyGraphCache('my_graph_first')
     self._graph_second_cache = MyGraphCache('my_graph_second')
     self._state_handler = MyDashStateHandler(self._ticker_options)
     self._graph_key_first = ''
     self._detector_first = None
     self._pattern_data_first = None
     self._balance_saving_times = MyDate.get_epoch_seconds_for_current_day_as_list()
class MyDashTab4Portfolio(MyPatternDashBaseTab):
    _data_table_name = 'my_portfolio_table'
    _data_table_div = '{}_div'.format(_data_table_name)

    def __init__(self, app: Dash, sys_config: SystemConfiguration,
                 trade_handler_online: PatternTradeHandler):
        MyPatternDashBaseTab.__init__(self, app, sys_config)
        self.sys_config = self.__get_adjusted_sys_config_copy__(sys_config)
        self.exchange_config = self.sys_config.exchange_config
        self._table_rows = []
        self._reset_portfolio_selection_clicks = 0
        self._refresh_portfolio_selection_clicks = 0
        self._active_manage_button_clicks = 0
        self._selected_indicator = INDI.NONE
        self._selected_row_index = -1
        self._selected_row = None
        self._selected_ticker_id = ''
        self._selected_ticker_id_management = ''
        self._pattern_controller = PatternDetectionController(self.sys_config)
        self._trade_handler_online = trade_handler_online
        self._dd_handler = PortfolioTabDropDownHandler()
        self._selected_ticker_count_dict = {}

    @property
    def columns(self) -> list:
        return [
            PDC.EXCHANGE, PDC.ASSET, PDC.AMOUNT, PDC.AMOUNT_AVAILABLE,
            PDC.CURRENT_PRICE, PDC.CURRENT_TOTAL, PDC.ACTIVELY_MANAGED
        ]

    @staticmethod
    def __get_button_handler__():
        return PortfolioButtonHandler()

    @staticmethod
    def __get_adjusted_sys_config_copy__(
            sys_config: SystemConfiguration) -> SystemConfiguration:
        sys_config_copy = sys_config.get_semi_deep_copy(
        )  # we need some adjustments (_period, etc...)
        sys_config_copy.data_provider.from_db = False
        sys_config_copy.data_provider.period = sys_config.period
        sys_config_copy.data_provider.aggregation = sys_config.period_aggregation
        return sys_config_copy

    def __init_selected_row__(self, selected_row_indices: list = None):
        if selected_row_indices is None or len(selected_row_indices) != 1:
            self._selected_row_index = -1
            self._selected_row = None
            self._selected_ticker_id = ''
            self._selected_ticker_id_management = ''
        else:
            self._selected_row_index = selected_row_indices[0]
            self._selected_row = self._table_rows[self._selected_row_index]
            self._selected_ticker_id = '{}{}'.format(
                self._selected_row[PDC.ASSET], 'USD')
            self._selected_ticker_id_management = self._selected_row[
                PDC.ACTIVELY_MANAGED]
            if self._selected_ticker_id not in self._selected_ticker_count_dict:
                self._selected_ticker_count_dict[self._selected_ticker_id] = 0
            self._selected_ticker_count_dict[self._selected_ticker_id] += 1

    @staticmethod
    def __get_news_handler__():
        return NewsHandler('  \n', '')

    def get_div_for_tab(self):
        children_list = [
            MyHTMLTabPortfolioHeaderTable().get_table(),
            MyHTML.div_with_dcc_drop_down(
                **self._dd_handler.get_drop_down_parameters(
                    PODD.PERIOD_AGGREGATION,
                    default_value=self.sys_config.period_aggregation)),
            MyHTML.div_with_dcc_drop_down(
                **self._dd_handler.get_drop_down_parameters(
                    PODD.REFRESH_INTERVAL, default_value=900)),
            MyHTML.div_with_dcc_drop_down(
                **self._dd_handler.get_drop_down_parameters(
                    PODD.SECOND_GRAPH_RANGE)),
            MyHTML.div_with_dcc_drop_down(
                **self._dd_handler.get_drop_down_parameters(PODD.INDICATOR)),
            MyHTML.div_with_button(
                **self._button_handler.get_button_parameters(
                    PFBTN.RESET_PORTFOLIO_SELECTION)),
            MyHTML.div_with_html_button_submit('my_portfolio_refresh_button',
                                               'Refresh'),
            MyHTML.div_with_html_button_submit(
                'my_portfolio_active_manage_button',
                self.__get_position_manage_button_text__()),
            MyHTML.div_with_table(self._data_table_div,
                                  self.__get_table_for_portfolio__()),
            MyHTML.div('my_graph_portfolio_position_div'),
            MyHTML.div('my_graph_portfolio_position_second_div')
        ]
        return MyHTML.div('my_portfolio_div', children_list)

    def init_callbacks(self):
        self.__init_callback_for_portfolio_position__()
        self.__init_callbacks_for_portfolio_refresh_button__()
        self.__init_callbacks_for_position_manage_button__()
        self.__init_callback_for_portfolio_markdown__()
        self.__init_callback_for_portfolio_table__()
        self.__init_callback_for_graph_for_selected_position__()
        self.__init_callback_for_reset_portfolio_button__()

    def __init_callbacks_for_portfolio_refresh_button__(self):
        @self.app.callback(Output(
            'my_portfolio_refresh_button', DSHVT.HIDDEN), [
                Input('my_graph_portfolio_position_div', DSHVT.CHILDREN),
                Input('my_portfolio_aggregation', DSHVT.VALUE),
                Input('my_portfolio_refresh_interval_selection', DSHVT.VALUE),
                Input('my_portfolio_graph_second_days_selection', DSHVT.VALUE),
                Input('my_portfolio_indicator_selection', DSHVT.VALUE)
            ])
        def handle_callback_for_portfolio_refresh_button_hidden(
                graph, aggregation, interval, second_days, indicator):
            if indicator != self._selected_indicator:
                self._selected_indicator = indicator
                return ''
            return 'hidden'

    def __init_callbacks_for_position_manage_button__(self):
        @self.app.callback(
            Output('my_portfolio_active_manage_button', DSHVT.HIDDEN),
            [Input('my_graph_portfolio_position_div', DSHVT.CHILDREN)])
        def handle_callback_for_position_manage_button_hidden(graph):
            if graph == '':
                return 'hidden'
            return ''

        @self.app.callback(
            Output('my_portfolio_active_manage_button', DSHVT.CHILDREN),
            [Input('my_graph_portfolio_position_div', DSHVT.CHILDREN)])
        def handle_callback_for_position_manage_button_text(graph):
            return self.__get_position_manage_button_text__()

    def __init_callback_for_portfolio_position__(self):
        @self.app.callback(
            Output('my_portfolio_position_div', DSHVT.CHILDREN),
            [Input(self._data_table_name, DSHVT.SELECTED_ROW_INDICES)],
            [State(self._data_table_name, DSHVT.ROWS)])
        def handle_callback_for_portfolio_position(selected_row_indices: list,
                                                   rows: list):
            self.__init_selected_row__(selected_row_indices)
            if self._selected_ticker_id == '':
                return ''
            return rows[self._selected_row_index][PDC.ASSET]

    def __init_callback_for_graph_for_selected_position__(self):
        @self.app.callback(
            Output('my_graph_portfolio_position_div', DSHVT.CHILDREN), [
                Input(self._data_table_name, DSHVT.ROWS),
                Input(self._data_table_name, DSHVT.SELECTED_ROW_INDICES),
                Input('my_portfolio_refresh_button', DSHVT.N_CLICKS)
            ], [
                State('my_portfolio_aggregation', DSHVT.VALUE),
                State('my_portfolio_refresh_interval_selection', DSHVT.VALUE),
                State('my_portfolio_indicator_selection', DSHVT.VALUE)
            ])
        def handle_callback_for_graph_first(
                rows: list, selected_row_indices: list, refresh_n_clicks: int,
                aggregation: int, refresh_interval: int, indicator: str):
            self.__fill_thread_filled_graph_dict__(rows,
                                                   refresh_interval,
                                                   limit=300,
                                                   indicator=indicator)
            if self._refresh_portfolio_selection_clicks != refresh_n_clicks:
                self._refresh_portfolio_selection_clicks = refresh_n_clicks
                selected_row_indices = []
            self.__init_selected_row__(selected_row_indices)
            if self._selected_ticker_id == '':
                return ''
            self.sys_config.data_provider.period = PRD.INTRADAY
            self.sys_config.data_provider.aggregation = aggregation
            graph, graph_key = self.__get_graph__(self._selected_ticker_id,
                                                  refresh_interval,
                                                  limit=300,
                                                  indicator=indicator)
            return graph

        @self.app.callback(
            Output('my_graph_portfolio_position_second_div', DSHVT.CHILDREN),
            [Input('my_graph_portfolio_position_div', DSHVT.CHILDREN)], [
                State('my_portfolio_aggregation', DSHVT.VALUE),
                State('my_portfolio_refresh_interval_selection', DSHVT.VALUE),
                State('my_portfolio_graph_second_days_selection', DSHVT.VALUE),
                State('my_portfolio_indicator_selection', DSHVT.VALUE)
            ])
        def handle_callback_for_graph_second(graph_first,
                                             aggregation_first: int,
                                             refresh_interval: int,
                                             range_list: list, indicator: str):
            if self._selected_ticker_id == '' or len(range_list) == 0:
                return ''
            graph_list = []
            sorted_range_list = sorted(range_list)
            for graph_range in sorted_range_list:
                if graph_range == 1:
                    self.sys_config.data_provider.period = PRD.INTRADAY
                    self.sys_config.data_provider.aggregation = {
                        5: 15,
                        15: 30,
                        30: 15
                    }.get(aggregation_first, 30)
                    graph, key = self.__get_graph__(self._selected_ticker_id,
                                                    refresh_interval,
                                                    limit=300,
                                                    indicator=indicator)
                else:
                    self.sys_config.data_provider.period = PRD.DAILY
                    graph, key = self.__get_graph__(self._selected_ticker_id,
                                                    refresh_interval,
                                                    graph_range, indicator)
                graph_list.append(graph)
            return graph_list

    def __init_callback_for_portfolio_markdown__(self):
        @self.app.callback(Output('my_portfolio_markdown', DSHVT.CHILDREN),
                           [Input('my_position_markdown', DSHVT.CHILDREN)])
        def handle_callback_for_portfolio_markdown(children):
            return self.__get_portfolio_markdown__()

    def __init_callback_for_portfolio_table__(self):
        @self.app.callback(Output(self._data_table_div, DSHVT.CHILDREN), [
            Input('my_position_markdown', DSHVT.CHILDREN),
            Input(self._button_handler.my_portfolio_reset_button,
                  DSHVT.N_CLICKS),
            Input('my_portfolio_active_manage_button', DSHVT.N_CLICKS)
        ])
        def handle_callback_for_positions_options(children,
                                                  reset_n_clicks: int,
                                                  n_clicks: int):
            if self._reset_portfolio_selection_clicks != reset_n_clicks:
                self._reset_portfolio_selection_clicks = reset_n_clicks
                self.__init_selected_row__([])
            elif n_clicks > self._active_manage_button_clicks:
                self._active_manage_button_clicks = n_clicks
                self.__toggle_flag_for_active_managed__()
            return self.__get_table_for_portfolio__()

    def __init_callback_for_reset_portfolio_button__(self):
        @self.app.callback(
            Output(self._button_handler.my_portfolio_reset_button_div,
                   DSHVT.STYLE),
            [Input(self._data_table_name, DSHVT.SELECTED_ROW_INDICES)])
        def handle_callback_for_reset_portfolio_button(
                selected_row_indices: list):
            if selected_row_indices is not None and len(
                    selected_row_indices) > 0:
                return self._button_handler.get_style_display(
                    PFBTN.RESET_PORTFOLIO_SELECTION)
            return {'display': 'none'}

    def __fill_thread_filled_graph_dict__(self, rows: list,
                                          refresh_interval: int, limit: int,
                                          indicator: str):
        if rows is None:
            return
        for asset_name in [row[PDC.ASSET] for row in rows]:
            ticker_id = '{}{}'.format(asset_name, 'USD')
            if ticker_id in self._selected_ticker_count_dict:
                if self._selected_ticker_count_dict[
                        ticker_id] > 0:  # we pre-proceed only "used" symbols....
                    print('\nStart thread: {}'.format(ticker_id))
                    self._thread_pool.submit(self.__get_graph__, ticker_id,
                                             refresh_interval, limit,
                                             indicator)

    def __get_graph__(self,
                      ticker_id: str,
                      refresh_interval: int,
                      limit=0,
                      indicator=INDI.NONE):
        period = self.sys_config.period
        aggregation = self.sys_config.period_aggregation
        kwargs = {} if indicator == INDI.NONE else {
            'Aggregation': aggregation,
            'Indicator': indicator
        }
        graph_cache_id = self.sys_config.graph_cache.get_cache_id(
            ticker_id, period, aggregation, limit, **kwargs)
        if self.sys_config.graph_cache.is_valid_cached_object_available(
                graph_cache_id):
            pass
            # print('__get_graph__: get_from_cache: {}'.format(graph_cache_id))
        else:
            self.__add_calculated_graph_to_cache__(graph_cache_id, ticker_id,
                                                   period, aggregation, limit,
                                                   indicator, refresh_interval)
        graph = self.sys_config.graph_cache.get_cached_object_by_key(
            graph_cache_id)
        return graph, graph_cache_id

    def __add_calculated_graph_to_cache__(self, graph_cache_id: str,
                                          ticker_id: str, period: str,
                                          aggregation: int, limit: int,
                                          indicator: str,
                                          refresh_interval: int):
        date_start = MyDate.adjust_by_days(MyDate.get_datetime_object().date(),
                                           -limit)
        and_clause = "Date > '{}'".format(date_start)
        graph_title = self.sys_config.graph_cache.get_cache_title(
            ticker_id, period, aggregation, limit)
        detector = self._pattern_controller.get_detector_for_fibonacci_and_pattern(
            self.sys_config, ticker_id, and_clause, limit)
        graph_api = DccGraphApi(graph_cache_id, graph_title)
        graph_api.ticker_id = ticker_id
        graph_api.indicator = None if indicator == INDI.NONE else indicator
        graph_api.df = detector.pdh.pattern_data.df
        graph = self.__get_dcc_graph_element__(detector, graph_api)
        cache_api = self.sys_config.graph_cache.get_cache_object_api(
            graph_cache_id, graph, period, refresh_interval)
        self.sys_config.graph_cache.add_cache_object(cache_api)
        return graph

    def __update_selected_row_number_after_refresh__(self, rows: list):
        selected_row_id = self._selected_row[PDC.ASSET]
        for index, row in enumerate(rows):
            if row[PDC.ASSET] == selected_row_id:
                if self._selected_row_index != index:
                    print('...updated selected row number: old={} -> {}=new'.
                          format(self._selected_row_index, index))
                    self._selected_row_index = index
                return
        self.__init_selected_row__()  # we have to reset the selected row

    def __get_portfolio_markdown__(self):
        balances = self._trade_handler_online.balances
        text_list = [
            '_**{}**_: {:.2f} ({:.2f}): {:.2f}$'.format(
                b.asset, b.amount, b.amount_available, b.current_value)
            for b in balances
        ]
        return '  \n'.join(text_list)

    def __get_position_manage_button_text__(self) -> str:
        if self._selected_ticker_id_management == 'Yes':
            return PMBT.SWITCH_TO_NO_MANAGEMENT
        return PMBT.SWITCH_TO_ACTIVE_MANAGEMENT

    def __get_table_for_portfolio__(self):
        self.__set_portfolio_rows_for_data_table__()
        return MyDCC.data_table(
            self._data_table_name,
            rows=self._table_rows,
            selected_row_indices=[]
            if self._selected_row_index == -1 else [self._selected_row_index],
            style_cell_conditional=self.get_table_style_cell_conditional(),
            style_data_conditional=self.get_table_style_data_conditional(
                self._table_rows),
            columns=self.columns,
        )

    @staticmethod
    def get_table_style_cell_conditional() -> list:
        return [{
            'if': {
                'column_id': c
            },
            'textAlign': 'left'
        } for c in [PDC.EXCHANGE, PDC.ASSET]]

    def get_table_style_data_conditional(self, rows: list):
        column_id = PDC.CURRENT_TOTAL
        filter_green = '{{{}}}  > 1000'.format(column_id)
        filter_ivory = '{{{}}} < 1000'.format(column_id)
        table_style_data = [
            {
                'if': {
                    'column_id': column_id,
                    'filter': filter_green
                },
                'backgroundColor': 'green',
                'color': 'white'
            },
            {
                'if': {
                    'column_id': column_id,
                    'filter': filter_ivory
                },
                'backgroundColor': 'ivory',
                'color': 'black'
            },
        ]
        self.add_row_specific_styles_to_table_style_data(
            rows, table_style_data)
        return table_style_data

    @staticmethod
    def add_row_specific_styles_to_table_style_data(rows, table_style_data):
        column_id = PDC.AMOUNT_AVAILABLE
        for row in rows:
            if row[PDC.EXCHANGE] != '':
                amount = MyMath.get_float_for_string(row[PDC.AMOUNT])
                amount_available = MyMath.get_float_for_string(
                    row[PDC.AMOUNT_AVAILABLE])
                if amount != amount_available:
                    bg_color = 'bisque'
                    filter_color = '{{{}}}  eq "{}"'.format(
                        PDC.ASSET, row[PDC.ASSET])
                    table_style_data.append({
                        'if': {
                            'column_id': column_id,
                            'filter': filter_color
                        },
                        'backgroundColor': bg_color
                    })

    def __set_portfolio_rows_for_data_table__(self):
        if self._trade_handler_online.balances is None:
            self.__init_table_rows__()
        else:
            self.__set_table_rows_by_balance_and_old_values__()

    def __set_table_rows_by_balance_and_old_values__(self):
        rows_new = []
        for balance in self._trade_handler_online.balances:
            if balance.asset != 'USD':
                exchange, ticker_id = self.__get_exchange_and_ticker_id_for_row__(
                    balance.asset)
                rows_new.append({
                    PDC.EXCHANGE:
                    exchange,
                    PDC.ASSET:
                    balance.asset,
                    PDC.AMOUNT:
                    balance.amount,
                    PDC.AMOUNT_AVAILABLE:
                    balance.amount_available,
                    PDC.CURRENT_PRICE:
                    self._trade_handler_online.get_current_price_for_ticker_id(
                        ticker_id),
                    PDC.CURRENT_TOTAL:
                    balance.current_value,
                    PDC.ACTIVELY_MANAGED:
                    self.__get_flag_for_actively_managed__(balance.asset)
                })
        self._table_rows = rows_new

    def __get_exchange_and_ticker_id_for_row__(self, asset: str):
        if self.sys_config.index_config.is_symbol_crypto(asset):
            return 'Bitfinex', '{}USD'.format(asset)
        else:
            return 'IBKR', asset

    def __init_table_rows__(self):
        self._table_rows = [{col: '' for col in self.columns}]

    def __get_flag_for_actively_managed__(self, asset: str) -> str:
        for row in self._table_rows:
            if row[PDC.ASSET] == asset:
                return row[PDC.ACTIVELY_MANAGED]
        return 'No'

    def __toggle_flag_for_active_managed__(self):
        value = 'No' if self._selected_ticker_id_management == 'Yes' else 'Yes'
        self._selected_ticker_id_management = value
        self._table_rows[self._selected_row_index][
            PDC.ACTIVELY_MANAGED] = value
pattern_type_pattern_id_dict = {
    FT.CHANNEL: ['1_1_1_AXP_10_2018-05-22_00:00_2018-07-12_00:00'],
    FT.CHANNEL_DOWN: ['1_1_1_MMM_12_2018-03-12_00:00_2018-05-21_00:00'],
    FT.TRIANGLE: ['1_1_1_CVX_20_2017-10-24_00:00_2017-11-24_00:00'],
    # FT.TRIANGLE_TOP: ['1_1_1_XOM_23_2018-06-11_00:00_2018-07-26_00:00'],
    FT.TRIANGLE_BOTTOM: ['1_1_1_MRK_24_2018-02-16_00:00_2018-03-29_00:00'],
    FT.TRIANGLE_DOWN: ['1_1_1_AXP_22_2018-02-27_00:00_2018-04-05_00:00'],
    # FT.TKE_BOTTOM: [''],
    FT.FIBONACCI_DESC: ['1_1_1_KO_56_2018-01-26_00:00_2018-05-15_00:00'],
    FT.HEAD_SHOULDER_BOTTOM:
    ['1_1_1_JNJ_44_2018-04-16_00:00_2018-07-16_00:00'],
    FT.HEAD_SHOULDER_BOTTOM_DESC:
    ['1_1_1_MMM_46_2015-11-20_00:00_2016-02-24_00:00']
}

pattern_type_list_to_test = [FT.TRIANGLE_DOWN]
trade_strategy_dict_list = [{BT.BREAKOUT: [TSTR.LIMIT_FIX]}]

for pattern_type in pattern_type_list_to_test:
    sys_config.config.pattern_ids_to_find = pattern_type_pattern_id_dict[
        pattern_type]
    sys_config.config.pattern_ids_to_find = [
        '1_1_1_CAT_10_2017-10-31_00:00_2017-11-15_00:00'
    ]

    for trade_strategy_dict in trade_strategy_dict_list:
        sys_config.exchange_config.trade_strategy_dict = trade_strategy_dict
        pattern_controller = PatternDetectionController(sys_config)
        pattern_controller.run_pattern_detector()
Esempio n. 9
0
 def get_pattern_detector_for_replay(self, api: TradeTestApi) -> PatternDetector:
     self.__adjust_sys_config_for_replay__(api)
     pattern_controller = PatternDetectionController(self.sys_config)
     return pattern_controller.get_detector_for_dash(self.sys_config, api.symbol, api.and_clause)
Esempio n. 10
0
 def __get_pattern_list_for_back_testing__(self, api: TradeTestApi):
     pattern_controller = PatternDetectionController(self.sys_config)
     detector = pattern_controller.get_detector_for_dash(self.sys_config, api.symbol, api.and_clause)
     return detector.get_pattern_list_for_back_testing()
 def __init__(self, sys_config=None):
     self.sys_config = SystemConfiguration(
     ) if sys_config is None else sys_config
     self.db_stock = self.sys_config.db_stock
     self.pattern_controller = PatternDetectionController(self.sys_config)
class StockDatabaseUpdater:
    def __init__(self, sys_config=None):
        self.sys_config = SystemConfiguration(
        ) if sys_config is None else sys_config
        self.db_stock = self.sys_config.db_stock
        self.pattern_controller = PatternDetectionController(self.sys_config)

    def calculate_index_list(self, index_list: list, save_to_database=False):
        for index in index_list:
            print('\nCalculate index values for {}...'.format(index))
            index_handler = IndexHandler(index,
                                         self.db_stock,
                                         save_to_database=save_to_database)
            index_handler.calculate_index_data_frame()

    def delete_inconsistent_wave_records(self, scheduled_job=False) -> int:
        """
        We have to take care that the waves are reasonable to avoid problems with the fibonacci predictions:
        :param scheduled_job:
        :return:
        """
        print('Delete inconsistent wave records..')
        access_layer_wave = AccessLayer4Wave(self.db_stock)
        df_wave_to_process = access_layer_wave.get_inconsistent_waves_as_data_frame(
        )
        delete_counter = 0
        for wave_index, wave_row in df_wave_to_process.iterrows():
            access_layer_wave.delete_record_by_rowid(wave_row[DC.ROWID])
            delete_counter += 1
        if scheduled_job:
            self.sys_config.file_log.log_scheduler_process(
                log_message='Deleted: {}/{}'.format(
                    delete_counter, df_wave_to_process.shape[0]),
                process='Update wave records',
                process_step='delete_inconsistent_wave_records')
        return delete_counter

    def add_wave_end_data_to_wave_records(self,
                                          symbol='',
                                          ts_start=0,
                                          ts_end=0,
                                          scheduled_job=False) -> int:
        """
        Some attributes have to be calculated AFTER the waves completes:
        DC.WAVE_END_FLAG, DC.WAVE_MAX_RETR_PCT, DC.WAVE_MAX_RETR_TS_PCT
        """
        print('Add wave end data to wave records..')
        access_layer_wave = AccessLayer4Wave(self.db_stock)
        access_layer_stocks = AccessLayer4Stock(self.db_stock)
        df_wave_to_process = access_layer_wave.get_wave_data_frame_without_end_data(
            symbol, ts_start, ts_end)
        ts_start_stocks = ts_start if ts_end == 0 else ts_end
        df_stocks = access_layer_stocks.get_stocks_data_frame_for_wave_completing(
            symbol=symbol, ts_start=ts_start_stocks)
        tolerance = 0.01
        update_counter = 0
        ts_now = MyDate.time_stamp_now()
        for wave_index, wave_row in df_wave_to_process.iterrows():
            wave_entity = WaveEntity(wave_row)
            ts_start, ts_end = wave_entity.get_ts_start_end_for_check_period()
            ts_start_dt = MyDate.get_date_from_epoch_seconds(ts_start)
            ts_end_dt = MyDate.get_date_from_epoch_seconds(ts_end)
            wave_end_value, wave_value_range = wave_entity.wave_end_value, wave_entity.wave_value_range
            if ts_end < ts_now:
                wave_end_reached = 1
                max_retracement = 0
                max_retracement_ts = ts_start
                max_retracement_dt = ts_start_dt
                df_filtered_stocks = df_stocks[np.logical_and(
                    df_stocks[DC.SYMBOL] == wave_entity.symbol,
                    np.logical_and(df_stocks[DC.TIMESTAMP] > ts_start,
                                   df_stocks[DC.TIMESTAMP] <= ts_end))]
                for index_stocks, row_stocks in df_filtered_stocks.iterrows():
                    row_low, row_high, row_ts = row_stocks[DC.LOW], row_stocks[
                        DC.HIGH], row_stocks[DC.TIMESTAMP]
                    if wave_entity.wave_type == FD.ASC:
                        if wave_end_value < row_high * (1 - tolerance):
                            wave_end_reached = 0
                            break
                        else:
                            retracement = wave_end_value - row_low
                    else:
                        if wave_end_value > row_low * (1 + tolerance):
                            wave_end_reached = 0
                            break
                        else:
                            retracement = row_high - wave_end_value
                    if retracement > max_retracement:
                        max_retracement = round(retracement, 2)
                        max_retracement_ts = row_ts
                        max_retracement_dt = row_stocks[DC.DATE]
                max_retracement_pct = round(
                    max_retracement / wave_value_range * 100, 2)
                max_retracement_ts_pct = round(
                    (max_retracement_ts - ts_start) /
                    wave_entity.wave_ts_range * 100, 2)
                data_dict = {
                    DC.WAVE_END_FLAG: wave_end_reached,
                    DC.WAVE_MAX_RETR_PCT: max_retracement_pct,
                    DC.WAVE_MAX_RETR_TS_PCT: max_retracement_ts_pct
                }
                access_layer_wave.update_record(wave_entity.row_id, data_dict)
                update_counter += 1
        if scheduled_job:
            self.sys_config.file_log.log_scheduler_process(
                log_message='Updated: {}/{}'.format(
                    update_counter, df_wave_to_process.shape[0]),
                process='Update wave records',
                process_step='add_wave_end_data')
        return update_counter

    def add_wave_prediction_data_to_wave_records(self,
                                                 symbol='',
                                                 ts_start=0,
                                                 ts_end=0,
                                                 scheduled_job=False):
        """
        This job calculates the prediction data for all kind of waves (daily and intraday
        """
        print('Add prediction data to wave records..')
        access_layer_wave = AccessLayer4Wave(self.db_stock)
        df_wave_to_process = access_layer_wave.get_wave_data_without_prediction_data(
            symbol, ts_start, ts_end)
        update_counter = 0
        for wave_index, wave_row in df_wave_to_process.iterrows():
            wave_entity = WaveEntity(wave_row)
            x_data = wave_entity.data_list_for_prediction_x_data
            prediction_dict = self.sys_config.fibonacci_predictor.get_prediction_as_dict(
                x_data)
            data_dict = {}
            FibonacciWave.update_dict_by_prediction_dict(
                prediction_dict, data_dict)
            access_layer_wave.update_record(wave_entity.row_id, data_dict)
            update_counter += 1

        if scheduled_job:
            self.sys_config.file_log.log_scheduler_process(
                log_message='Updated: {}/{}'.format(
                    update_counter, df_wave_to_process.shape[0]),
                process='Update wave records',
                process_step='add_wave_prediction')

    def update_equity_records(self) -> StockDatabaseUpdateJobResult:
        result_obj = StockDatabaseUpdateJobResult()
        access_layer = AccessLayer4Equity(self.db_stock)
        dt_today = MyDate.get_date_from_datetime()
        # dt_today = MyDate.adjust_by_days(dt_today, 40)
        dt_valid_until = MyDate.adjust_by_days(dt_today, 30)
        dt_today_str = str(dt_today)
        dt_valid_until_str = str(dt_valid_until)
        exchange_equity_type_dict = self.__get_equity_dict__()
        result_obj.number_processed_records = len(exchange_equity_type_dict)
        for exchange, equity_type in exchange_equity_type_dict.items():
            value_dict = access_layer.get_index_dict(exchange)
            if access_layer.are_any_records_available_for_date(
                    dt_today, exchange, equity_type):
                result_obj.number_deleted_records += access_layer.delete_existing_equities(
                    equity_type, exchange)
                sleep(2)
                self.__update_equity_records_for_equity_type__(
                    access_layer, dt_today_str, dt_valid_until_str, exchange,
                    equity_type)
                result_obj.number_saved_records += 1
        return result_obj

    @staticmethod
    def __get_equity_dict__():
        return {
            TRC.BITFINEX: EQUITY_TYPE.CRYPTO,
            INDICES.DOW_JONES: EQUITY_TYPE.SHARE,
            INDICES.NASDAQ100: EQUITY_TYPE.SHARE,
            INDICES.FOREX: EQUITY_TYPE.CURRENCY
        }

    def __update_equity_records_for_equity_type__(
            self, access_layer: AccessLayer4Equity, dt_today_str: str,
            dt_valid_until_str: str, exchange: str, equity_type: str):
        # a) get symbol - name dictionaries
        if equity_type == EQUITY_TYPE.SHARE:
            index = exchange
        elif equity_type == EQUITY_TYPE.CURRENCY:
            index = INDICES.FOREX
        else:
            index = INDICES.CRYPTO_CCY
        source_data_dict = self.sys_config.index_config.get_ticker_dict_for_index(
            index)

        # b) get existing records
        existing_data_dict = access_layer.get_existing_equities_as_data_dict(
            equity_type, exchange)

        # b) none available => get new ones
        for key, value in source_data_dict.items():
            if key in existing_data_dict:
                pass
            else:
                data_dict = {
                    EDC.EQUITY_KEY: key,
                    EDC.EQUITY_NAME: value,
                    EDC.VALID_FROM_DT: dt_today_str,
                    EDC.VALID_TO_DT: dt_valid_until_str,
                    EDC.EXCHANGE: exchange,
                    EDC.EQUITY_TYPE: equity_type,
                    EDC.EQUITY_STATUS: EST.ACTIVE
                }
                self.db_stock.insert_equity_data([data_dict])

    def update_trade_records(self,
                             mean: int,
                             sma_number: int,
                             trade_strategies: dict = None,
                             pattern_end_date=str):
        if pattern_end_date == '':
            pattern_end_date = str(MyDate.adjust_by_days(
                None, -90))  # only the ones which are 3 month back
        self.sys_config.init_detection_process_for_automated_trade_update(
            mean, sma_number)
        if trade_strategies is None:
            trade_strategies = {
                BT.BREAKOUT: [
                    TSTR.LIMIT, TSTR.LIMIT_FIX, TSTR.TRAILING_STOP,
                    TSTR.TRAILING_STEPPED_STOP
                ]
            }
        for pattern_type in FT.get_long_trade_able_types():
            where_clause = "pattern_type = '{}' and period = 'DAILY' and trade_type in ('', 'long')".format(
                pattern_type)
            if pattern_end_date != '':
                where_clause += " AND {} > '{}'".format(
                    DC.PATTERN_END_DT, pattern_end_date)
            df_pattern_for_pattern_type = self.db_stock.get_pattern_records_as_dataframe(
                where_clause)
            pattern_id_dict = {
                row[DC.ID]: row[DC.TRADE_TYPE]
                for index, row in df_pattern_for_pattern_type.iterrows()
            }
            # pattern_id_list = ['20_1_1_LTCUSD_20_2016-11-02_00:00_2016-12-04_00:00']
            counter = 0
            for pattern_id, trade_type in pattern_id_dict.items():
                counter += 1
                run_detail = '{} ({:03d} of {:03d}): {}'.format(
                    pattern_type, counter, len(pattern_id_dict), pattern_id)
                result_dict = self.db_stock.get_missing_trade_strategies_for_pattern_id(
                    pattern_id, trade_strategies, mean, sma_number)
                for buy_trigger, trade_strategy_list in result_dict.items():
                    if len(trade_strategy_list) == 0:
                        print('{}: OK'.format(run_detail))
                    else:
                        print('{}: processing...'.format(run_detail))
                        self.sys_config.config.pattern_ids_to_find = [
                            pattern_id
                        ]
                        self.sys_config.exchange_config.trade_strategy_dict = {
                            buy_trigger: trade_strategy_list
                        }
                        pattern_controller = PatternDetectionController(
                            self.sys_config)
                        pattern_controller.run_pattern_detector()
                if trade_type == '':
                    self.db_stock.update_trade_type_for_pattern(
                        pattern_id, TRT.LONG)

    def update_pattern_data_by_index_for_daily_period(self,
                                                      index: str,
                                                      start_after=''):
        print('Update pattern data for index: {}'.format(index))
        self.sys_config.init_detection_process_for_automated_pattern_update()
        self.sys_config.data_provider.use_index(index)
        if start_after != '':
            self.sys_config.data_provider.start_after(start_after)
        pattern_controller = PatternDetectionController(self.sys_config)
        pattern_controller.run_pattern_detector()

    def update_wave_data_by_index_for_daily_period(self,
                                                   index: str,
                                                   limit: int,
                                                   start_after='',
                                                   last_days=0):
        ticker_dic = self.__get_configured_ticker_dict_for_index__(index)
        for ticker in ticker_dic:
            if start_after == '' or ticker > start_after:
                self.update_wave_records_for_daily_period(
                    ticker, limit, last_days)
            else:
                print('Excluded from current run: {}'.format(ticker))

    def update_wave_records_for_daily_period(self, ticker_id: str, limit: int,
                                             last_days: int):
        self.sys_config.config.save_wave_data = True
        self.sys_config.data_provider.period = PRD.DAILY
        self.sys_config.data_provider.from_db = True
        for k in range(0, last_days + 1):
            date_end = MyDate.adjust_by_days(
                MyDate.get_datetime_object().date(), -k)
            date_start = MyDate.adjust_by_days(
                MyDate.get_datetime_object().date(), -limit - k)
            and_clause = "Date > '{}' AND Date <= '{}'".format(
                date_start, date_end)
            print('update_wave_records_for_daily_period: {} for {}'.format(
                ticker_id, and_clause))
            if self.sys_config.db_stock.is_symbol_loaded(
                    ticker_id, and_clause=and_clause):
                detector = self.pattern_controller.get_detector_for_fibonacci(
                    self.sys_config, ticker_id, and_clause, limit)
                detector.save_wave_data()
            else:
                print('No data available for {} and {}'.format(
                    ticker_id, and_clause))

    def update_wave_data_by_index_for_intraday(self,
                                               index: str,
                                               aggregation: int = 30,
                                               offset_day_range: int = 0,
                                               start_after=""):
        if index == INDICES.Q_FSE:
            aggregation = 30  # we use the data from  https://stooq.com/db/h/ which are only available in 5 min steps
        print('Update wave data for index: {} ({}min)'.format(
            index, aggregation))
        ticker_dict = self.__get_configured_ticker_dict_for_index__(index)
        for ticker in ticker_dict:
            try:
                if start_after == '' or ticker > start_after:
                    self.update_wave_records_for_intraday(
                        ticker, aggregation, offset_day_range)
            except:
                self.sys_config.file_log.log_error()
            # break  # ToDo get rid when test finished

    def __get_configured_ticker_dict_for_index__(self, index):
        if index == INDICES.CRYPTO_CCY:
            ticker_dic = self.sys_config.index_config.get_ticker_dict_for_index(
                index,
                ticker_id_list=self.sys_config.exchange_config.ticker_id_list)
        else:
            ticker_dic = self.sys_config.index_config.get_ticker_dict_for_index(
                index)
        return ticker_dic

    def update_wave_records_for_intraday(self, ticker_id: str,
                                         aggregation: int,
                                         offset_day_range: int):
        self.sys_config.config.save_wave_data = True
        self.sys_config.data_provider.period = PRD.INTRADAY
        self.sys_config.data_provider.aggregation = aggregation
        self.sys_config.data_provider.from_db = False
        for offset in range(0, offset_day_range + 1):
            detector = self.pattern_controller.get_detector_for_fibonacci(
                self.sys_config, ticker_id, offset=offset)
            detector.save_wave_data()

    def update_trade_policy_metric_for_today(self, pattern_type_list: list):
        print("\nSTARTING 'update_trade_policy_metric_for_today' for {}...".
              format(pattern_type_list))
        access_layer = AccessLayer4TradePolicyMetric(self.db_stock)
        dt_today_str = str(MyDate.get_date_from_datetime())
        check_dict = {TPMDC.VALID_DT: dt_today_str}
        if access_layer.are_any_records_available(check_dict):
            print(
                "END 'update_trade_policy_metric_for_today': No updates for today\n"
            )
            # return
        policy_list = TradePolicyFactory.get_trade_policies_for_metric_calculation(
        )
        period_list = [PRD.DAILY]
        mean_aggregation_list = [4, 8, 16]
        for pattern_type in pattern_type_list:
            for period in period_list:
                for mean_aggregation in mean_aggregation_list:
                    policy_handler = TradePolicyHandler(
                        pattern_type=pattern_type,
                        period=period,
                        mean_aggregation=mean_aggregation)
                    if policy_handler.are_enough_data_available():
                        for policy in policy_list:
                            rewards, entity_counter = policy_handler.run_policy(
                                policy, False)
                            insert_dict = {
                                TPMDC.VALID_DT:
                                dt_today_str,
                                TPMDC.POLICY:
                                policy.policy_name,
                                TPMDC.EPISODES:
                                1,
                                TPMDC.PERIOD:
                                period,
                                TPMDC.AGGREGATION:
                                1,
                                TPMDC.TRADE_MEAN_AGGREGATION:
                                mean_aggregation,
                                TPMDC.PATTERN_TYPE:
                                pattern_type,
                                TPMDC.NUMBER_TRADES:
                                entity_counter,
                                TPMDC.REWARD_PCT_TOTAL:
                                round(rewards, 2),
                                TPMDC.REWARD_PCT_AVG:
                                round(rewards / entity_counter, 2),
                            }
                            access_layer.insert_data([insert_dict])
        print("END 'update_trade_policy_metric_for_today'\n")

    def handle_transaction_problems(self):
        print("\nSTARTING 'handle_transaction_problems'")
        line_to_keep_list = []
        file_path = self.sys_config.file_log.get_file_path_for_log_type(
            LOGT.TRANSACTIONS)
        file_with_transactions = MyFile(file_path)
        lines_as_list = file_with_transactions.get_lines_as_list()
        for line in lines_as_list:
            log_line = FileLogLine(line)
            # line_to_keep_list.append(line)
            if log_line.is_valid:
                table_name = log_line.process
                data_str_dict_list_as_string = log_line.step
                data_str_dict_list_as_string = data_str_dict_list_as_string.replace(
                    '#', ',')
                data_str_dict_list = MyText.get_list_from_text(
                    data_str_dict_list_as_string)
                data_dict_list = [
                    MyText.get_dict_from_text(dict_str)
                    for dict_str in data_str_dict_list
                ]
                print('Handle_transaction_problem for table {}: {}'.format(
                    table_name, data_dict_list))
                self.db_stock.correct_data_types_withing_data_dict_list(
                    table_name, data_dict_list)
                data_dict_list = self.db_stock.remove_existing_entries_from_data_dict_list(
                    table_name, data_dict_list)
                if len(data_dict_list) > 0:
                    inserted = self.db_stock.insert_data_into_table(
                        table_name, data_dict_list)
                    if inserted <= 0:
                        line_to_keep_list.append(line)
            else:
                print('{}: Line not valid in log file: {}'.format(
                    file_path, line))
        file_with_transactions.replace_file_when_changed(line_to_keep_list)
        print("END 'handle_transaction_problems'\n")

    def fill_asset_gaps(self, ts_last: int, ts_to: int, ts_interval: int):
        access_layer_stocks = AccessLayer4Stock(self.db_stock)
        last_asset_query = 'SELECT * FROM asset WHERE Validity_Timestamp={}'.format(
            ts_to)
        df_last_asset = AccessLayer4Asset(
            self.db_stock).select_data_by_query(last_asset_query)
        ts_actual = ts_last + ts_interval
        while ts_actual < ts_to:
            dt_saving = str(MyDate.get_date_time_from_epoch_seconds(ts_actual))
            for index, asset_row in df_last_asset.iterrows():
                equity_type = asset_row[DC.EQUITY_TYPE]
                asset = asset_row[DC.EQUITY_NAME]
                amount = asset_row[DC.QUANTITY]
                value_total = asset_row[DC.VALUE_TOTAL]
                balance = Balance('exchange', asset, amount, amount)
                if equity_type == EQUITY_TYPE.CASH:
                    balance.current_value = value_total
                else:
                    if equity_type == EQUITY_TYPE.CRYPTO:
                        asset = asset + 'USD'
                    current_price = access_layer_stocks.get_actual_price_for_symbol(
                        asset, ts_actual)
                    balance.current_value = value_total if current_price == 0 else round(
                        amount * current_price, 2)
                data_dict = AssetDataDictionary(
                ).get_data_dict_for_target_table_for_balance(
                    balance, ts_actual, dt_saving)
                self.db_stock.insert_asset_entry(data_dict)
            ts_actual += ts_interval
Esempio n. 13
0
class MyDashTab4Pattern(MyPatternDashBaseTab):
    def __init__(self, app: Dash, sys_config: SystemConfiguration, trade_handler_online: PatternTradeHandler):
        MyPatternDashBaseTab.__init__(self, app, sys_config)
        self.bitfinex_config = self.sys_config.exchange_config
        self.trade_handler_online = trade_handler_online
        self.sys_config_second = sys_config.get_semi_deep_copy()
        self.sys_config_fibonacci = self.sys_config.get_semi_deep_copy()
        self._pattern_controller = PatternDetectionController(self.sys_config, TP.ONLINE)
        self.detector = None
        self._index_options = self.sys_config.index_config.get_indices_as_options()
        self._ticker_options = []
        self._ticker_selected = ''
        self.__fill_ticker_options__()
        self._dd_handler = PatternTabDropDownHandler(self._index_options, self._ticker_options)
        self._time_stamp_next_refresh = None
        self._graph_first_data_provider_api = None
        self._graph_second_data_provider_api = None
        self._graph_first_cache = MyGraphCache('my_graph_first')
        self._graph_second_cache = MyGraphCache('my_graph_second')
        self._state_handler = MyDashStateHandler(self._ticker_options)
        self._graph_key_first = ''
        self._detector_first = None
        self._pattern_data_first = None
        self._balance_saving_times = MyDate.get_epoch_seconds_for_current_day_as_list()

    def init_callbacks(self):
        self.__init_interval_callback_for_interval_details__()
        self.__init_interval_setting_callback__()
        self.__init_callback_for_stock_symbol_options__()
        self.__init_callback_for_position_markdown__()
        self.__init_callback_for_dashboard_markdown__()
        self.__init_callback_for_pattern_markdown__()
        self.__init_callback_for_graph_first__()
        self.__init_callback_for_graph_second__()
        self.__init_callback_for_graphs_before_breakout__()
        self.__init_hover_over_callback__()
        self.__init_selection_callback__()
        self.__init_ticker_selection_callback__()
        # self.__init_callback_for_system_configuration_table__()

    def __init_callback_for_system_configuration_table__(self):
        @self.app.callback(
            Output('my_table_SystemConfiguration', 'children'),
            [Input('my_period_aggregation', 'value')])
        def handle_callback_for_system_configuration_table(aggregation: str):
            print('_aggregation = {}, children={}'.format(aggregation, ''))
            return MyHTMLSystemConfigurationTable(self.sys_config).get_table()

    def get_div_for_tab(self):
        # print('MyHTMLHeaderTable.get_table={}'.format(MyHTMLHeaderTable().get_table()))
        li = [MyHTMLTabPatternHeaderTable().get_table()]
        li.append(MyHTML.div_with_dcc_drop_down(**self._dd_handler.get_drop_down_parameters(PDD.INDEX))),
        li.append(MyHTML.div_with_dcc_drop_down(**self._dd_handler.get_drop_down_parameters(PDD.STOCK_SYMBOL)))
        li.append(MyHTML.div_with_dcc_drop_down(**self._dd_handler.get_drop_down_parameters(
            PDD.PERIOD_AGGREGATION, default_value=self.sys_config.period_aggregation)))
        li.append(MyHTML.div_with_dcc_drop_down(**self._dd_handler.get_drop_down_parameters(PDD.REFRESH_INTERVAL)))
        li.append(MyHTML.div_with_dcc_drop_down(**self._dd_handler.get_drop_down_parameters(PDD.SECOND_GRAPH_RANGE)))
        if self.sys_config.from_db:
            li.append(self.__get_html_div_with_date_picker_range__())
        li.append(MyHTML.div_with_html_button_submit('my_refresh_button', 'Refresh'))
        li.append(MyHTML.div('my_graph_first_div'))
        li.append(MyHTML.div('my_graph_second_div'))
        li.append(MyHTML.div('my_graphs_before_breakout_div'))
        # li.append(MyHTML.div_embedded('my_graphs_before_breakout_div'))
        li.append(MyHTML.div_with_html_pre('my_hover_data'))
        return MyHTML.div('', li)

    @staticmethod
    def __get_html_div_with_date_picker_range__():
        return html.Div(
            [
                MyHTML.h3('Select start and end dates:'),
                MyDCC.get_date_picker_range('my_date_picker', datetime.today() - timedelta(days=160))
            ],
            style={'display': 'inline-block', 'vertical-align': 'bottom', 'height': 20}
        )

    def __init_callback_for_position_markdown__(self):
        @self.app.callback(
            Output('my_position_markdown', 'children'),
            [Input('my_interval_refresh', 'n_intervals'),
             Input('my_reset_total_result_button', 'n_clicks')])
        def handle_callback_for_position_markdown(n_intervals: int, reset_n_clicks: int):
            # self.__add_fibonacci_waves_to_news__()
            # self.__add_bollinger_band_breaks_to_news__()
            reset_button_clicked = self._state_handler.change_for_my_reset_total_result_button(reset_n_clicks)
            markdown_text = self.__get_position_markdown__(n_intervals, reset_button_clicked)
            if not reset_button_clicked:  # we don't want to save the data when the reset button is clicked
                self.__save_balances_to_database__()  # this must be after __get_position_markdown__ !!!
            return markdown_text

    def __save_balances_to_database__(self):
        if len(self._balance_saving_times) == 0:  # already all done
            return
        processed_list = []
        ts_now = MyDate.get_epoch_seconds_from_datetime()
        for ts_saving in self._balance_saving_times:
            if ts_saving <= ts_now:
                dt_saving = MyDate.get_date_time_from_epoch_seconds_as_string(ts_saving)
                self.__save_balances__(ts_saving, dt_saving)
                processed_list.append(ts_saving)
        for values in processed_list:
            self._balance_saving_times.remove(values)

    def __save_balances__(self, ts_saving: int, dt_saving: str):
        if self.sys_config.db_stock.is_any_asset_already_available_for_timestamp(ts_saving):
            return
        for position in self.trade_handler_online.balances:
            data_dict = AssetDataDictionary().get_data_dict_for_target_table_for_balance(position, ts_saving, dt_saving)
            self.sys_config.db_stock.insert_asset_entry(data_dict)

    def __init_callback_for_dashboard_markdown__(self):
        @self.app.callback(
            Output('my_dashboard_markdown', 'children'),
            [Input('my_interval_timer', 'n_intervals')])
        def handle_callback_for_dashboard_markdown(n_intervals: int):
            return self.__get_dashboard_markdown__(n_intervals)

    def __get_dashboard_markdown__(self, n_intervals: int):
        news = self.__get_markdown_news__()
        trades = self.__get_markdown_trades__()
        statistics = self.__get_markdown_statistics__()
        text = '- _**News**_: {}\n- _**Trades**_: {}\n - _**Daily statistics**_: {}'.format(news, trades, statistics)
        return text

    def __get_position_markdown__(self, n_intervals: int, reset_button_clicked: bool):
        text = dedent('''{}''').format(self.__get_position_markdown_for_active_positions__(reset_button_clicked))
        return text

    def __get_position_markdown_for_active_positions__(self, reset_button_clicked: bool):
        balances = self.trade_handler_online.get_balances_with_current_values()
        self.trade_handler_online.balances = balances
        text_list = ['_**{}**_: {:,.0f} ({:,.0f}): {:,.0f}$'.format(
                b.asset, b.amount, b.amount_available, b.current_value) for b in balances]
        total_value = sum([balance.current_value for balance in balances])
        if reset_button_clicked:  # reset the value_total_start
            self.trade_handler_online.value_total_start = total_value
        text_list.append(self.__get_position_total__(total_value))
        return '  \n'.join(text_list)

    def __get_position_total__(self, total_value: float):
        if self.trade_handler_online.value_total_start == 0:
            self.trade_handler_online.value_total_start = total_value
        diff_value = total_value - self.trade_handler_online.value_total_start
        diff_pct = diff_value/self.trade_handler_online.value_total_start*100
        return '_**Total**_: {:,.0f}$ ({:+,.0f}$ / {:+.2f}%)'.format(total_value, diff_value, diff_pct)

    def __get_markdown_news__(self):
        return self._news_handler.get_news_for_markdown_since_last_refresh(self._time_stamp_last_refresh)

    def __add_fibonacci_waves_to_news__(self):
        # example: Fibonacci: BTCUSD (15min): descending - last tick at 10:30:00
        sys_config = self.sys_config_fibonacci
        indicator_list = self.bitfinex_config.get_fibonacci_indicators()
        result_list = []
        # indicator_list = [['XMRUSD', 15]]
        for indicators in indicator_list:
            sys_config.init_by_indicator(indicators)
            detector = self._pattern_controller.get_detector_for_fibonacci(sys_config, indicators[0])
            for fib_wave in detector.fib_wave_tree.fibonacci_wave_list:
                aggregation = indicators[1]
                if fib_wave.is_wave_indicator_for_dash(aggregation):
                    result_list.append('{} ({}min): {}'.format(
                        indicators[0], aggregation, fib_wave.get_details_as_dash_indicator()))
            for fib_wave in detector.fib_wave_tree.get_actual_forecast_wave_list():
                aggregation = indicators[1]
                result_list.append('{} ({}min): {}'.format(
                    indicators[0], aggregation, fib_wave.get_details_as_dash_forecast_indicator()))
        if len(result_list) > 0:
            self.sys_config.sound_machine.play_alarm_fibonacci()
            self._news_handler.add_news('Fibonacci', ', '.join(result_list))

    def __add_bollinger_band_breaks_to_news__(self):
        # example: Bollinger break: BTCUSD (15min): last tick at 10:30:00
        sys_config = self.sys_config_fibonacci
        indicator_list = self.bitfinex_config.get_bollinger_band_indicators()
        result_list = []
        # indicator_list = [['XMRUSD', 15]]
        for indicators in indicator_list:
            sys_config.init_by_indicator(indicators)
            detector = self._pattern_controller.get_detector_for_bollinger_band(sys_config, indicators[0])
            bollinger_bound_break_direction = detector.pdh.get_bollinger_band_boundary_break_direction()
            if bollinger_bound_break_direction != '':
                aggregation = indicators[1]
                msg = '{} broken'.format(bollinger_bound_break_direction)
                result_list.append('{} ({}min): {}'.format(indicators[0], aggregation, msg))
        if len(result_list) > 0:
            self.sys_config.sound_machine.play_alarm_bollinger_band_break()
            self._news_handler.add_news('Bollinger Band', ', '.join(result_list))

    def __get_markdown_trades__(self):
        return '- none -'

    def __get_markdown_statistics__(self):
        return '- none -'

    def __init_interval_callback_for_interval_details__(self):
        @self.app.callback(
            Output('my_last_refresh_time_div', 'children'),
            [Input('my_interval_refresh', 'n_intervals')])
        def handle_interval_callback_for_last_refresh(n_intervals):
            # self._news_handler.clear()
            self._time_stamp_last_refresh = MyDate.time_stamp_now()
            last_refresh_dt = MyDate.get_time_from_datetime(datetime.now())
            return '{} ({})'.format(last_refresh_dt, n_intervals)

        @self.app.callback(
            Output('my_next_refresh_time_div', 'children'),
            [Input('my_interval_refresh', 'n_intervals'), Input('my_interval_refresh', 'interval')])
        def handle_interval_callback_for_next_refresh(n_intervals, interval_ms):
            dt_next = datetime.now() + timedelta(milliseconds=interval_ms)
            self._time_stamp_next_refresh = int(dt_next.timestamp())
            return '{}'.format(MyDate.get_time_from_datetime(dt_next))

    def __init_interval_setting_callback__(self):
        @self.app.callback(
            Output('my_interval_refresh', 'interval'),
            [Input('my_interval_selection', 'value')])
        def handle_interval_setting_callback(interval_selected):
            print('Refresh interval set to: {}'.format(interval_selected))
            return interval_selected * 1000

    def __init_callback_for_stock_symbol_options__(self):
        @self.app.callback(
            Output('my_pattern_ticker_selection', 'options'),
            [Input('my_pattern_index_selection', 'value')])
        def handle_callback_for_stock_symbol_options(selected_index: str):
            # print('__init_callback_for_stock_symbol_options__: selected_index={}'.format(selected_index))
            self.sys_config.data_provider.use_index(selected_index)
            self.__fill_ticker_options__()
            # print('__init_callback_for_stock_symbol_options__: options={}'.format(self._ticker_options))
            return self._ticker_options

    def __init_callback_for_pattern_markdown__(self):
        @self.app.callback(
            Output('my_pattern_markdown', 'children'),
            [Input('my_graph_first_div', 'children')])
        def handle_callback_for_ticket_markdown(children):
            annotation = self.__get_annotation_for_markdown__()
            wave_tick_list = self.trade_handler_online.get_latest_tickers_as_wave_tick_list(self._ticker_selected)
            text_list = [wave_tick_list.get_markdown_text_for_second_last_wave_tick(),
                         wave_tick_list.get_markdown_text_for_last_wave_tick()]
            if annotation != '':
                annotation = MyText.get_text_for_markdown(annotation)
                text_list.append('**Annotations (next breakout)**: {}'.format(annotation))
            return '  \n'.join(text_list)

    def __get_annotation_for_markdown__(self):
        annotation = ''
        for pattern in self._detector_first.pattern_list:
            if not pattern.was_breakout_done():
                annotation = pattern.get_annotation_parameter().text
        return annotation

    def __init_callback_for_graph_first__(self):
        @self.app.callback(
            Output('my_graph_first_div', 'children'),
            [Input('my_interval_refresh', 'n_intervals'),
             Input('my_refresh_button', 'n_clicks')],
            [State('my_pattern_ticker_selection', 'value')])
        def handle_callback_for_graph_first(n_intervals, n_clicks, ticker):
            self._ticker_selected = ticker
            graph, graph_key = self.__get_graph_first__(ticker)
            self._graph_key_first = graph_key
            self.__cache_others_ticker_values__(n_intervals, ticker)
            if self._graph_first_cache.was_breakout_since_last_data_update(graph_key):
                self._news_handler.add_news(ticker, 'Breakout since last data refresh !!!!')
                self.sys_config.sound_machine.play_alarm_new_pattern()
            elif self._graph_first_cache.was_touch_since_last_data_update(graph_key):
                self._news_handler.add_news(ticker, 'Touch since last data refresh !!!!')
                self.sys_config.sound_machine.play_alarm_touch_point()
            return graph

    def __cache_others_ticker_values__(self, n_intervals: int, ticker_selected: str):
        if n_intervals > 0:
            for element_dict in self._ticker_options:
                ticker = element_dict['value']
                if ticker != ticker_selected:
                    self.__add_cache_object_for_ticker_to_graph_first_cache__(ticker)

    def __init_callback_for_graph_second__(self):
        @self.app.callback(
            Output('my_graph_second_div', 'children'),
            [Input('my_graph_second_days_selection', 'value'),
             Input('my_graph_first_div', 'children')],
            [State('my_pattern_ticker_selection', 'value'),
             State('my_period_aggregation', 'value')])
        def handle_callback_for_graph_second(days_selected, graph_first_div, ticker_selected: str, aggregation: int):
            if days_selected == 0 or ticker_selected is None:
                return ''
            self.__set_period_aggregation_to_sys_configs__(aggregation)
            return self.__get_graph_second__(ticker_selected, days_selected)[0]

    def __init_callback_for_graphs_before_breakout__(self):
        @self.app.callback(
            Output('my_graphs_before_breakout_div', 'children'),
            [Input('my_graph_first_div', 'children')])
        def handle_callback_for_graphs_before_breakout(graph_first_div):
            play_sound = False
            graphs, new_observations = self._graph_first_cache.get_graph_list_for_observation(self._graph_key_first)
            if new_observations:
                play_sound = True
                self._news_handler.add_news('Observations', 'Some new since last refresh !!!!')
            pattern_list = self._graph_first_cache.get_pattern_list_for_buy_trigger(BT.BREAKOUT)
            self.trade_handler_online.add_pattern_list_for_trade(pattern_list)
            if self._graph_first_cache.number_of_finished_fibonacci_waves_since_last_refresh > 2:
                self._news_handler.add_news('Fibonacci numbers', 'More than 2 waves finished since last refresh !!!!')
                play_sound = True
            if play_sound:
                self.sys_config.sound_machine.play_alarm_new_pattern()
            return graphs

    def __init_selection_callback__(self):
        @self.app.callback(
            Output('my_refresh_button', 'hidden'),
            [Input('my_pattern_ticker_selection', 'value'),
             Input('my_period_aggregation', 'value'),
             Input('my_interval_selection', 'value'),
             Input('my_interval_refresh', 'n_intervals'),
             Input('my_refresh_button', 'n_clicks')],
            [State('my_interval_timer', 'n_intervals'),
             State('my_pattern_index_selection', 'value')])
        def handle_selection_callback(ticker_selected, period_aggregation: int, interval_selected: int,
                                      n_intervals, n_clicks, n_intervals_sec, selected_indices: list):
            # print('selected_indizes: {}'.format(selected_indices))
            self.__set_period_aggregation_to_sys_configs__(period_aggregation)
            indices_change = self._state_handler.change_for_my_selected_indices(selected_indices)
            if indices_change:
                return ''
            pa_change = self._state_handler.change_for_my_period_aggregation_selection(period_aggregation)
            i_change = self._state_handler.change_for_my_interval_selection(interval_selected)
            if pa_change or i_change:
                self._graph_first_cache.clear()
            if self._state_handler.change_for_my_interval(n_intervals):  # hide button after interval refresh
                return 'hidden'
            if self._state_handler.change_for_my_refresh_button(n_clicks):  # hide button after refresh button click
                return 'hidden'
            return 'hidden' if n_intervals_sec == 0 else ''

        @self.app.callback(
            Output('my_ticker_div', 'children'),
            [Input('my_pattern_ticker_selection', 'value')])
        def handle_ticker_selection_callback_for_ticker_label(ticker_selected):
            return self.__get_ticker_label__(ticker_selected)

    def __set_period_aggregation_to_sys_configs__(self, selected_period_aggregation: int):
        self.sys_config.data_provider.aggregation = selected_period_aggregation
        self.sys_config_second.data_provider.aggregation = self.__get_period_aggregation_for_second_graph__()

    def __get_period_aggregation_for_second_graph__(self):
        return {5: 15, 15: 30, 30: 15}.get(self.sys_config.period_aggregation)

    def __init_hover_over_callback__(self):
        @self.app.callback(
            Output('my_hover_data', 'children'),
            [Input('my_graph_first', 'hoverData'), Input('my_graph_second', 'hoverData')])
        def handle_hover_over_callback(hover_data_graph_1, hover_data_graph_2):
            return json.dumps(hover_data_graph_1, indent=2) + '\n' + json.dumps(hover_data_graph_2, indent=2)

    def __init_ticker_selection_callback__(self):
        @self.app.callback(
            Output('my_graph_second_days_selection', 'value'),
            [Input('my_pattern_ticker_selection', 'value')],
            [State('my_graph_second_days_selection', 'value')])
        def handle_ticker_selection_callback_for_days_selection(ticker_selected, second_days_selection):
            return second_days_selection if second_days_selection == 1 else 0  # we want to keep Intraday

    def __get_graph_first__(self, ticker: str, and_clause=''):
        self.__add_cache_object_for_ticker_to_graph_first_cache__(ticker, and_clause)
        graph_key = self._graph_first_cache.get_cache_key(ticker, 0)
        cached_graph = self._graph_first_cache.get_cached_object_by_key(graph_key)
        self._detector_first = self._graph_first_cache.get_detector(graph_key)
        self._pattern_data_first = self._graph_first_cache.get_pattern_data(graph_key)
        return cached_graph, graph_key

    def __add_cache_object_for_ticker_to_graph_first_cache__(self, ticker: str, and_clause=''):
        graph_id = self._graph_first_cache.graph_id
        cache_key = self._graph_first_cache.get_cache_key(ticker, 0)
        if self._graph_first_cache.get_cached_object_by_key(cache_key) is None:
            aggregation = self.sys_config.period_aggregation
            graph_title = self.__get_graph_title__(ticker, self.sys_config.period, aggregation)
            detector = self._pattern_controller.get_detector_for_dash(self.sys_config, ticker, and_clause)
            pattern_data = detector.pdh.pattern_data
            graph_api = DccGraphApi(graph_id, graph_title)
            graph_api.ticker_id = ticker
            graph_api.df = detector.pdh.pattern_data.df
            graph = self.__get_dcc_graph_element__(detector, graph_api)
            cache_api = self.__get_cache_api__(cache_key, graph, detector, pattern_data)
            self._graph_first_cache.add_cache_object(cache_api)
            print('{}: Cached into graph_first_cache: {}'.format(MyDate.get_time_str_from_datetime(), cache_key))
        else:
            print('{}: Already cached by graph_first_cache: {}'.format(MyDate.get_time_str_from_datetime(), cache_key))

    @staticmethod
    def __get_graph_title__(ticker, period: str, aggregation: int, days: int=0):
        if period == PRD.DAILY:
            return '{} {} ({}days)'.format(ticker, period, days)
        return '{} {} ({}min)'.format(ticker, period, aggregation)

    def __get_graph_second__(self, ticker: str, days: int):
        graph_id = self._graph_second_cache.graph_id
        period = PRD.DAILY if days > 1 else PRD.INTRADAY
        aggregation_second_graph = self.__get_period_aggregation_for_second_graph__()
        graph_title = self.__get_graph_title__(ticker, period, aggregation_second_graph, days)
        graph_key = self._graph_second_cache.get_cache_key(ticker, days, aggregation_second_graph)
        cached_graph = self._graph_second_cache.get_cached_object_by_key(graph_key)
        if cached_graph is not None:
            # print('...return cached graph_second: {}'.format(graph_key))
            return cached_graph, graph_key
        self.__update_data_provider_parameters_for_graph_second__(days, period, aggregation_second_graph)
        if days == 1:
            detector = self._pattern_controller.get_detector_for_dash(self.sys_config_second, ticker)
            graph_api = DccGraphSecondApi(graph_id, graph_title)
            graph_api.ticker_id = ticker
            graph_api.df = detector.pdh.pattern_data.df
            graph = self.__get_dcc_graph_element__(detector, graph_api)
            cache_api = self.__get_cache_api__(graph_key, graph, detector, None)
            self._graph_second_cache.add_cache_object(cache_api)
        else:
            date_from = datetime.today() - timedelta(days=days)
            date_to = datetime.today() + timedelta(days=5)
            and_clause = self.sys_config.data_provider.get_and_clause(date_from, date_to)
            detector = self._pattern_controller.get_detector_for_dash(self.sys_config_second, ticker, and_clause)
            graph_api = DccGraphSecondApi(graph_id, graph_title)
            graph_api.ticker_id = ticker
            graph_api.df = detector.pdh.pattern_data.df
            graph = self.__get_dcc_graph_element__(detector, graph_api)
            cache_api = self.__get_cache_api__(graph_key, graph, detector, None)
            self._graph_second_cache.add_cache_object(cache_api)
        return graph, graph_key

    def __update_data_provider_parameters_for_graph_second__(self, days: int, period: str, aggregation: int=1):
        if days == 1:
            self.sys_config_second.data_provider.from_db = False
            self.sys_config_second.data_provider.period = period
            self.sys_config_second.data_provider.aggregation = aggregation
        else:
            self.sys_config_second.data_provider.from_db = True
            self.sys_config_second.data_provider.period = period
            self.sys_config_second.data_provider.aggregation = 1

    def __get_cache_api__(self, graph_key, graph, detector, pattern_data):
        cache_api = MyGraphCacheObjectApi(self.sys_config)
        cache_api.key = graph_key
        cache_api.object = graph
        cache_api.detector = detector
        cache_api.pattern_data = pattern_data
        cache_api.valid_until_ts = self._time_stamp_next_refresh
        cache_api.last_refresh_ts = self._time_stamp_last_refresh
        return cache_api

    def __fill_ticker_options__(self):
        self._ticker_options = []
        if self.sys_config.data_provider.index_used == INDICES.CRYPTO_CCY:
            for ticker_id in self.bitfinex_config.ticker_id_list:
                if ticker_id in self.sys_config.data_provider.ticker_dict:
                    name = self.sys_config.data_provider.ticker_dict[ticker_id]
                    self._ticker_options.append({'label': '{}'.format(name), 'value': ticker_id})
        else:  # currently we take all - but this is definitely to much for calculation...
            for symbol, name in self.sys_config.data_provider.ticker_dict.items():
                self._ticker_options.append({'label': '{}'.format(name), 'value': symbol})

    def __get_ticker_label__(self, ticker_value: str):
        for elements in self._ticker_options:
            if elements['value'] == ticker_value:
                return elements['label']
        return ticker_value
class MyDashTab4Recommender(MyPatternDashBaseTab):
    _data_table_name = 'my_recommender_table'
    _data_table_div = '{}_div'.format(_data_table_name)

    def __init__(self, app: Dash, sys_config: SystemConfiguration,
                 trade_handler_online: PatternTradeHandler):
        MyPatternDashBaseTab.__init__(self, app, sys_config)
        self.sys_config = self.__get_adjusted_sys_config_copy__(sys_config)
        self.exchange_config = self.sys_config.exchange_config
        self._pattern_controller = PatternDetectionController(self.sys_config)
        self._recommender_table = RecommenderTable(
            self.sys_config.db_stock, self.sys_config.data_provider)
        self._active_manage_button_clicks = 0
        self._refresh_button_clicks = 0
        self._trade_handler_online = trade_handler_online
        self._dd_handler = RecommenderTabDropDownHandler()

    @staticmethod
    def __get_adjusted_sys_config_copy__(
            sys_config: SystemConfiguration) -> SystemConfiguration:
        sys_config_copy = sys_config.get_semi_deep_copy(
        )  # we need some adjustments (_period, etc...)
        sys_config_copy.data_provider.from_db = False
        sys_config_copy.data_provider.period = sys_config.period
        sys_config_copy.data_provider.aggregation = sys_config.period_aggregation
        return sys_config_copy

    @staticmethod
    def __get_news_handler__():
        return NewsHandler('  \n', '')

    def get_div_for_tab(self):
        children_list = [
            MyHTMLTabRecommenderHeaderTable().get_table(),
            MyHTML.div_with_dcc_drop_down(
                **self._dd_handler.get_drop_down_parameters(REDD.INDEX)),
            MyHTML.div_with_dcc_drop_down(
                **self._dd_handler.get_drop_down_parameters(
                    REDD.PERIOD_AGGREGATION,
                    default_value=self.sys_config.period_aggregation)),
            MyHTML.div_with_dcc_drop_down(
                **self._dd_handler.get_drop_down_parameters(
                    REDD.REFRESH_INTERVAL)),
            MyHTML.div_with_dcc_drop_down(
                **self._dd_handler.get_drop_down_parameters(
                    REDD.SECOND_GRAPH_RANGE)),
            MyHTML.div_with_dcc_drop_down(
                **self._dd_handler.get_drop_down_parameters(REDD.SCORING)),
            MyHTML.div_with_html_button_submit('my_recommender_refresh_button',
                                               'Refresh',
                                               hidden=''),
            MyHTML.div_with_html_button_submit(
                'my_recommender_active_manage_button',
                self.__get_position_manage_button_text__()),
            MyHTML.div_with_table(self._data_table_div,
                                  self.__get_table_for_recommender__()),
            MyHTML.div('my_graph_recommender_position_div'),
            MyHTML.div('my_graph_recommender_position_second_div')
        ]
        return MyHTML.div('my_recommender_div', children_list)

    def init_callbacks(self):
        self.__init_callbacks_for_position_manage_button__()
        self.__init_callback_for_recommender_markdown__()
        self.__init_callback_for_recommender_table__()
        self.__init_callback_for_graph_for_selected_position__()
        self.__init_callback_for_selected_row_indices__()

    def __init_callbacks_for_position_manage_button__(self):
        @self.app.callback(
            Output('my_recommender_active_manage_button', DSHVT.HIDDEN),
            [Input('my_graph_recommender_position_div', DSHVT.CHILDREN)])
        def handle_callback_for_position_manage_button_hidden(graph):
            if graph == '':
                return 'hidden'
            return ''

        @self.app.callback(
            Output('my_recommender_active_manage_button', DSHVT.CHILDREN),
            [Input('my_graph_recommender_position_div', DSHVT.CHILDREN)])
        def handle_callback_for_position_manage_button_text(graph):
            return self.__get_position_manage_button_text__()

    def __init_callback_for_graph_for_selected_position__(self):
        @self.app.callback(
            Output('my_graph_recommender_position_div', DSHVT.CHILDREN), [
                Input(self._data_table_name, DSHVT.ROWS),
                Input(self._data_table_name, DSHVT.SELECTED_ROW_INDICES)
            ], [
                State('my_recommender_aggregation', DSHVT.VALUE),
                State('my_recommender_refresh_interval_selection', DSHVT.VALUE)
            ])
        def handle_callback_for_graph_first(rows: list,
                                            selected_row_indices: list,
                                            aggregation: int,
                                            refresh_interval: int):
            # print('selected_row_indices={}'.format(selected_row_indices))
            self._recommender_table.init_selected_row(rows,
                                                      selected_row_indices)
            if self._recommender_table.selected_row_index == -1:
                return ''
            self.sys_config.data_provider.period = PRD.INTRADAY
            self.sys_config.data_provider.aggregation = aggregation
            selected_ticker_id = self._recommender_table.selected_symbol
            graph, graph_key = self.__get_graph__(selected_ticker_id,
                                                  refresh_interval)
            return graph

        @self.app.callback(
            Output('my_graph_recommender_position_second_div', DSHVT.CHILDREN),
            [Input('my_graph_recommender_position_div', DSHVT.CHILDREN)], [
                State('my_recommender_aggregation', DSHVT.VALUE),
                State('my_recommender_refresh_interval_selection',
                      DSHVT.VALUE),
                State('my_recommender_graph_second_days_selection',
                      DSHVT.VALUE)
            ])
        def handle_callback_for_graph_second(graph_first,
                                             aggregation_first: int,
                                             refresh_interval: int,
                                             range_list: list):
            if self._recommender_table.selected_row_index == -1 or len(
                    range_list) == 0:
                return ''
            selected_ticker_id = self._recommender_table.selected_symbol
            graph_list = []
            sorted_range_list = sorted(range_list)
            for graph_range in sorted_range_list:
                if graph_range == 1:
                    self.sys_config.data_provider.period = PRD.INTRADAY
                    self.sys_config.data_provider.aggregation = {
                        5: 15,
                        15: 30,
                        30: 15
                    }.get(aggregation_first, 30)
                    graph, key = self.__get_graph__(selected_ticker_id,
                                                    refresh_interval)
                else:
                    self.sys_config.data_provider.period = PRD.DAILY
                    graph, key = self.__get_graph__(selected_ticker_id,
                                                    refresh_interval,
                                                    graph_range)
                graph_list.append(graph)
            return graph_list

    def __init_callback_for_recommender_markdown__(self):
        @self.app.callback(Output('my_recommender_markdown', DSHVT.CHILDREN),
                           [Input('my_position_markdown', DSHVT.CHILDREN)])
        def handle_callback_for_recommender_markdown(children):
            return self.__get_recommender_markdown__()

    def __init_callback_for_recommender_table__(self):
        @self.app.callback(Output(self._data_table_div, DSHVT.CHILDREN), [
            Input('my_position_markdown', DSHVT.CHILDREN),
            Input('my_recommender_active_manage_button', DSHVT.N_CLICKS),
            Input('my_recommender_refresh_button', DSHVT.N_CLICKS)
        ], [
            State('my_recommender_index', DSHVT.VALUE),
            State('my_recommender_scoring_selection', DSHVT.VALUE)
        ])
        def handle_callback_for_positions_options(children,
                                                  manage_n_clicks: int,
                                                  refresh_n_clicks: int,
                                                  selected_indices: list,
                                                  scoring: str):
            if manage_n_clicks > self._active_manage_button_clicks:
                self._active_manage_button_clicks = manage_n_clicks
                self.__toggle_flag_for_active_managed__()
            elif refresh_n_clicks > self._refresh_button_clicks:
                self._refresh_button_clicks = refresh_n_clicks
                self._recommender_table.update_rows_for_selected_indices(
                    selected_indices, scoring)
            return self.__get_table_for_recommender__()

    def __init_callback_for_selected_row_indices__(self):
        @self.app.callback(
            Output(self._data_table_name, DSHVT.SELECTED_ROW_INDICES),
            [Input(self._data_table_name, DSHVT.ROWS)])
        def handle_callback_for_selected_row_indices(rows):
            if self._recommender_table.selected_row_index == -1 or len(
                    rows) == 0:
                return []
            return [self._recommender_table.selected_row_index]

    def __get_graph__(self,
                      ticker_id: str,
                      refresh_interval: int,
                      limit: int = 0):
        period = self.sys_config.period
        aggregation = self.sys_config.period_aggregation
        graph_cache_id = self.sys_config.graph_cache.get_cache_id(
            ticker_id, period, aggregation, limit)
        graph = self.sys_config.graph_cache.get_cached_object_by_key(
            graph_cache_id)
        if graph is not None:
            return graph, graph_cache_id

        if period == PRD.DAILY and self._recommender_table.selected_index != INDICES.CRYPTO_CCY:
            self.sys_config.data_provider.from_db = True
        else:
            self.sys_config.data_provider.from_db = False
        date_start = MyDate.adjust_by_days(MyDate.get_datetime_object().date(),
                                           -limit)
        and_clause = "Date > '{}'".format(date_start)
        graph_title = self.sys_config.graph_cache.get_cache_title(
            ticker_id, period, aggregation, limit)
        detector = self._pattern_controller.get_detector_for_fibonacci_and_pattern(
            self.sys_config, ticker_id, and_clause, limit)
        graph_api = DccGraphApi(graph_cache_id, graph_title)
        graph_api.ticker_id = ticker_id
        graph_api.df = detector.pdh.pattern_data.df
        graph = self.__get_dcc_graph_element__(detector, graph_api)
        cache_api = self.sys_config.graph_cache.get_cache_object_api(
            graph_cache_id, graph, period, refresh_interval)
        self.sys_config.graph_cache.add_cache_object(cache_api)
        return graph, graph_cache_id

    def __get_recommender_markdown__(self):
        balances = self._trade_handler_online.balances
        text_list = [
            '_**{}**_: {:.2f} ({:.2f}): {:.2f}$'.format(
                b.asset, b.amount, b.amount_available, b.current_value)
            for b in balances
        ]
        return '  \n'.join(text_list)

    def __get_position_manage_button_text__(self) -> str:
        return RMBT.SWITCH_TO_ACTIVE_MANAGEMENT
        # if self._selected_ticker_id_management == 'Yes':
        #     return RMBT.SWITCH_TO_NO_MANAGEMENT
        # return RMBT.SWITCH_TO_ACTIVE_MANAGEMENT

    def __get_table_for_recommender__(self):
        rows = self._recommender_table.get_rows_for_selected_indices()
        min_height = self._recommender_table.height_for_display
        return MyDCC.data_table(self._data_table_name,
                                rows,
                                columns=self._recommender_table.columns)

    def __toggle_flag_for_active_managed__(self):
        pass