parent
f4e71c1f14
commit
43d7f9ac67
@ -62,12 +62,13 @@ Mandatory parameters are marked as **Required**, which means that they are requi
|
|||||||
| `unfilledtimeout.buy` | **Required.** How long (in minutes) the bot will wait for an unfilled buy order to complete, after which the order will be cancelled and repeated at current (new) price, as long as there is a signal. [Strategy Override](#parameters-in-the-strategy).<br> **Datatype:** Integer
|
| `unfilledtimeout.buy` | **Required.** How long (in minutes) the bot will wait for an unfilled buy order to complete, after which the order will be cancelled and repeated at current (new) price, as long as there is a signal. [Strategy Override](#parameters-in-the-strategy).<br> **Datatype:** Integer
|
||||||
| `unfilledtimeout.sell` | **Required.** How long (in minutes) the bot will wait for an unfilled sell order to complete, after which the order will be cancelled and repeated at current (new) price, as long as there is a signal. [Strategy Override](#parameters-in-the-strategy).<br> **Datatype:** Integer
|
| `unfilledtimeout.sell` | **Required.** How long (in minutes) the bot will wait for an unfilled sell order to complete, after which the order will be cancelled and repeated at current (new) price, as long as there is a signal. [Strategy Override](#parameters-in-the-strategy).<br> **Datatype:** Integer
|
||||||
| `bid_strategy.price_side` | Select the side of the spread the bot should look at to get the buy rate. [More information below](#buy-price-side).<br> *Defaults to `bid`.* <br> **Datatype:** String (either `ask` or `bid`).
|
| `bid_strategy.price_side` | Select the side of the spread the bot should look at to get the buy rate. [More information below](#buy-price-side).<br> *Defaults to `bid`.* <br> **Datatype:** String (either `ask` or `bid`).
|
||||||
| `bid_strategy.ask_last_balance` | **Required.** Set the bidding price. More information [below](#buy-price-without-orderbook-enabled).
|
| `bid_strategy.ask_last_balance` | **Required.** Interpolate the bidding price. More information [below](#buy-price-without-orderbook-enabled).
|
||||||
| `bid_strategy.use_order_book` | Enable buying using the rates in [Order Book Bids](#buy-price-with-orderbook-enabled). <br> **Datatype:** Boolean
|
| `bid_strategy.use_order_book` | Enable buying using the rates in [Order Book Bids](#buy-price-with-orderbook-enabled). <br> **Datatype:** Boolean
|
||||||
| `bid_strategy.order_book_top` | Bot will use the top N rate in Order Book Bids to buy. I.e. a value of 2 will allow the bot to pick the 2nd bid rate in [Order Book Bids](#buy-price-with-orderbook-enabled). <br>*Defaults to `1`.* <br> **Datatype:** Positive Integer
|
| `bid_strategy.order_book_top` | Bot will use the top N rate in Order Book Bids to buy. I.e. a value of 2 will allow the bot to pick the 2nd bid rate in [Order Book Bids](#buy-price-with-orderbook-enabled). <br>*Defaults to `1`.* <br> **Datatype:** Positive Integer
|
||||||
| `bid_strategy. check_depth_of_market.enabled` | Do not buy if the difference of buy orders and sell orders is met in Order Book. [Check market depth](#check-depth-of-market). <br>*Defaults to `false`.* <br> **Datatype:** Boolean
|
| `bid_strategy. check_depth_of_market.enabled` | Do not buy if the difference of buy orders and sell orders is met in Order Book. [Check market depth](#check-depth-of-market). <br>*Defaults to `false`.* <br> **Datatype:** Boolean
|
||||||
| `bid_strategy. check_depth_of_market.bids_to_ask_delta` | The difference ratio of buy orders and sell orders found in Order Book. A value below 1 means sell order size is greater, while value greater than 1 means buy order size is higher. [Check market depth](#check-depth-of-market) <br> *Defaults to `0`.* <br> **Datatype:** Float (as ratio)
|
| `bid_strategy. check_depth_of_market.bids_to_ask_delta` | The difference ratio of buy orders and sell orders found in Order Book. A value below 1 means sell order size is greater, while value greater than 1 means buy order size is higher. [Check market depth](#check-depth-of-market) <br> *Defaults to `0`.* <br> **Datatype:** Float (as ratio)
|
||||||
| `ask_strategy.price_side` | Select the side of the spread the bot should look at to get the sell rate. [More information below](#sell-price-side).<br> *Defaults to `ask`.* <br> **Datatype:** String (either `ask` or `bid`).
|
| `ask_strategy.price_side` | Select the side of the spread the bot should look at to get the sell rate. [More information below](#sell-price-side).<br> *Defaults to `ask`.* <br> **Datatype:** String (either `ask` or `bid`).
|
||||||
|
| `ask_strategy.bid_last_balance` | Interpolate the selling price. More information [below](#sell-price-without-orderbook-enabled).
|
||||||
| `ask_strategy.use_order_book` | Enable selling of open trades using [Order Book Asks](#sell-price-with-orderbook-enabled). <br> **Datatype:** Boolean
|
| `ask_strategy.use_order_book` | Enable selling of open trades using [Order Book Asks](#sell-price-with-orderbook-enabled). <br> **Datatype:** Boolean
|
||||||
| `ask_strategy.order_book_min` | Bot will scan from the top min to max Order Book Asks searching for a profitable rate. <br>*Defaults to `1`.* <br> **Datatype:** Positive Integer
|
| `ask_strategy.order_book_min` | Bot will scan from the top min to max Order Book Asks searching for a profitable rate. <br>*Defaults to `1`.* <br> **Datatype:** Positive Integer
|
||||||
| `ask_strategy.order_book_max` | Bot will scan from the top min to max Order Book Asks searching for a profitable rate. <br>*Defaults to `1`.* <br> **Datatype:** Positive Integer
|
| `ask_strategy.order_book_max` | Bot will scan from the top min to max Order Book Asks searching for a profitable rate. <br>*Defaults to `1`.* <br> **Datatype:** Positive Integer
|
||||||
|
@ -103,6 +103,10 @@ A fixed slot (mirroring `bid_strategy.order_book_top`) can be defined by setting
|
|||||||
|
|
||||||
When not using orderbook (`ask_strategy.use_order_book=False`), the price at the `ask_strategy.price_side` side (defaults to `"ask"`) from the ticker will be used as the sell price.
|
When not using orderbook (`ask_strategy.use_order_book=False`), the price at the `ask_strategy.price_side` side (defaults to `"ask"`) from the ticker will be used as the sell price.
|
||||||
|
|
||||||
|
When not using orderbook (`ask_strategy.use_order_book=False`), Freqtrade uses the best `side` price from the ticker if it's below the `last` traded price from the ticker. Otherwise (when the `side` price is above the `last` price), it calculates a rate between `side` and `last` price.
|
||||||
|
|
||||||
|
The `ask_strategy.bid_last_balance` configuration parameter controls this. A value of `0.0` will use `side` price, while `1.0` will use the last price and values between those interpolate between `side` and last price.
|
||||||
|
|
||||||
### Market order pricing
|
### Market order pricing
|
||||||
|
|
||||||
When using market orders, prices should be configured to use the "correct" side of the orderbook to allow realistic pricing detection.
|
When using market orders, prices should be configured to use the "correct" side of the orderbook to allow realistic pricing detection.
|
||||||
|
@ -165,6 +165,12 @@ CONF_SCHEMA = {
|
|||||||
'type': 'object',
|
'type': 'object',
|
||||||
'properties': {
|
'properties': {
|
||||||
'price_side': {'type': 'string', 'enum': ORDERBOOK_SIDES, 'default': 'ask'},
|
'price_side': {'type': 'string', 'enum': ORDERBOOK_SIDES, 'default': 'ask'},
|
||||||
|
'bid_last_balance': {
|
||||||
|
'type': 'number',
|
||||||
|
'minimum': 0,
|
||||||
|
'maximum': 1,
|
||||||
|
'exclusiveMaximum': False,
|
||||||
|
},
|
||||||
'use_order_book': {'type': 'boolean'},
|
'use_order_book': {'type': 'boolean'},
|
||||||
'order_book_min': {'type': 'integer', 'minimum': 1},
|
'order_book_min': {'type': 'integer', 'minimum': 1},
|
||||||
'order_book_max': {'type': 'integer', 'minimum': 1, 'maximum': 50},
|
'order_book_max': {'type': 'integer', 'minimum': 1, 'maximum': 50},
|
||||||
|
@ -432,7 +432,7 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
ticker = self.exchange.fetch_ticker(pair)
|
ticker = self.exchange.fetch_ticker(pair)
|
||||||
ticker_rate = ticker[bid_strategy['price_side']]
|
ticker_rate = ticker[bid_strategy['price_side']]
|
||||||
if ticker['last'] and ticker_rate > ticker['last']:
|
if ticker['last'] and ticker_rate > ticker['last']:
|
||||||
balance = self.config['bid_strategy']['ask_last_balance']
|
balance = bid_strategy['ask_last_balance']
|
||||||
ticker_rate = ticker_rate + balance * (ticker['last'] - ticker_rate)
|
ticker_rate = ticker_rate + balance * (ticker['last'] - ticker_rate)
|
||||||
used_rate = ticker_rate
|
used_rate = ticker_rate
|
||||||
|
|
||||||
@ -745,7 +745,13 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
logger.warning("Sell Price at location from orderbook could not be determined.")
|
logger.warning("Sell Price at location from orderbook could not be determined.")
|
||||||
raise PricingError from e
|
raise PricingError from e
|
||||||
else:
|
else:
|
||||||
rate = self.exchange.fetch_ticker(pair)[ask_strategy['price_side']]
|
ticker = self.exchange.fetch_ticker(pair)
|
||||||
|
ticker_rate = ticker[ask_strategy['price_side']]
|
||||||
|
if ticker['last'] and ticker_rate < ticker['last']:
|
||||||
|
balance = ask_strategy.get('bid_last_balance', 0.0)
|
||||||
|
ticker_rate = ticker_rate - balance * (ticker_rate - ticker['last'])
|
||||||
|
rate = ticker_rate
|
||||||
|
|
||||||
if rate is None:
|
if rate is None:
|
||||||
raise PricingError(f"Sell-Rate for {pair} was empty.")
|
raise PricingError(f"Sell-Rate for {pair} was empty.")
|
||||||
self._sell_rate_cache[pair] = rate
|
self._sell_rate_cache[pair] = rate
|
||||||
|
@ -4112,22 +4112,33 @@ def test_order_book_ask_strategy(default_conf, limit_buy_order_open, limit_buy_o
|
|||||||
assert log_has('Sell Price at location 1 from orderbook could not be determined.', caplog)
|
assert log_has('Sell Price at location 1 from orderbook could not be determined.', caplog)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('side,ask,bid,expected', [
|
@pytest.mark.parametrize('side,ask,bid,last,last_ab,expected', [
|
||||||
('bid', 10.0, 11.0, 11.0),
|
('bid', 12.0, 11.0, 11.5, 0.0, 11.0), # full bid side
|
||||||
('bid', 10.0, 11.2, 11.2),
|
('bid', 12.0, 11.0, 11.5, 1.0, 11.5), # full last side
|
||||||
('bid', 10.0, 11.0, 11.0),
|
('bid', 12.0, 11.0, 11.5, 0.5, 11.25), # between bid and lat
|
||||||
('bid', 9.8, 11.0, 11.0),
|
('bid', 12.0, 11.2, 10.5, 0.0, 11.2), # Last smaller than bid
|
||||||
('bid', 0.0001, 0.002, 0.002),
|
('bid', 12.0, 11.2, 10.5, 1.0, 11.2), # Last smaller than bid - uses bid
|
||||||
('ask', 10.0, 11.0, 10.0),
|
('bid', 12.0, 11.2, 10.5, 0.5, 11.2), # Last smaller than bid - uses bid
|
||||||
('ask', 10.11, 11.2, 10.11),
|
('bid', 0.003, 0.002, 0.005, 0.0, 0.002),
|
||||||
('ask', 0.001, 0.002, 0.001),
|
('ask', 12.0, 11.0, 12.5, 0.0, 12.0), # full ask side
|
||||||
('ask', 0.006, 1.0, 0.006),
|
('ask', 12.0, 11.0, 12.5, 1.0, 12.5), # full last side
|
||||||
|
('ask', 12.0, 11.0, 12.5, 0.5, 12.25), # between bid and lat
|
||||||
|
('ask', 12.2, 11.2, 10.5, 0.0, 12.2), # Last smaller than ask
|
||||||
|
('ask', 12.0, 11.0, 10.5, 1.0, 12.0), # Last smaller than ask - uses ask
|
||||||
|
('ask', 12.0, 11.2, 10.5, 0.5, 12.0), # Last smaller than ask - uses ask
|
||||||
|
('ask', 10.0, 11.0, 11.0, 0.0, 10.0),
|
||||||
|
('ask', 10.11, 11.2, 11.0, 0.0, 10.11),
|
||||||
|
('ask', 0.001, 0.002, 11.0, 0.0, 0.001),
|
||||||
|
('ask', 0.006, 1.0, 11.0, 0.0, 0.006),
|
||||||
])
|
])
|
||||||
def test_get_sell_rate(default_conf, mocker, caplog, side, bid, ask, expected) -> None:
|
def test_get_sell_rate(default_conf, mocker, caplog, side, bid, ask,
|
||||||
|
last, last_ab, expected) -> None:
|
||||||
caplog.set_level(logging.DEBUG)
|
caplog.set_level(logging.DEBUG)
|
||||||
|
|
||||||
default_conf['ask_strategy']['price_side'] = side
|
default_conf['ask_strategy']['price_side'] = side
|
||||||
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', return_value={'ask': ask, 'bid': bid})
|
default_conf['ask_strategy']['bid_last_balance'] = last_ab
|
||||||
|
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
|
||||||
|
return_value={'ask': ask, 'bid': bid, 'last': last})
|
||||||
pair = "ETH/BTC"
|
pair = "ETH/BTC"
|
||||||
|
|
||||||
# Test regular mode
|
# Test regular mode
|
||||||
@ -4186,7 +4197,7 @@ def test_get_sell_rate_exception(default_conf, mocker, caplog):
|
|||||||
default_conf['ask_strategy']['price_side'] = 'ask'
|
default_conf['ask_strategy']['price_side'] = 'ask'
|
||||||
pair = "ETH/BTC"
|
pair = "ETH/BTC"
|
||||||
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
|
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
|
||||||
return_value={'ask': None, 'bid': 0.12})
|
return_value={'ask': None, 'bid': 0.12, 'last': None})
|
||||||
ft = get_patched_freqtradebot(mocker, default_conf)
|
ft = get_patched_freqtradebot(mocker, default_conf)
|
||||||
with pytest.raises(PricingError, match=r"Sell-Rate for ETH/BTC was empty."):
|
with pytest.raises(PricingError, match=r"Sell-Rate for ETH/BTC was empty."):
|
||||||
ft.get_sell_rate(pair, True)
|
ft.get_sell_rate(pair, True)
|
||||||
@ -4195,7 +4206,7 @@ def test_get_sell_rate_exception(default_conf, mocker, caplog):
|
|||||||
assert ft.get_sell_rate(pair, True) == 0.12
|
assert ft.get_sell_rate(pair, True) == 0.12
|
||||||
# Reverse sides
|
# Reverse sides
|
||||||
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
|
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
|
||||||
return_value={'ask': 0.13, 'bid': None})
|
return_value={'ask': 0.13, 'bid': None, 'last': None})
|
||||||
with pytest.raises(PricingError, match=r"Sell-Rate for ETH/BTC was empty."):
|
with pytest.raises(PricingError, match=r"Sell-Rate for ETH/BTC was empty."):
|
||||||
ft.get_sell_rate(pair, True)
|
ft.get_sell_rate(pair, True)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user