merged with lev-exchange

This commit is contained in:
Sam Germain 2021-09-10 03:05:13 -06:00
commit d582ccd2e6
9 changed files with 112 additions and 154 deletions

View File

@ -55,7 +55,10 @@ class Binance(Exchange):
:param side: "buy" or "sell" :param side: "buy" or "sell"
""" """
# Limit price threshold: As limit price should always be below stop-price # 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 rate = stop_price * limit_price_pct
ordertype = "stop_loss_limit" ordertype = "stop_loss_limit"

View File

@ -75,8 +75,6 @@ class Exchange:
} }
_ft_has: Dict = {} _ft_has: Dict = {}
_leverage_brackets: Dict = {}
_supported_trading_mode_collateral_pairs: List[Tuple[TradingMode, Collateral]] = [ _supported_trading_mode_collateral_pairs: List[Tuple[TradingMode, Collateral]] = [
# TradingMode.SPOT always supported and not required in this list # TradingMode.SPOT always supported and not required in this list
] ]
@ -90,6 +88,7 @@ class Exchange:
self._api: ccxt.Exchange = None self._api: ccxt.Exchange = None
self._api_async: ccxt_async.Exchange = None self._api_async: ccxt_async.Exchange = None
self._markets: Dict = {} self._markets: Dict = {}
self._leverage_brackets: Dict = {}
self._config.update(config) self._config.update(config)
@ -624,6 +623,7 @@ class Exchange:
def _apply_leverage_to_stake_amount(self, stake_amount: float, leverage: float): 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 # * 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 Takes the minimum stake amount for a pair with no leverage and returns the minimum
stake amount when leverage is considered stake amount when leverage is considered
@ -1581,31 +1581,6 @@ class Exchange:
self._async_get_trade_history(pair=pair, since=since, self._async_get_trade_history(pair=pair, since=since,
until=until, from_id=from_id)) 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 @retrier
def fill_leverage_brackets(self): def fill_leverage_brackets(self):
""" """

View File

@ -57,7 +57,10 @@ class Ftx(Exchange):
Limit orders are defined by having orderPrice set, otherwise a market order is used. 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 limit_rate = stop_price * limit_price_pct
ordertype = "stop" ordertype = "stop"
@ -164,10 +167,6 @@ class Ftx(Exchange):
return safe_value_fallback2(order, order, 'id_stop', 'id') return safe_value_fallback2(order, order, 'id_stop', 'id')
return order['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): def fill_leverage_brackets(self):
""" """
FTX leverage is static across the account, and doesn't change from pair to pair, FTX leverage is static across the account, and doesn't change from pair to pair,

View File

@ -9,12 +9,22 @@ from tests.conftest import get_patched_exchange
from tests.exchange.test_exchange import ccxt_exceptionhandlers from tests.exchange.test_exchange import ccxt_exceptionhandlers
@pytest.mark.parametrize('limitratio,expected', [ @pytest.mark.parametrize('limitratio,exchangelimitratio,expected,side', [
(None, 220 * 0.99), (None, 1.05, 220 * 0.99, "sell"),
(0.99, 220 * 0.99), (0.99, 1.05, 220 * 0.99, "sell"),
(0.98, 220 * 0.98), (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() api_mock = MagicMock()
order_id = 'test_prod_buy_{}'.format(randint(0, 10 ** 6)) order_id = 'test_prod_buy_{}'.format(randint(0, 10 ** 6))
order_type = 'stop_loss_limit' 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') exchange = get_patched_exchange(mocker, default_conf, api_mock, 'binance')
with pytest.raises(OperationalException): with pytest.raises(OperationalException):
order = exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=190, side="sell", order = exchange.stoploss(
order_types={'stoploss_on_exchange_limit_ratio': 1.05}) pair='ETH/BTC',
amount=1,
stop_price=190,
side=side,
order_types={'stoploss_on_exchange_limit_ratio': exchangelimitratio}
)
api_mock.create_order.reset_mock() api_mock.create_order.reset_mock()
order_types = {} if limitratio is None else {'stoploss_on_exchange_limit_ratio': limitratio} 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 = 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 'id' in order
assert 'info' in order assert 'info' in order
assert order['id'] == order_id 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]['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]['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 assert api_mock.create_order.call_args_list[0][1]['amount'] == 1
# Price should be 1% below stopprice # Price should be 1% below stopprice
assert api_mock.create_order.call_args_list[0][1]['price'] == expected 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): with pytest.raises(DependencyException):
api_mock.create_order = MagicMock(side_effect=ccxt.InsufficientFunds("0 balance")) api_mock.create_order = MagicMock(side_effect=ccxt.InsufficientFunds("0 balance"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, 'binance') 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): with pytest.raises(InvalidOrderException):
api_mock.create_order = MagicMock( api_mock.create_order = MagicMock(
side_effect=ccxt.InvalidOrder("binance Order would trigger immediately.")) side_effect=ccxt.InvalidOrder("binance Order would trigger immediately."))
exchange = get_patched_exchange(mocker, default_conf, api_mock, 'binance') 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", ccxt_exceptionhandlers(mocker, default_conf, api_mock, "binance",
"stoploss", "create_order", retries=1, "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): 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 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') exchange = get_patched_exchange(mocker, default_conf, id='binance')
order = { order = {
'type': 'stop_loss_limit', 'type': 'stop_loss_limit',
'price': 1500, 'price': 1500,
'info': {'stopPrice': 1500}, 'info': {'stopPrice': 1500},
} }
assert exchange.stoploss_adjust(1501, order, side="sell") assert exchange.stoploss_adjust(sl1, order, side=side)
assert not exchange.stoploss_adjust(1499, order, side="sell") assert not exchange.stoploss_adjust(sl2, order, side=side)
# Test with invalid order case # Test with invalid order case
order['type'] = 'stop_loss' 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', [ @pytest.mark.parametrize('pair,nominal_value,max_lev', [

View File

@ -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 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", [ @pytest.mark.parametrize("collateral", [
(Collateral.CROSS), (Collateral.CROSS),
(Collateral.ISOLATED) (Collateral.ISOLATED)

View File

@ -13,10 +13,12 @@ from .test_exchange import ccxt_exceptionhandlers
STOPLOSS_ORDERTYPE = 'stop' STOPLOSS_ORDERTYPE = 'stop'
# TODO-lev: All these stoploss tests with shorts
@pytest.mark.parametrize('order_price,exchangelimitratio,side', [
def test_stoploss_order_ftx(default_conf, mocker): (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() api_mock = MagicMock()
order_id = 'test_prod_buy_{}'.format(randint(0, 10 ** 6)) 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') exchange = get_patched_exchange(mocker, default_conf, api_mock, 'ftx')
# stoploss_on_exchange_limit_ratio is irrelevant for ftx market orders # 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 = exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=190, side=side,
order_types={'stoploss_on_exchange_limit_ratio': 1.05}) 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]['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]['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 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 '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'] 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() 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 'id' in order
assert 'info' in order assert 'info' in order
assert order['id'] == order_id 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]['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]['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 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 '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 assert api_mock.create_order.call_args_list[0][1]['params']['stopPrice'] == 220
api_mock.create_order.reset_mock() api_mock.create_order.reset_mock()
order = exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, 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 'id' in order
assert 'info' in order assert 'info' in order
assert order['id'] == order_id 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]['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]['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 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 '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 assert api_mock.create_order.call_args_list[0][1]['params']['stopPrice'] == 220
# test exception handling # test exception handling
with pytest.raises(DependencyException): with pytest.raises(DependencyException):
api_mock.create_order = MagicMock(side_effect=ccxt.InsufficientFunds("0 balance")) api_mock.create_order = MagicMock(side_effect=ccxt.InsufficientFunds("0 balance"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, 'ftx') 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): with pytest.raises(InvalidOrderException):
api_mock.create_order = MagicMock( api_mock.create_order = MagicMock(
side_effect=ccxt.InvalidOrder("ftx Order would trigger immediately.")) side_effect=ccxt.InvalidOrder("ftx Order would trigger immediately."))
exchange = get_patched_exchange(mocker, default_conf, api_mock, 'ftx') 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", ccxt_exceptionhandlers(mocker, default_conf, api_mock, "ftx",
"stoploss", "create_order", retries=1, "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() api_mock = MagicMock()
default_conf['dry_run'] = True default_conf['dry_run'] = True
mocker.patch('freqtrade.exchange.Exchange.amount_to_precision', lambda s, x, y: y) 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() 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 'id' in order
assert 'info' in order assert 'info' in order
@ -114,20 +117,24 @@ def test_stoploss_order_dry_run_ftx(default_conf, mocker):
assert order['amount'] == 1 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') exchange = get_patched_exchange(mocker, default_conf, id='ftx')
order = { order = {
'type': STOPLOSS_ORDERTYPE, 'type': STOPLOSS_ORDERTYPE,
'price': 1500, 'price': 1500,
} }
assert exchange.stoploss_adjust(1501, order, side="sell") assert exchange.stoploss_adjust(sl1, order, side=side)
assert not exchange.stoploss_adjust(1499, order, side="sell") assert not exchange.stoploss_adjust(sl2, order, side=side)
# Test with invalid order case ... # Test with invalid order case ...
order['type'] = 'stop_loss_limit' 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 default_conf['dry_run'] = True
order = MagicMock() order = MagicMock()
order.myid = 123 order.myid = 123
@ -160,6 +167,16 @@ def test_fetch_stoploss_order(default_conf, mocker, limit_sell_order):
assert resp['type'] == 'stop' assert resp['type'] == 'stop'
assert resp['status_stop'] == 'triggered' 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): with pytest.raises(InvalidOrderException):
api_mock.fetch_orders = MagicMock(side_effect=ccxt.InvalidOrder("Order not found")) api_mock.fetch_orders = MagicMock(side_effect=ccxt.InvalidOrder("Order not found"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, id='ftx') exchange = get_patched_exchange(mocker, default_conf, api_mock, id='ftx')

View File

@ -168,7 +168,11 @@ def test_get_balances_prod(default_conf, mocker):
@pytest.mark.parametrize('ordertype', ['market', 'limit']) @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() api_mock = MagicMock()
order_id = 'test_prod_buy_{}'.format(randint(0, 10 ** 6)) 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') 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, order_types={'stoploss': ordertype,
'stoploss_on_exchange_limit_ratio': 0.99 'stoploss_on_exchange_limit_ratio': limitratio
}) })
assert 'id' in order assert 'id' in order
@ -197,12 +201,12 @@ def test_stoploss_order_kraken(default_conf, mocker, ordertype):
if ordertype == 'limit': 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]['type'] == STOPLOSS_LIMIT_ORDERTYPE
assert api_mock.create_order.call_args_list[0][1]['params'] == { assert api_mock.create_order.call_args_list[0][1]['params'] == {
'trading_agreement': 'agree', 'price2': 217.8} 'trading_agreement': 'agree', 'price2': adjustedprice}
else: else:
assert api_mock.create_order.call_args_list[0][1]['type'] == STOPLOSS_ORDERTYPE assert api_mock.create_order.call_args_list[0][1]['type'] == STOPLOSS_ORDERTYPE
assert api_mock.create_order.call_args_list[0][1]['params'] == { assert api_mock.create_order.call_args_list[0][1]['params'] == {
'trading_agreement': 'agree'} '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]['amount'] == 1
assert api_mock.create_order.call_args_list[0][1]['price'] == 220 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): with pytest.raises(DependencyException):
api_mock.create_order = MagicMock(side_effect=ccxt.InsufficientFunds("0 balance")) api_mock.create_order = MagicMock(side_effect=ccxt.InsufficientFunds("0 balance"))
exchange = get_patched_exchange(mocker, default_conf, api_mock, 'kraken') 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): with pytest.raises(InvalidOrderException):
api_mock.create_order = MagicMock( api_mock.create_order = MagicMock(
side_effect=ccxt.InvalidOrder("kraken Order would trigger immediately.")) side_effect=ccxt.InvalidOrder("kraken Order would trigger immediately."))
exchange = get_patched_exchange(mocker, default_conf, api_mock, 'kraken') 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", ccxt_exceptionhandlers(mocker, default_conf, api_mock, "kraken",
"stoploss", "create_order", retries=1, "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() api_mock = MagicMock()
default_conf['dry_run'] = True default_conf['dry_run'] = True
mocker.patch('freqtrade.exchange.Exchange.amount_to_precision', lambda s, x, y: y) 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() 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 'id' in order
assert 'info' in order assert 'info' in order
@ -244,17 +249,21 @@ def test_stoploss_order_dry_run_kraken(default_conf, mocker):
assert order['amount'] == 1 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') exchange = get_patched_exchange(mocker, default_conf, id='kraken')
order = { order = {
'type': STOPLOSS_ORDERTYPE, 'type': STOPLOSS_ORDERTYPE,
'price': 1500, 'price': 1500,
} }
assert exchange.stoploss_adjust(1501, order, side="sell") assert exchange.stoploss_adjust(sl1, order, side=side)
assert not exchange.stoploss_adjust(1499, order, side="sell") assert not exchange.stoploss_adjust(sl2, order, side=side)
# Test with invalid order case ... # Test with invalid order case ...
order['type'] = 'stop_loss_limit' 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', [ @pytest.mark.parametrize('pair,nominal_value,max_lev', [

View File

@ -1253,6 +1253,7 @@ def test_create_stoploss_order_insufficient_funds(mocker, default_conf, caplog,
@pytest.mark.usefixtures("init_persistence") @pytest.mark.usefixtures("init_persistence")
def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee,
limit_buy_order, limit_sell_order) -> None: limit_buy_order, limit_sell_order) -> None:
# TODO-lev: test for short
# When trailing stoploss is set # When trailing stoploss is set
stoploss = MagicMock(return_value={'id': 13434334}) stoploss = MagicMock(return_value={'id': 13434334})
patch_RPCManager(mocker) 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, def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, caplog,
limit_buy_order, limit_sell_order) -> None: limit_buy_order, limit_sell_order) -> None:
# TODO-lev: test for short
# When trailing stoploss is set # When trailing stoploss is set
stoploss = MagicMock(return_value={'id': 13434334}) stoploss = MagicMock(return_value={'id': 13434334})
patch_exchange(mocker) 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, def test_handle_stoploss_on_exchange_custom_stop(mocker, default_conf, fee,
limit_buy_order, limit_sell_order) -> None: limit_buy_order, limit_sell_order) -> None:
# When trailing stoploss is set # When trailing stoploss is set
# TODO-lev: test for short
stoploss = MagicMock(return_value={'id': 13434334}) stoploss = MagicMock(return_value={'id': 13434334})
patch_RPCManager(mocker) patch_RPCManager(mocker)
mocker.patch.multiple( 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, def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog,
limit_buy_order, limit_sell_order) -> None: limit_buy_order, limit_sell_order) -> None:
# TODO-lev: test for short
# When trailing stoploss is set # When trailing stoploss is set
stoploss = MagicMock(return_value={'id': 13434334}) stoploss = MagicMock(return_value={'id': 13434334})
patch_RPCManager(mocker) patch_RPCManager(mocker)
@ -1732,7 +1735,7 @@ def test_exit_positions_exception(mocker, default_conf, limit_buy_order, caplog)
) )
n = freqtrade.exit_positions(trades) n = freqtrade.exit_positions(trades)
assert n == 0 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: def test_update_trade_state(mocker, default_conf, limit_buy_order, caplog) -> None: