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,60 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
"""
|
|
3
|
+
QuantNodes 对象基类
|
|
4
|
+
|
|
5
|
+
使用 dataclass 替代 traits.HasTraits
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
from typing import Any, Dict
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class QuantNodesObject:
|
|
15
|
+
"""
|
|
16
|
+
QuantNodes 基础对象类
|
|
17
|
+
|
|
18
|
+
使用 dataclass 提供配置属性系统
|
|
19
|
+
|
|
20
|
+
Attributes:
|
|
21
|
+
name: 对象名称
|
|
22
|
+
config: 配置字典
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
name: str = "QuantNodesObject"
|
|
26
|
+
config: Dict[str, Any] = field(default_factory=dict)
|
|
27
|
+
_logger: logging.Logger = field(default=None, repr=False)
|
|
28
|
+
|
|
29
|
+
def __post_init__(self):
|
|
30
|
+
if self._logger is None:
|
|
31
|
+
self._logger = logging.getLogger(f"QuantNodes.{self.__class__.__name__}")
|
|
32
|
+
|
|
33
|
+
def get_config(self, key: str, default: Any = None) -> Any:
|
|
34
|
+
"""
|
|
35
|
+
获取配置值
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
key: 配置键
|
|
39
|
+
default: 默认值
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
配置值
|
|
43
|
+
"""
|
|
44
|
+
return self.config.get(key, default)
|
|
45
|
+
|
|
46
|
+
def set_config(self, key: str, value: Any) -> None:
|
|
47
|
+
"""
|
|
48
|
+
设置配置值
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
key: 配置键
|
|
52
|
+
value: 配置值
|
|
53
|
+
"""
|
|
54
|
+
self.config[key] = value
|
|
55
|
+
|
|
56
|
+
def __repr__(self) -> str:
|
|
57
|
+
return f"<{self.__class__.__name__}: {self.name}>"
|
|
58
|
+
|
|
59
|
+
def __str__(self) -> str:
|
|
60
|
+
return self.__repr__()
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
"""QuantNodes MCP server.
|
|
3
|
+
|
|
4
|
+
Exposes the 8 core quant tools as MCP (Model Context Protocol) tools so
|
|
5
|
+
they can be invoked by:
|
|
6
|
+
- HKUDS/nanobot 0.2.1 (via nanobot_config.json mcpServers entry)
|
|
7
|
+
- Any MCP client (Claude Desktop, Cursor, VSCode, ...)
|
|
8
|
+
- A second nanobot instance pointing at this server
|
|
9
|
+
|
|
10
|
+
Run as stdio (default for nanobot)::
|
|
11
|
+
|
|
12
|
+
python -m QuantNodes.mcp_server
|
|
13
|
+
|
|
14
|
+
Run with HTTP transport (for testing / inspector)::
|
|
15
|
+
|
|
16
|
+
python -m QuantNodes.mcp_server --transport http --port 8765
|
|
17
|
+
|
|
18
|
+
Inspect with MCP devtools::
|
|
19
|
+
|
|
20
|
+
mcp dev QuantNodes.mcp_server.server:app
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from .server import app, mcp, main
|
|
24
|
+
|
|
25
|
+
__version__ = "3.0.0"
|
|
26
|
+
|
|
27
|
+
__all__ = ["app", "mcp", "main", "__version__"]
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
"""FastMCP server exposing QuantNodes quant tools over MCP.
|
|
3
|
+
|
|
4
|
+
Approach
|
|
5
|
+
--------
|
|
6
|
+
Each QuantNodes tool is a class with ``name``, ``description``, ``parameters``
|
|
7
|
+
(JSON Schema), and ``async execute(**kwargs)``. FastMCP doesn't support
|
|
8
|
+
``**kwargs`` function signatures, so we use a single ``call_quant_tool``
|
|
9
|
+
dispatcher MCP tool that:
|
|
10
|
+
1. Accepts ``tool_name`` + arbitrary ``arguments`` dict (object).
|
|
11
|
+
2. Looks up the QuantNodes tool by name.
|
|
12
|
+
3. Invokes ``tool.execute(**arguments)`` and returns the result.
|
|
13
|
+
|
|
14
|
+
This is cleaner than registering 8 separate MCP tools (which would require
|
|
15
|
+
dynamic signature generation) and provides a uniform contract.
|
|
16
|
+
|
|
17
|
+
Run as stdio (default for nanobot)::
|
|
18
|
+
|
|
19
|
+
python -m QuantNodes.mcp_server
|
|
20
|
+
|
|
21
|
+
Run with HTTP transport (for inspector)::
|
|
22
|
+
|
|
23
|
+
python -m QuantNodes.mcp_server --transport http --port 8765
|
|
24
|
+
|
|
25
|
+
Inspect with MCP devtools::
|
|
26
|
+
|
|
27
|
+
mcp dev QuantNodes.mcp_server.server:app
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
import argparse
|
|
33
|
+
import inspect
|
|
34
|
+
import json
|
|
35
|
+
import logging
|
|
36
|
+
import sys
|
|
37
|
+
from pathlib import Path
|
|
38
|
+
from typing import Any, Dict, List, Optional
|
|
39
|
+
|
|
40
|
+
from fastmcp import FastMCP
|
|
41
|
+
from pydantic import Field, create_model
|
|
42
|
+
|
|
43
|
+
from QuantNodes.constants import DEFAULT_HOST, DEFAULT_WEBSOCKET_PORT
|
|
44
|
+
|
|
45
|
+
logger = logging.getLogger(__name__)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
_DEFAULT_WORKSPACE = Path(".agent")
|
|
49
|
+
|
|
50
|
+
mcp = FastMCP(
|
|
51
|
+
name="quant",
|
|
52
|
+
instructions=(
|
|
53
|
+
"QuantNodes quant toolkit dispatcher. Use `list_quant_tools` to discover "
|
|
54
|
+
"available tools, then `call_quant_tool(tool_name, arguments)` to invoke. "
|
|
55
|
+
"Each tool accepts a JSON Schema-defined arguments object."
|
|
56
|
+
),
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _build_tools_registry(workspace: Path) -> Dict[str, Any]:
|
|
61
|
+
"""Instantiate the 8 core quant tools (lazy + cached)."""
|
|
62
|
+
if hasattr(mcp, "_quant_tools_cache") and mcp._quant_tools_cache is not None:
|
|
63
|
+
return mcp._quant_tools_cache
|
|
64
|
+
|
|
65
|
+
try:
|
|
66
|
+
from QuantNodes.agent.tools import (
|
|
67
|
+
BacktestTool,
|
|
68
|
+
ConfigBacktestTool,
|
|
69
|
+
FactorTool,
|
|
70
|
+
PipelineTool,
|
|
71
|
+
SandboxTool,
|
|
72
|
+
StrategyTool,
|
|
73
|
+
WikiTool,
|
|
74
|
+
)
|
|
75
|
+
workspace_dep = {"WikiTool": {"wiki_path": str(workspace / "wiki")}}
|
|
76
|
+
factories = [BacktestTool, ConfigBacktestTool, FactorTool, PipelineTool,
|
|
77
|
+
SandboxTool, StrategyTool, WikiTool]
|
|
78
|
+
except Exception as exc:
|
|
79
|
+
logger.warning("QuantNodes tool stack unavailable: %s", exc)
|
|
80
|
+
mcp._quant_tools_cache = {}
|
|
81
|
+
return mcp._quant_tools_cache
|
|
82
|
+
|
|
83
|
+
registry: Dict[str, Any] = {}
|
|
84
|
+
for factory in factories:
|
|
85
|
+
kwargs = workspace_dep.get(factory.__name__, {})
|
|
86
|
+
try:
|
|
87
|
+
tool = factory(**kwargs) if kwargs else factory()
|
|
88
|
+
except Exception as exc:
|
|
89
|
+
logger.warning("Failed to instantiate %s: %s", factory.__name__, exc)
|
|
90
|
+
continue
|
|
91
|
+
registry[tool.name] = tool
|
|
92
|
+
mcp._quant_tools_cache = registry
|
|
93
|
+
return registry
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _safe_field_type(prop: Dict[str, Any]):
|
|
97
|
+
"""Map JSON Schema type to a Python type suitable for Pydantic Field."""
|
|
98
|
+
t = prop.get("type", "string")
|
|
99
|
+
return {
|
|
100
|
+
"string": str,
|
|
101
|
+
"integer": int,
|
|
102
|
+
"number": float,
|
|
103
|
+
"boolean": bool,
|
|
104
|
+
"array": list,
|
|
105
|
+
"object": dict,
|
|
106
|
+
}.get(t, Any)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _build_pydantic_model(name: str, schema: Dict[str, Any]):
|
|
110
|
+
"""Build a Pydantic model from a JSON Schema object for a tool's arguments."""
|
|
111
|
+
props = schema.get("properties", {})
|
|
112
|
+
required = set(schema.get("required", []))
|
|
113
|
+
fields: Dict[str, Any] = {}
|
|
114
|
+
for pname, pschema in props.items():
|
|
115
|
+
py_type = _safe_field_type(pschema)
|
|
116
|
+
default = ... if pname in required else pschema.get("default", None)
|
|
117
|
+
description = pschema.get("description", "")
|
|
118
|
+
fields[pname] = (py_type, Field(default=default, description=description))
|
|
119
|
+
if not fields:
|
|
120
|
+
return create_model(name)
|
|
121
|
+
return create_model(name, **fields)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _register_tool_call_dispatcher(workspace: Path) -> None:
|
|
125
|
+
"""Register a dynamic Pydantic-model-based MCP tool for each QuantNodes tool.
|
|
126
|
+
|
|
127
|
+
Each tool's arguments are typed via a dynamically generated Pydantic model,
|
|
128
|
+
so FastMCP can produce a proper JSON Schema in the MCP tool listing.
|
|
129
|
+
"""
|
|
130
|
+
registry = _build_tools_registry(workspace)
|
|
131
|
+
for tool_name, tool in registry.items():
|
|
132
|
+
schema = tool.parameters if hasattr(tool, "parameters") else {"type": "object", "properties": {}}
|
|
133
|
+
model_name = f"{tool_name.title().replace('_', '')}Args"
|
|
134
|
+
ArgsModel = _build_pydantic_model(model_name, schema)
|
|
135
|
+
|
|
136
|
+
async def _call(arguments: Dict[str, Any], _tool=tool) -> Dict[str, Any]:
|
|
137
|
+
try:
|
|
138
|
+
result = await _tool.execute(**arguments)
|
|
139
|
+
if isinstance(result, (dict, list, str, int, float, bool)) or result is None:
|
|
140
|
+
return {"status": "ok", "result": result}
|
|
141
|
+
return {"status": "ok", "result": str(result)}
|
|
142
|
+
except Exception as exc:
|
|
143
|
+
logger.exception("Tool %s failed", tool_name)
|
|
144
|
+
return {"status": "error", "tool": tool_name, "error": str(exc)}
|
|
145
|
+
|
|
146
|
+
_call.__name__ = f"call_{tool_name}"
|
|
147
|
+
_call.__doc__ = (
|
|
148
|
+
f"Call QuantNodes tool '{tool_name}'. {tool.description}"
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
decorated = mcp.tool(
|
|
152
|
+
name=f"call_{tool_name}",
|
|
153
|
+
description=_call.__doc__,
|
|
154
|
+
exclude_args=["_tool"],
|
|
155
|
+
)
|
|
156
|
+
decorated(_call)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _register_list_tools(workspace: Path) -> None:
|
|
160
|
+
"""Register a `list_quant_tools` MCP tool that lists available tools + schemas."""
|
|
161
|
+
|
|
162
|
+
@mcp.tool
|
|
163
|
+
async def list_quant_tools() -> Dict[str, Any]:
|
|
164
|
+
"""List all available QuantNodes tools with their JSON Schema.
|
|
165
|
+
|
|
166
|
+
Returns a dict with keys:
|
|
167
|
+
- tools: list of {name, description, parameters, mcp_name}
|
|
168
|
+
- count: number of tools
|
|
169
|
+
"""
|
|
170
|
+
registry = _build_tools_registry(workspace)
|
|
171
|
+
tools = []
|
|
172
|
+
for tname, tool in registry.items():
|
|
173
|
+
tools.append({
|
|
174
|
+
"name": tname,
|
|
175
|
+
"description": tool.description,
|
|
176
|
+
"parameters": tool.parameters,
|
|
177
|
+
"mcp_name": f"call_{tname}",
|
|
178
|
+
})
|
|
179
|
+
return {"count": len(tools), "tools": tools}
|
|
180
|
+
|
|
181
|
+
list_quant_tools.__doc__ = (
|
|
182
|
+
"List all available QuantNodes tools (with JSON Schema). "
|
|
183
|
+
"Use this to discover what tools are exposed via this MCP server."
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _register_data_query(workspace: Path) -> None:
|
|
188
|
+
"""Register a data_query MCP tool — DuckDB query (v0)."""
|
|
189
|
+
from pydantic import Field as _F
|
|
190
|
+
|
|
191
|
+
class DataQueryArgs:
|
|
192
|
+
pass
|
|
193
|
+
|
|
194
|
+
@mcp.tool
|
|
195
|
+
async def data_query(sql: str = _F(..., description="Read-only SQL query to execute"),
|
|
196
|
+
source: str = _F("duckdb", description="Data source: duckdb or clickhouse")) -> Dict[str, Any]:
|
|
197
|
+
"""Execute a read-only SQL query against the default DuckDB source (v0)."""
|
|
198
|
+
if source != "duckdb":
|
|
199
|
+
return {"status": "error", "error": f"source={source} not yet supported in v0"}
|
|
200
|
+
try:
|
|
201
|
+
import duckdb
|
|
202
|
+
db_path = workspace / "quantnodes.db"
|
|
203
|
+
if not db_path.exists():
|
|
204
|
+
return {"status": "error", "error": f"db not found: {db_path}"}
|
|
205
|
+
con = duckdb.connect(str(db_path), read_only=True)
|
|
206
|
+
try:
|
|
207
|
+
rows = con.execute(sql).fetchall()
|
|
208
|
+
cols = [d[0] for d in con.description] if con.description else []
|
|
209
|
+
return {
|
|
210
|
+
"status": "ok",
|
|
211
|
+
"columns": cols,
|
|
212
|
+
"rows": [list(r) for r in rows[:1000]],
|
|
213
|
+
"row_count": len(rows),
|
|
214
|
+
}
|
|
215
|
+
finally:
|
|
216
|
+
con.close()
|
|
217
|
+
except Exception as exc:
|
|
218
|
+
return {"status": "error", "error": str(exc)}
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
# Eager registration at import time
|
|
222
|
+
_register_tool_call_dispatcher(_DEFAULT_WORKSPACE)
|
|
223
|
+
_register_list_tools(_DEFAULT_WORKSPACE)
|
|
224
|
+
_register_data_query(_DEFAULT_WORKSPACE)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
# FastMCP exposes the ASGI app as ``mcp.http_app`` for HTTP transports
|
|
228
|
+
app = mcp.http_app
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def main(argv: list[str] | None = None) -> int:
|
|
232
|
+
parser = argparse.ArgumentParser(
|
|
233
|
+
description="QuantNodes MCP server (exposes quant tools to nanobot/Claude/Cursor)"
|
|
234
|
+
)
|
|
235
|
+
parser.add_argument("--transport", choices=["stdio", "http", "sse", "streamable-http"],
|
|
236
|
+
default="stdio", help="MCP transport (default: stdio)")
|
|
237
|
+
parser.add_argument("--port", type=int, default=DEFAULT_WEBSOCKET_PORT,
|
|
238
|
+
help="HTTP port (only for http/sse transports)")
|
|
239
|
+
parser.add_argument("--host", default=DEFAULT_HOST,
|
|
240
|
+
help="HTTP host (only for http/sse transports)")
|
|
241
|
+
parser.add_argument("--workspace", default=".agent",
|
|
242
|
+
help="QuantNodes workspace path (default: .agent)")
|
|
243
|
+
parser.add_argument("-v", "--verbose", action="store_true")
|
|
244
|
+
args = parser.parse_args(argv)
|
|
245
|
+
|
|
246
|
+
logging.basicConfig(
|
|
247
|
+
level=logging.DEBUG if args.verbose else logging.INFO,
|
|
248
|
+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
ws = Path(args.workspace).expanduser().resolve()
|
|
252
|
+
if ws != _DEFAULT_WORKSPACE.resolve():
|
|
253
|
+
mcp._quant_tools_cache = None
|
|
254
|
+
_register_tool_call_dispatcher(ws)
|
|
255
|
+
_register_list_tools(ws)
|
|
256
|
+
_register_data_query(ws)
|
|
257
|
+
|
|
258
|
+
logger.info("Starting QuantNodes MCP server (transport=%s, workspace=%s)",
|
|
259
|
+
args.transport, ws)
|
|
260
|
+
if args.transport == "stdio":
|
|
261
|
+
mcp.run(transport="stdio", show_banner=not args.verbose)
|
|
262
|
+
elif args.transport in ("http", "streamable-http"):
|
|
263
|
+
mcp.run(transport=args.transport, host=args.host, port=args.port,
|
|
264
|
+
show_banner=not args.verbose)
|
|
265
|
+
elif args.transport == "sse":
|
|
266
|
+
mcp.run(transport="sse", host=args.host, port=args.port,
|
|
267
|
+
show_banner=not args.verbose)
|
|
268
|
+
return 0
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
if __name__ == "__main__":
|
|
272
|
+
sys.exit(main())
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
"""
|
|
3
|
+
QuantNodes Methods Package
|
|
4
|
+
|
|
5
|
+
Pure method implementations for external agents.
|
|
6
|
+
These methods can be called via API without LLM dependencies.
|
|
7
|
+
|
|
8
|
+
核心入口:
|
|
9
|
+
- validate_code / execute_code: 代码沙箱执行 (api/routers/code.py)
|
|
10
|
+
- validate_pipeline: pipeline 校验 (api/routers/code.py)
|
|
11
|
+
|
|
12
|
+
历史:
|
|
13
|
+
早期版本曾导出 run_backtest / analyze_factor / query_wiki / FileOperations /
|
|
14
|
+
CodeSearch / GitOperations 等,均无生产调用方,已在 v3.0.0 清理。
|
|
15
|
+
对应 Agent 工具实现见 `agent/tools/`。
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from .sandbox import execute_code, validate_code, ExecutionResult, ValidationResult
|
|
19
|
+
from .pipeline import validate_pipeline, PipelineValidationResult
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"validate_code",
|
|
23
|
+
"execute_code",
|
|
24
|
+
"ValidationResult",
|
|
25
|
+
"ExecutionResult",
|
|
26
|
+
"validate_pipeline",
|
|
27
|
+
"PipelineValidationResult",
|
|
28
|
+
]
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
"""
|
|
3
|
+
Pipeline Method
|
|
4
|
+
|
|
5
|
+
validate_pipeline(code) -> PipelineValidationResult
|
|
6
|
+
|
|
7
|
+
Validates QuantNodes Pipeline code for external agents.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import re
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from typing import List
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class PipelineValidationResult:
|
|
17
|
+
is_valid: bool
|
|
18
|
+
errors: List[str] = field(default_factory=list)
|
|
19
|
+
warnings: List[str] = field(default_factory=list)
|
|
20
|
+
nodes: List[str] = field(default_factory=list)
|
|
21
|
+
security_status: str = "unknown"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
CODE_BLOCK_PATTERN = re.compile(r'```(?:python)?\s*(.*?)```', re.DOTALL)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def validate_pipeline(code: str, **kwargs) -> PipelineValidationResult:
|
|
28
|
+
"""Validate QuantNodes Pipeline code.
|
|
29
|
+
|
|
30
|
+
Checks:
|
|
31
|
+
- Syntax validity
|
|
32
|
+
- Security (via CodeSandbox)
|
|
33
|
+
- Node extraction
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
code: QuantNodes Pipeline Python代码
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
PipelineValidationResult with validation status and extracted nodes
|
|
40
|
+
"""
|
|
41
|
+
result = PipelineValidationResult(is_valid=True)
|
|
42
|
+
|
|
43
|
+
extracted_code = _extract_code(code)
|
|
44
|
+
if not extracted_code:
|
|
45
|
+
result.is_valid = False
|
|
46
|
+
result.errors.append("No valid code found")
|
|
47
|
+
return result
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
compile(extracted_code, '<string>', 'exec')
|
|
51
|
+
except SyntaxError as e:
|
|
52
|
+
result.is_valid = False
|
|
53
|
+
result.errors.append("Syntax error: %s" % str(e))
|
|
54
|
+
return result
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
from QuantNodes.ai.sandbox import CodeSandbox
|
|
58
|
+
|
|
59
|
+
sandbox = CodeSandbox()
|
|
60
|
+
validation = sandbox.validate(extracted_code)
|
|
61
|
+
|
|
62
|
+
result.security_status = "safe" if validation.is_safe else "unsafe"
|
|
63
|
+
|
|
64
|
+
if not validation.is_safe:
|
|
65
|
+
result.is_valid = False
|
|
66
|
+
result.errors.extend(validation.errors)
|
|
67
|
+
|
|
68
|
+
if validation.warnings:
|
|
69
|
+
result.warnings.extend(validation.warnings)
|
|
70
|
+
|
|
71
|
+
except ImportError:
|
|
72
|
+
result.security_status = "skipped"
|
|
73
|
+
result.warnings.append("CodeSandbox not available, security check skipped")
|
|
74
|
+
|
|
75
|
+
result.nodes = _extract_nodes(extracted_code)
|
|
76
|
+
|
|
77
|
+
return result
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _extract_code(code: str) -> str:
|
|
81
|
+
"""Extract code from markdown code blocks if present."""
|
|
82
|
+
match = CODE_BLOCK_PATTERN.search(code)
|
|
83
|
+
if match:
|
|
84
|
+
return match.group(1).strip()
|
|
85
|
+
return code.strip()
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _extract_nodes(code: str) -> List[str]:
|
|
89
|
+
"""Extract node types from code."""
|
|
90
|
+
nodes = []
|
|
91
|
+
patterns = [
|
|
92
|
+
r'(\w+Node)\s*\(',
|
|
93
|
+
r'FactorPipeline\s*\(',
|
|
94
|
+
r'Pipeline\s*\(',
|
|
95
|
+
r'BacktestPipeline\s*\(',
|
|
96
|
+
]
|
|
97
|
+
for pattern in patterns:
|
|
98
|
+
matches = re.findall(pattern, code)
|
|
99
|
+
nodes.extend(matches)
|
|
100
|
+
return list(set(nodes))
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
"""
|
|
3
|
+
Sandbox Method
|
|
4
|
+
|
|
5
|
+
validate_code(code) -> ValidationResult
|
|
6
|
+
execute_code(code, **kwargs) -> ExecutionResult
|
|
7
|
+
|
|
8
|
+
Provides code security validation for external agents.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from typing import Any, Dict, List
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class ValidationResult:
|
|
17
|
+
is_safe: bool
|
|
18
|
+
errors: List[str] = field(default_factory=list)
|
|
19
|
+
warnings: List[str] = field(default_factory=list)
|
|
20
|
+
warnings_only: bool = False
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class ExecutionResult:
|
|
25
|
+
status: str
|
|
26
|
+
result: Any = None
|
|
27
|
+
errors: List[str] = field(default_factory=list)
|
|
28
|
+
warnings: List[str] = field(default_factory=list)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def validate_code(
|
|
32
|
+
code: str,
|
|
33
|
+
allow_warnings: bool = False,
|
|
34
|
+
max_code_length: int = 10000,
|
|
35
|
+
**kwargs
|
|
36
|
+
) -> ValidationResult:
|
|
37
|
+
"""Validate Python code安全性.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
code: 待验证的Python代码
|
|
41
|
+
allow_warnings: 是否允许警告
|
|
42
|
+
max_code_length: 最大代码长度
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
ValidationResult with is_safe, errors, warnings
|
|
46
|
+
"""
|
|
47
|
+
from QuantNodes.ai.sandbox import CodeSandbox
|
|
48
|
+
|
|
49
|
+
sandbox = CodeSandbox(
|
|
50
|
+
allow_warnings=allow_warnings,
|
|
51
|
+
max_code_length=max_code_length
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
result = sandbox.validate(code)
|
|
55
|
+
|
|
56
|
+
return ValidationResult(
|
|
57
|
+
is_safe=result.is_safe,
|
|
58
|
+
errors=result.errors,
|
|
59
|
+
warnings=result.warnings,
|
|
60
|
+
warnings_only=result.warnings_only
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def execute_code(
|
|
65
|
+
code: str,
|
|
66
|
+
context: Dict[str, Any] = None,
|
|
67
|
+
**kwargs
|
|
68
|
+
) -> ExecutionResult:
|
|
69
|
+
"""Execute code in sandbox environment.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
code: 待执行的Python代码
|
|
73
|
+
context: 执行上下文(可选)
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
ExecutionResult with status, result, errors
|
|
77
|
+
"""
|
|
78
|
+
from QuantNodes.ai.sandbox import CodeSandbox
|
|
79
|
+
|
|
80
|
+
sandbox = CodeSandbox()
|
|
81
|
+
validation = sandbox.validate(code)
|
|
82
|
+
|
|
83
|
+
if not validation.is_safe:
|
|
84
|
+
return ExecutionResult(
|
|
85
|
+
status="error",
|
|
86
|
+
errors=validation.errors,
|
|
87
|
+
warnings=validation.warnings
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
try:
|
|
91
|
+
namespace = sandbox.validate_and_execute(code, context or {})
|
|
92
|
+
return ExecutionResult(
|
|
93
|
+
status="success",
|
|
94
|
+
result={k: v for k, v in namespace.items() if not k.startswith('_')},
|
|
95
|
+
warnings=validation.warnings
|
|
96
|
+
)
|
|
97
|
+
except Exception as e:
|
|
98
|
+
return ExecutionResult(
|
|
99
|
+
status="error",
|
|
100
|
+
errors=[str(e)],
|
|
101
|
+
warnings=validation.warnings
|
|
102
|
+
)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""策略监控与优化模块
|
|
2
|
+
|
|
3
|
+
功能2: 调度层 + 监控层 + 版本管理层
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from .storage.models import (
|
|
7
|
+
StrategyRun, PerformanceSnapshot, DriftAlert, StrategyVersion,
|
|
8
|
+
)
|
|
9
|
+
from .storage.repository import (
|
|
10
|
+
DatabaseManager,
|
|
11
|
+
StrategyRunRepository,
|
|
12
|
+
PerformanceRepository,
|
|
13
|
+
DriftAlertRepository,
|
|
14
|
+
VersionRepository,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"StrategyRun",
|
|
19
|
+
"PerformanceSnapshot",
|
|
20
|
+
"DriftAlert",
|
|
21
|
+
"StrategyVersion",
|
|
22
|
+
"DatabaseManager",
|
|
23
|
+
"StrategyRunRepository",
|
|
24
|
+
"PerformanceRepository",
|
|
25
|
+
"DriftAlertRepository",
|
|
26
|
+
"VersionRepository",
|
|
27
|
+
]
|