quantnodes 3.0.0__py3-none-any.whl

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 (399) hide show
  1. QuantNodes/__init__.py +15 -0
  2. QuantNodes/__main__.py +14 -0
  3. QuantNodes/agent/__init__.py +158 -0
  4. QuantNodes/agent/agents/__init__.py +13 -0
  5. QuantNodes/agent/agents/definition.py +180 -0
  6. QuantNodes/agent/agents/manager.py +73 -0
  7. QuantNodes/agent/config/__init__.py +34 -0
  8. QuantNodes/agent/config/executor.py +958 -0
  9. QuantNodes/agent/config/loader.py +427 -0
  10. QuantNodes/agent/config/templates/bollinger_bands.yaml +84 -0
  11. QuantNodes/agent/config/templates/dual_ma.yaml +72 -0
  12. QuantNodes/agent/config/templates/empty.yaml +56 -0
  13. QuantNodes/agent/config/templates/mean_reversion.yaml +47 -0
  14. QuantNodes/agent/config/templates/mean_reversion_zscore.yaml +90 -0
  15. QuantNodes/agent/config/templates/momentum.yaml +81 -0
  16. QuantNodes/agent/config/templates/momentum_breakout.yaml +84 -0
  17. QuantNodes/agent/config/templates/rsi_strategy.yaml +72 -0
  18. QuantNodes/agent/config/templates/volume_price.yaml +86 -0
  19. QuantNodes/agent/config/types.py +156 -0
  20. QuantNodes/agent/config_mapper.py +293 -0
  21. QuantNodes/agent/core/__init__.py +19 -0
  22. QuantNodes/agent/core/dream.py +47 -0
  23. QuantNodes/agent/core/quant_dream.py +274 -0
  24. QuantNodes/agent/cron_jobs.py +314 -0
  25. QuantNodes/agent/nanobot_bridge.py +242 -0
  26. QuantNodes/agent/permission/__init__.py +30 -0
  27. QuantNodes/agent/permission/defaults.py +36 -0
  28. QuantNodes/agent/permission/evaluate.py +41 -0
  29. QuantNodes/agent/permission/models.py +59 -0
  30. QuantNodes/agent/permission/service.py +133 -0
  31. QuantNodes/agent/providers/__init__.py +11 -0
  32. QuantNodes/agent/providers/base.py +102 -0
  33. QuantNodes/agent/providers/quantnodes.py +610 -0
  34. QuantNodes/agent/providers/rate_limiter.py +326 -0
  35. QuantNodes/agent/providers/registry.py +163 -0
  36. QuantNodes/agent/skills/__init__.py +20 -0
  37. QuantNodes/agent/skills/base.py +118 -0
  38. QuantNodes/agent/skills/bridge.py +73 -0
  39. QuantNodes/agent/skills/factor/__init__.py +14 -0
  40. QuantNodes/agent/skills/factor/correlation.py +99 -0
  41. QuantNodes/agent/skills/factor/group_backtest.py +114 -0
  42. QuantNodes/agent/skills/factor/ic_analysis.py +106 -0
  43. QuantNodes/agent/skills/loader.py +107 -0
  44. QuantNodes/agent/skills/registry.py +105 -0
  45. QuantNodes/agent/skills/strategy/__init__.py +16 -0
  46. QuantNodes/agent/skills/strategy/bollinger.py +86 -0
  47. QuantNodes/agent/skills/strategy/dual_ma.py +82 -0
  48. QuantNodes/agent/skills/strategy/momentum.py +74 -0
  49. QuantNodes/agent/skills/strategy/rsi_reversal.py +99 -0
  50. QuantNodes/agent/skills_quant/__init__.py +14 -0
  51. QuantNodes/agent/skills_quant/backtest-analyze/SKILL.md +42 -0
  52. QuantNodes/agent/skills_quant/config-driven/SKILL.md +72 -0
  53. QuantNodes/agent/skills_quant/factor-research/SKILL.md +40 -0
  54. QuantNodes/agent/skills_quant/quant-dream/SKILL.md +55 -0
  55. QuantNodes/agent/skills_quant/risk-management/SKILL.md +45 -0
  56. QuantNodes/agent/skills_quant/strategy-design/SKILL.md +43 -0
  57. QuantNodes/agent/templates/__init__.py +4 -0
  58. QuantNodes/agent/tools/__init__.py +173 -0
  59. QuantNodes/agent/tools/_workspace.py +51 -0
  60. QuantNodes/agent/tools/alpha_backtest.py +328 -0
  61. QuantNodes/agent/tools/alpha_evaluate.py +493 -0
  62. QuantNodes/agent/tools/backtest.py +226 -0
  63. QuantNodes/agent/tools/base.py +133 -0
  64. QuantNodes/agent/tools/code_search.py +207 -0
  65. QuantNodes/agent/tools/config_backtest.py +401 -0
  66. QuantNodes/agent/tools/context.py +97 -0
  67. QuantNodes/agent/tools/dream_skill.py +77 -0
  68. QuantNodes/agent/tools/echo.py +38 -0
  69. QuantNodes/agent/tools/factor.py +231 -0
  70. QuantNodes/agent/tools/file_ops.py +201 -0
  71. QuantNodes/agent/tools/git_ops.py +190 -0
  72. QuantNodes/agent/tools/operator_lookup.py +218 -0
  73. QuantNodes/agent/tools/output_truncation.py +77 -0
  74. QuantNodes/agent/tools/path_check.py +43 -0
  75. QuantNodes/agent/tools/pipeline.py +62 -0
  76. QuantNodes/agent/tools/registry.py +150 -0
  77. QuantNodes/agent/tools/sandbox.py +62 -0
  78. QuantNodes/agent/tools/shell_safety.py +63 -0
  79. QuantNodes/agent/tools/strategy.py +106 -0
  80. QuantNodes/agent/tools/task.py +171 -0
  81. QuantNodes/agent/tools/web_fetch.py +142 -0
  82. QuantNodes/agent/tools/web_search.py +114 -0
  83. QuantNodes/agent/tools/wiki.py +370 -0
  84. QuantNodes/agent/utils/__init__.py +11 -0
  85. QuantNodes/agent/utils/helpers.py +43 -0
  86. QuantNodes/agent/utils/prompt_templates.py +30 -0
  87. QuantNodes/agent/workflows/__init__.py +20 -0
  88. QuantNodes/agent/workflows/implementations/__init__.py +8 -0
  89. QuantNodes/agent/workflows/implementations/alpha_gpt.py +508 -0
  90. QuantNodes/agent/workflows/implementations/mcts.py +442 -0
  91. QuantNodes/agent/workflows/parsers.py +44 -0
  92. QuantNodes/agent/workflows/registry.py +119 -0
  93. QuantNodes/agent/workflows/step_agent.py +219 -0
  94. QuantNodes/agent/workflows/tool.py +198 -0
  95. QuantNodes/ai/__init__.py +93 -0
  96. QuantNodes/ai/llm/__init__.py +75 -0
  97. QuantNodes/ai/llm/base.py +233 -0
  98. QuantNodes/ai/llm/decorators.py +281 -0
  99. QuantNodes/ai/llm/gateway.py +571 -0
  100. QuantNodes/ai/llm/null.py +76 -0
  101. QuantNodes/ai/llm/openai.py +435 -0
  102. QuantNodes/ai/optimizer.py +405 -0
  103. QuantNodes/ai/prompts/__init__.py +229 -0
  104. QuantNodes/ai/sandbox.py +371 -0
  105. QuantNodes/ai/sandbox_pandas_bridge.py +150 -0
  106. QuantNodes/ai/strategy_gen.py +396 -0
  107. QuantNodes/backtest/__init__.py +64 -0
  108. QuantNodes/backtest/backtest_node.py +188 -0
  109. QuantNodes/backtest/broker_node.py +378 -0
  110. QuantNodes/backtest/config_runner.py +397 -0
  111. QuantNodes/backtest/config_strategy.py +64 -0
  112. QuantNodes/backtest/risk_node.py +360 -0
  113. QuantNodes/backtest/strategy_node.py +268 -0
  114. QuantNodes/cache_node/__init__.py +19 -0
  115. QuantNodes/cache_node/base.py +244 -0
  116. QuantNodes/cache_node/cache_store.py +99 -0
  117. QuantNodes/cache_node/metadata.py +100 -0
  118. QuantNodes/cli/__init__.py +109 -0
  119. QuantNodes/cli/_helpers.py +511 -0
  120. QuantNodes/cli/command.py +110 -0
  121. QuantNodes/cli/commands/__init__.py +69 -0
  122. QuantNodes/cli/commands/agent.py +158 -0
  123. QuantNodes/cli/commands/alpha.py +951 -0
  124. QuantNodes/cli/commands/chat.py +38 -0
  125. QuantNodes/cli/commands/evolve.py +120 -0
  126. QuantNodes/cli/commands/factor.py +569 -0
  127. QuantNodes/cli/commands/init.py +190 -0
  128. QuantNodes/cli/commands/run.py +259 -0
  129. QuantNodes/cli/commands/serve.py +398 -0
  130. QuantNodes/cli/commands/version.py +120 -0
  131. QuantNodes/cli/enhanced.py +146 -0
  132. QuantNodes/conf_node/__init__.py +37 -0
  133. QuantNodes/conf_node/base.py +120 -0
  134. QuantNodes/conf_node/env_config.py +132 -0
  135. QuantNodes/conf_node/ini_config.py +70 -0
  136. QuantNodes/conf_node/json_config.py +69 -0
  137. QuantNodes/conf_node/yaml_config.py +78 -0
  138. QuantNodes/constants.py +17 -0
  139. QuantNodes/core/__init__.py +196 -0
  140. QuantNodes/core/_lookback_helpers.py +49 -0
  141. QuantNodes/core/ast_parser.py +198 -0
  142. QuantNodes/core/base.py +61 -0
  143. QuantNodes/core/cache_manager.py +344 -0
  144. QuantNodes/core/cache_utils.py +150 -0
  145. QuantNodes/core/cond_builder.py +53 -0
  146. QuantNodes/core/config.py +170 -0
  147. QuantNodes/core/constants.py +48 -0
  148. QuantNodes/core/control.py +412 -0
  149. QuantNodes/core/data_preprocessing.py +453 -0
  150. QuantNodes/core/data_source.py +46 -0
  151. QuantNodes/core/events.py +178 -0
  152. QuantNodes/core/evolution/__init__.py +22 -0
  153. QuantNodes/core/evolution/loop.py +583 -0
  154. QuantNodes/core/evolution/operators.py +289 -0
  155. QuantNodes/core/evolution/settings.py +44 -0
  156. QuantNodes/core/expression.py +841 -0
  157. QuantNodes/core/feedback/__init__.py +38 -0
  158. QuantNodes/core/feedback/channels.py +182 -0
  159. QuantNodes/core/feedback/collector.py +91 -0
  160. QuantNodes/core/feedback/dataclass.py +239 -0
  161. QuantNodes/core/feedback/llm_judge.py +138 -0
  162. QuantNodes/core/knowledge/__init__.py +69 -0
  163. QuantNodes/core/knowledge/knowledge_base.py +217 -0
  164. QuantNodes/core/knowledge/lineage_compress.py +196 -0
  165. QuantNodes/core/knowledge/lineage_expand.py +123 -0
  166. QuantNodes/core/knowledge/metrics/__init__.py +43 -0
  167. QuantNodes/core/knowledge/metrics/evaluator.py +176 -0
  168. QuantNodes/core/knowledge/metrics/metrics.py +220 -0
  169. QuantNodes/core/knowledge/rag_prompt.py +196 -0
  170. QuantNodes/core/knowledge/retriever.py +209 -0
  171. QuantNodes/core/lambda_node.py +81 -0
  172. QuantNodes/core/monitoring/__init__.py +22 -0
  173. QuantNodes/core/monitoring/collector.py +292 -0
  174. QuantNodes/core/monitoring/dashboard.py +365 -0
  175. QuantNodes/core/node.py +375 -0
  176. QuantNodes/core/pandas_utils.py +504 -0
  177. QuantNodes/core/parallel/__init__.py +15 -0
  178. QuantNodes/core/parallel/worker.py +140 -0
  179. QuantNodes/core/parallel/worker_process.py +265 -0
  180. QuantNodes/core/path_utils.py +73 -0
  181. QuantNodes/core/pipeline.py +328 -0
  182. QuantNodes/core/plugin.py +135 -0
  183. QuantNodes/core/quality_gate/__init__.py +32 -0
  184. QuantNodes/core/quality_gate/complexity.py +94 -0
  185. QuantNodes/core/quality_gate/consistency.py +26 -0
  186. QuantNodes/core/quality_gate/node.py +97 -0
  187. QuantNodes/core/quality_gate/redundancy.py +51 -0
  188. QuantNodes/core/quality_gate/settings.py +43 -0
  189. QuantNodes/core/quality_gate/zoo.py +98 -0
  190. QuantNodes/core/serializable.py +116 -0
  191. QuantNodes/core/serialization.py +673 -0
  192. QuantNodes/core/tools.py +333 -0
  193. QuantNodes/core/trajectory/__init__.py +25 -0
  194. QuantNodes/core/trajectory/entry.py +116 -0
  195. QuantNodes/core/trajectory/lineage.py +67 -0
  196. QuantNodes/core/trajectory/pool.py +211 -0
  197. QuantNodes/core/trajectory/selector.py +140 -0
  198. QuantNodes/core/visualization/__init__.py +33 -0
  199. QuantNodes/core/visualization/builder.py +233 -0
  200. QuantNodes/core/visualization/gate_breakdown.py +140 -0
  201. QuantNodes/core/visualization/lineage_dag.py +203 -0
  202. QuantNodes/core/visualization/metric_distribution.py +125 -0
  203. QuantNodes/core/visualization/report.py +68 -0
  204. QuantNodes/database_node/__init__.py +69 -0
  205. QuantNodes/database_node/base.py +135 -0
  206. QuantNodes/database_node/clickhouse_node.py +272 -0
  207. QuantNodes/database_node/csv_node.py +83 -0
  208. QuantNodes/database_node/duckdb_node.py +86 -0
  209. QuantNodes/database_node/factory.py +83 -0
  210. QuantNodes/database_node/mysql_node.py +100 -0
  211. QuantNodes/database_node/parquet_node.py +75 -0
  212. QuantNodes/database_node/sqlite_node.py +67 -0
  213. QuantNodes/factor_node/__init__.py +50 -0
  214. QuantNodes/factor_node/factor.py +563 -0
  215. QuantNodes/factor_node/factor_db.py +421 -0
  216. QuantNodes/factor_node/factor_functions/__init__.py +252 -0
  217. QuantNodes/factor_node/factor_functions/_helpers.py +358 -0
  218. QuantNodes/factor_node/factor_functions/_helpers_debug.py +317 -0
  219. QuantNodes/factor_node/factor_functions/composite_ops.py +136 -0
  220. QuantNodes/factor_node/factor_functions/math_ops.py +433 -0
  221. QuantNodes/factor_node/factor_functions/section_ops.py +290 -0
  222. QuantNodes/factor_node/factor_functions/talib_ops.py +1293 -0
  223. QuantNodes/factor_node/factor_functions/time_ops.py +535 -0
  224. QuantNodes/factor_node/factor_operation.py +1115 -0
  225. QuantNodes/factor_node/factor_table.py +1073 -0
  226. QuantNodes/factor_node/quant_nodes_object.py +60 -0
  227. QuantNodes/mcp_server/__init__.py +27 -0
  228. QuantNodes/mcp_server/__main__.py +4 -0
  229. QuantNodes/mcp_server/server.py +272 -0
  230. QuantNodes/methods/__init__.py +28 -0
  231. QuantNodes/methods/pipeline.py +100 -0
  232. QuantNodes/methods/sandbox.py +102 -0
  233. QuantNodes/monitor/__init__.py +27 -0
  234. QuantNodes/monitor/agent_tools/__init__.py +5 -0
  235. QuantNodes/monitor/agent_tools/monitor_tool.py +98 -0
  236. QuantNodes/monitor/agent_tools/schedule_tool.py +98 -0
  237. QuantNodes/monitor/agent_tools/version_tool.py +133 -0
  238. QuantNodes/monitor/monitor/__init__.py +6 -0
  239. QuantNodes/monitor/monitor/alerter.py +60 -0
  240. QuantNodes/monitor/monitor/collector.py +164 -0
  241. QuantNodes/monitor/monitor/dashboard.py +115 -0
  242. QuantNodes/monitor/monitor/drift.py +190 -0
  243. QuantNodes/monitor/scheduler/__init__.py +4 -0
  244. QuantNodes/monitor/scheduler/runner.py +133 -0
  245. QuantNodes/monitor/scheduler/scheduler.py +184 -0
  246. QuantNodes/monitor/storage/__init__.py +16 -0
  247. QuantNodes/monitor/storage/models.py +70 -0
  248. QuantNodes/monitor/storage/repository.py +407 -0
  249. QuantNodes/monitor/version/__init__.py +4 -0
  250. QuantNodes/monitor/version/diff.py +81 -0
  251. QuantNodes/monitor/version/version_manager.py +182 -0
  252. QuantNodes/operator_node/__init__.py +28 -0
  253. QuantNodes/operator_node/base.py +97 -0
  254. QuantNodes/operator_node/query_node.py +129 -0
  255. QuantNodes/operator_node/sql_builder.py +125 -0
  256. QuantNodes/operator_node/sql_utils.py +172 -0
  257. QuantNodes/operator_node/transform.py +130 -0
  258. QuantNodes/operators/__init__.py +90 -0
  259. QuantNodes/operators/_engine.py +108 -0
  260. QuantNodes/operators/composite.py +161 -0
  261. QuantNodes/operators/composite_dag.py +667 -0
  262. QuantNodes/operators/composite_dag_ops.py +343 -0
  263. QuantNodes/operators/composite_dag_pandas_ops.py +382 -0
  264. QuantNodes/operators/custom.py +408 -0
  265. QuantNodes/operators/facade.py +164 -0
  266. QuantNodes/operators/math.py +163 -0
  267. QuantNodes/operators/proxy.py +29 -0
  268. QuantNodes/operators/registry.py +144 -0
  269. QuantNodes/operators/section.py +99 -0
  270. QuantNodes/operators/talib.py +757 -0
  271. QuantNodes/operators/templates.py +95 -0
  272. QuantNodes/operators/time_series.py +136 -0
  273. QuantNodes/prompts/__init__.py +20 -0
  274. QuantNodes/prompts/backtest/__init__.py +12 -0
  275. QuantNodes/prompts/backtest/factor_based.py +86 -0
  276. QuantNodes/prompts/backtest/standard.py +73 -0
  277. QuantNodes/prompts/factor/__init__.py +14 -0
  278. QuantNodes/prompts/factor/correlation.py +77 -0
  279. QuantNodes/prompts/factor/group_backtest.py +86 -0
  280. QuantNodes/prompts/factor/ic_analysis.py +91 -0
  281. QuantNodes/prompts/strategy/__init__.py +18 -0
  282. QuantNodes/prompts/strategy/market_neutral.py +96 -0
  283. QuantNodes/prompts/strategy/mean_reversion.py +107 -0
  284. QuantNodes/prompts/strategy/momentum.py +160 -0
  285. QuantNodes/prompts/strategy/pairs_trading.py +107 -0
  286. QuantNodes/prompts/strategy/trend_following.py +96 -0
  287. QuantNodes/research/README.md +106 -0
  288. QuantNodes/research/__init__.py +154 -0
  289. QuantNodes/research/_legacy_3c/__init__.py +61 -0
  290. QuantNodes/research/_legacy_3c/auto_researcher.py +289 -0
  291. QuantNodes/research/_legacy_3c/factor_evaluator.py +560 -0
  292. QuantNodes/research/_legacy_3c/factor_miner.py +318 -0
  293. QuantNodes/research/_legacy_3c/mcts_search.py +324 -0
  294. QuantNodes/research/factor_test/__init__.py +25 -0
  295. QuantNodes/research/factor_test/config.py +184 -0
  296. QuantNodes/research/factor_test/config_builder.py +276 -0
  297. QuantNodes/research/factor_test/e2e/data_prep.py +163 -0
  298. QuantNodes/research/factor_test/e2e/run_evolution_e2e.py +309 -0
  299. QuantNodes/research/factor_test/evolution_adapter.py +231 -0
  300. QuantNodes/research/factor_test/feedback_wrapper.py +102 -0
  301. QuantNodes/research/factor_test/ifind_db/__init__.py +7 -0
  302. QuantNodes/research/factor_test/ifind_db/fetcher.py +224 -0
  303. QuantNodes/research/factor_test/ifind_db/ifind_database.py +689 -0
  304. QuantNodes/research/factor_test/nodes/__init__.py +1 -0
  305. QuantNodes/research/factor_test/nodes/_base.py +91 -0
  306. QuantNodes/research/factor_test/nodes/adjust_date_node.py +48 -0
  307. QuantNodes/research/factor_test/nodes/configs.py +240 -0
  308. QuantNodes/research/factor_test/nodes/factor_neutralize_node.py +87 -0
  309. QuantNodes/research/factor_test/nodes/factor_preprocess_node.py +222 -0
  310. QuantNodes/research/factor_test/nodes/factor_score_node.py +141 -0
  311. QuantNodes/research/factor_test/nodes/factor_test_report_node.py +153 -0
  312. QuantNodes/research/factor_test/nodes/group_analyzer_node.py +317 -0
  313. QuantNodes/research/factor_test/nodes/ic_analyzer_node.py +112 -0
  314. QuantNodes/research/factor_test/nodes/load_data_node.py +100 -0
  315. QuantNodes/research/factor_test/nodes/long_short_node.py +93 -0
  316. QuantNodes/research/factor_test/nodes/neutralizers.py +222 -0
  317. QuantNodes/research/factor_test/nodes/preprocess_strategies.py +277 -0
  318. QuantNodes/research/factor_test/nodes/risk_correlation_node.py +112 -0
  319. QuantNodes/research/factor_test/nodes/sample_pool_filter_node.py +110 -0
  320. QuantNodes/research/factor_test/nodes/tradability_filter_node.py +92 -0
  321. QuantNodes/research/factor_test/pipeline_runner.py +305 -0
  322. QuantNodes/research/factor_test/pipeline_spec.py +216 -0
  323. QuantNodes/research/factor_test/utils/__init__.py +26 -0
  324. QuantNodes/research/factor_test/utils/constants.py +86 -0
  325. QuantNodes/research/factor_test/utils/data_loader.py +141 -0
  326. QuantNodes/research/factor_test/utils/date_utils.py +232 -0
  327. QuantNodes/research/factor_test/utils/file_loaders.py +150 -0
  328. QuantNodes/research/factor_test/utils/labels.py +37 -0
  329. QuantNodes/research/factor_test/utils/metrics_extractor.py +55 -0
  330. QuantNodes/research/factor_test/utils/performance_metrics.py +175 -0
  331. QuantNodes/research/factor_test/utils/safe_load.py +106 -0
  332. QuantNodes/research/quant_alpha/CHANGELOG.md +80 -0
  333. QuantNodes/research/quant_alpha/README.md +142 -0
  334. QuantNodes/research/quant_alpha/__init__.py +45 -0
  335. QuantNodes/research/quant_alpha/adapters/__init__.py +99 -0
  336. QuantNodes/research/quant_alpha/adapters/calculator.py +503 -0
  337. QuantNodes/research/quant_alpha/adapters/expression.py +387 -0
  338. QuantNodes/research/quant_alpha/alpha101_design/__init__.py +50 -0
  339. QuantNodes/research/quant_alpha/alpha101_design/few_shot_examples.py +243 -0
  340. QuantNodes/research/quant_alpha/alpha101_design/philosophy.py +474 -0
  341. QuantNodes/research/quant_alpha/alpha158_design/__init__.py +63 -0
  342. QuantNodes/research/quant_alpha/alpha158_design/few_shot_examples.py +219 -0
  343. QuantNodes/research/quant_alpha/alpha158_design/philosophy.py +240 -0
  344. QuantNodes/research/quant_alpha/evaluation/__init__.py +47 -0
  345. QuantNodes/research/quant_alpha/evaluation/baselines/__init__.py +8 -0
  346. QuantNodes/research/quant_alpha/evaluation/baselines/g1_handcrafted.py +135 -0
  347. QuantNodes/research/quant_alpha/evaluation/baselines/g2_llm_only.py +269 -0
  348. QuantNodes/research/quant_alpha/evaluation/baselines/g3_alpha_gpt.py +152 -0
  349. QuantNodes/research/quant_alpha/evaluation/clickhouse_data_loader.py +227 -0
  350. QuantNodes/research/quant_alpha/evaluation/contracts.py +376 -0
  351. QuantNodes/research/quant_alpha/evaluation/evaluators/__init__.py +6 -0
  352. QuantNodes/research/quant_alpha/evaluation/evaluators/polars_evaluator.py +545 -0
  353. QuantNodes/research/quant_alpha/evaluation/mock_data_loader.py +226 -0
  354. QuantNodes/research/quant_alpha/evaluation/runner.py +243 -0
  355. QuantNodes/research/quant_alpha/llm/__init__.py +38 -0
  356. QuantNodes/research/quant_alpha/llm/parser.py +681 -0
  357. QuantNodes/research/quant_alpha/logic_driven_pipeline.py +411 -0
  358. QuantNodes/research/quant_alpha/logic_mining/__init__.py +74 -0
  359. QuantNodes/research/quant_alpha/logic_mining/compiler.py +457 -0
  360. QuantNodes/research/quant_alpha/logic_mining/generator.py +366 -0
  361. QuantNodes/research/quant_alpha/logic_mining/models.py +252 -0
  362. QuantNodes/research/quant_alpha/logic_mining/parser.py +287 -0
  363. QuantNodes/research/quant_alpha/logic_mining/pipelines.py +297 -0
  364. QuantNodes/research/quant_alpha/logic_mining/sources.py +149 -0
  365. QuantNodes/research/quant_alpha/mcts/__init__.py +66 -0
  366. QuantNodes/research/quant_alpha/mcts/cache.py +262 -0
  367. QuantNodes/research/quant_alpha/mcts/extension_ops.py +320 -0
  368. QuantNodes/research/quant_alpha/mcts/feedback.py +825 -0
  369. QuantNodes/research/quant_alpha/mcts/op_prior.py +180 -0
  370. QuantNodes/research/quant_alpha/mcts/search.py +540 -0
  371. QuantNodes/research/quant_alpha/mcts/tree.py +201 -0
  372. QuantNodes/research/quant_alpha/operator_vocab/__init__.py +50 -0
  373. QuantNodes/research/quant_alpha/operator_vocab/config.py +54 -0
  374. QuantNodes/research/quant_alpha/operator_vocab/metadata.py +263 -0
  375. QuantNodes/research/quant_alpha/operator_vocab/vocabulary.py +481 -0
  376. QuantNodes/research/quant_alpha/pipeline.py +1027 -0
  377. QuantNodes/research/quant_alpha/types/__init__.py +27 -0
  378. QuantNodes/research/quant_alpha/types/constants.py +28 -0
  379. QuantNodes/research/quant_alpha/types/state.py +205 -0
  380. QuantNodes/research/quant_alpha/workflow/__init__.py +32 -0
  381. QuantNodes/research/quant_alpha/workflow/alpha_gpt.py +911 -0
  382. QuantNodes/research/quant_alpha/workflow/alpha_logics.py +416 -0
  383. QuantNodes/research/quant_alpha/workflow/state.py +27 -0
  384. QuantNodes/research/report_reproducer.py +485 -0
  385. QuantNodes/research/wiki.py +1155 -0
  386. QuantNodes/symbolic/__init__.py +51 -0
  387. QuantNodes/symbolic/compiler.py +113 -0
  388. QuantNodes/symbolic/dialect.py +260 -0
  389. QuantNodes/symbolic/executor.py +147 -0
  390. QuantNodes/symbolic/expression.py +234 -0
  391. QuantNodes/symbolic/functions.py +433 -0
  392. QuantNodes/symbolic/optimizer.py +165 -0
  393. QuantNodes/ui_node/__init__.py +30 -0
  394. QuantNodes/ui_node/base.py +222 -0
  395. quantnodes-3.0.0.dist-info/METADATA +463 -0
  396. quantnodes-3.0.0.dist-info/RECORD +399 -0
  397. quantnodes-3.0.0.dist-info/WHEEL +5 -0
  398. quantnodes-3.0.0.dist-info/entry_points.txt +24 -0
  399. quantnodes-3.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,951 @@
1
+ # coding=utf-8
2
+ """
3
+ CLI 命令: alpha-mcts 等 QuantAlpha 子包相关命令
4
+
5
+ M2 PR 新增:
6
+ - `quantnodes alpha-mcts` —— MCTS 因子搜索
7
+
8
+ 后续 M3-M6 PR 会陆续添加:
9
+ - `quantnodes alpha-101` (M3)
10
+ - `quantnodes alpha-158` (M3)
11
+ - `quantnodes alpha-360` (M3)
12
+ - `quantnodes alpha-gpt` (M6) ← 新增
13
+ - `quantnodes alpha-compare` (M7)
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import argparse
19
+ import json
20
+ import sys
21
+ from pathlib import Path
22
+ from typing import Any, Optional
23
+
24
+ from QuantNodes.cli.command import Command
25
+ from QuantNodes.research.quant_alpha.mcts import (
26
+ MCTSFeedbackConfig,
27
+ MCTSSearch,
28
+ MCTSSearchConfig,
29
+ )
30
+
31
+
32
+ class AlphaMctsCommand(Command):
33
+ """quantnodes alpha-mcts - MCTS 因子搜索
34
+
35
+ 用法:
36
+ quantnodes alpha-mcts --iterations 50
37
+ quantnodes alpha-mcts --iterations 100 --data data.parquet
38
+ quantnodes alpha-mcts --iterations 50 --date-column date
39
+ quantnodes alpha-mcts --iterations 50 --max-depth 6
40
+ """
41
+ name = "alpha-mcts"
42
+ description = "MCTS 因子搜索(QuantAlpha M2)"
43
+
44
+ def add_arguments(self, subparsers: Any) -> None:
45
+ parser = subparsers.add_parser(
46
+ self.name,
47
+ help=self.description,
48
+ description=(
49
+ "基于 OperatorVocab(162 算子)+ 5 通道反馈的 MCTS 因子搜索。\n"
50
+ "详见 docs/quant_alpha/PROJECT_PLAN.md M2 PR。"
51
+ ),
52
+ )
53
+ parser.add_argument(
54
+ "--iterations", "-n",
55
+ type=int, default=50,
56
+ help="MCTS 迭代次数(默认 50)",
57
+ )
58
+ parser.add_argument(
59
+ "--data",
60
+ type=str, default=None,
61
+ help="行情数据路径(Parquet/CSV/JSON),None=用合成数据",
62
+ )
63
+ parser.add_argument(
64
+ "--date-column",
65
+ type=str, default="date",
66
+ help="日期列名(默认 date)",
67
+ )
68
+ parser.add_argument(
69
+ "--code-column",
70
+ type=str, default="code",
71
+ help="股票代码列名(默认 code)",
72
+ )
73
+ parser.add_argument(
74
+ "--max-depth",
75
+ type=int, default=4,
76
+ help="MCTS 树最大深度(默认 4)",
77
+ )
78
+ parser.add_argument(
79
+ "--exploration-weight",
80
+ type=float, default=1.414,
81
+ help="UCB1 exploration weight(默认 sqrt(2) ≈ 1.414)",
82
+ )
83
+ parser.add_argument(
84
+ "--seed",
85
+ type=int, default=42,
86
+ help="随机种子(默认 42)",
87
+ )
88
+ parser.add_argument(
89
+ "--top-k",
90
+ type=int, default=10,
91
+ help="返回 top-k 节点(默认 10)",
92
+ )
93
+ parser.add_argument(
94
+ "--quiet", "-q",
95
+ action="store_true",
96
+ help="安静模式(不打印每个节点详情)",
97
+ )
98
+
99
+ def run(self, args: argparse.Namespace) -> int:
100
+ # 1. 加载数据
101
+ try:
102
+ df = self._load_data(args.data, args.date_column, args.code_column)
103
+ except Exception as e:
104
+ print(f"❌ 数据加载失败: {e}", file=sys.stderr)
105
+ return 1
106
+
107
+ print(f"📊 数据加载完成: {len(df)} 行, {len(df.columns)} 列")
108
+ if not args.quiet:
109
+ print(f" 列: {df.columns[:10].to_list()}{'...' if len(df.columns) > 10 else ''}")
110
+
111
+ # 2. 配置 MCTS
112
+ config = MCTSSearchConfig(
113
+ iterations=args.iterations,
114
+ max_depth=args.max_depth,
115
+ exploration_weight=args.exploration_weight,
116
+ seed=args.seed,
117
+ feedback_config=MCTSFeedbackConfig(),
118
+ )
119
+ mcts = MCTSSearch(config=config)
120
+
121
+ # 3. 执行搜索
122
+ print(f"🔍 开始 MCTS 搜索: iterations={args.iterations}, max_depth={args.max_depth}")
123
+ result = mcts.search(data=df, date_column=args.date_column)
124
+
125
+ # 4. 输出结果
126
+ print()
127
+ print(f"✅ MCTS 搜索完成: {result.elapsed_seconds:.2f}s")
128
+ print(f" 迭代: {result.total_iterations}")
129
+ print(f" 候选公式: {result.formula_count}")
130
+ print(f" 通过验证: {result.valid_count}")
131
+ print(f" 拒绝: {result.rejected_count}")
132
+ print(f" 剪枝: {result.pruned_count}")
133
+ print()
134
+ print(f"🏆 Top {min(args.top_k, len(result.best_k_nodes))} 节点:")
135
+ for i, n in enumerate(result.best_k_nodes[:args.top_k], 1):
136
+ print(
137
+ f" {i:2d}. score={n.overall_score:.3f} "
138
+ f"depth={n.depth} visits={n.visits} "
139
+ f"formula={n.formula!r}"
140
+ )
141
+ if not args.quiet and n.metadata:
142
+ if "feedback_summary" in n.metadata:
143
+ print(f" feedback: {n.metadata['feedback_summary']}")
144
+
145
+ return 0
146
+
147
+ def _load_data(
148
+ self,
149
+ path: Optional[str],
150
+ date_column: str,
151
+ code_column: str,
152
+ ):
153
+ """加载数据:路径 → Parquet/CSV/JSON;None → 合成"""
154
+ import polars as pl
155
+ import numpy as np
156
+
157
+ if path is None:
158
+ # 合成 5 票 × 20 日
159
+ np.random.seed(42)
160
+ dates = (
161
+ [f"2024-01-{d:02d}" for d in range(1, 21)]
162
+ )
163
+ rows = []
164
+ for date in dates:
165
+ for code in ["A", "B", "C", "D", "E"]:
166
+ rows.append({
167
+ date_column: date,
168
+ code_column: code,
169
+ "close": float(np.random.randn() * 5 + 100),
170
+ "open": float(np.random.randn() * 5 + 100),
171
+ "high": float(np.random.randn() * 5 + 102),
172
+ "low": float(np.random.randn() * 5 + 98),
173
+ "vol": float(np.random.randint(1000, 5000)),
174
+ })
175
+ return pl.DataFrame(rows).with_columns(
176
+ pl.col(date_column).str.to_date()
177
+ )
178
+
179
+ p = Path(path)
180
+ if not p.exists():
181
+ raise FileNotFoundError(f"数据文件不存在: {path}")
182
+
183
+ if p.suffix == ".parquet":
184
+ return pl.read_parquet(p)
185
+ if p.suffix == ".csv":
186
+ return pl.read_csv(p)
187
+ if p.suffix == ".json":
188
+ return pl.read_json(p)
189
+ raise ValueError(f"不支持的数据格式: {p.suffix}")
190
+
191
+
192
+ # 后续 M3-M6 PR 会添加:
193
+ # - Alpha101Command (M3)
194
+ # - Alpha158Command (M3)
195
+ # - Alpha360Command (M3)
196
+ # - AlphaGptCommand (M6) ← added
197
+ # - AlphaCompareCommand (M7)
198
+
199
+
200
+ # ==============================================================================
201
+ # M6: Alpha-GPT 命令
202
+ # ==============================================================================
203
+
204
+
205
+ class AlphaGptCommand(Command):
206
+ """quantnodes alpha-gpt - Alpha-GPT 5 智能体自动化因子挖掘
207
+
208
+ 用法:
209
+ quantnodes alpha-gpt --objective "捕捉 A 股反转效应" --data data.parquet
210
+ quantnodes alpha-gpt --objective "..." --iterations 5 --pool-size 10 --backtest
211
+ quantnodes alpha-gpt --objective "..." --llm openai --model gpt-4o
212
+ quantnodes alpha-gpt --objective "..." --quiet --output result.json
213
+
214
+ 详见 docs/quant_alpha/alpha_gpt_user_guide.md
215
+ """
216
+ name = "alpha-gpt"
217
+ description = "Alpha-GPT 5 智能体自动化因子挖掘(QuantAlpha M6)"
218
+
219
+ def add_arguments(self, subparsers: Any) -> None:
220
+ parser = subparsers.add_parser(
221
+ self.name,
222
+ help=self.description,
223
+ description=(
224
+ "基于 nanobot Agent 体系的 5 智能体编排自动化因子挖掘。\n"
225
+ "5 轮 iteration × 5 subagent spawn(idea-generator → formula-translator →\n"
226
+ "evaluator → reflector → critic)。\n"
227
+ "详见 docs/quant_alpha/alpha_gpt_architecture.md。"
228
+ ),
229
+ )
230
+ # 必选
231
+ parser.add_argument(
232
+ "--objective", "-o",
233
+ type=str, required=True,
234
+ help="研究目标(如 '捕捉 A 股反转效应')",
235
+ )
236
+ parser.add_argument(
237
+ "--data", "-d",
238
+ type=str, default=None,
239
+ help="行情数据路径(Parquet/CSV),None=合成测试数据",
240
+ )
241
+ # 工作流
242
+ parser.add_argument(
243
+ "--iterations", "-n",
244
+ type=int, default=5,
245
+ help="迭代轮次(默认 5)",
246
+ )
247
+ parser.add_argument(
248
+ "--pool-size", "-p",
249
+ type=int, default=10,
250
+ help="每轮想法/公式数量(默认 10)",
251
+ )
252
+ parser.add_argument(
253
+ "--top-k", "-k",
254
+ type=int, default=10,
255
+ help="最终返回的 top-K 公式数量(默认 10)",
256
+ )
257
+ parser.add_argument(
258
+ "--min-ir",
259
+ type=float, default=0.5,
260
+ help="IR 阈值(默认 0.5)",
261
+ )
262
+ parser.add_argument(
263
+ "--max-mutual-ic",
264
+ type=float, default=0.7,
265
+ help="最大 mutual IC(默认 0.7)",
266
+ )
267
+ # LLM
268
+ parser.add_argument(
269
+ "--llm",
270
+ type=str, default="deepseek",
271
+ choices=["deepseek", "openai", "qwen", "azure", "mock"],
272
+ help="LLM provider(默认 deepseek)",
273
+ )
274
+ parser.add_argument(
275
+ "--model",
276
+ type=str, default=None,
277
+ help="模型名(如 'gpt-4o' / 'deepseek-chat')",
278
+ )
279
+ parser.add_argument(
280
+ "--temperature",
281
+ type=float, default=0.7,
282
+ help="采样温度(默认 0.7)",
283
+ )
284
+ # 数据
285
+ parser.add_argument(
286
+ "--date-column",
287
+ type=str, default="date",
288
+ help="日期列名(默认 date)",
289
+ )
290
+ parser.add_argument(
291
+ "--code-column",
292
+ type=str, default="code",
293
+ help="股票代码列名(默认 code)",
294
+ )
295
+ parser.add_argument(
296
+ "--forward-returns",
297
+ type=str, default="1,5,20",
298
+ help="前瞻期列表(逗号分隔,默认 '1,5,20')",
299
+ )
300
+ # Trading 回测(可选)
301
+ parser.add_argument(
302
+ "--backtest",
303
+ action="store_true",
304
+ help="启用 Trading 回测(默认禁用,仅对 top-K 评估)",
305
+ )
306
+ parser.add_argument(
307
+ "--top-k-backtest",
308
+ type=int, default=10,
309
+ help="跑回测的 top-K 数量(默认 10)",
310
+ )
311
+ parser.add_argument(
312
+ "--initial-cash",
313
+ type=float, default=1_000_000.0,
314
+ help="初始资金(默认 1,000,000)",
315
+ )
316
+ parser.add_argument(
317
+ "--commission",
318
+ type=float, default=0.001,
319
+ help="手续费率(默认 0.001)",
320
+ )
321
+ # 输出
322
+ parser.add_argument(
323
+ "--output", "-O",
324
+ type=str, default="alpha_pool.json",
325
+ help="结果保存路径(默认 alpha_pool.json)",
326
+ )
327
+ parser.add_argument(
328
+ "--verbose", "-v",
329
+ action="store_true",
330
+ help="详细输出",
331
+ )
332
+ parser.add_argument(
333
+ "--quiet", "-q",
334
+ action="store_true",
335
+ help="安静模式(只输出 final pool)",
336
+ )
337
+
338
+ def run(self, args: argparse.Namespace) -> int:
339
+ # 1. 解析 forward_returns
340
+ try:
341
+ forward_returns = [int(x.strip()) for x in args.forward_returns.split(",") if x.strip()]
342
+ except ValueError:
343
+ print(f"❌ --forward-returns 格式错误: {args.forward_returns}", file=sys.stderr)
344
+ return 1
345
+
346
+ # 2. 加载数据
347
+ try:
348
+ df = self._load_data(args.data, args.date_column, args.code_column)
349
+ except Exception as e:
350
+ print(f"❌ 数据加载失败: {e}", file=sys.stderr)
351
+ return 1
352
+
353
+ if not args.quiet:
354
+ print(f"🎯 Alpha-GPT 自动化因子挖掘")
355
+ print(f"📊 数据:{len(df)} 行 × {len(df.columns)} 列")
356
+ print(f"🧠 LLM:{args.llm}{f' ({args.model})' if args.model else ''}")
357
+ print(f"🔄 {args.iterations} 轮 × {args.pool_size} 候选 = {args.iterations * args.pool_size} 个公式")
358
+ if args.backtest:
359
+ print(f"💹 Trading 回测:启用(top-K={args.top_k_backtest})")
360
+ else:
361
+ print(f"💹 Trading 回测:禁用")
362
+ print()
363
+
364
+ # 3. 配置 + 运行 workflow
365
+ from QuantNodes.research.quant_alpha.workflow import (
366
+ AlphaGptConfig, AlphaGptWorkflow,
367
+ )
368
+
369
+ config = AlphaGptConfig(
370
+ objective=args.objective,
371
+ iterations=args.iterations,
372
+ pool_size=args.pool_size,
373
+ top_k=args.top_k,
374
+ min_ir_threshold=args.min_ir,
375
+ max_mutual_ic_threshold=args.max_mutual_ic,
376
+ forward_returns=forward_returns,
377
+ date_column=args.date_column,
378
+ code_column=args.code_column,
379
+ llm_provider=args.llm,
380
+ llm_model=args.model,
381
+ temperature=args.temperature,
382
+ enable_backtest=args.backtest,
383
+ top_k_backtest=args.top_k_backtest,
384
+ )
385
+
386
+ llm_client = self._build_llm_client(args) if args.llm != "mock" else None
387
+
388
+ workflow = AlphaGptWorkflow(
389
+ config=config,
390
+ data=df,
391
+ llm_client=llm_client,
392
+ )
393
+ result = workflow.run()
394
+
395
+ # 4. 输出结果
396
+ if args.quiet:
397
+ print(json.dumps(
398
+ [f.to_dict() for f in result.final_pool],
399
+ ensure_ascii=False, indent=2,
400
+ ))
401
+ else:
402
+ self._print_human_readable(result, verbose=args.verbose)
403
+
404
+ # 5. 保存到 JSON
405
+ out_path = Path(args.output)
406
+ out_data = {
407
+ "metadata": {
408
+ "objective": config.objective,
409
+ "iterations": result.iterations_completed,
410
+ "pool_size": config.pool_size,
411
+ "data_rows": len(df),
412
+ "llm_provider": config.llm_provider,
413
+ "started_at": None,
414
+ "completed_at": None,
415
+ "elapsed_seconds": result.elapsed_seconds,
416
+ },
417
+ "final_pool": [f.to_dict() for f in result.final_pool],
418
+ "summary": result.summary,
419
+ }
420
+ with open(out_path, "w", encoding="utf-8") as f:
421
+ json.dump(out_data, f, ensure_ascii=False, indent=2, default=str)
422
+
423
+ if not args.quiet:
424
+ print(f"\n💾 结果已保存:{out_path}")
425
+
426
+ return 0
427
+
428
+ def _load_data(
429
+ self,
430
+ path: Optional[str],
431
+ date_column: str,
432
+ code_column: str,
433
+ ) -> Any:
434
+ """加载数据:路径 → Parquet/CSV;None → 合成"""
435
+ import polars as pl
436
+ import numpy as np
437
+
438
+ if path is None:
439
+ np.random.seed(42)
440
+ dates = [f"2024-01-{d:02d}" for d in range(1, 21)]
441
+ rows = []
442
+ for date in dates:
443
+ for code in ["A", "B", "C", "D", "E"]:
444
+ close = float(np.random.randn() * 5 + 100)
445
+ rows.append({
446
+ date_column: date, code_column: code,
447
+ "close": close,
448
+ "open": close + np.random.randn() * 0.5,
449
+ "high": close + abs(np.random.randn()),
450
+ "low": close - abs(np.random.randn()),
451
+ "vol": float(np.random.randint(1000, 5000)),
452
+ })
453
+ return pl.DataFrame(rows).with_columns(pl.col(date_column).str.to_date())
454
+
455
+ p = Path(path)
456
+ if not p.exists():
457
+ raise FileNotFoundError(f"数据文件不存在: {path}")
458
+
459
+ if p.suffix == ".parquet":
460
+ return pl.read_parquet(p)
461
+ if p.suffix == ".csv":
462
+ df = pl.read_csv(p)
463
+ if date_column in df.columns and df[date_column].dtype == pl.Utf8:
464
+ df = df.with_columns(pl.col(date_column).str.to_date())
465
+ return df
466
+ raise ValueError(f"不支持的数据格式: {p.suffix}")
467
+
468
+ def _build_llm_client(self, args: argparse.Namespace) -> Any:
469
+ """根据 --llm 参数构造 LLM client
470
+
471
+ 复用 nanobot Agent 体系。
472
+ 若 nanobot 未安装或 API key 缺失,返回 None(用 mock)。
473
+ """
474
+ try:
475
+ from QuantNodes.agent import NANOBOT_AVAILABLE, NanobotNotInstalled
476
+ if not NANOBOT_AVAILABLE:
477
+ print(f"⚠️ nanobot-ai 未安装,使用 mock LLM", file=sys.stderr)
478
+ return None
479
+ from QuantNodes.ai.llm.gateway import get_llm_gateway
480
+ return get_llm_gateway(workspace=".agent")
481
+ except NanobotNotInstalled:
482
+ print(f"⚠️ nanobot-ai 未安装,使用 mock LLM", file=sys.stderr)
483
+ return None
484
+
485
+ def _print_human_readable(self, result: Any, verbose: bool = False) -> None:
486
+ print()
487
+ print(f"✅ 完成:{result.iterations_completed} 轮, "
488
+ f"{result.total_formulas} 公式, "
489
+ f"{len(result.final_pool)} 个 top-K")
490
+ print(f"⏱️ 耗时:{result.elapsed_seconds:.2f}s")
491
+ print(f"📈 best_ir={result.summary.get('best_ir', 0):.3f}, "
492
+ f"avg_ir={result.summary.get('avg_ir', 0):.3f}")
493
+ print()
494
+
495
+ if not result.final_pool:
496
+ print("⚠️ final pool 为空(可能所有公式都失败了)")
497
+ return
498
+
499
+ print(f"🏆 Top {len(result.final_pool)} 公式:")
500
+ for f in result.final_pool:
501
+ print(f" rank={f.rank:<3d} IR={f.ir:>6.3f} "
502
+ f"formula={f.formula!r}")
503
+ if verbose and f.selection_reason:
504
+ print(f" reason: {f.selection_reason[:100]}")
505
+
506
+
507
+ class NanobotLLMWrapper:
508
+ """把 nanobot Agent 包装成 workflow 期望的 client 接口
509
+
510
+ 现在内部委托 LLMGateway。
511
+ """
512
+
513
+ def __init__(self, args: argparse.Namespace):
514
+ from QuantNodes.ai.llm.gateway import get_llm_gateway
515
+ self._gateway = get_llm_gateway(workspace=".agent")
516
+ self.temperature = args.temperature
517
+ self.model = args.model
518
+
519
+ def complete(self, agent_id: str, prompt: str) -> str:
520
+ """同步调用 LLMGateway.complete()"""
521
+ return self._gateway.complete(agent_id=agent_id, prompt=prompt)
522
+
523
+
524
+ # ==============================================================================
525
+ # 端到端流水线命令
526
+ # ==============================================================================
527
+
528
+
529
+ class AlphaPipelineCommand(Command):
530
+ """quantnodes alpha-pipeline - 端到端因子挖掘流水线(多轮迭代版)
531
+
532
+ 用法:
533
+ quantnodes alpha-pipeline --objective "捕捉 A 股反转效应" --data data.parquet
534
+ quantnodes alpha-pipeline --objective "..." --max-rounds 5 --target-factors 10
535
+ quantnodes alpha-pipeline --objective "..." --no-early-stopping --patience 3
536
+
537
+ 详见 docs/quant_alpha/pipeline_multi_round_design.md
538
+ """
539
+ name = "alpha-pipeline"
540
+ description = "端到端因子挖掘流水线(Alpha-GPT → MCTS → 去重 → Wiki,支持多轮迭代)"
541
+
542
+ def add_arguments(self, subparsers: Any) -> None:
543
+ parser = subparsers.add_parser(
544
+ self.name,
545
+ help=self.description,
546
+ description=(
547
+ "端到端因子挖掘流水线(多轮迭代版):\n"
548
+ "Round 1: Alpha-GPT → MCTS → 去重 → 反馈\n"
549
+ "Round 2: Alpha-GPT(←反馈) → MCTS → 去重 → 反馈\n"
550
+ "...\n"
551
+ "Round N: 最终结果 → Wiki 持久化\n"
552
+ "详见 docs/quant_alpha/pipeline_multi_round_design.md"
553
+ ),
554
+ )
555
+ # 必选
556
+ parser.add_argument(
557
+ "--objective", "-o",
558
+ type=str, required=True,
559
+ help="研究目标(如 '捕捉 A 股反转效应')",
560
+ )
561
+ parser.add_argument(
562
+ "--data", "-d",
563
+ type=str, default=None,
564
+ help="行情数据路径(Parquet/CSV),None=合成测试数据",
565
+ )
566
+ # Wiki
567
+ parser.add_argument(
568
+ "--wiki-path",
569
+ type=str, default="wiki/",
570
+ help="Wiki 因子库路径(默认 wiki/)",
571
+ )
572
+ # 终止条件配置(新增)
573
+ parser.add_argument(
574
+ "--max-rounds",
575
+ type=int, default=5,
576
+ help="最大迭代轮次(默认 5)",
577
+ )
578
+ parser.add_argument(
579
+ "--target-factors",
580
+ type=int, default=10,
581
+ help="目标因子数量(默认 10)",
582
+ )
583
+ parser.add_argument(
584
+ "--min-improvement",
585
+ type=float, default=0.01,
586
+ help="最小 IR 提升阈值(默认 0.01)",
587
+ )
588
+ parser.add_argument(
589
+ "--no-early-stopping",
590
+ action="store_true",
591
+ help="禁用早停机制",
592
+ )
593
+ parser.add_argument(
594
+ "--patience",
595
+ type=int, default=3,
596
+ help="早停耐心值:连续 N 轮无改善则停止(默认 3)",
597
+ )
598
+ parser.add_argument(
599
+ "--timeout",
600
+ type=int, default=3600,
601
+ help="总超时时间(秒,默认 3600)",
602
+ )
603
+ parser.add_argument(
604
+ "--round-timeout",
605
+ type=int, default=600,
606
+ help="单轮超时时间(秒,默认 600)",
607
+ )
608
+ # Alpha-GPT 配置
609
+ parser.add_argument(
610
+ "--alphagpt-iterations",
611
+ type=int, default=3,
612
+ help="Alpha-GPT 迭代轮次(默认 3)",
613
+ )
614
+ parser.add_argument(
615
+ "--alphagpt-pool-size",
616
+ type=int, default=10,
617
+ help="Alpha-GPT 每轮想法数量(默认 10)",
618
+ )
619
+ # MCTS 配置
620
+ parser.add_argument(
621
+ "--mcts-iterations",
622
+ type=int, default=50,
623
+ help="MCTS 迭代次数(默认 50)",
624
+ )
625
+ parser.add_argument(
626
+ "--mcts-max-depth",
627
+ type=int, default=5,
628
+ help="MCTS 最大深度(默认 5)",
629
+ )
630
+ # 去重配置
631
+ parser.add_argument(
632
+ "--max-mutual-ic",
633
+ type=float, default=0.7,
634
+ help="最大 mutual IC 阈值(默认 0.7)",
635
+ )
636
+ # Alpha-GPT 过滤配置
637
+ parser.add_argument(
638
+ "--min-ir-threshold",
639
+ type=float, default=0.1,
640
+ help="最小 IR 阈值(默认 0.1)",
641
+ )
642
+ # 通用配置
643
+ parser.add_argument(
644
+ "--top-k", "-k",
645
+ type=int, default=10,
646
+ help="最终返回的 top-K 公式数量(默认 10)",
647
+ )
648
+ parser.add_argument(
649
+ "--date-column",
650
+ type=str, default="date",
651
+ help="日期列名(默认 date)",
652
+ )
653
+ parser.add_argument(
654
+ "--code-column",
655
+ type=str, default="code",
656
+ help="股票代码列名(默认 code)",
657
+ )
658
+ parser.add_argument(
659
+ "--forward-returns",
660
+ type=str, default="1,5,20",
661
+ help="前瞻期列表(逗号分隔,默认 '1,5,20')",
662
+ )
663
+ # LLM 配置
664
+ parser.add_argument(
665
+ "--llm",
666
+ type=str, default="minimax",
667
+ choices=["minimax", "deepseek", "openai", "qwen", "azure", "mock"],
668
+ help="LLM provider(默认 minimax)",
669
+ )
670
+ parser.add_argument(
671
+ "--model",
672
+ type=str, default=None,
673
+ help="模型名(如 'minimax-M3' / 'gpt-4o')",
674
+ )
675
+ parser.add_argument(
676
+ "--temperature",
677
+ type=float, default=0.7,
678
+ help="采样温度(默认 0.7)",
679
+ )
680
+ parser.add_argument(
681
+ "--temperature-idea-gen",
682
+ type=float, default=0.8,
683
+ help="idea-generator 温度(默认 0.8)",
684
+ )
685
+ parser.add_argument(
686
+ "--temperature-formula",
687
+ type=float, default=0.4,
688
+ help="formula-translator 温度(默认 0.4)",
689
+ )
690
+ parser.add_argument(
691
+ "--temperature-reflector",
692
+ type=float, default=0.6,
693
+ help="reflector 温度(默认 0.6)",
694
+ )
695
+ parser.add_argument(
696
+ "--temperature-critic",
697
+ type=float, default=0.3,
698
+ help="critic 温度(默认 0.3)",
699
+ )
700
+ # 输出
701
+ parser.add_argument(
702
+ "--output", "-O",
703
+ type=str, default="pipeline_result.json",
704
+ help="结果保存路径(默认 pipeline_result.json)",
705
+ )
706
+ parser.add_argument(
707
+ "--output-dir",
708
+ type=str, default="pipeline_output",
709
+ help="详细结果输出目录(默认 pipeline_output)",
710
+ )
711
+ parser.add_argument(
712
+ "--verbose", "-v",
713
+ action="store_true",
714
+ help="详细输出",
715
+ )
716
+ parser.add_argument(
717
+ "--quiet", "-q",
718
+ action="store_true",
719
+ help="安静模式(只输出摘要)",
720
+ )
721
+
722
+ def run(self, args: argparse.Namespace) -> int:
723
+ from QuantNodes.research.quant_alpha.pipeline import (
724
+ AlphaPipeline, PipelineConfig, TerminationConfig,
725
+ )
726
+
727
+ # 1. 解析 forward_returns
728
+ try:
729
+ forward_returns = tuple(
730
+ int(x.strip()) for x in args.forward_returns.split(",") if x.strip()
731
+ )
732
+ except ValueError:
733
+ print(f"❌ --forward-returns 格式错误: {args.forward_returns}", file=sys.stderr)
734
+ return 1
735
+
736
+ # 2. 加载数据
737
+ data = self._load_data(args.data, args.date_column, args.code_column)
738
+ if data is None:
739
+ return 1
740
+
741
+ if not args.quiet:
742
+ print(f"🔬 端到端因子挖掘流水线(多轮迭代版)")
743
+ print(f"📊 数据:{len(data)} 行 × {len(data.columns)} 列")
744
+ print(f"🎯 目标:{args.objective}")
745
+ print(f"🧠 LLM:{args.llm}{f' ({args.model})' if args.model else ''}")
746
+ print(f"🔄 最大轮次: {args.max_rounds}, 目标因子: {args.target_factors}")
747
+ print(f"🔄 Alpha-GPT: {args.alphagpt_iterations} 轮 × {args.alphagpt_pool_size} 候选")
748
+ print(f"🔄 MCTS: {args.mcts_iterations} 次迭代")
749
+ print(f"⏹️ 早停: {'禁用' if args.no_early_stopping else f'启用 (patience={args.patience})'}")
750
+ print(f"📚 Wiki:{args.wiki_path}")
751
+ print()
752
+
753
+ # 3. 配置 + 运行流水线
754
+ termination = TerminationConfig(
755
+ max_rounds=args.max_rounds,
756
+ target_factors=args.target_factors,
757
+ min_improvement=args.min_improvement,
758
+ early_stopping=not args.no_early_stopping,
759
+ patience=args.patience,
760
+ timeout_seconds=args.timeout,
761
+ round_timeout_seconds=args.round_timeout,
762
+ )
763
+
764
+ config = PipelineConfig(
765
+ objective=args.objective,
766
+ wiki_path=args.wiki_path,
767
+ termination=termination,
768
+ alphagpt_iterations=args.alphagpt_iterations,
769
+ alphagpt_pool_size=args.alphagpt_pool_size,
770
+ mcts_iterations=args.mcts_iterations,
771
+ mcts_max_depth=args.mcts_max_depth,
772
+ max_mutual_ic=args.max_mutual_ic,
773
+ min_ir_threshold=args.min_ir_threshold,
774
+ top_k=args.top_k,
775
+ date_column=args.date_column,
776
+ code_column=args.code_column,
777
+ forward_returns=forward_returns,
778
+ llm_provider=args.llm,
779
+ llm_model=args.model,
780
+ temperature=args.temperature,
781
+ temperature_idea_gen=args.temperature_idea_gen,
782
+ temperature_formula=args.temperature_formula,
783
+ temperature_reflector=args.temperature_reflector,
784
+ temperature_critic=args.temperature_critic,
785
+ output_dir=args.output_dir,
786
+ )
787
+
788
+ pipeline = AlphaPipeline(config)
789
+ result = pipeline.run(data)
790
+
791
+ # 4. 保存结果
792
+ self._save_result(result, args.output, args.verbose)
793
+
794
+ # 5. 打印结果
795
+ if not args.quiet:
796
+ self._print_human_readable(result, args.verbose)
797
+
798
+ return 0
799
+
800
+ def _load_data(
801
+ self, data_path: Optional[str], date_column: str, code_column: str
802
+ ) -> Optional[Any]:
803
+ """加载数据"""
804
+ import polars as pl
805
+
806
+ if data_path is None:
807
+ print("⚠️ 未指定数据路径,使用合成测试数据", file=sys.stderr)
808
+ return self._generate_synthetic_data()
809
+
810
+ path = Path(data_path)
811
+ if not path.exists():
812
+ print(f"❌ 数据文件不存在: {data_path}", file=sys.stderr)
813
+ return None
814
+
815
+ try:
816
+ if path.suffix == ".parquet":
817
+ df = pl.read_parquet(path)
818
+ elif path.suffix == ".csv":
819
+ df = pl.read_csv(path)
820
+ elif path.suffix == ".json":
821
+ df = pl.read_json(path)
822
+ else:
823
+ print(f"❌ 不支持的文件格式: {path.suffix}", file=sys.stderr)
824
+ return None
825
+
826
+ # 确保日期列存在
827
+ if date_column not in df.columns:
828
+ print(f"❌ 缺少日期列: {date_column}", file=sys.stderr)
829
+ return None
830
+
831
+ # 确保代码列存在
832
+ if code_column not in df.columns:
833
+ print(f"❌ 缺少代码列: {code_column}", file=sys.stderr)
834
+ return None
835
+
836
+ return df
837
+
838
+ except Exception as e:
839
+ print(f"❌ 数据加载失败: {e}", file=sys.stderr)
840
+ return None
841
+
842
+ def _generate_synthetic_data(self) -> Any:
843
+ """生成合成测试数据"""
844
+ import numpy as np
845
+ import polars as pl
846
+
847
+ np.random.seed(42)
848
+ dates = [f"2024-01-{d:02d}" for d in range(1, 21)]
849
+ rows = []
850
+ for date in dates:
851
+ for code in ["A", "B", "C", "D", "E"]:
852
+ close = float(np.random.randn() * 5 + 100)
853
+ rows.append({
854
+ "date": date,
855
+ "code": code,
856
+ "close": close,
857
+ "open": close,
858
+ "high": close + 1,
859
+ "low": close - 1,
860
+ "vol": 1000.0,
861
+ })
862
+ return pl.DataFrame(rows).with_columns(pl.col("date").str.to_date())
863
+
864
+ def _save_result(self, result: Any, output_path: str, verbose: bool) -> None:
865
+ """保存结果"""
866
+ try:
867
+ output = {
868
+ "summary": result.summary,
869
+ "rounds": [
870
+ {
871
+ "round_num": r.round_num,
872
+ "elapsed_seconds": r.elapsed_seconds,
873
+ "feedback": r.feedback.to_dict() if r.feedback else None,
874
+ "final_factors": len(r.final_pool),
875
+ }
876
+ for r in result.rounds
877
+ ],
878
+ "final_pool": [
879
+ {
880
+ "formula_id": m.formula_id,
881
+ "ic_mean": m.ic_mean,
882
+ "ic_std": m.ic_std,
883
+ "ir": m.ir,
884
+ "rank_ic_mean": m.rank_ic_mean,
885
+ "overall_score": m.overall_score,
886
+ }
887
+ for m in result.final_pool
888
+ ],
889
+ "wiki_pages": result.wiki_pages,
890
+ "elapsed_seconds": result.elapsed_seconds,
891
+ }
892
+
893
+ if verbose:
894
+ output["alphagpt_summary"] = (
895
+ result.rounds[-1].alphagpt_result.summary
896
+ if result.rounds and result.rounds[-1].alphagpt_result
897
+ else None
898
+ )
899
+ output["mcts_stats"] = (
900
+ {
901
+ "formula_count": result.rounds[-1].mcts_result.formula_count,
902
+ "valid_count": result.rounds[-1].mcts_result.valid_count,
903
+ "rejected_count": result.rounds[-1].mcts_result.rejected_count,
904
+ }
905
+ if result.rounds and result.rounds[-1].mcts_result
906
+ else None
907
+ )
908
+
909
+ Path(output_path).write_text(
910
+ json.dumps(output, ensure_ascii=False, indent=2)
911
+ )
912
+ print(f"💾 结果已保存到: {output_path}")
913
+
914
+ except Exception as e:
915
+ print(f"⚠️ 结果保存失败: {e}", file=sys.stderr)
916
+
917
+ def _print_human_readable(self, result: Any, verbose: bool = False) -> None:
918
+ print()
919
+ print(f"✅ 流水线完成(多轮迭代)")
920
+ print(f"⏱️ 耗时:{result.elapsed_seconds:.1f}s")
921
+ print(f"📊 摘要:")
922
+ print(f" - 总轮次: {result.summary.get('total_rounds', 0)}")
923
+ print(f" - 最终因子: {result.summary.get('final_factors', 0)}")
924
+ print(f" - Wiki 页面: {result.summary.get('wiki_pages', 0)}")
925
+ print(f" - best_ir: {result.summary.get('best_ir', 0):.3f}")
926
+ print(f" - avg_ir: {result.summary.get('avg_ir', 0):.3f}")
927
+ print()
928
+
929
+ # 显示每轮统计
930
+ if result.rounds:
931
+ print(f"🔄 各轮统计:")
932
+ for r in result.rounds:
933
+ feedback = r.feedback
934
+ if feedback:
935
+ print(f" Round {r.round_num}: "
936
+ f"IR={feedback.best_ir:.3f}, "
937
+ f"有效因子={feedback.valid_count}, "
938
+ f"耗时={r.elapsed_seconds:.1f}s")
939
+ print()
940
+
941
+ if not result.final_pool:
942
+ print("⚠️ final pool 为空(可能所有公式都失败了)")
943
+ return
944
+
945
+ print(f"🏆 Top {len(result.final_pool)} 因子:")
946
+ for m in result.final_pool:
947
+ print(f" IR={m.ir:>6.3f} IC={m.ic_mean:>6.4f} formula_id={m.formula_id}")
948
+ if verbose:
949
+ print(f" rank_ic={m.rank_ic_mean:.4f} "
950
+ f"stability={m.stability_score:.4f} "
951
+ f"overall={m.overall_score:.4f}")