mirror of
https://github.com/freqtrade/freqtrade.git
synced 2024-11-10 10:21:59 +00:00
Add max-slippage limiting for dry-run orders to avoid insane market order fills
This commit is contained in:
parent
0b6aedbc4c
commit
61c076563f
|
@ -618,6 +618,8 @@ class Exchange:
|
|||
if self.exchange_has('fetchL2OrderBook'):
|
||||
ob = self.fetch_l2_order_book(pair, 20)
|
||||
ob_type = 'asks' if side == 'buy' else 'bids'
|
||||
slippage = 0.05
|
||||
max_slippage_val = rate * ((1 + slippage) if side == 'buy' else (1 - slippage))
|
||||
|
||||
remaining_amount = amount
|
||||
filled_amount = 0
|
||||
|
@ -626,7 +628,9 @@ class Exchange:
|
|||
book_entry_coin_volume = book_entry[1]
|
||||
if remaining_amount > 0:
|
||||
if remaining_amount < book_entry_coin_volume:
|
||||
# Orderbook at this slot bigger than remaining amount
|
||||
filled_amount += remaining_amount * book_entry_price
|
||||
break
|
||||
else:
|
||||
filled_amount += book_entry_coin_volume * book_entry_price
|
||||
remaining_amount -= book_entry_coin_volume
|
||||
|
@ -635,7 +639,14 @@ class Exchange:
|
|||
else:
|
||||
# If remaining_amount wasn't consumed completely (break was not called)
|
||||
filled_amount += remaining_amount * book_entry_price
|
||||
forecast_avg_filled_price = filled_amount / amount
|
||||
forecast_avg_filled_price = max(filled_amount, 0) / amount
|
||||
# Limit max. slippage to specified value
|
||||
if side == 'buy':
|
||||
forecast_avg_filled_price = min(forecast_avg_filled_price, max_slippage_val)
|
||||
|
||||
else:
|
||||
forecast_avg_filled_price = max(forecast_avg_filled_price, max_slippage_val)
|
||||
|
||||
return self.price_to_precision(pair, forecast_avg_filled_price)
|
||||
|
||||
return rate
|
||||
|
|
|
@ -984,16 +984,21 @@ def test_create_dry_run_order_limit_fill(default_conf, mocker, side, startprice,
|
|||
assert order['fee']
|
||||
|
||||
|
||||
@pytest.mark.parametrize("side,amount,endprice", [
|
||||
("buy", 1, 25.566),
|
||||
("buy", 100, 25.5672), # Requires interpolation
|
||||
("buy", 1000, 25.575), # More than orderbook return
|
||||
("sell", 1, 25.563),
|
||||
("sell", 100, 25.5625), # Requires interpolation
|
||||
("sell", 1000, 25.5555), # More than orderbook return
|
||||
@pytest.mark.parametrize("side,rate,amount,endprice", [
|
||||
# spread is 25.263-25.266
|
||||
("buy", 25.564, 1, 25.566),
|
||||
("buy", 25.564, 100, 25.5672), # Requires interpolation
|
||||
("buy", 25.590, 100, 25.5672), # Price above spread ... average is lower
|
||||
("buy", 25.564, 1000, 25.575), # More than orderbook return
|
||||
("buy", 24.000, 100000, 25.200), # Run into max_slippage of 5%
|
||||
("sell", 25.564, 1, 25.563),
|
||||
("sell", 25.564, 100, 25.5625), # Requires interpolation
|
||||
("sell", 25.510, 100, 25.5625), # price below spread - average is higher
|
||||
("sell", 25.564, 1000, 25.5555), # More than orderbook return
|
||||
("sell", 27, 10000, 25.65), # max-slippage 5%
|
||||
])
|
||||
@pytest.mark.parametrize("exchange_name", EXCHANGES)
|
||||
def test_create_dry_run_order_market_fill(default_conf, mocker, side, amount, endprice,
|
||||
def test_create_dry_run_order_market_fill(default_conf, mocker, side, rate, amount, endprice,
|
||||
exchange_name, order_book_l2_usd):
|
||||
default_conf['dry_run'] = True
|
||||
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
|
||||
|
@ -1003,7 +1008,7 @@ def test_create_dry_run_order_market_fill(default_conf, mocker, side, amount, en
|
|||
)
|
||||
|
||||
order = exchange.create_dry_run_order(
|
||||
pair='LTC/USDT', ordertype='market', side=side, amount=amount, rate=25.5)
|
||||
pair='LTC/USDT', ordertype='market', side=side, amount=amount, rate=rate)
|
||||
assert 'id' in order
|
||||
assert f'dry_run_{side}_' in order["id"]
|
||||
assert order["side"] == side
|
||||
|
|
Loading…
Reference in New Issue
Block a user