From a76ca771f8c557a6a596f7b7c0cbb748f6418ade Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 10 Mar 2023 18:00:20 +0100 Subject: [PATCH 1/6] telegram: Fix sending telegram message with exception --- freqtrade/enums/rpcmessagetype.py | 1 + freqtrade/freqtradebot.py | 4 ++-- freqtrade/rpc/telegram.py | 3 +++ freqtrade/rpc/webhook.py | 1 + freqtrade/worker.py | 7 +++++-- 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/freqtrade/enums/rpcmessagetype.py b/freqtrade/enums/rpcmessagetype.py index 404c75401..16d81b1d8 100644 --- a/freqtrade/enums/rpcmessagetype.py +++ b/freqtrade/enums/rpcmessagetype.py @@ -4,6 +4,7 @@ from enum import Enum class RPCMessageType(str, Enum): STATUS = 'status' WARNING = 'warning' + EXCEPTION = 'exception' STARTUP = 'startup' ENTRY = 'entry' diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index cec7176f6..feb0baf5a 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -133,13 +133,13 @@ class FreqtradeBot(LoggingMixin): # Initialize protections AFTER bot start - otherwise parameters are not loaded. self.protections = ProtectionManager(self.config, self.strategy.protections) - def notify_status(self, msg: str) -> None: + def notify_status(self, msg: str, msg_type=RPCMessageType.STATUS) -> None: """ Public method for users of this class (worker, etc.) to send notifications via RPC about changes in the bot status. """ self.rpc.send_msg({ - 'type': RPCMessageType.STATUS, + 'type': msg_type, 'status': msg }) diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 30aa55359..0c0b24f00 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -414,6 +414,9 @@ class Telegram(RPCHandler): elif msg_type == RPCMessageType.WARNING: message = f"\N{WARNING SIGN} *Warning:* `{msg['status']}`" + elif msg_type == RPCMessageType.EXCEPTION: + # Errors will contain exceptions, which are wrapped in tripple ticks. + message = f"\N{WARNING SIGN} *ERROR:* \n {msg['status']}" elif msg_type == RPCMessageType.STARTUP: message = f"{msg['status']}" diff --git a/freqtrade/rpc/webhook.py b/freqtrade/rpc/webhook.py index d81d8d24f..0967de70d 100644 --- a/freqtrade/rpc/webhook.py +++ b/freqtrade/rpc/webhook.py @@ -58,6 +58,7 @@ class Webhook(RPCHandler): valuedict = whconfig.get('webhookexitcancel') elif msg['type'] in (RPCMessageType.STATUS, RPCMessageType.STARTUP, + RPCMessageType.EXCEPTION, RPCMessageType.WARNING): valuedict = whconfig.get('webhookstatus') elif msg['type'].value in whconfig: diff --git a/freqtrade/worker.py b/freqtrade/worker.py index 388163678..fb89e7a2d 100644 --- a/freqtrade/worker.py +++ b/freqtrade/worker.py @@ -12,7 +12,7 @@ import sdnotify from freqtrade import __version__ from freqtrade.configuration import Configuration from freqtrade.constants import PROCESS_THROTTLE_SECS, RETRY_TIMEOUT, Config -from freqtrade.enums import State +from freqtrade.enums import RPCMessageType, State from freqtrade.exceptions import OperationalException, TemporaryError from freqtrade.exchange import timeframe_to_next_date from freqtrade.freqtradebot import FreqtradeBot @@ -185,7 +185,10 @@ class Worker: tb = traceback.format_exc() hint = 'Issue `/start` if you think it is safe to restart.' - self.freqtrade.notify_status(f'OperationalException:\n```\n{tb}```{hint}') + self.freqtrade.notify_status( + f'*OperationalException:*\n```\n{tb}```\n {hint}', + msg_type=RPCMessageType.EXCEPTION + ) logger.exception('OperationalException. Stopping trader ...') self.freqtrade.state = State.STOPPED From a2336f256bda321f54172693335a8dcdafad9e3f Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 11 Mar 2023 08:25:45 +0100 Subject: [PATCH 2/6] Add profit descriptions closes #8234 --- docs/bot-basics.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/bot-basics.md b/docs/bot-basics.md index 925fc7862..1aa8f3085 100644 --- a/docs/bot-basics.md +++ b/docs/bot-basics.md @@ -12,6 +12,9 @@ This page provides you some basic concepts on how Freqtrade works and operates. * **Indicators**: Technical indicators (SMA, EMA, RSI, ...). * **Limit order**: Limit orders which execute at the defined limit price or better. * **Market order**: Guaranteed to fill, may move price depending on the order size. +* **Current Profit**: Currently pending (unrealized) profit for this trade. This is mainly used throughout the bot and UI. +* **Realized Profit**: Already realized profit. Only relevant in combination with [partial exits](strategy-callbacks.md#adjust-trade-position) - which also explains the calculation logic for this. +* **Total Profit**: Combined realized and unrealized profit. The relative number (%) is calculated against the total investment in this trade. ## Fee handling From 39c651e40c9d9f83b4dd2c58e82a17488b475a0c Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 11 Mar 2023 15:03:06 +0100 Subject: [PATCH 3/6] Remove pointless reset of close_profit --- freqtrade/freqtradebot.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index feb0baf5a..281d90432 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1468,8 +1468,6 @@ class FreqtradeBot(LoggingMixin): return False trade.close_rate = None trade.close_rate_requested = None - trade.close_profit = None - trade.close_profit_abs = None # Set exit_reason for fill message exit_reason_prev = trade.exit_reason trade.exit_reason = trade.exit_reason + f", {reason}" if trade.exit_reason else reason From 59d2ff3ffabe55dfeb35b820c9376bf4e5ec32b0 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 11 Mar 2023 15:12:32 +0100 Subject: [PATCH 4/6] Simplify `handle_cancel_exit ` --- freqtrade/freqtradebot.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 281d90432..3924b111f 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1460,32 +1460,32 @@ class FreqtradeBot(LoggingMixin): return False try: - co = self.exchange.cancel_order_with_result(order['id'], trade.pair, - trade.amount) + order = self.exchange.cancel_order_with_result(order['id'], trade.pair, + trade.amount) except InvalidOrderException: logger.exception( f"Could not cancel {trade.exit_side} order {trade.open_order_id}") return False - trade.close_rate = None - trade.close_rate_requested = None + # Set exit_reason for fill message exit_reason_prev = trade.exit_reason trade.exit_reason = trade.exit_reason + f", {reason}" if trade.exit_reason else reason - self.update_trade_state(trade, trade.open_order_id, co) # Order might be filled above in odd timing issues. - if co.get('status') in ('canceled', 'cancelled'): + if order.get('status') in ('canceled', 'cancelled'): trade.exit_reason = None - trade.open_order_id = None else: trade.exit_reason = exit_reason_prev - - logger.info(f'{trade.exit_side.capitalize()} order {reason} for {trade}.') cancelled = True else: reason = constants.CANCEL_REASON['CANCELLED_ON_EXCHANGE'] - logger.info(f'{trade.exit_side.capitalize()} order {reason} for {trade}.') - self.update_trade_state(trade, trade.open_order_id, order) - trade.open_order_id = None + trade.exit_reason = None + + self.update_trade_state(trade, trade.open_order_id, order) + + logger.info(f'{trade.exit_side.capitalize()} order {reason} for {trade}.') + trade.open_order_id = None + trade.close_rate = None + trade.close_rate_requested = None self._notify_exit_cancel( trade, From 8726a4645d7fc69bb2c9120bb49b039db60fb142 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 11 Mar 2023 15:15:32 +0100 Subject: [PATCH 5/6] Don't use deprecated Type construct --- freqtrade/misc.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/freqtrade/misc.py b/freqtrade/misc.py index 87cea54c0..0cd5c6ffd 100644 --- a/freqtrade/misc.py +++ b/freqtrade/misc.py @@ -6,8 +6,7 @@ import logging import re from datetime import datetime from pathlib import Path -from typing import Any, Dict, Iterator, List, Mapping, Optional, Union -from typing.io import IO +from typing import Any, Dict, Iterator, List, Mapping, Optional, TextIO, Union from urllib.parse import urlparse import orjson @@ -103,7 +102,7 @@ def file_dump_joblib(filename: Path, data: Any, log: bool = True) -> None: logger.debug(f'done joblib dump to "{filename}"') -def json_load(datafile: IO) -> Any: +def json_load(datafile: Union[gzip.GzipFile, TextIO]) -> Any: """ load data with rapidjson Use this to have a consistent experience, From b23841fbfedc970c1dc706f5ea057f1143babba4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 11 Mar 2023 17:35:30 +0100 Subject: [PATCH 6/6] Bump ccxt to 2.9.12 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index c972ae1d4..5977a6440 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ numpy==1.24.2 pandas==1.5.3 pandas-ta==0.3.14b -ccxt==2.9.4 +ccxt==2.9.12 cryptography==39.0.2 aiohttp==3.8.4 SQLAlchemy==2.0.5.post1