This commit is contained in:
Kavinkumar 2022-02-26 22:55:51 +05:30
parent 45faac95f6
commit 21ef84617c
3 changed files with 68 additions and 205 deletions

View File

@ -478,6 +478,15 @@ class FreqtradeBot(LoggingMixin):
if stake_amount is not None and stake_amount < 0.0: if stake_amount is not None and stake_amount < 0.0:
# We should decrease our position # We should decrease our position
#TODO : debug
open_sell_order=trade.select_order('sell', True)
if open_sell_order:
msg = {
'type': RPCMessageType.WARNING,
'status':'bug open_order_id is None'
}
self.rpc.send_msg(msg)
return
self.execute_trade_exit(trade, current_rate, sell_reason=SellCheckTuple( self.execute_trade_exit(trade, current_rate, sell_reason=SellCheckTuple(
sell_type=SellType.CUSTOM_SELL), sub_trade_amt=-stake_amount) sell_type=SellType.CUSTOM_SELL), sub_trade_amt=-stake_amount)
@ -1228,7 +1237,7 @@ 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, sub_trade=bool(sub_trade_amt)) self._notify_exit(trade, order_type, sub_trade=bool(sub_trade_amt), order=order)
# 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,
@ -1245,9 +1254,9 @@ class FreqtradeBot(LoggingMixin):
# Use cached rates here - it was updated seconds ago. # Use cached rates here - it was updated seconds ago.
current_rate = self.exchange.get_rate( current_rate = self.exchange.get_rate(
trade.pair, refresh=False, side="sell") if not fill else None trade.pair, refresh=False, side="sell") if not fill else None
if order: if sub_trade:
profit_rate = safe_value_fallback(order, 'average', 'price') amount = order.get('filled') or order.get('amount') or 0
amount = safe_value_fallback(order, 'filled', 'amount') profit_rate = order.get('average') or order.get('price') or 0
profit = trade.process_sell_sub_trade(order, is_closed=False) profit = trade.process_sell_sub_trade(order, is_closed=False)
open_rate = trade.get_open_rate(profit, profit_rate, amount) open_rate = trade.get_open_rate(profit, profit_rate, amount)
open_cost=open_rate * amount * (1+ trade.fee_open) open_cost=open_rate * amount * (1+ trade.fee_open)
@ -1377,7 +1386,7 @@ class FreqtradeBot(LoggingMixin):
order = self.handle_order_fee(trade, order) order = self.handle_order_fee(trade, order)
trade.update(order, sub_trade=sub_trade) trade.update(order)
trade.recalc_trade_from_orders() trade.recalc_trade_from_orders()
Trade.commit() Trade.commit()
@ -1390,9 +1399,10 @@ class FreqtradeBot(LoggingMixin):
if not trade.is_open: if not trade.is_open:
self.handle_protections(trade.pair) self.handle_protections(trade.pair)
sub_trade = order.get('filled') != trade.amount
if order.get('side', None) == 'sell': if order.get('side', None) == 'sell':
if send_msg and not stoploss_order and not trade.open_order_id: if send_msg and not stoploss_order and not trade.open_order_id:
self._notify_exit(trade, '', True, sub_trade=sub_trade) self._notify_exit(trade, '', True, sub_trade=sub_trade, order=order)
elif send_msg and not trade.open_order_id: elif send_msg and not trade.open_order_id:
# Buy fill # Buy fill
self._notify_enter(trade, order, fill=True, sub_trade=sub_trade) self._notify_enter(trade, order, fill=True, sub_trade=sub_trade)
@ -1486,6 +1496,13 @@ class FreqtradeBot(LoggingMixin):
if len(trades) == 0: if len(trades) == 0:
logger.info("Applying fee on amount for %s failed: myTrade-Dict empty found", trade) logger.info("Applying fee on amount for %s failed: myTrade-Dict empty found", trade)
msg = {
'type': RPCMessageType.WARNING,
'status': "fees bug"
}
self.rpc.send_msg(msg)
return order_amount return order_amount
fee_currency = None fee_currency = None
amount = 0 amount = 0
@ -1543,3 +1560,4 @@ class FreqtradeBot(LoggingMixin):
return max( return max(
min(valid_custom_price, max_custom_price_allowed), min(valid_custom_price, max_custom_price_allowed),
min_custom_price_allowed) min_custom_price_allowed)
5

View File

@ -453,7 +453,7 @@ class LocalTrade():
f"Trailing stoploss saved us: " f"Trailing stoploss saved us: "
f"{float(self.stop_loss) - float(self.initial_stop_loss):.8f}.") 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) -> None:
""" """
Updates this entity with amount and actual open/close rates. Updates this entity with amount and actual open/close rates.
:param order: order retrieved by exchange.fetch_order() :param order: order retrieved by exchange.fetch_order()
@ -478,12 +478,8 @@ class LocalTrade():
if self.is_open: if self.is_open:
logger.info(f'{order_type.upper()}_SELL has been fulfilled for {self}.') logger.info(f'{order_type.upper()}_SELL has been fulfilled for {self}.')
self.open_order_id = None self.open_order_id = None
if sub_trade or 1: self.process_sell_sub_trade(order)
logger.info(f'debug1:{sub_trade}') return
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'): elif order_type in ('stop_loss_limit', 'stop-loss', 'stop-loss-limit', 'stop'):
self.stoploss_order_id = None self.stoploss_order_id = None
self.close_rate_requested = self.stop_loss self.close_rate_requested = self.stop_loss
@ -497,14 +493,20 @@ class LocalTrade():
def process_sell_sub_trade(self, order: Dict, is_closed: bool = True) -> float: def process_sell_sub_trade(self, order: Dict, is_closed: bool = True) -> float:
orders = (self.select_filled_orders('buy')) orders = (self.select_filled_orders('buy'))
if len(orders)<1:
#Todo /test_freqtradebot.py::test_execute_trade_exit_market_order
self.close(0)
Trade.commit()
logger.info("debug:"*10);return 0
# is_closed = order['ft_is_open'] # is_closed = order['ft_is_open']
sell_amount = float(safe_value_fallback(order, 'filled', 'amount')) sell_amount = order.get('filled') or order.get('amount')
sell_rate = order.get('average') or order.get('price')
if is_closed: if is_closed:
if sell_amount == self.amount: if sell_amount == self.amount:
self.close(safe_value_fallback(order, 'average', 'price')) self.close(sell_rate)
Trade.commit() Trade.commit()
return return
sell_rate = float(safe_value_fallback(order, 'average', 'price'))
profit = 0.0 profit = 0.0
idx = -1 idx = -1
while sell_amount: while sell_amount:
@ -525,7 +527,7 @@ class LocalTrade():
profit += self.calc_profit2(buy_rate, sell_rate, amount) profit += self.calc_profit2(buy_rate, sell_rate, amount)
b_order2 = orders[idx] b_order2 = orders[idx]
amount2 = b_order2.filled or b_order2.amount amount2 = b_order2.filled or b_order2.amount
if is_closed : if is_closed:
b_order2.average = (b_order2.average * amount2 - profit) / amount2 b_order2.average = (b_order2.average * amount2 - profit) / amount2
self.update_order(b_order2) self.update_order(b_order2)
Order.query.session.commit() Order.query.session.commit()
@ -541,9 +543,9 @@ class LocalTrade():
def get_open_rate(self, profit: float, close_rate: float, def get_open_rate(self, profit: float, close_rate: float,
amount: float) -> float: amount: float) -> float:
return float(Decimal(amount) * return float((Decimal(amount) *
(Decimal(1 - self.fee_close) * Decimal(close_rate)) - (Decimal(1 - self.fee_close) * Decimal(close_rate)) -
profit)/(Decimal(amount) * Decimal(1 + self.fee_open)) Decimal(profit))/(Decimal(amount) * Decimal(1 + self.fee_open)))
def close(self, rate: float, *, show_msg: bool = True) -> None: def close(self, rate: float, *, show_msg: bool = True) -> None:
""" """
@ -671,12 +673,6 @@ class LocalTrade():
return float(f"{profit_ratio:.8f}") return float(f"{profit_ratio:.8f}")
def recalc_trade_from_orders(self): def recalc_trade_from_orders(self):
# We need at least 2 entry orders for averaging amounts and rates.
if len(self.select_filled_orders('buy')) < 2:
# Just in case, still recalc open trade value
self.recalc_open_trade_value()
return
total_amount = 0.0 total_amount = 0.0
total_stake = 0.0 total_stake = 0.0
for o in self.orders: for o in self.orders:
@ -693,7 +689,6 @@ class LocalTrade():
if tmp_amount > 0.0 and tmp_price is not None: if tmp_amount > 0.0 and tmp_price is not None:
total_amount += tmp_amount total_amount += tmp_amount
total_stake += tmp_price * tmp_amount total_stake += tmp_price * tmp_amount
if total_amount > 0: if total_amount > 0:
self.open_rate = total_stake / total_amount self.open_rate = total_stake / total_amount
self.stake_amount = total_stake self.stake_amount = total_stake

View File

@ -4574,19 +4574,20 @@ def test_position_adjust(mocker, default_conf_usdt, fee) -> None:
assert order.order_id == '653' assert order.order_id == '653'
def test_position_adjust2(mocker, default_conf_usdt, fee) -> None: def test_position_adjust2(mocker, default_conf_usdt, fee) -> None:
return
patch_RPCManager(mocker) patch_RPCManager(mocker)
patch_exchange(mocker) patch_exchange(mocker)
patch_wallet(mocker, free=10000) patch_wallet(mocker, free=10000)
default_conf_usdt.update({ default_conf_usdt.update({
"position_adjustment_enable": True, "position_adjustment_enable": True,
"dry_run": False, "dry_run": False,
"stake_amount": 10.0, "stake_amount": 200.0,
"dry_run_wallet": 1000.0, "dry_run_wallet": 1000.0,
}) })
freqtrade = FreqtradeBot(default_conf_usdt) freqtrade = FreqtradeBot(default_conf_usdt)
freqtrade.strategy.confirm_trade_entry = MagicMock(return_value=True) freqtrade.strategy.confirm_trade_entry = MagicMock(return_value=True)
bid = 11 bid = 11
stake_amount = 10 amount = 100
buy_rate_mock = MagicMock(return_value=bid) buy_rate_mock = MagicMock(return_value=bid)
mocker.patch.multiple( mocker.patch.multiple(
'freqtrade.exchange.Exchange', 'freqtrade.exchange.Exchange',
@ -4610,18 +4611,18 @@ def test_position_adjust2(mocker, default_conf_usdt, fee) -> None:
'status': 'closed', 'status': 'closed',
'price': bid, 'price': bid,
'average': bid, 'average': bid,
'cost': bid * stake_amount, 'cost': bid * amount,
'amount': stake_amount, 'amount': amount,
'filled': stake_amount, 'filled': amount,
'ft_is_open': False, 'ft_is_open': False,
'id': '650', 'id': '600',
'order_id': '650' 'order_id': '600'
} }
mocker.patch('freqtrade.exchange.Exchange.create_order', mocker.patch('freqtrade.exchange.Exchange.create_order',
MagicMock(return_value=closed_successful_buy_order)) MagicMock(return_value=closed_successful_buy_order))
mocker.patch('freqtrade.exchange.Exchange.fetch_order_or_stoploss_order', mocker.patch('freqtrade.exchange.Exchange.fetch_order_or_stoploss_order',
MagicMock(return_value=closed_successful_buy_order)) MagicMock(return_value=closed_successful_buy_order))
assert freqtrade.execute_entry(pair, stake_amount) assert freqtrade.execute_entry(pair, amount)
# Should create an closed trade with an no open order id # Should create an closed trade with an no open order id
# Order is filled and trade is open # Order is filled and trade is open
orders = Order.query.all() orders = Order.query.all()
@ -4632,7 +4633,7 @@ def test_position_adjust2(mocker, default_conf_usdt, fee) -> None:
assert trade.is_open is True assert trade.is_open is True
assert trade.open_order_id is None assert trade.open_order_id is None
assert trade.open_rate == 11 assert trade.open_rate == 11
assert trade.stake_amount == 110 assert trade.stake_amount == 1100
# Assume it does nothing since order is closed and trade is open # Assume it does nothing since order is closed and trade is open
freqtrade.update_closed_trades_without_assigned_fees() freqtrade.update_closed_trades_without_assigned_fees()
@ -4642,7 +4643,7 @@ def test_position_adjust2(mocker, default_conf_usdt, fee) -> None:
assert trade.is_open is True assert trade.is_open is True
assert trade.open_order_id is None assert trade.open_order_id is None
assert trade.open_rate == 11 assert trade.open_rate == 11
assert trade.stake_amount == 110 assert trade.stake_amount == 1100
assert not trade.fee_updated('buy') assert not trade.fee_updated('buy')
freqtrade.check_handle_timedout() freqtrade.check_handle_timedout()
@ -4652,165 +4653,10 @@ def test_position_adjust2(mocker, default_conf_usdt, fee) -> None:
assert trade.is_open is True assert trade.is_open is True
assert trade.open_order_id is None assert trade.open_order_id is None
assert trade.open_rate == 11 assert trade.open_rate == 11
assert trade.stake_amount == 110 assert trade.stake_amount == 1100
assert not trade.fee_updated('buy') assert not trade.fee_updated('buy')
# First position adjustment buy.
open_dca_order_1 = {
'ft_pair': pair,
'ft_order_side': 'buy',
'side': 'buy',
'type': 'limit',
'status': None,
'price': 9,
'amount': 12,
'cost': 108,
'ft_is_open': True,
'id': '651',
'order_id': '651'
}
mocker.patch('freqtrade.exchange.Exchange.create_order',
MagicMock(return_value=open_dca_order_1))
mocker.patch('freqtrade.exchange.Exchange.fetch_order_or_stoploss_order',
MagicMock(return_value=open_dca_order_1))
assert freqtrade.execute_entry(pair, stake_amount, trade=trade)
orders = Order.query.all()
assert orders
assert len(orders) == 2
trade = Trade.query.first()
assert trade
assert trade.open_order_id == '651'
assert trade.open_rate == 11
assert trade.amount == 10
assert trade.stake_amount == 110
assert not trade.fee_updated('buy')
trades: List[Trade] = Trade.get_open_trades_without_assigned_fees()
assert len(trades) == 1
assert trade.is_open
assert not trade.fee_updated('buy')
order = trade.select_order('buy', False)
assert order
assert order.order_id == '650'
def make_sure_its_651(*args, **kwargs):
if args[0] == '650':
return closed_successful_buy_order
if args[0] == '651':
return open_dca_order_1
return None
# Assume it does nothing since order is still open
fetch_order_mm = MagicMock(side_effect=make_sure_its_651)
mocker.patch('freqtrade.exchange.Exchange.create_order', fetch_order_mm)
mocker.patch('freqtrade.exchange.Exchange.fetch_order', fetch_order_mm)
mocker.patch('freqtrade.exchange.Exchange.fetch_order_or_stoploss_order', fetch_order_mm)
freqtrade.update_closed_trades_without_assigned_fees()
orders = Order.query.all()
assert orders
assert len(orders) == 2
# Assert that the trade is found as open and without fees
trades: List[Trade] = Trade.get_open_trades_without_assigned_fees()
assert len(trades) == 1
# Assert trade is as expected
trade = Trade.query.first()
assert trade
assert trade.open_order_id == '651'
assert trade.open_rate == 11
assert trade.amount == 10
assert trade.stake_amount == 110
assert not trade.fee_updated('buy')
# Make sure the closed order is found as the first order.
order = trade.select_order('buy', False)
assert order.order_id == '650'
# Now close the order so it should update.
closed_dca_order_1 = {
'ft_pair': pair,
'ft_order_side': 'buy',
'side': 'buy',
'type': 'limit',
'status': 'closed',
'price': 9,
'average': 9,
'amount': 12,
'filled': 12,
'cost': 108,
'ft_is_open': False,
'id': '651',
'order_id': '651'
}
mocker.patch('freqtrade.exchange.Exchange.create_order',
MagicMock(return_value=closed_dca_order_1))
mocker.patch('freqtrade.exchange.Exchange.fetch_order',
MagicMock(return_value=closed_dca_order_1))
mocker.patch('freqtrade.exchange.Exchange.fetch_order_or_stoploss_order',
MagicMock(return_value=closed_dca_order_1))
freqtrade.check_handle_timedout()
# Assert trade is as expected (averaged dca)
trade = Trade.query.first()
assert trade
assert trade.open_order_id is None
assert pytest.approx(trade.open_rate) == 9.90909090909
assert trade.amount == 22
assert trade.stake_amount == 218
orders = Order.query.all()
assert orders
assert len(orders) == 2
# Make sure the closed order is found as the second order.
order = trade.select_order('buy', False)
assert order.order_id == '651'
# Assert that the trade is not found as open and without fees
trades: List[Trade] = Trade.get_open_trades_without_assigned_fees()
assert len(trades) == 1
# Add a second DCA
closed_dca_order_2 = {
'ft_pair': pair,
'status': 'closed',
'ft_order_side': 'buy',
'side': 'buy',
'type': 'limit',
'price': 7,
'average': 7,
'amount': 15,
'filled': 15,
'cost': 105,
'ft_is_open': False,
'id': '652',
'order_id': '652'
}
mocker.patch('freqtrade.exchange.Exchange.create_order',
MagicMock(return_value=closed_dca_order_2))
mocker.patch('freqtrade.exchange.Exchange.fetch_order',
MagicMock(return_value=closed_dca_order_2))
mocker.patch('freqtrade.exchange.Exchange.fetch_order_or_stoploss_order',
MagicMock(return_value=closed_dca_order_2))
assert freqtrade.execute_entry(pair, stake_amount, trade=trade)
# Assert trade is as expected (averaged dca)
trade = Trade.query.first()
assert trade
assert trade.open_order_id is None
assert pytest.approx(trade.open_rate) == 8.729729729729
assert trade.amount == 37
assert trade.stake_amount == 323
orders = Order.query.all()
assert orders
assert len(orders) == 3
# 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 = { closed_sell_dca_order_1 = {
'ft_pair': pair, 'ft_pair': pair,
'status': 'closed', 'status': 'closed',
@ -4819,12 +4665,12 @@ def test_position_adjust2(mocker, default_conf_usdt, fee) -> None:
'type': 'limit', 'type': 'limit',
'price': 8, 'price': 8,
'average': 8, 'average': 8,
'amount': 15, 'amount': 50,
'filled': 15, 'filled': 50,
'cost': 120, 'cost': 120,
'ft_is_open': False, 'ft_is_open': False,
'id': '653', 'id': '601',
'order_id': '653' 'order_id': '601'
} }
mocker.patch('freqtrade.exchange.Exchange.create_order', mocker.patch('freqtrade.exchange.Exchange.create_order',
MagicMock(return_value=closed_sell_dca_order_1)) MagicMock(return_value=closed_sell_dca_order_1))
@ -4834,23 +4680,27 @@ def test_position_adjust2(mocker, default_conf_usdt, fee) -> None:
MagicMock(return_value=closed_sell_dca_order_1)) MagicMock(return_value=closed_sell_dca_order_1))
assert freqtrade.execute_trade_exit(trade=trade, limit=8, assert freqtrade.execute_trade_exit(trade=trade, limit=8,
sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS), sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS),
sub_trade_amt=15) sub_trade_amt=50)
trades: List[Trade] = trade.get_open_trades_without_assigned_fees()
assert len(trades) == 1
# Assert trade is as expected (averaged dca) # Assert trade is as expected (averaged dca)
trade = Trade.query.first() trade = Trade.query.first()
print (trade.amount, trade.open_rate, trade.stake_amount, trade.close_rate, trade.close_profit_abs)
assert trade assert trade
assert trade.open_order_id is None assert trade.open_order_id is None
assert trade.amount == 22 assert trade.amount == 100
assert trade.stake_amount == 203.5625 assert trade.stake_amount == 1100
assert pytest.approx(trade.open_rate) == 9.252840909090908 assert pytest.approx(trade.open_rate) == 11.0
orders = Order.query.all() orders = Order.query.all()
assert orders assert orders
assert len(orders) == 4 assert len(orders) == 2
print(vars(orders[0]))
print(vars(orders[1]))
print(vars(trade))
# 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('sell', False) order = trade.select_order('sell', False)
assert order.order_id == '653' assert order.order_id == '601'
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({