diff --git a/ChanKLC.py b/ChanKLC.py index d6fbcd0..98e3074 100644 --- a/ChanKLC.py +++ b/ChanKLC.py @@ -31,10 +31,6 @@ class ChanKLC(): self.rsi = klu.rsi self.volume_ratio = klu.volume_ratio self.macdhist = 0 - - # === 新增:KLC类型 === - self.klc_type = None # KLC类型:大阳线、大阴线、小阳线、小阴线 - def set_klc_fx_type(self, klc_fx_type): #print(self.start_time, klc_fx_type, self.get_feature_data()['klu_macd'], self.get_feature_data()['klu_macdhist'], self.get_feature_data()['klu_rsi']) self.klc_fx_type = klc_fx_type @@ -53,13 +49,7 @@ class ChanKLC(): self.volume_ratio = self.volume_ratio / len(self.klus) self.volume = self.volume / len(self.klus) self.macdhist = self.macdhist / len(self.klus) - def contain_klu_fx(self): - if len(self.klus) > 0: - for klu in self.klus: - klu.update_realtime_analysis() - if klu.fx_type == self.fx and klu.fx_strength > 1.8: - return True - return False + def set_next(self, klc): self.next = klc def set_pre(self, klc): @@ -1157,78 +1147,6 @@ class ChanKLC(): return features - def check_fx_klu_strength(self): - if self.pre and self.next and self.next.end_klu: - strength = 0 - klc1 = self.pre - klc2 = self - klc3 = self.next - # 检查包含关系 - inc = self.check_include_relation(klc1, klc2, klc3) - if not inc: - # 检查分型类型 - if self.fx == Chan_FX_TYPE.TOP: - # (1)没有包含关系+1 - strength += 0 - print(self.start_time, "1") - # (2)第1条K线是一条大阳线,而第2、3条K线是小阴线、小阳线,那么这个分型结构的意义就不大了,强度 -1 - if self.pre.cal_klu_min_max() > 0.5 and self.cal_klu_min_max() < 0.2 and self.next.cal_klu_min_max() < 0.2: - strength += -1 - print(self.start_time, "2") - # (3)第2条K线有长上影线或者就是大阴线,而第3条K线不能以阳线收在第2条K线区间的一半之上,那么该顶分型的力度就比较大 - if ((self.close < self.open and self.cal_klu_min_max() > 0.5) or self.cal_klu_upper_shadow() > 0.6) and not (self.next.close > self.next.open and self.next.close > (self.high-self.low)/2): - strength += 1 - print(self.start_time, "3") - else: - if self.fx == Chan_FX_TYPE.TOP: - # (4)第2条K线和第3条K线为包含关系,而第3条K线为大阴线(直接把为阳线的第2条K线“吃掉”),这是最坏的一种包含关系。 - if self.start_klu.index - self.end_klu.index < 0: - klu1 = self.start_klu - klu2 = self.klus[1] - print(self.start_time, self.end_time, "length: ", len(self.klus), klu1.index, klu2.index) - if klu2.close < klu2.open and 100*abs(klu2.open - klu2.close) / klu2.open > 0.5 and klu1.high <= klu2.high and klu1.low >= klu2.low: - strength += 1 - print(self.start_time, "4") - # (5)第3条K线如果跌破第1条K线的底而且不能高于第1条K线区间的一半之上,则属于较弱的一种,也就是说这种顶分型出现后股价见顶的可能性不大。 - if self.next.low < self.pre.low and self.next.high < (self.pre.high + self.pre.low)/2: - strength += -1 - print(self.start_time, "5") - return strength - else: - return 0 - def cal_klu_upper_shadow(self): - """ - 计算KLC的上影线长度 - 上影线 = 最高价 - max(开盘价, 收盘价) - """ - if self.high <= 0: # 避免无效数据 - return 0 - - # 计算上影线长度 - upper_shadow = self.high - max(self.open, self.close) - - # 计算相对上影线长度(相对于整个K线区间) - total_range = self.high - self.low - if total_range > 0: - upper_shadow_ratio = upper_shadow / total_range - else: - upper_shadow_ratio = 0 - - return upper_shadow_ratio - def cal_klu_min_max(self): - """ - 计算KLC类型:大阳线、大阴线、小阳线、小阴线 - """ - if self.open <= 0: # 避免除零错误 - return 0 - line_type = 1000*abs(self.open - self.close) / self.open - print(self.start_time, line_type) - return line_type - def check_include_relation(self, klc1, klc2, klc3): - if klc1.start_klu.index - klc1.end_klu.index == 0 and klc2.start_klu.index - klc2.end_klu.index == 0 and klc3.start_klu.index - klc3.end_klu.index == 0: - return False - else: - return True def cal_fx_strength(self): """ 用self.pre和self.next实现分型强弱判断 @@ -1246,12 +1164,11 @@ class ChanKLC(): -2: 弱分型(明显中继) -3: 极弱分型(无效分型) """ - #return self.check_fx_klu_strength() # 检查是否为分型,且有前后K线数据 - if self.fx == Chan_FX_TYPE.UNKNOWN or self.klc_fx_type == Chan_FX_TYPE.UNKNOWN: + if self.fx == Chan_FX_TYPE.UNKNOWN: return 0 if not self.pre or not self.next: - return -100 + return 100 # === 核心判断:分型在笔中的位置 === # 1. 检查这个分型是否能够终结当前笔 @@ -1283,7 +1200,6 @@ class ChanKLC(): # 分型质量调整 base_score += fx_quality - #base_score += self.check_fx_klu_strength() #print(self.start_time, base_score, is_bi_end, post_fx_confirmation, fx_quality) # 限制在-3到3范围内 return max(-3, min(3, base_score)) @@ -1334,7 +1250,7 @@ class ChanKLC(): first_low = self.pre.low middle_low = self.low key_support = min(first_low, middle_low) - last_klc = None + for i, klc in enumerate(subsequent_klcs): # 检查是否跌破关键支撑 if klc.low < key_support: @@ -1347,8 +1263,7 @@ class ChanKLC(): # 检查下跌趋势 if i > 0 and klc.close < subsequent_klcs[i-1].close: downward_trend += 1 - last_klc = klc - print(last_klc.start_time, last_klc.end_time) + # 强烈笔终结:跌破关键位且无新高 if broken_key_levels >= 1 and new_highs == 0 and downward_trend >= 2: return 2 @@ -1381,7 +1296,7 @@ class ChanKLC(): first_high = self.pre.high middle_high = self.high key_resistance = max(first_high, middle_high) - last_klc = None + for i, klc in enumerate(subsequent_klcs): # 检查是否突破关键阻力 if klc.high > key_resistance: @@ -1394,8 +1309,7 @@ class ChanKLC(): # 检查上涨趋势 if i > 0 and klc.close > subsequent_klcs[i-1].close: upward_trend += 1 - last_klc = klc - print(last_klc.start_time, last_klc.end_time) + # 强烈笔终结:突破关键位且无新低 if broken_key_levels >= 1 and new_lows == 0 and upward_trend >= 2: return 2 diff --git a/ChanLun.py b/ChanLun.py index 51930bc..838f985 100644 --- a/ChanLun.py +++ b/ChanLun.py @@ -67,12 +67,12 @@ class ChanLun(): else: print(bi.start_klc.end_time, bi.dir, bi.is_sure) def check_fx(self, klc): - if klc.pre and klc.next and klc.next.end_klu: + if klc.pre and klc.next: if klc.high > klc.pre.high and klc.high > klc.next.high: klc.set_fx(Chan_FX_TYPE.TOP) #print(klc.start_time, klc.end_time,klc.next.start_time, klc.next.end_time,klc.fx, "TOP") return Chan_FX_TYPE.TOP - if klc.pre and klc.next and klc.next.end_klu: + if klc.pre and klc.next: if klc.low < klc.pre.low and klc.low < klc.next.low: klc.set_fx(Chan_FX_TYPE.BOTTOM) #print(klc.start_time, klc.end_time,klc.next.start_time, klc.next.end_time,klc.fx, "BOTTOM") @@ -149,9 +149,9 @@ class ChanLun(): klc = klc_list[klc_index] if klc.end_klu and klc.end_klu.idx == index: klc_index += 1 - if (klc.klc_fx_type == Chan_KLC_FX.TOP1 or klc.klc_fx_type == Chan_KLC_FX.TOP2) and klc.contain_klu_fx(): + if klc.klc_fx_type == Chan_KLC_FX.TOP1 or klc.klc_fx_type == Chan_KLC_FX.TOP2: fx_list.append(1) - elif (klc.klc_fx_type == Chan_KLC_FX.BOTTOM1 or klc.klc_fx_type == Chan_KLC_FX.BOTTOM2) and klc.contain_klu_fx(): + elif klc.klc_fx_type == Chan_KLC_FX.BOTTOM1 or klc.klc_fx_type == Chan_KLC_FX.BOTTOM2: fx_list.append(-1) else: fx_list.append(0) @@ -235,7 +235,6 @@ class ChanLun(): def get_kl_data(self, dataframe:DataFrame): fields = "time,open,high,low,close,volume" klu_list = [] - last_klu = None for i in range(0, len(dataframe)): item = dataframe.iloc[i] date = item['date'] @@ -259,13 +258,8 @@ class ChanLun(): klu = ChanKLU(time_str, o, h, l, c, v) klu.set_idx(i) klu_list.append(klu) - if last_klu: - klu.set_pre(last_klu) - last_klu.set_next(klu) - last_klu.detect_realtime_fx() if 'macd' in item: klu.set_indicators(item) - last_klu = klu return klu_list def cal_volume_ratio(self, dataframe, window=10): df = dataframe.copy() @@ -697,11 +691,10 @@ class ChanLun(): else: # A new top found #last_top.set_fx(Chan_FX_TYPE.UNKNOWN) - #print(klc.start_time, last_top.start_time, klc.cal_fx_strength(), "一类卖点Sell 1") last_top = klc #print(klc.start_time, klc.fx, bi_list[-1].dir, "Last Top Change 1") klc.set_klc_fx_type(Chan_KLC_FX.TOP1) - + #print(klc.start_time, klc.fx, "一类卖点Sell 1") #klc.set_fx(fx) #klc.set_state("10") bi_list[-1].add_klc(klc) @@ -732,7 +725,6 @@ class ChanLun(): #klc.set_state("10") #print(klc.start_time, klc.fx, "笔卖点Sell 1") klc.set_klc_fx_type(Chan_KLC_FX.TOP2) - #print(klc.start_time, last_top.start_time, klc.fx, "二类卖点Sell 2") bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) else: @@ -757,7 +749,7 @@ class ChanLun(): #klc.set_state('30') bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) - #print(klc.start_time, last_top.start_time, "Normal TOP Found, Confirm down bi 4") + #print(klc.start_time, last_bottom.start_time, "Normal TOP Found, Confirm down bi 4") #print(klc.start_time, klc.fx, "笔卖点Sell 2") # last bottom = None else: @@ -1222,8 +1214,8 @@ class ChanLun(): klc.set_pre(last_klc) last_klc.set_end_klu(last_klu) klc.set_pre_fx() - #else: - #last_klc.add_klu(klu) + else: + last_klc.add_klu(klu) else: ddir = Chan_KLINE_DIR.UP if klu.open > klu.close: diff --git a/strategies/ChanLun_BTC_15.py b/strategies/ChanLun_BTC_15.py index 1e702c8..a57c7f8 100644 --- a/strategies/ChanLun_BTC_15.py +++ b/strategies/ChanLun_BTC_15.py @@ -14,23 +14,20 @@ import talib.abstract as ta from pandas import DataFrame from datetime import datetime, timedelta from freqtrade.persistence import Trade -from typing import Optional, List, Dict +from typing import Optional import logging logger = logging.getLogger(__name__) -from freqtrade.optimize.space import Categorical, Dimension, Integer, SKDecimal ### 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 -c ./user_data/Chan/config/ChanLun_BTC_15.json --strategy ChanLun_BTC_15 --export none --strategy-path ./user_data/Chan/strategies --timerange=20250525- # freqtrade backtesting -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 SOL/USDT:USDT --timerange=20250405- -# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLossDaily --spaces stoploss --strategy ChanLun_BTC_15 --strategy-path ./user_data/Chan/strategies -c ./user_data/Chan/config/ChanLun_BTC_15.json -e 200 --timerange=20250201-20250501 -# freqtrade live-backtest -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 chanlun_btc backtesting -c ./user_data/Chan/config/ChanLun_BTC_15.json --strategy ChanLun_BTC_15 --strategy-path ./user_data/Chan/strategies --timerange=20250525- -# sudo docker compose run --rm chanlun_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 chanlun_btc trade -c ./user_data/Chan/config/ChanLun_BTC_15.json --strategy ChanLun_BTC_15 --strategy-path ./user_data/Chan/strategies +# 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 @@ -38,10 +35,10 @@ class ChanLun_BTC_15(IStrategy): # This attribute will be overridden if the config file contains "minimal_roi" # 30m and 1h minimal_roi = { - "0": 0.15, - "240": 0.1, - "480": 0.02, - "960": 0 + "0": 0.60, + "360": 0.2, + "640": 0.1, + "1200": 0 } # 5m and 15m minimal_roi_1 = { @@ -64,13 +61,14 @@ class ChanLun_BTC_15(IStrategy): "3600": 0 } can_short = True - lev = 10 - stoploss = -0.8 + lev = 50.0 + stoploss = -0.3 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 = 600 time5 = 5 @@ -173,13 +171,11 @@ class ChanLun_BTC_15(IStrategy): 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) - #state_str = 'state' - #fx_str = 'fx' dataframe.loc[ ( #(dataframe['state'] == "-30") - (dataframe[state_str].shift(self.time5*2) > 0) & - (dataframe[fx_str].shift(self.time5*2) == -1) + (dataframe[state_str].shift(self.time5) > 1.0) & + (dataframe[fx_str].shift(self.time5) == -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") @@ -189,8 +185,8 @@ class ChanLun_BTC_15(IStrategy): dataframe.loc[ ( #(dataframe['state'] == "-30") - (dataframe[state_str].shift(self.time5*2) > 0) & - (dataframe[fx_str].shift(self.time5*2) == 1) + (dataframe[state_str].shift(self.time5) > 1.0) & + (dataframe[fx_str].shift(self.time5) == 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") @@ -201,13 +197,11 @@ class ChanLun_BTC_15(IStrategy): 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) - #state_str = 'state' - #fx_str = 'fx' dataframe.loc[ ( #(dataframe['state']== "30") - (dataframe[state_str].shift(self.time5*2) > 0) & - (dataframe[fx_str].shift(self.time5*2) == 1) + (dataframe[state_str].shift(self.time5) > 1.0) & + (dataframe[fx_str].shift(self.time5) == 1) #(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time30)] == "10") & #(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time60)] == "10") ), @@ -215,8 +209,8 @@ class ChanLun_BTC_15(IStrategy): dataframe.loc[ ( #(dataframe['state']== "30") - (dataframe[state_str].shift(self.time5*2) > 0) & - (dataframe[fx_str].shift(self.time5*2) == -1) + (dataframe[state_str].shift(self.time5) > 1.0) & + (dataframe[fx_str].shift(self.time5) == -1) #(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time30)] == "10") & #(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time60)] == "10") ), diff --git a/web/nginx/logs/error.log b/web/nginx/logs/error.log index f9c73d6..0bd7bd2 100644 --- a/web/nginx/logs/error.log +++ b/web/nginx/logs/error.log @@ -16,3 +16,60 @@ 2025/05/27 01:57:54 [notice] 1#1: start worker process 29 2025/05/27 01:57:54 [notice] 1#1: start worker process 30 2025/05/27 01:57:54 [notice] 1#1: start worker process 31 +2025/06/05 02:20:20 [notice] 1#1: signal 3 (SIGQUIT) received, shutting down +2025/06/05 02:20:20 [notice] 20#20: gracefully shutting down +2025/06/05 02:20:20 [notice] 20#20: exiting +2025/06/05 02:20:20 [notice] 21#21: gracefully shutting down +2025/06/05 02:20:20 [notice] 21#21: exiting +2025/06/05 02:20:20 [notice] 22#22: gracefully shutting down +2025/06/05 02:20:20 [notice] 23#23: gracefully shutting down +2025/06/05 02:20:20 [notice] 23#23: exiting +2025/06/05 02:20:20 [notice] 29#29: gracefully shutting down +2025/06/05 02:20:20 [notice] 29#29: exiting +2025/06/05 02:20:20 [notice] 30#30: gracefully shutting down +2025/06/05 02:20:20 [notice] 30#30: exiting +2025/06/05 02:20:20 [notice] 28#28: gracefully shutting down +2025/06/05 02:20:20 [notice] 28#28: exiting +2025/06/05 02:20:20 [notice] 31#31: gracefully shutting down +2025/06/05 02:20:20 [notice] 31#31: exiting +2025/06/05 02:20:20 [notice] 24#24: gracefully shutting down +2025/06/05 02:20:20 [notice] 24#24: exiting +2025/06/05 02:20:20 [notice] 25#25: gracefully shutting down +2025/06/05 02:20:20 [notice] 25#25: exiting +2025/06/05 02:20:20 [notice] 27#27: gracefully shutting down +2025/06/05 02:20:20 [notice] 27#27: exiting +2025/06/05 02:20:20 [notice] 26#26: gracefully shutting down +2025/06/05 02:20:20 [notice] 26#26: exiting +2025/06/05 02:20:20 [notice] 20#20: exit +2025/06/05 02:20:20 [notice] 21#21: exit +2025/06/05 02:20:20 [notice] 22#22: exiting +2025/06/05 02:20:20 [notice] 23#23: exit +2025/06/05 02:20:20 [notice] 29#29: exit +2025/06/05 02:20:20 [notice] 30#30: exit +2025/06/05 02:20:20 [notice] 28#28: exit +2025/06/05 02:20:20 [notice] 31#31: exit +2025/06/05 02:20:20 [notice] 24#24: exit +2025/06/05 02:20:20 [notice] 25#25: exit +2025/06/05 02:20:20 [notice] 27#27: exit +2025/06/05 02:20:20 [notice] 26#26: exit +2025/06/05 02:20:20 [notice] 22#22: exit +2025/06/05 02:20:20 [notice] 1#1: signal 17 (SIGCHLD) received from 25 +2025/06/05 02:20:20 [notice] 1#1: worker process 21 exited with code 0 +2025/06/05 02:20:20 [notice] 1#1: worker process 22 exited with code 0 +2025/06/05 02:20:20 [notice] 1#1: worker process 24 exited with code 0 +2025/06/05 02:20:20 [notice] 1#1: worker process 25 exited with code 0 +2025/06/05 02:20:20 [notice] 1#1: worker process 26 exited with code 0 +2025/06/05 02:20:20 [notice] 1#1: worker process 29 exited with code 0 +2025/06/05 02:20:20 [notice] 1#1: worker process 31 exited with code 0 +2025/06/05 02:20:20 [notice] 1#1: signal 29 (SIGIO) received +2025/06/05 02:20:20 [notice] 1#1: signal 17 (SIGCHLD) received from 24 +2025/06/05 02:20:20 [notice] 1#1: signal 17 (SIGCHLD) received from 27 +2025/06/05 02:20:20 [notice] 1#1: worker process 27 exited with code 0 +2025/06/05 02:20:20 [notice] 1#1: worker process 28 exited with code 0 +2025/06/05 02:20:20 [notice] 1#1: signal 29 (SIGIO) received +2025/06/05 02:20:20 [notice] 1#1: signal 17 (SIGCHLD) received from 28 +2025/06/05 02:20:20 [notice] 1#1: signal 17 (SIGCHLD) received from 20 +2025/06/05 02:20:20 [notice] 1#1: worker process 20 exited with code 0 +2025/06/05 02:20:20 [notice] 1#1: worker process 23 exited with code 0 +2025/06/05 02:20:20 [notice] 1#1: worker process 30 exited with code 0 +2025/06/05 02:20:20 [notice] 1#1: exit