# source: https://raw.githubusercontent.com/hextropian/hextropian-freqtrade-bots/d6e098b848a760dd8d50e0a5eab1bdcd83b29e31/user_data/strategies/BB_CIX.py
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement

from pandas import DataFrame

from freqtrade.strategy import IStrategy
from typing import Optional
from datetime import datetime
from freqtrade.persistence import Trade
from datetime import timedelta

import talib.abstract as ta
import freqtrade.vendor.qtpylib.indicators as qtpylib
import logging

log = logging.getLogger(__name__)

# This strategy leverages the high volatility in the lower timeframes for the CIX100 token
# ---
# Designed and written by @hextropian (Twitter), a.k.a. as DrWho?#8511 (Discord)
# Use at your own risk - no warranties of success whatsoever.

# Hyperopted parameters:
# freqtrade hyperopt -c ./config-single.json --strategy Github_hextropian_hextropian_freqtrade_bots__BB_CIX__20221029_171423 --hyperopt-loss SharpeHyperOptLoss --timerange=20220101- -e 1000 --spaces stoploss roi trailing
# 518/1000:    589 trades. 354/0/235 Wins/Draws/Losses. 
# Avg profit   0.41%. 
# Median profit   0.08%. 
# Total profit 288681.10608409 USDT ( 577.36%). 
# Avg duration 3:37:00 min. 
# Objective: -4.37348

class Github_hextropian_hextropian_freqtrade_bots__BB_CIX__20221029_171423(IStrategy):
  
    INTERFACE_VERSION = 3

    minimal_roi = {
        "0": 0.224,
        "116": 0.14,
        "240": 0.05,
        "422": 0
    }

    # stoploss = -0.99 # Effectively disabled
    stoploss = -0.026 # Hyperopted
    use_custom_stoploss = False 

    # Hyperopted numbers
    # Trailing stop:
    trailing_stop = True
    trailing_stop_positive = 0.01
    trailing_stop_positive_offset = 0.039
    trailing_only_offset_is_reached = True

    # Optimal timeframe for the strategy.
    timeframe = '15m'

    # Run "populate_indicators()" only for new candle.
    process_only_new_candles = True

    # These values can be overridden in the "ask_strategy" section in the config.
    use_exit_signal = True
    exit_profit_only = False
    ignore_roi_if_entry_signal = False

    # Number of candles the strategy requires before producing valid signals
    startup_candle_count: int = 60


    def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float,
                            time_in_force: str, current_time: datetime, entry_tag: Optional[str],
                            side: str, **kwargs) -> bool:
        """
        From NostalgiaForInfinityX by iterativ 
        https://github.com/iterativv/NostalgiaForInfinity
        allow force entries and protects against slippage.
        """

        if (entry_tag == 'force_entry'):
            return True

        dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)

        if(len(dataframe) < 1):
            return True

        dataframe = dataframe.iloc[-1].squeeze()

        if ((rate > dataframe['close'])):
            slippage = ((rate / dataframe['close']) - 1.0)

            if slippage < 0.044:
                return True
            else:
                log.warning(
                    "Cancelling buy for %s due to slippage %s",
                    pair, slippage
                )
                return False

        return True


    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        
        # Bollinger bands
        bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
        dataframe['bb_lowerband'] = bollinger['lower']
        # dataframe['bb_middleband'] = bollinger['mid'] # (Currently unused)
        dataframe['bb_upperband'] = bollinger['upper']

        return dataframe


    #################################################################################
    ##                                                                             ## 
    ##                            BUY (Enter) conditions                           ##
    ##                                                                             ## 
    ################################################################################# 
    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:

        dataframe.loc[
            (
                 (
                    (dataframe['close'] <= dataframe['bb_lowerband'])
                    & (dataframe['volume'] > 0)
                )
            ),
            'enter_long'] = 1

        return dataframe
    
    #################################################################################
    ##                                                                             ## 
    ##                            SELL (Exit) conditions                           ##
    ##                                                                             ## 
    ################################################################################# 
    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (
                 (dataframe['close'] > dataframe['bb_upperband'])
               & (dataframe['volume'] > 0)
            ),
            'exit_long'] = 1

        return dataframe


# Helper function 
def to_minutes(**timdelta_kwargs):
    return int(timedelta(**timdelta_kwargs).total_seconds() / 60)
