# source: https://raw.githubusercontent.com/hehehe-jing/-/71e70664c713bebb44f14cc154ca64eba5b03e11/实盘经历/E0V1EN.py
from datetime import datetime, timedelta

import requests
import talib.abstract as ta
import pandas_ta as pta
from freqtrade.persistence import Trade
from freqtrade.strategy.interface import IStrategy
from pandas import DataFrame
from freqtrade.strategy import DecimalParameter, IntParameter
from functools import reduce
import warnings
from typing import Dict, Optional, Union, Tuple
import logging

warnings.simplefilter(action="ignore", category=RuntimeWarning)
TMP_HOLD = []
TMP_HOLD1 = []

logger = logging.getLogger(__name__)
class Github_hehehe_jing____E0V1EN__20260615_001114(IStrategy):
    minimal_roi = {
        "0": 1
    }
    timeframe = '5m'
    process_only_new_candles = True
    startup_candle_count = 240
    order_types = {
        'entry': 'market',
        'exit': 'market',
        'emergency_exit': 'market',
        'force_entry': 'market',
        'force_exit': "market",
        'stoploss': 'market',
        'stoploss_on_exchange': False,
        'stoploss_on_exchange_interval': 60,
        'stoploss_on_exchange_market_ratio': 0.99
    }
    slippage_protection = {
        'retries': 3,
        'max_slippage': -0.02
    }
    cc = {}
    # current_candle = {}

    stoploss = -0.25
    trailing_stop = False
    trailing_stop_positive = 0.002
    trailing_stop_positive_offset = 0.05
    trailing_only_offset_is_reached = True

    use_custom_stoploss = True

    is_optimize_32 = True
    buy_rsi_fast_32 = IntParameter(20, 70, default=40, space='buy', optimize=is_optimize_32)
    buy_rsi_32 = IntParameter(15, 50, default=42, space='buy', optimize=is_optimize_32)
    buy_sma15_32 = DecimalParameter(0.900, 1, default=0.973, decimals=3, space='buy', optimize=is_optimize_32)
    buy_cti_32 = DecimalParameter(-1, 1, default=0.69, decimals=2, space='buy', optimize=is_optimize_32)

    sell_fastx = IntParameter(50, 100, default=84, space='sell', optimize=True)

    cci_opt = True
    sell_loss_cci = IntParameter(low=0, high=600, default=120, space='sell', optimize=cci_opt)
    sell_loss_cci_profit = DecimalParameter(-0.15, 0, default=-0.05, decimals=2, space='sell', optimize=cci_opt)


    buy_rsi_period = IntParameter(10, 190, default=20, space="buy")
    buy_rsi_fast_period = IntParameter(10, 190, default=10, space="buy")
    buy_rsi_slow_period = IntParameter(10, 190, default=40, space="buy")
    buy_sma_period = IntParameter(10, 190, default=15, space="buy")

    # --- 企业微信 Webhook（替换为你自己的key）---
    def _send_wecom(self, content: str) -> None:
        webhook_url = "**************************************************************************8"
        headers = {"Content-Type": "application/json"}
        data = {"msgtype": "markdown", "markdown": {"content": content}}
        try:
            response = requests.post(webhook_url, json=data, headers=headers, timeout=10)
            logger.info(f"WeCom response: {response.json()}")
        except Exception as e:
            logger.error(f"Failed to send WeCom message: {e}")
    @property
    def protections(self):

        return [
            {
                "method": "LowProfitPairs",
                "lookback_period_candles": 60,
                "trade_limit": 1,
                "stop_duration_candles": 60,
                "required_profit": -0.05
            },
            {
                "method": "CooldownPeriod",
                "stop_duration_candles": 5
            }
        ]

    def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime,
                        current_rate: float, current_profit: float, **kwargs) -> float:

        if current_profit >= 0.05:
            return -0.002

        if str(trade.enter_tag) == "buy_new" and current_profit >= 0.03:
            return -0.003

        return None

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # buy_1 indicators
        buy_sma15_32 = 2 - self.buy_sma15_32.value
        dataframe["sma_15"] = ta.SMA(
            dataframe, timeperiod=int(self.buy_sma_period.value)
        )
        dataframe['sma_15_a'] = dataframe['sma_15'] * buy_sma15_32
        dataframe['sma_15_b'] = dataframe['sma_15'] * self.buy_sma15_32.value
        dataframe["cti"] = pta.cti(dataframe["close"], length=20)
        dataframe["rsi"] = ta.RSI(dataframe, timeperiod=int(self.buy_rsi_period.value))
        dataframe["rsi_fast"] = ta.RSI(
            dataframe, timeperiod=int(self.buy_rsi_fast_period.value)
        )
        dataframe["rsi_slow"] = ta.RSI(
            dataframe, timeperiod=int(self.buy_rsi_slow_period.value)
        )
        # profit sell indicators
        stoch_fast = ta.STOCHF(dataframe, 5, 3, 0, 3, 0)
        dataframe['fastk'] = stoch_fast['fastk']

        dataframe['cci'] = ta.CCI(dataframe, timeperiod=20)

        dataframe['ma120'] = ta.MA(dataframe, timeperiod=120)
        dataframe['ma240'] = ta.MA(dataframe, timeperiod=240)

        # my add
        dataframe['change'] = (100 / dataframe['open'] * dataframe['close'] - 100)

        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        conditions = []
        dataframe.loc[:, 'enter_tag'] = ''
        buy_1 = (
                (dataframe['rsi_slow'] < dataframe['rsi_slow'].shift(1)) &
                (dataframe['rsi_fast'] < self.buy_rsi_fast_32.value) &
                (dataframe['rsi'] > self.buy_rsi_32.value) &
                (dataframe['close'] < dataframe['sma_15'] * self.buy_sma15_32.value) &
                (dataframe['cti'] < self.buy_cti_32.value)
        )

        # buy_new = (
        #         (dataframe['rsi_slow'] < dataframe['rsi_slow'].shift(1)) &
        #         (dataframe['rsi_fast'] < 34) &
        #         (dataframe['rsi'] > 28) &
        #         (dataframe['close'] < dataframe['sma_15'] * 0.96) &
        #         (dataframe['cti'] < self.buy_cti_32.value)
        # )


        conditions.append(buy_1)
        dataframe.loc[buy_1, 'enter_tag'] += 'buy_1'

        # conditions.append(buy_new)
        # dataframe.loc[buy_new, 'enter_tag'] += 'buy_new'

        if conditions:
            dataframe.loc[
                reduce(lambda x, y: x | y, conditions),
                'enter_long'] = 1
        return dataframe

    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:

        trade_hist = Trade.get_trades_proxy(is_open=False, close_date=current_time - timedelta(hours=int(current_time.strftime("%H"))) - timedelta(minutes=int(current_time.strftime("%M"))))
        profit = 0
        for t in trade_hist:
            profit = profit + t.close_profit

        if profit >= 0.05:
            return False

        msg = (
            f"**QuickSignal 买入**\n"
            f"交易对: {pair}\n"
            f"价格: {rate:.2f}\n"
            f"时间: {current_time.strftime('%Y-%m-%d %H:%M:%S')}"
        )
        self._send_wecom(msg)
        return True


    def custom_exit(self, pair: str, trade: 'Trade', current_time: 'datetime', current_rate: float,
                    current_profit: float, **kwargs):
        dataframe, _ = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe)
        current_candle = dataframe.iloc[-1].squeeze()

        min_profit = trade.calc_profit_ratio(trade.min_rate)

        if self.config['runmode'].value in ('live', 'dry_run'):
            state = self.cc
            pc = state.get(trade.id, {'date': current_candle['date'], 'open': current_candle['close'], 'high': current_candle['close'], 'low': current_candle['close'], 'close': current_rate, 'volume': 0})
            if current_candle['date'] != pc['date']:
                pc['date'] = current_candle['date']
                pc['high'] = current_candle['close']
                pc['low'] = current_candle['close']
                pc['open'] = current_candle['close']
                pc['close'] = current_rate
            if current_rate > pc['high']:
                pc['high'] = current_rate
            if current_rate < pc['low']:
                pc['low'] = current_rate
            if current_rate != pc['close']:
                pc['close'] = current_rate

            state[trade.id] = pc

        if trade.id not in TMP_HOLD:
            if len(dataframe.loc[dataframe['date'] < trade.open_date_utc]) > 0:
                open_candle = dataframe.loc[dataframe['date'] < trade.open_date_utc].iloc[-1].squeeze()
                if open_candle['close'] > open_candle["ma120"] and open_candle['close'] > open_candle["ma240"]:
                    TMP_HOLD.append(trade.id)
            elif current_candle['close'] > current_candle["ma120"] and current_candle['close'] > current_candle["ma240"]:
                TMP_HOLD.append(trade.id)

        if trade.id not in TMP_HOLD1:
            if (trade.open_rate - current_candle["ma120"]) / trade.open_rate >= 0.1:
                TMP_HOLD1.append(trade.id)

        if current_profit > 0:
            if self.config['runmode'].value in ('live', 'dry_run'):
                if current_time > pc['date'] + timedelta(minutes=9) + timedelta(seconds=55):
                    df = dataframe.copy()
                    df = df._append(pc, ignore_index = True)
                    stoch_fast = ta.STOCHF(df, 5, 3, 0, 3, 0)
                    df['fastk'] = stoch_fast['fastk']
                    cc = df.iloc[-1].squeeze()
                    if cc["fastk"] > self.sell_fastx.value:
                        return "fastk_profit_sell_2"
                else:
                    if current_candle["fastk"] > self.sell_fastx.value:
                        return "fastk_profit_sell"
            else:
                if current_candle["fastk"] > self.sell_fastx.value:
                    return "fastk_profit_sell"

        if min_profit <= -0.1:
            if current_profit > self.sell_loss_cci_profit.value:
                if current_candle["cci"] > self.sell_loss_cci.value:
                    return "cci_loss_sell"

        if trade.id in TMP_HOLD1 and current_candle["close"] < current_candle["ma120"]:
            TMP_HOLD1.remove(trade.id)
            return "ma120_sell_fast"

        if trade.id in TMP_HOLD and current_candle["close"] < current_candle["ma120"] and current_candle["close"] < current_candle["ma240"]:
            if min_profit <= -0.1:
                TMP_HOLD.remove(trade.id)
                return "ma120_sell"

        return None

    def confirm_trade_exit(self, pair: str, trade, order_type: str, amount: float,
                           rate: float, time_in_force: str, exit_reason: str,
                           current_time: datetime, **kwargs) -> bool:
        profit_pct = ((rate - trade.open_rate) / trade.open_rate) * 100
        msg = (
            f"**QuickSignal 卖出**\n"
            f"交易对: {pair}\n"
            f"价格: {rate:.2f}\n"
            f"盈亏: {profit_pct:.2f}%\n"
            f"原因: {exit_reason}\n"
            f"时间: {current_time.strftime('%Y-%m-%d %H:%M:%S')}"
        )
        self._send_wecom(msg)
        return True

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[:, ['exit_long', 'exit_tag']] = (0, 'long_out')
        return dataframe
