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,398 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
"""``quantnodes serve / stop / status / logs`` — backend lifecycle.
|
|
3
|
+
|
|
4
|
+
v3.1.0: Zero-subprocess architecture.
|
|
5
|
+
|
|
6
|
+
Commands:
|
|
7
|
+
serve — 启动 FastAPI (+ nanobot gateway), 可选前端/ MCP
|
|
8
|
+
stop — 通过 pidfile 停止 serve
|
|
9
|
+
status — 打印 health + /api/agent/status JSON
|
|
10
|
+
logs — tail -f logs/quantnodes_serve.log
|
|
11
|
+
|
|
12
|
+
Design:
|
|
13
|
+
- serve 直接调用 uvicorn.Server.run() (同进程, 无子进程)
|
|
14
|
+
- --daemon 时双 fork 脱离终端, 写 .quantnodes.pid
|
|
15
|
+
- --frontend 时 spawn npm dev server (subprocess, lifespan 管理)
|
|
16
|
+
- --mcp 时 spawn MCP server (subprocess, 供外部客户端)
|
|
17
|
+
- 前台时 Ctrl+C 直接终止 (所有组件 in-process)
|
|
18
|
+
"""
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import json
|
|
22
|
+
import os
|
|
23
|
+
import signal
|
|
24
|
+
import subprocess
|
|
25
|
+
import sys
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
from typing import Optional
|
|
28
|
+
|
|
29
|
+
from QuantNodes.cli.command import Command
|
|
30
|
+
|
|
31
|
+
from QuantNodes.constants import DEFAULT_WEBSOCKET_PORT
|
|
32
|
+
|
|
33
|
+
from .._helpers import (
|
|
34
|
+
DEFAULT_API_PORT,
|
|
35
|
+
DEFAULT_FRONTEND_PORT,
|
|
36
|
+
DEFAULT_GATEWAY_PORT,
|
|
37
|
+
DEFAULT_HOST,
|
|
38
|
+
PIDFILE_NAME,
|
|
39
|
+
get_project_root,
|
|
40
|
+
is_pid_alive,
|
|
41
|
+
is_port_free,
|
|
42
|
+
print_nanobot_install_hint,
|
|
43
|
+
read_pidfile,
|
|
44
|
+
remove_pidfile,
|
|
45
|
+
wait_for_health,
|
|
46
|
+
write_pidfile,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _read_gateway_token() -> str:
|
|
51
|
+
"""Read the gateway tokenIssueSecret from .agent/nanobot_config.json."""
|
|
52
|
+
try:
|
|
53
|
+
cfg_path = get_project_root() / ".agent" / "nanobot_config.json"
|
|
54
|
+
if cfg_path.exists():
|
|
55
|
+
cfg = json.loads(cfg_path.read_text(encoding="utf-8"))
|
|
56
|
+
return cfg.get("channels", {}).get("websocket", {}).get("token", "")
|
|
57
|
+
except Exception:
|
|
58
|
+
pass
|
|
59
|
+
return ""
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _log_dir() -> Path:
|
|
63
|
+
"""Return logs/ dir at project root, creating it on first call."""
|
|
64
|
+
log_dir = get_project_root() / "logs"
|
|
65
|
+
log_dir.mkdir(parents=True, exist_ok=True)
|
|
66
|
+
return log_dir
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _serve_log_path() -> Path:
|
|
70
|
+
"""Path to the rolling serve log file."""
|
|
71
|
+
return _log_dir() / "quantnodes_serve.log"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _start_frontend(host: str, frontend_port: int, api_port: int,
|
|
75
|
+
gateway_port: int = DEFAULT_GATEWAY_PORT) -> subprocess.Popen:
|
|
76
|
+
"""Spawn Vite dev server (npm run dev) with HOST/PORT/API_PORT injected.
|
|
77
|
+
|
|
78
|
+
The frontend subprocess lifecycle is managed by the caller's finally
|
|
79
|
+
block (terminate on shutdown).
|
|
80
|
+
"""
|
|
81
|
+
env = os.environ.copy()
|
|
82
|
+
env["HOST"] = host
|
|
83
|
+
env["PORT"] = str(frontend_port)
|
|
84
|
+
env["API_PORT"] = str(api_port)
|
|
85
|
+
env["GATEWAY_PORT"] = str(gateway_port)
|
|
86
|
+
env["VITE_NANOBOT_GATEWAY_URL"] = f"http://{host}:{gateway_port}/"
|
|
87
|
+
return subprocess.Popen(
|
|
88
|
+
["npm", "run", "dev"],
|
|
89
|
+
cwd=str(get_project_root() / "frontend"),
|
|
90
|
+
env=env,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _start_mcp_server(host: str, port: int) -> subprocess.Popen:
|
|
95
|
+
"""Spawn MCP server as HTTP subprocess for external clients.
|
|
96
|
+
|
|
97
|
+
The MCP server exposes quant tools over MCP protocol (HTTP transport)
|
|
98
|
+
so that Claude Desktop, Cursor, etc. can connect.
|
|
99
|
+
"""
|
|
100
|
+
cmd = [
|
|
101
|
+
sys.executable, "-m", "QuantNodes.mcp_server",
|
|
102
|
+
"--transport", "http",
|
|
103
|
+
"--host", host,
|
|
104
|
+
"--port", str(port),
|
|
105
|
+
]
|
|
106
|
+
return subprocess.Popen(
|
|
107
|
+
cmd, cwd=str(get_project_root()),
|
|
108
|
+
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _print_startup_info(args, pid: int) -> None:
|
|
113
|
+
"""Print startup info (used in daemon mode)."""
|
|
114
|
+
api_url = f"http://{args.host}:{args.port}"
|
|
115
|
+
gateway_url = f"http://{args.host}:{args.gateway_port}"
|
|
116
|
+
log_path = _serve_log_path()
|
|
117
|
+
print("=" * 50)
|
|
118
|
+
print("✓ QuantNodes 已后台启动")
|
|
119
|
+
print("=" * 50)
|
|
120
|
+
print(f" API: {api_url}")
|
|
121
|
+
print(f" WebUI: {gateway_url}/")
|
|
122
|
+
_token = _read_gateway_token()
|
|
123
|
+
if _token:
|
|
124
|
+
print(f" Token: {_token}")
|
|
125
|
+
if getattr(args, "mcp", False):
|
|
126
|
+
print(f" MCP: http://{args.host}:{getattr(args, 'mcp_port', DEFAULT_WEBSOCKET_PORT)}/")
|
|
127
|
+
print(f" PID: {pid} 日志: {log_path}")
|
|
128
|
+
print(f" 停止: quantnodes stop")
|
|
129
|
+
print(f" 状态: quantnodes status")
|
|
130
|
+
print()
|
|
131
|
+
print_nanobot_install_hint()
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def _kill_procs(*procs: Optional[subprocess.Popen]) -> None:
|
|
135
|
+
"""Terminate a list of subprocesses gracefully, with SIGKILL fallback."""
|
|
136
|
+
for proc in procs:
|
|
137
|
+
if proc is None or proc.poll() is not None:
|
|
138
|
+
continue
|
|
139
|
+
proc.terminate()
|
|
140
|
+
try:
|
|
141
|
+
proc.wait(timeout=5)
|
|
142
|
+
except subprocess.TimeoutExpired:
|
|
143
|
+
proc.kill()
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def cmd_serve(args) -> int:
|
|
147
|
+
"""Start the QuantNodes backend.
|
|
148
|
+
|
|
149
|
+
Foreground by default; --daemon double-forks and writes .quantnodes.pid.
|
|
150
|
+
uvicorn runs in-process (no subprocess) for clean signal handling.
|
|
151
|
+
"""
|
|
152
|
+
if not load_env_quietly():
|
|
153
|
+
print("⚠ .env 未找到(继续运行,部分功能可能受限)")
|
|
154
|
+
print(" 建议运行: quantnodes init 生成 .env")
|
|
155
|
+
|
|
156
|
+
# 1. Port conflict detection
|
|
157
|
+
if not is_port_free(args.port):
|
|
158
|
+
print(f"✗ 后端端口 {args.port} 已被占用")
|
|
159
|
+
print(f" 提示: 用 --port 换其他端口 (默认 {DEFAULT_API_PORT})")
|
|
160
|
+
return 1
|
|
161
|
+
if not is_port_free(args.gateway_port):
|
|
162
|
+
print(f"✗ nanobot gateway 端口 {args.gateway_port} 已被占用")
|
|
163
|
+
if args.gateway_port == 18080:
|
|
164
|
+
print(" 提示: 18080 常被 gpustack 等占用,建议换 --gateway-port 18090")
|
|
165
|
+
print(f" 当前默认: {DEFAULT_GATEWAY_PORT}")
|
|
166
|
+
return 1
|
|
167
|
+
if getattr(args, "mcp", False) and not is_port_free(getattr(args, "mcp_port", DEFAULT_WEBSOCKET_PORT)):
|
|
168
|
+
print(f"✗ MCP server 端口 {args.mcp_port} 已被占用")
|
|
169
|
+
return 1
|
|
170
|
+
|
|
171
|
+
# 2. --check-env
|
|
172
|
+
if args.check_env and not os.environ.get("QUANTNODES__LLM__API_KEY"):
|
|
173
|
+
print("✗ .env 中缺少 QUANTNODES__LLM__API_KEY")
|
|
174
|
+
print(" 请运行: quantnodes init 或手动编辑 .env")
|
|
175
|
+
return 1
|
|
176
|
+
|
|
177
|
+
# 3. Set environment variables (for lifespan / nanobot)
|
|
178
|
+
os.environ["NANOBOT_GATEWAY_HOST"] = args.host
|
|
179
|
+
os.environ["NANOBOT_GATEWAY_PORT"] = str(args.gateway_port)
|
|
180
|
+
|
|
181
|
+
# 4. Optional frontend subprocess
|
|
182
|
+
frontend_proc: Optional[subprocess.Popen] = None
|
|
183
|
+
if args.frontend:
|
|
184
|
+
frontend_proc = _start_frontend(args.host, args.frontend_port, args.port,
|
|
185
|
+
gateway_port=args.gateway_port)
|
|
186
|
+
|
|
187
|
+
# 5. Optional MCP server subprocess
|
|
188
|
+
mcp_proc: Optional[subprocess.Popen] = None
|
|
189
|
+
if getattr(args, "mcp", False):
|
|
190
|
+
mcp_proc = _start_mcp_server(args.host, getattr(args, "mcp_port", DEFAULT_WEBSOCKET_PORT))
|
|
191
|
+
|
|
192
|
+
# 6. Daemon mode: double-fork to detach from terminal
|
|
193
|
+
if args.daemon:
|
|
194
|
+
# Fork 1: exit parent, child becomes session leader
|
|
195
|
+
if os.fork() > 0:
|
|
196
|
+
_kill_procs(frontend_proc, mcp_proc)
|
|
197
|
+
sys.exit(0)
|
|
198
|
+
os.setsid()
|
|
199
|
+
# Fork 2: prevent re-acquiring controlling terminal
|
|
200
|
+
if os.fork() > 0:
|
|
201
|
+
sys.exit(0)
|
|
202
|
+
# Redirect stdio to log file
|
|
203
|
+
sys.stdin = open(os.devnull)
|
|
204
|
+
log_path = _serve_log_path()
|
|
205
|
+
log_fd = open(log_path, "a")
|
|
206
|
+
sys.stdout = sys.stderr = log_fd
|
|
207
|
+
write_pidfile(os.getpid())
|
|
208
|
+
_print_startup_info(args, os.getpid())
|
|
209
|
+
|
|
210
|
+
# 7. Run uvicorn in-process (blocking)
|
|
211
|
+
import uvicorn
|
|
212
|
+
config = uvicorn.Config(
|
|
213
|
+
"api.main:app",
|
|
214
|
+
host=args.host,
|
|
215
|
+
port=args.port,
|
|
216
|
+
log_level="info",
|
|
217
|
+
)
|
|
218
|
+
server = uvicorn.Server(config)
|
|
219
|
+
|
|
220
|
+
# Signal: SIGTERM → graceful shutdown
|
|
221
|
+
def _shutdown(sig, frame):
|
|
222
|
+
server.should_exit = True
|
|
223
|
+
signal.signal(signal.SIGTERM, _shutdown)
|
|
224
|
+
|
|
225
|
+
try:
|
|
226
|
+
server.run()
|
|
227
|
+
except KeyboardInterrupt:
|
|
228
|
+
pass
|
|
229
|
+
finally:
|
|
230
|
+
_kill_procs(frontend_proc, mcp_proc)
|
|
231
|
+
if not args.daemon:
|
|
232
|
+
remove_pidfile()
|
|
233
|
+
|
|
234
|
+
return 0
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def load_env_quietly() -> bool:
|
|
238
|
+
"""Call load_env_file without printing on missing optional dep."""
|
|
239
|
+
try:
|
|
240
|
+
from .._helpers import load_env_file
|
|
241
|
+
return load_env_file()
|
|
242
|
+
except Exception:
|
|
243
|
+
return False
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def cmd_stop(args) -> int:
|
|
247
|
+
"""Send SIGTERM to the PID stored in .quantnodes.pid."""
|
|
248
|
+
pid = read_pidfile()
|
|
249
|
+
if pid is None:
|
|
250
|
+
print(f"未找到 {PIDFILE_NAME},服务可能未启动或已手动停止")
|
|
251
|
+
return 1
|
|
252
|
+
if not is_pid_alive(pid):
|
|
253
|
+
print(f"PID {pid} 不存在(stale pidfile,已清理)")
|
|
254
|
+
remove_pidfile()
|
|
255
|
+
return 0
|
|
256
|
+
try:
|
|
257
|
+
os.kill(pid, signal.SIGTERM)
|
|
258
|
+
print(f"✓ 已发送 SIGTERM 到 PID {pid}")
|
|
259
|
+
except ProcessLookupError:
|
|
260
|
+
print(f"PID {pid} 已退出")
|
|
261
|
+
except PermissionError:
|
|
262
|
+
print(f"✗ 无权终止 PID {pid}(可能属于其他用户)")
|
|
263
|
+
return 1
|
|
264
|
+
remove_pidfile()
|
|
265
|
+
print(f" pidfile 已清理: {PIDFILE_NAME}")
|
|
266
|
+
return 0
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def cmd_status(args) -> int:
|
|
270
|
+
"""Print pidfile + health endpoint + /api/agent/status as one JSON summary."""
|
|
271
|
+
api_url = args.api_url.rstrip("/")
|
|
272
|
+
pid = read_pidfile()
|
|
273
|
+
pid_alive = is_pid_alive(pid) if pid else False
|
|
274
|
+
|
|
275
|
+
summary: dict = {
|
|
276
|
+
"pidfile": {
|
|
277
|
+
"path": str(Path.cwd() / PIDFILE_NAME),
|
|
278
|
+
"pid": pid,
|
|
279
|
+
"alive": pid_alive,
|
|
280
|
+
},
|
|
281
|
+
"api_url": api_url,
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
try:
|
|
285
|
+
import httpx
|
|
286
|
+
r = httpx.get(f"{api_url}/api/agent/status", timeout=3.0)
|
|
287
|
+
if r.status_code == 200:
|
|
288
|
+
summary["agent_status"] = r.json()
|
|
289
|
+
summary["reachable"] = True
|
|
290
|
+
else:
|
|
291
|
+
summary["reachable"] = False
|
|
292
|
+
summary["http_status"] = r.status_code
|
|
293
|
+
except Exception as e:
|
|
294
|
+
summary["reachable"] = False
|
|
295
|
+
summary["error"] = str(e)
|
|
296
|
+
|
|
297
|
+
import json
|
|
298
|
+
print(json.dumps(summary, indent=2, ensure_ascii=False))
|
|
299
|
+
return 0 if summary.get("agent_status", {}).get("state") == "running" else 1
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def cmd_logs(args) -> int:
|
|
303
|
+
"""tail -f logs/quantnodes_serve.log (or cat if --no-follow)."""
|
|
304
|
+
log_path = _serve_log_path()
|
|
305
|
+
if not log_path.is_file():
|
|
306
|
+
print(f"日志文件不存在: {log_path}")
|
|
307
|
+
print("(需要先运行 quantnodes serve)")
|
|
308
|
+
return 1
|
|
309
|
+
if args.follow:
|
|
310
|
+
try:
|
|
311
|
+
subprocess.run(["tail", "-F", "-n", "+1", str(log_path)])
|
|
312
|
+
except KeyboardInterrupt:
|
|
313
|
+
pass
|
|
314
|
+
else:
|
|
315
|
+
try:
|
|
316
|
+
subprocess.run(["tail", "-n", "200", str(log_path)])
|
|
317
|
+
except FileNotFoundError:
|
|
318
|
+
text = log_path.read_text(encoding="utf-8", errors="replace")
|
|
319
|
+
lines = text.splitlines()[-200:]
|
|
320
|
+
print("\n".join(lines))
|
|
321
|
+
return 0
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
# ============================================================================
|
|
325
|
+
# Command classes (registry pattern)
|
|
326
|
+
# ============================================================================
|
|
327
|
+
|
|
328
|
+
class ServeCommand(Command):
|
|
329
|
+
"""``quantnodes serve`` — 启动后端(+ 可选前端/MCP)."""
|
|
330
|
+
|
|
331
|
+
name = "serve"
|
|
332
|
+
description = "启动 QuantNodes 后端(FastAPI + nanobot gateway)"
|
|
333
|
+
|
|
334
|
+
def add_arguments(self, subparsers) -> None:
|
|
335
|
+
p = subparsers.add_parser(self.name, help=self.description)
|
|
336
|
+
p.add_argument("--host", default=DEFAULT_HOST, help=f"绑定主机 (默认 {DEFAULT_HOST})")
|
|
337
|
+
p.add_argument("--port", type=int, default=DEFAULT_API_PORT,
|
|
338
|
+
help=f"后端端口 (默认 {DEFAULT_API_PORT})")
|
|
339
|
+
p.add_argument("--gateway-port", type=int, default=DEFAULT_GATEWAY_PORT,
|
|
340
|
+
help=f"nanobot WebSocket gateway 端口 (默认 {DEFAULT_GATEWAY_PORT})")
|
|
341
|
+
p.add_argument("--frontend", action="store_true",
|
|
342
|
+
help="同时启动 Vite dev server (开发模式)")
|
|
343
|
+
p.add_argument("--frontend-port", type=int, default=DEFAULT_FRONTEND_PORT,
|
|
344
|
+
help=f"前端端口 (默认 {DEFAULT_FRONTEND_PORT})")
|
|
345
|
+
p.add_argument("--mcp", action="store_true",
|
|
346
|
+
help="同时启动 MCP server (供 Claude Desktop / Cursor 等外部客户端)")
|
|
347
|
+
p.add_argument("--mcp-port", type=int, default=DEFAULT_WEBSOCKET_PORT,
|
|
348
|
+
help=f"MCP server HTTP 端口 (默认 {DEFAULT_WEBSOCKET_PORT})")
|
|
349
|
+
p.add_argument("--daemon", action="store_true",
|
|
350
|
+
help="后台运行,写入 .quantnodes.pid")
|
|
351
|
+
p.add_argument("--check-env", action="store_true",
|
|
352
|
+
help="启动前校验 .env 中 QUANTNODES__LLM__API_KEY")
|
|
353
|
+
|
|
354
|
+
def run(self, args) -> int:
|
|
355
|
+
return cmd_serve(args)
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
class StopCommand(Command):
|
|
359
|
+
"""``quantnodes stop`` — 停止 serve(通过 .quantnodes.pid)."""
|
|
360
|
+
|
|
361
|
+
name = "stop"
|
|
362
|
+
description = "通过 pidfile 停止 quantnodes serve"
|
|
363
|
+
|
|
364
|
+
def add_arguments(self, subparsers) -> None:
|
|
365
|
+
subparsers.add_parser(self.name, help=self.description)
|
|
366
|
+
|
|
367
|
+
def run(self, args) -> int:
|
|
368
|
+
return cmd_stop(args)
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
class StatusCommand(Command):
|
|
372
|
+
"""``quantnodes status`` — 综合服务状态(pidfile + /api/agent/status)."""
|
|
373
|
+
|
|
374
|
+
name = "status"
|
|
375
|
+
description = "查服务运行状态(pidfile + /api/agent/status)"
|
|
376
|
+
|
|
377
|
+
def add_arguments(self, subparsers) -> None:
|
|
378
|
+
p = subparsers.add_parser(self.name, help=self.description)
|
|
379
|
+
p.add_argument("--api-url", default=f"http://{DEFAULT_HOST}:{DEFAULT_API_PORT}",
|
|
380
|
+
help=f"FastAPI base URL (默认 http://{DEFAULT_HOST}:{DEFAULT_API_PORT})")
|
|
381
|
+
|
|
382
|
+
def run(self, args) -> int:
|
|
383
|
+
return cmd_status(args)
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
class LogsCommand(Command):
|
|
387
|
+
"""``quantnodes logs`` — 查看 / tail serve 日志."""
|
|
388
|
+
|
|
389
|
+
name = "logs"
|
|
390
|
+
description = "查看 serve 日志 (logs/quantnodes_serve.log)"
|
|
391
|
+
|
|
392
|
+
def add_arguments(self, subparsers) -> None:
|
|
393
|
+
p = subparsers.add_parser(self.name, help=self.description)
|
|
394
|
+
p.add_argument("-f", "--follow", action="store_true",
|
|
395
|
+
help="实时滚动 (默认只打印最后 200 行)")
|
|
396
|
+
|
|
397
|
+
def run(self, args) -> int:
|
|
398
|
+
return cmd_logs(args)
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
"""``quantnodes version`` and ``quantnodes help`` commands.
|
|
3
|
+
|
|
4
|
+
Phase 3.1 (2026-06-22): 改为 Command pattern — VersionCommand + HelpCommand.
|
|
5
|
+
旧的 cmd_version / cmd_help 函数保留作 backward compat.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from QuantNodes import __version__
|
|
9
|
+
from QuantNodes.cli.command import Command
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def cmd_version(args) -> int:
|
|
13
|
+
"""Show version information."""
|
|
14
|
+
print(f"QuantNodes version {__version__}")
|
|
15
|
+
return 0
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def cmd_help(args) -> int:
|
|
19
|
+
"""Show help message."""
|
|
20
|
+
print("""
|
|
21
|
+
QuantNodes CLI - 量化研究节点架构命令行工具
|
|
22
|
+
|
|
23
|
+
用法:
|
|
24
|
+
quantnodes <命令> [选项]
|
|
25
|
+
|
|
26
|
+
命令:
|
|
27
|
+
init 初始化当前目录
|
|
28
|
+
run 启动服务
|
|
29
|
+
chat 启动 Agent 对话模式
|
|
30
|
+
evolve 多轮演化主入口 (Week 5)
|
|
31
|
+
factor-info 显示 TrajectoryPool 统计 (Week 5)
|
|
32
|
+
factor-best 显示 Top-N 最佳 entry (Week 5)
|
|
33
|
+
factor-visual 生成可视化 HTML 报告 (Week 6)
|
|
34
|
+
factor-rag-show RAG 检索演示 (Week 7)
|
|
35
|
+
factor-rag-eval 批量评估 RAG 质量 (Week 10)
|
|
36
|
+
factor-data-fetch 从 iFinD 拉取数据 + 写 H5 (Week 12)
|
|
37
|
+
factor-dashboard 生成 3 类指标 dashboard (Week 13)
|
|
38
|
+
version 显示版本
|
|
39
|
+
help 显示帮助
|
|
40
|
+
|
|
41
|
+
evolve 选项:
|
|
42
|
+
--config PATH YAML 配置文件路径 (必填)
|
|
43
|
+
--directions LIST 逗号分隔的研究方向
|
|
44
|
+
--initial-json JSON 初始 candidates JSON
|
|
45
|
+
--max-rounds N 覆盖 config.evolution.max_rounds
|
|
46
|
+
--early-stop N 覆盖 config.evolution.early_stop_patience
|
|
47
|
+
|
|
48
|
+
factor-info / factor-best / factor-visual / factor-rag-show 选项:
|
|
49
|
+
--pool-dir PATH TrajectoryPool 目录
|
|
50
|
+
--top N Top-N (默认 5, 仅 factor-best/rag-show)
|
|
51
|
+
--metric NAME 排序指标 (默认 sharpe, 仅 factor-best/visual)
|
|
52
|
+
--output PATH HTML 输出路径 (仅 factor-visual)
|
|
53
|
+
--title TITLE 报告标题 (仅 factor-visual)
|
|
54
|
+
--query TEXT 查询文本 (仅 factor-rag-show)
|
|
55
|
+
--compress 启用谱系压缩 (Week 9, 仅 factor-rag-show)
|
|
56
|
+
--ancestor-depth N 祖先深度 (默认 2, 仅 --compress)
|
|
57
|
+
--descendant-depth N 后裔深度 (默认 2, 仅 --compress)
|
|
58
|
+
--max-tokens N 压缩最大字符数 (默认 200, 仅 --compress)
|
|
59
|
+
|
|
60
|
+
init 选项:
|
|
61
|
+
--force 强制重新初始化 (覆盖现有配置)
|
|
62
|
+
|
|
63
|
+
run 选项:
|
|
64
|
+
--host HOST 绑定主机 (默认: localhost)
|
|
65
|
+
--port PORT 前端端口 (默认: 5173),设置后后端端口自动设为 PORT+1000
|
|
66
|
+
--api-port PORT 后端端口 (默认: 19380),优先级高于 --port 联动
|
|
67
|
+
--daemon 后台运行 (仅 Linux)
|
|
68
|
+
--api-only 仅启动后端
|
|
69
|
+
--frontend-only 仅启动前端
|
|
70
|
+
|
|
71
|
+
示例:
|
|
72
|
+
quantnodes init
|
|
73
|
+
quantnodes run # 前端:5173, 后端:19380
|
|
74
|
+
quantnodes run --port 18380 # 前端:18380, 后端:19380 (联动)
|
|
75
|
+
quantnodes run --port 18380 --api-port 9000 # 前端:18380, 后端:9000 (指定后端)
|
|
76
|
+
quantnodes run --daemon
|
|
77
|
+
quantnodes run --api-only
|
|
78
|
+
quantnodes evolve --config configs/evolve.yaml \\
|
|
79
|
+
--directions momentum,reversal --max-rounds 3 --workers 4
|
|
80
|
+
quantnodes factor-info --pool-dir output/trajectory
|
|
81
|
+
quantnodes factor-best --pool-dir output/trajectory --top 10 --metric sharpe
|
|
82
|
+
quantnodes factor-visual --pool-dir output/trajectory --output report.html
|
|
83
|
+
quantnodes factor-rag-show --pool-dir output/trajectory \\
|
|
84
|
+
--query "momentum effect" --top 5
|
|
85
|
+
quantnodes factor-rag-eval --pool-dir output/trajectory \\
|
|
86
|
+
--queries "momentum,reversal" --top 5
|
|
87
|
+
quantnodes factor-data-fetch --output-dir /tmp/real_data/ \\
|
|
88
|
+
--date-beg 20260101 --factors momentum_20d,reversal_5d
|
|
89
|
+
quantnodes factor-dashboard --pool-dir output/trajectory --output dashboard.html
|
|
90
|
+
quantnodes version
|
|
91
|
+
|
|
92
|
+
详细文档: docs/QuickStart.md
|
|
93
|
+
""")
|
|
94
|
+
return 0
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class VersionCommand(Command):
|
|
98
|
+
"""``quantnodes version`` subcommand."""
|
|
99
|
+
|
|
100
|
+
name = "version"
|
|
101
|
+
description = "显示版本"
|
|
102
|
+
|
|
103
|
+
def add_arguments(self, subparsers) -> None:
|
|
104
|
+
subparsers.add_parser(self.name, help=self.description)
|
|
105
|
+
|
|
106
|
+
def run(self, args) -> int:
|
|
107
|
+
return cmd_version(args)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class HelpCommand(Command):
|
|
111
|
+
"""``quantnodes help`` subcommand."""
|
|
112
|
+
|
|
113
|
+
name = "help"
|
|
114
|
+
description = "显示帮助"
|
|
115
|
+
|
|
116
|
+
def add_arguments(self, subparsers) -> None:
|
|
117
|
+
subparsers.add_parser(self.name, help=self.description)
|
|
118
|
+
|
|
119
|
+
def run(self, args) -> int:
|
|
120
|
+
return cmd_help(args)
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
"""
|
|
3
|
+
Enhanced CLI - Rich Agent 交互终端
|
|
4
|
+
|
|
5
|
+
提供流式输出、Markdown 渲染、命令历史的 Agent 对话界面。
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
|
|
10
|
+
from rich.console import Console
|
|
11
|
+
from rich.markdown import Markdown
|
|
12
|
+
from rich.panel import Panel
|
|
13
|
+
from rich.text import Text
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
console = Console()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
async def _stream_chat(agent, message: str) -> None:
|
|
20
|
+
"""流式输出 Agent 响应"""
|
|
21
|
+
full_response = ""
|
|
22
|
+
try:
|
|
23
|
+
async for chunk in agent.chat(message):
|
|
24
|
+
if chunk:
|
|
25
|
+
full_response += chunk
|
|
26
|
+
console.print(chunk, end="", highlight=False)
|
|
27
|
+
console.print()
|
|
28
|
+
except Exception as e:
|
|
29
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
30
|
+
return
|
|
31
|
+
|
|
32
|
+
if full_response:
|
|
33
|
+
console.print()
|
|
34
|
+
try:
|
|
35
|
+
console.print(Markdown(full_response))
|
|
36
|
+
except Exception:
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _print_welcome() -> None:
|
|
41
|
+
"""打印欢迎信息"""
|
|
42
|
+
welcome = Text()
|
|
43
|
+
welcome.append("QuantNodes Agent", style="bold cyan")
|
|
44
|
+
welcome.append(" - 量化研究智能助手\n", style="dim")
|
|
45
|
+
welcome.append("输入问题开始对话,", style="dim")
|
|
46
|
+
welcome.append("exit", style="bold")
|
|
47
|
+
welcome.append(" 退出\n", style="dim")
|
|
48
|
+
console.print(Panel(welcome, border_style="cyan"))
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _print_help() -> None:
|
|
52
|
+
"""打印帮助信息"""
|
|
53
|
+
help_text = """
|
|
54
|
+
**可用命令:**
|
|
55
|
+
- `exit` / `quit` / `q` - 退出
|
|
56
|
+
- `help` - 显示此帮助
|
|
57
|
+
- `clear` - 清屏
|
|
58
|
+
- `history` - 查看对话历史
|
|
59
|
+
- 直接输入文本开始对话
|
|
60
|
+
"""
|
|
61
|
+
console.print(Markdown(help_text))
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def chat(workspace: str = ".", config: dict = None) -> None:
|
|
65
|
+
"""启动 Agent 对话模式
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
workspace: 工作目录
|
|
69
|
+
config: Agent 配置
|
|
70
|
+
"""
|
|
71
|
+
from QuantNodes.agent import Agent
|
|
72
|
+
|
|
73
|
+
console.print("[bold cyan]正在初始化 Agent...[/bold cyan]")
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
agent = Agent(workspace=workspace, config=config or {})
|
|
77
|
+
except Exception as e:
|
|
78
|
+
console.print(f"[red]Agent 初始化失败: {e}[/red]")
|
|
79
|
+
return
|
|
80
|
+
|
|
81
|
+
_print_welcome()
|
|
82
|
+
|
|
83
|
+
history: list[str] = []
|
|
84
|
+
|
|
85
|
+
while True:
|
|
86
|
+
try:
|
|
87
|
+
user_input = console.input("[bold green]> [/bold green]").strip()
|
|
88
|
+
except (EOFError, KeyboardInterrupt):
|
|
89
|
+
console.print("\n[dim]再见![/dim]")
|
|
90
|
+
break
|
|
91
|
+
|
|
92
|
+
if not user_input:
|
|
93
|
+
continue
|
|
94
|
+
|
|
95
|
+
cmd = user_input.lower()
|
|
96
|
+
if cmd in ("exit", "quit", "q"):
|
|
97
|
+
console.print("[dim]再见![/dim]")
|
|
98
|
+
break
|
|
99
|
+
elif cmd == "help":
|
|
100
|
+
_print_help()
|
|
101
|
+
continue
|
|
102
|
+
elif cmd == "clear":
|
|
103
|
+
console.clear()
|
|
104
|
+
continue
|
|
105
|
+
elif cmd == "history":
|
|
106
|
+
if history:
|
|
107
|
+
for i, msg in enumerate(history, 1):
|
|
108
|
+
console.print(f"[dim]{i}.[/dim] {msg[:80]}...")
|
|
109
|
+
else:
|
|
110
|
+
console.print("[dim]暂无历史记录[/dim]")
|
|
111
|
+
continue
|
|
112
|
+
|
|
113
|
+
history.append(user_input)
|
|
114
|
+
|
|
115
|
+
try:
|
|
116
|
+
loop = asyncio.new_event_loop()
|
|
117
|
+
loop.run_until_complete(_stream_chat(agent, user_input))
|
|
118
|
+
loop.close()
|
|
119
|
+
except KeyboardInterrupt:
|
|
120
|
+
console.print("\n[yellow]已中断[/yellow]")
|
|
121
|
+
except Exception as e:
|
|
122
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def chat_single(message: str, workspace: str = ".", config: dict = None) -> None:
|
|
126
|
+
"""单次问答模式
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
message: 用户消息
|
|
130
|
+
workspace: 工作目录
|
|
131
|
+
config: Agent 配置
|
|
132
|
+
"""
|
|
133
|
+
from QuantNodes.agent import Agent
|
|
134
|
+
|
|
135
|
+
try:
|
|
136
|
+
agent = Agent(workspace=workspace, config=config or {})
|
|
137
|
+
except Exception as e:
|
|
138
|
+
console.print(f"[red]Agent 初始化失败: {e}[/red]")
|
|
139
|
+
return
|
|
140
|
+
|
|
141
|
+
try:
|
|
142
|
+
loop = asyncio.new_event_loop()
|
|
143
|
+
loop.run_until_complete(_stream_chat(agent, message))
|
|
144
|
+
loop.close()
|
|
145
|
+
except Exception as e:
|
|
146
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
"""
|
|
3
|
+
Configuration Node Module
|
|
4
|
+
|
|
5
|
+
提供配置节点体系,用于从各种来源加载配置:
|
|
6
|
+
- IniConfigNode: INI 配置文件
|
|
7
|
+
- YamlConfigNode: YAML 配置文件
|
|
8
|
+
- JSONConfigNode: JSON 配置文件
|
|
9
|
+
- EnvConfigNode: 环境变量
|
|
10
|
+
|
|
11
|
+
Examples:
|
|
12
|
+
>>> # 从 INI 文件读取
|
|
13
|
+
>>> ini = IniConfigNode(file_path="config.ini", section="database")
|
|
14
|
+
>>> settings = ini.execute()
|
|
15
|
+
>>>
|
|
16
|
+
>>> # 从 YAML 文件读取
|
|
17
|
+
>>> yaml_config = YamlConfigNode(file_path="config.yaml", key="app")
|
|
18
|
+
>>> app_config = yaml_config.execute()
|
|
19
|
+
>>>
|
|
20
|
+
>>> # 从环境变量读取
|
|
21
|
+
>>> env = EnvConfigNode(prefix="MYAPP_", types={'PORT': int})
|
|
22
|
+
>>> env_config = env.execute()
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from QuantNodes.conf_node.base import ConfigNode
|
|
26
|
+
from QuantNodes.conf_node.ini_config import IniConfigNode
|
|
27
|
+
from QuantNodes.conf_node.yaml_config import YamlConfigNode
|
|
28
|
+
from QuantNodes.conf_node.env_config import EnvConfigNode
|
|
29
|
+
from QuantNodes.conf_node.json_config import JSONConfigNode
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
'ConfigNode',
|
|
33
|
+
'IniConfigNode',
|
|
34
|
+
'YamlConfigNode',
|
|
35
|
+
'EnvConfigNode',
|
|
36
|
+
'JSONConfigNode',
|
|
37
|
+
]
|