# source: https://raw.githubusercontent.com/matthieu-hm/freqtrade_consumer_producer_strategies/f77c2dd5a506fb0f7c9742af8ae7409572996dd6/DummyConsumerStrategy.py
import copy
import logging
import pathlib
import rapidjson
import freqtrade.vendor.qtpylib.indicators as qtpylib
import numpy as np
import talib.abstract as ta
import pandas as pd
import pandas_ta as pta
from freqtrade.strategy.interface import IStrategy
from freqtrade.strategy import merge_informative_pair
from pandas import DataFrame, Series
from functools import reduce, partial
from freqtrade.persistence import Trade, LocalTrade
from datetime import datetime, timedelta
import time
from typing import Optional
import warnings

log = logging.getLogger(__name__)
# log.setLevel(logging.DEBUG)
warnings.simplefilter(action='ignore', category=pd.errors.PerformanceWarning)

class github_matthieu_hm_freqtrade_consumer_producer_strategies__DummyConsumerStrategy__20230713_150648(IStrategy):
    INTERFACE_VERSION: int = 3


    #############################################################
    # Strategy parameters -->

    timeframe = '5m'

    stoploss = -0.05

    # <-- Strategy parameters
    #############################################################


    #############################################################
    # Producers / Consumer

    is_consumer = False

    process_only_new_candles = True


    def __init__(self, config: dict) -> None:
        super().__init__(config)

        if ('external_message_consumer' in self.config and 'producers' in self.config['external_message_consumer']
                and self.config['external_message_consumer']['enabled']
                and len(self.config['external_message_consumer']['producers']) > 0):
            self.is_consumer = True
            self.process_only_new_candles = False  # required for consumers


    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        if self.is_consumer:
            return self.get_dataframe_from_producers(dataframe, metadata['pair'])

        # ... (strategy logic)
        return dataframe


    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        if self.is_consumer:
            # Dataframe already analysed
            return dataframe

        # ... (strategy logic)
        return dataframe


    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        if self.is_consumer:
            # Dataframe already analysed
            return dataframe

        # ... (strategy logic)
        return dataframe


    # OPTIONAL: If the strategy uses other callbacks that require the dataframe
    #           check if the dataframe is available before (for the consumer)


    # # OPTIONAL: If the strategy uses this callback
    # def adjust_trade_position(self, trade: Trade, current_time: datetime,
    #                       current_rate: float, current_profit: float,
    #                       min_stake: Optional[float], max_stake: float,
    #                       current_entry_rate: float, current_exit_rate: float,
    #                       current_entry_profit: float, current_exit_profit: float,
    #                       **kwargs) -> Optional[float]:
    #     if (self.position_adjustment_enable == False):
    #         return None

    #     if (self.is_consumer and not self.has_access_to_dataframe_from_producers(trade.pair)):
    #         # Consumer started before the producer that handles this pair and has active trades
    #         # No data available yet, do nothing
    #         return None

    #     # ... (strategy logic)
    #     return None


    # # OPTIONAL: If the strategy uses this callback
    # def custom_exit(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float,
    #                 current_profit: float, **kwargs):
    #     if (self.is_consumer and not self.has_access_to_dataframe_from_producers(pair)):
    #         # Consumer started before the producer that handles this pair and has active trades
    #         # No data available yet, do nothing
    #         return None

    #     # ... (strategy logic)
    #     return None


# +---------------------------------------------------------------------------+
# |                              Consumer                                     |
# +---------------------------------------------------------------------------+

    def get_dataframe_from_producers(self, dataframe: DataFrame, pair: str) -> DataFrame:
        for producer in self.config['external_message_consumer']['producers']:
            if (producer['name']):
                # This func returns the analyzed dataframe, and when it was analyzed
                producer_dataframe, _ = self.dp.get_producer_df(pair, producer_name=producer['name'])

                if not producer_dataframe.empty:
                    log.debug(f"[{pair}] Get dataframe from producer \"{producer['name']}\"")

                    merged_dataframe = merge_informative_pair(dataframe, producer_dataframe,
                                                      self.timeframe, self.timeframe,
                                                      ffill=True,
                                                      append_timeframe=False)

                    # The function merge_informative_pair() adds suffixes to the requiered columns
                    # ('date', 'open', 'high', 'low', 'close', 'volume')
                    # origin dataframe columns: _x
                    # producer_dataframe columns: _y

                    # -> We keep only the columns from the consumer dataframe (origin),
                    #    as the consumer update the values more often than the producers

                    # Get all merged_dataframe columns ending with _x
                    merged_dataframe_columns_x = [col for col in merged_dataframe.columns if col.endswith('_x')]
                    # Remove suffix "_x" from merged_dataframe columns
                    merged_dataframe.rename(columns=dict(zip(merged_dataframe_columns_x, [col[:-2] for col in merged_dataframe_columns_x])), inplace=True)

                    return merged_dataframe

        # No dataframe provided by any producer: minimal dataframe fallback
        required_columns_default_0 = ['enter_long', 'enter_short', 'exit_long', 'exit_short']
        required_columns_default_none = ['enter_tag','exit_tag']

        dataframe[required_columns_default_0] = 0
        dataframe[required_columns_default_none] = None
        log.debug(f"[{pair}] No dataframe found from producers")
        return dataframe


    def has_access_to_dataframe_from_producers(self, pair: str) -> bool:
        for producer in self.config['external_message_consumer']['producers']:
            if (producer['name']):
                # This func returns the analyzed dataframe, and when it was analyzed
                producer_dataframe, _ = self.dp.get_producer_df(pair, producer_name=producer['name'])

                if not producer_dataframe.empty:
                    log.debug(f"[{pair}] Found dataframe: producer \"{producer['name']}\"")
                    return True

        log.debug(f"[{pair}] No dataframe found from producers")
        return False
