diff --git a/ChanLun.py b/ChanLun.py index 044957d..daabe2f 100644 --- a/ChanLun.py +++ b/ChanLun.py @@ -32,6 +32,7 @@ class ChanLun(): time30 = 30 time60 = 60 time4h = 240 + last_peak = {'high': 0, 'low': float('inf')} def create_all_data(self, dataframe, ticker_indicator): all_data = dict() all_data['1m'] = dataframe @@ -156,8 +157,8 @@ class ChanLun(): else: fx_list.append(0) klc_strength_list.append(klc.cal_fx_strength(2)) - if klc.klc_fx_type != Chan_KLC_FX.UNKNOWN and klc.cal_fx_strength() > 1: - print(klc.start_time, klc.end_time, klc.cal_fx_strength(), klc.klc_fx_type, fx_list[-1], klc_strength_list[-1]) + #if klc.klc_fx_type != Chan_KLC_FX.UNKNOWN and klc.cal_fx_strength() > 1: + #print(klc.start_time, klc.end_time, klc.cal_fx_strength(), klc.klc_fx_type, fx_list[-1], klc_strength_list[-1]) else: klc_strength_list.append(0) fx_list.append(0) @@ -305,6 +306,10 @@ class ChanLun(): c, v ] + if h > self.last_peak['high']: + self.last_peak['high'] = h + if l < self.last_peak['low']: + self.last_peak['low'] = l #klu = KLU(self.create_item_dict(item_data, GetColumnNameFromFieldList(fields))) klu = ChanKLU(time_str, o, h, l, c, v) klu.set_idx(i) diff --git a/ChanPY.py b/ChanPY.py index 868fb2d..bc67e45 100644 --- a/ChanPY.py +++ b/ChanPY.py @@ -1,7 +1,7 @@ import sys import os -#sys.path.append(os.path.abspath("/Users/jack/Documents/GitHub/chan.py")) -sys.path.append(os.path.abspath("/Users/jack/Project/chan.py")) +sys.path.append(os.path.abspath("/Users/jack/Documents/GitHub/chan.py")) +#sys.path.append(os.path.abspath("/Users/jack/Project/chan.py")) from Chan import CChan from BuySellPoint.BS_Point import CBS_Point from ChanConfig import CChanConfig @@ -193,7 +193,7 @@ class ChanPY(): if bsp_list_pre_len > len(bsp_list): if abs(last_bsp_value) == 1 or abs(last_bsp_value) == 2: bsps.append(1) - print(klu.time, klu.idx, last_bsp.klu.time, last_bsp.klu.idx, last_bsp_value, 98) + #print(klu.time, klu.idx, last_bsp.klu.time, last_bsp.klu.idx, last_bsp_value, 98) else: bsps.append(99) else: @@ -201,13 +201,13 @@ class ChanPY(): if klu.idx == last_bsp.klu.idx: last_bsp_value = self.get_bsp_type(last_bsp.type[0], last_bsp.is_buy) bsps.append(last_bsp_value) - print(klu.time, klu.idx, last_bsp.klu.time, last_bsp.klu.idx, last_bsp_value) + #print(klu.time, klu.idx, last_bsp.klu.time, last_bsp.klu.idx, last_bsp_value) else: bsps.append(0) else: last_bsp_value = self.get_bsp_type(last_bsp.type[0], last_bsp.is_buy) bsps.append(last_bsp_value) - print(klu.time, klu.idx, last_bsp.klu.time, last_bsp.klu.idx, last_bsp_value) + #print(klu.time, klu.idx, last_bsp.klu.time, last_bsp.klu.idx, last_bsp_value) else: bsps.append(0) bsp_list_pre_len = len(bsp_list) @@ -288,7 +288,68 @@ class ChanPY(): #print(zs.begin.time, zs.end.time) return bsps, updown, bi_sure def get_bsp_state(self, dataframe:DataFrame): - bsps, updown, bi_sure = self.get_bsps(dataframe) - #print(bsps) + fields = "time,open,high,low,close,volume" + bsps = [] + if self.chanIn: + kl_data = self.get_kl_data(dataframe) + bsp_list = [] + bsp_list_pre_len = 0 + last_bsp_value = 0 + for klu in kl_data: # 获取单根K线 + self.chan.trigger_load({self.k_type: [klu]}) # 喂给CChan新增k线 + self.last_kline = klu + bsp_list = self.chan.get_bsp() + kl_datas = self.chan.kl_datas[self.k_type] + bi_list = kl_datas.bi_list + lst = kl_datas.lst + if len(bsp_list) > 0: + last_bsp = bsp_list[-1] + #print(klu.time, klu.idx, last_bsp.klu.time, last_bsp.klu.idx, last_bsp_value, lst[-2].fx, bi_list[-1].dir, bi_list[-1].is_sure,klu.close) + if bsp_list_pre_len > len(bsp_list): + if abs(last_bsp_value) == 1 or abs(last_bsp_value) == 2: + bsps.append(1) + #print(klu.time, klu.idx, last_bsp.klu.time, last_bsp.klu.idx, last_bsp_value, 98) + else: + bsps.append(99) + else: + if bsp_list_pre_len == len(bsp_list): + if klu.idx == last_bsp.klu.idx: + last_bsp_value = self.get_bsp_type(last_bsp.type[0], last_bsp.is_buy) + bsps.append(last_bsp_value) + #if abs(last_bsp_value) == 1 or abs(last_bsp_value) == 2: + #print(klu.time, klu.idx, last_bsp.klu.time, last_bsp.klu.idx, last_bsp_value, "Knonw") + else: + bsps.append(0) + else: + last_bsp_value = self.get_bsp_type(last_bsp.type[0], last_bsp.is_buy) + bsps.append(last_bsp_value) + #if abs(last_bsp_value) == 1 or abs(last_bsp_value) == 2: + #print(klu.time, klu.idx, last_bsp.klu.time, last_bsp.klu.idx, last_bsp_value, "Unknow") + else: + bsps.append(0) + bsp_list_pre_len = len(bsp_list) + self.chanIn = False + else: + klu = CKLine_Unit(self.create_item_dict(self.get_last_item_data(dataframe), GetColumnNameFromFieldList(fields)), autofix=True) + if self.last_kline.time < klu.time: + self.chan.trigger_load({self.k_type: [klu]}) # 喂给CChan新增k线 + self.last_kline = klu + bsp_list = self.chan.get_bsp() + last_bsp = bsp_list[-1] + if last_bsp.klu.idx == klu.idx: + bsps.append(self.get_bsp_type(last_bsp.type[0], last_bsp.is_buy)) + else: + bsps.append(0) + for index in range(0, len(bsps)): + if not (abs(bsps[index]) == 1 or abs(bsps[index]) == 2): + bsps[index] = 0 + else: + if bsps[index] == 2: + bsps[index] = 1 + else: + if bsps[index] == -2: + bsps[index] = -1 + else: + bsps[index] = 0 return bsps diff --git a/strategies/ChanLun_BTC_30.py b/strategies/ChanLun_BTC_30.py index e4f2b3a..2cf2818 100644 --- a/strategies/ChanLun_BTC_30.py +++ b/strategies/ChanLun_BTC_30.py @@ -14,7 +14,7 @@ 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 freqtrade.persistence import Trade, Order from typing import Optional import logging logger = logging.getLogger(__name__) @@ -42,14 +42,14 @@ class ChanLun_BTC_30(IStrategy): "1200": 0 } # 5m and 15m - minimal_roi_1 = { + minimal_roi = { "0": 0.1, "60": 0.05, "120": 0.02, "240": 0 } # 15m and 30m - minimal_roi_1 = { + minimal_roi = { "0": 0.1, "240": 0.05, "480": 0.03, @@ -62,12 +62,12 @@ class ChanLun_BTC_30(IStrategy): "3600": 0 } can_short = True - lev = 1.0 + lev = 2.0 stoploss = -0.3 - trailing_stop = False + trailing_stop = True trailing_stop_positive = 0.025 trailing_stop_positive_offset = 0.045 - trailing_only_offset_is_reached = False + trailing_only_offset_is_reached = True position_adjustment_enable = True startup_candle_count = 600 @@ -81,6 +81,7 @@ class ChanLun_BTC_30(IStrategy): chan = ChanLun() chanpy = ChanPY() classifier = ChanLunClassifier(None) + last_trade = None def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # resample our dataframes @@ -104,17 +105,13 @@ class ChanLun_BTC_30(IStrategy): dataframe_4h = self.add_indicators(dataframe_4h) dataframe_1d = self.add_indicators(dataframe_1d) #self.chan.plot_dual(dataframe_5, dataframe_30) - dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) dataframe_5['chanpy_state'] = self.chanpy.get_bsp_state(dataframe_5) state_list, fx_list = self.chan.get_klc_strength_list(dataframe_30) dataframe_30['state'] = state_list dataframe_30['fx'] = fx_list - klc_list = self.chan.get_klc_list(dataframe_30) - bi_list = self.chan.cal_bi_list(klc_list) + if self.last_time + timedelta(minutes=1) < datetime.now(): - print(state_list[-1], state_list[-2], state_list[-3], state_list[-4], state_list[-5]) - print(fx_list[-1], fx_list[-2], fx_list[-3], fx_list[-4], fx_list[-5]) - print(klc_list[-1].klc_fx_type, klc_list[-2].klc_fx_type, klc_list[-3].klc_fx_type, klc_list[-4].klc_fx_type, klc_list[-5].klc_fx_type) + print("-------------------------------------------------------------------------------") self.last_time = datetime.now() dataframe = resampled_merge(dataframe, dataframe_5) @@ -168,18 +165,84 @@ class ChanLun_BTC_30(IStrategy): else: new_exitprice = proposed_rate - 50 return new_exitprice - + + def confirm_trade_entry(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: + if self.last_trade.open_date + timedelta(minutes=30) > current_time: + return False + #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_exit(self, pair: str, trade: Trade, current_time: datetime, current_rate: float, + current_profit: float, **kwargs): + #dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) + #last_candle = dataframe.iloc[-1].squeeze() + """ + # Above 20% profit, sell when rsi < 80 + if current_profit > 0.2: + if last_candle["rsi"] < 80: + return "rsi_below_80" + + # Between 2% and 10%, sell if EMA-long above EMA-short + if 0.02 < current_profit < 0.1: + if last_candle["emalong"] > last_candle["emashort"]: + return "ema_long_below_80" + + # Sell any positions at a loss if they are held for more than one day. + if current_profit < 0.0 and (current_time - trade.open_date_utc).days >= 1: + return "unclog" + """ + if trade.is_short: + last_high = trade.get_custom_data(key="entry_candle_high") + if 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: + print(trade.open_date, last_low, current_rate, "Relay Bottom FX exit") + return "Relay Bottom FX exit" + 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). + :param pair: Pair for trade + :param trade: trade object. + :param order: Order object. + :param current_time: datetime object, containing the current datetime + :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. + """ + # 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)) + bi_list = self.chan.cal_bi_list(klc_list) + last_high = klc_list[-3].high + last_low = klc_list[-3].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) + 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) + self.last_trade = trade + return None def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: state_str = 'resample_{}_state'.format(self.get_ticker_indicator()*self.time30) fx_str = 'resample_{}_fx'.format(self.get_ticker_indicator()*self.time30) chanpy_state_str = 'resample_{}_chanpy_state'.format(self.get_ticker_indicator()*self.time5) - shift_time = self.time30 + shift_time = self.time30*2 + strength = 2.2 dataframe.loc[ ( #(dataframe['state'] == "-30") - (dataframe[state_str].shift(shift_time) > 1.0) & + (dataframe[state_str].shift(shift_time) > strength) & (dataframe[fx_str].shift(shift_time) == -1) & - (dataframe[chanpy_state_str].shift(shift_time) == 1) + (dataframe[chanpy_state_str].shift(shift_time+10) == 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,9 +252,9 @@ class ChanLun_BTC_30(IStrategy): dataframe.loc[ ( #(dataframe['state'] == "-30") - (dataframe[state_str].shift(shift_time) > 1.0) & + (dataframe[state_str].shift(shift_time) > strength) & (dataframe[fx_str].shift(shift_time) == 1) & - (dataframe[chanpy_state_str].shift(shift_time) == -1) + (dataframe[chanpy_state_str].shift(shift_time+10) == -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") @@ -203,13 +266,14 @@ class ChanLun_BTC_30(IStrategy): state_str = 'resample_{}_state'.format(self.get_ticker_indicator()*self.time30) fx_str = 'resample_{}_fx'.format(self.get_ticker_indicator()*self.time30) chanpy_state_str = 'resample_{}_chanpy_state'.format(self.get_ticker_indicator()*self.time5) - shift_time = self.time30 + shift_time = self.time30*2 + strength = 2.2 dataframe.loc[ ( #(dataframe['state']== "30") - (dataframe[state_str].shift(shift_time) > 1.0) & + (dataframe[state_str].shift(shift_time) > strength) & (dataframe[fx_str].shift(shift_time) == 1) & - (dataframe[chanpy_state_str].shift(shift_time) == -1) + (dataframe[chanpy_state_str].shift(shift_time+10) == -1) #(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time30)] == "10") & #(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time60)] == "10") ), @@ -217,9 +281,9 @@ class ChanLun_BTC_30(IStrategy): dataframe.loc[ ( #(dataframe['state']== "30") - (dataframe[state_str].shift(shift_time) > 1.0) & + (dataframe[state_str].shift(shift_time) > strength) & (dataframe[fx_str].shift(shift_time) == -1) & - (dataframe[chanpy_state_str].shift(shift_time) == 1) + (dataframe[chanpy_state_str].shift(shift_time+10) == 1) #(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time30)] == "10") & #(dataframe['resample_{}_state'.format(self.get_ticker_indicator()*self.time60)] == "10") ),