From 4ea4b8cac5cb902a55f73093f48e7e2cb8812096 Mon Sep 17 00:00:00 2001 From: jackyu66git Date: Thu, 11 Sep 2025 18:39:57 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BD=BF=E7=94=A8=E6=9C=AA?= =?UTF-8?q?=E6=9D=A5=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ChanKLU.py | 4 +- ChanMACD.py | 6 --- ChanMACDHistSet.py | 29 +++++++------- config/ChanLun_BTC_30.json | 6 +-- strategies/ChanLun_BTC_30.py | 48 +++++++++++------------ web/nginx/logs/error.log | 75 ++++++++++++++++++++++++++++++++++++ 6 files changed, 117 insertions(+), 51 deletions(-) diff --git a/ChanKLU.py b/ChanKLU.py index aa21388..52296e3 100644 --- a/ChanKLU.py +++ b/ChanKLU.py @@ -653,10 +653,10 @@ class ChanKLU: return self.macd_state if self.macd > 0: if self.macd < self.signal and self.close > self.ema52 and self.low < self.ema52: - self.near0_return = 6 + self.near0_return = 0 else: if self.macd > self.signal and self.close < self.ema52 and self.high > self.ema52: - self.near0_return = 7 + self.near0_return = 0 # CROSS0 仅以 Signal 穿越零轴判定 if self.pre.signal >= 0 and self.signal < 0: self.macd_state = Chan_MACD_STATE.CROSS0_DOWN diff --git a/ChanMACD.py b/ChanMACD.py index 02d9c75..0515096 100644 --- a/ChanMACD.py +++ b/ChanMACD.py @@ -131,12 +131,6 @@ class ChanMACD(): last_klu = klu klu.cal_macd_state() #print(klu.time, klu.macd_state, klu.continue_div, klu.separate_div, klu.macd, klu.signal, klu.macdhist, klu.ema24, klu.ema52, klu.close) - if last_histset: - last_histset.set_end_klu(last_klu) - if last_unittf: - last_unittf.set_end_klu(last_klu, None) - if last_seg: - last_seg.set_end_klu(last_klu) return self.klu_list def cal_macd(self): last_seg = None diff --git a/ChanMACDHistSet.py b/ChanMACDHistSet.py index 6ce09f3..4e10807 100644 --- a/ChanMACDHistSet.py +++ b/ChanMACDHistSet.py @@ -1,4 +1,4 @@ -from ChanEnum import Chan_MACDHISTSET_DIR, Chan_MACDUNITTF_DIV +from ChanEnum import Chan_MACDHISTSET_DIR, Chan_MACDUNITTF_DIV, Chan_MACD_STATE class ChanMACDHistSet(): def __init__(self, index, start_time, start_klu, pre_histset, dir): @@ -52,11 +52,12 @@ class ChanMACDHistSet(): if self.peak_klu: if abs(klu.pre.macdhist) > abs(self.peak_klu.macdhist): self.peak_klu = klu.pre - self.div_count = 0 - self.peak_div_list = [] + #self.div_count = 0 + #self.peak_div_list = [] else: - self.peak_div_list.append(klu.pre) - self.div_count += 1 + if klu.pre.macd * klu.pre.macdhist > 0: + self.peak_div_list.append(klu.pre) + self.div_count += 1 else: self.peak_klu = klu.pre else: @@ -66,20 +67,18 @@ class ChanMACDHistSet(): if self.peak_klu: if abs(klu.pre.macdhist) > abs(self.peak_klu.macdhist): self.peak_klu = klu.pre - self.div_count = 0 - self.peak_div_list = [] + #self.div_count = 0 + #self.peak_div_list = [] else: - self.peak_div_list.append(klu.pre) - self.div_count += 1 + if klu.pre.macd * klu.pre.macdhist > 0 and klu.pre.signal * klu.pre.macdhist > 0 and abs(klu.pre.signal) > abs(klu.pre.macdhist): + self.peak_div_list.append(klu.pre) + self.div_count += 1 + klu.pre.continue_div = True else: self.peak_klu = klu.pre def set_end_klu(self, end_klu): self.end_klu = end_klu self.end_time = end_klu.time - if len(self.peak_div_list) > 0: - klu = self.peak_div_list[-1] - if klu.macd * klu.macdhist > 0: - klu.continue_div = True if self.start_klu.index == end_klu.index: self.peak_klu = self.start_klu if self.start_klu.index + 1 == end_klu.index: @@ -94,8 +93,10 @@ class ChanMACDHistSet(): peak = klu self.peak_klu = peak peak_str = "" + state_str = "" for peak_div in self.peak_div_list: peak_str += f"{peak_div.time}, " + state_str += f"{peak_div.macd_state}, " #if self.peak_klu and len(self.peak_div_list) > 0: - #print("Continue Div: ",self.start_time, "Peak:", self.peak_klu.time, "Div: ", peak_str, self.histset_dir, self.div_count) + #print("Continue Div: ",self.start_time, "Peak:", self.peak_klu.time, "Div: ", peak_str, state_str) \ No newline at end of file diff --git a/config/ChanLun_BTC_30.json b/config/ChanLun_BTC_30.json index cb75620..0d5524d 100644 --- a/config/ChanLun_BTC_30.json +++ b/config/ChanLun_BTC_30.json @@ -42,7 +42,7 @@ "ccxt_config": {}, "ccxt_async_config": {}, "pair_whitelist": [ - "ETH/USDT:USDT" + "BTC/USDT:USDT" ], "pair_blacklist": [ "BNB/.*" @@ -64,8 +64,8 @@ }, "api_server": { "enabled": true, - "listen_ip_address": "127.0.0.1", - "listen_port": 8812, + "listen_ip_address": "0.0.0.0", + "listen_port": 8813, "verbosity": "error", "enable_openapi": false, "jwt_secret_key": "14d3510740e2c39a973a8895f1aa2704d98d08b86170260085709fa5ea48251d", diff --git a/strategies/ChanLun_BTC_30.py b/strategies/ChanLun_BTC_30.py index 7702ae9..815fe9f 100644 --- a/strategies/ChanLun_BTC_30.py +++ b/strategies/ChanLun_BTC_30.py @@ -22,7 +22,9 @@ logger = logging.getLogger(__name__) # freqtrade trade -c ./user_data/Chan/config/ChanLun_BTC_30.json --strategy ChanLun_BTC_30 --strategy-path ./user_data/Chan/strategies # freqtrade backtesting -c ./user_data/Chan/config/ChanLun_BTC_30.json --strategy ChanLun_BTC_30 --strategy-path ./user_data/Chan/strategies --timerange=20250901- # freqtrade download-data -c ./user_data/Chan/config/ChanLun_BTC_30.json -t 1m --pairs BTC/USDT:USDT --timerange=20250405- -# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLossDaily --spaces roi stoploss --strategy ChanLun_BTC_30 --strategy-path ./user_data/Chan/strategies -c ./user_data/Chan/config/ChanLun_BTC_30.json -e 200 --timerange=20250201-20250401 +# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLossDaily --spaces roi --strategy ChanLun_BTC_30 --strategy-path ./user_data/Chan/strategies -c ./user_data/Chan/config/ChanLun_BTC_30.json -e 200 --timerange=20250201-20250901 +# freqtrade edge -c ./user_data/Chan/config/ChanLun_BTC_30.json --strategy ChanLun_BTC_30 --strategy-path ./user_data/Chan/strategies --timerange 20250721-20250901 +# freqtrade plot-dataframe -c ./user_data/Chan/config/ChanLun_BTC_30.json --strategy ChanLun_BTC_30 --strategy-path ./user_data/Chan/strategies --timerange 20250721-20250901 # sudo docker compose run --rm chanlun_btc backtesting -c ./user_data/Chan/config/ChanLun_BTC_30.json --strategy ChanLun_BTC_30 --strategy-path ./user_data/Chan/strategies --timerange=20250721- # sudo docker compose run --rm chanlun_btc download-data -c ./user_data/Chan/config/ChanLun_BTC_30.json --pairs BTC/USDT:USDT -t 1m --timerange 20240101- @@ -67,8 +69,8 @@ class ChanLun_BTC_30(IStrategy): use_custom_stoploss = True # 启用自定义止损 trailing_stop = False - trailing_stop_positive = 0.025 - trailing_stop_positive_offset = 0.045 + trailing_stop_positive = 0.03 + trailing_stop_positive_offset = 0.06 trailing_only_offset_is_reached = False # 关闭分批止盈/仓位调整 @@ -133,18 +135,8 @@ class ChanLun_BTC_30(IStrategy): dataframe_4h['state'] = state_list state_list = self.chan.get_klu_state_list(dataframe_1d) dataframe_1d['state'] = state_list - #bi_list_1 = self.chan.get_bi_list(dataframe) - #bi_list_5 = self.chan.get_bi_list(dataframe_5) - #bi_list_15 = self.chan.get_bi_list(dataframe_15) - #bi_list_30 = self.chan.get_bi_list(dataframe_30) - #bi_list_60 = self.chan.get_bi_list(dataframe_60) + if self.last_time + timedelta(minutes=1) < datetime.now(): - #self.print_bi(bi_list_1) - #self.print_bi(bi_list_5) - #self.print_bi(bi_list_15) - #self.print_bi(bi_list_30) - #self.print_bi(bi_list_60) - self.print_seg(dataframe_5) print("-------------------------------------------------------------------------------") self.last_time = datetime.now() dataframe = resampled_merge(dataframe, dataframe_3) @@ -228,9 +220,9 @@ class ChanLun_BTC_30(IStrategy): new_entryprice = proposed_rate if trade: if trade.is_short: - new_entryprice = proposed_rate - 50 + new_entryprice = proposed_rate - 5 else: - new_entryprice = proposed_rate + 50 + new_entryprice = proposed_rate + 5 return new_entryprice def custom_exit_price(self, pair: str, trade: Trade, @@ -239,9 +231,9 @@ class ChanLun_BTC_30(IStrategy): new_exitprice = proposed_rate if trade: if trade.is_short: - new_exitprice = proposed_rate + 50 + new_exitprice = proposed_rate + 5 else: - new_exitprice = proposed_rate - 50 + new_exitprice = proposed_rate - 5 return new_exitprice def adjust_trade_position(self, trade: Trade, current_time: datetime, @@ -260,6 +252,10 @@ class ChanLun_BTC_30(IStrategy): 止损 = 开仓价 ± 1 * ATR(开仓时的ATR)。 多单: 开仓价 - ATR;空单: 开仓价 + ATR。 """ + # 保本止损:当浮盈达到或超过 1% 时,将止损提至开仓价 + #if current_profit is not None and current_profit >= 0.14: + #return stoploss_from_absolute(trade.open_rate, current_rate, is_short=trade.is_short) + entry_atr = trade.get_custom_data(key="entry_atr") if entry_atr is None: # 回退:取当前数据的 ATR 估算 @@ -292,10 +288,10 @@ class ChanLun_BTC_30(IStrategy): if dataframe is None or len(dataframe) == 0: return False last = dataframe.iloc[-1] - atr_str = 'resample_{}_atr'.format(self.get_ticker_indicator()*self.time30) + atr_str = 'resample_{}_atr'.format(self.get_ticker_indicator()*self.time60) atr_val = float(last.get(atr_str, 0) or 0) if atr_val < 0.001: - logger.info(f"ATR过滤:atr={atr_val:.2f} < 100, 拒绝进场 {pair}") + #logger.info(f"ATR过滤:atr={atr_val:.2f} < 100, 拒绝进场 {pair}") return False return True except Exception as e: @@ -315,15 +311,15 @@ class ChanLun_BTC_30(IStrategy): # Obtain pair dataframe (just to show how to access it) dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe) last_candle = dataframe.iloc[-1].squeeze() - atr_str = 'resample_{}_atr'.format(self.get_ticker_indicator()*self.time30) + atr_str = 'resample_{}_atr'.format(self.get_ticker_indicator()*self.time60) # 保存开仓时的ATR值用于止损计算 if (trade.nr_of_successful_entries == 1) and (order.ft_order_side == trade.entry_side): entry_atr = last_candle[atr_str] * 4 trade.set_custom_data(key="entry_atr", value=entry_atr) - logger.info(f"保存开仓时ATR值: {entry_atr}") + #logger.info(f"保存开仓时ATR值: {entry_atr}") return None def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: - shift_time = self.time30 + shift_time = self.time60 state_str = 'resample_{}_state'.format(self.get_ticker_indicator()*shift_time) dataframe.loc[ @@ -338,19 +334,19 @@ class ChanLun_BTC_30(IStrategy): ['enter_short', 'enter_tag']] = (1, 'short_30') return dataframe def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: - shift_time = self.time30 + shift_time = self.time60 state_str = 'resample_{}_state'.format(self.get_ticker_indicator()*shift_time) dataframe.loc[ ( (dataframe[state_str].shift(shift_time) == "20") ), - ['exit_long', 'exit_tag']] = (1, 'long_close_15') + ['exit_long', 'exit_tag']] = (1, 'long_close_30') dataframe.loc[ ( (dataframe[state_str].shift(shift_time) == "-20") ), - ['exit_short', 'exit_tag']] = (1, 'short_close_15') + ['exit_short', 'exit_tag']] = (1, 'short_close_30') 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, diff --git a/web/nginx/logs/error.log b/web/nginx/logs/error.log index f12fa62..5512244 100644 --- a/web/nginx/logs/error.log +++ b/web/nginx/logs/error.log @@ -148,3 +148,78 @@ 2025/08/29 12:39:35 [notice] 1#1: worker process 30 exited with code 0 2025/08/29 12:39:35 [notice] 1#1: worker process 31 exited with code 0 2025/08/29 12:39:35 [notice] 1#1: exit +2025/09/09 17:01:49 [notice] 1#1: using the "epoll" event method +2025/09/09 17:01:49 [notice] 1#1: nginx/1.27.5 +2025/09/09 17:01:49 [notice] 1#1: built by gcc 12.2.0 (Debian 12.2.0-14) +2025/09/09 17:01:49 [notice] 1#1: OS: Linux 6.10.14-linuxkit +2025/09/09 17:01:49 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576 +2025/09/09 17:01:49 [notice] 1#1: start worker processes +2025/09/09 17:01:49 [notice] 1#1: start worker process 20 +2025/09/09 17:01:49 [notice] 1#1: start worker process 21 +2025/09/09 17:01:49 [notice] 1#1: start worker process 22 +2025/09/09 17:01:49 [notice] 1#1: start worker process 23 +2025/09/09 17:01:49 [notice] 1#1: start worker process 24 +2025/09/09 17:01:49 [notice] 1#1: start worker process 25 +2025/09/09 17:01:49 [notice] 1#1: start worker process 26 +2025/09/09 17:01:49 [notice] 1#1: start worker process 27 +2025/09/09 17:01:49 [notice] 1#1: start worker process 28 +2025/09/09 17:01:49 [notice] 1#1: start worker process 29 +2025/09/09 17:01:49 [notice] 1#1: start worker process 30 +2025/09/09 17:01:49 [notice] 1#1: start worker process 31 +2025/09/09 17:02:20 [notice] 1#1: signal 3 (SIGQUIT) received, shutting down +2025/09/09 17:02:20 [notice] 21#21: gracefully shutting down +2025/09/09 17:02:20 [notice] 21#21: exiting +2025/09/09 17:02:20 [notice] 20#20: gracefully shutting down +2025/09/09 17:02:20 [notice] 20#20: exiting +2025/09/09 17:02:20 [notice] 23#23: gracefully shutting down +2025/09/09 17:02:20 [notice] 23#23: exiting +2025/09/09 17:02:20 [notice] 22#22: gracefully shutting down +2025/09/09 17:02:20 [notice] 22#22: exiting +2025/09/09 17:02:20 [notice] 24#24: gracefully shutting down +2025/09/09 17:02:20 [notice] 24#24: exiting +2025/09/09 17:02:20 [notice] 29#29: gracefully shutting down +2025/09/09 17:02:20 [notice] 29#29: exiting +2025/09/09 17:02:20 [notice] 30#30: gracefully shutting down +2025/09/09 17:02:20 [notice] 30#30: exiting +2025/09/09 17:02:20 [notice] 31#31: gracefully shutting down +2025/09/09 17:02:20 [notice] 31#31: exiting +2025/09/09 17:02:20 [notice] 28#28: gracefully shutting down +2025/09/09 17:02:20 [notice] 28#28: exiting +2025/09/09 17:02:20 [notice] 25#25: gracefully shutting down +2025/09/09 17:02:20 [notice] 25#25: exiting +2025/09/09 17:02:20 [notice] 27#27: gracefully shutting down +2025/09/09 17:02:20 [notice] 27#27: exiting +2025/09/09 17:02:20 [notice] 26#26: gracefully shutting down +2025/09/09 17:02:20 [notice] 26#26: exiting +2025/09/09 17:02:20 [notice] 21#21: exit +2025/09/09 17:02:20 [notice] 20#20: exit +2025/09/09 17:02:20 [notice] 23#23: exit +2025/09/09 17:02:20 [notice] 22#22: exit +2025/09/09 17:02:20 [notice] 24#24: exit +2025/09/09 17:02:20 [notice] 29#29: exit +2025/09/09 17:02:20 [notice] 30#30: exit +2025/09/09 17:02:20 [notice] 31#31: exit +2025/09/09 17:02:20 [notice] 28#28: exit +2025/09/09 17:02:20 [notice] 25#25: exit +2025/09/09 17:02:20 [notice] 27#27: exit +2025/09/09 17:02:20 [notice] 26#26: exit +2025/09/09 17:02:20 [notice] 1#1: signal 17 (SIGCHLD) received from 20 +2025/09/09 17:02:20 [notice] 1#1: worker process 20 exited with code 0 +2025/09/09 17:02:20 [notice] 1#1: worker process 21 exited with code 0 +2025/09/09 17:02:20 [notice] 1#1: worker process 22 exited with code 0 +2025/09/09 17:02:20 [notice] 1#1: worker process 27 exited with code 0 +2025/09/09 17:02:20 [notice] 1#1: worker process 28 exited with code 0 +2025/09/09 17:02:20 [notice] 1#1: worker process 31 exited with code 0 +2025/09/09 17:02:20 [notice] 1#1: signal 29 (SIGIO) received +2025/09/09 17:02:20 [notice] 1#1: signal 17 (SIGCHLD) received from 31 +2025/09/09 17:02:20 [notice] 1#1: signal 17 (SIGCHLD) received from 29 +2025/09/09 17:02:20 [notice] 1#1: worker process 24 exited with code 0 +2025/09/09 17:02:20 [notice] 1#1: worker process 25 exited with code 0 +2025/09/09 17:02:20 [notice] 1#1: worker process 26 exited with code 0 +2025/09/09 17:02:20 [notice] 1#1: worker process 29 exited with code 0 +2025/09/09 17:02:20 [notice] 1#1: signal 29 (SIGIO) received +2025/09/09 17:02:20 [notice] 1#1: signal 17 (SIGCHLD) received from 25 +2025/09/09 17:02:20 [notice] 1#1: signal 17 (SIGCHLD) received from 30 +2025/09/09 17:02:20 [notice] 1#1: worker process 23 exited with code 0 +2025/09/09 17:02:20 [notice] 1#1: worker process 30 exited with code 0 +2025/09/09 17:02:20 [notice] 1#1: exit