Merge pull request #7491 from freqtrade/partial_close_leverage

Partial close leverage
This commit is contained in:
Matthias 2022-09-29 19:42:16 +02:00 committed by GitHub
commit 578da343dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 28 additions and 16 deletions

View File

@ -643,7 +643,7 @@ This callback is **not** called when there is an open order (either buy or sell)
Additional Buys are ignored once you have reached the maximum amount of extra buys that you have set on `max_entry_position_adjustment`, but the callback is called anyway looking for partial exits. Additional Buys are ignored once you have reached the maximum amount of extra buys that you have set on `max_entry_position_adjustment`, but the callback is called anyway looking for partial exits.
Position adjustments will always be applied in the direction of the trade, so a positive value will always increase your position (negative values will decrease your position), no matter if it's a long or short trade. Modifications to leverage are not possible. Position adjustments will always be applied in the direction of the trade, so a positive value will always increase your position (negative values will decrease your position), no matter if it's a long or short trade. Modifications to leverage are not possible, and the stake-amount is assumed to be before applying leverage.
!!! Note "About stake size" !!! Note "About stake size"
Using fixed stake size means it will be the amount used for the first order, just like without position adjustment. Using fixed stake size means it will be the amount used for the first order, just like without position adjustment.

View File

@ -597,7 +597,7 @@ class FreqtradeBot(LoggingMixin):
# We should decrease our position # We should decrease our position
amount = self.exchange.amount_to_contract_precision( amount = self.exchange.amount_to_contract_precision(
trade.pair, trade.pair,
abs(float(FtPrecise(stake_amount) / FtPrecise(current_exit_rate)))) abs(float(FtPrecise(stake_amount * trade.leverage) / FtPrecise(current_exit_rate))))
if amount > trade.amount: if amount > trade.amount:
# This is currently ineffective as remaining would become < min tradable # This is currently ineffective as remaining would become < min tradable
# Fixing this would require checking for 0.0 there - # Fixing this would require checking for 0.0 there -

View File

@ -540,7 +540,7 @@ class Backtesting:
if stake_amount is not None and stake_amount < 0.0: if stake_amount is not None and stake_amount < 0.0:
amount = amount_to_contract_precision( amount = amount_to_contract_precision(
abs(stake_amount) / current_rate, trade.amount_precision, abs(stake_amount * trade.leverage) / current_rate, trade.amount_precision,
self.precision_mode, trade.contract_size) self.precision_mode, trade.contract_size)
if amount == 0.0: if amount == 0.0:
return trade return trade

View File

@ -93,11 +93,16 @@ def test_backtest_position_adjustment(default_conf, fee, mocker, testdatadir) ->
t["close_rate"], 6) < round(ln.iloc[0]["high"], 6)) t["close_rate"], 6) < round(ln.iloc[0]["high"], 6))
def test_backtest_position_adjustment_detailed(default_conf, fee, mocker) -> None: @pytest.mark.parametrize('leverage', [
1, 2
])
def test_backtest_position_adjustment_detailed(default_conf, fee, mocker, leverage) -> None:
default_conf['use_exit_signal'] = False default_conf['use_exit_signal'] = False
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=10) mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=10)
mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf')) mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf'))
mocker.patch("freqtrade.exchange.Exchange.get_max_leverage", return_value=10)
patch_exchange(mocker) patch_exchange(mocker)
default_conf.update({ default_conf.update({
"stake_amount": 100.0, "stake_amount": 100.0,
@ -105,6 +110,7 @@ def test_backtest_position_adjustment_detailed(default_conf, fee, mocker) -> Non
"strategy": "StrategyTestV3" "strategy": "StrategyTestV3"
}) })
backtesting = Backtesting(default_conf) backtesting = Backtesting(default_conf)
backtesting._can_short = True
backtesting._set_strategy(backtesting.strategylist[0]) backtesting._set_strategy(backtesting.strategylist[0])
pair = 'XRP/USDT' pair = 'XRP/USDT'
row = [ row = [
@ -120,18 +126,19 @@ def test_backtest_position_adjustment_detailed(default_conf, fee, mocker) -> Non
'', # enter_tag '', # enter_tag
'', # exit_tag '', # exit_tag
] ]
backtesting.strategy.leverage = MagicMock(return_value=leverage)
trade = backtesting._enter_trade(pair, row=row, direction='long') trade = backtesting._enter_trade(pair, row=row, direction='long')
trade.orders[0].close_bt_order(row[0], trade) trade.orders[0].close_bt_order(row[0], trade)
assert trade assert trade
assert pytest.approx(trade.stake_amount) == 100.0 assert pytest.approx(trade.stake_amount) == 100.0
assert pytest.approx(trade.amount) == 47.61904762 assert pytest.approx(trade.amount) == 47.61904762 * leverage
assert len(trade.orders) == 1 assert len(trade.orders) == 1
backtesting.strategy.adjust_trade_position = MagicMock(return_value=None) backtesting.strategy.adjust_trade_position = MagicMock(return_value=None)
trade = backtesting._get_adjust_trade_entry_for_candle(trade, row) trade = backtesting._get_adjust_trade_entry_for_candle(trade, row)
assert trade assert trade
assert pytest.approx(trade.stake_amount) == 100.0 assert pytest.approx(trade.stake_amount) == 100.0
assert pytest.approx(trade.amount) == 47.61904762 assert pytest.approx(trade.amount) == 47.61904762 * leverage
assert len(trade.orders) == 1 assert len(trade.orders) == 1
# Increase position by 100 # Increase position by 100
backtesting.strategy.adjust_trade_position = MagicMock(return_value=100) backtesting.strategy.adjust_trade_position = MagicMock(return_value=100)
@ -140,7 +147,7 @@ def test_backtest_position_adjustment_detailed(default_conf, fee, mocker) -> Non
assert trade assert trade
assert pytest.approx(trade.stake_amount) == 200.0 assert pytest.approx(trade.stake_amount) == 200.0
assert pytest.approx(trade.amount) == 95.23809524 assert pytest.approx(trade.amount) == 95.23809524 * leverage
assert len(trade.orders) == 2 assert len(trade.orders) == 2
# Reduce by more than amount - no change to trade. # Reduce by more than amount - no change to trade.
@ -150,7 +157,7 @@ def test_backtest_position_adjustment_detailed(default_conf, fee, mocker) -> Non
assert trade assert trade
assert pytest.approx(trade.stake_amount) == 200.0 assert pytest.approx(trade.stake_amount) == 200.0
assert pytest.approx(trade.amount) == 95.23809524 assert pytest.approx(trade.amount) == 95.23809524 * leverage
assert len(trade.orders) == 2 assert len(trade.orders) == 2
assert trade.nr_of_successful_entries == 2 assert trade.nr_of_successful_entries == 2
@ -160,7 +167,7 @@ def test_backtest_position_adjustment_detailed(default_conf, fee, mocker) -> Non
assert trade assert trade
assert pytest.approx(trade.stake_amount) == 100.0 assert pytest.approx(trade.stake_amount) == 100.0
assert pytest.approx(trade.amount) == 47.61904762 assert pytest.approx(trade.amount) == 47.61904762 * leverage
assert len(trade.orders) == 3 assert len(trade.orders) == 3
assert trade.nr_of_successful_entries == 2 assert trade.nr_of_successful_entries == 2
assert trade.nr_of_successful_exits == 1 assert trade.nr_of_successful_exits == 1
@ -171,7 +178,7 @@ def test_backtest_position_adjustment_detailed(default_conf, fee, mocker) -> Non
assert trade assert trade
assert pytest.approx(trade.stake_amount) == 100.0 assert pytest.approx(trade.stake_amount) == 100.0
assert pytest.approx(trade.amount) == 47.61904762 assert pytest.approx(trade.amount) == 47.61904762 * leverage
assert len(trade.orders) == 3 assert len(trade.orders) == 3
assert trade.nr_of_successful_entries == 2 assert trade.nr_of_successful_entries == 2
assert trade.nr_of_successful_exits == 1 assert trade.nr_of_successful_exits == 1

View File

@ -2,7 +2,7 @@ from unittest.mock import MagicMock
import pytest import pytest
from freqtrade.enums import ExitCheckTuple, ExitType from freqtrade.enums import ExitCheckTuple, ExitType, TradingMode
from freqtrade.persistence import Trade from freqtrade.persistence import Trade
from freqtrade.persistence.models import Order from freqtrade.persistence.models import Order
from freqtrade.rpc.rpc import RPC from freqtrade.rpc.rpc import RPC
@ -455,10 +455,12 @@ def test_dca_order_adjust(default_conf_usdt, ticker_usdt, fee, mocker) -> None:
assert pytest.approx(trade.orders[-1].amount) == 61.538461232 assert pytest.approx(trade.orders[-1].amount) == 61.538461232
def test_dca_exiting(default_conf_usdt, ticker_usdt, fee, mocker, caplog) -> None: @pytest.mark.parametrize('leverage', [1, 2])
def test_dca_exiting(default_conf_usdt, ticker_usdt, fee, mocker, caplog, leverage) -> None:
default_conf_usdt['position_adjustment_enable'] = True default_conf_usdt['position_adjustment_enable'] = True
freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
freqtrade.trading_mode = TradingMode.FUTURES
mocker.patch.multiple( mocker.patch.multiple(
'freqtrade.exchange.Exchange', 'freqtrade.exchange.Exchange',
fetch_ticker=ticker_usdt, fetch_ticker=ticker_usdt,
@ -467,15 +469,17 @@ def test_dca_exiting(default_conf_usdt, ticker_usdt, fee, mocker, caplog) -> Non
price_to_precision=lambda s, x, y: y, price_to_precision=lambda s, x, y: y,
get_min_pair_stake_amount=MagicMock(return_value=10), get_min_pair_stake_amount=MagicMock(return_value=10),
) )
mocker.patch("freqtrade.exchange.Exchange.get_max_leverage", return_value=10)
patch_get_signal(freqtrade) patch_get_signal(freqtrade)
freqtrade.strategy.leverage = MagicMock(return_value=leverage)
freqtrade.enter_positions() freqtrade.enter_positions()
assert len(Trade.get_trades().all()) == 1 assert len(Trade.get_trades().all()) == 1
trade = Trade.get_trades().first() trade = Trade.get_trades().first()
assert len(trade.orders) == 1 assert len(trade.orders) == 1
assert pytest.approx(trade.stake_amount) == 60 assert pytest.approx(trade.stake_amount) == 60
assert pytest.approx(trade.amount) == 30.0 assert pytest.approx(trade.amount) == 30.0 * leverage
assert trade.open_rate == 2.0 assert trade.open_rate == 2.0
# Too small size # Too small size
@ -484,8 +488,9 @@ def test_dca_exiting(default_conf_usdt, ticker_usdt, fee, mocker, caplog) -> Non
trade = Trade.get_trades().first() trade = Trade.get_trades().first()
assert len(trade.orders) == 1 assert len(trade.orders) == 1
assert pytest.approx(trade.stake_amount) == 60 assert pytest.approx(trade.stake_amount) == 60
assert pytest.approx(trade.amount) == 30.0 assert pytest.approx(trade.amount) == 30.0 * leverage
assert log_has_re("Remaining amount of 1.6.* would be smaller than the minimum of 10.", caplog) assert log_has_re(
r"Remaining amount of \d\.\d+.* would be smaller than the minimum of 10.", caplog)
freqtrade.strategy.adjust_trade_position = MagicMock(return_value=-20) freqtrade.strategy.adjust_trade_position = MagicMock(return_value=-20)
@ -494,7 +499,7 @@ def test_dca_exiting(default_conf_usdt, ticker_usdt, fee, mocker, caplog) -> Non
assert len(trade.orders) == 2 assert len(trade.orders) == 2
assert trade.orders[-1].ft_order_side == 'sell' assert trade.orders[-1].ft_order_side == 'sell'
assert pytest.approx(trade.stake_amount) == 40.198 assert pytest.approx(trade.stake_amount) == 40.198
assert pytest.approx(trade.amount) == 20.099 assert pytest.approx(trade.amount) == 20.099 * leverage
assert trade.open_rate == 2.0 assert trade.open_rate == 2.0
assert trade.is_open assert trade.is_open
caplog.clear() caplog.clear()