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,673 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
"""
|
|
3
|
+
序列化工具模块 - 提供多种序列化方案
|
|
4
|
+
|
|
5
|
+
方案对比:
|
|
6
|
+
| 方案 | 安全 | 可读 | 速度 | 跨语言 | 零依赖 | 推荐场景 |
|
|
7
|
+
|-------------|------|------|------|--------|--------|------------------------|
|
|
8
|
+
| JSON | ✅ | ✅ | ⭐⭐ | ✅ | ✅ | 默认、调试、跨系统交互 |
|
|
9
|
+
| JSON+zlib | ✅ | ⚠️ | ⭐⭐⭐| ✅ | ✅ | 网络传输、持久化存储 |
|
|
10
|
+
| msgpack | ✅ | ❌ | ⭐⭐⭐⭐ | ✅ | ❌ | 高性能场景(可选依赖) |
|
|
11
|
+
| pickle | ⚠️ | ❌ | ⭐⭐⭐⭐ | ❌ | ✅ | 仅限可信内部环境 |
|
|
12
|
+
|
|
13
|
+
使用建议:
|
|
14
|
+
- 开发/调试 -> serialize_json()
|
|
15
|
+
- 网络传输 -> serialize_compact()
|
|
16
|
+
- 极致性能 -> serialize_msgpack() (需要 msgpack)
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import json
|
|
22
|
+
import zlib
|
|
23
|
+
import warnings
|
|
24
|
+
from typing import Union, Optional
|
|
25
|
+
|
|
26
|
+
from QuantNodes.core.expression import Expression
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# ============================================================================
|
|
30
|
+
# 序列化方案
|
|
31
|
+
# ============================================================================
|
|
32
|
+
|
|
33
|
+
def serialize_json(expr, indent: Optional[int] = 2) -> str:
|
|
34
|
+
"""
|
|
35
|
+
JSON 序列化 - 默认方案
|
|
36
|
+
|
|
37
|
+
特点:安全、可读、跨语言、零依赖
|
|
38
|
+
"""
|
|
39
|
+
return json.dumps(expr.serialize(), indent=indent, ensure_ascii=False)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def serialize_json_bytes(expr: Expression) -> bytes:
|
|
43
|
+
"""JSON 序列化(字节形式)"""
|
|
44
|
+
return serialize_json(expr, indent=None).encode('utf-8')
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def serialize_compact(expr, compress_level: int = 6) -> bytes:
|
|
48
|
+
"""
|
|
49
|
+
紧凑序列化 - 推荐用于网络传输
|
|
50
|
+
|
|
51
|
+
JSON + zlib 压缩,零额外依赖
|
|
52
|
+
压缩率通常可达 30%-50%
|
|
53
|
+
"""
|
|
54
|
+
json_bytes = serialize_json_bytes(expr)
|
|
55
|
+
return zlib.compress(json_bytes, level=compress_level)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def serialize_msgpack(expr) -> bytes:
|
|
59
|
+
"""
|
|
60
|
+
msgpack 序列化 - 极致性能
|
|
61
|
+
|
|
62
|
+
要求:pip install msgpack
|
|
63
|
+
特点:比 JSON 小 30-40%,速度快 2-3 倍
|
|
64
|
+
"""
|
|
65
|
+
try:
|
|
66
|
+
import msgpack
|
|
67
|
+
except ImportError:
|
|
68
|
+
raise ImportError(
|
|
69
|
+
"msgpack 未安装。请运行: pip install msgpack"
|
|
70
|
+
) from None
|
|
71
|
+
return msgpack.packb(expr.serialize(), use_bin_type=True)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def serialize_pickle(expr: Expression, protocol: int = 4) -> bytes:
|
|
75
|
+
"""
|
|
76
|
+
Pickle 序列化 - 仅限内部可信环境
|
|
77
|
+
|
|
78
|
+
警告:存在代码执行安全风险,仅反序列化可信来源的数据!
|
|
79
|
+
"""
|
|
80
|
+
import pickle
|
|
81
|
+
warnings.warn(
|
|
82
|
+
"Pickle 序列化存在安全风险,仅在可信内部环境使用。"
|
|
83
|
+
"建议优先使用 serialize_compact() 或 serialize_json()。",
|
|
84
|
+
UserWarning,
|
|
85
|
+
stacklevel=2
|
|
86
|
+
)
|
|
87
|
+
return pickle.dumps(expr, protocol=protocol)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
# ============================================================================
|
|
91
|
+
# 反序列化方案
|
|
92
|
+
# ============================================================================
|
|
93
|
+
|
|
94
|
+
def deserialize_json(data: Union[str, bytes]) -> Expression:
|
|
95
|
+
"""从 JSON 反序列化"""
|
|
96
|
+
if isinstance(data, bytes):
|
|
97
|
+
data = data.decode('utf-8')
|
|
98
|
+
expr_data = json.loads(data)
|
|
99
|
+
return Expression.from_dict(expr_data)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def deserialize_compact(data: bytes) -> Expression:
|
|
103
|
+
"""从紧凑格式(JSON + zlib)反序列化"""
|
|
104
|
+
json_bytes = zlib.decompress(data)
|
|
105
|
+
return deserialize_json(json_bytes)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def deserialize_msgpack(data: bytes) -> Expression:
|
|
109
|
+
"""从 msgpack 反序列化"""
|
|
110
|
+
try:
|
|
111
|
+
import msgpack
|
|
112
|
+
except ImportError:
|
|
113
|
+
raise ImportError(
|
|
114
|
+
"msgpack 未安装。请运行: pip install msgpack"
|
|
115
|
+
) from None
|
|
116
|
+
expr_data = msgpack.unpackb(data, raw=False)
|
|
117
|
+
return Expression.deserialize(expr_data)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def deserialize_pickle(data: bytes) -> Expression:
|
|
121
|
+
"""
|
|
122
|
+
从 pickle 反序列化
|
|
123
|
+
|
|
124
|
+
警告:仅反序列化可信来源的数据!
|
|
125
|
+
"""
|
|
126
|
+
import pickle
|
|
127
|
+
warnings.warn(
|
|
128
|
+
"Pickle 反序列化存在安全风险,仅反序列化可信来源的数据。",
|
|
129
|
+
UserWarning,
|
|
130
|
+
stacklevel=2
|
|
131
|
+
)
|
|
132
|
+
return pickle.loads(data)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
# ============================================================================
|
|
136
|
+
# 自动检测反序列化
|
|
137
|
+
# ============================================================================
|
|
138
|
+
|
|
139
|
+
def deserialize_auto(data: Union[str, bytes]) -> Expression:
|
|
140
|
+
"""
|
|
141
|
+
自动检测格式并反序列化
|
|
142
|
+
|
|
143
|
+
支持:JSON(str/bytes), JSON+zlib(bytes), msgpack(bytes), pickle(bytes)
|
|
144
|
+
"""
|
|
145
|
+
if isinstance(data, str):
|
|
146
|
+
return deserialize_json(data)
|
|
147
|
+
|
|
148
|
+
if not isinstance(data, bytes):
|
|
149
|
+
raise TypeError(f"不支持的数据类型: {type(data)}")
|
|
150
|
+
|
|
151
|
+
# 尝试 zlib 压缩格式
|
|
152
|
+
if len(data) >= 2 and data[0:2] == b'x\x9c': # zlib 魔数
|
|
153
|
+
try:
|
|
154
|
+
return deserialize_compact(data)
|
|
155
|
+
except Exception:
|
|
156
|
+
pass
|
|
157
|
+
|
|
158
|
+
# 尝试 msgpack (第一个字节通常是 map 标记 0x80-0x8f)
|
|
159
|
+
if data and 0x80 <= data[0] <= 0x8f:
|
|
160
|
+
try:
|
|
161
|
+
return deserialize_msgpack(data)
|
|
162
|
+
except Exception:
|
|
163
|
+
pass
|
|
164
|
+
|
|
165
|
+
# 尝试 JSON
|
|
166
|
+
try:
|
|
167
|
+
return deserialize_json(data)
|
|
168
|
+
except Exception:
|
|
169
|
+
pass
|
|
170
|
+
|
|
171
|
+
# 最后尝试 pickle(带警告)
|
|
172
|
+
return deserialize_pickle(data)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
# ============================================================================
|
|
176
|
+
# Protobuf 序列化 - 高性能跨语言方案
|
|
177
|
+
# ============================================================================
|
|
178
|
+
|
|
179
|
+
_PROTOBUF_AVAILABLE = False
|
|
180
|
+
try:
|
|
181
|
+
import importlib.util
|
|
182
|
+
_PROTOBUF_AVAILABLE = importlib.util.find_spec("google.protobuf") is not None
|
|
183
|
+
except ImportError:
|
|
184
|
+
pass
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
# TODO [未完成]: 动态生成表达式的 protobuf schema
|
|
188
|
+
#
|
|
189
|
+
# 设计思路:
|
|
190
|
+
# - 当前实现:使用 struct_pb2.Struct 进行通用序列化(见 serialize_proto)
|
|
191
|
+
# - 预期目标:大规模场景下生成专属 .proto 文件以提升性能
|
|
192
|
+
#
|
|
193
|
+
# 完整实现需要:
|
|
194
|
+
# 1. 定义 Expression 的 protobuf 消息结构
|
|
195
|
+
# 2. 动态生成 .proto 描述符
|
|
196
|
+
# 3. 使用 protoc 编译或动态编译
|
|
197
|
+
#
|
|
198
|
+
# 当前状态:stub - 只有 pass,无实际功能
|
|
199
|
+
# 优先级:低 - 当前实现已满足需求
|
|
200
|
+
def _get_proto_descriptor():
|
|
201
|
+
"""
|
|
202
|
+
动态生成表达式的 protobuf schema
|
|
203
|
+
|
|
204
|
+
说明:不需要预编译 .proto 文件,动态构建描述符
|
|
205
|
+
"""
|
|
206
|
+
# 简单实现:先序列化为 dict 再用 protobuf 打包
|
|
207
|
+
# 完整实现需要生成 .proto 文件并编译
|
|
208
|
+
pass
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def serialize_proto(expr) -> bytes:
|
|
212
|
+
"""
|
|
213
|
+
Protobuf 序列化
|
|
214
|
+
|
|
215
|
+
要求:pip install protobuf
|
|
216
|
+
特点:高性能、跨语言、强类型、前向兼容
|
|
217
|
+
"""
|
|
218
|
+
if not _PROTOBUF_AVAILABLE:
|
|
219
|
+
raise ImportError(
|
|
220
|
+
"protobuf 未安装。请运行: pip install protobuf"
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
from google.protobuf import struct_pb2
|
|
224
|
+
|
|
225
|
+
data = expr.serialize()
|
|
226
|
+
struct = struct_pb2.Struct()
|
|
227
|
+
struct.update(data)
|
|
228
|
+
return struct.SerializeToString()
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def _fix_numbers(obj):
|
|
232
|
+
"""递归修复数字类型:Protobuf 会把整数转成浮点数,转换回来"""
|
|
233
|
+
if isinstance(obj, dict):
|
|
234
|
+
return {k: _fix_numbers(v) for k, v in obj.items()}
|
|
235
|
+
elif isinstance(obj, list):
|
|
236
|
+
return [_fix_numbers(v) for v in obj]
|
|
237
|
+
elif isinstance(obj, float) and obj.is_integer():
|
|
238
|
+
return int(obj)
|
|
239
|
+
return obj
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def deserialize_proto(data: bytes) -> Expression:
|
|
243
|
+
"""从 Protobuf 反序列化"""
|
|
244
|
+
if not _PROTOBUF_AVAILABLE:
|
|
245
|
+
raise ImportError(
|
|
246
|
+
"protobuf 未安装。请运行: pip install protobuf"
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
from google.protobuf import struct_pb2
|
|
250
|
+
|
|
251
|
+
struct = struct_pb2.Struct()
|
|
252
|
+
struct.ParseFromString(data)
|
|
253
|
+
|
|
254
|
+
# 转换为 dict
|
|
255
|
+
from google.protobuf.json_format import MessageToDict
|
|
256
|
+
data_dict = MessageToDict(struct, preserving_proto_field_name=True)
|
|
257
|
+
|
|
258
|
+
# 修复被转成浮点数的整数
|
|
259
|
+
data_dict = _fix_numbers(data_dict)
|
|
260
|
+
return Expression.deserialize(data_dict)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
# ============================================================================
|
|
264
|
+
# 加密序列化 - 用于敏感策略保护
|
|
265
|
+
# ============================================================================
|
|
266
|
+
|
|
267
|
+
def serialize_encrypted(
|
|
268
|
+
expr: Expression,
|
|
269
|
+
key: Union[str, bytes],
|
|
270
|
+
algorithm: str = 'AES-GCM'
|
|
271
|
+
) -> bytes:
|
|
272
|
+
"""
|
|
273
|
+
加密序列化 - 保护敏感策略表达式
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
expr: 表达式对象
|
|
277
|
+
key: 加密密钥(字符串或 bytes)
|
|
278
|
+
algorithm: 加密算法(默认 AES-GCM,带认证)
|
|
279
|
+
|
|
280
|
+
要求:使用 Python 标准库 cryptography
|
|
281
|
+
pip install cryptography
|
|
282
|
+
|
|
283
|
+
安全特性:
|
|
284
|
+
✅ AES-256-GCM 加密
|
|
285
|
+
✅ 带认证标签,防止篡改
|
|
286
|
+
✅ 随机 nonce,相同明文每次加密结果不同
|
|
287
|
+
✅ 密钥派生(PBKDF2),支持字符串密码
|
|
288
|
+
"""
|
|
289
|
+
try:
|
|
290
|
+
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
|
291
|
+
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
|
|
292
|
+
from cryptography.hazmat.primitives import hashes
|
|
293
|
+
from cryptography.hazmat.backends import default_backend
|
|
294
|
+
except ImportError:
|
|
295
|
+
raise ImportError(
|
|
296
|
+
"cryptography 未安装。请运行: pip install cryptography"
|
|
297
|
+
) from None
|
|
298
|
+
|
|
299
|
+
import os
|
|
300
|
+
|
|
301
|
+
# 1. 先序列化为紧凑 JSON
|
|
302
|
+
plaintext = expr.to_json(indent=None).encode('utf-8')
|
|
303
|
+
|
|
304
|
+
# 2. 处理密钥
|
|
305
|
+
if isinstance(key, str):
|
|
306
|
+
# 字符串密码 -> 使用 PBKDF2 派生密钥
|
|
307
|
+
salt = os.urandom(16)
|
|
308
|
+
kdf = PBKDF2HMAC(
|
|
309
|
+
algorithm=hashes.SHA256(),
|
|
310
|
+
length=32, # AES-256
|
|
311
|
+
salt=salt,
|
|
312
|
+
iterations=100000,
|
|
313
|
+
backend=default_backend(),
|
|
314
|
+
)
|
|
315
|
+
key_bytes = kdf.derive(key.encode('utf-8'))
|
|
316
|
+
key_type = b'P' # PBKDF2 派生的密钥
|
|
317
|
+
else:
|
|
318
|
+
# 直接使用 bytes 密钥
|
|
319
|
+
key_bytes = key
|
|
320
|
+
salt = b''
|
|
321
|
+
key_type = b'R' # 原始密钥
|
|
322
|
+
|
|
323
|
+
# 3. AES-GCM 加密
|
|
324
|
+
nonce = os.urandom(12) # 96 bits = NIST 推荐
|
|
325
|
+
aesgcm = AESGCM(key_bytes)
|
|
326
|
+
ciphertext = aesgcm.encrypt(nonce, plaintext, None)
|
|
327
|
+
|
|
328
|
+
# 4. 打包:[key_type(1)] + [salt_len(1)] + [salt] + [nonce] + [ciphertext]
|
|
329
|
+
# 格式说明:
|
|
330
|
+
# - key_type: 'P' = 密码派生, 'R' = 原始密钥
|
|
331
|
+
# - salt_len: salt 的长度(原始密钥时为 0)
|
|
332
|
+
# - salt: PBKDF2 的盐值(16 字节)
|
|
333
|
+
# - nonce: AES-GCM 的 nonce(12 字节)
|
|
334
|
+
# - ciphertext: 加密数据 + 16 字节认证标签
|
|
335
|
+
salt_len = len(salt)
|
|
336
|
+
return b''.join([
|
|
337
|
+
key_type,
|
|
338
|
+
bytes([salt_len]),
|
|
339
|
+
salt,
|
|
340
|
+
nonce,
|
|
341
|
+
ciphertext,
|
|
342
|
+
])
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def deserialize_encrypted(
|
|
346
|
+
data: bytes,
|
|
347
|
+
key: Union[str, bytes]
|
|
348
|
+
) -> Expression:
|
|
349
|
+
"""
|
|
350
|
+
解密反序列化
|
|
351
|
+
|
|
352
|
+
Args:
|
|
353
|
+
data: 加密的字节数据
|
|
354
|
+
key: 解密密钥(字符串或 bytes)
|
|
355
|
+
|
|
356
|
+
Raises:
|
|
357
|
+
cryptography.exceptions.InvalidTag: 密钥错误或数据被篡改
|
|
358
|
+
"""
|
|
359
|
+
try:
|
|
360
|
+
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
|
361
|
+
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
|
|
362
|
+
from cryptography.hazmat.primitives import hashes
|
|
363
|
+
from cryptography.hazmat.backends import default_backend
|
|
364
|
+
except ImportError:
|
|
365
|
+
raise ImportError(
|
|
366
|
+
"cryptography 未安装。请运行: pip install cryptography"
|
|
367
|
+
) from None
|
|
368
|
+
|
|
369
|
+
# 1. 解包
|
|
370
|
+
key_type = data[0:1]
|
|
371
|
+
salt_len = data[1]
|
|
372
|
+
offset = 2
|
|
373
|
+
|
|
374
|
+
salt = data[offset:offset + salt_len]
|
|
375
|
+
offset += salt_len
|
|
376
|
+
|
|
377
|
+
nonce = data[offset:offset + 12]
|
|
378
|
+
offset += 12
|
|
379
|
+
|
|
380
|
+
ciphertext = data[offset:]
|
|
381
|
+
|
|
382
|
+
# 2. 处理密钥
|
|
383
|
+
if key_type == b'P':
|
|
384
|
+
# PBKDF2 密钥派生
|
|
385
|
+
if not isinstance(key, str):
|
|
386
|
+
raise ValueError("数据使用字符串密码加密,请提供字符串密钥")
|
|
387
|
+
kdf = PBKDF2HMAC(
|
|
388
|
+
algorithm=hashes.SHA256(),
|
|
389
|
+
length=32,
|
|
390
|
+
salt=salt,
|
|
391
|
+
iterations=100000,
|
|
392
|
+
backend=default_backend(),
|
|
393
|
+
)
|
|
394
|
+
key_bytes = kdf.derive(key.encode('utf-8'))
|
|
395
|
+
else:
|
|
396
|
+
# 原始密钥
|
|
397
|
+
if isinstance(key, str):
|
|
398
|
+
raise ValueError("数据使用原始密钥加密,请提供 bytes 密钥")
|
|
399
|
+
key_bytes = key
|
|
400
|
+
|
|
401
|
+
# 3. AES-GCM 解密(自动验证认证标签)
|
|
402
|
+
aesgcm = AESGCM(key_bytes)
|
|
403
|
+
plaintext = aesgcm.decrypt(nonce, ciphertext, None)
|
|
404
|
+
|
|
405
|
+
# 4. 反序列化
|
|
406
|
+
return Expression.deserialize(plaintext)
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
# ============================================================================
|
|
410
|
+
# 节点序列化方案
|
|
411
|
+
# ============================================================================
|
|
412
|
+
|
|
413
|
+
def serialize_node_json(node, indent: Optional[int] = 2) -> str:
|
|
414
|
+
"""
|
|
415
|
+
节点 JSON 序列化
|
|
416
|
+
|
|
417
|
+
Args:
|
|
418
|
+
node: BaseNode 实例
|
|
419
|
+
indent: 缩进空格数,None 表示紧凑格式
|
|
420
|
+
|
|
421
|
+
Returns:
|
|
422
|
+
JSON 字符串
|
|
423
|
+
"""
|
|
424
|
+
return json.dumps(node.serialize(), indent=indent, ensure_ascii=False)
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
def serialize_node_json_bytes(node) -> bytes:
|
|
428
|
+
"""节点 JSON 序列化(字节形式)"""
|
|
429
|
+
return serialize_node_json(node, indent=None).encode('utf-8')
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
def serialize_node_compact(node, compress_level: int = 6) -> bytes:
|
|
433
|
+
"""
|
|
434
|
+
节点紧凑序列化 - 推荐用于网络传输/持久化
|
|
435
|
+
|
|
436
|
+
JSON + zlib 压缩,零额外依赖
|
|
437
|
+
压缩率通常可达 30%-50%
|
|
438
|
+
"""
|
|
439
|
+
json_bytes = serialize_node_json_bytes(node)
|
|
440
|
+
return zlib.compress(json_bytes, level=compress_level)
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
def serialize_node_msgpack(node) -> bytes:
|
|
444
|
+
"""
|
|
445
|
+
节点 msgpack 序列化 - 极致性能
|
|
446
|
+
|
|
447
|
+
要求:pip install msgpack
|
|
448
|
+
"""
|
|
449
|
+
try:
|
|
450
|
+
import msgpack
|
|
451
|
+
except ImportError:
|
|
452
|
+
raise ImportError(
|
|
453
|
+
"msgpack 未安装。请运行: pip install msgpack"
|
|
454
|
+
) from None
|
|
455
|
+
return msgpack.packb(node.serialize(), use_bin_type=True)
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
# ============================================================================
|
|
459
|
+
# 节点反序列化方案
|
|
460
|
+
# ============================================================================
|
|
461
|
+
|
|
462
|
+
def deserialize_node_json(data: Union[str, bytes]):
|
|
463
|
+
"""
|
|
464
|
+
从 JSON 反序列化节点
|
|
465
|
+
|
|
466
|
+
Args:
|
|
467
|
+
data: JSON 字符串或字节
|
|
468
|
+
|
|
469
|
+
Returns:
|
|
470
|
+
BaseNode 实例
|
|
471
|
+
"""
|
|
472
|
+
from QuantNodes.core.node import BaseNode
|
|
473
|
+
|
|
474
|
+
if isinstance(data, bytes):
|
|
475
|
+
data = data.decode('utf-8')
|
|
476
|
+
node_data = json.loads(data)
|
|
477
|
+
return BaseNode.deserialize(node_data)
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
def deserialize_node_compact(data: bytes):
|
|
481
|
+
"""
|
|
482
|
+
从紧凑格式(JSON + zlib)反序列化节点
|
|
483
|
+
|
|
484
|
+
Args:
|
|
485
|
+
data: 压缩的字节数据
|
|
486
|
+
|
|
487
|
+
Returns:
|
|
488
|
+
BaseNode 实例
|
|
489
|
+
"""
|
|
490
|
+
|
|
491
|
+
json_bytes = zlib.decompress(data)
|
|
492
|
+
return deserialize_node_json(json_bytes)
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
def deserialize_node_msgpack(data: bytes):
|
|
496
|
+
"""
|
|
497
|
+
从 msgpack 反序列化节点
|
|
498
|
+
|
|
499
|
+
Args:
|
|
500
|
+
data: msgpack 字节数据
|
|
501
|
+
|
|
502
|
+
Returns:
|
|
503
|
+
BaseNode 实例
|
|
504
|
+
"""
|
|
505
|
+
from QuantNodes.core.node import BaseNode
|
|
506
|
+
|
|
507
|
+
try:
|
|
508
|
+
import msgpack
|
|
509
|
+
except ImportError:
|
|
510
|
+
raise ImportError(
|
|
511
|
+
"msgpack 未安装。请运行: pip install msgpack"
|
|
512
|
+
) from None
|
|
513
|
+
node_data = msgpack.unpackb(data, raw=False)
|
|
514
|
+
return BaseNode.deserialize(node_data)
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
# ============================================================================
|
|
518
|
+
# 节点自动检测反序列化
|
|
519
|
+
# ============================================================================
|
|
520
|
+
|
|
521
|
+
def deserialize_node_auto(data: Union[str, bytes]):
|
|
522
|
+
"""
|
|
523
|
+
自动检测格式并反序列化节点
|
|
524
|
+
|
|
525
|
+
支持:JSON(str/bytes), JSON+zlib(bytes), msgpack(bytes)
|
|
526
|
+
"""
|
|
527
|
+
if isinstance(data, str):
|
|
528
|
+
return deserialize_node_json(data)
|
|
529
|
+
|
|
530
|
+
if not isinstance(data, bytes):
|
|
531
|
+
raise TypeError(f"不支持的数据类型: {type(data)}")
|
|
532
|
+
|
|
533
|
+
# 尝试 zlib 压缩格式
|
|
534
|
+
if len(data) >= 2 and data[0:2] == b'x\x9c':
|
|
535
|
+
try:
|
|
536
|
+
return deserialize_node_compact(data)
|
|
537
|
+
except Exception:
|
|
538
|
+
pass
|
|
539
|
+
|
|
540
|
+
# 尝试 msgpack (第一个字节通常是 map 标记 0x80-0x8f)
|
|
541
|
+
if data and 0x80 <= data[0] <= 0x8f:
|
|
542
|
+
try:
|
|
543
|
+
return deserialize_node_msgpack(data)
|
|
544
|
+
except Exception:
|
|
545
|
+
pass
|
|
546
|
+
|
|
547
|
+
# 尝试 JSON
|
|
548
|
+
return deserialize_node_json(data)
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
# ============================================================================
|
|
552
|
+
# 节点加密序列化
|
|
553
|
+
# ============================================================================
|
|
554
|
+
|
|
555
|
+
def serialize_node_encrypted(node, key: Union[str, bytes]) -> bytes:
|
|
556
|
+
"""
|
|
557
|
+
节点加密序列化 - 保护敏感策略
|
|
558
|
+
|
|
559
|
+
Args:
|
|
560
|
+
node: BaseNode 实例
|
|
561
|
+
key: 加密密钥(字符串或 bytes)
|
|
562
|
+
|
|
563
|
+
要求:pip install cryptography
|
|
564
|
+
"""
|
|
565
|
+
try:
|
|
566
|
+
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
|
567
|
+
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
|
|
568
|
+
from cryptography.hazmat.primitives import hashes
|
|
569
|
+
from cryptography.hazmat.backends import default_backend
|
|
570
|
+
except ImportError:
|
|
571
|
+
raise ImportError(
|
|
572
|
+
"cryptography 未安装。请运行: pip install cryptography"
|
|
573
|
+
) from None
|
|
574
|
+
|
|
575
|
+
import os
|
|
576
|
+
|
|
577
|
+
# 1. 先序列化为紧凑 JSON
|
|
578
|
+
plaintext = node.serialize()
|
|
579
|
+
plaintext_bytes = json.dumps(plaintext, separators=(',', ':')).encode('utf-8')
|
|
580
|
+
|
|
581
|
+
# 2. 处理密钥
|
|
582
|
+
if isinstance(key, str):
|
|
583
|
+
salt = os.urandom(16)
|
|
584
|
+
kdf = PBKDF2HMAC(
|
|
585
|
+
algorithm=hashes.SHA256(),
|
|
586
|
+
length=32,
|
|
587
|
+
salt=salt,
|
|
588
|
+
iterations=100000,
|
|
589
|
+
backend=default_backend(),
|
|
590
|
+
)
|
|
591
|
+
key_bytes = kdf.derive(key.encode('utf-8'))
|
|
592
|
+
key_type = b'P'
|
|
593
|
+
else:
|
|
594
|
+
key_bytes = key
|
|
595
|
+
salt = b''
|
|
596
|
+
key_type = b'R'
|
|
597
|
+
|
|
598
|
+
# 3. AES-GCM 加密
|
|
599
|
+
nonce = os.urandom(12)
|
|
600
|
+
aesgcm = AESGCM(key_bytes)
|
|
601
|
+
ciphertext = aesgcm.encrypt(nonce, plaintext_bytes, None)
|
|
602
|
+
|
|
603
|
+
# 4. 打包
|
|
604
|
+
salt_len = len(salt)
|
|
605
|
+
return b''.join([
|
|
606
|
+
key_type,
|
|
607
|
+
bytes([salt_len]),
|
|
608
|
+
salt,
|
|
609
|
+
nonce,
|
|
610
|
+
ciphertext,
|
|
611
|
+
])
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
def deserialize_node_encrypted(data: bytes, key: Union[str, bytes]):
|
|
615
|
+
"""
|
|
616
|
+
解密反序列化节点
|
|
617
|
+
|
|
618
|
+
Args:
|
|
619
|
+
data: 加密的字节数据
|
|
620
|
+
key: 解密密钥
|
|
621
|
+
"""
|
|
622
|
+
try:
|
|
623
|
+
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
|
624
|
+
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
|
|
625
|
+
from cryptography.hazmat.primitives import hashes
|
|
626
|
+
from cryptography.hazmat.backends import default_backend
|
|
627
|
+
except ImportError:
|
|
628
|
+
raise ImportError(
|
|
629
|
+
"cryptography 未安装。请运行: pip install cryptography"
|
|
630
|
+
) from None
|
|
631
|
+
|
|
632
|
+
# 1. 解包
|
|
633
|
+
key_type = data[0:1]
|
|
634
|
+
salt_len = data[1]
|
|
635
|
+
offset = 2
|
|
636
|
+
|
|
637
|
+
salt = data[offset:offset + salt_len]
|
|
638
|
+
offset += salt_len
|
|
639
|
+
|
|
640
|
+
nonce = data[offset:offset + 12]
|
|
641
|
+
offset += 12
|
|
642
|
+
|
|
643
|
+
ciphertext = data[offset:]
|
|
644
|
+
|
|
645
|
+
# 2. 处理密钥
|
|
646
|
+
if key_type == b'P':
|
|
647
|
+
if not isinstance(key, str):
|
|
648
|
+
raise ValueError("数据使用字符串密码加密,请提供字符串密钥")
|
|
649
|
+
kdf = PBKDF2HMAC(
|
|
650
|
+
algorithm=hashes.SHA256(),
|
|
651
|
+
length=32,
|
|
652
|
+
salt=salt,
|
|
653
|
+
iterations=100000,
|
|
654
|
+
backend=default_backend(),
|
|
655
|
+
)
|
|
656
|
+
key_bytes = kdf.derive(key.encode('utf-8'))
|
|
657
|
+
else:
|
|
658
|
+
if isinstance(key, str):
|
|
659
|
+
raise ValueError("数据使用原始密钥加密,请提供 bytes 密钥")
|
|
660
|
+
key_bytes = key
|
|
661
|
+
|
|
662
|
+
# 3. AES-GCM 解密
|
|
663
|
+
aesgcm = AESGCM(key_bytes)
|
|
664
|
+
plaintext = aesgcm.decrypt(nonce, ciphertext, None)
|
|
665
|
+
|
|
666
|
+
# 4. 反序列化
|
|
667
|
+
return deserialize_node_json(plaintext)
|
|
668
|
+
|
|
669
|
+
|
|
670
|
+
# 注:原 _update_expression_serialization() 已删除 (Phase F, 2026-06-20)。
|
|
671
|
+
# 该函数原本通过 monkey patching 给 Expression 类注入 to_proto/from_proto 等方法,
|
|
672
|
+
# 已被 expression.py 中的直接实现取代 (expression.py:159-179), 无外部 caller。
|
|
673
|
+
# 详见 git log: 历史 commit 9b8a64c 引入后, 2026-05 改写为直接方法。
|