@@ -340,9 +340,9 @@ def add_symbol(self, symbol):
340340 return
341341 try :
342342 if symbol not in self .symbols_to_monitor :
343- # Validate symbol format
344- if not symbol or not isinstance (symbol , str ) or not symbol . endswith ( 'USDC' ) :
345- print (f"[WEBSOCKET] ❌ Invalid symbol format : { symbol } " )
343+ # Validate symbol format - support ALLOWED_SYMBOLS including BTCETH
344+ if not symbol or not isinstance (symbol , str ) or symbol not in ALLOWED_SYMBOLS :
345+ print (f"[WEBSOCKET] ❌ Symbol not in ALLOWED_SYMBOLS : { symbol } " )
346346 return
347347 # Add to monitoring list
348348 self .symbols_to_monitor .add (symbol )
@@ -834,12 +834,12 @@ def update_websocket_symbols():
834834 except Exception as e :
835835 print (f"[WEBSOCKET UPDATE] YAML error: { e } " )
836836
837- # Filter to valid symbols and limit total count
837+ # Filter to valid symbols (ALLOWED_SYMBOLS) and limit total count
838838 valid_symbols = [s for s in symbols_to_monitor
839- if s and isinstance (s , str ) and s . endswith ( 'USDC' ) ]
839+ if s and isinstance (s , str ) and s in ALLOWED_SYMBOLS ]
840840
841- # Limit to maximum 5 symbols for deployment performance
842- valid_symbols = valid_symbols [:5 ]
841+ # Limit to maximum 3 symbols for deployment performance (BTCUSDC, ETHUSDC, BTCETH)
842+ valid_symbols = valid_symbols [:3 ]
843843
844844 if valid_symbols :
845845 print (f"[WEBSOCKET UPDATE] Updating to monitor { len (valid_symbols )} symbols: { valid_symbols } " )
@@ -1458,13 +1458,22 @@ async def telegram_handle_message(update: Update, context: ContextTypes.DEFAULT_
14581458 sync_positions_with_binance (client , positions )
14591459
14601460 # Support both button text and commands
1461- if text in ["📊 Balance" , "/balance" , "/bal" ]:
1462- fetch_usdc_balance ()
1461+ if text in ["� Portfolio" , "� 📊 Balance" , "/balance" , "/bal" ]:
1462+ fetch_all_balances () # ✅ Use cross-asset balance fetching
14631463
1464+ btc = balance ['btc' ]
1465+ eth = balance ['eth' ]
14641466 usdc = balance ['usd' ]
14651467 total_invested = 0.0
14661468 invested_details = []
14671469
1470+ # Calculate USD values for BTC and ETH
1471+ btc_price = get_latest_price ('BTCUSDC' ) or 0
1472+ eth_price = get_latest_price ('ETHUSDC' ) or 0
1473+ btc_value = btc * btc_price
1474+ eth_value = eth * eth_price
1475+
1476+ # Calculate total invested in positions
14681477 for s , p in positions .items ():
14691478 price = get_latest_price (s )
14701479 if price is None :
@@ -1476,22 +1485,31 @@ async def telegram_handle_message(update: Update, context: ContextTypes.DEFAULT_
14761485 total_invested += value
14771486 invested_details .append (f"{ s } : { qty :.6f} @ ${ price :.2f} = ${ value :.2f} " )
14781487
1488+ # Cross-asset portfolio message
14791489 msg = (
1480- f"USDC Balance: **${ usdc :.2f} **\n "
1481- f"Total Invested: **${ total_invested :.2f} **\n "
1482- f"Portfolio Value: **${ usdc + total_invested :.2f} USDC**"
1490+ f"**💰 Cross-Asset Portfolio Balance:**\n \n "
1491+ f"**Available Assets:**\n "
1492+ f"• BTC: **{ btc :.6f} ** (~${ btc_value :.2f} )\n "
1493+ f"• ETH: **{ eth :.6f} ** (~${ eth_value :.2f} )\n "
1494+ f"• USDC: **${ usdc :.2f} **\n \n "
1495+ f"**Portfolio Summary:**\n "
1496+ f"• Available: **${ btc_value + eth_value + usdc :.2f} **\n "
1497+ f"• Invested: **${ total_invested :.2f} **\n "
1498+ f"• **Total Portfolio Value: ${ btc_value + eth_value + usdc + total_invested :.2f} **"
14831499 )
14841500
14851501 if invested_details :
1486- msg += "\n \n *Investments:*"
1487- for line in invested_details :
1488- msg += f"\n - { line } "
1502+ msg += "\n \n *Active Investments:*"
1503+ for line in invested_details [:5 ]: # Limit to first 5 to avoid message length
1504+ msg += f"\n • { line } "
1505+ if len (invested_details ) > 5 :
1506+ msg += f"\n • ... and { len (invested_details ) - 5 } more positions"
14891507
14901508 await send_with_keyboard (update , msg , parse_mode = 'Markdown' )
14911509
14921510
14931511
1494- elif text in ["💼 Investments" , "/investments" , "/inv" , "/positions" ]:
1512+ elif text in ["💼 Positions" , "💼 Investments" , "/investments" , "/inv" , "/positions" ]:
14951513 msg = format_investments_message (positions , get_latest_price , DUST_LIMIT )
14961514 await send_with_keyboard (update , msg )
14971515
@@ -1531,8 +1549,8 @@ async def telegram_handle_message(update: Update, context: ContextTypes.DEFAULT_
15311549 continue
15321550 await send_with_keyboard (update , f"```{ msg } ```" , parse_mode = 'Markdown' )
15331551
1534- elif text in ["🔍 Diagnose" , "/diagnose" , "/diag" ]:
1535- await send_with_keyboard (update , "🔍 Running investment diagnostic for ETHUSDC ..." )
1552+ elif text in ["🔍 Diagnostics" , "🔍 Diagnose" , "/diagnose" , "/diag" ]:
1553+ await send_with_keyboard (update , "🔍 Running cross-asset investment diagnostic..." )
15361554
15371555 # Capture diagnostic output
15381556 import io
@@ -1542,21 +1560,34 @@ async def telegram_handle_message(update: Update, context: ContextTypes.DEFAULT_
15421560 sys .stdout = captured_output = io .StringIO ()
15431561
15441562 try :
1545- diagnose_investment_failure ("ETHUSDC" )
1563+ # Run diagnostic for all allowed symbols instead of just ETHUSDC
1564+ for i , symbol in enumerate (ALLOWED_SYMBOLS ):
1565+ if i > 0 :
1566+ print ("\n " + "=" * 50 + "\n " ) # Separator between symbols
1567+ diagnose_investment_failure (symbol )
15461568 diagnostic_text = captured_output .getvalue ()
15471569 finally :
15481570 sys .stdout = old_stdout
15491571
1550- # Split into chunks if too long for Telegram
1551- max_length = 4000
1572+ # Enhanced diagnostic summary
1573+ summary_msg = f"🔍 **Cross-Asset Diagnostic Summary**\n \n "
1574+ summary_msg += f"**Checked Symbols:** { ', ' .join (ALLOWED_SYMBOLS )} \n "
1575+ summary_msg += f"**Bot Status:** { '⏸️ PAUSED' if is_paused () else '▶️ ACTIVE' } \n "
1576+ summary_msg += f"**Market Risk:** { '🔴 HIGH' if 'market_is_risky' in globals () and market_is_risky () else '🟢 NORMAL' } \n "
1577+ summary_msg += f"**Positions:** { len ([p for s , p in positions .items () if get_latest_price (s ) and p .get ('qty' , 0 ) * get_latest_price (s ) > DUST_LIMIT ])} /{ MAX_POSITIONS } \n \n "
1578+
1579+ await send_with_keyboard (update , summary_msg , parse_mode = 'Markdown' )
1580+
1581+ # Split detailed diagnostic into chunks if too long for Telegram
1582+ max_length = 3500 # Shorter chunks to ensure delivery
15521583 if len (diagnostic_text ) > max_length :
15531584 chunks = [diagnostic_text [i :i + max_length ] for i in range (0 , len (diagnostic_text ), max_length )]
15541585 for i , chunk in enumerate (chunks ):
1555- await send_with_keyboard (update , f"```\n Diagnostic Report ({ i + 1 } /{ len (chunks )} ):\n { chunk } \n ```" , parse_mode = 'Markdown' )
1586+ await send_with_keyboard (update , f"```\n Detailed Report ({ i + 1 } /{ len (chunks )} ):\n { chunk } \n ```" , parse_mode = 'Markdown' )
15561587 else :
1557- await send_with_keyboard (update , f"```\n Diagnostic Report:\n { diagnostic_text } \n ```" , parse_mode = 'Markdown' )
1588+ await send_with_keyboard (update , f"```\n Detailed Diagnostic Report:\n { diagnostic_text } \n ```" , parse_mode = 'Markdown' )
15581589
1559- elif text in ["🔄 WebSocket Status" , "/websocket" , "/ws" ]:
1590+ elif text in ["🔄 Connection" , "🔄 WebSocket Status" , "/websocket" , "/ws" , "/connection " ]:
15601591 status = get_realtime_price_summary ()
15611592 msg = (
15621593 f"🔄 **WebSocket Price Monitoring Status**\n \n "
@@ -1582,45 +1613,109 @@ async def telegram_handle_message(update: Update, context: ContextTypes.DEFAULT_
15821613
15831614 await send_with_keyboard (update , msg , parse_mode = 'Markdown' )
15841615
1585- elif text in ["⚡ Check Momentum" , "/momentum" , "/check" ]:
1586- await send_with_keyboard (update , "⚡ Running manual momentum check..." )
1616+ elif text in ["⚡ Quick Check" , "⚡ Check Momentum" , "/momentum" , "/check" , "/quick " ]:
1617+ await send_with_keyboard (update , "⚡ Quick momentum check for top symbols ..." )
15871618
15881619 try :
1589- # Run the quick momentum check manually
15901620 start_time = time .time ()
1591- await alarm_job (context )
1592- end_time = time .time ()
15931621
1622+ # Quick check of only ALLOWED_SYMBOLS for performance
1623+ results = []
1624+
1625+ for symbol in ALLOWED_SYMBOLS :
1626+ try :
1627+ # Get current price
1628+ current_price = get_latest_price (symbol )
1629+ if not current_price :
1630+ results .append (f"• { symbol } : ❌ No price data" )
1631+ continue
1632+
1633+ # Quick momentum check using has_recent_momentum if available
1634+ try :
1635+ has_momentum = has_recent_momentum (symbol ) if 'has_recent_momentum' in globals () else False
1636+ momentum_status = "✅ Strong" if has_momentum else "⚪ Weak"
1637+ except :
1638+ # Fallback: simple 1m price change check
1639+ try :
1640+ klines = client .get_klines (symbol = symbol , interval = '1m' , limit = 2 )
1641+ if len (klines ) >= 2 :
1642+ prev_close = float (klines [- 2 ][4 ])
1643+ current_close = float (klines [- 1 ][4 ])
1644+ pct_change = ((current_close - prev_close ) / prev_close ) * 100
1645+ momentum_status = "✅ Rising" if pct_change > 0.3 else "⚪ Stable" if pct_change > - 0.3 else "🔴 Falling"
1646+ else :
1647+ momentum_status = "❓ No data"
1648+ except :
1649+ momentum_status = "❌ Error"
1650+
1651+ results .append (f"• { symbol } : { momentum_status } @ ${ current_price :.4f} " )
1652+
1653+ except Exception as e :
1654+ results .append (f"• { symbol } : ❌ Error - { str (e )[:30 ]} " )
1655+
1656+ end_time = time .time ()
15941657 processing_time = end_time - start_time
1595- await send_with_keyboard (update ,
1596- f"✅ Manual momentum check completed in { processing_time :.2f} seconds" )
1658+
1659+ msg = (
1660+ f"⚡ **Quick Momentum Check Results:**\n "
1661+ f"⏱️ Completed in { processing_time :.2f} s\n \n "
1662+ + "\n " .join (results ) +
1663+ f"\n \n 💡 *This is a fast check of allowed symbols only.*"
1664+ )
1665+
1666+ await send_with_keyboard (update , msg , parse_mode = 'Markdown' )
1667+
15971668 except Exception as e :
1598- await send_with_keyboard (update , f"❌ Momentum check failed: { str (e )} " )
1669+ await send_with_keyboard (update , f"❌ Quick momentum check failed: { str (e )} " )
15991670
1600- elif text in ["📊 Trading Status" , "/status" , "/trading" ]:
1601- await send_with_keyboard (update , "📊 Getting trading status ..." )
1671+ elif text in ["� Overview" , "📈 Status" , "� 📊 Trading Status" , "/status" , "/trading" , "/overview " ]:
1672+ await send_with_keyboard (update , "� Getting complete cross-asset overview ..." )
16021673
1603- # Capture debug output
1604- import io
1605- import sys
1674+ fetch_all_balances () # Get all asset balances
1675+ btc = balance ['btc' ]
1676+ eth = balance ['eth' ]
1677+ usdc = balance ['usd' ]
16061678
1607- old_stdout = sys .stdout
1608- sys .stdout = captured_output = io .StringIO ()
1679+ # Calculate USD values
1680+ btc_price = get_latest_price ('BTCUSDC' ) or 0
1681+ eth_price = get_latest_price ('ETHUSDC' ) or 0
1682+ btc_value = btc * btc_price
1683+ eth_value = eth * eth_price
1684+ available_value = btc_value + eth_value + usdc
16091685
1610- try :
1611- get_trading_status ()
1612- debug_text = captured_output .getvalue ()
1613- finally :
1614- sys .stdout = old_stdout
1686+ # Calculate total invested
1687+ total_invested = sum (get_latest_price (s ) * p .get ('qty' , 0 )
1688+ for s , p in positions .items ()
1689+ if get_latest_price (s ) and p .get ('qty' , 0 ) * get_latest_price (s ) > DUST_LIMIT )
16151690
1616- # Send debug info
1617- if len (debug_text ) > 4000 :
1618- # Split long messages
1619- chunks = [debug_text [i :i + 4000 ] for i in range (0 , len (debug_text ), 4000 )]
1620- for i , chunk in enumerate (chunks ):
1621- await send_with_keyboard (update , f"```\n Trading Status ({ i + 1 } /{ len (chunks )} ):\n { chunk } \n ```" , parse_mode = 'Markdown' )
1622- else :
1623- await send_with_keyboard (update , f"```\n Trading Status:\n { debug_text } \n ```" , parse_mode = 'Markdown' )
1691+ # Get trading mode
1692+ trading_mode = get_effective_trading_mode ()
1693+
1694+ status_msg = f"""**📈 Cross-Asset Trading Bot Overview**
1695+
1696+ **💰 Available Assets:**
1697+ • BTC: { btc :.6f} (~${ btc_value :.2f} )
1698+ • ETH: { eth :.6f} (~${ eth_value :.2f} )
1699+ • USDC: ${ usdc :.2f}
1700+ • **Available Total: ${ available_value :.2f} **
1701+
1702+ **💼 Investment Status:**
1703+ • Total Invested: ${ total_invested :.2f}
1704+ • **Portfolio Value: ${ available_value + total_invested :.2f} **
1705+ • Active Positions: { len ([p for s , p in positions .items () if get_latest_price (s ) and p .get ('qty' , 0 ) * get_latest_price (s ) > DUST_LIMIT ])} /{ MAX_POSITIONS }
1706+
1707+ **⚙️ Trading Status:**
1708+ • Bot State: { '⏸️ PAUSED' if is_paused () else '▶️ ACTIVE' }
1709+ • Trading Mode: { trading_mode }
1710+ • Market Risk: { '🔴 HIGH' if 'market_is_risky' in globals () and market_is_risky () else '🟢 NORMAL' }
1711+ • Cross-Asset Symbols: { ', ' .join (ALLOWED_SYMBOLS )}
1712+
1713+ **🔄 Connection Status:**
1714+ • WebSocket: { '🟢 Active' if ws_price_manager and ws_price_manager ._running else '🔴 Inactive' }
1715+ • Monitored: { len (ws_price_manager .get_monitored_symbols ()) if ws_price_manager else 0 } symbols
1716+
1717+ Type `/help` for all available commands."""
1718+ await send_with_keyboard (update , status_msg , parse_mode = 'Markdown' )
16241719
16251720 elif text in ["⚡ Fast Check" , "/fast" , "/quick" ]:
16261721 await send_with_keyboard (update , "⚡ Running fast momentum check for immediate opportunities..." )
@@ -1891,12 +1986,11 @@ def is_paused():
18911986 return state .get ("paused" , False )
18921987
18931988main_keyboard = [
1894- ["📊 Balance" , "💼 Investments" ],
1895- ["⏸ Pause Trading" , "▶️ Resume Trading" ],
1896- ["📝 Trade Log" , "🔍 Diagnose" ],
1897- ["🔄 WebSocket Status" , "⚡ Check Momentum" ],
1898- ["📊 Trading Status" , "⚡ Fast Check" ],
1899- ["🔧 API Test" , "📈 Status" ]
1989+ ["� Portfolio" , "💼 Positions" ], # Clear financial info
1990+ ["⏸ Pause Trading" , "▶️ Resume Trading" ], # Trading control
1991+ ["📝 Trade Log" , "🔍 Diagnostics" ], # History & enhanced diagnostics
1992+ ["🔄 Connection" , "⚡ Quick Check" ], # WebSocket + fast momentum
1993+ ["🔧 API Test" , "📈 Overview" ], # Technical test + complete status
19001994]
19011995
19021996async def start_handler (update : Update , context : ContextTypes .DEFAULT_TYPE ):
0 commit comments