Merge pull request #2982 from freqtrade/rate_side_optional
Rate side configurable
This commit is contained in:
commit
dea4ef957e
@ -25,6 +25,7 @@
|
|||||||
"sell": 30
|
"sell": 30
|
||||||
},
|
},
|
||||||
"bid_strategy": {
|
"bid_strategy": {
|
||||||
|
"price_side": "bid",
|
||||||
"use_order_book": false,
|
"use_order_book": false,
|
||||||
"ask_last_balance": 0.0,
|
"ask_last_balance": 0.0,
|
||||||
"order_book_top": 1,
|
"order_book_top": 1,
|
||||||
@ -34,6 +35,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ask_strategy":{
|
"ask_strategy":{
|
||||||
|
"price_side": "ask",
|
||||||
"use_order_book": false,
|
"use_order_book": false,
|
||||||
"order_book_min": 1,
|
"order_book_min": 1,
|
||||||
"order_book_max": 9,
|
"order_book_max": 9,
|
||||||
|
@ -60,11 +60,13 @@ Mandatory parameters are marked as **Required**, which means that they are requi
|
|||||||
| `trailing_only_offset_is_reached` | Only apply trailing stoploss when the offset is reached. [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `false`.* <br> **Datatype:** Boolean
|
| `trailing_only_offset_is_reached` | Only apply trailing stoploss when the offset is reached. [stoploss documentation](stoploss.md). [Strategy Override](#parameters-in-the-strategy). <br>*Defaults to `false`.* <br> **Datatype:** Boolean
|
||||||
| `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. [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. [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. [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. [Strategy Override](#parameters-in-the-strategy).<br> **Datatype:** Integer
|
||||||
| `bid_strategy.ask_last_balance` | **Required.** Set the bidding price. More information [below](#buy-price-without-orderbook).
|
| `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.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.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
|
||||||
@ -463,23 +465,72 @@ Orderbook `bid` (buy) side depth is then divided by the orderbook `ask` (sell) s
|
|||||||
!!! Note
|
!!! Note
|
||||||
A delta value below 1 means that `ask` (sell) orderbook side depth is greater than the depth of the `bid` (buy) orderbook side, while a value greater than 1 means opposite (depth of the buy side is higher than the depth of the sell side).
|
A delta value below 1 means that `ask` (sell) orderbook side depth is greater than the depth of the `bid` (buy) orderbook side, while a value greater than 1 means opposite (depth of the buy side is higher than the depth of the sell side).
|
||||||
|
|
||||||
|
#### Buy price side
|
||||||
|
|
||||||
|
The configuration setting `bid_strategy.price_side` defines the side of the spread the bot looks for when buying.
|
||||||
|
|
||||||
|
The following displays an orderbook.
|
||||||
|
|
||||||
|
``` explanation
|
||||||
|
...
|
||||||
|
103
|
||||||
|
102
|
||||||
|
101 # ask
|
||||||
|
-------------Current spread
|
||||||
|
99 # bid
|
||||||
|
98
|
||||||
|
97
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
If `bid_strategy.price_side` is set to `"bid"`, then the bot will use 99 as buying price.
|
||||||
|
In line with that, if `bid_strategy.price_side` is set to `"ask"`, then the bot will use 101 as buying price.
|
||||||
|
|
||||||
|
Using `ask` price often guarantees quicker filled orders, but the bot can also end up paying more than what would have been necessary.
|
||||||
|
Taker fees instead of maker fees will most likely apply even when using limit buy orders.
|
||||||
|
Also, prices at the "ask" side of the spread are higher than prices at the "bid" side in the orderbook, so the order behaves similar to a market order (however with a maximum price).
|
||||||
|
|
||||||
#### Buy price with Orderbook enabled
|
#### Buy price with Orderbook enabled
|
||||||
|
|
||||||
When buying with the orderbook enabled (`bid_strategy.use_order_book=True`), Freqtrade fetches the `bid_strategy.order_book_top` entries from the orderbook and then uses the entry specified as `bid_strategy.order_book_top` on the `bid` (buy) side of the orderbook. 1 specifies the topmost entry in the orderbook, while 2 would use the 2nd entry in the orderbook, and so on.
|
When buying with the orderbook enabled (`bid_strategy.use_order_book=True`), Freqtrade fetches the `bid_strategy.order_book_top` entries from the orderbook and then uses the entry specified as `bid_strategy.order_book_top` on the configured side (`bid_strategy.price_side`) of the orderbook. 1 specifies the topmost entry in the orderbook, while 2 would use the 2nd entry in the orderbook, and so on.
|
||||||
|
|
||||||
#### Buy price without Orderbook enabled
|
#### Buy price without Orderbook enabled
|
||||||
|
|
||||||
When not using orderbook (`bid_strategy.use_order_book=False`), Freqtrade uses the best `ask` (sell) price from the ticker if it's below the `last` traded price from the ticker. Otherwise (when the `ask` price is not below the `last` price), it calculates a rate between `ask` and `last` price.
|
The following section uses `side` as the configured `bid_strategy.price_side`.
|
||||||
|
|
||||||
The `bid_strategy.ask_last_balance` configuration parameter controls this. A value of `0.0` will use `ask` price, while `1.0` will use the `last` price and values between those interpolate between ask and last price.
|
When not using orderbook (`bid_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.
|
||||||
|
|
||||||
Using `ask` price often guarantees quicker success in the bid, but the bot can also end up paying more than what would have been necessary.
|
The `bid_strategy.ask_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 ask and last price.
|
||||||
|
|
||||||
### Sell price
|
### Sell price
|
||||||
|
|
||||||
|
#### Sell price side
|
||||||
|
|
||||||
|
The configuration setting `ask_strategy.price_side` defines the side of the spread the bot looks for when selling.
|
||||||
|
|
||||||
|
The following displays an orderbook:
|
||||||
|
|
||||||
|
``` explanation
|
||||||
|
...
|
||||||
|
103
|
||||||
|
102
|
||||||
|
101 # ask
|
||||||
|
-------------Current spread
|
||||||
|
99 # bid
|
||||||
|
98
|
||||||
|
97
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
If `ask_strategy.price_side` is set to `"ask"`, then the bot will use 101 as selling price.
|
||||||
|
In line with that, if `ask_strategy.price_side` is set to `"bid"`, then the bot will use 99 as selling price.
|
||||||
|
|
||||||
#### Sell price with Orderbook enabled
|
#### Sell price with Orderbook enabled
|
||||||
|
|
||||||
When selling with the orderbook enabled (`ask_strategy.use_order_book=True`), Freqtrade fetches the `ask_strategy.order_book_max` entries in the orderbook. Then each of the orderbook steps between `ask_strategy.order_book_min` and `ask_strategy.order_book_max` on the `ask` orderbook side are validated for a profitable sell-possibility based on the strategy configuration and the sell order is placed at the first profitable spot.
|
When selling with the orderbook enabled (`ask_strategy.use_order_book=True`), Freqtrade fetches the `ask_strategy.order_book_max` entries in the orderbook. Then each of the orderbook steps between `ask_strategy.order_book_min` and `ask_strategy.order_book_max` on the configured orderbook side are validated for a profitable sell-possibility based on the strategy configuration (`minimal_roi` conditions) and the sell order is placed at the first profitable spot.
|
||||||
|
|
||||||
|
!!! Note
|
||||||
|
Using `order_book_max` higher than `order_book_min` only makes sense when ask_strategy.price_side is set to `"ask"`.
|
||||||
|
|
||||||
The idea here is to place the sell order early, to be ahead in the queue.
|
The idea here is to place the sell order early, to be ahead in the queue.
|
||||||
|
|
||||||
@ -490,7 +541,7 @@ A fixed slot (mirroring `bid_strategy.order_book_top`) can be defined by setting
|
|||||||
|
|
||||||
#### Sell price without Orderbook enabled
|
#### Sell price without Orderbook enabled
|
||||||
|
|
||||||
When not using orderbook (`ask_strategy.use_order_book=False`), the `bid` price 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.
|
||||||
|
|
||||||
## Pairlists
|
## Pairlists
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ UNLIMITED_STAKE_AMOUNT = 'unlimited'
|
|||||||
DEFAULT_AMOUNT_RESERVE_PERCENT = 0.05
|
DEFAULT_AMOUNT_RESERVE_PERCENT = 0.05
|
||||||
REQUIRED_ORDERTIF = ['buy', 'sell']
|
REQUIRED_ORDERTIF = ['buy', 'sell']
|
||||||
REQUIRED_ORDERTYPES = ['buy', 'sell', 'stoploss', 'stoploss_on_exchange']
|
REQUIRED_ORDERTYPES = ['buy', 'sell', 'stoploss', 'stoploss_on_exchange']
|
||||||
|
ORDERBOOK_SIDES = ['ask', 'bid']
|
||||||
ORDERTYPE_POSSIBILITIES = ['limit', 'market']
|
ORDERTYPE_POSSIBILITIES = ['limit', 'market']
|
||||||
ORDERTIF_POSSIBILITIES = ['gtc', 'fok', 'ioc']
|
ORDERTIF_POSSIBILITIES = ['gtc', 'fok', 'ioc']
|
||||||
AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList',
|
AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList',
|
||||||
@ -113,6 +114,8 @@ CONF_SCHEMA = {
|
|||||||
'minimum': 0,
|
'minimum': 0,
|
||||||
'maximum': 1,
|
'maximum': 1,
|
||||||
'exclusiveMaximum': False,
|
'exclusiveMaximum': False,
|
||||||
|
},
|
||||||
|
'price_side': {'type': 'string', 'enum': ORDERBOOK_SIDES, 'default': 'bid'},
|
||||||
'use_order_book': {'type': 'boolean'},
|
'use_order_book': {'type': 'boolean'},
|
||||||
'order_book_top': {'type': 'integer', 'maximum': 20, 'minimum': 1},
|
'order_book_top': {'type': 'integer', 'maximum': 20, 'minimum': 1},
|
||||||
'check_depth_of_market': {
|
'check_depth_of_market': {
|
||||||
@ -123,12 +126,12 @@ CONF_SCHEMA = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
'required': ['ask_last_balance']
|
'required': ['ask_last_balance']
|
||||||
},
|
},
|
||||||
'ask_strategy': {
|
'ask_strategy': {
|
||||||
'type': 'object',
|
'type': 'object',
|
||||||
'properties': {
|
'properties': {
|
||||||
|
'price_side': {'type': 'string', 'enum': ORDERBOOK_SIDES, 'default': 'ask'},
|
||||||
'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},
|
||||||
@ -299,6 +302,7 @@ SCHEMA_TRADE_REQUIRED = [
|
|||||||
'last_stake_amount_min_ratio',
|
'last_stake_amount_min_ratio',
|
||||||
'dry_run',
|
'dry_run',
|
||||||
'dry_run_wallet',
|
'dry_run_wallet',
|
||||||
|
'ask_strategy',
|
||||||
'bid_strategy',
|
'bid_strategy',
|
||||||
'unfilledtimeout',
|
'unfilledtimeout',
|
||||||
'stoploss',
|
'stoploss',
|
||||||
|
@ -242,25 +242,25 @@ class FreqtradeBot:
|
|||||||
logger.info(f"Using cached buy rate for {pair}.")
|
logger.info(f"Using cached buy rate for {pair}.")
|
||||||
return rate
|
return rate
|
||||||
|
|
||||||
config_bid_strategy = self.config.get('bid_strategy', {})
|
bid_strategy = self.config.get('bid_strategy', {})
|
||||||
if 'use_order_book' in config_bid_strategy and\
|
if 'use_order_book' in bid_strategy and bid_strategy.get('use_order_book', False):
|
||||||
config_bid_strategy.get('use_order_book', False):
|
logger.info(
|
||||||
logger.info('Getting price from order book')
|
f"Getting price from order book {bid_strategy['price_side'].capitalize()} side."
|
||||||
order_book_top = config_bid_strategy.get('order_book_top', 1)
|
)
|
||||||
|
order_book_top = bid_strategy.get('order_book_top', 1)
|
||||||
order_book = self.exchange.get_order_book(pair, order_book_top)
|
order_book = self.exchange.get_order_book(pair, order_book_top)
|
||||||
logger.debug('order_book %s', order_book)
|
logger.debug('order_book %s', order_book)
|
||||||
# top 1 = index 0
|
# top 1 = index 0
|
||||||
order_book_rate = order_book['bids'][order_book_top - 1][0]
|
order_book_rate = order_book[f"{bid_strategy['price_side']}s"][order_book_top - 1][0]
|
||||||
logger.info('...top %s order book buy rate %0.8f', order_book_top, order_book_rate)
|
logger.info(f'...top {order_book_top} order book buy rate {order_book_rate:.8f}')
|
||||||
used_rate = order_book_rate
|
used_rate = order_book_rate
|
||||||
else:
|
else:
|
||||||
logger.info('Using Last Ask / Last Price')
|
logger.info(f"Using Last {bid_strategy['price_side'].capitalize()} / Last Price")
|
||||||
ticker = self.exchange.fetch_ticker(pair)
|
ticker = self.exchange.fetch_ticker(pair)
|
||||||
if ticker['ask'] < ticker['last']:
|
ticker_rate = ticker[bid_strategy['price_side']]
|
||||||
ticker_rate = ticker['ask']
|
if ticker['last'] and ticker_rate > ticker['last']:
|
||||||
else:
|
|
||||||
balance = self.config['bid_strategy']['ask_last_balance']
|
balance = self.config['bid_strategy']['ask_last_balance']
|
||||||
ticker_rate = ticker['ask'] + balance * (ticker['last'] - ticker['ask'])
|
ticker_rate = ticker_rate + balance * (ticker['last'] - ticker_rate)
|
||||||
used_rate = ticker_rate
|
used_rate = ticker_rate
|
||||||
|
|
||||||
self._buy_rate_cache[pair] = used_rate
|
self._buy_rate_cache[pair] = used_rate
|
||||||
@ -617,6 +617,15 @@ class FreqtradeBot:
|
|||||||
|
|
||||||
return trades_closed
|
return trades_closed
|
||||||
|
|
||||||
|
def _order_book_gen(self, pair: str, side: str, order_book_max: int = 1,
|
||||||
|
order_book_min: int = 1):
|
||||||
|
"""
|
||||||
|
Helper generator to query orderbook in loop (used for early sell-order placing)
|
||||||
|
"""
|
||||||
|
order_book = self.exchange.get_order_book(pair, order_book_max)
|
||||||
|
for i in range(order_book_min, order_book_max + 1):
|
||||||
|
yield order_book[side][i - 1][0]
|
||||||
|
|
||||||
def get_sell_rate(self, pair: str, refresh: bool) -> float:
|
def get_sell_rate(self, pair: str, refresh: bool) -> float:
|
||||||
"""
|
"""
|
||||||
Get sell rate - either using get-ticker bid or first bid based on orderbook
|
Get sell rate - either using get-ticker bid or first bid based on orderbook
|
||||||
@ -636,13 +645,12 @@ class FreqtradeBot:
|
|||||||
|
|
||||||
config_ask_strategy = self.config.get('ask_strategy', {})
|
config_ask_strategy = self.config.get('ask_strategy', {})
|
||||||
if config_ask_strategy.get('use_order_book', False):
|
if config_ask_strategy.get('use_order_book', False):
|
||||||
|
# This code is only used for notifications, selling uses the generator directly
|
||||||
logger.debug('Using order book to get sell rate')
|
logger.debug('Using order book to get sell rate')
|
||||||
|
rate = next(self._order_book_gen(pair, f"{config_ask_strategy['price_side']}s"))
|
||||||
order_book = self.exchange.get_order_book(pair, 1)
|
|
||||||
rate = order_book['bids'][0][0]
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
rate = self.exchange.fetch_ticker(pair)['bid']
|
rate = self.exchange.fetch_ticker(pair)[config_ask_strategy['price_side']]
|
||||||
self._sell_rate_cache[pair] = rate
|
self._sell_rate_cache[pair] = rate
|
||||||
return rate
|
return rate
|
||||||
|
|
||||||
@ -672,12 +680,13 @@ class FreqtradeBot:
|
|||||||
order_book_min = config_ask_strategy.get('order_book_min', 1)
|
order_book_min = config_ask_strategy.get('order_book_min', 1)
|
||||||
order_book_max = config_ask_strategy.get('order_book_max', 1)
|
order_book_max = config_ask_strategy.get('order_book_max', 1)
|
||||||
|
|
||||||
order_book = self.exchange.get_order_book(trade.pair, order_book_max)
|
order_book = self._order_book_gen(trade.pair, f"{config_ask_strategy['price_side']}s",
|
||||||
|
order_book_min=order_book_min,
|
||||||
|
order_book_max=order_book_max)
|
||||||
for i in range(order_book_min, order_book_max + 1):
|
for i in range(order_book_min, order_book_max + 1):
|
||||||
order_book_rate = order_book['asks'][i - 1][0]
|
sell_rate = next(order_book)
|
||||||
logger.debug(' order book asks top %s: %0.8f', i, order_book_rate)
|
logger.debug(f" order book {config_ask_strategy['price_side']} top {i}: "
|
||||||
sell_rate = order_book_rate
|
f"{sell_rate:0.8f}")
|
||||||
|
|
||||||
if self._check_and_execute_sell(trade, sell_rate, buy, sell):
|
if self._check_and_execute_sell(trade, sell_rate, buy, sell):
|
||||||
return True
|
return True
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
"sell": 30
|
"sell": 30
|
||||||
},
|
},
|
||||||
"bid_strategy": {
|
"bid_strategy": {
|
||||||
|
"price_side": "bid",
|
||||||
"ask_last_balance": 0.0,
|
"ask_last_balance": 0.0,
|
||||||
"use_order_book": false,
|
"use_order_book": false,
|
||||||
"order_book_top": 1,
|
"order_book_top": 1,
|
||||||
@ -20,6 +21,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ask_strategy": {
|
"ask_strategy": {
|
||||||
|
"price_side": "ask",
|
||||||
"use_order_book": false,
|
"use_order_book": false,
|
||||||
"order_book_min": 1,
|
"order_book_min": 1,
|
||||||
"order_book_max": 9,
|
"order_book_max": 9,
|
||||||
|
@ -51,13 +51,13 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
|
|||||||
'open_date_hum': ANY,
|
'open_date_hum': ANY,
|
||||||
'close_date': None,
|
'close_date': None,
|
||||||
'close_date_hum': None,
|
'close_date_hum': None,
|
||||||
'open_rate': 1.099e-05,
|
'open_rate': 1.098e-05,
|
||||||
'close_rate': None,
|
'close_rate': None,
|
||||||
'current_rate': 1.098e-05,
|
'current_rate': 1.099e-05,
|
||||||
'amount': 90.99181074,
|
'amount': 91.07468124,
|
||||||
'stake_amount': 0.001,
|
'stake_amount': 0.001,
|
||||||
'close_profit': None,
|
'close_profit': None,
|
||||||
'current_profit': -0.59,
|
'current_profit': -0.41,
|
||||||
'stop_loss': 0.0,
|
'stop_loss': 0.0,
|
||||||
'initial_stop_loss': 0.0,
|
'initial_stop_loss': 0.0,
|
||||||
'initial_stop_loss_pct': None,
|
'initial_stop_loss_pct': None,
|
||||||
@ -78,10 +78,10 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
|
|||||||
'open_date_hum': ANY,
|
'open_date_hum': ANY,
|
||||||
'close_date': None,
|
'close_date': None,
|
||||||
'close_date_hum': None,
|
'close_date_hum': None,
|
||||||
'open_rate': 1.099e-05,
|
'open_rate': 1.098e-05,
|
||||||
'close_rate': None,
|
'close_rate': None,
|
||||||
'current_rate': ANY,
|
'current_rate': ANY,
|
||||||
'amount': 90.99181074,
|
'amount': 91.07468124,
|
||||||
'stake_amount': 0.001,
|
'stake_amount': 0.001,
|
||||||
'close_profit': None,
|
'close_profit': None,
|
||||||
'current_profit': ANY,
|
'current_profit': ANY,
|
||||||
@ -121,7 +121,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None:
|
|||||||
assert "Pair" in headers
|
assert "Pair" in headers
|
||||||
assert 'instantly' == result[0][2]
|
assert 'instantly' == result[0][2]
|
||||||
assert 'ETH/BTC' in result[0][1]
|
assert 'ETH/BTC' in result[0][1]
|
||||||
assert '-0.59%' == result[0][3]
|
assert '-0.41%' == result[0][3]
|
||||||
# Test with fiatconvert
|
# Test with fiatconvert
|
||||||
|
|
||||||
rpc._fiat_converter = CryptoToFiatConverter()
|
rpc._fiat_converter = CryptoToFiatConverter()
|
||||||
@ -130,7 +130,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None:
|
|||||||
assert "Pair" in headers
|
assert "Pair" in headers
|
||||||
assert 'instantly' == result[0][2]
|
assert 'instantly' == result[0][2]
|
||||||
assert 'ETH/BTC' in result[0][1]
|
assert 'ETH/BTC' in result[0][1]
|
||||||
assert '-0.59% (-0.09)' == result[0][3]
|
assert '-0.41% (-0.06)' == result[0][3]
|
||||||
|
|
||||||
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_sell_rate',
|
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_sell_rate',
|
||||||
MagicMock(side_effect=DependencyException(f"Pair 'ETH/BTC' not available")))
|
MagicMock(side_effect=DependencyException(f"Pair 'ETH/BTC' not available")))
|
||||||
@ -245,9 +245,9 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
|
|||||||
assert prec_satoshi(stats['profit_closed_coin'], 6.217e-05)
|
assert prec_satoshi(stats['profit_closed_coin'], 6.217e-05)
|
||||||
assert prec_satoshi(stats['profit_closed_percent'], 6.2)
|
assert prec_satoshi(stats['profit_closed_percent'], 6.2)
|
||||||
assert prec_satoshi(stats['profit_closed_fiat'], 0.93255)
|
assert prec_satoshi(stats['profit_closed_fiat'], 0.93255)
|
||||||
assert prec_satoshi(stats['profit_all_coin'], 5.632e-05)
|
assert prec_satoshi(stats['profit_all_coin'], 5.802e-05)
|
||||||
assert prec_satoshi(stats['profit_all_percent'], 2.81)
|
assert prec_satoshi(stats['profit_all_percent'], 2.89)
|
||||||
assert prec_satoshi(stats['profit_all_fiat'], 0.8448)
|
assert prec_satoshi(stats['profit_all_fiat'], 0.8703)
|
||||||
assert stats['trade_count'] == 2
|
assert stats['trade_count'] == 2
|
||||||
assert stats['first_trade_date'] == 'just now'
|
assert stats['first_trade_date'] == 'just now'
|
||||||
assert stats['latest_trade_date'] == 'just now'
|
assert stats['latest_trade_date'] == 'just now'
|
||||||
@ -668,7 +668,7 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, limit_buy_order) -> None
|
|||||||
trade = rpc._rpc_forcebuy(pair, None)
|
trade = rpc._rpc_forcebuy(pair, None)
|
||||||
assert isinstance(trade, Trade)
|
assert isinstance(trade, Trade)
|
||||||
assert trade.pair == pair
|
assert trade.pair == pair
|
||||||
assert trade.open_rate == ticker()['ask']
|
assert trade.open_rate == ticker()['bid']
|
||||||
|
|
||||||
# Test buy duplicate
|
# Test buy duplicate
|
||||||
with pytest.raises(RPCException, match=r'position for ETH/BTC already open - id: 1'):
|
with pytest.raises(RPCException, match=r'position for ETH/BTC already open - id: 1'):
|
||||||
|
@ -426,20 +426,20 @@ def test_api_status(botclient, mocker, ticker, fee, markets):
|
|||||||
rc = client_get(client, f"{BASE_URI}/status")
|
rc = client_get(client, f"{BASE_URI}/status")
|
||||||
assert_response(rc)
|
assert_response(rc)
|
||||||
assert len(rc.json) == 1
|
assert len(rc.json) == 1
|
||||||
assert rc.json == [{'amount': 90.99181074,
|
assert rc.json == [{'amount': 91.07468124,
|
||||||
'base_currency': 'BTC',
|
'base_currency': 'BTC',
|
||||||
'close_date': None,
|
'close_date': None,
|
||||||
'close_date_hum': None,
|
'close_date_hum': None,
|
||||||
'close_profit': None,
|
'close_profit': None,
|
||||||
'close_rate': None,
|
'close_rate': None,
|
||||||
'current_profit': -0.59,
|
'current_profit': -0.41,
|
||||||
'current_rate': 1.098e-05,
|
'current_rate': 1.099e-05,
|
||||||
'initial_stop_loss': 0.0,
|
'initial_stop_loss': 0.0,
|
||||||
'initial_stop_loss_pct': None,
|
'initial_stop_loss_pct': None,
|
||||||
'open_date': ANY,
|
'open_date': ANY,
|
||||||
'open_date_hum': 'just now',
|
'open_date_hum': 'just now',
|
||||||
'open_order': '(limit buy rem=0.00000000)',
|
'open_order': '(limit buy rem=0.00000000)',
|
||||||
'open_rate': 1.099e-05,
|
'open_rate': 1.098e-05,
|
||||||
'pair': 'ETH/BTC',
|
'pair': 'ETH/BTC',
|
||||||
'stake_amount': 0.001,
|
'stake_amount': 0.001,
|
||||||
'stop_loss': 0.0,
|
'stop_loss': 0.0,
|
||||||
|
@ -720,13 +720,13 @@ def test_forcesell_handle(default_conf, update, ticker, fee,
|
|||||||
'exchange': 'Bittrex',
|
'exchange': 'Bittrex',
|
||||||
'pair': 'ETH/BTC',
|
'pair': 'ETH/BTC',
|
||||||
'gain': 'profit',
|
'gain': 'profit',
|
||||||
'limit': 1.172e-05,
|
'limit': 1.173e-05,
|
||||||
'amount': 90.99181073703367,
|
'amount': 91.07468123861567,
|
||||||
'order_type': 'limit',
|
'order_type': 'limit',
|
||||||
'open_rate': 1.099e-05,
|
'open_rate': 1.098e-05,
|
||||||
'current_rate': 1.172e-05,
|
'current_rate': 1.173e-05,
|
||||||
'profit_amount': 6.126e-05,
|
'profit_amount': 6.314e-05,
|
||||||
'profit_percent': 0.0611052,
|
'profit_percent': 0.0629778,
|
||||||
'stake_currency': 'BTC',
|
'stake_currency': 'BTC',
|
||||||
'fiat_currency': 'USD',
|
'fiat_currency': 'USD',
|
||||||
'sell_reason': SellType.FORCE_SELL.value,
|
'sell_reason': SellType.FORCE_SELL.value,
|
||||||
@ -779,13 +779,13 @@ def test_forcesell_down_handle(default_conf, update, ticker, fee,
|
|||||||
'exchange': 'Bittrex',
|
'exchange': 'Bittrex',
|
||||||
'pair': 'ETH/BTC',
|
'pair': 'ETH/BTC',
|
||||||
'gain': 'loss',
|
'gain': 'loss',
|
||||||
'limit': 1.044e-05,
|
'limit': 1.043e-05,
|
||||||
'amount': 90.99181073703367,
|
'amount': 91.07468123861567,
|
||||||
'order_type': 'limit',
|
'order_type': 'limit',
|
||||||
'open_rate': 1.099e-05,
|
'open_rate': 1.098e-05,
|
||||||
'current_rate': 1.044e-05,
|
'current_rate': 1.043e-05,
|
||||||
'profit_amount': -5.492e-05,
|
'profit_amount': -5.497e-05,
|
||||||
'profit_percent': -0.05478342,
|
'profit_percent': -0.05482878,
|
||||||
'stake_currency': 'BTC',
|
'stake_currency': 'BTC',
|
||||||
'fiat_currency': 'USD',
|
'fiat_currency': 'USD',
|
||||||
'sell_reason': SellType.FORCE_SELL.value,
|
'sell_reason': SellType.FORCE_SELL.value,
|
||||||
@ -827,13 +827,13 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None
|
|||||||
'exchange': 'Bittrex',
|
'exchange': 'Bittrex',
|
||||||
'pair': 'ETH/BTC',
|
'pair': 'ETH/BTC',
|
||||||
'gain': 'loss',
|
'gain': 'loss',
|
||||||
'limit': 1.098e-05,
|
'limit': 1.099e-05,
|
||||||
'amount': 90.99181073703367,
|
'amount': 91.07468123861567,
|
||||||
'order_type': 'limit',
|
'order_type': 'limit',
|
||||||
'open_rate': 1.099e-05,
|
'open_rate': 1.098e-05,
|
||||||
'current_rate': 1.098e-05,
|
'current_rate': 1.099e-05,
|
||||||
'profit_amount': -5.91e-06,
|
'profit_amount': -4.09e-06,
|
||||||
'profit_percent': -0.00589291,
|
'profit_percent': -0.00408133,
|
||||||
'stake_currency': 'BTC',
|
'stake_currency': 'BTC',
|
||||||
'fiat_currency': 'USD',
|
'fiat_currency': 'USD',
|
||||||
'sell_reason': SellType.FORCE_SELL.value,
|
'sell_reason': SellType.FORCE_SELL.value,
|
||||||
|
@ -761,8 +761,8 @@ def test_process_trade_creation(default_conf, ticker, limit_buy_order,
|
|||||||
assert trade.is_open
|
assert trade.is_open
|
||||||
assert trade.open_date is not None
|
assert trade.open_date is not None
|
||||||
assert trade.exchange == 'bittrex'
|
assert trade.exchange == 'bittrex'
|
||||||
assert trade.open_rate == 0.00001099
|
assert trade.open_rate == 0.00001098
|
||||||
assert trade.amount == 90.99181073703367
|
assert trade.amount == 91.07468123861567
|
||||||
|
|
||||||
assert log_has(
|
assert log_has(
|
||||||
'Buy signal found: about create a new trade with stake_amount: 0.001 ...', caplog
|
'Buy signal found: about create a new trade with stake_amount: 0.001 ...', caplog
|
||||||
@ -906,20 +906,37 @@ def test_process_informative_pairs_added(default_conf, ticker, mocker) -> None:
|
|||||||
assert ("ETH/BTC", default_conf["ticker_interval"]) in refresh_mock.call_args[0][0]
|
assert ("ETH/BTC", default_conf["ticker_interval"]) in refresh_mock.call_args[0][0]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("ask,last,last_ab,expected", [
|
@pytest.mark.parametrize("side,ask,bid,last,last_ab,expected", [
|
||||||
(20, 10, 0.0, 20), # Full ask side
|
('ask', 20, 19, 10, 0.0, 20), # Full ask side
|
||||||
(20, 10, 1.0, 10), # Full last side
|
('ask', 20, 19, 10, 1.0, 10), # Full last side
|
||||||
(20, 10, 0.5, 15), # Between ask and last
|
('ask', 20, 19, 10, 0.5, 15), # Between ask and last
|
||||||
(20, 10, 0.7, 13), # Between ask and last
|
('ask', 20, 19, 10, 0.7, 13), # Between ask and last
|
||||||
(20, 10, 0.3, 17), # Between ask and last
|
('ask', 20, 19, 10, 0.3, 17), # Between ask and last
|
||||||
(5, 10, 1.0, 5), # last bigger than ask
|
('ask', 5, 6, 10, 1.0, 5), # last bigger than ask
|
||||||
(5, 10, 0.5, 5), # last bigger than ask
|
('ask', 5, 6, 10, 0.5, 5), # last bigger than ask
|
||||||
|
('ask', 10, 20, None, 0.5, 10), # last not available - uses ask
|
||||||
|
('ask', 4, 5, None, 0.5, 4), # last not available - uses ask
|
||||||
|
('ask', 4, 5, None, 1, 4), # last not available - uses ask
|
||||||
|
('ask', 4, 5, None, 0, 4), # last not available - uses ask
|
||||||
|
('bid', 10, 20, 10, 0.0, 20), # Full bid side
|
||||||
|
('bid', 10, 20, 10, 1.0, 10), # Full last side
|
||||||
|
('bid', 10, 20, 10, 0.5, 15), # Between bid and last
|
||||||
|
('bid', 10, 20, 10, 0.7, 13), # Between bid and last
|
||||||
|
('bid', 10, 20, 10, 0.3, 17), # Between bid and last
|
||||||
|
('bid', 4, 5, 10, 1.0, 5), # last bigger than bid
|
||||||
|
('bid', 4, 5, 10, 0.5, 5), # last bigger than bid
|
||||||
|
('bid', 10, 20, None, 0.5, 20), # last not available - uses bid
|
||||||
|
('bid', 4, 5, None, 0.5, 5), # last not available - uses bid
|
||||||
|
('bid', 4, 5, None, 1, 5), # last not available - uses bid
|
||||||
|
('bid', 4, 5, None, 0, 5), # last not available - uses bid
|
||||||
])
|
])
|
||||||
def test_get_buy_rate(mocker, default_conf, caplog, ask, last, last_ab, expected) -> None:
|
def test_get_buy_rate(mocker, default_conf, caplog, side, ask, bid,
|
||||||
|
last, last_ab, expected) -> None:
|
||||||
default_conf['bid_strategy']['ask_last_balance'] = last_ab
|
default_conf['bid_strategy']['ask_last_balance'] = last_ab
|
||||||
|
default_conf['bid_strategy']['price_side'] = side
|
||||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||||
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
|
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
|
||||||
MagicMock(return_value={'ask': ask, 'last': last}))
|
MagicMock(return_value={'ask': ask, 'last': last, 'bid': bid}))
|
||||||
|
|
||||||
assert freqtrade.get_buy_rate('ETH/BTC', True) == expected
|
assert freqtrade.get_buy_rate('ETH/BTC', True) == expected
|
||||||
assert not log_has("Using cached buy rate for ETH/BTC.", caplog)
|
assert not log_has("Using cached buy rate for ETH/BTC.", caplog)
|
||||||
@ -1317,7 +1334,7 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog,
|
|||||||
stoploss_order_mock.assert_not_called()
|
stoploss_order_mock.assert_not_called()
|
||||||
|
|
||||||
assert freqtrade.handle_trade(trade) is False
|
assert freqtrade.handle_trade(trade) is False
|
||||||
assert trade.stop_loss == 0.00002344 * 0.95
|
assert trade.stop_loss == 0.00002346 * 0.95
|
||||||
|
|
||||||
# setting stoploss_on_exchange_interval to 0 seconds
|
# setting stoploss_on_exchange_interval to 0 seconds
|
||||||
freqtrade.strategy.order_types['stoploss_on_exchange_interval'] = 0
|
freqtrade.strategy.order_types['stoploss_on_exchange_interval'] = 0
|
||||||
@ -1325,10 +1342,10 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog,
|
|||||||
assert freqtrade.handle_stoploss_on_exchange(trade) is False
|
assert freqtrade.handle_stoploss_on_exchange(trade) is False
|
||||||
|
|
||||||
cancel_order_mock.assert_called_once_with(100, 'ETH/BTC')
|
cancel_order_mock.assert_called_once_with(100, 'ETH/BTC')
|
||||||
stoploss_order_mock.assert_called_once_with(amount=85.25149190110828,
|
stoploss_order_mock.assert_called_once_with(amount=85.32423208191126,
|
||||||
pair='ETH/BTC',
|
pair='ETH/BTC',
|
||||||
order_types=freqtrade.strategy.order_types,
|
order_types=freqtrade.strategy.order_types,
|
||||||
stop_price=0.00002344 * 0.95)
|
stop_price=0.00002346 * 0.95)
|
||||||
|
|
||||||
# price fell below stoploss, so dry-run sells trade.
|
# price fell below stoploss, so dry-run sells trade.
|
||||||
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={
|
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={
|
||||||
@ -1510,12 +1527,12 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog,
|
|||||||
assert freqtrade.handle_stoploss_on_exchange(trade) is False
|
assert freqtrade.handle_stoploss_on_exchange(trade) is False
|
||||||
|
|
||||||
# stoploss should be set to 1% as trailing is on
|
# stoploss should be set to 1% as trailing is on
|
||||||
assert trade.stop_loss == 0.00002344 * 0.99
|
assert trade.stop_loss == 0.00002346 * 0.99
|
||||||
cancel_order_mock.assert_called_once_with(100, 'NEO/BTC')
|
cancel_order_mock.assert_called_once_with(100, 'NEO/BTC')
|
||||||
stoploss_order_mock.assert_called_once_with(amount=2131074.168797954,
|
stoploss_order_mock.assert_called_once_with(amount=2132892.491467577,
|
||||||
pair='NEO/BTC',
|
pair='NEO/BTC',
|
||||||
order_types=freqtrade.strategy.order_types,
|
order_types=freqtrade.strategy.order_types,
|
||||||
stop_price=0.00002344 * 0.99)
|
stop_price=0.00002346 * 0.99)
|
||||||
|
|
||||||
|
|
||||||
def test_enter_positions(mocker, default_conf, caplog) -> None:
|
def test_enter_positions(mocker, default_conf, caplog) -> None:
|
||||||
@ -2292,12 +2309,12 @@ def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, mocker) -> N
|
|||||||
'pair': 'ETH/BTC',
|
'pair': 'ETH/BTC',
|
||||||
'gain': 'profit',
|
'gain': 'profit',
|
||||||
'limit': 1.172e-05,
|
'limit': 1.172e-05,
|
||||||
'amount': 90.99181073703367,
|
'amount': 91.07468123861567,
|
||||||
'order_type': 'limit',
|
'order_type': 'limit',
|
||||||
'open_rate': 1.099e-05,
|
'open_rate': 1.098e-05,
|
||||||
'current_rate': 1.172e-05,
|
'current_rate': 1.173e-05,
|
||||||
'profit_amount': 6.126e-05,
|
'profit_amount': 6.223e-05,
|
||||||
'profit_percent': 0.0611052,
|
'profit_percent': 0.0620716,
|
||||||
'stake_currency': 'BTC',
|
'stake_currency': 'BTC',
|
||||||
'fiat_currency': 'USD',
|
'fiat_currency': 'USD',
|
||||||
'sell_reason': SellType.ROI.value,
|
'sell_reason': SellType.ROI.value,
|
||||||
@ -2341,12 +2358,12 @@ def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, mocker)
|
|||||||
'pair': 'ETH/BTC',
|
'pair': 'ETH/BTC',
|
||||||
'gain': 'loss',
|
'gain': 'loss',
|
||||||
'limit': 1.044e-05,
|
'limit': 1.044e-05,
|
||||||
'amount': 90.99181073703367,
|
'amount': 91.07468123861567,
|
||||||
'order_type': 'limit',
|
'order_type': 'limit',
|
||||||
'open_rate': 1.099e-05,
|
'open_rate': 1.098e-05,
|
||||||
'current_rate': 1.044e-05,
|
'current_rate': 1.043e-05,
|
||||||
'profit_amount': -5.492e-05,
|
'profit_amount': -5.406e-05,
|
||||||
'profit_percent': -0.05478342,
|
'profit_percent': -0.05392257,
|
||||||
'stake_currency': 'BTC',
|
'stake_currency': 'BTC',
|
||||||
'fiat_currency': 'USD',
|
'fiat_currency': 'USD',
|
||||||
'sell_reason': SellType.STOP_LOSS.value,
|
'sell_reason': SellType.STOP_LOSS.value,
|
||||||
@ -2397,12 +2414,12 @@ def test_execute_sell_down_stoploss_on_exchange_dry_run(default_conf, ticker, fe
|
|||||||
'pair': 'ETH/BTC',
|
'pair': 'ETH/BTC',
|
||||||
'gain': 'loss',
|
'gain': 'loss',
|
||||||
'limit': 1.08801e-05,
|
'limit': 1.08801e-05,
|
||||||
'amount': 90.99181073703367,
|
'amount': 91.07468123861567,
|
||||||
'order_type': 'limit',
|
'order_type': 'limit',
|
||||||
'open_rate': 1.099e-05,
|
'open_rate': 1.098e-05,
|
||||||
'current_rate': 1.044e-05,
|
'current_rate': 1.043e-05,
|
||||||
'profit_amount': -1.498e-05,
|
'profit_amount': -1.408e-05,
|
||||||
'profit_percent': -0.01493766,
|
'profit_percent': -0.01404051,
|
||||||
'stake_currency': 'BTC',
|
'stake_currency': 'BTC',
|
||||||
'fiat_currency': 'USD',
|
'fiat_currency': 'USD',
|
||||||
'sell_reason': SellType.STOP_LOSS.value,
|
'sell_reason': SellType.STOP_LOSS.value,
|
||||||
@ -2587,7 +2604,7 @@ def test_execute_sell_market_order(default_conf, ticker, fee,
|
|||||||
freqtrade.execute_sell(trade=trade, limit=ticker_sell_up()['bid'], sell_reason=SellType.ROI)
|
freqtrade.execute_sell(trade=trade, limit=ticker_sell_up()['bid'], sell_reason=SellType.ROI)
|
||||||
|
|
||||||
assert not trade.is_open
|
assert not trade.is_open
|
||||||
assert trade.close_profit == 0.0611052
|
assert trade.close_profit == 0.0620716
|
||||||
|
|
||||||
assert rpc_mock.call_count == 2
|
assert rpc_mock.call_count == 2
|
||||||
last_msg = rpc_mock.call_args_list[-1][0][0]
|
last_msg = rpc_mock.call_args_list[-1][0][0]
|
||||||
@ -2597,12 +2614,12 @@ def test_execute_sell_market_order(default_conf, ticker, fee,
|
|||||||
'pair': 'ETH/BTC',
|
'pair': 'ETH/BTC',
|
||||||
'gain': 'profit',
|
'gain': 'profit',
|
||||||
'limit': 1.172e-05,
|
'limit': 1.172e-05,
|
||||||
'amount': 90.99181073703367,
|
'amount': 91.07468123861567,
|
||||||
'order_type': 'market',
|
'order_type': 'market',
|
||||||
'open_rate': 1.099e-05,
|
'open_rate': 1.098e-05,
|
||||||
'current_rate': 1.172e-05,
|
'current_rate': 1.173e-05,
|
||||||
'profit_amount': 6.126e-05,
|
'profit_amount': 6.223e-05,
|
||||||
'profit_percent': 0.0611052,
|
'profit_percent': 0.0620716,
|
||||||
'stake_currency': 'BTC',
|
'stake_currency': 'BTC',
|
||||||
'fiat_currency': 'USD',
|
'fiat_currency': 'USD',
|
||||||
'sell_reason': SellType.ROI.value,
|
'sell_reason': SellType.ROI.value,
|
||||||
@ -3624,13 +3641,20 @@ def test_order_book_ask_strategy(default_conf, limit_buy_order, limit_sell_order
|
|||||||
assert freqtrade.handle_trade(trade) is True
|
assert freqtrade.handle_trade(trade) is True
|
||||||
|
|
||||||
|
|
||||||
def test_get_sell_rate(default_conf, mocker, caplog, ticker, order_book_l2) -> None:
|
@pytest.mark.parametrize('side,ask,bid,expected', [
|
||||||
|
('bid', 10.0, 11.0, 11.0),
|
||||||
mocker.patch.multiple(
|
('bid', 10.0, 11.2, 11.2),
|
||||||
'freqtrade.exchange.Exchange',
|
('bid', 10.0, 11.0, 11.0),
|
||||||
get_order_book=order_book_l2,
|
('bid', 9.8, 11.0, 11.0),
|
||||||
fetch_ticker=ticker,
|
('bid', 0.0001, 0.002, 0.002),
|
||||||
)
|
('ask', 10.0, 11.0, 10.0),
|
||||||
|
('ask', 10.11, 11.2, 10.11),
|
||||||
|
('ask', 0.001, 0.002, 0.001),
|
||||||
|
('ask', 0.006, 1.0, 0.006),
|
||||||
|
])
|
||||||
|
def test_get_sell_rate(default_conf, mocker, caplog, side, bid, ask, expected) -> None:
|
||||||
|
default_conf['ask_strategy']['price_side'] = side
|
||||||
|
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', return_value={'ask': ask, 'bid': bid})
|
||||||
pair = "ETH/BTC"
|
pair = "ETH/BTC"
|
||||||
|
|
||||||
# Test regular mode
|
# Test regular mode
|
||||||
@ -3638,25 +3662,33 @@ def test_get_sell_rate(default_conf, mocker, caplog, ticker, order_book_l2) -> N
|
|||||||
rate = ft.get_sell_rate(pair, True)
|
rate = ft.get_sell_rate(pair, True)
|
||||||
assert not log_has("Using cached sell rate for ETH/BTC.", caplog)
|
assert not log_has("Using cached sell rate for ETH/BTC.", caplog)
|
||||||
assert isinstance(rate, float)
|
assert isinstance(rate, float)
|
||||||
assert rate == 0.00001098
|
assert rate == expected
|
||||||
# Use caching
|
# Use caching
|
||||||
rate = ft.get_sell_rate(pair, False)
|
rate = ft.get_sell_rate(pair, False)
|
||||||
assert rate == 0.00001098
|
assert rate == expected
|
||||||
assert log_has("Using cached sell rate for ETH/BTC.", caplog)
|
assert log_has("Using cached sell rate for ETH/BTC.", caplog)
|
||||||
|
|
||||||
caplog.clear()
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('side,expected', [
|
||||||
|
('bid', 0.043936), # Value from order_book_l2 fiture - bids side
|
||||||
|
('ask', 0.043949), # Value from order_book_l2 fiture - asks side
|
||||||
|
])
|
||||||
|
def test_get_sell_rate_orderbook(default_conf, mocker, caplog, side, expected, order_book_l2):
|
||||||
# Test orderbook mode
|
# Test orderbook mode
|
||||||
|
default_conf['ask_strategy']['price_side'] = side
|
||||||
default_conf['ask_strategy']['use_order_book'] = True
|
default_conf['ask_strategy']['use_order_book'] = True
|
||||||
default_conf['ask_strategy']['order_book_min'] = 1
|
default_conf['ask_strategy']['order_book_min'] = 1
|
||||||
default_conf['ask_strategy']['order_book_max'] = 2
|
default_conf['ask_strategy']['order_book_max'] = 2
|
||||||
|
# TODO: min/max is irrelevant for this test until refactoring
|
||||||
|
pair = "ETH/BTC"
|
||||||
|
mocker.patch('freqtrade.exchange.Exchange.get_order_book', order_book_l2)
|
||||||
ft = get_patched_freqtradebot(mocker, default_conf)
|
ft = get_patched_freqtradebot(mocker, default_conf)
|
||||||
rate = ft.get_sell_rate(pair, True)
|
rate = ft.get_sell_rate(pair, True)
|
||||||
assert not log_has("Using cached sell rate for ETH/BTC.", caplog)
|
assert not log_has("Using cached sell rate for ETH/BTC.", caplog)
|
||||||
assert isinstance(rate, float)
|
assert isinstance(rate, float)
|
||||||
assert rate == 0.043936
|
assert rate == expected
|
||||||
rate = ft.get_sell_rate(pair, False)
|
rate = ft.get_sell_rate(pair, False)
|
||||||
assert rate == 0.043936
|
assert rate == expected
|
||||||
assert log_has("Using cached sell rate for ETH/BTC.", caplog)
|
assert log_has("Using cached sell rate for ETH/BTC.", caplog)
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user