Merge branch 'lev-exchange' into lev-freqtradebot
This commit is contained in:
commit
1d7a8f667a
@ -112,9 +112,6 @@ class Binance(Exchange):
|
|||||||
except ccxt.BaseError as e:
|
except ccxt.BaseError as e:
|
||||||
raise OperationalException(e) from e
|
raise OperationalException(e) from e
|
||||||
|
|
||||||
def _apply_leverage_to_stake_amount(self, stake_amount: float, leverage: float):
|
|
||||||
return stake_amount / leverage
|
|
||||||
|
|
||||||
@retrier
|
@retrier
|
||||||
def fill_leverage_brackets(self):
|
def fill_leverage_brackets(self):
|
||||||
"""
|
"""
|
||||||
@ -154,3 +151,27 @@ class Binance(Exchange):
|
|||||||
if nominal_value >= min_amount:
|
if nominal_value >= min_amount:
|
||||||
max_lev = 1/margin_req
|
max_lev = 1/margin_req
|
||||||
return max_lev
|
return max_lev
|
||||||
|
|
||||||
|
@retrier
|
||||||
|
def _set_leverage(
|
||||||
|
self,
|
||||||
|
leverage: float,
|
||||||
|
pair: Optional[str] = None,
|
||||||
|
trading_mode: Optional[TradingMode] = None
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Set's the leverage before making a trade, in order to not
|
||||||
|
have the same leverage on every trade
|
||||||
|
"""
|
||||||
|
trading_mode = trading_mode or self.trading_mode
|
||||||
|
|
||||||
|
try:
|
||||||
|
if trading_mode == TradingMode.FUTURES:
|
||||||
|
self._api.set_leverage(symbol=pair, leverage=leverage)
|
||||||
|
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
|
||||||
|
@ -145,7 +145,7 @@ class Exchange:
|
|||||||
self._api_async = self._init_ccxt(
|
self._api_async = self._init_ccxt(
|
||||||
exchange_config, ccxt_async, ccxt_kwargs=ccxt_async_config)
|
exchange_config, ccxt_async, ccxt_kwargs=ccxt_async_config)
|
||||||
|
|
||||||
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')
|
||||||
else TradingMode.SPOT
|
else TradingMode.SPOT
|
||||||
@ -156,7 +156,7 @@ class Exchange:
|
|||||||
else None
|
else None
|
||||||
)
|
)
|
||||||
|
|
||||||
if trading_mode != TradingMode.SPOT:
|
if self.trading_mode != TradingMode.SPOT:
|
||||||
self.fill_leverage_brackets()
|
self.fill_leverage_brackets()
|
||||||
|
|
||||||
logger.info('Using Exchange "%s"', self.name)
|
logger.info('Using Exchange "%s"', self.name)
|
||||||
@ -176,7 +176,7 @@ class Exchange:
|
|||||||
self.validate_order_time_in_force(config.get('order_time_in_force', {}))
|
self.validate_order_time_in_force(config.get('order_time_in_force', {}))
|
||||||
self.validate_required_startup_candles(config.get('startup_candle_count', 0),
|
self.validate_required_startup_candles(config.get('startup_candle_count', 0),
|
||||||
config.get('timeframe', ''))
|
config.get('timeframe', ''))
|
||||||
self.validate_trading_mode_and_collateral(trading_mode, collateral)
|
self.validate_trading_mode_and_collateral(self.trading_mode, collateral)
|
||||||
# Converts the interval provided in minutes in config to seconds
|
# Converts the interval provided in minutes in config to seconds
|
||||||
self.markets_refresh_interval: int = exchange_config.get(
|
self.markets_refresh_interval: int = exchange_config.get(
|
||||||
"markets_refresh_interval", 60) * 60
|
"markets_refresh_interval", 60) * 60
|
||||||
@ -630,7 +630,7 @@ class Exchange:
|
|||||||
:param stake_amount: The stake amount for a pair before leverage is considered
|
:param stake_amount: The stake amount for a pair before leverage is considered
|
||||||
:param leverage: The amount of leverage being used on the current trade
|
:param leverage: The amount of leverage being used on the current trade
|
||||||
"""
|
"""
|
||||||
return stake_amount
|
return stake_amount / leverage
|
||||||
|
|
||||||
# Dry-run methods
|
# Dry-run methods
|
||||||
|
|
||||||
@ -771,12 +771,14 @@ class Exchange:
|
|||||||
# Order handling
|
# Order handling
|
||||||
|
|
||||||
def create_order(self, pair: str, ordertype: str, side: str, amount: float,
|
def create_order(self, pair: str, ordertype: str, side: str, amount: float,
|
||||||
rate: float, time_in_force: str = 'gtc') -> Dict:
|
rate: float, time_in_force: str = 'gtc', leverage=1.0) -> Dict:
|
||||||
|
|
||||||
if self._config['dry_run']:
|
if self._config['dry_run']:
|
||||||
dry_order = self.create_dry_run_order(pair, ordertype, side, amount, rate)
|
dry_order = self.create_dry_run_order(pair, ordertype, side, amount, rate)
|
||||||
return dry_order
|
return dry_order
|
||||||
|
|
||||||
|
if self.trading_mode != TradingMode.SPOT:
|
||||||
|
self._set_leverage(leverage, pair)
|
||||||
params = self._params.copy()
|
params = self._params.copy()
|
||||||
if time_in_force != 'gtc' and ordertype != 'market':
|
if time_in_force != 'gtc' and ordertype != 'market':
|
||||||
param = self._ft_has.get('time_in_force_parameter', '')
|
param = self._ft_has.get('time_in_force_parameter', '')
|
||||||
@ -1600,7 +1602,12 @@ class Exchange:
|
|||||||
return 1.0
|
return 1.0
|
||||||
|
|
||||||
@retrier
|
@retrier
|
||||||
def set_leverage(self, leverage: float, pair: Optional[str]):
|
def _set_leverage(
|
||||||
|
self,
|
||||||
|
leverage: float,
|
||||||
|
pair: Optional[str] = None,
|
||||||
|
trading_mode: Optional[TradingMode] = None
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Set's the leverage before making a trade, in order to not
|
Set's the leverage before making a trade, in order to not
|
||||||
have the same leverage on every trade
|
have the same leverage on every trade
|
||||||
|
@ -146,6 +146,7 @@ class Kraken(Exchange):
|
|||||||
leverages = {}
|
leverages = {}
|
||||||
|
|
||||||
for pair, market in self.markets.items():
|
for pair, market in self.markets.items():
|
||||||
|
leverages[pair] = [1]
|
||||||
info = market['info']
|
info = market['info']
|
||||||
leverage_buy = info.get('leverage_buy', [])
|
leverage_buy = info.get('leverage_buy', [])
|
||||||
leverage_sell = info.get('leverage_sell', [])
|
leverage_sell = info.get('leverage_sell', [])
|
||||||
@ -155,12 +156,12 @@ class Kraken(Exchange):
|
|||||||
f"The buy({leverage_buy}) and sell({leverage_sell}) leverage are not equal"
|
f"The buy({leverage_buy}) and sell({leverage_sell}) leverage are not equal"
|
||||||
"for {pair}. Please notify freqtrade because this has never happened before"
|
"for {pair}. Please notify freqtrade because this has never happened before"
|
||||||
)
|
)
|
||||||
if max(leverage_buy) < max(leverage_sell):
|
if max(leverage_buy) <= max(leverage_sell):
|
||||||
leverages[pair] = leverage_buy
|
leverages[pair] += [int(lev) for lev in leverage_buy]
|
||||||
else:
|
else:
|
||||||
leverages[pair] = leverage_sell
|
leverages[pair] += [int(lev) for lev in leverage_sell]
|
||||||
else:
|
else:
|
||||||
leverages[pair] = leverage_buy
|
leverages[pair] += [int(lev) for lev in leverage_buy]
|
||||||
self._leverage_brackets = leverages
|
self._leverage_brackets = leverages
|
||||||
|
|
||||||
def get_max_leverage(self, pair: Optional[str], nominal_value: Optional[float]) -> float:
|
def get_max_leverage(self, pair: Optional[str], nominal_value: Optional[float]) -> float:
|
||||||
@ -171,9 +172,18 @@ class Kraken(Exchange):
|
|||||||
"""
|
"""
|
||||||
return float(max(self._leverage_brackets[pair]))
|
return float(max(self._leverage_brackets[pair]))
|
||||||
|
|
||||||
def set_leverage(self, pair, leverage):
|
def _set_leverage(
|
||||||
|
self,
|
||||||
|
leverage: float,
|
||||||
|
pair: Optional[str] = None,
|
||||||
|
trading_mode: Optional[TradingMode] = None
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Kraken set's the leverage as an option in the order object, so it doesn't do
|
Kraken set's the leverage as an option in the order object, so we need to
|
||||||
anything in this function
|
add it to params
|
||||||
"""
|
"""
|
||||||
return
|
if leverage > 1.0:
|
||||||
|
self._params['leverage'] = leverage
|
||||||
|
else:
|
||||||
|
if 'leverage' in self._params:
|
||||||
|
del self._params['leverage']
|
||||||
|
@ -4,6 +4,7 @@ from unittest.mock import MagicMock, PropertyMock
|
|||||||
import ccxt
|
import ccxt
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from freqtrade.enums import TradingMode
|
||||||
from freqtrade.exceptions import DependencyException, InvalidOrderException, OperationalException
|
from freqtrade.exceptions import DependencyException, InvalidOrderException, OperationalException
|
||||||
from tests.conftest import get_patched_exchange
|
from tests.conftest import get_patched_exchange
|
||||||
from tests.exchange.test_exchange import ccxt_exceptionhandlers
|
from tests.exchange.test_exchange import ccxt_exceptionhandlers
|
||||||
@ -232,3 +233,25 @@ def test_fill_leverage_brackets_binance(default_conf, mocker):
|
|||||||
"fill_leverage_brackets",
|
"fill_leverage_brackets",
|
||||||
"load_leverage_brackets"
|
"load_leverage_brackets"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test__set_leverage_binance(mocker, default_conf):
|
||||||
|
|
||||||
|
api_mock = MagicMock()
|
||||||
|
api_mock.set_leverage = MagicMock()
|
||||||
|
type(api_mock).has = PropertyMock(return_value={'setLeverage': True})
|
||||||
|
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, id="binance")
|
||||||
|
exchange._set_leverage(3.0, trading_mode=TradingMode.MARGIN)
|
||||||
|
|
||||||
|
ccxt_exceptionhandlers(
|
||||||
|
mocker,
|
||||||
|
default_conf,
|
||||||
|
api_mock,
|
||||||
|
"binance",
|
||||||
|
"_set_leverage",
|
||||||
|
"set_leverage",
|
||||||
|
pair="XRP/USDT",
|
||||||
|
leverage=5.0,
|
||||||
|
trading_mode=TradingMode.FUTURES
|
||||||
|
)
|
||||||
|
@ -2969,18 +2969,11 @@ def test_calculate_backoff(retrycount, max_retries, expected):
|
|||||||
assert calculate_backoff(retrycount, max_retries) == expected
|
assert calculate_backoff(retrycount, max_retries) == expected
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('exchange,stake_amount,leverage,min_stake_with_lev', [
|
@pytest.mark.parametrize('exchange', ['binance', 'kraken', 'ftx'])
|
||||||
('binance', 9.0, 3.0, 3.0),
|
@pytest.mark.parametrize('stake_amount,leverage,min_stake_with_lev', [
|
||||||
('binance', 20.0, 5.0, 4.0),
|
(9.0, 3.0, 3.0),
|
||||||
('binance', 100.0, 100.0, 1.0),
|
(20.0, 5.0, 4.0),
|
||||||
# Kraken
|
(100.0, 100.0, 1.0)
|
||||||
('kraken', 9.0, 3.0, 9.0),
|
|
||||||
('kraken', 20.0, 5.0, 20.0),
|
|
||||||
('kraken', 100.0, 100.0, 100.0),
|
|
||||||
# FTX
|
|
||||||
('ftx', 9.0, 3.0, 9.0),
|
|
||||||
('ftx', 20.0, 5.0, 20.0),
|
|
||||||
('ftx', 100.0, 100.0, 100.0)
|
|
||||||
])
|
])
|
||||||
def test_apply_leverage_to_stake_amount(
|
def test_apply_leverage_to_stake_amount(
|
||||||
exchange,
|
exchange,
|
||||||
@ -2994,12 +2987,12 @@ 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
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("collateral", [
|
@pytest.mark.parametrize("exchange_name,trading_mode", [
|
||||||
(Collateral.CROSS),
|
("binance", TradingMode.FUTURES),
|
||||||
(Collateral.ISOLATED)
|
("ftx", TradingMode.MARGIN),
|
||||||
|
("ftx", TradingMode.FUTURES)
|
||||||
])
|
])
|
||||||
@pytest.mark.parametrize("exchange_name", [("ftx"), ("binance")])
|
def test__set_leverage(mocker, default_conf, exchange_name, trading_mode):
|
||||||
def test_set_leverage(mocker, default_conf, exchange_name, collateral):
|
|
||||||
|
|
||||||
api_mock = MagicMock()
|
api_mock = MagicMock()
|
||||||
api_mock.set_leverage = MagicMock()
|
api_mock.set_leverage = MagicMock()
|
||||||
@ -3010,10 +3003,11 @@ def test_set_leverage(mocker, default_conf, exchange_name, collateral):
|
|||||||
default_conf,
|
default_conf,
|
||||||
api_mock,
|
api_mock,
|
||||||
exchange_name,
|
exchange_name,
|
||||||
"set_leverage",
|
"_set_leverage",
|
||||||
"set_leverage",
|
"set_leverage",
|
||||||
pair="XRP/USDT",
|
pair="XRP/USDT",
|
||||||
leverage=5.0
|
leverage=5.0,
|
||||||
|
trading_mode=trading_mode
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -3021,8 +3015,7 @@ def test_set_leverage(mocker, default_conf, exchange_name, collateral):
|
|||||||
(Collateral.CROSS),
|
(Collateral.CROSS),
|
||||||
(Collateral.ISOLATED)
|
(Collateral.ISOLATED)
|
||||||
])
|
])
|
||||||
@pytest.mark.parametrize("exchange_name", [("ftx"), ("binance")])
|
def test_set_margin_mode(mocker, default_conf, collateral):
|
||||||
def test_set_margin_mode(mocker, default_conf, exchange_name, collateral):
|
|
||||||
|
|
||||||
api_mock = MagicMock()
|
api_mock = MagicMock()
|
||||||
api_mock.set_margin_mode = MagicMock()
|
api_mock.set_margin_mode = MagicMock()
|
||||||
@ -3032,7 +3025,7 @@ def test_set_margin_mode(mocker, default_conf, exchange_name, collateral):
|
|||||||
mocker,
|
mocker,
|
||||||
default_conf,
|
default_conf,
|
||||||
api_mock,
|
api_mock,
|
||||||
exchange_name,
|
"binance",
|
||||||
"set_margin_mode",
|
"set_margin_mode",
|
||||||
"set_margin_mode",
|
"set_margin_mode",
|
||||||
pair="XRP/USDT",
|
pair="XRP/USDT",
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
from random import randint
|
from random import randint
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock, PropertyMock
|
||||||
|
|
||||||
import ccxt
|
import ccxt
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from freqtrade.enums import TradingMode
|
||||||
from freqtrade.exceptions import DependencyException, InvalidOrderException
|
from freqtrade.exceptions import DependencyException, InvalidOrderException
|
||||||
from freqtrade.exchange.common import API_FETCH_ORDER_RETRY_COUNT
|
from freqtrade.exchange.common import API_FETCH_ORDER_RETRY_COUNT
|
||||||
from tests.conftest import get_patched_exchange
|
from tests.conftest import get_patched_exchange
|
||||||
@ -227,3 +228,26 @@ def test_fill_leverage_brackets_ftx(default_conf, mocker):
|
|||||||
exchange = get_patched_exchange(mocker, default_conf, id="ftx")
|
exchange = get_patched_exchange(mocker, default_conf, id="ftx")
|
||||||
exchange.fill_leverage_brackets()
|
exchange.fill_leverage_brackets()
|
||||||
assert exchange._leverage_brackets == {}
|
assert exchange._leverage_brackets == {}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("trading_mode", [
|
||||||
|
(TradingMode.MARGIN),
|
||||||
|
(TradingMode.FUTURES)
|
||||||
|
])
|
||||||
|
def test__set_leverage(mocker, default_conf, trading_mode):
|
||||||
|
|
||||||
|
api_mock = MagicMock()
|
||||||
|
api_mock.set_leverage = MagicMock()
|
||||||
|
type(api_mock).has = PropertyMock(return_value={'setLeverage': True})
|
||||||
|
|
||||||
|
ccxt_exceptionhandlers(
|
||||||
|
mocker,
|
||||||
|
default_conf,
|
||||||
|
api_mock,
|
||||||
|
"ftx",
|
||||||
|
"_set_leverage",
|
||||||
|
"set_leverage",
|
||||||
|
pair="XRP/USDT",
|
||||||
|
leverage=5.0,
|
||||||
|
trading_mode=trading_mode
|
||||||
|
)
|
||||||
|
@ -294,7 +294,28 @@ def test_fill_leverage_brackets_kraken(default_conf, mocker):
|
|||||||
exchange.fill_leverage_brackets()
|
exchange.fill_leverage_brackets()
|
||||||
|
|
||||||
assert exchange._leverage_brackets == {
|
assert exchange._leverage_brackets == {
|
||||||
'BLK/BTC': ['2', '3'],
|
'BLK/BTC': [1, 2, 3],
|
||||||
'TKN/BTC': ['2', '3', '4', '5'],
|
'TKN/BTC': [1, 2, 3, 4, 5],
|
||||||
'ETH/BTC': ['2']
|
'ETH/BTC': [1, 2],
|
||||||
|
'LTC/BTC': [1],
|
||||||
|
'XRP/BTC': [1],
|
||||||
|
'NEO/BTC': [1],
|
||||||
|
'BTT/BTC': [1],
|
||||||
|
'ETH/USDT': [1],
|
||||||
|
'LTC/USDT': [1],
|
||||||
|
'LTC/USD': [1],
|
||||||
|
'XLTCUSDT': [1],
|
||||||
|
'LTC/ETH': [1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test__set_leverage_kraken(default_conf, mocker):
|
||||||
|
exchange = get_patched_exchange(mocker, default_conf, id="kraken")
|
||||||
|
exchange._set_leverage(1)
|
||||||
|
assert 'leverage' not in exchange._params
|
||||||
|
exchange._set_leverage(3)
|
||||||
|
assert exchange._params['leverage'] == 3
|
||||||
|
exchange._set_leverage(1.0)
|
||||||
|
assert 'leverage' not in exchange._params
|
||||||
|
exchange._set_leverage(3.0)
|
||||||
|
assert exchange._params['leverage'] == 3
|
||||||
|
Loading…
Reference in New Issue
Block a user