Merge pull request #1868 from freqtrade/stoploss_restart

Stoploss restart
This commit is contained in:
Misagh 2019-06-12 10:29:17 +02:00 committed by GitHub
commit 1f3406b29b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 142 additions and 18 deletions

View File

@ -131,17 +131,11 @@ If it is not set in either Strategy or Configuration, a default of 1000% `{"0":
### Understand stoploss
The `stoploss` configuration parameter is loss in percentage that should trigger a sale.
For example, value `-0.10` will cause immediate sell if the
profit dips below -10% for a given trade. This parameter is optional.
Most of the strategy files already include the optimal `stoploss`
value. This parameter is optional. If you use it in the configuration file, it will take over the
`stoploss` value from the strategy file.
Go to the [stoploss documentation](stoploss.md) for more details.
### Understand trailing stoploss
Go to the [trailing stoploss Documentation](stoploss.md) for details on trailing stoploss.
Go to the [trailing stoploss Documentation](stoploss.md#trailing-stop-loss) for details on trailing stoploss.
### Understand initial_state

View File

@ -1,4 +1,13 @@
# Stop Loss support
# Stop Loss
The `stoploss` configuration parameter is loss in percentage that should trigger a sale.
For example, value `-0.10` will cause immediate sell if the profit dips below -10% for a given trade. This parameter is optional.
Most of the strategy files already include the optimal `stoploss`
value. This parameter is optional. If you use it in the configuration file, it will take over the
`stoploss` value from the strategy file.
## Stop Loss support
At this stage the bot contains the following stoploss support modes:
@ -16,13 +25,12 @@ In case of stoploss on exchange there is another parameter called `stoploss_on_e
!!! Note
Stoploss on exchange is only supported for Binance as of now.
## Static Stop Loss
This is very simple, basically you define a stop loss of x in your strategy file or alternative in the configuration, which
will overwrite the strategy definition. This will basically try to sell your asset, the second the loss exceeds the defined loss.
## Trail Stop Loss
## Trailing Stop Loss
The initial value for this stop loss, is defined in your strategy or configuration. Just as you would define your Stop Loss normally.
To enable this Feauture all you have to do is to define the configuration element:
@ -63,3 +71,13 @@ 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 (`trailing_stop_positive_offset`) lower than your minimal ROI, otherwise minimal ROI will apply first and sell your trade.
If `"trailing_only_offset_is_reached": true` then the trailing stoploss is only activated once the offset is reached. Until then, the stoploss remains at the configured`stoploss`.
## Changing stoploss on open trades
A stoploss on an open trade can be changed by changing the value in the configuration or strategy and use the `/reload_conf` command (alternatively, completely stopping and restarting the bot also works).
The new stoploss value will be applied to open trades (and corresponding log-messages will be generated).
### Limitations
Stoploss values cannot be changed if `trailing_stop` is enabled and the stoploss has already been adjusted, or if [Edge](edge.md) is enabled (since Edge would recalculate stoploss based on the current market situation).

View File

@ -218,9 +218,12 @@ stoploss = -0.10
```
This would signify a stoploss of -10%.
For the full documentation on stoploss features, look at the dedicated [stoploss page](stoploss.md).
If your exchange supports it, it's recommended to also set `"stoploss_on_exchange"` in the order dict, so your stoploss is on the exchange and cannot be missed for network-problems (or other problems).
For more information on order_types please look [here](https://github.com/freqtrade/freqtrade/blob/develop/docs/configuration.md#understand-order_types).
For more information on order_types please look [here](configuration.md#understand-order_types).
### Ticker interval

View File

@ -90,6 +90,16 @@ class FreqtradeBot(object):
self.rpc.cleanup()
persistence.cleanup()
def startup(self) -> None:
"""
Called on startup and after reloading the bot - triggers notifications and
performs startup tasks
"""
self.rpc.startup_messages(self.config, self.pairlists)
if not self.edge:
# Adjust stoploss if it was changed
Trade.stoploss_reinitialization(self.strategy.stoploss)
def process(self) -> bool:
"""
Queries the persistence layer for open trades and handles them,

View File

@ -422,3 +422,22 @@ class Trade(_DECL_BASE):
Query trades from persistence layer
"""
return Trade.query.filter(Trade.is_open.is_(True)).all()
@staticmethod
def stoploss_reinitialization(desired_stoploss):
"""
Adjust initial Stoploss to desired stoploss for all open trades.
"""
for trade in Trade.get_open_trades():
logger.info("Found open trade: %s", trade)
# skip case if trailing-stop changed the stoploss already.
if (trade.stop_loss == trade.initial_stop_loss
and trade.initial_stop_loss_pct != desired_stoploss):
# Stoploss value got changed
logger.info(f"Stoploss for {trade} needs adjustment.")
# Force reset of stoploss
trade.stop_loss = None
trade.adjust_stop_loss(trade.open_rate, desired_stoploss)
logger.info(f"new stoploss: {trade.stop_loss}, ")

View File

@ -2,6 +2,7 @@
import json
import logging
import re
from copy import deepcopy
from datetime import datetime
from functools import reduce
from unittest.mock import MagicMock, PropertyMock
@ -952,9 +953,10 @@ def buy_order_fee():
@pytest.fixture(scope="function")
def edge_conf(default_conf):
default_conf['max_open_trades'] = -1
default_conf['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT
default_conf['edge'] = {
conf = deepcopy(default_conf)
conf['max_open_trades'] = -1
conf['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT
conf['edge'] = {
"enabled": True,
"process_throttle_secs": 1800,
"calculate_since_number_of_days": 14,
@ -970,7 +972,7 @@ def edge_conf(default_conf):
"remove_pumps": False
}
return default_conf
return conf
@pytest.fixture

View File

@ -105,6 +105,7 @@ def test_cleanup(mocker, default_conf, caplog) -> None:
def test_worker_running(mocker, default_conf, caplog) -> None:
mock_throttle = MagicMock()
mocker.patch('freqtrade.worker.Worker._throttle', mock_throttle)
mocker.patch('freqtrade.persistence.Trade.stoploss_reinitialization', MagicMock())
worker = get_patched_worker(mocker, default_conf)
@ -3144,10 +3145,27 @@ def test_get_sell_rate(default_conf, mocker, ticker, order_book_l2) -> None:
assert rate == 0.043936
def test_startup_messages(default_conf, mocker):
def test_startup_state(default_conf, mocker):
default_conf['pairlist'] = {'method': 'VolumePairList',
'config': {'number_assets': 20}
}
mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True))
worker = get_patched_worker(mocker, default_conf)
assert worker.state is State.RUNNING
def test_startup_trade_reinit(default_conf, edge_conf, mocker):
mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True))
reinit_mock = MagicMock()
mocker.patch('freqtrade.persistence.Trade.stoploss_reinitialization', reinit_mock)
ftbot = get_patched_freqtradebot(mocker, default_conf)
ftbot.startup()
assert reinit_mock.call_count == 1
reinit_mock.reset_mock()
ftbot = get_patched_freqtradebot(mocker, edge_conf)
ftbot.startup()
assert reinit_mock.call_count == 0

View File

@ -777,3 +777,63 @@ def test_to_json(default_conf, fee):
'stop_loss_pct': None,
'initial_stop_loss': None,
'initial_stop_loss_pct': None}
def test_stoploss_reinitialization(default_conf, fee):
init(default_conf['db_url'])
trade = Trade(
pair='ETH/BTC',
stake_amount=0.001,
fee_open=fee.return_value,
open_date=arrow.utcnow().shift(hours=-2).datetime,
amount=10,
fee_close=fee.return_value,
exchange='bittrex',
open_rate=1,
max_rate=1,
)
trade.adjust_stop_loss(trade.open_rate, 0.05, True)
assert trade.stop_loss == 0.95
assert trade.stop_loss_pct == -0.05
assert trade.initial_stop_loss == 0.95
assert trade.initial_stop_loss_pct == -0.05
Trade.session.add(trade)
# Lower stoploss
Trade.stoploss_reinitialization(0.06)
trades = Trade.get_open_trades()
assert len(trades) == 1
trade_adj = trades[0]
assert trade_adj.stop_loss == 0.94
assert trade_adj.stop_loss_pct == -0.06
assert trade_adj.initial_stop_loss == 0.94
assert trade_adj.initial_stop_loss_pct == -0.06
# Raise stoploss
Trade.stoploss_reinitialization(0.04)
trades = Trade.get_open_trades()
assert len(trades) == 1
trade_adj = trades[0]
assert trade_adj.stop_loss == 0.96
assert trade_adj.stop_loss_pct == -0.04
assert trade_adj.initial_stop_loss == 0.96
assert trade_adj.initial_stop_loss_pct == -0.04
# Trailing stoploss (move stoplos up a bit)
trade.adjust_stop_loss(1.02, 0.04)
assert trade_adj.stop_loss == 0.9792
assert trade_adj.initial_stop_loss == 0.96
Trade.stoploss_reinitialization(0.04)
trades = Trade.get_open_trades()
assert len(trades) == 1
trade_adj = trades[0]
# Stoploss should not change in this case.
assert trade_adj.stop_loss == 0.9792
assert trade_adj.stop_loss_pct == -0.04
assert trade_adj.initial_stop_loss == 0.96
assert trade_adj.initial_stop_loss_pct == -0.04

View File

@ -91,7 +91,7 @@ class Worker(object):
})
logger.info('Changing state to: %s', state.name)
if state == State.RUNNING:
self.freqtrade.rpc.startup_messages(self._config, self.freqtrade.pairlists)
self.freqtrade.startup()
if state == State.STOPPED:
# Ping systemd watchdog before sleeping in the stopped state