Merge branch 'lev-exchange' into lev-freqtradebot

This commit is contained in:
Sam Germain 2021-09-12 03:15:32 -06:00
commit 1d7a8f667a
7 changed files with 142 additions and 43 deletions

View File

@ -112,9 +112,6 @@ class Binance(Exchange):
except ccxt.BaseError as e:
raise OperationalException(e) from e
def _apply_leverage_to_stake_amount(self, stake_amount: float, leverage: float):
return stake_amount / leverage
@retrier
def fill_leverage_brackets(self):
"""
@ -154,3 +151,27 @@ class Binance(Exchange):
if nominal_value >= min_amount:
max_lev = 1/margin_req
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

View File

@ -145,7 +145,7 @@ class Exchange:
self._api_async = self._init_ccxt(
exchange_config, ccxt_async, ccxt_kwargs=ccxt_async_config)
trading_mode: TradingMode = (
self.trading_mode: TradingMode = (
TradingMode(config.get('trading_mode'))
if config.get('trading_mode')
else TradingMode.SPOT
@ -156,7 +156,7 @@ class Exchange:
else None
)
if trading_mode != TradingMode.SPOT:
if self.trading_mode != TradingMode.SPOT:
self.fill_leverage_brackets()
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_required_startup_candles(config.get('startup_candle_count', 0),
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
self.markets_refresh_interval: int = exchange_config.get(
"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 leverage: The amount of leverage being used on the current trade
"""
return stake_amount
return stake_amount / leverage
# Dry-run methods
@ -771,12 +771,14 @@ class Exchange:
# Order handling
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']:
dry_order = self.create_dry_run_order(pair, ordertype, side, amount, rate)
return dry_order
if self.trading_mode != TradingMode.SPOT:
self._set_leverage(leverage, pair)
params = self._params.copy()
if time_in_force != 'gtc' and ordertype != 'market':
param = self._ft_has.get('time_in_force_parameter', '')
@ -1600,7 +1602,12 @@ class Exchange:
return 1.0
@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
have the same leverage on every trade

View File

@ -146,6 +146,7 @@ class Kraken(Exchange):
leverages = {}
for pair, market in self.markets.items():
leverages[pair] = [1]
info = market['info']
leverage_buy = info.get('leverage_buy', [])
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"
"for {pair}. Please notify freqtrade because this has never happened before"
)
if max(leverage_buy) < max(leverage_sell):
leverages[pair] = leverage_buy
if max(leverage_buy) <= max(leverage_sell):
leverages[pair] += [int(lev) for lev in leverage_buy]
else:
leverages[pair] = leverage_sell
leverages[pair] += [int(lev) for lev in leverage_sell]
else:
leverages[pair] = leverage_buy
leverages[pair] += [int(lev) for lev in leverage_buy]
self._leverage_brackets = leverages
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]))
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
anything in this function
Kraken set's the leverage as an option in the order object, so we need to
add it to params
"""
return
if leverage > 1.0:
self._params['leverage'] = leverage
else:
if 'leverage' in self._params:
del self._params['leverage']

View File

@ -4,6 +4,7 @@ from unittest.mock import MagicMock, PropertyMock
import ccxt
import pytest
from freqtrade.enums import TradingMode
from freqtrade.exceptions import DependencyException, InvalidOrderException, OperationalException
from tests.conftest import get_patched_exchange
from tests.exchange.test_exchange import ccxt_exceptionhandlers
@ -232,3 +233,25 @@ def test_fill_leverage_brackets_binance(default_conf, mocker):
"fill_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
)

View File

@ -2969,18 +2969,11 @@ def test_calculate_backoff(retrycount, max_retries, expected):
assert calculate_backoff(retrycount, max_retries) == expected
@pytest.mark.parametrize('exchange,stake_amount,leverage,min_stake_with_lev', [
('binance', 9.0, 3.0, 3.0),
('binance', 20.0, 5.0, 4.0),
('binance', 100.0, 100.0, 1.0),
# Kraken
('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)
@pytest.mark.parametrize('exchange', ['binance', 'kraken', 'ftx'])
@pytest.mark.parametrize('stake_amount,leverage,min_stake_with_lev', [
(9.0, 3.0, 3.0),
(20.0, 5.0, 4.0),
(100.0, 100.0, 1.0)
])
def test_apply_leverage_to_stake_amount(
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
@pytest.mark.parametrize("collateral", [
(Collateral.CROSS),
(Collateral.ISOLATED)
@pytest.mark.parametrize("exchange_name,trading_mode", [
("binance", TradingMode.FUTURES),
("ftx", TradingMode.MARGIN),
("ftx", TradingMode.FUTURES)
])
@pytest.mark.parametrize("exchange_name", [("ftx"), ("binance")])
def test_set_leverage(mocker, default_conf, exchange_name, collateral):
def test__set_leverage(mocker, default_conf, exchange_name, trading_mode):
api_mock = MagicMock()
api_mock.set_leverage = MagicMock()
@ -3010,10 +3003,11 @@ def test_set_leverage(mocker, default_conf, exchange_name, collateral):
default_conf,
api_mock,
exchange_name,
"set_leverage",
"_set_leverage",
"set_leverage",
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.ISOLATED)
])
@pytest.mark.parametrize("exchange_name", [("ftx"), ("binance")])
def test_set_margin_mode(mocker, default_conf, exchange_name, collateral):
def test_set_margin_mode(mocker, default_conf, collateral):
api_mock = MagicMock()
api_mock.set_margin_mode = MagicMock()
@ -3032,7 +3025,7 @@ def test_set_margin_mode(mocker, default_conf, exchange_name, collateral):
mocker,
default_conf,
api_mock,
exchange_name,
"binance",
"set_margin_mode",
"set_margin_mode",
pair="XRP/USDT",

View File

@ -1,9 +1,10 @@
from random import randint
from unittest.mock import MagicMock
from unittest.mock import MagicMock, PropertyMock
import ccxt
import pytest
from freqtrade.enums import TradingMode
from freqtrade.exceptions import DependencyException, InvalidOrderException
from freqtrade.exchange.common import API_FETCH_ORDER_RETRY_COUNT
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.fill_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
)

View File

@ -294,7 +294,28 @@ def test_fill_leverage_brackets_kraken(default_conf, mocker):
exchange.fill_leverage_brackets()
assert exchange._leverage_brackets == {
'BLK/BTC': ['2', '3'],
'TKN/BTC': ['2', '3', '4', '5'],
'ETH/BTC': ['2']
'BLK/BTC': [1, 2, 3],
'TKN/BTC': [1, 2, 3, 4, 5],
'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