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,396 @@
1
+ # coding=utf-8
2
+ """
3
+ 策略生成器
4
+
5
+ 提供从自然语言生成 QuantNodes Pipeline 的功能。
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import re
10
+ import logging
11
+ from typing import Any, Dict, List, Optional, Union
12
+ from dataclasses import dataclass, field
13
+
14
+ from QuantNodes.ai.llm.base import LLMClientBase, Message, MessageRole
15
+ from QuantNodes.ai.prompts import PromptLibrary
16
+ from QuantNodes.ai.sandbox import CodeSandbox, CodeValidationResult
17
+
18
+
19
+ @dataclass
20
+ class GenerationResult:
21
+ """生成结果"""
22
+ code: str
23
+ is_valid: bool
24
+ validation_result: Optional[CodeValidationResult] = None
25
+ error_message: Optional[str] = None
26
+ warnings: List[str] = field(default_factory=list)
27
+
28
+
29
+ class StrategyGenerator:
30
+ """
31
+ 策略生成器
32
+
33
+ 将自然语言描述转换为 QuantNodes Pipeline 代码。
34
+
35
+ Examples:
36
+ >>> generator = StrategyGenerator(llm_client)
37
+ >>> result = generator.generate("Create a momentum strategy")
38
+ >>> print(result.code)
39
+ """
40
+
41
+ CODE_BLOCK_PATTERN = re.compile(r'```(?:python)?\s*(.*?)```', re.DOTALL)
42
+
43
+ def __init__(
44
+ self,
45
+ llm_client=None,
46
+ code_sandbox: Optional[CodeSandbox] = None,
47
+ temperature: float = 0.7,
48
+ max_tokens: Optional[int] = None,
49
+ **kwargs
50
+ ):
51
+ """
52
+ 初始化策略生成器
53
+
54
+ Args:
55
+ llm_client: LLM 客户端 (默认使用 LLMGateway)
56
+ code_sandbox: 代码沙箱(用于验证)
57
+ temperature: 生成温度
58
+ max_tokens: 最大 token 数
59
+ """
60
+ if llm_client is None:
61
+ from QuantNodes.ai.llm.gateway import get_llm_gateway
62
+ llm_client = get_llm_gateway()
63
+ self.llm = llm_client
64
+ self.sandbox = code_sandbox or CodeSandbox()
65
+ self.temperature = temperature
66
+ self.max_tokens = max_tokens
67
+ self.extra_config = kwargs
68
+ self.logger = logging.getLogger(f"strategy.{self.__class__.__name__}")
69
+
70
+ def generate(
71
+ self,
72
+ description: str,
73
+ validate: bool = True,
74
+ context: Optional[Dict[str, Any]] = None,
75
+ **kwargs
76
+ ) -> GenerationResult:
77
+ """
78
+ 生成策略代码
79
+
80
+ Args:
81
+ description: 策略描述
82
+ validate: 是否验证代码
83
+ context: 额外上下文
84
+ **kwargs: 额外参数
85
+
86
+ Returns:
87
+ GenerationResult 生成结果
88
+ """
89
+ try:
90
+ system_prompt, user_prompt = PromptLibrary.format(
91
+ "strategy_generation",
92
+ trading_description=description
93
+ )
94
+
95
+ messages = [
96
+ Message(role=MessageRole.SYSTEM, content=system_prompt),
97
+ Message(role=MessageRole.USER, content=user_prompt),
98
+ ]
99
+
100
+ response = self.llm.chat(
101
+ messages=messages,
102
+ temperature=self.temperature,
103
+ max_tokens=self.max_tokens,
104
+ **kwargs
105
+ )
106
+
107
+ if not response:
108
+ return GenerationResult(
109
+ code="",
110
+ is_valid=False,
111
+ error_message="No response from LLM"
112
+ )
113
+
114
+ code = self._extract_code(response.content)
115
+
116
+ if not code:
117
+ return GenerationResult(
118
+ code="",
119
+ is_valid=False,
120
+ error_message="No code found in response"
121
+ )
122
+
123
+ validation_result = None
124
+ if validate:
125
+ validation_result = self.sandbox.validate(code)
126
+ if not validation_result.is_safe:
127
+ return GenerationResult(
128
+ code=code,
129
+ is_valid=False,
130
+ validation_result=validation_result,
131
+ error_message=f"Code validation failed: {validation_result.errors}"
132
+ )
133
+
134
+ return GenerationResult(
135
+ code=code,
136
+ is_valid=True,
137
+ validation_result=validation_result,
138
+ warnings=validation_result.warnings if validation_result else []
139
+ )
140
+
141
+ except Exception as e:
142
+ self.logger.error(f"Generation failed: {e}")
143
+ return GenerationResult(
144
+ code="",
145
+ is_valid=False,
146
+ error_message=str(e)
147
+ )
148
+
149
+ def _extract_code(self, content: str) -> str:
150
+ """从响应中提取代码"""
151
+ matches = self.CODE_BLOCK_PATTERN.findall(content)
152
+ if matches:
153
+ return matches[0].strip()
154
+
155
+ if '```' in content:
156
+ return ""
157
+
158
+ lines = content.split('\n')
159
+ code_lines = []
160
+ in_code = False
161
+
162
+ for line in lines:
163
+ if line.startswith('```'):
164
+ in_code = not in_code
165
+ continue
166
+ starts_code = (
167
+ line.startswith('import ') or line.startswith('from ')
168
+ or line.startswith('#')
169
+ )
170
+ if in_code or starts_code:
171
+ code_lines.append(line)
172
+
173
+ if code_lines:
174
+ return '\n'.join(code_lines)
175
+
176
+ return content.strip()
177
+
178
+ def generate_with_refinement(
179
+ self,
180
+ description: str,
181
+ max_iterations: int = 3,
182
+ **kwargs
183
+ ) -> GenerationResult:
184
+ """
185
+ 带精化的生成(自动修复问题)
186
+
187
+ Args:
188
+ description: 策略描述
189
+ max_iterations: 最大迭代次数
190
+ **kwargs: 额外参数
191
+
192
+ Returns:
193
+ GenerationResult 生成结果
194
+ """
195
+ result = self.generate(description, validate=True, **kwargs)
196
+
197
+ for iteration in range(max_iterations):
198
+ if result.is_valid:
199
+ return result
200
+
201
+ if not result.validation_result:
202
+ break
203
+
204
+ if result.validation_result.warnings_only:
205
+ refinement_prompt = (
206
+ f"Refine the following code to address warnings:\n\n"
207
+ f"{result.code}\n\n"
208
+ f"Warnings: {result.validation_result.warnings}"
209
+ )
210
+ result = self._refine_code(refinement_prompt, **kwargs)
211
+ else:
212
+ refinement_prompt = (
213
+ f"Fix the following code errors:\n\n"
214
+ f"{result.code}\n\n"
215
+ f"Errors: {result.validation_result.errors}"
216
+ )
217
+ result = self._refine_code(refinement_prompt, **kwargs)
218
+
219
+ return result
220
+
221
+ def _refine_code(self, refinement_prompt: str, **kwargs) -> GenerationResult:
222
+ """精化代码"""
223
+ messages = [
224
+ Message(role=MessageRole.SYSTEM, content=PromptLibrary.get_system_prompt()),
225
+ Message(role=MessageRole.USER, content=refinement_prompt),
226
+ ]
227
+
228
+ try:
229
+ response = self.llm.chat(
230
+ messages=messages,
231
+ temperature=self.temperature,
232
+ max_tokens=self.max_tokens,
233
+ **kwargs
234
+ )
235
+
236
+ if not response:
237
+ return GenerationResult(
238
+ code="",
239
+ is_valid=False,
240
+ error_message="No response from LLM"
241
+ )
242
+
243
+ code = self._extract_code(response.content)
244
+ validation_result = self.sandbox.validate(code)
245
+
246
+ return GenerationResult(
247
+ code=code,
248
+ is_valid=validation_result.is_safe,
249
+ validation_result=validation_result,
250
+ error_message=(
251
+ None if validation_result.is_safe
252
+ else f"Validation failed: {validation_result.errors}"
253
+ ),
254
+ warnings=validation_result.warnings
255
+ )
256
+
257
+ except Exception as e:
258
+ return GenerationResult(
259
+ code="",
260
+ is_valid=False,
261
+ error_message=str(e)
262
+ )
263
+
264
+ def review(
265
+ self,
266
+ code: str,
267
+ **kwargs
268
+ ) -> str:
269
+ """
270
+ 审查代码
271
+
272
+ Args:
273
+ code: 待审查的代码
274
+ **kwargs: 额外参数
275
+
276
+ Returns:
277
+ str 审查意见
278
+ """
279
+ messages = [
280
+ Message(role=MessageRole.SYSTEM, content=PromptLibrary.get_system_prompt()),
281
+ Message(
282
+ role=MessageRole.USER,
283
+ content=f"Review the following QuantNodes code:\n\n{code}",
284
+ ),
285
+ ]
286
+
287
+ try:
288
+ response = self.llm.chat(
289
+ messages=messages,
290
+ temperature=0.3,
291
+ max_tokens=self.max_tokens,
292
+ **kwargs
293
+ )
294
+
295
+ return response.content if response else "No response"
296
+
297
+ except Exception as e:
298
+ self.logger.error(f"Review failed: {e}")
299
+ return f"Review failed: {e}"
300
+
301
+ def explain_strategy(self, code: str, **kwargs) -> str:
302
+ """
303
+ 解释策略代码
304
+
305
+ Args:
306
+ code: 策略代码
307
+ **kwargs: 额外参数
308
+
309
+ Returns:
310
+ str 解释
311
+ """
312
+ prompt = (
313
+ f"Explain what this QuantNodes strategy does:\n\n{code}\n\n"
314
+ f"Provide a clear explanation of the pipeline and its components."
315
+ )
316
+
317
+ messages = [
318
+ Message(role=MessageRole.SYSTEM, content=PromptLibrary.get_system_prompt()),
319
+ Message(role=MessageRole.USER, content=prompt),
320
+ ]
321
+
322
+ try:
323
+ response = self.llm.chat(
324
+ messages=messages,
325
+ temperature=0.3,
326
+ max_tokens=self.max_tokens,
327
+ **kwargs
328
+ )
329
+
330
+ return response.content if response else "No response"
331
+
332
+ except Exception as e:
333
+ self.logger.error(f"Explanation failed: {e}")
334
+ return f"Explanation failed: {e}"
335
+
336
+
337
+ class NaturalLanguageToPipeline:
338
+ """
339
+ 自然语言转 Pipeline 工具
340
+
341
+ 提供更高级的 Pipeline 生成接口。
342
+ """
343
+
344
+ def __init__(
345
+ self,
346
+ llm_client=None,
347
+ code_sandbox: Optional[CodeSandbox] = None,
348
+ **kwargs
349
+ ):
350
+ """
351
+ 初始化转换器
352
+
353
+ Args:
354
+ llm_client: LLM 客户端 (默认使用 LLMGateway)
355
+ code_sandbox: 代码沙箱
356
+ """
357
+ if llm_client is None:
358
+ from QuantNodes.ai.llm.gateway import get_llm_gateway
359
+ llm_client = get_llm_gateway()
360
+ self.generator = StrategyGenerator(llm_client, code_sandbox, **kwargs)
361
+
362
+ def convert(
363
+ self,
364
+ description: str,
365
+ return_pipeline: bool = False,
366
+ **kwargs
367
+ ) -> Union[GenerationResult, Any]:
368
+ """
369
+ 转换自然语言为 Pipeline
370
+
371
+ Args:
372
+ description: 策略描述
373
+ return_pipeline: 是否返回可执行的 Pipeline 对象
374
+ **kwargs: 额外参数
375
+
376
+ Returns:
377
+ GenerationResult 或 Pipeline 对象
378
+ """
379
+ result = self.generator.generate_with_refinement(description, **kwargs)
380
+
381
+ if return_pipeline and result.is_valid:
382
+ try:
383
+ return self._execute_code(result.code)
384
+ except Exception as e:
385
+ result.warnings.append(f"Failed to execute code: {e}")
386
+ return result
387
+
388
+ return result
389
+
390
+ def _execute_code(self, code: str):
391
+ """执行代码并返回 Pipeline"""
392
+ context = {
393
+ 'QuantNodes': __import__('QuantNodes'),
394
+ }
395
+ exec_globals = self.generator.sandbox.validate_and_execute(code, context)
396
+ return exec_globals
@@ -0,0 +1,64 @@
1
+ # coding=utf-8
2
+ """
3
+ Backtest module for QuantNodes.
4
+
5
+ BaseNode-integrated backtest components.
6
+ """
7
+
8
+ from QuantNodes.backtest.backtest_node import (
9
+ BacktestNode,
10
+ BacktestResult,
11
+ BacktestPipeline,
12
+ )
13
+ from QuantNodes.backtest.strategy_node import (
14
+ StrategyNode,
15
+ Order,
16
+ Signal,
17
+ OrdersResult,
18
+ MAStrategyNode,
19
+ MomentumStrategyNode,
20
+ )
21
+ from QuantNodes.backtest.broker_node import (
22
+ BrokerNode,
23
+ Trade,
24
+ TradeResult,
25
+ SimulatedBrokerNode,
26
+ ExecutionBrokerNode,
27
+ )
28
+ from QuantNodes.backtest.risk_node import (
29
+ RiskNode,
30
+ RiskCheck,
31
+ RiskResult,
32
+ PositionLimitRiskNode,
33
+ StopLossRiskNode,
34
+ CashRiskNode,
35
+ CompositeRiskNode,
36
+ )
37
+ from QuantNodes.backtest.config_strategy import ConfigStrategyNode
38
+ from QuantNodes.backtest.config_runner import ConfigBacktestRunner
39
+
40
+ __all__ = [
41
+ 'BacktestNode',
42
+ 'BacktestResult',
43
+ 'BacktestPipeline',
44
+ 'StrategyNode',
45
+ 'Order',
46
+ 'Signal',
47
+ 'OrdersResult',
48
+ 'MAStrategyNode',
49
+ 'MomentumStrategyNode',
50
+ 'BrokerNode',
51
+ 'Trade',
52
+ 'TradeResult',
53
+ 'SimulatedBrokerNode',
54
+ 'ExecutionBrokerNode',
55
+ 'RiskNode',
56
+ 'RiskCheck',
57
+ 'RiskResult',
58
+ 'PositionLimitRiskNode',
59
+ 'StopLossRiskNode',
60
+ 'CashRiskNode',
61
+ 'CompositeRiskNode',
62
+ 'ConfigStrategyNode',
63
+ 'ConfigBacktestRunner',
64
+ ]
@@ -0,0 +1,188 @@
1
+ # coding=utf-8
2
+ """
3
+ BacktestNode - 回测引擎节点基类
4
+
5
+ 提供回测引擎节点的基础架构,继承自 BaseNode。
6
+ """
7
+ from __future__ import annotations
8
+
9
+ from abc import ABC, abstractmethod
10
+ from typing import Any, Dict, List, Optional, Union
11
+ from dataclasses import dataclass, field
12
+
13
+ import pandas as pd
14
+
15
+ from QuantNodes.core.node import BaseNode
16
+
17
+
18
+ @dataclass
19
+ class BacktestResult:
20
+ """回测结果容器"""
21
+ positions: pd.DataFrame = field(default_factory=pd.DataFrame)
22
+ trades: pd.DataFrame = field(default_factory=pd.DataFrame)
23
+ orders: pd.DataFrame = field(default_factory=pd.DataFrame)
24
+ equity_curve: pd.DataFrame = field(default_factory=pd.DataFrame)
25
+ statistics: Dict[str, Any] = field(default_factory=dict)
26
+ final_cash: float = 0.0
27
+ final_positions: Dict[str, float] = field(default_factory=dict)
28
+ total_return: float = 0.0
29
+ sharpe_ratio: float = 0.0
30
+ max_drawdown: float = 0.0
31
+ win_rate: float = 0.0
32
+
33
+
34
+ class BacktestNode(BaseNode[pd.DataFrame, BacktestResult], ABC):
35
+ """
36
+ 回测引擎节点基类
37
+
38
+ 提供统一的回测执行接口。
39
+
40
+ Subclasses must implement:
41
+ _run_backtest(): 执行具体回测逻辑
42
+
43
+ Examples:
44
+ >>> backtest = BacktestNode(config={
45
+ ... 'cash': 100000,
46
+ ... 'commission': 0.001,
47
+ ... 'margin': 0.1
48
+ ... })
49
+ >>> result = backtest.execute(data)
50
+ """
51
+
52
+ _enable_validation: bool = True
53
+ _enable_stats: bool = True
54
+
55
+ def __init__(self, name: str = None, config: Dict[str, Any] = None, **kwargs):
56
+ default_name = f"{self.__class__.__name__}"
57
+ super().__init__(name=name or default_name, config=config, **kwargs)
58
+
59
+ self._result: Optional[BacktestResult] = None
60
+ self._cash: float = self.config.get('cash', 100000)
61
+ self._commission: float = self.config.get('commission', 0.001)
62
+ self._margin: float = self.config.get('margin', 0.1)
63
+ self._trade_on_close: bool = self.config.get('trade_on_close', False)
64
+ self._hedging: bool = self.config.get('hedging', False)
65
+
66
+ @abstractmethod
67
+ def _run_backtest(
68
+ self,
69
+ quote_data: pd.DataFrame,
70
+ signals: pd.DataFrame,
71
+ **kwargs
72
+ ) -> BacktestResult:
73
+ """
74
+ 执行具体回测逻辑
75
+
76
+ Args:
77
+ quote_data: 行情数据 DataFrame
78
+ signals: 交易信号 DataFrame
79
+ **kwargs: 额外执行参数
80
+
81
+ Returns:
82
+ 回测结果 BacktestResult
83
+ """
84
+ pass
85
+
86
+ def _execute(
87
+ self,
88
+ input_data: Union[pd.DataFrame, tuple, None] = None,
89
+ **kwargs
90
+ ) -> BacktestResult:
91
+ """
92
+ 执行回测
93
+
94
+ Args:
95
+ input_data: 输入数据,可以是:
96
+ - pd.DataFrame: 行情数据
97
+ - tuple: (quote_data, signals) 元组
98
+ - None: 使用 config 中的默认数据
99
+ **kwargs: 额外执行参数
100
+
101
+ Returns:
102
+ BacktestResult 回测结果
103
+ """
104
+ if input_data is None:
105
+ quote_data = kwargs.get('quote_data')
106
+ signals = kwargs.get('signals')
107
+ elif isinstance(input_data, pd.DataFrame):
108
+ quote_data = input_data
109
+ signals = kwargs.get('signals')
110
+ elif isinstance(input_data, tuple) and len(input_data) == 2:
111
+ quote_data, signals = input_data
112
+ else:
113
+ raise ValueError(
114
+ f"input_data must be DataFrame or (quote_data, signals) tuple, "
115
+ f"got {type(input_data).__name__}"
116
+ )
117
+
118
+ if quote_data is None:
119
+ raise ValueError("quote_data is required")
120
+
121
+ self._result = self._run_backtest(quote_data, signals, **kwargs)
122
+ return self._result
123
+
124
+ def _validate_input(self, input_data: Any) -> None:
125
+ """验证输入数据"""
126
+ if input_data is None:
127
+ return
128
+
129
+ if isinstance(input_data, tuple):
130
+ quote_data, signals = input_data
131
+ if not isinstance(quote_data, pd.DataFrame):
132
+ raise ValueError(f"quote_data must be DataFrame, got {type(quote_data).__name__}")
133
+ elif isinstance(input_data, pd.DataFrame):
134
+ pass
135
+ else:
136
+ raise ValueError(
137
+ f"input_data must be DataFrame or tuple, got {type(input_data).__name__}"
138
+ )
139
+
140
+ def get_statistics(self) -> Dict[str, Any]:
141
+ """获取回测统计信息"""
142
+ if self._result is None:
143
+ return {}
144
+ return self._result.statistics
145
+
146
+ def get_equity_curve(self) -> pd.DataFrame:
147
+ """获取权益曲线"""
148
+ if self._result is None:
149
+ return pd.DataFrame()
150
+ return self._result.equity_curve
151
+
152
+ def reset(self) -> None:
153
+ """重置回测状态"""
154
+ super().reset()
155
+ self._result = None
156
+
157
+
158
+ class BacktestPipeline:
159
+ """
160
+ 回测管道
161
+
162
+ 将多个回测节点组合在一起执行。
163
+ """
164
+
165
+ def __init__(self, nodes: List[BacktestNode]):
166
+ self.nodes = nodes
167
+
168
+ def execute(
169
+ self,
170
+ input_data: Any = None,
171
+ **kwargs
172
+ ) -> List[BacktestResult]:
173
+ """执行所有回测节点"""
174
+ results = []
175
+ current_data = input_data
176
+
177
+ for node in self.nodes:
178
+ result = node.execute(current_data, **kwargs)
179
+ results.append(result)
180
+ current_data = result
181
+
182
+ return results
183
+
184
+ def __rshift__(self, other: BacktestNode) -> 'BacktestPipeline':
185
+ """重载 >> 运算符"""
186
+ if isinstance(other, BacktestPipeline):
187
+ return BacktestPipeline(self.nodes + other.nodes)
188
+ return BacktestPipeline(self.nodes + [other])