mirror of
https://github.com/freqtrade/freqtrade.git
synced 2024-11-10 10:21:59 +00:00
Merge pull request #8394 from freqtrade/dependabot/pip/develop/python-telegram-bot-20.2
Bump python-telegram-bot from 13.15 to 20.2
This commit is contained in:
commit
459e5e67bd
|
@ -107,8 +107,7 @@ class Exchange:
|
|||
# Lock event loop. This is necessary to avoid race-conditions when using force* commands
|
||||
# Due to funding fee fetching.
|
||||
self._loop_lock = Lock()
|
||||
self.loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(self.loop)
|
||||
self.loop = self._init_async_loop()
|
||||
self._config: Config = {}
|
||||
|
||||
self._config.update(config)
|
||||
|
@ -212,6 +211,11 @@ class Exchange:
|
|||
if self.loop and not self.loop.is_closed():
|
||||
self.loop.close()
|
||||
|
||||
def _init_async_loop(self) -> asyncio.AbstractEventLoop:
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
return loop
|
||||
|
||||
def validate_config(self, config):
|
||||
# Check if timeframe is available
|
||||
self.validate_timeframes(config.get('timeframe'))
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
"""
|
||||
This module manage Telegram communication
|
||||
"""
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
|
@ -13,15 +14,17 @@ from functools import partial
|
|||
from html import escape
|
||||
from itertools import chain
|
||||
from math import isnan
|
||||
from typing import Any, Callable, Dict, List, Optional, Union
|
||||
from threading import Thread
|
||||
from typing import Any, Callable, Coroutine, Dict, List, Optional, Union
|
||||
|
||||
import arrow
|
||||
from tabulate import tabulate
|
||||
from telegram import (MAX_MESSAGE_LENGTH, CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup,
|
||||
KeyboardButton, ParseMode, ReplyKeyboardMarkup, Update)
|
||||
from telegram import (CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton,
|
||||
ReplyKeyboardMarkup, Update)
|
||||
from telegram.constants import MessageLimit, ParseMode
|
||||
from telegram.error import BadRequest, NetworkError, TelegramError
|
||||
from telegram.ext import CallbackContext, CallbackQueryHandler, CommandHandler, Updater
|
||||
from telegram.utils.helpers import escape_markdown
|
||||
from telegram.ext import Application, CallbackContext, CallbackQueryHandler, CommandHandler
|
||||
from telegram.helpers import escape_markdown
|
||||
|
||||
from freqtrade.__init__ import __version__
|
||||
from freqtrade.constants import DUST_PER_COIN, Config
|
||||
|
@ -33,6 +36,9 @@ from freqtrade.rpc import RPC, RPCException, RPCHandler
|
|||
from freqtrade.rpc.rpc_types import RPCSendMsg
|
||||
|
||||
|
||||
MAX_MESSAGE_LENGTH = MessageLimit.MAX_TEXT_LENGTH
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
logger.debug('Included module rpc.telegram ...')
|
||||
|
@ -47,14 +53,14 @@ class TimeunitMappings:
|
|||
default: int
|
||||
|
||||
|
||||
def authorized_only(command_handler: Callable[..., None]) -> Callable[..., Any]:
|
||||
def authorized_only(command_handler: Callable[..., Coroutine[Any, Any, None]]):
|
||||
"""
|
||||
Decorator to check if the message comes from the correct chat_id
|
||||
:param command_handler: Telegram CommandHandler
|
||||
:return: decorated function
|
||||
"""
|
||||
|
||||
def wrapper(self, *args, **kwargs):
|
||||
async def wrapper(self, *args, **kwargs):
|
||||
""" Decorator logic """
|
||||
update = kwargs.get('update') or args[0]
|
||||
|
||||
|
@ -76,9 +82,9 @@ def authorized_only(command_handler: Callable[..., None]) -> Callable[..., Any]:
|
|||
chat_id
|
||||
)
|
||||
try:
|
||||
return command_handler(self, *args, **kwargs)
|
||||
return await command_handler(self, *args, **kwargs)
|
||||
except RPCException as e:
|
||||
self._send_msg(str(e))
|
||||
await self._send_msg(str(e))
|
||||
except BaseException:
|
||||
logger.exception('Exception occurred within Telegram module')
|
||||
finally:
|
||||
|
@ -99,9 +105,17 @@ class Telegram(RPCHandler):
|
|||
"""
|
||||
super().__init__(rpc, config)
|
||||
|
||||
self._updater: Updater
|
||||
self._app: Application
|
||||
self._loop: asyncio.AbstractEventLoop
|
||||
self._init_keyboard()
|
||||
self._init()
|
||||
self._start_thread()
|
||||
|
||||
def _start_thread(self):
|
||||
"""
|
||||
Creates and starts the polling thread
|
||||
"""
|
||||
self._thread = Thread(target=self._init, name='FTTelegram')
|
||||
self._thread.start()
|
||||
|
||||
def _init_keyboard(self) -> None:
|
||||
"""
|
||||
|
@ -152,14 +166,23 @@ class Telegram(RPCHandler):
|
|||
logger.info('using custom keyboard from '
|
||||
f'config.json: {self._keyboard}')
|
||||
|
||||
def _init_telegram_app(self):
|
||||
return Application.builder().token(self._config['telegram']['token']).build()
|
||||
|
||||
def _init(self) -> None:
|
||||
"""
|
||||
Initializes this module with the given config,
|
||||
registers all known command handlers
|
||||
and starts polling for message updates
|
||||
Runs in a separate thread.
|
||||
"""
|
||||
self._updater = Updater(token=self._config['telegram']['token'], workers=0,
|
||||
use_context=True)
|
||||
try:
|
||||
self._loop = asyncio.get_running_loop()
|
||||
except RuntimeError:
|
||||
self._loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(self._loop)
|
||||
|
||||
self._app = self._init_telegram_app()
|
||||
|
||||
# Register command handler and start telegram message polling
|
||||
handles = [
|
||||
|
@ -218,21 +241,38 @@ class Telegram(RPCHandler):
|
|||
CallbackQueryHandler(self._force_enter_inline, pattern=r"\S+\/\S+"),
|
||||
]
|
||||
for handle in handles:
|
||||
self._updater.dispatcher.add_handler(handle)
|
||||
self._app.add_handler(handle)
|
||||
|
||||
for callback in callbacks:
|
||||
self._updater.dispatcher.add_handler(callback)
|
||||
self._app.add_handler(callback)
|
||||
|
||||
self._updater.start_polling(
|
||||
bootstrap_retries=-1,
|
||||
timeout=20,
|
||||
read_latency=60, # Assumed transmission latency
|
||||
drop_pending_updates=True,
|
||||
)
|
||||
logger.info(
|
||||
'rpc.telegram is listening for following commands: %s',
|
||||
[h.command for h in handles]
|
||||
[[x for x in sorted(h.commands)] for h in handles]
|
||||
)
|
||||
self._loop.run_until_complete(self._startup_telegram())
|
||||
|
||||
async def _startup_telegram(self) -> None:
|
||||
await self._app.initialize()
|
||||
await self._app.start()
|
||||
if self._app.updater:
|
||||
await self._app.updater.start_polling(
|
||||
bootstrap_retries=-1,
|
||||
timeout=20,
|
||||
# read_latency=60, # Assumed transmission latency
|
||||
drop_pending_updates=True,
|
||||
# stop_signals=[], # Necessary as we don't run on the main thread
|
||||
)
|
||||
while True:
|
||||
await asyncio.sleep(10)
|
||||
if not self._app.updater.running:
|
||||
break
|
||||
|
||||
async def _cleanup_telegram(self) -> None:
|
||||
if self._app.updater:
|
||||
await self._app.updater.stop()
|
||||
await self._app.stop()
|
||||
await self._app.shutdown()
|
||||
|
||||
def cleanup(self) -> None:
|
||||
"""
|
||||
|
@ -240,7 +280,8 @@ class Telegram(RPCHandler):
|
|||
:return: None
|
||||
"""
|
||||
# This can take up to `timeout` from the call to `start_polling`.
|
||||
self._updater.stop()
|
||||
asyncio.run_coroutine_threadsafe(self._cleanup_telegram(), self._loop)
|
||||
self._thread.join()
|
||||
|
||||
def _exchange_from_msg(self, msg: Dict[str, Any]) -> str:
|
||||
"""
|
||||
|
@ -453,7 +494,9 @@ class Telegram(RPCHandler):
|
|||
|
||||
message = self.compose_message(deepcopy(msg), msg_type) # type: ignore
|
||||
if message:
|
||||
self._send_msg(message, disable_notification=(noti == 'silent'))
|
||||
asyncio.run_coroutine_threadsafe(
|
||||
self._send_msg(message, disable_notification=(noti == 'silent')),
|
||||
self._loop)
|
||||
|
||||
def _get_sell_emoji(self, msg):
|
||||
"""
|
||||
|
@ -536,7 +579,7 @@ class Telegram(RPCHandler):
|
|||
return lines_detail
|
||||
|
||||
@authorized_only
|
||||
def _status(self, update: Update, context: CallbackContext) -> None:
|
||||
async def _status(self, update: Update, context: CallbackContext) -> None:
|
||||
"""
|
||||
Handler for /status.
|
||||
Returns the current TradeThread status
|
||||
|
@ -546,12 +589,12 @@ class Telegram(RPCHandler):
|
|||
"""
|
||||
|
||||
if context.args and 'table' in context.args:
|
||||
self._status_table(update, context)
|
||||
await self._status_table(update, context)
|
||||
return
|
||||
else:
|
||||
self._status_msg(update, context)
|
||||
await self._status_msg(update, context)
|
||||
|
||||
def _status_msg(self, update: Update, context: CallbackContext) -> None:
|
||||
async def _status_msg(self, update: Update, context: CallbackContext) -> None:
|
||||
"""
|
||||
handler for `/status` and `/status <id>`.
|
||||
|
||||
|
@ -635,9 +678,9 @@ class Telegram(RPCHandler):
|
|||
lines_detail = self._prepare_order_details(
|
||||
r['orders'], r['quote_currency'], r['is_open'])
|
||||
lines.extend(lines_detail if lines_detail else "")
|
||||
self.__send_status_msg(lines, r)
|
||||
await self.__send_status_msg(lines, r)
|
||||
|
||||
def __send_status_msg(self, lines: List[str], r: Dict[str, Any]) -> None:
|
||||
async def __send_status_msg(self, lines: List[str], r: Dict[str, Any]) -> None:
|
||||
"""
|
||||
Send status message.
|
||||
"""
|
||||
|
@ -648,13 +691,13 @@ class Telegram(RPCHandler):
|
|||
if (len(msg) + len(line) + 1) < MAX_MESSAGE_LENGTH:
|
||||
msg += line + '\n'
|
||||
else:
|
||||
self._send_msg(msg.format(**r))
|
||||
await self._send_msg(msg.format(**r))
|
||||
msg = "*Trade ID:* `{trade_id}` - continued\n" + line + '\n'
|
||||
|
||||
self._send_msg(msg.format(**r))
|
||||
await self._send_msg(msg.format(**r))
|
||||
|
||||
@authorized_only
|
||||
def _status_table(self, update: Update, context: CallbackContext) -> None:
|
||||
async def _status_table(self, update: Update, context: CallbackContext) -> None:
|
||||
"""
|
||||
Handler for /status table.
|
||||
Returns the current TradeThread status in table format
|
||||
|
@ -687,12 +730,11 @@ class Telegram(RPCHandler):
|
|||
# insert separators line between Total
|
||||
lines = message.split("\n")
|
||||
message = "\n".join(lines[:-1] + [lines[1]] + [lines[-1]])
|
||||
self._send_msg(f"<pre>{message}</pre>", parse_mode=ParseMode.HTML,
|
||||
reload_able=True, callback_path="update_status_table",
|
||||
query=update.callback_query)
|
||||
await self._send_msg(f"<pre>{message}</pre>", parse_mode=ParseMode.HTML,
|
||||
reload_able=True, callback_path="update_status_table",
|
||||
query=update.callback_query)
|
||||
|
||||
@authorized_only
|
||||
def _timeunit_stats(self, update: Update, context: CallbackContext, unit: str) -> None:
|
||||
async def _timeunit_stats(self, update: Update, context: CallbackContext, unit: str) -> None:
|
||||
"""
|
||||
Handler for /daily <n>
|
||||
Returns a daily profit (in BTC) over the last n days.
|
||||
|
@ -739,11 +781,11 @@ class Telegram(RPCHandler):
|
|||
f'<b>{val.message} Profit over the last {timescale} {val.message2}</b>:\n'
|
||||
f'<pre>{stats_tab}</pre>'
|
||||
)
|
||||
self._send_msg(message, parse_mode=ParseMode.HTML, reload_able=True,
|
||||
callback_path=val.callback, query=update.callback_query)
|
||||
await self._send_msg(message, parse_mode=ParseMode.HTML, reload_able=True,
|
||||
callback_path=val.callback, query=update.callback_query)
|
||||
|
||||
@authorized_only
|
||||
def _daily(self, update: Update, context: CallbackContext) -> None:
|
||||
async def _daily(self, update: Update, context: CallbackContext) -> None:
|
||||
"""
|
||||
Handler for /daily <n>
|
||||
Returns a daily profit (in BTC) over the last n days.
|
||||
|
@ -751,10 +793,10 @@ class Telegram(RPCHandler):
|
|||
:param update: message update
|
||||
:return: None
|
||||
"""
|
||||
self._timeunit_stats(update, context, 'days')
|
||||
await self._timeunit_stats(update, context, 'days')
|
||||
|
||||
@authorized_only
|
||||
def _weekly(self, update: Update, context: CallbackContext) -> None:
|
||||
async def _weekly(self, update: Update, context: CallbackContext) -> None:
|
||||
"""
|
||||
Handler for /weekly <n>
|
||||
Returns a weekly profit (in BTC) over the last n weeks.
|
||||
|
@ -762,10 +804,10 @@ class Telegram(RPCHandler):
|
|||
:param update: message update
|
||||
:return: None
|
||||
"""
|
||||
self._timeunit_stats(update, context, 'weeks')
|
||||
await self._timeunit_stats(update, context, 'weeks')
|
||||
|
||||
@authorized_only
|
||||
def _monthly(self, update: Update, context: CallbackContext) -> None:
|
||||
async def _monthly(self, update: Update, context: CallbackContext) -> None:
|
||||
"""
|
||||
Handler for /monthly <n>
|
||||
Returns a monthly profit (in BTC) over the last n months.
|
||||
|
@ -773,10 +815,10 @@ class Telegram(RPCHandler):
|
|||
:param update: message update
|
||||
:return: None
|
||||
"""
|
||||
self._timeunit_stats(update, context, 'months')
|
||||
await self._timeunit_stats(update, context, 'months')
|
||||
|
||||
@authorized_only
|
||||
def _profit(self, update: Update, context: CallbackContext) -> None:
|
||||
async def _profit(self, update: Update, context: CallbackContext) -> None:
|
||||
"""
|
||||
Handler for /profit.
|
||||
Returns a cumulative profit statistics.
|
||||
|
@ -850,11 +892,11 @@ class Telegram(RPCHandler):
|
|||
f"*Max Drawdown:* `{stats['max_drawdown']:.2%} "
|
||||
f"({round_coin_value(stats['max_drawdown_abs'], stake_cur)})`"
|
||||
)
|
||||
self._send_msg(markdown_msg, reload_able=True, callback_path="update_profit",
|
||||
query=update.callback_query)
|
||||
await self._send_msg(markdown_msg, reload_able=True, callback_path="update_profit",
|
||||
query=update.callback_query)
|
||||
|
||||
@authorized_only
|
||||
def _stats(self, update: Update, context: CallbackContext) -> None:
|
||||
async def _stats(self, update: Update, context: CallbackContext) -> None:
|
||||
"""
|
||||
Handler for /stats
|
||||
Show stats of recent trades
|
||||
|
@ -885,7 +927,7 @@ class Telegram(RPCHandler):
|
|||
headers=['Exit Reason', 'Exits', 'Wins', 'Losses']
|
||||
)
|
||||
if len(exit_reasons_tabulate) > 25:
|
||||
self._send_msg(f"```\n{exit_reasons_msg}```", ParseMode.MARKDOWN)
|
||||
await self._send_msg(f"```\n{exit_reasons_msg}```", ParseMode.MARKDOWN)
|
||||
exit_reasons_msg = ''
|
||||
|
||||
durations = stats['durations']
|
||||
|
@ -900,10 +942,10 @@ class Telegram(RPCHandler):
|
|||
)
|
||||
msg = (f"""```\n{exit_reasons_msg}```\n```\n{duration_msg}```""")
|
||||
|
||||
self._send_msg(msg, ParseMode.MARKDOWN)
|
||||
await self._send_msg(msg, ParseMode.MARKDOWN)
|
||||
|
||||
@authorized_only
|
||||
def _balance(self, update: Update, context: CallbackContext) -> None:
|
||||
async def _balance(self, update: Update, context: CallbackContext) -> None:
|
||||
""" Handler for /balance """
|
||||
full_result = context.args and 'full' in context.args
|
||||
result = self._rpc._rpc_balance(self._config['stake_currency'],
|
||||
|
@ -957,7 +999,7 @@ class Telegram(RPCHandler):
|
|||
|
||||
# Handle overflowing message length
|
||||
if len(output + curr_output) >= MAX_MESSAGE_LENGTH:
|
||||
self._send_msg(output)
|
||||
await self._send_msg(output)
|
||||
output = curr_output
|
||||
else:
|
||||
output += curr_output
|
||||
|
@ -981,11 +1023,11 @@ class Telegram(RPCHandler):
|
|||
f"\t`{result['stake']}: {total_stake}`{stake_improve}\n"
|
||||
f"\t`{result['symbol']}: {value}`{fiat_val}\n"
|
||||
)
|
||||
self._send_msg(output, reload_able=True, callback_path="update_balance",
|
||||
query=update.callback_query)
|
||||
await self._send_msg(output, reload_able=True, callback_path="update_balance",
|
||||
query=update.callback_query)
|
||||
|
||||
@authorized_only
|
||||
def _start(self, update: Update, context: CallbackContext) -> None:
|
||||
async def _start(self, update: Update, context: CallbackContext) -> None:
|
||||
"""
|
||||
Handler for /start.
|
||||
Starts TradeThread
|
||||
|
@ -994,10 +1036,10 @@ class Telegram(RPCHandler):
|
|||
:return: None
|
||||
"""
|
||||
msg = self._rpc._rpc_start()
|
||||
self._send_msg(f"Status: `{msg['status']}`")
|
||||
await self._send_msg(f"Status: `{msg['status']}`")
|
||||
|
||||
@authorized_only
|
||||
def _stop(self, update: Update, context: CallbackContext) -> None:
|
||||
async def _stop(self, update: Update, context: CallbackContext) -> None:
|
||||
"""
|
||||
Handler for /stop.
|
||||
Stops TradeThread
|
||||
|
@ -1006,10 +1048,10 @@ class Telegram(RPCHandler):
|
|||
:return: None
|
||||
"""
|
||||
msg = self._rpc._rpc_stop()
|
||||
self._send_msg(f"Status: `{msg['status']}`")
|
||||
await self._send_msg(f"Status: `{msg['status']}`")
|
||||
|
||||
@authorized_only
|
||||
def _reload_config(self, update: Update, context: CallbackContext) -> None:
|
||||
async def _reload_config(self, update: Update, context: CallbackContext) -> None:
|
||||
"""
|
||||
Handler for /reload_config.
|
||||
Triggers a config file reload
|
||||
|
@ -1018,10 +1060,10 @@ class Telegram(RPCHandler):
|
|||
:return: None
|
||||
"""
|
||||
msg = self._rpc._rpc_reload_config()
|
||||
self._send_msg(f"Status: `{msg['status']}`")
|
||||
await self._send_msg(f"Status: `{msg['status']}`")
|
||||
|
||||
@authorized_only
|
||||
def _stopentry(self, update: Update, context: CallbackContext) -> None:
|
||||
async def _stopentry(self, update: Update, context: CallbackContext) -> None:
|
||||
"""
|
||||
Handler for /stop_buy.
|
||||
Sets max_open_trades to 0 and gracefully sells all open trades
|
||||
|
@ -1030,10 +1072,10 @@ class Telegram(RPCHandler):
|
|||
:return: None
|
||||
"""
|
||||
msg = self._rpc._rpc_stopentry()
|
||||
self._send_msg(f"Status: `{msg['status']}`")
|
||||
await self._send_msg(f"Status: `{msg['status']}`")
|
||||
|
||||
@authorized_only
|
||||
def _force_exit(self, update: Update, context: CallbackContext) -> None:
|
||||
async def _force_exit(self, update: Update, context: CallbackContext) -> None:
|
||||
"""
|
||||
Handler for /forceexit <id>.
|
||||
Sells the given trade at current price
|
||||
|
@ -1044,14 +1086,14 @@ class Telegram(RPCHandler):
|
|||
|
||||
if context.args:
|
||||
trade_id = context.args[0]
|
||||
self._force_exit_action(trade_id)
|
||||
await self._force_exit_action(trade_id)
|
||||
else:
|
||||
fiat_currency = self._config.get('fiat_display_currency', '')
|
||||
try:
|
||||
statlist, _, _ = self._rpc._rpc_status_table(
|
||||
self._config['stake_currency'], fiat_currency)
|
||||
except RPCException:
|
||||
self._send_msg(msg='No open trade found.')
|
||||
await self._send_msg(msg='No open trade found.')
|
||||
return
|
||||
trades = []
|
||||
for trade in statlist:
|
||||
|
@ -1064,51 +1106,51 @@ class Telegram(RPCHandler):
|
|||
|
||||
buttons_aligned.append([InlineKeyboardButton(
|
||||
text='Cancel', callback_data='force_exit__cancel')])
|
||||
self._send_msg(msg="Which trade?", keyboard=buttons_aligned)
|
||||
await self._send_msg(msg="Which trade?", keyboard=buttons_aligned)
|
||||
|
||||
def _force_exit_action(self, trade_id):
|
||||
async def _force_exit_action(self, trade_id):
|
||||
if trade_id != 'cancel':
|
||||
try:
|
||||
self._rpc._rpc_force_exit(trade_id)
|
||||
except RPCException as e:
|
||||
self._send_msg(str(e))
|
||||
await self._send_msg(str(e))
|
||||
|
||||
def _force_exit_inline(self, update: Update, _: CallbackContext) -> None:
|
||||
async def _force_exit_inline(self, update: Update, _: CallbackContext) -> None:
|
||||
if update.callback_query:
|
||||
query = update.callback_query
|
||||
if query.data and '__' in query.data:
|
||||
# Input data is "force_exit__<tradid|cancel>"
|
||||
trade_id = query.data.split("__")[1].split(' ')[0]
|
||||
if trade_id == 'cancel':
|
||||
query.answer()
|
||||
query.edit_message_text(text="Force exit canceled.")
|
||||
await query.answer()
|
||||
await query.edit_message_text(text="Force exit canceled.")
|
||||
return
|
||||
trade: Optional[Trade] = Trade.get_trades(trade_filter=Trade.id == trade_id).first()
|
||||
query.answer()
|
||||
await query.answer()
|
||||
if trade:
|
||||
query.edit_message_text(
|
||||
await query.edit_message_text(
|
||||
text=f"Manually exiting Trade #{trade_id}, {trade.pair}")
|
||||
self._force_exit_action(trade_id)
|
||||
await self._force_exit_action(trade_id)
|
||||
else:
|
||||
query.edit_message_text(text=f"Trade {trade_id} not found.")
|
||||
await query.edit_message_text(text=f"Trade {trade_id} not found.")
|
||||
|
||||
def _force_enter_action(self, pair, price: Optional[float], order_side: SignalDirection):
|
||||
async def _force_enter_action(self, pair, price: Optional[float], order_side: SignalDirection):
|
||||
if pair != 'cancel':
|
||||
try:
|
||||
self._rpc._rpc_force_entry(pair, price, order_side=order_side)
|
||||
except RPCException as e:
|
||||
logger.exception("Forcebuy error!")
|
||||
self._send_msg(str(e), ParseMode.HTML)
|
||||
await self._send_msg(str(e), ParseMode.HTML)
|
||||
|
||||
def _force_enter_inline(self, update: Update, _: CallbackContext) -> None:
|
||||
async def _force_enter_inline(self, update: Update, _: CallbackContext) -> None:
|
||||
if update.callback_query:
|
||||
query = update.callback_query
|
||||
if query.data and '_||_' in query.data:
|
||||
pair, side = query.data.split('_||_')
|
||||
order_side = SignalDirection(side)
|
||||
query.answer()
|
||||
query.edit_message_text(text=f"Manually entering {order_side} for {pair}")
|
||||
self._force_enter_action(pair, None, order_side)
|
||||
await query.answer()
|
||||
await query.edit_message_text(text=f"Manually entering {order_side} for {pair}")
|
||||
await self._force_enter_action(pair, None, order_side)
|
||||
|
||||
@staticmethod
|
||||
def _layout_inline_keyboard(
|
||||
|
@ -1121,7 +1163,7 @@ class Telegram(RPCHandler):
|
|||
return [buttons[i:i + cols] for i in range(0, len(buttons), cols)]
|
||||
|
||||
@authorized_only
|
||||
def _force_enter(
|
||||
async def _force_enter(
|
||||
self, update: Update, context: CallbackContext, order_side: SignalDirection) -> None:
|
||||
"""
|
||||
Handler for /forcelong <asset> <price> and `/forceshort <asset> <price>
|
||||
|
@ -1133,7 +1175,7 @@ class Telegram(RPCHandler):
|
|||
if context.args:
|
||||
pair = context.args[0]
|
||||
price = float(context.args[1]) if len(context.args) > 1 else None
|
||||
self._force_enter_action(pair, price, order_side)
|
||||
await self._force_enter_action(pair, price, order_side)
|
||||
else:
|
||||
whitelist = self._rpc._rpc_whitelist()['whitelist']
|
||||
pair_buttons = [
|
||||
|
@ -1143,12 +1185,12 @@ class Telegram(RPCHandler):
|
|||
buttons_aligned = self._layout_inline_keyboard(pair_buttons)
|
||||
|
||||
buttons_aligned.append([InlineKeyboardButton(text='Cancel', callback_data='cancel')])
|
||||
self._send_msg(msg="Which pair?",
|
||||
keyboard=buttons_aligned,
|
||||
query=update.callback_query)
|
||||
await self._send_msg(msg="Which pair?",
|
||||
keyboard=buttons_aligned,
|
||||
query=update.callback_query)
|
||||
|
||||
@authorized_only
|
||||
def _trades(self, update: Update, context: CallbackContext) -> None:
|
||||
async def _trades(self, update: Update, context: CallbackContext) -> None:
|
||||
"""
|
||||
Handler for /trades <n>
|
||||
Returns last n recent trades.
|
||||
|
@ -1177,10 +1219,10 @@ class Telegram(RPCHandler):
|
|||
tablefmt='simple')
|
||||
message = (f"<b>{min(trades['trades_count'], nrecent)} recent trades</b>:\n"
|
||||
+ (f"<pre>{trades_tab}</pre>" if trades['trades_count'] > 0 else ''))
|
||||
self._send_msg(message, parse_mode=ParseMode.HTML)
|
||||
await self._send_msg(message, parse_mode=ParseMode.HTML)
|
||||
|
||||
@authorized_only
|
||||
def _delete_trade(self, update: Update, context: CallbackContext) -> None:
|
||||
async def _delete_trade(self, update: Update, context: CallbackContext) -> None:
|
||||
"""
|
||||
Handler for /delete <id>.
|
||||
Delete the given trade
|
||||
|
@ -1192,13 +1234,13 @@ class Telegram(RPCHandler):
|
|||
raise RPCException("Trade-id not set.")
|
||||
trade_id = int(context.args[0])
|
||||
msg = self._rpc._rpc_delete(trade_id)
|
||||
self._send_msg(
|
||||
await self._send_msg(
|
||||
f"`{msg['result_msg']}`\n"
|
||||
'Please make sure to take care of this asset on the exchange manually.'
|
||||
)
|
||||
|
||||
@authorized_only
|
||||
def _cancel_open_order(self, update: Update, context: CallbackContext) -> None:
|
||||
async def _cancel_open_order(self, update: Update, context: CallbackContext) -> None:
|
||||
"""
|
||||
Handler for /cancel_open_order <id>.
|
||||
Cancel open order for tradeid
|
||||
|
@ -1210,10 +1252,10 @@ class Telegram(RPCHandler):
|
|||
raise RPCException("Trade-id not set.")
|
||||
trade_id = int(context.args[0])
|
||||
self._rpc._rpc_cancel_open_order(trade_id)
|
||||
self._send_msg('Open order canceled.')
|
||||
await self._send_msg('Open order canceled.')
|
||||
|
||||
@authorized_only
|
||||
def _performance(self, update: Update, context: CallbackContext) -> None:
|
||||
async def _performance(self, update: Update, context: CallbackContext) -> None:
|
||||
"""
|
||||
Handler for /performance.
|
||||
Shows a performance statistic from finished trades
|
||||
|
@ -1231,17 +1273,17 @@ class Telegram(RPCHandler):
|
|||
f"({trade['count']})</code>\n")
|
||||
|
||||
if len(output + stat_line) >= MAX_MESSAGE_LENGTH:
|
||||
self._send_msg(output, parse_mode=ParseMode.HTML)
|
||||
await self._send_msg(output, parse_mode=ParseMode.HTML)
|
||||
output = stat_line
|
||||
else:
|
||||
output += stat_line
|
||||
|
||||
self._send_msg(output, parse_mode=ParseMode.HTML,
|
||||
reload_able=True, callback_path="update_performance",
|
||||
query=update.callback_query)
|
||||
await self._send_msg(output, parse_mode=ParseMode.HTML,
|
||||
reload_able=True, callback_path="update_performance",
|
||||
query=update.callback_query)
|
||||
|
||||
@authorized_only
|
||||
def _enter_tag_performance(self, update: Update, context: CallbackContext) -> None:
|
||||
async def _enter_tag_performance(self, update: Update, context: CallbackContext) -> None:
|
||||
"""
|
||||
Handler for /buys PAIR .
|
||||
Shows a performance statistic from finished trades
|
||||
|
@ -1263,17 +1305,17 @@ class Telegram(RPCHandler):
|
|||
f"({trade['count']})</code>\n")
|
||||
|
||||
if len(output + stat_line) >= MAX_MESSAGE_LENGTH:
|
||||
self._send_msg(output, parse_mode=ParseMode.HTML)
|
||||
await self._send_msg(output, parse_mode=ParseMode.HTML)
|
||||
output = stat_line
|
||||
else:
|
||||
output += stat_line
|
||||
|
||||
self._send_msg(output, parse_mode=ParseMode.HTML,
|
||||
reload_able=True, callback_path="update_enter_tag_performance",
|
||||
query=update.callback_query)
|
||||
await self._send_msg(output, parse_mode=ParseMode.HTML,
|
||||
reload_able=True, callback_path="update_enter_tag_performance",
|
||||
query=update.callback_query)
|
||||
|
||||
@authorized_only
|
||||
def _exit_reason_performance(self, update: Update, context: CallbackContext) -> None:
|
||||
async def _exit_reason_performance(self, update: Update, context: CallbackContext) -> None:
|
||||
"""
|
||||
Handler for /sells.
|
||||
Shows a performance statistic from finished trades
|
||||
|
@ -1295,17 +1337,17 @@ class Telegram(RPCHandler):
|
|||
f"({trade['count']})</code>\n")
|
||||
|
||||
if len(output + stat_line) >= MAX_MESSAGE_LENGTH:
|
||||
self._send_msg(output, parse_mode=ParseMode.HTML)
|
||||
await self._send_msg(output, parse_mode=ParseMode.HTML)
|
||||
output = stat_line
|
||||
else:
|
||||
output += stat_line
|
||||
|
||||
self._send_msg(output, parse_mode=ParseMode.HTML,
|
||||
reload_able=True, callback_path="update_exit_reason_performance",
|
||||
query=update.callback_query)
|
||||
await self._send_msg(output, parse_mode=ParseMode.HTML,
|
||||
reload_able=True, callback_path="update_exit_reason_performance",
|
||||
query=update.callback_query)
|
||||
|
||||
@authorized_only
|
||||
def _mix_tag_performance(self, update: Update, context: CallbackContext) -> None:
|
||||
async def _mix_tag_performance(self, update: Update, context: CallbackContext) -> None:
|
||||
"""
|
||||
Handler for /mix_tags.
|
||||
Shows a performance statistic from finished trades
|
||||
|
@ -1327,17 +1369,17 @@ class Telegram(RPCHandler):
|
|||
f"({trade['count']})</code>\n")
|
||||
|
||||
if len(output + stat_line) >= MAX_MESSAGE_LENGTH:
|
||||
self._send_msg(output, parse_mode=ParseMode.HTML)
|
||||
await self._send_msg(output, parse_mode=ParseMode.HTML)
|
||||
output = stat_line
|
||||
else:
|
||||
output += stat_line
|
||||
|
||||
self._send_msg(output, parse_mode=ParseMode.HTML,
|
||||
reload_able=True, callback_path="update_mix_tag_performance",
|
||||
query=update.callback_query)
|
||||
await self._send_msg(output, parse_mode=ParseMode.HTML,
|
||||
reload_able=True, callback_path="update_mix_tag_performance",
|
||||
query=update.callback_query)
|
||||
|
||||
@authorized_only
|
||||
def _count(self, update: Update, context: CallbackContext) -> None:
|
||||
async def _count(self, update: Update, context: CallbackContext) -> None:
|
||||
"""
|
||||
Handler for /count.
|
||||
Returns the number of trades running
|
||||
|
@ -1351,19 +1393,19 @@ class Telegram(RPCHandler):
|
|||
tablefmt='simple')
|
||||
message = f"<pre>{message}</pre>"
|
||||
logger.debug(message)
|
||||
self._send_msg(message, parse_mode=ParseMode.HTML,
|
||||
reload_able=True, callback_path="update_count",
|
||||
query=update.callback_query)
|
||||
await self._send_msg(message, parse_mode=ParseMode.HTML,
|
||||
reload_able=True, callback_path="update_count",
|
||||
query=update.callback_query)
|
||||
|
||||
@authorized_only
|
||||
def _locks(self, update: Update, context: CallbackContext) -> None:
|
||||
async def _locks(self, update: Update, context: CallbackContext) -> None:
|
||||
"""
|
||||
Handler for /locks.
|
||||
Returns the currently active locks
|
||||
"""
|
||||
rpc_locks = self._rpc._rpc_locks()
|
||||
if not rpc_locks['locks']:
|
||||
self._send_msg('No active locks.', parse_mode=ParseMode.HTML)
|
||||
await self._send_msg('No active locks.', parse_mode=ParseMode.HTML)
|
||||
|
||||
for locks in chunks(rpc_locks['locks'], 25):
|
||||
message = tabulate([[
|
||||
|
@ -1375,10 +1417,10 @@ class Telegram(RPCHandler):
|
|||
tablefmt='simple')
|
||||
message = f"<pre>{escape(message)}</pre>"
|
||||
logger.debug(message)
|
||||
self._send_msg(message, parse_mode=ParseMode.HTML)
|
||||
await self._send_msg(message, parse_mode=ParseMode.HTML)
|
||||
|
||||
@authorized_only
|
||||
def _delete_locks(self, update: Update, context: CallbackContext) -> None:
|
||||
async def _delete_locks(self, update: Update, context: CallbackContext) -> None:
|
||||
"""
|
||||
Handler for /delete_locks.
|
||||
Returns the currently active locks
|
||||
|
@ -1393,10 +1435,10 @@ class Telegram(RPCHandler):
|
|||
pair = arg
|
||||
|
||||
self._rpc._rpc_delete_lock(lockid=lockid, pair=pair)
|
||||
self._locks(update, context)
|
||||
await self._locks(update, context)
|
||||
|
||||
@authorized_only
|
||||
def _whitelist(self, update: Update, context: CallbackContext) -> None:
|
||||
async def _whitelist(self, update: Update, context: CallbackContext) -> None:
|
||||
"""
|
||||
Handler for /whitelist
|
||||
Shows the currently active whitelist
|
||||
|
@ -1413,39 +1455,39 @@ class Telegram(RPCHandler):
|
|||
message += f"`{', '.join(whitelist['whitelist'])}`"
|
||||
|
||||
logger.debug(message)
|
||||
self._send_msg(message)
|
||||
await self._send_msg(message)
|
||||
|
||||
@authorized_only
|
||||
def _blacklist(self, update: Update, context: CallbackContext) -> None:
|
||||
async def _blacklist(self, update: Update, context: CallbackContext) -> None:
|
||||
"""
|
||||
Handler for /blacklist
|
||||
Shows the currently active blacklist
|
||||
"""
|
||||
self.send_blacklist_msg(self._rpc._rpc_blacklist(context.args))
|
||||
await self.send_blacklist_msg(self._rpc._rpc_blacklist(context.args))
|
||||
|
||||
def send_blacklist_msg(self, blacklist: Dict):
|
||||
async def send_blacklist_msg(self, blacklist: Dict):
|
||||
errmsgs = []
|
||||
for pair, error in blacklist['errors'].items():
|
||||
errmsgs.append(f"Error: {error['error_msg']}")
|
||||
if errmsgs:
|
||||
self._send_msg('\n'.join(errmsgs))
|
||||
await self._send_msg('\n'.join(errmsgs))
|
||||
|
||||
message = f"Blacklist contains {blacklist['length']} pairs\n"
|
||||
message += f"`{', '.join(blacklist['blacklist'])}`"
|
||||
|
||||
logger.debug(message)
|
||||
self._send_msg(message)
|
||||
await self._send_msg(message)
|
||||
|
||||
@authorized_only
|
||||
def _blacklist_delete(self, update: Update, context: CallbackContext) -> None:
|
||||
async def _blacklist_delete(self, update: Update, context: CallbackContext) -> None:
|
||||
"""
|
||||
Handler for /bl_delete
|
||||
Deletes pair(s) from current blacklist
|
||||
"""
|
||||
self.send_blacklist_msg(self._rpc._rpc_blacklist_delete(context.args or []))
|
||||
await self.send_blacklist_msg(self._rpc._rpc_blacklist_delete(context.args or []))
|
||||
|
||||
@authorized_only
|
||||
def _logs(self, update: Update, context: CallbackContext) -> None:
|
||||
async def _logs(self, update: Update, context: CallbackContext) -> None:
|
||||
"""
|
||||
Handler for /logs
|
||||
Shows the latest logs
|
||||
|
@ -1464,17 +1506,17 @@ class Telegram(RPCHandler):
|
|||
escape_markdown(logrec[4], version=2))
|
||||
if len(msgs + msg) + 10 >= MAX_MESSAGE_LENGTH:
|
||||
# Send message immediately if it would become too long
|
||||
self._send_msg(msgs, parse_mode=ParseMode.MARKDOWN_V2)
|
||||
await self._send_msg(msgs, parse_mode=ParseMode.MARKDOWN_V2)
|
||||
msgs = msg + '\n'
|
||||
else:
|
||||
# Append message to messages to send
|
||||
msgs += msg + '\n'
|
||||
|
||||
if msgs:
|
||||
self._send_msg(msgs, parse_mode=ParseMode.MARKDOWN_V2)
|
||||
await self._send_msg(msgs, parse_mode=ParseMode.MARKDOWN_V2)
|
||||
|
||||
@authorized_only
|
||||
def _edge(self, update: Update, context: CallbackContext) -> None:
|
||||
async def _edge(self, update: Update, context: CallbackContext) -> None:
|
||||
"""
|
||||
Handler for /edge
|
||||
Shows information related to Edge
|
||||
|
@ -1482,17 +1524,17 @@ class Telegram(RPCHandler):
|
|||
edge_pairs = self._rpc._rpc_edge()
|
||||
if not edge_pairs:
|
||||
message = '<b>Edge only validated following pairs:</b>'
|
||||
self._send_msg(message, parse_mode=ParseMode.HTML)
|
||||
await self._send_msg(message, parse_mode=ParseMode.HTML)
|
||||
|
||||
for chunk in chunks(edge_pairs, 25):
|
||||
edge_pairs_tab = tabulate(chunk, headers='keys', tablefmt='simple')
|
||||
message = (f'<b>Edge only validated following pairs:</b>\n'
|
||||
f'<pre>{edge_pairs_tab}</pre>')
|
||||
|
||||
self._send_msg(message, parse_mode=ParseMode.HTML)
|
||||
await self._send_msg(message, parse_mode=ParseMode.HTML)
|
||||
|
||||
@authorized_only
|
||||
def _help(self, update: Update, context: CallbackContext) -> None:
|
||||
async def _help(self, update: Update, context: CallbackContext) -> None:
|
||||
"""
|
||||
Handler for /help.
|
||||
Show commands of the bot
|
||||
|
@ -1570,20 +1612,20 @@ class Telegram(RPCHandler):
|
|||
"*/version:* `Show version`"
|
||||
)
|
||||
|
||||
self._send_msg(message, parse_mode=ParseMode.MARKDOWN)
|
||||
await self._send_msg(message, parse_mode=ParseMode.MARKDOWN)
|
||||
|
||||
@authorized_only
|
||||
def _health(self, update: Update, context: CallbackContext) -> None:
|
||||
async def _health(self, update: Update, context: CallbackContext) -> None:
|
||||
"""
|
||||
Handler for /health
|
||||
Shows the last process timestamp
|
||||
"""
|
||||
health = self._rpc.health()
|
||||
message = f"Last process: `{health['last_process_loc']}`"
|
||||
self._send_msg(message)
|
||||
await self._send_msg(message)
|
||||
|
||||
@authorized_only
|
||||
def _version(self, update: Update, context: CallbackContext) -> None:
|
||||
async def _version(self, update: Update, context: CallbackContext) -> None:
|
||||
"""
|
||||
Handler for /version.
|
||||
Show version information
|
||||
|
@ -1596,10 +1638,10 @@ class Telegram(RPCHandler):
|
|||
if strategy_version is not None:
|
||||
version_string += f', *Strategy version: * `{strategy_version}`'
|
||||
|
||||
self._send_msg(version_string)
|
||||
await self._send_msg(version_string)
|
||||
|
||||
@authorized_only
|
||||
def _show_config(self, update: Update, context: CallbackContext) -> None:
|
||||
async def _show_config(self, update: Update, context: CallbackContext) -> None:
|
||||
"""
|
||||
Handler for /show_config.
|
||||
Show config information information
|
||||
|
@ -1628,7 +1670,7 @@ class Telegram(RPCHandler):
|
|||
else:
|
||||
pa_info = "*Position adjustment:* Off\n"
|
||||
|
||||
self._send_msg(
|
||||
await self._send_msg(
|
||||
f"*Mode:* `{'Dry-run' if val['dry_run'] else 'Live'}`\n"
|
||||
f"*Exchange:* `{val['exchange']}`\n"
|
||||
f"*Market: * `{val['trading_mode']}`\n"
|
||||
|
@ -1644,8 +1686,8 @@ class Telegram(RPCHandler):
|
|||
f"*Current state:* `{val['state']}`"
|
||||
)
|
||||
|
||||
def _update_msg(self, query: CallbackQuery, msg: str, callback_path: str = "",
|
||||
reload_able: bool = False, parse_mode: str = ParseMode.MARKDOWN) -> None:
|
||||
async def _update_msg(self, query: CallbackQuery, msg: str, callback_path: str = "",
|
||||
reload_able: bool = False, parse_mode: str = ParseMode.MARKDOWN) -> None:
|
||||
if reload_able:
|
||||
reply_markup = InlineKeyboardMarkup([
|
||||
[InlineKeyboardButton("Refresh", callback_data=callback_path)],
|
||||
|
@ -1659,7 +1701,7 @@ class Telegram(RPCHandler):
|
|||
message_id = query.message.message_id
|
||||
|
||||
try:
|
||||
self._updater.bot.edit_message_text(
|
||||
await self._app.bot.edit_message_text(
|
||||
chat_id=chat_id,
|
||||
message_id=message_id,
|
||||
text=msg,
|
||||
|
@ -1674,12 +1716,12 @@ class Telegram(RPCHandler):
|
|||
except TelegramError as telegram_err:
|
||||
logger.warning('TelegramError: %s! Giving up on that message.', telegram_err.message)
|
||||
|
||||
def _send_msg(self, msg: str, parse_mode: str = ParseMode.MARKDOWN,
|
||||
disable_notification: bool = False,
|
||||
keyboard: Optional[List[List[InlineKeyboardButton]]] = None,
|
||||
callback_path: str = "",
|
||||
reload_able: bool = False,
|
||||
query: Optional[CallbackQuery] = None) -> None:
|
||||
async def _send_msg(self, msg: str, parse_mode: str = ParseMode.MARKDOWN,
|
||||
disable_notification: bool = False,
|
||||
keyboard: Optional[List[List[InlineKeyboardButton]]] = None,
|
||||
callback_path: str = "",
|
||||
reload_able: bool = False,
|
||||
query: Optional[CallbackQuery] = None) -> None:
|
||||
"""
|
||||
Send given markdown message
|
||||
:param msg: message
|
||||
|
@ -1689,20 +1731,20 @@ class Telegram(RPCHandler):
|
|||
"""
|
||||
reply_markup: Union[InlineKeyboardMarkup, ReplyKeyboardMarkup]
|
||||
if query:
|
||||
self._update_msg(query=query, msg=msg, parse_mode=parse_mode,
|
||||
callback_path=callback_path, reload_able=reload_able)
|
||||
await self._update_msg(query=query, msg=msg, parse_mode=parse_mode,
|
||||
callback_path=callback_path, reload_able=reload_able)
|
||||
return
|
||||
if reload_able and self._config['telegram'].get('reload', True):
|
||||
reply_markup = InlineKeyboardMarkup([
|
||||
[InlineKeyboardButton("Refresh", callback_data=callback_path)]])
|
||||
else:
|
||||
if keyboard is not None:
|
||||
reply_markup = InlineKeyboardMarkup(keyboard, resize_keyboard=True)
|
||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||
else:
|
||||
reply_markup = ReplyKeyboardMarkup(self._keyboard, resize_keyboard=True)
|
||||
try:
|
||||
try:
|
||||
self._updater.bot.send_message(
|
||||
await self._app.bot.send_message(
|
||||
self._config['telegram']['chat_id'],
|
||||
text=msg,
|
||||
parse_mode=parse_mode,
|
||||
|
@ -1716,7 +1758,7 @@ class Telegram(RPCHandler):
|
|||
'Telegram NetworkError: %s! Trying one more time.',
|
||||
network_err.message
|
||||
)
|
||||
self._updater.bot.send_message(
|
||||
await self._app.bot.send_message(
|
||||
self._config['telegram']['chat_id'],
|
||||
text=msg,
|
||||
parse_mode=parse_mode,
|
||||
|
@ -1730,7 +1772,7 @@ class Telegram(RPCHandler):
|
|||
)
|
||||
|
||||
@authorized_only
|
||||
def _changemarketdir(self, update: Update, context: CallbackContext) -> None:
|
||||
async def _changemarketdir(self, update: Update, context: CallbackContext) -> None:
|
||||
"""
|
||||
Handler for /marketdir.
|
||||
Updates the bot's market_direction
|
||||
|
@ -1753,14 +1795,14 @@ class Telegram(RPCHandler):
|
|||
|
||||
if new_market_dir is not None:
|
||||
self._rpc._update_market_direction(new_market_dir)
|
||||
self._send_msg("Successfully updated market direction"
|
||||
f" from *{old_market_dir}* to *{new_market_dir}*.")
|
||||
await self._send_msg("Successfully updated market direction"
|
||||
f" from *{old_market_dir}* to *{new_market_dir}*.")
|
||||
else:
|
||||
raise RPCException("Invalid market direction provided. \n"
|
||||
"Valid market directions: *long, short, even, none*")
|
||||
elif context.args is not None and len(context.args) == 0:
|
||||
old_market_dir = self._rpc._get_market_direction()
|
||||
self._send_msg(f"Currently set market direction: *{old_market_dir}*")
|
||||
await self._send_msg(f"Currently set market direction: *{old_market_dir}*")
|
||||
else:
|
||||
raise RPCException("Invalid usage of command /marketdir. \n"
|
||||
"Usage: */marketdir [short | long | even | none]*")
|
||||
|
|
|
@ -18,8 +18,6 @@ pytest-random-order==1.1.0
|
|||
isort==5.12.0
|
||||
# For datetime mocking
|
||||
time-machine==2.9.0
|
||||
# fastapi testing
|
||||
httpx==0.24.0
|
||||
|
||||
# Convert jupyter notebooks to markdown documents
|
||||
nbconvert==7.3.1
|
||||
|
|
|
@ -6,7 +6,9 @@ ccxt==3.0.75
|
|||
cryptography==40.0.2
|
||||
aiohttp==3.8.4
|
||||
SQLAlchemy==2.0.10
|
||||
python-telegram-bot==13.15
|
||||
python-telegram-bot==20.2
|
||||
# can't be hard-pinned due to telegram-bot pinning httpx with ~
|
||||
httpx>=0.23.3
|
||||
arrow==1.2.3
|
||||
cachetools==4.2.2
|
||||
requests==2.28.2
|
||||
|
|
|
@ -3,7 +3,7 @@ import json
|
|||
import logging
|
||||
import re
|
||||
from copy import deepcopy
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import timedelta
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
from unittest.mock import MagicMock, Mock, PropertyMock
|
||||
|
@ -12,7 +12,6 @@ import arrow
|
|||
import numpy as np
|
||||
import pandas as pd
|
||||
import pytest
|
||||
from telegram import Chat, Message, Update
|
||||
|
||||
from freqtrade import constants
|
||||
from freqtrade.commands import Arguments
|
||||
|
@ -550,13 +549,6 @@ def get_default_conf_usdt(testdatadir):
|
|||
return configuration
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def update():
|
||||
_update = Update(0)
|
||||
_update.message = Message(0, datetime.utcnow(), Chat(0, 0))
|
||||
return _update
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def fee():
|
||||
return MagicMock(return_value=0.0025)
|
||||
|
|
|
@ -283,7 +283,7 @@ def test_api__init__(default_conf, mocker):
|
|||
"username": "TestUser",
|
||||
"password": "testPass",
|
||||
}})
|
||||
mocker.patch('freqtrade.rpc.telegram.Updater', MagicMock())
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram._init')
|
||||
mocker.patch('freqtrade.rpc.api_server.webserver.ApiServer.start_api', MagicMock())
|
||||
apiserver = ApiServer(default_conf)
|
||||
apiserver.add_rpc_handler(RPC(get_patched_freqtradebot(mocker, default_conf)))
|
||||
|
@ -341,7 +341,7 @@ def test_api_run(default_conf, mocker, caplog):
|
|||
"username": "TestUser",
|
||||
"password": "testPass",
|
||||
}})
|
||||
mocker.patch('freqtrade.rpc.telegram.Updater', MagicMock())
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram._init')
|
||||
|
||||
server_inst_mock = MagicMock()
|
||||
server_inst_mock.run_in_thread = MagicMock()
|
||||
|
@ -419,7 +419,7 @@ def test_api_cleanup(default_conf, mocker, caplog):
|
|||
"username": "TestUser",
|
||||
"password": "testPass",
|
||||
}})
|
||||
mocker.patch('freqtrade.rpc.telegram.Updater', MagicMock())
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram._init')
|
||||
|
||||
server_mock = MagicMock()
|
||||
server_mock.cleanup = MagicMock()
|
||||
|
@ -1877,7 +1877,7 @@ def test_api_ws_send_msg(default_conf, mocker, caplog):
|
|||
"password": _TEST_PASS,
|
||||
"ws_token": _TEST_WS_TOKEN
|
||||
}})
|
||||
mocker.patch('freqtrade.rpc.telegram.Updater')
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram._init')
|
||||
mocker.patch('freqtrade.rpc.api_server.ApiServer.start_api')
|
||||
apiserver = ApiServer(default_conf)
|
||||
apiserver.add_rpc_handler(RPC(get_patched_freqtradebot(mocker, default_conf)))
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user