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,303 @@
|
|
|
1
|
+
"""
|
|
2
|
+
agents/financial/earnings.py — 财报解读 Agent
|
|
3
|
+
=============================================
|
|
4
|
+
分析最近一期财报:EPS/营收 beat or miss、同比/环比变化、
|
|
5
|
+
指引调整及市场反应。在财报发布后 5 天内尤为有效。
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
from datetime import datetime, timedelta
|
|
12
|
+
from typing import Any, Dict, List, Optional
|
|
13
|
+
|
|
14
|
+
from ..base import BaseAgent, AgentResult
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class EarningsAgent(BaseAgent):
|
|
20
|
+
|
|
21
|
+
name = "earnings"
|
|
22
|
+
description = "财报解读 — EPS/营收 beat or miss、同比增速、指引变化"
|
|
23
|
+
|
|
24
|
+
_SYSTEM = (
|
|
25
|
+
"You are a financial analyst specializing in earnings analysis. "
|
|
26
|
+
"Evaluate the most recent quarterly earnings report:\n"
|
|
27
|
+
"1. Was it a beat or miss on EPS and revenue?\n"
|
|
28
|
+
"2. How does this compare to prior quarters (trend)?\n"
|
|
29
|
+
"3. Was guidance raised, lowered, or maintained?\n"
|
|
30
|
+
"4. What is the market likely to do with this information?\n"
|
|
31
|
+
"5. Conclude: STRONG_BEAT / BEAT / IN_LINE / MISS / STRONG_MISS\n"
|
|
32
|
+
"Map to signal: BEAT/STRONG_BEAT→BUY, IN_LINE→HOLD, MISS/STRONG_MISS→SELL"
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
async def fetch_data(self, symbol: str) -> Dict[str, Any]:
|
|
36
|
+
data = await super().fetch_data(symbol)
|
|
37
|
+
earnings: Dict[str, Any] = {}
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
import yfinance as yf
|
|
41
|
+
ticker = yf.Ticker(symbol)
|
|
42
|
+
|
|
43
|
+
# 1. Quarterly earnings history
|
|
44
|
+
try:
|
|
45
|
+
qe = ticker.quarterly_earnings
|
|
46
|
+
if qe is not None and not qe.empty:
|
|
47
|
+
rows = []
|
|
48
|
+
for ts, row in qe.sort_index(ascending=False).head(4).iterrows():
|
|
49
|
+
rows.append({
|
|
50
|
+
"period": str(ts)[:10],
|
|
51
|
+
"actual_eps": _safe_float(row.get("Earnings")),
|
|
52
|
+
"est_eps": _safe_float(row.get("Estimate", row.get("EPS Estimate"))),
|
|
53
|
+
"revenue": _safe_float(row.get("Revenue")),
|
|
54
|
+
"rev_estimate": _safe_float(row.get("Revenue Estimate")),
|
|
55
|
+
})
|
|
56
|
+
earnings["quarterly_eps"] = rows
|
|
57
|
+
except Exception as e:
|
|
58
|
+
logger.debug("[earnings] quarterly_earnings %s: %s", symbol, e)
|
|
59
|
+
|
|
60
|
+
# 2. Earnings dates
|
|
61
|
+
try:
|
|
62
|
+
ed = ticker.earnings_dates
|
|
63
|
+
if ed is not None and not ed.empty:
|
|
64
|
+
recent = ed.sort_index(ascending=False).head(2)
|
|
65
|
+
dates = []
|
|
66
|
+
for ts, row in recent.iterrows():
|
|
67
|
+
reported_eps = _safe_float(row.get("Reported EPS", row.get("EPS")))
|
|
68
|
+
est_eps = _safe_float(row.get("EPS Estimate"))
|
|
69
|
+
if reported_eps is not None:
|
|
70
|
+
beat = reported_eps - est_eps if est_eps is not None else None
|
|
71
|
+
dates.append({
|
|
72
|
+
"date": str(ts)[:10],
|
|
73
|
+
"reported_eps": reported_eps,
|
|
74
|
+
"estimated_eps": est_eps,
|
|
75
|
+
"beat_by": round(beat, 4) if beat is not None else None,
|
|
76
|
+
"pct_surprise": round(beat / abs(est_eps) * 100, 1)
|
|
77
|
+
if (beat is not None and est_eps and est_eps != 0) else None,
|
|
78
|
+
})
|
|
79
|
+
earnings["recent_reports"] = dates
|
|
80
|
+
except Exception as e:
|
|
81
|
+
logger.debug("[earnings] earnings_dates %s: %s", symbol, e)
|
|
82
|
+
|
|
83
|
+
# 3. Revenue trend (quarterly income)
|
|
84
|
+
try:
|
|
85
|
+
qs = ticker.quarterly_financials
|
|
86
|
+
if qs is not None and not qs.empty:
|
|
87
|
+
rev_row = None
|
|
88
|
+
for label in ("Total Revenue", "Revenue", "Net Revenue"):
|
|
89
|
+
if label in qs.index:
|
|
90
|
+
rev_row = qs.loc[label]
|
|
91
|
+
break
|
|
92
|
+
if rev_row is not None:
|
|
93
|
+
revs = []
|
|
94
|
+
for ts, val in rev_row.sort_index(ascending=False).head(4).items():
|
|
95
|
+
revs.append({"period": str(ts)[:10], "revenue": _safe_float(val)})
|
|
96
|
+
earnings["revenue_trend"] = revs
|
|
97
|
+
except Exception as e:
|
|
98
|
+
logger.debug("[earnings] quarterly_financials %s: %s", symbol, e)
|
|
99
|
+
|
|
100
|
+
# 4. Stock price reaction (compare price before/after earnings)
|
|
101
|
+
try:
|
|
102
|
+
recent = earnings.get("recent_reports", [])
|
|
103
|
+
if recent:
|
|
104
|
+
from datetime import date
|
|
105
|
+
report_date_str = recent[0].get("date", "")
|
|
106
|
+
if report_date_str:
|
|
107
|
+
hist = ticker.history(start=report_date_str, period="5d")
|
|
108
|
+
if not hist.empty and len(hist) >= 2:
|
|
109
|
+
open_price = float(hist["Open"].iloc[0])
|
|
110
|
+
close_price = float(hist["Close"].iloc[-1])
|
|
111
|
+
earnings["price_reaction_pct"] = round(
|
|
112
|
+
(close_price - open_price) / open_price * 100, 2
|
|
113
|
+
)
|
|
114
|
+
except Exception as e:
|
|
115
|
+
logger.debug("[earnings] price_reaction %s: %s", symbol, e)
|
|
116
|
+
|
|
117
|
+
except Exception as e:
|
|
118
|
+
logger.debug("[earnings] yfinance init %s: %s", symbol, e)
|
|
119
|
+
|
|
120
|
+
data["earnings"] = earnings
|
|
121
|
+
return data
|
|
122
|
+
|
|
123
|
+
async def analyze(self, symbol: str, data: Dict[str, Any]) -> AgentResult:
|
|
124
|
+
earnings = data.get("earnings", {})
|
|
125
|
+
quote = data.get("quote", {})
|
|
126
|
+
price = quote.get("price", 0)
|
|
127
|
+
|
|
128
|
+
if not earnings:
|
|
129
|
+
return AgentResult(
|
|
130
|
+
agent=self.name, symbol=symbol,
|
|
131
|
+
analysis=f"{symbol}: 未获取到财报数据。",
|
|
132
|
+
confidence=0.3, signal="HOLD",
|
|
133
|
+
key_points=["无财报数据"],
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
earnings_block = _format_earnings(earnings)
|
|
137
|
+
most_recent = earnings.get("recent_reports", [{}])[0] if earnings.get("recent_reports") else {}
|
|
138
|
+
beat_miss_summary = _assess_beat_miss(most_recent, earnings)
|
|
139
|
+
|
|
140
|
+
prompt = (
|
|
141
|
+
f"Stock: {symbol} Price: {price}\n\n"
|
|
142
|
+
f"Earnings Data:\n{earnings_block}\n\n"
|
|
143
|
+
f"Initial Assessment: {beat_miss_summary}\n\n"
|
|
144
|
+
"Provide detailed earnings analysis:\n"
|
|
145
|
+
"1. Most recent quarter: beat or miss? By how much?\n"
|
|
146
|
+
"2. Revenue & EPS trend direction (improving / deteriorating / stable)\n"
|
|
147
|
+
"3. Likely market interpretation\n"
|
|
148
|
+
"4. Conclude: STRONG_BEAT / BEAT / IN_LINE / MISS / STRONG_MISS"
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
analysis = await self._call_llm(self._SYSTEM, prompt, max_tokens=500)
|
|
152
|
+
if not analysis:
|
|
153
|
+
analysis = _template_analysis(symbol, earnings, most_recent, beat_miss_summary)
|
|
154
|
+
|
|
155
|
+
signal, confidence = _derive_signal(analysis, most_recent, earnings)
|
|
156
|
+
key_points = _build_key_points(earnings, most_recent, signal)
|
|
157
|
+
|
|
158
|
+
return AgentResult(
|
|
159
|
+
agent=self.name, symbol=symbol,
|
|
160
|
+
analysis=analysis,
|
|
161
|
+
confidence=confidence,
|
|
162
|
+
signal=signal,
|
|
163
|
+
key_points=key_points,
|
|
164
|
+
data_used={
|
|
165
|
+
"beat_miss": beat_miss_summary,
|
|
166
|
+
"pct_surprise": most_recent.get("pct_surprise"),
|
|
167
|
+
"price_reaction_pct": earnings.get("price_reaction_pct"),
|
|
168
|
+
},
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
# ── Helpers ───────────────────────────────────────────────────────────────────
|
|
173
|
+
|
|
174
|
+
def _safe_float(val) -> Optional[float]:
|
|
175
|
+
try:
|
|
176
|
+
v = float(val)
|
|
177
|
+
return None if (v != v) else v # NaN check
|
|
178
|
+
except (TypeError, ValueError):
|
|
179
|
+
return None
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def _format_earnings(e: Dict) -> str:
|
|
183
|
+
lines = []
|
|
184
|
+
recent = e.get("recent_reports", [])
|
|
185
|
+
if recent:
|
|
186
|
+
lines.append("Recent Reports:")
|
|
187
|
+
for r in recent[:2]:
|
|
188
|
+
act = r.get("reported_eps")
|
|
189
|
+
est = r.get("estimated_eps")
|
|
190
|
+
sup = r.get("pct_surprise")
|
|
191
|
+
lines.append(
|
|
192
|
+
f" {r.get('date','')} EPS: actual {act} est {est} "
|
|
193
|
+
f"surprise {sup:+.1f}%" if sup else
|
|
194
|
+
f" {r.get('date','')} EPS: actual {act} est {est}"
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
rx = e.get("price_reaction_pct")
|
|
198
|
+
if rx is not None:
|
|
199
|
+
lines.append(f"Post-earnings price reaction: {rx:+.1f}%")
|
|
200
|
+
|
|
201
|
+
qt = e.get("quarterly_eps", [])
|
|
202
|
+
if qt:
|
|
203
|
+
lines.append("Quarterly EPS (last 4):")
|
|
204
|
+
for q in qt[:4]:
|
|
205
|
+
ep = q.get("actual_eps")
|
|
206
|
+
lines.append(f" {q.get('period','')} EPS: {ep}")
|
|
207
|
+
|
|
208
|
+
rev = e.get("revenue_trend", [])
|
|
209
|
+
if rev:
|
|
210
|
+
lines.append("Revenue Trend (last 4 quarters):")
|
|
211
|
+
prev = None
|
|
212
|
+
for r in rev[:4]:
|
|
213
|
+
rv = r.get("revenue")
|
|
214
|
+
growth = ""
|
|
215
|
+
if rv and prev:
|
|
216
|
+
g = (rv - prev) / abs(prev) * 100
|
|
217
|
+
growth = f" {g:+.1f}% QoQ"
|
|
218
|
+
lines.append(f" {r.get('period','')} {_fmt_num(rv)}{growth}")
|
|
219
|
+
prev = rv
|
|
220
|
+
|
|
221
|
+
return "\n".join(lines) or "无财报数据"
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def _fmt_num(v) -> str:
|
|
225
|
+
if v is None: return "N/A"
|
|
226
|
+
if abs(v) >= 1e9: return f"${v/1e9:.2f}B"
|
|
227
|
+
if abs(v) >= 1e6: return f"${v/1e6:.1f}M"
|
|
228
|
+
return f"{v:.4f}"
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def _assess_beat_miss(most_recent: Dict, earnings: Dict) -> str:
|
|
232
|
+
sup = most_recent.get("pct_surprise")
|
|
233
|
+
if sup is None:
|
|
234
|
+
return "无充分数据判断"
|
|
235
|
+
if sup >= 10: return f"大幅超预期 +{sup:.1f}%"
|
|
236
|
+
if sup >= 3: return f"超预期 +{sup:.1f}%"
|
|
237
|
+
if sup >= -3: return f"基本符合预期 {sup:+.1f}%"
|
|
238
|
+
if sup >= -10: return f"略低预期 {sup:.1f}%"
|
|
239
|
+
return f"大幅低于预期 {sup:.1f}%"
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def _derive_signal(analysis: str, most_recent: Dict, earnings: Dict) -> tuple[str, float]:
|
|
243
|
+
text = analysis.upper()
|
|
244
|
+
sup = most_recent.get("pct_surprise")
|
|
245
|
+
rx = earnings.get("price_reaction_pct")
|
|
246
|
+
|
|
247
|
+
if "STRONG_BEAT" in text:
|
|
248
|
+
return "BUY", 0.75
|
|
249
|
+
if "STRONG_MISS" in text:
|
|
250
|
+
return "SELL", 0.75
|
|
251
|
+
if "BEAT" in text and "MISS" not in text:
|
|
252
|
+
conf = 0.65 if (rx and rx > 2) else 0.55
|
|
253
|
+
return "BUY", conf
|
|
254
|
+
if "MISS" in text and "BEAT" not in text:
|
|
255
|
+
conf = 0.65 if (rx and rx < -2) else 0.55
|
|
256
|
+
return "SELL", conf
|
|
257
|
+
|
|
258
|
+
# Fallback to raw surprise
|
|
259
|
+
if sup is not None:
|
|
260
|
+
if sup >= 10: return "BUY", 0.70
|
|
261
|
+
if sup >= 3: return "BUY", 0.55
|
|
262
|
+
if sup <= -10: return "SELL", 0.70
|
|
263
|
+
if sup <= -3: return "SELL", 0.55
|
|
264
|
+
|
|
265
|
+
return "HOLD", 0.40
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def _build_key_points(earnings: Dict, most_recent: Dict, signal: str) -> List[str]:
|
|
269
|
+
pts = []
|
|
270
|
+
sup = most_recent.get("pct_surprise")
|
|
271
|
+
if sup is not None:
|
|
272
|
+
pts.append(f"EPS 超预期 {sup:+.1f}%" if sup >= 0 else f"EPS 低于预期 {sup:.1f}%")
|
|
273
|
+
rx = earnings.get("price_reaction_pct")
|
|
274
|
+
if rx is not None:
|
|
275
|
+
pts.append(f"财报后股价反应: {rx:+.1f}%")
|
|
276
|
+
rev = earnings.get("revenue_trend", [])
|
|
277
|
+
if len(rev) >= 2:
|
|
278
|
+
v0 = rev[0].get("revenue")
|
|
279
|
+
v1 = rev[1].get("revenue")
|
|
280
|
+
if v0 and v1:
|
|
281
|
+
qoq = (v0 - v1) / abs(v1) * 100
|
|
282
|
+
pts.append(f"营收环比 {qoq:+.1f}%")
|
|
283
|
+
pts.append(f"财报信号: {signal}")
|
|
284
|
+
return pts[:5]
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def _template_analysis(symbol: str, earnings: Dict, most_recent: Dict, summary: str) -> str:
|
|
288
|
+
sup = most_recent.get("pct_surprise")
|
|
289
|
+
if sup is not None:
|
|
290
|
+
if sup >= 10: verdict = "STRONG_BEAT"
|
|
291
|
+
elif sup >= 3: verdict = "BEAT"
|
|
292
|
+
elif sup >= -3: verdict = "IN_LINE"
|
|
293
|
+
elif sup >= -10:verdict = "MISS"
|
|
294
|
+
else: verdict = "STRONG_MISS"
|
|
295
|
+
else:
|
|
296
|
+
verdict = "IN_LINE"
|
|
297
|
+
|
|
298
|
+
return (
|
|
299
|
+
f"{symbol} 财报解读(模板):\n"
|
|
300
|
+
f"初步判断:{summary}\n"
|
|
301
|
+
f"财报评级:{verdict}\n"
|
|
302
|
+
f"建议信号:{'BUY' if 'BEAT' in verdict else ('SELL' if 'MISS' in verdict else 'HOLD')}"
|
|
303
|
+
)
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"""
|
|
2
|
+
agents/financial/fundamental.py — 基本面分析 Agent
|
|
3
|
+
====================================================
|
|
4
|
+
分析:PE/PB/ROE、营收增速、竞争壁垒、估值水位。
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
import os
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any, Dict, List, Optional
|
|
11
|
+
from ..base import BaseAgent, AgentResult
|
|
12
|
+
|
|
13
|
+
# 动态加载 aria_tools 中的 MCP 工具
|
|
14
|
+
# 相对本文件定位同仓库的 packages/,不写死开发者本机路径
|
|
15
|
+
_PKGS = str(Path(__file__).resolve().parents[2] / "packages")
|
|
16
|
+
if _PKGS not in sys.path:
|
|
17
|
+
sys.path.append(_PKGS)
|
|
18
|
+
try:
|
|
19
|
+
from aria_tools.financial.factors import run_factor_research
|
|
20
|
+
except ImportError:
|
|
21
|
+
async def run_factor_research(*args, **kwargs) -> str:
|
|
22
|
+
return "Error: run_factor_research tool is not available."
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class FundamentalAgent(BaseAgent):
|
|
26
|
+
name = "fundamental"
|
|
27
|
+
description = "基本面分析:估值/ROE/竞争优势/财务健康"
|
|
28
|
+
|
|
29
|
+
_SYSTEM = (
|
|
30
|
+
"You are a fundamental equity analyst. Evaluate: PE/PB valuation levels "
|
|
31
|
+
"(vs historical range and sector peers), ROE quality, revenue growth, "
|
|
32
|
+
"balance sheet health, and competitive moat. "
|
|
33
|
+
"Be data-driven. End with: UNDERVALUED / FAIRLY_VALUED / OVERVALUED."
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
async def _execute_tool(self, tool_name: str, tool_args: Dict[str, Any]) -> str:
|
|
37
|
+
"""拦截 factor_research 的调用,其他的交给基类"""
|
|
38
|
+
if tool_name == "run_factor_research":
|
|
39
|
+
if self.on_tool_start:
|
|
40
|
+
self.on_tool_start(tool_name, tool_args)
|
|
41
|
+
res = await run_factor_research(**tool_args)
|
|
42
|
+
if self.on_tool_end:
|
|
43
|
+
self.on_tool_end(tool_name, res)
|
|
44
|
+
import json
|
|
45
|
+
return json.dumps({"result": res}, ensure_ascii=False)
|
|
46
|
+
|
|
47
|
+
return await super()._execute_tool(tool_name, tool_args)
|
|
48
|
+
|
|
49
|
+
async def fetch_data(self, symbol: str) -> Dict[str, Any]:
|
|
50
|
+
data = await super().fetch_data(symbol)
|
|
51
|
+
if self.data:
|
|
52
|
+
try:
|
|
53
|
+
f = self.data.fundamentals(symbol)
|
|
54
|
+
if f:
|
|
55
|
+
data["fundamentals"] = {
|
|
56
|
+
"pe_ttm": f.pe_ttm,
|
|
57
|
+
"pb": f.pb,
|
|
58
|
+
"roe": f.roe,
|
|
59
|
+
"revenue_growth": f.revenue_growth,
|
|
60
|
+
"dividend_yield": f.dividend_yield,
|
|
61
|
+
"source": f.source,
|
|
62
|
+
}
|
|
63
|
+
except Exception:
|
|
64
|
+
pass
|
|
65
|
+
return data
|
|
66
|
+
|
|
67
|
+
async def analyze(self, symbol: str, data: Dict[str, Any]) -> AgentResult:
|
|
68
|
+
quote = data.get("quote", {})
|
|
69
|
+
fund = data.get("fundamentals", {})
|
|
70
|
+
price = _num_or_none(quote.get("price"))
|
|
71
|
+
# Accept both schemas: the agent's own fetch_data (pe_ttm/pb) AND the
|
|
72
|
+
# shared team data bundle (pe_ratio/pb_ratio from finnhub/yahoo).
|
|
73
|
+
pe = _num_or_none(
|
|
74
|
+
fund.get("pe_ttm") or fund.get("pe_ratio") or fund.get("pe")
|
|
75
|
+
or quote.get("pe_ttm") or quote.get("pe_ratio")
|
|
76
|
+
)
|
|
77
|
+
pb = _num_or_none(
|
|
78
|
+
fund.get("pb") or fund.get("pb_ratio")
|
|
79
|
+
or quote.get("pb") or quote.get("pb_ratio")
|
|
80
|
+
)
|
|
81
|
+
roe = _num_or_none(fund.get("roe"))
|
|
82
|
+
rev_g = _num_or_none(fund.get("revenue_growth") or fund.get("rev_growth"))
|
|
83
|
+
div_y = _num_or_none(fund.get("dividend_yield"))
|
|
84
|
+
|
|
85
|
+
fund_str = (
|
|
86
|
+
f" PE(TTM): {_fmt_num(pe, 1, 'x')}\n"
|
|
87
|
+
f" PB: {_fmt_num(pb, 2, 'x')}\n"
|
|
88
|
+
f" ROE: {_fmt_num(roe, 1, '%')}\n"
|
|
89
|
+
f" Revenue growth: {_fmt_num(rev_g, 1, '%')}\n"
|
|
90
|
+
f" Dividend yield: {_fmt_num(div_y, 2, '%')}"
|
|
91
|
+
) if pe or pb else " (fundamental data unavailable)"
|
|
92
|
+
|
|
93
|
+
prompt = (
|
|
94
|
+
f"Stock: {symbol} Price: {price}\n"
|
|
95
|
+
f"Fundamentals:\n{fund_str}\n\n"
|
|
96
|
+
"Evaluate:\n"
|
|
97
|
+
"1. Valuation (PE/PB vs historical/sector)\n"
|
|
98
|
+
"2. Profitability quality (ROE trend)\n"
|
|
99
|
+
"3. Growth outlook (revenue/earnings)\n"
|
|
100
|
+
"4. Balance sheet and dividend\n"
|
|
101
|
+
"5. Competitive moat\n"
|
|
102
|
+
"Conclusion: UNDERVALUED / FAIRLY_VALUED / OVERVALUED\n\n"
|
|
103
|
+
"## Available Tools\n"
|
|
104
|
+
"You have access to a tool named `run_factor_research` to evaluate factor validity.\n"
|
|
105
|
+
"If you need to evaluate a factor (like pe, pb, roe), output exactly in this format:\n"
|
|
106
|
+
"{\"type\": \"tool_call\", \"name\": \"run_factor_research\", \"args\": {\"factor_name\": \"pe\"}}\n"
|
|
107
|
+
"Wait for the tool result before concluding."
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
analysis = await self._call_llm(self._SYSTEM, prompt, max_tokens=500, quote=quote)
|
|
111
|
+
if not analysis:
|
|
112
|
+
analysis = _template_fundamental(symbol, pe, pb, roe, rev_g)
|
|
113
|
+
|
|
114
|
+
signal = _extract_signal(analysis, pe or 0)
|
|
115
|
+
confidence = _calc_confidence(pe or 0, pb or 0, roe or 0)
|
|
116
|
+
key_points = _extract_key_points(analysis)
|
|
117
|
+
|
|
118
|
+
return AgentResult(
|
|
119
|
+
agent=self.name, symbol=symbol,
|
|
120
|
+
analysis=analysis, confidence=confidence,
|
|
121
|
+
signal=signal, key_points=key_points,
|
|
122
|
+
data_used={"pe": pe, "pb": pb, "roe": roe},
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _num_or_none(value: Any) -> Optional[float]:
|
|
127
|
+
try:
|
|
128
|
+
if value is None:
|
|
129
|
+
return None
|
|
130
|
+
out = float(value)
|
|
131
|
+
return out if out != 0 else None
|
|
132
|
+
except (TypeError, ValueError):
|
|
133
|
+
return None
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _fmt_num(value: Optional[float], digits: int = 1, suffix: str = "") -> str:
|
|
137
|
+
if value is None:
|
|
138
|
+
return "数据不足"
|
|
139
|
+
return f"{value:.{digits}f}{suffix}"
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _extract_signal(text: str, pe: float = 0) -> str:
|
|
143
|
+
t = text.upper()
|
|
144
|
+
if "UNDERVALUED" in t or "低估" in t: return "BUY"
|
|
145
|
+
if "OVERVALUED" in t or "高估" in t: return "SELL"
|
|
146
|
+
# PE 辅助判断
|
|
147
|
+
if pe > 0:
|
|
148
|
+
if pe < 15: return "BUY"
|
|
149
|
+
if pe > 50: return "SELL"
|
|
150
|
+
return "HOLD"
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _calc_confidence(pe: float, pb: float, roe: float) -> float:
|
|
154
|
+
if pe <= 0 and pb <= 0 and roe == 0:
|
|
155
|
+
return 0.4 # 无数据,低置信度
|
|
156
|
+
score = 0.5
|
|
157
|
+
if pe > 0:
|
|
158
|
+
if pe < 15: score += 0.15
|
|
159
|
+
elif pe < 25: score += 0.05
|
|
160
|
+
elif pe > 50: score -= 0.15
|
|
161
|
+
if roe > 20: score += 0.15
|
|
162
|
+
elif roe > 10: score += 0.05
|
|
163
|
+
return round(min(0.95, max(0.2, score)), 2)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _extract_key_points(text: str) -> List[str]:
|
|
167
|
+
points = []
|
|
168
|
+
for line in text.split("\n"):
|
|
169
|
+
line = line.strip()
|
|
170
|
+
if line.startswith(("1.", "2.", "3.", "4.", "5.", "•", "-", "·")) and len(line) > 5:
|
|
171
|
+
points.append(line.lstrip("1234567890.-•· "))
|
|
172
|
+
return points[:4]
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _template_fundamental(symbol: str, pe: Optional[float], pb: Optional[float],
|
|
176
|
+
roe: Optional[float], rev_g: Optional[float]) -> str:
|
|
177
|
+
if not pe or pe <= 0:
|
|
178
|
+
valuation, conclusion = "数据不足", "DATA_LIMITED"
|
|
179
|
+
elif pe < 15:
|
|
180
|
+
valuation, conclusion = "低估", "UNDERVALUED"
|
|
181
|
+
elif pe > 40:
|
|
182
|
+
valuation, conclusion = "高估", "OVERVALUED"
|
|
183
|
+
else:
|
|
184
|
+
valuation, conclusion = "合理", "FAIRLY_VALUED"
|
|
185
|
+
return (
|
|
186
|
+
f"{symbol} 基本面分析(模板):\n"
|
|
187
|
+
f"• 估值:PE={_fmt_num(pe, 1, 'x')} PB={_fmt_num(pb, 2, 'x')} → {valuation}\n"
|
|
188
|
+
f"• 盈利能力:ROE={_fmt_num(roe, 1, '%')} 营收增速={_fmt_num(rev_g, 1, '%')}\n"
|
|
189
|
+
"• 基本面数据不足时,不应把缺失值当作 0;建议结合财报和行业对比复核\n"
|
|
190
|
+
f"• 结论: {conclusion}"
|
|
191
|
+
)
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""
|
|
2
|
+
agents/financial/macro.py — 宏观环境 Agent
|
|
3
|
+
===========================================
|
|
4
|
+
分析:利率环境、汇率、行业周期、政策面、大盘趋势。
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
from typing import Any, Dict, List
|
|
8
|
+
from ..base import BaseAgent, AgentResult
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class MacroAgent(BaseAgent):
|
|
12
|
+
name = "macro"
|
|
13
|
+
description = "宏观分析:利率/汇率/政策/大盘趋势"
|
|
14
|
+
|
|
15
|
+
_SYSTEM = (
|
|
16
|
+
"You are a macro strategist covering China and global markets. "
|
|
17
|
+
"Focus on: interest rate environment, USD/CNY trend, sector cycle, "
|
|
18
|
+
"regulatory policy, and market sentiment. Be concise and data-driven. "
|
|
19
|
+
"End with: TAILWIND / NEUTRAL / HEADWIND for the given stock."
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
async def fetch_data(self, symbol: str) -> Dict[str, Any]:
|
|
23
|
+
data = await super().fetch_data(symbol)
|
|
24
|
+
# 尝试拉取指数数据作为大盘参考
|
|
25
|
+
if self.data:
|
|
26
|
+
for idx in ["000001", "399006"]: # 上证 + 创业板
|
|
27
|
+
try:
|
|
28
|
+
q = self.data.quote(idx)
|
|
29
|
+
if q:
|
|
30
|
+
data.setdefault("indices", {})[idx] = q.to_dict()
|
|
31
|
+
except Exception:
|
|
32
|
+
pass
|
|
33
|
+
return data
|
|
34
|
+
|
|
35
|
+
async def analyze(self, symbol: str, data: Dict[str, Any]) -> AgentResult:
|
|
36
|
+
quote = data.get("quote", {})
|
|
37
|
+
indices = data.get("indices", {})
|
|
38
|
+
|
|
39
|
+
idx_summary = ""
|
|
40
|
+
for code, q in indices.items():
|
|
41
|
+
name = {"000001": "上证", "399006": "创业板"}.get(code, code)
|
|
42
|
+
idx_summary += f" {name}: {q.get('price',0):.2f} {q.get('change_pct',0):+.2f}%\n"
|
|
43
|
+
|
|
44
|
+
prompt = (
|
|
45
|
+
f"Symbol: {symbol}\n"
|
|
46
|
+
f"Market Indices:\n{idx_summary or ' (unavailable)'}\n\n"
|
|
47
|
+
"Analyze the macroeconomic backdrop for this stock:\n"
|
|
48
|
+
"1. Current rate/liquidity environment (PBOC stance)\n"
|
|
49
|
+
"2. Sector regulatory environment (any recent policies)\n"
|
|
50
|
+
"3. Market sentiment and positioning\n"
|
|
51
|
+
"4. Key macro risks to watch\n"
|
|
52
|
+
"Conclusion: TAILWIND / NEUTRAL / HEADWIND"
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
analysis = await self._call_llm(self._SYSTEM, prompt, max_tokens=500, quote=quote)
|
|
56
|
+
if not analysis:
|
|
57
|
+
analysis = _template_macro(symbol, indices)
|
|
58
|
+
|
|
59
|
+
signal = _extract_signal(analysis)
|
|
60
|
+
key_points = _extract_key_points(analysis)
|
|
61
|
+
|
|
62
|
+
return AgentResult(
|
|
63
|
+
agent=self.name, symbol=symbol,
|
|
64
|
+
analysis=analysis, confidence=0.65,
|
|
65
|
+
signal=signal, key_points=key_points,
|
|
66
|
+
data_used={"indices": indices},
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _extract_signal(text: str) -> str:
|
|
71
|
+
t = text.upper()
|
|
72
|
+
if "TAILWIND" in t or "看多" in t: return "BUY"
|
|
73
|
+
if "HEADWIND" in t or "看空" in t: return "SELL"
|
|
74
|
+
return "HOLD"
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _extract_key_points(text: str) -> List[str]:
|
|
78
|
+
points = []
|
|
79
|
+
for line in text.split("\n"):
|
|
80
|
+
line = line.strip()
|
|
81
|
+
if line.startswith(("1.", "2.", "3.", "4.", "•", "-", "·")) and len(line) > 5:
|
|
82
|
+
points.append(line.lstrip("1234567890.-•· "))
|
|
83
|
+
return points[:4]
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
_IDX_NAMES = {"000001": "上证", "399006": "创业板"}
|
|
87
|
+
|
|
88
|
+
def _template_macro(symbol: str, indices: Dict) -> str:
|
|
89
|
+
idx_str = "、".join(
|
|
90
|
+
f"{_IDX_NAMES.get(k, k)} {v.get('change_pct', 0):+.2f}%"
|
|
91
|
+
for k, v in indices.items()
|
|
92
|
+
) if indices else "指数数据不可用"
|
|
93
|
+
return (
|
|
94
|
+
f"{symbol} 宏观分析(模板):\n"
|
|
95
|
+
f"• 市场指数: {idx_str}\n"
|
|
96
|
+
"• 当前货币政策偏宽松,流动性充裕\n"
|
|
97
|
+
"• 建议关注政策面变化和外资动向\n"
|
|
98
|
+
"• 结论: NEUTRAL"
|
|
99
|
+
)
|