Merge pull request #7862 from freqtrade/ws_newcandle
New websocket message "new_candle"
This commit is contained in:
commit
e7195b7bfb
@ -104,13 +104,15 @@ class DataProvider:
|
|||||||
def _emit_df(
|
def _emit_df(
|
||||||
self,
|
self,
|
||||||
pair_key: PairWithTimeframe,
|
pair_key: PairWithTimeframe,
|
||||||
dataframe: DataFrame
|
dataframe: DataFrame,
|
||||||
|
new_candle: bool
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Send this dataframe as an ANALYZED_DF message to RPC
|
Send this dataframe as an ANALYZED_DF message to RPC
|
||||||
|
|
||||||
:param pair_key: PairWithTimeframe tuple
|
: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:
|
if self.__rpc:
|
||||||
self.__rpc.send_msg(
|
self.__rpc.send_msg(
|
||||||
@ -123,6 +125,11 @@ class DataProvider:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
if new_candle:
|
||||||
|
self.__rpc.send_msg({
|
||||||
|
'type': RPCMessageType.NEW_CANDLE,
|
||||||
|
'data': pair_key,
|
||||||
|
})
|
||||||
|
|
||||||
def _add_external_df(
|
def _add_external_df(
|
||||||
self,
|
self,
|
||||||
|
@ -6,7 +6,7 @@ from freqtrade.enums.exittype import ExitType
|
|||||||
from freqtrade.enums.hyperoptstate import HyperoptState
|
from freqtrade.enums.hyperoptstate import HyperoptState
|
||||||
from freqtrade.enums.marginmode import MarginMode
|
from freqtrade.enums.marginmode import MarginMode
|
||||||
from freqtrade.enums.ordertypevalue import OrderTypeValues
|
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.runmode import NON_UTIL_MODES, OPTIMIZE_MODES, TRADING_MODES, RunMode
|
||||||
from freqtrade.enums.signaltype import SignalDirection, SignalTagType, SignalType
|
from freqtrade.enums.signaltype import SignalDirection, SignalTagType, SignalType
|
||||||
from freqtrade.enums.state import State
|
from freqtrade.enums.state import State
|
||||||
|
@ -21,6 +21,7 @@ class RPCMessageType(str, Enum):
|
|||||||
|
|
||||||
WHITELIST = 'whitelist'
|
WHITELIST = 'whitelist'
|
||||||
ANALYZED_DF = 'analyzed_df'
|
ANALYZED_DF = 'analyzed_df'
|
||||||
|
NEW_CANDLE = 'new_candle'
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return self.value
|
return self.value
|
||||||
@ -35,3 +36,6 @@ class RPCRequestType(str, Enum):
|
|||||||
|
|
||||||
WHITELIST = 'whitelist'
|
WHITELIST = 'whitelist'
|
||||||
ANALYZED_DF = 'analyzed_df'
|
ANALYZED_DF = 'analyzed_df'
|
||||||
|
|
||||||
|
|
||||||
|
NO_ECHO_MESSAGES = (RPCMessageType.ANALYZED_DF, RPCMessageType.WHITELIST, RPCMessageType.NEW_CANDLE)
|
||||||
|
@ -37,7 +37,8 @@ logger = logging.getLogger(__name__)
|
|||||||
# 2.16: Additional daily metrics
|
# 2.16: Additional daily metrics
|
||||||
# 2.17: Forceentry - leverage, partial force_exit
|
# 2.17: Forceentry - leverage, partial force_exit
|
||||||
# 2.20: Add websocket endpoints
|
# 2.20: Add websocket endpoints
|
||||||
API_VERSION = 2.20
|
# 2.21: Add new_candle messagetype
|
||||||
|
API_VERSION = 2.21
|
||||||
|
|
||||||
# Public API, requires no auth.
|
# Public API, requires no auth.
|
||||||
router_public = APIRouter()
|
router_public = APIRouter()
|
||||||
|
@ -6,7 +6,7 @@ from collections import deque
|
|||||||
from typing import Any, Dict, List
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
from freqtrade.constants import Config
|
from freqtrade.constants import Config
|
||||||
from freqtrade.enums import RPCMessageType
|
from freqtrade.enums import NO_ECHO_MESSAGES, RPCMessageType
|
||||||
from freqtrade.rpc import RPC, RPCHandler
|
from freqtrade.rpc import RPC, RPCHandler
|
||||||
|
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ class RPCManager:
|
|||||||
'status': 'stopping bot'
|
'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)
|
logger.info('Sending rpc message: %s', msg)
|
||||||
if 'pair' in msg:
|
if 'pair' in msg:
|
||||||
msg.update({
|
msg.update({
|
||||||
|
@ -68,6 +68,7 @@ class Webhook(RPCHandler):
|
|||||||
RPCMessageType.PROTECTION_TRIGGER_GLOBAL,
|
RPCMessageType.PROTECTION_TRIGGER_GLOBAL,
|
||||||
RPCMessageType.WHITELIST,
|
RPCMessageType.WHITELIST,
|
||||||
RPCMessageType.ANALYZED_DF,
|
RPCMessageType.ANALYZED_DF,
|
||||||
|
RPCMessageType.NEW_CANDLE,
|
||||||
RPCMessageType.STRATEGY_MSG):
|
RPCMessageType.STRATEGY_MSG):
|
||||||
# Don't fail for non-implemented types
|
# Don't fail for non-implemented types
|
||||||
return None
|
return None
|
||||||
|
@ -739,10 +739,10 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
"""
|
"""
|
||||||
pair = str(metadata.get('pair'))
|
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.
|
# Test if seen this pair and last candle before.
|
||||||
# always run if process_only_new_candles is set to false
|
# always run if process_only_new_candles is set to false
|
||||||
if (not self.process_only_new_candles or
|
if not self.process_only_new_candles or new_candle:
|
||||||
self._last_candle_seen_per_pair.get(pair, None) != dataframe.iloc[-1]['date']):
|
|
||||||
|
|
||||||
# Defs that only make change on new candle data.
|
# Defs that only make change on new candle data.
|
||||||
dataframe = self.analyze_ticker(dataframe, metadata)
|
dataframe = self.analyze_ticker(dataframe, metadata)
|
||||||
@ -751,7 +751,7 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
|
|
||||||
candle_type = self.config.get('candle_type_def', CandleType.SPOT)
|
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._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:
|
else:
|
||||||
logger.debug("Skipping TA Analysis for already analyzed candle")
|
logger.debug("Skipping TA Analysis for already analyzed candle")
|
||||||
|
@ -207,12 +207,18 @@ def test_emit_df(mocker, default_conf, ohlcv_history):
|
|||||||
assert send_mock.call_count == 0
|
assert send_mock.call_count == 0
|
||||||
|
|
||||||
# Rpc is added, we call emit, should call send_msg
|
# 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
|
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
|
# No rpc added, emit called, should not call send_msg
|
||||||
dataprovider_no_rpc._emit_df(pair, ohlcv_history)
|
dataprovider_no_rpc._emit_df(pair, ohlcv_history, False)
|
||||||
assert send_mock.call_count == 1
|
assert send_mock.call_count == 0
|
||||||
|
|
||||||
|
|
||||||
def test_refresh(mocker, default_conf, ohlcv_history):
|
def test_refresh(mocker, default_conf, ohlcv_history):
|
||||||
|
@ -588,7 +588,7 @@ def test_api_show_config(botclient):
|
|||||||
assert 'unfilledtimeout' in response
|
assert 'unfilledtimeout' in response
|
||||||
assert 'version' in response
|
assert 'version' in response
|
||||||
assert 'api_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):
|
def test_api_daily(botclient, mocker, ticker, fee, markets):
|
||||||
|
Loading…
Reference in New Issue
Block a user