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,1115 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ 因子运算模块
4
+
5
+ 提供因子运算操作类,包括单点运算、时间序列运算、截面运算和面板运算。
6
+ v2.0: 移除 traits 和 multiprocessing 依赖,使用纯 Python/Polars
7
+
8
+ Classes:
9
+ DerivativeFactor: 因子运算基类
10
+ PointOperation: 单点运算,对描述子进行单点运算
11
+ TimeOperation: 时间序列运算,对描述子进行时间序列运算(滚动/扩展窗口)
12
+ SectionOperation: 截面运算,对描述子进行截面运算
13
+ PanelOperation: 面板运算,结合时间序列和截面运算
14
+ """
15
+ from __future__ import annotations
16
+
17
+ import numpy as np
18
+ import pandas as pd
19
+ from dataclasses import dataclass, field
20
+ from enum import Enum
21
+ from multiprocessing import Queue, Event
22
+ from typing import Any, Callable, Dict, List, Optional, Tuple
23
+
24
+ from QuantNodes.core.base import FactorError
25
+ from QuantNodes.core.cache_utils import (
26
+ create_empty_dataframe,
27
+ create_std_data,
28
+ partition_ids_for_pid,
29
+ write_cache_file,
30
+ write_cache_files_for_all_pids,
31
+ )
32
+ from QuantNodes.core.tools import partition_list, partition_list_moving_sampling
33
+ from QuantNodes.factor_node.factor import Factor
34
+
35
+
36
+ def _DefaultOperator(f: Factor, idt: Any, iid: Any, x: List[np.ndarray], args: Dict[str, Any]) -> np.ndarray:
37
+ """默认算子,返回 NaN"""
38
+ return np.nan
39
+
40
+
41
+ class DataOperationType(Enum):
42
+ """数据类型枚举"""
43
+ DOUBLE = "double"
44
+ STRING = "string"
45
+ OBJECT = "object"
46
+
47
+
48
+ @dataclass
49
+ class DerivativeFactor(Factor):
50
+ """因子运算基类
51
+
52
+ 所有因子运算类的基类,提供描述子管理和通用接口。
53
+
54
+ Attributes:
55
+ Operator: 运算函数,签名为 (f, idt, iid, x, args) -> result
56
+ ModelArgs: 参数字典
57
+ DataType: 数据类型
58
+ """
59
+ operator: Callable = field(default=_DefaultOperator)
60
+ model_args: Dict[str, Any] = field(default_factory=dict)
61
+ data_type: DataOperationType = DataOperationType.DOUBLE
62
+ _descriptors: List[Factor] = field(default_factory=list)
63
+
64
+ def __init__(self, name: str = "", descriptors: List[Factor] = None, sys_args: Dict[str, Any] = None, **kwargs):
65
+ self._Descriptors = descriptors if descriptors else []
66
+ self.UserData = {}
67
+ self.model_args = sys_args or {}
68
+ self.operator = kwargs.get('operator', _DefaultOperator)
69
+ if descriptors:
70
+ kwargs.setdefault("logger", descriptors[0]._QN_logger)
71
+ return super().__init__(name=name, ft=None, sys_args=sys_args or {}, config_file=None, **kwargs)
72
+
73
+ @property
74
+ def Descriptors(self) -> List[Factor]:
75
+ """描述子列表"""
76
+ return self._Descriptors
77
+
78
+ @property
79
+ def Operator(self) -> Callable:
80
+ return self.operator
81
+
82
+ @Operator.setter
83
+ def Operator(self, value: Callable):
84
+ self.operator = value
85
+
86
+ @property
87
+ def ModelArgs(self) -> Dict[str, Any]:
88
+ return self.model_args
89
+
90
+ @property
91
+ def DataType(self) -> DataOperationType:
92
+ return self.data_type
93
+
94
+ def getMetaData(self, key: str = None, args: Dict[str, Any] = None) -> Any:
95
+ """获取元数据
96
+
97
+ Args:
98
+ key: 元数据键名,None时返回包含DataType的Series
99
+ args: 参数字典
100
+
101
+ Returns:
102
+ 元数据值或包含元数据的Series
103
+ """
104
+ DataType = args.get("数据类型", self.DataType) if args else self.DataType
105
+ if key is None:
106
+ return pd.Series({"DataType": DataType})
107
+ elif key == "DataType":
108
+ return DataType
109
+ return None
110
+
111
+ def start(self, dts: List[Any], **kwargs) -> int:
112
+ """开始运算前的初始化
113
+
114
+ Args:
115
+ dts: 时间点列表
116
+ **kwargs: 其他关键字参数
117
+
118
+ Returns:
119
+ 成功返回0
120
+ """
121
+ for iDescriptor in self._Descriptors:
122
+ iDescriptor.start(dts=dts, **kwargs)
123
+ return 0
124
+
125
+ def end(self) -> int:
126
+ """运算结束后的清理
127
+
128
+ Returns:
129
+ 成功返回0
130
+ """
131
+ for iDescriptor in self._Descriptors:
132
+ iDescriptor.end()
133
+ return 0
134
+
135
+
136
+ # 单点运算
137
+ # f: 该算子所属的因子, 因子对象
138
+ # idt: 当前待计算的时点, 如果运算时点为多时点,则该值为[时点]
139
+ # iid: 当前待计算的ID, 如果运算ID为多ID,则该值为 [ID]
140
+ # x: 描述子当期的数据, [单个描述子值 or array]
141
+ # args: 参数, {参数名:参数值}
142
+ # 如果运算时点参数为单时点, 运算ID参数为单ID, 那么 x 元素为单个描述子值, 返回单个元素
143
+ # 如果运算时点参数为单时点, 运算ID参数为多ID, 那么 x 元素为 array(shape=(nID, )), 注意并发时 ID 并不是全截面, 返回 array(shape=(nID,))
144
+ # 如果运算时点参数为多时点, 运算ID参数为单ID, 那么 x 元素为 array(shape=(nDT, )), 返回 array(shape=(nID, ))
145
+ # 如果运算时点参数为多时点, 运算ID参数为多ID, 那么 x 元素为 array(shape=(nDT, nID)), 注意并发时 ID 并不是全截面, 返回 array(shape=(nDT, nID))
146
+ class DTModeType(Enum):
147
+ """运算时点模式"""
148
+ SINGLE = "单时点"
149
+ MULTI = "多时点"
150
+
151
+
152
+ class IDModeType(Enum):
153
+ """运算ID模式"""
154
+ SINGLE = "单ID"
155
+ MULTI = "多ID"
156
+
157
+
158
+ @dataclass
159
+ class PointOperation(DerivativeFactor):
160
+ """单点运算
161
+
162
+ 对描述子进行单点运算,即每个时点-ID组合独立计算。
163
+
164
+ Attributes:
165
+ dt_mode: 运算时点模式
166
+ id_mode: 运算ID模式
167
+ """
168
+ dt_mode: DTModeType = DTModeType.SINGLE
169
+ id_mode: IDModeType = IDModeType.SINGLE
170
+
171
+ _DT_ID_DISPATCH = {
172
+ (DTModeType.MULTI, IDModeType.MULTI): "_calcData_multi_time_multi_id",
173
+ (DTModeType.SINGLE, IDModeType.SINGLE): "_calcData_single_time_single_id",
174
+ (DTModeType.MULTI, IDModeType.SINGLE): "_calcData_multi_time_single_id",
175
+ (DTModeType.SINGLE, IDModeType.MULTI): "_calcData_single_time_multi_id",
176
+ }
177
+
178
+ @property
179
+ def DTMode(self) -> DTModeType:
180
+ return self.dt_mode
181
+
182
+ @property
183
+ def IDMode(self) -> IDModeType:
184
+ return self.id_mode
185
+
186
+ def readData(self, ids: List[Any], dts: List[Any], **kwargs) -> pd.DataFrame:
187
+ """读取并计算数据
188
+
189
+ Args:
190
+ ids: ID列表
191
+ dts: 时间点列表
192
+ **kwargs: 其他关键字参数
193
+
194
+ Returns:
195
+ 计算结果DataFrame,index为dts,columns为ids
196
+ """
197
+ if len(dts) == 0:
198
+ return create_empty_dataframe(dts, ids, self.DataType)
199
+ StdData = self._calcData(
200
+ ids=ids,
201
+ dts=dts,
202
+ descriptor_data=[iDescriptor.readData(ids=ids, dts=dts, **kwargs).values
203
+ for iDescriptor in self._Descriptors]
204
+ )
205
+ return pd.DataFrame(StdData, index=dts, columns=ids)
206
+
207
+ def _QN_init_operation(self, start_dt: Any, dt_dict: Dict[str, Any], prepare_ids: List[Any], id_dict: Dict[str, List[Any]]) -> None:
208
+ """初始化运算环境
209
+
210
+ Args:
211
+ start_dt: 起始时间点
212
+ dt_dict: 时间点字典
213
+ prepare_ids: 准备计算的ID列表
214
+ id_dict: ID字典
215
+ """
216
+ super()._QN_init_operation(start_dt, dt_dict, prepare_ids, id_dict)
217
+ for i, iDescriptor in enumerate(self._Descriptors):
218
+ iDescriptor._QN_init_operation(dt_dict[self.Name], dt_dict, prepare_ids, id_dict)
219
+
220
+ def _calcData(self, ids: List[Any], dts: List[Any], descriptor_data: List[np.ndarray]) -> np.ndarray:
221
+ """计算数据(策略分派入口)
222
+
223
+ Args:
224
+ ids: ID列表
225
+ dts: 时间点列表
226
+ descriptor_data: 描述子数据列表
227
+
228
+ Returns:
229
+ 计算结果数组
230
+ """
231
+ handler_name = self._DT_ID_DISPATCH.get((self.DTMode, self.IDMode))
232
+ if handler_name:
233
+ return getattr(self, handler_name)(ids, dts, descriptor_data)
234
+ return create_std_data(dts, ids, self.DataType)
235
+
236
+ def _calcData_multi_time_multi_id(self, ids: List[Any], dts: List[Any], descriptor_data: List[np.ndarray]) -> np.ndarray:
237
+ """多时点-多ID模式计算"""
238
+ return self.Operator(self, dts, ids, descriptor_data, self.ModelArgs)
239
+
240
+ def _calcData_single_time_single_id(self, ids: List[Any], dts: List[Any], descriptor_data: List[np.ndarray]) -> np.ndarray:
241
+ """单时点-单ID模式计算
242
+
243
+ Args:
244
+ ids: ID列表
245
+ dts: 时间点列表
246
+ descriptor_data: 描述子数据列表
247
+
248
+ Returns:
249
+ 计算结果数组
250
+ """
251
+ StdData = create_std_data(dts, ids, self.DataType)
252
+ for i, iDT in enumerate(dts):
253
+ for j, jID in enumerate(ids):
254
+ StdData[i, j] = self.Operator(
255
+ self, iDT, jID,
256
+ [iData[i, j] for iData in descriptor_data],
257
+ self.ModelArgs
258
+ )
259
+ return StdData
260
+
261
+ def _calcData_multi_time_single_id(self, ids: List[Any], dts: List[Any], descriptor_data: List[np.ndarray]) -> np.ndarray:
262
+ """多时点-单ID模式计算
263
+
264
+ Args:
265
+ ids: ID列表
266
+ dts: 时间点列表
267
+ descriptor_data: 描述子数据列表
268
+
269
+ Returns:
270
+ 计算结果数组
271
+ """
272
+ StdData = create_std_data(dts, ids, self.DataType)
273
+ for j, jID in enumerate(ids):
274
+ StdData[:, j] = self.Operator(
275
+ self, dts, jID,
276
+ [iData[:, j] for iData in descriptor_data],
277
+ self.ModelArgs
278
+ )
279
+ return StdData
280
+
281
+ def _calcData_single_time_multi_id(self, ids: List[Any], dts: List[Any], descriptor_data: List[np.ndarray]) -> np.ndarray:
282
+ """单时点-多ID模式计算
283
+
284
+ Args:
285
+ ids: ID列表
286
+ dts: 时间点列表
287
+ descriptor_data: 描述子数据列表
288
+
289
+ Returns:
290
+ 计算结果数组
291
+ """
292
+ StdData = create_std_data(dts, ids, self.DataType)
293
+ for i, iDT in enumerate(dts):
294
+ StdData[i, :] = self.Operator(
295
+ self, iDT, ids,
296
+ [iData[i, :] for iData in descriptor_data],
297
+ self.ModelArgs
298
+ )
299
+ return StdData
300
+
301
+ def __QN_prepare_cache_data__(self, ids: Optional[List[Any]] = None) -> pd.DataFrame:
302
+ """准备缓存数据
303
+
304
+ Args:
305
+ ids: ID列表
306
+
307
+ Returns:
308
+ 标准数据DataFrame
309
+ """
310
+ PID = self._OperationMode._iPID
311
+ StartDT = self._OperationMode._FactorStartDT[self.Name]
312
+ EndDT = self._OperationMode.DateTimes[-1]
313
+ StartInd, EndInd = (
314
+ self._OperationMode.DTRuler.index(StartDT),
315
+ self._OperationMode.DTRuler.index(EndDT)
316
+ )
317
+ DTs = list(self._OperationMode.DTRuler[StartInd:EndInd + 1])
318
+ IDs = partition_ids_for_pid(self._OperationMode, self._OperationMode._FactorPrepareIDs[self.Name], PID)
319
+ if IDs:
320
+ StdData = self._calcData(
321
+ ids=IDs, dts=DTs,
322
+ descriptor_data=[iDescriptor._QN_get_data(DTs, pids=[PID]).values
323
+ for iDescriptor in self._Descriptors]
324
+ )
325
+ StdData = pd.DataFrame(StdData, index=DTs, columns=IDs)
326
+ else:
327
+ StdData = create_empty_dataframe(DTs, [], self.DataType)
328
+ write_cache_file(
329
+ self._OperationMode, PID, self.Name,
330
+ self._OperationMode._FactorID[self.Name], StdData, IDs
331
+ )
332
+ self._isCacheDataOK = True
333
+ return StdData
334
+
335
+
336
+ class LookBackMode(Enum):
337
+ """回溯模式"""
338
+ ROLLING = "滚动窗口"
339
+ EXPANDING = "扩张窗口"
340
+
341
+
342
+ @dataclass
343
+ class _LookBackOperation(DerivativeFactor):
344
+ """带 LookBack 窗口运算的基类
345
+
346
+ 提取 TimeOperation 和 PanelOperation 中相同的 LookBack 窗口计算逻辑。
347
+ 子类需要实现具体的 _calcData 方法或使用策略分派模式。
348
+
349
+ Attributes:
350
+ look_back: 回溯期数列表,对应每个描述子
351
+ look_back_mode: 回溯模式列表
352
+ i_look_back: 自身回溯期数
353
+ i_look_back_mode: 自身回溯模式
354
+ i_init_data: 自身初始值DataFrame
355
+ """
356
+ look_back: List[int] = field(default_factory=list)
357
+ look_back_mode: List[LookBackMode] = field(default_factory=list)
358
+ i_look_back: int = 0
359
+ i_look_back_mode: LookBackMode = LookBackMode.ROLLING
360
+ i_init_data: Optional[pd.DataFrame] = None
361
+
362
+ def __init__(self, name: str = "", descriptors: List[Factor] = None, sys_args: Dict = None, **kwargs):
363
+ super().__init__(name=name, descriptors=descriptors, sys_args=sys_args, **kwargs)
364
+ self._init_lookback()
365
+
366
+ def _init_lookback(self) -> None:
367
+ """初始化回溯参数"""
368
+ n = len(self._Descriptors)
369
+ self.look_back = [0] * n
370
+ self.look_back_mode = [LookBackMode.ROLLING] * n
371
+
372
+ def _prepare_lookback_data(
373
+ self,
374
+ ids: List[Any],
375
+ dts: List[Any],
376
+ descriptor_data: List[np.ndarray],
377
+ dt_ruler: List[Any]
378
+ ) -> Tuple[np.ndarray, int, List[Any], List[Tuple[int, int]], int, int, List[np.ndarray]]:
379
+ """准备 LookBack 窗口数据
380
+
381
+ 计算窗口参数、处理初始数据、扩展时间标尺。
382
+
383
+ Args:
384
+ ids: ID列表
385
+ dts: 时间点列表
386
+ descriptor_data: 描述子数据列表
387
+ dt_ruler: 时间标尺列表
388
+
389
+ Returns:
390
+ tuple: (StdData, iStartInd, DTRuler, StartIndAndLen, MaxLookBack, MaxLen, descriptor_data)
391
+ - StdData: 预分配的结果数组
392
+ - iStartInd: 初始数据起始索引
393
+ - DTRuler: 扩展后的时间标尺
394
+ - StartIndAndLen: 每个描述子的(起始索引, 窗口长度)列表
395
+ - MaxLookBack: 最大回溯期数
396
+ - MaxLen: 最大窗口长度
397
+ - descriptor_data: 处理后的描述子数据
398
+ """
399
+ StdData = create_std_data(dts, ids, self.DataType)
400
+ StartIndAndLen, MaxLookBack, MaxLen = [], 0, 1
401
+ for i in range(len(self._Descriptors)):
402
+ iLookBack = self.look_back[i]
403
+ if self.look_back_mode[i] == "滚动窗口":
404
+ StartIndAndLen.append((iLookBack, iLookBack + 1))
405
+ MaxLen = max(MaxLen, iLookBack + 1)
406
+ else:
407
+ StartIndAndLen.append((iLookBack, np.inf))
408
+ MaxLen = np.inf
409
+ MaxLookBack = max(MaxLookBack, iLookBack)
410
+ iStartInd = 0
411
+ if (self.i_look_back_mode == LookBackMode.EXPANDING) or (self.i_look_back != 0):
412
+ if self.i_init_data is not None:
413
+ iInitData = self.i_init_data.loc[self.i_init_data.index < dts[0], :]
414
+ if iInitData.shape[0] > 0:
415
+ if iInitData.columns.intersection(ids).shape[0] > 0:
416
+ iInitData = iInitData.loc[:, ids].values.astype(StdData.dtype)
417
+ else:
418
+ iInitData = np.full(shape=(iInitData.shape[0], len(ids)), dtype=StdData.dtype)
419
+ iStartInd = min(self.i_look_back, iInitData.shape[0])
420
+ StdData = np.r_[iInitData[-iStartInd:], StdData]
421
+ if self.i_look_back_mode == LookBackMode.EXPANDING:
422
+ StartIndAndLen.insert(0, (iStartInd - 1, np.inf))
423
+ MaxLen = np.inf
424
+ else:
425
+ StartIndAndLen.insert(0, (iStartInd - 1, self.i_look_back))
426
+ MaxLen = max(MaxLen, self.i_look_back + 1)
427
+ MaxLookBack = max(MaxLookBack, self.i_look_back)
428
+ descriptor_data.insert(0, StdData)
429
+ start_ind = dt_ruler.index(dts[0])
430
+ if start_ind >= MaxLookBack:
431
+ DTRuler = dt_ruler[start_ind - MaxLookBack:]
432
+ else:
433
+ DTRuler = [None] * (MaxLookBack - start_ind) + dt_ruler
434
+ return StdData, iStartInd, DTRuler, StartIndAndLen, MaxLookBack, MaxLen, descriptor_data
435
+
436
+
437
+ # 时间序列运算
438
+ # f: 该算子所属的因子, 因子对象
439
+ # idt: 当前待计算的时点, 如果运算日期为多时点,则该值为 [时点]
440
+ # iid: 当前待计算的ID, 如果运算ID为多ID,则该值为 [ID]
441
+ # x: 描述子当期的数据, [array]
442
+ # args: 参数, {参数名:参数值}
443
+ # 如果运算时点参数为单时点, 运算ID参数为单ID, 那么x元素为array(shape=(回溯期数, )), 返回单个元素
444
+ # 如果运算时点参数为单时点, 运算ID参数为多ID, 那么x元素为array(shape=(回溯期数, nID)), 注意并发时 ID 并不是全截面, 返回 array(shape=(nID, ))
445
+ # 如果运算时点参数为多时点, 运算ID参数为单ID, 那么x元素为array(shape=(回溯期数+nDT, )), 返回 array(shape=(nDate,))
446
+ # 如果运算时点参数为多时点, 运算ID参数为多ID, 那么x元素为array(shape=(回溯期数+nDT, nID)), 注意并发时 ID 并不是全截面, 返回 array(shape=(nDT, nID))
447
+ class OutputModeType(Enum):
448
+ """输出模式"""
449
+ FULL_SECTION = "全截面"
450
+ SINGLE_ID = "单ID"
451
+
452
+
453
+ @dataclass
454
+ class TimeOperation(_LookBackOperation):
455
+ """时间序列运算"""
456
+ dt_mode: DTModeType = DTModeType.SINGLE
457
+ id_mode: IDModeType = IDModeType.SINGLE
458
+
459
+ _DT_ID_DISPATCH = {
460
+ (DTModeType.SINGLE, IDModeType.SINGLE): "_calcData_single_time_single_id",
461
+ (DTModeType.SINGLE, IDModeType.MULTI): "_calcData_single_time_multi_id",
462
+ (DTModeType.MULTI, IDModeType.SINGLE): "_calcData_multi_time_single_id",
463
+ (DTModeType.MULTI, IDModeType.MULTI): "_calcData_multi_time_multi_id",
464
+ }
465
+
466
+ @property
467
+ def DTMode(self) -> DTModeType:
468
+ return self.dt_mode
469
+
470
+ @property
471
+ def IDMode(self) -> IDModeType:
472
+ return self.id_mode
473
+
474
+ def _QN_init_operation(
475
+ self,
476
+ start_dt: Any,
477
+ dt_dict: Dict[str, Any],
478
+ prepare_ids: List[Any],
479
+ id_dict: Dict[str, List[Any]]
480
+ ) -> None:
481
+ """初始化运算环境
482
+
483
+ Args:
484
+ start_dt: 起始时间点
485
+ dt_dict: 时间点字典
486
+ prepare_ids: 准备计算的ID列表
487
+ id_dict: ID字典
488
+ """
489
+ super(_LookBackOperation, self)._QN_init_operation(start_dt, dt_dict, prepare_ids, id_dict)
490
+ if len(self._Descriptors) > len(self.look_back):
491
+ raise FactorError(
492
+ "时间序列运算因子 : '%s' 的参数'回溯期数'序列长度小于描述子个数!" % self.Name
493
+ )
494
+ StartDT = dt_dict[self.Name]
495
+ StartInd = self._OperationMode.DTRuler.index(StartDT)
496
+ if (self.i_look_back_mode == "扩张窗口") and (self.i_init_data is not None) and (self.i_init_data.shape[0] > 0):
497
+ if self.i_init_data.index[-1] not in self._OperationMode.DTRuler:
498
+ self._QN_logger.warning(
499
+ "注意: 因子 '%s' 的初始值不在时点标尺的范围内, 初始值和时点标尺之间的时间间隔将被忽略!" % (self.Name,)
500
+ )
501
+ else:
502
+ StartInd = min(StartInd, self._OperationMode.DTRuler.index(self.i_init_data.index[-1]) + 1)
503
+ for i, iDescriptor in enumerate(self._Descriptors):
504
+ iStartInd = StartInd - self.look_back[i]
505
+ if iStartInd < 0:
506
+ self._QN_logger.warning(
507
+ "注意: 对于因子 '%s' 的描述子 '%s', 时点标尺长度不足, 不足的部分将填充 nan!" % (self.Name, iDescriptor.Name)
508
+ )
509
+ iStartDT = self._OperationMode.DTRuler[max(0, iStartInd)]
510
+ iDescriptor._QN_init_operation(iStartDT, dt_dict, prepare_ids, id_dict)
511
+
512
+ def readData(self, ids: List[Any], dts: List[Any], **kwargs) -> pd.DataFrame:
513
+ """读取并计算数据
514
+
515
+ Args:
516
+ ids: ID列表
517
+ dts: 时间点列表
518
+ **kwargs: 其他关键字参数,支持 dt_ruler 指定时间标尺
519
+
520
+ Returns:
521
+ 计算结果DataFrame,index为dts,columns为ids
522
+ """
523
+ if len(dts) == 0:
524
+ return create_empty_dataframe(dts, ids, self.DataType)
525
+ DTRuler = kwargs.get("dt_ruler", dts)
526
+ StartInd = (DTRuler.index(dts[0]) if dts[0] in DTRuler else 0)
527
+ if (self.i_look_back_mode == "扩张窗口") and (self.i_init_data is not None) and (self.i_init_data.shape[0] > 0):
528
+ if self.i_init_data.index[-1] not in DTRuler:
529
+ self._QN_logger.warning("注意: 因子 '%s' 的初始值不在时点标尺的范围内, 初始值和时点标尺之间的时间间隔将被忽略!" % (self.Name,))
530
+ else:
531
+ StartInd = min(StartInd, DTRuler.index(self.i_init_data.index[-1]) + 1)
532
+ EndInd = (DTRuler.index(dts[-1]) if dts[-1] in DTRuler else len(DTRuler) - 1)
533
+ if StartInd > EndInd:
534
+ return pd.DataFrame(index=dts, columns=ids)
535
+ nID = len(ids)
536
+ DescriptorData = []
537
+ for i, iDescriptor in enumerate(self._Descriptors):
538
+ iDTs = DTRuler[max(StartInd - self.look_back[i], 0):EndInd + 1]
539
+ if iDTs:
540
+ iDescriptorData = iDescriptor.readData(ids=ids, dts=iDTs, **kwargs).values
541
+ else:
542
+ iDescriptorData = np.full((0, nID), np.nan)
543
+ if StartInd < self.look_back[i]:
544
+ iLookBackData = np.full((self.look_back[i] - StartInd, nID), np.nan)
545
+ iDescriptorData = np.r_[iLookBackData, iDescriptorData]
546
+ DescriptorData.append(iDescriptorData)
547
+ StdData = self._calcData(
548
+ ids=ids,
549
+ dts=DTRuler[StartInd:EndInd + 1],
550
+ descriptor_data=DescriptorData,
551
+ dt_ruler=DTRuler
552
+ )
553
+ return pd.DataFrame(StdData, index=DTRuler[StartInd:EndInd + 1], columns=ids).loc[dts, :]
554
+
555
+ def _calcData(
556
+ self,
557
+ ids: List[Any],
558
+ dts: List[Any],
559
+ descriptor_data: List[np.ndarray],
560
+ dt_ruler: List[Any]
561
+ ) -> np.ndarray:
562
+ """计算数据(策略分派入口)
563
+
564
+ Args:
565
+ ids: ID列表
566
+ dts: 时间点列表
567
+ descriptor_data: 描述子数据列表
568
+ dt_ruler: 时间标尺
569
+
570
+ Returns:
571
+ 计算结果数组
572
+ """
573
+ StdData, iStartInd, DTRuler, StartIndAndLen, MaxLookBack, MaxLen, descriptor_data = \
574
+ self._prepare_lookback_data(ids, dts, descriptor_data, dt_ruler)
575
+ handler_name = self._DT_ID_DISPATCH.get((self.DTMode, self.IDMode))
576
+ if handler_name:
577
+ return getattr(self, handler_name)(
578
+ StdData, iStartInd, DTRuler, StartIndAndLen, MaxLookBack, MaxLen,
579
+ ids, dts, descriptor_data
580
+ )
581
+ return self.Operator(self, DTRuler, ids, descriptor_data, self.ModelArgs)
582
+
583
+ def _calcData_single_time_single_id(
584
+ self,
585
+ StdData: np.ndarray,
586
+ iStartInd: int,
587
+ DTRuler: List[Any],
588
+ StartIndAndLen: List[Tuple[int, int]],
589
+ MaxLookBack: int,
590
+ MaxLen: int,
591
+ ids: List[Any],
592
+ dts: List[Any],
593
+ descriptor_data: List[np.ndarray]
594
+ ) -> np.ndarray:
595
+ """单时点-单ID模式计算"""
596
+ for i, iDT in enumerate(dts):
597
+ iDTs = DTRuler[max(0, MaxLookBack + i + 1 - MaxLen):i + 1 + MaxLookBack]
598
+ for j, jID in enumerate(ids):
599
+ x = []
600
+ for k, kDescriptorData in enumerate(descriptor_data):
601
+ kStartInd, kLen = StartIndAndLen[k]
602
+ x.append(kDescriptorData[max(0, kStartInd + 1 + i - kLen):kStartInd + 1 + i, j])
603
+ StdData[iStartInd + i, j] = self.Operator(self, iDTs, jID, x, self.ModelArgs)
604
+ return StdData[iStartInd:, :]
605
+
606
+ def _calcData_single_time_multi_id(
607
+ self,
608
+ StdData: np.ndarray,
609
+ iStartInd: int,
610
+ DTRuler: List[Any],
611
+ StartIndAndLen: List[Tuple[int, int]],
612
+ MaxLookBack: int,
613
+ MaxLen: int,
614
+ ids: List[Any],
615
+ dts: List[Any],
616
+ descriptor_data: List[np.ndarray]
617
+ ) -> np.ndarray:
618
+ """单时点-多ID模式计算"""
619
+ for i, iDT in enumerate(dts):
620
+ iDTs = DTRuler[max(0, MaxLookBack + i + 1 - MaxLen):i + 1 + MaxLookBack]
621
+ x = []
622
+ for k, kDescriptorData in enumerate(descriptor_data):
623
+ kStartInd, kLen = StartIndAndLen[k]
624
+ x.append(kDescriptorData[max(0, kStartInd + 1 + i - kLen):kStartInd + 1 + i])
625
+ StdData[iStartInd + i, :] = self.Operator(self, iDTs, ids, x, self.ModelArgs)
626
+ return StdData[iStartInd:, :]
627
+
628
+ def _calcData_multi_time_single_id(
629
+ self,
630
+ StdData: np.ndarray,
631
+ iStartInd: int,
632
+ DTRuler: List[Any],
633
+ StartIndAndLen: List[Tuple[int, int]],
634
+ MaxLookBack: int,
635
+ MaxLen: int,
636
+ ids: List[Any],
637
+ dts: List[Any],
638
+ descriptor_data: List[np.ndarray]
639
+ ) -> np.ndarray:
640
+ """多时点-单ID模式计算"""
641
+ for j, jID in enumerate(ids):
642
+ StdData[iStartInd:, j] = self.Operator(
643
+ self, DTRuler, jID,
644
+ [kDescriptorData[:, j] for kDescriptorData in descriptor_data],
645
+ self.ModelArgs
646
+ )
647
+ return StdData[iStartInd:, :]
648
+
649
+ def _calcData_multi_time_multi_id(
650
+ self,
651
+ StdData: np.ndarray,
652
+ iStartInd: int,
653
+ DTRuler: List[Any],
654
+ StartIndAndLen: List[Tuple[int, int]],
655
+ MaxLookBack: int,
656
+ MaxLen: int,
657
+ ids: List[Any],
658
+ dts: List[Any],
659
+ descriptor_data: List[np.ndarray]
660
+ ) -> np.ndarray:
661
+ """多时点-多ID模式计算"""
662
+ return self.Operator(self, DTRuler, ids, descriptor_data, self.ModelArgs)
663
+
664
+ def __QN_prepare_cache_data__(self, ids: Optional[List[Any]] = None) -> pd.DataFrame:
665
+ """准备缓存数据
666
+
667
+ Args:
668
+ ids: ID列表
669
+
670
+ Returns:
671
+ 标准数据DataFrame
672
+ """
673
+ PID = self._OperationMode._iPID
674
+ StartDT = self._OperationMode._FactorStartDT[self.Name]
675
+ EndDT = self._OperationMode.DateTimes[-1]
676
+ StartInd, EndInd = (
677
+ self._OperationMode.DTRuler.index(StartDT),
678
+ self._OperationMode.DTRuler.index(EndDT)
679
+ )
680
+ DTs = list(self._OperationMode.DTRuler[StartInd:EndInd + 1])
681
+ IDs = partition_ids_for_pid(self._OperationMode, self._OperationMode._FactorPrepareIDs[self.Name], PID)
682
+ if IDs:
683
+ DescriptorData = []
684
+ for i, iDescriptor in enumerate(self._Descriptors):
685
+ iStartInd = StartInd - self.look_back[i]
686
+ iDTs = list(self._OperationMode.DTRuler[max(0, iStartInd):StartInd]) + DTs
687
+ iDescriptorData = iDescriptor._QN_get_data(iDTs, pids=[PID]).values
688
+ if iStartInd < 0:
689
+ iDescriptorData = np.r_[
690
+ np.full(shape=(abs(iStartInd), iDescriptorData.shape[1]), fill_value=np.nan),
691
+ iDescriptorData
692
+ ]
693
+ DescriptorData.append(iDescriptorData)
694
+ StdData = self._calcData(
695
+ ids=IDs, dts=DTs, descriptor_data=DescriptorData,
696
+ dt_ruler=self._OperationMode.DTRuler
697
+ )
698
+ StdData = pd.DataFrame(StdData, index=DTs, columns=IDs)
699
+ else:
700
+ StdData = create_empty_dataframe(DTs, [], self.DataType)
701
+ write_cache_file(
702
+ self._OperationMode, PID, self.Name,
703
+ self._OperationMode._FactorID[self.Name], StdData, IDs
704
+ )
705
+ self._isCacheDataOK = True
706
+ return StdData
707
+
708
+
709
+ # 截面运算
710
+ # f: 该算子所属的因子, 因子对象
711
+ # idt: 当前待计算的时点, 如果运算日期为多时点,则该值为 [时点]
712
+ # iid: 当前待计算的ID, 如果输出形式为全截面, 则该值为 [ID], 该序列在并发时也是全体截面 ID
713
+ # x: 描述子当期的数据, [array]
714
+ # args: 参数, {参数名:参数值}
715
+ # 如果运算时点参数为单时点, 那么 x 元素为 array(shape=(nID, )), 如果输出形式为全截面返回 array(shape=(nID, )), 否则返回单个值
716
+ # 如果运算时点参数为多时点, 那么 x 元素为 array(shape=(nDT, nID)), 如果输出形式为全截面返回 array(shape=(nDT, nID)), 否则返回 array(shape=(nDT, ))
717
+ class SectionOperation(DerivativeFactor):
718
+ """截面运算
719
+
720
+ 对描述子进行截面运算,即在同一时点对全截面ID进行计算。
721
+
722
+ Attributes:
723
+ dt_mode: 运算时点模式
724
+ output_mode: 输出形式
725
+ descriptor_section: 描述子截面列表
726
+ """
727
+ dt_mode: DTModeType = DTModeType.SINGLE
728
+ output_mode: OutputModeType = OutputModeType.FULL_SECTION
729
+ descriptor_section: List = None
730
+
731
+ _OUTPUT_DT_DISPATCH = {
732
+ (OutputModeType.FULL_SECTION, DTModeType.SINGLE): "_calcData_full_section_single_time",
733
+ (OutputModeType.FULL_SECTION, DTModeType.MULTI): "_calcData_full_section_multi_time",
734
+ (OutputModeType.SINGLE_ID, DTModeType.SINGLE): "_calcData_single_id_single_time",
735
+ (OutputModeType.SINGLE_ID, DTModeType.MULTI): "_calcData_single_id_multi_time",
736
+ }
737
+
738
+ @property
739
+ def DTMode(self) -> DTModeType:
740
+ return self.dt_mode
741
+
742
+ @property
743
+ def OutputMode(self) -> OutputModeType:
744
+ return self.output_mode
745
+
746
+ def __init__(self, name: str = "", descriptors: List[Factor] = None, sys_args: Dict = None, **kwargs):
747
+ super().__init__(name=name, descriptors=descriptors, sys_args=sys_args, **kwargs)
748
+ if descriptors:
749
+ self.descriptor_section = [None] * len(descriptors)
750
+
751
+ def readData(self, ids: List[Any], dts: List[Any], **kwargs) -> pd.DataFrame:
752
+ """读取并计算数据
753
+
754
+ Args:
755
+ ids: ID列表
756
+ dts: 时间点列表
757
+ **kwargs: 其他关键字参数,支持 section_ids 指定截面ID
758
+
759
+ Returns:
760
+ 计算结果DataFrame,index为dts,columns为ids
761
+ """
762
+ SectionIDs = kwargs.pop("section_ids", ids)
763
+ DescriptorData = []
764
+ for i, iDescriptor in enumerate(self._Descriptors):
765
+ iSectionIDs = self.descriptor_section[i]
766
+ if iSectionIDs is None:
767
+ iSectionIDs = SectionIDs
768
+ DescriptorData.append(iDescriptor.readData(ids=iSectionIDs, dts=dts, **kwargs).values)
769
+ StdData = self._calcData(ids=SectionIDs, dts=dts, descriptor_data=DescriptorData)
770
+ return pd.DataFrame(StdData, index=dts, columns=SectionIDs).loc[:, ids]
771
+
772
+ def _QN_init_operation(
773
+ self,
774
+ start_dt: Any,
775
+ dt_dict: Dict[str, Any],
776
+ prepare_ids: List[Any],
777
+ id_dict: Dict[str, List[Any]]
778
+ ) -> None:
779
+ """初始化运算环境
780
+
781
+ Args:
782
+ start_dt: 起始时间点
783
+ dt_dict: 时间点字典
784
+ prepare_ids: 准备计算的ID列表
785
+ id_dict: ID字典
786
+ """
787
+ OldStartDT = dt_dict.get(self.Name, None)
788
+ if (OldStartDT is None) or (start_dt < OldStartDT):
789
+ dt_dict[self.Name] = start_dt
790
+ StartInd, EndInd = (
791
+ self._OperationMode.DTRuler.index(dt_dict[self.Name]),
792
+ self._OperationMode.DTRuler.index(self._OperationMode.DateTimes[-1])
793
+ )
794
+ DTs = self._OperationMode.DTRuler[StartInd:EndInd + 1]
795
+ DTPartition = partition_list(DTs, len(self._OperationMode._PIDs))
796
+ self._PID_DTs = {iPID: DTPartition[i] for i, iPID in enumerate(self._OperationMode._PIDs)}
797
+ PrepareIDs = id_dict.setdefault(self.Name, prepare_ids)
798
+ if prepare_ids != PrepareIDs:
799
+ raise FactorError("因子 %s 指定了不同的截面!" % self.Name)
800
+ for i, iDescriptor in enumerate(self._Descriptors):
801
+ if self.descriptor_section[i] is None:
802
+ iDescriptor._QN_init_operation(start_dt, dt_dict, prepare_ids, id_dict)
803
+ else:
804
+ iDescriptor._QN_init_operation(start_dt, dt_dict, self.descriptor_section[i], id_dict)
805
+ if (self._OperationMode.SubProcessNum > 0) and (self.Name not in self._OperationMode._Event):
806
+ self._OperationMode._Event[self.Name] = (Queue(), Event())
807
+
808
+ def _calcData(
809
+ self,
810
+ ids: List[Any],
811
+ dts: List[Any],
812
+ descriptor_data: List[np.ndarray]
813
+ ) -> np.ndarray:
814
+ """计算数据(策略分派入口)
815
+
816
+ Args:
817
+ ids: ID列表
818
+ dts: 时间点列表
819
+ descriptor_data: 描述子数据列表
820
+
821
+ Returns:
822
+ 计算结果数组
823
+ """
824
+ StdData = create_std_data(dts, ids, self.DataType)
825
+ handler_name = self._OUTPUT_DT_DISPATCH.get((self.OutputMode, self.DTMode))
826
+ if handler_name:
827
+ return getattr(self, handler_name)(StdData, ids, dts, descriptor_data)
828
+ return StdData
829
+
830
+ def _calcData_full_section_single_time(
831
+ self,
832
+ StdData: np.ndarray,
833
+ ids: List[Any],
834
+ dts: List[Any],
835
+ descriptor_data: List[np.ndarray]
836
+ ) -> np.ndarray:
837
+ """全截面-单时点模式计算"""
838
+ for i, iDT in enumerate(dts):
839
+ StdData[i, :] = self.Operator(
840
+ self, iDT, ids,
841
+ [kDescriptorData[i] for kDescriptorData in descriptor_data],
842
+ self.ModelArgs
843
+ )
844
+ return StdData
845
+
846
+ def _calcData_full_section_multi_time(
847
+ self,
848
+ StdData: np.ndarray,
849
+ ids: List[Any],
850
+ dts: List[Any],
851
+ descriptor_data: List[np.ndarray]
852
+ ) -> np.ndarray:
853
+ """全截面-多时点模式计算"""
854
+ return self.Operator(self, dts, ids, descriptor_data, self.ModelArgs)
855
+
856
+ def _calcData_single_id_single_time(
857
+ self,
858
+ StdData: np.ndarray,
859
+ ids: List[Any],
860
+ dts: List[Any],
861
+ descriptor_data: List[np.ndarray]
862
+ ) -> np.ndarray:
863
+ """单ID-单时点模式计算"""
864
+ for i, iDT in enumerate(dts):
865
+ x = [kDescriptorData[i] for kDescriptorData in descriptor_data]
866
+ for j, jID in enumerate(ids):
867
+ StdData[i, j] = self.Operator(self, iDT, jID, x, self.ModelArgs)
868
+ return StdData
869
+
870
+ def _calcData_single_id_multi_time(
871
+ self,
872
+ StdData: np.ndarray,
873
+ ids: List[Any],
874
+ dts: List[Any],
875
+ descriptor_data: List[np.ndarray]
876
+ ) -> np.ndarray:
877
+ """单ID-多时点模式计算"""
878
+ for j, jID in enumerate(ids):
879
+ StdData[:, j] = self.Operator(self, dts, jID, descriptor_data, self.ModelArgs)
880
+ return StdData
881
+
882
+ def __QN_prepare_cache_data__(self, ids: Optional[List[Any]] = None) -> pd.DataFrame:
883
+ """准备缓存数据
884
+
885
+ Args:
886
+ ids: ID列表
887
+
888
+ Returns:
889
+ 标准数据DataFrame
890
+ """
891
+ DTs = list(self._PID_DTs[self._OperationMode._iPID])
892
+ IDs = self._OperationMode._FactorPrepareIDs[self.Name]
893
+ if IDs is None:
894
+ IDs = list(self._OperationMode.IDs)
895
+ if len(DTs) == 0:
896
+ iDTs = [self._OperationMode.DateTimes[-1]]
897
+ for i, iDescriptor in enumerate(self._Descriptors):
898
+ iDescriptor._QN_get_data(iDTs, pids=None)
899
+ StdData = create_empty_dataframe([], IDs, self.DataType, include_index=False)
900
+ elif IDs:
901
+ StdData = self._calcData(
902
+ ids=IDs, dts=DTs,
903
+ descriptor_data=[iDescriptor._QN_get_data(DTs, pids=None).values
904
+ for i, iDescriptor in enumerate(self._Descriptors)]
905
+ )
906
+ StdData = pd.DataFrame(StdData, index=DTs, columns=IDs)
907
+ else:
908
+ StdData = create_empty_dataframe(DTs, [], self.DataType)
909
+ PID_IDs = (
910
+ self._OperationMode._PID_IDs
911
+ if self._OperationMode._FactorPrepareIDs[self.Name] is None
912
+ else {
913
+ self._OperationMode._PIDs[i]: iSubIDs
914
+ for i, iSubIDs in enumerate(partition_list(IDs, len(self._OperationMode._PIDs)))
915
+ }
916
+ )
917
+ write_cache_files_for_all_pids(
918
+ self._OperationMode, PID_IDs, self.Name,
919
+ self._OperationMode._FactorID[self.Name], StdData
920
+ )
921
+ StdData = None
922
+ if self._OperationMode.SubProcessNum > 0:
923
+ Sub2MainQueue, PIDEvent = self._OperationMode._Event[self.Name]
924
+ Sub2MainQueue.put(1)
925
+ PIDEvent.wait()
926
+ self._isCacheDataOK = True
927
+ return StdData
928
+
929
+
930
+ # 面板运算
931
+ # f: 该算子所属的因子, 因子对象
932
+ # idt: 当前待计算的时点, 如果运算日期为多日期,则该值为 [回溯期数]+[时点]
933
+ # iid: 当前待计算的 ID, 如果输出形式为全截面, 则该值为 [ID], 该序列在并发时也是全体截面 ID
934
+ # x: 描述子当期的数据, [array]
935
+ # args: 参数, {参数名:参数值}
936
+ # 如果运算时点参数为单时点, 那么 x 元素为 array(shape=(回溯期数, nID)), 如果输出形式为全截面返回 array(shape=(nID, )), 否则返回单个值
937
+ # 如果运算时点参数为多时点, 那么 x 元素为 array(shape=(回溯期数+nDT, nID)), 如果输出形式为全截面返回 array(shape=(nDT, nID)), 否则返回 array(shape=(nDT, ))
938
+ class PanelOperation(_LookBackOperation):
939
+ """面板运算
940
+
941
+ 结合时间序列和截面运算,对描述子进行面板数据计算。
942
+
943
+ Attributes:
944
+ dt_mode: 运算时点模式
945
+ output_mode: 输出形式
946
+ descriptor_section: 描述子截面列表
947
+ """
948
+ dt_mode: DTModeType = DTModeType.SINGLE
949
+ output_mode: OutputModeType = OutputModeType.FULL_SECTION
950
+ descriptor_section: List = None
951
+
952
+ _OUTPUT_DT_DISPATCH = {
953
+ (OutputModeType.FULL_SECTION, DTModeType.SINGLE): "_calcData_full_section_single_time",
954
+ (OutputModeType.FULL_SECTION, DTModeType.MULTI): "_calcData_full_section_multi_time",
955
+ (OutputModeType.SINGLE_ID, DTModeType.SINGLE): "_calcData_single_id_single_time",
956
+ (OutputModeType.SINGLE_ID, DTModeType.MULTI): "_calcData_single_id_multi_time",
957
+ }
958
+
959
+ @property
960
+ def DTMode(self) -> DTModeType:
961
+ return self.dt_mode
962
+
963
+ @property
964
+ def OutputMode(self) -> OutputModeType:
965
+ return self.output_mode
966
+
967
+ def __init__(self, name: str = "", descriptors: List[Factor] = None, sys_args: Dict = None, **kwargs):
968
+ super().__init__(name=name, descriptors=descriptors, sys_args=sys_args, **kwargs)
969
+ if descriptors:
970
+ self.descriptor_section = [None] * len(descriptors)
971
+
972
+ def _QN_init_operation(self, start_dt, dt_dict, prepare_ids, id_dict):
973
+ if len(self._Descriptors) > len(self.look_back):
974
+ raise FactorError(
975
+ "面板运算因子 : '%s' 的参数'回溯期数'序列长度小于描述子个数!" % self.name)
976
+ OldStartDT = dt_dict.get(self.Name, None)
977
+ DTRuler = self._OperationMode.DTRuler
978
+ if (OldStartDT is None) or (start_dt < OldStartDT):
979
+ StartDT = dt_dict[self.Name] = start_dt
980
+ StartInd, EndInd = DTRuler.index(StartDT), DTRuler.index(self._OperationMode.DateTimes[-1])
981
+ if (self.i_look_back_mode == "扩张窗口") and (self.i_init_data is not None) and (self.i_init_data.shape[0] > 0):
982
+ if self.i_init_data.index[-1] not in self._OperationMode.DTRuler:
983
+ self._QN_logger.warning("注意: 因子 '%s' 的初始值不在时点标尺的范围内, 初始值和时点标尺之间的时间间隔将被忽略!" % (self.Name,))
984
+ else:
985
+ StartInd = min(StartInd, self._OperationMode.DTRuler.index(self.i_init_data.index[-1]) + 1)
986
+ DTs = DTRuler[StartInd:EndInd + 1]
987
+ if self.i_look_back_mode == "扩张窗口":
988
+ DTPartition = [DTs] + [[]] * (len(self._OperationMode._PIDs) - 1)
989
+ else:
990
+ DTPartition = partition_list(DTs, len(self._OperationMode._PIDs))
991
+ self._PID_DTs = {iPID: DTPartition[i] for i, iPID in enumerate(self._OperationMode._PIDs)}
992
+ else:
993
+ StartInd = DTRuler.index(OldStartDT)
994
+ PrepareIDs = id_dict.setdefault(self.Name, prepare_ids)
995
+ if prepare_ids != PrepareIDs:
996
+ raise FactorError("因子 %s 指定了不同的截面!" % self.Name)
997
+ for i, iDescriptor in enumerate(self._Descriptors):
998
+ iStartInd = StartInd - self.look_back[i]
999
+ if iStartInd < 0:
1000
+ self._QN_logger.warning(
1001
+ "注意: 对于因子 '%s' 的描述子 '%s', 时点标尺长度不足!" % (self.Name, iDescriptor.Name))
1002
+ iStartDT = DTRuler[max(0, iStartInd)]
1003
+ if self.descriptor_section[i] is None:
1004
+ iDescriptor._QN_init_operation(iStartDT, dt_dict, prepare_ids, id_dict)
1005
+ else:
1006
+ iDescriptor._QN_init_operation(iStartDT, dt_dict, self.descriptor_section[i], id_dict)
1007
+ if (self._OperationMode.SubProcessNum > 0) and (self.Name not in self._OperationMode._Event):
1008
+ self._OperationMode._Event[self.Name] = (Queue(), Event())
1009
+
1010
+ def readData(self, ids, dts, **kwargs):
1011
+ DTRuler = kwargs.get("dt_ruler", dts)
1012
+ SectionIDs = kwargs.pop("section_ids", ids)
1013
+ StartInd = (DTRuler.index(dts[0]) if dts[0] in DTRuler else 0)
1014
+ if (self.i_look_back_mode == "扩张窗口") and (self.i_init_data is not None) and (self.i_init_data.shape[0] > 0):
1015
+ if self.i_init_data.index[-1] not in DTRuler:
1016
+ self._QN_logger.warning("注意: 因子 '%s' 的初始值不在时点标尺的范围内, 初始值和时点标尺之间的时间间隔将被忽略!" % (self.Name,))
1017
+ else:
1018
+ StartInd = min(StartInd, DTRuler.index(self.i_init_data.index[-1]) + 1)
1019
+ EndInd = (DTRuler.index(dts[-1]) if dts[-1] in DTRuler else len(DTRuler) - 1)
1020
+ if StartInd > EndInd:
1021
+ return pd.DataFrame(index=dts, columns=ids)
1022
+ DescriptorData = []
1023
+ for i, iDescriptor in enumerate(self._Descriptors):
1024
+ iDTs = DTRuler[max(StartInd - self.look_back[i], 0):EndInd + 1]
1025
+ iSectionIDs = self.descriptor_section[i]
1026
+ if iSectionIDs is None:
1027
+ iSectionIDs = SectionIDs
1028
+ iIDNum = len(iSectionIDs)
1029
+ if iDTs:
1030
+ iDescriptorData = iDescriptor.readData(ids=iSectionIDs, dts=iDTs, **kwargs).values
1031
+ else:
1032
+ iDescriptorData = np.full((0, iIDNum), np.nan)
1033
+ if StartInd < self.look_back[i]:
1034
+ iLookBackData = np.full((self.look_back[i] - StartInd, iIDNum), np.nan)
1035
+ iDescriptorData = np.r_[iLookBackData, iDescriptorData]
1036
+ DescriptorData.append(iDescriptorData)
1037
+ StdData = self._calcData(ids=SectionIDs, dts=DTRuler[StartInd:EndInd + 1], descriptor_data=DescriptorData,
1038
+ dt_ruler=DTRuler)
1039
+ return pd.DataFrame(StdData, index=DTRuler[StartInd:EndInd + 1], columns=SectionIDs).loc[dts, ids]
1040
+
1041
+ def _calcData(self, ids, dts, descriptor_data, dt_ruler):
1042
+ StdData, iStartInd, DTRuler, StartIndAndLen, MaxLookBack, MaxLen, descriptor_data = \
1043
+ self._prepare_lookback_data(ids, dts, descriptor_data, dt_ruler)
1044
+ handler_name = self._OUTPUT_DT_DISPATCH.get((self.OutputMode, self.DTMode))
1045
+ if handler_name:
1046
+ return getattr(self, handler_name)(StdData, iStartInd, DTRuler, StartIndAndLen, MaxLookBack, MaxLen, ids, dts, descriptor_data)
1047
+ return self.Operator(self, DTRuler, ids, descriptor_data, self.ModelArgs)
1048
+
1049
+ def _calcData_full_section_single_time(self, StdData, iStartInd, DTRuler, StartIndAndLen, MaxLookBack, MaxLen, ids, dts, descriptor_data):
1050
+ for i, iDT in enumerate(dts):
1051
+ iDTs = DTRuler[max(0, MaxLookBack + i + 1 - MaxLen):i + 1 + MaxLookBack]
1052
+ x = []
1053
+ for k, kDescriptorData in enumerate(descriptor_data):
1054
+ kStartInd, kLen = StartIndAndLen[k]
1055
+ x.append(kDescriptorData[max(0, kStartInd + 1 + i - kLen):kStartInd + 1 + i])
1056
+ StdData[iStartInd + i, :] = self.Operator(self, iDTs, ids, x, self.ModelArgs)
1057
+ return StdData[iStartInd:, :]
1058
+
1059
+ def _calcData_full_section_multi_time(self, StdData, iStartInd, DTRuler, StartIndAndLen, MaxLookBack, MaxLen, ids, dts, descriptor_data):
1060
+ return self.Operator(self, DTRuler, ids, descriptor_data, self.ModelArgs)
1061
+
1062
+ def _calcData_single_id_single_time(self, StdData, iStartInd, DTRuler, StartIndAndLen, MaxLookBack, MaxLen, ids, dts, descriptor_data):
1063
+ for i, iDT in enumerate(dts):
1064
+ iDTs = DTRuler[max(0, MaxLookBack + i + 1 - MaxLen):i + 1 + MaxLookBack]
1065
+ x = []
1066
+ for k, kDescriptorData in enumerate(descriptor_data):
1067
+ kStartInd, kLen = StartIndAndLen[k]
1068
+ x.append(kDescriptorData[max(0, kStartInd + 1 + i - kLen):kStartInd + 1 + i])
1069
+ for j, jID in enumerate(ids):
1070
+ StdData[iStartInd + i, j] = self.Operator(self, iDTs, jID, x, self.ModelArgs)
1071
+ return StdData[iStartInd:, :]
1072
+
1073
+ def _calcData_single_id_multi_time(self, StdData, iStartInd, DTRuler, StartIndAndLen, MaxLookBack, MaxLen, ids, dts, descriptor_data):
1074
+ for j, jID in enumerate(ids):
1075
+ StdData[iStartInd:, j] = self.Operator(self, DTRuler, jID, descriptor_data, self.ModelArgs)
1076
+ return StdData[iStartInd:, :]
1077
+
1078
+ def __QN_prepare_cache_data__(self, ids=None):
1079
+ DTs = list(self._PID_DTs[self._OperationMode._iPID])
1080
+ IDs = self._OperationMode._FactorPrepareIDs[self.Name]
1081
+ if IDs is None:
1082
+ IDs = list(self._OperationMode.IDs)
1083
+ if len(DTs) == 0:
1084
+ iDTs = [self._OperationMode.DateTimes[-1]]
1085
+ for i, iDescriptor in enumerate(self._Descriptors):
1086
+ iDescriptor._QN_get_data(iDTs, pids=None)
1087
+ StdData = create_empty_dataframe([], IDs, self.DataType, include_index=False)
1088
+ elif IDs:
1089
+ DescriptorData = []
1090
+ StartInd = self._OperationMode.DTRuler.index(DTs[0])
1091
+ for i, iDescriptor in enumerate(self._Descriptors):
1092
+ iStartInd = StartInd - self.look_back[i]
1093
+ iDTs = list(self._OperationMode.DTRuler[max(0, iStartInd):StartInd]) + DTs
1094
+ iDescriptorData = iDescriptor._QN_get_data(iDTs, pids=None).values
1095
+ if iStartInd < 0:
1096
+ iDescriptorData = np.r_[
1097
+ np.full(shape=(abs(iStartInd), iDescriptorData.shape[1]), fill_value=np.nan), iDescriptorData]
1098
+ DescriptorData.append(iDescriptorData)
1099
+ StdData = self._calcData(ids=IDs, dts=DTs, descriptor_data=DescriptorData,
1100
+ dt_ruler=self._OperationMode.DTRuler)
1101
+ DescriptorData, iDescriptorData, StdData = None, None, pd.DataFrame(StdData, index=DTs, columns=IDs)
1102
+ else:
1103
+ StdData = create_empty_dataframe(DTs, [], self.DataType)
1104
+ PID_IDs = self._OperationMode._PID_IDs if self._OperationMode._FactorPrepareIDs[self.Name] is None else \
1105
+ {self._OperationMode._PIDs[i]: iSubIDs for i, iSubIDs in
1106
+ enumerate(partition_list_moving_sampling(IDs, len(self._OperationMode._PIDs)))}
1107
+ write_cache_files_for_all_pids(self._OperationMode, PID_IDs, self.Name,
1108
+ self._OperationMode._FactorID[self.Name], StdData)
1109
+ StdData = None
1110
+ if self._OperationMode.SubProcessNum > 0:
1111
+ Sub2MainQueue, PIDEvent = self._OperationMode._Event[self.Name]
1112
+ Sub2MainQueue.put(1)
1113
+ PIDEvent.wait()
1114
+ self._isCacheDataOK = True
1115
+ return StdData