From 6e4da31dcf27eccb9e291c27d7c5ce204d76b57e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 22 Nov 2025 04:14:41 +0000 Subject: [PATCH 1/5] Initial plan From 7c7c842f74ba5c99126b60f92d1a4a866a273cf8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 22 Nov 2025 04:19:56 +0000 Subject: [PATCH 2/5] Add system info endpoint and Telegram command Co-authored-by: QOAB <216093345+QOAB@users.noreply.github.com> --- backend/api/routes.py | 140 ++++++++++++++++++++++++++++++++++++++++++ bot/main.py | 78 +++++++++++++++++++++++ 2 files changed, 218 insertions(+) diff --git a/backend/api/routes.py b/backend/api/routes.py index 6544130..216abfa 100644 --- a/backend/api/routes.py +++ b/backend/api/routes.py @@ -327,3 +327,143 @@ async def health_check(): "service": "trading-bot-api", "timestamp": datetime.utcnow().isoformat() } + + +@router.get("/system-info") +async def get_system_info(db: AsyncSession = Depends(get_db)): + """ + Comprehensive system information endpoint. + Returns complete inventory of what the system has: + - System configuration and parameters + - Strategy settings and indicators + - Portfolio state and performance + - Database statistics + - Exchange connection info + - Available features + """ + try: + # Portfolio status + portfolio = paper_engine.get_status() + + # Database statistics + result = await db.execute(select(Trade)) + all_trades = result.scalars().all() + + closed_trades = [t for t in all_trades if t.status == "closed"] + open_trades = [t for t in all_trades if t.status == "open"] + + # Calculate statistics + total_pnl = sum(t.pnl for t in closed_trades if t.pnl is not None) + winning_trades = [t for t in closed_trades if t.pnl and t.pnl > 0] + losing_trades = [t for t in closed_trades if t.pnl and t.pnl < 0] + win_rate = (len(winning_trades) / len(closed_trades) * 100) if closed_trades else 0 + + # Average win/loss + avg_win = sum(t.pnl for t in winning_trades) / len(winning_trades) if winning_trades else 0 + avg_loss = sum(t.pnl for t in losing_trades) / len(losing_trades) if losing_trades else 0 + + # Profit factor + gross_profit = sum(t.pnl for t in winning_trades) + gross_loss = abs(sum(t.pnl for t in losing_trades)) + profit_factor = gross_profit / gross_loss if gross_loss > 0 else 0 + + # Exchange status + try: + ticker = await market_data.get_ticker("BTCUSDT") + exchange_status = "connected" + exchange_last_price = ticker.get("last", 0) + except Exception: + exchange_status = "disconnected" + exchange_last_price = 0 + + return { + "system": { + "name": "Swing Trend Trading Bot", + "version": "1.0.0", + "phase": "Phase 0 - Paper Trading", + "timestamp": datetime.utcnow().isoformat() + }, + "configuration": { + "initial_capital": settings.INITIAL_CAPITAL, + "risk_per_trade": f"{settings.RISK_PER_TRADE}%", + "max_open_positions": settings.MAX_OPEN_POSITIONS, + "daily_loss_limit": f"{settings.DAILY_LOSS_LIMIT}%", + "timeframe": settings.TIMEFRAME, + "timezone": settings.TIMEZONE + }, + "strategy": { + "name": "Swing Trend Baseline", + "indicators": { + "ema_fast": settings.EMA_FAST, + "ema_slow": settings.EMA_SLOW, + "rsi_length": settings.RSI_LENGTH, + "rsi_long_threshold": settings.RSI_LONG_THRESHOLD, + "rsi_short_threshold": settings.RSI_SHORT_THRESHOLD, + "atr_length": settings.ATR_LENGTH, + "breakout_lookback": settings.LOOKBACK + }, + "risk_management": { + "stop_loss": "Entry ± 2*ATR", + "take_profit": f"Entry ± {settings.RR_RATIO}*(Entry-StopLoss)", + "risk_reward_ratio": settings.RR_RATIO + } + }, + "portfolio": { + "equity": portfolio["equity"], + "available_capital": portfolio["available"], + "open_positions": len(portfolio["positions"]), + "positions_detail": portfolio["positions"], + "total_pnl": round(total_pnl, 2), + "pnl_percentage": round((total_pnl / settings.INITIAL_CAPITAL) * 100, 2) if settings.INITIAL_CAPITAL > 0 else 0 + }, + "statistics": { + "total_trades": len(all_trades), + "closed_trades": len(closed_trades), + "open_trades": len(open_trades), + "winning_trades": len(winning_trades), + "losing_trades": len(losing_trades), + "win_rate": round(win_rate, 2), + "average_win": round(avg_win, 2), + "average_loss": round(avg_loss, 2), + "profit_factor": round(profit_factor, 2), + "gross_profit": round(gross_profit, 2), + "gross_loss": round(gross_loss, 2) + }, + "exchange": { + "name": "Binance", + "mode": "testnet" if settings.EXCHANGE_TESTNET else "live", + "status": exchange_status, + "btcusdt_price": exchange_last_price, + "api_configured": bool(settings.EXCHANGE_API_KEY and settings.EXCHANGE_API_SECRET) + }, + "features": { + "api_endpoints": [ + "GET /api/v1/status - Portfolio status", + "GET /api/v1/signals/{symbol} - Check trading signal", + "POST /api/v1/execute/{symbol} - Open position", + "GET /api/v1/positions - List open positions", + "POST /api/v1/update-positions/{symbol} - Update positions", + "GET /api/v1/trades/history - Trade history", + "GET /api/v1/system-info - System information", + "GET /health - Health check" + ], + "telegram_commands": [ + "/start - Welcome message", + "/help - Command help", + "/status - Portfolio status", + "/signals BTCUSDT - Check signal", + "/execute BTCUSDT - Open position", + "/positions - List positions", + "/update BTCUSDT - Update positions", + "/history - Trade history", + "/system - System information" + ] + }, + "database": { + "connection": "PostgreSQL + TimescaleDB", + "tables": ["trades", "ohlcv", "portfolio_state"], + "total_records": len(all_trades) + } + } + except Exception as e: + raise HTTPException(status_code=500, detail=f"Error fetching system info: {str(e)}") diff --git a/bot/main.py b/bot/main.py index fcb3eb0..43871d4 100644 --- a/bot/main.py +++ b/bot/main.py @@ -38,6 +38,7 @@ async def start_command(update: Update, context: ContextTypes.DEFAULT_TYPE): "/positions - Показать открытые позиции\n" "/update - Обновить позиции (проверить SL/TP)\n" "/history - Показать историю сделок\n" + "/system - Показать информацию о системе\n" "/help - Показать помощь\n\n" "⚠️ *ВНИМАНИЕ:* Это paper trading (тестовый режим)!\n" "Реальные деньги не используются." @@ -71,6 +72,11 @@ async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE): "Обновляет позиции (проверяет SL/TP hits)\n\n" "*6️⃣ /history*\n" "Показывает последние 20 закрытых сделок\n\n" + "*7️⃣ /system*\n" + "Показывает полную информацию о системе:\n" + "• Конфигурация и параметры\n" + "• Статистика торговли\n" + "• Состояние биржи и базы данных\n\n" "*⚙️ Параметры стратегии:*\n" f"• Timeframe: {settings.TIMEFRAME}\n" f"• Risk per trade: {settings.RISK_PER_TRADE}%\n" @@ -342,6 +348,77 @@ async def history_command(update: Update, context: ContextTypes.DEFAULT_TYPE): await update.message.reply_text(f"❌ Ошибка: {str(e)}") +async def system_command(update: Update, context: ContextTypes.DEFAULT_TYPE): + """Handle /system command - show comprehensive system information.""" + try: + processing_msg = await update.message.reply_text("⏳ Загрузка информации о системе...") + + async with httpx.AsyncClient() as client: + response = await client.get(f"{API_BASE}/system-info", timeout=15.0) + response.raise_for_status() + data = response.json() + + # Build comprehensive message + message = ( + f"🤖 *{data['system']['name']}*\n" + f"📌 {data['system']['phase']}\n" + f"🔖 Version: {data['system']['version']}\n\n" + + f"⚙️ *Конфигурация:*\n" + f"💰 Initial Capital: ${data['configuration']['initial_capital']:.0f}\n" + f"⚠️ Risk per Trade: {data['configuration']['risk_per_trade']}\n" + f"📊 Max Positions: {data['configuration']['max_open_positions']}\n" + f"🛑 Daily Loss Limit: {data['configuration']['daily_loss_limit']}\n" + f"⏰ Timeframe: {data['configuration']['timeframe']}\n\n" + + f"📈 *Стратегия: {data['strategy']['name']}*\n" + f"• EMA Fast/Slow: {data['strategy']['indicators']['ema_fast']}/{data['strategy']['indicators']['ema_slow']}\n" + f"• RSI: {data['strategy']['indicators']['rsi_length']}\n" + f"• ATR: {data['strategy']['indicators']['atr_length']}\n" + f"• Breakout Lookback: {data['strategy']['indicators']['breakout_lookback']}\n" + f"• R:R Ratio: {data['strategy']['risk_management']['risk_reward_ratio']}\n\n" + + f"💼 *Портфель:*\n" + f"💰 Equity: ${data['portfolio']['equity']:.2f}\n" + f"💵 Available: ${data['portfolio']['available_capital']:.2f}\n" + f"📊 Open Positions: {data['portfolio']['open_positions']}\n" + f"💸 Total P&L: ${data['portfolio']['total_pnl']:.2f} ({data['portfolio']['pnl_percentage']:.2f}%)\n\n" + + f"📊 *Статистика:*\n" + f"📝 Total Trades: {data['statistics']['total_trades']}\n" + f"✅ Closed: {data['statistics']['closed_trades']} | 🔓 Open: {data['statistics']['open_trades']}\n" + f"🟢 Wins: {data['statistics']['winning_trades']} | 🔴 Losses: {data['statistics']['losing_trades']}\n" + f"🎯 Win Rate: {data['statistics']['win_rate']:.1f}%\n" + f"📈 Avg Win: ${data['statistics']['average_win']:.2f}\n" + f"📉 Avg Loss: ${data['statistics']['average_loss']:.2f}\n" + f"💹 Profit Factor: {data['statistics']['profit_factor']:.2f}\n\n" + + f"🔗 *Exchange:*\n" + f"• {data['exchange']['name']} ({data['exchange']['mode'].upper()})\n" + f"• Status: {data['exchange']['status']}\n" + f"• API Configured: {'✅' if data['exchange']['api_configured'] else '❌'}\n" + ) + + if data['exchange']['btcusdt_price'] > 0: + message += f"• BTC/USDT: ${data['exchange']['btcusdt_price']:.2f}\n" + + message += ( + f"\n💾 *База данных:*\n" + f"• {data['database']['connection']}\n" + f"• Tables: {len(data['database']['tables'])}\n" + f"• Records: {data['database']['total_records']}\n" + ) + + await processing_msg.edit_text(message, parse_mode='Markdown') + + except httpx.HTTPError as e: + logger.error(f"HTTP error in system_command: {e}") + await update.message.reply_text("❌ Ошибка при получении системной информации") + except Exception as e: + logger.error(f"Error in system_command: {e}") + await update.message.reply_text(f"❌ Ошибка: {str(e)}") + + def main(): """Start the bot.""" # Create application @@ -356,6 +433,7 @@ def main(): application.add_handler(CommandHandler("positions", positions_command)) application.add_handler(CommandHandler("update", update_command)) application.add_handler(CommandHandler("history", history_command)) + application.add_handler(CommandHandler("system", system_command)) # Start bot logger.info("Starting Telegram bot...") From 8437733151da937be7fb844fafddd2480bf062f6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 22 Nov 2025 04:21:08 +0000 Subject: [PATCH 3/5] Update documentation with /system command Co-authored-by: QOAB <216093345+QOAB@users.noreply.github.com> --- QUICKSTART.md | 1 + README.md | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/QUICKSTART.md b/QUICKSTART.md index f77d4ff..d358ce0 100644 --- a/QUICKSTART.md +++ b/QUICKSTART.md @@ -114,6 +114,7 @@ docker-compose exec postgres psql -U postgres -d trading_bot -c "SELECT COUNT(*) /positions Show all open positions /update BTCUSDT Update positions (check SL/TP) /history Last 20 closed trades +/system Complete system information and stats ``` --- diff --git a/README.md b/README.md index 432e585..bd2c047 100644 --- a/README.md +++ b/README.md @@ -270,6 +270,13 @@ docker-compose down -v /history Показывает последние 20 закрытых сделок +/system +Показывает полную информацию о системе: +• Конфигурация и параметры стратегии +• Статистика торговли (win rate, profit factor) +• Состояние портфеля и биржи +• Доступные функции + /help Справка по всем командам ``` From d5faab332f4f6631f0443e6963b846d5029446e5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 22 Nov 2025 04:22:20 +0000 Subject: [PATCH 4/5] Add comprehensive system info feature documentation Co-authored-by: QOAB <216093345+QOAB@users.noreply.github.com> --- SYSTEM_INFO_FEATURE.md | 305 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 305 insertions(+) create mode 100644 SYSTEM_INFO_FEATURE.md diff --git a/SYSTEM_INFO_FEATURE.md b/SYSTEM_INFO_FEATURE.md new file mode 100644 index 0000000..e2408bb --- /dev/null +++ b/SYSTEM_INFO_FEATURE.md @@ -0,0 +1,305 @@ +# System Information Feature - "что мы имеем" (What We Have) + +## Overview + +This feature answers the question "что мы имеем" (Russian: "what we have") by providing a comprehensive system inventory and status through both REST API and Telegram bot interfaces. + +## Implementation Details + +### 1. REST API Endpoint + +**Endpoint:** `GET /api/v1/system-info` + +**Location:** `backend/api/routes.py` + +**Response Structure:** +```json +{ + "system": { + "name": "Swing Trend Trading Bot", + "version": "1.0.0", + "phase": "Phase 0 - Paper Trading", + "timestamp": "2025-11-22T12:00:00" + }, + "configuration": { + "initial_capital": 500.0, + "risk_per_trade": "2.0%", + "max_open_positions": 1, + "daily_loss_limit": "6.0%", + "timeframe": "1h", + "timezone": "Europe/Luxembourg" + }, + "strategy": { + "name": "Swing Trend Baseline", + "indicators": { + "ema_fast": 9, + "ema_slow": 21, + "rsi_length": 14, + "rsi_long_threshold": 50, + "rsi_short_threshold": 50, + "atr_length": 14, + "breakout_lookback": 40 + }, + "risk_management": { + "stop_loss": "Entry ± 2*ATR", + "take_profit": "Entry ± 2.5*(Entry-StopLoss)", + "risk_reward_ratio": 2.5 + } + }, + "portfolio": { + "equity": 500.00, + "available_capital": 500.00, + "open_positions": 0, + "positions_detail": [], + "total_pnl": 0.00, + "pnl_percentage": 0.00 + }, + "statistics": { + "total_trades": 0, + "closed_trades": 0, + "open_trades": 0, + "winning_trades": 0, + "losing_trades": 0, + "win_rate": 0.00, + "average_win": 0.00, + "average_loss": 0.00, + "profit_factor": 0.00, + "gross_profit": 0.00, + "gross_loss": 0.00 + }, + "exchange": { + "name": "Binance", + "mode": "testnet", + "status": "connected", + "btcusdt_price": 45000.00, + "api_configured": true + }, + "features": { + "api_endpoints": [ + "GET /api/v1/status - Portfolio status", + "GET /api/v1/signals/{symbol} - Check trading signal", + "POST /api/v1/execute/{symbol} - Open position", + "GET /api/v1/positions - List open positions", + "POST /api/v1/update-positions/{symbol} - Update positions", + "GET /api/v1/trades/history - Trade history", + "GET /api/v1/system-info - System information", + "GET /health - Health check" + ], + "telegram_commands": [ + "/start - Welcome message", + "/help - Command help", + "/status - Portfolio status", + "/signals BTCUSDT - Check signal", + "/execute BTCUSDT - Open position", + "/positions - List positions", + "/update BTCUSDT - Update positions", + "/history - Trade history", + "/system - System information" + ] + }, + "database": { + "connection": "PostgreSQL + TimescaleDB", + "tables": ["trades", "ohlcv", "portfolio_state"], + "total_records": 0 + } +} +``` + +### 2. Telegram Bot Command + +**Command:** `/system` + +**Location:** `bot/main.py` + +**Display Format:** +``` +🤖 Swing Trend Trading Bot +📌 Phase 0 - Paper Trading +🔖 Version: 1.0.0 + +⚙️ Конфигурация: +💰 Initial Capital: $500 +⚠️ Risk per Trade: 2.0% +📊 Max Positions: 1 +🛑 Daily Loss Limit: 6.0% +⏰ Timeframe: 1h + +📈 Стратегия: Swing Trend Baseline +• EMA Fast/Slow: 9/21 +• RSI: 14 +• ATR: 14 +• Breakout Lookback: 40 +• R:R Ratio: 2.5 + +💼 Портфель: +💰 Equity: $500.00 +💵 Available: $500.00 +📊 Open Positions: 0 +💸 Total P&L: $0.00 (0.00%) + +📊 Статистика: +📝 Total Trades: 0 +✅ Closed: 0 | 🔓 Open: 0 +🟢 Wins: 0 | 🔴 Losses: 0 +🎯 Win Rate: 0.0% +📈 Avg Win: $0.00 +📉 Avg Loss: $0.00 +💹 Profit Factor: 0.00 + +🔗 Exchange: +• Binance (TESTNET) +• Status: connected +• API Configured: ✅ +• BTC/USDT: $45000.00 + +💾 База данных: +• PostgreSQL + TimescaleDB +• Tables: 3 +• Records: 0 +``` + +## Changes Summary + +### Modified Files + +1. **backend/api/routes.py** (+140 lines) + - Added `get_system_info()` endpoint + - Comprehensive data aggregation from multiple sources + - Error handling and exception management + +2. **bot/main.py** (+78 lines) + - Added `system_command()` handler + - Updated `start_command()` to include `/system` + - Updated `help_command()` with `/system` documentation + - Registered handler in `main()` + +3. **README.md** (+5 lines) + - Added `/system` command description + - Documented command features + +4. **QUICKSTART.md** (+1 line) + - Added `/system` to command reference + +## Use Cases + +### 1. Quick System Overview +**User Action:** Send `/system` in Telegram or GET `/api/v1/system-info` +**Use Case:** Get a complete snapshot of system state, configuration, and performance + +### 2. Performance Monitoring +**User Action:** Check statistics section +**Use Case:** Monitor win rate, profit factor, and trading performance + +### 3. Configuration Verification +**User Action:** Check configuration section +**Use Case:** Verify risk management parameters and strategy settings + +### 4. System Health Check +**User Action:** Check exchange and database status +**Use Case:** Ensure all system components are operational + +### 5. Feature Discovery +**User Action:** Check features section +**Use Case:** Learn what API endpoints and commands are available + +## Testing + +### Manual Testing Steps + +1. **Start the system:** + ```bash + docker-compose up -d + ``` + +2. **Test REST API:** + ```bash + # Via curl + curl http://localhost:8000/api/v1/system-info | jq + + # Via browser + http://localhost:8000/docs + # Then test the /api/v1/system-info endpoint + ``` + +3. **Test Telegram Bot:** + - Open Telegram + - Find your bot + - Send `/system` + - Verify comprehensive output + +### Expected Behaviors + +- ✅ Endpoint returns JSON with all sections +- ✅ No errors when database is empty (0 trades) +- ✅ Exchange status reflects actual connection state +- ✅ Telegram command displays formatted, readable output +- ✅ Statistics calculate correctly (handle division by zero) +- ✅ All configuration values match .env settings + +## Benefits + +1. **Single Command Visibility:** Everything about the system in one command +2. **Troubleshooting:** Quick diagnosis of configuration issues +3. **Monitoring:** Real-time performance metrics +4. **Documentation:** Self-documenting available features +5. **Onboarding:** New users can understand system capabilities instantly + +## Future Enhancements + +Potential improvements for future iterations: + +1. **Caching:** Cache system info for 1 minute to reduce database queries +2. **Filtering:** Allow filtering sections (e.g., `/system --stats-only`) +3. **Export:** Add CSV/JSON export of statistics +4. **Comparison:** Compare current vs. initial state +5. **Alerts:** Notify when key metrics cross thresholds +6. **Historical:** Track system state changes over time + +## Architecture Notes + +### Data Sources + +- **Configuration:** `backend/config.py` (Settings class) +- **Portfolio:** `PaperTradingEngine.get_status()` +- **Statistics:** Database queries on `Trade` model +- **Exchange:** `MarketDataService.get_ticker()` +- **Features:** Hardcoded list (consider generating dynamically) + +### Performance Considerations + +- Database query fetches ALL trades (consider pagination for large datasets) +- Exchange ticker call may timeout if Binance is down +- Async operations ensure non-blocking behavior + +### Error Handling + +- Graceful degradation if exchange is disconnected +- Safe division (avoid division by zero in statistics) +- HTTPException with 500 status on unexpected errors + +## Maintenance + +### Updating Feature Lists + +When adding new endpoints or commands, update the hardcoded lists in: +- `backend/api/routes.py` line ~413 (api_endpoints list) +- `backend/api/routes.py` line ~423 (telegram_commands list) + +### Version Management + +Update version in: +- `backend/api/routes.py` line ~364 ("version": "1.0.0") +- `backend/main.py` line ~14 (FastAPI version) + +## Related Documentation + +- **API Documentation:** http://localhost:8000/docs (Swagger UI) +- **Project Summary:** `PROJECT_SUMMARY.md` +- **Quick Start:** `QUICKSTART.md` +- **README:** `README.md` + +--- + +**Last Updated:** November 22, 2025 +**Author:** Copilot SWE Agent +**Feature Status:** ✅ Implemented and Documented From aac64c4912db2edb6ecd4d57b12dc4072e5764c4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 22 Nov 2025 04:23:25 +0000 Subject: [PATCH 5/5] Fix code review issues: add pagination and specific exception handling Co-authored-by: QOAB <216093345+QOAB@users.noreply.github.com> --- backend/api/routes.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/backend/api/routes.py b/backend/api/routes.py index 216abfa..3078a6f 100644 --- a/backend/api/routes.py +++ b/backend/api/routes.py @@ -345,8 +345,12 @@ async def get_system_info(db: AsyncSession = Depends(get_db)): # Portfolio status portfolio = paper_engine.get_status() - # Database statistics - result = await db.execute(select(Trade)) + # Database statistics - limit to last 1000 trades for performance + result = await db.execute( + select(Trade) + .order_by(desc(Trade.opened_at)) + .limit(1000) + ) all_trades = result.scalars().all() closed_trades = [t for t in all_trades if t.status == "closed"] @@ -367,14 +371,18 @@ async def get_system_info(db: AsyncSession = Depends(get_db)): gross_loss = abs(sum(t.pnl for t in losing_trades)) profit_factor = gross_profit / gross_loss if gross_loss > 0 else 0 - # Exchange status + # Exchange status - handle specific exceptions try: ticker = await market_data.get_ticker("BTCUSDT") exchange_status = "connected" exchange_last_price = ticker.get("last", 0) - except Exception: + except (ConnectionError, TimeoutError) as e: exchange_status = "disconnected" exchange_last_price = 0 + except Exception as e: + # Log unexpected errors but don't fail the endpoint + exchange_status = "error" + exchange_last_price = 0 return { "system": {