Fixed create order margin call count tests and made _ccxt_config a computed property
This commit is contained in:
parent
32e52cd460
commit
2c21bbfa0c
@ -20,4 +20,7 @@ class Bibox(Exchange):
|
|||||||
|
|
||||||
# fetchCurrencies API point requires authentication for Bibox,
|
# fetchCurrencies API point requires authentication for Bibox,
|
||||||
# so switch it off for Freqtrade load_markets()
|
# so switch it off for Freqtrade load_markets()
|
||||||
_ccxt_config: Dict = {"has": {"fetchCurrencies": False}}
|
@property
|
||||||
|
def _ccxt_config(self) -> Dict:
|
||||||
|
# Parameters to add directly to ccxt sync/async initialization.
|
||||||
|
return {"has": {"fetchCurrencies": False}}
|
||||||
|
@ -33,9 +33,27 @@ class Binance(Exchange):
|
|||||||
# TradingMode.SPOT always supported and not required in this list
|
# TradingMode.SPOT always supported and not required in this list
|
||||||
# (TradingMode.MARGIN, Collateral.CROSS), # TODO-lev: Uncomment once supported
|
# (TradingMode.MARGIN, Collateral.CROSS), # TODO-lev: Uncomment once supported
|
||||||
# (TradingMode.FUTURES, Collateral.CROSS), # TODO-lev: Uncomment once supported
|
# (TradingMode.FUTURES, Collateral.CROSS), # TODO-lev: Uncomment once supported
|
||||||
(TradingMode.FUTURES, Collateral.ISOLATED)
|
# (TradingMode.FUTURES, Collateral.ISOLATED) # TODO-lev: Uncomment once supported
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _ccxt_config(self) -> Dict:
|
||||||
|
# Parameters to add directly to ccxt sync/async initialization.
|
||||||
|
if self.trading_mode == TradingMode.MARGIN:
|
||||||
|
return {
|
||||||
|
"options": {
|
||||||
|
"defaultType": "margin"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elif self.trading_mode == TradingMode.FUTURES:
|
||||||
|
return {
|
||||||
|
"options": {
|
||||||
|
"defaultType": "future"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return {}
|
||||||
|
|
||||||
def stoploss_adjust(self, stop_loss: float, order: Dict, side: str) -> bool:
|
def stoploss_adjust(self, stop_loss: float, order: Dict, side: str) -> bool:
|
||||||
"""
|
"""
|
||||||
Verify stop_loss against stoploss-order value (limit or price)
|
Verify stop_loss against stoploss-order value (limit or price)
|
||||||
|
@ -49,9 +49,6 @@ class Exchange:
|
|||||||
|
|
||||||
_config: Dict = {}
|
_config: Dict = {}
|
||||||
|
|
||||||
# Parameters to add directly to ccxt sync/async initialization.
|
|
||||||
_ccxt_config: Dict = {}
|
|
||||||
|
|
||||||
# Parameters to add directly to buy/sell calls (like agreeing to trading agreement)
|
# Parameters to add directly to buy/sell calls (like agreeing to trading agreement)
|
||||||
_params: Dict = {}
|
_params: Dict = {}
|
||||||
|
|
||||||
@ -131,21 +128,6 @@ class Exchange:
|
|||||||
self._trades_pagination = self._ft_has['trades_pagination']
|
self._trades_pagination = self._ft_has['trades_pagination']
|
||||||
self._trades_pagination_arg = self._ft_has['trades_pagination_arg']
|
self._trades_pagination_arg = self._ft_has['trades_pagination_arg']
|
||||||
|
|
||||||
# Initialize ccxt objects
|
|
||||||
ccxt_config = self._ccxt_config.copy()
|
|
||||||
ccxt_config = deep_merge_dicts(exchange_config.get('ccxt_config', {}), ccxt_config)
|
|
||||||
ccxt_config = deep_merge_dicts(exchange_config.get('ccxt_sync_config', {}), ccxt_config)
|
|
||||||
|
|
||||||
self._api = self._init_ccxt(exchange_config, ccxt_kwargs=ccxt_config)
|
|
||||||
|
|
||||||
ccxt_async_config = self._ccxt_config.copy()
|
|
||||||
ccxt_async_config = deep_merge_dicts(exchange_config.get('ccxt_config', {}),
|
|
||||||
ccxt_async_config)
|
|
||||||
ccxt_async_config = deep_merge_dicts(exchange_config.get('ccxt_async_config', {}),
|
|
||||||
ccxt_async_config)
|
|
||||||
self._api_async = self._init_ccxt(
|
|
||||||
exchange_config, ccxt_async, ccxt_kwargs=ccxt_async_config)
|
|
||||||
|
|
||||||
self.trading_mode: TradingMode = (
|
self.trading_mode: TradingMode = (
|
||||||
TradingMode(config.get('trading_mode'))
|
TradingMode(config.get('trading_mode'))
|
||||||
if config.get('trading_mode')
|
if config.get('trading_mode')
|
||||||
@ -157,6 +139,21 @@ class Exchange:
|
|||||||
else None
|
else None
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Initialize ccxt objects
|
||||||
|
ccxt_config = self._ccxt_config
|
||||||
|
ccxt_config = deep_merge_dicts(exchange_config.get('ccxt_config', {}), ccxt_config)
|
||||||
|
ccxt_config = deep_merge_dicts(exchange_config.get('ccxt_sync_config', {}), ccxt_config)
|
||||||
|
|
||||||
|
self._api = self._init_ccxt(exchange_config, ccxt_kwargs=ccxt_config)
|
||||||
|
|
||||||
|
ccxt_async_config = self._ccxt_config
|
||||||
|
ccxt_async_config = deep_merge_dicts(exchange_config.get('ccxt_config', {}),
|
||||||
|
ccxt_async_config)
|
||||||
|
ccxt_async_config = deep_merge_dicts(exchange_config.get('ccxt_async_config', {}),
|
||||||
|
ccxt_async_config)
|
||||||
|
self._api_async = self._init_ccxt(
|
||||||
|
exchange_config, ccxt_async, ccxt_kwargs=ccxt_async_config)
|
||||||
|
|
||||||
if self.trading_mode != TradingMode.SPOT:
|
if self.trading_mode != TradingMode.SPOT:
|
||||||
self.fill_leverage_brackets()
|
self.fill_leverage_brackets()
|
||||||
|
|
||||||
@ -210,7 +207,7 @@ class Exchange:
|
|||||||
'secret': exchange_config.get('secret'),
|
'secret': exchange_config.get('secret'),
|
||||||
'password': exchange_config.get('password'),
|
'password': exchange_config.get('password'),
|
||||||
'uid': exchange_config.get('uid', ''),
|
'uid': exchange_config.get('uid', ''),
|
||||||
'options': exchange_config.get('options', {})
|
# 'options': exchange_config.get('options', {})
|
||||||
}
|
}
|
||||||
if ccxt_kwargs:
|
if ccxt_kwargs:
|
||||||
logger.info('Applying additional ccxt config: %s', ccxt_kwargs)
|
logger.info('Applying additional ccxt config: %s', ccxt_kwargs)
|
||||||
@ -231,6 +228,11 @@ class Exchange:
|
|||||||
|
|
||||||
return api
|
return api
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _ccxt_config(self) -> Dict:
|
||||||
|
# Parameters to add directly to ccxt sync/async initialization.
|
||||||
|
return {}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
"""exchange Name (from ccxt)"""
|
"""exchange Name (from ccxt)"""
|
||||||
@ -258,13 +260,6 @@ class Exchange:
|
|||||||
"""exchange ccxt precisionMode"""
|
"""exchange ccxt precisionMode"""
|
||||||
return self._api.precisionMode
|
return self._api.precisionMode
|
||||||
|
|
||||||
@property
|
|
||||||
def running_live_mode(self) -> bool:
|
|
||||||
return (
|
|
||||||
self._config['runmode'].value not in ('backtest', 'hyperopt') and
|
|
||||||
not self._config['dry_run']
|
|
||||||
)
|
|
||||||
|
|
||||||
def _log_exchange_response(self, endpoint, response) -> None:
|
def _log_exchange_response(self, endpoint, response) -> None:
|
||||||
""" Log exchange responses """
|
""" Log exchange responses """
|
||||||
if self.log_responses:
|
if self.log_responses:
|
||||||
@ -624,12 +619,12 @@ class Exchange:
|
|||||||
# The value returned should satisfy both limits: for amount (base currency) and
|
# The value returned should satisfy both limits: for amount (base currency) and
|
||||||
# for cost (quote, stake currency), so max() is used here.
|
# for cost (quote, stake currency), so max() is used here.
|
||||||
# See also #2575 at github.
|
# See also #2575 at github.
|
||||||
return self._divide_stake_amount_by_leverage(
|
return self._get_stake_amount_considering_leverage(
|
||||||
max(min_stake_amounts) * amount_reserve_percent,
|
max(min_stake_amounts) * amount_reserve_percent,
|
||||||
leverage or 1.0
|
leverage or 1.0
|
||||||
)
|
)
|
||||||
|
|
||||||
def _divide_stake_amount_by_leverage(self, stake_amount: float, leverage: float):
|
def _get_stake_amount_considering_leverage(self, stake_amount: float, leverage: float):
|
||||||
"""
|
"""
|
||||||
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
|
||||||
|
@ -18,7 +18,7 @@ from freqtrade import constants
|
|||||||
from freqtrade.commands import Arguments
|
from freqtrade.commands import Arguments
|
||||||
from freqtrade.data.converter import ohlcv_to_dataframe
|
from freqtrade.data.converter import ohlcv_to_dataframe
|
||||||
from freqtrade.edge import Edge, PairInfo
|
from freqtrade.edge import Edge, PairInfo
|
||||||
from freqtrade.enums import RunMode
|
from freqtrade.enums import Collateral, RunMode, TradingMode
|
||||||
from freqtrade.exchange import Exchange
|
from freqtrade.exchange import Exchange
|
||||||
from freqtrade.freqtradebot import FreqtradeBot
|
from freqtrade.freqtradebot import FreqtradeBot
|
||||||
from freqtrade.persistence import LocalTrade, Trade, init_db
|
from freqtrade.persistence import LocalTrade, Trade, init_db
|
||||||
@ -81,7 +81,13 @@ def patched_configuration_load_config_file(mocker, config) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def patch_exchange(mocker, api_mock=None, id='binance', mock_markets=True) -> None:
|
def patch_exchange(
|
||||||
|
mocker,
|
||||||
|
api_mock=None,
|
||||||
|
id='binance',
|
||||||
|
mock_markets=True,
|
||||||
|
mock_supported_modes=True
|
||||||
|
) -> None:
|
||||||
mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock(return_value={}))
|
mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock(return_value={}))
|
||||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
||||||
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock())
|
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock())
|
||||||
@ -90,10 +96,22 @@ def patch_exchange(mocker, api_mock=None, id='binance', mock_markets=True) -> No
|
|||||||
mocker.patch('freqtrade.exchange.Exchange.id', PropertyMock(return_value=id))
|
mocker.patch('freqtrade.exchange.Exchange.id', PropertyMock(return_value=id))
|
||||||
mocker.patch('freqtrade.exchange.Exchange.name', PropertyMock(return_value=id.title()))
|
mocker.patch('freqtrade.exchange.Exchange.name', PropertyMock(return_value=id.title()))
|
||||||
mocker.patch('freqtrade.exchange.Exchange.precisionMode', PropertyMock(return_value=2))
|
mocker.patch('freqtrade.exchange.Exchange.precisionMode', PropertyMock(return_value=2))
|
||||||
|
|
||||||
if mock_markets:
|
if mock_markets:
|
||||||
mocker.patch('freqtrade.exchange.Exchange.markets',
|
mocker.patch('freqtrade.exchange.Exchange.markets',
|
||||||
PropertyMock(return_value=get_markets()))
|
PropertyMock(return_value=get_markets()))
|
||||||
|
|
||||||
|
if mock_supported_modes:
|
||||||
|
mocker.patch(
|
||||||
|
f'freqtrade.exchange.{id.capitalize()}._supported_trading_mode_collateral_pairs',
|
||||||
|
PropertyMock(return_value=[
|
||||||
|
(TradingMode.MARGIN, Collateral.CROSS),
|
||||||
|
(TradingMode.MARGIN, Collateral.ISOLATED),
|
||||||
|
(TradingMode.FUTURES, Collateral.CROSS),
|
||||||
|
(TradingMode.FUTURES, Collateral.ISOLATED)
|
||||||
|
])
|
||||||
|
)
|
||||||
|
|
||||||
if api_mock:
|
if api_mock:
|
||||||
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
||||||
else:
|
else:
|
||||||
@ -101,8 +119,8 @@ def patch_exchange(mocker, api_mock=None, id='binance', mock_markets=True) -> No
|
|||||||
|
|
||||||
|
|
||||||
def get_patched_exchange(mocker, config, api_mock=None, id='binance',
|
def get_patched_exchange(mocker, config, api_mock=None, id='binance',
|
||||||
mock_markets=True) -> Exchange:
|
mock_markets=True, mock_supported_modes=True) -> Exchange:
|
||||||
patch_exchange(mocker, api_mock, id, mock_markets)
|
patch_exchange(mocker, api_mock, id, mock_markets, mock_supported_modes)
|
||||||
config['exchange']['name'] = id
|
config['exchange']['name'] = id
|
||||||
try:
|
try:
|
||||||
exchange = ExchangeResolver.load_exchange(id, config)
|
exchange = ExchangeResolver.load_exchange(id, config)
|
||||||
|
@ -336,3 +336,15 @@ async def test__async_get_historic_ohlcv_binance(default_conf, mocker, caplog):
|
|||||||
assert exchange._api_async.fetch_ohlcv.call_count == 2
|
assert exchange._api_async.fetch_ohlcv.call_count == 2
|
||||||
assert res == ohlcv
|
assert res == ohlcv
|
||||||
assert log_has_re(r"Candle-data for ETH/BTC available starting with .*", caplog)
|
assert log_has_re(r"Candle-data for ETH/BTC available starting with .*", caplog)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("trading_mode,collateral,config", [
|
||||||
|
("", "", {}),
|
||||||
|
("margin", "cross", {"options": {"defaultType": "margin"}}),
|
||||||
|
("futures", "isolated", {"options": {"defaultType": "future"}}),
|
||||||
|
])
|
||||||
|
def test__ccxt_config(default_conf, mocker, trading_mode, collateral, config):
|
||||||
|
default_conf['trading_mode'] = trading_mode
|
||||||
|
default_conf['collateral'] = collateral
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, id="binance")
|
||||||
|
assert exchange._ccxt_config == config
|
||||||
|
@ -132,10 +132,9 @@ def test_init_ccxt_kwargs(default_conf, mocker, caplog):
|
|||||||
|
|
||||||
assert log_has("Applying additional ccxt config: {'TestKWARG': 11, 'TestKWARG44': 11}", caplog)
|
assert log_has("Applying additional ccxt config: {'TestKWARG': 11, 'TestKWARG44': 11}", caplog)
|
||||||
assert ex._api.headers == {'hello': 'world'}
|
assert ex._api.headers == {'hello': 'world'}
|
||||||
|
assert ex._ccxt_config == {}
|
||||||
Exchange._headers = {}
|
Exchange._headers = {}
|
||||||
|
|
||||||
# TODO-lev: Test with options
|
|
||||||
|
|
||||||
|
|
||||||
def test_destroy(default_conf, mocker, caplog):
|
def test_destroy(default_conf, mocker, caplog):
|
||||||
caplog.set_level(logging.DEBUG)
|
caplog.set_level(logging.DEBUG)
|
||||||
@ -1116,6 +1115,8 @@ def test_create_order(default_conf, mocker, side, ordertype, rate, marketprice,
|
|||||||
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)
|
||||||
mocker.patch('freqtrade.exchange.Exchange.price_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 = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
|
||||||
|
exchange._set_leverage = MagicMock()
|
||||||
|
exchange.set_margin_mode = MagicMock()
|
||||||
|
|
||||||
order = exchange.create_order(
|
order = exchange.create_order(
|
||||||
pair='ETH/BTC',
|
pair='ETH/BTC',
|
||||||
@ -1134,10 +1135,10 @@ def test_create_order(default_conf, mocker, side, ordertype, rate, marketprice,
|
|||||||
assert api_mock.create_order.call_args[0][2] == side
|
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][3] == 1
|
||||||
assert api_mock.create_order.call_args[0][4] is rate
|
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
|
||||||
|
|
||||||
assert api_mock._set_leverage.call_count == 0 if side == "buy" else 1
|
exchange.trading_mode = TradingMode.FUTURES
|
||||||
assert api_mock.set_margin_mode.call_count == 0 if side == "buy" else 1
|
|
||||||
|
|
||||||
order = exchange.create_order(
|
order = exchange.create_order(
|
||||||
pair='ETH/BTC',
|
pair='ETH/BTC',
|
||||||
ordertype=ordertype,
|
ordertype=ordertype,
|
||||||
@ -1147,8 +1148,8 @@ def test_create_order(default_conf, mocker, side, ordertype, rate, marketprice,
|
|||||||
leverage=3.0
|
leverage=3.0
|
||||||
)
|
)
|
||||||
|
|
||||||
assert api_mock._set_leverage.call_count == 1
|
assert exchange._set_leverage.call_count == 1
|
||||||
assert api_mock.set_margin_mode.call_count == 1
|
assert exchange.set_margin_mode.call_count == 1
|
||||||
|
|
||||||
|
|
||||||
def test_buy_dry_run(default_conf, mocker):
|
def test_buy_dry_run(default_conf, mocker):
|
||||||
@ -3042,7 +3043,6 @@ def test_calculate_fee_rate(mocker, default_conf, order, expected) -> None:
|
|||||||
(3, 5, 5),
|
(3, 5, 5),
|
||||||
(4, 5, 2),
|
(4, 5, 2),
|
||||||
(5, 5, 1),
|
(5, 5, 1),
|
||||||
|
|
||||||
])
|
])
|
||||||
def test_calculate_backoff(retrycount, max_retries, expected):
|
def test_calculate_backoff(retrycount, max_retries, expected):
|
||||||
assert calculate_backoff(retrycount, max_retries) == expected
|
assert calculate_backoff(retrycount, max_retries) == expected
|
||||||
@ -3054,7 +3054,7 @@ def test_calculate_backoff(retrycount, max_retries, expected):
|
|||||||
(20.0, 5.0, 4.0),
|
(20.0, 5.0, 4.0),
|
||||||
(100.0, 100.0, 1.0)
|
(100.0, 100.0, 1.0)
|
||||||
])
|
])
|
||||||
def test_divide_stake_amount_by_leverage(
|
def test_get_stake_amount_considering_leverage(
|
||||||
exchange,
|
exchange,
|
||||||
stake_amount,
|
stake_amount,
|
||||||
leverage,
|
leverage,
|
||||||
@ -3063,7 +3063,8 @@ def test_divide_stake_amount_by_leverage(
|
|||||||
default_conf
|
default_conf
|
||||||
):
|
):
|
||||||
exchange = get_patched_exchange(mocker, default_conf, id=exchange)
|
exchange = get_patched_exchange(mocker, default_conf, id=exchange)
|
||||||
assert exchange._divide_stake_amount_by_leverage(stake_amount, leverage) == min_stake_with_lev
|
assert exchange._get_stake_amount_considering_leverage(
|
||||||
|
stake_amount, leverage) == min_stake_with_lev
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("exchange_name,trading_mode", [
|
@pytest.mark.parametrize("exchange_name,trading_mode", [
|
||||||
@ -3132,6 +3133,7 @@ def test_set_margin_mode(mocker, default_conf, collateral):
|
|||||||
# TODO-lev: Remove once implemented
|
# TODO-lev: Remove once implemented
|
||||||
("binance", TradingMode.MARGIN, Collateral.CROSS, True),
|
("binance", TradingMode.MARGIN, Collateral.CROSS, True),
|
||||||
("binance", TradingMode.FUTURES, Collateral.CROSS, True),
|
("binance", TradingMode.FUTURES, Collateral.CROSS, True),
|
||||||
|
("binance", TradingMode.FUTURES, Collateral.ISOLATED, True),
|
||||||
("kraken", TradingMode.MARGIN, Collateral.CROSS, True),
|
("kraken", TradingMode.MARGIN, Collateral.CROSS, True),
|
||||||
("kraken", TradingMode.FUTURES, Collateral.CROSS, True),
|
("kraken", TradingMode.FUTURES, Collateral.CROSS, True),
|
||||||
("ftx", TradingMode.MARGIN, Collateral.CROSS, True),
|
("ftx", TradingMode.MARGIN, Collateral.CROSS, True),
|
||||||
@ -3140,7 +3142,7 @@ def test_set_margin_mode(mocker, default_conf, collateral):
|
|||||||
# TODO-lev: Uncomment once implemented
|
# TODO-lev: Uncomment once implemented
|
||||||
# ("binance", TradingMode.MARGIN, Collateral.CROSS, False),
|
# ("binance", TradingMode.MARGIN, Collateral.CROSS, False),
|
||||||
# ("binance", TradingMode.FUTURES, Collateral.CROSS, False),
|
# ("binance", TradingMode.FUTURES, Collateral.CROSS, False),
|
||||||
("binance", TradingMode.FUTURES, Collateral.ISOLATED, False),
|
# ("binance", TradingMode.FUTURES, Collateral.ISOLATED, False),
|
||||||
# ("kraken", TradingMode.MARGIN, Collateral.CROSS, False),
|
# ("kraken", TradingMode.MARGIN, Collateral.CROSS, False),
|
||||||
# ("kraken", TradingMode.FUTURES, Collateral.CROSS, False),
|
# ("kraken", TradingMode.FUTURES, Collateral.CROSS, False),
|
||||||
# ("ftx", TradingMode.MARGIN, Collateral.CROSS, False),
|
# ("ftx", TradingMode.MARGIN, Collateral.CROSS, False),
|
||||||
@ -3154,7 +3156,8 @@ def test_validate_trading_mode_and_collateral(
|
|||||||
collateral,
|
collateral,
|
||||||
exception_thrown
|
exception_thrown
|
||||||
):
|
):
|
||||||
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
|
exchange = get_patched_exchange(
|
||||||
|
mocker, default_conf, id=exchange_name, mock_supported_modes=False)
|
||||||
if (exception_thrown):
|
if (exception_thrown):
|
||||||
with pytest.raises(OperationalException):
|
with pytest.raises(OperationalException):
|
||||||
exchange.validate_trading_mode_and_collateral(trading_mode, collateral)
|
exchange.validate_trading_mode_and_collateral(trading_mode, collateral)
|
||||||
|
Loading…
Reference in New Issue
Block a user