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,14 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
"""
|
|
3
|
+
Factor Skills
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from .ic_analysis import ICAnalysisSkill
|
|
7
|
+
from .group_backtest import GroupBacktestSkill
|
|
8
|
+
from .correlation import CorrelationSkill
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"ICAnalysisSkill",
|
|
12
|
+
"GroupBacktestSkill",
|
|
13
|
+
"CorrelationSkill",
|
|
14
|
+
]
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
"""
|
|
3
|
+
Correlation Analysis Skill
|
|
4
|
+
|
|
5
|
+
Phase 4.4: Factor Skills
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Dict
|
|
9
|
+
|
|
10
|
+
from ..base import Skill, SkillCategory, SkillMetadata, SkillResult
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class CorrelationSkill(Skill):
|
|
14
|
+
"""Factor Correlation Analysis Skill"""
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def metadata(self) -> SkillMetadata:
|
|
18
|
+
return SkillMetadata(
|
|
19
|
+
name="correlation",
|
|
20
|
+
description="计算因子之间的相关性矩阵,分析因子独立性",
|
|
21
|
+
category=SkillCategory.FACTOR,
|
|
22
|
+
tags=["因子分析", "相关性", "多重共线性"],
|
|
23
|
+
examples=[
|
|
24
|
+
"计算多个因子的相关性矩阵",
|
|
25
|
+
"检测高相关性因子",
|
|
26
|
+
],
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
async def execute(self, context: Dict[str, Any]) -> SkillResult:
|
|
30
|
+
"""Execute correlation analysis"""
|
|
31
|
+
factors = context.get("factors", ["factor1", "factor2"])
|
|
32
|
+
|
|
33
|
+
code = f'''
|
|
34
|
+
def correlation_analysis(data, factor_cols={factors}):
|
|
35
|
+
"""
|
|
36
|
+
相关性分析
|
|
37
|
+
- 计算因子之间的Pearson相关系数矩阵
|
|
38
|
+
- 识别高相关性因子对 (|corr| > 0.8)
|
|
39
|
+
|
|
40
|
+
Parameters:
|
|
41
|
+
- data: DataFrame with factor columns
|
|
42
|
+
- factor_cols: 因子列名列表
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
- corr_matrix: 相关性矩阵
|
|
46
|
+
- high_corr_pairs: 高相关性因子对
|
|
47
|
+
"""
|
|
48
|
+
import pandas as pd
|
|
49
|
+
import numpy as np
|
|
50
|
+
|
|
51
|
+
factor_data = data[factor_cols]
|
|
52
|
+
|
|
53
|
+
corr_matrix = factor_data.corr()
|
|
54
|
+
|
|
55
|
+
high_corr_threshold = 0.8
|
|
56
|
+
high_corr_pairs = []
|
|
57
|
+
for i in range(len(factor_cols)):
|
|
58
|
+
for j in range(i + 1, len(factor_cols)):
|
|
59
|
+
corr_val = corr_matrix.iloc[i, j]
|
|
60
|
+
if abs(corr_val) > high_corr_threshold:
|
|
61
|
+
high_corr_pairs.append({{
|
|
62
|
+
"factor1": factor_cols[i],
|
|
63
|
+
"factor2": factor_cols[j],
|
|
64
|
+
"correlation": corr_val,
|
|
65
|
+
}})
|
|
66
|
+
|
|
67
|
+
result = {{
|
|
68
|
+
"factors": {factors},
|
|
69
|
+
"corr_matrix": corr_matrix.to_dict(),
|
|
70
|
+
"high_corr_pairs": high_corr_pairs,
|
|
71
|
+
"mean_abs_corr": corr_matrix.abs().values[
|
|
72
|
+
np.triu_indices_from(corr_matrix.values, k=1)
|
|
73
|
+
].mean(),
|
|
74
|
+
}}
|
|
75
|
+
return result
|
|
76
|
+
'''
|
|
77
|
+
return SkillResult(
|
|
78
|
+
success=True,
|
|
79
|
+
data={
|
|
80
|
+
"skill": "correlation",
|
|
81
|
+
"factors": factors,
|
|
82
|
+
"code": code.strip(),
|
|
83
|
+
"description": f"相关性分析: {', '.join(factors)}",
|
|
84
|
+
},
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
def get_parameters_schema(self) -> Dict[str, Any]:
|
|
88
|
+
"""Return parameter schema"""
|
|
89
|
+
return {
|
|
90
|
+
"type": "object",
|
|
91
|
+
"properties": {
|
|
92
|
+
"factors": {
|
|
93
|
+
"type": "array",
|
|
94
|
+
"items": {"type": "string"},
|
|
95
|
+
"description": "因子列表",
|
|
96
|
+
"default": ["factor1", "factor2"],
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
"""
|
|
3
|
+
Group Backtest Skill
|
|
4
|
+
|
|
5
|
+
Phase 4.4: Factor Skills
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Dict
|
|
9
|
+
|
|
10
|
+
from ..base import Skill, SkillCategory, SkillMetadata, SkillResult
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class GroupBacktestSkill(Skill):
|
|
14
|
+
"""Group Backtest by Factor Quantiles"""
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def metadata(self) -> SkillMetadata:
|
|
18
|
+
return SkillMetadata(
|
|
19
|
+
name="group_backtest",
|
|
20
|
+
description="按因子分位数分组回测,计算各组收益差异",
|
|
21
|
+
category=SkillCategory.FACTOR,
|
|
22
|
+
tags=["因子分析", "分组回测", "分位数", "多空组合"],
|
|
23
|
+
examples=[
|
|
24
|
+
"按因子分5组回测",
|
|
25
|
+
"计算多空组合收益",
|
|
26
|
+
],
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
async def execute(self, context: Dict[str, Any]) -> SkillResult:
|
|
30
|
+
"""Execute group backtest"""
|
|
31
|
+
n_groups = context.get("n_groups", 5)
|
|
32
|
+
period = context.get("period", 20)
|
|
33
|
+
|
|
34
|
+
code = f'''
|
|
35
|
+
def group_backtest(
|
|
36
|
+
data, factor_col="factor", return_col="return",
|
|
37
|
+
n_groups={n_groups}, period={period},
|
|
38
|
+
):
|
|
39
|
+
"""
|
|
40
|
+
分组回测
|
|
41
|
+
- 按因子值分{n_groups}组
|
|
42
|
+
- 计算每组未来{period}天收益
|
|
43
|
+
- 计算多空组合收益 (Group{n_groups} - Group1)
|
|
44
|
+
|
|
45
|
+
Parameters:
|
|
46
|
+
- data: DataFrame with factor and return columns
|
|
47
|
+
- factor_col: 因子列名
|
|
48
|
+
- return_col: 收益列名
|
|
49
|
+
- n_groups: 分组数量
|
|
50
|
+
- period: 持有周期
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
- group_returns: 各组平均收益
|
|
54
|
+
- long_short_return: 多空组合收益
|
|
55
|
+
- turnover: 换手率
|
|
56
|
+
"""
|
|
57
|
+
import pandas as pd
|
|
58
|
+
import numpy as np
|
|
59
|
+
|
|
60
|
+
data = data.copy()
|
|
61
|
+
data["group"] = pd.qcut(data[factor_col], q={n_groups}, labels=False, duplicates="drop")
|
|
62
|
+
|
|
63
|
+
future_returns = data[return_col].shift(-{period})
|
|
64
|
+
data["future_return"] = future_returns
|
|
65
|
+
|
|
66
|
+
group_returns = data.groupby("group")["future_return"].mean()
|
|
67
|
+
|
|
68
|
+
if len(group_returns) >= {n_groups}:
|
|
69
|
+
long_short_return = group_returns.iloc[-1] - group_returns.iloc[0]
|
|
70
|
+
else:
|
|
71
|
+
long_short_return = np.nan
|
|
72
|
+
|
|
73
|
+
position = data.groupby("date")["group"].shift(1)
|
|
74
|
+
turnover = (position != position.shift(1)).sum() / len(data)
|
|
75
|
+
|
|
76
|
+
result = {{
|
|
77
|
+
"n_groups": {n_groups},
|
|
78
|
+
"period": {period},
|
|
79
|
+
"group_returns": group_returns.to_dict(),
|
|
80
|
+
"long_short_return": long_short_return,
|
|
81
|
+
"turnover": turnover,
|
|
82
|
+
"top_group_return": group_returns.iloc[-1] if len(group_returns) > 0 else np.nan,
|
|
83
|
+
"bottom_group_return": group_returns.iloc[0] if len(group_returns) > 0 else np.nan,
|
|
84
|
+
}}
|
|
85
|
+
return result
|
|
86
|
+
'''
|
|
87
|
+
return SkillResult(
|
|
88
|
+
success=True,
|
|
89
|
+
data={
|
|
90
|
+
"skill": "group_backtest",
|
|
91
|
+
"n_groups": n_groups,
|
|
92
|
+
"period": period,
|
|
93
|
+
"code": code.strip(),
|
|
94
|
+
"description": f"分组回测: {n_groups}组, {period}天持有期",
|
|
95
|
+
},
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
def get_parameters_schema(self) -> Dict[str, Any]:
|
|
99
|
+
"""Return parameter schema"""
|
|
100
|
+
return {
|
|
101
|
+
"type": "object",
|
|
102
|
+
"properties": {
|
|
103
|
+
"n_groups": {
|
|
104
|
+
"type": "integer",
|
|
105
|
+
"description": "分组数量",
|
|
106
|
+
"default": 5,
|
|
107
|
+
},
|
|
108
|
+
"period": {
|
|
109
|
+
"type": "integer",
|
|
110
|
+
"description": "持有周期(天)",
|
|
111
|
+
"default": 20,
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
"""
|
|
3
|
+
IC Analysis Skill
|
|
4
|
+
|
|
5
|
+
Phase 4.4: Factor Skills
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Dict
|
|
9
|
+
|
|
10
|
+
from ..base import Skill, SkillCategory, SkillMetadata, SkillResult
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ICAnalysisSkill(Skill):
|
|
14
|
+
"""IC (Information Coefficient) Analysis Skill"""
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def metadata(self) -> SkillMetadata:
|
|
18
|
+
return SkillMetadata(
|
|
19
|
+
name="ic_analysis",
|
|
20
|
+
description="计算并分析因子IC值,包括IC均值、ICIR、Rank IC等指标",
|
|
21
|
+
category=SkillCategory.FACTOR,
|
|
22
|
+
tags=["因子分析", "IC", "ICIR", "Rank IC"],
|
|
23
|
+
examples=[
|
|
24
|
+
"分析因子的IC表现",
|
|
25
|
+
"计算ICIR指标",
|
|
26
|
+
],
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
async def execute(self, context: Dict[str, Any]) -> SkillResult:
|
|
30
|
+
"""Execute IC analysis"""
|
|
31
|
+
factor_name = context.get("factor_name", "factor")
|
|
32
|
+
n_days = context.get("n_days", 252)
|
|
33
|
+
|
|
34
|
+
code = f'''
|
|
35
|
+
def calculate_ic_analysis(data, factor_col="factor", return_col="return", n_days={n_days}):
|
|
36
|
+
"""
|
|
37
|
+
IC分析
|
|
38
|
+
- 计算因子IC序列
|
|
39
|
+
- 计算IC均值、IC标准差、ICIR
|
|
40
|
+
- 计算Rank IC
|
|
41
|
+
|
|
42
|
+
Parameters:
|
|
43
|
+
- data: DataFrame with factor and return columns
|
|
44
|
+
- factor_col: 因子列名
|
|
45
|
+
- return_col: 收益列名
|
|
46
|
+
- n_days: 计算周期
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
- ic_series: IC时间序列
|
|
50
|
+
- ic_mean: IC均值
|
|
51
|
+
- ic_std: IC标准差
|
|
52
|
+
- icir: ICIR (IC均值/IC标准差)
|
|
53
|
+
- rank_ic_mean: Rank IC均值
|
|
54
|
+
"""
|
|
55
|
+
import pandas as pd
|
|
56
|
+
import numpy as np
|
|
57
|
+
|
|
58
|
+
ic_series = data[factor_col].corr(data[return_col])
|
|
59
|
+
rank_ic_series = data[factor_col].rank().corr(data[return_col].rank())
|
|
60
|
+
|
|
61
|
+
ic_mean = ic_series.mean()
|
|
62
|
+
ic_std = ic_series.std()
|
|
63
|
+
icir = ic_mean / ic_std if ic_std != 0 else 0
|
|
64
|
+
|
|
65
|
+
rank_ic_mean = rank_ic_series.mean()
|
|
66
|
+
|
|
67
|
+
result = {{
|
|
68
|
+
"factor_name": "{factor_name}",
|
|
69
|
+
"n_dates": n_days,
|
|
70
|
+
"ic_series": ic_series,
|
|
71
|
+
"ic_mean": ic_mean,
|
|
72
|
+
"ic_std": ic_std,
|
|
73
|
+
"icir": icir,
|
|
74
|
+
"rank_ic_mean": rank_ic_mean,
|
|
75
|
+
"ic_t_stat": ic_mean / (ic_std / np.sqrt(n_days)) if ic_std != 0 else 0,
|
|
76
|
+
}}
|
|
77
|
+
return result
|
|
78
|
+
'''
|
|
79
|
+
return SkillResult(
|
|
80
|
+
success=True,
|
|
81
|
+
data={
|
|
82
|
+
"skill": "ic_analysis",
|
|
83
|
+
"factor_name": factor_name,
|
|
84
|
+
"n_days": n_days,
|
|
85
|
+
"code": code.strip(),
|
|
86
|
+
"description": f"IC分析: {factor_name}, {n_days}天",
|
|
87
|
+
},
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
def get_parameters_schema(self) -> Dict[str, Any]:
|
|
91
|
+
"""Return parameter schema"""
|
|
92
|
+
return {
|
|
93
|
+
"type": "object",
|
|
94
|
+
"properties": {
|
|
95
|
+
"factor_name": {
|
|
96
|
+
"type": "string",
|
|
97
|
+
"description": "因子名称",
|
|
98
|
+
"default": "factor",
|
|
99
|
+
},
|
|
100
|
+
"n_days": {
|
|
101
|
+
"type": "integer",
|
|
102
|
+
"description": "分析天数",
|
|
103
|
+
"default": 252,
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
"""
|
|
3
|
+
Skill Loader - Progressive Skill Loader
|
|
4
|
+
|
|
5
|
+
Phase 4.1: Skill Infrastructure
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import importlib.util
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Dict, List, Any
|
|
11
|
+
|
|
12
|
+
from .base import Skill, SkillCategory
|
|
13
|
+
from .registry import SkillRegistry
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class SkillLoader:
|
|
17
|
+
"""Progressive Skill Loader"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, registry: SkillRegistry = None):
|
|
20
|
+
self.registry = registry or SkillRegistry()
|
|
21
|
+
self._loaded: Dict[str, bool] = {}
|
|
22
|
+
self._load_order: List[str] = []
|
|
23
|
+
self._failed: Dict[str, str] = {}
|
|
24
|
+
|
|
25
|
+
def discover_skills(self, skills_dir: Path) -> List[str]:
|
|
26
|
+
"""Discover all skills in the skills directory"""
|
|
27
|
+
discovered = []
|
|
28
|
+
if not skills_dir.exists():
|
|
29
|
+
return discovered
|
|
30
|
+
for item in skills_dir.iterdir():
|
|
31
|
+
if item.is_dir() and not item.name.startswith("_"):
|
|
32
|
+
skill_file = item / f"{item.name}.py"
|
|
33
|
+
if skill_file.exists():
|
|
34
|
+
discovered.append(str(item))
|
|
35
|
+
return discovered
|
|
36
|
+
|
|
37
|
+
async def load_skill(
|
|
38
|
+
self, skill_path: Path, category: SkillCategory = None
|
|
39
|
+
) -> bool:
|
|
40
|
+
"""Load a single skill from path"""
|
|
41
|
+
spec = importlib.util.spec_from_file_location("skill_module", skill_path)
|
|
42
|
+
if spec is None or spec.loader is None:
|
|
43
|
+
return False
|
|
44
|
+
module = importlib.util.module_from_spec(spec)
|
|
45
|
+
try:
|
|
46
|
+
spec.loader.exec_module(module)
|
|
47
|
+
except Exception as e:
|
|
48
|
+
self._failed[str(skill_path)] = str(e)
|
|
49
|
+
return False
|
|
50
|
+
for attr_name in dir(module):
|
|
51
|
+
attr = getattr(module, attr_name)
|
|
52
|
+
if isinstance(attr, type) and issubclass(attr, Skill) and attr != Skill:
|
|
53
|
+
skill_instance = attr()
|
|
54
|
+
if category:
|
|
55
|
+
skill_instance._metadata.category = category
|
|
56
|
+
self.registry.register(skill_instance)
|
|
57
|
+
self._loaded[skill_instance.name] = True
|
|
58
|
+
self._load_order.append(skill_instance.name)
|
|
59
|
+
return True
|
|
60
|
+
return False
|
|
61
|
+
|
|
62
|
+
async def load_all(self, skills_dir: Path) -> Dict[str, bool]:
|
|
63
|
+
"""Load all discovered skills"""
|
|
64
|
+
results = {}
|
|
65
|
+
for skill_path in self.discover_skills(skills_dir):
|
|
66
|
+
skill_name = Path(skill_path).name
|
|
67
|
+
success = await self.load_skill(
|
|
68
|
+
Path(skill_path) / f"{skill_name}.py"
|
|
69
|
+
)
|
|
70
|
+
results[skill_name] = success
|
|
71
|
+
return results
|
|
72
|
+
|
|
73
|
+
async def progressive_load(
|
|
74
|
+
self, skills_dir: Path, requested_skills: List[str]
|
|
75
|
+
) -> Dict[str, Skill]:
|
|
76
|
+
"""Progressive load: only load requested skills"""
|
|
77
|
+
loaded = {}
|
|
78
|
+
for skill_name in requested_skills:
|
|
79
|
+
skill_path = skills_dir / skill_name / f"{skill_name}.py"
|
|
80
|
+
if skill_path.exists() and skill_name not in self._loaded:
|
|
81
|
+
success = await self.load_skill(skill_path)
|
|
82
|
+
if success:
|
|
83
|
+
skill = self.registry.get(skill_name)
|
|
84
|
+
if skill:
|
|
85
|
+
loaded[skill_name] = skill
|
|
86
|
+
return loaded
|
|
87
|
+
|
|
88
|
+
def get_loading_status(self) -> Dict[str, Any]:
|
|
89
|
+
"""Get loading status"""
|
|
90
|
+
return {
|
|
91
|
+
"loaded": self._loaded,
|
|
92
|
+
"load_order": self._load_order,
|
|
93
|
+
"failed": self._failed,
|
|
94
|
+
"total_loaded": len(self._loaded),
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
def reload_skill(self, name: str) -> bool:
|
|
98
|
+
"""Reload a skill"""
|
|
99
|
+
if name in self._loaded:
|
|
100
|
+
self._loaded.pop(name)
|
|
101
|
+
self._load_order.remove(name)
|
|
102
|
+
self.registry.unregister(name)
|
|
103
|
+
return True
|
|
104
|
+
|
|
105
|
+
def is_loaded(self, name: str) -> bool:
|
|
106
|
+
"""Check if a skill is loaded"""
|
|
107
|
+
return name in self._loaded
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
"""
|
|
3
|
+
Skill Registry - Singleton Registry for Skills (Thread-Safe)
|
|
4
|
+
|
|
5
|
+
Phase 4.1: Skill Infrastructure
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import threading
|
|
9
|
+
from typing import Dict, List, Optional
|
|
10
|
+
from collections import defaultdict
|
|
11
|
+
|
|
12
|
+
from .base import Skill, SkillCategory
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class SkillRegistry:
|
|
16
|
+
"""Skill Registry (Singleton + Thread-Safe)"""
|
|
17
|
+
|
|
18
|
+
_instance: Optional["SkillRegistry"] = None
|
|
19
|
+
_lock = threading.RLock()
|
|
20
|
+
|
|
21
|
+
def __new__(cls):
|
|
22
|
+
with cls._lock:
|
|
23
|
+
if cls._instance is None:
|
|
24
|
+
cls._instance = super().__new__(cls)
|
|
25
|
+
cls._instance._skills: Dict[str, Skill] = {}
|
|
26
|
+
cls._instance._categories: Dict[
|
|
27
|
+
SkillCategory, List[str]
|
|
28
|
+
] = defaultdict(list)
|
|
29
|
+
cls._instance._aliases: Dict[str, str] = {}
|
|
30
|
+
return cls._instance
|
|
31
|
+
|
|
32
|
+
def register(self, skill: Skill, aliases: List[str] = None) -> None:
|
|
33
|
+
"""Register a skill"""
|
|
34
|
+
name = skill.name
|
|
35
|
+
if name in self._skills:
|
|
36
|
+
raise ValueError(f"Skill '{name}' already registered")
|
|
37
|
+
self._skills[name] = skill
|
|
38
|
+
self._categories[skill.category].append(name)
|
|
39
|
+
if aliases:
|
|
40
|
+
for alias in aliases:
|
|
41
|
+
self._aliases[alias] = name
|
|
42
|
+
|
|
43
|
+
def get(self, name: str) -> Optional[Skill]:
|
|
44
|
+
"""Get a skill by name or alias"""
|
|
45
|
+
resolved = self._aliases.get(name, name)
|
|
46
|
+
return self._skills.get(resolved)
|
|
47
|
+
|
|
48
|
+
def list_by_category(self, category: SkillCategory) -> List[Skill]:
|
|
49
|
+
"""List all skills in a category"""
|
|
50
|
+
return [self._skills[name] for name in self._categories.get(category, [])]
|
|
51
|
+
|
|
52
|
+
def list_all(self) -> List[Skill]:
|
|
53
|
+
"""List all registered skills"""
|
|
54
|
+
return list(self._skills.values())
|
|
55
|
+
|
|
56
|
+
def list_names(self) -> List[str]:
|
|
57
|
+
"""List all skill names"""
|
|
58
|
+
return list(self._skills.keys())
|
|
59
|
+
|
|
60
|
+
def exists(self, name: str) -> bool:
|
|
61
|
+
"""Check if a skill exists"""
|
|
62
|
+
return name in self._skills or name in self._aliases
|
|
63
|
+
|
|
64
|
+
def unregister(self, name: str) -> bool:
|
|
65
|
+
"""Unregister a skill"""
|
|
66
|
+
resolved = self._aliases.pop(name, None) or name
|
|
67
|
+
if resolved not in self._skills:
|
|
68
|
+
return False
|
|
69
|
+
skill = self._skills.pop(resolved)
|
|
70
|
+
if resolved in self._categories[skill.category]:
|
|
71
|
+
self._categories[skill.category].remove(resolved)
|
|
72
|
+
return True
|
|
73
|
+
|
|
74
|
+
def clear(self) -> None:
|
|
75
|
+
"""Clear all skills"""
|
|
76
|
+
self._skills.clear()
|
|
77
|
+
self._categories.clear()
|
|
78
|
+
self._aliases.clear()
|
|
79
|
+
|
|
80
|
+
def get_skill_info(self, name: str) -> Optional[Dict]:
|
|
81
|
+
"""Get skill information"""
|
|
82
|
+
skill = self.get(name)
|
|
83
|
+
if not skill:
|
|
84
|
+
return None
|
|
85
|
+
return {
|
|
86
|
+
"name": skill.name,
|
|
87
|
+
"description": skill.description,
|
|
88
|
+
"category": skill.category.value,
|
|
89
|
+
"version": skill.metadata.version,
|
|
90
|
+
"tags": skill.metadata.tags,
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
def search(self, query: str, limit: int = 10) -> List[Dict]:
|
|
94
|
+
"""Search skills by name or description"""
|
|
95
|
+
results = []
|
|
96
|
+
query_lower = query.lower()
|
|
97
|
+
for skill in self._skills.values():
|
|
98
|
+
if (
|
|
99
|
+
query_lower in skill.name.lower()
|
|
100
|
+
or query_lower in skill.description.lower()
|
|
101
|
+
):
|
|
102
|
+
results.append(self.get_skill_info(skill.name))
|
|
103
|
+
if len(results) >= limit:
|
|
104
|
+
break
|
|
105
|
+
return results
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
"""
|
|
3
|
+
Strategy Skills
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from .dual_ma import DualMaSkill
|
|
7
|
+
from .bollinger import BollingerSkill
|
|
8
|
+
from .momentum import MomentumSkill
|
|
9
|
+
from .rsi_reversal import RSIReversalSkill
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"DualMaSkill",
|
|
13
|
+
"BollingerSkill",
|
|
14
|
+
"MomentumSkill",
|
|
15
|
+
"RSIReversalSkill",
|
|
16
|
+
]
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
"""
|
|
3
|
+
Bollinger Bands Strategy Skill
|
|
4
|
+
|
|
5
|
+
Phase 4.3: Strategy Skills
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Dict
|
|
9
|
+
|
|
10
|
+
from ..base import Skill, SkillCategory, SkillMetadata, SkillResult
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class BollingerSkill(Skill):
|
|
14
|
+
"""Bollinger Bands Strategy"""
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def metadata(self) -> SkillMetadata:
|
|
18
|
+
return SkillMetadata(
|
|
19
|
+
name="bollinger_strategy",
|
|
20
|
+
description="布林带策略 - 价格触及下轨买入,触及上轨卖出",
|
|
21
|
+
category=SkillCategory.STRATEGY,
|
|
22
|
+
tags=["趋势跟踪", "布林带", "均值回归"],
|
|
23
|
+
examples=[
|
|
24
|
+
"生成布林带策略代码",
|
|
25
|
+
"布林带策略参数优化",
|
|
26
|
+
],
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
async def execute(self, context: Dict[str, Any]) -> SkillResult:
|
|
30
|
+
"""Execute Bollinger bands strategy generation"""
|
|
31
|
+
period = context.get("period", 20)
|
|
32
|
+
std_dev = context.get("std_dev", 2)
|
|
33
|
+
|
|
34
|
+
code = f'''
|
|
35
|
+
def bollinger_strategy(data, period={period}, std_dev={std_dev}):
|
|
36
|
+
"""
|
|
37
|
+
布林带策略
|
|
38
|
+
- Period: {period}日移动平均
|
|
39
|
+
- Std Dev: {std_dev}倍标准差
|
|
40
|
+
Signals:
|
|
41
|
+
- 价格触及下轨买入(1)
|
|
42
|
+
- 价格触及上轨卖出(-1)
|
|
43
|
+
- 持有(0)
|
|
44
|
+
"""
|
|
45
|
+
import pandas as pd
|
|
46
|
+
|
|
47
|
+
data["mid"] = data["close"].rolling({period}).mean()
|
|
48
|
+
data["std"] = data["close"].rolling({period}).std()
|
|
49
|
+
data["upper"] = data["mid"] + {std_dev} * data["std"]
|
|
50
|
+
data["lower"] = data["mid"] - {std_dev} * data["std"]
|
|
51
|
+
|
|
52
|
+
data["signal"] = 0
|
|
53
|
+
data.loc[data["close"] <= data["lower"], "signal"] = 1
|
|
54
|
+
data.loc[data["close"] >= data["upper"], "signal"] = -1
|
|
55
|
+
|
|
56
|
+
data["position"] = data["signal"].shift(1)
|
|
57
|
+
return data
|
|
58
|
+
'''
|
|
59
|
+
return SkillResult(
|
|
60
|
+
success=True,
|
|
61
|
+
data={
|
|
62
|
+
"strategy": "bollinger",
|
|
63
|
+
"period": period,
|
|
64
|
+
"std_dev": std_dev,
|
|
65
|
+
"code": code.strip(),
|
|
66
|
+
"description": f"布林带策略: {period}日周期,{std_dev}倍标准差",
|
|
67
|
+
},
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
def get_parameters_schema(self) -> Dict[str, Any]:
|
|
71
|
+
"""Return parameter schema"""
|
|
72
|
+
return {
|
|
73
|
+
"type": "object",
|
|
74
|
+
"properties": {
|
|
75
|
+
"period": {
|
|
76
|
+
"type": "integer",
|
|
77
|
+
"description": "移动平均周期",
|
|
78
|
+
"default": 20,
|
|
79
|
+
},
|
|
80
|
+
"std_dev": {
|
|
81
|
+
"type": "number",
|
|
82
|
+
"description": "标准差倍数",
|
|
83
|
+
"default": 2,
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
}
|