diff --git a/freqtrade/rpc/__init__.py b/freqtrade/rpc/__init__.py index 88978519b..0a0130ca7 100644 --- a/freqtrade/rpc/__init__.py +++ b/freqtrade/rpc/__init__.py @@ -1,3 +1,3 @@ # flake8: noqa: F401 -from .rpc import RPC, RPCException, RPCMessageType +from .rpc import RPC, RPCException, RPCHandler, RPCMessageType from .rpc_manager import RPCManager diff --git a/freqtrade/rpc/api_server.py b/freqtrade/rpc/api_server.py index 804c83207..b489586c8 100644 --- a/freqtrade/rpc/api_server.py +++ b/freqtrade/rpc/api_server.py @@ -20,7 +20,7 @@ from freqtrade.__init__ import __version__ from freqtrade.constants import DATETIME_PRINT_FORMAT, USERPATH_STRATEGIES from freqtrade.exceptions import OperationalException from freqtrade.persistence import Trade -from freqtrade.rpc.rpc import RPC, RPCException +from freqtrade.rpc.rpc import RPC, RPCException, RPCHandler logger = logging.getLogger(__name__) @@ -78,7 +78,7 @@ def shutdown_session(exception=None): Trade.session.remove() -class ApiServer(RPC): +class ApiServer(RPCHandler): """ This class runs api server and provides rpc.rpc functionality to it @@ -89,13 +89,14 @@ class ApiServer(RPC): return (safe_str_cmp(username, self._config['api_server'].get('username')) and safe_str_cmp(password, self._config['api_server'].get('password'))) - def __init__(self, freqtrade) -> None: + def __init__(self, rpc: RPC, config: Dict[str, Any]) -> None: """ - Init the api server, and init the super class RPC - :param freqtrade: Instance of a freqtrade bot + Init the api server, and init the super class RPCHandler + :param rpc: instance of RPC Helper class + :param config: Configuration object :return: None """ - super().__init__(freqtrade) + super().__init__(rpc, config) self.app = Flask(__name__) self._cors = CORS(self.app, @@ -282,7 +283,7 @@ class ApiServer(RPC): Handler for /start. Starts TradeThread in bot if stopped. """ - msg = self._rpc_start() + msg = self._rpc._rpc_start() return jsonify(msg) @require_login @@ -292,7 +293,7 @@ class ApiServer(RPC): Handler for /stop. Stops TradeThread in bot if running """ - msg = self._rpc_stop() + msg = self._rpc._rpc_stop() return jsonify(msg) @require_login @@ -302,7 +303,7 @@ class ApiServer(RPC): Handler for /stopbuy. Sets max_open_trades to 0 and gracefully sells all open trades """ - msg = self._rpc_stopbuy() + msg = self._rpc._rpc_stopbuy() return jsonify(msg) @rpc_catch_errors @@ -326,7 +327,7 @@ class ApiServer(RPC): """ Prints the bot's version """ - return jsonify(RPC._rpc_show_config(self._config, self._freqtrade.state)) + return jsonify(RPC._rpc_show_config(self._config, self._rpc._freqtrade.state)) @require_login @rpc_catch_errors @@ -335,7 +336,7 @@ class ApiServer(RPC): Handler for /reload_config. Triggers a config file reload """ - msg = self._rpc_reload_config() + msg = self._rpc._rpc_reload_config() return jsonify(msg) @require_login @@ -345,7 +346,7 @@ class ApiServer(RPC): Handler for /count. Returns the number of trades running """ - msg = self._rpc_count() + msg = self._rpc._rpc_count() return jsonify(msg) @require_login @@ -355,7 +356,7 @@ class ApiServer(RPC): Handler for /locks. Returns the currently active locks. """ - return jsonify(self._rpc_locks()) + return jsonify(self._rpc._rpc_locks()) @require_login @rpc_catch_errors @@ -368,10 +369,10 @@ class ApiServer(RPC): timescale = request.args.get('timescale', 7) timescale = int(timescale) - stats = self._rpc_daily_profit(timescale, - self._config['stake_currency'], - self._config.get('fiat_display_currency', '') - ) + stats = self._rpc._rpc_daily_profit(timescale, + self._config['stake_currency'], + self._config.get('fiat_display_currency', '') + ) return jsonify(stats) @@ -394,7 +395,7 @@ class ApiServer(RPC): Returns information related to Edge. :return: edge stats """ - stats = self._rpc_edge() + stats = self._rpc._rpc_edge() return jsonify(stats) @@ -408,9 +409,9 @@ class ApiServer(RPC): :return: stats """ - stats = self._rpc_trade_statistics(self._config['stake_currency'], - self._config.get('fiat_display_currency') - ) + stats = self._rpc._rpc_trade_statistics(self._config['stake_currency'], + self._config.get('fiat_display_currency') + ) return jsonify(stats) @@ -422,7 +423,7 @@ class ApiServer(RPC): Returns a Object with "durations" and "sell_reasons" as keys. """ - stats = self._rpc_stats() + stats = self._rpc._rpc_stats() return jsonify(stats) @@ -435,7 +436,7 @@ class ApiServer(RPC): Returns a cumulative performance statistics :return: stats """ - stats = self._rpc_performance() + stats = self._rpc._rpc_performance() return jsonify(stats) @@ -448,7 +449,7 @@ class ApiServer(RPC): Returns the current status of the trades in json format """ try: - results = self._rpc_trade_status() + results = self._rpc._rpc_trade_status() return jsonify(results) except RPCException: return jsonify([]) @@ -461,8 +462,8 @@ class ApiServer(RPC): Returns the current status of the trades in json format """ - results = self._rpc_balance(self._config['stake_currency'], - self._config.get('fiat_display_currency', '')) + results = self._rpc._rpc_balance(self._config['stake_currency'], + self._config.get('fiat_display_currency', '')) return jsonify(results) @require_login @@ -474,7 +475,7 @@ class ApiServer(RPC): Returns the X last trades in json format """ limit = int(request.args.get('limit', 0)) - results = self._rpc_trade_history(limit) + results = self._rpc._rpc_trade_history(limit) return jsonify(results) @require_login @@ -487,7 +488,7 @@ class ApiServer(RPC): param: tradeid: Numeric trade-id assigned to the trade. """ - result = self._rpc_delete(tradeid) + result = self._rpc._rpc_delete(tradeid) return jsonify(result) @require_login @@ -496,7 +497,7 @@ class ApiServer(RPC): """ Handler for /whitelist. """ - results = self._rpc_whitelist() + results = self._rpc._rpc_whitelist() return jsonify(results) @require_login @@ -506,7 +507,7 @@ class ApiServer(RPC): Handler for /blacklist. """ add = request.json.get("blacklist", None) if request.method == 'POST' else None - results = self._rpc_blacklist(add) + results = self._rpc._rpc_blacklist(add) return jsonify(results) @require_login @@ -519,7 +520,7 @@ class ApiServer(RPC): price = request.json.get("price", None) price = float(price) if price is not None else price - trade = self._rpc_forcebuy(asset, price) + trade = self._rpc._rpc_forcebuy(asset, price) if trade: return jsonify(trade.to_json()) else: @@ -532,7 +533,7 @@ class ApiServer(RPC): Handler for /forcesell. """ tradeid = request.json.get("tradeid") - results = self._rpc_forcesell(tradeid) + results = self._rpc._rpc_forcesell(tradeid) return jsonify(results) @require_login @@ -554,7 +555,7 @@ class ApiServer(RPC): if not pair or not timeframe: return self.rest_error("Mandatory parameter missing.", 400) - results = self._rpc_analysed_dataframe(pair, timeframe, limit) + results = self._rpc._rpc_analysed_dataframe(pair, timeframe, limit) return jsonify(results) @require_login @@ -593,7 +594,7 @@ class ApiServer(RPC): """ Handler for /plot_config. """ - return jsonify(self._rpc_plot_config()) + return jsonify(self._rpc._rpc_plot_config()) @require_login @rpc_catch_errors diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 8c8e42c28..42ab76622 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -65,6 +65,32 @@ class RPCException(Exception): } +class RPCHandler: + + def __init__(self, rpc: 'RPC', config: Dict[str, Any]) -> None: + """ + Initializes RPCHandlers + :param rpc: instance of RPC Helper class + :param config: Configuration object + :return: None + """ + self._rpc = rpc + self._config: Dict[str, Any] = config + + @property + def name(self) -> str: + """ Returns the lowercase name of the implementation """ + return self.__class__.__name__.lower() + + @abstractmethod + def cleanup(self) -> None: + """ Cleanup pending module resources """ + + @abstractmethod + def send_msg(self, msg: Dict[str, str]) -> None: + """ Sends a message to all registered rpc modules """ + + class RPC: """ RPC class can be used to have extra feature, like bot data, and access to DB data @@ -83,19 +109,6 @@ class RPC: if self._config.get('fiat_display_currency', None): self._fiat_converter = CryptoToFiatConverter() - @property - def name(self) -> str: - """ Returns the lowercase name of the implementation """ - return self.__class__.__name__.lower() - - @abstractmethod - def cleanup(self) -> None: - """ Cleanup pending module resources """ - - @abstractmethod - def send_msg(self, msg: Dict[str, str]) -> None: - """ Sends a message to all registered rpc modules """ - @staticmethod def _rpc_show_config(config, botstate: State) -> Dict[str, Any]: """ diff --git a/freqtrade/rpc/rpc_manager.py b/freqtrade/rpc/rpc_manager.py index c42878f99..38a4e95fd 100644 --- a/freqtrade/rpc/rpc_manager.py +++ b/freqtrade/rpc/rpc_manager.py @@ -4,7 +4,7 @@ This module contains class to manage RPC communications (Telegram, Slack, ...) import logging from typing import Any, Dict, List -from freqtrade.rpc import RPC, RPCMessageType +from freqtrade.rpc import RPC, RPCHandler, RPCMessageType logger = logging.getLogger(__name__) @@ -16,25 +16,26 @@ class RPCManager: """ def __init__(self, freqtrade) -> None: """ Initializes all enabled rpc modules """ - self.registered_modules: List[RPC] = [] - + self.registered_modules: List[RPCHandler] = [] + self._rpc = RPC(freqtrade) + config = freqtrade.config # Enable telegram - if freqtrade.config.get('telegram', {}).get('enabled', False): + if config.get('telegram', {}).get('enabled', False): logger.info('Enabling rpc.telegram ...') from freqtrade.rpc.telegram import Telegram - self.registered_modules.append(Telegram(freqtrade)) + self.registered_modules.append(Telegram(self._rpc, config)) # Enable Webhook - if freqtrade.config.get('webhook', {}).get('enabled', False): + if config.get('webhook', {}).get('enabled', False): logger.info('Enabling rpc.webhook ...') from freqtrade.rpc.webhook import Webhook - self.registered_modules.append(Webhook(freqtrade)) + self.registered_modules.append(Webhook(self._rpc, config)) # Enable local rest api server for cmd line control - if freqtrade.config.get('api_server', {}).get('enabled', False): + if config.get('api_server', {}).get('enabled', False): logger.info('Enabling rpc.api_server') from freqtrade.rpc.api_server import ApiServer - self.registered_modules.append(ApiServer(freqtrade)) + self.registered_modules.append(ApiServer(self._rpc, config)) def cleanup(self) -> None: """ Stops all enabled rpc modules """ diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index e15071845..7ec67e5d0 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -18,7 +18,7 @@ from telegram.utils.helpers import escape_markdown from freqtrade.__init__ import __version__ from freqtrade.exceptions import OperationalException -from freqtrade.rpc import RPC, RPCException, RPCMessageType +from freqtrade.rpc import RPC, RPCException, RPCHandler, RPCMessageType logger = logging.getLogger(__name__) @@ -62,16 +62,18 @@ def authorized_only(command_handler: Callable[..., None]) -> Callable[..., Any]: return wrapper -class Telegram(RPC): +class Telegram(RPCHandler): """ This class handles all telegram communication """ - def __init__(self, freqtrade) -> None: + def __init__(self, rpc: RPC, config: Dict[str, Any]) -> None: + """ - Init the Telegram call, and init the super class RPC - :param freqtrade: Instance of a freqtrade bot + Init the Telegram call, and init the super class RPCHandler + :param rpc: instance of RPC Helper class + :param config: Configuration object :return: None """ - super().__init__(freqtrade) + super().__init__(rpc, config) self._updater: Updater self._init_keyboard() @@ -181,8 +183,8 @@ class Telegram(RPC): return if msg['type'] == RPCMessageType.BUY_NOTIFICATION: - if self._fiat_converter: - msg['stake_amount_fiat'] = self._fiat_converter.convert_amount( + if self._rpc._fiat_converter: + msg['stake_amount_fiat'] = self._rpc._fiat_converter.convert_amount( msg['stake_amount'], msg['stake_currency'], msg['fiat_currency']) else: msg['stake_amount_fiat'] = 0 @@ -222,8 +224,8 @@ class Telegram(RPC): # Check if all sell properties are available. # This might not be the case if the message origin is triggered by /forcesell if (all(prop in msg for prop in ['gain', 'fiat_currency', 'stake_currency']) - and self._fiat_converter): - msg['profit_fiat'] = self._fiat_converter.convert_amount( + and self._rpc._fiat_converter): + msg['profit_fiat'] = self._rpc._fiat_converter.convert_amount( msg['profit_amount'], msg['stake_currency'], msg['fiat_currency']) message += (' `({gain}: {profit_amount:.8f} {stake_currency}' ' / {profit_fiat:.3f} {fiat_currency})`').format(**msg) @@ -275,7 +277,7 @@ class Telegram(RPC): return try: - results = self._rpc_trade_status() + results = self._rpc._rpc_trade_status() messages = [] for r in results: @@ -325,8 +327,9 @@ class Telegram(RPC): :return: None """ try: - statlist, head = self._rpc_status_table(self._config['stake_currency'], - self._config.get('fiat_display_currency', '')) + statlist, head = self._rpc._rpc_status_table( + self._config['stake_currency'], self._config.get('fiat_display_currency', '')) + message = tabulate(statlist, headers=head, tablefmt='simple') self._send_msg(f"
{message}", parse_mode=ParseMode.HTML)
except RPCException as e:
@@ -348,7 +351,7 @@ class Telegram(RPC):
except (TypeError, ValueError, IndexError):
timescale = 7
try:
- stats = self._rpc_daily_profit(
+ stats = self._rpc._rpc_daily_profit(
timescale,
stake_cur,
fiat_disp_cur
@@ -382,7 +385,7 @@ class Telegram(RPC):
stake_cur = self._config['stake_currency']
fiat_disp_cur = self._config.get('fiat_display_currency', '')
- stats = self._rpc_trade_statistics(
+ stats = self._rpc._rpc_trade_statistics(
stake_cur,
fiat_disp_cur)
profit_closed_coin = stats['profit_closed_coin']
@@ -433,7 +436,7 @@ class Telegram(RPC):
Handler for /stats
Show stats of recent trades
"""
- stats = self._rpc_stats()
+ stats = self._rpc._rpc_stats()
reason_map = {
'roi': 'ROI',
@@ -473,8 +476,8 @@ class Telegram(RPC):
def _balance(self, update: Update, context: CallbackContext) -> None:
""" Handler for /balance """
try:
- result = self._rpc_balance(self._config['stake_currency'],
- self._config.get('fiat_display_currency', ''))
+ result = self._rpc._rpc_balance(self._config['stake_currency'],
+ self._config.get('fiat_display_currency', ''))
output = ''
if self._config['dry_run']:
@@ -517,7 +520,7 @@ class Telegram(RPC):
:param update: message update
:return: None
"""
- msg = self._rpc_start()
+ msg = self._rpc._rpc_start()
self._send_msg('Status: `{status}`'.format(**msg))
@authorized_only
@@ -529,7 +532,7 @@ class Telegram(RPC):
:param update: message update
:return: None
"""
- msg = self._rpc_stop()
+ msg = self._rpc._rpc_stop()
self._send_msg('Status: `{status}`'.format(**msg))
@authorized_only
@@ -541,7 +544,7 @@ class Telegram(RPC):
:param update: message update
:return: None
"""
- msg = self._rpc_reload_config()
+ msg = self._rpc._rpc_reload_config()
self._send_msg('Status: `{status}`'.format(**msg))
@authorized_only
@@ -553,7 +556,7 @@ class Telegram(RPC):
:param update: message update
:return: None
"""
- msg = self._rpc_stopbuy()
+ msg = self._rpc._rpc_stopbuy()
self._send_msg('Status: `{status}`'.format(**msg))
@authorized_only
@@ -571,7 +574,7 @@ class Telegram(RPC):
self._send_msg("You must specify a trade-id or 'all'.")
return
try:
- msg = self._rpc_forcesell(trade_id)
+ msg = self._rpc._rpc_forcesell(trade_id)
self._send_msg('Forcesell Result: `{result}`'.format(**msg))
except RPCException as e:
@@ -590,7 +593,7 @@ class Telegram(RPC):
pair = context.args[0]
price = float(context.args[1]) if len(context.args) > 1 else None
try:
- self._rpc_forcebuy(pair, price)
+ self._rpc._rpc_forcebuy(pair, price)
except RPCException as e:
self._send_msg(str(e))
@@ -609,7 +612,7 @@ class Telegram(RPC):
except (TypeError, ValueError, IndexError):
nrecent = 10
try:
- trades = self._rpc_trade_history(
+ trades = self._rpc._rpc_trade_history(
nrecent
)
trades_tab = tabulate(
@@ -642,7 +645,7 @@ class Telegram(RPC):
if not context.args or len(context.args) == 0:
raise RPCException("Trade-id not set.")
trade_id = int(context.args[0])
- msg = self._rpc_delete(trade_id)
+ msg = self._rpc._rpc_delete(trade_id)
self._send_msg((
'`{result_msg}`\n'
'Please make sure to take care of this asset on the exchange manually.'
@@ -661,7 +664,7 @@ class Telegram(RPC):
:return: None
"""
try:
- trades = self._rpc_performance()
+ trades = self._rpc._rpc_performance()
stats = '\n'.join('{index}.\t{pair}\t{profit:.2f}% ({count})'.format(
index=i + 1,
pair=trade['pair'],
@@ -683,7 +686,7 @@ class Telegram(RPC):
:return: None
"""
try:
- counts = self._rpc_count()
+ counts = self._rpc._rpc_count()
message = tabulate({k: [v] for k, v in counts.items()},
headers=['current', 'max', 'total stake'],
tablefmt='simple')
@@ -700,7 +703,7 @@ class Telegram(RPC):
Returns the currently active locks
"""
try:
- locks = self._rpc_locks()
+ locks = self._rpc._rpc_locks()
message = tabulate([[
lock['pair'],
lock['lock_end_time'],
@@ -720,7 +723,7 @@ class Telegram(RPC):
Shows the currently active whitelist
"""
try:
- whitelist = self._rpc_whitelist()
+ whitelist = self._rpc._rpc_whitelist()
message = f"Using whitelist `{whitelist['method']}` with {whitelist['length']} pairs\n"
message += f"`{', '.join(whitelist['whitelist'])}`"
@@ -738,7 +741,7 @@ class Telegram(RPC):
"""
try:
- blacklist = self._rpc_blacklist(context.args)
+ blacklist = self._rpc._rpc_blacklist(context.args)
errmsgs = []
for pair, error in blacklist['errors'].items():
errmsgs.append(f"Error adding `{pair}` to blacklist: `{error['error_msg']}`")
@@ -792,7 +795,7 @@ class Telegram(RPC):
Shows information related to Edge
"""
try:
- edge_pairs = self._rpc_edge()
+ edge_pairs = self._rpc._rpc_edge()
edge_pairs_tab = tabulate(edge_pairs, headers='keys', tablefmt='simple')
message = f'Edge only validated following pairs:\n{edge_pairs_tab}'
self._send_msg(message, parse_mode=ParseMode.HTML)
@@ -862,7 +865,7 @@ class Telegram(RPC):
:param update: message update
:return: None
"""
- val = RPC._rpc_show_config(self._freqtrade.config, self._freqtrade.state)
+ val = RPC._rpc_show_config(self._config, self._rpc._freqtrade.state)
if val['trailing_stop']:
sl_info = (
diff --git a/freqtrade/rpc/webhook.py b/freqtrade/rpc/webhook.py
index f4008a70f..5796201b5 100644
--- a/freqtrade/rpc/webhook.py
+++ b/freqtrade/rpc/webhook.py
@@ -6,7 +6,7 @@ from typing import Any, Dict
from requests import RequestException, post
-from freqtrade.rpc import RPC, RPCMessageType
+from freqtrade.rpc import RPC, RPCHandler, RPCMessageType
logger = logging.getLogger(__name__)
@@ -14,16 +14,17 @@ logger = logging.getLogger(__name__)
logger.debug('Included module rpc.webhook ...')
-class Webhook(RPC):
+class Webhook(RPCHandler):
""" This class handles all webhook communication """
- def __init__(self, freqtrade) -> None:
+ def __init__(self, rpc: RPC, config: Dict[str, Any]) -> None:
"""
- Init the Webhook class, and init the super class RPC
- :param freqtrade: Instance of a freqtrade bot
+ Init the Webhook class, and init the super class RPCHandler
+ :param rpc: instance of RPC Helper class
+ :param config: Configuration object
:return: None
"""
- super().__init__(freqtrade)
+ super().__init__(rpc, config)
self._url = self._config['webhook']['url']
diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py
index a1f4f7c9d..e7eee6f05 100644
--- a/tests/rpc/test_rpc_apiserver.py
+++ b/tests/rpc/test_rpc_apiserver.py
@@ -13,6 +13,7 @@ from requests.auth import _basic_auth_str
from freqtrade.__init__ import __version__
from freqtrade.loggers import setup_logging, setup_logging_pre
from freqtrade.persistence import PairLocks, Trade
+from freqtrade.rpc import RPC
from freqtrade.rpc.api_server import BASE_URI, ApiServer
from freqtrade.state import RunMode, State
from tests.conftest import create_mock_trades, get_patched_freqtradebot, log_has, patch_get_signal
@@ -36,8 +37,9 @@ def botclient(default_conf, mocker):
}})
ftbot = get_patched_freqtradebot(mocker, default_conf)
+ rpc = RPC(ftbot)
mocker.patch('freqtrade.rpc.api_server.ApiServer.run', MagicMock())
- apiserver = ApiServer(ftbot)
+ apiserver = ApiServer(rpc, default_conf)
yield ftbot, apiserver.app.test_client()
# Cleanup ... ?
@@ -179,8 +181,7 @@ def test_api__init__(default_conf, mocker):
}})
mocker.patch('freqtrade.rpc.telegram.Updater', MagicMock())
mocker.patch('freqtrade.rpc.api_server.ApiServer.run', MagicMock())
-
- apiserver = ApiServer(get_patched_freqtradebot(mocker, default_conf))
+ apiserver = ApiServer(RPC(get_patched_freqtradebot(mocker, default_conf)), default_conf)
assert apiserver._config == default_conf
@@ -197,7 +198,7 @@ def test_api_run(default_conf, mocker, caplog):
server_mock = MagicMock()
mocker.patch('freqtrade.rpc.api_server.make_server', server_mock)
- apiserver = ApiServer(get_patched_freqtradebot(mocker, default_conf))
+ apiserver = ApiServer(RPC(get_patched_freqtradebot(mocker, default_conf)), default_conf)
assert apiserver._config == default_conf
apiserver.run()
@@ -251,7 +252,7 @@ def test_api_cleanup(default_conf, mocker, caplog):
mocker.patch('freqtrade.rpc.api_server.threading.Thread', MagicMock())
mocker.patch('freqtrade.rpc.api_server.make_server', MagicMock())
- apiserver = ApiServer(get_patched_freqtradebot(mocker, default_conf))
+ apiserver = ApiServer(RPC(get_patched_freqtradebot(mocker, default_conf)), default_conf)
apiserver.run()
stop_mock = MagicMock()
stop_mock.shutdown = MagicMock()
diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py
index 5040f35cf..97b9e5e7c 100644
--- a/tests/rpc/test_rpc_telegram.py
+++ b/tests/rpc/test_rpc_telegram.py
@@ -20,7 +20,7 @@ from freqtrade.exceptions import OperationalException
from freqtrade.freqtradebot import FreqtradeBot
from freqtrade.loggers import setup_logging
from freqtrade.persistence import PairLocks, Trade
-from freqtrade.rpc import RPCMessageType
+from freqtrade.rpc import RPC, RPCMessageType
from freqtrade.rpc.telegram import Telegram, authorized_only
from freqtrade.state import RunMode, State
from freqtrade.strategy.interface import SellType
@@ -32,8 +32,8 @@ class DummyCls(Telegram):
"""
Dummy class for testing the Telegram @authorized_only decorator
"""
- def __init__(self, freqtrade) -> None:
- super().__init__(freqtrade)
+ def __init__(self, rpc: RPC, config) -> None:
+ super().__init__(rpc, config)
self.state = {'called': False}
def _init(self):
@@ -54,7 +54,7 @@ class DummyCls(Telegram):
raise Exception('test')
-def get_telegram_testobject(mocker, default_conf, mock=True):
+def get_telegram_testobject(mocker, default_conf, mock=True, ftbot=None):
msg_mock = MagicMock()
if mock:
mocker.patch.multiple(
@@ -62,8 +62,10 @@ def get_telegram_testobject(mocker, default_conf, mock=True):
_init=MagicMock(),
_send_msg=msg_mock
)
- ftbot = get_patched_freqtradebot(mocker, default_conf)
- telegram = Telegram(ftbot)
+ if not ftbot:
+ ftbot = get_patched_freqtradebot(mocker, default_conf)
+ rpc = RPC(ftbot)
+ telegram = Telegram(rpc, default_conf)
return telegram, ftbot, msg_mock
@@ -112,8 +114,10 @@ def test_authorized_only(default_conf, mocker, caplog, update) -> None:
default_conf['telegram']['enabled'] = False
bot = FreqtradeBot(default_conf)
+ rpc = RPC(bot)
+ dummy = DummyCls(rpc, default_conf)
+
patch_get_signal(bot, (True, False))
- dummy = DummyCls(bot)
dummy.dummy_handler(update=update, context=MagicMock())
assert dummy.state['called'] is True
assert log_has('Executing handler: dummy_handler for chat_id: 0', caplog)
@@ -129,8 +133,10 @@ def test_authorized_only_unauthorized(default_conf, mocker, caplog) -> None:
default_conf['telegram']['enabled'] = False
bot = FreqtradeBot(default_conf)
+ rpc = RPC(bot)
+ dummy = DummyCls(rpc, default_conf)
+
patch_get_signal(bot, (True, False))
- dummy = DummyCls(bot)
dummy.dummy_handler(update=update, context=MagicMock())
assert dummy.state['called'] is False
assert not log_has('Executing handler: dummy_handler for chat_id: 3735928559', caplog)
@@ -144,8 +150,9 @@ def test_authorized_only_exception(default_conf, mocker, caplog, update) -> None
default_conf['telegram']['enabled'] = False
bot = FreqtradeBot(default_conf)
+ rpc = RPC(bot)
+ dummy = DummyCls(rpc, default_conf)
patch_get_signal(bot, (True, False))
- dummy = DummyCls(bot)
dummy.dummy_exception(update=update, context=MagicMock())
assert dummy.state['called'] is False
@@ -160,8 +167,10 @@ def test_telegram_status(default_conf, update, mocker) -> None:
default_conf['telegram']['chat_id'] = "123"
status_table = MagicMock()
+ mocker.patch('freqtrade.rpc.telegram.Telegram._status_table', status_table)
+
mocker.patch.multiple(
- 'freqtrade.rpc.telegram.Telegram',
+ 'freqtrade.rpc.rpc.RPC',
_rpc_trade_status=MagicMock(return_value=[{
'trade_id': 1,
'pair': 'ETH/BTC',
@@ -188,7 +197,6 @@ def test_telegram_status(default_conf, update, mocker) -> None:
'open_order': '(limit buy rem=0.00000000)',
'is_open': True
}]),
- _status_table=status_table,
)
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
@@ -642,8 +650,9 @@ def test_telegram_forcesell_handle(default_conf, update, ticker, fee,
)
freqtradebot = FreqtradeBot(default_conf)
+ rpc = RPC(freqtradebot)
+ telegram = Telegram(rpc, default_conf)
patch_get_signal(freqtradebot, (True, False))
- telegram = Telegram(freqtradebot)
# Create some test data
freqtradebot.enter_positions()
@@ -698,8 +707,9 @@ def test_telegram_forcesell_down_handle(default_conf, update, ticker, fee,
)
freqtradebot = FreqtradeBot(default_conf)
+ rpc = RPC(freqtradebot)
+ telegram = Telegram(rpc, default_conf)
patch_get_signal(freqtradebot, (True, False))
- telegram = Telegram(freqtradebot)
# Create some test data
freqtradebot.enter_positions()
@@ -756,8 +766,9 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None
)
default_conf['max_open_trades'] = 4
freqtradebot = FreqtradeBot(default_conf)
+ rpc = RPC(freqtradebot)
+ telegram = Telegram(rpc, default_conf)
patch_get_signal(freqtradebot, (True, False))
- telegram = Telegram(freqtradebot)
# Create some test data
freqtradebot.enter_positions()
@@ -1216,8 +1227,8 @@ def test_send_msg_sell_notification(default_conf, mocker) -> None:
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
- old_convamount = telegram._fiat_converter.convert_amount
- telegram._fiat_converter.convert_amount = lambda a, b, c: -24.812
+ old_convamount = telegram._rpc._fiat_converter.convert_amount
+ telegram._rpc._fiat_converter.convert_amount = lambda a, b, c: -24.812
telegram.send_msg({
'type': RPCMessageType.SELL_NOTIFICATION,
'exchange': 'Binance',
@@ -1274,15 +1285,15 @@ def test_send_msg_sell_notification(default_conf, mocker) -> None:
'*Duration:* `1 day, 2:30:00 (1590.0 min)`\n'
'*Profit:* `-57.41%`')
# Reset singleton function to avoid random breaks
- telegram._fiat_converter.convert_amount = old_convamount
+ telegram._rpc._fiat_converter.convert_amount = old_convamount
def test_send_msg_sell_cancel_notification(default_conf, mocker) -> None:
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
- old_convamount = telegram._fiat_converter.convert_amount
- telegram._fiat_converter.convert_amount = lambda a, b, c: -24.812
+ old_convamount = telegram._rpc._fiat_converter.convert_amount
+ telegram._rpc._fiat_converter.convert_amount = lambda a, b, c: -24.812
telegram.send_msg({
'type': RPCMessageType.SELL_CANCEL_NOTIFICATION,
'exchange': 'Binance',
@@ -1303,7 +1314,7 @@ def test_send_msg_sell_cancel_notification(default_conf, mocker) -> None:
assert msg_mock.call_args[0][0] \
== ('\N{WARNING SIGN} *Binance:* Cancelling Open Sell Order for KEY/ETH. Reason: timeout')
# Reset singleton function to avoid random breaks
- telegram._fiat_converter.convert_amount = old_convamount
+ telegram._rpc._fiat_converter.convert_amount = old_convamount
def test_send_msg_status_notification(default_conf, mocker) -> None:
@@ -1449,6 +1460,7 @@ def test__send_msg_keyboard(default_conf, mocker, caplog) -> None:
bot = MagicMock()
bot.send_message = MagicMock()
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
+ rpc = RPC(freqtradebot)
invalid_keys_list = [['/not_valid', '/profit'], ['/daily'], ['/alsoinvalid']]
default_keys_list = [['/daily', '/profit', '/balance'],
@@ -1461,7 +1473,7 @@ def test__send_msg_keyboard(default_conf, mocker, caplog) -> None:
custom_keyboard = ReplyKeyboardMarkup(custom_keys_list)
def init_telegram(freqtradebot):
- telegram = Telegram(freqtradebot)
+ telegram = Telegram(rpc, default_conf)
telegram._updater = MagicMock()
telegram._updater.bot = bot
return telegram
diff --git a/tests/rpc/test_rpc_webhook.py b/tests/rpc/test_rpc_webhook.py
index 9256a5316..4ca547390 100644
--- a/tests/rpc/test_rpc_webhook.py
+++ b/tests/rpc/test_rpc_webhook.py
@@ -5,7 +5,7 @@ from unittest.mock import MagicMock
import pytest
from requests import RequestException
-from freqtrade.rpc import RPCMessageType
+from freqtrade.rpc import RPC, RPCMessageType
from freqtrade.rpc.webhook import Webhook
from freqtrade.strategy.interface import SellType
from tests.conftest import get_patched_freqtradebot, log_has
@@ -45,7 +45,7 @@ def get_webhook_dict() -> dict:
def test__init__(mocker, default_conf):
default_conf['webhook'] = {'enabled': True, 'url': "https://DEADBEEF.com"}
- webhook = Webhook(get_patched_freqtradebot(mocker, default_conf))
+ webhook = Webhook(RPC(get_patched_freqtradebot(mocker, default_conf)), default_conf)
assert webhook._config == default_conf
@@ -53,7 +53,7 @@ def test_send_msg(default_conf, mocker):
default_conf["webhook"] = get_webhook_dict()
msg_mock = MagicMock()
mocker.patch("freqtrade.rpc.webhook.Webhook._send_msg", msg_mock)
- webhook = Webhook(get_patched_freqtradebot(mocker, default_conf))
+ webhook = Webhook(RPC(get_patched_freqtradebot(mocker, default_conf)), default_conf)
# Test buy
msg_mock = MagicMock()
mocker.patch("freqtrade.rpc.webhook.Webhook._send_msg", msg_mock)
@@ -172,7 +172,7 @@ def test_exception_send_msg(default_conf, mocker, caplog):
default_conf["webhook"] = get_webhook_dict()
del default_conf["webhook"]["webhookbuy"]
- webhook = Webhook(get_patched_freqtradebot(mocker, default_conf))
+ webhook = Webhook(RPC(get_patched_freqtradebot(mocker, default_conf)), default_conf)
webhook.send_msg({'type': RPCMessageType.BUY_NOTIFICATION})
assert log_has(f"Message type '{RPCMessageType.BUY_NOTIFICATION}' not configured for webhooks",
caplog)
@@ -181,7 +181,7 @@ def test_exception_send_msg(default_conf, mocker, caplog):
default_conf["webhook"]["webhookbuy"]["value1"] = "{DEADBEEF:8f}"
msg_mock = MagicMock()
mocker.patch("freqtrade.rpc.webhook.Webhook._send_msg", msg_mock)
- webhook = Webhook(get_patched_freqtradebot(mocker, default_conf))
+ webhook = Webhook(RPC(get_patched_freqtradebot(mocker, default_conf)), default_conf)
msg = {
'type': RPCMessageType.BUY_NOTIFICATION,
'exchange': 'Bittrex',
@@ -209,7 +209,7 @@ def test_exception_send_msg(default_conf, mocker, caplog):
def test__send_msg(default_conf, mocker, caplog):
default_conf["webhook"] = get_webhook_dict()
- webhook = Webhook(get_patched_freqtradebot(mocker, default_conf))
+ webhook = Webhook(RPC(get_patched_freqtradebot(mocker, default_conf)), default_conf)
msg = {'value1': 'DEADBEEF',
'value2': 'ALIVEBEEF',
'value3': 'FREQTRADE'}