* fix: harden agent runtime and config handling
* fix: restore cors test import and update review status
* fix: close remaining agent timeout gaps
* fix: align agent timeout defaults and fallback budget
* docs: add Ollama local model configuration guide (Fixes#690)
- Add Example 4 and channel mode example in LLM_CONFIG_GUIDE
- Add OLLAMA_API_BASE to .env.example and full-guide
- Add Ollama entry to litellm_config.example.yaml
- Add Q12b to FAQ (CN/EN)
- Add troubleshooting row for Ollama 404 / api/generate/api/show
- Update CHANGELOG
Made-with: Cursor
* docs: sync README with Ollama config per AGENTS.md (address review)
- Add Ollama to AI models list in README.md
- Add OLLAMA_API_BASE to Secrets table with link to config guide
- Add pitfall note: do not use OPENAI_BASE_URL for Ollama
- Sync docs/README_EN.md with same changes
- Update CHANGELOG to mention README sync
Made-with: Cursor
* docs: add OLLAMA_API_BASE to full-guide env table and note (address review)
* docs: remove duplicate OLLAMA_API_BASE entries in full-guide (address review)
* fix#692: decouple agent and analysis primary model selection
* fix#692: preserve yaml alias for agent primary model
* docs #692: sync agent primary model in EN/CHT readmes
Integrate real-time social media sentiment data from api.adanos.org as an
additional intelligence source for US stock analysis. When configured, the
system fetches Reddit community sentiment, X/Twitter buzz, and Polymarket
prediction market data — and injects it alongside news context into the
LLM analysis prompt.
- New SocialSentimentService with retry, TTL caching, and graceful fallback
- Only activates for US stock tickers (A-shares/HK stocks are unaffected)
- Fully optional: requires SOCIAL_SENTIMENT_API_KEY env var
- Works in both standard and Agent analysis pipelines (via news_context)
- 22 unit tests covering all code paths including zero-value edge cases
- Updated README.md and docs/CHANGELOG.md
Co-authored-by: mumu <42829555+ZhuLinsen@users.noreply.github.com>
* fix: runtime robustness — efinance timeout, type-safe integrity, report save decoupling
- efinance_fetcher: wrap all efinance API calls with _ef_call_with_timeout()
to prevent indefinite hangs when eastmoney hosts are unreachable (was 81min+)
- analyzer: make check_content_integrity() type-safe when LLM returns dict
for operation_advice or string for core_conclusion/intelligence/battle_plan
- pipeline: decouple report saving from notification flag (--no-notify no longer
skips local report generation); normalize operation_advice dict to string
- backtest_engine: handle operation_advice as dict in _compute_advice_breakdown
- runner: guard response.usage against None to prevent AttributeError
- orchestrator: log non-critical stage failures (intel/risk) instead of silent skip
* fix: address PR #660 review — timeout shutdown, env guard, decision_type normalize, non-str advice
- efinance_fetcher: _ef_call_with_timeout() now uses explicit executor.shutdown(
wait=False) to avoid re-blocking on hung thread after FuturesTimeoutError
- efinance_fetcher: guard _EF_CALL_TIMEOUT parsing with try/except to prevent
module load failure on invalid EFINANCE_CALL_TIMEOUT env value
- pipeline: normalize decision_type with strip/lower before operation_advice
dict lookup so 'BUY'/'Buy' variants map correctly instead of falling to default
- analyzer: treat non-str operation_advice/analysis_summary as missing to
prevent downstream TypeError (e.g. get_emoji() with unhashable dict)
- .env.example: add EFINANCE_CALL_TIMEOUT config entry
* feat(llm): multi-channel LLM config with WebUI editor, protocol/model normalization, and connectivity test
- Add env-based multi-channel LLM configuration (LLM_CHANNELS, per-channel
PROTOCOL/BASE_URL/API_KEY/MODELS/ENABLED) with automatic protocol inference
and model name normalization for LiteLLM Router
- Add full-featured LLM Channel Editor in WebUI with inline channel
management, model selection, temperature control, and one-click
connectivity testing per channel
- Add POST /api/v1/system-config/test-llm-channel endpoint for real-time
LLM channel validation with latency reporting
- Add comprehensive validation for channel definitions, model references,
and runtime selection in SystemConfigService
- Add unified LLM_TEMPERATURE with backward-compatible fallback to legacy
provider-specific temperature vars (GEMINI/OPENAI/ANTHROPIC_TEMPERATURE)
- Default localhost channels to openai protocol (not ollama) for
compatibility with vLLM/LM Studio/LocalAI; Ollama requires explicit
PROTOCOL=ollama
- Allow direct-env LiteLLM providers (groq/*, bedrock/*, etc.) to bypass
channel-only model validation on both backend and frontend
- Skip channel validation when LITELLM_CONFIG YAML is the active model source
- Normalize slash-prefixed model IDs (e.g. Qwen/Qwen3-8B on SiliconFlow)
consistently between frontend and backend
- Prevent WebUI from resetting unsaved channel edits when unrelated AI
settings in the same category are modified
- Protect against invalid temperature env values with try/except fallback
- Add regression tests for channel parsing, temperature fallback, protocol
inference, YAML priority, and direct-env provider passthrough
Refs #544, Refs #507
* fix: resolve CI test failures and address PR review feedback
- Fix 3 failing validate_structured tests by aligning litellm_model with model lists
- Add direct-env provider bypass for primary and fallback model validation
- Add ValueError/TypeError -> 422 handling in test-llm-channel endpoint
- Add Space key preventDefault for accessible channel header toggle
- Canonicalize alias prefixes in normalizeModelForRuntime to prevent double-prefixing
* fix(ui): align temperature resolver and API_KEYS priority with backend
- Replace hard-coded temperature fallback chain with resolveTemperatureFromItems()
that mirrors backend resolve_unified_llm_temperature: reads LLM_TEMPERATURE first,
then the active model's provider-specific env, then any legacy *_TEMPERATURE
- Swap LLM_{NAME}_API_KEYS / LLM_{NAME}_API_KEY hydration order so multi-key
configs (rotation/load-balancing) are not silently dropped on save
* fix(security): SSRF guard on base_url + validate parsed API key segments
- Add _is_safe_base_url() to block link-local/cloud metadata addresses
(169.254.0.0/16, metadata.google.internal, 100.100.100.200) while
preserving legitimate localhost/LAN endpoints like Ollama
- Apply ssrf_blocked validation error in _validate_llm_channel_definition
- Replace api_key_value truthy-string check with parsed-segment check so
',' / ' , ' inputs are caught as missing_api_key instead of silently
producing zero-key channels at runtime
- Add regression tests: rejects_comma_only_api_key (3 subtests),
rejects_ssrf_metadata_base_url (3 subtests), allows_localhost_base_url
* fix: direct-env models bypass Router when channel mode is active
In channel/YAML mode, Router.completion() is called for all models but
the Router model_list only contains channel/YAML-declared entries. Direct-
env providers (groq/, bedrock/, etc.) are never added to it, so they caused
'model not found' errors at inference time even though validate_structured()
correctly exempted them from membership checks.
Fix: before dispatching to the Router, check whether the model is actually
in the Router model_list (via get_configured_llm_models). Models absent from
the list fall through to litellm.completion() with env-based credentials,
matching the existing legacy-path behavior. Applied consistently in both
src/analyzer.py and src/agent/llm_adapter.py.
* fix: add missing get_configured_llm_models import in analyzer.py
* 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
- Add prepare_webui_frontend_assets() to main.py: auto-runs npm install && npm
run build in apps/dsa-web before starting FastAPI server
- New env var WEBUI_AUTO_BUILD (default: true) to control auto-build behavior
- Replace bare JSON root response with styled HTML guide page when frontend
static files are not present (api/app.py)
- Remove unused RootResponse import from api/app.py
- Update README.md startup instructions and .env.example documentation
- Add changelog entry in docs/CHANGELOG.md
#patch
* fix(agent): include framework category in skill instructions rendering
`get_skill_instructions()` only iterated over ["trend", "pattern",
"reversal"] categories, silently dropping all strategies with
`category: framework` (box_oscillation, chan_theory, wave_theory,
emotion_cycle). Add "framework" to the category map and use a
dynamic fallback so future custom categories are never lost.
Also:
- Document `AGENT_SKILLS=all` shorthand in .env.example
- Add `AGENT_SKILLS` config to README
- Add CHANGELOG entry
- Update strategies/README.md and Skill docstring with framework category
Fixes#403
* fix(docker): include strategies directory in Docker image and compose
Dockerfile was missing `COPY strategies/ ./strategies/`, and
docker-compose.yml did not mount the strategies directory, causing
all 11 built-in strategies to fail loading in containerized deployments.
---------
Co-authored-by: ma_k <ma_k@ctrip.com>
Add PUSHPLUS_TOPIC config for PushPlus one-to-many group push.
- src/config.py: add pushplus_topic field
- src/notification.py: include topic param in PushPlus API call
- .env.example / README.md / CHANGELOG.md: document new config
- 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 agent strategy chat across API, bot, and web UI #minor
- add /api/v1/agent endpoints for strategies and streaming chat
- route pipeline to agent mode when specific skills are configured
- persist multi-turn conversation history in database
- add web chat page, bot /ask command, built-in strategies, and tests
* fix(agent): improve session handling, progress messaging, and chat type safety #patch
Generate UUID session IDs for stream chat requests and use running loop API.
Persist user messages even on failed runs, and store assistant error notes for context continuity.
Add contextual thinking messages after tool calls in agent executor.
Fix strategy display name lookup in bot ask command.
Refine ChatPage TypeScript types and error handling for SSE parsing.
Use SQLAlchemy select+execute for conversation history query.
* docs: add Agent strategy chat to README and CHANGELOG #skip
* fix(tests): update strategy count assertions 6->11; clean up CHANGELOG [Unreleased] section #patch
* chore: update readme and fix unitest
* feat: optimize tools
* fix: Fix hard-coded test values and resolve nested mapping in Dashboard
* update changelog
* refactor(agent): extract shared factory, cache ToolRegistry/SkillManager, fix Gemini model name
* - CRITICAL: fix 4 wrong tool names in AGENT_SYSTEM_PROMPT and
CHAT_SYSTEM_PROMPT (get_k_history→get_daily_history,
get_technical_analysis→analyze_trend, get_chip_analysis→
get_chip_distribution, search_news→search_stock_news)
- fix _THINKING_TOOL_LABELS: update 7 stale keys, add calculate_ma
and get_analysis_context (13 tools now fully mapped)
- add isinstance(dict) guard in _parse_dashboard to reject non-dict
JSON (arrays, scalars) that would crash downstream dict access
- use getattr() for config.agent_skills in pipeline.py (AttributeError safety)
- move to top-level, remove unused imports (get_config,
Callable, Any/Dict/Optional)
- add phased workflow directives to both system prompts to enforce
sequential tool calling across analysis stages
- expand .env.example AGENT_SKILLS with all 11 built-in strategies
and usage examples
- fix factory.py docstring referencing non-existent comprehensive_analysis
- Add report_summary_only config (env: REPORT_SUMMARY_ONLY, default false)
- When true, notification shows only summary block without per-stock details
- Applies to generate_dashboard_report, generate_wechat_dashboard, generate_daily_report
- Update README, full-guide, CHANGELOG, .env.example
- Register in Web UI config (config_registry, systemConfigI18n)
- No workflow change: GitHub Actions users can add REPORT_SUMMARY_ONLY via repo vars when needed
- Add MERGE_EMAIL_NOTIFICATION env var (default false) to control merged push
- When enabled, combine个股分析 and 大盘复盘 into single notification
- Reduces email count and spam detection risk
- Incompatible with SINGLE_STOCK_NOTIFY, merge disabled in single-stock mode
- Update README, .env.example, full-guide, CHANGELOG
Fixes#190
* 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
- Add STOCK_GROUP_N + EMAIL_GROUP_N config for routing reports to different emails
- Market review sends to all configured email receivers
- Minimal changes: optional params on send_to_email and send(), new helpers
* fix: report signal accuracy, data source reliability, and market review quality #patch
- Fix signal emoji mismatch for compound advice and *ST markdown escaping
- Inject structured data (indices/stats/sectors) into market review
- Fix tushare return type and API endpoint; auto-inject realtime priority
- Add search cache (500-cap FIFO), limit supplement requests to 1
- Fix analysis API report=None and ReportStrategy type inconsistency
- Update CHANGELOG for v3.0.1 ~ v3.0.5
* fix: F821 undefined name 'UnifiedRealtimeQuote'
Add a complete backtest/evaluation system that measures the accuracy of
AI-generated stock analysis recommendations against actual market outcomes.
Backend:
- Backtest engine (src/core/backtest_engine.py) with direction inference,
stop-loss/take-profit simulation, and outcome classification (win/loss/neutral)
- Repository layer (src/repositories/backtest_repo.py) with SQLite persistence
for backtest_results and backtest_summaries tables
- Service layer (src/services/backtest_service.py) orchestrating evaluation runs
with configurable window days, neutral band, and min-age filters
- REST API endpoints: POST /run, GET /results, GET /performance, GET /performance/{code}
- Pydantic schemas for request/response validation
Frontend (apps/dsa-web):
- New Backtest page with performance dashboard sidebar showing direction
accuracy, win rate, simulated returns, SL/TP trigger rates, and W/L/N counts
- Paginated results table with outcome badges, direction indicators, and
color-coded return percentages
- Stock code filter and one-click "Run Backtest" trigger
- Full TypeScript types and API client matching backend schemas
Direction mapping fix:
- "Wait/observe" (观望) advice now maps to direction_expected="down" instead
of "flat", correctly reflecting that "wait" means "stay out due to downside
risk" rather than predicting a flat market
Tests:
- 21 unit tests covering engine logic, service orchestration, and summary
aggregation (all passing)
Docs:
- Updated README, full-guide, and translations with backtest feature docs
- akshare_fetcher: Parse volume_ratio (49), PE (39), PB (46), and market cap (44/45) from Tencent API.
- efinance_fetcher: Add parsing for volume_ratio, PE, and market capitalization.
- base.py: Integrated Tushare Pro as an optional real-time quote source.
- config: Reordered default priority to prioritize Tencent (tencent > akshare_sina > efinance > akshare_em).
- documentation: Updated .env.example and GitHub Actions workflow with real-time source priority instructions.
This fix ensures critical indicators like volume ratio (量比) and turnover rate are correctly fetched and displayed across different data sources.
Closes#155