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,305 @@
1
+ # coding: utf-8
2
+ """Pipeline Runner / 管线编排器
3
+
4
+ 混合模式数据传递:
5
+ - Phase 1 (严格串联): LoadData >> SampleFilter >> TradabilityFilter >>
6
+ AdjustDate >> Preprocess >> Neutralize
7
+ - Phase 2 (Context 共享): ICAnalyzer / GroupAnalyzer / FactorScore / RiskCorrelation
8
+ - Phase 3 (依赖分析): LongShort
9
+ - Phase 4 (输出): FactorTestReport
10
+
11
+ FactorFeedback 集成 (Week 1.5, 可选):
12
+ - feedback.enabled=False: 现有行为完全不变 (向后兼容)
13
+ - feedback.enabled=True: 5 个分析节点返回值自动包装为 FactorFeedback,
14
+ 聚合到 ctx['Feedback'], 可选持久化到 feedback.output_dir
15
+
16
+ Phase R2 重构 (2026-06-19):
17
+ - 12 阶段从手写 137 行缩减为 PIPELINE_SPEC 数据驱动 (~30 行 run loop)
18
+ - Feedback 包装拆到 feedback_wrapper.py
19
+ - Evolution 适配拆到 evolution_adapter.py
20
+ - 指标提取拆到 utils/metrics_extractor.py
21
+ - 公共 API (`PipelineRunner.run()` / `.run_evolution()`) 完全兼容
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import logging
27
+ import uuid
28
+ from typing import TYPE_CHECKING, List, Optional
29
+
30
+ import pandas as pd
31
+ import yaml
32
+
33
+ from QuantNodes.core.feedback import FactorFeedback
34
+ from QuantNodes.research.factor_test.config import SingleFactorTestConfig
35
+ from QuantNodes.research.factor_test.evolution_adapter import (
36
+ build_evolution_loop,
37
+ build_quality_gate,
38
+ build_trajectory_pool,
39
+ evaluate_candidate,
40
+ run_evolution as _run_evolution,
41
+ run_one_factor,
42
+ )
43
+ from QuantNodes.research.factor_test.feedback_wrapper import (
44
+ ANALYSIS_NODES as _ANALYSIS_NODES, # noqa: F401 re-export 兼容
45
+ build_feedback,
46
+ maybe_build_judge,
47
+ maybe_persist_feedback,
48
+ )
49
+ from QuantNodes.research.factor_test.pipeline_spec import PIPELINE_SPEC, PhaseSpec
50
+ from QuantNodes.research.factor_test.utils.metrics_extractor import (
51
+ extract_metrics_from_ctx as _extract_metrics_from_ctx, # noqa: F401 re-export
52
+ )
53
+
54
+ if TYPE_CHECKING:
55
+ from QuantNodes.core.evolution import EvolutionResult, FactorCandidate
56
+ from QuantNodes.core.feedback import LLMJudge
57
+ from QuantNodes.core.quality_gate import QualityGateNode
58
+ from QuantNodes.core.trajectory import TrajectoryPool
59
+
60
+
61
+ logger = logging.getLogger(__name__)
62
+
63
+
64
+ class PipelineRunner:
65
+ """单因子回测管线编排器
66
+
67
+ 用法:
68
+ config = SingleFactorTestConfig(...)
69
+ runner = PipelineRunner(config)
70
+ result = runner.run()
71
+
72
+ # 或从 YAML:
73
+ runner = PipelineRunner.from_yaml("config.yaml")
74
+ result = runner.run()
75
+
76
+ FactorFeedback 集成:
77
+ 当 config.feedback.enabled=True 时, 5 个分析节点返回值会自动包装为
78
+ FactorFeedback, 聚合到 ctx['Feedback'] = {node_name: FactorFeedback}。
79
+ 若 config.feedback.output_dir 不为 None, 还会持久化到该目录。
80
+ """
81
+
82
+ def __init__(
83
+ self,
84
+ config: SingleFactorTestConfig,
85
+ specs: Optional[List["PhaseSpec"]] = None,
86
+ ):
87
+ """
88
+ Args:
89
+ config: 单因子回测配置.
90
+ specs: PR-QN-2 (2026-06-21) 新增. 自定义 phase 列表 (**追加**到
91
+ 标准 12 阶段之后). 默认 None = 使用 ``list(PIPELINE_SPEC)``.
92
+ 若要**完全替换** 12 阶段, 用 ``list(PIPELINE_SPEC) + specs``
93
+ 显式拼接.
94
+
95
+ Note:
96
+ 不传 specs 时, 行为与 PR-QN-2 之前**完全一致**. 4608+ 现有
97
+ tests 无需任何修改.
98
+ """
99
+ self.config = config
100
+ self._context: dict = {}
101
+ # PR-QN-2: 实例级 _specs (默认 = 标准 12 阶段; 传 specs 时**追加**)
102
+ if specs:
103
+ self._specs: list = list(PIPELINE_SPEC) + list(specs)
104
+ else:
105
+ self._specs = list(PIPELINE_SPEC)
106
+
107
+ @classmethod
108
+ def from_yaml(cls, yaml_path: str) -> "PipelineRunner":
109
+ """从 YAML 配置文件创建 Runner"""
110
+ with open(yaml_path, "r", encoding="utf-8") as f:
111
+ raw = yaml.safe_load(f)
112
+ return cls(SingleFactorTestConfig(**raw))
113
+
114
+ @classmethod
115
+ def from_dict(
116
+ cls,
117
+ data: dict,
118
+ extra_phases: Optional[List["PhaseSpec"]] = None,
119
+ ) -> "PipelineRunner":
120
+ """从 dict 创建 Runner
121
+
122
+ Args:
123
+ data: 配置 dict.
124
+ extra_phases: PR-QN-2 (2026-06-21) 新增. 追加的自定义 phase 列表
125
+ (在标准 12 阶段之后执行). 默认 None = 与 PR 之前完全一致.
126
+ """
127
+ return cls(SingleFactorTestConfig(**data), specs=extra_phases)
128
+
129
+ # ============================================================
130
+ # Phase R2: 声明式 12 节点 run loop
131
+ # ============================================================
132
+
133
+ def run(self) -> dict:
134
+ """执行完整单因子回测管线
135
+
136
+ Returns:
137
+ dict: 完整结果, 各节点输出按 ``ctx[node_name]`` 索引
138
+ 当 ``config.feedback.enabled=True`` 时, 还包含 'Feedback' 键
139
+ """
140
+ cfg = self.config
141
+ ctx, pre_seeded = self._seed_ctx()
142
+
143
+ feedback_enabled = cfg.feedback.enabled
144
+ factor_id = str(uuid.uuid4())
145
+ factor_name = cfg.factor.name
146
+ judge = maybe_build_judge(cfg) if feedback_enabled else None
147
+
148
+ logger.info("=" * 60)
149
+ logger.info("单因子回测: %s", cfg.factor.name)
150
+ logger.info(
151
+ "时间范围: %s ~ %s",
152
+ cfg.preprocess.adj_date_beg,
153
+ cfg.preprocess.adj_date_end,
154
+ )
155
+ if feedback_enabled:
156
+ logger.info("FactorFeedback: ENABLED (factor_id=%s...)", factor_id[:8])
157
+ logger.info("=" * 60)
158
+
159
+ for spec in self._specs:
160
+ self._run_phase(spec, ctx, pre_seeded)
161
+
162
+ # FactorFeedback 自动包装 (可选)
163
+ if feedback_enabled:
164
+ ctx["Feedback"] = build_feedback(ctx, factor_id, factor_name, cfg, judge)
165
+ maybe_persist_feedback(ctx["Feedback"], cfg)
166
+ n_passed = sum(1 for fb in ctx["Feedback"].values() if fb.decision)
167
+ logger.info(
168
+ "\n[Feedback] 包装完成: %d 节点, %d 通过",
169
+ len(ctx["Feedback"]),
170
+ n_passed,
171
+ )
172
+
173
+ logger.info("\n" + "=" * 60)
174
+ logger.info("单因子回测完成!")
175
+ logger.info("=" * 60)
176
+ return ctx
177
+
178
+ def _seed_ctx(self) -> tuple[dict, bool]:
179
+ """初始化 ctx; 若已注入 LoadData, 复用之, 否则空 dict.
180
+
181
+ Returns:
182
+ (ctx, pre_seeded) — pre_seeded=True 表示 LoadData 已外部注入
183
+ """
184
+ if "LoadData" in self._context:
185
+ ctx = dict(self._context)
186
+ shape = ctx["LoadData"].get("factor", pd.DataFrame()).shape
187
+ logger.info(" [LoadData 跳过] 使用已注入数据 (factor shape: %s)", shape)
188
+ return ctx, True
189
+ return {}, False
190
+
191
+ def _run_phase(self, spec: PhaseSpec, ctx: dict, pre_seeded: bool) -> None:
192
+ """执行单个阶段 (含 skip / log)."""
193
+ if spec.skip_if_in_ctx and spec.name in ctx:
194
+ return
195
+ logger.info("\n[Phase %d] %s...", spec.phase_no, spec.title)
196
+ node = spec.node_cls(config=spec.build_cfg(self.config))
197
+ ctx[spec.name] = node.execute(context=ctx)
198
+ if spec.log_summary is not None:
199
+ line = spec.log_summary(self.config, ctx[spec.name])
200
+ if line:
201
+ logger.info(line)
202
+
203
+ # ============================================================
204
+ # 兼容旧 API: 私有方法 facade re-export
205
+ # ============================================================
206
+
207
+ def _build_feedback(
208
+ self,
209
+ ctx: dict,
210
+ factor_id: str,
211
+ factor_name: str,
212
+ judge: Optional["LLMJudge"],
213
+ ) -> dict:
214
+ """向后兼容: 委托给 ``feedback_wrapper.build_feedback``."""
215
+ return build_feedback(ctx, factor_id, factor_name, self.config, judge)
216
+
217
+ def _maybe_persist_feedback(
218
+ self,
219
+ feedbacks: dict,
220
+ cfg: SingleFactorTestConfig,
221
+ ) -> None:
222
+ """向后兼容: 委托给 ``feedback_wrapper.maybe_persist_feedback``."""
223
+ maybe_persist_feedback(feedbacks, cfg)
224
+
225
+ @staticmethod
226
+ def _maybe_build_judge(cfg: SingleFactorTestConfig) -> Optional["LLMJudge"]:
227
+ """向后兼容: 委托给 ``feedback_wrapper.maybe_build_judge``."""
228
+ return maybe_build_judge(cfg)
229
+
230
+ # ============================================================
231
+ # Week 4: 演化集成 (向后兼容 facade, 实现在 evolution_adapter.py)
232
+ # ============================================================
233
+
234
+ def _build_quality_gate(self) -> Optional["QualityGateNode"]:
235
+ return build_quality_gate(self)
236
+
237
+ def _build_trajectory_pool(self) -> Optional["TrajectoryPool"]:
238
+ return build_trajectory_pool(self)
239
+
240
+ def _build_evolution_loop(
241
+ self,
242
+ pool: "TrajectoryPool",
243
+ quality_gate: Optional["QualityGateNode"],
244
+ workers: int = 1,
245
+ ):
246
+ return build_evolution_loop(self, pool, quality_gate, workers=workers)
247
+
248
+ def _evaluate_candidate(
249
+ self,
250
+ candidate: "FactorCandidate",
251
+ ) -> tuple[bool, dict, "FactorFeedback"]:
252
+ return evaluate_candidate(self, candidate)
253
+
254
+ def _run_one_factor(self, candidate: "FactorCandidate") -> dict:
255
+ return run_one_factor(self, candidate)
256
+
257
+ def run_evolution(
258
+ self,
259
+ initial_directions: list[str] | None = None,
260
+ initial_candidates: list["FactorCandidate"] | None = None,
261
+ workers: int = 1,
262
+ ) -> "EvolutionResult":
263
+ """多轮演化主入口 (委托给 ``evolution_adapter.run_evolution``)."""
264
+ return _run_evolution(
265
+ self,
266
+ initial_directions=initial_directions,
267
+ initial_candidates=initial_candidates,
268
+ workers=workers,
269
+ )
270
+
271
+ @property
272
+ def context(self) -> dict:
273
+ """获取当前上下文"""
274
+ return self._context
275
+
276
+
277
+ # ============================================================
278
+ # 便捷函数
279
+ # ============================================================
280
+
281
+
282
+ def run_single_factor_test(config: dict) -> dict:
283
+ """便捷函数: 从 dict 配置运行单因子回测
284
+
285
+ Args:
286
+ config: 配置字典
287
+
288
+ Returns:
289
+ dict: 完整结果
290
+ """
291
+ runner = PipelineRunner.from_dict(config)
292
+ return runner.run()
293
+
294
+
295
+ def run_single_factor_test_yaml(yaml_path: str) -> dict:
296
+ """便捷函数: 从 YAML 配置运行单因子回测
297
+
298
+ Args:
299
+ yaml_path: YAML 配置文件路径
300
+
301
+ Returns:
302
+ dict: 完整结果
303
+ """
304
+ runner = PipelineRunner.from_yaml(yaml_path)
305
+ return runner.run()
@@ -0,0 +1,216 @@
1
+ # coding: utf-8
2
+ """声明式 12 节点 Pipeline / Pipeline Spec.
3
+
4
+ 把 ``pipeline_runner.run()`` 中 137 行手写阶段拆为 ``PIPELINE_SPEC`` 数据驱动:
5
+
6
+ - ``PhaseSpec`` 描述每阶段的 ``(name, node_cls, build_cfg, log_summary)``
7
+ - 增 / 删 / 调序节点 = 改 ``PIPELINE_SPEC`` 列表 (单点)
8
+ - ``run()`` 主循环 ~10 行
9
+
10
+ Phase R2 (2026-06-19): 从 pipeline_runner.py 抽出, 单一职责.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from dataclasses import dataclass
16
+ from typing import Any, Callable, Optional
17
+
18
+ from QuantNodes.research.factor_test.config import SingleFactorTestConfig
19
+ from QuantNodes.research.factor_test.nodes.adjust_date_node import AdjustDateNode
20
+ from QuantNodes.research.factor_test.nodes.factor_neutralize_node import FactorNeutralizeNode
21
+ from QuantNodes.research.factor_test.nodes.factor_preprocess_node import FactorPreprocessNode
22
+ from QuantNodes.research.factor_test.nodes.factor_score_node import FactorScoreNode
23
+ from QuantNodes.research.factor_test.nodes.factor_test_report_node import FactorTestReportNode
24
+ from QuantNodes.research.factor_test.nodes.group_analyzer_node import GroupAnalyzerNode
25
+ from QuantNodes.research.factor_test.nodes.ic_analyzer_node import ICAnalyzerNode
26
+ from QuantNodes.research.factor_test.nodes.load_data_node import LoadDataNode
27
+ from QuantNodes.research.factor_test.nodes.long_short_node import LongShortNode
28
+ from QuantNodes.research.factor_test.nodes.risk_correlation_node import RiskCorrelationNode
29
+ from QuantNodes.research.factor_test.nodes.sample_pool_filter_node import SamplePoolFilterNode
30
+ from QuantNodes.research.factor_test.nodes.tradability_filter_node import TradabilityFilterNode
31
+
32
+
33
+ @dataclass(frozen=True)
34
+ class PhaseSpec:
35
+ """单个 Pipeline 阶段描述.
36
+
37
+ Attributes:
38
+ name: 节点名 (用作 ``ctx[name]`` 键)
39
+ phase_no: 1-12 阶段编号 (仅显示)
40
+ title: 阶段中文标题 (打印 banner 用)
41
+ node_cls: 节点类 (``BaseNode`` 子类)
42
+ build_cfg: ``cfg -> dict`` 构造节点 config
43
+ skip_if_in_ctx: True 时若 ``name in ctx`` 则跳过 (LoadData 注入场景)
44
+ log_summary: ``(cfg, output) -> str`` 阶段成功后的额外日志, 可为 None
45
+ """
46
+ name: str
47
+ phase_no: int
48
+ title: str
49
+ node_cls: type
50
+ build_cfg: Callable[[SingleFactorTestConfig], dict]
51
+ skip_if_in_ctx: bool = False
52
+ log_summary: Optional[Callable[[SingleFactorTestConfig, Any], str]] = None
53
+
54
+
55
+ # ── 12 节点 config builder ────────────────────────────────────
56
+
57
+ def _cfg_load(cfg: SingleFactorTestConfig) -> dict:
58
+ return {
59
+ "factor": cfg.factor.model_dump(),
60
+ "data_path": cfg.data_path,
61
+ "load_keys": cfg.load_keys,
62
+ }
63
+
64
+
65
+ def _cfg_sample(cfg: SingleFactorTestConfig) -> dict:
66
+ return {
67
+ "sample_index": cfg.preprocess.sample_index,
68
+ "sample_industry": cfg.preprocess.sample_industry,
69
+ "sample_index_customdir": cfg.preprocess.sample_index_customdir,
70
+ }
71
+
72
+
73
+ def _cfg_trad(cfg: SingleFactorTestConfig) -> dict:
74
+ return {"tradable": cfg.preprocess.tradable.model_dump()}
75
+
76
+
77
+ def _cfg_adj(cfg: SingleFactorTestConfig) -> dict:
78
+ return {
79
+ "adj_date_beg": cfg.preprocess.adj_date_beg,
80
+ "adj_date_end": cfg.preprocess.adj_date_end,
81
+ "adj_mode": cfg.preprocess.adj_mode,
82
+ }
83
+
84
+
85
+ def _cfg_preprocess(cfg: SingleFactorTestConfig) -> dict:
86
+ return {
87
+ "missing": cfg.preprocess.missing,
88
+ "extreme": cfg.preprocess.extreme,
89
+ "norm": cfg.preprocess.norm,
90
+ }
91
+
92
+
93
+ def _cfg_neutralize(cfg: SingleFactorTestConfig) -> dict:
94
+ return {
95
+ "industry_neutral": cfg.preprocess.industry_neutral,
96
+ "risk_neutral": cfg.preprocess.risk_neutral,
97
+ "risk_factors": cfg.preprocess.risk_factors,
98
+ }
99
+
100
+
101
+ def _cfg_ic(cfg: SingleFactorTestConfig) -> dict:
102
+ return {"min_group_size": cfg.analysis.ic.min_group_size}
103
+
104
+
105
+ def _cfg_group(cfg: SingleFactorTestConfig) -> dict:
106
+ return {
107
+ "groups": cfg.analysis.group.groups,
108
+ "factor_direction": cfg.analysis.group.factor_direction,
109
+ "floor_mode": cfg.analysis.group.floor_mode,
110
+ "hedge": cfg.analysis.group.hedge,
111
+ "hedge_path": cfg.analysis.group.hedge_path,
112
+ }
113
+
114
+
115
+ def _cfg_longshort(cfg: SingleFactorTestConfig) -> dict:
116
+ return {"factor_direction": cfg.analysis.longshort.factor_direction}
117
+
118
+
119
+ def _cfg_score(cfg: SingleFactorTestConfig) -> dict:
120
+ return {"enabled": cfg.analysis.score.enabled}
121
+
122
+
123
+ def _cfg_risk_corr(cfg: SingleFactorTestConfig) -> dict:
124
+ return {"factors": cfg.analysis.risk_corr.factors}
125
+
126
+
127
+ def _cfg_report(cfg: SingleFactorTestConfig) -> dict:
128
+ return {"dir": cfg.output.dir, "format": cfg.output.format}
129
+
130
+
131
+ # ── log_summary 辅助 ──────────────────────────────────────────
132
+
133
+ def _log_adj(cfg, out) -> str:
134
+ return f" 调仓日数: {len(out)}"
135
+
136
+
137
+ def _log_preprocess(cfg, out) -> str:
138
+ shape = getattr(out, "shape", "?")
139
+ return f" 预处理后因子形状: {shape}"
140
+
141
+
142
+ def _log_ic(cfg, out) -> str:
143
+ if not isinstance(out, dict):
144
+ return ""
145
+ ic_result = out.get("ic_result")
146
+ if not isinstance(ic_result, dict):
147
+ return ""
148
+ parts = []
149
+ if ic_result.get("IC均值") is not None:
150
+ parts.append(f"IC均值: {ic_result['IC均值']:.4f}")
151
+ if ic_result.get("ICIR") is not None:
152
+ parts.append(f"ICIR: {ic_result['ICIR']:.4f}")
153
+ return " " + " | ".join(parts) if parts else ""
154
+
155
+
156
+ def _log_group(cfg, out) -> str:
157
+ return f" 分组数: {cfg.analysis.group.groups}"
158
+
159
+
160
+ # ── 12 节点 Pipeline 声明 ─────────────────────────────────────
161
+
162
+ PIPELINE_SPEC: list[PhaseSpec] = [
163
+ PhaseSpec(
164
+ name="LoadData", phase_no=1, title="数据加载",
165
+ node_cls=LoadDataNode, build_cfg=_cfg_load,
166
+ skip_if_in_ctx=True,
167
+ ),
168
+ PhaseSpec(
169
+ name="SamplePoolFilter", phase_no=2, title="样本池筛选",
170
+ node_cls=SamplePoolFilterNode, build_cfg=_cfg_sample,
171
+ ),
172
+ PhaseSpec(
173
+ name="TradabilityFilter", phase_no=3, title="可交易性筛选",
174
+ node_cls=TradabilityFilterNode, build_cfg=_cfg_trad,
175
+ ),
176
+ PhaseSpec(
177
+ name="AdjustDate", phase_no=4, title="调仓日生成",
178
+ node_cls=AdjustDateNode, build_cfg=_cfg_adj,
179
+ log_summary=_log_adj,
180
+ ),
181
+ PhaseSpec(
182
+ name="FactorPreprocess", phase_no=5, title="因子预处理",
183
+ node_cls=FactorPreprocessNode, build_cfg=_cfg_preprocess,
184
+ log_summary=_log_preprocess,
185
+ ),
186
+ PhaseSpec(
187
+ name="FactorNeutralize", phase_no=6, title="因子中性化",
188
+ node_cls=FactorNeutralizeNode, build_cfg=_cfg_neutralize,
189
+ ),
190
+ PhaseSpec(
191
+ name="ICAnalyzer", phase_no=7, title="IC 分析",
192
+ node_cls=ICAnalyzerNode, build_cfg=_cfg_ic,
193
+ log_summary=_log_ic,
194
+ ),
195
+ PhaseSpec(
196
+ name="GroupAnalyzer", phase_no=8, title="分组分析",
197
+ node_cls=GroupAnalyzerNode, build_cfg=_cfg_group,
198
+ log_summary=_log_group,
199
+ ),
200
+ PhaseSpec(
201
+ name="LongShort", phase_no=9, title="多空组合",
202
+ node_cls=LongShortNode, build_cfg=_cfg_longshort,
203
+ ),
204
+ PhaseSpec(
205
+ name="FactorScore", phase_no=10, title="市值行业分层打分",
206
+ node_cls=FactorScoreNode, build_cfg=_cfg_score,
207
+ ),
208
+ PhaseSpec(
209
+ name="RiskCorrelation", phase_no=11, title="风险因子相关性",
210
+ node_cls=RiskCorrelationNode, build_cfg=_cfg_risk_corr,
211
+ ),
212
+ PhaseSpec(
213
+ name="FactorTestReport", phase_no=12, title="生成报告",
214
+ node_cls=FactorTestReportNode, build_cfg=_cfg_report,
215
+ ),
216
+ ]
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ from .data_loader import DataLoader
3
+ from .date_utils import (
4
+ valid_date, datenum_to_datetime, datetime_to_datenum,
5
+ chg_idx_to_datestr, resample_trade_date, get_adjust_date, offset_date,
6
+ )
7
+ from .performance_metrics import calc_max_drawdown, evaluation, cal_net_simple
8
+ from .constants import INDEX_MAPPING, INDEX_CP_MAPPING, INDUSTRY_MAPPING, ANNUAL_DAYS
9
+
10
+ __all__ = [
11
+ "DataLoader",
12
+ "valid_date",
13
+ "datenum_to_datetime",
14
+ "datetime_to_datenum",
15
+ "chg_idx_to_datestr",
16
+ "resample_trade_date",
17
+ "get_adjust_date",
18
+ "offset_date",
19
+ "calc_max_drawdown",
20
+ "evaluation",
21
+ "cal_net_simple",
22
+ "INDEX_MAPPING",
23
+ "INDEX_CP_MAPPING",
24
+ "INDUSTRY_MAPPING",
25
+ "ANNUAL_DAYS",
26
+ ]
@@ -0,0 +1,86 @@
1
+ # coding: utf-8
2
+ """常量定义 / Constants
3
+
4
+ H13-H16: 行业/指数/天数支持外部 JSON 覆盖 (默认内置)。
5
+ 用法:
6
+ from QuantNodes.research.factor_test.utils.constants import load_overrides
7
+ overrides = load_overrides(Path("./my_industry_map.json"))
8
+ ind_map = overrides.get("INDUSTRY_MAP", DEFAULT_INDUSTRY_MAP)
9
+ """
10
+
11
+ from pathlib import Path
12
+ from typing import Any, Optional
13
+
14
+ import json
15
+
16
+ # 指数映射 (默认; SZ50 id_50 路由不在 ifind_database 中, 仅作占位)
17
+ INDEX_MAPPING = {
18
+ 'HS300': ('stk_daily.h5', 'id_300'),
19
+ 'ZZ500': ('stk_daily.h5', 'id_500'),
20
+ }
21
+
22
+ # 指数收盘价映射
23
+ INDEX_CP_MAPPING = {
24
+ 'HS300': '000300.SH',
25
+ 'ZZ500': '000905.SH',
26
+ }
27
+
28
+ # 中信行业映射
29
+ INDUSTRY_MAPPING = {
30
+ 'id_citic1A': 'ind_name_CITIC_1A',
31
+ 'id_citic1': 'ind_name_CITIC_1',
32
+ }
33
+
34
+ # 年化天数 (A 股 250; 美股/港股 252; 24h 市场 365)
35
+ ANNUAL_DAYS = 250
36
+
37
+
38
+ def load_overrides(path: Optional[Path | str] = None) -> dict[str, Any]:
39
+ """加载外部 JSON 覆盖 (H13-H16)。
40
+
41
+ 支持覆盖字段:
42
+ - INDUSTRY_MAP: dict[行业 key, 显示名 key]
43
+ - INDEX_MAPPING: dict[指数名, (h5, key)]
44
+ - INDEX_CP_MAPPING: dict[指数名, 代码]
45
+ - ANNUAL_DAYS: int
46
+
47
+ Args:
48
+ path: JSON 路径, None 或文件不存在 → 返回空 dict (用全部默认)
49
+
50
+ Returns:
51
+ dict: 覆盖项 (子集, 只含实际有 override 的字段)
52
+ """
53
+ if path is None:
54
+ return {}
55
+ path = Path(path)
56
+ if not path.exists():
57
+ return {}
58
+ try:
59
+ with path.open("r", encoding="utf-8") as f:
60
+ return json.load(f)
61
+ except Exception:
62
+ return {}
63
+
64
+
65
+ def resolve_industry_map(overrides: dict[str, Any] | None = None) -> dict[str, str]:
66
+ """解析 INDUSTRY_MAP (含 override 合并)。"""
67
+ base = dict(INDUSTRY_MAPPING)
68
+ if overrides and "INDUSTRY_MAP" in overrides:
69
+ base.update(overrides["INDUSTRY_MAP"])
70
+ return base
71
+
72
+
73
+ def resolve_index_mapping(overrides: dict[str, Any] | None = None) -> dict[str, tuple]:
74
+ """解析 INDEX_MAPPING (含 override 合并)。"""
75
+ base = {k: tuple(v) for k, v in INDEX_MAPPING.items()}
76
+ if overrides and "INDEX_MAPPING" in overrides:
77
+ for k, v in overrides["INDEX_MAPPING"].items():
78
+ base[k] = tuple(v)
79
+ return base
80
+
81
+
82
+ def resolve_annual_days(overrides: dict[str, Any] | None = None) -> int:
83
+ """解析 ANNUAL_DAYS (含 override)。"""
84
+ if overrides and "ANNUAL_DAYS" in overrides:
85
+ return int(overrides["ANNUAL_DAYS"])
86
+ return ANNUAL_DAYS