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,140 @@
|
|
|
1
|
+
"""Quality Gate 拦截率柱状图 — Plotly bar chart."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from collections import defaultdict
|
|
5
|
+
from typing import Any, Mapping
|
|
6
|
+
|
|
7
|
+
# v3.0.0 graceful degradation: if plotly is not installed, the figure
|
|
8
|
+
# functions return None. The HTML generators downstream skip None
|
|
9
|
+
# figures with a friendly install hint.
|
|
10
|
+
try:
|
|
11
|
+
import plotly.graph_objects as _PLOTLY_GO
|
|
12
|
+
except ImportError:
|
|
13
|
+
_PLOTLY_GO = None
|
|
14
|
+
|
|
15
|
+
import warnings
|
|
16
|
+
warnings.warn(
|
|
17
|
+
"plotly not installed; visualization figures will be skipped. "
|
|
18
|
+
"Install plotly for full rendering: pip install plotly",
|
|
19
|
+
ImportWarning,
|
|
20
|
+
stacklevel=1,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def gate_breakdown_figure(
|
|
26
|
+
entries,
|
|
27
|
+
title: str | None = None,
|
|
28
|
+
) -> Any:
|
|
29
|
+
"""统计每种 quality gate 通道的拦截率, 生成柱状图。
|
|
30
|
+
|
|
31
|
+
拦截定义: feedback.decision = False (任一通道失败)
|
|
32
|
+
按 channel 细分: CODE / VALUE / LLM (来自 FactorFeedback.channels)
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
entries: TrajectoryEntry 列表
|
|
36
|
+
title: 标题
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
plotly.graph_objects.Figure, or None if plotly is not installed.
|
|
40
|
+
"""
|
|
41
|
+
if _PLOTLY_GO is None:
|
|
42
|
+
return None
|
|
43
|
+
go = _PLOTLY_GO
|
|
44
|
+
|
|
45
|
+
items = list(entries.values() if isinstance(entries, Mapping) else entries)
|
|
46
|
+
if not items:
|
|
47
|
+
fig = go.Figure()
|
|
48
|
+
fig.update_layout(title="空 pool")
|
|
49
|
+
return fig
|
|
50
|
+
|
|
51
|
+
# 统计每通道 pass / fail
|
|
52
|
+
from ..feedback import FeedbackChannel
|
|
53
|
+
channel_stats: dict[str, dict[str, int]] = {
|
|
54
|
+
ch.value: {"pass": 0, "fail": 0} for ch in FeedbackChannel
|
|
55
|
+
}
|
|
56
|
+
for e in items:
|
|
57
|
+
if not e.feedback or not e.feedback.channels:
|
|
58
|
+
continue
|
|
59
|
+
for ch, fb in e.feedback.channels.items():
|
|
60
|
+
key = ch.value
|
|
61
|
+
if key not in channel_stats:
|
|
62
|
+
continue
|
|
63
|
+
if fb.passed:
|
|
64
|
+
channel_stats[key]["pass"] += 1
|
|
65
|
+
else:
|
|
66
|
+
channel_stats[key]["fail"] += 1
|
|
67
|
+
|
|
68
|
+
channels = sorted(channel_stats.keys())
|
|
69
|
+
pass_counts = [channel_stats[ch]["pass"] for ch in channels]
|
|
70
|
+
fail_counts = [channel_stats[ch]["fail"] for ch in channels]
|
|
71
|
+
|
|
72
|
+
fig = go.Figure()
|
|
73
|
+
fig.add_trace(go.Bar(
|
|
74
|
+
x=channels, y=pass_counts,
|
|
75
|
+
name="pass", marker_color="#54A24B",
|
|
76
|
+
hovertemplate="%{x}<br>pass=%{y}<extra></extra>",
|
|
77
|
+
))
|
|
78
|
+
fig.add_trace(go.Bar(
|
|
79
|
+
x=channels, y=fail_counts,
|
|
80
|
+
name="fail", marker_color="#E45756",
|
|
81
|
+
hovertemplate="%{x}<br>fail=%{y}<extra></extra>",
|
|
82
|
+
))
|
|
83
|
+
|
|
84
|
+
fig.update_layout(
|
|
85
|
+
title=title or "Quality Gate 拦截率 (按通道)",
|
|
86
|
+
barmode="stack",
|
|
87
|
+
xaxis_title="channel",
|
|
88
|
+
yaxis_title="count",
|
|
89
|
+
height=400,
|
|
90
|
+
plot_bgcolor="white",
|
|
91
|
+
)
|
|
92
|
+
return fig
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def operation_breakdown_figure(
|
|
96
|
+
entries,
|
|
97
|
+
title: str | None = None,
|
|
98
|
+
) -> Any:
|
|
99
|
+
"""按 operation 分类的成功率柱状图。"""
|
|
100
|
+
if _PLOTLY_GO is None:
|
|
101
|
+
return None
|
|
102
|
+
go = _PLOTLY_GO
|
|
103
|
+
|
|
104
|
+
items = list(entries.values() if isinstance(entries, Mapping) else entries)
|
|
105
|
+
if not items:
|
|
106
|
+
fig = go.Figure()
|
|
107
|
+
fig.update_layout(title="空 pool")
|
|
108
|
+
return fig
|
|
109
|
+
|
|
110
|
+
stats: dict[str, dict[str, int]] = defaultdict(lambda: {"pass": 0, "fail": 0})
|
|
111
|
+
for e in items:
|
|
112
|
+
decision = bool(e.feedback.decision) if e.feedback else False
|
|
113
|
+
key = "pass" if decision else "fail"
|
|
114
|
+
stats[e.operation][key] += 1
|
|
115
|
+
|
|
116
|
+
ops = sorted(stats.keys())
|
|
117
|
+
pass_counts = [stats[o]["pass"] for o in ops]
|
|
118
|
+
fail_counts = [stats[o]["fail"] for o in ops]
|
|
119
|
+
|
|
120
|
+
fig = go.Figure()
|
|
121
|
+
fig.add_trace(go.Bar(
|
|
122
|
+
x=ops, y=pass_counts,
|
|
123
|
+
name="pass", marker_color="#54A24B",
|
|
124
|
+
text=[str(c) for c in pass_counts], textposition="inside",
|
|
125
|
+
))
|
|
126
|
+
fig.add_trace(go.Bar(
|
|
127
|
+
x=ops, y=fail_counts,
|
|
128
|
+
name="fail", marker_color="#E45756",
|
|
129
|
+
text=[str(c) for c in fail_counts], textposition="inside",
|
|
130
|
+
))
|
|
131
|
+
|
|
132
|
+
fig.update_layout(
|
|
133
|
+
title=title or "Operation 通过率",
|
|
134
|
+
barmode="stack",
|
|
135
|
+
xaxis_title="operation",
|
|
136
|
+
yaxis_title="count",
|
|
137
|
+
height=400,
|
|
138
|
+
plot_bgcolor="white",
|
|
139
|
+
)
|
|
140
|
+
return fig
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"""谱系 DAG 布局 — BFS 分层 + 节点定位。
|
|
2
|
+
|
|
3
|
+
返回:
|
|
4
|
+
- nodes: list of (entry_id, x, y, label, color, metadata)
|
|
5
|
+
- edges: list of (parent_id, child_id)
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from collections import defaultdict
|
|
10
|
+
from typing import Any, Mapping
|
|
11
|
+
|
|
12
|
+
from ..trajectory import TrajectoryEntry, Operation
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# 节点颜色 (按 operation) — M1: 改用 Operation enum 自动同步
|
|
16
|
+
_OPERATION_COLORS = {
|
|
17
|
+
Operation.ORIGINAL.value: "#4C78A8",
|
|
18
|
+
Operation.MUTATION.value: "#F58518",
|
|
19
|
+
Operation.CROSSOVER.value: "#E45756",
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def build_lineage_layout(
|
|
24
|
+
entries: Mapping[str, TrajectoryEntry] | list[TrajectoryEntry],
|
|
25
|
+
metric: str = "sharpe",
|
|
26
|
+
) -> dict[str, Any]:
|
|
27
|
+
"""构造谱系 DAG 布局。
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
entries: TrajectoryEntry 列表或 dict
|
|
31
|
+
metric: 用于节点大小/颜色的指标 (默认 sharpe)
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
dict: {
|
|
35
|
+
'nodes': [{
|
|
36
|
+
'id', 'x', 'y', 'label', 'color', 'size',
|
|
37
|
+
'metric', 'operation', 'round_idx',
|
|
38
|
+
}, ...],
|
|
39
|
+
'edges': [{'source', 'target', 'color'}, ...],
|
|
40
|
+
}
|
|
41
|
+
"""
|
|
42
|
+
items = list(entries.values() if isinstance(entries, Mapping) else entries)
|
|
43
|
+
if not items:
|
|
44
|
+
return {"nodes": [], "edges": []}
|
|
45
|
+
|
|
46
|
+
# 1. 分层: BFS 找每个节点的 round_idx (depth)
|
|
47
|
+
depths: dict[str, int] = {}
|
|
48
|
+
for e in items:
|
|
49
|
+
if not e.parent_ids:
|
|
50
|
+
depths[e.entry_id] = 0
|
|
51
|
+
else:
|
|
52
|
+
# 找最深 parent + 1
|
|
53
|
+
max_parent_depth = -1
|
|
54
|
+
for pid in e.parent_ids:
|
|
55
|
+
if pid in depths:
|
|
56
|
+
max_parent_depth = max(max_parent_depth, depths[pid])
|
|
57
|
+
depths[e.entry_id] = max_parent_depth + 1
|
|
58
|
+
|
|
59
|
+
# 2. 按 depth 分组
|
|
60
|
+
by_depth: dict[int, list[TrajectoryEntry]] = defaultdict(list)
|
|
61
|
+
for e in items:
|
|
62
|
+
by_depth[depths[e.entry_id]].append(e)
|
|
63
|
+
|
|
64
|
+
# 3. 节点布局: x = index in layer, y = depth
|
|
65
|
+
nodes = []
|
|
66
|
+
for depth in sorted(by_depth.keys()):
|
|
67
|
+
layer = by_depth[depth]
|
|
68
|
+
for x_idx, e in enumerate(layer):
|
|
69
|
+
metric_val = float((e.metrics or {}).get(metric, 0) or 0)
|
|
70
|
+
op = e.operation
|
|
71
|
+
color = _OPERATION_COLORS.get(op, "#999999")
|
|
72
|
+
# 节点大小: 按 metric (min 10, max 30)
|
|
73
|
+
size = 10 + min(20, max(0, metric_val) * 4)
|
|
74
|
+
name = e.feedback.factor_name if e.feedback else e.entry_id[:8]
|
|
75
|
+
decision = e.feedback.decision if e.feedback else False
|
|
76
|
+
nodes.append({
|
|
77
|
+
"id": e.entry_id,
|
|
78
|
+
"x": x_idx,
|
|
79
|
+
"y": depth,
|
|
80
|
+
"label": f"{name}<br>r{depth} {op}<br>{metric}={metric_val:.2f}",
|
|
81
|
+
"color": color,
|
|
82
|
+
"size": size,
|
|
83
|
+
"metric": metric_val,
|
|
84
|
+
"operation": op,
|
|
85
|
+
"round_idx": depth,
|
|
86
|
+
"decision": decision,
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
# 4. 边: parent_id → child_id
|
|
90
|
+
edges = []
|
|
91
|
+
by_id = {e.entry_id: e for e in items}
|
|
92
|
+
for e in items:
|
|
93
|
+
for pid in e.parent_ids:
|
|
94
|
+
if pid in by_id:
|
|
95
|
+
parent_op = by_id[pid].operation
|
|
96
|
+
edges.append({
|
|
97
|
+
"source": pid,
|
|
98
|
+
"target": e.entry_id,
|
|
99
|
+
"color": _OPERATION_COLORS.get(parent_op, "#999999"),
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
return {"nodes": nodes, "edges": edges}
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def lineage_dag_figure(entries, metric: str = "sharpe", title: str | None = None) -> Any:
|
|
106
|
+
"""生成 Plotly Figure (交互式谱系 DAG)。
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
entries: TrajectoryEntry 列表
|
|
110
|
+
metric: 用于节点大小/颜色的指标
|
|
111
|
+
title: 图标题
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
plotly.graph_objects.Figure, or None if plotly is not installed.
|
|
115
|
+
"""
|
|
116
|
+
# v3.0.0 graceful degradation: return None when plotly is unavailable.
|
|
117
|
+
try:
|
|
118
|
+
import plotly.graph_objects as go
|
|
119
|
+
except ImportError:
|
|
120
|
+
import warnings
|
|
121
|
+
warnings.warn(
|
|
122
|
+
"plotly not installed; lineage_dag_figure will return None. "
|
|
123
|
+
"Install plotly: pip install plotly",
|
|
124
|
+
ImportWarning,
|
|
125
|
+
stacklevel=2,
|
|
126
|
+
)
|
|
127
|
+
return None
|
|
128
|
+
|
|
129
|
+
layout = build_lineage_layout(entries, metric=metric)
|
|
130
|
+
if not layout["nodes"]:
|
|
131
|
+
fig = go.Figure()
|
|
132
|
+
fig.update_layout(title="空 pool: 无 entry 可视化")
|
|
133
|
+
return fig
|
|
134
|
+
|
|
135
|
+
# 节点: scatter (x, y)
|
|
136
|
+
node_x = [n["x"] for n in layout["nodes"]]
|
|
137
|
+
node_y = [n["y"] for n in layout["nodes"]]
|
|
138
|
+
node_color = [n["color"] for n in layout["nodes"]]
|
|
139
|
+
node_size = [n["size"] for n in layout["nodes"]]
|
|
140
|
+
node_custom = [n for n in layout["nodes"]]
|
|
141
|
+
|
|
142
|
+
# 边: 通过 (source_x, source_y) → (target_x, target_y) 划线
|
|
143
|
+
by_id = {n["id"]: n for n in layout["nodes"]}
|
|
144
|
+
edge_x: list[float | None] = []
|
|
145
|
+
edge_y: list[float | None] = []
|
|
146
|
+
for e in layout["edges"]:
|
|
147
|
+
src = by_id.get(e["source"])
|
|
148
|
+
tgt = by_id.get(e["target"])
|
|
149
|
+
if src is None or tgt is None:
|
|
150
|
+
continue
|
|
151
|
+
edge_x.extend([src["x"], tgt["x"], None])
|
|
152
|
+
edge_y.extend([src["y"], tgt["y"], None])
|
|
153
|
+
|
|
154
|
+
fig = go.Figure()
|
|
155
|
+
|
|
156
|
+
# 边先画 (在底层)
|
|
157
|
+
if edge_x:
|
|
158
|
+
fig.add_trace(go.Scatter(
|
|
159
|
+
x=edge_x,
|
|
160
|
+
y=edge_y,
|
|
161
|
+
mode="lines",
|
|
162
|
+
line=dict(width=1.5, color="#888"),
|
|
163
|
+
hoverinfo="skip",
|
|
164
|
+
showlegend=False,
|
|
165
|
+
))
|
|
166
|
+
|
|
167
|
+
# 节点
|
|
168
|
+
fig.add_trace(go.Scatter(
|
|
169
|
+
x=node_x,
|
|
170
|
+
y=node_y,
|
|
171
|
+
mode="markers+text",
|
|
172
|
+
marker=dict(
|
|
173
|
+
size=node_size,
|
|
174
|
+
color=node_color,
|
|
175
|
+
line=dict(width=2, color="#222"),
|
|
176
|
+
opacity=0.85,
|
|
177
|
+
),
|
|
178
|
+
text=[n["label"] for n in layout["nodes"]],
|
|
179
|
+
textposition="top center",
|
|
180
|
+
hovertemplate="%{text}<extra></extra>",
|
|
181
|
+
customdata=node_custom,
|
|
182
|
+
showlegend=False,
|
|
183
|
+
))
|
|
184
|
+
|
|
185
|
+
# 图例 (operation 颜色)
|
|
186
|
+
for op, color in _OPERATION_COLORS.items():
|
|
187
|
+
fig.add_trace(go.Scatter(
|
|
188
|
+
x=[None], y=[None],
|
|
189
|
+
mode="markers",
|
|
190
|
+
marker=dict(size=12, color=color),
|
|
191
|
+
name=op,
|
|
192
|
+
showlegend=True,
|
|
193
|
+
))
|
|
194
|
+
|
|
195
|
+
fig.update_layout(
|
|
196
|
+
title=title or f"演化谱系 DAG (按 {metric})",
|
|
197
|
+
xaxis=dict(title="layer index", showgrid=False, zeroline=False),
|
|
198
|
+
yaxis=dict(title="round (depth)", autorange="reversed", showgrid=False, zeroline=False),
|
|
199
|
+
hovermode="closest",
|
|
200
|
+
height=600,
|
|
201
|
+
plot_bgcolor="white",
|
|
202
|
+
)
|
|
203
|
+
return fig
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"""指标分布直方图 — Plotly histogram。"""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import Any, Mapping
|
|
5
|
+
|
|
6
|
+
# v3.0.0 graceful degradation: if plotly is not installed, the figure
|
|
7
|
+
# functions return None.
|
|
8
|
+
try:
|
|
9
|
+
import plotly.graph_objects as _PLOTLY_GO
|
|
10
|
+
except ImportError:
|
|
11
|
+
_PLOTLY_GO = None
|
|
12
|
+
|
|
13
|
+
import warnings
|
|
14
|
+
warnings.warn(
|
|
15
|
+
"plotly not installed; metric_distribution_figure will return None. "
|
|
16
|
+
"Install plotly: pip install plotly",
|
|
17
|
+
ImportWarning,
|
|
18
|
+
stacklevel=1,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def metric_distribution_figure(
|
|
24
|
+
entries,
|
|
25
|
+
metric: str = "sharpe",
|
|
26
|
+
title: str | None = None,
|
|
27
|
+
n_bins: int = 20,
|
|
28
|
+
) -> Any:
|
|
29
|
+
"""生成 Plotly histogram (按 metric 分桶, 颜色按 operation)。
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
entries: TrajectoryEntry 列表
|
|
33
|
+
metric: 指标名
|
|
34
|
+
title: 标题
|
|
35
|
+
n_bins: 直方图桶数
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
plotly.graph_objects.Figure, or None if plotly is not installed.
|
|
39
|
+
"""
|
|
40
|
+
if _PLOTLY_GO is None:
|
|
41
|
+
return None
|
|
42
|
+
go = _PLOTLY_GO
|
|
43
|
+
|
|
44
|
+
items = list(entries.values() if isinstance(entries, Mapping) else entries)
|
|
45
|
+
# 按 operation 分组
|
|
46
|
+
by_op: dict[str, list[float]] = {}
|
|
47
|
+
for e in items:
|
|
48
|
+
val = (e.metrics or {}).get(metric)
|
|
49
|
+
if val is None:
|
|
50
|
+
continue
|
|
51
|
+
op = e.operation
|
|
52
|
+
by_op.setdefault(op, []).append(float(val))
|
|
53
|
+
|
|
54
|
+
if not by_op:
|
|
55
|
+
fig = go.Figure()
|
|
56
|
+
fig.update_layout(title=f"无 {metric} 指标数据")
|
|
57
|
+
return fig
|
|
58
|
+
|
|
59
|
+
fig = go.Figure()
|
|
60
|
+
for op, vals in sorted(by_op.items()):
|
|
61
|
+
fig.add_trace(go.Histogram(
|
|
62
|
+
x=vals,
|
|
63
|
+
name=op,
|
|
64
|
+
opacity=0.7,
|
|
65
|
+
xbins=dict(size=(max(vals) - min(vals)) / n_bins if max(vals) > min(vals) else 1),
|
|
66
|
+
hovertemplate=f"{op}<br>{metric}=%{{x:.4f}}<br>count=%{{y}}<extra></extra>",
|
|
67
|
+
))
|
|
68
|
+
|
|
69
|
+
fig.update_layout(
|
|
70
|
+
title=title or f"{metric} 分布 (按 operation 着色)",
|
|
71
|
+
barmode="overlay",
|
|
72
|
+
xaxis_title=metric,
|
|
73
|
+
yaxis_title="count",
|
|
74
|
+
height=450,
|
|
75
|
+
plot_bgcolor="white",
|
|
76
|
+
)
|
|
77
|
+
return fig
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def metric_per_round_figure(
|
|
81
|
+
entries,
|
|
82
|
+
metric: str = "sharpe",
|
|
83
|
+
title: str | None = None,
|
|
84
|
+
) -> Any:
|
|
85
|
+
"""每轮 best metric 趋势线 (line chart)。"""
|
|
86
|
+
if _PLOTLY_GO is None:
|
|
87
|
+
return None
|
|
88
|
+
go = _PLOTLY_GO
|
|
89
|
+
|
|
90
|
+
items = list(entries.values() if isinstance(entries, Mapping) else entries)
|
|
91
|
+
if not items:
|
|
92
|
+
fig = go.Figure()
|
|
93
|
+
fig.update_layout(title="空 pool")
|
|
94
|
+
return fig
|
|
95
|
+
|
|
96
|
+
# 按 round 分组, 取每轮 max
|
|
97
|
+
by_round: dict[int, list[float]] = {}
|
|
98
|
+
for e in items:
|
|
99
|
+
val = (e.metrics or {}).get(metric)
|
|
100
|
+
if val is None:
|
|
101
|
+
continue
|
|
102
|
+
by_round.setdefault(e.round_idx, []).append(float(val))
|
|
103
|
+
|
|
104
|
+
rounds = sorted(by_round.keys())
|
|
105
|
+
bests = [max(by_round[r]) for r in rounds]
|
|
106
|
+
means = [sum(by_round[r]) / len(by_round[r]) for r in rounds]
|
|
107
|
+
|
|
108
|
+
fig = go.Figure()
|
|
109
|
+
fig.add_trace(go.Scatter(
|
|
110
|
+
x=rounds, y=bests, mode="lines+markers",
|
|
111
|
+
name=f"best {metric}", line=dict(color="green", width=3),
|
|
112
|
+
))
|
|
113
|
+
fig.add_trace(go.Scatter(
|
|
114
|
+
x=rounds, y=means, mode="lines+markers",
|
|
115
|
+
name=f"mean {metric}", line=dict(color="blue", width=2, dash="dash"),
|
|
116
|
+
))
|
|
117
|
+
|
|
118
|
+
fig.update_layout(
|
|
119
|
+
title=title or f"{metric} per round 趋势",
|
|
120
|
+
xaxis_title="round",
|
|
121
|
+
yaxis_title=metric,
|
|
122
|
+
height=400,
|
|
123
|
+
plot_bgcolor="white",
|
|
124
|
+
)
|
|
125
|
+
return fig
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""Visualization report — generate_html() 主入口, 拼接 3 图 + 概览表。
|
|
2
|
+
|
|
3
|
+
Phase 1.3: 内部委托给 ReportBuilder, 保留向后兼容 API。
|
|
4
|
+
新代码推荐使用 ReportBuilder 流式 API。
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any, Mapping
|
|
10
|
+
|
|
11
|
+
from QuantNodes.core.trajectory.entry import TrajectoryEntry
|
|
12
|
+
|
|
13
|
+
from .builder import ReportBuilder
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def generate_report(
|
|
17
|
+
entries: list[TrajectoryEntry] | Mapping[str, TrajectoryEntry],
|
|
18
|
+
metric: str = "sharpe",
|
|
19
|
+
title: str = "QuantNodes 演化实验报告",
|
|
20
|
+
) -> dict[str, Any]:
|
|
21
|
+
"""生成 4 个 Plotly Figure (不输出 HTML)。
|
|
22
|
+
|
|
23
|
+
Phase 1.3: 内部委托 ReportBuilder.with_evolve_preset()。
|
|
24
|
+
返回格式保持向后兼容 (dict with overview + figure keys)。
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
dict: {
|
|
28
|
+
'overview': {...},
|
|
29
|
+
'lineage_dag': go.Figure,
|
|
30
|
+
'metric_distribution': go.Figure,
|
|
31
|
+
'metric_per_round': go.Figure,
|
|
32
|
+
'gate_breakdown': go.Figure,
|
|
33
|
+
'operation_breakdown': go.Figure,
|
|
34
|
+
}
|
|
35
|
+
"""
|
|
36
|
+
builder = (
|
|
37
|
+
ReportBuilder()
|
|
38
|
+
.with_title(title)
|
|
39
|
+
.with_evolve_preset(entries, metric=metric)
|
|
40
|
+
)
|
|
41
|
+
return builder.build().to_dict()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def generate_html(
|
|
45
|
+
entries,
|
|
46
|
+
metric: str = "sharpe",
|
|
47
|
+
title: str = "QuantNodes 演化实验报告",
|
|
48
|
+
output_path: str | Path | None = None,
|
|
49
|
+
) -> str:
|
|
50
|
+
"""生成完整 HTML 报告, 含 4 个交互图 + 概览表。
|
|
51
|
+
|
|
52
|
+
Phase 1.3: 内部委托 ReportBuilder.build_to_html()。
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
entries: TrajectoryEntry 列表
|
|
56
|
+
metric: 用于可视化的指标
|
|
57
|
+
title: 报告标题
|
|
58
|
+
output_path: 若指定, 写入文件; 否则返回字符串
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
str: HTML 内容 (若 output_path=None)
|
|
62
|
+
"""
|
|
63
|
+
return (
|
|
64
|
+
ReportBuilder()
|
|
65
|
+
.with_title(title)
|
|
66
|
+
.with_evolve_preset(entries, metric=metric)
|
|
67
|
+
.build_to_html(output_path=output_path)
|
|
68
|
+
)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""database_node - 数据库节点统一入口
|
|
3
|
+
|
|
4
|
+
支持 SQLite, DuckDB, MySQL, ClickHouse, CSV, Parquet 等数据源
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
from QuantNodes.database_node import (
|
|
8
|
+
SQLiteNode,
|
|
9
|
+
DuckDBNode,
|
|
10
|
+
MySQLNode,
|
|
11
|
+
ClickHouseNode,
|
|
12
|
+
CSVNode,
|
|
13
|
+
ParquetNode,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
# SQLite 内存模式
|
|
17
|
+
sqlite = SQLiteNode(":memory:")
|
|
18
|
+
sqlite.connect()
|
|
19
|
+
|
|
20
|
+
# DuckDB 文件模式
|
|
21
|
+
duckdb = DuckDBNode("/data/analysis.duckdb")
|
|
22
|
+
|
|
23
|
+
# MySQL 带连接池
|
|
24
|
+
mysql = MySQLNode(
|
|
25
|
+
host="localhost",
|
|
26
|
+
user="root",
|
|
27
|
+
passwd="password",
|
|
28
|
+
db="mydb",
|
|
29
|
+
pool_size=20
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
# ClickHouse HTTP 接口
|
|
33
|
+
ch = ClickHouseNode(
|
|
34
|
+
host="localhost",
|
|
35
|
+
user="default",
|
|
36
|
+
passwd="",
|
|
37
|
+
database="default"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
# CSV 读取
|
|
41
|
+
csv = CSVNode("/data/users.csv")
|
|
42
|
+
df = csv.query("SELECT * WHERE age > 18")
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
from QuantNodes.database_node.base import BaseDBNode
|
|
46
|
+
from QuantNodes.database_node.sqlite_node import SQLiteNode
|
|
47
|
+
from QuantNodes.database_node.duckdb_node import DuckDBNode
|
|
48
|
+
from QuantNodes.database_node.mysql_node import MySQLNode
|
|
49
|
+
from QuantNodes.database_node.clickhouse_node import ClickHouseNode
|
|
50
|
+
from QuantNodes.database_node.csv_node import CSVNode
|
|
51
|
+
from QuantNodes.database_node.parquet_node import ParquetNode
|
|
52
|
+
from QuantNodes.database_node.factory import (
|
|
53
|
+
create_db_node,
|
|
54
|
+
register_db_node,
|
|
55
|
+
available_sources,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
__all__ = [
|
|
59
|
+
'BaseDBNode',
|
|
60
|
+
'SQLiteNode',
|
|
61
|
+
'DuckDBNode',
|
|
62
|
+
'MySQLNode',
|
|
63
|
+
'ClickHouseNode',
|
|
64
|
+
'CSVNode',
|
|
65
|
+
'ParquetNode',
|
|
66
|
+
'create_db_node',
|
|
67
|
+
'register_db_node',
|
|
68
|
+
'available_sources',
|
|
69
|
+
]
|