- EfinanceFetcher: switch ETF from ef.fund.get_quote_history (NAV only)
to ef.stock.get_quote_history (full OHLCV) so ETFs get proper
open/high/low/close/volume/amount data instead of zeros
- Remove obsolete NAV column mappings from _normalize_data
- Pin tiktoken>=0.8.0,<0.12.0 to avoid 'Unknown encoding cl100k_base'
plugin registration issue in tiktoken 0.12.0
Fixes#541Fixes#527Fixes#537
* feat: multi-channel LLM support with visual channel editor
- Add three-tier LLM config: LITELLM_CONFIG (YAML) > LLM_CHANNELS (env) > legacy keys
- Each channel gets independent base_url / api_key / models (no OPENAI_BASE_URL conflict)
- Support DEEPSEEK_API_KEY as standalone provider (auto-infers deepseek-chat model)
- Add LLMChannelEditor component with 9 presets (AIHubmix/DeepSeek/Dashscope/GLM/Moonshot/SiliconFlow/OpenRouter/Gemini/Custom)
- Expand config_registry with ~50 new fields for web settings coverage
- Rewrite .env.example AI section with clear quick-start guide (Scenario A vs B)
- Add litellm_config.example.yaml template
- Add PyYAML dependency for YAML config support
- Full backward compatibility: existing single-key configs work unchanged
* chore: replace placeholder key values with empty defaults in .env.example
* fix: resolve ESLint errors in LLMChannelEditor and HomePage
* refactor: use native litellm deepseek/ provider and extract shared LLM helpers
- Replace openai/deepseek-* + manual api_base with deepseek/ prefix (litellm
natively resolves DEEPSEEK_API_KEY and base_url)
- Extract duplicated _get_api_keys_for_model and _extra_litellm_params from
analyzer.py and llm_adapter.py into shared functions in config.py
- Update litellm_config.example.yaml to use deepseek/ prefix
- Thinking mode (deepseek-chat opt-in, deepseek-reasoner auto) unaffected:
get_thinking_extra_body uses model short name stripped of provider prefix
* fix: migrate _call_gemini to google-genai SDK for thought_signature support (Fixes#440, Refs #421)
- Replace deprecated google-generativeai with google-genai in llm_adapter.py
- Add base64 round-trip for thought_signature (bytes <-> str) for Gemini 3 multi-turn tool calls
- Fix to_gemini_declaration() to use JSON Schema lowercase types (object/string) for parameters_json_schema
- Add base64 decode error handling for robustness
- Add google-genai>=1.0.0 to requirements.txt; keep google-generativeai for analyzer/image_stock_extractor
* test: update test_gemini_declaration for JSON Schema lowercase types
* fix: use role="tool" for function response per Gemini API docs (review feedback)
- Add trading_calendar module with exchange-calendars for A/HK/US markets
- Per-stock filtering: analyze only stocks whose markets are open today
- Add TRADING_DAY_CHECK_ENABLED config and --force-run CLI override
- Add is_hk_stock_code to data_provider public API
- Update CHANGELOG, full-guide, README, .env.example
* feat: Add image stock code extraction, Anthropic API, and Pytdx config (Fixes#257)
- Add POST /api/v1/stocks/extract-from-image with Vision LLM (Gemini/Anthropic/OpenAI)
- Add image_stock_extractor service with 60s timeout, rate limit (10/min), X-Forwarded-For support
- Add ImageStockExtractor UI in Settings for upload and merge-to-watchlist
- Add ANTHROPIC_API_KEY, ANTHROPIC_MODEL to config and analyzer fallback chain
- Add PYTDX_HOST, PYTDX_PORT, PYTDX_SERVERS for custom Pytdx server
- Add OPENAI_VISION_MODEL for image-only models
- Handle 409 conflict in merge flow with user-friendly message
* fix: resolve TypeScript build error in stocks.ts (avoid Record<> generic parsing)
* feat: Redis rate limiter, API field unification, anthropic_max_tokens
- Add rate_limiter.py: Memory + Redis implementations for extract-from-image
- Integrate get_extract_rate_limiter() in stocks.py; remove image param, keep file only
- Add anthropic_max_tokens config for analyzer
- Update .env.example, config_registry, full-guide, CHANGELOG
- Add redis>=5.0 as optional dependency
* refactor: rename GeminiAnalyzer to LLMAnalyzer for multi-backend clarity
* revert: restore GeminiAnalyzer naming (keep original upstream convention)
* docs: add image extraction and from-image-add usage to README
* fix: Anthropic runtime fallback to OpenAI; remove Redis rate limiter
- Init both Anthropic and OpenAI at startup so Anthropic 429/network glitch can fall back to OpenAI
- Check Anthropic before OpenAI in _call_api_with_retry to preserve priority
- Remove Redis rate limiter per review; keep in-memory only
* remove rate limiter for extract-from-image per review
- Delete rate_limiter.py; extract-from-image has no rate limiting
- Remove Request param and 429 response from stocks endpoint
- Update docs to drop rate limit mentions
- requirements: Added `json-repair` dependency for JSON validation and repair.
- analyzer: Utilized `repair_json` function to ensure JSON strings are correctly formatted before processing.
This enhancement improves the robustness of JSON handling in the analysis workflow.
- Add triple-source fallback for A-share data (EM → Sina → Tencent)
- Add multi-source fallback for chip distribution (Akshare → Tushare → Efinance)
- Fix efinance ETF API parameter errors and OHLC data completion
- Fix tushare index realtime quote code format (sh/sz prefix)
- Fix stock name display issue showing code instead of name
- Upgrade SerpAPI to Google search with Knowledge Graph support
Fixes#206, #171, #139
Related #207
#minor
Resolve conflicts in config.py and notification.py:
- config.py: Merge realtime quote configuration and Discord bot status
- notification.py: Add markdown2 import and enhance single stock report with risk alerts, catalysts, and operation points