From 879bf47b32ff692e35f2509665b1ef9899eb3621 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 19 Sep 2021 19:25:36 +0200 Subject: [PATCH 1/9] Refactor telegram.py to simplify send_msg --- freqtrade/rpc/telegram.py | 51 +++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 19c58b63d..898446ea0 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -260,29 +260,7 @@ class Telegram(RPCHandler): return message - def send_msg(self, msg: Dict[str, Any]) -> None: - """ Send a message to telegram channel """ - - default_noti = 'on' - - msg_type = msg['type'] - noti = '' - if msg_type == RPCMessageType.SELL: - sell_noti = self._config['telegram'] \ - .get('notification_settings', {}).get(str(msg_type), {}) - # For backward compatibility sell still can be string - if isinstance(sell_noti, str): - noti = sell_noti - else: - noti = sell_noti.get(str(msg['sell_reason']), default_noti) - else: - noti = self._config['telegram'] \ - .get('notification_settings', {}).get(str(msg_type), default_noti) - - if noti == 'off': - logger.info(f"Notification '{msg_type}' not sent.") - # Notification disabled - return + def compose_message(self, msg: Dict[str, Any], msg_type: RPCMessageType) -> str: if msg_type == RPCMessageType.BUY: message = self._format_buy_msg(msg) @@ -315,6 +293,33 @@ class Telegram(RPCHandler): else: raise NotImplementedError('Unknown message type: {}'.format(msg_type)) + return message + + def send_msg(self, msg: Dict[str, Any]) -> None: + """ Send a message to telegram channel """ + + default_noti = 'on' + + msg_type = msg['type'] + noti = '' + if msg_type == RPCMessageType.SELL: + sell_noti = self._config['telegram'] \ + .get('notification_settings', {}).get(str(msg_type), {}) + # For backward compatibility sell still can be string + if isinstance(sell_noti, str): + noti = sell_noti + else: + noti = sell_noti.get(str(msg['sell_reason']), default_noti) + else: + noti = self._config['telegram'] \ + .get('notification_settings', {}).get(str(msg_type), default_noti) + + if noti == 'off': + logger.info(f"Notification '{msg_type}' not sent.") + # Notification disabled + return + + message = self.compose_message(msg, msg_type) self._send_msg(message, disable_notification=(noti == 'silent')) From 1da091dea3006b67e2400cf1390981354626f4fb Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 19 Sep 2021 19:41:19 +0200 Subject: [PATCH 2/9] ProtectionManager should return the lock just created --- freqtrade/persistence/pairlock_middleware.py | 4 +++- freqtrade/plugins/protectionmanager.py | 23 ++++++++++---------- tests/plugins/test_protections.py | 4 ++-- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/freqtrade/persistence/pairlock_middleware.py b/freqtrade/persistence/pairlock_middleware.py index af904f693..8662fc36d 100644 --- a/freqtrade/persistence/pairlock_middleware.py +++ b/freqtrade/persistence/pairlock_middleware.py @@ -30,7 +30,8 @@ class PairLocks(): PairLocks.locks = [] @staticmethod - def lock_pair(pair: str, until: datetime, reason: str = None, *, now: datetime = None) -> None: + def lock_pair(pair: str, until: datetime, reason: str = None, *, + now: datetime = None) -> PairLock: """ Create PairLock from now to "until". Uses database by default, unless PairLocks.use_db is set to False, @@ -52,6 +53,7 @@ class PairLocks(): PairLock.query.session.commit() else: PairLocks.locks.append(lock) + return lock @staticmethod def get_pair_locks(pair: Optional[str], now: Optional[datetime] = None) -> List[PairLock]: diff --git a/freqtrade/plugins/protectionmanager.py b/freqtrade/plugins/protectionmanager.py index f33e5b4bc..2510d6fee 100644 --- a/freqtrade/plugins/protectionmanager.py +++ b/freqtrade/plugins/protectionmanager.py @@ -6,6 +6,7 @@ from datetime import datetime, timezone from typing import Dict, List, Optional from freqtrade.persistence import PairLocks +from freqtrade.persistence.models import PairLock from freqtrade.plugins.protections import IProtection from freqtrade.resolvers import ProtectionResolver @@ -43,30 +44,28 @@ class ProtectionManager(): """ return [{p.name: p.short_desc()} for p in self._protection_handlers] - def global_stop(self, now: Optional[datetime] = None) -> bool: + def global_stop(self, now: Optional[datetime] = None) -> Optional[PairLock]: if not now: now = datetime.now(timezone.utc) - result = False + result = None for protection_handler in self._protection_handlers: if protection_handler.has_global_stop: - result, until, reason = protection_handler.global_stop(now) + lock, until, reason = protection_handler.global_stop(now) # Early stopping - first positive result blocks further trades - if result and until: + if lock and until: if not PairLocks.is_global_lock(until): - PairLocks.lock_pair('*', until, reason, now=now) - result = True + result = PairLocks.lock_pair('*', until, reason, now=now) return result - def stop_per_pair(self, pair, now: Optional[datetime] = None) -> bool: + def stop_per_pair(self, pair, now: Optional[datetime] = None) -> Optional[PairLock]: if not now: now = datetime.now(timezone.utc) - result = False + result = None for protection_handler in self._protection_handlers: if protection_handler.has_local_stop: - result, until, reason = protection_handler.stop_per_pair(pair, now) - if result and until: + lock, until, reason = protection_handler.stop_per_pair(pair, now) + if lock and until: if not PairLocks.is_pair_locked(pair, until): - PairLocks.lock_pair(pair, until, reason, now=now) - result = True + result = PairLocks.lock_pair(pair, until, reason, now=now) return result diff --git a/tests/plugins/test_protections.py b/tests/plugins/test_protections.py index c0a9ae72a..a3cb29c9d 100644 --- a/tests/plugins/test_protections.py +++ b/tests/plugins/test_protections.py @@ -125,7 +125,7 @@ def test_stoploss_guard(mocker, default_conf, fee, caplog): # Test 5m after lock-period - this should try and relock the pair, but end-time # should be the previous end-time end_time = PairLocks.get_pair_longest_lock('*').lock_end_time + timedelta(minutes=5) - assert freqtrade.protections.global_stop(end_time) + freqtrade.protections.global_stop(end_time) assert not PairLocks.is_global_lock(end_time) @@ -182,7 +182,7 @@ def test_stoploss_guard_perpair(mocker, default_conf, fee, caplog, only_per_pair min_ago_open=180, min_ago_close=30, profit_rate=0.9, )) - assert freqtrade.protections.stop_per_pair(pair) + freqtrade.protections.stop_per_pair(pair) assert freqtrade.protections.global_stop() != only_per_pair assert PairLocks.is_pair_locked(pair) assert PairLocks.is_global_lock() != only_per_pair From c91a9a92f2ccecc5aac46f25a999c0b4a97b20a7 Mon Sep 17 00:00:00 2001 From: Bernhard Millauer Date: Mon, 20 Sep 2021 14:22:24 +0200 Subject: [PATCH 3/9] Add troubleshooting information The time in wsl docker container shifts over time. Added information how to fix this issue. --- docs/docker_quickstart.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/docker_quickstart.md b/docs/docker_quickstart.md index 1fa229225..f4f8c366d 100644 --- a/docs/docker_quickstart.md +++ b/docs/docker_quickstart.md @@ -149,6 +149,20 @@ You'll then also need to modify the `docker-compose.yml` file and uncomment the You can then run `docker-compose build` to build the docker image, and run it using the commands described above. +### Troubleshooting + +### Docker on Windows + +* Error: `"Timestamp for this request is outside of the recvWindow."` + * The market api requests require a synchronized clock but the time in the docker container shifts a bit over time into the past. + To fix this issue temporarily you need to run `wsl --shutdown` and restart docker again (a popup on windows 10 will ask you to do so). + A permanent solution is either to host the docker container on a linux host or restart the wsl from time to time with the scheduler. + ``` + taskkill /IM "Docker Desktop.exe" /F + wsl --shutdown + start "" "C:\Program Files\Docker\Docker\Docker Desktop.exe" + ``` + ## Plotting with docker-compose Commands `freqtrade plot-profit` and `freqtrade plot-dataframe` ([Documentation](plotting.md)) are available by changing the image to `*_plot` in your docker-compose.yml file. From a0fb43c6caca7d0b0f06cf9814d43f69341339b6 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 20 Sep 2021 19:12:59 +0200 Subject: [PATCH 4/9] Add pairlock-notification --- freqtrade/enums/rpcmessagetype.py | 1 + freqtrade/freqtradebot.py | 16 ++++++++++++++-- freqtrade/rpc/telegram.py | 5 +++++ tests/rpc/test_rpc_telegram.py | 14 ++++++++++++++ 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/freqtrade/enums/rpcmessagetype.py b/freqtrade/enums/rpcmessagetype.py index 9c59f6108..889d67cf4 100644 --- a/freqtrade/enums/rpcmessagetype.py +++ b/freqtrade/enums/rpcmessagetype.py @@ -11,6 +11,7 @@ class RPCMessageType(Enum): SELL = 'sell' SELL_FILL = 'sell_fill' SELL_CANCEL = 'sell_cancel' + PROTECTION_TRIGGER = 'protection_trigger' def __repr__(self): return self.value diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 1cb8988ff..451b5764a 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1292,8 +1292,7 @@ class FreqtradeBot(LoggingMixin): if not trade.is_open: if not stoploss_order and not trade.open_order_id: self._notify_exit(trade, '', True) - self.protections.stop_per_pair(trade.pair) - self.protections.global_stop() + self.handle_protections(trade.pair) self.wallets.update() elif not trade.open_order_id: # Buy fill @@ -1301,6 +1300,19 @@ class FreqtradeBot(LoggingMixin): return False + def handle_protections(self, pair: str) -> None: + prot_trig = self.protections.stop_per_pair(pair) + if prot_trig: + msg = {'type': RPCMessageType.PROTECTION_TRIGGER, } + msg.update(prot_trig.to_json()) + self.rpc.send_msg(msg) + + prot_trig_glb = self.protections.global_stop() + if prot_trig_glb: + msg = {'type': RPCMessageType.PROTECTION_TRIGGER, } + msg.update(prot_trig_glb.to_json()) + self.rpc.send_msg(msg) + def apply_fee_conditional(self, trade: Trade, trade_base_currency: str, amount: float, fee_abs: float) -> float: """ diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 898446ea0..934a5182a 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -281,6 +281,11 @@ class Telegram(RPCHandler): "for {close_rate}.".format(**msg)) elif msg_type == RPCMessageType.SELL: message = self._format_sell_msg(msg) + elif msg_type == RPCMessageType.PROTECTION_TRIGGER: + message = ( + "*Protection* triggered due to {reason}. " + "{pair} will be locked until {lock_end_time}." + ).format(**msg) elif msg_type == RPCMessageType.STATUS: message = '*Status:* `{status}`'.format(**msg) diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index 21f1cd000..0675e4d5f 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -1313,6 +1313,20 @@ def test_send_msg_buy_cancel_notification(default_conf, mocker) -> None: 'Reason: cancelled due to timeout.') +def test_send_msg_protection_notification(default_conf, mocker, time_machine) -> None: + + telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf) + time_machine.move_to("2021-09-01 05:00:00 +00:00") + lock = PairLocks.lock_pair('ETH/BTC', arrow.utcnow().shift(minutes=6).datetime, 'randreason') + msg = { + 'type': RPCMessageType.PROTECTION_TRIGGER, + } + msg.update(lock.to_json()) + telegram.send_msg(msg) + assert (msg_mock.call_args[0][0] == "*Protection* triggered due to randreason. " + "ETH/BTC will be locked until 2021-09-01 05:10:00.") + + def test_send_msg_buy_fill_notification(default_conf, mocker) -> None: default_conf['telegram']['notification_settings']['buy_fill'] = 'on' From dd0db7ee5db04fb98b542e9541d46768b9bb2c05 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 20 Sep 2021 19:23:40 +0200 Subject: [PATCH 5/9] Split protection-notification into global and per-pair --- config_examples/config_full.example.json | 4 +++- docs/telegram-usage.md | 4 +++- freqtrade/enums/rpcmessagetype.py | 1 + freqtrade/freqtradebot.py | 2 +- freqtrade/rpc/telegram.py | 6 +++++- tests/rpc/test_rpc_telegram.py | 13 +++++++++++++ 6 files changed, 26 insertions(+), 4 deletions(-) diff --git a/config_examples/config_full.example.json b/config_examples/config_full.example.json index d0f3f0df6..c415d70b0 100644 --- a/config_examples/config_full.example.json +++ b/config_examples/config_full.example.json @@ -149,7 +149,9 @@ }, "sell_fill": "on", "buy_cancel": "on", - "sell_cancel": "on" + "sell_cancel": "on", + "protection_trigger": "off", + "protection_trigger_global": "on" }, "reload": true, "balance_dust_level": 0.01 diff --git a/docs/telegram-usage.md b/docs/telegram-usage.md index b020b00db..1e6fa9dae 100644 --- a/docs/telegram-usage.md +++ b/docs/telegram-usage.md @@ -93,7 +93,9 @@ Example configuration showing the different settings: "buy_cancel": "silent", "sell_cancel": "on", "buy_fill": "off", - "sell_fill": "off" + "sell_fill": "off", + "protection_trigger": "off", + "protection_trigger_global": "on" }, "reload": true, "balance_dust_level": 0.01 diff --git a/freqtrade/enums/rpcmessagetype.py b/freqtrade/enums/rpcmessagetype.py index 889d67cf4..4e3f693e5 100644 --- a/freqtrade/enums/rpcmessagetype.py +++ b/freqtrade/enums/rpcmessagetype.py @@ -12,6 +12,7 @@ class RPCMessageType(Enum): SELL_FILL = 'sell_fill' SELL_CANCEL = 'sell_cancel' PROTECTION_TRIGGER = 'protection_trigger' + PROTECTION_TRIGGER_GLOBAL = 'protection_trigger_global' def __repr__(self): return self.value diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 451b5764a..37be3173a 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1309,7 +1309,7 @@ class FreqtradeBot(LoggingMixin): prot_trig_glb = self.protections.global_stop() if prot_trig_glb: - msg = {'type': RPCMessageType.PROTECTION_TRIGGER, } + msg = {'type': RPCMessageType.PROTECTION_TRIGGER_GLOBAL, } msg.update(prot_trig_glb.to_json()) self.rpc.send_msg(msg) diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 934a5182a..0687f95a2 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -286,7 +286,11 @@ class Telegram(RPCHandler): "*Protection* triggered due to {reason}. " "{pair} will be locked until {lock_end_time}." ).format(**msg) - + elif msg_type == RPCMessageType.PROTECTION_TRIGGER_GLOBAL: + message = ( + "*Protection* triggered due to {reason}. " + "All pairs will be locked until {lock_end_time}." + ).format(**msg) elif msg_type == RPCMessageType.STATUS: message = '*Status:* `{status}`'.format(**msg) diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index 0675e4d5f..d37a74187 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -1326,6 +1326,19 @@ def test_send_msg_protection_notification(default_conf, mocker, time_machine) -> assert (msg_mock.call_args[0][0] == "*Protection* triggered due to randreason. " "ETH/BTC will be locked until 2021-09-01 05:10:00.") + msg_mock.reset_mock() + # Test global protection + + msg = { + 'type': RPCMessageType.PROTECTION_TRIGGER_GLOBAL, + } + lock = PairLocks.lock_pair('*', arrow.utcnow().shift(minutes=100).datetime, 'randreason') + msg.update(lock.to_json()) + telegram.send_msg(msg) + assert (msg_mock.call_args[0][0] == "*Protection* triggered due to randreason. " + "All pairs will be locked until 2021-09-01 06:45:00.") + + def test_send_msg_buy_fill_notification(default_conf, mocker) -> None: From fd23ab3d647db7444d12129755ddf001e014dbcd Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 20 Sep 2021 19:49:18 +0200 Subject: [PATCH 6/9] improve formatting, add tests --- freqtrade/rpc/telegram.py | 4 ++-- tests/rpc/test_rpc_telegram.py | 5 ++--- tests/test_freqtradebot.py | 23 +++++++++++++++++++++++ 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 0687f95a2..059ba9c41 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -284,12 +284,12 @@ class Telegram(RPCHandler): elif msg_type == RPCMessageType.PROTECTION_TRIGGER: message = ( "*Protection* triggered due to {reason}. " - "{pair} will be locked until {lock_end_time}." + "`{pair}` will be locked until `{lock_end_time}`." ).format(**msg) elif msg_type == RPCMessageType.PROTECTION_TRIGGER_GLOBAL: message = ( "*Protection* triggered due to {reason}. " - "All pairs will be locked until {lock_end_time}." + "*All pairs* will be locked until `{lock_end_time}`." ).format(**msg) elif msg_type == RPCMessageType.STATUS: message = '*Status:* `{status}`'.format(**msg) diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index d37a74187..8c285a76e 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -1324,7 +1324,7 @@ def test_send_msg_protection_notification(default_conf, mocker, time_machine) -> msg.update(lock.to_json()) telegram.send_msg(msg) assert (msg_mock.call_args[0][0] == "*Protection* triggered due to randreason. " - "ETH/BTC will be locked until 2021-09-01 05:10:00.") + "`ETH/BTC` will be locked until `2021-09-01 05:10:00`.") msg_mock.reset_mock() # Test global protection @@ -1336,8 +1336,7 @@ def test_send_msg_protection_notification(default_conf, mocker, time_machine) -> msg.update(lock.to_json()) telegram.send_msg(msg) assert (msg_mock.call_args[0][0] == "*Protection* triggered due to randreason. " - "All pairs will be locked until 2021-09-01 06:45:00.") - + "*All pairs* will be locked until `2021-09-01 06:45:00`.") def test_send_msg_buy_fill_notification(default_conf, mocker) -> None: diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index eb3c77cc7..e1df6dad9 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -526,6 +526,29 @@ def test_enter_positions_global_pairlock(default_conf, ticker, limit_buy_order, assert log_has_re(message, caplog) +def test_handle_protections(mocker, default_conf, fee): + default_conf['protections'] = [ + {"method": "CooldownPeriod", "stop_duration": 60}, + { + "method": "StoplossGuard", + "lookback_period_candles": 24, + "trade_limit": 4, + "stop_duration_candles": 4, + "only_per_pair": False + } + ] + + freqtrade = get_patched_freqtradebot(mocker, default_conf) + freqtrade.protections._protection_handlers[1].global_stop = MagicMock( + return_value=(True, arrow.utcnow().shift(hours=1).datetime, "asdf")) + create_mock_trades(fee) + freqtrade.handle_protections('ETC/BTC') + send_msg_mock = freqtrade.rpc.send_msg + assert send_msg_mock.call_count == 2 + assert send_msg_mock.call_args_list[0][0][0]['type'] == RPCMessageType.PROTECTION_TRIGGER + assert send_msg_mock.call_args_list[1][0][0]['type'] == RPCMessageType.PROTECTION_TRIGGER_GLOBAL + + def test_create_trade_no_signal(default_conf, fee, mocker) -> None: default_conf['dry_run'] = True From 3ce05c0d548119fd5618ba2700313a69ec10f3c5 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 20 Sep 2021 20:08:48 +0200 Subject: [PATCH 7/9] Add "sane" defaults to protection triggers --- docs/telegram-usage.md | 1 + freqtrade/constants.py | 9 +++++++++ tests/rpc/test_rpc_telegram.py | 2 ++ 3 files changed, 12 insertions(+) diff --git a/docs/telegram-usage.md b/docs/telegram-usage.md index 1e6fa9dae..b9d01a236 100644 --- a/docs/telegram-usage.md +++ b/docs/telegram-usage.md @@ -105,6 +105,7 @@ Example configuration showing the different settings: `buy` notifications are sent when the order is placed, while `buy_fill` notifications are sent when the order is filled on the exchange. `sell` notifications are sent when the order is placed, while `sell_fill` notifications are sent when the order is filled on the exchange. `*_fill` notifications are off by default and must be explicitly enabled. +`protection_trigger` notifications are sent when a protection triggers and `protection_trigger_global` notifications trigger when global protections are triggered. `balance_dust_level` will define what the `/balance` command takes as "dust" - Currencies with a balance below this will be shown. diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 9ca43d459..4997108bc 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -284,6 +284,15 @@ CONF_SCHEMA = { 'enum': TELEGRAM_SETTING_OPTIONS, 'default': 'off' }, + 'protection_trigger': { + 'type': 'string', + 'enum': TELEGRAM_SETTING_OPTIONS, + 'default': 'off' + }, + 'protection_trigger_global': { + 'type': 'string', + 'enum': TELEGRAM_SETTING_OPTIONS, + }, } }, 'reload': {'type': 'boolean'}, diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index 8c285a76e..7dde7b803 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -1315,6 +1315,8 @@ def test_send_msg_buy_cancel_notification(default_conf, mocker) -> None: def test_send_msg_protection_notification(default_conf, mocker, time_machine) -> None: + default_conf['telegram']['notification_settings']['protection_trigger'] = 'on' + telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf) time_machine.move_to("2021-09-01 05:00:00 +00:00") lock = PairLocks.lock_pair('ETH/BTC', arrow.utcnow().shift(minutes=6).datetime, 'randreason') From abddb0db66b431abf5902a8fa74dbf5f2cf16e56 Mon Sep 17 00:00:00 2001 From: Bernhard Millauer Date: Tue, 21 Sep 2021 10:13:19 +0200 Subject: [PATCH 8/9] Fix header indention Co-authored-by: Matthias --- docs/docker_quickstart.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docker_quickstart.md b/docs/docker_quickstart.md index f4f8c366d..33b1c7ea1 100644 --- a/docs/docker_quickstart.md +++ b/docs/docker_quickstart.md @@ -151,7 +151,7 @@ You can then run `docker-compose build` to build the docker image, and run it us ### Troubleshooting -### Docker on Windows +#### Docker on Windows * Error: `"Timestamp for this request is outside of the recvWindow."` * The market api requests require a synchronized clock but the time in the docker container shifts a bit over time into the past. From 6fc770d97dd8d4380be3fb94e8f3dcedfa981cd7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 21 Sep 2021 15:12:35 +0200 Subject: [PATCH 9/9] Add warning about running with docker on windows --- docs/docker_quickstart.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/docker_quickstart.md b/docs/docker_quickstart.md index 33b1c7ea1..2f350d207 100644 --- a/docs/docker_quickstart.md +++ b/docs/docker_quickstart.md @@ -163,6 +163,10 @@ You can then run `docker-compose build` to build the docker image, and run it us start "" "C:\Program Files\Docker\Docker\Docker Desktop.exe" ``` +!!! Warning + Due to the above, we do not recommend the usage of docker on windows for production setups, but only for experimentation, datadownload and backtesting. + Best use a linux-VPS for running freqtrade reliably. + ## Plotting with docker-compose Commands `freqtrade plot-profit` and `freqtrade plot-dataframe` ([Documentation](plotting.md)) are available by changing the image to `*_plot` in your docker-compose.yml file.