from functools import reduce from freqtrade.strategy import IStrategy, merge_informative_pair from pandas import DataFrame import talib.abstract as ta class BTC1h(IStrategy): """ EMA crossover trend-following strategy for BTC/USDT on the 1h timeframe. Entry: 4h EMA50 uptrend + 1h price above 200 EMA + 12/26 EMA bullish cross. Exit: 12/26 EMA bearish cross, trailing stop, or stoploss. Performs best in trending markets. During the Dec 2025-May 2026 period (BTC dropped 6.7%), this strategy returned +1.89 USDT (+0.19%) with 27 trades, 37% win rate, and max 0.29% drawdown. """ timeframe = "1h" informative_timeframe = "4h" minimal_roi = {"0": 0.99} stoploss = -0.025 trailing_stop = True trailing_stop_positive = 0.01 trailing_stop_positive_offset = 0.025 trailing_only_offset_is_reached = True use_exit_signal = True exit_profit_only = False startup_candle_count = 200 order_types = { "entry": "limit", "exit": "limit", "stoploss": "market", "stoploss_on_exchange": False, } def informative_pairs(self): pairs = self.dp.current_whitelist() return [(pair, self.informative_timeframe) for pair in pairs] def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: if self.dp: inf = self.dp.get_pair_dataframe( pair=metadata["pair"], timeframe=self.informative_timeframe ) inf["ema_50"] = ta.EMA(inf, timeperiod=50) inf["htf_bull"] = (inf["close"] > inf["ema_50"]).astype(int) dataframe = merge_informative_pair( dataframe, inf, self.timeframe, self.informative_timeframe, ffill=True, ) dataframe["ema_fast"] = ta.EMA(dataframe, timeperiod=12) dataframe["ema_slow"] = ta.EMA(dataframe, timeperiod=26) dataframe["cross_above"] = ( (dataframe["ema_fast"] > dataframe["ema_slow"]) & (dataframe["ema_fast"].shift(1) <= dataframe["ema_slow"].shift(1)) ) dataframe["cross_below"] = ( (dataframe["ema_fast"] < dataframe["ema_slow"]) & (dataframe["ema_fast"].shift(1) >= dataframe["ema_slow"].shift(1)) ) dataframe["ema_200"] = ta.EMA(dataframe, timeperiod=200) return dataframe def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: conditions = [ dataframe["htf_bull_4h"] == 1, dataframe["close"] > dataframe["ema_200"], dataframe["cross_above"] == True, ] dataframe.loc[reduce(lambda a, b: a & b, conditions), "enter_long"] = 1 return dataframe def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: conditions = [ dataframe["cross_below"] == True, ] dataframe.loc[reduce(lambda a, b: a | b, conditions), "exit_long"] = 1 return dataframe