Merge pull request #2652 from freqtrade/safe_sell_amount

Safe sell amount
This commit is contained in:
hroff-1902 2019-12-29 00:09:21 +03:00 committed by GitHub
commit 433fd2a7c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 109 additions and 4 deletions

View File

@ -917,6 +917,27 @@ class FreqtradeBot:
# TODO: figure out how to handle partially complete sell orders
return False
def _safe_sell_amount(self, pair: str, amount: float) -> float:
"""
Get sellable amount.
Should be trade.amount - but will fall back to the available amount if necessary.
This should cover cases where get_real_amount() was not able to update the amount
for whatever reason.
:param pair: Pair we're trying to sell
:param amount: amount we expect to be available
:return: amount to sell
:raise: DependencyException: if available balance is not within 2% of the available amount.
"""
wallet_amount = self.wallets.get_free(pair.split('/')[0])
logger.debug(f"{pair} - Wallet: {wallet_amount} - Trade-amount: {amount}")
if wallet_amount > amount:
return amount
elif wallet_amount > amount * 0.98:
logger.info(f"{pair} - Falling back to wallet-amount.")
return wallet_amount
else:
raise DependencyException("Not enough amount to sell.")
def execute_sell(self, trade: Trade, limit: float, sell_reason: SellType) -> None:
"""
Executes a limit sell for the given trade and limit
@ -947,10 +968,12 @@ class FreqtradeBot:
# Emergencysells (default to market!)
ordertype = self.strategy.order_types.get("emergencysell", "market")
amount = self._safe_sell_amount(trade.pair, trade.amount)
# Execute sell and update trade record
order = self.exchange.sell(pair=str(trade.pair),
ordertype=ordertype,
amount=trade.amount, rate=limit,
amount=amount, rate=limit,
time_in_force=self.strategy.order_time_in_force['sell']
)

View File

@ -887,7 +887,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order) -> None:
'freqtrade.freqtradebot.FreqtradeBot',
get_target_bid=get_bid,
_get_min_pair_stake_amount=MagicMock(return_value=1)
)
)
buy_mm = MagicMock(return_value={'id': limit_buy_order['id']})
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
@ -1682,6 +1682,7 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order, fee, mock
time.sleep(0.01) # Race condition fix
trade.update(limit_buy_order)
assert trade.is_open is True
freqtrade.wallets.update()
patch_get_signal(freqtrade, value=(False, True))
assert freqtrade.handle_trade(trade) is True
@ -2326,6 +2327,7 @@ def test_execute_sell_down_stoploss_on_exchange_dry_run(default_conf, ticker, fe
def test_execute_sell_sloe_cancel_exception(mocker, default_conf, ticker, fee, caplog) -> None:
freqtrade = get_patched_freqtradebot(mocker, default_conf)
mocker.patch('freqtrade.exchange.Exchange.cancel_order', side_effect=InvalidOrderException())
mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(return_value=300))
sellmock = MagicMock()
patch_exchange(mocker)
mocker.patch.multiple(
@ -2548,6 +2550,7 @@ def test_sell_profit_only_enable_profit(default_conf, limit_buy_order,
trade = Trade.query.first()
trade.update(limit_buy_order)
freqtrade.wallets.update()
patch_get_signal(freqtrade, value=(False, True))
assert freqtrade.handle_trade(trade) is True
assert trade.sell_reason == SellType.SELL_SIGNAL.value
@ -2578,6 +2581,7 @@ def test_sell_profit_only_disable_profit(default_conf, limit_buy_order,
trade = Trade.query.first()
trade.update(limit_buy_order)
freqtrade.wallets.update()
patch_get_signal(freqtrade, value=(False, True))
assert freqtrade.handle_trade(trade) is True
assert trade.sell_reason == SellType.SELL_SIGNAL.value
@ -2603,7 +2607,7 @@ def test_sell_profit_only_enable_loss(default_conf, limit_buy_order, fee, mocker
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
freqtrade.strategy.stop_loss_reached = MagicMock(return_value=SellCheckTuple(
sell_flag=False, sell_type=SellType.NONE))
sell_flag=False, sell_type=SellType.NONE))
freqtrade.create_trades()
trade = Trade.query.first()
@ -2638,11 +2642,87 @@ def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, fee, mocke
trade = Trade.query.first()
trade.update(limit_buy_order)
freqtrade.wallets.update()
patch_get_signal(freqtrade, value=(False, True))
assert freqtrade.handle_trade(trade) is True
assert trade.sell_reason == SellType.SELL_SIGNAL.value
def test_sell_not_enough_balance(default_conf, limit_buy_order,
fee, mocker, caplog) -> None:
patch_RPCManager(mocker)
patch_exchange(mocker)
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
fetch_ticker=MagicMock(return_value={
'bid': 0.00002172,
'ask': 0.00002173,
'last': 0.00002172
}),
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_fee=fee,
)
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
freqtrade.create_trades()
trade = Trade.query.first()
amnt = trade.amount
trade.update(limit_buy_order)
patch_get_signal(freqtrade, value=(False, True))
mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(return_value=trade.amount * 0.985))
assert freqtrade.handle_trade(trade) is True
assert log_has_re(r'.*Falling back to wallet-amount.', caplog)
assert trade.amount != amnt
def test__safe_sell_amount(default_conf, fee, caplog, mocker):
patch_RPCManager(mocker)
patch_exchange(mocker)
amount = 95.33
amount_wallet = 95.29
mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(return_value=amount_wallet))
trade = Trade(
pair='LTC/ETH',
amount=amount,
exchange='binance',
open_rate=0.245441,
open_order_id="123456",
fee_open=fee.return_value,
fee_close=fee.return_value,
)
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
assert freqtrade._safe_sell_amount(trade.pair, trade.amount) == amount_wallet
assert log_has_re(r'.*Falling back to wallet-amount.', caplog)
def test__safe_sell_amount_error(default_conf, fee, caplog, mocker):
patch_RPCManager(mocker)
patch_exchange(mocker)
amount = 95.33
amount_wallet = 91.29
mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(return_value=amount_wallet))
trade = Trade(
pair='LTC/ETH',
amount=amount,
exchange='binance',
open_rate=0.245441,
open_order_id="123456",
fee_open=fee.return_value,
fee_close=fee.return_value,
)
freqtrade = FreqtradeBot(default_conf)
patch_get_signal(freqtrade)
with pytest.raises(DependencyException, match=r"Not enough amount to sell."):
assert freqtrade._safe_sell_amount(trade.pair, trade.amount)
def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down, mocker, caplog) -> None:
patch_RPCManager(mocker)
patch_exchange(mocker)
@ -2703,6 +2783,7 @@ def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order, fee, mocker) ->
trade = Trade.query.first()
trade.update(limit_buy_order)
freqtrade.wallets.update()
patch_get_signal(freqtrade, value=(True, True))
assert freqtrade.handle_trade(trade) is False
@ -3440,6 +3521,7 @@ def test_order_book_ask_strategy(default_conf, limit_buy_order, limit_sell_order
time.sleep(0.01) # Race condition fix
trade.update(limit_buy_order)
freqtrade.wallets.update()
assert trade.is_open is True
patch_get_signal(freqtrade, value=(False, True))

View File

@ -71,7 +71,7 @@ def test_may_execute_sell_stoploss_on_exchange_multi(default_conf, ticker, fee,
)
mocker.patch("freqtrade.strategy.interface.IStrategy.should_sell", should_sell_mock)
wallets_mock = mocker.patch("freqtrade.wallets.Wallets.update", MagicMock())
mocker.patch("freqtrade.wallets.Wallets.get_free", MagicMock(return_value=1))
mocker.patch("freqtrade.wallets.Wallets.get_free", MagicMock(return_value=1000))
freqtrade = get_patched_freqtradebot(mocker, default_conf)
freqtrade.strategy.order_types['stoploss_on_exchange'] = True