Merge pull request #1034 from freqtrade/feat/positive_sl_limit

add offset for positive trailing stop loss
This commit is contained in:
Samuel Husso 2018-07-29 08:30:29 +03:00 committed by GitHub
commit 187e039a58
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 82 additions and 8 deletions

View File

@ -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,

View File

@ -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.

View File

@ -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.

View File

@ -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': {

View File

@ -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)

View File

@ -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