# --- Do not remove these libs --- from statistics import median from freqtrade.strategy import IStrategy, stoploss_from_absolute import sys import os # 添加父目录到系统路径 sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from ChanLun import ChanLun from 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 import pandas as pd from datetime import datetime, timedelta from freqtrade.persistence import Trade, Order from typing import Optional import logging logger = logging.getLogger(__name__) """ 大周期:1h 小周期:15m,30m 大周期EMA156以下找做空机会 找到最近的中枢,中枢下跌以后穿过EMA156,EMA52均线,形成死叉,macd黄白线穿越0轴 EMA24,EMA52,EMA104,EMA156成下跌趋势依次排列(EMA156 > EMA104 > EMA52 > EMA24) 做空 1. 做空开始点位条件: 确定下跌周期,价格在大于大周期的时间周期找到MACD归零轴+EMA52阻力线,按照K线动能理论,小周期确认是否背驰,背驰则开仓并且MACD穿零轴 止损放到最近的顶分型高点或者价格突破EMA156 2. 开始点位止盈策略 计算盈亏比方式:至少1:2,到达1:2后平仓一半,移动止损到开仓价,1:3再平仓剩下的一半仓位,依次类推 如果大周期遇到底背离可以平完所有仓位 3. 加仓点位 小周期顶分型+价格接近或突破大周期EMA24但是不突破EMA52后下跌可以加仓到最大仓位+大周期黄白线归零轴/小周期顶分型+小周期EMA52归零轴 大周期顶分型+大周期macd归零轴可以加仓到最大仓位 大周期顶分型或顶分型后,macd穿零轴后价格和macd红绿柱背驰可以加仓到最大仓位 小周期顶分型+大周期macd归零轴 """ ### Now you can use logger.info('asfd') to log # freqtrade plot-dataframe --strategy ChanLun_BTC --datadir user_data/data/binance -c ./user_data/ChanLun_SOL_30.json --timerange=20250309- # freqtrade trade -c ./user_data/Chan/config/ChanLun_EMA_Align.json --strategy ChanLun_EMA_Align --strategy-path ./user_data/Chan/strategies # freqtrade backtesting -c ./user_data/Chan/config/ChanLun_EMA_Align.json --strategy ChanLun_EMA_Align --strategy-path ./user_data/Chan/strategies --timerange=20260101- # freqtrade download-data -c ./user_data/Chan/config/ChanLun_EMA_Align.json -t 1m 1m 1h 1d 1w 1M --pairs BTC/USDT:USDT --timerange=20240101- # freqtrade download-data -c ./user_data/Chan/config/ChanLun_EMA_Align.json -t 1m 1h 1d 1M --pairs BTC/USDT --timerange=20170101- # freqtrade hyperopt --hyperopt-loss SharpeHyperOptLossDaily --spaces roi --strategy ChanLun_EMA_Align --strategy-path ./user_data/Chan/strategies -c ./user_data/Chan/config/ChanLun_EMA_Align.json -e 200 --timerange=20250201-20250901 # freqtrade edge -c ./user_data/Chan/config/ChanLun_EMA_Align.json --strategy ChanLun_EMA_Align --strategy-path ./user_data/Chan/strategies --timerange 20250721-20250901 # freqtrade plot-dataframe -c ./user_data/Chan/config/ChanLun_EMA_Align.json --strategy ChanLun_EMA_Align --strategy-path ./user_data/Chan/strategies --timerange 20250721-20250901 # sudo docker compose run --rm chanlun_btc backtesting -c ./user_data/Chan/config/ChanLun_EMA_Align.json --strategy ChanLun_EMA_Align --strategy-path ./user_data/Chan/strategies --timerange=20250721- # sudo docker compose run --rm chanlun_btc download-data -c ./user_data/Chan/config/ChanLun_EMA_Align.json --pairs BTC/USDT:USDT -t 1m --timerange 20240101- # sudo docker compose run --rm chanlun_btc trade -c ./user_data/Chan/config/ChanLun_EMA_Align.json --strategy ChanLun_EMA_Align --strategy-path ./user_data/Chan/strategies class ChanLun_EMA_Align(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.15, "360": 0.2, "640": 0.1, "1200": 0 } # 5m and 15m minimal_roi_1 = { "0": 0.1, "60": 0.05, "120": 0.02, "240": 0 } # 15m and 30m minimal_roi_1 = { "0": 0.1, "240": 0.05, "480": 0.03, "600": 0 } minimal_roi_1 = { "0": 1.50, "120": 0.05, "240": 0.025, "360": 0 } can_short = True lev = 1.0 stoploss = -0.3 # 设置为很大的负值,让custom_stoploss来控制 use_custom_stoploss = False # 启用自定义止损 trailing_stop = False trailing_stop_positive = 0.03 trailing_stop_positive_offset = 0.06 trailing_only_offset_is_reached = False # 关闭分批止盈/仓位调整 position_adjustment_enable = False # startup_candle_count = 1600 time5 = 15 time15 = 15 time30 = 30 time60 = 60 def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe = self.add_indicators(dataframe) dataframe_5m = resample_to_interval(dataframe, self.get_ticker_indicator() * self.time5) dataframe_5m = self.add_indicators(dataframe_5m) dataframe = resampled_merge(dataframe, dataframe_5m) return dataframe def add_indicators(self, dataframe): dataframe['ema24'] = ta.EMA(dataframe, timeperiod=24) dataframe['dir24'] = dataframe['close'] - dataframe['ema24'] dataframe['ema52'] = ta.EMA(dataframe, timeperiod=52) dataframe['dir52'] = dataframe['close'] - dataframe['ema52'] dataframe['ema104'] = ta.EMA(dataframe, timeperiod=104) dataframe['dir104'] = dataframe['close'] - dataframe['ema104'] dataframe['ema156'] = ta.EMA(dataframe, timeperiod=156) dataframe['dir156'] = dataframe['close'] - dataframe['ema156'] dataframe['dir52_156'] = dataframe['dir52'] - dataframe['dir156'] dataframe['dir52_104'] = dataframe['dir52'] - dataframe['dir104'] dataframe_macd = ta.MACD(dataframe, fast=12, slow=26, signal=9) dataframe['macdsignal'] = dataframe_macd['macdsignal'] dataframe['macd'] = dataframe_macd['macd'] dataframe['macdhist'] = dataframe_macd['macdhist'] dataframe['ema_align'] = ( ((dataframe['ema24'] > dataframe['ema52']) & (dataframe['ema52'] > dataframe['ema104'])) | ((dataframe['ema24'] < dataframe['ema52']) & (dataframe['ema52'] < dataframe['ema104'])) ) 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 adjust_trade_position(self, trade: Trade, current_time: datetime, current_rate: float, current_profit: float, min_stake: Optional[float], max_stake: float, current_entry_rate: float, current_exit_rate: float, current_entry_profit: float, current_exit_profit: float, **kwargs) -> Optional[float]: # 关闭分批止盈,始终不调整仓位 return None def custom_exit(self, pair: str, trade: Trade, current_time: datetime, current_rate: float, current_profit: float, **kwargs): # 不做分批止盈/最终止盈处理,退出由策略信号/ROI/止损决定 return None def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: resample_5m_align = 'resample_{}_ema_align'.format(self.get_ticker_indicator() * self.time5) # 使用高周期的 dir52_156 方向作为多空判定依据 resample_5m_dir = 'resample_{}_dir52_104'.format(self.get_ticker_indicator() * self.time5) resample_5m_signal = 'resample_{}_macdsignal'.format(self.get_ticker_indicator() * self.time5) dataframe.loc[ (dataframe[resample_5m_align]) & (dataframe[resample_5m_dir] < 0) & (dataframe[resample_5m_signal] > 0), ['enter_long', 'enter_tag']] = (1, 'long_signal_chan') dataframe.loc[ (dataframe[resample_5m_align]) & (dataframe[resample_5m_dir] > 0) & (dataframe[resample_5m_signal] < 0), ['enter_short', 'enter_tag']] = (1, 'short_signal_chan') return dataframe def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[ (dataframe['dir156'] < 0) & (dataframe['dir52_156'] < 0) & (dataframe['macdhist'] < 0), ['exit_long', 'exit_tag']] = (1, 'long_exit_signal_chan') dataframe.loc[ (dataframe['macd'] > 0) & (dataframe['dir156'] > 0) & (dataframe['dir52_156'] > 0) & (dataframe['macdhist'] > 0), ['exit_short', 'exit_tag']] = (1, 'short_exit_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])