diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index 9a96e1f19..5680a7b47 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -55,7 +55,10 @@ class Binance(Exchange): :param side: "buy" or "sell" """ # Limit price threshold: As limit price should always be below stop-price - limit_price_pct = order_types.get('stoploss_on_exchange_limit_ratio', 0.99) + limit_price_pct = order_types.get( + 'stoploss_on_exchange_limit_ratio', + 0.99 if side == 'sell' else 1.01 + ) rate = stop_price * limit_price_pct ordertype = "stop_loss_limit" diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 6e25689f3..b9da0cf7c 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -75,8 +75,6 @@ class Exchange: } _ft_has: Dict = {} - _leverage_brackets: Dict = {} - _supported_trading_mode_collateral_pairs: List[Tuple[TradingMode, Collateral]] = [ # TradingMode.SPOT always supported and not required in this list ] @@ -90,6 +88,7 @@ class Exchange: self._api: ccxt.Exchange = None self._api_async: ccxt_async.Exchange = None self._markets: Dict = {} + self._leverage_brackets: Dict = {} self._config.update(config) @@ -624,6 +623,7 @@ class Exchange: def _apply_leverage_to_stake_amount(self, stake_amount: float, leverage: float): """ + #TODO-lev: Find out how this works on Kraken and FTX # * Should be implemented by child classes if leverage affects the stake_amount Takes the minimum stake amount for a pair with no leverage and returns the minimum stake amount when leverage is considered @@ -1581,31 +1581,6 @@ class Exchange: self._async_get_trade_history(pair=pair, since=since, until=until, from_id=from_id)) - @retrier - def get_interest_rate( - self, - pair: str, - maker_or_taker: str, - is_short: bool - ) -> Tuple[float, float]: - """ - Gets the rate of interest for borrowed currency when margin trading - :param pair: base/quote currency pair - :param maker_or_taker: "maker" if limit order, "taker" if market order - :param is_short: True if requesting base interest, False if requesting quote interest - :return: (open_interest, rollover_interest) - """ - try: - # TODO-lev: implement, currently there is no ccxt method for this - return (0.0005, 0.0005) - except ccxt.DDoSProtection as e: - raise DDosProtection(e) from e - except (ccxt.NetworkError, ccxt.ExchangeError) as e: - raise TemporaryError( - f'Could not set leverage due to {e.__class__.__name__}. Message: {e}') from e - except ccxt.BaseError as e: - raise OperationalException(e) from e - @retrier def fill_leverage_brackets(self): """ diff --git a/freqtrade/exchange/ftx.py b/freqtrade/exchange/ftx.py index 3e6ff01a3..870791cf5 100644 --- a/freqtrade/exchange/ftx.py +++ b/freqtrade/exchange/ftx.py @@ -57,7 +57,10 @@ class Ftx(Exchange): Limit orders are defined by having orderPrice set, otherwise a market order is used. """ - limit_price_pct = order_types.get('stoploss_on_exchange_limit_ratio', 0.99) + limit_price_pct = order_types.get( + 'stoploss_on_exchange_limit_ratio', + 0.99 if side == "sell" else 1.01 + ) limit_rate = stop_price * limit_price_pct ordertype = "stop" @@ -164,10 +167,6 @@ class Ftx(Exchange): return safe_value_fallback2(order, order, 'id_stop', 'id') return order['id'] - def _apply_leverage_to_stake_amount(self, stake_amount: float, leverage: float): - # TODO-lev: implement - return stake_amount - def fill_leverage_brackets(self): """ FTX leverage is static across the account, and doesn't change from pair to pair, diff --git a/freqtrade/exchange/kraken.py b/freqtrade/exchange/kraken.py index 5207018ad..69d0fb512 100644 --- a/freqtrade/exchange/kraken.py +++ b/freqtrade/exchange/kraken.py @@ -84,7 +84,7 @@ class Kraken(Exchange): (side == "buy" and stop_loss < float(order['price'])) )) - @ retrier(retries=0) + @retrier(retries=0) def stoploss(self, pair: str, amount: float, stop_price: float, order_types: Dict, side: str) -> Dict: """ diff --git a/tests/exchange/test_binance.py b/tests/exchange/test_binance.py index f2bd68154..ad55ede9b 100644 --- a/tests/exchange/test_binance.py +++ b/tests/exchange/test_binance.py @@ -9,12 +9,22 @@ from tests.conftest import get_patched_exchange from tests.exchange.test_exchange import ccxt_exceptionhandlers -@pytest.mark.parametrize('limitratio,expected', [ - (None, 220 * 0.99), - (0.99, 220 * 0.99), - (0.98, 220 * 0.98), +@pytest.mark.parametrize('limitratio,exchangelimitratio,expected,side', [ + (None, 1.05, 220 * 0.99, "sell"), + (0.99, 1.05, 220 * 0.99, "sell"), + (0.98, 1.05, 220 * 0.98, "sell"), + (None, 0.95, 220 * 1.01, "buy"), + (1.01, 0.95, 220 * 1.01, "buy"), + (1.02, 0.95, 220 * 1.02, "buy"), ]) -def test_stoploss_order_binance(default_conf, mocker, limitratio, expected): +def test_stoploss_order_binance( + default_conf, + mocker, + limitratio, + exchangelimitratio, + expected, + side +): api_mock = MagicMock() order_id = 'test_prod_buy_{}'.format(randint(0, 10 ** 6)) order_type = 'stop_loss_limit' @@ -32,20 +42,25 @@ def test_stoploss_order_binance(default_conf, mocker, limitratio, expected): exchange = get_patched_exchange(mocker, default_conf, api_mock, 'binance') with pytest.raises(OperationalException): - order = exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=190, side="sell", - order_types={'stoploss_on_exchange_limit_ratio': 1.05}) + order = exchange.stoploss( + pair='ETH/BTC', + amount=1, + stop_price=190, + side=side, + order_types={'stoploss_on_exchange_limit_ratio': exchangelimitratio} + ) api_mock.create_order.reset_mock() order_types = {} if limitratio is None else {'stoploss_on_exchange_limit_ratio': limitratio} order = exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, - order_types=order_types, side="sell") + order_types=order_types, side=side) assert 'id' in order assert 'info' in order assert order['id'] == order_id assert api_mock.create_order.call_args_list[0][1]['symbol'] == 'ETH/BTC' assert api_mock.create_order.call_args_list[0][1]['type'] == order_type - assert api_mock.create_order.call_args_list[0][1]['side'] == 'sell' + assert api_mock.create_order.call_args_list[0][1]['side'] == side assert api_mock.create_order.call_args_list[0][1]['amount'] == 1 # Price should be 1% below stopprice assert api_mock.create_order.call_args_list[0][1]['price'] == expected @@ -55,17 +70,17 @@ def test_stoploss_order_binance(default_conf, mocker, limitratio, expected): with pytest.raises(DependencyException): api_mock.create_order = MagicMock(side_effect=ccxt.InsufficientFunds("0 balance")) exchange = get_patched_exchange(mocker, default_conf, api_mock, 'binance') - exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={}, side="sell") + exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={}, side=side) with pytest.raises(InvalidOrderException): api_mock.create_order = MagicMock( side_effect=ccxt.InvalidOrder("binance Order would trigger immediately.")) exchange = get_patched_exchange(mocker, default_conf, api_mock, 'binance') - exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={}, side="sell") + exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={}, side=side) ccxt_exceptionhandlers(mocker, default_conf, api_mock, "binance", "stoploss", "create_order", retries=1, - pair='ETH/BTC', amount=1, stop_price=220, order_types={}, side="sell") + pair='ETH/BTC', amount=1, stop_price=220, order_types={}, side=side) def test_stoploss_order_dry_run_binance(default_conf, mocker): @@ -94,18 +109,22 @@ def test_stoploss_order_dry_run_binance(default_conf, mocker): assert order['amount'] == 1 -def test_stoploss_adjust_binance(mocker, default_conf): +@pytest.mark.parametrize('sl1,sl2,sl3,side', [ + (1501, 1499, 1501, "sell"), + (1499, 1501, 1499, "buy") +]) +def test_stoploss_adjust_binance(mocker, default_conf, sl1, sl2, sl3, side): exchange = get_patched_exchange(mocker, default_conf, id='binance') order = { 'type': 'stop_loss_limit', 'price': 1500, 'info': {'stopPrice': 1500}, } - assert exchange.stoploss_adjust(1501, order, side="sell") - assert not exchange.stoploss_adjust(1499, order, side="sell") + assert exchange.stoploss_adjust(sl1, order, side=side) + assert not exchange.stoploss_adjust(sl2, order, side=side) # Test with invalid order case order['type'] = 'stop_loss' - assert not exchange.stoploss_adjust(1501, order, side="sell") + assert not exchange.stoploss_adjust(sl3, order, side=side) @pytest.mark.parametrize('pair,nominal_value,max_lev', [ diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 348aa3290..239704bdd 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -2994,73 +2994,6 @@ def test_apply_leverage_to_stake_amount( assert exchange._apply_leverage_to_stake_amount(stake_amount, leverage) == min_stake_with_lev -def test_fill_leverage_brackets(): - return - - -# TODO-lev: These tests don't test anything real, they need to be replaced with real values once -# get_interest_rates is written -@pytest.mark.parametrize('exchange_name,pair,maker_or_taker,is_short,borrow_rate,interest_rate', [ - ('binance', "ADA/USDT", "maker", True, 0.0005, 0.0005), - ('binance', "ADA/USDT", "maker", False, 0.0005, 0.0005), - ('binance', "ADA/USDT", "taker", True, 0.0005, 0.0005), - ('binance', "ADA/USDT", "taker", False, 0.0005, 0.0005), - # Kraken - ('kraken', "ADA/USDT", "maker", True, 0.0005, 0.0005), - ('kraken', "ADA/USDT", "maker", False, 0.0005, 0.0005), - ('kraken', "ADA/USDT", "taker", True, 0.0005, 0.0005), - ('kraken', "ADA/USDT", "taker", False, 0.0005, 0.0005), - # FTX - ('ftx', "ADA/USDT", "maker", True, 0.0005, 0.0005), - ('ftx', "ADA/USDT", "maker", False, 0.0005, 0.0005), - ('ftx', "ADA/USDT", "taker", True, 0.0005, 0.0005), - ('ftx', "ADA/USDT", "taker", False, 0.0005, 0.0005), -]) -def test_get_interest_rate( - default_conf, - mocker, - exchange_name, - pair, - maker_or_taker, - is_short, - borrow_rate, - interest_rate -): - exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) - assert exchange.get_interest_rate( - pair, maker_or_taker, is_short) == (borrow_rate, interest_rate) - - -@pytest.mark.parametrize("exchange_name", [("binance"), ("ftx"), ("kraken")]) -@pytest.mark.parametrize("maker_or_taker", [("maker"), ("taker")]) -@pytest.mark.parametrize("is_short", [(True), (False)]) -def test_get_interest_rate_exceptions( - mocker, - default_conf, - exchange_name, - maker_or_taker, - is_short -): - - # api_mock = MagicMock() - # # TODO-lev: get_interest_rate currently not implemented on CCXT, so this may be renamed - # api_mock.get_interest_rate = MagicMock() - # type(api_mock).has = PropertyMock(return_value={'getInterestRate': True}) - - # ccxt_exceptionhandlers( - # mocker, - # default_conf, - # api_mock, - # exchange_name, - # "get_interest_rate", - # "get_interest_rate", - # pair="XRP/USDT", - # is_short=is_short, - # maker_or_taker="maker_or_taker" - # ) - return - - @pytest.mark.parametrize("collateral", [ (Collateral.CROSS), (Collateral.ISOLATED) diff --git a/tests/exchange/test_ftx.py b/tests/exchange/test_ftx.py index 1ed528dd9..3fcd35e37 100644 --- a/tests/exchange/test_ftx.py +++ b/tests/exchange/test_ftx.py @@ -13,10 +13,12 @@ from .test_exchange import ccxt_exceptionhandlers STOPLOSS_ORDERTYPE = 'stop' -# TODO-lev: All these stoploss tests with shorts - -def test_stoploss_order_ftx(default_conf, mocker): +@pytest.mark.parametrize('order_price,exchangelimitratio,side', [ + (217.8, 1.05, "sell"), + (222.2, 0.95, "buy"), +]) +def test_stoploss_order_ftx(default_conf, mocker, order_price, exchangelimitratio, side): api_mock = MagicMock() order_id = 'test_prod_buy_{}'.format(randint(0, 10 ** 6)) @@ -34,12 +36,12 @@ def test_stoploss_order_ftx(default_conf, mocker): exchange = get_patched_exchange(mocker, default_conf, api_mock, 'ftx') # stoploss_on_exchange_limit_ratio is irrelevant for ftx market orders - order = exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=190, side="sell", - order_types={'stoploss_on_exchange_limit_ratio': 1.05}) + order = exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=190, side=side, + order_types={'stoploss_on_exchange_limit_ratio': exchangelimitratio}) assert api_mock.create_order.call_args_list[0][1]['symbol'] == 'ETH/BTC' assert api_mock.create_order.call_args_list[0][1]['type'] == STOPLOSS_ORDERTYPE - assert api_mock.create_order.call_args_list[0][1]['side'] == 'sell' + assert api_mock.create_order.call_args_list[0][1]['side'] == side assert api_mock.create_order.call_args_list[0][1]['amount'] == 1 assert 'orderPrice' not in api_mock.create_order.call_args_list[0][1]['params'] assert 'stopPrice' in api_mock.create_order.call_args_list[0][1]['params'] @@ -49,51 +51,52 @@ def test_stoploss_order_ftx(default_conf, mocker): api_mock.create_order.reset_mock() - order = exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={}, side="sell") + order = exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={}, side=side) assert 'id' in order assert 'info' in order assert order['id'] == order_id assert api_mock.create_order.call_args_list[0][1]['symbol'] == 'ETH/BTC' assert api_mock.create_order.call_args_list[0][1]['type'] == STOPLOSS_ORDERTYPE - assert api_mock.create_order.call_args_list[0][1]['side'] == 'sell' + assert api_mock.create_order.call_args_list[0][1]['side'] == side assert api_mock.create_order.call_args_list[0][1]['amount'] == 1 assert 'orderPrice' not in api_mock.create_order.call_args_list[0][1]['params'] assert api_mock.create_order.call_args_list[0][1]['params']['stopPrice'] == 220 api_mock.create_order.reset_mock() order = exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, - order_types={'stoploss': 'limit'}, side="sell") + order_types={'stoploss': 'limit'}, side=side) assert 'id' in order assert 'info' in order assert order['id'] == order_id assert api_mock.create_order.call_args_list[0][1]['symbol'] == 'ETH/BTC' assert api_mock.create_order.call_args_list[0][1]['type'] == STOPLOSS_ORDERTYPE - assert api_mock.create_order.call_args_list[0][1]['side'] == 'sell' + assert api_mock.create_order.call_args_list[0][1]['side'] == side assert api_mock.create_order.call_args_list[0][1]['amount'] == 1 assert 'orderPrice' in api_mock.create_order.call_args_list[0][1]['params'] - assert api_mock.create_order.call_args_list[0][1]['params']['orderPrice'] == 217.8 + assert api_mock.create_order.call_args_list[0][1]['params']['orderPrice'] == order_price assert api_mock.create_order.call_args_list[0][1]['params']['stopPrice'] == 220 # 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, 'ftx') - exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={}, side="sell") + exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={}, side=side) with pytest.raises(InvalidOrderException): api_mock.create_order = MagicMock( side_effect=ccxt.InvalidOrder("ftx Order would trigger immediately.")) exchange = get_patched_exchange(mocker, default_conf, api_mock, 'ftx') - exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={}, side="sell") + exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={}, side=side) ccxt_exceptionhandlers(mocker, default_conf, api_mock, "ftx", "stoploss", "create_order", retries=1, - pair='ETH/BTC', amount=1, stop_price=220, order_types={}, side="sell") + pair='ETH/BTC', amount=1, stop_price=220, order_types={}, side=side) -def test_stoploss_order_dry_run_ftx(default_conf, mocker): +@pytest.mark.parametrize('side', [("sell"), ("buy")]) +def test_stoploss_order_dry_run_ftx(default_conf, mocker, side): api_mock = MagicMock() default_conf['dry_run'] = True mocker.patch('freqtrade.exchange.Exchange.amount_to_precision', lambda s, x, y: y) @@ -103,7 +106,7 @@ def test_stoploss_order_dry_run_ftx(default_conf, mocker): api_mock.create_order.reset_mock() - order = exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={}, side="sell") + order = exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={}, side=side) assert 'id' in order assert 'info' in order @@ -114,20 +117,24 @@ def test_stoploss_order_dry_run_ftx(default_conf, mocker): assert order['amount'] == 1 -def test_stoploss_adjust_ftx(mocker, default_conf): +@pytest.mark.parametrize('sl1,sl2,sl3,side', [ + (1501, 1499, 1501, "sell"), + (1499, 1501, 1499, "buy") +]) +def test_stoploss_adjust_ftx(mocker, default_conf, sl1, sl2, sl3, side): exchange = get_patched_exchange(mocker, default_conf, id='ftx') order = { 'type': STOPLOSS_ORDERTYPE, 'price': 1500, } - assert exchange.stoploss_adjust(1501, order, side="sell") - assert not exchange.stoploss_adjust(1499, order, side="sell") + assert exchange.stoploss_adjust(sl1, order, side=side) + assert not exchange.stoploss_adjust(sl2, order, side=side) # Test with invalid order case ... order['type'] = 'stop_loss_limit' - assert not exchange.stoploss_adjust(1501, order, side="sell") + assert not exchange.stoploss_adjust(sl3, order, side=side) -def test_fetch_stoploss_order(default_conf, mocker, limit_sell_order): +def test_fetch_stoploss_order(default_conf, mocker, limit_sell_order, limit_buy_order): default_conf['dry_run'] = True order = MagicMock() order.myid = 123 @@ -160,6 +167,16 @@ def test_fetch_stoploss_order(default_conf, mocker, limit_sell_order): assert resp['type'] == 'stop' assert resp['status_stop'] == 'triggered' + api_mock.fetch_order = MagicMock(return_value=limit_buy_order) + + resp = exchange.fetch_stoploss_order('X', 'TKN/BTC') + assert resp + assert api_mock.fetch_order.call_count == 1 + assert resp['id_stop'] == 'mocked_limit_buy' + assert resp['id'] == 'X' + assert resp['type'] == 'stop' + assert resp['status_stop'] == 'triggered' + with pytest.raises(InvalidOrderException): api_mock.fetch_orders = MagicMock(side_effect=ccxt.InvalidOrder("Order not found")) exchange = get_patched_exchange(mocker, default_conf, api_mock, id='ftx') diff --git a/tests/exchange/test_kraken.py b/tests/exchange/test_kraken.py index 8222f5ce8..39f61028b 100644 --- a/tests/exchange/test_kraken.py +++ b/tests/exchange/test_kraken.py @@ -168,7 +168,11 @@ def test_get_balances_prod(default_conf, mocker): @pytest.mark.parametrize('ordertype', ['market', 'limit']) -def test_stoploss_order_kraken(default_conf, mocker, ordertype): +@pytest.mark.parametrize('side,limitratio,adjustedprice', [ + ("buy", 0.99, 217.8), + ("sell", 1.01, 222.2), +]) +def test_stoploss_order_kraken(default_conf, mocker, ordertype, side, limitratio, adjustedprice): api_mock = MagicMock() order_id = 'test_prod_buy_{}'.format(randint(0, 10 ** 6)) @@ -185,9 +189,9 @@ def test_stoploss_order_kraken(default_conf, mocker, ordertype): exchange = get_patched_exchange(mocker, default_conf, api_mock, 'kraken') - order = exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, side="sell", + order = exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, side=side, order_types={'stoploss': ordertype, - 'stoploss_on_exchange_limit_ratio': 0.99 + 'stoploss_on_exchange_limit_ratio': limitratio }) assert 'id' in order @@ -197,12 +201,12 @@ def test_stoploss_order_kraken(default_conf, mocker, ordertype): if ordertype == 'limit': assert api_mock.create_order.call_args_list[0][1]['type'] == STOPLOSS_LIMIT_ORDERTYPE assert api_mock.create_order.call_args_list[0][1]['params'] == { - 'trading_agreement': 'agree', 'price2': 217.8} + 'trading_agreement': 'agree', 'price2': adjustedprice} else: assert api_mock.create_order.call_args_list[0][1]['type'] == STOPLOSS_ORDERTYPE assert api_mock.create_order.call_args_list[0][1]['params'] == { 'trading_agreement': 'agree'} - assert api_mock.create_order.call_args_list[0][1]['side'] == 'sell' + assert api_mock.create_order.call_args_list[0][1]['side'] == side assert api_mock.create_order.call_args_list[0][1]['amount'] == 1 assert api_mock.create_order.call_args_list[0][1]['price'] == 220 @@ -210,20 +214,21 @@ def test_stoploss_order_kraken(default_conf, mocker, ordertype): with pytest.raises(DependencyException): api_mock.create_order = MagicMock(side_effect=ccxt.InsufficientFunds("0 balance")) exchange = get_patched_exchange(mocker, default_conf, api_mock, 'kraken') - exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={}, side="sell") + exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={}, side=side) with pytest.raises(InvalidOrderException): api_mock.create_order = MagicMock( side_effect=ccxt.InvalidOrder("kraken Order would trigger immediately.")) exchange = get_patched_exchange(mocker, default_conf, api_mock, 'kraken') - exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={}, side="sell") + exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={}, side=side) ccxt_exceptionhandlers(mocker, default_conf, api_mock, "kraken", "stoploss", "create_order", retries=1, - pair='ETH/BTC', amount=1, stop_price=220, order_types={}, side="sell") + pair='ETH/BTC', amount=1, stop_price=220, order_types={}, side=side) -def test_stoploss_order_dry_run_kraken(default_conf, mocker): +@pytest.mark.parametrize('side', ['buy', 'sell']) +def test_stoploss_order_dry_run_kraken(default_conf, mocker, side): api_mock = MagicMock() default_conf['dry_run'] = True mocker.patch('freqtrade.exchange.Exchange.amount_to_precision', lambda s, x, y: y) @@ -233,7 +238,7 @@ def test_stoploss_order_dry_run_kraken(default_conf, mocker): api_mock.create_order.reset_mock() - order = exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={}, side="sell") + order = exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={}, side=side) assert 'id' in order assert 'info' in order @@ -244,17 +249,21 @@ def test_stoploss_order_dry_run_kraken(default_conf, mocker): assert order['amount'] == 1 -def test_stoploss_adjust_kraken(mocker, default_conf): +@pytest.mark.parametrize('sl1,sl2,sl3,side', [ + (1501, 1499, 1501, "sell"), + (1499, 1501, 1499, "buy") +]) +def test_stoploss_adjust_kraken(mocker, default_conf, sl1, sl2, sl3, side): exchange = get_patched_exchange(mocker, default_conf, id='kraken') order = { 'type': STOPLOSS_ORDERTYPE, 'price': 1500, } - assert exchange.stoploss_adjust(1501, order, side="sell") - assert not exchange.stoploss_adjust(1499, order, side="sell") + assert exchange.stoploss_adjust(sl1, order, side=side) + assert not exchange.stoploss_adjust(sl2, order, side=side) # Test with invalid order case ... order['type'] = 'stop_loss_limit' - assert not exchange.stoploss_adjust(1501, order, side="sell") + assert not exchange.stoploss_adjust(sl3, order, side=side) @pytest.mark.parametrize('pair,nominal_value,max_lev', [ diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 5dc21257d..0e92dbc84 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -1253,6 +1253,7 @@ def test_create_stoploss_order_insufficient_funds(mocker, default_conf, caplog, @pytest.mark.usefixtures("init_persistence") def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, limit_buy_order, limit_sell_order) -> None: + # TODO-lev: test for short # When trailing stoploss is set stoploss = MagicMock(return_value={'id': 13434334}) patch_RPCManager(mocker) @@ -1363,6 +1364,7 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, caplog, limit_buy_order, limit_sell_order) -> None: + # TODO-lev: test for short # When trailing stoploss is set stoploss = MagicMock(return_value={'id': 13434334}) patch_exchange(mocker) @@ -1440,6 +1442,7 @@ def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, c def test_handle_stoploss_on_exchange_custom_stop(mocker, default_conf, fee, limit_buy_order, limit_sell_order) -> None: # When trailing stoploss is set + # TODO-lev: test for short stoploss = MagicMock(return_value={'id': 13434334}) patch_RPCManager(mocker) mocker.patch.multiple( @@ -1549,7 +1552,7 @@ def test_handle_stoploss_on_exchange_custom_stop(mocker, default_conf, fee, def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog, limit_buy_order, limit_sell_order) -> None: - + # TODO-lev: test for short # When trailing stoploss is set stoploss = MagicMock(return_value={'id': 13434334}) patch_RPCManager(mocker) @@ -1732,7 +1735,7 @@ def test_exit_positions_exception(mocker, default_conf, limit_buy_order, caplog) ) n = freqtrade.exit_positions(trades) assert n == 0 - assert log_has('Unable to sell trade ETH/BTC: ', caplog) + assert log_has('Unable to exit trade ETH/BTC: ', caplog) def test_update_trade_state(mocker, default_conf, limit_buy_order, caplog) -> None: