# source: https://raw.githubusercontent.com/wiktorj137/btc-strategy-lab/b68a5518b4a3eba2fde1733160d7d7de356023b5/user_data/strategies/EmaCrossFlow.py
# directory_url: https://github.com/wiktorj137/btc-strategy-lab/blob/main/user_data/strategies/
# User: wiktorj137
# Repository: btc-strategy-lab
# --------------------"""EmaCrossFunding + filtr pozycjonowania tlumu.

Do trzech regul EmaCross i filtra funding dokladamy czwarty sygnal z danych
pozarynkowych: stosunek kont long do short na Binance Futures.

Dlaczego akurat ten: w tescie na przeplywie zlecen byl jedynym obok
"duzi kontra tlum", ktory na obu polowach danych mial ten sam znak i przewage
powyzej progu kosztow (-42.7 bps na horyzoncie dobowym). Wszystko inne
miescilo sie w szumie albo zmienialo znak.

Kierunek jest kontrarianski: gdy wiekszosc kont siedzi na long, zwroty w
kolejnej dobie sa nizsze. Uzywamy tego jednostronnie - nie wchodzimy w
maksymalnie wylewarowany rynek. Tak samo jak filtr funding: sygnal moze sie
odwrocic, wiec nie budujemy na nim kierunku, tylko unikamy skrajnosci.

ZASTRZEZENIE: efekt slabnie. -76 bps w pierwszej polowie proby, -14 w drugiej.
"""
from __future__ import annotations

from pathlib import Path

import freqtrade.vendor.qtpylib.indicators as qtpylib
import pandas as pd
import talib.abstract as ta
from pandas import DataFrame

from freqtrade.strategy import DecimalParameter, IntParameter, IStrategy

WEB = Path(__file__).resolve().parents[2] / "research/webdata"


class Github_wiktorj137_btc_strategy_lab__EmaCrossFlow__20260822_031820(IStrategy):
    INTERFACE_VERSION = 3

    timeframe = "1h"
    can_short = False
    process_only_new_candles = True
    startup_candle_count = 1300

    minimal_roi = {"0": 10}
    stoploss = -0.15
    trailing_stop = True

    ema_period = IntParameter(300, 1300, default=600, space="buy", optimize=True)
    exit_threshold = DecimalParameter(0.3, 3.0, default=2.0, decimals=1,
                                      space="sell", optimize=True)
    funding_max_pct = IntParameter(50, 100, default=55, space="buy", optimize=True)
    crowd_max_pct = IntParameter(50, 100, default=100, space="buy", optimize=True)

    _funding = None
    _crowd = None

    @classmethod
    def _load(cls) -> None:
        if cls._funding is None:
            f = pd.read_feather(WEB / "funding.feather")
            f["date"] = pd.to_datetime(f["date"], utc=True)
            cls._funding = f.set_index("date").sort_index()["funding"]
        if cls._crowd is None:
            o = pd.read_feather(WEB / "orderflow.feather")
            o["date"] = pd.to_datetime(o["date"], utc=True)
            cls._crowd = o.set_index("date").sort_index()["count_long_short_ratio"]

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        self._load()
        dataframe["ema"] = ta.EMA(dataframe, timeperiod=int(self.ema_period.value))
        dataframe["ema_exit"] = dataframe["ema"] * (100 - float(self.exit_threshold.value)) / 100

        idx = pd.to_datetime(dataframe["date"], utc=True)

        # ffill = ostatnia OPUBLIKOWANA wartosc, nigdy przyszla
        f = self._funding.reindex(idx, method="ffill")
        dataframe["funding_pct"] = (
            pd.Series(f.rolling(72, min_periods=24).mean().to_numpy())
            .rolling(24 * 180, min_periods=24 * 30).rank(pct=True) * 100).to_numpy()

        c = self._crowd.reindex(idx, method="ffill")
        dataframe["crowd_pct"] = (
            pd.Series(c.rolling(24, min_periods=6).mean().to_numpy())
            .rolling(24 * 180, min_periods=24 * 30).rank(pct=True) * 100).to_numpy()
        return dataframe

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (qtpylib.crossed_above(dataframe["close"], dataframe["ema"]))
            & (dataframe["volume"] > 0)
            & ((dataframe["funding_pct"] < float(self.funding_max_pct.value))
               | dataframe["funding_pct"].isna())
            & ((dataframe["crowd_pct"] < float(self.crowd_max_pct.value))
               | dataframe["crowd_pct"].isna()),
            ["enter_long", "enter_tag"],
        ] = (1, "ema_flow_ok")
        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            qtpylib.crossed_below(dataframe["close"], dataframe["ema_exit"]),
            ["exit_long", "exit_tag"],
        ] = (1, "ema_cross_down")
        return dataframe
