aria-code 0.52.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.
- aria_code/__init__.py +42 -0
- aria_code/_version.py +20 -0
- aria_code/adk_apps/__init__.py +1 -0
- aria_code/adk_apps/aria_code_review/__init__.py +1 -0
- aria_code/adk_apps/aria_code_review/agent.py +32 -0
- aria_code/adk_apps/aria_research/__init__.py +1 -0
- aria_code/adk_apps/aria_research/agent.py +31 -0
- aria_code/agents/__init__.py +34 -0
- aria_code/agents/base.py +318 -0
- aria_code/agents/code_review.py +200 -0
- aria_code/agents/deep/__init__.py +37 -0
- aria_code/agents/deep/calibration_loop.py +144 -0
- aria_code/agents/deep/critic.py +125 -0
- aria_code/agents/deep/deepen.py +193 -0
- aria_code/agents/deep/models.py +149 -0
- aria_code/agents/deep/pipeline.py +164 -0
- aria_code/agents/deep/quant_fusion.py +192 -0
- aria_code/agents/deep/themes.py +95 -0
- aria_code/agents/deep/tiers.py +106 -0
- aria_code/agents/engineering/__init__.py +14 -0
- aria_code/agents/engineering/coder.py +222 -0
- aria_code/agents/engineering/tester.py +145 -0
- aria_code/agents/financial/__init__.py +21 -0
- aria_code/agents/financial/cashflow_burn.py +119 -0
- aria_code/agents/financial/catalyst.py +279 -0
- aria_code/agents/financial/corporate_finance.py +175 -0
- aria_code/agents/financial/debate.py +224 -0
- aria_code/agents/financial/earnings.py +303 -0
- aria_code/agents/financial/fundamental.py +191 -0
- aria_code/agents/financial/macro.py +99 -0
- aria_code/agents/financial/news.py +207 -0
- aria_code/agents/financial/risk.py +165 -0
- aria_code/agents/financial/sector.py +279 -0
- aria_code/agents/financial/strategist.py +121 -0
- aria_code/agents/financial/stripe_revenue.py +127 -0
- aria_code/agents/financial/synthesis.py +274 -0
- aria_code/agents/financial/technical.py +258 -0
- aria_code/agents/orchestrator.py +44 -0
- aria_code/agents/portfolio_agent.py +363 -0
- aria_code/agents/realty/__init__.py +62 -0
- aria_code/agents/realty/asset_diagnosis.py +152 -0
- aria_code/agents/realty/business_match.py +166 -0
- aria_code/agents/realty/cashflow_verify.py +209 -0
- aria_code/agents/realty/contract_rules.py +210 -0
- aria_code/agents/realty/energy_anomaly.py +189 -0
- aria_code/agents/realty/exit_settlement.py +208 -0
- aria_code/agents/realty/fulfillment_risk.py +206 -0
- aria_code/agents/realty/ops_optimize.py +160 -0
- aria_code/agents/realty/revenue_share.py +215 -0
- aria_code/agents/registry.py +158 -0
- aria_code/agents/signal_scheme.py +111 -0
- aria_code/agents/sports/__init__.py +0 -0
- aria_code/agents/sports/football_agent.py +197 -0
- aria_code/agents/supervisor.py +113 -0
- aria_code/agents/team.py +492 -0
- aria_code/agents/warehouse/__init__.py +25 -0
- aria_code/agents/warehouse/contracts.py +26 -0
- aria_code/agents/warehouse/dashboard.py +155 -0
- aria_code/agents/warehouse/fulfillment_leadtime.py +95 -0
- aria_code/agents/warehouse/inbound_exceptions.py +25 -0
- aria_code/agents/warehouse/inventory_health.py +24 -0
- aria_code/agents/warehouse/local_run.py +71 -0
- aria_code/agents/warehouse/local_snapshot.py +132 -0
- aria_code/agents/warehouse/logistics_cost.py +84 -0
- aria_code/agents/warehouse/logistics_sync.py +21 -0
- aria_code/agents/warehouse/workflow.py +31 -0
- aria_code/aliyun_data_client.py +4 -0
- aria_code/apps/README.md +12 -0
- aria_code/apps/__init__.py +2 -0
- aria_code/apps/channels/README.md +15 -0
- aria_code/apps/channels/__init__.py +17 -0
- aria_code/apps/channels/intake.py +92 -0
- aria_code/apps/channels/registry.py +95 -0
- aria_code/apps/channels/tradingview.py +94 -0
- aria_code/apps/cli/README.md +13 -0
- aria_code/apps/cli/__init__.py +2 -0
- aria_code/apps/cli/_optional.py +120 -0
- aria_code/apps/cli/bootstrap.py +145 -0
- aria_code/apps/cli/broker_render.py +206 -0
- aria_code/apps/cli/codegen_paths.py +29 -0
- aria_code/apps/cli/commands/__init__.py +16 -0
- aria_code/apps/cli/commands/_business_render.py +78 -0
- aria_code/apps/cli/commands/_ui.py +71 -0
- aria_code/apps/cli/commands/analysis_cmds.py +369 -0
- aria_code/apps/cli/commands/ashare_prediction_cmds.py +327 -0
- aria_code/apps/cli/commands/auth_cmds.py +239 -0
- aria_code/apps/cli/commands/backtest_cmds.py +2543 -0
- aria_code/apps/cli/commands/broker_cmds.py +1234 -0
- aria_code/apps/cli/commands/business_workflow_cmds.py +319 -0
- aria_code/apps/cli/commands/canvas_cmds.py +77 -0
- aria_code/apps/cli/commands/catalog.py +87 -0
- aria_code/apps/cli/commands/core_cmds.py +2520 -0
- aria_code/apps/cli/commands/data_cmds.py +457 -0
- aria_code/apps/cli/commands/diagnostic_cmds.py +230 -0
- aria_code/apps/cli/commands/diagnostic_ops_cmds.py +813 -0
- aria_code/apps/cli/commands/file_cmds.py +312 -0
- aria_code/apps/cli/commands/finance_service_cmds.py +60 -0
- aria_code/apps/cli/commands/fx_commodity_cmds.py +123 -0
- aria_code/apps/cli/commands/market.py +434 -0
- aria_code/apps/cli/commands/market_cmds.py +1381 -0
- aria_code/apps/cli/commands/market_context.py +425 -0
- aria_code/apps/cli/commands/market_render.py +7 -0
- aria_code/apps/cli/commands/model_cmds.py +2153 -0
- aria_code/apps/cli/commands/ops_cmds.py +721 -0
- aria_code/apps/cli/commands/orchestrator_cmds.py +179 -0
- aria_code/apps/cli/commands/pdf_export_cmds.py +125 -0
- aria_code/apps/cli/commands/portfolio_cmds.py +1278 -0
- aria_code/apps/cli/commands/report.py +437 -0
- aria_code/apps/cli/commands/scaffold_templates.py +617 -0
- aria_code/apps/cli/commands/session_cmds.py +236 -0
- aria_code/apps/cli/commands/session_ux_cmds.py +359 -0
- aria_code/apps/cli/commands/team.py +669 -0
- aria_code/apps/cli/commands/team_render.py +8 -0
- aria_code/apps/cli/commands/ui_cmds.py +401 -0
- aria_code/apps/cli/commands/warehouse_cmds.py +79 -0
- aria_code/apps/cli/commands/workflow_cmds.py +480 -0
- aria_code/apps/cli/commands/workspace_cmds.py +1494 -0
- aria_code/apps/cli/config_paths.py +73 -0
- aria_code/apps/cli/config_store.py +78 -0
- aria_code/apps/cli/constants.py +88 -0
- aria_code/apps/cli/context.py +34 -0
- aria_code/apps/cli/deterministic.py +191 -0
- aria_code/apps/cli/direct.py +48 -0
- aria_code/apps/cli/finance_service_catalog.py +111 -0
- aria_code/apps/cli/football_reports.py +231 -0
- aria_code/apps/cli/github_app_auth.py +135 -0
- aria_code/apps/cli/google_login.py +196 -0
- aria_code/apps/cli/handlers/__init__.py +11 -0
- aria_code/apps/cli/handlers/broker_handlers.py +122 -0
- aria_code/apps/cli/handlers/chart_handlers.py +1309 -0
- aria_code/apps/cli/handlers/market_handlers.py +2513 -0
- aria_code/apps/cli/handlers/realty_handlers.py +114 -0
- aria_code/apps/cli/handlers/sports_handlers.py +22 -0
- aria_code/apps/cli/handlers/strategy_advice.py +82 -0
- aria_code/apps/cli/helpers.py +302 -0
- aria_code/apps/cli/hooks.py +180 -0
- aria_code/apps/cli/i18n.py +297 -0
- aria_code/apps/cli/intent.py +136 -0
- aria_code/apps/cli/intent_router.py +276 -0
- aria_code/apps/cli/intent_signals.py +344 -0
- aria_code/apps/cli/lifecycle_hooks.py +48 -0
- aria_code/apps/cli/local_tool_schemas.py +536 -0
- aria_code/apps/cli/main.py +29 -0
- aria_code/apps/cli/market_metadata.py +135 -0
- aria_code/apps/cli/market_universe.py +487 -0
- aria_code/apps/cli/message_processing.py +310 -0
- aria_code/apps/cli/model_catalog.py +620 -0
- aria_code/apps/cli/pdf_report.py +977 -0
- aria_code/apps/cli/plan_mode.py +139 -0
- aria_code/apps/cli/plotly_html.py +15 -0
- aria_code/apps/cli/prediction_feedback.py +202 -0
- aria_code/apps/cli/preflight.py +502 -0
- aria_code/apps/cli/project_aria.py +60 -0
- aria_code/apps/cli/prompt_assembly.py +59 -0
- aria_code/apps/cli/prompts/__init__.py +0 -0
- aria_code/apps/cli/prompts/coding.py +53 -0
- aria_code/apps/cli/prompts/select.py +142 -0
- aria_code/apps/cli/prompts/system_prompts.py +552 -0
- aria_code/apps/cli/prompts/ui.py +436 -0
- aria_code/apps/cli/provider_endpoints.py +237 -0
- aria_code/apps/cli/provider_keys.py +73 -0
- aria_code/apps/cli/providers/__init__.py +1 -0
- aria_code/apps/cli/providers/base.py +521 -0
- aria_code/apps/cli/providers/chat_routing.py +128 -0
- aria_code/apps/cli/providers/collaboration.py +98 -0
- aria_code/apps/cli/providers/llm/__init__.py +1 -0
- aria_code/apps/cli/providers/llm/offline_responses.py +68 -0
- aria_code/apps/cli/providers/llm/ollama_stream.py +1594 -0
- aria_code/apps/cli/providers/llm/response_cache.py +60 -0
- aria_code/apps/cli/providers/llm/sse_stream.py +257 -0
- aria_code/apps/cli/providers/runtime_bridge.py +353 -0
- aria_code/apps/cli/providers/vertexai_stream.py +316 -0
- aria_code/apps/cli/runtime_consumer.py +657 -0
- aria_code/apps/cli/sandbox.py +32 -0
- aria_code/apps/cli/session_export.py +120 -0
- aria_code/apps/cli/session_jsonl.py +208 -0
- aria_code/apps/cli/session_store.py +112 -0
- aria_code/apps/cli/skills_catalog.py +273 -0
- aria_code/apps/cli/todo_tracker.py +190 -0
- aria_code/apps/cli/tool_executor.py +823 -0
- aria_code/apps/cli/tool_registry.py +34 -0
- aria_code/apps/cli/tool_scope.py +106 -0
- aria_code/apps/cli/tools/__init__.py +40 -0
- aria_code/apps/cli/tools/context.py +46 -0
- aria_code/apps/cli/tools/file_tools.py +180 -0
- aria_code/apps/cli/tools/market_tools.py +620 -0
- aria_code/apps/cli/tools/notebook_tools.py +111 -0
- aria_code/apps/cli/tools/system_tools.py +726 -0
- aria_code/apps/cli/tools/write_tools.py +845 -0
- aria_code/apps/cli/tradingview_bridge.py +529 -0
- aria_code/apps/cli/turn_planning.py +137 -0
- aria_code/apps/cli/update_check.py +153 -0
- aria_code/apps/cli/utils/__init__.py +0 -0
- aria_code/apps/cli/utils/market_detect.py +1597 -0
- aria_code/apps/daemon/README.md +14 -0
- aria_code/apps/vscode/README.md +115 -0
- aria_code/apps/vscode/package.json +70 -0
- aria_code/aria_cli.py +6743 -0
- aria_code/aria_daemon.py +1398 -0
- aria_code/aria_feishu_bot.py +1423 -0
- aria_code/aria_mcp_server.py +7 -0
- aria_code/aria_relay_client.py +4 -0
- aria_code/aria_relay_server.py +490 -0
- aria_code/aria_telegram_bot.py +202 -0
- aria_code/ariarc.py +385 -0
- aria_code/artifacts.py +581 -0
- aria_code/backtest_engine.py +4 -0
- aria_code/backtest_report.py +4 -0
- aria_code/brokers/__init__.py +78 -0
- aria_code/brokers/automation.py +193 -0
- aria_code/brokers/base.py +231 -0
- aria_code/brokers/capabilities.py +264 -0
- aria_code/brokers/cn/__init__.py +10 -0
- aria_code/brokers/cn/easytrader_broker.py +193 -0
- aria_code/brokers/cn/futu_broker.py +194 -0
- aria_code/brokers/cn/longbridge_broker.py +190 -0
- aria_code/brokers/cn/tiger_broker.py +196 -0
- aria_code/brokers/cn/xtquant_broker.py +225 -0
- aria_code/brokers/config.py +414 -0
- aria_code/brokers/intl/__init__.py +5 -0
- aria_code/brokers/intl/alpaca_broker.py +183 -0
- aria_code/brokers/intl/ibkr_broker.py +215 -0
- aria_code/brokers/intl/trading212_broker.py +183 -0
- aria_code/brokers/intl/webull_broker.py +156 -0
- aria_code/brokers/paper_broker.py +266 -0
- aria_code/brokers/planning.py +296 -0
- aria_code/brokers/registry.py +213 -0
- aria_code/brokers/trading.py +267 -0
- aria_code/canva_client.py +4 -0
- aria_code/change_store.py +127 -0
- aria_code/clients/__init__.py +0 -0
- aria_code/clients/aliyun_data_client.py +660 -0
- aria_code/clients/aria_relay_client.py +182 -0
- aria_code/clients/canva_client.py +284 -0
- aria_code/clients/figma_client.py +100 -0
- aria_code/clients/football_data_client.py +1671 -0
- aria_code/clients/kling_video_client.py +182 -0
- aria_code/clients/market_data_client.py +1976 -0
- aria_code/clients/openai_image_client.py +242 -0
- aria_code/clients/runway_video_client.py +157 -0
- aria_code/clients/warehouse_erp_client.py +105 -0
- aria_code/cloud_memory.py +181 -0
- aria_code/command_safety.py +19 -0
- aria_code/computer_use_tools.py +4 -0
- aria_code/dashboard_generator.py +579 -0
- aria_code/data_analysis_tools.py +4 -0
- aria_code/data_cleaner.py +483 -0
- aria_code/data_service.py +513 -0
- aria_code/datasources/__init__.py +23 -0
- aria_code/datasources/base.py +166 -0
- aria_code/datasources/router.py +253 -0
- aria_code/datasources/sources/__init__.py +15 -0
- aria_code/datasources/sources/akshare_source.py +269 -0
- aria_code/datasources/sources/alpha_vantage_source.py +203 -0
- aria_code/datasources/sources/edgar_source.py +219 -0
- aria_code/datasources/sources/finnhub_source.py +198 -0
- aria_code/datasources/sources/fred_source.py +223 -0
- aria_code/datasources/sources/tushare_source.py +143 -0
- aria_code/datasources/sources/web_scraper_source.py +278 -0
- aria_code/datasources/sources/world_bank_source.py +206 -0
- aria_code/datasources/sources/yfinance_source.py +152 -0
- aria_code/demo_player.py +207 -0
- aria_code/doctor.py +813 -0
- aria_code/domain/__init__.py +0 -0
- aria_code/domain/backtest_engine.py +354 -0
- aria_code/domain/backtest_report.py +472 -0
- aria_code/domain/finance_formulas.py +389 -0
- aria_code/domain/portfolio_ledger.py +307 -0
- aria_code/domain/strategy_vault.py +421 -0
- aria_code/evals/__init__.py +37 -0
- aria_code/evals/harness.py +598 -0
- aria_code/evals/runner.py +238 -0
- aria_code/external_agent_runner.py +115 -0
- aria_code/figma_client.py +4 -0
- aria_code/file_analysis_tools.py +4 -0
- aria_code/finance_formulas.py +4 -0
- aria_code/football_data_client.py +4 -0
- aria_code/image_gen_tools.py +4 -0
- aria_code/intent_classifier.py +386 -0
- aria_code/kling_video_client.py +4 -0
- aria_code/licensing.py +151 -0
- aria_code/local_finance_tools.py +4 -0
- aria_code/local_image_provider.py +4 -0
- aria_code/local_llm_provider.py +4 -0
- aria_code/macro_tools.py +4 -0
- aria_code/markdown_pdf.py +289 -0
- aria_code/market_data_client.py +4 -0
- aria_code/mcp_client.py +639 -0
- aria_code/memory_manager.py +246 -0
- aria_code/model_capability.py +486 -0
- aria_code/notification_tools.py +4 -0
- aria_code/openai_image_client.py +4 -0
- aria_code/packages/__init__.py +23 -0
- aria_code/packages/adk_bridge/__init__.py +11 -0
- aria_code/packages/adk_bridge/code_review_tools.py +49 -0
- aria_code/packages/adk_bridge/market_tools.py +125 -0
- aria_code/packages/aria_agents/__init__.py +5 -0
- aria_code/packages/aria_agents/manifest.py +78 -0
- aria_code/packages/aria_core/__init__.py +34 -0
- aria_code/packages/aria_core/architecture.py +269 -0
- aria_code/packages/aria_core/export.py +147 -0
- aria_code/packages/aria_core/manifest.py +65 -0
- aria_code/packages/aria_core/paths.py +57 -0
- aria_code/packages/aria_core/secure_file.py +68 -0
- aria_code/packages/aria_infra/__init__.py +15 -0
- aria_code/packages/aria_infra/arthera.py +52 -0
- aria_code/packages/aria_infra/doctor.py +246 -0
- aria_code/packages/aria_infra/product.py +40 -0
- aria_code/packages/aria_mcp/__init__.py +25 -0
- aria_code/packages/aria_mcp/bridge.py +201 -0
- aria_code/packages/aria_mcp/circuit.py +82 -0
- aria_code/packages/aria_mcp/config.py +111 -0
- aria_code/packages/aria_mcp/installer.py +95 -0
- aria_code/packages/aria_mcp/server.py +1550 -0
- aria_code/packages/aria_mcp/tools.py +81 -0
- aria_code/packages/aria_sdk/__init__.py +19 -0
- aria_code/packages/aria_sdk/client.py +396 -0
- aria_code/packages/aria_sdk/providers.py +70 -0
- aria_code/packages/aria_sdk/streaming.py +79 -0
- aria_code/packages/aria_sdk/types.py +86 -0
- aria_code/packages/aria_services/__init__.py +92 -0
- aria_code/packages/aria_services/cache.py +60 -0
- aria_code/packages/aria_services/context.py +356 -0
- aria_code/packages/aria_services/context_checkpoints.py +157 -0
- aria_code/packages/aria_services/data.py +11 -0
- aria_code/packages/aria_services/provider_health.py +268 -0
- aria_code/packages/aria_services/references.py +247 -0
- aria_code/packages/aria_services/registry.py +249 -0
- aria_code/packages/aria_services/research_protocol.py +57 -0
- aria_code/packages/aria_services/research_quality.py +186 -0
- aria_code/packages/aria_services/settings.py +157 -0
- aria_code/packages/aria_services/usage.py +151 -0
- aria_code/packages/aria_skills/__init__.py +46 -0
- aria_code/packages/aria_skills/catalog.py +149 -0
- aria_code/packages/aria_skills/loader.py +455 -0
- aria_code/packages/aria_skills/registry.py +84 -0
- aria_code/packages/aria_tools/__init__.py +5 -0
- aria_code/packages/aria_tools/financial/__init__.py +8 -0
- aria_code/packages/aria_tools/financial/_paths.py +130 -0
- aria_code/packages/aria_tools/financial/compliance.py +31 -0
- aria_code/packages/aria_tools/financial/factors.py +33 -0
- aria_code/packages/aria_tools/financial/risk_tools.py +33 -0
- aria_code/packages/aria_tools/financial/strategy.py +31 -0
- aria_code/packages/aria_tools/registry.py +128 -0
- aria_code/packages/quant_engine/__init__.py +34 -0
- aria_code/packages/quant_engine/_namespace.py +62 -0
- aria_code/packages/quant_engine/backtest/__init__.py +14 -0
- aria_code/packages/quant_engine/backtest/core.py +53 -0
- aria_code/packages/quant_engine/backtest/engine.py +96 -0
- aria_code/packages/quant_engine/portfolio/__init__.py +13 -0
- aria_code/packages/quant_engine/portfolio/optimizer.py +85 -0
- aria_code/packages/quant_engine/sports/__init__.py +80 -0
- aria_code/packages/quant_engine/sports/calibrator.py +354 -0
- aria_code/packages/quant_engine/sports/dixon_coles.py +234 -0
- aria_code/packages/quant_engine/sports/elo.py +300 -0
- aria_code/packages/quant_engine/sports/form.py +188 -0
- aria_code/packages/quant_engine/sports/h2h.py +195 -0
- aria_code/packages/quant_engine/sports/ml_model.py +377 -0
- aria_code/packages/quant_engine/sports/predictor.py +312 -0
- aria_code/packages/quant_engine/sports/tracker.py +665 -0
- aria_code/packages/quant_engine/stochastic/__init__.py +35 -0
- aria_code/packages/quant_engine/stochastic/gbm_enhanced.py +195 -0
- aria_code/packages/quant_engine/stochastic/ito_calculus.py +477 -0
- aria_code/packages/quant_engine/stochastic/kelly_criterion.py +181 -0
- aria_code/packages/quant_engine/stochastic/monte_carlo_advanced.py +95 -0
- aria_code/packages/quant_engine/stochastic/options_pricing.py +576 -0
- aria_code/packages/quant_engine/stochastic/stochastic_processes.py +90 -0
- aria_code/packs/__init__.py +85 -0
- aria_code/packs/base.py +168 -0
- aria_code/packs/finance/__init__.py +250 -0
- aria_code/packs/logistics/__init__.py +169 -0
- aria_code/packs/payments/__init__.py +139 -0
- aria_code/packs/realty/__init__.py +190 -0
- aria_code/packs/registry.py +202 -0
- aria_code/packs/rules.py +90 -0
- aria_code/plan_utils.py +194 -0
- aria_code/plugin_loader.py +328 -0
- aria_code/portfolio_ledger.py +4 -0
- aria_code/preview_server.py +449 -0
- aria_code/privacy/__init__.py +5 -0
- aria_code/privacy/feedback.py +123 -0
- aria_code/project_review.py +278 -0
- aria_code/project_review_api.py +235 -0
- aria_code/project_review_server.py +18 -0
- aria_code/project_tools.py +4 -0
- aria_code/providers/__init__.py +30 -0
- aria_code/providers/llm/__init__.py +19 -0
- aria_code/providers/llm/anthropic.py +184 -0
- aria_code/providers/llm/autoconfig.py +213 -0
- aria_code/providers/llm/base.py +139 -0
- aria_code/providers/llm/custom.py +158 -0
- aria_code/providers/llm/ollama.py +129 -0
- aria_code/providers/llm/openai_compat.py +313 -0
- aria_code/providers/llm/registry.py +444 -0
- aria_code/providers/local_image_provider.py +202 -0
- aria_code/providers/local_llm_provider.py +589 -0
- aria_code/realty_data_tools.py +4 -0
- aria_code/report_exporters.py +150 -0
- aria_code/report_generator.py +1764 -0
- aria_code/runtime/__init__.py +169 -0
- aria_code/runtime/acceptance.py +481 -0
- aria_code/runtime/agent_loop.py +1520 -0
- aria_code/runtime/approval.py +57 -0
- aria_code/runtime/budget.py +232 -0
- aria_code/runtime/checkpoints.py +432 -0
- aria_code/runtime/crash_report.py +174 -0
- aria_code/runtime/events.py +113 -0
- aria_code/runtime/gateway.py +150 -0
- aria_code/runtime/lsp.py +484 -0
- aria_code/runtime/orchestrator.py +55 -0
- aria_code/runtime/repo_map.py +700 -0
- aria_code/runtime/run_state.py +127 -0
- aria_code/runtime/run_store.py +450 -0
- aria_code/runtime/self_healing.py +422 -0
- aria_code/runtime/subagent.py +547 -0
- aria_code/runtime/task_ledger.py +86 -0
- aria_code/runtime/task_orchestrator.py +90 -0
- aria_code/runtime/tool_executor.py +217 -0
- aria_code/runtime/tool_policy.py +107 -0
- aria_code/runtime/worktrees.py +215 -0
- aria_code/runway_video_client.py +4 -0
- aria_code/safety/__init__.py +23 -0
- aria_code/safety/permissions.py +306 -0
- aria_code/safety/service.py +102 -0
- aria_code/setup_wizard.py +673 -0
- aria_code/spreadsheet_tools.py +4 -0
- aria_code/strategy_vault.py +4 -0
- aria_code/tools/__init__.py +0 -0
- aria_code/tools/artifact_tools.py +252 -0
- aria_code/tools/broker_tools.py +92 -0
- aria_code/tools/build_quant_engine.py +97 -0
- aria_code/tools/code_audit_tools.py +192 -0
- aria_code/tools/computer_use_tools.py +504 -0
- aria_code/tools/data_analysis_tools.py +809 -0
- aria_code/tools/enterprise_finance_tools.py +156 -0
- aria_code/tools/extended_tools.py +240 -0
- aria_code/tools/file_analysis_tools.py +885 -0
- aria_code/tools/image_gen_tools.py +114 -0
- aria_code/tools/local_finance_tools.py +3417 -0
- aria_code/tools/logistics_tools.py +135 -0
- aria_code/tools/macro_tools.py +369 -0
- aria_code/tools/notification_tools.py +249 -0
- aria_code/tools/project_tools.py +525 -0
- aria_code/tools/realty_data_tools.py +659 -0
- aria_code/tools/spreadsheet_tools.py +388 -0
- aria_code/tools/stripe_tools.py +115 -0
- aria_code/ui/__init__.py +105 -0
- aria_code/ui/banner.py +508 -0
- aria_code/ui/completer.py +501 -0
- aria_code/ui/console.py +281 -0
- aria_code/ui/image_render.py +243 -0
- aria_code/ui/input_box.py +488 -0
- aria_code/ui/picker.py +201 -0
- aria_code/ui/render/__init__.py +11 -0
- aria_code/ui/render/finance.py +1705 -0
- aria_code/ui/render/market.py +225 -0
- aria_code/ui/render/output.py +775 -0
- aria_code/ui/render/responsive.py +75 -0
- aria_code/ui/render/team.py +354 -0
- aria_code/ui/robot.py +235 -0
- aria_code/ui/startup_dashboard.py +109 -0
- aria_code/utils/vscode_uri.py +37 -0
- aria_code/video_analysis.py +147 -0
- aria_code/video_editor.py +271 -0
- aria_code/workspace/__init__.py +6 -0
- aria_code/workspace/files.py +193 -0
- aria_code/workspace/verify.py +113 -0
- aria_code/workspace_changes.py +199 -0
- aria_code/workspace_context.py +233 -0
- aria_code/workspace_service.py +196 -0
- aria_code/workspace_tools.py +150 -0
- aria_code-0.52.0.dist-info/METADATA +237 -0
- aria_code-0.52.0.dist-info/RECORD +478 -0
- aria_code-0.52.0.dist-info/WHEEL +5 -0
- aria_code-0.52.0.dist-info/entry_points.txt +3 -0
- aria_code-0.52.0.dist-info/licenses/LICENSE +201 -0
- aria_code-0.52.0.dist-info/licenses/NOTICE +49 -0
- aria_code-0.52.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import asyncio
|
|
4
|
+
|
|
5
|
+
from ._paths import find_skill_script, missing_script_message
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
from arthera_sdk.mcp import mcp_tool
|
|
9
|
+
except ImportError:
|
|
10
|
+
def mcp_tool(*args, **kwargs):
|
|
11
|
+
def wrapper(f): return f
|
|
12
|
+
return wrapper
|
|
13
|
+
|
|
14
|
+
@mcp_tool(description="运行合规审计拦截网关。验证投资组合是否违反合规风控限制(如流动性、黑名单、集中度)。")
|
|
15
|
+
async def run_compliance_audit(strategy_name: str, assets: list[str]) -> str:
|
|
16
|
+
resolved = find_skill_script("compliance-audit-trail", "governance_manifest_gate.py")
|
|
17
|
+
if resolved is None:
|
|
18
|
+
return missing_script_message("compliance-audit-trail", "governance_manifest_gate.py")
|
|
19
|
+
script_path = str(resolved)
|
|
20
|
+
try:
|
|
21
|
+
proc = await asyncio.create_subprocess_exec(
|
|
22
|
+
"python3", script_path, "--demo", "--strategy", strategy_name, "--assets", ",".join(assets),
|
|
23
|
+
stdout=asyncio.subprocess.PIPE,
|
|
24
|
+
stderr=asyncio.subprocess.PIPE
|
|
25
|
+
)
|
|
26
|
+
stdout, stderr = await proc.communicate()
|
|
27
|
+
if proc.returncode == 0:
|
|
28
|
+
return stdout.decode()
|
|
29
|
+
return f"合规验证拦截:\n{stderr.decode()}"
|
|
30
|
+
except Exception as e:
|
|
31
|
+
return f"执行出错: {e}"
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import asyncio
|
|
4
|
+
|
|
5
|
+
from ._paths import ensure_arthera_sdk, find_skill_script, missing_script_message
|
|
6
|
+
|
|
7
|
+
ensure_arthera_sdk()
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
from arthera_sdk.mcp import mcp_tool
|
|
11
|
+
except ImportError:
|
|
12
|
+
def mcp_tool(*args, **kwargs):
|
|
13
|
+
def wrapper(f): return f
|
|
14
|
+
return wrapper
|
|
15
|
+
|
|
16
|
+
@mcp_tool(description="运行核心财务因子的量化验证和研究分析。调用 factor_evaluate.py 进行验证。")
|
|
17
|
+
async def run_factor_research(symbol: str, factors: list[str]) -> str:
|
|
18
|
+
resolved = find_skill_script("factor-research", "factor_evaluate.py")
|
|
19
|
+
if resolved is None:
|
|
20
|
+
return missing_script_message("factor-research", "factor_evaluate.py")
|
|
21
|
+
script_path = str(resolved)
|
|
22
|
+
try:
|
|
23
|
+
proc = await asyncio.create_subprocess_exec(
|
|
24
|
+
"python3", script_path, "--symbol", symbol, "--factors", ",".join(factors),
|
|
25
|
+
stdout=asyncio.subprocess.PIPE,
|
|
26
|
+
stderr=asyncio.subprocess.PIPE
|
|
27
|
+
)
|
|
28
|
+
stdout, stderr = await proc.communicate()
|
|
29
|
+
if proc.returncode == 0:
|
|
30
|
+
return stdout.decode()
|
|
31
|
+
return f"脚本执行失败:\n{stderr.decode()}"
|
|
32
|
+
except Exception as e:
|
|
33
|
+
return f"执行出错: {e}"
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import asyncio
|
|
4
|
+
|
|
5
|
+
from ._paths import ensure_arthera_sdk, find_skill_script, missing_script_message
|
|
6
|
+
|
|
7
|
+
ensure_arthera_sdk()
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
from arthera_sdk.mcp import mcp_tool
|
|
11
|
+
except ImportError:
|
|
12
|
+
def mcp_tool(*args, **kwargs):
|
|
13
|
+
def wrapper(f): return f
|
|
14
|
+
return wrapper
|
|
15
|
+
|
|
16
|
+
@mcp_tool(description="运行黑天鹅风险、相关性与下行压力测试。调用 risk_profile.py 进行验证。")
|
|
17
|
+
async def run_risk_profile(symbol: str) -> str:
|
|
18
|
+
resolved = find_skill_script("risk-assessment", "risk_profile.py")
|
|
19
|
+
if resolved is None:
|
|
20
|
+
return missing_script_message("risk-assessment", "risk_profile.py")
|
|
21
|
+
script_path = str(resolved)
|
|
22
|
+
try:
|
|
23
|
+
proc = await asyncio.create_subprocess_exec(
|
|
24
|
+
"python3", script_path, "--demo",
|
|
25
|
+
stdout=asyncio.subprocess.PIPE,
|
|
26
|
+
stderr=asyncio.subprocess.PIPE
|
|
27
|
+
)
|
|
28
|
+
stdout, stderr = await proc.communicate()
|
|
29
|
+
if proc.returncode == 0:
|
|
30
|
+
return stdout.decode()
|
|
31
|
+
return f"脚本执行失败:\n{stderr.decode()}"
|
|
32
|
+
except Exception as e:
|
|
33
|
+
return f"执行出错: {e}"
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import asyncio
|
|
4
|
+
|
|
5
|
+
from ._paths import find_skill_script, missing_script_message
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
from arthera_sdk.mcp import mcp_tool
|
|
9
|
+
except ImportError:
|
|
10
|
+
def mcp_tool(*args, **kwargs):
|
|
11
|
+
def wrapper(f): return f
|
|
12
|
+
return wrapper
|
|
13
|
+
|
|
14
|
+
@mcp_tool(description="自动校验量化策略代码的逻辑规范与极端边界条件,生成策略鲁棒性诊断报告。")
|
|
15
|
+
async def validate_strategy_spec(strategy_name: str) -> str:
|
|
16
|
+
resolved = find_skill_script("strategy-generation", "validate_strategy_spec.py")
|
|
17
|
+
if resolved is None:
|
|
18
|
+
return missing_script_message("strategy-generation", "validate_strategy_spec.py")
|
|
19
|
+
script_path = str(resolved)
|
|
20
|
+
try:
|
|
21
|
+
proc = await asyncio.create_subprocess_exec(
|
|
22
|
+
"python3", script_path, "--demo",
|
|
23
|
+
stdout=asyncio.subprocess.PIPE,
|
|
24
|
+
stderr=asyncio.subprocess.PIPE
|
|
25
|
+
)
|
|
26
|
+
stdout, stderr = await proc.communicate()
|
|
27
|
+
if proc.returncode == 0:
|
|
28
|
+
return stdout.decode()
|
|
29
|
+
return f"策略校验失败:\n{stderr.decode()}"
|
|
30
|
+
except Exception as e:
|
|
31
|
+
return f"执行出错: {e}"
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""Tool registry facade over Aria Code's legacy LOCAL_TOOLS shape."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from typing import Any, Callable, Dict, Iterable, List, Mapping, Optional
|
|
7
|
+
|
|
8
|
+
from aria_code.packages.aria_core import CapabilityManifest, PermissionLevel, ServiceKind
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
Handler = Callable[[dict], dict]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
_PERMISSION_BY_TOOL = {
|
|
15
|
+
"read_file": [PermissionLevel.READ_ONLY],
|
|
16
|
+
"list_files": [PermissionLevel.READ_ONLY],
|
|
17
|
+
"search_code": [PermissionLevel.READ_ONLY],
|
|
18
|
+
"glob": [PermissionLevel.READ_ONLY],
|
|
19
|
+
"write_file": [PermissionLevel.WORKSPACE_WRITE],
|
|
20
|
+
"edit_file": [PermissionLevel.WORKSPACE_WRITE],
|
|
21
|
+
"notebook_edit": [PermissionLevel.WORKSPACE_WRITE],
|
|
22
|
+
"run_command": [PermissionLevel.WORKSPACE_WRITE],
|
|
23
|
+
"web_fetch": [PermissionLevel.NETWORK],
|
|
24
|
+
"github": [PermissionLevel.NETWORK],
|
|
25
|
+
"get_market_data": [PermissionLevel.NETWORK],
|
|
26
|
+
"broker_query": [PermissionLevel.BROKER_READ],
|
|
27
|
+
"broker_order": [PermissionLevel.BROKER_TRADE],
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass(frozen=True)
|
|
32
|
+
class ToolSpec:
|
|
33
|
+
name: str
|
|
34
|
+
handler: Optional[Handler]
|
|
35
|
+
description: str
|
|
36
|
+
schema: Dict[str, Any] = field(default_factory=dict)
|
|
37
|
+
permissions: List[PermissionLevel] = field(default_factory=list)
|
|
38
|
+
capabilities: List[str] = field(default_factory=list)
|
|
39
|
+
|
|
40
|
+
def manifest(self) -> CapabilityManifest:
|
|
41
|
+
return CapabilityManifest(
|
|
42
|
+
name=self.name,
|
|
43
|
+
kind=ServiceKind.TOOL,
|
|
44
|
+
description=self.description,
|
|
45
|
+
capabilities=self.capabilities,
|
|
46
|
+
permissions=self.permissions,
|
|
47
|
+
input_schema=self.schema,
|
|
48
|
+
tags=["tool"],
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class ToolRegistry:
|
|
53
|
+
"""Small typed registry that can wrap existing dict-based tools."""
|
|
54
|
+
|
|
55
|
+
def __init__(self) -> None:
|
|
56
|
+
self._tools: Dict[str, ToolSpec] = {}
|
|
57
|
+
|
|
58
|
+
def register(self, spec: ToolSpec, *, overwrite: bool = False) -> None:
|
|
59
|
+
if spec.name in self._tools and not overwrite:
|
|
60
|
+
raise ValueError(f"tool already registered: {spec.name}")
|
|
61
|
+
self._tools[spec.name] = spec
|
|
62
|
+
|
|
63
|
+
def get(self, name: str) -> Optional[ToolSpec]:
|
|
64
|
+
return self._tools.get(name)
|
|
65
|
+
|
|
66
|
+
def list(self) -> List[ToolSpec]:
|
|
67
|
+
return [self._tools[name] for name in sorted(self._tools)]
|
|
68
|
+
|
|
69
|
+
def manifests(self) -> List[CapabilityManifest]:
|
|
70
|
+
return [tool.manifest() for tool in self.list()]
|
|
71
|
+
|
|
72
|
+
def legacy_tools(self) -> Dict[str, tuple]:
|
|
73
|
+
return {
|
|
74
|
+
name: (spec.handler, spec.description)
|
|
75
|
+
for name, spec in self._tools.items()
|
|
76
|
+
if spec.handler is not None
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _schema_by_name(schemas: Iterable[dict]) -> Dict[str, dict]:
|
|
81
|
+
out: Dict[str, dict] = {}
|
|
82
|
+
for schema in schemas or []:
|
|
83
|
+
fn = schema.get("function") or {}
|
|
84
|
+
name = fn.get("name")
|
|
85
|
+
if name:
|
|
86
|
+
out[name] = fn.get("parameters") or {}
|
|
87
|
+
return out
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _capabilities_for_tool(name: str) -> List[str]:
|
|
91
|
+
if name.startswith("broker_"):
|
|
92
|
+
return ["broker"]
|
|
93
|
+
if name in ("get_market_data",) or "market" in name or "quote" in name:
|
|
94
|
+
return ["market.data"]
|
|
95
|
+
if name in ("read_file", "list_files", "search_code", "glob"):
|
|
96
|
+
return ["workspace.read"]
|
|
97
|
+
if name in ("write_file", "edit_file", "notebook_edit"):
|
|
98
|
+
return ["workspace.write"]
|
|
99
|
+
if name == "run_command":
|
|
100
|
+
return ["shell"]
|
|
101
|
+
if name in ("web_fetch", "github"):
|
|
102
|
+
return ["network"]
|
|
103
|
+
return ["tool"]
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def build_registry_from_legacy(
|
|
107
|
+
tools: Mapping[str, tuple],
|
|
108
|
+
schemas: Iterable[dict] = (),
|
|
109
|
+
) -> ToolRegistry:
|
|
110
|
+
"""Create a typed registry from ``LOCAL_TOOLS`` and tool schemas."""
|
|
111
|
+
|
|
112
|
+
registry = ToolRegistry()
|
|
113
|
+
schema_map = _schema_by_name(schemas)
|
|
114
|
+
for name, value in tools.items():
|
|
115
|
+
handler = value[0] if value else None
|
|
116
|
+
description = value[1] if len(value) > 1 else ""
|
|
117
|
+
registry.register(
|
|
118
|
+
ToolSpec(
|
|
119
|
+
name=name,
|
|
120
|
+
handler=handler,
|
|
121
|
+
description=description,
|
|
122
|
+
schema=schema_map.get(name, {}),
|
|
123
|
+
permissions=_PERMISSION_BY_TOOL.get(name, [PermissionLevel.READ_ONLY]),
|
|
124
|
+
capabilities=_capabilities_for_tool(name),
|
|
125
|
+
),
|
|
126
|
+
overwrite=True,
|
|
127
|
+
)
|
|
128
|
+
return registry
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""quant_engine — vendored commodity quant modules (sports + stochastic).
|
|
2
|
+
|
|
3
|
+
Elo/Dixon-Coles football prediction and Black-Scholes/Monte-Carlo/Kelly
|
|
4
|
+
options+derivatives math. Proprietary factor/ML/strategy engines stay in the
|
|
5
|
+
private Arthera platform.
|
|
6
|
+
|
|
7
|
+
Open-core boundary: callers MUST go through ``is_available()`` and degrade
|
|
8
|
+
gracefully when it returns False. Today the source is bundled, so it is always
|
|
9
|
+
available — but routing every use through this boundary means the day the engine
|
|
10
|
+
is split into a separate compiled (.so) wheel, the free shell keeps working with
|
|
11
|
+
no caller changes (see tools/build_quant_engine.py, CLOSING_SOURCE.md).
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from importlib.util import find_spec
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
# ``packages`` is a namespace facade into the private Arthera platform, but
|
|
18
|
+
# this public shell also bundles a small ``quant_engine`` package. Python stops
|
|
19
|
+
# namespace lookup once it finds this regular subpackage, so extend *this*
|
|
20
|
+
# package path as well; otherwise ``packages.quant_engine.services`` (the
|
|
21
|
+
# audited A-share prediction engine) is silently unreachable from the CLI.
|
|
22
|
+
_private_quant_engine = Path(__file__).resolve().parents[3] / "Arthera" / "packages" / "quant_engine"
|
|
23
|
+
if _private_quant_engine.is_dir() and str(_private_quant_engine) not in __path__:
|
|
24
|
+
__path__.append(str(_private_quant_engine))
|
|
25
|
+
|
|
26
|
+
__version__ = "1.0.0"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def is_available() -> bool:
|
|
30
|
+
"""True if the quant engine (bundled source or compiled wheel) can be imported."""
|
|
31
|
+
try:
|
|
32
|
+
return find_spec("packages.quant_engine.stochastic") is not None
|
|
33
|
+
except Exception:
|
|
34
|
+
return False
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""让 quant_engine 的子包也能穿透到私有 Arthera 仓。
|
|
2
|
+
|
|
3
|
+
背景:packages/__init__.py 与 packages/quant_engine/__init__.py 都做了
|
|
4
|
+
``__path__.append(<Arthera 对应目录>)``,于是 ``packages.quant_engine.services``
|
|
5
|
+
这类"只有 Arthera 才有"的模块可以透明解析。但拼接**只做了这两层**。
|
|
6
|
+
|
|
7
|
+
Python 的包解析在找到一个常规子包(带 __init__.py 的目录)后就停止查找,
|
|
8
|
+
不会继续看父包 __path__ 里的其它条目。所以一旦 aria-code 自己有
|
|
9
|
+
``stochastic/`` 这个目录,``packages.quant_engine.stochastic.<任何模块>``
|
|
10
|
+
就只会在 aria-code 里找——哪怕 Arthera 有同名子包、有额外的模块。
|
|
11
|
+
|
|
12
|
+
后果是同一个包的不同子模块,解析规则不一致:
|
|
13
|
+
|
|
14
|
+
packages.quant_engine.services.* → 穿透到 Arthera ✅(aria-code 没这个目录)
|
|
15
|
+
packages.quant_engine.stochastic.* → 只看 aria-code ❌(aria-code 有这个目录)
|
|
16
|
+
|
|
17
|
+
实测确认过:删掉 aria-code 的 stochastic/ito_calculus.py 之后,
|
|
18
|
+
``import packages.quant_engine.stochastic.ito_calculus`` 直接
|
|
19
|
+
ModuleNotFoundError,而不是回落到 Arthera 那份同名文件;只有把整个
|
|
20
|
+
stochastic/ 目录删掉才会穿透。
|
|
21
|
+
|
|
22
|
+
这个不一致本身就是 bug 来源:Arthera 里 3202 行的 backtest/engine.py(内部有
|
|
23
|
+
18 个文件依赖它)在 aria-code 侧被 93 行的同名文件完全遮蔽,而
|
|
24
|
+
``sports/tracker.py`` 又是反过来的(aria-code 665 行、Arthera 454 行),
|
|
25
|
+
说明两边都被单独改过、已经分叉。改了哪份生效取决于从哪个目录启动进程,
|
|
26
|
+
这类 bug 极难发现。
|
|
27
|
+
|
|
28
|
+
extend() 把规则统一成一句话:**aria-code 自带的实现优先,Arthera 补齐缺口。**
|
|
29
|
+
append(而非 insert(0))保证公开仓的版本仍然赢——这维持了既有行为,本次改动
|
|
30
|
+
只是让"Arthera 独有的模块"在所有层级都能被找到,不改变任何已能解析的模块
|
|
31
|
+
指向谁。
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
from __future__ import annotations
|
|
35
|
+
|
|
36
|
+
from pathlib import Path
|
|
37
|
+
from typing import List
|
|
38
|
+
|
|
39
|
+
__all__ = ["extend"]
|
|
40
|
+
|
|
41
|
+
# packages/quant_engine/_namespace.py → 上溯 3 层到 aria-code/,再取同级 Arthera
|
|
42
|
+
_PRIVATE_ROOT = Path(__file__).resolve().parents[3] / "Arthera" / "packages" / "quant_engine"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def extend(package_path: List[str], subpackage: str) -> None:
|
|
46
|
+
"""把 Arthera 侧的同名子包目录追加进 ``__path__``。
|
|
47
|
+
|
|
48
|
+
子包 ``__init__.py`` 里调用::
|
|
49
|
+
|
|
50
|
+
from .._namespace import extend
|
|
51
|
+
extend(__path__, "stochastic")
|
|
52
|
+
|
|
53
|
+
Arthera checkout 不存在时静默跳过——公开仓的用户本来就没有它,
|
|
54
|
+
那不是错误状态。
|
|
55
|
+
"""
|
|
56
|
+
candidate = _PRIVATE_ROOT / subpackage
|
|
57
|
+
try:
|
|
58
|
+
if candidate.is_dir() and str(candidate) not in package_path:
|
|
59
|
+
package_path.append(str(candidate))
|
|
60
|
+
except OSError:
|
|
61
|
+
# 路径不可访问(权限、断开的符号链接)不该让 import 失败
|
|
62
|
+
pass
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# 让本子包也能穿透到私有 Arthera 仓的同名子包。
|
|
2
|
+
# 父层 (packages / quant_engine) 已经做了 __path__ 拼接,但 Python 找到
|
|
3
|
+
# 常规子包后就停止查找,不会继续看父包 __path__ 的其它条目——所以每一层
|
|
4
|
+
# 都得自己声明。规则:aria-code 自带的实现优先,Arthera 补齐缺口。
|
|
5
|
+
# 详见 packages/quant_engine/_namespace.py 的模块文档。
|
|
6
|
+
from .._namespace import extend as _extend_namespace
|
|
7
|
+
_extend_namespace(__path__, "backtest")
|
|
8
|
+
|
|
9
|
+
"""Backtest engine for evaluating AI trading signals."""
|
|
10
|
+
|
|
11
|
+
from .core import Order, Position, Portfolio
|
|
12
|
+
from .engine import BacktestEngine
|
|
13
|
+
|
|
14
|
+
__all__ = ["Order", "Position", "Portfolio", "BacktestEngine"]
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from typing import Dict, List, Optional
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
|
|
5
|
+
@dataclass
|
|
6
|
+
class Order:
|
|
7
|
+
symbol: str
|
|
8
|
+
quantity: float
|
|
9
|
+
price: float
|
|
10
|
+
order_type: str = "MARKET"
|
|
11
|
+
direction: str = "BUY" # BUY or SELL
|
|
12
|
+
status: str = "PENDING"
|
|
13
|
+
timestamp: Optional[datetime] = None
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class Position:
|
|
17
|
+
symbol: str
|
|
18
|
+
quantity: float = 0.0
|
|
19
|
+
avg_price: float = 0.0
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def market_value(self, current_price: float) -> float:
|
|
23
|
+
return self.quantity * current_price
|
|
24
|
+
|
|
25
|
+
def update(self, quantity: float, price: float, is_buy: bool):
|
|
26
|
+
if is_buy:
|
|
27
|
+
total_cost = (self.quantity * self.avg_price) + (quantity * price)
|
|
28
|
+
self.quantity += quantity
|
|
29
|
+
self.avg_price = total_cost / self.quantity if self.quantity > 0 else 0
|
|
30
|
+
else:
|
|
31
|
+
self.quantity -= quantity
|
|
32
|
+
if self.quantity <= 0:
|
|
33
|
+
self.avg_price = 0.0
|
|
34
|
+
self.quantity = 0.0
|
|
35
|
+
|
|
36
|
+
class Portfolio:
|
|
37
|
+
def __init__(self, initial_cash: float = 100000.0):
|
|
38
|
+
self.initial_cash = initial_cash
|
|
39
|
+
self.cash = initial_cash
|
|
40
|
+
self.positions: Dict[str, Position] = {}
|
|
41
|
+
self.history: List[Dict] = []
|
|
42
|
+
|
|
43
|
+
def get_position(self, symbol: str) -> Position:
|
|
44
|
+
if symbol not in self.positions:
|
|
45
|
+
self.positions[symbol] = Position(symbol=symbol)
|
|
46
|
+
return self.positions[symbol]
|
|
47
|
+
|
|
48
|
+
def total_value(self, current_prices: Dict[str, float]) -> float:
|
|
49
|
+
pos_value = sum(
|
|
50
|
+
pos.quantity * current_prices.get(sym, pos.avg_price)
|
|
51
|
+
for sym, pos in self.positions.items()
|
|
52
|
+
)
|
|
53
|
+
return self.cash + pos_value
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
from typing import List, Dict, Any
|
|
2
|
+
import pandas as pd
|
|
3
|
+
from .core import Portfolio, Order
|
|
4
|
+
|
|
5
|
+
class BacktestEngine:
|
|
6
|
+
"""Minimal Event-Driven Backtest Engine for Agent Signals"""
|
|
7
|
+
|
|
8
|
+
def __init__(self, initial_cash: float = 100000.0):
|
|
9
|
+
self.portfolio = Portfolio(initial_cash=initial_cash)
|
|
10
|
+
self.orders: List[Order] = []
|
|
11
|
+
|
|
12
|
+
def run_signals(self, price_data: pd.DataFrame, signals: List[Dict[str, Any]]):
|
|
13
|
+
"""
|
|
14
|
+
Run backtest based on daily prices and agent signals.
|
|
15
|
+
price_data expects index to be datetime, with 'close' column.
|
|
16
|
+
signals expects format: [{'date': 'YYYY-MM-DD', 'signal': 'BUY', 'confidence': 0.8, 'symbol': 'AAPL'}]
|
|
17
|
+
"""
|
|
18
|
+
# Convert signals to a dictionary indexed by date for O(1) lookup
|
|
19
|
+
signal_dict = {}
|
|
20
|
+
for s in signals:
|
|
21
|
+
try:
|
|
22
|
+
date_str = pd.to_datetime(s['date']).strftime('%Y-%m-%d')
|
|
23
|
+
signal_dict[date_str] = s
|
|
24
|
+
except (ValueError, TypeError, KeyError):
|
|
25
|
+
# 裸 except 会连 KeyboardInterrupt / SystemExit 一起吞掉——
|
|
26
|
+
# 循环里跑着时用户按 Ctrl+C 会被静默忽略。这里真正要容忍的
|
|
27
|
+
# 只是"某条信号的日期字段解析不了",收窄到具体异常。
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
equity_curve = []
|
|
31
|
+
|
|
32
|
+
for date, row in price_data.iterrows():
|
|
33
|
+
current_price = float(row['close'])
|
|
34
|
+
date_str = date.strftime('%Y-%m-%d')
|
|
35
|
+
symbol = "UNKNOWN" # Simplification for single-asset backtest for now
|
|
36
|
+
|
|
37
|
+
# Check for signals on this date
|
|
38
|
+
if date_str in signal_dict:
|
|
39
|
+
sig = signal_dict[date_str]
|
|
40
|
+
symbol = sig.get('symbol', 'UNKNOWN')
|
|
41
|
+
action = sig.get('signal', 'HOLD').upper()
|
|
42
|
+
confidence = float(sig.get('confidence', 0.5))
|
|
43
|
+
|
|
44
|
+
if action == 'BUY':
|
|
45
|
+
# Allocate portion of cash based on confidence
|
|
46
|
+
target_investment = self.portfolio.cash * confidence
|
|
47
|
+
qty = int(target_investment / current_price)
|
|
48
|
+
if qty > 0:
|
|
49
|
+
cost = qty * current_price
|
|
50
|
+
self.portfolio.cash -= cost
|
|
51
|
+
pos = self.portfolio.get_position(symbol)
|
|
52
|
+
pos.update(qty, current_price, True)
|
|
53
|
+
self.orders.append(Order(symbol, qty, current_price, direction="BUY", status="FILLED", timestamp=date))
|
|
54
|
+
|
|
55
|
+
elif action in ['SELL', 'REDUCE']:
|
|
56
|
+
pos = self.portfolio.get_position(symbol)
|
|
57
|
+
if pos.quantity > 0:
|
|
58
|
+
# Sell portion based on confidence
|
|
59
|
+
sell_qty = int(pos.quantity * confidence)
|
|
60
|
+
if sell_qty > 0:
|
|
61
|
+
proceeds = sell_qty * current_price
|
|
62
|
+
self.portfolio.cash += proceeds
|
|
63
|
+
pos.update(sell_qty, current_price, False)
|
|
64
|
+
self.orders.append(Order(symbol, sell_qty, current_price, direction="SELL", status="FILLED", timestamp=date))
|
|
65
|
+
|
|
66
|
+
# Record daily equity
|
|
67
|
+
total_val = self.portfolio.total_value({symbol: current_price})
|
|
68
|
+
equity_curve.append({
|
|
69
|
+
'date': date,
|
|
70
|
+
'total_value': total_val,
|
|
71
|
+
'cash': self.portfolio.cash
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
return pd.DataFrame(equity_curve).set_index('date')
|
|
75
|
+
|
|
76
|
+
def calculate_metrics(self, equity_df: pd.DataFrame) -> Dict[str, float]:
|
|
77
|
+
if equity_df.empty:
|
|
78
|
+
return {}
|
|
79
|
+
|
|
80
|
+
returns = equity_df['total_value'].pct_change().dropna()
|
|
81
|
+
total_return = (equity_df['total_value'].iloc[-1] / self.portfolio.initial_cash) - 1.0
|
|
82
|
+
|
|
83
|
+
ann_vol = returns.std() * (252 ** 0.5) if not returns.empty else 0.0
|
|
84
|
+
sharpe = (returns.mean() * 252) / ann_vol if ann_vol > 0 else 0.0
|
|
85
|
+
|
|
86
|
+
cum_ret = (1 + returns).cumprod()
|
|
87
|
+
peak = cum_ret.cummax()
|
|
88
|
+
drawdown = (cum_ret - peak) / peak
|
|
89
|
+
max_dd = drawdown.min()
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
"total_return_pct": round(total_return * 100, 2),
|
|
93
|
+
"annualized_volatility_pct": round(ann_vol * 100, 2),
|
|
94
|
+
"sharpe_ratio": round(sharpe, 2),
|
|
95
|
+
"max_drawdown_pct": round(max_dd * 100, 2) if not pd.isna(max_dd) else 0.0
|
|
96
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# 让本子包也能穿透到私有 Arthera 仓的同名子包。
|
|
2
|
+
# 父层 (packages / quant_engine) 已经做了 __path__ 拼接,但 Python 找到
|
|
3
|
+
# 常规子包后就停止查找,不会继续看父包 __path__ 的其它条目——所以每一层
|
|
4
|
+
# 都得自己声明。规则:aria-code 自带的实现优先,Arthera 补齐缺口。
|
|
5
|
+
# 详见 packages/quant_engine/_namespace.py 的模块文档。
|
|
6
|
+
from .._namespace import extend as _extend_namespace
|
|
7
|
+
_extend_namespace(__path__, "portfolio")
|
|
8
|
+
|
|
9
|
+
"""Portfolio optimization engine."""
|
|
10
|
+
|
|
11
|
+
from .optimizer import PortfolioOptimizer
|
|
12
|
+
|
|
13
|
+
__all__ = ["PortfolioOptimizer"]
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pandas as pd
|
|
3
|
+
from scipy.optimize import minimize
|
|
4
|
+
from typing import Dict, List, Tuple
|
|
5
|
+
|
|
6
|
+
class PortfolioOptimizer:
|
|
7
|
+
"""Quantitative Portfolio Optimization Engine"""
|
|
8
|
+
|
|
9
|
+
def __init__(self, risk_free_rate: float = 0.02):
|
|
10
|
+
self.risk_free_rate = risk_free_rate
|
|
11
|
+
|
|
12
|
+
def _get_returns_stats(self, prices_df: pd.DataFrame) -> Tuple[np.ndarray, np.ndarray]:
|
|
13
|
+
returns = prices_df.pct_change().dropna()
|
|
14
|
+
mean_returns = returns.mean().values * 252
|
|
15
|
+
cov_matrix = returns.cov().values * 252
|
|
16
|
+
return mean_returns, cov_matrix
|
|
17
|
+
|
|
18
|
+
def portfolio_performance(self, weights: np.ndarray, mean_returns: np.ndarray, cov_matrix: np.ndarray) -> Tuple[float, float]:
|
|
19
|
+
returns = np.sum(mean_returns * weights)
|
|
20
|
+
std = np.sqrt(np.dot(weights.T, np.dot(cov_matrix, weights)))
|
|
21
|
+
return returns, std
|
|
22
|
+
|
|
23
|
+
def min_variance(self, prices_df: pd.DataFrame) -> Dict[str, float]:
|
|
24
|
+
"""Minimize portfolio variance"""
|
|
25
|
+
symbols = prices_df.columns.tolist()
|
|
26
|
+
num_assets = len(symbols)
|
|
27
|
+
_, cov_matrix = self._get_returns_stats(prices_df)
|
|
28
|
+
|
|
29
|
+
args = (cov_matrix,)
|
|
30
|
+
def portfolio_volatility(weights, cov):
|
|
31
|
+
return np.sqrt(np.dot(weights.T, np.dot(cov, weights)))
|
|
32
|
+
|
|
33
|
+
constraints = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
|
|
34
|
+
bounds = tuple((0.0, 1.0) for asset in range(num_assets))
|
|
35
|
+
initial_guess = num_assets * [1. / num_assets,]
|
|
36
|
+
|
|
37
|
+
result = minimize(portfolio_volatility, initial_guess, args=args,
|
|
38
|
+
method='SLSQP', bounds=bounds, constraints=constraints)
|
|
39
|
+
|
|
40
|
+
return {sym: round(weight, 4) for sym, weight in zip(symbols, result.x)}
|
|
41
|
+
|
|
42
|
+
def max_sharpe(self, prices_df: pd.DataFrame) -> Dict[str, float]:
|
|
43
|
+
"""Maximize Sharpe Ratio"""
|
|
44
|
+
symbols = prices_df.columns.tolist()
|
|
45
|
+
num_assets = len(symbols)
|
|
46
|
+
mean_returns, cov_matrix = self._get_returns_stats(prices_df)
|
|
47
|
+
|
|
48
|
+
def neg_sharpe_ratio(weights, mean_ret, cov, rf_rate):
|
|
49
|
+
p_ret, p_std = self.portfolio_performance(weights, mean_ret, cov)
|
|
50
|
+
return -(p_ret - rf_rate) / p_std
|
|
51
|
+
|
|
52
|
+
args = (mean_returns, cov_matrix, self.risk_free_rate)
|
|
53
|
+
constraints = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
|
|
54
|
+
bounds = tuple((0.0, 1.0) for asset in range(num_assets))
|
|
55
|
+
initial_guess = num_assets * [1. / num_assets,]
|
|
56
|
+
|
|
57
|
+
result = minimize(neg_sharpe_ratio, initial_guess, args=args,
|
|
58
|
+
method='SLSQP', bounds=bounds, constraints=constraints)
|
|
59
|
+
|
|
60
|
+
return {sym: round(weight, 4) for sym, weight in zip(symbols, result.x)}
|
|
61
|
+
|
|
62
|
+
def risk_parity(self, prices_df: pd.DataFrame) -> Dict[str, float]:
|
|
63
|
+
"""Inverse volatility weighting (naive risk parity)"""
|
|
64
|
+
returns = prices_df.pct_change().dropna()
|
|
65
|
+
volatilities = returns.std() * np.sqrt(252)
|
|
66
|
+
inv_vol = 1.0 / volatilities
|
|
67
|
+
weights = inv_vol / inv_vol.sum()
|
|
68
|
+
|
|
69
|
+
return {sym: round(weights[sym], 4) for sym in prices_df.columns}
|
|
70
|
+
|
|
71
|
+
def combine_agent_signals(self, optimal_weights: Dict[str, float], agent_confidences: Dict[str, float]) -> Dict[str, float]:
|
|
72
|
+
"""
|
|
73
|
+
Adjust optimal weights based on Agent confidence.
|
|
74
|
+
For example, if max_sharpe gives AAPL 0.6, but Agent confidence is 0.5,
|
|
75
|
+
we scale it down and keep remaining in cash.
|
|
76
|
+
"""
|
|
77
|
+
final_weights = {}
|
|
78
|
+
for sym, w in optimal_weights.items():
|
|
79
|
+
conf = agent_confidences.get(sym, 1.0)
|
|
80
|
+
final_weights[sym] = round(w * conf, 4)
|
|
81
|
+
|
|
82
|
+
cash_weight = round(1.0 - sum(final_weights.values()), 4)
|
|
83
|
+
final_weights['CASH'] = max(0.0, cash_weight)
|
|
84
|
+
|
|
85
|
+
return final_weights
|