Repository: freqtrade/freqtrade

Issue:
FTX - orderbook no trades 
It seems dogehedge pair does not have any orderbook trades in the last 24 hrs, ccxt queries and gets a NoneType which then yields an error. 

Using market order, latest dev

```
2020-03-01 01:17:28,780 - freqtrade.wallets - INFO - Wallets synced.
2020-03-01 01:17:28,782 - freqtrade.freqtradebot - INFO - Buy signal found: about create a new trade with stake_amount: 108 ...
2020-03-01 01:17:28,782 - freqtrade.freqtradebot - INFO - Executing Buy for DOGEHEDGE/USD
2020-03-01 01:17:28,782 - freqtrade.freqtradebot - INFO - Using Last Ask / Last Price
2020-03-01 01:17:28,859 - freqtrade.commands.trade_commands - INFO - worker found ... calling exit
2020-03-01 01:17:28,859 - freqtrade.rpc.rpc_manager - INFO - Sending rpc message: {'type': status, 'status': 'process died'}
2020-03-01 01:17:29,229 - freqtrade.freqtradebot - INFO - Cleaning up modules ...
2020-03-01 01:17:29,229 - freqtrade.rpc.rpc_manager - INFO - Cleaning up rpc modules ...
^[[C^[[C2020-03-01 01:17:52,307 - freqtrade - ERROR - Fatal exception!
Traceback (most recent call last):
  File "/home/dante/freqtrade/freqtrade/main.py", line 36, in main
    return_code = args['func'](args)
  File "/home/dante/freqtrade/freqtrade/commands/trade_commands.py", line 20, in start_trading
    worker.run()
  File "/home/dante/freqtrade/freqtrade/worker.py", line 66, in run
    state = self._worker(old_state=state)
  File "/home/dante/freqtrade/freqtrade/worker.py", line 104, in _worker
    self._throttle(func=self._process_running, throttle_secs=self._throttle_secs)
  File "/home/dante/freqtrade/freqtrade/worker.py", line 125, in _throttle
    result = func(*args, **kwargs)
  File "/home/dante/freqtrade/freqtrade/worker.py", line 139, in _process_running
    self.freqtrade.process()
  File "/home/dante/freqtrade/freqtrade/freqtradebot.py", line 151, in process
    self.enter_positions()
  File "/home/dante/freqtrade/freqtrade/freqtradebot.py", line 217, in enter_positions
    trades_created += self.create_trade(pair)
  File "/home/dante/freqtrade/freqtrade/freqtradebot.py", line 413, in create_trade    return self.execute_buy(pair, stake_amount)
  File "/home/dante/freqtrade/freqtrade/freqtradebot.py", line 453, in execute_buy
    buy_limit_requested = self.get_buy_rate(pair, True)
  File "/home/dante/freqtrade/freqtrade/freqtradebot.py", line 249, in get_buy_rate    if ticker['ask'] < ticker['last']:
TypeError: '<' not supported between instances of 'float' and 'NoneType'
```


Discussion:

Resolving patch:
diff --git a/freqtrade/constants.py b/freqtrade/constants.py
--- a/freqtrade/constants.py
+++ b/freqtrade/constants.py
@@ -15,6 +15,7 @@
 DEFAULT_AMOUNT_RESERVE_PERCENT = 0.05
 REQUIRED_ORDERTIF = ['buy', 'sell']
 REQUIRED_ORDERTYPES = ['buy', 'sell', 'stoploss', 'stoploss_on_exchange']
+ORDERBOOK_SIDES = ['ask', 'bid']
 ORDERTYPE_POSSIBILITIES = ['limit', 'market']
 ORDERTIF_POSSIBILITIES = ['gtc', 'fok', 'ioc']
 AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList',
@@ -113,15 +114,16 @@
                     'minimum': 0,
                     'maximum': 1,
                     'exclusiveMaximum': False,
-                    'use_order_book': {'type': 'boolean'},
-                    'order_book_top': {'type': 'integer', 'maximum': 20, 'minimum': 1},
-                    'check_depth_of_market': {
-                        'type': 'object',
-                        'properties': {
-                            'enabled': {'type': 'boolean'},
-                            'bids_to_ask_delta': {'type': 'number', 'minimum': 0},
-                        }
-                    },
+                },
+                'price_side': {'type': 'string', 'enum': ORDERBOOK_SIDES, 'default': 'bid'},
+                'use_order_book': {'type': 'boolean'},
+                'order_book_top': {'type': 'integer', 'maximum': 20, 'minimum': 1},
+                'check_depth_of_market': {
+                    'type': 'object',
+                    'properties': {
+                        'enabled': {'type': 'boolean'},
+                        'bids_to_ask_delta': {'type': 'number', 'minimum': 0},
+                    }
                 },
             },
             'required': ['ask_last_balance']
@@ -129,6 +131,7 @@
         'ask_strategy': {
             'type': 'object',
             'properties': {
+                'price_side': {'type': 'string', 'enum': ORDERBOOK_SIDES, 'default': 'ask'},
                 'use_order_book': {'type': 'boolean'},
                 'order_book_min': {'type': 'integer', 'minimum': 1},
                 'order_book_max': {'type': 'integer', 'minimum': 1, 'maximum': 50},
@@ -299,6 +302,7 @@
     'last_stake_amount_min_ratio',
     'dry_run',
     'dry_run_wallet',
+    'ask_strategy',
     'bid_strategy',
     'unfilledtimeout',
     'stoploss',
diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py
--- a/freqtrade/freqtradebot.py
+++ b/freqtrade/freqtradebot.py
@@ -242,25 +242,25 @@ def get_buy_rate(self, pair: str, refresh: bool) -> float:
                 logger.info(f"Using cached buy rate for {pair}.")
                 return rate
 
-        config_bid_strategy = self.config.get('bid_strategy', {})
-        if 'use_order_book' in config_bid_strategy and\
-                config_bid_strategy.get('use_order_book', False):
-            logger.info('Getting price from order book')
-            order_book_top = config_bid_strategy.get('order_book_top', 1)
+        bid_strategy = self.config.get('bid_strategy', {})
+        if 'use_order_book' in bid_strategy and bid_strategy.get('use_order_book', False):
+            logger.info(
+                f"Getting price from order book {bid_strategy['price_side'].capitalize()} side."
+                )
+            order_book_top = bid_strategy.get('order_book_top', 1)
             order_book = self.exchange.get_order_book(pair, order_book_top)
             logger.debug('order_book %s', order_book)
             # top 1 = index 0
-            order_book_rate = order_book['bids'][order_book_top - 1][0]
-            logger.info('...top %s order book buy rate %0.8f', order_book_top, order_book_rate)
+            order_book_rate = order_book[f"{bid_strategy['price_side']}s"][order_book_top - 1][0]
+            logger.info(f'...top {order_book_top} order book buy rate {order_book_rate:.8f}')
             used_rate = order_book_rate
         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)
-            if ticker['ask'] < ticker['last']:
-                ticker_rate = ticker['ask']
-            else:
+            ticker_rate = ticker[bid_strategy['price_side']]
+            if ticker['last'] and ticker_rate > ticker['last']:
                 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
 
         self._buy_rate_cache[pair] = used_rate
@@ -617,6 +617,15 @@ def exit_positions(self, trades: List[Any]) -> int:
 
         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:
         """
         Get sell rate - either using get-ticker bid or first bid based on orderbook
@@ -636,13 +645,12 @@ def get_sell_rate(self, pair: str, refresh: bool) -> float:
 
         config_ask_strategy = self.config.get('ask_strategy', {})
         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')
-
-            order_book = self.exchange.get_order_book(pair, 1)
-            rate = order_book['bids'][0][0]
+            rate = next(self._order_book_gen(pair, f"{config_ask_strategy['price_side']}s"))
 
         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
         return rate
 
@@ -672,12 +680,13 @@ def handle_trade(self, trade: Trade) -> bool:
             order_book_min = config_ask_strategy.get('order_book_min', 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):
-                order_book_rate = order_book['asks'][i - 1][0]
-                logger.debug('  order book asks top %s: %0.8f', i, order_book_rate)
-                sell_rate = order_book_rate
+                sell_rate = next(order_book)
+                logger.debug(f"  order book {config_ask_strategy['price_side']} top {i}: "
+                             f"{sell_rate:0.8f}")
 
                 if self._check_and_execute_sell(trade, sell_rate, buy, sell):
                     return True


Test patch:
diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py
--- a/tests/rpc/test_rpc.py
+++ b/tests/rpc/test_rpc.py
@@ -51,13 +51,13 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
         'open_date_hum': ANY,
         'close_date': None,
         'close_date_hum': None,
-        'open_rate': 1.099e-05,
+        'open_rate': 1.098e-05,
         'close_rate': None,
-        'current_rate': 1.098e-05,
-        'amount': 90.99181074,
+        'current_rate': 1.099e-05,
+        'amount': 91.07468124,
         'stake_amount': 0.001,
         'close_profit': None,
-        'current_profit': -0.59,
+        'current_profit': -0.41,
         'stop_loss': 0.0,
         'initial_stop_loss': 0.0,
         'initial_stop_loss_pct': None,
@@ -78,10 +78,10 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
         'open_date_hum': ANY,
         'close_date': None,
         'close_date_hum': None,
-        'open_rate': 1.099e-05,
+        'open_rate': 1.098e-05,
         'close_rate': None,
         'current_rate': ANY,
-        'amount': 90.99181074,
+        'amount': 91.07468124,
         'stake_amount': 0.001,
         'close_profit': None,
         'current_profit': ANY,
@@ -121,7 +121,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None:
     assert "Pair" in headers
     assert 'instantly' == result[0][2]
     assert 'ETH/BTC' in result[0][1]
-    assert '-0.59%' == result[0][3]
+    assert '-0.41%' == result[0][3]
     # Test with fiatconvert
 
     rpc._fiat_converter = CryptoToFiatConverter()
@@ -130,7 +130,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None:
     assert "Pair" in headers
     assert 'instantly' == result[0][2]
     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',
                  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_percent'], 6.2)
     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_percent'], 2.81)
-    assert prec_satoshi(stats['profit_all_fiat'], 0.8448)
+    assert prec_satoshi(stats['profit_all_coin'], 5.802e-05)
+    assert prec_satoshi(stats['profit_all_percent'], 2.89)
+    assert prec_satoshi(stats['profit_all_fiat'], 0.8703)
     assert stats['trade_count'] == 2
     assert stats['first_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)
     assert isinstance(trade, Trade)
     assert trade.pair == pair
-    assert trade.open_rate == ticker()['ask']
+    assert trade.open_rate == ticker()['bid']
 
     # Test buy duplicate
     with pytest.raises(RPCException, match=r'position for ETH/BTC already open - id: 1'):
diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py
--- a/tests/rpc/test_rpc_apiserver.py
+++ b/tests/rpc/test_rpc_apiserver.py
@@ -426,20 +426,20 @@ def test_api_status(botclient, mocker, ticker, fee, markets):
     rc = client_get(client, f"{BASE_URI}/status")
     assert_response(rc)
     assert len(rc.json) == 1
-    assert rc.json == [{'amount': 90.99181074,
+    assert rc.json == [{'amount': 91.07468124,
                         'base_currency': 'BTC',
                         'close_date': None,
                         'close_date_hum': None,
                         'close_profit': None,
                         'close_rate': None,
-                        'current_profit': -0.59,
-                        'current_rate': 1.098e-05,
+                        'current_profit': -0.41,
+                        'current_rate': 1.099e-05,
                         'initial_stop_loss': 0.0,
                         'initial_stop_loss_pct': None,
                         'open_date': ANY,
                         'open_date_hum': 'just now',
                         'open_order': '(limit buy rem=0.00000000)',
-                        'open_rate': 1.099e-05,
+                        'open_rate': 1.098e-05,
                         'pair': 'ETH/BTC',
                         'stake_amount': 0.001,
                         'stop_loss': 0.0,
diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py
--- a/tests/rpc/test_rpc_telegram.py
+++ b/tests/rpc/test_rpc_telegram.py
@@ -720,13 +720,13 @@ def test_forcesell_handle(default_conf, update, ticker, fee,
         'exchange': 'Bittrex',
         'pair': 'ETH/BTC',
         'gain': 'profit',
-        'limit': 1.172e-05,
-        'amount': 90.99181073703367,
+        'limit': 1.173e-05,
+        'amount': 91.07468123861567,
         'order_type': 'limit',
-        'open_rate': 1.099e-05,
-        'current_rate': 1.172e-05,
-        'profit_amount': 6.126e-05,
-        'profit_percent': 0.0611052,
+        'open_rate': 1.098e-05,
+        'current_rate': 1.173e-05,
+        'profit_amount': 6.314e-05,
+        'profit_percent': 0.0629778,
         'stake_currency': 'BTC',
         'fiat_currency': 'USD',
         'sell_reason': SellType.FORCE_SELL.value,
@@ -779,13 +779,13 @@ def test_forcesell_down_handle(default_conf, update, ticker, fee,
         'exchange': 'Bittrex',
         'pair': 'ETH/BTC',
         'gain': 'loss',
-        'limit': 1.044e-05,
-        'amount': 90.99181073703367,
+        'limit': 1.043e-05,
+        'amount': 91.07468123861567,
         'order_type': 'limit',
-        'open_rate': 1.099e-05,
-        'current_rate': 1.044e-05,
-        'profit_amount': -5.492e-05,
-        'profit_percent': -0.05478342,
+        'open_rate': 1.098e-05,
+        'current_rate': 1.043e-05,
+        'profit_amount': -5.497e-05,
+        'profit_percent': -0.05482878,
         'stake_currency': 'BTC',
         'fiat_currency': 'USD',
         'sell_reason': SellType.FORCE_SELL.value,
@@ -827,13 +827,13 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None
         'exchange': 'Bittrex',
         'pair': 'ETH/BTC',
         'gain': 'loss',
-        'limit': 1.098e-05,
-        'amount': 90.99181073703367,
+        'limit': 1.099e-05,
+        'amount': 91.07468123861567,
         'order_type': 'limit',
-        'open_rate': 1.099e-05,
-        'current_rate': 1.098e-05,
-        'profit_amount': -5.91e-06,
-        'profit_percent': -0.00589291,
+        'open_rate': 1.098e-05,
+        'current_rate': 1.099e-05,
+        'profit_amount': -4.09e-06,
+        'profit_percent': -0.00408133,
         'stake_currency': 'BTC',
         'fiat_currency': 'USD',
         'sell_reason': SellType.FORCE_SELL.value,
diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py
--- a/tests/test_freqtradebot.py
+++ b/tests/test_freqtradebot.py
@@ -761,8 +761,8 @@ def test_process_trade_creation(default_conf, ticker, limit_buy_order,
     assert trade.is_open
     assert trade.open_date is not None
     assert trade.exchange == 'bittrex'
-    assert trade.open_rate == 0.00001099
-    assert trade.amount == 90.99181073703367
+    assert trade.open_rate == 0.00001098
+    assert trade.amount == 91.07468123861567
 
     assert log_has(
         'Buy signal found: about create a new trade with stake_amount: 0.001 ...', caplog
@@ -906,20 +906,37 @@ def _refresh_whitelist(list):
     assert ("ETH/BTC", default_conf["ticker_interval"]) in refresh_mock.call_args[0][0]
 
 
-@pytest.mark.parametrize("ask,last,last_ab,expected", [
-    (20, 10, 0.0, 20),  # Full ask side
-    (20, 10, 1.0, 10),  # Full last side
-    (20, 10, 0.5, 15),  # Between ask and last
-    (20, 10, 0.7, 13),  # Between ask and last
-    (20, 10, 0.3, 17),  # Between ask and last
-    (5, 10, 1.0, 5),  # last bigger than ask
-    (5, 10, 0.5, 5),  # last bigger than ask
+@pytest.mark.parametrize("side,ask,bid,last,last_ab,expected", [
+    ('ask', 20, 19, 10, 0.0, 20),  # Full ask side
+    ('ask', 20, 19, 10, 1.0, 10),  # Full last side
+    ('ask', 20, 19, 10, 0.5, 15),  # Between ask and last
+    ('ask', 20, 19, 10, 0.7, 13),  # Between ask and last
+    ('ask', 20, 19, 10, 0.3, 17),  # Between ask and last
+    ('ask', 5, 6, 10, 1.0, 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']['price_side'] = side
     freqtrade = get_patched_freqtradebot(mocker, default_conf)
     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 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()
 
     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
     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
 
     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',
                                                 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.
     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
 
     # 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')
-    stoploss_order_mock.assert_called_once_with(amount=2131074.168797954,
+    stoploss_order_mock.assert_called_once_with(amount=2132892.491467577,
                                                 pair='NEO/BTC',
                                                 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:
@@ -2292,12 +2309,12 @@ def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, mocker) -> N
         'pair': 'ETH/BTC',
         'gain': 'profit',
         'limit': 1.172e-05,
-        'amount': 90.99181073703367,
+        'amount': 91.07468123861567,
         'order_type': 'limit',
-        'open_rate': 1.099e-05,
-        'current_rate': 1.172e-05,
-        'profit_amount': 6.126e-05,
-        'profit_percent': 0.0611052,
+        'open_rate': 1.098e-05,
+        'current_rate': 1.173e-05,
+        'profit_amount': 6.223e-05,
+        'profit_percent': 0.0620716,
         'stake_currency': 'BTC',
         'fiat_currency': 'USD',
         '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',
         'gain': 'loss',
         'limit': 1.044e-05,
-        'amount': 90.99181073703367,
+        'amount': 91.07468123861567,
         'order_type': 'limit',
-        'open_rate': 1.099e-05,
-        'current_rate': 1.044e-05,
-        'profit_amount': -5.492e-05,
-        'profit_percent': -0.05478342,
+        'open_rate': 1.098e-05,
+        'current_rate': 1.043e-05,
+        'profit_amount': -5.406e-05,
+        'profit_percent': -0.05392257,
         'stake_currency': 'BTC',
         'fiat_currency': 'USD',
         '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',
         'gain': 'loss',
         'limit': 1.08801e-05,
-        'amount': 90.99181073703367,
+        'amount': 91.07468123861567,
         'order_type': 'limit',
-        'open_rate': 1.099e-05,
-        'current_rate': 1.044e-05,
-        'profit_amount': -1.498e-05,
-        'profit_percent': -0.01493766,
+        'open_rate': 1.098e-05,
+        'current_rate': 1.043e-05,
+        'profit_amount': -1.408e-05,
+        'profit_percent': -0.01404051,
         'stake_currency': 'BTC',
         'fiat_currency': 'USD',
         '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)
 
     assert not trade.is_open
-    assert trade.close_profit == 0.0611052
+    assert trade.close_profit == 0.0620716
 
     assert rpc_mock.call_count == 2
     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',
         'gain': 'profit',
         'limit': 1.172e-05,
-        'amount': 90.99181073703367,
+        'amount': 91.07468123861567,
         'order_type': 'market',
-        'open_rate': 1.099e-05,
-        'current_rate': 1.172e-05,
-        'profit_amount': 6.126e-05,
-        'profit_percent': 0.0611052,
+        'open_rate': 1.098e-05,
+        'current_rate': 1.173e-05,
+        'profit_amount': 6.223e-05,
+        'profit_percent': 0.0620716,
         'stake_currency': 'BTC',
         'fiat_currency': 'USD',
         '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
 
 
-def test_get_sell_rate(default_conf, mocker, caplog, ticker, order_book_l2) -> None:
-
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        get_order_book=order_book_l2,
-        fetch_ticker=ticker,
-    )
+@pytest.mark.parametrize('side,ask,bid,expected', [
+    ('bid', 10.0, 11.0, 11.0),
+    ('bid', 10.0, 11.2, 11.2),
+    ('bid', 10.0, 11.0, 11.0),
+    ('bid', 9.8, 11.0, 11.0),
+    ('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"
 
     # 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)
     assert not log_has("Using cached sell rate for ETH/BTC.", caplog)
     assert isinstance(rate, float)
-    assert rate == 0.00001098
+    assert rate == expected
     # Use caching
     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)
 
-    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
+    default_conf['ask_strategy']['price_side'] = side
     default_conf['ask_strategy']['use_order_book'] = True
     default_conf['ask_strategy']['order_book_min'] = 1
     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)
     rate = ft.get_sell_rate(pair, True)
     assert not log_has("Using cached sell rate for ETH/BTC.", caplog)
     assert isinstance(rate, float)
-    assert rate == 0.043936
+    assert rate == expected
     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)