Merge pull request #7491 from freqtrade/partial_close_leverage
Partial close leverage
This commit is contained in:
commit
578da343dc
@ -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.
|
||||||
|
@ -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 -
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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()
|
||||||
|
Loading…
Reference in New Issue
Block a user