included unit test

This commit is contained in:
Kavinkumar 2022-02-19 18:55:07 +05:30
parent da48ac9dab
commit 250664d8b3
5 changed files with 91 additions and 30 deletions

View File

@ -480,7 +480,7 @@ class FreqtradeBot(LoggingMixin):
# We should decrease our position # We should decrease our position
proposed_exit_rate = self.exchange.get_rate(pair, refresh=True, side="buy") proposed_exit_rate = self.exchange.get_rate(pair, refresh=True, side="buy")
self.execute_trade_exit(trade, proposed_exit_rate, sell_reason=SellCheckTuple( 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: def _check_depth_of_market_buy(self, pair: str, conf: Dict) -> bool:
""" """
@ -620,7 +620,7 @@ class FreqtradeBot(LoggingMixin):
# Updating wallets # Updating wallets
self.wallets.update() 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 pos_adjust:
if order_status == 'closed': if order_status == 'closed':
@ -677,7 +677,7 @@ class FreqtradeBot(LoggingMixin):
return enter_limit_requested, stake_amount return enter_limit_requested, stake_amount
def _notify_enter(self, trade: Trade, order: Dict, order_type: Optional[str] = None, 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. Sends rpc notification when a buy occurred.
""" """
@ -704,13 +704,13 @@ class FreqtradeBot(LoggingMixin):
'amount': safe_value_fallback(order, 'filled', 'amount') or trade.amount, 'amount': safe_value_fallback(order, 'filled', 'amount') or trade.amount,
'open_date': trade.open_date or datetime.utcnow(), 'open_date': trade.open_date or datetime.utcnow(),
'current_rate': current_rate, 'current_rate': current_rate,
'partial': partial, 'sub_trade': sub_trade,
} }
# Send the message # Send the message
self.rpc.send_msg(msg) 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. Sends rpc notification when a buy cancel occurred.
""" """
@ -731,6 +731,7 @@ class FreqtradeBot(LoggingMixin):
'open_date': trade.open_date, 'open_date': trade.open_date,
'current_rate': current_rate, 'current_rate': current_rate,
'reason': reason, 'reason': reason,
'sub_trade': sub_trade,
} }
# Send the message # Send the message
@ -1154,7 +1155,7 @@ class FreqtradeBot(LoggingMixin):
*, *,
exit_tag: Optional[str] = None, exit_tag: Optional[str] = None,
ordertype: Optional[str] = None, ordertype: Optional[str] = None,
partial: float = None, sub_trade_amt: float = None,
) -> bool: ) -> bool:
""" """
Executes a trade exit for the given trade and limit Executes a trade exit for the given trade and limit
@ -1192,7 +1193,7 @@ class FreqtradeBot(LoggingMixin):
# Emergency sells (default to market!) # Emergency sells (default to market!)
order_type = self.strategy.order_types.get("emergencysell", "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'] time_in_force = self.strategy.order_time_in_force['sell']
if not strategy_safe_wrapper(self.strategy.confirm_trade_exit, default_retval=True)( 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), self.strategy.lock_pair(trade.pair, datetime.now(timezone.utc),
reason='Auto lock') 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 # In case of market sell orders the order can be closed immediately
if order.get('status', 'unknown') in ('closed', 'expired'): 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() Trade.commit()
return True 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. Sends rpc notification when a sell occurred.
""" """
@ -1269,7 +1270,8 @@ class FreqtradeBot(LoggingMixin):
'stake_amount': trade.stake_amount, 'stake_amount': trade.stake_amount,
'stake_currency': self.config['stake_currency'], 'stake_currency': self.config['stake_currency'],
'fiat_currency': self.config.get('fiat_display_currency', None), '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: if 'fiat_display_currency' in self.config:
@ -1280,7 +1282,7 @@ class FreqtradeBot(LoggingMixin):
# Send the message # Send the message
self.rpc.send_msg(msg) 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. Sends rpc notification when a sell cancel occurred.
""" """
@ -1315,6 +1317,8 @@ class FreqtradeBot(LoggingMixin):
'stake_currency': self.config['stake_currency'], 'stake_currency': self.config['stake_currency'],
'fiat_currency': self.config.get('fiat_display_currency', None), 'fiat_currency': self.config.get('fiat_display_currency', None),
'reason': reason, 'reason': reason,
'sub_trade': sub_trade,
'stake_amount': trade.stake_amount,
} }
if 'fiat_display_currency' in self.config: 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, 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 Checks trades with open orders and updates the amount if necessary
Handles closing both buy and sell orders. Handles closing both buy and sell orders.
@ -1363,7 +1367,7 @@ class FreqtradeBot(LoggingMixin):
order = self.handle_order_fee(trade, order) order = self.handle_order_fee(trade, order)
trade.update(order) trade.update(order,sub_trade=sub_trade)
trade.recalc_trade_from_orders() trade.recalc_trade_from_orders()
Trade.commit() Trade.commit()

View File

@ -116,7 +116,7 @@ class Order(_DECL_BASE):
ft_order_side = Column(String(25), nullable=False) ft_order_side = Column(String(25), nullable=False)
ft_pair = Column(String(25), nullable=False) ft_pair = Column(String(25), nullable=False)
ft_is_open = Column(Boolean, nullable=False, default=True, index=True) 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) order_id = Column(String(255), nullable=False, index=True)
status = Column(String(255), nullable=True) status = Column(String(255), nullable=True)
symbol = Column(String(25), nullable=True) symbol = Column(String(25), nullable=True)
@ -481,9 +481,17 @@ class LocalTrade():
orders=(self.select_filled_orders('buy')) orders=(self.select_filled_orders('buy'))
lbuy=orders[-2] lbuy=orders[-2]
lamount = (lbuy.filled or lbuy.amount) 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() Order.query.session.commit()
self.orders.remove(orders[-1])
self.recalc_trade_from_orders() self.recalc_trade_from_orders()
Trade.commit() Trade.commit()
else: else:
@ -499,8 +507,11 @@ class LocalTrade():
raise ValueError(f'Unknown order type: {order_type}') raise ValueError(f'Unknown order type: {order_type}')
Trade.commit() Trade.commit()
def calc_profit2(self, open_rate: float, close_rate: float, amount: Float) ->float: 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) ) 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: def close(self, rate: float, *, show_msg: bool = True) -> None:
""" """
Sets close_rate to the given rate, calculates total profit Sets close_rate to the given rate, calculates total profit
@ -638,6 +649,7 @@ class LocalTrade():
for o in self.orders: for o in self.orders:
if (o.ft_is_open or if (o.ft_is_open or
(o.ft_order_side != 'buy') or (o.ft_order_side != 'buy') or
o.is_recovered==True or
(o.status not in NON_OPEN_EXCHANGE_STATES)): (o.status not in NON_OPEN_EXCHANGE_STATES)):
continue 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)) 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 and o.ft_is_open is False and
(o.filled or 0) > 0 and (o.filled or 0) > 0 and
o.is_recovered==False and
o.status in NON_OPEN_EXCHANGE_STATES] o.status in NON_OPEN_EXCHANGE_STATES]
@property @property

View File

@ -246,10 +246,10 @@ class Telegram(RPCHandler):
total, msg['stake_currency'], msg['fiat_currency']) total, msg['stake_currency'], msg['fiat_currency'])
else: else:
total_fiat = 0 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): 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 += ")`" message += ")`"
if msg.get('sub_trade'): if msg.get('sub_trade'):

View File

@ -1037,6 +1037,8 @@ def test_telegram_forcesell_handle(default_conf, update, ticker, fee,
'open_date': ANY, 'open_date': ANY,
'close_date': ANY, 'close_date': ANY,
'close_rate': ANY, 'close_rate': ANY,
'stake_amount': 0.0009999999999054,
'sub_trade': False
} == last_msg } == last_msg
@ -1102,6 +1104,8 @@ def test_telegram_forcesell_down_handle(default_conf, update, ticker, fee,
'open_date': ANY, 'open_date': ANY,
'close_date': ANY, 'close_date': ANY,
'close_rate': ANY, 'close_rate': ANY,
'stake_amount': 0.0009999999999054,
'sub_trade': False
} == last_msg } == last_msg
@ -1157,6 +1161,8 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None
'open_date': ANY, 'open_date': ANY,
'close_date': ANY, 'close_date': ANY,
'close_rate': ANY, 'close_rate': ANY,
'stake_amount': 0.0009999999999054,
'sub_trade': False
} == msg } == msg
@ -1734,7 +1740,7 @@ def test_send_msg_buy_notification(default_conf, mocker, caplog) -> None:
'pair': 'ETH/BTC', 'pair': 'ETH/BTC',
'limit': 1.099e-05, 'limit': 1.099e-05,
'order_type': 'limit', 'order_type': 'limit',
'stake_amount': 0.001, 'stake_amount': 0.01465333,
'stake_amount_fiat': 0.0, 'stake_amount_fiat': 0.0,
'stake_currency': 'BTC', 'stake_currency': 'BTC',
'fiat_currency': 'USD', 'fiat_currency': 'USD',
@ -1751,7 +1757,7 @@ def test_send_msg_buy_notification(default_conf, mocker, caplog) -> None:
'*Amount:* `1333.33333333`\n' \ '*Amount:* `1333.33333333`\n' \
'*Open Rate:* `0.00001099`\n' \ '*Open Rate:* `0.00001099`\n' \
'*Current 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'} freqtradebot.config['telegram']['notification_settings'] = {'buy': 'off'}
caplog.clear() caplog.clear()
@ -1825,7 +1831,7 @@ def test_send_msg_buy_fill_notification(default_conf, mocker) -> None:
'buy_tag': 'buy_signal_01', 'buy_tag': 'buy_signal_01',
'exchange': 'Binance', 'exchange': 'Binance',
'pair': 'ETH/BTC', 'pair': 'ETH/BTC',
'stake_amount': 0.001, 'stake_amount': 0.01465333,
# 'stake_amount_fiat': 0.0, # 'stake_amount_fiat': 0.0,
'stake_currency': 'BTC', 'stake_currency': 'BTC',
'fiat_currency': 'USD', 'fiat_currency': 'USD',
@ -1839,7 +1845,7 @@ def test_send_msg_buy_fill_notification(default_conf, mocker) -> None:
'*Buy Tag:* `buy_signal_01`\n' \ '*Buy Tag:* `buy_signal_01`\n' \
'*Amount:* `1333.33333333`\n' \ '*Amount:* `1333.33333333`\n' \
'*Open Rate:* `0.00001099`\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: 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', 'pair': 'ETH/BTC',
'limit': 1.099e-05, 'limit': 1.099e-05,
'order_type': 'limit', 'order_type': 'limit',
'stake_amount': 0.001, 'stake_amount': 0.01465333,
'stake_amount_fiat': 0.0, 'stake_amount_fiat': 0.0,
'stake_currency': 'BTC', 'stake_currency': 'BTC',
'fiat_currency': None, 'fiat_currency': None,
@ -2044,7 +2050,7 @@ def test_send_msg_buy_notification_no_fiat(default_conf, mocker) -> None:
'*Amount:* `1333.33333333`\n' '*Amount:* `1333.33333333`\n'
'*Open Rate:* `0.00001099`\n' '*Open Rate:* `0.00001099`\n'
'*Current 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: def test_send_msg_sell_notification_no_fiat(default_conf, mocker) -> None:

View File

@ -4320,7 +4320,6 @@ def test_position_adjust(mocker, default_conf_usdt, fee) -> None:
get_fee=fee, get_fee=fee,
) )
pair = 'ETH/USDT' pair = 'ETH/USDT'
# Initial buy # Initial buy
closed_successful_buy_order = { closed_successful_buy_order = {
'pair': pair, 'pair': pair,
@ -4385,7 +4384,7 @@ def test_position_adjust(mocker, default_conf_usdt, fee) -> None:
'status': None, 'status': None,
'price': 9, 'price': 9,
'amount': 12, 'amount': 12,
'cost': 100, 'cost': 108,
'ft_is_open': True, 'ft_is_open': True,
'id': '651', 'id': '651',
'order_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. # Make sure the closed order is found as the second order.
order = trade.select_order('buy', False) order = trade.select_order('buy', False)
assert order.order_id == '652' 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: def test_process_open_trade_positions_exception(mocker, default_conf_usdt, fee, caplog) -> None:
default_conf_usdt.update({ default_conf_usdt.update({