aria-code 4.1.3__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.
Files changed (284) hide show
  1. agents/__init__.py +32 -0
  2. agents/base.py +190 -0
  3. agents/deep/__init__.py +37 -0
  4. agents/deep/calibration_loop.py +144 -0
  5. agents/deep/critic.py +125 -0
  6. agents/deep/deepen.py +193 -0
  7. agents/deep/models.py +149 -0
  8. agents/deep/pipeline.py +164 -0
  9. agents/deep/quant_fusion.py +192 -0
  10. agents/deep/themes.py +95 -0
  11. agents/deep/tiers.py +106 -0
  12. agents/financial/__init__.py +10 -0
  13. agents/financial/catalyst.py +279 -0
  14. agents/financial/debate.py +145 -0
  15. agents/financial/earnings.py +303 -0
  16. agents/financial/fundamental.py +159 -0
  17. agents/financial/macro.py +99 -0
  18. agents/financial/news.py +207 -0
  19. agents/financial/risk.py +132 -0
  20. agents/financial/sector.py +279 -0
  21. agents/financial/synthesis.py +274 -0
  22. agents/financial/technical.py +258 -0
  23. agents/portfolio_agent.py +333 -0
  24. agents/realty/__init__.py +62 -0
  25. agents/realty/asset_diagnosis.py +150 -0
  26. agents/realty/business_match.py +165 -0
  27. agents/realty/cashflow_verify.py +208 -0
  28. agents/realty/contract_rules.py +209 -0
  29. agents/realty/energy_anomaly.py +188 -0
  30. agents/realty/exit_settlement.py +207 -0
  31. agents/realty/fulfillment_risk.py +205 -0
  32. agents/realty/ops_optimize.py +159 -0
  33. agents/realty/revenue_share.py +214 -0
  34. agents/registry.py +144 -0
  35. agents/sports/__init__.py +0 -0
  36. agents/sports/football_agent.py +169 -0
  37. agents/team.py +289 -0
  38. aliyun_data_client.py +660 -0
  39. apps/README.md +12 -0
  40. apps/__init__.py +2 -0
  41. apps/channels/README.md +15 -0
  42. apps/cli/README.md +13 -0
  43. apps/cli/__init__.py +2 -0
  44. apps/cli/bootstrap.py +99 -0
  45. apps/cli/codegen_paths.py +29 -0
  46. apps/cli/commands/__init__.py +16 -0
  47. apps/cli/commands/analysis_cmds.py +288 -0
  48. apps/cli/commands/backtest_cmds.py +1887 -0
  49. apps/cli/commands/broker_cmds.py +1154 -0
  50. apps/cli/commands/business_workflow_cmds.py +289 -0
  51. apps/cli/commands/catalog.py +84 -0
  52. apps/cli/commands/data_cmds.py +405 -0
  53. apps/cli/commands/diagnostic_cmds.py +179 -0
  54. apps/cli/commands/diagnostic_ops_cmds.py +696 -0
  55. apps/cli/commands/finance_render.py +12 -0
  56. apps/cli/commands/market.py +399 -0
  57. apps/cli/commands/market_cmds.py +1276 -0
  58. apps/cli/commands/market_context.py +425 -0
  59. apps/cli/commands/market_render.py +7 -0
  60. apps/cli/commands/model_cmds.py +1579 -0
  61. apps/cli/commands/ops_cmds.py +668 -0
  62. apps/cli/commands/portfolio_cmds.py +962 -0
  63. apps/cli/commands/report.py +377 -0
  64. apps/cli/commands/scaffold_templates.py +617 -0
  65. apps/cli/commands/session_cmds.py +179 -0
  66. apps/cli/commands/session_ux_cmds.py +280 -0
  67. apps/cli/commands/team.py +588 -0
  68. apps/cli/commands/team_render.py +8 -0
  69. apps/cli/commands/ui_cmds.py +358 -0
  70. apps/cli/commands/workflow_cmds.py +279 -0
  71. apps/cli/commands/workspace_cmds.py +1414 -0
  72. apps/cli/config_paths.py +70 -0
  73. apps/cli/config_store.py +61 -0
  74. apps/cli/deterministic.py +122 -0
  75. apps/cli/direct.py +48 -0
  76. apps/cli/github_app_auth.py +135 -0
  77. apps/cli/handlers/__init__.py +11 -0
  78. apps/cli/handlers/broker_handlers.py +122 -0
  79. apps/cli/handlers/chart_handlers.py +1309 -0
  80. apps/cli/handlers/market_handlers.py +2509 -0
  81. apps/cli/handlers/realty_handlers.py +114 -0
  82. apps/cli/handlers/strategy_advice.py +82 -0
  83. apps/cli/hooks.py +180 -0
  84. apps/cli/i18n.py +284 -0
  85. apps/cli/intent.py +136 -0
  86. apps/cli/intent_router.py +217 -0
  87. apps/cli/lifecycle_hooks.py +48 -0
  88. apps/cli/main.py +29 -0
  89. apps/cli/market_metadata.py +135 -0
  90. apps/cli/market_universe.py +265 -0
  91. apps/cli/message_processing.py +257 -0
  92. apps/cli/plan_mode.py +139 -0
  93. apps/cli/plotly_html.py +15 -0
  94. apps/cli/prediction_feedback.py +202 -0
  95. apps/cli/preflight.py +497 -0
  96. apps/cli/project_aria.py +60 -0
  97. apps/cli/prompts/__init__.py +0 -0
  98. apps/cli/prompts/coding.py +658 -0
  99. apps/cli/prompts/system_prompts.py +531 -0
  100. apps/cli/prompts/ui.py +434 -0
  101. apps/cli/providers/__init__.py +1 -0
  102. apps/cli/providers/base.py +271 -0
  103. apps/cli/providers/chat_routing.py +80 -0
  104. apps/cli/providers/llm/__init__.py +1 -0
  105. apps/cli/providers/llm/ollama_stream.py +1170 -0
  106. apps/cli/providers/llm/sse_stream.py +216 -0
  107. apps/cli/providers/runtime_bridge.py +185 -0
  108. apps/cli/runtime_consumer.py +489 -0
  109. apps/cli/session_export.py +87 -0
  110. apps/cli/session_jsonl.py +207 -0
  111. apps/cli/session_store.py +112 -0
  112. apps/cli/todo_tracker.py +190 -0
  113. apps/cli/tools/__init__.py +40 -0
  114. apps/cli/tools/context.py +46 -0
  115. apps/cli/tools/file_tools.py +112 -0
  116. apps/cli/tools/market_tools.py +549 -0
  117. apps/cli/tools/notebook_tools.py +111 -0
  118. apps/cli/tools/system_tools.py +669 -0
  119. apps/cli/tools/write_tools.py +715 -0
  120. apps/cli/tradingview_bridge.py +434 -0
  121. apps/cli/update_check.py +152 -0
  122. apps/cli/utils/__init__.py +0 -0
  123. apps/cli/utils/market_detect.py +1578 -0
  124. apps/daemon/README.md +14 -0
  125. apps/vscode/README.md +115 -0
  126. apps/vscode/package.json +70 -0
  127. aria_cli.py +11636 -0
  128. aria_code-4.1.3.dist-info/METADATA +952 -0
  129. aria_code-4.1.3.dist-info/RECORD +284 -0
  130. aria_code-4.1.3.dist-info/WHEEL +5 -0
  131. aria_code-4.1.3.dist-info/entry_points.txt +2 -0
  132. aria_code-4.1.3.dist-info/licenses/LICENSE +121 -0
  133. aria_code-4.1.3.dist-info/top_level.txt +50 -0
  134. aria_daemon.py +1295 -0
  135. aria_feishu_bot.py +1359 -0
  136. aria_relay_client.py +182 -0
  137. aria_relay_server.py +405 -0
  138. aria_telegram_bot.py +202 -0
  139. ariarc.py +328 -0
  140. artifacts.py +491 -0
  141. backtest_report.py +472 -0
  142. brokers/__init__.py +72 -0
  143. brokers/base.py +207 -0
  144. brokers/capabilities.py +264 -0
  145. brokers/cn/__init__.py +10 -0
  146. brokers/cn/easytrader_broker.py +193 -0
  147. brokers/cn/futu_broker.py +194 -0
  148. brokers/cn/longbridge_broker.py +190 -0
  149. brokers/cn/tiger_broker.py +196 -0
  150. brokers/cn/xtquant_broker.py +175 -0
  151. brokers/config.py +364 -0
  152. brokers/intl/__init__.py +5 -0
  153. brokers/intl/alpaca_broker.py +183 -0
  154. brokers/intl/ibkr_broker.py +215 -0
  155. brokers/intl/webull_broker.py +156 -0
  156. brokers/paper_broker.py +259 -0
  157. brokers/planning.py +296 -0
  158. brokers/registry.py +181 -0
  159. brokers/trading.py +237 -0
  160. change_store.py +127 -0
  161. command_safety.py +19 -0
  162. computer_use_tools.py +504 -0
  163. dashboard_generator.py +578 -0
  164. data_analysis_tools.py +808 -0
  165. data_cleaner.py +483 -0
  166. data_service.py +481 -0
  167. datasources/__init__.py +23 -0
  168. datasources/base.py +166 -0
  169. datasources/router.py +221 -0
  170. datasources/sources/__init__.py +15 -0
  171. datasources/sources/akshare_source.py +269 -0
  172. datasources/sources/alpha_vantage_source.py +202 -0
  173. datasources/sources/edgar_source.py +218 -0
  174. datasources/sources/finnhub_source.py +197 -0
  175. datasources/sources/fred_source.py +219 -0
  176. datasources/sources/tushare_source.py +141 -0
  177. datasources/sources/web_scraper_source.py +278 -0
  178. datasources/sources/world_bank_source.py +205 -0
  179. datasources/sources/yfinance_source.py +152 -0
  180. demo_player.py +204 -0
  181. doctor.py +508 -0
  182. file_analysis_tools.py +734 -0
  183. finance_formulas.py +389 -0
  184. football_data_client.py +1670 -0
  185. intent_classifier.py +358 -0
  186. local_finance_tools.py +3221 -0
  187. local_llm_provider.py +552 -0
  188. macro_tools.py +368 -0
  189. market_data_client.py +1899 -0
  190. mcp_client.py +506 -0
  191. memory_manager.py +245 -0
  192. model_capability.py +416 -0
  193. notification_tools.py +248 -0
  194. packages/__init__.py +23 -0
  195. packages/aria_agents/__init__.py +5 -0
  196. packages/aria_agents/manifest.py +69 -0
  197. packages/aria_core/__init__.py +34 -0
  198. packages/aria_core/architecture.py +192 -0
  199. packages/aria_core/export.py +124 -0
  200. packages/aria_core/manifest.py +65 -0
  201. packages/aria_infra/__init__.py +15 -0
  202. packages/aria_infra/arthera.py +52 -0
  203. packages/aria_infra/doctor.py +246 -0
  204. packages/aria_infra/product.py +37 -0
  205. packages/aria_mcp/__init__.py +25 -0
  206. packages/aria_mcp/bridge.py +38 -0
  207. packages/aria_mcp/config.py +97 -0
  208. packages/aria_mcp/tools.py +61 -0
  209. packages/aria_sdk/__init__.py +19 -0
  210. packages/aria_sdk/client.py +396 -0
  211. packages/aria_sdk/providers.py +70 -0
  212. packages/aria_sdk/streaming.py +73 -0
  213. packages/aria_sdk/types.py +86 -0
  214. packages/aria_services/__init__.py +55 -0
  215. packages/aria_services/context.py +258 -0
  216. packages/aria_services/data.py +11 -0
  217. packages/aria_services/provider_health.py +189 -0
  218. packages/aria_services/registry.py +213 -0
  219. packages/aria_services/usage.py +138 -0
  220. packages/aria_skills/__init__.py +5 -0
  221. packages/aria_skills/registry.py +59 -0
  222. packages/aria_tools/__init__.py +5 -0
  223. packages/aria_tools/registry.py +128 -0
  224. packages/quant_engine/__init__.py +6 -0
  225. packages/quant_engine/sports/__init__.py +72 -0
  226. packages/quant_engine/sports/calibrator.py +353 -0
  227. packages/quant_engine/sports/dixon_coles.py +234 -0
  228. packages/quant_engine/sports/elo.py +299 -0
  229. packages/quant_engine/sports/form.py +188 -0
  230. packages/quant_engine/sports/h2h.py +195 -0
  231. packages/quant_engine/sports/ml_model.py +354 -0
  232. packages/quant_engine/sports/predictor.py +311 -0
  233. packages/quant_engine/sports/tracker.py +664 -0
  234. packages/quant_engine/stochastic/__init__.py +27 -0
  235. packages/quant_engine/stochastic/gbm_enhanced.py +195 -0
  236. packages/quant_engine/stochastic/ito_calculus.py +477 -0
  237. packages/quant_engine/stochastic/kelly_criterion.py +181 -0
  238. packages/quant_engine/stochastic/monte_carlo_advanced.py +95 -0
  239. packages/quant_engine/stochastic/options_pricing.py +573 -0
  240. packages/quant_engine/stochastic/stochastic_processes.py +90 -0
  241. plan_utils.py +194 -0
  242. plugin_loader.py +328 -0
  243. portfolio_ledger.py +262 -0
  244. privacy/__init__.py +5 -0
  245. privacy/feedback.py +123 -0
  246. project_tools.py +525 -0
  247. providers/__init__.py +30 -0
  248. providers/llm/__init__.py +19 -0
  249. providers/llm/anthropic.py +184 -0
  250. providers/llm/base.py +139 -0
  251. providers/llm/ollama.py +128 -0
  252. providers/llm/openai_compat.py +282 -0
  253. providers/llm/registry.py +358 -0
  254. realty_data_tools.py +659 -0
  255. report_generator.py +1314 -0
  256. runtime/__init__.py +103 -0
  257. runtime/agent_loop.py +1183 -0
  258. runtime/approval.py +51 -0
  259. runtime/events.py +102 -0
  260. runtime/gateway.py +128 -0
  261. runtime/lsp.py +346 -0
  262. runtime/subagent.py +258 -0
  263. runtime/tool_executor.py +104 -0
  264. runtime/tool_policy.py +106 -0
  265. safety/__init__.py +21 -0
  266. safety/permissions.py +275 -0
  267. setup_wizard.py +653 -0
  268. strategy_vault.py +420 -0
  269. ui/__init__.py +100 -0
  270. ui/banner.py +310 -0
  271. ui/completer.py +391 -0
  272. ui/console.py +271 -0
  273. ui/image_render.py +243 -0
  274. ui/input_box.py +376 -0
  275. ui/picker.py +195 -0
  276. ui/render/__init__.py +11 -0
  277. ui/render/finance.py +1480 -0
  278. ui/render/market.py +225 -0
  279. ui/render/output.py +681 -0
  280. ui/render/team.py +346 -0
  281. ui/robot.py +235 -0
  282. workspace/__init__.py +6 -0
  283. workspace/files.py +170 -0
  284. workspace/verify.py +113 -0
brokers/base.py ADDED
@@ -0,0 +1,207 @@
1
+ """
2
+ brokers/base.py — 券商接入统一接口
3
+ ====================================
4
+ 所有券商适配器继承 BrokerBase,对外暴露统一 schema,
5
+ 上层代码不关心底层是 XTQuant / IBKR / Alpaca / 富途 / 老虎。
6
+
7
+ 数据类:
8
+ AccountInfo 账户资金汇总
9
+ Position 持仓明细
10
+ Order 订单记录
11
+ OrderResult 下单结果
12
+ PortfolioSummary 持仓组合摘要
13
+
14
+ 抽象接口:
15
+ connect() 建立连接
16
+ disconnect() 断开连接
17
+ account_info() 账户资金
18
+ positions() 当前持仓
19
+ orders(status) 历史/活跃订单
20
+ place_order(...) 下单(需用户二次确认,不在此层执行)
21
+ cancel_order(id) 撤单
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ from abc import ABC, abstractmethod
27
+ from dataclasses import dataclass, field
28
+ from typing import Any, Dict, List, Optional
29
+
30
+
31
+ # ── 统一数据 Schema ────────────────────────────────────────────────────────────
32
+
33
+ @dataclass
34
+ class AccountInfo:
35
+ broker_id: str # 配置中的 id 字段
36
+ broker_type: str # xtquant / ibkr / alpaca …
37
+ label: str # 用户自定义别名
38
+ account_id: str # 账户号(脱敏后展示末4位)
39
+ currency: str = "CNY"
40
+ total_assets: float = 0.0 # 总资产
41
+ cash: float = 0.0 # 可用现金
42
+ market_value: float = 0.0 # 持仓市值
43
+ frozen: float = 0.0 # 冻结资金
44
+ pnl_today: float = 0.0 # 当日盈亏
45
+ pnl_total: float = 0.0 # 累计盈亏
46
+ pnl_pct: float = 0.0 # 持仓收益率(%)
47
+ risk_level: str = "" # low / medium / high
48
+ extra: Dict[str, Any] = field(default_factory=dict)
49
+
50
+ @property
51
+ def masked_account(self) -> str:
52
+ return f"****{self.account_id[-4:]}" if len(self.account_id) > 4 else "****"
53
+
54
+
55
+ @dataclass
56
+ class Position:
57
+ symbol: str
58
+ name: str = ""
59
+ quantity: float = 0.0 # 持仓数量(股/手)
60
+ available_qty: float = 0.0 # 可卖数量
61
+ cost_price: float = 0.0 # 持仓均价
62
+ current_price: float = 0.0 # 最新价
63
+ market_value: float = 0.0 # 市值
64
+ pnl: float = 0.0 # 盈亏金额
65
+ pnl_pct: float = 0.0 # 盈亏比例(%)
66
+ currency: str = "CNY"
67
+ market: str = "" # a_share / us / hk / crypto
68
+ extra: Dict[str, Any] = field(default_factory=dict)
69
+
70
+
71
+ @dataclass
72
+ class Order:
73
+ order_id: str
74
+ symbol: str
75
+ name: str = ""
76
+ side: str = "" # buy / sell
77
+ order_type: str = "" # limit / market / stop
78
+ quantity: float = 0.0
79
+ filled_qty: float = 0.0
80
+ price: float = 0.0 # 委托价
81
+ avg_price: float = 0.0 # 成交均价
82
+ status: str = "" # open / filled / cancelled / partial
83
+ created_at: str = ""
84
+ updated_at: str = ""
85
+ currency: str = "CNY"
86
+ extra: Dict[str, Any] = field(default_factory=dict)
87
+
88
+
89
+ @dataclass
90
+ class OrderResult:
91
+ success: bool
92
+ order_id: str = ""
93
+ message: str = ""
94
+ broker_id: str = ""
95
+
96
+
97
+ @dataclass
98
+ class PortfolioSummary:
99
+ broker_id: str
100
+ positions: List[Position] = field(default_factory=list)
101
+ total_assets: float = 0.0
102
+ total_market_value: float = 0.0
103
+ cash: float = 0.0
104
+ total_pnl: float = 0.0
105
+ total_pnl_pct: float = 0.0
106
+ top_holding: Optional[Position] = None
107
+ currency: str = "CNY"
108
+
109
+
110
+ # ── 抽象基类 ───────────────────────────────────────────────────────────────────
111
+
112
+ class BrokerBase(ABC):
113
+ """所有券商适配器的抽象基类。"""
114
+
115
+ broker_type: str = "base" # 子类覆盖: xtquant / ibkr / alpaca …
116
+ broker_name: str = "券商" # 展示名称
117
+ market: str = "CN" # CN / US / HK / GLOBAL
118
+ read_only: bool = False # True = 只支持查询,不支持下单
119
+
120
+ def __init__(self, broker_id: str, config: Dict[str, Any]):
121
+ self.broker_id = broker_id
122
+ self.config = config
123
+ self.label = config.get("label", broker_id)
124
+ self._connected = False
125
+
126
+ # ── 连接管理 ──────────────────────────────────────────────────────────────
127
+
128
+ @abstractmethod
129
+ def connect(self) -> bool:
130
+ """建立连接。返回 True 表示成功。"""
131
+
132
+ def disconnect(self) -> None:
133
+ """断开连接(默认空实现,子类可覆盖)。"""
134
+ self._connected = False
135
+
136
+ @property
137
+ def is_connected(self) -> bool:
138
+ return self._connected
139
+
140
+ # ── 账户查询 ──────────────────────────────────────────────────────────────
141
+
142
+ @abstractmethod
143
+ def account_info(self) -> AccountInfo:
144
+ """返回账户资金汇总。"""
145
+
146
+ @abstractmethod
147
+ def positions(self) -> List[Position]:
148
+ """返回当前持仓列表。"""
149
+
150
+ @abstractmethod
151
+ def orders(self, status: str = "all", limit: int = 50) -> List[Order]:
152
+ """
153
+ 返回订单列表。
154
+ status: "open" | "filled" | "cancelled" | "all"
155
+ """
156
+
157
+ # ── 交易操作(需上层二次确认后才调用)──────────────────────────────────────
158
+
159
+ def place_order(
160
+ self,
161
+ symbol: str,
162
+ side: str, # "buy" | "sell"
163
+ quantity: float,
164
+ order_type: str = "limit",
165
+ price: float = 0.0,
166
+ **kwargs: Any,
167
+ ) -> OrderResult:
168
+ """
169
+ 下单接口。默认返回不支持,子类覆盖。
170
+ ⚠️ 此方法只应在用户明确确认后被调用。
171
+ """
172
+ if self.read_only:
173
+ return OrderResult(success=False, message=f"{self.broker_name} 为只读模式,不支持下单")
174
+ return OrderResult(success=False, message="此券商暂不支持程序化下单")
175
+
176
+ def cancel_order(self, order_id: str) -> bool:
177
+ """撤单。返回 True 表示成功。"""
178
+ return False
179
+
180
+ # ── 工具方法 ─────────────────────────────────────────────────────────────
181
+
182
+ def portfolio_summary(self) -> PortfolioSummary:
183
+ """构建持仓摘要(默认实现,子类可覆盖)。"""
184
+ try:
185
+ acct = self.account_info()
186
+ pos = self.positions()
187
+ top = max(pos, key=lambda p: abs(p.market_value), default=None) if pos else None
188
+ total_pnl = sum(p.pnl for p in pos)
189
+ total_mv = sum(p.market_value for p in pos)
190
+ pnl_pct = (total_pnl / (total_mv - total_pnl) * 100) if (total_mv - total_pnl) > 0 else 0.0
191
+ return PortfolioSummary(
192
+ broker_id=self.broker_id,
193
+ positions=pos,
194
+ total_assets=acct.total_assets,
195
+ total_market_value=acct.market_value,
196
+ cash=acct.cash,
197
+ total_pnl=total_pnl,
198
+ total_pnl_pct=pnl_pct,
199
+ top_holding=top,
200
+ currency=acct.currency,
201
+ )
202
+ except Exception as e:
203
+ return PortfolioSummary(broker_id=self.broker_id)
204
+
205
+ def __repr__(self) -> str:
206
+ status = "connected" if self._connected else "disconnected"
207
+ return f"<{self.__class__.__name__} id={self.broker_id!r} {status}>"
@@ -0,0 +1,264 @@
1
+ """Broker capability catalog and setup playbooks.
2
+
3
+ This module is intentionally UI-free so CLI, daemon, docs, and tests can share
4
+ one source of truth for supported broker connectors.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from dataclasses import dataclass
10
+ from importlib.util import find_spec
11
+ from typing import Iterable
12
+
13
+
14
+ @dataclass(frozen=True)
15
+ class BrokerCapability:
16
+ broker_type: str
17
+ display_name: str
18
+ markets: tuple[str, ...]
19
+ sdk_module: str
20
+ pip_package: str
21
+ credential_fields: tuple[str, ...]
22
+ local_runtime: str
23
+ read_capabilities: tuple[str, ...]
24
+ trade_capability: str
25
+ safety_notes: tuple[str, ...]
26
+ setup_steps: tuple[str, ...]
27
+
28
+ @property
29
+ def can_trade(self) -> bool:
30
+ return self.trade_capability not in ("read_only", "unsupported")
31
+
32
+
33
+ _BROKER_CAPABILITIES: tuple[BrokerCapability, ...] = (
34
+ BrokerCapability(
35
+ broker_type="paper",
36
+ display_name="Aria 本地仿盘账户",
37
+ markets=("美股", "港股", "A股", "加密货币"),
38
+ sdk_module="json",
39
+ pip_package="builtin",
40
+ credential_fields=("starting_cash", "currency"),
41
+ local_runtime="不需要真实券商或本地网关;订单写入本地 paper ledger。",
42
+ read_capabilities=("account", "positions", "orders"),
43
+ trade_capability="paper_trade",
44
+ safety_notes=("仿盘成交不触达真实市场。", "仍使用同一套风控、预览、确认和审计流程。"),
45
+ setup_steps=(
46
+ "运行 /paper start 100000 USD 创建本地仿盘账户。",
47
+ "运行 /broker connect paper_main 连接仿盘账户。",
48
+ "使用 /trade preview AAPL buy 10 190 生成订单预览,再 /trade confirm <preview_id>。",
49
+ ),
50
+ ),
51
+ BrokerCapability(
52
+ broker_type="xtquant",
53
+ display_name="迅投 XTQuant / QMT",
54
+ markets=("A股",),
55
+ sdk_module="xtquant",
56
+ pip_package="xtquant",
57
+ credential_fields=("account_id",),
58
+ local_runtime="QMT 量化终端需已登录并保持运行;主要面向 Windows。",
59
+ read_capabilities=("account", "positions", "orders"),
60
+ trade_capability="live_trade",
61
+ safety_notes=("实盘交易必须由 /broker_order 二次确认后执行。", "依赖包通常由券商提供,pip 可能不可直接安装。"),
62
+ setup_steps=(
63
+ "安装并登录券商 QMT/XTQuant 客户端。",
64
+ "运行 /broker add xtquant 填写 account_id。",
65
+ "运行 /broker connect <id>,再用 /account 与 /positions 验证。",
66
+ ),
67
+ ),
68
+ BrokerCapability(
69
+ broker_type="easytrader",
70
+ display_name="EasyTrader",
71
+ markets=("A股",),
72
+ sdk_module="easytrader",
73
+ pip_package="easytrader",
74
+ credential_fields=("broker_name", "exe_path"),
75
+ local_runtime="券商桌面交易客户端需已登录;主要面向 Windows。",
76
+ read_capabilities=("account", "positions", "orders"),
77
+ trade_capability="live_trade",
78
+ safety_notes=("属于客户端自动化路径,稳定性取决于券商客户端界面。",),
79
+ setup_steps=(
80
+ "安装并登录同花顺/通达信/华泰/国君等交易客户端。",
81
+ "运行 /broker add easytrader 填写 broker_name 与 exe_path。",
82
+ "先用 /account 只读校验,再启用交易。",
83
+ ),
84
+ ),
85
+ BrokerCapability(
86
+ broker_type="futu",
87
+ display_name="富途牛牛 OpenAPI",
88
+ markets=("港股", "美股", "A股"),
89
+ sdk_module="futu",
90
+ pip_package="futu-api",
91
+ credential_fields=("host", "port", "market"),
92
+ local_runtime="FutuOpenD 需在本机或局域网运行,默认 127.0.0.1:11111。",
93
+ read_capabilities=("account", "positions", "orders"),
94
+ trade_capability="live_trade",
95
+ safety_notes=("OpenD 的交易解锁、市场权限和账户类型会影响可用能力。",),
96
+ setup_steps=(
97
+ "安装 futu-api 并启动 FutuOpenD。",
98
+ "运行 /broker add futu 填写 host、port、market。",
99
+ "运行 /broker connect <id> 后检查 /positions 与 /orders。",
100
+ ),
101
+ ),
102
+ BrokerCapability(
103
+ broker_type="tiger",
104
+ display_name="老虎证券 OpenAPI",
105
+ markets=("美股", "港股", "A股"),
106
+ sdk_module="tigeropen",
107
+ pip_package="tigeropen",
108
+ credential_fields=("tiger_id", "private_key_path", "account"),
109
+ local_runtime="不需要本地网关,但需要开发者账号、账户号和 RSA 私钥。",
110
+ read_capabilities=("account", "positions", "orders"),
111
+ trade_capability="live_trade",
112
+ safety_notes=("私钥文件必须保存在本机,不要写入对话或日志。",),
113
+ setup_steps=(
114
+ "在老虎开放平台注册应用并保存 RSA 私钥。",
115
+ "运行 /broker add tiger 填写 tiger_id、account、private_key_path。",
116
+ "用 /broker doctor 检查依赖与字段,再 /broker connect <id>。",
117
+ ),
118
+ ),
119
+ BrokerCapability(
120
+ broker_type="longbridge",
121
+ display_name="长桥证券 OpenAPI",
122
+ markets=("港股", "美股", "A股"),
123
+ sdk_module="longbridge",
124
+ pip_package="longbridge",
125
+ credential_fields=("app_key", "app_secret", "access_token"),
126
+ local_runtime="不需要本地网关;需要长桥 OpenAPI App Key/Secret/Token。",
127
+ read_capabilities=("account", "positions", "orders"),
128
+ trade_capability="live_trade",
129
+ safety_notes=("Token 建议只保存在 brokers.json 或环境变量,不进入提示词。",),
130
+ setup_steps=(
131
+ "在长桥 OpenAPI 开发者中心创建应用。",
132
+ "运行 /broker add longbridge 填写 App Key、Secret、Access Token。",
133
+ "连接后使用 /account、/positions 校验账户数据。",
134
+ ),
135
+ ),
136
+ BrokerCapability(
137
+ broker_type="ibkr",
138
+ display_name="Interactive Brokers",
139
+ markets=("全球市场",),
140
+ sdk_module="ib_insync",
141
+ pip_package="ib_insync",
142
+ credential_fields=("host", "port", "client_id"),
143
+ local_runtime="TWS 或 IB Gateway 需已登录并开启 Socket API。",
144
+ read_capabilities=("account", "positions", "orders"),
145
+ trade_capability="live_trade",
146
+ safety_notes=("模拟端口通常为 7497/4002,实盘端口通常为 7496/4001。",),
147
+ setup_steps=(
148
+ "启动 TWS 或 IB Gateway,并启用 API Socket。",
149
+ "运行 /broker add ibkr 填写 host、port、client_id。",
150
+ "建议先连模拟盘,再切实盘端口。",
151
+ ),
152
+ ),
153
+ BrokerCapability(
154
+ broker_type="alpaca",
155
+ display_name="Alpaca Markets",
156
+ markets=("美股", "加密货币"),
157
+ sdk_module="alpaca",
158
+ pip_package="alpaca-py",
159
+ credential_fields=("api_key", "api_secret", "paper"),
160
+ local_runtime="不需要本地网关;支持 paper=true 模拟盘。",
161
+ read_capabilities=("account", "positions", "orders"),
162
+ trade_capability="paper_or_live_trade",
163
+ safety_notes=("默认建议使用 paper=true;实盘需确认账户权限。",),
164
+ setup_steps=(
165
+ "在 Alpaca 控制台生成 API Key/Secret。",
166
+ "运行 /broker add alpaca,优先选择 paper=true。",
167
+ "用 /broker connect <id> 与 /account 验证模拟盘。",
168
+ ),
169
+ ),
170
+ BrokerCapability(
171
+ broker_type="webull",
172
+ display_name="Webull",
173
+ markets=("美股",),
174
+ sdk_module="webull",
175
+ pip_package="webull",
176
+ credential_fields=("username", "password", "device_id"),
177
+ local_runtime="不需要本地网关;当前适配器默认只读。",
178
+ read_capabilities=("account", "positions", "orders"),
179
+ trade_capability="read_only",
180
+ safety_notes=("非官方 API,默认不开放程序化下单。",),
181
+ setup_steps=(
182
+ "安装 webull SDK 并准备登录凭证。",
183
+ "运行 /broker add webull。",
184
+ "仅用于 /account、/positions、/orders 只读查询。",
185
+ ),
186
+ ),
187
+ )
188
+
189
+
190
+ def list_broker_capabilities() -> tuple[BrokerCapability, ...]:
191
+ return _BROKER_CAPABILITIES
192
+
193
+
194
+ def get_broker_capability(broker_type: str) -> BrokerCapability | None:
195
+ normalized = str(broker_type or "").strip().lower()
196
+ for spec in _BROKER_CAPABILITIES:
197
+ if spec.broker_type == normalized:
198
+ return spec
199
+ return None
200
+
201
+
202
+ def broker_dependency_state(spec: BrokerCapability) -> dict[str, object]:
203
+ installed = bool(find_spec(spec.sdk_module))
204
+ return {
205
+ "broker_type": spec.broker_type,
206
+ "module": spec.sdk_module,
207
+ "package": spec.pip_package,
208
+ "installed": installed,
209
+ "install_hint": f"/install {spec.pip_package}" if not installed else "",
210
+ }
211
+
212
+
213
+ def broker_connection_plan(broker_type: str) -> tuple[str, ...]:
214
+ spec = get_broker_capability(broker_type)
215
+ if not spec:
216
+ return (
217
+ "运行 /broker guide 查看支持的券商类型。",
218
+ "选择券商后运行 /broker add <type>。",
219
+ )
220
+ dep = broker_dependency_state(spec)
221
+ first = (
222
+ f"依赖已安装: {spec.sdk_module}"
223
+ if dep["installed"]
224
+ else f"安装依赖: /install {spec.pip_package}"
225
+ )
226
+ return (first, *spec.setup_steps, "常用服务: /account, /positions, /orders, /risk, /report")
227
+
228
+
229
+ def broker_service_playbook() -> tuple[dict[str, str], ...]:
230
+ """Return user-facing broker-to-service usage flows."""
231
+
232
+ return (
233
+ {
234
+ "service": "账户与持仓",
235
+ "commands": "/broker connect <id> → /account → /positions → /orders",
236
+ "used_by": "LLM broker_query 工具、持仓风险分析、组合报告",
237
+ "guardrail": "只读查询,不触发交易",
238
+ },
239
+ {
240
+ "service": "策略到交易计划",
241
+ "commands": "/backtest → /signal → broker_order",
242
+ "used_by": "QuantEngine 信号、回测结果、Kelly/仓位测算",
243
+ "guardrail": "下单前必须展示订单计划并等待用户确认",
244
+ },
245
+ {
246
+ "service": "TradingView 告警联动",
247
+ "commands": "TradingView alert → Aria daemon webhook → 分析/通知/订单草案",
248
+ "used_by": "/tv、/daemon、webhook、通知渠道",
249
+ "guardrail": "Webhook 默认只生成分析和订单草案,不自动实盘下单",
250
+ },
251
+ {
252
+ "service": "报告与审计",
253
+ "commands": "/report, /dashboard, /export",
254
+ "used_by": "账户快照、持仓、行情、信号、风控、交易计划",
255
+ "guardrail": "报告落本地 artifacts;敏感字段脱敏",
256
+ },
257
+ )
258
+
259
+
260
+ def filter_capabilities(names: Iterable[str]) -> tuple[BrokerCapability, ...]:
261
+ wanted = {str(name).strip().lower() for name in names if str(name).strip()}
262
+ if not wanted:
263
+ return list_broker_capabilities()
264
+ return tuple(spec for spec in _BROKER_CAPABILITIES if spec.broker_type in wanted)
brokers/cn/__init__.py ADDED
@@ -0,0 +1,10 @@
1
+ from .xtquant_broker import XTQuantBroker
2
+ from .easytrader_broker import EasyTraderBroker
3
+ from .futu_broker import FutuBroker
4
+ from .tiger_broker import TigerBroker
5
+ from .longbridge_broker import LongbridgeBroker
6
+
7
+ __all__ = [
8
+ "XTQuantBroker", "EasyTraderBroker",
9
+ "FutuBroker", "TigerBroker", "LongbridgeBroker",
10
+ ]
@@ -0,0 +1,193 @@
1
+ """
2
+ brokers/cn/easytrader_broker.py — EasyTrader 适配器
3
+ =====================================================
4
+ 支持券商(通过客户端界面自动化):
5
+ 同花顺(ths)、通达信(tdx)、华泰(huatai)、国泰君安(guojun)、
6
+ 银河证券(yh)、平安证券(pa)、招商证券(zszq)、雪球模拟(xq)
7
+
8
+ 安装:pip install easytrader
9
+ 文档:https://github.com/shidenggui/easytrader
10
+
11
+ 配置示例::
12
+
13
+ {
14
+ "id": "ht_main",
15
+ "type": "easytrader",
16
+ "label": "华泰账户",
17
+ "broker_name": "huatai",
18
+ "exe_path": "C:\\华泰证券\\xiadan.exe",
19
+ "comm_password": "可选"
20
+ }
21
+
22
+ 注意:EasyTrader 通过操控客户端窗口,仅支持 Windows。
23
+ """
24
+
25
+ from __future__ import annotations
26
+
27
+ from typing import Any, Dict, List
28
+
29
+ from ..base import BrokerBase, AccountInfo, Position, Order, OrderResult
30
+
31
+
32
+ class EasyTraderBroker(BrokerBase):
33
+ broker_type = "easytrader"
34
+ broker_name = "EasyTrader"
35
+ market = "CN"
36
+
37
+ _BROKER_NAMES = {
38
+ "huatai": "华泰证券",
39
+ "guojun": "国泰君安",
40
+ "ths": "同花顺",
41
+ "tdx": "通达信",
42
+ "yh": "银河证券",
43
+ "zszq": "招商证券",
44
+ "xq": "雪球模拟",
45
+ }
46
+
47
+ def __init__(self, broker_id: str, config: Dict[str, Any]):
48
+ super().__init__(broker_id, config)
49
+ self._broker_name = config.get("broker_name", "ths")
50
+ self._exe_path = config.get("exe_path", "")
51
+ self._comm_pwd = config.get("comm_password", "")
52
+ self._client = None
53
+ self.broker_name = self._BROKER_NAMES.get(self._broker_name, self._broker_name)
54
+
55
+ def connect(self) -> bool:
56
+ try:
57
+ import easytrader
58
+ except ImportError:
59
+ raise ImportError("easytrader 未安装。请运行: pip install easytrader")
60
+
61
+ import sys
62
+ if sys.platform != "win32":
63
+ raise RuntimeError("EasyTrader 仅支持 Windows 系统")
64
+
65
+ try:
66
+ client = easytrader.use(self._broker_name)
67
+ if self._exe_path:
68
+ client.prepare(self._exe_path, comm_password=self._comm_pwd or None)
69
+ else:
70
+ client.prepare(comm_password=self._comm_pwd or None)
71
+ self._client = client
72
+ self._connected = True
73
+ return True
74
+ except Exception as e:
75
+ raise RuntimeError(f"EasyTrader 连接失败: {e}") from e
76
+
77
+ def account_info(self) -> AccountInfo:
78
+ self._require_connected()
79
+ try:
80
+ bal = self._client.balance
81
+ total = float(bal.get("总资产", bal.get("资产", 0)))
82
+ cash = float(bal.get("可用金额", bal.get("可用资金", 0)))
83
+ mv = float(bal.get("证券市值", 0))
84
+ frozen= float(bal.get("冻结金额", 0))
85
+ pnl = float(bal.get("盈亏金额", bal.get("参考盈亏", 0)))
86
+ except Exception as e:
87
+ raise RuntimeError(f"EasyTrader 账户资金查询失败: {e}") from e
88
+
89
+ return AccountInfo(
90
+ broker_id=self.broker_id,
91
+ broker_type=self.broker_type,
92
+ label=self.label,
93
+ account_id=self.config.get("account_id", ""),
94
+ currency="CNY",
95
+ total_assets=total,
96
+ cash=cash,
97
+ market_value=mv,
98
+ frozen=frozen,
99
+ pnl_today=pnl,
100
+ )
101
+
102
+ def positions(self) -> List[Position]:
103
+ self._require_connected()
104
+ try:
105
+ raw = self._client.position
106
+ except Exception as e:
107
+ raise RuntimeError(f"EasyTrader 持仓查询失败: {e}") from e
108
+
109
+ result = []
110
+ for row in (raw or []):
111
+ sym = str(row.get("证券代码", row.get("股票代码", "")))
112
+ name = str(row.get("证券名称", row.get("股票名称", "")))
113
+ qty = float(row.get("持仓数量", row.get("持股数量", 0)))
114
+ avail = float(row.get("可用数量", row.get("可卖数量", 0)))
115
+ cost = float(row.get("成本价", row.get("持仓成本", 0)))
116
+ price = float(row.get("当前价", row.get("最新价", 0)))
117
+ mv = float(row.get("证券市值", row.get("参考市值", 0)))
118
+ pnl = float(row.get("盈亏金额", row.get("参考盈亏", 0)))
119
+ pnl_pct = float(row.get("盈亏比例(%)", 0))
120
+ result.append(Position(
121
+ symbol=sym, name=name,
122
+ quantity=qty, available_qty=avail,
123
+ cost_price=cost, current_price=price,
124
+ market_value=mv, pnl=pnl, pnl_pct=pnl_pct,
125
+ currency="CNY", market="a_share",
126
+ ))
127
+ return result
128
+
129
+ def orders(self, status: str = "all", limit: int = 50) -> List[Order]:
130
+ self._require_connected()
131
+ try:
132
+ raw = self._client.today_entrusts
133
+ except Exception as e:
134
+ raise RuntimeError(f"EasyTrader 订单查询失败: {e}") from e
135
+
136
+ result = []
137
+ for row in (raw or [])[:limit]:
138
+ op = str(row.get("操作", ""))
139
+ side = "buy" if "买" in op else "sell"
140
+ raw_st = str(row.get("状态", row.get("委托状态", "")))
141
+ mapped = _easy_order_status(raw_st)
142
+ if status != "all" and mapped != status:
143
+ continue
144
+ result.append(Order(
145
+ order_id=str(row.get("委托编号", row.get("合同编号", ""))),
146
+ symbol=str(row.get("证券代码", "")),
147
+ name=str(row.get("证券名称", "")),
148
+ side=side,
149
+ order_type="limit",
150
+ quantity=float(row.get("委托数量", 0)),
151
+ filled_qty=float(row.get("成交数量", 0)),
152
+ price=float(row.get("委托价格", row.get("委托价",0))),
153
+ avg_price=float(row.get("成交均价", row.get("成交价",0))),
154
+ status=mapped,
155
+ created_at=str(row.get("委托时间", "")),
156
+ currency="CNY",
157
+ ))
158
+ return result
159
+
160
+ def place_order(self, symbol: str, side: str, quantity: float,
161
+ order_type: str = "limit", price: float = 0.0, **kwargs) -> OrderResult:
162
+ self._require_connected()
163
+ try:
164
+ if side == "buy":
165
+ result = self._client.buy(security=symbol, price=price, amount=int(quantity))
166
+ else:
167
+ result = self._client.sell(security=symbol, price=price, amount=int(quantity))
168
+ oid = str(result.get("entrust_no", result.get("委托编号", "")))
169
+ return OrderResult(success=True, order_id=oid, broker_id=self.broker_id)
170
+ except Exception as e:
171
+ return OrderResult(success=False, message=str(e), broker_id=self.broker_id)
172
+
173
+ def cancel_order(self, order_id: str) -> bool:
174
+ self._require_connected()
175
+ try:
176
+ self._client.cancel_entrust(order_id)
177
+ return True
178
+ except Exception:
179
+ return False
180
+
181
+ def _require_connected(self):
182
+ if not self._connected or not self._client:
183
+ raise RuntimeError("EasyTrader 未连接,请先调用 connect()")
184
+
185
+
186
+ def _easy_order_status(raw: str) -> str:
187
+ if any(x in raw for x in ("已成", "全部成交")):
188
+ return "filled"
189
+ if any(x in raw for x in ("部分成交",)):
190
+ return "partial"
191
+ if any(x in raw for x in ("已撤", "撤单", "废单")):
192
+ return "cancelled"
193
+ return "open"