fund-cli 3.1.0__tar.gz → 3.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {fund_cli-3.1.0 → fund_cli-3.2.0}/CHANGELOG.md +39 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/PKG-INFO +20 -2
- {fund_cli-3.1.0 → fund_cli-3.2.0}/README.md +19 -1
- {fund_cli-3.1.0 → fund_cli-3.2.0}/pyproject.toml +1 -1
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/__init__.py +1 -1
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/ai/agent.py +13 -48
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/ai/analyzer.py +6 -6
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/ai/memory.py +1 -3
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/ai/nodes.py +10 -17
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/ai/providers.py +1 -1
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/ai/tools.py +733 -640
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/analysis/performance.py +24 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/cli.py +3 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/commands/ai_cmd.py +6 -7
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/commands/analyze_cmd.py +85 -3
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/commands/config_cmd.py +10 -2
- fund_cli-3.2.0/src/fund_cli/commands/report_cmd.py +202 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/config.py +29 -1
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/ai_analyzer.py +44 -15
- fund_cli-3.2.0/src/fund_cli/core/ai_validator.py +142 -0
- fund_cli-3.2.0/src/fund_cli/core/audit_logger.py +185 -0
- fund_cli-3.2.0/src/fund_cli/core/calc_validator.py +185 -0
- fund_cli-3.2.0/src/fund_cli/core/cross_validator.py +162 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/data_gateway.py +38 -23
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/data_manager.py +47 -24
- fund_cli-3.2.0/src/fund_cli/core/data_quality.py +414 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/monitor.py +167 -7
- fund_cli-3.2.0/src/fund_cli/core/quality_gate.py +118 -0
- fund_cli-3.2.0/src/fund_cli/core/report_validator.py +131 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/reporter.py +1 -1
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/reporters/__init__.py +2 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/reporters/docx_reporter.py +26 -14
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/reporters/html_reporter.py +23 -2
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/reporters/markdown_reporter.py +8 -1
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/reporters/pdf_reporter.py +13 -5
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/reporters/pptx_reporter.py +30 -6
- fund_cli-3.2.0/src/fund_cli/core/reporters/risk_control_reporter.py +264 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/template_engine.py +76 -13
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/data/adapters/__init__.py +11 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/data/adapters/akshare_adapter.py +5 -9
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/data/adapters/mixins.py +6 -18
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/data/adapters/tushare_adapter.py +28 -21
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/data/adapters/wind_adapter.py +2 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/data/base.py +9 -18
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/data/cache.py +49 -3
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/data/normalizer.py +51 -6
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/mcp/__init__.py +2 -2
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/mcp/server.py +20 -44
- fund_cli-3.2.0/src/fund_cli/utils/decorators.py +125 -0
- fund_cli-3.2.0/src/fund_cli/utils/validators.py +139 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/views/reports.py +6 -6
- fund_cli-3.2.0/tests/integration/test_end_to_end.py +131 -0
- fund_cli-3.2.0/tests/unit/test_ai/test_analyzer.py +261 -0
- fund_cli-3.2.0/tests/unit/test_ai/test_tools.py +71 -0
- fund_cli-3.2.0/tests/unit/test_cli.py +232 -0
- fund_cli-3.2.0/tests/unit/test_commands/test_ai_cmd.py +288 -0
- fund_cli-3.2.0/tests/unit/test_commands/test_analyze_cmd.py +194 -0
- fund_cli-3.2.0/tests/unit/test_commands/test_compare_cmd.py +239 -0
- fund_cli-3.2.0/tests/unit/test_commands/test_holding_cmd.py +293 -0
- fund_cli-3.2.0/tests/unit/test_commands/test_interactive_cmd.py +40 -0
- fund_cli-3.2.0/tests/unit/test_commands/test_main.py +62 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_commands/test_report_cmd.py +129 -23
- fund_cli-3.2.0/tests/unit/test_core/test_ai_validator.py +103 -0
- fund_cli-3.2.0/tests/unit/test_core/test_audit_logger.py +116 -0
- fund_cli-3.2.0/tests/unit/test_core/test_calc_validator.py +125 -0
- fund_cli-3.2.0/tests/unit/test_core/test_cross_validator.py +109 -0
- fund_cli-3.2.0/tests/unit/test_core/test_monitor.py +248 -0
- fund_cli-3.2.0/tests/unit/test_core/test_quality_gate.py +92 -0
- fund_cli-3.2.0/tests/unit/test_core/test_report_validator.py +130 -0
- fund_cli-3.2.0/tests/unit/test_core/test_risk_control_reporter.py +254 -0
- fund_cli-3.2.0/tests/unit/test_core/test_risk_parity.py +117 -0
- fund_cli-3.2.0//345/220/210/350/247/204/351/243/216/346/216/247/350/264/250/346/243/200/346/267/261/345/272/246/350/260/203/347/240/224/346/212/245/345/221/212.docx +0 -0
- fund_cli-3.2.0//346/225/260/346/215/256/350/264/250/351/207/217/351/243/216/351/231/251/346/262/273/347/220/206/346/267/261/345/272/246/350/260/203/347/240/224/346/212/245/345/221/212.docx +0 -0
- fund_cli-3.1.0/src/fund_cli/commands/report_cmd.py +0 -86
- fund_cli-3.1.0/src/fund_cli/core/data_quality.py +0 -162
- fund_cli-3.1.0/src/fund_cli/utils/decorators.py +0 -90
- fund_cli-3.1.0/src/fund_cli/utils/validators.py +0 -77
- fund_cli-3.1.0/tests/integration/test_end_to_end.py +0 -1430
- fund_cli-3.1.0/tests/unit/test_ai/test_analyzer.py +0 -740
- fund_cli-3.1.0/tests/unit/test_ai/test_tools.py +0 -1152
- fund_cli-3.1.0/tests/unit/test_commands/test_analyze_cmd.py +0 -502
- fund_cli-3.1.0/tests/unit/test_commands/test_cli.py +0 -104
- fund_cli-3.1.0/tests/unit/test_commands/test_compare_cmd.py +0 -336
- fund_cli-3.1.0/tests/unit/test_commands/test_holding_cmd.py +0 -30
- fund_cli-3.1.0/tests/unit/test_core/test_monitor.py +0 -122
- {fund_cli-3.1.0 → fund_cli-3.2.0}/.env.example +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/.gitignore +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/.pre-commit-config.yaml +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/.trae/documents/documentation-verification-plan.md +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/CODE_OF_CONDUCT.md +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/CONTRIBUTING.md +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/Dockerfile +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/LICENSE +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/Makefile +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/SECURITY.md +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/comparison_report.md +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/docker-compose.yml +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/api/ai.md +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/api/analysis.md +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/api/core.md +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/api/data.md +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/api/optimizers.md +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/api/reference.md +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/api/reporters.md +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/contributing.md +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/development.md +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/index.md +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/installation.md +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/usage/ai-analysis.md +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/usage/analysis.md +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/usage/comparison.md +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/usage/filter-and-data.md +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/usage/holding-and-manager.md +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/usage/index.md +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/usage/interactive.md +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/usage/monitoring.md +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/usage/optimization.md +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/usage/report-generation.md +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/examples/basic_analysis.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/examples/batch_processing.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/examples/portfolio_optimization.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/mkdocs.yml +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/report_market_flow_portfolio.html +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/report_portfolio_portfolio.html +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/report_risk_control_portfolio.html +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/report_single_fund_000001.html +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/report_single_fund_000001.md +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/404.html +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/api/ai/index.html +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/api/analysis/index.html +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/api/core/index.html +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/api/data/index.html +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/api/optimizers/index.html +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/api/reference/index.html +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/api/reporters/index.html +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/_mkdocstrings.css +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/images/favicon.png +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/bundle.79ae519e.min.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/bundle.79ae519e.min.js.map +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.ar.min.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.da.min.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.de.min.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.du.min.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.el.min.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.es.min.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.fi.min.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.fr.min.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.he.min.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.hi.min.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.hu.min.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.hy.min.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.it.min.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.ja.min.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.jp.min.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.kn.min.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.ko.min.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.multi.min.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.nl.min.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.no.min.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.pt.min.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.ro.min.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.ru.min.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.sa.min.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.stemmer.support.min.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.sv.min.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.ta.min.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.te.min.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.th.min.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.tr.min.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.vi.min.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.zh.min.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/tinyseg.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/wordcut.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/workers/search.2c215733.min.js +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/workers/search.2c215733.min.js.map +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/stylesheets/main.484c7ddc.min.css +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/stylesheets/main.484c7ddc.min.css.map +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/stylesheets/palette.ab4e12ef.min.css +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/stylesheets/palette.ab4e12ef.min.css.map +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/contributing/index.html +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/development/index.html +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/index.html +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/installation/index.html +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/objects.inv +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/search/search_index.json +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/sitemap.xml +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/sitemap.xml.gz +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/usage/ai-analysis/index.html +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/usage/analysis/index.html +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/usage/comparison/index.html +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/usage/filter-and-data/index.html +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/usage/holding-and-manager/index.html +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/usage/index.html +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/usage/interactive/index.html +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/usage/monitoring/index.html +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/usage/optimization/index.html +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/site/usage/report-generation/index.html +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/__main__.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/ai/__init__.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/ai/prompts.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/ai/state.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/analysis/__init__.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/analysis/attribution.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/analysis/backtest.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/analysis/holding.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/analysis/manager.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/analysis/portfolio.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/analysis/risk.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/commands/__init__.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/commands/compare_cmd.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/commands/data_cmd.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/commands/filter_cmd.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/commands/holding_cmd.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/commands/interactive_cmd.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/commands/main.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/commands/manager_cmd.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/commands/monitor_cmd.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/commands/optimize_cmd.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/__init__.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/analyzer.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/optimizer.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/optimizers/__init__.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/optimizers/efficient_frontier.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/optimizers/max_sharpe.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/optimizers/mean_variance.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/optimizers/risk_parity.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/screener.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/data/__init__.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/data/models.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/templates/base.html +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/templates/market_flow/report.html +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/templates/portfolio/report.html +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/templates/risk_control/report.html +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/templates/single_fund/report.html +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/utils/__init__.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/utils/helpers.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/views/__init__.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/views/charts.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/views/tables.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/test_mvp.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/__init__.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/conftest.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/fixtures/sample_data.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/integration/__init__.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/integration/test_data_flow.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/integration/test_monitoring_flow.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/integration/test_optimization_flow.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/performance/__init__.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/performance/test_performance.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_ai/__init__.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_ai/test_agent.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_ai/test_prompts.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_ai/test_providers.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_analysis/test_attribution.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_analysis/test_backtest.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_analysis/test_holding.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_analysis/test_manager.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_analysis/test_performance.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_analysis/test_performance_enhanced.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_base_classes.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_commands/test_config_cmd.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_commands/test_data_cmd.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_commands/test_filter_cmd.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_commands/test_interactive.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_commands/test_manager_cmd.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_commands/test_monitor_cmd.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_commands/test_optimize_cmd.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_core/__init__.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_core/test_ai_analyzer.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_core/test_ai_analyzer_extended.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_core/test_data_gateway.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_core/test_data_manager.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_core/test_data_manager_extended.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_core/test_data_quality.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_core/test_data_quality_extended.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_core/test_docx_pptx_reporter.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_core/test_optimizers.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_core/test_pdf_reporter.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_core/test_reporters.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_core/test_screener.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_core/test_template_engine.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_data/test_akshare_adapter.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_data/test_akshare_adapter_extended.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_data/test_akshare_p0.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_data/test_akshare_p1.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_data/test_akshare_p2.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_data/test_base.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_data/test_cache_extended.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_data/test_mixins.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_data/test_models.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_data/test_models_extended.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_data/test_normalizer.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_data/test_tushare_adapter.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_data/test_wind_adapter.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_utils/__init__.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_utils/test_helpers.py +0 -0
- {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_views.py +0 -0
|
@@ -5,6 +5,45 @@
|
|
|
5
5
|
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/),
|
|
6
6
|
并且本项目遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
|
|
7
7
|
|
|
8
|
+
## [3.2.0] - 2026-05-10
|
|
9
|
+
|
|
10
|
+
### Added - 数据质量风险治理体系(核心特性)
|
|
11
|
+
- 五层数据质量治理架构
|
|
12
|
+
- Layer 1 数据采集层质量门禁: DataManager路由改用Gateway,激活熔断器/重试/降级
|
|
13
|
+
- Layer 2 数据标准化管道: Normalizer集成Pydantic模型验证、重复检测、净值范围校验
|
|
14
|
+
- Layer 3 数据质量检查引擎: Expectation风格8项自动化检查(非空/数据量/列完整/净值非空/净值范围/收益率范围/日期唯一/时效性)
|
|
15
|
+
- Layer 4 计算验证层: 12项指标合理性边界验证、交叉验证(PerformanceAnalyzer vs RiskAnalyzer)
|
|
16
|
+
- Layer 5 输出合规层: 报告完整性验证、免责声明检查、质量徽章
|
|
17
|
+
- 质量门禁 (QualityGate): 分析入口强制执行数据质量检查,不达标拦截
|
|
18
|
+
- 计算结果验证器 (CalcValidator): Sharpe/回撤/波动率/Beta/VaR等12项指标合理性检查
|
|
19
|
+
- 交叉验证器 (CrossValidator): 多引擎计算结果交叉比对
|
|
20
|
+
- AI输出验证器 (AIOutputValidator): AI生成文本与源数据一致性校验、矛盾表述检测
|
|
21
|
+
- 报告验证器 (ReportValidator): 必需字段检查、模板数据完整性、免责声明检查
|
|
22
|
+
- 审计日志 (AuditLogger): 质量检查/分析操作/报告生成日志,支持合规审计和查询
|
|
23
|
+
- 合规风控报告生成器 (RiskControlReporter): 风险概览/集中度分析/合规检查数据填充
|
|
24
|
+
- 质量配置 (QualityConfig): 质量门禁阈值、异常检测参数、审计日志配置
|
|
25
|
+
- Monitor扩展: 支持回撤/波动率/夏普比率监控规则
|
|
26
|
+
- 71个新增单元测试(覆盖全部6个新模块)
|
|
27
|
+
- 总测试数: 2001 passed
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
- DataManager: 关键方法(get_fund_info/nav/holdings/manager/benchmark)路由改用Gateway+Normalizer
|
|
31
|
+
- DataSourceGateway: 激活请求级内存缓存(5min TTL),修复hash碰撞(MD5),print→logging
|
|
32
|
+
- DataCache: 版本控制(CACHE_VERSION)、1GB容量限制、增强统计信息
|
|
33
|
+
- Normalizer: Pydantic模型验证集成、重复行检测、净值范围校验(0<nav<=10000)
|
|
34
|
+
- validators.py: 新增validate_nav_value/validate_daily_return/validate_data_min_rows/validate_date_strict
|
|
35
|
+
- decorators.py: retry增加指数退避+抖动,新增validate_input装饰器
|
|
36
|
+
- report_cmd.py: 修复空数据问题,集成ReportValidator,实现真实数据获取和分析
|
|
37
|
+
- analyze_cmd.py: 集成质量门禁+交叉验证+计算验证,显示质量评分
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
- DataManager第107行日志变量bug(主数据源切换日志显示错误)
|
|
41
|
+
- report_cmd.py传入空metrics={}的严重问题
|
|
42
|
+
- 风控报告模板(risk_control)无后端数据填充
|
|
43
|
+
- DataQualityChecker孤岛模块(从未被自动调用)
|
|
44
|
+
- Pydantic模型与数据流脱节(11个模型零调用)
|
|
45
|
+
- validators.py/decorators.py死代码(7个工具零调用)
|
|
46
|
+
|
|
8
47
|
## [3.1.0] - 2026-05-09
|
|
9
48
|
|
|
10
49
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fund-cli
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.2.0
|
|
4
4
|
Summary: 专业基金分析CLI工具 - 面向机构客户
|
|
5
5
|
Project-URL: Homepage, https://github.com/jarrey-0804/fund-cli
|
|
6
6
|
Project-URL: Documentation, https://fund-cli.readthedocs.io
|
|
@@ -72,7 +72,7 @@ Description-Content-Type: text/markdown
|
|
|
72
72
|
|
|
73
73
|
**专业基金分析CLI工具 - 面向机构客户**
|
|
74
74
|
|
|
75
|
-
[](https://github.com/jarrey-0804/fund-cli)
|
|
76
76
|
[](https://www.python.org/downloads/)
|
|
77
77
|
[](https://opensource.org/licenses/MIT)
|
|
78
78
|
[](https://github.com/psf/black)
|
|
@@ -85,6 +85,23 @@ Description-Content-Type: text/markdown
|
|
|
85
85
|
|
|
86
86
|
Fund CLI 是一款面向机构客户的专业基金分析命令行工具,提供基金筛选、业绩分析、组合对比、风险监控等功能。基于开源技术栈构建,支持多数据源接入和AI辅助分析。
|
|
87
87
|
|
|
88
|
+
## v3.2 新特性
|
|
89
|
+
|
|
90
|
+
### 五层数据质量治理架构
|
|
91
|
+
- **Layer 1 数据采集层**: Gateway路由 + 熔断器/重试/降级机制
|
|
92
|
+
- **Layer 2 数据标准化管道**: Pydantic模型验证 + 重复检测 + 净值范围校验
|
|
93
|
+
- **Layer 3 质量检查引擎**: 8项Expectation风格自动化检查
|
|
94
|
+
- **Layer 4 计算验证层**: 12项指标合理性边界验证 + 交叉验证
|
|
95
|
+
- **Layer 5 输出合规层**: 报告完整性验证 + 免责声明检查
|
|
96
|
+
|
|
97
|
+
### 新增质量模块
|
|
98
|
+
- **QualityGate**: 分析入口强制执行数据质量检查
|
|
99
|
+
- **CalcValidator**: Sharpe/回撤/波动率等12项指标合理性验证
|
|
100
|
+
- **CrossValidator**: PerformanceAnalyzer与RiskAnalyzer交叉验证
|
|
101
|
+
- **AIOutputValidator**: AI生成内容与源数据一致性校验
|
|
102
|
+
- **ReportValidator**: 报告必需字段和合规性检查
|
|
103
|
+
- **AuditLogger**: 质量检查/分析操作/报告生成审计日志
|
|
104
|
+
|
|
88
105
|
## v3.1 新特性
|
|
89
106
|
|
|
90
107
|
### 多数据源架构
|
|
@@ -125,6 +142,7 @@ Fund CLI 是一款面向机构客户的专业基金分析命令行工具,提
|
|
|
125
142
|
- **多数据源架构** (v3.1) - Tushare/AKShare/Wind 统一接入,熔断降级机制
|
|
126
143
|
- **报告引擎** (v3.1) - HTML/Markdown/PDF/Word/PPT 5种格式报告
|
|
127
144
|
- **AI增强** (v3.1) - 规则引擎 + OpenAI 双后端智能分析
|
|
145
|
+
- **数据质量治理** (v3.2) - 五层质量架构 + 审计日志 + 合规验证
|
|
128
146
|
|
|
129
147
|
---
|
|
130
148
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
**专业基金分析CLI工具 - 面向机构客户**
|
|
6
6
|
|
|
7
|
-
[](https://github.com/jarrey-0804/fund-cli)
|
|
8
8
|
[](https://www.python.org/downloads/)
|
|
9
9
|
[](https://opensource.org/licenses/MIT)
|
|
10
10
|
[](https://github.com/psf/black)
|
|
@@ -17,6 +17,23 @@
|
|
|
17
17
|
|
|
18
18
|
Fund CLI 是一款面向机构客户的专业基金分析命令行工具,提供基金筛选、业绩分析、组合对比、风险监控等功能。基于开源技术栈构建,支持多数据源接入和AI辅助分析。
|
|
19
19
|
|
|
20
|
+
## v3.2 新特性
|
|
21
|
+
|
|
22
|
+
### 五层数据质量治理架构
|
|
23
|
+
- **Layer 1 数据采集层**: Gateway路由 + 熔断器/重试/降级机制
|
|
24
|
+
- **Layer 2 数据标准化管道**: Pydantic模型验证 + 重复检测 + 净值范围校验
|
|
25
|
+
- **Layer 3 质量检查引擎**: 8项Expectation风格自动化检查
|
|
26
|
+
- **Layer 4 计算验证层**: 12项指标合理性边界验证 + 交叉验证
|
|
27
|
+
- **Layer 5 输出合规层**: 报告完整性验证 + 免责声明检查
|
|
28
|
+
|
|
29
|
+
### 新增质量模块
|
|
30
|
+
- **QualityGate**: 分析入口强制执行数据质量检查
|
|
31
|
+
- **CalcValidator**: Sharpe/回撤/波动率等12项指标合理性验证
|
|
32
|
+
- **CrossValidator**: PerformanceAnalyzer与RiskAnalyzer交叉验证
|
|
33
|
+
- **AIOutputValidator**: AI生成内容与源数据一致性校验
|
|
34
|
+
- **ReportValidator**: 报告必需字段和合规性检查
|
|
35
|
+
- **AuditLogger**: 质量检查/分析操作/报告生成审计日志
|
|
36
|
+
|
|
20
37
|
## v3.1 新特性
|
|
21
38
|
|
|
22
39
|
### 多数据源架构
|
|
@@ -57,6 +74,7 @@ Fund CLI 是一款面向机构客户的专业基金分析命令行工具,提
|
|
|
57
74
|
- **多数据源架构** (v3.1) - Tushare/AKShare/Wind 统一接入,熔断降级机制
|
|
58
75
|
- **报告引擎** (v3.1) - HTML/Markdown/PDF/Word/PPT 5种格式报告
|
|
59
76
|
- **AI增强** (v3.1) - 规则引擎 + OpenAI 双后端智能分析
|
|
77
|
+
- **数据质量治理** (v3.2) - 五层质量架构 + 审计日志 + 合规验证
|
|
60
78
|
|
|
61
79
|
---
|
|
62
80
|
|
|
@@ -91,9 +91,7 @@ class FundAgent:
|
|
|
91
91
|
from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver
|
|
92
92
|
|
|
93
93
|
logger.info("使用 PostgreSQL 持久化存储")
|
|
94
|
-
return AsyncPostgresSaver.from_conn_string(
|
|
95
|
-
config.database.connection_string
|
|
96
|
-
)
|
|
94
|
+
return AsyncPostgresSaver.from_conn_string(config.database.connection_string)
|
|
97
95
|
except ImportError:
|
|
98
96
|
logger.warning("langgraph-checkpoint-postgres 未安装,回退到 MemorySaver")
|
|
99
97
|
return MemorySaver()
|
|
@@ -121,7 +119,8 @@ class FundAgent:
|
|
|
121
119
|
temperature=config.temperature,
|
|
122
120
|
max_tokens=config.max_tokens,
|
|
123
121
|
api_key=config.qwen_api_key or config.api_key,
|
|
124
|
-
base_url=config.qwen_base_url
|
|
122
|
+
base_url=config.qwen_base_url
|
|
123
|
+
or "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
|
125
124
|
)
|
|
126
125
|
else:
|
|
127
126
|
# 默认使用 OpenAI
|
|
@@ -153,26 +152,12 @@ class FundAgent:
|
|
|
153
152
|
if self.enable_human_review:
|
|
154
153
|
# 启用人工审核时,增加 human_review 分支
|
|
155
154
|
workflow.add_node("human_review", create_human_input_node())
|
|
156
|
-
workflow.add_conditional_edges(
|
|
157
|
-
"llm",
|
|
158
|
-
router_node,
|
|
159
|
-
{
|
|
160
|
-
"tools": "tools",
|
|
161
|
-
"end": "summary"
|
|
162
|
-
}
|
|
163
|
-
)
|
|
155
|
+
workflow.add_conditional_edges("llm", router_node, {"tools": "tools", "end": "summary"})
|
|
164
156
|
workflow.add_edge("tools", "llm")
|
|
165
157
|
workflow.add_edge("summary", "human_review")
|
|
166
158
|
workflow.add_edge("human_review", END)
|
|
167
159
|
else:
|
|
168
|
-
workflow.add_conditional_edges(
|
|
169
|
-
"llm",
|
|
170
|
-
router_node,
|
|
171
|
-
{
|
|
172
|
-
"tools": "tools",
|
|
173
|
-
"end": "summary"
|
|
174
|
-
}
|
|
175
|
-
)
|
|
160
|
+
workflow.add_conditional_edges("llm", router_node, {"tools": "tools", "end": "summary"})
|
|
176
161
|
workflow.add_edge("tools", "llm")
|
|
177
162
|
workflow.add_edge("summary", END)
|
|
178
163
|
|
|
@@ -182,10 +167,7 @@ class FundAgent:
|
|
|
182
167
|
return workflow
|
|
183
168
|
|
|
184
169
|
async def ainvoke(
|
|
185
|
-
self,
|
|
186
|
-
user_input: str,
|
|
187
|
-
user_id: str = "default",
|
|
188
|
-
thread_id: str | None = None
|
|
170
|
+
self, user_input: str, user_id: str = "default", thread_id: str | None = None
|
|
189
171
|
) -> str:
|
|
190
172
|
"""
|
|
191
173
|
异步调用 Agent
|
|
@@ -201,12 +183,7 @@ class FundAgent:
|
|
|
201
183
|
if thread_id is None:
|
|
202
184
|
thread_id = str(uuid.uuid4())
|
|
203
185
|
|
|
204
|
-
config = {
|
|
205
|
-
"configurable": {
|
|
206
|
-
"user_id": user_id,
|
|
207
|
-
"thread_id": thread_id
|
|
208
|
-
}
|
|
209
|
-
}
|
|
186
|
+
config = {"configurable": {"user_id": user_id, "thread_id": thread_id}}
|
|
210
187
|
|
|
211
188
|
# 准备初始状态
|
|
212
189
|
initial_state: FundAgentState = {
|
|
@@ -219,7 +196,7 @@ class FundAgent:
|
|
|
219
196
|
"needs_human_review": False,
|
|
220
197
|
"human_feedback": None,
|
|
221
198
|
"final_response": None,
|
|
222
|
-
"error": None
|
|
199
|
+
"error": None,
|
|
223
200
|
}
|
|
224
201
|
|
|
225
202
|
try:
|
|
@@ -234,7 +211,7 @@ class FundAgent:
|
|
|
234
211
|
messages = result.get("messages", [])
|
|
235
212
|
if messages:
|
|
236
213
|
last_message = messages[-1]
|
|
237
|
-
if hasattr(last_message,
|
|
214
|
+
if hasattr(last_message, "content"):
|
|
238
215
|
return last_message.content
|
|
239
216
|
|
|
240
217
|
return "抱歉,我无法处理您的请求。"
|
|
@@ -243,10 +220,7 @@ class FundAgent:
|
|
|
243
220
|
return f"执行过程中出现错误: {str(e)}"
|
|
244
221
|
|
|
245
222
|
def invoke(
|
|
246
|
-
self,
|
|
247
|
-
user_input: str,
|
|
248
|
-
user_id: str = "default",
|
|
249
|
-
thread_id: str | None = None
|
|
223
|
+
self, user_input: str, user_id: str = "default", thread_id: str | None = None
|
|
250
224
|
) -> str:
|
|
251
225
|
"""
|
|
252
226
|
同步调用 Agent
|
|
@@ -267,9 +241,7 @@ class FundAgent:
|
|
|
267
241
|
loop = asyncio.new_event_loop()
|
|
268
242
|
asyncio.set_event_loop(loop)
|
|
269
243
|
|
|
270
|
-
return loop.run_until_complete(
|
|
271
|
-
self.ainvoke(user_input, user_id, thread_id)
|
|
272
|
-
)
|
|
244
|
+
return loop.run_until_complete(self.ainvoke(user_input, user_id, thread_id))
|
|
273
245
|
|
|
274
246
|
def get_history(self, user_id: str, thread_id: str) -> list:
|
|
275
247
|
"""
|
|
@@ -282,12 +254,7 @@ class FundAgent:
|
|
|
282
254
|
Returns:
|
|
283
255
|
对话历史列表
|
|
284
256
|
"""
|
|
285
|
-
config = {
|
|
286
|
-
"configurable": {
|
|
287
|
-
"user_id": user_id,
|
|
288
|
-
"thread_id": thread_id
|
|
289
|
-
}
|
|
290
|
-
}
|
|
257
|
+
config = {"configurable": {"user_id": user_id, "thread_id": thread_id}}
|
|
291
258
|
|
|
292
259
|
try:
|
|
293
260
|
history = list(self.app.get_state_history(config))
|
|
@@ -316,9 +283,7 @@ _fund_agent: FundAgent | None = None
|
|
|
316
283
|
|
|
317
284
|
|
|
318
285
|
def get_fund_agent(
|
|
319
|
-
llm: BaseChatModel | None = None,
|
|
320
|
-
checkpointer=None,
|
|
321
|
-
force_new: bool = False
|
|
286
|
+
llm: BaseChatModel | None = None, checkpointer=None, force_new: bool = False
|
|
322
287
|
) -> FundAgent:
|
|
323
288
|
"""
|
|
324
289
|
获取 Fund Agent 实例(单例)
|
|
@@ -79,12 +79,12 @@ class AIAnalyzer:
|
|
|
79
79
|
|
|
80
80
|
fund_text = f"""
|
|
81
81
|
基金 {i + 1}: {code}
|
|
82
|
-
- 名称: {info.get(
|
|
83
|
-
- 类型: {info.get(
|
|
84
|
-
- 年化收益: {metrics.get(
|
|
85
|
-
- 夏普比率: {metrics.get(
|
|
86
|
-
- 最大回撤: {metrics.get(
|
|
87
|
-
- 波动率: {metrics.get(
|
|
82
|
+
- 名称: {info.get("name", "未知")}
|
|
83
|
+
- 类型: {info.get("type", "未知")}
|
|
84
|
+
- 年化收益: {metrics.get("cagr", 0)}%
|
|
85
|
+
- 夏普比率: {metrics.get("sharpe_ratio", 0)}
|
|
86
|
+
- 最大回撤: {metrics.get("max_drawdown", 0)}%
|
|
87
|
+
- 波动率: {metrics.get("volatility", 0)}%
|
|
88
88
|
"""
|
|
89
89
|
funds_text.append(fund_text)
|
|
90
90
|
|
|
@@ -33,9 +33,7 @@ from typing import Any
|
|
|
33
33
|
try:
|
|
34
34
|
import chromadb
|
|
35
35
|
except ImportError as exc:
|
|
36
|
-
raise ImportError(
|
|
37
|
-
"chromadb 包未安装,请执行: pip install chromadb"
|
|
38
|
-
) from exc
|
|
36
|
+
raise ImportError("chromadb 包未安装,请执行: pip install chromadb") from exc
|
|
39
37
|
|
|
40
38
|
logger = logging.getLogger(__name__)
|
|
41
39
|
|
|
@@ -89,10 +89,7 @@ def create_llm_node(llm):
|
|
|
89
89
|
# 调用 LLM
|
|
90
90
|
response = llm_with_tools.invoke(messages, config)
|
|
91
91
|
|
|
92
|
-
return {
|
|
93
|
-
"messages": [response],
|
|
94
|
-
"current_step": "llm_response"
|
|
95
|
-
}
|
|
92
|
+
return {"messages": [response], "current_step": "llm_response"}
|
|
96
93
|
|
|
97
94
|
return llm_node
|
|
98
95
|
|
|
@@ -128,7 +125,11 @@ def router_node(state: FundAgentState) -> Literal["tools", "end"]:
|
|
|
128
125
|
last_message = messages[-1]
|
|
129
126
|
|
|
130
127
|
# 检查是否有工具调用
|
|
131
|
-
if
|
|
128
|
+
if (
|
|
129
|
+
isinstance(last_message, AIMessage)
|
|
130
|
+
and hasattr(last_message, "tool_calls")
|
|
131
|
+
and last_message.tool_calls
|
|
132
|
+
):
|
|
132
133
|
return "tools"
|
|
133
134
|
|
|
134
135
|
return "end"
|
|
@@ -146,10 +147,7 @@ def create_error_handler_node():
|
|
|
146
147
|
|
|
147
148
|
if error:
|
|
148
149
|
error_message = f"执行过程中出现错误: {error}"
|
|
149
|
-
return {
|
|
150
|
-
"messages": [AIMessage(content=error_message)],
|
|
151
|
-
"final_response": error_message
|
|
152
|
-
}
|
|
150
|
+
return {"messages": [AIMessage(content=error_message)], "final_response": error_message}
|
|
153
151
|
|
|
154
152
|
return {}
|
|
155
153
|
|
|
@@ -169,10 +167,7 @@ def create_human_input_node():
|
|
|
169
167
|
# 目前返回一个提示消息
|
|
170
168
|
prompt = "请提供更多信息以继续分析..."
|
|
171
169
|
|
|
172
|
-
return {
|
|
173
|
-
"messages": [AIMessage(content=prompt)],
|
|
174
|
-
"current_step": "waiting_for_input"
|
|
175
|
-
}
|
|
170
|
+
return {"messages": [AIMessage(content=prompt)], "current_step": "waiting_for_input"}
|
|
176
171
|
|
|
177
172
|
return human_input_node
|
|
178
173
|
|
|
@@ -190,10 +185,8 @@ def create_summary_node():
|
|
|
190
185
|
|
|
191
186
|
# 提取 AI 的最后一条消息作为最终响应
|
|
192
187
|
for msg in reversed(messages):
|
|
193
|
-
if isinstance(msg, AIMessage) and not getattr(msg,
|
|
194
|
-
return {
|
|
195
|
-
"final_response": msg.content
|
|
196
|
-
}
|
|
188
|
+
if isinstance(msg, AIMessage) and not getattr(msg, "tool_calls", None):
|
|
189
|
+
return {"final_response": msg.content}
|
|
197
190
|
|
|
198
191
|
return {"final_response": "分析完成。"}
|
|
199
192
|
|
|
@@ -280,7 +280,7 @@ def get_provider(config: AIConfig | None = None) -> LLMProvider:
|
|
|
280
280
|
|
|
281
281
|
if provider_class is None:
|
|
282
282
|
raise ValueError(
|
|
283
|
-
f"不支持的提供商: {provider_name}.
|
|
283
|
+
f"不支持的提供商: {provider_name}. 支持的提供商: {', '.join(providers.keys())}"
|
|
284
284
|
)
|
|
285
285
|
|
|
286
286
|
return provider_class(config) # type: ignore[abstract]
|