From 250664d8b303f37d356ba1a8a80baa17a81c06aa Mon Sep 17 00:00:00 2001 From: Kavinkumar <33546454+mkavinkumar1@users.noreply.github.com> Date: Sat, 19 Feb 2022 18:55:07 +0530 Subject: [PATCH] included unit test --- freqtrade/freqtradebot.py | 34 ++++++++++++++------------ freqtrade/persistence/models.py | 23 ++++++++++++++---- freqtrade/rpc/telegram.py | 4 ++-- tests/rpc/test_rpc_telegram.py | 18 +++++++++----- tests/test_freqtradebot.py | 42 +++++++++++++++++++++++++++++++-- 5 files changed, 91 insertions(+), 30 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 71daf741c..789a3edaa 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -480,7 +480,7 @@ class FreqtradeBot(LoggingMixin): # We should decrease our position proposed_exit_rate = self.exchange.get_rate(pair, refresh=True, side="buy") self.execute_trade_exit(trade, proposed_exit_rate, sell_reason=SellCheckTuple( - sell_type=SellType.CUSTOM_SELL), partial=stake_amount) + sell_type=SellType.CUSTOM_SELL), sub_trade_amt=stake_amount) def _check_depth_of_market_buy(self, pair: str, conf: Dict) -> bool: """ @@ -620,7 +620,7 @@ class FreqtradeBot(LoggingMixin): # Updating wallets self.wallets.update() - self._notify_enter(trade, order, order_type, partial=pos_adjust) + self._notify_enter(trade, order, order_type, sub_trade=pos_adjust) if pos_adjust: if order_status == 'closed': @@ -677,7 +677,7 @@ class FreqtradeBot(LoggingMixin): return enter_limit_requested, stake_amount def _notify_enter(self, trade: Trade, order: Dict, order_type: Optional[str] = None, - fill: bool = False, partial: bool = False) -> None: + fill: bool = False, sub_trade: bool = False) -> None: """ Sends rpc notification when a buy occurred. """ @@ -704,13 +704,13 @@ class FreqtradeBot(LoggingMixin): 'amount': safe_value_fallback(order, 'filled', 'amount') or trade.amount, 'open_date': trade.open_date or datetime.utcnow(), 'current_rate': current_rate, - 'partial': partial, + 'sub_trade': sub_trade, } # Send the message self.rpc.send_msg(msg) - def _notify_enter_cancel(self, trade: Trade, order_type: str, reason: str) -> 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. """ @@ -731,6 +731,7 @@ class FreqtradeBot(LoggingMixin): 'open_date': trade.open_date, 'current_rate': current_rate, 'reason': reason, + 'sub_trade': sub_trade, } # Send the message @@ -1154,7 +1155,7 @@ class FreqtradeBot(LoggingMixin): *, exit_tag: Optional[str] = None, ordertype: Optional[str] = None, - partial: float = None, + sub_trade_amt: float = None, ) -> bool: """ Executes a trade exit for the given trade and limit @@ -1192,7 +1193,7 @@ class FreqtradeBot(LoggingMixin): # Emergency sells (default to market!) order_type = self.strategy.order_types.get("emergencysell", "market") - amount = self._safe_exit_amount(trade.pair, trade.amount) + amount = sub_trade_amt or self._safe_exit_amount(trade.pair, trade.amount) time_in_force = self.strategy.order_time_in_force['sell'] if not strategy_safe_wrapper(self.strategy.confirm_trade_exit, default_retval=True)( @@ -1227,15 +1228,15 @@ class FreqtradeBot(LoggingMixin): self.strategy.lock_pair(trade.pair, datetime.now(timezone.utc), reason='Auto lock') - self._notify_exit(trade, order_type, partial=bool(partial)) + 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) + 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, partial: 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. """ @@ -1269,7 +1270,8 @@ class FreqtradeBot(LoggingMixin): 'stake_amount': trade.stake_amount, 'stake_currency': self.config['stake_currency'], 'fiat_currency': self.config.get('fiat_display_currency', None), - 'partial': partial, + 'sub_trade': sub_trade, + 'stake_amount': trade.stake_amount, } if 'fiat_display_currency' in self.config: @@ -1280,7 +1282,7 @@ class FreqtradeBot(LoggingMixin): # Send the message self.rpc.send_msg(msg) - def _notify_exit_cancel(self, trade: Trade, order_type: str, reason: str) -> 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. """ @@ -1315,6 +1317,8 @@ class FreqtradeBot(LoggingMixin): 'stake_currency': self.config['stake_currency'], 'fiat_currency': self.config.get('fiat_display_currency', None), 'reason': reason, + 'sub_trade': sub_trade, + 'stake_amount': trade.stake_amount, } if 'fiat_display_currency' in self.config: @@ -1330,7 +1334,7 @@ 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) -> 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. @@ -1363,7 +1367,7 @@ class FreqtradeBot(LoggingMixin): order = self.handle_order_fee(trade, order) - trade.update(order) + trade.update(order,sub_trade=sub_trade) trade.recalc_trade_from_orders() Trade.commit() @@ -1527,4 +1531,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) + min_custom_price_allowed) \ No newline at end of file diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py index 0b701aa45..4e9e9e93a 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_recovered = Column(Boolean, nullable=False, default=False) order_id = Column(String(255), nullable=False, index=True) status = Column(String(255), nullable=True) symbol = Column(String(25), nullable=True) @@ -481,9 +481,17 @@ class LocalTrade(): orders=(self.select_filled_orders('buy')) lbuy=orders[-2] lamount = (lbuy.filled or lbuy.amount) - lbuy.average=(lbuy.average * lamount - self.calc_profit2(orders[-1].rate, order.rate, order.filled or order.amount))/lamount + 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_recovered=True + self.update_order(orders[-1]) + + + # self.orders.remove(orders[-1]) + self.update_order(lbuy) Order.query.session.commit() - self.orders.remove(orders[-1]) self.recalc_trade_from_orders() Trade.commit() else: @@ -499,8 +507,11 @@ 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 Decimal(self.amount) *( (1-self.fee_close)* Decimal(close_rate) -(1+self.fee_open)* Decimal(open_rate) ) + 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: """ Sets close_rate to the given rate, calculates total profit @@ -638,6 +649,7 @@ class LocalTrade(): for o in self.orders: if (o.ft_is_open or (o.ft_order_side != 'buy') or + o.is_recovered==True or (o.status not in NON_OPEN_EXCHANGE_STATES)): continue @@ -695,6 +707,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_recovered==False and o.status in NON_OPEN_EXCHANGE_STATES] @property diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 6c9ad083a..4da5007a7 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -246,10 +246,10 @@ class Telegram(RPCHandler): total, msg['stake_currency'], msg['fiat_currency']) else: total_fiat = 0 - message += f"*Total:* `({round_coin_value(msg['stake_amount'], msg['stake_currency'])}" + message += f"*Total:* `({round_coin_value(total, msg['stake_currency'])}" if msg.get('fiat_currency', None): - message += f", {round_coin_value(total, msg['fiat_currency'])}" + message += f", {round_coin_value(total_fiat, msg['fiat_currency'])}" message += ")`" if msg.get('sub_trade'): diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index 67a6c72fe..d7c81d2bc 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -1037,6 +1037,8 @@ def test_telegram_forcesell_handle(default_conf, update, ticker, fee, 'open_date': ANY, 'close_date': ANY, 'close_rate': ANY, + 'stake_amount': 0.0009999999999054, + 'sub_trade': False } == last_msg @@ -1102,6 +1104,8 @@ def test_telegram_forcesell_down_handle(default_conf, update, ticker, fee, 'open_date': ANY, 'close_date': ANY, 'close_rate': ANY, + 'stake_amount': 0.0009999999999054, + 'sub_trade': False } == last_msg @@ -1157,6 +1161,8 @@ 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, + 'sub_trade': False } == msg @@ -1734,7 +1740,7 @@ def test_send_msg_buy_notification(default_conf, mocker, caplog) -> None: 'pair': 'ETH/BTC', 'limit': 1.099e-05, 'order_type': 'limit', - 'stake_amount': 0.001, + 'stake_amount': 0.01465333, 'stake_amount_fiat': 0.0, 'stake_currency': 'BTC', 'fiat_currency': 'USD', @@ -1751,7 +1757,7 @@ def test_send_msg_buy_notification(default_conf, mocker, caplog) -> None: '*Amount:* `1333.33333333`\n' \ '*Open Rate:* `0.00001099`\n' \ '*Current Rate:* `0.00001099`\n' \ - '*Total:* `(0.00100000 BTC, 12.345 USD)`' + '*Total:* `(0.01465333 BTC, 180.895 USD)`' freqtradebot.config['telegram']['notification_settings'] = {'buy': 'off'} caplog.clear() @@ -1825,7 +1831,7 @@ def test_send_msg_buy_fill_notification(default_conf, mocker) -> None: 'buy_tag': 'buy_signal_01', 'exchange': 'Binance', 'pair': 'ETH/BTC', - 'stake_amount': 0.001, + 'stake_amount': 0.01465333, # 'stake_amount_fiat': 0.0, 'stake_currency': 'BTC', 'fiat_currency': 'USD', @@ -1839,7 +1845,7 @@ def test_send_msg_buy_fill_notification(default_conf, mocker) -> None: '*Buy Tag:* `buy_signal_01`\n' \ '*Amount:* `1333.33333333`\n' \ '*Open Rate:* `0.00001099`\n' \ - '*Total:* `(0.00100000 BTC, 12.345 USD)`' + '*Total:* `(0.01465333 BTC, 180.895 USD)`' def test_send_msg_sell_notification(default_conf, mocker) -> None: @@ -2031,7 +2037,7 @@ def test_send_msg_buy_notification_no_fiat(default_conf, mocker) -> None: 'pair': 'ETH/BTC', 'limit': 1.099e-05, 'order_type': 'limit', - 'stake_amount': 0.001, + 'stake_amount': 0.01465333, 'stake_amount_fiat': 0.0, 'stake_currency': 'BTC', 'fiat_currency': None, @@ -2044,7 +2050,7 @@ def test_send_msg_buy_notification_no_fiat(default_conf, mocker) -> None: '*Amount:* `1333.33333333`\n' '*Open Rate:* `0.00001099`\n' '*Current Rate:* `0.00001099`\n' - '*Total:* `(0.00100000 BTC)`') + '*Total:* `(0.01465333 BTC)`') def test_send_msg_sell_notification_no_fiat(default_conf, mocker) -> None: diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 66c983240..334e04b14 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -4320,7 +4320,6 @@ def test_position_adjust(mocker, default_conf_usdt, fee) -> None: get_fee=fee, ) pair = 'ETH/USDT' - # Initial buy closed_successful_buy_order = { 'pair': pair, @@ -4385,7 +4384,7 @@ def test_position_adjust(mocker, default_conf_usdt, fee) -> None: 'status': None, 'price': 9, 'amount': 12, - 'cost': 100, + 'cost': 108, 'ft_is_open': True, 'id': '651', 'order_id': '651' @@ -4532,7 +4531,46 @@ def test_position_adjust(mocker, default_conf_usdt, fee) -> None: # Make sure the closed order is found as the second order. order = trade.select_order('buy', False) assert order.order_id == '652' + closed_sell_dca_order_1 = { + 'ft_pair': pair, + 'status': 'closed', + 'ft_order_side': 'sell', + 'side': 'sell', + 'type': 'limit', + 'price': 8, + 'average': 8, + 'amount': 15, + 'filled': 15, + 'cost': 120, + 'ft_is_open': False, + 'id': '653', + 'order_id': '653' + } + mocker.patch('freqtrade.exchange.Exchange.create_order', + MagicMock(return_value=closed_sell_dca_order_1)) + mocker.patch('freqtrade.exchange.Exchange.fetch_order', + MagicMock(return_value=closed_sell_dca_order_1)) + 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) + # 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 + assert trade.stake_amount == 203.5625 + assert pytest.approx(trade.open_rate) == 9.252840909090908 + + orders = Order.query.all() + assert orders + assert len(orders) == 4 + + # Make sure the closed order is found as the second order. + 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({