Merge pull request #6908 from eSeR1805/feature_keyval_storage

Persistent storage of user-custom information
This commit is contained in:
Matthias
2024-03-08 07:00:17 +01:00
committed by GitHub
13 changed files with 623 additions and 22 deletions

View File

@@ -999,6 +999,32 @@ class RPC:
'cancel_order_count': c_count,
}
def _rpc_list_custom_data(self, trade_id: int, key: Optional[str]) -> List[Dict[str, Any]]:
# Query for trade
trade = Trade.get_trades(trade_filter=[Trade.id == trade_id]).first()
if trade is None:
return []
# Query custom_data
custom_data = []
if key:
data = trade.get_custom_data(key=key)
if data:
custom_data = [data]
else:
custom_data = trade.get_all_custom_data()
return [
{
'id': data_entry.id,
'ft_trade_id': data_entry.ft_trade_id,
'cd_key': data_entry.cd_key,
'cd_type': data_entry.cd_type,
'cd_value': data_entry.cd_value,
'created_at': data_entry.created_at,
'updated_at': data_entry.updated_at
}
for data_entry in custom_data
]
def _rpc_performance(self) -> List[Dict[str, Any]]:
"""
Handler for performance.

View File

@@ -33,7 +33,7 @@ from freqtrade.misc import chunks, plural
from freqtrade.persistence import Trade
from freqtrade.rpc import RPC, RPCException, RPCHandler
from freqtrade.rpc.rpc_types import RPCEntryMsg, RPCExitMsg, RPCOrderMsg, RPCSendMsg
from freqtrade.util import dt_humanize, fmt_coin, round_value
from freqtrade.util import dt_humanize, fmt_coin, format_date, round_value
MAX_MESSAGE_LENGTH = MessageLimit.MAX_TEXT_LENGTH
@@ -243,6 +243,7 @@ class Telegram(RPCHandler):
CommandHandler('version', self._version),
CommandHandler('marketdir', self._changemarketdir),
CommandHandler('order', self._order),
CommandHandler('list_custom_data', self._list_custom_data),
]
callbacks = [
CallbackQueryHandler(self._status_table, pattern='update_status_table'),
@@ -1667,6 +1668,8 @@ class Telegram(RPCHandler):
"*/marketdir [long | short | even | none]:* `Updates the user managed variable "
"that represents the current market direction. If no direction is provided `"
"`the currently set market direction will be output.` \n"
"*/list_custom_data <trade_id> <key>:* `List custom_data for Trade ID & Key combo.`\n"
"`If no Key is supplied it will list all key-value pairs found for that Trade ID.`"
"_Statistics_\n"
"------------\n"
@@ -1689,7 +1692,7 @@ class Telegram(RPCHandler):
"*/stats:* `Shows Wins / losses by Sell reason as well as "
"Avg. holding durations for buys and sells.`\n"
"*/help:* `This help message`\n"
"*/version:* `Show version`"
"*/version:* `Show version`\n"
)
await self._send_msg(message, parse_mode=ParseMode.MARKDOWN)
@@ -1766,6 +1769,53 @@ class Telegram(RPCHandler):
f"*Current state:* `{val['state']}`"
)
@authorized_only
async def _list_custom_data(self, update: Update, context: CallbackContext) -> None:
"""
Handler for /list_custom_data <id> <key>.
List custom_data for specified trade (and key if supplied).
:param bot: telegram bot
:param update: message update
:return: None
"""
try:
if not context.args or len(context.args) == 0:
raise RPCException("Trade-id not set.")
trade_id = int(context.args[0])
key = None if len(context.args) < 2 else str(context.args[1])
results = self._rpc._rpc_list_custom_data(trade_id, key)
messages = []
if len(results) > 0:
messages.append(
'Found custom-data entr' + ('ies: ' if len(results) > 1 else 'y: ')
)
for result in results:
lines = [
f"*Key:* `{result['cd_key']}`",
f"*ID:* `{result['id']}`",
f"*Trade ID:* `{result['ft_trade_id']}`",
f"*Type:* `{result['cd_type']}`",
f"*Value:* `{result['cd_value']}`",
f"*Create Date:* `{format_date(result['created_at'])}`",
f"*Update Date:* `{format_date(result['updated_at'])}`"
]
# Filter empty lines using list-comprehension
messages.append("\n".join([line for line in lines if line]))
for msg in messages:
if len(msg) > MAX_MESSAGE_LENGTH:
msg = "Message dropped because length exceeds "
msg += f"maximum allowed characters: {MAX_MESSAGE_LENGTH}"
logger.warning(msg)
await self._send_msg(msg)
else:
message = f"Didn't find any custom-data entries for Trade ID: `{trade_id}`"
message += f" and Key: `{key}`." if key is not None else ""
await self._send_msg(message)
except RPCException as e:
await self._send_msg(str(e))
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: