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.
- QuantNodes/__init__.py +15 -0
- QuantNodes/__main__.py +14 -0
- QuantNodes/agent/__init__.py +158 -0
- QuantNodes/agent/agents/__init__.py +13 -0
- QuantNodes/agent/agents/definition.py +180 -0
- QuantNodes/agent/agents/manager.py +73 -0
- QuantNodes/agent/config/__init__.py +34 -0
- QuantNodes/agent/config/executor.py +958 -0
- QuantNodes/agent/config/loader.py +427 -0
- QuantNodes/agent/config/templates/bollinger_bands.yaml +84 -0
- QuantNodes/agent/config/templates/dual_ma.yaml +72 -0
- QuantNodes/agent/config/templates/empty.yaml +56 -0
- QuantNodes/agent/config/templates/mean_reversion.yaml +47 -0
- QuantNodes/agent/config/templates/mean_reversion_zscore.yaml +90 -0
- QuantNodes/agent/config/templates/momentum.yaml +81 -0
- QuantNodes/agent/config/templates/momentum_breakout.yaml +84 -0
- QuantNodes/agent/config/templates/rsi_strategy.yaml +72 -0
- QuantNodes/agent/config/templates/volume_price.yaml +86 -0
- QuantNodes/agent/config/types.py +156 -0
- QuantNodes/agent/config_mapper.py +293 -0
- QuantNodes/agent/core/__init__.py +19 -0
- QuantNodes/agent/core/dream.py +47 -0
- QuantNodes/agent/core/quant_dream.py +274 -0
- QuantNodes/agent/cron_jobs.py +314 -0
- QuantNodes/agent/nanobot_bridge.py +242 -0
- QuantNodes/agent/permission/__init__.py +30 -0
- QuantNodes/agent/permission/defaults.py +36 -0
- QuantNodes/agent/permission/evaluate.py +41 -0
- QuantNodes/agent/permission/models.py +59 -0
- QuantNodes/agent/permission/service.py +133 -0
- QuantNodes/agent/providers/__init__.py +11 -0
- QuantNodes/agent/providers/base.py +102 -0
- QuantNodes/agent/providers/quantnodes.py +610 -0
- QuantNodes/agent/providers/rate_limiter.py +326 -0
- QuantNodes/agent/providers/registry.py +163 -0
- QuantNodes/agent/skills/__init__.py +20 -0
- QuantNodes/agent/skills/base.py +118 -0
- QuantNodes/agent/skills/bridge.py +73 -0
- QuantNodes/agent/skills/factor/__init__.py +14 -0
- QuantNodes/agent/skills/factor/correlation.py +99 -0
- QuantNodes/agent/skills/factor/group_backtest.py +114 -0
- QuantNodes/agent/skills/factor/ic_analysis.py +106 -0
- QuantNodes/agent/skills/loader.py +107 -0
- QuantNodes/agent/skills/registry.py +105 -0
- QuantNodes/agent/skills/strategy/__init__.py +16 -0
- QuantNodes/agent/skills/strategy/bollinger.py +86 -0
- QuantNodes/agent/skills/strategy/dual_ma.py +82 -0
- QuantNodes/agent/skills/strategy/momentum.py +74 -0
- QuantNodes/agent/skills/strategy/rsi_reversal.py +99 -0
- QuantNodes/agent/skills_quant/__init__.py +14 -0
- QuantNodes/agent/skills_quant/backtest-analyze/SKILL.md +42 -0
- QuantNodes/agent/skills_quant/config-driven/SKILL.md +72 -0
- QuantNodes/agent/skills_quant/factor-research/SKILL.md +40 -0
- QuantNodes/agent/skills_quant/quant-dream/SKILL.md +55 -0
- QuantNodes/agent/skills_quant/risk-management/SKILL.md +45 -0
- QuantNodes/agent/skills_quant/strategy-design/SKILL.md +43 -0
- QuantNodes/agent/templates/__init__.py +4 -0
- QuantNodes/agent/tools/__init__.py +173 -0
- QuantNodes/agent/tools/_workspace.py +51 -0
- QuantNodes/agent/tools/alpha_backtest.py +328 -0
- QuantNodes/agent/tools/alpha_evaluate.py +493 -0
- QuantNodes/agent/tools/backtest.py +226 -0
- QuantNodes/agent/tools/base.py +133 -0
- QuantNodes/agent/tools/code_search.py +207 -0
- QuantNodes/agent/tools/config_backtest.py +401 -0
- QuantNodes/agent/tools/context.py +97 -0
- QuantNodes/agent/tools/dream_skill.py +77 -0
- QuantNodes/agent/tools/echo.py +38 -0
- QuantNodes/agent/tools/factor.py +231 -0
- QuantNodes/agent/tools/file_ops.py +201 -0
- QuantNodes/agent/tools/git_ops.py +190 -0
- QuantNodes/agent/tools/operator_lookup.py +218 -0
- QuantNodes/agent/tools/output_truncation.py +77 -0
- QuantNodes/agent/tools/path_check.py +43 -0
- QuantNodes/agent/tools/pipeline.py +62 -0
- QuantNodes/agent/tools/registry.py +150 -0
- QuantNodes/agent/tools/sandbox.py +62 -0
- QuantNodes/agent/tools/shell_safety.py +63 -0
- QuantNodes/agent/tools/strategy.py +106 -0
- QuantNodes/agent/tools/task.py +171 -0
- QuantNodes/agent/tools/web_fetch.py +142 -0
- QuantNodes/agent/tools/web_search.py +114 -0
- QuantNodes/agent/tools/wiki.py +370 -0
- QuantNodes/agent/utils/__init__.py +11 -0
- QuantNodes/agent/utils/helpers.py +43 -0
- QuantNodes/agent/utils/prompt_templates.py +30 -0
- QuantNodes/agent/workflows/__init__.py +20 -0
- QuantNodes/agent/workflows/implementations/__init__.py +8 -0
- QuantNodes/agent/workflows/implementations/alpha_gpt.py +508 -0
- QuantNodes/agent/workflows/implementations/mcts.py +442 -0
- QuantNodes/agent/workflows/parsers.py +44 -0
- QuantNodes/agent/workflows/registry.py +119 -0
- QuantNodes/agent/workflows/step_agent.py +219 -0
- QuantNodes/agent/workflows/tool.py +198 -0
- QuantNodes/ai/__init__.py +93 -0
- QuantNodes/ai/llm/__init__.py +75 -0
- QuantNodes/ai/llm/base.py +233 -0
- QuantNodes/ai/llm/decorators.py +281 -0
- QuantNodes/ai/llm/gateway.py +571 -0
- QuantNodes/ai/llm/null.py +76 -0
- QuantNodes/ai/llm/openai.py +435 -0
- QuantNodes/ai/optimizer.py +405 -0
- QuantNodes/ai/prompts/__init__.py +229 -0
- QuantNodes/ai/sandbox.py +371 -0
- QuantNodes/ai/sandbox_pandas_bridge.py +150 -0
- QuantNodes/ai/strategy_gen.py +396 -0
- QuantNodes/backtest/__init__.py +64 -0
- QuantNodes/backtest/backtest_node.py +188 -0
- QuantNodes/backtest/broker_node.py +378 -0
- QuantNodes/backtest/config_runner.py +397 -0
- QuantNodes/backtest/config_strategy.py +64 -0
- QuantNodes/backtest/risk_node.py +360 -0
- QuantNodes/backtest/strategy_node.py +268 -0
- QuantNodes/cache_node/__init__.py +19 -0
- QuantNodes/cache_node/base.py +244 -0
- QuantNodes/cache_node/cache_store.py +99 -0
- QuantNodes/cache_node/metadata.py +100 -0
- QuantNodes/cli/__init__.py +109 -0
- QuantNodes/cli/_helpers.py +511 -0
- QuantNodes/cli/command.py +110 -0
- QuantNodes/cli/commands/__init__.py +69 -0
- QuantNodes/cli/commands/agent.py +158 -0
- QuantNodes/cli/commands/alpha.py +951 -0
- QuantNodes/cli/commands/chat.py +38 -0
- QuantNodes/cli/commands/evolve.py +120 -0
- QuantNodes/cli/commands/factor.py +569 -0
- QuantNodes/cli/commands/init.py +190 -0
- QuantNodes/cli/commands/run.py +259 -0
- QuantNodes/cli/commands/serve.py +398 -0
- QuantNodes/cli/commands/version.py +120 -0
- QuantNodes/cli/enhanced.py +146 -0
- QuantNodes/conf_node/__init__.py +37 -0
- QuantNodes/conf_node/base.py +120 -0
- QuantNodes/conf_node/env_config.py +132 -0
- QuantNodes/conf_node/ini_config.py +70 -0
- QuantNodes/conf_node/json_config.py +69 -0
- QuantNodes/conf_node/yaml_config.py +78 -0
- QuantNodes/constants.py +17 -0
- QuantNodes/core/__init__.py +196 -0
- QuantNodes/core/_lookback_helpers.py +49 -0
- QuantNodes/core/ast_parser.py +198 -0
- QuantNodes/core/base.py +61 -0
- QuantNodes/core/cache_manager.py +344 -0
- QuantNodes/core/cache_utils.py +150 -0
- QuantNodes/core/cond_builder.py +53 -0
- QuantNodes/core/config.py +170 -0
- QuantNodes/core/constants.py +48 -0
- QuantNodes/core/control.py +412 -0
- QuantNodes/core/data_preprocessing.py +453 -0
- QuantNodes/core/data_source.py +46 -0
- QuantNodes/core/events.py +178 -0
- QuantNodes/core/evolution/__init__.py +22 -0
- QuantNodes/core/evolution/loop.py +583 -0
- QuantNodes/core/evolution/operators.py +289 -0
- QuantNodes/core/evolution/settings.py +44 -0
- QuantNodes/core/expression.py +841 -0
- QuantNodes/core/feedback/__init__.py +38 -0
- QuantNodes/core/feedback/channels.py +182 -0
- QuantNodes/core/feedback/collector.py +91 -0
- QuantNodes/core/feedback/dataclass.py +239 -0
- QuantNodes/core/feedback/llm_judge.py +138 -0
- QuantNodes/core/knowledge/__init__.py +69 -0
- QuantNodes/core/knowledge/knowledge_base.py +217 -0
- QuantNodes/core/knowledge/lineage_compress.py +196 -0
- QuantNodes/core/knowledge/lineage_expand.py +123 -0
- QuantNodes/core/knowledge/metrics/__init__.py +43 -0
- QuantNodes/core/knowledge/metrics/evaluator.py +176 -0
- QuantNodes/core/knowledge/metrics/metrics.py +220 -0
- QuantNodes/core/knowledge/rag_prompt.py +196 -0
- QuantNodes/core/knowledge/retriever.py +209 -0
- QuantNodes/core/lambda_node.py +81 -0
- QuantNodes/core/monitoring/__init__.py +22 -0
- QuantNodes/core/monitoring/collector.py +292 -0
- QuantNodes/core/monitoring/dashboard.py +365 -0
- QuantNodes/core/node.py +375 -0
- QuantNodes/core/pandas_utils.py +504 -0
- QuantNodes/core/parallel/__init__.py +15 -0
- QuantNodes/core/parallel/worker.py +140 -0
- QuantNodes/core/parallel/worker_process.py +265 -0
- QuantNodes/core/path_utils.py +73 -0
- QuantNodes/core/pipeline.py +328 -0
- QuantNodes/core/plugin.py +135 -0
- QuantNodes/core/quality_gate/__init__.py +32 -0
- QuantNodes/core/quality_gate/complexity.py +94 -0
- QuantNodes/core/quality_gate/consistency.py +26 -0
- QuantNodes/core/quality_gate/node.py +97 -0
- QuantNodes/core/quality_gate/redundancy.py +51 -0
- QuantNodes/core/quality_gate/settings.py +43 -0
- QuantNodes/core/quality_gate/zoo.py +98 -0
- QuantNodes/core/serializable.py +116 -0
- QuantNodes/core/serialization.py +673 -0
- QuantNodes/core/tools.py +333 -0
- QuantNodes/core/trajectory/__init__.py +25 -0
- QuantNodes/core/trajectory/entry.py +116 -0
- QuantNodes/core/trajectory/lineage.py +67 -0
- QuantNodes/core/trajectory/pool.py +211 -0
- QuantNodes/core/trajectory/selector.py +140 -0
- QuantNodes/core/visualization/__init__.py +33 -0
- QuantNodes/core/visualization/builder.py +233 -0
- QuantNodes/core/visualization/gate_breakdown.py +140 -0
- QuantNodes/core/visualization/lineage_dag.py +203 -0
- QuantNodes/core/visualization/metric_distribution.py +125 -0
- QuantNodes/core/visualization/report.py +68 -0
- QuantNodes/database_node/__init__.py +69 -0
- QuantNodes/database_node/base.py +135 -0
- QuantNodes/database_node/clickhouse_node.py +272 -0
- QuantNodes/database_node/csv_node.py +83 -0
- QuantNodes/database_node/duckdb_node.py +86 -0
- QuantNodes/database_node/factory.py +83 -0
- QuantNodes/database_node/mysql_node.py +100 -0
- QuantNodes/database_node/parquet_node.py +75 -0
- QuantNodes/database_node/sqlite_node.py +67 -0
- QuantNodes/factor_node/__init__.py +50 -0
- QuantNodes/factor_node/factor.py +563 -0
- QuantNodes/factor_node/factor_db.py +421 -0
- QuantNodes/factor_node/factor_functions/__init__.py +252 -0
- QuantNodes/factor_node/factor_functions/_helpers.py +358 -0
- QuantNodes/factor_node/factor_functions/_helpers_debug.py +317 -0
- QuantNodes/factor_node/factor_functions/composite_ops.py +136 -0
- QuantNodes/factor_node/factor_functions/math_ops.py +433 -0
- QuantNodes/factor_node/factor_functions/section_ops.py +290 -0
- QuantNodes/factor_node/factor_functions/talib_ops.py +1293 -0
- QuantNodes/factor_node/factor_functions/time_ops.py +535 -0
- QuantNodes/factor_node/factor_operation.py +1115 -0
- QuantNodes/factor_node/factor_table.py +1073 -0
- QuantNodes/factor_node/quant_nodes_object.py +60 -0
- QuantNodes/mcp_server/__init__.py +27 -0
- QuantNodes/mcp_server/__main__.py +4 -0
- QuantNodes/mcp_server/server.py +272 -0
- QuantNodes/methods/__init__.py +28 -0
- QuantNodes/methods/pipeline.py +100 -0
- QuantNodes/methods/sandbox.py +102 -0
- QuantNodes/monitor/__init__.py +27 -0
- QuantNodes/monitor/agent_tools/__init__.py +5 -0
- QuantNodes/monitor/agent_tools/monitor_tool.py +98 -0
- QuantNodes/monitor/agent_tools/schedule_tool.py +98 -0
- QuantNodes/monitor/agent_tools/version_tool.py +133 -0
- QuantNodes/monitor/monitor/__init__.py +6 -0
- QuantNodes/monitor/monitor/alerter.py +60 -0
- QuantNodes/monitor/monitor/collector.py +164 -0
- QuantNodes/monitor/monitor/dashboard.py +115 -0
- QuantNodes/monitor/monitor/drift.py +190 -0
- QuantNodes/monitor/scheduler/__init__.py +4 -0
- QuantNodes/monitor/scheduler/runner.py +133 -0
- QuantNodes/monitor/scheduler/scheduler.py +184 -0
- QuantNodes/monitor/storage/__init__.py +16 -0
- QuantNodes/monitor/storage/models.py +70 -0
- QuantNodes/monitor/storage/repository.py +407 -0
- QuantNodes/monitor/version/__init__.py +4 -0
- QuantNodes/monitor/version/diff.py +81 -0
- QuantNodes/monitor/version/version_manager.py +182 -0
- QuantNodes/operator_node/__init__.py +28 -0
- QuantNodes/operator_node/base.py +97 -0
- QuantNodes/operator_node/query_node.py +129 -0
- QuantNodes/operator_node/sql_builder.py +125 -0
- QuantNodes/operator_node/sql_utils.py +172 -0
- QuantNodes/operator_node/transform.py +130 -0
- QuantNodes/operators/__init__.py +90 -0
- QuantNodes/operators/_engine.py +108 -0
- QuantNodes/operators/composite.py +161 -0
- QuantNodes/operators/composite_dag.py +667 -0
- QuantNodes/operators/composite_dag_ops.py +343 -0
- QuantNodes/operators/composite_dag_pandas_ops.py +382 -0
- QuantNodes/operators/custom.py +408 -0
- QuantNodes/operators/facade.py +164 -0
- QuantNodes/operators/math.py +163 -0
- QuantNodes/operators/proxy.py +29 -0
- QuantNodes/operators/registry.py +144 -0
- QuantNodes/operators/section.py +99 -0
- QuantNodes/operators/talib.py +757 -0
- QuantNodes/operators/templates.py +95 -0
- QuantNodes/operators/time_series.py +136 -0
- QuantNodes/prompts/__init__.py +20 -0
- QuantNodes/prompts/backtest/__init__.py +12 -0
- QuantNodes/prompts/backtest/factor_based.py +86 -0
- QuantNodes/prompts/backtest/standard.py +73 -0
- QuantNodes/prompts/factor/__init__.py +14 -0
- QuantNodes/prompts/factor/correlation.py +77 -0
- QuantNodes/prompts/factor/group_backtest.py +86 -0
- QuantNodes/prompts/factor/ic_analysis.py +91 -0
- QuantNodes/prompts/strategy/__init__.py +18 -0
- QuantNodes/prompts/strategy/market_neutral.py +96 -0
- QuantNodes/prompts/strategy/mean_reversion.py +107 -0
- QuantNodes/prompts/strategy/momentum.py +160 -0
- QuantNodes/prompts/strategy/pairs_trading.py +107 -0
- QuantNodes/prompts/strategy/trend_following.py +96 -0
- QuantNodes/research/README.md +106 -0
- QuantNodes/research/__init__.py +154 -0
- QuantNodes/research/_legacy_3c/__init__.py +61 -0
- QuantNodes/research/_legacy_3c/auto_researcher.py +289 -0
- QuantNodes/research/_legacy_3c/factor_evaluator.py +560 -0
- QuantNodes/research/_legacy_3c/factor_miner.py +318 -0
- QuantNodes/research/_legacy_3c/mcts_search.py +324 -0
- QuantNodes/research/factor_test/__init__.py +25 -0
- QuantNodes/research/factor_test/config.py +184 -0
- QuantNodes/research/factor_test/config_builder.py +276 -0
- QuantNodes/research/factor_test/e2e/data_prep.py +163 -0
- QuantNodes/research/factor_test/e2e/run_evolution_e2e.py +309 -0
- QuantNodes/research/factor_test/evolution_adapter.py +231 -0
- QuantNodes/research/factor_test/feedback_wrapper.py +102 -0
- QuantNodes/research/factor_test/ifind_db/__init__.py +7 -0
- QuantNodes/research/factor_test/ifind_db/fetcher.py +224 -0
- QuantNodes/research/factor_test/ifind_db/ifind_database.py +689 -0
- QuantNodes/research/factor_test/nodes/__init__.py +1 -0
- QuantNodes/research/factor_test/nodes/_base.py +91 -0
- QuantNodes/research/factor_test/nodes/adjust_date_node.py +48 -0
- QuantNodes/research/factor_test/nodes/configs.py +240 -0
- QuantNodes/research/factor_test/nodes/factor_neutralize_node.py +87 -0
- QuantNodes/research/factor_test/nodes/factor_preprocess_node.py +222 -0
- QuantNodes/research/factor_test/nodes/factor_score_node.py +141 -0
- QuantNodes/research/factor_test/nodes/factor_test_report_node.py +153 -0
- QuantNodes/research/factor_test/nodes/group_analyzer_node.py +317 -0
- QuantNodes/research/factor_test/nodes/ic_analyzer_node.py +112 -0
- QuantNodes/research/factor_test/nodes/load_data_node.py +100 -0
- QuantNodes/research/factor_test/nodes/long_short_node.py +93 -0
- QuantNodes/research/factor_test/nodes/neutralizers.py +222 -0
- QuantNodes/research/factor_test/nodes/preprocess_strategies.py +277 -0
- QuantNodes/research/factor_test/nodes/risk_correlation_node.py +112 -0
- QuantNodes/research/factor_test/nodes/sample_pool_filter_node.py +110 -0
- QuantNodes/research/factor_test/nodes/tradability_filter_node.py +92 -0
- QuantNodes/research/factor_test/pipeline_runner.py +305 -0
- QuantNodes/research/factor_test/pipeline_spec.py +216 -0
- QuantNodes/research/factor_test/utils/__init__.py +26 -0
- QuantNodes/research/factor_test/utils/constants.py +86 -0
- QuantNodes/research/factor_test/utils/data_loader.py +141 -0
- QuantNodes/research/factor_test/utils/date_utils.py +232 -0
- QuantNodes/research/factor_test/utils/file_loaders.py +150 -0
- QuantNodes/research/factor_test/utils/labels.py +37 -0
- QuantNodes/research/factor_test/utils/metrics_extractor.py +55 -0
- QuantNodes/research/factor_test/utils/performance_metrics.py +175 -0
- QuantNodes/research/factor_test/utils/safe_load.py +106 -0
- QuantNodes/research/quant_alpha/CHANGELOG.md +80 -0
- QuantNodes/research/quant_alpha/README.md +142 -0
- QuantNodes/research/quant_alpha/__init__.py +45 -0
- QuantNodes/research/quant_alpha/adapters/__init__.py +99 -0
- QuantNodes/research/quant_alpha/adapters/calculator.py +503 -0
- QuantNodes/research/quant_alpha/adapters/expression.py +387 -0
- QuantNodes/research/quant_alpha/alpha101_design/__init__.py +50 -0
- QuantNodes/research/quant_alpha/alpha101_design/few_shot_examples.py +243 -0
- QuantNodes/research/quant_alpha/alpha101_design/philosophy.py +474 -0
- QuantNodes/research/quant_alpha/alpha158_design/__init__.py +63 -0
- QuantNodes/research/quant_alpha/alpha158_design/few_shot_examples.py +219 -0
- QuantNodes/research/quant_alpha/alpha158_design/philosophy.py +240 -0
- QuantNodes/research/quant_alpha/evaluation/__init__.py +47 -0
- QuantNodes/research/quant_alpha/evaluation/baselines/__init__.py +8 -0
- QuantNodes/research/quant_alpha/evaluation/baselines/g1_handcrafted.py +135 -0
- QuantNodes/research/quant_alpha/evaluation/baselines/g2_llm_only.py +269 -0
- QuantNodes/research/quant_alpha/evaluation/baselines/g3_alpha_gpt.py +152 -0
- QuantNodes/research/quant_alpha/evaluation/clickhouse_data_loader.py +227 -0
- QuantNodes/research/quant_alpha/evaluation/contracts.py +376 -0
- QuantNodes/research/quant_alpha/evaluation/evaluators/__init__.py +6 -0
- QuantNodes/research/quant_alpha/evaluation/evaluators/polars_evaluator.py +545 -0
- QuantNodes/research/quant_alpha/evaluation/mock_data_loader.py +226 -0
- QuantNodes/research/quant_alpha/evaluation/runner.py +243 -0
- QuantNodes/research/quant_alpha/llm/__init__.py +38 -0
- QuantNodes/research/quant_alpha/llm/parser.py +681 -0
- QuantNodes/research/quant_alpha/logic_driven_pipeline.py +411 -0
- QuantNodes/research/quant_alpha/logic_mining/__init__.py +74 -0
- QuantNodes/research/quant_alpha/logic_mining/compiler.py +457 -0
- QuantNodes/research/quant_alpha/logic_mining/generator.py +366 -0
- QuantNodes/research/quant_alpha/logic_mining/models.py +252 -0
- QuantNodes/research/quant_alpha/logic_mining/parser.py +287 -0
- QuantNodes/research/quant_alpha/logic_mining/pipelines.py +297 -0
- QuantNodes/research/quant_alpha/logic_mining/sources.py +149 -0
- QuantNodes/research/quant_alpha/mcts/__init__.py +66 -0
- QuantNodes/research/quant_alpha/mcts/cache.py +262 -0
- QuantNodes/research/quant_alpha/mcts/extension_ops.py +320 -0
- QuantNodes/research/quant_alpha/mcts/feedback.py +825 -0
- QuantNodes/research/quant_alpha/mcts/op_prior.py +180 -0
- QuantNodes/research/quant_alpha/mcts/search.py +540 -0
- QuantNodes/research/quant_alpha/mcts/tree.py +201 -0
- QuantNodes/research/quant_alpha/operator_vocab/__init__.py +50 -0
- QuantNodes/research/quant_alpha/operator_vocab/config.py +54 -0
- QuantNodes/research/quant_alpha/operator_vocab/metadata.py +263 -0
- QuantNodes/research/quant_alpha/operator_vocab/vocabulary.py +481 -0
- QuantNodes/research/quant_alpha/pipeline.py +1027 -0
- QuantNodes/research/quant_alpha/types/__init__.py +27 -0
- QuantNodes/research/quant_alpha/types/constants.py +28 -0
- QuantNodes/research/quant_alpha/types/state.py +205 -0
- QuantNodes/research/quant_alpha/workflow/__init__.py +32 -0
- QuantNodes/research/quant_alpha/workflow/alpha_gpt.py +911 -0
- QuantNodes/research/quant_alpha/workflow/alpha_logics.py +416 -0
- QuantNodes/research/quant_alpha/workflow/state.py +27 -0
- QuantNodes/research/report_reproducer.py +485 -0
- QuantNodes/research/wiki.py +1155 -0
- QuantNodes/symbolic/__init__.py +51 -0
- QuantNodes/symbolic/compiler.py +113 -0
- QuantNodes/symbolic/dialect.py +260 -0
- QuantNodes/symbolic/executor.py +147 -0
- QuantNodes/symbolic/expression.py +234 -0
- QuantNodes/symbolic/functions.py +433 -0
- QuantNodes/symbolic/optimizer.py +165 -0
- QuantNodes/ui_node/__init__.py +30 -0
- QuantNodes/ui_node/base.py +222 -0
- quantnodes-3.0.0.dist-info/METADATA +463 -0
- quantnodes-3.0.0.dist-info/RECORD +399 -0
- quantnodes-3.0.0.dist-info/WHEEL +5 -0
- quantnodes-3.0.0.dist-info/entry_points.txt +24 -0
- 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
|