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,401 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
"""
|
|
3
|
+
配置驱动的回测工具
|
|
4
|
+
|
|
5
|
+
接受 YAML 配置,通过 ConfigBacktestRunner 直接执行回测。
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Dict
|
|
9
|
+
import yaml
|
|
10
|
+
|
|
11
|
+
from QuantNodes.agent.tools.base import Tool
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ConfigBacktestTool(Tool):
|
|
15
|
+
"""配置驱动的回测工具
|
|
16
|
+
|
|
17
|
+
通过 YAML 配置文件定义策略,直接执行回测。
|
|
18
|
+
|
|
19
|
+
工作流程:
|
|
20
|
+
1. 加载 YAML 配置
|
|
21
|
+
2. 检查算子覆盖度
|
|
22
|
+
3. 调用 ConfigBacktestRunner 执行回测
|
|
23
|
+
4. 返回结果
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self):
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def name(self) -> str:
|
|
31
|
+
return "config_backtest"
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def description(self) -> str:
|
|
35
|
+
return (
|
|
36
|
+
"通过 YAML 配置文件执行策略回测。"
|
|
37
|
+
"支持因子定义、运算配置、组合因子和回测参数。"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def parameters(self) -> Dict[str, Any]:
|
|
42
|
+
return {
|
|
43
|
+
"type": "object",
|
|
44
|
+
"properties": {
|
|
45
|
+
"config_yaml": {
|
|
46
|
+
"type": "string",
|
|
47
|
+
"description": "YAML 格式的策略配置字符串"
|
|
48
|
+
},
|
|
49
|
+
"config_path": {
|
|
50
|
+
"type": "string",
|
|
51
|
+
"description": "YAML 配置文件路径"
|
|
52
|
+
},
|
|
53
|
+
"data_path": {
|
|
54
|
+
"type": "string",
|
|
55
|
+
"description": "数据文件路径 (csv/parquet)"
|
|
56
|
+
},
|
|
57
|
+
"start_date": {
|
|
58
|
+
"type": "string",
|
|
59
|
+
"description": "覆盖配置中的开始日期"
|
|
60
|
+
},
|
|
61
|
+
"end_date": {
|
|
62
|
+
"type": "string",
|
|
63
|
+
"description": "覆盖配置中的结束日期"
|
|
64
|
+
},
|
|
65
|
+
"initial_cash": {
|
|
66
|
+
"type": "number",
|
|
67
|
+
"description": "覆盖配置中的初始资金"
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
"required": []
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def read_only(self) -> bool:
|
|
75
|
+
return False
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def concurrency_safe(self) -> bool:
|
|
79
|
+
return False
|
|
80
|
+
|
|
81
|
+
async def execute(
|
|
82
|
+
self,
|
|
83
|
+
config_yaml: str = None,
|
|
84
|
+
config_path: str = None,
|
|
85
|
+
data_path: str = None,
|
|
86
|
+
start_date: str = None,
|
|
87
|
+
end_date: str = None,
|
|
88
|
+
initial_cash: float = None,
|
|
89
|
+
**kwargs
|
|
90
|
+
) -> Dict[str, Any]:
|
|
91
|
+
result = {
|
|
92
|
+
"status": "success",
|
|
93
|
+
"summary": {},
|
|
94
|
+
"config_info": {},
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
try:
|
|
98
|
+
# 1. 加载配置
|
|
99
|
+
strategy_config = self._load_config(config_yaml, config_path)
|
|
100
|
+
|
|
101
|
+
if strategy_config is None:
|
|
102
|
+
result["status"] = "error"
|
|
103
|
+
result["errors"] = ["Need config_yaml or config_path"]
|
|
104
|
+
return result
|
|
105
|
+
|
|
106
|
+
# 2. 覆盖参数
|
|
107
|
+
if start_date and strategy_config.backtest:
|
|
108
|
+
strategy_config.backtest.start_date = start_date
|
|
109
|
+
if end_date and strategy_config.backtest:
|
|
110
|
+
strategy_config.backtest.end_date = end_date
|
|
111
|
+
if initial_cash and strategy_config.backtest:
|
|
112
|
+
strategy_config.backtest.initial_cash = initial_cash
|
|
113
|
+
|
|
114
|
+
# 3. 检查覆盖度
|
|
115
|
+
from QuantNodes.agent.config.loader import ConfigLoader
|
|
116
|
+
loader = ConfigLoader()
|
|
117
|
+
coverage = loader.check_coverage(strategy_config)
|
|
118
|
+
|
|
119
|
+
if not coverage.is_complete:
|
|
120
|
+
result["status"] = "warning"
|
|
121
|
+
result["warnings"] = [
|
|
122
|
+
f"Unresolved operators: {coverage.unresolved}"
|
|
123
|
+
]
|
|
124
|
+
|
|
125
|
+
# 4. 加载数据
|
|
126
|
+
data = self._load_data(strategy_config, data_path)
|
|
127
|
+
|
|
128
|
+
# 5. 调用 ConfigBacktestRunner
|
|
129
|
+
from QuantNodes.backtest.config_runner import ConfigBacktestRunner
|
|
130
|
+
|
|
131
|
+
runner = ConfigBacktestRunner()
|
|
132
|
+
bt_result = runner.run(strategy_config, data)
|
|
133
|
+
|
|
134
|
+
# 6. 保存输出文件
|
|
135
|
+
saved_files = {}
|
|
136
|
+
if strategy_config.output is not None:
|
|
137
|
+
signals_df = None
|
|
138
|
+
if bt_result.statistics.get("total_trades", 0) > 0:
|
|
139
|
+
signals_df = bt_result.trades
|
|
140
|
+
saved_files = runner.save_output(
|
|
141
|
+
bt_result, strategy_config, signals_df=signals_df
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
# 7. 格式化返回结果
|
|
145
|
+
result["status"] = "success"
|
|
146
|
+
result["summary"] = {
|
|
147
|
+
"total_trades": bt_result.statistics.get("total_trades", 0),
|
|
148
|
+
"final_cash": bt_result.final_cash,
|
|
149
|
+
"total_commission": bt_result.statistics.get("total_commission", 0),
|
|
150
|
+
"total_return": bt_result.total_return,
|
|
151
|
+
"sharpe_ratio": bt_result.sharpe_ratio,
|
|
152
|
+
"max_drawdown": bt_result.max_drawdown,
|
|
153
|
+
"win_rate": bt_result.win_rate,
|
|
154
|
+
"annualized_return": bt_result.statistics.get("annualized_return", 0),
|
|
155
|
+
"annualized_volatility": bt_result.statistics.get("annualized_volatility", 0),
|
|
156
|
+
"sortino_ratio": bt_result.statistics.get("sortino_ratio", 0),
|
|
157
|
+
"calmar_ratio": bt_result.statistics.get("calmar_ratio", 0),
|
|
158
|
+
"profit_factor": bt_result.statistics.get("profit_factor", 0),
|
|
159
|
+
"avg_trade_pnl": bt_result.statistics.get("avg_trade_pnl", 0),
|
|
160
|
+
"trading_days": bt_result.statistics.get("trading_days", 0),
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
# 8. 附加配置信息
|
|
164
|
+
result["config_info"] = {
|
|
165
|
+
"name": strategy_config.name,
|
|
166
|
+
"description": strategy_config.description,
|
|
167
|
+
"factors": len(strategy_config.factors),
|
|
168
|
+
"operations": len(strategy_config.operations),
|
|
169
|
+
"composites": len(strategy_config.composite),
|
|
170
|
+
"has_backtest": strategy_config.backtest is not None,
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if saved_files:
|
|
174
|
+
result["output_files"] = saved_files
|
|
175
|
+
|
|
176
|
+
except Exception as e:
|
|
177
|
+
result["status"] = "error"
|
|
178
|
+
result["errors"] = [str(e)]
|
|
179
|
+
|
|
180
|
+
return result
|
|
181
|
+
|
|
182
|
+
def _load_config(self, config_yaml: str = None, config_path: str = None):
|
|
183
|
+
"""加载配置"""
|
|
184
|
+
from QuantNodes.agent.config.loader import ConfigLoader
|
|
185
|
+
|
|
186
|
+
if config_yaml:
|
|
187
|
+
try:
|
|
188
|
+
data = yaml.safe_load(config_yaml)
|
|
189
|
+
if data is None:
|
|
190
|
+
return None
|
|
191
|
+
loader = ConfigLoader()
|
|
192
|
+
return loader._parse(data)
|
|
193
|
+
except yaml.YAMLError as e:
|
|
194
|
+
raise ValueError(f"Invalid YAML: {e}")
|
|
195
|
+
|
|
196
|
+
if config_path:
|
|
197
|
+
loader = ConfigLoader()
|
|
198
|
+
return loader.load(config_path)
|
|
199
|
+
|
|
200
|
+
return None
|
|
201
|
+
|
|
202
|
+
def _load_data(self, config, data_path: str = None):
|
|
203
|
+
"""加载数据为 Polars LazyFrame
|
|
204
|
+
|
|
205
|
+
数据加载分发逻辑:
|
|
206
|
+
1. data_path 参数 → 直接读文件(向后兼容)
|
|
207
|
+
2. config.data.source == "csv"/"parquet" → 读文件
|
|
208
|
+
3. config.data.source == "clickhouse"/"mysql" → _load_from_db()
|
|
209
|
+
4. config.data.source == "sqlite"/"duckdb" → _load_from_db() (path-based)
|
|
210
|
+
"""
|
|
211
|
+
|
|
212
|
+
# 优先使用 data_path 参数
|
|
213
|
+
if data_path:
|
|
214
|
+
return self._read_data_file(data_path)
|
|
215
|
+
|
|
216
|
+
if not config.data:
|
|
217
|
+
raise ValueError("No data configuration provided. Set config.data or data_path")
|
|
218
|
+
|
|
219
|
+
source = config.data.source
|
|
220
|
+
|
|
221
|
+
# 文件类数据源
|
|
222
|
+
if source in ("csv", "parquet"):
|
|
223
|
+
if config.data.path:
|
|
224
|
+
lf = self._read_data_file(config.data.path)
|
|
225
|
+
# 应用列名映射
|
|
226
|
+
if config.data.column_mapping:
|
|
227
|
+
lf = lf.rename(config.data.column_mapping)
|
|
228
|
+
return lf
|
|
229
|
+
raise ValueError(f"source='{source}' requires config.data.path")
|
|
230
|
+
|
|
231
|
+
# 数据库类数据源
|
|
232
|
+
if source in ("clickhouse", "mysql", "sqlite", "duckdb"):
|
|
233
|
+
return self._load_from_db(config)
|
|
234
|
+
|
|
235
|
+
raise ValueError(f"Unsupported data source: {source}")
|
|
236
|
+
|
|
237
|
+
def _load_from_db(self, config):
|
|
238
|
+
"""从 database_node 加载数据
|
|
239
|
+
|
|
240
|
+
流程: 读取 conn.ini → 构建 SQL → 查询 → 列名映射 → 返回 LazyFrame
|
|
241
|
+
|
|
242
|
+
如果 cache_enabled=True, 优先使用 MarketDataCacheNode 缓存查询结果。
|
|
243
|
+
|
|
244
|
+
注意: DateTime 转换使用映射后的 date_column(而非 db_date_column)。
|
|
245
|
+
"""
|
|
246
|
+
import polars as pl
|
|
247
|
+
|
|
248
|
+
data_cfg = config.data
|
|
249
|
+
source = data_cfg.source
|
|
250
|
+
|
|
251
|
+
# 1. 构建 Node 实例
|
|
252
|
+
node = self._build_db_node(source, data_cfg)
|
|
253
|
+
|
|
254
|
+
# 2. 缓存逻辑
|
|
255
|
+
if data_cfg.cache_enabled:
|
|
256
|
+
df = self._load_with_cache(data_cfg, node)
|
|
257
|
+
else:
|
|
258
|
+
df = self._load_from_db_direct(data_cfg, node)
|
|
259
|
+
|
|
260
|
+
# 3. 列名映射
|
|
261
|
+
if data_cfg.column_mapping:
|
|
262
|
+
if hasattr(df, "to_pandas"):
|
|
263
|
+
# Polars DataFrame — rename() takes positional dict
|
|
264
|
+
df = df.rename(data_cfg.column_mapping)
|
|
265
|
+
else:
|
|
266
|
+
# pandas DataFrame — rename() takes columns= kwarg
|
|
267
|
+
df = df.rename(columns=data_cfg.column_mapping)
|
|
268
|
+
|
|
269
|
+
# 4. DateTime → Date 类型转换(使用映射后的 date_column)
|
|
270
|
+
date_col = data_cfg.date_column
|
|
271
|
+
if date_col in df.columns:
|
|
272
|
+
try:
|
|
273
|
+
import polars as pl_polars
|
|
274
|
+
pdf = pl_polars.from_pandas(df)
|
|
275
|
+
if pdf.schema.get(date_col) == pl_polars.Datetime:
|
|
276
|
+
pdf = pdf.with_columns(pl_polars.col(date_col).cast(pl_polars.Date))
|
|
277
|
+
df = pdf.to_pandas()
|
|
278
|
+
except Exception:
|
|
279
|
+
pass
|
|
280
|
+
|
|
281
|
+
return pl.from_pandas(df).lazy()
|
|
282
|
+
|
|
283
|
+
def _load_with_cache(self, data_cfg, node):
|
|
284
|
+
"""使用缓存加载数据"""
|
|
285
|
+
from QuantNodes.cache_node import MarketDataCacheNode
|
|
286
|
+
|
|
287
|
+
cache_node = MarketDataCacheNode(config={
|
|
288
|
+
"cache_dir": data_cfg.cache_dir,
|
|
289
|
+
"ttl_days": data_cfg.cache_ttl_days,
|
|
290
|
+
"force_refresh": data_cfg.cache_force_refresh,
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
return cache_node.execute({
|
|
294
|
+
"source": data_cfg.source,
|
|
295
|
+
"table": data_cfg.table,
|
|
296
|
+
"columns": data_cfg.columns,
|
|
297
|
+
"query_filter": data_cfg.query_filter,
|
|
298
|
+
"node": node,
|
|
299
|
+
"date_column": data_cfg.db_date_column or data_cfg.date_column,
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
def _load_from_db_direct(self, data_cfg, node):
|
|
303
|
+
"""直接从数据库加载 (不使用缓存)"""
|
|
304
|
+
try:
|
|
305
|
+
node.connect()
|
|
306
|
+
sql = self._build_query(data_cfg)
|
|
307
|
+
df = node.query(sql)
|
|
308
|
+
finally:
|
|
309
|
+
node.disconnect()
|
|
310
|
+
return df
|
|
311
|
+
|
|
312
|
+
def _build_db_node(self, source, data_cfg):
|
|
313
|
+
"""构建 database_node 实例 (委托 database_node.create_db_node 工厂)"""
|
|
314
|
+
from pathlib import Path
|
|
315
|
+
from QuantNodes.conf_node.ini_config import IniConfigNode
|
|
316
|
+
from QuantNodes.database_node import create_db_node
|
|
317
|
+
|
|
318
|
+
if source in ("sqlite", "duckdb"):
|
|
319
|
+
return self._build_embedded_node(source, data_cfg)
|
|
320
|
+
|
|
321
|
+
# clickhouse / mysql: 从 conn.ini 读取连接参数
|
|
322
|
+
if not data_cfg.conn_ini:
|
|
323
|
+
raise ValueError(f"source='{source}' requires conn_ini")
|
|
324
|
+
|
|
325
|
+
ini_path = Path(data_cfg.conn_ini)
|
|
326
|
+
if not ini_path.exists():
|
|
327
|
+
raise FileNotFoundError(f"conn.ini not found: {ini_path}")
|
|
328
|
+
|
|
329
|
+
ini = IniConfigNode(str(ini_path), section=data_cfg.conn_section)
|
|
330
|
+
conn_params = ini.execute()
|
|
331
|
+
|
|
332
|
+
if source == "clickhouse":
|
|
333
|
+
return create_db_node(
|
|
334
|
+
"clickhouse",
|
|
335
|
+
host=conn_params.get("host", "localhost"),
|
|
336
|
+
port=int(conn_params.get("port", 8123)),
|
|
337
|
+
user=conn_params.get("user", "default"),
|
|
338
|
+
passwd=conn_params.get("passwd", ""),
|
|
339
|
+
database=conn_params.get("db", "default"),
|
|
340
|
+
)
|
|
341
|
+
elif source == "mysql":
|
|
342
|
+
return create_db_node(
|
|
343
|
+
"mysql",
|
|
344
|
+
host=conn_params.get("host", "localhost"),
|
|
345
|
+
port=int(conn_params.get("port", 3306)),
|
|
346
|
+
user=conn_params.get("user", "root"),
|
|
347
|
+
passwd=conn_params.get("passwd", ""),
|
|
348
|
+
db=conn_params.get("db", ""),
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
def _build_embedded_node(self, source, data_cfg):
|
|
352
|
+
"""构建嵌入式数据库 Node (sqlite/duckdb, 委托 create_db_node 工厂)"""
|
|
353
|
+
from QuantNodes.database_node import create_db_node
|
|
354
|
+
|
|
355
|
+
path = data_cfg.path
|
|
356
|
+
if not path:
|
|
357
|
+
raise ValueError(f"source='{source}' requires config.data.path")
|
|
358
|
+
|
|
359
|
+
if source == "sqlite":
|
|
360
|
+
return create_db_node("sqlite", database=path)
|
|
361
|
+
elif source == "duckdb":
|
|
362
|
+
return create_db_node("duckdb", database=path)
|
|
363
|
+
|
|
364
|
+
def _build_query(self, data_cfg):
|
|
365
|
+
"""从 DataConfig 构建 SQL 查询
|
|
366
|
+
|
|
367
|
+
使用 db_*_column 作为 SQL 标识符(数据库原始列名)。
|
|
368
|
+
如果 db_*_column 为空,则 fallback 到 date_column/code_column。
|
|
369
|
+
"""
|
|
370
|
+
cols = data_cfg.columns or ["*"]
|
|
371
|
+
cols_str = ", ".join(cols)
|
|
372
|
+
table = data_cfg.table
|
|
373
|
+
|
|
374
|
+
if not table:
|
|
375
|
+
raise ValueError("DataConfig.table is required for database sources")
|
|
376
|
+
|
|
377
|
+
sql = f"SELECT {cols_str} FROM {table}"
|
|
378
|
+
|
|
379
|
+
where_parts = []
|
|
380
|
+
if data_cfg.query_filter:
|
|
381
|
+
where_parts.append(data_cfg.query_filter.lstrip("WHERE "))
|
|
382
|
+
|
|
383
|
+
if where_parts:
|
|
384
|
+
sql += " WHERE " + " AND ".join(where_parts)
|
|
385
|
+
|
|
386
|
+
code_col = data_cfg.db_code_column or data_cfg.code_column
|
|
387
|
+
date_col = data_cfg.db_date_column or data_cfg.date_column
|
|
388
|
+
sql += f" ORDER BY {code_col}, {date_col}"
|
|
389
|
+
|
|
390
|
+
return sql
|
|
391
|
+
|
|
392
|
+
def _read_data_file(self, path: str):
|
|
393
|
+
"""读取数据文件"""
|
|
394
|
+
import polars as pl
|
|
395
|
+
|
|
396
|
+
if path.endswith(".csv"):
|
|
397
|
+
return pl.scan_csv(path)
|
|
398
|
+
elif path.endswith(".parquet"):
|
|
399
|
+
return pl.scan_parquet(path)
|
|
400
|
+
else:
|
|
401
|
+
raise ValueError(f"Unsupported file format: {path}")
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
"""
|
|
3
|
+
Tool Context - Execution context for tools with permission checking
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
from typing import Any, Dict, List, Optional
|
|
8
|
+
import asyncio
|
|
9
|
+
|
|
10
|
+
from ..permission.service import PermissionService
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class ToolContext:
|
|
15
|
+
"""Tool execution context containing permission checking and session info.
|
|
16
|
+
|
|
17
|
+
This context is passed to tools during execution and provides
|
|
18
|
+
access to permission services and abort signals.
|
|
19
|
+
"""
|
|
20
|
+
session_id: str
|
|
21
|
+
agent_id: str
|
|
22
|
+
project_root: str
|
|
23
|
+
permission_service: PermissionService
|
|
24
|
+
abort_signal: asyncio.Event = field(default_factory=asyncio.Event)
|
|
25
|
+
tool_defaults: Dict[str, Any] = field(default_factory=dict)
|
|
26
|
+
|
|
27
|
+
async def check_permission(
|
|
28
|
+
self,
|
|
29
|
+
tool: str,
|
|
30
|
+
permission: str,
|
|
31
|
+
patterns: List[str],
|
|
32
|
+
always_patterns: Optional[List[str]] = None,
|
|
33
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
34
|
+
) -> bool:
|
|
35
|
+
"""Check if an action is permitted.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
tool: Tool name
|
|
39
|
+
permission: Permission category (e.g., "bash", "read", "edit")
|
|
40
|
+
patterns: Target patterns to check
|
|
41
|
+
always_patterns: Patterns that can be permanently allowed
|
|
42
|
+
metadata: Additional information (e.g., diff for edits)
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
True if allowed
|
|
46
|
+
|
|
47
|
+
Raises:
|
|
48
|
+
PermissionDeniedError: If permission is denied
|
|
49
|
+
PermissionRejectedError: If user rejects
|
|
50
|
+
"""
|
|
51
|
+
return await self.permission_service.check(
|
|
52
|
+
tool=tool,
|
|
53
|
+
permission=permission,
|
|
54
|
+
patterns=patterns,
|
|
55
|
+
always_patterns=always_patterns or [],
|
|
56
|
+
metadata=metadata or {},
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
def is_aborted(self) -> bool:
|
|
60
|
+
"""Check if execution should abort"""
|
|
61
|
+
return self.abort_signal.is_set()
|
|
62
|
+
|
|
63
|
+
async def wait_for_abort(self, timeout: Optional[float] = None) -> bool:
|
|
64
|
+
"""Wait for abort signal.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
timeout: Maximum time to wait in seconds
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
True if abort was signaled, False if timeout
|
|
71
|
+
"""
|
|
72
|
+
try:
|
|
73
|
+
await asyncio.wait_for(self.abort_signal.wait(), timeout=timeout)
|
|
74
|
+
return True
|
|
75
|
+
except asyncio.TimeoutError:
|
|
76
|
+
return False
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class ToolContextFactory:
|
|
80
|
+
"""Factory for creating ToolContext instances"""
|
|
81
|
+
|
|
82
|
+
@staticmethod
|
|
83
|
+
def create(
|
|
84
|
+
session_id: str,
|
|
85
|
+
agent_id: str,
|
|
86
|
+
project_root: str,
|
|
87
|
+
permission_service: PermissionService,
|
|
88
|
+
**kwargs: Any,
|
|
89
|
+
) -> ToolContext:
|
|
90
|
+
"""Create a new ToolContext"""
|
|
91
|
+
return ToolContext(
|
|
92
|
+
session_id=session_id,
|
|
93
|
+
agent_id=agent_id,
|
|
94
|
+
project_root=project_root,
|
|
95
|
+
permission_service=permission_service,
|
|
96
|
+
**kwargs,
|
|
97
|
+
)
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
"""
|
|
3
|
+
DreamSkill - Agent Tool for Querying Dreams
|
|
4
|
+
|
|
5
|
+
Phase 4.2: Dream System
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Dict
|
|
9
|
+
|
|
10
|
+
from ..skills.base import Skill, SkillCategory, SkillMetadata, SkillResult
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class DreamSkill(Skill):
|
|
14
|
+
"""Skill for querying Dream system insights"""
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def metadata(self) -> SkillMetadata:
|
|
18
|
+
return SkillMetadata(
|
|
19
|
+
name="dream_insight",
|
|
20
|
+
description="查询 Dream 系统生成的投资洞察",
|
|
21
|
+
category=SkillCategory.DREAM,
|
|
22
|
+
examples=[
|
|
23
|
+
"查看最近的投资洞察",
|
|
24
|
+
"有什么新的市场发现",
|
|
25
|
+
],
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
async def execute(self, context: Dict[str, Any]) -> SkillResult:
|
|
29
|
+
"""Execute dream insight query"""
|
|
30
|
+
from ..core.memory import MemoryStore
|
|
31
|
+
|
|
32
|
+
workspace = context.get("workspace", ".")
|
|
33
|
+
dream_store = MemoryStore(workspace).get_dream_store()
|
|
34
|
+
|
|
35
|
+
query = context.get("query", "recent")
|
|
36
|
+
limit = context.get("limit", 5)
|
|
37
|
+
|
|
38
|
+
if query == "recent":
|
|
39
|
+
dreams = dream_store.get_recent_dreams(limit)
|
|
40
|
+
else:
|
|
41
|
+
dreams = dream_store.get_dreams_by_type(query, limit)
|
|
42
|
+
|
|
43
|
+
data = [
|
|
44
|
+
{
|
|
45
|
+
"id": d.id,
|
|
46
|
+
"type": d.type,
|
|
47
|
+
"content": d.content,
|
|
48
|
+
"insights": d.insights,
|
|
49
|
+
"source": d.source,
|
|
50
|
+
"confidence": d.confidence,
|
|
51
|
+
"timestamp": d.timestamp,
|
|
52
|
+
}
|
|
53
|
+
for d in dreams
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
return SkillResult(success=True, data=data)
|
|
57
|
+
|
|
58
|
+
def get_parameters_schema(self) -> Dict[str, Any]:
|
|
59
|
+
"""Return parameter schema"""
|
|
60
|
+
return {
|
|
61
|
+
"type": "object",
|
|
62
|
+
"properties": {
|
|
63
|
+
"query": {
|
|
64
|
+
"type": "string",
|
|
65
|
+
"description": (
|
|
66
|
+
"查询类型: recent 或具体类型 "
|
|
67
|
+
"(wiki_insight/factor_insight/strategy_insight)"
|
|
68
|
+
),
|
|
69
|
+
"default": "recent",
|
|
70
|
+
},
|
|
71
|
+
"limit": {
|
|
72
|
+
"type": "integer",
|
|
73
|
+
"description": "返回数量",
|
|
74
|
+
"default": 5,
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
"""
|
|
3
|
+
Echo测试工具
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from .base import Tool
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class EchoTool(Tool):
|
|
10
|
+
"""Echo测试工具 - 返回输入内容"""
|
|
11
|
+
|
|
12
|
+
@property
|
|
13
|
+
def name(self) -> str:
|
|
14
|
+
return "echo"
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def description(self) -> str:
|
|
18
|
+
return "返回输入的消息内容,用于测试工具调用功能"
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def parameters(self) -> dict:
|
|
22
|
+
return {
|
|
23
|
+
"type": "object",
|
|
24
|
+
"properties": {
|
|
25
|
+
"message": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"description": "要返回的消息内容"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"required": ["message"]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def read_only(self) -> bool:
|
|
35
|
+
return True
|
|
36
|
+
|
|
37
|
+
async def execute(self, message: str, **kwargs) -> str:
|
|
38
|
+
return message
|