1. Mass position deletion (portfolio.py): remove_position now rejects
empty position_id — previously position_id="" matched all positions
and deleted every holding for a ticker across ALL accounts.
2. Path traversal in get_recommendation (portfolio.py): added ticker/date
validation (no ".." or path separators) + resolved-path check against
RECOMMENDATIONS_DIR to prevent ../../etc/passwd attacks.
3. Path traversal in get_report_content (main.py): same ticker/date
validation + resolved-path check against get_results_dir().
4. china_data import stub (interface.py + new china_data.py): the actual
akshare implementation lives in web_dashboard/backend/china_data.py
(different package); tradingagents/dataflows/china_data.py was missing
entirely, so _china_data_available was always False. Added stub file
and AttributeError to the import exception handler so the module
gracefully degrades instead of silently hiding the missing vendor.
Magic numbers also extracted to named constants:
- MAX_RETRY_COUNT, RETRY_BASE_DELAY_SECS (main.py)
- MAX_CONCURRENT_YFINANCE_REQUESTS (portfolio.py)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- PDF: try multiple DejaVu font paths (macOS + Linux) instead of hardcoded macOS
- Frontend: add 15s AbortController timeout to all API calls + proper error handling
- yfinance: cap concurrent price fetches at 5 via asyncio.Semaphore
- Batch analysis: retry failed stock analyses up to 2x with exponential backoff
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1. main.py: move API key validation before task state creation —
prevents phantom "running" tasks when ANTHROPIC_API_KEY is missing
2. portfolio.py: make get_positions() async and fetch yfinance prices
concurrently via run_in_executor — no longer blocks event loop
3. portfolio.py: add fcntl.LOCK_EX around all JSON read-modify-write
operations on watchlist.json and positions.json — eliminates TOCTOU
lost-write races under concurrent requests
4. main.py: use tempfile.mkstemp with mode 0o600 instead of world-
readable /tmp/analysis_{task_id}.py — script content no longer
exposed to other users on shared hosts
Also: remove unused UploadFile/File imports, undefined _save_to_cache
function, dead code in _delete_task_status, and unused
get_or_create_default_account helper.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New backend:
- api/portfolio.py: watchlist CRUD, positions with live P&L, recommendations
- POST /api/portfolio/analyze: batch analysis of watchlist tickers
- GET /api/portfolio/positions: live price from yfinance + unrealized P&L
New frontend:
- PortfolioPanel.jsx with 3 tabs: 自选股 / 持仓 / 今日建议
- portfolioApi.js service
- Route /portfolio (keyboard shortcut: 5)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>