From c34d1742a648cde558daad40b70a0f2f90767122 Mon Sep 17 00:00:00 2001 From: hazeone <709547807@qq.com> Date: Thu, 30 Oct 2025 16:03:33 +0800 Subject: [PATCH 1/4] optimize the autotrading agent output --- .../agents/auto_trading_agent/agent.py | 22 +++++++--------- .../agents/auto_trading_agent/formatters.py | 25 ++++++++++--------- 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/python/valuecell/agents/auto_trading_agent/agent.py b/python/valuecell/agents/auto_trading_agent/agent.py index 79663dba1..f78d65699 100644 --- a/python/valuecell/agents/auto_trading_agent/agent.py +++ b/python/valuecell/agents/auto_trading_agent/agent.py @@ -548,35 +548,31 @@ def _get_instance_status_component_data( output = [] # Header - output.append(f"šŸ“Š **Trading Portfolio Status** - {instance_id}") - output.append("\n**Instance Configuration**") + output.append("šŸ“Š **Instance Configuration**\n") output.append(f"- Model: `{config.agent_model}`") output.append(f"- Symbols: {', '.join(config.crypto_symbols)}") output.append( - f"- Status: {'🟢 Active' if instance['active'] else 'šŸ”“ Stopped'}" + f"- Status: {'🟢 Active' if instance['active'] else 'šŸ”“ Stopped'}\n" ) # Portfolio Summary Section - output.append("\nšŸ’° **Portfolio Summary**") - output.append("\n**Overall Performance**") - output.append(f"- Initial Capital: `${config.initial_capital:,.2f}`") - output.append(f"- Current Value: `${portfolio_value:,.2f}`") + output.append("šŸ’° **Portfolio Summary**\n") + output.append("**Overall Performance**\n") + output.append(f"- Current Value: `${portfolio_value:,.2f}`\n") pnl_emoji = "🟢" if total_pnl >= 0 else "šŸ”“" pnl_sign = "+" if total_pnl >= 0 else "" output.append( - f"- Total P&L: {pnl_emoji} **{pnl_sign}${total_pnl:,.2f}** ({pnl_sign}{pnl_pct:.2f}%)" + f"- Total P&L: {pnl_emoji} **{pnl_sign}${total_pnl:,.2f}** ({pnl_sign}{pnl_pct:.2f}%)\n" ) - - output.append("\n**Cash Position**") - output.append(f"- Available Cash: `${available_cash:,.2f}`") + output.append(f"- Available Cash: `${available_cash:,.2f}`\n") # Current Positions Section - output.append(f"\nšŸ“ˆ **Current Positions ({len(executor.positions)})**") + output.append(f"šŸ“ˆ **Current Positions ({len(executor.positions)})**") if executor.positions: output.append( - "\n| Symbol | Type | Quantity | Avg Price | Current Price | Position Value | Unrealized P&L |" + "\n| Symbol | Type | Quantity | Avg | Current | Position | P&L |" ) output.append( "|--------|------|----------|-----------|---------------|----------------|----------------|" diff --git a/python/valuecell/agents/auto_trading_agent/formatters.py b/python/valuecell/agents/auto_trading_agent/formatters.py index 9e2e9a8eb..79a6e89f8 100644 --- a/python/valuecell/agents/auto_trading_agent/formatters.py +++ b/python/valuecell/agents/auto_trading_agent/formatters.py @@ -35,11 +35,11 @@ def format_trade_notification( if action == "opened": message = ( - f"{agent_name} opened a {trade_type} position on {symbol}!\n" - f"{timestamp.strftime('%m/%d, %I:%M %p')}\n" - f"Price: ${trade_details['entry_price']:,.2f}\n" - f"Quantity: {trade_details['quantity']:.4f}\n" - f"Notional: ${trade_details['notional']:,.2f}" + f"**{agent_name}** opened a **{trade_type}** position on **{symbol}**!\n\n" + f"šŸ“… {timestamp.strftime('%m/%d, %I:%M %p')}\n" + f"**Price:** `${trade_details['entry_price']:,.2f}`\n" + f"**Quantity:** `{trade_details['quantity']:.4f}`\n" + f"**Notional:** `${trade_details['notional']:,.2f}`" ) else: # closed hours = int(trade_details["holding_time"].total_seconds() // 3600) @@ -48,15 +48,16 @@ def format_trade_notification( ) pnl = trade_details["pnl"] pnl_sign = "+" if pnl >= 0 else "" + pnl_emoji = "🟢" if pnl >= 0 else "šŸ”“" message = ( - f"{agent_name} completed a {trade_type} trade on {symbol}!\n" - f"{timestamp.strftime('%m/%d, %I:%M %p')}\n" - f"Price: ${trade_details['entry_price']:,.2f} → ${trade_details['exit_price']:,.2f}\n" - f"Quantity: {trade_details['quantity']:.4f}\n" - f"Notional: ${trade_details['entry_notional']:,.2f} → ${trade_details['exit_notional']:,.2f}\n" - f"Holding time: {hours}H {minutes}M\n" - f"Net P&L: {pnl_sign}${pnl:,.2f}" + f"**{agent_name}** completed a **{trade_type}** trade on **{symbol}**!\n\n" + f"šŸ“… {timestamp.strftime('%m/%d, %I:%M %p')}\n" + f"**Price:** `${trade_details['entry_price']:,.2f}` → `${trade_details['exit_price']:,.2f}`\n" + f"**Quantity:** `{trade_details['quantity']:.4f}`\n" + f"**Notional:** `${trade_details['entry_notional']:,.2f}` → `${trade_details['exit_notional']:,.2f}`\n" + f"**Holding time:** `{hours}H {minutes}M`\n" + f"**Net P&L:** {pnl_emoji} **{pnl_sign}${pnl:,.2f}**" ) return message From 50d985338f62dacb835d8776dc954f3a22a446f7 Mon Sep 17 00:00:00 2001 From: hazeone <709547807@qq.com> Date: Thu, 30 Oct 2025 18:35:29 +0800 Subject: [PATCH 2/4] reformat auto trading agent output --- .../agents/auto_trading_agent/agent.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/python/valuecell/agents/auto_trading_agent/agent.py b/python/valuecell/agents/auto_trading_agent/agent.py index f78d65699..8da76e090 100644 --- a/python/valuecell/agents/auto_trading_agent/agent.py +++ b/python/valuecell/agents/auto_trading_agent/agent.py @@ -572,10 +572,10 @@ def _get_instance_status_component_data( if executor.positions: output.append( - "\n| Symbol | Type | Quantity | Avg | Current | Position | P&L |" + "\n| Symbol | Type | **Position**/Quantity | **Current**/Avg | **P&L** |" ) output.append( - "|--------|------|----------|-----------|---------------|----------------|----------------|" + "|--------|------|---------|--------|--------|" ) for symbol, pos in executor.positions.items(): @@ -599,15 +599,14 @@ def _get_instance_status_component_data( ) position_value = pos.notional + unrealized_pnl - # Format row - pnl_emoji = "🟢" if unrealized_pnl >= 0 else "šŸ”“" + # Format row with merged columns pnl_sign = "+" if unrealized_pnl >= 0 else "" output.append( f"| **{symbol}** | {pos.trade_type.value.upper()} | " - f"{abs(pos.quantity):.4f} | ${pos.entry_price:,.2f} | " - f"${current_price:,.2f} | ${position_value:,.2f} | " - f"{pnl_emoji} {pnl_sign}${unrealized_pnl:,.2f} |" + f"**${position_value:,.2f}**
{abs(pos.quantity):.4f} | " + f"**${current_price:,.2f}**
${pos.entry_price:,.2f} | " + f"**{pnl_sign}${unrealized_pnl:,.2f}** |" ) except Exception as e: @@ -615,8 +614,9 @@ def _get_instance_status_component_data( # Fallback display with entry price only output.append( f"| **{symbol}** | {pos.trade_type.value.upper()} | " - f"{abs(pos.quantity):.4f} | ${pos.entry_price:,.2f} | " - f"N/A | ${pos.notional:,.2f} | N/A |" + f"**${pos.notional:,.2f}**
{abs(pos.quantity):.4f} | " + f"**${pos.entry_price:,.2f}**
${pos.entry_price:,.2f} | " + f"**N/A** |" ) else: output.append("\n*No open positions*") From 57d9410d094579123962ee3ae71a39cb9958ddc6 Mon Sep 17 00:00:00 2001 From: hazeone <709547807@qq.com> Date: Thu, 30 Oct 2025 18:52:58 +0800 Subject: [PATCH 3/4] format as markdown --- .../valuecell/agents/auto_trading_agent/agent.py | 6 ++---- .../agents/auto_trading_agent/formatters.py | 16 ++++++++-------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/python/valuecell/agents/auto_trading_agent/agent.py b/python/valuecell/agents/auto_trading_agent/agent.py index 8da76e090..aa46892e4 100644 --- a/python/valuecell/agents/auto_trading_agent/agent.py +++ b/python/valuecell/agents/auto_trading_agent/agent.py @@ -267,7 +267,7 @@ async def _process_trading_instance( ) trade_message = FilteredCardPushNotificationComponentData( title=f"{config.agent_model} Trade", - data=f"šŸ’° **Trade Executed:**\n{trade_message_text}\n", + data=f"šŸ’° **Trade Executed**\n\n{trade_message_text}\n", filters=[config.agent_model], table_title="Trade Detail", create_time=datetime.now(timezone.utc).strftime( @@ -574,9 +574,7 @@ def _get_instance_status_component_data( output.append( "\n| Symbol | Type | **Position**/Quantity | **Current**/Avg | **P&L** |" ) - output.append( - "|--------|------|---------|--------|--------|" - ) + output.append("|--------|------|---------|--------|--------|") for symbol, pos in executor.positions.items(): try: diff --git a/python/valuecell/agents/auto_trading_agent/formatters.py b/python/valuecell/agents/auto_trading_agent/formatters.py index 79a6e89f8..6663b7782 100644 --- a/python/valuecell/agents/auto_trading_agent/formatters.py +++ b/python/valuecell/agents/auto_trading_agent/formatters.py @@ -36,9 +36,9 @@ def format_trade_notification( if action == "opened": message = ( f"**{agent_name}** opened a **{trade_type}** position on **{symbol}**!\n\n" - f"šŸ“… {timestamp.strftime('%m/%d, %I:%M %p')}\n" - f"**Price:** `${trade_details['entry_price']:,.2f}`\n" - f"**Quantity:** `{trade_details['quantity']:.4f}`\n" + f"šŸ“… {timestamp.strftime('%m/%d, %I:%M %p')}\n\n" + f"**Price:** `${trade_details['entry_price']:,.2f}`\n\n" + f"**Quantity:** `{trade_details['quantity']:.4f}`\n\n" f"**Notional:** `${trade_details['notional']:,.2f}`" ) else: # closed @@ -52,11 +52,11 @@ def format_trade_notification( message = ( f"**{agent_name}** completed a **{trade_type}** trade on **{symbol}**!\n\n" - f"šŸ“… {timestamp.strftime('%m/%d, %I:%M %p')}\n" - f"**Price:** `${trade_details['entry_price']:,.2f}` → `${trade_details['exit_price']:,.2f}`\n" - f"**Quantity:** `{trade_details['quantity']:.4f}`\n" - f"**Notional:** `${trade_details['entry_notional']:,.2f}` → `${trade_details['exit_notional']:,.2f}`\n" - f"**Holding time:** `{hours}H {minutes}M`\n" + f"šŸ“… {timestamp.strftime('%m/%d, %I:%M %p')}\n\n" + f"**Price:** `${trade_details['entry_price']:,.2f}` → `${trade_details['exit_price']:,.2f}`\n\n" + f"**Quantity:** `{trade_details['quantity']:.4f}`\n\n" + f"**Notional:** `${trade_details['entry_notional']:,.2f}` → `${trade_details['exit_notional']:,.2f}`\n\n" + f"**Holding time:** `{hours}H {minutes}M`\n\n" f"**Net P&L:** {pnl_emoji} **{pnl_sign}${pnl:,.2f}**" ) From 6824fa82925c910ce99b74f88b821e5c9bd63edb Mon Sep 17 00:00:00 2001 From: hazeone <709547807@qq.com> Date: Thu, 30 Oct 2025 19:14:28 +0800 Subject: [PATCH 4/4] add time formatter --- .../agents/auto_trading_agent/formatters.py | 79 ++++++++++++++++++- 1 file changed, 75 insertions(+), 4 deletions(-) diff --git a/python/valuecell/agents/auto_trading_agent/formatters.py b/python/valuecell/agents/auto_trading_agent/formatters.py index 6663b7782..12cbed843 100644 --- a/python/valuecell/agents/auto_trading_agent/formatters.py +++ b/python/valuecell/agents/auto_trading_agent/formatters.py @@ -5,6 +5,7 @@ from datetime import datetime, timezone from typing import Any, Dict, Optional +from ...utils.i18n_utils import convert_timezone, get_current_timezone from .models import Position, TechnicalIndicators, TradeAction, TradeType logger = logging.getLogger(__name__) @@ -13,6 +14,63 @@ class MessageFormatter: """Formats various messages and notifications""" + @staticmethod + def _convert_and_format_timestamp( + dt: datetime, format_str: str = "%m/%d, %I:%M %p", include_tz: bool = False + ) -> str: + """ + Convert timestamp to user's timezone and format it. + + Args: + dt: DateTime to convert and format + format_str: Format string for strftime + include_tz: Whether to include timezone abbreviation + + Returns: + Formatted timestamp string + """ + try: + # Get user's configured timezone + user_tz = get_current_timezone() + + # Convert from UTC to user's timezone + # Assume input is UTC if no timezone info + if dt.tzinfo is None: + dt = datetime( + dt.year, + dt.month, + dt.day, + dt.hour, + dt.minute, + dt.second, + tzinfo=timezone.utc, + ) + + converted_dt = convert_timezone(dt, "UTC", user_tz) + + # Format the datetime + formatted = converted_dt.strftime(format_str) + + # Optionally append timezone info + if include_tz: + formatted += f" ({user_tz})" + + return formatted + except Exception as e: + logger.warning(f"Failed to convert timezone: {e}, using UTC") + # Fallback to UTC format + if dt.tzinfo is None: + dt = datetime( + dt.year, + dt.month, + dt.day, + dt.hour, + dt.minute, + dt.second, + tzinfo=timezone.utc, + ) + return dt.strftime(format_str) + @staticmethod def format_trade_notification( trade_details: Dict[str, Any], agent_name: str = "AutoTrading" @@ -33,10 +91,13 @@ def format_trade_notification( trade_type = trade_details["trade_type"] timestamp = trade_details["timestamp"] + # Convert timestamp to user's timezone + formatted_time = MessageFormatter._convert_and_format_timestamp(timestamp) + if action == "opened": message = ( f"**{agent_name}** opened a **{trade_type}** position on **{symbol}**!\n\n" - f"šŸ“… {timestamp.strftime('%m/%d, %I:%M %p')}\n\n" + f"šŸ“… {formatted_time}\n\n" f"**Price:** `${trade_details['entry_price']:,.2f}`\n\n" f"**Quantity:** `{trade_details['quantity']:.4f}`\n\n" f"**Notional:** `${trade_details['notional']:,.2f}`" @@ -52,7 +113,7 @@ def format_trade_notification( message = ( f"**{agent_name}** completed a **{trade_type}** trade on **{symbol}**!\n\n" - f"šŸ“… {timestamp.strftime('%m/%d, %I:%M %p')}\n\n" + f"šŸ“… {formatted_time}\n\n" f"**Price:** `${trade_details['entry_price']:,.2f}` → `${trade_details['exit_price']:,.2f}`\n\n" f"**Quantity:** `{trade_details['quantity']:.4f}`\n\n" f"**Notional:** `${trade_details['entry_notional']:,.2f}` → `${trade_details['exit_notional']:,.2f}`\n\n" @@ -107,9 +168,14 @@ def format_portfolio_notification( "data": {"Portfolio": portfolio_value}, } + # Convert timestamp to user's timezone for display + formatted_time = MessageFormatter._convert_and_format_timestamp( + timestamp, format_str="%m/%d, %I:%M %p", include_tz=True + ) + display_message = ( f"šŸ’° Portfolio Update\n" - f"Time: {timestamp.strftime('%m/%d, %I:%M %p UTC')}\n" + f"Time: {formatted_time}\n" f"Total Value: ${portfolio_value:,.2f}\n" f"Open Positions: {positions_count}\n" f"Available Capital: ${current_capital:,.2f}" @@ -147,6 +213,11 @@ def format_market_analysis_notification( try: timestamp = datetime.now(timezone.utc) + # Convert timestamp to user's timezone for display + formatted_time = MessageFormatter._convert_and_format_timestamp( + timestamp, format_str="%m/%d, %I:%M %p", include_tz=True + ) + # Format action with emoji action_emoji = { TradeAction.BUY: "🟢", @@ -156,7 +227,7 @@ def format_market_analysis_notification( message = ( f"šŸ“Š **Market Analysis - {symbol}**\n" - f"Time: {timestamp.strftime('%m/%d, %I:%M %p UTC')}\n\n" + f"Time: {formatted_time}\n\n" f"**Current Price:** ${indicators.close_price:,.2f}\n" f"**Decision:** {action_emoji.get(action, '')} {action.value.upper()}" )