From fcddb0969474b27c619732495af25717c779b3f8 Mon Sep 17 00:00:00 2001 From: Kavinkumar <33546454+mkavinkumar1@users.noreply.github.com> Date: Mon, 21 Feb 2022 11:57:15 +0530 Subject: [PATCH] updated logic --- freqtrade/freqtradebot.py | 24 +++++++----- freqtrade/persistence/models.py | 68 +++++++++++++++++++++------------ freqtrade/rpc/telegram.py | 6 ++- tests/rpc/test_rpc_telegram.py | 6 +-- tests/test_freqtradebot.py | 20 +++++----- 5 files changed, 75 insertions(+), 49 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 789a3edaa..c6ad061f1 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -478,7 +478,7 @@ class FreqtradeBot(LoggingMixin): if stake_amount is not None and stake_amount < 0.0: # We should decrease our position - proposed_exit_rate = self.exchange.get_rate(pair, refresh=True, side="buy") + proposed_exit_rate = self.exchange.get_rate(trade.pair, refresh=True, side="buy") self.execute_trade_exit(trade, proposed_exit_rate, sell_reason=SellCheckTuple( sell_type=SellType.CUSTOM_SELL), sub_trade_amt=stake_amount) @@ -710,7 +710,8 @@ class FreqtradeBot(LoggingMixin): # Send the message self.rpc.send_msg(msg) - def _notify_enter_cancel(self, trade: Trade, order_type: str, reason: str, sub_trade: bool = False) -> None: + def _notify_enter_cancel(self, trade: Trade, order_type: str, reason: str, + sub_trade: bool = False) -> None: """ Sends rpc notification when a buy cancel occurred. """ @@ -1231,12 +1232,14 @@ class FreqtradeBot(LoggingMixin): self._notify_exit(trade, order_type, sub_trade=bool(sub_trade_amt)) # In case of market sell orders the order can be closed immediately if order.get('status', 'unknown') in ('closed', 'expired'): - self.update_trade_state(trade, trade.open_order_id, order, sub_trade=bool(sub_trade_amt)) + self.update_trade_state(trade, trade.open_order_id, order, + sub_trade=bool(sub_trade_amt)) Trade.commit() return True - def _notify_exit(self, trade: Trade, order_type: str, fill: bool = False, sub_trade: bool = False) -> None: + def _notify_exit(self, trade: Trade, order_type: str, fill: bool = False, + sub_trade: bool = False) -> None: """ Sends rpc notification when a sell occurred. """ @@ -1271,8 +1274,7 @@ class FreqtradeBot(LoggingMixin): 'stake_currency': self.config['stake_currency'], 'fiat_currency': self.config.get('fiat_display_currency', None), 'sub_trade': sub_trade, - 'stake_amount': trade.stake_amount, - } + } if 'fiat_display_currency' in self.config: msg.update({ @@ -1282,7 +1284,8 @@ class FreqtradeBot(LoggingMixin): # Send the message self.rpc.send_msg(msg) - def _notify_exit_cancel(self, trade: Trade, order_type: str, reason: str, sub_trade: bool = False) -> None: + def _notify_exit_cancel(self, trade: Trade, order_type: str, reason: str, + sub_trade: bool = False) -> None: """ Sends rpc notification when a sell cancel occurred. """ @@ -1334,7 +1337,8 @@ class FreqtradeBot(LoggingMixin): # def update_trade_state(self, trade: Trade, order_id: str, action_order: Dict[str, Any] = None, - stoploss_order: bool = False, send_msg: bool = True, sub_trade : bool = False) -> bool: + stoploss_order: bool = False, send_msg: bool = True, + sub_trade: bool = False) -> bool: """ Checks trades with open orders and updates the amount if necessary Handles closing both buy and sell orders. @@ -1367,7 +1371,7 @@ class FreqtradeBot(LoggingMixin): order = self.handle_order_fee(trade, order) - trade.update(order,sub_trade=sub_trade) + trade.update(order, sub_trade=sub_trade) trade.recalc_trade_from_orders() Trade.commit() @@ -1531,4 +1535,4 @@ class FreqtradeBot(LoggingMixin): # Bracket between min_custom_price_allowed and max_custom_price_allowed return max( min(valid_custom_price, max_custom_price_allowed), - min_custom_price_allowed) \ No newline at end of file + min_custom_price_allowed) diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py index 61abde616..e1b309091 100644 --- a/freqtrade/persistence/models.py +++ b/freqtrade/persistence/models.py @@ -116,7 +116,7 @@ class Order(_DECL_BASE): ft_order_side = Column(String(25), nullable=False) ft_pair = Column(String(25), nullable=False) ft_is_open = Column(Boolean, nullable=False, default=True, index=True) - is_processed = Column(Boolean, nullable=True, default=False) + is_realized = Column(Boolean, nullable=True, default=False) order_id = Column(String(255), nullable=False, index=True) status = Column(String(255), nullable=True) @@ -453,7 +453,7 @@ class LocalTrade(): f"Trailing stoploss saved us: " f"{float(self.stop_loss) - float(self.initial_stop_loss):.8f}.") - def update(self, order: Dict, sub_trade:bool=False) -> None: + def update(self, order: Dict, sub_trade: bool = False) -> None: """ Updates this entity with amount and actual open/close rates. :param order: order retrieved by exchange.fetch_order() @@ -479,22 +479,8 @@ class LocalTrade(): logger.info(f'{order_type.upper()}_SELL has been fulfilled for {self}.') self.open_order_id = None if sub_trade: - orders=(self.select_filled_orders('buy')) - lbuy=orders[-2] - lamount = (lbuy.filled or lbuy.amount) - o_rate = float(safe_value_fallback(order, 'average', 'price')) - o_amount = float(safe_value_fallback(order, 'filled', 'amount')) - o1_rate = orders[-1].average or orders[-1].price - lbuy.average=(lbuy.average * lamount - self.calc_profit2(o1_rate, o_rate, o_amount))/lamount - orders[-1].is_processed=True - self.update_order(orders[-1]) - - - # self.orders.remove(orders[-1]) - self.update_order(lbuy) - Order.query.session.commit() - self.recalc_trade_from_orders() - Trade.commit() + self.process_sell_sub_trade(order) + return else: self.close(safe_value_fallback(order, 'average', 'price')) elif order_type in ('stop_loss_limit', 'stop-loss', 'stop-loss-limit', 'stop'): @@ -508,10 +494,44 @@ class LocalTrade(): raise ValueError(f'Unknown order type: {order_type}') Trade.commit() - def calc_profit2(self, open_rate: float, close_rate: float, amount: float) ->float: - return float (Decimal(amount) * \ - ( Decimal(1-self.fee_close)* Decimal(close_rate) - \ - Decimal(1+self.fee_open)* Decimal(open_rate) )) + def process_sell_sub_trade(self, order: Dict) -> None: + orders = (self.select_filled_orders('buy')) + sell_rate = float(safe_value_fallback(order, 'average', 'price')) + sell_amount = float(safe_value_fallback(order, 'filled', 'amount')) + profit = 0 + idx = -1 + while sell_amount: + border = orders[idx] + buy_amount = border.filled or border.amount + buy_rate = border.average or border.price + if sell_amount < buy_amount: + amount = sell_amount + else: + if len(orders) == 1 and sell_amount == buy_amount: + self.close(safe_value_fallback(order, 'average', 'price')) + Trade.commit() + return + border.is_realized = True + self.update_order(border) + idx -= 1 + amount = buy_amount + sell_amount -= amount + profit += self.calc_profit2(buy_rate, sell_rate, amount) + border2 = orders[idx] + if not border.is_realized: + border2.filled -= amount + amount2 = border2.filled or border2.amount + border2.average = (border2.average * amount2 - profit) / amount2 + self.update_order(border2) + Order.query.session.commit() + self.recalc_trade_from_orders() + Trade.commit() + + def calc_profit2(self, open_rate: float, close_rate: float, + amount: float) -> float: + return float(Decimal(amount) * + (Decimal(1 - self.fee_close) * Decimal(close_rate) - + Decimal(1 + self.fee_open) * Decimal(open_rate))) def close(self, rate: float, *, show_msg: bool = True) -> None: """ @@ -650,7 +670,7 @@ class LocalTrade(): for o in self.orders: if (o.ft_is_open or (o.ft_order_side != 'buy') or - o.is_processed==True or + o.is_realized or (o.status not in NON_OPEN_EXCHANGE_STATES)): continue @@ -708,7 +728,7 @@ class LocalTrade(): return [o for o in self.orders if ((o.ft_order_side == order_side) or (order_side is None)) and o.ft_is_open is False and (o.filled or 0) > 0 and - o.is_processed!=True and + not o.is_realized and o.status in NON_OPEN_EXCHANGE_STATES] @property diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 4da5007a7..367dafd12 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -253,7 +253,8 @@ class Telegram(RPCHandler): message += ")`" if msg.get('sub_trade'): - message += f"\n*Balance:* `({round_coin_value(msg['stake_amount'], msg['stake_currency'])}" + bal = round_coin_value(msg['stake_amount'], msg['stake_currency']) + message += f"\n*Balance:* `({bal}" if msg.get('fiat_currency', None): message += f", {round_coin_value(msg['stake_amount_fiat'], msg['fiat_currency'])}" @@ -306,7 +307,8 @@ class Telegram(RPCHandler): msg['stake_amount'], msg['stake_currency'], msg['fiat_currency']) else: msg['stake_amount_fiat'] = 0 - message += f"\n*Balance:* `({round_coin_value(msg['stake_amount'], msg['stake_currency'])}" + bal = round_coin_value(msg['stake_amount'], msg['stake_currency']) + message += f"\n*Balance:* `({bal}" if msg.get('fiat_currency', None): message += f", {round_coin_value(msg['stake_amount_fiat'], msg['fiat_currency'])}" diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index d7c81d2bc..eaff9891a 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -1037,7 +1037,7 @@ def test_telegram_forcesell_handle(default_conf, update, ticker, fee, 'open_date': ANY, 'close_date': ANY, 'close_rate': ANY, - 'stake_amount': 0.0009999999999054, + 'stake_amount': 0.0009999999999054, 'sub_trade': False } == last_msg @@ -1104,7 +1104,7 @@ def test_telegram_forcesell_down_handle(default_conf, update, ticker, fee, 'open_date': ANY, 'close_date': ANY, 'close_rate': ANY, - 'stake_amount': 0.0009999999999054, + 'stake_amount': 0.0009999999999054, 'sub_trade': False } == last_msg @@ -1161,7 +1161,7 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None 'open_date': ANY, 'close_date': ANY, 'close_rate': ANY, - 'stake_amount': 0.0009999999999054, + 'stake_amount': 0.0009999999999054, 'sub_trade': False } == msg diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 334e04b14..bdb56bac8 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -2621,8 +2621,7 @@ def test_execute_trade_exit_up(default_conf_usdt, ticker_usdt, fee, ticker_usdt_ 'close_rate': ANY, 'sub_trade': False, 'stake_amount': 60.0, - - } == last_msg + } == last_msg def test_execute_trade_exit_down(default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_down, @@ -2677,8 +2676,8 @@ def test_execute_trade_exit_down(default_conf_usdt, ticker_usdt, fee, ticker_usd 'close_date': ANY, 'close_rate': ANY, 'sub_trade': False, - 'stake_amount': 60.0, - } == last_msg + 'stake_amount': 60.0, + } == last_msg def test_execute_trade_exit_custom_exit_price(default_conf_usdt, ticker_usdt, fee, @@ -2747,8 +2746,8 @@ def test_execute_trade_exit_custom_exit_price(default_conf_usdt, ticker_usdt, fe 'close_date': ANY, 'close_rate': ANY, 'sub_trade': False, - 'stake_amount': 60.0, - } == last_msg + 'stake_amount': 60.0, + } == last_msg def test_execute_trade_exit_down_stoploss_on_exchange_dry_run( @@ -2809,7 +2808,7 @@ def test_execute_trade_exit_down_stoploss_on_exchange_dry_run( 'close_date': ANY, 'close_rate': ANY, 'sub_trade': False, - 'stake_amount': 60.0, + 'stake_amount': 60.0, } == last_msg @@ -3029,7 +3028,7 @@ def test_execute_trade_exit_market_order(default_conf_usdt, ticker_usdt, fee, 'close_date': ANY, 'close_rate': ANY, 'sub_trade': False, - 'stake_amount': 60.0, + 'stake_amount': 60.0, } == last_msg @@ -4553,11 +4552,11 @@ def test_position_adjust(mocker, default_conf_usdt, fee) -> None: mocker.patch('freqtrade.exchange.Exchange.fetch_order_or_stoploss_order', MagicMock(return_value=closed_sell_dca_order_1)) assert freqtrade.execute_trade_exit(trade=trade, limit=8, - sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS),sub_trade_amt=15) + sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS), + sub_trade_amt=15) # Assert trade is as expected (averaged dca) trade = Trade.query.first() - print(trade.open_rate,trade.amount,trade.stake_amount,'DEBUG') assert trade assert trade.open_order_id is None assert trade.amount == 22 @@ -4572,6 +4571,7 @@ def test_position_adjust(mocker, default_conf_usdt, fee) -> None: order = trade.select_order('sell', False) assert order.order_id == '653' + def test_process_open_trade_positions_exception(mocker, default_conf_usdt, fee, caplog) -> None: default_conf_usdt.update({ "position_adjustment_enable": True,