Merge pull request #1034 from freqtrade/feat/positive_sl_limit
add offset for positive trailing stop loss
This commit is contained in:
commit
187e039a58
@ -7,6 +7,7 @@
|
|||||||
"ticker_interval": "5m",
|
"ticker_interval": "5m",
|
||||||
"trailing_stop": false,
|
"trailing_stop": false,
|
||||||
"trailing_stop_positive": 0.005,
|
"trailing_stop_positive": 0.005,
|
||||||
|
"trailing_stop_positive_offset": 0.0051,
|
||||||
"minimal_roi": {
|
"minimal_roi": {
|
||||||
"40": 0.0,
|
"40": 0.0,
|
||||||
"30": 0.01,
|
"30": 0.01,
|
||||||
|
@ -27,6 +27,7 @@ The table below will list all configuration parameters.
|
|||||||
| `stoploss` | -0.10 | No | Value of the stoploss in percent used by the bot. More information below. If set, this parameter will override `stoploss` from your strategy file.
|
| `stoploss` | -0.10 | No | Value of the stoploss in percent used by the bot. More information below. If set, this parameter will override `stoploss` from your strategy file.
|
||||||
| `trailing_stoploss` | false | No | Enables trailing stop-loss (based on `stoploss` in either configuration or strategy file).
|
| `trailing_stoploss` | false | No | Enables trailing stop-loss (based on `stoploss` in either configuration or strategy file).
|
||||||
| `trailing_stoploss_positve` | 0 | No | Changes stop-loss once profit has been reached.
|
| `trailing_stoploss_positve` | 0 | No | Changes stop-loss once profit has been reached.
|
||||||
|
| `trailing_stoploss_positve_offset` | 0 | No | Offset on when to apply `trailing_stoploss_positive`. Percentage value which should be positive.
|
||||||
| `unfilledtimeout.buy` | 10 | Yes | How long (in minutes) the bot will wait for an unfilled buy order to complete, after which the order will be cancelled.
|
| `unfilledtimeout.buy` | 10 | Yes | How long (in minutes) the bot will wait for an unfilled buy order to complete, after which the order will be cancelled.
|
||||||
| `unfilledtimeout.sell` | 10 | Yes | How long (in minutes) the bot will wait for an unfilled sell order to complete, after which the order will be cancelled.
|
| `unfilledtimeout.sell` | 10 | Yes | How long (in minutes) the bot will wait for an unfilled sell order to complete, after which the order will be cancelled.
|
||||||
| `bid_strategy.ask_last_balance` | 0.0 | Yes | Set the bidding price. More information below.
|
| `bid_strategy.ask_last_balance` | 0.0 | Yes | Set the bidding price. More information below.
|
||||||
|
@ -35,14 +35,17 @@ basically what this means is that your stop loss will be adjusted to be always b
|
|||||||
|
|
||||||
### Custom positive loss
|
### Custom positive loss
|
||||||
|
|
||||||
Due to demand, it is possible to have a default stop loss, when you are in the red with your buy, but once your buy turns positive,
|
Due to demand, it is possible to have a default stop loss, when you are in the red with your buy, but once your profit surpasses a certain percentage,
|
||||||
the system will utilize a new stop loss, which can be a different value. For example your default stop loss is 5%, but once you are in the
|
the system will utilize a new stop loss, which can be a different value. For example your default stop loss is 5%, but once you have 1.1% profit,
|
||||||
black, it will be changed to be only a 1% stop loss
|
it will be changed to be only a 1% stop loss, which trails the green candles until it goes below them.
|
||||||
|
|
||||||
This can be configured in the main configuration file and requires `"trailing_stop": true` to be set to true.
|
Both values can be configured in the main configuration file and requires `"trailing_stop": true` to be set to true.
|
||||||
|
|
||||||
``` json
|
``` json
|
||||||
"trailing_stop_positive": 0.01,
|
"trailing_stop_positive": 0.01,
|
||||||
|
"trailing_stop_positive_offset": 0.011,
|
||||||
```
|
```
|
||||||
|
|
||||||
The 0.01 would translate to a 1% stop loss, once you hit profit.
|
The 0.01 would translate to a 1% stop loss, once you hit 1.1% profit.
|
||||||
|
|
||||||
|
You should also make sure to have this value higher than your minimal ROI, otherwise minimal ROI will apply first and sell your trade.
|
||||||
|
@ -63,6 +63,7 @@ CONF_SCHEMA = {
|
|||||||
'stoploss': {'type': 'number', 'maximum': 0, 'exclusiveMaximum': True},
|
'stoploss': {'type': 'number', 'maximum': 0, 'exclusiveMaximum': True},
|
||||||
'trailing_stop': {'type': 'boolean'},
|
'trailing_stop': {'type': 'boolean'},
|
||||||
'trailing_stop_positive': {'type': 'number', 'minimum': 0, 'maximum': 1},
|
'trailing_stop_positive': {'type': 'number', 'minimum': 0, 'maximum': 1},
|
||||||
|
'trailing_stop_positive_offset': {'type': 'number', 'minimum': 0, 'maximum': 1},
|
||||||
'unfilledtimeout': {
|
'unfilledtimeout': {
|
||||||
'type': 'object',
|
'type': 'object',
|
||||||
'properties': {
|
'properties': {
|
||||||
|
@ -200,6 +200,7 @@ class IStrategy(ABC):
|
|||||||
"""
|
"""
|
||||||
Based on current profit of the trade and configured (trailing) stoploss,
|
Based on current profit of the trade and configured (trailing) stoploss,
|
||||||
decides to sell or not
|
decides to sell or not
|
||||||
|
:param current_profit: current profit in percent
|
||||||
"""
|
"""
|
||||||
|
|
||||||
trailing_stop = self.config.get('trailing_stop', False)
|
trailing_stop = self.config.get('trailing_stop', False)
|
||||||
@ -227,12 +228,15 @@ class IStrategy(ABC):
|
|||||||
# check if we have a special stop loss for positive condition
|
# check if we have a special stop loss for positive condition
|
||||||
# and if profit is positive
|
# and if profit is positive
|
||||||
stop_loss_value = self.stoploss
|
stop_loss_value = self.stoploss
|
||||||
if 'trailing_stop_positive' in self.config and current_profit > 0:
|
sl_offset = self.config.get('trailing_stop_positive_offset', 0.0)
|
||||||
|
|
||||||
|
if 'trailing_stop_positive' in self.config and current_profit > sl_offset:
|
||||||
|
|
||||||
# Ignore mypy error check in configuration that this is a float
|
# Ignore mypy error check in configuration that this is a float
|
||||||
stop_loss_value = self.config.get('trailing_stop_positive') # type: ignore
|
stop_loss_value = self.config.get('trailing_stop_positive') # type: ignore
|
||||||
logger.debug(f"using positive stop loss mode: {stop_loss_value} "
|
logger.debug(f"using positive stop loss mode: {stop_loss_value} "
|
||||||
f"since we have profit {current_profit}")
|
f"with offset {sl_offset:.4g} "
|
||||||
|
f"since we have profit {current_profit:.4f}%")
|
||||||
|
|
||||||
trade.adjust_stop_loss(current_rate, stop_loss_value)
|
trade.adjust_stop_loss(current_rate, stop_loss_value)
|
||||||
|
|
||||||
|
@ -1814,7 +1814,71 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order, fee, markets
|
|||||||
}))
|
}))
|
||||||
# stop-loss not reached, adjusted stoploss
|
# stop-loss not reached, adjusted stoploss
|
||||||
assert freqtrade.handle_trade(trade) is False
|
assert freqtrade.handle_trade(trade) is False
|
||||||
assert log_has(f'using positive stop loss mode: 0.01 since we have profit 0.26662643',
|
assert log_has(f'using positive stop loss mode: 0.01 with offset 0 '
|
||||||
|
f'since we have profit 0.2666%',
|
||||||
|
caplog.record_tuples)
|
||||||
|
assert log_has(f'adjusted stop loss', caplog.record_tuples)
|
||||||
|
assert trade.stop_loss == 0.0000138501
|
||||||
|
|
||||||
|
mocker.patch('freqtrade.exchange.Exchange.get_ticker',
|
||||||
|
MagicMock(return_value={
|
||||||
|
'bid': buy_price + 0.000002,
|
||||||
|
'ask': buy_price + 0.000002,
|
||||||
|
'last': buy_price + 0.000002
|
||||||
|
}))
|
||||||
|
# Lower price again (but still positive)
|
||||||
|
assert freqtrade.handle_trade(trade) is True
|
||||||
|
assert log_has(
|
||||||
|
f'HIT STOP: current price at {buy_price + 0.000002:.6f}, '
|
||||||
|
f'stop loss is {trade.stop_loss:.6f}, '
|
||||||
|
f'initial stop loss was at 0.000010, trade opened at 0.000011', caplog.record_tuples)
|
||||||
|
|
||||||
|
|
||||||
|
def test_trailing_stop_loss_offset(default_conf, limit_buy_order, fee, caplog, mocker) -> None:
|
||||||
|
"""
|
||||||
|
Test sell_profit_only feature when enabled and we have a loss
|
||||||
|
"""
|
||||||
|
buy_price = limit_buy_order['price']
|
||||||
|
patch_RPCManager(mocker)
|
||||||
|
patch_coinmarketcap(mocker)
|
||||||
|
mocker.patch.multiple(
|
||||||
|
'freqtrade.exchange.Exchange',
|
||||||
|
validate_pairs=MagicMock(),
|
||||||
|
get_ticker=MagicMock(return_value={
|
||||||
|
'bid': buy_price - 0.000001,
|
||||||
|
'ask': buy_price - 0.000001,
|
||||||
|
'last': buy_price - 0.000001
|
||||||
|
}),
|
||||||
|
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
||||||
|
get_fee=fee,
|
||||||
|
)
|
||||||
|
|
||||||
|
conf = deepcopy(default_conf)
|
||||||
|
conf['trailing_stop'] = True
|
||||||
|
conf['trailing_stop_positive'] = 0.01
|
||||||
|
conf['trailing_stop_positive_offset'] = 0.011
|
||||||
|
freqtrade = FreqtradeBot(conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: False
|
||||||
|
freqtrade.create_trade()
|
||||||
|
|
||||||
|
trade = Trade.query.first()
|
||||||
|
trade.update(limit_buy_order)
|
||||||
|
caplog.set_level(logging.DEBUG)
|
||||||
|
# stop-loss not reached
|
||||||
|
assert freqtrade.handle_trade(trade) is False
|
||||||
|
|
||||||
|
# Raise ticker above buy price
|
||||||
|
mocker.patch('freqtrade.exchange.Exchange.get_ticker',
|
||||||
|
MagicMock(return_value={
|
||||||
|
'bid': buy_price + 0.000003,
|
||||||
|
'ask': buy_price + 0.000003,
|
||||||
|
'last': buy_price + 0.000003
|
||||||
|
}))
|
||||||
|
# stop-loss not reached, adjusted stoploss
|
||||||
|
assert freqtrade.handle_trade(trade) is False
|
||||||
|
assert log_has(f'using positive stop loss mode: 0.01 with offset 0.011 '
|
||||||
|
f'since we have profit 0.2666%',
|
||||||
caplog.record_tuples)
|
caplog.record_tuples)
|
||||||
assert log_has(f'adjusted stop loss', caplog.record_tuples)
|
assert log_has(f'adjusted stop loss', caplog.record_tuples)
|
||||||
assert trade.stop_loss == 0.0000138501
|
assert trade.stop_loss == 0.0000138501
|
||||||
|
Loading…
Reference in New Issue
Block a user