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:
# 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(
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),
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
if order.get('status', 'unknown') in ('closed', 'expired'):
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.
current_rate = self.exchange.get_rate(
trade.pair, refresh=False, side="sell") if not fill else None
if order:
profit_rate = safe_value_fallback(order, 'average', 'price')
amount = safe_value_fallback(order, 'filled', 'amount')
if sub_trade:
amount = order.get('filled') or order.get('amount') or 0
profit_rate = order.get('average') or order.get('price') or 0
profit = trade.process_sell_sub_trade(order, is_closed=False)
open_rate = trade.get_open_rate(profit, profit_rate, amount)
open_cost=open_rate * amount * (1+ trade.fee_open)
@ -1377,7 +1386,7 @@ class FreqtradeBot(LoggingMixin):
order = self.handle_order_fee(trade, order)
trade.update(order, sub_trade=sub_trade)
trade.update(order)
trade.recalc_trade_from_orders()
Trade.commit()
@ -1390,9 +1399,10 @@ class FreqtradeBot(LoggingMixin):
if not trade.is_open:
self.handle_protections(trade.pair)
sub_trade = order.get('filled') != trade.amount
if order.get('side', None) == 'sell':
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:
# Buy fill
self._notify_enter(trade, order, fill=True, sub_trade=sub_trade)
@ -1486,6 +1496,13 @@ class FreqtradeBot(LoggingMixin):
if len(trades) == 0:
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
fee_currency = None
amount = 0
@ -1543,3 +1560,4 @@ class FreqtradeBot(LoggingMixin):
return max(
min(valid_custom_price, max_custom_price_allowed),
min_custom_price_allowed)
5

View File

@ -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) -> None:
"""
Updates this entity with amount and actual open/close rates.
:param order: order retrieved by exchange.fetch_order()
@ -478,12 +478,8 @@ class LocalTrade():
if self.is_open:
logger.info(f'{order_type.upper()}_SELL has been fulfilled for {self}.')
self.open_order_id = None
if sub_trade or 1:
logger.info(f'debug1:{sub_trade}')
self.process_sell_sub_trade(order)
return
# else:
# self.close(safe_value_fallback(order, 'average', 'price'))
self.process_sell_sub_trade(order)
return
elif order_type in ('stop_loss_limit', 'stop-loss', 'stop-loss-limit', 'stop'):
self.stoploss_order_id = None
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:
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']
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 sell_amount == self.amount:
self.close(safe_value_fallback(order, 'average', 'price'))
self.close(sell_rate)
Trade.commit()
return
sell_rate = float(safe_value_fallback(order, 'average', 'price'))
profit = 0.0
idx = -1
while sell_amount:
@ -525,7 +527,7 @@ class LocalTrade():
profit += self.calc_profit2(buy_rate, sell_rate, amount)
b_order2 = orders[idx]
amount2 = b_order2.filled or b_order2.amount
if is_closed :
if is_closed:
b_order2.average = (b_order2.average * amount2 - profit) / amount2
self.update_order(b_order2)
Order.query.session.commit()
@ -541,9 +543,9 @@ class LocalTrade():
def get_open_rate(self, profit: float, close_rate: float,
amount: float) -> float:
return float(Decimal(amount) *
return float((Decimal(amount) *
(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:
"""
@ -671,12 +673,6 @@ class LocalTrade():
return float(f"{profit_ratio:.8f}")
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_stake = 0.0
for o in self.orders:
@ -693,7 +689,6 @@ class LocalTrade():
if tmp_amount > 0.0 and tmp_price is not None:
total_amount += tmp_amount
total_stake += tmp_price * tmp_amount
if total_amount > 0:
self.open_rate = total_stake / total_amount
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'
def test_position_adjust2(mocker, default_conf_usdt, fee) -> None:
return
patch_RPCManager(mocker)
patch_exchange(mocker)
patch_wallet(mocker, free=10000)
default_conf_usdt.update({
"position_adjustment_enable": True,
"dry_run": False,
"stake_amount": 10.0,
"stake_amount": 200.0,
"dry_run_wallet": 1000.0,
})
freqtrade = FreqtradeBot(default_conf_usdt)
freqtrade.strategy.confirm_trade_entry = MagicMock(return_value=True)
bid = 11
stake_amount = 10
amount = 100
buy_rate_mock = MagicMock(return_value=bid)
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
@ -4610,18 +4611,18 @@ def test_position_adjust2(mocker, default_conf_usdt, fee) -> None:
'status': 'closed',
'price': bid,
'average': bid,
'cost': bid * stake_amount,
'amount': stake_amount,
'filled': stake_amount,
'cost': bid * amount,
'amount': amount,
'filled': amount,
'ft_is_open': False,
'id': '650',
'order_id': '650'
'id': '600',
'order_id': '600'
}
mocker.patch('freqtrade.exchange.Exchange.create_order',
MagicMock(return_value=closed_successful_buy_order))
mocker.patch('freqtrade.exchange.Exchange.fetch_order_or_stoploss_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
# Order is filled and trade is open
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.open_order_id is None
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
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.open_order_id is None
assert trade.open_rate == 11
assert trade.stake_amount == 110
assert trade.stake_amount == 1100
assert not trade.fee_updated('buy')
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.open_order_id is None
assert trade.open_rate == 11
assert trade.stake_amount == 110
assert trade.stake_amount == 1100
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 = {
'ft_pair': pair,
'status': 'closed',
@ -4819,12 +4665,12 @@ def test_position_adjust2(mocker, default_conf_usdt, fee) -> None:
'type': 'limit',
'price': 8,
'average': 8,
'amount': 15,
'filled': 15,
'amount': 50,
'filled': 50,
'cost': 120,
'ft_is_open': False,
'id': '653',
'order_id': '653'
'id': '601',
'order_id': '601'
}
mocker.patch('freqtrade.exchange.Exchange.create_order',
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))
assert freqtrade.execute_trade_exit(trade=trade, limit=8,
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)
trade = Trade.query.first()
print (trade.amount, trade.open_rate, trade.stake_amount, trade.close_rate, trade.close_profit_abs)
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
assert trade.amount == 100
assert trade.stake_amount == 1100
assert pytest.approx(trade.open_rate) == 11.0
orders = Order.query.all()
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.
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:
default_conf_usdt.update({