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,51 @@
1
+ # coding=utf-8
2
+ """
3
+ 符号计算引擎
4
+
5
+ 提供表达式到 SQL 的编译能力,支持多种数据库方言。
6
+ """
7
+
8
+ from QuantNodes.symbolic.dialect import (
9
+ DialectType,
10
+ SQLDialect,
11
+ ClickHouseDialect,
12
+ DuckDBDialect,
13
+ MySQLDialect,
14
+ )
15
+ from QuantNodes.symbolic.compiler import SQLCompiler, compile_expression
16
+ from QuantNodes.symbolic.expression import (
17
+ SQLExpression,
18
+ ColumnRef,
19
+ LiteralValue,
20
+ SQLBinaryOp,
21
+ SQLUnaryOp,
22
+ SQLComparison,
23
+ SQLLogicalOp,
24
+ SQLFunction,
25
+ SQLCase,
26
+ )
27
+ from QuantNodes.symbolic.functions import (
28
+ TechnicalFunctions,
29
+ TA_FUNCTIONS,
30
+ )
31
+
32
+ __all__ = [
33
+ "DialectType",
34
+ "SQLDialect",
35
+ "ClickHouseDialect",
36
+ "DuckDBDialect",
37
+ "MySQLDialect",
38
+ "SQLCompiler",
39
+ "compile_expression",
40
+ "SQLExpression",
41
+ "ColumnRef",
42
+ "LiteralValue",
43
+ "SQLBinaryOp",
44
+ "SQLUnaryOp",
45
+ "SQLComparison",
46
+ "SQLLogicalOp",
47
+ "SQLFunction",
48
+ "SQLCase",
49
+ "TechnicalFunctions",
50
+ "TA_FUNCTIONS",
51
+ ]
@@ -0,0 +1,113 @@
1
+ # coding=utf-8
2
+ """
3
+ 符号计算引擎 - SQL 编译器
4
+
5
+ 使用 Visitor 模式将 SQLExpression AST 编译为 SQL 字符串。
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple
11
+
12
+ if TYPE_CHECKING:
13
+ from QuantNodes.symbolic.dialect import SQLDialect
14
+ from QuantNodes.symbolic.expression import SQLExpression
15
+
16
+
17
+ class SQLCompiler:
18
+ """
19
+ SQL 编译器
20
+
21
+ 将 SQLExpression AST 编译为 SQL 字符串。
22
+
23
+ Examples:
24
+ >>> from QuantNodes.symbolic import ClickHouseDialect, ColumnRef, SQLBinaryOp
25
+ >>> compiler = SQLCompiler(ClickHouseDialect())
26
+ >>> expr = ColumnRef("close") + ColumnRef("open")
27
+ >>> sql = compiler.compile(expr)
28
+ >>> print(sql)
29
+ (`close` + `open`)
30
+ """
31
+
32
+ def __init__(self, dialect: "SQLDialect"):
33
+ self.dialect = dialect
34
+ self._context: Dict[str, Any] = {}
35
+
36
+ def compile(self, expr: "SQLExpression") -> str:
37
+ """编译表达式为 SQL 字符串"""
38
+ return expr.to_sql(self.dialect)
39
+
40
+ def compile_to_select(
41
+ self,
42
+ columns: List["SQLExpression"],
43
+ table: str,
44
+ where: Optional["SQLExpression"] = None,
45
+ group_by: Optional[List["SQLExpression"]] = None,
46
+ having: Optional["SQLExpression"] = None,
47
+ order_by: Optional[List[Tuple["SQLExpression", str]]] = None,
48
+ limit: Optional[int] = None,
49
+ ) -> str:
50
+ """编译为完整 SELECT 语句"""
51
+ col_sqls = [col.to_sql(self.dialect) for col in columns]
52
+ cols_str = ", ".join(col_sqls)
53
+
54
+ table_sql = self.dialect.quote_identifier(table)
55
+
56
+ sql_parts = [f"SELECT {cols_str}", f"FROM {table_sql}"]
57
+
58
+ if where is not None:
59
+ sql_parts.append(f"WHERE {where.to_sql(self.dialect)}")
60
+
61
+ if group_by is not None:
62
+ group_sqls = [g.to_sql(self.dialect) for g in group_by]
63
+ sql_parts.append(f"GROUP BY {', '.join(group_sqls)}")
64
+
65
+ if having is not None:
66
+ sql_parts.append(f"HAVING {having.to_sql(self.dialect)}")
67
+
68
+ if order_by is not None:
69
+ order_sqls = []
70
+ for expr, direction in order_by:
71
+ order_sqls.append(f"{expr.to_sql(self.dialect)} {direction}")
72
+ sql_parts.append(f"ORDER BY {', '.join(order_sqls)}")
73
+
74
+ if limit is not None:
75
+ sql_parts.append(f"LIMIT {limit}")
76
+
77
+ return " ".join(sql_parts)
78
+
79
+
80
+ def compile_expression(
81
+ expr: "SQLExpression",
82
+ dialect: Optional["SQLDialect"] = None,
83
+ dialect_type: Optional[str] = None,
84
+ ) -> str:
85
+ """
86
+ 便捷函数:编译表达式为 SQL 字符串
87
+
88
+ Args:
89
+ expr: SQL 表达式
90
+ dialect: SQL 方言实例
91
+ dialect_type: 方言类型名称 (clickhouse/duckdb/mysql)
92
+
93
+ Returns:
94
+ SQL 字符串
95
+ """
96
+ if dialect is None and dialect_type is None:
97
+ from QuantNodes.symbolic.dialect import ClickHouseDialect
98
+ dialect = ClickHouseDialect()
99
+ elif dialect is None and dialect_type is not None:
100
+ dialect_map = {
101
+ "clickhouse": "ClickHouseDialect",
102
+ "duckdb": "DuckDBDialect",
103
+ "mysql": "MySQLDialect",
104
+ }
105
+ dialect_name = dialect_map.get(dialect_type.lower())
106
+ if dialect_name is None:
107
+ raise ValueError(f"Unknown dialect type: {dialect_type}")
108
+ from QuantNodes.symbolic.dialect import ClickHouseDialect, DuckDBDialect, MySQLDialect # noqa: F401
109
+ dialect_cls = locals()[dialect_name]
110
+ dialect = dialect_cls()
111
+
112
+ compiler = SQLCompiler(dialect)
113
+ return compiler.compile(expr)
@@ -0,0 +1,260 @@
1
+ # coding=utf-8
2
+ """符号计算引擎 - 数据库方言抽象"""
3
+ from __future__ import annotations
4
+ from abc import ABC, abstractmethod
5
+ from enum import Enum
6
+ from typing import Any, List, Optional, Tuple
7
+
8
+ class DialectType(Enum):
9
+ CLICKHOUSE = "clickhouse"
10
+ DUCKDB = "duckdb"
11
+ MYSQL = "mysql"
12
+ POSTGRESQL = "postgresql"
13
+
14
+ class SQLDialect(ABC):
15
+ dialect_name: DialectType
16
+ @abstractmethod
17
+ def quote_identifier(self, name: str) -> str: pass
18
+ @abstractmethod
19
+ def quote_literal(self, value: Any) -> str: pass
20
+ @abstractmethod
21
+ def func_now(self) -> str: pass
22
+ @abstractmethod
23
+ def func_date_diff(self, unit: str, start: str, end: str) -> str: pass
24
+ @abstractmethod
25
+ def func_coalesce(self, *args: str) -> str: pass
26
+ @abstractmethod
27
+ def func_ifnull(self, expr: str, default: str) -> str: pass
28
+ @abstractmethod
29
+ def func_if(self, condition: str, then: str, else_: str) -> str: pass
30
+ @abstractmethod
31
+ def func_case(self, when_clauses: List[Tuple[str, str]], else_: Optional[str] = None) -> str: pass
32
+ @abstractmethod
33
+ def func_cast(self, expr: str, target_type: str) -> str: pass
34
+ @abstractmethod
35
+ def func_interval(self, value: str, unit: str) -> str: pass
36
+ @abstractmethod
37
+ def is_distinct_from(self, a: str, b: str) -> str: pass
38
+ @abstractmethod
39
+ def is_not_distinct_from(self, a: str, b: str) -> str: pass
40
+ @abstractmethod
41
+ def func_rank(self) -> str: pass
42
+ @abstractmethod
43
+ def func_dense_rank(self) -> str: pass
44
+ @abstractmethod
45
+ def func_row_number(self) -> str: pass
46
+ @abstractmethod
47
+ def func_lag(self, expr: str, offset: int, default: Optional[str] = None) -> str: pass
48
+ @abstractmethod
49
+ def func_lead(self, expr: str, offset: int, default: Optional[str] = None) -> str: pass
50
+ @abstractmethod
51
+ def func_avg(self, expr: str) -> str: pass
52
+ @abstractmethod
53
+ def func_sum(self, expr: str) -> str: pass
54
+ @abstractmethod
55
+ def func_max(self, expr: str) -> str: pass
56
+ @abstractmethod
57
+ def func_min(self, expr: str) -> str: pass
58
+ @abstractmethod
59
+ def func_count(self, expr: str) -> str: pass
60
+ @abstractmethod
61
+ def func_stddev(self, expr: str) -> str: pass
62
+ @abstractmethod
63
+ def func_variance(self, expr: str) -> str: pass
64
+ @abstractmethod
65
+ def func_quantile(self, expr: str, quantile: float) -> str: pass
66
+ @abstractmethod
67
+ def func_abs(self, expr: str) -> str: pass
68
+ @abstractmethod
69
+ def func_ceil(self, expr: str) -> str: pass
70
+ @abstractmethod
71
+ def func_floor(self, expr: str) -> str: pass
72
+ @abstractmethod
73
+ def func_round(self, expr: str, decimals: int = 0) -> str: pass
74
+ @abstractmethod
75
+ def func_pow(self, expr: str, power: float) -> str: pass
76
+ @abstractmethod
77
+ def func_sqrt(self, expr: str) -> str: pass
78
+ @abstractmethod
79
+ def func_ln(self, expr: str) -> str: pass
80
+ @abstractmethod
81
+ def func_log10(self, expr: str) -> str: pass
82
+ @abstractmethod
83
+ def func_cumsum(self, expr: str) -> str: pass
84
+ @abstractmethod
85
+ def func_running_diff(self, expr: str) -> str: pass
86
+ @abstractmethod
87
+ def func_length(self, expr: str) -> str: pass
88
+ @abstractmethod
89
+ def func_concat(self, *args: str) -> str: pass
90
+ @abstractmethod
91
+ def func_upper(self, expr: str) -> str: pass
92
+ @abstractmethod
93
+ def func_lower(self, expr: str) -> str: pass
94
+ @abstractmethod
95
+ def func_substring(self, expr: str, start: int, length: Optional[int] = None) -> str: pass
96
+ @abstractmethod
97
+ def parse_datetime(self, expr: str, format: str) -> str: pass
98
+ @abstractmethod
99
+ def extract_date_part(self, expr: str, part: str) -> str: pass
100
+ class BaseSQLDialect(SQLDialect):
101
+ def func_case(self, when_clauses: List[Tuple[str, str]], else_: Optional[str] = None) -> str:
102
+ parts = ["CASE WHEN " + cond + " THEN " + val for cond, val in when_clauses]
103
+ if else_:
104
+ parts.append("ELSE " + else_)
105
+ parts.append("END")
106
+ return " ".join(parts)
107
+
108
+ def func_cast(self, expr: str, target_type: str) -> str:
109
+ return "CAST(" + expr + " AS " + target_type + ")"
110
+
111
+ def func_rank(self) -> str: return "rank()"
112
+ def func_dense_rank(self) -> str: return "dense_rank()"
113
+ def func_row_number(self) -> str: return "row_number()"
114
+ def func_avg(self, expr: str) -> str: return "avg(" + expr + ")"
115
+ def func_sum(self, expr: str) -> str: return "sum(" + expr + ")"
116
+ def func_max(self, expr: str) -> str: return "max(" + expr + ")"
117
+ def func_min(self, expr: str) -> str: return "min(" + expr + ")"
118
+ def func_count(self, expr: str) -> str: return "count(" + expr + ")"
119
+ def func_abs(self, expr: str) -> str: return "abs(" + expr + ")"
120
+ def func_floor(self, expr: str) -> str: return "floor(" + expr + ")"
121
+ def func_round(self, expr: str, decimals: int = 0) -> str: return "round(" + expr + ", " + str(decimals) + ")"
122
+ def func_pow(self, expr: str, power: float) -> str: return "pow(" + expr + ", " + str(power) + ")"
123
+ def func_sqrt(self, expr: str) -> str: return "sqrt(" + expr + ")"
124
+ def func_concat(self, *args: str) -> str: return "concat(" + ", ".join(args) + ")"
125
+ def func_upper(self, expr: str) -> str: return "upper(" + expr + ")"
126
+ def func_lower(self, expr: str) -> str: return "lower(" + expr + ")"
127
+
128
+ def func_substring(self, expr: str, start: int, length: Optional[int] = None) -> str:
129
+ if length is not None:
130
+ return "substring(" + expr + ", " + str(start) + ", " + str(length) + ")"
131
+ return "substring(" + expr + ", " + str(start) + ")"
132
+
133
+ class ClickHouseDialect(BaseSQLDialect):
134
+ dialect_name = DialectType.CLICKHOUSE
135
+
136
+ def quote_identifier(self, name: str) -> str: return "`" + name + "`"
137
+
138
+ def quote_literal(self, value: Any) -> str:
139
+ if value is None: return "NULL"
140
+ if isinstance(value, str): return "'" + value.replace("'", "\'\'") + "'"
141
+ if isinstance(value, bool): return "1" if value else "0"
142
+ return str(value)
143
+
144
+ def func_now(self) -> str: return "now()"
145
+ def func_date_diff(self, unit: str, start: str, end: str) -> str: return "dateDiff('" + unit + "', " + start + ", " + end + ")"
146
+ def func_coalesce(self, *args: str) -> str: return "coalesce(" + ", ".join(args) + ")"
147
+ def func_ifnull(self, expr: str, default: str) -> str: return "ifNull(" + expr + ", " + default + ")"
148
+ def func_if(self, condition: str, then: str, else_: str) -> str: return "if(" + condition + ", " + then + ", " + else_ + ")"
149
+ def func_interval(self, value: str, unit: str) -> str: return "INTERVAL " + value + " " + unit
150
+ def is_distinct_from(self, a: str, b: str) -> str: return a + " IS DISTINCT FROM " + b
151
+ def is_not_distinct_from(self, a: str, b: str) -> str: return a + " IS NOT DISTINCT FROM " + b
152
+
153
+ def func_lag(self, expr: str, offset: int, default: Optional[str] = None) -> str:
154
+ if default is not None: return "lagInFrame(" + expr + ", " + str(offset) + ", " + default + ")"
155
+ return "lagInFrame(" + expr + ", " + str(offset) + ")"
156
+
157
+ def func_lead(self, expr: str, offset: int, default: Optional[str] = None) -> str:
158
+ if default is not None: return "leadInFrame(" + expr + ", " + str(offset) + ", " + default + ")"
159
+ return "leadInFrame(" + expr + ", " + str(offset) + ")"
160
+
161
+ def func_stddev(self, expr: str) -> str: return "stddevPop(" + expr + ")"
162
+ def func_variance(self, expr: str) -> str: return "varPop(" + expr + ")"
163
+ def func_quantile(self, expr: str, quantile: float) -> str: return "quantile(" + str(quantile) + ")(" + expr + ")"
164
+ def func_ceil(self, expr: str) -> str: return "ceil(" + expr + ")"
165
+ def func_ln(self, expr: str) -> str: return "log(" + expr + ")"
166
+ def func_log10(self, expr: str) -> str: return "log10(" + expr + ")"
167
+ def func_cumsum(self, expr: str) -> str: return "sum(" + expr + ")"
168
+ def func_running_diff(self, expr: str) -> str: return "runningDifference(" + expr + ")"
169
+ def func_length(self, expr: str) -> str: return "length(" + expr + ")"
170
+ def parse_datetime(self, expr: str, format: str) -> str: return "parseDateTime(" + expr + ", '" + format + "')"
171
+ def extract_date_part(self, expr: str, part: str) -> str: return "extract(" + part + " FROM " + expr + ")"
172
+
173
+ class DuckDBDialect(BaseSQLDialect):
174
+ dialect_name = DialectType.DUCKDB
175
+
176
+ def quote_identifier(self, name: str) -> str: return '"' + name + '"'
177
+
178
+ def quote_literal(self, value: Any) -> str:
179
+ if value is None: return "NULL"
180
+ if isinstance(value, str): return "'" + value.replace("'", "''") + "'"
181
+ if isinstance(value, bool): return "TRUE" if value else "FALSE"
182
+ return str(value)
183
+
184
+ def func_now(self) -> str: return "now()"
185
+ def func_date_diff(self, unit: str, start: str, end: str) -> str: return "datediff('" + unit + "', " + start + ", " + end + ")"
186
+ def func_coalesce(self, *args: str) -> str: return "coalesce(" + ", ".join(args) + ")"
187
+ def func_ifnull(self, expr: str, default: str) -> str: return "ifnull(" + expr + ", " + default + ")"
188
+ def func_if(self, condition: str, then: str, else_: str) -> str: return "if(" + condition + ", " + then + ", " + else_ + ")"
189
+ def func_interval(self, value: str, unit: str) -> str: return "INTERVAL '" + value + "' " + unit
190
+ def is_distinct_from(self, a: str, b: str) -> str: return a + " IS DISTINCT FROM " + b
191
+ def is_not_distinct_from(self, a: str, b: str) -> str: return a + " IS NOT DISTINCT FROM " + b
192
+
193
+ def func_lag(self, expr: str, offset: int, default: Optional[str] = None) -> str:
194
+ if default is not None: return "lag(" + expr + ", " + str(offset) + ", " + default + ")"
195
+ return "lag(" + expr + ", " + str(offset) + ")"
196
+
197
+ def func_lead(self, expr: str, offset: int, default: Optional[str] = None) -> str:
198
+ if default is not None: return "lead(" + expr + ", " + str(offset) + ", " + default + ")"
199
+ return "lead(" + expr + ", " + str(offset) + ")"
200
+
201
+ def func_stddev(self, expr: str) -> str: return "stddev_pop(" + expr + ")"
202
+ def func_variance(self, expr: str) -> str: return "var_pop(" + expr + ")"
203
+ def func_quantile(self, expr: str, quantile: float) -> str: return "quantile_cont(" + expr + ", " + str(quantile) + ")"
204
+ def func_ceil(self, expr: str) -> str: return "ceil(" + expr + ")"
205
+ def func_ln(self, expr: str) -> str: return "ln(" + expr + ")"
206
+ def func_log10(self, expr: str) -> str: return "log10(" + expr + ")"
207
+ def func_cumsum(self, expr: str) -> str: return "sum(" + expr + ")"
208
+ def func_running_diff(self, expr: str) -> str: return expr + " - lag(" + expr + ", 1)"
209
+ def func_length(self, expr: str) -> str: return "length(" + expr + ")"
210
+ def parse_datetime(self, expr: str, format: str) -> str: return "strptime(" + expr + ", '" + format + "')"
211
+ def extract_date_part(self, expr: str, part: str) -> str: return "date_part('" + part + "', " + expr + ")"
212
+
213
+ class MySQLDialect(BaseSQLDialect):
214
+ dialect_name = DialectType.MYSQL
215
+
216
+ def quote_identifier(self, name: str) -> str: return "`" + name + "`"
217
+
218
+ def quote_literal(self, value: Any) -> str:
219
+ if value is None: return "NULL"
220
+ if isinstance(value, str): return "'" + value.replace("'", "''") + "'"
221
+ if isinstance(value, bool): return "1" if value else "0"
222
+ return str(value)
223
+
224
+ def func_now(self) -> str: return "NOW()"
225
+ def func_date_diff(self, unit: str, start: str, end: str) -> str: return "DATEDIFF(" + end + ", " + start + ")"
226
+ def func_coalesce(self, *args: str) -> str: return "COALESCE(" + ", ".join(args) + ")"
227
+ def func_ifnull(self, expr: str, default: str) -> str: return "IFNULL(" + expr + ", " + default + ")"
228
+ def func_if(self, condition: str, then: str, else_: str) -> str: return "IF(" + condition + ", " + then + ", " + else_ + ")"
229
+ def func_interval(self, value: str, unit: str) -> str: return "INTERVAL " + value + " " + unit
230
+
231
+ def is_distinct_from(self, a: str, b: str) -> str:
232
+ return "(" + a + " IS NULL AND " + b + " IS NOT NULL OR " + a + " IS NOT NULL AND " + b + " IS NULL OR " + a + " <> " + b + ")"
233
+
234
+ def is_not_distinct_from(self, a: str, b: str) -> str:
235
+ return "(" + a + " IS NULL AND " + b + " IS NULL OR " + a + " = " + b + ")"
236
+
237
+ def func_lag(self, expr: str, offset: int, default: Optional[str] = None) -> str:
238
+ if default is not None: return "LAG(" + expr + ", " + str(offset) + ", " + default + ")"
239
+ return "LAG(" + expr + ", " + str(offset) + ")"
240
+
241
+ def func_lead(self, expr: str, offset: int, default: Optional[str] = None) -> str:
242
+ if default is not None: return "LEAD(" + expr + ", " + str(offset) + ", " + default + ")"
243
+ return "LEAD(" + expr + ", " + str(offset) + ")"
244
+
245
+ def func_stddev(self, expr: str) -> str: return "STDDEV_POP(" + expr + ")"
246
+ def func_variance(self, expr: str) -> str: return "VAR_POP(" + expr + ")"
247
+ def func_quantile(self, expr: str, quantile: float) -> str: return "QUANTILE_CONT(" + expr + ", " + str(quantile) + ")"
248
+ def func_ceil(self, expr: str) -> str: return "CEILING(" + expr + ")"
249
+ def func_ln(self, expr: str) -> str: return "LN(" + expr + ")"
250
+ def func_log10(self, expr: str) -> str: return "LOG10(" + expr + ")"
251
+ def func_cumsum(self, expr: str) -> str: return "SUM(" + expr + ")"
252
+ def func_running_diff(self, expr: str) -> str: return expr + " - LAG(" + expr + ", 1)"
253
+ def func_length(self, expr: str) -> str: return "LENGTH(" + expr + ")"
254
+ def func_upper(self, expr: str) -> str: return "UPPER(" + expr + ")"
255
+ def func_lower(self, expr: str) -> str: return "LOWER(" + expr + ")"
256
+ def func_substring(self, expr: str, start: int, length: Optional[int] = None) -> str:
257
+ if length is not None: return "SUBSTRING(" + expr + ", " + str(start) + ", " + str(length) + ")"
258
+ return "SUBSTRING(" + expr + ", " + str(start) + ")"
259
+ def parse_datetime(self, expr: str, format: str) -> str: return "STR_TO_DATE(" + expr + ", '" + format + "')"
260
+ def extract_date_part(self, expr: str, part: str) -> str: return "EXTRACT(" + part + " FROM " + expr + ")"
@@ -0,0 +1,147 @@
1
+ # coding=utf-8
2
+ """
3
+ 符号计算引擎 - 执行引擎
4
+
5
+ 在数据库上执行编译后的 SQL 表达式。
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional
11
+
12
+ if TYPE_CHECKING:
13
+ import pandas as pd
14
+ from QuantNodes.symbolic.compiler import SQLCompiler
15
+ from QuantNodes.symbolic.expression import SQLExpression
16
+
17
+
18
+ class SQLExecutor:
19
+ """
20
+ SQL 执行引擎
21
+
22
+ 在数据库连接上执行编译后的 SQL。
23
+
24
+ Examples:
25
+ >>> from QuantNodes.symbolic import ClickHouseDialect, SQLExecutor
26
+ >>> from QuantNodes.symbolic.expression import ColumnRef
27
+ >>> executor = SQLExecutor(db_connection, ClickHouseDialect())
28
+ >>> result = executor.execute("SELECT * FROM t")
29
+ """
30
+
31
+ def __init__(
32
+ self,
33
+ connection: Any,
34
+ compiler: Optional["SQLCompiler"] = None,
35
+ dialect: Optional["SQLDialect"] = None, # noqa: F821
36
+ ):
37
+ """
38
+ Args:
39
+ connection: 数据库连接对象
40
+ compiler: SQL 编译器实例
41
+ dialect: SQL 方言 (如果 compiler 为 None)
42
+ """
43
+ self.connection = connection
44
+ if compiler is not None:
45
+ self.compiler = compiler
46
+ elif dialect is not None:
47
+ from QuantNodes.symbolic.compiler import SQLCompiler
48
+ self.compiler = SQLCompiler(dialect)
49
+ else:
50
+ from QuantNodes.symbolic.dialect import ClickHouseDialect
51
+ from QuantNodes.symbolic.compiler import SQLCompiler
52
+ self.compiler = SQLCompiler(ClickHouseDialect())
53
+
54
+ def execute(
55
+ self,
56
+ sql: str,
57
+ params: Optional[Dict[str, Any]] = None,
58
+ ) -> "pd.DataFrame":
59
+ """
60
+ 执行 SQL 查询
61
+
62
+ Args:
63
+ sql: SQL 字符串
64
+ params: 查询参数
65
+
66
+ Returns:
67
+ DataFrame 结果
68
+ """
69
+ import pandas as pd
70
+
71
+ cursor = self.connection.cursor()
72
+
73
+ if params:
74
+ cursor.execute(sql, params)
75
+ else:
76
+ cursor.execute(sql)
77
+
78
+ if cursor.description:
79
+ columns = [desc[0] for desc in cursor.description]
80
+ rows = cursor.fetchall()
81
+ return pd.DataFrame(rows, columns=columns)
82
+
83
+ return pd.DataFrame()
84
+
85
+ def execute_expression(
86
+ self,
87
+ expr: "SQLExpression",
88
+ table: str,
89
+ columns: Optional[List["SQLExpression"]] = None,
90
+ where: Optional["SQLExpression"] = None,
91
+ group_by: Optional[List["SQLExpression"]] = None,
92
+ having: Optional["SQLExpression"] = None,
93
+ order_by: Optional[List] = None,
94
+ limit: Optional[int] = None,
95
+ ) -> "pd.DataFrame":
96
+ """
97
+ 执行表达式
98
+
99
+ Args:
100
+ expr: SQL 表达式
101
+ table: 表名
102
+ columns: 选择列
103
+ where: WHERE 条件
104
+ group_by: GROUP BY 列
105
+ having: HAVING 条件
106
+ order_by: ORDER BY 列
107
+ limit: LIMIT
108
+
109
+ Returns:
110
+ DataFrame 结果
111
+ """
112
+ if columns is None:
113
+ columns = [expr]
114
+ elif expr not in columns:
115
+ columns = columns + [expr]
116
+
117
+ sql = self.compiler.compile_to_select(
118
+ columns=columns,
119
+ table=table,
120
+ where=where,
121
+ group_by=group_by,
122
+ having=having,
123
+ order_by=order_by,
124
+ limit=limit,
125
+ )
126
+
127
+ return self.execute(sql)
128
+
129
+
130
+ def execute_sql(
131
+ sql: str,
132
+ connection: Any,
133
+ params: Optional[Dict[str, Any]] = None,
134
+ ) -> "pd.DataFrame":
135
+ """
136
+ 便捷函数:执行 SQL
137
+
138
+ Args:
139
+ sql: SQL 字符串
140
+ connection: 数据库连接
141
+ params: 查询参数
142
+
143
+ Returns:
144
+ DataFrame 结果
145
+ """
146
+ executor = SQLExecutor(connection)
147
+ return executor.execute(sql, params)