Files
daily_stock_analysis/docs
sunkai174634 fb4735a105 feat: add Futu OpenD as optional HK realtime and fundamental data source (#2269)
* 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>
2026-08-25 22:54:01 +08:00
..
2026-07-19 16:32:55 +08:00

AI Stock Analysis System

GitHub stars CI License: MIT Python 3.10+ GitHub Actions Docker

#1 Python Repository Of The Day | Trendshift Featured|HelloGitHub

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

English | 简体中文 | 繁體中文

💖 Sponsors

Anspire Open all-in-one model and search service Easily scrape real-time financial news data from search engines - SerpApi

🖥️ Product Preview

DSA Web workspace demo

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

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

See Data Source Configuration.

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, skill naming compatibility, multi-agent mode, and budget guards are covered in the Full Guide and LLM Config Guide.

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

Email zhuls345@gmail.com
Project consulting, deployment support, and feature extensions
Xiaohongshu QR code
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.