From 5e533b550f777e6e898091cc86f354ab7ef91a48 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 26 Oct 2022 20:22:58 +0200 Subject: [PATCH 1/6] Emit a simple "new candle" message to allow UI's to refresh charts --- freqtrade/data/dataprovider.py | 18 ++++++++++++------ freqtrade/enums/rpcmessagetype.py | 1 + freqtrade/rpc/webhook.py | 1 + freqtrade/strategy/interface.py | 6 +++--- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index 4d7296ee7..7657549a3 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -101,16 +101,13 @@ class DataProvider: """ return self.__producer_pairs.get(producer_name, []).copy() - def _emit_df( - self, - pair_key: PairWithTimeframe, - dataframe: DataFrame - ) -> None: + def _emit_df(self, pair_key: PairWithTimeframe, dataframe: DataFrame, new_candle: bool) -> None: """ Send this dataframe as an ANALYZED_DF message to RPC :param pair_key: PairWithTimeframe tuple - :param data: Tuple containing the DataFrame and the datetime it was cached + :param dataframe: Dataframe to emit + :param new_candle: This is a new candle """ if self.__rpc: self.__rpc.send_msg( @@ -123,6 +120,15 @@ class DataProvider: } } ) + if new_candle: + self.__rpc.send_msg( + { + 'type': RPCMessageType.NEW_CANDLE, + 'data': { + 'key': pair_key, + } + } + ) def _add_external_df( self, diff --git a/freqtrade/enums/rpcmessagetype.py b/freqtrade/enums/rpcmessagetype.py index fae121a09..8b3596465 100644 --- a/freqtrade/enums/rpcmessagetype.py +++ b/freqtrade/enums/rpcmessagetype.py @@ -21,6 +21,7 @@ class RPCMessageType(str, Enum): WHITELIST = 'whitelist' ANALYZED_DF = 'analyzed_df' + NEW_CANDLE = 'new_candle' def __repr__(self): return self.value diff --git a/freqtrade/rpc/webhook.py b/freqtrade/rpc/webhook.py index 19c4166b3..d81d8d24f 100644 --- a/freqtrade/rpc/webhook.py +++ b/freqtrade/rpc/webhook.py @@ -68,6 +68,7 @@ class Webhook(RPCHandler): RPCMessageType.PROTECTION_TRIGGER_GLOBAL, RPCMessageType.WHITELIST, RPCMessageType.ANALYZED_DF, + RPCMessageType.NEW_CANDLE, RPCMessageType.STRATEGY_MSG): # Don't fail for non-implemented types return None diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 681c5fcbb..781ae6c5c 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -739,10 +739,10 @@ class IStrategy(ABC, HyperStrategyMixin): """ pair = str(metadata.get('pair')) + new_candle = self._last_candle_seen_per_pair.get(pair, None) != dataframe.iloc[-1]['date'] # Test if seen this pair and last candle before. # always run if process_only_new_candles is set to false - if (not self.process_only_new_candles or - self._last_candle_seen_per_pair.get(pair, None) != dataframe.iloc[-1]['date']): + if not self.process_only_new_candles or new_candle: # Defs that only make change on new candle data. dataframe = self.analyze_ticker(dataframe, metadata) @@ -751,7 +751,7 @@ class IStrategy(ABC, HyperStrategyMixin): candle_type = self.config.get('candle_type_def', CandleType.SPOT) self.dp._set_cached_df(pair, self.timeframe, dataframe, candle_type=candle_type) - self.dp._emit_df((pair, self.timeframe, candle_type), dataframe) + self.dp._emit_df((pair, self.timeframe, candle_type), dataframe, new_candle) else: logger.debug("Skipping TA Analysis for already analyzed candle") From 687eefa06e1318b41e55a3aee5eb4e3cb3d33df4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 26 Oct 2022 20:31:31 +0200 Subject: [PATCH 2/6] Improve emit_df testcase --- tests/data/test_dataprovider.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/data/test_dataprovider.py b/tests/data/test_dataprovider.py index 8500fa06c..025e6d08a 100644 --- a/tests/data/test_dataprovider.py +++ b/tests/data/test_dataprovider.py @@ -207,12 +207,18 @@ def test_emit_df(mocker, default_conf, ohlcv_history): assert send_mock.call_count == 0 # Rpc is added, we call emit, should call send_msg - dataprovider._emit_df(pair, ohlcv_history) + dataprovider._emit_df(pair, ohlcv_history, False) assert send_mock.call_count == 1 + send_mock.reset_mock() + dataprovider._emit_df(pair, ohlcv_history, True) + assert send_mock.call_count == 2 + + send_mock.reset_mock() + # No rpc added, emit called, should not call send_msg - dataprovider_no_rpc._emit_df(pair, ohlcv_history) - assert send_mock.call_count == 1 + dataprovider_no_rpc._emit_df(pair, ohlcv_history, False) + assert send_mock.call_count == 0 def test_refresh(mocker, default_conf, ohlcv_history): From d30a872ed48c6a72acdfc23ff64d045c2d1f33d0 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 5 Dec 2022 19:23:03 +0100 Subject: [PATCH 3/6] Move message-silencing list next to enum --- freqtrade/enums/__init__.py | 2 +- freqtrade/enums/rpcmessagetype.py | 3 +++ freqtrade/rpc/rpc_manager.py | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/freqtrade/enums/__init__.py b/freqtrade/enums/__init__.py index 146d65f2d..eb70a2894 100644 --- a/freqtrade/enums/__init__.py +++ b/freqtrade/enums/__init__.py @@ -6,7 +6,7 @@ from freqtrade.enums.exittype import ExitType from freqtrade.enums.hyperoptstate import HyperoptState from freqtrade.enums.marginmode import MarginMode from freqtrade.enums.ordertypevalue import OrderTypeValues -from freqtrade.enums.rpcmessagetype import RPCMessageType, RPCRequestType +from freqtrade.enums.rpcmessagetype import NO_ECHO_MESSAGES, RPCMessageType, RPCRequestType from freqtrade.enums.runmode import NON_UTIL_MODES, OPTIMIZE_MODES, TRADING_MODES, RunMode from freqtrade.enums.signaltype import SignalDirection, SignalTagType, SignalType from freqtrade.enums.state import State diff --git a/freqtrade/enums/rpcmessagetype.py b/freqtrade/enums/rpcmessagetype.py index 8b3596465..2453d16d9 100644 --- a/freqtrade/enums/rpcmessagetype.py +++ b/freqtrade/enums/rpcmessagetype.py @@ -36,3 +36,6 @@ class RPCRequestType(str, Enum): WHITELIST = 'whitelist' ANALYZED_DF = 'analyzed_df' + + +NO_ECHO_MESSAGES = (RPCMessageType.ANALYZED_DF, RPCMessageType.WHITELIST, RPCMessageType.NEW_CANDLE) diff --git a/freqtrade/rpc/rpc_manager.py b/freqtrade/rpc/rpc_manager.py index 9c25723b0..c4d4fa2dd 100644 --- a/freqtrade/rpc/rpc_manager.py +++ b/freqtrade/rpc/rpc_manager.py @@ -6,7 +6,7 @@ from collections import deque from typing import Any, Dict, List from freqtrade.constants import Config -from freqtrade.enums import RPCMessageType +from freqtrade.enums import NO_ECHO_MESSAGES, RPCMessageType from freqtrade.rpc import RPC, RPCHandler @@ -67,7 +67,7 @@ class RPCManager: 'status': 'stopping bot' } """ - if msg.get('type') not in (RPCMessageType.ANALYZED_DF, RPCMessageType.WHITELIST): + if msg.get('type') not in NO_ECHO_MESSAGES: logger.info('Sending rpc message: %s', msg) if 'pair' in msg: msg.update({ From 24edc276ea31d4e733667155aecc8e403a43f7f2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 5 Dec 2022 19:43:36 +0100 Subject: [PATCH 4/6] Simplify new_candle message --- freqtrade/data/dataprovider.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index 7657549a3..6b220c8b4 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -101,7 +101,12 @@ class DataProvider: """ return self.__producer_pairs.get(producer_name, []).copy() - def _emit_df(self, pair_key: PairWithTimeframe, dataframe: DataFrame, new_candle: bool) -> None: + def _emit_df( + self, + pair_key: PairWithTimeframe, + dataframe: DataFrame, + new_candle: bool + ) -> None: """ Send this dataframe as an ANALYZED_DF message to RPC @@ -121,14 +126,10 @@ class DataProvider: } ) if new_candle: - self.__rpc.send_msg( - { + self.__rpc.send_msg({ 'type': RPCMessageType.NEW_CANDLE, - 'data': { - 'key': pair_key, - } - } - ) + 'data': pair_key, + }) def _add_external_df( self, From 7c27eedda54e36c06471559a1dcf92bb98248405 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 5 Dec 2022 19:56:33 +0100 Subject: [PATCH 5/6] Bump API version --- freqtrade/rpc/api_server/api_v1.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/rpc/api_server/api_v1.py b/freqtrade/rpc/api_server/api_v1.py index c0c9b8f57..9e4b140e4 100644 --- a/freqtrade/rpc/api_server/api_v1.py +++ b/freqtrade/rpc/api_server/api_v1.py @@ -37,7 +37,8 @@ logger = logging.getLogger(__name__) # 2.16: Additional daily metrics # 2.17: Forceentry - leverage, partial force_exit # 2.20: Add websocket endpoints -API_VERSION = 2.20 +# 2.21: Add new_candle messagetype +API_VERSION = 2.21 # Public API, requires no auth. router_public = APIRouter() From 72472587ddb4251270502e19f2808493c53cb2ca Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 5 Dec 2022 20:18:47 +0100 Subject: [PATCH 6/6] Increase test range for api version test --- tests/rpc/test_rpc_apiserver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 043666853..ee067f911 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -588,7 +588,7 @@ def test_api_show_config(botclient): assert 'unfilledtimeout' in response assert 'version' in response assert 'api_version' in response - assert 2.1 <= response['api_version'] <= 2.2 + assert 2.1 <= response['api_version'] < 3.0 def test_api_daily(botclient, mocker, ticker, fee, markets):