# --- Do not remove these libs --- from statistics import median from freqtrade.strategy import IStrategy import sys import os # 添加父目录到系统路径 sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from chanlun.core.ChanEnum import Chan_FX_TYPE, Chan_KLC_FX, Chan_BI_DIR, Chan_KLC_FX # -------------------------------- from technical.util import resample_to_interval, resampled_merge import talib.abstract as ta from pandas import DataFrame from datetime import datetime, timedelta from freqtrade.persistence import Trade from typing import Optional import logging logger = logging.getLogger(__name__) from chanlun import TF_DF ### Now you can use logger.info('asfd') to log # freqtrade plot-dataframe --strategy ChanLun_BTC_15 --datadir user_data/data/binance -c ./user_data/ChanLun_SOL_15.json --timerange=20250309- # freqtrade trade -c ./user_data/Chan/config/ChanLun_BTC_15.json --strategy ChanLun_BTC_15 --strategy-path ./user_data/Chan/strategies # freqtrade backtesting --export none -c ./user_data/Chan/config/ChanLun_BTC_15.json --strategy ChanLun_BTC_15 --strategy-path ./user_data/Chan/strategies --timerange=20250525- # freqtrade lookahead-analysis --export none -c ./user_data/Chan/config/ChanLun_BTC_15.json --strategy ChanLun_BTC_15 --strategy-path ./user_data/Chan/strategies --timerange=20250525- # freqtrade download-data -c ./user_data/Chan/config/ChanLun_BTC_15.json -t 1m --pairs BTC/USDT:USDT --timerange=20250405- # freqtrade hyperopt --hyperopt-loss SharpeHyperOptLossDaily --spaces roi stoploss --strategy ChanLun_BTC_15 --strategy-path ./user_data/Chan/strategies -c ./user_data/Chan/config/ChanLun_BTC_15.json -e 200 --timerange=20250201-20250401 # sudo docker compose run --rm chan_btc backtesting -c ./user_data/Chan/config/ChanLun_SOL.json --strategy ChanLun_SOL --strategy-path ./user_data/Chan/strategies --timerange=20250101- # sudo docker compose run --rm chan_btc download-data -c ./user_data/Chan/config/ChanLun_BTC_15.json --pairs BTC/USDT:USDT -t 1m --timerange 20240101- # sudo docker compose run --rm chan_btc trade -c ./user_data/Chan/config/ChanLun_BTC_15.json --strategy ChanLun_BTC_15 --strategy-path ./user_data/Chan/strategies class ChanLun_BTC_15(IStrategy): INTERFACE_VERSION: int = 3 # Minimal ROI designed for the strategy. # This attribute will be overridden if the config file contains "minimal_roi" # 30m and 1h minimal_roi = { "0": 0.60, "360": 0.2, "640": 0.1, "1200": 0 } # 5m and 15m minimal_roi = { "0": 0.1, "60": 0.05, "120": 0.02, "240": 0 } # 5m and 15m minimal_roi_1 = { "0": 0.05, "120": 0.02, "240": 0.01, "360": 0 } # 15m and 30m minimal_roi_1 = { "0": 0.1, "240": 0.05, "480": 0.03, "600": 0 } minimal_roi_2 = { "0": 0.10, "1200": 0.05, "2400": 0.025, "3600": 0 } can_short = True lev = 1.0 stoploss = -0.3 bsp_offset = 2 trailing_stop = False trailing_stop_positive = 0.025 trailing_stop_positive_offset = 0.045 trailing_only_offset_is_reached = False position_adjustment_enable = True startup_candle_count = 1000 time5 = 5 time15 = 15 time30 = 30 time60 = 60 time4h = 240 time1d = 1440 time5 = 1440 last_time = datetime.now() def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: df_5m = resample_to_interval(dataframe, self.time5) df_15m = resample_to_interval(dataframe, self.time15) dataframe = TF_DF.add_indicators(dataframe) df_5m = TF_DF.add_indicators(df_5m) df_15m = TF_DF.add_indicators(df_15m) dataframe = resampled_merge(dataframe, df_5m) dataframe = resampled_merge(dataframe, df_15m) return dataframe def custom_entry_price(self, pair: str, trade: Trade | None, current_time: datetime, proposed_rate: float, entry_tag: str | None, side: str, **kwargs) -> float: new_entryprice = proposed_rate if trade: if trade.is_short: new_entryprice = proposed_rate - 50 else: new_entryprice = proposed_rate + 50 return new_entryprice def custom_exit_price(self, pair: str, trade: Trade, current_time: datetime, proposed_rate: float, current_profit: float, exit_tag: str | None, **kwargs) -> float: new_exitprice = proposed_rate if trade: if trade.is_short: new_exitprice = proposed_rate + 50 else: new_exitprice = proposed_rate - 50 return new_exitprice def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: state_str = 'resample_{}_state'.format(self.get_ticker_indicator()*self.time5) fx_str = 'resample_{}_fx'.format(self.get_ticker_indicator()*self.time5) bsp_str = 'resample_{}_bsp'.format(self.get_ticker_indicator()*self.time5) shift = self.time5*self.bsp_offset dataframe.loc[ ( #(dataframe['state'] == "-30") #(dataframe[state_str].shift(shift) > 1.0) & #(dataframe[fx_str].shift(shift) == -1) (dataframe[bsp_str].shift(shift) == -1) #(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time5)].shift(self.time5) == "-10") & #(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time30)] == "-10") & #(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time5)].shift(self.time5) == "-10") #(qtpylib.crossed_above(dataframe['macd'], dataframe['macdsignal'])) ), ['enter_long', 'enter_tag']] = (1, 'long_signal_chan') dataframe.loc[ ( #(dataframe['state'] == "-30") #(dataframe[state_str].shift(shift) > 1.0) & #(dataframe[fx_str].shift(shift) == 1) (dataframe[bsp_str].shift(shift) == 1) #(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time5)].shift(self.time5) == "-10") & #(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time30)] == "-10") & #(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time5)].shift(self.time5) == "-10") #(qtpylib.crossed_above(dataframe['macd'], dataframe['macdsignal'])) ), ['enter_short', 'enter_tag']] = (1, 'short_signal_chan') return dataframe def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: state_str = 'resample_{}_state'.format(self.get_ticker_indicator()*self.time5) fx_str = 'resample_{}_fx'.format(self.get_ticker_indicator()*self.time5) bsp_str = 'resample_{}_bsp'.format(self.get_ticker_indicator()*self.time5) shift = self.time5*self.bsp_offset dataframe.loc[ ( #(dataframe['state']== "30") #(dataframe[state_str].shift(shift) > 1.0) & #(dataframe[fx_str].shift(shift) == 1) (dataframe[bsp_str].shift(shift) == 1) #(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time30)] == "10") & #(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time60)] == "10") ), ['exit_long', 'exit_tag']] = (1, 'long_close_signal_chan') dataframe.loc[ ( #(dataframe['state']== "30") #(dataframe[state_str].shift(shift) > 1.0) & #(dataframe[fx_str].shift(shift) == -1) (dataframe[bsp_str].shift(shift) == -1) #(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time30)] == "10") & #(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time60)] == "10") ), ['exit_short', 'exit_tag']] = (1, 'short_close_signal_chan') return dataframe def leverage(self, pair: str, current_time: datetime, current_rate: float, proposed_leverage: float, max_leverage: float, entry_tag: Optional[str], side: str, **kwargs) -> float: return self.lev def get_ticker_indicator(self): return int(self.timeframe[:-1])