From 203e22dc387201579dfdce0db09d2e4b47176335 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E0=AE=AE=E0=AE=A9=E0=AF=8B=E0=AE=9C=E0=AF=8D=E0=AE=95?= =?UTF-8?q?=E0=AF=81=E0=AE=AE=E0=AE=BE=E0=AE=B0=E0=AF=8D=20=E0=AE=AA?= =?UTF-8?q?=E0=AE=B4=E0=AE=A9=E0=AE=BF=E0=AE=9A=E0=AF=8D=E0=AE=9A=E0=AE=BE?= =?UTF-8?q?=E0=AE=AE=E0=AE=BF?= Date: Sun, 8 May 2022 13:45:48 +0530 Subject: [PATCH] remove *.orig files --- tests/exchange/test_exchange.py.orig | 5094 -------------------- tests/optimize/test_backtesting.py.orig | 1684 ------- tests/rpc/test_rpc_telegram.py.orig | 2452 ---------- tests/test_freqtradebot.py.orig | 5832 ----------------------- tests/test_persistence.py.orig | 2644 ---------- 5 files changed, 17706 deletions(-) delete mode 100644 tests/exchange/test_exchange.py.orig delete mode 100644 tests/optimize/test_backtesting.py.orig delete mode 100644 tests/rpc/test_rpc_telegram.py.orig delete mode 100644 tests/test_freqtradebot.py.orig delete mode 100644 tests/test_persistence.py.orig diff --git a/tests/exchange/test_exchange.py.orig b/tests/exchange/test_exchange.py.orig deleted file mode 100644 index 3a916579c..000000000 --- a/tests/exchange/test_exchange.py.orig +++ /dev/null @@ -1,5094 +0,0 @@ -import copy -import logging -from copy import deepcopy -from datetime import datetime, timedelta, timezone -from math import isclose -from random import randint -from unittest.mock import MagicMock, Mock, PropertyMock, patch - -import arrow -import ccxt -import pytest -from pandas import DataFrame - -from freqtrade.enums import CandleType, MarginMode, TradingMode -from freqtrade.exceptions import (DDosProtection, DependencyException, InvalidOrderException, - OperationalException, PricingError, TemporaryError) -from freqtrade.exchange import Binance, Bittrex, Exchange, Kraken -from freqtrade.exchange.common import (API_FETCH_ORDER_RETRY_COUNT, API_RETRY_COUNT, - calculate_backoff, remove_credentials) -from freqtrade.exchange.exchange import (market_is_active, timeframe_to_minutes, timeframe_to_msecs, - timeframe_to_next_date, timeframe_to_prev_date, - timeframe_to_seconds) -from freqtrade.resolvers.exchange_resolver import ExchangeResolver -from tests.conftest import get_mock_coro, get_patched_exchange, log_has, log_has_re, num_log_has_re - - -# Make sure to always keep one exchange here which is NOT subclassed!! -EXCHANGES = ['bittrex', 'binance', 'kraken', 'ftx', 'gateio'] - -get_buy_rate_data = [ - ('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', 20, 19, 10, None, 20), # ask_last_balance missing - ('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', 21, 20, 10, 0.0, 20), # Full bid side - ('bid', 21, 20, 10, 1.0, 10), # Full last side - ('bid', 21, 20, 10, 0.5, 15), # Between bid and last - ('bid', 21, 20, 10, 0.7, 13), # Between bid and last - ('bid', 21, 20, 10, 0.3, 17), # Between bid and last - ('bid', 6, 5, 10, 1.0, 5), # last bigger than bid - ('bid', 21, 20, 10, None, 20), # ask_last_balance missing - ('bid', 6, 5, 10, 0.5, 5), # last bigger than bid - ('bid', 21, 20, None, 0.5, 20), # last not available - uses bid - ('bid', 6, 5, None, 0.5, 5), # last not available - uses bid - ('bid', 6, 5, None, 1, 5), # last not available - uses bid - ('bid', 6, 5, None, 0, 5), # last not available - uses bid -] - -get_sell_rate_data = [ - ('bid', 12.0, 11.0, 11.5, 0.0, 11.0), # full bid side - ('bid', 12.0, 11.0, 11.5, 1.0, 11.5), # full last side - ('bid', 12.0, 11.0, 11.5, 0.5, 11.25), # between bid and lat - ('bid', 12.0, 11.2, 10.5, 0.0, 11.2), # Last smaller than bid - ('bid', 12.0, 11.2, 10.5, 1.0, 11.2), # Last smaller than bid - uses bid - ('bid', 12.0, 11.2, 10.5, 0.5, 11.2), # Last smaller than bid - uses bid - ('bid', 0.003, 0.002, 0.005, 0.0, 0.002), - ('bid', 0.003, 0.002, 0.005, None, 0.002), - ('ask', 12.0, 11.0, 12.5, 0.0, 12.0), # full ask side - ('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), - ('ask', 0.006, 1.0, 11.0, None, 0.006), -] - - -def ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name, - fun, mock_ccxt_fun, retries=API_RETRY_COUNT + 1, **kwargs): - - with patch('freqtrade.exchange.common.time.sleep'): - with pytest.raises(DDosProtection): - api_mock.__dict__[mock_ccxt_fun] = MagicMock(side_effect=ccxt.DDoSProtection("DDos")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - getattr(exchange, fun)(**kwargs) - assert api_mock.__dict__[mock_ccxt_fun].call_count == retries - - with pytest.raises(TemporaryError): - api_mock.__dict__[mock_ccxt_fun] = MagicMock(side_effect=ccxt.NetworkError("DeaDBeef")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - getattr(exchange, fun)(**kwargs) - assert api_mock.__dict__[mock_ccxt_fun].call_count == retries - - with pytest.raises(OperationalException): - api_mock.__dict__[mock_ccxt_fun] = MagicMock(side_effect=ccxt.BaseError("DeadBeef")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - getattr(exchange, fun)(**kwargs) - assert api_mock.__dict__[mock_ccxt_fun].call_count == 1 - - -async def async_ccxt_exception(mocker, default_conf, api_mock, fun, mock_ccxt_fun, - retries=API_RETRY_COUNT + 1, **kwargs): - - with patch('freqtrade.exchange.common.asyncio.sleep', get_mock_coro(None)): - with pytest.raises(DDosProtection): - api_mock.__dict__[mock_ccxt_fun] = MagicMock(side_effect=ccxt.DDoSProtection("Dooh")) - exchange = get_patched_exchange(mocker, default_conf, api_mock) - await getattr(exchange, fun)(**kwargs) - assert api_mock.__dict__[mock_ccxt_fun].call_count == retries - - with pytest.raises(TemporaryError): - api_mock.__dict__[mock_ccxt_fun] = MagicMock(side_effect=ccxt.NetworkError("DeadBeef")) - exchange = get_patched_exchange(mocker, default_conf, api_mock) - await getattr(exchange, fun)(**kwargs) - assert api_mock.__dict__[mock_ccxt_fun].call_count == retries - - with pytest.raises(OperationalException): - api_mock.__dict__[mock_ccxt_fun] = MagicMock(side_effect=ccxt.BaseError("DeadBeef")) - exchange = get_patched_exchange(mocker, default_conf, api_mock) - await getattr(exchange, fun)(**kwargs) - assert api_mock.__dict__[mock_ccxt_fun].call_count == 1 - - -def test_init(default_conf, mocker, caplog): - caplog.set_level(logging.INFO) - get_patched_exchange(mocker, default_conf) - assert log_has('Instance is running with dry_run enabled', caplog) - - -def test_remove_credentials(default_conf, caplog) -> None: - conf = deepcopy(default_conf) - conf['dry_run'] = False - remove_credentials(conf) - - assert conf['exchange']['key'] != '' - assert conf['exchange']['secret'] != '' - - conf['dry_run'] = True - remove_credentials(conf) - assert conf['exchange']['key'] == '' - assert conf['exchange']['secret'] == '' - assert conf['exchange']['password'] == '' - assert conf['exchange']['uid'] == '' - - -def test_init_ccxt_kwargs(default_conf, mocker, caplog): - mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={})) - mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency') - caplog.set_level(logging.INFO) - conf = copy.deepcopy(default_conf) - conf['exchange']['ccxt_async_config'] = {'aiohttp_trust_env': True, 'asyncio_loop': True} - ex = Exchange(conf) - assert log_has( - "Applying additional ccxt config: {'aiohttp_trust_env': True, 'asyncio_loop': True}", - caplog) - assert ex._api_async.aiohttp_trust_env - assert not ex._api.aiohttp_trust_env - - # Reset logging and config - caplog.clear() - conf = copy.deepcopy(default_conf) - conf['exchange']['ccxt_config'] = {'TestKWARG': 11} - conf['exchange']['ccxt_sync_config'] = {'TestKWARG44': 11} - conf['exchange']['ccxt_async_config'] = {'asyncio_loop': True} - asynclogmsg = "Applying additional ccxt config: {'TestKWARG': 11, 'asyncio_loop': True}" - ex = Exchange(conf) - assert not ex._api_async.aiohttp_trust_env - assert hasattr(ex._api, 'TestKWARG') - assert ex._api.TestKWARG == 11 - # ccxt_config is assigned to both sync and async - assert not hasattr(ex._api_async, 'TestKWARG44') - - assert hasattr(ex._api_async, 'TestKWARG') - assert log_has("Applying additional ccxt config: {'TestKWARG': 11, 'TestKWARG44': 11}", caplog) - assert log_has(asynclogmsg, caplog) - # Test additional headers case - Exchange._headers = {'hello': 'world'} - ex = Exchange(conf) - - assert log_has("Applying additional ccxt config: {'TestKWARG': 11, 'TestKWARG44': 11}", caplog) - assert ex._api.headers == {'hello': 'world'} - assert ex._ccxt_config == {} - Exchange._headers = {} - - -def test_destroy(default_conf, mocker, caplog): - caplog.set_level(logging.DEBUG) - get_patched_exchange(mocker, default_conf) - assert log_has('Exchange object destroyed, closing async loop', caplog) - - -def test_init_exception(default_conf, mocker): - default_conf['exchange']['name'] = 'wrong_exchange_name' - - with pytest.raises(OperationalException, - match=f"Exchange {default_conf['exchange']['name']} is not supported"): - Exchange(default_conf) - - default_conf['exchange']['name'] = 'binance' - with pytest.raises(OperationalException, - match=f"Exchange {default_conf['exchange']['name']} is not supported"): - mocker.patch("ccxt.binance", MagicMock(side_effect=AttributeError)) - Exchange(default_conf) - - with pytest.raises(OperationalException, - match=r"Initialization of ccxt failed. Reason: DeadBeef"): - mocker.patch("ccxt.binance", MagicMock(side_effect=ccxt.BaseError("DeadBeef"))) - Exchange(default_conf) - - -def test_exchange_resolver(default_conf, mocker, caplog): - mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=MagicMock())) - mocker.patch('freqtrade.exchange.Exchange._load_async_markets') - mocker.patch('freqtrade.exchange.Exchange.validate_pairs') - mocker.patch('freqtrade.exchange.Exchange.validate_timeframes') - mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency') - mocker.patch('freqtrade.exchange.Exchange.validate_pricing') - - exchange = ExchangeResolver.load_exchange('zaif', default_conf) - assert isinstance(exchange, Exchange) - assert log_has_re(r"No .* specific subclass found. Using the generic class instead.", caplog) - caplog.clear() - - exchange = ExchangeResolver.load_exchange('Bittrex', default_conf) - assert isinstance(exchange, Exchange) - assert isinstance(exchange, Bittrex) - assert not log_has_re(r"No .* specific subclass found. Using the generic class instead.", - caplog) - caplog.clear() - - exchange = ExchangeResolver.load_exchange('kraken', default_conf) - assert isinstance(exchange, Exchange) - assert isinstance(exchange, Kraken) - assert not isinstance(exchange, Binance) - assert not log_has_re(r"No .* specific subclass found. Using the generic class instead.", - caplog) - - exchange = ExchangeResolver.load_exchange('binance', default_conf) - assert isinstance(exchange, Exchange) - assert isinstance(exchange, Binance) - assert not isinstance(exchange, Kraken) - - assert not log_has_re(r"No .* specific subclass found. Using the generic class instead.", - caplog) - - # Test mapping - exchange = ExchangeResolver.load_exchange('binanceus', default_conf) - assert isinstance(exchange, Exchange) - assert isinstance(exchange, Binance) - assert not isinstance(exchange, Kraken) - - -def test_validate_order_time_in_force(default_conf, mocker, caplog): - caplog.set_level(logging.INFO) - # explicitly test bittrex, exchanges implementing other policies need separate tests - ex = get_patched_exchange(mocker, default_conf, id="bittrex") - tif = { - "buy": "gtc", - "sell": "gtc", - } - - ex.validate_order_time_in_force(tif) - tif2 = { - "buy": "fok", - "sell": "ioc", - } - with pytest.raises(OperationalException, match=r"Time in force.*not supported for .*"): - ex.validate_order_time_in_force(tif2) - - # Patch to see if this will pass if the values are in the ft dict - ex._ft_has.update({"order_time_in_force": ["gtc", "fok", "ioc"]}) - ex.validate_order_time_in_force(tif2) - - -@pytest.mark.parametrize("amount,precision_mode,precision,contract_size,expected,trading_mode", [ - (2.34559, 2, 4, 1, 2.3455, 'spot'), - (2.34559, 2, 5, 1, 2.34559, 'spot'), - (2.34559, 2, 3, 1, 2.345, 'spot'), - (2.9999, 2, 3, 1, 2.999, 'spot'), - (2.9909, 2, 3, 1, 2.990, 'spot'), - # Tests for Tick-size - (2.34559, 4, 0.0001, 1, 2.3455, 'spot'), - (2.34559, 4, 0.00001, 1, 2.34559, 'spot'), - (2.34559, 4, 0.001, 1, 2.345, 'spot'), - (2.9999, 4, 0.001, 1, 2.999, 'spot'), - (2.9909, 4, 0.001, 1, 2.990, 'spot'), - (2.9909, 4, 0.005, 0.01, 2.99, 'futures'), - (2.9999, 4, 0.005, 10, 2.995, 'futures'), -]) -def test_amount_to_precision( - default_conf, - mocker, - amount, - precision_mode, - precision, - contract_size, - expected, - trading_mode -): - """ - Test rounds down - """ - - markets = PropertyMock(return_value={ - 'ETH/BTC': { - 'contractSize': contract_size, - 'precision': { - 'amount': precision - } - } - }) - - default_conf['trading_mode'] = trading_mode - default_conf['margin_mode'] = 'isolated' - - exchange = get_patched_exchange(mocker, default_conf, id="binance") - # digits counting mode - # DECIMAL_PLACES = 2 - # SIGNIFICANT_DIGITS = 3 - # TICK_SIZE = 4 - mocker.patch('freqtrade.exchange.Exchange.precisionMode', - PropertyMock(return_value=precision_mode)) - mocker.patch('freqtrade.exchange.Exchange.markets', markets) - - pair = 'ETH/BTC' - assert exchange.amount_to_precision(pair, amount) == expected - - -@pytest.mark.parametrize("price,precision_mode,precision,expected", [ - (2.34559, 2, 4, 2.3456), - (2.34559, 2, 5, 2.34559), - (2.34559, 2, 3, 2.346), - (2.9999, 2, 3, 3.000), - (2.9909, 2, 3, 2.991), - # Tests for Tick_size - (2.34559, 4, 0.0001, 2.3456), - (2.34559, 4, 0.00001, 2.34559), - (2.34559, 4, 0.001, 2.346), - (2.9999, 4, 0.001, 3.000), - (2.9909, 4, 0.001, 2.991), - (2.9909, 4, 0.005, 2.995), - (2.9973, 4, 0.005, 3.0), - (2.9977, 4, 0.005, 3.0), - (234.43, 4, 0.5, 234.5), - (234.53, 4, 0.5, 235.0), - (0.891534, 4, 0.0001, 0.8916), - (64968.89, 4, 0.01, 64968.89), - -]) -def test_price_to_precision(default_conf, mocker, price, precision_mode, precision, expected): - """Test price to precision""" - markets = PropertyMock(return_value={'ETH/BTC': {'precision': {'price': precision}}}) - - exchange = get_patched_exchange(mocker, default_conf, id="binance") - mocker.patch('freqtrade.exchange.Exchange.markets', markets) - # digits counting mode - # DECIMAL_PLACES = 2 - # SIGNIFICANT_DIGITS = 3 - # TICK_SIZE = 4 - mocker.patch('freqtrade.exchange.Exchange.precisionMode', - PropertyMock(return_value=precision_mode)) - - pair = 'ETH/BTC' - assert exchange.price_to_precision(pair, price) == expected - - -@pytest.mark.parametrize("price,precision_mode,precision,expected", [ - (2.34559, 2, 4, 0.0001), - (2.34559, 2, 5, 0.00001), - (2.34559, 2, 3, 0.001), - (2.9999, 2, 3, 0.001), - (200.0511, 2, 3, 0.001), - # Tests for Tick_size - (2.34559, 4, 0.0001, 0.0001), - (2.34559, 4, 0.00001, 0.00001), - (2.34559, 4, 0.0025, 0.0025), - (2.9909, 4, 0.0025, 0.0025), - (234.43, 4, 0.5, 0.5), - (234.43, 4, 0.0025, 0.0025), - (234.43, 4, 0.00013, 0.00013), - -]) -def test_price_get_one_pip(default_conf, mocker, price, precision_mode, precision, expected): - markets = PropertyMock(return_value={'ETH/BTC': {'precision': {'price': precision}}}) - exchange = get_patched_exchange(mocker, default_conf, id="binance") - mocker.patch('freqtrade.exchange.Exchange.markets', markets) - mocker.patch('freqtrade.exchange.Exchange.precisionMode', - PropertyMock(return_value=precision_mode)) - pair = 'ETH/BTC' - assert pytest.approx(exchange.price_get_one_pip(pair, price)) == expected - - -def test__get_stake_amount_limit(mocker, default_conf) -> None: - - exchange = get_patched_exchange(mocker, default_conf, id="binance") - stoploss = -0.05 - markets = {'ETH/BTC': {'symbol': 'ETH/BTC'}} - - # no pair found - mocker.patch( - 'freqtrade.exchange.Exchange.markets', - PropertyMock(return_value=markets) - ) - with pytest.raises(ValueError, match=r'.*get market information.*'): - exchange.get_min_pair_stake_amount('BNB/BTC', 1, stoploss) - - # no cost/amount Min - markets["ETH/BTC"]["limits"] = { - 'cost': {'min': None, 'max': None}, - 'amount': {'min': None, 'max': None}, - } - mocker.patch( - 'freqtrade.exchange.Exchange.markets', - PropertyMock(return_value=markets) - ) - result = exchange.get_min_pair_stake_amount('ETH/BTC', 1, stoploss) - assert result is None - result = exchange.get_max_pair_stake_amount('ETH/BTC', 1) - assert result == float('inf') - - # min/max cost is set - markets["ETH/BTC"]["limits"] = { - 'cost': {'min': 2, 'max': 10000}, - 'amount': {'min': None, 'max': None}, - } - mocker.patch( - 'freqtrade.exchange.Exchange.markets', - PropertyMock(return_value=markets) - ) - # min - result = exchange.get_min_pair_stake_amount('ETH/BTC', 1, stoploss) - expected_result = 2 * (1+0.05) / (1-abs(stoploss)) - assert isclose(result, expected_result) - # With Leverage - result = exchange.get_min_pair_stake_amount('ETH/BTC', 1, stoploss, 3.0) - assert isclose(result, expected_result/3) - # max - result = exchange.get_max_pair_stake_amount('ETH/BTC', 2) - assert result == 10000 - - # min amount is set - markets["ETH/BTC"]["limits"] = { - 'cost': {'min': None, 'max': None}, - 'amount': {'min': 2, 'max': 10000}, - } - mocker.patch( - 'freqtrade.exchange.Exchange.markets', - PropertyMock(return_value=markets) - ) - result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, stoploss) - expected_result = 2 * 2 * (1+0.05) / (1-abs(stoploss)) - assert isclose(result, expected_result) - # With Leverage - result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, stoploss, 5.0) - assert isclose(result, expected_result/5) - # max - result = exchange.get_max_pair_stake_amount('ETH/BTC', 2) - assert result == 20000 - - # min amount and cost are set (cost is minimal) - markets["ETH/BTC"]["limits"] = { - 'cost': {'min': 2, 'max': None}, - 'amount': {'min': 2, 'max': None}, - } - mocker.patch( - 'freqtrade.exchange.Exchange.markets', - PropertyMock(return_value=markets) - ) - result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, stoploss) - expected_result = max(2, 2 * 2) * (1+0.05) / (1-abs(stoploss)) - assert isclose(result, expected_result) - # With Leverage - result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, stoploss, 10) - assert isclose(result, expected_result/10) - - # min amount and cost are set (amount is minial) - markets["ETH/BTC"]["limits"] = { - 'cost': {'min': 8, 'max': 10000}, - 'amount': {'min': 2, 'max': 500}, - } - mocker.patch( - 'freqtrade.exchange.Exchange.markets', - PropertyMock(return_value=markets) - ) - result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, stoploss) - expected_result = max(8, 2 * 2) * (1+0.05) / (1-abs(stoploss)) - assert isclose(result, expected_result) - # With Leverage - result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, stoploss, 7.0) - assert isclose(result, expected_result/7.0) - # Max - result = exchange.get_max_pair_stake_amount('ETH/BTC', 2) - assert result == 1000 - - result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, -0.4) - expected_result = max(8, 2 * 2) * 1.5 - assert isclose(result, expected_result) - # With Leverage - result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, -0.4, 8.0) - assert isclose(result, expected_result/8.0) - # Max - result = exchange.get_max_pair_stake_amount('ETH/BTC', 2) - assert result == 1000 - - # Really big stoploss - result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, -1) - expected_result = max(8, 2 * 2) * 1.5 - assert isclose(result, expected_result) - # With Leverage - result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, -1, 12.0) - assert isclose(result, expected_result/12) - # Max - result = exchange.get_max_pair_stake_amount('ETH/BTC', 2) - assert result == 1000 - - markets["ETH/BTC"]["contractSize"] = '0.01' - default_conf['trading_mode'] = 'futures' - default_conf['margin_mode'] = 'isolated' - exchange = get_patched_exchange(mocker, default_conf, id="binance") - mocker.patch( - 'freqtrade.exchange.Exchange.markets', - PropertyMock(return_value=markets) - ) - - # Contract size 0.01 - result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, -1) - assert isclose(result, expected_result * 0.01) - # Max - result = exchange.get_max_pair_stake_amount('ETH/BTC', 2) - assert result == 10 - - markets["ETH/BTC"]["contractSize"] = '10' - mocker.patch( - 'freqtrade.exchange.Exchange.markets', - PropertyMock(return_value=markets) - ) - # With Leverage, Contract size 10 - result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, -1, 12.0) - assert isclose(result, (expected_result/12) * 10.0) - # Max - result = exchange.get_max_pair_stake_amount('ETH/BTC', 2) - assert result == 10000 - - -def test_get_min_pair_stake_amount_real_data(mocker, default_conf) -> None: - exchange = get_patched_exchange(mocker, default_conf, id="binance") - stoploss = -0.05 - markets = {'ETH/BTC': {'symbol': 'ETH/BTC'}} - - # ~Real Binance data - markets["ETH/BTC"]["limits"] = { - 'cost': {'min': 0.0001, 'max': 4000}, - 'amount': {'min': 0.001, 'max': 10000}, - } - mocker.patch( - 'freqtrade.exchange.Exchange.markets', - PropertyMock(return_value=markets) - ) - result = exchange.get_min_pair_stake_amount('ETH/BTC', 0.020405, stoploss) - expected_result = max(0.0001, 0.001 * 0.020405) * (1+0.05) / (1-abs(stoploss)) - assert round(result, 8) == round(expected_result, 8) - # Max - result = exchange.get_max_pair_stake_amount('ETH/BTC', 2.0) - assert result == 4000 - - # Leverage - result = exchange.get_min_pair_stake_amount('ETH/BTC', 0.020405, stoploss, 3.0) - assert round(result, 8) == round(expected_result/3, 8) - - # Contract_size - markets["ETH/BTC"]["contractSize"] = 0.1 - result = exchange.get_min_pair_stake_amount('ETH/BTC', 0.020405, stoploss, 3.0) - assert round(result, 8) == round((expected_result/3), 8) - - # Max - result = exchange.get_max_pair_stake_amount('ETH/BTC', 12.0) - assert result == 4000 - - -def test_set_sandbox(default_conf, mocker): - """ - Test working scenario - """ - api_mock = MagicMock() - api_mock.load_markets = MagicMock(return_value={ - 'ETH/BTC': '', 'LTC/BTC': '', 'XRP/BTC': '', 'NEO/BTC': '' - }) - url_mock = PropertyMock(return_value={'test': "api-public.sandbox.gdax.com", - 'api': 'https://api.gdax.com'}) - type(api_mock).urls = url_mock - exchange = get_patched_exchange(mocker, default_conf, api_mock) - liveurl = exchange._api.urls['api'] - default_conf['exchange']['sandbox'] = True - exchange.set_sandbox(exchange._api, default_conf['exchange'], 'Logname') - assert exchange._api.urls['api'] != liveurl - - -def test_set_sandbox_exception(default_conf, mocker): - """ - Test Fail scenario - """ - api_mock = MagicMock() - api_mock.load_markets = MagicMock(return_value={ - 'ETH/BTC': '', 'LTC/BTC': '', 'XRP/BTC': '', 'NEO/BTC': '' - }) - url_mock = PropertyMock(return_value={'api': 'https://api.gdax.com'}) - type(api_mock).urls = url_mock - - with pytest.raises(OperationalException, match=r'does not provide a sandbox api'): - exchange = get_patched_exchange(mocker, default_conf, api_mock) - default_conf['exchange']['sandbox'] = True - exchange.set_sandbox(exchange._api, default_conf['exchange'], 'Logname') - - -def test__load_async_markets(default_conf, mocker, caplog): - mocker.patch('freqtrade.exchange.Exchange._init_ccxt') - mocker.patch('freqtrade.exchange.Exchange.validate_pairs') - mocker.patch('freqtrade.exchange.Exchange.validate_timeframes') - mocker.patch('freqtrade.exchange.Exchange._load_markets') - mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency') - mocker.patch('freqtrade.exchange.Exchange.validate_pricing') - exchange = Exchange(default_conf) - exchange._api_async.load_markets = get_mock_coro(None) - exchange._load_async_markets() - assert exchange._api_async.load_markets.call_count == 1 - caplog.set_level(logging.DEBUG) - - exchange._api_async.load_markets = Mock(side_effect=ccxt.BaseError("deadbeef")) - exchange._load_async_markets() - - assert log_has('Could not load async markets. Reason: deadbeef', caplog) - - -def test__load_markets(default_conf, mocker, caplog): - caplog.set_level(logging.INFO) - api_mock = MagicMock() - api_mock.load_markets = MagicMock(side_effect=ccxt.BaseError("SomeError")) - mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) - mocker.patch('freqtrade.exchange.Exchange.validate_pairs') - mocker.patch('freqtrade.exchange.Exchange.validate_timeframes') - mocker.patch('freqtrade.exchange.Exchange._load_async_markets') - mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency') - mocker.patch('freqtrade.exchange.Exchange.validate_pricing') - Exchange(default_conf) - assert log_has('Unable to initialize markets.', caplog) - - expected_return = {'ETH/BTC': 'available'} - api_mock = MagicMock() - api_mock.load_markets = MagicMock(return_value=expected_return) - mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) - default_conf['exchange']['pair_whitelist'] = ['ETH/BTC'] - ex = Exchange(default_conf) - - assert ex.markets == expected_return - - -def test_reload_markets(default_conf, mocker, caplog): - caplog.set_level(logging.DEBUG) - initial_markets = {'ETH/BTC': {}} - updated_markets = {'ETH/BTC': {}, "LTC/BTC": {}} - - api_mock = MagicMock() - api_mock.load_markets = MagicMock(return_value=initial_markets) - default_conf['exchange']['markets_refresh_interval'] = 10 - exchange = get_patched_exchange(mocker, default_conf, api_mock, id="binance", - mock_markets=False) - exchange._load_async_markets = MagicMock() - exchange._last_markets_refresh = arrow.utcnow().int_timestamp - - assert exchange.markets == initial_markets - - # less than 10 minutes have passed, no reload - exchange.reload_markets() - assert exchange.markets == initial_markets - assert exchange._load_async_markets.call_count == 0 - - api_mock.load_markets = MagicMock(return_value=updated_markets) - # more than 10 minutes have passed, reload is executed - exchange._last_markets_refresh = arrow.utcnow().int_timestamp - 15 * 60 - exchange.reload_markets() - assert exchange.markets == updated_markets - assert exchange._load_async_markets.call_count == 1 - assert log_has('Performing scheduled market reload..', caplog) - - -def test_reload_markets_exception(default_conf, mocker, caplog): - caplog.set_level(logging.DEBUG) - - api_mock = MagicMock() - api_mock.load_markets = MagicMock(side_effect=ccxt.NetworkError("LoadError")) - default_conf['exchange']['markets_refresh_interval'] = 10 - exchange = get_patched_exchange(mocker, default_conf, api_mock, id="binance") - - # less than 10 minutes have passed, no reload - exchange.reload_markets() - assert exchange._last_markets_refresh == 0 - assert log_has_re(r"Could not reload markets.*", caplog) - - -@pytest.mark.parametrize("stake_currency", ['ETH', 'BTC', 'USDT']) -def test_validate_stakecurrency(default_conf, stake_currency, mocker, caplog): - default_conf['stake_currency'] = stake_currency - api_mock = MagicMock() - type(api_mock).load_markets = MagicMock(return_value={ - 'ETH/BTC': {'quote': 'BTC'}, 'LTC/BTC': {'quote': 'BTC'}, - 'XRP/ETH': {'quote': 'ETH'}, 'NEO/USDT': {'quote': 'USDT'}, - }) - mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) - mocker.patch('freqtrade.exchange.Exchange.validate_pairs') - mocker.patch('freqtrade.exchange.Exchange.validate_timeframes') - mocker.patch('freqtrade.exchange.Exchange._load_async_markets') - mocker.patch('freqtrade.exchange.Exchange.validate_pricing') - Exchange(default_conf) - - -def test_validate_stakecurrency_error(default_conf, mocker, caplog): - default_conf['stake_currency'] = 'XRP' - api_mock = MagicMock() - type(api_mock).load_markets = MagicMock(return_value={ - 'ETH/BTC': {'quote': 'BTC'}, 'LTC/BTC': {'quote': 'BTC'}, - 'XRP/ETH': {'quote': 'ETH'}, 'NEO/USDT': {'quote': 'USDT'}, - }) - mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) - mocker.patch('freqtrade.exchange.Exchange.validate_pairs') - mocker.patch('freqtrade.exchange.Exchange.validate_timeframes') - mocker.patch('freqtrade.exchange.Exchange._load_async_markets') - with pytest.raises(OperationalException, - match=r'XRP is not available as stake on .*' - 'Available currencies are: BTC, ETH, USDT'): - Exchange(default_conf) - - type(api_mock).load_markets = MagicMock(side_effect=ccxt.NetworkError('No connection.')) - mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) - - with pytest.raises(OperationalException, - match=r'Could not load markets, therefore cannot start\. Please.*'): - Exchange(default_conf) - - -def test_get_quote_currencies(default_conf, mocker): - ex = get_patched_exchange(mocker, default_conf) - - assert set(ex.get_quote_currencies()) == set(['USD', 'ETH', 'BTC', 'USDT', 'BUSD']) - - -@pytest.mark.parametrize('pair,expected', [ - ('XRP/BTC', 'BTC'), - ('LTC/USD', 'USD'), - ('ETH/USDT', 'USDT'), - ('XLTCUSDT', 'USDT'), - ('XRP/NOCURRENCY', ''), -]) -def test_get_pair_quote_currency(default_conf, mocker, pair, expected): - ex = get_patched_exchange(mocker, default_conf) - assert ex.get_pair_quote_currency(pair) == expected - - -@pytest.mark.parametrize('pair,expected', [ - ('XRP/BTC', 'XRP'), - ('LTC/USD', 'LTC'), - ('ETH/USDT', 'ETH'), - ('XLTCUSDT', 'LTC'), - ('XRP/NOCURRENCY', ''), -]) -def test_get_pair_base_currency(default_conf, mocker, pair, expected): - ex = get_patched_exchange(mocker, default_conf) - assert ex.get_pair_base_currency(pair) == expected - - -def test_validate_pairs(default_conf, mocker): # test exchange.validate_pairs directly - api_mock = MagicMock() - type(api_mock).load_markets = MagicMock(return_value={ - 'ETH/BTC': {'quote': 'BTC'}, - 'LTC/BTC': {'quote': 'BTC'}, - 'XRP/BTC': {'quote': 'BTC'}, - 'NEO/BTC': {'quote': 'BTC'}, - }) - id_mock = PropertyMock(return_value='test_exchange') - type(api_mock).id = id_mock - - mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) - mocker.patch('freqtrade.exchange.Exchange.validate_timeframes') - mocker.patch('freqtrade.exchange.Exchange._load_async_markets') - mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency') - mocker.patch('freqtrade.exchange.Exchange.validate_pricing') - Exchange(default_conf) - - -def test_validate_pairs_not_available(default_conf, mocker): - api_mock = MagicMock() - type(api_mock).markets = PropertyMock(return_value={ - 'XRP/BTC': {'inactive': True, 'base': 'XRP', 'quote': 'BTC'} - }) - mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) - mocker.patch('freqtrade.exchange.Exchange.validate_timeframes') - mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency') - mocker.patch('freqtrade.exchange.Exchange._load_async_markets') - - with pytest.raises(OperationalException, match=r'not available'): - Exchange(default_conf) - - -def test_validate_pairs_exception(default_conf, mocker, caplog): - caplog.set_level(logging.INFO) - api_mock = MagicMock() - mocker.patch('freqtrade.exchange.Exchange.name', PropertyMock(return_value='Binance')) - - type(api_mock).markets = PropertyMock(return_value={}) - mocker.patch('freqtrade.exchange.Exchange._init_ccxt', api_mock) - mocker.patch('freqtrade.exchange.Exchange.validate_timeframes') - mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency') - mocker.patch('freqtrade.exchange.Exchange.validate_pricing') - mocker.patch('freqtrade.exchange.Exchange._load_async_markets') - - with pytest.raises(OperationalException, match=r'Pair ETH/BTC is not available on Binance'): - Exchange(default_conf) - - mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value={})) - Exchange(default_conf) - assert log_has('Unable to validate pairs (assuming they are correct).', caplog) - - -def test_validate_pairs_restricted(default_conf, mocker, caplog): - api_mock = MagicMock() - type(api_mock).load_markets = MagicMock(return_value={ - 'ETH/BTC': {'quote': 'BTC'}, 'LTC/BTC': {'quote': 'BTC'}, - 'XRP/BTC': {'quote': 'BTC', 'info': {'prohibitedIn': ['US']}}, - 'NEO/BTC': {'quote': 'BTC', 'info': 'TestString'}, # info can also be a string ... - }) - mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) - mocker.patch('freqtrade.exchange.Exchange.validate_timeframes') - mocker.patch('freqtrade.exchange.Exchange._load_async_markets') - mocker.patch('freqtrade.exchange.Exchange.validate_pricing') - mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency') - - Exchange(default_conf) - assert log_has("Pair XRP/BTC is restricted for some users on this exchange." - "Please check if you are impacted by this restriction " - "on the exchange and eventually remove XRP/BTC from your whitelist.", caplog) - - -def test_validate_pairs_stakecompatibility(default_conf, mocker, caplog): - api_mock = MagicMock() - type(api_mock).load_markets = MagicMock(return_value={ - 'ETH/BTC': {'quote': 'BTC'}, 'LTC/BTC': {'quote': 'BTC'}, - 'XRP/BTC': {'quote': 'BTC'}, 'NEO/BTC': {'quote': 'BTC'}, - 'HELLO-WORLD': {'quote': 'BTC'}, - }) - mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) - mocker.patch('freqtrade.exchange.Exchange.validate_timeframes') - mocker.patch('freqtrade.exchange.Exchange._load_async_markets') - mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency') - mocker.patch('freqtrade.exchange.Exchange.validate_pricing') - - Exchange(default_conf) - - -def test_validate_pairs_stakecompatibility_downloaddata(default_conf, mocker, caplog): - api_mock = MagicMock() - default_conf['stake_currency'] = '' - type(api_mock).load_markets = MagicMock(return_value={ - 'ETH/BTC': {'quote': 'BTC'}, 'LTC/BTC': {'quote': 'BTC'}, - 'XRP/BTC': {'quote': 'BTC'}, 'NEO/BTC': {'quote': 'BTC'}, - 'HELLO-WORLD': {'quote': 'BTC'}, - }) - mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) - mocker.patch('freqtrade.exchange.Exchange.validate_timeframes') - mocker.patch('freqtrade.exchange.Exchange._load_async_markets') - mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency') - mocker.patch('freqtrade.exchange.Exchange.validate_pricing') - - Exchange(default_conf) - assert type(api_mock).load_markets.call_count == 1 - - -def test_validate_pairs_stakecompatibility_fail(default_conf, mocker, caplog): - default_conf['exchange']['pair_whitelist'].append('HELLO-WORLD') - api_mock = MagicMock() - type(api_mock).load_markets = MagicMock(return_value={ - 'ETH/BTC': {'quote': 'BTC'}, 'LTC/BTC': {'quote': 'BTC'}, - 'XRP/BTC': {'quote': 'BTC'}, 'NEO/BTC': {'quote': 'BTC'}, - 'HELLO-WORLD': {'quote': 'USDT'}, - }) - mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) - mocker.patch('freqtrade.exchange.Exchange.validate_timeframes') - mocker.patch('freqtrade.exchange.Exchange._load_async_markets') - mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency') - - with pytest.raises(OperationalException, match=r"Stake-currency 'BTC' not compatible with.*"): - Exchange(default_conf) - - -@pytest.mark.parametrize("timeframe", [ - ('5m'), ("1m"), ("15m"), ("1h") -]) -def test_validate_timeframes(default_conf, mocker, timeframe): - default_conf["timeframe"] = timeframe - api_mock = MagicMock() - id_mock = PropertyMock(return_value='test_exchange') - type(api_mock).id = id_mock - timeframes = PropertyMock(return_value={'1m': '1m', - '5m': '5m', - '15m': '15m', - '1h': '1h'}) - type(api_mock).timeframes = timeframes - - mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) - mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={})) - mocker.patch('freqtrade.exchange.Exchange.validate_pairs') - mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency') - mocker.patch('freqtrade.exchange.Exchange.validate_pricing') - Exchange(default_conf) - - -def test_validate_timeframes_failed(default_conf, mocker): - default_conf["timeframe"] = "3m" - api_mock = MagicMock() - id_mock = PropertyMock(return_value='test_exchange') - type(api_mock).id = id_mock - timeframes = PropertyMock(return_value={'15s': '15s', - '1m': '1m', - '5m': '5m', - '15m': '15m', - '1h': '1h'}) - type(api_mock).timeframes = timeframes - - mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) - mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={})) - mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock()) - with pytest.raises(OperationalException, - match=r"Invalid timeframe '3m'. This exchange supports.*"): - Exchange(default_conf) - default_conf["timeframe"] = "15s" - - with pytest.raises(OperationalException, - match=r"Timeframes < 1m are currently not supported by Freqtrade."): - Exchange(default_conf) - - -def test_validate_timeframes_emulated_ohlcv_1(default_conf, mocker): - default_conf["timeframe"] = "3m" - api_mock = MagicMock() - id_mock = PropertyMock(return_value='test_exchange') - type(api_mock).id = id_mock - - # delete timeframes so magicmock does not autocreate it - del api_mock.timeframes - - mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) - mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={})) - mocker.patch('freqtrade.exchange.Exchange.validate_pairs') - mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency') - with pytest.raises(OperationalException, - match=r'The ccxt library does not provide the list of timeframes ' - r'for the exchange ".*" and this exchange ' - r'is therefore not supported. *'): - Exchange(default_conf) - - -def test_validate_timeframes_emulated_ohlcvi_2(default_conf, mocker): - default_conf["timeframe"] = "3m" - api_mock = MagicMock() - id_mock = PropertyMock(return_value='test_exchange') - type(api_mock).id = id_mock - - # delete timeframes so magicmock does not autocreate it - del api_mock.timeframes - - mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) - mocker.patch('freqtrade.exchange.Exchange._load_markets', - MagicMock(return_value={'timeframes': None})) - mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock()) - mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency') - with pytest.raises(OperationalException, - match=r'The ccxt library does not provide the list of timeframes ' - r'for the exchange ".*" and this exchange ' - r'is therefore not supported. *'): - Exchange(default_conf) - - -def test_validate_timeframes_not_in_config(default_conf, mocker): - del default_conf["timeframe"] - api_mock = MagicMock() - id_mock = PropertyMock(return_value='test_exchange') - type(api_mock).id = id_mock - timeframes = PropertyMock(return_value={'1m': '1m', - '5m': '5m', - '15m': '15m', - '1h': '1h'}) - type(api_mock).timeframes = timeframes - - mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) - mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={})) - mocker.patch('freqtrade.exchange.Exchange.validate_pairs') - mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency') - mocker.patch('freqtrade.exchange.Exchange.validate_pricing') - Exchange(default_conf) - - -def test_validate_pricing(default_conf, mocker): - api_mock = MagicMock() - has = { - 'fetchL2OrderBook': True, - 'fetchTicker': True, - } - type(api_mock).has = PropertyMock(return_value=has) - mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) - mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={})) - mocker.patch('freqtrade.exchange.exchange.Exchange.validate_trading_mode_and_margin_mode') - mocker.patch('freqtrade.exchange.Exchange.validate_pairs') - mocker.patch('freqtrade.exchange.Exchange.validate_timeframes') - mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency') - mocker.patch('freqtrade.exchange.Exchange.name', 'Binance') - ExchangeResolver.load_exchange('binance', default_conf) - has.update({'fetchTicker': False}) - with pytest.raises(OperationalException, match="Ticker pricing not available for .*"): - ExchangeResolver.load_exchange('binance', default_conf) - - has.update({'fetchTicker': True}) - - default_conf['exit_pricing']['use_order_book'] = True - ExchangeResolver.load_exchange('binance', default_conf) - has.update({'fetchL2OrderBook': False}) - - with pytest.raises(OperationalException, match="Orderbook not available for .*"): - ExchangeResolver.load_exchange('binance', default_conf) - - has.update({'fetchL2OrderBook': True}) - - # Binance has no tickers on futures - default_conf['trading_mode'] = TradingMode.FUTURES - default_conf['margin_mode'] = MarginMode.ISOLATED - - with pytest.raises(OperationalException, match="Ticker pricing not available for .*"): - ExchangeResolver.load_exchange('binance', default_conf) - - -def test_validate_ordertypes(default_conf, mocker): - api_mock = MagicMock() - - type(api_mock).has = PropertyMock(return_value={'createMarketOrder': True}) - mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) - mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={})) - mocker.patch('freqtrade.exchange.Exchange.validate_pairs') - mocker.patch('freqtrade.exchange.Exchange.validate_timeframes') - mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency') - mocker.patch('freqtrade.exchange.Exchange.validate_pricing') - mocker.patch('freqtrade.exchange.Exchange.name', 'Bittrex') - - default_conf['order_types'] = { - 'entry': 'limit', - 'exit': 'limit', - 'stoploss': 'market', - 'stoploss_on_exchange': False - } - Exchange(default_conf) - - type(api_mock).has = PropertyMock(return_value={'createMarketOrder': False}) - mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) - - default_conf['order_types'] = { - 'entry': 'limit', - 'exit': 'limit', - 'stoploss': 'market', - 'stoploss_on_exchange': False - } - with pytest.raises(OperationalException, - match=r'Exchange .* does not support market orders.'): - Exchange(default_conf) - - default_conf['order_types'] = { - 'entry': 'limit', - 'exit': 'limit', - 'stoploss': 'limit', - 'stoploss_on_exchange': True - } - with pytest.raises(OperationalException, - match=r'On exchange stoploss is not supported for .*'): - Exchange(default_conf) - - -def test_validate_order_types_not_in_config(default_conf, mocker): - api_mock = MagicMock() - mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock)) - mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={})) - mocker.patch('freqtrade.exchange.Exchange.validate_pairs') - mocker.patch('freqtrade.exchange.Exchange.validate_timeframes') - mocker.patch('freqtrade.exchange.Exchange.validate_pricing') - mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency') - - conf = copy.deepcopy(default_conf) - Exchange(conf) - - -def test_validate_required_startup_candles(default_conf, mocker, caplog): - api_mock = MagicMock() - mocker.patch('freqtrade.exchange.Exchange.name', PropertyMock(return_value='Binance')) - - mocker.patch('freqtrade.exchange.Exchange._init_ccxt', api_mock) - mocker.patch('freqtrade.exchange.Exchange.validate_timeframes') - mocker.patch('freqtrade.exchange.Exchange._load_async_markets') - mocker.patch('freqtrade.exchange.Exchange.validate_pairs') - mocker.patch('freqtrade.exchange.Exchange.validate_pricing') - mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency') - - default_conf['startup_candle_count'] = 20 - ex = Exchange(default_conf) - assert ex - # assumption is that the exchange provides 500 candles per call.s - assert ex.validate_required_startup_candles(200, '5m') == 1 - assert ex.validate_required_startup_candles(499, '5m') == 1 - assert ex.validate_required_startup_candles(600, '5m') == 2 - assert ex.validate_required_startup_candles(501, '5m') == 2 - assert ex.validate_required_startup_candles(499, '5m') == 1 - assert ex.validate_required_startup_candles(1000, '5m') == 3 - assert ex.validate_required_startup_candles(2499, '5m') == 5 - assert log_has_re(r'Using 5 calls to get OHLCV. This.*', caplog) - - with pytest.raises(OperationalException, match=r'This strategy requires 2500.*'): - ex.validate_required_startup_candles(2500, '5m') - - # Ensure the same also happens on init - default_conf['startup_candle_count'] = 6000 - with pytest.raises(OperationalException, match=r'This strategy requires 6000.*'): - Exchange(default_conf) - - -def test_exchange_has(default_conf, mocker): - exchange = get_patched_exchange(mocker, default_conf) - assert not exchange.exchange_has('ASDFASDF') - api_mock = MagicMock() - - type(api_mock).has = PropertyMock(return_value={'deadbeef': True}) - exchange = get_patched_exchange(mocker, default_conf, api_mock) - assert exchange.exchange_has("deadbeef") - - type(api_mock).has = PropertyMock(return_value={'deadbeef': False}) - exchange = get_patched_exchange(mocker, default_conf, api_mock) - assert not exchange.exchange_has("deadbeef") - - -@pytest.mark.parametrize("side,leverage", [ - ("buy", 1), - ("buy", 5), - ("sell", 1.0), - ("sell", 5.0), -]) -@pytest.mark.parametrize("exchange_name", EXCHANGES) -def test_create_dry_run_order(default_conf, mocker, side, exchange_name, leverage): - default_conf['dry_run'] = True - exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) - - order = exchange.create_dry_run_order( - pair='ETH/BTC', - ordertype='limit', - side=side, - amount=1, - rate=200, - leverage=leverage - ) - assert 'id' in order - assert f'dry_run_{side}_' in order["id"] - assert order["side"] == side - assert order["type"] == "limit" - assert order["symbol"] == "ETH/BTC" - assert order["amount"] == 1 - assert order["leverage"] == leverage - assert order["cost"] == 1 * 200 / leverage - - -@pytest.mark.parametrize("side,startprice,endprice", [ - ("buy", 25.563, 25.566), - ("sell", 25.566, 25.563) -]) -@pytest.mark.parametrize("exchange_name", EXCHANGES) -def test_create_dry_run_order_limit_fill(default_conf, mocker, side, startprice, endprice, - exchange_name, order_book_l2_usd): - default_conf['dry_run'] = True - exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) - mocker.patch.multiple('freqtrade.exchange.Exchange', - exchange_has=MagicMock(return_value=True), - fetch_l2_order_book=order_book_l2_usd, - ) - - order = exchange.create_dry_run_order( - pair='LTC/USDT', - ordertype='limit', - side=side, - amount=1, - rate=startprice, - leverage=1.0 - ) - assert order_book_l2_usd.call_count == 1 - assert 'id' in order - assert f'dry_run_{side}_' in order["id"] - assert order["side"] == side - assert order["type"] == "limit" - assert order["symbol"] == "LTC/USDT" - order_book_l2_usd.reset_mock() - - order_closed = exchange.fetch_dry_run_order(order['id']) - assert order_book_l2_usd.call_count == 1 - assert order_closed['status'] == 'open' - assert not order['fee'] - assert order_closed['filled'] == 0 - - order_book_l2_usd.reset_mock() - order_closed['price'] = endprice - - order_closed = exchange.fetch_dry_run_order(order['id']) - assert order_closed['status'] == 'closed' - assert order['fee'] - assert order_closed['filled'] == 1 - assert order_closed['filled'] == order_closed['amount'] - - # Empty orderbook test - mocker.patch('freqtrade.exchange.Exchange.fetch_l2_order_book', - return_value={'asks': [], 'bids': []}) - exchange._dry_run_open_orders[order['id']]['status'] = 'open' - order_closed = exchange.fetch_dry_run_order(order['id']) - - -@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, rate, amount, endprice, - exchange_name, order_book_l2_usd): - default_conf['dry_run'] = True - exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) - mocker.patch.multiple('freqtrade.exchange.Exchange', - exchange_has=MagicMock(return_value=True), - fetch_l2_order_book=order_book_l2_usd, - ) - - order = exchange.create_dry_run_order( - pair='LTC/USDT', - ordertype='market', - side=side, - amount=amount, - rate=rate, - leverage=1.0 - ) - assert 'id' in order - assert f'dry_run_{side}_' in order["id"] - assert order["side"] == side - assert order["type"] == "market" - assert order["symbol"] == "LTC/USDT" - assert order['status'] == 'closed' - assert order['filled'] == amount - assert round(order["average"], 4) == round(endprice, 4) - - -@pytest.mark.parametrize("side", ["buy", "sell"]) -@pytest.mark.parametrize("ordertype,rate,marketprice", [ - ("market", None, None), - ("market", 200, True), - ("limit", 200, None), - ("stop_loss_limit", 200, None) -]) -@pytest.mark.parametrize("exchange_name", EXCHANGES) -def test_create_order(default_conf, mocker, side, ordertype, rate, marketprice, exchange_name): - api_mock = MagicMock() - order_id = 'test_prod_{}_{}'.format(side, randint(0, 10 ** 6)) - api_mock.options = {} if not marketprice else {"createMarketBuyOrderRequiresPrice": True} - api_mock.create_order = MagicMock(return_value={ - 'id': order_id, - 'info': { - 'foo': 'bar' - }, - 'symbol': 'XLTCUSDT', - 'amount': 1 - }) - default_conf['dry_run'] = False - default_conf['margin_mode'] = 'isolated' - mocker.patch('freqtrade.exchange.Exchange.amount_to_precision', lambda s, x, y: y) - mocker.patch('freqtrade.exchange.Exchange.price_to_precision', lambda s, x, y: y) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange._set_leverage = MagicMock() - exchange.set_margin_mode = MagicMock() - - order = exchange.create_order( - pair='XLTCUSDT', - ordertype=ordertype, - side=side, - amount=1, - rate=rate, - leverage=1.0 - ) - - assert 'id' in order - assert 'info' in order - assert order['id'] == order_id - assert order['amount'] == 1 - assert api_mock.create_order.call_args[0][0] == 'XLTCUSDT' - assert api_mock.create_order.call_args[0][1] == ordertype - assert api_mock.create_order.call_args[0][2] == side - assert api_mock.create_order.call_args[0][3] == 1 - assert api_mock.create_order.call_args[0][4] is rate - assert exchange._set_leverage.call_count == 0 - assert exchange.set_margin_mode.call_count == 0 - - api_mock.create_order = MagicMock(return_value={ - 'id': order_id, - 'info': { - 'foo': 'bar' - }, - 'symbol': 'ADA/USDT:USDT', - 'amount': 1 - }) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange.trading_mode = TradingMode.FUTURES - exchange._set_leverage = MagicMock() - exchange.set_margin_mode = MagicMock() - order = exchange.create_order( - pair='ADA/USDT:USDT', - ordertype=ordertype, - side=side, - amount=1, - rate=200, - leverage=3.0 - ) - - assert exchange._set_leverage.call_count == 1 - assert exchange.set_margin_mode.call_count == 1 - assert order['amount'] == 0.01 - - -def test_buy_dry_run(default_conf, mocker): - default_conf['dry_run'] = True - exchange = get_patched_exchange(mocker, default_conf) - - order = exchange.create_order(pair='ETH/BTC', ordertype='limit', side="buy", - amount=1, rate=200, leverage=1.0, - time_in_force='gtc') - assert 'id' in order - assert 'dry_run_buy_' in order['id'] - - -@pytest.mark.parametrize("exchange_name", EXCHANGES) -def test_buy_prod(default_conf, mocker, exchange_name): - api_mock = MagicMock() - order_id = 'test_prod_buy_{}'.format(randint(0, 10 ** 6)) - order_type = 'market' - time_in_force = 'gtc' - api_mock.options = {} - api_mock.create_order = MagicMock(return_value={ - 'id': order_id, - 'symbol': 'ETH/BTC', - 'info': { - 'foo': 'bar' - } - }) - default_conf['dry_run'] = False - mocker.patch('freqtrade.exchange.Exchange.amount_to_precision', lambda s, x, y: y) - mocker.patch('freqtrade.exchange.Exchange.price_to_precision', lambda s, x, y: y) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - - order = exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="buy", - amount=1, rate=200, leverage=1.0, - time_in_force=time_in_force) - - assert 'id' in order - assert 'info' in order - assert order['id'] == order_id - assert api_mock.create_order.call_args[0][0] == 'ETH/BTC' - assert api_mock.create_order.call_args[0][1] == order_type - assert api_mock.create_order.call_args[0][2] == 'buy' - assert api_mock.create_order.call_args[0][3] == 1 - assert api_mock.create_order.call_args[0][4] is None - - api_mock.create_order.reset_mock() - order_type = 'limit' - order = exchange.create_order( - pair='ETH/BTC', - ordertype=order_type, - side="buy", - amount=1, - rate=200, - leverage=1.0, - time_in_force=time_in_force - ) - assert api_mock.create_order.call_args[0][0] == 'ETH/BTC' - assert api_mock.create_order.call_args[0][1] == order_type - assert api_mock.create_order.call_args[0][2] == 'buy' - assert api_mock.create_order.call_args[0][3] == 1 - assert api_mock.create_order.call_args[0][4] == 200 - - # test exception handling - with pytest.raises(DependencyException): - api_mock.create_order = MagicMock(side_effect=ccxt.InsufficientFunds("Not enough funds")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="buy", - amount=1, rate=200, leverage=1.0, - time_in_force=time_in_force) - - with pytest.raises(DependencyException): - api_mock.create_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange.create_order(pair='ETH/BTC', ordertype='limit', side="buy", - amount=1, rate=200, leverage=1.0, - time_in_force=time_in_force) - - with pytest.raises(DependencyException): - api_mock.create_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange.create_order(pair='ETH/BTC', ordertype='market', side="buy", - amount=1, rate=200, leverage=1.0, - time_in_force=time_in_force) - - with pytest.raises(TemporaryError): - api_mock.create_order = MagicMock(side_effect=ccxt.NetworkError("Network disconnect")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="buy", - amount=1, rate=200, leverage=1.0, - time_in_force=time_in_force) - - with pytest.raises(OperationalException): - api_mock.create_order = MagicMock(side_effect=ccxt.BaseError("Unknown error")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="buy", - amount=1, rate=200, leverage=1.0, - time_in_force=time_in_force) - - -@pytest.mark.parametrize("exchange_name", EXCHANGES) -def test_buy_considers_time_in_force(default_conf, mocker, exchange_name): - api_mock = MagicMock() - order_id = 'test_prod_buy_{}'.format(randint(0, 10 ** 6)) - api_mock.options = {} - api_mock.create_order = MagicMock(return_value={ - 'id': order_id, - 'symbol': 'ETH/BTC', - 'info': { - 'foo': 'bar' - } - }) - default_conf['dry_run'] = False - mocker.patch('freqtrade.exchange.Exchange.amount_to_precision', lambda s, x, y: y) - mocker.patch('freqtrade.exchange.Exchange.price_to_precision', lambda s, x, y: y) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - - order_type = 'limit' - time_in_force = 'ioc' - - order = exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="buy", - amount=1, rate=200, leverage=1.0, - time_in_force=time_in_force) - - assert 'id' in order - assert 'info' in order - assert order['id'] == order_id - assert api_mock.create_order.call_args[0][0] == 'ETH/BTC' - assert api_mock.create_order.call_args[0][1] == order_type - assert api_mock.create_order.call_args[0][2] == 'buy' - assert api_mock.create_order.call_args[0][3] == 1 - assert api_mock.create_order.call_args[0][4] == 200 - assert "timeInForce" in api_mock.create_order.call_args[0][5] - assert api_mock.create_order.call_args[0][5]["timeInForce"] == time_in_force - - order_type = 'market' - time_in_force = 'ioc' - - order = exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="buy", - amount=1, rate=200, leverage=1.0, - time_in_force=time_in_force) - - assert 'id' in order - assert 'info' in order - assert order['id'] == order_id - assert api_mock.create_order.call_args[0][0] == 'ETH/BTC' - assert api_mock.create_order.call_args[0][1] == order_type - assert api_mock.create_order.call_args[0][2] == 'buy' - assert api_mock.create_order.call_args[0][3] == 1 - assert api_mock.create_order.call_args[0][4] is None - # Market orders should not send timeInForce!! - assert "timeInForce" not in api_mock.create_order.call_args[0][5] - - -def test_sell_dry_run(default_conf, mocker): - default_conf['dry_run'] = True - exchange = get_patched_exchange(mocker, default_conf) - - order = exchange.create_order(pair='ETH/BTC', ordertype='limit', - side="sell", amount=1, rate=200, leverage=1.0) - assert 'id' in order - assert 'dry_run_sell_' in order['id'] - - -@pytest.mark.parametrize("exchange_name", EXCHANGES) -def test_sell_prod(default_conf, mocker, exchange_name): - api_mock = MagicMock() - order_id = 'test_prod_sell_{}'.format(randint(0, 10 ** 6)) - order_type = 'market' - api_mock.options = {} - api_mock.create_order = MagicMock(return_value={ - 'id': order_id, - 'symbol': 'ETH/BTC', - 'info': { - 'foo': 'bar' - } - }) - default_conf['dry_run'] = False - - mocker.patch('freqtrade.exchange.Exchange.amount_to_precision', lambda s, x, y: y) - mocker.patch('freqtrade.exchange.Exchange.price_to_precision', lambda s, x, y: y) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - - order = exchange.create_order(pair='ETH/BTC', ordertype=order_type, - side="sell", amount=1, rate=200, leverage=1.0) - - assert 'id' in order - assert 'info' in order - assert order['id'] == order_id - assert api_mock.create_order.call_args[0][0] == 'ETH/BTC' - assert api_mock.create_order.call_args[0][1] == order_type - assert api_mock.create_order.call_args[0][2] == 'sell' - assert api_mock.create_order.call_args[0][3] == 1 - assert api_mock.create_order.call_args[0][4] is None - - api_mock.create_order.reset_mock() - order_type = 'limit' - order = exchange.create_order(pair='ETH/BTC', ordertype=order_type, - side="sell", amount=1, rate=200, - leverage=1.0) - assert api_mock.create_order.call_args[0][0] == 'ETH/BTC' - assert api_mock.create_order.call_args[0][1] == order_type - assert api_mock.create_order.call_args[0][2] == 'sell' - assert api_mock.create_order.call_args[0][3] == 1 - assert api_mock.create_order.call_args[0][4] == 200 - - # test exception handling - with pytest.raises(DependencyException): - api_mock.create_order = MagicMock(side_effect=ccxt.InsufficientFunds("0 balance")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="sell", amount=1, rate=200, - leverage=1.0) - - with pytest.raises(DependencyException): - api_mock.create_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange.create_order(pair='ETH/BTC', ordertype='limit', side="sell", amount=1, rate=200, - leverage=1.0) - - # Market orders don't require price, so the behaviour is slightly different - with pytest.raises(DependencyException): - api_mock.create_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange.create_order(pair='ETH/BTC', ordertype='market', side="sell", amount=1, rate=200, - leverage=1.0) - - with pytest.raises(TemporaryError): - api_mock.create_order = MagicMock(side_effect=ccxt.NetworkError("No Connection")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="sell", amount=1, rate=200, - leverage=1.0) - - with pytest.raises(OperationalException): - api_mock.create_order = MagicMock(side_effect=ccxt.BaseError("DeadBeef")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="sell", amount=1, rate=200, - leverage=1.0) - - -@pytest.mark.parametrize("exchange_name", EXCHANGES) -def test_sell_considers_time_in_force(default_conf, mocker, exchange_name): - api_mock = MagicMock() - order_id = 'test_prod_sell_{}'.format(randint(0, 10 ** 6)) - api_mock.create_order = MagicMock(return_value={ - 'id': order_id, - 'symbol': 'ETH/BTC', - 'info': { - 'foo': 'bar' - } - }) - api_mock.options = {} - default_conf['dry_run'] = False - mocker.patch('freqtrade.exchange.Exchange.amount_to_precision', lambda s, x, y: y) - mocker.patch('freqtrade.exchange.Exchange.price_to_precision', lambda s, x, y: y) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - - order_type = 'limit' - time_in_force = 'ioc' - - order = exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="sell", - amount=1, rate=200, leverage=1.0, - time_in_force=time_in_force) - - assert 'id' in order - assert 'info' in order - assert order['id'] == order_id - assert api_mock.create_order.call_args[0][0] == 'ETH/BTC' - assert api_mock.create_order.call_args[0][1] == order_type - assert api_mock.create_order.call_args[0][2] == 'sell' - assert api_mock.create_order.call_args[0][3] == 1 - assert api_mock.create_order.call_args[0][4] == 200 - assert "timeInForce" in api_mock.create_order.call_args[0][5] - assert api_mock.create_order.call_args[0][5]["timeInForce"] == time_in_force - - order_type = 'market' - time_in_force = 'ioc' - order = exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="sell", - amount=1, rate=200, leverage=1.0, - time_in_force=time_in_force) - - assert 'id' in order - assert 'info' in order - assert order['id'] == order_id - assert api_mock.create_order.call_args[0][0] == 'ETH/BTC' - assert api_mock.create_order.call_args[0][1] == order_type - assert api_mock.create_order.call_args[0][2] == 'sell' - assert api_mock.create_order.call_args[0][3] == 1 - assert api_mock.create_order.call_args[0][4] is None - # Market orders should not send timeInForce!! - assert "timeInForce" not in api_mock.create_order.call_args[0][5] - - -@pytest.mark.parametrize("exchange_name", EXCHANGES) -def test_get_balances_prod(default_conf, mocker, exchange_name): - balance_item = { - 'free': 10.0, - 'total': 10.0, - 'used': 0.0 - } - - api_mock = MagicMock() - api_mock.fetch_balance = MagicMock(return_value={ - '1ST': balance_item, - '2ST': balance_item, - '3ST': balance_item - }) - default_conf['dry_run'] = False - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - assert len(exchange.get_balances()) == 3 - assert exchange.get_balances()['1ST']['free'] == 10.0 - assert exchange.get_balances()['1ST']['total'] == 10.0 - assert exchange.get_balances()['1ST']['used'] == 0.0 - - ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name, - "get_balances", "fetch_balance") - - -@pytest.mark.parametrize("exchange_name", EXCHANGES) -def test_fetch_positions(default_conf, mocker, exchange_name): - mocker.patch('freqtrade.exchange.Exchange.validate_trading_mode_and_margin_mode') - api_mock = MagicMock() - api_mock.fetch_positions = MagicMock(return_value=[ - {'symbol': 'ETH/USDT:USDT', 'leverage': 5}, - {'symbol': 'XRP/USDT:USDT', 'leverage': 5}, - ]) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - assert exchange.fetch_positions() == [] - default_conf['dry_run'] = False - default_conf['trading_mode'] = 'futures' - - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - res = exchange.fetch_positions() - assert len(res) == 2 - - ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name, - "fetch_positions", "fetch_positions") - - -def test_fetch_trading_fees(default_conf, mocker): - api_mock = MagicMock() - tick = { - '1INCH/USDT:USDT': { - 'info': {'user_id': '', - 'taker_fee': '0.0018', - 'maker_fee': '0.0018', - 'gt_discount': False, - 'gt_taker_fee': '0', - 'gt_maker_fee': '0', - 'loan_fee': '0.18', - 'point_type': '1', - 'futures_taker_fee': '0.0005', - 'futures_maker_fee': '0'}, - 'symbol': '1INCH/USDT:USDT', - 'maker': 0.0, - 'taker': 0.0005}, - 'ETH/USDT:USDT': { - 'info': {'user_id': '', - 'taker_fee': '0.0018', - 'maker_fee': '0.0018', - 'gt_discount': False, - 'gt_taker_fee': '0', - 'gt_maker_fee': '0', - 'loan_fee': '0.18', - 'point_type': '1', - 'futures_taker_fee': '0.0005', - 'futures_maker_fee': '0'}, - 'symbol': 'ETH/USDT:USDT', - 'maker': 0.0, - 'taker': 0.0005} - } - exchange_name = 'gateio' - default_conf['dry_run'] = False - default_conf['trading_mode'] = TradingMode.FUTURES - default_conf['margin_mode'] = MarginMode.ISOLATED - api_mock.fetch_trading_fees = MagicMock(return_value=tick) - mocker.patch('freqtrade.exchange.Exchange.exchange_has', return_value=True) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - - assert '1INCH/USDT:USDT' in exchange._trading_fees - assert 'ETH/USDT:USDT' in exchange._trading_fees - assert api_mock.fetch_trading_fees.call_count == 1 - - api_mock.fetch_trading_fees.reset_mock() - - ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name, - "fetch_trading_fees", "fetch_trading_fees") - - api_mock.fetch_trading_fees = MagicMock(return_value={}) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange.fetch_trading_fees() - mocker.patch('freqtrade.exchange.Exchange.exchange_has', return_value=True) - assert exchange.fetch_trading_fees() == {} - - -def test_fetch_bids_asks(default_conf, mocker): - api_mock = MagicMock() - tick = {'ETH/BTC': { - 'symbol': 'ETH/BTC', - 'bid': 0.5, - 'ask': 1, - 'last': 42, - }, 'BCH/BTC': { - 'symbol': 'BCH/BTC', - 'bid': 0.6, - 'ask': 0.5, - 'last': 41, - } - } - exchange_name = 'binance' - api_mock.fetch_bids_asks = MagicMock(return_value=tick) - mocker.patch('freqtrade.exchange.Exchange.exchange_has', return_value=True) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - # retrieve original ticker - bidsasks = exchange.fetch_bids_asks() - - assert 'ETH/BTC' in bidsasks - assert 'BCH/BTC' in bidsasks - assert bidsasks['ETH/BTC']['bid'] == 0.5 - assert bidsasks['ETH/BTC']['ask'] == 1 - assert bidsasks['BCH/BTC']['bid'] == 0.6 - assert bidsasks['BCH/BTC']['ask'] == 0.5 - assert api_mock.fetch_bids_asks.call_count == 1 - - api_mock.fetch_bids_asks.reset_mock() - - # Cached ticker should not call api again - tickers2 = exchange.fetch_bids_asks(cached=True) - assert tickers2 == bidsasks - assert api_mock.fetch_bids_asks.call_count == 0 - tickers2 = exchange.fetch_bids_asks(cached=False) - assert api_mock.fetch_bids_asks.call_count == 1 - - ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name, - "fetch_bids_asks", "fetch_bids_asks") - - with pytest.raises(OperationalException): - api_mock.fetch_bids_asks = MagicMock(side_effect=ccxt.NotSupported("DeadBeef")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange.fetch_bids_asks() - - api_mock.fetch_bids_asks = MagicMock(return_value={}) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange.fetch_bids_asks() - mocker.patch('freqtrade.exchange.Exchange.exchange_has', return_value=True) - assert exchange.fetch_bids_asks() == {} - - -@pytest.mark.parametrize("exchange_name", EXCHANGES) -def test_get_tickers(default_conf, mocker, exchange_name): - api_mock = MagicMock() - tick = {'ETH/BTC': { - 'symbol': 'ETH/BTC', - 'bid': 0.5, - 'ask': 1, - 'last': 42, - }, 'BCH/BTC': { - 'symbol': 'BCH/BTC', - 'bid': 0.6, - 'ask': 0.5, - 'last': 41, - } - } - api_mock.fetch_tickers = MagicMock(return_value=tick) - api_mock.fetch_bids_asks = MagicMock(return_value={}) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - # retrieve original ticker - tickers = exchange.get_tickers() - - assert 'ETH/BTC' in tickers - assert 'BCH/BTC' in tickers - assert tickers['ETH/BTC']['bid'] == 0.5 - assert tickers['ETH/BTC']['ask'] == 1 - assert tickers['BCH/BTC']['bid'] == 0.6 - assert tickers['BCH/BTC']['ask'] == 0.5 - assert api_mock.fetch_tickers.call_count == 1 - assert api_mock.fetch_bids_asks.call_count == 0 - - api_mock.fetch_tickers.reset_mock() - - # Cached ticker should not call api again - tickers2 = exchange.get_tickers(cached=True) - assert tickers2 == tickers - assert api_mock.fetch_tickers.call_count == 0 - assert api_mock.fetch_bids_asks.call_count == 0 - tickers2 = exchange.get_tickers(cached=False) - assert api_mock.fetch_tickers.call_count == 1 - assert api_mock.fetch_bids_asks.call_count == 0 - - ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name, - "get_tickers", "fetch_tickers") - - with pytest.raises(OperationalException): - api_mock.fetch_tickers = MagicMock(side_effect=ccxt.NotSupported("DeadBeef")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange.get_tickers() - - api_mock.fetch_tickers = MagicMock(return_value={}) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange.get_tickers() - - api_mock.fetch_tickers.reset_mock() - api_mock.fetch_bids_asks.reset_mock() - default_conf['trading_mode'] = TradingMode.FUTURES - default_conf['margin_mode'] = MarginMode.ISOLATED - mocker.patch('freqtrade.exchange.Exchange.exchange_has', return_value=True) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - - exchange.get_tickers() - assert api_mock.fetch_tickers.call_count == 1 - assert api_mock.fetch_bids_asks.call_count == (1 if exchange_name == 'binance' else 0) - - -@pytest.mark.parametrize("exchange_name", EXCHANGES) -def test_fetch_ticker(default_conf, mocker, exchange_name): - api_mock = MagicMock() - tick = { - 'symbol': 'ETH/BTC', - 'bid': 0.00001098, - 'ask': 0.00001099, - 'last': 0.0001, - } - api_mock.fetch_ticker = MagicMock(return_value=tick) - api_mock.markets = {'ETH/BTC': {'active': True}} - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - # retrieve original ticker - ticker = exchange.fetch_ticker(pair='ETH/BTC') - - assert ticker['bid'] == 0.00001098 - assert ticker['ask'] == 0.00001099 - - # change the ticker - tick = { - 'symbol': 'ETH/BTC', - 'bid': 0.5, - 'ask': 1, - 'last': 42, - } - api_mock.fetch_ticker = MagicMock(return_value=tick) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - - # if not caching the result we should get the same ticker - # if not fetching a new result we should get the cached ticker - ticker = exchange.fetch_ticker(pair='ETH/BTC') - - assert api_mock.fetch_ticker.call_count == 1 - assert ticker['bid'] == 0.5 - assert ticker['ask'] == 1 - - ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name, - "fetch_ticker", "fetch_ticker", - pair='ETH/BTC') - - api_mock.fetch_ticker = MagicMock(return_value={}) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange.fetch_ticker(pair='ETH/BTC') - - with pytest.raises(DependencyException, match=r'Pair XRP/ETH not available'): - exchange.fetch_ticker(pair='XRP/ETH') - - -@pytest.mark.parametrize("exchange_name", EXCHANGES) -@pytest.mark.parametrize('candle_type', ['mark', '']) -def test_get_historic_ohlcv(default_conf, mocker, caplog, exchange_name, candle_type): - exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) - ohlcv = [ - [ - arrow.utcnow().int_timestamp * 1000, # unix timestamp ms - 1, # open - 2, # high - 3, # low - 4, # close - 5, # volume (in quote currency) - ] - ] - pair = 'ETH/BTC' - - async def mock_candle_hist(pair, timeframe, candle_type, since_ms): - return pair, timeframe, candle_type, ohlcv - - exchange._async_get_candle_history = Mock(wraps=mock_candle_hist) - # one_call calculation * 1.8 should do 2 calls - - since = 5 * 60 * exchange.ohlcv_candle_limit('5m') * 1.8 - ret = exchange.get_historic_ohlcv( - pair, - "5m", - int((arrow.utcnow().int_timestamp - since) * 1000), - candle_type=candle_type - ) - - assert exchange._async_get_candle_history.call_count == 2 - # Returns twice the above OHLCV data - assert len(ret) == 2 - assert log_has_re(r'Downloaded data for .* with length .*\.', caplog) - - caplog.clear() - - async def mock_get_candle_hist_error(pair, *args, **kwargs): - raise TimeoutError() - - exchange._async_get_candle_history = MagicMock(side_effect=mock_get_candle_hist_error) - ret = exchange.get_historic_ohlcv( - pair, - "5m", - int((arrow.utcnow().int_timestamp - since) * 1000), - candle_type=candle_type - ) - assert log_has_re(r"Async code raised an exception: .*", caplog) - - -@pytest.mark.parametrize("exchange_name", EXCHANGES) -@pytest.mark.parametrize('candle_type', ['mark', '']) -def test_get_historic_ohlcv_as_df(default_conf, mocker, exchange_name, candle_type): - exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) - ohlcv = [ - [ - arrow.utcnow().int_timestamp * 1000, # unix timestamp ms - 1, # open - 2, # high - 3, # low - 4, # close - 5, # volume (in quote currency) - ], - [ - arrow.utcnow().shift(minutes=5).int_timestamp * 1000, # unix timestamp ms - 1, # open - 2, # high - 3, # low - 4, # close - 5, # volume (in quote currency) - ], - [ - arrow.utcnow().shift(minutes=10).int_timestamp * 1000, # unix timestamp ms - 1, # open - 2, # high - 3, # low - 4, # close - 5, # volume (in quote currency) - ] - ] - pair = 'ETH/BTC' - - async def mock_candle_hist(pair, timeframe, candle_type, since_ms): - return pair, timeframe, candle_type, ohlcv - - exchange._async_get_candle_history = Mock(wraps=mock_candle_hist) - # one_call calculation * 1.8 should do 2 calls - - since = 5 * 60 * exchange.ohlcv_candle_limit('5m') * 1.8 - ret = exchange.get_historic_ohlcv_as_df( - pair, - "5m", - int((arrow.utcnow().int_timestamp - since) * 1000), - candle_type=candle_type - ) - - assert exchange._async_get_candle_history.call_count == 2 - # Returns twice the above OHLCV data - assert len(ret) == 2 - assert isinstance(ret, DataFrame) - assert 'date' in ret.columns - assert 'open' in ret.columns - assert 'close' in ret.columns - assert 'high' in ret.columns - - -@pytest.mark.asyncio -@pytest.mark.parametrize("exchange_name", EXCHANGES) -@pytest.mark.parametrize('candle_type', [CandleType.MARK, CandleType.SPOT]) -async def test__async_get_historic_ohlcv(default_conf, mocker, caplog, exchange_name, candle_type): - ohlcv = [ - [ - int((datetime.now(timezone.utc).timestamp() - 1000) * 1000), - 1, # open - 2, # high - 3, # low - 4, # close - 5, # volume (in quote currency) - ] - ] - exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) - # Monkey-patch async function - exchange._api_async.fetch_ohlcv = get_mock_coro(ohlcv) - - pair = 'ETH/USDT' - respair, restf, _, res = await exchange._async_get_historic_ohlcv( - pair, "5m", 1500000000000, candle_type=candle_type, is_new_pair=False) - assert respair == pair - assert restf == '5m' - # Call with very old timestamp - causes tons of requests - assert exchange._api_async.fetch_ohlcv.call_count > 200 - assert res[0] == ohlcv[0] - - -@pytest.mark.parametrize('candle_type', [CandleType.FUTURES, CandleType.MARK, CandleType.SPOT]) -def test_refresh_latest_ohlcv(mocker, default_conf, caplog, candle_type) -> None: - ohlcv = [ - [ - (arrow.utcnow().int_timestamp - 1) * 1000, # unix timestamp ms - 1, # open - 2, # high - 3, # low - 4, # close - 5, # volume (in quote currency) - ], - [ - arrow.utcnow().int_timestamp * 1000, # unix timestamp ms - 3, # open - 1, # high - 4, # low - 6, # close - 5, # volume (in quote currency) - ] - ] - - caplog.set_level(logging.DEBUG) - exchange = get_patched_exchange(mocker, default_conf) - exchange._api_async.fetch_ohlcv = get_mock_coro(ohlcv) - - pairs = [('IOTA/ETH', '5m', candle_type), ('XRP/ETH', '5m', candle_type)] - # empty dicts - assert not exchange._klines - res = exchange.refresh_latest_ohlcv(pairs, cache=False) - # No caching - assert not exchange._klines - - assert len(res) == len(pairs) - assert exchange._api_async.fetch_ohlcv.call_count == 2 - exchange._api_async.fetch_ohlcv.reset_mock() - - exchange.required_candle_call_count = 2 - res = exchange.refresh_latest_ohlcv(pairs) - assert len(res) == len(pairs) - - assert log_has(f'Refreshing candle (OHLCV) data for {len(pairs)} pairs', caplog) - assert exchange._klines - assert exchange._api_async.fetch_ohlcv.call_count == 4 - exchange._api_async.fetch_ohlcv.reset_mock() - for pair in pairs: - assert isinstance(exchange.klines(pair), DataFrame) - assert len(exchange.klines(pair)) > 0 - - # klines function should return a different object on each call - # if copy is "True" - assert exchange.klines(pair) is not exchange.klines(pair) - assert exchange.klines(pair) is not exchange.klines(pair, copy=True) - assert exchange.klines(pair, copy=True) is not exchange.klines(pair, copy=True) - assert exchange.klines(pair, copy=False) is exchange.klines(pair, copy=False) - - # test caching - res = exchange.refresh_latest_ohlcv( - [('IOTA/ETH', '5m', candle_type), ('XRP/ETH', '5m', candle_type)]) - assert len(res) == len(pairs) - - assert exchange._api_async.fetch_ohlcv.call_count == 0 - exchange.required_candle_call_count = 1 - assert log_has(f"Using cached candle (OHLCV) data for {pairs[0][0]}, " - f"{pairs[0][1]}, {candle_type} ...", - caplog) - pairlist = [ - ('IOTA/ETH', '5m', candle_type), - ('XRP/ETH', '5m', candle_type), - ('XRP/ETH', '1d', candle_type)] - res = exchange.refresh_latest_ohlcv(pairlist, cache=False) - assert len(res) == 3 - assert exchange._api_async.fetch_ohlcv.call_count == 3 - - # Test the same again, should NOT return from cache! - exchange._api_async.fetch_ohlcv.reset_mock() - res = exchange.refresh_latest_ohlcv(pairlist, cache=False) - assert len(res) == 3 - assert exchange._api_async.fetch_ohlcv.call_count == 3 - exchange._api_async.fetch_ohlcv.reset_mock() - caplog.clear() - # Call with invalid timeframe - res = exchange.refresh_latest_ohlcv([('IOTA/ETH', '3m', candle_type)], cache=False) - if candle_type != CandleType.MARK: - assert not res - assert len(res) == 0 - assert log_has_re(r'Cannot download \(IOTA\/ETH, 3m\).*', caplog) - else: - assert len(res) == 1 - - -@pytest.mark.asyncio -@pytest.mark.parametrize("exchange_name", EXCHANGES) -async def test__async_get_candle_history(default_conf, mocker, caplog, exchange_name): - ohlcv = [ - [ - arrow.utcnow().int_timestamp * 1000, # unix timestamp ms - 1, # open - 2, # high - 3, # low - 4, # close - 5, # volume (in quote currency) - ] - ] - - caplog.set_level(logging.DEBUG) - exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) - # Monkey-patch async function - exchange._api_async.fetch_ohlcv = get_mock_coro(ohlcv) - - pair = 'ETH/BTC' - res = await exchange._async_get_candle_history(pair, "5m", CandleType.SPOT) - assert type(res) is tuple - assert len(res) == 4 - assert res[0] == pair - assert res[1] == "5m" - assert res[2] == CandleType.SPOT - assert res[3] == ohlcv - assert exchange._api_async.fetch_ohlcv.call_count == 1 - assert not log_has(f"Using cached candle (OHLCV) data for {pair} ...", caplog) - - # exchange = Exchange(default_conf) - await async_ccxt_exception(mocker, default_conf, MagicMock(), - "_async_get_candle_history", "fetch_ohlcv", - pair='ABCD/BTC', timeframe=default_conf['timeframe'], - candle_type=CandleType.SPOT) - - api_mock = MagicMock() - with pytest.raises(OperationalException, - match=r'Could not fetch historical candle \(OHLCV\) data.*'): - api_mock.fetch_ohlcv = MagicMock(side_effect=ccxt.BaseError("Unknown error")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - await exchange._async_get_candle_history(pair, "5m", CandleType.SPOT, - (arrow.utcnow().int_timestamp - 2000) * 1000) - - with pytest.raises(OperationalException, match=r'Exchange.* does not support fetching ' - r'historical candle \(OHLCV\) data\..*'): - api_mock.fetch_ohlcv = MagicMock(side_effect=ccxt.NotSupported("Not supported")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - await exchange._async_get_candle_history(pair, "5m", CandleType.SPOT, - (arrow.utcnow().int_timestamp - 2000) * 1000) - - -@pytest.mark.asyncio -async def test__async_kucoin_get_candle_history(default_conf, mocker, caplog): - caplog.set_level(logging.INFO) - api_mock = MagicMock() - api_mock.fetch_ohlcv = MagicMock(side_effect=ccxt.DDoSProtection( - "kucoin GET https://openapi-v2.kucoin.com/api/v1/market/candles?" - "symbol=ETH-BTC&type=5min&startAt=1640268735&endAt=1640418735" - "429 Too Many Requests" '{"code":"429000","msg":"Too Many Requests"}')) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id="KuCoin") - mocker.patch('freqtrade.exchange.Exchange.name', PropertyMock(return_value='KuCoin')) - - msg = "Kucoin 429 error, avoid triggering DDosProtection backoff delay" - assert not num_log_has_re(msg, caplog) - - for _ in range(3): - with pytest.raises(DDosProtection, match=r'429 Too Many Requests'): - await exchange._async_get_candle_history( - "ETH/BTC", "5m", (arrow.utcnow().int_timestamp - 2000) * 1000, count=3) - assert num_log_has_re(msg, caplog) == 3 - - caplog.clear() - # Test regular non-kucoin message - api_mock.fetch_ohlcv = MagicMock(side_effect=ccxt.DDoSProtection( - "kucoin GET https://openapi-v2.kucoin.com/api/v1/market/candles?" - "symbol=ETH-BTC&type=5min&startAt=1640268735&endAt=1640418735" - "429 Too Many Requests" '{"code":"2222222","msg":"Too Many Requests"}')) - - msg = r'_async_get_candle_history\(\) returned exception: .*' - msg2 = r'Applying DDosProtection backoff delay: .*' - with patch('freqtrade.exchange.common.asyncio.sleep', get_mock_coro(None)): - for _ in range(3): - with pytest.raises(DDosProtection, match=r'429 Too Many Requests'): - await exchange._async_get_candle_history( - "ETH/BTC", "5m", (arrow.utcnow().int_timestamp - 2000) * 1000, count=3) - # Expect the "returned exception" message 12 times (4 retries * 3 (loop)) - assert num_log_has_re(msg, caplog) == 12 - assert num_log_has_re(msg2, caplog) == 9 - - -@pytest.mark.asyncio -async def test__async_get_candle_history_empty(default_conf, mocker, caplog): - """ Test empty exchange result """ - ohlcv = [] - - caplog.set_level(logging.DEBUG) - exchange = get_patched_exchange(mocker, default_conf) - # Monkey-patch async function - exchange._api_async.fetch_ohlcv = get_mock_coro([]) - - exchange = Exchange(default_conf) - pair = 'ETH/BTC' - res = await exchange._async_get_candle_history(pair, "5m", CandleType.SPOT) - assert type(res) is tuple - assert len(res) == 4 - assert res[0] == pair - assert res[1] == "5m" - assert res[2] == CandleType.SPOT - assert res[3] == ohlcv - assert exchange._api_async.fetch_ohlcv.call_count == 1 - - -def test_refresh_latest_ohlcv_inv_result(default_conf, mocker, caplog): - - async def mock_get_candle_hist(pair, *args, **kwargs): - if pair == 'ETH/BTC': - return [[]] - else: - raise TypeError() - - exchange = get_patched_exchange(mocker, default_conf) - - # Monkey-patch async function with empty result - exchange._api_async.fetch_ohlcv = MagicMock(side_effect=mock_get_candle_hist) - - pairs = [("ETH/BTC", "5m", ''), ("XRP/BTC", "5m", '')] - res = exchange.refresh_latest_ohlcv(pairs) - assert exchange._klines - assert exchange._api_async.fetch_ohlcv.call_count == 2 - - assert type(res) is dict - assert len(res) == 1 - # Test that each is in list at least once as order is not guaranteed - assert log_has("Error loading ETH/BTC. Result was [[]].", caplog) - assert log_has("Async code raised an exception: TypeError()", caplog) - - -def test_get_next_limit_in_list(): - limit_range = [5, 10, 20, 50, 100, 500, 1000] - assert Exchange.get_next_limit_in_list(1, limit_range) == 5 - assert Exchange.get_next_limit_in_list(5, limit_range) == 5 - assert Exchange.get_next_limit_in_list(6, limit_range) == 10 - assert Exchange.get_next_limit_in_list(9, limit_range) == 10 - assert Exchange.get_next_limit_in_list(10, limit_range) == 10 - assert Exchange.get_next_limit_in_list(11, limit_range) == 20 - assert Exchange.get_next_limit_in_list(19, limit_range) == 20 - assert Exchange.get_next_limit_in_list(21, limit_range) == 50 - assert Exchange.get_next_limit_in_list(51, limit_range) == 100 - assert Exchange.get_next_limit_in_list(1000, limit_range) == 1000 - # Going over the limit ... - assert Exchange.get_next_limit_in_list(1001, limit_range) == 1000 - assert Exchange.get_next_limit_in_list(2000, limit_range) == 1000 - # Without required range - assert Exchange.get_next_limit_in_list(2000, limit_range, False) is None - assert Exchange.get_next_limit_in_list(15, limit_range, False) == 20 - - assert Exchange.get_next_limit_in_list(21, None) == 21 - assert Exchange.get_next_limit_in_list(100, None) == 100 - assert Exchange.get_next_limit_in_list(1000, None) == 1000 - - -@pytest.mark.parametrize("exchange_name", EXCHANGES) -def test_fetch_l2_order_book(default_conf, mocker, order_book_l2, exchange_name): - default_conf['exchange']['name'] = exchange_name - api_mock = MagicMock() - - api_mock.fetch_l2_order_book = order_book_l2 - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - order_book = exchange.fetch_l2_order_book(pair='ETH/BTC', limit=10) - assert 'bids' in order_book - assert 'asks' in order_book - assert len(order_book['bids']) == 10 - assert len(order_book['asks']) == 10 - assert api_mock.fetch_l2_order_book.call_args_list[0][0][0] == 'ETH/BTC' - - for val in [1, 5, 10, 12, 20, 50, 100]: - api_mock.fetch_l2_order_book.reset_mock() - - order_book = exchange.fetch_l2_order_book(pair='ETH/BTC', limit=val) - assert api_mock.fetch_l2_order_book.call_args_list[0][0][0] == 'ETH/BTC' - # Not all exchanges support all limits for orderbook - if not exchange._ft_has['l2_limit_range'] or val in exchange._ft_has['l2_limit_range']: - assert api_mock.fetch_l2_order_book.call_args_list[0][0][1] == val - else: - next_limit = exchange.get_next_limit_in_list(val, exchange._ft_has['l2_limit_range']) - assert api_mock.fetch_l2_order_book.call_args_list[0][0][1] == next_limit - - -@pytest.mark.parametrize("exchange_name", EXCHANGES) -def test_fetch_l2_order_book_exception(default_conf, mocker, exchange_name): - api_mock = MagicMock() - with pytest.raises(OperationalException): - api_mock.fetch_l2_order_book = MagicMock(side_effect=ccxt.NotSupported("Not supported")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange.fetch_l2_order_book(pair='ETH/BTC', limit=50) - with pytest.raises(TemporaryError): - api_mock.fetch_l2_order_book = MagicMock(side_effect=ccxt.NetworkError("DeadBeef")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange.fetch_l2_order_book(pair='ETH/BTC', limit=50) - with pytest.raises(OperationalException): - api_mock.fetch_l2_order_book = MagicMock(side_effect=ccxt.BaseError("DeadBeef")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange.fetch_l2_order_book(pair='ETH/BTC', limit=50) - - -<<<<<<< HEAD -@pytest.mark.parametrize("side,ask,bid,last,last_ab,expected", get_buy_rate_data) -def test_get_buy_rate(mocker, default_conf, caplog, side, ask, bid, - last, last_ab, expected) -> None: -======= -@pytest.mark.parametrize("side,ask,bid,last,last_ab,expected", [ - ('other', 20, 19, 10, 0.0, 20), # Full ask side - ('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', 20, 19, 10, None, 20), # price_last_balance missing - ('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 - ('same', 21, 20, 10, 0.0, 20), # Full bid side - ('bid', 21, 20, 10, 0.0, 20), # Full bid side - ('bid', 21, 20, 10, 1.0, 10), # Full last side - ('bid', 21, 20, 10, 0.5, 15), # Between bid and last - ('bid', 21, 20, 10, 0.7, 13), # Between bid and last - ('bid', 21, 20, 10, 0.3, 17), # Between bid and last - ('bid', 6, 5, 10, 1.0, 5), # last bigger than bid - ('bid', 21, 20, 10, None, 20), # price_last_balance missing - ('bid', 6, 5, 10, 0.5, 5), # last bigger than bid - ('bid', 21, 20, None, 0.5, 20), # last not available - uses bid - ('bid', 6, 5, None, 0.5, 5), # last not available - uses bid - ('bid', 6, 5, None, 1, 5), # last not available - uses bid - ('bid', 6, 5, None, 0, 5), # last not available - uses bid -]) -def test_get_entry_rate(mocker, default_conf, caplog, side, ask, bid, - last, last_ab, expected) -> None: ->>>>>>> develop - caplog.set_level(logging.DEBUG) - if last_ab is None: - del default_conf['entry_pricing']['price_last_balance'] - else: - default_conf['entry_pricing']['price_last_balance'] = last_ab - default_conf['entry_pricing']['price_side'] = side - exchange = get_patched_exchange(mocker, default_conf) - mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', - return_value={'ask': ask, 'last': last, 'bid': bid}) - - assert exchange.get_rate('ETH/BTC', side="entry", is_short=False, refresh=True) == expected - assert not log_has("Using cached entry rate for ETH/BTC.", caplog) - - assert exchange.get_rate('ETH/BTC', side="entry", is_short=False, refresh=False) == expected - assert log_has("Using cached entry rate for ETH/BTC.", caplog) - # Running a 2nd time with Refresh on! - caplog.clear() - assert exchange.get_rate('ETH/BTC', side="entry", is_short=False, refresh=True) == expected - assert not log_has("Using cached entry rate for ETH/BTC.", caplog) - - -<<<<<<< HEAD -@pytest.mark.parametrize('side,ask,bid,last,last_ab,expected', get_sell_rate_data) -def test_get_sell_rate(default_conf, mocker, caplog, side, bid, ask, -======= -@pytest.mark.parametrize('side,ask,bid,last,last_ab,expected', [ - ('bid', 12.0, 11.0, 11.5, 0.0, 11.0), # full bid side - ('bid', 12.0, 11.0, 11.5, 1.0, 11.5), # full last side - ('bid', 12.0, 11.0, 11.5, 0.5, 11.25), # between bid and lat - ('bid', 12.0, 11.2, 10.5, 0.0, 11.2), # Last smaller than bid - ('bid', 12.0, 11.2, 10.5, 1.0, 11.2), # Last smaller than bid - uses bid - ('bid', 12.0, 11.2, 10.5, 0.5, 11.2), # Last smaller than bid - uses bid - ('bid', 0.003, 0.002, 0.005, 0.0, 0.002), - ('bid', 0.003, 0.002, 0.005, None, 0.002), - ('ask', 12.0, 11.0, 12.5, 0.0, 12.0), # full ask side - ('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), - ('ask', 0.006, 1.0, 11.0, None, 0.006), -]) -def test_get_exit_rate(default_conf, mocker, caplog, side, bid, ask, ->>>>>>> develop - last, last_ab, expected) -> None: - caplog.set_level(logging.DEBUG) - - default_conf['exit_pricing']['price_side'] = side - if last_ab is not None: - default_conf['exit_pricing']['price_last_balance'] = last_ab - mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', - return_value={'ask': ask, 'bid': bid, 'last': last}) - pair = "ETH/BTC" - - # Test regular mode - exchange = get_patched_exchange(mocker, default_conf) - rate = exchange.get_rate(pair, side="exit", is_short=False, refresh=True) - assert not log_has("Using cached exit rate for ETH/BTC.", caplog) - assert isinstance(rate, float) - assert rate == expected - # Use caching - rate = exchange.get_rate(pair, side="exit", is_short=False, refresh=False) - assert rate == expected -<<<<<<< HEAD - assert log_has("Using cached sell rate for ETH/BTC.", caplog) - - -get_ticker_rate_error = [ - ('buy', 'ask', None, 4, 4, 0, 4), # ask not available - ('buy', 'ask', None, None, 4, 0, 4), # ask not available - ('buy', 'bid', 6, None, 4, 0, 5), # bid not available - ('buy', 'bid', None, None, 4, 0, 5), # No rate available - ('sell', 'ask', None, 4, 4, 0, 4), # ask not available - ('sell', 'ask', None, None, 4, 0, 4), # ask not available - ('sell', 'bid', 6, None, 4, 0, 5), # bid not available - ('sell', 'bid', None, None, 4, 0, 5), # bid not available -] - - -@pytest.mark.parametrize("entry,side,ask,bid,last,last_ab,expected", get_ticker_rate_error) -def test_get_ticker_rate_error(mocker, entry, default_conf, caplog, side, ask, bid, -======= - assert log_has("Using cached exit rate for ETH/BTC.", caplog) - - -@pytest.mark.parametrize("entry,is_short,side,ask,bid,last,last_ab,expected", [ - ('entry', False, 'ask', None, 4, 4, 0, 4), # ask not available - ('entry', False, 'ask', None, None, 4, 0, 4), # ask not available - ('entry', False, 'bid', 6, None, 4, 0, 5), # bid not available - ('entry', False, 'bid', None, None, 4, 0, 5), # No rate available - ('exit', False, 'ask', None, 4, 4, 0, 4), # ask not available - ('exit', False, 'ask', None, None, 4, 0, 4), # ask not available - ('exit', False, 'bid', 6, None, 4, 0, 5), # bid not available - ('exit', False, 'bid', None, None, 4, 0, 5), # bid not available -]) -def test_get_ticker_rate_error(mocker, entry, default_conf, caplog, side, is_short, ask, bid, ->>>>>>> develop - last, last_ab, expected) -> None: - caplog.set_level(logging.DEBUG) - default_conf['entry_pricing']['price_last_balance'] = last_ab - default_conf['entry_pricing']['price_side'] = side - default_conf['exit_pricing']['price_side'] = side - default_conf['exit_pricing']['price_last_balance'] = last_ab - exchange = get_patched_exchange(mocker, default_conf) - mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', - return_value={'ask': ask, 'last': last, 'bid': bid}) - - with pytest.raises(PricingError): -<<<<<<< HEAD - exchange.get_rate('ETH/BTC', refresh=True, side=entry) - - -@pytest.mark.parametrize('side,expected', [ - ('bid', 0.043936), # Value from order_book_l2 fixture - bids side - ('ask', 0.043949), # Value from order_book_l2 fixture - asks side -======= - exchange.get_rate('ETH/BTC', refresh=True, side=entry, is_short=is_short) - - -@pytest.mark.parametrize('is_short,side,expected', [ - (False, 'bid', 0.043936), # Value from order_book_l2 fitxure - bids side - (False, 'ask', 0.043949), # Value from order_book_l2 fitxure - asks side - (False, 'other', 0.043936), # Value from order_book_l2 fitxure - bids side - (False, 'same', 0.043949), # Value from order_book_l2 fitxure - asks side - (True, 'bid', 0.043936), # Value from order_book_l2 fitxure - bids side - (True, 'ask', 0.043949), # Value from order_book_l2 fitxure - asks side - (True, 'other', 0.043949), # Value from order_book_l2 fitxure - asks side - (True, 'same', 0.043936), # Value from order_book_l2 fitxure - bids side ->>>>>>> develop -]) -def test_get_exit_rate_orderbook( - default_conf, mocker, caplog, is_short, side, expected, order_book_l2): - caplog.set_level(logging.DEBUG) - # Test orderbook mode - default_conf['exit_pricing']['price_side'] = side - default_conf['exit_pricing']['use_order_book'] = True - default_conf['exit_pricing']['order_book_top'] = 1 - pair = "ETH/BTC" - mocker.patch('freqtrade.exchange.Exchange.fetch_l2_order_book', order_book_l2) - exchange = get_patched_exchange(mocker, default_conf) - rate = exchange.get_rate(pair, refresh=True, side="exit", is_short=is_short) - assert not log_has("Using cached exit rate for ETH/BTC.", caplog) - assert isinstance(rate, float) - assert rate == expected - rate = exchange.get_rate(pair, refresh=False, side="exit", is_short=is_short) - assert rate == expected - assert log_has("Using cached exit rate for ETH/BTC.", caplog) - - -def test_get_exit_rate_orderbook_exception(default_conf, mocker, caplog): - # Test orderbook mode - default_conf['exit_pricing']['price_side'] = 'ask' - default_conf['exit_pricing']['use_order_book'] = True - default_conf['exit_pricing']['order_book_top'] = 1 - pair = "ETH/BTC" - # Test What happens if the exchange returns an empty orderbook. - mocker.patch('freqtrade.exchange.Exchange.fetch_l2_order_book', - return_value={'bids': [[]], 'asks': [[]]}) - exchange = get_patched_exchange(mocker, default_conf) - with pytest.raises(PricingError): -<<<<<<< HEAD - exchange.get_rate(pair, refresh=True, side="sell") - assert log_has_re( - rf"{pair} - Sell Price at location 1 from orderbook " - rf"could not be determined\..*", caplog) -======= - exchange.get_rate(pair, refresh=True, side="exit", is_short=False) - assert log_has_re(r"Exit Price at location 1 from orderbook could not be determined\..*", - caplog) ->>>>>>> develop - - -@pytest.mark.parametrize('is_short', [True, False]) -def test_get_exit_rate_exception(default_conf, mocker, is_short): - # Ticker on one side can be empty in certain circumstances. - default_conf['exit_pricing']['price_side'] = 'ask' - pair = "ETH/BTC" - mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', - return_value={'ask': None, 'bid': 0.12, 'last': None}) - exchange = get_patched_exchange(mocker, default_conf) - with pytest.raises(PricingError, match=r"Exit-Rate for ETH/BTC was empty."): - exchange.get_rate(pair, refresh=True, side="exit", is_short=is_short) - - exchange._config['exit_pricing']['price_side'] = 'bid' - assert exchange.get_rate(pair, refresh=True, side="exit", is_short=is_short) == 0.12 - # Reverse sides - mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', - return_value={'ask': 0.13, 'bid': None, 'last': None}) - with pytest.raises(PricingError, match=r"Exit-Rate for ETH/BTC was empty."): - exchange.get_rate(pair, refresh=True, side="exit", is_short=is_short) - - exchange._config['exit_pricing']['price_side'] = 'ask' - assert exchange.get_rate(pair, refresh=True, side="exit", is_short=is_short) == 0.13 - - -@pytest.mark.parametrize("side,ask,bid,last,last_ab,expected", get_buy_rate_data) -@pytest.mark.parametrize("side2", ['bid', 'ask']) -@pytest.mark.parametrize("use_order_book", [True, False]) -def test_get_rates_testing_buy(mocker, default_conf, caplog, side, ask, bid, - last, last_ab, expected, - side2, use_order_book, order_book_l2) -> None: - caplog.set_level(logging.DEBUG) - if last_ab is None: - del default_conf['bid_strategy']['ask_last_balance'] - else: - default_conf['bid_strategy']['ask_last_balance'] = last_ab - default_conf['bid_strategy']['price_side'] = side - default_conf['ask_strategy']['price_side'] = side2 - default_conf['ask_strategy']['use_order_book'] = use_order_book - api_mock = MagicMock() - api_mock.fetch_l2_order_book = order_book_l2 - api_mock.fetch_ticker = MagicMock( - return_value={'ask': ask, 'last': last, 'bid': bid}) - exchange = get_patched_exchange(mocker, default_conf, api_mock) - - assert exchange.get_rates('ETH/BTC', refresh=True)[0] == expected - assert not log_has("Using cached buy rate for ETH/BTC.", caplog) - - assert exchange.get_rates('ETH/BTC', refresh=False)[0] == expected - assert log_has("Using cached buy rate for ETH/BTC.", caplog) - # Running a 2nd time with Refresh on! - caplog.clear() - assert exchange.get_rates('ETH/BTC', refresh=True)[0] == expected - assert not log_has("Using cached buy rate for ETH/BTC.", caplog) - - api_mock.fetch_l2_order_book.call_count = int(use_order_book) - api_mock.fetch_ticker.call_count = 1 - - -@pytest.mark.parametrize('side,ask,bid,last,last_ab,expected', get_sell_rate_data) -@pytest.mark.parametrize("side2", ['bid', 'ask']) -@pytest.mark.parametrize("use_order_book", [True, False]) -def test_get_rates_testing_sell(default_conf, mocker, caplog, side, bid, ask, - last, last_ab, expected, - side2, use_order_book, order_book_l2) -> None: - caplog.set_level(logging.DEBUG) - - default_conf['ask_strategy']['price_side'] = side - if last_ab is not None: - default_conf['ask_strategy']['bid_last_balance'] = last_ab - - default_conf['bid_strategy']['price_side'] = side2 - default_conf['bid_strategy']['use_order_book'] = use_order_book - api_mock = MagicMock() - api_mock.fetch_l2_order_book = order_book_l2 - api_mock.fetch_ticker = MagicMock( - return_value={'ask': ask, 'last': last, 'bid': bid}) - exchange = get_patched_exchange(mocker, default_conf, api_mock) - - pair = "ETH/BTC" - - # Test regular mode - rate = exchange.get_rates(pair, refresh=True)[1] - assert not log_has("Using cached sell rate for ETH/BTC.", caplog) - assert isinstance(rate, float) - assert rate == expected - # Use caching - rate = exchange.get_rates(pair, refresh=False)[1] - assert rate == expected - assert log_has("Using cached sell rate for ETH/BTC.", caplog) - - api_mock.fetch_l2_order_book.call_count = int(use_order_book) - api_mock.fetch_ticker.call_count = 1 - - -@pytest.mark.parametrize("exchange_name", EXCHANGES) -@pytest.mark.asyncio -async def test___async_get_candle_history_sort(default_conf, mocker, exchange_name): - def sort_data(data, key): - return sorted(data, key=key) - - # GDAX use-case (real data from GDAX) - # This OHLCV data is ordered DESC (newest first, oldest last) - ohlcv = [ - [1527833100000, 0.07666, 0.07671, 0.07666, 0.07668, 16.65244264], - [1527832800000, 0.07662, 0.07666, 0.07662, 0.07666, 1.30051526], - [1527832500000, 0.07656, 0.07661, 0.07656, 0.07661, 12.034778840000001], - [1527832200000, 0.07658, 0.07658, 0.07655, 0.07656, 0.59780186], - [1527831900000, 0.07658, 0.07658, 0.07658, 0.07658, 1.76278136], - [1527831600000, 0.07658, 0.07658, 0.07658, 0.07658, 2.22646521], - [1527831300000, 0.07655, 0.07657, 0.07655, 0.07657, 1.1753], - [1527831000000, 0.07654, 0.07654, 0.07651, 0.07651, 0.8073060299999999], - [1527830700000, 0.07652, 0.07652, 0.07651, 0.07652, 10.04822687], - [1527830400000, 0.07649, 0.07651, 0.07649, 0.07651, 2.5734867] - ] - exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) - exchange._api_async.fetch_ohlcv = get_mock_coro(ohlcv) - sort_mock = mocker.patch('freqtrade.exchange.exchange.sorted', MagicMock(side_effect=sort_data)) - # Test the OHLCV data sort - res = await exchange._async_get_candle_history( - 'ETH/BTC', default_conf['timeframe'], CandleType.SPOT) - assert res[0] == 'ETH/BTC' - res_ohlcv = res[3] - - assert sort_mock.call_count == 1 - assert res_ohlcv[0][0] == 1527830400000 - assert res_ohlcv[0][1] == 0.07649 - assert res_ohlcv[0][2] == 0.07651 - assert res_ohlcv[0][3] == 0.07649 - assert res_ohlcv[0][4] == 0.07651 - assert res_ohlcv[0][5] == 2.5734867 - - assert res_ohlcv[9][0] == 1527833100000 - assert res_ohlcv[9][1] == 0.07666 - assert res_ohlcv[9][2] == 0.07671 - assert res_ohlcv[9][3] == 0.07666 - assert res_ohlcv[9][4] == 0.07668 - assert res_ohlcv[9][5] == 16.65244264 - - # Bittrex use-case (real data from Bittrex) - # This OHLCV data is ordered ASC (oldest first, newest last) - ohlcv = [ - [1527827700000, 0.07659999, 0.0766, 0.07627, 0.07657998, 1.85216924], - [1527828000000, 0.07657995, 0.07657995, 0.0763, 0.0763, 26.04051037], - [1527828300000, 0.0763, 0.07659998, 0.0763, 0.0764, 10.36434124], - [1527828600000, 0.0764, 0.0766, 0.0764, 0.0766, 5.71044773], - [1527828900000, 0.0764, 0.07666998, 0.0764, 0.07666998, 47.48888565], - [1527829200000, 0.0765, 0.07672999, 0.0765, 0.07672999, 3.37640326], - [1527829500000, 0.0766, 0.07675, 0.0765, 0.07675, 8.36203831], - [1527829800000, 0.07675, 0.07677999, 0.07620002, 0.076695, 119.22963884], - [1527830100000, 0.076695, 0.07671, 0.07624171, 0.07671, 1.80689244], - [1527830400000, 0.07671, 0.07674399, 0.07629216, 0.07655213, 2.31452783] - ] - exchange._api_async.fetch_ohlcv = get_mock_coro(ohlcv) - # Reset sort mock - sort_mock = mocker.patch('freqtrade.exchange.sorted', MagicMock(side_effect=sort_data)) - # Test the OHLCV data sort - res = await exchange._async_get_candle_history( - 'ETH/BTC', default_conf['timeframe'], CandleType.SPOT) - assert res[0] == 'ETH/BTC' - assert res[1] == default_conf['timeframe'] - res_ohlcv = res[3] - # Sorted not called again - data is already in order - assert sort_mock.call_count == 0 - assert res_ohlcv[0][0] == 1527827700000 - assert res_ohlcv[0][1] == 0.07659999 - assert res_ohlcv[0][2] == 0.0766 - assert res_ohlcv[0][3] == 0.07627 - assert res_ohlcv[0][4] == 0.07657998 - assert res_ohlcv[0][5] == 1.85216924 - - assert res_ohlcv[9][0] == 1527830400000 - assert res_ohlcv[9][1] == 0.07671 - assert res_ohlcv[9][2] == 0.07674399 - assert res_ohlcv[9][3] == 0.07629216 - assert res_ohlcv[9][4] == 0.07655213 - assert res_ohlcv[9][5] == 2.31452783 - - -@pytest.mark.asyncio -@pytest.mark.parametrize("exchange_name", EXCHANGES) -async def test__async_fetch_trades(default_conf, mocker, caplog, exchange_name, - fetch_trades_result): - caplog.set_level(logging.DEBUG) - exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) - # Monkey-patch async function - exchange._api_async.fetch_trades = get_mock_coro(fetch_trades_result) - - pair = 'ETH/BTC' - res = await exchange._async_fetch_trades(pair, since=None, params=None) - assert type(res) is list - assert isinstance(res[0], list) - assert isinstance(res[1], list) - - assert exchange._api_async.fetch_trades.call_count == 1 - assert exchange._api_async.fetch_trades.call_args[0][0] == pair - assert exchange._api_async.fetch_trades.call_args[1]['limit'] == 1000 - - assert log_has_re(f"Fetching trades for pair {pair}, since .*", caplog) - caplog.clear() - exchange._api_async.fetch_trades.reset_mock() - res = await exchange._async_fetch_trades(pair, since=None, params={'from': '123'}) - assert exchange._api_async.fetch_trades.call_count == 1 - assert exchange._api_async.fetch_trades.call_args[0][0] == pair - assert exchange._api_async.fetch_trades.call_args[1]['limit'] == 1000 - assert exchange._api_async.fetch_trades.call_args[1]['params'] == {'from': '123'} - assert log_has_re(f"Fetching trades for pair {pair}, params: .*", caplog) - - exchange = Exchange(default_conf) - await async_ccxt_exception(mocker, default_conf, MagicMock(), - "_async_fetch_trades", "fetch_trades", - pair='ABCD/BTC', since=None) - - api_mock = MagicMock() - with pytest.raises(OperationalException, match=r'Could not fetch trade data*'): - api_mock.fetch_trades = MagicMock(side_effect=ccxt.BaseError("Unknown error")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - await exchange._async_fetch_trades(pair, since=(arrow.utcnow().int_timestamp - 2000) * 1000) - - with pytest.raises(OperationalException, match=r'Exchange.* does not support fetching ' - r'historical trade data\..*'): - api_mock.fetch_trades = MagicMock(side_effect=ccxt.NotSupported("Not supported")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - await exchange._async_fetch_trades(pair, since=(arrow.utcnow().int_timestamp - 2000) * 1000) - - -@pytest.mark.asyncio -@pytest.mark.parametrize("exchange_name", EXCHANGES) -async def test__async_fetch_trades_contract_size(default_conf, mocker, caplog, exchange_name, - fetch_trades_result): - caplog.set_level(logging.DEBUG) - default_conf['margin_mode'] = 'isolated' - default_conf['trading_mode'] = 'futures' - exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) - # Monkey-patch async function - exchange._api_async.fetch_trades = get_mock_coro([ - {'info': {'a': 126181333, - 'p': '0.01952600', - 'q': '0.01200000', - 'f': 138604158, - 'l': 138604158, - 'T': 1565798399872, - 'm': True, - 'M': True}, - 'timestamp': 1565798399872, - 'datetime': '2019-08-14T15:59:59.872Z', - 'symbol': 'ETH/USDT:USDT', - 'id': '126181383', - 'order': None, - 'type': None, - 'takerOrMaker': None, - 'side': 'sell', - 'price': 2.0, - 'amount': 30.0, - 'cost': 60.0, - 'fee': None}] - ) - - pair = 'ETH/USDT:USDT' - res = await exchange._async_fetch_trades(pair, since=None, params=None) - assert res[0][5] == 300 - - -@pytest.mark.asyncio -@pytest.mark.parametrize("exchange_name", EXCHANGES) -async def test__async_get_trade_history_id(default_conf, mocker, exchange_name, - fetch_trades_result): - - exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) - pagination_arg = exchange._trades_pagination_arg - - async def mock_get_trade_hist(pair, *args, **kwargs): - if 'since' in kwargs: - # Return first 3 - return fetch_trades_result[:-2] - elif kwargs.get('params', {}).get(pagination_arg) == fetch_trades_result[-3]['id']: - # Return 2 - return fetch_trades_result[-3:-1] - else: - # Return last 2 - return fetch_trades_result[-2:] - # Monkey-patch async function - exchange._api_async.fetch_trades = MagicMock(side_effect=mock_get_trade_hist) - - pair = 'ETH/BTC' - ret = await exchange._async_get_trade_history_id(pair, - since=fetch_trades_result[0]['timestamp'], - until=fetch_trades_result[-1]['timestamp'] - 1) - assert type(ret) is tuple - assert ret[0] == pair - assert type(ret[1]) is list - assert len(ret[1]) == len(fetch_trades_result) - assert exchange._api_async.fetch_trades.call_count == 3 - fetch_trades_cal = exchange._api_async.fetch_trades.call_args_list - # first call (using since, not fromId) - assert fetch_trades_cal[0][0][0] == pair - assert fetch_trades_cal[0][1]['since'] == fetch_trades_result[0]['timestamp'] - - # 2nd call - assert fetch_trades_cal[1][0][0] == pair - assert 'params' in fetch_trades_cal[1][1] - assert exchange._ft_has['trades_pagination_arg'] in fetch_trades_cal[1][1]['params'] - - -@pytest.mark.asyncio -@pytest.mark.parametrize("exchange_name", EXCHANGES) -async def test__async_get_trade_history_time(default_conf, mocker, caplog, exchange_name, - fetch_trades_result): - - caplog.set_level(logging.DEBUG) - - async def mock_get_trade_hist(pair, *args, **kwargs): - if kwargs['since'] == fetch_trades_result[0]['timestamp']: - return fetch_trades_result[:-1] - else: - return fetch_trades_result[-1:] - - caplog.set_level(logging.DEBUG) - exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) - # Monkey-patch async function - exchange._api_async.fetch_trades = MagicMock(side_effect=mock_get_trade_hist) - pair = 'ETH/BTC' - ret = await exchange._async_get_trade_history_time(pair, - since=fetch_trades_result[0]['timestamp'], - until=fetch_trades_result[-1]['timestamp']-1) - assert type(ret) is tuple - assert ret[0] == pair - assert type(ret[1]) is list - assert len(ret[1]) == len(fetch_trades_result) - assert exchange._api_async.fetch_trades.call_count == 2 - fetch_trades_cal = exchange._api_async.fetch_trades.call_args_list - # first call (using since, not fromId) - assert fetch_trades_cal[0][0][0] == pair - assert fetch_trades_cal[0][1]['since'] == fetch_trades_result[0]['timestamp'] - - # 2nd call - assert fetch_trades_cal[1][0][0] == pair - assert fetch_trades_cal[1][1]['since'] == fetch_trades_result[-2]['timestamp'] - assert log_has_re(r"Stopping because until was reached.*", caplog) - - -@pytest.mark.asyncio -@pytest.mark.parametrize("exchange_name", EXCHANGES) -async def test__async_get_trade_history_time_empty(default_conf, mocker, caplog, exchange_name, - trades_history): - - caplog.set_level(logging.DEBUG) - - async def mock_get_trade_hist(pair, *args, **kwargs): - if kwargs['since'] == trades_history[0][0]: - return trades_history[:-1] - else: - return [] - - caplog.set_level(logging.DEBUG) - exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) - # Monkey-patch async function - exchange._async_fetch_trades = MagicMock(side_effect=mock_get_trade_hist) - pair = 'ETH/BTC' - ret = await exchange._async_get_trade_history_time(pair, since=trades_history[0][0], - until=trades_history[-1][0]-1) - assert type(ret) is tuple - assert ret[0] == pair - assert type(ret[1]) is list - assert len(ret[1]) == len(trades_history) - 1 - assert exchange._async_fetch_trades.call_count == 2 - fetch_trades_cal = exchange._async_fetch_trades.call_args_list - # first call (using since, not fromId) - assert fetch_trades_cal[0][0][0] == pair - assert fetch_trades_cal[0][1]['since'] == trades_history[0][0] - - -@pytest.mark.parametrize("exchange_name", EXCHANGES) -def test_get_historic_trades(default_conf, mocker, caplog, exchange_name, trades_history): - mocker.patch('freqtrade.exchange.Exchange.exchange_has', return_value=True) - exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) - - pair = 'ETH/BTC' - - exchange._async_get_trade_history_id = get_mock_coro((pair, trades_history)) - exchange._async_get_trade_history_time = get_mock_coro((pair, trades_history)) - ret = exchange.get_historic_trades(pair, since=trades_history[0][0], - until=trades_history[-1][0]) - - # Depending on the exchange, one or the other method should be called - assert sum([exchange._async_get_trade_history_id.call_count, - exchange._async_get_trade_history_time.call_count]) == 1 - - assert len(ret) == 2 - assert ret[0] == pair - assert len(ret[1]) == len(trades_history) - - -@pytest.mark.parametrize("exchange_name", EXCHANGES) -def test_get_historic_trades_notsupported(default_conf, mocker, caplog, exchange_name, - trades_history): - mocker.patch('freqtrade.exchange.Exchange.exchange_has', return_value=False) - exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) - - pair = 'ETH/BTC' - - with pytest.raises(OperationalException, - match="This exchange does not support downloading Trades."): - exchange.get_historic_trades(pair, since=trades_history[0][0], - until=trades_history[-1][0]) - - -@pytest.mark.parametrize("exchange_name", EXCHANGES) -def test_cancel_order_dry_run(default_conf, mocker, exchange_name): - default_conf['dry_run'] = True - exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) - mocker.patch('freqtrade.exchange.Exchange._is_dry_limit_order_filled', return_value=True) - assert exchange.cancel_order(order_id='123', pair='TKN/BTC') == {} - assert exchange.cancel_stoploss_order(order_id='123', pair='TKN/BTC') == {} - - order = exchange.create_order( - pair='ETH/BTC', - ordertype='limit', - side='buy', - amount=5, - rate=0.55, - time_in_force='gtc', - leverage=1.0, - ) - - cancel_order = exchange.cancel_order(order_id=order['id'], pair='ETH/BTC') - assert order['id'] == cancel_order['id'] - assert order['amount'] == cancel_order['amount'] - assert order['symbol'] == cancel_order['symbol'] - assert cancel_order['status'] == 'canceled' - - -@pytest.mark.parametrize("exchange_name", EXCHANGES) -@pytest.mark.parametrize("order,result", [ - ({'status': 'closed', 'filled': 10}, False), - ({'status': 'closed', 'filled': 0.0}, True), - ({'status': 'canceled', 'filled': 0.0}, True), - ({'status': 'canceled', 'filled': 10.0}, False), - ({'status': 'unknown', 'filled': 10.0}, False), - ({'result': 'testest123'}, False), -]) -def test_check_order_canceled_empty(mocker, default_conf, exchange_name, order, result): - exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) - assert exchange.check_order_canceled_empty(order) == result - - -@pytest.mark.parametrize("exchange_name", EXCHANGES) -@pytest.mark.parametrize("order,result", [ - ({'status': 'closed', 'amount': 10, 'fee': {}}, True), - ({'status': 'closed', 'amount': 0.0, 'fee': {}}, True), - ({'status': 'canceled', 'amount': 0.0, 'fee': {}}, True), - ({'status': 'canceled', 'amount': 10.0}, False), - ({'amount': 10.0, 'fee': {}}, False), - ({'result': 'testest123'}, False), - ('hello_world', False), -]) -def test_is_cancel_order_result_suitable(mocker, default_conf, exchange_name, order, result): - exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) - assert exchange.is_cancel_order_result_suitable(order) == result - - -@pytest.mark.parametrize("exchange_name", EXCHANGES) -@pytest.mark.parametrize("corder,call_corder,call_forder", [ - ({'status': 'closed', 'amount': 10, 'fee': {}}, 1, 0), - ({'amount': 10, 'fee': {}}, 1, 1), -]) -def test_cancel_order_with_result(default_conf, mocker, exchange_name, corder, - call_corder, call_forder): - default_conf['dry_run'] = False - api_mock = MagicMock() - api_mock.cancel_order = MagicMock(return_value=corder) - api_mock.fetch_order = MagicMock(return_value={}) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - res = exchange.cancel_order_with_result('1234', 'ETH/BTC', 1234) - assert isinstance(res, dict) - assert api_mock.cancel_order.call_count == call_corder - assert api_mock.fetch_order.call_count == call_forder - - -@pytest.mark.parametrize("exchange_name", EXCHANGES) -def test_cancel_order_with_result_error(default_conf, mocker, exchange_name, caplog): - default_conf['dry_run'] = False - api_mock = MagicMock() - api_mock.cancel_order = MagicMock(side_effect=ccxt.InvalidOrder("Did not find order")) - api_mock.fetch_order = MagicMock(side_effect=ccxt.InvalidOrder("Did not find order")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - - res = exchange.cancel_order_with_result('1234', 'ETH/BTC', 1541) - assert isinstance(res, dict) - assert log_has("Could not cancel order 1234 for ETH/BTC.", caplog) - assert log_has("Could not fetch cancelled order 1234.", caplog) - assert res['amount'] == 1541 - - -# Ensure that if not dry_run, we should call API -@pytest.mark.parametrize("exchange_name", EXCHANGES) -def test_cancel_order(default_conf, mocker, exchange_name): - default_conf['dry_run'] = False - api_mock = MagicMock() - api_mock.cancel_order = MagicMock(return_value={'id': '123'}) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - assert exchange.cancel_order(order_id='_', pair='TKN/BTC') == {'id': '123'} - - with pytest.raises(InvalidOrderException): - api_mock.cancel_order = MagicMock(side_effect=ccxt.InvalidOrder("Did not find order")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange.cancel_order(order_id='_', pair='TKN/BTC') - assert api_mock.cancel_order.call_count == 1 - - ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name, - "cancel_order", "cancel_order", - order_id='_', pair='TKN/BTC') - - -@pytest.mark.parametrize("exchange_name", EXCHANGES) -def test_cancel_stoploss_order(default_conf, mocker, exchange_name): - default_conf['dry_run'] = False - api_mock = MagicMock() - api_mock.cancel_order = MagicMock(return_value={'id': '123'}) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - assert exchange.cancel_stoploss_order(order_id='_', pair='TKN/BTC') == {'id': '123'} - - with pytest.raises(InvalidOrderException): - api_mock.cancel_order = MagicMock(side_effect=ccxt.InvalidOrder("Did not find order")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange.cancel_stoploss_order(order_id='_', pair='TKN/BTC') - assert api_mock.cancel_order.call_count == 1 - - ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name, - "cancel_stoploss_order", "cancel_order", - order_id='_', pair='TKN/BTC') - - -@pytest.mark.parametrize("exchange_name", EXCHANGES) -def test_cancel_stoploss_order_with_result(default_conf, mocker, exchange_name): - default_conf['dry_run'] = False - mocker.patch('freqtrade.exchange.Exchange.fetch_stoploss_order', return_value={'for': 123}) - mocker.patch('freqtrade.exchange.Ftx.fetch_stoploss_order', return_value={'for': 123}) - mocker.patch('freqtrade.exchange.Gateio.fetch_stoploss_order', return_value={'for': 123}) - exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) - - res = {'fee': {}, 'status': 'canceled', 'amount': 1234} - mocker.patch('freqtrade.exchange.Exchange.cancel_stoploss_order', return_value=res) - mocker.patch('freqtrade.exchange.Ftx.cancel_stoploss_order', return_value=res) - mocker.patch('freqtrade.exchange.Gateio.cancel_stoploss_order', return_value=res) - co = exchange.cancel_stoploss_order_with_result(order_id='_', pair='TKN/BTC', amount=555) - assert co == res - - mocker.patch('freqtrade.exchange.Exchange.cancel_stoploss_order', return_value='canceled') - mocker.patch('freqtrade.exchange.Ftx.cancel_stoploss_order', return_value='canceled') - mocker.patch('freqtrade.exchange.Gateio.cancel_stoploss_order', return_value='canceled') - # Fall back to fetch_stoploss_order - co = exchange.cancel_stoploss_order_with_result(order_id='_', pair='TKN/BTC', amount=555) - assert co == {'for': 123} - - exc = InvalidOrderException("") - mocker.patch('freqtrade.exchange.Exchange.fetch_stoploss_order', side_effect=exc) - mocker.patch('freqtrade.exchange.Ftx.fetch_stoploss_order', side_effect=exc) - mocker.patch('freqtrade.exchange.Gateio.fetch_stoploss_order', side_effect=exc) - co = exchange.cancel_stoploss_order_with_result(order_id='_', pair='TKN/BTC', amount=555) - assert co['amount'] == 555 - assert co == {'fee': {}, 'status': 'canceled', 'amount': 555, 'info': {}} - - with pytest.raises(InvalidOrderException): - exc = InvalidOrderException("Did not find order") - mocker.patch('freqtrade.exchange.Exchange.cancel_stoploss_order', side_effect=exc) - mocker.patch('freqtrade.exchange.Ftx.cancel_stoploss_order', side_effect=exc) - mocker.patch('freqtrade.exchange.Gateio.cancel_stoploss_order', side_effect=exc) - exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) - exchange.cancel_stoploss_order_with_result(order_id='_', pair='TKN/BTC', amount=123) - - -@pytest.mark.parametrize("exchange_name", EXCHANGES) -def test_fetch_order(default_conf, mocker, exchange_name, caplog): - default_conf['dry_run'] = True - default_conf['exchange']['log_responses'] = True - order = MagicMock() - order.myid = 123 - order.symbol = 'TKN/BTC' - - exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) - exchange._dry_run_open_orders['X'] = order - assert exchange.fetch_order('X', 'TKN/BTC').myid == 123 - - with pytest.raises(InvalidOrderException, match=r'Tried to get an invalid dry-run-order.*'): - exchange.fetch_order('Y', 'TKN/BTC') - - default_conf['dry_run'] = False - api_mock = MagicMock() - api_mock.fetch_order = MagicMock(return_value={'id': '123', 'amount': 2, 'symbol': 'TKN/BTC'}) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - assert exchange.fetch_order( - 'X', 'TKN/BTC') == {'id': '123', 'amount': 2, 'symbol': 'TKN/BTC'} - assert log_has( - ("API fetch_order: {\'id\': \'123\', \'amount\': 2, \'symbol\': \'TKN/BTC\'}" - ), - caplog - ) - - with pytest.raises(InvalidOrderException): - api_mock.fetch_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange.fetch_order(order_id='_', pair='TKN/BTC') - assert api_mock.fetch_order.call_count == 1 - - api_mock.fetch_order = MagicMock(side_effect=ccxt.OrderNotFound("Order not found")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - with patch('freqtrade.exchange.common.time.sleep') as tm: - with pytest.raises(InvalidOrderException): - exchange.fetch_order(order_id='_', pair='TKN/BTC') - # Ensure backoff is called - assert tm.call_args_list[0][0][0] == 1 - assert tm.call_args_list[1][0][0] == 2 - if API_FETCH_ORDER_RETRY_COUNT > 2: - assert tm.call_args_list[2][0][0] == 5 - if API_FETCH_ORDER_RETRY_COUNT > 3: - assert tm.call_args_list[3][0][0] == 10 - assert api_mock.fetch_order.call_count == API_FETCH_ORDER_RETRY_COUNT + 1 - - ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name, - 'fetch_order', 'fetch_order', retries=API_FETCH_ORDER_RETRY_COUNT + 1, - order_id='_', pair='TKN/BTC') - - -@pytest.mark.parametrize("exchange_name", EXCHANGES) -def test_fetch_stoploss_order(default_conf, mocker, exchange_name): - # Don't test FTX here - that needs a separate test - if exchange_name == 'ftx': - return - default_conf['dry_run'] = True - order = MagicMock() - order.myid = 123 - exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) - exchange._dry_run_open_orders['X'] = order - assert exchange.fetch_stoploss_order('X', 'TKN/BTC').myid == 123 - - with pytest.raises(InvalidOrderException, match=r'Tried to get an invalid dry-run-order.*'): - exchange.fetch_stoploss_order('Y', 'TKN/BTC') - - default_conf['dry_run'] = False - api_mock = MagicMock() - api_mock.fetch_order = MagicMock(return_value={'id': '123', 'symbol': 'TKN/BTC'}) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - assert exchange.fetch_stoploss_order('X', 'TKN/BTC') == {'id': '123', 'symbol': 'TKN/BTC'} - - with pytest.raises(InvalidOrderException): - api_mock.fetch_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found")) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange.fetch_stoploss_order(order_id='_', pair='TKN/BTC') - assert api_mock.fetch_order.call_count == 1 - - ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name, - 'fetch_stoploss_order', 'fetch_order', - retries=API_FETCH_ORDER_RETRY_COUNT + 1, - order_id='_', pair='TKN/BTC') - - -def test_fetch_order_or_stoploss_order(default_conf, mocker): - exchange = get_patched_exchange(mocker, default_conf, id='binance') - fetch_order_mock = MagicMock() - fetch_stoploss_order_mock = MagicMock() - mocker.patch.multiple('freqtrade.exchange.Exchange', - fetch_order=fetch_order_mock, - fetch_stoploss_order=fetch_stoploss_order_mock, - ) - - exchange.fetch_order_or_stoploss_order('1234', 'ETH/BTC', False) - assert fetch_order_mock.call_count == 1 - assert fetch_order_mock.call_args_list[0][0][0] == '1234' - assert fetch_order_mock.call_args_list[0][0][1] == 'ETH/BTC' - assert fetch_stoploss_order_mock.call_count == 0 - - fetch_order_mock.reset_mock() - fetch_stoploss_order_mock.reset_mock() - - exchange.fetch_order_or_stoploss_order('1234', 'ETH/BTC', True) - assert fetch_order_mock.call_count == 0 - assert fetch_stoploss_order_mock.call_count == 1 - assert fetch_stoploss_order_mock.call_args_list[0][0][0] == '1234' - assert fetch_stoploss_order_mock.call_args_list[0][0][1] == 'ETH/BTC' - - -@pytest.mark.parametrize("exchange_name", EXCHANGES) -def test_name(default_conf, mocker, exchange_name): - exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) - - assert exchange.name == exchange_name.title() - assert exchange.id == exchange_name - - -@pytest.mark.parametrize("trading_mode,amount", [ - ('spot', 0.2340606), - ('futures', 2.340606), -]) -@pytest.mark.parametrize("exchange_name", EXCHANGES) -def test_get_trades_for_order(default_conf, mocker, exchange_name, trading_mode, amount): - order_id = 'ABCD-ABCD' - since = datetime(2018, 5, 5, 0, 0, 0) - default_conf["dry_run"] = False - default_conf["trading_mode"] = trading_mode - default_conf["margin_mode"] = 'isolated' - mocker.patch('freqtrade.exchange.Exchange.exchange_has', return_value=True) - api_mock = MagicMock() - - api_mock.fetch_my_trades = MagicMock(return_value=[{'id': 'TTR67E-3PFBD-76IISV', - 'order': 'ABCD-ABCD', - 'info': {'pair': 'XLTCZBTC', - 'time': 1519860024.4388, - 'type': 'buy', - 'ordertype': 'limit', - 'price': '20.00000', - 'cost': '38.62000', - 'fee': '0.06179', - 'vol': '5', - 'id': 'ABCD-ABCD'}, - 'timestamp': 1519860024438, - 'datetime': '2018-02-28T23:20:24.438Z', - 'symbol': 'ETH/USDT:USDT', - 'type': 'limit', - 'side': 'buy', - 'price': 165.0, - 'amount': 0.2340606, - 'fee': {'cost': 0.06179, 'currency': 'BTC'} - }]) - - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - - orders = exchange.get_trades_for_order(order_id, 'ETH/USDT:USDT', since) - assert len(orders) == 1 - assert orders[0]['price'] == 165 - assert isclose(orders[0]['amount'], amount) - assert api_mock.fetch_my_trades.call_count == 1 - # since argument should be - assert isinstance(api_mock.fetch_my_trades.call_args[0][1], int) - assert api_mock.fetch_my_trades.call_args[0][0] == 'ETH/USDT:USDT' - # Same test twice, hardcoded number and doing the same calculation - assert api_mock.fetch_my_trades.call_args[0][1] == 1525478395000 - assert api_mock.fetch_my_trades.call_args[0][1] == int(since.replace( - tzinfo=timezone.utc).timestamp() - 5) * 1000 - - ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name, - 'get_trades_for_order', 'fetch_my_trades', - order_id=order_id, pair='ETH/USDT:USDT', since=since) - - mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=False)) - assert exchange.get_trades_for_order(order_id, 'ETH/USDT:USDT', since) == [] - - -@pytest.mark.parametrize("exchange_name", EXCHANGES) -def test_get_fee(default_conf, mocker, exchange_name): - api_mock = MagicMock() - api_mock.calculate_fee = MagicMock(return_value={ - 'type': 'taker', - 'currency': 'BTC', - 'rate': 0.025, - 'cost': 0.05 - }) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange._config.pop('fee', None) - - assert exchange.get_fee('ETH/BTC') == 0.025 - assert api_mock.calculate_fee.call_count == 1 - - ccxt_exceptionhandlers(mocker, default_conf, api_mock, exchange_name, - 'get_fee', 'calculate_fee', symbol="ETH/BTC") - - api_mock.calculate_fee.reset_mock() - exchange._config['fee'] = 0.001 - - assert exchange.get_fee('ETH/BTC') == 0.001 - assert api_mock.calculate_fee.call_count == 0 - - -def test_stoploss_order_unsupported_exchange(default_conf, mocker): - exchange = get_patched_exchange(mocker, default_conf, id='bittrex') - with pytest.raises(OperationalException, match=r"stoploss is not implemented .*"): - exchange.stoploss( - pair='ETH/BTC', - amount=1, - stop_price=220, - order_types={}, - side="sell", - leverage=1.0 - ) - - with pytest.raises(OperationalException, match=r"stoploss is not implemented .*"): - exchange.stoploss_adjust(1, {}, side="sell") - - -def test_merge_ft_has_dict(default_conf, mocker): - mocker.patch.multiple('freqtrade.exchange.Exchange', - _init_ccxt=MagicMock(return_value=MagicMock()), - _load_async_markets=MagicMock(), - validate_pairs=MagicMock(), - validate_timeframes=MagicMock(), - validate_stakecurrency=MagicMock(), - validate_pricing=MagicMock(), - ) - ex = Exchange(default_conf) - assert ex._ft_has == Exchange._ft_has_default - - ex = Kraken(default_conf) - assert ex._ft_has != Exchange._ft_has_default - assert ex._ft_has['trades_pagination'] == 'id' - assert ex._ft_has['trades_pagination_arg'] == 'since' - - # Binance defines different values - ex = Binance(default_conf) - assert ex._ft_has != Exchange._ft_has_default - assert ex._ft_has['stoploss_on_exchange'] - assert ex._ft_has['order_time_in_force'] == ['gtc', 'fok', 'ioc'] - assert ex._ft_has['trades_pagination'] == 'id' - assert ex._ft_has['trades_pagination_arg'] == 'fromId' - - conf = copy.deepcopy(default_conf) - conf['exchange']['_ft_has_params'] = {"DeadBeef": 20, - "stoploss_on_exchange": False} - # Use settings from configuration (overriding stoploss_on_exchange) - ex = Binance(conf) - assert ex._ft_has != Exchange._ft_has_default - assert not ex._ft_has['stoploss_on_exchange'] - assert ex._ft_has['DeadBeef'] == 20 - - -def test_get_valid_pair_combination(default_conf, mocker, markets): - mocker.patch.multiple('freqtrade.exchange.Exchange', - _init_ccxt=MagicMock(return_value=MagicMock()), - _load_async_markets=MagicMock(), - validate_pairs=MagicMock(), - validate_timeframes=MagicMock(), - validate_pricing=MagicMock(), - markets=PropertyMock(return_value=markets)) - ex = Exchange(default_conf) - - assert ex.get_valid_pair_combination("ETH", "BTC") == "ETH/BTC" - assert ex.get_valid_pair_combination("BTC", "ETH") == "ETH/BTC" - with pytest.raises(DependencyException, match=r"Could not combine.* to get a valid pair."): - ex.get_valid_pair_combination("NOPAIR", "ETH") - - -@pytest.mark.parametrize( - "base_currencies,quote_currencies,tradable_only,active_only,spot_only," - "futures_only,expected_keys,test_comment", [ - # Testing markets (in conftest.py): - # 'BLK/BTC': 'active': True - # 'BTT/BTC': 'active': True - # 'ETH/BTC': 'active': True - # 'ETH/USDT': 'active': True - # 'LTC/BTC': 'active': False - # 'LTC/ETH': 'active': True - # 'LTC/USD': 'active': True - # 'LTC/USDT': 'active': True - # 'NEO/BTC': 'active': False - # 'TKN/BTC': 'active' not set - # 'XLTCUSDT': 'active': True, not a pair - # 'XRP/BTC': 'active': False - ([], [], False, False, False, False, - ['BLK/BTC', 'BTT/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/ETH', 'LTC/USD', 'LTC/USDT', - 'NEO/BTC', 'TKN/BTC', 'XLTCUSDT', 'XRP/BTC', 'ADA/USDT:USDT', - 'ETH/USDT:USDT'], - 'all markets'), - ([], [], False, False, True, False, - ['BLK/BTC', 'BTT/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/ETH', 'LTC/USD', - 'LTC/USDT', 'NEO/BTC', 'TKN/BTC', 'XRP/BTC'], - 'all markets, only spot pairs'), - ([], [], False, True, False, False, - ['BLK/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/ETH', 'LTC/USD', 'NEO/BTC', - 'TKN/BTC', 'XLTCUSDT', 'XRP/BTC', 'ADA/USDT:USDT', 'ETH/USDT:USDT'], - 'active markets'), - ([], [], True, False, False, False, - ['BLK/BTC', 'BTT/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/ETH', 'LTC/USD', - 'LTC/USDT', 'NEO/BTC', 'TKN/BTC', 'XRP/BTC'], - 'all pairs'), - ([], [], True, True, False, False, - ['BLK/BTC', 'ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/ETH', 'LTC/USD', 'NEO/BTC', - 'TKN/BTC', 'XRP/BTC'], - 'active pairs'), - (['ETH', 'LTC'], [], False, False, False, False, - ['ETH/BTC', 'ETH/USDT', 'LTC/BTC', 'LTC/ETH', 'LTC/USD', 'LTC/USDT', 'XLTCUSDT', - 'ETH/USDT:USDT'], - 'all markets, base=ETH, LTC'), - (['LTC'], [], False, False, False, False, - ['LTC/BTC', 'LTC/ETH', 'LTC/USD', 'LTC/USDT', 'XLTCUSDT'], - 'all markets, base=LTC'), - (['LTC'], [], False, False, True, False, - ['LTC/BTC', 'LTC/ETH', 'LTC/USD', 'LTC/USDT'], - 'spot markets, base=LTC'), - ([], ['USDT'], False, False, False, False, - ['ETH/USDT', 'LTC/USDT', 'XLTCUSDT', 'ADA/USDT:USDT', 'ETH/USDT:USDT'], - 'all markets, quote=USDT'), - ([], ['USDT'], False, False, False, True, - ['ADA/USDT:USDT', 'ETH/USDT:USDT'], - 'Futures markets, quote=USDT'), - ([], ['USDT', 'USD'], False, False, False, False, - ['ETH/USDT', 'LTC/USD', 'LTC/USDT', 'XLTCUSDT', 'ADA/USDT:USDT', 'ETH/USDT:USDT'], - 'all markets, quote=USDT, USD'), - ([], ['USDT', 'USD'], False, False, True, False, - ['ETH/USDT', 'LTC/USD', 'LTC/USDT'], - 'spot markets, quote=USDT, USD'), - (['LTC'], ['USDT'], False, False, False, False, - ['LTC/USDT', 'XLTCUSDT'], - 'all markets, base=LTC, quote=USDT'), - (['LTC'], ['USDT'], True, False, False, False, - ['LTC/USDT'], - 'all pairs, base=LTC, quote=USDT'), - (['LTC'], ['USDT', 'NONEXISTENT'], False, False, False, False, - ['LTC/USDT', 'XLTCUSDT'], - 'all markets, base=LTC, quote=USDT, NONEXISTENT'), - (['LTC'], ['NONEXISTENT'], False, False, False, False, - [], - 'all markets, base=LTC, quote=NONEXISTENT'), - ]) -def test_get_markets(default_conf, mocker, markets_static, - base_currencies, quote_currencies, tradable_only, active_only, - spot_only, futures_only, expected_keys, - test_comment # Here for debugging purposes (Not used within method) - ): - mocker.patch.multiple('freqtrade.exchange.Exchange', - _init_ccxt=MagicMock(return_value=MagicMock()), - _load_async_markets=MagicMock(), - validate_pairs=MagicMock(), - validate_timeframes=MagicMock(), - validate_pricing=MagicMock(), - markets=PropertyMock(return_value=markets_static)) - ex = Exchange(default_conf) - pairs = ex.get_markets(base_currencies, - quote_currencies, - tradable_only=tradable_only, - spot_only=spot_only, - futures_only=futures_only, - active_only=active_only) - assert sorted(pairs.keys()) == sorted(expected_keys) - - -def test_get_markets_error(default_conf, mocker): - ex = get_patched_exchange(mocker, default_conf) - mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=None)) - with pytest.raises(OperationalException, match="Markets were not loaded."): - ex.get_markets('LTC', 'USDT', True, False) - - -@pytest.mark.parametrize("exchange_name", EXCHANGES) -def test_ohlcv_candle_limit(default_conf, mocker, exchange_name): - exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) - timeframes = ('1m', '5m', '1h') - expected = exchange._ft_has['ohlcv_candle_limit'] - for timeframe in timeframes: - if 'ohlcv_candle_limit_per_timeframe' in exchange._ft_has: - expected = exchange._ft_has['ohlcv_candle_limit_per_timeframe'][timeframe] - # This should only run for bittrex - assert exchange_name == 'bittrex' - assert exchange.ohlcv_candle_limit(timeframe) == expected - - -def test_timeframe_to_minutes(): - assert timeframe_to_minutes("5m") == 5 - assert timeframe_to_minutes("10m") == 10 - assert timeframe_to_minutes("1h") == 60 - assert timeframe_to_minutes("1d") == 1440 - - -def test_timeframe_to_seconds(): - assert timeframe_to_seconds("5m") == 300 - assert timeframe_to_seconds("10m") == 600 - assert timeframe_to_seconds("1h") == 3600 - assert timeframe_to_seconds("1d") == 86400 - - -def test_timeframe_to_msecs(): - assert timeframe_to_msecs("5m") == 300000 - assert timeframe_to_msecs("10m") == 600000 - assert timeframe_to_msecs("1h") == 3600000 - assert timeframe_to_msecs("1d") == 86400000 - - -def test_timeframe_to_prev_date(): - # 2019-08-12 13:22:08 - date = datetime.fromtimestamp(1565616128, tz=timezone.utc) - - tf_list = [ - # 5m -> 2019-08-12 13:20:00 - ("5m", datetime(2019, 8, 12, 13, 20, 0, tzinfo=timezone.utc)), - # 10m -> 2019-08-12 13:20:00 - ("10m", datetime(2019, 8, 12, 13, 20, 0, tzinfo=timezone.utc)), - # 1h -> 2019-08-12 13:00:00 - ("1h", datetime(2019, 8, 12, 13, 00, 0, tzinfo=timezone.utc)), - # 2h -> 2019-08-12 12:00:00 - ("2h", datetime(2019, 8, 12, 12, 00, 0, tzinfo=timezone.utc)), - # 4h -> 2019-08-12 12:00:00 - ("4h", datetime(2019, 8, 12, 12, 00, 0, tzinfo=timezone.utc)), - # 1d -> 2019-08-12 00:00:00 - ("1d", datetime(2019, 8, 12, 00, 00, 0, tzinfo=timezone.utc)), - ] - for interval, result in tf_list: - assert timeframe_to_prev_date(interval, date) == result - - date = datetime.now(tz=timezone.utc) - assert timeframe_to_prev_date("5m") < date - # Does not round - time = datetime(2019, 8, 12, 13, 20, 0, tzinfo=timezone.utc) - assert timeframe_to_prev_date('5m', time) == time - time = datetime(2019, 8, 12, 13, 0, 0, tzinfo=timezone.utc) - assert timeframe_to_prev_date('1h', time) == time - - -def test_timeframe_to_next_date(): - # 2019-08-12 13:22:08 - date = datetime.fromtimestamp(1565616128, tz=timezone.utc) - tf_list = [ - # 5m -> 2019-08-12 13:25:00 - ("5m", datetime(2019, 8, 12, 13, 25, 0, tzinfo=timezone.utc)), - # 10m -> 2019-08-12 13:30:00 - ("10m", datetime(2019, 8, 12, 13, 30, 0, tzinfo=timezone.utc)), - # 1h -> 2019-08-12 14:00:00 - ("1h", datetime(2019, 8, 12, 14, 00, 0, tzinfo=timezone.utc)), - # 2h -> 2019-08-12 14:00:00 - ("2h", datetime(2019, 8, 12, 14, 00, 0, tzinfo=timezone.utc)), - # 4h -> 2019-08-12 14:00:00 - ("4h", datetime(2019, 8, 12, 16, 00, 0, tzinfo=timezone.utc)), - # 1d -> 2019-08-13 00:00:00 - ("1d", datetime(2019, 8, 13, 0, 0, 0, tzinfo=timezone.utc)), - ] - - for interval, result in tf_list: - assert timeframe_to_next_date(interval, date) == result - - date = datetime.now(tz=timezone.utc) - assert timeframe_to_next_date("5m") > date - - date = datetime(2019, 8, 12, 13, 30, 0, tzinfo=timezone.utc) - assert timeframe_to_next_date("5m", date) == date + timedelta(minutes=5) - - -@pytest.mark.parametrize( - "market_symbol,base,quote,exchange,spot,margin,futures,trademode,add_dict,expected_result", - [ - ("BTC/USDT", 'BTC', 'USDT', "binance", True, False, False, 'spot', {}, True), - ("USDT/BTC", 'USDT', 'BTC', "binance", True, False, False, 'spot', {}, True), - # No seperating / - ("BTCUSDT", 'BTC', 'USDT', "binance", True, False, False, 'spot', {}, True), - ("BTCUSDT", None, "USDT", "binance", True, False, False, 'spot', {}, False), - ("USDT/BTC", "BTC", None, "binance", True, False, False, 'spot', {}, False), - ("BTCUSDT", "BTC", None, "binance", True, False, False, 'spot', {}, False), - ("BTC/USDT", "BTC", "USDT", "binance", True, False, False, 'spot', {}, True), - # Futures mode, spot pair - ("BTC/USDT", "BTC", "USDT", "binance", True, False, False, 'futures', {}, False), - ("BTC/USDT", "BTC", "USDT", "binance", True, False, False, 'margin', {}, False), - ("BTC/USDT", "BTC", "USDT", "binance", True, True, True, 'margin', {}, True), - ("BTC/USDT", "BTC", "USDT", "binance", False, True, False, 'margin', {}, True), - # Futures mode, futures pair - ("BTC/USDT", "BTC", "USDT", "binance", False, False, True, 'futures', {}, True), - # Futures market - ("BTC/UNK", "BTC", 'UNK', "binance", False, False, True, 'spot', {}, False), - ("BTC/EUR", 'BTC', 'EUR', "kraken", True, False, False, 'spot', {"darkpool": False}, True), - ("EUR/BTC", 'EUR', 'BTC', "kraken", True, False, False, 'spot', {"darkpool": False}, True), - # no darkpools - ("BTC/EUR", 'BTC', 'EUR', "kraken", True, False, False, 'spot', - {"darkpool": True}, False), - # no darkpools - ("BTC/EUR.d", 'BTC', 'EUR', "kraken", True, False, False, 'spot', - {"darkpool": True}, False), - ("BTC/USD", 'BTC', 'USD', "ftx", True, False, False, 'spot', {}, True), - ("USD/BTC", 'USD', 'BTC', "ftx", True, False, False, 'spot', {}, True), - # Can only trade spot markets - ("BTC/USD", 'BTC', 'USD', "ftx", False, False, True, 'spot', {}, False), - ("BTC/USD", 'BTC', 'USD', "ftx", False, False, True, 'futures', {}, True), - # Can only trade spot markets - ("BTC-PERP", 'BTC', 'USD', "ftx", False, False, True, 'spot', {}, False), - ("BTC-PERP", 'BTC', 'USD', "ftx", False, False, True, 'margin', {}, False), - ("BTC-PERP", 'BTC', 'USD', "ftx", False, False, True, 'futures', {}, True), - - ("BTC/USDT:USDT", 'BTC', 'USD', "okx", False, False, True, 'spot', {}, False), - ("BTC/USDT:USDT", 'BTC', 'USD', "okx", False, False, True, 'margin', {}, False), - ("BTC/USDT:USDT", 'BTC', 'USD', "okx", False, False, True, 'futures', {}, True), - ]) -def test_market_is_tradable( - mocker, default_conf, market_symbol, base, - quote, spot, margin, futures, trademode, add_dict, exchange, expected_result -) -> None: - default_conf['trading_mode'] = trademode - mocker.patch('freqtrade.exchange.exchange.Exchange.validate_trading_mode_and_margin_mode') - ex = get_patched_exchange(mocker, default_conf, id=exchange) - market = { - 'symbol': market_symbol, - 'base': base, - 'quote': quote, - 'spot': spot, - 'future': futures, - 'swap': futures, - 'margin': margin, - 'linear': True, - **(add_dict), - } - assert ex.market_is_tradable(market) == expected_result - - -@pytest.mark.parametrize("market,expected_result", [ - ({'symbol': 'ETH/BTC', 'active': True}, True), - ({'symbol': 'ETH/BTC', 'active': False}, False), - ({'symbol': 'ETH/BTC', }, True), -]) -def test_market_is_active(market, expected_result) -> None: - assert market_is_active(market) == expected_result - - -@pytest.mark.parametrize("order,expected", [ - ([{'fee'}], False), - ({'fee': None}, False), - ({'fee': {'currency': 'ETH/BTC'}}, False), - ({'fee': {'currency': 'ETH/BTC', 'cost': None}}, False), - ({'fee': {'currency': 'ETH/BTC', 'cost': 0.01}}, True), -]) -def test_order_has_fee(order, expected) -> None: - assert Exchange.order_has_fee(order) == expected - - -@pytest.mark.parametrize("order,expected", [ - ({'symbol': 'ETH/BTC', 'fee': {'currency': 'ETH', 'cost': 0.43}}, - (0.43, 'ETH', 0.01)), - ({'symbol': 'ETH/USDT', 'fee': {'currency': 'USDT', 'cost': 0.01}}, - (0.01, 'USDT', 0.01)), - ({'symbol': 'BTC/USDT', 'fee': {'currency': 'USDT', 'cost': 0.34, 'rate': 0.01}}, - (0.34, 'USDT', 0.01)), -]) -def test_extract_cost_curr_rate(mocker, default_conf, order, expected) -> None: - mocker.patch('freqtrade.exchange.Exchange.calculate_fee_rate', MagicMock(return_value=0.01)) - ex = get_patched_exchange(mocker, default_conf) - assert ex.extract_cost_curr_rate(order) == expected - - -@pytest.mark.parametrize("order,unknown_fee_rate,expected", [ - # Using base-currency - ({'symbol': 'ETH/BTC', 'amount': 0.04, 'cost': 0.05, - 'fee': {'currency': 'ETH', 'cost': 0.004, 'rate': None}}, None, 0.1), - ({'symbol': 'ETH/BTC', 'amount': 0.05, 'cost': 0.05, - 'fee': {'currency': 'ETH', 'cost': 0.004, 'rate': None}}, None, 0.08), - # Using quote currency - ({'symbol': 'ETH/BTC', 'amount': 0.04, 'cost': 0.05, - 'fee': {'currency': 'BTC', 'cost': 0.005}}, None, 0.1), - ({'symbol': 'ETH/BTC', 'amount': 0.04, 'cost': 0.05, - 'fee': {'currency': 'BTC', 'cost': 0.002, 'rate': None}}, None, 0.04), - # Using foreign currency - ({'symbol': 'ETH/BTC', 'amount': 0.04, 'cost': 0.05, - 'fee': {'currency': 'NEO', 'cost': 0.0012}}, None, 0.001944), - ({'symbol': 'ETH/BTC', 'amount': 2.21, 'cost': 0.02992561, - 'fee': {'currency': 'NEO', 'cost': 0.00027452}}, None, 0.00074305), - # Rate included in return - return as is - ({'symbol': 'ETH/BTC', 'amount': 0.04, 'cost': 0.05, - 'fee': {'currency': 'USDT', 'cost': 0.34, 'rate': 0.01}}, None, 0.01), - ({'symbol': 'ETH/BTC', 'amount': 0.04, 'cost': 0.05, - 'fee': {'currency': 'USDT', 'cost': 0.34, 'rate': 0.005}}, None, 0.005), - # 0.1% filled - no costs (kraken - #3431) - ({'symbol': 'ETH/BTC', 'amount': 0.04, 'cost': 0.0, - 'fee': {'currency': 'BTC', 'cost': 0.0, 'rate': None}}, None, None), - ({'symbol': 'ETH/BTC', 'amount': 0.04, 'cost': 0.0, - 'fee': {'currency': 'ETH', 'cost': 0.0, 'rate': None}}, None, 0.0), - ({'symbol': 'ETH/BTC', 'amount': 0.04, 'cost': 0.0, - 'fee': {'currency': 'NEO', 'cost': 0.0, 'rate': None}}, None, None), - # Invalid pair combination - POINT/BTC is not a pair - ({'symbol': 'POINT/BTC', 'amount': 0.04, 'cost': 0.5, - 'fee': {'currency': 'POINT', 'cost': 2.0, 'rate': None}}, None, None), - ({'symbol': 'POINT/BTC', 'amount': 0.04, 'cost': 0.5, - 'fee': {'currency': 'POINT', 'cost': 2.0, 'rate': None}}, 1, 4.0), - ({'symbol': 'POINT/BTC', 'amount': 0.04, 'cost': 0.5, - 'fee': {'currency': 'POINT', 'cost': 2.0, 'rate': None}}, 2, 8.0), -]) -def test_calculate_fee_rate(mocker, default_conf, order, expected, unknown_fee_rate) -> None: - mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', return_value={'last': 0.081}) - if unknown_fee_rate: - default_conf['exchange']['unknown_fee_rate'] = unknown_fee_rate - - ex = get_patched_exchange(mocker, default_conf) - - assert ex.calculate_fee_rate(order) == expected - - -@pytest.mark.parametrize('retrycount,max_retries,expected', [ - (0, 3, 10), - (1, 3, 5), - (2, 3, 2), - (3, 3, 1), - (0, 1, 2), - (1, 1, 1), - (0, 4, 17), - (1, 4, 10), - (2, 4, 5), - (3, 4, 2), - (4, 4, 1), - (0, 5, 26), - (1, 5, 17), - (2, 5, 10), - (3, 5, 5), - (4, 5, 2), - (5, 5, 1), -]) -def test_calculate_backoff(retrycount, max_retries, expected): - assert calculate_backoff(retrycount, max_retries) == expected - - -@pytest.mark.parametrize("exchange_name", ['binance', 'ftx']) -def test__get_funding_fees_from_exchange(default_conf, mocker, exchange_name): - api_mock = MagicMock() - api_mock.fetch_funding_history = MagicMock(return_value=[ - { - 'amount': 0.14542, - 'code': 'USDT', - 'datetime': '2021-09-01T08:00:01.000Z', - 'id': '485478', - 'info': {'asset': 'USDT', - 'income': '0.14542', - 'incomeType': 'FUNDING_FEE', - 'info': 'FUNDING_FEE', - 'symbol': 'XRPUSDT', - 'time': '1630382001000', - 'tradeId': '', - 'tranId': '993203'}, - 'symbol': 'XRP/USDT', - 'timestamp': 1630382001000 - }, - { - 'amount': -0.14642, - 'code': 'USDT', - 'datetime': '2021-09-01T16:00:01.000Z', - 'id': '485479', - 'info': {'asset': 'USDT', - 'income': '-0.14642', - 'incomeType': 'FUNDING_FEE', - 'info': 'FUNDING_FEE', - 'symbol': 'XRPUSDT', - 'time': '1630314001000', - 'tradeId': '', - 'tranId': '993204'}, - 'symbol': 'XRP/USDT', - 'timestamp': 1630314001000 - } - ]) - type(api_mock).has = PropertyMock(return_value={'fetchFundingHistory': True}) - - # mocker.patch('freqtrade.exchange.Exchange.get_funding_fees', lambda pair, since: y) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - date_time = datetime.strptime("2021-09-01T00:00:01.000Z", '%Y-%m-%dT%H:%M:%S.%fZ') - unix_time = int(date_time.timestamp()) - expected_fees = -0.001 # 0.14542341 + -0.14642341 - fees_from_datetime = exchange._get_funding_fees_from_exchange( - pair='XRP/USDT', - since=date_time - ) - fees_from_unix_time = exchange._get_funding_fees_from_exchange( - pair='XRP/USDT', - since=unix_time - ) - - assert(isclose(expected_fees, fees_from_datetime)) - assert(isclose(expected_fees, fees_from_unix_time)) - - ccxt_exceptionhandlers( - mocker, - default_conf, - api_mock, - exchange_name, - "_get_funding_fees_from_exchange", - "fetch_funding_history", - pair="XRP/USDT", - since=unix_time - ) - - -@pytest.mark.parametrize('exchange', ['binance', 'kraken', 'ftx']) -@pytest.mark.parametrize('stake_amount,leverage,min_stake_with_lev', [ - (9.0, 3.0, 3.0), - (20.0, 5.0, 4.0), - (100.0, 100.0, 1.0) -]) -def test_get_stake_amount_considering_leverage( - exchange, - stake_amount, - leverage, - min_stake_with_lev, - mocker, - default_conf -): - exchange = get_patched_exchange(mocker, default_conf, id=exchange) - assert exchange._get_stake_amount_considering_leverage( - stake_amount, leverage) == min_stake_with_lev - - -@pytest.mark.parametrize("exchange_name,trading_mode", [ - ("binance", TradingMode.FUTURES), - ("ftx", TradingMode.MARGIN), - ("ftx", TradingMode.FUTURES) -]) -def test__set_leverage(mocker, default_conf, exchange_name, trading_mode): - - api_mock = MagicMock() - api_mock.set_leverage = MagicMock() - type(api_mock).has = PropertyMock(return_value={'setLeverage': True}) - default_conf['dry_run'] = False - - ccxt_exceptionhandlers( - mocker, - default_conf, - api_mock, - exchange_name, - "_set_leverage", - "set_leverage", - pair="XRP/USDT", - leverage=5.0, - trading_mode=trading_mode - ) - - -@pytest.mark.parametrize("margin_mode", [ - (MarginMode.CROSS), - (MarginMode.ISOLATED) -]) -def test_set_margin_mode(mocker, default_conf, margin_mode): - - api_mock = MagicMock() - api_mock.set_margin_mode = MagicMock() - type(api_mock).has = PropertyMock(return_value={'setMarginMode': True}) - default_conf['dry_run'] = False - - ccxt_exceptionhandlers( - mocker, - default_conf, - api_mock, - "binance", - "set_margin_mode", - "set_margin_mode", - pair="XRP/USDT", - margin_mode=margin_mode - ) - - -@pytest.mark.parametrize("exchange_name, trading_mode, margin_mode, exception_thrown", [ - ("binance", TradingMode.SPOT, None, False), - ("binance", TradingMode.MARGIN, MarginMode.ISOLATED, True), - ("kraken", TradingMode.SPOT, None, False), - ("kraken", TradingMode.MARGIN, MarginMode.ISOLATED, True), - ("kraken", TradingMode.FUTURES, MarginMode.ISOLATED, True), - ("ftx", TradingMode.SPOT, None, False), - ("ftx", TradingMode.MARGIN, MarginMode.ISOLATED, True), - ("ftx", TradingMode.FUTURES, MarginMode.ISOLATED, True), - ("bittrex", TradingMode.SPOT, None, False), - ("bittrex", TradingMode.MARGIN, MarginMode.CROSS, True), - ("bittrex", TradingMode.MARGIN, MarginMode.ISOLATED, True), - ("bittrex", TradingMode.FUTURES, MarginMode.CROSS, True), - ("bittrex", TradingMode.FUTURES, MarginMode.ISOLATED, True), - ("gateio", TradingMode.MARGIN, MarginMode.ISOLATED, True), - ("okx", TradingMode.SPOT, None, False), - ("okx", TradingMode.MARGIN, MarginMode.CROSS, True), - ("okx", TradingMode.MARGIN, MarginMode.ISOLATED, True), - ("okx", TradingMode.FUTURES, MarginMode.CROSS, True), - - ("binance", TradingMode.FUTURES, MarginMode.ISOLATED, False), - ("gateio", TradingMode.FUTURES, MarginMode.ISOLATED, False), - ("okx", TradingMode.FUTURES, MarginMode.ISOLATED, False), - - # * Remove once implemented - ("binance", TradingMode.MARGIN, MarginMode.CROSS, True), - ("binance", TradingMode.FUTURES, MarginMode.CROSS, True), - ("kraken", TradingMode.MARGIN, MarginMode.CROSS, True), - ("kraken", TradingMode.FUTURES, MarginMode.CROSS, True), - ("ftx", TradingMode.MARGIN, MarginMode.CROSS, True), - ("ftx", TradingMode.FUTURES, MarginMode.CROSS, True), - ("gateio", TradingMode.MARGIN, MarginMode.CROSS, True), - ("gateio", TradingMode.FUTURES, MarginMode.CROSS, True), - - # * Uncomment once implemented - # ("binance", TradingMode.MARGIN, MarginMode.CROSS, False), - # ("binance", TradingMode.FUTURES, MarginMode.CROSS, False), - # ("kraken", TradingMode.MARGIN, MarginMode.CROSS, False), - # ("kraken", TradingMode.FUTURES, MarginMode.CROSS, False), - # ("ftx", TradingMode.MARGIN, MarginMode.CROSS, False), - # ("ftx", TradingMode.FUTURES, MarginMode.CROSS, False), - # ("gateio", TradingMode.MARGIN, MarginMode.CROSS, False), - # ("gateio", TradingMode.FUTURES, MarginMode.CROSS, False), -]) -def test_validate_trading_mode_and_margin_mode( - default_conf, - mocker, - exchange_name, - trading_mode, - margin_mode, - exception_thrown -): - exchange = get_patched_exchange( - mocker, default_conf, id=exchange_name, mock_supported_modes=False) - if (exception_thrown): - with pytest.raises(OperationalException): - exchange.validate_trading_mode_and_margin_mode(trading_mode, margin_mode) - else: - exchange.validate_trading_mode_and_margin_mode(trading_mode, margin_mode) - - -@pytest.mark.parametrize("exchange_name,trading_mode,ccxt_config", [ - ("binance", "spot", {}), - ("binance", "margin", {"options": {"defaultType": "margin"}}), - ("binance", "futures", {"options": {"defaultType": "future"}}), - ("bibox", "spot", {"has": {"fetchCurrencies": False}}), - ("bibox", "margin", {"has": {"fetchCurrencies": False}, "options": {"defaultType": "margin"}}), - ("bibox", "futures", {"has": {"fetchCurrencies": False}, "options": {"defaultType": "swap"}}), - ("bybit", "futures", {"options": {"defaultType": "linear"}}), - ("ftx", "futures", {"options": {"defaultType": "swap"}}), - ("gateio", "futures", {"options": {"defaultType": "swap"}}), - ("hitbtc", "futures", {"options": {"defaultType": "swap"}}), - ("kraken", "futures", {"options": {"defaultType": "swap"}}), - ("kucoin", "futures", {"options": {"defaultType": "swap"}}), - ("okx", "futures", {"options": {"defaultType": "swap"}}), -]) -def test__ccxt_config( - default_conf, - mocker, - exchange_name, - trading_mode, - ccxt_config -): - default_conf['trading_mode'] = trading_mode - default_conf['margin_mode'] = 'isolated' - exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) - assert exchange._ccxt_config == ccxt_config - - -@pytest.mark.parametrize('pair,nominal_value,max_lev', [ - ("ETH/BTC", 0.0, 2.0), - ("TKN/BTC", 100.0, 5.0), - ("BLK/BTC", 173.31, 3.0), - ("LTC/BTC", 0.0, 1.0), - ("TKN/USDT", 210.30, 1.0), -]) -def test_get_max_leverage_from_margin(default_conf, mocker, pair, nominal_value, max_lev): - default_conf['trading_mode'] = 'margin' - default_conf['margin_mode'] = 'isolated' - api_mock = MagicMock() - type(api_mock).has = PropertyMock(return_value={'fetchLeverageTiers': False}) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id="gateio") - assert exchange.get_max_leverage(pair, nominal_value) == max_lev - - -@pytest.mark.parametrize( - 'size,funding_rate,mark_price,time_in_ratio,funding_fee,kraken_fee', [ - (10, 0.0001, 2.0, 1.0, 0.002, 0.002), - (10, 0.0002, 2.0, 0.01, 0.004, 0.00004), - (10, 0.0002, 2.5, None, 0.005, None), - ]) -def test_calculate_funding_fees( - default_conf, - mocker, - size, - funding_rate, - mark_price, - funding_fee, - kraken_fee, - time_in_ratio -): - exchange = get_patched_exchange(mocker, default_conf) - kraken = get_patched_exchange(mocker, default_conf, id="kraken") - prior_date = timeframe_to_prev_date('1h', datetime.now(timezone.utc) - timedelta(hours=1)) - trade_date = timeframe_to_prev_date('1h', datetime.now(timezone.utc)) - funding_rates = DataFrame([ - {'date': prior_date, 'open': funding_rate}, # Line not used. - {'date': trade_date, 'open': funding_rate}, - ]) - mark_rates = DataFrame([ - {'date': prior_date, 'open': mark_price}, - {'date': trade_date, 'open': mark_price}, - ]) - df = exchange.combine_funding_and_mark(funding_rates, mark_rates) - - assert exchange.calculate_funding_fees( - df, - amount=size, - is_short=True, - open_date=trade_date, - close_date=trade_date, - time_in_ratio=time_in_ratio, - ) == funding_fee - - if (kraken_fee is None): - with pytest.raises(OperationalException): - kraken.calculate_funding_fees( - df, - amount=size, - is_short=True, - open_date=trade_date, - close_date=trade_date, - time_in_ratio=time_in_ratio, - ) - - else: - assert kraken.calculate_funding_fees( - df, - amount=size, - is_short=True, - open_date=trade_date, - close_date=trade_date, - time_in_ratio=time_in_ratio, - ) == kraken_fee - - -def test_get_or_calculate_liquidation_price(mocker, default_conf): - - api_mock = MagicMock() - positions = [ - { - 'info': {}, - 'symbol': 'NEAR/USDT:USDT', - 'timestamp': 1642164737148, - 'datetime': '2022-01-14T12:52:17.148Z', - 'initialMargin': 1.51072, - 'initialMarginPercentage': 0.1, - 'maintenanceMargin': 0.38916147, - 'maintenanceMarginPercentage': 0.025, - 'entryPrice': 18.884, - 'notional': 15.1072, - 'leverage': 9.97, - 'unrealizedPnl': 0.0048, - 'contracts': 8, - 'contractSize': 0.1, - 'marginRatio': None, - 'liquidationPrice': 17.47, - 'markPrice': 18.89, - 'margin_mode': 1.52549075, - 'marginType': 'isolated', - 'side': 'buy', - 'percentage': 0.003177292946409658 - } - ] - api_mock.fetch_positions = MagicMock(return_value=positions) - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - exchange_has=MagicMock(return_value=True), - ) - default_conf['dry_run'] = False - default_conf['trading_mode'] = 'futures' - default_conf['margin_mode'] = 'isolated' - default_conf['liquidation_buffer'] = 0.0 - - exchange = get_patched_exchange(mocker, default_conf, api_mock) - liq_price = exchange.get_or_calculate_liquidation_price( - pair='NEAR/USDT:USDT', - open_rate=18.884, - is_short=False, - position=0.8, - wallet_balance=0.8, - ) - assert liq_price == 17.47 - - default_conf['liquidation_buffer'] = 0.05 - exchange = get_patched_exchange(mocker, default_conf, api_mock) - liq_price = exchange.get_or_calculate_liquidation_price( - pair='NEAR/USDT:USDT', - open_rate=18.884, - is_short=False, - position=0.8, - wallet_balance=0.8, - ) - assert liq_price == 17.540699999999998 - - ccxt_exceptionhandlers( - mocker, - default_conf, - api_mock, - "binance", - "get_or_calculate_liquidation_price", - "fetch_positions", - pair="XRP/USDT", - open_rate=0.0, - is_short=False, - position=0.0, - wallet_balance=0.0, - ) - - -@pytest.mark.parametrize('exchange,rate_start,rate_end,d1,d2,amount,expected_fees', [ - ('binance', 0, 2, "2021-09-01 01:00:00", "2021-09-01 04:00:00", 30.0, 0.0), - ('binance', 0, 2, "2021-09-01 00:00:00", "2021-09-01 08:00:00", 30.0, -0.00091409999), - ('binance', 0, 2, "2021-09-01 00:00:15", "2021-09-01 08:00:00", 30.0, -0.0002493), - ('binance', 1, 2, "2021-09-01 01:00:14", "2021-09-01 08:00:00", 30.0, -0.0002493), - ('binance', 1, 2, "2021-09-01 00:00:16", "2021-09-01 08:00:00", 30.0, -0.0002493), - ('binance', 0, 1, "2021-09-01 00:00:00", "2021-09-01 07:59:59", 30.0, -0.00066479999), - ('binance', 0, 2, "2021-09-01 00:00:00", "2021-09-01 12:00:00", 30.0, -0.00091409999), - ('binance', 0, 2, "2021-09-01 00:00:01", "2021-09-01 08:00:00", 30.0, -0.0002493), - # TODO: Uncoment once _calculate_funding_fees can pas time_in_ratio to exchange._get_funding_fee - # ('kraken', "2021-09-01 00:00:00", "2021-09-01 08:00:00", 30.0, -0.0014937), - # ('kraken', "2021-09-01 00:00:15", "2021-09-01 08:00:00", 30.0, -0.0008289), - # ('kraken', "2021-09-01 01:00:14", "2021-09-01 08:00:00", 30.0, -0.0008289), - # ('kraken', "2021-09-01 00:00:00", "2021-09-01 07:59:59", 30.0, -0.0012443999999999999), - # ('kraken', "2021-09-01 00:00:00", "2021-09-01 12:00:00", 30.0, 0.0045759), - # ('kraken', "2021-09-01 00:00:01", "2021-09-01 08:00:00", 30.0, -0.0008289), - ('ftx', 0, 2, "2021-09-01 00:10:00", "2021-09-01 00:30:00", 30.0, 0.0), - ('ftx', 0, 9, "2021-09-01 00:00:00", "2021-09-01 08:00:00", 30.0, 0.0010008), - ('ftx', 0, 13, "2021-09-01 00:00:00", "2021-09-01 12:00:00", 30.0, 0.0146691), - ('ftx', 0, 9, "2021-09-01 00:00:00", "2021-09-01 08:00:00", 50.0, 0.001668), - ('ftx', 1, 9, "2021-09-01 00:00:01", "2021-09-01 08:00:00", 30.0, 0.0019932), - ('gateio', 0, 2, "2021-09-01 00:10:00", "2021-09-01 04:00:00", 30.0, 0.0), - ('gateio', 0, 2, "2021-09-01 00:00:00", "2021-09-01 08:00:00", 30.0, -0.0009140999), - ('gateio', 0, 2, "2021-09-01 00:00:00", "2021-09-01 12:00:00", 30.0, -0.0009140999), - ('gateio', 1, 2, "2021-09-01 00:00:01", "2021-09-01 08:00:00", 30.0, -0.0002493), - ('binance', 0, 2, "2021-09-01 00:00:00", "2021-09-01 08:00:00", 50.0, -0.0015235), - # TODO: Uncoment once _calculate_funding_fees can pas time_in_ratio to exchange._get_funding_fee - # ('kraken', "2021-09-01 00:00:00", "2021-09-01 08:00:00", 50.0, -0.0024895), -]) -def test__fetch_and_calculate_funding_fees( - mocker, - default_conf, - funding_rate_history_hourly, - funding_rate_history_octohourly, - rate_start, - rate_end, - mark_ohlcv, - exchange, - d1, - d2, - amount, - expected_fees -): - """ - nominal_value = mark_price * size - funding_fee = nominal_value * funding_rate - size: 30 - time: 0, mark: 2.77, nominal_value: 83.1, fundRate: -0.000008, fundFee: -0.0006648 - time: 1, mark: 2.73, nominal_value: 81.9, fundRate: -0.000004, fundFee: -0.0003276 - time: 2, mark: 2.74, nominal_value: 82.2, fundRate: 0.000012, fundFee: 0.0009864 - time: 3, mark: 2.76, nominal_value: 82.8, fundRate: -0.000003, fundFee: -0.0002484 - time: 4, mark: 2.76, nominal_value: 82.8, fundRate: -0.000007, fundFee: -0.0005796 - time: 5, mark: 2.77, nominal_value: 83.1, fundRate: 0.000003, fundFee: 0.0002493 - time: 6, mark: 2.78, nominal_value: 83.4, fundRate: 0.000019, fundFee: 0.0015846 - time: 7, mark: 2.78, nominal_value: 83.4, fundRate: 0.000003, fundFee: 0.0002502 - time: 8, mark: 2.77, nominal_value: 83.1, fundRate: -0.000003, fundFee: -0.0002493 - time: 9, mark: 2.77, nominal_value: 83.1, fundRate: 0, fundFee: 0.0 - time: 10, mark: 2.84, nominal_value: 85.2, fundRate: 0.000013, fundFee: 0.0011076 - time: 11, mark: 2.81, nominal_value: 84.3, fundRate: 0.000077, fundFee: 0.0064911 - time: 12, mark: 2.81, nominal_value: 84.3, fundRate: 0.000072, fundFee: 0.0060696 - time: 13, mark: 2.82, nominal_value: 84.6, fundRate: 0.000097, fundFee: 0.0082062 - - size: 50 - time: 0, mark: 2.77, nominal_value: 138.5, fundRate: -0.000008, fundFee: -0.001108 - time: 1, mark: 2.73, nominal_value: 136.5, fundRate: -0.000004, fundFee: -0.000546 - time: 2, mark: 2.74, nominal_value: 137.0, fundRate: 0.000012, fundFee: 0.001644 - time: 3, mark: 2.76, nominal_value: 138.0, fundRate: -0.000003, fundFee: -0.000414 - time: 4, mark: 2.76, nominal_value: 138.0, fundRate: -0.000007, fundFee: -0.000966 - time: 5, mark: 2.77, nominal_value: 138.5, fundRate: 0.000003, fundFee: 0.0004155 - time: 6, mark: 2.78, nominal_value: 139.0, fundRate: 0.000019, fundFee: 0.002641 - time: 7, mark: 2.78, nominal_value: 139.0, fundRate: 0.000003, fundFee: 0.000417 - time: 8, mark: 2.77, nominal_value: 138.5, fundRate: -0.000003, fundFee: -0.0004155 - time: 9, mark: 2.77, nominal_value: 138.5, fundRate: 0, fundFee: 0.0 - time: 10, mark: 2.84, nominal_value: 142.0, fundRate: 0.000013, fundFee: 0.001846 - time: 11, mark: 2.81, nominal_value: 140.5, fundRate: 0.000077, fundFee: 0.0108185 - time: 12, mark: 2.81, nominal_value: 140.5, fundRate: 0.000072, fundFee: 0.010116 - time: 13, mark: 2.82, nominal_value: 141.0, fundRate: 0.000097, fundFee: 0.013677 - """ - d1 = datetime.strptime(f"{d1} +0000", '%Y-%m-%d %H:%M:%S %z') - d2 = datetime.strptime(f"{d2} +0000", '%Y-%m-%d %H:%M:%S %z') - funding_rate_history = { - 'binance': funding_rate_history_octohourly, - 'ftx': funding_rate_history_hourly, - 'gateio': funding_rate_history_octohourly, - }[exchange][rate_start:rate_end] - api_mock = MagicMock() - api_mock.fetch_funding_rate_history = get_mock_coro(return_value=funding_rate_history) - api_mock.fetch_ohlcv = get_mock_coro(return_value=mark_ohlcv) - type(api_mock).has = PropertyMock(return_value={'fetchOHLCV': True}) - type(api_mock).has = PropertyMock(return_value={'fetchFundingRateHistory': True}) - - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange) - mocker.patch('freqtrade.exchange.Exchange.timeframes', PropertyMock( - return_value=['1h', '4h', '8h'])) - funding_fees = exchange._fetch_and_calculate_funding_fees( - pair='ADA/USDT', amount=amount, is_short=True, open_date=d1, close_date=d2) - assert pytest.approx(funding_fees) == expected_fees - # Fees for Longs are inverted - funding_fees = exchange._fetch_and_calculate_funding_fees( - pair='ADA/USDT', amount=amount, is_short=False, open_date=d1, close_date=d2) - assert pytest.approx(funding_fees) == -expected_fees - - -@pytest.mark.parametrize('exchange,expected_fees', [ - ('binance', -0.0009140999999999999), - ('gateio', -0.0009140999999999999), -]) -def test__fetch_and_calculate_funding_fees_datetime_called( - mocker, - default_conf, - funding_rate_history_octohourly, - mark_ohlcv, - exchange, - time_machine, - expected_fees -): - api_mock = MagicMock() - api_mock.fetch_ohlcv = get_mock_coro(return_value=mark_ohlcv) - api_mock.fetch_funding_rate_history = get_mock_coro( - return_value=funding_rate_history_octohourly) - type(api_mock).has = PropertyMock(return_value={'fetchOHLCV': True}) - type(api_mock).has = PropertyMock(return_value={'fetchFundingRateHistory': True}) - mocker.patch('freqtrade.exchange.Exchange.timeframes', PropertyMock(return_value=['4h', '8h'])) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange) - d1 = datetime.strptime("2021-09-01 00:00:00 +0000", '%Y-%m-%d %H:%M:%S %z') - - time_machine.move_to("2021-09-01 08:00:00 +00:00") - funding_fees = exchange._fetch_and_calculate_funding_fees('ADA/USDT', 30.0, True, d1) - assert funding_fees == expected_fees - funding_fees = exchange._fetch_and_calculate_funding_fees('ADA/USDT', 30.0, False, d1) - assert funding_fees == 0 - expected_fees - - -@pytest.mark.parametrize('pair,expected_size,trading_mode', [ - ('XLTCUSDT', 1, 'spot'), - ('LTC/USD', 1, 'futures'), - ('XLTCUSDT', 0.01, 'futures'), - ('ETH/USDT:USDT', 10, 'futures') -]) -def test__get_contract_size(mocker, default_conf, pair, expected_size, trading_mode): - api_mock = MagicMock() - default_conf['trading_mode'] = trading_mode - default_conf['margin_mode'] = 'isolated' - exchange = get_patched_exchange(mocker, default_conf, api_mock) - mocker.patch('freqtrade.exchange.Exchange.markets', { - 'LTC/USD': { - 'symbol': 'LTC/USD', - 'contractSize': None, - }, - 'XLTCUSDT': { - 'symbol': 'XLTCUSDT', - 'contractSize': '0.01', - }, - 'ETH/USDT:USDT': { - 'symbol': 'ETH/USDT:USDT', - 'contractSize': '10', - } - }) - size = exchange._get_contract_size(pair) - assert expected_size == size - - -@pytest.mark.parametrize('pair,contract_size,trading_mode', [ - ('XLTCUSDT', 1, 'spot'), - ('LTC/USD', 1, 'futures'), - ('ADA/USDT:USDT', 0.01, 'futures'), - ('LTC/ETH', 1, 'futures'), - ('ETH/USDT:USDT', 10, 'futures'), -]) -def test__order_contracts_to_amount( - mocker, - default_conf, - markets, - pair, - contract_size, - trading_mode, -): - api_mock = MagicMock() - default_conf['trading_mode'] = trading_mode - default_conf['margin_mode'] = 'isolated' - mocker.patch('freqtrade.exchange.Exchange.markets', markets) - exchange = get_patched_exchange(mocker, default_conf, api_mock) - - orders = [ - { - 'id': '123456320', - 'clientOrderId': '12345632018', - 'timestamp': 1640124992000, - 'datetime': 'Tue 21 Dec 2021 22:16:32 UTC', - 'lastTradeTimestamp': 1640124911000, - 'status': 'active', - 'symbol': pair, - 'type': 'limit', - 'timeInForce': 'gtc', - 'postOnly': None, - 'side': 'buy', - 'price': 2.0, - 'stopPrice': None, - 'average': None, - 'amount': 30.0, - 'cost': 60.0, - 'filled': None, - 'remaining': 30.0, - 'fee': 0.06, - 'fees': [{ - 'currency': 'USDT', - 'cost': 0.06, - }], - 'trades': None, - 'info': {}, - }, - { - 'id': '123456380', - 'clientOrderId': '12345638203', - 'timestamp': 1640124992000, - 'datetime': 'Tue 21 Dec 2021 22:16:32 UTC', - 'lastTradeTimestamp': 1640124911000, - 'status': 'active', - 'symbol': pair, - 'type': 'limit', - 'timeInForce': 'gtc', - 'postOnly': None, - 'side': 'sell', - 'price': 2.2, - 'stopPrice': None, - 'average': None, - 'amount': 40.0, - 'cost': 80.0, - 'filled': None, - 'remaining': 40.0, - 'fee': 0.08, - 'fees': [{ - 'currency': 'USDT', - 'cost': 0.08, - }], - 'trades': None, - 'info': {}, - }, - { - # Realistic stoploss order on gateio. - 'id': '123456380', - 'clientOrderId': '12345638203', - 'timestamp': None, - 'datetime': None, - 'lastTradeTimestamp': None, - 'status': None, - 'symbol': None, - 'type': None, - 'timeInForce': None, - 'postOnly': None, - 'side': None, - 'price': None, - 'stopPrice': None, - 'average': None, - 'amount': None, - 'cost': None, - 'filled': None, - 'remaining': None, - 'fee': None, - 'fees': [], - 'trades': None, - 'info': {}, - }, - ] - - order1 = exchange._order_contracts_to_amount(orders[0]) - order2 = exchange._order_contracts_to_amount(orders[1]) - exchange._order_contracts_to_amount(orders[2]) - assert order1['amount'] == 30.0 * contract_size - assert order2['amount'] == 40.0 * contract_size - - -@pytest.mark.parametrize('pair,contract_size,trading_mode', [ - ('XLTCUSDT', 1, 'spot'), - ('LTC/USD', 1, 'futures'), - ('ADA/USDT:USDT', 0.01, 'futures'), - ('LTC/ETH', 1, 'futures'), - ('ETH/USDT:USDT', 10, 'futures'), -]) -def test__trades_contracts_to_amount( - mocker, - default_conf, - markets, - pair, - contract_size, - trading_mode, -): - api_mock = MagicMock() - default_conf['trading_mode'] = trading_mode - default_conf['margin_mode'] = 'isolated' - mocker.patch('freqtrade.exchange.Exchange.markets', markets) - exchange = get_patched_exchange(mocker, default_conf, api_mock) - - trades = [ - { - 'symbol': pair, - 'amount': 30.0, - }, - { - 'symbol': pair, - 'amount': 40.0, - } - ] - - new_amount_trades = exchange._trades_contracts_to_amount(trades) - assert new_amount_trades[0]['amount'] == 30.0 * contract_size - assert new_amount_trades[1]['amount'] == 40.0 * contract_size - - -@pytest.mark.parametrize('pair,param_amount,param_size', [ - ('ADA/USDT:USDT', 40, 4000), - ('LTC/ETH', 30, 30), - ('LTC/USD', 30, 30), - ('ETH/USDT:USDT', 10, 1), -]) -def test__amount_to_contracts( - mocker, - default_conf, - pair, - param_amount, - param_size -): - api_mock = MagicMock() - default_conf['trading_mode'] = 'spot' - default_conf['margin_mode'] = 'isolated' - exchange = get_patched_exchange(mocker, default_conf, api_mock) - mocker.patch('freqtrade.exchange.Exchange.markets', { - 'LTC/USD': { - 'symbol': 'LTC/USD', - 'contractSize': None, - }, - 'XLTCUSDT': { - 'symbol': 'XLTCUSDT', - 'contractSize': '0.01', - }, - 'LTC/ETH': { - 'symbol': 'LTC/ETH', - }, - 'ETH/USDT:USDT': { - 'symbol': 'ETH/USDT:USDT', - 'contractSize': '10', - } - }) - result_size = exchange._amount_to_contracts(pair, param_amount) - assert result_size == param_amount - result_amount = exchange._contracts_to_amount(pair, param_size) - assert result_amount == param_size - - default_conf['trading_mode'] = 'futures' - exchange = get_patched_exchange(mocker, default_conf, api_mock) - result_size = exchange._amount_to_contracts(pair, param_amount) - assert result_size == param_size - result_amount = exchange._contracts_to_amount(pair, param_size) - assert result_amount == param_amount - - -@pytest.mark.parametrize('exchange_name,open_rate,is_short,trading_mode,margin_mode', [ - # Bittrex - ('bittrex', 2.0, False, 'spot', None), - ('bittrex', 2.0, False, 'spot', 'cross'), - ('bittrex', 2.0, True, 'spot', 'isolated'), - # Binance - ('binance', 2.0, False, 'spot', None), - ('binance', 2.0, False, 'spot', 'cross'), - ('binance', 2.0, True, 'spot', 'isolated'), -]) -def test_liquidation_price_is_none( - mocker, - default_conf, - exchange_name, - open_rate, - is_short, - trading_mode, - margin_mode -): - default_conf['trading_mode'] = trading_mode - default_conf['margin_mode'] = margin_mode - exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) - assert exchange.get_or_calculate_liquidation_price( - pair='DOGE/USDT', - open_rate=open_rate, - is_short=is_short, - position=71200.81144, - wallet_balance=-56354.57, - mm_ex_1=0.10, - upnl_ex_1=0.0 - ) is None - - -@pytest.mark.parametrize( - 'exchange_name, is_short, trading_mode, margin_mode, wallet_balance, ' - 'mm_ex_1, upnl_ex_1, maintenance_amt, position, open_rate, ' - 'mm_ratio, expected', - [ - ("binance", False, 'futures', 'isolated', 1535443.01, 0.0, - 0.0, 135365.00, 3683.979, 1456.84, 0.10, 1114.78), - ("binance", False, 'futures', 'isolated', 1535443.01, 0.0, - 0.0, 16300.000, 109.488, 32481.980, 0.025, 18778.73), - ("binance", False, 'futures', 'cross', 1535443.01, 71200.81144, - -56354.57, 135365.00, 3683.979, 1456.84, 0.10, 1153.26), - ("binance", False, 'futures', 'cross', 1535443.01, 356512.508, - -448192.89, 16300.000, 109.488, 32481.980, 0.025, 26316.89) - ]) -def test_liquidation_price( - mocker, default_conf, exchange_name, open_rate, is_short, trading_mode, - margin_mode, wallet_balance, mm_ex_1, upnl_ex_1, maintenance_amt, position, mm_ratio, expected -): - default_conf['trading_mode'] = trading_mode - default_conf['margin_mode'] = margin_mode - default_conf['liquidation_buffer'] = 0.0 - exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) - exchange.get_maintenance_ratio_and_amt = MagicMock(return_value=(mm_ratio, maintenance_amt)) - assert isclose(round(exchange.get_or_calculate_liquidation_price( - pair='DOGE/USDT', - open_rate=open_rate, - is_short=is_short, - wallet_balance=wallet_balance, - mm_ex_1=mm_ex_1, - upnl_ex_1=upnl_ex_1, - position=position, - ), 2), expected) - - -def test_get_max_pair_stake_amount( - mocker, - default_conf, -): - api_mock = MagicMock() - default_conf['margin_mode'] = 'isolated' - default_conf['trading_mode'] = 'futures' - exchange = get_patched_exchange(mocker, default_conf, api_mock) - markets = { - 'XRP/USDT:USDT': { - 'limits': { - 'amount': { - 'min': 0.001, - 'max': 10000 - }, - 'cost': { - 'min': 5, - 'max': None - }, - }, - 'contractSize': None, - 'spot': False, - }, - 'LTC/USDT:USDT': { - 'limits': { - 'amount': { - 'min': 0.001, - 'max': None - }, - 'cost': { - 'min': 5, - 'max': None - }, - }, - 'contractSize': 0.01, - 'spot': False, - }, - 'ETH/USDT:USDT': { - 'limits': { - 'amount': { - 'min': 0.001, - 'max': 10000 - }, - 'cost': { - 'min': 5, - 'max': 30000, - }, - }, - 'contractSize': 0.01, - 'spot': False, - }, - 'BTC/USDT': { - 'limits': { - 'amount': { - 'min': 0.001, - 'max': 10000 - }, - 'cost': { - 'min': 5, - 'max': None - }, - }, - 'contractSize': 0.01, - 'spot': True, - }, - 'ADA/USDT': { - 'limits': { - 'amount': { - 'min': 0.001, - 'max': 10000 - }, - 'cost': { - 'min': 5, - 'max': 500, - }, - }, - 'contractSize': 0.01, - 'spot': True, - }, - 'DOGE/USDT:USDT': { - 'limits': { - 'amount': { - 'min': 0.001, - 'max': 10000 - }, - 'cost': { - 'min': 5, - 'max': 500 - }, - }, - 'contractSize': None, - 'spot': False, - }, - 'LUNA/USDT:USDT': { - 'limits': { - 'amount': { - 'min': 0.001, - 'max': 10000 - }, - 'cost': { - 'min': 5, - 'max': 500 - }, - }, - 'contractSize': 0.01, - 'spot': False, - }, - } - - mocker.patch('freqtrade.exchange.Exchange.markets', markets) - assert exchange.get_max_pair_stake_amount('XRP/USDT:USDT', 2.0) == 20000 - assert exchange.get_max_pair_stake_amount('XRP/USDT:USDT', 2.0, 5) == 4000 - assert exchange.get_max_pair_stake_amount('LTC/USDT:USDT', 2.0) == float('inf') - assert exchange.get_max_pair_stake_amount('ETH/USDT:USDT', 2.0) == 200 - assert exchange.get_max_pair_stake_amount('DOGE/USDT:USDT', 2.0) == 500 - assert exchange.get_max_pair_stake_amount('LUNA/USDT:USDT', 2.0) == 5.0 - - default_conf['trading_mode'] = 'spot' - exchange = get_patched_exchange(mocker, default_conf, api_mock) - mocker.patch('freqtrade.exchange.Exchange.markets', markets) - assert exchange.get_max_pair_stake_amount('BTC/USDT', 2.0) == 20000 - assert exchange.get_max_pair_stake_amount('ADA/USDT', 2.0) == 500 - - -@pytest.mark.parametrize('exchange_name', EXCHANGES) -def test_load_leverage_tiers(mocker, default_conf, leverage_tiers, exchange_name): - api_mock = MagicMock() - api_mock.fetch_leverage_tiers = MagicMock() - type(api_mock).has = PropertyMock(return_value={'fetchLeverageTiers': True}) - default_conf['dry_run'] = False - mocker.patch('freqtrade.exchange.exchange.Exchange.validate_trading_mode_and_margin_mode') - - api_mock.fetch_leverage_tiers = MagicMock(return_value={ - 'ADA/USDT:USDT': [ - { - 'tier': 1, - 'notionalFloor': 0, - 'notionalCap': 500, - 'maintenanceMarginRate': 0.02, - 'maxLeverage': 75, - 'info': { - 'baseMaxLoan': '', - 'imr': '0.013', - 'instId': '', - 'maxLever': '75', - 'maxSz': '500', - 'minSz': '0', - 'mmr': '0.01', - 'optMgnFactor': '0', - 'quoteMaxLoan': '', - 'tier': '1', - 'uly': 'ADA-USDT' - } - }, - ] - }) - - # SPOT - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - assert exchange.load_leverage_tiers() == {} - - default_conf['trading_mode'] = 'futures' - default_conf['margin_mode'] = 'isolated' - - if exchange_name != 'binance': - # FUTURES has.fetchLeverageTiers == False - type(api_mock).has = PropertyMock(return_value={'fetchLeverageTiers': False}) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - assert exchange.load_leverage_tiers() == {} - - # FUTURES regular - type(api_mock).has = PropertyMock(return_value={'fetchLeverageTiers': True}) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - assert exchange.load_leverage_tiers() == { - 'ADA/USDT:USDT': [ - { - 'tier': 1, - 'notionalFloor': 0, - 'notionalCap': 500, - 'maintenanceMarginRate': 0.02, - 'maxLeverage': 75, - 'info': { - 'baseMaxLoan': '', - 'imr': '0.013', - 'instId': '', - 'maxLever': '75', - 'maxSz': '500', - 'minSz': '0', - 'mmr': '0.01', - 'optMgnFactor': '0', - 'quoteMaxLoan': '', - 'tier': '1', - 'uly': 'ADA-USDT' - } - }, - ] - } - - ccxt_exceptionhandlers( - mocker, - default_conf, - api_mock, - exchange_name, - "load_leverage_tiers", - "fetch_leverage_tiers", - ) - - -def test_parse_leverage_tier(mocker, default_conf): - exchange = get_patched_exchange(mocker, default_conf) - - tier = { - "tier": 1, - "notionalFloor": 0, - "notionalCap": 100000, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20, - "info": { - "bracket": "1", - "initialLeverage": "20", - "notionalCap": "100000", - "notionalFloor": "0", - "maintMarginRatio": "0.025", - "cum": "0.0" - } - } - - assert exchange.parse_leverage_tier(tier) == { - "min": 0, - "max": 100000, - "mmr": 0.025, - "lev": 20, - "maintAmt": 0.0, - } - - tier2 = { - 'tier': 1, - 'notionalFloor': 0, - 'notionalCap': 2000, - 'maintenanceMarginRate': 0.01, - 'maxLeverage': 75, - 'info': { - 'baseMaxLoan': '', - 'imr': '0.013', - 'instId': '', - 'maxLever': '75', - 'maxSz': '2000', - 'minSz': '0', - 'mmr': '0.01', - 'optMgnFactor': '0', - 'quoteMaxLoan': '', - 'tier': '1', - 'uly': 'SHIB-USDT' - } - } - - assert exchange.parse_leverage_tier(tier2) == { - 'min': 0, - 'max': 2000, - 'mmr': 0.01, - 'lev': 75, - "maintAmt": None, - } - - -def test_get_maintenance_ratio_and_amt_exceptions(mocker, default_conf, leverage_tiers): - api_mock = MagicMock() - default_conf['trading_mode'] = 'futures' - default_conf['margin_mode'] = 'isolated' - mocker.patch('freqtrade.exchange.Exchange.exchange_has', return_value=True) - exchange = get_patched_exchange(mocker, default_conf, api_mock) - - exchange._leverage_tiers = leverage_tiers - with pytest.raises( - OperationalException, - match='nominal value can not be lower than 0', - ): - exchange.get_maintenance_ratio_and_amt('1000SHIB/USDT', -1) - - exchange._leverage_tiers = {} - - with pytest.raises( - InvalidOrderException, - match="Maintenance margin rate for 1000SHIB/USDT is unavailable for", - ): - exchange.get_maintenance_ratio_and_amt('1000SHIB/USDT', 10000) - - -@pytest.mark.parametrize('pair,value,mmr,maintAmt', [ - ('ADA/BUSD', 500, 0.025, 0.0), - ('ADA/BUSD', 20000000, 0.5, 1527500.0), - ('ZEC/USDT', 500, 0.01, 0.0), - ('ZEC/USDT', 20000000, 0.5, 654500.0), -]) -def test_get_maintenance_ratio_and_amt( - mocker, - default_conf, - leverage_tiers, - pair, - value, - mmr, - maintAmt -): - api_mock = MagicMock() - default_conf['trading_mode'] = 'futures' - default_conf['margin_mode'] = 'isolated' - mocker.patch('freqtrade.exchange.Exchange.exchange_has', return_value=True) - exchange = get_patched_exchange(mocker, default_conf, api_mock) - exchange._leverage_tiers = leverage_tiers - exchange.get_maintenance_ratio_and_amt(pair, value) == (mmr, maintAmt) - - -def test_get_max_leverage_futures(default_conf, mocker, leverage_tiers): - - # Test Spot - exchange = get_patched_exchange(mocker, default_conf, id="binance") - assert exchange.get_max_leverage("BNB/USDT", 100.0) == 1.0 - - # Test Futures - default_conf['trading_mode'] = 'futures' - default_conf['margin_mode'] = 'isolated' - exchange = get_patched_exchange(mocker, default_conf, id="binance") - - exchange._leverage_tiers = leverage_tiers - - assert exchange.get_max_leverage("BNB/BUSD", 1.0) == 20.0 - assert exchange.get_max_leverage("BNB/USDT", 100.0) == 75.0 - assert exchange.get_max_leverage("BTC/USDT", 170.30) == 125.0 - assert isclose(exchange.get_max_leverage("BNB/BUSD", 99999.9), 5.000005) - assert isclose(exchange.get_max_leverage("BNB/USDT", 1500), 33.333333333333333) - assert exchange.get_max_leverage("BTC/USDT", 300000000) == 2.0 - assert exchange.get_max_leverage("BTC/USDT", 600000000) == 1.0 # Last tier - - assert exchange.get_max_leverage("SPONGE/USDT", 200) == 1.0 # Pair not in leverage_tiers - assert exchange.get_max_leverage("BTC/USDT", 0.0) == 125.0 # No stake amount - with pytest.raises( - InvalidOrderException, - match=r'Amount 1000000000.01 too high for BTC/USDT' - ): - exchange.get_max_leverage("BTC/USDT", 1000000000.01) - - -@pytest.mark.parametrize("exchange_name", ['bittrex', 'binance', 'kraken', 'ftx', 'gateio', 'okx']) -def test__get_params(mocker, default_conf, exchange_name): - api_mock = MagicMock() - mocker.patch('freqtrade.exchange.Exchange.exchange_has', return_value=True) - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange._params = {'test': True} - - params1 = {'test': True} - params2 = { - 'test': True, - 'timeInForce': 'ioc', - 'reduceOnly': True, - } - - if exchange_name == 'kraken': - params2['leverage'] = 3.0 - - if exchange_name == 'okx': - params2['tdMode'] = 'isolated' - - assert exchange._get_params( - ordertype='market', - reduceOnly=False, - time_in_force='gtc', - leverage=1.0, - ) == params1 - - assert exchange._get_params( - ordertype='market', - reduceOnly=False, - time_in_force='ioc', - leverage=1.0, - ) == params1 - - assert exchange._get_params( - ordertype='limit', - reduceOnly=False, - time_in_force='gtc', - leverage=1.0, - ) == params1 - - default_conf['trading_mode'] = 'futures' - default_conf['margin_mode'] = 'isolated' - exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) - exchange._params = {'test': True} - - assert exchange._get_params( - ordertype='limit', - reduceOnly=True, - time_in_force='ioc', - leverage=3.0, - ) == params2 - - -@pytest.mark.parametrize('liquidation_buffer', [0.0, 0.05]) -@pytest.mark.parametrize( - "is_short,trading_mode,exchange_name,margin_mode,leverage,open_rate,amount,expected_liq", [ - (False, 'spot', 'binance', '', 5.0, 10.0, 1.0, None), - (True, 'spot', 'binance', '', 5.0, 10.0, 1.0, None), - (False, 'spot', 'gateio', '', 5.0, 10.0, 1.0, None), - (True, 'spot', 'gateio', '', 5.0, 10.0, 1.0, None), - (False, 'spot', 'okx', '', 5.0, 10.0, 1.0, None), - (True, 'spot', 'okx', '', 5.0, 10.0, 1.0, None), - # Binance, short - (True, 'futures', 'binance', 'isolated', 5.0, 10.0, 1.0, 11.89108910891089), - (True, 'futures', 'binance', 'isolated', 3.0, 10.0, 1.0, 13.211221122079207), - (True, 'futures', 'binance', 'isolated', 5.0, 8.0, 1.0, 9.514851485148514), - (True, 'futures', 'binance', 'isolated', 5.0, 10.0, 0.6, 12.557755775577558), - # Binance, long - (False, 'futures', 'binance', 'isolated', 5, 10, 1.0, 8.070707070707071), - (False, 'futures', 'binance', 'isolated', 5, 8, 1.0, 6.454545454545454), - (False, 'futures', 'binance', 'isolated', 3, 10, 1.0, 6.717171717171718), - (False, 'futures', 'binance', 'isolated', 5, 10, 0.6, 7.39057239057239), - # Gateio/okx, short - (True, 'futures', 'gateio', 'isolated', 5, 10, 1.0, 11.87413417771621), - (True, 'futures', 'gateio', 'isolated', 5, 10, 2.0, 11.87413417771621), - (True, 'futures', 'gateio', 'isolated', 3, 10, 1.0, 13.476180850346978), - (True, 'futures', 'gateio', 'isolated', 5, 8, 1.0, 9.499307342172967), - # Gateio/okx, long - (False, 'futures', 'gateio', 'isolated', 5.0, 10.0, 1.0, 8.085708510208207), - (False, 'futures', 'gateio', 'isolated', 3.0, 10.0, 1.0, 6.738090425173506), - # (True, 'futures', 'okx', 'isolated', 11.87413417771621), - # (False, 'futures', 'okx', 'isolated', 8.085708510208207), - ] -) -def test_get_liquidation_price( - mocker, - default_conf_usdt, - is_short, - trading_mode, - exchange_name, - margin_mode, - leverage, - open_rate, - amount, - expected_liq, - liquidation_buffer, -): - """ - position = 0.2 * 5 - wb: wallet balance (stake_amount if isolated) - cum_b: maintenance amount - side_1: -1 if is_short else 1 - ep1: entry price - mmr_b: maintenance margin ratio - - Binance, Short - leverage = 5, open_rate = 10, amount = 1.0 - ((wb + cum_b) - (side_1 * position * ep1)) / ((position * mmr_b) - (side_1 * position)) - ((2 + 0.01) - ((-1) * 1 * 10)) / ((1 * 0.01) - ((-1) * 1)) = 11.89108910891089 - leverage = 3, open_rate = 10, amount = 1.0 - ((3.3333333333 + 0.01) - ((-1) * 1.0 * 10)) / ((1.0 * 0.01) - ((-1) * 1.0)) = 13.2112211220 - leverage = 5, open_rate = 8, amount = 1.0 - ((1.6 + 0.01) - ((-1) * 1 * 8)) / ((1 * 0.01) - ((-1) * 1)) = 9.514851485148514 - leverage = 5, open_rate = 10, amount = 0.6 - ((1.6 + 0.01) - ((-1) * 0.6 * 10)) / ((0.6 * 0.01) - ((-1) * 0.6)) = 12.557755775577558 - - Binance, Long - leverage = 5, open_rate = 10, amount = 1.0 - ((wb + cum_b) - (side_1 * position * ep1)) / ((position * mmr_b) - (side_1 * position)) - ((2 + 0.01) - (1 * 1 * 10)) / ((1 * 0.01) - (1 * 1)) = 8.070707070707071 - leverage = 5, open_rate = 8, amount = 1.0 - ((1.6 + 0.01) - (1 * 1 * 8)) / ((1 * 0.01) - (1 * 1)) = 6.454545454545454 - leverage = 3, open_rate = 10, amount = 1.0 - ((2 + 0.01) - (1 * 0.6 * 10)) / ((0.6 * 0.01) - (1 * 0.6)) = 6.717171717171718 - leverage = 5, open_rate = 10, amount = 0.6 - ((1.6 + 0.01) - (1 * 0.6 * 10)) / ((0.6 * 0.01) - (1 * 0.6)) = 7.39057239057239 - - Gateio/Okx, Short - leverage = 5, open_rate = 10, amount = 1.0 - (open_rate + (wallet_balance / position)) / (1 + (mm_ratio + taker_fee_rate)) - (10 + (2 / 1.0)) / (1 + (0.01 + 0.0006)) = 11.87413417771621 - leverage = 5, open_rate = 10, amount = 2.0 - (10 + (4 / 2.0)) / (1 + (0.01 + 0.0006)) = 11.87413417771621 - leverage = 3, open_rate = 10, amount = 1.0 - (10 + (3.3333333333333 / 1.0)) / (1 - (0.01 + 0.0006)) = 13.476180850346978 - leverage = 5, open_rate = 8, amount = 1.0 - (8 + (1.6 / 1.0)) / (1 + (0.01 + 0.0006)) = 9.499307342172967 - - Gateio/Okx, Long - leverage = 5, open_rate = 10, amount = 1.0 - (open_rate - (wallet_balance / position)) / (1 - (mm_ratio + taker_fee_rate)) - (10 - (2 / 1)) / (1 - (0.01 + 0.0006)) = 8.085708510208207 - leverage = 5, open_rate = 10, amount = 2.0 - (10 - (4 / 2.0)) / (1 + (0.01 + 0.0006)) = 7.916089451810806 - leverage = 3, open_rate = 10, amount = 1.0 - (10 - (3.333333333333333333 / 1.0)) / (1 - (0.01 + 0.0006)) = 6.738090425173506 - leverage = 5, open_rate = 8, amount = 1.0 - (8 - (1.6 / 1.0)) / (1 + (0.01 + 0.0006)) = 6.332871561448645 - """ - default_conf_usdt['liquidation_buffer'] = liquidation_buffer - default_conf_usdt['trading_mode'] = trading_mode - default_conf_usdt['exchange']['name'] = exchange_name - default_conf_usdt['margin_mode'] = margin_mode - mocker.patch('freqtrade.exchange.Gateio.validate_ordertypes') - exchange = get_patched_exchange(mocker, default_conf_usdt) - - exchange.get_maintenance_ratio_and_amt = MagicMock(return_value=(0.01, 0.01)) - exchange.name = exchange_name - # default_conf_usdt.update({ - # "dry_run": False, - # }) - liq = exchange.get_liquidation_price( - pair='ETH/USDT:USDT', - open_rate=open_rate, - amount=amount, - leverage=leverage, - is_short=is_short, - ) - if expected_liq is None: - assert liq is None - else: - buffer_amount = liquidation_buffer * abs(open_rate - expected_liq) - expected_liq = expected_liq - buffer_amount if is_short else expected_liq + buffer_amount - isclose(expected_liq, liq) - - -@pytest.mark.parametrize('contract_size,order_amount', [ - (10, 10), - (0.01, 10000), -]) -def test_stoploss_contract_size(mocker, default_conf, contract_size, order_amount): - api_mock = MagicMock() - order_id = 'test_prod_buy_{}'.format(randint(0, 10 ** 6)) - - api_mock.create_order = MagicMock(return_value={ - 'id': order_id, - 'info': { - 'foo': 'bar' - }, - 'amount': order_amount, - 'cost': order_amount, - 'filled': order_amount, - 'remaining': order_amount, - 'symbol': 'ETH/BTC', - }) - default_conf['dry_run'] = False - mocker.patch('freqtrade.exchange.Exchange.amount_to_precision', lambda s, x, y: y) - mocker.patch('freqtrade.exchange.Exchange.price_to_precision', lambda s, x, y: y) - - exchange = get_patched_exchange(mocker, default_conf, api_mock) - exchange._get_contract_size = MagicMock(return_value=contract_size) - - api_mock.create_order.reset_mock() - order = exchange.stoploss( - pair='ETH/BTC', - amount=100, - stop_price=220, - order_types={}, - side='buy', - leverage=1.0 - ) - - assert api_mock.create_order.call_args_list[0][1]['amount'] == order_amount - assert order['amount'] == 100 - assert order['cost'] == 100 - assert order['filled'] == 100 - assert order['remaining'] == 100 diff --git a/tests/optimize/test_backtesting.py.orig b/tests/optimize/test_backtesting.py.orig deleted file mode 100644 index c7b3dd691..000000000 --- a/tests/optimize/test_backtesting.py.orig +++ /dev/null @@ -1,1684 +0,0 @@ -# pragma pylint: disable=missing-docstring, W0212, line-too-long, C0103, unused-argument - -import random -from copy import deepcopy -from datetime import datetime, timedelta, timezone -from pathlib import Path -from unittest.mock import MagicMock, PropertyMock - -import numpy as np -import pandas as pd -import pytest -from arrow import Arrow - -from freqtrade import constants -from freqtrade.commands.optimize_commands import setup_optimize_configuration, start_backtesting -from freqtrade.configuration import TimeRange -from freqtrade.data import history -from freqtrade.data.btanalysis import BT_DATA_COLUMNS, evaluate_result_multi -from freqtrade.data.converter import clean_ohlcv_dataframe -from freqtrade.data.dataprovider import DataProvider -from freqtrade.data.history import get_timerange -from freqtrade.enums import ExitType, RunMode -from freqtrade.exceptions import DependencyException, OperationalException -from freqtrade.exchange.exchange import timeframe_to_next_date -from freqtrade.misc import get_strategy_run_id -from freqtrade.optimize.backtesting import Backtesting -from freqtrade.persistence import LocalTrade -from freqtrade.resolvers import StrategyResolver -from tests.conftest import (CURRENT_TEST_STRATEGY, get_args, log_has, log_has_re, patch_exchange, - patched_configuration_load_config_file) - - -ORDER_TYPES = [ - { - 'entry': 'limit', - 'exit': 'limit', - 'stoploss': 'limit', - 'stoploss_on_exchange': False - }, - { - 'entry': 'limit', - 'exit': 'limit', - 'stoploss': 'limit', - 'stoploss_on_exchange': True - }] - - -def trim_dictlist(dict_list, num): - new = {} - for pair, pair_data in dict_list.items(): - new[pair] = pair_data[num:].reset_index() - return new - - -@pytest.fixture(autouse=True) -def backtesting_cleanup() -> None: - yield None - - Backtesting.cleanup() - - -def load_data_test(what, testdatadir): - timerange = TimeRange.parse_timerange('1510694220-1510700340') - data = history.load_pair_history(pair='UNITTEST/BTC', datadir=testdatadir, - timeframe='1m', timerange=timerange, - drop_incomplete=False, - fill_up_missing=False) - - base = 0.001 - if what == 'raise': - data.loc[:, 'open'] = data.index * base - data.loc[:, 'high'] = data.index * base + 0.0001 - data.loc[:, 'low'] = data.index * base - 0.0001 - data.loc[:, 'close'] = data.index * base - - if what == 'lower': - data.loc[:, 'open'] = 1 - data.index * base - data.loc[:, 'high'] = 1 - data.index * base + 0.0001 - data.loc[:, 'low'] = 1 - data.index * base - 0.0001 - data.loc[:, 'close'] = 1 - data.index * base - - if what == 'sine': - hz = 0.1 # frequency - data.loc[:, 'open'] = np.sin(data.index * hz) / 1000 + base - data.loc[:, 'high'] = np.sin(data.index * hz) / 1000 + base + 0.0001 - data.loc[:, 'low'] = np.sin(data.index * hz) / 1000 + base - 0.0001 - data.loc[:, 'close'] = np.sin(data.index * hz) / 1000 + base - - return {'UNITTEST/BTC': clean_ohlcv_dataframe(data, timeframe='1m', pair='UNITTEST/BTC', - fill_missing=True)} - - -def simple_backtest(config, contour, mocker, testdatadir) -> None: - patch_exchange(mocker) - config['timeframe'] = '1m' - backtesting = Backtesting(config) - backtesting._set_strategy(backtesting.strategylist[0]) - - data = load_data_test(contour, testdatadir) - processed = backtesting.strategy.advise_all_indicators(data) - min_date, max_date = get_timerange(processed) - assert isinstance(processed, dict) - results = backtesting.backtest( - processed=processed, - start_date=min_date, - end_date=max_date, - max_open_trades=1, - position_stacking=False, - enable_protections=config.get('enable_protections', False), - ) - # results :: - return results - - -# FIX: fixturize this? -def _make_backtest_conf(mocker, datadir, conf=None, pair='UNITTEST/BTC'): - data = history.load_data(datadir=datadir, timeframe='1m', pairs=[pair]) - data = trim_dictlist(data, -201) - patch_exchange(mocker) - backtesting = Backtesting(conf) - backtesting._set_strategy(backtesting.strategylist[0]) - processed = backtesting.strategy.advise_all_indicators(data) - min_date, max_date = get_timerange(processed) - return { - 'processed': processed, - 'start_date': min_date, - 'end_date': max_date, - 'max_open_trades': 10, - 'position_stacking': False, - } - - -def _trend(signals, buy_value, sell_value): - n = len(signals['low']) - buy = np.zeros(n) - sell = np.zeros(n) - for i in range(0, len(signals['date'])): - if random.random() > 0.5: # Both buy and sell signals at same timeframe - buy[i] = buy_value - sell[i] = sell_value - signals['enter_long'] = buy - signals['exit_long'] = sell - signals['enter_short'] = 0 - signals['exit_short'] = 0 - return signals - - -def _trend_alternate(dataframe=None, metadata=None): - signals = dataframe - low = signals['low'] - n = len(low) - buy = np.zeros(n) - sell = np.zeros(n) - for i in range(0, len(buy)): - if i % 2 == 0: - buy[i] = 1 - else: - sell[i] = 1 - signals['enter_long'] = buy - signals['exit_long'] = sell - signals['enter_short'] = 0 - signals['exit_short'] = 0 - return dataframe - - -# Unit tests -def test_setup_optimize_configuration_without_arguments(mocker, default_conf, caplog) -> None: - patched_configuration_load_config_file(mocker, default_conf) - - args = [ - 'backtesting', - '--config', 'config.json', - '--strategy', CURRENT_TEST_STRATEGY, - '--export', 'none' - ] - - config = setup_optimize_configuration(get_args(args), RunMode.BACKTEST) - assert 'max_open_trades' in config - assert 'stake_currency' in config - assert 'stake_amount' in config - assert 'exchange' in config - assert 'pair_whitelist' in config['exchange'] - assert 'datadir' in config - assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog) - assert 'timeframe' in config - assert not log_has_re('Parameter -i/--ticker-interval detected .*', caplog) - - assert 'position_stacking' not in config - assert not log_has('Parameter --enable-position-stacking detected ...', caplog) - - assert 'timerange' not in config - assert 'export' in config - assert config['export'] == 'none' - assert 'runmode' in config - assert config['runmode'] == RunMode.BACKTEST - - -def test_setup_bt_configuration_with_arguments(mocker, default_conf, caplog) -> None: - patched_configuration_load_config_file(mocker, default_conf) - mocker.patch( - 'freqtrade.configuration.configuration.create_datadir', - lambda c, x: x - ) - - args = [ - 'backtesting', - '--config', 'config.json', - '--strategy', CURRENT_TEST_STRATEGY, - '--datadir', '/foo/bar', - '--timeframe', '1m', - '--enable-position-stacking', - '--disable-max-market-positions', - '--timerange', ':100', - '--export-filename', 'foo_bar.json', - '--fee', '0', - ] - - config = setup_optimize_configuration(get_args(args), RunMode.BACKTEST) - assert 'max_open_trades' in config - assert 'stake_currency' in config - assert 'stake_amount' in config - assert 'exchange' in config - assert 'pair_whitelist' in config['exchange'] - assert 'datadir' in config - assert config['runmode'] == RunMode.BACKTEST - - assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog) - assert 'timeframe' in config - assert log_has('Parameter -i/--timeframe detected ... Using timeframe: 1m ...', - caplog) - - assert 'position_stacking' in config - assert log_has('Parameter --enable-position-stacking detected ...', caplog) - - assert 'use_max_market_positions' in config - assert log_has('Parameter --disable-max-market-positions detected ...', caplog) - assert log_has('max_open_trades set to unlimited ...', caplog) - - assert 'timerange' in config - assert log_has('Parameter --timerange detected: {} ...'.format(config['timerange']), caplog) - - assert 'export' in config - assert 'exportfilename' in config - assert isinstance(config['exportfilename'], Path) - assert log_has('Storing backtest results to {} ...'.format(config['exportfilename']), caplog) - - assert 'fee' in config - assert log_has('Parameter --fee detected, setting fee to: {} ...'.format(config['fee']), caplog) - - -def test_setup_optimize_configuration_stake_amount(mocker, default_conf, caplog) -> None: - - patched_configuration_load_config_file(mocker, default_conf) - - args = [ - 'backtesting', - '--config', 'config.json', - '--strategy', CURRENT_TEST_STRATEGY, - '--stake-amount', '1', - '--starting-balance', '2' - ] - - conf = setup_optimize_configuration(get_args(args), RunMode.BACKTEST) - assert isinstance(conf, dict) - - args = [ - 'backtesting', - '--config', 'config.json', - '--strategy', CURRENT_TEST_STRATEGY, - '--stake-amount', '1', - '--starting-balance', '0.5' - ] - with pytest.raises(OperationalException, match=r"Starting balance .* smaller .*"): - setup_optimize_configuration(get_args(args), RunMode.BACKTEST) - - -def test_start(mocker, fee, default_conf, caplog) -> None: - start_mock = MagicMock() - mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) - patch_exchange(mocker) - mocker.patch('freqtrade.optimize.backtesting.Backtesting.start', start_mock) - patched_configuration_load_config_file(mocker, default_conf) - - args = [ - 'backtesting', - '--config', 'config.json', - '--strategy', CURRENT_TEST_STRATEGY, - ] - pargs = get_args(args) - start_backtesting(pargs) - assert log_has('Starting freqtrade in Backtesting mode', caplog) - assert start_mock.call_count == 1 - - -@pytest.mark.parametrize("order_types", ORDER_TYPES) -def test_backtesting_init(mocker, default_conf, order_types) -> None: - """ - Check that stoploss_on_exchange is set to False while backtesting - since backtesting assumes a perfect stoploss anyway. - """ - default_conf["order_types"] = order_types - patch_exchange(mocker) - get_fee = mocker.patch('freqtrade.exchange.Exchange.get_fee', MagicMock(return_value=0.5)) - backtesting = Backtesting(default_conf) - backtesting._set_strategy(backtesting.strategylist[0]) - assert backtesting.config == default_conf - assert backtesting.timeframe == '5m' - assert callable(backtesting.strategy.advise_all_indicators) - assert callable(backtesting.strategy.advise_entry) - assert callable(backtesting.strategy.advise_exit) - assert isinstance(backtesting.strategy.dp, DataProvider) - get_fee.assert_called() - assert backtesting.fee == 0.5 - assert not backtesting.strategy.order_types["stoploss_on_exchange"] - - -def test_backtesting_init_no_timeframe(mocker, default_conf, caplog) -> None: - patch_exchange(mocker) - del default_conf['timeframe'] - default_conf['strategy_list'] = [CURRENT_TEST_STRATEGY, - 'HyperoptableStrategy'] - - mocker.patch('freqtrade.exchange.Exchange.get_fee', MagicMock(return_value=0.5)) - with pytest.raises(OperationalException, - match=r"Timeframe needs to be set in either configuration"): - Backtesting(default_conf) - - -def test_data_with_fee(default_conf, mocker) -> None: - patch_exchange(mocker) - default_conf['fee'] = 0.1234 - - fee_mock = mocker.patch('freqtrade.exchange.Exchange.get_fee', MagicMock(return_value=0.5)) - backtesting = Backtesting(default_conf) - backtesting._set_strategy(backtesting.strategylist[0]) - assert backtesting.fee == 0.1234 - assert fee_mock.call_count == 0 - - default_conf['fee'] = 0.0 - backtesting = Backtesting(default_conf) - backtesting._set_strategy(backtesting.strategylist[0]) - assert backtesting.fee == 0.0 - assert fee_mock.call_count == 0 - - -def test_data_to_dataframe_bt(default_conf, mocker, testdatadir) -> None: - patch_exchange(mocker) - timerange = TimeRange.parse_timerange('1510694220-1510700340') - data = history.load_data(testdatadir, '1m', ['UNITTEST/BTC'], timerange=timerange, - fill_up_missing=True) - backtesting = Backtesting(default_conf) - backtesting._set_strategy(backtesting.strategylist[0]) - processed = backtesting.strategy.advise_all_indicators(data) - assert len(processed['UNITTEST/BTC']) == 102 - - # Load strategy to compare the result between Backtesting function and strategy are the same - strategy = StrategyResolver.load_strategy(default_conf) - - processed2 = strategy.advise_all_indicators(data) - assert processed['UNITTEST/BTC'].equals(processed2['UNITTEST/BTC']) - - -def test_backtest_abort(default_conf, mocker, testdatadir) -> None: - patch_exchange(mocker) - backtesting = Backtesting(default_conf) - backtesting.check_abort() - - backtesting.abort = True - - with pytest.raises(DependencyException, match="Stop requested"): - backtesting.check_abort() - # abort flag resets - assert backtesting.abort is False - assert backtesting.progress.progress == 0 - - -def test_backtesting_start(default_conf, mocker, testdatadir, caplog) -> None: - def get_timerange(input1): - return Arrow(2017, 11, 14, 21, 17), Arrow(2017, 11, 14, 22, 59) - - mocker.patch('freqtrade.data.history.get_timerange', get_timerange) - patch_exchange(mocker) - mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest') - mocker.patch('freqtrade.optimize.backtesting.generate_backtest_stats') - mocker.patch('freqtrade.optimize.backtesting.show_backtest_results') - sbs = mocker.patch('freqtrade.optimize.backtesting.store_backtest_stats') - mocker.patch('freqtrade.plugins.pairlistmanager.PairListManager.whitelist', - PropertyMock(return_value=['UNITTEST/BTC'])) - - default_conf['timeframe'] = '1m' - default_conf['datadir'] = testdatadir - default_conf['export'] = 'trades' - default_conf['exportfilename'] = 'export.txt' - default_conf['timerange'] = '-1510694220' - - backtesting = Backtesting(default_conf) - backtesting._set_strategy(backtesting.strategylist[0]) - backtesting.strategy.bot_loop_start = MagicMock() - backtesting.start() - # check the logs, that will contain the backtest result - exists = [ - 'Backtesting with data from 2017-11-14 21:17:00 ' - 'up to 2017-11-14 22:59:00 (0 days).' - ] - for line in exists: - assert log_has(line, caplog) - assert backtesting.strategy.dp._pairlists is not None - assert backtesting.strategy.bot_loop_start.call_count == 1 - assert sbs.call_count == 1 - - -def test_backtesting_start_no_data(default_conf, mocker, caplog, testdatadir) -> None: - def get_timerange(input1): - return Arrow(2017, 11, 14, 21, 17), Arrow(2017, 11, 14, 22, 59) - - mocker.patch('freqtrade.data.history.history_utils.load_pair_history', - MagicMock(return_value=pd.DataFrame())) - mocker.patch('freqtrade.data.history.get_timerange', get_timerange) - patch_exchange(mocker) - mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest') - mocker.patch('freqtrade.plugins.pairlistmanager.PairListManager.whitelist', - PropertyMock(return_value=['UNITTEST/BTC'])) - - default_conf['timeframe'] = "1m" - default_conf['datadir'] = testdatadir - default_conf['export'] = 'none' - default_conf['timerange'] = '20180101-20180102' - - backtesting = Backtesting(default_conf) - backtesting._set_strategy(backtesting.strategylist[0]) - with pytest.raises(OperationalException, match='No data found. Terminating.'): - backtesting.start() - - -def test_backtesting_no_pair_left(default_conf, mocker, caplog, testdatadir) -> None: - mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True)) - mocker.patch('freqtrade.data.history.history_utils.load_pair_history', - MagicMock(return_value=pd.DataFrame())) - mocker.patch('freqtrade.data.history.get_timerange', get_timerange) - patch_exchange(mocker) - mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest') - mocker.patch('freqtrade.plugins.pairlistmanager.PairListManager.whitelist', - PropertyMock(return_value=[])) - - default_conf['timeframe'] = "1m" - default_conf['datadir'] = testdatadir - default_conf['export'] = 'none' - default_conf['timerange'] = '20180101-20180102' - - with pytest.raises(OperationalException, match='No pair in whitelist.'): - Backtesting(default_conf) - - default_conf['pairlists'] = [{"method": "VolumePairList", "number_assets": 5}] - with pytest.raises(OperationalException, - match=r'VolumePairList not allowed for backtesting\..*StaticPairlist.*'): - Backtesting(default_conf) - - default_conf.update({ - 'pairlists': [{"method": "StaticPairList"}], - 'timeframe_detail': '1d', - }) - - with pytest.raises(OperationalException, - match='Detail timeframe must be smaller than strategy timeframe.'): - Backtesting(default_conf) - - -def test_backtesting_pairlist_list(default_conf, mocker, caplog, testdatadir, tickers) -> None: - mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True)) - mocker.patch('freqtrade.exchange.Exchange.get_tickers', tickers) - mocker.patch('freqtrade.exchange.Exchange.price_to_precision', lambda s, x, y: y) - mocker.patch('freqtrade.data.history.get_timerange', get_timerange) - patch_exchange(mocker) - mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest') - mocker.patch('freqtrade.plugins.pairlistmanager.PairListManager.whitelist', - PropertyMock(return_value=['XRP/BTC'])) - mocker.patch('freqtrade.plugins.pairlistmanager.PairListManager.refresh_pairlist') - - default_conf['ticker_interval'] = "1m" - default_conf['datadir'] = testdatadir - default_conf['export'] = 'none' - # Use stoploss from strategy - del default_conf['stoploss'] - default_conf['timerange'] = '20180101-20180102' - - default_conf['pairlists'] = [{"method": "VolumePairList", "number_assets": 5}] - with pytest.raises(OperationalException, - match=r'VolumePairList not allowed for backtesting\..*StaticPairlist.*'): - Backtesting(default_conf) - - default_conf['pairlists'] = [{"method": "StaticPairList"}, {"method": "PerformanceFilter"}] - with pytest.raises(OperationalException, - match='PerformanceFilter not allowed for backtesting.'): - Backtesting(default_conf) - - default_conf['pairlists'] = [{"method": "StaticPairList"}, {"method": "PrecisionFilter"}, ] - Backtesting(default_conf) - - # Multiple strategies - default_conf['strategy_list'] = [CURRENT_TEST_STRATEGY, 'TestStrategyLegacyV1'] - with pytest.raises(OperationalException, - match='PrecisionFilter not allowed for backtesting multiple strategies.'): - Backtesting(default_conf) - - -def test_backtest__enter_trade(default_conf, fee, mocker) -> None: - default_conf['use_sell_signal'] = False - mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) - mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001) - mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf')) - patch_exchange(mocker) - default_conf['stake_amount'] = 'unlimited' - default_conf['max_open_trades'] = 2 - backtesting = Backtesting(default_conf) - backtesting._set_strategy(backtesting.strategylist[0]) - pair = 'UNITTEST/BTC' - row = [ - pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=0), - 1, # Buy - 0.001, # Open - 0.0011, # Close - 0, # Sell - 0.00099, # Low - 0.0012, # High - '', # Buy Signal Name - ] - trade = backtesting._enter_trade(pair, row=row, direction='long') - assert isinstance(trade, LocalTrade) - assert trade.stake_amount == 495 - - # Fake 2 trades, so there's not enough amount for the next trade left. - LocalTrade.trades_open.append(trade) - LocalTrade.trades_open.append(trade) - backtesting.wallets.update() - trade = backtesting._enter_trade(pair, row=row, direction='long') - assert trade is None - LocalTrade.trades_open.pop() - trade = backtesting._enter_trade(pair, row=row, direction='long') - assert trade is not None - - backtesting.strategy.custom_stake_amount = lambda **kwargs: 123.5 - backtesting.wallets.update() - trade = backtesting._enter_trade(pair, row=row, direction='long') - assert trade - assert trade.stake_amount == 123.5 - - # In case of error - use proposed stake - backtesting.strategy.custom_stake_amount = lambda **kwargs: 20 / 0 - trade = backtesting._enter_trade(pair, row=row, direction='long') - assert trade - assert trade.stake_amount == 495 - assert trade.is_short is False - - trade = backtesting._enter_trade(pair, row=row, direction='short') - assert trade - assert trade.stake_amount == 495 - assert trade.is_short is True - - mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=300.0) - trade = backtesting._enter_trade(pair, row=row, direction='long') - assert trade - assert trade.stake_amount == 300.0 - - -def test_backtest__enter_trade_futures(default_conf_usdt, fee, mocker) -> None: - default_conf_usdt['use_sell_signal'] = False - mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) - mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001) - mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf')) - mocker.patch("freqtrade.exchange.Exchange.get_max_leverage", return_value=100) - patch_exchange(mocker) - default_conf_usdt['stake_amount'] = 300 - default_conf_usdt['max_open_trades'] = 2 - default_conf_usdt['trading_mode'] = 'futures' - default_conf_usdt['margin_mode'] = 'isolated' - default_conf_usdt['stake_currency'] = 'USDT' - default_conf_usdt['exchange']['pair_whitelist'] = ['.*'] - backtesting = Backtesting(default_conf_usdt) - backtesting._set_strategy(backtesting.strategylist[0]) - pair = 'UNITTEST/USDT:USDT' - row = [ - pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=0), - 0.001, # Open - 0.0012, # High - 0.00099, # Low - 0.0011, # Close - 1, # enter_long - 0, # exit_long - 1, # enter_short - 0, # exit_hsort - '', # Long Signal Name - '', # Short Signal Name - '', # Exit Signal Name - ] - - backtesting.strategy.leverage = MagicMock(return_value=5.0) - mocker.patch("freqtrade.exchange.Exchange.get_maintenance_ratio_and_amt", - return_value=(0.01, 0.01)) - - # leverage = 5 - # ep1(trade.open_rate) = 0.001 - # position(trade.amount) = 1500000 - # stake_amount = 300 -> wb = 300 / 5 = 60 - # mmr = 0.01 - # cum_b = 0.01 - # side_1: -1 if is_short else 1 - # liq_buffer = 0.05 - # - # Binance, Long - # liquidation_price - # = ((wb + cum_b) - (side_1 * position * ep1)) / ((position * mmr_b) - (side_1 * position)) - # = ((300 + 0.01) - (1 * 1500000 * 0.001)) / ((1500000 * 0.01) - (1 * 1500000)) - # = 0.0008080740740740741 - # freqtrade_liquidation_price = liq + (abs(open_rate - liq) * liq_buffer * side_1) - # = 0.0008080740740740741 + ((0.001 - 0.0008080740740740741) * 0.05 * 1) - # = 0.0008176703703703704 - - trade = backtesting._enter_trade(pair, row=row, direction='long') - assert pytest.approx(trade.liquidation_price) == 0.00081767037 - - # Binance, Short - # liquidation_price - # = ((wb + cum_b) - (side_1 * position * ep1)) / ((position * mmr_b) - (side_1 * position)) - # = ((300 + 0.01) - ((-1) * 1500000 * 0.001)) / ((1500000 * 0.01) - ((-1) * 1500000)) - # = 0.0011881254125412541 - # freqtrade_liquidation_price = liq + (abs(open_rate - liq) * liq_buffer * side_1) - # = 0.0011881254125412541 + (abs(0.001 - 0.0011881254125412541) * 0.05 * -1) - # = 0.0011787191419141915 - - trade = backtesting._enter_trade(pair, row=row, direction='short') - assert pytest.approx(trade.liquidation_price) == 0.0011787191 - - # Stake-amount too high! - mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=600.0) - - trade = backtesting._enter_trade(pair, row=row, direction='long') - assert trade is None - - # Stake-amount throwing error - mocker.patch("freqtrade.wallets.Wallets.get_trade_stake_amount", - side_effect=DependencyException) - - trade = backtesting._enter_trade(pair, row=row, direction='long') - assert trade is None - - -def test_backtest__get_sell_trade_entry(default_conf, fee, mocker) -> None: - default_conf['use_sell_signal'] = False - mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) - mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001) - mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf')) - patch_exchange(mocker) - default_conf['timeframe_detail'] = '1m' - default_conf['max_open_trades'] = 2 - backtesting = Backtesting(default_conf) - backtesting._set_strategy(backtesting.strategylist[0]) - pair = 'UNITTEST/BTC' - row = [ - pd.Timestamp(year=2020, month=1, day=1, hour=4, minute=55, tzinfo=timezone.utc), - 200, # Open - 201.5, # High - 195, # Low - 201, # Close - 1, # enter_long - 0, # exit_long - 0, # enter_short - 0, # exit_hsort - '', # Long Signal Name - '', # Short Signal Name - '', # Exit Signal Name - ] - - trade = backtesting._enter_trade(pair, row=row, direction='long') - assert isinstance(trade, LocalTrade) - - row_sell = [ - pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=0, tzinfo=timezone.utc), - 200, # Open - 210.5, # High - 195, # Low - 201, # Close - 0, # enter_long - 0, # exit_long - 0, # enter_short - 0, # exit_short - '', # long Signal Name - '', # Short Signal Name - '', # Exit Signal Name - - ] - row_detail = pd.DataFrame( - [ - [ - pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=0, tzinfo=timezone.utc), - 200, 200.1, 197, 199, 1, 0, 0, 0, '', '', '', - ], [ - pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=1, tzinfo=timezone.utc), - 199, 199.7, 199, 199.5, 0, 0, 0, 0, '', '', '', - ], [ - pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=2, tzinfo=timezone.utc), - 199.5, 200.8, 199, 200.9, 0, 0, 0, 0, '', '', '', - ], [ - pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=3, tzinfo=timezone.utc), - 200.5, 210.5, 193, 210.5, 0, 0, 0, 0, '', '', '', # ROI sell (?) - ], [ - pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=4, tzinfo=timezone.utc), - 200, 200.1, 193, 199, 0, 0, 0, 0, '', '', '', - ], - ], columns=['date', 'open', 'high', 'low', 'close', 'enter_long', 'exit_long', - 'enter_short', 'exit_short', 'long_tag', 'short_tag', 'exit_tag'] - ) - - # No data available. - res = backtesting._get_sell_trade_entry(trade, row_sell) - assert res is not None - assert res.exit_reason == ExitType.ROI.value - assert res.close_date_utc == datetime(2020, 1, 1, 5, 0, tzinfo=timezone.utc) - - # Enter new trade - trade = backtesting._enter_trade(pair, row=row, direction='long') - assert isinstance(trade, LocalTrade) - # Assign empty ... no result. - backtesting.detail_data[pair] = pd.DataFrame( - [], columns=['date', 'open', 'high', 'low', 'close', 'enter_long', 'exit_long', - 'enter_short', 'exit_short', 'long_tag', 'short_tag', 'exit_tag']) - - res = backtesting._get_sell_trade_entry(trade, row) - assert res is None - - # Assign backtest-detail data - backtesting.detail_data[pair] = row_detail - - res = backtesting._get_sell_trade_entry(trade, row_sell) - assert res is not None -<<<<<<< HEAD - assert res.sell_reason == SellType.ROI.value - -======= - assert res.exit_reason == ExitType.ROI.value ->>>>>>> develop - # Sell at minute 3 (not available above!) - assert res.close_date_utc == datetime(2020, 1, 1, 5, 3, tzinfo=timezone.utc) - sell_order = res.select_order('sell', True) - assert sell_order is not None - - -def test_backtest_one(default_conf, fee, mocker, testdatadir) -> None: - default_conf['use_sell_signal'] = False - mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) - mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001) - mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf')) - patch_exchange(mocker) - backtesting = Backtesting(default_conf) - backtesting._set_strategy(backtesting.strategylist[0]) - pair = 'UNITTEST/BTC' - timerange = TimeRange('date', None, 1517227800, 0) - data = history.load_data(datadir=testdatadir, timeframe='5m', pairs=['UNITTEST/BTC'], - timerange=timerange) - processed = backtesting.strategy.advise_all_indicators(data) - min_date, max_date = get_timerange(processed) - - result = backtesting.backtest( - processed=deepcopy(processed), - start_date=min_date, - end_date=max_date, - max_open_trades=10, - position_stacking=False, - ) - results = result['results'] - assert not results.empty - assert len(results) == 2 - - expected = pd.DataFrame( - {'pair': [pair, pair], - 'stake_amount': [0.001, 0.001], - 'amount': [0.00957442, 0.0097064], - 'open_date': pd.to_datetime([Arrow(2018, 1, 29, 18, 40, 0).datetime, - Arrow(2018, 1, 30, 3, 30, 0).datetime], utc=True - ), - 'close_date': pd.to_datetime([Arrow(2018, 1, 29, 22, 35, 0).datetime, - Arrow(2018, 1, 30, 4, 10, 0).datetime], utc=True), - 'open_rate': [0.104445, 0.10302485], - 'close_rate': [0.104969, 0.103541], - 'fee_open': [0.0025, 0.0025], - 'fee_close': [0.0025, 0.0025], - 'trade_duration': [235, 40], - 'profit_ratio': [0.0, 0.0], - 'profit_abs': [0.0, 0.0], - 'exit_reason': [ExitType.ROI.value, ExitType.ROI.value], - 'initial_stop_loss_abs': [0.0940005, 0.09272236], - 'initial_stop_loss_ratio': [-0.1, -0.1], - 'stop_loss_abs': [0.0940005, 0.09272236], - 'stop_loss_ratio': [-0.1, -0.1], - 'min_rate': [0.10370188, 0.10300000000000001], - 'max_rate': [0.10501, 0.1038888], - 'is_open': [False, False], - 'enter_tag': [None, None], - "is_short": [False, False], - }) - pd.testing.assert_frame_equal(results, expected) - data_pair = processed[pair] - for _, t in results.iterrows(): - ln = data_pair.loc[data_pair["date"] == t["open_date"]] - # Check open trade rate alignes to open rate - assert ln is not None - assert round(ln.iloc[0]["open"], 6) == round(t["open_rate"], 6) - # check close trade rate alignes to close rate or is between high and low - ln = data_pair.loc[data_pair["date"] == t["close_date"]] - assert (round(ln.iloc[0]["open"], 6) == round(t["close_rate"], 6) or - round(ln.iloc[0]["low"], 6) < round( - t["close_rate"], 6) < round(ln.iloc[0]["high"], 6)) - - -def test_backtest_1min_timeframe(default_conf, fee, mocker, testdatadir) -> None: - default_conf['use_sell_signal'] = False - mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) - mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001) - mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf')) - patch_exchange(mocker) - backtesting = Backtesting(default_conf) - backtesting._set_strategy(backtesting.strategylist[0]) - - # Run a backtesting for an exiting 1min timeframe - timerange = TimeRange.parse_timerange('1510688220-1510700340') - data = history.load_data(datadir=testdatadir, timeframe='1m', pairs=['UNITTEST/BTC'], - timerange=timerange) - processed = backtesting.strategy.advise_all_indicators(data) - min_date, max_date = get_timerange(processed) - results = backtesting.backtest( - processed=processed, - start_date=min_date, - end_date=max_date, - max_open_trades=1, - position_stacking=False, - ) - assert not results['results'].empty - assert len(results['results']) == 1 - - -def test_backtest_trim_no_data_left(default_conf, fee, mocker, testdatadir) -> None: - default_conf['use_sell_signal'] = False - mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) - mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001) - mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf')) - patch_exchange(mocker) - backtesting = Backtesting(default_conf) - backtesting._set_strategy(backtesting.strategylist[0]) - timerange = TimeRange('date', None, 1517227800, 0) - backtesting.required_startup = 100 - backtesting.timerange = timerange - data = history.load_data(datadir=testdatadir, timeframe='5m', pairs=['UNITTEST/BTC'], - timerange=timerange) - df = data['UNITTEST/BTC'] - df.loc[:, 'date'] = df.loc[:, 'date'] - timedelta(days=1) - # Trimming 100 candles, so after 2nd trimming, no candle is left. - df = df.iloc[:100] - data['XRP/USDT'] = df - processed = backtesting.strategy.advise_all_indicators(data) - min_date, max_date = get_timerange(processed) - - backtesting.backtest( - processed=deepcopy(processed), - start_date=min_date, - end_date=max_date, - max_open_trades=10, - position_stacking=False, - ) - - -def test_processed(default_conf, mocker, testdatadir) -> None: - patch_exchange(mocker) - backtesting = Backtesting(default_conf) - backtesting._set_strategy(backtesting.strategylist[0]) - - dict_of_tickerrows = load_data_test('raise', testdatadir) - dataframes = backtesting.strategy.advise_all_indicators(dict_of_tickerrows) - dataframe = dataframes['UNITTEST/BTC'] - cols = dataframe.columns - # assert the dataframe got some of the indicator columns - for col in ['close', 'high', 'low', 'open', 'date', - 'ema10', 'rsi', 'fastd', 'plus_di']: - assert col in cols - - -def test_backtest_dataprovider_analyzed_df(default_conf, fee, mocker, testdatadir) -> None: - default_conf['use_sell_signal'] = False - mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) - mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001) - mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=100000) - patch_exchange(mocker) - backtesting = Backtesting(default_conf) - backtesting._set_strategy(backtesting.strategylist[0]) - timerange = TimeRange('date', None, 1517227800, 0) - data = history.load_data(datadir=testdatadir, timeframe='5m', pairs=['UNITTEST/BTC'], - timerange=timerange) - processed = backtesting.strategy.advise_all_indicators(data) - min_date, max_date = get_timerange(processed) - - global count - count = 0 - - def tmp_confirm_entry(pair, current_time, **kwargs): - dp = backtesting.strategy.dp - df, _ = dp.get_analyzed_dataframe(pair, backtesting.strategy.timeframe) - current_candle = df.iloc[-1].squeeze() - assert current_candle['enter_long'] == 1 - - candle_date = timeframe_to_next_date(backtesting.strategy.timeframe, current_candle['date']) - assert candle_date == current_time - # These asserts don't properly raise as they are nested, - # therefore we increment count and assert for that. - global count - count = count + 1 - - backtesting.strategy.confirm_trade_entry = tmp_confirm_entry - backtesting.backtest( - processed=deepcopy(processed), - start_date=min_date, - end_date=max_date, - max_open_trades=10, - position_stacking=False, - ) - assert count == 5 - - -def test_backtest_pricecontours_protections(default_conf, fee, mocker, testdatadir) -> None: - # While this test IS a copy of test_backtest_pricecontours, it's needed to ensure - # results do not carry-over to the next run, which is not given by using parametrize. - default_conf['protections'] = [ - { - "method": "CooldownPeriod", - "stop_duration": 3, - }] - - default_conf['enable_protections'] = True - mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) - mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001) - mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf')) - tests = [ - ['sine', 9], - ['raise', 10], - ['lower', 0], - ['sine', 9], - ['raise', 10], - ] - # While entry-signals are unrealistic, running backtesting - # over and over again should not cause different results - for [contour, numres] in tests: - # Debug output for random test failure - print(f"{contour}, {numres}") - assert len(simple_backtest(default_conf, contour, mocker, testdatadir)['results']) == numres - - -@pytest.mark.parametrize('protections,contour,expected', [ - (None, 'sine', 35), - (None, 'raise', 19), - (None, 'lower', 0), - (None, 'sine', 35), - (None, 'raise', 19), - ([{"method": "CooldownPeriod", "stop_duration": 3}], 'sine', 9), - ([{"method": "CooldownPeriod", "stop_duration": 3}], 'raise', 10), - ([{"method": "CooldownPeriod", "stop_duration": 3}], 'lower', 0), - ([{"method": "CooldownPeriod", "stop_duration": 3}], 'sine', 9), - ([{"method": "CooldownPeriod", "stop_duration": 3}], 'raise', 10), -]) -def test_backtest_pricecontours(default_conf, fee, mocker, testdatadir, - protections, contour, expected) -> None: - if protections: - default_conf['protections'] = protections - default_conf['enable_protections'] = True - - mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001) - mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf')) - mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) - # While entry-signals are unrealistic, running backtesting - # over and over again should not cause different results - assert len(simple_backtest(default_conf, contour, mocker, testdatadir)['results']) == expected - - -def test_backtest_clash_buy_sell(mocker, default_conf, testdatadir): - # Override the default buy trend function in our StrategyTest - def fun(dataframe=None, pair=None): - buy_value = 1 - sell_value = 1 - return _trend(dataframe, buy_value, sell_value) - - backtest_conf = _make_backtest_conf(mocker, conf=default_conf, datadir=testdatadir) - backtesting = Backtesting(default_conf) - backtesting._set_strategy(backtesting.strategylist[0]) - backtesting.strategy.advise_entry = fun # Override - backtesting.strategy.advise_exit = fun # Override - result = backtesting.backtest(**backtest_conf) - assert result['results'].empty - - -def test_backtest_only_sell(mocker, default_conf, testdatadir): - # Override the default buy trend function in our StrategyTest - def fun(dataframe=None, pair=None): - buy_value = 0 - sell_value = 1 - return _trend(dataframe, buy_value, sell_value) - - backtest_conf = _make_backtest_conf(mocker, conf=default_conf, datadir=testdatadir) - backtesting = Backtesting(default_conf) - backtesting._set_strategy(backtesting.strategylist[0]) - backtesting.strategy.advise_entry = fun # Override - backtesting.strategy.advise_exit = fun # Override - result = backtesting.backtest(**backtest_conf) - assert result['results'].empty - - -def test_backtest_alternate_buy_sell(default_conf, fee, mocker, testdatadir): - mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001) - mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf')) - mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) - backtest_conf = _make_backtest_conf(mocker, conf=default_conf, - pair='UNITTEST/BTC', datadir=testdatadir) - default_conf['timeframe'] = '1m' - backtesting = Backtesting(default_conf) - backtesting.required_startup = 0 - backtesting._set_strategy(backtesting.strategylist[0]) - backtesting.strategy.advise_entry = _trend_alternate # Override - backtesting.strategy.advise_exit = _trend_alternate # Override - result = backtesting.backtest(**backtest_conf) - # 200 candles in backtest data - # won't buy on first (shifted by 1) - # 100 buys signals - results = result['results'] - assert len(results) == 100 - # Cached data should be 200 - analyzed_df = backtesting.dataprovider.get_analyzed_dataframe('UNITTEST/BTC', '1m')[0] - assert len(analyzed_df) == 200 - # Expect last candle to be 1 below end date (as the last candle is assumed as "incomplete" - # during backtesting) - expected_last_candle_date = backtest_conf['end_date'] - timedelta(minutes=1) - assert analyzed_df.iloc[-1]['date'].to_pydatetime() == expected_last_candle_date - - # One trade was force-closed at the end - assert len(results.loc[results['is_open']]) == 0 - - -@pytest.mark.parametrize("pair", ['ADA/BTC', 'LTC/BTC']) -@pytest.mark.parametrize("tres", [0, 20, 30]) -def test_backtest_multi_pair(default_conf, fee, mocker, tres, pair, testdatadir): - - def _trend_alternate_hold(dataframe=None, metadata=None): - """ - Buy every xth candle - sell every other xth -2 (hold on to pairs a bit) - """ - if metadata['pair'] in ('ETH/BTC', 'LTC/BTC'): - multi = 20 - else: - multi = 18 - dataframe['enter_long'] = np.where(dataframe.index % multi == 0, 1, 0) - dataframe['exit_long'] = np.where((dataframe.index + multi - 2) % multi == 0, 1, 0) - dataframe['enter_short'] = 0 - dataframe['exit_short'] = 0 - return dataframe - - mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001) - mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf')) - mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) - patch_exchange(mocker) - - pairs = ['ADA/BTC', 'DASH/BTC', 'ETH/BTC', 'LTC/BTC', 'NXT/BTC'] - data = history.load_data(datadir=testdatadir, timeframe='5m', pairs=pairs) - # Only use 500 lines to increase performance - data = trim_dictlist(data, -500) - - # Remove data for one pair from the beginning of the data - if tres > 0: - data[pair] = data[pair][tres:].reset_index() - default_conf['timeframe'] = '5m' - - backtesting = Backtesting(default_conf) - backtesting._set_strategy(backtesting.strategylist[0]) - backtesting.strategy.advise_entry = _trend_alternate_hold # Override - backtesting.strategy.advise_exit = _trend_alternate_hold # Override - - processed = backtesting.strategy.advise_all_indicators(data) - min_date, max_date = get_timerange(processed) - backtest_conf = { - 'processed': deepcopy(processed), - 'start_date': min_date, - 'end_date': max_date, - 'max_open_trades': 3, - 'position_stacking': False, - } - - results = backtesting.backtest(**backtest_conf) - - # Make sure we have parallel trades - assert len(evaluate_result_multi(results['results'], '5m', 2)) > 0 - # make sure we don't have trades with more than configured max_open_trades - assert len(evaluate_result_multi(results['results'], '5m', 3)) == 0 - - # Cached data correctly removed amounts - offset = 1 if tres == 0 else 0 - removed_candles = len(data[pair]) - offset - backtesting.strategy.startup_candle_count - assert len(backtesting.dataprovider.get_analyzed_dataframe(pair, '5m')[0]) == removed_candles - assert len( - backtesting.dataprovider.get_analyzed_dataframe('NXT/BTC', '5m')[0] - ) == len(data['NXT/BTC']) - 1 - backtesting.strategy.startup_candle_count - - backtest_conf = { - 'processed': deepcopy(processed), - 'start_date': min_date, - 'end_date': max_date, - 'max_open_trades': 1, - 'position_stacking': False, - } - results = backtesting.backtest(**backtest_conf) - assert len(evaluate_result_multi(results['results'], '5m', 1)) == 0 - - -def test_backtest_start_timerange(default_conf, mocker, caplog, testdatadir): - - patch_exchange(mocker) - mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest') - mocker.patch('freqtrade.optimize.backtesting.generate_backtest_stats') - mocker.patch('freqtrade.optimize.backtesting.show_backtest_results') - mocker.patch('freqtrade.plugins.pairlistmanager.PairListManager.whitelist', - PropertyMock(return_value=['UNITTEST/BTC'])) - patched_configuration_load_config_file(mocker, default_conf) - - args = [ - 'backtesting', - '--config', 'config.json', - '--strategy', CURRENT_TEST_STRATEGY, - '--datadir', str(testdatadir), - '--timeframe', '1m', - '--timerange', '1510694220-1510700340', - '--enable-position-stacking', - '--disable-max-market-positions' - ] - args = get_args(args) - start_backtesting(args) - # check the logs, that will contain the backtest result - exists = [ - 'Parameter -i/--timeframe detected ... Using timeframe: 1m ...', - 'Ignoring max_open_trades (--disable-max-market-positions was used) ...', - 'Parameter --timerange detected: 1510694220-1510700340 ...', - f'Using data directory: {testdatadir} ...', - 'Loading data from 2017-11-14 20:57:00 ' - 'up to 2017-11-14 22:58:00 (0 days).', - 'Backtesting with data from 2017-11-14 21:17:00 ' - 'up to 2017-11-14 22:58:00 (0 days).', - 'Parameter --enable-position-stacking detected ...' - ] - - for line in exists: - assert log_has(line, caplog) - - -@pytest.mark.filterwarnings("ignore:deprecated") -def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir): - - default_conf.update({ - "use_sell_signal": True, - "sell_profit_only": False, - "sell_profit_offset": 0.0, - "ignore_roi_if_buy_signal": False, - }) - patch_exchange(mocker) - backtestmock = MagicMock(return_value={ - 'results': pd.DataFrame(columns=BT_DATA_COLUMNS), - 'config': default_conf, - 'locks': [], - 'rejected_signals': 20, - 'timedout_entry_orders': 0, - 'timedout_exit_orders': 0, - 'final_balance': 1000, - }) - mocker.patch('freqtrade.plugins.pairlistmanager.PairListManager.whitelist', - PropertyMock(return_value=['UNITTEST/BTC'])) - mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest', backtestmock) - text_table_mock = MagicMock() - sell_reason_mock = MagicMock() - strattable_mock = MagicMock() - strat_summary = MagicMock() - - mocker.patch.multiple('freqtrade.optimize.optimize_reports', - text_table_bt_results=text_table_mock, - text_table_strategy=strattable_mock, - generate_pair_metrics=MagicMock(), - generate_exit_reason_stats=sell_reason_mock, - generate_strategy_comparison=strat_summary, - generate_daily_stats=MagicMock(), - ) - patched_configuration_load_config_file(mocker, default_conf) - - args = [ - 'backtesting', - '--config', 'config.json', - '--datadir', str(testdatadir), - '--strategy-path', str(Path(__file__).parents[1] / 'strategy/strats'), - '--timeframe', '1m', - '--timerange', '1510694220-1510700340', - '--enable-position-stacking', - '--disable-max-market-positions', - '--strategy-list', - CURRENT_TEST_STRATEGY, - 'TestStrategyLegacyV1', - ] - args = get_args(args) - start_backtesting(args) - # 2 backtests, 4 tables - assert backtestmock.call_count == 2 - assert text_table_mock.call_count == 4 - assert strattable_mock.call_count == 1 - assert sell_reason_mock.call_count == 2 - assert strat_summary.call_count == 1 - - # check the logs, that will contain the backtest result - exists = [ - 'Parameter -i/--timeframe detected ... Using timeframe: 1m ...', - 'Ignoring max_open_trades (--disable-max-market-positions was used) ...', - 'Parameter --timerange detected: 1510694220-1510700340 ...', - f'Using data directory: {testdatadir} ...', - 'Loading data from 2017-11-14 20:57:00 ' - 'up to 2017-11-14 22:58:00 (0 days).', - 'Backtesting with data from 2017-11-14 21:17:00 ' - 'up to 2017-11-14 22:58:00 (0 days).', - 'Parameter --enable-position-stacking detected ...', - f'Running backtesting for Strategy {CURRENT_TEST_STRATEGY}', - 'Running backtesting for Strategy TestStrategyLegacyV1', - ] - - for line in exists: - assert log_has(line, caplog) - - -@pytest.mark.filterwarnings("ignore:deprecated") -def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdatadir, capsys): - default_conf.update({ - "use_sell_signal": True, - "sell_profit_only": False, - "sell_profit_offset": 0.0, - "ignore_roi_if_buy_signal": False, - }) - patch_exchange(mocker) - result1 = pd.DataFrame({'pair': ['XRP/BTC', 'LTC/BTC'], - 'profit_ratio': [0.0, 0.0], - 'profit_abs': [0.0, 0.0], - 'open_date': pd.to_datetime(['2018-01-29 18:40:00', - '2018-01-30 03:30:00', ], utc=True - ), - 'close_date': pd.to_datetime(['2018-01-29 20:45:00', - '2018-01-30 05:35:00', ], utc=True), - 'trade_duration': [235, 40], - 'is_open': [False, False], - 'stake_amount': [0.01, 0.01], - 'open_rate': [0.104445, 0.10302485], - 'close_rate': [0.104969, 0.103541], - "is_short": [False, False], - - 'exit_reason': [ExitType.ROI, ExitType.ROI] - }) - result2 = pd.DataFrame({'pair': ['XRP/BTC', 'LTC/BTC', 'ETH/BTC'], - 'profit_ratio': [0.03, 0.01, 0.1], - 'profit_abs': [0.01, 0.02, 0.2], - 'open_date': pd.to_datetime(['2018-01-29 18:40:00', - '2018-01-30 03:30:00', - '2018-01-30 05:30:00'], utc=True - ), - 'close_date': pd.to_datetime(['2018-01-29 20:45:00', - '2018-01-30 05:35:00', - '2018-01-30 08:30:00'], utc=True), - 'trade_duration': [47, 40, 20], - 'is_open': [False, False, False], - 'stake_amount': [0.01, 0.01, 0.01], - 'open_rate': [0.104445, 0.10302485, 0.122541], - 'close_rate': [0.104969, 0.103541, 0.123541], - "is_short": [False, False, False], - 'exit_reason': [ExitType.ROI, ExitType.ROI, ExitType.STOP_LOSS] - }) - backtestmock = MagicMock(side_effect=[ - { - 'results': result1, - 'config': default_conf, - 'locks': [], - 'rejected_signals': 20, - 'timedout_entry_orders': 0, - 'timedout_exit_orders': 0, - 'final_balance': 1000, - }, - { - 'results': result2, - 'config': default_conf, - 'locks': [], - 'rejected_signals': 20, - 'timedout_entry_orders': 0, - 'timedout_exit_orders': 0, - 'final_balance': 1000, - } - ]) - mocker.patch('freqtrade.plugins.pairlistmanager.PairListManager.whitelist', - PropertyMock(return_value=['UNITTEST/BTC'])) - mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest', backtestmock) - - patched_configuration_load_config_file(mocker, default_conf) - - args = [ - 'backtesting', - '--config', 'config.json', - '--datadir', str(testdatadir), - '--strategy-path', str(Path(__file__).parents[1] / 'strategy/strats'), - '--timeframe', '1m', - '--timerange', '1510694220-1510700340', - '--enable-position-stacking', - '--disable-max-market-positions', - '--breakdown', 'day', - '--strategy-list', - CURRENT_TEST_STRATEGY, - 'TestStrategyLegacyV1', - ] - args = get_args(args) - start_backtesting(args) - - # check the logs, that will contain the backtest result - exists = [ - 'Parameter -i/--timeframe detected ... Using timeframe: 1m ...', - 'Ignoring max_open_trades (--disable-max-market-positions was used) ...', - 'Parameter --timerange detected: 1510694220-1510700340 ...', - f'Using data directory: {testdatadir} ...', - 'Loading data from 2017-11-14 20:57:00 ' - 'up to 2017-11-14 22:58:00 (0 days).', - 'Backtesting with data from 2017-11-14 21:17:00 ' - 'up to 2017-11-14 22:58:00 (0 days).', - 'Parameter --enable-position-stacking detected ...', - f'Running backtesting for Strategy {CURRENT_TEST_STRATEGY}', - 'Running backtesting for Strategy TestStrategyLegacyV1', - ] - - for line in exists: - assert log_has(line, caplog) - - captured = capsys.readouterr() - assert 'BACKTESTING REPORT' in captured.out - assert 'EXIT REASON STATS' in captured.out - assert 'DAY BREAKDOWN' in captured.out - assert 'LEFT OPEN TRADES REPORT' in captured.out - assert '2017-11-14 21:17:00 -> 2017-11-14 22:58:00 | Max open trades : 1' in captured.out - assert 'STRATEGY SUMMARY' in captured.out - - -@pytest.mark.filterwarnings("ignore:deprecated") -def test_backtest_start_nomock_futures(default_conf_usdt, mocker, - caplog, testdatadir, capsys): - # Tests detail-data loading - default_conf_usdt.update({ - "trading_mode": "futures", - "margin_mode": "isolated", - "use_sell_signal": True, - "sell_profit_only": False, - "sell_profit_offset": 0.0, - "ignore_roi_if_buy_signal": False, - "strategy": CURRENT_TEST_STRATEGY, - }) - patch_exchange(mocker) - result1 = pd.DataFrame({'pair': ['XRP/USDT', 'XRP/USDT'], - 'profit_ratio': [0.0, 0.0], - 'profit_abs': [0.0, 0.0], - 'open_date': pd.to_datetime(['2021-11-18 18:00:00', - '2021-11-18 03:00:00', ], utc=True - ), - 'close_date': pd.to_datetime(['2021-11-18 20:00:00', - '2021-11-18 05:00:00', ], utc=True), - 'trade_duration': [235, 40], - 'is_open': [False, False], - 'is_short': [False, False], - 'stake_amount': [0.01, 0.01], - 'open_rate': [0.104445, 0.10302485], - 'close_rate': [0.104969, 0.103541], - 'exit_reason': [ExitType.ROI, ExitType.ROI] - }) - result2 = pd.DataFrame({'pair': ['XRP/USDT', 'XRP/USDT', 'XRP/USDT'], - 'profit_ratio': [0.03, 0.01, 0.1], - 'profit_abs': [0.01, 0.02, 0.2], - 'open_date': pd.to_datetime(['2021-11-19 18:00:00', - '2021-11-19 03:00:00', - '2021-11-19 05:00:00'], utc=True - ), - 'close_date': pd.to_datetime(['2021-11-19 20:00:00', - '2021-11-19 05:00:00', - '2021-11-19 08:00:00'], utc=True), - 'trade_duration': [47, 40, 20], - 'is_open': [False, False, False], - 'is_short': [False, False, False], - 'stake_amount': [0.01, 0.01, 0.01], - 'open_rate': [0.104445, 0.10302485, 0.122541], - 'close_rate': [0.104969, 0.103541, 0.123541], - 'exit_reason': [ExitType.ROI, ExitType.ROI, ExitType.STOP_LOSS] - }) - backtestmock = MagicMock(side_effect=[ - { - 'results': result1, - 'config': default_conf_usdt, - 'locks': [], - 'rejected_signals': 20, - 'timedout_entry_orders': 0, - 'timedout_exit_orders': 0, - 'final_balance': 1000, - }, - { - 'results': result2, - 'config': default_conf_usdt, - 'locks': [], - 'rejected_signals': 20, - 'timedout_entry_orders': 0, - 'timedout_exit_orders': 0, - 'final_balance': 1000, - } - ]) - mocker.patch('freqtrade.plugins.pairlistmanager.PairListManager.whitelist', - PropertyMock(return_value=['XRP/USDT'])) - mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest', backtestmock) - - patched_configuration_load_config_file(mocker, default_conf_usdt) - - args = [ - 'backtesting', - '--config', 'config.json', - '--datadir', str(testdatadir), - '--strategy-path', str(Path(__file__).parents[1] / 'strategy/strats'), - '--timeframe', '1h', - ] - args = get_args(args) - start_backtesting(args) - - # check the logs, that will contain the backtest result - exists = [ - 'Parameter -i/--timeframe detected ... Using timeframe: 1h ...', - f'Using data directory: {testdatadir} ...', - 'Loading data from 2021-11-17 01:00:00 ' - 'up to 2021-11-21 03:00:00 (4 days).', - 'Backtesting with data from 2021-11-17 21:00:00 ' - 'up to 2021-11-21 03:00:00 (3 days).', - 'XRP/USDT, funding_rate, 8h, data starts at 2021-11-18 00:00:00', - 'XRP/USDT, mark, 8h, data starts at 2021-11-18 00:00:00', - f'Running backtesting for Strategy {CURRENT_TEST_STRATEGY}', - ] - - for line in exists: - assert log_has(line, caplog) - - captured = capsys.readouterr() - assert 'BACKTESTING REPORT' in captured.out - assert 'EXIT REASON STATS' in captured.out - assert 'LEFT OPEN TRADES REPORT' in captured.out - - -@pytest.mark.filterwarnings("ignore:deprecated") -def test_backtest_start_multi_strat_nomock_detail(default_conf, mocker, - caplog, testdatadir, capsys): - # Tests detail-data loading - default_conf.update({ - "use_sell_signal": True, - "sell_profit_only": False, - "sell_profit_offset": 0.0, - "ignore_roi_if_buy_signal": False, - }) - patch_exchange(mocker) - result1 = pd.DataFrame({'pair': ['XRP/BTC', 'LTC/BTC'], - 'profit_ratio': [0.0, 0.0], - 'profit_abs': [0.0, 0.0], - 'open_date': pd.to_datetime(['2018-01-29 18:40:00', - '2018-01-30 03:30:00', ], utc=True - ), - 'close_date': pd.to_datetime(['2018-01-29 20:45:00', - '2018-01-30 05:35:00', ], utc=True), - 'trade_duration': [235, 40], - 'is_open': [False, False], - 'is_short': [False, False], - 'stake_amount': [0.01, 0.01], - 'open_rate': [0.104445, 0.10302485], - 'close_rate': [0.104969, 0.103541], - 'exit_reason': [ExitType.ROI, ExitType.ROI] - }) - result2 = pd.DataFrame({'pair': ['XRP/BTC', 'LTC/BTC', 'ETH/BTC'], - 'profit_ratio': [0.03, 0.01, 0.1], - 'profit_abs': [0.01, 0.02, 0.2], - 'open_date': pd.to_datetime(['2018-01-29 18:40:00', - '2018-01-30 03:30:00', - '2018-01-30 05:30:00'], utc=True - ), - 'close_date': pd.to_datetime(['2018-01-29 20:45:00', - '2018-01-30 05:35:00', - '2018-01-30 08:30:00'], utc=True), - 'trade_duration': [47, 40, 20], - 'is_open': [False, False, False], - 'is_short': [False, False, False], - 'stake_amount': [0.01, 0.01, 0.01], - 'open_rate': [0.104445, 0.10302485, 0.122541], - 'close_rate': [0.104969, 0.103541, 0.123541], - 'exit_reason': [ExitType.ROI, ExitType.ROI, ExitType.STOP_LOSS] - }) - backtestmock = MagicMock(side_effect=[ - { - 'results': result1, - 'config': default_conf, - 'locks': [], - 'rejected_signals': 20, - 'timedout_entry_orders': 0, - 'timedout_exit_orders': 0, - 'final_balance': 1000, - }, - { - 'results': result2, - 'config': default_conf, - 'locks': [], - 'rejected_signals': 20, - 'timedout_entry_orders': 0, - 'timedout_exit_orders': 0, - 'final_balance': 1000, - } - ]) - mocker.patch('freqtrade.plugins.pairlistmanager.PairListManager.whitelist', - PropertyMock(return_value=['XRP/ETH'])) - mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest', backtestmock) - - patched_configuration_load_config_file(mocker, default_conf) - - args = [ - 'backtesting', - '--config', 'config.json', - '--datadir', str(testdatadir), - '--strategy-path', str(Path(__file__).parents[1] / 'strategy/strats'), - '--timeframe', '5m', - '--timeframe-detail', '1m', - '--strategy-list', - CURRENT_TEST_STRATEGY - ] - args = get_args(args) - start_backtesting(args) - - # check the logs, that will contain the backtest result - exists = [ - 'Parameter -i/--timeframe detected ... Using timeframe: 5m ...', - 'Parameter --timeframe-detail detected, using 1m for intra-candle backtesting ...', - f'Using data directory: {testdatadir} ...', - 'Loading data from 2019-10-11 00:00:00 ' - 'up to 2019-10-13 11:10:00 (2 days).', - 'Backtesting with data from 2019-10-11 01:40:00 ' - 'up to 2019-10-13 11:10:00 (2 days).', - f'Running backtesting for Strategy {CURRENT_TEST_STRATEGY}', - ] - - for line in exists: - assert log_has(line, caplog) - - captured = capsys.readouterr() - assert 'BACKTESTING REPORT' in captured.out - assert 'EXIT REASON STATS' in captured.out - assert 'LEFT OPEN TRADES REPORT' in captured.out - - -@pytest.mark.filterwarnings("ignore:deprecated") -@pytest.mark.parametrize('run_id', ['2', 'changed']) -@pytest.mark.parametrize('start_delta', [{'days': 0}, {'days': 1}, {'weeks': 1}, {'weeks': 4}]) -@pytest.mark.parametrize('cache', constants.BACKTEST_CACHE_AGE) -def test_backtest_start_multi_strat_caching(default_conf, mocker, caplog, testdatadir, run_id, - start_delta, cache): - default_conf.update({ - "use_sell_signal": True, - "sell_profit_only": False, - "sell_profit_offset": 0.0, - "ignore_roi_if_buy_signal": False, - }) - patch_exchange(mocker) - backtestmock = MagicMock(return_value={ - 'results': pd.DataFrame(columns=BT_DATA_COLUMNS), - 'config': default_conf, - 'locks': [], - 'rejected_signals': 20, - 'timedout_entry_orders': 0, - 'timedout_exit_orders': 0, - 'final_balance': 1000, - }) - mocker.patch('freqtrade.plugins.pairlistmanager.PairListManager.whitelist', - PropertyMock(return_value=['UNITTEST/BTC'])) - mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest', backtestmock) - mocker.patch('freqtrade.optimize.backtesting.show_backtest_results', MagicMock()) - - now = min_backtest_date = datetime.now(tz=timezone.utc) - start_time = now - timedelta(**start_delta) + timedelta(hours=1) - if cache == 'none': - min_backtest_date = now + timedelta(days=1) - elif cache == 'day': - min_backtest_date = now - timedelta(days=1) - elif cache == 'week': - min_backtest_date = now - timedelta(weeks=1) - elif cache == 'month': - min_backtest_date = now - timedelta(weeks=4) - load_backtest_metadata = MagicMock(return_value={ - 'StrategyTestV2': {'run_id': '1', 'backtest_start_time': now.timestamp()}, - 'TestStrategyLegacyV1': {'run_id': run_id, 'backtest_start_time': start_time.timestamp()} - }) - load_backtest_stats = MagicMock(side_effect=[ - { - 'metadata': {'StrategyTestV2': {'run_id': '1'}}, - 'strategy': {'StrategyTestV2': {}}, - 'strategy_comparison': [{'key': 'StrategyTestV2'}] - }, - { - 'metadata': {'TestStrategyLegacyV1': {'run_id': '2'}}, - 'strategy': {'TestStrategyLegacyV1': {}}, - 'strategy_comparison': [{'key': 'TestStrategyLegacyV1'}] - } - ]) - mocker.patch('pathlib.Path.glob', return_value=[ - Path(datetime.strftime(datetime.now(), 'backtest-result-%Y-%m-%d_%H-%M-%S.json'))]) - mocker.patch.multiple('freqtrade.data.btanalysis', - load_backtest_metadata=load_backtest_metadata, - load_backtest_stats=load_backtest_stats) - mocker.patch('freqtrade.optimize.backtesting.get_strategy_run_id', side_effect=['1', '2', '2']) - - patched_configuration_load_config_file(mocker, default_conf) - - args = [ - 'backtesting', - '--config', 'config.json', - '--datadir', str(testdatadir), - '--strategy-path', str(Path(__file__).parents[1] / 'strategy/strats'), - '--timeframe', '1m', - '--timerange', '1510694220-1510700340', - '--enable-position-stacking', - '--disable-max-market-positions', - '--cache', cache, - '--strategy-list', - 'StrategyTestV2', - 'TestStrategyLegacyV1', - ] - args = get_args(args) - start_backtesting(args) - - # check the logs, that will contain the backtest result - exists = [ - 'Parameter -i/--timeframe detected ... Using timeframe: 1m ...', - 'Parameter --timerange detected: 1510694220-1510700340 ...', - f'Using data directory: {testdatadir} ...', - 'Loading data from 2017-11-14 20:57:00 ' - 'up to 2017-11-14 22:58:00 (0 days).', - 'Parameter --enable-position-stacking detected ...', - ] - - for line in exists: - assert log_has(line, caplog) - - if cache == 'none': - assert backtestmock.call_count == 2 - exists = [ - 'Running backtesting for Strategy StrategyTestV2', - 'Running backtesting for Strategy TestStrategyLegacyV1', - 'Ignoring max_open_trades (--disable-max-market-positions was used) ...', - 'Backtesting with data from 2017-11-14 21:17:00 up to 2017-11-14 22:58:00 (0 days).', - ] - elif run_id == '2' and min_backtest_date < start_time: - assert backtestmock.call_count == 0 - exists = [ - 'Reusing result of previous backtest for StrategyTestV2', - 'Reusing result of previous backtest for TestStrategyLegacyV1', - ] - else: - exists = [ - 'Reusing result of previous backtest for StrategyTestV2', - 'Running backtesting for Strategy TestStrategyLegacyV1', - 'Ignoring max_open_trades (--disable-max-market-positions was used) ...', - 'Backtesting with data from 2017-11-14 21:17:00 up to 2017-11-14 22:58:00 (0 days).', - ] - assert backtestmock.call_count == 1 - - for line in exists: - assert log_has(line, caplog) - - -def test_get_strategy_run_id(default_conf_usdt): - default_conf_usdt.update({ - 'strategy': 'StrategyTestV2', - 'max_open_trades': float('inf') - }) - strategy = StrategyResolver.load_strategy(default_conf_usdt) - x = get_strategy_run_id(strategy) - assert isinstance(x, str) diff --git a/tests/rpc/test_rpc_telegram.py.orig b/tests/rpc/test_rpc_telegram.py.orig deleted file mode 100644 index 9e3e6aa65..000000000 --- a/tests/rpc/test_rpc_telegram.py.orig +++ /dev/null @@ -1,2452 +0,0 @@ -# pragma pylint: disable=missing-docstring, C0103 -# pragma pylint: disable=protected-access, unused-argument, invalid-name -# pragma pylint: disable=too-many-lines, too-many-arguments - -import logging -import re -from datetime import datetime, timedelta, timezone -from functools import reduce -from random import choice, randint -from string import ascii_uppercase -from unittest.mock import ANY, MagicMock - -import arrow -import pytest -from telegram import Chat, Message, ReplyKeyboardMarkup, Update -from telegram.error import BadRequest, NetworkError, TelegramError - -from freqtrade import __version__ -from freqtrade.constants import CANCEL_REASON -from freqtrade.edge import PairInfo -from freqtrade.enums import ExitType, RPCMessageType, RunMode, SignalDirection, State -from freqtrade.exceptions import OperationalException -from freqtrade.freqtradebot import FreqtradeBot -from freqtrade.loggers import setup_logging -from freqtrade.persistence import PairLocks, Trade -from freqtrade.persistence.models import Order -from freqtrade.rpc import RPC -from freqtrade.rpc.rpc import RPCException -from freqtrade.rpc.telegram import Telegram, authorized_only -from tests.conftest import (CURRENT_TEST_STRATEGY, create_mock_trades, get_patched_freqtradebot, - log_has, log_has_re, patch_exchange, patch_get_signal, patch_whitelist) - - -class DummyCls(Telegram): - """ - Dummy class for testing the Telegram @authorized_only decorator - """ - - def __init__(self, rpc: RPC, config) -> None: - super().__init__(rpc, config) - self.state = {'called': False} - - def _init(self): - pass - - @authorized_only - def dummy_handler(self, *args, **kwargs) -> None: - """ - Fake method that only change the state of the object - """ - self.state['called'] = True - - @authorized_only - def dummy_exception(self, *args, **kwargs) -> None: - """ - Fake method that throw an exception - """ - raise Exception('test') - - -def get_telegram_testobject(mocker, default_conf, mock=True, ftbot=None): - msg_mock = MagicMock() - if mock: - mocker.patch.multiple( - 'freqtrade.rpc.telegram.Telegram', - _init=MagicMock(), - _send_msg=msg_mock - ) - if not ftbot: - ftbot = get_patched_freqtradebot(mocker, default_conf) - rpc = RPC(ftbot) - telegram = Telegram(rpc, default_conf) - - return telegram, ftbot, msg_mock - - -def test_telegram__init__(default_conf, mocker) -> None: - mocker.patch('freqtrade.rpc.telegram.Updater', MagicMock()) - mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock()) - - telegram, _, _ = get_telegram_testobject(mocker, default_conf) - assert telegram._config == default_conf - - -def test_telegram_init(default_conf, mocker, caplog) -> None: - start_polling = MagicMock() - mocker.patch('freqtrade.rpc.telegram.Updater', MagicMock(return_value=start_polling)) - - get_telegram_testobject(mocker, default_conf, mock=False) - assert start_polling.call_count == 0 - - # number of handles registered - assert start_polling.dispatcher.add_handler.call_count > 0 - assert start_polling.start_polling.call_count == 1 - - message_str = ("rpc.telegram is listening for following commands: [['status'], ['profit'], " - "['balance'], ['start'], ['stop'], " - "['forcesell', 'forceexit'], ['forcebuy', 'forcelong'], ['forceshort'], " - "['trades'], ['delete'], ['performance'], " - "['buys', 'entries'], ['sells', 'exits'], ['mix_tags'], " - "['stats'], ['daily'], ['weekly'], ['monthly'], " - "['count'], ['locks'], ['unlock', 'delete_locks'], " - "['reload_config', 'reload_conf'], ['show_config', 'show_conf'], " - "['stopbuy'], ['whitelist'], ['blacklist'], ['blacklist_delete', 'bl_delete'], " - "['logs'], ['edge'], ['health'], ['help'], ['version']" - "]") - - assert log_has(message_str, caplog) - - -def test_cleanup(default_conf, mocker, ) -> None: - updater_mock = MagicMock() - updater_mock.stop = MagicMock() - mocker.patch('freqtrade.rpc.telegram.Updater', updater_mock) - - telegram, _, _ = get_telegram_testobject(mocker, default_conf, mock=False) - telegram.cleanup() - assert telegram._updater.stop.call_count == 1 - - -def test_authorized_only(default_conf, mocker, caplog, update) -> None: - patch_exchange(mocker) - caplog.set_level(logging.DEBUG) - default_conf['telegram']['enabled'] = False - bot = FreqtradeBot(default_conf) - rpc = RPC(bot) - dummy = DummyCls(rpc, default_conf) - - patch_get_signal(bot) - dummy.dummy_handler(update=update, context=MagicMock()) - assert dummy.state['called'] is True - assert log_has('Executing handler: dummy_handler for chat_id: 0', caplog) - assert not log_has('Rejected unauthorized message from: 0', caplog) - assert not log_has('Exception occurred within Telegram module', caplog) - - -def test_authorized_only_unauthorized(default_conf, mocker, caplog) -> None: - patch_exchange(mocker) - caplog.set_level(logging.DEBUG) - chat = Chat(0xdeadbeef, 0) - update = Update(randint(1, 100)) - update.message = Message(randint(1, 100), datetime.utcnow(), chat) - - default_conf['telegram']['enabled'] = False - bot = FreqtradeBot(default_conf) - rpc = RPC(bot) - dummy = DummyCls(rpc, default_conf) - - patch_get_signal(bot) - dummy.dummy_handler(update=update, context=MagicMock()) - assert dummy.state['called'] is False - assert not log_has('Executing handler: dummy_handler for chat_id: 3735928559', caplog) - assert log_has('Rejected unauthorized message from: 3735928559', caplog) - assert not log_has('Exception occurred within Telegram module', caplog) - - -def test_authorized_only_exception(default_conf, mocker, caplog, update) -> None: - patch_exchange(mocker) - - default_conf['telegram']['enabled'] = False - - bot = FreqtradeBot(default_conf) - rpc = RPC(bot) - dummy = DummyCls(rpc, default_conf) - patch_get_signal(bot) - - dummy.dummy_exception(update=update, context=MagicMock()) - assert dummy.state['called'] is False - assert not log_has('Executing handler: dummy_handler for chat_id: 0', caplog) - assert not log_has('Rejected unauthorized message from: 0', caplog) - assert log_has('Exception occurred within Telegram module', caplog) - - -def test_telegram_status(default_conf, update, mocker) -> None: - update.message.chat.id = "123" - default_conf['telegram']['enabled'] = False - default_conf['telegram']['chat_id'] = "123" - - status_table = MagicMock() - mocker.patch('freqtrade.rpc.telegram.Telegram._status_table', status_table) - - mocker.patch.multiple( - 'freqtrade.rpc.rpc.RPC', - _rpc_trade_status=MagicMock(return_value=[{ - 'trade_id': 1, - 'pair': 'ETH/BTC', - 'base_currency': 'BTC', - 'open_date': arrow.utcnow(), - 'close_date': None, - 'open_rate': 1.099e-05, - 'close_rate': None, - 'current_rate': 1.098e-05, - 'amount': 90.99181074, - 'stake_amount': 90.99181074, - 'buy_tag': None, - 'enter_tag': None, - 'close_profit_ratio': None, - 'profit': -0.0059, - 'profit_ratio': -0.0059, - 'initial_stop_loss_abs': 1.098e-05, - 'stop_loss_abs': 1.099e-05, - 'exit_order_status': None, - 'initial_stop_loss_ratio': -0.0005, - 'stoploss_current_dist': 1e-08, - 'stoploss_current_dist_ratio': -0.0002, - 'stop_loss_ratio': -0.0001, - 'open_order': '(limit buy rem=0.00000000)', - 'is_open': True, - 'is_short': False, - 'filled_entry_orders': [], - 'orders': [] - }]), - ) - - telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf) - - telegram._status(update=update, context=MagicMock()) - assert msg_mock.call_count == 1 - - context = MagicMock() - # /status table - context.args = ["table"] - telegram._status(update=update, context=context) - assert status_table.call_count == 1 - - -@pytest.mark.usefixtures("init_persistence") -def test_telegram_status_multi_entry(default_conf, update, mocker, fee) -> None: - update.message.chat.id = "123" - default_conf['telegram']['enabled'] = False - default_conf['telegram']['chat_id'] = "123" - default_conf['position_adjustment_enable'] = True - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_order=MagicMock(return_value=None), - get_rate=MagicMock(return_value=0.22), - ) - - telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf) - - create_mock_trades(fee) - trades = Trade.get_open_trades() - trade = trades[0] - # Average may be empty on some exchanges - trade.orders[0].average = 0 - trade.orders.append(Order( - order_id='5412vbb', - ft_order_side='buy', - ft_pair=trade.pair, - ft_is_open=False, - status="closed", - symbol=trade.pair, - order_type="market", - side="buy", - price=trade.open_rate * 0.95, - average=0, - filled=trade.amount, - remaining=0, - cost=trade.amount, - order_date=trade.open_date, - order_filled_date=trade.open_date, - ) - ) - trade.recalc_trade_from_orders() - Trade.commit() - - telegram._status(update=update, context=MagicMock()) - assert msg_mock.call_count == 4 - msg = msg_mock.call_args_list[0][0][0] - assert re.search(r'Number of Entries.*2', msg) - assert re.search(r'Average Entry Price', msg) - assert re.search(r'Order filled at', msg) - assert re.search(r'Close Date:', msg) is None - assert re.search(r'Close Profit:', msg) is None - - -@pytest.mark.usefixtures("init_persistence") -def test_telegram_status_closed_trade(default_conf, update, mocker, fee) -> None: - update.message.chat.id = "123" - default_conf['telegram']['enabled'] = False - default_conf['telegram']['chat_id'] = "123" - default_conf['position_adjustment_enable'] = True - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_order=MagicMock(return_value=None), - get_rate=MagicMock(return_value=0.22), - ) - - telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf) - - create_mock_trades(fee) - trades = Trade.get_trades([Trade.is_open.is_(False)]) - trade = trades[0] - context = MagicMock() - context.args = [str(trade.id)] - telegram._status(update=update, context=context) - assert msg_mock.call_count == 1 - msg = msg_mock.call_args_list[0][0][0] - assert re.search(r'Close Date:', msg) - assert re.search(r'Close Profit:', msg) - - -def test_status_handle(default_conf, update, ticker, fee, mocker) -> None: - default_conf['max_open_trades'] = 3 - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, - get_fee=fee, - _is_dry_limit_order_filled=MagicMock(return_value=True), - ) - status_table = MagicMock() - mocker.patch.multiple( - 'freqtrade.rpc.telegram.Telegram', - _status_table=status_table, - ) - - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - - patch_get_signal(freqtradebot) - - freqtradebot.state = State.STOPPED - # Status is also enabled when stopped - telegram._status(update=update, context=MagicMock()) - assert msg_mock.call_count == 1 - assert 'no active trade' in msg_mock.call_args_list[0][0][0] - msg_mock.reset_mock() - - freqtradebot.state = State.RUNNING - telegram._status(update=update, context=MagicMock()) - assert msg_mock.call_count == 1 - assert 'no active trade' in msg_mock.call_args_list[0][0][0] - msg_mock.reset_mock() - - # Create some test data - freqtradebot.enter_positions() - # Trigger status while we have a fulfilled order for the open trade - telegram._status(update=update, context=MagicMock()) - - # close_rate should not be included in the message as the trade is not closed - # and no line should be empty - lines = msg_mock.call_args_list[0][0][0].split('\n') - assert '' not in lines - assert 'Close Rate' not in ''.join(lines) - assert 'Close Profit' not in ''.join(lines) - - assert msg_mock.call_count == 3 - assert 'ETH/BTC' in msg_mock.call_args_list[0][0][0] - assert 'LTC/BTC' in msg_mock.call_args_list[1][0][0] - - msg_mock.reset_mock() - context = MagicMock() - context.args = ["2", "3"] - - telegram._status(update=update, context=context) - - lines = msg_mock.call_args_list[0][0][0].split('\n') - assert '' not in lines - assert 'Close Rate' not in ''.join(lines) - assert 'Close Profit' not in ''.join(lines) - - assert msg_mock.call_count == 2 - assert 'LTC/BTC' in msg_mock.call_args_list[0][0][0] - - -def test_status_table_handle(default_conf, update, ticker, fee, mocker) -> None: - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, - get_fee=fee, - ) - - default_conf['stake_amount'] = 15.0 - - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - - patch_get_signal(freqtradebot) - - freqtradebot.state = State.STOPPED - # Status table is also enabled when stopped - telegram._status_table(update=update, context=MagicMock()) - assert msg_mock.call_count == 1 - assert 'no active trade' in msg_mock.call_args_list[0][0][0] - msg_mock.reset_mock() - - freqtradebot.state = State.RUNNING - telegram._status_table(update=update, context=MagicMock()) - assert msg_mock.call_count == 1 - assert 'no active trade' in msg_mock.call_args_list[0][0][0] - msg_mock.reset_mock() - - # Create some test data - freqtradebot.enter_positions() - - telegram._status_table(update=update, context=MagicMock()) - - text = re.sub('', '', msg_mock.call_args_list[-1][0][0]) - line = text.split("\n") - fields = re.sub('[ ]+', ' ', line[2].strip()).split(' ') - - assert int(fields[0]) == 1 - assert 'L' in fields[1] - assert 'ETH/BTC' in fields[2] - assert msg_mock.call_count == 1 - - -def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee, - limit_sell_order, mocker) -> None: - default_conf['max_open_trades'] = 1 - mocker.patch( - 'freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', - return_value=15000.0 - ) - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, - get_fee=fee, - ) - - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - - patch_get_signal(freqtradebot) - - # Create some test data - freqtradebot.enter_positions() - trade = Trade.query.first() - assert trade - - # Simulate fulfilled LIMIT_BUY order for trade - limit_buy_order['id'] = trade.orders[0].order_id - oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy') - trade.update_order(limit_buy_order) - trade.update_trade(oobj) - - # Simulate fulfilled LIMIT_SELL order for trade - oobjs = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell') - trade.update_trade(oobjs) - - trade.close_date = datetime.utcnow() - trade.is_open = False - - # Try valid data - # /daily 2 - context = MagicMock() - context.args = ["2"] - telegram._daily(update=update, context=context) - assert msg_mock.call_count == 1 - assert "Daily Profit over the last 2 days:" in msg_mock.call_args_list[0][0][0] - assert 'Day ' in msg_mock.call_args_list[0][0][0] - assert str(datetime.utcnow().date()) in msg_mock.call_args_list[0][0][0] - assert str(' 0.00006217 BTC') in msg_mock.call_args_list[0][0][0] - assert str(' 0.933 USD') in msg_mock.call_args_list[0][0][0] - assert str(' 1 trade') in msg_mock.call_args_list[0][0][0] - assert str(' 0 trade') in msg_mock.call_args_list[0][0][0] - - # Reset msg_mock - msg_mock.reset_mock() - context.args = [] - telegram._daily(update=update, context=context) - assert msg_mock.call_count == 1 - assert "Daily Profit over the last 7 days:" in msg_mock.call_args_list[0][0][0] - assert str(datetime.utcnow().date()) in msg_mock.call_args_list[0][0][0] - assert str(' 0.00006217 BTC') in msg_mock.call_args_list[0][0][0] - assert str(' 0.933 USD') in msg_mock.call_args_list[0][0][0] - assert str(' 1 trade') in msg_mock.call_args_list[0][0][0] - assert str(' 0 trade') in msg_mock.call_args_list[0][0][0] - - # Reset msg_mock - msg_mock.reset_mock() - freqtradebot.config['max_open_trades'] = 2 - # Add two other trades - n = freqtradebot.enter_positions() - assert n == 2 - - trades = Trade.query.all() - for trade in trades: - trade.orders[0] = oobj - trade.update_trade(oobj) - trade.update_trade(oobjs) - trade.close_date = datetime.utcnow() - trade.is_open = False - - # /daily 1 - context = MagicMock() - context.args = ["1"] - telegram._daily(update=update, context=context) - assert str(' 0.00018651 BTC') in msg_mock.call_args_list[0][0][0] - assert str(' 2.798 USD') in msg_mock.call_args_list[0][0][0] - assert str(' 3 trades') in msg_mock.call_args_list[0][0][0] - - -def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None: - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=ticker - ) - - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot) - - # Try invalid data - msg_mock.reset_mock() - freqtradebot.state = State.RUNNING - # /daily -2 - context = MagicMock() - context.args = ["-2"] - telegram._daily(update=update, context=context) - assert msg_mock.call_count == 1 - assert 'must be an integer greater than 0' in msg_mock.call_args_list[0][0][0] - - # Try invalid data - msg_mock.reset_mock() - freqtradebot.state = State.RUNNING - # /daily today - context = MagicMock() - context.args = ["today"] - telegram._daily(update=update, context=context) - assert str('Daily Profit over the last 7 days:') in msg_mock.call_args_list[0][0][0] - - -def test_weekly_handle(default_conf, update, ticker, limit_buy_order, fee, - limit_sell_order, mocker) -> None: - default_conf['max_open_trades'] = 1 - mocker.patch( - 'freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', - return_value=15000.0 - ) - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, - get_fee=fee, - ) - - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - - patch_get_signal(freqtradebot) - - # Create some test data - freqtradebot.enter_positions() - trade = Trade.query.first() - assert trade - - # Simulate fulfilled LIMIT_BUY order for trade - limit_buy_order['id'] = trade.orders[0].order_id - oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy') - trade.update_order(limit_buy_order) - trade.update_trade(oobj) - - # Simulate fulfilled LIMIT_SELL order for trade - oobjs = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell') - trade.update_trade(oobjs) - - trade.close_date = datetime.utcnow() - trade.is_open = False - - # Try valid data - # /weekly 2 - context = MagicMock() - context.args = ["2"] - telegram._weekly(update=update, context=context) - assert msg_mock.call_count == 1 - assert "Weekly Profit over the last 2 weeks (starting from Monday):" \ - in msg_mock.call_args_list[0][0][0] - assert 'Monday ' in msg_mock.call_args_list[0][0][0] - today = datetime.utcnow().date() - first_iso_day_of_current_week = today - timedelta(days=today.weekday()) - assert str(first_iso_day_of_current_week) in msg_mock.call_args_list[0][0][0] - assert str(' 0.00006217 BTC') in msg_mock.call_args_list[0][0][0] - assert str(' 0.933 USD') in msg_mock.call_args_list[0][0][0] - assert str(' 1 trade') in msg_mock.call_args_list[0][0][0] - assert str(' 0 trade') in msg_mock.call_args_list[0][0][0] - - # Reset msg_mock - msg_mock.reset_mock() - context.args = [] - telegram._weekly(update=update, context=context) - assert msg_mock.call_count == 1 - assert "Weekly Profit over the last 8 weeks (starting from Monday):" \ - in msg_mock.call_args_list[0][0][0] - assert 'Weekly' in msg_mock.call_args_list[0][0][0] - assert str(' 0.00006217 BTC') in msg_mock.call_args_list[0][0][0] - assert str(' 0.933 USD') in msg_mock.call_args_list[0][0][0] - assert str(' 1 trade') in msg_mock.call_args_list[0][0][0] - assert str(' 0 trade') in msg_mock.call_args_list[0][0][0] - - # Reset msg_mock - msg_mock.reset_mock() - freqtradebot.config['max_open_trades'] = 2 - # Add two other trades - n = freqtradebot.enter_positions() - assert n == 2 - - trades = Trade.query.all() - for trade in trades: - trade.orders[0] = oobj - trade.update_trade(oobj) - trade.update_trade(oobjs) - trade.close_date = datetime.utcnow() - trade.is_open = False - - # /weekly 1 - # By default, the 8 previous weeks are shown - # So the previous modified trade should be excluded from the stats - context = MagicMock() - context.args = ["1"] - telegram._weekly(update=update, context=context) - assert str(' 0.00018651 BTC') in msg_mock.call_args_list[0][0][0] - assert str(' 2.798 USD') in msg_mock.call_args_list[0][0][0] - assert str(' 3 trades') in msg_mock.call_args_list[0][0][0] - - -def test_weekly_wrong_input(default_conf, update, ticker, mocker) -> None: - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=ticker - ) - - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot) - - # Try invalid data - msg_mock.reset_mock() - freqtradebot.state = State.RUNNING - # /weekly -3 - context = MagicMock() - context.args = ["-3"] - telegram._weekly(update=update, context=context) - assert msg_mock.call_count == 1 - assert 'must be an integer greater than 0' in msg_mock.call_args_list[0][0][0] - - # Try invalid data - msg_mock.reset_mock() - freqtradebot.state = State.RUNNING - # /weekly this week - context = MagicMock() - context.args = ["this week"] - telegram._weekly(update=update, context=context) - assert str('Weekly Profit over the last 8 weeks (starting from Monday):') \ - in msg_mock.call_args_list[0][0][0] - - -def test_monthly_handle(default_conf, update, ticker, limit_buy_order, fee, - limit_sell_order, mocker) -> None: - default_conf['max_open_trades'] = 1 - mocker.patch( - 'freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', - return_value=15000.0 - ) - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, - get_fee=fee, - ) - - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - - patch_get_signal(freqtradebot) - - # Create some test data - freqtradebot.enter_positions() - trade = Trade.query.first() - assert trade - - # Simulate fulfilled LIMIT_BUY order for trade - limit_buy_order['id'] = trade.orders[0].order_id - oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy') - trade.update_order(limit_buy_order) - trade.update_trade(oobj) - - # Simulate fulfilled LIMIT_SELL order for trade - oobjs = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell') - trade.update_trade(oobjs) - - trade.close_date = datetime.utcnow() - trade.is_open = False - - # Try valid data - # /monthly 2 - context = MagicMock() - context.args = ["2"] - telegram._monthly(update=update, context=context) - assert msg_mock.call_count == 1 - assert 'Monthly Profit over the last 2 months:' in msg_mock.call_args_list[0][0][0] - assert 'Month ' in msg_mock.call_args_list[0][0][0] - today = datetime.utcnow().date() - current_month = f"{today.year}-{today.month:02} " - assert current_month in msg_mock.call_args_list[0][0][0] - assert str(' 0.00006217 BTC') in msg_mock.call_args_list[0][0][0] - assert str(' 0.933 USD') in msg_mock.call_args_list[0][0][0] - assert str(' 1 trade') in msg_mock.call_args_list[0][0][0] - assert str(' 0 trade') in msg_mock.call_args_list[0][0][0] - - # Reset msg_mock - msg_mock.reset_mock() - context.args = [] - telegram._monthly(update=update, context=context) - assert msg_mock.call_count == 1 - # Default to 6 months - assert 'Monthly Profit over the last 6 months:' in msg_mock.call_args_list[0][0][0] - assert 'Month ' in msg_mock.call_args_list[0][0][0] - assert current_month in msg_mock.call_args_list[0][0][0] - assert str(' 0.00006217 BTC') in msg_mock.call_args_list[0][0][0] - assert str(' 0.933 USD') in msg_mock.call_args_list[0][0][0] - assert str(' 1 trade') in msg_mock.call_args_list[0][0][0] - assert str(' 0 trade') in msg_mock.call_args_list[0][0][0] - - # Reset msg_mock - msg_mock.reset_mock() - freqtradebot.config['max_open_trades'] = 2 - # Add two other trades - n = freqtradebot.enter_positions() - assert n == 2 - - trades = Trade.query.all() - for trade in trades: - trade.orders[0] = oobj - trade.update_trade(oobj) - trade.update_trade(oobjs) - trade.close_date = datetime.utcnow() - trade.is_open = False - - # /monthly 12 - context = MagicMock() - context.args = ["12"] - telegram._monthly(update=update, context=context) - assert msg_mock.call_count == 1 - assert 'Monthly Profit over the last 12 months:' in msg_mock.call_args_list[0][0][0] - assert str(' 0.00018651 BTC') in msg_mock.call_args_list[0][0][0] - assert str(' 2.798 USD') in msg_mock.call_args_list[0][0][0] - assert str(' 3 trades') in msg_mock.call_args_list[0][0][0] - - # The one-digit months should contain a zero, Eg: September 2021 = "2021-09" - # Since we loaded the last 12 months, any month should appear - assert str('-09') in msg_mock.call_args_list[0][0][0] - - -def test_monthly_wrong_input(default_conf, update, ticker, mocker) -> None: - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=ticker - ) - - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot) - - # Try invalid data - msg_mock.reset_mock() - freqtradebot.state = State.RUNNING - # /monthly -3 - context = MagicMock() - context.args = ["-3"] - telegram._monthly(update=update, context=context) - assert msg_mock.call_count == 1 - assert 'must be an integer greater than 0' in msg_mock.call_args_list[0][0][0] - - # Try invalid data - msg_mock.reset_mock() - freqtradebot.state = State.RUNNING - # /monthly february - context = MagicMock() - context.args = ["february"] - telegram._monthly(update=update, context=context) - assert str('Monthly Profit over the last 6 months:') in msg_mock.call_args_list[0][0][0] - - -def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee, - limit_buy_order, limit_sell_order, mocker) -> None: - mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0) - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, - get_fee=fee, - ) - - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot) - - telegram._profit(update=update, context=MagicMock()) - assert msg_mock.call_count == 1 - assert 'No trades yet.' in msg_mock.call_args_list[0][0][0] - msg_mock.reset_mock() - - # Create some test data - freqtradebot.enter_positions() - trade = Trade.query.first() - - # Simulate fulfilled LIMIT_BUY order for trade - limit_buy_order['id'] = trade.orders[0].order_id - oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy') - trade.update_order(limit_buy_order) - trade.update_trade(oobj) - Trade.commit() - - context = MagicMock() - # Test with invalid 2nd argument (should silently pass) - context.args = ["aaa"] - telegram._profit(update=update, context=context) - assert msg_mock.call_count == 1 - assert 'No closed trade' in msg_mock.call_args_list[-1][0][0] - assert '*ROI:* All trades' in msg_mock.call_args_list[-1][0][0] - mocker.patch('freqtrade.wallets.Wallets.get_starting_balance', return_value=0.01) - assert ('∙ `-0.000005 BTC (-0.50%) (-0.0 \N{GREEK CAPITAL LETTER SIGMA}%)`' - in msg_mock.call_args_list[-1][0][0]) - msg_mock.reset_mock() - - # Update the ticker with a market going up - mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', ticker_sell_up) - # Simulate fulfilled LIMIT_SELL order for trade - oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell') - trade.update_trade(oobj) - - trade.close_date = datetime.now(timezone.utc) - trade.is_open = False - Trade.commit() - - context.args = [3] - telegram._profit(update=update, context=context) - assert msg_mock.call_count == 1 - assert '*ROI:* Closed trades' in msg_mock.call_args_list[-1][0][0] - assert ('∙ `0.00006217 BTC (6.20%) (0.62 \N{GREEK CAPITAL LETTER SIGMA}%)`' - in msg_mock.call_args_list[-1][0][0]) - assert '∙ `0.933 USD`' in msg_mock.call_args_list[-1][0][0] - assert '*ROI:* All trades' in msg_mock.call_args_list[-1][0][0] - assert ('∙ `0.00006217 BTC (6.20%) (0.62 \N{GREEK CAPITAL LETTER SIGMA}%)`' - in msg_mock.call_args_list[-1][0][0]) - assert '∙ `0.933 USD`' in msg_mock.call_args_list[-1][0][0] - - assert '*Best Performing:* `ETH/BTC: 6.20%`' in msg_mock.call_args_list[-1][0][0] - - -@pytest.mark.parametrize('is_short', [True, False]) -def test_telegram_stats(default_conf, update, ticker, ticker_sell_up, fee, - limit_buy_order, limit_sell_order, mocker, is_short) -> None: - mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0) - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, - get_fee=fee, - ) - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot) - - telegram._stats(update=update, context=MagicMock()) - assert msg_mock.call_count == 1 - assert 'No trades yet.' in msg_mock.call_args_list[0][0][0] - msg_mock.reset_mock() - - # Create some test data - create_mock_trades(fee, is_short=is_short) - - telegram._stats(update=update, context=MagicMock()) - assert msg_mock.call_count == 1 - assert 'Exit Reason' in msg_mock.call_args_list[-1][0][0] - assert 'ROI' in msg_mock.call_args_list[-1][0][0] - assert 'Avg. Duration' in msg_mock.call_args_list[-1][0][0] - msg_mock.reset_mock() - - -def test_telegram_balance_handle(default_conf, update, mocker, rpc_balance, tickers) -> None: - default_conf['dry_run'] = False - mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value=rpc_balance) - mocker.patch('freqtrade.exchange.Exchange.get_tickers', tickers) - mocker.patch('freqtrade.exchange.Exchange.get_valid_pair_combination', - side_effect=lambda a, b: f"{a}/{b}") - - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot) - - telegram._balance(update=update, context=MagicMock()) - result = msg_mock.call_args_list[0][0][0] - assert msg_mock.call_count == 1 - assert '*BTC:*' in result - assert '*ETH:*' not in result - assert '*USDT:*' not in result - assert '*EUR:*' not in result - assert '*LTC:*' in result - assert '*XRP:*' not in result - assert 'Balance:' in result - assert 'Est. BTC:' in result - assert 'BTC: 12' in result - assert "*3 Other Currencies (< 0.0001 BTC):*" in result - assert 'BTC: 0.00000309' in result - - -def test_balance_handle_empty_response(default_conf, update, mocker) -> None: - default_conf['dry_run'] = False - mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value={}) - - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot) - - freqtradebot.config['dry_run'] = False - telegram._balance(update=update, context=MagicMock()) - result = msg_mock.call_args_list[0][0][0] - assert msg_mock.call_count == 1 - assert 'Starting capital: `0 BTC' in result - - -def test_balance_handle_empty_response_dry(default_conf, update, mocker) -> None: - mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value={}) - - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot) - - telegram._balance(update=update, context=MagicMock()) - result = msg_mock.call_args_list[0][0][0] - assert msg_mock.call_count == 1 - assert "*Warning:* Simulated balances in Dry Mode." in result - assert "Starting capital: `1000 BTC`" in result - - -def test_balance_handle_too_large_response(default_conf, update, mocker) -> None: - balances = [] - for i in range(100): - curr = choice(ascii_uppercase) + choice(ascii_uppercase) + choice(ascii_uppercase) - balances.append({ - 'currency': curr, - 'free': 1.0, - 'used': 0.5, - 'balance': i, - 'est_stake': 1, - 'stake': 'BTC', - 'is_position': False, - 'leverage': 1.0, - 'position': 0.0, - 'side': 'long', - }) - mocker.patch('freqtrade.rpc.rpc.RPC._rpc_balance', return_value={ - 'currencies': balances, - 'total': 100.0, - 'symbol': 100.0, - 'value': 1000.0, - 'starting_capital': 1000, - 'starting_capital_fiat': 1000, - }) - - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot) - - telegram._balance(update=update, context=MagicMock()) - assert msg_mock.call_count > 1 - # Test if wrap happens around 4000 - - # and each single currency-output is around 120 characters long so we need - # an offset to avoid random test failures - assert len(msg_mock.call_args_list[0][0][0]) < 4096 - assert len(msg_mock.call_args_list[0][0][0]) > (4096 - 120) - - -def test_start_handle(default_conf, update, mocker) -> None: - - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - - freqtradebot.state = State.STOPPED - assert freqtradebot.state == State.STOPPED - telegram._start(update=update, context=MagicMock()) - assert freqtradebot.state == State.RUNNING - assert msg_mock.call_count == 1 - - -def test_start_handle_already_running(default_conf, update, mocker) -> None: - - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - - freqtradebot.state = State.RUNNING - assert freqtradebot.state == State.RUNNING - telegram._start(update=update, context=MagicMock()) - assert freqtradebot.state == State.RUNNING - assert msg_mock.call_count == 1 - assert 'already running' in msg_mock.call_args_list[0][0][0] - - -def test_stop_handle(default_conf, update, mocker) -> None: - - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - - freqtradebot.state = State.RUNNING - assert freqtradebot.state == State.RUNNING - telegram._stop(update=update, context=MagicMock()) - assert freqtradebot.state == State.STOPPED - assert msg_mock.call_count == 1 - assert 'stopping trader' in msg_mock.call_args_list[0][0][0] - - -def test_stop_handle_already_stopped(default_conf, update, mocker) -> None: - - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - - freqtradebot.state = State.STOPPED - assert freqtradebot.state == State.STOPPED - telegram._stop(update=update, context=MagicMock()) - assert freqtradebot.state == State.STOPPED - assert msg_mock.call_count == 1 - assert 'already stopped' in msg_mock.call_args_list[0][0][0] - - -def test_stopbuy_handle(default_conf, update, mocker) -> None: - - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - - assert freqtradebot.config['max_open_trades'] != 0 - telegram._stopbuy(update=update, context=MagicMock()) - assert freqtradebot.config['max_open_trades'] == 0 - assert msg_mock.call_count == 1 - assert 'No more buy will occur from now. Run /reload_config to reset.' \ - in msg_mock.call_args_list[0][0][0] - - -def test_reload_config_handle(default_conf, update, mocker) -> None: - - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - - freqtradebot.state = State.RUNNING - assert freqtradebot.state == State.RUNNING - telegram._reload_config(update=update, context=MagicMock()) - assert freqtradebot.state == State.RELOAD_CONFIG - assert msg_mock.call_count == 1 - assert 'Reloading config' in msg_mock.call_args_list[0][0][0] - - -def test_telegram_forcesell_handle(default_conf, update, ticker, fee, - ticker_sell_up, mocker) -> None: - mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0) - msg_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock()) - mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock()) - patch_exchange(mocker) - patch_whitelist(mocker, default_conf) - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, - get_fee=fee, - _is_dry_limit_order_filled=MagicMock(return_value=True), - ) - - freqtradebot = FreqtradeBot(default_conf) - rpc = RPC(freqtradebot) - telegram = Telegram(rpc, default_conf) - patch_get_signal(freqtradebot) - - # Create some test data - freqtradebot.enter_positions() - - trade = Trade.query.first() - assert trade - - # Increase the price and sell it - mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', ticker_sell_up) - - # /forcesell 1 - context = MagicMock() - context.args = ["1"] - telegram._forceexit(update=update, context=context) - - assert msg_mock.call_count == 4 - last_msg = msg_mock.call_args_list[-2][0][0] - assert { - 'type': RPCMessageType.SELL, - 'trade_id': 1, - 'exchange': 'Binance', - 'pair': 'ETH/BTC', - 'gain': 'profit', - 'leverage': 1.0, - 'limit': 1.173e-05, - 'amount': 91.07468123, - 'order_type': 'limit', - 'open_rate': 1.098e-05, - 'current_rate': 1.173e-05, - 'direction': 'Long', - 'profit_amount': 6.314e-05, - 'profit_ratio': 0.0629778, - 'stake_currency': 'BTC', - 'base_currency': 'ETH', - 'fiat_currency': 'USD', - 'buy_tag': ANY, - 'enter_tag': ANY, - 'sell_reason': ExitType.FORCE_SELL.value, - 'exit_reason': ExitType.FORCE_SELL.value, - 'open_date': ANY, - 'close_date': ANY, - 'close_rate': ANY, - 'stake_amount': 0.0009999999999054, - 'sub_trade': False, - } == last_msg - - -def test_telegram_forcesell_down_handle(default_conf, update, ticker, fee, - ticker_sell_down, mocker) -> None: - mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter._find_price', - return_value=15000.0) - msg_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock()) - mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock()) - patch_exchange(mocker) - patch_whitelist(mocker, default_conf) - - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, - get_fee=fee, - _is_dry_limit_order_filled=MagicMock(return_value=True), - ) - - freqtradebot = FreqtradeBot(default_conf) - rpc = RPC(freqtradebot) - telegram = Telegram(rpc, default_conf) - patch_get_signal(freqtradebot) - - # Create some test data - freqtradebot.enter_positions() - - # Decrease the price and sell it - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=ticker_sell_down - ) - - trade = Trade.query.first() - assert trade - - # /forcesell 1 - context = MagicMock() - context.args = ["1"] - telegram._forceexit(update=update, context=context) - - assert msg_mock.call_count == 4 - - last_msg = msg_mock.call_args_list[-2][0][0] - assert { - 'type': RPCMessageType.SELL, - 'trade_id': 1, - 'exchange': 'Binance', - 'pair': 'ETH/BTC', - 'gain': 'loss', - 'leverage': 1.0, - 'limit': 1.043e-05, - 'amount': 91.07468123, - 'order_type': 'limit', - 'open_rate': 1.098e-05, - 'current_rate': 1.043e-05, - 'direction': 'Long', - 'profit_amount': -5.497e-05, - 'profit_ratio': -0.05482878, - 'stake_currency': 'BTC', - 'base_currency': 'ETH', - 'fiat_currency': 'USD', - 'buy_tag': ANY, - 'enter_tag': ANY, - 'sell_reason': ExitType.FORCE_SELL.value, - 'exit_reason': ExitType.FORCE_SELL.value, - 'open_date': ANY, - 'close_date': ANY, - 'close_rate': ANY, - 'stake_amount': 0.0009999999999054, - 'sub_trade': False - } == last_msg - - -def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None: - patch_exchange(mocker) - mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter._find_price', - return_value=15000.0) - msg_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock()) - mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock()) - patch_whitelist(mocker, default_conf) - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, - get_fee=fee, - _is_dry_limit_order_filled=MagicMock(return_value=True), - ) - default_conf['max_open_trades'] = 4 - freqtradebot = FreqtradeBot(default_conf) - rpc = RPC(freqtradebot) - telegram = Telegram(rpc, default_conf) - patch_get_signal(freqtradebot) - - # Create some test data - freqtradebot.enter_positions() - msg_mock.reset_mock() - - # /forcesell all - context = MagicMock() - context.args = ["all"] - telegram._forceexit(update=update, context=context) - - # Called for each trade 2 times - assert msg_mock.call_count == 8 - msg = msg_mock.call_args_list[0][0][0] - assert { - 'type': RPCMessageType.SELL, - 'trade_id': 1, - 'exchange': 'Binance', - 'pair': 'ETH/BTC', - 'gain': 'loss', - 'leverage': 1.0, - 'limit': 1.099e-05, - 'amount': 91.07468123, - 'order_type': 'limit', - 'open_rate': 1.098e-05, - 'current_rate': 1.099e-05, - 'direction': 'Long', - 'profit_amount': -4.09e-06, - 'profit_ratio': -0.00408133, - 'stake_currency': 'BTC', - 'base_currency': 'ETH', - 'fiat_currency': 'USD', - 'buy_tag': ANY, - 'enter_tag': ANY, - 'sell_reason': ExitType.FORCE_SELL.value, - 'exit_reason': ExitType.FORCE_SELL.value, - 'open_date': ANY, - 'close_date': ANY, - 'close_rate': ANY, - 'stake_amount': 0.0009999999999054, - 'sub_trade': False - } == msg - - -def test_forcesell_handle_invalid(default_conf, update, mocker) -> None: - mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter._find_price', - return_value=15000.0) - - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot) - - # Trader is not running - freqtradebot.state = State.STOPPED - # /forcesell 1 - context = MagicMock() - context.args = ["1"] - telegram._forceexit(update=update, context=context) - assert msg_mock.call_count == 1 - assert 'not running' in msg_mock.call_args_list[0][0][0] - - # No argument - msg_mock.reset_mock() - freqtradebot.state = State.RUNNING - context = MagicMock() - context.args = [] - telegram._forceexit(update=update, context=context) - assert msg_mock.call_count == 1 - assert "You must specify a trade-id or 'all'." in msg_mock.call_args_list[0][0][0] - - # Invalid argument - msg_mock.reset_mock() - freqtradebot.state = State.RUNNING - # /forcesell 123456 - context = MagicMock() - context.args = ["123456"] - telegram._forceexit(update=update, context=context) - assert msg_mock.call_count == 1 - assert 'invalid argument' in msg_mock.call_args_list[0][0][0] - - -def test_forceenter_handle(default_conf, update, mocker) -> None: - mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0) - - fbuy_mock = MagicMock(return_value=None) - mocker.patch('freqtrade.rpc.RPC._rpc_force_entry', fbuy_mock) - - telegram, freqtradebot, _ = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot) - - # /forcelong ETH/BTC - context = MagicMock() - context.args = ["ETH/BTC"] - telegram._forceenter(update=update, context=context, order_side=SignalDirection.LONG) - - assert fbuy_mock.call_count == 1 - assert fbuy_mock.call_args_list[0][0][0] == 'ETH/BTC' - assert fbuy_mock.call_args_list[0][0][1] is None - assert fbuy_mock.call_args_list[0][1]['order_side'] == SignalDirection.LONG - - # Reset and retry with specified price - fbuy_mock = MagicMock(return_value=None) - mocker.patch('freqtrade.rpc.RPC._rpc_force_entry', fbuy_mock) - # /forcelong ETH/BTC 0.055 - context = MagicMock() - context.args = ["ETH/BTC", "0.055"] - telegram._forceenter(update=update, context=context, order_side=SignalDirection.LONG) - - assert fbuy_mock.call_count == 1 - assert fbuy_mock.call_args_list[0][0][0] == 'ETH/BTC' - assert isinstance(fbuy_mock.call_args_list[0][0][1], float) - assert fbuy_mock.call_args_list[0][0][1] == 0.055 - - -def test_forceenter_handle_exception(default_conf, update, mocker) -> None: - mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0) - - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot) - - update.message.text = '/forcebuy ETH/Nonepair' - telegram._forceenter(update=update, context=MagicMock(), order_side=SignalDirection.LONG) - - assert msg_mock.call_count == 1 - assert msg_mock.call_args_list[0][0][0] == 'Forceentry not enabled.' - - -def test_forceenter_no_pair(default_conf, update, mocker) -> None: - mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0) - - fbuy_mock = MagicMock(return_value=None) - mocker.patch('freqtrade.rpc.RPC._rpc_force_entry', fbuy_mock) - - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - - patch_get_signal(freqtradebot) - - context = MagicMock() - context.args = [] - telegram._forceenter(update=update, context=context, order_side=SignalDirection.LONG) - - assert fbuy_mock.call_count == 0 - assert msg_mock.call_count == 1 - assert msg_mock.call_args_list[0][1]['msg'] == 'Which pair?' - # assert msg_mock.call_args_list[0][1]['callback_query_handler'] == 'forcebuy' - keyboard = msg_mock.call_args_list[0][1]['keyboard'] - # One additional button - cancel - assert reduce(lambda acc, x: acc + len(x), keyboard, 0) == 5 - update = MagicMock() - update.callback_query = MagicMock() - update.callback_query.data = 'XRP/USDT_||_long' - telegram._forceenter_inline(update, None) - assert fbuy_mock.call_count == 1 - - -def test_telegram_performance_handle(default_conf, update, ticker, fee, - limit_buy_order, limit_sell_order, mocker) -> None: - - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, - get_fee=fee, - ) - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot) - - # Create some test data - freqtradebot.enter_positions() - trade = Trade.query.first() - assert trade - - # Simulate fulfilled LIMIT_BUY order for trade - limit_buy_order['id'] = trade.orders[0].order_id - oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy') - trade.update_order(limit_buy_order) - trade.update_trade(oobj) - - # Simulate fulfilled LIMIT_SELL order for trade - oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell') - trade.update_trade(oobj) - - trade.close_date = datetime.utcnow() - trade.is_open = False - telegram._performance(update=update, context=MagicMock()) - assert msg_mock.call_count == 1 - assert 'Performance' in msg_mock.call_args_list[0][0][0] - assert 'ETH/BTC\t0.00006217 BTC (6.20%) (1)' in msg_mock.call_args_list[0][0][0] - - -def test_telegram_entry_tag_performance_handle( - default_conf, update, ticker, fee, limit_buy_order, limit_sell_order, mocker) -> None: - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, - get_fee=fee, - ) - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot) - - # Create some test data - freqtradebot.enter_positions() - trade = Trade.query.first() - assert trade - - # Simulate fulfilled LIMIT_BUY order for trade - limit_buy_order['id'] = trade.orders[0].order_id - oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy') - trade.update_order(limit_buy_order) - trade.update_trade(oobj) - - trade.enter_tag = "TESTBUY" - # Simulate fulfilled LIMIT_SELL order for trade - oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell') - trade.update_trade(oobj) - - trade.close_date = datetime.utcnow() - trade.is_open = False - context = MagicMock() - telegram._enter_tag_performance(update=update, context=context) - assert msg_mock.call_count == 1 - assert 'Entry Tag Performance' in msg_mock.call_args_list[0][0][0] - assert 'TESTBUY\t0.00006217 BTC (6.20%) (1)' in msg_mock.call_args_list[0][0][0] - - context.args = [trade.pair] - telegram._enter_tag_performance(update=update, context=context) - assert msg_mock.call_count == 2 - - msg_mock.reset_mock() - mocker.patch('freqtrade.rpc.rpc.RPC._rpc_enter_tag_performance', - side_effect=RPCException('Error')) - telegram._enter_tag_performance(update=update, context=MagicMock()) - - assert msg_mock.call_count == 1 - assert "Error" in msg_mock.call_args_list[0][0][0] - - -def test_telegram_exit_reason_performance_handle(default_conf, update, ticker, fee, - limit_buy_order, limit_sell_order, mocker) -> None: - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, - get_fee=fee, - ) - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot) - - # Create some test data - freqtradebot.enter_positions() - trade = Trade.query.first() - assert trade - trade.exit_reason = 'TESTSELL' - # Simulate fulfilled LIMIT_BUY order for trade - limit_buy_order['id'] = trade.orders[0].order_id - oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy') - trade.update_order(limit_buy_order) - trade.update_trade(oobj) - - # Simulate fulfilled LIMIT_SELL order for trade - oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell') - trade.update_trade(oobj) - - trade.close_date = datetime.utcnow() - trade.is_open = False - context = MagicMock() - telegram._exit_reason_performance(update=update, context=context) - assert msg_mock.call_count == 1 - assert 'Exit Reason Performance' in msg_mock.call_args_list[0][0][0] - assert 'TESTSELL\t0.00006217 BTC (6.20%) (1)' in msg_mock.call_args_list[0][0][0] - context.args = [trade.pair] - - telegram._exit_reason_performance(update=update, context=context) - assert msg_mock.call_count == 2 - - msg_mock.reset_mock() - mocker.patch('freqtrade.rpc.rpc.RPC._rpc_exit_reason_performance', - side_effect=RPCException('Error')) - telegram._exit_reason_performance(update=update, context=MagicMock()) - - assert msg_mock.call_count == 1 - assert "Error" in msg_mock.call_args_list[0][0][0] - - -def test_telegram_mix_tag_performance_handle(default_conf, update, ticker, fee, - limit_buy_order, limit_sell_order, mocker) -> None: - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, - get_fee=fee, - ) - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot) - - # Create some test data - freqtradebot.enter_positions() - trade = Trade.query.first() - assert trade - - trade.enter_tag = "TESTBUY" - trade.exit_reason = "TESTSELL" - - # Simulate fulfilled LIMIT_BUY order for trade - limit_buy_order['id'] = trade.orders[0].order_id - oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy') - trade.update_order(limit_buy_order) - trade.update_trade(oobj) - - # Simulate fulfilled LIMIT_SELL order for trade - oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell') - trade.update_trade(oobj) - - trade.close_date = datetime.utcnow() - trade.is_open = False - - context = MagicMock() - telegram._mix_tag_performance(update=update, context=context) - assert msg_mock.call_count == 1 - assert 'Mix Tag Performance' in msg_mock.call_args_list[0][0][0] - assert ('TESTBUY TESTSELL\t0.00006217 BTC (6.20%) (1)' - in msg_mock.call_args_list[0][0][0]) - - context.args = [trade.pair] - telegram._mix_tag_performance(update=update, context=context) - assert msg_mock.call_count == 2 - - msg_mock.reset_mock() - mocker.patch('freqtrade.rpc.rpc.RPC._rpc_mix_tag_performance', - side_effect=RPCException('Error')) - telegram._mix_tag_performance(update=update, context=MagicMock()) - - assert msg_mock.call_count == 1 - assert "Error" in msg_mock.call_args_list[0][0][0] - - -def test_count_handle(default_conf, update, ticker, fee, mocker) -> None: - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, - get_fee=fee, - ) - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot) - - freqtradebot.state = State.STOPPED - telegram._count(update=update, context=MagicMock()) - assert msg_mock.call_count == 1 - assert 'not running' in msg_mock.call_args_list[0][0][0] - msg_mock.reset_mock() - freqtradebot.state = State.RUNNING - - # Create some test data - freqtradebot.enter_positions() - msg_mock.reset_mock() - telegram._count(update=update, context=MagicMock()) - - msg = ('
  current    max    total stake\n---------  -----  -------------\n'
-           '        1      {}          {}
').format( - default_conf['max_open_trades'], - default_conf['stake_amount'] - ) - assert msg in msg_mock.call_args_list[0][0][0] - - -def test_telegram_lock_handle(default_conf, update, ticker, fee, mocker) -> None: - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, - get_fee=fee, - ) - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - patch_get_signal(freqtradebot) - telegram._locks(update=update, context=MagicMock()) - assert msg_mock.call_count == 1 - assert 'No active locks.' in msg_mock.call_args_list[0][0][0] - - msg_mock.reset_mock() - - PairLocks.lock_pair('ETH/BTC', arrow.utcnow().shift(minutes=4).datetime, 'randreason') - PairLocks.lock_pair('XRP/BTC', arrow.utcnow().shift(minutes=20).datetime, 'deadbeef') - - telegram._locks(update=update, context=MagicMock()) - - assert 'Pair' in msg_mock.call_args_list[0][0][0] - assert 'Until' in msg_mock.call_args_list[0][0][0] - assert 'Reason\n' in msg_mock.call_args_list[0][0][0] - assert 'ETH/BTC' in msg_mock.call_args_list[0][0][0] - assert 'XRP/BTC' in msg_mock.call_args_list[0][0][0] - assert 'deadbeef' in msg_mock.call_args_list[0][0][0] - assert 'randreason' in msg_mock.call_args_list[0][0][0] - - context = MagicMock() - context.args = ['XRP/BTC'] - msg_mock.reset_mock() - telegram._delete_locks(update=update, context=context) - - assert 'ETH/BTC' in msg_mock.call_args_list[0][0][0] - assert 'randreason' in msg_mock.call_args_list[0][0][0] - assert 'XRP/BTC' not in msg_mock.call_args_list[0][0][0] - assert 'deadbeef' not in msg_mock.call_args_list[0][0][0] - - -def test_whitelist_static(default_conf, update, mocker) -> None: - - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - - telegram._whitelist(update=update, context=MagicMock()) - assert msg_mock.call_count == 1 - assert ("Using whitelist `['StaticPairList']` with 4 pairs\n" - "`ETH/BTC, LTC/BTC, XRP/BTC, NEO/BTC`" in msg_mock.call_args_list[0][0][0]) - - -def test_whitelist_dynamic(default_conf, update, mocker) -> None: - mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True)) - default_conf['pairlists'] = [{'method': 'VolumePairList', - 'number_assets': 4 - }] - telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf) - - telegram._whitelist(update=update, context=MagicMock()) - assert msg_mock.call_count == 1 - assert ("Using whitelist `['VolumePairList']` with 4 pairs\n" - "`ETH/BTC, LTC/BTC, XRP/BTC, NEO/BTC`" in msg_mock.call_args_list[0][0][0]) - - -def test_blacklist_static(default_conf, update, mocker) -> None: - - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) - - telegram._blacklist(update=update, context=MagicMock()) - assert msg_mock.call_count == 1 - assert ("Blacklist contains 2 pairs\n`DOGE/BTC, HOT/BTC`" - in msg_mock.call_args_list[0][0][0]) - - msg_mock.reset_mock() - - # /blacklist ETH/BTC - context = MagicMock() - context.args = ["ETH/BTC"] - telegram._blacklist(update=update, context=context) - assert msg_mock.call_count == 1 - assert ("Blacklist contains 3 pairs\n`DOGE/BTC, HOT/BTC, ETH/BTC`" - in msg_mock.call_args_list[0][0][0]) - assert freqtradebot.pairlists.blacklist == ["DOGE/BTC", "HOT/BTC", "ETH/BTC"] - - msg_mock.reset_mock() - context = MagicMock() - context.args = ["XRP/.*"] - telegram._blacklist(update=update, context=context) - assert msg_mock.call_count == 1 - - assert ("Blacklist contains 4 pairs\n`DOGE/BTC, HOT/BTC, ETH/BTC, XRP/.*`" - in msg_mock.call_args_list[0][0][0]) - assert freqtradebot.pairlists.blacklist == ["DOGE/BTC", "HOT/BTC", "ETH/BTC", "XRP/.*"] - - msg_mock.reset_mock() - context.args = ["DOGE/BTC"] - telegram._blacklist_delete(update=update, context=context) - assert msg_mock.call_count == 1 - assert ("Blacklist contains 3 pairs\n`HOT/BTC, ETH/BTC, XRP/.*`" - in msg_mock.call_args_list[0][0][0]) - - -def test_telegram_logs(default_conf, update, mocker) -> None: - mocker.patch.multiple( - 'freqtrade.rpc.telegram.Telegram', - _init=MagicMock(), - ) - setup_logging(default_conf) - - telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf) - - context = MagicMock() - context.args = [] - telegram._logs(update=update, context=context) - assert msg_mock.call_count == 1 - assert "freqtrade\\.rpc\\.telegram" in msg_mock.call_args_list[0][0][0] - - msg_mock.reset_mock() - context.args = ["1"] - telegram._logs(update=update, context=context) - assert msg_mock.call_count == 1 - - msg_mock.reset_mock() - # Test with changed MaxMessageLength - mocker.patch('freqtrade.rpc.telegram.MAX_TELEGRAM_MESSAGE_LENGTH', 200) - context = MagicMock() - context.args = [] - telegram._logs(update=update, context=context) - # Called at least 2 times. Exact times will change with unrelated changes to setup messages - # Therefore we don't test for this explicitly. - assert msg_mock.call_count >= 2 - - -def test_edge_disabled(default_conf, update, mocker) -> None: - - telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf) - - telegram._edge(update=update, context=MagicMock()) - assert msg_mock.call_count == 1 - assert "Edge is not enabled." in msg_mock.call_args_list[0][0][0] - - -def test_edge_enabled(edge_conf, update, mocker) -> None: - mocker.patch('freqtrade.edge.Edge._cached_pairs', mocker.PropertyMock( - return_value={ - 'E/F': PairInfo(-0.01, 0.66, 3.71, 0.50, 1.71, 10, 60), - } - )) - - telegram, _, msg_mock = get_telegram_testobject(mocker, edge_conf) - - telegram._edge(update=update, context=MagicMock()) - assert msg_mock.call_count == 1 - assert 'Edge only validated following pairs:\n
' in msg_mock.call_args_list[0][0][0]
-    assert 'Pair      Winrate    Expectancy    Stoploss' in msg_mock.call_args_list[0][0][0]
-
-    msg_mock.reset_mock()
-
-    mocker.patch('freqtrade.edge.Edge._cached_pairs', mocker.PropertyMock(
-        return_value={}))
-    telegram._edge(update=update, context=MagicMock())
-    assert msg_mock.call_count == 1
-    assert 'Edge only validated following pairs:' in msg_mock.call_args_list[0][0][0]
-    assert 'Winrate' not in msg_mock.call_args_list[0][0][0]
-
-
-@pytest.mark.parametrize('is_short,regex_pattern',
-                         [(True, r"just now[ ]*XRP\/BTC \(#3\)  -1.00% \("),
-                          (False, r"just now[ ]*XRP\/BTC \(#3\)  1.00% \(")])
-def test_telegram_trades(mocker, update, default_conf, fee, is_short, regex_pattern):
-
-    telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
-
-    context = MagicMock()
-    context.args = []
-
-    telegram._trades(update=update, context=context)
-    assert "0 recent trades:" in msg_mock.call_args_list[0][0][0]
-    assert "
" not in msg_mock.call_args_list[0][0][0]
-    msg_mock.reset_mock()
-
-    context.args = ['hello']
-    telegram._trades(update=update, context=context)
-    assert "0 recent trades:" in msg_mock.call_args_list[0][0][0]
-    assert "
" not in msg_mock.call_args_list[0][0][0]
-    msg_mock.reset_mock()
-
-    create_mock_trades(fee, is_short=is_short)
-
-    context = MagicMock()
-    context.args = [5]
-    telegram._trades(update=update, context=context)
-    msg_mock.call_count == 1
-    assert "2 recent trades:" in msg_mock.call_args_list[0][0][0]
-    assert "Profit (" in msg_mock.call_args_list[0][0][0]
-    assert "Close Date" in msg_mock.call_args_list[0][0][0]
-    assert "
" in msg_mock.call_args_list[0][0][0]
-    assert bool(re.search(regex_pattern, msg_mock.call_args_list[0][0][0]))
-
-
-@pytest.mark.parametrize('is_short', [True, False])
-def test_telegram_delete_trade(mocker, update, default_conf, fee, is_short):
-
-    telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
-    context = MagicMock()
-    context.args = []
-
-    telegram._delete_trade(update=update, context=context)
-    assert "Trade-id not set." in msg_mock.call_args_list[0][0][0]
-
-    msg_mock.reset_mock()
-    create_mock_trades(fee, is_short=is_short)
-
-    context = MagicMock()
-    context.args = [1]
-    telegram._delete_trade(update=update, context=context)
-    msg_mock.call_count == 1
-    assert "Deleted trade 1." in msg_mock.call_args_list[0][0][0]
-    assert "Please make sure to take care of this asset" in msg_mock.call_args_list[0][0][0]
-
-
-def test_help_handle(default_conf, update, mocker) -> None:
-    telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
-
-    telegram._help(update=update, context=MagicMock())
-    assert msg_mock.call_count == 1
-    assert '*/help:* `This help message`' in msg_mock.call_args_list[0][0][0]
-
-
-def test_version_handle(default_conf, update, mocker) -> None:
-
-    telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
-
-    telegram._version(update=update, context=MagicMock())
-    assert msg_mock.call_count == 1
-    assert '*Version:* `{}`'.format(__version__) in msg_mock.call_args_list[0][0][0]
-
-    msg_mock.reset_mock()
-    freqtradebot.strategy.version = lambda: '1.1.1'
-
-    telegram._version(update=update, context=MagicMock())
-    assert msg_mock.call_count == 1
-    assert '*Version:* `{}`'.format(__version__) in msg_mock.call_args_list[0][0][0]
-    assert '*Strategy version: * `1.1.1`' in msg_mock.call_args_list[0][0][0]
-
-
-def test_show_config_handle(default_conf, update, mocker) -> None:
-
-    default_conf['runmode'] = RunMode.DRY_RUN
-
-    telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
-
-    telegram._show_config(update=update, context=MagicMock())
-    assert msg_mock.call_count == 1
-    assert '*Mode:* `{}`'.format('Dry-run') in msg_mock.call_args_list[0][0][0]
-    assert '*Exchange:* `binance`' in msg_mock.call_args_list[0][0][0]
-    assert f'*Strategy:* `{CURRENT_TEST_STRATEGY}`' in msg_mock.call_args_list[0][0][0]
-    assert '*Stoploss:* `-0.1`' in msg_mock.call_args_list[0][0][0]
-
-    msg_mock.reset_mock()
-    freqtradebot.config['trailing_stop'] = True
-    telegram._show_config(update=update, context=MagicMock())
-    assert msg_mock.call_count == 1
-    assert '*Mode:* `{}`'.format('Dry-run') in msg_mock.call_args_list[0][0][0]
-    assert '*Exchange:* `binance`' in msg_mock.call_args_list[0][0][0]
-    assert f'*Strategy:* `{CURRENT_TEST_STRATEGY}`' in msg_mock.call_args_list[0][0][0]
-    assert '*Initial Stoploss:* `-0.1`' in msg_mock.call_args_list[0][0][0]
-
-
-@pytest.mark.parametrize('message_type,enter,enter_signal,leverage', [
-    (RPCMessageType.BUY, 'Long', 'long_signal_01', None),
-    (RPCMessageType.BUY, 'Long', 'long_signal_01', 1.0),
-    (RPCMessageType.BUY, 'Long', 'long_signal_01', 5.0),
-    (RPCMessageType.SHORT, 'Short', 'short_signal_01', 2.0)])
-def test_send_msg_buy_notification(default_conf, mocker, caplog, message_type,
-                                   enter, enter_signal, leverage) -> None:
-
-    msg = {
-        'type': message_type,
-        'trade_id': 1,
-        'enter_tag': enter_signal,
-        'exchange': 'Binance',
-        'pair': 'ETH/BTC',
-        'leverage': leverage,
-        'limit': 1.099e-05,
-        'order_type': 'limit',
-        'stake_amount': 0.01465333,
-        'stake_amount_fiat': 0.0,
-        'stake_currency': 'BTC',
-        'fiat_currency': 'USD',
-        'current_rate': 1.099e-05,
-        'amount': 1333.3333333333335,
-        'open_date': arrow.utcnow().shift(hours=-1)
-    }
-    telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
-
-    telegram.send_msg(msg)
-    leverage_text = f'*Leverage:* `{leverage}`\n' if leverage and leverage != 1.0 else ''
-
-    assert msg_mock.call_args[0][0] == (
-        f'\N{LARGE BLUE CIRCLE} *Binance:* {enter} ETH/BTC (#1)\n'
-        f'*Enter Tag:* `{enter_signal}`\n'
-        '*Amount:* `1333.33333333`\n'
-        f'{leverage_text}'
-        '*Open Rate:* `0.00001099`\n'
-        '*Current Rate:* `0.00001099`\n'
-        '*Total:* `(0.01465333 BTC, 180.895 USD)`'
-    )
-
-    freqtradebot.config['telegram']['notification_settings'] = {'buy': 'off'}
-    caplog.clear()
-    msg_mock.reset_mock()
-    telegram.send_msg(msg)
-    msg_mock.call_count == 0
-    log_has("Notification 'buy' not sent.", caplog)
-
-    freqtradebot.config['telegram']['notification_settings'] = {'buy': 'silent'}
-    caplog.clear()
-    msg_mock.reset_mock()
-
-    telegram.send_msg(msg)
-    msg_mock.call_count == 1
-    msg_mock.call_args_list[0][1]['disable_notification'] is True
-
-
-@pytest.mark.parametrize('message_type,enter_signal', [
-    (RPCMessageType.BUY_CANCEL, 'long_signal_01'),
-    (RPCMessageType.SHORT_CANCEL, 'short_signal_01')])
-def test_send_msg_buy_cancel_notification(default_conf, mocker, message_type, enter_signal) -> None:
-
-    telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
-
-    telegram.send_msg({
-        'type': message_type,
-        'enter_tag': enter_signal,
-        'trade_id': 1,
-        'exchange': 'Binance',
-        'pair': 'ETH/BTC',
-        'reason': CANCEL_REASON['TIMEOUT']
-    })
-    assert (msg_mock.call_args[0][0] == '\N{WARNING SIGN} *Binance:* '
-            'Cancelling enter Order for ETH/BTC (#1). '
-            'Reason: cancelled due to timeout.')
-
-
-def test_send_msg_protection_notification(default_conf, mocker, time_machine) -> None:
-
-    default_conf['telegram']['notification_settings']['protection_trigger'] = 'on'
-
-    telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
-    time_machine.move_to("2021-09-01 05:00:00 +00:00")
-    lock = PairLocks.lock_pair('ETH/BTC', arrow.utcnow().shift(minutes=6).datetime, 'randreason')
-    msg = {
-        'type': RPCMessageType.PROTECTION_TRIGGER,
-    }
-    msg.update(lock.to_json())
-    telegram.send_msg(msg)
-    assert (msg_mock.call_args[0][0] == "*Protection* triggered due to randreason. "
-            "`ETH/BTC` will be locked until `2021-09-01 05:10:00`.")
-
-    msg_mock.reset_mock()
-    # Test global protection
-
-    msg = {
-        'type': RPCMessageType.PROTECTION_TRIGGER_GLOBAL,
-    }
-    lock = PairLocks.lock_pair('*', arrow.utcnow().shift(minutes=100).datetime, 'randreason')
-    msg.update(lock.to_json())
-    telegram.send_msg(msg)
-    assert (msg_mock.call_args[0][0] == "*Protection* triggered due to randreason. "
-            "*All pairs* will be locked until `2021-09-01 06:45:00`.")
-
-
-@pytest.mark.parametrize('message_type,entered,enter_signal,leverage', [
-    (RPCMessageType.BUY_FILL, 'Longed', 'long_signal_01', 1.0),
-    (RPCMessageType.BUY_FILL, 'Longed', 'long_signal_02', 2.0),
-    (RPCMessageType.SHORT_FILL, 'Shorted', 'short_signal_01', 2.0),
-])
-def test_send_msg_buy_fill_notification(default_conf, mocker, message_type, entered,
-                                        enter_signal, leverage) -> None:
-
-    default_conf['telegram']['notification_settings']['buy_fill'] = 'on'
-    telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
-
-    telegram.send_msg({
-        'type': message_type,
-        'trade_id': 1,
-        'enter_tag': enter_signal,
-        'exchange': 'Binance',
-        'pair': 'ETH/BTC',
-        'leverage': leverage,
-        'stake_amount': 0.01465333,
-        'stake_currency': 'BTC',
-        'fiat_currency': 'USD',
-        'open_rate': 1.099e-05,
-        'amount': 1333.3333333333335,
-        'open_date': arrow.utcnow().shift(hours=-1)
-    })
-    leverage_text = f'*Leverage:* `{leverage}`\n' if leverage != 1.0 else ''
-    assert msg_mock.call_args[0][0] == (
-        f'\N{CHECK MARK} *Binance:* {entered} ETH/BTC (#1)\n'
-        f'*Enter Tag:* `{enter_signal}`\n'
-        '*Amount:* `1333.33333333`\n'
-        f"{leverage_text}"
-        '*Open Rate:* `0.00001099`\n'
-        '*Total:* `(0.01465333 BTC, 180.895 USD)`'
-    )
-
-    msg_mock.reset_mock()
-    telegram.send_msg({
-        'type': RPCMessageType.BUY_FILL,
-        'trade_id': 1,
-        'buy_tag': 'buy_signal_01',
-        'exchange': 'Binance',
-        'pair': 'ETH/BTC',
-        'stake_amount': 0.01465333,
-        'sub_trade': True,
-
-
-        'stake_currency': 'BTC',
-        'fiat_currency': 'USD',
-        'open_rate': 1.099e-05,
-        'amount': 1333.3333333333335,
-        'open_date': arrow.utcnow().shift(hours=-1)
-    })
-
-    assert msg_mock.call_args[0][0] \
-        == '\N{CHECK MARK} *Binance:* Bought ETH/BTC (#1)\n' \
-           '*Buy Tag:* `buy_signal_01`\n' \
-           '*Amount:* `1333.33333333`\n' \
-           '*Open Rate:* `0.00001099`\n' \
-           '*Total:* `(0.01465333 BTC, 180.895 USD)`\n' \
-           '*Balance:* `(0.01465333 BTC, 180.895 USD)`'
-
-
-def test_send_msg_sell_notification(default_conf, mocker) -> None:
-
-    telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
-
-    old_convamount = telegram._rpc._fiat_converter.convert_amount
-    telegram._rpc._fiat_converter.convert_amount = lambda a, b, c: -24.812
-    telegram.send_msg({
-        'type': RPCMessageType.SELL,
-        'trade_id': 1,
-        'exchange': 'Binance',
-        'pair': 'KEY/ETH',
-        'leverage': 1.0,
-        'direction': 'Long',
-        'gain': 'loss',
-        'limit': 3.201e-05,
-        'amount': 1333.3333333333335,
-        'order_type': 'market',
-        'open_rate': 7.5e-05,
-        'current_rate': 3.201e-05,
-        'profit_amount': -0.05746268,
-        'profit_ratio': -0.57405275,
-        'stake_currency': 'ETH',
-        'fiat_currency': 'USD',
-        'enter_tag': 'buy_signal1',
-        'exit_reason': ExitType.STOP_LOSS.value,
-        'open_date': arrow.utcnow().shift(hours=-1),
-        'close_date': arrow.utcnow(),
-    })
-<<<<<<< HEAD
-    assert msg_mock.call_args[0][0] \
-        == ('\N{WARNING SIGN} *Binance:* Selling KEY/ETH (#1)\n'
-            '*Unrealized Profit:* `-57.41% (loss: -0.05746268 ETH / -24.812 USD)`\n'
-            '*Buy Tag:* `buy_signal1`\n'
-            '*Sell Reason:* `stop_loss`\n'
-            '*Amount:* `1333.33333333`\n'
-            '*Open Rate:* `0.00007500`\n'
-            '*Current Rate:* `0.00003201`\n'
-            '*Close Rate:* `0.00003201`\n'
-            '*Duration:* `1:00:00 (60.0 min)`'
-            )
-
-    msg_mock.reset_mock()
-    telegram.send_msg({
-        'type': RPCMessageType.SELL,
-        'trade_id': 1,
-        'exchange': 'Binance',
-        'pair': 'KEY/ETH',
-        'gain': 'loss',
-        'limit': 3.201e-05,
-        'amount': 1333.3333333333335,
-        'order_type': 'market',
-        'open_rate': 7.5e-05,
-        'current_rate': 3.201e-05,
-        'cumulative_profit': -0.05746268,
-        'profit_amount': -0.05746268,
-        'profit_ratio': -0.57405275,
-        'stake_currency': 'ETH',
-        'fiat_currency': 'USD',
-        'buy_tag': 'buy_signal1',
-        'sell_reason': SellType.STOP_LOSS.value,
-        'open_date': arrow.utcnow().shift(days=-1, hours=-2, minutes=-30),
-        'close_date': arrow.utcnow(),
-        'stake_amount': 1234,
-        'sub_trade': True
-    })
-    assert msg_mock.call_args[0][0] \
-        == ('\N{WARNING SIGN} *Binance:* Selling KEY/ETH (#1)\n'
-            '*Unrealized Cumulative Profit:* `-57.41% (loss: -0.05746268 ETH / -24.812 USD)`\n'
-            '*Buy Tag:* `buy_signal1`\n'
-            '*Sell Reason:* `stop_loss`\n'
-            '*Amount:* `1333.33333333`\n'
-            '*Open Rate:* `0.00007500`\n'
-            '*Current Rate:* `0.00003201`\n'
-            '*Close Rate:* `0.00003201`\n'
-            '*Remaining:* `(1234 ETH, -24.812 USD)`'
-            )
-=======
-    assert msg_mock.call_args[0][0] == (
-        '\N{WARNING SIGN} *Binance:* Exiting KEY/ETH (#1)\n'
-        '*Unrealized Profit:* `-57.41% (loss: -0.05746268 ETH / -24.812 USD)`\n'
-        '*Enter Tag:* `buy_signal1`\n'
-        '*Exit Reason:* `stop_loss`\n'
-        '*Duration:* `1:00:00 (60.0 min)`\n'
-        '*Direction:* `Long`\n'
-        '*Amount:* `1333.33333333`\n'
-        '*Open Rate:* `0.00007500`\n'
-        '*Current Rate:* `0.00003201`\n'
-        '*Close Rate:* `0.00003201`'
-    )
->>>>>>> develop
-
-    msg_mock.reset_mock()
-    telegram.send_msg({
-        'type': RPCMessageType.SELL,
-        'trade_id': 1,
-        'exchange': 'Binance',
-        'pair': 'KEY/ETH',
-        'direction': 'Long',
-        'gain': 'loss',
-        'limit': 3.201e-05,
-        'amount': 1333.3333333333335,
-        'order_type': 'market',
-        'open_rate': 7.5e-05,
-        'current_rate': 3.201e-05,
-        'profit_amount': -0.05746268,
-        'profit_ratio': -0.57405275,
-        'stake_currency': 'ETH',
-        'enter_tag': 'buy_signal1',
-        'exit_reason': ExitType.STOP_LOSS.value,
-        'open_date': arrow.utcnow().shift(days=-1, hours=-2, minutes=-30),
-        'close_date': arrow.utcnow(),
-    })
-<<<<<<< HEAD
-    assert msg_mock.call_args[0][0] \
-        == ('\N{WARNING SIGN} *Binance:* Selling KEY/ETH (#1)\n'
-            '*Unrealized Profit:* `-57.41% (loss: -0.05746268 ETH)`\n'
-            '*Buy Tag:* `buy_signal1`\n'
-            '*Sell Reason:* `stop_loss`\n'
-            '*Amount:* `1333.33333333`\n'
-            '*Open Rate:* `0.00007500`\n'
-            '*Current Rate:* `0.00003201`\n'
-            '*Close Rate:* `0.00003201`\n'
-            '*Duration:* `1 day, 2:30:00 (1590.0 min)`'
-            )
-=======
-    assert msg_mock.call_args[0][0] == (
-        '\N{WARNING SIGN} *Binance:* Exiting KEY/ETH (#1)\n'
-        '*Unrealized Profit:* `-57.41%`\n'
-        '*Enter Tag:* `buy_signal1`\n'
-        '*Exit Reason:* `stop_loss`\n'
-        '*Duration:* `1 day, 2:30:00 (1590.0 min)`\n'
-        '*Direction:* `Long`\n'
-        '*Amount:* `1333.33333333`\n'
-        '*Open Rate:* `0.00007500`\n'
-        '*Current Rate:* `0.00003201`\n'
-        '*Close Rate:* `0.00003201`'
-    )
->>>>>>> develop
-    # Reset singleton function to avoid random breaks
-    telegram._rpc._fiat_converter.convert_amount = old_convamount
-
-
-def test_send_msg_sell_cancel_notification(default_conf, mocker) -> None:
-
-    telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
-
-    old_convamount = telegram._rpc._fiat_converter.convert_amount
-    telegram._rpc._fiat_converter.convert_amount = lambda a, b, c: -24.812
-    telegram.send_msg({
-        'type': RPCMessageType.SELL_CANCEL,
-        'trade_id': 1,
-        'exchange': 'Binance',
-        'pair': 'KEY/ETH',
-        'reason': 'Cancelled on exchange'
-    })
-    assert msg_mock.call_args[0][0] == (
-        '\N{WARNING SIGN} *Binance:* Cancelling exit Order for KEY/ETH (#1).'
-        ' Reason: Cancelled on exchange.')
-
-    msg_mock.reset_mock()
-    telegram.send_msg({
-        'type': RPCMessageType.SELL_CANCEL,
-        'trade_id': 1,
-        'exchange': 'Binance',
-        'pair': 'KEY/ETH',
-        'reason': 'timeout'
-    })
-    assert msg_mock.call_args[0][0] == (
-        '\N{WARNING SIGN} *Binance:* Cancelling exit Order for KEY/ETH (#1). Reason: timeout.')
-    # Reset singleton function to avoid random breaks
-    telegram._rpc._fiat_converter.convert_amount = old_convamount
-
-
-@pytest.mark.parametrize('direction,enter_signal,leverage', [
-    ('Long', 'long_signal_01', None),
-    ('Long', 'long_signal_01', 1.0),
-    ('Long', 'long_signal_01', 5.0),
-    ('Short', 'short_signal_01', 2.0)])
-def test_send_msg_sell_fill_notification(default_conf, mocker, direction,
-                                         enter_signal, leverage) -> None:
-
-    default_conf['telegram']['notification_settings']['sell_fill'] = 'on'
-    telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
-
-    telegram.send_msg({
-        'type': RPCMessageType.SELL_FILL,
-        'trade_id': 1,
-        'exchange': 'Binance',
-        'pair': 'KEY/ETH',
-        'leverage': leverage,
-        'direction': direction,
-        'gain': 'loss',
-        'limit': 3.201e-05,
-        'amount': 1333.3333333333335,
-        'order_type': 'market',
-        'open_rate': 7.5e-05,
-        'close_rate': 3.201e-05,
-        'profit_amount': -0.05746268,
-        'profit_ratio': -0.57405275,
-        'stake_currency': 'ETH',
-        'enter_tag': enter_signal,
-        'exit_reason': ExitType.STOP_LOSS.value,
-        'open_date': arrow.utcnow().shift(days=-1, hours=-2, minutes=-30),
-        'close_date': arrow.utcnow(),
-    })
-<<<<<<< HEAD
-    assert msg_mock.call_args[0][0] \
-        == ('\N{WARNING SIGN} *Binance:* Sold KEY/ETH (#1)\n'
-            '*Profit:* `-57.41% (loss: -0.05746268 ETH)`\n'
-            '*Buy Tag:* `buy_signal1`\n'
-            '*Sell Reason:* `stop_loss`\n'
-            '*Amount:* `1333.33333333`\n'
-            '*Open Rate:* `0.00007500`\n'
-            '*Close Rate:* `0.00003201`\n'
-            '*Duration:* `1 day, 2:30:00 (1590.0 min)`'
-            )
-=======
-
-    leverage_text = f'*Leverage:* `{leverage}`\n' if leverage and leverage != 1.0 else ''
-    assert msg_mock.call_args[0][0] == (
-        '\N{WARNING SIGN} *Binance:* Exited KEY/ETH (#1)\n'
-        '*Profit:* `-57.41%`\n'
-        f'*Enter Tag:* `{enter_signal}`\n'
-        '*Exit Reason:* `stop_loss`\n'
-        '*Duration:* `1 day, 2:30:00 (1590.0 min)`\n'
-        f"*Direction:* `{direction}`\n"
-        f"{leverage_text}"
-        '*Amount:* `1333.33333333`\n'
-        '*Open Rate:* `0.00007500`\n'
-        '*Close Rate:* `0.00003201`'
-    )
->>>>>>> develop
-
-
-def test_send_msg_status_notification(default_conf, mocker) -> None:
-
-    telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
-    telegram.send_msg({
-        'type': RPCMessageType.STATUS,
-        'status': 'running'
-    })
-    assert msg_mock.call_args[0][0] == '*Status:* `running`'
-
-
-def test_warning_notification(default_conf, mocker) -> None:
-    telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
-    telegram.send_msg({
-        'type': RPCMessageType.WARNING,
-        'status': 'message'
-    })
-    assert msg_mock.call_args[0][0] == '\N{WARNING SIGN} *Warning:* `message`'
-
-
-def test_startup_notification(default_conf, mocker) -> None:
-    telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
-    telegram.send_msg({
-        'type': RPCMessageType.STARTUP,
-        'status': '*Custom:* `Hello World`'
-    })
-    assert msg_mock.call_args[0][0] == '*Custom:* `Hello World`'
-
-
-def test_send_msg_unknown_type(default_conf, mocker) -> None:
-    telegram, _, _ = get_telegram_testobject(mocker, default_conf)
-    with pytest.raises(NotImplementedError, match=r'Unknown message type: None'):
-        telegram.send_msg({
-            'type': None,
-        })
-
-
-@pytest.mark.parametrize('message_type,enter,enter_signal,leverage', [
-    (RPCMessageType.BUY, 'Long', 'long_signal_01', None),
-    (RPCMessageType.BUY, 'Long', 'long_signal_01', 2.0),
-    (RPCMessageType.SHORT, 'Short', 'short_signal_01', 2.0)])
-def test_send_msg_buy_notification_no_fiat(
-        default_conf, mocker, message_type, enter, enter_signal, leverage) -> None:
-    del default_conf['fiat_display_currency']
-    telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
-
-    telegram.send_msg({
-        'type': message_type,
-        'enter_tag': enter_signal,
-        'trade_id': 1,
-        'exchange': 'Binance',
-        'pair': 'ETH/BTC',
-        'leverage': leverage,
-        'limit': 1.099e-05,
-        'order_type': 'limit',
-        'stake_amount': 0.01465333,
-        'stake_amount_fiat': 0.0,
-        'stake_currency': 'BTC',
-        'fiat_currency': None,
-        'current_rate': 1.099e-05,
-        'amount': 1333.3333333333335,
-        'open_date': arrow.utcnow().shift(hours=-1)
-    })
-
-    leverage_text = f'*Leverage:* `{leverage}`\n' if leverage and leverage != 1.0 else ''
-    assert msg_mock.call_args[0][0] == (
-        f'\N{LARGE BLUE CIRCLE} *Binance:* {enter} ETH/BTC (#1)\n'
-        f'*Enter Tag:* `{enter_signal}`\n'
-        '*Amount:* `1333.33333333`\n'
-        f'{leverage_text}'
-        '*Open Rate:* `0.00001099`\n'
-        '*Current Rate:* `0.00001099`\n'
-        '*Total:* `(0.01465333 BTC)`'
-    )
-
-
-@pytest.mark.parametrize('direction,enter_signal,leverage', [
-    ('Long', 'long_signal_01', None),
-    ('Long', 'long_signal_01', 1.0),
-    ('Long', 'long_signal_01', 5.0),
-    ('Short', 'short_signal_01', 2.0),
-])
-def test_send_msg_sell_notification_no_fiat(
-        default_conf, mocker, direction, enter_signal, leverage) -> None:
-    del default_conf['fiat_display_currency']
-    telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
-
-    telegram.send_msg({
-        'type': RPCMessageType.SELL,
-        'trade_id': 1,
-        'exchange': 'Binance',
-        'pair': 'KEY/ETH',
-        'gain': 'loss',
-        'leverage': leverage,
-        'direction': direction,
-        'limit': 3.201e-05,
-        'amount': 1333.3333333333335,
-        'order_type': 'limit',
-        'open_rate': 7.5e-05,
-        'current_rate': 3.201e-05,
-        'profit_amount': -0.05746268,
-        'profit_ratio': -0.57405275,
-        'stake_currency': 'ETH',
-        'fiat_currency': 'USD',
-        'enter_tag': enter_signal,
-        'exit_reason': ExitType.STOP_LOSS.value,
-        'open_date': arrow.utcnow().shift(hours=-2, minutes=-35, seconds=-3),
-        'close_date': arrow.utcnow(),
-    })
-<<<<<<< HEAD
-    assert msg_mock.call_args[0][0] == ('\N{WARNING SIGN} *Binance:* Selling KEY/ETH (#1)\n'
-                                        '*Unrealized Profit:* `-57.41% (loss: -0.05746268 ETH)`\n'
-                                        '*Buy Tag:* `buy_signal1`\n'
-                                        '*Sell Reason:* `stop_loss`\n'
-                                        '*Amount:* `1333.33333333`\n'
-                                        '*Open Rate:* `0.00007500`\n'
-                                        '*Current Rate:* `0.00003201`\n'
-                                        '*Close Rate:* `0.00003201`\n'
-                                        '*Duration:* `2:35:03 (155.1 min)`'
-                                        )
-=======
-
-    leverage_text = f'*Leverage:* `{leverage}`\n' if leverage and leverage != 1.0 else ''
-    assert msg_mock.call_args[0][0] == (
-        '\N{WARNING SIGN} *Binance:* Exiting KEY/ETH (#1)\n'
-        '*Unrealized Profit:* `-57.41%`\n'
-        f'*Enter Tag:* `{enter_signal}`\n'
-        '*Exit Reason:* `stop_loss`\n'
-        '*Duration:* `2:35:03 (155.1 min)`\n'
-        f'*Direction:* `{direction}`\n'
-        f'{leverage_text}'
-        '*Amount:* `1333.33333333`\n'
-        '*Open Rate:* `0.00007500`\n'
-        '*Current Rate:* `0.00003201`\n'
-        '*Close Rate:* `0.00003201`'
-    )
->>>>>>> develop
-
-
-@pytest.mark.parametrize('msg,expected', [
-    ({'profit_percent': 20.1, 'exit_reason': 'roi'}, "\N{ROCKET}"),
-    ({'profit_percent': 5.1, 'exit_reason': 'roi'}, "\N{ROCKET}"),
-    ({'profit_percent': 2.56, 'exit_reason': 'roi'}, "\N{EIGHT SPOKED ASTERISK}"),
-    ({'profit_percent': 1.0, 'exit_reason': 'roi'}, "\N{EIGHT SPOKED ASTERISK}"),
-    ({'profit_percent': 0.0, 'exit_reason': 'roi'}, "\N{EIGHT SPOKED ASTERISK}"),
-    ({'profit_percent': -5.0, 'exit_reason': 'stop_loss'}, "\N{WARNING SIGN}"),
-    ({'profit_percent': -2.0, 'exit_reason': 'sell_signal'}, "\N{CROSS MARK}"),
-])
-def test__sell_emoji(default_conf, mocker, msg, expected):
-    del default_conf['fiat_display_currency']
-
-    telegram, _, _ = get_telegram_testobject(mocker, default_conf)
-
-    assert telegram._get_sell_emoji(msg) == expected
-
-
-def test_telegram__send_msg(default_conf, mocker, caplog) -> None:
-    mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
-    bot = MagicMock()
-    telegram, _, _ = get_telegram_testobject(mocker, default_conf, mock=False)
-    telegram._updater = MagicMock()
-    telegram._updater.bot = bot
-
-    telegram._config['telegram']['enabled'] = True
-    telegram._send_msg('test')
-    assert len(bot.method_calls) == 1
-
-    # Test update
-    query = MagicMock()
-    telegram._send_msg('test', callback_path="DeadBeef", query=query, reload_able=True)
-    edit_message_text = telegram._updater.bot.edit_message_text
-    assert edit_message_text.call_count == 1
-    assert "Updated: " in edit_message_text.call_args_list[0][1]['text']
-
-    telegram._updater.bot.edit_message_text = MagicMock(side_effect=BadRequest("not modified"))
-    telegram._send_msg('test', callback_path="DeadBeef", query=query)
-    assert telegram._updater.bot.edit_message_text.call_count == 1
-    assert not log_has_re(r"TelegramError: .*", caplog)
-
-    telegram._updater.bot.edit_message_text = MagicMock(side_effect=BadRequest(""))
-    telegram._send_msg('test2', callback_path="DeadBeef", query=query)
-    assert telegram._updater.bot.edit_message_text.call_count == 1
-    assert log_has_re(r"TelegramError: .*", caplog)
-
-    telegram._updater.bot.edit_message_text = MagicMock(side_effect=TelegramError("DeadBEEF"))
-    telegram._send_msg('test3', callback_path="DeadBeef", query=query)
-
-    assert log_has_re(r"TelegramError: DeadBEEF! Giving up.*", caplog)
-
-
-def test__send_msg_network_error(default_conf, mocker, caplog) -> None:
-    mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
-    bot = MagicMock()
-    bot.send_message = MagicMock(side_effect=NetworkError('Oh snap'))
-    telegram, _, _ = get_telegram_testobject(mocker, default_conf, mock=False)
-    telegram._updater = MagicMock()
-    telegram._updater.bot = bot
-
-    telegram._config['telegram']['enabled'] = True
-    telegram._send_msg('test')
-
-    # Bot should've tried to send it twice
-    assert len(bot.method_calls) == 2
-    assert log_has('Telegram NetworkError: Oh snap! Trying one more time.', caplog)
-
-
-def test__send_msg_keyboard(default_conf, mocker, caplog) -> None:
-    mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
-    bot = MagicMock()
-    bot.send_message = MagicMock()
-    freqtradebot = get_patched_freqtradebot(mocker, default_conf)
-    rpc = RPC(freqtradebot)
-
-    invalid_keys_list = [['/not_valid', '/profit'], ['/daily'], ['/alsoinvalid']]
-    default_keys_list = [['/daily', '/profit', '/balance'],
-                         ['/status', '/status table', '/performance'],
-                         ['/count', '/start', '/stop', '/help']]
-    default_keyboard = ReplyKeyboardMarkup(default_keys_list)
-
-    custom_keys_list = [['/daily', '/stats', '/balance', '/profit', '/profit 5'],
-                        ['/count', '/start', '/reload_config', '/help']]
-    custom_keyboard = ReplyKeyboardMarkup(custom_keys_list)
-
-    def init_telegram(freqtradebot):
-        telegram = Telegram(rpc, default_conf)
-        telegram._updater = MagicMock()
-        telegram._updater.bot = bot
-        return telegram
-
-    # no keyboard in config -> default keyboard
-    freqtradebot.config['telegram']['enabled'] = True
-    telegram = init_telegram(freqtradebot)
-    telegram._send_msg('test')
-    used_keyboard = bot.send_message.call_args[1]['reply_markup']
-    assert used_keyboard == default_keyboard
-
-    # invalid keyboard in config -> default keyboard
-    freqtradebot.config['telegram']['enabled'] = True
-    freqtradebot.config['telegram']['keyboard'] = invalid_keys_list
-    err_msg = re.escape("config.telegram.keyboard: Invalid commands for custom "
-                        "Telegram keyboard: ['/not_valid', '/alsoinvalid']"
-                        "\nvalid commands are: ") + r"*"
-    with pytest.raises(OperationalException, match=err_msg):
-        telegram = init_telegram(freqtradebot)
-
-    # valid keyboard in config -> custom keyboard
-    freqtradebot.config['telegram']['enabled'] = True
-    freqtradebot.config['telegram']['keyboard'] = custom_keys_list
-    telegram = init_telegram(freqtradebot)
-    telegram._send_msg('test')
-    used_keyboard = bot.send_message.call_args[1]['reply_markup']
-    assert used_keyboard == custom_keyboard
-    assert log_has("using custom keyboard from config.json: "
-                   "[['/daily', '/stats', '/balance', '/profit', '/profit 5'], ['/count', "
-                   "'/start', '/reload_config', '/help']]", caplog)
diff --git a/tests/test_freqtradebot.py.orig b/tests/test_freqtradebot.py.orig
deleted file mode 100644
index 5001384be..000000000
--- a/tests/test_freqtradebot.py.orig
+++ /dev/null
@@ -1,5832 +0,0 @@
-# pragma pylint: disable=missing-docstring, C0103
-# pragma pylint: disable=protected-access, too-many-lines, invalid-name, too-many-arguments
-import logging
-import time
-from copy import deepcopy
-from math import isclose
-from typing import List
-from unittest.mock import ANY, MagicMock, PropertyMock, patch
-
-import arrow
-import pytest
-from pandas import DataFrame
-
-from freqtrade.constants import CANCEL_REASON, MATH_CLOSE_PREC, UNLIMITED_STAKE_AMOUNT
-from freqtrade.enums import (CandleType, ExitCheckTuple, ExitType, RPCMessageType, RunMode,
-                             SignalDirection, State)
-from freqtrade.exceptions import (DependencyException, ExchangeError, InsufficientFundsError,
-                                  InvalidOrderException, OperationalException, PricingError,
-                                  TemporaryError)
-from freqtrade.freqtradebot import FreqtradeBot
-from freqtrade.persistence import Order, PairLocks, Trade
-from freqtrade.persistence.models import PairLock
-from freqtrade.worker import Worker
-from tests.conftest import (create_mock_trades, get_patched_freqtradebot, get_patched_worker,
-                            log_has, log_has_re, patch_edge, patch_exchange, patch_get_signal,
-                            patch_wallet, patch_whitelist)
-from tests.conftest_trades import (MOCK_TRADE_COUNT, enter_side, exit_side, mock_order_1,
-                                   mock_order_2, mock_order_2_sell, mock_order_3, mock_order_3_sell,
-                                   mock_order_4, mock_order_5_stoploss, mock_order_6_sell)
-
-
-def patch_RPCManager(mocker) -> MagicMock:
-    """
-    This function mock RPC manager to avoid repeating this code in almost every tests
-    :param mocker: mocker to patch RPCManager class
-    :return: RPCManager.send_msg MagicMock to track if this method is called
-    """
-    mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
-    rpc_mock = mocker.patch('freqtrade.freqtradebot.RPCManager.send_msg', MagicMock())
-    return rpc_mock
-
-
-# Unit tests
-
-
-def test_freqtradebot_state(mocker, default_conf_usdt, markets) -> None:
-    mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets))
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-    assert freqtrade.state is State.RUNNING
-
-    default_conf_usdt.pop('initial_state')
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    assert freqtrade.state is State.STOPPED
-
-
-def test_process_stopped(mocker, default_conf_usdt) -> None:
-
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-    coo_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.cancel_all_open_orders')
-    freqtrade.process_stopped()
-    assert coo_mock.call_count == 0
-
-    default_conf_usdt['cancel_open_orders_on_exit'] = True
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-    freqtrade.process_stopped()
-    assert coo_mock.call_count == 1
-
-
-def test_bot_cleanup(mocker, default_conf_usdt, caplog) -> None:
-    mock_cleanup = mocker.patch('freqtrade.freqtradebot.cleanup_db')
-    coo_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.cancel_all_open_orders')
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-    freqtrade.cleanup()
-    assert log_has('Cleaning up modules ...', caplog)
-    assert mock_cleanup.call_count == 1
-    assert coo_mock.call_count == 0
-
-    freqtrade.config['cancel_open_orders_on_exit'] = True
-    freqtrade.cleanup()
-    assert coo_mock.call_count == 1
-
-
-@pytest.mark.parametrize('runmode', [
-    RunMode.DRY_RUN,
-    RunMode.LIVE
-])
-def test_order_dict(default_conf_usdt, mocker, runmode, caplog) -> None:
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    conf = default_conf_usdt.copy()
-    conf['runmode'] = runmode
-    conf['order_types'] = {
-        'entry': 'market',
-        'exit': 'limit',
-        'stoploss': 'limit',
-        'stoploss_on_exchange': True,
-    }
-    conf['entry_pricing']['price_side'] = 'ask'
-
-    freqtrade = FreqtradeBot(conf)
-    if runmode == RunMode.LIVE:
-        assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog)
-    assert freqtrade.strategy.order_types['stoploss_on_exchange']
-
-    caplog.clear()
-    # is left untouched
-    conf = default_conf_usdt.copy()
-    conf['runmode'] = runmode
-    conf['order_types'] = {
-        'entry': 'market',
-        'exit': 'limit',
-        'stoploss': 'limit',
-        'stoploss_on_exchange': False,
-    }
-    freqtrade = FreqtradeBot(conf)
-    assert not freqtrade.strategy.order_types['stoploss_on_exchange']
-    assert not log_has_re(".*stoploss_on_exchange .* dry-run", caplog)
-
-
-def test_get_trade_stake_amount(default_conf_usdt, mocker) -> None:
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-
-    freqtrade = FreqtradeBot(default_conf_usdt)
-
-    result = freqtrade.wallets.get_trade_stake_amount('ETH/USDT')
-    assert result == default_conf_usdt['stake_amount']
-
-
-@pytest.mark.parametrize("amend_last,wallet,max_open,lsamr,expected", [
-                        (False, 120, 2, 0.5, [60, None]),
-                        (True, 120, 2, 0.5, [60, 58.8]),
-                        (False, 180, 3, 0.5, [60, 60, None]),
-                        (True, 180, 3, 0.5, [60, 60, 58.2]),
-                        (False, 122, 3, 0.5, [60, 60, None]),
-                        (True, 122, 3, 0.5, [60, 60, 0.0]),
-                        (True, 167, 3, 0.5, [60, 60, 45.33]),
-                        (True, 122, 3, 1, [60, 60, 0.0]),
-])
-def test_check_available_stake_amount(
-    default_conf_usdt, ticker_usdt, mocker, fee, limit_buy_order_usdt_open,
-    amend_last, wallet, max_open, lsamr, expected
-) -> None:
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        create_order=MagicMock(return_value=limit_buy_order_usdt_open),
-        get_fee=fee
-    )
-    default_conf_usdt['dry_run_wallet'] = wallet
-
-    default_conf_usdt['amend_last_stake_amount'] = amend_last
-    default_conf_usdt['last_stake_amount_min_ratio'] = lsamr
-
-    freqtrade = FreqtradeBot(default_conf_usdt)
-
-    for i in range(0, max_open):
-
-        if expected[i] is not None:
-            limit_buy_order_usdt_open['id'] = str(i)
-            result = freqtrade.wallets.get_trade_stake_amount('ETH/USDT')
-            assert pytest.approx(result) == expected[i]
-            freqtrade.execute_entry('ETH/USDT', result)
-        else:
-            with pytest.raises(DependencyException):
-                freqtrade.wallets.get_trade_stake_amount('ETH/USDT')
-
-
-def test_edge_called_in_process(mocker, edge_conf) -> None:
-    patch_RPCManager(mocker)
-    patch_edge(mocker)
-
-    patch_exchange(mocker)
-    freqtrade = FreqtradeBot(edge_conf)
-    patch_get_signal(freqtrade)
-    freqtrade.process()
-    assert freqtrade.active_pair_whitelist == ['NEO/BTC', 'LTC/BTC']
-
-
-def test_edge_overrides_stake_amount(mocker, edge_conf) -> None:
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    patch_edge(mocker)
-    edge_conf['dry_run_wallet'] = 999.9
-    freqtrade = FreqtradeBot(edge_conf)
-
-    assert freqtrade.wallets.get_trade_stake_amount(
-        'NEO/BTC', freqtrade.edge) == (999.9 * 0.5 * 0.01) / 0.20
-    assert freqtrade.wallets.get_trade_stake_amount(
-        'LTC/BTC', freqtrade.edge) == (999.9 * 0.5 * 0.01) / 0.21
-
-
-@pytest.mark.parametrize('buy_price_mult,ignore_strat_sl', [
-    (0.79, False),   # Override stoploss
-    (0.85, True),    # Override strategy stoploss
-])
-def test_edge_overrides_stoploss(limit_order, fee, caplog, mocker,
-                                 buy_price_mult, ignore_strat_sl, edge_conf) -> None:
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    patch_edge(mocker)
-    edge_conf['max_open_trades'] = float('inf')
-
-    # Strategy stoploss is -0.1 but Edge imposes a stoploss at -0.2
-    # Thus, if price falls 21%, stoploss should be triggered
-    #
-    # mocking the ticker: price is falling ...
-    enter_price = limit_order['buy']['price']
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=MagicMock(return_value={
-            'bid': enter_price * buy_price_mult,
-            'ask': enter_price * buy_price_mult,
-            'last': enter_price * buy_price_mult,
-        }),
-        get_fee=fee,
-    )
-    #############################################
-
-    # Create a trade with "limit_buy_order_usdt" price
-    freqtrade = FreqtradeBot(edge_conf)
-    freqtrade.active_pair_whitelist = ['NEO/BTC']
-    patch_get_signal(freqtrade)
-    freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
-    freqtrade.enter_positions()
-    trade = Trade.query.first()
-<<<<<<< HEAD
-    limit_buy_order_usdt['id'] = trade.orders[0].order_id
-    oobj = Order.parse_from_ccxt_object(limit_buy_order_usdt, 'NEO/BTC', 'buy')
-    trade.update_order(limit_buy_order_usdt)
-=======
-    caplog.clear()
-    oobj = Order.parse_from_ccxt_object(limit_order['buy'], 'ADA/USDT', 'buy')
->>>>>>> develop
-    trade.update_trade(oobj)
-    #############################################
-
-    # stoploss shoud be hit
-    assert freqtrade.handle_trade(trade) is not ignore_strat_sl
-    if not ignore_strat_sl:
-        assert log_has_re('Exit for NEO/BTC detected. Reason: stop_loss.*', caplog)
-        assert trade.exit_reason == ExitType.STOP_LOSS.value
-        # Test compatibility ...
-        assert trade.sell_reason == ExitType.STOP_LOSS.value
-
-
-def test_total_open_trades_stakes(mocker, default_conf_usdt, ticker_usdt, fee) -> None:
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    default_conf_usdt['max_open_trades'] = 2
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        get_fee=fee,
-        _is_dry_limit_order_filled=MagicMock(return_value=False),
-    )
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade)
-    freqtrade.enter_positions()
-    trade = Trade.query.first()
-
-    assert trade is not None
-    assert trade.stake_amount == 60.0
-    assert trade.is_open
-    assert trade.open_date is not None
-
-    freqtrade.enter_positions()
-    trade = Trade.query.order_by(Trade.id.desc()).first()
-
-    assert trade is not None
-    assert trade.stake_amount == 60.0
-    assert trade.is_open
-    assert trade.open_date is not None
-
-    assert Trade.total_open_trades_stakes() == 120.0
-
-
-@pytest.mark.parametrize("is_short,open_rate", [
-    (False, 2.0),
-    (True, 2.2)
-])
-def test_create_trade(default_conf_usdt, ticker_usdt, limit_order,
-                      fee, mocker, is_short, open_rate) -> None:
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        get_fee=fee,
-        _is_dry_limit_order_filled=MagicMock(return_value=False),
-    )
-
-    # Save state of current whitelist
-    whitelist = deepcopy(default_conf_usdt['exchange']['pair_whitelist'])
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
-    freqtrade.create_trade('ETH/USDT')
-
-    trade = Trade.query.first()
-    trade.is_short = is_short
-    assert trade is not None
-    assert pytest.approx(trade.stake_amount) == 60.0
-    assert trade.is_open
-    assert trade.open_date is not None
-    assert trade.exchange == 'binance'
-
-    # Simulate fulfilled LIMIT_BUY order for trade
-    oobj = Order.parse_from_ccxt_object(
-        limit_order[enter_side(is_short)], 'ADA/USDT', enter_side(is_short))
-    trade.update_trade(oobj)
-
-    assert trade.open_rate == open_rate
-    assert trade.amount == 30.0
-
-    assert whitelist == default_conf_usdt['exchange']['pair_whitelist']
-
-
-def test_create_trade_no_stake_amount(default_conf_usdt, ticker_usdt, fee, mocker) -> None:
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    patch_wallet(mocker, free=default_conf_usdt['stake_amount'] * 0.5)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        get_fee=fee,
-    )
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade)
-
-    with pytest.raises(DependencyException, match=r'.*stake amount.*'):
-        freqtrade.create_trade('ETH/USDT')
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-@pytest.mark.parametrize('stake_amount,create,amount_enough,max_open_trades', [
-    (5.0, True, True, 99),
-    (0.049, True, False, 99),  # Amount will be adjusted to min - which is 0.051
-    (0, False, True, 99),
-    (UNLIMITED_STAKE_AMOUNT, False, True, 0),
-])
-def test_create_trade_minimal_amount(
-    default_conf_usdt, ticker_usdt, limit_order_open, fee, mocker,
-    stake_amount, create, amount_enough, max_open_trades, caplog, is_short
-) -> None:
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    enter_mock = MagicMock(return_value=limit_order_open[enter_side(is_short)])
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        create_order=enter_mock,
-        get_fee=fee,
-    )
-    default_conf_usdt['max_open_trades'] = max_open_trades
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    freqtrade.config['stake_amount'] = stake_amount
-    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
-
-    if create:
-        assert freqtrade.create_trade('ETH/USDT')
-        if amount_enough:
-            rate, amount = enter_mock.call_args[1]['rate'], enter_mock.call_args[1]['amount']
-            assert rate * amount <= default_conf_usdt['stake_amount']
-        else:
-            assert log_has_re(
-                r"Stake amount for pair .* is too small.*",
-                caplog
-            )
-    else:
-        assert not freqtrade.create_trade('ETH/USDT')
-        if not max_open_trades:
-            assert freqtrade.wallets.get_trade_stake_amount('ETH/USDT', freqtrade.edge) == 0
-
-
-@pytest.mark.parametrize('whitelist,positions', [
-    (["ETH/USDT"], 1),  # No pairs left
-    ([], 0),  # No pairs in whitelist
-])
-def test_enter_positions_no_pairs_left(default_conf_usdt, ticker_usdt, limit_buy_order_usdt_open,
-                                       fee, whitelist, positions, mocker, caplog) -> None:
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        create_order=MagicMock(return_value=limit_buy_order_usdt_open),
-        get_fee=fee,
-    )
-    default_conf_usdt['exchange']['pair_whitelist'] = whitelist
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade)
-
-    n = freqtrade.enter_positions()
-    assert n == positions
-    if positions:
-        assert not log_has_re(r"No currency pair in active pair whitelist.*", caplog)
-        n = freqtrade.enter_positions()
-        assert n == 0
-        assert log_has_re(r"No currency pair in active pair whitelist.*", caplog)
-    else:
-        assert n == 0
-        assert log_has("Active pair whitelist is empty.", caplog)
-
-
-@pytest.mark.usefixtures("init_persistence")
-def test_enter_positions_global_pairlock(default_conf_usdt, ticker_usdt, limit_buy_order_usdt, fee,
-                                         mocker, caplog) -> None:
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        create_order=MagicMock(return_value={'id': limit_buy_order_usdt['id']}),
-        get_fee=fee,
-    )
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade)
-    n = freqtrade.enter_positions()
-    message = r"Global pairlock active until.* Not creating new trades."
-    n = freqtrade.enter_positions()
-    # 0 trades, but it's not because of pairlock.
-    assert n == 0
-    assert not log_has_re(message, caplog)
-    caplog.clear()
-
-    PairLocks.lock_pair('*', arrow.utcnow().shift(minutes=20).datetime, 'Just because')
-    n = freqtrade.enter_positions()
-    assert n == 0
-    assert log_has_re(message, caplog)
-
-
-@pytest.mark.parametrize('is_short', [False, True])
-def test_handle_protections(mocker, default_conf_usdt, fee, is_short):
-    default_conf_usdt['protections'] = [
-        {"method": "CooldownPeriod", "stop_duration": 60},
-        {
-            "method": "StoplossGuard",
-            "lookback_period_candles": 24,
-            "trade_limit": 4,
-            "stop_duration_candles": 4,
-            "only_per_pair": False
-        }
-    ]
-
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-    freqtrade.protections._protection_handlers[1].global_stop = MagicMock(
-        return_value=(True, arrow.utcnow().shift(hours=1).datetime, "asdf"))
-    create_mock_trades(fee, is_short)
-    freqtrade.handle_protections('ETC/BTC')
-    send_msg_mock = freqtrade.rpc.send_msg
-    assert send_msg_mock.call_count == 2
-    assert send_msg_mock.call_args_list[0][0][0]['type'] == RPCMessageType.PROTECTION_TRIGGER
-    assert send_msg_mock.call_args_list[1][0][0]['type'] == RPCMessageType.PROTECTION_TRIGGER_GLOBAL
-
-
-def test_create_trade_no_signal(default_conf_usdt, fee, mocker) -> None:
-    default_conf_usdt['dry_run'] = True
-
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        get_fee=fee,
-    )
-    default_conf_usdt['stake_amount'] = 10
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade, enter_long=False, exit_long=False)
-
-    Trade.query = MagicMock()
-    Trade.query.filter = MagicMock()
-    assert not freqtrade.create_trade('ETH/USDT')
-
-
-@pytest.mark.parametrize("max_open", range(0, 5))
-@pytest.mark.parametrize("tradable_balance_ratio,modifier", [(1.0, 1), (0.99, 0.8), (0.5, 0.5)])
-def test_create_trades_multiple_trades(
-    default_conf_usdt, ticker_usdt, fee, mocker, limit_buy_order_usdt_open,
-    max_open, tradable_balance_ratio, modifier
-) -> None:
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    default_conf_usdt['max_open_trades'] = max_open
-    default_conf_usdt['tradable_balance_ratio'] = tradable_balance_ratio
-    default_conf_usdt['dry_run_wallet'] = 60.0 * max_open
-
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        create_order=MagicMock(return_value=limit_buy_order_usdt_open),
-        get_fee=fee,
-    )
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade)
-
-    n = freqtrade.enter_positions()
-    trades = Trade.get_open_trades()
-    # Expected trades should be max_open * a modified value
-    # depending on the configured tradable_balance
-    assert n == max(int(max_open * modifier), 0)
-    assert len(trades) == max(int(max_open * modifier), 0)
-
-
-def test_create_trades_preopen(default_conf_usdt, ticker_usdt, fee, mocker,
-                               limit_buy_order_usdt_open) -> None:
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    default_conf_usdt['max_open_trades'] = 4
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        create_order=MagicMock(return_value=limit_buy_order_usdt_open),
-        get_fee=fee,
-    )
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade)
-
-    # Create 2 existing trades
-    freqtrade.execute_entry('ETH/USDT', default_conf_usdt['stake_amount'])
-    freqtrade.execute_entry('NEO/BTC', default_conf_usdt['stake_amount'])
-
-    assert len(Trade.get_open_trades()) == 2
-    # Change order_id for new orders
-    limit_buy_order_usdt_open['id'] = '123444'
-
-    # Create 2 new trades using create_trades
-    assert freqtrade.create_trade('ETH/USDT')
-    assert freqtrade.create_trade('NEO/BTC')
-
-    trades = Trade.get_open_trades()
-    assert len(trades) == 4
-
-
-@pytest.mark.parametrize('is_short', [False, True])
-def test_process_trade_creation(default_conf_usdt, ticker_usdt, limit_order, limit_order_open,
-                                is_short, fee, mocker, caplog
-                                ) -> None:
-    ticker_side = 'ask' if is_short else 'bid'
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        create_order=MagicMock(return_value=limit_order_open[enter_side(is_short)]),
-        fetch_order=MagicMock(return_value=limit_order[enter_side(is_short)]),
-        get_fee=fee,
-    )
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
-
-    trades = Trade.query.filter(Trade.is_open.is_(True)).all()
-    assert not trades
-
-    freqtrade.process()
-
-    trades = Trade.query.filter(Trade.is_open.is_(True)).all()
-    assert len(trades) == 1
-    trade = trades[0]
-    assert trade is not None
-    assert pytest.approx(trade.stake_amount) == default_conf_usdt['stake_amount']
-    assert trade.is_open
-    assert trade.open_date is not None
-    assert trade.exchange == 'binance'
-    assert trade.open_rate == ticker_usdt.return_value[ticker_side]
-    assert isclose(trade.amount, 60 / ticker_usdt.return_value[ticker_side])
-
-    assert log_has(
-        f'{"Short" if is_short else "Long"} signal found: about create a new trade for ETH/USDT '
-        'with stake_amount: 60.0 ...',
-        caplog
-    )
-
-
-def test_process_exchange_failures(default_conf_usdt, ticker_usdt, mocker) -> None:
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        create_order=MagicMock(side_effect=TemporaryError)
-    )
-    sleep_mock = mocker.patch('time.sleep', side_effect=lambda _: None)
-
-    worker = Worker(args=None, config=default_conf_usdt)
-    patch_get_signal(worker.freqtrade)
-
-    worker._process_running()
-    assert sleep_mock.has_calls()
-
-
-def test_process_operational_exception(default_conf_usdt, ticker_usdt, mocker) -> None:
-    msg_mock = patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        create_order=MagicMock(side_effect=OperationalException)
-    )
-    worker = Worker(args=None, config=default_conf_usdt)
-    patch_get_signal(worker.freqtrade)
-
-    assert worker.freqtrade.state == State.RUNNING
-
-    worker._process_running()
-    assert worker.freqtrade.state == State.STOPPED
-    assert 'OperationalException' in msg_mock.call_args_list[-1][0][0]['status']
-
-
-def test_process_trade_handling(default_conf_usdt, ticker_usdt, limit_buy_order_usdt_open, fee,
-                                mocker) -> None:
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        create_order=MagicMock(return_value=limit_buy_order_usdt_open),
-        fetch_order=MagicMock(return_value=limit_buy_order_usdt_open),
-        get_fee=fee,
-    )
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade)
-
-    trades = Trade.query.filter(Trade.is_open.is_(True)).all()
-    assert not trades
-    freqtrade.process()
-
-    trades = Trade.query.filter(Trade.is_open.is_(True)).all()
-    assert len(trades) == 1
-
-    # Nothing happened ...
-    freqtrade.process()
-    assert len(trades) == 1
-
-
-def test_process_trade_no_whitelist_pair(default_conf_usdt, ticker_usdt, limit_buy_order_usdt,
-                                         fee, mocker) -> None:
-    """ Test process with trade not in pair list """
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        create_order=MagicMock(return_value={'id': limit_buy_order_usdt['id']}),
-        fetch_order=MagicMock(return_value=limit_buy_order_usdt),
-        get_fee=fee,
-    )
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade)
-    pair = 'BLK/BTC'
-    # Ensure the pair is not in the whitelist!
-    assert pair not in default_conf_usdt['exchange']['pair_whitelist']
-
-    # create open trade not in whitelist
-    Trade.query.session.add(Trade(
-        pair=pair,
-        stake_amount=0.001,
-        fee_open=fee.return_value,
-        fee_close=fee.return_value,
-        is_open=True,
-        amount=20,
-        open_rate=0.01,
-        exchange='binance',
-    ))
-    Trade.query.session.add(Trade(
-        pair='ETH/USDT',
-        stake_amount=0.001,
-        fee_open=fee.return_value,
-        fee_close=fee.return_value,
-        is_open=True,
-        amount=12,
-        open_rate=0.001,
-        exchange='binance',
-    ))
-
-    assert pair not in freqtrade.active_pair_whitelist
-    freqtrade.process()
-    assert pair in freqtrade.active_pair_whitelist
-    # Make sure each pair is only in the list once
-    assert len(freqtrade.active_pair_whitelist) == len(set(freqtrade.active_pair_whitelist))
-
-
-def test_process_informative_pairs_added(default_conf_usdt, ticker_usdt, mocker) -> None:
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-
-    refresh_mock = MagicMock()
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        create_order=MagicMock(side_effect=TemporaryError),
-        refresh_latest_ohlcv=refresh_mock,
-    )
-    inf_pairs = MagicMock(return_value=[
-        ("BTC/ETH", '1m', CandleType.SPOT),
-        ("ETH/USDT", "1h", CandleType.SPOT)
-    ])
-    mocker.patch.multiple(
-        'freqtrade.strategy.interface.IStrategy',
-        get_exit_signal=MagicMock(return_value=(False, False)),
-        get_entry_signal=MagicMock(return_value=(None, None))
-    )
-    mocker.patch('time.sleep', return_value=None)
-
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    freqtrade.strategy.informative_pairs = inf_pairs
-    # patch_get_signal(freqtrade)
-
-    freqtrade.process()
-    assert inf_pairs.call_count == 1
-    assert refresh_mock.call_count == 1
-    assert ("BTC/ETH", "1m", CandleType.SPOT) in refresh_mock.call_args[0][0]
-    assert ("ETH/USDT", "1h", CandleType.SPOT) in refresh_mock.call_args[0][0]
-    assert ("ETH/USDT", default_conf_usdt["timeframe"],
-            CandleType.SPOT) in refresh_mock.call_args[0][0]
-
-
-@pytest.mark.parametrize("is_short,trading_mode,exchange_name,margin_mode,liq_buffer,liq_price", [
-    (False, 'spot', 'binance', None, 0.0, None),
-    (True, 'spot', 'binance', None, 0.0, None),
-    (False, 'spot', 'gateio', None, 0.0, None),
-    (True, 'spot', 'gateio', None, 0.0, None),
-    (False, 'spot', 'okx', None, 0.0, None),
-    (True, 'spot', 'okx', None, 0.0, None),
-    (True, 'futures', 'binance', 'isolated', 0.0, 11.89108910891089),
-    (False, 'futures', 'binance', 'isolated', 0.0, 8.070707070707071),
-    (True, 'futures', 'gateio', 'isolated', 0.0, 11.87413417771621),
-    (False, 'futures', 'gateio', 'isolated', 0.0, 8.085708510208207),
-    (True, 'futures', 'binance', 'isolated', 0.05, 11.796534653465345),
-    (False, 'futures', 'binance', 'isolated', 0.05, 8.167171717171717),
-    (True, 'futures', 'gateio', 'isolated', 0.05, 11.7804274688304),
-    (False, 'futures', 'gateio', 'isolated', 0.05, 8.181423084697796),
-    (True, 'futures', 'okx', 'isolated', 0.0, 11.87413417771621),
-    (False, 'futures', 'okx', 'isolated', 0.0, 8.085708510208207),
-])
-def test_execute_entry(mocker, default_conf_usdt, fee, limit_order,
-                       limit_order_open, is_short, trading_mode,
-                       exchange_name, margin_mode, liq_buffer, liq_price) -> None:
-    """
-    exchange_name = binance, is_short = true
-        leverage = 5
-        position = 0.2 * 5
-        ((wb + cum_b) - (side_1 * position * ep1)) / ((position * mmr_b) - (side_1 * position))
-        ((2 + 0.01) - ((-1) * 1 * 10)) / ((1 * 0.01) - ((-1) * 1)) = 11.89108910891089
-
-    exchange_name = binance, is_short = false
-        ((wb + cum_b) - (side_1 * position * ep1)) / ((position * mmr_b) - (side_1 * position))
-        ((2 + 0.01) - (1 * 1 * 10)) / ((1 * 0.01) - (1 * 1)) = 8.070707070707071
-
-    exchange_name = gateio/okx, is_short = true
-        (open_rate + (wallet_balance / position)) / (1 + (mm_ratio + taker_fee_rate))
-        (10 + (2 / 1)) / (1 + (0.01 + 0.0006)) = 11.87413417771621
-
-    exchange_name = gateio/okx, is_short = false
-        (open_rate - (wallet_balance / position)) / (1 - (mm_ratio + taker_fee_rate))
-        (10 - (2 / 1)) / (1 - (0.01 + 0.0006)) = 8.085708510208207
-    """
-    # TODO: Split this test into multiple tests to improve readability
-    open_order = limit_order_open[enter_side(is_short)]
-    order = limit_order[enter_side(is_short)]
-    default_conf_usdt['trading_mode'] = trading_mode
-    default_conf_usdt['liquidation_buffer'] = liq_buffer
-    leverage = 1.0 if trading_mode == 'spot' else 5.0
-    default_conf_usdt['exchange']['name'] = exchange_name
-    if margin_mode:
-        default_conf_usdt['margin_mode'] = margin_mode
-    mocker.patch('freqtrade.exchange.Gateio.validate_ordertypes')
-    patch_RPCManager(mocker)
-    patch_exchange(mocker, id=exchange_name)
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    freqtrade.strategy.confirm_trade_entry = MagicMock(return_value=False)
-    freqtrade.strategy.leverage = MagicMock(return_value=leverage)
-    stake_amount = 2
-    bid = 0.11
-    enter_rate_mock = MagicMock(return_value=bid)
-    enter_mm = MagicMock(return_value=open_order)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        get_rate=enter_rate_mock,
-        fetch_ticker=MagicMock(return_value={
-            'bid': 1.9,
-            'ask': 2.2,
-            'last': 1.9
-        }),
-        create_order=enter_mm,
-        get_min_pair_stake_amount=MagicMock(return_value=1),
-        get_max_pair_stake_amount=MagicMock(return_value=500000),
-        get_fee=fee,
-        get_funding_fees=MagicMock(return_value=0),
-        name=exchange_name,
-        get_maintenance_ratio_and_amt=MagicMock(return_value=(0.01, 0.01)),
-        get_max_leverage=MagicMock(return_value=10),
-    )
-    mocker.patch.multiple(
-        'freqtrade.exchange.Okx',
-        get_max_pair_stake_amount=MagicMock(return_value=500000),
-    )
-    pair = 'ETH/USDT'
-
-    assert not freqtrade.execute_entry(pair, stake_amount, is_short=is_short)
-    assert enter_rate_mock.call_count == 1
-    assert enter_mm.call_count == 0
-    assert freqtrade.strategy.confirm_trade_entry.call_count == 1
-    enter_rate_mock.reset_mock()
-
-    open_order['id'] = '22'
-    freqtrade.strategy.confirm_trade_entry = MagicMock(return_value=True)
-    assert freqtrade.execute_entry(pair, stake_amount)
-    assert enter_rate_mock.call_count == 1
-    assert enter_mm.call_count == 1
-    call_args = enter_mm.call_args_list[0][1]
-    assert call_args['pair'] == pair
-    assert call_args['rate'] == bid
-    assert pytest.approx(call_args['amount']) == round(stake_amount / bid * leverage, 8)
-    enter_rate_mock.reset_mock()
-
-    # Should create an open trade with an open order id
-    # As the order is not fulfilled yet
-    trade = Trade.query.first()
-    trade.is_short = is_short
-    assert trade
-    assert trade.is_open is True
-    assert trade.open_order_id == '22'
-
-    # Test calling with price
-    open_order['id'] = '33'
-    fix_price = 0.06
-    assert freqtrade.execute_entry(pair, stake_amount, fix_price, is_short=is_short)
-    # Make sure get_rate wasn't called again
-    assert enter_rate_mock.call_count == 0
-
-    assert enter_mm.call_count == 2
-    call_args = enter_mm.call_args_list[1][1]
-    assert call_args['pair'] == pair
-    assert call_args['rate'] == fix_price
-    assert pytest.approx(call_args['amount']) == round(stake_amount / fix_price * leverage, 8)
-
-    # In case of closed order
-<<<<<<< HEAD
-    limit_buy_order_usdt['status'] = 'closed'
-    limit_buy_order_usdt['average'] = 10
-    limit_buy_order_usdt['cost'] = 300
-    limit_buy_order_usdt['id'] = '444'
-=======
-    order['status'] = 'closed'
-    order['price'] = 10
-    order['cost'] = 100
-    order['id'] = '444'
->>>>>>> develop
-
-    mocker.patch('freqtrade.exchange.Exchange.create_order',
-                 MagicMock(return_value=order))
-    assert freqtrade.execute_entry(pair, stake_amount, is_short=is_short)
-    trade = Trade.query.all()[2]
-    trade.is_short = is_short
-    assert trade
-    assert trade.open_order_id is None
-    assert trade.open_rate == 10
-<<<<<<< HEAD
-    assert trade.stake_amount == 300
-
-    # In case of rejected or expired order and partially filled
-    limit_buy_order_usdt['status'] = 'expired'
-    limit_buy_order_usdt['amount'] = 30.0
-    limit_buy_order_usdt['filled'] = 20.0
-    limit_buy_order_usdt['remaining'] = 10.00
-    limit_buy_order_usdt['average'] = 0.5
-    limit_buy_order_usdt['cost'] = 10.0
-    limit_buy_order_usdt['id'] = '555'
-=======
-    assert trade.stake_amount == round(order['price'] * order['filled'] / leverage, 8)
-
-    # In case of rejected or expired order and partially filled
-    order['status'] = 'expired'
-    order['amount'] = 30.0
-    order['filled'] = 20.0
-    order['remaining'] = 10.00
-    order['price'] = 0.5
-    order['cost'] = 15.0
-    order['id'] = '555'
->>>>>>> develop
-    mocker.patch('freqtrade.exchange.Exchange.create_order',
-                 MagicMock(return_value=order))
-    assert freqtrade.execute_entry(pair, stake_amount)
-    trade = Trade.query.all()[3]
-    trade.is_short = is_short
-    assert trade
-    assert trade.open_order_id == '555'
-    assert trade.open_rate == 0.5
-<<<<<<< HEAD
-    assert trade.stake_amount == 10.0
-=======
-    assert trade.stake_amount == round(order['price'] * order['filled'] / leverage, 8)
->>>>>>> develop
-
-    # Test with custom stake
-    order['status'] = 'open'
-    order['id'] = '556'
-
-    freqtrade.strategy.custom_stake_amount = lambda **kwargs: 150.0
-    assert freqtrade.execute_entry(pair, stake_amount, is_short=is_short)
-    trade = Trade.query.all()[4]
-    trade.is_short = is_short
-    assert trade
-    assert pytest.approx(trade.stake_amount) == 150
-
-    # Exception case
-    order['id'] = '557'
-    freqtrade.strategy.custom_stake_amount = lambda **kwargs: 20 / 0
-    assert freqtrade.execute_entry(pair, stake_amount, is_short=is_short)
-    trade = Trade.query.all()[5]
-    trade.is_short = is_short
-    assert trade
-    assert pytest.approx(trade.stake_amount) == 2.0
-
-    # In case of the order is rejected and not filled at all
-<<<<<<< HEAD
-    limit_buy_order_usdt['status'] = 'rejected'
-    limit_buy_order_usdt['amount'] = 30.0
-    limit_buy_order_usdt['filled'] = 0.0
-    limit_buy_order_usdt['remaining'] = 30.0
-    limit_buy_order_usdt['average'] = 0.5
-    limit_buy_order_usdt['cost'] = 0.0
-    limit_buy_order_usdt['id'] = '66'
-=======
-    order['status'] = 'rejected'
-    order['amount'] = 30.0 * leverage
-    order['filled'] = 0.0
-    order['remaining'] = 30.0
-    order['price'] = 0.5
-    order['cost'] = 0.0
-    order['id'] = '66'
->>>>>>> develop
-    mocker.patch('freqtrade.exchange.Exchange.create_order',
-                 MagicMock(return_value=order))
-    assert not freqtrade.execute_entry(pair, stake_amount)
-    assert freqtrade.strategy.leverage.call_count == 0 if trading_mode == 'spot' else 2
-
-    # Fail to get price...
-    mocker.patch('freqtrade.exchange.Exchange.get_rate', MagicMock(return_value=0.0))
-
-    with pytest.raises(PricingError, match="Could not determine entry price."):
-        freqtrade.execute_entry(pair, stake_amount, is_short=is_short)
-
-    # In case of custom entry price
-    mocker.patch('freqtrade.exchange.Exchange.get_rate', return_value=0.50)
-    order['status'] = 'open'
-    order['id'] = '5566'
-    freqtrade.strategy.custom_entry_price = lambda **kwargs: 0.508
-    assert freqtrade.execute_entry(pair, stake_amount, is_short=is_short)
-    trade = Trade.query.all()[6]
-    trade.is_short = is_short
-    assert trade
-    assert trade.open_rate_requested == 0.508
-
-    # In case of custom entry price set to None
-
-    order['status'] = 'open'
-    order['id'] = '5567'
-    freqtrade.strategy.custom_entry_price = lambda **kwargs: None
-
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        get_rate=MagicMock(return_value=10),
-    )
-
-    assert freqtrade.execute_entry(pair, stake_amount, is_short=is_short)
-    trade = Trade.query.all()[7]
-    trade.is_short = is_short
-    assert trade
-    assert trade.open_rate_requested == 10
-
-    # In case of custom entry price not float type
-    freqtrade.exchange.get_maintenance_ratio_and_amt = MagicMock(return_value=(0.01, 0.01))
-    freqtrade.exchange.name = exchange_name
-    order['status'] = 'open'
-    order['id'] = '5568'
-    freqtrade.strategy.custom_entry_price = lambda **kwargs: "string price"
-    assert freqtrade.execute_entry(pair, stake_amount, is_short=is_short)
-    trade = Trade.query.all()[8]
-    # Trade(id=9, pair=ETH/USDT, amount=0.20000000, is_short=False,
-    #   leverage=1.0, open_rate=10.00000000, open_since=...)
-    # Trade(id=9, pair=ETH/USDT, amount=0.60000000, is_short=True,
-    #   leverage=3.0, open_rate=10.00000000, open_since=...)
-    trade.is_short = is_short
-    assert trade
-    assert trade.open_rate_requested == 10
-    assert trade.liquidation_price == liq_price
-
-    # In case of too high stake amount
-
-    order['status'] = 'open'
-    order['id'] = '55672'
-
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        get_max_pair_stake_amount=MagicMock(return_value=500),
-    )
-    freqtrade.exchange.get_max_pair_stake_amount = MagicMock(return_value=500)
-
-    assert freqtrade.execute_entry(pair, 2000, is_short=is_short)
-    trade = Trade.query.all()[9]
-    trade.is_short = is_short
-    assert pytest.approx(trade.stake_amount) == 500
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-def test_execute_entry_confirm_error(mocker, default_conf_usdt, fee, limit_order, is_short) -> None:
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=MagicMock(return_value={
-            'bid': 1.9,
-            'ask': 2.2,
-            'last': 1.9
-        }),
-        create_order=MagicMock(return_value=limit_order[enter_side(is_short)]),
-        get_rate=MagicMock(return_value=0.11),
-        get_min_pair_stake_amount=MagicMock(return_value=1),
-        get_fee=fee,
-    )
-    stake_amount = 2
-    pair = 'ETH/USDT'
-
-    freqtrade.strategy.confirm_trade_entry = MagicMock(side_effect=ValueError)
-    assert freqtrade.execute_entry(pair, stake_amount)
-
-    limit_order[enter_side(is_short)]['id'] = '222'
-    freqtrade.strategy.confirm_trade_entry = MagicMock(side_effect=Exception)
-    assert freqtrade.execute_entry(pair, stake_amount)
-
-    limit_order[enter_side(is_short)]['id'] = '2223'
-    freqtrade.strategy.confirm_trade_entry = MagicMock(return_value=True)
-    assert freqtrade.execute_entry(pair, stake_amount)
-
-    freqtrade.strategy.confirm_trade_entry = MagicMock(return_value=False)
-    assert not freqtrade.execute_entry(pair, stake_amount)
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-def test_execute_entry_min_leverage(mocker, default_conf_usdt, fee, limit_order, is_short) -> None:
-    default_conf_usdt['trading_mode'] = 'futures'
-    default_conf_usdt['margin_mode'] = 'isolated'
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=MagicMock(return_value={
-            'bid': 1.9,
-            'ask': 2.2,
-            'last': 1.9
-        }),
-        create_order=MagicMock(return_value=limit_order[enter_side(is_short)]),
-        get_rate=MagicMock(return_value=0.11),
-        # Minimum stake-amount is ~5$
-        get_maintenance_ratio_and_amt=MagicMock(return_value=(0.0, 0.0)),
-        _fetch_and_calculate_funding_fees=MagicMock(return_value=0),
-        get_fee=fee,
-        get_max_leverage=MagicMock(return_value=5.0),
-    )
-    stake_amount = 2
-    pair = 'SOL/BUSD:BUSD'
-    freqtrade.strategy.leverage = MagicMock(return_value=5.0)
-
-    assert freqtrade.execute_entry(pair, stake_amount, is_short=is_short)
-    trade = Trade.query.first()
-    assert trade.leverage == 5.0
-    # assert trade.stake_amount == 2
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-def test_add_stoploss_on_exchange(mocker, default_conf_usdt, limit_order, is_short) -> None:
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    order = limit_order[enter_side(is_short)]
-    mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_trade', MagicMock(return_value=True))
-    mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=order)
-    mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=[])
-    mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount',
-                 return_value=order['amount'])
-
-    stoploss = MagicMock(return_value={'id': 13434334})
-    mocker.patch('freqtrade.exchange.Binance.stoploss', stoploss)
-
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    freqtrade.strategy.order_types['stoploss_on_exchange'] = True
-
-    trade = MagicMock()
-    trade.is_short = is_short
-    trade.open_order_id = None
-    trade.stoploss_order_id = None
-    trade.is_open = True
-    trades = [trade]
-
-    freqtrade.exit_positions(trades)
-    assert trade.stoploss_order_id == '13434334'
-    assert stoploss.call_count == 1
-    assert trade.is_open is True
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-def test_handle_stoploss_on_exchange(mocker, default_conf_usdt, fee, caplog, is_short,
-                                     limit_order) -> None:
-    stoploss = MagicMock(return_value={'id': 13434334})
-    enter_order = limit_order[enter_side(is_short)]
-    exit_order = limit_order[exit_side(is_short)]
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=MagicMock(return_value={
-            'bid': 1.9,
-            'ask': 2.2,
-            'last': 1.9
-        }),
-        create_order=MagicMock(side_effect=[
-            {'id': enter_order['id']},
-            exit_order,
-        ]),
-        get_fee=fee,
-        stoploss=stoploss
-    )
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
-
-    # First case: when stoploss is not yet set but the order is open
-    # should get the stoploss order id immediately
-    # and should return false as no trade actually happened
-    trade = MagicMock()
-    trade.is_short = is_short
-    trade.is_open = True
-    trade.open_order_id = None
-    trade.stoploss_order_id = None
-
-    assert freqtrade.handle_stoploss_on_exchange(trade) is False
-    assert stoploss.call_count == 1
-    assert trade.stoploss_order_id == "13434334"
-
-    # Second case: when stoploss is set but it is not yet hit
-    # should do nothing and return false
-    trade.is_open = True
-    trade.open_order_id = None
-    trade.stoploss_order_id = 100
-
-    hanging_stoploss_order = MagicMock(return_value={'status': 'open'})
-    mocker.patch('freqtrade.exchange.Exchange.fetch_stoploss_order', hanging_stoploss_order)
-
-    assert freqtrade.handle_stoploss_on_exchange(trade) is False
-    assert trade.stoploss_order_id == 100
-
-    # Third case: when stoploss was set but it was canceled for some reason
-    # should set a stoploss immediately and return False
-    caplog.clear()
-    trade.is_open = True
-    trade.open_order_id = None
-    trade.stoploss_order_id = 100
-
-    canceled_stoploss_order = MagicMock(return_value={'status': 'canceled'})
-    mocker.patch('freqtrade.exchange.Exchange.fetch_stoploss_order', canceled_stoploss_order)
-    stoploss.reset_mock()
-
-    assert freqtrade.handle_stoploss_on_exchange(trade) is False
-    assert stoploss.call_count == 1
-    assert trade.stoploss_order_id == "13434334"
-
-    # Fourth case: when stoploss is set and it is hit
-    # should unset stoploss_order_id and return true
-    # as a trade actually happened
-    caplog.clear()
-    freqtrade.enter_positions()
-    trade = Trade.query.first()
-    trade.is_short = is_short
-    trade.is_open = True
-    trade.open_order_id = None
-    trade.stoploss_order_id = "100"
-    trade.orders.append(Order(
-        ft_order_side='stoploss',
-        order_id='100',
-        ft_pair=trade.pair,
-        ft_is_open=True,
-    ))
-    assert trade
-
-    stoploss_order_hit = MagicMock(return_value={
-        'id': "100",
-        'status': 'closed',
-        'type': 'stop_loss_limit',
-        'price': 3,
-        'average': 2,
-        'amount': enter_order['amount'],
-    })
-    mocker.patch('freqtrade.exchange.Exchange.fetch_stoploss_order', stoploss_order_hit)
-    assert freqtrade.handle_stoploss_on_exchange(trade) is True
-    assert log_has_re(r'STOP_LOSS_LIMIT is hit for Trade\(id=1, .*\)\.', caplog)
-    assert trade.stoploss_order_id is None
-    assert trade.is_open is False
-    caplog.clear()
-
-    mocker.patch(
-        'freqtrade.exchange.Exchange.stoploss',
-        side_effect=ExchangeError()
-    )
-    trade.is_open = True
-    freqtrade.handle_stoploss_on_exchange(trade)
-    assert log_has('Unable to place a stoploss order on exchange.', caplog)
-    assert trade.stoploss_order_id is None
-
-    # Fifth case: fetch_order returns InvalidOrder
-    # It should try to add stoploss order
-    trade.stoploss_order_id = 100
-    stoploss.reset_mock()
-    mocker.patch('freqtrade.exchange.Exchange.fetch_stoploss_order',
-                 side_effect=InvalidOrderException())
-    mocker.patch('freqtrade.exchange.Exchange.stoploss', stoploss)
-    freqtrade.handle_stoploss_on_exchange(trade)
-    assert stoploss.call_count == 1
-
-    # Sixth case: Closed Trade
-    # Should not create new order
-    trade.stoploss_order_id = None
-    trade.is_open = False
-    stoploss.reset_mock()
-    mocker.patch('freqtrade.exchange.Exchange.fetch_order')
-    mocker.patch('freqtrade.exchange.Exchange.stoploss', stoploss)
-    assert freqtrade.handle_stoploss_on_exchange(trade) is False
-    assert stoploss.call_count == 0
-
-    # Seventh case: emergency exit triggered
-    # Trailing stop should not act anymore
-    stoploss_order_cancelled = MagicMock(side_effect=[{
-        'id': "100",
-        'status': 'canceled',
-        'type': 'stop_loss_limit',
-        'price': 3,
-        'average': 2,
-        'amount': enter_order['amount'],
-        'info': {'stopPrice': 22},
-    }])
-    trade.stoploss_order_id = 100
-    trade.is_open = True
-    trade.stoploss_last_update = arrow.utcnow().shift(hours=-1).datetime
-    trade.stop_loss = 24
-    trade.amount = limit_buy_order_usdt['amount']
-    freqtrade.config['trailing_stop'] = True
-    stoploss = MagicMock(side_effect=InvalidOrderException())
-
-    mocker.patch('freqtrade.exchange.Exchange.cancel_stoploss_order_with_result',
-                 side_effect=InvalidOrderException())
-    mocker.patch('freqtrade.exchange.Exchange.fetch_stoploss_order', stoploss_order_cancelled)
-    mocker.patch('freqtrade.exchange.Exchange.stoploss', stoploss)
-    assert freqtrade.handle_stoploss_on_exchange(trade) is False
-    assert trade.stoploss_order_id is None
-    assert trade.is_open is False
-    assert trade.exit_reason == str(ExitType.EMERGENCY_SELL)
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-def test_handle_sle_cancel_cant_recreate(mocker, default_conf_usdt, fee, caplog, is_short,
-                                         limit_order) -> None:
-    # Sixth case: stoploss order was cancelled but couldn't create new one
-    enter_order = limit_order[enter_side(is_short)]
-    exit_order = limit_order[exit_side(is_short)]
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=MagicMock(return_value={
-            'bid': 1.9,
-            'ask': 2.2,
-            'last': 1.9
-        }),
-        create_order=MagicMock(side_effect=[
-            {'id': enter_order['id']},
-            {'id': exit_order['id']},
-        ]),
-        get_fee=fee,
-    )
-    mocker.patch.multiple(
-        'freqtrade.exchange.Binance',
-        fetch_stoploss_order=MagicMock(return_value={'status': 'canceled', 'id': 100}),
-        stoploss=MagicMock(side_effect=ExchangeError()),
-    )
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
-
-    freqtrade.enter_positions()
-    trade = Trade.query.first()
-    trade.is_short = is_short
-    trade.is_open = True
-    trade.open_order_id = None
-    trade.stoploss_order_id = 100
-    assert trade
-
-    assert freqtrade.handle_stoploss_on_exchange(trade) is False
-    assert log_has_re(r'Stoploss order was cancelled, but unable to recreate one.*', caplog)
-    assert trade.stoploss_order_id is None
-    assert trade.is_open is True
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-def test_create_stoploss_order_invalid_order(
-    mocker, default_conf_usdt, caplog, fee, is_short, limit_order, limit_order_open
-):
-    open_order = limit_order_open[enter_side(is_short)]
-    order = limit_order[exit_side(is_short)]
-    rpc_mock = patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    create_order_mock = MagicMock(side_effect=[
-        open_order,
-        {'id': order['id']}
-    ])
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=MagicMock(return_value={
-            'bid': 1.9,
-            'ask': 2.2,
-            'last': 1.9
-        }),
-        create_order=create_order_mock,
-        get_fee=fee,
-    )
-    mocker.patch.multiple(
-        'freqtrade.exchange.Binance',
-        fetch_order=MagicMock(return_value={'status': 'canceled'}),
-        stoploss=MagicMock(side_effect=InvalidOrderException()),
-    )
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
-    freqtrade.strategy.order_types['stoploss_on_exchange'] = True
-
-    freqtrade.enter_positions()
-    trade = Trade.query.first()
-    trade.is_short = is_short
-    caplog.clear()
-    freqtrade.create_stoploss_order(trade, 200)
-    assert trade.stoploss_order_id is None
-    assert trade.exit_reason == ExitType.EMERGENCY_SELL.value
-    assert log_has("Unable to place a stoploss order on exchange. ", caplog)
-    assert log_has("Exiting the trade forcefully", caplog)
-
-    # Should call a market sell
-    assert create_order_mock.call_count == 2
-    assert create_order_mock.call_args[1]['ordertype'] == 'market'
-    assert create_order_mock.call_args[1]['pair'] == trade.pair
-    assert create_order_mock.call_args[1]['amount'] == trade.amount
-
-    # Rpc is sending first buy, then sell
-    assert rpc_mock.call_count == 2
-    assert rpc_mock.call_args_list[1][0][0]['sell_reason'] == ExitType.EMERGENCY_SELL.value
-    assert rpc_mock.call_args_list[1][0][0]['order_type'] == 'market'
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-def test_create_stoploss_order_insufficient_funds(
-    mocker, default_conf_usdt, caplog, fee, limit_order, is_short
-):
-    exit_order = limit_order[exit_side(is_short)]['id']
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-
-    mock_insuf = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_insufficient_funds')
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=MagicMock(return_value={
-            'bid': 1.9,
-            'ask': 2.2,
-            'last': 1.9
-        }),
-        create_order=MagicMock(side_effect=[
-            limit_order[enter_side(is_short)],
-            exit_order,
-        ]),
-        get_fee=fee,
-        fetch_order=MagicMock(return_value={'status': 'canceled'}),
-    )
-    mocker.patch.multiple(
-        'freqtrade.exchange.Binance',
-        stoploss=MagicMock(side_effect=InsufficientFundsError()),
-    )
-    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
-    freqtrade.strategy.order_types['stoploss_on_exchange'] = True
-
-    freqtrade.enter_positions()
-    trade = Trade.query.first()
-    trade.is_short = is_short
-    caplog.clear()
-    freqtrade.create_stoploss_order(trade, 200)
-    # stoploss_orderid was empty before
-    assert trade.stoploss_order_id is None
-    assert mock_insuf.call_count == 1
-    mock_insuf.reset_mock()
-
-    trade.stoploss_order_id = 'stoploss_orderid'
-    freqtrade.create_stoploss_order(trade, 200)
-    # No change to stoploss-orderid
-    assert trade.stoploss_order_id == 'stoploss_orderid'
-    assert mock_insuf.call_count == 1
-
-
-@pytest.mark.parametrize("is_short,bid,ask,stop_price,amt,hang_price", [
-    (False, [4.38, 4.16], [4.4, 4.17], ['2.0805', 4.4 * 0.95], 27.39726027, 3),
-    (True, [1.09, 1.21], [1.1, 1.22], ['2.321', 1.09 * 1.05], 27.27272727, 1.5),
-])
-@pytest.mark.usefixtures("init_persistence")
-def test_handle_stoploss_on_exchange_trailing(
-    mocker, default_conf_usdt, fee, is_short, bid, ask, limit_order, stop_price, amt, hang_price
-) -> None:
-    # When trailing stoploss is set
-    enter_order = limit_order[enter_side(is_short)]
-    exit_order = limit_order[exit_side(is_short)]
-    stoploss = MagicMock(return_value={'id': 13434334})
-    patch_RPCManager(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=MagicMock(return_value={
-            'bid': 2.19,
-            'ask': 2.2,
-            'last': 2.19,
-        }),
-        create_order=MagicMock(side_effect=[
-            {'id': enter_order['id']},
-            {'id': exit_order['id']},
-        ]),
-        get_fee=fee,
-    )
-    mocker.patch.multiple(
-        'freqtrade.exchange.Binance',
-        stoploss=stoploss,
-        stoploss_adjust=MagicMock(return_value=True),
-    )
-
-    # enabling TSL
-    default_conf_usdt['trailing_stop'] = True
-
-    # disabling ROI
-    default_conf_usdt['minimal_roi']['0'] = 999999999
-
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-
-    # enabling stoploss on exchange
-    freqtrade.strategy.order_types['stoploss_on_exchange'] = True
-
-    # setting stoploss
-    freqtrade.strategy.stoploss = 0.05 if is_short else -0.05
-
-    # setting stoploss_on_exchange_interval to 60 seconds
-    freqtrade.strategy.order_types['stoploss_on_exchange_interval'] = 60
-
-    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
-
-    freqtrade.enter_positions()
-    trade = Trade.query.first()
-    trade.is_short = is_short
-    trade.is_open = True
-    trade.open_order_id = None
-    trade.stoploss_order_id = 100
-
-    stoploss_order_hanging = MagicMock(return_value={
-        'id': 100,
-        'status': 'open',
-        'type': 'stop_loss_limit',
-        'price': hang_price,
-        'average': 2,
-        'info': {
-            'stopPrice': stop_price[0]
-        }
-    })
-
-    mocker.patch('freqtrade.exchange.Binance.fetch_stoploss_order', stoploss_order_hanging)
-
-    # stoploss initially at 5%
-    assert freqtrade.handle_trade(trade) is False
-    assert freqtrade.handle_stoploss_on_exchange(trade) is False
-
-    # price jumped 2x
-    mocker.patch(
-        'freqtrade.exchange.Exchange.fetch_ticker',
-        MagicMock(return_value={
-            'bid': bid[0],
-            'ask': ask[0],
-            'last': bid[0],
-        })
-    )
-
-    cancel_order_mock = MagicMock()
-    stoploss_order_mock = MagicMock(return_value={'id': 13434334})
-    mocker.patch('freqtrade.exchange.Binance.cancel_stoploss_order', cancel_order_mock)
-    mocker.patch('freqtrade.exchange.Binance.stoploss', stoploss_order_mock)
-
-    # stoploss should not be updated as the interval is 60 seconds
-    assert freqtrade.handle_trade(trade) is False
-    assert freqtrade.handle_stoploss_on_exchange(trade) is False
-    cancel_order_mock.assert_not_called()
-    stoploss_order_mock.assert_not_called()
-
-    assert freqtrade.handle_trade(trade) is False
-    assert trade.stop_loss == stop_price[1]
-
-    # setting stoploss_on_exchange_interval to 0 seconds
-    freqtrade.strategy.order_types['stoploss_on_exchange_interval'] = 0
-
-    assert freqtrade.handle_stoploss_on_exchange(trade) is False
-
-    cancel_order_mock.assert_called_once_with(100, 'ETH/USDT')
-    stoploss_order_mock.assert_called_once_with(
-        amount=pytest.approx(amt),
-        pair='ETH/USDT',
-        order_types=freqtrade.strategy.order_types,
-        stop_price=stop_price[1],
-        side=exit_side(is_short),
-        leverage=1.0
-    )
-
-    # price fell below stoploss, so dry-run sells trade.
-    mocker.patch(
-        'freqtrade.exchange.Exchange.fetch_ticker',
-        MagicMock(return_value={
-            'bid': bid[1],
-            'ask': ask[1],
-            'last': bid[1],
-        })
-    )
-    assert freqtrade.handle_trade(trade) is True
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-def test_handle_stoploss_on_exchange_trailing_error(
-    mocker, default_conf_usdt, fee, caplog, limit_order, is_short
-) -> None:
-    enter_order = limit_order[enter_side(is_short)]
-    exit_order = limit_order[exit_side(is_short)]
-    # When trailing stoploss is set
-    stoploss = MagicMock(return_value={'id': 13434334})
-    patch_exchange(mocker)
-
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=MagicMock(return_value={
-            'bid': 1.9,
-            'ask': 2.2,
-            'last': 1.9
-        }),
-        create_order=MagicMock(side_effect=[
-            {'id': enter_order['id']},
-            {'id': exit_order['id']},
-        ]),
-        get_fee=fee,
-    )
-    mocker.patch.multiple(
-        'freqtrade.exchange.Binance',
-        stoploss=stoploss,
-        stoploss_adjust=MagicMock(return_value=True),
-    )
-
-    # enabling TSL
-    default_conf_usdt['trailing_stop'] = True
-
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-    # enabling stoploss on exchange
-    freqtrade.strategy.order_types['stoploss_on_exchange'] = True
-
-    # setting stoploss
-    freqtrade.strategy.stoploss = 0.05 if is_short else -0.05
-
-    # setting stoploss_on_exchange_interval to 60 seconds
-    freqtrade.strategy.order_types['stoploss_on_exchange_interval'] = 60
-    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
-    freqtrade.enter_positions()
-    trade = Trade.query.first()
-    trade.is_short = is_short
-    trade.is_open = True
-    trade.open_order_id = None
-    trade.stoploss_order_id = "abcd"
-    trade.stop_loss = 0.2
-    trade.stoploss_last_update = arrow.utcnow().shift(minutes=-601).datetime.replace(tzinfo=None)
-    trade.is_short = is_short
-
-    stoploss_order_hanging = {
-        'id': "abcd",
-        'status': 'open',
-        'type': 'stop_loss_limit',
-        'price': 3,
-        'average': 2,
-        'info': {
-            'stopPrice': '0.1'
-        }
-    }
-    mocker.patch('freqtrade.exchange.Binance.cancel_stoploss_order',
-                 side_effect=InvalidOrderException())
-    mocker.patch('freqtrade.exchange.Binance.fetch_stoploss_order',
-                 return_value=stoploss_order_hanging)
-    freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging)
-    assert log_has_re(r"Could not cancel stoploss order abcd for pair ETH/USDT.*", caplog)
-
-    # Still try to create order
-    assert stoploss.call_count == 1
-
-    # Fail creating stoploss order
-    caplog.clear()
-    cancel_mock = mocker.patch("freqtrade.exchange.Binance.cancel_stoploss_order", MagicMock())
-    mocker.patch("freqtrade.exchange.Binance.stoploss", side_effect=ExchangeError())
-    freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging)
-    assert cancel_mock.call_count == 1
-    assert log_has_re(r"Could not create trailing stoploss order for pair ETH/USDT\..*", caplog)
-
-
-def test_stoploss_on_exchange_price_rounding(
-        mocker, default_conf_usdt, fee, open_trade_usdt) -> None:
-    patch_RPCManager(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        get_fee=fee,
-    )
-    price_mock = MagicMock(side_effect=lambda p, s: int(s))
-    stoploss_mock = MagicMock(return_value={'id': '13434334'})
-    adjust_mock = MagicMock(return_value=False)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Binance',
-        stoploss=stoploss_mock,
-        stoploss_adjust=adjust_mock,
-        price_to_precision=price_mock,
-    )
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-    open_trade_usdt.stoploss_order_id = '13434334'
-    open_trade_usdt.stop_loss = 222.55
-
-    freqtrade.handle_trailing_stoploss_on_exchange(open_trade_usdt, {})
-    assert price_mock.call_count == 1
-    assert adjust_mock.call_count == 1
-    assert adjust_mock.call_args_list[0][0][0] == 222
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-@pytest.mark.usefixtures("init_persistence")
-def test_handle_stoploss_on_exchange_custom_stop(
-    mocker, default_conf_usdt, fee, is_short, limit_order
-) -> None:
-    enter_order = limit_order[enter_side(is_short)]
-    exit_order = limit_order[exit_side(is_short)]
-    # When trailing stoploss is set
-    stoploss = MagicMock(return_value={'id': 13434334})
-    patch_RPCManager(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=MagicMock(return_value={
-            'bid': 1.9,
-            'ask': 2.2,
-            'last': 1.9
-        }),
-        create_order=MagicMock(side_effect=[
-            {'id': enter_order['id']},
-            {'id': exit_order['id']},
-        ]),
-        get_fee=fee,
-    )
-    mocker.patch.multiple(
-        'freqtrade.exchange.Binance',
-        stoploss=stoploss,
-        stoploss_adjust=MagicMock(return_value=True),
-    )
-
-    # enabling TSL
-    default_conf_usdt['use_custom_stoploss'] = True
-
-    # disabling ROI
-    default_conf_usdt['minimal_roi']['0'] = 999999999
-
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-
-    # enabling stoploss on exchange
-    freqtrade.strategy.order_types['stoploss_on_exchange'] = True
-
-    # setting stoploss
-    freqtrade.strategy.custom_stoploss = lambda *args, **kwargs: -0.04
-
-    # setting stoploss_on_exchange_interval to 60 seconds
-    freqtrade.strategy.order_types['stoploss_on_exchange_interval'] = 60
-
-    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
-
-    freqtrade.enter_positions()
-    trade = Trade.query.first()
-    trade.is_short = is_short
-    trade.is_open = True
-    trade.open_order_id = None
-    trade.stoploss_order_id = 100
-
-    stoploss_order_hanging = MagicMock(return_value={
-        'id': 100,
-        'status': 'open',
-        'type': 'stop_loss_limit',
-        'price': 3,
-        'average': 2,
-        'info': {
-            'stopPrice': '2.0805'
-        }
-    })
-
-    mocker.patch('freqtrade.exchange.Binance.fetch_stoploss_order', stoploss_order_hanging)
-
-    assert freqtrade.handle_trade(trade) is False
-    assert freqtrade.handle_stoploss_on_exchange(trade) is False
-
-    # price jumped 2x
-    mocker.patch(
-        'freqtrade.exchange.Exchange.fetch_ticker',
-        MagicMock(return_value={
-            'bid': 4.38 if not is_short else 1.9 / 2,
-            'ask': 4.4 if not is_short else 2.2 / 2,
-            'last': 4.38 if not is_short else 1.9 / 2,
-        })
-    )
-
-    cancel_order_mock = MagicMock()
-    stoploss_order_mock = MagicMock(return_value={'id': 13434334})
-    mocker.patch('freqtrade.exchange.Binance.cancel_stoploss_order', cancel_order_mock)
-    mocker.patch('freqtrade.exchange.Binance.stoploss', stoploss_order_mock)
-
-    # stoploss should not be updated as the interval is 60 seconds
-    assert freqtrade.handle_trade(trade) is False
-    assert freqtrade.handle_stoploss_on_exchange(trade) is False
-    cancel_order_mock.assert_not_called()
-    stoploss_order_mock.assert_not_called()
-
-    assert freqtrade.handle_trade(trade) is False
-    assert trade.stop_loss == 4.4 * 0.96 if not is_short else 1.1
-    assert trade.stop_loss_pct == -0.04 if not is_short else 0.04
-
-    # setting stoploss_on_exchange_interval to 0 seconds
-    freqtrade.strategy.order_types['stoploss_on_exchange_interval'] = 0
-
-    assert freqtrade.handle_stoploss_on_exchange(trade) is False
-
-    cancel_order_mock.assert_called_once_with(100, 'ETH/USDT')
-    # Long uses modified ask - offset, short modified bid + offset
-    stoploss_order_mock.assert_called_once_with(
-        amount=pytest.approx(trade.amount),
-        pair='ETH/USDT',
-        order_types=freqtrade.strategy.order_types,
-        stop_price=4.4 * 0.96 if not is_short else 0.95 * 1.04,
-        side=exit_side(is_short),
-        leverage=1.0
-    )
-
-    # price fell below stoploss, so dry-run sells trade.
-    mocker.patch(
-        'freqtrade.exchange.Exchange.fetch_ticker',
-        MagicMock(return_value={
-            'bid': 4.17,
-            'ask': 4.19,
-            'last': 4.17
-        })
-    )
-    assert freqtrade.handle_trade(trade) is True
-
-
-def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog,
-                                              limit_order) -> None:
-
-    enter_order = limit_order['buy']
-    exit_order = limit_order['sell']
-
-    # When trailing stoploss is set
-    stoploss = MagicMock(return_value={'id': 13434334})
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    patch_edge(mocker)
-    edge_conf['max_open_trades'] = float('inf')
-    edge_conf['dry_run_wallet'] = 999.9
-    edge_conf['exchange']['name'] = 'binance'
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=MagicMock(return_value={
-            'bid': 2.19,
-            'ask': 2.2,
-            'last': 2.19
-        }),
-        create_order=MagicMock(side_effect=[
-            {'id': enter_order['id']},
-            {'id': exit_order['id']},
-        ]),
-        get_fee=fee,
-        stoploss=stoploss,
-    )
-
-    # enabling TSL
-    edge_conf['trailing_stop'] = True
-    edge_conf['trailing_stop_positive'] = 0.01
-    edge_conf['trailing_stop_positive_offset'] = 0.011
-
-    # disabling ROI
-    edge_conf['minimal_roi']['0'] = 999999999
-
-    freqtrade = FreqtradeBot(edge_conf)
-
-    # enabling stoploss on exchange
-    freqtrade.strategy.order_types['stoploss_on_exchange'] = True
-
-    # setting stoploss
-    freqtrade.strategy.stoploss = -0.02
-
-    # setting stoploss_on_exchange_interval to 0 seconds
-    freqtrade.strategy.order_types['stoploss_on_exchange_interval'] = 0
-
-    patch_get_signal(freqtrade)
-
-    freqtrade.active_pair_whitelist = freqtrade.edge.adjust(freqtrade.active_pair_whitelist)
-
-    freqtrade.enter_positions()
-    trade = Trade.query.first()
-    trade.is_open = True
-    trade.open_order_id = None
-    trade.stoploss_order_id = 100
-
-    stoploss_order_hanging = MagicMock(return_value={
-        'id': 100,
-        'status': 'open',
-        'type': 'stop_loss_limit',
-        'price': 3,
-        'average': 2,
-        'info': {
-            'stopPrice': '2.178'
-        }
-    })
-
-    mocker.patch('freqtrade.exchange.Exchange.fetch_stoploss_order', stoploss_order_hanging)
-
-    # stoploss initially at 20% as edge dictated it.
-    assert freqtrade.handle_trade(trade) is False
-    assert freqtrade.handle_stoploss_on_exchange(trade) is False
-    assert isclose(trade.stop_loss, 1.76)
-
-    cancel_order_mock = MagicMock()
-    stoploss_order_mock = MagicMock()
-    mocker.patch('freqtrade.exchange.Exchange.cancel_stoploss_order', cancel_order_mock)
-    mocker.patch('freqtrade.exchange.Binance.stoploss', stoploss_order_mock)
-
-    # price goes down 5%
-    mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={
-        'bid': 2.19 * 0.95,
-        'ask': 2.2 * 0.95,
-        'last': 2.19 * 0.95
-    }))
-    assert freqtrade.handle_trade(trade) is False
-    assert freqtrade.handle_stoploss_on_exchange(trade) is False
-
-    # stoploss should remain the same
-    assert isclose(trade.stop_loss, 1.76)
-
-    # stoploss on exchange should not be canceled
-    cancel_order_mock.assert_not_called()
-
-    # price jumped 2x
-    mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={
-        'bid': 4.38,
-        'ask': 4.4,
-        'last': 4.38
-    }))
-
-    assert freqtrade.handle_trade(trade) is False
-    assert freqtrade.handle_stoploss_on_exchange(trade) is False
-
-    # stoploss should be set to 1% as trailing is on
-    assert trade.stop_loss == 4.4 * 0.99
-    cancel_order_mock.assert_called_once_with(100, 'NEO/BTC')
-    stoploss_order_mock.assert_called_once_with(
-        amount=pytest.approx(11.41438356),
-        pair='NEO/BTC',
-        order_types=freqtrade.strategy.order_types,
-        stop_price=4.4 * 0.99,
-        side='sell',
-        leverage=1.0
-    )
-
-
-@pytest.mark.parametrize('return_value,side_effect,log_message', [
-    (False, None, 'Found no enter signals for whitelisted currencies. Trying again...'),
-    (None, DependencyException, 'Unable to create trade for ETH/USDT: ')
-])
-def test_enter_positions(mocker, default_conf_usdt, return_value, side_effect,
-                         log_message, caplog) -> None:
-    caplog.set_level(logging.DEBUG)
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-
-    mock_ct = mocker.patch(
-        'freqtrade.freqtradebot.FreqtradeBot.create_trade',
-        MagicMock(
-            return_value=return_value,
-            side_effect=side_effect
-        )
-    )
-    n = freqtrade.enter_positions()
-    assert n == 0
-    assert log_has(log_message, caplog)
-    # create_trade should be called once for every pair in the whitelist.
-    assert mock_ct.call_count == len(default_conf_usdt['exchange']['pair_whitelist'])
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-def test_exit_positions(mocker, default_conf_usdt, limit_order, is_short, caplog) -> None:
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-
-    mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_trade', MagicMock(return_value=True))
-    mocker.patch('freqtrade.exchange.Exchange.fetch_order',
-                 return_value=limit_order[enter_side(is_short)])
-    mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=[])
-    mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount',
-                 return_value=limit_order[enter_side(is_short)]['amount'])
-
-    trade = MagicMock()
-    trade.is_short = is_short
-    trade.open_order_id = '123'
-    trade.open_fee = 0.001
-    trades = [trade]
-    n = freqtrade.exit_positions(trades)
-    assert n == 0
-    # Test amount not modified by fee-logic
-    assert not log_has(
-        'Applying fee to amount for Trade {} from 30.0 to 90.81'.format(trade), caplog
-    )
-
-    mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', return_value=90.81)
-    # test amount modified by fee-logic
-    n = freqtrade.exit_positions(trades)
-    assert n == 0
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-def test_exit_positions_exception(mocker, default_conf_usdt, limit_order, caplog, is_short) -> None:
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-    order = limit_order[enter_side(is_short)]
-    mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=order)
-
-    trade = MagicMock()
-    trade.is_short = is_short
-    trade.open_order_id = None
-    trade.pair = 'ETH/USDT'
-    trades = [trade]
-
-    # Test raise of DependencyException exception
-    mocker.patch(
-        'freqtrade.freqtradebot.FreqtradeBot.handle_trade',
-        side_effect=DependencyException()
-    )
-    caplog.clear()
-    n = freqtrade.exit_positions(trades)
-    assert n == 0
-    assert log_has('Unable to exit trade ETH/USDT: ', caplog)
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-def test_update_trade_state(mocker, default_conf_usdt, limit_order, is_short, caplog) -> None:
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-    order = limit_order[enter_side(is_short)]
-
-    mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_trade', MagicMock(return_value=True))
-    mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=order)
-    mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=[])
-    mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount',
-                 return_value=order['amount'])
-    order_id = order['id']
-
-    trade = Trade(
-        open_order_id=order_id,
-        fee_open=0.001,
-        fee_close=0.001,
-        open_rate=0.01,
-        open_date=arrow.utcnow().datetime,
-        amount=11,
-        exchange="binance",
-        is_short=is_short,
-        leverage=1,
-    )
-    trade.orders.append(Order(
-        ft_order_side=enter_side(is_short),
-        price=0.01,
-        order_id=order_id,
-
-    ))
-    assert not freqtrade.update_trade_state(trade, None)
-    assert log_has_re(r'Orderid for trade .* is empty.', caplog)
-    caplog.clear()
-    # Add datetime explicitly since sqlalchemy defaults apply only once written to database
-    freqtrade.update_trade_state(trade, order_id)
-    # Test amount not modified by fee-logic
-    assert not log_has_re(r'Applying fee to .*', caplog)
-    caplog.clear()
-    assert trade.open_order_id is None
-    assert trade.amount == order['amount']
-
-    trade.open_order_id = order_id
-    mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', return_value=90.81)
-    assert trade.amount != 90.81
-    # test amount modified by fee-logic
-    freqtrade.update_trade_state(trade, order_id)
-    assert trade.amount == 90.81
-    assert trade.open_order_id is None
-
-    trade.is_open = True
-    trade.open_order_id = None
-    # Assert we call handle_trade() if trade is feasible for execution
-    freqtrade.update_trade_state(trade, order_id)
-
-    assert log_has_re('Found open order for.*', caplog)
-    limit_buy_order_usdt_new = deepcopy(limit_order)
-    limit_buy_order_usdt_new['filled'] = 0.0
-    limit_buy_order_usdt_new['status'] = 'canceled'
-
-    mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', side_effect=ValueError)
-    mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=limit_buy_order_usdt_new)
-    res = freqtrade.update_trade_state(trade, order_id)
-    # Cancelled empty
-    assert res is True
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-@pytest.mark.parametrize('initial_amount,has_rounding_fee', [
-    (30.0 + 1e-14, True),
-    (8.0, False)
-])
-def test_update_trade_state_withorderdict(
-    default_conf_usdt, trades_for_order, limit_order, fee, mocker, initial_amount,
-    has_rounding_fee, is_short, caplog
-):
-    order = limit_order[enter_side(is_short)]
-    trades_for_order[0]['amount'] = initial_amount
-    order_id = "oid_123456"
-    order['id'] = order_id
-    mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order)
-    # fetch_order should not be called!!
-    mocker.patch('freqtrade.exchange.Exchange.fetch_order', MagicMock(side_effect=ValueError))
-    patch_exchange(mocker)
-    amount = sum(x['amount'] for x in trades_for_order)
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-    caplog.clear()
-    trade = Trade(
-        pair='LTC/USDT',
-        amount=amount,
-        exchange='binance',
-        open_rate=2.0,
-        open_date=arrow.utcnow().datetime,
-        fee_open=fee.return_value,
-        fee_close=fee.return_value,
-        open_order_id=order_id,
-        is_open=True,
-        leverage=1,
-        is_short=is_short,
-    )
-    trade.orders.append(
-        Order(
-            ft_order_side=enter_side(is_short),
-            ft_pair=trade.pair,
-            ft_is_open=True,
-            order_id=order_id,
-        )
-    )
-    log_text = r'Applying fee on amount for .*'
-    freqtrade.update_trade_state(trade, order_id, order)
-    assert trade.amount != amount
-    if has_rounding_fee:
-        assert pytest.approx(trade.amount) == 29.992
-        assert log_has_re(log_text, caplog)
-    else:
-        assert pytest.approx(trade.amount) == order['amount']
-        assert not log_has_re(log_text, caplog)
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-def test_update_trade_state_exception(mocker, default_conf_usdt, is_short, limit_order,
-                                      caplog) -> None:
-    order = limit_order[enter_side(is_short)]
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-    mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=order)
-
-    trade = MagicMock()
-    trade.open_order_id = '123'
-
-    # Test raise of OperationalException exception
-    mocker.patch(
-        'freqtrade.freqtradebot.FreqtradeBot.get_real_amount',
-        side_effect=DependencyException()
-    )
-    freqtrade.update_trade_state(trade, trade.open_order_id)
-    assert log_has('Could not update trade amount: ', caplog)
-
-
-def test_update_trade_state_orderexception(mocker, default_conf_usdt, caplog) -> None:
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-    mocker.patch('freqtrade.exchange.Exchange.fetch_order',
-                 MagicMock(side_effect=InvalidOrderException))
-
-    trade = MagicMock()
-    trade.open_order_id = '123'
-
-    # Test raise of OperationalException exception
-    grm_mock = mocker.patch("freqtrade.freqtradebot.FreqtradeBot.get_real_amount", MagicMock())
-    freqtrade.update_trade_state(trade, trade.open_order_id)
-    assert grm_mock.call_count == 0
-    assert log_has(f'Unable to fetch order {trade.open_order_id}: ', caplog)
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-def test_update_trade_state_sell(
-    default_conf_usdt, trades_for_order, limit_order_open, limit_order, is_short, mocker,
-):
-    open_order = limit_order_open[exit_side(is_short)]
-    l_order = limit_order[exit_side(is_short)]
-    mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order)
-    # fetch_order should not be called!!
-    mocker.patch('freqtrade.exchange.Exchange.fetch_order', MagicMock(side_effect=ValueError))
-    wallet_mock = MagicMock()
-    mocker.patch('freqtrade.wallets.Wallets.update', wallet_mock)
-
-    patch_exchange(mocker)
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-    amount = l_order["amount"]
-    wallet_mock.reset_mock()
-    trade = Trade(
-        pair='LTC/ETH',
-        amount=amount,
-        exchange='binance',
-        open_rate=0.245441,
-        fee_open=0.0025,
-        fee_close=0.0025,
-        open_date=arrow.utcnow().datetime,
-        open_order_id=open_order['id'],
-        is_open=True,
-        interest_rate=0.0005,
-        leverage=1,
-        is_short=is_short,
-    )
-    order = Order.parse_from_ccxt_object(open_order, 'LTC/ETH', exit_side(is_short))
-    trade.orders.append(order)
-    assert order.status == 'open'
-    freqtrade.update_trade_state(trade, trade.open_order_id, l_order)
-    assert trade.amount == l_order['amount']
-    # Wallet needs to be updated after closing a limit-sell order to reenable buying
-    assert wallet_mock.call_count == 1
-    assert not trade.is_open
-    # Order is updated by update_trade_state
-    assert order.status == 'closed'
-
-
-@pytest.mark.parametrize('is_short,close_profit', [
-    (False, 0.09451372),
-    (True, 0.08635224),
-])
-def test_handle_trade(
-    default_conf_usdt, limit_order_open, limit_order, fee, mocker, is_short, close_profit
-) -> None:
-    open_order = limit_order_open[exit_side(is_short)]
-    enter_order = limit_order[enter_side(is_short)]
-    exit_order = limit_order[exit_side(is_short)]
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=MagicMock(return_value={
-            'bid': 2.19,
-            'ask': 2.2,
-            'last': 2.19
-        }),
-        create_order=MagicMock(side_effect=[
-            enter_order,
-            open_order,
-        ]),
-        get_fee=fee,
-    )
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
-
-    freqtrade.enter_positions()
-
-    trade = Trade.query.first()
-    trade.is_short = is_short
-    assert trade
-
-    time.sleep(0.01)  # Race condition fix
-    oobj = Order.parse_from_ccxt_object(enter_order, enter_order['symbol'], enter_side(is_short))
-    trade.update_trade(oobj)
-    assert trade.is_open is True
-    freqtrade.wallets.update()
-
-    patch_get_signal(freqtrade, enter_long=False, exit_short=is_short,
-                     exit_long=not is_short, exit_tag='sell_signal1')
-    assert freqtrade.handle_trade(trade) is True
-    assert trade.open_order_id == exit_order['id']
-
-    # Simulate fulfilled LIMIT_SELL order for trade
-    oobj = Order.parse_from_ccxt_object(exit_order, exit_order['symbol'], exit_side(is_short))
-    trade.update_trade(oobj)
-
-    assert trade.close_rate == 2.0 if is_short else 2.2
-    assert trade.close_profit == close_profit
-    assert trade.calc_profit() == 5.685
-    assert trade.close_date is not None
-    assert trade.exit_reason == 'sell_signal1'
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-def test_handle_overlapping_signals(
-    default_conf_usdt, ticker_usdt, limit_order_open, fee, mocker, is_short
-) -> None:
-    open_order = limit_order_open[exit_side(is_short)]
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        create_order=MagicMock(side_effect=[
-            open_order,
-            {'id': 1234553382},
-        ]),
-        get_fee=fee,
-    )
-
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    if is_short:
-        patch_get_signal(freqtrade, enter_long=False, enter_short=True, exit_short=True)
-    else:
-        patch_get_signal(freqtrade, enter_long=True, exit_long=True)
-    freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
-
-    freqtrade.enter_positions()
-
-    # Buy and Sell triggering, so doing nothing ...
-    trades = Trade.query.all()
-
-    nb_trades = len(trades)
-    assert nb_trades == 0
-
-    # Buy is triggering, so buying ...
-    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
-    freqtrade.enter_positions()
-    trades = Trade.query.all()
-    for trade in trades:
-        trade.is_short = is_short
-    nb_trades = len(trades)
-    assert nb_trades == 1
-    assert trades[0].is_open is True
-
-    # Buy and Sell are not triggering, so doing nothing ...
-    patch_get_signal(freqtrade, enter_long=False)
-    assert freqtrade.handle_trade(trades[0]) is False
-    trades = Trade.query.all()
-    for trade in trades:
-        trade.is_short = is_short
-    nb_trades = len(trades)
-    assert nb_trades == 1
-    assert trades[0].is_open is True
-
-    # Buy and Sell are triggering, so doing nothing ...
-    if is_short:
-        patch_get_signal(freqtrade, enter_long=False, enter_short=True, exit_short=True)
-    else:
-        patch_get_signal(freqtrade, enter_long=True, exit_long=True)
-    assert freqtrade.handle_trade(trades[0]) is False
-    trades = Trade.query.all()
-    for trade in trades:
-        trade.is_short = is_short
-    nb_trades = len(trades)
-    assert nb_trades == 1
-    assert trades[0].is_open is True
-
-    # Sell is triggering, guess what : we are Selling!
-    if is_short:
-        patch_get_signal(freqtrade, enter_long=False, exit_short=True)
-    else:
-        patch_get_signal(freqtrade, enter_long=False, exit_long=True)
-    trades = Trade.query.all()
-    for trade in trades:
-        trade.is_short = is_short
-    assert freqtrade.handle_trade(trades[0]) is True
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-def test_handle_trade_roi(default_conf_usdt, ticker_usdt, limit_order_open, fee, mocker, caplog,
-                          is_short) -> None:
-
-    open_order = limit_order_open[enter_side(is_short)]
-
-    caplog.set_level(logging.DEBUG)
-
-    patch_RPCManager(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        create_order=MagicMock(side_effect=[
-            open_order,
-            {'id': 1234553382},
-        ]),
-        get_fee=fee,
-    )
-
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
-    freqtrade.strategy.min_roi_reached = MagicMock(return_value=True)
-
-    freqtrade.enter_positions()
-
-    trade = Trade.query.first()
-    trade.is_short = is_short
-    trade.is_open = True
-
-    # FIX: sniffing logs, suggest handle_trade should not execute_trade_exit
-    #      instead that responsibility should be moved out of handle_trade(),
-    #      we might just want to check if we are in a sell condition without
-    #      executing
-    # if ROI is reached we must sell
-    caplog.clear()
-    patch_get_signal(freqtrade)
-    assert freqtrade.handle_trade(trade)
-    assert log_has("ETH/USDT - Required profit reached. exit_type=ExitType.ROI",
-                   caplog)
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-def test_handle_trade_use_sell_signal(
-    default_conf_usdt, ticker_usdt, limit_order_open, fee, mocker, caplog, is_short
-) -> None:
-
-    enter_open_order = limit_order_open[exit_side(is_short)]
-    exit_open_order = limit_order_open[enter_side(is_short)]
-
-    # use_sell_signal is True buy default
-    caplog.set_level(logging.DEBUG)
-    patch_RPCManager(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        create_order=MagicMock(side_effect=[
-            enter_open_order,
-            exit_open_order,
-        ]),
-        get_fee=fee,
-    )
-
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
-    freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
-    freqtrade.enter_positions()
-
-    trade = Trade.query.first()
-    trade.is_short = is_short
-    trade.is_open = True
-
-    patch_get_signal(freqtrade, enter_long=False, exit_long=False)
-    assert not freqtrade.handle_trade(trade)
-
-    if is_short:
-        patch_get_signal(freqtrade, enter_long=False, exit_short=True)
-    else:
-        patch_get_signal(freqtrade, enter_long=False, exit_long=True)
-    assert freqtrade.handle_trade(trade)
-    assert log_has("ETH/USDT - Sell signal received. exit_type=ExitType.SELL_SIGNAL",
-                   caplog)
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-def test_close_trade(
-    default_conf_usdt, ticker_usdt, limit_order_open, limit_order, fee, mocker, is_short
-) -> None:
-    open_order = limit_order_open[exit_side(is_short)]
-    enter_order = limit_order[exit_side(is_short)]
-    exit_order = limit_order[enter_side(is_short)]
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        create_order=MagicMock(return_value=open_order),
-        get_fee=fee,
-    )
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
-
-    # Create trade and sell it
-    freqtrade.enter_positions()
-
-    trade = Trade.query.first()
-    trade.is_short = is_short
-    assert trade
-
-    oobj = Order.parse_from_ccxt_object(enter_order, enter_order['symbol'], 'buy')
-    trade.update_trade(oobj)
-    oobj = Order.parse_from_ccxt_object(exit_order, exit_order['symbol'], 'sell')
-    trade.update_trade(oobj)
-    assert trade.is_open is False
-
-    with pytest.raises(DependencyException, match=r'.*closed trade.*'):
-        freqtrade.handle_trade(trade)
-
-
-def test_bot_loop_start_called_once(mocker, default_conf_usdt, caplog):
-    ftbot = get_patched_freqtradebot(mocker, default_conf_usdt)
-    mocker.patch('freqtrade.freqtradebot.FreqtradeBot.create_trade')
-    patch_get_signal(ftbot)
-    ftbot.strategy.bot_loop_start = MagicMock(side_effect=ValueError)
-    ftbot.strategy.analyze = MagicMock()
-
-    ftbot.process()
-    assert log_has_re(r'Strategy caused the following exception.*', caplog)
-    assert ftbot.strategy.bot_loop_start.call_count == 1
-    assert ftbot.strategy.analyze.call_count == 1
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-def test_check_handle_timedout_entry_usercustom(
-    default_conf_usdt, ticker_usdt, limit_buy_order_old, open_trade,
-    limit_sell_order_old, fee, mocker, is_short
-) -> None:
-
-    old_order = limit_sell_order_old if is_short else limit_buy_order_old
-    old_order['id'] = open_trade.open_order_id
-
-    default_conf_usdt["unfilledtimeout"] = {"entry": 1400, "exit": 30}
-
-    rpc_mock = patch_RPCManager(mocker)
-<<<<<<< HEAD
-    cancel_buy_order = deepcopy(limit_buy_order_old)
-    cancel_buy_order['status'] = 'canceled'
-    cancel_order_mock = MagicMock(return_value=limit_buy_order_old)
-    cancel_order_wr_mock = MagicMock(return_value=cancel_buy_order)
-=======
-    cancel_order_mock = MagicMock(return_value=old_order)
-    cancel_enter_order = deepcopy(old_order)
-    cancel_enter_order['status'] = 'canceled'
-    cancel_order_wr_mock = MagicMock(return_value=cancel_enter_order)
->>>>>>> develop
-
-    patch_exchange(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-<<<<<<< HEAD
-        fetch_order=MagicMock(return_value=limit_buy_order_old),
-=======
-        fetch_order=MagicMock(return_value=old_order),
-        cancel_order_with_result=cancel_order_wr_mock,
->>>>>>> develop
-        cancel_order=cancel_order_mock,
-        cancel_order_with_result=cancel_order_wr_mock,
-        get_fee=fee
-    )
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    open_trade.is_short = is_short
-    open_trade.orders[0].side = 'sell' if is_short else 'buy'
-    open_trade.orders[0].ft_order_side = 'sell' if is_short else 'buy'
-    Trade.query.session.add(open_trade)
-
-    # Ensure default is to return empty (so not mocked yet)
-    freqtrade.check_handle_timedout()
-    assert cancel_order_mock.call_count == 0
-
-    # Return false - trade remains open
-    freqtrade.strategy.check_entry_timeout = MagicMock(return_value=False)
-    freqtrade.check_handle_timedout()
-    assert cancel_order_mock.call_count == 0
-    trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all()
-    nb_trades = len(trades)
-    assert nb_trades == 1
-    assert freqtrade.strategy.check_entry_timeout.call_count == 1
-    freqtrade.strategy.check_entry_timeout = MagicMock(side_effect=KeyError)
-
-    freqtrade.check_handle_timedout()
-    assert cancel_order_mock.call_count == 0
-    trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all()
-    nb_trades = len(trades)
-    assert nb_trades == 1
-    assert freqtrade.strategy.check_entry_timeout.call_count == 1
-    freqtrade.strategy.check_entry_timeout = MagicMock(return_value=True)
-
-    # Trade should be closed since the function returns true
-    freqtrade.check_handle_timedout()
-    assert cancel_order_wr_mock.call_count == 1
-    assert rpc_mock.call_count == 1
-    trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all()
-    nb_trades = len(trades)
-    assert nb_trades == 0
-    assert freqtrade.strategy.check_entry_timeout.call_count == 1
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-def test_check_handle_timedout_entry(
-    default_conf_usdt, ticker_usdt, limit_buy_order_old, open_trade,
-    limit_sell_order_old, fee, mocker, is_short
-) -> None:
-    old_order = limit_sell_order_old if is_short else limit_buy_order_old
-    rpc_mock = patch_RPCManager(mocker)
-<<<<<<< HEAD
-    open_trade.open_order_id = limit_buy_order_old['id']
-    order = Order.parse_from_ccxt_object(limit_buy_order_old, 'mocked', 'buy')
-    open_trade.orders[0] = order
-    limit_buy_cancel = deepcopy(limit_buy_order_old)
-=======
-    old_order['id'] = open_trade.open_order_id
-    limit_buy_cancel = deepcopy(old_order)
->>>>>>> develop
-    limit_buy_cancel['status'] = 'canceled'
-    cancel_order_mock = MagicMock(return_value=limit_buy_cancel)
-    patch_exchange(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        fetch_order=MagicMock(return_value=old_order),
-        cancel_order_with_result=cancel_order_mock,
-        get_fee=fee
-    )
-    freqtrade = FreqtradeBot(default_conf_usdt)
-
-    open_trade.is_short = is_short
-    Trade.query.session.add(open_trade)
-
-    freqtrade.strategy.check_entry_timeout = MagicMock(return_value=False)
-    # check it does cancel buy orders over the time limit
-    freqtrade.check_handle_timedout()
-    assert cancel_order_mock.call_count == 1
-    assert rpc_mock.call_count == 1
-    trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all()
-    nb_trades = len(trades)
-    assert nb_trades == 0
-    # Custom user buy-timeout is never called
-    assert freqtrade.strategy.check_entry_timeout.call_count == 0
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-def test_check_handle_cancelled_buy(
-    default_conf_usdt, ticker_usdt, limit_buy_order_old, open_trade,
-    limit_sell_order_old, fee, mocker, caplog, is_short
-) -> None:
-    """ Handle Buy order cancelled on exchange"""
-    old_order = limit_sell_order_old if is_short else limit_buy_order_old
-    rpc_mock = patch_RPCManager(mocker)
-    cancel_order_mock = MagicMock()
-    patch_exchange(mocker)
-    old_order.update({"status": "canceled", 'filled': 0.0})
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        fetch_order=MagicMock(return_value=old_order),
-        cancel_order=cancel_order_mock,
-        get_fee=fee
-    )
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    open_trade.is_short = is_short
-    Trade.query.session.add(open_trade)
-
-    # check it does cancel buy orders over the time limit
-    freqtrade.check_handle_timedout()
-    assert cancel_order_mock.call_count == 0
-    assert rpc_mock.call_count == 1
-    trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all()
-    nb_trades = len(trades)
-    assert nb_trades == 0
-    assert log_has_re(
-        f"{'Sell' if is_short else 'Buy'} order cancelled on exchange for Trade.*", caplog)
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-def test_check_handle_timedout_buy_exception(
-    default_conf_usdt, ticker_usdt, open_trade, is_short, fee, mocker
-) -> None:
-    rpc_mock = patch_RPCManager(mocker)
-    cancel_order_mock = MagicMock()
-    patch_exchange(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        validate_pairs=MagicMock(),
-        fetch_ticker=ticker_usdt,
-        fetch_order=MagicMock(side_effect=ExchangeError),
-        cancel_order=cancel_order_mock,
-        get_fee=fee
-    )
-    freqtrade = FreqtradeBot(default_conf_usdt)
-
-    open_trade.is_short = is_short
-    Trade.query.session.add(open_trade)
-
-    # check it does cancel buy orders over the time limit
-    freqtrade.check_handle_timedout()
-    assert cancel_order_mock.call_count == 0
-    assert rpc_mock.call_count == 0
-    trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all()
-    nb_trades = len(trades)
-    assert nb_trades == 1
-
-
-<<<<<<< HEAD
-def test_check_handle_timedout_sell_usercustom(default_conf_usdt, ticker_usdt, limit_sell_order_old,
-                                               mocker, open_trade, caplog) -> None:
-    default_conf_usdt["unfilledtimeout"] = {"buy": 1440, "sell": 1440, "exit_timeout_count": 1}
-    open_trade.open_order_id = limit_sell_order_old['id']
-    order = Order.parse_from_ccxt_object(limit_sell_order_old, 'mocked', 'sell')
-    open_trade.orders[0] = order
-=======
-@pytest.mark.parametrize("is_short", [False, True])
-def test_check_handle_timedout_exit_usercustom(
-    default_conf_usdt, ticker_usdt, limit_sell_order_old, mocker,
-    is_short, open_trade_usdt, caplog
-) -> None:
-    default_conf_usdt["unfilledtimeout"] = {"entry": 1440, "exit": 1440, "exit_timeout_count": 1}
-    limit_sell_order_old['id'] = open_trade_usdt.open_order_id
-    if is_short:
-        limit_sell_order_old['side'] = 'buy'
-        open_trade_usdt.is_short = is_short
-
->>>>>>> develop
-    rpc_mock = patch_RPCManager(mocker)
-    cancel_order_mock = MagicMock()
-    patch_exchange(mocker)
-    et_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.execute_trade_exit')
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        fetch_order=MagicMock(return_value=limit_sell_order_old),
-        cancel_order=cancel_order_mock
-    )
-    freqtrade = FreqtradeBot(default_conf_usdt)
-
-    open_trade_usdt.open_date = arrow.utcnow().shift(hours=-5).datetime
-    open_trade_usdt.close_date = arrow.utcnow().shift(minutes=-601).datetime
-    open_trade_usdt.close_profit_abs = 0.001
-    open_trade_usdt.is_open = False
-
-    Trade.query.session.add(open_trade_usdt)
-    # Ensure default is false
-    freqtrade.check_handle_timedout()
-    assert cancel_order_mock.call_count == 0
-
-    freqtrade.strategy.check_exit_timeout = MagicMock(return_value=False)
-    freqtrade.strategy.check_entry_timeout = MagicMock(return_value=False)
-    # Return false - No impact
-    freqtrade.check_handle_timedout()
-    assert cancel_order_mock.call_count == 0
-    assert rpc_mock.call_count == 0
-    assert open_trade_usdt.is_open is False
-    assert freqtrade.strategy.check_exit_timeout.call_count == 1
-    assert freqtrade.strategy.check_entry_timeout.call_count == 0
-
-    freqtrade.strategy.check_exit_timeout = MagicMock(side_effect=KeyError)
-    freqtrade.strategy.check_entry_timeout = MagicMock(side_effect=KeyError)
-    # Return Error - No impact
-    freqtrade.check_handle_timedout()
-    assert cancel_order_mock.call_count == 0
-    assert rpc_mock.call_count == 0
-    assert open_trade_usdt.is_open is False
-    assert freqtrade.strategy.check_exit_timeout.call_count == 1
-    assert freqtrade.strategy.check_entry_timeout.call_count == 0
-
-    # Return True - sells!
-    freqtrade.strategy.check_exit_timeout = MagicMock(return_value=True)
-    freqtrade.strategy.check_entry_timeout = MagicMock(return_value=True)
-    freqtrade.check_handle_timedout()
-    assert cancel_order_mock.call_count == 1
-    assert rpc_mock.call_count == 1
-    assert open_trade_usdt.is_open is True
-    assert freqtrade.strategy.check_exit_timeout.call_count == 1
-    assert freqtrade.strategy.check_entry_timeout.call_count == 0
-
-    # 2nd canceled trade - Fail execute sell
-    caplog.clear()
-    open_trade_usdt.open_order_id = limit_sell_order_old['id']
-    mocker.patch('freqtrade.persistence.Trade.get_exit_order_count', return_value=1)
-    mocker.patch('freqtrade.freqtradebot.FreqtradeBot.execute_trade_exit',
-                 side_effect=DependencyException)
-    freqtrade.check_handle_timedout()
-    assert log_has_re('Unable to emergency sell .*', caplog)
-
-    et_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.execute_trade_exit')
-    caplog.clear()
-    # 2nd canceled trade ...
-    open_trade_usdt.open_order_id = limit_sell_order_old['id']
-
-    # If cancelling fails - no emergency sell!
-    with patch('freqtrade.freqtradebot.FreqtradeBot.handle_cancel_exit', return_value=False):
-        freqtrade.check_handle_timedout()
-        assert et_mock.call_count == 0
-
-    freqtrade.check_handle_timedout()
-    assert log_has_re('Emergency exiting trade.*', caplog)
-    assert et_mock.call_count == 1
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-def test_check_handle_timedout_exit(
-    default_conf_usdt, ticker_usdt, limit_sell_order_old, mocker, is_short, open_trade_usdt
-) -> None:
-    rpc_mock = patch_RPCManager(mocker)
-    cancel_order_mock = MagicMock()
-    limit_sell_order_old['id'] = open_trade_usdt.open_order_id
-    limit_sell_order_old['side'] = 'buy' if is_short else 'sell'
-    patch_exchange(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        fetch_order=MagicMock(return_value=limit_sell_order_old),
-        cancel_order=cancel_order_mock
-    )
-    freqtrade = FreqtradeBot(default_conf_usdt)
-
-    open_trade_usdt.open_date = arrow.utcnow().shift(hours=-5).datetime
-    open_trade_usdt.close_date = arrow.utcnow().shift(minutes=-601).datetime
-    open_trade_usdt.close_profit_abs = 0.001
-    open_trade_usdt.is_open = False
-    open_trade_usdt.is_short = is_short
-
-    Trade.query.session.add(open_trade_usdt)
-
-    freqtrade.strategy.check_exit_timeout = MagicMock(return_value=False)
-    freqtrade.strategy.check_entry_timeout = MagicMock(return_value=False)
-    # check it does cancel sell orders over the time limit
-    freqtrade.check_handle_timedout()
-    assert cancel_order_mock.call_count == 1
-    assert rpc_mock.call_count == 1
-    assert open_trade_usdt.is_open is True
-    # Custom user sell-timeout is never called
-    assert freqtrade.strategy.check_exit_timeout.call_count == 0
-    assert freqtrade.strategy.check_entry_timeout.call_count == 0
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-def test_check_handle_cancelled_exit(
-    default_conf_usdt, ticker_usdt, limit_sell_order_old, open_trade_usdt,
-    is_short, mocker, caplog
-) -> None:
-    """ Handle sell order cancelled on exchange"""
-    rpc_mock = patch_RPCManager(mocker)
-    cancel_order_mock = MagicMock()
-    limit_sell_order_old.update({"status": "canceled", 'filled': 0.0})
-    limit_sell_order_old['side'] = 'buy' if is_short else 'sell'
-    patch_exchange(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        fetch_order=MagicMock(return_value=limit_sell_order_old),
-        cancel_order_with_result=cancel_order_mock
-    )
-    freqtrade = FreqtradeBot(default_conf_usdt)
-
-    open_trade_usdt.open_date = arrow.utcnow().shift(hours=-5).datetime
-    open_trade_usdt.close_date = arrow.utcnow().shift(minutes=-601).datetime
-    open_trade_usdt.is_open = False
-    open_trade_usdt.is_short = is_short
-
-    Trade.query.session.add(open_trade_usdt)
-
-    # check it does cancel sell orders over the time limit
-    freqtrade.check_handle_timedout()
-    assert cancel_order_mock.call_count == 0
-    assert rpc_mock.call_count == 1
-    assert open_trade_usdt.is_open is True
-    exit_name = 'Buy' if is_short else 'Sell'
-    assert log_has_re(f"{exit_name} order cancelled on exchange for Trade.*", caplog)
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-@pytest.mark.parametrize("leverage", [1, 3, 5, 10])
-def test_check_handle_timedout_partial(
-    default_conf_usdt, ticker_usdt, limit_buy_order_old_partial, is_short, leverage,
-    open_trade, mocker
-) -> None:
-    rpc_mock = patch_RPCManager(mocker)
-    open_trade.is_short = is_short
-    open_trade.leverage = leverage
-    limit_buy_order_old_partial['id'] = open_trade.open_order_id
-    limit_buy_order_old_partial['side'] = 'sell' if is_short else 'buy'
-    limit_buy_canceled = deepcopy(limit_buy_order_old_partial)
-    limit_buy_canceled['status'] = 'canceled'
-
-    cancel_order_mock = MagicMock(return_value=limit_buy_canceled)
-    patch_exchange(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        fetch_order=MagicMock(return_value=limit_buy_order_old_partial),
-        cancel_order_with_result=cancel_order_mock
-    )
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    prior_stake = open_trade.stake_amount
-    Trade.query.session.add(open_trade)
-
-    # check it does cancel buy orders over the time limit
-    # note this is for a partially-complete buy order
-    freqtrade.check_handle_timedout()
-    assert cancel_order_mock.call_count == 1
-    assert rpc_mock.call_count == 2
-    trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all()
-    assert len(trades) == 1
-    assert trades[0].amount == 23.0
-    assert trades[0].stake_amount == open_trade.open_rate * trades[0].amount / leverage
-    assert trades[0].stake_amount != prior_stake
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-def test_check_handle_timedout_partial_fee(
-    default_conf_usdt, ticker_usdt, open_trade, caplog, fee, is_short,
-    limit_buy_order_old_partial, trades_for_order,
-    limit_buy_order_old_partial_canceled, mocker
-) -> None:
-    open_trade.is_short = is_short
-    open_trade.orders[0].ft_order_side = 'sell' if is_short else 'buy'
-    rpc_mock = patch_RPCManager(mocker)
-    limit_buy_order_old_partial['id'] = open_trade.open_order_id
-    limit_buy_order_old_partial_canceled['id'] = open_trade.open_order_id
-    limit_buy_order_old_partial['side'] = 'sell' if is_short else 'buy'
-    limit_buy_order_old_partial_canceled['side'] = 'sell' if is_short else 'buy'
-
-    cancel_order_mock = MagicMock(return_value=limit_buy_order_old_partial_canceled)
-    mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(return_value=0))
-    patch_exchange(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        fetch_order=MagicMock(return_value=limit_buy_order_old_partial),
-        cancel_order_with_result=cancel_order_mock,
-        get_trades_for_order=MagicMock(return_value=trades_for_order),
-    )
-    freqtrade = FreqtradeBot(default_conf_usdt)
-
-    assert open_trade.amount == limit_buy_order_old_partial['amount']
-
-    open_trade.fee_open = fee()
-    open_trade.fee_close = fee()
-    Trade.query.session.add(open_trade)
-    # cancelling a half-filled order should update the amount to the bought amount
-    # and apply fees if necessary.
-    freqtrade.check_handle_timedout()
-
-    assert log_has_re(r"Applying fee on amount for Trade.*", caplog)
-
-    assert cancel_order_mock.call_count == 1
-    assert rpc_mock.call_count == 2
-    trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all()
-    assert len(trades) == 1
-    # Verify that trade has been updated
-    assert trades[0].amount == (limit_buy_order_old_partial['amount'] -
-                                limit_buy_order_old_partial['remaining']) - 0.023
-    assert trades[0].open_order_id is None
-    assert trades[0].fee_updated(open_trade.enter_side)
-    assert pytest.approx(trades[0].fee_open) == 0.001
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-def test_check_handle_timedout_partial_except(
-    default_conf_usdt, ticker_usdt, open_trade, caplog, fee, is_short,
-    limit_buy_order_old_partial, trades_for_order,
-    limit_buy_order_old_partial_canceled, mocker
-) -> None:
-    open_trade.is_short = is_short
-    rpc_mock = patch_RPCManager(mocker)
-    limit_buy_order_old_partial_canceled['id'] = open_trade.open_order_id
-    limit_buy_order_old_partial['id'] = open_trade.open_order_id
-    if is_short:
-        limit_buy_order_old_partial['side'] = 'sell'
-    cancel_order_mock = MagicMock(return_value=limit_buy_order_old_partial_canceled)
-    patch_exchange(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        fetch_order=MagicMock(return_value=limit_buy_order_old_partial),
-        cancel_order_with_result=cancel_order_mock,
-        get_trades_for_order=MagicMock(return_value=trades_for_order),
-    )
-    mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount',
-                 MagicMock(side_effect=DependencyException))
-    freqtrade = FreqtradeBot(default_conf_usdt)
-
-    assert open_trade.amount == limit_buy_order_old_partial['amount']
-
-    open_trade.fee_open = fee()
-    open_trade.fee_close = fee()
-    Trade.query.session.add(open_trade)
-    # cancelling a half-filled order should update the amount to the bought amount
-    # and apply fees if necessary.
-    freqtrade.check_handle_timedout()
-
-    assert log_has_re(r"Could not update trade amount: .*", caplog)
-
-    assert cancel_order_mock.call_count == 1
-    assert rpc_mock.call_count == 2
-    trades = Trade.query.filter(Trade.open_order_id.is_(open_trade.open_order_id)).all()
-    assert len(trades) == 1
-    # Verify that trade has been updated
-
-    assert trades[0].amount == (limit_buy_order_old_partial['amount'] -
-                                limit_buy_order_old_partial['remaining'])
-    assert trades[0].open_order_id is None
-    assert trades[0].fee_open == fee()
-
-
-def test_check_handle_timedout_exception(default_conf_usdt, ticker_usdt, open_trade_usdt, mocker,
-                                         caplog) -> None:
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    cancel_order_mock = MagicMock()
-
-    mocker.patch.multiple(
-        'freqtrade.freqtradebot.FreqtradeBot',
-        handle_cancel_enter=MagicMock(),
-        handle_cancel_exit=MagicMock(),
-    )
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        fetch_order=MagicMock(side_effect=ExchangeError('Oh snap')),
-        cancel_order=cancel_order_mock
-    )
-    freqtrade = FreqtradeBot(default_conf_usdt)
-
-    Trade.query.session.add(open_trade_usdt)
-
-    caplog.clear()
-    freqtrade.check_handle_timedout()
-    assert log_has_re(r"Cannot query order for Trade\(id=1, pair=ADA/USDT, amount=30.00000000, "
-                      r"is_short=False, leverage=1.0, "
-                      r"open_rate=2.00000000, open_since="
-                      f"{open_trade_usdt.open_date.strftime('%Y-%m-%d %H:%M:%S')}"
-                      r"\) due to Traceback \(most recent call last\):\n*",
-                      caplog)
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-def test_handle_cancel_enter(mocker, caplog, default_conf_usdt, limit_order, is_short) -> None:
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    l_order = limit_order[enter_side(is_short)]
-    cancel_buy_order = deepcopy(limit_order[enter_side(is_short)])
-    cancel_buy_order['status'] = 'canceled'
-    del cancel_buy_order['filled']
-
-    cancel_order_mock = MagicMock(return_value=cancel_buy_order)
-    mocker.patch('freqtrade.exchange.Exchange.cancel_order_with_result', cancel_order_mock)
-
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    freqtrade._notify_enter_cancel = MagicMock()
-
-    trade = MagicMock()
-    trade.pair = 'LTC/USDT'
-    trade.open_rate = 200
-    trade.is_short = False
-    trade.enter_side = "buy"
-    l_order['filled'] = 0.0
-    l_order['status'] = 'open'
-    reason = CANCEL_REASON['TIMEOUT']
-    assert freqtrade.handle_cancel_enter(trade, l_order, reason)
-    assert cancel_order_mock.call_count == 1
-
-    cancel_order_mock.reset_mock()
-    caplog.clear()
-    l_order['filled'] = 0.01
-    assert not freqtrade.handle_cancel_enter(trade, l_order, reason)
-    assert cancel_order_mock.call_count == 0
-    assert log_has_re("Order .* for .* not cancelled, as the filled amount.* unexitable.*", caplog)
-
-    caplog.clear()
-    cancel_order_mock.reset_mock()
-    l_order['filled'] = 2
-    assert not freqtrade.handle_cancel_enter(trade, l_order, reason)
-    assert cancel_order_mock.call_count == 1
-
-    # Order remained open for some reason (cancel failed)
-    cancel_buy_order['status'] = 'open'
-    cancel_order_mock = MagicMock(return_value=cancel_buy_order)
-    mocker.patch('freqtrade.exchange.Exchange.cancel_order_with_result', cancel_order_mock)
-    assert not freqtrade.handle_cancel_enter(trade, l_order, reason)
-    assert log_has_re(r"Order .* for .* not cancelled.", caplog)
-    # min_pair_stake empty should not crash
-    mocker.patch('freqtrade.exchange.Exchange.get_min_pair_stake_amount', return_value=None)
-    assert not freqtrade.handle_cancel_enter(trade, limit_order[enter_side(is_short)], reason)
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-@pytest.mark.parametrize("limit_buy_order_canceled_empty", ['binance', 'ftx', 'kraken', 'bittrex'],
-                         indirect=['limit_buy_order_canceled_empty'])
-def test_handle_cancel_enter_exchanges(mocker, caplog, default_conf_usdt, is_short,
-                                       limit_buy_order_canceled_empty) -> None:
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    cancel_order_mock = mocker.patch(
-        'freqtrade.exchange.Exchange.cancel_order_with_result',
-        return_value=limit_buy_order_canceled_empty)
-    nofiy_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot._notify_enter_cancel')
-    freqtrade = FreqtradeBot(default_conf_usdt)
-
-    reason = CANCEL_REASON['TIMEOUT']
-    trade = MagicMock()
-    trade.pair = 'LTC/ETH'
-    trade.enter_side = "sell" if is_short else "buy"
-    assert freqtrade.handle_cancel_enter(trade, limit_buy_order_canceled_empty, reason)
-    assert cancel_order_mock.call_count == 0
-    assert log_has_re(
-        f'{trade.enter_side.capitalize()} order fully cancelled. '
-        r'Removing .* from database\.',
-        caplog
-    )
-    assert nofiy_mock.call_count == 1
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-@pytest.mark.parametrize('cancelorder', [
-    {},
-    {'remaining': None},
-    'String Return value',
-    123
-])
-def test_handle_cancel_enter_corder_empty(mocker, default_conf_usdt, limit_order, is_short,
-                                          cancelorder) -> None:
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    l_order = limit_order[enter_side(is_short)]
-    cancel_order_mock = MagicMock(return_value=cancelorder)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        cancel_order=cancel_order_mock
-    )
-
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    freqtrade._notify_enter_cancel = MagicMock()
-
-    trade = MagicMock()
-    trade.pair = 'LTC/USDT'
-    trade.enter_side = "buy"
-    trade.open_rate = 200
-    trade.enter_side = "buy"
-    l_order['filled'] = 0.0
-    l_order['status'] = 'open'
-    reason = CANCEL_REASON['TIMEOUT']
-    assert freqtrade.handle_cancel_enter(trade, l_order, reason)
-    assert cancel_order_mock.call_count == 1
-
-    cancel_order_mock.reset_mock()
-    l_order['filled'] = 1.0
-    assert not freqtrade.handle_cancel_enter(trade, l_order, reason)
-    assert cancel_order_mock.call_count == 1
-
-
-def test_handle_cancel_exit_limit(mocker, default_conf_usdt, fee) -> None:
-    send_msg_mock = patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    cancel_order_mock = MagicMock()
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        cancel_order=cancel_order_mock,
-    )
-    mocker.patch('freqtrade.exchange.Exchange.get_rate', return_value=0.245441)
-
-    freqtrade = FreqtradeBot(default_conf_usdt)
-
-    trade = Trade(
-        pair='LTC/ETH',
-        amount=2,
-        exchange='binance',
-        open_rate=0.245441,
-        open_order_id="123456",
-        open_date=arrow.utcnow().shift(days=-2).datetime,
-        fee_open=fee.return_value,
-        fee_close=fee.return_value,
-        close_rate=0.555,
-        close_date=arrow.utcnow().datetime,
-        exit_reason="sell_reason_whatever",
-    )
-    order = {'id': "123456",
-             'remaining': 1,
-             'amount': 1,
-             'status': "open"}
-    reason = CANCEL_REASON['TIMEOUT']
-    assert freqtrade.handle_cancel_exit(trade, order, reason)
-    assert cancel_order_mock.call_count == 1
-    assert send_msg_mock.call_count == 1
-    assert trade.close_rate is None
-    assert trade.exit_reason is None
-
-    send_msg_mock.reset_mock()
-
-    order['amount'] = 2
-    assert not freqtrade.handle_cancel_exit(trade, order, reason)
-    # Assert cancel_order was not called (callcount remains unchanged)
-    assert cancel_order_mock.call_count == 1
-    assert send_msg_mock.call_count == 1
-    assert (send_msg_mock.call_args_list[0][0][0]['reason']
-            == CANCEL_REASON['PARTIALLY_FILLED_KEEP_OPEN'])
-
-    assert not freqtrade.handle_cancel_exit(trade, order, reason)
-
-    send_msg_mock.call_args_list[0][0][0]['reason'] = CANCEL_REASON['PARTIALLY_FILLED_KEEP_OPEN']
-
-    # Message should not be iterated again
-    assert trade.exit_order_status == CANCEL_REASON['PARTIALLY_FILLED_KEEP_OPEN']
-    assert send_msg_mock.call_count == 1
-
-
-def test_handle_cancel_exit_cancel_exception(mocker, default_conf_usdt) -> None:
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    mocker.patch(
-        'freqtrade.exchange.Exchange.cancel_order_with_result', side_effect=InvalidOrderException())
-
-    freqtrade = FreqtradeBot(default_conf_usdt)
-
-    trade = MagicMock()
-    reason = CANCEL_REASON['TIMEOUT']
-    order = {'remaining': 1,
-             'amount': 1,
-             'status': "open"}
-    assert not freqtrade.handle_cancel_exit(trade, order, reason)
-
-
-@pytest.mark.parametrize("is_short, open_rate, amt", [
-    (False, 2.0, 30.0),
-    (True, 2.02, 29.70297029),
-])
-def test_execute_trade_exit_up(default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_up, mocker,
-                               ticker_usdt_sell_down, is_short, open_rate, amt) -> None:
-    rpc_mock = patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        get_fee=fee,
-        _is_dry_limit_order_filled=MagicMock(return_value=False),
-    )
-    patch_whitelist(mocker, default_conf_usdt)
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
-    freqtrade.strategy.confirm_trade_exit = MagicMock(return_value=False)
-
-    # Create some test data
-    freqtrade.enter_positions()
-    rpc_mock.reset_mock()
-
-    trade = Trade.query.first()
-    assert trade.is_short == is_short
-    assert trade
-    assert freqtrade.strategy.confirm_trade_exit.call_count == 0
-
-    # Increase the price and sell it
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt_sell_down if is_short else ticker_usdt_sell_up
-    )
-    # Prevented sell ...
-    freqtrade.execute_trade_exit(
-        trade=trade,
-        limit=(ticker_usdt_sell_down()['ask'] if is_short else ticker_usdt_sell_up()['bid']),
-        exit_check=ExitCheckTuple(exit_type=ExitType.ROI)
-    )
-    assert rpc_mock.call_count == 0
-    assert freqtrade.strategy.confirm_trade_exit.call_count == 1
-    assert id(freqtrade.strategy.confirm_trade_exit.call_args_list[0][1]['trade']) != id(trade)
-    assert freqtrade.strategy.confirm_trade_exit.call_args_list[0][1]['trade'].id == trade.id
-
-    # Repatch with true
-    freqtrade.strategy.confirm_trade_exit = MagicMock(return_value=True)
-    freqtrade.execute_trade_exit(
-        trade=trade,
-        limit=(ticker_usdt_sell_down()['ask'] if is_short else ticker_usdt_sell_up()['bid']),
-        exit_check=ExitCheckTuple(exit_type=ExitType.ROI)
-    )
-    assert freqtrade.strategy.confirm_trade_exit.call_count == 1
-
-    assert rpc_mock.call_count == 1
-    last_msg = rpc_mock.call_args_list[-1][0][0]
-    assert {
-        'trade_id': 1,
-        'type': RPCMessageType.SELL,
-        'exchange': 'Binance',
-        'pair': 'ETH/USDT',
-        'gain': 'profit',
-        'limit': 2.0 if is_short else 2.2,
-        'amount': pytest.approx(amt),
-        'order_type': 'limit',
-        'buy_tag': None,
-        'direction': 'Short' if trade.is_short else 'Long',
-        'leverage': 1.0,
-        'enter_tag': None,
-        'open_rate': open_rate,
-        'current_rate': 2.01 if is_short else 2.3,
-        'profit_amount': 0.29554455 if is_short else 5.685,
-        'profit_ratio': 0.00493809 if is_short else 0.09451372,
-        'stake_currency': 'USDT',
-        'fiat_currency': 'USD',
-        'sell_reason': ExitType.ROI.value,
-        'exit_reason': ExitType.ROI.value,
-        'open_date': ANY,
-        'close_date': ANY,
-        'close_rate': ANY,
-        'sub_trade': False,
-        'stake_amount': 60.0,
-    } == last_msg
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-def test_execute_trade_exit_down(default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_down,
-                                 ticker_usdt_sell_up, mocker, is_short) -> None:
-    rpc_mock = patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        get_fee=fee,
-        _is_dry_limit_order_filled=MagicMock(return_value=False),
-    )
-    patch_whitelist(mocker, default_conf_usdt)
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
-
-    # Create some test data
-    freqtrade.enter_positions()
-
-    trade = Trade.query.first()
-    trade.is_short = is_short
-    assert trade
-
-    # Decrease the price and sell it
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt_sell_up if is_short else ticker_usdt_sell_down
-    )
-    freqtrade.execute_trade_exit(
-        trade=trade, limit=(ticker_usdt_sell_up if is_short else ticker_usdt_sell_down)()['bid'],
-        exit_check=ExitCheckTuple(exit_type=ExitType.STOP_LOSS))
-
-    assert rpc_mock.call_count == 2
-    last_msg = rpc_mock.call_args_list[-1][0][0]
-    assert {
-        'type': RPCMessageType.SELL,
-        'trade_id': 1,
-        'exchange': 'Binance',
-        'pair': 'ETH/USDT',
-        'direction': 'Short' if trade.is_short else 'Long',
-        'leverage': 1.0,
-        'gain': 'loss',
-        'limit': 2.2 if is_short else 2.01,
-        'amount': pytest.approx(29.70297029) if is_short else 30.0,
-        'order_type': 'limit',
-        'buy_tag': None,
-        'enter_tag': None,
-        'open_rate': 2.02 if is_short else 2.0,
-        'current_rate': 2.2 if is_short else 2.0,
-        'profit_amount': -5.65990099 if is_short else -0.00075,
-        'profit_ratio': -0.0945681 if is_short else -1.247e-05,
-        'stake_currency': 'USDT',
-        'fiat_currency': 'USD',
-        'sell_reason': ExitType.STOP_LOSS.value,
-        'exit_reason': ExitType.STOP_LOSS.value,
-        'open_date': ANY,
-        'close_date': ANY,
-        'close_rate': ANY,
-        'sub_trade': False,
-        'stake_amount': 60.0,
-            } == last_msg
-
-
-@pytest.mark.parametrize(
-    "is_short,amount,open_rate,current_rate,limit,profit_amount,profit_ratio,profit_or_loss", [
-        (False, 30, 2.0, 2.3, 2.25, 7.18125, 0.11938903, 'profit'),
-        (True, 29.70297029, 2.02, 2.2, 2.25, -7.14876237, -0.11944465, 'loss'),
-    ])
-def test_execute_trade_exit_custom_exit_price(
-        default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_up, is_short, amount, open_rate,
-        current_rate, limit, profit_amount, profit_ratio, profit_or_loss, mocker) -> None:
-    rpc_mock = patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        get_fee=fee,
-        _is_dry_limit_order_filled=MagicMock(return_value=False),
-    )
-    config = deepcopy(default_conf_usdt)
-    config['custom_price_max_distance_ratio'] = 0.1
-    patch_whitelist(mocker, config)
-    freqtrade = FreqtradeBot(config)
-    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
-    freqtrade.strategy.confirm_trade_exit = MagicMock(return_value=False)
-
-    # Create some test data
-    freqtrade.enter_positions()
-    rpc_mock.reset_mock()
-
-    trade = Trade.query.first()
-    trade.is_short = is_short
-    assert trade
-    assert freqtrade.strategy.confirm_trade_exit.call_count == 0
-
-    # Increase the price and sell it
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt_sell_up
-    )
-
-    freqtrade.strategy.confirm_trade_exit = MagicMock(return_value=True)
-
-    # Set a custom exit price
-    freqtrade.strategy.custom_exit_price = lambda **kwargs: 2.25
-    freqtrade.execute_trade_exit(
-        trade=trade,
-        limit=ticker_usdt_sell_up()['ask' if is_short else 'bid'],
-        exit_check=ExitCheckTuple(exit_type=ExitType.SELL_SIGNAL)
-    )
-
-    # Sell price must be different to default bid price
-
-    assert freqtrade.strategy.confirm_trade_exit.call_count == 1
-
-    assert rpc_mock.call_count == 1
-    last_msg = rpc_mock.call_args_list[-1][0][0]
-    assert {
-        'trade_id': 1,
-        'type': RPCMessageType.SELL,
-        'exchange': 'Binance',
-        'pair': 'ETH/USDT',
-        'direction': 'Short' if trade.is_short else 'Long',
-        'leverage': 1.0,
-        'gain': profit_or_loss,
-        'limit': limit,
-        'amount': pytest.approx(amount),
-        'order_type': 'limit',
-        'buy_tag': None,
-        'enter_tag': None,
-        'open_rate': open_rate,
-        'current_rate': current_rate,
-        'profit_amount': pytest.approx(profit_amount),
-        'profit_ratio': profit_ratio,
-        'stake_currency': 'USDT',
-        'fiat_currency': 'USD',
-        'sell_reason': ExitType.SELL_SIGNAL.value,
-        'exit_reason': ExitType.SELL_SIGNAL.value,
-        'open_date': ANY,
-        'close_date': ANY,
-        'close_rate': ANY,
-        'sub_trade': False,
-        'stake_amount': 60.0,
-    } == last_msg
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-def test_execute_trade_exit_down_stoploss_on_exchange_dry_run(
-        default_conf_usdt, ticker_usdt, fee, is_short, ticker_usdt_sell_down,
-        ticker_usdt_sell_up, mocker) -> None:
-    rpc_mock = patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        get_fee=fee,
-        _is_dry_limit_order_filled=MagicMock(return_value=False),
-    )
-    patch_whitelist(mocker, default_conf_usdt)
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
-
-    # Create some test data
-    freqtrade.enter_positions()
-
-    trade = Trade.query.first()
-    assert trade.is_short == is_short
-    assert trade
-
-    # Decrease the price and sell it
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt_sell_up if is_short else ticker_usdt_sell_down
-    )
-
-    default_conf_usdt['dry_run'] = True
-    freqtrade.strategy.order_types['stoploss_on_exchange'] = True
-    # Setting trade stoploss to 0.01
-
-    trade.stop_loss = 2.0 * 1.01 if is_short else 2.0 * 0.99
-    freqtrade.execute_trade_exit(
-        trade=trade, limit=(ticker_usdt_sell_up if is_short else ticker_usdt_sell_down())['bid'],
-        exit_check=ExitCheckTuple(exit_type=ExitType.STOP_LOSS))
-
-    assert rpc_mock.call_count == 2
-    last_msg = rpc_mock.call_args_list[-1][0][0]
-
-    assert {
-        'type': RPCMessageType.SELL,
-        'trade_id': 1,
-        'exchange': 'Binance',
-        'pair': 'ETH/USDT',
-        'direction': 'Short' if trade.is_short else 'Long',
-        'leverage': 1.0,
-        'gain': 'loss',
-        'limit': 2.02 if is_short else 1.98,
-        'amount': pytest.approx(29.70297029 if is_short else 30.0),
-        'order_type': 'limit',
-        'buy_tag': None,
-        'enter_tag': None,
-        'open_rate': 2.02 if is_short else 2.0,
-        'current_rate': 2.2 if is_short else 2.0,
-        'profit_amount': -0.3 if is_short else -0.8985,
-        'profit_ratio': -0.00501253 if is_short else -0.01493766,
-        'stake_currency': 'USDT',
-        'fiat_currency': 'USD',
-        'sell_reason': ExitType.STOP_LOSS.value,
-        'exit_reason': ExitType.STOP_LOSS.value,
-        'open_date': ANY,
-        'close_date': ANY,
-        'close_rate': ANY,
-        'sub_trade': False,
-        'stake_amount': 60.0,
-    } == last_msg
-
-
-def test_execute_trade_exit_sloe_cancel_exception(
-        mocker, default_conf_usdt, ticker_usdt, fee, caplog) -> None:
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-    mocker.patch('freqtrade.exchange.Exchange.cancel_stoploss_order',
-                 side_effect=InvalidOrderException())
-    mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(return_value=300))
-    create_order_mock = MagicMock(side_effect=[
-        {'id': '12345554'},
-        {'id': '12345555'},
-    ])
-    patch_exchange(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        get_fee=fee,
-        create_order=create_order_mock,
-    )
-
-    freqtrade.strategy.order_types['stoploss_on_exchange'] = True
-    patch_get_signal(freqtrade)
-    freqtrade.enter_positions()
-
-    trade = Trade.query.first()
-    PairLock.session = MagicMock()
-
-    freqtrade.config['dry_run'] = False
-    trade.stoploss_order_id = "abcd"
-
-    freqtrade.execute_trade_exit(trade=trade, limit=1234,
-                                 exit_check=ExitCheckTuple(exit_type=ExitType.STOP_LOSS))
-    assert create_order_mock.call_count == 2
-    assert log_has('Could not cancel stoploss order abcd', caplog)
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-def test_execute_trade_exit_with_stoploss_on_exchange(
-        default_conf_usdt, ticker_usdt, fee, ticker_usdt_sell_up, is_short, mocker) -> None:
-
-    default_conf_usdt['exchange']['name'] = 'binance'
-    rpc_mock = patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    stoploss = MagicMock(return_value={
-        'id': 123,
-        'info': {
-            'foo': 'bar'
-        }
-    })
-
-    cancel_order = MagicMock(return_value=True)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        get_fee=fee,
-        amount_to_precision=lambda s, x, y: y,
-        price_to_precision=lambda s, x, y: y,
-        stoploss=stoploss,
-        cancel_stoploss_order=cancel_order,
-        _is_dry_limit_order_filled=MagicMock(side_effect=[True, False]),
-    )
-
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    freqtrade.strategy.order_types['stoploss_on_exchange'] = True
-    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
-
-    # Create some test data
-    freqtrade.enter_positions()
-
-    trade = Trade.query.first()
-    trade.is_short = is_short
-    assert trade
-    trades = [trade]
-
-    freqtrade.check_handle_timedout()
-    freqtrade.exit_positions(trades)
-
-    # Increase the price and sell it
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt_sell_up
-    )
-
-    freqtrade.execute_trade_exit(
-        trade=trade,
-        limit=ticker_usdt_sell_up()['ask' if is_short else 'bid'],
-        exit_check=ExitCheckTuple(exit_type=ExitType.STOP_LOSS)
-    )
-
-    trade = Trade.query.first()
-    trade.is_short = is_short
-    assert trade
-    assert cancel_order.call_count == 1
-    assert rpc_mock.call_count == 3
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-def test_may_execute_trade_exit_after_stoploss_on_exchange_hit(
-        default_conf_usdt, ticker_usdt, fee, mocker, is_short) -> None:
-    default_conf_usdt['exchange']['name'] = 'binance'
-    rpc_mock = patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        get_fee=fee,
-        amount_to_precision=lambda s, x, y: y,
-        price_to_precision=lambda s, x, y: y,
-        _is_dry_limit_order_filled=MagicMock(side_effect=[False, True]),
-    )
-
-    stoploss = MagicMock(return_value={
-        'id': 123,
-        'info': {
-            'foo': 'bar'
-        }
-    })
-
-    mocker.patch('freqtrade.exchange.Binance.stoploss', stoploss)
-
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    freqtrade.strategy.order_types['stoploss_on_exchange'] = True
-    patch_get_signal(freqtrade, enter_long=not is_short, enter_short=is_short)
-
-    # Create some test data
-    freqtrade.enter_positions()
-    freqtrade.check_handle_timedout()
-    trade = Trade.query.first()
-    trades = [trade]
-    assert trade.stoploss_order_id is None
-
-    freqtrade.exit_positions(trades)
-    assert trade
-    assert trade.stoploss_order_id == '123'
-    assert trade.open_order_id is None
-
-    # Assuming stoploss on exchange is hit
-    # stoploss_order_id should become None
-    # and trade should be sold at the price of stoploss
-    stoploss_executed = MagicMock(return_value={
-        "id": "123",
-        "timestamp": 1542707426845,
-        "datetime": "2018-11-20T09:50:26.845Z",
-        "lastTradeTimestamp": None,
-        "symbol": "BTC/USDT",
-        "type": "stop_loss_limit",
-        "side": "sell",
-        "price": 1.08801,
-        "amount": 90.99181074,
-        "cost": 99.0000000032274,
-        "average": 1.08801,
-        "filled": 90.99181074,
-        "remaining": 0.0,
-        "status": "closed",
-        "fee": None,
-        "trades": None
-    })
-    mocker.patch('freqtrade.exchange.Exchange.fetch_stoploss_order', stoploss_executed)
-
-    freqtrade.exit_positions(trades)
-    assert trade.stoploss_order_id is None
-    assert trade.is_open is False
-    assert trade.exit_reason == ExitType.STOPLOSS_ON_EXCHANGE.value
-    assert rpc_mock.call_count == 3
-    if is_short:
-        assert rpc_mock.call_args_list[0][0][0]['type'] == RPCMessageType.SHORT
-        assert rpc_mock.call_args_list[1][0][0]['type'] == RPCMessageType.SHORT_FILL
-        assert rpc_mock.call_args_list[2][0][0]['type'] == RPCMessageType.SELL
-
-    else:
-        assert rpc_mock.call_args_list[0][0][0]['type'] == RPCMessageType.BUY
-        assert rpc_mock.call_args_list[1][0][0]['type'] == RPCMessageType.BUY_FILL
-        assert rpc_mock.call_args_list[2][0][0]['type'] == RPCMessageType.SELL
-
-
-@pytest.mark.parametrize(
-    "is_short,amount,current_rate,limit,profit_amount,profit_ratio,profit_or_loss", [
-        (False, 30, 2.3, 2.2, 5.685, 0.09451372, 'profit'),
-        (True, 29.70297029, 2.2, 2.3, -8.63762376, -0.1443212, 'loss'),
-    ])
-def test_execute_trade_exit_market_order(
-    default_conf_usdt, ticker_usdt, fee, is_short, current_rate, amount,
-    limit, profit_amount, profit_ratio, profit_or_loss, ticker_usdt_sell_up, mocker
-) -> None:
-    """
-    amount
-        long: 60 / 2.0 = 30
-        short: 60 / 2.02 = 29.70297029
-    open_value
-        long: (30 * 2.0) + (30 * 2.0 * 0.0025) = 60.15
-        short: (29.702970297029704 * 2.02) - (29.702970297029704 * 2.02 * 0.0025) = 59.85
-    close_value
-        long: (30 * 2.2) - (30 * 2.2 * 0.0025) = 65.835
-        short: (29.702970297029704 * 2.3) + (29.702970297029704 * 2.3 * 0.0025) = 68.48762376237624
-    profit
-        long: 65.835 - 60.15 = 5.684999999999995
-        short: 59.85 - 68.48762376237624 = -8.637623762376244
-    profit_ratio
-        long: (65.835/60.15) - 1 = 0.0945137157107232
-        short: 1 - (68.48762376237624/59.85) = -0.1443211990371971
-    """
-    open_rate = ticker_usdt.return_value['ask' if is_short else 'bid']
-    rpc_mock = patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        get_fee=fee,
-        _is_dry_limit_order_filled=MagicMock(return_value=False),
-    )
-    patch_whitelist(mocker, default_conf_usdt)
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
-
-    # Create some test data
-    freqtrade.enter_positions()
-
-    trade = Trade.query.first()
-    trade.is_short = is_short
-    assert trade
-
-    # Increase the price and sell it
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt_sell_up
-    )
-    freqtrade.config['order_types']['exit'] = 'market'
-
-    freqtrade.execute_trade_exit(
-        trade=trade,
-        limit=ticker_usdt_sell_up()['ask' if is_short else 'bid'],
-        exit_check=ExitCheckTuple(exit_type=ExitType.ROI)
-    )
-
-    assert not trade.is_open
-    assert trade.close_profit == profit_ratio
-
-    assert rpc_mock.call_count == 3
-    last_msg = rpc_mock.call_args_list[-2][0][0]
-    assert {
-        'type': RPCMessageType.SELL,
-        'trade_id': 1,
-        'exchange': 'Binance',
-        'pair': 'ETH/USDT',
-        'direction': 'Short' if trade.is_short else 'Long',
-        'leverage': 1.0,
-        'gain': profit_or_loss,
-        'limit': limit,
-        'amount': pytest.approx(amount),
-        'order_type': 'market',
-        'buy_tag': None,
-        'enter_tag': None,
-        'open_rate': open_rate,
-        'current_rate': current_rate,
-        'profit_amount': pytest.approx(profit_amount),
-        'profit_ratio': profit_ratio,
-        'stake_currency': 'USDT',
-        'fiat_currency': 'USD',
-        'sell_reason': ExitType.ROI.value,
-        'exit_reason': ExitType.ROI.value,
-        'open_date': ANY,
-        'close_date': ANY,
-        'close_rate': ANY,
-        'sub_trade': False,
-        'stake_amount': 60.0,
-
-    } == last_msg
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-def test_execute_trade_exit_insufficient_funds_error(default_conf_usdt, ticker_usdt, fee, is_short,
-                                                     ticker_usdt_sell_up, mocker) -> None:
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-    mock_insuf = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_insufficient_funds')
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        get_fee=fee,
-        create_order=MagicMock(side_effect=[
-            {'id': 1234553382},
-            InsufficientFundsError(),
-        ]),
-    )
-    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
-
-    # Create some test data
-    freqtrade.enter_positions()
-
-    trade = Trade.query.first()
-    trade.is_short = is_short
-    assert trade
-
-    # Increase the price and sell it
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt_sell_up
-    )
-
-    sell_reason = ExitCheckTuple(exit_type=ExitType.ROI)
-    assert not freqtrade.execute_trade_exit(
-        trade=trade,
-        limit=ticker_usdt_sell_up()['ask' if is_short else 'bid'],
-        exit_check=sell_reason
-    )
-    assert mock_insuf.call_count == 1
-
-
-@pytest.mark.parametrize('profit_only,bid,ask,handle_first,handle_second,exit_type,is_short', [
-    # Enable profit
-    (True, 2.18, 2.2, False, True, ExitType.SELL_SIGNAL.value, False),
-    (True, 2.18, 2.2, False, True, ExitType.SELL_SIGNAL.value, True),
-    # # Disable profit
-    (False, 3.19, 3.2, True,  False, ExitType.SELL_SIGNAL.value, False),
-    (False, 3.19, 3.2, True,  False, ExitType.SELL_SIGNAL.value, True),
-    # # Enable loss
-    # # * Shouldn't this be ExitType.STOP_LOSS.value
-    (True, 0.21, 0.22, False, False, None, False),
-    (True, 2.41, 2.42, False, False, None, True),
-    # Disable loss
-    (False, 0.10, 0.22, True, False, ExitType.SELL_SIGNAL.value, False),
-    (False, 0.10, 0.22, True, False, ExitType.SELL_SIGNAL.value, True),
-])
-def test_sell_profit_only(
-        default_conf_usdt, limit_order, limit_order_open, is_short,
-        fee, mocker, profit_only, bid, ask, handle_first, handle_second, exit_type) -> None:
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    eside = enter_side(is_short)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=MagicMock(return_value={
-            'bid': bid,
-            'ask': ask,
-            'last': bid
-        }),
-        create_order=MagicMock(side_effect=[
-            limit_order_open[eside],
-            {'id': 1234553382},
-        ]),
-        get_fee=fee,
-    )
-    default_conf_usdt.update({
-        'use_sell_signal': True,
-        'sell_profit_only': profit_only,
-        'sell_profit_offset': 0.1,
-    })
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
-    if exit_type == ExitType.SELL_SIGNAL.value:
-        freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
-    else:
-        freqtrade.strategy.stop_loss_reached = MagicMock(return_value=ExitCheckTuple(
-            exit_type=ExitType.NONE))
-    freqtrade.enter_positions()
-
-    trade = Trade.query.first()
-    trade.is_short = is_short
-    oobj = Order.parse_from_ccxt_object(limit_order[eside], limit_order[eside]['symbol'], eside)
-    trade.update_trade(oobj)
-    freqtrade.wallets.update()
-    patch_get_signal(freqtrade, enter_long=False, exit_short=is_short, exit_long=not is_short)
-    assert freqtrade.handle_trade(trade) is handle_first
-
-    if handle_second:
-        freqtrade.strategy.sell_profit_offset = 0.0
-        assert freqtrade.handle_trade(trade) is True
-
-
-def test_sell_not_enough_balance(default_conf_usdt, limit_order, limit_order_open,
-                                 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
-        }),
-        create_order=MagicMock(side_effect=[
-            limit_order_open['buy'],
-            {'id': 1234553382},
-        ]),
-        get_fee=fee,
-    )
-
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade)
-    freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
-
-    freqtrade.enter_positions()
-
-    trade = Trade.query.first()
-    amnt = trade.amount
-
-    oobj = Order.parse_from_ccxt_object(limit_order['buy'], limit_order['buy']['symbol'], 'buy')
-    trade.update_trade(oobj)
-    patch_get_signal(freqtrade, enter_long=False, exit_long=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
-
-
-@pytest.mark.parametrize('amount_wallet,has_err', [
-    (95.29, False),
-    (91.29, True)
-])
-def test__safe_exit_amount(default_conf_usdt, fee, caplog, mocker, amount_wallet, has_err):
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    amount = 95.33
-    amount_wallet = amount_wallet
-    mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(return_value=amount_wallet))
-    wallet_update = mocker.patch('freqtrade.wallets.Wallets.update')
-    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_usdt)
-    patch_get_signal(freqtrade)
-    if has_err:
-        with pytest.raises(DependencyException, match=r"Not enough amount to exit trade."):
-            assert freqtrade._safe_exit_amount(trade.pair, trade.amount)
-    else:
-        wallet_update.reset_mock()
-        assert freqtrade._safe_exit_amount(trade.pair, trade.amount) == amount_wallet
-        assert log_has_re(r'.*Falling back to wallet-amount.', caplog)
-        assert wallet_update.call_count == 1
-        caplog.clear()
-        wallet_update.reset_mock()
-        assert freqtrade._safe_exit_amount(trade.pair, amount_wallet) == amount_wallet
-        assert not log_has_re(r'.*Falling back to wallet-amount.', caplog)
-        assert wallet_update.call_count == 1
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-def test_locked_pairs(default_conf_usdt, ticker_usdt, fee,
-                      ticker_usdt_sell_down, mocker, caplog, is_short) -> None:
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        get_fee=fee,
-    )
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
-
-    # Create some test data
-    freqtrade.enter_positions()
-
-    trade = Trade.query.first()
-    trade.is_short = is_short
-    assert trade
-
-    # Decrease the price and sell it
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt_sell_down
-    )
-
-    freqtrade.execute_trade_exit(
-        trade=trade,
-        limit=ticker_usdt_sell_down()['ask' if is_short else 'bid'],
-        exit_check=ExitCheckTuple(exit_type=ExitType.STOP_LOSS)
-    )
-    trade.close(ticker_usdt_sell_down()['bid'])
-    assert freqtrade.strategy.is_pair_locked(trade.pair)
-
-    # reinit - should buy other pair.
-    caplog.clear()
-    freqtrade.enter_positions()
-
-    assert log_has_re(f"Pair {trade.pair} is still locked.*", caplog)
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-def test_ignore_roi_if_buy_signal(default_conf_usdt, limit_order, limit_order_open, is_short,
-                                  fee, mocker) -> None:
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    eside = enter_side(is_short)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=MagicMock(return_value={
-            'bid': 2.19,
-            'ask': 2.2,
-            'last': 2.19
-        }),
-        create_order=MagicMock(side_effect=[
-            limit_order_open[eside],
-            {'id': 1234553382},
-        ]),
-        get_fee=fee,
-    )
-    default_conf_usdt['ignore_roi_if_buy_signal'] = True
-
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
-    freqtrade.strategy.min_roi_reached = MagicMock(return_value=True)
-
-    freqtrade.enter_positions()
-
-    trade = Trade.query.first()
-    trade.is_short = is_short
-    oobj = Order.parse_from_ccxt_object(
-        limit_order[eside], limit_order[eside]['symbol'], eside)
-    trade.update_trade(oobj)
-    freqtrade.wallets.update()
-    if is_short:
-        patch_get_signal(freqtrade, enter_long=False, enter_short=True, exit_short=True)
-    else:
-        patch_get_signal(freqtrade, enter_long=True, exit_long=True)
-
-    assert freqtrade.handle_trade(trade) is False
-
-    # Test if entry-signal is absent (should sell due to roi = true)
-    if is_short:
-        patch_get_signal(freqtrade, enter_long=False, exit_short=False)
-    else:
-        patch_get_signal(freqtrade, enter_long=False, exit_long=False)
-    assert freqtrade.handle_trade(trade) is True
-    assert trade.exit_reason == ExitType.ROI.value
-
-
-@pytest.mark.parametrize("is_short,val1,val2", [
-    (False, 1.5, 1.1),
-    (True, 0.5, 0.9)
-])
-def test_trailing_stop_loss(default_conf_usdt, limit_order_open,
-                            is_short, val1, val2, fee, caplog, mocker) -> None:
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=MagicMock(return_value={
-            'bid': 2.0,
-            'ask': 2.0,
-            'last': 2.0
-        }),
-        create_order=MagicMock(side_effect=[
-            limit_order_open[enter_side(is_short)],
-            {'id': 1234553382},
-        ]),
-        get_fee=fee,
-    )
-    default_conf_usdt['trailing_stop'] = True
-    patch_whitelist(mocker, default_conf_usdt)
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
-    freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
-
-    freqtrade.enter_positions()
-    trade = Trade.query.first()
-    assert trade.is_short == is_short
-    assert freqtrade.handle_trade(trade) is False
-
-    # Raise praise into profits
-    mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
-                 MagicMock(return_value={
-                     'bid': 2.0 * val1,
-                     'ask': 2.0 * val1,
-                     'last': 2.0 * val1
-                 }))
-
-    # Stoploss should be adjusted
-    assert freqtrade.handle_trade(trade) is False
-    caplog.clear()
-    # Price fell
-    mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
-                 MagicMock(return_value={
-                     'bid': 2.0 * val2,
-                     'ask': 2.0 * val2,
-                     'last': 2.0 * val2
-                 }))
-
-    caplog.set_level(logging.DEBUG)
-    # Sell as trailing-stop is reached
-    assert freqtrade.handle_trade(trade) is True
-    stop_multi = 1.1 if is_short else 0.9
-    assert log_has(f"ETH/USDT - HIT STOP: current price at {(2.0 * val2):6f}, "
-                   f"stoploss is {(2.0 * val1 * stop_multi):6f}, "
-                   f"initial stoploss was at {(2.0 * stop_multi):6f}, trade opened at 2.000000",
-                   caplog)
-    assert trade.exit_reason == ExitType.TRAILING_STOP_LOSS.value
-
-
-@pytest.mark.parametrize('offset,trail_if_reached,second_sl,is_short', [
-    (0, False, 2.0394, False),
-    (0.011, False, 2.0394, False),
-    (0.055, True, 1.8, False),
-    (0, False, 2.1614, True),
-    (0.011, False, 2.1614, True),
-    (0.055, True, 2.42, True),
-])
-def test_trailing_stop_loss_positive(
-    default_conf_usdt, limit_order, limit_order_open,
-    offset, fee, caplog, mocker, trail_if_reached, second_sl, is_short
-) -> None:
-    enter_price = limit_order[enter_side(is_short)]['price']
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    eside = enter_side(is_short)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=MagicMock(return_value={
-            'bid': enter_price - (-0.01 if is_short else 0.01),
-            'ask': enter_price - (-0.01 if is_short else 0.01),
-            'last': enter_price - (-0.01 if is_short else 0.01),
-        }),
-        create_order=MagicMock(side_effect=[
-            limit_order_open[eside],
-            {'id': 1234553382},
-        ]),
-        get_fee=fee,
-    )
-    default_conf_usdt['trailing_stop'] = True
-    default_conf_usdt['trailing_stop_positive'] = 0.01
-    if offset:
-        default_conf_usdt['trailing_stop_positive_offset'] = offset
-        default_conf_usdt['trailing_only_offset_is_reached'] = trail_if_reached
-    patch_whitelist(mocker, default_conf_usdt)
-
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
-    freqtrade.strategy.min_roi_reached = MagicMock(return_value=False)
-    freqtrade.enter_positions()
-
-    trade = Trade.query.first()
-    assert trade.is_short == is_short
-    oobj = Order.parse_from_ccxt_object(limit_order[eside], limit_order[eside]['symbol'], eside)
-    trade.update_trade(oobj)
-    caplog.set_level(logging.DEBUG)
-    # stop-loss not reached
-    assert freqtrade.handle_trade(trade) is False
-
-    # Raise ticker_usdt above buy price
-    mocker.patch(
-        'freqtrade.exchange.Exchange.fetch_ticker',
-        MagicMock(return_value={
-            'bid': enter_price + (-0.06 if is_short else 0.06),
-            'ask': enter_price + (-0.06 if is_short else 0.06),
-            'last': enter_price + (-0.06 if is_short else 0.06),
-        })
-    )
-    # stop-loss not reached, adjusted stoploss
-    assert freqtrade.handle_trade(trade) is False
-    caplog_text = (f"ETH/USDT - Using positive stoploss: 0.01 offset: {offset} profit: "
-                   f"{'2.49' if not is_short else '2.24'}%")
-    if trail_if_reached:
-        assert not log_has(caplog_text, caplog)
-        assert not log_has("ETH/USDT - Adjusting stoploss...", caplog)
-    else:
-        assert log_has(caplog_text, caplog)
-        assert log_has("ETH/USDT - Adjusting stoploss...", caplog)
-    assert pytest.approx(trade.stop_loss) == second_sl
-    caplog.clear()
-
-    mocker.patch(
-        'freqtrade.exchange.Exchange.fetch_ticker',
-        MagicMock(return_value={
-            'bid': enter_price + (-0.135 if is_short else 0.125),
-            'ask': enter_price + (-0.135 if is_short else 0.125),
-            'last': enter_price + (-0.135 if is_short else 0.125),
-        })
-    )
-    assert freqtrade.handle_trade(trade) is False
-    assert log_has(
-        f"ETH/USDT - Using positive stoploss: 0.01 offset: {offset} profit: "
-        f"{'5.72' if not is_short else '5.67'}%",
-        caplog
-    )
-    assert log_has("ETH/USDT - Adjusting stoploss...", caplog)
-
-    mocker.patch(
-        'freqtrade.exchange.Exchange.fetch_ticker',
-        MagicMock(return_value={
-            'bid': enter_price + (-0.02 if is_short else 0.02),
-            'ask': enter_price + (-0.02 if is_short else 0.02),
-            'last': enter_price + (-0.02 if is_short else 0.02),
-        })
-    )
-    # Lower price again (but still positive)
-    assert freqtrade.handle_trade(trade) is True
-    assert log_has(
-        f"ETH/USDT - HIT STOP: current price at {enter_price + (-0.02 if is_short else 0.02):.6f}, "
-        f"stoploss is {trade.stop_loss:.6f}, "
-        f"initial stoploss was at {'2.42' if is_short else '1.80'}0000, "
-        f"trade opened at {2.2 if is_short else 2.0}00000",
-        caplog)
-    assert trade.exit_reason == ExitType.TRAILING_STOP_LOSS.value
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-def test_disable_ignore_roi_if_buy_signal(default_conf_usdt, limit_order, limit_order_open,
-                                          is_short, fee, mocker) -> None:
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    eside = enter_side(is_short)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=MagicMock(return_value={
-            'bid': 2.0,
-            'ask': 2.0,
-            'last': 2.0
-        }),
-        create_order=MagicMock(side_effect=[
-            limit_order_open[eside],
-            {'id': 1234553382},
-            {'id': 1234553383}
-        ]),
-        get_fee=fee,
-        _is_dry_limit_order_filled=MagicMock(return_value=False),
-    )
-    default_conf_usdt['exit_pricing'] = {
-        'ignore_roi_if_buy_signal': False
-    }
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
-    freqtrade.strategy.min_roi_reached = MagicMock(return_value=True)
-
-    freqtrade.enter_positions()
-
-    trade = Trade.query.first()
-    trade.is_short = is_short
-
-    oobj = Order.parse_from_ccxt_object(
-        limit_order[eside], limit_order[eside]['symbol'], eside)
-    trade.update_trade(oobj)
-    # Sell due to min_roi_reached
-    patch_get_signal(freqtrade, enter_long=not is_short, enter_short=is_short, exit_short=is_short)
-    assert freqtrade.handle_trade(trade) is True
-
-    # Test if entry-signal is absent
-    patch_get_signal(freqtrade)
-    assert freqtrade.handle_trade(trade) is True
-    assert trade.exit_reason == ExitType.ROI.value
-
-
-def test_get_real_amount_quote(default_conf_usdt, trades_for_order, buy_order_fee, fee, caplog,
-                               mocker):
-    mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order)
-    amount = sum(x['amount'] for x in trades_for_order)
-    trade = Trade(
-        pair='LTC/ETH',
-        amount=amount,
-        exchange='binance',
-        open_rate=0.245441,
-        fee_open=fee.return_value,
-        fee_close=fee.return_value,
-        open_order_id="123456"
-    )
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-
-    caplog.clear()
-    order_obj = Order.parse_from_ccxt_object(buy_order_fee, 'LTC/ETH', 'buy')
-    # Amount is reduced by "fee"
-    assert freqtrade.get_real_amount(trade, buy_order_fee, order_obj) == amount - (amount * 0.001)
-    assert log_has(
-        'Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, is_short=False,'
-        ' leverage=1.0, open_rate=0.24544100, open_since=closed) (from 8.0 to 7.992).',
-        caplog
-    )
-
-
-def test_get_real_amount_quote_dust(default_conf_usdt, trades_for_order, buy_order_fee, fee,
-                                    caplog, mocker):
-    mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order)
-    walletmock = mocker.patch('freqtrade.wallets.Wallets.update')
-    mocker.patch('freqtrade.wallets.Wallets.get_free', return_value=8.1122)
-    amount = sum(x['amount'] for x in trades_for_order)
-    trade = Trade(
-        pair='LTC/ETH',
-        amount=amount,
-        exchange='binance',
-        open_rate=0.245441,
-        fee_open=fee.return_value,
-        fee_close=fee.return_value,
-        open_order_id="123456"
-    )
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-
-    walletmock.reset_mock()
-    order_obj = Order.parse_from_ccxt_object(buy_order_fee, 'LTC/ETH', 'buy')
-    # Amount is kept as is
-    assert freqtrade.get_real_amount(trade, buy_order_fee, order_obj) == amount
-    assert walletmock.call_count == 1
-    assert log_has_re(r'Fee amount for Trade.* was in base currency '
-                      '- Eating Fee 0.008 into dust', caplog)
-
-
-def test_get_real_amount_no_trade(default_conf_usdt, buy_order_fee, caplog, mocker, fee):
-    mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=[])
-
-    amount = buy_order_fee['amount']
-    trade = Trade(
-        pair='LTC/ETH',
-        amount=amount,
-        exchange='binance',
-        open_rate=0.245441,
-        fee_open=fee.return_value,
-        fee_close=fee.return_value,
-        open_order_id="123456"
-    )
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-
-    order_obj = Order.parse_from_ccxt_object(buy_order_fee, 'LTC/ETH', 'buy')
-    # Amount is reduced by "fee"
-    assert freqtrade.get_real_amount(trade, buy_order_fee, order_obj) == amount
-    assert log_has(
-        'Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, '
-        'is_short=False, leverage=1.0, open_rate=0.24544100, open_since=closed) failed: '
-        'myTrade-Dict empty found',
-        caplog
-    )
-
-
-@pytest.mark.parametrize(
-    'fee_par,fee_reduction_amount,use_ticker_usdt_rate,expected_log', [
-        # basic, amount does not change
-        ({'cost': 0.008, 'currency': 'ETH'}, 0, False, None),
-        # no currency in fee
-        ({'cost': 0.004, 'currency': None}, 0, True, None),
-        # BNB no rate
-        ({'cost': 0.00094518, 'currency': 'BNB'}, 0, True, (
-            'Fee for Trade Trade(id=None, pair=LTC/ETH, amount=8.00000000, is_short=False, '
-            'leverage=1.0, open_rate=0.24544100, open_since=closed) [buy]: 0.00094518 BNB -'
-            ' rate: None'
-        )),
-        # from order
-        ({'cost': 0.004, 'currency': 'LTC'}, 0.004, False, (
-            'Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, '
-            'is_short=False, leverage=1.0, open_rate=0.24544100, open_since=closed) (from'
-            ' 8.0 to 7.996).'
-        )),
-        # invalid, no currency in from fee dict
-        ({'cost': 0.008, 'currency': None}, 0, True, None),
-    ])
-def test_get_real_amount(
-    default_conf_usdt, trades_for_order, buy_order_fee, fee, mocker, caplog,
-    fee_par, fee_reduction_amount, use_ticker_usdt_rate, expected_log
-):
-
-    buy_order = deepcopy(buy_order_fee)
-    buy_order['fee'] = fee_par
-    trades_for_order[0]['fee'] = fee_par
-
-    mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order)
-    amount = sum(x['amount'] for x in trades_for_order)
-    trade = Trade(
-        pair='LTC/ETH',
-        amount=amount,
-        exchange='binance',
-        fee_open=fee.return_value,
-        fee_close=fee.return_value,
-        open_rate=0.245441,
-        open_order_id="123456"
-    )
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-
-    if not use_ticker_usdt_rate:
-        mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', side_effect=ExchangeError)
-
-    caplog.clear()
-    order_obj = Order.parse_from_ccxt_object(buy_order_fee, 'LTC/ETH', 'buy')
-    assert freqtrade.get_real_amount(trade, buy_order, order_obj) == amount - fee_reduction_amount
-
-    if expected_log:
-        assert log_has(expected_log, caplog)
-
-
-@pytest.mark.parametrize(
-    'fee_cost, fee_currency, fee_reduction_amount, expected_fee, expected_log_amount', [
-        # basic, amount is reduced by fee
-        (None, None, 0.001, 0.001, 7.992),
-        # different fee currency on both trades, fee is average of both trade's fee
-        (0.02, 'BNB', 0.0005, 0.001518575, 7.996),
-    ])
-def test_get_real_amount_multi(
-    default_conf_usdt, trades_for_order2, buy_order_fee, caplog, fee, mocker, markets,
-    fee_cost, fee_currency, fee_reduction_amount, expected_fee, expected_log_amount,
-):
-
-    trades_for_order = deepcopy(trades_for_order2)
-    if fee_cost:
-        trades_for_order[0]['fee']['cost'] = fee_cost
-    if fee_currency:
-        trades_for_order[0]['fee']['currency'] = fee_currency
-
-    mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order)
-    amount = float(sum(x['amount'] for x in trades_for_order))
-    default_conf_usdt['stake_currency'] = "ETH"
-
-    trade = Trade(
-        pair='LTC/ETH',
-        amount=amount,
-        exchange='binance',
-        fee_open=fee.return_value,
-        fee_close=fee.return_value,
-        open_rate=0.245441,
-        open_order_id="123456"
-    )
-
-    # Fake markets entry to enable fee parsing
-    markets['BNB/ETH'] = markets['ETH/USDT']
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-    mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets))
-    mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
-                 return_value={'ask': 0.19, 'last': 0.2})
-
-    # Amount is reduced by "fee"
-    expected_amount = amount - (amount * fee_reduction_amount)
-    order_obj = Order.parse_from_ccxt_object(buy_order_fee, 'LTC/ETH', 'buy')
-    assert freqtrade.get_real_amount(trade, buy_order_fee, order_obj) == expected_amount
-    assert log_has(
-        (
-            'Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, '
-            'is_short=False, leverage=1.0, open_rate=0.24544100, open_since=closed) '
-            f'(from 8.0 to {expected_log_amount}).'
-        ),
-        caplog
-    )
-
-    assert trade.fee_open == expected_fee
-    assert trade.fee_close == expected_fee
-    assert trade.fee_open_cost is not None
-    assert trade.fee_open_currency is not None
-    assert trade.fee_close_cost is None
-    assert trade.fee_close_currency is None
-
-
-def test_get_real_amount_invalid_order(default_conf_usdt, trades_for_order, buy_order_fee, fee,
-                                       mocker):
-    limit_buy_order_usdt = deepcopy(buy_order_fee)
-    limit_buy_order_usdt['fee'] = {'cost': 0.004}
-
-    mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=[])
-    amount = float(sum(x['amount'] for x in trades_for_order))
-    trade = Trade(
-        pair='LTC/ETH',
-        amount=amount,
-        exchange='binance',
-        fee_open=fee.return_value,
-        fee_close=fee.return_value,
-        open_rate=0.245441,
-        open_order_id="123456"
-    )
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-
-    order_obj = Order.parse_from_ccxt_object(buy_order_fee, 'LTC/ETH', 'buy')
-    # Amount does not change
-    assert freqtrade.get_real_amount(trade, limit_buy_order_usdt, order_obj) == amount
-
-
-def test_get_real_amount_fees_order(default_conf_usdt, market_buy_order_usdt_doublefee,
-                                    fee, mocker):
-
-    tfo_mock = mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=[])
-    mocker.patch('freqtrade.exchange.Exchange.get_valid_pair_combination', return_value='BNB/USDT')
-    mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', return_value={'last': 200})
-    trade = Trade(
-        pair='LTC/USDT',
-        amount=30.0,
-        exchange='binance',
-        fee_open=fee.return_value,
-        fee_close=fee.return_value,
-        open_rate=0.245441,
-        open_order_id="123456"
-    )
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-
-    # Amount does not change
-    assert trade.fee_open == 0.0025
-    order_obj = Order.parse_from_ccxt_object(market_buy_order_usdt_doublefee, 'LTC/ETH', 'buy')
-    assert freqtrade.get_real_amount(trade, market_buy_order_usdt_doublefee, order_obj) == 30.0
-    assert tfo_mock.call_count == 0
-    # Fetch fees from trades dict if available to get "proper" values
-    assert round(trade.fee_open, 4) == 0.001
-
-
-def test_get_real_amount_wrong_amount(default_conf_usdt, trades_for_order, buy_order_fee, fee,
-                                      mocker):
-    limit_buy_order_usdt = deepcopy(buy_order_fee)
-    limit_buy_order_usdt['amount'] = limit_buy_order_usdt['amount'] - 0.001
-
-    mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order)
-    amount = float(sum(x['amount'] for x in trades_for_order))
-    trade = Trade(
-        pair='LTC/ETH',
-        amount=amount,
-        exchange='binance',
-        open_rate=0.245441,
-        fee_open=fee.return_value,
-        fee_close=fee.return_value,
-        open_order_id="123456"
-    )
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-
-    order_obj = Order.parse_from_ccxt_object(buy_order_fee, 'LTC/ETH', 'buy')
-    # Amount does not change
-    with pytest.raises(DependencyException, match=r"Half bought\? Amounts don't match"):
-        freqtrade.get_real_amount(trade, limit_buy_order_usdt, order_obj)
-
-
-def test_get_real_amount_wrong_amount_rounding(default_conf_usdt, trades_for_order, buy_order_fee,
-                                               fee, mocker):
-    # Floats should not be compared directly.
-    limit_buy_order_usdt = deepcopy(buy_order_fee)
-    trades_for_order[0]['amount'] = trades_for_order[0]['amount'] + 1e-15
-
-    mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order)
-    amount = float(sum(x['amount'] for x in trades_for_order))
-    trade = Trade(
-        pair='LTC/ETH',
-        amount=amount,
-        exchange='binance',
-        fee_open=fee.return_value,
-        fee_close=fee.return_value,
-        open_rate=0.245441,
-        open_order_id="123456"
-    )
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-
-    order_obj = Order.parse_from_ccxt_object(buy_order_fee, 'LTC/ETH', 'buy')
-    # Amount changes by fee amount.
-    assert isclose(
-        freqtrade.get_real_amount(trade, limit_buy_order_usdt, order_obj),
-        amount - (amount * 0.001),
-        abs_tol=MATH_CLOSE_PREC,
-    )
-
-
-def test_get_real_amount_open_trade_usdt(default_conf_usdt, fee, mocker):
-    amount = 12345
-    trade = Trade(
-        pair='LTC/ETH',
-        amount=amount,
-        exchange='binance',
-        open_rate=0.245441,
-        fee_open=fee.return_value,
-        fee_close=fee.return_value,
-        open_order_id="123456"
-    )
-    order = {
-        'id': 'mocked_order',
-        'amount': amount,
-        'status': 'open',
-        'side': 'buy',
-    }
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-    order_obj = Order.parse_from_ccxt_object(order, 'LTC/ETH', 'buy')
-    assert freqtrade.get_real_amount(trade, order, order_obj) == amount
-
-
-@pytest.mark.parametrize('amount,fee_abs,wallet,amount_exp', [
-    (8.0, 0.0, 10, 8),
-    (8.0, 0.0, 0, 8),
-    (8.0, 0.1, 0, 7.9),
-    (8.0, 0.1, 10, 8),
-    (8.0, 0.1, 8.0, 8.0),
-    (8.0, 0.1, 7.9, 7.9),
-])
-def test_apply_fee_conditional(default_conf_usdt, fee, mocker,
-                               amount, fee_abs, wallet, amount_exp):
-    walletmock = mocker.patch('freqtrade.wallets.Wallets.update')
-    mocker.patch('freqtrade.wallets.Wallets.get_free', return_value=wallet)
-    trade = Trade(
-        pair='LTC/ETH',
-        amount=amount,
-        exchange='binance',
-        open_rate=0.245441,
-        fee_open=fee.return_value,
-        fee_close=fee.return_value,
-        open_order_id="123456"
-    )
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-
-    walletmock.reset_mock()
-    # Amount is kept as is
-    assert freqtrade.apply_fee_conditional(trade, 'LTC', amount, fee_abs) == amount_exp
-    assert walletmock.call_count == 1
-
-
-@pytest.mark.parametrize("delta, is_high_delta", [
-    (0.1, False),
-    (100, True),
-])
-@pytest.mark.parametrize('is_short', [False, True])
-def test_order_book_depth_of_market(
-    default_conf_usdt, ticker_usdt, limit_order_open,
-    fee, mocker, order_book_l2, delta, is_high_delta, is_short
-):
-    ticker_side = 'ask' if is_short else 'bid'
-
-    default_conf_usdt['entry_pricing']['check_depth_of_market']['enabled'] = True
-    default_conf_usdt['entry_pricing']['check_depth_of_market']['bids_to_ask_delta'] = delta
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    mocker.patch('freqtrade.exchange.Exchange.fetch_l2_order_book', order_book_l2)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        create_order=MagicMock(return_value=limit_order_open[enter_side(is_short)]),
-        get_fee=fee,
-    )
-
-    # Save state of current whitelist
-    whitelist = deepcopy(default_conf_usdt['exchange']['pair_whitelist'])
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short)
-    freqtrade.enter_positions()
-
-    trade = Trade.query.first()
-    if is_high_delta:
-        assert trade is None
-    else:
-        trade.is_short = is_short
-        assert trade is not None
-        assert pytest.approx(trade.stake_amount) == 60.0
-        assert trade.is_open
-        assert trade.open_date is not None
-        assert trade.exchange == 'binance'
-
-        assert len(Trade.query.all()) == 1
-
-        # Simulate fulfilled LIMIT_BUY order for trade
-        oobj = Order.parse_from_ccxt_object(
-            limit_order_open[enter_side(is_short)], 'ADA/USDT', enter_side(is_short))
-        trade.update_trade(oobj)
-
-        assert trade.open_rate == ticker_usdt.return_value[ticker_side]
-        assert whitelist == default_conf_usdt['exchange']['pair_whitelist']
-
-
-@pytest.mark.parametrize('exception_thrown,ask,last,order_book_top,order_book', [
-    (False, 0.045, 0.046, 2, None),
-    (True,  0.042, 0.046, 1, {'bids': [[]], 'asks': [[]]})
-])
-def test_order_book_entry_pricing1(mocker, default_conf_usdt, order_book_l2, exception_thrown,
-                                   ask, last, order_book_top, order_book, caplog) -> None:
-    """
-    test if function get_rate will return the order book price instead of the ask rate
-    """
-    patch_exchange(mocker)
-    ticker_usdt_mock = MagicMock(return_value={'ask': ask, 'last': last})
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_l2_order_book=MagicMock(return_value=order_book) if order_book else order_book_l2,
-        fetch_ticker=ticker_usdt_mock,
-    )
-    default_conf_usdt['exchange']['name'] = 'binance'
-    default_conf_usdt['entry_pricing']['use_order_book'] = True
-    default_conf_usdt['entry_pricing']['order_book_top'] = order_book_top
-    default_conf_usdt['entry_pricing']['price_last_balance'] = 0
-    default_conf_usdt['telegram']['enabled'] = False
-
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    if exception_thrown:
-        with pytest.raises(PricingError):
-            freqtrade.exchange.get_rate('ETH/USDT', side="entry", is_short=False, refresh=True)
-        assert log_has_re(
-<<<<<<< HEAD
-            r'ETH/USDT - Buy Price at location 1 from orderbook could not be determined.', caplog)
-=======
-            r'Entry Price at location 1 from orderbook could not be determined.', caplog)
->>>>>>> develop
-    else:
-        assert freqtrade.exchange.get_rate(
-            'ETH/USDT', side="entry", is_short=False, refresh=True) == 0.043935
-        assert ticker_usdt_mock.call_count == 0
-
-
-def test_check_depth_of_market(default_conf_usdt, mocker, order_book_l2) -> None:
-    """
-    test check depth of market
-    """
-    patch_exchange(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_l2_order_book=order_book_l2
-    )
-    default_conf_usdt['telegram']['enabled'] = False
-    default_conf_usdt['exchange']['name'] = 'binance'
-    default_conf_usdt['entry_pricing']['check_depth_of_market']['enabled'] = True
-    # delta is 100 which is impossible to reach. hence function will return false
-    default_conf_usdt['entry_pricing']['check_depth_of_market']['bids_to_ask_delta'] = 100
-    freqtrade = FreqtradeBot(default_conf_usdt)
-
-    conf = default_conf_usdt['entry_pricing']['check_depth_of_market']
-    assert freqtrade._check_depth_of_market('ETH/BTC', conf, side=SignalDirection.LONG) is False
-
-
-@pytest.mark.parametrize('is_short', [False, True])
-def test_order_book_exit_pricing(
-        default_conf_usdt, limit_buy_order_usdt_open, limit_buy_order_usdt, fee, is_short,
-        limit_sell_order_usdt_open, mocker, order_book_l2, caplog) -> None:
-    """
-    test order book ask strategy
-    """
-    mocker.patch('freqtrade.exchange.Exchange.fetch_l2_order_book', order_book_l2)
-    default_conf_usdt['exchange']['name'] = 'binance'
-    default_conf_usdt['exit_pricing']['use_order_book'] = True
-    default_conf_usdt['exit_pricing']['order_book_top'] = 1
-    default_conf_usdt['telegram']['enabled'] = False
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=MagicMock(return_value={
-            'bid': 1.9,
-            'ask': 2.2,
-            'last': 1.9
-        }),
-        create_order=MagicMock(side_effect=[
-            limit_buy_order_usdt_open,
-            limit_sell_order_usdt_open,
-        ]),
-        get_fee=fee,
-    )
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    patch_get_signal(freqtrade)
-
-    freqtrade.enter_positions()
-
-    trade = Trade.query.first()
-    assert trade
-
-    time.sleep(0.01)  # Race condition fix
-    oobj = Order.parse_from_ccxt_object(limit_buy_order_usdt, limit_buy_order_usdt['symbol'], 'buy')
-    trade.update_trade(oobj)
-    freqtrade.wallets.update()
-    assert trade.is_open is True
-
-    if is_short:
-        patch_get_signal(freqtrade, enter_long=False,  exit_short=True)
-    else:
-        patch_get_signal(freqtrade, enter_long=False, exit_long=True)
-    assert freqtrade.handle_trade(trade) is True
-    assert trade.close_rate_requested == order_book_l2.return_value['asks'][0][0]
-
-    mocker.patch('freqtrade.exchange.Exchange.fetch_l2_order_book',
-                 return_value={'bids': [[]], 'asks': [[]]})
-    with pytest.raises(PricingError):
-        freqtrade.handle_trade(trade)
-<<<<<<< HEAD
-    pair = 'ETH/USDT'
-    assert log_has_re(
-        rf"{pair} - Sell Price at location 1 from orderbook could not be determined\..*",
-        caplog)
-=======
-    assert log_has_re(r'Exit Price at location 1 from orderbook could not be determined\..*',
-                      caplog)
->>>>>>> develop
-
-
-def test_startup_state(default_conf_usdt, mocker):
-    default_conf_usdt['pairlist'] = {'method': 'VolumePairList',
-                                     'config': {'number_assets': 20}
-                                     }
-    mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True))
-    worker = get_patched_worker(mocker, default_conf_usdt)
-    assert worker.freqtrade.state is State.RUNNING
-
-
-def test_startup_trade_reinit(default_conf_usdt, edge_conf, mocker):
-
-    mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True))
-    reinit_mock = MagicMock()
-    mocker.patch('freqtrade.persistence.Trade.stoploss_reinitialization', reinit_mock)
-
-    ftbot = get_patched_freqtradebot(mocker, default_conf_usdt)
-    ftbot.startup()
-    assert reinit_mock.call_count == 1
-
-    reinit_mock.reset_mock()
-
-    ftbot = get_patched_freqtradebot(mocker, edge_conf)
-    ftbot.startup()
-    assert reinit_mock.call_count == 0
-
-
-@pytest.mark.usefixtures("init_persistence")
-def test_sync_wallet_dry_run(mocker, default_conf_usdt, ticker_usdt, fee, limit_buy_order_usdt_open,
-                             caplog):
-    default_conf_usdt['dry_run'] = True
-    # Initialize to 2 times stake amount
-    default_conf_usdt['dry_run_wallet'] = 120.0
-    default_conf_usdt['max_open_trades'] = 2
-    default_conf_usdt['tradable_balance_ratio'] = 1.0
-    patch_exchange(mocker)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        fetch_ticker=ticker_usdt,
-        create_order=MagicMock(return_value=limit_buy_order_usdt_open),
-        get_fee=fee,
-    )
-
-    bot = get_patched_freqtradebot(mocker, default_conf_usdt)
-    patch_get_signal(bot)
-    assert bot.wallets.get_free('USDT') == 120.0
-
-    n = bot.enter_positions()
-    assert n == 2
-    trades = Trade.query.all()
-    assert len(trades) == 2
-
-    bot.config['max_open_trades'] = 3
-    n = bot.enter_positions()
-    assert n == 0
-    assert log_has_re(r"Unable to create trade for XRP/USDT: "
-                      r"Available balance \(0.0 USDT\) is lower than stake amount \(60.0 USDT\)",
-                      caplog)
-
-
-@pytest.mark.usefixtures("init_persistence")
-@pytest.mark.parametrize("is_short,buy_calls,sell_calls", [
-    (False, 1, 2),
-    (True, 1, 2),
-])
-def test_cancel_all_open_orders(mocker, default_conf_usdt, fee, limit_order, limit_order_open,
-                                is_short, buy_calls, sell_calls):
-    default_conf_usdt['cancel_open_orders_on_exit'] = True
-    mocker.patch(
-        'freqtrade.exchange.Exchange.fetch_order',
-        side_effect=[
-            ExchangeError(),
-            limit_order[exit_side(is_short)],
-            limit_order_open[enter_side(is_short)],
-            limit_order_open[exit_side(is_short)],
-        ]
-    )
-    buy_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_cancel_enter')
-    sell_mock = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_cancel_exit')
-
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-    create_mock_trades(fee, is_short=is_short)
-    trades = Trade.query.all()
-    assert len(trades) == MOCK_TRADE_COUNT
-    freqtrade.cancel_all_open_orders()
-    assert buy_mock.call_count == buy_calls
-    assert sell_mock.call_count == sell_calls
-
-
-@pytest.mark.usefixtures("init_persistence")
-@pytest.mark.parametrize("is_short", [False, True])
-def test_check_for_open_trades(mocker, default_conf_usdt, fee, is_short):
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-
-    freqtrade.check_for_open_trades()
-    assert freqtrade.rpc.send_msg.call_count == 0
-
-    create_mock_trades(fee, is_short)
-    trade = Trade.query.first()
-    trade.is_short = is_short
-    trade.is_open = True
-
-    freqtrade.check_for_open_trades()
-    assert freqtrade.rpc.send_msg.call_count == 1
-    assert 'Handle these trades manually' in freqtrade.rpc.send_msg.call_args[0][0]['status']
-
-
-@pytest.mark.parametrize("is_short", [False, True])
-@pytest.mark.usefixtures("init_persistence")
-def test_startup_update_open_orders(mocker, default_conf_usdt, fee, caplog, is_short):
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-    create_mock_trades(fee, is_short=is_short)
-
-    freqtrade.startup_update_open_orders()
-    assert not log_has_re(r"Error updating Order .*", caplog)
-    caplog.clear()
-
-    freqtrade.config['dry_run'] = False
-    freqtrade.startup_update_open_orders()
-
-    assert log_has_re(r"Error updating Order .*", caplog)
-    caplog.clear()
-
-    assert len(Order.get_open_orders()) == 3
-    matching_buy_order = mock_order_4(is_short=is_short)
-    matching_buy_order.update({
-        'status': 'closed',
-    })
-    mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=matching_buy_order)
-    freqtrade.startup_update_open_orders()
-    # Only stoploss and sell orders are kept open
-    assert len(Order.get_open_orders()) == 2
-
-
-@pytest.mark.usefixtures("init_persistence")
-@pytest.mark.parametrize("is_short", [False, True])
-def test_update_closed_trades_without_assigned_fees(mocker, default_conf_usdt, fee, is_short):
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-
-    def patch_with_fee(order):
-        order.update({'fee': {'cost': 0.1, 'rate': 0.01,
-                      'currency': order['symbol'].split('/')[0]}})
-        return order
-
-    mocker.patch('freqtrade.exchange.Exchange.fetch_order_or_stoploss_order',
-                 side_effect=[
-                     patch_with_fee(mock_order_2_sell(is_short=is_short)),
-                     patch_with_fee(mock_order_3_sell(is_short=is_short)),
-                     patch_with_fee(mock_order_1(is_short=is_short)),
-                     patch_with_fee(mock_order_2(is_short=is_short)),
-                     patch_with_fee(mock_order_3(is_short=is_short)),
-                     patch_with_fee(mock_order_4(is_short=is_short)),
-                 ]
-                 )
-
-    create_mock_trades(fee, is_short=is_short)
-    trades = Trade.get_trades().all()
-    assert len(trades) == MOCK_TRADE_COUNT
-    for trade in trades:
-        trade.is_short = is_short
-        assert trade.fee_open_cost is None
-        assert trade.fee_open_currency is None
-        assert trade.fee_close_cost is None
-        assert trade.fee_close_currency is None
-
-    freqtrade.update_closed_trades_without_assigned_fees()
-
-    # Does nothing for dry-run
-    trades = Trade.get_trades().all()
-    assert len(trades) == MOCK_TRADE_COUNT
-    for trade in trades:
-        assert trade.fee_open_cost is None
-        assert trade.fee_open_currency is None
-        assert trade.fee_close_cost is None
-        assert trade.fee_close_currency is None
-
-    freqtrade.config['dry_run'] = False
-
-    freqtrade.update_closed_trades_without_assigned_fees()
-
-    trades = Trade.get_trades().all()
-    assert len(trades) == MOCK_TRADE_COUNT
-
-    for trade in trades:
-        if trade.is_open:
-            # Exclude Trade 4 - as the order is still open.
-            if trade.select_order(enter_side(is_short), False):
-                assert trade.fee_open_cost is not None
-                assert trade.fee_open_currency is not None
-            else:
-                assert trade.fee_open_cost is None
-                assert trade.fee_open_currency is None
-
-        else:
-            assert trade.fee_close_cost is not None
-            assert trade.fee_close_currency is not None
-
-
-@pytest.mark.usefixtures("init_persistence")
-@pytest.mark.parametrize("is_short", [False, True])
-def test_reupdate_enter_order_fees(mocker, default_conf_usdt, fee, caplog, is_short):
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-    mock_uts = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.update_trade_state')
-
-    mocker.patch('freqtrade.exchange.Exchange.fetch_order_or_stoploss_order',
-                 return_value={'status': 'open'})
-    create_mock_trades(fee, is_short)
-    trades = Trade.get_trades().all()
-
-    freqtrade.handle_insufficient_funds(trades[3])
-    # assert log_has_re(r"Trying to reupdate buy fees for .*", caplog)
-    assert mock_uts.call_count == 1
-    assert mock_uts.call_args_list[0][0][0] == trades[3]
-    assert mock_uts.call_args_list[0][0][1] == mock_order_4(is_short)['id']
-    assert log_has_re(r"Trying to refind lost order for .*", caplog)
-    mock_uts.reset_mock()
-    caplog.clear()
-
-    # Test with trade without orders
-    trade = Trade(
-        pair='XRP/ETH',
-        stake_amount=60.0,
-        fee_open=fee.return_value,
-        fee_close=fee.return_value,
-        open_date=arrow.utcnow().datetime,
-        is_open=True,
-        amount=30,
-        open_rate=2.0,
-        exchange='binance',
-        is_short=is_short
-    )
-    Trade.query.session.add(trade)
-
-    freqtrade.handle_insufficient_funds(trade)
-    # assert log_has_re(r"Trying to reupdate buy fees for .*", caplog)
-    assert mock_uts.call_count == 0
-
-
-@pytest.mark.usefixtures("init_persistence")
-@pytest.mark.parametrize("is_short", [False, True])
-def test_handle_insufficient_funds(mocker, default_conf_usdt, fee, is_short, caplog):
-    caplog.set_level(logging.DEBUG)
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-    mock_uts = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.update_trade_state')
-
-    mock_fo = mocker.patch('freqtrade.exchange.Exchange.fetch_order_or_stoploss_order',
-                           return_value={'status': 'open'})
-
-    def reset_open_orders(trade):
-        trade.open_order_id = None
-        trade.stoploss_order_id = None
-        trade.is_short = is_short
-
-    create_mock_trades(fee, is_short=is_short)
-    trades = Trade.get_trades().all()
-
-    caplog.clear()
-
-    # No open order
-    trade = trades[0]
-    reset_open_orders(trade)
-    assert trade.open_order_id is None
-    assert trade.stoploss_order_id is None
-
-    freqtrade.handle_insufficient_funds(trade)
-    order = mock_order_1(is_short=is_short)
-    assert log_has_re(r"Order Order(.*order_id=" + order['id'] + ".*) is no longer open.", caplog)
-    assert mock_fo.call_count == 0
-    assert mock_uts.call_count == 0
-    # No change to orderid - as update_trade_state is mocked
-    assert trade.open_order_id is None
-    assert trade.stoploss_order_id is None
-
-    caplog.clear()
-    mock_fo.reset_mock()
-
-    # Open buy order
-    trade = trades[3]
-    reset_open_orders(trade)
-    assert trade.open_order_id is None
-    assert trade.stoploss_order_id is None
-
-    freqtrade.handle_insufficient_funds(trade)
-    order = mock_order_4(is_short=is_short)
-    assert log_has_re(r"Trying to refind Order\(.*", caplog)
-    assert mock_fo.call_count == 1
-    assert mock_uts.call_count == 1
-    # Found open buy order
-    assert trade.open_order_id is not None
-    assert trade.stoploss_order_id is None
-
-    caplog.clear()
-    mock_fo.reset_mock()
-
-    # Open stoploss order
-    trade = trades[4]
-    reset_open_orders(trade)
-    assert trade.open_order_id is None
-    assert trade.stoploss_order_id is None
-
-    freqtrade.handle_insufficient_funds(trade)
-    order = mock_order_5_stoploss(is_short=is_short)
-    assert log_has_re(r"Trying to refind Order\(.*", caplog)
-    assert mock_fo.call_count == 1
-    assert mock_uts.call_count == 2
-    # stoploss_order_id is "refound" and added to the trade
-    assert trade.open_order_id is None
-    assert trade.stoploss_order_id is not None
-
-    caplog.clear()
-    mock_fo.reset_mock()
-    mock_uts.reset_mock()
-
-    # Open sell order
-    trade = trades[5]
-    reset_open_orders(trade)
-    assert trade.open_order_id is None
-    assert trade.stoploss_order_id is None
-
-    freqtrade.handle_insufficient_funds(trade)
-    order = mock_order_6_sell(is_short=is_short)
-    assert log_has_re(r"Trying to refind Order\(.*", caplog)
-    assert mock_fo.call_count == 1
-    assert mock_uts.call_count == 1
-    # sell-orderid is "refound" and added to the trade
-    assert trade.open_order_id == order['id']
-    assert trade.stoploss_order_id is None
-
-    caplog.clear()
-
-    # Test error case
-    mock_fo = mocker.patch('freqtrade.exchange.Exchange.fetch_order_or_stoploss_order',
-                           side_effect=ExchangeError())
-    order = mock_order_5_stoploss(is_short=is_short)
-
-    freqtrade.handle_insufficient_funds(trades[4])
-    assert log_has(f"Error updating {order['id']}.", caplog)
-
-
-def test_get_valid_price(mocker, default_conf_usdt) -> None:
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    freqtrade.config['custom_price_max_distance_ratio'] = 0.02
-
-    custom_price_string = "10"
-    custom_price_badstring = "10abc"
-    custom_price_float = 10.0
-    custom_price_int = 10
-
-    custom_price_over_max_alwd = 11.0
-    custom_price_under_min_alwd = 9.0
-    proposed_price = 10.1
-
-    valid_price_from_string = freqtrade.get_valid_price(custom_price_string, proposed_price)
-    valid_price_from_badstring = freqtrade.get_valid_price(custom_price_badstring, proposed_price)
-    valid_price_from_int = freqtrade.get_valid_price(custom_price_int, proposed_price)
-    valid_price_from_float = freqtrade.get_valid_price(custom_price_float, proposed_price)
-
-    valid_price_at_max_alwd = freqtrade.get_valid_price(custom_price_over_max_alwd, proposed_price)
-    valid_price_at_min_alwd = freqtrade.get_valid_price(custom_price_under_min_alwd, proposed_price)
-
-    assert isinstance(valid_price_from_string, float)
-    assert isinstance(valid_price_from_badstring, float)
-    assert isinstance(valid_price_from_int, float)
-    assert isinstance(valid_price_from_float, float)
-
-    assert valid_price_from_string == custom_price_float
-    assert valid_price_from_badstring == proposed_price
-    assert valid_price_from_int == custom_price_int
-    assert valid_price_from_float == custom_price_float
-
-    assert valid_price_at_max_alwd < custom_price_over_max_alwd
-    assert valid_price_at_max_alwd > proposed_price
-
-    assert valid_price_at_min_alwd > custom_price_under_min_alwd
-    assert valid_price_at_min_alwd < proposed_price
-
-
-@pytest.mark.parametrize('trading_mode,calls,t1,t2', [
-    ('spot', 0, "2021-09-01 00:00:00", "2021-09-01 08:00:00"),
-    ('margin', 0, "2021-09-01 00:00:00", "2021-09-01 08:00:00"),
-    ('futures', 31, "2021-09-01 00:00:02", "2021-09-01 08:00:01"),
-    ('futures', 32, "2021-08-31 23:59:59", "2021-09-01 08:00:01"),
-    ('futures', 32, "2021-09-01 00:00:02", "2021-09-01 08:00:02"),
-    ('futures', 33, "2021-08-31 23:59:59", "2021-09-01 08:00:02"),
-    ('futures', 33, "2021-08-31 23:59:59", "2021-09-01 08:00:03"),
-    ('futures', 33, "2021-08-31 23:59:59", "2021-09-01 08:00:04"),
-    ('futures', 33, "2021-08-31 23:59:59", "2021-09-01 08:00:05"),
-    ('futures', 33, "2021-08-31 23:59:59", "2021-09-01 08:00:06"),
-    ('futures', 33, "2021-08-31 23:59:59", "2021-09-01 08:00:07"),
-    ('futures', 33, "2021-08-31 23:59:58", "2021-09-01 08:00:07"),
-])
-def test_update_funding_fees_schedule(mocker, default_conf, trading_mode, calls, time_machine,
-                                      t1, t2):
-    time_machine.move_to(f"{t1} +00:00")
-
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    mocker.patch('freqtrade.freqtradebot.FreqtradeBot.update_funding_fees', return_value=True)
-    default_conf['trading_mode'] = trading_mode
-    default_conf['margin_mode'] = 'isolated'
-    freqtrade = get_patched_freqtradebot(mocker, default_conf)
-
-    time_machine.move_to(f"{t2} +00:00")
-    # Check schedule jobs in debugging with freqtrade._schedule.jobs
-    freqtrade._schedule.run_pending()
-
-    assert freqtrade.update_funding_fees.call_count == calls
-
-
-@pytest.mark.parametrize('schedule_off', [False, True])
-@pytest.mark.parametrize('is_short', [True, False])
-def test_update_funding_fees(
-    mocker,
-    default_conf,
-    time_machine,
-    fee,
-    ticker_usdt_sell_up,
-    is_short,
-    limit_order_open,
-    schedule_off
-):
-    """
-    nominal_value = mark_price * size
-    funding_fee = nominal_value * funding_rate
-    size = 123
-    "LTC/USDT"
-        time: 0, mark: 3.3, fundRate: 0.00032583, nominal_value: 405.9, fundFee: 0.132254397
-        time: 8, mark: 3.2, fundRate: 0.00024472, nominal_value: 393.6, fundFee: 0.096321792
-    "ETH/USDT"
-        time: 0, mark: 2.4, fundRate: 0.0001, nominal_value: 295.2, fundFee: 0.02952
-        time: 8, mark: 2.5, fundRate: 0.0001, nominal_value: 307.5, fundFee: 0.03075
-    "ETC/USDT"
-        time: 0, mark: 4.3, fundRate: 0.00031077, nominal_value: 528.9, fundFee: 0.164366253
-        time: 8, mark: 4.1, fundRate: 0.00022655, nominal_value: 504.3, fundFee: 0.114249165
-    "XRP/USDT"
-        time: 0, mark: 1.2, fundRate: 0.00049426, nominal_value: 147.6, fundFee: 0.072952776
-        time: 8, mark: 1.2, fundRate: 0.00032715, nominal_value: 147.6, fundFee: 0.04828734
-    """
-    # SETUP
-    time_machine.move_to("2021-09-01 00:00:00 +00:00")
-
-    open_order = limit_order_open[enter_side(is_short)]
-    open_exit_order = limit_order_open[exit_side(is_short)]
-    bid = 0.11
-    enter_rate_mock = MagicMock(return_value=bid)
-    enter_mm = MagicMock(return_value=open_order)
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    default_conf['trading_mode'] = 'futures'
-    default_conf['margin_mode'] = 'isolated'
-
-    date_midnight = arrow.get('2021-09-01 00:00:00').datetime
-    date_eight = arrow.get('2021-09-01 08:00:00').datetime
-    date_sixteen = arrow.get('2021-09-01 16:00:00').datetime
-    columns = ['date', 'open', 'high', 'low', 'close', 'volume']
-    # 16:00 entry is actually never used
-    # But should be kept in the test to ensure we're filtering correctly.
-    funding_rates = {
-        "LTC/USDT":
-            DataFrame([
-                [date_midnight, 0.00032583, 0, 0, 0, 0],
-                [date_eight, 0.00024472, 0, 0, 0, 0],
-                [date_sixteen, 0.00024472, 0, 0, 0, 0],
-            ], columns=columns),
-        "ETH/USDT":
-            DataFrame([
-                [date_midnight, 0.0001, 0, 0, 0, 0],
-                [date_eight, 0.0001, 0, 0, 0, 0],
-                [date_sixteen, 0.0001, 0, 0, 0, 0],
-            ], columns=columns),
-        "XRP/USDT":
-            DataFrame([
-                [date_midnight, 0.00049426, 0, 0, 0, 0],
-                [date_eight, 0.00032715, 0, 0, 0, 0],
-                [date_sixteen, 0.00032715, 0, 0, 0, 0],
-            ], columns=columns)
-    }
-
-    mark_prices = {
-        "LTC/USDT":
-            DataFrame([
-                [date_midnight, 3.3, 0, 0, 0, 0],
-                [date_eight, 3.2, 0, 0, 0, 0],
-                [date_sixteen, 3.2, 0, 0, 0, 0],
-            ], columns=columns),
-        "ETH/USDT":
-            DataFrame([
-                [date_midnight, 2.4, 0, 0, 0, 0],
-                [date_eight, 2.5, 0, 0, 0, 0],
-                [date_sixteen, 2.5, 0, 0, 0, 0],
-            ], columns=columns),
-        "XRP/USDT":
-            DataFrame([
-                [date_midnight, 1.2, 0, 0, 0, 0],
-                [date_eight, 1.2, 0, 0, 0, 0],
-                [date_sixteen, 1.2, 0, 0, 0, 0],
-            ], columns=columns)
-    }
-
-    def refresh_latest_ohlcv_mock(pairlist, **kwargs):
-        ret = {}
-        for p, tf, ct in pairlist:
-            if ct == CandleType.MARK:
-                ret[(p, tf, ct)] = mark_prices[p]
-            else:
-                ret[(p, tf, ct)] = funding_rates[p]
-
-        return ret
-
-    mocker.patch('freqtrade.exchange.Exchange.refresh_latest_ohlcv',
-                 side_effect=refresh_latest_ohlcv_mock)
-
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        get_rate=enter_rate_mock,
-        fetch_ticker=MagicMock(return_value={
-            'bid': 1.9,
-            'ask': 2.2,
-            'last': 1.9
-        }),
-        create_order=enter_mm,
-        get_min_pair_stake_amount=MagicMock(return_value=1),
-        get_fee=fee,
-        get_maintenance_ratio_and_amt=MagicMock(return_value=(0.01, 0.01)),
-    )
-
-    freqtrade = get_patched_freqtradebot(mocker, default_conf)
-
-    # initial funding fees,
-    freqtrade.execute_entry('ETH/USDT', 123, is_short=is_short)
-    freqtrade.execute_entry('LTC/USDT', 2.0, is_short=is_short)
-    freqtrade.execute_entry('XRP/USDT', 123, is_short=is_short)
-    multipl = 1 if is_short else -1
-    trades = Trade.get_open_trades()
-    assert len(trades) == 3
-    for trade in trades:
-        assert pytest.approx(trade.funding_fees) == 0
-    mocker.patch('freqtrade.exchange.Exchange.create_order', return_value=open_exit_order)
-    time_machine.move_to("2021-09-01 08:00:00 +00:00")
-    if schedule_off:
-        for trade in trades:
-            freqtrade.execute_trade_exit(
-                trade=trade,
-                # The values of the next 2 params are irrelevant for this test
-                limit=ticker_usdt_sell_up()['bid'],
-                exit_check=ExitCheckTuple(exit_type=ExitType.ROI)
-            )
-            assert trade.funding_fees == pytest.approx(sum(
-                trade.amount *
-                mark_prices[trade.pair].iloc[1:2]['open'] *
-                funding_rates[trade.pair].iloc[1:2]['open'] * multipl
-            ))
-
-    else:
-        freqtrade._schedule.run_pending()
-
-    # Funding fees for 00:00 and 08:00
-    for trade in trades:
-        assert trade.funding_fees == pytest.approx(sum(
-            trade.amount *
-            mark_prices[trade.pair].iloc[1:2]['open'] *
-            funding_rates[trade.pair].iloc[1:2]['open'] *
-            multipl
-        ))
-
-
-def test_position_adjust(mocker, default_conf_usdt, fee) -> None:
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    patch_wallet(mocker, free=10000)
-    default_conf_usdt.update({
-        "position_adjustment_enable": True,
-        "dry_run": False,
-        "stake_amount": 10.0,
-        "dry_run_wallet": 1000.0,
-    })
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    freqtrade.strategy.confirm_trade_entry = MagicMock(return_value=True)
-    bid = 11
-    stake_amount = 10
-    buy_rate_mock = MagicMock(return_value=bid)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        get_rate=buy_rate_mock,
-        fetch_ticker=MagicMock(return_value={
-            'bid': 10,
-            'ask': 12,
-            'last': 11
-        }),
-        get_min_pair_stake_amount=MagicMock(return_value=1),
-        get_fee=fee,
-    )
-    pair = 'ETH/USDT'
-
-    # Initial buy
-    closed_successful_buy_order = {
-        'pair': pair,
-        'ft_pair': pair,
-        'ft_order_side': 'buy',
-        'side': 'buy',
-        'type': 'limit',
-        'status': 'closed',
-        'price': bid,
-        'average': bid,
-        'cost': bid * stake_amount,
-        'amount': stake_amount,
-        'filled': stake_amount,
-        'ft_is_open': False,
-        'id': '650',
-        'order_id': '650'
-    }
-    mocker.patch('freqtrade.exchange.Exchange.create_order',
-                 MagicMock(return_value=closed_successful_buy_order))
-    mocker.patch('freqtrade.exchange.Exchange.fetch_order_or_stoploss_order',
-                 MagicMock(return_value=closed_successful_buy_order))
-    assert freqtrade.execute_entry(pair, stake_amount)
-    # Should create an closed trade with an no open order id
-    # Order is filled and trade is open
-    orders = Order.query.all()
-    assert orders
-    assert len(orders) == 1
-    trade = Trade.query.first()
-    assert trade
-    assert trade.is_open is True
-    assert trade.open_order_id is None
-    assert trade.open_rate == 11
-    assert trade.stake_amount == 110
-
-    # Assume it does nothing since order is closed and trade is open
-    freqtrade.update_closed_trades_without_assigned_fees()
-
-    trade = Trade.query.first()
-    assert trade
-    assert trade.is_open is True
-    assert trade.open_order_id is None
-    assert trade.open_rate == 11
-    assert trade.stake_amount == 110
-    assert not trade.fee_updated('buy')
-
-    freqtrade.check_handle_timedout()
-
-    trade = Trade.query.first()
-    assert trade
-    assert trade.is_open is True
-    assert trade.open_order_id is None
-    assert trade.open_rate == 11
-    assert trade.stake_amount == 110
-    assert not trade.fee_updated('buy')
-
-    # First position adjustment buy.
-    open_dca_order_1 = {
-        'ft_pair': pair,
-        'ft_order_side': 'buy',
-        'side': 'buy',
-        'type': 'limit',
-        'status': None,
-        'price': 9,
-        'amount': 12,
-        'cost': 108,
-        'ft_is_open': True,
-        'id': '651',
-        'order_id': '651'
-    }
-    mocker.patch('freqtrade.exchange.Exchange.create_order',
-                 MagicMock(return_value=open_dca_order_1))
-    mocker.patch('freqtrade.exchange.Exchange.fetch_order_or_stoploss_order',
-                 MagicMock(return_value=open_dca_order_1))
-    assert freqtrade.execute_entry(pair, stake_amount, trade=trade)
-
-    orders = Order.query.all()
-    assert orders
-    assert len(orders) == 2
-    trade = Trade.query.first()
-    assert trade
-    assert trade.open_order_id == '651'
-    assert trade.open_rate == 11
-    assert trade.amount == 10
-    assert trade.stake_amount == 110
-    assert not trade.fee_updated('buy')
-    trades: List[Trade] = Trade.get_open_trades_without_assigned_fees()
-    assert len(trades) == 1
-    assert trade.is_open
-    assert not trade.fee_updated('buy')
-    order = trade.select_order('buy', False)
-    assert order
-    assert order.order_id == '650'
-
-    def make_sure_its_651(*args, **kwargs):
-
-        if args[0] == '650':
-            return closed_successful_buy_order
-        if args[0] == '651':
-            return open_dca_order_1
-        return None
-
-    # Assume it does nothing since order is still open
-    fetch_order_mm = MagicMock(side_effect=make_sure_its_651)
-    mocker.patch('freqtrade.exchange.Exchange.create_order', fetch_order_mm)
-    mocker.patch('freqtrade.exchange.Exchange.fetch_order', fetch_order_mm)
-    mocker.patch('freqtrade.exchange.Exchange.fetch_order_or_stoploss_order', fetch_order_mm)
-    freqtrade.update_closed_trades_without_assigned_fees()
-
-    orders = Order.query.all()
-    assert orders
-    assert len(orders) == 2
-    # Assert that the trade is found as open and without fees
-    trades: List[Trade] = Trade.get_open_trades_without_assigned_fees()
-    assert len(trades) == 1
-    # Assert trade is as expected
-    trade = Trade.query.first()
-    assert trade
-    assert trade.open_order_id == '651'
-    assert trade.open_rate == 11
-    assert trade.amount == 10
-    assert trade.stake_amount == 110
-    assert not trade.fee_updated('buy')
-
-    # Make sure the closed order is found as the first order.
-    order = trade.select_order('buy', False)
-    assert order.order_id == '650'
-
-    # Now close the order so it should update.
-    closed_dca_order_1 = {
-        'ft_pair': pair,
-        'ft_order_side': 'buy',
-        'side': 'buy',
-        'type': 'limit',
-        'status': 'closed',
-        'price': 9,
-        'average': 9,
-        'amount': 12,
-        'filled': 12,
-        'cost': 108,
-        'ft_is_open': False,
-        'id': '651',
-        'order_id': '651',
-        'datetime': arrow.utcnow().isoformat(),
-    }
-
-    mocker.patch('freqtrade.exchange.Exchange.create_order',
-                 MagicMock(return_value=closed_dca_order_1))
-    mocker.patch('freqtrade.exchange.Exchange.fetch_order',
-                 MagicMock(return_value=closed_dca_order_1))
-    mocker.patch('freqtrade.exchange.Exchange.fetch_order_or_stoploss_order',
-                 MagicMock(return_value=closed_dca_order_1))
-    freqtrade.check_handle_timedout()
-
-    # Assert trade is as expected (averaged dca)
-    trade = Trade.query.first()
-    assert trade
-    assert trade.open_order_id is None
-    assert pytest.approx(trade.open_rate) == 9.90909090909
-    assert trade.amount == 22
-    assert pytest.approx(trade.stake_amount) == 218
-
-    orders = Order.query.all()
-    assert orders
-    assert len(orders) == 2
-
-    # Make sure the closed order is found as the second order.
-    order = trade.select_order('buy', False)
-    assert order.order_id == '651'
-
-    # Assert that the trade is not found as open and without fees
-    trades: List[Trade] = Trade.get_open_trades_without_assigned_fees()
-    assert len(trades) == 1
-
-    # Add a second DCA
-    closed_dca_order_2 = {
-        'ft_pair': pair,
-        'status': 'closed',
-        'ft_order_side': 'buy',
-        'side': 'buy',
-        'type': 'limit',
-        'price': 7,
-        'average': 7,
-        'amount': 15,
-        'filled': 15,
-        'cost': 105,
-        'ft_is_open': False,
-        'id': '652',
-        'order_id': '652'
-    }
-    mocker.patch('freqtrade.exchange.Exchange.create_order',
-                 MagicMock(return_value=closed_dca_order_2))
-    mocker.patch('freqtrade.exchange.Exchange.fetch_order',
-                 MagicMock(return_value=closed_dca_order_2))
-    mocker.patch('freqtrade.exchange.Exchange.fetch_order_or_stoploss_order',
-                 MagicMock(return_value=closed_dca_order_2))
-    assert freqtrade.execute_entry(pair, stake_amount, trade=trade)
-
-    # Assert trade is as expected (averaged dca)
-    trade = Trade.query.first()
-    assert trade
-    assert trade.open_order_id is None
-    assert pytest.approx(trade.open_rate) == 8.729729729729
-    assert trade.amount == 37
-    assert trade.stake_amount == 323
-
-    orders = Order.query.all()
-    assert orders
-    assert len(orders) == 3
-
-    # Make sure the closed order is found as the second order.
-    order = trade.select_order('buy', False)
-    assert order.order_id == '652'
-    closed_sell_dca_order_1 = {
-        'ft_pair': pair,
-        'status': 'closed',
-        'ft_order_side': 'sell',
-        'side': 'sell',
-        'type': 'limit',
-        'price': 8,
-        'average': 8,
-        'amount': 15,
-        'filled': 15,
-        'cost': 120,
-        'ft_is_open': False,
-        'id': '653',
-        'order_id': '653'
-    }
-    mocker.patch('freqtrade.exchange.Exchange.create_order',
-                 MagicMock(return_value=closed_sell_dca_order_1))
-    mocker.patch('freqtrade.exchange.Exchange.fetch_order',
-                 MagicMock(return_value=closed_sell_dca_order_1))
-    mocker.patch('freqtrade.exchange.Exchange.fetch_order_or_stoploss_order',
-                 MagicMock(return_value=closed_sell_dca_order_1))
-    assert freqtrade.execute_trade_exit(trade=trade, limit=8,
-                                        sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS),
-                                        sub_trade_amt=15)
-
-    # Assert trade is as expected (averaged dca)
-    trade = Trade.query.first()
-    assert trade
-    assert trade.open_order_id is None
-    assert trade.is_open
-    assert trade.amount == 22
-    assert trade.stake_amount == 192.05405405405406
-    assert pytest.approx(trade.open_rate) == 8.729729729729
-
-    orders = Order.query.all()
-    assert orders
-    assert len(orders) == 4
-
-    # Make sure the closed order is found as the second order.
-    order = trade.select_order('sell', False)
-    assert order.order_id == '653'
-
-
-def test_position_adjust2(mocker, default_conf_usdt, fee) -> None:
-    """
-    buy 100 @ 11
-    sell 50 @ 8
-    sell 50 @ 16
-    """
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    patch_wallet(mocker, free=10000)
-    default_conf_usdt.update({
-        "position_adjustment_enable": True,
-        "dry_run": False,
-        "stake_amount": 200.0,
-        "dry_run_wallet": 1000.0,
-    })
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    freqtrade.strategy.confirm_trade_entry = MagicMock(return_value=True)
-    bid = 11
-    amount = 100
-    buy_rate_mock = MagicMock(return_value=bid)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        get_rate=buy_rate_mock,
-        fetch_ticker=MagicMock(return_value={
-            'bid': 10,
-            'ask': 12,
-            'last': 11
-        }),
-        get_min_pair_stake_amount=MagicMock(return_value=1),
-        get_fee=fee,
-    )
-    pair = 'ETH/USDT'
-    # Initial buy
-    closed_successful_buy_order = {
-        'pair': pair,
-        'ft_pair': pair,
-        'ft_order_side': 'buy',
-        'side': 'buy',
-        'type': 'limit',
-        'status': 'closed',
-        'price': bid,
-        'average': bid,
-        'cost': bid * amount,
-        'amount': amount,
-        'filled': amount,
-        'ft_is_open': False,
-        'id': '600',
-        'order_id': '600'
-    }
-    mocker.patch('freqtrade.exchange.Exchange.create_order',
-                 MagicMock(return_value=closed_successful_buy_order))
-    mocker.patch('freqtrade.exchange.Exchange.fetch_order_or_stoploss_order',
-                 MagicMock(return_value=closed_successful_buy_order))
-    assert freqtrade.execute_entry(pair, amount)
-    # Should create an closed trade with an no open order id
-    # Order is filled and trade is open
-    orders = Order.query.all()
-    assert orders
-    assert len(orders) == 1
-    trade = Trade.query.first()
-    assert trade
-    assert trade.is_open is True
-    assert trade.open_order_id is None
-    assert trade.open_rate == bid
-    assert trade.stake_amount == bid * amount
-
-    # Assume it does nothing since order is closed and trade is open
-    freqtrade.update_closed_trades_without_assigned_fees()
-
-    trade = Trade.query.first()
-    assert trade
-    assert trade.is_open is True
-    assert trade.open_order_id is None
-    assert trade.open_rate == bid
-    assert trade.stake_amount == bid * amount
-    assert not trade.fee_updated('buy')
-
-    freqtrade.check_handle_timedout()
-
-    trade = Trade.query.first()
-    assert trade
-    assert trade.is_open is True
-    assert trade.open_order_id is None
-    assert trade.open_rate == bid
-    assert trade.stake_amount == bid * amount
-    assert not trade.fee_updated('buy')
-
-    amount = 50
-    ask = 8
-    closed_sell_dca_order_1 = {
-        'ft_pair': pair,
-        'status': 'closed',
-        'ft_order_side': 'sell',
-        'side': 'sell',
-        'type': 'limit',
-        'price': ask,
-        'average': ask,
-        'amount': amount,
-        'filled': amount,
-        'cost': amount * ask,
-        'ft_is_open': False,
-        'id': '601',
-        'order_id': '601'
-    }
-    mocker.patch('freqtrade.exchange.Exchange.create_order',
-                 MagicMock(return_value=closed_sell_dca_order_1))
-    mocker.patch('freqtrade.exchange.Exchange.fetch_order',
-                 MagicMock(return_value=closed_sell_dca_order_1))
-    mocker.patch('freqtrade.exchange.Exchange.fetch_order_or_stoploss_order',
-                 MagicMock(return_value=closed_sell_dca_order_1))
-    assert freqtrade.execute_trade_exit(trade=trade, limit=ask,
-                                        sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS),
-                                        sub_trade_amt=amount)
-    trades: List[Trade] = trade.get_open_trades_without_assigned_fees()
-    assert len(trades) == 1
-    # Assert trade is as expected (averaged dca)
-
-    trade = Trade.query.first()
-    assert trade
-    assert trade.open_order_id is None
-    assert trade.amount == 50
-    assert trade.open_rate == 11
-    assert trade.stake_amount == 550
-    assert pytest.approx(trade.realized_profit) == -152.375
-    assert pytest.approx(trade.close_profit_abs) == -152.375
-
-    orders = Order.query.all()
-    assert orders
-    assert len(orders) == 2
-    # Make sure the closed order is found as the second order.
-    order = trade.select_order('sell', False)
-    assert order.order_id == '601'
-
-    amount = 50
-    ask = 16
-    closed_sell_dca_order_2 = {
-        'ft_pair': pair,
-        'status': 'closed',
-        'ft_order_side': 'sell',
-        'side': 'sell',
-        'type': 'limit',
-        'price': ask,
-        'average': ask,
-        'amount': amount,
-        'filled': amount,
-        'cost': amount * ask,
-        'ft_is_open': False,
-        'id': '602',
-        'order_id': '602'
-    }
-    mocker.patch('freqtrade.exchange.Exchange.create_order',
-                 MagicMock(return_value=closed_sell_dca_order_2))
-    mocker.patch('freqtrade.exchange.Exchange.fetch_order',
-                 MagicMock(return_value=closed_sell_dca_order_2))
-    mocker.patch('freqtrade.exchange.Exchange.fetch_order_or_stoploss_order',
-                 MagicMock(return_value=closed_sell_dca_order_2))
-    assert freqtrade.execute_trade_exit(trade=trade, limit=ask,
-                                        sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS),
-                                        sub_trade_amt=amount)
-    # Assert trade is as expected (averaged dca)
-
-    trade = Trade.query.first()
-    assert trade
-    assert trade.open_order_id is None
-    assert trade.amount == 50
-    assert trade.open_rate == 11
-    assert trade.stake_amount == 550
-    assert pytest.approx(trade.realized_profit) == -152.375
-    assert pytest.approx(trade.close_profit_abs) == 94.25
-    orders = Order.query.all()
-    assert orders
-    assert len(orders) == 3
-
-    # Make sure the closed order is found as the second order.
-    order = trade.select_order('sell', False)
-    assert order.order_id == '602'
-    assert trade.is_open is False
-
-
-@pytest.mark.parametrize('data', [
-    (
-        # tuple 1 - side amount, price
-        # tuple 2 - amount, open_rate, stake_amount, cumulative_profit, realized_profit
-        (('buy', 100, 10), (100.0, 10.0, 1000.0, 0.0, None)),
-        (('buy', 100, 15), (200.0, 12.5, 2500.0, 0.0, None)),
-        (('sell', 50, 12), (150.0, 12.5, 1875.0, -28.0625, -28.0625)),
-        (('sell', 100, 20), (50.0, 12.5, 625.0, 713.8125, 741.875)),
-        (('sell', 50, 5), (50.0, 12.5, 625.0, 713.8125, 336.625)),
-    ),
-    (
-        (('buy', 100, 3), (100.0, 3.0, 300.0, 0.0, None)),
-        (('buy', 100, 7), (200.0, 5.0, 1000.0, 0.0, None)),
-        (('sell', 100, 11), (100.0, 5.0, 500.0, 596.0, 596.0)),
-        (('buy', 150, 15), (250.0, 11.0, 2750.0, 596.0, 596.0)),
-        (('sell', 100, 19), (150.0, 11.0, 1650.0, 1388.5, 792.5)),
-        (('sell', 150, 23), (150.0, 11.0, 1650.0, 1388.5, 3175.75)),
-    )
-])
-def test_position_adjust3(mocker, default_conf_usdt, fee, data) -> None:
-    default_conf_usdt.update({
-        "position_adjustment_enable": True,
-        "dry_run": False,
-        "stake_amount": 200.0,
-        "dry_run_wallet": 1000.0,
-    })
-    patch_RPCManager(mocker)
-    patch_exchange(mocker)
-    patch_wallet(mocker, free=10000)
-    freqtrade = FreqtradeBot(default_conf_usdt)
-    trade = None
-    freqtrade.strategy.confirm_trade_entry = MagicMock(return_value=True)
-    for idx, (order, result) in enumerate(data):
-        amount = order[1]
-        price = order[2]
-        price_mock = MagicMock(return_value=price)
-        mocker.patch.multiple(
-            'freqtrade.exchange.Exchange',
-            get_rate=price_mock,
-            fetch_ticker=MagicMock(return_value={
-                'bid': 10,
-                'ask': 12,
-                'last': 11
-            }),
-            get_min_pair_stake_amount=MagicMock(return_value=1),
-            get_fee=fee,
-        )
-        pair = 'ETH/USDT'
-        # Initial buy
-        closed_successful_order = {
-            'pair': pair,
-            'ft_pair': pair,
-            'ft_order_side': 'buy',
-            'side': 'buy',
-            'type': 'limit',
-            'status': 'closed',
-            'price': price,
-            'average': price,
-            'cost': price * amount,
-            'amount': amount,
-            'filled': amount,
-            'ft_is_open': False,
-            'id': f'60{idx}',
-            'order_id': f'60{idx}'
-        }
-        mocker.patch('freqtrade.exchange.Exchange.create_order',
-                     MagicMock(return_value=closed_successful_order))
-        mocker.patch('freqtrade.exchange.Exchange.fetch_order_or_stoploss_order',
-                     MagicMock(return_value=closed_successful_order))
-        if order[0] == 'buy':
-            assert freqtrade.execute_entry(pair, amount, trade=trade)
-        else:
-            assert freqtrade.execute_trade_exit(
-                trade=trade, limit=price,
-                sell_reason=SellCheckTuple(sell_type=SellType.STOP_LOSS),
-                sub_trade_amt=amount)
-
-        orders1 = Order.query.all()
-        assert orders1
-        assert len(orders1) == idx + 1
-
-        trade = Trade.query.first()
-        assert trade
-        if idx < len(data) - 1:
-            assert trade.is_open is True
-        assert trade.open_order_id is None
-        assert trade.amount == result[0]
-        assert trade.open_rate == result[1]
-        assert trade.stake_amount == result[2]
-        assert pytest.approx(trade.realized_profit) == result[3]
-        assert pytest.approx(trade.close_profit_abs) == result[4]
-
-        order_obj = trade.select_order(order[0], False)
-        assert order_obj.order_id == f'60{idx}'
-
-    trade = Trade.query.first()
-    assert trade
-    assert trade.open_order_id is None
-    assert trade.is_open is False
-
-
-def test_process_open_trade_positions_exception(mocker, default_conf_usdt, fee, caplog) -> None:
-    default_conf_usdt.update({
-        "position_adjustment_enable": True,
-    })
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-
-    mocker.patch('freqtrade.freqtradebot.FreqtradeBot.check_and_call_adjust_trade_position',
-                 side_effect=DependencyException())
-
-    create_mock_trades(fee)
-
-    freqtrade.process_open_trade_positions()
-    assert log_has_re(r"Unable to adjust position of trade for .*", caplog)
-
-
-def test_check_and_call_adjust_trade_position(mocker, default_conf_usdt, fee, caplog) -> None:
-    default_conf_usdt.update({
-        "position_adjustment_enable": True,
-        "max_entry_position_adjustment": 0,
-    })
-    freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
-    buy_rate_mock = MagicMock(return_value=10)
-    mocker.patch.multiple(
-        'freqtrade.exchange.Exchange',
-        get_rate=buy_rate_mock,
-        fetch_ticker=MagicMock(return_value={
-            'bid': 10,
-            'ask': 12,
-            'last': 11
-        }),
-        get_min_pair_stake_amount=MagicMock(return_value=1),
-        get_fee=fee,
-    )
-    create_mock_trades(fee)
-    caplog.set_level(logging.DEBUG)
-    freqtrade.strategy.adjust_trade_position = MagicMock(return_value=10)
-    freqtrade.process_open_trade_positions()
-    assert log_has_re(r"Max adjustment entries for .* has been reached\.", caplog)
diff --git a/tests/test_persistence.py.orig b/tests/test_persistence.py.orig
deleted file mode 100644
index 35c4e7265..000000000
--- a/tests/test_persistence.py.orig
+++ /dev/null
@@ -1,2644 +0,0 @@
-# pragma pylint: disable=missing-docstring, C0103
-import logging
-from datetime import datetime, timedelta, timezone
-from math import isclose
-from pathlib import Path
-from types import FunctionType
-from unittest.mock import MagicMock
-
-import arrow
-import pytest
-from sqlalchemy import create_engine, text
-
-from freqtrade import constants
-from freqtrade.enums import TradingMode
-from freqtrade.exceptions import DependencyException, OperationalException
-from freqtrade.persistence import LocalTrade, Order, Trade, clean_dry_run_db, init_db
-from freqtrade.persistence.migrations import get_last_sequence_ids, set_sequence_ids
-from tests.conftest import create_mock_trades, create_mock_trades_with_leverage, log_has, log_has_re
-
-
-spot, margin, futures = TradingMode.SPOT, TradingMode.MARGIN, TradingMode.FUTURES
-
-
-def test_init_create_session(default_conf):
-    # Check if init create a session
-    init_db(default_conf['db_url'], default_conf['dry_run'])
-    assert hasattr(Trade, '_session')
-    assert 'scoped_session' in type(Trade._session).__name__
-
-
-def test_init_custom_db_url(default_conf, tmpdir):
-    # Update path to a value other than default, but still in-memory
-    filename = f"{tmpdir}/freqtrade2_test.sqlite"
-    assert not Path(filename).is_file()
-
-    default_conf.update({'db_url': f'sqlite:///{filename}'})
-
-    init_db(default_conf['db_url'], default_conf['dry_run'])
-    assert Path(filename).is_file()
-    r = Trade._session.execute(text("PRAGMA journal_mode"))
-    assert r.first() == ('wal',)
-
-
-def test_init_invalid_db_url():
-    # Update path to a value other than default, but still in-memory
-    with pytest.raises(OperationalException, match=r'.*no valid database URL*'):
-        init_db('unknown:///some.url', True)
-
-    with pytest.raises(OperationalException, match=r'Bad db-url.*For in-memory database, pl.*'):
-        init_db('sqlite:///', True)
-
-
-def test_init_prod_db(default_conf, mocker):
-    default_conf.update({'dry_run': False})
-    default_conf.update({'db_url': constants.DEFAULT_DB_PROD_URL})
-
-    create_engine_mock = mocker.patch('freqtrade.persistence.models.create_engine', MagicMock())
-
-    init_db(default_conf['db_url'], default_conf['dry_run'])
-    assert create_engine_mock.call_count == 1
-    assert create_engine_mock.mock_calls[0][1][0] == 'sqlite:///tradesv3.sqlite'
-
-
-def test_init_dryrun_db(default_conf, tmpdir):
-    filename = f"{tmpdir}/freqtrade2_prod.sqlite"
-    assert not Path(filename).is_file()
-    default_conf.update({
-        'dry_run': True,
-        'db_url': f'sqlite:///{filename}'
-    })
-
-    init_db(default_conf['db_url'], default_conf['dry_run'])
-    assert Path(filename).is_file()
-
-
-@pytest.mark.parametrize('is_short', [False, True])
-@pytest.mark.usefixtures("init_persistence")
-def test_enter_exit_side(fee, is_short):
-    enter_side, exit_side = ("sell", "buy") if is_short else ("buy", "sell")
-    trade = Trade(
-        id=2,
-        pair='ADA/USDT',
-        stake_amount=0.001,
-        open_rate=0.01,
-        amount=5,
-        is_open=True,
-        open_date=arrow.utcnow().datetime,
-        fee_open=fee.return_value,
-        fee_close=fee.return_value,
-        exchange='binance',
-        is_short=is_short,
-        leverage=2.0,
-        trading_mode=margin
-    )
-    assert trade.enter_side == enter_side
-    assert trade.exit_side == exit_side
-    assert trade.trade_direction == 'short' if is_short else 'long'
-
-
-@pytest.mark.usefixtures("init_persistence")
-def test_set_stop_loss_isolated_liq(fee):
-    trade = Trade(
-        id=2,
-        pair='ADA/USDT',
-        stake_amount=60.0,
-        open_rate=2.0,
-        amount=30.0,
-        is_open=True,
-        open_date=arrow.utcnow().datetime,
-        fee_open=fee.return_value,
-        fee_close=fee.return_value,
-        exchange='binance',
-        is_short=False,
-        leverage=2.0,
-        trading_mode=margin
-    )
-    trade.set_isolated_liq(0.09)
-    assert trade.liquidation_price == 0.09
-    assert trade.stop_loss is None
-    assert trade.initial_stop_loss is None
-
-    trade._set_stop_loss(0.1, (1.0/9.0))
-    assert trade.liquidation_price == 0.09
-    assert trade.stop_loss == 0.1
-    assert trade.initial_stop_loss == 0.1
-
-    trade.set_isolated_liq(0.08)
-    assert trade.liquidation_price == 0.08
-    assert trade.stop_loss == 0.1
-    assert trade.initial_stop_loss == 0.1
-
-    trade.set_isolated_liq(0.11)
-    trade._set_stop_loss(0.1, 0)
-    assert trade.liquidation_price == 0.11
-    assert trade.stop_loss == 0.11
-    assert trade.initial_stop_loss == 0.1
-
-    # lower stop doesn't move stoploss
-    trade._set_stop_loss(0.1, 0)
-    assert trade.liquidation_price == 0.11
-    assert trade.stop_loss == 0.11
-    assert trade.initial_stop_loss == 0.1
-
-    trade.stop_loss = None
-    trade.liquidation_price = None
-    trade.initial_stop_loss = None
-
-    trade._set_stop_loss(0.07, 0)
-    assert trade.liquidation_price is None
-    assert trade.stop_loss == 0.07
-    assert trade.initial_stop_loss == 0.07
-
-    trade.is_short = True
-    trade.recalc_open_trade_value()
-    trade.stop_loss = None
-    trade.initial_stop_loss = None
-
-    trade.set_isolated_liq(0.09)
-    assert trade.liquidation_price == 0.09
-    assert trade.stop_loss is None
-    assert trade.initial_stop_loss is None
-
-    trade._set_stop_loss(0.08, (1.0/9.0))
-    assert trade.liquidation_price == 0.09
-    assert trade.stop_loss == 0.08
-    assert trade.initial_stop_loss == 0.08
-
-    trade.set_isolated_liq(0.1)
-    assert trade.liquidation_price == 0.1
-    assert trade.stop_loss == 0.08
-    assert trade.initial_stop_loss == 0.08
-
-    trade.set_isolated_liq(0.07)
-    trade._set_stop_loss(0.1, (1.0/8.0))
-    assert trade.liquidation_price == 0.07
-    assert trade.stop_loss == 0.07
-    assert trade.initial_stop_loss == 0.08
-
-    # Stop doesn't move stop higher
-    trade._set_stop_loss(0.1, (1.0/9.0))
-    assert trade.liquidation_price == 0.07
-    assert trade.stop_loss == 0.07
-    assert trade.initial_stop_loss == 0.08
-
-
-@pytest.mark.parametrize('exchange,is_short,lev,minutes,rate,interest,trading_mode', [
-    ("binance", False, 3, 10, 0.0005, round(0.0008333333333333334, 8), margin),
-    ("binance", True, 3, 10, 0.0005, 0.000625, margin),
-    ("binance", False, 3, 295, 0.0005, round(0.004166666666666667, 8), margin),
-    ("binance", True, 3, 295, 0.0005, round(0.0031249999999999997, 8), margin),
-    ("binance", False, 3, 295, 0.00025, round(0.0020833333333333333, 8), margin),
-    ("binance", True, 3, 295, 0.00025, round(0.0015624999999999999, 8), margin),
-    ("binance", False, 5, 295, 0.0005, 0.005, margin),
-    ("binance", True, 5, 295, 0.0005, round(0.0031249999999999997, 8), margin),
-    ("binance", False, 1, 295, 0.0005, 0.0, spot),
-    ("binance", True, 1, 295, 0.0005, 0.003125, margin),
-
-    ("binance", False, 3, 10, 0.0005, 0.0, futures),
-    ("binance", True, 3, 295, 0.0005, 0.0, futures),
-    ("binance", False, 5, 295, 0.0005, 0.0, futures),
-    ("binance", True, 5, 295, 0.0005, 0.0, futures),
-    ("binance", False, 1, 295, 0.0005, 0.0, futures),
-    ("binance", True, 1, 295, 0.0005, 0.0, futures),
-
-    ("kraken", False, 3, 10, 0.0005, 0.040, margin),
-    ("kraken", True, 3, 10, 0.0005, 0.030, margin),
-    ("kraken", False, 3, 295, 0.0005, 0.06, margin),
-    ("kraken", True, 3, 295, 0.0005, 0.045, margin),
-    ("kraken", False, 3, 295, 0.00025, 0.03, margin),
-    ("kraken", True, 3, 295, 0.00025, 0.0225, margin),
-    ("kraken", False, 5, 295, 0.0005, round(0.07200000000000001, 8), margin),
-    ("kraken", True, 5, 295, 0.0005, 0.045, margin),
-    ("kraken", False, 1, 295, 0.0005, 0.0, spot),
-    ("kraken", True, 1, 295, 0.0005, 0.045, margin),
-
-])
-@pytest.mark.usefixtures("init_persistence")
-def test_interest(fee, exchange, is_short, lev, minutes, rate, interest,
-                  trading_mode):
-    """
-        10min, 5hr limit trade on Binance/Kraken at 3x,5x leverage
-        fee: 0.25 % quote
-        interest_rate: 0.05 % per 4 hrs
-        open_rate: 2.00 quote
-        close_rate: 2.20 quote
-        amount: = 30.0 crypto
-        stake_amount
-            3x, -3x: 20.0  quote
-            5x, -5x: 12.0  quote
-        borrowed
-          10min
-             3x: 40 quote
-            -3x: 30 crypto
-             5x: 48 quote
-            -5x: 30 crypto
-             1x: 0
-            -1x: 30 crypto
-        hours: 1/6 (10 minutes)
-        time-periods:
-            10min
-                kraken: (1 + 1) 4hr_periods = 2 4hr_periods
-                binance: 1/24 24hr_periods
-            4.95hr
-                kraken: ceil(1 + 4.95/4) 4hr_periods = 3 4hr_periods
-                binance: ceil(4.95)/24 24hr_periods = 5/24 24hr_periods
-        interest: borrowed * interest_rate * time-periods
-          10min
-            binance     3x: 40 * 0.0005 * 1/24 = 0.0008333333333333334 quote
-            kraken      3x: 40 * 0.0005 * 2    = 0.040 quote
-            binace     -3x: 30 * 0.0005 * 1/24 = 0.000625 crypto
-            kraken     -3x: 30 * 0.0005 * 2    = 0.030 crypto
-          5hr
-            binance     3x: 40 * 0.0005 * 5/24 = 0.004166666666666667 quote
-            kraken      3x: 40 * 0.0005 * 3    = 0.06 quote
-            binace     -3x: 30 * 0.0005 * 5/24 = 0.0031249999999999997 crypto
-            kraken     -3x: 30 * 0.0005 * 3    = 0.045 crypto
-          0.00025 interest
-            binance     3x: 40 * 0.00025 * 5/24 = 0.0020833333333333333 quote
-            kraken      3x: 40 * 0.00025 * 3    = 0.03 quote
-            binace     -3x: 30 * 0.00025 * 5/24 = 0.0015624999999999999 crypto
-            kraken     -3x: 30 * 0.00025 * 3    = 0.0225 crypto
-          5x leverage, 0.0005 interest, 5hr
-            binance     5x: 48 * 0.0005 * 5/24 = 0.005 quote
-            kraken      5x: 48 * 0.0005 * 3    = 0.07200000000000001 quote
-            binace     -5x: 30 * 0.0005 * 5/24 = 0.0031249999999999997 crypto
-            kraken     -5x: 30 * 0.0005 * 3    = 0.045 crypto
-          1x leverage, 0.0005 interest, 5hr
-            binance,kraken 1x: 0.0 quote
-            binace        -1x: 30 * 0.0005 * 5/24 = 0.003125 crypto
-            kraken        -1x: 30 * 0.0005 * 3    = 0.045 crypto
-    """
-
-    trade = Trade(
-        pair='ADA/USDT',
-        stake_amount=20.0,
-        amount=30.0,
-        open_rate=2.0,
-        open_date=datetime.utcnow() - timedelta(minutes=minutes),
-        fee_open=fee.return_value,
-        fee_close=fee.return_value,
-        exchange=exchange,
-        leverage=lev,
-        interest_rate=rate,
-        is_short=is_short,
-        trading_mode=trading_mode
-    )
-
-    assert round(float(trade.calculate_interest()), 8) == interest
-
-
-@pytest.mark.parametrize('is_short,lev,borrowed,trading_mode', [
-    (False, 1.0, 0.0, spot),
-    (True, 1.0, 30.0, margin),
-    (False, 3.0, 40.0, margin),
-    (True, 3.0, 30.0, margin),
-])
-@pytest.mark.usefixtures("init_persistence")
-def test_borrowed(limit_buy_order_usdt, limit_sell_order_usdt, fee,
-                  caplog, is_short, lev, borrowed, trading_mode):
-    """
-        10 minute limit trade on Binance/Kraken at 1x, 3x leverage
-        fee: 0.25% quote
-        interest_rate: 0.05% per 4 hrs
-        open_rate: 2.00 quote
-        close_rate: 2.20 quote
-        amount: = 30.0 crypto
-        stake_amount
-            1x,-1x: 60.0  quote
-            3x,-3x: 20.0  quote
-        borrowed
-             1x:  0 quote
-             3x: 40 quote
-            -1x: 30 crypto
-            -3x: 30 crypto
-        hours: 1/6 (10 minutes)
-        time-periods:
-            kraken: (1 + 1) 4hr_periods = 2 4hr_periods
-            binance: 1/24 24hr_periods
-        interest: borrowed * interest_rate * time-periods
-            1x            :  /
-            binance     3x: 40 * 0.0005 * 1/24 = 0.0008333333333333334 quote
-            kraken      3x: 40 * 0.0005 * 2 = 0.040 quote
-            binace -1x,-3x: 30 * 0.0005 * 1/24 = 0.000625 crypto
-            kraken -1x,-3x: 30 * 0.0005 * 2 = 0.030 crypto
-        open_value: (amount * open_rate) ± (amount * open_rate * fee)
-             1x, 3x: 30 * 2 + 30 * 2 * 0.0025 = 60.15 quote
-            -1x,-3x: 30 * 2 - 30 * 2 * 0.0025 = 59.850 quote
-        amount_closed:
-            1x, 3x         : amount
-            -1x, -3x       : amount + interest
-            binance -1x,-3x: 30 + 0.000625 = 30.000625 crypto
-            kraken  -1x,-3x: 30 + 0.03 = 30.03 crypto
-        close_value:
-             1x, 3x: (amount_closed * close_rate) - (amount_closed * close_rate * fee) - interest
-            -1x,-3x: (amount_closed * close_rate) + (amount_closed * close_rate * fee)
-            binance,kraken 1x: (30.00 * 2.20) - (30.00 * 2.20 * 0.0025)         = 65.835
-            binance        3x: (30.00 * 2.20) - (30.00 * 2.20 * 0.0025) - 0.00083333 = 65.83416667
-            kraken         3x: (30.00 * 2.20) - (30.00 * 2.20 * 0.0025) - 0.040 = 65.795
-            binance   -1x,-3x: (30.000625 * 2.20) + (30.000625 * 2.20 * 0.0025) = 66.16637843750001
-            kraken    -1x,-3x: (30.03 * 2.20) + (30.03 * 2.20 * 0.0025)         = 66.231165
-        total_profit:
-            1x, 3x : close_value - open_value
-            -1x,-3x: open_value  - close_value
-            binance,kraken 1x: 65.835 - 60.15             = 5.685
-            binance        3x: 65.83416667 - 60.15        = 5.684166670000003
-            kraken         3x: 65.795 - 60.15             = 5.645
-            binance   -1x,-3x: 59.850 - 66.16637843750001 = -6.316378437500013
-            kraken    -1x,-3x: 59.850 - 66.231165          = -6.381165
-        total_profit_ratio:
-            1x, 3x : ((close_value/open_value) - 1) * leverage
-            -1x,-3x: (1 - (close_value/open_value)) * leverage
-            binance  1x: ((65.835 / 60.15) - 1)  * 1 = 0.0945137157107232
-            binance  3x: ((65.83416667 / 60.15) - 1)  * 3 = 0.2834995845386534
-            kraken   1x: ((65.835 / 60.15) - 1)  * 1 = 0.0945137157107232
-            kraken   3x: ((65.795 / 60.15) - 1)  * 3 = 0.2815461346633419
-            binance -1x: (1-(66.1663784375 / 59.85)) * 1 = -0.1055368159983292
-            binance -3x: (1-(66.1663784375 / 59.85)) * 3 = -0.3166104479949876
-            kraken  -1x: (1-(66.2311650 / 59.85)) * 1    = -0.106619298245614
-            kraken  -3x: (1-(66.2311650 / 59.85)) * 3    = -0.319857894736842
-    """
-
-    trade = Trade(
-        id=2,
-        pair='ADA/USDT',
-        stake_amount=60.0,
-        open_rate=2.0,
-        amount=30.0,
-        is_open=True,
-        open_date=arrow.utcnow().datetime,
-        fee_open=fee.return_value,
-        fee_close=fee.return_value,
-        exchange='binance',
-        is_short=is_short,
-        leverage=lev,
-        trading_mode=trading_mode
-    )
-    assert trade.borrowed == borrowed
-
-
-@pytest.mark.parametrize('is_short,open_rate,close_rate,lev,profit,trading_mode', [
-    (False, 2.0, 2.2, 1.0, 0.09451372, spot),
-    (True, 2.2, 2.0, 3.0, 0.25894253, margin),
-])
-@pytest.mark.usefixtures("init_persistence")
-def test_update_limit_order(fee, caplog, limit_buy_order_usdt, limit_sell_order_usdt, time_machine,
-                            is_short, open_rate, close_rate, lev, profit, trading_mode):
-    """
-        10 minute limit trade on Binance/Kraken at 1x, 3x leverage
-        fee: 0.25% quote
-        interest_rate: 0.05% per 4 hrs
-        open_rate: 2.00 quote
-        close_rate: 2.20 quote
-        amount: = 30.0 crypto
-        stake_amount
-            1x,-1x: 60.0  quote
-            3x,-3x: 20.0  quote
-        borrowed
-             1x:  0 quote
-             3x: 40 quote
-            -1x: 30 crypto
-            -3x: 30 crypto
-        hours: 1/6 (10 minutes)
-        time-periods:
-            kraken: (1 + 1) 4hr_periods = 2 4hr_periods
-            binance: 1/24 24hr_periods
-        interest: borrowed * interest_rate * time-periods
-            1x            :  /
-            binance     3x: 40 * 0.0005 * 1/24 = 0.0008333333333333334 quote
-            kraken      3x: 40 * 0.0005 * 2 = 0.040 quote
-            binace -1x,-3x: 30 * 0.0005 * 1/24 = 0.000625 crypto
-            kraken -1x,-3x: 30 * 0.0005 * 2 = 0.030 crypto
-        open_value: (amount * open_rate) ± (amount * open_rate * fee)
-             1x, 3x: 30 * 2 + 30 * 2 * 0.0025 = 60.15 quote
-            -1x,-3x: 30 * 2 - 30 * 2 * 0.0025 = 59.850 quote
-        amount_closed:
-            1x, 3x         : amount
-            -1x, -3x       : amount + interest
-            binance -1x,-3x: 30 + 0.000625 = 30.000625 crypto
-            kraken  -1x,-3x: 30 + 0.03 = 30.03 crypto
-        close_value:
-             1x, 3x: (amount_closed * close_rate) - (amount_closed * close_rate * fee) - interest
-            -1x,-3x: (amount_closed * close_rate) + (amount_closed * close_rate * fee)
-            binance,kraken 1x: (30.00 * 2.20) - (30.00 * 2.20 * 0.0025)         = 65.835
-            binance        3x: (30.00 * 2.20) - (30.00 * 2.20 * 0.0025) - 0.00083333 = 65.83416667
-            kraken         3x: (30.00 * 2.20) - (30.00 * 2.20 * 0.0025) - 0.040 = 65.795
-            binance   -1x,-3x: (30.000625 * 2.20) + (30.000625 * 2.20 * 0.0025) = 66.16637843750001
-            kraken    -1x,-3x: (30.03 * 2.20) + (30.03 * 2.20 * 0.0025)         = 66.231165
-        total_profit:
-            1x, 3x : close_value - open_value
-            -1x,-3x: open_value  - close_value
-            binance,kraken 1x: 65.835 - 60.15             = 5.685
-            binance        3x: 65.83416667 - 60.15        = 5.684166670000003
-            kraken         3x: 65.795 - 60.15             = 5.645
-            binance   -1x,-3x: 59.850 - 66.16637843750001 = -6.316378437500013
-            kraken    -1x,-3x: 59.850 - 66.231165          = -6.381165
-        total_profit_ratio:
-            1x, 3x : ((close_value/open_value) - 1) * leverage
-            -1x,-3x: (1 - (close_value/open_value)) * leverage
-            binance  1x: ((65.835 / 60.15) - 1)  * 1 = 0.0945137157107232
-            binance  3x: ((65.83416667 / 60.15) - 1)  * 3 = 0.2834995845386534
-            kraken   1x: ((65.835 / 60.15) - 1)  * 1 = 0.0945137157107232
-            kraken   3x: ((65.795 / 60.15) - 1)  * 3 = 0.2815461346633419
-            binance -1x: (1-(66.1663784375 / 59.85)) * 1 = -0.1055368159983292
-            binance -3x: (1-(66.1663784375 / 59.85)) * 3 = -0.3166104479949876
-            kraken  -1x: (1-(66.2311650 / 59.85)) * 1    = -0.106619298245614
-            kraken  -3x: (1-(66.2311650 / 59.85)) * 3    = -0.319857894736842
-        open_rate: 2.2, close_rate: 2.0, -3x, binance, short
-            open_value: 30 * 2.2 - 30 * 2.2 * 0.0025 = 65.835 quote
-            amount_closed: 30 + 0.000625 = 30.000625 crypto
-            close_value: (30.000625 * 2.0) + (30.000625 * 2.0 * 0.0025) = 60.151253125
-            total_profit: 65.835 - 60.151253125 = 5.683746874999997
-            total_profit_ratio: (1-(60.151253125/65.835)) * 3 = 0.2589996297562085
-
-    """
-    time_machine.move_to("2022-03-31 20:45:00 +00:00")
-
-    enter_order = limit_sell_order_usdt if is_short else limit_buy_order_usdt
-    exit_order = limit_buy_order_usdt if is_short else limit_sell_order_usdt
-    enter_side, exit_side = ("sell", "buy") if is_short else ("buy", "sell")
-
-    trade = Trade(
-        id=2,
-        pair='ADA/USDT',
-        stake_amount=60.0,
-        open_rate=open_rate,
-        amount=30.0,
-        is_open=True,
-        open_date=arrow.utcnow().datetime,
-        fee_open=fee.return_value,
-        fee_close=fee.return_value,
-        exchange='binance',
-        is_short=is_short,
-        interest_rate=0.0005,
-        leverage=lev,
-        trading_mode=trading_mode
-    )
-    assert trade.open_order_id is None
-    assert trade.close_profit is None
-    assert trade.close_date is None
-
-<<<<<<< HEAD
-    trade.open_order_id = 'mocked_limit_buy_usdt'
-    oobj = Order.parse_from_ccxt_object(limit_buy_order_usdt, 'ADA/USDT', 'buy')
-=======
-    trade.open_order_id = 'something'
-    oobj = Order.parse_from_ccxt_object(enter_order, 'ADA/USDT', enter_side)
->>>>>>> develop
-    trade.update_trade(oobj)
-    assert trade.open_order_id is None
-    assert trade.open_rate == open_rate
-    assert trade.close_profit is None
-    assert trade.close_date is None
-    assert log_has_re(f"LIMIT_{enter_side.upper()} has been fulfilled for "
-                      r"Trade\(id=2, pair=ADA/USDT, amount=30.00000000, "
-                      f"is_short={is_short}, leverage={lev}, open_rate={open_rate}0000000, "
-                      r"open_since=.*\).",
-                      caplog)
-
-    caplog.clear()
-<<<<<<< HEAD
-    trade.open_order_id = 'mocked_limit_sell_usdt'
-    oobj = Order.parse_from_ccxt_object(limit_sell_order_usdt, 'ADA/USDT', 'sell')
-=======
-    trade.open_order_id = 'something'
-    time_machine.move_to("2022-03-31 21:45:05 +00:00")
-    oobj = Order.parse_from_ccxt_object(exit_order, 'ADA/USDT', exit_side)
->>>>>>> develop
-    trade.update_trade(oobj)
-
-    assert trade.open_order_id is None
-    assert trade.close_rate == close_rate
-    assert trade.close_profit == profit
-    assert trade.close_date is not None
-    assert log_has_re(f"LIMIT_{exit_side.upper()} has been fulfilled for "
-                      r"Trade\(id=2, pair=ADA/USDT, amount=30.00000000, "
-                      f"is_short={is_short}, leverage={lev}, open_rate={open_rate}0000000, "
-                      r"open_since=.*\).",
-                      caplog)
-    caplog.clear()
-
-
-@pytest.mark.usefixtures("init_persistence")
-def test_update_market_order(market_buy_order_usdt, market_sell_order_usdt, fee, caplog):
-    trade = Trade(
-        id=1,
-        pair='ADA/USDT',
-        stake_amount=60.0,
-        open_rate=2.0,
-        amount=30.0,
-        is_open=True,
-        fee_open=fee.return_value,
-        fee_close=fee.return_value,
-        open_date=arrow.utcnow().datetime,
-        exchange='binance',
-        trading_mode=margin,
-        leverage=1.0,
-    )
-
-    trade.open_order_id = 'mocked_market_buy'
-    oobj = Order.parse_from_ccxt_object(market_buy_order_usdt, 'ADA/USDT', 'buy')
-    trade.update_trade(oobj)
-    assert trade.open_order_id is None
-    assert trade.open_rate == 2.0
-    assert trade.close_profit is None
-    assert trade.close_date is None
-    assert log_has_re(r"MARKET_BUY has been fulfilled for Trade\(id=1, "
-                      r"pair=ADA/USDT, amount=30.00000000, is_short=False, leverage=1.0, "
-                      r"open_rate=2.00000000, open_since=.*\).",
-                      caplog)
-
-    caplog.clear()
-    trade.is_open = True
-    trade.open_order_id = 'mocked_market_sell'
-    oobj = Order.parse_from_ccxt_object(market_sell_order_usdt, 'ADA/USDT', 'sell')
-    trade.update_trade(oobj)
-    assert trade.open_order_id is None
-    assert trade.close_rate == 2.2
-    assert trade.close_profit == round(0.0945137157107232, 8)
-    assert trade.close_date is not None
-    assert log_has_re(r"MARKET_SELL has been fulfilled for Trade\(id=1, "
-                      r"pair=ADA/USDT, amount=30.00000000, is_short=False, leverage=1.0, "
-                      r"open_rate=2.00000000, open_since=.*\).",
-                      caplog)
-
-
-@pytest.mark.parametrize(
-    'exchange,is_short,lev,open_value,close_value,profit,profit_ratio,trading_mode,funding_fees', [
-        ("binance", False, 1, 60.15, 65.835, 5.685, 0.09451371, spot, 0.0),
-        ("binance", True, 1, 59.850, 66.1663784375, -6.3163784375, -0.1055368, margin, 0.0),
-        ("binance", False, 3, 60.15, 65.83416667, 5.68416667, 0.28349958, margin, 0.0),
-        ("binance", True, 3, 59.85, 66.1663784375, -6.3163784375, -0.31661044, margin, 0.0),
-
-        ("kraken", False, 1, 60.15, 65.835, 5.685, 0.09451371, spot, 0.0),
-        ("kraken", True, 1, 59.850, 66.231165, -6.381165, -0.1066192, margin, 0.0),
-        ("kraken", False, 3, 60.15, 65.795, 5.645, 0.28154613, margin, 0.0),
-        ("kraken", True, 3, 59.850, 66.231165, -6.381165, -0.3198578, margin, 0.0),
-
-        ("binance", False, 1, 60.15, 65.835,  5.685, 0.09451371, futures, 0.0),
-        ("binance", False, 1, 60.15, 66.835,  6.685, 0.11113881, futures, 1.0),
-        ("binance", True, 1, 59.85,  66.165, -6.315, -0.10551378, futures, 0.0),
-        ("binance", True, 1, 59.85,  67.165, -7.315, -0.12222222, futures, -1.0),
-        ("binance", False, 3, 60.15, 64.835,  4.685, 0.23366583, futures, -1.0),
-        ("binance", True, 3, 59.85,  65.165, -5.315, -0.26641604, futures, 1.0),
-    ])
-@pytest.mark.usefixtures("init_persistence")
-def test_calc_open_close_trade_price(
-    limit_buy_order_usdt, limit_sell_order_usdt, fee, exchange, is_short, lev,
-    open_value, close_value, profit, profit_ratio, trading_mode, funding_fees
-):
-    trade: Trade = Trade(
-        pair='ADA/USDT',
-        stake_amount=60.0,
-        open_rate=2.0,
-        amount=30.0,
-        open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=10),
-        interest_rate=0.0005,
-        fee_open=fee.return_value,
-        fee_close=fee.return_value,
-        exchange=exchange,
-        is_short=is_short,
-        leverage=lev,
-        trading_mode=trading_mode,
-        funding_fees=funding_fees
-    )
-
-    trade.open_order_id = f'something-{is_short}-{lev}-{exchange}'
-
-    oobj = Order.parse_from_ccxt_object(limit_buy_order_usdt, 'ADA/USDT', 'buy')
-    trade.update_trade(oobj)
-
-    oobj = Order.parse_from_ccxt_object(limit_sell_order_usdt, 'ADA/USDT', 'sell')
-    trade.update_trade(oobj)
-
-    trade.open_rate = 2.0
-    trade.close_rate = 2.2
-    trade.recalc_open_trade_value()
-    assert isclose(trade._calc_open_trade_value(), open_value)
-    assert isclose(trade.calc_close_trade_value(), close_value)
-    assert isclose(trade.calc_profit(), round(profit, 8))
-    assert pytest.approx(trade.calc_profit_ratio()) == profit_ratio
-
-
-@pytest.mark.usefixtures("init_persistence")
-def test_trade_close(limit_buy_order_usdt, limit_sell_order_usdt, fee):
-    trade = Trade(
-        pair='ADA/USDT',
-        stake_amount=60.0,
-        open_rate=2.0,
-        amount=30.0,
-        is_open=True,
-        fee_open=fee.return_value,
-        fee_close=fee.return_value,
-        open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=10),
-        interest_rate=0.0005,
-        exchange='binance',
-        trading_mode=margin
-    )
-    assert trade.close_profit is None
-    assert trade.close_date is None
-    assert trade.is_open is True
-    trade.close(2.2)
-    assert trade.is_open is False
-    assert trade.close_profit == round(0.0945137157107232, 8)
-    assert trade.close_date is not None
-
-    new_date = arrow.Arrow(2020, 2, 2, 15, 6, 1).datetime,
-    assert trade.close_date != new_date
-    # Close should NOT update close_date if the trade has been closed already
-    assert trade.is_open is False
-    trade.close_date = new_date
-    trade.close(2.2)
-    assert trade.close_date == new_date
-
-
-@pytest.mark.usefixtures("init_persistence")
-def test_calc_close_trade_price_exception(limit_buy_order_usdt, fee):
-    trade = Trade(
-        pair='ADA/USDT',
-        stake_amount=60.0,
-        open_rate=2.0,
-        amount=30.0,
-        fee_open=fee.return_value,
-        fee_close=fee.return_value,
-        exchange='binance',
-        trading_mode=margin,
-        leverage=1.0,
-    )
-
-    trade.open_order_id = 'something'
-    oobj = Order.parse_from_ccxt_object(limit_buy_order_usdt, 'ADA/USDT', 'buy')
-    trade.update_trade(oobj)
-    assert trade.calc_close_trade_value() == 0.0
-
-
-@pytest.mark.usefixtures("init_persistence")
-def test_update_open_order(limit_buy_order_usdt):
-    trade = Trade(
-        pair='ADA/USDT',
-        stake_amount=60.0,
-        open_rate=2.0,
-        amount=30.0,
-        fee_open=0.1,
-        fee_close=0.1,
-        exchange='binance',
-        trading_mode=margin
-    )
-
-    assert trade.open_order_id is None
-    assert trade.close_profit is None
-    assert trade.close_date is None
-
-    limit_buy_order_usdt['status'] = 'open'
-    oobj = Order.parse_from_ccxt_object(limit_buy_order_usdt, 'ADA/USDT', 'buy')
-    trade.update_trade(oobj)
-
-    assert trade.open_order_id is None
-    assert trade.close_profit is None
-    assert trade.close_date is None
-
-
-@pytest.mark.usefixtures("init_persistence")
-def test_update_invalid_order(limit_buy_order_usdt):
-    trade = Trade(
-        pair='ADA/USDT',
-        stake_amount=60.0,
-        amount=30.0,
-        open_rate=2.0,
-        fee_open=0.1,
-        fee_close=0.1,
-        exchange='binance',
-        trading_mode=margin
-    )
-    limit_buy_order_usdt['type'] = 'invalid'
-    oobj = Order.parse_from_ccxt_object(limit_buy_order_usdt, 'ADA/USDT', 'meep')
-    with pytest.raises(ValueError, match=r'Unknown order type'):
-        trade.update_trade(oobj)
-
-
-@pytest.mark.parametrize('exchange', ['binance', 'kraken'])
-@pytest.mark.parametrize('trading_mode', [spot, margin, futures])
-@pytest.mark.parametrize('lev', [1, 3])
-@pytest.mark.parametrize('is_short,fee_rate,result', [
-    (False, 0.003, 60.18),
-    (False, 0.0025, 60.15),
-    (False, 0.003, 60.18),
-    (False, 0.0025, 60.15),
-    (True, 0.003, 59.82),
-    (True, 0.0025, 59.85),
-    (True, 0.003, 59.82),
-    (True, 0.0025, 59.85)
-])
-@pytest.mark.usefixtures("init_persistence")
-def test_calc_open_trade_value(
-    limit_buy_order_usdt,
-    exchange,
-    lev,
-    is_short,
-    fee_rate,
-    result,
-    trading_mode
-):
-    # 10 minute limit trade on Binance/Kraken at 1x, 3x leverage
-    # fee: 0.25 %, 0.3% quote
-    # open_rate: 2.00 quote
-    # amount: = 30.0 crypto
-    # stake_amount
-    #     1x, -1x: 60.0  quote
-    #     3x, -3x: 20.0  quote
-    # open_value: (amount * open_rate) ± (amount * open_rate * fee)
-    # 0.25% fee
-    #      1x, 3x: 30 * 2 + 30 * 2 * 0.0025 = 60.15 quote
-    #     -1x,-3x: 30 * 2 - 30 * 2 * 0.0025 = 59.85 quote
-    # 0.3% fee
-    #      1x, 3x: 30 * 2 + 30 * 2 * 0.003  = 60.18 quote
-    #     -1x,-3x: 30 * 2 - 30 * 2 * 0.003  = 59.82 quote
-    trade = Trade(
-        pair='ADA/USDT',
-        stake_amount=60.0,
-        amount=30.0,
-        open_rate=2.0,
-        open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=10),
-        fee_open=fee_rate,
-        fee_close=fee_rate,
-        exchange=exchange,
-        leverage=lev,
-        is_short=is_short,
-        trading_mode=trading_mode
-    )
-    trade.open_order_id = 'open_trade'
-    oobj = Order.parse_from_ccxt_object(
-        limit_buy_order_usdt, 'ADA/USDT', 'sell' if is_short else 'buy')
-    trade.update_trade(oobj)  # Buy @ 2.0
-
-    # Get the open rate price with the standard fee rate
-    assert trade._calc_open_trade_value() == result
-
-
-@pytest.mark.parametrize(
-    'exchange,is_short,lev,open_rate,close_rate,fee_rate,result,trading_mode,funding_fees', [
-        ('binance', False, 1, 2.0, 2.5, 0.0025, 74.8125, spot, 0),
-        ('binance', False, 1, 2.0, 2.5, 0.003, 74.775, spot, 0),
-        ('binance', False, 1, 2.0, 2.2, 0.005, 65.67, margin, 0),
-        ('binance', False, 3, 2.0, 2.5, 0.0025, 74.81166667, margin, 0),
-        ('binance', False, 3, 2.0, 2.5, 0.003, 74.77416667, margin, 0),
-        ('binance', True, 3, 2.2, 2.5, 0.0025, 75.18906641, margin, 0),
-        ('binance', True, 3, 2.2, 2.5, 0.003, 75.22656719, margin, 0),
-        ('binance', True, 1, 2.2, 2.5, 0.0025, 75.18906641, margin, 0),
-        ('binance', True, 1, 2.2, 2.5, 0.003, 75.22656719, margin, 0),
-
-        # Kraken
-        ('kraken', False, 3, 2.0, 2.5, 0.0025, 74.7725, margin, 0),
-        ('kraken', False, 3, 2.0, 2.5, 0.003, 74.735, margin, 0),
-        ('kraken', True, 3, 2.2, 2.5, 0.0025, 75.2626875, margin, 0),
-        ('kraken', True, 3, 2.2, 2.5, 0.003, 75.300225, margin, 0),
-        ('kraken', True, 1, 2.2, 2.5, 0.0025, 75.2626875, margin, 0),
-        ('kraken', True, 1, 2.2, 2.5, 0.003, 75.300225, margin, 0),
-
-        ('binance', False, 1, 2.0, 2.5, 0.0025, 75.8125, futures, 1),
-        ('binance', False, 3, 2.0, 2.5, 0.0025, 73.8125, futures, -1),
-        ('binance', True, 3, 2.0, 2.5, 0.0025,  74.1875, futures, 1),
-        ('binance', True, 1, 2.0, 2.5, 0.0025,  76.1875, futures, -1),
-
-    ])
-@pytest.mark.usefixtures("init_persistence")
-def test_calc_close_trade_price(
-    open_rate, exchange, is_short,
-    lev, close_rate, fee_rate, result, trading_mode, funding_fees
-):
-    trade = Trade(
-        pair='ADA/USDT',
-        stake_amount=60.0,
-        amount=30.0,
-        open_rate=open_rate,
-        open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=10),
-        fee_open=fee_rate,
-        fee_close=fee_rate,
-        exchange=exchange,
-        interest_rate=0.0005,
-        is_short=is_short,
-        leverage=lev,
-        trading_mode=trading_mode,
-        funding_fees=funding_fees
-    )
-    trade.open_order_id = 'close_trade'
-    assert round(trade.calc_close_trade_value(rate=close_rate, fee=fee_rate), 8) == result
-
-
-@pytest.mark.parametrize(
-    'exchange,is_short,lev,close_rate,fee_close,profit,profit_ratio,trading_mode,funding_fees', [
-        ('binance', False, 1, 2.1, 0.0025, 2.6925, 0.044763092, spot, 0),
-        ('binance', False, 3, 2.1, 0.0025, 2.69166667, 0.134247714, margin, 0),
-        ('binance', True, 1, 2.1, 0.0025, -3.3088157, -0.055285142, margin, 0),
-        ('binance', True, 3, 2.1, 0.0025, -3.3088157, -0.16585542, margin, 0),
-
-        ('binance', False, 1, 1.9, 0.0025, -3.2925, -0.054738154, margin, 0),
-        ('binance', False, 3, 1.9, 0.0025, -3.29333333, -0.164256026, margin, 0),
-        ('binance', True, 1, 1.9, 0.0025, 2.70630953, 0.0452182043, margin, 0),
-        ('binance', True, 3, 1.9, 0.0025, 2.70630953, 0.135654613, margin, 0),
-
-        ('binance', False, 1, 2.2, 0.0025, 5.685, 0.09451371, margin, 0),
-        ('binance', False, 3, 2.2, 0.0025, 5.68416667, 0.28349958, margin, 0),
-        ('binance', True, 1, 2.2, 0.0025, -6.3163784, -0.10553681, margin, 0),
-        ('binance', True, 3, 2.2, 0.0025, -6.3163784, -0.31661044, margin, 0),
-
-        # Kraken
-        ('kraken', False, 1, 2.1, 0.0025, 2.6925, 0.044763092, spot, 0),
-        ('kraken', False, 3, 2.1, 0.0025, 2.6525, 0.132294264, margin, 0),
-        ('kraken', True, 1, 2.1, 0.0025, -3.3706575, -0.056318421, margin, 0),
-        ('kraken', True, 3, 2.1, 0.0025, -3.3706575, -0.168955263, margin, 0),
-
-        ('kraken', False, 1, 1.9, 0.0025, -3.2925, -0.054738154, margin, 0),
-        ('kraken', False, 3, 1.9, 0.0025, -3.3325, -0.166209476, margin, 0),
-        ('kraken', True, 1, 1.9, 0.0025, 2.6503575, 0.044283333, margin, 0),
-        ('kraken', True, 3, 1.9, 0.0025, 2.6503575, 0.132850000, margin, 0),
-
-        ('kraken', False, 1, 2.2, 0.0025, 5.685, 0.09451371, margin, 0),
-        ('kraken', False, 3, 2.2, 0.0025, 5.645, 0.28154613, margin, 0),
-        ('kraken', True, 1, 2.2, 0.0025, -6.381165, -0.1066192, margin, 0),
-        ('kraken', True, 3, 2.2, 0.0025, -6.381165, -0.3198578, margin, 0),
-
-        ('binance', False, 1, 2.1, 0.003, 2.66100000, 0.044239401, spot, 0),
-        ('binance', False, 1, 1.9, 0.003, -3.3209999, -0.055211970, spot, 0),
-        ('binance', False, 1, 2.2, 0.003, 5.6520000, 0.093965087, spot, 0),
-
-        # # FUTURES, funding_fee=1
-        ('binance', False, 1, 2.1, 0.0025, 3.6925, 0.06138819, futures, 1),
-        ('binance', False, 3, 2.1, 0.0025, 3.6925, 0.18416458, futures, 1),
-        ('binance', True, 1, 2.1, 0.0025, -2.3074999, -0.03855472, futures, 1),
-        ('binance', True, 3, 2.1, 0.0025, -2.3074999, -0.11566416, futures, 1),
-
-        ('binance', False, 1, 1.9, 0.0025, -2.2925, -0.03811305, futures, 1),
-        ('binance', False, 3, 1.9, 0.0025, -2.2925, -0.11433915, futures, 1),
-        ('binance', True, 1, 1.9, 0.0025, 3.7075, 0.06194653, futures, 1),
-        ('binance', True, 3, 1.9, 0.0025, 3.7075, 0.18583959, futures, 1),
-
-        ('binance', False, 1, 2.2, 0.0025, 6.685, 0.11113881, futures, 1),
-        ('binance', False, 3, 2.2, 0.0025, 6.685, 0.33341645, futures, 1),
-        ('binance', True, 1, 2.2, 0.0025, -5.315, -0.08880534, futures, 1),
-        ('binance', True, 3, 2.2, 0.0025, -5.315, -0.26641604, futures, 1),
-
-        # FUTURES, funding_fee=-1
-        ('binance', False, 1, 2.1, 0.0025, 1.6925, 0.02813798, futures, -1),
-        ('binance', False, 3, 2.1, 0.0025, 1.6925, 0.08441396, futures, -1),
-        ('binance', True, 1, 2.1, 0.0025, -4.307499, -0.07197159, futures, -1),
-        ('binance', True, 3, 2.1, 0.0025, -4.307499, -0.21591478, futures, -1),
-
-        ('binance', False, 1, 1.9, 0.0025, -4.292499, -0.07136325, futures, -1),
-        ('binance', False, 3, 1.9, 0.0025, -4.292499, -0.21408977, futures, -1),
-        ('binance', True, 1, 1.9, 0.0025, 1.7075, 0.02852965, futures, -1),
-        ('binance', True, 3, 1.9, 0.0025, 1.7075, 0.08558897, futures, -1),
-
-        ('binance', False, 1, 2.2, 0.0025, 4.684999, 0.07788861, futures, -1),
-        ('binance', False, 3, 2.2, 0.0025, 4.684999, 0.23366583, futures, -1),
-        ('binance', True, 1, 2.2, 0.0025, -7.315, -0.12222222, futures, -1),
-        ('binance', True, 3, 2.2, 0.0025, -7.315, -0.36666666, futures, -1),
-    ])
-@pytest.mark.usefixtures("init_persistence")
-def test_calc_profit(
-    exchange,
-    is_short,
-    lev,
-    close_rate,
-    fee_close,
-    profit,
-    profit_ratio,
-    trading_mode,
-    funding_fees
-):
-    """
-        10 minute limit trade on Binance/Kraken at 1x, 3x leverage
-        arguments:
-            fee:
-                0.25% quote
-                0.30% quote
-            interest_rate: 0.05% per 4 hrs
-            open_rate: 2.0 quote
-            close_rate:
-                1.9 quote
-                2.1 quote
-                2.2 quote
-            amount: = 30.0 crypto
-            stake_amount
-                1x,-1x: 60.0  quote
-                3x,-3x: 20.0  quote
-            hours: 1/6 (10 minutes)
-            funding_fees: 1
-        borrowed
-             1x:  0 quote
-             3x: 40 quote
-            -1x: 30 crypto
-            -3x: 30 crypto
-        time-periods:
-            kraken: (1 + 1) 4hr_periods = 2 4hr_periods
-            binance: 1/24 24hr_periods
-        interest: borrowed * interest_rate * time-periods
-            1x            :  /
-            binance     3x: 40 * 0.0005 * 1/24 = 0.0008333333333333334 quote
-            kraken      3x: 40 * 0.0005 * 2    = 0.040 quote
-            binace -1x,-3x: 30 * 0.0005 * 1/24 = 0.000625 crypto
-            kraken -1x,-3x: 30 * 0.0005 * 2    = 0.030 crypto
-        open_value: (amount * open_rate) ± (amount * open_rate * fee)
-          0.0025 fee
-             1x, 3x: 30 * 2 + 30 * 2 * 0.0025 = 60.15 quote
-            -1x,-3x: 30 * 2 - 30 * 2 * 0.0025 = 59.85 quote
-          0.003 fee: Is only applied to close rate in this test
-        amount_closed:
-            1x, 3x                         = amount
-            -1x, -3x                       = amount + interest
-            binance -1x,-3x: 30 + 0.000625 = 30.000625 crypto
-            kraken  -1x,-3x: 30 + 0.03     = 30.03 crypto
-        close_value:
-            equations:
-                1x, 3x: (amount_closed * close_rate) - (amount_closed * close_rate * fee) - interest
-                -1x,-3x: (amount_closed * close_rate) + (amount_closed * close_rate * fee)
-            2.1 quote
-                bin,krak  1x: (30.00 * 2.1) - (30.00 * 2.1 * 0.0025)                = 62.8425
-                bin       3x: (30.00 * 2.1) - (30.00 * 2.1 * 0.0025) - 0.0008333333 = 62.8416666667
-                krak      3x: (30.00 * 2.1) - (30.00 * 2.1 * 0.0025) - 0.040        = 62.8025
-                bin  -1x,-3x: (30.000625 * 2.1) + (30.000625 * 2.1 * 0.0025)        = 63.15881578125
-                krak -1x,-3x: (30.03 * 2.1) + (30.03 * 2.1 * 0.0025)                = 63.2206575
-            1.9 quote
-                bin,krak  1x: (30.00 * 1.9) - (30.00 * 1.9 * 0.0025)                = 56.8575
-                bin       3x: (30.00 * 1.9) - (30.00 * 1.9 * 0.0025) - 0.0008333333 = 56.85666667
-                krak      3x: (30.00 * 1.9) - (30.00 * 1.9 * 0.0025) - 0.040        = 56.8175
-                bin  -1x,-3x: (30.000625 * 1.9) + (30.000625 * 1.9 * 0.0025)        = 57.14369046875
-                krak -1x,-3x: (30.03 * 1.9) + (30.03 * 1.9 * 0.0025)                = 57.1996425
-            2.2 quote
-                bin,krak  1x: (30.00 * 2.20) - (30.00 * 2.20 * 0.0025)              = 65.835
-                bin       3x: (30.00 * 2.20) - (30.00 * 2.20 * 0.0025) - 0.00083333 = 65.83416667
-                krak      3x: (30.00 * 2.20) - (30.00 * 2.20 * 0.0025) - 0.040      = 65.795
-                bin  -1x,-3x: (30.000625 * 2.20) + (30.000625 * 2.20 * 0.0025)      = 66.1663784375
-                krak -1x,-3x: (30.03 * 2.20) + (30.03 * 2.20 * 0.0025)              = 66.231165
-        total_profit:
-            equations:
-                1x, 3x : close_value - open_value
-                -1x,-3x: open_value - close_value
-            2.1 quote
-                binance,kraken 1x: 62.8425     - 60.15          = 2.6925
-                binance        3x: 62.84166667 - 60.15          = 2.69166667
-                kraken         3x: 62.8025     - 60.15          = 2.6525
-                binance   -1x,-3x: 59.850      - 63.15881578125 = -3.308815781249997
-                kraken    -1x,-3x: 59.850      - 63.2206575     = -3.3706575
-            1.9 quote
-                binance,kraken 1x: 56.8575     - 60.15          = -3.2925
-                binance        3x: 56.85666667 - 60.15          = -3.29333333
-                kraken         3x: 56.8175     - 60.15          = -3.3325
-                binance   -1x,-3x: 59.850      - 57.14369046875 = 2.7063095312499996
-                kraken    -1x,-3x: 59.850      - 57.1996425     = 2.6503575
-            2.2 quote
-                binance,kraken 1x: 65.835      - 60.15          = 5.685
-                binance        3x: 65.83416667 - 60.15          = 5.68416667
-                kraken         3x: 65.795      - 60.15          = 5.645
-                binance   -1x,-3x: 59.850      - 66.1663784375  = -6.316378437499999
-                kraken    -1x,-3x: 59.850      - 66.231165      = -6.381165
-        total_profit_ratio:
-            equations:
-                1x, 3x : ((close_value/open_value) - 1) * leverage
-                -1x,-3x: (1 - (close_value/open_value)) * leverage
-            2.1 quote
-                binance,kraken 1x: (62.8425 / 60.15) - 1             = 0.04476309226932673
-                binance        3x: ((62.84166667 / 60.15) - 1)*3     = 0.13424771421446402
-                kraken         3x: ((62.8025 / 60.15) - 1)*3         = 0.13229426433915248
-                binance       -1x: 1 - (63.15881578125 / 59.850)     = -0.05528514254385963
-                binance       -3x: (1 - (63.15881578125 / 59.850))*3 = -0.1658554276315789
-                kraken        -1x: 1 - (63.2206575 / 59.850)         = -0.05631842105263152
-                kraken        -3x: (1 - (63.2206575 / 59.850))*3     = -0.16895526315789455
-            1.9 quote
-                binance,kraken 1x: (56.8575 / 60.15) - 1             = -0.05473815461346632
-                binance        3x: ((56.85666667 / 60.15) - 1)*3     = -0.16425602643391513
-                kraken         3x: ((56.8175 / 60.15) - 1)*3         = -0.16620947630922667
-                binance       -1x: 1 - (57.14369046875 / 59.850)     = 0.045218204365079395
-                binance       -3x: (1 - (57.14369046875 / 59.850))*3 = 0.13565461309523819
-                kraken        -1x: 1 - (57.1996425 / 59.850)         = 0.04428333333333334
-                kraken        -3x: (1 - (57.1996425 / 59.850))*3     = 0.13285000000000002
-            2.2 quote
-                binance,kraken 1x: (65.835 / 60.15) - 1             = 0.0945137157107232
-                binance        3x: ((65.83416667 / 60.15) - 1)*3     = 0.2834995845386534
-                kraken         3x: ((65.795 / 60.15) - 1)*3         = 0.2815461346633419
-                binance       -1x: 1 - (66.1663784375 / 59.850)     = -0.1055368159983292
-                binance       -3x: (1 - (66.1663784375 / 59.850))*3 = -0.3166104479949876
-                kraken        -1x: 1 - (66.231165 / 59.850)         = -0.106619298245614
-                kraken        -3x: (1 - (66.231165 / 59.850))*3     = -0.319857894736842
-        fee: 0.003, 1x
-            close_value:
-                2.1 quote: (30.00 * 2.1) - (30.00 * 2.1 * 0.003) = 62.811
-                1.9 quote: (30.00 * 1.9) - (30.00 * 1.9 * 0.003) = 56.829
-                2.2 quote: (30.00 * 2.2) - (30.00 * 2.2 * 0.003) = 65.802
-            total_profit
-                fee: 0.003, 1x
-                    2.1 quote: 62.811 - 60.15 = 2.6610000000000014
-                    1.9 quote: 56.829 - 60.15 = -3.320999999999998
-                    2.2 quote: 65.802 - 60.15 = 5.652000000000008
-            total_profit_ratio
-                fee: 0.003, 1x
-                    2.1 quote: (62.811 / 60.15) - 1 = 0.04423940149625927
-                    1.9 quote: (56.829 / 60.15) - 1 = -0.05521197007481293
-                    2.2 quote: (65.802 / 60.15) - 1 = 0.09396508728179565
-        futures (live):
-            funding_fee: 1
-                close_value:
-                    equations:
-                        1x,3x: (amount * close_rate) - (amount * close_rate * fee) + funding_fees
-                        -1x,-3x: (amount * close_rate) + (amount * close_rate * fee) - funding_fees
-                    2.1 quote
-                        1x,3x: (30.00 * 2.1) - (30.00 * 2.1 * 0.0025) + 1   = 63.8425
-                        -1x,-3x: (30.00 * 2.1) + (30.00 * 2.1 * 0.0025) - 1   = 62.1575
-                    1.9 quote
-                        1x,3x: (30.00 * 1.9) - (30.00 * 1.9 * 0.0025) + 1   = 57.8575
-                        -1x,-3x: (30.00 * 1.9) + (30.00 * 1.9 * 0.0025) - 1   = 56.1425
-                    2.2 quote:
-                        1x,3x: (30.00 * 2.20) - (30.00 * 2.20 * 0.0025) + 1 = 66.835
-                        -1x,-3x: (30.00 * 2.20) + (30.00 * 2.20 * 0.0025) - 1 = 65.165
-                total_profit:
-                    2.1 quote
-                        1x,3x:   63.8425     - 60.15          = 3.6925
-                        -1x,-3x: 59.850      - 62.1575        = -2.3074999999999974
-                    1.9 quote
-                        1x,3x:   57.8575     - 60.15          = -2.2925
-                        -1x,-3x: 59.850      - 56.1425        = 3.707500000000003
-                    2.2 quote:
-                        1x,3x:   66.835      - 60.15          = 6.685
-                        -1x,-3x: 59.850      - 65.165         = -5.315000000000005
-                total_profit_ratio:
-                    2.1 quote
-                        1x: (63.8425 / 60.15) - 1             = 0.06138819617622615
-                        3x: ((63.8425 / 60.15) - 1)*3         = 0.18416458852867845
-                        -1x: 1 - (62.1575 / 59.850)           = -0.038554720133667564
-                        -3x: (1 - (62.1575 / 59.850))*3       = -0.11566416040100269
-                    1.9 quote
-                        1x: (57.8575 / 60.15) - 1             = -0.0381130507065669
-                        3x: ((57.8575 / 60.15) - 1)*3         = -0.1143391521197007
-                        -1x: 1 - (56.1425 / 59.850)           = 0.06194653299916464
-                        -3x: (1 - (56.1425 / 59.850))*3       = 0.18583959899749392
-                    2.2 quote
-                        1x: (66.835 / 60.15) - 1             = 0.11113881961762262
-                        3x: ((66.835 / 60.15) - 1)*3         = 0.33341645885286786
-                        -1x: 1 - (65.165 / 59.850)           = -0.08880534670008355
-                        -3x: (1 - (65.165 / 59.850))*3       = -0.26641604010025066
-            funding_fee: -1
-                close_value:
-                    equations:
-                        (amount * close_rate) - (amount * close_rate * fee) + funding_fees
-                        (amount * close_rate) - (amount * close_rate * fee) - funding_fees
-                    2.1 quote
-                        1x,3x:  (30.00 * 2.1) - (30.00 * 2.1 * 0.0025) + (-1)   = 61.8425
-                        -1x,-3x: (30.00 * 2.1) + (30.00 * 2.1 * 0.0025) - (-1)   = 64.1575
-                    1.9 quote
-                        1x,3x:  (30.00 * 1.9) - (30.00 * 1.9 * 0.0025) + (-1)   = 55.8575
-                        -1x,-3x: (30.00 * 1.9) + (30.00 * 1.9 * 0.0025) - (-1)   = 58.1425
-                    2.2 quote:
-                        1x,3x:  (30.00 * 2.20) - (30.00 * 2.20 * 0.0025) + (-1) = 64.835
-                        -1x,-3x: (30.00 * 2.20) + (30.00 * 2.20 * 0.0025) - (-1) = 67.165
-                total_profit:
-                    2.1 quote
-                        1x,3x:   61.8425     - 60.15          = 1.6925000000000026
-                        -1x,-3x: 59.850      - 64.1575        = -4.307499999999997
-                    1.9 quote
-                        1x,3x:   55.8575     - 60.15          = -4.292499999999997
-                        -1x,-3x: 59.850      - 58.1425        = 1.7075000000000031
-                    2.2 quote:
-                        1x,3x:   64.835      - 60.15          = 4.684999999999995
-                        -1x,-3x: 59.850      - 67.165         = -7.315000000000005
-                total_profit_ratio:
-                    2.1 quote
-                        1x: (61.8425 / 60.15) - 1             = 0.028137988362427313
-                        3x: ((61.8425 / 60.15) - 1)*3         = 0.08441396508728194
-                        -1x: 1 - (64.1575 / 59.850)           = -0.07197159565580624
-                        -3x: (1 - (64.1575 / 59.850))*3       = -0.21591478696741873
-                    1.9 quote
-                        1x: (55.8575 / 60.15) - 1             = -0.07136325852036574
-                        3x: ((55.8575 / 60.15) - 1)*3         = -0.2140897755610972
-                        -1x: 1 - (58.1425 / 59.850)           = 0.02852965747702596
-                        -3x: (1 - (58.1425 / 59.850))*3       = 0.08558897243107788
-                    2.2 quote
-                        1x: (64.835 / 60.15) - 1              = 0.07788861180382378
-                        3x: ((64.835 / 60.15) - 1)*3          = 0.23366583541147135
-                        -1x: 1 - (67.165 / 59.850)            = -0.12222222222222223
-                        -3x: (1 - (67.165 / 59.850))*3        = -0.3666666666666667
-    """
-    trade = Trade(
-        pair='ADA/USDT',
-        stake_amount=60.0,
-        amount=30.0,
-        open_rate=2.0,
-        open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=10),
-        interest_rate=0.0005,
-        exchange=exchange,
-        is_short=is_short,
-        leverage=lev,
-        fee_open=0.0025,
-        fee_close=fee_close,
-        trading_mode=trading_mode,
-        funding_fees=funding_fees
-    )
-    trade.open_order_id = 'something'
-
-    assert pytest.approx(trade.calc_profit(rate=close_rate)) == round(profit, 8)
-    assert pytest.approx(trade.calc_profit_ratio(rate=close_rate)) == round(profit_ratio, 8)
-
-
-@pytest.mark.usefixtures("init_persistence")
-def test_clean_dry_run_db(default_conf, fee):
-
-    # Simulate dry_run entries
-    trade = Trade(
-        pair='ADA/USDT',
-        stake_amount=0.001,
-        amount=123.0,
-        fee_open=fee.return_value,
-        fee_close=fee.return_value,
-        open_rate=0.123,
-        exchange='binance',
-        open_order_id='dry_run_buy_12345'
-    )
-    Trade.query.session.add(trade)
-
-    trade = Trade(
-        pair='ETC/BTC',
-        stake_amount=0.001,
-        amount=123.0,
-        fee_open=fee.return_value,
-        fee_close=fee.return_value,
-        open_rate=0.123,
-        exchange='binance',
-        open_order_id='dry_run_sell_12345'
-    )
-    Trade.query.session.add(trade)
-
-    # Simulate prod entry
-    trade = Trade(
-        pair='ETC/BTC',
-        stake_amount=0.001,
-        amount=123.0,
-        fee_open=fee.return_value,
-        fee_close=fee.return_value,
-        open_rate=0.123,
-        exchange='binance',
-        open_order_id='prod_buy_12345'
-    )
-    Trade.query.session.add(trade)
-
-    # We have 3 entries: 2 dry_run, 1 prod
-    assert len(Trade.query.filter(Trade.open_order_id.isnot(None)).all()) == 3
-
-    clean_dry_run_db()
-
-    # We have now only the prod
-    assert len(Trade.query.filter(Trade.open_order_id.isnot(None)).all()) == 1
-
-
-def test_migrate_new(mocker, default_conf, fee, caplog):
-    """
-    Test Database migration (starting with new pairformat)
-    """
-    caplog.set_level(logging.DEBUG)
-    amount = 103.223
-    # Always create all columns apart from the last!
-    create_table_old = """CREATE TABLE IF NOT EXISTS "trades" (
-                                id INTEGER NOT NULL,
-                                exchange VARCHAR NOT NULL,
-                                pair VARCHAR NOT NULL,
-                                is_open BOOLEAN NOT NULL,
-                                fee FLOAT NOT NULL,
-                                open_rate FLOAT,
-                                close_rate FLOAT,
-                                close_profit FLOAT,
-                                stake_amount FLOAT NOT NULL,
-                                amount FLOAT,
-                                open_date DATETIME NOT NULL,
-                                close_date DATETIME,
-                                open_order_id VARCHAR,
-                                stop_loss FLOAT,
-                                initial_stop_loss FLOAT,
-                                max_rate FLOAT,
-                                sell_reason VARCHAR,
-                                strategy VARCHAR,
-                                ticker_interval INTEGER,
-                                stoploss_order_id VARCHAR,
-                                PRIMARY KEY (id),
-                                CHECK (is_open IN (0, 1))
-                                );"""
-    insert_table_old = """INSERT INTO trades (exchange, pair, is_open, fee,
-                          open_rate, stake_amount, amount, open_date,
-                          stop_loss, initial_stop_loss, max_rate, ticker_interval,
-                          open_order_id, stoploss_order_id)
-                          VALUES ('binance', 'ETC/BTC', 1, {fee},
-                          0.00258580, {stake}, {amount},
-                          '2019-11-28 12:44:24.000000',
-                          0.0, 0.0, 0.0, '5m',
-                          'buy_order', 'stop_order_id222')
-                          """.format(fee=fee.return_value,
-                                     stake=default_conf.get("stake_amount"),
-                                     amount=amount
-                                     )
-    engine = create_engine('sqlite://')
-    mocker.patch('freqtrade.persistence.models.create_engine', lambda *args, **kwargs: engine)
-
-    # Create table using the old format
-    with engine.begin() as connection:
-        connection.execute(text(create_table_old))
-        connection.execute(text("create index ix_trades_is_open on trades(is_open)"))
-        connection.execute(text("create index ix_trades_pair on trades(pair)"))
-        connection.execute(text(insert_table_old))
-
-        # fake previous backup
-        connection.execute(text("create table trades_bak as select * from trades"))
-
-        connection.execute(text("create table trades_bak1 as select * from trades"))
-    # Run init to test migration
-    init_db(default_conf['db_url'], default_conf['dry_run'])
-
-    assert len(Trade.query.filter(Trade.id == 1).all()) == 1
-    trade = Trade.query.filter(Trade.id == 1).first()
-    assert trade.fee_open == fee.return_value
-    assert trade.fee_close == fee.return_value
-    assert trade.open_rate_requested is None
-    assert trade.close_rate_requested is None
-    assert trade.is_open == 1
-    assert trade.amount == amount
-    assert trade.amount_requested == amount
-    assert trade.stake_amount == default_conf.get("stake_amount")
-    assert trade.pair == "ETC/BTC"
-    assert trade.exchange == "binance"
-    assert trade.max_rate == 0.0
-    assert trade.min_rate is None
-    assert trade.stop_loss == 0.0
-    assert trade.initial_stop_loss == 0.0
-    assert trade.exit_reason is None
-    assert trade.strategy is None
-    assert trade.timeframe == '5m'
-    assert trade.stoploss_order_id == 'stop_order_id222'
-    assert trade.stoploss_last_update is None
-    assert log_has("trying trades_bak1", caplog)
-    assert log_has("trying trades_bak2", caplog)
-    assert log_has("Running database migration for trades - backup: trades_bak2, orders_bak0",
-                   caplog)
-    assert trade.open_trade_value == trade._calc_open_trade_value()
-    assert trade.close_profit_abs is None
-
-    assert log_has("Moving open orders to Orders table.", caplog)
-    orders = Order.query.all()
-    assert len(orders) == 2
-    assert orders[0].order_id == 'buy_order'
-    assert orders[0].ft_order_side == 'buy'
-
-    assert orders[1].order_id == 'stop_order_id222'
-    assert orders[1].ft_order_side == 'stoploss'
-
-
-def test_migrate_mid_state(mocker, default_conf, fee, caplog):
-    """
-    Test Database migration (starting with new pairformat)
-    """
-    caplog.set_level(logging.DEBUG)
-    amount = 103.223
-    create_table_old = """CREATE TABLE IF NOT EXISTS "trades" (
-                                id INTEGER NOT NULL,
-                                exchange VARCHAR NOT NULL,
-                                pair VARCHAR NOT NULL,
-                                is_open BOOLEAN NOT NULL,
-                                fee_open FLOAT NOT NULL,
-                                fee_close FLOAT NOT NULL,
-                                open_rate FLOAT,
-                                close_rate FLOAT,
-                                close_profit FLOAT,
-                                stake_amount FLOAT NOT NULL,
-                                amount FLOAT,
-                                open_date DATETIME NOT NULL,
-                                close_date DATETIME,
-                                open_order_id VARCHAR,
-                                PRIMARY KEY (id),
-                                CHECK (is_open IN (0, 1))
-                                );"""
-    insert_table_old = """INSERT INTO trades (exchange, pair, is_open, fee_open, fee_close,
-                          open_rate, stake_amount, amount, open_date)
-                          VALUES ('binance', 'ETC/BTC', 1, {fee}, {fee},
-                          0.00258580, {stake}, {amount},
-                          '2019-11-28 12:44:24.000000')
-                          """.format(fee=fee.return_value,
-                                     stake=default_conf.get("stake_amount"),
-                                     amount=amount
-                                     )
-    engine = create_engine('sqlite://')
-    mocker.patch('freqtrade.persistence.models.create_engine', lambda *args, **kwargs: engine)
-
-    # Create table using the old format
-    with engine.begin() as connection:
-        connection.execute(text(create_table_old))
-        connection.execute(text(insert_table_old))
-
-    # Run init to test migration
-    init_db(default_conf['db_url'], default_conf['dry_run'])
-
-    assert len(Trade.query.filter(Trade.id == 1).all()) == 1
-    trade = Trade.query.filter(Trade.id == 1).first()
-    assert trade.fee_open == fee.return_value
-    assert trade.fee_close == fee.return_value
-    assert trade.open_rate_requested is None
-    assert trade.close_rate_requested is None
-    assert trade.is_open == 1
-    assert trade.amount == amount
-    assert trade.stake_amount == default_conf.get("stake_amount")
-    assert trade.pair == "ETC/BTC"
-    assert trade.exchange == "binance"
-    assert trade.max_rate == 0.0
-    assert trade.stop_loss == 0.0
-    assert trade.initial_stop_loss == 0.0
-    assert trade.open_trade_value == trade._calc_open_trade_value()
-    assert log_has("trying trades_bak0", caplog)
-    assert log_has("Running database migration for trades - backup: trades_bak0, orders_bak0",
-                   caplog)
-
-
-def test_migrate_get_last_sequence_ids():
-    engine = MagicMock()
-    engine.begin = MagicMock()
-    engine.name = 'postgresql'
-    get_last_sequence_ids(engine, 'trades_bak', 'orders_bak')
-
-    assert engine.begin.call_count == 2
-    engine.reset_mock()
-    engine.begin.reset_mock()
-
-    engine.name = 'somethingelse'
-    get_last_sequence_ids(engine, 'trades_bak', 'orders_bak')
-
-    assert engine.begin.call_count == 0
-
-
-def test_migrate_set_sequence_ids():
-    engine = MagicMock()
-    engine.begin = MagicMock()
-    engine.name = 'postgresql'
-    set_sequence_ids(engine, 22, 55)
-
-    assert engine.begin.call_count == 1
-    engine.reset_mock()
-    engine.begin.reset_mock()
-
-    engine.name = 'somethingelse'
-    set_sequence_ids(engine, 22, 55)
-
-    assert engine.begin.call_count == 0
-
-
-def test_adjust_stop_loss(fee):
-    trade = Trade(
-        pair='ADA/USDT',
-        stake_amount=30.0,
-        amount=30,
-        fee_open=fee.return_value,
-        fee_close=fee.return_value,
-        exchange='binance',
-        open_rate=1,
-        max_rate=1,
-    )
-
-    trade.adjust_stop_loss(trade.open_rate, 0.05, True)
-    assert trade.stop_loss == 0.95
-    assert trade.stop_loss_pct == -0.05
-    assert trade.initial_stop_loss == 0.95
-    assert trade.initial_stop_loss_pct == -0.05
-
-    # Get percent of profit with a lower rate
-    trade.adjust_stop_loss(0.96, 0.05)
-    assert trade.stop_loss == 0.95
-    assert trade.stop_loss_pct == -0.05
-    assert trade.initial_stop_loss == 0.95
-    assert trade.initial_stop_loss_pct == -0.05
-
-    # Get percent of profit with a custom rate (Higher than open rate)
-    trade.adjust_stop_loss(1.3, -0.1)
-    assert round(trade.stop_loss, 8) == 1.17
-    assert trade.stop_loss_pct == -0.1
-    assert trade.initial_stop_loss == 0.95
-    assert trade.initial_stop_loss_pct == -0.05
-
-    # current rate lower again ... should not change
-    trade.adjust_stop_loss(1.2, 0.1)
-    assert round(trade.stop_loss, 8) == 1.17
-    assert trade.initial_stop_loss == 0.95
-    assert trade.initial_stop_loss_pct == -0.05
-
-    # current rate higher... should raise stoploss
-    trade.adjust_stop_loss(1.4, 0.1)
-    assert round(trade.stop_loss, 8) == 1.26
-    assert trade.initial_stop_loss == 0.95
-    assert trade.initial_stop_loss_pct == -0.05
-
-    #  Initial is true but stop_loss set - so doesn't do anything
-    trade.adjust_stop_loss(1.7, 0.1, True)
-    assert round(trade.stop_loss, 8) == 1.26
-    assert trade.initial_stop_loss == 0.95
-    assert trade.initial_stop_loss_pct == -0.05
-    assert trade.stop_loss_pct == -0.1
-
-
-def test_adjust_stop_loss_short(fee):
-    trade = Trade(
-        pair='ADA/USDT',
-        stake_amount=0.001,
-        amount=5,
-        fee_open=fee.return_value,
-        fee_close=fee.return_value,
-        exchange='binance',
-        open_rate=1,
-        max_rate=1,
-        is_short=True,
-    )
-    trade.adjust_stop_loss(trade.open_rate, 0.05, True)
-    assert trade.stop_loss == 1.05
-    assert trade.stop_loss_pct == -0.05
-    assert trade.initial_stop_loss == 1.05
-    assert trade.initial_stop_loss_pct == -0.05
-    # Get percent of profit with a lower rate
-    trade.adjust_stop_loss(1.04, 0.05)
-    assert trade.stop_loss == 1.05
-    assert trade.stop_loss_pct == -0.05
-    assert trade.initial_stop_loss == 1.05
-    assert trade.initial_stop_loss_pct == -0.05
-    # Get percent of profit with a custom rate (Higher than open rate)
-    trade.adjust_stop_loss(0.7, 0.1)
-    # If the price goes down to 0.7, with a trailing stop of 0.1,
-    # the new stoploss at 0.1 above 0.7 would be 0.7*0.1 higher
-    assert round(trade.stop_loss, 8) == 0.77
-    assert trade.stop_loss_pct == -0.1
-    assert trade.initial_stop_loss == 1.05
-    assert trade.initial_stop_loss_pct == -0.05
-    # current rate lower again ... should not change
-    trade.adjust_stop_loss(0.8, -0.1)
-    assert round(trade.stop_loss, 8) == 0.77
-    assert trade.initial_stop_loss == 1.05
-    assert trade.initial_stop_loss_pct == -0.05
-    # current rate higher... should raise stoploss
-    trade.adjust_stop_loss(0.6, -0.1)
-    assert round(trade.stop_loss, 8) == 0.66
-    assert trade.initial_stop_loss == 1.05
-    assert trade.initial_stop_loss_pct == -0.05
-    #  Initial is true but stop_loss set - so doesn't do anything
-    trade.adjust_stop_loss(0.3, -0.1, True)
-    assert round(trade.stop_loss, 8) == 0.66
-    assert trade.initial_stop_loss == 1.05
-    assert trade.initial_stop_loss_pct == -0.05
-    assert trade.stop_loss_pct == -0.1
-    trade.set_isolated_liq(0.63)
-    trade.adjust_stop_loss(0.59, -0.1)
-    assert trade.stop_loss == 0.63
-    assert trade.liquidation_price == 0.63
-
-
-def test_adjust_min_max_rates(fee):
-    trade = Trade(
-        pair='ADA/USDT',
-        stake_amount=30.0,
-        amount=30.0,
-        fee_open=fee.return_value,
-        fee_close=fee.return_value,
-        exchange='binance',
-        open_rate=1,
-    )
-
-    trade.adjust_min_max_rates(trade.open_rate, trade.open_rate)
-    assert trade.max_rate == 1
-    assert trade.min_rate == 1
-
-    # check min adjusted, max remained
-    trade.adjust_min_max_rates(0.96, 0.96)
-    assert trade.max_rate == 1
-    assert trade.min_rate == 0.96
-
-    # check max adjusted, min remains
-    trade.adjust_min_max_rates(1.05, 1.05)
-    assert trade.max_rate == 1.05
-    assert trade.min_rate == 0.96
-
-    # current rate "in the middle" - no adjustment
-    trade.adjust_min_max_rates(1.03, 1.03)
-    assert trade.max_rate == 1.05
-    assert trade.min_rate == 0.96
-
-    # current rate "in the middle" - no adjustment
-    trade.adjust_min_max_rates(1.10, 0.91)
-    assert trade.max_rate == 1.10
-    assert trade.min_rate == 0.91
-
-
-@pytest.mark.usefixtures("init_persistence")
-@pytest.mark.parametrize('use_db', [True, False])
-@pytest.mark.parametrize('is_short', [True, False])
-def test_get_open(fee, is_short, use_db):
-    Trade.use_db = use_db
-    Trade.reset_trades()
-
-    create_mock_trades(fee, is_short, use_db)
-    assert len(Trade.get_open_trades()) == 4
-
-    Trade.use_db = True
-
-
-@pytest.mark.usefixtures("init_persistence")
-@pytest.mark.parametrize('use_db', [True, False])
-def test_get_open_lev(fee, use_db):
-    Trade.use_db = use_db
-    Trade.reset_trades()
-
-    create_mock_trades_with_leverage(fee, use_db)
-    assert len(Trade.get_open_trades()) == 5
-
-    Trade.use_db = True
-
-
-@pytest.mark.usefixtures("init_persistence")
-def test_to_json(fee):
-
-    # Simulate dry_run entries
-    trade = Trade(
-        pair='ADA/USDT',
-        stake_amount=0.001,
-        amount=123.0,
-        amount_requested=123.0,
-        fee_open=fee.return_value,
-        fee_close=fee.return_value,
-        open_date=arrow.utcnow().shift(hours=-2).datetime,
-        open_rate=0.123,
-        exchange='binance',
-        enter_tag=None,
-        open_order_id='dry_run_buy_12345'
-    )
-    result = trade.to_json()
-    assert isinstance(result, dict)
-
-    assert result == {'trade_id': None,
-                      'pair': 'ADA/USDT',
-                      'is_open': None,
-                      'open_date': trade.open_date.strftime("%Y-%m-%d %H:%M:%S"),
-                      'open_timestamp': int(trade.open_date.timestamp() * 1000),
-                      'open_order_id': 'dry_run_buy_12345',
-                      'close_date': None,
-                      'close_timestamp': None,
-                      'open_rate': 0.123,
-                      'open_rate_requested': None,
-                      'open_trade_value': 15.1668225,
-                      'fee_close': 0.0025,
-                      'fee_close_cost': None,
-                      'fee_close_currency': None,
-                      'fee_open': 0.0025,
-                      'fee_open_cost': None,
-                      'fee_open_currency': None,
-                      'close_rate': None,
-                      'close_rate_requested': None,
-                      'amount': 123.0,
-                      'amount_requested': 123.0,
-                      'stake_amount': 0.001,
-                      'trade_duration': None,
-                      'trade_duration_s': None,
-                      'realized_profit': 0.0,
-                      'close_profit': None,
-                      'close_profit_pct': None,
-                      'close_profit_abs': None,
-                      'profit_ratio': None,
-                      'profit_pct': None,
-                      'profit_abs': None,
-                      'sell_reason': None,
-                      'exit_reason': None,
-                      'exit_order_status': None,
-                      'stop_loss_abs': None,
-                      'stop_loss_ratio': None,
-                      'stop_loss_pct': None,
-                      'stoploss_order_id': None,
-                      'stoploss_last_update': None,
-                      'stoploss_last_update_timestamp': None,
-                      'initial_stop_loss_abs': None,
-                      'initial_stop_loss_pct': None,
-                      'initial_stop_loss_ratio': None,
-                      'min_rate': None,
-                      'max_rate': None,
-                      'strategy': None,
-                      'buy_tag': None,
-                      'enter_tag': None,
-                      'timeframe': None,
-                      'exchange': 'binance',
-                      'leverage': None,
-                      'interest_rate': None,
-                      'liquidation_price': None,
-                      'is_short': None,
-                      'trading_mode': None,
-                      'funding_fees': None,
-                      'orders': [],
-                      }
-
-    # Simulate dry_run entries
-    trade = Trade(
-        pair='XRP/BTC',
-        stake_amount=0.001,
-        amount=100.0,
-        amount_requested=101.0,
-        fee_open=fee.return_value,
-        fee_close=fee.return_value,
-        open_date=arrow.utcnow().shift(hours=-2).datetime,
-        close_date=arrow.utcnow().shift(hours=-1).datetime,
-        open_rate=0.123,
-        close_rate=0.125,
-        enter_tag='buys_signal_001',
-        exchange='binance',
-    )
-    result = trade.to_json()
-    assert isinstance(result, dict)
-
-    assert result == {'trade_id': None,
-                      'pair': 'XRP/BTC',
-                      'open_date': trade.open_date.strftime("%Y-%m-%d %H:%M:%S"),
-                      'open_timestamp': int(trade.open_date.timestamp() * 1000),
-                      'close_date': trade.close_date.strftime("%Y-%m-%d %H:%M:%S"),
-                      'close_timestamp': int(trade.close_date.timestamp() * 1000),
-                      'open_rate': 0.123,
-                      'close_rate': 0.125,
-                      'amount': 100.0,
-                      'amount_requested': 101.0,
-                      'stake_amount': 0.001,
-                      'trade_duration': 60,
-                      'trade_duration_s': 3600,
-                      'stop_loss_abs': None,
-                      'stop_loss_pct': None,
-                      'stop_loss_ratio': None,
-                      'stoploss_order_id': None,
-                      'stoploss_last_update': None,
-                      'stoploss_last_update_timestamp': None,
-                      'initial_stop_loss_abs': None,
-                      'initial_stop_loss_pct': None,
-                      'initial_stop_loss_ratio': None,
-                      'realized_profit': 0.0,
-                      'close_profit': None,
-                      'close_profit_pct': None,
-                      'close_profit_abs': None,
-                      'profit_ratio': None,
-                      'profit_pct': None,
-                      'profit_abs': None,
-                      'close_rate_requested': None,
-                      'fee_close': 0.0025,
-                      'fee_close_cost': None,
-                      'fee_close_currency': None,
-                      'fee_open': 0.0025,
-                      'fee_open_cost': None,
-                      'fee_open_currency': None,
-                      'is_open': None,
-                      'max_rate': None,
-                      'min_rate': None,
-                      'open_order_id': None,
-                      'open_rate_requested': None,
-                      'open_trade_value': 12.33075,
-                      'sell_reason': None,
-                      'exit_reason': None,
-                      'exit_order_status': None,
-                      'strategy': None,
-                      'buy_tag': 'buys_signal_001',
-                      'enter_tag': 'buys_signal_001',
-                      'timeframe': None,
-                      'exchange': 'binance',
-                      'leverage': None,
-                      'interest_rate': None,
-                      'liquidation_price': None,
-                      'is_short': None,
-                      'trading_mode': None,
-                      'funding_fees': None,
-                      'orders': [],
-                      }
-
-
-def test_stoploss_reinitialization(default_conf, fee):
-    init_db(default_conf['db_url'])
-    trade = Trade(
-        pair='ADA/USDT',
-        stake_amount=30.0,
-        fee_open=fee.return_value,
-        open_date=arrow.utcnow().shift(hours=-2).datetime,
-        amount=30.0,
-        fee_close=fee.return_value,
-        exchange='binance',
-        open_rate=1,
-        max_rate=1,
-    )
-
-    trade.adjust_stop_loss(trade.open_rate, 0.05, True)
-    assert trade.stop_loss == 0.95
-    assert trade.stop_loss_pct == -0.05
-    assert trade.initial_stop_loss == 0.95
-    assert trade.initial_stop_loss_pct == -0.05
-    Trade.query.session.add(trade)
-
-    # Lower stoploss
-    Trade.stoploss_reinitialization(0.06)
-
-    trades = Trade.get_open_trades()
-    assert len(trades) == 1
-    trade_adj = trades[0]
-    assert trade_adj.stop_loss == 0.94
-    assert trade_adj.stop_loss_pct == -0.06
-    assert trade_adj.initial_stop_loss == 0.94
-    assert trade_adj.initial_stop_loss_pct == -0.06
-
-    # Raise stoploss
-    Trade.stoploss_reinitialization(0.04)
-
-    trades = Trade.get_open_trades()
-    assert len(trades) == 1
-    trade_adj = trades[0]
-    assert trade_adj.stop_loss == 0.96
-    assert trade_adj.stop_loss_pct == -0.04
-    assert trade_adj.initial_stop_loss == 0.96
-    assert trade_adj.initial_stop_loss_pct == -0.04
-
-    # Trailing stoploss (move stoplos up a bit)
-    trade.adjust_stop_loss(1.02, 0.04)
-    assert trade_adj.stop_loss == 0.9792
-    assert trade_adj.initial_stop_loss == 0.96
-
-    Trade.stoploss_reinitialization(0.04)
-
-    trades = Trade.get_open_trades()
-    assert len(trades) == 1
-    trade_adj = trades[0]
-    # Stoploss should not change in this case.
-    assert trade_adj.stop_loss == 0.9792
-    assert trade_adj.stop_loss_pct == -0.04
-    assert trade_adj.initial_stop_loss == 0.96
-    assert trade_adj.initial_stop_loss_pct == -0.04
-
-
-def test_stoploss_reinitialization_leverage(default_conf, fee):
-    init_db(default_conf['db_url'])
-    trade = Trade(
-        pair='ADA/USDT',
-        stake_amount=30.0,
-        fee_open=fee.return_value,
-        open_date=arrow.utcnow().shift(hours=-2).datetime,
-        amount=30.0,
-        fee_close=fee.return_value,
-        exchange='binance',
-        open_rate=1,
-        max_rate=1,
-        leverage=5.0,
-    )
-
-    trade.adjust_stop_loss(trade.open_rate, 0.1, True)
-    assert trade.stop_loss == 0.98
-    assert trade.stop_loss_pct == -0.1
-    assert trade.initial_stop_loss == 0.98
-    assert trade.initial_stop_loss_pct == -0.1
-    Trade.query.session.add(trade)
-
-    # Lower stoploss
-    Trade.stoploss_reinitialization(0.15)
-
-    trades = Trade.get_open_trades()
-    assert len(trades) == 1
-    trade_adj = trades[0]
-    assert trade_adj.stop_loss == 0.97
-    assert trade_adj.stop_loss_pct == -0.15
-    assert trade_adj.initial_stop_loss == 0.97
-    assert trade_adj.initial_stop_loss_pct == -0.15
-
-    # Raise stoploss
-    Trade.stoploss_reinitialization(0.05)
-
-    trades = Trade.get_open_trades()
-    assert len(trades) == 1
-    trade_adj = trades[0]
-    assert trade_adj.stop_loss == 0.99
-    assert trade_adj.stop_loss_pct == -0.05
-    assert trade_adj.initial_stop_loss == 0.99
-    assert trade_adj.initial_stop_loss_pct == -0.05
-
-    # Trailing stoploss (move stoplos up a bit)
-    trade.adjust_stop_loss(1.02, 0.05)
-    assert trade_adj.stop_loss == 1.0098
-    assert trade_adj.initial_stop_loss == 0.99
-
-    Trade.stoploss_reinitialization(0.05)
-
-    trades = Trade.get_open_trades()
-    assert len(trades) == 1
-    trade_adj = trades[0]
-    # Stoploss should not change in this case.
-    assert trade_adj.stop_loss == 1.0098
-    assert trade_adj.stop_loss_pct == -0.05
-    assert trade_adj.initial_stop_loss == 0.99
-    assert trade_adj.initial_stop_loss_pct == -0.05
-
-
-def test_stoploss_reinitialization_short(default_conf, fee):
-    init_db(default_conf['db_url'])
-    trade = Trade(
-        pair='ADA/USDT',
-        stake_amount=0.001,
-        fee_open=fee.return_value,
-        open_date=arrow.utcnow().shift(hours=-2).datetime,
-        amount=10,
-        fee_close=fee.return_value,
-        exchange='binance',
-        open_rate=1,
-        max_rate=1,
-        is_short=True,
-        leverage=5.0,
-    )
-    trade.adjust_stop_loss(trade.open_rate, -0.1, True)
-    assert trade.stop_loss == 1.02
-    assert trade.stop_loss_pct == -0.1
-    assert trade.initial_stop_loss == 1.02
-    assert trade.initial_stop_loss_pct == -0.1
-    Trade.query.session.add(trade)
-    # Lower stoploss
-    Trade.stoploss_reinitialization(-0.15)
-    trades = Trade.get_open_trades()
-    assert len(trades) == 1
-    trade_adj = trades[0]
-    assert trade_adj.stop_loss == 1.03
-    assert trade_adj.stop_loss_pct == -0.15
-    assert trade_adj.initial_stop_loss == 1.03
-    assert trade_adj.initial_stop_loss_pct == -0.15
-    # Raise stoploss
-    Trade.stoploss_reinitialization(-0.05)
-    trades = Trade.get_open_trades()
-    assert len(trades) == 1
-    trade_adj = trades[0]
-    assert trade_adj.stop_loss == 1.01
-    assert trade_adj.stop_loss_pct == -0.05
-    assert trade_adj.initial_stop_loss == 1.01
-    assert trade_adj.initial_stop_loss_pct == -0.05
-    # Trailing stoploss
-    trade.adjust_stop_loss(0.98, -0.05)
-    assert trade_adj.stop_loss == 0.9898
-    assert trade_adj.initial_stop_loss == 1.01
-    Trade.stoploss_reinitialization(-0.05)
-    trades = Trade.get_open_trades()
-    assert len(trades) == 1
-    trade_adj = trades[0]
-    # Stoploss should not change in this case.
-    assert trade_adj.stop_loss == 0.9898
-    assert trade_adj.stop_loss_pct == -0.05
-    assert trade_adj.initial_stop_loss == 1.01
-    assert trade_adj.initial_stop_loss_pct == -0.05
-    # Stoploss can't go above liquidation price
-    trade_adj.set_isolated_liq(0.985)
-    trade.adjust_stop_loss(0.9799, -0.05)
-    assert trade_adj.stop_loss == 0.985
-    assert trade_adj.stop_loss == 0.985
-
-
-def test_update_fee(fee):
-    trade = Trade(
-        pair='ADA/USDT',
-        stake_amount=30.0,
-        fee_open=fee.return_value,
-        open_date=arrow.utcnow().shift(hours=-2).datetime,
-        amount=30.0,
-        fee_close=fee.return_value,
-        exchange='binance',
-        open_rate=1,
-        max_rate=1,
-    )
-    fee_cost = 0.15
-    fee_currency = 'BTC'
-    fee_rate = 0.0075
-    assert trade.fee_open_currency is None
-    assert not trade.fee_updated('buy')
-    assert not trade.fee_updated('sell')
-
-    trade.update_fee(fee_cost, fee_currency, fee_rate, 'buy')
-    assert trade.fee_updated('buy')
-    assert not trade.fee_updated('sell')
-    assert trade.fee_open_currency == fee_currency
-    assert trade.fee_open_cost == fee_cost
-    assert trade.fee_open == fee_rate
-    # Setting buy rate should "guess" close rate
-    assert trade.fee_close == fee_rate
-    assert trade.fee_close_currency is None
-    assert trade.fee_close_cost is None
-
-    fee_rate = 0.0076
-    trade.update_fee(fee_cost, fee_currency, fee_rate, 'sell')
-    assert trade.fee_updated('buy')
-    assert trade.fee_updated('sell')
-    assert trade.fee_close == 0.0076
-    assert trade.fee_close_cost == fee_cost
-    assert trade.fee_close == fee_rate
-
-
-def test_fee_updated(fee):
-    trade = Trade(
-        pair='ADA/USDT',
-        stake_amount=30.0,
-        fee_open=fee.return_value,
-        open_date=arrow.utcnow().shift(hours=-2).datetime,
-        amount=30.0,
-        fee_close=fee.return_value,
-        exchange='binance',
-        open_rate=1,
-        max_rate=1,
-    )
-
-    assert trade.fee_open_currency is None
-    assert not trade.fee_updated('buy')
-    assert not trade.fee_updated('sell')
-    assert not trade.fee_updated('asdf')
-
-    trade.update_fee(0.15, 'BTC', 0.0075, 'buy')
-    assert trade.fee_updated('buy')
-    assert not trade.fee_updated('sell')
-    assert trade.fee_open_currency is not None
-    assert trade.fee_close_currency is None
-
-    trade.update_fee(0.15, 'ABC', 0.0075, 'sell')
-    assert trade.fee_updated('buy')
-    assert trade.fee_updated('sell')
-    assert not trade.fee_updated('asfd')
-
-
-@pytest.mark.usefixtures("init_persistence")
-@pytest.mark.parametrize('is_short', [True, False])
-@pytest.mark.parametrize('use_db', [True, False])
-def test_total_open_trades_stakes(fee, is_short, use_db):
-
-    Trade.use_db = use_db
-    Trade.reset_trades()
-    res = Trade.total_open_trades_stakes()
-    assert res == 0
-    create_mock_trades(fee, is_short, use_db)
-    res = Trade.total_open_trades_stakes()
-    assert res == 0.004
-
-    Trade.use_db = True
-
-
-@pytest.mark.usefixtures("init_persistence")
-@pytest.mark.parametrize('is_short,result', [
-    (True, -0.006739127),
-    (False, 0.000739127),
-    (None, -0.005429127),
-])
-@pytest.mark.parametrize('use_db', [True, False])
-def test_get_total_closed_profit(fee, use_db, is_short, result):
-
-    Trade.use_db = use_db
-    Trade.reset_trades()
-    res = Trade.get_total_closed_profit()
-    assert res == 0
-    create_mock_trades(fee, is_short, use_db)
-    res = Trade.get_total_closed_profit()
-    assert pytest.approx(res) == result
-
-    Trade.use_db = True
-
-
-@pytest.mark.usefixtures("init_persistence")
-@pytest.mark.parametrize('is_short', [True, False])
-@pytest.mark.parametrize('use_db', [True, False])
-def test_get_trades_proxy(fee, use_db, is_short):
-    Trade.use_db = use_db
-    Trade.reset_trades()
-    create_mock_trades(fee, is_short, use_db)
-    trades = Trade.get_trades_proxy()
-    assert len(trades) == 6
-
-    assert isinstance(trades[0], Trade)
-
-    trades = Trade.get_trades_proxy(is_open=True)
-    assert len(trades) == 4
-    assert trades[0].is_open
-    trades = Trade.get_trades_proxy(is_open=False)
-
-    assert len(trades) == 2
-    assert not trades[0].is_open
-
-    opendate = datetime.now(tz=timezone.utc) - timedelta(minutes=15)
-
-    assert len(Trade.get_trades_proxy(open_date=opendate)) == 3
-
-    Trade.use_db = True
-
-
-def test_get_trades_backtest():
-    Trade.use_db = False
-    with pytest.raises(NotImplementedError, match=r"`Trade.get_trades\(\)` not .*"):
-        Trade.get_trades([])
-    Trade.use_db = True
-
-
-@pytest.mark.usefixtures("init_persistence")
-# @pytest.mark.parametrize('is_short', [True, False])
-def test_get_overall_performance(fee):
-
-    create_mock_trades(fee, False)
-    res = Trade.get_overall_performance()
-
-    assert len(res) == 2
-    assert 'pair' in res[0]
-    assert 'profit' in res[0]
-    assert 'count' in res[0]
-
-
-@pytest.mark.usefixtures("init_persistence")
-@pytest.mark.parametrize('is_short,pair,profit', [
-    (True, 'ETC/BTC', -0.005),
-    (False, 'XRP/BTC', 0.01),
-    (None, 'XRP/BTC', 0.01),
-])
-def test_get_best_pair(fee, is_short, pair, profit):
-
-    res = Trade.get_best_pair()
-    assert res is None
-
-    create_mock_trades(fee, is_short)
-    res = Trade.get_best_pair()
-    assert len(res) == 2
-    assert res[0] == pair
-    assert res[1] == profit
-
-
-@pytest.mark.usefixtures("init_persistence")
-def test_get_best_pair_lev(fee):
-
-    res = Trade.get_best_pair()
-    assert res is None
-
-    create_mock_trades_with_leverage(fee)
-    res = Trade.get_best_pair()
-    assert len(res) == 2
-    assert res[0] == 'DOGE/BTC'
-    assert res[1] == 0.1713156134055116
-
-
-@pytest.mark.usefixtures("init_persistence")
-@pytest.mark.parametrize('is_short', [True, False])
-def test_get_exit_order_count(fee, is_short):
-
-    create_mock_trades(fee, is_short=is_short)
-    trade = Trade.get_trades([Trade.pair == 'ETC/BTC']).first()
-    assert trade.get_exit_order_count() == 1
-
-
-@pytest.mark.usefixtures("init_persistence")
-def test_update_order_from_ccxt(caplog):
-    # Most basic order return (only has orderid)
-    o = Order.parse_from_ccxt_object({'id': '1234'}, 'ADA/USDT', 'buy')
-    assert isinstance(o, Order)
-    assert o.ft_pair == 'ADA/USDT'
-    assert o.ft_order_side == 'buy'
-    assert o.order_id == '1234'
-    assert o.ft_is_open
-    ccxt_order = {
-        'id': '1234',
-        'side': 'buy',
-        'symbol': 'ADA/USDT',
-        'type': 'limit',
-        'price': 1234.5,
-        'amount': 20.0,
-        'filled': 9,
-        'remaining': 11,
-        'status': 'open',
-        'timestamp': 1599394315123
-    }
-    o = Order.parse_from_ccxt_object(ccxt_order, 'ADA/USDT', 'buy')
-    assert isinstance(o, Order)
-    assert o.ft_pair == 'ADA/USDT'
-    assert o.ft_order_side == 'buy'
-    assert o.order_id == '1234'
-    assert o.order_type == 'limit'
-    assert o.price == 1234.5
-    assert o.filled == 9
-    assert o.remaining == 11
-    assert o.order_date is not None
-    assert o.ft_is_open
-    assert o.order_filled_date is None
-
-    # Order is unfilled, "filled" not set
-    # https://github.com/freqtrade/freqtrade/issues/5404
-    ccxt_order.update({'filled': None, 'remaining': 20.0, 'status': 'canceled'})
-    o.update_from_ccxt_object(ccxt_order)
-
-    # Order has been closed
-    ccxt_order.update({'filled': 20.0, 'remaining': 0.0, 'status': 'closed'})
-    o.update_from_ccxt_object(ccxt_order)
-
-    assert o.filled == 20.0
-    assert o.remaining == 0.0
-    assert not o.ft_is_open
-    assert o.order_filled_date is not None
-
-    ccxt_order.update({'id': 'somethingelse'})
-    with pytest.raises(DependencyException, match=r"Order-id's don't match"):
-        o.update_from_ccxt_object(ccxt_order)
-
-    message = "aaaa is not a valid response object."
-    assert not log_has(message, caplog)
-    Order.update_orders([o], 'aaaa')
-    assert log_has(message, caplog)
-
-    # Call regular update - shouldn't fail.
-    Order.update_orders([o], {'id': '1234'})
-
-
-@pytest.mark.usefixtures("init_persistence")
-@pytest.mark.parametrize('is_short', [True, False])
-def test_select_order(fee, is_short):
-    create_mock_trades(fee, is_short)
-
-    trades = Trade.get_trades().all()
-
-    # Open buy order, no sell order
-    order = trades[0].select_order(trades[0].enter_side, True)
-    assert order is None
-    order = trades[0].select_order(trades[0].enter_side, False)
-    assert order is not None
-    order = trades[0].select_order(trades[0].exit_side, None)
-    assert order is None
-
-    # closed buy order, and open sell order
-    order = trades[1].select_order(trades[1].enter_side, True)
-    assert order is None
-    order = trades[1].select_order(trades[1].enter_side, False)
-    assert order is not None
-    order = trades[1].select_order(trades[1].enter_side, None)
-    assert order is not None
-    order = trades[1].select_order(trades[1].exit_side, True)
-    assert order is None
-    order = trades[1].select_order(trades[1].exit_side, False)
-    assert order is not None
-
-    # Has open buy order
-    order = trades[3].select_order(trades[3].enter_side, True)
-    assert order is not None
-    order = trades[3].select_order(trades[3].enter_side, False)
-    assert order is None
-
-    # Open sell order
-    order = trades[4].select_order(trades[4].enter_side, True)
-    assert order is None
-    order = trades[4].select_order(trades[4].enter_side, False)
-    assert order is not None
-
-    trades[4].orders[1].ft_order_side = trades[4].exit_side
-    order = trades[4].select_order(trades[4].exit_side, True)
-    assert order is not None
-
-    trades[4].orders[1].ft_order_side = 'stoploss'
-    order = trades[4].select_order('stoploss', None)
-    assert order is not None
-    assert order.ft_order_side == 'stoploss'
-
-
-def test_Trade_object_idem():
-
-    assert issubclass(Trade, LocalTrade)
-
-    trade = vars(Trade)
-    localtrade = vars(LocalTrade)
-
-    excludes = (
-        'delete',
-        'session',
-        'commit',
-        'query',
-        'open_date',
-        'get_best_pair',
-        'get_overall_performance',
-        'get_total_closed_profit',
-        'total_open_trades_stakes',
-        'get_closed_trades_without_assigned_fees',
-        'get_open_trades_without_assigned_fees',
-        'get_open_order_trades',
-        'get_trades',
-        'get_exit_reason_performance',
-        'get_enter_tag_performance',
-        'get_mix_tag_performance',
-
-    )
-
-    # Parent (LocalTrade) should have the same attributes
-    for item in trade:
-        # Exclude private attributes and open_date (as it's not assigned a default)
-        if (not item.startswith('_') and item not in excludes):
-            assert item in localtrade
-
-    # Fails if only a column is added without corresponding parent field
-    for item in localtrade:
-        if (not item.startswith('__')
-                and item not in ('trades', 'trades_open', 'total_profit')
-                and type(getattr(LocalTrade, item)) not in (property, FunctionType)):
-            assert item in trade
-
-
-def test_recalc_trade_from_orders(fee):
-
-    o1_amount = 100
-    o1_rate = 1
-    o1_cost = o1_amount * o1_rate
-    o1_fee_cost = o1_cost * fee.return_value
-    o1_trade_val = o1_cost + o1_fee_cost
-
-    trade = Trade(
-        pair='ADA/USDT',
-        stake_amount=o1_cost,
-        open_date=arrow.utcnow().shift(hours=-2).datetime,
-        amount=o1_amount,
-        fee_open=fee.return_value,
-        fee_close=fee.return_value,
-        exchange='binance',
-        open_rate=o1_rate,
-        max_rate=o1_rate,
-        leverage=1,
-    )
-
-    assert fee.return_value == 0.0025
-    assert trade._calc_open_trade_value() == o1_trade_val
-    assert trade.amount == o1_amount
-    assert trade.stake_amount == o1_cost
-    assert trade.open_rate == o1_rate
-    assert trade.open_trade_value == o1_trade_val
-
-    # Calling without orders should not throw exceptions and change nothing
-    trade.recalc_trade_from_orders()
-    assert trade.amount == o1_amount
-    assert trade.stake_amount == o1_cost
-    assert trade.open_rate == o1_rate
-    assert trade.open_trade_value == o1_trade_val
-
-    trade.update_fee(o1_fee_cost, 'BNB', fee.return_value, 'buy')
-
-    assert len(trade.orders) == 0
-
-    # Check with 1 order
-    order1 = Order(
-        ft_order_side='buy',
-        ft_pair=trade.pair,
-        ft_is_open=False,
-        status="closed",
-        symbol=trade.pair,
-        order_type="market",
-        side="buy",
-        price=o1_rate,
-        average=o1_rate,
-        filled=o1_amount,
-        remaining=0,
-        cost=o1_amount,
-        order_date=trade.open_date,
-        order_filled_date=trade.open_date,
-    )
-    trade.orders.append(order1)
-    trade.recalc_trade_from_orders()
-
-    # Calling recalc with single initial order should not change anything
-    assert trade.amount == o1_amount
-    assert trade.stake_amount == o1_amount
-    assert trade.open_rate == o1_rate
-    assert trade.fee_open_cost == o1_fee_cost
-    assert trade.open_trade_value == o1_trade_val
-
-    # One additional adjustment / DCA order
-    o2_amount = 125
-    o2_rate = 0.9
-    o2_cost = o2_amount * o2_rate
-    o2_fee_cost = o2_cost * fee.return_value
-    o2_trade_val = o2_cost + o2_fee_cost
-
-    order2 = Order(
-        ft_order_side='buy',
-        ft_pair=trade.pair,
-        ft_is_open=False,
-        status="closed",
-        symbol=trade.pair,
-        order_type="market",
-        side="buy",
-        price=o2_rate,
-        average=o2_rate,
-        filled=o2_amount,
-        remaining=0,
-        cost=o2_cost,
-        order_date=arrow.utcnow().shift(hours=-1).datetime,
-        order_filled_date=arrow.utcnow().shift(hours=-1).datetime,
-    )
-    trade.orders.append(order2)
-    trade.recalc_trade_from_orders()
-
-    # Validate that the trade now has new averaged open price and total values
-    avg_price = (o1_cost + o2_cost) / (o1_amount + o2_amount)
-    assert trade.amount == o1_amount + o2_amount
-    assert trade.stake_amount == o1_amount + o2_cost
-    assert trade.open_rate == avg_price
-    assert trade.fee_open_cost == o1_fee_cost + o2_fee_cost
-    assert trade.open_trade_value == o1_trade_val + o2_trade_val
-
-    # Let's try with multiple additional orders
-    o3_amount = 150
-    o3_rate = 0.85
-    o3_cost = o3_amount * o3_rate
-    o3_fee_cost = o3_cost * fee.return_value
-    o3_trade_val = o3_cost + o3_fee_cost
-
-    order3 = Order(
-        ft_order_side='buy',
-        ft_pair=trade.pair,
-        ft_is_open=False,
-        status="closed",
-        symbol=trade.pair,
-        order_type="market",
-        side="buy",
-        price=o3_rate,
-        average=o3_rate,
-        filled=o3_amount,
-        remaining=0,
-        cost=o3_cost,
-        order_date=arrow.utcnow().shift(hours=-1).datetime,
-        order_filled_date=arrow.utcnow().shift(hours=-1).datetime,
-    )
-    trade.orders.append(order3)
-    trade.recalc_trade_from_orders()
-
-    # Validate that the sum is still correct and open rate is averaged
-    avg_price = (o1_cost + o2_cost + o3_cost) / (o1_amount + o2_amount + o3_amount)
-    assert trade.amount == o1_amount + o2_amount + o3_amount
-    assert trade.stake_amount == o1_cost + o2_cost + o3_cost
-    assert trade.open_rate == avg_price
-    assert pytest.approx(trade.fee_open_cost) == o1_fee_cost + o2_fee_cost + o3_fee_cost
-    assert pytest.approx(trade.open_trade_value) == o1_trade_val + o2_trade_val + o3_trade_val
-
-    # Just to make sure non partial sell orders are ignored, let's calculate one more time.
-
-    sell1 = Order(
-        ft_order_side='sell',
-        ft_pair=trade.pair,
-        ft_is_open=False,
-        status="closed",
-        symbol=trade.pair,
-        order_type="market",
-        side="sell",
-        price=avg_price + 0.95,
-        average=avg_price + 0.95,
-        filled=o1_amount + o2_amount + o3_amount,
-        remaining=0,
-        cost=o1_cost + o2_cost + o3_cost,
-        order_date=trade.open_date,
-        order_filled_date=trade.open_date,
-    )
-    trade.orders.append(sell1)
-    trade.recalc_trade_from_orders()
-
-    assert trade.amount == o1_amount + o2_amount + o3_amount
-    assert trade.stake_amount == o1_cost + o2_cost + o3_cost
-    assert trade.open_rate == avg_price
-    assert pytest.approx(trade.fee_open_cost) == o1_fee_cost + o2_fee_cost + o3_fee_cost
-    assert pytest.approx(trade.open_trade_value) == o1_trade_val + o2_trade_val + o3_trade_val
-
-
-@pytest.mark.parametrize('is_short', [True, False])
-def test_recalc_trade_from_orders_ignores_bad_orders(fee, is_short):
-
-    o1_amount = 100
-    o1_rate = 1
-    o1_cost = o1_amount * o1_rate
-    o1_fee_cost = o1_cost * fee.return_value
-    o1_trade_val = o1_cost - o1_fee_cost if is_short else o1_cost + o1_fee_cost
-    enter_side = "sell" if is_short else "buy"
-    exit_side = "buy" if is_short else "sell"
-
-    trade = Trade(
-        pair='ADA/USDT',
-        stake_amount=o1_cost,
-        open_date=arrow.utcnow().shift(hours=-2).datetime,
-        amount=o1_amount,
-        fee_open=fee.return_value,
-        fee_close=fee.return_value,
-        exchange='binance',
-        open_rate=o1_rate,
-        max_rate=o1_rate,
-        is_short=is_short,
-        leverage=1.0,
-    )
-    trade.update_fee(o1_fee_cost, 'BNB', fee.return_value, enter_side)
-    # Check with 1 order
-    order1 = Order(
-        ft_order_side=enter_side,
-        ft_pair=trade.pair,
-        ft_is_open=False,
-        status="closed",
-        symbol=trade.pair,
-        order_type="market",
-        side=enter_side,
-        price=o1_rate,
-        average=o1_rate,
-        filled=o1_amount,
-        remaining=0,
-        cost=o1_amount,
-        order_date=trade.open_date,
-        order_filled_date=trade.open_date,
-    )
-    trade.orders.append(order1)
-    trade.recalc_trade_from_orders()
-
-    # Calling recalc with single initial order should not change anything
-    assert trade.amount == o1_amount
-    assert trade.stake_amount == o1_amount
-    assert trade.open_rate == o1_rate
-    assert trade.fee_open_cost == o1_fee_cost
-    assert trade.open_trade_value == o1_trade_val
-    assert trade.nr_of_successful_entries == 1
-
-    order2 = Order(
-        ft_order_side=enter_side,
-        ft_pair=trade.pair,
-        ft_is_open=True,
-        status="open",
-        symbol=trade.pair,
-        order_type="market",
-        side=enter_side,
-        price=o1_rate,
-        average=o1_rate,
-        filled=o1_amount,
-        remaining=0,
-        cost=o1_cost,
-        order_date=arrow.utcnow().shift(hours=-1).datetime,
-        order_filled_date=arrow.utcnow().shift(hours=-1).datetime,
-    )
-    trade.orders.append(order2)
-    trade.recalc_trade_from_orders()
-
-    # Validate that the trade values have not been changed
-    assert trade.amount == o1_amount
-    assert trade.stake_amount == o1_amount
-    assert trade.open_rate == o1_rate
-    assert trade.fee_open_cost == o1_fee_cost
-    assert trade.open_trade_value == o1_trade_val
-    assert trade.nr_of_successful_entries == 1
-
-    # Let's try with some other orders
-    order3 = Order(
-        ft_order_side=enter_side,
-        ft_pair=trade.pair,
-        ft_is_open=False,
-        status="cancelled",
-        symbol=trade.pair,
-        order_type="market",
-        side=enter_side,
-        price=1,
-        average=2,
-        filled=0,
-        remaining=4,
-        cost=5,
-        order_date=arrow.utcnow().shift(hours=-1).datetime,
-        order_filled_date=arrow.utcnow().shift(hours=-1).datetime,
-    )
-    trade.orders.append(order3)
-    trade.recalc_trade_from_orders()
-
-    # Validate that the order values still are ignoring orders 2 and 3
-    assert trade.amount == o1_amount
-    assert trade.stake_amount == o1_amount
-    assert trade.open_rate == o1_rate
-    assert trade.fee_open_cost == o1_fee_cost
-    assert trade.open_trade_value == o1_trade_val
-    assert trade.nr_of_successful_entries == 1
-
-    order4 = Order(
-        ft_order_side=enter_side,
-        ft_pair=trade.pair,
-        ft_is_open=False,
-        status="closed",
-        symbol=trade.pair,
-        order_type="market",
-        side=enter_side,
-        price=o1_rate,
-        average=o1_rate,
-        filled=o1_amount,
-        remaining=0,
-        cost=o1_cost,
-        order_date=arrow.utcnow().shift(hours=-1).datetime,
-        order_filled_date=arrow.utcnow().shift(hours=-1).datetime,
-    )
-    trade.orders.append(order4)
-    trade.recalc_trade_from_orders()
-
-    # Validate that the trade values have been changed
-    assert trade.amount == 2 * o1_amount
-    assert trade.stake_amount == 2 * o1_amount
-    assert trade.open_rate == o1_rate
-    assert trade.fee_open_cost == 2 * o1_fee_cost
-    assert trade.open_trade_value == 2 * o1_trade_val
-    assert trade.nr_of_successful_entries == 2
-
-<<<<<<< HEAD
-    # Just to make sure non partial sell orders are ignored, let's calculate one more time.
-=======
-    # Just to make sure exit orders are ignored, let's calculate one more time.
->>>>>>> develop
-    sell1 = Order(
-        ft_order_side=exit_side,
-        ft_pair=trade.pair,
-        ft_is_open=False,
-        status="closed",
-        symbol=trade.pair,
-        order_type="market",
-        side=exit_side,
-        price=4,
-        average=3,
-        filled=o1_amount,
-        remaining=1,
-        cost=5,
-        order_date=trade.open_date,
-        order_filled_date=trade.open_date,
-    )
-    trade.orders.append(sell1)
-    trade.recalc_trade_from_orders()
-
-    assert trade.amount == o1_amount
-    assert trade.stake_amount == o1_amount
-    assert trade.open_rate == o1_rate
-<<<<<<< HEAD
-    assert trade.fee_open_cost == o1_fee_cost
-    assert trade.open_trade_value == o1_trade_val
-    assert trade.nr_of_successful_buys == 2
-=======
-    assert trade.fee_open_cost == 2 * o1_fee_cost
-    assert trade.open_trade_value == 2 * o1_trade_val
-    assert trade.nr_of_successful_entries == 2
->>>>>>> develop
-
-    # Check with 1 order
-    order_noavg = Order(
-        ft_order_side=enter_side,
-        ft_pair=trade.pair,
-        ft_is_open=False,
-        status="closed",
-        symbol=trade.pair,
-        order_type="market",
-        side=enter_side,
-        price=o1_rate,
-        average=None,
-        filled=o1_amount,
-        remaining=0,
-        cost=o1_amount,
-        order_date=trade.open_date,
-        order_filled_date=trade.open_date,
-    )
-    trade.orders.append(order_noavg)
-    trade.recalc_trade_from_orders()
-
-    # Calling recalc with single initial order should not change anything
-    assert trade.amount == 2 * o1_amount
-    assert trade.stake_amount == 2 * o1_amount
-    assert trade.open_rate == o1_rate
-<<<<<<< HEAD
-    assert trade.fee_open_cost == 2 * o1_fee_cost
-    assert trade.open_trade_value == 2 * o1_trade_val
-    assert trade.nr_of_successful_buys == 3
-=======
-    assert trade.fee_open_cost == 3 * o1_fee_cost
-    assert trade.open_trade_value == 3 * o1_trade_val
-    assert trade.nr_of_successful_entries == 3
->>>>>>> develop
-
-
-@pytest.mark.usefixtures("init_persistence")
-def test_select_filled_orders(fee):
-    create_mock_trades(fee)
-
-    trades = Trade.get_trades().all()
-
-    # Closed buy order, no sell order
-    orders = trades[0].select_filled_orders('buy')
-    assert orders is not None
-    assert len(orders) == 1
-    order = orders[0]
-    assert order.amount > 0
-    assert order.filled > 0
-    assert order.side == 'buy'
-    assert order.ft_order_side == 'buy'
-    assert order.status == 'closed'
-    orders = trades[0].select_filled_orders('sell')
-    assert orders is not None
-    assert len(orders) == 0
-
-    # closed buy order, and closed sell order
-    orders = trades[1].select_filled_orders('buy')
-    assert orders is not None
-    assert len(orders) == 1
-
-    orders = trades[1].select_filled_orders('sell')
-    assert orders is not None
-    assert len(orders) == 1
-
-    # Has open buy order
-    orders = trades[3].select_filled_orders('buy')
-    assert orders is not None
-    assert len(orders) == 0
-    orders = trades[3].select_filled_orders('sell')
-    assert orders is not None
-    assert len(orders) == 0
-
-    # Open sell order
-    orders = trades[4].select_filled_orders('buy')
-    assert orders is not None
-    assert len(orders) == 1
-    orders = trades[4].select_filled_orders('sell')
-    assert orders is not None
-    assert len(orders) == 0