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,360 @@
1
+ # coding=utf-8
2
+ """
3
+ RiskNode - 风控节点
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 numpy as np
14
+ import pandas as pd
15
+
16
+ from QuantNodes.core.node import BaseNode
17
+ from QuantNodes.backtest.strategy_node import Order, OrdersResult
18
+ from QuantNodes.backtest.broker_node import TradeResult
19
+
20
+
21
+ @dataclass
22
+ class RiskCheck:
23
+ """风控检查结果"""
24
+ passed: bool
25
+ order: Order
26
+ reason: Optional[str] = None
27
+ adjusted_size: Optional[float] = None
28
+
29
+
30
+ @dataclass
31
+ class RiskResult:
32
+ """风控结果容器"""
33
+ passed_orders: List[Order] = field(default_factory=list)
34
+ rejected_orders: List[RiskCheck] = field(default_factory=list)
35
+ adjusted_orders: List[RiskCheck] = field(default_factory=list)
36
+
37
+ def to_dataframe(self) -> pd.DataFrame:
38
+ """转换为 DataFrame"""
39
+ if not self.passed_orders:
40
+ return pd.DataFrame()
41
+ return pd.DataFrame([
42
+ {
43
+ 'code': o.code,
44
+ 'size': o.size,
45
+ 'limit_price': o.limit_price,
46
+ 'create_date': o.create_date,
47
+ }
48
+ for o in self.passed_orders
49
+ ])
50
+
51
+
52
+ class RiskNode(BaseNode[OrdersResult, RiskResult], ABC):
53
+ """
54
+ 风控节点基类
55
+
56
+ 提供统一的风控检查接口。
57
+
58
+ Subclasses must implement:
59
+ _check_order(): 检查单个订单
60
+
61
+ Examples:
62
+ >>> risk = RiskNode(config={'max_position': 10000})
63
+ >>> result = risk.execute(orders)
64
+ """
65
+
66
+ _enable_validation: bool = True
67
+ _enable_stats: bool = True
68
+
69
+ def __init__(self, name: str = None, config: Dict[str, Any] = None, **kwargs):
70
+ default_name = f"{self.__class__.__name__}"
71
+ super().__init__(name=name or default_name, config=config, **kwargs)
72
+
73
+ self._result: Optional[RiskResult] = None
74
+ self._positions: Dict[str, float] = {}
75
+ self._max_position: float = self.config.get('max_position', float('inf'))
76
+ self._max_order_size: float = self.config.get('max_order_size', float('inf'))
77
+ self._min_order_size: float = self.config.get('min_order_size', 0)
78
+
79
+ @abstractmethod
80
+ def _check_order(
81
+ self,
82
+ order: Order,
83
+ positions: Dict[str, float],
84
+ **kwargs
85
+ ) -> RiskCheck:
86
+ """
87
+ 检查单个订单
88
+
89
+ Args:
90
+ order: 订单
91
+ positions: 当前持仓
92
+ **kwargs: 额外执行参数
93
+
94
+ Returns:
95
+ RiskCheck 风控检查结果
96
+ """
97
+ pass
98
+
99
+ def _execute(
100
+ self,
101
+ input_data: Union[OrdersResult, tuple, None] = None,
102
+ **kwargs
103
+ ) -> RiskResult:
104
+ """
105
+ 执行风控检查
106
+
107
+ Args:
108
+ input_data: 输入数据,可以是:
109
+ - OrdersResult: 订单结果
110
+ - tuple: (orders, positions) 元组
111
+ - None: 使用 config 中的默认数据
112
+ **kwargs: 额外执行参数
113
+
114
+ Returns:
115
+ RiskResult 风控结果
116
+ """
117
+ if input_data is None:
118
+ orders = kwargs.get('orders')
119
+ positions = kwargs.get('positions', {})
120
+ elif isinstance(input_data, OrdersResult):
121
+ orders = input_data
122
+ positions = kwargs.get('positions', {})
123
+ elif isinstance(input_data, tuple) and len(input_data) == 2:
124
+ orders, positions = input_data
125
+ if isinstance(positions, dict):
126
+ pass
127
+ elif isinstance(positions, TradeResult):
128
+ positions = self._derive_positions_from_trades(positions)
129
+ else:
130
+ raise ValueError(
131
+ f"positions must be dict or TradeResult, got {type(positions).__name__}"
132
+ )
133
+ else:
134
+ raise ValueError(
135
+ f"input_data must be OrdersResult or (orders, positions) tuple, "
136
+ f"got {type(input_data).__name__}"
137
+ )
138
+
139
+ if orders is None:
140
+ raise ValueError("orders is required")
141
+
142
+ self._result = RiskResult()
143
+
144
+ for order in orders.orders:
145
+ check = self._check_order(order, positions, **kwargs)
146
+ if check.passed:
147
+ self._result.passed_orders.append(order)
148
+ elif check.adjusted_size is not None:
149
+ adjusted_order = Order(
150
+ code=order.code,
151
+ size=check.adjusted_size,
152
+ limit_price=order.limit_price,
153
+ stop_price=order.stop_price,
154
+ sl_price=order.sl_price,
155
+ tp_price=order.tp_price,
156
+ order_id=order.order_id,
157
+ create_date=order.create_date,
158
+ )
159
+ self._result.passed_orders.append(adjusted_order)
160
+ self._result.adjusted_orders.append(check)
161
+ else:
162
+ self._result.rejected_orders.append(check)
163
+
164
+ return self._result
165
+
166
+ def _derive_positions_from_trades(self, trade_result: TradeResult) -> Dict[str, float]:
167
+ """从交易结果推导持仓"""
168
+ positions = {}
169
+ for trade in trade_result.trades:
170
+ current = positions.get(trade.code, 0)
171
+ if trade.side == 'buy':
172
+ positions[trade.code] = current + trade.size
173
+ else:
174
+ positions[trade.code] = current - trade.size
175
+ return positions
176
+
177
+ def _validate_input(self, input_data: Any) -> None:
178
+ """验证输入数据"""
179
+ if input_data is None:
180
+ return
181
+
182
+ if isinstance(input_data, OrdersResult):
183
+ pass
184
+ elif isinstance(input_data, tuple):
185
+ orders, positions = input_data
186
+ if not isinstance(orders, OrdersResult):
187
+ raise ValueError(f"orders must be OrdersResult, got {type(orders).__name__}")
188
+ else:
189
+ raise ValueError(
190
+ f"input_data must be OrdersResult or tuple, got {type(input_data).__name__}"
191
+ )
192
+
193
+ def get_passed_orders(self) -> List[Order]:
194
+ """获取通过的订单"""
195
+ if self._result is None:
196
+ return []
197
+ return self._result.passed_orders
198
+
199
+ def get_rejected_orders(self) -> List[RiskCheck]:
200
+ """获取拒绝的订单"""
201
+ if self._result is None:
202
+ return []
203
+ return self._result.rejected_orders
204
+
205
+ def set_positions(self, positions: Dict[str, float]) -> None:
206
+ """设置当前持仓"""
207
+ self._positions = positions.copy()
208
+
209
+
210
+ class PositionLimitRiskNode(RiskNode):
211
+ """仓位限制风控节点"""
212
+
213
+ def __init__(self, name: str = None, config: Dict[str, Any] = None, **kwargs):
214
+ super().__init__(name=name or "PositionLimitRisk", config=config, **kwargs)
215
+ self._max_position = self.config.get('max_position', float('inf'))
216
+ self._max_order_size = self.config.get('max_order_size', float('inf'))
217
+ self._min_order_size = self.config.get('min_order_size', 0)
218
+
219
+ def _check_order(
220
+ self,
221
+ order: Order,
222
+ positions: Dict[str, float],
223
+ **kwargs
224
+ ) -> RiskCheck:
225
+ """检查仓位限制"""
226
+ current_position = positions.get(order.code, 0)
227
+ new_position = current_position + order.size
228
+
229
+ if abs(order.size) < self._min_order_size:
230
+ return RiskCheck(
231
+ passed=False,
232
+ order=order,
233
+ reason=f"Order size {abs(order.size)} below minimum {self._min_order_size}"
234
+ )
235
+
236
+ if abs(order.size) > self._max_order_size:
237
+ adjusted_size = np.copysign(self._max_order_size, order.size)
238
+ return RiskCheck(
239
+ passed=True,
240
+ order=order,
241
+ reason=f"Order size adjusted from {abs(order.size)} to {self._max_order_size}",
242
+ adjusted_size=adjusted_size
243
+ )
244
+
245
+ if abs(new_position) > self._max_position:
246
+ return RiskCheck(
247
+ passed=False,
248
+ order=order,
249
+ reason=(
250
+ f"Position limit would be exceeded: {abs(new_position)}"
251
+ f" > {self._max_position}"
252
+ )
253
+ )
254
+
255
+ return RiskCheck(passed=True, order=order)
256
+
257
+
258
+ class StopLossRiskNode(RiskNode):
259
+ """止损风控节点"""
260
+
261
+ def __init__(self, name: str = None, config: Dict[str, Any] = None, **kwargs):
262
+ super().__init__(name=name or "StopLossRisk", config=config, **kwargs)
263
+ self._max_loss = self.config.get('max_loss', float('inf'))
264
+ self._current_pnl = 0.0
265
+
266
+ def _check_order(
267
+ self,
268
+ order: Order,
269
+ positions: Dict[str, float],
270
+ **kwargs
271
+ ) -> RiskCheck:
272
+ """检查止损风控"""
273
+ if order.sl_price is None or order.size >= 0:
274
+ return RiskCheck(passed=True, order=order)
275
+
276
+ if self._current_pnl < -self._max_loss:
277
+ return RiskCheck(
278
+ passed=False,
279
+ order=order,
280
+ reason=f"Stop loss triggered: PnL {self._current_pnl} below limit {-self._max_loss}"
281
+ )
282
+
283
+ return RiskCheck(passed=True, order=order)
284
+
285
+ def update_pnl(self, pnl: float) -> None:
286
+ """更新当前盈亏"""
287
+ self._current_pnl = pnl
288
+
289
+
290
+ class CashRiskNode(RiskNode):
291
+ """现金风控节点"""
292
+
293
+ def __init__(self, name: str = None, config: Dict[str, Any] = None, **kwargs):
294
+ super().__init__(name=name or "CashRisk", config=config, **kwargs)
295
+ self._min_cash = self.config.get('min_cash', 0)
296
+
297
+ def _check_order(
298
+ self,
299
+ order: Order,
300
+ positions: Dict[str, float],
301
+ **kwargs
302
+ ) -> RiskCheck:
303
+ """检查现金限制"""
304
+ cash = kwargs.get('cash', float('inf'))
305
+
306
+ if order.size <= 0:
307
+ return RiskCheck(passed=True, order=order)
308
+
309
+ commission_rate = self._config.get('commission', 0.001)
310
+ estimated_cost = (
311
+ order.size * (order.limit_price or 0) * (1 + commission_rate)
312
+ )
313
+
314
+ if cash - estimated_cost < self._min_cash:
315
+ return RiskCheck(
316
+ passed=False,
317
+ order=order,
318
+ reason=f"Insufficient cash: need ~{estimated_cost}, have {cash}"
319
+ )
320
+
321
+ return RiskCheck(passed=True, order=order)
322
+
323
+
324
+ class CompositeRiskNode(RiskNode):
325
+ """复合风控节点(组合多个风控规则)"""
326
+
327
+ def __init__(
328
+ self,
329
+ name: str = None,
330
+ config: Dict[str, Any] = None,
331
+ risk_nodes: List[RiskNode] = None,
332
+ **kwargs
333
+ ):
334
+ super().__init__(name=name or "CompositeRisk", config=config, **kwargs)
335
+ self._risk_nodes = risk_nodes or []
336
+ self._mode = self.config.get('mode', 'all')
337
+
338
+ def add_risk_node(self, node: RiskNode) -> None:
339
+ """添加风控节点"""
340
+ self._risk_nodes.append(node)
341
+
342
+ def _check_order(
343
+ self,
344
+ order: Order,
345
+ positions: Dict[str, float],
346
+ **kwargs
347
+ ) -> RiskCheck:
348
+ """执行所有风控检查"""
349
+ for risk_node in self._risk_nodes:
350
+ check = risk_node._check_order(order, positions, **kwargs)
351
+ if not check.passed:
352
+ if self._mode == 'all':
353
+ return check
354
+ elif check.adjusted_size is not None:
355
+ order.size = check.adjusted_size
356
+
357
+ if self._mode == 'any':
358
+ return RiskCheck(passed=True, order=order)
359
+
360
+ return RiskCheck(passed=True, order=order)
@@ -0,0 +1,268 @@
1
+ # coding=utf-8
2
+ """
3
+ StrategyNode - 策略节点
4
+
5
+ 提供策略节点的基础架构,继承自 BaseNode。
6
+ """
7
+ from __future__ import annotations
8
+
9
+ from abc import ABC, abstractmethod
10
+ from typing import Any, Dict, List, Optional
11
+ from dataclasses import dataclass
12
+
13
+ import pandas as pd
14
+
15
+ from QuantNodes.core.node import BaseNode
16
+
17
+
18
+ @dataclass
19
+ class Order:
20
+ """订单数据结构"""
21
+ code: str
22
+ size: float
23
+ limit_price: Optional[float] = None
24
+ stop_price: Optional[float] = None
25
+ sl_price: Optional[float] = None
26
+ tp_price: Optional[float] = None
27
+ order_id: Optional[str] = None
28
+ create_date: Optional[str] = None
29
+
30
+
31
+ @dataclass
32
+ class Signal:
33
+ """交易信号数据结构"""
34
+ code: str
35
+ signal_type: str
36
+ strength: float = 1.0
37
+ price: Optional[float] = None
38
+ date: Optional[str] = None
39
+
40
+
41
+ class OrdersResult:
42
+ """订单结果容器"""
43
+ def __init__(self):
44
+ self.orders: List[Order] = []
45
+ self.signals: List[Signal] = []
46
+
47
+ def add_order(self, order: Order) -> None:
48
+ self.orders.append(order)
49
+
50
+ def add_signal(self, signal: Signal) -> None:
51
+ self.signals.append(signal)
52
+
53
+ def to_dataframe(self) -> pd.DataFrame:
54
+ """转换为 DataFrame"""
55
+ if not self.orders:
56
+ return pd.DataFrame()
57
+ return pd.DataFrame([
58
+ {
59
+ 'code': o.code,
60
+ 'size': o.size,
61
+ 'limit_price': o.limit_price,
62
+ 'stop_price': o.stop_price,
63
+ 'sl_price': o.sl_price,
64
+ 'tp_price': o.tp_price,
65
+ 'order_id': o.order_id,
66
+ 'create_date': o.create_date,
67
+ }
68
+ for o in self.orders
69
+ ])
70
+
71
+
72
+ class StrategyNode(BaseNode[pd.DataFrame, OrdersResult], ABC):
73
+ """
74
+ 策略节点基类
75
+
76
+ 提供统一的策略执行接口。
77
+
78
+ Subclasses must implement:
79
+ _generate_signals(): 生成交易信号
80
+ _create_orders(): 根据信号创建订单
81
+
82
+ Examples:
83
+ >>> strategy = MAStrategyNode(config={'short_window': 5, 'long_window': 20})
84
+ >>> result = strategy.execute(data)
85
+ """
86
+
87
+ _enable_validation: bool = True
88
+ _enable_stats: bool = True
89
+
90
+ def __init__(self, name: str = None, config: Dict[str, Any] = None, **kwargs):
91
+ default_name = f"{self.__class__.__name__}"
92
+ super().__init__(name=name or default_name, config=config, **kwargs)
93
+
94
+ self._result: Optional[OrdersResult] = None
95
+ self._signals: List[Signal] = []
96
+ self._orders: List[Order] = []
97
+
98
+ @abstractmethod
99
+ def _generate_signals(
100
+ self,
101
+ input_data: pd.DataFrame,
102
+ **kwargs
103
+ ) -> List[Signal]:
104
+ """
105
+ 生成交易信号
106
+
107
+ Args:
108
+ input_data: 市场数据 DataFrame
109
+ **kwargs: 额外执行参数
110
+
111
+ Returns:
112
+ 信号列表
113
+ """
114
+ pass
115
+
116
+ def _create_orders(
117
+ self,
118
+ signals: List[Signal],
119
+ **kwargs
120
+ ) -> List[Order]:
121
+ """
122
+ 根据信号创建订单(默认实现)
123
+
124
+ Args:
125
+ signals: 信号列表
126
+ **kwargs: 额外执行参数
127
+
128
+ Returns:
129
+ 订单列表
130
+ """
131
+ orders = []
132
+ for signal in signals:
133
+ order = Order(
134
+ code=signal.code,
135
+ size=signal.strength * (1 if signal.signal_type == 'buy' else -1),
136
+ limit_price=signal.price,
137
+ create_date=signal.date,
138
+ )
139
+ orders.append(order)
140
+ return orders
141
+
142
+ def _execute(
143
+ self,
144
+ input_data: pd.DataFrame = None,
145
+ **kwargs
146
+ ) -> OrdersResult:
147
+ """
148
+ 执行策略
149
+
150
+ Args:
151
+ input_data: 市场数据 DataFrame
152
+ **kwargs: 额外执行参数
153
+
154
+ Returns:
155
+ OrdersResult 订单结果
156
+ """
157
+ self._signals = self._generate_signals(input_data, **kwargs)
158
+ self._orders = self._create_orders(self._signals, **kwargs)
159
+
160
+ result = OrdersResult()
161
+ result.signals = self._signals
162
+ result.orders = self._orders
163
+
164
+ self._result = result
165
+ return result
166
+
167
+ def _validate_input(self, input_data: Any) -> None:
168
+ """验证输入数据"""
169
+ if input_data is None:
170
+ return
171
+ if not isinstance(input_data, pd.DataFrame):
172
+ raise ValueError(f"input_data must be DataFrame, got {type(input_data).__name__}")
173
+
174
+ def get_signals(self) -> List[Signal]:
175
+ """获取当前信号"""
176
+ return self._signals
177
+
178
+ def get_orders(self) -> List[Order]:
179
+ """获取当前订单"""
180
+ return self._orders
181
+
182
+
183
+ class MAStrategyNode(StrategyNode):
184
+ """移动平均线策略节点"""
185
+
186
+ def __init__(self, name: str = None, config: Dict[str, Any] = None, **kwargs):
187
+ super().__init__(name=name or "MA_Strategy", config=config, **kwargs)
188
+ self._short_window = self.config.get('short_window', 5)
189
+ self._long_window = self.config.get('long_window', 20)
190
+
191
+ def _generate_signals(self, input_data: pd.DataFrame, **kwargs) -> List[Signal]:
192
+ """计算 MA 并生成信号"""
193
+ if input_data is None or input_data.empty:
194
+ return []
195
+
196
+ df = input_data.copy()
197
+ df['MA_Short'] = df['Close'].rolling(window=self._short_window).mean()
198
+ df['MA_Long'] = df['Close'].rolling(window=self._long_window).mean()
199
+
200
+ signals = []
201
+ codes = df['Code'].unique() if 'Code' in df.columns else [None]
202
+
203
+ for code in codes:
204
+ if code is not None:
205
+ code_df = df[df['Code'] == code].copy()
206
+ else:
207
+ code_df = df.copy()
208
+
209
+ code_df['signal'] = 0
210
+ code_df.loc[code_df['MA_Short'] > code_df['MA_Long'], 'signal'] = 1
211
+ code_df.loc[code_df['MA_Short'] < code_df['MA_Long'], 'signal'] = -1
212
+
213
+ code_df['signal_diff'] = code_df['signal'].diff()
214
+
215
+ for _, row in code_df.iterrows():
216
+ if row.get('signal_diff', 0) != 0:
217
+ signal_type = 'buy' if row['signal'] == 1 else 'sell'
218
+ signals.append(Signal(
219
+ code=code or 'DEFAULT',
220
+ signal_type=signal_type,
221
+ strength=1.0,
222
+ price=row.get('Close'),
223
+ date=str(row.get('date', '')),
224
+ ))
225
+
226
+ return signals
227
+
228
+
229
+ class MomentumStrategyNode(StrategyNode):
230
+ """动量策略节点"""
231
+
232
+ def __init__(self, name: str = None, config: Dict[str, Any] = None, **kwargs):
233
+ super().__init__(name=name or "Momentum_Strategy", config=config, **kwargs)
234
+ self._lookback = self.config.get('lookback', 20)
235
+ self._threshold = self.config.get('threshold', 0.05)
236
+
237
+ def _generate_signals(self, input_data: pd.DataFrame, **kwargs) -> List[Signal]:
238
+ """计算动量并生成信号"""
239
+ if input_data is None or input_data.empty:
240
+ return []
241
+
242
+ df = input_data.copy()
243
+ df['Return'] = df['Close'].pct_change(self._lookback)
244
+
245
+ signals = []
246
+ codes = df['Code'].unique() if 'Code' in df.columns else [None]
247
+
248
+ for code in codes:
249
+ if code is not None:
250
+ code_df = df[df['Code'] == code].copy()
251
+ else:
252
+ code_df = df.copy()
253
+
254
+ code_df = code_df.dropna(subset=['Return'])
255
+
256
+ for _, row in code_df.iterrows():
257
+ ret = row['Return']
258
+ if abs(ret) > self._threshold:
259
+ signal_type = 'buy' if ret > 0 else 'sell'
260
+ signals.append(Signal(
261
+ code=code or 'DEFAULT',
262
+ signal_type=signal_type,
263
+ strength=min(abs(ret) / self._threshold, 2.0),
264
+ price=row.get('Close'),
265
+ date=str(row.get('date', '')),
266
+ ))
267
+
268
+ return signals
@@ -0,0 +1,19 @@
1
+ # coding=utf-8
2
+ """
3
+ cache_node - 行情数据缓存节点
4
+
5
+ 提供 Parquet 文件缓存能力, 支持:
6
+ - 透明代理模式: ConfigBacktestTool 自动调用
7
+ - 独立 Pipeline 节点: YAML 配置驱动
8
+ """
9
+
10
+ from QuantNodes.cache_node.base import MarketDataCacheNode
11
+ from QuantNodes.cache_node.cache_store import ParquetCacheStore
12
+ from QuantNodes.cache_node.metadata import CacheMetadata, CacheMeta
13
+
14
+ __all__ = [
15
+ 'MarketDataCacheNode',
16
+ 'ParquetCacheStore',
17
+ 'CacheMetadata',
18
+ 'CacheMeta',
19
+ ]