* feat: add Futu OpenD as an optional HK realtime and fundamental data source Add FutuFetcher and FutuFundamentalAdapter behind FUTU_OPEND_HOST/PORT, register the settings in Config and config_registry so the Web settings page can expose host, port and HK realtime priority, and route HK realtime quotes through a configurable futu/longbridge/akshare/yfinance order while keeping A-share priority untouched. Include offline tests for the adapter, config schema and HK routing/fallback, plus docs and CHANGELOG entries. * fix: wire Futu fundamentals into HK pipeline and restore quote supplementation - _fetch_offshore_fundamental_bundle() prefers the Futu fundamental adapter for HK when FUTU_OPEND_HOST is configured, and falls back to yfinance when Futu is absent or returns no usable content. - HK realtime priority loop now supplements missing quote fields (volume_ratio / turnover_rate / pe/pb / market cap) from later configured sources instead of returning after the first non-empty quote, matching the US path's _supplement_quote behavior. - capital_flow / boards blocks are filled from the Futu bundle for HK instead of being hard-coded not_supported; status and missing_fields aggregation updated accordingly. - Add regression tests for partial-quote supplementation and Futu fundamental bundle routing/fallback. * test: expect boards block ok when bundle provides belong_boards The Futu integration made the offshore boards block data-driven instead of hard-coded not_supported; update the existing US/HK fundamental context test to match (belong_boards from the bundle now surface as an ok boards block). * fix: preserve HK fallback_from metadata and normalize Futu quote timestamps - HK realtime priority loop now records the failed preferred source token and passes it as fallback_from when a later source takes over, so the pipeline and analysis context can mark the quote as degraded. - Futu snapshot update_time is a naive Beijing-time (UTC+8) string; attach the +08:00 offset before storing provider_timestamp so stale_seconds / is_stale / provider_timestamp freshness semantics are correct instead of being parsed as UTC. - Add regression tests for fallback_from propagation and timestamp normalization. * fix: normalize Futu belong_boards to name/type/code contract OpenD owner_plate returns plate_code / plate_name / plate_type, but DSA downstream consumers (notification, extract_board_detail_fields, market structure) only read name/type/code. Map the fields in FutuFundamentalAdapter._boards so HK Futu boards are actually consumed instead of silently dropped, and add regression tests including an end-to-end check through extract_board_detail_fields. * fix: merge yfinance bundle when Futu fundamental returns partial blocks Futu partial success (e.g. statements failed but static info worked) used to short-circuit the whole bundle, silently dropping the growth/earnings that the existing yfinance path could still provide. Now, when Futu returns content but is missing growth or earnings, fetch the yfinance bundle within the remaining budget and merge the missing blocks (growth/earnings/institution/capital_flow/belong_boards), keeping Futu-preferred values where both exist. Add regression test for the partial-success merge path. * fix: use field-level checks when deciding Futu-vs-yfinance growth/earnings The previous merge condition only checked dict truthiness, so a truthy growth/earnings shell (all-None core values or metadata-only keys such as report_date/period/currency) would skip the yfinance supplement and silently downgrade existing HK fundamentals. Add _earnings_block_has_values (a core numeric field or a populated dividend is required) and reuse the existing _has_meaningful_payload for growth; both the missing_core check and the merge loop now use these. Add regression test for the all-None-shell scenario. * fix: fill HK fundamental field gaps from yfinance instead of block-level checks Block-level meaningful checks still skipped the yfinance supplement when Futu hit only part of the growth/earnings fields (e.g. revenue_yoy but None net_profit_yoy, or earnings with only basic_eps), silently dropping fields the main branch used to provide. Replace the missing_core decision with a per-field gap list (growth: revenue_yoy/net_profit_yoy/gross_margin; earnings.financial_report: revenue/net_profit_parent/basic_eps/gross_profit) and make the merge field-level: keep Futu values, fill each missing field from yfinance. Add regression tests for partial-hit and all-None shells. * fix: normalize Futu dividends to the repo contract and treat dividend gaps as supplement triggers Futu OpenD dividend_list carries raw fields (statement/ex_date/record_date) which the notification/data_processing market-structure consumers do not read; the repo contract is ttm_cash_dividend_per_share, ttm_dividend_yield_pct and events[].cash_dividend_per_share / ex_dividend_date / event_date. Normalize events in FutuFundamentalAdapter._dividends_and_splits, compute TTM count/cash and yield from the latest quote, and teach _field_gaps/_merge_bundles to treat a dividend block that does not satisfy the contract as a gap so yfinance supplements it. Also dedupe FUTU_OPEND_HOST/PORT in full-guide_EN. * fix: read dividend yield price from UnifiedRealtimeQuote objects FutuFetcher.get_realtime_quote returns a UnifiedRealtimeQuote dataclass, not a dict, so the yield branch in _dividends_and_splits that guarded on isinstance(quote, dict) never ran on the live Futu path, silently dropping ttm_dividend_yield_pct while the contract check considered the dividend block complete. Read price via getattr(quote, 'price', None) and keep the dict fallback for other fetchers; add a regression test driving the real UnifiedRealtimeQuote shape. * fix: treat dividend blocks with TTM cash but no yield as supplement gaps The repo contract consumes ttm_cash_dividend_per_share and ttm_dividend_yield_pct together. When the Futu dividend path has events and TTM cash but the extra realtime price snapshot failed (quote None / no price), ttm_dividend_yield_pct cannot be computed and the block was previously treated as complete, so yfinance was never consulted and the notification rendered the yield as N/A. _dividend_contract_has_values() now requires the paired yield whenever TTM cash is present, so _field_gaps() triggers the yfinance supplement and _merge_bundles() replaces the incomplete dividend block. Add regression tests for the adapter-level gap shape (quote unavailable leaves no yield) and the manager-level supplement path (Futu cash without yield pulls yfinance and fills the yield). * fix: skip unconfigured Futu in HK realtime routing When FUTU_OPEND_HOST is not configured, the HK realtime priority loop used to still attempt the futu source, record it as the failed primary, and attach fallback_from='futu' to a successful quote from the next enabled source (longbridge/akshare/yfinance). Consumers then wrongly treated an enabled source's first success as degraded fallback data, contradicting the documented contract that Futu only participates when OpenD is configured. The HK loop now checks FutuFetcher.has_configured_endpoint() once and skips the futu token entirely when it is disabled, so no fallback_from is written. Existing configured-Futu routing tests explicitly patch the endpoint check; a new regression test asserts an unconfigured Futu is never called and the enriched quote carries fallback_from=None. * fix: release cached HK Futu fundamental fetcher in DataFetcherManager.close() The HK Futu fundamental path lazily creates and caches its own FutuFetcher (an OpenQuoteContext-backed OpenD connection) on _futu_fundamental_fetcher, but close() only released the TickFlow fetcher and the default fetchers snapshot. Explicit close / reload paths therefore left the OpenD connection hanging. close() now takes the cached _futu_fundamental_fetcher, clears the reference and calls its close() best-effort. A regression test injects an observable fetcher into _futu_fundamental_fetcher and asserts close() invokes it and clears the attribute. --------- Co-authored-by: BayMax local review <baymax-local@invalid>
AI Stock Analysis System
AI-powered stock analysis system for A-shares / Hong Kong / US / Japanese / Korean / Taiwan stocks
Analyze your watchlist daily -> generate a decision dashboard -> push to Telegram / Discord / Slack / Email / WeChat Work / Feishu.
Product Preview · Key Features · Quick Start · Sample Output · Documentation Index · Full Guide
💖 Sponsors
🖥️ Product Preview
✨ Key Features
| Capability | Coverage |
|---|---|
| AI decision reports | Core conclusion, score, trend, entry/exit levels, risk alerts, catalysts, and action checklist |
| Multi-market data | Covers A-shares, Hong Kong, US, Japanese, Korean, Taiwan stocks, and ETFs, with quotes, K-lines, technical indicators, news, announcements, fundamentals, and report context. Data-source coverage and market boundaries are documented in market boundaries |
| Web / desktop workspace | Manual analysis, task progress, history, full Markdown reports, backtest, portfolio, settings, and light/dark themes |
| Agent strategy chat | Multi-turn Q&A with 15 built-in strategies across Web/Bot/API |
| Smart import & autocomplete | Image, CSV/Excel, clipboard import; code/name/pinyin/alias autocomplete |
| Automation & notifications | GitHub Actions, Docker, local scheduler, FastAPI service, and WeChat Work / Feishu / Telegram / Discord / Slack / Email delivery |
Detailed fields, fundamental P0 timeout semantics, trading rules, data-source priority, Web/API behavior, and troubleshooting live in the Full Guide.
Tech Stack & Data Sources
| Type | Supported |
|---|---|
| AI Models | Anspire, AIHubMix, Gemini, OpenAI-compatible providers, DeepSeek, Qwen, Claude, Ollama |
| Market Data | TickFlow, AkShare, Tushare, Pytdx, Baostock, YFinance, Longbridge |
| News Search | Anspire, SerpAPI, Tavily, Bocha, Brave, MiniMax, SearXNG |
| Social Sentiment | Stock Sentiment API for Reddit / X / Polymarket, US stocks only |
The project includes free market-data sources such as AkShare, Baostock, and YFinance and can run without extra data-source credentials. These free sources can be rate-limited, change upstream contracts, or fluctuate by network condition, so stability is not guaranteed. For scheduled runs, batch analysis, or steadier quotes, configure token-based sources such as TickFlow, Tushare, or Longbridge; market coverage, Actions mappings, and fallback rules are documented in Data Source Configuration.
🚀 Quick Start
Option 1: GitHub Actions (Recommended)
Deploy in about 5 minutes, with no server and no infrastructure cost.
1. Fork this repository
Click Fork in the upper-right corner. A star is very welcome if this project helps you.
2. Configure Secrets
Open your forked repository, then go to Settings -> Secrets and variables -> Actions -> New repository secret.
AI model configuration (configure at least one)
Start with one provider and one API key. For multi-model routing, image recognition, local models, or advanced routing, see the LLM Config Guide.
| Secret Name | Description | Required |
|---|---|---|
ANSPIRE_API_KEYS |
Anspire API key, one key for popular LLMs and web search with free quota for this project | Recommended |
AIHUBMIX_KEY |
AIHubMix API key, one key for multiple model families and a 10% top-up discount for this project | Recommended |
GEMINI_API_KEY |
Google Gemini API key | Optional |
ANTHROPIC_API_KEY |
Anthropic Claude API key | Optional |
OPENAI_API_KEY |
OpenAI-compatible API key, including DeepSeek and Qwen-compatible services | Optional |
OPENAI_BASE_URL / OPENAI_MODEL |
Fill these when using an OpenAI-compatible provider | Optional |
Ollama is better suited for local or Docker deployment. GitHub Actions is usually smoother with a cloud API.
Notification channels (configure at least one)
| Secret Name | Description |
|---|---|
WECHAT_WEBHOOK_URL |
WeChat Work bot |
FEISHU_WEBHOOK_URL |
Feishu bot |
TELEGRAM_BOT_TOKEN + TELEGRAM_CHAT_ID |
Telegram |
DISCORD_WEBHOOK_URL |
Discord webhook |
SLACK_BOT_TOKEN + SLACK_CHANNEL_ID |
Slack bot |
EMAIL_SENDER + EMAIL_PASSWORD |
Email push |
More channels, signatures, email groups, and Markdown-to-image settings are in Notification Configuration.
Watchlist (required)
| Secret Name | Description | Required |
|---|---|---|
STOCK_LIST |
Watchlist codes, such as 600519,hk00700,AAPL,7203.T,005930.KS,2330.TW |
✅ |
News sources (recommended)
News search strongly improves sentiment, announcements, events, and catalyst quality. Configure at least one search provider if possible.
| Secret Name | Description | Required |
|---|---|---|
ANSPIRE_API_KEYS |
Anspire AI Search, optimized for Chinese content and A-share analysis; the same key can also be used for Anspire LLM fallback examples | Recommended |
SERPAPI_API_KEYS |
SerpAPI, search-engine results for realtime financial news | Recommended |
TAVILY_API_KEYS |
Tavily, general news search API | Optional |
BOCHA_API_KEYS |
Bocha, Chinese search with AI summaries | Optional |
BRAVE_API_KEYS |
Brave Search, privacy-first search and US-stock news enrichment | Optional |
MINIMAX_API_KEYS |
MiniMax, structured search results | Optional |
SEARXNG_BASE_URLS |
Self-hosted SearXNG instances for quota-free fallback | Optional |
More search providers, social sentiment, and fallback behavior are in Search Configuration.
Market data sources (optional)
Free sources like AkShare, Baostock, and YFinance are used by default. "Not configured" messages in the logs are informational and do not affect execution. For more stable data, configure the following secrets per market:
| Secret Name | Market | Description |
|---|---|---|
TUSHARE_TOKEN |
A-shares | Improves historical data stability |
LONGBRIDGE_OAUTH_CLIENT_ID + LONGBRIDGE_OAUTH_TOKEN_CACHE_B64 |
HK/US stocks | Fills in volume ratio, turnover rate, P/E, and other fields |
3. Enable Actions
Open the Actions tab and click I understand my workflows, go ahead and enable them.
4. Manual Test
Actions -> Daily Stock Analysis -> Run workflow -> Run workflow.
Done
By default, the workflow runs every weekday at 18:00 Beijing time and skips non-trading days. Forced runs, trading-day checks, and resume rules are covered in the Full Guide.
Option 2: Local / Docker Deployment
# Clone the project
git clone https://github.com/ZhuLinsen/daily_stock_analysis.git && cd daily_stock_analysis
# Install dependencies
pip install -r requirements.txt
# Configure environment variables
cp .env.example .env && vim .env
# Run analysis
python main.py
Common commands:
python main.py --debug
python main.py --dry-run
python main.py --stocks 600519,hk00700,AAPL,2330.TW
python main.py --market-review
python main.py --schedule
python main.py --serve-only
Docker deployment, scheduling, and cloud-server WebUI access are documented in the Full Guide.
📱 Sample Output
Decision Dashboard
🎯 2026-02-08 Decision Dashboard
Analyzed 3 stocks | 🟢 Buy:0 🟡 Watch:2 🔴 Sell:1
📊 Summary
🟡 000657: Watch | Score 65 | Bullish
🟡 600105: Watch | Score 48 | Range-bound
🔴 300260: Sell | Score 35 | Bearish
🚨 Risk Alerts:
Risk 1: Main-force funds showed notable outflow.
Risk 2: Chip concentration suggests short-term resistance.
✨ Positive Catalysts:
Catalyst 1: AI-server supply-chain exposure remains a market focus.
Catalyst 2: Recent earnings growth provides fundamental support.
Market Review
🎯 2026-01-10 Market Review
📊 Major Indices
- SSE Composite: 3250.12 (+0.85%)
- SZSE Component: 10521.36 (+1.02%)
- ChiNext: 2156.78 (+1.35%)
📈 Market Breadth
Up: 3920 | Down: 1349 | Limit up: 155 | Limit down: 3
⚙️ Configuration
Full environment variables, model routing, notification channels, data-source priority, trading rules, fundamental P0 semantics, and deployment details are in the Full Guide.
🖥️ Web UI
The Web workspace supports settings, task monitoring, manual analysis, history reports, full Markdown reports, Agent strategy chat, backtest, portfolio management, smart import, and light/dark themes.
python main.py --webui
python main.py --webui-only
Visit http://127.0.0.1:8000. Authentication, smart import, autocomplete, report copying, and cloud-server access are documented in Local WebUI Management.
🤖 Agent Strategy Chat
After configuring any available AI API key, the Web /chat page can use strategy chat. Set AGENT_MODE=false only if you want to disable it explicitly.
- Built-in strategies include moving-average crossovers, Chan theory, Elliott wave, bull trend, hot themes, event-driven, growth quality, expectation repricing, and more
- Calls realtime quotes, K-line data, technical indicators, news, and risk context
- Supports follow-up questions, session export, notification sending, and background execution
- Supports custom strategy files and experimental multi-agent orchestration
Agent parameters,
skillnaming compatibility, multi-agent mode, and budget guards are covered in the Full Guide and LLM Config Guide.
🧩 Related Projects
DSA focuses on daily analysis reports. Its screening implementation references AlphaSift, while AlphaEvo covers strategy validation and evolution.
| Project | Focus |
|---|---|
| AlphaSift | Reference project for DSA's screening implementation |
| AlphaEvo | Strategy backtesting and self-evolution experiments for validating rules and iteratively exploring strategy parameters and combinations |
📞 Contact
|
zhuls345@gmail.com Project consulting, deployment support, and feature extensions |
![]() Follow on Xiaohongshu |
|
| Xiaohongshu | Follow on Xiaohongshu | |
| Feedback | GitHub Issues · Discussions |
📄 License
MIT License © 2026 ZhuLinsen
If you use or build on this project, attribution with a link back to this repository is appreciated.
⚠️ Disclaimer
This project is for informational and educational purposes only. AI-generated analysis is not investment advice. Stock market investing involves risk; do your own research and consult a licensed financial advisor when needed.



