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.
Files changed (297) hide show
  1. {fund_cli-3.1.0 → fund_cli-3.2.0}/CHANGELOG.md +39 -0
  2. {fund_cli-3.1.0 → fund_cli-3.2.0}/PKG-INFO +20 -2
  3. {fund_cli-3.1.0 → fund_cli-3.2.0}/README.md +19 -1
  4. {fund_cli-3.1.0 → fund_cli-3.2.0}/pyproject.toml +1 -1
  5. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/__init__.py +1 -1
  6. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/ai/agent.py +13 -48
  7. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/ai/analyzer.py +6 -6
  8. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/ai/memory.py +1 -3
  9. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/ai/nodes.py +10 -17
  10. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/ai/providers.py +1 -1
  11. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/ai/tools.py +733 -640
  12. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/analysis/performance.py +24 -0
  13. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/cli.py +3 -0
  14. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/commands/ai_cmd.py +6 -7
  15. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/commands/analyze_cmd.py +85 -3
  16. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/commands/config_cmd.py +10 -2
  17. fund_cli-3.2.0/src/fund_cli/commands/report_cmd.py +202 -0
  18. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/config.py +29 -1
  19. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/ai_analyzer.py +44 -15
  20. fund_cli-3.2.0/src/fund_cli/core/ai_validator.py +142 -0
  21. fund_cli-3.2.0/src/fund_cli/core/audit_logger.py +185 -0
  22. fund_cli-3.2.0/src/fund_cli/core/calc_validator.py +185 -0
  23. fund_cli-3.2.0/src/fund_cli/core/cross_validator.py +162 -0
  24. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/data_gateway.py +38 -23
  25. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/data_manager.py +47 -24
  26. fund_cli-3.2.0/src/fund_cli/core/data_quality.py +414 -0
  27. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/monitor.py +167 -7
  28. fund_cli-3.2.0/src/fund_cli/core/quality_gate.py +118 -0
  29. fund_cli-3.2.0/src/fund_cli/core/report_validator.py +131 -0
  30. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/reporter.py +1 -1
  31. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/reporters/__init__.py +2 -0
  32. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/reporters/docx_reporter.py +26 -14
  33. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/reporters/html_reporter.py +23 -2
  34. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/reporters/markdown_reporter.py +8 -1
  35. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/reporters/pdf_reporter.py +13 -5
  36. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/reporters/pptx_reporter.py +30 -6
  37. fund_cli-3.2.0/src/fund_cli/core/reporters/risk_control_reporter.py +264 -0
  38. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/template_engine.py +76 -13
  39. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/data/adapters/__init__.py +11 -0
  40. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/data/adapters/akshare_adapter.py +5 -9
  41. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/data/adapters/mixins.py +6 -18
  42. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/data/adapters/tushare_adapter.py +28 -21
  43. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/data/adapters/wind_adapter.py +2 -0
  44. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/data/base.py +9 -18
  45. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/data/cache.py +49 -3
  46. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/data/normalizer.py +51 -6
  47. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/mcp/__init__.py +2 -2
  48. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/mcp/server.py +20 -44
  49. fund_cli-3.2.0/src/fund_cli/utils/decorators.py +125 -0
  50. fund_cli-3.2.0/src/fund_cli/utils/validators.py +139 -0
  51. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/views/reports.py +6 -6
  52. fund_cli-3.2.0/tests/integration/test_end_to_end.py +131 -0
  53. fund_cli-3.2.0/tests/unit/test_ai/test_analyzer.py +261 -0
  54. fund_cli-3.2.0/tests/unit/test_ai/test_tools.py +71 -0
  55. fund_cli-3.2.0/tests/unit/test_cli.py +232 -0
  56. fund_cli-3.2.0/tests/unit/test_commands/test_ai_cmd.py +288 -0
  57. fund_cli-3.2.0/tests/unit/test_commands/test_analyze_cmd.py +194 -0
  58. fund_cli-3.2.0/tests/unit/test_commands/test_compare_cmd.py +239 -0
  59. fund_cli-3.2.0/tests/unit/test_commands/test_holding_cmd.py +293 -0
  60. fund_cli-3.2.0/tests/unit/test_commands/test_interactive_cmd.py +40 -0
  61. fund_cli-3.2.0/tests/unit/test_commands/test_main.py +62 -0
  62. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_commands/test_report_cmd.py +129 -23
  63. fund_cli-3.2.0/tests/unit/test_core/test_ai_validator.py +103 -0
  64. fund_cli-3.2.0/tests/unit/test_core/test_audit_logger.py +116 -0
  65. fund_cli-3.2.0/tests/unit/test_core/test_calc_validator.py +125 -0
  66. fund_cli-3.2.0/tests/unit/test_core/test_cross_validator.py +109 -0
  67. fund_cli-3.2.0/tests/unit/test_core/test_monitor.py +248 -0
  68. fund_cli-3.2.0/tests/unit/test_core/test_quality_gate.py +92 -0
  69. fund_cli-3.2.0/tests/unit/test_core/test_report_validator.py +130 -0
  70. fund_cli-3.2.0/tests/unit/test_core/test_risk_control_reporter.py +254 -0
  71. fund_cli-3.2.0/tests/unit/test_core/test_risk_parity.py +117 -0
  72. 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
  73. 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
  74. fund_cli-3.1.0/src/fund_cli/commands/report_cmd.py +0 -86
  75. fund_cli-3.1.0/src/fund_cli/core/data_quality.py +0 -162
  76. fund_cli-3.1.0/src/fund_cli/utils/decorators.py +0 -90
  77. fund_cli-3.1.0/src/fund_cli/utils/validators.py +0 -77
  78. fund_cli-3.1.0/tests/integration/test_end_to_end.py +0 -1430
  79. fund_cli-3.1.0/tests/unit/test_ai/test_analyzer.py +0 -740
  80. fund_cli-3.1.0/tests/unit/test_ai/test_tools.py +0 -1152
  81. fund_cli-3.1.0/tests/unit/test_commands/test_analyze_cmd.py +0 -502
  82. fund_cli-3.1.0/tests/unit/test_commands/test_cli.py +0 -104
  83. fund_cli-3.1.0/tests/unit/test_commands/test_compare_cmd.py +0 -336
  84. fund_cli-3.1.0/tests/unit/test_commands/test_holding_cmd.py +0 -30
  85. fund_cli-3.1.0/tests/unit/test_core/test_monitor.py +0 -122
  86. {fund_cli-3.1.0 → fund_cli-3.2.0}/.env.example +0 -0
  87. {fund_cli-3.1.0 → fund_cli-3.2.0}/.gitignore +0 -0
  88. {fund_cli-3.1.0 → fund_cli-3.2.0}/.pre-commit-config.yaml +0 -0
  89. {fund_cli-3.1.0 → fund_cli-3.2.0}/.trae/documents/documentation-verification-plan.md +0 -0
  90. {fund_cli-3.1.0 → fund_cli-3.2.0}/CODE_OF_CONDUCT.md +0 -0
  91. {fund_cli-3.1.0 → fund_cli-3.2.0}/CONTRIBUTING.md +0 -0
  92. {fund_cli-3.1.0 → fund_cli-3.2.0}/Dockerfile +0 -0
  93. {fund_cli-3.1.0 → fund_cli-3.2.0}/LICENSE +0 -0
  94. {fund_cli-3.1.0 → fund_cli-3.2.0}/Makefile +0 -0
  95. {fund_cli-3.1.0 → fund_cli-3.2.0}/SECURITY.md +0 -0
  96. {fund_cli-3.1.0 → fund_cli-3.2.0}/comparison_report.md +0 -0
  97. {fund_cli-3.1.0 → fund_cli-3.2.0}/docker-compose.yml +0 -0
  98. {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/api/ai.md +0 -0
  99. {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/api/analysis.md +0 -0
  100. {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/api/core.md +0 -0
  101. {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/api/data.md +0 -0
  102. {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/api/optimizers.md +0 -0
  103. {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/api/reference.md +0 -0
  104. {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/api/reporters.md +0 -0
  105. {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/contributing.md +0 -0
  106. {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/development.md +0 -0
  107. {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/index.md +0 -0
  108. {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/installation.md +0 -0
  109. {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/usage/ai-analysis.md +0 -0
  110. {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/usage/analysis.md +0 -0
  111. {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/usage/comparison.md +0 -0
  112. {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/usage/filter-and-data.md +0 -0
  113. {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/usage/holding-and-manager.md +0 -0
  114. {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/usage/index.md +0 -0
  115. {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/usage/interactive.md +0 -0
  116. {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/usage/monitoring.md +0 -0
  117. {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/usage/optimization.md +0 -0
  118. {fund_cli-3.1.0 → fund_cli-3.2.0}/docs/usage/report-generation.md +0 -0
  119. {fund_cli-3.1.0 → fund_cli-3.2.0}/examples/basic_analysis.py +0 -0
  120. {fund_cli-3.1.0 → fund_cli-3.2.0}/examples/batch_processing.py +0 -0
  121. {fund_cli-3.1.0 → fund_cli-3.2.0}/examples/portfolio_optimization.py +0 -0
  122. {fund_cli-3.1.0 → fund_cli-3.2.0}/mkdocs.yml +0 -0
  123. {fund_cli-3.1.0 → fund_cli-3.2.0}/report_market_flow_portfolio.html +0 -0
  124. {fund_cli-3.1.0 → fund_cli-3.2.0}/report_portfolio_portfolio.html +0 -0
  125. {fund_cli-3.1.0 → fund_cli-3.2.0}/report_risk_control_portfolio.html +0 -0
  126. {fund_cli-3.1.0 → fund_cli-3.2.0}/report_single_fund_000001.html +0 -0
  127. {fund_cli-3.1.0 → fund_cli-3.2.0}/report_single_fund_000001.md +0 -0
  128. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/404.html +0 -0
  129. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/api/ai/index.html +0 -0
  130. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/api/analysis/index.html +0 -0
  131. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/api/core/index.html +0 -0
  132. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/api/data/index.html +0 -0
  133. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/api/optimizers/index.html +0 -0
  134. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/api/reference/index.html +0 -0
  135. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/api/reporters/index.html +0 -0
  136. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/_mkdocstrings.css +0 -0
  137. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/images/favicon.png +0 -0
  138. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/bundle.79ae519e.min.js +0 -0
  139. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/bundle.79ae519e.min.js.map +0 -0
  140. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.ar.min.js +0 -0
  141. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.da.min.js +0 -0
  142. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.de.min.js +0 -0
  143. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.du.min.js +0 -0
  144. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.el.min.js +0 -0
  145. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.es.min.js +0 -0
  146. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.fi.min.js +0 -0
  147. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.fr.min.js +0 -0
  148. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.he.min.js +0 -0
  149. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.hi.min.js +0 -0
  150. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.hu.min.js +0 -0
  151. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.hy.min.js +0 -0
  152. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.it.min.js +0 -0
  153. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.ja.min.js +0 -0
  154. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.jp.min.js +0 -0
  155. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.kn.min.js +0 -0
  156. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.ko.min.js +0 -0
  157. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.multi.min.js +0 -0
  158. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.nl.min.js +0 -0
  159. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.no.min.js +0 -0
  160. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.pt.min.js +0 -0
  161. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.ro.min.js +0 -0
  162. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.ru.min.js +0 -0
  163. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.sa.min.js +0 -0
  164. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.stemmer.support.min.js +0 -0
  165. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.sv.min.js +0 -0
  166. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.ta.min.js +0 -0
  167. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.te.min.js +0 -0
  168. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.th.min.js +0 -0
  169. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.tr.min.js +0 -0
  170. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.vi.min.js +0 -0
  171. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/min/lunr.zh.min.js +0 -0
  172. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/tinyseg.js +0 -0
  173. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/lunr/wordcut.js +0 -0
  174. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/workers/search.2c215733.min.js +0 -0
  175. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/javascripts/workers/search.2c215733.min.js.map +0 -0
  176. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/stylesheets/main.484c7ddc.min.css +0 -0
  177. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/stylesheets/main.484c7ddc.min.css.map +0 -0
  178. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/stylesheets/palette.ab4e12ef.min.css +0 -0
  179. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/assets/stylesheets/palette.ab4e12ef.min.css.map +0 -0
  180. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/contributing/index.html +0 -0
  181. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/development/index.html +0 -0
  182. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/index.html +0 -0
  183. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/installation/index.html +0 -0
  184. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/objects.inv +0 -0
  185. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/search/search_index.json +0 -0
  186. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/sitemap.xml +0 -0
  187. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/sitemap.xml.gz +0 -0
  188. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/usage/ai-analysis/index.html +0 -0
  189. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/usage/analysis/index.html +0 -0
  190. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/usage/comparison/index.html +0 -0
  191. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/usage/filter-and-data/index.html +0 -0
  192. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/usage/holding-and-manager/index.html +0 -0
  193. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/usage/index.html +0 -0
  194. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/usage/interactive/index.html +0 -0
  195. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/usage/monitoring/index.html +0 -0
  196. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/usage/optimization/index.html +0 -0
  197. {fund_cli-3.1.0 → fund_cli-3.2.0}/site/usage/report-generation/index.html +0 -0
  198. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/__main__.py +0 -0
  199. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/ai/__init__.py +0 -0
  200. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/ai/prompts.py +0 -0
  201. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/ai/state.py +0 -0
  202. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/analysis/__init__.py +0 -0
  203. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/analysis/attribution.py +0 -0
  204. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/analysis/backtest.py +0 -0
  205. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/analysis/holding.py +0 -0
  206. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/analysis/manager.py +0 -0
  207. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/analysis/portfolio.py +0 -0
  208. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/analysis/risk.py +0 -0
  209. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/commands/__init__.py +0 -0
  210. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/commands/compare_cmd.py +0 -0
  211. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/commands/data_cmd.py +0 -0
  212. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/commands/filter_cmd.py +0 -0
  213. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/commands/holding_cmd.py +0 -0
  214. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/commands/interactive_cmd.py +0 -0
  215. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/commands/main.py +0 -0
  216. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/commands/manager_cmd.py +0 -0
  217. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/commands/monitor_cmd.py +0 -0
  218. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/commands/optimize_cmd.py +0 -0
  219. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/__init__.py +0 -0
  220. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/analyzer.py +0 -0
  221. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/optimizer.py +0 -0
  222. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/optimizers/__init__.py +0 -0
  223. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/optimizers/efficient_frontier.py +0 -0
  224. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/optimizers/max_sharpe.py +0 -0
  225. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/optimizers/mean_variance.py +0 -0
  226. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/optimizers/risk_parity.py +0 -0
  227. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/core/screener.py +0 -0
  228. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/data/__init__.py +0 -0
  229. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/data/models.py +0 -0
  230. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/templates/base.html +0 -0
  231. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/templates/market_flow/report.html +0 -0
  232. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/templates/portfolio/report.html +0 -0
  233. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/templates/risk_control/report.html +0 -0
  234. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/templates/single_fund/report.html +0 -0
  235. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/utils/__init__.py +0 -0
  236. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/utils/helpers.py +0 -0
  237. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/views/__init__.py +0 -0
  238. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/views/charts.py +0 -0
  239. {fund_cli-3.1.0 → fund_cli-3.2.0}/src/fund_cli/views/tables.py +0 -0
  240. {fund_cli-3.1.0 → fund_cli-3.2.0}/test_mvp.py +0 -0
  241. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/__init__.py +0 -0
  242. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/conftest.py +0 -0
  243. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/fixtures/sample_data.py +0 -0
  244. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/integration/__init__.py +0 -0
  245. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/integration/test_data_flow.py +0 -0
  246. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/integration/test_monitoring_flow.py +0 -0
  247. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/integration/test_optimization_flow.py +0 -0
  248. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/performance/__init__.py +0 -0
  249. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/performance/test_performance.py +0 -0
  250. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_ai/__init__.py +0 -0
  251. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_ai/test_agent.py +0 -0
  252. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_ai/test_prompts.py +0 -0
  253. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_ai/test_providers.py +0 -0
  254. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_analysis/test_attribution.py +0 -0
  255. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_analysis/test_backtest.py +0 -0
  256. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_analysis/test_holding.py +0 -0
  257. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_analysis/test_manager.py +0 -0
  258. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_analysis/test_performance.py +0 -0
  259. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_analysis/test_performance_enhanced.py +0 -0
  260. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_base_classes.py +0 -0
  261. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_commands/test_config_cmd.py +0 -0
  262. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_commands/test_data_cmd.py +0 -0
  263. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_commands/test_filter_cmd.py +0 -0
  264. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_commands/test_interactive.py +0 -0
  265. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_commands/test_manager_cmd.py +0 -0
  266. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_commands/test_monitor_cmd.py +0 -0
  267. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_commands/test_optimize_cmd.py +0 -0
  268. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_core/__init__.py +0 -0
  269. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_core/test_ai_analyzer.py +0 -0
  270. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_core/test_ai_analyzer_extended.py +0 -0
  271. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_core/test_data_gateway.py +0 -0
  272. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_core/test_data_manager.py +0 -0
  273. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_core/test_data_manager_extended.py +0 -0
  274. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_core/test_data_quality.py +0 -0
  275. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_core/test_data_quality_extended.py +0 -0
  276. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_core/test_docx_pptx_reporter.py +0 -0
  277. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_core/test_optimizers.py +0 -0
  278. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_core/test_pdf_reporter.py +0 -0
  279. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_core/test_reporters.py +0 -0
  280. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_core/test_screener.py +0 -0
  281. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_core/test_template_engine.py +0 -0
  282. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_data/test_akshare_adapter.py +0 -0
  283. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_data/test_akshare_adapter_extended.py +0 -0
  284. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_data/test_akshare_p0.py +0 -0
  285. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_data/test_akshare_p1.py +0 -0
  286. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_data/test_akshare_p2.py +0 -0
  287. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_data/test_base.py +0 -0
  288. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_data/test_cache_extended.py +0 -0
  289. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_data/test_mixins.py +0 -0
  290. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_data/test_models.py +0 -0
  291. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_data/test_models_extended.py +0 -0
  292. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_data/test_normalizer.py +0 -0
  293. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_data/test_tushare_adapter.py +0 -0
  294. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_data/test_wind_adapter.py +0 -0
  295. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_utils/__init__.py +0 -0
  296. {fund_cli-3.1.0 → fund_cli-3.2.0}/tests/unit/test_utils/test_helpers.py +0 -0
  297. {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.1.0
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
- [![Version](https://img.shields.io/badge/version-3.1.0-blue.svg)](https://github.com/jarrey-0804/fund-cli)
75
+ [![Version](https://img.shields.io/badge/version-3.2.0-blue.svg)](https://github.com/jarrey-0804/fund-cli)
76
76
  [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
77
77
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
78
78
  [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](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
- [![Version](https://img.shields.io/badge/version-3.1.0-blue.svg)](https://github.com/jarrey-0804/fund-cli)
7
+ [![Version](https://img.shields.io/badge/version-3.2.0-blue.svg)](https://github.com/jarrey-0804/fund-cli)
8
8
  [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
9
9
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
10
10
  [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](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
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "fund-cli"
3
- version = "3.1.0"
3
+ version = "3.2.0"
4
4
  description = "专业基金分析CLI工具 - 面向机构客户"
5
5
  readme = "README.md"
6
6
  license = {text = "MIT"}
@@ -12,7 +12,7 @@ warnings.filterwarnings(
12
12
  message=r".*allowed_objects.*will change.*",
13
13
  )
14
14
 
15
- __version__ = "3.1.0"
15
+ __version__ = "3.2.0"
16
16
  __author__ = "Fund CLI Team"
17
17
  __license__ = "MIT"
18
18
 
@@ -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 or "https://dashscope.aliyuncs.com/compatible-mode/v1",
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, 'content'):
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('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)}%
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 isinstance(last_message, AIMessage) and hasattr(last_message, 'tool_calls') and last_message.tool_calls:
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, 'tool_calls', None):
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}. " f"支持的提供商: {', '.join(providers.keys())}"
283
+ f"不支持的提供商: {provider_name}. 支持的提供商: {', '.join(providers.keys())}"
284
284
  )
285
285
 
286
286
  return provider_class(config) # type: ignore[abstract]