From 349c0619aa30dba6fe38d70575cea7b710b23e27 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 19 May 2019 20:06:26 +0200 Subject: [PATCH 1/9] Move startup to freqtradebot --- freqtrade/freqtradebot.py | 8 ++++++++ freqtrade/worker.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 8b29d6d40..425b7e3a9 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -89,6 +89,14 @@ 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 + : return: None + """ + self.rpc.startup_messages(self.config, self.pairlists) + def process(self) -> bool: """ Queries the persistence layer for open trades and handles them, diff --git a/freqtrade/worker.py b/freqtrade/worker.py index 19a570505..c224b4ee5 100755 --- a/freqtrade/worker.py +++ b/freqtrade/worker.py @@ -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 From 6a5daab520b4f26181abafe44a024777dfafa181 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 20 May 2019 07:06:40 +0200 Subject: [PATCH 2/9] add logic for stoploss reinitialization after startup --- freqtrade/freqtradebot.py | 2 ++ freqtrade/persistence.py | 20 ++++++++++++++++++++ freqtrade/tests/test_freqtradebot.py | 1 + 3 files changed, 23 insertions(+) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 425b7e3a9..7d5cddd6c 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -96,6 +96,8 @@ class FreqtradeBot(object): : return: None """ self.rpc.startup_messages(self.config, self.pairlists) + # Adjust stoploss if it was changed + Trade.stoploss_reinitialization(self.strategy.stoploss) def process(self) -> bool: """ diff --git a/freqtrade/persistence.py b/freqtrade/persistence.py index e64e0b89c..3fc9a189e 100644 --- a/freqtrade/persistence.py +++ b/freqtrade/persistence.py @@ -421,3 +421,23 @@ 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.") + logger.info(f"Stoploss: {trade.initial_stop_loss_pct}: {desired_stoploss}") + # 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}, ") diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 67b05ac3e..c683f6273 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -114,6 +114,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.adjust_initial_stoploss', MagicMock()) worker = get_patched_worker(mocker, default_conf) From 9f54181494a7b211a7f0cd4213150c36f4193634 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 20 May 2019 07:12:54 +0200 Subject: [PATCH 3/9] Add test for stoploss_reinit --- freqtrade/tests/test_persistence.py | 61 +++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/freqtrade/tests/test_persistence.py b/freqtrade/tests/test_persistence.py index 8c15fa8e8..93bad0797 100644 --- a/freqtrade/tests/test_persistence.py +++ b/freqtrade/tests/test_persistence.py @@ -777,3 +777,64 @@ 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) + 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 From 53af8f331d985fc559d64e0d5808f9f57e515d70 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 20 May 2019 19:35:19 +0200 Subject: [PATCH 4/9] Deep-copy default_conf for edge config --- freqtrade/tests/conftest.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/freqtrade/tests/conftest.py b/freqtrade/tests/conftest.py index 0bff1d5e9..692fda368 100644 --- a/freqtrade/tests/conftest.py +++ b/freqtrade/tests/conftest.py @@ -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 @@ -942,9 +943,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, @@ -960,4 +962,4 @@ def edge_conf(default_conf): "remove_pumps": False } - return default_conf + return conf From a39cdd3b2b3b59719bb9b5e53f89f1085776e7dd Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 20 May 2019 19:35:48 +0200 Subject: [PATCH 5/9] Exclude Edge from startup-stoploss calc Edge would recalculate / reevaluate stoploss values on startup, so these values are not reliable --- freqtrade/freqtradebot.py | 5 +++-- freqtrade/tests/test_freqtradebot.py | 19 ++++++++++++++++++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 7d5cddd6c..0121512ee 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -96,8 +96,9 @@ class FreqtradeBot(object): : return: None """ self.rpc.startup_messages(self.config, self.pairlists) - # Adjust stoploss if it was changed - Trade.stoploss_reinitialization(self.strategy.stoploss) + if not self.edge: + # Adjust stoploss if it was changed + Trade.stoploss_reinitialization(self.strategy.stoploss) def process(self) -> bool: """ diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index c683f6273..48eb51b54 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -3136,10 +3136,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 From 11fd8a59af3fb92f405e59f3db47f4d82be809f3 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 20 May 2019 20:06:13 +0200 Subject: [PATCH 6/9] cleanup stoploss documentations --- docs/configuration.md | 10 ++-------- docs/stoploss.md | 15 ++++++++++++--- docs/strategy-customization.md | 5 ++++- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index df116b3c2..d097712c6 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -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 diff --git a/docs/stoploss.md b/docs/stoploss.md index cbe4fd3c4..4c731940e 100644 --- a/docs/stoploss.md +++ b/docs/stoploss.md @@ -1,4 +1,14 @@ -# 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 +26,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: diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index 51540f690..1256f4a32 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -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 to [here](configuration.md#understand-order_types). ### Ticker interval From 58ced364451c28ba2e6c71fbb5f9f4f6085de975 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 20 May 2019 20:06:35 +0200 Subject: [PATCH 7/9] Add documentation for stoploss updates --- docs/stoploss.md | 10 ++++++++++ docs/strategy-customization.md | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/stoploss.md b/docs/stoploss.md index 4c731940e..975c2aeb5 100644 --- a/docs/stoploss.md +++ b/docs/stoploss.md @@ -72,3 +72,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). diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index 1256f4a32..85d8104b0 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -223,7 +223,7 @@ For the full documentation on stoploss features, look at the dedicated [stoploss 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 to [here](configuration.md#understand-order_types). +For more information on order_types please look [here](configuration.md#understand-order_types). ### Ticker interval From 51aa469f67e0ed3c7504e444063032e6d60f9553 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 20 May 2019 20:13:01 +0200 Subject: [PATCH 8/9] Cleanups --- docs/stoploss.md | 1 - freqtrade/freqtradebot.py | 1 - freqtrade/persistence.py | 1 - freqtrade/tests/test_freqtradebot.py | 2 +- freqtrade/tests/test_persistence.py | 1 - 5 files changed, 1 insertion(+), 5 deletions(-) diff --git a/docs/stoploss.md b/docs/stoploss.md index 975c2aeb5..f5e2f8df6 100644 --- a/docs/stoploss.md +++ b/docs/stoploss.md @@ -7,7 +7,6 @@ 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: diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 0121512ee..81c9dc5d3 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -93,7 +93,6 @@ class FreqtradeBot(object): """ Called on startup and after reloading the bot - triggers notifications and performs startup tasks - : return: None """ self.rpc.startup_messages(self.config, self.pairlists) if not self.edge: diff --git a/freqtrade/persistence.py b/freqtrade/persistence.py index 3fc9a189e..ed09f6f22 100644 --- a/freqtrade/persistence.py +++ b/freqtrade/persistence.py @@ -436,7 +436,6 @@ class Trade(_DECL_BASE): # Stoploss value got changed logger.info(f"Stoploss for {trade} needs adjustment.") - logger.info(f"Stoploss: {trade.initial_stop_loss_pct}: {desired_stoploss}") # Force reset of stoploss trade.stop_loss = None trade.adjust_stop_loss(trade.open_rate, desired_stoploss) diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 48eb51b54..2587b1b36 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -114,7 +114,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.adjust_initial_stoploss', MagicMock()) + mocker.patch('freqtrade.persistence.Trade.stoploss_reinitialization', MagicMock()) worker = get_patched_worker(mocker, default_conf) diff --git a/freqtrade/tests/test_persistence.py b/freqtrade/tests/test_persistence.py index 93bad0797..3312bc21d 100644 --- a/freqtrade/tests/test_persistence.py +++ b/freqtrade/tests/test_persistence.py @@ -822,7 +822,6 @@ def test_stoploss_reinitialization(default_conf, fee): 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 From adc12ed043320a9aee2884cdfad6aa5e86cb3069 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 8 Jun 2019 20:26:25 +0200 Subject: [PATCH 9/9] Fix new test after develop merge --- freqtrade/tests/test_persistence.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/tests/test_persistence.py b/freqtrade/tests/test_persistence.py index 57f054dee..bb00fa8f4 100644 --- a/freqtrade/tests/test_persistence.py +++ b/freqtrade/tests/test_persistence.py @@ -780,7 +780,7 @@ def test_to_json(default_conf, fee): def test_stoploss_reinitialization(default_conf, fee): - init(default_conf) + init(default_conf['db_url']) trade = Trade( pair='ETH/BTC', stake_amount=0.001,