diff --git a/HeikinAshi.py b/HeikinAshi.py index ea6e866..0a145a2 100644 --- a/HeikinAshi.py +++ b/HeikinAshi.py @@ -129,6 +129,7 @@ class HeikinAshi(IStrategy): pairs = { pair: { + "last_max": 0, "trade_info": {}, "max_touch": 0.0, @@ -139,7 +140,8 @@ class HeikinAshi(IStrategy): 'expected_profit': 0, "last_candle": {}, "last_trade": None, - 'base_stake_amount': 0 + 'base_stake_amount': 0, + 'stop_buy': False } for pair in ["BTC/USDT", "ETH/USDT", "DOGE/USDT", "DASH/USDT", "XRP/USDT", "SOL/USDT"] } @@ -164,7 +166,7 @@ class HeikinAshi(IStrategy): dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe) last_candle = dataframe.iloc[-1].squeeze() - # last_candle_decalage = dataframe.iloc[-1 - self.decalage.value].squeeze() + last_candle_3 = dataframe.iloc[-4].squeeze() # last_candle_24 = dataframe.iloc[-25].squeeze() # if (last_candle['sma5_diff_1d'] < -0.1): @@ -176,67 +178,7 @@ class HeikinAshi(IStrategy): dispo = round(self.wallets.get_available_stake_amount()) hours = (current_time - trade.date_last_filled_utc).total_seconds() / 3600.0 - # if (current_profit > 0.008) \ - # and (last_candle['up_pct'] >= 1)\ - # and (last_candle['volume'] >= 250) \ - # and (hours >= 1): - # additional_stake = self.config['stake_amount'] - # self.log_trade( - # last_candle=last_candle, - # date=current_time, - # action="Gain +", - # dispo=dispo, - # pair=trade.pair, - # rate=current_rate, - # trade_type='Increase', - # profit=round(current_profit, 4), # round(current_profit * trade.stake_amount, 2), - # buys=trade.nr_of_successful_entries, - # stake=round(additional_stake, 2) - # ) - # # self.pairs[trade.pair]['last_max'] = last_candle['haclose'] - # self.pairs[trade.pair]['max_touch'] = last_candle['haclose'] - # return additional_stake - - # if (last_candle['percent'] > 0.001) and (current_profit > 0): - # # and (last_candle_decalage['min12'] == last_candle['min12']) \ - # # and (last_candle_decalage['close'] < last_candle_decalage['mid288']): - # additional_stake = self.config['stake_amount'] / 10 - # self.log_trade( - # last_candle=last_candle, - # date=current_time, - # action="Gain +", - # dispo=dispo, - # pair=trade.pair, - # rate=current_rate, - # trade_type='Increase', - # profit=round(current_profit, 4), # round(current_profit * trade.stake_amount, 2), - # buys=trade.nr_of_successful_entries, - # stake=round(additional_stake, 2) - # ) - # return additional_stake max_touch = self.pairs[trade.pair]['max_touch'] - pct_max = - round(100 * (last_candle['close'] - max_touch) / max_touch, 1) - - # if (last_candle['enter_long'] == 1) and (current_profit < - 0.0075 or hours >= 1) and (count_of_buys == 1): - # additional_stake = self.config['stake_amount'] / 2 - # self.log_trade( - # last_candle=last_candle, - # date=current_time, - # action="Long", - # dispo=dispo, - # pair=trade.pair, - # rate=current_rate, - # trade_type='Increase', - # profit=round(current_profit, 4), # round(current_profit * trade.stake_amount, 2), - # buys=trade.nr_of_successful_entries + 1, - # stake=round(additional_stake, 2) - # ) - # self.expectedProfit(trade.pair, last_candle, current_rate) - # self.pairs[trade.pair]['last_buy'] = current_rate - # self.pairs[trade.pair]['max_touch'] = last_candle['close'] - # self.pairs[trade.pair]['last_candle'] = last_candle - # - # return additional_stake limit_buy = 5 if (count_of_buys < limit_buy) \ @@ -252,7 +194,7 @@ class HeikinAshi(IStrategy): dispo=dispo, pair=trade.pair, rate=current_rate, - trade_type='Decrease', + trade_type=last_candle['enter_tag'], profit=round(current_profit, 4), # round(current_profit * trade.stake_amount, 2), buys=trade.nr_of_successful_entries + 1, stake=round(additional_stake, 2) @@ -264,29 +206,11 @@ class HeikinAshi(IStrategy): return additional_stake - # if (count_of_buys == limit_buy) & (current_profit < - 0.03 * count_of_buys)\ - # and ((last_candle['enter_long'] == 1) or last_candle['percent48'] < - 0.03): - # additional_stake = - trade.stake_amount / 2 #self.config['stake_amount'] * (-current_profit / 0.10) - # self.log_trade( - # last_candle=last_candle, - # date=current_time, - # action="Loss -", - # dispo=dispo, - # pair=trade.pair, - # rate=current_rate, - # trade_type='Decrease', - # profit=round(current_profit, 4), # round(current_profit * trade.stake_amount, 2), - # buys=trade.nr_of_successful_entries, - # stake=round(additional_stake, 2) - # ) - # # self.pairs[trade.pair]['last_max'] = last_candle['haclose'] - # self.pairs[trade.pair]['max_touch'] = last_candle['haclose'] - # return additional_stake - pct_limit = (-0.015 * limit_buy) + (- 0.03 * (count_of_buys - limit_buy)) - if (count_of_buys >= limit_buy) & (current_profit < pct_limit) \ - and ((last_candle['enter_long'] == 1) or last_candle['percent48'] < - 0.03): - additional_stake = self.calculate_stake(trade.pair, last_candle, 1) * (-current_profit / 0.10) + if (count_of_buys >= limit_buy) and (current_profit < pct_limit) \ + and ((last_candle['enter_long'] == 1) or + (last_candle['percent48'] < - 0.03 and last_candle['min200'] == last_candle_3['min200'])): + additional_stake = self.calculate_stake(trade.pair, last_candle, 1) * (-current_profit / 0.1) self.log_trade( last_candle=last_candle, date=current_time, @@ -294,7 +218,7 @@ class HeikinAshi(IStrategy): dispo=dispo, pair=trade.pair, rate=current_rate, - trade_type='Decrease', + trade_type=last_candle['enter_tag'], profit=round(current_profit, 4), # round(current_profit * trade.stake_amount, 2), buys=trade.nr_of_successful_entries + 1, stake=round(additional_stake, 2) @@ -306,6 +230,26 @@ class HeikinAshi(IStrategy): return additional_stake + # if (current_profit > 0) and (count_of_buys >= limit_buy) and (hours > 24) and (last_candle['enter_long'] == 1): + # additional_stake = self.calculate_stake(trade.pair, last_candle, 1) + # self.log_trade( + # last_candle=last_candle, + # date=current_time, + # action="Gain +", + # dispo=dispo, + # pair=trade.pair, + # rate=current_rate, + # trade_type=last_candle['enter_tag'], + # profit=round(current_profit, 4), # round(current_profit * trade.stake_amount, 2), + # buys=trade.nr_of_successful_entries + 1, + # stake=round(additional_stake, 2) + # ) + # self.expectedProfit(trade.pair, last_candle, current_rate) + # self.pairs[trade.pair]['last_buy'] = current_rate + # self.pairs[trade.pair]['max_touch'] = last_candle['close'] + # self.pairs[trade.pair]['last_candle'] = last_candle + # + # return additional_stake return None def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, @@ -316,30 +260,29 @@ class HeikinAshi(IStrategy): # Obtenir les données actuelles pour cette paire last_candle = dataframe.iloc[-1].squeeze() stake_amount = self.config['stake_amount'] - # if last_candle['close'] < last_candle['max5_1d'] * 0.98 : - # stake_amount = 2 * stake_amount - # else: - # if last_candle['close'] > last_candle['max5_1d'] * 1.02: - # stake_amount = 0.5 * stake_amount - # if last_candle['entry_tag'] == 'buy_hammer': - # stake_amount = stake_amount * 2 + m = max(last_candle['max12_1d'], current_rate) + if last_candle['max12_1d'] > 0: + if (last_candle['close'] < m * 0.90): + stake_amount = stake_amount * 3 + else: + if (last_candle['close'] < m * 0.95): + stake_amount = stake_amount * 2 return stake_amount def calculate_stake(self, pair, last_candle, factor=1): - # if self.pairs[pair]['count_of_buys'] == 1 and factor == 1: - # if last_candle['close'] > last_candle['min5_1d'] + (last_candle['max5_1d'] - last_candle['min5_1d']) / 2: - # factor = 0.5 - # amount = self.config['stake_amount'] * factor - # else: - # amount = self.config['stake_amount'] - # self.pairs[pair]['base_stake_amount'] = amount - # else: - # amount = max(self.config['stake_amount'], self.pairs[pair]['base_stake_amount']) + stake_amount = self.config['stake_amount'] + m = max(last_candle['max12_1d'], last_candle['close']) - amount = self.config['stake_amount'] - return amount + if last_candle['max12_1d'] > 0: + if (last_candle['close'] < m * 0.90): + stake_amount = stake_amount * 3 + else: + if (last_candle['close'] < m * 0.95): + stake_amount = stake_amount * 2 + + return stake_amount def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, current_time: datetime, entry_tag: Optional[str], **kwargs) -> bool: @@ -361,12 +304,28 @@ class HeikinAshi(IStrategy): # return False self.pairs[pair]['last_buy'] = rate self.pairs[pair]['max_touch'] = last_candle['close'] - self.pairs[pair]['last_max'] = last_candle['close'] self.pairs[pair]['last_candle'] = last_candle self.pairs[pair]['count_of_buys'] = 1 self.pairs[pair]['current_profit'] = 0 stake_amount = self.calculate_stake(pair, last_candle, 1) + # if self.pairs[pair]['stop_buy']: + # if last_candle['sma5_diff_1d'] > 0: + # self.pairs[pair]['stop_buy'] = False + # else: + # self.log_trade( + # last_candle=last_candle, + # date=current_time, + # action="CANCEL BUY", + # pair=pair, + # rate=rate, + # dispo=dispo, + # profit=0, + # trade_type='stop_buy', + # buys=1, + # stake=0 + # ) + # return False # self.columns_logged = False print( f"|{'-' * 18}+{'-' * 12}+{'-' * 12}+{'-' * 20}+{'-' * 14}+{'-' * 8}+{'-' * 10}+{'-' * 7}+{'-' * 13}+{'-' * 14}+{'-' * 14}+{'-' * 7}+{'-' * 12}|" @@ -411,7 +370,6 @@ class HeikinAshi(IStrategy): dispo=dispo, profit=round(trade.calc_profit(rate, amount), 2) ) - self.pairs[pair]['last_max'] = 0 self.pairs[pair]['max_touch'] = 0 self.pairs[pair]['last_buy'] = 0 @@ -426,7 +384,6 @@ class HeikinAshi(IStrategy): before_last_candle = dataframe.iloc[-2].squeeze() max_touch_before = self.pairs[pair]['max_touch'] - last_max_before = self.pairs[pair]['last_max'] self.pairs[pair]['last_max'] = max(last_candle['haclose'], self.pairs[pair]['last_max']) last_lost = (last_candle['close'] - max_touch_before) / max_touch_before @@ -444,14 +401,13 @@ class HeikinAshi(IStrategy): # print( # f"{current_time} days={days} expected={expected_profit:.3f} rate={current_rate} max_touch={max_touch_before:.1f} profit={current_profit:.3f} last_lost={last_lost:.3f} buys={count_of_buys} percent={last_candle['percent']:.4f}") + # if count_of_buys >= 5 and current_profit < 0 and (last_candle['percent12'] < -0.015): + # self.pairs[pair]['stop_buy'] = True + # return 'count_' + str(count_of_buys) if (current_profit > expected_profit) \ - & (last_candle['percent'] < 0.0) \ + & (last_candle['percent5'] < 0.0) \ & (last_lost > - current_profit / 5): - # & (before_last_candle['hasma5'] < last_candle['hasma5']): - # & (last_lost < min(-0.003, - min(0.006, current_profit / 4))): - # & (last_candle['up_count'] > 0): - return 'last_lost_' + str(count_of_buys) self.pairs[pair]['max_touch'] = max(last_candle['haclose'], self.pairs[pair]['max_touch']) @@ -460,7 +416,7 @@ class HeikinAshi(IStrategy): # & (last_candle['percent3'] < - min(0.01, current_profit / 4)): # return 'profit_' + str(count_of_buys) - def detect_loose_hammer(self, df: DataFrame) -> DataFrame: + def detect_loose_hammer(self, df: DataFrame, fact=2.5) -> DataFrame: """ Détection large de marteaux : accepte des corps plus gros, ne vérifie pas le volume, ne demande pas de divergence, juste un pattern visuel simple. @@ -472,14 +428,14 @@ class HeikinAshi(IStrategy): # Critères simplifiés : df['loose_hammer'] = ( - (lower_shadow > body * 2.5) # mèche basse > 1.5x corps + (lower_shadow > body * fact) # mèche basse > corps & (upper_shadow < body) # petite mèche haute - & (df['low'] < df['bb_lowerband']) # bougie verte (optionnel, on peut prendre aussi les rouges) + & (df['low'] < df['bb_lowerband']) ).astype(int) df['won_hammer'] = ( - (upper_shadow > body * 2.5) # mèche basse > 1.5x corps + (upper_shadow > body * fact) # mèche basse > corps & (lower_shadow < body) # petite mèche haute - & (df['high'] > df['bb_upperband']) # bougie verte (optionnel, on peut prendre aussi les rouges) + & (df['high'] > df['bb_upperband']) ).astype(int) return df @@ -488,7 +444,6 @@ class HeikinAshi(IStrategy): last_buy = self.pairs[pair]['last_buy'] max_touch = self.pairs[pair]['max_touch'] - last_max = self.pairs[pair]['last_max'] expected_profit = ((max_touch - last_buy) / max_touch) self.pairs[pair]['expected_profit'] = max(0.004, expected_profit) @@ -532,28 +487,19 @@ class HeikinAshi(IStrategy): # action = self.color_line(action, action) sma5_1d = '' sma5_1h = '' - # if last_candle['sma5_pct_1d'] is not None: - # sma5_1d = round(last_candle['sma5_pct_1d'] * 100, 2) - # if last_candle['sma5_pct_1h'] is not None: - # sma5_1h = round(last_candle['sma5_pct_1h'] * 100, 2) - sma5 = str(sma5_1d) + ' ' + str(sma5_1h) - first_rate = self.pairs[pair]['last_max'] - # if action != 'Sell': - # profit = round((last_candle['close'] - self.pairs[pair]['last_max']) / self.pairs[pair]['last_max'], 2) + sma5 = str(sma5_1d) + ' ' + str(sma5_1h) + last_lost = round((last_candle['haclose'] - self.pairs[pair]['max_touch']) / self.pairs[pair]['max_touch'], 3) - limit_sell = rsi_pct # round((last_candle['close'] - self.pairs[pair]['last_max']) / self.pairs[pair]['last_max'], 4) - max_touch = round(self.pairs[pair]['max_touch'], - 1) # last_candle['max7_1d'] #round(100 * (last_candle['close'] - self.pairs[pair]['last_max']) / self.pairs[pair]['last_max'], 1) - pct_max = round(100 * self.pairs[pair]['current_profit'], - 1) # round(100 * (last_candle['close'] - max_touch) / max_touch, 1) + max_touch = round(self.pairs[pair]['max_touch'], 1) + pct_max = round(100 * self.pairs[pair]['current_profit'], 1) if trade_type is not None: trade_type = trade_type + " " + str(round(100 * self.pairs[pair]['expected_profit'], 1)) print( - f"| {date:<16} | {action:<10} | {pair:<10} | {trade_type or '-':<18} | {rate or '-':>12} | {dispo or '-':>6} | {profit or '-':>8} | {pct_max or '-':>5} | {max_touch or '-':>11} | {last_lost or '-':>12} | {round(self.pairs[pair]['last_max'], 2) or '-':>12} | {self.pairs[pair]['count_of_buys'] or '-':>5} | {stake or '-':>10} |" + f"| {date:<16} | {action:<10} | {pair:<10} | {trade_type or '-':<18} | {rate or '-':>12} | {dispo or '-':>6} | {profit or '-':>8} | {pct_max or '-':>5} | {max_touch or '-':>11} | {last_lost or '-':>12} | {round(self.pairs[pair]['last_max'], 2) or '-':>12} | {buys or '-':>5} | {stake or '-':>10} |" ) def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: @@ -575,12 +521,17 @@ class HeikinAshi(IStrategy): dataframe['min288'] = talib.MIN(dataframe['close'], timeperiod=288) dataframe['max288'] = talib.MAX(dataframe['close'], timeperiod=288) dataframe['mid288'] = dataframe['min288'] + (dataframe['max288'] - dataframe['min288']) / 2 + dataframe['min200'] = talib.MIN(dataframe['close'], timeperiod=200) + dataframe['max200'] = talib.MAX(dataframe['close'], timeperiod=200) + dataframe['min_max200'] = (dataframe['max200'] - dataframe['min200']) / dataframe['min200'] dataframe["percent"] = (dataframe["close"] - dataframe["open"]) / dataframe["open"] dataframe["percent3"] = dataframe['close'].pct_change(3) dataframe["percent5"] = dataframe['close'].pct_change(5) + dataframe["percent12"] = dataframe['close'].pct_change(12) dataframe["percent48"] = dataframe['close'].pct_change(48) + dataframe['average_line_288'] = talib.MIDPOINT(dataframe['close'], timeperiod=288) # Bollinger Bands bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2) @@ -606,6 +557,10 @@ class HeikinAshi(IStrategy): # # ======================================================================================Decrease # ################### INFORMATIVE 1d informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe="1d") + bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2) + informative['bb_lowerband'] = bollinger['lower'] + informative['bb_middleband'] = bollinger['mid'] + informative['bb_upperband'] = bollinger['upper'] # # Moving Averages # informative['ema5'] = EMAIndicator(informative['close'], window=5).ema_indicator() # informative['ema20'] = EMAIndicator(informative['close'], window=20).ema_indicator() @@ -618,9 +573,9 @@ class HeikinAshi(IStrategy): informative['max12'] = talib.MAX(informative['close'], timeperiod=12) informative['min5'] = talib.MIN(informative['close'], timeperiod=5) informative['min12'] = talib.MIN(informative['close'], timeperiod=12) - informative['sma5'] = talib.SMA(informative, timeperiod=25) + informative['sma5'] = talib.SMA(informative, timeperiod=5) informative['sma5_diff'] = 100 * (informative['sma5'] - informative['sma5'].shift(1)) / informative['sma5'] - # informative = self.detect_loose_hammer(informative) + informative = self.detect_loose_hammer(informative, 1.5) dataframe = merge_informative_pair(dataframe, informative, self.timeframe, "1d", ffill=True) @@ -673,12 +628,20 @@ class HeikinAshi(IStrategy): ) & (dataframe['down_count'] == 0) , - ['enter_long', 'enter_tag']] = [1, 'buy_down'] + ['enter_long', 'enter_tag']] = [1, 'down'] dataframe.loc[(dataframe['loose_hammer'] == 1) , - ['enter_long', 'enter_tag']] = [1, 'buy_hammer'] - + ['enter_long', 'enter_tag']] = [1, 'hammer'] + # dataframe.loc[ + # ( + # (dataframe['low'] <= dataframe['min200']) + # # & (dataframe['min_max200'] > 0.015) + # # & (dataframe['percent5'] < 0) + # # & (dataframe['haopen'] < buy_level) + # # & (dataframe['open'] < dataframe['average_line_288']) + # & (dataframe['min200'].shift(3) == dataframe['min200']) + # ), ['enter_long', 'enter_tag']] = (1, 'min200') return dataframe def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: