Fixed AttributeError exceptions in /status and /positions commands:
- Replaced invalid risk.calculate_pnl() with inline P&L calculation from balance dict
- Changed risk.circuit_breaker_threshold to risk._cb_threshold
- Replaced balance.stocks access with account summary from output2 dict
- Updated tests to match new account summary format
All 27 telegram command tests pass. Live bot testing confirms no errors.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Remove /start command as name doesn't match functionality, and fix
command parsing to handle @botname suffix for group chat compatibility.
Changes:
- Remove handle_start function and registration
- Remove /start from help command list
- Remove test_start_command_content test
- Strip @botname suffix from commands (e.g., /help@mybot → help)
Rationale:
- /start command name implies bot initialization, but it was just
showing help text (duplicate of /help)
- Better to have one clear /help command
- @botname suffix handling needed for group chats
Test:
- 27 tests pass (1 removed, 1 added for @botname handling)
- All existing functionality preserved
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add real-time status and portfolio monitoring via Telegram.
Changes:
- Implement /status handler (mode, markets, P&L, trading state)
- Implement /positions handler (holdings with grouping by market)
- Integrate with Broker API and RiskManager
- Add 5 comprehensive tests for status commands
Features:
- /status: Shows trading mode, enabled markets, pause state, P&L, circuit breaker
- /positions: Lists holdings grouped by market (domestic/overseas)
- Error handling: Graceful degradation on API failures
- Empty state: Handles portfolios with no positions
Integration:
- Uses broker.get_balance() for account data
- Uses risk.calculate_pnl() for P&L calculation
- Accesses pause_trading.is_set() for trading state
- Groups positions by market for better readability
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add pause/resume functionality for remote trading control via Telegram.
Changes:
- Add pause_trading Event to main.py
- Implement /stop handler (pause trading)
- Implement /resume handler (resume trading)
- Integrate pause logic into both daily and realtime trading loops
- Add 4 comprehensive tests for trading control
Features:
- /stop: Pauses all trading operations
- /resume: Resumes trading operations
- Idempotent: Handles repeated stop/resume gracefully
- Status feedback: Informs if already paused/active
- Works in both daily and realtime trading modes
Security:
- Commands verified by TelegramCommandHandler chat_id check
- Only authorized users can control trading
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Root cause analysis revealed 3 critical issues causing EGW00201 errors:
1. **Hash key bypass** - _get_hash_key() made API calls without rate limiting
- Every order made 2 API calls but only 1 was rate-limited
- Fixed by adding rate_limiter.acquire() to _get_hash_key()
2. **Scanner concurrent burst** - scan_market() launched all stocks via asyncio.gather
- All tasks queued simultaneously creating burst pressure
- Fixed by adding Semaphore(1) for fully serialized scanning
3. **RPS too aggressive** - 5.0 RPS exceeded KIS API's real ~2 RPS limit
- Lowered to 2.0 RPS (500ms interval) for maximum safety
Changes:
- src/broker/kis_api.py: Add rate limiter to _get_hash_key()
- src/analysis/scanner.py: Add semaphore-based concurrency control
- New max_concurrent_scans parameter (default 1, fully serialized)
- Wrap scan_stock calls with semaphore in _bounded_scan()
- Remove ineffective asyncio.sleep(0.2) from scan_stock()
- src/config.py: Lower RATE_LIMIT_RPS from 5.0 to 2.0
- tests/test_broker.py: Add 2 tests for hash key rate limiting
- tests/test_volatility.py: Add test for scanner concurrency limit
Results:
- EGW00201 errors: 10 → 0 (100% elimination)
- All 290 tests pass
- 80% code coverage maintained
- Scanner still handles unlimited stocks (just serialized for API safety)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Adds telegram.close() to finally block to ensure aiohttp session cleanup.
Changes:
- src/main.py:553 - Add await telegram.close() in shutdown
Before:
- broker.close() called ✅
- telegram.close() NOT called ❌
- "Unclosed client session" error on shutdown
After:
- broker.close() called ✅
- telegram.close() called ✅
- Clean shutdown, no resource leak errors
Impact:
- Eliminates aiohttp resource leak warnings
- Proper cleanup of Telegram API connections
- No memory leaks in long-running processes
Related:
- KISBroker.close() already handles broker session
- OverseasBroker reuses KISBroker session (no separate close needed)
- TelegramClient has separate session that needs cleanup
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add safe_float() helper function to safely convert API response values
to float, handling empty strings, None, and invalid values that cause
ValueError: "could not convert string to float: ''".
Changes:
- Add safe_float() function in src/main.py with full docstring
- Replace all float() calls with safe_float() in trading_cycle()
- Domestic market: orderbook prices, balance amounts
- Overseas market: price data, balance info
- Add 6 comprehensive unit tests for safe_float()
The function handles:
- Empty strings ("") → default (0.0)
- None values → default (0.0)
- Invalid strings ("abc") → default (0.0)
- Valid strings ("123.45") → parsed float
- Float inputs (123.45) → pass through
This prevents crashes when KIS API returns empty strings during
market closed hours or data unavailability.
Fixes: #44
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add type checking for output2 response from get_overseas_balance API.
The API can return either list format [{}] or dict format {}, causing
KeyError when accessing output2[0].
Changes:
- Check isinstance before accessing output2[0]
- Handle list, dict, and empty cases
- Add safe fallback with "or" for empty strings
- Add 3 test cases for list/dict/empty formats
Fixes: #41
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Integrate Telegram notifications throughout the main trading loop to provide
real-time alerts for critical events and trading activities.
Changes:
- Add TelegramClient initialization in run() function
- Send system startup notification on agent start
- Send market open/close notifications when markets change state
- Send trade execution notifications for BUY/SELL orders
- Send fat finger rejection notifications when orders are blocked
- Send circuit breaker notifications when loss threshold is exceeded
- Pass telegram client to trading_cycle() function
- Add tests for all notification scenarios in test_main.py
All notifications wrapped in try/except to ensure trading continues even
if Telegram API fails. Notifications are non-blocking and do not affect
core trading logic.
Test coverage: 273 tests passed, overall coverage 79%
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implement comprehensive multi-market trading system with automatic
market selection based on timezone and trading hours.
## New Features
- Market schedule module with 10 global markets (KR, US, JP, HK, CN, VN)
- Overseas broker for KIS API international stock trading
- Automatic market detection based on current time and timezone
- Next market open waiting logic when all markets closed
- ConnectionError retry with exponential backoff (max 3 attempts)
## Architecture Changes
- Market-aware trading cycle with domestic/overseas broker routing
- Market context in AI prompts for better decision making
- Database schema extended with market and exchange_code columns
- Config setting ENABLED_MARKETS for market selection
## Testing
- 19 new tests for market schedule (timezone, DST, lunch breaks)
- All 54 tests passing
- Lint fixes with ruff
## Files Added
- src/markets/schedule.py - Market schedule and timezone logic
- src/broker/overseas.py - KIS overseas stock API client
- tests/test_market_schedule.py - Market schedule test suite
## Files Modified
- src/main.py - Multi-market main loop with retry logic
- src/config.py - ENABLED_MARKETS setting
- src/db.py - market/exchange_code columns with migration
- src/brain/gemini_client.py - Dynamic market context in prompts
Resolves#5
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>