# source: https://raw.githubusercontent.com/rmallarapu-bc/brahma/9287745fc036c2f4c00c586e598290885c2883b9/archive/Raja1.py
import freqtrade.vendor.qtpylib.indicators as qtpylib
import numpy as np  # noqa
import pandas as pd  # noqa
# --------------------------------
# Add your lib to import here
import talib.abstract as ta
from freqtrade.strategy import DecimalParameter
from freqtrade.strategy import (
    IStrategy,
)
from pandas import DataFrame
from technical.util import resample_to_interval, resampled_merge
from freqtrade.persistence import Trade
from datetime import timedelta, datetime
from typing import Optional

import logging

# from log import FATAL

log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)


class Github_rmallarapu_bc_brahma__Raja1__20240229_213751(IStrategy):
    INTERFACE_VERSION = 3
    timeframe = "1h"
    minimal_roi = {
        "0": 0.1
    }
    stoploss = -0.32

    buy_params = {
        "buy_limit": 0.943,
    }

    # DCA options
    position_adjustment_enable = True
    max_entry_position_adjustment = 10
    initial_order_size = 0.2  # 20% of total capital
    incremental_order_size = 0.4  # increase capital by 40% of initial order for every {initial_profit} loss
    initial_profit = 0.01
    recurring_loss = -0.01
    loss_upper_limit = -0.10

    # leverage
    leverage_num = 1

    # Trailing stoploss
    trailing_stop = True
    trailing_stop_positive = 0.001
    trailing_stop_positive_offset = 0.005
    trailing_only_offset_is_reached = True


    # Can this strategy go short?
    can_short: bool = True

    # Optional order type mapping.
    order_types = {
        'entry': 'limit',
        'exit': 'limit',
        'stoploss': 'limit',
        'stoploss_on_exchange': False
    }

    # Optional order time in force.
    order_time_in_force = {
        'entry': 'gtc',
        'exit': 'gtc'
    }

    # process_only_new_candles = False
    # use_exit_signal = True
    # exit_profit_only = False
    # ignore_roi_if_entry_signal = True

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

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

    buy_limit = DecimalParameter(0.90, 0.98, default=0.98, space='buy', decimals=3, optimize=True, load=True)

    @property
    def protections(self):
        return [
            {
                "method": "CooldownPeriod",
                "stop_duration_candles": 1
            },
            {
                "method": "StoplossGuard",
                "lookback_period_candles": 24,
                "trade_limit": 4,
                "stop_duration_candles": 2,
                "only_per_pair": False
            },

        ]

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # RSI
        dataframe["rsi"] = ta.RSI(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"]
        dataframe["bb_upperband"] = bollinger["upper"]

        # TEMA - Triple Exponential Moving Average
        dataframe["tema"] = ta.TEMA(dataframe, timeperiod=9)

        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (
                    (dataframe['close'] <= self.buy_limit.value * dataframe['bb_middleband'])
                    & (qtpylib.crossed_above(dataframe["rsi"], 20))
                    & (dataframe["tema"] <= dataframe["bb_middleband"])
                    & (dataframe["tema"] < dataframe["tema"].shift(1))
                    & (dataframe["volume"] > 0)
            ),
            "enter_long",
        ] = 0

        dataframe.loc[
            (
                    (dataframe['close'] > self.buy_limit.value * dataframe['bb_middleband'])
                    & (qtpylib.crossed_above(dataframe["rsi"], 70))
                    & (dataframe["tema"] > dataframe["bb_middleband"])
                    & (dataframe["tema"] < dataframe["tema"].shift(1))
                    & (dataframe["volume"] > 0)
            ),
            "enter_short",
        ] = 0

        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        return super().populate_exit_trend(dataframe, metadata)

    # This is called when placing the initial order (opening trade)
    def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float,
                            proposed_stake: float, min_stake: Optional[float], max_stake: float,
                            leverage: float, entry_tag: Optional[str], side: str,
                            **kwargs) -> float:

        # We need to leave most of the funds for possible further DCA orders
        # This also applies to fixed stakes
        return proposed_stake * self.initial_order_size

    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]:
        """
        Custom trade adjustment logic, returning the stake amount that a trade should be
        increased or decreased.
        This means extra buy or sell orders with additional fees.
        Only called when `position_adjustment_enable` is set to True.

        For full documentation please go to https://www.freqtrade.io/en/latest/strategy-advanced/

        When not implemented by a strategy, returns None

        :param trade: trade object.
        :param current_time: datetime object, containing the current datetime
        :param current_rate: Current buy rate.
        :param current_profit: Current profit (as ratio), calculated based on current_rate.
        :param min_stake: Minimal stake size allowed by exchange (for both entries and exits)
        :param max_stake: Maximum stake allowed (either through balance, or by exchange limits).
        :param current_entry_rate: Current rate using entry pricing.
        :param current_exit_rate: Current rate using exit pricing.
        :param current_entry_profit: Current profit using entry pricing.
        :param current_exit_profit: Current profit using exit pricing.
        :param **kwargs: Ensure to keep this here so updates to this won't break your strategy.
        :return float: Stake amount to adjust your trade,
                       Positive values to increase position, Negative values to decrease position.
                       Return None for no action.
        """

        filled_entries = trade.select_filled_orders(trade.entry_side)
        count_of_entries = trade.nr_of_successful_entries
        count_of_exits = trade.nr_of_successful_exits
        stake_amount = filled_entries[0].cost

        if current_profit > self.initial_profit and count_of_exits == 0:
            return -(trade.stake_amount / 2)

        # if current_profit < self.loss_upper_limit: return None  # if the losses are greater than 10%, stop

        # Only buy when prices are not actively falling.
        dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe)
        last_candle = dataframe.iloc[-1].squeeze()
        previous_candle = dataframe.iloc[-2].squeeze()
        if last_candle['close'] < previous_candle['close']:
            return None

        # determine the stake amount now
        if current_profit < self.recurring_loss and count_of_entries <= self.max_entry_position_adjustment:
            return stake_amount * self.incremental_order_size
        else:
            return None

    def leverage(self, pair: str, current_time: datetime, current_rate: float,
                 proposed_leverage: float, max_leverage: float, side: str,
                 **kwargs) -> float:
        return self.leverage_num
