aria-code 0.167.0__py3-none-win_amd64.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_bots/__init__.py +7 -0
- aria_bots/channels/README.md +97 -0
- aria_bots/channels/__init__.py +1 -0
- aria_bots/channels/approvals.py +263 -0
- aria_bots/channels/conversation.py +280 -0
- aria_bots/channels/digest.py +186 -0
- aria_bots/channels/registry.py +95 -0
- aria_bots/engine.py +144 -0
- aria_bots/feishu_bot.py +1385 -0
- aria_bots/logistics/__init__.py +1 -0
- aria_bots/logistics/carriers.py +253 -0
- aria_bots/logistics/freight.py +154 -0
- aria_bots/logistics/inventory.py +242 -0
- aria_bots/logistics/mcp_server.py +156 -0
- aria_bots/logistics/records.py +119 -0
- aria_bots/logistics/tenancy.py +152 -0
- aria_bots/market.py +91 -0
- aria_bots/relay_client.py +326 -0
- aria_bots/telegram_bot.py +299 -0
- aria_code/__init__.py +111 -0
- aria_code/_bin/aria-native.exe +0 -0
- aria_code/_bin/aria-runtime.exe +0 -0
- aria_code/_version.py +20 -0
- aria_code/account_memory.py +296 -0
- aria_code/account_session.py +166 -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 +49 -0
- aria_code/agents/base.py +329 -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 +315 -0
- aria_code/agents/financial/fundamental.py +178 -0
- aria_code/agents/financial/macro.py +111 -0
- aria_code/agents/financial/news.py +231 -0
- aria_code/agents/financial/risk.py +146 -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 +277 -0
- aria_code/agents/orchestrator.py +44 -0
- aria_code/agents/portfolio_agent.py +392 -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 +168 -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 +500 -0
- aria_code/agents/video/__init__.py +16 -0
- aria_code/agents/video/director.py +376 -0
- aria_code/agents/video/qa_agent.py +143 -0
- aria_code/agents/video/script_doctor.py +112 -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/navigation.schema.json +110 -0
- aria_code/agents/warehouse/task_agent.py +172 -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 +9 -0
- aria_code/apps/channels/__init__.py +17 -0
- aria_code/apps/channels/approvals.py +11 -0
- aria_code/apps/channels/conversation.py +11 -0
- aria_code/apps/channels/digest.py +11 -0
- aria_code/apps/channels/intake.py +92 -0
- aria_code/apps/channels/registry.py +11 -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/app_server.py +656 -0
- aria_code/apps/cli/auto_update.py +125 -0
- aria_code/apps/cli/bootstrap.py +206 -0
- aria_code/apps/cli/broker_render.py +206 -0
- aria_code/apps/cli/chat_turn.py +1327 -0
- aria_code/apps/cli/cloud_models.py +231 -0
- aria_code/apps/cli/codegen_paths.py +29 -0
- aria_code/apps/cli/commands/__init__.py +34 -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 +378 -0
- aria_code/apps/cli/commands/ashare_prediction_cmds.py +327 -0
- aria_code/apps/cli/commands/auth_cmds.py +215 -0
- aria_code/apps/cli/commands/backtest_cmds.py +2518 -0
- aria_code/apps/cli/commands/broker_cmds.py +1236 -0
- aria_code/apps/cli/commands/business_workflow_cmds.py +379 -0
- aria_code/apps/cli/commands/canvas_cmds.py +77 -0
- aria_code/apps/cli/commands/catalog.py +189 -0
- aria_code/apps/cli/commands/core_cmds.py +2748 -0
- aria_code/apps/cli/commands/data_cmds.py +521 -0
- aria_code/apps/cli/commands/diagnostic_cmds.py +285 -0
- aria_code/apps/cli/commands/diagnostic_ops_cmds.py +813 -0
- aria_code/apps/cli/commands/engineering_cmds.py +399 -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 +544 -0
- aria_code/apps/cli/commands/market_cmds.py +1419 -0
- aria_code/apps/cli/commands/market_context.py +432 -0
- aria_code/apps/cli/commands/market_render.py +7 -0
- aria_code/apps/cli/commands/model_cmds.py +2374 -0
- aria_code/apps/cli/commands/ops_cmds.py +721 -0
- aria_code/apps/cli/commands/orchestrator_cmds.py +181 -0
- aria_code/apps/cli/commands/pdf_export_cmds.py +125 -0
- aria_code/apps/cli/commands/portfolio_cmds.py +1316 -0
- aria_code/apps/cli/commands/report.py +476 -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 +723 -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 +297 -0
- aria_code/apps/cli/commands/workflow_cmds.py +629 -0
- aria_code/apps/cli/commands/workspace_cmds.py +1543 -0
- aria_code/apps/cli/config_paths.py +73 -0
- aria_code/apps/cli/config_store.py +158 -0
- aria_code/apps/cli/constants.py +88 -0
- aria_code/apps/cli/context.py +34 -0
- aria_code/apps/cli/conversation_policy.py +53 -0
- aria_code/apps/cli/deterministic.py +200 -0
- aria_code/apps/cli/direct.py +48 -0
- aria_code/apps/cli/earnings_view.py +172 -0
- aria_code/apps/cli/endpoint_migration.py +64 -0
- aria_code/apps/cli/exec_events.py +255 -0
- aria_code/apps/cli/file_references.py +155 -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 +239 -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 +1314 -0
- aria_code/apps/cli/handlers/market_handlers.py +2570 -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/headless.py +352 -0
- aria_code/apps/cli/helpers.py +326 -0
- aria_code/apps/cli/hooks.py +180 -0
- aria_code/apps/cli/i18n.py +310 -0
- aria_code/apps/cli/intent.py +136 -0
- aria_code/apps/cli/intent_router.py +305 -0
- aria_code/apps/cli/intent_shadow.py +72 -0
- aria_code/apps/cli/intent_signals.py +347 -0
- aria_code/apps/cli/lifecycle_hooks.py +48 -0
- aria_code/apps/cli/local_tool_schemas.py +550 -0
- aria_code/apps/cli/main.py +39 -0
- aria_code/apps/cli/market_hours.py +100 -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/mixin_binding.py +32 -0
- aria_code/apps/cli/model_catalog.py +652 -0
- aria_code/apps/cli/model_probe.py +169 -0
- aria_code/apps/cli/model_retirement.py +65 -0
- aria_code/apps/cli/native_branding.py +12 -0
- aria_code/apps/cli/native_bridge.py +118 -0
- aria_code/apps/cli/native_entry.py +58 -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 +78 -0
- aria_code/apps/cli/prompts/__init__.py +0 -0
- aria_code/apps/cli/prompts/coding.py +71 -0
- aria_code/apps/cli/prompts/select.py +193 -0
- aria_code/apps/cli/prompts/system_prompts.py +562 -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 +775 -0
- aria_code/apps/cli/providers/chat_routing.py +139 -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 +1574 -0
- aria_code/apps/cli/providers/llm/response_cache.py +60 -0
- aria_code/apps/cli/providers/llm/sse_stream.py +298 -0
- aria_code/apps/cli/providers/runtime_bridge.py +586 -0
- aria_code/apps/cli/providers/vertexai_stream.py +592 -0
- aria_code/apps/cli/release_download.py +159 -0
- aria_code/apps/cli/review_runner.py +111 -0
- aria_code/apps/cli/runtime_consumer.py +769 -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_refresh.py +123 -0
- aria_code/apps/cli/session_store.py +125 -0
- aria_code/apps/cli/skills_catalog.py +273 -0
- aria_code/apps/cli/task_isolation.py +211 -0
- aria_code/apps/cli/terminal_chrome.py +412 -0
- aria_code/apps/cli/todo_tracker.py +190 -0
- aria_code/apps/cli/tool_executor.py +932 -0
- aria_code/apps/cli/tool_registry.py +34 -0
- aria_code/apps/cli/tool_scope.py +113 -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 +181 -0
- aria_code/apps/cli/tools/market_tools.py +626 -0
- aria_code/apps/cli/tools/notebook_tools.py +111 -0
- aria_code/apps/cli/tools/system_tools.py +656 -0
- aria_code/apps/cli/tools/write_tools.py +961 -0
- aria_code/apps/cli/tradingview_bridge.py +529 -0
- aria_code/apps/cli/transactions.py +269 -0
- aria_code/apps/cli/turn_planning.py +137 -0
- aria_code/apps/cli/update_check.py +257 -0
- aria_code/apps/cli/updater.py +373 -0
- aria_code/apps/cli/utils/__init__.py +0 -0
- aria_code/apps/cli/utils/market_detect.py +1663 -0
- aria_code/apps/cli/web_login.py +136 -0
- aria_code/apps/cli/workflow_runner.py +127 -0
- aria_code/apps/cli/workspace_route.py +121 -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 +5244 -0
- aria_code/aria_daemon.py +1372 -0
- aria_code/aria_feishu_bot.py +16 -0
- aria_code/aria_mcp_server.py +61 -0
- aria_code/aria_relay_client.py +4 -0
- aria_code/aria_telegram_bot.py +11 -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 +16 -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 +1985 -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 +190 -0
- aria_code/command_safety.py +19 -0
- aria_code/computer_use_server.py +276 -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/sec_statements.py +217 -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/doctor.py +860 -0
- aria_code/domain/__init__.py +0 -0
- aria_code/domain/_finance_core/__init__.py +5 -0
- aria_code/domain/_finance_core/backtest_engine.py +325 -0
- aria_code/domain/_finance_core/performance_stats.py +101 -0
- aria_code/domain/backtest_engine.py +75 -0
- aria_code/domain/backtest_report.py +488 -0
- aria_code/domain/finance_formulas.py +389 -0
- aria_code/domain/performance_stats.py +6 -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/behavior.py +113 -0
- aria_code/evals/gap.py +93 -0
- aria_code/evals/harness.py +949 -0
- aria_code/evals/review_probe.py +132 -0
- aria_code/evals/runner.py +425 -0
- aria_code/evals/scorecard.py +322 -0
- aria_code/evals/summary.py +125 -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 +459 -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 +744 -0
- aria_code/memory_manager.py +273 -0
- aria_code/model_capability.py +512 -0
- aria_code/notification_tools.py +4 -0
- aria_code/openai_image_client.py +4 -0
- aria_code/packages/__init__.py +26 -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 +58 -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 +232 -0
- aria_code/packages/aria_mcp/circuit.py +82 -0
- aria_code/packages/aria_mcp/config.py +162 -0
- aria_code/packages/aria_mcp/installer.py +95 -0
- aria_code/packages/aria_mcp/server.py +1705 -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 +393 -0
- aria_code/packages/aria_sdk/providers.py +70 -0
- aria_code/packages/aria_sdk/streaming.py +75 -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 +304 -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 +137 -0
- aria_code/packages/aria_services/research_quality.py +186 -0
- aria_code/packages/aria_services/settings.py +160 -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 +5 -0
- aria_code/packages/aria_tools/financial/_paths.py +92 -0
- aria_code/packages/aria_tools/registry.py +129 -0
- aria_code/packages/quant_engine/__init__.py +33 -0
- aria_code/packages/quant_engine/_namespace.py +24 -0
- aria_code/packages/quant_engine/backtest/__init__.py +10 -0
- aria_code/packages/quant_engine/backtest/core.py +53 -0
- aria_code/packages/quant_engine/backtest/engine.py +102 -0
- aria_code/packages/quant_engine/portfolio/__init__.py +9 -0
- aria_code/packages/quant_engine/portfolio/optimizer.py +85 -0
- aria_code/packages/quant_engine/sports/__init__.py +76 -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/predictor.py +312 -0
- aria_code/packages/quant_engine/sports/tracker.py +665 -0
- aria_code/packages/quant_engine/stochastic/__init__.py +31 -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/healthcare/__init__.py +40 -0
- aria_code/packs/logistics/__init__.py +171 -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/google_video_provider.py +256 -0
- aria_code/providers/llm/__init__.py +19 -0
- aria_code/providers/llm/anthropic.py +184 -0
- aria_code/providers/llm/arthera_gateway.py +90 -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 +137 -0
- aria_code/providers/llm/openai_compat.py +350 -0
- aria_code/providers/llm/registry.py +457 -0
- aria_code/providers/local_image_provider.py +202 -0
- aria_code/providers/local_llm_provider.py +549 -0
- aria_code/providers/ollama_models.py +79 -0
- aria_code/providers/openai_params.py +32 -0
- aria_code/providers/tool_messages.py +100 -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/review_service.py +497 -0
- aria_code/runtime/__init__.py +176 -0
- aria_code/runtime/acceptance.py +632 -0
- aria_code/runtime/agent_loop.py +1851 -0
- aria_code/runtime/agent_profiles.py +75 -0
- aria_code/runtime/approval.py +71 -0
- aria_code/runtime/baseline.py +219 -0
- aria_code/runtime/budget.py +232 -0
- aria_code/runtime/checkpoints.py +481 -0
- aria_code/runtime/consult.py +65 -0
- aria_code/runtime/contract.py +282 -0
- aria_code/runtime/delivery.py +432 -0
- aria_code/runtime/events.py +113 -0
- aria_code/runtime/gateway.py +167 -0
- aria_code/runtime/lsp.py +484 -0
- aria_code/runtime/native_index.py +153 -0
- aria_code/runtime/orchestrator.py +55 -0
- aria_code/runtime/processes.py +343 -0
- aria_code/runtime/project_graph.py +614 -0
- aria_code/runtime/repo_map.py +712 -0
- aria_code/runtime/research_bundle.py +105 -0
- aria_code/runtime/review.py +269 -0
- aria_code/runtime/run_state.py +127 -0
- aria_code/runtime/run_store.py +522 -0
- aria_code/runtime/scoped_runs.py +113 -0
- aria_code/runtime/self_healing.py +422 -0
- aria_code/runtime/semantic_diff.py +171 -0
- aria_code/runtime/subagent.py +721 -0
- aria_code/runtime/symbol_edit.py +117 -0
- aria_code/runtime/task_brief.py +46 -0
- aria_code/runtime/task_context.py +145 -0
- aria_code/runtime/task_ledger.py +108 -0
- aria_code/runtime/task_orchestrator.py +90 -0
- aria_code/runtime/task_outcome.py +33 -0
- aria_code/runtime/task_verification.py +266 -0
- aria_code/runtime/task_worktree.py +448 -0
- aria_code/runtime/tool_executor.py +329 -0
- aria_code/runtime/tool_policy.py +107 -0
- aria_code/runtime/transactions.py +216 -0
- aria_code/runtime/windows_job.py +115 -0
- aria_code/runtime/workflows.py +222 -0
- aria_code/runtime/worktrees.py +215 -0
- aria_code/runway_video_client.py +4 -0
- aria_code/safety/__init__.py +27 -0
- aria_code/safety/client_isolation.py +178 -0
- aria_code/safety/permissions.py +339 -0
- aria_code/safety/risk.py +699 -0
- aria_code/safety/sandbox.py +148 -0
- aria_code/safety/service.py +102 -0
- aria_code/service_endpoints.py +14 -0
- aria_code/setup_wizard.py +684 -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 +509 -0
- aria_code/tools/data_analysis_tools.py +831 -0
- aria_code/tools/enterprise_finance_tools.py +202 -0
- aria_code/tools/extended_tools.py +240 -0
- aria_code/tools/file_analysis_tools.py +880 -0
- aria_code/tools/image_gen_tools.py +114 -0
- aria_code/tools/local_finance_tools.py +3488 -0
- aria_code/tools/logistics_carriers.py +11 -0
- aria_code/tools/logistics_inventory.py +11 -0
- aria_code/tools/logistics_io.py +11 -0
- aria_code/tools/logistics_tenancy.py +11 -0
- aria_code/tools/logistics_tools.py +11 -0
- aria_code/tools/macro_tools.py +412 -0
- aria_code/tools/notification_tools.py +249 -0
- aria_code/tools/project_tools.py +525 -0
- aria_code/tools/realty_data_tools.py +662 -0
- aria_code/tools/spreadsheet_tools.py +399 -0
- aria_code/tools/stripe_tools.py +115 -0
- aria_code/tools/video_tools.py +500 -0
- aria_code/training/__init__.py +1 -0
- aria_code/training/compare.py +117 -0
- aria_code/training/prelude_commands.py +111 -0
- aria_code/training/prelude_eval.py +390 -0
- aria_code/training/prelude_real.py +183 -0
- aria_code/training/prelude_train.py +248 -0
- aria_code/training/sft_dataset.py +230 -0
- aria_code/training/trajectory_dataset.py +263 -0
- aria_code/ui/__init__.py +105 -0
- aria_code/ui/banner.py +404 -0
- aria_code/ui/completer.py +571 -0
- aria_code/ui/console.py +285 -0
- aria_code/ui/input_box.py +505 -0
- aria_code/ui/mentions.py +206 -0
- aria_code/ui/picker.py +248 -0
- aria_code/ui/render/__init__.py +11 -0
- aria_code/ui/render/actions.py +297 -0
- aria_code/ui/render/finance.py +1732 -0
- aria_code/ui/render/market.py +253 -0
- aria_code/ui/render/output.py +954 -0
- aria_code/ui/render/responsive.py +75 -0
- aria_code/ui/render/team.py +367 -0
- aria_code/ui/robot.py +239 -0
- aria_code/ui/startup_dashboard.py +110 -0
- aria_code/video_analysis.py +147 -0
- aria_code/video_editor.py +518 -0
- aria_code/video_workflow/__init__.py +8 -0
- aria_code/video_workflow/__main__.py +61 -0
- aria_code/video_workflow/engines/__init__.py +18 -0
- aria_code/video_workflow/engines/base.py +69 -0
- aria_code/video_workflow/engines/custom_model.py +204 -0
- aria_code/video_workflow/engines/google_veo.py +94 -0
- aria_code/video_workflow/engines/local_engine.py +71 -0
- aria_code/video_workflow/engines/registry.py +98 -0
- aria_code/video_workflow/google_models.py +278 -0
- aria_code/video_workflow/music/__init__.py +5 -0
- aria_code/video_workflow/music/foley_service.py +132 -0
- aria_code/video_workflow/music/service.py +212 -0
- aria_code/video_workflow/pipeline.py +620 -0
- aria_code/video_workflow/plan.py +202 -0
- aria_code/video_workflow/session.py +297 -0
- aria_code/workspace/__init__.py +6 -0
- aria_code/workspace/files.py +219 -0
- aria_code/workspace/verify.py +167 -0
- aria_code/workspace_changes.py +199 -0
- aria_code/workspace_service.py +196 -0
- aria_code/workspace_tools.py +150 -0
- aria_code-0.167.0.dist-info/METADATA +330 -0
- aria_code-0.167.0.dist-info/RECORD +601 -0
- aria_code-0.167.0.dist-info/WHEEL +5 -0
- aria_code-0.167.0.dist-info/entry_points.txt +5 -0
- aria_code-0.167.0.dist-info/licenses/LICENSE +201 -0
- aria_code-0.167.0.dist-info/licenses/NOTICE +49 -0
- aria_code-0.167.0.dist-info/top_level.txt +2 -0
|
File without changes
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
"""backtest_engine.py — event-driven, multi-asset backtest engine.
|
|
2
|
+
|
|
3
|
+
A small, dependency-light engine that fixes the gaps of the template backtests:
|
|
4
|
+
|
|
5
|
+
* Event-driven: iterate bars chronologically; the strategy decides on each
|
|
6
|
+
bar's CLOSE and orders fill at the NEXT bar's OPEN (no look-ahead bias).
|
|
7
|
+
* Multi-asset portfolio: cash + per-symbol positions, target-weight rebalance.
|
|
8
|
+
* Realistic costs: commission + slippage on every fill.
|
|
9
|
+
* Backtest <-> live parity: a strategy implements ``Strategy.on_bar(ctx)`` and
|
|
10
|
+
returns ``StrategyOrder``s. The SAME strategy object can drive live trading —
|
|
11
|
+
``strategy_order_to_intent`` converts an order into a broker ``OrderIntent``
|
|
12
|
+
for the preview -> risk -> (auto)execute pipeline.
|
|
13
|
+
|
|
14
|
+
The engine is pure (operates on provided ``Bar`` data) so it is fully unit
|
|
15
|
+
testable offline; ``load_bars`` bridges to DataService for real runs.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
from abc import ABC, abstractmethod
|
|
21
|
+
from dataclasses import dataclass, field
|
|
22
|
+
from typing import Any, Callable, Dict, List, Optional
|
|
23
|
+
|
|
24
|
+
from . import performance_stats as stats
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class Bar:
|
|
29
|
+
date: str
|
|
30
|
+
open: float
|
|
31
|
+
high: float
|
|
32
|
+
low: float
|
|
33
|
+
close: float
|
|
34
|
+
volume: float = 0.0
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass
|
|
38
|
+
class StrategyOrder:
|
|
39
|
+
symbol: str
|
|
40
|
+
action: str # "buy" | "sell" | "target"
|
|
41
|
+
quantity: float = 0.0 # shares, for buy/sell
|
|
42
|
+
target_weight: Optional[float] = None # fraction of equity, for "target"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class Portfolio:
|
|
47
|
+
cash: float
|
|
48
|
+
positions: Dict[str, float] = field(default_factory=dict)
|
|
49
|
+
|
|
50
|
+
def market_value(self, prices: Dict[str, float]) -> float:
|
|
51
|
+
return sum(qty * prices.get(s, 0.0) for s, qty in self.positions.items())
|
|
52
|
+
|
|
53
|
+
def equity(self, prices: Dict[str, float]) -> float:
|
|
54
|
+
return self.cash + self.market_value(prices)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class Context:
|
|
58
|
+
"""Read-only view handed to ``Strategy.on_bar`` each step."""
|
|
59
|
+
|
|
60
|
+
def __init__(self, date: str, bars: Dict[str, Bar],
|
|
61
|
+
history: Dict[str, List[Bar]], portfolio: Portfolio):
|
|
62
|
+
self.date = date
|
|
63
|
+
self.bars = bars
|
|
64
|
+
self.history = history
|
|
65
|
+
self.portfolio = portfolio
|
|
66
|
+
|
|
67
|
+
def price(self, symbol: str) -> float:
|
|
68
|
+
b = self.bars.get(symbol)
|
|
69
|
+
return b.close if b else 0.0
|
|
70
|
+
|
|
71
|
+
def position(self, symbol: str) -> float:
|
|
72
|
+
return self.portfolio.positions.get(symbol, 0.0)
|
|
73
|
+
|
|
74
|
+
def closes(self, symbol: str, n: int | None = None) -> List[float]:
|
|
75
|
+
hist = [b.close for b in self.history.get(symbol, [])]
|
|
76
|
+
return hist[-n:] if n else hist
|
|
77
|
+
|
|
78
|
+
def equity(self) -> float:
|
|
79
|
+
return self.portfolio.equity({s: self.price(s) for s in self.bars})
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class Strategy(ABC):
|
|
83
|
+
"""Backtest/live strategy. ``on_bar`` is called on each bar's close."""
|
|
84
|
+
|
|
85
|
+
name: str = "strategy"
|
|
86
|
+
|
|
87
|
+
def on_bar(self, ctx: Context) -> List[StrategyOrder]: # pragma: no cover - abstract
|
|
88
|
+
raise NotImplementedError
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@dataclass
|
|
92
|
+
class BacktestResult:
|
|
93
|
+
strategy: str
|
|
94
|
+
symbols: List[str]
|
|
95
|
+
starting_cash: float
|
|
96
|
+
equity_curve: List[Dict[str, Any]] # [{date, equity}]
|
|
97
|
+
trades: List[Dict[str, Any]]
|
|
98
|
+
metrics: Dict[str, Any]
|
|
99
|
+
benchmark: Dict[str, Any]
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _metrics(equity_curve: List[Dict[str, Any]], trades: List[Dict[str, Any]],
|
|
103
|
+
periods: Optional[int] = None) -> Dict[str, Any]:
|
|
104
|
+
"""Headline statistics for a run.
|
|
105
|
+
|
|
106
|
+
`periods` is the caller's own periods-per-year when they gave one, and then
|
|
107
|
+
behaves exactly as it always did. Left out, the span comes from the equity
|
|
108
|
+
curve's dates (see performance_stats), so a year of weekly bars and a year
|
|
109
|
+
of daily bars both annualize to a year. A curve whose dates cannot be read
|
|
110
|
+
falls back to 252, the old behaviour.
|
|
111
|
+
"""
|
|
112
|
+
eq = [pt["equity"] for pt in equity_curve]
|
|
113
|
+
if len(eq) < 2 or eq[0] <= 0:
|
|
114
|
+
return {"total_return": 0.0, "annual_return": 0.0, "sharpe": 0.0,
|
|
115
|
+
"max_drawdown": 0.0, "n_trades": len(trades), "win_rate": 0.0}
|
|
116
|
+
rets = [eq[i] / eq[i - 1] - 1 for i in range(1, len(eq)) if eq[i - 1] > 0]
|
|
117
|
+
total = eq[-1] / eq[0] - 1
|
|
118
|
+
n = len(eq)
|
|
119
|
+
years = stats.span_years(equity_curve[0].get("date"), equity_curve[-1].get("date"))
|
|
120
|
+
if periods is not None:
|
|
121
|
+
sharpe_periods = periods
|
|
122
|
+
annual = -1.0 if total <= -1 else (1 + total) ** (periods / n) - 1
|
|
123
|
+
elif years is not None:
|
|
124
|
+
sharpe_periods = len(rets) / years
|
|
125
|
+
annual = stats.annualized_return(total, years)
|
|
126
|
+
else:
|
|
127
|
+
sharpe_periods = stats.FALLBACK_PERIODS_PER_YEAR
|
|
128
|
+
annual = -1.0 if total <= -1 else (1 + total) ** (sharpe_periods / n) - 1
|
|
129
|
+
sells = [t for t in trades if t["side"] == "sell"]
|
|
130
|
+
wins = [t for t in sells if t.get("realized_pnl", 0) > 0]
|
|
131
|
+
return {
|
|
132
|
+
"total_return": round(total, 6),
|
|
133
|
+
"annual_return": round(annual, 6),
|
|
134
|
+
"sharpe": round(stats.sharpe(rets, sharpe_periods), 4),
|
|
135
|
+
"max_drawdown": round(stats.max_drawdown(eq), 6),
|
|
136
|
+
"n_trades": len(trades),
|
|
137
|
+
"win_rate": round(len(wins) / len(sells), 4) if sells else 0.0,
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class BacktestEngine:
|
|
142
|
+
def __init__(self, *, starting_cash: float = 100_000.0,
|
|
143
|
+
commission: float = 0.0005, slippage: float = 0.0005,
|
|
144
|
+
periods_per_year: Optional[int] = None):
|
|
145
|
+
self.starting_cash = float(starting_cash)
|
|
146
|
+
self.commission = float(commission)
|
|
147
|
+
self.slippage = float(slippage)
|
|
148
|
+
# None: read it from the data's dates. A number: the caller knows their data.
|
|
149
|
+
self.periods_per_year = None if periods_per_year is None else int(periods_per_year)
|
|
150
|
+
|
|
151
|
+
def run(self, data: Dict[str, List[Bar]], strategy: Strategy) -> BacktestResult:
|
|
152
|
+
symbols = list(data.keys())
|
|
153
|
+
by_date: Dict[str, Dict[str, Bar]] = {
|
|
154
|
+
s: {b.date: b for b in data[s]} for s in symbols
|
|
155
|
+
}
|
|
156
|
+
all_dates = sorted({b.date for bars in data.values() for b in bars})
|
|
157
|
+
|
|
158
|
+
cash = self.starting_cash
|
|
159
|
+
positions: Dict[str, float] = {s: 0.0 for s in symbols}
|
|
160
|
+
cost_basis: Dict[str, float] = {s: 0.0 for s in symbols} # avg cost
|
|
161
|
+
history: Dict[str, List[Bar]] = {s: [] for s in symbols}
|
|
162
|
+
last_close: Dict[str, float] = {s: 0.0 for s in symbols}
|
|
163
|
+
pending: List[StrategyOrder] = [] # buy/sell orders queued for next open
|
|
164
|
+
equity_curve: List[Dict[str, Any]] = []
|
|
165
|
+
trades: List[Dict[str, Any]] = []
|
|
166
|
+
|
|
167
|
+
for date in all_dates:
|
|
168
|
+
cur: Dict[str, Bar] = {}
|
|
169
|
+
for s in symbols:
|
|
170
|
+
b = by_date[s].get(date)
|
|
171
|
+
if b is not None:
|
|
172
|
+
cur[s] = b
|
|
173
|
+
history[s].append(b)
|
|
174
|
+
last_close[s] = b.close
|
|
175
|
+
|
|
176
|
+
# 1) Fill yesterday's orders at today's open (cost + slippage).
|
|
177
|
+
for order in pending:
|
|
178
|
+
b = cur.get(order.symbol)
|
|
179
|
+
if b is None or order.quantity <= 0:
|
|
180
|
+
continue
|
|
181
|
+
side = order.action
|
|
182
|
+
fill = b.open * (1 + self.slippage) if side == "buy" else b.open * (1 - self.slippage)
|
|
183
|
+
qty = order.quantity
|
|
184
|
+
if side == "buy":
|
|
185
|
+
gross = fill * qty
|
|
186
|
+
fee = gross * self.commission
|
|
187
|
+
if gross + fee > cash: # cap to affordable size
|
|
188
|
+
qty = max(0.0, (cash) / (fill * (1 + self.commission)))
|
|
189
|
+
gross = fill * qty
|
|
190
|
+
fee = gross * self.commission
|
|
191
|
+
if qty <= 0:
|
|
192
|
+
continue
|
|
193
|
+
prev_qty = positions[order.symbol]
|
|
194
|
+
new_qty = prev_qty + qty
|
|
195
|
+
cost_basis[order.symbol] = (
|
|
196
|
+
(cost_basis[order.symbol] * prev_qty + fill * qty) / new_qty
|
|
197
|
+
if new_qty > 0 else 0.0
|
|
198
|
+
)
|
|
199
|
+
positions[order.symbol] = new_qty
|
|
200
|
+
cash -= gross + fee
|
|
201
|
+
trades.append({"date": date, "symbol": order.symbol, "side": "buy",
|
|
202
|
+
"qty": round(qty, 6), "price": round(fill, 6),
|
|
203
|
+
"fee": round(fee, 6)})
|
|
204
|
+
else: # sell
|
|
205
|
+
qty = min(qty, positions[order.symbol])
|
|
206
|
+
if qty <= 0:
|
|
207
|
+
continue
|
|
208
|
+
gross = fill * qty
|
|
209
|
+
fee = gross * self.commission
|
|
210
|
+
realized = (fill - cost_basis[order.symbol]) * qty - fee
|
|
211
|
+
positions[order.symbol] -= qty
|
|
212
|
+
cash += gross - fee
|
|
213
|
+
trades.append({"date": date, "symbol": order.symbol, "side": "sell",
|
|
214
|
+
"qty": round(qty, 6), "price": round(fill, 6),
|
|
215
|
+
"fee": round(fee, 6), "realized_pnl": round(realized, 6)})
|
|
216
|
+
pending = []
|
|
217
|
+
|
|
218
|
+
# 2) Mark-to-market at today's close; record equity.
|
|
219
|
+
prices = {s: last_close[s] for s in symbols}
|
|
220
|
+
equity = cash + sum(positions[s] * prices[s] for s in symbols)
|
|
221
|
+
equity_curve.append({"date": date, "equity": round(equity, 4)})
|
|
222
|
+
|
|
223
|
+
# 3) Strategy decides on today's close → queue orders for next open.
|
|
224
|
+
ctx = Context(date, cur, history, Portfolio(cash, dict(positions)))
|
|
225
|
+
try:
|
|
226
|
+
orders = strategy.on_bar(ctx) or []
|
|
227
|
+
except Exception:
|
|
228
|
+
orders = []
|
|
229
|
+
for o in orders:
|
|
230
|
+
pending.append(self._normalize(o, equity, prices, positions))
|
|
231
|
+
|
|
232
|
+
metrics = _metrics(equity_curve, trades, self.periods_per_year)
|
|
233
|
+
benchmark = self._buy_hold(data, all_dates)
|
|
234
|
+
return BacktestResult(
|
|
235
|
+
strategy=getattr(strategy, "name", "strategy"),
|
|
236
|
+
symbols=symbols, starting_cash=self.starting_cash,
|
|
237
|
+
equity_curve=equity_curve, trades=trades,
|
|
238
|
+
metrics=metrics, benchmark=benchmark,
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
def _normalize(self, o: StrategyOrder, equity: float,
|
|
242
|
+
prices: Dict[str, float], positions: Dict[str, float]) -> StrategyOrder:
|
|
243
|
+
"""Convert a target-weight order into a concrete buy/sell quantity."""
|
|
244
|
+
if o.action != "target" or o.target_weight is None:
|
|
245
|
+
return o
|
|
246
|
+
px = prices.get(o.symbol, 0.0)
|
|
247
|
+
if px <= 0:
|
|
248
|
+
return StrategyOrder(o.symbol, "buy", 0.0)
|
|
249
|
+
target_shares = (o.target_weight * equity) / px
|
|
250
|
+
delta = target_shares - positions.get(o.symbol, 0.0)
|
|
251
|
+
if delta >= 0:
|
|
252
|
+
return StrategyOrder(o.symbol, "buy", round(delta, 6))
|
|
253
|
+
return StrategyOrder(o.symbol, "sell", round(-delta, 6))
|
|
254
|
+
|
|
255
|
+
def _buy_hold(self, data: Dict[str, List[Bar]], all_dates: List[str]) -> Dict[str, Any]:
|
|
256
|
+
"""Equal-weight buy-and-hold benchmark over the same window."""
|
|
257
|
+
symbols = [s for s in data if data[s]]
|
|
258
|
+
if not symbols or len(all_dates) < 2:
|
|
259
|
+
return {"total_return": 0.0}
|
|
260
|
+
firsts, lasts = {}, {}
|
|
261
|
+
for s in symbols:
|
|
262
|
+
bars = data[s]
|
|
263
|
+
firsts[s] = bars[0].close
|
|
264
|
+
lasts[s] = bars[-1].close
|
|
265
|
+
rets = [(lasts[s] / firsts[s] - 1) for s in symbols if firsts[s] > 0]
|
|
266
|
+
return {"total_return": round(sum(rets) / len(rets), 6) if rets else 0.0}
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
# ── Backtest <-> live parity bridge ────────────────────────────────────────────
|
|
270
|
+
|
|
271
|
+
# Strategies only simulate fills here; broker execution belongs to the host.
|
|
272
|
+
|
|
273
|
+
class BuyHoldStrategy(Strategy):
|
|
274
|
+
name = "buy_hold"
|
|
275
|
+
|
|
276
|
+
def __init__(self, weight: float = 1.0):
|
|
277
|
+
self.weight = weight
|
|
278
|
+
self._entered: set[str] = set()
|
|
279
|
+
|
|
280
|
+
def on_bar(self, ctx: Context) -> List[StrategyOrder]:
|
|
281
|
+
orders = []
|
|
282
|
+
per = self.weight / max(1, len(ctx.bars))
|
|
283
|
+
for s in ctx.bars:
|
|
284
|
+
if s not in self._entered:
|
|
285
|
+
orders.append(StrategyOrder(s, "target", target_weight=per))
|
|
286
|
+
self._entered.add(s)
|
|
287
|
+
return orders
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
class SmaCrossStrategy(Strategy):
|
|
291
|
+
name = "sma_cross"
|
|
292
|
+
|
|
293
|
+
def __init__(self, fast: int = 20, slow: int = 60, weight: float = 0.95):
|
|
294
|
+
self.fast, self.slow, self.weight = fast, slow, weight
|
|
295
|
+
|
|
296
|
+
def on_bar(self, ctx: Context) -> List[StrategyOrder]:
|
|
297
|
+
orders = []
|
|
298
|
+
per = self.weight / max(1, len(ctx.bars))
|
|
299
|
+
for s in ctx.bars:
|
|
300
|
+
closes = ctx.closes(s)
|
|
301
|
+
if len(closes) < self.slow:
|
|
302
|
+
continue
|
|
303
|
+
fast_ma = sum(closes[-self.fast:]) / self.fast
|
|
304
|
+
slow_ma = sum(closes[-self.slow:]) / self.slow
|
|
305
|
+
held = ctx.position(s) > 0
|
|
306
|
+
if fast_ma > slow_ma and not held:
|
|
307
|
+
orders.append(StrategyOrder(s, "target", target_weight=per))
|
|
308
|
+
elif fast_ma <= slow_ma and held:
|
|
309
|
+
orders.append(StrategyOrder(s, "target", target_weight=0.0))
|
|
310
|
+
return orders
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
_STRATEGIES: Dict[str, Callable[..., Strategy]] = {
|
|
314
|
+
"buy_hold": BuyHoldStrategy,
|
|
315
|
+
"sma_cross": SmaCrossStrategy,
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def get_strategy(name: str, **kwargs: Any) -> Strategy:
|
|
320
|
+
cls = _STRATEGIES.get(str(name).lower())
|
|
321
|
+
if not cls:
|
|
322
|
+
raise ValueError(f"unknown strategy: {name} (have: {', '.join(_STRATEGIES)})")
|
|
323
|
+
return cls(**kwargs)
|
|
324
|
+
|
|
325
|
+
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""Performance statistics, from the data's own calendar.
|
|
2
|
+
|
|
3
|
+
There were two backtest calculators in this package, and they disagreed. The
|
|
4
|
+
engine annualized as (1 + total) ** (252 / bars) - 1, which is right only when
|
|
5
|
+
a year holds 252 bars. Run on a calendar year of daily bars for a market that
|
|
6
|
+
trades every day it reported a 10.5% return as 7.1% a year, and on a year of
|
|
7
|
+
weekly bars it reported 59%. The report module annualized by calendar span but
|
|
8
|
+
still scaled volatility and Sharpe by sqrt(252). Neither could be right for a
|
|
9
|
+
24/7 market, for weekly bars, or for an intraday run, and nothing tested the
|
|
10
|
+
numbers, because the only check was the output of the same code.
|
|
11
|
+
|
|
12
|
+
The statistics here read the span from the dates themselves, so one rule holds
|
|
13
|
+
for every bar frequency:
|
|
14
|
+
|
|
15
|
+
years = (last date - first date) / 365.25
|
|
16
|
+
annualized return = (1 + total) ** (1 / years) - 1
|
|
17
|
+
periods per year = observations / years
|
|
18
|
+
Sharpe = mean / sample-std * sqrt(periods per year)
|
|
19
|
+
|
|
20
|
+
An explicit periods-per-year is still honoured: someone who passes 365 knows
|
|
21
|
+
their data. Dates that cannot be read fall back to 252, the previous behaviour,
|
|
22
|
+
which keeps synthetic series ("1", "2", "3") working. There is a Go port of
|
|
23
|
+
this arithmetic (go/runtime/quant) checked against the fixtures this module
|
|
24
|
+
generates; change both together.
|
|
25
|
+
"""
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
import math
|
|
29
|
+
from datetime import date
|
|
30
|
+
from typing import Optional, Sequence
|
|
31
|
+
|
|
32
|
+
#: What the engine assumed for every series, and what an undatable one still gets.
|
|
33
|
+
FALLBACK_PERIODS_PER_YEAR = 252
|
|
34
|
+
|
|
35
|
+
DAYS_PER_YEAR = 365.25
|
|
36
|
+
|
|
37
|
+
#: A span shorter than one trading day would compound a result hundreds of
|
|
38
|
+
#: times over. The report module already clamped here; the engine now does too.
|
|
39
|
+
MIN_YEARS = 1.0 / FALLBACK_PERIODS_PER_YEAR
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def parse_day(text: object) -> Optional[date]:
|
|
43
|
+
"""The calendar day of an ISO timestamp or date, or None when it is not one."""
|
|
44
|
+
try:
|
|
45
|
+
return date.fromisoformat(str(text)[:10])
|
|
46
|
+
except ValueError:
|
|
47
|
+
return None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def span_years(first: object, last: object) -> Optional[float]:
|
|
51
|
+
"""Years between two dates, or None when either is unreadable or the span is not forward.
|
|
52
|
+
|
|
53
|
+
Clamped to MIN_YEARS from below so a one-day span cannot compound into nonsense.
|
|
54
|
+
"""
|
|
55
|
+
a, b = parse_day(first), parse_day(last)
|
|
56
|
+
if a is None or b is None or b <= a:
|
|
57
|
+
return None
|
|
58
|
+
return max((b - a).days / DAYS_PER_YEAR, MIN_YEARS)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def annualized_return(total_return: float, years: float) -> float:
|
|
62
|
+
"""The constant yearly rate that compounds to `total_return` over `years`."""
|
|
63
|
+
if total_return <= -1.0:
|
|
64
|
+
return -1.0
|
|
65
|
+
return (1.0 + total_return) ** (1.0 / years) - 1.0
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def stddev(values: Sequence[float]) -> float:
|
|
69
|
+
"""Sample standard deviation (n - 1); 0 for fewer than two values."""
|
|
70
|
+
if len(values) < 2:
|
|
71
|
+
return 0.0
|
|
72
|
+
mean = sum(values) / len(values)
|
|
73
|
+
variance = sum((v - mean) ** 2 for v in values) / (len(values) - 1)
|
|
74
|
+
return math.sqrt(max(variance, 0.0))
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def sharpe(returns: Sequence[float], periods_per_year: float) -> float:
|
|
78
|
+
"""Annualized mean over sample standard deviation; 0 when there is no spread."""
|
|
79
|
+
if len(returns) < 2:
|
|
80
|
+
return 0.0
|
|
81
|
+
std = stddev(returns)
|
|
82
|
+
if std <= 0:
|
|
83
|
+
return 0.0
|
|
84
|
+
return (sum(returns) / len(returns)) / std * math.sqrt(periods_per_year)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def max_drawdown(equity: Sequence[float]) -> float:
|
|
88
|
+
"""The worst peak-to-trough fall, as a negative fraction (0 when never below a peak)."""
|
|
89
|
+
peak = equity[0] if equity else 0.0
|
|
90
|
+
worst = 0.0
|
|
91
|
+
for value in equity:
|
|
92
|
+
peak = max(peak, value)
|
|
93
|
+
if peak > 0:
|
|
94
|
+
worst = min(worst, value / peak - 1.0)
|
|
95
|
+
return worst
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def periods_per_year_from_dates(n_returns: int, first: object, last: object) -> Optional[float]:
|
|
99
|
+
"""How many observations a year this series holds, or None when its dates cannot say."""
|
|
100
|
+
years = span_years(first, last)
|
|
101
|
+
return None if years is None else n_returns / years
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""Backtest compatibility API and host-specific data/broker adapters.
|
|
2
|
+
|
|
3
|
+
The pure simulation and strategy implementation is owned by financial-service
|
|
4
|
+
and pinned in finance-core.lock.json. This module retains existing imports,
|
|
5
|
+
data loading and order-intent conversion; it does not execute broker orders.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import List
|
|
11
|
+
|
|
12
|
+
from ._finance_core.backtest_engine import (
|
|
13
|
+
Bar,
|
|
14
|
+
BacktestEngine,
|
|
15
|
+
BacktestResult,
|
|
16
|
+
BuyHoldStrategy,
|
|
17
|
+
Context,
|
|
18
|
+
Portfolio,
|
|
19
|
+
SmaCrossStrategy,
|
|
20
|
+
Strategy,
|
|
21
|
+
StrategyOrder,
|
|
22
|
+
get_strategy,
|
|
23
|
+
stats,
|
|
24
|
+
_metrics,
|
|
25
|
+
_STRATEGIES,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
"Bar",
|
|
30
|
+
"BacktestEngine",
|
|
31
|
+
"BacktestResult",
|
|
32
|
+
"BuyHoldStrategy",
|
|
33
|
+
"Context",
|
|
34
|
+
"Portfolio",
|
|
35
|
+
"SmaCrossStrategy",
|
|
36
|
+
"Strategy",
|
|
37
|
+
"StrategyOrder",
|
|
38
|
+
"get_strategy",
|
|
39
|
+
"load_bars",
|
|
40
|
+
"strategy_order_to_intent",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def strategy_order_to_intent(order: StrategyOrder, *, price: float | None = None, source: str = "strategy"):
|
|
45
|
+
"""Convert a StrategyOrder into a broker OrderIntent so the SAME strategy can
|
|
46
|
+
drive live trading through the preview -> risk -> (auto)execute pipeline."""
|
|
47
|
+
from aria_code.brokers.trading import OrderIntent
|
|
48
|
+
|
|
49
|
+
if order.action == "target":
|
|
50
|
+
return OrderIntent(symbol=order.symbol, side="buy", target_weight=order.target_weight, source=source)
|
|
51
|
+
return OrderIntent(symbol=order.symbol, side=order.action, quantity=order.quantity, price=price, source=source)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def load_bars(symbol: str, days: int = 365, interval: str = "1d") -> List[Bar]:
|
|
55
|
+
"""Pull OHLC history via DataService and adapt to Bar objects (for real runs)."""
|
|
56
|
+
from aria_code.data_service import DataService
|
|
57
|
+
|
|
58
|
+
res = DataService().history(symbol, days=days, interval=interval)
|
|
59
|
+
rows = (res.data or {}).get("data") or []
|
|
60
|
+
out: List[Bar] = []
|
|
61
|
+
for r in rows:
|
|
62
|
+
try:
|
|
63
|
+
out.append(
|
|
64
|
+
Bar(
|
|
65
|
+
date=str(r.get("date"))[:10],
|
|
66
|
+
open=float(r.get("open", 0) or 0),
|
|
67
|
+
high=float(r.get("high", 0) or 0),
|
|
68
|
+
low=float(r.get("low", 0) or 0),
|
|
69
|
+
close=float(r.get("close", 0) or 0),
|
|
70
|
+
volume=float(r.get("volume", 0) or 0),
|
|
71
|
+
)
|
|
72
|
+
)
|
|
73
|
+
except (TypeError, ValueError):
|
|
74
|
+
continue
|
|
75
|
+
return out
|