Merge branch 'feat/short' into max-amount

This commit is contained in:
Sam Germain 2022-02-04 04:42:38 -06:00
commit dc6cb445fd
10 changed files with 187 additions and 40 deletions

View File

@ -9,3 +9,4 @@ class MarginMode(Enum):
""" """
CROSS = "cross" CROSS = "cross"
ISOLATED = "isolated" ISOLATED = "isolated"
NONE = ''

View File

@ -941,11 +941,10 @@ class Exchange:
side: str, side: str,
amount: float, amount: float,
rate: float, rate: float,
leverage: float,
reduceOnly: bool = False, reduceOnly: bool = False,
leverage: float = 1.0,
time_in_force: str = 'gtc', time_in_force: str = 'gtc',
) -> Dict: ) -> Dict:
# TODO-lev: remove default for leverage
if self._config['dry_run']: if self._config['dry_run']:
dry_order = self.create_dry_run_order(pair, ordertype, side, amount, rate, leverage) dry_order = self.create_dry_run_order(pair, ordertype, side, amount, rate, leverage)
return dry_order return dry_order

View File

@ -605,7 +605,7 @@ class FreqtradeBot(LoggingMixin):
self, self,
pair: str, pair: str,
open_rate: float, open_rate: float,
amount: float, amount: float, # quote currency, includes leverage
leverage: float, leverage: float,
is_short: bool is_short: bool
) -> Tuple[float, Optional[float]]: ) -> Tuple[float, Optional[float]]:
@ -1285,7 +1285,8 @@ class FreqtradeBot(LoggingMixin):
# to the order dict acquired before cancelling. # to the order dict acquired before cancelling.
# we need to fall back to the values from order if corder does not contain these keys. # we need to fall back to the values from order if corder does not contain these keys.
trade.amount = filled_amount trade.amount = filled_amount
# TODO-lev: Check edge cases, we don't want to make leverage > 1.0 if we don't have to # * Check edge cases, we don't want to make leverage > 1.0 if we don't have to
# * (for leverage modes which aren't isolated futures)
trade.stake_amount = trade.amount * trade.open_rate trade.stake_amount = trade.amount * trade.open_rate
self.update_trade_state(trade, trade.open_order_id, corder) self.update_trade_state(trade, trade.open_order_id, corder)
@ -1352,13 +1353,14 @@ class FreqtradeBot(LoggingMixin):
:return: amount to sell :return: amount to sell
:raise: DependencyException: if available balance is not within 2% of the available amount. :raise: DependencyException: if available balance is not within 2% of the available amount.
""" """
# TODO-lev Maybe update?
# Update wallets to ensure amounts tied up in a stoploss is now free! # Update wallets to ensure amounts tied up in a stoploss is now free!
self.wallets.update() self.wallets.update()
trade_base_currency = self.exchange.get_pair_base_currency(pair) trade_base_currency = self.exchange.get_pair_base_currency(pair)
wallet_amount = self.wallets.get_free(trade_base_currency) wallet_amount = self.wallets.get_free(trade_base_currency)
logger.debug(f"{pair} - Wallet: {wallet_amount} - Trade-amount: {amount}") logger.debug(f"{pair} - Wallet: {wallet_amount} - Trade-amount: {amount}")
if wallet_amount >= amount: # TODO-lev: Get wallet amount + value of positions
if wallet_amount >= amount or self.trading_mode == TradingMode.FUTURES:
# A safe exit amount isn't needed for futures, you can just exit/close the position
return amount return amount
elif wallet_amount > amount * 0.98: elif wallet_amount > amount * 0.98:
logger.info(f"{pair} - Falling back to wallet-amount {wallet_amount} -> {amount}.") logger.info(f"{pair} - Falling back to wallet-amount {wallet_amount} -> {amount}.")
@ -1436,6 +1438,7 @@ class FreqtradeBot(LoggingMixin):
side=trade.exit_side, side=trade.exit_side,
amount=amount, amount=amount,
rate=limit, rate=limit,
leverage=trade.leverage,
reduceOnly=self.trading_mode == TradingMode.FUTURES, reduceOnly=self.trading_mode == TradingMode.FUTURES,
time_in_force=time_in_force time_in_force=time_in_force
) )

View File

@ -333,8 +333,12 @@ class Backtesting:
df_analyzed.loc[:, col] = 0 if not tag_col else None df_analyzed.loc[:, col] = 0 if not tag_col else None
# Update dataprovider cache # Update dataprovider cache
self.dataprovider._set_cached_df(pair, self.timeframe, df_analyzed, CandleType.SPOT) self.dataprovider._set_cached_df(
# TODO-lev: Candle-type should be conditional, either "spot" or futures pair,
self.timeframe,
df_analyzed,
self.config['candle_type_def']
)
df_analyzed = df_analyzed.drop(df_analyzed.head(1).index) df_analyzed = df_analyzed.drop(df_analyzed.head(1).index)
@ -499,7 +503,7 @@ class Backtesting:
sell_candle_time: datetime = sell_row[DATE_IDX].to_pydatetime() sell_candle_time: datetime = sell_row[DATE_IDX].to_pydatetime()
if self.trading_mode == TradingMode.FUTURES: if self.trading_mode == TradingMode.FUTURES:
# TODO-lev: Other fees / liquidation price? # TODO-lev: liquidation price?
trade.funding_fees = self.exchange.calculate_funding_fees( trade.funding_fees = self.exchange.calculate_funding_fees(
self.futures_data[trade.pair], self.futures_data[trade.pair],
amount=trade.amount, amount=trade.amount,

View File

@ -569,7 +569,6 @@ class LocalTrade():
payment = "BUY" if self.is_short else "SELL" payment = "BUY" if self.is_short else "SELL"
# * On margin shorts, you buy a little bit more than the amount (amount + interest) # * On margin shorts, you buy a little bit more than the amount (amount + interest)
logger.info(f'{order_type.upper()}_{payment} has been fulfilled for {self}.') logger.info(f'{order_type.upper()}_{payment} has been fulfilled for {self}.')
# TODO-lev: Double check this
self.close(safe_value_fallback(order, 'average', 'price')) self.close(safe_value_fallback(order, 'average', 'price'))
elif order_type in ('stop_loss_limit', 'stop-loss', 'stop-loss-limit', 'stop'): elif order_type in ('stop_loss_limit', 'stop-loss', 'stop-loss-limit', 'stop'):
self.stoploss_order_id = None self.stoploss_order_id = None

View File

@ -431,7 +431,6 @@ def generate_candlestick_graph(pair: str, data: pd.DataFrame, trades: pd.DataFra
) )
fig.add_trace(candles, 1, 1) fig.add_trace(candles, 1, 1)
# TODO-lev: Needs short equivalent
if 'enter_long' in data.columns: if 'enter_long' in data.columns:
df_buy = data[data['enter_long'] == 1] df_buy = data[data['enter_long'] == 1]
if len(df_buy) > 0: if len(df_buy) > 0:

View File

@ -1227,7 +1227,8 @@ def test_buy_dry_run(default_conf, mocker):
exchange = get_patched_exchange(mocker, default_conf) exchange = get_patched_exchange(mocker, default_conf)
order = exchange.create_order(pair='ETH/BTC', ordertype='limit', side="buy", order = exchange.create_order(pair='ETH/BTC', ordertype='limit', side="buy",
amount=1, rate=200, time_in_force='gtc') amount=1, rate=200, leverage=1.0,
time_in_force='gtc')
assert 'id' in order assert 'id' in order
assert 'dry_run_buy_' in order['id'] assert 'dry_run_buy_' in order['id']
@ -1252,7 +1253,8 @@ def test_buy_prod(default_conf, mocker, exchange_name):
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
order = exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="buy", order = exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="buy",
amount=1, rate=200, time_in_force=time_in_force) amount=1, rate=200, leverage=1.0,
time_in_force=time_in_force)
assert 'id' in order assert 'id' in order
assert 'info' in order assert 'info' in order
@ -1271,7 +1273,9 @@ def test_buy_prod(default_conf, mocker, exchange_name):
side="buy", side="buy",
amount=1, amount=1,
rate=200, rate=200,
time_in_force=time_in_force) leverage=1.0,
time_in_force=time_in_force
)
assert api_mock.create_order.call_args[0][0] == 'ETH/BTC' assert api_mock.create_order.call_args[0][0] == 'ETH/BTC'
assert api_mock.create_order.call_args[0][1] == order_type assert api_mock.create_order.call_args[0][1] == order_type
assert api_mock.create_order.call_args[0][2] == 'buy' assert api_mock.create_order.call_args[0][2] == 'buy'
@ -1283,31 +1287,36 @@ def test_buy_prod(default_conf, mocker, exchange_name):
api_mock.create_order = MagicMock(side_effect=ccxt.InsufficientFunds("Not enough funds")) api_mock.create_order = MagicMock(side_effect=ccxt.InsufficientFunds("Not enough funds"))
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.create_order(pair='ETH/BTC', ordertype=order_type, side="buy", exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="buy",
amount=1, rate=200, time_in_force=time_in_force) amount=1, rate=200, leverage=1.0,
time_in_force=time_in_force)
with pytest.raises(DependencyException): with pytest.raises(DependencyException):
api_mock.create_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found")) api_mock.create_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found"))
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.create_order(pair='ETH/BTC', ordertype='limit', side="buy", exchange.create_order(pair='ETH/BTC', ordertype='limit', side="buy",
amount=1, rate=200, time_in_force=time_in_force) amount=1, rate=200, leverage=1.0,
time_in_force=time_in_force)
with pytest.raises(DependencyException): with pytest.raises(DependencyException):
api_mock.create_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found")) api_mock.create_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found"))
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.create_order(pair='ETH/BTC', ordertype='market', side="buy", exchange.create_order(pair='ETH/BTC', ordertype='market', side="buy",
amount=1, rate=200, time_in_force=time_in_force) amount=1, rate=200, leverage=1.0,
time_in_force=time_in_force)
with pytest.raises(TemporaryError): with pytest.raises(TemporaryError):
api_mock.create_order = MagicMock(side_effect=ccxt.NetworkError("Network disconnect")) api_mock.create_order = MagicMock(side_effect=ccxt.NetworkError("Network disconnect"))
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.create_order(pair='ETH/BTC', ordertype=order_type, side="buy", exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="buy",
amount=1, rate=200, time_in_force=time_in_force) amount=1, rate=200, leverage=1.0,
time_in_force=time_in_force)
with pytest.raises(OperationalException): with pytest.raises(OperationalException):
api_mock.create_order = MagicMock(side_effect=ccxt.BaseError("Unknown error")) api_mock.create_order = MagicMock(side_effect=ccxt.BaseError("Unknown error"))
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.create_order(pair='ETH/BTC', ordertype=order_type, side="buy", exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="buy",
amount=1, rate=200, time_in_force=time_in_force) amount=1, rate=200, leverage=1.0,
time_in_force=time_in_force)
@pytest.mark.parametrize("exchange_name", EXCHANGES) @pytest.mark.parametrize("exchange_name", EXCHANGES)
@ -1331,7 +1340,8 @@ def test_buy_considers_time_in_force(default_conf, mocker, exchange_name):
time_in_force = 'ioc' time_in_force = 'ioc'
order = exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="buy", order = exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="buy",
amount=1, rate=200, time_in_force=time_in_force) amount=1, rate=200, leverage=1.0,
time_in_force=time_in_force)
assert 'id' in order assert 'id' in order
assert 'info' in order assert 'info' in order
@ -1348,7 +1358,8 @@ def test_buy_considers_time_in_force(default_conf, mocker, exchange_name):
time_in_force = 'ioc' time_in_force = 'ioc'
order = exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="buy", order = exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="buy",
amount=1, rate=200, time_in_force=time_in_force) amount=1, rate=200, leverage=1.0,
time_in_force=time_in_force)
assert 'id' in order assert 'id' in order
assert 'info' in order assert 'info' in order
@ -1367,7 +1378,7 @@ def test_sell_dry_run(default_conf, mocker):
exchange = get_patched_exchange(mocker, default_conf) exchange = get_patched_exchange(mocker, default_conf)
order = exchange.create_order(pair='ETH/BTC', ordertype='limit', order = exchange.create_order(pair='ETH/BTC', ordertype='limit',
side="sell", amount=1, rate=200) side="sell", amount=1, rate=200, leverage=1.0)
assert 'id' in order assert 'id' in order
assert 'dry_run_sell_' in order['id'] assert 'dry_run_sell_' in order['id']
@ -1392,7 +1403,7 @@ def test_sell_prod(default_conf, mocker, exchange_name):
exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name) exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
order = exchange.create_order(pair='ETH/BTC', ordertype=order_type, order = exchange.create_order(pair='ETH/BTC', ordertype=order_type,
side="sell", amount=1, rate=200) side="sell", amount=1, rate=200, leverage=1.0)
assert 'id' in order assert 'id' in order
assert 'info' in order assert 'info' in order
@ -1406,7 +1417,8 @@ def test_sell_prod(default_conf, mocker, exchange_name):
api_mock.create_order.reset_mock() api_mock.create_order.reset_mock()
order_type = 'limit' order_type = 'limit'
order = exchange.create_order(pair='ETH/BTC', ordertype=order_type, order = exchange.create_order(pair='ETH/BTC', ordertype=order_type,
side="sell", amount=1, rate=200) side="sell", amount=1, rate=200,
leverage=1.0)
assert api_mock.create_order.call_args[0][0] == 'ETH/BTC' assert api_mock.create_order.call_args[0][0] == 'ETH/BTC'
assert api_mock.create_order.call_args[0][1] == order_type assert api_mock.create_order.call_args[0][1] == order_type
assert api_mock.create_order.call_args[0][2] == 'sell' assert api_mock.create_order.call_args[0][2] == 'sell'
@ -1417,28 +1429,33 @@ def test_sell_prod(default_conf, mocker, exchange_name):
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, id=exchange_name) exchange = get_patched_exchange(mocker, default_conf, api_mock, id=exchange_name)
exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="sell", amount=1, rate=200) exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="sell", amount=1, rate=200,
leverage=1.0)
with pytest.raises(DependencyException): with pytest.raises(DependencyException):
api_mock.create_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found")) api_mock.create_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found"))
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.create_order(pair='ETH/BTC', ordertype='limit', side="sell", amount=1, rate=200) exchange.create_order(pair='ETH/BTC', ordertype='limit', side="sell", amount=1, rate=200,
leverage=1.0)
# Market orders don't require price, so the behaviour is slightly different # Market orders don't require price, so the behaviour is slightly different
with pytest.raises(DependencyException): with pytest.raises(DependencyException):
api_mock.create_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found")) api_mock.create_order = MagicMock(side_effect=ccxt.InvalidOrder("Order not found"))
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.create_order(pair='ETH/BTC', ordertype='market', side="sell", amount=1, rate=200) exchange.create_order(pair='ETH/BTC', ordertype='market', side="sell", amount=1, rate=200,
leverage=1.0)
with pytest.raises(TemporaryError): with pytest.raises(TemporaryError):
api_mock.create_order = MagicMock(side_effect=ccxt.NetworkError("No Connection")) api_mock.create_order = MagicMock(side_effect=ccxt.NetworkError("No Connection"))
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.create_order(pair='ETH/BTC', ordertype=order_type, side="sell", amount=1, rate=200) exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="sell", amount=1, rate=200,
leverage=1.0)
with pytest.raises(OperationalException): with pytest.raises(OperationalException):
api_mock.create_order = MagicMock(side_effect=ccxt.BaseError("DeadBeef")) api_mock.create_order = MagicMock(side_effect=ccxt.BaseError("DeadBeef"))
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.create_order(pair='ETH/BTC', ordertype=order_type, side="sell", amount=1, rate=200) exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="sell", amount=1, rate=200,
leverage=1.0)
@pytest.mark.parametrize("exchange_name", EXCHANGES) @pytest.mark.parametrize("exchange_name", EXCHANGES)
@ -1462,7 +1479,8 @@ def test_sell_considers_time_in_force(default_conf, mocker, exchange_name):
time_in_force = 'ioc' time_in_force = 'ioc'
order = exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="sell", order = exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="sell",
amount=1, rate=200, time_in_force=time_in_force) amount=1, rate=200, leverage=1.0,
time_in_force=time_in_force)
assert 'id' in order assert 'id' in order
assert 'info' in order assert 'info' in order
@ -1478,7 +1496,8 @@ def test_sell_considers_time_in_force(default_conf, mocker, exchange_name):
order_type = 'market' order_type = 'market'
time_in_force = 'ioc' time_in_force = 'ioc'
order = exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="sell", order = exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="sell",
amount=1, rate=200, time_in_force=time_in_force) amount=1, rate=200, leverage=1.0,
time_in_force=time_in_force)
assert 'id' in order assert 'id' in order
assert 'info' in order assert 'info' in order
@ -3792,9 +3811,10 @@ def test__fetch_and_calculate_funding_fees_datetime_called(
d1 = datetime.strptime("2021-09-01 00:00:00 +0000", '%Y-%m-%d %H:%M:%S %z') d1 = datetime.strptime("2021-09-01 00:00:00 +0000", '%Y-%m-%d %H:%M:%S %z')
time_machine.move_to("2021-09-01 08:00:00 +00:00") time_machine.move_to("2021-09-01 08:00:00 +00:00")
# TODO-lev: test this for longs
funding_fees = exchange._fetch_and_calculate_funding_fees('ADA/USDT', 30.0, True, d1) funding_fees = exchange._fetch_and_calculate_funding_fees('ADA/USDT', 30.0, True, d1)
assert funding_fees == expected_fees assert funding_fees == expected_fees
funding_fees = exchange._fetch_and_calculate_funding_fees('ADA/USDT', 30.0, False, d1)
assert funding_fees == 0 - expected_fees
@pytest.mark.parametrize('pair,expected_size,trading_mode', [ @pytest.mark.parametrize('pair,expected_size,trading_mode', [

View File

@ -32,8 +32,15 @@ def test_buy_kraken_trading_agreement(default_conf, mocker):
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="kraken") exchange = get_patched_exchange(mocker, default_conf, api_mock, id="kraken")
order = exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="buy", order = exchange.create_order(
amount=1, rate=200, time_in_force=time_in_force) pair='ETH/BTC',
ordertype=order_type,
side="buy",
amount=1,
rate=200,
leverage=1.0,
time_in_force=time_in_force
)
assert 'id' in order assert 'id' in order
assert 'info' in order assert 'info' in order
@ -66,7 +73,7 @@ def test_sell_kraken_trading_agreement(default_conf, mocker):
exchange = get_patched_exchange(mocker, default_conf, api_mock, id="kraken") exchange = get_patched_exchange(mocker, default_conf, api_mock, id="kraken")
order = exchange.create_order(pair='ETH/BTC', ordertype=order_type, order = exchange.create_order(pair='ETH/BTC', ordertype=order_type,
side="sell", amount=1, rate=200) side="sell", amount=1, rate=200, leverage=1.0)
assert 'id' in order assert 'id' in order
assert 'info' in order assert 'info' in order

View File

@ -169,7 +169,6 @@ class StrategyTestV3(IStrategy):
), ),
'exit_short'] = 1 'exit_short'] = 1
# TODO-lev: Add short logic
return dataframe return dataframe
def leverage(self, pair: str, current_time: datetime, current_rate: float, def leverage(self, pair: str, current_time: datetime, current_rate: float,

View File

@ -4799,9 +4799,125 @@ def test_get_valid_price(mocker, default_conf_usdt) -> None:
assert valid_price_at_min_alwd < proposed_price assert valid_price_at_min_alwd < proposed_price
def test_leverage_prep(): @pytest.mark.parametrize(
# TODO-lev "is_short,trading_mode,exchange_name,margin_mode,leverage,open_rate,amount,expected_liq", [
return (False, 'spot', 'binance', '', 5.0, 10.0, 1.0, None),
(True, 'spot', 'binance', '', 5.0, 10.0, 1.0, None),
(False, 'spot', 'gateio', '', 5.0, 10.0, 1.0, None),
(True, 'spot', 'gateio', '', 5.0, 10.0, 1.0, None),
(False, 'spot', 'okex', '', 5.0, 10.0, 1.0, None),
(True, 'spot', 'okex', '', 5.0, 10.0, 1.0, None),
# Binance, short
(True, 'futures', 'binance', 'isolated', 5.0, 10.0, 1.0, 11.89108910891089),
(True, 'futures', 'binance', 'isolated', 3.0, 10.0, 1.0, 13.211221122079207),
(True, 'futures', 'binance', 'isolated', 5.0, 8.0, 1.0, 9.514851485148514),
(True, 'futures', 'binance', 'isolated', 5.0, 10.0, 0.6, 12.557755775577558),
# Binance, long
(False, 'futures', 'binance', 'isolated', 5, 10, 1.0, 8.070707070707071),
(False, 'futures', 'binance', 'isolated', 5, 8, 1.0, 6.454545454545454),
(False, 'futures', 'binance', 'isolated', 3, 10, 1.0, 6.717171717171718),
(False, 'futures', 'binance', 'isolated', 5, 10, 0.6, 7.39057239057239),
# Gateio/okex, short
(True, 'futures', 'gateio', 'isolated', 5, 10, 1.0, 11.87413417771621),
(True, 'futures', 'gateio', 'isolated', 5, 10, 2.0, 11.87413417771621),
(True, 'futures', 'gateio', 'isolated', 3, 10, 1.0, 13.476180850346978),
(True, 'futures', 'gateio', 'isolated', 5, 8, 1.0, 9.499307342172967),
# Gateio/okex, long
(False, 'futures', 'gateio', 'isolated', 5.0, 10.0, 1.0, 8.085708510208207),
(False, 'futures', 'gateio', 'isolated', 3.0, 10.0, 1.0, 6.738090425173506),
# (True, 'futures', 'okex', 'isolated', 11.87413417771621),
# (False, 'futures', 'okex', 'isolated', 8.085708510208207),
]
)
def test_leverage_prep(
mocker,
default_conf_usdt,
is_short,
trading_mode,
exchange_name,
margin_mode,
leverage,
open_rate,
amount,
expected_liq,
):
"""
position = 0.2 * 5
wb: wallet balance (stake_amount if isolated)
cum_b: maintenance amount
side_1: -1 if is_short else 1
ep1: entry price
mmr_b: maintenance margin ratio
Binance, Short
leverage = 5, open_rate = 10, amount = 1.0
((wb + cum_b) - (side_1 * position * ep1)) / ((position * mmr_b) - (side_1 * position))
((2 + 0.01) - ((-1) * 1 * 10)) / ((1 * 0.01) - ((-1) * 1)) = 11.89108910891089
leverage = 3, open_rate = 10, amount = 1.0
((3.3333333333 + 0.01) - ((-1) * 1.0 * 10)) / ((1.0 * 0.01) - ((-1) * 1.0)) = 13.2112211220
leverage = 5, open_rate = 8, amount = 1.0
((1.6 + 0.01) - ((-1) * 1 * 8)) / ((1 * 0.01) - ((-1) * 1)) = 9.514851485148514
leverage = 5, open_rate = 10, amount = 0.6
((1.6 + 0.01) - ((-1) * 0.6 * 10)) / ((0.6 * 0.01) - ((-1) * 0.6)) = 12.557755775577558
Binance, Long
leverage = 5, open_rate = 10, amount = 1.0
((wb + cum_b) - (side_1 * position * ep1)) / ((position * mmr_b) - (side_1 * position))
((2 + 0.01) - (1 * 1 * 10)) / ((1 * 0.01) - (1 * 1)) = 8.070707070707071
leverage = 5, open_rate = 8, amount = 1.0
((1.6 + 0.01) - (1 * 1 * 8)) / ((1 * 0.01) - (1 * 1)) = 6.454545454545454
leverage = 3, open_rate = 10, amount = 1.0
((2 + 0.01) - (1 * 0.6 * 10)) / ((0.6 * 0.01) - (1 * 0.6)) = 6.717171717171718
leverage = 5, open_rate = 10, amount = 0.6
((1.6 + 0.01) - (1 * 0.6 * 10)) / ((0.6 * 0.01) - (1 * 0.6)) = 7.39057239057239
Gateio/Okex, Short
leverage = 5, open_rate = 10, amount = 1.0
(open_rate + (wallet_balance / position)) / (1 + (mm_ratio + taker_fee_rate))
(10 + (2 / 1.0)) / (1 + (0.01 + 0.0006)) = 11.87413417771621
leverage = 5, open_rate = 10, amount = 2.0
(10 + (4 / 2.0)) / (1 + (0.01 + 0.0006)) = 11.87413417771621
leverage = 3, open_rate = 10, amount = 1.0
(10 + (3.3333333333333 / 1.0)) / (1 - (0.01 + 0.0006)) = 13.476180850346978
leverage = 5, open_rate = 8, amount = 1.0
(8 + (1.6 / 1.0)) / (1 + (0.01 + 0.0006)) = 9.499307342172967
Gateio/Okex, Long
leverage = 5, open_rate = 10, amount = 1.0
(open_rate - (wallet_balance / position)) / (1 - (mm_ratio + taker_fee_rate))
(10 - (2 / 1)) / (1 - (0.01 + 0.0006)) = 8.085708510208207
leverage = 5, open_rate = 10, amount = 2.0
(10 - (4 / 2.0)) / (1 + (0.01 + 0.0006)) = 7.916089451810806
leverage = 3, open_rate = 10, amount = 1.0
(10 - (3.333333333333333333 / 1.0)) / (1 - (0.01 + 0.0006)) = 6.738090425173506
leverage = 5, open_rate = 8, amount = 1.0
(8 - (1.6 / 1.0)) / (1 + (0.01 + 0.0006)) = 6.332871561448645
"""
default_conf_usdt['trading_mode'] = trading_mode
default_conf_usdt['exchange']['name'] = exchange_name
default_conf_usdt['margin_mode'] = margin_mode
mocker.patch('freqtrade.exchange.Gateio.validate_ordertypes')
patch_RPCManager(mocker)
patch_exchange(mocker, id=exchange_name)
freqtrade = FreqtradeBot(default_conf_usdt)
freqtrade.exchange.get_maintenance_ratio_and_amt = MagicMock(return_value=(0.01, 0.01))
freqtrade.exchange.name = exchange_name
# default_conf_usdt.update({
# "dry_run": False,
# })
(interest, liq) = freqtrade.leverage_prep(
pair='ETH/USDT:USDT',
open_rate=open_rate,
amount=amount,
leverage=leverage,
is_short=is_short,
)
assert interest == 0.0
if expected_liq is None:
assert liq is None
else:
isclose(expected_liq, liq)
@pytest.mark.parametrize('trading_mode,calls,t1,t2', [ @pytest.mark.parametrize('trading_mode,calls,t1,t2', [