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,358 @@
1
+ # coding=utf-8
2
+ """
3
+ 辅助函数模块
4
+
5
+ 此模块包含所有算子实现所需的辅助函数,独立于 factor_functions 包,
6
+ 以避免循环导入问题。
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import functools
12
+ import inspect
13
+ from typing import Any, Callable, Dict, Optional, Union
14
+
15
+ import numpy as np
16
+ import polars as pl
17
+ from polars import Expr
18
+
19
+
20
+ # ==============================================================================
21
+ # 注册表(全局单例)
22
+ # ==============================================================================
23
+
24
+ _OPERATOR_REGISTRY: Dict[str, Dict[str, Dict[str, Any]]] = {
25
+ "point": {},
26
+ "time": {},
27
+ "section": {},
28
+ "multi_section": {},
29
+ "talib": {},
30
+ }
31
+
32
+
33
+ class OperatorCategory:
34
+ POINT = "point"
35
+ TIME = "time"
36
+ SECTION = "section"
37
+ MULTI_SECTION = "multi_section"
38
+ TALIB = "talib"
39
+
40
+
41
+ # ==============================================================================
42
+ # 注册函数
43
+ # ==============================================================================
44
+
45
+ def register_operator(category: str, name: Optional[str] = None):
46
+ """装饰器:自动注册算子到注册表"""
47
+ def decorator(func: Callable):
48
+ op_name = name or func.__name__
49
+ sig = inspect.signature(func)
50
+
51
+ _OPERATOR_REGISTRY[category][op_name] = {
52
+ "name": op_name,
53
+ "category": category,
54
+ "func": func,
55
+ "doc": inspect.getdoc(func) or "",
56
+ "signature": str(sig),
57
+ "parameters": list(sig.parameters.keys()),
58
+ }
59
+ return func
60
+ return decorator
61
+
62
+
63
+ # ==============================================================================
64
+ # 辅助函数
65
+ # ==============================================================================
66
+
67
+ def _ensure_expr(f: Any) -> Expr:
68
+ """确保是表达式"""
69
+ if isinstance(f, pl.Expr):
70
+ return f
71
+ if isinstance(f, str):
72
+ return pl.col(f)
73
+ return pl.lit(f)
74
+
75
+ def _combo_add(a: Expr, b: Expr) -> Expr:
76
+ return a + b
77
+
78
+
79
+ def _combo_mul(a: Expr, b: Expr) -> Expr:
80
+ return a * b
81
+
82
+
83
+ def _combo_max(a: Expr, b: Expr) -> Expr:
84
+ return pl.max_horizontal(a, b)
85
+
86
+
87
+ def _combo_min(a: Expr, b: Expr) -> Expr:
88
+ return pl.min_horizontal(a, b)
89
+
90
+
91
+ _COMBO_METHODS = {
92
+ "add": _combo_add,
93
+ "sum": _combo_add,
94
+ "mul": _combo_mul,
95
+ "max": _combo_max,
96
+ "min": _combo_min,
97
+ }
98
+
99
+
100
+ def _inject(name: str, func: Callable):
101
+ """将函数注入调用者的全局作用域"""
102
+ import sys
103
+ frame = sys._getframe(2)
104
+ frame.f_globals[name] = func
105
+
106
+
107
+ def _expanding_var_expr(f: Union[Expr, str]) -> Expr:
108
+ """扩展窗口方差核心公式"""
109
+ e = _ensure_expr(f)
110
+ n = pl.int_range(0, pl.len()) + 1
111
+ mean = e.cum_sum() / n
112
+ mean_sq = (e ** 2).cum_sum() / n
113
+ return mean_sq - mean ** 2
114
+
115
+
116
+ def _apply_weights(f: Union[Expr, str], weights) -> Expr:
117
+ """通用加权移动算子"""
118
+ expr = _ensure_expr(f)
119
+ result = expr * weights[0]
120
+ for i in range(1, len(weights)):
121
+ result = result + expr.shift(i) * weights[i]
122
+ return result
123
+
124
+
125
+ def _cum_single_median(window):
126
+ window.sort()
127
+ n = len(window)
128
+ mid = n // 2
129
+ if n % 2 == 1:
130
+ return window[mid]
131
+ return (window[mid - 1] + window[mid]) / 2
132
+
133
+
134
+ def _cum_single_kurt(window):
135
+ if len(window) < 4:
136
+ return None
137
+ arr = np.array(window, dtype=np.float64)
138
+ m = arr.mean()
139
+ s2 = arr.std(ddof=1)
140
+ if s2 < 1e-15:
141
+ return None
142
+ return float(np.mean(((arr - m) / s2) ** 4) - 3)
143
+
144
+
145
+ def _cum_single_skew(window):
146
+ if len(window) < 3:
147
+ return None
148
+ arr = np.array(window, dtype=np.float64)
149
+ m = arr.mean()
150
+ s2 = arr.std(ddof=1)
151
+ if s2 < 1e-15:
152
+ return None
153
+ n = len(arr)
154
+ return float(n / ((n - 1) * (n - 2)) * np.sum(((arr - m) / s2) ** 3))
155
+
156
+
157
+ def _cum_single_quantile(window, quantile=0.5):
158
+ window.sort()
159
+ n = len(window)
160
+ idx = quantile * (n - 1)
161
+ lo = int(idx)
162
+ hi = min(lo + 1, n - 1)
163
+ frac = idx - lo
164
+ return window[lo] * (1 - frac) + window[hi] * frac
165
+
166
+
167
+ _CUM_SINGLE_FUNCS = {
168
+ "median": _cum_single_median,
169
+ "kurt": _cum_single_kurt,
170
+ "skew": _cum_single_skew,
171
+ }
172
+
173
+
174
+ def _cumulative_map_batches_single(e: Expr, func_name: str, return_dtype=pl.Float64) -> Expr:
175
+ """单因子扩展窗口 map_batches 通用包装"""
176
+ def _cum_stat(s: pl.Series) -> pl.Series:
177
+ vals = s.to_list()
178
+ result = []
179
+ for i in range(len(vals)):
180
+ window = [v for v in vals[:i + 1] if v is not None]
181
+ if len(window) == 0:
182
+ result.append(None)
183
+ else:
184
+ result.append(_CUM_SINGLE_FUNCS[func_name](window))
185
+ return pl.Series(values=result)
186
+ return e.map_batches(_cum_stat, return_dtype=return_dtype)
187
+
188
+
189
+ def _cum_dual_corr(arr1, arr2):
190
+ c = np.corrcoef(arr1, arr2)[0, 1]
191
+ return float(c) if np.isfinite(c) else None
192
+
193
+
194
+ def _cum_dual_cov(arr1, arr2):
195
+ c = np.cov(arr1, arr2)[0, 1]
196
+ return float(c) if np.isfinite(c) else None
197
+
198
+
199
+ _CUM_DUAL_FUNCS = {
200
+ "corr": _cum_dual_corr,
201
+ "cov": _cum_dual_cov,
202
+ }
203
+
204
+
205
+ def _cumulative_map_batches_dual(e1: Expr, e2: Expr, func_name: str,
206
+ return_dtype=pl.Float64) -> Expr:
207
+ """双因子扩展窗口 map_batches 通用包装"""
208
+ def _cum_stat(args: list) -> pl.Series:
209
+ s1, s2 = args[0], args[1]
210
+ v1 = s1.to_list()
211
+ v2 = s2.to_list()
212
+ result = []
213
+ for i in range(len(v1)):
214
+ pairs = [(a, b) for a, b in zip(v1[:i + 1], v2[:i + 1])
215
+ if a is not None and b is not None]
216
+ if len(pairs) < 2:
217
+ result.append(None)
218
+ else:
219
+ arr1, arr2 = zip(*pairs)
220
+ result.append(_CUM_DUAL_FUNCS[func_name](arr1, arr2))
221
+ return pl.Series(values=result)
222
+ return pl.map_batches([e1, e2], _cum_stat, return_dtype=return_dtype)
223
+
224
+
225
+ # ==============================================================================
226
+ # 工厂函数
227
+ # ==============================================================================
228
+
229
+ def _make_rolling_ts_wrapper(name: str, polars_method: str, doc: str):
230
+ """创建使用 polars 滚动方法的包装器"""
231
+ def _wrapper(f, window=20, min_periods=None, **kwargs):
232
+ e = _ensure_expr(f)
233
+ mp = min_periods or max(1, window // 2)
234
+ return getattr(e, polars_method)(window, min_samples=mp)
235
+
236
+ _wrapper.__name__ = name
237
+ _wrapper.__doc__ = doc
238
+ _wrapper.__qualname__ = name
239
+ register_operator(OperatorCategory.TIME, name)(_wrapper)
240
+ _inject(name, _wrapper)
241
+ return _wrapper
242
+
243
+
244
+ def _make_expanding_wrapper(name: str, polars_method: str, doc: str):
245
+ """创建使用 polars expanding 方法的包装器"""
246
+ def _wrapper(f, min_periods=None, **kwargs):
247
+ return _ensure_expr(f).__getattribute__(polars_method)()
248
+
249
+ _wrapper.__name__ = name
250
+ _wrapper.__doc__ = doc
251
+ _wrapper.__qualname__ = name
252
+ register_operator(OperatorCategory.TIME, name)(_wrapper)
253
+ _inject(name, _wrapper)
254
+ return _wrapper
255
+
256
+
257
+ _POLARS_NAN_METHOD_MAP = {
258
+ "nanmax": "nan_max",
259
+ "nanmin": "nan_min",
260
+ "nanargmax": "arg_max",
261
+ "nanargmin": "arg_min",
262
+ "nanmedian": "median",
263
+ "nancount": "count",
264
+ "nanprod": "product",
265
+ "nanmean": "mean",
266
+ "nansum": "sum",
267
+ "nanstd": "std",
268
+ "nanvar": "var",
269
+ }
270
+
271
+
272
+ def _make_nan_wrapper(name: str, polars_method: str, doc: str, extra_params=None):
273
+ """创建 NaN 跨截面聚合包装器"""
274
+ actual_method = _POLARS_NAN_METHOD_MAP.get(polars_method, polars_method)
275
+
276
+ def _wrapper(f, **kwargs):
277
+ return getattr(_ensure_expr(f), actual_method)(**(extra_params or {}))
278
+
279
+ _wrapper.__name__ = name
280
+ _wrapper.__doc__ = doc
281
+ _wrapper.__qualname__ = name
282
+ register_operator(OperatorCategory.POINT, name)(_wrapper)
283
+ _inject(name, _wrapper)
284
+ return _wrapper
285
+
286
+
287
+ def _make_dual_rolling_wrapper(name: str, doc: str, is_corr: bool = True):
288
+ """创建双因子滚动相关/协方差包装器(使用纯 Polars)"""
289
+ def _wrapper(f1, f2, window=20, min_periods=None, **kwargs):
290
+ e1 = _ensure_expr(f1)
291
+ e2 = _ensure_expr(f2)
292
+ mp = min_periods or max(1, window // 2)
293
+
294
+ if is_corr:
295
+ mean1 = e1.rolling_mean(window, min_samples=mp)
296
+ mean2 = e2.rolling_mean(window, min_samples=mp)
297
+ mean12 = (e1 * e2).rolling_mean(window, min_samples=mp)
298
+ cov = mean12 - mean1 * mean2
299
+ std1 = e1.rolling_std(window, min_samples=mp, ddof=0)
300
+ std2 = e2.rolling_std(window, min_samples=mp, ddof=0)
301
+ return cov / (std1 * std2 + 1e-8)
302
+ else:
303
+ m1 = e1.rolling_mean(window)
304
+ m2 = e2.rolling_mean(window)
305
+ return ((e1 - m1) * (e2 - m2)).rolling_mean(window)
306
+
307
+ _wrapper.__name__ = name
308
+ _wrapper.__doc__ = doc
309
+ _wrapper.__qualname__ = name
310
+ register_operator(OperatorCategory.TIME, name)(_wrapper)
311
+ _inject(name, _wrapper)
312
+ return _wrapper
313
+
314
+
315
+ def _make_alias(name: str, target_func: Callable, doc: str, category: str = None):
316
+ """创建算子别名"""
317
+ @functools.wraps(target_func)
318
+ def _alias(*args, **kwargs):
319
+ return target_func(*args, **kwargs)
320
+
321
+ _alias.__name__ = name
322
+ _alias.__doc__ = doc
323
+ _alias.__qualname__ = name
324
+ cat = category or OperatorCategory.TIME
325
+ register_operator(cat, name)(_alias)
326
+ _inject(name, _alias)
327
+ return _alias
328
+
329
+
330
+ def _make_diff_wrapper(name: str, polars_method: str, doc: str, neg_periods: bool = False):
331
+ """创建差分/变化包装器(使用纯 Polars)"""
332
+ def _wrapper(f, periods=1, **kwargs):
333
+ e = _ensure_expr(f)
334
+ if neg_periods:
335
+ return getattr(e, polars_method)(-periods)
336
+ return getattr(e, polars_method)(periods)
337
+
338
+ _wrapper.__name__ = name
339
+ _wrapper.__doc__ = doc
340
+ _wrapper.__qualname__ = name
341
+ register_operator(OperatorCategory.TIME, name)(_wrapper)
342
+ _inject(name, _wrapper)
343
+ return _wrapper
344
+
345
+
346
+ def _make_aggr_wrapper(method: str, doc: str):
347
+ """创建聚合算子包装器"""
348
+ from QuantNodes.factor_node.factor_functions.composite_ops import aggregate
349
+
350
+ def _wrapper(f, group_by, **kwargs):
351
+ return aggregate(f, group_by, method)
352
+
353
+ _wrapper.__name__ = f"aggr_{method}"
354
+ _wrapper.__doc__ = doc
355
+ _wrapper.__qualname__ = f"aggr_{method}"
356
+ register_operator(OperatorCategory.MULTI_SECTION, f"aggr_{method}")(_wrapper)
357
+ _inject(f"aggr_{method}", _wrapper)
358
+ return _wrapper
@@ -0,0 +1,317 @@
1
+ # coding=utf-8
2
+ """
3
+ 辅助函数模块
4
+
5
+ 此模块包含所有算子实现所需的辅助函数,独立于 factor_functions 包,
6
+ 以避免循环导入问题。
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import functools
12
+ import inspect
13
+ from typing import Any, Callable, Dict, Optional, Union
14
+
15
+ import numpy as np
16
+ import polars as pl
17
+ from polars import Expr
18
+
19
+
20
+ # ==============================================================================
21
+ # 注册表(全局单例)
22
+ # ==============================================================================
23
+
24
+ _OPERATOR_REGISTRY: Dict[str, Dict[str, Dict[str, Any]]] = {
25
+ "point": {},
26
+ "time": {},
27
+ "section": {},
28
+ "multi_section": {},
29
+ "talib": {},
30
+ }
31
+
32
+
33
+ class OperatorCategory:
34
+ POINT = "point"
35
+ TIME = "time"
36
+ SECTION = "section"
37
+ MULTI_SECTION = "multi_section"
38
+ TALIB = "talib"
39
+
40
+
41
+ # ==============================================================================
42
+ # 注册函数
43
+ # ==============================================================================
44
+
45
+ def register_operator(category: str, name: Optional[str] = None):
46
+ """装饰器:自动注册算子到注册表"""
47
+ def decorator(func: Callable):
48
+ op_name = name or func.__name__
49
+ sig = inspect.signature(func)
50
+
51
+ _OPERATOR_REGISTRY[category][op_name] = {
52
+ "name": op_name,
53
+ "category": category,
54
+ "func": func,
55
+ "doc": inspect.getdoc(func) or "",
56
+ "signature": str(sig),
57
+ "parameters": list(sig.parameters.keys()),
58
+ }
59
+ return func
60
+ return decorator
61
+
62
+
63
+ # ==============================================================================
64
+ # 辅助函数
65
+ # ==============================================================================
66
+
67
+ def _ensure_expr(f: Any) -> Expr:
68
+ """确保是表达式"""
69
+ if isinstance(f, pl.Expr):
70
+ return f
71
+ if isinstance(f, str):
72
+ return pl.col(f)
73
+ return pl.lit(f)
74
+
75
+
76
+ def _inject(name: str, func: Callable):
77
+ """将函数注入调用者的全局作用域"""
78
+ import sys
79
+ frame = sys._getframe(1)
80
+ frame.f_globals[name] = func
81
+
82
+
83
+ def _expanding_var_expr(f: Union[Expr, str]) -> Expr:
84
+ """扩展窗口方差核心公式"""
85
+ e = _ensure_expr(f)
86
+ n = pl.int_range(0, pl.len()) + 1
87
+ mean = e.cum_sum() / n
88
+ mean_sq = (e ** 2).cum_sum() / n
89
+ return mean_sq - mean ** 2
90
+
91
+
92
+ def _apply_weights(f: Union[Expr, str], weights) -> Expr:
93
+ """通用加权移动算子"""
94
+ expr = _ensure_expr(f)
95
+ result = expr * weights[0]
96
+ for i in range(1, len(weights)):
97
+ result = result + expr.shift(i) * weights[i]
98
+ return result
99
+
100
+
101
+ def _cum_single_median(window):
102
+ window.sort()
103
+ n = len(window)
104
+ mid = n // 2
105
+ if n % 2 == 1:
106
+ return window[mid]
107
+ return (window[mid - 1] + window[mid]) / 2
108
+
109
+
110
+ def _cum_single_kurt(window):
111
+ if len(window) < 4:
112
+ return None
113
+ arr = np.array(window, dtype=np.float64)
114
+ m = arr.mean()
115
+ s2 = arr.std(ddof=1)
116
+ if s2 < 1e-15:
117
+ return None
118
+ return float(np.mean(((arr - m) / s2) ** 4) - 3)
119
+
120
+
121
+ def _cum_single_skew(window):
122
+ if len(window) < 3:
123
+ return None
124
+ arr = np.array(window, dtype=np.float64)
125
+ m = arr.mean()
126
+ s2 = arr.std(ddof=1)
127
+ if s2 < 1e-15:
128
+ return None
129
+ n = len(arr)
130
+ return float(n / ((n - 1) * (n - 2)) * np.sum(((arr - m) / s2) ** 3))
131
+
132
+
133
+ def _cum_single_quantile(window, quantile=0.5):
134
+ window.sort()
135
+ n = len(window)
136
+ idx = quantile * (n - 1)
137
+ lo = int(idx)
138
+ hi = min(lo + 1, n - 1)
139
+ frac = idx - lo
140
+ return window[lo] * (1 - frac) + window[hi] * frac
141
+
142
+
143
+ _CUM_SINGLE_FUNCS = {
144
+ "median": _cum_single_median,
145
+ "kurt": _cum_single_kurt,
146
+ "skew": _cum_single_skew,
147
+ }
148
+
149
+
150
+ def _cumulative_map_batches_single(e: Expr, func_name: str, return_dtype=pl.Float64) -> Expr:
151
+ """单因子扩展窗口 map_batches 通用包装"""
152
+ def _cum_stat(s: pl.Series) -> pl.Series:
153
+ vals = s.to_list()
154
+ result = []
155
+ for i in range(len(vals)):
156
+ window = [v for v in vals[:i + 1] if v is not None]
157
+ if len(window) == 0:
158
+ result.append(None)
159
+ else:
160
+ result.append(_CUM_SINGLE_FUNCS[func_name](window))
161
+ return pl.Series(values=result)
162
+ return e.map_batches(_cum_stat, return_dtype=return_dtype)
163
+
164
+
165
+ def _cum_dual_corr(arr1, arr2):
166
+ c = np.corrcoef(arr1, arr2)[0, 1]
167
+ return float(c) if np.isfinite(c) else None
168
+
169
+
170
+ def _cum_dual_cov(arr1, arr2):
171
+ c = np.cov(arr1, arr2)[0, 1]
172
+ return float(c) if np.isfinite(c) else None
173
+
174
+
175
+ _CUM_DUAL_FUNCS = {
176
+ "corr": _cum_dual_corr,
177
+ "cov": _cum_dual_cov,
178
+ }
179
+
180
+
181
+ def _cumulative_map_batches_dual(e1: Expr, e2: Expr, func_name: str,
182
+ return_dtype=pl.Float64) -> Expr:
183
+ """双因子扩展窗口 map_batches 通用包装"""
184
+ def _cum_stat(args: list) -> pl.Series:
185
+ s1, s2 = args[0], args[1]
186
+ v1 = s1.to_list()
187
+ v2 = s2.to_list()
188
+ result = []
189
+ for i in range(len(v1)):
190
+ pairs = [(a, b) for a, b in zip(v1[:i + 1], v2[:i + 1])
191
+ if a is not None and b is not None]
192
+ if len(pairs) < 2:
193
+ result.append(None)
194
+ else:
195
+ arr1, arr2 = zip(*pairs)
196
+ result.append(_CUM_DUAL_FUNCS[func_name](arr1, arr2))
197
+ return pl.Series(values=result)
198
+ return pl.map_batches([e1, e2], _cum_stat, return_dtype=return_dtype)
199
+
200
+
201
+ # ==============================================================================
202
+ # 工厂函数
203
+ # ==============================================================================
204
+
205
+ def _make_rolling_ts_wrapper(name: str, ts_method: str, doc: str):
206
+ """创建委托 TimeSeriesOperators 的滚动窗口包装器"""
207
+ from QuantNodes.operators.time_series import TimeSeriesOperators
208
+ _ts_method = getattr(TimeSeriesOperators, ts_method)
209
+
210
+ def _wrapper(f, window=20, min_periods=None, **kwargs):
211
+ return _ts_method(f, window, min_periods)
212
+
213
+ _wrapper.__name__ = name
214
+ _wrapper.__doc__ = doc
215
+ _wrapper.__qualname__ = name
216
+ register_operator(OperatorCategory.TIME, name)(_wrapper)
217
+ _inject(name, _wrapper)
218
+ print(f"DEBUG: Injected {name}, checking namespace...")
219
+ import sys
220
+ frame = sys._getframe(0)
221
+ while frame:
222
+ if 'rolling_corr' in frame.f_globals:
223
+ print(f" Found rolling_corr in frame: {frame.f_code.co_name}")
224
+ break
225
+ frame = frame.f_back
226
+ else:
227
+ print(" rolling_corr NOT FOUND in any frame!")
228
+ return _wrapper
229
+
230
+
231
+ def _make_expanding_wrapper(name: str, polars_method: str, doc: str):
232
+ """创建使用 polars expanding 方法的包装器"""
233
+ def _wrapper(f, min_periods=None, **kwargs):
234
+ return _ensure_expr(f).__getattribute__(polars_method)()
235
+
236
+ _wrapper.__name__ = name
237
+ _wrapper.__doc__ = doc
238
+ _wrapper.__qualname__ = name
239
+ register_operator(OperatorCategory.TIME, name)(_wrapper)
240
+ _inject(name, _wrapper)
241
+ return _wrapper
242
+
243
+
244
+ def _make_nan_wrapper(name: str, polars_method: str, doc: str, extra_params=None):
245
+ """创建 NaN 跨截面聚合包装器"""
246
+ def _wrapper(f, **kwargs):
247
+ return getattr(_ensure_expr(f), polars_method)(**(extra_params or {}))
248
+
249
+ _wrapper.__name__ = name
250
+ _wrapper.__doc__ = doc
251
+ _wrapper.__qualname__ = name
252
+ register_operator(OperatorCategory.POINT, name)(_wrapper)
253
+ _inject(name, _wrapper)
254
+ return _wrapper
255
+
256
+
257
+ def _make_dual_rolling_wrapper(name: str, ts_method: str, doc: str):
258
+ """创建双因子滚动相关/协方差包装器"""
259
+ from QuantNodes.operators.time_series import TimeSeriesOperators
260
+ _ts_func = getattr(TimeSeriesOperators, ts_method)
261
+
262
+ def _wrapper(f1, f2, window=20, min_periods=None, **kwargs):
263
+ min_periods = min_periods or max(1, window // 2)
264
+ return _ts_func(f1, f2, window, min_periods)
265
+
266
+ _wrapper.__name__ = name
267
+ _wrapper.__doc__ = doc
268
+ _wrapper.__qualname__ = name
269
+ register_operator(OperatorCategory.TIME, name)(_wrapper)
270
+ _inject(name, _wrapper)
271
+ return _wrapper
272
+
273
+
274
+ def _make_alias(name: str, target_func: Callable, doc: str, category: str = None):
275
+ """创建算子别名"""
276
+ @functools.wraps(target_func)
277
+ def _alias(*args, **kwargs):
278
+ return target_func(*args, **kwargs)
279
+
280
+ _alias.__name__ = name
281
+ _alias.__doc__ = doc
282
+ _alias.__qualname__ = name
283
+ cat = category or OperatorCategory.TIME
284
+ register_operator(cat, name)(_alias)
285
+ _inject(name, _alias)
286
+ return _alias
287
+
288
+
289
+ def _make_diff_wrapper(name: str, ts_method: str, doc: str):
290
+ """创建差分/变化包装器"""
291
+ from QuantNodes.operators.time_series import TimeSeriesOperators
292
+ _ts_func = getattr(TimeSeriesOperators, ts_method)
293
+
294
+ def _wrapper(f, periods=1, **kwargs):
295
+ return _ts_func(f, periods)
296
+
297
+ _wrapper.__name__ = name
298
+ _wrapper.__doc__ = doc
299
+ _wrapper.__qualname__ = name
300
+ register_operator(OperatorCategory.TIME, name)(_wrapper)
301
+ _inject(name, _wrapper)
302
+ return _wrapper
303
+
304
+
305
+ def _make_aggr_wrapper(method: str, doc: str):
306
+ """创建聚合算子包装器"""
307
+ from QuantNodes.factor_node.factor_functions.composite_ops import aggregate
308
+
309
+ def _wrapper(f, group_by, **kwargs):
310
+ return aggregate(f, group_by, method)
311
+
312
+ _wrapper.__name__ = f"aggr_{method}"
313
+ _wrapper.__doc__ = doc
314
+ _wrapper.__qualname__ = f"aggr_{method}"
315
+ register_operator(OperatorCategory.MULTI_SECTION, f"aggr_{method}")(_wrapper)
316
+ _inject(f"aggr_{method}", _wrapper)
317
+ return _wrapper