|
@@ -10,7 +10,7 @@ from telegram import Update
|
|
|
from telegram.ext import ContextTypes
|
|
|
|
|
|
from src.config.config import Config
|
|
|
-from src.utils.price_formatter import format_price_with_symbol, get_formatter
|
|
|
+from src.utils.token_display_formatter import format_price_with_symbol, get_formatter
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
@@ -204,14 +204,18 @@ class InfoCommands:
|
|
|
# --- Format Output String ---
|
|
|
# Get token info for formatting prices
|
|
|
# Assuming get_formatter() is available and provides necessary precision
|
|
|
- # For direct use, we can fetch token_info if get_formatter() isn't what we expect
|
|
|
- token_info = self.trading_engine.get_token_info(base_asset) # Ensure this method exists and works
|
|
|
- base_precision = token_info.get('precision', {}).get('amount', 6) if token_info and token_info.get('precision') else 6 # Default amount precision
|
|
|
-
|
|
|
formatter = get_formatter() # Keep using if it wraps these precisions
|
|
|
+
|
|
|
+ # Get price precisions
|
|
|
entry_price_str = formatter.format_price_with_symbol(entry_price, base_asset)
|
|
|
mark_price_str = formatter.format_price_with_symbol(mark_price, base_asset)
|
|
|
-
|
|
|
+
|
|
|
+ # Get amount precision for position size
|
|
|
+ # base_precision = int(token_info.get('precision', {}).get('amount', 6) if token_info and token_info.get('precision') else 6) # Old way
|
|
|
+ # No longer need to fetch token_info separately for base_precision
|
|
|
+ # The formatter now handles amount precision directly.
|
|
|
+ size_str = formatter.format_amount(abs_current_amount, base_asset)
|
|
|
+
|
|
|
type_indicator = ""
|
|
|
# Determine type_indicator based on trade_lifecycle_id or trade_type
|
|
|
if position_trade.get('trade_lifecycle_id'): # Primary indicator for bot managed
|
|
@@ -220,7 +224,7 @@ class InfoCommands:
|
|
|
type_indicator = " 🔄"
|
|
|
|
|
|
positions_text += f"{pos_emoji} <b>{base_asset} ({direction_text}){type_indicator}</b>\n"
|
|
|
- positions_text += f" 📏 Size: {abs_current_amount:.{base_precision}f} {base_asset}\n"
|
|
|
+ positions_text += f" 📏 Size: {size_str} {base_asset}\n" # Use the formatted size_str
|
|
|
positions_text += f" 💰 Entry: {entry_price_str}\n"
|
|
|
|
|
|
if mark_price != 0 and abs(mark_price - entry_price) > 1e-9: # Only show mark if significantly different
|
|
@@ -713,6 +717,7 @@ class InfoCommands:
|
|
|
return
|
|
|
|
|
|
token_stats = stats.get_token_detailed_stats(token)
|
|
|
+ formatter = get_formatter() # Get the formatter instance
|
|
|
|
|
|
# Check if token has any data
|
|
|
if token_stats.get('total_trades', 0) == 0:
|
|
@@ -729,6 +734,7 @@ class InfoCommands:
|
|
|
|
|
|
# Check if there's a message (no completed trades)
|
|
|
if 'message' in token_stats and token_stats.get('completed_trades', 0) == 0:
|
|
|
+ total_volume_str = formatter.format_price_with_symbol(token_stats.get('total_volume', 0), quote_asset=Config.QUOTE_CURRENCY) # Assuming total volume is in quote currency
|
|
|
await context.bot.send_message(chat_id=chat_id, text=
|
|
|
f"📊 <b>{token} Performance</b>\n\n"
|
|
|
f"{token_stats['message']}\n\n"
|
|
@@ -736,7 +742,7 @@ class InfoCommands:
|
|
|
f"• Total Trades: {token_stats['total_trades']}\n"
|
|
|
f"• Buy Orders: {token_stats.get('buy_trades', 0)}\n"
|
|
|
f"• Sell Orders: {token_stats.get('sell_trades', 0)}\n"
|
|
|
- f"• Volume: ${token_stats.get('total_volume', 0):,.2f}\n\n"
|
|
|
+ f"• Volume: {total_volume_str}\n\n"
|
|
|
f"💡 Complete some trades to see P&L statistics!\n"
|
|
|
f"🔄 Use <code>/performance</code> to see all token rankings.",
|
|
|
parse_mode='HTML'
|
|
@@ -746,13 +752,22 @@ class InfoCommands:
|
|
|
# Detailed stats display
|
|
|
pnl_emoji = "🟢" if token_stats['total_pnl'] >= 0 else "🔴"
|
|
|
|
|
|
+ total_pnl_str = formatter.format_price_with_symbol(token_stats['total_pnl'], quote_asset=Config.QUOTE_CURRENCY)
|
|
|
+ completed_volume_str = formatter.format_price_with_symbol(token_stats['completed_volume'], quote_asset=Config.QUOTE_CURRENCY)
|
|
|
+ expectancy_str = formatter.format_price_with_symbol(token_stats['expectancy'], quote_asset=Config.QUOTE_CURRENCY)
|
|
|
+ largest_win_str = formatter.format_price_with_symbol(token_stats['largest_win'], quote_asset=Config.QUOTE_CURRENCY)
|
|
|
+ largest_loss_str = formatter.format_price_with_symbol(token_stats['largest_loss'], quote_asset=Config.QUOTE_CURRENCY) # Assuming loss is positive number
|
|
|
+ avg_win_str = formatter.format_price_with_symbol(token_stats['avg_win'], quote_asset=Config.QUOTE_CURRENCY)
|
|
|
+ avg_loss_str = formatter.format_price_with_symbol(token_stats['avg_loss'], quote_asset=Config.QUOTE_CURRENCY) # Assuming loss is positive number
|
|
|
+
|
|
|
+
|
|
|
performance_text = f"""
|
|
|
📊 <b>{token} Detailed Performance</b>
|
|
|
|
|
|
💰 <b>P&L Summary:</b>
|
|
|
-• {pnl_emoji} Total P&L: ${token_stats['total_pnl']:,.2f} ({token_stats['pnl_percentage']:+.2f}%)
|
|
|
-• 💵 Total Volume: ${token_stats['completed_volume']:,.2f}
|
|
|
-• 📈 Expectancy: ${token_stats['expectancy']:,.2f}
|
|
|
+• {pnl_emoji} Total P&L: {total_pnl_str} ({token_stats['pnl_percentage']:+.2f}%)
|
|
|
+• 💵 Total Volume: {completed_volume_str}
|
|
|
+• 📈 Expectancy: {expectancy_str}
|
|
|
|
|
|
📊 <b>Trading Activity:</b>
|
|
|
• Total Trades: {token_stats['total_trades']}
|
|
@@ -766,10 +781,10 @@ class InfoCommands:
|
|
|
• Wins: {token_stats['total_wins']} | Losses: {token_stats['total_losses']}
|
|
|
|
|
|
💡 <b>Best/Worst:</b>
|
|
|
-• Largest Win: ${token_stats['largest_win']:,.2f}
|
|
|
-• Largest Loss: ${token_stats['largest_loss']:,.2f}
|
|
|
-• Avg Win: ${token_stats['avg_win']:,.2f}
|
|
|
-• Avg Loss: ${token_stats['avg_loss']:,.2f}
|
|
|
+• Largest Win: {largest_win_str}
|
|
|
+• Largest Loss: {largest_loss_str}
|
|
|
+• Avg Win: {avg_win_str}
|
|
|
+• Avg Loss: {avg_loss_str}
|
|
|
"""
|
|
|
|
|
|
# Add recent trades if available
|
|
@@ -778,9 +793,20 @@ class InfoCommands:
|
|
|
for trade in token_stats['recent_trades'][-3:]: # Last 3 trades
|
|
|
trade_time = datetime.fromisoformat(trade['timestamp']).strftime('%m/%d %H:%M')
|
|
|
side_emoji = "🟢" if trade['side'] == 'buy' else "🔴"
|
|
|
- pnl_display = f" | P&L: ${trade.get('pnl', 0):.2f}" if trade.get('pnl', 0) != 0 else ""
|
|
|
|
|
|
- performance_text += f"• {side_emoji} {trade['side'].upper()} ${trade['value']:,.0f} @ {trade_time}{pnl_display}\n"
|
|
|
+ # trade_symbol is required for format_price and format_amount
|
|
|
+ trade_symbol = trade.get('symbol', token) # Fallback to token if symbol not in trade dict
|
|
|
+ trade_base_asset = trade_symbol.split('/')[0] if '/' in trade_symbol else trade_symbol
|
|
|
+
|
|
|
+ # Formatting trade value. Assuming 'value' is in quote currency.
|
|
|
+ trade_value_str = formatter.format_price_with_symbol(trade.get('value', 0), quote_asset=Config.QUOTE_CURRENCY)
|
|
|
+
|
|
|
+ pnl_display_str = ""
|
|
|
+ if trade.get('pnl', 0) != 0:
|
|
|
+ trade_pnl_str = formatter.format_price_with_symbol(trade.get('pnl', 0), quote_asset=Config.QUOTE_CURRENCY)
|
|
|
+ pnl_display_str = f" | P&L: {trade_pnl_str}"
|
|
|
+
|
|
|
+ performance_text += f"• {side_emoji} {trade['side'].upper()} {trade_value_str} @ {trade_time}{pnl_display_str}\n"
|
|
|
|
|
|
performance_text += f"\n🔄 Use <code>/performance</code> to see all token rankings"
|
|
|
|
|
@@ -800,49 +826,50 @@ class InfoCommands:
|
|
|
return
|
|
|
|
|
|
daily_stats = stats.get_daily_stats(10)
|
|
|
+ formatter = get_formatter() # Get formatter
|
|
|
|
|
|
if not daily_stats:
|
|
|
await context.bot.send_message(chat_id=chat_id, text=
|
|
|
- "📅 <b>Daily Performance</b>\n\n"
|
|
|
- "📭 No daily performance data available yet.\n\n"
|
|
|
- "💡 Daily stats are calculated from completed trades.\n"
|
|
|
+ "📅 <b>Daily Performance</b>\\n\\n"
|
|
|
+ "📭 No daily performance data available yet.\\n\\n"
|
|
|
+ "💡 Daily stats are calculated from completed trades.\\n"
|
|
|
"Start trading to see daily performance!",
|
|
|
parse_mode='HTML'
|
|
|
)
|
|
|
return
|
|
|
|
|
|
- daily_text = "📅 <b>Daily Performance (Last 10 Days)</b>\n\n"
|
|
|
+ daily_text = "📅 <b>Daily Performance (Last 10 Days)</b>\\n\\n"
|
|
|
|
|
|
- total_pnl = 0
|
|
|
- total_trades = 0
|
|
|
- trading_days = 0
|
|
|
+ total_pnl_all_days = 0 # Renamed to avoid conflict
|
|
|
+ total_trades_all_days = 0 # Renamed
|
|
|
+ trading_days_count = 0 # Renamed
|
|
|
|
|
|
- for day_stats in daily_stats:
|
|
|
- if day_stats['has_trades']:
|
|
|
- # Day with completed trades
|
|
|
- pnl_emoji = "🟢" if day_stats['pnl'] >= 0 else "🔴"
|
|
|
- daily_text += f"📊 <b>{day_stats['date_formatted']}</b>\n"
|
|
|
- daily_text += f" {pnl_emoji} P&L: ${day_stats['pnl']:,.2f} ({day_stats['pnl_pct']:+.1f}%)\n"
|
|
|
- daily_text += f" 🔄 Trades: {day_stats['trades']}\n\n"
|
|
|
+ for day_stats_item in daily_stats: # Renamed to avoid conflict
|
|
|
+ if day_stats_item['has_trades']:
|
|
|
+ pnl_emoji = "🟢" if day_stats_item['pnl'] >= 0 else "🔴"
|
|
|
+ pnl_str = formatter.format_price_with_symbol(day_stats_item['pnl'], quote_asset=Config.QUOTE_CURRENCY)
|
|
|
+ daily_text += f"📊 <b>{day_stats_item['date_formatted']}</b>\\n"
|
|
|
+ daily_text += f" {pnl_emoji} P&L: {pnl_str} ({day_stats_item['pnl_pct']:+.1f}%)\\n"
|
|
|
+ daily_text += f" 🔄 Trades: {day_stats_item['trades']}\\n\\n"
|
|
|
|
|
|
- total_pnl += day_stats['pnl']
|
|
|
- total_trades += day_stats['trades']
|
|
|
- trading_days += 1
|
|
|
+ total_pnl_all_days += day_stats_item['pnl']
|
|
|
+ total_trades_all_days += day_stats_item['trades']
|
|
|
+ trading_days_count += 1
|
|
|
else:
|
|
|
- # Day with no trades
|
|
|
- daily_text += f"📊 <b>{day_stats['date_formatted']}</b>\n"
|
|
|
- daily_text += f" 📭 No trading activity\n\n"
|
|
|
+ daily_text += f"📊 <b>{day_stats_item['date_formatted']}</b>\\n"
|
|
|
+ daily_text += f" 📭 No trading activity\\n\\n"
|
|
|
|
|
|
- # Add summary
|
|
|
- if trading_days > 0:
|
|
|
- avg_daily_pnl = total_pnl / trading_days
|
|
|
+ if trading_days_count > 0:
|
|
|
+ avg_daily_pnl = total_pnl_all_days / trading_days_count
|
|
|
avg_pnl_emoji = "🟢" if avg_daily_pnl >= 0 else "🔴"
|
|
|
+ total_pnl_all_days_str = formatter.format_price_with_symbol(total_pnl_all_days, quote_asset=Config.QUOTE_CURRENCY)
|
|
|
+ avg_daily_pnl_str = formatter.format_price_with_symbol(avg_daily_pnl, quote_asset=Config.QUOTE_CURRENCY)
|
|
|
|
|
|
- daily_text += f"📈 <b>Period Summary:</b>\n"
|
|
|
- daily_text += f" {avg_pnl_emoji} Total P&L: ${total_pnl:,.2f}\n"
|
|
|
- daily_text += f" 📊 Trading Days: {trading_days}/10\n"
|
|
|
- daily_text += f" 📈 Avg Daily P&L: ${avg_daily_pnl:,.2f}\n"
|
|
|
- daily_text += f" 🔄 Total Trades: {total_trades}\n"
|
|
|
+ daily_text += f"📈 <b>Period Summary:</b>\\n"
|
|
|
+ daily_text += f" {avg_pnl_emoji} Total P&L: {total_pnl_all_days_str}\\n"
|
|
|
+ daily_text += f" 📊 Trading Days: {trading_days_count}/10\\n"
|
|
|
+ daily_text += f" 📅 Avg Daily P&L: {avg_daily_pnl_str}\\n"
|
|
|
+ daily_text += f" 🔄 Total Trades: {total_trades_all_days}\\n"
|
|
|
|
|
|
await context.bot.send_message(chat_id=chat_id, text=daily_text.strip(), parse_mode='HTML')
|
|
|
|
|
@@ -864,60 +891,59 @@ class InfoCommands:
|
|
|
await context.bot.send_message(chat_id=chat_id, text="❌ Could not load trading statistics")
|
|
|
return
|
|
|
|
|
|
- weekly_stats = stats.get_weekly_stats(10)
|
|
|
+ weekly_stats_list = stats.get_weekly_stats(10) # Renamed variable
|
|
|
+ formatter = get_formatter() # Get formatter
|
|
|
|
|
|
- if not weekly_stats:
|
|
|
+ if not weekly_stats_list:
|
|
|
await context.bot.send_message(chat_id=chat_id, text=
|
|
|
- "📊 <b>Weekly Performance</b>\n\n"
|
|
|
- "📭 No weekly performance data available yet.\n\n"
|
|
|
- "💡 Weekly stats are calculated from completed trades.\n"
|
|
|
+ "📊 <b>Weekly Performance</b>\\n\\n"
|
|
|
+ "📭 No weekly performance data available yet.\\n\\n"
|
|
|
+ "💡 Weekly stats are calculated from completed trades.\\n"
|
|
|
"Start trading to see weekly performance!",
|
|
|
parse_mode='HTML'
|
|
|
)
|
|
|
return
|
|
|
|
|
|
- weekly_text = "📊 <b>Weekly Performance (Last 10 Weeks)</b>\n\n"
|
|
|
+ weekly_text = "📊 <b>Weekly Performance (Last 10 Weeks)</b>\\n\\n"
|
|
|
|
|
|
- total_pnl = 0
|
|
|
- total_trades = 0
|
|
|
- trading_weeks = 0
|
|
|
+ total_pnl_all_weeks = 0 # Renamed
|
|
|
+ total_trades_all_weeks = 0 # Renamed
|
|
|
+ trading_weeks_count = 0 # Renamed
|
|
|
|
|
|
- for week_stats in weekly_stats:
|
|
|
- if week_stats['has_trades']:
|
|
|
- # Week with completed trades
|
|
|
- pnl_emoji = "🟢" if week_stats['pnl'] >= 0 else "🔴"
|
|
|
- weekly_text += f"📈 <b>{week_stats['week_formatted']}</b>\n"
|
|
|
- weekly_text += f" {pnl_emoji} P&L: ${week_stats['pnl']:,.2f} ({week_stats['pnl_pct']:+.1f}%)\n"
|
|
|
- weekly_text += f" 🔄 Trades: {week_stats['trades']}\n\n"
|
|
|
+ for week_stats_item in weekly_stats_list: # Renamed
|
|
|
+ if week_stats_item['has_trades']:
|
|
|
+ pnl_emoji = "🟢" if week_stats_item['pnl'] >= 0 else "🔴"
|
|
|
+ pnl_str = formatter.format_price_with_symbol(week_stats_item['pnl'], quote_asset=Config.QUOTE_CURRENCY)
|
|
|
+ weekly_text += f"📈 <b>{week_stats_item['week_formatted']}</b>\\n"
|
|
|
+ weekly_text += f" {pnl_emoji} P&L: {pnl_str} ({week_stats_item['pnl_pct']:+.1f}%)\\n"
|
|
|
+ weekly_text += f" 🔄 Trades: {week_stats_item['trades']}\\n\\n"
|
|
|
|
|
|
- total_pnl += week_stats['pnl']
|
|
|
- total_trades += week_stats['trades']
|
|
|
- trading_weeks += 1
|
|
|
+ total_pnl_all_weeks += week_stats_item['pnl']
|
|
|
+ total_trades_all_weeks += week_stats_item['trades']
|
|
|
+ trading_weeks_count += 1
|
|
|
else:
|
|
|
- # Week with no trades
|
|
|
- weekly_text += f"📈 <b>{week_stats['week_formatted']}</b>\n"
|
|
|
- weekly_text += f" 📭 No completed trades\n\n"
|
|
|
-
|
|
|
- # Add summary
|
|
|
- if trading_weeks > 0:
|
|
|
- total_pnl_emoji = "🟢" if total_pnl >= 0 else "🔴"
|
|
|
- weekly_text += f"💼 <b>10-Week Summary:</b>\n"
|
|
|
- weekly_text += f" {total_pnl_emoji} Total P&L: ${total_pnl:,.2f}\n"
|
|
|
- weekly_text += f" 🔄 Total Trades: {total_trades}\n"
|
|
|
- weekly_text += f" 📈 Trading Weeks: {trading_weeks}/10\n"
|
|
|
- weekly_text += f" 📊 Avg per Trading Week: ${total_pnl/trading_weeks:,.2f}"
|
|
|
- else:
|
|
|
- weekly_text += f"💼 <b>10-Week Summary:</b>\n"
|
|
|
- weekly_text += f" 📭 No completed trades in the last 10 weeks\n"
|
|
|
- weekly_text += f" 💡 Start trading to see weekly performance!"
|
|
|
+ weekly_text += f"📈 <b>{week_stats_item['week_formatted']}</b>\\n"
|
|
|
+ weekly_text += f" 📭 No trading activity\\n\\n"
|
|
|
+
|
|
|
+ if trading_weeks_count > 0:
|
|
|
+ avg_weekly_pnl = total_pnl_all_weeks / trading_weeks_count
|
|
|
+ avg_pnl_emoji = "🟢" if avg_weekly_pnl >= 0 else "🔴"
|
|
|
+ total_pnl_all_weeks_str = formatter.format_price_with_symbol(total_pnl_all_weeks, quote_asset=Config.QUOTE_CURRENCY)
|
|
|
+ avg_weekly_pnl_str = formatter.format_price_with_symbol(avg_weekly_pnl, quote_asset=Config.QUOTE_CURRENCY)
|
|
|
+
|
|
|
+ weekly_text += f"📅 <b>Period Summary:</b>\\n"
|
|
|
+ weekly_text += f" {avg_pnl_emoji} Total P&L: {total_pnl_all_weeks_str}\\n"
|
|
|
+ weekly_text += f" 📊 Trading Weeks: {trading_weeks_count}/10\\n"
|
|
|
+ weekly_text += f" 📅 Avg Weekly P&L: {avg_weekly_pnl_str}\\n"
|
|
|
+ weekly_text += f" 🔄 Total Trades: {total_trades_all_weeks}\\n"
|
|
|
|
|
|
await context.bot.send_message(chat_id=chat_id, text=weekly_text.strip(), parse_mode='HTML')
|
|
|
-
|
|
|
+
|
|
|
except Exception as e:
|
|
|
error_message = f"❌ Error processing weekly command: {str(e)}"
|
|
|
await context.bot.send_message(chat_id=chat_id, text=error_message)
|
|
|
logger.error(f"Error in weekly command: {e}")
|
|
|
-
|
|
|
+
|
|
|
async def monthly_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
|
|
"""Handle the /monthly command to show monthly performance stats."""
|
|
|
chat_id = update.effective_chat.id
|
|
@@ -931,55 +957,54 @@ class InfoCommands:
|
|
|
await context.bot.send_message(chat_id=chat_id, text="❌ Could not load trading statistics")
|
|
|
return
|
|
|
|
|
|
- monthly_stats = stats.get_monthly_stats(10)
|
|
|
+ monthly_stats_list = stats.get_monthly_stats(12) # Renamed variable, 12 months
|
|
|
+ formatter = get_formatter() # Get formatter
|
|
|
|
|
|
- if not monthly_stats:
|
|
|
+ if not monthly_stats_list:
|
|
|
await context.bot.send_message(chat_id=chat_id, text=
|
|
|
- "📆 <b>Monthly Performance</b>\n\n"
|
|
|
- "📭 No monthly performance data available yet.\n\n"
|
|
|
- "💡 Monthly stats are calculated from completed trades.\n"
|
|
|
+ "🗓️ <b>Monthly Performance</b>\\n\\n"
|
|
|
+ "📭 No monthly performance data available yet.\\n\\n"
|
|
|
+ "💡 Monthly stats are calculated from completed trades.\\n"
|
|
|
"Start trading to see monthly performance!",
|
|
|
parse_mode='HTML'
|
|
|
)
|
|
|
return
|
|
|
|
|
|
- monthly_text = "📆 <b>Monthly Performance (Last 10 Months)</b>\n\n"
|
|
|
+ monthly_text = "🗓️ <b>Monthly Performance (Last 12 Months)</b>\\n\\n"
|
|
|
|
|
|
- total_pnl = 0
|
|
|
- total_trades = 0
|
|
|
- trading_months = 0
|
|
|
+ total_pnl_all_months = 0 # Renamed
|
|
|
+ total_trades_all_months = 0 # Renamed
|
|
|
+ trading_months_count = 0 # Renamed
|
|
|
|
|
|
- for month_stats in monthly_stats:
|
|
|
- if month_stats['has_trades']:
|
|
|
- # Month with completed trades
|
|
|
- pnl_emoji = "🟢" if month_stats['pnl'] >= 0 else "🔴"
|
|
|
- monthly_text += f"📅 <b>{month_stats['month_formatted']}</b>\n"
|
|
|
- monthly_text += f" {pnl_emoji} P&L: ${month_stats['pnl']:,.2f} ({month_stats['pnl_pct']:+.1f}%)\n"
|
|
|
- monthly_text += f" 🔄 Trades: {month_stats['trades']}\n\n"
|
|
|
+ for month_stats_item in monthly_stats_list: # Renamed
|
|
|
+ if month_stats_item['has_trades']:
|
|
|
+ pnl_emoji = "🟢" if month_stats_item['pnl'] >= 0 else "🔴"
|
|
|
+ pnl_str = formatter.format_price_with_symbol(month_stats_item['pnl'], quote_asset=Config.QUOTE_CURRENCY)
|
|
|
+ monthly_text += f"📅 <b>{month_stats_item['month_formatted']}</b>\\n"
|
|
|
+ monthly_text += f" {pnl_emoji} P&L: {pnl_str} ({month_stats_item['pnl_pct']:+.1f}%)\\n"
|
|
|
+ monthly_text += f" 🔄 Trades: {month_stats_item['trades']}\\n\\n"
|
|
|
|
|
|
- total_pnl += month_stats['pnl']
|
|
|
- total_trades += month_stats['trades']
|
|
|
- trading_months += 1
|
|
|
+ total_pnl_all_months += month_stats_item['pnl']
|
|
|
+ total_trades_all_months += month_stats_item['trades']
|
|
|
+ trading_months_count += 1
|
|
|
else:
|
|
|
- # Month with no trades
|
|
|
- monthly_text += f"📅 <b>{month_stats['month_formatted']}</b>\n"
|
|
|
- monthly_text += f" 📭 No completed trades\n\n"
|
|
|
-
|
|
|
- # Add summary
|
|
|
- if trading_months > 0:
|
|
|
- total_pnl_emoji = "🟢" if total_pnl >= 0 else "🔴"
|
|
|
- monthly_text += f"💼 <b>10-Month Summary:</b>\n"
|
|
|
- monthly_text += f" {total_pnl_emoji} Total P&L: ${total_pnl:,.2f}\n"
|
|
|
- monthly_text += f" 🔄 Total Trades: {total_trades}\n"
|
|
|
- monthly_text += f" 📈 Trading Months: {trading_months}/10\n"
|
|
|
- monthly_text += f" 📊 Avg per Trading Month: ${total_pnl/trading_months:,.2f}"
|
|
|
- else:
|
|
|
- monthly_text += f"💼 <b>10-Month Summary:</b>\n"
|
|
|
- monthly_text += f" 📭 No completed trades in the last 10 months\n"
|
|
|
- monthly_text += f" 💡 Start trading to see monthly performance!"
|
|
|
+ monthly_text += f"📅 <b>{month_stats_item['month_formatted']}</b>\\n"
|
|
|
+ monthly_text += f" 📭 No trading activity\\n\\n"
|
|
|
+
|
|
|
+ if trading_months_count > 0:
|
|
|
+ avg_monthly_pnl = total_pnl_all_months / trading_months_count
|
|
|
+ avg_pnl_emoji = "🟢" if avg_monthly_pnl >= 0 else "🔴"
|
|
|
+ total_pnl_all_months_str = formatter.format_price_with_symbol(total_pnl_all_months, quote_asset=Config.QUOTE_CURRENCY)
|
|
|
+ avg_monthly_pnl_str = formatter.format_price_with_symbol(avg_monthly_pnl, quote_asset=Config.QUOTE_CURRENCY)
|
|
|
+
|
|
|
+ monthly_text += f"📈 <b>Period Summary:</b>\\n"
|
|
|
+ monthly_text += f" {avg_pnl_emoji} Total P&L: {total_pnl_all_months_str}\\n"
|
|
|
+ monthly_text += f" 📊 Trading Months: {trading_months_count}/12\\n"
|
|
|
+ monthly_text += f" 🗓️ Avg Monthly P&L: {avg_monthly_pnl_str}\\n"
|
|
|
+ monthly_text += f" 🔄 Total Trades: {total_trades_all_months}\\n"
|
|
|
|
|
|
await context.bot.send_message(chat_id=chat_id, text=monthly_text.strip(), parse_mode='HTML')
|
|
|
-
|
|
|
+
|
|
|
except Exception as e:
|
|
|
error_message = f"❌ Error processing monthly command: {str(e)}"
|
|
|
await context.bot.send_message(chat_id=chat_id, text=error_message)
|