Copy disabled (too large)
Download .txt
Showing preview only (14,130K chars total). Download the full file to get everything.
Repository: hsliuping/TradingAgents-CN
Branch: main
Commit: bd599607e83c
Files: 1890
Total size: 13.1 MB
Directory structure:
gitextract_0f83fkc7/
├── .dockerignore
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ ├── config.yml
│ │ ├── documentation.md
│ │ ├── feature_request.md
│ │ └── question.md
│ ├── pull_request_template.md
│ └── workflows/
│ ├── docker-publish.yml
│ └── upstream-sync-check.yml
├── .gitignore
├── .python-version
├── .streamlit/
│ └── config.toml
├── ACKNOWLEDGMENTS.md
├── COMMERCIAL_LICENSE_TEMPLATE.md
├── CONTRIBUTORS.md
├── COPYRIGHT.md
├── Dockerfile.backend
├── Dockerfile.frontend
├── LICENSE
├── LICENSING.md
├── README.md
├── VERSION
├── app/
│ ├── LICENSE
│ ├── __init__.py
│ ├── __main__.py
│ ├── constants/
│ │ └── model_capabilities.py
│ ├── core/
│ │ ├── __init__.py
│ │ ├── config.py
│ │ ├── config_bridge.py
│ │ ├── config_compat.py
│ │ ├── database.py
│ │ ├── dev_config.py
│ │ ├── logging_config.py
│ │ ├── logging_context.py
│ │ ├── rate_limiter.py
│ │ ├── redis_client.py
│ │ ├── response.py
│ │ ├── startup_validator.py
│ │ └── unified_config.py
│ ├── main.py
│ ├── middleware/
│ │ ├── __init__.py
│ │ ├── error_handler.py
│ │ ├── operation_log_middleware.py
│ │ ├── rate_limit.py
│ │ └── request_id.py
│ ├── models/
│ │ ├── __init__.py
│ │ ├── analysis.py
│ │ ├── config.py
│ │ ├── notification.py
│ │ ├── operation_log.py
│ │ ├── screening.py
│ │ ├── stock_models.py
│ │ └── user.py
│ ├── routers/
│ │ ├── __init__.py
│ │ ├── akshare_init.py
│ │ ├── analysis.py
│ │ ├── auth_db.py
│ │ ├── baostock_init.py
│ │ ├── cache.py
│ │ ├── config.py
│ │ ├── database.py
│ │ ├── favorites.py
│ │ ├── financial_data.py
│ │ ├── health.py
│ │ ├── historical_data.py
│ │ ├── internal_messages.py
│ │ ├── logs.py
│ │ ├── model_capabilities.py
│ │ ├── multi_market_stocks.py
│ │ ├── multi_period_sync.py
│ │ ├── multi_source_sync.py
│ │ ├── news_data.py
│ │ ├── notifications.py
│ │ ├── operation_logs.py
│ │ ├── paper.py
│ │ ├── queue.py
│ │ ├── reports.py
│ │ ├── scheduler.py
│ │ ├── screening.py
│ │ ├── social_media.py
│ │ ├── sse.py
│ │ ├── stock_data.py
│ │ ├── stock_sync.py
│ │ ├── stocks.py
│ │ ├── sync.py
│ │ ├── system_config.py
│ │ ├── tags.py
│ │ ├── tushare_init.py
│ │ ├── usage_statistics.py
│ │ └── websocket_notifications.py
│ ├── schemas/
│ │ └── __init__.py
│ ├── scripts/
│ │ └── init_providers.py
│ ├── services/
│ │ ├── __init__.py
│ │ ├── analysis/
│ │ │ ├── __init__.py
│ │ │ └── status_update_utils.py
│ │ ├── analysis_service.py
│ │ ├── auth_service.py
│ │ ├── basics_sync/
│ │ │ ├── __init__.py
│ │ │ ├── processing.py
│ │ │ └── utils.py
│ │ ├── basics_sync_service.py
│ │ ├── config_provider.py
│ │ ├── config_service.py
│ │ ├── data_consistency_checker.py
│ │ ├── data_sources/
│ │ │ ├── __init__.py
│ │ │ ├── akshare_adapter.py
│ │ │ ├── baostock_adapter.py
│ │ │ ├── base.py
│ │ │ ├── data_consistency_checker.py
│ │ │ ├── manager.py
│ │ │ └── tushare_adapter.py
│ │ ├── database/
│ │ │ ├── __init__.py
│ │ │ ├── backups.py
│ │ │ ├── cleanup.py
│ │ │ ├── serialization.py
│ │ │ └── status_checks.py
│ │ ├── database_screening_service.py
│ │ ├── database_service.py
│ │ ├── enhanced_screening/
│ │ │ └── utils.py
│ │ ├── enhanced_screening_service.py
│ │ ├── favorites_service.py
│ │ ├── financial_data_service.py
│ │ ├── foreign_stock_service.py
│ │ ├── historical_data_service.py
│ │ ├── internal_message_service.py
│ │ ├── log_export_service.py
│ │ ├── memory_state_manager.py
│ │ ├── model_capability_service.py
│ │ ├── multi_source_basics_sync_service.py
│ │ ├── news_data_service.py
│ │ ├── notifications_service.py
│ │ ├── operation_log_service.py
│ │ ├── progress/
│ │ │ ├── __init__.py
│ │ │ ├── log_handler.py
│ │ │ └── tracker.py
│ │ ├── progress_log_handler.py
│ │ ├── queue/
│ │ │ ├── __init__.py
│ │ │ ├── helpers.py
│ │ │ └── keys.py
│ │ ├── queue_service.py
│ │ ├── quotes_ingestion_service.py
│ │ ├── quotes_service.py
│ │ ├── redis_progress_tracker.py
│ │ ├── scheduler_service.py
│ │ ├── screening/
│ │ │ └── eval_utils.py
│ │ ├── screening_service.py
│ │ ├── simple_analysis_service.py
│ │ ├── social_media_service.py
│ │ ├── stock_data_service.py
│ │ ├── tags_service.py
│ │ ├── unified_stock_service.py
│ │ ├── usage_statistics_service.py
│ │ ├── user_service.py
│ │ └── websocket_manager.py
│ ├── utils/
│ │ ├── api_key_utils.py
│ │ ├── error_formatter.py
│ │ ├── report_exporter.py
│ │ ├── timezone.py
│ │ └── trading_time.py
│ ├── worker/
│ │ ├── __init__.py
│ │ ├── akshare_init_service.py
│ │ ├── akshare_sync_service.py
│ │ ├── analysis_worker.py
│ │ ├── baostock_init_service.py
│ │ ├── baostock_sync_service.py
│ │ ├── example_sdk_sync_service.py
│ │ ├── financial_data_sync_service.py
│ │ ├── hk_data_service.py
│ │ ├── hk_sync_service.py
│ │ ├── multi_period_sync_service.py
│ │ ├── news_data_sync_service.py
│ │ ├── tushare_init_service.py
│ │ ├── tushare_sync_service.py
│ │ ├── us_data_service.py
│ │ └── us_sync_service.py
│ └── worker.py
├── cli/
│ ├── __init__.py
│ ├── akshare_init.py
│ ├── baostock_init.py
│ ├── main.py
│ ├── models.py
│ ├── static/
│ │ └── welcome.txt
│ ├── tushare_init.py
│ └── utils.py
├── config/
│ ├── README.md
│ ├── logging.toml
│ └── logging_docker.toml
├── docker/
│ └── nginx.conf
├── docker-compose.hub.nginx.arm.yml
├── docker-compose.hub.nginx.yml
├── docker-compose.yml
├── docs/
│ ├── ANALYST_DATA_CONFIGURATION.md
│ ├── API_KEY_MANAGEMENT_ANALYSIS.md
│ ├── API_KEY_TESTING_GUIDE.md
│ ├── BUILD_GUIDE.md
│ ├── CNAME
│ ├── CONFIG_VALIDATION_FIX_SUMMARY.md
│ ├── DOCKER_REGISTRY_STRATEGY.md
│ ├── ENHANCED_HISTORY_FEATURES_SUMMARY.md
│ ├── GITHUB_BRANCH_PROTECTION.md
│ ├── LLM_ADAPTER_TEMPLATE.py
│ ├── MODEL_RECOMMENDATION_UI_UPDATE.md
│ ├── QUICK_BUILD_REFERENCE.md
│ ├── QUICK_START.md
│ ├── README.md
│ ├── SETTINGS_MERGE.md
│ ├── SILICONFLOW_SETUP_GUIDE.md
│ ├── STRUCTURE.md
│ ├── WINDOWS_INSTALLER_OPTIMIZATION.md
│ ├── agents/
│ │ └── v0.1.13/
│ │ ├── analysts.md
│ │ ├── managers.md
│ │ ├── researchers.md
│ │ ├── risk-management.md
│ │ └── trader.md
│ ├── analysis/
│ │ ├── 4级深度分析验证报告_20251011.md
│ │ ├── analysis-nodes-and-tools.md
│ │ ├── combined_data_quick_reference.md
│ │ ├── combined_data_structure_analysis.md
│ │ ├── market_analyst_technical_analysis_issue.md
│ │ ├── pe-pb-data-update-analysis.md
│ │ ├── quotes_ingestion_optimization_summary.md
│ │ ├── quotes_ingestion_service_analysis.md
│ │ └── 时间统计准确性分析_20251011.md
│ ├── api/
│ │ └── batch-analysis-limits.md
│ ├── architecture/
│ │ ├── API_ARCHITECTURE_UPGRADE.md
│ │ ├── DATA_SOURCE_REFACTOR.md
│ │ ├── cache/
│ │ │ ├── CACHE_REFACTORING_SUMMARY.md
│ │ │ ├── CACHE_SYSTEM_ANALYSIS.md
│ │ │ ├── CACHE_SYSTEM_BUSINESS_ANALYSIS.md
│ │ │ ├── CACHE_SYSTEM_CORRECT_ANALYSIS.md
│ │ │ ├── CACHE_SYSTEM_FINAL_ANALYSIS.md
│ │ │ └── CACHE_SYSTEM_SOLUTION.md
│ │ ├── data-source/
│ │ │ └── data_priority_analysis.md
│ │ ├── data-sources-unit-comparison.md
│ │ ├── database/
│ │ │ ├── DATABASE_MANAGEMENT_IMPLEMENTATION.md
│ │ │ ├── MONGODB_COLLECTIONS_COMPARISON.md
│ │ │ ├── REQUIREMENTS_DB_UPDATE.md
│ │ │ ├── database_field_standardization_analysis.md
│ │ │ └── database_field_standardization_completed.md
│ │ ├── dataflows/
│ │ │ ├── DATAFLOWS_ARCHITECTURE_ANALYSIS.md
│ │ │ ├── DATAFLOWS_COMPREHENSIVE_OPTIMIZATION.md
│ │ │ ├── DATAFLOWS_CONSERVATIVE_REFACTORING.md
│ │ │ └── STREAM_MODE_IMPACT_ANALYSIS.md
│ │ ├── report-modules-structure.md
│ │ ├── v0.1.13/
│ │ │ ├── agent-architecture.md
│ │ │ ├── data-flow-architecture.md
│ │ │ ├── graph-structure.md
│ │ │ └── system-architecture.md
│ │ └── v0.1.16/
│ │ └── system-architecture.md
│ ├── archive/
│ │ ├── AUTHENTICATION_FIX_SUMMARY.md
│ │ ├── BACKEND_STARTUP.md
│ │ ├── FIXES_SUMMARY.md
│ │ ├── README-ORIGINAL.md
│ │ └── SOLUTION_SUMMARY.md
│ ├── blog/
│ │ ├── 2025-10-19-v1.0.0-preview-bugfixes.md
│ │ ├── 2025-10-20-system-stability-and-docker-multiarch.md
│ │ ├── 2025-10-21-configuration-system-overhaul.md
│ │ ├── 2025-10-22-config-testing-and-docker-fixes.md
│ │ ├── 2025-10-23-websocket-notifications-and-data-fixes.md
│ │ ├── 2025-10-24-docker-hub-update-and-clean-volumes.md
│ │ ├── 2025-10-24-realtime-quotes-optimization.md
│ │ ├── 2025-10-25-302ai-integration-and-ui-improvements.md
│ │ ├── 2025-10-26-user-preferences-and-financial-metrics-optimization.md
│ │ ├── 2025-10-27-compliance-optimization-and-bug-fixes.md
│ │ ├── 2025-10-28-multi-source-architecture-and-realtime-enhancements.md
│ │ ├── 2025-10-28-multi-source-data-isolation-design.md
│ │ ├── 2025-10-28-realtime-pe-pb-calculation-with-fallback-strategy.md
│ │ ├── 2025-10-29-data-source-unification-and-report-export-features.md
│ │ ├── 2025-10-30-priority-retries-and-realtime-backfill.md
│ │ ├── 2025-11-01-to-11-04-windows-installer-and-fundamental-analysis-enhancements.md
│ │ ├── 2025-11-05-to-11-06-technical-indicators-accuracy-and-data-quality.md
│ │ ├── 2025-11-07-task-execution-and-data-sync-enhancements.md
│ │ ├── 2025-11-11-us-data-source-and-cache-system-overhaul.md
│ │ ├── 2025-11-12-multi-market-support-and-async-optimization.md
│ │ ├── 2025-11-13-to-11-14-data-quality-and-system-stability-improvements.md
│ │ ├── 2025-11-15-learning-center-and-compliance-updates.md
│ │ └── green-version-backup-restore-upgrade.md
│ ├── bugfix/
│ │ ├── 2025-10-26-async-sync-conflict-fix.md
│ │ ├── 2025-10-26-estimation-audit-summary.md
│ │ ├── 2025-10-26-ps-calculation-fix.md
│ │ ├── 2025-10-26-ps-pe-calculation-summary.md
│ │ ├── 2025-10-26-realtime-api-ttm-issues.md
│ │ ├── 2025-10-26-settings-save-issues.md
│ │ ├── 2025-10-26-ttm-calculation-summary.md
│ │ ├── 2025-10-26-tushare-token-priority-issue.md
│ │ ├── 2025-10-27-add-symbol-field-to-stock-basic-info.md
│ │ └── 2025-10-27-app-error-logging-fix.md
│ ├── changes/
│ │ ├── DEPRECATION_NOTICE.md
│ │ ├── realtime-pe-pb-implementation.md
│ │ ├── remove-batch-operations.md
│ │ ├── remove-price-alert-feature.md
│ │ └── report-detail-layout-adjustment.md
│ ├── community/
│ │ ├── CALL_FOR_TESTERS.md
│ │ └── CALL_FOR_TESTERS_SHORT.md
│ ├── config/
│ │ ├── architecture.md
│ │ └── error_log_separation.md
│ ├── configuration/
│ │ ├── API_KEY_PRIORITY.md
│ │ ├── CACHE_CONFIGURATION.md
│ │ ├── CONFIGURATION_VALIDATOR.md
│ │ ├── CONFIG_MATRIX.md
│ │ ├── CONFIG_SYSTEM_VERIFICATION.md
│ │ ├── DEFAULT_BASE_URL_USAGE.md
│ │ ├── ENV_CONFIG_UPDATE.md
│ │ ├── UNIFIED_CONFIG.md
│ │ ├── config-bridge/
│ │ │ ├── CONFIG_BRIDGE_DETAILS.md
│ │ │ ├── CONFIG_BRIDGE_TEST_RESULTS.md
│ │ │ └── config_bridge_explanation.md
│ │ ├── config-guide.md
│ │ ├── configuration_analysis.md
│ │ ├── configuration_guide.md
│ │ ├── configuration_optimization_plan.md
│ │ ├── custom-openai-endpoint.md
│ │ ├── dashscope-config.md
│ │ ├── data-directory-configuration.md
│ │ ├── deepseek-config.md
│ │ ├── docker-config.md
│ │ ├── google-ai-setup.md
│ │ ├── llm-config.md
│ │ ├── migration/
│ │ │ ├── CONFIGURATION_MIGRATION.md
│ │ │ ├── CONFIG_MIGRATION.md
│ │ │ ├── CONFIG_MIGRATION_PLAN.md
│ │ │ ├── CONFIG_MIGRATION_SUMMARY.md
│ │ │ └── CONFIG_MIGRATION_TESTING.md
│ │ ├── online-tools-config.md
│ │ ├── proxy_configuration.md
│ │ ├── quotes_ingestion_config.md
│ │ └── token-tracking-guide.md
│ ├── database_setup.md
│ ├── deployment/
│ │ ├── DOCKER_LOGS_GUIDE.md
│ │ ├── EMBEDDED_PYTHON_GUIDE.md
│ │ ├── IMPLEMENTATION_SUMMARY.md
│ │ ├── PORTABLE_FAQ.md
│ │ ├── QUICK_REFERENCE.md
│ │ ├── SIMPLE_DEPLOYMENT_GUIDE.md
│ │ ├── WINDOWS_PORTABLE.md
│ │ ├── database/
│ │ │ ├── DATABASE_SETUP_GUIDE.md
│ │ │ └── export-sanitization-guide.md
│ │ ├── demo/
│ │ │ ├── demo_deployment_summary.md
│ │ │ ├── deploy_demo_system.md
│ │ │ ├── deploy_demo_with_docker.md
│ │ │ └── export_config_for_demo.md
│ │ ├── docker/
│ │ │ ├── BUILD_MULTIARCH_GUIDE.md
│ │ │ ├── DOCKER_DEPLOYMENT_v1.0.0.md
│ │ │ ├── DOCKER_FILES_README.md
│ │ │ ├── DOCKER_HUB_PUBLISH_GUIDE.md
│ │ │ ├── DOCKER_PUBLISH_GUIDE.md
│ │ │ ├── GITHUB_ACTIONS_QUICKSTART.md
│ │ │ ├── GITHUB_ACTIONS_SETUP.md
│ │ │ ├── MULTIARCH_BUILD.md
│ │ │ ├── MULTIARCH_BUILD_OPTIMIZATION.md
│ │ │ ├── docker-compose.split.yml
│ │ │ ├── docker_deployment_guide.md
│ │ │ └── quick_deploy_with_docker_hub.md
│ │ ├── docker-build-guide.md
│ │ ├── operations/
│ │ │ ├── EMERGENCY_PROCEDURES.md
│ │ │ ├── service_control.md
│ │ │ └── startup-commands-update.md
│ │ ├── portable-port-configuration.md
│ │ ├── portable-python-independence.md
│ │ ├── stop-services-guide.md
│ │ ├── v0.1.16/
│ │ │ └── deployment-guide.md
│ │ └── v1.0.0-source-installation.md
│ ├── design/
│ │ ├── README.md
│ │ ├── api_specification.md
│ │ ├── configuration_management.md
│ │ ├── hk_stock_data_source_priority.md
│ │ ├── paper_trading_multi_market_design.md
│ │ ├── stock_analysis_system_design.md
│ │ ├── stock_data_methods_analysis.md
│ │ ├── stock_data_model_design.md
│ │ ├── stock_data_quick_reference.md
│ │ ├── timezone-strategy.md
│ │ ├── v0.1.16/
│ │ │ └── api-specification.md
│ │ └── v1.0.1/
│ │ ├── 00_COMPLETION_REPORT.md
│ │ ├── 00_START_HERE.md
│ │ ├── AGENT_TEMPLATE_SPECIFICATIONS.md
│ │ ├── DATABASE_AND_USER_MANAGEMENT.md
│ │ ├── DESIGN_COMPLETION_REPORT.md
│ │ ├── DESIGN_COMPLETION_SUMMARY.md
│ │ ├── ENHANCED_API_DESIGN.md
│ │ ├── ENHANCED_IMPLEMENTATION_ROADMAP.md
│ │ ├── ENHANCEMENT_SUMMARY.md
│ │ ├── EXTENDED_AGENTS_SUPPORT.md
│ │ ├── FINAL_COMPLETION_REPORT.md
│ │ ├── FINAL_DESIGN_NOTES.md
│ │ ├── FINAL_SUMMARY.md
│ │ ├── FRONTEND_UI_DESIGN.md
│ │ ├── IMPLEMENTATION_CHECKLIST.md
│ │ ├── IMPLEMENTATION_IN_APP_DIRECTORY.md
│ │ ├── IMPLEMENTATION_ROADMAP.md
│ │ ├── INDEX.md
│ │ ├── INTEGRATION_WITH_EXISTING_SYSTEM.md
│ │ ├── PROMPT_TEMPLATE_SYSTEM_SUMMARY.md
│ │ ├── QUICK_REFERENCE.md
│ │ ├── README.md
│ │ ├── VERSION_UPDATE_SUMMARY.md
│ │ ├── prompt_template_architecture_comparison.md
│ │ ├── prompt_template_architecture_diagram.md
│ │ ├── prompt_template_implementation_guide.md
│ │ ├── prompt_template_system_design.md
│ │ ├── prompt_template_technical_spec.md
│ │ └── prompt_template_usage_examples.md
│ ├── development/
│ │ ├── 2025-10-19-dev-plan-unified-standard-plugin-llm.md
│ │ ├── ADD_NEW_DATA_SOURCE.md
│ │ ├── BRANCH_GUIDE.md
│ │ ├── BRANCH_MANAGEMENT_STRATEGY.md
│ │ ├── CIRCULAR_CALL_ANALYSIS.md
│ │ ├── CONTRIBUTING.md
│ │ ├── DEVELOPMENT_SETUP.md
│ │ ├── DEVELOPMENT_WORKFLOW.md
│ │ ├── US_DATA_SOURCE_UPGRADE_PLAN.md
│ │ ├── architecture/
│ │ │ ├── screening_a_shares_daily_p0.md
│ │ │ └── technical_indicators_unification.md
│ │ ├── branch-strategy.md
│ │ ├── development-workflow.md
│ │ ├── project-structure.md
│ │ ├── roadmap/
│ │ │ └── trading_workflow_dev_plan.md
│ │ └── v0.1.16/
│ │ └── frontend-guide.md
│ ├── docker/
│ │ ├── pdf-export-support.md
│ │ ├── startup-guide.md
│ │ └── volumes/
│ │ ├── docker_volumes_analysis.md
│ │ ├── docker_volumes_unified.md
│ │ ├── switch_to_old_mongodb_volume.md
│ │ └── volumes_cleanup_completed.md
│ ├── docker-multiarch-build.md
│ ├── docker-report-export.md
│ ├── error-handling-improvement.md
│ ├── examples/
│ │ ├── advanced-examples.md
│ │ └── basic-examples.md
│ ├── faq/
│ │ └── faq.md
│ ├── features/
│ │ ├── NEWS_ANALYST_TOOL_CALL_FIX_REPORT.md
│ │ ├── NEWS_FILTERING_SOLUTION_DESIGN.md
│ │ ├── NEWS_QUALITY_ANALYSIS_REPORT.md
│ │ ├── aggregator/
│ │ │ ├── AGGREGATOR_IMPLEMENTATION_SUMMARY.md
│ │ │ ├── AGGREGATOR_MODEL_CATALOG.md
│ │ │ ├── AGGREGATOR_QUICKSTART.md
│ │ │ ├── AGGREGATOR_SUPPORT.md
│ │ │ └── CHANGELOG_AGGREGATOR.md
│ │ ├── config-wizard/
│ │ │ ├── CONFIG_WIZARD.md
│ │ │ ├── CONFIG_WIZARD_BACKEND_INTEGRATION.md
│ │ │ ├── CONFIG_WIZARD_USAGE.md
│ │ │ └── CONFIG_WIZARD_VS_CONFIG_MANAGEMENT.md
│ │ ├── data-sync/
│ │ │ ├── MULTI_PERIOD_DATA_SYNC_UPDATE.md
│ │ │ └── MULTI_SOURCE_SYNC_GUIDE.md
│ │ ├── docker-deployment.md
│ │ ├── model-settings/
│ │ │ ├── LLM_CONFIG_UI_UPDATE.md
│ │ │ ├── SYSTEM_SETTINGS_MODEL_SELECTION.md
│ │ │ └── model-capability-ui-update.md
│ │ ├── news/
│ │ │ ├── NEWS_SENTIMENT_ANALYSIS.md
│ │ │ ├── NEWS_SYNC_FEATURE.md
│ │ │ └── news-analysis-system.md
│ │ ├── paper-trading/
│ │ │ ├── PAPER_TRADING_IMPROVEMENTS.md
│ │ │ └── PAPER_TRADING_SELL_BUTTON.md
│ │ ├── progress-tracking/
│ │ │ ├── PROGRESS_TRACKING_SOLUTION.md
│ │ │ ├── progress-tracking-explanation.md
│ │ │ ├── progress_issue_analysis.md
│ │ │ └── progress_optimization.md
│ │ ├── reporting/
│ │ │ ├── REPORT_TO_TRADING_FEATURE.md
│ │ │ ├── analysis_report_comparison_summary.md
│ │ │ ├── report-detail-metrics-enhancement.md
│ │ │ └── report-export.md
│ │ ├── stock-detail/
│ │ │ ├── STOCK_DETAIL_FUNDAMENTALS_ENHANCEMENT.md
│ │ │ └── STOCK_DETAIL_UI_OPTIMIZATION.md
│ │ └── usage-statistics/
│ │ ├── HOW_TO_ACCESS_USAGE_STATISTICS.md
│ │ ├── USAGE_STATISTICS_AND_PRICING.md
│ │ ├── USAGE_STATISTICS_FRONTEND_GUIDE.md
│ │ ├── USAGE_STATISTICS_IMPLEMENTATION_SUMMARY.md
│ │ └── USAGE_STATISTICS_QUICK_TEST.md
│ ├── fixes/
│ │ ├── 2025-10-11_bug_fixes_summary.md
│ │ ├── 2025-10-11_code_cleanup_summary.md
│ │ ├── 2025-10-11_debug_logging_enhancement.md
│ │ ├── 2025-10-11_remove_online_tools_config.md
│ │ ├── 2025-10-21-config-validation-placeholder-detection.md
│ │ ├── 2025-10-21-pyproject-missing-dependencies.md
│ │ ├── 2025-10-21-summary.md
│ │ ├── 2025-10-30-data-source-priority-fixes.md
│ │ ├── API_PATH_FIX.md
│ │ ├── DASHBOARD_MARKET_NEWS_EMPTY_FIX.md
│ │ ├── DATAFRAME_ARROW_CONVERSION_FIX.md
│ │ ├── MARKET_QUOTES_NULL_CODE_FIX.md
│ │ ├── NEWS_SYNC_SCHEDULER_SETUP.md
│ │ ├── REDIS_CONNECTION_LEAK_ANALYSIS.md
│ │ ├── SUMMARY.md
│ │ ├── amount-unit-fix.md
│ │ ├── analyst_infinite_loop_fix.md
│ │ ├── asyncio_thread_pool_fix.md
│ │ ├── batch-analysis-api-response-fix.md
│ │ ├── batch-analysis-router-fix.md
│ │ ├── batch_analysis_5_levels_verification.md
│ │ ├── confidence-score-normalization-fix.md
│ │ ├── dashboard/
│ │ │ ├── DASHBOARD_DATA_FIX.md
│ │ │ ├── DASHBOARD_MARKET_NEWS_FIX.md
│ │ │ └── DASHBOARD_RECENT_TASKS_FIX.md
│ │ ├── dashboard_news_improvements.md
│ │ ├── data-source/
│ │ │ ├── BUG_FIX_FULL_SYMBOL_INDEX.md
│ │ │ ├── PROVIDER_ID_FIX.md
│ │ │ ├── bugfix_akshare_import_error.md
│ │ │ ├── financial_metrics_fix_report.md
│ │ │ ├── fix_7digit_stock_code_issue.md
│ │ │ ├── fix_baostock_realtime_quotes_issue.md
│ │ │ ├── fix_financial_data_code_field_issue.md
│ │ │ ├── fix_hk_stock_code_normalization.md
│ │ │ ├── fix_multi_source_basics_sync.md
│ │ │ ├── fix_stock_utils_hk_recognition.md
│ │ │ └── weekend_trading_data_issue.md
│ │ ├── debate_rounds_logging.md
│ │ ├── frontend/
│ │ │ ├── FRONTEND_API_URL_FIX.md
│ │ │ ├── FRONTEND_ROUTE_FIX.md
│ │ │ ├── FRONTEND_VMODEL_FIX.md
│ │ │ ├── MODEL_NAME_DISPLAY_FIX.md
│ │ │ ├── PAPER_TRADING_REPORT_LINK_FIX.md
│ │ │ ├── PAPER_TRADING_STOCK_NAME_FIX.md
│ │ │ ├── REPORT_DETAIL_CASH_FIX.md
│ │ │ ├── STOCK_DETAIL_REPORTS_FIX.md
│ │ │ └── STOCK_SCREENING_DETAIL_LINK_FIX.md
│ │ ├── fundamentals-duplicate-tool-call-fix.md
│ │ ├── llm_timeout_monitoring.md
│ │ ├── llm_wrong_tool_call_analysis.md
│ │ ├── misc/
│ │ │ ├── COMPATIBILITY_FIX_SUMMARY.md
│ │ │ ├── ISSUE_FIX_SUMMARY.md
│ │ │ └── TRADING_FIX_SUMMARY.md
│ │ ├── missing_report_modules_analysis.md
│ │ ├── model/
│ │ │ ├── model_capability_validation_fix.md
│ │ │ ├── model_config_params_fix.md
│ │ │ ├── model_routing_fix.md
│ │ │ └── research_depth_mapping_fix.md
│ │ ├── mongodb_objectid_serialization_fix.md
│ │ ├── performance/
│ │ │ ├── BUG_FIX_ANALYSIS_STUCK.md
│ │ │ ├── async_blocking_fix.md
│ │ │ ├── estimated_time_fix.md
│ │ │ ├── estimated_total_time_fix.md
│ │ │ └── progress-tracking-fix.md
│ │ ├── reports-market-filter-fix.md
│ │ ├── reports-market-type-fix-complete.md
│ │ ├── reports-market-type-missing-fix.md
│ │ ├── research_depth_5_levels.md
│ │ ├── roe_debt_ratio_fix.md
│ │ ├── tdx_removal.md
│ │ ├── tushare_rt_k_fix.md
│ │ ├── undefined_variable_is_china_fix.md
│ │ └── volume-unit-fix.md
│ ├── frontend/
│ │ ├── DASHBOARD_LAYOUT_ADJUSTMENT.md
│ │ ├── DASHBOARD_PAPER_TRADING.md
│ │ ├── FRONTEND_CONFIG_REFACTOR.md
│ │ ├── FRONTEND_MULTI_SOURCE_SYNC.md
│ │ ├── batch-analysis-improvements.md
│ │ ├── guide-auto-hide.md
│ │ └── price-format-update.md
│ ├── frontend-auth-optimization.md
│ ├── google-ai-base-url-support.md
│ ├── guides/
│ │ ├── CURRENCY_GUIDE.md
│ │ ├── DATABASE_BACKUP_RESTORE.md
│ │ ├── INSTALLATION_GUIDE.md
│ │ ├── INSTALLATION_GUIDE_V1.md
│ │ ├── INSTALLATION_QUICK_START.md
│ │ ├── LINUX_BUILD_GUIDE.md
│ │ ├── README.md
│ │ ├── TESTING_GUIDE.md
│ │ ├── US_DATA_SOURCE_CONFIG.md
│ │ ├── a-share-analysis-guide.md
│ │ ├── akshare_unified/
│ │ │ ├── README.md
│ │ │ └── SYNC_FREQUENCY_GUIDE.md
│ │ ├── baostock_unified/
│ │ │ └── README.md
│ │ ├── config-management-guide.md
│ │ ├── deepseek-usage-guide.md
│ │ ├── docker-deployment-guide.md
│ │ ├── financial_data_system/
│ │ │ └── README.md
│ │ ├── historical_data_optimization/
│ │ │ └── README.md
│ │ ├── installation/
│ │ │ └── pdf_tools.md
│ │ ├── installation-guide.md
│ │ ├── message_data_system/
│ │ │ └── README.md
│ │ ├── multi_period_historical_data/
│ │ │ └── README.md
│ │ ├── news-analysis-guide.md
│ │ ├── news_data_system/
│ │ │ └── README.md
│ │ ├── pdf_export_guide.md
│ │ ├── portable-installation-guide.md
│ │ ├── quick-reference-nodes-tools.md
│ │ ├── quick-start-guide.md
│ │ ├── report-export-guide.md
│ │ ├── research-depth-guide.md
│ │ ├── scheduled_tasks_guide.md
│ │ ├── scheduler_frontend_bugfix.md
│ │ ├── scheduler_frontend_complete.md
│ │ ├── scheduler_frontend_implementation.md
│ │ ├── scheduler_frontend_summary.md
│ │ ├── scheduler_management.md
│ │ ├── scheduler_management_summary.md
│ │ ├── scheduler_metadata_feature.md
│ │ ├── sdk_integration_checklist.md
│ │ ├── stock_basics_sync.md
│ │ ├── stock_data_sdk_integration_guide.md
│ │ ├── tushare_financial_data/
│ │ │ └── README.md
│ │ ├── tushare_news_integration/
│ │ │ └── README.md
│ │ ├── tushare_unified/
│ │ │ ├── README.md
│ │ │ ├── apscheduler_integration_report.md
│ │ │ ├── current_data_sources_analysis.md
│ │ │ ├── data_initialization_guide.md
│ │ │ ├── data_sources_architecture_planning.md
│ │ │ ├── data_sources_migration_plan_a.md
│ │ │ ├── deployment_verification_report.md
│ │ │ ├── tushare_unified_design.md
│ │ │ └── tushare_unified_test_report.md
│ │ └── websocket_notifications.md
│ ├── images/
│ │ └── README.md
│ ├── implementation/
│ │ ├── foreign_stock_support.md
│ │ └── realtime-pe-pb-implementation-plan.md
│ ├── import_config_with_script.md
│ ├── improvements/
│ │ ├── BACKEND_OPTIMIZATION.md
│ │ ├── TRADINGAGENTS_OPTIMIZATION_ANALYSIS.md
│ │ ├── UTILS_CLEANUP_SUMMARY.md
│ │ ├── cli-web-report-unification.md
│ │ ├── refactoring_summary.md
│ │ └── request_deduplication.md
│ ├── installation-mirror.md
│ ├── integration/
│ │ ├── adapters/
│ │ │ ├── ADAPTER_PROVIDER_REORGANIZATION.md
│ │ │ └── data_adapters_analysis.md
│ │ ├── data-sources/
│ │ │ ├── DATA_SOURCE_LOGGING.md
│ │ │ ├── DATA_SOURCE_MANAGER_ENHANCEMENT.md
│ │ │ ├── KLINE_DATA_SOURCE.md
│ │ │ ├── STOCK_DATA_SERVICE_VS_DATA_SOURCE_MANAGER.md
│ │ │ ├── realtime_quotes_data_source.md
│ │ │ ├── stock_code_validation.md
│ │ │ └── stock_code_validation_backend.md
│ │ ├── dataflows_integration_plan.md
│ │ ├── enhanced_data_integration.md
│ │ ├── google/
│ │ │ ├── google_ai_dependencies_update.md
│ │ │ └── google_api_proxy_setup.md
│ │ ├── integration_summary.md
│ │ ├── providers/
│ │ │ ├── mixed_provider_mode.md
│ │ │ ├── tushare/
│ │ │ │ ├── TDX_TO_TUSHARE_MIGRATION.md
│ │ │ │ ├── TUSHARE_ADAPTER_REFACTORING.md
│ │ │ │ ├── TUSHARE_ARCHITECTURE_REFACTOR.md
│ │ │ │ ├── TUSHARE_INTEGRATION_SUMMARY.md
│ │ │ │ ├── TUSHARE_USAGE_GUIDE.md
│ │ │ │ └── tdx_removal_complete.md
│ │ │ └── us/
│ │ │ ├── US_PROVIDERS_EXPLANATION.md
│ │ │ └── US_PROVIDERS_MIGRATION_SUMMARY.md
│ │ └── rate-limit/
│ │ ├── RATE_LIMIT_HANDLING.md
│ │ └── test_akshare_rate_limit.md
│ ├── learning/
│ │ ├── 01-ai-basics/
│ │ │ └── what-is-llm.md
│ │ ├── 02-prompt-engineering/
│ │ │ ├── best-practices.md
│ │ │ └── prompt-basics.md
│ │ ├── 03-model-selection/
│ │ │ └── model-comparison.md
│ │ ├── 04-analysis-principles/
│ │ │ └── multi-agent-system.md
│ │ ├── 05-risks-limitations/
│ │ │ └── risk-warnings.md
│ │ ├── 06-resources/
│ │ │ ├── paper-guide.md
│ │ │ └── tradingagents-intro.md
│ │ ├── 08-faq/
│ │ │ └── general-questions.md
│ │ └── README.md
│ ├── llm/
│ │ ├── LLM_INTEGRATION_GUIDE.md
│ │ ├── LLM_TESTING_VALIDATION_GUIDE.md
│ │ ├── MODEL_CATALOG_IMPLEMENTATION_SUMMARY.md
│ │ ├── MODEL_CATALOG_MANAGEMENT.md
│ │ ├── MODEL_CATALOG_PROVIDER_SELECT.md
│ │ ├── MODEL_CATALOG_QUICKSTART.md
│ │ ├── MODEL_FILTERING.md
│ │ ├── MODEL_PRICING_GUIDE.md
│ │ ├── MODEL_PRICING_SYNC.md
│ │ ├── MODEL_USAGE_VERIFICATION.md
│ │ ├── QIANFAN_INTEGRATION_GUIDE.md
│ │ ├── README.md
│ │ ├── google_models_guide.md
│ │ ├── google_tool_handler_optimization.md
│ │ ├── model-capability-system.md
│ │ └── model_update_summary.md
│ ├── localization/
│ │ └── chinese-social-media-integration.md
│ ├── maintenance/
│ │ ├── mongodb_index_optimization.md
│ │ └── upstream-sync.md
│ ├── migration/
│ │ ├── DATA_DIRECTORY_MIGRATION_COMPLETED.md
│ │ └── DATA_DIRECTORY_REORGANIZATION_PLAN.md
│ ├── overview/
│ │ ├── OPEN_SOURCE_DISCLAIMER.md
│ │ ├── installation.md
│ │ ├── project-overview.md
│ │ └── quick-start.md
│ ├── paper/
│ │ └── TradingAgents_论文中文版.md
│ ├── releases/
│ │ ├── CHANGELOG.md
│ │ ├── CHANGELOG_v0.1.11.md
│ │ ├── CHANGELOG_v0.1.12.md
│ │ ├── CHANGELOG_v1.0.0-preview.md
│ │ ├── VERSION_0.1.6_RELEASE_NOTES.md
│ │ ├── VERSION_0.1.7_RELEASE_NOTES.md
│ │ ├── upgrade-guide.md
│ │ ├── upgrade-to-v0.1.13-preview.md
│ │ ├── v0.1.10-release-notes.md
│ │ ├── v0.1.11-release-notes.md
│ │ ├── v0.1.12-release-notes.md
│ │ ├── v0.1.13-highlights.md
│ │ ├── v0.1.13-known-issues.md
│ │ ├── v0.1.13-release-notes.md
│ │ ├── v0.1.14-release-notes.md
│ │ ├── v0.1.15-release-notes.md
│ │ ├── v0.1.16-design-document.md
│ │ ├── v0.1.16-preview-release-notes.md
│ │ ├── v0.1.7-release-notes.md
│ │ ├── v0.1.8-release-notes.md
│ │ ├── v0.1.9-release-notes.md
│ │ ├── v1.0.0-preview-release-notes.md
│ │ └── version-comparison.md
│ ├── security/
│ │ ├── api_keys_security.md
│ │ └── auth_system_improvement.md
│ ├── summary/
│ │ ├── DOCUMENTATION_UPDATE_SUMMARY.md
│ │ ├── RECENT_IMPROVEMENTS_SUMMARY.md
│ │ ├── pe-pb-realtime-solution-summary.md
│ │ └── phase/
│ │ ├── PHASE1_CLEANUP_SUMMARY.md
│ │ ├── PHASE2_COMPLETION.md
│ │ ├── PHASE2_REORGANIZATION_SUMMARY.md
│ │ ├── PHASE3_COMPLETION.md
│ │ └── PHASE3_WEB_UI_OPTIMIZATION.md
│ ├── survey/
│ │ ├── ONLINE_SURVEY_TEMPLATE.json
│ │ ├── PROMOTION_TEMPLATES.md
│ │ ├── README.md
│ │ ├── SURVEY_ANALYSIS_GUIDE.md
│ │ └── USER_SURVEY_2025.md
│ ├── tech_reviews/
│ │ ├── 2025-10-19-backtest-papertrade-licensing-architecture.md
│ │ ├── 2025-10-19-meeting-minutes-data-consistency-backtest-paper-architecture.md
│ │ ├── 2025-10-19-plugin-architecture-and-governance.md
│ │ ├── 2025-10-19-prompt-strategization-guide.md
│ │ ├── 2025-10-19-unified-data-standard-implementation.md
│ │ ├── 2025-10-21-multi-market-code-templates.md
│ │ ├── 2025-10-21-multi-market-data-architecture-guide.md
│ │ └── 2025-11-08-multi-market-implementation-plan.md
│ ├── technical/
│ │ ├── DASHSCOPE_ADAPTER_FIX_REPORT.md
│ │ ├── DASHSCOPE_ADAPTER_SIMPLIFICATION_REPORT.md
│ │ ├── DASHSCOPE_TOOL_CALL_DEFECTS_ANALYSIS.md
│ │ ├── DASHSCOPE_TOOL_CALL_ENHANCEMENT_REPORT.md
│ │ ├── DEEPSEEK_INTEGRATION.md
│ │ ├── DeepSeek新闻分析师修复报告.md
│ │ ├── DeepSeek新闻分析师死循环修复完成报告.md
│ │ ├── DeepSeek新闻分析师死循环问题分析报告.md
│ │ ├── LLM_TOOL_CALL_FIX_REPORT.md
│ │ ├── OPENAI_COMPATIBLE_ADAPTERS.md
│ │ ├── REACTIVE_IN_H_FUNCTION.md
│ │ └── v0.1.16/
│ │ └── testing-strategy.md
│ ├── technical-debt/
│ │ └── tradingagents_optimization.md
│ ├── test_environment_setup.md
│ ├── time_estimation_optimization.md
│ ├── troubleshooting/
│ │ ├── README.md
│ │ ├── batch-analysis-concurrent-fix.md
│ │ ├── batch-analysis-fix-summary.md
│ │ ├── concurrent-safety-summary.md
│ │ ├── docker-troubleshooting.md
│ │ ├── export-issues.md
│ │ ├── finnhub-news-data-setup.md
│ │ ├── google_client_options_error.md
│ │ ├── llm-config-test-fix.md
│ │ ├── pdf_word_export_issues.md
│ │ ├── stock_name_issue.md
│ │ ├── streamlit-file-watcher-fix.md
│ │ ├── web-startup-issues.md
│ │ ├── windows10-chromadb-fix.md
│ │ └── windows_cairo_fix.md
│ ├── troubleshooting-mongodb-docker.md
│ └── usage/
│ ├── deepseek-usage-guide.md
│ ├── investment_analysis_guide.md
│ ├── web-interface-detailed-guide.md
│ └── web-interface-guide.md
├── examples/
│ ├── README.md
│ ├── __init__.py
│ ├── batch_analysis.py
│ ├── cli_demo.py
│ ├── config_management_demo.py
│ ├── crawlers/
│ │ ├── internal_message_crawler.py
│ │ ├── message_crawler_scheduler.py
│ │ └── social_media_crawler.py
│ ├── custom_analysis_demo.py
│ ├── dashscope_examples/
│ │ ├── __init__.py
│ │ ├── demo_dashscope.py
│ │ ├── demo_dashscope_chinese.py
│ │ ├── demo_dashscope_no_memory.py
│ │ └── demo_dashscope_simple.py
│ ├── data_dir_config_demo.py
│ ├── demo_deepseek_analysis.py
│ ├── demo_deepseek_simple.py
│ ├── demo_news_filtering.py
│ ├── enhanced_history_demo.py
│ ├── my_stock_analysis.py
│ ├── run_message_crawlers.py
│ ├── simple_analysis_demo.py
│ ├── stock_data_model_usage.py
│ ├── stock_list_example.py
│ ├── stock_query_examples.py
│ ├── test_enhanced_data_integration.py
│ ├── test_installation.py
│ ├── test_news_timeout.py
│ ├── token_tracking_demo.py
│ ├── tushare_demo.py
│ └── tushare_unified_demo.py
├── frontend/
│ ├── .eslintrc.cjs
│ ├── .prettierrc.json
│ ├── .yarnrc
│ ├── LICENSE
│ ├── README.md
│ ├── clear_auth.html
│ ├── env.d.ts
│ ├── index.html
│ ├── package.json
│ ├── public/
│ │ └── manifest.json
│ ├── src/
│ │ ├── App.vue
│ │ ├── api/
│ │ │ ├── analysis.ts
│ │ │ ├── auth.ts
│ │ │ ├── cache.ts
│ │ │ ├── config.ts
│ │ │ ├── database.ts
│ │ │ ├── favorites.ts
│ │ │ ├── logs.ts
│ │ │ ├── modelCapabilities.ts
│ │ │ ├── multiMarket.ts
│ │ │ ├── news.ts
│ │ │ ├── notifications.ts
│ │ │ ├── operationLogs.ts
│ │ │ ├── paper.ts
│ │ │ ├── request.ts
│ │ │ ├── scheduler.ts
│ │ │ ├── screening.ts
│ │ │ ├── stockSync.ts
│ │ │ ├── stocks.ts
│ │ │ ├── sync.ts
│ │ │ ├── tags.ts
│ │ │ ├── templates.ts
│ │ │ └── usage.ts
│ │ ├── components/
│ │ │ ├── ConfigValidator.vue
│ │ │ ├── ConfigWizard.vue
│ │ │ ├── Dashboard/
│ │ │ │ └── MultiSourceSyncCard.vue
│ │ │ ├── DeepModelSelector.vue
│ │ │ ├── Dev/
│ │ │ │ └── DevPanel.vue
│ │ │ ├── Global/
│ │ │ │ ├── GlobalConfirm.vue
│ │ │ │ ├── GlobalNotification.vue
│ │ │ │ ├── MarketSelector.vue
│ │ │ │ ├── MultiMarketStockSearch.vue
│ │ │ │ ├── TaskReportDialog.vue
│ │ │ │ └── TaskResultDialog.vue
│ │ │ ├── Layout/
│ │ │ │ ├── AppFooter.vue
│ │ │ │ ├── Breadcrumb.vue
│ │ │ │ ├── HeaderActions.vue
│ │ │ │ ├── SidebarMenu.vue
│ │ │ │ └── UserProfile.vue
│ │ │ ├── ModelConfig.vue
│ │ │ ├── NetworkStatus.vue
│ │ │ ├── Sync/
│ │ │ │ ├── DataSourceStatus.vue
│ │ │ │ ├── SyncControl.vue
│ │ │ │ ├── SyncHistory.vue
│ │ │ │ └── SyncRecommendations.vue
│ │ │ └── index.ts
│ │ ├── constants/
│ │ │ └── analysts.ts
│ │ ├── layouts/
│ │ │ └── BasicLayout.vue
│ │ ├── main.ts
│ │ ├── router/
│ │ │ └── index.ts
│ │ ├── stores/
│ │ │ ├── app.ts
│ │ │ ├── auth.ts
│ │ │ └── notifications.ts
│ │ ├── styles/
│ │ │ ├── dark-theme.scss
│ │ │ ├── index.scss
│ │ │ └── variables.scss
│ │ ├── test-import.js
│ │ ├── types/
│ │ │ ├── analysis.ts
│ │ │ ├── auth.ts
│ │ │ ├── config.ts
│ │ │ └── router.d.ts
│ │ ├── utils/
│ │ │ ├── __tests__/
│ │ │ │ └── market.test.ts
│ │ │ ├── auth.ts
│ │ │ ├── datetime.ts
│ │ │ ├── market.ts
│ │ │ ├── stock.ts
│ │ │ └── stockValidator.ts
│ │ └── views/
│ │ ├── About/
│ │ │ └── index.vue
│ │ ├── Analysis/
│ │ │ ├── AnalysisHistory.vue
│ │ │ ├── BatchAnalysis.vue
│ │ │ └── SingleAnalysis.vue
│ │ ├── Auth/
│ │ │ └── Login.vue
│ │ ├── Dashboard/
│ │ │ └── index.vue
│ │ ├── Error/
│ │ │ └── 404.vue
│ │ ├── Favorites/
│ │ │ └── index.vue
│ │ ├── Learning/
│ │ │ ├── Article.vue
│ │ │ ├── Category.vue
│ │ │ └── index.vue
│ │ ├── PaperTrading/
│ │ │ └── index.vue
│ │ ├── Queue/
│ │ │ └── index.vue
│ │ ├── Reports/
│ │ │ ├── ReportDetail.vue
│ │ │ ├── TokenStatistics.vue
│ │ │ └── index.vue
│ │ ├── Screening/
│ │ │ └── index.vue
│ │ ├── Settings/
│ │ │ ├── CacheManagement.vue
│ │ │ ├── ConfigManagement.vue
│ │ │ ├── UsageStatistics.vue
│ │ │ ├── components/
│ │ │ │ ├── DataSourceConfigDialog.vue
│ │ │ │ ├── DataSourceGroupingDialog.vue
│ │ │ │ ├── LLMConfigDialog.vue
│ │ │ │ ├── MarketCategoryDialog.vue
│ │ │ │ ├── MarketCategoryManagement.vue
│ │ │ │ ├── ModelCatalogManagement.vue
│ │ │ │ ├── ProviderDialog.vue
│ │ │ │ └── SortableDataSourceList.vue
│ │ │ └── index.vue
│ │ ├── Stocks/
│ │ │ └── Detail.vue
│ │ ├── System/
│ │ │ ├── DatabaseManagement.vue
│ │ │ ├── LogManagement.vue
│ │ │ ├── MultiSourceSync.vue
│ │ │ ├── OperationLogs.vue
│ │ │ └── SchedulerManagement.vue
│ │ └── Tasks/
│ │ └── TaskCenter.vue
│ ├── tsconfig.json
│ └── vite.config.ts
├── install/
│ ├── database_export_config.json
│ └── database_export_config_2025-11-13.json
├── main.py
├── nginx/
│ └── nginx.conf
├── pyproject.toml
├── reports/
│ ├── duplicate_logger_fix_report.md
│ ├── logger_position_fix_report.md
│ ├── logging_import_fix_report.md
│ ├── pip_freeze_local.txt
│ ├── print_to_log_conversion_report.md
│ └── syntax_error_files_report.txt
├── requirements-lock.txt
├── requirements.txt
├── scripts/
│ ├── README.md
│ ├── README_import_config.md
│ ├── USER_MANAGEMENT.md
│ ├── add_302ai_provider.py
│ ├── akshare_force_sync_all.py
│ ├── akshare_sync_optimized.py
│ ├── analyze_amount_distribution.py
│ ├── analyze_data_calls.py
│ ├── archived/
│ │ └── container_quick_init.py
│ ├── backup_branches.sh
│ ├── backup_volumes.ps1
│ ├── batch_update_docs.py
│ ├── build-amd64.ps1
│ ├── build-amd64.sh
│ ├── build-and-publish-linux.sh
│ ├── build-arm64.sh
│ ├── build-multiarch.ps1
│ ├── build-multiarch.sh
│ ├── build_docker_with_pdf.ps1
│ ├── build_docker_with_pdf.py
│ ├── build_docker_with_pdf.sh
│ ├── capture_web_screenshots.py
│ ├── check-build-context.sh
│ ├── check_000001_data.py
│ ├── check_688788_info.py
│ ├── check_akshare_data_structure.py
│ ├── check_akshare_fields.py
│ ├── check_amount_unit.py
│ ├── check_analysis_reports.py
│ ├── check_api_config.py
│ ├── check_config_coverage.py
│ ├── check_config_reports.py
│ ├── check_datasource_names.py
│ ├── check_datasource_priority_simple.py
│ ├── check_db_data.py
│ ├── check_doc_consistency.py
│ ├── check_existing_reports.py
│ ├── check_export_file.py
│ ├── check_financial_data.py
│ ├── check_financial_sample.py
│ ├── check_financial_structure.py
│ ├── check_gemini_config.py
│ ├── check_gemini_provider.py
│ ├── check_google_llm_attrs.py
│ ├── check_license.py
│ ├── check_llm_pricing.py
│ ├── check_llm_providers.py
│ ├── check_missing_dependencies.py
│ ├── check_missing_stocks.py
│ ├── check_model_config.py
│ ├── check_mongodb_data_range.py
│ ├── check_mongodb_financial_data.py
│ ├── check_mongodb_system_config.py
│ ├── check_news_data.py
│ ├── check_news_fields.py
│ ├── check_news_in_db.py
│ ├── check_ningde_data.py
│ ├── check_null_code.py
│ ├── check_old_mongodb_volume.py
│ ├── check_pdf_tools.py
│ ├── check_provider_values.py
│ ├── check_redis_cache.py
│ ├── check_redis_connections.py
│ ├── check_roe.py
│ ├── check_stock_daily_data.py
│ ├── check_stock_daily_quotes_fields.py
│ ├── check_stock_fields.py
│ ├── check_stock_source.py
│ ├── check_token_usage_collection.py
│ ├── check_tushare_data_range.py
│ ├── check_us_cache_status.py
│ ├── check_us_datasource_priority.py
│ ├── check_usage_records.py
│ ├── clean_invalid_trade_date.py
│ ├── clean_volumes.ps1
│ ├── cleanup_old_system_config.py
│ ├── cleanup_test_env.ps1
│ ├── cleanup_unused_volumes.ps1
│ ├── compare_requirements.py
│ ├── config/
│ │ └── cleanup_sensitive_in_db.py
│ ├── container_init.sh
│ ├── convert_prints_to_logs.py
│ ├── create_default_admin.py
│ ├── create_default_users.py
│ ├── debug/
│ │ ├── check_industry_data.py
│ │ ├── check_log_timezone.py
│ │ ├── check_mongodb_data.py
│ │ ├── check_real_estate_data.py
│ │ ├── check_report_detail.py
│ │ ├── check_report_fields.py
│ │ ├── check_timezone.py
│ │ ├── check_user.py
│ │ ├── check_zhipu_config.py
│ │ ├── debug_000002_detailed.py
│ │ ├── debug_000002_pe.py
│ │ ├── debug_000002_simple.py
│ │ ├── debug_analysis_issue.py
│ │ ├── debug_api_response.py
│ │ ├── debug_industries.py
│ │ ├── debug_providers.py
│ │ ├── debug_valuation_data.py
│ │ └── quick_test_stock_code.py
│ ├── debug_backfill.py
│ ├── debug_bulk_write_issue.py
│ ├── debug_data_save_process.py
│ ├── debug_default_base_url.py
│ ├── debug_docker.ps1
│ ├── debug_docker.sh
│ ├── debug_enhanced_adapter.py
│ ├── debug_frontend_api.py
│ ├── debug_mongodb_connection.py
│ ├── debug_mongodb_daily_data.py
│ ├── debug_mongodb_query.py
│ ├── debug_mongodb_time.py
│ ├── debug_news_format.py
│ ├── debug_tushare_historical_sync.py
│ ├── demo_user_activity.py
│ ├── deploy_demo.sh
│ ├── deployment/
│ │ ├── README.md
│ │ ├── build_portable_package.ps1
│ │ ├── create_github_release.py
│ │ ├── create_portable_venv.ps1
│ │ ├── create_standalone_venv.ps1
│ │ ├── deploy_stop_scripts.ps1
│ │ ├── get_vendors.ps1
│ │ ├── migrate_to_embedded_python.ps1
│ │ ├── package_venv_with_runtime.ps1
│ │ ├── rebuild_portable_venv.ps1
│ │ ├── release_v0.1.2.py
│ │ ├── release_v0.1.3.py
│ │ ├── release_v0.1.9.py
│ │ ├── setup_embedded_python.ps1
│ │ ├── sync_and_build_only.ps1
│ │ ├── sync_to_portable.ps1
│ │ ├── temp_original_build.ps1
│ │ ├── update_scripts_for_embedded_python.ps1
│ │ └── verify_venv.ps1
│ ├── development/
│ │ ├── adaptive_cache_manager.py
│ │ ├── calculate_valuation_300750.py
│ │ ├── calculate_valuation_300750_v2.py
│ │ ├── download_finnhub_sample_data.py
│ │ ├── extract_comparison_results.py
│ │ ├── fix_streamlit_watcher.py
│ │ ├── organize_scripts.py
│ │ ├── prepare_upstream_contribution.py
│ │ ├── test_hk_data_fields.py
│ │ ├── test_hk_data_with_preclose.py
│ │ ├── test_hk_pe_pb.py
│ │ ├── test_hk_technical_indicators.py
│ │ ├── test_hk_valuation_apis.py
│ │ ├── test_hk_with_financials.py
│ │ ├── test_lookback_days.py
│ │ ├── test_pre_close_fix.py
│ │ ├── test_rsi_styles.py
│ │ ├── test_unified_indicators.py
│ │ └── verify_601899_stock_info.py
│ ├── diagnose_empty_data.py
│ ├── diagnose_env_vars.py
│ ├── diagnose_historical_data_sync.py
│ ├── diagnose_nginx.ps1
│ ├── diagnose_pe_pb_data.py
│ ├── diagnose_system.py
│ ├── diagnose_usage_statistics.py
│ ├── disable_structured_logs.py
│ ├── docker/
│ │ ├── README.md
│ │ ├── docker-compose-start.bat
│ │ ├── mongo-init.js
│ │ ├── start_docker_services.bat
│ │ ├── start_docker_services.sh
│ │ ├── start_services_alt_ports.bat
│ │ ├── start_services_simple.bat
│ │ ├── stop_docker_services.bat
│ │ └── stop_docker_services.sh
│ ├── docker-init.ps1
│ ├── docker-init.sh
│ ├── docker_deployment_init.py
│ ├── docker_init.ps1
│ ├── download_finnhub_data.py
│ ├── easy_install.ps1
│ ├── easy_install.sh
│ ├── enable_mongodb_cache.py
│ ├── ensure_logs_dir.py
│ ├── export_config_data.ps1
│ ├── export_config_simple.ps1
│ ├── extract_error_files.py
│ ├── fix_auth_imports.py
│ ├── fix_chromadb.ps1
│ ├── fix_chromadb.sh
│ ├── fix_chromadb_win10.ps1
│ ├── fix_depth_value.py
│ ├── fix_docker_logging.py
│ ├── fix_duplicate_loggers.py
│ ├── fix_full_symbol_index.py
│ ├── fix_logger_position.py
│ ├── fix_logging_config_error.py
│ ├── fix_logging_imports.py
│ ├── fix_market_quotes_null_code.py
│ ├── fix_null_full_symbol.py
│ ├── fix_paper_trading_initial_cash.py
│ ├── fix_provider_id_types.py
│ ├── fix_pyyaml_windows.ps1
│ ├── fix_stock_code_issue.py
│ ├── fix_us_datasource_enabled.py
│ ├── fixes/
│ │ └── fix_level3_deadlock.py
│ ├── full_redeploy_linux.sh
│ ├── get_container_logs.py
│ ├── get_main_branch_logs.py
│ ├── git/
│ │ ├── README.md
│ │ ├── branch_manager.py
│ │ ├── check_branch_overlap.py
│ │ ├── setup_fork_environment.sh
│ │ └── upstream_git_workflow.sh
│ ├── import_config_and_create_user.py
│ ├── init-directories.ps1
│ ├── init-directories.sh
│ ├── init_model_catalog.py
│ ├── init_paper_trading_market_rules.py
│ ├── init_scheduler_metadata.py
│ ├── init_system_data.py
│ ├── inspect_view_data.py
│ ├── install_and_run.py
│ ├── install_pandoc.py
│ ├── install_pdf_tools.py
│ ├── installer/
│ │ ├── setup.ps1
│ │ ├── start_all.ps1
│ │ ├── start_services_clean.ps1
│ │ └── stop_all.ps1
│ ├── log_analyzer.py
│ ├── maintenance/
│ │ ├── analyze_differences.ps1
│ │ ├── branch_manager.py
│ │ ├── cleanup_cache.py
│ │ ├── cleanup_duplicate_stocks.py
│ │ ├── create_scripts_structure.ps1
│ │ ├── debug_integration.ps1
│ │ ├── dumpmongodb.py
│ │ ├── finalize_script_organization.py
│ │ ├── fix_imports.py
│ │ ├── fix_mongodb_reports.py
│ │ ├── fix_timezone_data.py
│ │ ├── integrate_cache_improvements.ps1
│ │ ├── migrate_env_direct.py
│ │ ├── migrate_first_contribution.ps1
│ │ ├── optimize_mongodb_indexes.py
│ │ ├── organize_root_scripts.py
│ │ ├── remove_contribution_from_git.ps1
│ │ ├── reset_stock_basics.py
│ │ ├── restart_api_and_test.py
│ │ ├── sync_upstream.py
│ │ └── version_manager.py
│ ├── manual_sync_trigger.py
│ ├── migrate_add_market_type.py
│ ├── migrate_auth_to_db.py
│ ├── migrate_config.py
│ ├── migrate_config_to_db.py
│ ├── migrate_config_to_webapi.py
│ ├── migrate_data_directories.py
│ ├── migrate_financial_data_symbol_to_code.py
│ ├── migrate_paper_trading_multi_market.py
│ ├── migrate_to_unified_logging.py
│ ├── migrate_user_preferences.py
│ ├── migrate_users_to_api.py
│ ├── migration/
│ │ ├── migrate_paper_accounts_cash_structure.py
│ │ └── standardize_stock_code_fields.py
│ ├── migrations/
│ │ ├── add_symbol_field_to_stock_basic_info.py
│ │ ├── fix_stock_basic_info_symbol.py
│ │ ├── migrate_financial_data_add_symbol.py
│ │ └── migrate_stock_basic_info_add_source_index.py
│ ├── mongo-init-debug.js
│ ├── mongo-init.js
│ ├── portable/
│ │ ├── README.md
│ │ ├── stop_all.ps1
│ │ └── stop_all_services.bat
│ ├── publish-docker-images.ps1
│ ├── publish-docker-images.sh
│ ├── quick_get_logs.ps1
│ ├── quick_get_logs.sh
│ ├── quick_login_fix.py
│ ├── quick_syntax_check.py
│ ├── quick_test.py
│ ├── quick_test_pe_pb.py
│ ├── rebuild_and_test.ps1
│ ├── restore_user_analysts.py
│ ├── restore_volumes.ps1
│ ├── setup/
│ │ ├── configure_pip_source.py
│ │ ├── create_financial_data_collection.py
│ │ ├── create_historical_data_collection.py
│ │ ├── create_message_collections.py
│ │ ├── create_news_data_collection.py
│ │ ├── create_stock_screening_view.py
│ │ ├── init_database.py
│ │ ├── init_mongodb_indexes.py
│ │ ├── init_multi_market_collections.py
│ │ ├── initialize_system.py
│ │ ├── install_packages.bat
│ │ ├── install_packages_venv.bat
│ │ ├── install_pdf_tools.py
│ │ ├── manual_pip_config.py
│ │ ├── migrate_env_to_config.py
│ │ ├── pip_manager.bat
│ │ ├── quick_install.py
│ │ ├── run_in_venv.bat
│ │ ├── setup_databases.py
│ │ ├── setup_fork_environment.ps1
│ │ ├── setup_pip_source.ps1
│ │ ├── update_gitignore.bat
│ │ └── update_historical_data_indexes.py
│ ├── setup-docker.py
│ ├── simple_async_test.py
│ ├── simple_auth_migration.py
│ ├── simple_log_test.py
│ ├── smart_start.ps1
│ ├── smart_start.sh
│ ├── start_backend_with_proxy.ps1
│ ├── start_docker.ps1
│ ├── start_docker.sh
│ ├── start_test_db.ps1
│ ├── start_worker.py
│ ├── startup/
│ │ ├── restart_mongodb_with_timezone.bat
│ │ ├── restart_mongodb_with_timezone.sh
│ │ ├── start_api.py
│ │ ├── start_backend.bat
│ │ ├── start_backend.py
│ │ ├── start_backend.sh
│ │ ├── start_backend_direct.py
│ │ ├── start_debug_services.bat
│ │ ├── start_frontend.py
│ │ ├── start_production.py
│ │ ├── start_simple.bat
│ │ ├── start_simple.sh
│ │ ├── start_web.bat
│ │ ├── start_web.ps1
│ │ ├── start_web.py
│ │ └── start_web.sh
│ ├── stock_code_validator.py
│ ├── stop_test_db.ps1
│ ├── switch_and_cleanup_volumes.ps1
│ ├── switch_to_prod_env.ps1
│ ├── switch_to_test_env.ps1
│ ├── switch_volumes_simple.ps1
│ ├── sync_financial_data.py
│ ├── sync_market_news.py
│ ├── sync_model_config_to_json.py
│ ├── sync_pricing_now.py
│ ├── syntax_checker.py
│ ├── syntax_test_script.py
│ ├── test/
│ │ └── test_hk_sync.py
│ ├── test_000001_sync.py
│ ├── test_actual_analysis_url.py
│ ├── test_aggregator_support.py
│ ├── test_akshare_baostock_multi_period.py
│ ├── test_akshare_batch_quotes.py
│ ├── test_akshare_date_format.py
│ ├── test_akshare_docker.py
│ ├── test_akshare_news.py
│ ├── test_akshare_news_sync.py
│ ├── test_akshare_rate_limit.ps1
│ ├── test_akshare_rate_limit.py
│ ├── test_akshare_ttm_calculation.py
│ ├── test_akshare_with_curl_cffi.py
│ ├── test_all_base_url_fixes.py
│ ├── test_all_sources_historical_days.py
│ ├── test_alpha_vantage_finnhub.py
│ ├── test_analyst_base_url.py
│ ├── test_api_key_edit.py
│ ├── test_api_key_priority.py
│ ├── test_api_key_validation.py
│ ├── test_api_report_000002.py
│ ├── test_api_settings.py
│ ├── test_async_progress.py
│ ├── test_bridge_system_settings.py
│ ├── test_concurrent_api.py
│ ├── test_config_bridge.py
│ ├── test_config_compatibility.py
│ ├── test_config_reload.py
│ ├── test_config_service.py
│ ├── test_config_usage.py
│ ├── test_curl_cffi.py
│ ├── test_data_preparation.py
│ ├── test_data_source_logging.py
│ ├── test_database_api.py
│ ├── test_datasource_groupings.py
│ ├── test_datasource_mapping.py
│ ├── test_date_format_fix.py
│ ├── test_default_base_url.py
│ ├── test_default_base_url_fix.py
│ ├── test_direct_mongodb.py
│ ├── test_direct_news_api.py
│ ├── test_docker_export.sh
│ ├── test_docker_logging.py
│ ├── test_docker_pdf.py
│ ├── test_eastmoney_columns.py
│ ├── test_enhanced_logging.py
│ ├── test_env_config.py
│ ├── test_env_validation.py
│ ├── test_error_formatter.py
│ ├── test_estimated_total_time.py
│ ├── test_fallback_mechanism.py
│ ├── test_financial_data_fix.py
│ ├── test_financial_data_flow.py
│ ├── test_financial_fallback.py
│ ├── test_fixed_historical_sync.py
│ ├── test_foreign_stock_api.py
│ ├── test_foreign_stock_priority.py
│ ├── test_frontend_api.py
│ ├── test_fundamentals_realtime.py
│ ├── test_fundamentals_unified.py
│ ├── test_fundamentals_with_stock_name.py
│ ├── test_google_api_connection.py
│ ├── test_google_api_with_proxy.py
│ ├── test_google_base_url.py
│ ├── test_google_sdk_basic.py
│ ├── test_historical_days_fix.py
│ ├── test_hk_error_handling.py
│ ├── test_import_export.py
│ ├── test_integration_validation.py
│ ├── test_kline_realtime.py
│ ├── test_market_type_fix.py
│ ├── test_migration.py
│ ├── test_mixed_provider_mode.py
│ ├── test_model_api_base.py
│ ├── test_model_config_fix.py
│ ├── test_model_config_params.py
│ ├── test_model_features_fix.py
│ ├── test_mongodb_as_datasource.py
│ ├── test_mongodb_model_config.py
│ ├── test_mongodb_standalone.sh
│ ├── test_mongodb_storage_init.py
│ ├── test_monkey_patch.py
│ ├── test_multi_period_data.py
│ ├── test_multi_period_sync.py
│ ├── test_multi_source_sync.py
│ ├── test_news_from_db.py
│ ├── test_news_sentiment_analysis.py
│ ├── test_news_sync.py
│ ├── test_news_unified.py
│ ├── test_no_data_error.py
│ ├── test_no_infinite_retry.py
│ ├── test_pct_chg_filter.py
│ ├── test_pe_pb_fix.py
│ ├── test_preferred_sources.py
│ ├── test_progress_fix.py
│ ├── test_progress_tracking.py
│ ├── test_provider_lookup.py
│ ├── test_proxy_config.ps1
│ ├── test_ps_calculation_verification.py
│ ├── test_qianfan_connect.py
│ ├── test_qianfan_raw.py
│ ├── test_queue.py
│ ├── test_rate_limiter.py
│ ├── test_roe_fetch.py
│ ├── test_scheduler_api_response.py
│ ├── test_scheduler_frontend.py
│ ├── test_scheduler_management.py
│ ├── test_scheduler_metadata.py
│ ├── test_screening_view.py
│ ├── test_selective_sync.py
│ ├── test_settings_meta.py
│ ├── test_simple.py
│ ├── test_sina_api.py
│ ├── test_sina_columns.py
│ ├── test_smart_progress.py
│ ├── test_ssl_retry.py
│ ├── test_startup_validator.py
│ ├── test_stock_data_api.py
│ ├── test_stock_data_preparation.py
│ ├── test_stock_fundamentals_enhanced.py
│ ├── test_stock_info.py
│ ├── test_stock_info_fallback.py
│ ├── test_stock_info_fallback_fixed.py
│ ├── test_stock_info_unified.py
│ ├── test_stock_name_issue.py
│ ├── test_string_slice.py
│ ├── test_time_estimation.py
│ ├── test_token_tracking.py
│ ├── test_ttm_calculation.py
│ ├── test_ttm_calculation_logic.py
│ ├── test_tushare_roe.py
│ ├── test_tushare_rt_k.py
│ ├── test_unified_config.py
│ ├── test_update_quotes.py
│ ├── test_usage_recording.py
│ ├── test_wait_and_retry.py
│ ├── trigger_quotes_backfill.py
│ ├── unified_data_manager.py
│ ├── update_analysis_models.py
│ ├── update_db_api_keys.py
│ ├── update_model_catalog_with_pricing.py
│ ├── user_activity_manager.py
│ ├── user_manager.bat
│ ├── user_manager.ps1
│ ├── user_password_manager.py
│ ├── validate_api_keys.py
│ ├── validation/
│ │ ├── README.md
│ │ ├── analyze_missing_pe.py
│ │ ├── analyze_stock_count.py
│ │ ├── check_300750.py
│ │ ├── check_dependencies.py
│ │ ├── check_extended_fields.py
│ │ ├── check_imports.py
│ │ ├── check_stock_collections.py
│ │ ├── check_system_status.py
│ │ ├── debug_tushare_data.py
│ │ ├── diagnose_missing_fields.py
│ │ ├── inspect_analysis_tasks_schema.py
│ │ ├── smart_config.py
│ │ ├── verify_extended_fields.py
│ │ └── verify_gitignore.py
│ ├── verify_docker_logs.py
│ ├── verify_fix.py
│ ├── verify_imported_config.py
│ ├── verify_migration.py
│ ├── verify_reports_display.md
│ ├── verify_ttm_calculation_000001.py
│ ├── view_logs.py
│ ├── windows-installer/
│ │ ├── README.md
│ │ ├── nsis/
│ │ │ └── installer.nsi
│ │ ├── prepare/
│ │ │ ├── build_portable.ps1
│ │ │ └── probe_ports.ps1
│ │ └── test_installer.ps1
│ └── 补充行业信息_akshare.py
├── tests/
│ ├── 0.1.14/
│ │ ├── cleanup_test_data.py
│ │ ├── create_sample_reports.py
│ │ ├── test_analysis_save.py
│ │ ├── test_backup_datasource.py
│ │ ├── test_comprehensive_backup.py
│ │ ├── test_data_structure.py
│ │ ├── test_fallback_mechanism.py
│ │ ├── test_google_tool_handler_fix.py
│ │ ├── test_guide_auto_hide.py
│ │ ├── test_import_fix.py
│ │ ├── test_online_tools_config.py
│ │ ├── test_real_scenario_fix.py
│ │ ├── test_tool_selection_logic.py
│ │ ├── test_tushare_direct.py
│ │ └── test_us_stock_independence.py
│ ├── FILE_ORGANIZATION_SUMMARY.md
│ ├── README.md
│ ├── __init__.py
│ ├── akshare_check_fixed.py
│ ├── akshare_isolated_test.py
│ ├── analyze_akshare_data.py
│ ├── check_key_metrics.py
│ ├── config/
│ │ ├── test_deprecations.py
│ │ ├── test_logging_config.py
│ │ ├── test_logging_json.py
│ │ └── test_settings.py
│ ├── conftest.py
│ ├── dataflows/
│ │ └── test_realtime_metrics.py
│ ├── debug_akshare_daily_basic.py
│ ├── debug_baostock_fields.py
│ ├── debug_baostock_stock_list.py
│ ├── debug_deepseek_cost.py
│ ├── debug_deepseek_cost_issue.py
│ ├── debug_full_flow.py
│ ├── debug_imports.py
│ ├── debug_test_execution.py
│ ├── debug_tool_binding_issue.py
│ ├── debug_web_issue.py
│ ├── demo_fallback_system.py
│ ├── final_gemini_test.py
│ ├── fundamentals_analyst_clean.py
│ ├── integration/
│ │ ├── __init__.py
│ │ └── test_dashscope_integration.py
│ ├── middleware/
│ │ └── test_trace_id.py
│ ├── pytest.ini
│ ├── quick_akshare_check.py
│ ├── quick_redis_test.py
│ ├── quick_test.py
│ ├── quick_test_hk.py
│ ├── services/
│ │ ├── test_quotes_backfill.py
│ │ ├── test_quotes_ingestion_and_enrichment.py
│ │ ├── test_scheduler_quotes_job.py
│ │ └── test_screening_roe_field.py
│ ├── simple_akshare_test.py
│ ├── simple_env_test.py
│ ├── system/
│ │ ├── test_config_summary.py
│ │ └── test_llm_provider_sanitization.py
│ ├── test_000002_valuation.py
│ ├── test_002027_specific.py
│ ├── test_300750_final.py
│ ├── test_agent_utils_tushare_fix.py
│ ├── test_akshare_alternative.py
│ ├── test_akshare_amount.py
│ ├── test_akshare_api.py
│ ├── test_akshare_code_format.py
│ ├── test_akshare_debug.py
│ ├── test_akshare_direct.py
│ ├── test_akshare_fixed.py
│ ├── test_akshare_functionality.py
│ ├── test_akshare_hk.py
│ ├── test_akshare_hk_apis.py
│ ├── test_akshare_performance.py
│ ├── test_akshare_priority.py
│ ├── test_akshare_priority_clean.py
│ ├── test_akshare_priority_fix.py
│ ├── test_all_analysts_hk_fix.py
│ ├── test_all_apis.py
│ ├── test_amount_fix.py
│ ├── test_amplitude_api.py
│ ├── test_analysis.py
│ ├── test_analysis_result.py
│ ├── test_analysis_with_apis.py
│ ├── test_analyst_loop_fix.py
│ ├── test_api_analysis.py
│ ├── test_api_format.py
│ ├── test_app_error_logging.py
│ ├── test_async_analysis.py
│ ├── test_asyncio_thread_pool_fix.py
│ ├── test_baostock_fixed.py
│ ├── test_baostock_quick.py
│ ├── test_baostock_stock_filter.py
│ ├── test_baostock_valuation.py
│ ├── test_batch_analysis_planA.py
│ ├── test_cache_optimization.py
│ ├── test_chinese_output.py
│ ├── test_cli_fix.py
│ ├── test_cli_hk.py
│ ├── test_cli_logging_fix.py
│ ├── test_cli_progress_display.py
│ ├── test_cli_version.py
│ ├── test_code_normalization.py
│ ├── test_complete_tool_workflow.py
│ ├── test_conditional_logic_config.py
│ ├── test_config_loading.py
│ ├── test_config_management.py
│ ├── test_config_system.py
│ ├── test_conversion.py
│ ├── test_correct_apis.py
│ ├── test_dashscope_adapter_fix.py
│ ├── test_dashscope_agent_friendly.py
│ ├── test_dashscope_openai_fix.py
│ ├── test_dashscope_quick_fix.py
│ ├── test_dashscope_simple_fix.py
│ ├── test_dashscope_token_tracking.py
│ ├── test_dashscope_tool_call_fix.py
│ ├── test_dashscope_tool_calling_fix.py
│ ├── test_data_config_cli.py
│ ├── test_data_consistency.py
│ ├── test_data_depth_levels.py
│ ├── test_data_sources_comprehensive.py
│ ├── test_data_sources_simple.py
│ ├── test_database_api.py
│ ├── test_dataframe_fix.py
│ ├── test_db_requirements_fix.py
│ ├── test_debate_flow_simulation.py
│ ├── test_decision_data.py
│ ├── test_deepseek_cost_calculation.py
│ ├── test_deepseek_cost_debug.py
│ ├── test_deepseek_cost_fix.py
│ ├── test_deepseek_integration.py
│ ├── test_deepseek_react_fix.py
│ ├── test_deepseek_token_tracking.py
│ ├── test_detailed_data_display.py
│ ├── test_detailed_progress_display.py
│ ├── test_documentation_consistency.py
│ ├── test_duplicate_progress_fix.py
│ ├── test_embedding_models.py
│ ├── test_enhanced_analysis_history.py
│ ├── test_enhanced_screening.py
│ ├── test_env_compatibility.py
│ ├── test_env_config.py
│ ├── test_existing_results.py
│ ├── test_field_config_api.py
│ ├── test_file_loading_debug.py
│ ├── test_final_config.py
│ ├── test_final_integration.py
│ ├── test_final_unified_architecture.py
│ ├── test_final_verification.py
│ ├── test_final_verification_with_config.py
│ ├── test_financial_data_validation.py
│ ├── test_financial_metrics_fix.py
│ ├── test_finnhub_connection.py
│ ├── test_finnhub_fundamentals.py
│ ├── test_finnhub_hk.py
│ ├── test_finnhub_news_fix.py
│ ├── test_fix.py
│ ├── test_fixed_analysis.py
│ ├── test_format_fix.py
│ ├── test_frontend_backend_integration.py
│ ├── test_frontend_display.py
│ ├── test_full_analysis_debug.py
│ ├── test_full_fundamentals_flow.py
│ ├── test_fundamentals_cache.py
│ ├── test_fundamentals_debug.py
│ ├── test_fundamentals_generation.py
│ ├── test_fundamentals_no_duplicate.py
│ ├── test_fundamentals_react_hk_fix.py
│ ├── test_fundamentals_tracking.py
│ ├── test_gemini_25_pro.py
│ ├── test_gemini_final.py
│ ├── test_gemini_simple.py
│ ├── test_google_memory_fix.py
│ ├── test_google_tool_handler_improvements.py
│ ├── test_graph_routing.py
│ ├── test_hk_apis_simple.py
│ ├── test_hk_data_source_fix.py
│ ├── test_hk_error_handling.py
│ ├── test_hk_fundamentals_final.py
│ ├── test_hk_fundamentals_fix.py
│ ├── test_hk_improved.py
│ ├── test_hk_priority.py
│ ├── test_hk_simple.py
│ ├── test_hk_simple_improved.py
│ ├── test_hk_stock_functionality.py
│ ├── test_import.py
│ ├── test_import_fix.py
│ ├── test_improved_hk_utils.py
│ ├── test_industries_api.py
│ ├── test_industry_screening_fix.py
│ ├── test_investment_advice_fix.py
│ ├── test_level3_deadlock_debug.py
│ ├── test_level3_fix.py
│ ├── test_llm_technical_analysis_debug.py
│ ├── test_llm_tool_call.py
│ ├── test_llm_tool_calling_comparison.py
│ ├── test_logging_fix.py
│ ├── test_login_api.py
│ ├── test_market_analyst_fix.py
│ ├── test_market_analyst_lookback.py
│ ├── test_middleware.py
│ ├── test_model_config.py
│ ├── test_mongodb_check.py
│ ├── test_mongodb_save.py
│ ├── test_news_analyst_fix.py
│ ├── test_news_analyst_integration.py
│ ├── test_news_filtering.py
│ ├── test_news_timeout_fix.py
│ ├── test_non_blocking.py
│ ├── test_notification_removal.py
│ ├── test_openai_config_fix.py
│ ├── test_operation_logs.py
│ ├── test_optimized_data_depth.py
│ ├── test_optimized_fundamentals.py
│ ├── test_optimized_fundamentals_simple.py
│ ├── test_optimized_prompts.py
│ ├── test_pb_calculation_fix.py
│ ├── test_performance_comparison.py
│ ├── test_profitable_stock.py
│ ├── test_progress.py
│ ├── test_progress_steps.py
│ ├── test_progress_time_calculation.py
│ ├── test_prompt_optimization_effect.py
│ ├── test_pydantic_fix.py
│ ├── test_pypandoc_functionality.py
│ ├── test_query.py
│ ├── test_quick_async.py
│ ├── test_quick_fix.py
│ ├── test_quotes_ingestion.py
│ ├── test_quotes_sync_status.py
│ ├── test_raw_data_display.py
│ ├── test_real_data_levels.py
│ ├── test_real_deepseek_cost.py
│ ├── test_real_estate_api.py
│ ├── test_real_volume_issue.py
│ ├── test_redis_performance.py
│ ├── test_reports_api.py
│ ├── test_reports_fix.py
│ ├── test_request_deduplication.py
│ ├── test_research_depth_5_levels.py
│ ├── test_research_depth_mapping.py
│ ├── test_risk_assessment.py
│ ├── test_sanitize_export.py
│ ├── test_sanitize_real_data.py
│ ├── test_screening_fields.py
│ ├── test_screening_fix.py
│ ├── test_server_config.py
│ ├── test_signal_processing_logging.py
│ ├── test_signal_processor_debug.py
│ ├── test_signal_processor_fix.py
│ ├── test_simple_depth_check.py
│ ├── test_simple_fundamentals.py
│ ├── test_simple_tracking.py
│ ├── test_smart_system.py
│ ├── test_sse_and_worker_config.py
│ ├── test_stock_code_tracking.py
│ ├── test_stock_codes.py
│ ├── test_stock_data_service.py
│ ├── test_stock_info_debug.py
│ ├── test_stock_market_identification.py
│ ├── test_summary_recommendation.py
│ ├── test_symbol_field_fix.py
│ ├── test_sync_control_functions.py
│ ├── test_sync_history_api.py
│ ├── test_sync_history_fix.py
│ ├── test_sync_user_feedback.py
│ ├── test_system_config_summary_sse_queue.py
│ ├── test_system_simple.py
│ ├── test_target_price.py
│ ├── test_time_estimation_display.py
│ ├── test_timezone_fix.py
│ ├── test_tool_binding_fix.py
│ ├── test_tool_call_issue.py
│ ├── test_tool_execution_flow.py
│ ├── test_tool_interception.py
│ ├── test_tool_removal.py
│ ├── test_tool_selection_debug.py
│ ├── test_toolkit_tools.py
│ ├── test_trading_time_logic.py
│ ├── test_tradingagents_runtime_settings.py
│ ├── test_tushare_integration.py
│ ├── test_tushare_unified/
│ │ ├── __init__.py
│ │ ├── test_tushare_provider.py
│ │ └── test_tushare_sync_service.py
│ ├── test_unified_architecture.py
│ ├── test_unified_config.py
│ ├── test_unified_fundamentals.py
│ ├── test_unified_news_tool.py
│ ├── test_us_stock_analysis.py
│ ├── test_user_check.py
│ ├── test_validation_fix.py
│ ├── test_valuation_check.py
│ ├── test_valuation_simple.py
│ ├── test_volume_format.html
│ ├── test_volume_mapping_issue.py
│ ├── test_vscode_config.py
│ ├── test_web_api_akshare.py
│ ├── test_web_config_page.py
│ ├── test_web_fix.py
│ ├── test_web_hk.py
│ ├── test_web_interface.py
│ ├── test_workflow_integration.py
│ ├── testgoogle.py
│ ├── tradingagents/
│ │ └── test_app_cache_toggle.py
│ ├── unit/
│ │ ├── dataflows/
│ │ │ └── test_unified_dataframe.py
│ │ ├── test_stocks_kline_news_api.py
│ │ └── tools/
│ │ └── analysis/
│ │ └── test_indicators_uil.py
│ ├── verify_config.py
│ └── verify_mongodb_data.py
├── tradingagents/
│ ├── __init__.py
│ ├── agents/
│ │ ├── __init__.py
│ │ ├── analysts/
│ │ │ ├── china_market_analyst.py
│ │ │ ├── fundamentals_analyst.py
│ │ │ ├── market_analyst.py
│ │ │ ├── news_analyst.py
│ │ │ └── social_media_analyst.py
│ │ ├── managers/
│ │ │ ├── research_manager.py
│ │ │ └── risk_manager.py
│ │ ├── researchers/
│ │ │ ├── bear_researcher.py
│ │ │ └── bull_researcher.py
│ │ ├── risk_mgmt/
│ │ │ ├── aggresive_debator.py
│ │ │ ├── conservative_debator.py
│ │ │ └── neutral_debator.py
│ │ ├── trader/
│ │ │ └── trader.py
│ │ └── utils/
│ │ ├── agent_states.py
│ │ ├── agent_utils.py
│ │ ├── chromadb_config.py
│ │ ├── google_tool_handler.py
│ │ └── memory.py
│ ├── api/
│ │ └── stock_api.py
│ ├── config/
│ │ ├── __init__.py
│ │ ├── config_manager.py
│ │ ├── database_config.py
│ │ ├── database_manager.py
│ │ ├── env_utils.py
│ │ ├── mongodb_storage.py
│ │ ├── providers_config.py
│ │ ├── runtime_settings.py
│ │ ├── tushare_config.py
│ │ └── usage_models.py
│ ├── constants/
│ │ ├── __init__.py
│ │ └── data_sources.py
│ ├── dataflows/
│ │ ├── README.md
│ │ ├── __init__.py
│ │ ├── _compat_imports.py
│ │ ├── cache/
│ │ │ ├── __init__.py
│ │ │ ├── adaptive.py
│ │ │ ├── app_adapter.py
│ │ │ ├── data_cache/
│ │ │ │ └── us_fundamentals/
│ │ │ │ └── 300750.SZ_fundamentals_a1cc6e9ff077.txt
│ │ │ ├── db_cache.py
│ │ │ ├── file_cache.py
│ │ │ ├── integrated.py
│ │ │ └── mongodb_cache_adapter.py
│ │ ├── data_completeness_checker.py
│ │ ├── data_source_manager.py
│ │ ├── interface.py
│ │ ├── news/
│ │ │ ├── __init__.py
│ │ │ ├── chinese_finance.py
│ │ │ ├── google_news.py
│ │ │ ├── realtime_news.py
│ │ │ └── reddit.py
│ │ ├── optimized_china_data.py
│ │ ├── providers/
│ │ │ ├── __init__.py
│ │ │ ├── base_provider.py
│ │ │ ├── china/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── akshare.py
│ │ │ │ ├── baostock.py
│ │ │ │ ├── fundamentals_snapshot.py
│ │ │ │ └── tushare.py
│ │ │ ├── examples/
│ │ │ │ ├── __init__.py
│ │ │ │ └── example_sdk.py
│ │ │ ├── hk/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── hk_stock.py
│ │ │ │ └── improved_hk.py
│ │ │ └── us/
│ │ │ ├── __init__.py
│ │ │ ├── alpha_vantage_common.py
│ │ │ ├── alpha_vantage_fundamentals.py
│ │ │ ├── alpha_vantage_news.py
│ │ │ ├── finnhub.py
│ │ │ ├── optimized.py
│ │ │ └── yfinance.py
│ │ ├── realtime_metrics.py
│ │ ├── realtime_news_utils.py
│ │ ├── stock_api.py
│ │ ├── stock_data_service.py
│ │ └── technical/
│ │ ├── __init__.py
│ │ └── stockstats.py
│ ├── default_config.py
│ ├── graph/
│ │ ├── __init__.py
│ │ ├── conditional_logic.py
│ │ ├── propagation.py
│ │ ├── reflection.py
│ │ ├── setup.py
│ │ ├── signal_processing.py
│ │ └── trading_graph.py
│ ├── llm_adapters/
│ │ ├── __init__.py
│ │ ├── dashscope_openai_adapter.py
│ │ ├── deepseek_adapter.py
│ │ ├── google_openai_adapter.py
│ │ └── openai_compatible_base.py
│ ├── models/
│ │ └── stock_data_models.py
│ ├── tools/
│ │ ├── analysis/
│ │ │ └── indicators.py
│ │ └── unified_news_tool.py
│ └── utils/
│ ├── dataflow_utils.py
│ ├── enhanced_news_filter.py
│ ├── enhanced_news_retriever.py
│ ├── logging_init.py
│ ├── logging_manager.py
│ ├── news_filter.py
│ ├── news_filter_integration.py
│ ├── stock_utils.py
│ ├── stock_validator.py
│ └── tool_logging.py
├── utils/
│ ├── check_version_consistency.py
│ ├── cleanup_unnecessary_dirs.py
│ ├── data_config.py
│ ├── fundamentals_analysis_fix.md
│ └── update_data_source_references.py
└── web/
├── CACHE_CLEANING_GUIDE.md
├── README.md
├── app.py
├── components/
│ ├── __init__.py
│ ├── analysis_form.py
│ ├── analysis_results.py
│ ├── async_progress_display.py
│ ├── header.py
│ ├── login.py
│ ├── operation_logs.py
│ ├── results_display.py
│ ├── sidebar.py
│ └── user_activity_dashboard.py
├── config/
│ └── USER_MANAGEMENT.md
├── modules/
│ ├── cache_management.py
│ ├── config_management.py
│ ├── database_management.py
│ └── token_statistics.py
├── run_web.py
└── utils/
├── __init__.py
├── analysis_runner.py
├── api_checker.py
├── async_progress_tracker.py
├── auth_manager.py
├── cookie_manager.py
├── docker_pdf_adapter.py
├── file_session_manager.py
├── mongodb_report_manager.py
├── persistence.py
├── progress_log_handler.py
├── progress_tracker.py
├── redis_session_manager.py
├── report_exporter.py
├── session_persistence.py
├── smart_session_manager.py
├── thread_tracker.py
├── ui_utils.py
└── user_activity_logger.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .dockerignore
================================================
# TradingAgents-CN Docker构建忽略文件
# 用于减小Docker镜像大小和加快构建速度
#
# 注意:此文件同时用于后端和前端镜像构建
# 前端构建需要保留 frontend/ 目录下的源代码和配置文件
# Git相关
.git
.gitignore
.gitattributes
# Python相关
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
.pytest_cache/
.coverage
.coverage.*
htmlcov/
.tox/
.hypothesis/
.mypy_cache/
.dmypy.json
dmypy.json
# 虚拟环境
venv/
.venv/
ENV/
env/
# 环境变量文件(敏感信息,不应打包到镜像)
.env
.env.local
.env.*.local
# Node.js相关
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
.npm
.eslintcache
.node_repl_history
*.tgz
.yarn-integrity
# 前端构建产物(在Dockerfile中会重新构建)
# 注意:只排除构建产物和node_modules,不排除源代码
frontend/dist/
frontend/node_modules/
frontend/.vite/
frontend/coverage/
# IDE和编辑器
.vscode/
.idea/
*.swp
*.swo
*~
.DS_Store
*.sublime-project
*.sublime-workspace
# 日志文件
logs/
*.log
log/
# 数据文件
data/
*.db
*.sqlite
*.sqlite3
# 临时文件
tmp/
temp/
*.tmp
# 测试相关(排除根目录的测试,但保留frontend/src下的测试文件)
tests/
test/
coverage/
# 前端测试文件在构建时会被排除,这里不需要特别处理
# 文档(保留部署与前端需要的文档)
# 默认忽略所有 Markdown,但为前端构建需要的目录开白名单
*.md
!README.md
!docs/docker_deployment_guide.md
!docs/auth_system_improvement.md
!docs/learning/**
!docs/paper/**
# Docker相关
Dockerfile.legacy
docker-compose.yml
docker-compose.split.yml
docker-compose.*.yml
!docker-compose.v1.0.0.yml
# 脚本(保留Python脚本,排除Shell脚本)
# scripts/ - 注释掉,因为需要Python初始化脚本
scripts/*.sh
scripts/*.ps1
scripts/build-and-publish-*.sh
scripts/full_redeploy_*.sh
# 配置示例文件
.env.example
*.example
# 其他配置文件
.editorconfig
.prettierrc
# 注意:不排除 tsconfig.json、vite.config.js 等,因为前端构建需要这些文件
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: 🐛 Bug报告 / Bug Report
about: 报告一个问题帮助我们改进 / Report a bug to help us improve
title: '[BUG] '
labels: ['bug', 'needs-triage']
assignees: ''
---
## 🐛 问题描述 / Bug Description
**问题类型 / Issue Type:**
- [ ] 🚀 启动/安装问题 / Startup/Installation Issue
- [ ] 🌐 Web界面问题 / Web Interface Issue
- [ ] 💻 CLI工具问题 / CLI Tool Issue
- [ ] 🤖 LLM调用问题 / LLM API Issue
- [ ] 📊 数据获取问题 / Data Acquisition Issue
- [ ] 🐳 Docker部署问题 / Docker Deployment Issue
- [ ] ⚙️ 配置问题 / Configuration Issue
- [ ] 🔄 功能异常 / Feature Malfunction
- [ ] 🐌 性能问题 / Performance Issue
- [ ] 其他 / Other: ___________
**简要描述问题 / Brief description:**
清晰简洁地描述遇到的问题。
**期望行为 / Expected behavior:**
描述您期望发生的行为。
**实际行为 / Actual behavior:**
描述实际发生的行为。
## 🔄 复现步骤 / Steps to Reproduce
请提供详细的复现步骤:
1. 进入 '...'
2. 点击 '....'
3. 滚动到 '....'
4. 看到错误
## 📱 环境信息 / Environment
**系统信息 / System Info:**
- 操作系统 / OS: [例如 Windows 11, macOS 13, Ubuntu 22.04]
- Python版本 / Python Version: [例如 3.10.0]
- 项目版本 / Project Version: [例如 v0.1.6]
**安装方式 / Installation Method:**
- [ ] 本地安装 / Local Installation
- [ ] Docker部署 / Docker Deployment
- [ ] 其他 / Other: ___________
**依赖版本 / Dependencies:**
```bash
# 请运行以下命令并粘贴结果 / Please run the following command and paste the result
pip list | grep -E "(streamlit|langchain|openai|requests|tushare|akshare|baostock)"
```
**浏览器信息 / Browser Info (仅Web界面问题):**
- 浏览器 / Browser: [例如 Chrome 120, Firefox 121, Safari 17]
- 浏览器版本 / Version:
- 是否使用无痕模式 / Incognito mode: [ ] 是 / Yes [ ] 否 / No
## 📊 配置信息 / Configuration
**API配置 / API Configuration:**
- [ ] 已配置Tushare Token
- [ ] 已配置DeepSeek API Key
- [ ] 已配置DashScope API Key
- [ ] 已配置FinnHub API Key
- [ ] 已配置数据库 / Database configured
**数据源 / Data Sources:**
- 中国股票数据源 / Chinese Stock Source: [tushare/akshare/baostock]
- 美股数据源 / US Stock Source: [finnhub/yfinance]
## 📝 错误日志 / Error Logs
**控制台错误 / Console Errors:**
```
请粘贴完整的错误信息和堆栈跟踪
Please paste the complete error message and stack trace
```
**日志文件 / Log Files:**
```bash
# 如果启用了日志记录,请提供相关日志
# If logging is enabled, please provide relevant logs
# Web应用日志 / Web app logs
tail -n 50 logs/tradingagents.log
# Docker日志 / Docker logs
docker-compose logs web
```
**网络请求错误 / Network Request Errors:**
如果是API调用问题,请提供:
- API响应状态码 / API response status code
- 错误响应内容 / Error response content
- 请求参数(隐藏敏感信息)/ Request parameters (hide sensitive info)
## 📸 截图 / Screenshots
如果适用,请添加截图来帮助解释问题。
If applicable, add screenshots to help explain your problem.
## 🔍 额外信息 / Additional Context
添加任何其他有关问题的上下文信息。
Add any other context about the problem here.
## ✅ 检查清单 / Checklist
请确认您已经:
- [ ] 搜索了现有的issues,确认这不是重复问题
- [ ] 使用了最新版本的代码
- [ ] 提供了完整的错误信息
- [ ] 包含了复现步骤
- [ ] 填写了环境信息
---
**感谢您的反馈!我们会尽快处理这个问题。**
**Thank you for your feedback! We will address this issue as soon as possible.**
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
- name: 📖 项目文档 / Project Documentation
url: https://github.com/hsliuping/TradingAgents-CN/blob/main/README.md
about: 查看完整的项目文档和使用指南 / View complete project documentation and usage guide
- name: 🐳 Docker部署指南 / Docker Deployment Guide
url: https://github.com/hsliuping/TradingAgents-CN/blob/main/docs/DOCKER_GUIDE.md
about: Docker容器化部署的详细指南 / Detailed guide for Docker containerized deployment
- name: 💬 讨论区 / Discussions
url: https://github.com/hsliuping/TradingAgents-CN/discussions
about: 技术讨论、想法分享和社区交流 / Technical discussions, idea sharing and community communication
- name: 📧 邮件联系 / Email Contact
url: mailto:hsliup@163.com
about: 直接邮件联系项目维护者 / Direct email contact with project maintainer
- name: 🌟 源项目 / Original Project
url: https://github.com/TauricResearch/TradingAgents
about: 查看原始的TradingAgents项目 / View the original TradingAgents project
================================================
FILE: .github/ISSUE_TEMPLATE/documentation.md
================================================
---
name: 📚 文档改进 / Documentation Improvement
about: 报告文档问题或建议改进 / Report documentation issues or suggest improvements
title: '[DOCS] '
labels: ['documentation', 'good-first-issue']
assignees: ''
---
## 📚 文档问题 / Documentation Issue
**问题类型 / Issue Type:**
- [ ] 🐛 文档错误 / Documentation Error
- [ ] 📝 内容缺失 / Missing Content
- [ ] 🔄 内容过时 / Outdated Content
- [ ] 🌐 翻译问题 / Translation Issue
- [ ] 💡 改进建议 / Improvement Suggestion
- [ ] 🎨 格式问题 / Formatting Issue
- [ ] 🔗 链接失效 / Broken Links
- [ ] 其他 / Other: ___________
## 📍 文档位置 / Document Location
**文件路径 / File Path:**
请指明具体的文档文件和位置。
```
例如: README.md 第123行
例如: docs/DOCKER_GUIDE.md 安装部分
```
**相关链接 / Related Links:**
如果是在线文档,请提供链接。
## 🔍 问题详情 / Issue Details
**当前内容 / Current Content:**
请引用或描述有问题的当前内容。
**问题描述 / Problem Description:**
详细描述文档中的问题。
**建议修改 / Suggested Changes:**
请提供您建议的修改内容。
## 💡 改进建议 / Improvement Suggestions
**缺失内容 / Missing Content:**
如果是内容缺失,请描述需要添加的内容。
**目标读者 / Target Audience:**
- [ ] 🆕 新手用户 / Beginner Users
- [ ] 👨💻 开发者 / Developers
- [ ] 🔧 系统管理员 / System Administrators
- [ ] 🎓 学习者 / Learners
- [ ] 所有用户 / All Users
**内容类型 / Content Type:**
- [ ] 📖 使用教程 / Usage Tutorial
- [ ] ⚙️ 安装指南 / Installation Guide
- [ ] 🔧 配置说明 / Configuration Instructions
- [ ] 🐳 Docker部署 / Docker Deployment
- [ ] 🤖 API文档 / API Documentation
- [ ] 💡 最佳实践 / Best Practices
- [ ] 🔍 故障排除 / Troubleshooting
- [ ] 📊 示例代码 / Code Examples
- [ ] 其他 / Other: ___________
## 🌐 多语言支持 / Multi-language Support
**语言问题 / Language Issues:**
- [ ] 中文翻译错误 / Chinese translation error
- [ ] 英文翻译错误 / English translation error
- [ ] 术语不一致 / Inconsistent terminology
- [ ] 缺少翻译 / Missing translation
**建议翻译 / Suggested Translation:**
如果是翻译问题,请提供正确的翻译。
## 📝 具体修改建议 / Specific Change Suggestions
**修改前 / Before:**
```markdown
当前的文档内容
Current documentation content
```
**修改后 / After:**
```markdown
建议的修改内容
Suggested modified content
```
## 🎯 用户体验 / User Experience
**遇到困难的场景 / Problematic Scenario:**
描述用户在什么情况下会遇到这个文档问题。
**期望的用户体验 / Expected User Experience:**
描述理想的用户阅读体验。
## 📊 优先级 / Priority
**重要性 / Importance:**
- [ ] 🔥 高优先级 / High Priority - 严重影响用户使用
- [ ] 🟡 中优先级 / Medium Priority - 影响用户体验
- [ ] 🟢 低优先级 / Low Priority - 小幅改进
**影响范围 / Impact Scope:**
- [ ] 🌍 影响所有用户 / Affects all users
- [ ] 👥 影响特定用户群 / Affects specific user group
- [ ] 🔧 影响开发者 / Affects developers
- [ ] 📱 影响特定平台 / Affects specific platform
## 🔗 相关资源 / Related Resources
**参考文档 / Reference Documentation:**
- 相关的官方文档
- 类似项目的文档示例
- 技术标准或规范
**相关Issues / Related Issues:**
- 相关的文档问题: #
- 相关的功能请求: #
## ✅ 检查清单 / Checklist
请确认您已经:
- [ ] 明确指出了文档位置
- [ ] 详细描述了问题
- [ ] 提供了改进建议
- [ ] 考虑了目标读者
- [ ] 检查了相关文档
## 🤝 贡献意愿 / Contribution Willingness
**是否愿意贡献 / Willing to Contribute:**
- [ ] ✅ 我愿意提交PR修复这个文档问题
- [ ] 📝 我可以提供内容,但需要他人协助格式化
- [ ] 💡 我只是提供建议,希望他人实施
- [ ] 🌐 我可以协助翻译工作
---
**感谢您帮助改进项目文档!**
**Thank you for helping improve the project documentation!**
## 📖 文档贡献指南 / Documentation Contribution Guide
1. **Fork项目** / Fork the project
2. **创建分支** / Create a branch: `git checkout -b docs/improve-xxx`
3. **修改文档** / Edit documentation
4. **提交PR** / Submit PR
5. **等待审核** / Wait for review
**文档规范 / Documentation Standards:**
- 使用Markdown格式
- 保持中英文对照
- 添加适当的示例
- 确保链接有效
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: ✨ 功能请求 / Feature Request
about: 建议一个新功能或改进 / Suggest a new feature or improvement
title: '[FEATURE] '
labels: ['enhancement', 'needs-discussion']
assignees: ''
---
## ✨ 功能描述 / Feature Description
**简要描述 / Brief description:**
清晰简洁地描述您想要的功能。
**详细说明 / Detailed description:**
详细描述这个功能应该如何工作。
## 🎯 使用场景 / Use Case
**问题背景 / Problem:**
这个功能请求是否与某个问题相关?请描述。
Is your feature request related to a problem? Please describe.
**解决方案 / Solution:**
描述您希望看到的解决方案。
Describe the solution you'd like.
**使用示例 / Usage Example:**
提供一个具体的使用示例。
```python
# 示例代码
example_code_here()
```
## 💡 实现建议 / Implementation Suggestions
**技术方案 / Technical Approach:**
如果您有技术实现的想法,请分享。
**相关组件 / Related Components:**
- [ ] 数据获取 / Data Acquisition
- [ ] LLM集成 / LLM Integration
- [ ] 分析引擎 / Analysis Engine
- [ ] Web界面 / Web Interface
- [ ] CLI工具 / CLI Tools
- [ ] 数据库 / Database
- [ ] 配置管理 / Configuration
- [ ] 其他 / Other: ___________
## 🔄 替代方案 / Alternatives
**其他解决方案 / Alternative solutions:**
描述您考虑过的其他替代解决方案。
**现有工具 / Existing tools:**
是否有其他工具或项目已经实现了类似功能?
## 📊 优先级 / Priority
**重要性 / Importance:**
- [ ] 🔥 高优先级 / High Priority - 核心功能缺失
- [ ] 🟡 中优先级 / Medium Priority - 重要改进
- [ ] 🟢 低优先级 / Low Priority - 便利性功能
**紧急性 / Urgency:**
- [ ] 🚨 紧急 / Urgent - 阻塞当前工作
- [ ] ⏰ 尽快 / Soon - 影响用户体验
- [ ] 📅 可以等待 / Can Wait - 未来版本
## 🎨 界面设计 / UI/UX Design
**界面要求 / UI Requirements:**
如果涉及界面变更,请描述期望的用户体验。
**交互流程 / User Flow:**
描述用户如何与这个功能交互。
## 📈 影响评估 / Impact Assessment
**受益用户 / Target Users:**
- [ ] 新手用户 / Beginner Users
- [ ] 高级用户 / Advanced Users
- [ ] 开发者 / Developers
- [ ] 所有用户 / All Users
**预期收益 / Expected Benefits:**
- 提升性能 / Performance improvement
- 增强易用性 / Better usability
- 扩展功能 / Extended functionality
- 其他 / Other: ___________
## 🔗 相关资源 / Related Resources
**参考链接 / References:**
- 相关文档 / Documentation:
- 类似项目 / Similar projects:
- 技术资料 / Technical resources:
**相关Issues / Related Issues:**
- 关联的bug报告 / Related bug reports: #
- 相关功能请求 / Related feature requests: #
## 📝 额外信息 / Additional Context
添加任何其他有关功能请求的上下文、截图或示例。
Add any other context, screenshots, or examples about the feature request here.
## ✅ 检查清单 / Checklist
请确认您已经:
- [ ] 搜索了现有的issues,确认这不是重复请求
- [ ] 清晰地描述了功能需求
- [ ] 提供了使用场景和示例
- [ ] 考虑了实现的可行性
- [ ] 评估了功能的优先级
---
**感谢您的建议!我们会认真考虑这个功能请求。**
**Thank you for your suggestion! We will carefully consider this feature request.**
================================================
FILE: .github/ISSUE_TEMPLATE/question.md
================================================
---
name: ❓ 问题咨询 / Question
about: 使用问题或技术咨询 / Usage questions or technical consultation
title: '[QUESTION] '
labels: ['question', 'help-wanted']
assignees: ''
---
## ❓ 问题描述 / Question Description
**您的问题 / Your Question:**
清晰地描述您想要了解的问题。
**问题类型 / Question Type:**
- [ ] 🚀 安装和配置 / Installation & Configuration
- [ ] 🔧 使用方法 / Usage Instructions
- [ ] 🤖 LLM配置 / LLM Configuration
- [ ] 📊 数据源设置 / Data Source Setup
- [ ] 🐳 Docker部署 / Docker Deployment
- [ ] 🔍 功能理解 / Feature Understanding
- [ ] 💡 最佳实践 / Best Practices
- [ ] 🔄 故障排除 / Troubleshooting
- [ ] 其他 / Other: ___________
## 🎯 具体场景 / Specific Scenario
**使用场景 / Use Case:**
描述您想要实现的具体场景或目标。
**当前状态 / Current Status:**
描述您目前的进展和遇到的困难。
## 🔧 环境信息 / Environment Info
**系统环境 / System:**
- 操作系统 / OS: [Windows/macOS/Linux]
- Python版本 / Python Version:
- 项目版本 / Project Version:
**安装方式 / Installation:**
- [ ] 本地安装 / Local Installation
- [ ] Docker部署 / Docker Deployment
**配置状态 / Configuration Status:**
- [ ] 已配置API密钥 / API keys configured
- [ ] 已配置数据库 / Database configured
- [ ] 已测试基本功能 / Basic functions tested
## 📝 已尝试的方法 / What You've Tried
**尝试过的解决方案 / Attempted Solutions:**
请描述您已经尝试过的方法。
**参考的文档 / Referenced Documentation:**
- [ ] README.md
- [ ] Docker部署指南 / Docker Guide
- [ ] 项目文档 / Project Documentation
- [ ] 其他资源 / Other resources: ___________
## 🔍 期望的帮助 / Expected Help
**希望得到的帮助 / What help you need:**
- [ ] 📖 使用指导 / Usage guidance
- [ ] 🔧 配置帮助 / Configuration help
- [ ] 💡 解决方案建议 / Solution suggestions
- [ ] 📚 相关文档推荐 / Documentation recommendations
- [ ] 🎯 最佳实践分享 / Best practices sharing
- [ ] 其他 / Other: ___________
## 📊 相关信息 / Related Information
**错误信息 / Error Messages:**
如果有错误信息,请粘贴完整内容。
```
错误信息粘贴在这里
Error messages here
```
**配置文件 / Configuration:**
如果相关,请分享您的配置(请隐藏敏感信息如API密钥)。
```bash
# 示例配置(请隐藏敏感信息)
TRADINGAGENTS_CHINA_DATA_SOURCE=tushare
TRADINGAGENTS_US_DATA_SOURCE=finnhub
# ... 其他配置
```
## 📸 截图 / Screenshots
如果有助于说明问题,请添加截图。
If helpful, please add screenshots.
## 🔗 相关链接 / Related Links
**相关Issues / Related Issues:**
如果有相关的issues,请链接。
**参考资料 / References:**
您查阅过的相关资料或文档。
## ✅ 检查清单 / Checklist
请确认您已经:
- [ ] 查阅了项目文档和README
- [ ] 搜索了现有的issues
- [ ] 提供了足够的上下文信息
- [ ] 描述了具体的使用场景
- [ ] 说明了已尝试的解决方法
---
**我们会尽快回复您的问题!**
**We will respond to your question as soon as possible!**
## 💡 快速帮助 / Quick Help
**常见问题 / FAQ:**
- 📖 [项目文档](../docs/)
- 🐳 [Docker部署指南](../docs/DOCKER_GUIDE.md)
- 🚀 [快速开始指南](../README.md#🚀-启动应用)
- ⚙️ [配置说明](../README.md#配置api密钥)
**社区支持 / Community Support:**
- 💬 [GitHub Discussions](https://github.com/hsliuping/TradingAgents-CN/discussions)
- 📧 邮箱: hsliup@163.com
================================================
FILE: .github/pull_request_template.md
================================================
# Pull Request 模板
## 📋 PR 类型
请标记此 PR 的类型:
- [ ] 🌟 新功能 (feature)
- [ ] 🐛 Bug 修复 (bugfix)
- [ ] 🧹 代码重构 (refactor)
- [ ] 📝 文档更新 (documentation)
- [ ] 🎨 样式优化 (style)
- [ ] ⚡ 性能优化 (performance)
- [ ] 🔧 配置/构建 (config/build)
- [ ] 🧪 测试相关 (test)
- [ ] 🤖 LLM 适配器集成 (llm-adapter)
## 📖 PR 描述
### 变更摘要
<!-- 请简要描述此 PR 的主要变更 -->
### 变更详情
<!-- 请详细描述具体的改动内容 -->
### 相关 Issue
<!-- 如果此 PR 解决了某个 Issue,请链接:Fixes #issue_number -->
## 🤖 LLM 适配器集成检查清单
> **注意**: 如果此 PR 涉及 LLM 适配器集成,请完成以下检查清单。如果不涉及,可以跳过此部分。
### ✅ 代码实现检查
- [ ] **适配器类实现**
- [ ] 创建了继承自 `OpenAICompatibleBase` 的适配器类
- [ ] 正确设置了 `provider_name`、`api_key_env_var`、`base_url`
- [ ] 实现了必要的模型配置
- [ ] **注册和集成**
- [ ] 在 `OPENAI_COMPATIBLE_PROVIDERS` 字典中注册了提供商
- [ ] 在 `__init__.py` 中添加了适配器导出
- [ ] 在前端 `sidebar.py` 中添加了提供商选项
- [ ] **环境变量配置**
- [ ] 在 `.env.example` 中添加了 API Key 示例
- [ ] 环境变量命名遵循 `{PROVIDER}_API_KEY` 格式
- [ ] 提供了正确的 `base_url` 配置
### ✅ 测试和验证
- [ ] **基础功能测试**
- [ ] API 连接测试通过
- [ ] 简单文本生成功能正常
- [ ] 错误处理机制有效
- [ ] **工具调用测试**
- [ ] Function calling 功能正常工作
- [ ] 工具参数解析正确
- [ ] 复杂工具调用场景稳定
- [ ] **集成测试**
- [ ] 前端界面显示正常
- [ ] 模型选择器工作正确
- [ ] TradingGraph 集成成功
- [ ] 端到端分析流程正常
- [ ] **性能和稳定性测试**
- [ ] 响应时间合理(< 30秒)
- [ ] 连续运行测试通过(> 30分钟)
- [ ] 内存使用稳定
- [ ] 并发请求处理正确
### ✅ 文档和配置
- [ ] **代码文档**
- [ ] 适配器类包含完整的 docstring
- [ ] 关键方法有适当的注释
- [ ] 参数说明清晰
- [ ] **用户文档**
- [ ] 更新了相关的用户指南(如果需要)
- [ ] 提供了配置示例
- [ ] 包含故障排除信息(如果适用)
### 📝 测试报告
如果这是 LLM 适配器 PR,请提供以下信息:
**提供商信息**:
- 提供商名称:
- 官方网站:
- API 文档:
- 支持的模型:
**测试结果**:
- 基础连接: ✅/❌
- 工具调用: ✅/❌
- Web 集成: ✅/❌
- 端到端: ✅/❌
**性能指标**:
- 平均响应时间: ___ 秒
- 工具调用成功率: ___%
- 内存使用: ___ MB
**已知问题**:
<!-- 列出任何已知的问题或限制 -->
## 🧪 测试说明
### 如何测试此 PR
<!-- 请提供测试此 PR 的步骤 -->
1.
2.
3.
### 测试环境
- [ ] 本地开发环境
- [ ] Docker 环境
- [ ] 生产环境
### 破坏性变更
- [ ] 此 PR 包含破坏性变更
- [ ] 此 PR 不包含破坏性变更
如果包含破坏性变更,请说明:
<!-- 描述破坏性变更的影响和迁移指南 -->
## 📊 影响范围
请标记此 PR 影响的组件:
- [ ] 核心交易逻辑
- [ ] LLM 适配器
- [ ] Web 界面
- [ ] 数据获取
- [ ] 配置系统
- [ ] 测试框架
- [ ] 文档
- [ ] 部署配置
## 🔗 相关链接
- 相关文档:
- 参考资料:
- 相关 PR:
## 📷 截图/演示
<!-- 如果涉及 UI 变更,请提供截图或演示视频 -->
## ✅ 检查清单
请确认以下项目:
### 代码质量
- [ ] 代码遵循项目的编码规范
- [ ] 没有不必要的调试代码或注释
- [ ] 变量和函数命名清晰明确
- [ ] 代码复用性良好,避免重复代码
### 测试覆盖
- [ ] 新功能有相应的测试用例
- [ ] 所有测试通过
- [ ] 手动测试已完成
- [ ] 边界情况已考虑
### 文档更新
- [ ] README 已更新(如果需要)
- [ ] API 文档已更新(如果需要)
- [ ] 变更日志已更新(如果需要)
- [ ] 配置文档已更新(如果需要)
### 安全考虑
- [ ] 没有硬编码的密钥或敏感信息
- [ ] 输入验证充分
- [ ] 错误处理不泄露敏感信息
- [ ] 第三方依赖安全可靠
### 性能考虑
- [ ] 新功能不会显著影响性能
- [ ] 内存使用合理
- [ ] 网络请求优化
- [ ] 数据库查询优化(如果适用)
## 🏷️ 标签建议
请为此 PR 建议适当的标签:
- [ ] `enhancement` - 新功能或改进
- [ ] `bug` - Bug 修复
- [ ] `documentation` - 文档相关
- [ ] `refactor` - 代码重构
- [ ] `performance` - 性能优化
- [ ] `security` - 安全相关
- [ ] `llm-adapter` - LLM 适配器
- [ ] `ui/ux` - 用户界面/体验
- [ ] `config` - 配置相关
- [ ] `testing` - 测试相关
## 👥 审查者
建议的审查者:
<!-- @mention 建议的审查者 -->
## 📝 额外说明
<!-- 任何其他需要审查者知道的信息 -->
---
**感谢您的贡献!** 🎉
请确保您已经阅读并遵循了我们的 [贡献指南](../docs/LLM_INTEGRATION_GUIDE.md)。如果您有任何问题,请随时在 PR 中提问或联系维护者。
================================================
FILE: .github/workflows/docker-publish.yml
================================================
name: Docker Publish to Docker Hub
on:
push:
tags:
- 'v*'
workflow_dispatch:
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
with:
platforms: linux/amd64,linux/arm64
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
platforms: linux/amd64,linux/arm64
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Extract metadata for backend
id: meta-backend
uses: docker/metadata-action@v5
with:
images: ${{ secrets.DOCKERHUB_USERNAME }}/tradingagents-backend
tags: |
type=ref,event=tag
type=raw,value=latest
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
- name: Build and push backend image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile.backend
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta-backend.outputs.tags }}
labels: ${{ steps.meta-backend.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Extract metadata for frontend
id: meta-frontend
uses: docker/metadata-action@v5
with:
images: ${{ secrets.DOCKERHUB_USERNAME }}/tradingagents-frontend
tags: |
type=ref,event=tag
type=raw,value=latest
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
- name: Build and push frontend image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile.frontend
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta-frontend.outputs.tags }}
labels: ${{ steps.meta-frontend.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Summary
run: |
echo "## Docker Images Published 🚀" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Multi-Architecture Support" >> $GITHUB_STEP_SUMMARY
echo "✅ linux/amd64 (Intel/AMD x86_64)" >> $GITHUB_STEP_SUMMARY
echo "✅ linux/arm64 (Apple Silicon, Raspberry Pi, AWS Graviton)" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Backend Image" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo "${{ steps.meta-backend.outputs.tags }}" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Frontend Image" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo "${{ steps.meta-frontend.outputs.tags }}" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Usage" >> $GITHUB_STEP_SUMMARY
echo '```bash' >> $GITHUB_STEP_SUMMARY
echo "# Pull images (Docker will automatically select the correct architecture)" >> $GITHUB_STEP_SUMMARY
echo "docker pull ${{ secrets.DOCKERHUB_USERNAME }}/tradingagents-backend:latest" >> $GITHUB_STEP_SUMMARY
echo "docker pull ${{ secrets.DOCKERHUB_USERNAME }}/tradingagents-frontend:latest" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "# Run with docker-compose" >> $GITHUB_STEP_SUMMARY
echo "docker-compose -f docker-compose.hub.yml up -d" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Verify Architecture" >> $GITHUB_STEP_SUMMARY
echo '```bash' >> $GITHUB_STEP_SUMMARY
echo "docker buildx imagetools inspect ${{ secrets.DOCKERHUB_USERNAME }}/tradingagents-backend:latest" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
================================================
FILE: .github/workflows/upstream-sync-check.yml
================================================
name: 上游同步检查
on:
schedule:
# 每周一上午9点检查上游更新
- cron: '0 9 * * 1'
workflow_dispatch:
# 允许手动触发
inputs:
force_sync:
description: '强制同步(跳过确认)'
required: false
default: 'false'
type: boolean
jobs:
check-upstream:
runs-on: ubuntu-latest
name: 检查上游更新
steps:
- name: 检出代码
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: 设置Python环境
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: 安装依赖
run: |
python -m pip install --upgrade pip
pip install requests
- name: 配置Git
run: |
git config --global user.name 'GitHub Actions'
git config --global user.email 'actions@github.com'
- name: 添加上游仓库
run: |
git remote add upstream https://github.com/TauricResearch/TradingAgents.git || true
git fetch upstream
- name: 检查上游更新
id: check_updates
run: |
# 获取上游新提交数量
NEW_COMMITS=$(git rev-list --count HEAD..upstream/main)
echo "new_commits=$NEW_COMMITS" >> $GITHUB_OUTPUT
if [ "$NEW_COMMITS" -gt 0 ]; then
echo "has_updates=true" >> $GITHUB_OUTPUT
echo "发现 $NEW_COMMITS 个新提交"
# 获取最新提交信息
git log --oneline --no-merges HEAD..upstream/main | head -10 > recent_commits.txt
echo "recent_commits<<EOF" >> $GITHUB_OUTPUT
cat recent_commits.txt >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
else
echo "has_updates=false" >> $GITHUB_OUTPUT
echo "没有新的上游更新"
fi
- name: 分析更新类型
if: steps.check_updates.outputs.has_updates == 'true'
id: analyze_updates
run: |
# 分析提交类型
FEATURES=$(git log --oneline --no-merges HEAD..upstream/main | grep -i -E "(feat|feature|add)" | wc -l)
FIXES=$(git log --oneline --no-merges HEAD..upstream/main | grep -i -E "(fix|bug|patch)" | wc -l)
DOCS=$(git log --oneline --no-merges HEAD..upstream/main | grep -i -E "(doc|readme)" | wc -l)
echo "features=$FEATURES" >> $GITHUB_OUTPUT
echo "fixes=$FIXES" >> $GITHUB_OUTPUT
echo "docs=$DOCS" >> $GITHUB_OUTPUT
# 判断更新优先级
if [ "$FIXES" -gt 0 ]; then
echo "priority=high" >> $GITHUB_OUTPUT
echo "reason=包含Bug修复" >> $GITHUB_OUTPUT
elif [ "$FEATURES" -gt 2 ]; then
echo "priority=medium" >> $GITHUB_OUTPUT
echo "reason=包含多个新功能" >> $GITHUB_OUTPUT
else
echo "priority=low" >> $GITHUB_OUTPUT
echo "reason=常规更新" >> $GITHUB_OUTPUT
fi
- name: 创建Issue报告
if: steps.check_updates.outputs.has_updates == 'true'
uses: actions/github-script@v7
with:
script: |
const newCommits = '${{ steps.check_updates.outputs.new_commits }}';
const recentCommits = `${{ steps.check_updates.outputs.recent_commits }}`;
const features = '${{ steps.analyze_updates.outputs.features }}';
const fixes = '${{ steps.analyze_updates.outputs.fixes }}';
const docs = '${{ steps.analyze_updates.outputs.docs }}';
const priority = '${{ steps.analyze_updates.outputs.priority }}';
const reason = '${{ steps.analyze_updates.outputs.reason }}';
const issueTitle = `🔄 上游更新检测 - ${newCommits} 个新提交`;
const issueBody = `
## 📊 更新概览
- **新提交数量**: ${newCommits}
- **更新优先级**: ${priority.toUpperCase()}
- **优先级原因**: ${reason}
## 📈 更新分析
- 🆕 新功能: ${features} 个
- 🐛 Bug修复: ${fixes} 个
- 📚 文档更新: ${docs} 个
## 📋 最近提交
\`\`\`
${recentCommits}
\`\`\`
## 🎯 建议行动
${priority === 'high' ?
'⚠️ **建议立即同步** - 包含重要的Bug修复' :
priority === 'medium' ?
'📅 **建议本周内同步** - 包含有价值的新功能' :
'📝 **可以计划同步** - 常规更新,可以安排时间同步'
}
## 🔧 同步步骤
1. 检查当前工作状态
2. 运行同步脚本: \`python scripts/sync_upstream.py\`
3. 解决可能的冲突
4. 测试功能完整性
5. 更新相关文档
## 📞 相关链接
- [上游仓库](https://github.com/TauricResearch/TradingAgents)
- [同步策略文档](docs/maintenance/upstream-sync.md)
- [同步脚本](scripts/sync_upstream.py)
---
*此Issue由GitHub Actions自动创建于 ${new Date().toISOString()}*
`;
// 检查是否已有相似的Issue
const existingIssues = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
labels: 'upstream-sync'
});
const hasOpenSyncIssue = existingIssues.data.some(issue =>
issue.title.includes('上游更新检测')
);
if (!hasOpenSyncIssue) {
await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: issueTitle,
body: issueBody,
labels: ['upstream-sync', priority === 'high' ? 'priority-high' : priority === 'medium' ? 'priority-medium' : 'priority-low']
});
console.log('✅ 已创建上游更新Issue');
} else {
console.log('ℹ️ 已存在开放的同步Issue,跳过创建');
}
- name: 发送通知
if: steps.check_updates.outputs.has_updates == 'true'
run: |
echo "📧 上游更新通知已发送"
echo "- 新提交数量: ${{ steps.check_updates.outputs.new_commits }}"
echo "- 更新优先级: ${{ steps.analyze_updates.outputs.priority }}"
echo "- 已创建Issue进行跟踪"
auto-sync:
runs-on: ubuntu-latest
name: 自动同步(仅限低风险更新)
needs: check-upstream
if: github.event.inputs.force_sync == 'true' || (needs.check-upstream.outputs.priority == 'low' && needs.check-upstream.outputs.fixes == '0')
steps:
- name: 检出代码
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: 设置Python环境
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: 配置Git
run: |
git config --global user.name 'GitHub Actions Bot'
git config --global user.email 'actions@github.com'
- name: 添加上游仓库
run: |
git remote add upstream https://github.com/TauricResearch/TradingAgents.git
git fetch upstream
- name: 执行自动同步
run: |
python scripts/sync_upstream.py --auto
- name: 推送更新
run: |
git push origin main
- name: 创建同步报告
uses: actions/github-script@v7
with:
script: |
const reportTitle = '🤖 自动同步完成';
const reportBody = `
## ✅ 自动同步成功
GitHub Actions 已自动完成上游同步。
**同步时间**: ${new Date().toISOString()}
**触发方式**: ${context.eventName === 'workflow_dispatch' ? '手动触发' : '自动触发'}
## 📋 后续建议
1. 检查同步的更改是否正常
2. 运行本地测试验证功能
3. 更新相关文档(如需要)
---
*此报告由GitHub Actions自动生成*
`;
await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: reportTitle,
body: reportBody,
labels: ['upstream-sync', 'auto-sync']
});
================================================
FILE: .python-version
================================================
3.10
================================================
FILE: .streamlit/config.toml
================================================
[server]
# 服务器配置
port = 8501
address = "0.0.0.0" # Docker环境需要监听所有接口
headless = true # Docker环境无头模式
enableCORS = false
enableXsrfProtection = false
# 文件监控配置 - 解决Windows下的文件锁定问题
fileWatcherType = "none"
runOnSave = false
[browser]
# 浏览器配置
gatherUsageStats = false
[logger]
# 日志配置
level = "info"
[global]
# 全局配置
developmentMode = false
[theme]
# 主题配置
base = "light"
primaryColor = "#1f77b4"
backgroundColor = "#ffffff"
secondaryBackgroundColor = "#f0f2f6"
textColor = "#262730"
================================================
FILE: ACKNOWLEDGMENTS.md
================================================
# 致敬与感谢 | Acknowledgments
## 🌟 向源项目开发者致以最崇高的敬意
### [Tauric Research](https://github.com/TauricResearch) 团队
我们向 **Tauric Research** 团队及 **[TradingAgents](https://github.com/TauricResearch/TradingAgents)** 项目的所有贡献者表达最诚挚的敬意和感谢!
#### 🎯 创新贡献与源码价值
**革命性理念**
- 创造了多智能体协作交易的全新范式
- 将AI技术与金融实务完美结合
- 模拟真实交易公司的专业分工和决策流程
**珍贵的源码贡献**
- **🏗️ 核心架构代码**: 感谢您们提供的优雅且可扩展的系统架构源码
- **🤖 智能体实现**: 感谢您们开源的多个专业化AI智能体协作机制代码
- **📊 分析算法**: 感谢您们分享的金融分析和风险管理算法实现
- **🔧 工具链代码**: 感谢您们提供的完整开发工具链和配置代码
- **📚 示例代码**: 感谢您们编写的详细示例和最佳实践代码
**技术突破与代码质量**
- 每一行代码都体现了对金融交易本质的深刻理解
- 代码结构清晰,注释详细,极大降低了学习门槛
- 模块化设计使得扩展和定制变得简单易行
- 完整的错误处理和日志记录展现了工程化的严谨态度
**无私的开源精神**
- 选择Apache 2.0协议,给予开发者最大的使用自由
- 不仅开源代码,更开源了宝贵的设计思想和实现经验
- 持续维护和更新,为社区提供稳定可靠的代码基础
- 积极回应社区反馈,不断改进和完善代码质量
#### 🏗️ 技术架构的卓越设计
感谢您们创建的优秀架构:
- **分析师团队**: 基本面、技术面、新闻面、社交媒体四大专业分析师
- **研究团队**: 多空观点的深度研究和辩论机制
- **交易团队**: 基于研究结果的交易决策执行
- **风险管理**: 多层次的风险评估和控制体系
- **投资组合**: 智能的资产配置和管理策略
这个架构不仅技术先进,更重要的是体现了对金融交易本质的深刻理解。
## 🇨🇳 我们的使命:更好地推广TradingAgents
### 创建初衷
本项目的创建有着明确的使命:**为了更好地在中国推广TradingAgents这个优秀的框架**。
我们深深被TradingAgents的创新理念和技术实力所震撼,同时也意识到语言和技术环境的差异可能会阻碍这个优秀项目在中国的推广和应用。因此,我们决定创建这个中文增强版本。
### 🌉 搭建技术桥梁
#### 语言无障碍
- **完整中文化**: 提供全面的中文文档、界面和提示信息
- **本土化表达**: 使用符合中文用户习惯的术语和表达方式
- **文化适配**: 考虑中文用户的使用习惯和思维方式
#### 技术本土化
- **国产大模型**: 集成阿里百炼、DeepSeek等国产大语言模型
- **网络环境**: 适应国内网络环境,无需翻墙即可使用
- **数据源集成**: 支持Tushare、AkShare等中文金融数据源
#### 社区建设
- **中文社区**: 为中文开发者提供交流和学习平台
- **技术分享**: 分享AI金融技术的最佳实践和应用经验
- **人才培养**: 帮助培养更多AI金融复合型人才
### 🎓 推动教育和研究
#### 高校合作
- 为高校提供AI金融教学工具和案例
- 支持相关课程的开设和教学实践
- 促进产学研合作和技术转化
#### 研究支持
- 为研究机构提供技术平台和数据支持
- 推动AI金融领域的学术研究和创新
- 促进国际学术交流与合作
#### 人才培养
- 培养具备AI技术和金融知识的复合型人才
- 提供实践平台和项目经验
- 推动行业人才队伍建设
### 🚀 促进产业应用
#### 金融科技创新
- 推动AI技术在中国金融科技领域的应用
- 支持金融机构的数字化转型
- 促进新技术与传统金融的融合
#### 市场适配
- 支持A股、港股、新三板等中国金融市场
- 适应中国金融监管环境和合规要求
- 提供符合本土需求的功能特性
## 🤝 合作与贡献
### 🙏 对源码和持续贡献的深深感谢
#### 源码价值的深度认知
虽然Apache 2.0协议赋予了我们使用源码的法律权利,但我们深知:
- **💎 源码的珍贵价值**: 每一行代码都凝聚着开发者的智慧和心血
- **⏰ 时间成本**: 背后是无数个日夜的思考、编码、测试和优化
- **🧠 知识积累**: 代码中蕴含的领域知识和技术经验无比珍贵
- **🎯 设计理念**: 优秀的架构设计思想比代码本身更有价值
#### 持续贡献的感谢
我们特别感谢源项目团队的持续贡献:
- **🔄 持续维护**: 感谢您们持续维护和更新代码库
- **🐛 Bug修复**: 感谢您们及时修复发现的问题和漏洞
- **✨ 功能增强**: 感谢您们不断添加新功能和改进
- **📖 文档完善**: 感谢您们持续完善文档和使用指南
- **💬 社区支持**: 感谢您们积极回应社区问题和建议
#### 我们的承诺与回馈
基于对源码价值的深度认知,我们郑重承诺:
- **🔗 永久标注**: 在所有相关文档和代码中永久标注源项目信息
- **📢 积极推广**: 在中文社区积极推广和宣传源项目的价值
- **🔄 反馈贡献**: 将我们的改进和创新及时反馈给源项目
- **🤝 协同发展**: 与源项目保持技术同步和长期协作关系
- **💰 支持方式**: 在可能的情况下,通过各种方式支持源项目的发展
### 开源社区贡献
- **代码贡献**: 贡献高质量的代码和功能改进
- **文档完善**: 提供详细的中文文档和使用指南
- **测试验证**: 进行充分的测试和验证工作
- **用户支持**: 为中文用户提供技术支持和帮助
### 技术交流
我们热切期望与源项目团队和全球开发者进行技术交流:
- **经验分享**: 分享中文化和本土化的经验
- **技术讨论**: 参与技术方案的讨论和改进
- **合作开发**: 在可能的情况下进行合作开发
- **标准制定**: 参与相关技术标准的制定
## 🌍 致谢名单
### 核心贡献者
- **[Tauric Research](https://github.com/TauricResearch)** - 源项目开发团队
- **TradingAgents项目** - 提供了卓越的技术基础
### 中文增强版贡献者
- **项目发起人**: hsliuping
- **文档贡献者**: 中文文档翻译和改进团队
- **测试志愿者**: 功能测试和验证团队
- **社区用户**: 所有提供反馈和建议的用户
### 技术支持
- **阿里云**: 提供百炼大模型技术支持
- **开源社区**: 提供各种开源工具和库的支持
## 📜 关于Apache 2.0协议与感谢
### 法律权利与道德义务
虽然Apache 2.0协议赋予了我们以下法律权利:
- ✅ 自由使用源代码
- ✅ 修改和分发代码
- ✅ 商业使用权利
- ✅ 专利使用许可
但我们认为,**法律权利不等于道德义务的免除**。我们坚信:
- **💎 源码价值**: 每一行代码都是开发者智慧和时间的结晶
- **🙏 感恩之心**: 使用他人的劳动成果,理应表达感谢和敬意
- **🤝 社区精神**: 开源社区的繁荣需要相互尊重和感谢
- **🔄 良性循环**: 感谢和致敬能促进更多优秀项目的诞生
### 我们的感谢原则
- **永远感谢**: 无论协议如何规定,我们都会感谢源码贡献者
- **主动致敬**: 不仅在法律上合规,更要在道德上致敬
- **积极推广**: 在使用源码的同时,积极推广源项目的价值
- **回馈社区**: 将我们的改进和创新反馈给开源社区
## 💝 感恩的心
我们怀着感恩的心,感谢所有为这个项目做出贡献的个人和组织。正是因为有了大家的支持和帮助,我们才能够:
- 让更多中文用户体验到TradingAgents的强大功能
- 推动AI金融技术在中国的普及和应用
- 为全球开源社区贡献中国智慧和力量
- 促进中西方技术社区的交流与合作
## 🔮 未来展望
我们将继续努力:
- **持续改进**: 不断完善中文增强版本的功能和体验
- **技术创新**: 在尊重源项目的基础上进行技术创新
- **社区建设**: 建设活跃的中文开发者社区
- **国际合作**: 加强与国际开源社区的合作与交流
让我们携手共进,为AI金融技术的发展贡献力量!
---
*"站在巨人的肩膀上,我们能看得更远。感谢Tauric Research团队为我们提供了如此坚实的肩膀。"*
**TradingAgents-CN 团队**
2025年6月
================================================
FILE: COMMERCIAL_LICENSE_TEMPLATE.md
================================================
# TradingAgents-CN 商业许可证模板
# TradingAgents-CN Commercial License Template
## 商业软件许可协议
## Commercial Software License Agreement
**许可方 / Licensor**: hsliuping
**被许可方 / Licensee**: [客户公司名称 / Client Company Name]
**软件 / Software**: TradingAgents-CN Web Application (app/ 和 frontend/ 目录)
**协议日期 / Agreement Date**: [日期 / Date]
---
## 第一条 许可范围
1.1 **许可软件**: 本协议涵盖 TradingAgents-CN 项目中的以下组件:
- FastAPI 后端应用 (`app/` 目录)
- Vue.js 前端应用 (`frontend/` 目录)
- 相关文档和配置文件
1.2 **许可类型**: [选择一项]
- [ ] **单用户许可**: 限制单个用户使用
- [ ] **企业许可**: 允许企业内部使用
- [ ] **分发许可**: 允许重新分发给最终用户
- [ ] **OEM许可**: 允许集成到其他产品中
## 第二条 使用权限
被许可方在本协议期限内享有以下权利:
2.1 **使用权**: 在许可范围内安装和使用软件
2.2 **修改权**: 根据业务需求修改软件代码
2.3 **内部分发权**: 在组织内部分发软件副本
2.4 **技术支持**: 享受约定的技术支持服务
## 第三条 限制条款
3.1 **禁止行为**:
- 不得向第三方转让或再许可本软件
- 不得逆向工程、反编译或反汇编软件
- 不得移除或修改版权声明和许可证信息
- 不得将软件用于违法或有害活动
3.2 **保密义务**:
- 对软件源代码和技术信息承担保密义务
- 不得泄露软件的技术细节给竞争对手
## 第四条 费用和支付
4.1 **许可费用**: [具体金额] 人民币
4.2 **支付方式**: [支付方式和时间]
4.3 **维护费用**: 年度维护费用为许可费用的 [百分比]%
## 第五条 技术支持
5.1 **支持范围**:
- 软件安装和配置指导
- 使用问题解答
- Bug 修复和更新
- 定制开发服务(另行收费)
5.2 **支持方式**:
- 邮件支持:[support-email]
- 在线文档:[documentation-url]
- 远程协助:根据需要安排
## 第六条 知识产权
6.1 **所有权**: 软件的所有知识产权归许可方所有
6.2 **商标**: 被许可方不得使用许可方的商标和标识
6.3 **衍生作品**: 基于软件创建的衍生作品的知识产权归属需另行约定
## 第七条 免责声明
7.1 软件按"现状"提供,许可方不提供任何明示或暗示的担保
7.2 许可方不对使用软件造成的任何损失承担责任
7.3 被许可方应自行评估软件的适用性和风险
## 第八条 协议期限
8.1 **有效期**: 本协议自签署之日起生效,有效期为 [期限]
8.2 **续约**: 协议到期前 30 天内可协商续约
8.3 **终止**: 任何一方违约时,另一方可终止协议
## 第九条 争议解决
9.1 **管辖法律**: 本协议受中华人民共和国法律管辖
9.2 **争议解决**: 争议应通过友好协商解决,协商不成可提交仲裁
## 第十条 其他条款
10.1 **完整协议**: 本协议构成双方就软件许可的完整协议
10.2 **修改**: 协议修改需双方书面同意
10.3 **可分割性**: 协议部分条款无效不影响其他条款效力
---
**许可方签字**: _________________ **日期**: _________
**被许可方签字**: _________________ **日期**: _________
---
## 联系信息
**商业许可咨询 / Commercial License Inquiries**:
- 邮箱 / Email: hsliup@163.com
- GitHub: https://github.com/hsliuping/TradingAgents-CN
- QQ群 / QQ Group: 782124367
================================================
FILE: CONTRIBUTORS.md
================================================
# 🤝 贡献者名单
感谢所有为TradingAgents-CN项目做出贡献的开发者和用户!
## 🌟 贡献者分类
### 🐳 Docker容器化功能
- **[@breeze303](https://github.com/breeze303)**
- 贡献内容:提供完整的Docker Compose配置和容器化部署方案
- 影响:大大简化了项目的部署和开发环境配置
- 贡献时间:2025年
### 📄 报告导出功能
- **[@baiyuxiong](https://github.com/baiyuxiong)** (baiyuxiong@163.com)
- 贡献内容:设计并实现了完整的多格式报告导出系统
- 技术细节:包括Word、PDF、Markdown格式支持
- 影响:为用户提供了灵活的分析报告输出选项
- 贡献时间:2025年
### 🤖 AI模型集成与扩展
- **[@charliecai](https://github.com/charliecai)**
- 贡献内容:添加硅基流动(SiliconFlow) LLM提供商支持
- 技术细节:完整的API集成、配置管理和用户界面支持
- 影响:为用户提供了更多的AI模型选择,扩展了平台的LLM生态
- 贡献时间:2025年
- **[@yifanhere](https://github.com/yifanhere)**
- 贡献内容:修复logging_manager.py中的NameError异常
- 技术细节:添加模块级自举日志器,解决配置文件加载失败时未定义logger变量的问题
- 影响:修复了系统启动时的关键错误,提升了日志系统的稳定性和可靠性
- 贡献时间:2025年8月
### 🐛 Bug修复与系统优化
- **[@YifanHere](https://github.com/YifanHere)**
- **主要贡献**:
- 🔧 **CLI代码质量改进** ([PR #158](https://github.com/hsliuping/TradingAgents-CN/pull/158))
- 优化命令行界面的用户体验和错误处理机制
- 提升了命令行工具的稳定性和用户友好性
- 贡献时间:2025年
- 🐛 **关键Bug修复** ([PR #173](https://github.com/hsliuping/TradingAgents-CN/pull/173))
- 发现并报告了关键的 `KeyError: 'volume'` 问题
- 提供了详细的问题分析、根因定位和修复方案
- 显著提升了Tushare数据源的系统稳定性,解决了缓存数据标准化问题
- 贡献时间:2025年7月
- **总体影响**:通过多次贡献持续改善项目的稳定性和用户体验
- **[@BG8CFB](https://github.com/BG8CFB)**
- **主要贡献**:
- 🐛 修复 GLM 模型无法调用新闻分析的问题 ([PR #457](https://github.com/hsliuping/TradingAgents-CN/pull/457))
- 修正新闻分析模块与 GLM 模型的适配问题
- 提升新闻分析功能在 GLM 模型下的可用性与稳定性
- 贡献时间:2025年11月
## 🎯 贡献统计
### 按贡献类型统计
| 贡献类型 | 贡献者数量 | 主要贡献 |
| ------------- | ---------- | ------------------------------- |
| 🐳 容器化部署 | 1 | Docker配置、部署优化 |
| 📄 功能开发 | 1 | 报告导出系统 |
| 🤖 AI模型集成 | 3 | 硅基流动LLM提供商支持、日志系统修复、千帆模型集成 |
| 🐛 Bug修复 | 2 | 关键稳定性问题修复、CLI错误处理、GLM新闻分析修复 |
| 🔧 代码优化 | 1 | 命令行界面优化、用户体验改进 |
###
## 🏆 特别贡献奖
### 🥇 最佳持续贡献奖
- **[@YifanHere](https://github.com/YifanHere)** - 通过多个PR持续改善项目质量,包括CLI优化(#158)和关键Bug修复(#173)
### 🥈 最佳功能贡献奖
- **[@baiyuxiong](https://github.com/baiyuxiong)** - 完整的报告导出系统实现
### 🥉 最佳部署优化奖
- **[@breeze303](https://github.com/breeze303)** - Docker容器化部署方案
### 🏅 最佳AI集成贡献奖
- **[@charliecai](https://github.com/charliecai)** - 硅基流动(SiliconFlow) LLM提供商集成
- **TradingAgents-CN团队** - 百度千帆(Qianfan) ERNIE模型集成,提供OpenAI兼容接口
### 🛠️ 最佳Bug修复贡献奖
- **[@yifanhere](https://github.com/yifanhere)** - 修复了logging_manager.py中的关键NameError异常,通过添加自举日志器解决了系统启动时的核心问题,大幅提升了系统稳定性
## 🌟 其他贡献
### 📝 问题反馈与建议
- **所有提交Issue的用户** - 感谢您们的问题反馈和功能建议
- **测试用户** - 感谢您们在开发过程中的测试和反馈
- **文档贡献者** - 感谢您们对项目文档的完善和改进
### 🌍 社区推广
- **技术博客作者** - 感谢您们撰写技术文章推广项目
- **社交媒体推广者** - 感谢您们在各平台分享项目信息
- **会议演讲者** - 感谢您们在技术会议上介绍项目
## 🤝 如何成为贡献者
我们欢迎各种形式的贡献:
### 🔧 技术贡献
- **代码贡献**:Bug修复、新功能开发、性能优化
- **测试贡献**:编写测试用例、发现并报告Bug
- **文档贡献**:完善文档、编写教程、翻译内容
### 💡 非技术贡献
- **用户反馈**:使用体验反馈、功能需求建议
- **社区建设**:回答问题、帮助新用户、组织活动
- **推广宣传**:撰写文章、社交媒体分享、会议演讲
### 📋 贡献流程
1. **Fork项目** - 创建项目的个人副本
2. **创建分支** - 为您的贡献创建特性分支
3. **开发测试** - 实现功能并确保测试通过
4. **提交PR** - 提交Pull Request并描述您的更改
5. **代码审查** - 配合维护者进行代码审查
6. **合并发布** - 通过审查后合并到主分支
## 📞 联系方式
如果您想成为贡献者或有任何问题,请通过以下方式联系我们:
- **GitHub Issues**: [提交问题或建议](https://github.com/hsliuping/TradingAgents-CN/issues)
- **GitHub Discussions**: [参与社区讨论](https://github.com/hsliuping/TradingAgents-CN/discussions)
- **Pull Requests**: [提交代码贡献](https://github.com/hsliuping/TradingAgents-CN/pulls)
- 加入到QQ群:782124367
## 🙏 致谢
感谢每一位贡献者的无私奉献!正是因为有了大家的支持和贡献,TradingAgents-CN才能不断发展壮大,为中文用户提供更好的AI金融分析工具。
---
**最后更新时间**: 2025年11月15日
**贡献者总数**: 7位
**总PR数量**: 8个 (Docker化、报告导出、AI模型集成、CLI优化、Bug修复、日志系统修复、GLM新闻分析修复等)
**活跃贡献者**: 7位
================================================
FILE: COPYRIGHT.md
================================================
# TradingAgents-CN 版权信息
# TradingAgents-CN Copyright Information
## 📋 版权声明 / Copyright Notice
### 专有组件 / Proprietary Components
**版权所有者 / Copyright Owner**: hsliuping
**版权年份 / Copyright Year**: 2025
**适用组件 / Applicable Components**:
- `app/` - FastAPI 后端应用 / FastAPI Backend Application
- `frontend/` - Vue.js 前端应用 / Vue.js Frontend Application
### 开源组件 / Open Source Components
**许可证 / License**: Apache License 2.0
**适用组件 / Applicable Components**:
- `tradingagents/` - 核心交易智能体库 / Core Trading Agents Library
- `cli/` - 命令行工具 / Command Line Tools
- `scripts/` - 运维脚本 / Operational Scripts
- `docs/` - 文档 / Documentation
- `examples/` - 示例代码 / Example Code
- `web/` - Streamlit Web 应用 / Streamlit Web Application
- `tests/` - 测试文件 / Test Files
- 其他配置文件 / Other Configuration Files
## 🏛️ 法律管辖 / Legal Jurisdiction
**适用法律 / Governing Law**: 中华人民共和国法律 / Laws of the People's Republic of China
## 📞 联系信息 / Contact Information
**版权所有者 / Copyright Owner**: hsliuping
**邮箱 / Email**: hsliup@163.com
**GitHub**: https://github.com/hsliuping/TradingAgents-CN
**QQ群 / QQ Group**: 782124367
## 💼 商业许可 / Commercial Licensing
如需获得专有组件的商业使用许可,请联系版权所有者。
For commercial licensing of proprietary components, please contact the copyright owner.
**商业许可包含 / Commercial License Includes**:
- 商业使用权 / Commercial Use Rights
- 修改权 / Modification Rights
- 内部分发权 / Internal Distribution Rights
- 技术支持 / Technical Support
- 定制开发服务 / Custom Development Services
## ⚖️ 使用条款 / Terms of Use
### 专有组件 / Proprietary Components
- ❌ 禁止重新分发 / No Redistribution
- ❌ 禁止商业使用(需授权)/ No Commercial Use (License Required)
- ❌ 禁止修改 / No Modification
- ✅ 允许个人评估 / Personal Evaluation Allowed
- ✅ 允许教育用途 / Educational Use Allowed
### 开源组件 / Open Source Components
- ✅ 自由使用 / Free Use
- ✅ 商业使用 / Commercial Use
- ✅ 修改和分发 / Modification and Distribution
- ✅ 创建衍生作品 / Create Derivative Works
## 📚 相关文档 / Related Documents
- [LICENSE](./LICENSE) - 主许可证文件 / Main License File
- [app/LICENSE](./app/LICENSE) - 后端专有许可证 / Backend Proprietary License
- [frontend/LICENSE](./frontend/LICENSE) - 前端专有许可证 / Frontend Proprietary License
- [LICENSING.md](./LICENSING.md) - 详细许可证说明 / Detailed License Information
- [COMMERCIAL_LICENSE_TEMPLATE.md](./COMMERCIAL_LICENSE_TEMPLATE.md) - 商业许可证模板 / Commercial License Template
## 🔍 许可证验证 / License Verification
运行以下命令检查许可证状态:
Run the following command to check license status:
```bash
python scripts/check_license.py
```
---
**最后更新 / Last Updated**: 2025年10月 / October 2025
**版本 / Version**: v1.0
================================================
FILE: Dockerfile.backend
================================================
# Backend Dockerfile for FastAPI service (TradingAgents-CN v1.0.0-preview)
# 前后端分离架构 - 后端服务
# 支持多架构: amd64, arm64
# 使用 Debian Bookworm (稳定版) 而不是 Trixie (测试版)
FROM python:3.10-slim-bookworm
# 获取构建架构信息
ARG TARGETARCH
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1 \
TZ=Asia/Shanghai
WORKDIR /app
# 创建必需的目录并安装系统依赖
# - curl: 健康检查
# - pandoc: 从 GitHub 下载最新版本(避免 Debian 仓库问题)
# - wkhtmltopdf: 从官方下载(用于 PDF 生成)
# - 中文字体: 用于 PDF 中文显示
RUN mkdir -p /app/logs /app/data /app/config && \
# 配置 apt 重试和超时
echo 'Acquire::Retries "3";' > /etc/apt/apt.conf.d/80-retries && \
echo 'Acquire::http::Timeout "30";' >> /etc/apt/apt.conf.d/80-retries && \
echo 'Acquire::https::Timeout "30";' >> /etc/apt/apt.conf.d/80-retries && \
# 更新软件源,允许失败后继续
(apt-get update || apt-get update || apt-get update) && \
apt-get install -y --no-install-recommends \
ca-certificates \
curl \
fontconfig \
fonts-noto-cjk \
wget \
xvfb && \
# 根据架构设置变量
if [ "$TARGETARCH" = "arm64" ]; then \
PANDOC_ARCH="arm64"; \
WKHTMLTOPDF_ARCH="arm64"; \
else \
PANDOC_ARCH="amd64"; \
WKHTMLTOPDF_ARCH="amd64"; \
fi && \
# 下载并安装 pandoc
wget -q https://github.com/jgm/pandoc/releases/download/3.8.2.1/pandoc-3.8.2.1-1-${PANDOC_ARCH}.deb && \
dpkg -i pandoc-3.8.2.1-1-${PANDOC_ARCH}.deb && \
rm pandoc-3.8.2.1-1-${PANDOC_ARCH}.deb && \
# 下载并安装 wkhtmltopdf
wget -q https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-3/wkhtmltox_0.12.6.1-3.bookworm_${WKHTMLTOPDF_ARCH}.deb && \
apt-get install -y --no-install-recommends \
./wkhtmltox_0.12.6.1-3.bookworm_${WKHTMLTOPDF_ARCH}.deb && \
rm wkhtmltox_0.12.6.1-3.bookworm_${WKHTMLTOPDF_ARCH}.deb && \
# 更新字体缓存
fc-cache -fv && \
rm -rf /var/lib/apt/lists/*
# 复制pyproject.toml和README.md(pip安装需要)
COPY pyproject.toml README.md ./
# 安装Python依赖
# 优化说明:
# 1. 使用清华镜像加速下载
# 2. 优先使用预编译的二进制wheel包(--prefer-binary)
# 3. 避免从源码编译,大幅提升ARM架构构建速度
# 4. 安装 PDF 导出工具: pdfkit
RUN pip install --upgrade pip -i https://pypi.tuna.tsinghua.edu.cn/simple && \
pip install --prefer-binary . -i https://pypi.tuna.tsinghua.edu.cn/simple && \
pip install --prefer-binary pdfkit -i https://pypi.tuna.tsinghua.edu.cn/simple
# 复制后端代码和必需模块
COPY app ./app
COPY tradingagents ./tradingagents
COPY config ./config
COPY scripts ./scripts
COPY docs ./docs
COPY install ./install
# 复制Docker环境配置文件
COPY .env.docker ./.env
# 暴露后端端口(与docker-compose.v1.0.0.yml一致)
EXPOSE 8000
# Docker环境标识
ENV DOCKER_CONTAINER=true
# 启动FastAPI服务
CMD ["python", "-m", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
================================================
FILE: Dockerfile.frontend
================================================
# Frontend Dockerfile for Vue 3 + Vite app (TradingAgents-CN v1.0.0-preview)
# 前后端分离架构 - 前端服务
# 构建阶段:使用Node.js 22.x(与项目开发环境一致)
FROM node:22-alpine AS build
ENV NODE_ENV=production
WORKDIR /app/frontend
# 启用Corepack并使用Yarn 1.22.22(项目使用的包管理器)
RUN corepack enable && corepack prepare yarn@1.22.22 --activate
# 复制package.json、yarn.lock和.yarnrc(配置国内镜像源)
COPY frontend/package.json frontend/yarn.lock frontend/.yarnrc ./
# 安装依赖(使用yarn.lock确保版本一致)
# 增加网络超时时间到5分钟,适应跨平台构建的网络延迟
RUN yarn install --frozen-lockfile --production=false --network-timeout 300000
# 复制前端源代码
COPY frontend/. ./
# 复制根目录的静态资源与文档到构建环境
# - assets: 提供 /assets/* 静态资源(前端使用绝对路径)
# - docs: 提供 Article.vue 中通过 ?raw 引用的 Markdown 文档
COPY assets /app/frontend/public/assets
COPY docs /app/docs
# 构建生产版本(跳过类型检查以加快构建速度)
RUN yarn vite build
# 运行阶段:使用Nginx提供静态文件服务
FROM nginx:alpine AS runtime
WORKDIR /usr/share/nginx/html
# 从构建阶段复制构建产物
COPY --from=build /app/frontend/dist .
# 复制Nginx配置(支持SPA路由)
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
# 暴露端口80
EXPOSE 80
# 启动Nginx
CMD ["nginx", "-g", "daemon off;"]
================================================
FILE: LICENSE
================================================
TradingAgents-CN - Mixed License Project
TradingAgents-CN - 混合许可证项目
This project uses multiple licenses for different components:
本项目对不同组件使用多种许可证:
1. APACHE LICENSE 2.0 (Default)
1. APACHE 许可证 2.0(默认)
- Applies to: All files and directories EXCEPT "app/" and "frontend/"
- 适用于:除"app/"和"frontend/"之外的所有文件和目录
- Original TradingAgents framework and related components
- 原始 TradingAgents 框架和相关组件
- See full Apache License 2.0 terms below
- 完整的 Apache License 2.0 条款见下文
2. PROPRIETARY LICENSE
2. 专有许可证
- Applies to: "app/" directory (FastAPI backend)
- 适用于:"app/"目录(FastAPI 后端)
- Applies to: "frontend/" directory (Vue.js frontend)
- 适用于:"frontend/"目录(Vue.js 前端)
- See respective LICENSE files in those directories
- 请查看这些目录中相应的 LICENSE 文件
- Commercial use requires separate licensing agreement
- 商业使用需要单独的许可协议
For commercial licensing of proprietary components, contact: [hsliup@163.com
专有组件的商业许可,请联系:hsliup@163.com]
================================================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: LICENSING.md
================================================
# TradingAgents-CN 许可证说明
## 📋 许可证概述
TradingAgents-CN 项目采用**混合许可证策略**,不同组件使用不同的许可证:
## 🔓 开源组件 (Apache License 2.0)
以下组件继续使用 Apache License 2.0,保持开源:
```
├── tradingagents/ # 核心交易智能体库
├── cli/ # 命令行工具
├── scripts/ # 运维脚本
├── docs/ # 文档
├── examples/ # 示例代码
├── web/ # Streamlit Web 应用
├── assets/ # 静态资源
├── tests/ # 测试文件
├── *.py # 根目录 Python 文件
├── *.md # 文档文件
├── *.yml, *.yaml # 配置文件
└── 其他配置文件
```
**权限**:
- ✅ 自由使用、修改、分发
- ✅ 商业使用
- ✅ 创建衍生作品
- ✅ 私有使用
## 🔒 专有组件 (Proprietary License)
以下组件使用专有许可证,保护商业利益:
```
├── app/ # FastAPI 后端应用
│ ├── models/ # 数据模型
│ ├── routers/ # API 路由
│ ├── services/ # 业务服务
│ ├── middleware/ # 中间件
│ └── worker/ # 后台任务
└── frontend/ # Vue.js 前端应用
├── src/ # 源代码
├── components/ # 组件
└── views/ # 页面视图
```
**限制**:
- ❌ 不得重新分发
- ❌ 不得商业使用(需授权)
- ❌ 不得修改或创建衍生作品
- ❌ 不得逆向工程
**允许**:
- ✅ 个人评估和测试
- ✅ 教育用途(非商业)
- ✅ 内部业务评估
## 💼 商业许可
### 如需商业使用专有组件,请联系获取商业许可:
**联系方式**:
- 📧 邮箱:hsliup@163.com
- 🌐 GitHub:https://github.com/hsliuping/TradingAgents-CN
- � QQ群:782124367
### 商业许可包含:
1. **商业使用权** - 在商业环境中使用软件
2. **分发权** - 在组织内部分发软件
3. **技术支持** - 专业技术支持服务
4. **定制开发** - 根据需求定制功能
## 🎯 许可证选择原因
### 为什么采用混合许可证?
1. **保护创新成果** - 新开发的 Web 应用是核心商业价值
2. **维持开源精神** - 原有框架继续开源,回馈社区
3. **商业可持续性** - 通过商业许可支持项目持续发展
4. **灵活授权** - 为不同用户提供合适的使用方式
### 开源 vs 专有的划分逻辑
- **开源部分**:基于原项目的增强和优化
- **专有部分**:全新开发的现代化 Web 应用架构
## 📚 使用指南
### 个人用户
- 可以自由使用所有开源组件
- 可以评估测试专有组件
- 不得将专有组件用于商业用途
### 企业用户
- 可以自由使用所有开源组件进行商业活动
- 需要商业许可才能使用专有组件
- 联系我们获取企业级支持和定制服务
### 开发者
- 欢迎为开源组件贡献代码
- 专有组件的贡献需要签署贡献者协议
- 可以基于开源组件创建自己的项目
## ⚖️ 法律声明
本许可证说明仅为概述,具体条款以各组件目录下的 LICENSE 文件为准。
如有许可证相关问题,请咨询专业法律顾问。
---
**最后更新**:2025年10月
**版本**:v1.0
================================================
FILE: README.md
================================================
# TradingAgents 中文增强版
[](https://opensource.org/licenses/Apache-2.0)
[](https://www.python.org/)
[](./VERSION)
[](./docs/)
[](https://github.com/TauricResearch/TradingAgents)
---
## ⚠️ 重要版权声明与授权说明
### 🚨 版权侵权警告
**我们注意到 `tradingagents-ai.com` 网站未经授权使用了我们的专有代码,并声称是他们公司的产品。**
**⚠️ 重要提醒**:
- ❌ **我们项目组目前没有给任何组织或个人进行过商业授权**
- ❌ **该网站未经授权使用我们的代码,属于侵权行为**
- ⚠️ **请大家注意识别,避免上当受骗**
**✅ 官方唯一渠道**:
- 📦 GitHub 仓库:https://github.com/hsliuping/TradingAgents-CN
- 📧 官方邮箱:hsliup@163.com
- 📱 微信公众号:TradingAgents-CN
如发现任何未经授权的商业使用,请通过上述渠道联系我们。
### 📋 版本授权说明
#### v1.0.0-preview(当前版本)
- ✅ **个人使用**:完全开源,可自由使用
- ❌ **商业使用**:**必须获得商业授权**,未经授权禁止商业使用
- 📧 **授权联系**:[hsliup@163.com](mailto:hsliup@163.com)
#### v2.0.0(开发中)
- 🔄 **开发状态**:已完成两轮内测,接近完工上线阶段
- ⚠️ **开源计划**:**因存在盗版问题,v2.0 版本暂时不进行开源**
- 📢 **发布方式**:将通过官方渠道发布,敬请关注
### 📄 许可证详情
本项目采用**混合许可证**模式:
- 🔓 **开源部分**(Apache 2.0):除 `app/` 和 `frontend/` 外的所有文件
- 🔒 **专有部分**(需商业授权):`app/`(FastAPI后端)和 `frontend/`(Vue前端)目录
详细说明请查看:[版权声明](./COPYRIGHT.md) | [许可证文件](./LICENSE)
---
>
> 🎓 **学习中心**: AI基础 | 提示词工程 | 模型选择 | 多智能体分析原理 | 风险与局限 | 源项目与论文 | 实战教程(部分为外链) | 常见问题
> 🎯 **核心功能**: 原生OpenAI支持 | Google AI全面集成 | 自定义端点配置 | 智能模型选择 | 多LLM提供商支持 | 模型选择持久化 | Docker容器化部署 | 专业报告导出 | 完整A股支持 | 中文本地化
面向中文用户的**多智能体与大模型股票分析学习平台**。帮助你系统化学习如何使用多智能体交易框架与 AI 大模型进行合规的股票研究与策略实验,不提供实盘交易指令,平台定位为学习与研究用途。
## 🙏 致敬源项目
感谢 [Tauric Research](https://github.com/TauricResearch) 团队创造的革命性多智能体交易框架 [TradingAgents](https://github.com/TauricResearch/TradingAgents)!
**🎯 我们的定位与使命**: 专注学习与研究,提供中文化学习中心与工具,合规友好,支持 A股/港股/美股 的分析与教学,推动 AI 金融技术在中文社区的普及与正确使用。
## 🎉 v1.0.0-preview 版本上线 - 全新架构升级
> 🚀 **重磅发布**: v1.0.0-preview 版本现已正式!全新的 FastAPI + Vue 3 架构,带来企业级的性能和体验!
### ✨ 核心特性
#### 🏗️ **全新技术架构**
- **后端升级**: 从 Streamlit 迁移到 FastAPI,提供更强大的 RESTful API
- **前端重构**: 采用 Vue 3 + Element Plus,打造现代化的单页应用
- **数据库优化**: MongoDB + Redis 双数据库架构,性能提升 10 倍
- **容器化部署**: 完整的 Docker 多架构支持(amd64 + arm64)
#### 🎯 **企业级功能**
- **用户权限管理**: 完整的用户认证、角色管理、操作日志系统
- **配置管理中心**: 可视化的大模型配置、数据源管理、系统设置
- **缓存管理系统**: 智能缓存策略,支持 MongoDB/Redis/文件多级缓存
- **实时通知系统**: SSE+WebSocket 双通道推送,实时跟踪分析进度和系统状态
- **批量分析功能**: 支持多只股票同时分析,提升工作效率
- **智能股票筛选**: 基于多维度指标的股票筛选和排序系统
- **自选股管理**: 个人自选股收藏、分组管理和跟踪功能
- **个股详情页**: 完整的个股信息展示和历史分析记录
- **模拟交易系统**: 虚拟交易环境,验证投资策略效果
#### 🤖 **智能分析增强**
- **动态供应商管理**: 支持动态添加和配置 LLM 供应商
- **模型能力管理**: 智能模型选择,根据任务自动匹配最佳模型
- **多数据源同步**: 统一的数据源管理,支持 Tushare、AkShare、BaoStock
- **报告导出功能**: 支持 Markdown/Word/PDF 多格式专业报告导出
#### � **重大Bug修复**
- **技术指标计算修复**: 彻底解决市场分析师技术指标计算不准确问题
- **基本面数据修复**: 修复基本面分析师PE、PB等关键财务数据计算错误
- **死循环问题修复**: 解决部分用户在分析过程中触发的无限循环问题
- **数据一致性优化**: 确保所有分析师使用统一、准确的数据源
#### �🐳 **Docker 多架构支持**
- **跨平台部署**: 支持 x86_64 和 ARM64 架构(Apple Silicon、树莓派、AWS Graviton)
- **GitHub Actions**: 自动化构建和发布 Docker 镜像
- **一键部署**: 完整的 Docker Compose 配置,5 分钟快速启动
### 📊 技术栈升级
| 组件 | v0.1.x | v1.0.0-preview |
|------|--------|----------------|
| **后端框架** | Streamlit | FastAPI + Uvicorn |
| **前端框架** | Streamlit | Vue 3 + Vite + Element Plus |
| **数据库** | 可选 MongoDB | MongoDB + Redis |
| **API 架构** | 单体应用 | RESTful API + WebSocket |
| **部署方式** | 本地/Docker | Docker 多架构 + GitHub Actions |
#### 📥 安装部署
**三种部署方式,任选其一**:
| 部署方式 | 适用场景 | 难度 | 文档链接 |
|---------|---------|------|---------|
| 🟢 **绿色版** | Windows 用户、快速体验 | ⭐ 简单 | [绿色版安装指南](https://mp.weixin.qq.com/s/eoo_HeIGxaQZVT76LBbRJQ) |
| 🐳 **Docker版** | 生产环境、跨平台 | ⭐⭐ 中等 | [Docker 部署指南](https://mp.weixin.qq.com/s/JkA0cOu8xJnoY_3LC5oXNw) |
| 💻 **本地代码版** | 开发者、定制需求 | ⭐⭐⭐ 较难 | [本地安装指南](https://mp.weixin.qq.com/s/cqUGf-sAzcBV19gdI4sYfA) |
⚠️ **重要提醒**:在分析股票之前,请按相关文档要求,将股票数据同步完成,否则分析结果将会出现数据错误。
#### 📚 使用指南
在使用前,建议先阅读详细的使用指南:
- **[0、📘 TradingAgents-CN v1.0.0-preview 快速入门视频](https://www.bilibili.com/video/BV1i2CeBwEP7/?vd_source=5d790a5b8d2f46d2c10fd4e770be1594)**
- **[1、📘 TradingAgents-CN v1.0.0-preview 使用指南](https://mp.weixin.qq.com/s/ppsYiBncynxlsfKFG8uEbw)**
- **[2、📘 使用 Docker Compose 部署TradingAgents-CN v1.0.0-preview(完全版)](https://mp.weixin.qq.com/s/JkA0cOu8xJnoY_3LC5oXNw)**
- **[3、📘 从 Docker Hub 更新 TradingAgents‑CN 镜像](https://mp.weixin.qq.com/s/WKYhW8J80Watpg8K6E_dSQ)**
- **[4、📘 TradingAgents-CN v1.0.0-preview绿色版安装和升级指南](https://mp.weixin.qq.com/s/eoo_HeIGxaQZVT76LBbRJQ)**
- **[5、📘 TradingAgents-CN v1.0.0-preview绿色版端口配置说明](https://mp.weixin.qq.com/s/o5QdNuh2-iKkIHzJXCj7vQ)**
- **[6、📘 TradingAgents v1.0.0-preview 源码版安装手册(修订版)](https://mp.weixin.qq.com/s/cqUGf-sAzcBV19gdI4sYfA)**
- **[7、📘 TradingAgents v1.0.0-preview 源码安装视频教程](https://www.bilibili.com/video/BV1FxCtBHEte/?vd_source=5d790a5b8d2f46d2c10fd4e770be1594)**
使用指南包含:
- ✅ 完整的功能介绍和操作演示
- ✅ 详细的配置说明和最佳实践
- ✅ 常见问题解答和故障排除
- ✅ 实际使用案例和效果展示
#### 关注公众号
1. **关注公众号**: 微信搜索 **"TradingAgents-CN"** 并关注
2. 公众号每天推送项目最新进展和使用教程
- **微信公众号**: TradingAgents-CN(推荐)
<img src="assets/wexin.png" alt="微信公众号" width="200"/>
## 🆚 中文增强特色
**相比原版新增**: 智能新闻分析 | 多层次新闻过滤 | 新闻质量评估 | 统一新闻工具 | 多LLM提供商集成 | 模型选择持久化 | 快速切换按钮 | | 实时进度显示 | 智能会话管理 | 中文界面 | A股数据 | 国产LLM | Docker部署 | 专业报告导出 | 统一日志管理 | Web配置界面 | 成本优化
## 📢 招募测试志愿者
### 🎯 我们需要你的帮助!
TradingAgentsCN 已经获得 **13,000+ stars**,但一直由我一个人开发维护。每次发布新版本时,尽管我会尽力测试,但仍然会有一些隐藏的 bug 没有被发现。
**我需要你的帮助来让这个项目变得更好!**
### 🙋 我们需要什么样的志愿者?
- ✅ 对股票分析或 AI 应用感兴趣
- ✅ 愿意在新版本发布前进行测试
- ✅ 能够清晰描述遇到的问题
- ✅ 每周可以投入 2-4 小时(弹性时间)
**不需要编程经验!** 功能测试、文档测试、用户体验测试都非常有价值。
### 🎁 你将获得什么?
1. **优先体验权** - 提前体验新功能和新版本
2. **技术成长** - 深入了解多智能体系统和 LLM 应用开发
3. **社区认可** - 在 README 和发布说明中致谢,获得 "Core Tester" 标签
4. **开源贡献** - 为 13,000+ stars 的项目做出实质性贡献
5. **未来机会** - 如果项目商业化,可能会有相应的报酬
### 🚀 如何加入?
**方式一:微信公众号申请(推荐)**
1. 关注微信公众号:**TradingAgentsCN**
2. 在公众号菜单选择"测试申请"菜单
3. 填写申请信息
**方式二:邮件申请**
- 发送邮件到:hsliup@163.com
- 主题:测试志愿者申请
### 📋 测试内容示例
- **日常测试**(每周 2-4 小时):测试新功能和 bug 修复,在不同环境下验证功能
- **版本发布前测试**(每月 1-2 次):完整的功能回归测试、安装和部署流程测试
### 🌟 特别需要的测试方向
- 🪟 **Windows 用户** - 测试 Windows 安装程序和绿色版
- 🍎 **macOS 用户** - 测试 macOS 兼容性
- 🐧 **Linux 用户** - 测试 Linux 兼容性
- 🐳 **Docker 用户** - 测试 Docker 部署
- 📊 **多市场用户** - 测试 A 股、港股、美股数据源
- 🤖 **多 LLM 用户** - 测试不同 LLM 提供商(OpenAI/Gemini/DeepSeek/通义千问等)
**详细信息**: 查看完整招募公告 → [📢 测试志愿者招募](docs/community/CALL_FOR_TESTERS.md)
## 🤝 贡献指南
我们欢迎各种形式的贡献:
### 贡献类型
- 🐛 **Bug修复** - 发现并修复问题
- ✨ **新功能** - 添加新的功能特性
- 📚 **文档改进** - 完善文档和教程
- 🌐 **本地化** - 翻译和本地化工作
- 🎨 **代码优化** - 性能优化和代码重构
### 贡献流程
1. Fork 本仓库
2. 创建特性分支 (`git checkout -b feature/AmazingFeature`)
3. 提交更改 (`git commit -m 'Add some AmazingFeature'`)
4. 推送到分支 (`git push origin feature/AmazingFeature`)
5. 创建 Pull Request
### 📋 查看贡献者
查看所有贡献者和详细贡献内容:**[🤝 贡献者名单](CONTRIBUTORS.md)**
## 📄 许可证详情
本项目采用**混合许可证**模式,详见 [LICENSE](LICENSE) 文件:
### 🔓 开源部分(Apache 2.0)
- **适用范围**:除 `app/` 和 `frontend/` 外的所有文件
- **权限**:商业使用 ✅ | 修改分发 ✅ | 私人使用 ✅ | 专利使用 ✅
- **条件**:保留版权声明 ❗ | 包含许可证副本 ❗
### 🔒 专有部分(需商业授权)
- **适用范围**:`app/`(FastAPI后端)和 `frontend/`(Vue前端)目录
- **商业使用**:需要单独许可协议
- **联系授权**:[hsliup@163.com](mailto:hsliup@163.com)
### 📋 许可证选择建议
- **个人学习/研究**:可自由使用全部功能
- **商业应用**:请联系获取专有组件授权
- **定制开发**:欢迎咨询商业合作方案
### 📚 相关文档
- [版权声明](./COPYRIGHT.md) - 详细的版权信息和使用条款
- [主许可证](./LICENSE) - Apache 2.0 许可证
- [后端专有许可证](./app/LICENSE) - 后端专有组件许可证
- [前端专有许可证](./frontend/LICENSE) - 前端专有组件许可证
## 🙏 致谢与感恩
### 🌟 向源项目开发者致敬
我们向 [Tauric Research](https://github.com/TauricResearch) 团队表达最深的敬意和感谢:
- **🎯 愿景领导者**: 感谢您们在AI金融领域的前瞻性思考和创新实践
- **💎 珍贵源码**: 感谢您们开源的每一行代码,它们凝聚着无数的智慧和心血
- **🏗️ 架构大师**: 感谢您们设计了如此优雅、可扩展的多智能体框架
- **💡 技术先驱**: 感谢您们将前沿AI技术与金融实务完美结合
- **🔄 持续贡献**: 感谢您们持续的维护、更新和改进工作
### 🤝 社区贡献者致谢
感谢所有为TradingAgents-CN项目做出贡献的开发者和用户!
详细的贡献者名单和贡献内容请查看:**[📋 贡献者名单](CONTRIBUTORS.md)**
包括但不限于:
- 🐳 **Docker容器化** - 部署方案优化
- 📄 **报告导出功能** - 多格式输出支持
- 🐛 **Bug修复** - 系统稳定性提升
- 🔧 **代码优化** - 用户体验改进
- 📝 **文档完善** - 使用指南和教程
- 🌍 **社区建设** - 问题反馈和推广
- **🌍 开源贡献**: 感谢您们选择Apache 2.0协议,给予开发者最大的自由
- **📚 知识分享**: 感谢您们提供的详细文档和最佳实践指导
**特别感谢**:[TradingAgents](https://github.com/TauricResearch/TradingAgents) 项目为我们提供了坚实的技术基础。虽然Apache 2.0协议赋予了我们使用源码的权利,但我们深知每一行代码的珍贵价值,将永远铭记并感谢您们的无私贡献。
### 🇨🇳 推广使命的初心
创建这个中文增强版本,我们怀着以下初心:
- **🌉 技术传播**: 让优秀的TradingAgents技术在中国得到更广泛的应用
- **🎓 教育普及**: 为中国的AI金融教育提供更好的工具和资源
- **🤝 文化桥梁**: 在中西方技术社区之间搭建交流合作的桥梁
- **🚀 创新推动**: 推动中国金融科技领域的AI技术创新和应用
### 🌍 开源社区
感谢所有为本项目贡献代码、文档、建议和反馈的开发者和用户。正是因为有了大家的支持,我们才能更好地服务中文用户社区。
### 🤝 合作共赢
我们承诺:
- **尊重原创**: 始终尊重源项目的知识产权和开源协议
- **反馈贡献**: 将有价值的改进和创新反馈给源项目和开源社区
- **持续改进**: 不断完善中文增强版本,提供更好的用户体验
- **开放合作**: 欢迎与源项目团队和全球开发者进行技术交流与合作
## 📈 版本历史
- **v0.1.13** (2025-08-02): 🤖 原生OpenAI支持与Google AI生态系统全面集成 ✨ **最新版本**
- **v0.1.12** (2025-07-29): 🧠 智能新闻分析模块与项目结构优化
- **v0.1.11** (2025-07-27): 🤖 多LLM提供商集成与模型选择持久化
- **v0.1.10** (2025-07-18): 🚀 Web界面实时进度显示与智能会话管理
- **v0.1.9** (2025-07-16): 🎯 CLI用户体验重大优化与统一日志管理
- **v0.1.8** (2025-07-15): 🎨 Web界面全面优化与用户体验提升
- **v0.1.7** (2025-07-13): 🐳 容器化部署与专业报告导出
- **v0.1.6** (2025-07-11): 🔧 阿里百炼修复与数据源升级
- **v0.1.5** (2025-07-08): 📊 添加Deepseek模型支持
- **v0.1.4** (2025-07-05): 🏗️ 架构优化与配置管理重构
- **v0.1.3** (2025-06-28): 🇨🇳 A股市场完整支持
- **v0.1.2** (2025-06-15): 🌐 Web界面和配置管理
- **v0.1.1** (2025-06-01): 🧠 国产LLM集成
📋 **详细更新日志**: [CHANGELOG.md](./docs/releases/CHANGELOG.md)
## 📞 联系方式
- **GitHub Issues**: [提交问题和建议](https://github.com/hsliuping/TradingAgents-CN/issues)
- **邮箱**: hsliup@163.com
- 项目QQ群:1009816091
- 项目微信公众号:TradingAgents-CN
<img src="assets/wexin.png" alt="微信公众号" width="200"/>
- **原项目**: [TauricResearch/TradingAgents](https://github.com/TauricResearch/TradingAgents)
- **文档**: [完整文档目录](docs/)
## ⚠️ 风险提示
**重要声明**: 本框架仅用于研究和教育目的,不构成投资建议。
- 📊 交易表现可能因多种因素而异
- 🤖 AI模型的预测存在不确定性
- 💰 投资有风险,决策需谨慎
- 👨💼 建议咨询专业财务顾问
---
<div align="center">
**🌟 如果这个项目对您有帮助,请给我们一个 Star!**
[⭐ Star this repo](https://github.com/hsliuping/TradingAgents-CN) | [🍴 Fork this repo](https://github.com/hsliuping/TradingAgents-CN/fork) | [📖 Read the docs](./docs/)
</div>
================================================
FILE: VERSION
================================================
v1.0.0-preview
================================================
FILE: app/LICENSE
================================================
TradingAgents-CN Web Application - Proprietary License
TradingAgents-CN Web 应用程序 - 专有许可证
Copyright (c) 2025 [hsliuping]. All rights reserved.
版权所有 (c) 2025 [hsliuping]。保留所有权利。
PROPRIETARY SOFTWARE LICENSE AGREEMENT
专有软件许可协议
This software and associated documentation files (the "Software") contained in the
"app/" directory are proprietary and confidential to hsliuping
("Licensor").
本软件及相关文档文件("软件")包含在"app/"目录中,属于[hsliuping]
("许可方")的专有和机密信息。
RESTRICTIONS:
限制条款:
1. NO REDISTRIBUTION: You may not distribute, sublicense, lease, rent, or otherwise
transfer the Software to any third party.
1. 禁止重新分发:您不得向任何第三方分发、转授权、租赁、出租或以其他方式转让本软件。
2. NO MODIFICATION: You may not modify, adapt, alter, translate, or create derivative
works based upon the Software.
2. 禁止修改:您不得修改、改编、更改、翻译或基于本软件创建衍生作品。
3. NO REVERSE ENGINEERING: You may not reverse engineer, disassemble, decompile, or
otherwise attempt to derive the source code of the Software.
3. 禁止逆向工程:您不得对本软件进行逆向工程、反汇编、反编译或以其他方式试图获取源代码。
4. NO COMMERCIAL USE: You may not use the Software for any commercial purposes without
explicit written permission from the Licensor.
4. 禁止商业使用:未经许可方明确书面许可,您不得将本软件用于任何商业目的。
5. PERSONAL USE ONLY: The Software is licensed for personal, non-commercial use only.
5. 仅限个人使用:本软件仅授权用于个人、非商业用途。
PERMITTED USES:
允许的使用方式:
- Personal evaluation and testing
- 个人评估和测试
- Educational purposes (non-commercial)
- 教育目的(非商业)
- Internal business evaluation (with prior written consent)
- 内部业务评估(需事先书面同意)
COMMERCIAL LICENSING:
商业许可:
For commercial use, distribution, or modification rights, please contact:
如需商业使用、分发或修改权限,请联系:
hsliuping (hsliup@163.com)
DISCLAIMER:
免责声明:
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
本软件按"现状"提供,不提供任何形式的明示或暗示担保,包括但不限于适销性、
特定用途适用性和非侵权性的担保。在任何情况下,作者或版权持有人均不对任何
索赔、损害或其他责任负责,无论是在合同诉讼、侵权行为还是其他方面,
由软件或软件的使用或其他交易引起、产生或与之相关。
TERMINATION:
终止:
This license is effective until terminated. Your rights under this license will
terminate automatically without notice if you fail to comply with any term(s) of
this license.
本许可证在终止前一直有效。如果您未能遵守本许可证的任何条款,
您在本许可证下的权利将自动终止,无需通知。
GOVERNING LAW:
适用法律:
This license shall be governed by and construed in accordance with the laws of
the People's Republic of China.
本许可证应受中华人民共和国法律管辖并按其解释。
---
For commercial licensing inquiries, please contact: hsliup@163.com
商业许可咨询,请联系:hsliup@163.com
================================================
FILE: app/__init__.py
================================================
"""TradingAgents-CN Web API package."""
================================================
FILE: app/__main__.py
================================================
"""
TradingAgents-CN Backend Entry Point
支持 python -m app 启动方式
"""
import uvicorn
import sys
import os
from pathlib import Path
# ============================================================================
# 全局 UTF-8 编码设置(必须在最开始,支持 emoji 和中文)
# ============================================================================
if sys.platform == 'win32':
try:
# 1. 设置环境变量,让 Python 全局使用 UTF-8
os.environ['PYTHONIOENCODING'] = 'utf-8'
os.environ['PYTHONUTF8'] = '1'
# 2. 设置标准输出和错误输出为 UTF-8
import io
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')
# 3. 尝试设置控制台代码页为 UTF-8 (65001)
try:
import ctypes
ctypes.windll.kernel32.SetConsoleCP(65001)
ctypes.windll.kernel32.SetConsoleOutputCP(65001)
except Exception:
pass
except Exception as e:
# 如果设置失败,打印警告但继续运行
print(f"Warning: Failed to set UTF-8 encoding: {e}", file=sys.stderr)
# 添加项目根目录到Python路径
project_root = Path(__file__).parent.parent
sys.path.insert(0, str(project_root))
# 检查并打印.env文件加载信息
def check_env_file():
"""检查并打印.env文件加载信息"""
import logging
logger = logging.getLogger("app.startup")
logger.info("🔍 检查环境配置文件...")
# 检查当前工作目录
current_dir = Path.cwd()
logger.info(f"📂 当前工作目录: {current_dir}")
# 检查项目根目录
logger.info(f"📂 项目根目录: {project_root}")
# 检查可能的.env文件位置(按优先级排序)
env_locations = [
project_root / ".env", # 优先:项目根目录(标准位置)
current_dir / ".env", # 次选:当前工作目录
Path(__file__).parent / ".env" # 最后:app目录下(不推荐)
]
env_found = False
for env_path in env_locations:
if env_path.exists():
if not env_found: # 只显示第一个找到的文件详情
logger.info(f"✅ 找到.env文件: {env_path}")
logger.info(f"📏 文件大小: {env_path.stat().st_size} bytes")
env_found = True
# 读取并显示部分内容(隐藏敏感信息)
try:
with open(env_path, 'r', encoding='utf-8') as f:
lines = f.readlines()
logger.info(f"📄 .env文件内容预览 (共{len(lines)}行):")
for i, line in enumerate(lines[:10]): # 只显示前10行
line = line.strip()
if line and not line.startswith('#'):
# 隐藏敏感信息
if any(keyword in line.upper() for keyword in ['SECRET', 'PASSWORD', 'TOKEN', 'KEY']):
key = line.split('=')[0] if '=' in line else line
logger.info(f" {key}=***")
else:
logger.info(f" {line}")
if len(lines) > 10:
logger.info(f" ... (还有{len(lines) - 10}行)")
except Exception as e:
logger.warning(f"⚠️ 读取.env文件时出错: {e}")
else:
# 如果已经找到一个,只记录其他位置也有文件(可能重复)
logger.debug(f"ℹ️ 其他位置也有.env文件: {env_path}")
if not env_found:
logger.warning("⚠️ 未找到.env文件,将使用默认配置")
logger.info(f"💡 提示: 请在项目根目录 ({project_root}) 创建 .env 文件")
logger.info("-" * 50)
try:
from app.core.config import settings
from app.core.dev_config import DEV_CONFIG
except Exception as e:
import traceback
print(f"❌ 导入配置模块失败: {e}")
print("📋 详细错误信息:")
print("-" * 50)
traceback.print_exc()
print("-" * 50)
sys.exit(1)
def main():
"""主启动函数"""
import logging
logger = logging.getLogger("app.startup")
logger.info("🚀 Starting TradingAgents-CN Backend...")
logger.info(f"📍 Host: {settings.HOST}")
logger.info(f"🔌 Port: {settings.PORT}")
logger.info(f"🐛 Debug Mode: {settings.DEBUG}")
logger.info(f"📚 API Docs: http://{settings.HOST}:{settings.PORT}/docs" if settings.DEBUG else "📚 API Docs: Disabled in production")
# 打印关键配置信息
logger.info("🔧 关键配置信息:")
logger.info(f" 📊 MongoDB: {settings.MONGODB_HOST}:{settings.MONGODB_PORT}/{settings.MONGODB_DATABASE}")
logger.info(f" 🔴 Redis: {settings.REDIS_HOST}:{settings.REDIS_PORT}/{settings.REDIS_DB}")
logger.info(f" 🔐 JWT Secret: {'已配置' if settings.JWT_SECRET != 'change-me-in-production' else '⚠️ 使用默认值'}")
logger.info(f" 📝 日志级别: {settings.LOG_LEVEL}")
# 检查环境变量加载状态
logger.info("🌍 环境变量加载状态:")
env_vars_to_check = [
('MONGODB_HOST', settings.MONGODB_HOST, 'localhost'),
('MONGODB_PORT', str(settings.MONGODB_PORT), '27017'),
('MONGODB_DATABASE', settings.MONGODB_DATABASE, 'tradingagents'),
('REDIS_HOST', settings.REDIS_HOST, 'localhost'),
('REDIS_PORT', str(settings.REDIS_PORT), '6379'),
('JWT_SECRET', '***' if settings.JWT_SECRET != 'change-me-in-production' else settings.JWT_SECRET, 'change-me-in-production')
]
for env_name, current_value, default_value in env_vars_to_check:
status = "✅ 已设置" if current_value != default_value else "⚠️ 默认值"
logger.info(f" {env_name}: {current_value} ({status})")
logger.info("-" * 50)
# 获取uvicorn配置
uvicorn_config = DEV_CONFIG.get_uvicorn_config(settings.DEBUG)
# 设置简化的日志配置
logger.info("🔧 正在设置日志配置...")
try:
from app.core.logging_config import setup_logging as app_setup_logging
app_setup_logging(settings.LOG_LEVEL)
except Exception:
# 回退到开发环境简化日志配置
DEV_CONFIG.setup_logging(settings.DEBUG)
logger.info("✅ 日志配置设置完成")
# 在日志系统初始化后检查.env文件
logger.info("📋 Configuration Loading Phase:")
check_env_file()
try:
uvicorn.run(
"app.main:app",
host=settings.HOST,
port=settings.PORT,
**uvicorn_config
)
except KeyboardInterrupt:
logger.info("🛑 Server stopped by user")
except Exception as e:
import traceback
logger.error(f"❌ Failed to start server: {e}")
logger.error("📋 详细错误信息:")
logger.error("-" * 50)
traceback.print_exc()
logger.error("-" * 50)
sys.exit(1)
if __name__ == "__main__":
main()
================================================
FILE: app/constants/model_capabilities.py
================================================
"""
模型能力分级系统
定义模型的能力等级、适用角色、特性标签等元数据,
用于智能匹配分析深度和模型选择。
🆕 聚合渠道支持:
- 支持 302.AI、OpenRouter、One API 等聚合渠道
- 聚合渠道的模型名称格式:{provider}/{model}(如 openai/gpt-4)
- 系统会自动映射到原厂模型的能力配置
"""
from enum import IntEnum, Enum
from typing import Dict, List, Any, Tuple
class ModelCapabilityLevel(IntEnum):
"""模型能力等级(1-5级)"""
BASIC = 1 # 基础:适合1-2级分析,轻量快速
STANDARD = 2 # 标准:适合1-3级分析,日常使用
ADVANCED = 3 # 高级:适合1-4级分析,复杂推理
PROFESSIONAL = 4 # 专业:适合1-5级分析,专业级分析
FLAGSHIP = 5 # 旗舰:适合所有级别,最强能力
class ModelRole(str, Enum):
"""模型角色类型"""
QUICK_ANALYSIS = "quick_analysis" # 快速分析(数据收集、工具调用)
DEEP_ANALYSIS = "deep_analysis" # 深度分析(推理、决策)
BOTH = "both" # 两者都适合
class ModelFeature(str, Enum):
"""模型特性标签"""
TOOL_CALLING = "tool_calling" # 支持工具调用(必需)
LONG_CONTEXT = "long_context" # 支持长上下文
REASONING = "reasoning" # 强推理能力
VISION = "vision" # 支持视觉输入
FAST_RESPONSE = "fast_response" # 快速响应
COST_EFFECTIVE = "cost_effective" # 成本效益高
# 能力等级描述
CAPABILITY_DESCRIPTIONS = {
1: "基础模型 - 适合快速分析和简单任务,响应快速,成本低",
2: "标准模型 - 适合日常分析和常规任务,平衡性能和成本",
3: "高级模型 - 适合深度分析和复杂推理,质量较高",
4: "专业模型 - 适合专业级分析和多轮辩论,高质量输出",
5: "旗舰模型 - 最强能力,适合全面分析和关键决策"
}
# 分析深度要求的最低能力等级
ANALYSIS_DEPTH_REQUIREMENTS = {
"快速": {
"min_capability": 1,
"quick_model_min": 1,
"deep_model_min": 1,
"required_features": [ModelFeature.TOOL_CALLING],
"description": "1级快速分析:任何模型都可以,优先选择快速响应的模型"
},
"基础": {
"min_capability": 1,
"quick_model_min": 1,
"deep_model_min": 2,
"required_features": [ModelFeature.TOOL_CALLING],
"description": "2级基础分析:快速模型可用基础级,深度模型建议标准级以上"
},
"标准": {
"min_capability": 2,
"quick_model_min": 1,
"deep_model_min": 2,
"required_features": [ModelFeature.TOOL_CALLING],
"description": "3级标准分析:快速模型可用基础级,深度模型需要标准级以上"
},
"深度": {
"min_capability": 3,
"quick_model_min": 2,
"deep_model_min": 3,
"required_features": [ModelFeature.TOOL_CALLING, ModelFeature.REASONING],
"description": "4级深度分析:快速模型需标准级,深度模型需高级以上,需要推理能力"
},
"全面": {
"min_capability": 4,
"quick_model_min": 2,
"deep_model_min": 4,
"required_features": [ModelFeature.TOOL_CALLING, ModelFeature.REASONING],
"description": "5级全面分析:快速模型需标准级,深度模型需专业级以上,强推理能力"
}
}
# 常见模型的默认能力配置(用于初始化和参考)
DEFAULT_MODEL_CAPABILITIES: Dict[str, Dict[str, Any]] = {
# ==================== 阿里百炼 (DashScope) ====================
"qwen-turbo": {
"capability_level": 1,
"suitable_roles": [ModelRole.QUICK_ANALYSIS],
"features": [ModelFeature.TOOL_CALLING, ModelFeature.FAST_RESPONSE, ModelFeature.COST_EFFECTIVE],
"recommended_depths": ["快速", "基础"],
"performance_metrics": {"speed": 5, "cost": 5, "quality": 3},
"description": "通义千问轻量版,快速响应,适合数据收集"
},
"qwen-plus": {
"capability_level": 2,
"suitable_roles": [ModelRole.BOTH],
"features": [ModelFeature.TOOL_CALLING, ModelFeature.LONG_CONTEXT],
"recommended_depths": ["快速", "基础", "标准"],
"performance_metrics": {"speed": 4, "cost": 4, "quality": 4},
"description": "通义千问标准版,平衡性能和成本"
},
"qwen-max": {
"capability_level": 4,
"suitable_roles": [ModelRole.BOTH],
"features": [ModelFeature.TOOL_CALLING, ModelFeature.LONG_CONTEXT, ModelFeature.REASONING],
"recommended_depths": ["标准", "深度", "全面"],
"performance_metrics": {"speed": 3, "cost": 2, "quality": 5},
"description": "通义千问旗舰版,强大推理能力"
},
"qwen3-max": {
"capability_level": 5,
"suitable_roles": [ModelRole.DEEP_ANALYSIS],
"features": [ModelFeature.TOOL_CALLING, ModelFeature.LONG_CONTEXT, ModelFeature.REASONING],
"recommended_depths": ["深度", "全面"],
"performance_metrics": {"speed": 2, "cost": 1, "quality": 5},
"description": "通义千问长文本版,超长上下文"
},
# ==================== OpenAI ====================
"gpt-3.5-turbo": {
"capability_level": 1,
"suitable_roles": [ModelRole.QUICK_ANALYSIS],
"features": [ModelFeature.TOOL_CALLING, ModelFeature.FAST_RESPONSE, ModelFeature.COST_EFFECTIVE],
"recommended_depths": ["快速", "基础"],
"performance_metrics": {"speed": 5, "cost": 5, "quality": 3},
"description": "GPT-3.5 Turbo,快速且经济"
},
"gpt-4": {
"capability_level": 3,
"suitable_roles": [ModelRole.BOTH],
"features": [ModelFeature.TOOL_CALLING, ModelFeature.REASONING],
"recommended_depths": ["基础", "标准", "深度"],
"performance_metrics": {"speed": 3, "cost": 3, "quality": 4},
"description": "GPT-4,强大的推理能力"
},
"gpt-4-turbo": {
"capability_level": 4,
"suitable_roles": [ModelRole.BOTH],
"features": [ModelFeature.TOOL_CALLING, ModelFeature.LONG_CONTEXT, ModelFeature.REASONING, ModelFeature.VISION],
"recommended_depths": ["标准", "深度", "全面"],
"performance_metrics": {"speed": 4, "cost": 2, "quality": 5},
"description": "GPT-4 Turbo,更快更强"
},
"gpt-4o-mini": {
"capability_level": 2,
"suitable_roles": [ModelRole.BOTH],
"features": [ModelFeature.TOOL_CALLING, ModelFeature.FAST_RESPONSE, ModelFeature.COST_EFFECTIVE],
"recommended_depths": ["快速", "基础", "标准"],
"performance_metrics": {"speed": 5, "cost": 5, "quality": 3},
"description": "GPT-4o Mini,经济实惠"
},
"o1-mini": {
"capability_level": 4,
"suitable_roles": [ModelRole.DEEP_ANALYSIS],
"features": [ModelFeature.REASONING],
"recommended_depths": ["深度", "全面"],
"performance_metrics": {"speed": 2, "cost": 3, "quality": 5},
"description": "O1 Mini,强推理模型"
},
"o1": {
"capability_level": 5,
"suitable_roles": [ModelRole.DEEP_ANALYSIS],
"features": [ModelFeature.REASONING],
"recommended_depths": ["全面"],
"performance_metrics": {"speed": 1, "cost": 1, "quality": 5},
"description": "O1,最强推理能力"
},
"o4-mini": {
"capability_level": 4,
"suitable_roles": [ModelRole.DEEP_ANALYSIS],
"features": [ModelFeature.REASONING],
"recommended_depths": ["深度", "全面"],
"performance_metrics": {"speed": 2, "cost": 3, "quality": 5},
"description": "O4 Mini,新一代推理模型"
},
# ==================== DeepSeek ====================
"deepseek-chat": {
"capability_level": 3,
"suitable_roles": [ModelRole.BOTH],
"features": [ModelFeature.TOOL_CALLING, ModelFeature.LONG_CONTEXT, ModelFeature.COST_EFFECTIVE],
"recommended_depths": ["基础", "标准", "深度"],
"performance_metrics": {"speed": 4, "cost": 5, "quality": 4},
"description": "DeepSeek Chat,性价比高"
},
# ==================== 百度文心 (Qianfan) ====================
"ernie-3.5": {
"capability_level": 2,
"suitable_roles": [ModelRole.BOTH],
"features": [ModelFeature.TOOL_CALLING],
"recommended_depths": ["快速", "基础", "标准"],
"performance_metrics": {"speed": 4, "cost": 4, "quality": 3},
"description": "文心一言3.5,标准版本"
},
"ernie-4.0": {
"capability_level": 3,
"suitable_roles": [ModelRole.BOTH],
"features": [ModelFeature.TOOL_CALLING, ModelFeature.REASONING],
"recommended_depths": ["基础", "标准", "深度"],
"performance_metrics": {"speed": 3, "cost": 3, "quality": 4},
"description": "文心一言4.0,高级版本"
},
"ernie-4.0-turbo": {
"capability_level": 4,
"suitable_roles": [ModelRole.BOTH],
"features": [ModelFeature.TOOL_CALLING, ModelFeature.REASONING, ModelFeature.FAST_RESPONSE],
"recommended_depths": ["标准", "深度", "全面"],
"performance_metrics": {"speed": 4, "cost": 2, "quality": 5},
"description": "文心一言4.0 Turbo,旗舰版本"
},
# ==================== 智谱AI (GLM) ====================
"glm-3-turbo": {
"capability_level": 1,
"suitable_roles": [ModelRole.QUICK_ANALYSIS],
"features": [ModelFeature.TOOL_CALLING, ModelFeature.FAST_RESPONSE, ModelFeature.COST_EFFECTIVE],
"recommended_depths": ["快速", "基础"],
"performance_metrics": {"speed": 5, "cost": 5, "quality": 3},
"description": "智谱GLM-3 Turbo,快速版本"
},
"glm-4": {
"capability_level": 3,
"suitable_roles": [ModelRole.BOTH],
"features": [ModelFeature.TOOL_CALLING, ModelFeature.REASONING],
"recommended_depths": ["基础", "标准", "深度"],
"performance_metrics": {"speed": 3, "cost": 3, "quality": 4},
"description": "智谱GLM-4,标准版本"
},
"glm-4-plus": {
"capability_level": 4,
"suitable_roles": [ModelRole.BOTH],
"features": [ModelFeature.TOOL_CALLING, ModelFeature.LONG_CONTEXT, ModelFeature.REASONING],
"recommended_depths": ["标准", "深度", "全面"],
"performance_metrics": {"speed": 3, "cost": 2, "quality": 5},
"description": "智谱GLM-4 Plus,旗舰版本"
},
# ==================== Anthropic Claude ====================
"claude-3-haiku": {
"capability_level": 2,
"suitable_roles": [ModelRole.QUICK_ANALYSIS],
"features": [ModelFeature.TOOL_CALLING, ModelFeature.FAST_RESPONSE],
"recommended_depths": ["快速", "基础", "标准"],
"performance_metrics": {"speed": 5, "cost": 4, "quality": 3},
"description": "Claude 3 Haiku,快速版本"
},
"claude-3-sonnet": {
"capability_level": 3,
"suitable_roles": [ModelRole.BOTH],
"features": [ModelFeature.TOOL_CALLING, ModelFeature.LONG_CONTEXT, ModelFeature.VISION],
"recommended_depths": ["基础", "标准", "深度"],
"performance_metrics": {"speed": 4, "cost": 3, "quality": 4},
"description": "Claude 3 Sonnet,平衡版本"
},
"claude-3-opus": {
"capability_level": 4,
"suitable_roles": [ModelRole.BOTH],
"features": [ModelFeature.TOOL_CALLING, ModelFeature.LONG_CONTEXT, ModelFeature.REASONING, ModelFeature.VISION],
"recommended_depths": ["标准", "深度", "全面"],
"performance_metrics": {"speed": 3, "cost": 2, "quality": 5},
"description": "Claude 3 Opus,旗舰版本"
},
"claude-3.5-sonnet": {
"capability_level": 5,
"suitable_roles": [ModelRole.BOTH],
"features": [ModelFeature.TOOL_CALLING, ModelFeature.LONG_CONTEXT, ModelFeature.REASONING, ModelFeature.VISION],
"recommended_depths": ["标准", "深度", "全面"],
"performance_metrics": {"speed": 4, "cost": 2, "quality": 5},
"description": "Claude 3.5 Sonnet,最新旗舰"
},
# ==================== Google Gemini ====================
"gemini-pro": {
"capability_level": 3,
"suitable_roles": [ModelRole.BOTH],
"features": [ModelFeature.TOOL_CALLING, ModelFeature.REASONING],
"recommended_depths": ["基础", "标准", "深度"],
"performance_metrics": {"speed": 4, "cost": 4, "quality": 4},
"description": "Gemini Pro,经典稳定版本"
},
"gemini-1.5-pro": {
"capability_level": 4,
"suitable_roles": [ModelRole.BOTH],
"features": [ModelFeature.TOOL_CALLING, ModelFeature.LONG_CONTEXT, ModelFeature.REASONING, ModelFeature.VISION],
"recommended_depths": ["标准", "深度", "全面"],
"performance_metrics": {"speed": 4, "cost": 3, "quality": 5},
"description": "Gemini 1.5 Pro,长上下文旗舰"
},
"gemini-1.5-flash": {
"capability_level": 2,
"suitable_roles": [ModelRole.QUICK_ANALYSIS],
"features": [ModelFeature.TOOL_CALLING, ModelFeature.FAST_RESPONSE, ModelFeature.COST_EFFECTIVE],
"recommended_depths": ["快速", "基础", "标准"],
"performance_metrics": {"speed": 5, "cost": 5, "quality": 3},
"description": "Gemini 1.5 Flash,快速响应版本"
},
"gemini-2.0-flash": {
"capability_level": 4,
"suitable_roles": [ModelRole.BOTH],
"features": [ModelFeature.TOOL_CALLING, ModelFeature.LONG_CONTEXT, ModelFeature.REASONING, ModelFeature.FAST_RESPONSE],
"recommended_depths": ["标准", "深度", "全面"],
"performance_metrics": {"speed": 5, "cost": 3, "quality": 5},
"description": "Gemini 2.0 Flash,新一代快速旗舰"
},
"gemini-2.5-flash-lite-preview-06-17": {
"capability_level": 2,
"suitable_roles": [ModelRole.QUICK_ANALYSIS],
"features": [ModelFeature.TOOL_CALLING, ModelFeature.FAST_RESPONSE, ModelFeature.COST_EFFECTIVE],
"recommended_depths": ["快速", "基础"],
"performance_metrics": {"speed": 5, "cost": 5, "quality": 3},
"description": "Gemini 2.5 Flash Lite,轻量预览版"
},
# ==================== 月之暗面 (Moonshot) ====================
"moonshot-v1-8k": {
"capability_level": 2,
"suitable_roles": [ModelRole.BOTH],
"features": [ModelFeature.TOOL_CALLING],
"recommended_depths": ["快速", "基础", "标准"],
"performance_metrics": {"speed": 4, "cost": 4, "quality": 3},
"description": "Moonshot V1 8K,标准版本"
},
"moonshot-v1-32k": {
"capability_level": 3,
"suitable_roles": [ModelRole.BOTH],
"features": [ModelFeature.TOOL_CALLING, ModelFeature.LONG_CONTEXT],
"recommended_depths": ["基础", "标准", "深度"],
"performance_metrics": {"speed": 3, "cost": 3, "quality": 4},
"description": "Moonshot V1 32K,长上下文版本"
},
"moonshot-v1-128k": {
"capability_level": 4,
"suitable_roles": [ModelRole.DEEP_ANALYSIS],
"features": [ModelFeature.TOOL_CALLING, ModelFeature.LONG_CONTEXT, ModelFeature.REASONING],
"recommended_depths": ["标准", "深度", "全面"],
"performance_metrics": {"speed": 2, "cost": 2, "quality": 5},
"description": "Moonshot V1 128K,超长上下文旗舰"
},
}
def get_model_capability_badge(level: int) -> Dict[str, str]:
"""获取能力等级徽章样式"""
badges = {
1: {"text": "基础", "color": "#909399", "icon": "⚡"},
2: {"text": "标准", "color": "#409EFF", "icon": "📊"},
3: {"text": "高级", "color": "#67C23A", "icon": "🎯"},
4: {"text": "专业", "color": "#E6A23C", "icon": "🔥"},
5: {"text": "旗舰", "color": "#F56C6C", "icon": "👑"}
}
return badges.get(level, badges[2])
def get_role_badge(role: ModelRole) -> Dict[str, str]:
"""获取角色徽章样式"""
badges = {
ModelRole.QUICK_ANALYSIS: {"text": "快速分析", "color": "success", "icon": "⚡"},
ModelRole.DEEP_ANALYSIS: {"text": "深度推理", "color": "warning", "icon": "🧠"},
ModelRole.BOTH: {"text": "通用", "color": "primary", "icon": "🎯"}
}
return badges.get(role, badges[ModelRole.BOTH])
def get_feature_badge(feature: ModelFeature) -> Dict[str, str]:
"""获取特性徽章样式"""
badges = {
ModelFeature.TOOL_CALLING: {"text": "工具调用", "color": "info", "icon": "🔧"},
ModelFeature.LONG_CONTEXT: {"text": "长上下文", "color": "success", "icon": "📚"},
ModelFeature.REASONING: {"text": "强推理", "color": "warning", "icon": "🧠"},
ModelFeature.VISION: {"text": "视觉", "color": "primary", "icon": "👁️"},
ModelFeature.FAST_RESPONSE: {"text": "快速", "color": "success", "icon": "⚡"},
ModelFeature.COST_EFFECTIVE: {"text": "经济", "color": "success", "icon": "💰"}
}
return badges.get(feature, {"text": str(feature), "color": "info", "icon": "✨"})
# ==================== 聚合渠道配置 ====================
# 聚合渠道的默认配置
AGGREGATOR_PROVIDERS = {
"302ai": {
"display_name": "302.AI",
"description": "302.AI 聚合平台,提供多厂商模型统一接口",
"website": "https://302.ai",
"api_doc_url": "https://doc.302.ai",
"default_base_url": "https://api.302.ai/v1",
"model_name_format": "{provider}/{model}", # 如: openai/gpt-4
"supported_providers": ["openai", "anthropic", "google", "deepseek", "qwen"]
},
"openrouter": {
"display_name": "OpenRouter",
"description": "OpenRouter 聚合平台,支持多种 AI 模型",
"website": "https://openrouter.ai",
"api_doc_url": "https://openrouter.ai/docs",
"default_base_url": "https://openrouter.ai/api/v1",
"model_name_format": "{provider}/{model}",
"supported_providers": ["openai", "anthropic", "google", "meta", "mistral"]
},
"oneapi": {
"display_name": "One API",
"description": "One API 开源聚合平台",
"website": "https://github.com/songquanpeng/one-api",
"api_doc_url": "https://github.com/songquanpeng/one-api",
"default_base_url": "http://localhost:3000/v1", # 需要用户自行部署
"model_name_format": "{model}", # One API 通常不需要前缀
"supported_providers": ["openai", "anthropic", "google", "azure", "claude"]
},
"newapi": {
"display_name": "New API",
"description": "New API 聚合平台",
"website": "https://github.com/Calcium-Ion/new-api",
"api_doc_url": "https://github.com/Calcium-Ion/new-api",
"default_base_url": "http://localhost:3000/v1",
"model_name_format": "{model}",
"supported_providers": ["openai", "anthropic", "google", "azure", "claude"]
}
}
def is_aggregator_model(model_name: str) -> bool:
"""
判断是否为聚合渠道模型名称
Args:
model_name: 模型名称
Returns:
是否为聚合渠道模型
"""
return "/" in model_name
def parse_aggregator_model(model_name: str) -> Tuple[str, str]:
"""
解析聚合渠道模型名称
Args:
model_name: 模型名称(如 openai/gpt-4)
Returns:
(provider, model) 元组
"""
if "/" in model_name:
parts = model_name.split("/", 1)
return parts[0], parts[1]
return "", model_name
================================================
FILE: app/core/__init__.py
================================================
"""
Core module for TradingAgents FastAPI backend
"""
================================================
FILE: app/core/config.py
================================================
from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict
from typing import List
import os
import warnings
# Legacy env var aliases (deprecated): map API_HOST/PORT/DEBUG -> HOST/PORT/DEBUG
_LEGACY_ENV_ALIASES = {
"API_HOST": "HOST",
"API_PORT": "PORT",
"API_DEBUG": "DEBUG",
}
for _legacy, _new in _LEGACY_ENV_ALIASES.items():
if _new not in os.environ and _legacy in os.environ:
os.environ[_new] = os.environ[_legacy]
warnings.warn(
f"Environment variable {_legacy} is deprecated; use {_new} instead.",
DeprecationWarning,
stacklevel=2,
)
class Settings(BaseSettings):
# 基础配置
DEBUG: bool = Field(default=True)
HOST: str = Field(default="0.0.0.0")
PORT: int = Field(default=8000)
ALLOWED_ORIGINS: List[str] = Field(default_factory=lambda: ["*"])
ALLOWED_HOSTS: List[str] = Field(default_factory=lambda: ["*"])
# MongoDB配置
MONGODB_HOST: str = Field(default="localhost")
MONGODB_PORT: int = Field(default=27017)
MONGODB_USERNAME: str = Field(default="")
MONGODB_PASSWORD: str = Field(default="")
MONGODB_DATABASE: str = Field(default="tradingagents")
MONGODB_AUTH_SOURCE: str = Field(default="admin")
MONGO_MAX_CONNECTIONS: int = Field(default=100)
MONGO_MIN_CONNECTIONS: int = Field(default=10)
# MongoDB超时参数(毫秒)- 用于处理大量历史数据
MONGO_CONNECT_TIMEOUT_MS: int = Field(default=30000) # 连接超时:30秒(原为10秒)
MONGO_SOCKET_TIMEOUT_MS: int = Field(default=60000) # 套接字超时:60秒(原为20秒)
MONGO_SERVER_SELECTION_TIMEOUT_MS: int = Field(default=5000) # 服务器选择超时:5秒
@property
def MONGO_URI(self) -> str:
"""构建MongoDB URI"""
if self.MONGODB_USERNAME and self.MONGODB_PASSWORD:
return f"mongodb://{self.MONGODB_USERNAME}:{self.MONGODB_PASSWORD}@{self.MONGODB_HOST}:{self.MONGODB_PORT}/{self.MONGODB_DATABASE}?authSource={self.MONGODB_AUTH_SOURCE}"
else:
return f"mongodb://{self.MONGODB_HOST}:{self.MONGODB_PORT}/{self.MONGODB_DATABASE}"
@property
def MONGO_DB(self) -> str:
"""获取数据库名称"""
return self.MONGODB_DATABASE
# Redis配置
REDIS_HOST: str = Field(default="localhost")
REDIS_PORT: int = Field(default=6379)
REDIS_PASSWORD: str = Field(default="")
REDIS_DB: int = Field(default=0)
REDIS_MAX_CONNECTIONS: int = Field(default=20)
REDIS_RETRY_ON_TIMEOUT: bool = Field(default=True)
@property
def REDIS_URL(self) -> str:
"""构建Redis URL"""
if self.REDIS_PASSWORD:
return f"redis://:{self.REDIS_PASSWORD}@{self.REDIS_HOST}:{self.REDIS_PORT}/{self.REDIS_DB}"
else:
return f"redis://{self.REDIS_HOST}:{self.REDIS_PORT}/{self.REDIS_DB}"
# JWT配置
JWT_SECRET: str = Field(default="change-me-in-production")
JWT_ALGORITHM: str = Field(default="HS256")
ACCESS_TOKEN_EXPIRE_MINUTES: int = Field(default=60)
REFRESH_TOKEN_EXPIRE_DAYS: int = Field(default=30)
# 队列配置
QUEUE_MAX_SIZE: int = Field(default=10000)
QUEUE_VISIBILITY_TIMEOUT: int = Field(default=300) # 5分钟
QUEUE_MAX_RETRIES: int = Field(default=3)
WORKER_HEARTBEAT_INTERVAL: int = Field(default=30) # 30秒
# 队列轮询/清理间隔(秒)
QUEUE_POLL_INTERVAL_SECONDS: float = Field(default=1.0)
QUEUE_CLEANUP_INTERVAL_SECONDS: float = Field(default=60.0)
# 并发控制
DEFAULT_USER_CONCURRENT_LIMIT: int = Field(default=3)
GLOBAL_CONCURRENT_LIMIT: int = Field(default=50)
DEFAULT_DAILY_QUOTA: int = Field(default=1000)
# 速率限制
RATE_LIMIT_ENABLED: bool = Field(default=True)
DEFAULT_RATE_LIMIT: int = Field(default=100) # 每分钟请求数
# 日志配置
LOG_LEVEL: str = Field(default="INFO")
LOG_FORMAT: str = Field(default="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
LOG_FILE: str = Field(default="logs/tradingagents.log")
# 代理配置
# 用于配置需要绕过代理的域名(国内数据源)
# 多个域名用逗号分隔
# ⚠️ Windows 不支持通配符 *,必须使用完整域名
# 详细说明: docs/proxy_configuration.md
HTTP_PROXY: str = Field(default="")
HTTPS_PROXY: str = Field(default="")
NO_PROXY: str = Field(
default="localhost,127.0.0.1,eastmoney.com,push2.eastmoney.com,82.push2.eastmoney.com,82.push2delay.eastmoney.com,gtimg.cn,sinaimg.cn,api.tushare.pro,baostock.com"
)
# 文件上传配置
MAX_UPLOAD_SIZE: int = Field(default=10 * 1024 * 1024) # 10MB
UPLOAD_DIR: str = Field(default="uploads")
# 缓存配置
CACHE_TTL: int = Field(default=3600) # 1小时
SCREENING_CACHE_TTL: int = Field(default=1800) # 30分钟
# 安全配置
BCRYPT_ROUNDS: int = Field(default=12)
SESSION_EXPIRE_HOURS: int = Field(default=24)
CSRF_SECRET: str = Field(default="change-me-csrf-secret")
# 外部服务配置
STOCK_DATA_API_URL: str = Field(default="")
STOCK_DATA_API_KEY: str = Field(default="")
# SSE 配置
SSE_POLL_TIMEOUT_SECONDS: float = Field(default=1.0)
SSE_HEARTBEAT_INTERVAL_SECONDS: int = Field(default=10)
SSE_TASK_MAX_IDLE_SECONDS: int = Field(default=300)
SSE_BATCH_POLL_INTERVAL_SECONDS: float = Field(default=2.0)
SSE_BATCH_MAX_IDLE_SECONDS: int = Field(default=600)
# 监控配置
METRICS_ENABLED: bool = Field(default=True)
HEALTH_CHECK_INTERVAL: int = Field(default=60) # 60秒
# 配置真相来源(方案A):file|db|hybrid
# - file:以文件/env 为准(推荐,生产缺省)
# - db:以数据库为准(仅兼容旧版,不推荐)
# - hybrid:文件/env 优先,DB 作为兜底
CONFIG_SOT: str = Field(default="file")
# 基础信息同步任务配置(可配置调度)
SYNC_STOCK_BASICS_ENABLED: bool = Field(default=True)
# 优先使用 CRON 表达式,例如 "30 6 * * *" 表示每日 06:30
SYNC_STOCK_BASICS_CRON: str = Field(default="")
# 若未提供 CRON,则使用简单时间字符串 "HH:MM"(24小时制)
SYNC_STOCK_BASICS_TIME: str = Field(default="06:30")
# 时区
TIMEZONE: str = Field(default="Asia/Shanghai")
# 实时行情入库任务
QUOTES_INGEST_ENABLED: bool = Field(default=True)
QUOTES_INGEST_INTERVAL_SECONDS: int = Field(
default=360,
description="实时行情采集间隔(秒)。默认360秒(6分钟),免费用户建议>=300秒,付费用户可设置5-60秒"
)
# 休市期/启动兜底补数(填充上一笔快照)
QUOTES_BACKFILL_ON_STARTUP: bool = Field(default=True)
QUOTES_BACKFILL_ON_OFFHOURS: bool = Field(default=True)
# 实时行情接口轮换配置
QUOTES_ROTATION_ENABLED: bool = Field(
default=True,
description="启用接口轮换机制(Tushare → AKShare东方财富 → AKShare新浪财经)"
)
QUOTES_TUSHARE_HOURLY_LIMIT: int = Field(
default=2,
description="Tushare rt_k接口每小时调用次数限制(免费用户2次,付费用户可设置更高)"
)
QUOTES_AUTO_DETECT_TUSHARE_PERMISSION: bool = Field(
default=True,
description="自动检测Tushare rt_k接口权限,付费用户自动切换到高频模式(5秒)"
)
# Tushare基础配置
TUSHARE_TOKEN: str = Field(default="", description="Tushare API Token")
TUSHARE_ENABLED: bool = Field(default=True, description="启用Tushare数据源")
TUSHARE_TIER: str = Field(default="standard", description="Tushare积分等级 (free/basic/standard/premium/vip)")
TUSHARE_RATE_LIMIT_SAFETY_MARGIN: float = Field(default=0.8, ge=0.1, le=1.0, description="速率限制安全边际")
# Tushare统一数据同步配置
TUSHARE_UNIFIED_ENABLED: bool = Field(default=True)
TUSHARE_BASIC_INFO_SYNC_ENABLED: bool = Field(default=True)
TUSHARE_BASIC_INFO_SYNC_CRON: str = Field(default="0 2 * * *") # 每日凌晨2点
TUSHARE_QUOTES_SYNC_ENABLED: bool = Field(default=True)
TUSHARE_QUOTES_SYNC_CRON: str = Field(default="*/5 9-15 * * 1-5") # 交易时间每5分钟
TUSHARE_HISTORICAL_SYNC_ENABLED: bool = Field(default=True)
TUSHARE_HISTORICAL_SYNC_CRON: str = Field(default="0 16 * * 1-5") # 工作日16点
TUSHARE_FINANCIAL_SYNC_ENABLED: bool = Field(default=True)
TUSHARE_FINANCIAL_SYNC_CRON: str = Field(default="0 3 * * 0") # 周日凌晨3点
TUSHARE_STATUS_CHECK_ENABLED: bool = Field(default=True)
TUSHARE_STATUS_CHECK_CRON: str = Field(default="0 * * * *") # 每小时
# Tushare数据初始化配置
TUSHARE_INIT_HISTORICAL_DAYS: int = Field(default=365, ge=1, le=3650, description="初始化历史数据天数")
TUSHARE_INIT_BATCH_SIZE: int = Field(default=100, ge=10, le=1000, description="初始化批处理大小")
TUSHARE_INIT_AUTO_START: bool = Field(default=False, description="应用启动时自动检查并初始化数据")
# AKShare统一数据同步配置
AKSHARE_UNIFIED_ENABLED: bool = Field(default=True, description="启用AKShare统一数据同步")
AKSHARE_BASIC_INFO_SYNC_ENABLED: bool = Field(default=True, description="启用基础信息同步")
AKSHARE_BASIC_INFO_SYNC_CRON: str = Field(default="0 3 * * *", description="基础信息同步CRON表达式") # 每日凌晨3点
AKSHARE_QUOTES_SYNC_ENABLED: bool = Field(default=True, description="启用行情同步")
AKSHARE_QUOTES_SYNC_CRON: str = Field(default="*/30 9-15 * * 1-5", description="行情同步CRON表达式") # 交易时间每30分钟(避免频率限制)
AKSHARE_HISTORICAL_SYNC_ENABLED: bool = Field(default=True, description="启用历史数据同步")
AKSHARE_HISTORICAL_SYNC_CRON: str = Field(default="0 17 * * 1-5", description="历史数据同步CRON表达式") # 工作日17点
AKSHARE_FINANCIAL_SYNC_ENABLED: bool = Field(default=True, description="启用财务数据同步")
AKSHARE_FINANCIAL_SYNC_CRON: str = Field(default="0 4 * * 0", description="财务数据同步CRON表达式") # 周日凌晨4点
AKSHARE_STATUS_CHECK_ENABLED: bool = Field(default=True, description="启用状态检查")
AKSHARE_STATUS_CHECK_CRON: str = Field(default="30 * * * *", description="状态检查CRON表达式") # 每小时30分
# AKShare数据初始化配置
AKSHARE_INIT_HISTORICAL_DAYS: int = Field(default=365, ge=1, le=3650, description="初始化历史数据天数")
AKSHARE_INIT_BATCH_SIZE: int = Field(default=100, ge=10, le=1000, description="初始化批处理大小")
AKSHARE_INIT_AUTO_START: bool = Field(default=False, description="应用启动时自动检查并初始化数据")
# ==================== 分析师数据获取配置 ====================
# 市场分析师数据范围配置
# 默认60天:可覆盖MA60等所有常用技术指标(MA5/10/20/60, MACD, RSI, BOLL)
MARKET_ANALYST_LOOKBACK_DAYS: int = Field(default=60, ge=5, le=365, description="市场分析回溯天数(用于技术分析)")
# ==================== BaoStock统一数据同步配置 ====================
# BaoStock统一数据同步总开关
BAOSTOCK_UNIFIED_ENABLED: bool = Field(default=True, description="启用BaoStock统一数据同步")
# BaoStock数据同步任务配置
BAOSTOCK_BASIC_INFO_SYNC_ENABLED: bool = Field(default=True, description="启用基础信息同步")
BAOSTOCK_BASIC_INFO_SYNC_CRON: str = Field(default="0 4 * * *", description="基础信息同步CRON表达式") # 每日凌晨4点
BAOSTOCK_DAILY_QUOTES_SYNC_ENABLED: bool = Field(default=True, description="启用日K线同步(注意:BaoStock不支持实时行情)")
BAOSTOCK_DAILY_QUOTES_SYNC_CRON: str = Field(default="0 16 * * 1-5", description="日K线同步CRON表达式") # 工作日收盘后16:00
BAOSTOCK_HISTORICAL_SYNC_ENABLED: bool = Field(default=True, description="启用历史数据同步")
BAOSTOCK_HISTORICAL_SYNC_CRON: str = Field(default="0 18 * * 1-5", description="历史数据同步CRON表达式") # 工作日18点
BAOSTOCK_STATUS_CHECK_ENABLED: bool = Field(default=True, description="启用状态检查")
BAOSTOCK_STATUS_CHECK_CRON: str = Field(default="45 * * * *", description="状态检查CRON表达式") # 每小时45分
# BaoStock数据初始化配置
BAOSTOCK_INIT_HISTORICAL_DAYS: int = Field(default=365, ge=1, le=3650, description="初始化历史数据天数")
BAOSTOCK_INIT_BATCH_SIZE: int = Field(default=50, ge=10, le=500, description="初始化批处理大小")
BAOSTOCK_INIT_AUTO_START: bool = Field(default=False, description="应用启动时自动检查并初始化数据")
# 数据目录配置
TRADINGAGENTS_DATA_DIR: str = Field(default="./data")
@property
def log_dir(self) -> str:
"""获取日志目录"""
return os.path.dirname(self.LOG_FILE)
# ==================== 港股数据配置 ====================
# 港股数据源配置(按需获取+缓存模式)
HK_DATA_CACHE_HOURS: int = Field(default=24, ge=1, le=168, description="港股数据缓存时长(小时)")
HK_DEFAULT_DATA_SOURCE: str = Field(default="yfinance", description="港股默认数据源(yfinance/akshare)")
# ==================== 美股数据配置 ====================
# 美股数据源配置(按需获取+缓存模式)
US_DATA_CACHE_HOURS: int = Field(default=24, ge=1, le=168, description="美股数据缓存时长(小时)")
US_DEFAULT_DATA_SOURCE: str = Field(default="yfinance", description="美股默认数据源(yfinance/finnhub)")
# ===== 新闻数据同步服务配置 =====
NEWS_SYNC_ENABLED: bool = Field(default=True)
NEWS_SYNC_CRON: str = Field(default="0 */2 * * *") # 每2小时
NEWS_SYNC_HOURS_BACK: int = Field(default=24)
NEWS_SYNC_MAX_PER_SOURCE: int = Field(default=50)
@property
def is_production(self) -> bool:
"""是否为生产环境"""
return not self.DEBUG
# Ignore any extra environment variables present in .env or process env
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="ignore")
settings = Settings()
# 自动将代理配置设置到环境变量
# 这样 requests 库可以直接读取 os.environ['NO_PROXY']
if settings.HTTP_PROXY:
os.environ['HTTP_PROXY'] = settings.HTTP_PROXY
if settings.HTTPS_PROXY:
os.environ['HTTPS_PROXY'] = settings.HTTPS_PROXY
if settings.NO_PROXY:
os.environ['NO_PROXY'] = settings.NO_PROXY
def get_settings() -> Settings:
"""获取配置实例"""
return settings
================================================
FILE: app/core/config_bridge.py
================================================
"""
配置桥接模块
将统一配置系统的配置桥接到环境变量,供 TradingAgents 核心库使用
"""
import os
import json
import logging
from pathlib import Path
from typing import Optional
logger = logging.getLogger("app.config_bridge")
def bridge_config_to_env():
"""
将统一配置桥接到环境变量
这个函数会:
1. 从数据库读取大模型厂家配置(API 密钥、超时、温度等)
2. 将配置写入环境变量
3. 将默认模型写入环境变量
4. 将数据源配置写入环境变量(API 密钥、超时、重试等)
5. 将系统运行时配置写入环境变量
这样 TradingAgents 核心库就能通过环境变量读取到用户配置的数据
"""
try:
from app.core.unified_config import unified_config
from app.services.config_service import config_service
logger.info("🔧 开始桥接配置到环境变量...")
bridged_count = 0
# 强制启用 MongoDB 存储(用于 Token 使用统计)
# 从 .env 文件读取配置,如果未设置则默认启用
use_mongodb_storage = os.getenv("USE_MONGODB_STORAGE", "true")
os.environ["USE_MONGODB_STORAGE"] = use_mongodb_storage
logger.info(f" ✓ 桥接 USE_MONGODB_STORAGE: {use_mongodb_storage}")
bridged_count += 1
# 桥接 MongoDB 连接字符串
mongodb_conn_str = os.getenv("MONGODB_CONNECTION_STRING")
if mongodb_conn_str:
os.environ["MONGODB_CONNECTION_STRING"] = mongodb_conn_str
logger.info(f" ✓ 桥接 MONGODB_CONNECTION_STRING (长度: {len(mongodb_conn_str)})")
bridged_count += 1
# 桥接 MongoDB 数据库名称
mongodb_db_name = os.getenv("MONGODB_DATABASE_NAME", "tradingagents")
os.environ["MONGODB_DATABASE_NAME"] = mongodb_db_name
logger.info(f" ✓ 桥接 MONGODB_DATABASE_NAME: {mongodb_db_name}")
bridged_count += 1
# 1. 桥接大模型配置(基础 API 密钥)
# 🔧 [优先级] .env 文件 > 数据库厂家配置
# 🔥 修改:从数据库的 llm_providers 集合读取厂家配置,而不是从 JSON 文件
# 只有当环境变量不存在或为占位符时,才使用数据库中的配置
try:
# 使用同步 MongoDB 客户端读取厂家配置
from pymongo import MongoClient
from app.core.config import settings
from app.models.config import LLMProvider
# 创建同步 MongoDB 客户端
client = MongoClient(settings.MONGO_URI)
db = client[settings.MONGO_DB]
providers_collection = db.llm_providers
# 查询所有厂家配置
providers_data = list(providers_collection.find())
providers = [LLMProvider(**data) for data in providers_data]
logger.info(f" 📊 从数据库读取到 {len(providers)} 个厂家配置")
for provider in providers:
if not provider.is_active:
logger.debug(f" ⏭️ 厂家 {provider.name} 未启用,跳过")
continue
env_key = f"{provider.name.upper()}_API_KEY"
existing_env_value = os.getenv(env_key)
# 检查环境变量是否已存在且有效(不是占位符)
if existing_env_value and not existing_env_value.startswith("your_"):
logger.info(f" ✓ 使用 .env 文件中的 {env_key} (长度: {len(existing_env_value)})")
bridged_count += 1
elif provider.api_key and not provider.api_key.startswith("your_"):
# 只有当环境变量不存在或为占位符时,才使用数据库配置
os.environ[env_key] = provider.api_key
logger.info(f" ✓ 使用数据库厂家配置的 {env_key} (长度: {len(provider.api_key)})")
bridged_count += 1
else:
logger.debug(f" ⏭️ {env_key} 未配置有效的 API Key")
# 关闭同步客户端
client.close()
except Exception as e:
logger.error(f"❌ 从数据库读取厂家配置失败: {e}", exc_info=True)
logger.warning("⚠️ 将尝试从 JSON 文件读取配置作为后备方案")
# 后备方案:从 JSON 文件读取
llm_configs = unified_config.get_llm_configs()
for llm_config in llm_configs:
# provider 现在是字符串类型,不再是枚举
env_key = f"{llm_config.provider.upper()}_API_KEY"
existing_env_value = os.getenv(env_key)
# 检查环境变量是否已存在且有效(不是占位符)
if existing_env_value and not existing_env_value.startswith("your_"):
logger.info(f" ✓ 使用 .env 文件中的 {env_key} (长度: {len(existing_env_value)})")
bridged_count += 1
elif llm_config.enabled and llm_config.api_key:
# 只有当环境变量不存在或为占位符时,才使用数据库配置
if not llm_config.api_key.startswith("your_"):
os.environ[env_key] = llm_config.api_key
logger.info(f" ✓ 使用 JSON 文件中的 {env_key} (长度: {len(llm_config.api_key)})")
bridged_count += 1
else:
logger.warning(f" ⚠️ {env_key} 在 .env 和 JSON 文件中都是占位符,跳过")
else:
logger.debug(f" ⏭️ {env_key} 未配置")
# 2. 桥接默认模型配置
default_model = unified_config.get_default_model()
if default_model:
os.environ['TRADINGAGENTS_DEFAULT_MODEL'] = default_model
logger.info(f" ✓ 桥接默认模型: {default_model}")
bridged_count += 1
quick_model = unified_config.get_quick_analysis_model()
if quick_model:
os.environ['TRADINGAGENTS_QUICK_MODEL'] = quick_model
logger.info(f" ✓ 桥接快速分析模型: {quick_model}")
bridged_count += 1
deep_model = unified_config.get_deep_analysis_model()
if deep_model:
os.environ['TRADINGAGENTS_DEEP_MODEL'] = deep_model
logger.info(f" ✓ 桥接深度分析模型: {deep_model}")
bridged_count += 1
# 3. 桥接数据源配置(基础 API 密钥)
# 🔧 [优先级] .env 文件 > 数据库配置
# 🔥 修改:从数据库的 system_configs 集合读取数据源配置,而不是从 JSON 文件
try:
# 使用同步 MongoDB 客户端读取系统配置
from pymongo import MongoClient
from app.core.config import settings
from app.models.config import SystemConfig
# 创建同步 MongoDB 客户端
client = MongoClient(settings.MONGO_URI)
db = client[settings.MONGO_DB]
config_collection = db.system_configs
# 查询最新的系统配置
config_data = config_collection.find_one(
{"is_active": True},
sort=[("version", -1)]
)
if config_data and config_data.get('data_source_configs'):
system_config = SystemConfig(**config_data)
data_source_configs = system_config.data_source_configs
logger.info(f" 📊 从数据库读取到 {len(data_source_configs)} 个数据源配置")
else:
logger.warning(" ⚠️ 数据库中没有数据源配置,使用 JSON 文件配置")
data_source_configs = unified_config.get_data_source_configs()
# 关闭同步客户端
client.close()
except Exception as e:
logger.error(f"❌ 从数据库读取数据源配置失败: {e}", exc_info=True)
logger.warning("⚠️ 将尝试从 JSON 文件读取配置作为后备方案")
data_source_configs = unified_config.get_data_source_configs()
for ds_config in data_source_configs:
if ds_config.enabled and ds_config.api_key:
# Tushare Token
# 🔥 优先级:数据库配置 > .env 文件(用户在 Web 后台修改后立即生效)
if ds_config.type.value == 'tushare':
existing_token = os.getenv('TUSHARE_TOKEN')
# 优先使用数据库配置
if ds_config.api_key and not ds_config.api_key.startswith("your_"):
os.environ['TUSHARE_TOKEN'] = ds_config.api_key
logger.info(f" ✓ 使用数据库中的 TUSHARE_TOKEN (长度: {len(ds_config.api_key)})")
if existing_token and existing_token != ds_config.api_key:
logger.info(f" ℹ️ 已覆盖 .env 文件中的 TUSHARE_TOKEN")
# 降级到 .env 文件配置
elif existing_token and not existing_token.startswith("your_"):
logger.info(f" ✓ 使用 .env 文件中的 TUSHARE_TOKEN (长度: {len(existing_token)})")
logger.info(f" ℹ️ 数据库中未配置有效的 TUSHARE_TOKEN,使用 .env 降级方案")
else:
logger.warning(f" ⚠️ TUSHARE_TOKEN 在数据库和 .env 中都未配置有效值")
continue
bridged_count += 1
# FinnHub API Key
# 🔥 优先级:数据库配置 > .env 文件
elif ds_config.type.value == 'finnhub':
existing_key = os.getenv('FINNHUB_API_KEY')
# 优先使用数据库配置
if ds_config.api_key and not ds_config.api_key.startswith("your_"):
os.environ['FINNHUB_API_KEY'] = ds_config.api_key
logger.info(f" ✓ 使用数据库中的 FINNHUB_API_KEY (长度: {len(ds_config.api_key)})")
if existing_key and existing_key != ds_config.api_key:
logger.info(f" ℹ️ 已覆盖 .env 文件中的 FINNHUB_API_KEY")
# 降级到 .env 文件配置
elif existing_key and not existing_key.startswith("your_"):
logger.info(f" ✓ 使用 .env 文件中的 FINNHUB_API_KEY (长度: {len(existing_key)})")
logger.info(f" ℹ️ 数据库中未配置有效的 FINNHUB_API_KEY,使用 .env 降级方案")
else:
logger.warning(f" ⚠️ FINNHUB_API_KEY 在数据库和 .env 中都未配置有效值")
continue
bridged_count += 1
# 4. 桥接数据源细节配置(超时、重试、缓存等)
bridged_count += _bridge_datasource_details(data_source_configs)
# 5. 桥接系统运行时配置
bridged_count += _bridge_system_settings()
# 6. 重新初始化 tradingagents 库的 MongoDB 存储
# 因为全局 config_manager 实例是在模块导入时创建的,那时环境变量还没有被桥接
try:
from tradingagents.config.config_manager import config_manager
from tradingagents.config.mongodb_storage import MongoDBStorage
logger.info("🔄 重新初始化 tradingagents MongoDB 存储...")
# 调试:检查环境变量
use_mongodb = os.getenv("USE_MONGODB_STORAGE", "false")
mongodb_conn = os.getenv("MONGODB_CONNECTION_STRING", "未设置")
mongodb_db = os.getenv("MONGODB_DATABASE_NAME", "tradingagents")
logger.info(f" 📋 USE_MONGODB_STORAGE: {use_mongodb}")
logger.info(f" 📋 MONGODB_CONNECTION_STRING: {mongodb_conn[:30]}..." if len(mongodb_conn) > 30 else f" 📋 MONGODB_CONNECTION_STRING: {mongodb_conn}")
logger.info(f" 📋 MONGODB_DATABASE_NAME: {mongodb_db}")
# 直接创建 MongoDBStorage 实例,而不是调用 _init_mongodb_storage()
# 这样可以捕获更详细的错误信息
if use_mongodb.lower() == "true":
try:
# 🔍 详细日志:显示完整的连接字符串(用于调试)
logger.info(f" 🔍 实际传入的连接字符串: {mongodb_conn}")
logger.info(f" 🔍 实际传入的数据库名称: {mongodb_db}")
config_manager.mongodb_storage = MongoDBStorage(
connection_string=mongodb_conn,
database_name=mongodb_db
)
if config_manager.mongodb_storage.is_connected():
logger.info("✅ tradingagents MongoDB 存储已启用")
else:
logger.warning("⚠️ tradingagents MongoDB 连接失败,将使用 JSON 文件存储")
config_manager.mongodb_storage = None
except Exception as e:
logger.error(f"❌ 创建 MongoDBStorage 实例失败: {e}")
import traceback
logger.error(traceback.format_exc())
config_manager.mongodb_storage = None
else:
logger.info("ℹ️ USE_MONGODB_STORAGE 未启用,将使用 JSON 文件存储")
except Exception as e:
logger.error(f"❌ 重新初始化 tradingagents MongoDB 存储失败: {e}")
import traceback
logger.error(traceback.format_exc())
# 7. 同步定价配置到 tradingagents 的 config/pricing.json
# 注意:这里需要从数据库读取配置,因为文件中的配置没有定价信息
# 使用异步方式同步定价配置
import asyncio
try:
loop = asyncio.get_running_loop()
# 在异步上下文中,创建后台任务
task = loop.create_task(_sync_pricing_config_from_db())
task.add_done_callback(_handle_sync_task_result)
logger.info("🔄 定价配置同步任务已创建(后台执行)")
except RuntimeError:
# 不在异步上下文中,使用 asyncio.run
asyncio.run(_sync_pricing_config_from_db())
logger.info(f"✅ 配置桥接完成,共桥接 {bridged_count} 项配置")
return True
except Exception as e:
logger.error(f"❌ 配置桥接失败: {e}", exc_info=True)
logger.warning("⚠️ TradingAgents 将使用 .env 文件中的配置")
return False
def _bridge_datasource_details(data_source_configs) -> int:
"""
桥接数据源细节配置到环境变量
Args:
data_source_configs: 数据源配置列表
Returns:
int: 桥接的配置项数量
"""
bridged_count = 0
for ds_config in data_source_configs:
if not ds_config.enabled:
continue
# 注意:字段名是 type 而不是 source_type
source_type = ds_config.type.value.upper()
# 超时时间
if ds_config.timeout:
env_key = f"{source_type}_TIMEOUT"
os.environ[env_key] = str(ds_config.timeout)
logger.debug(f" ✓ 桥接 {env_key}: {ds_config.timeout}")
bridged_count += 1
# 速率限制
if ds_config.rate_limit:
env_key = f"{source_type}_RATE_LIMIT"
os.environ[env_key] = str(ds_config.rate_limit / 60.0) # 转换为每秒请求数
logger.debug(f" ✓ 桥接 {env_key}: {ds_config.rate_limit / 60.0}")
bridged_count += 1
# 最大重试次数(从 config_params 中获取)
if ds_config.config_params and 'max_retries' in ds_config.config_params:
env_key = f"{source_type}_MAX_RETRIES"
os.environ[env_key] = str(ds_config.config_params['max_retries'])
logger.debug(f" ✓ 桥接 {env_key}: {ds_config.config_params['max_retries']}")
bridged_count += 1
# 缓存 TTL(从 config_params 中获取)
if ds_config.config_params and 'cache_ttl' in ds_config.config_params:
env_key = f"{source_type}_CACHE_TTL"
os.environ[env_key] = str(ds_config.config_params['cache_ttl'])
logger.debug(f" ✓ 桥接 {env_key}: {ds_config.config_params['cache_ttl']}")
bridged_count += 1
# 是否启用缓存(从 config_params 中获取)
if ds_config.config_params and 'cache_enabled' in ds_config.config_params:
env_key = f"{source_type}_CACHE_ENABLED"
os.environ[env_key] = str(ds_config.config_params['cache_enabled']).lower()
logger.debug(f" ✓ 桥接 {env_key}: {ds_config.config_params['cache_enabled']}")
bridged_count += 1
if bridged_count > 0:
logger.info(f" ✓ 桥接数据源细节配置: {bridged_count} 项")
return bridged_count
def _bridge_system_settings() -> int:
"""
桥接系统运行时配置到环境变量
Returns:
int: 桥接的配置项数量
"""
try:
# 使用同步的 MongoDB 客户端
from pymongo import MongoClient
from app.core.config import settings
# 创建同步客户端
client = MongoClient(
settings.MONGO_URI,
serverSelectionTimeoutMS=5000,
connectTimeoutMS=5000
)
try:
db = client[settings.MONGO_DB]
# 从 system_configs 集合中读取激活的配置
config_doc = db.system_configs.find_one({"is_active": True})
if not config_doc or 'system_settings' not in config_doc:
logger.debug(" ⚠️ 系统设置为空,跳过桥接")
return 0
system_settings = config_doc['system_settings']
except Exception as e:
logger.debug(f" ⚠️ 无法从数据库获取系统设置: {e}")
import traceback
logger.debug(traceback.format_exc())
return 0
finally:
client.close()
if not system_settings:
logger.debug(" ⚠️ 系统设置为空,跳过桥接")
return 0
logger.debug(f" 📋 获取到 {len(system_settings)} 个系统设置")
bridged_count = 0
# TradingAgents 运行时配置
ta_settings = {
'ta_hk_min_request_interval_seconds': 'TA_HK_MIN_REQUEST_INTERVAL_SECONDS',
'ta_hk_timeout_seconds': 'TA_HK_TIMEOUT_SECONDS',
'ta_hk_max_retries': 'TA_HK_MAX_RETRIES',
'ta_hk_rate_limit_wait_seconds': 'TA_HK_RATE_LIMIT_WAIT_SECONDS',
'ta_hk_cache_ttl_seconds': 'TA_HK_CACHE_TTL_SECONDS',
'ta_use_app_cache': 'TA_USE_APP_CACHE',
}
# Token 使用统计配置
token_tracking_settings = {
'enable_cost_tracking': 'ENABLE_COST_TRACKING',
'auto_save_usage': 'AUTO_SAVE_USAGE',
}
for setting_key, env_key in ta_settings.items():
# 检查 .env 文件中是否已经设置了该环境变量
env_value = os.getenv(env_key)
if env_value is not None:
# .env 文件中已设置,优先使用 .env 的值
logger.info(f" ✓ 使用 .env 文件中的 {env_key}: {env_value}")
bridged_count += 1
elif setting_key in system_settings:
# .env 文件中未设置,使用数据库中的值
value = system_settings[setting_key]
os.environ[env_key] = str(value).lower() if isinstance(value, bool) else str(value)
logger.info(f" ✓ 桥接 {env_key}: {value}")
bridged_count += 1
else:
logger.debug(f" ⚠️ 配置键 {setting_key} 不存在于系统设置中")
# 桥接 Token 使用统计配置
for setting_key, env_key in token_tracking_settings.items():
if setting_key in system_settings:
value = system_settings[setting_key]
os.environ[env_key] = str(value).lower() if isinstance(value, bool) else str(value)
logger.info(f" ✓ 桥接 {env_key}: {value}")
bridged_count += 1
else:
logger.debug(f" ⚠️ 配置键 {setting_key} 不存在于系统设置中")
# 时区配置
if 'app_timezone' in system_settings:
os.environ['APP_TIMEZONE'] = system_settings['app_timezone']
logger.debug(f" ✓ 桥接 APP_TIMEZONE: {system_settings['app_timezone']}")
bridged_count += 1
# 货币偏好
if 'currency_preference' in system_settings:
os.environ['CURRENCY_PREFERENCE'] = system_settings['currency_preference']
logger.debug(f" ✓ 桥接 CURRENCY_PREFERENCE: {system_settings['currency_preference']}")
bridged_count += 1
if bridged_count > 0:
logger.info(f" ✓ 桥接系统运行时配置: {bridged_count} 项")
# 同步到文件系统(供 unified_config 使用)
try:
print(f"🔄 [config_bridge] 准备同步系统设置到文件系统")
print(f"🔄 [config_bridge] system_settings 包含 {len(system_settings)} 项")
# 检查关键字段
if "quick_analysis_model" in system_settings:
print(f" ✓ [config_bridge] 包含 quick_analysis_model: {system_settings['quick_analysis_model']}")
else:
print(f" ⚠️ [config_bridge] 不包含 quick_analysis_model")
if "deep_analysis_model" in system_settings:
print(f" ✓ [config_bridge] 包含 deep_analysis_model: {system_settings['deep_analysis_model']}")
else:
print(f" ⚠️ [config_bridge] 不包含 deep_analysis_model")
from app.core.unified_config import unified_config
result = unified_config.save_system_settings(system_settings)
if result:
logger.info(f" ✓ 系统设置已同步到文件系统")
print(f"✅ [config_bridge] 系统设置同步成功")
else:
logger.warning(f" ⚠️ 系统设置同步返回 False")
print(f"⚠️ [config_bridge] 系统设置同步返回 False")
except Exception as e:
logger.warning(f" ⚠️ 同步系统设置到文件系统失败: {e}")
print(f"❌ [config_bridge] 同步系统设置到文件系统失败: {e}")
import traceback
print(traceback.format_exc())
return bridged_count
except Exception as e:
logger.warning(f" ⚠️ 桥接系统设置失败: {e}")
return 0
def get_bridged_api_key(provider: str) -> Optional[str]:
"""
获取桥接的 API 密钥
Args:
provider: 提供商名称 (如: openai, deepseek, dashscope)
Returns:
API 密钥,如果不存在返回 None
"""
env_key = f"{provider.upper()}_API_KEY"
return os.environ.get(env_key)
def get_bridged_model(model_type: str = "default") -> Optional[str]:
"""
获取桥接的模型名称
Args:
model_type: 模型类型 (default, quick, deep)
Returns:
模型名称,如果不存在返回 None
"""
if model_type == "quick":
return os.environ.get('TRADINGAGENTS_QUICK_MODEL')
elif model_type == "deep":
return os.environ.get('TRADINGAGENTS_DEEP_MODEL')
else:
return os.environ.get('TRADINGAGENTS_DEFAULT_MODEL')
def clear_bridged_config():
"""
清除桥接的配置
用于测试或重新加载配置
"""
keys_to_clear = [
# 模型配置
'TRADINGAGENTS_DEFAULT_MODEL',
'TRADINGAGENTS_QUICK_MODEL',
'TRADINGAGENTS_DEEP_MODEL',
# 数据源 API 密钥
'TUSHARE_TOKEN',
'FINNHUB_API_KEY',
# 系统配置
'APP_TIMEZONE',
'CURRENCY_PREFERENCE',
]
# 清除所有可能的 API 密钥
providers = ['OPENAI', 'ANTHROPIC', 'GOOGLE', 'DEEPSEEK', 'DASHSCOPE', 'QIANFAN']
for provider in providers:
keys_to_clear.append(f'{provider}_API_KEY')
# 清除数据源细节配置
data_sources = ['TUSHARE', 'AKSHARE', 'FINNHUB']
for ds in data_sources:
keys_to_clear.extend([
f'{ds}_TIMEOUT',
f'{ds}_RATE_LIMIT',
f'{ds}_MAX_RETRIES',
f'{ds}_CACHE_TTL',
f'{ds}_CACHE_ENABLED',
])
# 清除 TradingAgents 运行时配置
ta_runtime_keys = [
'TA_HK_MIN_REQUEST_INTERVAL_SECONDS',
'TA_HK_TIMEOUT_SECONDS',
'TA_HK_MAX_RETRIES',
'TA_HK_RATE_LIMIT_WAIT_SECONDS',
'TA_HK_CACHE_TTL_SECONDS',
'TA_USE_APP_CACHE',
]
keys_to_clear.extend(ta_runtime_keys)
for key in keys_to_clear:
if key in os.environ:
del os.environ[key]
logger.debug(f" 清除环境变量: {key}")
logger.info("✅ 已清除所有桥接的配置")
def reload_bridged_config():
"""
重新加载桥接的配置
用于配置更新后重新桥接
"""
logger.info("🔄 重新加载配置桥接...")
clear_bridged_config()
return bridge_config_to_env()
def _sync_pricing_config(llm_configs):
"""
同步定价配置到 tradingagents 的 config/pricing.json
Args:
llm_configs: LLM 配置列表
"""
try:
# 获取项目根目录的 config 目录
project_root = Path(__file__).parent.parent.parent
config_dir = project_root / "config"
config_dir.mkdir(exist_ok=True)
pricing_file = config_dir / "pricing.json"
# 构建定价配置列表
pricing_configs = []
for llm_config in llm_configs:
if llm_config.enabled:
pricing_config = {
# provider 现在是字符串类型,不再是枚举
"provider": llm_config.provider,
"model_name": llm_config.model_name,
"input_price_per_1k": llm_config.input_price_per_1k or 0.0,
"output_price_per_1k": llm_config.output_price_per_1k or 0.0,
"currency": llm_config.currency or "CNY"
}
pricing_configs.append(pricing_config)
# 保存到文件
with open(pricing_file, 'w', encoding='utf-8') as f:
json.dump(pricing_configs, f, ensure_ascii=False, indent=2)
logger.info(f" ✓ 同步定价配置到 {pricing_file}: {len(pricing_configs)} 个模型")
except Exception as e:
logger.warning(f" ⚠️ 同步定价配置失败: {e}")
def sync_pricing_config_now():
"""
立即同步定价配置(用于配置更新后实时同步)
注意:这个函数会在后台异步执行同步操作
"""
import asyncio
try:
# 如果在异步上下文中,创建后台任务
try:
loop = asyncio.get_running_loop()
# 在异步上下文中,创建一个后台任务(不等待完成)
task = loop.create_task(_sync_pricing_config_from_db())
# 添加回调来记录错误
task.add_done_callback(_handle_sync_task_result)
logger.info("🔄 定价配置同步任务已创建(后台执行)")
return True
except RuntimeError:
# 不在异步上下文中,使用 asyncio.run
asyncio.run(_sync_pricing_config_from_db())
return True
except Exception as e:
logger.error(f"❌ 立即同步定价配置失败: {e}")
import traceback
logger.error(traceback.format_exc())
return False
def _handle_sync_task_result(task):
"""处理同步任务的结果"""
try:
task.result()
except Exception as e:
logger.error(f"❌ 定价配置同步任务执行失败: {e}")
import traceback
logger.error(traceback.format_exc())
async def _sync_pricing_config_from_db():
"""
从数据库同步定价配置(异步版本)
"""
try:
from app.core.database import get_mongo_db
from app.models.config import LLMConfig
db = get_mongo_db()
# 获取最新的激活配置
config = await db['system_configs'].find_one(
{'is_active': True},
sort=[('version', -1)]
)
if not config:
logger.warning("⚠️ 未找到激活的配置")
return
# 获取项目根目录的 config 目录
project_root = Path(__file__).parent.parent.parent
config_dir = project_root / "config"
config_dir.mkdir(exist_ok=True)
pricing_file = config_dir / "pricing.json"
# 构建定价配置列表
pricing_configs = []
for llm_config in config.get('llm_configs', []):
if llm_config.get('enabled', False):
# 从数据库读取的是字典,直接使用字符串 provider
provider = llm_config.get('provider')
# 如果 provider 是枚举类型,转换为字符串
if hasattr(provider, 'value'):
provider = provider.value
pricing_config = {
"provider": provider,
"model_name": llm_config.get('model_name'),
"input_price_per_1k": llm_config.get('input_price_per_1k') or 0.0,
"output_price_per_1k": llm_config.get('output_price_per_1k') or 0.0,
"currency": llm_config.get('currency') or "CNY"
}
pricing_configs.append(pricing_config)
# 保存到文件
with open(pricing_file, 'w', encoding='utf-8') as f:
json.dump(pricing_configs, f, ensure_ascii=False, indent=2)
logger.info(f"✅ 同步定价配置到 {pricing_file}: {len(pricing_configs)} 个模型")
except Exception as e:
logger.error(f"❌ 从数据库同步定价配置失败: {e}")
import traceback
logger.error(traceback.format_exc())
# 导出函数
__all__ = [
'bridge_config_to_env',
'get_bridged_api_key',
'get_bridged_model',
'clear_bridged_config',
'reload_bridged_config',
'sync_pricing_config_now',
]
================================================
FILE: app/core/config_compat.py
================================================
"""
配置系统兼容层
为旧的 tradingagents 库提供配置兼容接口,
使其能够使用新的配置系统而无需修改代码。
⚠️ 此模块仅用于向后兼容,新代码应直接使用 ConfigService
"""
import os
import asyncio
from typing import Dict, Any, Optional, List
from functools import lru_cache
import warnings
from app.core.config import settings
class ConfigManagerCompat:
"""
ConfigManager 兼容类
提供与旧 ConfigManager 相同的接口,但使用新的配置系统。
"""
def __init__(self):
"""初始化兼容层"""
self._warned = False
self._emit_deprecation_warning()
def _emit_deprecation_warning(self):
"""发出废弃警告(仅一次)"""
if not self._warned:
warnings.warn(
"ConfigManagerCompat is a compatibility layer for legacy code. "
"Please migrate to app.services.config_service.ConfigService. "
"See docs/DEPRECATION_NOTICE.md for details.",
DeprecationWarning,
stacklevel=3
)
self._warned = True
def get_data_dir(self) -> str:
"""
获取数据目录
Returns:
str: 数据目录路径
"""
# 优先从环境变量读取
data_dir = os.getenv("DATA_DIR")
if data_dir:
return data_dir
# 默认值
return "./data"
def load_settings(self) -> Dict[str, Any]:
"""
加载系统设置
Returns:
Dict[str, Any]: 系统设置字典
"""
try:
# 尝试从新配置系统加载
from app.services.config_service import config_service
# 在同步上下文中运行异步代码
loop = asyncio.get_event_loop()
if loop.is_running():
# 如果事件循环正在运行,返回默认值
return self._get_default_settings()
else:
config = loop.run_until_complete(config_service.get_system_config())
if config and config.system_settings:
return config.system_settings
except Exception:
pass
# 返回默认设置
return self._get_default_settings()
def save_settings(self, settings_dict: Dict[str, Any]) -> bool:
"""
保存系统设置
Args:
settings_dict: 系统设置字典
Returns:
bool: 是否保存成功
"""
try:
from app.services.config_service import config_service
loop = asyncio.get_event_loop()
if loop.is_running():
# 如果事件循环正在运行,无法保存
warnings.warn("Cannot save settings in running event loop", RuntimeWarning)
return False
else:
loop.run_until_complete(
config_service.update_system_settings(settings_dict)
)
return True
except Exception as e:
warnings.warn(f"Failed to save settings: {e}", RuntimeWarning)
return False
def get_models(self) -> List[Dict[str, Any]]:
"""
获取模型配置列表
Returns:
List[Dict[str, Any]]: 模型配置列表
"""
try:
from app.services.config_service import config_service
loop = asyncio.get_event_loop()
if loop.is_running():
return []
else:
config = loop.run_until_complete(config_service.get_system_config())
if config and config.llm_configs:
return [
{
"provider": llm.provider,
"model_name": llm.model_name,
"api_key": llm.api_key or "",
"base_url": llm.base_url,
"max_tokens": llm.max_tokens,
"temperature": llm.temperature,
"enabled": llm.enabled,
}
for llm in config.llm_configs
]
except Exception:
pass
return []
def get_model_config(self, provider: str, model_name: str) -> Optional[Dict[str, Any]]:
"""
获取指定模型的配置
Args:
provider: 提供商名称
model_name: 模型名称
Returns:
Optional[Dict[str, Any]]: 模型配置,如果不存在则返回 None
"""
models = self.get_models()
for model in models:
if model["provider"] == provider and model["model_name"] == model_name:
return model
return None
def _get_default_settings(self) -> Dict[str, Any]:
"""
获取默认系统设置
Returns:
Dict[str, Any]: 默认设置
"""
return {
"max_debate_rounds": 1,
"max_risk_discuss_rounds": 1,
"online_tools": True,
"online_news": True,
"realtime_data": False,
"memory_enabled": True,
"debug": False,
}
class TokenTrackerCompat:
"""
TokenTracker 兼容类
提供与旧 TokenTracker 相同的接口。
"""
def __init__(self):
"""初始化兼容层"""
self._usage_data = {}
def track_usage(
self,
provider: str,
model_name: str,
input_tokens: int,
output_tokens: int,
cost: float = 0.0
):
"""
记录 Token 使用量
Args:
provider: 提供商名称
model_name: 模型名称
input_tokens: 输入 Token 数
output_tokens: 输出 Token 数
cost: 成本
"""
key = f"{provider}:{model_name}"
if key not in self._usage_data:
self._usage_data[key] = {
"provider": provider,
"model_name": model_name,
"total_input_tokens": 0,
"total_output_tokens": 0,
"total_cost": 0.0,
"call_count": 0,
}
self._usage_data[key]["total_input_tokens"] += input_tokens
self._usage_data[key]["total_output_tokens"] += output_tokens
self._usage_data[key]["total_cost"] += cost
self._usage_data[key]["call_count"] += 1
# 注意:此兼容层仅提供内存缓存,不持久化到数据库
# 如需持久化,请使用 app.services.llm_service 中的相关功能
def get_usage_summary(self) -> Dict[str, Any]:
"""
获取使用统计摘要
Returns:
Dict[str, Any]: 使用统计摘要
"""
return self._usage_data.copy()
def reset_usage(self):
"""重置使用统计"""
self._usage_data.clear()
# 创建全局实例(用于向后兼容)
config_manager_compat = ConfigManagerCompat()
token_tracker_compat = TokenTrackerCompat()
# 便捷函数
def get_config_manager() -> ConfigManagerCompat:
"""
获取配置管理器兼容实例
Returns:
ConfigManagerCompat: 配置管理器兼容实例
"""
return config_manager_compat
def get_token_tracker() -> TokenTrackerCompat:
"""
获取 Token 跟踪器兼容实例
Returns:
TokenTrackerCompat: Token 跟踪器兼容实例
"""
return token_tracker_compat
================================================
FILE: app/core/database.py
================================================
"""
数据库连接管理模块
增强版本,支持连接池、健康检查和错误恢复
"""
import logging
import asyncio
from typing import Optional
from motor.motor_asyncio import AsyncIOMotorClient, AsyncIOMotorDatabase
from pymongo import MongoClient
from pymongo.database import Database
from redis.asyncio import Redis, ConnectionPool
from pymongo.errors import ServerSelectionTimeoutError, ConnectionFailure
from redis.exceptions import ConnectionError as RedisConnectionError
from .config import settings
logger = logging.getLogger(__name__)
# 全局连接实例
mongo_client: Optional[AsyncIOMotorClient] = None
mongo_db: Optional[AsyncIOMotorDatabase] = None
redis_client: Optional[Redis] = None
redis_pool: Optional[ConnectionPool] = None
# 同步 MongoDB 连接(用于非异步上下文)
_sync_mongo_client: Optional[MongoClient] = None
_sync_mongo_db: Optional[Database] = None
class DatabaseManager:
"""数据库连接管理器"""
def __init__(self):
self.mongo_client: Optional[AsyncIOMotorClient] = None
self.mongo_db: Optional[AsyncIOMotorDatabase] = None
self.redis_client: Optional[Redis] = None
self.redis_pool: Optional[ConnectionPool] = None
self._mongo_healthy = False
self._redis_healthy = False
async def init_mongodb(self):
"""初始化MongoDB连接"""
try:
logger.info("🔄 正在初始化MongoDB连接...")
# 创建MongoDB客户端,配置连接池
self.mongo_client = AsyncIOMotorClient(
settings.MONGO_URI,
maxPoolSize=settings.MONGO_MAX_CONNECTIONS,
minPoolSize=settings.MONGO_MIN_CONNECTIONS,
maxIdleTimeMS=30000, # 30秒空闲超时
serverSelectionTimeoutMS=settings.MONGO_SERVER_SELECTION_TIMEOUT_MS, # 服务器选择超时
connectTimeoutMS=settings.MONGO_CONNECT_TIMEOUT_MS, # 连接超时
socketTimeoutMS=settings.MONGO_SOCKET_TIMEOUT_MS, # 套接字超时
)
# 获取数据库实例
self.mongo_db = self.mongo_client[settings.MONGO_DB]
# 测试连接
await self.mongo_client.admin.command('ping')
self._mongo_healthy = True
logger.info("✅ MongoDB连接成功建立")
logger.info(f"📊 数据库: {settings.MONGO_DB}")
logger.info(f"🔗 连接池: {settings.MONGO_MIN_CONNECTIONS}-{settings.MONGO_MAX_CONNECTIONS}")
logger.info(f"⏱️ 超时配置: connectTimeout={settings.MONGO_CONNECT_TIMEOUT_MS}ms, socketTimeout={settings.MONGO_SOCKET_TIMEOUT_MS}ms")
except Exception as e:
logger.error(f"❌ MongoDB连接失败: {e}")
self._mongo_healthy = False
raise
async def init_redis(self):
"""初始化Redis连接"""
try:
logger.info("🔄 正在初始化Redis连接...")
# 创建Redis连接池
self.redis_pool = ConnectionPool.from_url(
settings.REDIS_URL,
max_connections=settings.REDIS_MAX_CONNECTIONS,
retry_on_timeout=settings.REDIS_RETRY_ON_TIMEOUT,
decode_responses=True,
socket_connect_timeout=5, # 5秒连接超时
socket_timeout=10, # 10秒套接字超时
)
# 创建Redis客户端
self.redis_client = Redis(connection_pool=self.redis_pool)
# 测试连接
await self.redis_client.ping()
self._redis_healthy = True
logger.info("✅ Redis连接成功建立")
logger.info(f"🔗 连接池大小: {settings.REDIS_MAX_CONNECTIONS}")
except Exception as e:
logger.error(f"❌ Redis连接失败: {e}")
self._redis_healthy = False
raise
async def close_connections(self):
"""关闭所有数据库连接"""
logger.info("🔄 正在关闭数据库连接...")
# 关闭MongoDB连接
if self.mongo_client:
try:
self.mongo_client.close()
self._mongo_healthy = False
logger.info("✅ MongoDB连接已关闭")
except Exception as e:
logger.error(f"❌ 关闭MongoDB连接时出错: {e}")
# 关闭Redis连接
if self.redis_client:
try:
await self.redis_client.close()
self._redis_healthy = False
logger.info("✅ Redis连接已关闭")
except Exception as e:
logger.error(f"❌ 关闭Redis连接时出错: {e}")
# 关闭Redis连接池
if self.redis_pool:
try:
await self.redis_pool.disconnect()
logger.info("✅ Redis连接池已关闭")
except Exception as e:
logger.error(f"❌ 关闭Redis连接池时出错: {e}")
async def health_check(self) -> dict:
"""数据库健康检查"""
health_status = {
"mongodb": {"status": "unknown", "details": None},
"redis": {"status": "unknown", "details": None}
}
# 检查MongoDB
try:
if self.mongo_client:
result = await self.mongo_client.admin.command('ping')
health_status["mongodb"] = {
"status": "healthy",
"details": {"ping": result, "database": settings.MONGO_DB}
}
self._mongo_healthy = True
else:
health_status["mongodb"]["status"] = "disconnected"
except Exception as e:
health_status["mongodb"] = {
"status": "unhealthy",
"details": {"error": str(e)}
}
self._mongo_healthy = False
# 检查Redis
try:
if self.redis_client:
result = await self.redis_client.ping()
health_status["redis"] = {
"status": "healthy",
"details": {"ping": result}
}
self._redis_healthy = True
else:
health_status["redis"]["status"] = "disconnected"
except Exception as e:
health_status["redis"] = {
"status": "unhealthy",
"details": {"error": str(e)}
}
self._redis_healthy = False
return health_status
@property
def is_healthy(self) -> bool:
"""检查所有数据库连接是否健康"""
return self._mongo_healthy and self._redis_healthy
# 全局数据库管理器实例
db_manager = DatabaseManager()
async def init_database():
"""初始化数据库连接"""
global mongo_client, mongo_db, redis_client, redis_pool
try:
# 初始化MongoDB
await db_manager.init_mongodb()
mongo_client = db_manager.mongo_client
mongo_db = db_manager.mongo_db
# 初始化Redis
await db_manager.init_redis()
redis_client = db_manager.redis_client
redis_pool = db_manager.redis_pool
logger.info("🎉 所有数据库连接初始化完成")
# 🔥 初始化数据库视图和索引
await init_database_views_and_indexes()
except Exception as e:
logger.error(f"💥 数据库初始化失败: {e}")
raise
async def init_database_views_and_indexes():
"""初始化数据库视图和索引"""
try:
db = get_mongo_db()
# 1. 创建股票筛选视图
await create_stock_screening_view(db)
# 2. 创建必要的索引
await create_database_indexes(db)
logger.info("✅ 数据库视图和索引初始化完成")
except Exception as e:
logger.warning(f"⚠️ 数据库视图和索引初始化失败: {e}")
# 不抛出异常,允许应用继续启动
async def create_stock_screening_view(db):
"""创建股票筛选视图"""
try:
# 检查视图是否已存在
collections = await db.list_collection_names()
if "stock_screening_view" in collections:
logger.info("📋 视图 stock_screening_view 已存在,跳过创建")
return
# 创建视图:将 stock_basic_info、market_quotes 和 stock_financial_data 关联
pipeline = [
# 第一步:关联实时行情数据 (market_quotes)
{
"$lookup": {
"from": "market_quotes",
"localField": "code",
"foreignField": "code",
"as": "quote_data"
}
},
# 第二步:展开 quote_data 数组
{
"$unwind": {
"path": "$quote_data",
"preserveNullAndEmptyArrays": True
}
},
# 第三步:关联财务数据 (stock_financial_data)
{
"$lookup": {
"from": "stock_financial_data",
"let": {"stock_code": "$code", "stock_source": "$source"},
"pipeline": [
{
"$match": {
"$expr": {
"$and": [
{"$eq": ["$code", "$$stock_code"]},
{"$eq": ["$data_source", "$$stock_source"]}
]
}
}
},
{"$sort": {"report_period": -1}},
{"$limit": 1}
],
"as": "financial_data"
}
},
# 第四步:展开 financial_data 数组
{
"$unwind": {
"path": "$financial_data",
"preserveNullAndEmptyArrays": True
}
},
# 第五步:重新组织字段结构
{
"$project": {
# 基础信息字段
"code": 1,
"name": 1,
"industry": 1,
"area": 1,
"market": 1,
"list_date": 1,
"source": 1,
# 市值信息
"total_mv": 1,
"circ_mv": 1,
# 估值指标
"pe": 1,
"pb": 1,
"pe_ttm": 1,
"pb_mrq": 1,
# 财务指标
"roe": "$financial_data.roe",
"roa": "$financial_data.roa",
"netprofit_margin": "$financial_data.netprofit_margin",
"gross_margin": "$financial_data.gross_margin",
"report_period": "$financial_data.report_period",
# 交易指标
"turnover_rate": 1,
"volume_ratio": 1,
# 实时行情数据
"close": "$quote_data.close",
"open": "$quote_data.open",
"high": "$quote_data.high",
"low": "$quote_data.low",
"pre_close": "$quote_data.pre_close",
"pct_chg": "$quote_data.pct_chg",
"amount": "$quote_data.amount",
"volume": "$quote_data.volume",
"trade_date": "$quote_data.trade_date",
# 时间戳
"updated_at": 1,
"quote_updated_at": "$quote_data.updated_at",
"financial_updated_at": "$financial_data.updated_at"
}
}
]
# 创建视图
await db.command({
"create": "stock_screening_view",
"viewOn": "stock_basic_info",
"pipeline": pipeline
})
logger.info("✅ 视图 stock_screening_view 创建成功")
except Exception as e:
logger.warning(f"⚠️ 创建视图失败: {e}")
async def create_database_indexes(db):
"""创建数据库索引"""
try:
# stock_basic_info 的索引
basic_info = db["stock_basic_info"]
await basic_info.create_index([("code", 1), ("source", 1)], unique=True)
await basic_info.create_index([("industry", 1)])
await basic_info.create_index([("total_mv", -1)])
await basic_info.create_index([("pe", 1)])
await basic_info.create_index([("pb", 1)])
# market_quotes 的索引
market_quotes = db["market_quotes"]
await market_quotes.create_index([("code", 1)], unique=True)
await market_quotes.create_index([("pct_chg", -1)])
await market_quotes.create_index([("amount", -1)])
await market_quotes.create_index([("updated_at", -1)])
logger.info("✅ 数据库索引创建完成")
except Exception as e:
logger.warning(f"⚠️ 创建索引失败: {e}")
async def close_database():
"""关闭数据库连接"""
global mongo_client, mongo_db, redis_client, redis_pool
await db_manager.close_connections()
# 清空全局变量
mongo_client = None
mongo_db = None
redis_client = None
redis_pool = None
def get_mongo_client() -> AsyncIOMotorClient:
"""获取MongoDB客户端"""
if mongo_client is None:
raise RuntimeError("MongoDB客户端未初始化")
return mongo_client
def get_mongo_db() -> AsyncIOMotorDatabase:
"""获取MongoDB数据库实例"""
if mongo_db is None:
raise RuntimeError("MongoDB数据库未初始化")
return mongo_db
def get_mongo_db_sync() -> Database:
"""
获取同步版本的MongoDB数据库实例
用于非异步上下文(如普通函数调用)
"""
global _sync_mongo_client, _sync_mongo_db
if _sync_mongo_db is not None:
return _sync_mongo_db
# 创建同步 MongoDB 客户端
if _sync_mongo_client is None:
_sync_mongo_client = MongoClient(
settings.MONGO_URI,
maxPoolSize=settings.MONGO_MAX_CONNECTIONS,
minPoolSize=settings.MONGO_MIN_CONNECTIONS,
maxIdleTimeMS=30000,
serverSelectionTimeoutMS=5000
)
_sync_mongo_db = _sync_mongo_client[settings.MONGO_DB]
return _sync_mongo_db
def get_redis_client() -> Redis:
"""获取Redis客户端"""
if redis_client is None:
raise RuntimeError("Redis客户端未初始化")
return redis_client
async def get_database_health() -> dict:
"""获取数据库健康状态"""
return await db_manager.health_check()
# 兼容性别名
init_db = init_database
close_db = close_database
def get_database():
"""获取数据库实例"""
if db_manager.mongo_client is None:
raise RuntimeError("MongoDB客户端未初始化")
return db_manager.mongo_client.tradingagents
================================================
FILE: app/core/dev_config.py
================================================
"""
开发环境配置
优化开发体验,减少不必要的文件监控
"""
import logging
from typing import List, Optional
class DevConfig:
"""开发环境配置类"""
# 文件监控配置
RELOAD_DIRS: List[str] = ["app"]
# 排除的文件和目录
RELOAD_EXCLUDES: List[str] = [
# Python缓存文件
"__pycache__",
"*.pyc",
"*.pyo",
"*.pyd",
# 版本控制
".git",
".gitignore",
# 测试和缓存
".pytest_cache",
".coverage",
"htmlcov",
# 日志文件
"*.log",
"logs",
# 临时文件
"*.tmp",
"*.temp",
"*.swp",
"*.swo",
# 系统文件
".DS_Store",
"Thumbs.db",
"desktop.ini",
# IDE文件
".vscode",
".idea",
"*.sublime-*",
# 数据文件
"*.db",
"*.sqlite",
"*.sqlite3",
# 配置文件(避免敏感信息重载)
".env",
".env.local",
".env.production",
# 文档和静态文件
"*.md",
"*.txt",
"*.json",
"*.yaml",
"*.yml",
"*.toml",
# 前端文件
"node_modules",
"dist",
"build",
"*.js",
"*.css",
"*.html",
# 其他
"requirements*.txt",
"Dockerfile*",
"docker-compose*"
]
# 只监控的文件类型
RELOAD_INCLUDES: List[str] = [
"*.py"
]
# 重载延迟(秒)
RELOAD_DELAY: float = 0.5
# 日志配置
LOG_LEVEL: str = "info"
# 是否显示访问日志
ACCESS_LOG: bool = True
@classmethod
def get_uvicorn_config(cls, debug: bool = True) -> dict:
"""获取uvicorn配置"""
# 统一禁用reload,避免日志配置冲突
return {
"reload": False, # 禁用自动重载,手动重启
"log_level": cls.LOG_LEVEL,
"access_log": cls.ACCESS_LOG,
# 确保使用我们自定义的日志配置
"log_config": None # 禁用uvicorn默认日志配置,使用我们的配置
}
@classmethod
def setup_logging(cls, debug: bool = True):
"""设置简化的日志配置"""
# 设置统一的日志格式
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
force=True # 强制重新配置,覆盖之前的设置
)
if debug:
# 开发环境:减少噪音日志
logging.getLogger("watchfiles").setLevel(logging.ERROR)
logging.getLogger("watchfiles.main").setLevel(logging.ERROR)
logging.getLogger("watchfiles.watcher").setLevel(logging.ERROR)
# 确保重要日志正常显示
logging.getLogger("webapi").setLevel(logging.INFO)
logging.getLogger("app.core.database").setLevel(logging.INFO)
logging.getLogger("uvicorn.error").setLevel(logging.INFO)
# 测试webapi logger是否工作
webapi_logger = logging.getLogger("webapi")
webapi_logger.info("🔧 DEV_CONFIG: webapi logger 测试消息")
else:
# 生产环境:更严格的日志控制
logging.getLogger("watchfiles").setLevel(logging.ERROR)
logging.getLogger("uvicorn").setLevel(logging.WARNING)
# 开发环境快捷配置
DEV_CONFIG = DevConfig()
================================================
FILE: app/core/logging_config.py
================================================
import logging
import logging.config
import sys
from pathlib import Path
import os
import platform
from app.core.logging_context import LoggingContextFilter, trace_id_var
# 🔥 在 Windows 上使用 concurrent-log-handler 避免文件占用问题
_IS_WINDOWS = platform.system() == "Windows"
if _IS_WINDOWS:
try:
from concurrent_log_handler import ConcurrentRotatingFileHandler
_USE_CONCURRENT_HANDLER = True
except ImportError:
_USE_CONCURRENT_HANDLER = False
logging.warning("concurrent-log-handler 未安装,在 Windows 上可能遇到日志轮转问题")
else:
_USE_CONCURRENT_HANDLER = False
try:
import tomllib as toml_loader # Python 3.11+
except Exception:
try:
import tomli as toml_loader # Python 3.10 fallback
except Exception:
toml_loader = None
def resolve_logging_cfg_path() -> Path:
"""根据环境选择日志配置文件路径(可能不存在)
优先 docker 配置,其次默认配置。
"""
profile = os.environ.get("LOGGING_PROFILE", "").lower()
is_docker_env = os.environ.get("DOCKER", "").lower() in {"1", "true", "yes"} or Path("/.dockerenv").exists()
cfg_candidate = "config/logging_docker.toml" if profile == "docker" or is_docker_env else "config/logging.toml"
return Path(cfg_candidate)
class SimpleJsonFormatter(logging.Formatter):
"""Minimal JSON formatter without external deps."""
def format(self, record: logging.LogRecord) -> str:
import json
obj = {
"time": self.formatTime(record, "%Y-%m-%d %H:%M:%S"),
"name": record.name,
"level": record.levelname,
"trace_id": getattr(record, "trace_id", "-"),
"message": record.getMessage(),
}
return json.dumps(obj, ensure_ascii=False)
def _parse_size(size_str: str) -> int:
"""解析大小字符串(如 '10MB')为字节数"""
if isinstance(size_str, int):
return size_str
if isinstance(size_str, str) and size_str.upper().endswith("MB"):
try:
return int(float(size_str[:-2]) * 1024 * 1024)
except Exception:
return 10 * 1024 * 1024
return 10 * 1024 * 1024
def setup_logging(log_level: str = "INFO"):
"""
设置应用日志配置:
1) 优先尝试从 config/logging.toml 读取并转化为 dictConfig
2) 失败或不存在时,回退到内置默认配置
"""
# 1) 若存在 TOML 配置且可解析,则优先使用
try:
cfg_path = resolve_logging_cfg_path()
print(f"🔍 [setup_logging] 日志配置文件路径: {cfg_path}")
print(f"🔍 [setup_logging] 配置文件存在: {cfg_path.exists()}")
print(f"🔍 [setup_logging] TOML加载器可用: {toml_loader is not None}")
if cfg_path.exists() and toml_loader is not None:
with cfg_path.open("rb") as f:
toml_data = toml_loader.load(f)
print(f"🔍 [setup_logging] 成功加载TOML配置")
# 读取基础字段
logging_root = toml_data.get("logging", {})
level = logging_root.get("level", log_level)
fmt_cfg = logging_root.get("format", {})
fmt_console = fmt_cfg.get(
"console", "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
fmt_file = fmt_cfg.get(
"file", "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
# 确保文本格式包含 trace_id(若未显式包含)
if "%(trace_id)" not in str(fmt_console):
fmt_console = str(fmt_console) + " trace=%(trace_id)s"
if "%(trace_id)" not in str(fmt_file):
fmt_file = str(fmt_file) + " trace=%(trace_id)s"
handlers_cfg = logging_root.get("handlers", {})
file_handler_cfg = handlers_cfg.get("file", {})
file_dir = file_handler_cfg.get("directory", "./logs")
file_level = file_handler_cfg.get("level", "DEBUG")
max_bytes = file_handler_cfg.get("max_size", "10MB")
# 支持 "10MB" 形式
if isinstance(max_bytes, str) and max_bytes.upper().endswith("MB"):
try:
max_bytes = int(float(max_bytes[:-2]) * 1024 * 1024)
except Exception:
max_bytes = 10 * 1024 * 1024
elif not isinstance(max_bytes, int):
max_bytes = 10 * 1024 * 1024
backup_count = int(file_handler_cfg.get("backup_count", 5))
Path(file_dir).mkdir(parents=True, exist_ok=True)
# 从TOML配置读取各个日志文件路径
main_handler_cfg = handlers_cfg.get("main", {})
webapi_handler_cfg = handlers_cfg.get("webapi", {})
worker_handler_cfg = handlers_cfg.get("worker", {})
print(f"🔍 [setup_logging] handlers配置: {list(handlers_cfg.keys())}")
print(f"🔍 [setup_logging] main_handler_cfg: {main_handler_cfg}")
print(f"🔍 [setup_logging] webapi_handler_cfg: {webapi_handler_cfg}")
print(f"🔍 [setup_logging] worker_handler_cfg: {worker_handler_cfg}")
# 主日志文件(tradingagents.log)
main_log = main_handler_cfg.get("filename", str(Path(file_dir) / "tradingagents.log"))
main_enabled = main_handler_cfg.get("enabled", True)
main_level = main_handler_cfg.get("level", "INFO")
main_max_bytes = _parse_size(main_handler_cfg.get("max_size", "100MB"))
main_backup_count = int(main_handler_cfg.get("backup_count", 5))
print(f"🔍 [setup_logging] 主日志文件配置:")
print(f" - 文件路径: {main_log}")
print(f" - 是否启用: {main_enabled}")
print(f" - 日志级别: {main_level}")
print(f" - 最大大小: {main_max_bytes} bytes")
print(f" - 备份数量: {main_backup_count}")
# WebAPI日志文件
webapi_log = webapi_handler_cfg.get("filename", str(Path(file_dir) / "webapi.log"))
webapi_enabled = webapi_handler_cfg.get("enabled", True)
webapi_level = webapi_handler_cfg.get("level", "DEBUG")
webapi_max_bytes = _parse_size(webapi_handler_cfg.get("max_size", "100MB"))
webapi_backup_count = int(webapi_handler_cfg.get("backup_count", 5))
print(f"🔍 [setup_logging] WebAPI日志文件: {webapi_log}, 启用: {webapi_enabled}")
# Worker日志文件
worker_log = worker_handler_cfg.get("filename", str(Path(file_dir) / "worker.log"))
worker_enabled = worker_handler_cfg.get("enabled", True)
worker_level = worker_handler_cfg.get("level", "DEBUG")
worker_max_bytes = _parse_size(worker_handler_cfg.get("max_size", "100MB"))
worker_backup_count = int(worker_handler_cfg.get("backup_count", 5))
print(f"🔍 [setup_logging] Worker日志文件: {worker_log}, 启用: {worker_enabled}")
# 错误日志文件
error_handler_cfg = handlers_cfg.get("error", {})
error_log = error_handler_cfg.get("filename", str(Path(file_dir) / "error.log"))
error_enabled = error_handler_cfg.get("enabled", True)
error_level = error_handler_cfg.get("level", "WARNING")
error_max_bytes = _parse_size(error_handler_cfg.get("max_size", "100MB"))
error_backup_count = int(error_handler_cfg.get("backup_count", 5))
# JSON 开关:保持向后兼容(json/mode 仅控制台);新增 file_json/file_mode 控制文件 handler
use_json_console = bool(fmt_cfg.get("json", False)) or str(fmt_cfg.get("mode", "")).lower() == "json"
use_json_file = (
bool(fmt_cfg.get("file_json", False))
or bool(fmt_cfg.get("json_file", False))
or str(fmt_cfg.get("file_mode", "")).lower() == "json"
)
# 构建处理器配置
handlers_config = {
"console": {
"class": "logging.StreamHandler",
"formatter": "json_console_fmt" if use_json_console else "console_fmt",
"level": level,
"filters": ["request_context"],
"stream": sys.stdout,
},
}
print(f"🔍 [setup_logging] 开始构建handlers配置")
# 🔥 选择日志处理器类(Windows 使用 ConcurrentRotatingFileHandler)
handler_class = "concurrent_log_handler.ConcurrentRotatingFileHandler" if _USE_CONCURRENT_HANDLER else "logging.handlers.RotatingFileHandler"
# 主日志文件(tradingagents.log)
if main_enabled:
print(f"✅ [setup_logging] 添加 main_file handler: {main_log} (使用 {handler_class})")
handlers_config["main_file"] = {
"class": handler_class,
"formatter": "json_file_fmt" if use_json_file else "file_fmt",
"level": main_level,
"filename": main_log,
"maxBytes": main_max_bytes,
"backupCount": main_backup_count,
"encoding": "utf-8",
"filters": ["request_context"],
}
else:
print(f"⚠️ [setup_logging] main_file handler 未启用")
# WebAPI日志文件
if webapi_enabled:
handlers_config["file"] = {
"class": handler_class,
"formatter": "json_file_fmt" if use_json_file else "file_fmt",
"level": webapi_level,
"filename": webapi_log,
"maxBytes": webapi_max_bytes,
"backupCount": webapi_backup_count,
"encoding": "utf-8",
"filters": ["request_context"],
}
# Worker日志文件
if worker_enabled:
handlers_config["worker_file"] = {
"class": handler_class,
"formatter": "json_file_fmt" if use_json_file else "file_fmt",
"level": worker_level,
"filename": worker_log,
"maxBytes": worker_max_bytes,
"backupCount": worker_backup_count,
"encoding": "utf-8",
"filters": ["request_context"],
}
# 添加错误日志处理器(如果启用)
if error_enabled:
handlers_config["error_file"] = {
"class": "logging.handlers.RotatingFileHandler",
"formatter": "json_file_fmt" if use_json_file else "file_fmt",
"level": error_level,
"filename": error_log,
"maxBytes": error_max_bytes,
"backupCount": error_backup_count,
"encoding": "utf-8",
"filters": ["request_context"],
}
# 构建logger handlers列表
main_handlers = ["console"]
if main_enabled:
main_handlers.append("main_file")
if error_enabled:
main_handlers.append("error_file")
print(f"🔍 [setup_logging] main_handlers: {main_handlers}")
webapi_handlers = ["console"]
if webapi_enabled:
webapi_handlers.append("file")
if main_enabled:
webapi_handlers.append("main_file")
if error_enabled:
webapi_handlers.append("error_file")
print(f"🔍 [setup_logging] webapi_handlers: {webapi_handlers}")
worker_handlers = ["console"]
if worker_enabled:
worker_handlers.append("worker_file")
if main_enabled:
worker_handlers.append("main_file")
if error_enabled:
worker_handlers.append("error_file")
print(f"🔍 [setup_logging] worker_handlers: {worker_handlers}")
logging_config = {
"version": 1,
"disable_existing_loggers": False,
"filters": {
"request_context": {"()": "app.core.logging_context.LoggingContextFilter"}
},
"formatters": {
"console_fmt": {
"format": fmt_console,
"datefmt": "%Y-%m-%d %H:%M:%S",
},
"file_fmt": {
"format": fmt_file,
"datefmt": "%Y-%m-%d %H:%M:%S",
},
"json_console_fmt": {
"()": "app.core.logging_config.SimpleJsonFormatter"
},
"json_file_fmt": {
"()": "app.core.logging_config.SimpleJsonFormatter"
},
},
"handlers": handlers_config,
"loggers": {
"tradingagents": {
"level": "INFO",
"handlers": main_handlers,
"propagate": False
},
"webapi": {
"level": "INFO",
"handlers": webapi_handlers,
"propagate": False
},
"worker": {
"level": "DEBUG",
"handlers": worker_handlers,
"propagate": False
},
"uvicorn": {
"level": "INFO",
"handlers": webapi_handlers,
"propagate": False
},
"fastapi": {
"level": "INFO",
"handlers": webapi_handlers,
"propagate": False
},
"app": {
"level": "INFO",
"handlers": main_handlers,
"propagate": False
},
},
"root": {"level": level, "handlers": main_handlers},
}
print(f"🔍 [setup_logging] 最终handlers配置: {list(handlers_config.keys())}")
print(f"🔍 [setup_logging] 开始应用 dictConfig")
logging.config.dictConfig(logging_config)
print(f"✅ [setup_logging] dictConfig 应用成功")
logging.getLogger("webapi").info(f"Logging configured from {cfg_path}")
# 测试主日志文件是否可写
if main_enabled:
test_logger = logging.getLogger("tradingagents")
test_logger.info(f"🔍 测试主日志文件写入: {main_log}")
print(f"🔍 [setup_logging] 已向 tradingagents logger 写入测试日志")
return
except Exception as e:
# TOML 存在但加载失败,回退到默认配置
logging.getLogger("webapi").warning(f"Failed to load logging.toml, fallback to defaults: {e}")
# 2) 默认内置配置(与原先一致)
log_dir = Path("logs")
log_dir.mkdir(exist_ok=True)
# 🔥 选择日志处理器类(Windows 使用 ConcurrentRotatingFileHandler)
handler_class = "concurrent_log_handler.ConcurrentRotatingFileHandler" if _USE_CONCURRENT_HANDLER else "logging.handlers.RotatingFileHandler"
logging_config = {
"version": 1,
"disable_existing_loggers": False,
"filters": {"request_context": {"()": "app.core.logging_context.LoggingContextFilter"}},
"formatters": {
"default": {
"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s trace=%(trace_id)s",
"datefmt": "%Y-%m-%d %H:%M:%S",
},
"detailed": {
"format": "%(asctime)s - %(name)s - %(levelname)s - %(pathname)s:%(lineno)d - %(message)s trace=%(trace_id)s",
"datefmt": "%Y-%m-%d %H:%M:%S",
},
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"formatter": "default",
"level": log_level,
"filters": ["request_context"],
"stream": sys.stdout,
},
"file": {
"class": handler_class,
"formatter": "detailed",
"level": "DEBUG",
"filters": ["request_context"],
"filename": "logs/webapi.log",
"maxBytes": 10485760,
"backupCount": 5,
"encoding": "utf-8",
},
"worker_file": {
"class": handler_class,
"formatter": "detailed",
"level": "DEBUG",
"filters": ["request_context"],
"filename": "logs/worker.log",
"maxBytes": 10485760,
"backupCount": 5,
"encoding": "utf-8",
},
"error_file": {
"class": handler_class,
"formatter": "detailed",
"level": "WARNING",
"filters": ["request_context"],
"filename": "logs/error.log",
"maxBytes": 10485760,
"backupCount": 5,
"encoding": "utf-8",
},
},
"loggers": {
"webapi": {"level": "INFO", "handlers": ["console", "file", "error_file"], "propagate": True},
"worker": {"level": "DEBUG", "handlers": ["console", "worker_file", "error_file"], "propagate": False},
"uvicorn": {"level": "INFO", "handlers": ["console", "file", "error_file"], "propagate": False},
"fastapi": {"level": "INFO", "handlers": ["console", "file", "error_file"], "propagate": False},
},
"root": {"level": log_level, "handlers": ["console"]},
}
logging.config.dictConfig(logging_config)
logging.getLogger("webapi").info("Logging configured successfully (built-in)")
================================================
FILE: app/core/logging_context.py
================================================
import logging
import contextvars
# Shared contextvar for trace id across the whole process
trace_id_var: contextvars.ContextVar[str] = contextvars.ContextVar("trace_id", default="-")
class LoggingContextFilter(logging.Filter):
"""Injects trace_id from contextvars into LogRecord.
Always sets record.trace_id to a string (default '-') so formatters are safe.
"""
def filter(self, record: logging.LogRecord) -> bool:
try:
record.trace_id = trace_id_var.get()
except Exception:
record.trace_id = "-"
return True
================================================
FILE: app/core/rate_limiter.py
================================================
"""
速率限制器
用于控制API调用频率,避免超过数据源的限流限制
"""
import asyncio
import time
import logging
from collections import deque
from typing import Optional
logger = logging.getLogger(__name__)
class RateLimiter:
"""
滑动窗口速率限制器
使用滑动窗口算法精确控制API调用频率
"""
def __init__(self, max_calls: int, time_window: float, name: str = "RateLimiter"):
"""
初始化速率限制器
Args:
max_calls: 时间窗口内最大调用次数
time_window: 时间窗口大小(秒)
name: 限制器名称(用于日志)
"""
self.max_calls = max_calls
self.time_window = time_window
self.name = name
self.calls = deque() # 存储调用时间戳
self.lock = asyncio.Lock() # 确保线程安全
# 统计信息
self.total_calls = 0
self.total_waits = 0
self.total_wait_time = 0.0
logger.info(f"🔧 {self.name} 初始化: {max_calls}次/{time_window}秒")
async def acquire(self):
"""
获取调用许可
如果超过速率限制,会等待直到可以调用
"""
async with self.lock:
now = time.time()
# 移除时间窗口外的旧调用记录
while self.calls and self.calls[0] <= now - self.time_window:
self.calls.popleft()
# 如果当前窗口内调用次数已达上限,需要等待
if len(self.calls) >= self.max_calls:
# 计算需要等待的时间
oldest_call = self.calls[0]
wait_time = oldest_call + self.time_window - now + 0.01 # 加一点缓冲
if wait_time > 0:
self.total_waits += 1
self.total_wait_time += wait_time
logger.debug(f"⏳ {self.name} 达到速率限制,等待 {wait_time:.2f}秒")
await asyncio.sleep(wait_time)
# 重新获取当前时间
now = time.time()
# 再次清理旧记录
while self.calls and self.calls[0] <= now - self.time_window:
self.calls.popleft()
# 记录本次调用
self.calls.append(now)
self.total_calls += 1
def get_stats(self) -> dict:
"""获取统计信息"""
return {
"name": self.name,
"max_calls": self.max_calls,
"time_window": self.time_window,
"current_calls": len(self.calls),
"total_calls": self.total_calls,
"total_waits": self.total_waits,
"total_wait_time": self.total_wait_time,
"avg_wait_time": self.total_wait_time / self.total_waits if self.total_waits > 0 else 0
}
def reset_stats(self):
"""重置统计信息"""
self.total_calls = 0
self.total_waits = 0
self.total_wait_time = 0.0
logger.info(f"🔄 {self.name} 统计信息已重置")
class TushareRateLimiter(RateLimiter):
"""
Tushare专用速率限制器
根据Tushare的积分等级自动调整限流策略
"""
# Tushare积分等级对应的限流配置
TIER_LIMITS = {
"free": {"max_calls": 100, "time_window": 60}, # 免费用户: 100次/分钟
"basic": {"max_calls": 200, "time_window": 60}, # 基础用户: 200次/分钟
"standard": {"max_calls": 400, "time_window": 60}, # 标准用户: 400次/分钟
"premium": {"max_calls": 600, "time_window": 60}, # 高级用户: 600次/分钟
"vip": {"max_calls": 800, "time_window": 60}, # VIP用户: 800次/分钟
}
def __init__(self, tier: str = "standard", safety_margin: float = 0.8):
"""
初始化Tushare速率限制器
Args:
tier: 积分等级 (free/basic/standard/premium/vip)
safety_margin: 安全边际(0-1),实际限制为理论限制的百分比
"""
if tier not in self.TIER_LIMITS:
logger.warning(f"⚠️ 未知的Tushare积分等级: {tier},使用默认值 'standard'")
tier = "standard"
limits = self.TIER_LIMITS[tier]
# 应用安全边际
max_calls = int(limits["max_calls"] * safety_margin)
time_window = limits["time_window"]
super().__init__(
max_calls=max_calls,
time_window=time_window,
name=f"TushareRateLimiter({tier})"
)
self.tier = tier
self.safety_margin = safety_margin
logger.info(f"✅ Tushare速率限制器已配置: {tier}等级, "
f"{max_calls}次/{time_window}秒 (安全边际: {safety_margin*100:.0f}%)")
class AKShareRateLimiter(RateLimiter):
"""
AKShare专用速率限制器
AKShare没有明确的限流规则,使用保守的限流策略
"""
def __init__(self, max_calls: int = 60, time_window: float = 60):
"""
初始化AKShare速率限制器
Args:
max_calls: 时间窗口内最大调用次数(默认60次/分钟)
time_window: 时间窗口大小(秒)
"""
super().__init__(
max_calls=max_calls,
time_window=time_window,
name="AKShareRateLimiter"
)
class BaoStockRateLimiter(RateLimiter):
"""
BaoStock专用速率限制器
BaoStock没有明确的限流规则,使用保守的限流策略
"""
def __init__(self, max_calls: int = 100, time_window: float = 60):
"""
初始化BaoStock速率限制器
Args:
max_calls: 时间窗口内最大调用次数(默认100次/分钟)
time_window: 时间窗口大小(秒)
"""
super().__init__(
max_calls=max_calls,
time_window=time_window,
name="BaoStockRateLimiter"
)
# 全局速率限制器实例
_tushare_limiter: Optional[TushareRateLimiter] = None
_akshare_limiter: Optional[AKShareRateLimiter] = None
_baostock_limiter: Optional[BaoStockRateLimiter] = None
def get_tushare_rate_limiter(tier: str = "standard", safety_margin: float = 0.8) -> TushareRateLimiter:
"""获取Tushare速率限制器(单例)"""
global _tushare_limiter
if _tushare_limiter is None:
_tushare_limiter = TushareRateLimiter(tier=tier, safety_margin=safety_margin)
return _tushare_limiter
def get_akshare_rate_limiter() -> AKShareRateLimiter:
"""获取AKShare速率限制器(单例)"""
global _akshare_limiter
if _akshare_limiter is None:
_akshare_limiter = AKShareRateLimiter()
return _akshare_limiter
def get_baostock_rate_limiter() -> BaoStockRateLimiter:
"""获取BaoStock速率限制器(单例)"""
global _baostock_limiter
if _baostock_limiter is None:
_baostock_limiter = BaoStockRateLimiter()
return _baostock_limiter
def reset_all_limiters():
"""重置所有速率限制器"""
global _tushare_limiter, _akshare_limiter, _baostock_limiter
_tushare_limiter = None
_akshare_limiter = None
_baostock_limiter = None
logger.info("🔄 所有速率限制器已重置")
================================================
FILE: app/core/redis_client.py
================================================
"""
Redis客户端配置和连接管理
"""
import redis.asyncio as redis
import logging
from typing import Optional
from .config import settings
logger = logging.getLogger(__name__)
# 全局Redis连接池
redis_pool: Optional[redis.ConnectionPool] = None
redis_client: Optional[redis.Redis] = None
async def init_redis():
"""初始化Redis连接"""
global redis_pool, redis_client
try:
# 创建连接池
redis_pool = redis.ConnectionPool.from_url(
settings.REDIS_URL,
max_connections=settings.REDIS_MAX_CONNECTIONS, # 使用配置文件中的值
retry_on_timeout=settings.REDIS_RETRY_ON_TIMEOUT,
decode_responses=True,
socket_keepalive=True, # 启用 TCP keepalive
socket_keepalive_options={
1: 60, # TCP_KEEPIDLE: 60秒后开始发送keepalive探测
2: 10, # TCP_KEEPINTVL: 每10秒发送一次探测
3: 3, # TCP_KEEPCNT: 最多发送3次探测
},
health_check_interval=30, # 每30秒检查一次连接健康状态
)
# 创建Redis客户端
redis_client = redis.Redis(connection_pool=redis_pool)
# 测试连接
await redis_client.ping()
logger.info(f"✅ Redis连接成功建立 (max_connections={settings.REDIS_MAX_CONNECTIONS})")
except Exception as e:
logger.error(f"❌ Redis连接失败: {e}")
raise
async def close_redis():
"""关闭Redis连接"""
global redis_pool, redis_client
try:
if redis_client:
await redis_client.close()
if redis_pool:
await redis_pool.disconnect()
logger.info("✅ Redis连接已关闭")
except Exception as e:
logger.error(f"❌ 关闭Redis连接时出错: {e}")
def get_redis() -> redis.Redis:
"""获取Redis客户端实例"""
if redis_client is None:
raise RuntimeError("Redis客户端未初始化")
return redis_client
class RedisKeys:
"""Redis键名常量"""
# 队列相关
USER_PENDING_QUEUE = "user:{user_id}:pending"
USER_PROCESSING_SET = "user:{user_id}:processing"
GLOBAL_PENDING_QUEUE = "global:pending"
GLOBAL_PROCESSING_SET = "global:processing"
# 任务相关
TASK_PROGRESS = "task:{task_id}:progress"
TASK_RESULT = "task:{task_id}:result"
TASK_LOCK = "task:{task_id}:lock"
# 批次相关
BATCH_PROGRESS = "batch:{batch_id}:progress"
BATCH_TASKS = "batch:{batch_id}:tasks"
BATCH_LOCK = "batch:{batch_id}:lock"
# 用户相关
USER_SESSION = "session:{session_id}"
USER_RATE_LIMIT = "rate_limit:{user_id}:{endpoint}"
USER_DAILY_QUOTA = "quota:{user_id}:{date}"
# 系统相关
QUEUE_STATS = "queue:stats"
SYSTEM_CONFIG = "system:config"
WORKER_HEARTBEAT = "worker:{worker_id}:heartbeat"
# 缓存相关
SCREENING_CACHE = "screening:{cache_key}"
ANALYSIS_CACHE = "analysis:{cache_key}"
class RedisService:
"""Redis服务封装类"""
def __init__(self):
self.redis = get_redis()
async def set_with_ttl(self, key: str, value: str, ttl: int = 3600):
"""设置带TTL的键值"""
await self.redis.setex(key, ttl, value)
async def get_json(self, key: str):
"""获取JSON格式的值"""
import json
value = await self.redis.get(key)
if value:
return json.loads(value)
return None
async def set_json(self, key: str, value: dict, ttl: int = None):
"""设置JSON格式的值"""
import json
json_str = json.dumps(value, ensure_ascii=False)
if ttl:
await self.redis.setex(key, ttl, json_str)
else:
await self.redis.set(key, json_str)
async def increment_with_ttl(self, key: str, ttl: int = 3600):
"""递增计数器并设置TTL"""
pipe = self.redis.pipeline()
pipe.incr(key)
pipe.expire(key, ttl)
results = await pipe.execute()
return results[0]
async def add_to_queue(self, queue_key: str, item: dict):
"""添加项目到队列"""
import json
await self.redis.lpush(queue_key, json.dumps(item, ensure_ascii=False))
async def pop_from_queue(self, queue_key: str, timeout: int = 1):
"""从队列弹出项目"""
import json
result = await self.redis.brpop(queue_key, timeout=timeout)
if result:
return json.loads(result[1])
return None
async def get_queue_length(self, queue_key: str):
"""获取队列长度"""
return await self.redis.llen(queue_key)
async def add_to_set(self, set_key: str, value: str):
"""添加到集合"""
await self.redis.sadd(set_key, value)
async def remove_from_set(self, set_key: str, value: str):
"""从集合移除"""
await self.redis.srem(set_key, value)
async def is_in_set(self, set_key: str, value: str):
"""检查是否在集合中"""
return await self.redis.sismember(set_key, value)
async def get_set_size(self, set_key: str):
"""获取集合大小"""
return await self.redis.scard(set_key)
async def acquire_lock(self, lock_key: str, timeout: int = 30):
"""获取分布式锁"""
import uuid
lock_value = str(uuid.uuid4())
acquired = await self.redis.set(lock_key, lock_value, nx=True, ex=timeout)
if acquired:
return lock_value
return None
async def release_lock(self, lock_key: str, lock_value: str):
"""释放分布式锁"""
lua_script = """
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
"""
return await self.redis.eval(lua_script, 1, lock_key, lock_value)
# 全局Redis服务实例
redis_service: Optional[RedisService] = None
def get_redis_service() -> RedisService:
"""获取Redis服务实例"""
global redis_service
if redis_service is None:
redis_service = RedisService()
return redis_service
================================================
FILE: app/core/response.py
================================================
"""
统一API响应格式工具
"""
from datetime import datetime
from typing import Any, Optional, Dict
from app.utils.timezone import now_tz
def ok(data: Any = None, message: str = "ok") -> Dict[str, Any]:
"""标准成功响应
返回结构:{"success": True, "data": data, "message": message, "timestamp": ...}
"""
return {
"success": True,
"data": data,
"message": message,
"timestamp": now_tz().isoformat()
}
def fail(message: str = "error", code: int = 500, data: Any = None) -> Dict[str, Any]:
"""标准失败响应(一般错误仍建议用 HTTPException 抛出,此函数用于业务失败场景)"""
return {
"success": False,
"data": data,
"message": message,
"code": code,
"timestamp": now_tz().isoformat()
}
================================================
FILE: app/core/startup_validator.py
================================================
"""
启动配置验证器
验证系统启动所需的必需配置项,提供友好的错误提示。
"""
import os
import logging
from typing import List, Dict, Any, Optional
from dataclasses import dataclass
from enum import Enum
logger = logging.getLogger(__name__)
class ConfigLevel(Enum):
"""配置级别"""
REQUIRED = "required" # 必需配置,缺少则无法启动
RECOMMENDED = "recommended" # 推荐配置,缺少会影响功能
OPTIONAL = "optional" # 可选配置,缺少不影响基本功能
@dataclass
class ConfigItem:
"""配置项"""
key: str # 配置键名
level: ConfigLevel # 配置级别
description: str # 配置描述
example: Optional[str] = None # 配置示例
help_url: Optional[str] = None # 帮助链接
validator: Optional[callable] = None # 自定义验证函数
@dataclass
class ValidationResult:
"""验证结果"""
success: bool # 是否验证成功
missing_required: List[ConfigItem] # 缺少的必需配置
missing_recommended: List[ConfigItem] # 缺少的推荐配置
invalid_configs: List[tuple[ConfigItem, str]] # 无效的配置(配置项,错误信息)
warnings: List[str] # 警告信息
class StartupValidator:
"""启动配置验证器"""
# 必需配置项
REQUIRED_CONFIGS = [
ConfigItem(
key="MONGODB_HOST",
level=ConfigLevel.REQUIRED,
description="MongoDB主机地址",
example="localhost"
),
ConfigItem(
key="MONGODB_PORT",
level=ConfigLevel.REQUIRED,
description="MongoDB端口",
example="27017",
validator=lambda v: v.isdigit() and 1 <= int(v) <= 65535
),
ConfigItem(
key="MONGODB_DATABASE",
level=ConfigLevel.REQUIRED,
description="MongoDB数据库名称",
example="tradingagents"
),
ConfigItem(
key="REDIS_HOST",
level=ConfigLevel.REQUIRED,
description="Redis主机地址",
example="localhost"
),
ConfigItem(
key="REDIS_PORT",
level=ConfigLevel.REQUIRED,
description="Redis端口",
example="6379",
validator=lambda v: v.isdigit() and 1 <= int(v) <= 65535
),
ConfigItem(
key="JWT_SECRET",
level=ConfigLevel.REQUIRED,
description="JWT密钥(用于生成认证令牌)",
example="your-super-secret-jwt-key-change-in-production",
validator=lambda v: len(v) >= 16
),
]
# 推荐配置项
RECOMMENDED_CONFIGS = [
ConfigItem(
key="DEEPSEEK_API_KEY",
level=ConfigLevel.RECOMMENDED,
description="DeepSeek API密钥(推荐,性价比高)",
example="sk-xxx",
help_url="https://platform.deepseek.com/"
),
ConfigItem(
key="DASHSCOPE_API_KEY",
level=ConfigLevel.RECOMMENDED,
description="阿里百炼API密钥(推荐,国产稳定)",
example="sk-xxx",
help_url="https://dashscope.aliyun.com/"
),
ConfigItem(
key="TUSHARE_TOKEN",
level=ConfigLevel.RECOMMENDED,
description="Tushare Token(推荐,专业A股数据)",
example="xxx",
help_url="https://tushare.pro/register?reg=tacn"
),
]
def __init__(self):
self.result = ValidationResult(
success=True,
missing_required=[],
missing_recommended=[],
invalid_configs=[],
warnings=[]
)
def _is_valid_api_key(self, api_key: str) -> bool:
"""
判断 API Key 是否有效(不是占位符)
Args:
api_key: 待验证的 API Key
Returns:
bool: True 表示有效,False 表示无效或占位符
"""
if not api_key:
return False
# 去除首尾空格和引号
api_key = api_key.strip().strip('"').strip("'")
# 检查是否为空
if not api_key:
return False
# 检查是否为占位符(前缀)
if api_key.startswith('your_') or api_key.startswith('your-'):
return False
# 检查是否为占位符(后缀)
if api_key.endswith('_here') or api_key.endswith('-here'):
return False
# 检查长度(大多数 API Key 都 > 10 个字符)
if len(api_key) <= 10:
return False
return True
def validate(self) -> ValidationResult:
"""
验证配置
Returns:
ValidationResult: 验证结果
"""
logger.info("🔍 开始验证启动配置...")
# 验证必需配置
self._validate_required_configs()
# 验证推荐配置
self._validate_recommended_configs()
# 检查安全配置
self._check_security_configs()
# 设置验证结果
self.result.success = len(self.result.missing_required) == 0 and len(self.result.invalid_configs) == 0
# 输出验证结果
self._print_validation_result()
return self.result
def _validate_required_configs(self):
"""验证必需配置"""
for config in self.REQUIRED_CONFIGS:
value = os.getenv(config.key)
if not value:
self.result.missing_required.append(config)
logger.error(f"❌ 缺少必需配置: {config.key}")
elif config.validator and not config.validator(value):
self.result.invalid_configs.append((config, "配置值格式不正确"))
logger.error(f"❌ 配置格式错误: {config.key}")
else:
logger.debug(f"✅ {config.key}: 已配置")
def _validate_recommended_configs(self):
"""验证推荐配置"""
for config in self.RECOMMENDED_CONFIGS:
value = os.getenv(config.key)
if not value:
self.result.missing_recommended.append(config)
logger.warning(f"⚠️ 缺少推荐配置: {config.key}")
elif not self._is_valid_api_key(value):
# API Key 存在但是占位符,视为未配置
self.result.missing_recommen
gitextract_0f83fkc7/
├── .dockerignore
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ ├── config.yml
│ │ ├── documentation.md
│ │ ├── feature_request.md
│ │ └── question.md
│ ├── pull_request_template.md
│ └── workflows/
│ ├── docker-publish.yml
│ └── upstream-sync-check.yml
├── .gitignore
├── .python-version
├── .streamlit/
│ └── config.toml
├── ACKNOWLEDGMENTS.md
├── COMMERCIAL_LICENSE_TEMPLATE.md
├── CONTRIBUTORS.md
├── COPYRIGHT.md
├── Dockerfile.backend
├── Dockerfile.frontend
├── LICENSE
├── LICENSING.md
├── README.md
├── VERSION
├── app/
│ ├── LICENSE
│ ├── __init__.py
│ ├── __main__.py
│ ├── constants/
│ │ └── model_capabilities.py
│ ├── core/
│ │ ├── __init__.py
│ │ ├── config.py
│ │ ├── config_bridge.py
│ │ ├── config_compat.py
│ │ ├── database.py
│ │ ├── dev_config.py
│ │ ├── logging_config.py
│ │ ├── logging_context.py
│ │ ├── rate_limiter.py
│ │ ├── redis_client.py
│ │ ├── response.py
│ │ ├── startup_validator.py
│ │ └── unified_config.py
│ ├── main.py
│ ├── middleware/
│ │ ├── __init__.py
│ │ ├── error_handler.py
│ │ ├── operation_log_middleware.py
│ │ ├── rate_limit.py
│ │ └── request_id.py
│ ├── models/
│ │ ├── __init__.py
│ │ ├── analysis.py
│ │ ├── config.py
│ │ ├── notification.py
│ │ ├── operation_log.py
│ │ ├── screening.py
│ │ ├── stock_models.py
│ │ └── user.py
│ ├── routers/
│ │ ├── __init__.py
│ │ ├── akshare_init.py
│ │ ├── analysis.py
│ │ ├── auth_db.py
│ │ ├── baostock_init.py
│ │ ├── cache.py
│ │ ├── config.py
│ │ ├── database.py
│ │ ├── favorites.py
│ │ ├── financial_data.py
│ │ ├── health.py
│ │ ├── historical_data.py
│ │ ├── internal_messages.py
│ │ ├── logs.py
│ │ ├── model_capabilities.py
│ │ ├── multi_market_stocks.py
│ │ ├── multi_period_sync.py
│ │ ├── multi_source_sync.py
│ │ ├── news_data.py
│ │ ├── notifications.py
│ │ ├── operation_logs.py
│ │ ├── paper.py
│ │ ├── queue.py
│ │ ├── reports.py
│ │ ├── scheduler.py
│ │ ├── screening.py
│ │ ├── social_media.py
│ │ ├── sse.py
│ │ ├── stock_data.py
│ │ ├── stock_sync.py
│ │ ├── stocks.py
│ │ ├── sync.py
│ │ ├── system_config.py
│ │ ├── tags.py
│ │ ├── tushare_init.py
│ │ ├── usage_statistics.py
│ │ └── websocket_notifications.py
│ ├── schemas/
│ │ └── __init__.py
│ ├── scripts/
│ │ └── init_providers.py
│ ├── services/
│ │ ├── __init__.py
│ │ ├── analysis/
│ │ │ ├── __init__.py
│ │ │ └── status_update_utils.py
│ │ ├── analysis_service.py
│ │ ├── auth_service.py
│ │ ├── basics_sync/
│ │ │ ├── __init__.py
│ │ │ ├── processing.py
│ │ │ └── utils.py
│ │ ├── basics_sync_service.py
│ │ ├── config_provider.py
│ │ ├── config_service.py
│ │ ├── data_consistency_checker.py
│ │ ├── data_sources/
│ │ │ ├── __init__.py
│ │ │ ├── akshare_adapter.py
│ │ │ ├── baostock_adapter.py
│ │ │ ├── base.py
│ │ │ ├── data_consistency_checker.py
│ │ │ ├── manager.py
│ │ │ └── tushare_adapter.py
│ │ ├── database/
│ │ │ ├── __init__.py
│ │ │ ├── backups.py
│ │ │ ├── cleanup.py
│ │ │ ├── serialization.py
│ │ │ └── status_checks.py
│ │ ├── database_screening_service.py
│ │ ├── database_service.py
│ │ ├── enhanced_screening/
│ │ │ └── utils.py
│ │ ├── enhanced_screening_service.py
│ │ ├── favorites_service.py
│ │ ├── financial_data_service.py
│ │ ├── foreign_stock_service.py
│ │ ├── historical_data_service.py
│ │ ├── internal_message_service.py
│ │ ├── log_export_service.py
│ │ ├── memory_state_manager.py
│ │ ├── model_capability_service.py
│ │ ├── multi_source_basics_sync_service.py
│ │ ├── news_data_service.py
│ │ ├── notifications_service.py
│ │ ├── operation_log_service.py
│ │ ├── progress/
│ │ │ ├── __init__.py
│ │ │ ├── log_handler.py
│ │ │ └── tracker.py
│ │ ├── progress_log_handler.py
│ │ ├── queue/
│ │ │ ├── __init__.py
│ │ │ ├── helpers.py
│ │ │ └── keys.py
│ │ ├── queue_service.py
│ │ ├── quotes_ingestion_service.py
│ │ ├── quotes_service.py
│ │ ├── redis_progress_tracker.py
│ │ ├── scheduler_service.py
│ │ ├── screening/
│ │ │ └── eval_utils.py
│ │ ├── screening_service.py
│ │ ├── simple_analysis_service.py
│ │ ├── social_media_service.py
│ │ ├── stock_data_service.py
│ │ ├── tags_service.py
│ │ ├── unified_stock_service.py
│ │ ├── usage_statistics_service.py
│ │ ├── user_service.py
│ │ └── websocket_manager.py
│ ├── utils/
│ │ ├── api_key_utils.py
│ │ ├── error_formatter.py
│ │ ├── report_exporter.py
│ │ ├── timezone.py
│ │ └── trading_time.py
│ ├── worker/
│ │ ├── __init__.py
│ │ ├── akshare_init_service.py
│ │ ├── akshare_sync_service.py
│ │ ├── analysis_worker.py
│ │ ├── baostock_init_service.py
│ │ ├── baostock_sync_service.py
│ │ ├── example_sdk_sync_service.py
│ │ ├── financial_data_sync_service.py
│ │ ├── hk_data_service.py
│ │ ├── hk_sync_service.py
│ │ ├── multi_period_sync_service.py
│ │ ├── news_data_sync_service.py
│ │ ├── tushare_init_service.py
│ │ ├── tushare_sync_service.py
│ │ ├── us_data_service.py
│ │ └── us_sync_service.py
│ └── worker.py
├── cli/
│ ├── __init__.py
│ ├── akshare_init.py
│ ├── baostock_init.py
│ ├── main.py
│ ├── models.py
│ ├── static/
│ │ └── welcome.txt
│ ├── tushare_init.py
│ └── utils.py
├── config/
│ ├── README.md
│ ├── logging.toml
│ └── logging_docker.toml
├── docker/
│ └── nginx.conf
├── docker-compose.hub.nginx.arm.yml
├── docker-compose.hub.nginx.yml
├── docker-compose.yml
├── docs/
│ ├── ANALYST_DATA_CONFIGURATION.md
│ ├── API_KEY_MANAGEMENT_ANALYSIS.md
│ ├── API_KEY_TESTING_GUIDE.md
│ ├── BUILD_GUIDE.md
│ ├── CNAME
│ ├── CONFIG_VALIDATION_FIX_SUMMARY.md
│ ├── DOCKER_REGISTRY_STRATEGY.md
│ ├── ENHANCED_HISTORY_FEATURES_SUMMARY.md
│ ├── GITHUB_BRANCH_PROTECTION.md
│ ├── LLM_ADAPTER_TEMPLATE.py
│ ├── MODEL_RECOMMENDATION_UI_UPDATE.md
│ ├── QUICK_BUILD_REFERENCE.md
│ ├── QUICK_START.md
│ ├── README.md
│ ├── SETTINGS_MERGE.md
│ ├── SILICONFLOW_SETUP_GUIDE.md
│ ├── STRUCTURE.md
│ ├── WINDOWS_INSTALLER_OPTIMIZATION.md
│ ├── agents/
│ │ └── v0.1.13/
│ │ ├── analysts.md
│ │ ├── managers.md
│ │ ├── researchers.md
│ │ ├── risk-management.md
│ │ └── trader.md
│ ├── analysis/
│ │ ├── 4级深度分析验证报告_20251011.md
│ │ ├── analysis-nodes-and-tools.md
│ │ ├── combined_data_quick_reference.md
│ │ ├── combined_data_structure_analysis.md
│ │ ├── market_analyst_technical_analysis_issue.md
│ │ ├── pe-pb-data-update-analysis.md
│ │ ├── quotes_ingestion_optimization_summary.md
│ │ ├── quotes_ingestion_service_analysis.md
│ │ └── 时间统计准确性分析_20251011.md
│ ├── api/
│ │ └── batch-analysis-limits.md
│ ├── architecture/
│ │ ├── API_ARCHITECTURE_UPGRADE.md
│ │ ├── DATA_SOURCE_REFACTOR.md
│ │ ├── cache/
│ │ │ ├── CACHE_REFACTORING_SUMMARY.md
│ │ │ ├── CACHE_SYSTEM_ANALYSIS.md
│ │ │ ├── CACHE_SYSTEM_BUSINESS_ANALYSIS.md
│ │ │ ├── CACHE_SYSTEM_CORRECT_ANALYSIS.md
│ │ │ ├── CACHE_SYSTEM_FINAL_ANALYSIS.md
│ │ │ └── CACHE_SYSTEM_SOLUTION.md
│ │ ├── data-source/
│ │ │ └── data_priority_analysis.md
│ │ ├── data-sources-unit-comparison.md
│ │ ├── database/
│ │ │ ├── DATABASE_MANAGEMENT_IMPLEMENTATION.md
│ │ │ ├── MONGODB_COLLECTIONS_COMPARISON.md
│ │ │ ├── REQUIREMENTS_DB_UPDATE.md
│ │ │ ├── database_field_standardization_analysis.md
│ │ │ └── database_field_standardization_completed.md
│ │ ├── dataflows/
│ │ │ ├── DATAFLOWS_ARCHITECTURE_ANALYSIS.md
│ │ │ ├── DATAFLOWS_COMPREHENSIVE_OPTIMIZATION.md
│ │ │ ├── DATAFLOWS_CONSERVATIVE_REFACTORING.md
│ │ │ └── STREAM_MODE_IMPACT_ANALYSIS.md
│ │ ├── report-modules-structure.md
│ │ ├── v0.1.13/
│ │ │ ├── agent-architecture.md
│ │ │ ├── data-flow-architecture.md
│ │ │ ├── graph-structure.md
│ │ │ └── system-architecture.md
│ │ └── v0.1.16/
│ │ └── system-architecture.md
│ ├── archive/
│ │ ├── AUTHENTICATION_FIX_SUMMARY.md
│ │ ├── BACKEND_STARTUP.md
│ │ ├── FIXES_SUMMARY.md
│ │ ├── README-ORIGINAL.md
│ │ └── SOLUTION_SUMMARY.md
│ ├── blog/
│ │ ├── 2025-10-19-v1.0.0-preview-bugfixes.md
│ │ ├── 2025-10-20-system-stability-and-docker-multiarch.md
│ │ ├── 2025-10-21-configuration-system-overhaul.md
│ │ ├── 2025-10-22-config-testing-and-docker-fixes.md
│ │ ├── 2025-10-23-websocket-notifications-and-data-fixes.md
│ │ ├── 2025-10-24-docker-hub-update-and-clean-volumes.md
│ │ ├── 2025-10-24-realtime-quotes-optimization.md
│ │ ├── 2025-10-25-302ai-integration-and-ui-improvements.md
│ │ ├── 2025-10-26-user-preferences-and-financial-metrics-optimization.md
│ │ ├── 2025-10-27-compliance-optimization-and-bug-fixes.md
│ │ ├── 2025-10-28-multi-source-architecture-and-realtime-enhancements.md
│ │ ├── 2025-10-28-multi-source-data-isolation-design.md
│ │ ├── 2025-10-28-realtime-pe-pb-calculation-with-fallback-strategy.md
│ │ ├── 2025-10-29-data-source-unification-and-report-export-features.md
│ │ ├── 2025-10-30-priority-retries-and-realtime-backfill.md
│ │ ├── 2025-11-01-to-11-04-windows-installer-and-fundamental-analysis-enhancements.md
│ │ ├── 2025-11-05-to-11-06-technical-indicators-accuracy-and-data-quality.md
│ │ ├── 2025-11-07-task-execution-and-data-sync-enhancements.md
│ │ ├── 2025-11-11-us-data-source-and-cache-system-overhaul.md
│ │ ├── 2025-11-12-multi-market-support-and-async-optimization.md
│ │ ├── 2025-11-13-to-11-14-data-quality-and-system-stability-improvements.md
│ │ ├── 2025-11-15-learning-center-and-compliance-updates.md
│ │ └── green-version-backup-restore-upgrade.md
│ ├── bugfix/
│ │ ├── 2025-10-26-async-sync-conflict-fix.md
│ │ ├── 2025-10-26-estimation-audit-summary.md
│ │ ├── 2025-10-26-ps-calculation-fix.md
│ │ ├── 2025-10-26-ps-pe-calculation-summary.md
│ │ ├── 2025-10-26-realtime-api-ttm-issues.md
│ │ ├── 2025-10-26-settings-save-issues.md
│ │ ├── 2025-10-26-ttm-calculation-summary.md
│ │ ├── 2025-10-26-tushare-token-priority-issue.md
│ │ ├── 2025-10-27-add-symbol-field-to-stock-basic-info.md
│ │ └── 2025-10-27-app-error-logging-fix.md
│ ├── changes/
│ │ ├── DEPRECATION_NOTICE.md
│ │ ├── realtime-pe-pb-implementation.md
│ │ ├── remove-batch-operations.md
│ │ ├── remove-price-alert-feature.md
│ │ └── report-detail-layout-adjustment.md
│ ├── community/
│ │ ├── CALL_FOR_TESTERS.md
│ │ └── CALL_FOR_TESTERS_SHORT.md
│ ├── config/
│ │ ├── architecture.md
│ │ └── error_log_separation.md
│ ├── configuration/
│ │ ├── API_KEY_PRIORITY.md
│ │ ├── CACHE_CONFIGURATION.md
│ │ ├── CONFIGURATION_VALIDATOR.md
│ │ ├── CONFIG_MATRIX.md
│ │ ├── CONFIG_SYSTEM_VERIFICATION.md
│ │ ├── DEFAULT_BASE_URL_USAGE.md
│ │ ├── ENV_CONFIG_UPDATE.md
│ │ ├── UNIFIED_CONFIG.md
│ │ ├── config-bridge/
│ │ │ ├── CONFIG_BRIDGE_DETAILS.md
│ │ │ ├── CONFIG_BRIDGE_TEST_RESULTS.md
│ │ │ └── config_bridge_explanation.md
│ │ ├── config-guide.md
│ │ ├── configuration_analysis.md
│ │ ├── configuration_guide.md
│ │ ├── configuration_optimization_plan.md
│ │ ├── custom-openai-endpoint.md
│ │ ├── dashscope-config.md
│ │ ├── data-directory-configuration.md
│ │ ├── deepseek-config.md
│ │ ├── docker-config.md
│ │ ├── google-ai-setup.md
│ │ ├── llm-config.md
│ │ ├── migration/
│ │ │ ├── CONFIGURATION_MIGRATION.md
│ │ │ ├── CONFIG_MIGRATION.md
│ │ │ ├── CONFIG_MIGRATION_PLAN.md
│ │ │ ├── CONFIG_MIGRATION_SUMMARY.md
│ │ │ └── CONFIG_MIGRATION_TESTING.md
│ │ ├── online-tools-config.md
│ │ ├── proxy_configuration.md
│ │ ├── quotes_ingestion_config.md
│ │ └── token-tracking-guide.md
│ ├── database_setup.md
│ ├── deployment/
│ │ ├── DOCKER_LOGS_GUIDE.md
│ │ ├── EMBEDDED_PYTHON_GUIDE.md
│ │ ├── IMPLEMENTATION_SUMMARY.md
│ │ ├── PORTABLE_FAQ.md
│ │ ├── QUICK_REFERENCE.md
│ │ ├── SIMPLE_DEPLOYMENT_GUIDE.md
│ │ ├── WINDOWS_PORTABLE.md
│ │ ├── database/
│ │ │ ├── DATABASE_SETUP_GUIDE.md
│ │ │ └── export-sanitization-guide.md
│ │ ├── demo/
│ │ │ ├── demo_deployment_summary.md
│ │ │ ├── deploy_demo_system.md
│ │ │ ├── deploy_demo_with_docker.md
│ │ │ └── export_config_for_demo.md
│ │ ├── docker/
│ │ │ ├── BUILD_MULTIARCH_GUIDE.md
│ │ │ ├── DOCKER_DEPLOYMENT_v1.0.0.md
│ │ │ ├── DOCKER_FILES_README.md
│ │ │ ├── DOCKER_HUB_PUBLISH_GUIDE.md
│ │ │ ├── DOCKER_PUBLISH_GUIDE.md
│ │ │ ├── GITHUB_ACTIONS_QUICKSTART.md
│ │ │ ├── GITHUB_ACTIONS_SETUP.md
│ │ │ ├── MULTIARCH_BUILD.md
│ │ │ ├── MULTIARCH_BUILD_OPTIMIZATION.md
│ │ │ ├── docker-compose.split.yml
│ │ │ ├── docker_deployment_guide.md
│ │ │ └── quick_deploy_with_docker_hub.md
│ │ ├── docker-build-guide.md
│ │ ├── operations/
│ │ │ ├── EMERGENCY_PROCEDURES.md
│ │ │ ├── service_control.md
│ │ │ └── startup-commands-update.md
│ │ ├── portable-port-configuration.md
│ │ ├── portable-python-independence.md
│ │ ├── stop-services-guide.md
│ │ ├── v0.1.16/
│ │ │ └── deployment-guide.md
│ │ └── v1.0.0-source-installation.md
│ ├── design/
│ │ ├── README.md
│ │ ├── api_specification.md
│ │ ├── configuration_management.md
│ │ ├── hk_stock_data_source_priority.md
│ │ ├── paper_trading_multi_market_design.md
│ │ ├── stock_analysis_system_design.md
│ │ ├── stock_data_methods_analysis.md
│ │ ├── stock_data_model_design.md
│ │ ├── stock_data_quick_reference.md
│ │ ├── timezone-strategy.md
│ │ ├── v0.1.16/
│ │ │ └── api-specification.md
│ │ └── v1.0.1/
│ │ ├── 00_COMPLETION_REPORT.md
│ │ ├── 00_START_HERE.md
│ │ ├── AGENT_TEMPLATE_SPECIFICATIONS.md
│ │ ├── DATABASE_AND_USER_MANAGEMENT.md
│ │ ├── DESIGN_COMPLETION_REPORT.md
│ │ ├── DESIGN_COMPLETION_SUMMARY.md
│ │ ├── ENHANCED_API_DESIGN.md
│ │ ├── ENHANCED_IMPLEMENTATION_ROADMAP.md
│ │ ├── ENHANCEMENT_SUMMARY.md
│ │ ├── EXTENDED_AGENTS_SUPPORT.md
│ │ ├── FINAL_COMPLETION_REPORT.md
│ │ ├── FINAL_DESIGN_NOTES.md
│ │ ├── FINAL_SUMMARY.md
│ │ ├── FRONTEND_UI_DESIGN.md
│ │ ├── IMPLEMENTATION_CHECKLIST.md
│ │ ├── IMPLEMENTATION_IN_APP_DIRECTORY.md
│ │ ├── IMPLEMENTATION_ROADMAP.md
│ │ ├── INDEX.md
│ │ ├── INTEGRATION_WITH_EXISTING_SYSTEM.md
│ │ ├── PROMPT_TEMPLATE_SYSTEM_SUMMARY.md
│ │ ├── QUICK_REFERENCE.md
│ │ ├── README.md
│ │ ├── VERSION_UPDATE_SUMMARY.md
│ │ ├── prompt_template_architecture_comparison.md
│ │ ├── prompt_template_architecture_diagram.md
│ │ ├── prompt_template_implementation_guide.md
│ │ ├── prompt_template_system_design.md
│ │ ├── prompt_template_technical_spec.md
│ │ └── prompt_template_usage_examples.md
│ ├── development/
│ │ ├── 2025-10-19-dev-plan-unified-standard-plugin-llm.md
│ │ ├── ADD_NEW_DATA_SOURCE.md
│ │ ├── BRANCH_GUIDE.md
│ │ ├── BRANCH_MANAGEMENT_STRATEGY.md
│ │ ├── CIRCULAR_CALL_ANALYSIS.md
│ │ ├── CONTRIBUTING.md
│ │ ├── DEVELOPMENT_SETUP.md
│ │ ├── DEVELOPMENT_WORKFLOW.md
│ │ ├── US_DATA_SOURCE_UPGRADE_PLAN.md
│ │ ├── architecture/
│ │ │ ├── screening_a_shares_daily_p0.md
│ │ │ └── technical_indicators_unification.md
│ │ ├── branch-strategy.md
│ │ ├── development-workflow.md
│ │ ├── project-structure.md
│ │ ├── roadmap/
│ │ │ └── trading_workflow_dev_plan.md
│ │ └── v0.1.16/
│ │ └── frontend-guide.md
│ ├── docker/
│ │ ├── pdf-export-support.md
│ │ ├── startup-guide.md
│ │ └── volumes/
│ │ ├── docker_volumes_analysis.md
│ │ ├── docker_volumes_unified.md
│ │ ├── switch_to_old_mongodb_volume.md
│ │ └── volumes_cleanup_completed.md
│ ├── docker-multiarch-build.md
│ ├── docker-report-export.md
│ ├── error-handling-improvement.md
│ ├── examples/
│ │ ├── advanced-examples.md
│ │ └── basic-examples.md
│ ├── faq/
│ │ └── faq.md
│ ├── features/
│ │ ├── NEWS_ANALYST_TOOL_CALL_FIX_REPORT.md
│ │ ├── NEWS_FILTERING_SOLUTION_DESIGN.md
│ │ ├── NEWS_QUALITY_ANALYSIS_REPORT.md
│ │ ├── aggregator/
│ │ │ ├── AGGREGATOR_IMPLEMENTATION_SUMMARY.md
│ │ │ ├── AGGREGATOR_MODEL_CATALOG.md
│ │ │ ├── AGGREGATOR_QUICKSTART.md
│ │ │ ├── AGGREGATOR_SUPPORT.md
│ │ │ └── CHANGELOG_AGGREGATOR.md
│ │ ├── config-wizard/
│ │ │ ├── CONFIG_WIZARD.md
│ │ │ ├── CONFIG_WIZARD_BACKEND_INTEGRATION.md
│ │ │ ├── CONFIG_WIZARD_USAGE.md
│ │ │ └── CONFIG_WIZARD_VS_CONFIG_MANAGEMENT.md
│ │ ├── data-sync/
│ │ │ ├── MULTI_PERIOD_DATA_SYNC_UPDATE.md
│ │ │ └── MULTI_SOURCE_SYNC_GUIDE.md
│ │ ├── docker-deployment.md
│ │ ├── model-settings/
│ │ │ ├── LLM_CONFIG_UI_UPDATE.md
│ │ │ ├── SYSTEM_SETTINGS_MODEL_SELECTION.md
│ │ │ └── model-capability-ui-update.md
│ │ ├── news/
│ │ │ ├── NEWS_SENTIMENT_ANALYSIS.md
│ │ │ ├── NEWS_SYNC_FEATURE.md
│ │ │ └── news-analysis-system.md
│ │ ├── paper-trading/
│ │ │ ├── PAPER_TRADING_IMPROVEMENTS.md
│ │ │ └── PAPER_TRADING_SELL_BUTTON.md
│ │ ├── progress-tracking/
│ │ │ ├── PROGRESS_TRACKING_SOLUTION.md
│ │ │ ├── progress-tracking-explanation.md
│ │ │ ├── progress_issue_analysis.md
│ │ │ └── progress_optimization.md
│ │ ├── reporting/
│ │ │ ├── REPORT_TO_TRADING_FEATURE.md
│ │ │ ├── analysis_report_comparison_summary.md
│ │ │ ├── report-detail-metrics-enhancement.md
│ │ │ └── report-export.md
│ │ ├── stock-detail/
│ │ │ ├── STOCK_DETAIL_FUNDAMENTALS_ENHANCEMENT.md
│ │ │ └── STOCK_DETAIL_UI_OPTIMIZATION.md
│ │ └── usage-statistics/
│ │ ├── HOW_TO_ACCESS_USAGE_STATISTICS.md
│ │ ├── USAGE_STATISTICS_AND_PRICING.md
│ │ ├── USAGE_STATISTICS_FRONTEND_GUIDE.md
│ │ ├── USAGE_STATISTICS_IMPLEMENTATION_SUMMARY.md
│ │ └── USAGE_STATISTICS_QUICK_TEST.md
│ ├── fixes/
│ │ ├── 2025-10-11_bug_fixes_summary.md
│ │ ├── 2025-10-11_code_cleanup_summary.md
│ │ ├── 2025-10-11_debug_logging_enhancement.md
│ │ ├── 2025-10-11_remove_online_tools_config.md
│ │ ├── 2025-10-21-config-validation-placeholder-detection.md
│ │ ├── 2025-10-21-pyproject-missing-dependencies.md
│ │ ├── 2025-10-21-summary.md
│ │ ├── 2025-10-30-data-source-priority-fixes.md
│ │ ├── API_PATH_FIX.md
│ │ ├── DASHBOARD_MARKET_NEWS_EMPTY_FIX.md
│ │ ├── DATAFRAME_ARROW_CONVERSION_FIX.md
│ │ ├── MARKET_QUOTES_NULL_CODE_FIX.md
│ │ ├── NEWS_SYNC_SCHEDULER_SETUP.md
│ │ ├── REDIS_CONNECTION_LEAK_ANALYSIS.md
│ │ ├── SUMMARY.md
│ │ ├── amount-unit-fix.md
│ │ ├── analyst_infinite_loop_fix.md
│ │ ├── asyncio_thread_pool_fix.md
│ │ ├── batch-analysis-api-response-fix.md
│ │ ├── batch-analysis-router-fix.md
│ │ ├── batch_analysis_5_levels_verification.md
│ │ ├── confidence-score-normalization-fix.md
│ │ ├── dashboard/
│ │ │ ├── DASHBOARD_DATA_FIX.md
│ │ │ ├── DASHBOARD_MARKET_NEWS_FIX.md
│ │ │ └── DASHBOARD_RECENT_TASKS_FIX.md
│ │ ├── dashboard_news_improvements.md
│ │ ├── data-source/
│ │ │ ├── BUG_FIX_FULL_SYMBOL_INDEX.md
│ │ │ ├── PROVIDER_ID_FIX.md
│ │ │ ├── bugfix_akshare_import_error.md
│ │ │ ├── financial_metrics_fix_report.md
│ │ │ ├── fix_7digit_stock_code_issue.md
│ │ │ ├── fix_baostock_realtime_quotes_issue.md
│ │ │ ├── fix_financial_data_code_field_issue.md
│ │ │ ├── fix_hk_stock_code_normalization.md
│ │ │ ├── fix_multi_source_basics_sync.md
│ │ │ ├── fix_stock_utils_hk_recognition.md
│ │ │ └── weekend_trading_data_issue.md
│ │ ├── debate_rounds_logging.md
│ │ ├── frontend/
│ │ │ ├── FRONTEND_API_URL_FIX.md
│ │ │ ├── FRONTEND_ROUTE_FIX.md
│ │ │ ├── FRONTEND_VMODEL_FIX.md
│ │ │ ├── MODEL_NAME_DISPLAY_FIX.md
│ │ │ ├── PAPER_TRADING_REPORT_LINK_FIX.md
│ │ │ ├── PAPER_TRADING_STOCK_NAME_FIX.md
│ │ │ ├── REPORT_DETAIL_CASH_FIX.md
│ │ │ ├── STOCK_DETAIL_REPORTS_FIX.md
│ │ │ └── STOCK_SCREENING_DETAIL_LINK_FIX.md
│ │ ├── fundamentals-duplicate-tool-call-fix.md
│ │ ├── llm_timeout_monitoring.md
│ │ ├── llm_wrong_tool_call_analysis.md
│ │ ├── misc/
│ │ │ ├── COMPATIBILITY_FIX_SUMMARY.md
│ │ │ ├── ISSUE_FIX_SUMMARY.md
│ │ │ └── TRADING_FIX_SUMMARY.md
│ │ ├── missing_report_modules_analysis.md
│ │ ├── model/
│ │ │ ├── model_capability_validation_fix.md
│ │ │ ├── model_config_params_fix.md
│ │ │ ├── model_routing_fix.md
│ │ │ └── research_depth_mapping_fix.md
│ │ ├── mongodb_objectid_serialization_fix.md
│ │ ├── performance/
│ │ │ ├── BUG_FIX_ANALYSIS_STUCK.md
│ │ │ ├── async_blocking_fix.md
│ │ │ ├── estimated_time_fix.md
│ │ │ ├── estimated_total_time_fix.md
│ │ │ └── progress-tracking-fix.md
│ │ ├── reports-market-filter-fix.md
│ │ ├── reports-market-type-fix-complete.md
│ │ ├── reports-market-type-missing-fix.md
│ │ ├── research_depth_5_levels.md
│ │ ├── roe_debt_ratio_fix.md
│ │ ├── tdx_removal.md
│ │ ├── tushare_rt_k_fix.md
│ │ ├── undefined_variable_is_china_fix.md
│ │ └── volume-unit-fix.md
│ ├── frontend/
│ │ ├── DASHBOARD_LAYOUT_ADJUSTMENT.md
│ │ ├── DASHBOARD_PAPER_TRADING.md
│ │ ├── FRONTEND_CONFIG_REFACTOR.md
│ │ ├── FRONTEND_MULTI_SOURCE_SYNC.md
│ │ ├── batch-analysis-improvements.md
│ │ ├── guide-auto-hide.md
│ │ └── price-format-update.md
│ ├── frontend-auth-optimization.md
│ ├── google-ai-base-url-support.md
│ ├── guides/
│ │ ├── CURRENCY_GUIDE.md
│ │ ├── DATABASE_BACKUP_RESTORE.md
│ │ ├── INSTALLATION_GUIDE.md
│ │ ├── INSTALLATION_GUIDE_V1.md
│ │ ├── INSTALLATION_QUICK_START.md
│ │ ├── LINUX_BUILD_GUIDE.md
│ │ ├── README.md
│ │ ├── TESTING_GUIDE.md
│ │ ├── US_DATA_SOURCE_CONFIG.md
│ │ ├── a-share-analysis-guide.md
│ │ ├── akshare_unified/
│ │ │ ├── README.md
│ │ │ └── SYNC_FREQUENCY_GUIDE.md
│ │ ├── baostock_unified/
│ │ │ └── README.md
│ │ ├── config-management-guide.md
│ │ ├── deepseek-usage-guide.md
│ │ ├── docker-deployment-guide.md
│ │ ├── financial_data_system/
│ │ │ └── README.md
│ │ ├── historical_data_optimization/
│ │ │ └── README.md
│ │ ├── installation/
│ │ │ └── pdf_tools.md
│ │ ├── installation-guide.md
│ │ ├── message_data_system/
│ │ │ └── README.md
│ │ ├── multi_period_historical_data/
│ │ │ └── README.md
│ │ ├── news-analysis-guide.md
│ │ ├── news_data_system/
│ │ │ └── README.md
│ │ ├── pdf_export_guide.md
│ │ ├── portable-installation-guide.md
│ │ ├── quick-reference-nodes-tools.md
│ │ ├── quick-start-guide.md
│ │ ├── report-export-guide.md
│ │ ├── research-depth-guide.md
│ │ ├── scheduled_tasks_guide.md
│ │ ├── scheduler_frontend_bugfix.md
│ │ ├── scheduler_frontend_complete.md
│ │ ├── scheduler_frontend_implementation.md
│ │ ├── scheduler_frontend_summary.md
│ │ ├── scheduler_management.md
│ │ ├── scheduler_management_summary.md
│ │ ├── scheduler_metadata_feature.md
│ │ ├── sdk_integration_checklist.md
│ │ ├── stock_basics_sync.md
│ │ ├── stock_data_sdk_integration_guide.md
│ │ ├── tushare_financial_data/
│ │ │ └── README.md
│ │ ├── tushare_news_integration/
│ │ │ └── README.md
│ │ ├── tushare_unified/
│ │ │ ├── README.md
│ │ │ ├── apscheduler_integration_report.md
│ │ │ ├── current_data_sources_analysis.md
│ │ │ ├── data_initialization_guide.md
│ │ │ ├── data_sources_architecture_planning.md
│ │ │ ├── data_sources_migration_plan_a.md
│ │ │ ├── deployment_verification_report.md
│ │ │ ├── tushare_unified_design.md
│ │ │ └── tushare_unified_test_report.md
│ │ └── websocket_notifications.md
│ ├── images/
│ │ └── README.md
│ ├── implementation/
│ │ ├── foreign_stock_support.md
│ │ └── realtime-pe-pb-implementation-plan.md
│ ├── import_config_with_script.md
│ ├── improvements/
│ │ ├── BACKEND_OPTIMIZATION.md
│ │ ├── TRADINGAGENTS_OPTIMIZATION_ANALYSIS.md
│ │ ├── UTILS_CLEANUP_SUMMARY.md
│ │ ├── cli-web-report-unification.md
│ │ ├── refactoring_summary.md
│ │ └── request_deduplication.md
│ ├── installation-mirror.md
│ ├── integration/
│ │ ├── adapters/
│ │ │ ├── ADAPTER_PROVIDER_REORGANIZATION.md
│ │ │ └── data_adapters_analysis.md
│ │ ├── data-sources/
│ │ │ ├── DATA_SOURCE_LOGGING.md
│ │ │ ├── DATA_SOURCE_MANAGER_ENHANCEMENT.md
│ │ │ ├── KLINE_DATA_SOURCE.md
│ │ │ ├── STOCK_DATA_SERVICE_VS_DATA_SOURCE_MANAGER.md
│ │ │ ├── realtime_quotes_data_source.md
│ │ │ ├── stock_code_validation.md
│ │ │ └── stock_code_validation_backend.md
│ │ ├── dataflows_integration_plan.md
│ │ ├── enhanced_data_integration.md
│ │ ├── google/
│ │ │ ├── google_ai_dependencies_update.md
│ │ │ └── google_api_proxy_setup.md
│ │ ├── integration_summary.md
│ │ ├── providers/
│ │ │ ├── mixed_provider_mode.md
│ │ │ ├── tushare/
│ │ │ │ ├── TDX_TO_TUSHARE_MIGRATION.md
│ │ │ │ ├── TUSHARE_ADAPTER_REFACTORING.md
│ │ │ │ ├── TUSHARE_ARCHITECTURE_REFACTOR.md
│ │ │ │ ├── TUSHARE_INTEGRATION_SUMMARY.md
│ │ │ │ ├── TUSHARE_USAGE_GUIDE.md
│ │ │ │ └── tdx_removal_complete.md
│ │ │ └── us/
│ │ │ ├── US_PROVIDERS_EXPLANATION.md
│ │ │ └── US_PROVIDERS_MIGRATION_SUMMARY.md
│ │ └── rate-limit/
│ │ ├── RATE_LIMIT_HANDLING.md
│ │ └── test_akshare_rate_limit.md
│ ├── learning/
│ │ ├── 01-ai-basics/
│ │ │ └── what-is-llm.md
│ │ ├── 02-prompt-engineering/
│ │ │ ├── best-practices.md
│ │ │ └── prompt-basics.md
│ │ ├── 03-model-selection/
│ │ │ └── model-comparison.md
│ │ ├── 04-analysis-principles/
│ │ │ └── multi-agent-system.md
│ │ ├── 05-risks-limitations/
│ │ │ └── risk-warnings.md
│ │ ├── 06-resources/
│ │ │ ├── paper-guide.md
│ │ │ └── tradingagents-intro.md
│ │ ├── 08-faq/
│ │ │ └── general-questions.md
│ │ └── README.md
│ ├── llm/
│ │ ├── LLM_INTEGRATION_GUIDE.md
│ │ ├── LLM_TESTING_VALIDATION_GUIDE.md
│ │ ├── MODEL_CATALOG_IMPLEMENTATION_SUMMARY.md
│ │ ├── MODEL_CATALOG_MANAGEMENT.md
│ │ ├── MODEL_CATALOG_PROVIDER_SELECT.md
│ │ ├── MODEL_CATALOG_QUICKSTART.md
│ │ ├── MODEL_FILTERING.md
│ │ ├── MODEL_PRICING_GUIDE.md
│ │ ├── MODEL_PRICING_SYNC.md
│ │ ├── MODEL_USAGE_VERIFICATION.md
│ │ ├── QIANFAN_INTEGRATION_GUIDE.md
│ │ ├── README.md
│ │ ├── google_models_guide.md
│ │ ├── google_tool_handler_optimization.md
│ │ ├── model-capability-system.md
│ │ └── model_update_summary.md
│ ├── localization/
│ │ └── chinese-social-media-integration.md
│ ├── maintenance/
│ │ ├── mongodb_index_optimization.md
│ │ └── upstream-sync.md
│ ├── migration/
│ │ ├── DATA_DIRECTORY_MIGRATION_COMPLETED.md
│ │ └── DATA_DIRECTORY_REORGANIZATION_PLAN.md
│ ├── overview/
│ │ ├── OPEN_SOURCE_DISCLAIMER.md
│ │ ├── installation.md
│ │ ├── project-overview.md
│ │ └── quick-start.md
│ ├── paper/
│ │ └── TradingAgents_论文中文版.md
│ ├── releases/
│ │ ├── CHANGELOG.md
│ │ ├── CHANGELOG_v0.1.11.md
│ │ ├── CHANGELOG_v0.1.12.md
│ │ ├── CHANGELOG_v1.0.0-preview.md
│ │ ├── VERSION_0.1.6_RELEASE_NOTES.md
│ │ ├── VERSION_0.1.7_RELEASE_NOTES.md
│ │ ├── upgrade-guide.md
│ │ ├── upgrade-to-v0.1.13-preview.md
│ │ ├── v0.1.10-release-notes.md
│ │ ├── v0.1.11-release-notes.md
│ │ ├── v0.1.12-release-notes.md
│ │ ├── v0.1.13-highlights.md
│ │ ├── v0.1.13-known-issues.md
│ │ ├── v0.1.13-release-notes.md
│ │ ├── v0.1.14-release-notes.md
│ │ ├── v0.1.15-release-notes.md
│ │ ├── v0.1.16-design-document.md
│ │ ├── v0.1.16-preview-release-notes.md
│ │ ├── v0.1.7-release-notes.md
│ │ ├── v0.1.8-release-notes.md
│ │ ├── v0.1.9-release-notes.md
│ │ ├── v1.0.0-preview-release-notes.md
│ │ └── version-comparison.md
│ ├── security/
│ │ ├── api_keys_security.md
│ │ └── auth_system_improvement.md
│ ├── summary/
│ │ ├── DOCUMENTATION_UPDATE_SUMMARY.md
│ │ ├── RECENT_IMPROVEMENTS_SUMMARY.md
│ │ ├── pe-pb-realtime-solution-summary.md
│ │ └── phase/
│ │ ├── PHASE1_CLEANUP_SUMMARY.md
│ │ ├── PHASE2_COMPLETION.md
│ │ ├── PHASE2_REORGANIZATION_SUMMARY.md
│ │ ├── PHASE3_COMPLETION.md
│ │ └── PHASE3_WEB_UI_OPTIMIZATION.md
│ ├── survey/
│ │ ├── ONLINE_SURVEY_TEMPLATE.json
│ │ ├── PROMOTION_TEMPLATES.md
│ │ ├── README.md
│ │ ├── SURVEY_ANALYSIS_GUIDE.md
│ │ └── USER_SURVEY_2025.md
│ ├── tech_reviews/
│ │ ├── 2025-10-19-backtest-papertrade-licensing-architecture.md
│ │ ├── 2025-10-19-meeting-minutes-data-consistency-backtest-paper-architecture.md
│ │ ├── 2025-10-19-plugin-architecture-and-governance.md
│ │ ├── 2025-10-19-prompt-strategization-guide.md
│ │ ├── 2025-10-19-unified-data-standard-implementation.md
│ │ ├── 2025-10-21-multi-market-code-templates.md
│ │ ├── 2025-10-21-multi-market-data-architecture-guide.md
│ │ └── 2025-11-08-multi-market-implementation-plan.md
│ ├── technical/
│ │ ├── DASHSCOPE_ADAPTER_FIX_REPORT.md
│ │ ├── DASHSCOPE_ADAPTER_SIMPLIFICATION_REPORT.md
│ │ ├── DASHSCOPE_TOOL_CALL_DEFECTS_ANALYSIS.md
│ │ ├── DASHSCOPE_TOOL_CALL_ENHANCEMENT_REPORT.md
│ │ ├── DEEPSEEK_INTEGRATION.md
│ │ ├── DeepSeek新闻分析师修复报告.md
│ │ ├── DeepSeek新闻分析师死循环修复完成报告.md
│ │ ├── DeepSeek新闻分析师死循环问题分析报告.md
│ │ ├── LLM_TOOL_CALL_FIX_REPORT.md
│ │ ├── OPENAI_COMPATIBLE_ADAPTERS.md
│ │ ├── REACTIVE_IN_H_FUNCTION.md
│ │ └── v0.1.16/
│ │ └── testing-strategy.md
│ ├── technical-debt/
│ │ └── tradingagents_optimization.md
│ ├── test_environment_setup.md
│ ├── time_estimation_optimization.md
│ ├── troubleshooting/
│ │ ├── README.md
│ │ ├── batch-analysis-concurrent-fix.md
│ │ ├── batch-analysis-fix-summary.md
│ │ ├── concurrent-safety-summary.md
│ │ ├── docker-troubleshooting.md
│ │ ├── export-issues.md
│ │ ├── finnhub-news-data-setup.md
│ │ ├── google_client_options_error.md
│ │ ├── llm-config-test-fix.md
│ │ ├── pdf_word_export_issues.md
│ │ ├── stock_name_issue.md
│ │ ├── streamlit-file-watcher-fix.md
│ │ ├── web-startup-issues.md
│ │ ├── windows10-chromadb-fix.md
│ │ └── windows_cairo_fix.md
│ ├── troubleshooting-mongodb-docker.md
│ └── usage/
│ ├── deepseek-usage-guide.md
│ ├── investment_analysis_guide.md
│ ├── web-interface-detailed-guide.md
│ └── web-interface-guide.md
├── examples/
│ ├── README.md
│ ├── __init__.py
│ ├── batch_analysis.py
│ ├── cli_demo.py
│ ├── config_management_demo.py
│ ├── crawlers/
│ │ ├── internal_message_crawler.py
│ │ ├── message_crawler_scheduler.py
│ │ └── social_media_crawler.py
│ ├── custom_analysis_demo.py
│ ├── dashscope_examples/
│ │ ├── __init__.py
│ │ ├── demo_dashscope.py
│ │ ├── demo_dashscope_chinese.py
│ │ ├── demo_dashscope_no_memory.py
│ │ └── demo_dashscope_simple.py
│ ├── data_dir_config_demo.py
│ ├── demo_deepseek_analysis.py
│ ├── demo_deepseek_simple.py
│ ├── demo_news_filtering.py
│ ├── enhanced_history_demo.py
│ ├── my_stock_analysis.py
│ ├── run_message_crawlers.py
│ ├── simple_analysis_demo.py
│ ├── stock_data_model_usage.py
│ ├── stock_list_example.py
│ ├── stock_query_examples.py
│ ├── test_enhanced_data_integration.py
│ ├── test_installation.py
│ ├── test_news_timeout.py
│ ├── token_tracking_demo.py
│ ├── tushare_demo.py
│ └── tushare_unified_demo.py
├── frontend/
│ ├── .eslintrc.cjs
│ ├── .prettierrc.json
│ ├── .yarnrc
│ ├── LICENSE
│ ├── README.md
│ ├── clear_auth.html
│ ├── env.d.ts
│ ├── index.html
│ ├── package.json
│ ├── public/
│ │ └── manifest.json
│ ├── src/
│ │ ├── App.vue
│ │ ├── api/
│ │ │ ├── analysis.ts
│ │ │ ├── auth.ts
│ │ │ ├── cache.ts
│ │ │ ├── config.ts
│ │ │ ├── database.ts
│ │ │ ├── favorites.ts
│ │ │ ├── logs.ts
│ │ │ ├── modelCapabilities.ts
│ │ │ ├── multiMarket.ts
│ │ │ ├── news.ts
│ │ │ ├── notifications.ts
│ │ │ ├── operationLogs.ts
│ │ │ ├── paper.ts
│ │ │ ├── request.ts
│ │ │ ├── scheduler.ts
│ │ │ ├── screening.ts
│ │ │ ├── stockSync.ts
│ │ │ ├── stocks.ts
│ │ │ ├── sync.ts
│ │ │ ├── tags.ts
│ │ │ ├── templates.ts
│ │ │ └── usage.ts
│ │ ├── components/
│ │ │ ├── ConfigValidator.vue
│ │ │ ├── ConfigWizard.vue
│ │ │ ├── Dashboard/
│ │ │ │ └── MultiSourceSyncCard.vue
│ │ │ ├── DeepModelSelector.vue
│ │ │ ├── Dev/
│ │ │ │ └── DevPanel.vue
│ │ │ ├── Global/
│ │ │ │ ├── GlobalConfirm.vue
│ │ │ │ ├── GlobalNotification.vue
│ │ │ │ ├── MarketSelector.vue
│ │ │ │ ├── MultiMarketStockSearch.vue
│ │ │ │ ├── TaskReportDialog.vue
│ │ │ │ └── TaskResultDialog.vue
│ │ │ ├── Layout/
│ │ │ │ ├── AppFooter.vue
│ │ │ │ ├── Breadcrumb.vue
│ │ │ │ ├── HeaderActions.vue
│ │ │ │ ├── SidebarMenu.vue
│ │ │ │ └── UserProfile.vue
│ │ │ ├── ModelConfig.vue
│ │ │ ├── NetworkStatus.vue
│ │ │ ├── Sync/
│ │ │ │ ├── DataSourceStatus.vue
│ │ │ │ ├── SyncControl.vue
│ │ │ │ ├── SyncHistory.vue
│ │ │ │ └── SyncRecommendations.vue
│ │ │ └── index.ts
│ │ ├── constants/
│ │ │ └── analysts.ts
│ │ ├── layouts/
│ │ │ └── BasicLayout.vue
│ │ ├── main.ts
│ │ ├── router/
│ │ │ └── index.ts
│ │ ├── stores/
│ │ │ ├── app.ts
│ │ │ ├── auth.ts
│ │ │ └── notifications.ts
│ │ ├── styles/
│ │ │ ├── dark-theme.scss
│ │ │ ├── index.scss
│ │ │ └── variables.scss
│ │ ├── test-import.js
│ │ ├── types/
│ │ │ ├── analysis.ts
│ │ │ ├── auth.ts
│ │ │ ├── config.ts
│ │ │ └── router.d.ts
│ │ ├── utils/
│ │ │ ├── __tests__/
│ │ │ │ └── market.test.ts
│ │ │ ├── auth.ts
│ │ │ ├── datetime.ts
│ │ │ ├── market.ts
│ │ │ ├── stock.ts
│ │ │ └── stockValidator.ts
│ │ └── views/
│ │ ├── About/
│ │ │ └── index.vue
│ │ ├── Analysis/
│ │ │ ├── AnalysisHistory.vue
│ │ │ ├── BatchAnalysis.vue
│ │ │ └── SingleAnalysis.vue
│ │ ├── Auth/
│ │ │ └── Login.vue
│ │ ├── Dashboard/
│ │ │ └── index.vue
│ │ ├── Error/
│ │ │ └── 404.vue
│ │ ├── Favorites/
│ │ │ └── index.vue
│ │ ├── Learning/
│ │ │ ├── Article.vue
│ │ │ ├── Category.vue
│ │ │ └── index.vue
│ │ ├── PaperTrading/
│ │ │ └── index.vue
│ │ ├── Queue/
│ │ │ └── index.vue
│ │ ├── Reports/
│ │ │ ├── ReportDetail.vue
│ │ │ ├── TokenStatistics.vue
│ │ │ └── index.vue
│ │ ├── Screening/
│ │ │ └── index.vue
│ │ ├── Settings/
│ │ │ ├── CacheManagement.vue
│ │ │ ├── ConfigManagement.vue
│ │ │ ├── UsageStatistics.vue
│ │ │ ├── components/
│ │ │ │ ├── DataSourceConfigDialog.vue
│ │ │ │ ├── DataSourceGroupingDialog.vue
│ │ │ │ ├── LLMConfigDialog.vue
│ │ │ │ ├── MarketCategoryDialog.vue
│ │ │ │ ├── MarketCategoryManagement.vue
│ │ │ │ ├── ModelCatalogManagement.vue
│ │ │ │ ├── ProviderDialog.vue
│ │ │ │ └── SortableDataSourceList.vue
│ │ │ └── index.vue
│ │ ├── Stocks/
│ │ │ └── Detail.vue
│ │ ├── System/
│ │ │ ├── DatabaseManagement.vue
│ │ │ ├── LogManagement.vue
│ │ │ ├── MultiSourceSync.vue
│ │ │ ├── OperationLogs.vue
│ │ │ └── SchedulerManagement.vue
│ │ └── Tasks/
│ │ └── TaskCenter.vue
│ ├── tsconfig.json
│ └── vite.config.ts
├── install/
│ ├── database_export_config.json
│ └── database_export_config_2025-11-13.json
├── main.py
├── nginx/
│ └── nginx.conf
├── pyproject.toml
├── reports/
│ ├── duplicate_logger_fix_report.md
│ ├── logger_position_fix_report.md
│ ├── logging_import_fix_report.md
│ ├── pip_freeze_local.txt
│ ├── print_to_log_conversion_report.md
│ └── syntax_error_files_report.txt
├── requirements-lock.txt
├── requirements.txt
├── scripts/
│ ├── README.md
│ ├── README_import_config.md
│ ├── USER_MANAGEMENT.md
│ ├── add_302ai_provider.py
│ ├── akshare_force_sync_all.py
│ ├── akshare_sync_optimized.py
│ ├── analyze_amount_distribution.py
│ ├── analyze_data_calls.py
│ ├── archived/
│ │ └── container_quick_init.py
│ ├── backup_branches.sh
│ ├── backup_volumes.ps1
│ ├── batch_update_docs.py
│ ├── build-amd64.ps1
│ ├── build-amd64.sh
│ ├── build-and-publish-linux.sh
│ ├── build-arm64.sh
│ ├── build-multiarch.ps1
│ ├── build-multiarch.sh
│ ├── build_docker_with_pdf.ps1
│ ├── build_docker_with_pdf.py
│ ├── build_docker_with_pdf.sh
│ ├── capture_web_screenshots.py
│ ├── check-build-context.sh
│ ├── check_000001_data.py
│ ├── check_688788_info.py
│ ├── check_akshare_data_structure.py
│ ├── check_akshare_fields.py
│ ├── check_amount_unit.py
│ ├── check_analysis_reports.py
│ ├── check_api_config.py
│ ├── check_config_coverage.py
│ ├── check_config_reports.py
│ ├── check_datasource_names.py
│ ├── check_datasource_priority_simple.py
│ ├── check_db_data.py
│ ├── check_doc_consistency.py
│ ├── check_existing_reports.py
│ ├── check_export_file.py
│ ├── check_financial_data.py
│ ├── check_financial_sample.py
│ ├── check_financial_structure.py
│ ├── check_gemini_config.py
│ ├── check_gemini_provider.py
│ ├── check_google_llm_attrs.py
│ ├── check_license.py
│ ├── check_llm_pricing.py
│ ├── check_llm_providers.py
│ ├── check_missing_dependencies.py
│ ├── check_missing_stocks.py
│ ├── check_model_config.py
│ ├── check_mongodb_data_range.py
│ ├── check_mongodb_financial_data.py
│ ├── check_mongodb_system_config.py
│ ├── check_news_data.py
│ ├── check_news_fields.py
│ ├── check_news_in_db.py
│ ├── check_ningde_data.py
│ ├── check_null_code.py
│ ├── check_old_mongodb_volume.py
│ ├── check_pdf_tools.py
│ ├── check_provider_values.py
│ ├── check_redis_cache.py
│ ├── check_redis_connections.py
│ ├── check_roe.py
│ ├── check_stock_daily_data.py
│ ├── check_stock_daily_quotes_fields.py
│ ├── check_stock_fields.py
│ ├── check_stock_source.py
│ ├── check_token_usage_collection.py
│ ├── check_tushare_data_range.py
│ ├── check_us_cache_status.py
│ ├── check_us_datasource_priority.py
│ ├── check_usage_records.py
│ ├── clean_invalid_trade_date.py
│ ├── clean_volumes.ps1
│ ├── cleanup_old_system_config.py
│ ├── cleanup_test_env.ps1
│ ├── cleanup_unused_volumes.ps1
│ ├── compare_requirements.py
│ ├── config/
│ │ └── cleanup_sensitive_in_db.py
│ ├── container_init.sh
│ ├── convert_prints_to_logs.py
│ ├── create_default_admin.py
│ ├── create_default_users.py
│ ├── debug/
│ │ ├── check_industry_data.py
│ │ ├── check_log_timezone.py
│ │ ├── check_mongodb_data.py
│ │ ├── check_real_estate_data.py
│ │ ├── check_report_detail.py
│ │ ├── check_report_fields.py
│ │ ├── check_timezone.py
│ │ ├── check_user.py
│ │ ├── check_zhipu_config.py
│ │ ├── debug_000002_detailed.py
│ │ ├── debug_000002_pe.py
│ │ ├── debug_000002_simple.py
│ │ ├── debug_analysis_issue.py
│ │ ├── debug_api_response.py
│ │ ├── debug_industries.py
│ │ ├── debug_providers.py
│ │ ├── debug_valuation_data.py
│ │ └── quick_test_stock_code.py
│ ├── debug_backfill.py
│ ├── debug_bulk_write_issue.py
│ ├── debug_data_save_process.py
│ ├── debug_default_base_url.py
│ ├── debug_docker.ps1
│ ├── debug_docker.sh
│ ├── debug_enhanced_adapter.py
│ ├── debug_frontend_api.py
│ ├── debug_mongodb_connection.py
│ ├── debug_mongodb_daily_data.py
│ ├── debug_mongodb_query.py
│ ├── debug_mongodb_time.py
│ ├── debug_news_format.py
│ ├── debug_tushare_historical_sync.py
│ ├── demo_user_activity.py
│ ├── deploy_demo.sh
│ ├── deployment/
│ │ ├── README.md
│ │ ├── build_portable_package.ps1
│ │ ├── create_github_release.py
│ │ ├── create_portable_venv.ps1
│ │ ├── create_standalone_venv.ps1
│ │ ├── deploy_stop_scripts.ps1
│ │ ├── get_vendors.ps1
│ │ ├── migrate_to_embedded_python.ps1
│ │ ├── package_venv_with_runtime.ps1
│ │ ├── rebuild_portable_venv.ps1
│ │ ├── release_v0.1.2.py
│ │ ├── release_v0.1.3.py
│ │ ├── release_v0.1.9.py
│ │ ├── setup_embedded_python.ps1
│ │ ├── sync_and_build_only.ps1
│ │ ├── sync_to_portable.ps1
│ │ ├── temp_original_build.ps1
│ │ ├── update_scripts_for_embedded_python.ps1
│ │ └── verify_venv.ps1
│ ├── development/
│ │ ├── adaptive_cache_manager.py
│ │ ├── calculate_valuation_300750.py
│ │ ├── calculate_valuation_300750_v2.py
│ │ ├── download_finnhub_sample_data.py
│ │ ├── extract_comparison_results.py
│ │ ├── fix_streamlit_watcher.py
│ │ ├── organize_scripts.py
│ │ ├── prepare_upstream_contribution.py
│ │ ├── test_hk_data_fields.py
│ │ ├── test_hk_data_with_preclose.py
│ │ ├── test_hk_pe_pb.py
│ │ ├── test_hk_technical_indicators.py
│ │ ├── test_hk_valuation_apis.py
│ │ ├── test_hk_with_financials.py
│ │ ├── test_lookback_days.py
│ │ ├── test_pre_close_fix.py
│ │ ├── test_rsi_styles.py
│ │ ├── test_unified_indicators.py
│ │ └── verify_601899_stock_info.py
│ ├── diagnose_empty_data.py
│ ├── diagnose_env_vars.py
│ ├── diagnose_historical_data_sync.py
│ ├── diagnose_nginx.ps1
│ ├── diagnose_pe_pb_data.py
│ ├── diagnose_system.py
│ ├── diagnose_usage_statistics.py
│ ├── disable_structured_logs.py
│ ├── docker/
│ │ ├── README.md
│ │ ├── docker-compose-start.bat
│ │ ├── mongo-init.js
│ │ ├── start_docker_services.bat
│ │ ├── start_docker_services.sh
│ │ ├── start_services_alt_ports.bat
│ │ ├── start_services_simple.bat
│ │ ├── stop_docker_services.bat
│ │ └── stop_docker_services.sh
│ ├── docker-init.ps1
│ ├── docker-init.sh
│ ├── docker_deployment_init.py
│ ├── docker_init.ps1
│ ├── download_finnhub_data.py
│ ├── easy_install.ps1
│ ├── easy_install.sh
│ ├── enable_mongodb_cache.py
│ ├── ensure_logs_dir.py
│ ├── export_config_data.ps1
│ ├── export_config_simple.ps1
│ ├── extract_error_files.py
│ ├── fix_auth_imports.py
│ ├── fix_chromadb.ps1
│ ├── fix_chromadb.sh
│ ├── fix_chromadb_win10.ps1
│ ├── fix_depth_value.py
│ ├── fix_docker_logging.py
│ ├── fix_duplicate_loggers.py
│ ├── fix_full_symbol_index.py
│ ├── fix_logger_position.py
│ ├── fix_logging_config_error.py
│ ├── fix_logging_imports.py
│ ├── fix_market_quotes_null_code.py
│ ├── fix_null_full_symbol.py
│ ├── fix_paper_trading_initial_cash.py
│ ├── fix_provider_id_types.py
│ ├── fix_pyyaml_windows.ps1
│ ├── fix_stock_code_issue.py
│ ├── fix_us_datasource_enabled.py
│ ├── fixes/
│ │ └── fix_level3_deadlock.py
│ ├── full_redeploy_linux.sh
│ ├── get_container_logs.py
│ ├── get_main_branch_logs.py
│ ├── git/
│ │ ├── README.md
│ │ ├── branch_manager.py
│ │ ├── check_branch_overlap.py
│ │ ├── setup_fork_environment.sh
│ │ └── upstream_git_workflow.sh
│ ├── import_config_and_create_user.py
│ ├── init-directories.ps1
│ ├── init-directories.sh
│ ├── init_model_catalog.py
│ ├── init_paper_trading_market_rules.py
│ ├── init_scheduler_metadata.py
│ ├── init_system_data.py
│ ├── inspect_view_data.py
│ ├── install_and_run.py
│ ├── install_pandoc.py
│ ├── install_pdf_tools.py
│ ├── installer/
│ │ ├── setup.ps1
│ │ ├── start_all.ps1
│ │ ├── start_services_clean.ps1
│ │ └── stop_all.ps1
│ ├── log_analyzer.py
│ ├── maintenance/
│ │ ├── analyze_differences.ps1
│ │ ├── branch_manager.py
│ │ ├── cleanup_cache.py
│ │ ├── cleanup_duplicate_stocks.py
│ │ ├── create_scripts_structure.ps1
│ │ ├── debug_integration.ps1
│ │ ├── dumpmongodb.py
│ │ ├── finalize_script_organization.py
│ │ ├── fix_imports.py
│ │ ├── fix_mongodb_reports.py
│ │ ├── fix_timezone_data.py
│ │ ├── integrate_cache_improvements.ps1
│ │ ├── migrate_env_direct.py
│ │ ├── migrate_first_contribution.ps1
│ │ ├── optimize_mongodb_indexes.py
│ │ ├── organize_root_scripts.py
│ │ ├── remove_contribution_from_git.ps1
│ │ ├── reset_stock_basics.py
│ │ ├── restart_api_and_test.py
│ │ ├── sync_upstream.py
│ │ └── version_manager.py
│ ├── manual_sync_trigger.py
│ ├── migrate_add_market_type.py
│ ├── migrate_auth_to_db.py
│ ├── migrate_config.py
│ ├── migrate_config_to_db.py
│ ├── migrate_config_to_webapi.py
│ ├── migrate_data_directories.py
│ ├── migrate_financial_data_symbol_to_code.py
│ ├── migrate_paper_trading_multi_market.py
│ ├── migrate_to_unified_logging.py
│ ├── migrate_user_preferences.py
│ ├── migrate_users_to_api.py
│ ├── migration/
│ │ ├── migrate_paper_accounts_cash_structure.py
│ │ └── standardize_stock_code_fields.py
│ ├── migrations/
│ │ ├── add_symbol_field_to_stock_basic_info.py
│ │ ├── fix_stock_basic_info_symbol.py
│ │ ├── migrate_financial_data_add_symbol.py
│ │ └── migrate_stock_basic_info_add_source_index.py
│ ├── mongo-init-debug.js
│ ├── mongo-init.js
│ ├── portable/
│ │ ├── README.md
│ │ ├── stop_all.ps1
│ │ └── stop_all_services.bat
│ ├── publish-docker-images.ps1
│ ├── publish-docker-images.sh
│ ├── quick_get_logs.ps1
│ ├── quick_get_logs.sh
│ ├── quick_login_fix.py
│ ├── quick_syntax_check.py
│ ├── quick_test.py
│ ├── quick_test_pe_pb.py
│ ├── rebuild_and_test.ps1
│ ├── restore_user_analysts.py
│ ├── restore_volumes.ps1
│ ├── setup/
│ │ ├── configure_pip_source.py
│ │ ├── create_financial_data_collection.py
│ │ ├── create_historical_data_collection.py
│ │ ├── create_message_collections.py
│ │ ├── create_news_data_collection.py
│ │ ├── create_stock_screening_view.py
│ │ ├── init_database.py
│ │ ├── init_mongodb_indexes.py
│ │ ├── init_multi_market_collections.py
│ │ ├── initialize_system.py
│ │ ├── install_packages.bat
│ │ ├── install_packages_venv.bat
│ │ ├── install_pdf_tools.py
│ │ ├── manual_pip_config.py
│ │ ├── migrate_env_to_config.py
│ │ ├── pip_manager.bat
│ │ ├── quick_install.py
│ │ ├── run_in_venv.bat
│ │ ├── setup_databases.py
│ │ ├── setup_fork_environment.ps1
│ │ ├── setup_pip_source.ps1
│ │ ├── update_gitignore.bat
│ │ └── update_historical_data_indexes.py
│ ├── setup-docker.py
│ ├── simple_async_test.py
│ ├── simple_auth_migration.py
│ ├── simple_log_test.py
│ ├── smart_start.ps1
│ ├── smart_start.sh
│ ├── start_backend_with_proxy.ps1
│ ├── start_docker.ps1
│ ├── start_docker.sh
│ ├── start_test_db.ps1
│ ├── start_worker.py
│ ├── startup/
│ │ ├── restart_mongodb_with_timezone.bat
│ │ ├── restart_mongodb_with_timezone.sh
│ │ ├── start_api.py
│ │ ├── start_backend.bat
│ │ ├── start_backend.py
│ │ ├── start_backend.sh
│ │ ├── start_backend_direct.py
│ │ ├── start_debug_services.bat
│ │ ├── start_frontend.py
│ │ ├── start_production.py
│ │ ├── start_simple.bat
│ │ ├── start_simple.sh
│ │ ├── start_web.bat
│ │ ├── start_web.ps1
│ │ ├── start_web.py
│ │ └── start_web.sh
│ ├── stock_code_validator.py
│ ├── stop_test_db.ps1
│ ├── switch_and_cleanup_volumes.ps1
│ ├── switch_to_prod_env.ps1
│ ├── switch_to_test_env.ps1
│ ├── switch_volumes_simple.ps1
│ ├── sync_financial_data.py
│ ├── sync_market_news.py
│ ├── sync_model_config_to_json.py
│ ├── sync_pricing_now.py
│ ├── syntax_checker.py
│ ├── syntax_test_script.py
│ ├── test/
│ │ └── test_hk_sync.py
│ ├── test_000001_sync.py
│ ├── test_actual_analysis_url.py
│ ├── test_aggregator_support.py
│ ├── test_akshare_baostock_multi_period.py
│ ├── test_akshare_batch_quotes.py
│ ├── test_akshare_date_format.py
│ ├── test_akshare_docker.py
│ ├── test_akshare_news.py
│ ├── test_akshare_news_sync.py
│ ├── test_akshare_rate_limit.ps1
│ ├── test_akshare_rate_limit.py
│ ├── test_akshare_ttm_calculation.py
│ ├── test_akshare_with_curl_cffi.py
│ ├── test_all_base_url_fixes.py
│ ├── test_all_sources_historical_days.py
│ ├── test_alpha_vantage_finnhub.py
│ ├── test_analyst_base_url.py
│ ├── test_api_key_edit.py
│ ├── test_api_key_priority.py
│ ├── test_api_key_validation.py
│ ├── test_api_report_000002.py
│ ├── test_api_settings.py
│ ├── test_async_progress.py
│ ├── test_bridge_system_settings.py
│ ├── test_concurrent_api.py
│ ├── test_config_bridge.py
│ ├── test_config_compatibility.py
│ ├── test_config_reload.py
│ ├── test_config_service.py
│ ├── test_config_usage.py
│ ├── test_curl_cffi.py
│ ├── test_data_preparation.py
│ ├── test_data_source_logging.py
│ ├── test_database_api.py
│ ├── test_datasource_groupings.py
│ ├── test_datasource_mapping.py
│ ├── test_date_format_fix.py
│ ├── test_default_base_url.py
│ ├── test_default_base_url_fix.py
│ ├── test_direct_mongodb.py
│ ├── test_direct_news_api.py
│ ├── test_docker_export.sh
│ ├── test_docker_logging.py
│ ├── test_docker_pdf.py
│ ├── test_eastmoney_columns.py
│ ├── test_enhanced_logging.py
│ ├── test_env_config.py
│ ├── test_env_validation.py
│ ├── test_error_formatter.py
│ ├── test_estimated_total_time.py
│ ├── test_fallback_mechanism.py
│ ├── test_financial_data_fix.py
│ ├── test_financial_data_flow.py
│ ├── test_financial_fallback.py
│ ├── test_fixed_historical_sync.py
│ ├── test_foreign_stock_api.py
│ ├── test_foreign_stock_priority.py
│ ├── test_frontend_api.py
│ ├── test_fundamentals_realtime.py
│ ├── test_fundamentals_unified.py
│ ├── test_fundamentals_with_stock_name.py
│ ├── test_google_api_connection.py
│ ├── test_google_api_with_proxy.py
│ ├── test_google_base_url.py
│ ├── test_google_sdk_basic.py
│ ├── test_historical_days_fix.py
│ ├── test_hk_error_handling.py
│ ├── test_import_export.py
│ ├── test_integration_validation.py
│ ├── test_kline_realtime.py
│ ├── test_market_type_fix.py
│ ├── test_migration.py
│ ├── test_mixed_provider_mode.py
│ ├── test_model_api_base.py
│ ├── test_model_config_fix.py
│ ├── test_model_config_params.py
│ ├── test_model_features_fix.py
│ ├── test_mongodb_as_datasource.py
│ ├── test_mongodb_model_config.py
│ ├── test_mongodb_standalone.sh
│ ├── test_mongodb_storage_init.py
│ ├── test_monkey_patch.py
│ ├── test_multi_period_data.py
│ ├── test_multi_period_sync.py
│ ├── test_multi_source_sync.py
│ ├── test_news_from_db.py
│ ├── test_news_sentiment_analysis.py
│ ├── test_news_sync.py
│ ├── test_news_unified.py
│ ├── test_no_data_error.py
│ ├── test_no_infinite_retry.py
│ ├── test_pct_chg_filter.py
│ ├── test_pe_pb_fix.py
│ ├── test_preferred_sources.py
│ ├── test_progress_fix.py
│ ├── test_progress_tracking.py
│ ├── test_provider_lookup.py
│ ├── test_proxy_config.ps1
│ ├── test_ps_calculation_verification.py
│ ├── test_qianfan_connect.py
│ ├── test_qianfan_raw.py
│ ├── test_queue.py
│ ├── test_rate_limiter.py
│ ├── test_roe_fetch.py
│ ├── test_scheduler_api_response.py
│ ├── test_scheduler_frontend.py
│ ├── test_scheduler_management.py
│ ├── test_scheduler_metadata.py
│ ├── test_screening_view.py
│ ├── test_selective_sync.py
│ ├── test_settings_meta.py
│ ├── test_simple.py
│ ├── test_sina_api.py
│ ├── test_sina_columns.py
│ ├── test_smart_progress.py
│ ├── test_ssl_retry.py
│ ├── test_startup_validator.py
│ ├── test_stock_data_api.py
│ ├── test_stock_data_preparation.py
│ ├── test_stock_fundamentals_enhanced.py
│ ├── test_stock_info.py
│ ├── test_stock_info_fallback.py
│ ├── test_stock_info_fallback_fixed.py
│ ├── test_stock_info_unified.py
│ ├── test_stock_name_issue.py
│ ├── test_string_slice.py
│ ├── test_time_estimation.py
│ ├── test_token_tracking.py
│ ├── test_ttm_calculation.py
│ ├── test_ttm_calculation_logic.py
│ ├── test_tushare_roe.py
│ ├── test_tushare_rt_k.py
│ ├── test_unified_config.py
│ ├── test_update_quotes.py
│ ├── test_usage_recording.py
│ ├── test_wait_and_retry.py
│ ├── trigger_quotes_backfill.py
│ ├── unified_data_manager.py
│ ├── update_analysis_models.py
│ ├── update_db_api_keys.py
│ ├── update_model_catalog_with_pricing.py
│ ├── user_activity_manager.py
│ ├── user_manager.bat
│ ├── user_manager.ps1
│ ├── user_password_manager.py
│ ├── validate_api_keys.py
│ ├── validation/
│ │ ├── README.md
│ │ ├── analyze_missing_pe.py
│ │ ├── analyze_stock_count.py
│ │ ├── check_300750.py
│ │ ├── check_dependencies.py
│ │ ├── check_extended_fields.py
│ │ ├── check_imports.py
│ │ ├── check_stock_collections.py
│ │ ├── check_system_status.py
│ │ ├── debug_tushare_data.py
│ │ ├── diagnose_missing_fields.py
│ │ ├── inspect_analysis_tasks_schema.py
│ │ ├── smart_config.py
│ │ ├── verify_extended_fields.py
│ │ └── verify_gitignore.py
│ ├── verify_docker_logs.py
│ ├── verify_fix.py
│ ├── verify_imported_config.py
│ ├── verify_migration.py
│ ├── verify_reports_display.md
│ ├── verify_ttm_calculation_000001.py
│ ├── view_logs.py
│ ├── windows-installer/
│ │ ├── README.md
│ │ ├── nsis/
│ │ │ └── installer.nsi
│ │ ├── prepare/
│ │ │ ├── build_portable.ps1
│ │ │ └── probe_ports.ps1
│ │ └── test_installer.ps1
│ └── 补充行业信息_akshare.py
├── tests/
│ ├── 0.1.14/
│ │ ├── cleanup_test_data.py
│ │ ├── create_sample_reports.py
│ │ ├── test_analysis_save.py
│ │ ├── test_backup_datasource.py
│ │ ├── test_comprehensive_backup.py
│ │ ├── test_data_structure.py
│ │ ├── test_fallback_mechanism.py
│ │ ├── test_google_tool_handler_fix.py
│ │ ├── test_guide_auto_hide.py
│ │ ├── test_import_fix.py
│ │ ├── test_online_tools_config.py
│ │ ├── test_real_scenario_fix.py
│ │ ├── test_tool_selection_logic.py
│ │ ├── test_tushare_direct.py
│ │ └── test_us_stock_independence.py
│ ├── FILE_ORGANIZATION_SUMMARY.md
│ ├── README.md
│ ├── __init__.py
│ ├── akshare_check_fixed.py
│ ├── akshare_isolated_test.py
│ ├── analyze_akshare_data.py
│ ├── check_key_metrics.py
│ ├── config/
│ │ ├── test_deprecations.py
│ │ ├── test_logging_config.py
│ │ ├── test_logging_json.py
│ │ └── test_settings.py
│ ├── conftest.py
│ ├── dataflows/
│ │ └── test_realtime_metrics.py
│ ├── debug_akshare_daily_basic.py
│ ├── debug_baostock_fields.py
│ ├── debug_baostock_stock_list.py
│ ├── debug_deepseek_cost.py
│ ├── debug_deepseek_cost_issue.py
│ ├── debug_full_flow.py
│ ├── debug_imports.py
│ ├── debug_test_execution.py
│ ├── debug_tool_binding_issue.py
│ ├── debug_web_issue.py
│ ├── demo_fallback_system.py
│ ├── final_gemini_test.py
│ ├── fundamentals_analyst_clean.py
│ ├── integration/
│ │ ├── __init__.py
│ │ └── test_dashscope_integration.py
│ ├── middleware/
│ │ └── test_trace_id.py
│ ├── pytest.ini
│ ├── quick_akshare_check.py
│ ├── quick_redis_test.py
│ ├── quick_test.py
│ ├── quick_test_hk.py
│ ├── services/
│ │ ├── test_quotes_backfill.py
│ │ ├── test_quotes_ingestion_and_enrichment.py
│ │ ├── test_scheduler_quotes_job.py
│ │ └── test_screening_roe_field.py
│ ├── simple_akshare_test.py
│ ├── simple_env_test.py
│ ├── system/
│ │ ├── test_config_summary.py
│ │ └── test_llm_provider_sanitization.py
│ ├── test_000002_valuation.py
│ ├── test_002027_specific.py
│ ├── test_300750_final.py
│ ├── test_agent_utils_tushare_fix.py
│ ├── test_akshare_alternative.py
│ ├── test_akshare_amount.py
│ ├── test_akshare_api.py
│ ├── test_akshare_code_format.py
│ ├── test_akshare_debug.py
│ ├── test_akshare_direct.py
│ ├── test_akshare_fixed.py
│ ├── test_akshare_functionality.py
│ ├── test_akshare_hk.py
│ ├── test_akshare_hk_apis.py
│ ├── test_akshare_performance.py
│ ├── test_akshare_priority.py
│ ├── test_akshare_priority_clean.py
│ ├── test_akshare_priority_fix.py
│ ├── test_all_analysts_hk_fix.py
│ ├── test_all_apis.py
│ ├── test_amount_fix.py
│ ├── test_amplitude_api.py
│ ├── test_analysis.py
│ ├── test_analysis_result.py
│ ├── test_analysis_with_apis.py
│ ├── test_analyst_loop_fix.py
│ ├── test_api_analysis.py
│ ├── test_api_format.py
│ ├── test_app_error_logging.py
│ ├── test_async_analysis.py
│ ├── test_asyncio_thread_pool_fix.py
│ ├── test_baostock_fixed.py
│ ├── test_baostock_quick.py
│ ├── test_baostock_stock_filter.py
│ ├── test_baostock_valuation.py
│ ├── test_batch_analysis_planA.py
│ ├── test_cache_optimization.py
│ ├── test_chinese_output.py
│ ├── test_cli_fix.py
│ ├── test_cli_hk.py
│ ├── test_cli_logging_fix.py
│ ├── test_cli_progress_display.py
│ ├── test_cli_version.py
│ ├── test_code_normalization.py
│ ├── test_complete_tool_workflow.py
│ ├── test_conditional_logic_config.py
│ ├── test_config_loading.py
│ ├── test_config_management.py
│ ├── test_config_system.py
│ ├── test_conversion.py
│ ├── test_correct_apis.py
│ ├── test_dashscope_adapter_fix.py
│ ├── test_dashscope_agent_friendly.py
│ ├── test_dashscope_openai_fix.py
│ ├── test_dashscope_quick_fix.py
│ ├── test_dashscope_simple_fix.py
│ ├── test_dashscope_token_tracking.py
│ ├── test_dashscope_tool_call_fix.py
│ ├── test_dashscope_tool_calling_fix.py
│ ├── test_data_config_cli.py
│ ├── test_data_consistency.py
│ ├── test_data_depth_levels.py
│ ├── test_data_sources_comprehensive.py
│ ├── test_data_sources_simple.py
│ ├── test_database_api.py
│ ├── test_dataframe_fix.py
│ ├── test_db_requirements_fix.py
│ ├── test_debate_flow_simulation.py
│ ├── test_decision_data.py
│ ├── test_deepseek_cost_calculation.py
│ ├── test_deepseek_cost_debug.py
│ ├── test_deepseek_cost_fix.py
│ ├── test_deepseek_integration.py
│ ├── test_deepseek_react_fix.py
│ ├── test_deepseek_token_tracking.py
│ ├── test_detailed_data_display.py
│ ├── test_detailed_progress_display.py
│ ├── test_documentation_consistency.py
│ ├── test_duplicate_progress_fix.py
│ ├── test_embedding_models.py
│ ├── test_enhanced_analysis_history.py
│ ├── test_enhanced_screening.py
│ ├── test_env_compatibility.py
│ ├── test_env_config.py
│ ├── test_existing_results.py
│ ├── test_field_config_api.py
│ ├── test_file_loading_debug.py
│ ├── test_final_config.py
│ ├── test_final_integration.py
│ ├── test_final_unified_architecture.py
│ ├── test_final_verification.py
│ ├── test_final_verification_with_config.py
│ ├── test_financial_data_validation.py
│ ├── test_financial_metrics_fix.py
│ ├── test_finnhub_connection.py
│ ├── test_finnhub_fundamentals.py
│ ├── test_finnhub_hk.py
│ ├── test_finnhub_news_fix.py
│ ├── test_fix.py
│ ├── test_fixed_analysis.py
│ ├── test_format_fix.py
│ ├── test_frontend_backend_integration.py
│ ├── test_frontend_display.py
│ ├── test_full_analysis_debug.py
│ ├── test_full_fundamentals_flow.py
│ ├── test_fundamentals_cache.py
│ ├── test_fundamentals_debug.py
│ ├── test_fundamentals_generation.py
│ ├── test_fundamentals_no_duplicate.py
│ ├── test_fundamentals_react_hk_fix.py
│ ├── test_fundamentals_tracking.py
│ ├── test_gemini_25_pro.py
│ ├── test_gemini_final.py
│ ├── test_gemini_simple.py
│ ├── test_google_memory_fix.py
│ ├── test_google_tool_handler_improvements.py
│ ├── test_graph_routing.py
│ ├── test_hk_apis_simple.py
│ ├── test_hk_data_source_fix.py
│ ├── test_hk_error_handling.py
│ ├── test_hk_fundamentals_final.py
│ ├── test_hk_fundamentals_fix.py
│ ├── test_hk_improved.py
│ ├── test_hk_priority.py
│ ├── test_hk_simple.py
│ ├── test_hk_simple_improved.py
│ ├── test_hk_stock_functionality.py
│ ├── test_import.py
│ ├── test_import_fix.py
│ ├── test_improved_hk_utils.py
│ ├── test_industries_api.py
│ ├── test_industry_screening_fix.py
│ ├── test_investment_advice_fix.py
│ ├── test_level3_deadlock_debug.py
│ ├── test_level3_fix.py
│ ├── test_llm_technical_analysis_debug.py
│ ├── test_llm_tool_call.py
│ ├── test_llm_tool_calling_comparison.py
│ ├── test_logging_fix.py
│ ├── test_login_api.py
│ ├── test_market_analyst_fix.py
│ ├── test_market_analyst_lookback.py
│ ├── test_middleware.py
│ ├── test_model_config.py
│ ├── test_mongodb_check.py
│ ├── test_mongodb_save.py
│ ├── test_news_analyst_fix.py
│ ├── test_news_analyst_integration.py
│ ├── test_news_filtering.py
│ ├── test_news_timeout_fix.py
│ ├── test_non_blocking.py
│ ├── test_notification_removal.py
│ ├── test_openai_config_fix.py
│ ├── test_operation_logs.py
│ ├── test_optimized_data_depth.py
│ ├── test_optimized_fundamentals.py
│ ├── test_optimized_fundamentals_simple.py
│ ├── test_optimized_prompts.py
│ ├── test_pb_calculation_fix.py
│ ├── test_performance_comparison.py
│ ├── test_profitable_stock.py
│ ├── test_progress.py
│ ├── test_progress_steps.py
│ ├── test_progress_time_calculation.py
│ ├── test_prompt_optimization_effect.py
│ ├── test_pydantic_fix.py
│ ├── test_pypandoc_functionality.py
│ ├── test_query.py
│ ├── test_quick_async.py
│ ├── test_quick_fix.py
│ ├── test_quotes_ingestion.py
│ ├── test_quotes_sync_status.py
│ ├── test_raw_data_display.py
│ ├── test_real_data_levels.py
│ ├── test_real_deepseek_cost.py
│ ├── test_real_estate_api.py
│ ├── test_real_volume_issue.py
│ ├── test_redis_performance.py
│ ├── test_reports_api.py
│ ├── test_reports_fix.py
│ ├── test_request_deduplication.py
│ ├── test_research_depth_5_levels.py
│ ├── test_research_depth_mapping.py
│ ├── test_risk_assessment.py
│ ├── test_sanitize_export.py
│ ├── test_sanitize_real_data.py
│ ├── test_screening_fields.py
│ ├── test_screening_fix.py
│ ├── test_server_config.py
│ ├── test_signal_processing_logging.py
│ ├── test_signal_processor_debug.py
│ ├── test_signal_processor_fix.py
│ ├── test_simple_depth_check.py
│ ├── test_simple_fundamentals.py
│ ├── test_simple_tracking.py
│ ├── test_smart_system.py
│ ├── test_sse_and_worker_config.py
│ ├── test_stock_code_tracking.py
│ ├── test_stock_codes.py
│ ├── test_stock_data_service.py
│ ├── test_stock_info_debug.py
│ ├── test_stock_market_identification.py
│ ├── test_summary_recommendation.py
│ ├── test_symbol_field_fix.py
│ ├── test_sync_control_functions.py
│ ├── test_sync_history_api.py
│ ├── test_sync_history_fix.py
│ ├── test_sync_user_feedback.py
│ ├── test_system_config_summary_sse_queue.py
│ ├── test_system_simple.py
│ ├── test_target_price.py
│ ├── test_time_estimation_display.py
│ ├── test_timezone_fix.py
│ ├── test_tool_binding_fix.py
│ ├── test_tool_call_issue.py
│ ├── test_tool_execution_flow.py
│ ├── test_tool_interception.py
│ ├── test_tool_removal.py
│ ├── test_tool_selection_debug.py
│ ├── test_toolkit_tools.py
│ ├── test_trading_time_logic.py
│ ├── test_tradingagents_runtime_settings.py
│ ├── test_tushare_integration.py
│ ├── test_tushare_unified/
│ │ ├── __init__.py
│ │ ├── test_tushare_provider.py
│ │ └── test_tushare_sync_service.py
│ ├── test_unified_architecture.py
│ ├── test_unified_config.py
│ ├── test_unified_fundamentals.py
│ ├── test_unified_news_tool.py
│ ├── test_us_stock_analysis.py
│ ├── test_user_check.py
│ ├── test_validation_fix.py
│ ├── test_valuation_check.py
│ ├── test_valuation_simple.py
│ ├── test_volume_format.html
│ ├── test_volume_mapping_issue.py
│ ├── test_vscode_config.py
│ ├── test_web_api_akshare.py
│ ├── test_web_config_page.py
│ ├── test_web_fix.py
│ ├── test_web_hk.py
│ ├── test_web_interface.py
│ ├── test_workflow_integration.py
│ ├── testgoogle.py
│ ├── tradingagents/
│ │ └── test_app_cache_toggle.py
│ ├── unit/
│ │ ├── dataflows/
│ │ │ └── test_unified_dataframe.py
│ │ ├── test_stocks_kline_news_api.py
│ │ └── tools/
│ │ └── analysis/
│ │ └── test_indicators_uil.py
│ ├── verify_config.py
│ └── verify_mongodb_data.py
├── tradingagents/
│ ├── __init__.py
│ ├── agents/
│ │ ├── __init__.py
│ │ ├── analysts/
│ │ │ ├── china_market_analyst.py
│ │ │ ├── fundamentals_analyst.py
│ │ │ ├── market_analyst.py
│ │ │ ├── news_analyst.py
│ │ │ └── social_media_analyst.py
│ │ ├── managers/
│ │ │ ├── research_manager.py
│ │ │ └── risk_manager.py
│ │ ├── researchers/
│ │ │ ├── bear_researcher.py
│ │ │ └── bull_researcher.py
│ │ ├── risk_mgmt/
│ │ │ ├── aggresive_debator.py
│ │ │ ├── conservative_debator.py
│ │ │ └── neutral_debator.py
│ │ ├── trader/
│ │ │ └── trader.py
│ │ └── utils/
│ │ ├── agent_states.py
│ │ ├── agent_utils.py
│ │ ├── chromadb_config.py
│ │ ├── google_tool_handler.py
│ │ └── memory.py
│ ├── api/
│ │ └── stock_api.py
│ ├── config/
│ │ ├── __init__.py
│ │ ├── config_manager.py
│ │ ├── database_config.py
│ │ ├── database_manager.py
│ │ ├── env_utils.py
│ │ ├── mongodb_storage.py
│ │ ├── providers_config.py
│ │ ├── runtime_settings.py
│ │ ├── tushare_config.py
│ │ └── usage_models.py
│ ├── constants/
│ │ ├── __init__.py
│ │ └── data_sources.py
│ ├── dataflows/
│ │ ├── README.md
│ │ ├── __init__.py
│ │ ├── _compat_imports.py
│ │ ├── cache/
│ │ │ ├── __init__.py
│ │ │ ├── adaptive.py
│ │ │ ├── app_adapter.py
│ │ │ ├── data_cache/
│ │ │ │ └── us_fundamentals/
│ │ │ │ └── 300750.SZ_fundamentals_a1cc6e9ff077.txt
│ │ │ ├── db_cache.py
│ │ │ ├── file_cache.py
│ │ │ ├── integrated.py
│ │ │ └── mongodb_cache_adapter.py
│ │ ├── data_completeness_checker.py
│ │ ├── data_source_manager.py
│ │ ├── interface.py
│ │ ├── news/
│ │ │ ├── __init__.py
│ │ │ ├── chinese_finance.py
│ │ │ ├── google_news.py
│ │ │ ├── realtime_news.py
│ │ │ └── reddit.py
│ │ ├── optimized_china_data.py
│ │ ├── providers/
│ │ │ ├── __init__.py
│ │ │ ├── base_provider.py
│ │ │ ├── china/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── akshare.py
│ │ │ │ ├── baostock.py
│ │ │ │ ├── fundamentals_snapshot.py
│ │ │ │ └── tushare.py
│ │ │ ├── examples/
│ │ │ │ ├── __init__.py
│ │ │ │ └── example_sdk.py
│ │ │ ├── hk/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── hk_stock.py
│ │ │ │ └── improved_hk.py
│ │ │ └── us/
│ │ │ ├── __init__.py
│ │ │ ├── alpha_vantage_common.py
│ │ │ ├── alpha_vantage_fundamentals.py
│ │ │ ├── alpha_vantage_news.py
│ │ │ ├── finnhub.py
│ │ │ ├── optimized.py
│ │ │ └── yfinance.py
│ │ ├── realtime_metrics.py
│ │ ├── realtime_news_utils.py
│ │ ├── stock_api.py
│ │ ├── stock_data_service.py
│ │ └── technical/
│ │ ├── __init__.py
│ │ └── stockstats.py
│ ├── default_config.py
│ ├── graph/
│ │ ├── __init__.py
│ │ ├── conditional_logic.py
│ │ ├── propagation.py
│ │ ├── reflection.py
│ │ ├── setup.py
│ │ ├── signal_processing.py
│ │ └── trading_graph.py
│ ├── llm_adapters/
│ │ ├── __init__.py
│ │ ├── dashscope_openai_adapter.py
│ │ ├── deepseek_adapter.py
│ │ ├── google_openai_adapter.py
│ │ └── openai_compatible_base.py
│ ├── models/
│ │ └── stock_data_models.py
│ ├── tools/
│ │ ├── analysis/
│ │ │ └── indicators.py
│ │ └── unified_news_tool.py
│ └── utils/
│ ├── dataflow_utils.py
│ ├── enhanced_news_filter.py
│ ├── enhanced_news_retriever.py
│ ├── logging_init.py
│ ├── logging_manager.py
│ ├── news_filter.py
│ ├── news_filter_integration.py
│ ├── stock_utils.py
│ ├── stock_validator.py
│ └── tool_logging.py
├── utils/
│ ├── check_version_consistency.py
│ ├── cleanup_unnecessary_dirs.py
│ ├── data_config.py
│ ├── fundamentals_analysis_fix.md
│ └── update_data_source_references.py
└── web/
├── CACHE_CLEANING_GUIDE.md
├── README.md
├── app.py
├── components/
│ ├── __init__.py
│ ├── analysis_form.py
│ ├── analysis_results.py
│ ├── async_progress_display.py
│ ├── header.py
│ ├── login.py
│ ├── operation_logs.py
│ ├── results_display.py
│ ├── sidebar.py
│ └── user_activity_dashboard.py
├── config/
│ └── USER_MANAGEMENT.md
├── modules/
│ ├── cache_management.py
│ ├── config_management.py
│ ├── database_management.py
│ └── token_statistics.py
├── run_web.py
└── utils/
├── __init__.py
├── analysis_runner.py
├── api_checker.py
├── async_progress_tracker.py
├── auth_manager.py
├── cookie_manager.py
├── docker_pdf_adapter.py
├── file_session_manager.py
├── mongodb_report_manager.py
├── persistence.py
├── progress_log_handler.py
├── progress_tracker.py
├── redis_session_manager.py
├── report_exporter.py
├── session_persistence.py
├── smart_session_manager.py
├── thread_tracker.py
├── ui_utils.py
└── user_activity_logger.py
Showing preview only (527K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (5851 symbols across 980 files)
FILE: app/__main__.py
function check_env_file (line 42) | def check_env_file():
function main (line 113) | def main():
FILE: app/constants/model_capabilities.py
class ModelCapabilityLevel (line 17) | class ModelCapabilityLevel(IntEnum):
class ModelRole (line 26) | class ModelRole(str, Enum):
class ModelFeature (line 33) | class ModelFeature(str, Enum):
function get_model_capability_badge (line 353) | def get_model_capability_badge(level: int) -> Dict[str, str]:
function get_role_badge (line 365) | def get_role_badge(role: ModelRole) -> Dict[str, str]:
function get_feature_badge (line 375) | def get_feature_badge(feature: ModelFeature) -> Dict[str, str]:
function is_aggregator_model (line 431) | def is_aggregator_model(model_name: str) -> bool:
function parse_aggregator_model (line 444) | def parse_aggregator_model(model_name: str) -> Tuple[str, str]:
FILE: app/core/config.py
class Settings (line 22) | class Settings(BaseSettings):
method MONGO_URI (line 45) | def MONGO_URI(self) -> str:
method MONGO_DB (line 53) | def MONGO_DB(self) -> str:
method REDIS_URL (line 66) | def REDIS_URL(self) -> str:
method log_dir (line 257) | def log_dir(self) -> str:
method is_production (line 280) | def is_production(self) -> bool:
function get_settings (line 299) | def get_settings() -> Settings:
FILE: app/core/config_bridge.py
function bridge_config_to_env (line 15) | def bridge_config_to_env():
function _bridge_datasource_details (line 295) | def _bridge_datasource_details(data_source_configs) -> int:
function _bridge_system_settings (line 355) | def _bridge_system_settings() -> int:
function get_bridged_api_key (line 494) | def get_bridged_api_key(provider: str) -> Optional[str]:
function get_bridged_model (line 508) | def get_bridged_model(model_type: str = "default") -> Optional[str]:
function clear_bridged_config (line 526) | def clear_bridged_config():
function reload_bridged_config (line 580) | def reload_bridged_config():
function _sync_pricing_config (line 591) | def _sync_pricing_config(llm_configs):
function sync_pricing_config_now (line 630) | def sync_pricing_config_now():
function _handle_sync_task_result (line 659) | def _handle_sync_task_result(task):
function _sync_pricing_config_from_db (line 669) | async def _sync_pricing_config_from_db():
FILE: app/core/config_compat.py
class ConfigManagerCompat (line 19) | class ConfigManagerCompat:
method __init__ (line 26) | def __init__(self):
method _emit_deprecation_warning (line 31) | def _emit_deprecation_warning(self):
method get_data_dir (line 43) | def get_data_dir(self) -> str:
method load_settings (line 58) | def load_settings(self) -> Dict[str, Any]:
method save_settings (line 84) | def save_settings(self, settings_dict: Dict[str, Any]) -> bool:
method get_models (line 111) | def get_models(self) -> List[Dict[str, Any]]:
method get_model_config (line 144) | def get_model_config(self, provider: str, model_name: str) -> Optional...
method _get_default_settings (line 161) | def _get_default_settings(self) -> Dict[str, Any]:
class TokenTrackerCompat (line 179) | class TokenTrackerCompat:
method __init__ (line 186) | def __init__(self):
method track_usage (line 190) | def track_usage(
method get_usage_summary (line 228) | def get_usage_summary(self) -> Dict[str, Any]:
method reset_usage (line 237) | def reset_usage(self):
function get_config_manager (line 248) | def get_config_manager() -> ConfigManagerCompat:
function get_token_tracker (line 258) | def get_token_tracker() -> TokenTrackerCompat:
FILE: app/core/database.py
class DatabaseManager (line 30) | class DatabaseManager:
method __init__ (line 33) | def __init__(self):
method init_mongodb (line 41) | async def init_mongodb(self):
method init_redis (line 74) | async def init_redis(self):
method close_connections (line 104) | async def close_connections(self):
method health_check (line 134) | async def health_check(self) -> dict:
method is_healthy (line 180) | def is_healthy(self) -> bool:
function init_database (line 189) | async def init_database():
function init_database_views_and_indexes (line 214) | async def init_database_views_and_indexes():
function create_stock_screening_view (line 232) | async def create_stock_screening_view(db):
function create_database_indexes (line 347) | async def create_database_indexes(db):
function close_database (line 371) | async def close_database():
function get_mongo_client (line 384) | def get_mongo_client() -> AsyncIOMotorClient:
function get_mongo_db (line 391) | def get_mongo_db() -> AsyncIOMotorDatabase:
function get_mongo_db_sync (line 398) | def get_mongo_db_sync() -> Database:
function get_redis_client (line 422) | def get_redis_client() -> Redis:
function get_database_health (line 429) | async def get_database_health() -> dict:
function get_database (line 439) | def get_database():
FILE: app/core/dev_config.py
class DevConfig (line 10) | class DevConfig:
method get_uvicorn_config (line 100) | def get_uvicorn_config(cls, debug: bool = True) -> dict:
method setup_logging (line 112) | def setup_logging(cls, debug: bool = True):
FILE: app/core/logging_config.py
function resolve_logging_cfg_path (line 31) | def resolve_logging_cfg_path() -> Path:
class SimpleJsonFormatter (line 41) | class SimpleJsonFormatter(logging.Formatter):
method format (line 43) | def format(self, record: logging.LogRecord) -> str:
function _parse_size (line 55) | def _parse_size(size_str: str) -> int:
function setup_logging (line 66) | def setup_logging(log_level: str = "INFO"):
FILE: app/core/logging_context.py
class LoggingContextFilter (line 8) | class LoggingContextFilter(logging.Filter):
method filter (line 13) | def filter(self, record: logging.LogRecord) -> bool:
FILE: app/core/rate_limiter.py
class RateLimiter (line 14) | class RateLimiter:
method __init__ (line 21) | def __init__(self, max_calls: int, time_window: float, name: str = "Ra...
method acquire (line 43) | async def acquire(self):
method get_stats (line 79) | def get_stats(self) -> dict:
method reset_stats (line 92) | def reset_stats(self):
class TushareRateLimiter (line 100) | class TushareRateLimiter(RateLimiter):
method __init__ (line 116) | def __init__(self, tier: str = "standard", safety_margin: float = 0.8):
class AKShareRateLimiter (line 147) | class AKShareRateLimiter(RateLimiter):
method __init__ (line 154) | def __init__(self, max_calls: int = 60, time_window: float = 60):
class BaoStockRateLimiter (line 169) | class BaoStockRateLimiter(RateLimiter):
method __init__ (line 176) | def __init__(self, max_calls: int = 100, time_window: float = 60):
function get_tushare_rate_limiter (line 197) | def get_tushare_rate_limiter(tier: str = "standard", safety_margin: floa...
function get_akshare_rate_limiter (line 205) | def get_akshare_rate_limiter() -> AKShareRateLimiter:
function get_baostock_rate_limiter (line 213) | def get_baostock_rate_limiter() -> BaoStockRateLimiter:
function reset_all_limiters (line 221) | def reset_all_limiters():
FILE: app/core/redis_client.py
function init_redis (line 17) | async def init_redis():
function close_redis (line 49) | async def close_redis():
function get_redis (line 63) | def get_redis() -> redis.Redis:
class RedisKeys (line 70) | class RedisKeys:
class RedisService (line 104) | class RedisService:
method __init__ (line 107) | def __init__(self):
method set_with_ttl (line 110) | async def set_with_ttl(self, key: str, value: str, ttl: int = 3600):
method get_json (line 114) | async def get_json(self, key: str):
method set_json (line 122) | async def set_json(self, key: str, value: dict, ttl: int = None):
method increment_with_ttl (line 131) | async def increment_with_ttl(self, key: str, ttl: int = 3600):
method add_to_queue (line 139) | async def add_to_queue(self, queue_key: str, item: dict):
method pop_from_queue (line 144) | async def pop_from_queue(self, queue_key: str, timeout: int = 1):
method get_queue_length (line 152) | async def get_queue_length(self, queue_key: str):
method add_to_set (line 156) | async def add_to_set(self, set_key: str, value: str):
method remove_from_set (line 160) | async def remove_from_set(self, set_key: str, value: str):
method is_in_set (line 164) | async def is_in_set(self, set_key: str, value: str):
method get_set_size (line 168) | async def get_set_size(self, set_key: str):
method acquire_lock (line 172) | async def acquire_lock(self, lock_key: str, timeout: int = 30):
method release_lock (line 181) | async def release_lock(self, lock_key: str, lock_value: str):
function get_redis_service (line 197) | def get_redis_service() -> RedisService:
FILE: app/core/response.py
function ok (line 9) | def ok(data: Any = None, message: str = "ok") -> Dict[str, Any]:
function fail (line 21) | def fail(message: str = "error", code: int = 500, data: Any = None) -> D...
FILE: app/core/startup_validator.py
class ConfigLevel (line 16) | class ConfigLevel(Enum):
class ConfigItem (line 24) | class ConfigItem:
class ValidationResult (line 35) | class ValidationResult:
class StartupValidator (line 44) | class StartupValidator:
method __init__ (line 115) | def __init__(self):
method _is_valid_api_key (line 124) | def _is_valid_api_key(self, api_key: str) -> bool:
method validate (line 158) | def validate(self) -> ValidationResult:
method _validate_required_configs (line 184) | def _validate_required_configs(self):
method _validate_recommended_configs (line 198) | def _validate_recommended_configs(self):
method _check_security_configs (line 213) | def _check_security_configs(self):
method _print_validation_result (line 236) | def _print_validation_result(self):
method raise_if_failed (line 289) | def raise_if_failed(self):
class ConfigurationError (line 310) | class ConfigurationError(Exception):
function validate_startup_config (line 315) | def validate_startup_config() -> ValidationResult:
FILE: app/core/unified_config.py
class ConfigPaths (line 21) | class ConfigPaths:
class UnifiedConfigManager (line 34) | class UnifiedConfigManager:
method __init__ (line 37) | def __init__(self):
method _get_file_mtime (line 42) | def _get_file_mtime(self, file_path: Path) -> float:
method _is_cache_valid (line 49) | def _is_cache_valid(self, cache_key: str, file_path: Path) -> bool:
method _load_json_file (line 59) | def _load_json_file(self, file_path: Path, cache_key: str = None) -> D...
method _save_json_file (line 79) | def _save_json_file(self, file_path: Path, data: Dict[str, Any], cache...
method get_legacy_models (line 93) | def get_legacy_models(self) -> List[Dict[str, Any]]:
method get_llm_configs (line 97) | def get_llm_configs(self) -> List[LLMConfig]:
method save_llm_config (line 125) | def save_llm_config(self, llm_config: LLMConfig) -> bool:
method get_system_settings (line 163) | def get_system_settings(self) -> Dict[str, Any]:
method save_system_settings (line 167) | def save_system_settings(self, settings: Dict[str, Any]) -> bool:
method get_default_model (line 226) | def get_default_model(self) -> str:
method set_default_model (line 232) | def set_default_model(self, model_name: str) -> bool:
method get_quick_analysis_model (line 238) | def get_quick_analysis_model(self) -> str:
method get_deep_analysis_model (line 244) | def get_deep_analysis_model(self) -> str:
method set_analysis_models (line 250) | def set_analysis_models(self, quick_model: str, deep_model: str) -> bool:
method get_data_source_configs (line 259) | def get_data_source_configs(self) -> List[DataSourceConfig]:
method get_data_source_configs_async (line 327) | async def get_data_source_configs_async(self) -> List[DataSourceConfig]:
method get_database_configs (line 408) | def get_database_configs(self) -> List[DatabaseConfig]:
method get_unified_system_config (line 440) | async def get_unified_system_config(self) -> SystemConfig:
method sync_to_legacy_format (line 466) | def sync_to_legacy_format(self, system_config: SystemConfig) -> bool:
FILE: app/main.py
function get_version (line 74) | def get_version() -> str:
function _print_config_summary (line 85) | async def _print_config_summary(logger):
function lifespan (line 216) | async def lifespan(app: FastAPI):
function log_requests (line 636) | async def log_requests(request: Request, call_next):
function global_exception_handler (line 664) | async def global_exception_handler(request: Request, exc: Exception):
function test_log (line 680) | async def test_log():
function root (line 734) | async def root():
FILE: app/middleware/error_handler.py
class ErrorHandlerMiddleware (line 15) | class ErrorHandlerMiddleware(BaseHTTPMiddleware):
method dispatch (line 18) | async def dispatch(self, request: Request, call_next: Callable) -> Res...
method handle_error (line 25) | async def handle_error(self, request: Request, exc: Exception) -> JSON...
FILE: app/middleware/operation_log_middleware.py
function set_operation_log_enabled (line 21) | def set_operation_log_enabled(flag: bool) -> None:
class OperationLogMiddleware (line 27) | class OperationLogMiddleware(BaseHTTPMiddleware):
method __init__ (line 30) | def __init__(self, app, skip_paths: Optional[list] = None):
method dispatch (line 57) | async def dispatch(self, request: Request, call_next):
method _should_skip_logging (line 98) | def _should_skip_logging(self, request: Request) -> bool:
method _get_client_ip (line 121) | def _get_client_ip(self, request: Request) -> str:
method _get_user_info (line 138) | async def _get_user_info(self, request: Request) -> Optional[Dict[str,...
method _get_action_type (line 169) | def _get_action_type(self, path: str) -> str:
method _get_action_description (line 177) | def _get_action_description(self, method: str, path: str, request: Req...
method _log_operation (line 230) | async def _log_operation(
function manual_log_operation (line 283) | async def manual_log_operation(
FILE: app/middleware/rate_limit.py
class RateLimitMiddleware (line 15) | class RateLimitMiddleware(BaseHTTPMiddleware):
method __init__ (line 18) | def __init__(self, app, default_rate_limit: int = 100):
method dispatch (line 31) | async def dispatch(self, request: Request, call_next: Callable) -> Res...
method check_rate_limit (line 53) | async def check_rate_limit(self, user_id: str, endpoint: str):
class QuotaMiddleware (line 98) | class QuotaMiddleware(BaseHTTPMiddleware):
method __init__ (line 101) | def __init__(self, app, daily_quota: int = 1000):
method dispatch (line 112) | async def dispatch(self, request: Request, call_next: Callable) -> Res...
method check_daily_quota (line 134) | async def check_daily_quota(self, user_id: str):
FILE: app/middleware/request_id.py
class RequestIDMiddleware (line 19) | class RequestIDMiddleware(BaseHTTPMiddleware):
method dispatch (line 22) | async def dispatch(self, request: Request, call_next: Callable) -> Res...
FILE: app/models/analysis.py
class AnalysisStatus (line 14) | class AnalysisStatus(str, Enum):
class BatchStatus (line 23) | class BatchStatus(str, Enum):
class AnalysisParameters (line 33) | class AnalysisParameters(BaseModel):
class AnalysisResult (line 56) | class AnalysisResult(BaseModel):
class AnalysisTask (line 72) | class AnalysisTask(BaseModel):
class AnalysisBatch (line 106) | class AnalysisBatch(BaseModel):
class StockInfo (line 139) | class StockInfo(BaseModel):
class SingleAnalysisRequest (line 154) | class SingleAnalysisRequest(BaseModel):
method get_symbol (line 160) | def get_symbol(self) -> str:
class BatchAnalysisRequest (line 165) | class BatchAnalysisRequest(BaseModel):
method get_symbols (line 173) | def get_symbols(self) -> List[str]:
class AnalysisTaskResponse (line 178) | class AnalysisTaskResponse(BaseModel):
method serialize_datetime (line 193) | def serialize_datetime(self, dt: Optional[datetime], _info) -> Optiona...
class AnalysisBatchResponse (line 200) | class AnalysisBatchResponse(BaseModel):
method serialize_datetime (line 216) | def serialize_datetime(self, dt: Optional[datetime], _info) -> Optiona...
class AnalysisHistoryQuery (line 223) | class AnalysisHistoryQuery(BaseModel):
method get_symbol (line 234) | def get_symbol(self) -> Optional[str]:
FILE: app/models/config.py
class ModelProvider (line 14) | class ModelProvider(str, Enum):
class LLMProvider (line 42) | class LLMProvider(BaseModel):
class ModelInfo (line 69) | class ModelInfo(BaseModel):
class ModelCatalog (line 88) | class ModelCatalog(BaseModel):
class LLMProviderRequest (line 100) | class LLMProviderRequest(BaseModel):
class LLMProviderResponse (line 121) | class LLMProviderResponse(BaseModel):
class DataSourceType (line 146) | class DataSourceType(str, Enum):
class DatabaseType (line 177) | class DatabaseType(str, Enum):
class LLMConfig (line 186) | class LLMConfig(BaseModel):
class DataSourceConfig (line 237) | class DataSourceConfig(BaseModel):
class DatabaseConfig (line 258) | class DatabaseConfig(BaseModel):
class MarketCategory (line 274) | class MarketCategory(BaseModel):
class DataSourceGrouping (line 286) | class DataSourceGrouping(BaseModel):
class UsageRecord (line 296) | class UsageRecord(BaseModel):
class UsageStatistics (line 311) | class UsageStatistics(BaseModel):
class SystemConfig (line 323) | class SystemConfig(BaseModel):
class LLMConfigRequest (line 356) | class LLMConfigRequest(BaseModel):
class DataSourceConfigRequest (line 389) | class DataSourceConfigRequest(BaseModel):
class MarketCategoryRequest (line 408) | class MarketCategoryRequest(BaseModel):
class DataSourceGroupingRequest (line 418) | class DataSourceGroupingRequest(BaseModel):
class DataSourceOrderRequest (line 426) | class DataSourceOrderRequest(BaseModel):
class DatabaseConfigRequest (line 431) | class DatabaseConfigRequest(BaseModel):
class SystemConfigResponse (line 447) | class SystemConfigResponse(BaseModel):
method serialize_datetime (line 463) | def serialize_datetime(self, dt: Optional[datetime], _info) -> Optiona...
class ConfigTestRequest (line 470) | class ConfigTestRequest(BaseModel):
class ConfigTestResponse (line 476) | class ConfigTestResponse(BaseModel):
FILE: app/models/notification.py
function to_str_id (line 12) | def to_str_id(v: Any) -> str:
class NotificationCreate (line 25) | class NotificationCreate(BaseModel):
class NotificationDB (line 36) | class NotificationDB(BaseModel):
class NotificationOut (line 50) | class NotificationOut(BaseModel):
method serialize_datetime (line 61) | def serialize_datetime(self, dt: Optional[datetime], _info) -> Optiona...
class NotificationList (line 68) | class NotificationList(BaseModel):
FILE: app/models/operation_log.py
class OperationLogCreate (line 11) | class OperationLogCreate(BaseModel):
class OperationLogResponse (line 24) | class OperationLogResponse(BaseModel):
method serialize_datetime (line 42) | def serialize_datetime(self, dt: datetime, _info) -> Optional[str]:
class OperationLogQuery (line 49) | class OperationLogQuery(BaseModel):
class OperationLogListResponse (line 61) | class OperationLogListResponse(BaseModel):
class OperationLogStats (line 68) | class OperationLogStats(BaseModel):
class OperationLogStatsResponse (line 78) | class OperationLogStatsResponse(BaseModel):
class ClearLogsRequest (line 85) | class ClearLogsRequest(BaseModel):
class ClearLogsResponse (line 91) | class ClearLogsResponse(BaseModel):
class ActionType (line 99) | class ActionType:
function convert_objectid_to_str (line 132) | def convert_objectid_to_str(doc: Dict[str, Any]) -> Dict[str, Any]:
FILE: app/models/screening.py
class OperatorType (line 10) | class OperatorType(str, Enum):
class FieldType (line 26) | class FieldType(str, Enum):
class ScreeningCondition (line 33) | class ScreeningCondition(BaseModel):
class Config (line 40) | class Config:
class ScreeningRequest (line 44) | class ScreeningRequest(BaseModel):
class ScreeningResponse (line 62) | class ScreeningResponse(BaseModel):
class FieldInfo (line 71) | class FieldInfo(BaseModel):
class FieldStatistics (line 92) | class FieldStatistics(BaseModel):
FILE: app/models/stock_models.py
function to_str_id (line 11) | def to_str_id(v: Any) -> str:
class MarketInfo (line 28) | class MarketInfo(BaseModel):
class TechnicalIndicators (line 38) | class TechnicalIndicators(BaseModel):
class StockBasicInfoExtended (line 54) | class StockBasicInfoExtended(BaseModel):
class Config (line 119) | class Config:
class MarketQuotesExtended (line 153) | class MarketQuotesExtended(BaseModel):
class Config (line 197) | class Config:
class StockBasicInfoResponse (line 224) | class StockBasicInfoResponse(BaseModel):
class MarketQuotesResponse (line 231) | class MarketQuotesResponse(BaseModel):
class StockListResponse (line 238) | class StockListResponse(BaseModel):
FILE: app/models/user.py
function validate_object_id (line 14) | def validate_object_id(v: Any) -> ObjectId:
function serialize_object_id (line 24) | def serialize_object_id(v: ObjectId) -> str:
class UserPreferences (line 37) | class UserPreferences(BaseModel):
class FavoriteStock (line 61) | class FavoriteStock(BaseModel):
class User (line 73) | class User(BaseModel):
class UserCreate (line 102) | class UserCreate(BaseModel):
class UserUpdate (line 109) | class UserUpdate(BaseModel):
class UserResponse (line 117) | class UserResponse(BaseModel):
method serialize_datetime (line 134) | def serialize_datetime(self, dt: Optional[datetime], _info) -> Optiona...
class UserLogin (line 141) | class UserLogin(BaseModel):
class UserSession (line 147) | class UserSession(BaseModel):
method serialize_datetime (line 158) | def serialize_datetime(self, dt: Optional[datetime], _info) -> Optiona...
class TokenResponse (line 165) | class TokenResponse(BaseModel):
FILE: app/routers/akshare_init.py
class InitializationRequest (line 33) | class InitializationRequest(BaseModel):
class SyncRequest (line 40) | class SyncRequest(BaseModel):
function get_database_status (line 47) | async def get_database_status():
function test_akshare_connection (line 109) | async def test_akshare_connection():
function start_full_initialization (line 146) | async def start_full_initialization(
function start_basic_sync (line 204) | async def start_basic_sync(
function get_initialization_status (line 260) | async def get_initialization_status():
function stop_initialization (line 287) | async def stop_initialization(current_user: dict = Depends(get_current_u...
function _run_full_initialization_background (line 326) | async def _run_full_initialization_background(historical_days: int, forc...
function _run_basic_sync_background (line 352) | async def _run_basic_sync_background(force_update: bool):
FILE: app/routers/analysis.py
class SingleAnalyzeRequest (line 29) | class SingleAnalyzeRequest(BaseModel):
class BatchAnalyzeRequest (line 33) | class BatchAnalyzeRequest(BaseModel):
function submit_single_analysis (line 41) | async def submit_single_analysis(
function test_route (line 100) | async def test_route():
function get_task_status_new (line 106) | async def get_task_status_new(
function get_task_result (line 222) | async def get_task_result(
function list_all_tasks (line 707) | async def list_all_tasks(
function list_user_tasks (line 739) | async def list_user_tasks(
function submit_batch_analysis (line 772) | async def submit_batch_analysis(
function analyze_single (line 876) | async def analyze_single(
function analyze_batch (line 893) | async def analyze_batch(
function get_batch (line 910) | async def get_batch(batch_id: str, user: dict = Depends(get_current_user...
function cancel_task (line 949) | async def cancel_task(
function get_user_queue_status (line 970) | async def get_user_queue_status(
function get_user_analysis_history (line 985) | async def get_user_analysis_history(
function websocket_task_progress (line 1063) | async def websocket_task_progress(websocket: WebSocket, task_id: str):
function get_task_details (line 1100) | async def get_task_details(
function get_zombie_tasks (line 1115) | async def get_zombie_tasks(
function cleanup_zombie_tasks (line 1143) | async def cleanup_zombie_tasks(
function mark_task_as_failed (line 1170) | async def mark_task_as_failed(
function delete_task (line 1225) | async def delete_task(
FILE: app/routers/auth_db.py
function get_logger (line 24) | def get_logger(name: str) -> logging.Logger:
class ApiResponse (line 30) | class ApiResponse(BaseModel):
class LoginRequest (line 37) | class LoginRequest(BaseModel):
class LoginResponse (line 41) | class LoginResponse(BaseModel):
class RefreshTokenRequest (line 47) | class RefreshTokenRequest(BaseModel):
class RefreshTokenResponse (line 50) | class RefreshTokenResponse(BaseModel):
class ChangePasswordRequest (line 55) | class ChangePasswordRequest(BaseModel):
class ResetPasswordRequest (line 59) | class ResetPasswordRequest(BaseModel):
class CreateUserRequest (line 63) | class CreateUserRequest(BaseModel):
function get_current_user (line 69) | async def get_current_user(authorization: Optional[str] = Header(default...
function login (line 117) | async def login(payload: LoginRequest, request: Request):
function refresh_token (line 220) | async def refresh_token(payload: RefreshTokenRequest):
function logout (line 268) | async def logout(request: Request, user: dict = Depends(get_current_user)):
function me (line 304) | async def me(user: dict = Depends(get_current_user)):
function update_me (line 313) | async def update_me(
function change_password (line 370) | async def change_password(
function reset_password (line 399) | async def reset_password(
function create_user (line 428) | async def create_user(
function list_users (line 479) | async def list_users(
FILE: app/routers/baostock_init.py
class InitializationRequest (line 30) | class InitializationRequest(BaseModel):
class InitializationResponse (line 36) | class InitializationResponse(BaseModel):
function get_database_status (line 45) | async def get_database_status():
function test_baostock_connection (line 63) | async def test_baostock_connection():
function start_full_initialization (line 84) | async def start_full_initialization(
function start_basic_initialization (line 136) | async def start_basic_initialization(background_tasks: BackgroundTasks):
function get_initialization_status (line 178) | async def get_initialization_status():
function stop_initialization (line 222) | async def stop_initialization():
function _run_full_initialization_task (line 252) | async def _run_full_initialization_task(historical_days: int, force: boo...
function _run_basic_initialization_task (line 285) | async def _run_basic_initialization_task(task_id: str):
function get_service_status (line 316) | async def get_service_status():
FILE: app/routers/cache.py
function get_cache_stats (line 19) | async def get_cache_stats(current_user: dict = Depends(get_current_user)):
function cleanup_old_cache (line 57) | async def cleanup_old_cache(
function clear_all_cache (line 94) | async def clear_all_cache(current_user: dict = Depends(get_current_user)):
function get_cache_details (line 126) | async def get_cache_details(
function get_cache_backend_info (line 175) | async def get_cache_backend_info(current_user: dict = Depends(get_curren...
FILE: app/routers/config.py
function reload_config (line 38) | async def reload_config(current_user: dict = Depends(get_current_user)):
function _sanitize_llm_configs (line 83) | def _sanitize_llm_configs(items):
function _sanitize_datasource_configs (line 89) | def _sanitize_datasource_configs(items):
function _sanitize_database_configs (line 141) | def _sanitize_database_configs(items):
function _sanitize_kv (line 147) | def _sanitize_kv(d: Dict[str, Any]) -> Dict[str, Any]:
class SetDefaultRequest (line 166) | class SetDefaultRequest(BaseModel):
function get_system_config (line 172) | async def get_system_config(
function get_llm_providers (line 208) | async def get_llm_providers(
function add_llm_provider (line 279) | async def add_llm_provider(
function update_llm_provider (line 316) | async def update_llm_provider(
function delete_llm_provider (line 380) | async def delete_llm_provider(
function toggle_llm_provider (line 421) | async def toggle_llm_provider(
function fetch_provider_models (line 464) | async def fetch_provider_models(
function migrate_env_to_providers (line 485) | async def migrate_env_to_providers(
function init_aggregator_providers (line 523) | async def init_aggregator_providers(
function test_provider_api (line 562) | async def test_provider_api(
function add_llm_config (line 583) | async def add_llm_config(
function add_data_source_config (line 692) | async def add_data_source_config(
function add_database_config (line 795) | async def add_database_config(
function test_config (line 847) | async def test_config(
function test_saved_database_config (line 879) | async def test_saved_database_config(
function get_llm_configs (line 928) | async def get_llm_configs(
function delete_llm_config (line 970) | async def delete_llm_config(
function set_default_llm (line 1021) | async def set_default_llm(
function get_data_source_configs (line 1057) | async def get_data_source_configs(
function update_data_source_config (line 1074) | async def update_data_source_config(
function delete_data_source_config (line 1284) | async def delete_data_source_config(
function get_market_categories (line 1340) | async def get_market_categories(
function add_market_category (line 1355) | async def add_market_category(
function update_market_category (line 1393) | async def update_market_category(
function delete_market_category (line 1431) | async def delete_market_category(
function get_datasource_groupings (line 1470) | async def get_datasource_groupings(
function add_datasource_to_category (line 1485) | async def add_datasource_to_category(
function remove_datasource_from_category (line 1523) | async def remove_datasource_from_category(
function update_datasource_grouping (line 1561) | async def update_datasource_grouping(
function update_category_datasource_order (line 1600) | async def update_category_datasource_order(
function set_default_data_source (line 1638) | async def set_default_data_source(
function get_system_settings (line 1674) | async def get_system_settings(
function get_system_settings_meta (line 1689) | async def get_system_settings_meta(
function update_system_settings (line 1709) | async def update_system_settings(
function export_config (line 1774) | async def export_config(
function import_config (line 1805) | async def import_config(
function migrate_legacy_config (line 1841) | async def migrate_legacy_config(
function set_default_llm (line 1876) | async def set_default_llm(
function set_default_data_source (line 1914) | async def set_default_data_source(
function get_available_models (line 1952) | async def get_available_models(
function get_model_catalog (line 1969) | async def get_model_catalog(
function get_provider_model_catalog (line 1984) | async def get_provider_model_catalog(
class ModelCatalogRequest (line 2006) | class ModelCatalogRequest(BaseModel):
function save_model_catalog (line 2014) | async def save_model_catalog(
function delete_model_catalog (line 2064) | async def delete_model_catalog(
function init_model_catalog (line 2097) | async def init_model_catalog(
function get_database_configs (line 2122) | async def get_database_configs(
function get_database_config (line 2140) | async def get_database_config(
function add_database_config (line 2167) | async def add_database_config(
function update_database_config (line 2209) | async def update_database_config(
function delete_database_config (line 2259) | async def delete_database_config(
FILE: app/routers/database.py
class BackupRequest (line 22) | class BackupRequest(BaseModel):
class ImportRequest (line 27) | class ImportRequest(BaseModel):
class ExportRequest (line 33) | class ExportRequest(BaseModel):
class DatabaseStatusResponse (line 40) | class DatabaseStatusResponse(BaseModel):
class DatabaseStatsResponse (line 45) | class DatabaseStatsResponse(BaseModel):
class BackupResponse (line 52) | class BackupResponse(BaseModel):
function get_database_status (line 64) | async def get_database_status(
function get_database_stats (line 84) | async def get_database_stats(
function test_database_connections (line 104) | async def test_database_connections(
function create_backup (line 124) | async def create_backup(
function list_backups (line 149) | async def list_backups(
function import_data (line 168) | async def import_data(
function export_data (line 211) | async def export_data(
function delete_backup (line 239) | async def delete_backup(
function cleanup_old_data (line 259) | async def cleanup_old_data(
function cleanup_analysis_results (line 280) | async def cleanup_analysis_results(
function cleanup_operation_logs (line 301) | async def cleanup_operation_logs(
FILE: app/routers/favorites.py
class AddFavoriteRequest (line 20) | class AddFavoriteRequest(BaseModel):
class UpdateFavoriteRequest (line 31) | class UpdateFavoriteRequest(BaseModel):
class FavoriteStockResponse (line 39) | class FavoriteStockResponse(BaseModel):
function get_favorites (line 56) | async def get_favorites(
function add_favorite (line 71) | async def add_favorite(
function update_favorite (line 128) | async def update_favorite(
function remove_favorite (line 162) | async def remove_favorite(
function check_favorite (line 188) | async def check_favorite(
function get_user_tags (line 204) | async def get_user_tags(
class SyncFavoritesRequest (line 218) | class SyncFavoritesRequest(BaseModel):
function sync_favorites_realtime (line 224) | async def sync_favorites_realtime(
FILE: app/routers/financial_data.py
class FinancialSyncRequest (line 22) | class FinancialSyncRequest(BaseModel):
class SingleStockSyncRequest (line 37) | class SingleStockSyncRequest(BaseModel):
function query_financial_data (line 50) | async def query_financial_data(
function get_latest_financial_data (line 91) | async def get_latest_financial_data(
function get_financial_statistics (line 124) | async def get_financial_statistics() -> dict:
function start_financial_sync (line 148) | async def start_financial_sync(
function sync_single_stock_financial (line 184) | async def sync_single_stock_financial(
function get_sync_statistics (line 221) | async def get_sync_statistics() -> dict:
function health_check (line 242) | async def health_check() -> dict:
function _execute_financial_sync (line 277) | async def _execute_financial_sync(
FILE: app/routers/health.py
function get_version (line 8) | def get_version() -> str:
function health (line 20) | async def health():
function healthz (line 34) | async def healthz():
function readyz (line 39) | async def readyz():
FILE: app/routers/historical_data.py
class HistoricalDataQuery (line 19) | class HistoricalDataQuery(BaseModel):
class HistoricalDataResponse (line 29) | class HistoricalDataResponse(BaseModel):
function get_historical_data (line 37) | async def get_historical_data(
function query_historical_data (line 95) | async def query_historical_data(request: HistoricalDataQuery):
function get_latest_date (line 132) | async def get_latest_date(
function get_data_statistics (line 157) | async def get_data_statistics():
function compare_data_sources (line 175) | async def compare_data_sources(
function health_check (line 220) | async def health_check():
FILE: app/routers/internal_messages.py
class InternalMessage (line 20) | class InternalMessage(BaseModel):
class InternalMessageBatchRequest (line 50) | class InternalMessageBatchRequest(BaseModel):
class InternalMessageQueryRequest (line 56) | class InternalMessageQueryRequest(BaseModel):
function save_internal_messages (line 78) | async def save_internal_messages(request: InternalMessageBatchRequest):
function query_internal_messages (line 102) | async def query_internal_messages(request: InternalMessageQueryRequest):
function get_latest_messages (line 144) | async def get_latest_messages(
function search_messages (line 170) | async def search_messages(
function get_research_reports (line 196) | async def get_research_reports(
function get_analyst_notes (line 220) | async def get_analyst_notes(
function get_statistics (line 244) | async def get_statistics(
function get_message_types (line 275) | async def get_message_types():
function get_categories (line 314) | async def get_categories():
function health_check (line 348) | async def health_check():
FILE: app/routers/logs.py
class LogReadRequest (line 20) | class LogReadRequest(BaseModel):
class LogExportRequest (line 30) | class LogExportRequest(BaseModel):
class LogFileInfo (line 40) | class LogFileInfo(BaseModel):
class LogContentResponse (line 50) | class LogContentResponse(BaseModel):
class LogStatisticsResponse (line 57) | class LogStatisticsResponse(BaseModel):
function list_log_files (line 67) | async def list_log_files(
function read_log_file (line 89) | async def read_log_file(
function export_logs (line 125) | async def export_logs(
function get_log_statistics (line 173) | async def get_log_statistics(
function delete_log_file (line 200) | async def delete_log_file(
FILE: app/routers/model_capabilities.py
class ModelCapabilityInfo (line 31) | class ModelCapabilityInfo(BaseModel):
class ModelRecommendationRequest (line 42) | class ModelRecommendationRequest(BaseModel):
class ModelRecommendationResponse (line 47) | class ModelRecommendationResponse(BaseModel):
class ModelValidationRequest (line 56) | class ModelValidationRequest(BaseModel):
class ModelValidationResponse (line 63) | class ModelValidationResponse(BaseModel):
class BatchInitRequest (line 70) | class BatchInitRequest(BaseModel):
function get_default_model_configs (line 78) | async def get_default_model_configs():
function get_depth_requirements (line 109) | async def get_depth_requirements():
function get_capability_descriptions (line 134) | async def get_capability_descriptions():
function get_all_badges (line 144) | async def get_all_badges():
function recommend_models (line 173) | async def recommend_models(request: ModelRecommendationRequest):
function validate_models (line 235) | async def validate_models(request: ModelValidationRequest):
function batch_init_capabilities (line 258) | async def batch_init_capabilities(request: BatchInitRequest):
function get_model_capability (line 314) | async def get_model_capability(model_name: str):
FILE: app/routers/multi_market_stocks.py
function get_supported_markets (line 27) | async def get_supported_markets(current_user: dict = Depends(get_current...
function search_stocks (line 79) | async def search_stocks(
function get_stock_info (line 136) | async def get_stock_info(
function get_stock_quote (line 199) | async def get_stock_quote(
function get_stock_daily_quotes (line 260) | async def get_stock_daily_quotes(
FILE: app/routers/multi_period_sync.py
class MultiPeriodSyncRequest (line 19) | class MultiPeriodSyncRequest(BaseModel):
class MultiPeriodSyncResponse (line 29) | class MultiPeriodSyncResponse(BaseModel):
function start_multi_period_sync (line 37) | async def start_multi_period_sync(
function start_daily_sync (line 73) | async def start_daily_sync(
function start_weekly_sync (line 104) | async def start_weekly_sync(
function start_monthly_sync (line 135) | async def start_monthly_sync(
function start_all_history_sync (line 166) | async def start_all_history_sync(
function start_incremental_sync (line 203) | async def start_incremental_sync(
function get_sync_statistics (line 248) | async def get_sync_statistics():
function compare_period_data (line 266) | async def compare_period_data(
function get_supported_periods (line 314) | async def get_supported_periods():
function health_check (line 365) | async def health_check():
FILE: app/routers/multi_source_sync.py
class SyncRequest (line 19) | class SyncRequest(BaseModel):
class SyncResponse (line 25) | class SyncResponse(BaseModel):
class DataSourceStatus (line 32) | class DataSourceStatus(BaseModel):
function get_data_sources_status (line 41) | async def get_data_sources_status():
function get_current_data_source (line 89) | async def get_current_data_source():
function get_sync_status (line 138) | async def get_sync_status():
function run_stock_basics_sync (line 155) | async def run_stock_basics_sync(
function _test_single_adapter (line 193) | async def _test_single_adapter(adapter) -> dict:
class TestSourceRequest (line 272) | class TestSourceRequest(BaseModel):
function test_data_sources (line 278) | async def test_data_sources(request: TestSourceRequest = TestSourceReque...
function get_sync_recommendations (line 350) | async def get_sync_recommendations():
function get_sync_history (line 403) | async def get_sync_history(
function clear_sync_cache (line 449) | async def clear_sync_cache():
FILE: app/routers/news_data.py
class NewsQueryRequest (line 20) | class NewsQueryRequest(BaseModel):
class NewsSyncRequest (line 35) | class NewsSyncRequest(BaseModel):
function query_stock_news (line 44) | async def query_stock_news(
function query_news_advanced (line 134) | async def query_news_advanced(
function get_latest_news (line 184) | async def get_latest_news(
function search_news (line 229) | async def search_news(
function get_news_statistics (line 273) | async def get_news_statistics(
function start_news_sync (line 330) | async def start_news_sync(
function sync_single_stock_news (line 382) | async def sync_single_stock_news(
function cleanup_old_news (line 435) | async def cleanup_old_news(
function health_check (line 469) | async def health_check():
function _execute_stock_news_sync (line 490) | async def _execute_stock_news_sync(sync_service, request: NewsSyncRequest):
function _execute_market_news_sync (line 503) | async def _execute_market_news_sync(sync_service, request: NewsSyncReque...
FILE: app/routers/notifications.py
function list_notifications (line 18) | async def list_notifications(
function get_unread_count (line 33) | async def get_unread_count(user: dict = Depends(get_current_user)):
function mark_read (line 40) | async def mark_read(notif_id: str, user: dict = Depends(get_current_user)):
function mark_all_read (line 49) | async def mark_all_read(user: dict = Depends(get_current_user)):
function debug_redis_pool (line 56) | async def debug_redis_pool(user: dict = Depends(get_current_user)):
FILE: app/routers/operation_logs.py
function get_operation_logs (line 26) | async def get_operation_logs(
function get_operation_log_stats (line 74) | async def get_operation_log_stats(
function get_operation_log_detail (line 100) | async def get_operation_log_detail(
function clear_operation_logs (line 134) | async def clear_operation_logs(
function create_operation_log (line 169) | async def create_operation_log(
function export_logs_csv (line 207) | async def export_logs_csv(
FILE: app/routers/paper.py
class PlaceOrderRequest (line 24) | class PlaceOrderRequest(BaseModel):
function _detect_market_and_code (line 33) | def _detect_market_and_code(code: str) -> Tuple[str, str]:
function _get_or_create_account (line 65) | async def _get_or_create_account(user_id: str) -> Dict[str, Any]:
function _get_market_rules (line 118) | async def _get_market_rules(market: str) -> Optional[Dict[str, Any]]:
function _calculate_commission (line 127) | def _calculate_commission(market: str, side: str, amount: float, rules: ...
function _get_available_quantity (line 160) | async def _get_available_quantity(user_id: str, code: str, market: str) ...
function _get_last_price (line 194) | async def _get_last_price(code: str, market: str) -> Optional[float]:
function _zfill_code (line 263) | def _zfill_code(code: str) -> str:
function get_account (line 271) | async def get_account(current_user: dict = Depends(get_current_user)):
function place_order (line 345) | async def place_order(payload: PlaceOrderRequest, current_user: dict = D...
function list_positions (line 534) | async def list_positions(current_user: dict = Depends(get_current_user)):
function list_orders (line 564) | async def list_orders(limit: int = Query(50, ge=1, le=200), current_user...
function reset_account (line 574) | async def reset_account(confirm: bool = Query(False), current_user: dict...
FILE: app/routers/queue.py
function queue_stats (line 8) | async def queue_stats(user: dict = Depends(get_current_user), svc: Queue...
FILE: app/routers/reports.py
function get_stock_name (line 24) | def get_stock_name(stock_code: str) -> str:
function _build_report_query (line 89) | def _build_report_query(report_id: str) -> Dict[str, Any]:
class ReportFilter (line 103) | class ReportFilter(BaseModel):
class ReportListResponse (line 112) | class ReportListResponse(BaseModel):
function get_reports_list (line 120) | async def get_reports_list(
function get_report_detail (line 239) | async def get_report_detail(
function get_report_module_content (line 356) | async def get_report_module_content(
function delete_report (line 398) | async def delete_report(
function download_report (line 429) | async def download_report(
FILE: app/routers/scheduler.py
class JobTriggerRequest (line 20) | class JobTriggerRequest(BaseModel):
class JobUpdateRequest (line 26) | class JobUpdateRequest(BaseModel):
class JobMetadataUpdateRequest (line 33) | class JobMetadataUpdateRequest(BaseModel):
function list_jobs (line 40) | async def list_jobs(
function update_job_metadata_route (line 58) | async def update_job_metadata_route(
function get_job_detail (line 95) | async def get_job_detail(
function pause_job (line 121) | async def pause_job(
function resume_job (line 152) | async def resume_job(
function trigger_job (line 183) | async def trigger_job(
function get_job_history (line 224) | async def get_job_history(
function get_all_history (line 260) | async def get_all_history(
function get_scheduler_stats (line 303) | async def get_scheduler_stats(
function scheduler_health_check (line 321) | async def scheduler_health_check(
function get_job_executions (line 339) | async def get_job_executions(
function get_single_job_executions (line 381) | async def get_single_job_executions(
function get_job_execution_stats (line 423) | async def get_job_execution_stats(
function cancel_execution (line 445) | async def cancel_execution(
function mark_execution_failed (line 475) | async def mark_execution_failed(
function delete_execution (line 506) | async def delete_execution(
FILE: app/routers/screening.py
class FieldConfigResponse (line 19) | class FieldConfigResponse(BaseModel):
class OrderByItem (line 25) | class OrderByItem(BaseModel):
class ScreeningRequest (line 29) | class ScreeningRequest(BaseModel):
class ScreeningResponse (line 38) | class ScreeningResponse(BaseModel):
function get_screening_fields (line 48) | async def get_screening_fields(user: dict = Depends(get_current_user)):
function _convert_legacy_conditions_to_new_format (line 74) | def _convert_legacy_conditions_to_new_format(legacy_conditions: Dict[str...
function run_screening (line 157) | async def run_screening(req: ScreeningRequest, user: dict = Depends(get_...
function enhanced_screening (line 194) | async def enhanced_screening(req: NewScreeningRequest, user: dict = Depe...
function get_supported_fields (line 235) | async def get_supported_fields(user: dict = Depends(get_current_user)):
function get_field_info (line 247) | async def get_field_info(field_name: str, user: dict = Depends(get_curre...
function validate_conditions (line 263) | async def validate_conditions(conditions: List[ScreeningCondition], user...
function get_industries (line 276) | async def get_industries(user: dict = Depends(get_current_user)):
FILE: app/routers/social_media.py
class SocialMediaMessage (line 20) | class SocialMediaMessage(BaseModel):
class SocialMediaBatchRequest (line 43) | class SocialMediaBatchRequest(BaseModel):
class SocialMediaQueryRequest (line 49) | class SocialMediaQueryRequest(BaseModel):
function save_social_media_messages (line 69) | async def save_social_media_messages(request: SocialMediaBatchRequest):
function query_social_media_messages (line 94) | async def query_social_media_messages(request: SocialMediaQueryRequest):
function get_latest_messages (line 135) | async def get_latest_messages(
function search_messages (line 159) | async def search_messages(
function get_statistics (line 186) | async def get_statistics(
function get_supported_platforms (line 217) | async def get_supported_platforms():
function get_sentiment_analysis (line 266) | async def get_sentiment_analysis(
function health_check (line 340) | async def health_check():
FILE: app/routers/sse.py
function task_progress_generator (line 18) | async def task_progress_generator(task_id: str, user_id: str):
function batch_progress_generator (line 113) | async def batch_progress_generator(batch_id: str, user_id: str):
function stream_task_progress (line 225) | async def stream_task_progress(task_id: str, user: dict = Depends(get_cu...
function stream_batch_progress (line 244) | async def stream_batch_progress(batch_id: str, user: dict = Depends(get_...
FILE: app/routers/stock_data.py
function get_stock_basic_info (line 24) | async def get_stock_basic_info(
function get_market_quotes (line 61) | async def get_market_quotes(
function get_stock_list (line 98) | async def get_stock_list(
function get_combined_stock_data (line 146) | async def get_combined_stock_data(
function search_stocks (line 204) | async def search_stocks(
function get_market_summary (line 291) | async def get_market_summary(
function get_quotes_sync_status (line 344) | async def get_quotes_sync_status(
FILE: app/routers/stock_sync.py
function _sync_latest_to_market_quotes (line 25) | async def _sync_latest_to_market_quotes(symbol: str) -> None:
class SingleStockSyncRequest (line 101) | class SingleStockSyncRequest(BaseModel):
class BatchStockSyncRequest (line 112) | class BatchStockSyncRequest(BaseModel):
function sync_single_stock (line 123) | async def sync_single_stock(
function sync_batch_stocks (line 527) | async def sync_batch_stocks(
function get_sync_status (line 718) | async def get_sync_status(
FILE: app/routers/stocks.py
function _zfill_code (line 21) | def _zfill_code(code: str) -> str:
function _detect_market_and_code (line 31) | def _detect_market_and_code(code: str) -> Tuple[str, str]:
function get_quote (line 67) | async def get_quote(
function get_fundamentals (line 216) | async def get_fundamentals(
function get_kline (line 422) | async def get_kline(
function get_news (line 625) | async def get_news(code: str, days: int = 30, limit: int = 50, include_a...
FILE: app/routers/sync.py
function run_stock_basics_sync (line 17) | async def run_stock_basics_sync(force: bool = False):
function get_stock_basics_status (line 27) | async def get_stock_basics_status():
FILE: app/routers/system_config.py
function _mask_value (line 24) | def _mask_value(key: str, value: Any) -> Any:
function _build_summary (line 40) | def _build_summary() -> Dict[str, Any]:
function get_config_summary (line 53) | async def get_config_summary(current_user: dict = Depends(get_current_us...
function validate_config (line 64) | async def validate_config():
FILE: app/routers/tags.py
class TagCreate (line 15) | class TagCreate(BaseModel):
class TagUpdate (line 21) | class TagUpdate(BaseModel):
class TagResponse (line 27) | class TagResponse(BaseModel):
function list_tags (line 37) | async def list_tags(current_user: dict = Depends(get_current_user)):
function create_tag (line 46) | async def create_tag(payload: TagCreate, current_user: dict = Depends(ge...
function update_tag (line 61) | async def update_tag(tag_id: str, payload: TagUpdate, current_user: dict...
function delete_tag (line 80) | async def delete_tag(tag_id: str, current_user: dict = Depends(get_curre...
FILE: app/routers/tushare_init.py
class InitializationRequest (line 19) | class InitializationRequest(BaseModel):
class DatabaseStatusResponse (line 26) | class DatabaseStatusResponse(BaseModel):
class InitializationStatusResponse (line 36) | class InitializationStatusResponse(BaseModel):
function get_database_status (line 56) | async def get_database_status(
function get_initialization_status (line 112) | async def get_initialization_status(
function start_basic_initialization (line 137) | async def start_basic_initialization(
function start_full_initialization (line 161) | async def start_full_initialization(
function stop_initialization (line 194) | async def stop_initialization(
function _run_basic_initialization (line 226) | async def _run_basic_initialization():
function _run_full_initialization (line 253) | async def _run_full_initialization(historical_days: int, force_update: b...
FILE: app/routers/usage_statistics.py
function get_usage_records (line 20) | async def get_usage_records(
function get_usage_statistics (line 57) | async def get_usage_statistics(
function get_cost_by_provider (line 82) | async def get_cost_by_provider(
function get_cost_by_model (line 101) | async def get_cost_by_model(
function get_daily_cost (line 120) | async def get_daily_cost(
function delete_old_records (line 139) | async def delete_old_records(
FILE: app/routers/websocket_notifications.py
class ConnectionManager (line 18) | class ConnectionManager:
method __init__ (line 21) | def __init__(self):
method connect (line 26) | async def connect(self, websocket: WebSocket, user_id: str):
method disconnect (line 40) | async def disconnect(self, websocket: WebSocket, user_id: str):
method send_personal_message (line 51) | async def send_personal_message(self, message: dict, user_id: str):
method broadcast (line 81) | async def broadcast(self, message: dict):
method get_stats (line 96) | def get_stats(self) -> dict:
function websocket_notifications_endpoint (line 110) | async def websocket_notifications_endpoint(
function websocket_task_progress_endpoint (line 201) | async def websocket_task_progress_endpoint(
function get_websocket_stats (line 266) | async def get_websocket_stats():
function send_notification_via_websocket (line 272) | async def send_notification_via_websocket(user_id: str, notification: di...
function send_task_progress_via_websocket (line 287) | async def send_task_progress_via_websocket(task_id: str, progress_data: ...
FILE: app/scripts/init_providers.py
function init_providers (line 17) | async def init_providers():
FILE: app/services/analysis/status_update_utils.py
function perform_update_task_status (line 16) | async def perform_update_task_status(
function perform_update_task_status_with_tracker (line 57) | async def perform_update_task_status_with_tracker(
FILE: app/services/analysis_service.py
class AnalysisService (line 46) | class AnalysisService:
method __init__ (line 49) | def __init__(self):
method _convert_user_id (line 59) | def _convert_user_id(self, user_id: str) -> PyObjectId:
method _get_trading_graph (line 82) | def _get_trading_graph(self, config: Dict[str, Any]) -> TradingAgentsG...
method _execute_analysis_sync_with_progress (line 99) | def _execute_analysis_sync_with_progress(self, task: AnalysisTask, pro...
method _execute_analysis_sync (line 236) | def _execute_analysis_sync(self, task: AnalysisTask) -> AnalysisResult:
method _execute_single_analysis_async (line 348) | async def _execute_single_analysis_async(self, task: AnalysisTask):
method submit_single_analysis (line 422) | async def submit_single_analysis(
method submit_batch_analysis (line 514) | async def submit_batch_analysis(
method execute_analysis_task (line 616) | async def execute_analysis_task(
method _update_task_status (line 740) | async def _update_task_status(
method _update_task_status_with_tracker (line 754) | async def _update_task_status_with_tracker(
method get_task_status (line 768) | async def get_task_status(self, task_id: str) -> Optional[Dict[str, An...
method cancel_task (line 861) | async def cancel_task(self, task_id: str) -> bool:
method _record_token_usage (line 877) | async def _record_token_usage(
function get_analysis_service (line 949) | def get_analysis_service() -> AnalysisService:
FILE: app/services/auth_service.py
class TokenData (line 9) | class TokenData(BaseModel):
class AuthService (line 13) | class AuthService:
method create_access_token (line 15) | def create_access_token(sub: str, expires_minutes: int | None = None, ...
method verify_token (line 27) | def verify_token(token: str) -> Optional[TokenData]:
FILE: app/services/basics_sync/processing.py
function add_financial_metrics (line 8) | def add_financial_metrics(doc: Dict, daily_metrics: Dict) -> None:
FILE: app/services/basics_sync/utils.py
function fetch_stock_basic_df (line 12) | def fetch_stock_basic_df():
function find_latest_trade_date (line 93) | def find_latest_trade_date() -> str:
function fetch_daily_basic_mv_map (line 118) | def fetch_daily_basic_mv_map(trade_date: str) -> Dict[str, Dict[str, flo...
function fetch_latest_roe_map (line 168) | def fetch_latest_roe_map() -> Dict[str, Dict[str, float]]:
FILE: app/services/basics_sync_service.py
class SyncStats (line 40) | class SyncStats:
class BasicsSyncService (line 52) | class BasicsSyncService:
method __init__ (line 53) | def __init__(self) -> None:
method _ensure_indexes (line 59) | async def _ensure_indexes(self, db: AsyncIOMotorDatabase) -> None:
method get_status (line 113) | async def get_status(self, db: Optional[AsyncIOMotorDatabase] = None) ...
method _persist_status (line 125) | async def _persist_status(self, db: AsyncIOMotorDatabase, stats: Dict[...
method _execute_bulk_write_with_retry (line 130) | async def _execute_bulk_write_with_retry(
method run_full_sync (line 175) | async def run_full_sync(self, force: bool = False) -> Dict[str, Any]:
method _fetch_stock_basic_df (line 361) | def _fetch_stock_basic_df(self):
method _find_latest_trade_date (line 365) | def _find_latest_trade_date(self) -> str:
method _fetch_daily_basic_mv_map (line 369) | def _fetch_daily_basic_mv_map(self, trade_date: str) -> Dict[str, Dict...
method _fetch_latest_roe_map (line 373) | def _fetch_latest_roe_map(self) -> Dict[str, Dict[str, float]]:
method _generate_full_symbol (line 377) | def _generate_full_symbol(self, code: str) -> str:
function get_basics_sync_service (line 414) | def get_basics_sync_service() -> BasicsSyncService:
FILE: app/services/config_provider.py
class ConfigProvider (line 10) | class ConfigProvider:
method __init__ (line 18) | def __init__(self, ttl_seconds: int = 60) -> None:
method invalidate (line 23) | def invalidate(self) -> None:
method _is_cache_valid (line 27) | def _is_cache_valid(self) -> bool:
method get_effective_system_settings (line 34) | async def get_effective_system_settings(self) -> Dict[str, Any]:
method get_system_settings_meta (line 72) | async def get_system_settings_meta(self) -> Dict[str, Dict[str, Any]]:
FILE: app/services/config_service.py
class ConfigService (line 24) | class ConfigService:
method __init__ (line 27) | def __init__(self, db_manager=None):
method _get_db (line 31) | async def _get_db(self):
method get_market_categories (line 44) | async def get_market_categories(self) -> List[MarketCategory]:
method _create_default_market_categories (line 64) | async def _create_default_market_categories(self) -> List[MarketCatego...
method add_market_category (line 118) | async def add_market_category(self, category: MarketCategory) -> bool:
method update_market_category (line 135) | async def update_market_category(self, category_id: str, updates: Dict...
method delete_market_category (line 151) | async def delete_market_category(self, category_id: str) -> bool:
method get_datasource_groupings (line 173) | async def get_datasource_groupings(self) -> List[DataSourceGrouping]:
method add_datasource_to_category (line 185) | async def add_datasource_to_category(self, grouping: DataSourceGroupin...
method remove_datasource_from_category (line 205) | async def remove_datasource_from_category(self, data_source_name: str,...
method update_datasource_grouping (line 220) | async def update_datasource_grouping(self, data_source_name: str, cate...
method update_category_datasource_order (line 288) | async def update_category_datasource_order(self, category_id: str, ord...
method get_system_config (line 362) | async def get_system_config(self) -> Optional[SystemConfig]:
method _create_default_config (line 396) | async def _create_default_config(self) -> SystemConfig:
method save_system_config (line 516) | async def save_system_config(self, config: SystemConfig) -> bool:
method delete_llm_config (line 575) | async def delete_llm_config(self, provider: str, model_name: str) -> b...
method set_default_llm (line 619) | async def set_default_llm(self, model_name: str) -> bool:
method set_default_data_source (line 641) | async def set_default_data_source(self, data_source_name: str) -> bool:
method update_system_settings (line 663) | async def update_system_settings(self, settings: Dict[str, Any]) -> bool:
method get_system_settings (line 708) | async def get_system_settings(self) -> Dict[str, Any]:
method export_config (line 719) | async def export_config(self) -> Dict[str, Any]:
method import_config (line 771) | async def import_config(self, config_data: Dict[str, Any]) -> bool:
method _validate_config_data (line 822) | def _validate_config_data(self, config_data: Dict[str, Any]) -> bool:
method migrate_legacy_config (line 837) | async def migrate_legacy_config(self) -> bool:
method update_llm_config (line 851) | async def update_llm_config(self, llm_config: LLMConfig) -> bool:
method test_llm_config (line 878) | async def test_llm_config(self, llm_config: LLMConfig) -> Dict[str, Any]:
method _truncate_api_key (line 1110) | def _truncate_api_key(self, api_key: str, prefix_len: int = 6, suffix_...
method test_data_source_config (line 1127) | async def test_data_source_config(self, ds_config: DataSourceConfig) -...
method test_database_config (line 1686) | async def test_database_config(self, db_config: DatabaseConfig) -> Dic...
method add_database_config (line 2142) | async def add_database_config(self, db_config: DatabaseConfig) -> bool:
method update_database_config (line 2176) | async def update_database_config(self, db_config: DatabaseConfig) -> b...
method delete_database_config (line 2213) | async def delete_database_config(self, db_name: str) -> bool:
method get_database_config (line 2253) | async def get_database_config(self, db_name: str) -> Optional[Database...
method get_database_configs (line 2270) | async def get_database_configs(self) -> List[DatabaseConfig]:
method get_model_catalog (line 2285) | async def get_model_catalog(self) -> List[ModelCatalog]:
method get_provider_models (line 2300) | async def get_provider_models(self, provider: str) -> Optional[ModelCa...
method save_model_catalog (line 2314) | async def save_model_catalog(self, catalog: ModelCatalog) -> bool:
method delete_model_catalog (line 2334) | async def delete_model_catalog(self, provider: str) -> bool:
method init_default_model_catalog (line 2346) | async def init_default_model_catalog(self) -> bool:
method _get_default_model_catalog (line 2371) | def _get_default_model_catalog(self) -> List[Dict[str, Any]]:
method get_available_models (line 2667) | async def get_available_models(self) -> List[Dict[str, Any]]:
method set_default_llm (line 2705) | async def set_default_llm(self, model_name: str) -> bool:
method set_default_data_source (line 2727) | async def set_default_data_source(self, source_name: str) -> bool:
method get_llm_providers (line 2751) | async def get_llm_providers(self) -> List[LLMProvider]:
method _is_valid_api_key (line 2798) | def _is_valid_api_key(self, api_key: Optional[str]) -> bool:
method _get_env_api_key (line 2842) | def _get_env_api_key(self, provider_name: str) -> Optional[str]:
method add_llm_provider (line 2874) | async def add_llm_provider(self, provider: LLMProvider) -> str:
method update_llm_provider (line 2899) | async def update_llm_provider(self, provider_id: str, update_data: Dic...
method delete_llm_provider (line 2939) | async def delete_llm_provider(self, provider_id: str) -> bool:
method toggle_llm_provider (line 2991) | async def toggle_llm_provider(self, provider_id: str, is_active: bool)...
method init_aggregator_providers (line 3023) | async def init_aggregator_providers(self) -> Dict[str, Any]:
method migrate_env_to_providers (line 3128) | async def migrate_env_to_providers(self) -> Dict[str, Any]:
method test_provider_api (line 3251) | async def test_provider_api(self, provider_id: str) -> dict:
method _test_provider_connection (line 3309) | async def _test_provider_connection(self, provider_name: str, api_key:...
method _test_google_api (line 3367) | def _test_google_api(self, api_key: str, display_name: str, base_url: ...
method _test_deepseek_api (line 3546) | def _test_deepseek_api(self, api_key: str, display_name: str, model_na...
method _test_dashscope_api (line 3607) | def _test_dashscope_api(self, api_key: str, display_name: str, model_n...
method _test_openrouter_api (line 3669) | def _test_openrouter_api(self, api_key: str, display_name: str) -> dict:
method _test_openai_api (line 3725) | def _test_openai_api(self, api_key: str, display_name: str) -> dict:
method _test_anthropic_api (line 3779) | def _test_anthropic_api(self, api_key: str, display_name: str) -> dict:
method _test_qianfan_api (line 3833) | def _test_qianfan_api(self, api_key: str, display_name: str) -> dict:
method fetch_provider_models (line 3906) | async def fetch_provider_models(self, provider_id: str) -> dict:
method _fetch_models_from_api (line 3972) | def _fetch_models_from_api(self, api_key: str, base_url: str, display_...
method _format_models_with_pricing (line 4078) | def _format_models_with_pricing(self, models: list) -> list:
method _filter_popular_models (line 4157) | def _filter_popular_models(self, models: list) -> list:
method _test_openai_compatible_api (line 4233) | def _test_openai_compatible_api(self, api_key: str, display_name: str,...
FILE: app/services/data_consistency_checker.py
class DataConsistencyResult (line 15) | class DataConsistencyResult:
class FinancialMetricComparison (line 26) | class FinancialMetricComparison:
class DataConsistencyChecker (line 35) | class DataConsistencyChecker:
method __init__ (line 38) | def __init__(self):
method check_daily_basic_consistency (line 59) | def check_daily_basic_consistency(
method _find_common_stocks (line 133) | def _find_common_stocks(self, df1: pd.DataFrame, df2: pd.DataFrame) ->...
method _compare_metric (line 149) | def _compare_metric(
method _get_stock_metric_value (line 201) | def _get_stock_metric_value(self, df: pd.DataFrame, stock_code: str, m...
method _calculate_overall_consistency (line 216) | def _calculate_overall_consistency(
method resolve_data_conflicts (line 290) | def resolve_data_conflicts(
FILE: app/services/data_sources/akshare_adapter.py
class AKShareAdapter (line 14) | class AKShareAdapter(DataSourceAdapter):
method __init__ (line 17) | def __init__(self):
method name (line 21) | def name(self) -> str:
method _get_default_priority (line 24) | def _get_default_priority(self) -> int:
method is_available (line 27) | def is_available(self) -> bool:
method get_stock_list (line 35) | def get_stock_list(self) -> Optional[pd.DataFrame]:
method get_daily_basic (line 115) | def get_daily_basic(self, trade_date: str) -> Optional[pd.DataFrame]:
method _safe_float (line 186) | def _safe_float(self, value) -> Optional[float]:
method get_realtime_quotes (line 195) | def get_realtime_quotes(self, source: str = "eastmoney"):
method get_kline (line 295) | def get_kline(self, code: str, period: str = "day", limit: int = 120, ...
method get_news (line 345) | def get_news(self, code: str, days: int = 2, limit: int = 50, include_...
method find_latest_trade_date (line 388) | def find_latest_trade_date(self) -> Optional[str]:
FILE: app/services/data_sources/baostock_adapter.py
class BaoStockAdapter (line 14) | class BaoStockAdapter(DataSourceAdapter):
method __init__ (line 17) | def __init__(self):
method name (line 21) | def name(self) -> str:
method _get_default_priority (line 24) | def _get_default_priority(self) -> int:
method is_available (line 27) | def is_available(self) -> bool:
method get_stock_list (line 34) | def get_stock_list(self) -> Optional[pd.DataFrame]:
method get_daily_basic (line 107) | def get_daily_basic(self, trade_date: str, max_stocks: int = None) -> ...
method _safe_float (line 222) | def _safe_float(self, value) -> Optional[float]:
method get_realtime_quotes (line 231) | def get_realtime_quotes(self):
method get_kline (line 239) | def get_kline(self, code: str, period: str = "day", limit: int = 120, ...
method get_news (line 245) | def get_news(self, code: str, days: int = 2, limit: int = 50, include_...
method find_latest_trade_date (line 255) | def find_latest_trade_date(self) -> Optional[str]:
FILE: app/services/data_sources/base.py
class DataSourceAdapter (line 9) | class DataSourceAdapter(ABC):
method __init__ (line 12) | def __init__(self):
method name (line 17) | def name(self) -> str:
method priority (line 22) | def priority(self) -> int:
method _get_default_priority (line 30) | def _get_default_priority(self) -> int:
method is_available (line 35) | def is_available(self) -> bool:
method get_stock_list (line 40) | def get_stock_list(self) -> Optional[pd.DataFrame]:
method get_daily_basic (line 45) | def get_daily_basic(self, trade_date: str) -> Optional[pd.DataFrame]:
method find_latest_trade_date (line 50) | def find_latest_trade_date(self) -> Optional[str]:
method get_realtime_quotes (line 56) | def get_realtime_quotes(self) -> Optional[Dict[str, Dict[str, Optional...
method get_kline (line 62) | def get_kline(self, code: str, period: str = "day", limit: int = 120, ...
method get_news (line 67) | def get_news(self, code: str, days: int = 2, limit: int = 50, include_...
FILE: app/services/data_sources/data_consistency_checker.py
class DataConsistencyResult (line 13) | class DataConsistencyResult:
method __post_init__ (line 19) | def __post_init__(self):
class DataConsistencyChecker (line 24) | class DataConsistencyChecker:
method check_daily_basic_consistency (line 30) | def check_daily_basic_consistency(
method resolve_data_conflicts (line 40) | def resolve_data_conflicts(
FILE: app/services/data_sources/manager.py
class DataSourceManager (line 17) | class DataSourceManager:
method __init__ (line 25) | def __init__(self):
method _load_priority_from_database (line 45) | def _load_priority_from_database(self):
method get_available_adapters (line 91) | def get_available_adapters(self) -> List[DataSourceAdapter]:
method get_stock_list_with_fallback (line 103) | def get_stock_list_with_fallback(self, preferred_sources: Optional[Lis...
method get_daily_basic_with_fallback (line 140) | def get_daily_basic_with_fallback(self, trade_date: str, preferred_sou...
method find_latest_trade_date_with_fallback (line 172) | def find_latest_trade_date_with_fallback(self, preferred_sources: Opti...
method get_realtime_quotes_with_fallback (line 202) | def get_realtime_quotes_with_fallback(self) -> Tuple[Optional[Dict], O...
method get_daily_basic_with_consistency_check (line 221) | def get_daily_basic_with_consistency_check(
method get_kline_with_fallback (line 282) | def get_kline_with_fallback(self, code: str, period: str = "day", limi...
method get_news_with_fallback (line 296) | def get_news_with_fallback(self, code: str, days: int = 2, limit: int ...
FILE: app/services/data_sources/tushare_adapter.py
class TushareAdapter (line 14) | class TushareAdapter(DataSourceAdapter):
method __init__ (line 17) | def __init__(self):
method _initialize (line 22) | def _initialize(self):
method name (line 32) | def name(self) -> str:
method _get_default_priority (line 35) | def _get_default_priority(self) -> int:
method get_token_source (line 38) | def get_token_source(self) -> Optional[str]:
method is_available (line 44) | def is_available(self) -> bool:
method get_stock_list (line 59) | def get_stock_list(self) -> Optional[pd.DataFrame]:
method get_daily_basic (line 82) | def get_daily_basic(self, trade_date: str) -> Optional[pd.DataFrame]:
method get_realtime_quotes (line 100) | def get_realtime_quotes(self):
method get_kline (line 169) | def get_kline(self, code: str, period: str = "day", limit: int = 120, ...
method get_news (line 240) | def get_news(self, code: str, days: int = 2, limit: int = 50, include_...
method find_latest_trade_date (line 295) | def find_latest_trade_date(self) -> Optional[str]:
FILE: app/services/database/backups.py
function _check_mongodump_available (line 25) | def _check_mongodump_available() -> bool:
function create_backup_native (line 30) | async def create_backup_native(name: str, backup_dir: str, collections: ...
function create_backup (line 137) | async def create_backup(name: str, backup_dir: str, collections: Optiona...
function list_backups (line 203) | async def list_backups() -> List[Dict[str, Any]]:
function delete_backup (line 219) | async def delete_backup(backup_id: str) -> None:
function _convert_date_fields (line 236) | def _convert_date_fields(doc: dict) -> dict:
function import_data (line 265) | async def import_data(content: bytes, collection: str, *, format: str = ...
function _sanitize_document (line 402) | def _sanitize_document(doc: Any) -> Any:
function export_data (line 448) | async def export_data(collections: Optional[List[str]] = None, *, export...
FILE: app/services/database/cleanup.py
function cleanup_old_data (line 12) | async def cleanup_old_data(days: int) -> Dict[str, Any]:
function cleanup_analysis_results (line 44) | async def cleanup_analysis_results(days: int) -> Dict[str, Any]:
function cleanup_operation_logs (line 71) | async def cleanup_operation_logs(days: int) -> Dict[str, Any]:
FILE: app/services/database/serialization.py
function serialize_document (line 10) | def serialize_document(doc: dict) -> dict:
FILE: app/services/database/status_checks.py
function get_mongodb_status (line 13) | async def get_mongodb_status() -> Dict[str, Any]:
function get_redis_status (line 40) | async def get_redis_status() -> Dict[str, Any]:
function get_database_status (line 67) | async def get_database_status() -> Dict[str, Any]:
function test_mongodb_connection (line 73) | async def test_mongodb_connection() -> Dict[str, Any]:
function test_redis_connection (line 84) | async def test_redis_connection() -> Dict[str, Any]:
function test_connections (line 95) | async def test_connections() -> Dict[str, Any]:
FILE: app/services/database_screening_service.py
class DatabaseScreeningService (line 16) | class DatabaseScreeningService:
method __init__ (line 19) | def __init__(self):
method can_handle_conditions (line 70) | async def can_handle_conditions(self, conditions: List[Dict[str, Any]]...
method screen_stocks (line 96) | async def screen_stocks(
method _build_query (line 191) | async def _build_query(self, conditions: List[Dict[str, Any]]) -> Dict...
method _build_sort_conditions (line 231) | def _build_sort_conditions(self, order_by: Optional[List[Dict[str, str...
method _enrich_with_financial_data (line 253) | async def _enrich_with_financial_data(self, results: List[Dict[str, An...
method _format_result (line 324) | def _format_result(self, doc: Dict[str, Any]) -> Dict[str, Any]:
method get_field_statistics (line 390) | async def get_field_statistics(self, field: str) -> Dict[str, Any]:
method _separate_conditions (line 439) | def _separate_conditions(self, conditions: List[Dict[str, Any]]) -> Tu...
method _filter_by_quotes (line 464) | async def _filter_by_quotes(
method get_available_values (line 556) | async def get_available_values(self, field: str, limit: int = 100) -> ...
function get_database_screening_service (line 593) | def get_database_screening_service() -> DatabaseScreeningService:
FILE: app/services/database_service.py
class DatabaseService (line 29) | class DatabaseService:
method __init__ (line 32) | def __init__(self):
method get_database_status (line 40) | async def get_database_status(self) -> Dict[str, Any]:
method _get_mongodb_status (line 44) | async def _get_mongodb_status(self) -> Dict[str, Any]:
method _get_redis_status (line 48) | async def _get_redis_status(self) -> Dict[str, Any]:
method get_database_stats (line 52) | async def get_database_stats(self) -> Dict[str, Any]:
method test_connections (line 112) | async def test_connections(self) -> Dict[str, Any]:
method _test_mongodb_connection (line 116) | async def _test_mongodb_connection(self) -> Dict[str, Any]:
method _test_redis_connection (line 120) | async def _test_redis_connection(self) -> Dict[str, Any]:
method create_backup (line 124) | async def create_backup(self, name: str, collections: List[str] = None...
method list_backups (line 150) | async def list_backups(self) -> List[Dict[str, Any]]:
method delete_backup (line 154) | async def delete_backup(self, backup_id: str) -> None:
method cleanup_old_data (line 158) | async def cleanup_old_data(self, days: int) -> Dict[str, Any]:
method cleanup_analysis_results (line 162) | async def cleanup_analysis_results(self, days: int) -> Dict[str, Any]:
method cleanup_operation_logs (line 166) | async def cleanup_operation_logs(self, days: int) -> Dict[str, Any]:
method import_data (line 170) | async def import_data(self, content: bytes, collection: str, format: s...
method export_data (line 175) | async def export_data(self, collections: List[str] = None, format: str...
method _serialize_document (line 179) | def _serialize_document(self, doc: dict) -> dict:
FILE: app/services/enhanced_screening/utils.py
function analyze_conditions (line 11) | def analyze_conditions(conditions: List[ScreeningCondition]) -> Dict[str...
function convert_conditions_to_traditional_format (line 56) | def convert_conditions_to_traditional_format(conditions: List[ScreeningC...
FILE: app/services/enhanced_screening_service.py
class EnhancedScreeningService (line 24) | class EnhancedScreeningService:
method __init__ (line 27) | def __init__(self):
method screen_stocks (line 34) | async def screen_stocks(
method _analyze_conditions (line 151) | def _analyze_conditions(self, conditions: List[ScreeningCondition]) ->...
method _screen_with_database (line 157) | async def _screen_with_database(
method _screen_with_traditional_method (line 174) | async def _screen_with_traditional_method(
method _convert_conditions_to_traditional_format (line 205) | def _convert_conditions_to_traditional_format(
method _enrich_results_with_realtime_metrics (line 212) | async def _enrich_results_with_realtime_metrics(self, items: List[Dict...
method get_field_info (line 233) | async def get_field_info(self, field: str) -> Optional[Dict[str, Any]]:
method get_all_supported_fields (line 268) | async def get_all_supported_fields(self) -> List[Dict[str, Any]]:
method validate_conditions (line 279) | async def validate_conditions(self, conditions: List[ScreeningConditio...
function get_enhanced_screening_service (line 343) | def get_enhanced_screening_service() -> EnhancedScreeningService:
FILE: app/services/favorites_service.py
class FavoritesService (line 14) | class FavoritesService:
method __init__ (line 17) | def __init__(self):
method _get_db (line 20) | async def _get_db(self):
method _is_valid_object_id (line 26) | def _is_valid_object_id(self, user_id: str) -> bool:
method _format_favorite (line 35) | def _format_favorite(self, favorite: Dict[str, Any]) -> Dict[str, Any]:
method get_user_favorites (line 57) | async def get_user_favorites(self, user_id: str) -> List[Dict[str, Any]]:
method add_favorite (line 154) | async def add_favorite(
method remove_favorite (line 236) | async def remove_favorite(self, user_id: str, stock_code: str) -> bool:
method update_favorite (line 263) | async def update_favorite(
method is_favorite (line 315) | async def is_favorite(self, user_id: str, stock_code: str) -> bool:
method get_user_tags (line 364) | async def get_user_tags(self, user_id: str) -> List[str]:
method _get_mock_price (line 389) | def _get_mock_price(self, stock_code: str) -> float:
method _get_mock_change (line 395) | def _get_mock_change(self, stock_code: str) -> float:
method _get_mock_volume (line 401) | def _get_mock_volume(self, stock_code: str) -> int:
FILE: app/services/financial_data_service.py
class FinancialDataService (line 17) | class FinancialDataService:
method __init__ (line 20) | def __init__(self):
method initialize (line 24) | async def initialize(self):
method _ensure_indexes (line 40) | async def _ensure_indexes(self):
method save_financial_data (line 76) | async def save_financial_data(
method get_financial_data (line 164) | async def get_financial_data(
method get_latest_financial_data (line 218) | async def get_latest_financial_data(
method get_financial_statistics (line 232) | async def get_financial_statistics(self) -> Dict[str, Any]:
method _standardize_financial_data (line 289) | def _standardize_financial_data(
method _standardize_tushare_data (line 323) | def _standardize_tushare_data(
method _standardize_akshare_data (line 360) | def _standardize_akshare_data(
method _standardize_baostock_data (line 388) | def _standardize_baostock_data(
method _get_full_symbol (line 415) | def _get_full_symbol(self, symbol: str, market: str) -> str:
method _extract_latest_period (line 424) | def _extract_latest_period(self, financial_data: Dict[str, Any]) -> str:
method _extract_akshare_indicators (line 440) | def _extract_akshare_indicators(self, financial_data: Dict[str, Any]) ...
method _generate_current_period (line 481) | def _generate_current_period(self) -> str:
method _safe_float (line 503) | def _safe_float(self, value) -> Optional[float]:
function get_financial_data_service (line 520) | async def get_financial_data_service() -> FinancialDataService:
FILE: app/services/foreign_stock_service.py
class ForeignStockService (line 24) | class ForeignStockService:
method __init__ (line 41) | def __init__(self, db=None):
method get_quote (line 59) | async def get_quote(self, market: str, code: str, force_refresh: bool ...
method get_basic_info (line 84) | async def get_basic_info(self, market: str, code: str, force_refresh: ...
method get_kline (line 103) | async def get_kline(self, market: str, code: str, period: str = 'day',
method _get_hk_quote (line 125) | async def _get_hk_quote(self, code: str, force_refresh: bool = False) ...
method _get_source_priority (line 239) | async def _get_source_priority(self, market: str) -> List[str]:
method _get_hk_quote_from_yfinance (line 276) | def _get_hk_quote_from_yfinance(self, code: str) -> Dict:
method _get_hk_quote_from_akshare (line 283) | def _get_hk_quote_from_akshare(self, code: str) -> Dict:
method _get_us_quote (line 296) | async def _get_us_quote(self, code: str, force_refresh: bool = False) ...
method _get_us_quote_from_yfinance (line 424) | def _get_us_quote_from_yfinance(self, code: str) -> Dict:
method _get_us_quote_from_alpha_vantage (line 449) | def _get_us_quote_from_alpha_vantage(self, code: str) -> Dict:
method _get_us_quote_from_finnhub (line 492) | def _get_us_quote_from_finnhub(self, code: str) -> Dict:
method _get_hk_info (line 529) | async def _get_hk_info(self, code: str, force_refresh: bool = False) -...
method _get_us_info (line 608) | async def _get_us_info(self, code: str, force_refresh: bool = False) -...
method _get_hk_kline (line 711) | async def _get_hk_kline(self, code: str, period: str, limit: int, forc...
method _get_us_kline (line 788) | async def _get_us_kline(self, code: str, period: str, limit: int, forc...
method _format_hk_quote (line 865) | def _format_hk_quote(self, data: Dict, code: str, source: str) -> Dict:
method _format_hk_info (line 882) | def _format_hk_info(self, data: Dict, code: str, source: str) -> Dict:
method _parse_cached_data (line 910) | def _parse_cached_data(self, cached_data: str, market: str, code: str)...
method _parse_cached_kline (line 931) | def _parse_cached_kline(self, cached_data: str) -> List[Dict]:
method _get_us_info_from_yfinance (line 950) | def _get_us_info_from_yfinance(self, code: str) -> Dict:
method _safe_float (line 971) | def _safe_float(self, value, default=None):
method _get_us_info_from_alpha_vantage (line 980) | def _get_us_info_from_alpha_vantage(self, code: str) -> Dict:
method _get_us_info_from_finnhub (line 1007) | def _get_us_info_from_finnhub(self, code: str) -> Dict:
method _get_us_kline_from_yfinance (line 1037) | def _get_us_kline_from_yfinance(self, code: str, period: str, limit: i...
method _get_us_kline_from_alpha_vantage (line 1076) | def _get_us_kline_from_alpha_vantage(self, code: str, period: str, lim...
method _get_us_kline_from_finnhub (line 1134) | def _get_us_kline_from_finnhub(self, code: str, period: str, limit: in...
method get_hk_news (line 1204) | async def get_hk_news(self, code: str, days: int = 2, limit: int = 50)...
method get_us_news (line 1300) | async def get_us_news(self, code: str, days: int = 2, limit: int = 50)...
method _get_us_news_from_alpha_vantage (line 1396) | def _get_us_news_from_alpha_vantage(self, code: str, days: int, limit:...
method _get_us_news_from_finnhub (line 1459) | def _get_us_news_from_finnhub(self, code: str, days: int, limit: int) ...
method _get_hk_news_from_finnhub (line 1506) | def _get_hk_news_from_finnhub(self, code: str, days: int, limit: int) ...
method _get_hk_info_from_akshare (line 1556) | def _get_hk_info_from_akshare(self, code: str) -> Dict:
method _get_hk_info_from_yfinance (line 1615) | def _get_hk_info_from_yfinance(self, code: str) -> Dict:
method _get_hk_info_from_finnhub (line 1633) | def _get_hk_info_from_finnhub(self, code: str) -> Dict:
method _get_hk_kline_from_akshare (line 1666) | def _get_hk_kline_from_akshare(self, code: str, period: str, limit: in...
method _get_hk_kline_from_yfinance (line 1703) | def _get_hk_kline_from_yfinance(self, code: str, period: str, limit: i...
method _get_hk_kline_from_finnhub (line 1743) | def _get_hk_kline_from_finnhub(self, code: str, period: str, limit: in...
method _get_hk_news_from_akshare (line 1799) | def _get_hk_news_from_akshare(self, code: str, days: int, limit: int) ...
FILE: app/services/historical_data_service.py
class HistoricalDataService (line 18) | class HistoricalDataService:
method __init__ (line 21) | def __init__(self):
method initialize (line 26) | async def initialize(self):
method _ensure_indexes (line 40) | async def _ensure_indexes(self):
method save_historical_data (line 70) | async def save_historical_data(
method _execute_bulk_write_with_retry (line 193) | async def _execute_bulk_write_with_retry(
method _standardize_record (line 248) | def _standardize_record(
method _get_full_symbol (line 330) | def _get_full_symbol(self, symbol: str, market: str) -> str:
method _format_date (line 346) | def _format_date(self, date_value) -> str:
method _safe_float (line 364) | def _safe_float(self, value) -> Optional[float]:
method get_historical_data (line 373) | async def get_historical_data(
method get_latest_date (line 432) | async def get_latest_date(self, symbol: str, data_source: str) -> Opti...
method get_data_statistics (line 451) | async def get_data_statistics(self) -> Dict[str, Any]:
function get_historical_data_service (line 500) | async def get_historical_data_service() -> HistoricalDataService:
FILE: app/services/internal_message_service.py
function convert_objectid_to_str (line 18) | def convert_objectid_to_str(data: Union[Dict, List[Dict]]) -> Union[Dict...
class InternalMessageQueryParams (line 41) | class InternalMessageQueryParams:
class InternalMessageStats (line 65) | class InternalMessageStats:
class InternalMessageService (line 77) | class InternalMessageService:
method __init__ (line 80) | def __init__(self):
method initialize (line 85) | async def initialize(self):
method _get_collection (line 95) | async def _get_collection(self):
method save_internal_messages (line 101) | async def save_internal_messages(
method query_internal_messages (line 158) | async def query_internal_messages(
method get_latest_messages (line 245) | async def get_latest_messages(
method search_messages (line 263) | async def search_messages(
method get_research_reports (line 300) | async def get_research_reports(
method get_analyst_notes (line 317) | async def get_analyst_notes(
method get_internal_statistics (line 334) | async def get_internal_statistics(
function get_internal_message_service (line 410) | async def get_internal_message_service() -> InternalMessageService:
FILE: app/services/log_export_service.py
class LogExportService (line 18) | class LogExportService:
method __init__ (line 21) | def __init__(self, log_dir: str = "./logs"):
method list_log_files (line 45) | def list_log_files(self) -> List[Dict[str, Any]]:
method _get_log_type (line 107) | def _get_log_type(self, filename: str) -> str:
method read_log_file (line 128) | def read_log_file(
method export_logs (line 217) | def export_logs(
method get_log_statistics (line 317) | def get_log_statistics(self, days: int = 7) -> Dict[str, Any]:
function get_log_export_service (line 379) | def get_log_export_service() -> LogExportService:
function _get_log_directory (line 391) | def _get_log_directory() -> str:
FILE: app/services/memory_state_manager.py
class TaskStatus (line 16) | class TaskStatus(Enum):
class TaskState (line 25) | class TaskState:
method to_dict (line 48) | def to_dict(self) -> Dict[str, Any]:
class MemoryStateManager (line 94) | class MemoryStateManager:
method __init__ (line 97) | def __init__(self):
method set_websocket_manager (line 105) | def set_websocket_manager(self, websocket_manager):
method create_task (line 109) | async def create_task(
method _calculate_estimated_duration (line 140) | def _calculate_estimated_duration(self, parameters: Dict[str, Any]) ->...
method update_task_status (line 180) | async def update_task_status(
method get_task (line 246) | async def get_task(self, task_id: str) -> Optional[TaskState]:
method get_task_dict (line 259) | async def get_task_dict(self, task_id: str) -> Optional[Dict[str, Any]]:
method list_all_tasks (line 264) | async def list_all_tasks(
method list_user_tasks (line 287) | async def list_user_tasks(
method delete_task (line 312) | async def delete_task(self, task_id: str) -> bool:
method get_statistics (line 321) | async def get_statistics(self) -> Dict[str, Any]:
method cleanup_old_tasks (line 339) | async def cleanup_old_tasks(self, max_age_hours: int = 24) -> int:
method cleanup_zombie_tasks (line 356) | async def cleanup_zombie_tasks(self, max_running_hours: int = 2) -> int:
method remove_task (line 394) | async def remove_task(self, task_id: str) -> bool:
function get_memory_state_manager (line 415) | def get_memory_state_manager() -> MemoryStateManager:
FILE: app/services/model_capability_service.py
class ModelCapabilityService (line 22) | class ModelCapabilityService:
method _parse_aggregator_model_name (line 25) | def _parse_aggregator_model_name(self, model_name: str) -> Tuple[Optio...
method _get_model_capability_with_mapping (line 64) | def _get_model_capability_with_mapping(self, model_name: str) -> Tuple...
method get_model_capability (line 87) | def get_model_capability(self, model_name: str) -> int:
method get_model_config (line 113) | def get_model_config(self, model_name: str) -> Dict[str, Any]:
method validate_model_pair (line 217) | def validate_model_pair(
method recommend_models_for_depth (line 312) | def recommend_models_for_depth(
method _get_default_models (line 395) | def _get_default_models(self) -> Tuple[str, str]:
method _recommend_model (line 406) | def _recommend_model(self, model_type: str, min_level: int) -> str:
function get_model_capability_service (line 424) | def get_model_capability_service() -> ModelCapabilityService:
FILE: app/services/multi_source_basics_sync_service.py
class DataSourcePriority (line 33) | class DataSourcePriority(Enum):
class SyncStats (line 41) | class SyncStats:
class MultiSourceBasicsSyncService (line 58) | class MultiSourceBasicsSyncService:
method __init__ (line 61) | def __init__(self):
method get_status (line 66) | async def get_status(self) -> Dict[str, Any]:
method _persist_status (line 79) | async def _persist_status(self, db: AsyncIOMotorDatabase, stats: Dict[...
method _execute_bulk_write_with_retry (line 98) | async def _execute_bulk_write_with_retry(
method run_full_sync (line 143) | async def run_full_sync(self, force: bool = False, preferred_sources: ...
method _add_financial_metrics (line 336) | def _add_financial_metrics(self, doc: Dict, daily_metrics: Dict) -> None:
method _generate_full_symbol (line 340) | def _generate_full_symbol(self, code: str) -> str:
function get_multi_source_sync_service (line 376) | def get_multi_source_sync_service() -> MultiSourceBasicsSyncService:
FILE: app/services/news_data_service.py
function convert_objectid_to_str (line 18) | def convert_objectid_to_str(data: Union[Dict, List[Dict]]) -> Union[Dict...
class NewsQueryParams (line 41) | class NewsQueryParams:
class NewsStats (line 59) | class NewsStats:
method __post_init__ (line 71) | def __post_init__(self):
class NewsDataService (line 78) | class NewsDataService:
method __init__ (line 81) | def __init__(self):
method _ensure_indexes (line 87) | async def _ensure_indexes(self):
method _get_collection (line 139) | def _get_collection(self):
method save_news_data (line 146) | async def save_news_data(
method save_news_data_sync (line 244) | def save_news_data_sync(
method _standardize_news_data (line 347) | def _standardize_news_data(
method _get_full_symbol (line 400) | def _get_full_symbol(self, symbol: str, market: str) -> str:
method _parse_datetime (line 414) | def _parse_datetime(self, dt_value) -> Optional[datetime]:
method _safe_float (line 447) | def _safe_float(self, value) -> Optional[float]:
method query_news (line 457) | async def query_news(self, params: NewsQueryParams) -> List[Dict[str, ...
method get_latest_news (line 552) | async def get_latest_news(
method get_news_statistics (line 581) | async def get_news_statistics(
method delete_old_news (line 683) | async def delete_old_news(self, days_to_keep: int = 90) -> int:
method search_news (line 711) | async def search_news(
function get_news_data_service (line 760) | async def get_news_data_service() -> NewsDataService:
FILE: app/services/notifications_service.py
class NotificationsService (line 19) | class NotificationsService:
method __init__ (line 20) | def __init__(self):
method _ensure_indexes (line 26) | async def _ensure_indexes(self):
method create_and_publish (line 34) | async def create_and_publish(self, payload: NotificationCreate) -> str:
method unread_count (line 91) | async def unread_count(self, user_id: str) -> int:
method list (line 95) | async def list(self, user_id: str, *, status: Optional[str] = None, nt...
method mark_read (line 118) | async def mark_read(self, user_id: str, notif_id: str) -> bool:
method mark_all_read (line 127) | async def mark_all_read(self, user_id: str) -> int:
function get_notifications_service (line 136) | def get_notifications_service() -> NotificationsService:
FILE: app/services/operation_log_service.py
class OperationLogService (line 24) | class OperationLogService:
method __init__ (line 27) | def __init__(self):
method create_log (line 30) | async def create_log(
method get_logs (line 71) | async def get_logs(self, query: OperationLogQuery) -> Tuple[List[Opera...
method get_stats (line 131) | async def get_stats(self, days: int = 30) -> OperationLogStats:
method clear_logs (line 197) | async def clear_logs(self, days: Optional[int] = None, action_type: Op...
method get_log_by_id (line 228) | async def get_log_by_id(self, log_id: str) -> Optional[OperationLogRes...
function get_operation_log_service (line 249) | def get_operation_log_service() -> OperationLogService:
function log_operation (line 258) | async def log_operation(
FILE: app/services/progress/log_handler.py
class ProgressLogHandler (line 15) | class ProgressLogHandler(logging.Handler):
method __init__ (line 18) | def __init__(self):
method register_tracker (line 61) | def register_tracker(self, task_id: str, tracker: RedisProgressTracker):
method unregister_tracker (line 67) | def unregister_tracker(self, task_id: str):
method emit (line 74) | def emit(self, record):
method _extract_progress_message (line 104) | def _extract_progress_message(self, message: str) -> Optional[str]:
method _extract_stock_symbol (line 124) | def _extract_stock_symbol(self, message: str) -> Optional[str]:
function get_progress_log_handler (line 146) | def get_progress_log_handler() -> ProgressLogHandler:
function register_analysis_tracker (line 175) | def register_analysis_tracker(task_id: str, tracker: RedisProgressTracker):
function unregister_analysis_tracker (line 181) | def unregister_analysis_tracker(task_id: str):
FILE: app/services/progress/tracker.py
class AnalysisStep (line 21) | class AnalysisStep:
function safe_serialize (line 31) | def safe_serialize(data):
class RedisProgressTracker (line 46) | class RedisProgressTracker:
method __init__ (line 49) | def __init__(self, task_id: str, analysts: List[str], research_depth: ...
method _init_redis (line 91) | def _init_redis(self) -> bool:
method _generate_dynamic_steps (line 133) | def _generate_dynamic_steps(self) -> List[AnalysisStep]:
method _get_debate_rounds (line 176) | def _get_debate_rounds(self) -> int:
method _get_analyst_step_info (line 184) | def _get_analyst_step_info(self, analyst: str) -> Dict[str, str]:
method _estimate_step_time (line 194) | def _estimate_step_time(self, step: AnalysisStep) -> float:
method _get_base_total_time (line 198) | def _get_base_total_time(self) -> float:
method _calculate_time_estimates (line 256) | def _calculate_time_estimates(self) -> tuple[float, float, float]:
method _calculate_static_time_estimates (line 277) | def _calculate_static_time_estimates(progress_data: dict) -> dict:
method update_progress (line 300) | def update_progress(self, progress_update: Any) -> Dict[str, Any]:
method _update_steps_by_progress (line 345) | def _update_steps_by_progress(self, progress_pct: float) -> None:
method _detect_current_step (line 374) | def _detect_current_step(self) -> int:
method _find_step_by_name (line 394) | def _find_step_by_name(self, step_name: str) -> Optional[AnalysisStep]:
method _find_step_by_pattern (line 400) | def _find_step_by_pattern(self, pattern: str) -> Optional[AnalysisStep]:
method _save_progress (line 406) | def _save_progress(self) -> None:
method mark_completed (line 421) | def mark_completed(self) -> Dict[str, Any]:
method mark_failed (line 437) | def mark_failed(self, reason: str = "") -> Dict[str, Any]:
method to_dict (line 453) | def to_dict(self) -> Dict[str, Any]:
function get_progress_by_id (line 477) | def get_progress_by_id(task_id: str) -> Optional[Dict[str, Any]]:
FILE: app/services/queue/helpers.py
function check_user_concurrent_limit (line 18) | async def check_user_concurrent_limit(r: Redis, user_id: str, limit: int...
function check_global_concurrent_limit (line 25) | async def check_global_concurrent_limit(r: Redis, limit: int) -> bool:
function mark_task_processing (line 31) | async def mark_task_processing(r: Redis, task_id: str, user_id: str) -> ...
function unmark_task_processing (line 38) | async def unmark_task_processing(r: Redis, task_id: str, user_id: str) -...
function set_visibility_timeout (line 45) | async def set_visibility_timeout(r: Redis, task_id: str, worker_id: str,...
function clear_visibility_timeout (line 57) | async def clear_visibility_timeout(r: Redis, task_id: str) -> None:
FILE: app/services/queue_service.py
class QueueService (line 45) | class QueueService:
method __init__ (line 48) | def __init__(self, redis: Redis):
method enqueue_task (line 54) | async def enqueue_task(
method dequeue_task (line 100) | async def dequeue_task(self, worker_id: str) -> Optional[Dict[str, Any]]:
method ack_task (line 143) | async def ack_task(self, task_id: str, success: bool = True) -> bool:
method create_batch (line 179) | async def create_batch(self, user_id: str, symbols: List[str], params:...
method get_task (line 194) | async def get_task(self, task_id: str) -> Optional[Dict[str, Any]]:
method get_batch (line 211) | async def get_batch(self, batch_id: str) -> Optional[Dict[str, Any]]:
method stats (line 225) | async def stats(self) -> Dict[str, int]:
method _check_user_concurrent_limit (line 238) | async def _check_user_concurrent_limit(self, user_id: str) -> bool:
method _check_global_concurrent_limit (line 242) | async def _check_global_concurrent_limit(self) -> bool:
method _mark_task_processing (line 246) | async def _mark_task_processing(self, task_id: str, user_id: str, work...
method _unmark_task_processing (line 250) | async def _unmark_task_processing(self, task_id: str, user_id: str):
method _set_visibility_timeout (line 254) | async def _set_visibility_timeout(self, task_id: str, worker_id: str):
method _clear_visibility_timeout (line 258) | async def _clear_visibility_timeout(self, task_id: str):
method get_user_queue_status (line 262) | async def get_user_queue_status(self, user_id: str) -> Dict[str, int]:
method cleanup_expired_tasks (line 273) | async def cleanup_expired_tasks(self):
method _handle_expired_task (line 301) | async def _handle_expired_task(self, task_id: str):
method cancel_task (line 331) | async def cancel_task(self, task_id: str) -> bool:
function get_queue_service (line 363) | def get_queue_service() -> QueueService:
FILE: app/services/quotes_ingestion_service.py
class QuotesIngestionService (line 16) | class QuotesIngestionService:
method __init__ (line 28) | def __init__(self, collection_name: str = "market_quotes") -> None:
method _normalize_stock_code (line 48) | def _normalize_stock_code(code: str) -> str:
method ensure_indexes (line 87) | async def ensure_indexes(self) -> None:
method _record_sync_status (line 96) | async def _record_sync_status(
method get_sync_status (line 139) | async def get_sync_status(self) -> Dict[str, any]:
method _check_tushare_permission (line 207) | def _check_tushare_permission(self) -> bool:
method _can_call_tushare (line 255) | def _can_call_tushare(self) -> bool:
method _record_tushare_call (line 285) | def _record_tushare_call(self) -> None:
method _get_next_source (line 289) | def _get_next_source(self) -> Tuple[str, Optional[str]]:
method _is_trading_time (line 315) | def _is_trading_time(self, now: Optional[datetime] = None) -> bool:
method _collection_empty (line 343) | async def _collection_empty(self) -> bool:
method _collection_stale (line 352) | async def _collection_stale(self, latest_trade_date: Optional[str]) ->...
method _bulk_upsert (line 367) | async def _bulk_upsert(self, quotes_map: Dict[str, Dict], trade_date: ...
method backfill_from_historical_data (line 413) | async def backfill_from_historical_data(self) -> None:
method backfill_last_close_snapshot (line 500) | async def backfill_last_close_snapshot(self) -> None:
method backfill_last_close_snapshot_if_needed (line 517) | async def backfill_last_close_snapshot_if_needed(self) -> None:
method _fetch_quotes_from_source (line 537) | def _fetch_quotes_from_source(self, source_type: str, akshare_api: Opt...
method run_once (line 597) | async def run_once(self) -> None:
FILE: app/services/quotes_service.py
function _safe_float (line 16) | def _safe_float(v) -> Optional[float]:
class QuotesService (line 34) | class QuotesService:
method __init__ (line 35) | def __init__(self, ttl_seconds: int = 30) -> None:
method get_quotes (line 41) | async def get_quotes(self, codes: List[str]) -> Dict[str, Dict[str, Op...
method _fetch_spot_akshare (line 57) | def _fetch_spot_akshare(self) -> Dict[str, Dict[str, Optional[float]]]:
function get_quotes_service (line 106) | def get_quotes_service() -> QuotesService:
FILE: app/services/scheduler_service.py
function get_utc8_now (line 30) | def get_utc8_now():
class TaskCancelledException (line 40) | class TaskCancelledException(Exception):
class SchedulerService (line 45) | class SchedulerService:
method __init__ (line 48) | def __init__(self, scheduler: AsyncIOScheduler):
method _get_db (line 61) | def _get_db(self):
method list_jobs (line 67) | async def list_jobs(self) -> List[Dict[str, Any]]:
method get_job (line 87) | async def get_job(self, job_id: str) -> Optional[Dict[str, Any]]:
method pause_job (line 108) | async def pause_job(self, job_id: str) -> bool:
method resume_job (line 130) | async def resume_job(self, job_id: str) -> bool:
method trigger_job (line 152) | async def trigger_job(self, job_id: str, kwargs: Optional[Dict[str, An...
method get_job_history (line 221) | async def get_job_history(
method count_job_history (line 254) | async def count_job_history(self, job_id: str) -> int:
method get_all_history (line 272) | async def get_all_history(
method count_all_history (line 313) | async def count_all_history(
method get_job_executions (line 344) | async def get_job_executions(
method count_job_executions (line 409) | async def count_job_executions(
method cancel_job_execution (line 451) | async def cancel_job_execution(self, execution_id: str) -> bool:
method mark_execution_as_failed (line 496) | async def mark_execution_as_failed(self, execution_id: str, reason: st...
method delete_execution (line 538) | async def delete_execution(self, execution_id: str) -> bool:
method get_job_execution_stats (line 577) | async def get_job_execution_stats(self, job_id: str) -> Dict[str, Any]:
method get_stats (line 635) | async def get_stats(self) -> Dict[str, Any]:
method health_check (line 656) | async def health_check(self) -> Dict[str, Any]:
method _job_to_dict (line 670) | def _job_to_dict(self, job: Job, include_details: bool = False) -> Dic...
method _setup_event_listeners (line 700) | def _setup_event_listeners(self):
method _check_zombie_tasks (line 733) | async def _check_zombie_tasks(self):
method _on_job_executed (line 766) | def _on_job_executed(self, event: JobExecutionEvent):
method _on_job_error (line 783) | def _on_job_error(self, event: JobExecutionEvent):
method _on_job_missed (line 801) | def _on_job_missed(self, event: JobExecutionEvent):
method _record_job_execution (line 810) | async def _record_job_execution(
method _record_job_action (line 931) | async def _record_job_action(
method _get_job_metadata (line 959) | async def _get_job_metadata(self, job_id: str) -> Optional[Dict[str, A...
method update_job_metadata (line 980) | async def update_job_metadata(
function set_scheduler_instance (line 1034) | def set_scheduler_instance(scheduler: AsyncIOScheduler):
function get_scheduler_service (line 1046) | def get_scheduler_service() -> SchedulerService:
function update_job_progress (line 1065) | async def update_job_progress(
FILE: app/services/screening/eval_utils.py
function collect_fields_from_conditions (line 12) | def collect_fields_from_conditions(node: Dict[str, Any], allowed_fields:...
function evaluate_fund_conditions (line 31) | def evaluate_fund_conditions(snap: Dict[str, Any], node: Dict[str, Any],...
function evaluate_conditions (line 78) | def evaluate_conditions(
function safe_float (line 160) | def safe_float(v: Any) -> Optional[float]:
FILE: app/services/screening_service.py
class ScreeningParams (line 59) | class ScreeningParams:
class ScreeningService (line 71) | class ScreeningService:
method __init__ (line 72) | def __init__(self):
method run (line 77) | def run(self, conditions: Dict[str, Any], params: ScreeningParams) -> ...
method _evaluate_fund_conditions (line 187) | def _evaluate_fund_conditions(self, snap: Dict[str, Any], node: Dict[s...
method _collect_fields_from_conditions (line 192) | def _collect_fields_from_conditions(self, node: Dict[str, Any]) -> Lis...
method _evaluate_conditions (line 197) | def _evaluate_conditions(self, df: pd.DataFrame, node: Dict[str, Any])...
method _safe_float (line 202) | def _safe_float(self, v: Any) -> Optional[float]:
method _get_universe (line 206) | def _get_universe(self) -> List[str]:
FILE: app/services/simple_analysis_service.py
function _get_stock_info_safe (line 40) | def _get_stock_info_safe(stock_code: str):
function get_provider_by_model_name (line 53) | async def get_provider_by_model_name(model_name: str) -> str:
function get_provider_by_model_name_sync (line 86) | def get_provider_by_model_name_sync(model_name: str) -> str:
function get_provider_and_url_by_model_sync (line 100) | def get_provider_and_url_by_model_sync(model_name: str) -> dict:
function _get_env_api_key_for_provider (line 272) | def _get_env_api_key_for_provider(provider: str) -> str:
function _get_default_backend_url (line 305) | def _get_default_backend_url(provider: str) -> str:
function _get_default_provider_by_model (line 331) | def _get_default_provider_by_model(model_name: str) -> str:
function create_analysis_config (line 372) | def create_analysis_config(
class SimpleAnalysisService (line 591) | class SimpleAnalysisService:
method __init__ (line 594) | def __init__(self):
method _update_progress_async (line 621) | async def _update_progress_async(self, task_id: str, progress: int, me...
method _resolve_stock_name (line 652) | def _resolve_stock_name(self, code: Optional[str]) -> str:
method _enrich_stock_names (line 673) | def _enrich_stock_names(self, tasks: List[Dict[str, Any]]) -> List[Dic...
method _convert_user_id (line 685) | def _convert_user_id(self, user_id: str) -> PyObjectId:
method _get_trading_graph (line 707) | def _get_trading_graph(self, config: Dict[str, Any]) -> TradingAgentsG...
method create_analysis_task (line 730) | async def create_analysis_task(
method execute_analysis_background (line 809) | async def execute_analysis_background(
method _execute_analysis_sync (line 1063) | async def _execute_analysis_sync(
method _run_analysis_sync (line 1086) | def _run_analysis_sync(
method get_task_status (line 1849) | async def get_task_status(self, task_id: str) -> Optional[Dict[str, An...
method list_all_tasks (line 1936) | async def list_all_tasks(
method list_user_tasks (line 2021) | async def list_user_tasks(
method cleanup_zombie_tasks (line 2222) | async def cleanup_zombie_tasks(self, max_running_hours: int = 2) -> Di...
method get_zombie_tasks (line 2284) | async def get_zombie_tasks(self, max_running_hours: int = 2) -> List[D...
method _update_task_status (line 2339) | async def _update_task_status(
method _save_analysis_result (line 2373) | async def _save_analysis_result(self, task_id: str, result: Dict[str, ...
method _save_analysis_result_web_style (line 2385) | async def _save_analysis_result_web_style(self, task_id: str, result: ...
method _save_analysis_results_complete (line 2673) | async def _save_analysis_results_complete(self, task_id: str, result: ...
method _save_modular_reports_to_data_dir (line 2715) | async def _save_modular_reports_to_data_dir(self, result: Dict[str, An...
function get_simple_analysis_service (line 2900) | def get_simple_analysis_service() -> SimpleAnalysisService:
FILE: app/services/social_media_service.py
class SocialMediaQueryParams (line 18) | class SocialMediaQueryParams:
class SocialMediaStats (line 40) | class SocialMediaStats:
class SocialMediaService (line 56) | class SocialMediaService:
method __init__ (line 59) | def __init__(self):
method initialize (line 64) | async def initialize(self):
method _get_collection (line 74) | async def _get_collection(self):
method save_social_media_messages (line 80) | async def save_social_media_messages(
method query_social_media_messages (line 138) | async def query_social_media_messages(
method get_latest_messages (line 216) | async def get_latest_messages(
method search_messages (line 232) | async def search_messages(
method get_social_media_statistics (line 269) | async def get_social_media_statistics(
function get_social_media_service (line 346) | async def get_social_media_service() -> SocialMediaService:
FILE: app/services/stock_data_service.py
class StockDataService (line 23) | class StockDataService:
method __init__ (line 29) | def __init__(self):
method get_stock_basic_info (line 33) | async def get_stock_basic_info(
method get_market_quotes (line 91) | async def get_market_quotes(self, symbol: str) -> Optional[MarketQuote...
method get_stock_list (line 121) | async def get_stock_list(
method update_stock_basic_info (line 188) | async def update_stock_basic_info(
method update_market_quotes (line 235) | async def update_market_quotes(
method _standardize_basic_info (line 274) | def _standardize_basic_info(self, doc: Dict[str, Any]) -> Dict[str, Any]:
method _standardize_market_quotes (line 353) | def _standardize_market_quotes(self, doc: Dict[str, Any]) -> Dict[str,...
function get_stock_data_service (line 397) | def get_stock_data_service() -> StockDataService:
FILE: app/services/tags_service.py
class TagsService (line 12) | class TagsService:
method __init__ (line 13) | def __init__(self) -> None:
method _get_db (line 17) | async def _get_db(self):
method ensure_indexes (line 22) | async def ensure_indexes(self) -> None:
method _normalize_user_id (line 31) | def _normalize_user_id(self, user_id: str) -> str:
method _format_doc (line 35) | def _format_doc(self, doc: Dict[str, Any]) -> Dict[str, Any]:
method list_tags (line 45) | async def list_tags(self, user_id: str) -> List[Dict[str, Any]]:
method create_tag (line 54) | async def create_tag(self, user_id: str, name: str, color: Optional[st...
method update_tag (line 70) | async def update_tag(self, user_id: str, tag_id: str, *, name: Optiona...
method delete_tag (line 88) | async def delete_tag(self, user_id: str, tag_id: str) -> bool:
FILE: app/services/unified_stock_service.py
class UnifiedStockService (line 25) | class UnifiedStockService:
method __init__ (line 28) | def __init__(self, db: AsyncIOMotorDatabase):
method get_stock_info (line 56) | async def get_stock_info(
method _get_source_priority (line 102) | async def _get_source_priority(self, market: str) -> List[str]:
method get_stock_quote (line 144) | async def get_stock_quote(self, market: str, code: str) -> Optional[Di...
method search_stocks (line 159) | async def search_stocks(
method get_daily_quotes (line 221) | async def get_daily_quotes(
method get_supported_markets (line 256) | async def get_supported_markets(self) -> List[Dict]:
FILE: app/services/usage_statistics_service.py
class UsageStatisticsService (line 17) | class UsageStatisticsService:
method __init__ (line 20) | def __init__(self):
method add_usage_record (line 24) | async def add_usage_record(self, record: UsageRecord) -> bool:
method get_usage_records (line 39) | async def get_usage_records(
method get_usage_statistics (line 79) | async def get_usage_statistics(
method get_cost_by_provider (line 189) | async def get_cost_by_provider(self, days: int = 7) -> Dict[str, float]:
method get_cost_by_model (line 197) | async def get_cost_by_model(self, days: int = 7) -> Dict[str, float]:
method get_daily_cost (line 205) | async def get_daily_cost(self, days: int = 7) -> Dict[str, float]:
method delete_old_records (line 213) | async def delete_old_records(self, days: int = 90) -> int:
FILE: app/services/user_service.py
function get_logger (line 21) | def get_logger(name: str) -> logging.Logger:
class UserService (line 27) | class UserService:
method __init__ (line 30) | def __init__(self):
method close (line 35) | def close(self):
method __del__ (line 41) | def __del__(self):
method hash_password (line 46) | def hash_password(password: str) -> str:
method verify_password (line 52) | def verify_password(plain_password: str, hashed_password: str) -> bool:
method create_user (line 56) | async def create_user(self, user_data: UserCreate) -> Optional[User]:
method authenticate_user (line 119) | async def authenticate_user(self, username: str, password: str) -> Opt...
method get_user_by_username (line 164) | async def get_user_by_username(self, username: str) -> Optional[User]:
method get_user_by_id (line 175) | async def get_user_by_id(self, user_id: str) -> Optional[User]:
method update_user (line 189) | async def update_user(self, username: str, user_data: UserUpdate) -> O...
method change_password (line 231) | async def change_password(self, username: str, old_password: str, new_...
method reset_password (line 263) | async def reset_password(self, username: str, new_password: str) -> bool:
method create_admin_user (line 288) | async def create_admin_user(self, username: str = "admin", password: s...
method list_users (line 337) | async def list_users(self, skip: int = 0, limit: int = 100) -> List[Us...
method deactivate_user (line 367) | async def deactivate_user(self, username: str) -> bool:
method activate_user (line 391) | async def activate_user(self, username: str) -> bool:
FILE: app/services/websocket_manager.py
class WebSocketManager (line 14) | class WebSocketManager:
method __init__ (line 17) | def __init__(self):
method connect (line 22) | async def connect(self, websocket: WebSocket, task_id: str):
method disconnect (line 33) | async def disconnect(self, websocket: WebSocket, task_id: str):
method send_progress_update (line 43) | async def send_progress_update(self, task_id: str, message: Dict[str, ...
method broadcast_to_user (line 61) | async def broadcast_to_user(self, user_id: str, message: Dict[str, Any]):
method get_connection_count (line 67) | async def get_connection_count(self, task_id: str) -> int:
method get_total_connections (line 72) | async def get_total_connections(self) -> int:
function get_websocket_manager (line 83) | def get_websocket_manager() -> WebSocketManager:
FILE: app/utils/api_key_utils.py
function is_valid_api_key (line 11) | def is_valid_api_key(api_key: Optional[str]) -> bool:
function truncate_api_key (line 56) | def truncate_api_key(api_key: Optional[str]) -> Optional[str]:
function get_env_api_key_for_provider (line 76) | def get_env_api_key_for_provider(provider_name: str) -> Optional[str]:
function get_env_api_key_for_datasource (line 97) | def get_env_api_key_for_datasource(ds_type: str) -> Optional[str]:
function should_skip_api_key_update (line 137) | def should_skip_api_key_update(api_key: Optional[str]) -> bool:
FILE: app/utils/error_formatter.py
class ErrorCategory (line 12) | class ErrorCategory(str, Enum):
class ErrorFormatter (line 31) | class ErrorFormatter:
method format_error (line 57) | def format_error(cls, error_message: str, context: Optional[Dict] = No...
method _categorize_error (line 83) | def _categorize_error(cls, error_message: str, context: Dict) -> Tuple...
method _extract_llm_provider (line 178) | def _extract_llm_provider(cls, error_message: str) -> Optional[str]:
method _extract_data_source (line 187) | def _extract_data_source(cls, error_message: str) -> Optional[str]:
method _generate_friendly_message (line 196) | def _generate_friendly_message(
FILE: app/utils/report_exporter.py
class ReportExporter (line 64) | class ReportExporter:
method __init__ (line 67) | def __init__(self):
method generate_markdown_report (line 77) | def generate_markdown_report(self, report_doc: Dict[str, Any]) -> str:
method _clean_markdown_for_pandoc (line 167) | def _clean_markdown_for_pandoc(self, md_content: str) -> str:
method _create_pdf_css (line 210) | def _create_pdf_css(self) -> str:
method generate_docx_report (line 284) | def generate_docx_report(self, report_doc: Dict[str, Any]) -> bytes:
method _markdown_to_html (line 378) | def _markdown_to_html(self, md_content: str) -> str:
method _generate_pdf_with_pdfkit (line 612) | def _generate_pdf_with_pdfkit(self, html_content: str) -> bytes:
method generate_pdf_report (line 635) | def generate_pdf_report(self, report_doc: Dict[str, Any]) -> bytes:
FILE: app/utils/timezone.py
function get_tz_name (line 10) | def get_tz_name() -> str:
function get_tz (line 28) | def get_tz() -> ZoneInfo:
function now_tz (line 32) | def now_tz() -> datetime:
function to_config_tz (line 37) | def to_config_tz(dt: Optional[datetime]) -> Optional[datetime]:
function ensure_timezone (line 46) | def ensure_timezone(dt: Optional[datetime]) -> Optional[datetime]:
FILE: app/utils/trading_time.py
function is_trading_time (line 14) | def is_trading_time(now: Optional[datetime] = None) -> bool:
function is_strict_trading_time (line 53) | def is_strict_trading_time(now: Optional[datetime] = None) -> bool:
function is_pre_market_time (line 85) | def is_pre_market_time(now: Optional[datetime] = None) -> bool:
function is_after_market_time (line 109) | def is_after_market_time(now: Optional[datetime] = None) -> bool:
function get_trading_status (line 133) | def get_trading_status(now: Optional[datetime] = None) -> str:
FILE: app/worker.py
function publish_progress (line 35) | async def publish_progress(task_id: str, message: str, step: Optional[in...
function process_task (line 54) | async def process_task(task_id: str) -> None:
function worker_loop (line 185) | async def worker_loop(stop_event: asyncio.Event):
function main (line 204) | async def main():
FILE: app/worker/akshare_init_service.py
class AKShareInitializationStats (line 18) | class AKShareInitializationStats:
method __post_init__ (line 34) | def __post_init__(self):
class AKShareInitService (line 39) | class AKShareInitService:
method __init__ (line 52) | def __init__(self):
method initialize (line 57) | async def initialize(self):
method run_full_initialization (line 63) | async def run_full_initialization(
method _step_check_database_status (line 182) | async def _step_check_database_status(self, skip_if_exists: bool):
method _step_initialize_basic_info (line 202) | async def _step_initialize_basic_info(self):
method _step_initialize_historical_data (line 218) | async def _step_initialize_historical_data(self, historical_days: int):
method _step_initialize_weekly_data (line 249) | async def _step_initialize_weekly_data(self, historical_days: int):
method _step_initialize_monthly_data (line 285) | async def _step_initialize_monthly_data(self, historical_days: int):
method _step_initialize_financial_data (line 321) | async def _step_initialize_financial_data(self):
method _step_initialize_quotes (line 339) | async def _step_initialize_quotes(self):
method _step_initialize_news_data (line 357) | async def _step_initialize_news_data(self):
method _step_verify_data_integrity (line 377) | async def _step_verify_data_integrity(self):
method _get_initialization_summary (line 406) | def _get_initialization_summary(self) -> Dict[str, Any]:
function get_akshare_init_service (line 437) | async def get_akshare_init_service() -> AKShareInitService:
function run_akshare_full_initialization (line 447) | async def run_akshare_full_initialization(
FILE: app/worker/akshare_sync_service.py
class AKShareSyncService (line 18) | class AKShareSyncService:
method __init__ (line 29) | def __init__(self):
method initialize (line 37) | async def initialize(self):
method sync_stock_basic_info (line 62) | async def sync_stock_basic_info(self, force_update: bool = False) -> D...
method _process_basic_info_batch (line 133) | async def _process_basic_info_batch(self, batch: List[Dict[str, Any]],...
method _is_data_fresh (line 206) | def _is_data_fresh(self, updated_at: Any, hours: int = 24) -> bool:
method sync_realtime_quotes (line 234) | async def sync_realtime_quotes(self, symbols: List[str] = None, force:...
method _process_quotes_batch (line 389) | async def _process_quotes_batch(self, batch: List[str]) -> Dict[str, A...
method _process_quotes_batch_fallback (line 455) | async def _process_quotes_batch_fallback(self, batch: List[str]) -> Di...
method _get_and_save_quotes (line 490) | async def _get_and_save_quotes(self, symbol: str) -> bool:
method sync_historical_data (line 532) | async def sync_historical_data(
method _process_historical_batch (line 632) | async def _process_historical_batch(
method _get_last_sync_date (line 698) | async def _get_last_sync_date(self, symbol: str = None) -> str:
method sync_financial_data (line 754) | async def sync_financial_data(self, symbols: List[str] = None) -> Dict...
method _process_financial_batch (line 835) | async def _process_financial_batch(self, batch: List[str]) -> Dict[str...
method _save_financial_data (line 879) | async def _save_financial_data(self, symbol: str, financial_data: Dict...
method run_status_check (line 902) | async def run_status_check(self) -> Dict[str, Any]:
method _get_favorite_stocks (line 952) | async def _get_favorite_stocks(self) -> List[str]:
method sync_news_data (line 998) | async def sync_news_data(
method _process_news_batch (line 1092) | async def _process_news_batch(
function get_akshare_sync_service (line 1148) | async def get_akshare_sync_service() -> AKShareSyncService:
function run_akshare_basic_info_sync (line 1158) | async def run_akshare_basic_info_sync(force_update: bool = False):
function run_akshare_quotes_sync (line 1170) | async def run_akshare_quotes_sync(force: bool = False):
function run_akshare_historical_sync (line 1188) | async def run_akshare_historical_sync(incremental: bool = True):
function run_akshare_financial_sync (line 1200) | async def run_akshare_financial_sync():
function run_akshare_status_check (line 1212) | async def run_akshare_status_check():
function run_akshare_news_sync (line 1224) | async def run_akshare_news_sync(max_news_per_stock: int = 20):
FILE: app/worker/analysis_worker.py
class AnalysisWorker (line 32) | class AnalysisWorker:
method __init__ (line 35) | def __init__(self, worker_id: Optional[str] = None):
method _signal_handler (line 51) | def _signal_handler(self, signum, frame):
method start (line 56) | async def start(self):
method _work_loop (line 112) | async def _work_loop(self):
method _process_task (line 133) | async def _process_task(self, task_data: Dict[str, Any]):
method _progress_callback (line 183) | def _progress_callback(self, progress: int, message: str):
method _heartbeat_loop (line 187) | async def _heartbeat_loop(self):
method _send_heartbeat (line 199) | async def _send_heartbeat(self):
method _cleanup_loop (line 218) | async def _cleanup_loop(self):
method _cleanup (line 230) | async def _cleanup(self):
function main (line 251) | async def main():
FILE: app/worker/baostock_init_service.py
class BaoStockInitializationStats (line 20) | class BaoStockInitializationStats:
method duration (line 36) | def duration(self) -> float:
method progress (line 43) | def progress(self) -> str:
class BaoStockInitService (line 48) | class BaoStockInitService:
method __init__ (line 51) | def __init__(self):
method initialize (line 66) | async def initialize(self):
method check_database_status (line 81) | async def check_database_status(self) -> Dict[str, Any]:
method full_initialization (line 118) | async def full_initialization(self, historical_days: int = 365,
method _sync_financial_data (line 235) | async def _sync_financial_data(self) -> int:
method _verify_data_integrity (line 278) | async def _verify_data_integrity(self, stats: BaoStockInitializationSt...
method basic_initialization (line 297) | async def basic_initialization(self) -> BaoStockInitializationStats:
function run_baostock_full_initialization (line 345) | async def run_baostock_full_initialization():
function run_baostock_basic_initialization (line 356) | async def run_baostock_basic_initialization():
FILE: app/worker/baostock_sync_service.py
class BaoStockSyncStats (line 21) | class BaoStockSyncStats:
method __post_init__ (line 29) | def __post_init__(self):
class BaoStockSyncService (line 34) | class BaoStockSyncService:
method __init__ (line 37) | def __init__(self):
method initialize (line 54) | async def initialize(self):
method sync_stock_basic_info (line 71) | async def sync_stock_basic_info(self, batch_size: int = 100) -> BaoSto...
method _sync_basic_info_batch (line 117) | async def _sync_basic_info_batch(self, stock_batch: List[Dict[str, Any...
method _get_total_shares (line 172) | async def _get_total_shares(self, code: str) -> Optional[float]:
method _safe_float (line 221) | def _safe_float(self, value) -> Optional[float]:
method _update_stock_basic_info (line 230) | async def _update_stock_basic_info(self, basic_info: Dict[str, Any]):
method sync_daily_quotes (line 254) | async def sync_daily_quotes(self, batch_size: int = 50) -> BaoStockSyn...
method _sync_quotes_batch (line 306) | async def _sync_quotes_batch(self, code_batch: List[str]) -> BaoStockS...
method _update_stock_quotes (line 327) | async def _update_stock_quotes(self, quotes: Dict[str, Any]):
method sync_historical_data (line 348) | async def sync_historical_data(self, days: int = 30, batch_size: int =...
method _sync_historical_batch (line 413) | async def _sync_historical_batch(
method _update_historical_data (line 452) | async def _update_historical_data(self, code: str, hist_data, period: ...
method _get_last_sync_date (line 493) | async def _get_last_sync_date(self, symbol: str = None) -> str:
method check_service_status (line 528) | async def check_service_status(self) -> Dict[str, Any]:
function run_baostock_basic_info_sync (line 566) | async def run_baostock_basic_info_sync():
function run_baostock_daily_quotes_sync (line 577) | async def run_baostock_daily_quotes_sync():
function run_baostock_historical_sync (line 588) | async def run_baostock_historical_sync():
function run_baostock_status_check (line 599) | async def run_baostock_status_check():
FILE: app/worker/example_sdk_sync_service.py
class ExampleSDKSyncService (line 23) | class ExampleSDKSyncService:
method __init__ (line 40) | def __init__(self):
method sync_all_data (line 58) | async def sync_all_data(self):
method sync_basic_info (line 95) | async def sync_basic_info(self):
method sync_realtime_quotes (line 126) | async def sync_realtime_quotes(self):
method sync_financial_data (line 159) | async def sync_financial_data(self):
method _process_basic_info_batch (line 190) | async def _process_basic_info_batch(self, batch: List[Dict[str, Any]]):
method _process_quotes_batch (line 211) | async def _process_quotes_batch(self, batch: List[str]):
method _process_financial_data (line 234) | async def _process_financial_data(self, code: str):
method _record_sync_status (line 268) | async def _record_sync_status(self, status: str, start_time: datetime,...
method _log_sync_stats (line 293) | def _log_sync_stats(self):
method sync_incremental (line 304) | async def sync_incremental(self):
function run_full_sync (line 329) | async def run_full_sync():
function run_incremental_sync (line 335) | async def run_incremental_sync():
function main (line 343) | async def main():
FILE: app/worker/financial_data_sync_service.py
class FinancialSyncStats (line 22) | class FinancialSyncStats:
method to_dict (line 33) | def to_dict(self) -> Dict[str, Any]:
class FinancialDataSyncService (line 48) | class FinancialDataSyncService:
method __init__ (line 51) | def __init__(self):
method initialize (line 56) | async def initialize(self):
method sync_financial_data (line 75) | async def sync_financial_data(
method _sync_source_financial_data (line 143) | async def _sync_source_financial_data(
method _sync_symbol_financial_data (line 215) | async def _sync_symbol_financial_data(
method _get_stock_symbols (line 248) | async def _get_stock_symbols(self) -> List[str]:
method get_sync_statistics (line 271) | async def get_sync_statistics(self) -> Dict[str, Any]:
method sync_single_stock (line 283) | async def sync_single_stock(
function get_financial_sync_service (line 329) | async def get_financial_sync_service() -> FinancialDataSyncService:
FILE: app/worker/hk_data_service.py
class HKDataService (line 36) | class HKDataService:
method __init__ (line 39) | def __init__(self):
method initialize (line 53) | async def initialize(self):
method get_stock_info (line 57) | async def get_stock_info(
method _get_cached_info (line 117) | async def _get_cached_info(self, code: str, source: str) -> Optional[D...
method _save_to_cache (line 134) | async def _save_to_cache(self, stock_info: Dict[str, Any]) -> bool:
method _normalize_stock_info (line 148) | def _normalize_stock_info(self, stock_info: Dict, source: str) -> Dict:
function get_hk_data_service (line 186) | async def get_hk_data_service() -> HKDataService:
FILE: app/worker/hk_sync_service.py
class HKDataService (line 38) | class HKDataService:
method __init__ (line 41) | def __init__(self):
method initialize (line 60) | async def initialize(self):
method _get_hk_stock_list_from_akshare (line 64) | def _get_hk_stock_list_from_akshare(self) -> List[str]:
method _get_fallback_stock_list (line 110) | def _get_fallback_stock_list(self) -> List[str]:
method sync_basic_info_from_source (line 140) | async def sync_basic_info_from_source(
method _sync_basic_info_from_akshare_batch (line 235) | async def _sync_basic_info_from_akshare_batch(self, force_update: bool...
method _normalize_stock_info (line 342) | def _normalize_stock_info(self, stock_info: Dict, source: str) -> Dict:
method sync_quotes_from_source (line 376) | async def sync_quotes_from_source(
function get_hk_sync_service (line 466) | async def get_hk_sync_service() -> HKSyncService:
function run_hk_yfinance_basic_info_sync (line 477) | async def run_hk_yfinance_basic_info_sync(force_update: bool = False):
function run_hk_akshare_basic_info_sync (line 489) | async def run_hk_akshare_basic_info_sync(force_update: bool = False):
function run_hk_yfinance_quotes_sync (line 501) | async def run_hk_yfinance_quotes_sync():
function run_hk_status_check (line 513) | async def run_hk_status_check():
FILE: app/worker/multi_period_sync_service.py
class MultiPeriodSyncStats (line 21) | class MultiPeriodSyncStats:
method __post_init__ (line 31) | def __post_init__(self):
class MultiPeriodSyncService (line 36) | class MultiPeriodSyncService:
method __init__ (line 39) | def __init__(self):
method initialize (line 45) | async def initialize(self):
method sync_multi_period_data (line 66) | async def sync_multi_period_data(
method _sync_period_data (line 143) | async def _sync_period_data(
method _sync_batch_period_data (line 194) | async def _sync_batch_period_data(
method _get_all_symbols (line 245) | async def _get_all_symbols(self) -> List[str]:
method _get_full_history_date_range (line 263) | async def _get_full_history_date_range(self) -> tuple[str, str]:
method get_sync_statistics (line 288) | async def get_sync_statistics(self) -> Dict[str, Any]:
function get_multi_period_sync_service (line 340) | async def get_multi_period_sync_service() -> MultiPeriodSyncService:
function run_multi_period_sync (line 350) | async def run_multi_period_sync(periods: List[str] = None):
function run_daily_sync (line 362) | async def run_daily_sync():
function run_weekly_sync (line 367) | async def run_weekly_sync():
function run_monthly_sync (line 372) | async def run_monthly_sync():
FILE: app/worker/news_data_sync_service.py
class NewsSyncStats (line 20) | class NewsSyncStats:
method duration_seconds (line 31) | def duration_seconds(self) -> float:
method success_rate (line 38) | def success_rate(self) -> float:
class NewsDataSyncService (line 45) | class NewsDataSyncService:
method __init__ (line 48) | def __init__(self):
method _get_news_service (line 55) | async def _get_news_service(self):
method _get_tushare_provider (line 61) | async def _get_tushare_provider(self):
method _get_tushare_provider (line 68) | async def _get_tushare_provider(self):
method _get_akshare_provider (line 76) | async def _get_akshare_provider(self):
method _get_realtime_aggregator (line 83) | async def _get_realtime_aggregator(self):
method sync_stock_news (line 89) | async def sync_stock_news(
method _sync_tushare_news (line 183) | async def _sync_tushare_news(
method _sync_akshare_news (line 228) | async def _sync_akshare_news(
method _sync_realtime_news (line 260) | async def _sync_realtime_news(
method _standardize_tushare_news (line 291) | def _standardize_tushare_news(self, news: Dict[str, Any], symbol: str)...
method _standardize_akshare_news (line 313) | def _standardize_akshare_news(self, news: Dict[str, Any], symbol: str)...
method _standardize_realtime_news (line 335) | def _standardize_realtime_news(self, news_item, symbol: str) -> Option...
method _classify_news_category (line 357) | def _classify_news_category(self, title: str) -> str:
method _analyze_sentiment (line 372) | def _analyze_sentiment(self, text: str) -> str:
method _assess_importance (line 389) | def _assess_importance(self, title: str) -> str:
method _extract_keywords (line 403) | def _extract_keywords(self, text: str) -> List[str]:
method _deduplicate_news (line 419) | def _deduplicate_news(self, news_list: List[Dict[str, Any]]) -> List[D...
method sync_market_news (line 433) | async def sync_market_news(
function get_news_data_sync_service (line 512) | async def get_news_data_sync_service() -> NewsDataSyncService:
FILE: app/worker/tushare_init_service.py
class InitializationStats (line 18) | class InitializationStats:
method __post_init__ (line 34) | def __post_init__(self):
class TushareInitService (line 39) | class TushareInitService:
method __init__ (line 52) | def __init__(self):
method initialize (line 57) | async def initialize(self):
method run_full_initialization (line 63) | async def run_full_initialization(
method _step_check_database_status (line 174) | async def _step_check_database_status(self, skip_if_exists: bool):
method _step_initialize_basic_info (line 194) | async def _step_initialize_basic_info(self):
method _step_initialize_historical_data (line 210) | async def _step_initialize_historical_data(self, historical_days: int):
method _step_initialize_weekly_data (line 241) | async def _step_initialize_weekly_data(self, historical_days: int):
method _step_initialize_monthly_data (line 277) | async def _step_initialize_monthly_data(self, historical_days: int):
method _step_initialize_financial_data (line 313) | async def _step_initialize_financial_data(self):
method _step_initialize_quotes (line 331) | async def _step_initialize_quotes(self):
method _step_initialize_news_data (line 349) | async def _step_initialize_news_data(self, historical_days: int):
method _step_verify_data_integrity (line 373) | async def _step_verify_data_integrity(self):
method _get_initialization_summary (line 402) | def _get_initialization_summary(self) -> Dict[str, Any]:
function get_tushare_init_service (line 434) | async def get_tushare_init_service() -> TushareInitService:
function run_tushare_full_initialization (line 444) | async def run_tushare_full_initialization(
FILE: app/worker/tushare_sync_service.py
function get_utc8_now (line 25) | def get_utc8_now():
class TushareSyncService (line 35) | class TushareSyncService:
method __init__ (line 41) | def __init__(self):
method initialize (line 59) | async def initialize(self):
method sync_stock_basic_info (line 75) | async def sync_stock_basic_info(self, force_update: bool = False, job_...
method _process_basic_info_batch (line 160) | async def _process_basic_info_batch(self, batch: List[Dict[str, Any]],...
method sync_realtime_quotes (line 228) | async def sync_realtime_quotes(self, symbols: List[str] = None, force:...
method _process_quotes_batch (line 420) | async def _process_quotes_batch(self, batch: List[str]) -> Dict[str, A...
method _is_rate_limit_error (line 466) | def _is_rate_limit_error(self, error_msg: str) -> bool:
method _is_trading_time (line 479) | def _is_trading_time(self) -> bool:
method _get_and_save_quotes (line 517) | async def _get_and_save_quotes(self, symbol: str) -> bool:
method sync_historical_data (line 543) | async def sync_historical_data(
method _save_historical_data (line 759) | async def _save_historical_data(self, symbol: str, df, period: str = "...
method _get_last_sync_date (line 780) | async def _get_last_sync_date(self, symbol: str = None) -> str:
method sync_financial_data (line 838) | async def sync_financial_data(self, symbols: List[str] = None, limit: ...
method _save_financial_data (line 949) | async def _save_financial_data(self, symbol: str, financial_data: Dict...
method _is_data_fresh (line 975) | def _is_data_fresh(self, updated_at: datetime, hours: int = 24) -> bool:
method get_sync_status (line 983) | async def get_sync_status(self) -> Dict[str, Any]:
method sync_news_data (line 1021) | async def sync_news_data(
method _process_news_batch (line 1121) | async def _process_news_batch(
method _should_stop (line 1177) | async def _should_stop(self, job_id: str) -> bool:
method _update_progress (line 1203) | async def _update_progress(self, job_id: str, progress: int, message: ...
function get_tushare_sync_service (line 1267) | async def get_tushare_sync_service() -> TushareSyncService:
function run_tushare_basic_info_sync (line 1277) | async def run_tushare_basic_info_sync(force_update: bool = False):
function run_tushare_quotes_sync (line 1289) | async def run_tushare_quotes_sync(force: bool = False):
function run_tushare_historical_sync (line 1306) | async def run_tushare_historical_sync(incremental: bool = True):
function run_tushare_financial_sync (line 1322) | async def run_tushare_financial_sync():
function run_tushare_status_check (line 1334) | async def run_tushare_status_check():
function run_tushare_news_sync (line 1346) | async def run_tushare_news_sync(hours_back: int = 24, max_news_per_stock...
FILE: app/worker/us_data_service.py
class USDataService (line 35) | class USDataService:
method __init__ (line 38) | def __init__(self):
method initialize (line 52) | async def initialize(self):
method get_stock_info (line 56) | async def get_stock_info(
method _get_cached_info (line 116) | async def _get_cached_info(self, code: str, source: str) -> Optional[D...
method _save_to_cache (line 133) | async def _save_to_cache(self, stock_info: Dict[str, Any]) -> bool:
method _normalize_stock_info (line 147) | def _normalize_stock_info(self, stock_info: Dict, source: str) -> Dict:
function get_us_data_service (line 185) | async def get_us_data_service() -> USDataService:
FILE: app/worker/us_sync_service.py
class USSyncService (line 36) | class USSyncService:
method __init__ (line 39) | def __init__(self):
method initialize (line 54) | async def initialize(self):
method _get_finnhub_client (line 58) | def _get_finnhub_client(self):
method _get_us_stock_list_from_finnhub (line 78) | def _get_us_stock_list_from_finnhub(self) -> List[str]:
method _get_fallback_stock_list (line 132) | def _get_fallback_stock_list(self) -> List[str]:
method sync_basic_info_from_source (line 173) | async def sync_basic_info_from_source(
method _normalize_stock_info (line 262) | def _normalize_stock_info(self, stock_info: Dict, source: str) -> Dict:
method sync_quotes_from_source (line 296) | async def sync_quotes_from_source(
function get_us_sync_service (line 389) | async def get_us_sync_service() -> USSyncService:
function run_us_yfinance_basic_info_sync (line 400) | async def run_us_yfinance_basic_info_sync(force_update: bool = False):
function run_us_yfinance_quotes_sync (line 412) | async def run_us_yfinance_quotes_sync():
function run_us_status_check (line 424) | async def run_us_status_check():
FILE: cli/akshare_init.py
function check_database_status (line 34) | async def check_database_status():
function run_full_initialization (line 88) | async def run_full_initialization(
function run_basic_sync_only (line 143) | async def run_basic_sync_only():
function test_akshare_connection (line 165) | async def test_akshare_connection():
function print_help_detail (line 199) | def print_help_detail():
function main (line 259) | async def main():
FILE: cli/baostock_init.py
function print_banner (line 33) | def print_banner():
function print_stats (line 40) | def print_stats(stats):
function test_connection (line 59) | async def test_connection():
function check_database_status (line 79) | async def check_database_status():
function run_full_initialization (line 103) | async def run_full_initialization(historical_days: int = 365, force: boo...
function run_basic_initialization (line 124) | async def run_basic_initialization():
function print_help_detail (line 145) | def print_help_detail():
function main (line 200) | async def main():
FILE: cli/main.py
function setup_cli_logging (line 57) | def setup_cli_logging():
class CLIUserInterface (line 92) | class CLIUserInterface:
method __init__ (line 95) | def __init__(self):
method show_user_message (line 99) | def show_user_message(self, message: str, style: str = ""):
method show_progress (line 106) | def show_progress(self, message: str):
method show_success (line 112) | def show_success(self, message: str):
method show_error (line 117) | def show_error(self, message: str):
method show_warning (line 122) | def show_warning(self, message: str):
method show_step_header (line 127) | def show_step_header(self, step_num: int, title: str):
method show_data_info (line 132) | def show_data_info(self, data_type: str, symbol: str, details: str = ""):
class MessageBuffer (line 152) | class MessageBuffer:
method __init__ (line 153) | def __init__(self, max_length=DEFAULT_MESSAGE_BUFFER_SIZE):
method add_message (line 188) | def add_message(self, message_type, content):
method add_tool_call (line 192) | def add_tool_call(self, tool_name, args):
method update_agent_status (line 196) | def update_agent_status(self, agent, status):
method update_report_section (line 201) | def update_report_section(self, section_name, content):
method _update_current_report (line 206) | def _update_current_report(self):
method _update_final_report (line 235) | def _update_final_report(self):
function create_layout (line 287) | def create_layout():
function update_display (line 307) | def update_display(layout, spinner_text=None):
function get_user_selections (line 520) | def get_user_selections():
function select_market (line 643) | def select_market():
function get_ticker (line 694) | def get_ticker(market):
function get_analysis_date (line 737) | def get_analysis_date():
function display_complete_report (line 757) | def display_complete_report(final_state):
function update_research_team_status (line 947) | def update_research_team_status(status):
function extract_content_string (line 959) | def extract_content_string(content):
function check_api_keys (line 989) | def check_api_keys(llm_provider: str) -> bool:
function run_analysis (line 1028) | def run_analysis():
function analyze (line 1611) | def analyze():
function config (line 1623) | def config():
function version (line 1732) | def version():
function data_config (line 1777) | def data_config(
function examples (line 1874) | def examples():
function test (line 1929) | def test():
function help_chinese (line 1961) | def help_chinese():
function main (line 2022) | def main():
FILE: cli/models.py
class AnalystType (line 10) | class AnalystType(str, Enum):
FILE: cli/tushare_init.py
function print_banner (line 21) | def print_banner():
function print_help (line 29) | def print_help():
function check_database_status (line 82) | async def check_database_status():
function run_basic_initialization (line 135) | async def run_basic_initialization():
function run_full_initialization (line 158) | async def run_full_initialization(historical_days: int, force: bool, mul...
function main (line 209) | async def main():
FILE: cli/utils.py
function get_ticker (line 20) | def get_ticker() -> str:
function get_analysis_date (line 40) | def get_analysis_date() -> str:
function select_analysts (line 73) | def select_analysts(ticker: str = None) -> List[AnalystType]:
function select_research_depth (line 113) | def select_research_depth() -> int:
function select_shallow_thinking_agent (line 145) | def select_shallow_thinking_agent(provider) -> str:
function select_deep_thinking_agent (line 239) | def select_deep_thinking_agent(provider) -> str:
function select_llm_provider (line 336) | def select_llm_provider() -> tuple[str, str]:
FILE: docs/LLM_ADAPTER_TEMPLATE.py
class ChatProviderTemplate (line 17) | class ChatProviderTemplate(OpenAICompatibleBase):
method __init__ (line 20) | def __init__(
FILE: examples/batch_analysis.py
function batch_stock_analysis (line 28) | def batch_stock_analysis():
function generate_summary_report (line 98) | def generate_summary_report(results, llm):
function format_results_for_summary (line 158) | def format_results_for_summary(results):
FILE: examples/cli_demo.py
function run_command (line 16) | def run_command(command, description):
function main (line 40) | def main():
FILE: examples/config_management_demo.py
function demo_model_management (line 22) | def demo_model_management():
function demo_cost_calculation (line 44) | def demo_cost_calculation():
function demo_usage_tracking (line 68) | def demo_usage_tracking():
function demo_usage_statistics (line 125) | def demo_usage_statistics():
function demo_cost_estimation (line 148) | def demo_cost_estimation():
function demo_settings_management (line 204) | def demo_settings_management():
function main (line 222) | def main():
FILE: examples/crawlers/internal_message_crawler.py
class InternalMessageCrawler (line 33) | class InternalMessageCrawler:
method __init__ (line 36) | def __init__(self, source_type: str):
method __aenter__ (line 45) | async def __aenter__(self):
method __aexit__ (line 53) | async def __aexit__(self, exc_type, exc_val, exc_tb):
method clean_content (line 58) | def clean_content(self, text: str) -> str:
method extract_keywords (line 70) | def extract_keywords(self, text: str) -> List[str]:
method analyze_sentiment (line 89) | def analyze_sentiment(self, text: str) -> tuple:
method extract_risk_factors (line 110) | def extract_risk_factors(self, text: str) -> List[str]:
method extract_opportunities (line 126) | def extract_opportunities(self, text: str) -> List[str]:
class ResearchReportCrawler (line 143) | class ResearchReportCrawler(InternalMessageCrawler):
method __init__ (line 146) | def __init__(self):
method crawl_research_reports (line 150) | async def crawl_research_reports(self, symbol: str, limit: int = 10) -...
method _simulate_research_api (line 172) | async def _simulate_research_api(self, symbol: str, limit: int) -> Lis...
method _generate_report_title (line 207) | def _generate_report_title(self, symbol: str, report_type: str) -> str:
method _generate_report_content (line 217) | def _generate_report_content(self, symbol: str, report_type: str) -> str:
method _generate_report_summary (line 237) | def _generate_report_summary(self, symbol: str, report_type: str) -> str:
method _generate_report_tags (line 247) | def _generate_report_tags(self, report_type: str) -> List[str]:
method _standardize_research_report (line 257) | async def _standardize_research_report(self, raw_report: Dict[str, Any...
class AnalystNoteCrawler (line 319) | class AnalystNoteCrawler(InternalMessageCrawler):
method __init__ (line 322) | def __init__(self):
method crawl_analyst_notes (line 326) | async def crawl_analyst_notes(self, symbol: str, limit: int = 20) -> L...
method _simulate_analyst_api (line 348) | async def _simulate_analyst_api(self, symbol: str, limit: int) -> List...
method _generate_note_title (line 378) | def _generate_note_title(self, symbol: str, note_type: str) -> str:
method _generate_note_content (line 388) | def _generate_note_content(self, symbol: str, note_type: str) -> str:
method _generate_note_tags (line 398) | def _generate_note_tags(self, note_type: str) -> List[str]:
method _standardize_analyst_note (line 408) | async def _standardize_analyst_note(self, raw_note: Dict[str, Any], sy...
method _map_category (line 471) | def _map_category(self, note_type: str) -> str:
function crawl_and_save_internal_messages (line 482) | async def crawl_and_save_internal_messages(symbols: List[str], message_t...
function main (line 542) | async def main():
FILE: examples/crawlers/message_crawler_scheduler.py
class MessageCrawlerScheduler (line 39) | class MessageCrawlerScheduler:
method __init__ (line 42) | def __init__(self, config_file: str = None):
method _load_config (line 47) | def _load_config(self) -> Dict[str, Any]:
method run_social_media_crawl (line 108) | async def run_social_media_crawl(self) -> Dict[str, Any]:
method run_internal_message_crawl (line 143) | async def run_internal_message_crawl(self) -> Dict[str, Any]:
method run_full_crawl (line 178) | async def run_full_crawl(self) -> Dict[str, Any]:
method _save_run_log (line 235) | async def _save_run_log(self, summary: Dict[str, Any]):
method get_crawl_statistics (line 268) | async def get_crawl_statistics(self) -> Dict[str, Any]:
method print_config (line 315) | def print_config(self):
function main (line 325) | async def main():
FILE: examples/crawlers/social_media_crawler.py
class SocialMediaCrawler (line 33) | class SocialMediaCrawler:
method __init__ (line 36) | def __init__(self, platform: str):
method __aenter__ (line 44) | async def __aenter__(self):
method __aexit__ (line 52) | async def __aexit__(self, exc_type, exc_val, exc_tb):
method clean_content (line 57) | def clean_content(self, text: str) -> str:
method extract_hashtags (line 71) | def extract_hashtags(self, text: str) -> List[str]:
method extract_mentions (line 76) | def extract_mentions(self, text: str) -> List[str]:
method analyze_sentiment (line 81) | def analyze_sentiment(self, text: str) -> tuple:
method extract_keywords (line 102) | def extract_keywords(self, text: str) -> List[str]:
method assess_importance (line 119) | def assess_importance(self, engagement: Dict[str, Any], author_influen...
method assess_credibility (line 134) | def assess_credibility(self, author: Dict[str, Any], content: str) -> ...
class WeiboCrawler (line 160) | class WeiboCrawler(SocialMediaCrawler):
method __init__ (line 163) | def __init__(self):
method crawl_stock_messages (line 167) | async def crawl_stock_messages(self, symbol: str, limit: int = 50) -> ...
method _simulate_weibo_api (line 189) | async def _simulate_weibo_api(self, symbol: str, limit: int) -> List[D...
method _generate_mock_weibo_text (line 217) | def _generate_mock_weibo_text(self, symbol: str, index: int) -> str:
method _standardize_weibo_message (line 229) | async def _standardize_weibo_message(self, raw_msg: Dict[str, Any], sy...
class DouyinCrawler (line 307) | class DouyinCrawler(SocialMediaCrawler):
method __init__ (line 310) | def __init__(self):
method crawl_stock_messages (line 313) | async def crawl_stock_messages(self, symbol: str, limit: int = 30) -> ...
method _simulate_douyin_api (line 335) | async def _simulate_douyin_api(self, symbol: str, limit: int) -> List[...
method _generate_mock_douyin_text (line 366) | def _generate_mock_douyin_text(self, symbol: str, index: int) -> str:
method _standardize_douyin_message (line 378) | async def _standardize_douyin_message(self, raw_msg: Dict[str, Any], s...
function crawl_and_save_social_media (line 456) | async def crawl_and_save_social_media(symbols: List[str], platforms: Lis...
function main (line 516) | async def main():
FILE: examples/custom_analysis_demo.py
function analyze_stock_custom (line 26) | def analyze_stock_custom(symbol, analysis_focus="comprehensive"):
function interactive_analysis (line 151) | def interactive_analysis():
function batch_analysis_demo (line 218) | def batch_analysis_demo():
function main (line 251) | def main():
FILE: examples/dashscope_examples/demo_dashscope.py
function main (line 26) | def main():
FILE: examples/dashscope_examples/demo_dashscope_chinese.py
function analyze_stock_with_chinese_output (line 26) | def analyze_stock_with_chinese_output(stock_symbol="AAPL", analysis_date...
function compare_models_chinese (line 150) | def compare_models_chinese():
function main (line 176) | def main():
FILE: examples/dashscope_examples/demo_dashscope_no_memory.py
function main (line 26) | def main():
FILE: examples/dashscope_examples/demo_dashscope_simple.py
function test_simple_llm (line 24) | def test_simple_llm():
function test_multiple_models (line 91) | def test_multiple_models():
function main (line 120) | def main():
FILE: examples/data_dir_config_demo.py
function show_current_config (line 27) | def show_current_config():
function demo_set_custom_data_dir (line 72) | def demo_set_custom_data_dir():
function demo_config_integration (line 102) | def demo_config_integration():
function demo_environment_variable_override (line 120) | def demo_environment_variable_override():
function demo_directory_auto_creation (line 138) | def demo_directory_auto_creation():
function show_configuration_guide (line 170) | def show_configuration_guide():
function main (line 213) | def main():
FILE: examples/demo_deepseek_analysis.py
function check_deepseek_config (line 23) | def check_deepseek_config():
function demo_simple_chat (line 46) | def demo_simple_chat():
function demo_reasoning_analysis (line 79) | def demo_reasoning_analysis():
function demo_stock_analysis_with_tools (line 122) | def demo_stock_analysis_with_tools():
function demo_trading_system (line 202) | def demo_trading_system():
function main (line 240) | def main():
FILE: examples/demo_deepseek_simple.py
class SimpleDeepSeekAdapter (line 13) | class SimpleDeepSeekAdapter:
method __init__ (line 16) | def __init__(self):
method chat (line 26) | def chat(self, message: str) -> str:
function demo_simple_chat (line 36) | def demo_simple_chat():
function demo_stock_analysis (line 63) | def demo_stock_analysis():
function main (line 100) | def main():
FILE: examples/demo_news_filtering.py
function demo_basic_filtering (line 13) | def demo_basic_filtering():
function demo_real_news_filtering (line 58) | def demo_real_news_filtering():
function demo_enhanced_filtering (line 106) | def demo_enhanced_filtering():
function demo_integrated_filtering (line 152) | def demo_integrated_filtering():
function main (line 189) | def main():
FILE: examples/enhanced_history_demo.py
function demo_load_analysis_results (line 17) | def demo_load_analysis_results():
function demo_text_similarity (line 49) | def demo_text_similarity():
function demo_report_content_extraction (line 91) | def demo_report_content_extraction():
function demo_stock_grouping (line 148) | def demo_stock_grouping():
function create_demo_data (line 203) | def create_demo_data():
function main (line 238) | def main():
FILE: examples/my_stock_analysis.py
function analyze_my_stock (line 26) | def analyze_my_stock():
FILE: examples/run_message_crawlers.py
function run_social_media_crawler_example (line 23) | async def run_social_media_crawler_example():
function run_internal_message_crawler_example (line 45) | async def run_internal_message_crawler_example():
function run_scheduler_example (line 67) | async def run_scheduler_example():
function query_saved_messages (line 88) | async def query_saved_messages():
function main (line 144) | async def main():
FILE: examples/simple_analysis_demo.py
function quick_analysis_demo (line 19) | def quick_analysis_demo():
function show_analysis_workflow (line 99) | def show_analysis_workflow():
function show_model_comparison (line 161) | def show_model_comparison():
function main (line 196) | def main():
FILE: examples/stock_data_model_usage.py
function demo_basic_info (line 22) | async def demo_basic_info():
function demo_market_quotes (line 52) | async def demo_market_quotes():
function demo_stock_list (line 82) | async def demo_stock_list():
function demo_data_update (line 122) | async def demo_data_update():
function demo_data_validation (line 180) | async def demo_data_validation():
function main (line 239) | async def main():
FILE: examples/stock_list_example.py
function demo_stock_list_fetcher (line 17) | def demo_stock_list_fetcher():
function show_usage_examples (line 75) | def show_usage_examples():
FILE: examples/stock_query_examples.py
function demo_service_status (line 34) | def demo_service_status():
function demo_single_stock_query (line 60) | def demo_single_stock_query():
function demo_stock_search (line 103) | def demo_stock_search():
function demo_market_overview (line 130) | def demo_market_overview():
function demo_stock_data_query (line 160) | def demo_stock_data_query():
function demo_fallback_mechanism (line 187) | def demo_fallback_mechanism():
function main (line 215) | def main():
FILE: examples/test_enhanced_data_integration.py
function test_enhanced_data_adapter (line 21) | def test_enhanced_data_adapter():
function test_optimized_china_data_provider (line 76) | def test_optimized_china_data_provider():
function test_cache_mode_comparison (line 111) | def test_cache_mode_comparison():
function main (line 153) | def main():
FILE: examples/test_installation.py
class InstallationTester (line 17) | class InstallationTester:
method __init__ (line 20) | def __init__(self):
method test_python_version (line 24) | def test_python_version(self) -> bool:
method test_virtual_environment (line 36) | def test_virtual_environment(self) -> bool:
method test_core_modules (line 52) | def test_core_modules(self) -> bool:
method test_dependencies (line 75) | def test_dependencies(self) -> bool:
method test_config_files (line 103) | def test_config_files(self) -> bool:
method test_environment_variables (line 127) | def test_environment_variables(self) -> bool:
method test_web_application (line 169) | def test_web_application(self) -> bool:
method test_data_directories (line 190) | def test_data_directories(self) -> bool:
method run_all_tests (line 214) | def run_all_tests(self) -> Dict[str, bool]:
method print_summary (line 244) | def print_summary(self, test_results: Dict[str, bool]):
function main (line 280) | def main():
FILE: examples/test_news_timeout.py
function test_news_for_stock (line 23) | def test_news_for_stock(ticker):
function main (line 52) | def main():
FILE: examples/token_tracking_demo.py
function print_separator (line 35) | def print_separator(title=""):
function display_config_status (line 43) | def display_config_status():
function display_current_statistics (line 72) | def display_current_statistics():
function demo_basic_usage (line 96) | def demo_basic_usage():
function demo_cost_estimation (line 153) | def demo_cost_estimation():
function demo_mongodb_features (line 178) | def demo_mongodb_features():
function display_pricing_info (line 222) | def display_pricing_info():
function main (line 243) | def main():
FILE: examples/tushare_demo.py
function demo_basic_usage (line 20) | def demo_basic_usage():
function demo_interface_functions (line 79) | def demo_interface_functions():
function demo_batch_operations (line 129) | def demo_batch_operations():
function demo_cache_performance (line 173) | def demo_cache_performance():
function check_environment (line 232) | def check_environment():
function main (line 266) | def main():
FILE: examples/tushare_unified_demo.py
function test_tushare_provider (line 25) | async def test_tushare_provider():
function test_tushare_sync_service (line 133) | async def test_tushare_sync_service():
function performance_test (line 151) | async def performance_test():
function main (line 192) | async def main():
FILE: frontend/env.d.ts
type ImportMetaEnv (line 9) | interface ImportMetaEnv {
type ImportMeta (line 15) | interface ImportMeta {
FILE: frontend/src/api/analysis.ts
type AnalysisRequest (line 9) | interface AnalysisRequest {
type SingleAnalysisRequest (line 23) | interface SingleAnalysisRequest {
type AnalysisProgress (line 40) | interface AnalysisProgress {
type AnalysisStep (line 52) | interface AnalysisStep {
type AnalysisResult (line 63) | interface AnalysisResult {
type AnalysisHistory (line 111) | interface AnalysisHistory {
method startAnalysis (line 121) | startAnalysis(analysisRequest: AnalysisRequest): Promise<{ analysis_id: ...
method startSingleAnalysis (line 126) | startSingleAnalysis(analysisRequest: SingleAnalysisRequest): Promise<Api...
method getTaskStatus (line 131) | getTaskStatus(taskId: string): Promise<ApiResponse<any>> {
method getProgress (line 136) | getProgress(analysisId: string): Promise<AnalysisProgress> {
method getResult (line 141) | getResult(analysisId: string): Promise<AnalysisResult> {
method stopAnalysis (line 146) | stopAnalysis(analysisId: string): Promise<{ message: string }> {
method getHistory (line 151) | getHistory(params?: {
method deleteAnalysis (line 165) | deleteAnalysis(analysisId: string): Promise<{ message: string }> {
method exportAnalysis (line 170) | exportAnalysis(analysisId: string, format: 'pdf' | 'excel' | 'json' = 'p...
method startBatchAnalysis (line 178) | startBatchAnalysis(batchRequest: {
method getBatch (line 189) | getBatch(batchId: string): Promise<any> {
method getTaskDetails (line 194) | getTaskDetails(taskId: string): Promise<any> {
method getTaskList (line 199) | getTaskList(params?: { status?: string; limit?: number; offset?: number ...
method getTaskResult (line 204) | getTaskResult(taskId: string): Promise<any>{
method markTaskAsFailed (line 209) | markTaskAsFailed(taskId: string): Promise<{ success: boolean; message: s...
method deleteTask (line 214) | deleteTask(taskId: string): Promise<{ success: boolean; message: string ...
method shareAnalysis (line 219) | shareAnalysis(analysisId: string, options: {
method getStockInfo (line 228) | getStockInfo(symbol: string, market: string): Promise<{
method searchStocks (line 247) | searchStocks(query: string, market?: string): Promise<Array<{
method getPopularStocks (line 259) | getPopularStocks(market?: string, limit: number = 10): Promise<Array<{
method getAnalysisStats (line 274) | getAnalysisStats(params?: {
constant MARKET_TYPES (line 304) | const MARKET_TYPES = {
constant ANALYSIS_TYPES (line 310) | const ANALYSIS_TYPES = {
constant DATA_SOURCES (line 324) | const DATA_SOURCES = {
constant ANALYSIS_STATUS (line 350) | const ANALYSIS_STATUS = {
constant STEP_STATUS (line 358) | const STEP_STATUS = {
FILE: frontend/src/api/cache.ts
type CacheStats (line 9) | interface CacheStats {
type CacheDetailItem (line 21) | interface CacheDetailItem {
type CacheDetailsResponse (line 33) | interface CacheDetailsResponse {
type CacheBackendInfo (line 43) | interface CacheBackendInfo {
function getCacheStats (line 54) | function getCacheStats() {
function cleanupOldCache (line 65) | function cleanupOldCache(days: number) {
function clearAllCache (line 76) | function clearAllCache() {
function getCacheDetails (line 88) | function getCacheDetails(page: number = 1, pageSize: number = 20) {
function getCacheBackendInfo (line 99) | function getCacheBackendInfo() {
FILE: frontend/src/api/config.ts
type LLMProvider (line 10) | interface LLMProvider {
type LLMConfig (line 34) | interface LLMConfig {
type DataSourceConfig (line 67) | interface DataSourceConfig {
type MarketCategory (line 88) | interface MarketCategory {
type DataSourceGrouping (line 100) | interface DataSourceGrouping {
type DatabaseConfig (line 109) | interface DatabaseConfig {
type SystemConfig (line 124) | interface SystemConfig {
type ConfigTestRequest (line 139) | interface ConfigTestRequest {
type ConfigTestResponse (line 144) | interface ConfigTestResponse {
type SettingMeta (line 152) | interface SettingMeta {
method getSystemConfig (line 163) | getSystemConfig(): Promise<SystemConfig> {
method getLLMProviders (line 170) | getLLMProviders(): Promise<LLMProvider[]> {
method addLLMProvider (line 175) | addLLMProvider(provider: Partial<LLMProvider>): Promise<{ message: strin...
method updateLLMProvider (line 180) | updateLLMProvider(id: string, provider: Partial<LLMProvider>): Promise<{...
method deleteLLMProvider (line 185) | deleteLLMProvider(id: string): Promise<{ message: string }> {
method toggleLLMProvider (line 190) | toggleLLMProvider(id: string, isActive: boolean): Promise<{ message: str...
method migrateEnvToProviders (line 195) | migrateEnvToProviders(): Promise<{ message: string; data: any }> {
method initAggregatorProviders (line 200) | initAggregatorProviders(): Promise<{ success: boolean; message: string; ...
method testProviderAPI (line 205) | testProviderAPI(providerId: string): Promise<{ success: boolean; message...
method getAvailableModels (line 210) | getAvailableModels(): Promise<Array<{
method getModelCatalog (line 221) | getModelCatalog(): Promise<Array<{
method getProviderModelCatalog (line 242) | getProviderModelCatalog(provider: string): Promise<{
method saveModelCatalog (line 263) | saveModelCatalog(catalog: {
method deleteModelCatalog (line 272) | deleteModelCatalog(provider: string): Promise<{ success: boolean; messag...
method initModelCatalog (line 277) | initModelCatalog(): Promise<{ success: boolean; message: string }> {
method fetchProviderModels (line 282) | fetchProviderModels(provider: string): Promise<{
method getLLMConfigs (line 297) | getLLMConfigs(): Promise<LLMConfig[]> {
method updateLLMConfig (line 302) | updateLLMConfig(config: Partial<LLMConfig>): Promise<{ message: string; ...
method deleteLLMConfig (line 307) | deleteLLMConfig(provider: string, modelName: string): Promise<{ message:...
method setDefaultLLM (line 312) | setDefaultLLM(name: string): Promise<{ message: string; default_llm: str...
method getDataSourceConfigs (line 317) | getDataSourceConfigs(): Promise<DataSourceConfig[]> {
method addDataSourceConfig (line 322) | addDataSourceConfig(config: Partial<DataSourceConfig>): Promise<{ messag...
method setDefaultDataSource (line 327) | setDefaultDataSource(name: string): Promise<{ message: string; default_d...
method updateDataSourceConfig (line 332) | updateDataSourceConfig(name: string, config: Partial<DataSourceConfig>):...
method deleteDataSourceConfig (line 337) | deleteDataSourceConfig(name: string): Promise<{ message: string }> {
method getMarketCategories (line 342) | getMarketCategories(): Promise<MarketCategory[]> {
method addMarketCategory (line 346) | addMarketCategory(category: Partial<MarketCategory>): Promise<{ message:...
method updateMarketCategory (line 350) | updateMarketCategory(id: string, category: Partial<MarketCategory>): Pro...
method deleteMarketCategory (line 354) | deleteMarketCategory(id: string): Promise<{ message: string }> {
method getDataSourceGroupings (line 359) | getDataSourceGroupings(): Promise<DataSourceGrouping[]> {
method addDataSourceToCategory (line 363) | addDataSourceToCategory(dataSourceName: string, categoryId: string, prio...
method removeDataSourceFromCategory (line 372) | removeDataSourceFromCategory(dataSourceName: string, categoryId: string)...
method updateDataSourceGrouping (line 376) | updateDataSourceGrouping(dataSourceName: string, categoryId: string, upd...
method updateCategoryDataSourceOrder (line 381) | updateCategoryDataSourceOrder(categoryId: string, orderedDataSources: Ar...
method getSystemSettingsMeta (line 388) | getSystemSettingsMeta(): Promise<{ items: SettingMeta[] }> {
method getDatabaseConfigs (line 396) | getDatabaseConfigs(): Promise<DatabaseConfig[]> {
method getDatabaseConfig (line 401) | getDatabaseConfig(dbName: string): Promise<DatabaseConfig> {
method addDatabaseConfig (line 406) | addDatabaseConfig(config: Partial<DatabaseConfig>): Promise<{ success: b...
method updateDatabaseConfig (line 411) | updateDatabaseConfig(dbName: string, config: Partial<DatabaseConfig>): P...
method deleteDatabaseConfig (line 416) | deleteDatabaseConfig(dbName: string): Promise<{ success: boolean; messag...
method testDatabaseConfig (line 421) | testDatabaseConfig(dbName: string): Promise<ConfigTestResponse> {
method getSystemSettings (line 426) | getSystemSettings(): Promise<Record<string, any>> {
method getDefaultModels (line 431) | getDefaultModels(): Promise<{ quick_analysis_model: string; deep_analysi...
method updateSystemSettings (line 439) | updateSystemSettings(settings: Record<string, any>): Promise<{ message: ...
method testConfig (line 444) | testConfig(testRequest: ConfigTestRequest): Promise<ConfigTestResponse> {
method exportConfig (line 449) | exportConfig(): Promise<{ message: string; data: any; exported_at: strin...
method importConfig (line 454) | importConfig(configData: Record<string, any>): Promise<{ message: string...
method migrateLegacyConfig (line 459) | migrateLegacyConfig(): Promise<{ message: string }> {
method reloadConfig (line 464) | reloadConfig(): Promise<{ success: boolean; message: string; data?: any ...
constant CONFIG_PROVIDERS (line 470) | const CONFIG_PROVIDERS = {
constant DATA_SOURCE_TYPES (line 484) | const DATA_SOURCE_TYPES = {
constant DATABASE_TYPES (line 509) | const DATABASE_TYPES = {
constant DEFAULT_LLM_CONFIG (line 517) | const DEFAULT_LLM_CONFIG: Partial<LLMConfig> = {
constant DEFAULT_DATA_SOURCE_CONFIG (line 525) | const DEFAULT_DATA_SOURCE_CONFIG: Partial<DataSourceConfig> = {
constant DEFAULT_MARKET_CATEGORIES (line 535) | const DEFAULT_MARKET_CATEGORIES: Partial<MarketCategory>[] = [
constant DEFAULT_DATABASE_CONFIG (line 578) | const DEFAULT_DATABASE_CONFIG: Partial<DatabaseConfig> = {
FILE: frontend/src/api/database.ts
type DatabaseStatus (line 8) | interface DatabaseStatus {
type DatabaseStats (line 37) | interface DatabaseStats {
type BackupInfo (line 52) | interface BackupInfo {
type ConnectionTestResult (line 63) | interface ConnectionTestResult {
method getStatus (line 82) | async getStatus(): Promise<DatabaseStatus> {
method getStats (line 88) | async getStats(): Promise<DatabaseStats> {
method testConnections (line 94) | testConnections(): Promise<{ success: boolean; message: string; data: Co...
method createBackup (line 99) | createBackup(data: {
method getBackups (line 107) | getBackups(): Promise<{ success: boolean; data: BackupInfo[] }> {
method deleteBackup (line 112) | deleteBackup(backupId: string): Promise<{ success: boolean; message: str...
method importData (line 117) | importData(
method exportData (line 152) | exportData(options: {
method cleanupOldData (line 163) | cleanupOldData(days: number = 30): Promise<{
method cleanupAnalysisResults (line 176) | cleanupAnalysisResults(days: number = 30): Promise<{
method cleanupOperationLogs (line 189) | cleanupOperationLogs(days: number = 90): Promise<{
FILE: frontend/src/api/favorites.ts
type FavoriteItem (line 3) | interface FavoriteItem {
type AddFavoriteReq (line 20) | interface AddFavoriteReq {
FILE: frontend/src/api/logs.ts
type LogFileInfo (line 7) | interface LogFileInfo {
type LogContentResponse (line 16) | interface LogContentResponse {
type LogStatistics (line 29) | interface LogStatistics {
type LogReadRequest (line 37) | interface LogReadRequest {
type LogExportRequest (line 46) | interface LogExportRequest {
method listLogFiles (line 58) | listLogFiles(): Promise<LogFileInfo[]> {
method readLogFile (line 65) | readLogFile(request: LogReadRequest): Promise<LogContentResponse> {
method exportLogs (line 72) | async exportLogs(request: LogExportRequest): Promise<Blob> {
method getStatistics (line 82) | getStatistics(days: number = 7): Promise<LogStatistics> {
method deleteLogFile (line 89) | deleteLogFile(filename: string): Promise<{ success: boolean; message: st...
FILE: frontend/src/api/modelCapabilities.ts
type ModelCapabilityInfo (line 10) | interface ModelCapabilityInfo {
type ModelRecommendationResponse (line 27) | interface ModelRecommendationResponse {
type ModelValidationResponse (line 38) | interface ModelValidationResponse {
type BadgeStyle (line 47) | interface BadgeStyle {
type AllBadges (line 56) | interface AllBadges {
type DepthRequirement (line 65) | interface DepthRequirement {
function getDefaultModelConfigs (line 76) | function getDefaultModelConfigs() {
function getDepthRequirements (line 86) | function getDepthRequirements() {
function getCapabilityDescriptions (line 96) | function getCapabilityDescriptions() {
function getAllBadges (line 106) | function getAllBadges() {
function recommendModels (line 117) | function recommendModels(researchDepth: string) {
function validateModels (line 133) | function validateModels(quickModel: string, deepModel: string, researchD...
function batchInitCapabilities (line 149) | function batchInitCapabilities(overwrite: boolean = false) {
function getModelCapability (line 163) | function getModelCapability(modelName: string) {
FILE: frontend/src/api/multiMarket.ts
type Market (line 7) | interface Market {
type StockInfo (line 16) | interface StockInfo {
type StockQuote (line 33) | interface StockQuote {
type DailyQuote (line 48) | interface DailyQuote {
function getSupportedMarkets (line 61) | function getSupportedMarkets() {
function searchStocks (line 71) | function searchStocks(market: string, query: string, limit: number = 20) {
function getStockInfo (line 82) | function getStockInfo(market: string, code: string, source?: string) {
function getStockQuote (line 93) | function getStockQuote(market: string, code: string) {
function getStockDailyQuotes (line 103) | function getStockDailyQuotes(
FILE: frontend/src/api/news.ts
type NewsItem (line 6) | interface NewsItem {
type LatestNewsResponse (line 24) | interface LatestNewsResponse {
type NewsQueryResponse (line 35) | interface NewsQueryResponse {
type NewsSyncResponse (line 45) | interface NewsSyncResponse {
method getLatestNews (line 63) | async getLatestNews(symbol?: string, limit: number = 10, hours_back: num...
method queryStockNews (line 77) | async queryStockNews(symbol: string, hours_back: number = 24, limit: num...
method syncMarketNews (line 89) | async syncMarketNews(hours_back: number = 24, max_news_per_source: numbe...
FILE: frontend/src/api/notifications.ts
type NotificationItem (line 3) | interface NotificationItem {
type NotificationListResponse (line 14) | interface NotificationListResponse {
method getUnreadCount (line 22) | async getUnreadCount(): Promise<{ success: boolean; data: { count: numbe...
method getList (line 31) | async getList(params?: { status?: 'unread' | 'all'; page?: number; page_...
method markRead (line 45) | async markRead(id: string): Promise<{ success: boolean }> {
method markAllRead (line 53) | async markAllRead(): Promise<{ success: boolean }> {
FILE: frontend/src/api/operationLogs.ts
type OperationLog (line 8) | interface OperationLog {
type OperationLogQuery (line 26) | interface OperationLogQuery {
type OperationLogListResponse (line 37) | interface OperationLogListResponse {
type OperationLogStats (line 50) | interface OperationLogStats {
type OperationLogStatsResponse (line 63) | interface OperationLogStatsResponse {
type CreateOperationLogRequest (line 70) | interface CreateOperationLogRequest {
type ClearLogsRequest (line 81) | interface ClearLogsRequest {
type ClearLogsResponse (line 87) | interface ClearLogsResponse {
class OperationLogsApi (line 97) | class OperationLogsApi {
method getOperationLogs (line 101) | static getOperationLogs(params: OperationLogQuery = {}): Promise<Opera...
method getOperationLogStats (line 119) | static getOperationLogStats(days: number = 30): Promise<OperationLogSt...
method getOperationLogDetail (line 126) | static getOperationLogDetail(logId: string): Promise<{
method createOperationLog (line 137) | static createOperationLog(data: CreateOperationLogRequest): Promise<{
method clearOperationLogs (line 148) | static clearOperationLogs(data: ClearLogsRequest = {}): Promise<ClearL...
method exportOperationLogsCSV (line 155) | static exportOperationLogsCSV(params: {
FILE: frontend/src/api/paper.ts
type CurrencyAmount (line 3) | interface CurrencyAmount {
type PaperAccountSummary (line 9) | interface PaperAccountSummary {
type PaperPositionItem (line 17) | interface PaperPositionItem {
type PaperOrderItem (line 26) | interface PaperOrderItem {
type GetAccountResponse (line 38) | interface GetAccountResponse {
type PlaceOrderPayload (line 43) | interface PlaceOrderPayload {
method getAccount (line 51) | async getAccount() {
method placeOrder (line 54) | async placeOrder(data: PlaceOrderPayload) {
method getPositions (line 57) | async getPositions() {
method getOrders (line 60) | async getOrders(limit = 50) {
method resetAccount (line 63) | async resetAccount() {
FILE: frontend/src/api/request.ts
type ApiResponse (line 9) | interface ApiResponse<T = any> {
type RequestConfig (line 19) | interface RequestConfig extends AxiosRequestConfig {
constant MESSAGE_THROTTLE_TIME (line 31) | const MESSAGE_THROTTLE_TIME = 3000 // 3秒内相同消息不重复显示
class ApiClient (line 492) | class ApiClient {
method get (line 494) | static async get<T = any>(
method post (line 504) | static async post<T = any>(
method put (line 514) | static async put<T = any>(
method delete (line 524) | static async delete<T = any>(
method patch (line 533) | static async patch<T = any>(
method upload (line 543) | static async upload<T = any>(
method download (line 568) | static async download(
FILE: frontend/src/api/scheduler.ts
type Job (line 7) | interface Job {
type JobHistory (line 22) | interface JobHistory {
type JobExecution (line 30) | interface JobExecution {
type JobExecutionStats (line 51) | interface JobExecutionStats {
type SchedulerStats (line 64) | interface SchedulerStats {
type SchedulerHealth (line 72) | interface SchedulerHealth {
function getJobs (line 82) | function getJobs() {
function getJobDetail (line 89) | function getJobDetail(jobId: string) {
function pauseJob (line 96) | function pauseJob(jobId: string) {
function resumeJob (line 103) | function resumeJob(jobId: string) {
function triggerJob (line 110) | function triggerJob(jobId: string, force: boolean = true) {
function getJobHistory (line 117) | function getJobHistory(jobId: string, params?: { limit?: number; offset?...
function getAllHistory (line 129) | function getAllHistory(params?: {
function getSchedulerStats (line 146) | function getSchedulerStats() {
function getSchedulerHealth (line 153) | function getSchedulerHealth() {
function updateJobMetadata (line 160) | function updateJobMetadata(
function getJobExecutions (line 170) | function getJobExecutions(params?: {
function getSingleJobExecutions (line 188) | function getSingleJobExecutions(
function getJobExecutionStats (line 208) | function getJobExecutionStats(jobId: string) {
function cancelExecution (line 215) | function cancelExecution(executionId: string) {
function markExecutionFailed (line 222) | function markExecutionFailed(executionId: string, reason?: string) {
function deleteExecution (line 231) | function deleteExecution(executionId: string) {
FILE: frontend/src/api/screening.ts
type ScreeningOrderBy (line 3) | interface ScreeningOrderBy { field: string; direction: 'asc' | 'desc' }
type ScreeningRunReq (line 4) | interface ScreeningRunReq {
type ScreeningRunItem (line 14) | interface ScreeningRunItem {
type ScreeningRunResp (line 29) | interface ScreeningRunResp { total: number; items: ScreeningRunItem[] }
type FieldInfo (line 32) | interface FieldInfo {
type FieldConfigResponse (line 41) | interface FieldConfigResponse {
type IndustryOption (line 47) | interface IndustryOption {
type IndustriesResponse (line 53) | interface IndustriesResponse {
FILE: frontend/src/api/stockSync.ts
type SingleStockSyncRequest (line 7) | interface SingleStockSyncRequest {
type BatchStockSyncRequest (line 17) | interface BatchStockSyncRequest {
type SyncResult (line 26) | interface SyncResult {
type SingleStockSyncResponse (line 33) | interface SingleStockSyncResponse {
type BatchStockSyncResponse (line 41) | interface BatchStockSyncResponse {
type StockSyncStatus (line 64) | interface StockSyncStatus {
method syncSingle (line 82) | syncSingle(request: SingleStockSyncRequest) {
method syncBatch (line 91) | syncBatch(request: BatchStockSyncRequest) {
method getStatus (line 100) | getStatus(symbol: string) {
FILE: frontend/src/api/stocks.ts
type QuoteResponse (line 3) | interface QuoteResponse {
type FundamentalsResponse (line 19) | interface FundamentalsResponse {
type KlineBar (line 45) | interface KlineBar {
type KlineResponse (line 55) | interface KlineResponse {
type NewsItem (line 65) | interface NewsItem {
type NewsResponse (line 73) | interface NewsResponse {
method getQuote (line 88) | async getQuote(symbol: string) {
method getFundamentals (line 96) | async getFundamentals(symbol: string) {
method getKline (line 107) | async getKline(symbol: string, period: KlineResponse['period'] = 'day', ...
method getNews (line 118) | async getNews(symbol: string, days = 30, limit = 50, includeAnnouncement...
FILE: frontend/src/api/sync.ts
type DataSourceStatus (line 7) | interface DataSourceStatus {
type SyncStatus (line 16) | interface SyncStatus {
type SyncRequest (line 32) | interface SyncRequest {
type ApiResponse (line 38) | interface ApiResponse<T = any> {
type BaseTestResult (line 45) | interface BaseTestResult {
type DataSourceTestResult (line 53) | interface DataSourceTestResult {
type SyncRecommendations (line 62) | interface SyncRecommendations {
FILE: frontend/src/api/tags.ts
type TagItem (line 4) | interface TagItem {
type CreateTagDto (line 13) | interface CreateTagDto {
type UpdateTagDto (line 19) | interface UpdateTagDto {
method list (line 26) | async list(): Promise<ApiResponse<TagItem[]>> {
method create (line 29) | async create(payload: CreateTagDto): Promise<ApiResponse<TagItem>> {
method update (line 32) | async update(id: string, payload: UpdateTagDto): Promise<ApiResponse<{ i...
method remove (line 35) | async remove(id: string): Promise<ApiResponse<{ id: string }>> {
FILE: frontend/src/api/templates.ts
type TemplateItem (line 3) | interface TemplateItem {
type CreateTemplatePayload (line 13) | interface CreateTemplatePayload {
type UpdateTemplatePayload (line 20) | interface UpdateTemplatePayload extends Partial<CreateTemplatePayload> {}
method list (line 23) | list(params?: Record<string, any>) {
method get (line 27) | get(id: string) {
method create (line 31) | create(payload: CreateTemplatePayload) {
method update (line 35) | update(id: string, payload: UpdateTemplatePayload) {
method remove (line 39) | remove(id: string) {
method listAgentTemplates (line 43) | listAgentTemplates(params?: Record<string, any>) {
FILE: frontend/src/api/usage.ts
type UsageRecord (line 8) | interface UsageRecord {
type UsageStatistics (line 21) | interface UsageStatistics {
function getUsageRecords (line 35) | function getUsageRecords(params?: {
function getUsageStatistics (line 51) | function getUsageStatistics(params?: {
function getCostByProvider (line 65) | function getCostByProvider(days: number = 7) {
function getCostByModel (line 76) | function getCostByModel(days: number = 7) {
function getDailyCost (line 87) | function getDailyCost(days: number = 7) {
function deleteOldRecords (line 98) | function deleteOldRecords(days: number = 90) {
FILE: frontend/src/components/index.ts
function setupGlobalComponents (line 6) | function setupGlobalComponents(app: App) {
FILE: frontend/src/constants/analysts.ts
type Analyst (line 5) | interface Analyst {
constant ANALYSTS (line 13) | const ANALYSTS: Analyst[] = [
constant ANALYST_NAMES (line 41) | const ANALYST_NAMES = ANALYSTS.map(analyst => analyst.name)
constant DEFAULT_ANALYSTS (line 44) | const DEFAULT_ANALYSTS = ['市场分析师', '基本面分析师']
constant ANALYST_NAME_TO_ID_MAP (line 62) | const ANALYST_NAME_TO_ID_MAP: Record<string, string> = {
constant MODEL_TO_PROVIDER_MAP (line 83) | const MODEL_TO_PROVIDER_MAP: Record<string, string> = {
FILE: frontend/src/router/index.ts
method scrollBehavior (line 401) | scrollBehavior(to, from, savedPosition) {
FILE: frontend/src/stores/app.ts
type AppState (line 5) | interface AppState {
method isDarkTheme (line 76) | isDarkTheme(): boolean {
method actualSidebarWidth (line 84) | actualSidebarWidth(): number {
method currentPageTitle (line 89) | currentPageTitle(): string {
method appInfo (line 94) | appInfo(): Record<string, any> {
method setLoading (line 107) | setLoading(loading: boolean, progress = 0) {
method setLoadingProgress (line 113) | setLoadingProgress(progress: number) {
method toggleTheme (line 118) | toggleTheme() {
method setTheme (line 126) | setTheme(theme: 'light' | 'dark' | 'auto') {
method applyTheme (line 134) | applyTheme() {
method setLanguage (line 146) | setLanguage(language: 'zh-CN' | 'en-US') {
method toggleSidebar (line 154) | toggleSidebar() {
method setSidebarCollapsed (line 159) | setSidebarCollapsed(collapsed: boolean) {
method setSidebarWidth (line 166) | setSidebarWidth(width: number) {
method setCurrentRoute (line 173) | setCurrentRoute(route: RouteLocationNormalized) {
method updatePreferences (line 178) | updatePreferences(preferences: Partial<AppState['preferences']>) {
method resetPreferences (line 185) | resetPreferences() {
method setOnlineStatus (line 196) | setOnlineStatus(isOnline: boolean) {
method setApiConnected (line 201) | setApiConnected(connected: boolean) {
method checkApiConnection (line 207) | async checkApiConnection() {
method fetchApiVersion (line 234) | async fetchApiVersion() {
method resetAppState (line 265) | resetAppState() {
FILE: frontend/src/stores/auth.ts
type AuthState (line 6) | interface AuthState {
method userAvatar (line 71) | userAvatar(): string | undefined {
method userDisplayName (line 76) | userDisplayName(): string {
method isAdmin (line 81) | isAdmin(): boolean {
method hasPermission (line 86) | hasPermission(): (permission: string) => boolean {
method hasRole (line 93) | hasRole(): (role: string) => boolean {
method userStats (line 100) | userStats(): Record<string, number> {
method setAuthInfo (line 113) | setAuthInfo(token: string, refreshToken?: string, user?: User) {
method clearAuthInfo (line 146) | clearAuthInfo() {
method redirectToLogin (line 164) | redirectToLogin() {
method setAuthHeader (line 177) | setAuthHeader(token: string | null) {
method login (line 183) | async login(loginForm: LoginForm) {
method register (line 228) | async register(registerForm: RegisterForm) {
method logout (line 247) | async logout() {
method refreshAccessToken (line 264) | async refreshAccessToken() {
method fetchUserInfo (line 316) | async fetchUserInfo() {
method fetchUserPermissions (line 341) | async fetchUserPermissions() {
method updateUserInfo (line 348) | async updateUserInfo(userInfo: Partial<User>) {
method syncUserPreferencesToAppStore (line 372) | syncUserPreferencesToAppStore() {
method changePassword (line 410) | async changePassword(oldPassword: string, newPassword: string) {
method setRedirectPath (line 432) | setRedirectPath(path: string) {
method getAndClearRedirectPath (line 437) | getAndClearRedirectPath(): string {
method checkAuthStatus (line 444) | async checkAuthStatus() {
FILE: frontend/src/stores/notifications.ts
function refreshUnreadCount (line 24) | async function refreshUnreadCount() {
function loadList (line 33) | async function loadList(status: 'unread' | 'all' = 'all') {
function markRead (line 45) | async function markRead(id: string) {
function markAllRead (line 52) | async function markAllRead() {
function addNotification (line 58) | function addNotification(n: Omit<NotificationItem, 'id' | 'status' | 'cr...
function connectWebSocket (line 76) | function connectWebSocket() {
function handleWebSocketMessage (line 148) | function handleWebSocketMessage(message: any) {
function disconnectWebSocket (line 182) | function disconnectWebSocket() {
function connect (line 198) | function connect() {
function disconnect (line 204) | function disconnect() {
function setDrawerVisible (line 209) | function setDrawerVisible(v: boolean) {
FILE: frontend/src/types/analysis.ts
type AnalysisStatus (line 2) | enum AnalysisStatus {
type BatchStatus (line 11) | enum BatchStatus {
type AnalysisParameters (line 21) | interface AnalysisParameters {
type AnalysisResult (line 32) | interface AnalysisResult {
type AnalysisTask (line 47) | interface AnalysisTask {
type AnalysisBatch (line 76) | interface AnalysisBatch {
type StockInfo (line 104) | interface StockInfo {
type SingleAnalysisRequest (line 146) | interface SingleAnalysisRequest {
type BatchAnalysisRequest (line 153) | interface BatchAnalysisRequest {
type AnalysisTaskResponse (line 162) | interface AnalysisTaskResponse {
type AnalysisBatchResponse (line 177) | interface AnalysisBatchResponse {
type AnalysisHistoryQuery (line 193) | interface AnalysisHistoryQuery {
type AnalysisHistoryResponse (line 205) | interface AnalysisHistoryResponse {
type TaskProgress (line 215) | interface TaskProgress {
type BatchProgress (line 224) | interface BatchProgress {
type AnalysisStats (line 235) | interface AnalysisStats {
type QueueStatus (line 247) | interface QueueStatus {
type UserQueueStatus (line 256) | interface UserQueueStatus {
type AnalysisReport (line 264) | interface AnalysisReport {
FILE: frontend/src/types/auth.ts
type User (line 2) | interface User {
type UserPreferences (line 28) | interface UserPreferences {
type LoginForm (line 52) | interface LoginForm {
type RegisterForm (line 60) | interface RegisterForm {
type ChangePasswordForm (line 71) | interface ChangePasswordForm {
type ResetPasswordForm (line 78) | interface ResetPasswordForm {
type UserPermissions (line 84) | interface UserPermissions {
type LoginResponse (line 90) | interface LoginResponse {
type RefreshTokenResponse (line 99) | interface RefreshTokenResponse {
type UserSession (line 106) | interface UserSession {
type UserStats (line 117) | interface UserStats {
type UserActivity (line 129) | interface UserActivity {
type UserConfigUpdate (line 141) | interface UserConfigUpdate {
type CaptchaInfo (line 148) | interface CaptchaInfo {
type InvitationCode (line 155) | interface InvitationCode {
FILE: frontend/src/types/config.ts
type LLMProvider (line 4) | interface LLMProvider {
type LLMConfig
Copy disabled (too large)
Download .json
Condensed preview — 1890 files, each showing path, character count, and a content snippet. Download the .json file for the full structured content (14,734K chars).
[
{
"path": ".dockerignore",
"chars": 1643,
"preview": "# TradingAgents-CN Docker构建忽略文件\n# 用于减小Docker镜像大小和加快构建速度\n#\n# 注意:此文件同时用于后端和前端镜像构建\n# 前端构建需要保留 frontend/ 目录下的源代码和配置文件\n\n# Git"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 2762,
"preview": "---\nname: 🐛 Bug报告 / Bug Report\nabout: 报告一个问题帮助我们改进 / Report a bug to help us improve\ntitle: '[BUG] '\nlabels: ['bug', 'ne"
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 956,
"preview": "blank_issues_enabled: false\ncontact_links:\n - name: 📖 项目文档 / Project Documentation\n url: https://github.com/hsliupin"
},
{
"path": ".github/ISSUE_TEMPLATE/documentation.md",
"chars": 3197,
"preview": "---\nname: 📚 文档改进 / Documentation Improvement\nabout: 报告文档问题或建议改进 / Report documentation issues or suggest improvements\nti"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.md",
"chars": 2361,
"preview": "---\nname: ✨ 功能请求 / Feature Request\nabout: 建议一个新功能或改进 / Suggest a new feature or improvement\ntitle: '[FEATURE] '\nlabels: "
},
{
"path": ".github/ISSUE_TEMPLATE/question.md",
"chars": 2598,
"preview": "---\nname: ❓ 问题咨询 / Question\nabout: 使用问题或技术咨询 / Usage questions or technical consultation\ntitle: '[QUESTION] '\nlabels: ['"
},
{
"path": ".github/pull_request_template.md",
"chars": 2989,
"preview": "# Pull Request 模板\n\n## 📋 PR 类型\n请标记此 PR 的类型:\n\n- [ ] 🌟 新功能 (feature)\n- [ ] 🐛 Bug 修复 (bugfix)\n- [ ] 🧹 代码重构 (refactor)\n- [ ] "
},
{
"path": ".github/workflows/docker-publish.yml",
"chars": 4266,
"preview": "name: Docker Publish to Docker Hub\n\non:\n push:\n tags:\n - 'v*'\n workflow_dispatch:\n\njobs:\n build-and-push:\n "
},
{
"path": ".github/workflows/upstream-sync-check.yml",
"chars": 7662,
"preview": "name: 上游同步检查\n\non:\n schedule:\n # 每周一上午9点检查上游更新\n - cron: '0 9 * * 1'\n workflow_dispatch:\n # 允许手动触发\n inputs:\n"
},
{
"path": ".python-version",
"chars": 5,
"preview": "3.10\n"
},
{
"path": ".streamlit/config.toml",
"chars": 485,
"preview": "[server]\n# 服务器配置\nport = 8501\naddress = \"0.0.0.0\" # Docker环境需要监听所有接口\nheadless = true # Docker环境无头模式\nenableCORS = fa"
},
{
"path": "ACKNOWLEDGMENTS.md",
"chars": 3794,
"preview": "# 致敬与感谢 | Acknowledgments\n\n## 🌟 向源项目开发者致以最崇高的敬意\n\n### [Tauric Research](https://github.com/TauricResearch) 团队\n\n我们向 **Taur"
},
{
"path": "COMMERCIAL_LICENSE_TEMPLATE.md",
"chars": 1907,
"preview": "# TradingAgents-CN 商业许可证模板\n# TradingAgents-CN Commercial License Template\n\n## 商业软件许可协议\n## Commercial Software License Ag"
},
{
"path": "CONTRIBUTORS.md",
"chars": 3674,
"preview": "# 🤝 贡献者名单\n\n感谢所有为TradingAgents-CN项目做出贡献的开发者和用户!\n\n## 🌟 贡献者分类\n\n### 🐳 Docker容器化功能\n\n- **[@breeze303](https://github.com/breez"
},
{
"path": "COPYRIGHT.md",
"chars": 2524,
"preview": "# TradingAgents-CN 版权信息\n# TradingAgents-CN Copyright Information\n\n## 📋 版权声明 / Copyright Notice\n\n### 专有组件 / Proprietary C"
},
{
"path": "Dockerfile.backend",
"chars": 2709,
"preview": "# Backend Dockerfile for FastAPI service (TradingAgents-CN v1.0.0-preview)\n# 前后端分离架构 - 后端服务\n# 支持多架构: amd64, arm64\n\n# 使用 "
},
{
"path": "Dockerfile.frontend",
"chars": 1079,
"preview": "# Frontend Dockerfile for Vue 3 + Vite app (TradingAgents-CN v1.0.0-preview)\n# 前后端分离架构 - 前端服务\n\n# 构建阶段:使用Node.js 22.x(与项目"
},
{
"path": "LICENSE",
"chars": 12399,
"preview": "TradingAgents-CN - Mixed License Project\nTradingAgents-CN - 混合许可证项目\n\nThis project uses multiple licenses for different c"
},
{
"path": "LICENSING.md",
"chars": 1945,
"preview": "# TradingAgents-CN 许可证说明\n\n## 📋 许可证概述\n\nTradingAgents-CN 项目采用**混合许可证策略**,不同组件使用不同的许可证:\n\n## 🔓 开源组件 (Apache License 2.0)\n\n以下"
},
{
"path": "README.md",
"chars": 9873,
"preview": "# TradingAgents 中文增强版\n\n[](https://opensource.org/l"
},
{
"path": "VERSION",
"chars": 14,
"preview": "v1.0.0-preview"
},
{
"path": "app/LICENSE",
"chars": 2767,
"preview": "TradingAgents-CN Web Application - Proprietary License\nTradingAgents-CN Web 应用程序 - 专有许可证\n\nCopyright (c) 2025 [hsliuping]"
},
{
"path": "app/__init__.py",
"chars": 39,
"preview": "\"\"\"TradingAgents-CN Web API package.\"\"\""
},
{
"path": "app/__main__.py",
"chars": 6263,
"preview": "\"\"\"\nTradingAgents-CN Backend Entry Point\n支持 python -m app 启动方式\n\"\"\"\n\nimport uvicorn\nimport sys\nimport os\nfrom pathlib imp"
},
{
"path": "app/constants/model_capabilities.py",
"chars": 17638,
"preview": "\"\"\"\n模型能力分级系统\n\n定义模型的能力等级、适用角色、特性标签等元数据,\n用于智能匹配分析深度和模型选择。\n\n🆕 聚合渠道支持:\n- 支持 302.AI、OpenRouter、One API 等聚合渠道\n- 聚合渠道的模型名称格式:{p"
},
{
"path": "app/core/__init__.py",
"chars": 53,
"preview": "\"\"\"\nCore module for TradingAgents FastAPI backend\n\"\"\""
},
{
"path": "app/core/config.py",
"chars": 12604,
"preview": "from pydantic import Field\nfrom pydantic_settings import BaseSettings, SettingsConfigDict\nfrom typing import List\nimport"
},
{
"path": "app/core/config_bridge.py",
"chars": 26316,
"preview": "\"\"\"\n配置桥接模块\n将统一配置系统的配置桥接到环境变量,供 TradingAgents 核心库使用\n\"\"\"\n\nimport os\nimport json\nimport logging\nfrom pathlib import Path\nfr"
},
{
"path": "app/core/config_compat.py",
"chars": 6981,
"preview": "\"\"\"\n配置系统兼容层\n\n为旧的 tradingagents 库提供配置兼容接口,\n使其能够使用新的配置系统而无需修改代码。\n\n⚠️ 此模块仅用于向后兼容,新代码应直接使用 ConfigService\n\"\"\"\n\nimport os\nimpo"
},
{
"path": "app/core/database.py",
"chars": 13755,
"preview": "\"\"\"\n数据库连接管理模块\n增强版本,支持连接池、健康检查和错误恢复\n\"\"\"\n\nimport logging\nimport asyncio\nfrom typing import Optional\nfrom motor.motor_async"
},
{
"path": "app/core/dev_config.py",
"chars": 3156,
"preview": "\"\"\"\n开发环境配置\n优化开发体验,减少不必要的文件监控\n\"\"\"\n\nimport logging\nfrom typing import List, Optional\n\n\nclass DevConfig:\n \"\"\"开发环境配置类\"\"\"\n"
},
{
"path": "app/core/logging_config.py",
"chars": 17484,
"preview": "import logging\nimport logging.config\nimport sys\nfrom pathlib import Path\nimport os\nimport platform\n\nfrom app.core.loggin"
},
{
"path": "app/core/logging_context.py",
"chars": 579,
"preview": "import logging\nimport contextvars\n\n# Shared contextvar for trace id across the whole process\ntrace_id_var: contextvars.C"
},
{
"path": "app/core/rate_limiter.py",
"chars": 6466,
"preview": "\"\"\"\n速率限制器\n用于控制API调用频率,避免超过数据源的限流限制\n\"\"\"\nimport asyncio\nimport time\nimport logging\nfrom collections import deque\nfrom typi"
},
{
"path": "app/core/redis_client.py",
"chars": 5748,
"preview": "\"\"\"\nRedis客户端配置和连接管理\n\"\"\"\n\nimport redis.asyncio as redis\nimport logging\nfrom typing import Optional\nfrom .config import se"
},
{
"path": "app/core/response.py",
"chars": 736,
"preview": "\"\"\"\n统一API响应格式工具\n\"\"\"\nfrom datetime import datetime\nfrom typing import Any, Optional, Dict\nfrom app.utils.timezone import "
},
{
"path": "app/core/startup_validator.py",
"chars": 10198,
"preview": "\"\"\"\n启动配置验证器\n\n验证系统启动所需的必需配置项,提供友好的错误提示。\n\"\"\"\n\nimport os\nimport logging\nfrom typing import List, Dict, Any, Optional\nfrom d"
},
{
"path": "app/core/unified_config.py",
"chars": 18516,
"preview": "\"\"\"\n统一配置管理系统\n整合 config/、tradingagents/config/ 和 webapi 的配置管理\n\"\"\"\n\nimport json\nimport os\nfrom pathlib import Path\nfrom ty"
},
{
"path": "app/main.py",
"chars": 31739,
"preview": "\"\"\"\nTradingAgents-CN v1.0.0-preview FastAPI Backend\n主应用程序入口\n\nCopyright (c) 2025 hsliuping. All rights reserved.\n版权所有 (c)"
},
{
"path": "app/middleware/__init__.py",
"chars": 14,
"preview": "\"\"\"\n中间件模块\n\"\"\"\n"
},
{
"path": "app/middleware/error_handler.py",
"chars": 2575,
"preview": "\"\"\"\n错误处理中间件\n\"\"\"\n\nfrom fastapi import Request, Response\nfrom fastapi.responses import JSONResponse\nfrom starlette.middlew"
},
{
"path": "app/middleware/operation_log_middleware.py",
"chars": 9204,
"preview": "\"\"\"\n操作日志记录中间件\n自动记录用户的API操作日志\n\"\"\"\n\nimport time\nimport json\nimport logging\nfrom typing import Optional, Dict, Any\nfrom fas"
},
{
"path": "app/middleware/rate_limit.py",
"chars": 5454,
"preview": "\"\"\"\n速率限制中间件\n防止API滥用,实现用户级和端点级速率限制\n\"\"\"\n\nfrom fastapi import Request, Response, HTTPException\nfrom starlette.middleware.ba"
},
{
"path": "app/middleware/request_id.py",
"chars": 2062,
"preview": "\"\"\"\n请求ID/Trace-ID 中间件\n- 为每个请求生成唯一 ID(trace_id),写入 request.state 与响应头\n- 将 trace_id 写入 logging 的 contextvars,使所有日志自动带出\n\"\"\""
},
{
"path": "app/models/__init__.py",
"chars": 574,
"preview": "\"\"\"\n数据模型模块\n\"\"\"\n\n# 导入股票数据模型\nfrom .stock_models import (\n StockBasicInfoExtended,\n MarketQuotesExtended,\n MarketI"
},
{
"path": "app/models/analysis.py",
"chars": 7156,
"preview": "\"\"\"\n分析相关数据模型\n\"\"\"\n\nfrom datetime import datetime\nfrom typing import Optional, List, Dict, Any\nfrom pydantic import BaseMo"
},
{
"path": "app/models/config.py",
"chars": 18000,
"preview": "\"\"\"\n系统配置相关数据模型\n\"\"\"\n\nfrom datetime import datetime, timezone\nfrom app.utils.timezone import now_tz\nfrom typing import Opt"
},
{
"path": "app/models/notification.py",
"chars": 1922,
"preview": "\"\"\"\n通知数据模型(MongoDB + Pydantic)\n\"\"\"\nfrom datetime import datetime\nfrom typing import Optional, Literal, List, Dict, Any\nf"
},
{
"path": "app/models/operation_log.py",
"chars": 4993,
"preview": "\"\"\"\n操作日志数据模型\n\"\"\"\n\nfrom datetime import datetime\nfrom typing import Dict, Any, Optional, List\nfrom pydantic import BaseMo"
},
{
"path": "app/models/screening.py",
"chars": 12145,
"preview": "\"\"\"\n股票筛选相关的数据模型\n\"\"\"\n\nfrom pydantic import BaseModel, Field\nfrom typing import Any, Dict, List, Optional, Union\nfrom enum"
},
{
"path": "app/models/stock_models.py",
"chars": 8225,
"preview": "\"\"\"\n股票数据模型 - 基于现有集合扩展\n采用方案B: 在现有集合基础上扩展字段,保持向后兼容\n\"\"\"\nfrom datetime import datetime, date\nfrom typing import Optional, Di"
},
{
"path": "app/models/user.py",
"chars": 4868,
"preview": "\"\"\"\n用户数据模型\n\"\"\"\n\nfrom datetime import datetime, timezone\nfrom app.utils.timezone import now_tz\nfrom typing import Optiona"
},
{
"path": "app/routers/__init__.py",
"chars": 43,
"preview": "\"\"\"\nRouters package: expose API routers\n\"\"\""
},
{
"path": "app/routers/akshare_init.py",
"chars": 10687,
"preview": "\"\"\"\nAKShare数据初始化API路由\n提供Web接口进行AKShare数据初始化和管理\n\"\"\"\nimport asyncio\nimport logging\nfrom datetime import datetime\nfrom typi"
},
{
"path": "app/routers/analysis.py",
"chars": 50002,
"preview": "\"\"\"\n股票分析API路由\n增强版本,支持优先级、进度跟踪、任务管理等功能\n\"\"\"\n\nfrom fastapi import APIRouter, HTTPException, Depends, Query, BackgroundTasks"
},
{
"path": "app/routers/auth_db.py",
"chars": 15463,
"preview": "\"\"\"\n基于数据库的认证路由 - 改进版\n替代原有的基于配置文件的认证机制\n\"\"\"\n\nimport time\nfrom typing import Optional\n\nfrom fastapi import APIRouter, Depen"
},
{
"path": "app/routers/baostock_init.py",
"chars": 9739,
"preview": "#!/usr/bin/env python3\n\"\"\"\nBaoStock初始化API路由\n提供BaoStock数据初始化的RESTful API接口\n\"\"\"\nimport asyncio\nimport logging\nfrom datetim"
},
{
"path": "app/routers/cache.py",
"chars": 5141,
"preview": "\"\"\"\n缓存管理路由\n提供缓存统计、清理等功能\n\"\"\"\nfrom fastapi import APIRouter, HTTPException, Depends, Query\nfrom typing import Optional\nfro"
},
{
"path": "app/routers/config.py",
"chars": 79704,
"preview": "\"\"\"\n配置管理API路由\n\"\"\"\n\nimport logging\nfrom typing import List, Dict, Any\nfrom fastapi import APIRouter, Depends, HTTPExcepti"
},
{
"path": "app/routers/database.py",
"chars": 9179,
"preview": "\"\"\"\n数据库管理API路由\n\"\"\"\n\nimport logging\nimport json\nimport os\nfrom datetime import datetime\nfrom typing import Dict, Any, Lis"
},
{
"path": "app/routers/favorites.py",
"chars": 8969,
"preview": "\"\"\"\n自选股管理API路由\n\"\"\"\n\nfrom typing import List, Optional\nfrom fastapi import APIRouter, Depends, HTTPException, status\nfrom"
},
{
"path": "app/routers/financial_data.py",
"chars": 8500,
"preview": "#!/usr/bin/env python3\n\"\"\"\n财务数据API路由\n提供财务数据查询和同步管理接口\n\"\"\"\nimport logging\nfrom typing import Dict, Any, List, Optional\nfro"
},
{
"path": "app/routers/health.py",
"chars": 910,
"preview": "from fastapi import APIRouter\nimport time\nfrom pathlib import Path\n\nrouter = APIRouter()\n\n\ndef get_version() -> str:\n "
},
{
"path": "app/routers/historical_data.py",
"chars": 7313,
"preview": "#!/usr/bin/env python3\n\"\"\"\n历史数据查询API\n提供统一的历史K线数据查询接口\n\"\"\"\nimport logging\nfrom datetime import datetime, date\nfrom typing "
},
{
"path": "app/routers/internal_messages.py",
"chars": 11139,
"preview": "\"\"\"\n内部消息数据API路由\n提供内部消息的查询、搜索和管理接口\n\"\"\"\nfrom typing import Optional, List, Dict, Any\nfrom datetime import datetime, timede"
},
{
"path": "app/routers/logs.py",
"chars": 6404,
"preview": "\"\"\"\n日志管理API路由\n提供日志查询、过滤和导出功能\n\"\"\"\n\nimport logging\nfrom typing import List, Optional\nfrom fastapi import APIRouter, Depend"
},
{
"path": "app/routers/model_capabilities.py",
"chars": 9453,
"preview": "\"\"\"\n模型能力管理API路由\n\"\"\"\n\nfrom fastapi import APIRouter, HTTPException, Depends\nfrom typing import List, Dict, Any, Optional\n"
},
{
"path": "app/routers/multi_market_stocks.py",
"chars": 8819,
"preview": "\"\"\"\n多市场股票API路由\n支持A股、港股、美股的统一查询接口\n\n功能:\n1. 跨市场股票信息查询\n2. 多数据源优先级查询\n3. 统一的响应格式\n\n路径前缀: /api/markets\n\"\"\"\nfrom typing import Op"
},
{
"path": "app/routers/multi_period_sync.py",
"chars": 12274,
"preview": "#!/usr/bin/env python3\n\"\"\"\n多周期数据同步API\n提供日线、周线、月线数据的同步管理接口\n\"\"\"\nimport logging\nfrom datetime import datetime\nfrom typing i"
},
{
"path": "app/routers/multi_source_sync.py",
"chars": 16150,
"preview": "\"\"\"\nMulti-source synchronization API routes\nProvides endpoints for multi-source stock data synchronization\n\"\"\"\nimport as"
},
{
"path": "app/routers/news_data.py",
"chars": 14883,
"preview": "\"\"\"\n新闻数据API路由\n提供新闻数据查询、同步和管理接口\n\"\"\"\nfrom fastapi import APIRouter, HTTPException, BackgroundTasks, Depends, Query, status"
},
{
"path": "app/routers/notifications.py",
"chars": 3672,
"preview": "\"\"\"\n通知 REST API\n\"\"\"\nimport logging\nfrom typing import Optional\nfrom fastapi import APIRouter, Depends, HTTPException, Qu"
},
{
"path": "app/routers/operation_logs.py",
"chars": 7986,
"preview": "\"\"\"\n操作日志API路由\n\"\"\"\n\nimport logging\nfrom typing import Dict, Any\nfrom fastapi import APIRouter, Depends, HTTPException, st"
},
{
"path": "app/routers/paper.py",
"chars": 19489,
"preview": "from fastapi import APIRouter, Depends, HTTPException, status, Query\nfrom pydantic import BaseModel, Field\nfrom typing i"
},
{
"path": "app/routers/queue.py",
"chars": 385,
"preview": "from fastapi import APIRouter, Depends\nfrom app.routers.auth_db import get_current_user\nfrom app.services.queue_service "
},
{
"path": "app/routers/reports.py",
"chars": 19913,
"preview": "\"\"\"\n分析报告管理API路由\n\"\"\"\nimport os\nimport json\nfrom datetime import datetime, timedelta\nfrom typing import List, Optional, Di"
},
{
"path": "app/routers/scheduler.py",
"chars": 14025,
"preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\"\"\"\n定时任务管理路由\n提供定时任务的查询、暂停、恢复、手动触发等功能\n\"\"\"\n\nfrom fastapi import APIRouter, H"
},
{
"path": "app/routers/screening.py",
"chars": 12926,
"preview": "\nimport logging\nfrom fastapi import APIRouter, HTTPException, Depends\nfrom pydantic import BaseModel, Field\nfrom typing "
},
{
"path": "app/routers/social_media.py",
"chars": 11037,
"preview": "\"\"\"\n社媒消息数据API路由\n提供社媒消息的查询、搜索和统计接口\n\"\"\"\nfrom typing import Optional, List, Dict, Any\nfrom datetime import datetime, timede"
},
{
"path": "app/routers/sse.py",
"chars": 11239,
"preview": "from fastapi import APIRouter, Depends, HTTPException\nfrom fastapi.responses import StreamingResponse\nimport asyncio\nimp"
},
{
"path": "app/routers/stock_data.py",
"chars": 9995,
"preview": "\"\"\"\n股票数据API路由 - 基于扩展数据模型\n提供标准化的股票数据访问接口\n\"\"\"\nfrom typing import Optional, List\nfrom fastapi import APIRouter, Depends, HT"
},
{
"path": "app/routers/stock_sync.py",
"chars": 31149,
"preview": "\"\"\"\n股票数据同步API路由\n支持单个股票或批量股票的历史数据和财务数据同步\n\"\"\"\n\nfrom typing import List, Optional\nfrom fastapi import APIRouter, Depends, H"
},
{
"path": "app/routers/stocks.py",
"chars": 25297,
"preview": "\"\"\"\n股票详情相关API\n- 统一响应包: {success, data, message, timestamp}\n- 所有端点均需鉴权 (Bearer Token)\n- 路径前缀在 main.py 中挂载为 /api,当前路由自身前缀为"
},
{
"path": "app/routers/sync.py",
"chars": 954,
"preview": "\"\"\"\nSync router for stock basics synchronization\n- POST /api/sync/stock_basics/run -> trigger full sync\n- GET /api/sync"
},
{
"path": "app/routers/system_config.py",
"chars": 10996,
"preview": "from fastapi import APIRouter, Depends, HTTPException, status\nfrom typing import Any, Dict\nimport re\nimport logging\n\nfro"
},
{
"path": "app/routers/tags.py",
"chars": 2963,
"preview": "\"\"\"\n标签管理 API\n\"\"\"\nfrom typing import Optional, List\nfrom fastapi import APIRouter, Depends, HTTPException, status\nfrom py"
},
{
"path": "app/routers/tushare_init.py",
"chars": 9184,
"preview": "\"\"\"\nTushare数据初始化API路由\n提供Web界面的数据初始化功能\n\"\"\"\nimport asyncio\nfrom datetime import datetime\nfrom typing import Dict, Any, Opt"
},
{
"path": "app/routers/usage_statistics.py",
"chars": 4836,
"preview": "\"\"\"\n使用统计 API 路由\n\"\"\"\n\nimport logging\nfrom datetime import datetime\nfrom typing import Optional, List, Dict, Any\nfrom fast"
},
{
"path": "app/routers/websocket_notifications.py",
"chars": 9117,
"preview": "\"\"\"\nWebSocket 通知系统\n替代 SSE + Redis PubSub,解决连接泄漏问题\n\"\"\"\nimport asyncio\nimport json\nimport logging\nfrom typing import Dict,"
},
{
"path": "app/schemas/__init__.py",
"chars": 56,
"preview": "\"\"\"\nPydantic schemas for API request/response models\n\"\"\""
},
{
"path": "app/scripts/init_providers.py",
"chars": 4754,
"preview": "#!/usr/bin/env python3\n\"\"\"\n初始化大模型厂家数据脚本\n\"\"\"\n\nimport asyncio\nimport sys\nimport os\nfrom datetime import datetime\n\n# 添加项目根目"
},
{
"path": "app/services/__init__.py",
"chars": 57,
"preview": "\"\"\"\nService layer for business logic and integrations\n\"\"\""
},
{
"path": "app/services/analysis/__init__.py",
"chars": 172,
"preview": "\"\"\"Analysis service subpackage.\n\nThis package contains utilities split out from the monolithic analysis_service.py\nwitho"
},
{
"path": "app/services/analysis/status_update_utils.py",
"chars": 3482,
"preview": "\"\"\"Utilities for updating analysis task status.\n\nExtracted from AnalysisService to reduce file size and improve modulari"
},
{
"path": "app/services/analysis_service.py",
"chars": 39075,
"preview": "\"\"\"\n股票分析服务\n将现有的TradingAgents分析功能包装成API服务\n\"\"\"\n\nimport asyncio\nimport uuid\nimport json\nimport logging\nfrom datetime import"
},
{
"path": "app/services/auth_service.py",
"chars": 2225,
"preview": "import time\nfrom datetime import datetime, timedelta, timezone\nfrom app.utils.timezone import now_tz\nfrom typing import "
},
{
"path": "app/services/basics_sync/__init__.py",
"chars": 302,
"preview": "\"\"\"\n基础数据同步子包:封装与股票基础信息同步相关的阻塞调用与处理函数。\n- utils.py:与 Tushare 的阻塞式获取函数(股票列表、最新交易日、日度基础数据)\n- processing.py:共享的文档构建/指标处理函数\n\"\""
},
{
"path": "app/services/basics_sync/processing.py",
"chars": 1798,
"preview": "\"\"\"\n共享的文档指标处理函数\n- add_financial_metrics: 将日度基础指标(市值/估值/交易)追加到文档中\n\"\"\"\nfrom typing import Dict\n\n\ndef add_financial_metrics"
},
{
"path": "app/services/basics_sync/utils.py",
"chars": 7452,
"preview": "\"\"\"\n与 Tushare 相关的阻塞式工具函数:\n- fetch_stock_basic_df:获取股票列表(确保 Tushare 已连接)\n- find_latest_trade_date:探测最近可用交易日(YYYYMMDD)\n- f"
},
{
"path": "app/services/basics_sync_service.py",
"chars": 15875,
"preview": "\"\"\"\nStock basics synchronization service\n- Fetches A-share stock basic info from Tushare\n- Enriches with latest market c"
},
{
"path": "app/services/config_provider.py",
"chars": 4530,
"preview": "from __future__ import annotations\n\nfrom datetime import datetime, timedelta\nfrom typing import Any, Dict, Optional\nimpo"
},
{
"path": "app/services/config_service.py",
"chars": 173713,
"preview": "\"\"\"\n配置管理服务\n\"\"\"\n\nimport time\nimport asyncio\nimport logging\nfrom typing import List, Optional, Dict, Any\nfrom datetime imp"
},
{
"path": "app/services/data_consistency_checker.py",
"chars": 11128,
"preview": "\"\"\"\n数据一致性检查和处理服务\n处理多数据源之间的数据不一致性问题\n\"\"\"\nimport logging\nimport pandas as pd\nfrom typing import Dict, List, Optional, Tuple"
},
{
"path": "app/services/data_sources/__init__.py",
"chars": 304,
"preview": "\"\"\"\nData sources subpackage.\nExpose adapters and manager for backward-compatible imports.\n\"\"\"\nfrom .base import DataSour"
},
{
"path": "app/services/data_sources/akshare_adapter.py",
"chars": 16817,
"preview": "\"\"\"\nAKShare data source adapter\n\"\"\"\nfrom typing import Optional, Dict\nimport logging\nfrom datetime import datetime, time"
},
{
"path": "app/services/data_sources/baostock_adapter.py",
"chars": 12029,
"preview": "\"\"\"\nBaoStock data source adapter\n\"\"\"\nfrom typing import Optional\nimport logging\nfrom datetime import datetime, timedelta"
},
{
"path": "app/services/data_sources/base.py",
"chars": 2074,
"preview": "\"\"\"\nBase classes and shared typing for data source adapters\n\"\"\"\nfrom abc import ABC, abstractmethod\nfrom typing import O"
},
{
"path": "app/services/data_sources/data_consistency_checker.py",
"chars": 1445,
"preview": "\"\"\"\nMinimal stub for DataConsistencyChecker\n- Purpose: eliminate warning and provide no-op consistency checking\n- Behavi"
},
{
"path": "app/services/data_sources/manager.py",
"chars": 13078,
"preview": "\"\"\"\nData source manager that orchestrates multiple adapters with priority and optional consistency checks\n\"\"\"\nfrom typin"
},
{
"path": "app/services/data_sources/tushare_adapter.py",
"chars": 13966,
"preview": "\"\"\"\nTushare data source adapter\n\"\"\"\nfrom typing import Optional, Dict\nimport logging\nfrom datetime import datetime, time"
},
{
"path": "app/services/database/__init__.py",
"chars": 149,
"preview": "from . import status_checks, backups, cleanup, serialization\n\n__all__ = [\n \"status_checks\",\n \"backups\",\n \"clean"
},
{
"path": "app/services/database/backups.py",
"chars": 17060,
"preview": "\"\"\"\nBackup, import, and export routines extracted from DatabaseService.\n\"\"\"\nfrom __future__ import annotations\n\nimport j"
},
{
"path": "app/services/database/cleanup.py",
"chars": 3247,
"preview": "\"\"\"\nCleanup routines extracted from DatabaseService.\n\"\"\"\nfrom __future__ import annotations\n\nfrom datetime import dateti"
},
{
"path": "app/services/database/serialization.py",
"chars": 1248,
"preview": "\"\"\"\nSerialization helpers for MongoDB documents.\n\"\"\"\nfrom __future__ import annotations\n\nfrom datetime import datetime\nf"
},
{
"path": "app/services/database/status_checks.py",
"chars": 3531,
"preview": "\"\"\"\nDatabase status and connection checks, extracted from DatabaseService.\n\"\"\"\nfrom __future__ import annotations\n\nfrom "
},
{
"path": "app/services/database_screening_service.py",
"chars": 20229,
"preview": "\"\"\"\n基于MongoDB的股票筛选服务\n利用本地数据库中的股票基础信息进行高效筛选\n\"\"\"\n\nimport logging\nfrom typing import Any, Dict, List, Optional, Tuple\nfrom "
},
{
"path": "app/services/database_service.py",
"chars": 6557,
"preview": "\"\"\"\n数据库管理服务\n\"\"\"\n\nimport json\nimport os\nimport csv\nimport gzip\nimport shutil\nimport logging\nfrom datetime import datetime"
},
{
"path": "app/services/enhanced_screening/utils.py",
"chars": 2734,
"preview": "\"\"\"\nUtility helpers for EnhancedScreeningService to separate analysis and conversion logic.\n\"\"\"\nfrom __future__ import a"
},
{
"path": "app/services/enhanced_screening_service.py",
"chars": 11398,
"preview": "\"\"\"\n增强的股票筛选服务\n结合数据库优化和传统筛选方式,提供高效的股票筛选功能\n\"\"\"\n\nimport logging\nimport time\nfrom typing import Any, Dict, List, Optional, T"
},
{
"path": "app/services/favorites_service.py",
"chars": 15572,
"preview": "\"\"\"\n自选股服务\n\"\"\"\n\nfrom typing import List, Optional, Dict, Any\nfrom datetime import datetime\nfrom bson import ObjectId\n\nfro"
},
{
"path": "app/services/financial_data_service.py",
"chars": 17407,
"preview": "#!/usr/bin/env python3\n\"\"\"\n财务数据服务\n统一管理三数据源的财务数据存储和查询\n\"\"\"\nimport logging\nfrom datetime import datetime, timezone\nfrom typ"
},
{
"path": "app/services/foreign_stock_service.py",
"chars": 62184,
"preview": "\"\"\"\n港股和美股数据服务\n🔥 复用统一数据源管理器(UnifiedStockService)\n🔥 按照数据库配置的数据源优先级调用API\n🔥 请求去重机制:防止并发请求重复调用API\n\"\"\"\nfrom typing import Opti"
},
{
"path": "app/services/historical_data_service.py",
"chars": 17173,
"preview": "#!/usr/bin/env python3\n\"\"\"\n统一历史数据管理服务\n为三数据源提供统一的历史数据存储和查询接口\n\"\"\"\nimport asyncio\nimport logging\nfrom datetime import datet"
},
{
"path": "app/services/internal_message_service.py",
"chars": 13568,
"preview": "\"\"\"\n内部消息数据服务\n提供统一的内部消息存储、查询和管理功能\n\"\"\"\nfrom typing import Optional, List, Dict, Any, Union\nfrom datetime import datetime, "
},
{
"path": "app/services/log_export_service.py",
"chars": 16962,
"preview": "\"\"\"\n日志导出服务\n提供日志文件的查询、过滤和导出功能\n\"\"\"\n\nimport logging\nimport os\nimport zipfile\nfrom datetime import datetime, timedelta\nfrom "
},
{
"path": "app/services/memory_state_manager.py",
"chars": 14659,
"preview": "\"\"\"\n内存状态管理器\n类似于 analysis-engine 的实现,提供快速的状态读写\n\"\"\"\n\nimport asyncio\nimport threading\nfrom typing import Dict, Any, Optiona"
},
{
"path": "app/services/model_capability_service.py",
"chars": 15742,
"preview": "\"\"\"\n模型能力管理服务\n\n提供模型能力评估、验证和推荐功能。\n\"\"\"\n\nfrom typing import Tuple, Dict, Optional, List, Any\nfrom app.constants.model_capabi"
},
{
"path": "app/services/multi_source_basics_sync_service.py",
"chars": 13463,
"preview": "\"\"\"\nMulti-source stock basics synchronization service\n- Supports multiple data sources with fallback mechanism\n- Priorit"
},
{
"path": "app/services/news_data_service.py",
"chars": 24780,
"preview": "\"\"\"\n新闻数据服务\n提供统一的新闻数据存储、查询和管理功能\n\"\"\"\nfrom typing import Optional, List, Dict, Any, Union\nfrom datetime import datetime, ti"
},
{
"path": "app/services/notifications_service.py",
"chars": 5416,
"preview": "\"\"\"\n通知服务:持久化 + 列表 + 已读 + SSE 发布\n\"\"\"\nimport json\nimport logging\nfrom datetime import datetime, timedelta\nfrom typing impo"
},
{
"path": "app/services/operation_log_service.py",
"chars": 9802,
"preview": "\"\"\"\n操作日志服务\n\"\"\"\n\nimport logging\nfrom datetime import datetime, timedelta\nfrom typing import Dict, Any, List, Optional, Tu"
},
{
"path": "app/services/progress/__init__.py",
"chars": 287,
"preview": "\"\"\"\nProgress 子包(过渡期):对进度跟踪与日志处理进行结构化组织。\n当前阶段采用“新路径重导出到旧实现”的方式,保持 API 稳定。\n\"\"\"\nfrom .tracker import RedisProgressTracker, "
},
{
"path": "app/services/progress/log_handler.py",
"chars": 5739,
"preview": "\"\"\"\n进度日志处理器\n监控TradingAgents的日志输出,自动更新进度跟踪器\n\"\"\"\n\nimport logging\nimport re\nimport threading\nfrom typing import Dict, Optio"
},
{
"path": "app/services/progress/tracker.py",
"chars": 20714,
"preview": "\"\"\"\n进度跟踪器(过渡期)\n- 暂时从旧模块导入 RedisProgressTracker 类\n- 在本模块内提供 get_progress_by_id 的实现(与旧实现一致,修正 cls 引用)\n\"\"\"\nfrom typing impo"
},
{
"path": "app/services/progress_log_handler.py",
"chars": 465,
"preview": "\"\"\"\nThin re-export: ProgressLogHandler moved to app.services.progress.log_handler\nThis module keeps exports for backward"
},
{
"path": "app/services/queue/__init__.py",
"chars": 608,
"preview": "\"\"\"\nQueue 子包\n- keys: Redis 键名与常量\n- helpers: 队列相关的 Redis 操作辅助函数\n\"\"\"\nfrom .keys import (\n READY_LIST,\n TASK_PREFIX,\n"
},
{
"path": "app/services/queue/helpers.py",
"chars": 1846,
"preview": "\"\"\"\n队列服务的辅助函数(与 Redis 操作相关),便于在主服务中做薄委托。\n\"\"\"\nfrom __future__ import annotations\nimport time\nfrom typing import Dict\nfrom"
},
{
"path": "app/services/queue/keys.py",
"chars": 539,
"preview": "\"\"\"\n队列服务用到的 Redis 键名与配置常量(集中定义)\n\"\"\"\n\n# Redis键名常量\nREADY_LIST = \"qa:ready\"\n\nTASK_PREFIX = \"qa:task:\"\nBATCH_PREFIX = \"qa:ba"
},
{
"path": "app/services/queue_service.py",
"chars": 11758,
"preview": "\"\"\"\n增强版队列服务\n基于现有实现,添加并发控制、优先级队列、可见性超时等功能\n\"\"\"\n\nimport json\nimport time\nimport uuid\nimport asyncio\nimport logging\nfrom typ"
},
{
"path": "app/services/quotes_ingestion_service.py",
"chars": 23330,
"preview": "import logging\nfrom datetime import datetime, time as dtime, timedelta\nfrom typing import Dict, Optional, Tuple, List\nfr"
},
{
"path": "app/services/quotes_service.py",
"chars": 4080,
"preview": "\"\"\"\nQuotesService: 提供A股批量实时快照获取(AKShare东方财富 spot 接口),带内存TTL缓存。\n- 不使用通达信(TDX)作为兜底数据源。\n- 仅用于筛选返回前对 items 进行行情富集。\n\"\"\"\nfrom "
},
{
"path": "app/services/redis_progress_tracker.py",
"chars": 399,
"preview": "\"\"\"\nThin re-export: RedisProgressTracker moved to app.services.progress.tracker\nThis module keeps exports for backward c"
},
{
"path": "app/services/scheduler_service.py",
"chars": 34582,
"preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\"\"\"\n定时任务管理服务\n提供定时任务的查询、暂停、恢复、手动触发等功能\n\"\"\"\n\nimport asyncio\nfrom typing impor"
},
{
"path": "app/services/screening/eval_utils.py",
"chars": 5278,
"preview": "\"\"\"\nUtility functions for screening evaluation and DSL parsing.\nExtracted from ScreeningService to separate concerns whi"
},
{
"path": "app/services/screening_service.py",
"chars": 9241,
"preview": "from __future__ import annotations\n\nfrom dataclasses import dataclass\nfrom typing import Any, Dict, List, Optional, Tupl"
},
{
"path": "app/services/simple_analysis_service.py",
"chars": 123844,
"preview": "\"\"\"\n简化的股票分析服务\n直接调用现有的 TradingAgents 分析功能\n\"\"\"\n\nimport asyncio\nimport uuid\nimport logging\nfrom datetime import datetime\nfr"
},
{
"path": "app/services/social_media_service.py",
"chars": 11693,
"preview": "\"\"\"\n社媒消息数据服务\n提供统一的社媒消息存储、查询和分析功能\n\"\"\"\nfrom typing import Optional, List, Dict, Any, Union\nfrom datetime import datetime, "
},
{
"path": "app/services/stock_data_service.py",
"chars": 12511,
"preview": "\"\"\"\n股票数据服务层 - 统一数据访问接口\n基于现有MongoDB集合,提供标准化的数据访问服务\n\"\"\"\nimport logging\nfrom datetime import datetime, date\nfrom typing imp"
},
{
"path": "app/services/tags_service.py",
"chars": 3551,
"preview": "\"\"\"\n用户自定义标签服务\n\"\"\"\nfrom __future__ import annotations\nfrom typing import List, Optional, Dict, Any\nfrom datetime import d"
},
{
"path": "app/services/unified_stock_service.py",
"chars": 8334,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\"\"\"\n统一股票数据服务(跨市场,支持多数据源)\n\n功能:\n1. 跨市场数据访问(A股/港股/美股)\n2. 多数据源优先级查询\n3. 统一的查询接"
},
{
"path": "app/services/usage_statistics_service.py",
"chars": 8459,
"preview": "\"\"\"\n使用统计服务\n管理模型使用记录和成本统计\n\"\"\"\n\nimport logging\nfrom datetime import datetime, timedelta\nfrom typing import List, Dict, Any"
},
{
"path": "app/services/user_service.py",
"chars": 14772,
"preview": "\"\"\"\n用户服务 - 基于数据库的用户管理\n\"\"\"\n\nimport hashlib\nimport time\nfrom datetime import datetime\nfrom typing import Optional, Dict, A"
},
{
"path": "app/services/websocket_manager.py",
"chars": 2870,
"preview": "\"\"\"\nWebSocket 连接管理器\n用于实时推送分析进度更新\n\"\"\"\n\nimport asyncio\nimport json\nimport logging\nfrom typing import Dict, Set, Any\nfrom f"
},
{
"path": "app/utils/api_key_utils.py",
"chars": 3395,
"preview": "\"\"\"\nAPI Key 处理工具函数\n\n提供统一的 API Key 验证、缩略、环境变量读取等功能\n\"\"\"\n\nimport os\nfrom typing import Optional\n\n\ndef is_valid_api_key(api_"
},
{
"path": "app/utils/error_formatter.py",
"chars": 14553,
"preview": "\"\"\"\n错误信息格式化工具\n\n将技术性错误转换为用户友好的错误提示,明确指出问题所在(数据源、大模型、配置等)\n\"\"\"\n\nimport re\nfrom typing import Dict, Optional, Tuple\nfrom enu"
},
{
"path": "app/utils/report_exporter.py",
"chars": 18534,
"preview": "\"\"\"\n报告导出工具 - 支持 Markdown、Word、PDF 格式\n\n依赖安装:\n pip install pypandoc markdown\n\nPDF 导出需要额外工具:\n - wkhtmltopdf (推荐): htt"
},
{
"path": "app/utils/timezone.py",
"chars": 1734,
"preview": "from __future__ import annotations\n\nfrom datetime import datetime\nfrom zoneinfo import ZoneInfo\nfrom typing import Optio"
},
{
"path": "app/utils/trading_time.py",
"chars": 3751,
"preview": "\"\"\"\n交易时间判断工具模块\n\n提供统一的交易时间判断逻辑,用于判断当前是否在A股交易时间内。\n\"\"\"\n\nfrom datetime import datetime, time as dtime\nfrom typing import Opt"
},
{
"path": "app/worker/__init__.py",
"chars": 64,
"preview": "\"\"\"Worker package for analysis and related background jobs.\"\"\"\n\n"
},
{
"path": "app/worker/akshare_init_service.py",
"chars": 15801,
"preview": "\"\"\"\nAKShare数据初始化服务\n用于首次部署时的完整数据初始化,包括基础数据、历史数据、财务数据等\n\"\"\"\nimport asyncio\nimport logging\nfrom datetime import datetime, ti"
},
{
"path": "app/worker/akshare_sync_service.py",
"chars": 44815,
"preview": "\"\"\"\nAKShare数据同步服务\n基于AKShare提供器的统一数据同步方案\n\"\"\"\nimport asyncio\nimport logging\nfrom datetime import datetime, timedelta\nfrom "
},
{
"path": "app/worker/analysis_worker.py",
"chars": 8980,
"preview": "\"\"\"\n分析任务Worker进程\n消费队列中的分析任务,调用TradingAgents进行股票分析\n\"\"\"\n\nimport asyncio\nimport logging\nimport signal\nimport sys\nimport uui"
},
{
"path": "app/worker/baostock_init_service.py",
"chars": 13416,
"preview": "#!/usr/bin/env python3\n\"\"\"\nBaoStock数据初始化服务\n提供BaoStock数据的完整初始化功能\n\"\"\"\nimport asyncio\nimport logging\nfrom datetime import d"
},
{
"path": "app/worker/baostock_sync_service.py",
"chars": 21293,
"preview": "#!/usr/bin/env python3\n\"\"\"\nBaoStock数据同步服务\n提供BaoStock数据的批量同步功能,集成到APScheduler调度系统\n\"\"\"\nimport asyncio\nimport logging\nfrom "
},
{
"path": "app/worker/example_sdk_sync_service.py",
"chars": 11581,
"preview": "\"\"\"\n示例SDK数据同步服务 (app层)\n展示如何创建数据同步服务,将外部SDK数据写入标准化的MongoDB集合\n\n架构说明:\n- tradingagents层: 纯数据获取和标准化,不涉及数据库操作\n- app层: 数据同步服务,负"
},
{
"path": "app/worker/financial_data_sync_service.py",
"chars": 10989,
"preview": "#!/usr/bin/env python3\n\"\"\"\n财务数据同步服务\n统一管理三数据源的财务数据同步\n\"\"\"\nimport asyncio\nimport logging\nfrom datetime import datetime, tim"
},
{
"path": "app/worker/hk_data_service.py",
"chars": 5920,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\"\"\"\n港股数据服务(按需获取+缓存模式)\n\n功能:\n1. 按需从数据源获取港股信息(yfinance/akshare)\n2. 自动缓存到 Mon"
},
{
"path": "app/worker/hk_sync_service.py",
"chars": 17052,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\"\"\"\n港股数据服务(按需获取+缓存模式)\n\n功能:\n1. 按需从数据源获取港股信息(yfinance/akshare)\n2. 自动缓存到 Mon"
},
{
"path": "app/worker/multi_period_sync_service.py",
"chars": 12347,
"preview": "#!/usr/bin/env python3\n\"\"\"\n多周期历史数据同步服务\n支持日线、周线、月线数据的统一同步\n\"\"\"\nimport asyncio\nimport logging\nfrom datetime import datetime"
},
{
"path": "app/worker/news_data_sync_service.py",
"chars": 18736,
"preview": "\"\"\"\n新闻数据同步服务\n支持多数据源新闻数据同步和情绪分析\n\"\"\"\nimport asyncio\nimport logging\nfrom typing import List, Dict, Any, Optional\nfrom datet"
},
{
"path": "app/worker/tushare_init_service.py",
"chars": 15676,
"preview": "\"\"\"\nTushare数据初始化服务\n用于首次部署时的完整数据初始化,包括基础数据、历史数据、财务数据等\n\"\"\"\nimport asyncio\nimport logging\nfrom datetime import datetime, ti"
},
{
"path": "app/worker/tushare_sync_service.py",
"chars": 50017,
"preview": "\"\"\"\nTushare数据同步服务\n负责将Tushare数据同步到MongoDB标准化集合\n\"\"\"\nimport asyncio\nfrom datetime import datetime, timedelta, timezone\nfrom"
},
{
"path": "app/worker/us_data_service.py",
"chars": 5834,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\"\"\"\n美股数据服务(按需获取+缓存模式)\n\n功能:\n1. 按需从数据源获取美股信息(yfinance/finnhub)\n2. 自动缓存到 Mon"
},
{
"path": "app/worker/us_sync_service.py",
"chars": 13828,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\"\"\"\n美股数据同步服务(支持多数据源)\n\n功能:\n1. 从 yfinance 同步美股基础信息和行情\n2. 支持多数据源存储:同一股票可有多个数"
},
{
"path": "app/worker.py",
"chars": 8420,
"preview": "\"\"\"\nTradingAgents-CN WebAPI Worker\n\nConsumes tasks from Redis queue and processes them using actual stock analysis.\n\"\"\"\n"
},
{
"path": "cli/__init__.py",
"chars": 95,
"preview": "\n# 导入统一日志系统\nfrom tradingagents.utils.logging_init import get_logger\nlogger = get_logger(\"cli\")\n"
},
{
"path": "cli/akshare_init.py",
"chars": 10706,
"preview": "#!/usr/bin/env python3\n\"\"\"\nAKShare数据初始化CLI工具\n用于首次部署时的数据初始化和管理\n\"\"\"\nimport asyncio\nimport argparse\nimport logging\nimport s"
},
{
"path": "cli/baostock_init.py",
"chars": 7468,
"preview": "#!/usr/bin/env python3\n\"\"\"\nBaoStock数据初始化CLI工具\n提供命令行界面进行BaoStock数据初始化和管理\n\"\"\"\nimport asyncio\nimport argparse\nimport loggin"
},
{
"path": "cli/main.py",
"chars": 77816,
"preview": "# 标准库导入\nimport datetime\nimport os\nimport re\nimport subprocess\nimport sys\nimport time\nfrom collections import deque\nfrom "
},
{
"path": "cli/models.py",
"chars": 316,
"preview": "from enum import Enum\nfrom typing import List, Optional, Dict\nfrom pydantic import BaseModel\n\n# 导入统一日志系统\nfrom tradingage"
},
{
"path": "cli/static/welcome.txt",
"chars": 427,
"preview": "\n ______ ___ ___ __ \n /_ __/________ _____/ (_)___ ____ _/ | ____"
},
{
"path": "cli/tushare_init.py",
"chars": 9588,
"preview": "#!/usr/bin/env python3\n\"\"\"\nTushare数据初始化CLI工具\n用于首次部署时的数据初始化操作\n\"\"\"\nimport asyncio\nimport argparse\nimport sys\nimport os\nfro"
},
{
"path": "cli/utils.py",
"chars": 16013,
"preview": "import questionary\nfrom typing import List, Optional, Tuple, Dict\nfrom rich.console import Console\n\nfrom cli.models impo"
},
{
"path": "config/README.md",
"chars": 465,
"preview": "# Config 目录\n\n此目录用于存储TradingAgents的配置文件和使用统计数据。\n\n## 文件说明\n\n- `usage.json` - Token使用统计数据(自动生成)\n- `models.json` - 模型配置文件(自动生"
},
{
"path": "config/logging.toml",
"chars": 2389,
"preview": "# TradingAgents-CN 日志配置文件\n# 支持不同环境的日志配置\n\n[logging]\n# 全局日志级别:DEBUG, INFO, WARNING, ERROR, CRITICAL\nlevel = \"INFO\"\n\n# 日志格式"
},
{
"path": "config/logging_docker.toml",
"chars": 2380,
"preview": "# Docker环境专用日志配置 - 完整修复版\n# 解决KeyError: 'file'错误\n\n[logging]\nlevel = \"INFO\"\n\n[logging.format]\n# 必须包含所有格式配置\nconsole = \"%(as"
},
{
"path": "docker/nginx.conf",
"chars": 1320,
"preview": "server {\n listen 80;\n server_name localhost;\n\n root /usr/share/nginx/html;\n index index.html;\n\n "
},
{
"path": "docker-compose.hub.nginx.arm.yml",
"chars": 5652,
"preview": "version: '3.8'\n\n# TradingAgents-CN v1.0.0-preview Docker Compose配置(带 Nginx 反向代理)\n# 使用Docker Hub镜像 + Nginx 反向代理\n#\n# 使用方法:"
},
{
"path": "docker-compose.hub.nginx.yml",
"chars": 6003,
"preview": "version: '3.8'\n\n# TradingAgents-CN v1.0.0-preview Docker Compose配置(带 Nginx 反向代理)\n# 使用Docker Hub镜像 + Nginx 反向代理\n#\n# 使用方法:"
},
{
"path": "docker-compose.yml",
"chars": 5229,
"preview": "version: '3.8'\n\n# TradingAgents-CN v1.0.0-preview Docker Compose配置\n# 支持前后端分离部署\n\nservices:\n # FastAPI 后端服务\n backend:\n "
},
{
"path": "docs/ANALYST_DATA_CONFIGURATION.md",
"chars": 4183,
"preview": "# 📊 分析师数据获取配置指南\n\n## 📋 概述\n\nTradingAgents-CN 支持为不同类型的分析师配置不同的数据获取范围,以优化性能和分析质量。\n\n---\n\n## 🎯 配置参数\n\n### 1. 市场分析师数据范围\n\n**配置项**"
},
{
"path": "docs/API_KEY_MANAGEMENT_ANALYSIS.md",
"chars": 11611,
"preview": "# API Key 配置管理全流程分析\n\n## 📋 目录\n\n1. [核心规则定义](#核心规则定义)\n2. [涉及的组件](#涉及的组件)\n3. [完整流程分析](#完整流程分析)\n4. [当前问题分析](#当前问题分析)\n5. [建议的修"
},
{
"path": "docs/API_KEY_TESTING_GUIDE.md",
"chars": 6071,
"preview": "# API Key 配置管理测试指南\n\n## 📋 测试目标\n\n验证 API Key 配置管理的完整流程,确保:\n1. ✅ MongoDB 和 .env 配置来源明确区分\n2. ✅ 配置验证正确显示颜色(绿色/黄色/红色)\n3. ✅ 编辑对话"
},
{
"path": "docs/BUILD_GUIDE.md",
"chars": 5848,
"preview": "# 🏗️ TradingAgents-CN Docker 镜像构建指南\n\n本文档说明如何为不同架构构建 Docker 镜像。\n\n---\n\n## 📋 目录\n\n- [快速开始](#快速开始)\n- [架构选择](#架构选择)\n- [构建脚本](#"
},
{
"path": "docs/CNAME",
"chars": 23,
"preview": "www.tradingagentscn.com"
},
{
"path": "docs/CONFIG_VALIDATION_FIX_SUMMARY.md",
"chars": 4934,
"preview": "# 配置验证顶部提示修复总结\n\n## 📋 问题描述\n\n**用户反馈**:\n> 最上面这里,如果不是\"必须配置\"有问题不要显示红色,其它的显示黄色。\n\n**具体问题**:\n1. 配置验证顶部提示,只要有 MongoDB 警告就显示红色错误\n2"
},
{
"path": "docs/DOCKER_REGISTRY_STRATEGY.md",
"chars": 5844,
"preview": "# 🐳 Docker 镜像仓库策略\n\n## 📋 概述\n\n为了提高发布效率,TradingAgents-CN 采用**分架构独立仓库**策略:\n\n- **AMD64 版本**:独立仓库,频繁更新\n- **ARM64 版本**:独立仓库,按需更"
},
{
"path": "docs/ENHANCED_HISTORY_FEATURES_SUMMARY.md",
"chars": 3136,
"preview": "# 股票分析历史功能增强总结\n\n## 项目概述\n\n本次更新大幅增强了TradingAgents-CN Web界面的股票分析历史功能,从基础的历史查看升级为功能完整的分析对比和趋势分析平台。\n\n## 主要改进\n\n### 🔄 多模式对比分析\n\n"
},
{
"path": "docs/GITHUB_BRANCH_PROTECTION.md",
"chars": 3144,
"preview": "# GitHub 分支保护规则设置指南\n\n## 🎯 目标\n为 `main` 分支设置严格的保护规则,防止未经测试的代码直接推送到生产分支。\n\n## 📋 设置步骤\n\n### 1. 访问仓库设置\n1. 打开 GitHub 仓库:`https:/"
},
{
"path": "docs/LLM_ADAPTER_TEMPLATE.py",
"chars": 1363,
"preview": "\"\"\"\nLLM 适配器模板 - 适用于 OpenAI 兼容提供商\n\n使用方式:复制本文件为 tradingagents/llm_adapters/{provider}_adapter.py,\n并根据目标提供商修改 provider_name"
}
]
// ... and 1690 more files (download for full content)
About this extraction
This page contains the full source code of the hsliuping/TradingAgents-CN GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 1890 files (13.1 MB), approximately 3.5M tokens, and a symbol index with 5851 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.