diff --git a/ChanKLC.py b/ChanKLC.py index c9346ac..8c20229 100644 --- a/ChanKLC.py +++ b/ChanKLC.py @@ -40,6 +40,12 @@ class ChanKLC(): self.candle_dir = klu.candle_dir self.range = klu.range self.strength = klu.strength + self.last_top_klc = None + self.last_bottom_klc = None + def set_last_top_klu(self, last_top_klc): + self.last_top_klc = last_top_klc + def set_last_bottom_klc(self, last_bottom_klc): + self.last_bottom_klc = last_bottom_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 diff --git a/ChanLun.py b/ChanLun.py index f2c8b4a..786765e 100644 --- a/ChanLun.py +++ b/ChanLun.py @@ -92,20 +92,11 @@ class ChanLun(): klc_list = self.get_klc_list(dataframe) bi_list= self.cal_bi_list(klc_list) def get_klc_state_list(self, dataframe): - klu_list = self.get_klu_list(dataframe) klc_list = self.get_klc_list(dataframe) bi_list= self.cal_bi_list(klc_list) state_list = [] if len(klc_list) > 0: klc_index = 0 - # 添加趋势强度判断 - dataframe['trend_strength'] = abs(dataframe['close'].pct_change(20)) - - # 添加波动率判断 - dataframe['volatility'] = dataframe['close'].pct_change().rolling(window=20).std() - - # 添加成交量趋势 - dataframe['volume_trend'] = dataframe['volume'].rolling(window=20).mean() for index in range(0, len(dataframe)): if klc_index == len(klc_list): klc_index = len(klc_list) - 1 @@ -113,27 +104,18 @@ class ChanLun(): if klc.end_klu: if klc.end_klu.idx == index: klc_index += 1 - features = klc.get_feature_data() - if (klc.klc_fx_type == Chan_KLC_FX.TOP1 or klc.klc_fx_type == Chan_KLC_FX.TOP2) and klc.bi.dir == Chan_BI_DIR.UP: + if klc.klc_fx_type == Chan_KLC_FX.TOP3: state_list.append("10") - #print(klc.start_time, klc.klc_fx_type) - elif (klc.klc_fx_type == Chan_KLC_FX.BOTTOM1 or klc.klc_fx_type == Chan_KLC_FX.BOTTOM2) and klc.bi.dir == Chan_BI_DIR.DOWN: + print(klc.start_time, klc.end_time, klc.klc_fx_type) + elif klc.klc_fx_type == Chan_KLC_FX.BOTTOM3: state_list.append("-10") - #print(klc.end_time, klc.klc_fx_type, klc.bi.start_time, klc.bi.dir) + print(klc.start_time, klc.end_time, klc.klc_fx_type) else: state_list.append("00") else: state_list.append("00") else: state_list.append("00") - klu = klu_list[index] - if klu.volume_ratio > 4.0: - if klu.close < klu.open: - state_list[-1] = "99" - print(klu.time, klu.volume_ratio, "99") - else: - state_list[-1] = "-99" - print(klu.time, klu.volume_ratio, "-99") else: for index in range(0, len(dataframe)): state_list.append("00") @@ -405,7 +387,6 @@ class ChanLun(): up_sbi_list = [] last_up_sbi = ChanSBI(last_up_bi, len(up_sbi_list), last_up_bi.dir) up_sbi_list.append(last_up_sbi) - last_seg.add_bi(bi) #up_sbi_list.append(last_up_sbi) #print(last_up_bi.start_time, last_up_sbi.start_bi.start_time, "Reset up sbi list 1") #print(bi.start_time, look_for_top, "UP 2") @@ -434,7 +415,6 @@ class ChanLun(): up_sbi_list = [] last_up_sbi = ChanSBI(last_up_bi, len(up_sbi_list), last_up_bi.dir) up_sbi_list.append(last_up_sbi) - last_seg.add_bi(bi) #up_sbi_list.append(last_up_sbi) #print(last_up_bi.start_time, last_up_sbi.start_bi.start_time, "Reset up sbi list 3") last_down_sbi = down_sbi @@ -449,8 +429,9 @@ class ChanLun(): down_sbi.set_pre(last_down_sbi) down_sbi_list.append(down_sbi) last_down_sbi = down_sbi - last_seg.add_bi(bi) #print(bi.start_time, look_for_top, "UP 4") + last_seg.add_bi(bi) + else: last_down_sbi = ChanSBI(bi, len(down_sbi_list), bi.dir) down_sbi_list.append(last_down_sbi) @@ -466,8 +447,9 @@ class ChanLun(): up_sbi.set_pre(last_up_sbi) up_sbi_list.append(up_sbi) last_up_sbi = up_sbi - last_seg.add_bi(bi) #print(bi.start_time, look_for_top, "UP 6") + last_seg.add_bi(bi) + # Last seg is down else: if bi.dir == Chan_BI_DIR.UP: @@ -499,7 +481,6 @@ class ChanLun(): down_sbi_list = [] last_down_sbi = ChanSBI(last_down_bi, len(down_sbi_list), last_down_bi.dir) down_sbi_list.append(last_down_sbi) - last_seg.add_bi(bi) #down_sbi_list.append(last_down_sbi) #print(last_down_bi.start_time, last_down_sbi.start_bi.start_time, "Reset down sbi list 1") #print(bi.start_time, look_for_top, "DOWN 2") @@ -528,7 +509,6 @@ class ChanLun(): down_sbi_list = [] last_down_sbi = ChanSBI(last_down_bi, len(down_sbi_list), last_down_bi.dir) down_sbi_list.append(last_down_sbi) - last_seg.add_bi(bi) #down_sbi_list.append(last_down_sbi) #print(last_down_bi.start_time, last_down_sbi.start_bi.start_time, "Reset down sbi list 3") last_up_sbi = up_sbi @@ -586,6 +566,7 @@ class ChanLun(): else: last_down_bi = bi down_bi_list.append(bi) + """ if len(seg_list) > 1: seg = seg_list[-1] last_seg = seg_list[-2] @@ -635,6 +616,7 @@ class ChanLun(): last_seg = seg last_seg_bi = bi_list[i] break + """ return seg_list def get_bi_zs_list(self, bi_list): @@ -837,6 +819,7 @@ class ChanLun(): bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) klc.set_klc_fx_type(Chan_KLC_FX.TOP3) + klc.set_last_top_klu(last_top) #print(klc.start_time, klc.fx, "二类卖点Sell 1") else: # A new top found @@ -957,6 +940,8 @@ class ChanLun(): bi_list[-1].add_klc(klc) klc.set_bi(bi_list[-1]) klc.set_klc_fx_type(Chan_KLC_FX.BOTTOM3) + klc.set_last_bottom_klc(last_bottom) + #print(last_bottom.start_time, last_bottom.end_time, "--------------------------------1") #print(klc.start_time, klc.fx, "二类买点Buy 1") else: # A new bottom found diff --git a/strategies/ChanLun_BTC_30.py b/strategies/ChanLun_BTC_30.py index bb2fbfb..89b01e1 100644 --- a/strategies/ChanLun_BTC_30.py +++ b/strategies/ChanLun_BTC_30.py @@ -1,6 +1,6 @@ # --- Do not remove these libs --- from statistics import median -from freqtrade.strategy import IStrategy +from freqtrade.strategy import IStrategy, stoploss_from_absolute import sys import os # 添加父目录到系统路径 @@ -61,9 +61,11 @@ class ChanLun_BTC_30(IStrategy): "2400": 0.025, "3600": 0 } - can_short = False + can_short = True lev = 2.0 stoploss = -0.5 + use_custom_stoploss = True + trailing_stop = False trailing_stop_positive = 0.025 trailing_stop_positive_offset = 0.045 @@ -77,10 +79,12 @@ class ChanLun_BTC_30(IStrategy): time30 = 30 time60 = 60 time4h = 240 + time30 = 60 last_time = datetime.now() chan = ChanLun() chanpy = ChanPY() classifier = ChanLunClassifier(None) + last_order = None last_trade = None def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: @@ -107,9 +111,9 @@ class ChanLun_BTC_30(IStrategy): #self.chan.plot_dual(dataframe_5, dataframe_30) chanpy_state = self.chanpy.get_bsp_state(dataframe_5) dataframe_5['chanpy_state'] = chanpy_state - state_list, fx_list = self.chan.get_klc_strength_list(dataframe_30) - dataframe_30['state'] = state_list - dataframe_30['fx'] = fx_list + state_list = self.chan.get_klc_state_list(dataframe_60) + dataframe_60['state'] = state_list + dataframe_60['fx'] = state_list #bi_list_1 = self.chan.get_bi_list(dataframe) #bi_list_5 = self.chan.get_bi_list(dataframe_5) @@ -126,7 +130,7 @@ class ChanLun_BTC_30(IStrategy): print("-------------------------------------------------------------------------------") self.last_time = datetime.now() dataframe = resampled_merge(dataframe, dataframe_5) - dataframe = resampled_merge(dataframe, dataframe_30) + dataframe = resampled_merge(dataframe, dataframe_60) #dataframe = resampled_merge(dataframe, dataframe_30) #dataframe = resampled_merge(dataframe, dataframe_60) #dataframe = resampled_merge(dataframe, dataframe_4h) @@ -188,7 +192,7 @@ class ChanLun_BTC_30(IStrategy): new_exitprice = proposed_rate - 50 return new_exitprice - def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, + def confirm_trade_entry1(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, current_time: datetime, entry_tag: str | None, side: str, **kwargs) -> bool: if self.last_trade: @@ -207,7 +211,20 @@ class ChanLun_BTC_30(IStrategy): #if self.last_trade: #print(self.last_trade.open_date, current_time, self.last_trade.open_date + timedelta(minutes=self.time5)) return True - + def custom_stoploss1(self, pair: str, trade: Trade, current_time: datetime, + current_rate: float, current_profit: float, after_fill: bool, + **kwargs) -> float | None: + + last_high = trade.get_custom_data(key="entry_candle_high") + last_low = trade.get_custom_data(key="entry_candle_low") + + # Convert absolute price to percentage relative to current_rate + if last_high: + return stoploss_from_absolute(last_high, current_rate, is_short=trade.is_short) + if last_low: + return stoploss_from_absolute(last_low, current_rate, is_short=trade.is_short) + # return maximum stoploss value, keeping current stoploss price unchanged + return None def custom_exit1(self, pair: str, trade: Trade, current_time: datetime, current_rate: float, current_profit: float, **kwargs): #dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) @@ -229,15 +246,15 @@ class ChanLun_BTC_30(IStrategy): """ if trade.is_short: last_high = trade.get_custom_data(key="entry_candle_high") - if current_rate > last_high: + if last_high and current_rate > last_high: #print(trade.open_date, last_high, current_rate, "Relay Top FX exit") return "Relay Top FX exit" else: last_low = trade.get_custom_data(key="entry_candle_low") - if current_rate < last_low: + if last_low and current_rate < last_low: #print(trade.open_date, last_low, current_rate, "Relay Bottom FX exit") return "Relay Bottom FX exit" - def order_filled1(self, pair: str, trade: Trade, order: Order, current_time: datetime, **kwargs) -> None: + def order_filled(self, pair: str, trade: Trade, order: Order, current_time: datetime, **kwargs) -> None: """ Called right after an order fills. Will be called for all order types (entry, exit, stoploss, position adjustment). @@ -250,17 +267,26 @@ 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() - klc_list = self.chan.get_klc_list(resample_to_interval(dataframe, self.get_ticker_indicator() * 30)) + klc_list = self.chan.get_klc_list(resample_to_interval(dataframe, self.get_ticker_indicator() * self.time30)) bi_list = self.chan.cal_bi_list(klc_list) - last_high = klc_list[-2].high - last_low = klc_list[-2].low - if trade.is_short: - if (trade.nr_of_successful_entries == 1) and (order.ft_order_side == trade.entry_side): - trade.set_custom_data(key="entry_candle_high", value=last_high) + if self.last_order is None: + if trade.is_short and klc_list[-2].last_top_klc: + if (trade.nr_of_successful_entries == 1) and (order.ft_order_side == trade.entry_side): + last_high = klc_list[-2].last_top_klc.high + print(klc_list[-2].start_time, "--------------------------------", order.order_date, order.side, last_high) + trade.set_custom_data(key="entry_candle_high", value=last_high) + else: + if (trade.nr_of_successful_entries == 1) and (order.ft_order_side == trade.entry_side) and klc_list[-2].last_bottom_klc: + last_low = klc_list[-2].last_bottom_klc.low + trade.set_custom_data(key="entry_candle_low", value=last_low) + print(klc_list[-2].start_time, "--------------------------------", order.order_date, order.side, last_low) + #print(trade.open_date, trade.close_date, last_high, last_low, order.ft_order_side, klc_list[-2].start_time, klc_list[-2].end_time) + self.last_order = order else: - if (trade.nr_of_successful_entries == 1) and (order.ft_order_side == trade.entry_side): - trade.set_custom_data(key="entry_candle_low", value=last_low) - #print(trade.open_date, trade.close_date, last_high, last_low, order.ft_order_side, klc_list[-2].start_time, klc_list[-2].end_time) + if self.last_order.side != order.side: + self.last_order = None + trade.set_custom_data(key="entry_candle_high", value=None) + trade.set_custom_data(key="entry_candle_low", value=None) self.last_trade = trade return None def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: @@ -272,8 +298,8 @@ class ChanLun_BTC_30(IStrategy): dataframe.loc[ ( #(dataframe['state'] == "-30") - (dataframe[state_str].shift(shift_time) > strength) & - (dataframe[fx_str].shift(shift_time) == -1) + (dataframe[state_str].shift(shift_time) == "-10") + #(dataframe[fx_str].shift(shift_time) == -1) #(dataframe[chanpy_state_str].shift(shift_time+30) == 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") & @@ -284,8 +310,8 @@ class ChanLun_BTC_30(IStrategy): dataframe.loc[ ( #(dataframe['state'] == "-30") - (dataframe[state_str].shift(shift_time) > strength) & - (dataframe[fx_str].shift(shift_time) == 1) + (dataframe[state_str].shift(shift_time) == "10") + #(dataframe[fx_str].shift(shift_time) == 1) #(dataframe[chanpy_state_str].shift(shift_time+30) == -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") & @@ -303,8 +329,8 @@ class ChanLun_BTC_30(IStrategy): dataframe.loc[ ( #(dataframe['state']== "30") - (dataframe[state_str].shift(shift_time) > strength) & - (dataframe[fx_str].shift(shift_time) == 1) + (dataframe[state_str].shift(shift_time) == "101") + #(dataframe[fx_str].shift(shift_time) == 1) #(dataframe[chanpy_state_str].shift(shift_time+30) == -1) #(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time30)] == "10") & #(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time60)] == "10") @@ -313,8 +339,8 @@ class ChanLun_BTC_30(IStrategy): dataframe.loc[ ( #(dataframe['state']== "30") - (dataframe[state_str].shift(shift_time) > strength) & - (dataframe[fx_str].shift(shift_time) == -1) + (dataframe[state_str].shift(shift_time) == "-101") + #(dataframe[fx_str].shift(shift_time) == -1) #(dataframe[chanpy_state_str].shift(shift_time+30) == 1) #(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time30)] == "10") & #(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time60)] == "10")