Merge branch 'freqtrade:develop' into strategy_utils
This commit is contained in:
commit
d186f8f1e1
@ -12,6 +12,9 @@ This page provides you some basic concepts on how Freqtrade works and operates.
|
|||||||
* **Indicators**: Technical indicators (SMA, EMA, RSI, ...).
|
* **Indicators**: Technical indicators (SMA, EMA, RSI, ...).
|
||||||
* **Limit order**: Limit orders which execute at the defined limit price or better.
|
* **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.
|
* **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
|
## Fee handling
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ from enum import Enum
|
|||||||
class RPCMessageType(str, Enum):
|
class RPCMessageType(str, Enum):
|
||||||
STATUS = 'status'
|
STATUS = 'status'
|
||||||
WARNING = 'warning'
|
WARNING = 'warning'
|
||||||
|
EXCEPTION = 'exception'
|
||||||
STARTUP = 'startup'
|
STARTUP = 'startup'
|
||||||
|
|
||||||
ENTRY = 'entry'
|
ENTRY = 'entry'
|
||||||
|
@ -133,13 +133,13 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
# Initialize protections AFTER bot start - otherwise parameters are not loaded.
|
# Initialize protections AFTER bot start - otherwise parameters are not loaded.
|
||||||
self.protections = ProtectionManager(self.config, self.strategy.protections)
|
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
|
Public method for users of this class (worker, etc.) to send notifications
|
||||||
via RPC about changes in the bot status.
|
via RPC about changes in the bot status.
|
||||||
"""
|
"""
|
||||||
self.rpc.send_msg({
|
self.rpc.send_msg({
|
||||||
'type': RPCMessageType.STATUS,
|
'type': msg_type,
|
||||||
'status': msg
|
'status': msg
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -1460,34 +1460,32 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
co = self.exchange.cancel_order_with_result(order['id'], trade.pair,
|
order = self.exchange.cancel_order_with_result(order['id'], trade.pair,
|
||||||
trade.amount)
|
trade.amount)
|
||||||
except InvalidOrderException:
|
except InvalidOrderException:
|
||||||
logger.exception(
|
logger.exception(
|
||||||
f"Could not cancel {trade.exit_side} order {trade.open_order_id}")
|
f"Could not cancel {trade.exit_side} order {trade.open_order_id}")
|
||||||
return False
|
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
|
# Set exit_reason for fill message
|
||||||
exit_reason_prev = trade.exit_reason
|
exit_reason_prev = trade.exit_reason
|
||||||
trade.exit_reason = trade.exit_reason + f", {reason}" if trade.exit_reason else 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.
|
# 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.exit_reason = None
|
||||||
trade.open_order_id = None
|
|
||||||
else:
|
else:
|
||||||
trade.exit_reason = exit_reason_prev
|
trade.exit_reason = exit_reason_prev
|
||||||
|
|
||||||
logger.info(f'{trade.exit_side.capitalize()} order {reason} for {trade}.')
|
|
||||||
cancelled = True
|
cancelled = True
|
||||||
else:
|
else:
|
||||||
reason = constants.CANCEL_REASON['CANCELLED_ON_EXCHANGE']
|
reason = constants.CANCEL_REASON['CANCELLED_ON_EXCHANGE']
|
||||||
logger.info(f'{trade.exit_side.capitalize()} order {reason} for {trade}.')
|
trade.exit_reason = None
|
||||||
self.update_trade_state(trade, trade.open_order_id, order)
|
|
||||||
trade.open_order_id = 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(
|
self._notify_exit_cancel(
|
||||||
trade,
|
trade,
|
||||||
|
@ -6,8 +6,7 @@ import logging
|
|||||||
import re
|
import re
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, Iterator, List, Mapping, Optional, Union
|
from typing import Any, Dict, Iterator, List, Mapping, Optional, TextIO, Union
|
||||||
from typing.io import IO
|
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
import orjson
|
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}"')
|
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
|
load data with rapidjson
|
||||||
Use this to have a consistent experience,
|
Use this to have a consistent experience,
|
||||||
|
@ -414,6 +414,9 @@ class Telegram(RPCHandler):
|
|||||||
|
|
||||||
elif msg_type == RPCMessageType.WARNING:
|
elif msg_type == RPCMessageType.WARNING:
|
||||||
message = f"\N{WARNING SIGN} *Warning:* `{msg['status']}`"
|
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:
|
elif msg_type == RPCMessageType.STARTUP:
|
||||||
message = f"{msg['status']}"
|
message = f"{msg['status']}"
|
||||||
|
@ -58,6 +58,7 @@ class Webhook(RPCHandler):
|
|||||||
valuedict = whconfig.get('webhookexitcancel')
|
valuedict = whconfig.get('webhookexitcancel')
|
||||||
elif msg['type'] in (RPCMessageType.STATUS,
|
elif msg['type'] in (RPCMessageType.STATUS,
|
||||||
RPCMessageType.STARTUP,
|
RPCMessageType.STARTUP,
|
||||||
|
RPCMessageType.EXCEPTION,
|
||||||
RPCMessageType.WARNING):
|
RPCMessageType.WARNING):
|
||||||
valuedict = whconfig.get('webhookstatus')
|
valuedict = whconfig.get('webhookstatus')
|
||||||
elif msg['type'].value in whconfig:
|
elif msg['type'].value in whconfig:
|
||||||
|
@ -12,7 +12,7 @@ import sdnotify
|
|||||||
from freqtrade import __version__
|
from freqtrade import __version__
|
||||||
from freqtrade.configuration import Configuration
|
from freqtrade.configuration import Configuration
|
||||||
from freqtrade.constants import PROCESS_THROTTLE_SECS, RETRY_TIMEOUT, Config
|
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.exceptions import OperationalException, TemporaryError
|
||||||
from freqtrade.exchange import timeframe_to_next_date
|
from freqtrade.exchange import timeframe_to_next_date
|
||||||
from freqtrade.freqtradebot import FreqtradeBot
|
from freqtrade.freqtradebot import FreqtradeBot
|
||||||
@ -185,7 +185,10 @@ class Worker:
|
|||||||
tb = traceback.format_exc()
|
tb = traceback.format_exc()
|
||||||
hint = 'Issue `/start` if you think it is safe to restart.'
|
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 ...')
|
logger.exception('OperationalException. Stopping trader ...')
|
||||||
self.freqtrade.state = State.STOPPED
|
self.freqtrade.state = State.STOPPED
|
||||||
|
@ -2,7 +2,7 @@ numpy==1.24.2
|
|||||||
pandas==1.5.3
|
pandas==1.5.3
|
||||||
pandas-ta==0.3.14b
|
pandas-ta==0.3.14b
|
||||||
|
|
||||||
ccxt==2.9.4
|
ccxt==2.9.12
|
||||||
cryptography==39.0.2
|
cryptography==39.0.2
|
||||||
aiohttp==3.8.4
|
aiohttp==3.8.4
|
||||||
SQLAlchemy==2.0.5.post1
|
SQLAlchemy==2.0.5.post1
|
||||||
|
Loading…
Reference in New Issue
Block a user