# --- Do not remove these libs --- from statistics import median from freqtrade.strategy import IStrategy from technical.util import resample_to_interval, resampled_merge from pandas import DataFrame import talib.abstract as ta from technical import qtpylib ### Now you can use logger.info('asfd') to log # freqtrade plot-dataframe --strategy HeikinAshi_BTC --datadir user_data/data/binance -c ./user_data/Chan/HeikinAshi_BTC.json --timerange=20250309- # freqtrade backtesting -c ./user_data/Chan/config/HeikinAshi_BTC.json --strategy HeikinAshi_BTC --strategy-path ./user_data/Chan/strategies --timerange=20251008- # freqtrade download-data -c ./user_data/Chan/config/HeikinAshi_BTC.json -t 1m 1m 1h 1d 1M --pairs BTC/USDT:USDT --timerange=20250405- # freqtrade download-data -c ./user_data/Chan/config/HeikinAshi_BTC.json -t 1m 1h 1d 1M --pairs BTC/USDT --timerange=20170101- # freqtrade hyperopt --hyperopt-loss SharpeHyperOptLossDaily --spaces roi --strategy HeikinAshi_BTC --strategy-path ./user_data/Chan/strategies -c ./user_data/Chan/config/HeikinAshi_BTC.json -e 200 --timerange=20250201-20250901 # freqtrade edge -c ./user_data/Chan/config/HeikinAshi_BTC.json --strategy HeikinAshi_BTC --strategy-path ./user_data/Chan/strategies --timerange 20250721-20250901 # freqtrade plot-dataframe -c ./user_data/Chan/config/HeikinAshi_BTC.json --strategy HeikinAshi_BTC --strategy-path ./user_data/Chan/strategies --timerange 20250721-20250901 # sudo docker compose run --rm heikinashi_btc backtesting -c ./user_data/Chan/config/HeikinAshi_BTC.json --strategy HeikinAshi_BTC --strategy-path ./user_data/Chan/strategies --timerange=20250721- # sudo docker compose run --rm heikinashi_btc download-data -c ./user_data/Chan/config/HeikinAshi_BTC.json --pairs BTC/USDT:USDT -t 1m --timerange 20240101- # sudo docker compose run --rm heikinashi_btc trade -c ./user_data/Chan/config/HeikinAshi_BTC.json --strategy HeikinAshi_BTC --strategy-path ./user_data/Chan/strategies class HeikinAshi_BTC(IStrategy): """ 使用 Heikin Ashi 蜡烛的双均线趋势策略,支持多/空。 逻辑摘要: - 指标:ha_open/ha_close/ha_high/ha_low + ha_close 的快/慢 EMA - 做多:ha_close > ha_open 且 ha_ema_fast > ha_ema_slow - 做空:ha_close < ha_open 且 ha_ema_fast < ha_ema_slow - 退出:相反信号或均线反转 """ INTERFACE_VERSION: int = 3 time1h = 1440 can_short: bool = True timeframe: str = "1h" process_only_new_candles: bool = True # ROI 与止损可根据需要在配置中覆盖 minimal_roi = { "120": 0.01, "60": 0.02, "0": 0.03, } stoploss: float = -0.30 use_exit_signal: bool = True exit_profit_only: bool = False ignore_roi_if_entry_signal: bool = False # 需要的历史K线数量(包含EMA等指标预热) startup_candle_count: int = 200 plot_config = { "main_plot": { "ha_close": {"color": "orange"}, "ha_ema_fast": {"color": "green"}, "ha_ema_slow": {"color": "red"}, }, "subplots": {}, } def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """计算 Heikin Ashi 相关指标与均线。""" if dataframe is None or dataframe.empty: return dataframe dataframe_1h = resample_to_interval(dataframe, self.get_ticker_indicator() * self.time1h) ha = qtpylib.heikinashi(dataframe_1h) dataframe_1h["ha_open"] = ha["open"] dataframe_1h["ha_close"] = ha["close"] dataframe_1h["ha_high"] = ha["high"] dataframe_1h["ha_low"] = ha["low"] # 对 Heikin Ashi close 做 EMA 平滑,减少噪声 dataframe_1h["ha_ema_fast"] = ta.EMA(dataframe_1h["ha_close"], timeperiod=24) dataframe_1h["ha_ema_slow"] = ta.EMA(dataframe_1h["ha_close"], timeperiod=52) dataframe = resampled_merge(dataframe, dataframe_1h) return dataframe def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """根据 HA 趋势定义进场信号(多/空)。""" if dataframe is None or dataframe.empty: return dataframe ha_close = 'resample_{}_ha_close'.format(self.get_ticker_indicator()*self.time1h) ha_open = 'resample_{}_ha_open'.format(self.get_ticker_indicator()*self.time1h) ha_ema_fast = 'resample_{}_ha_ema_fast'.format(self.get_ticker_indicator()*self.time1h) ha_ema_slow = 'resample_{}_ha_ema_slow'.format(self.get_ticker_indicator()*self.time1h) volume = 'resample_{}_volume'.format(self.get_ticker_indicator()*self.time1h) # 做多:HA 实体向上 且 快线上穿慢线(金叉) dataframe.loc[ ( (dataframe[ha_close] > dataframe[ha_open]) & (qtpylib.crossed_above(dataframe[ha_ema_fast], dataframe[ha_ema_slow])) & (dataframe["volume"] > 0) ), ["enter_long", "enter_tag"], ] = (1, "ha_trend_long") # 做空:HA 实体向下 且 快线下穿慢线(死叉) dataframe.loc[ ( (dataframe[ha_close] < dataframe[ha_open]) & (qtpylib.crossed_below(dataframe[ha_ema_fast], dataframe[ha_ema_slow])) & (dataframe["volume"] > 0) ), ["enter_short", "enter_tag"], ] = (1, "ha_trend_short") return dataframe def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: """根据 HA 反转或均线反转定义出场信号(多/空)。""" if dataframe is None or dataframe.empty: return dataframe ha_close = 'resample_{}_ha_close'.format(self.get_ticker_indicator()*self.time1h) ha_open = 'resample_{}_ha_open'.format(self.get_ticker_indicator()*self.time1h) ha_ema_fast = 'resample_{}_ha_ema_fast'.format(self.get_ticker_indicator()*self.time1h) ha_ema_slow = 'resample_{}_ha_ema_slow'.format(self.get_ticker_indicator()*self.time1h) volume = 'resample_{}_volume'.format(self.get_ticker_indicator()*self.time1h) # 多单退出:HA 变为阴 或 快线下穿慢线(死叉) dataframe.loc[ ( ( (dataframe[ha_close] < dataframe[ha_open]) | (qtpylib.crossed_below(dataframe[ha_ema_fast], dataframe[ha_ema_slow])) ) & (dataframe["volume"] > 0) ), ["exit_long", "exit_tag"], ] = (1, "ha_long_exit") # 空单退出:HA 变为阳 或 快线上穿慢线(金叉) dataframe.loc[ ( ( (dataframe[ha_close] > dataframe[ha_open]) | (qtpylib.crossed_above(dataframe[ha_ema_fast], dataframe[ha_ema_slow])) ) & (dataframe["volume"] > 0) ), ["exit_short", "exit_tag"], ] = (1, "ha_short_exit") return dataframe def get_ticker_indicator(self): return int(self.timeframe[:-1])