LLM 交易风控与安全¶
适用版本:axon-llm v0.2.0+ 前置阅读:overview.md §4
本文档详述 axon-llm 的三道风控防线 + 失败模式 + 恢复策略。所有风控都是 fail-closed(任一阶段失败立刻拒绝,绝不进入后端)。
1. SafetyMode: DryRun / TwoPhase / Direct¶
1.1 语义¶
SafetyMode 是下单前最外层拦截:
| 模式 | 行为 | 典型用途 |
|---|---|---|
DryRun | 不真下单,仅 tracing 日志,返回 OrderAck { status: "DryRun" } | LLM 决策验证 / 集成测试 |
TwoPhase | 两次确认:第一次返回 confirm_token(uuid v4),第二次带相同 token 才真发 | 高风险操作的人工 in-the-loop 审批 |
Direct | 直接调后端,无任何拦截 | 生产实盘(已通过其他风控) |
1.2 TwoPhase 详细流程¶
# 第一次调用
ack1 = place_order_tool.execute({
"symbol": "BTC-USDT",
"side": "Buy",
"quantity": 0.1,
"price": 50000.0,
"confirm_token": None,
})
# ack1: { "status": "PendingConfirm", "confirm_token": "uuid-xxx", "order_id": None }
# 第二次调用(必须带相同 token)
ack2 = place_order_tool.execute({
"symbol": "BTC-USDT",
"side": "Buy",
"quantity": 0.1,
"price": 50000.0,
"confirm_token": "uuid-xxx",
})
# ack2: { "status": "Filled", "order_id": "real-id", ... }
注意:TwoPhase 状态在进程内(PendingOrder 缓存),重启后 token 失效,需要重新走第一阶段。
1.3 选型建议¶
- 开发 / 联调 / CI 测试:
DryRun,绝不下单 - LLM agent 灰度上线:
TwoPhase,人工确认关键操作 - 生产实盘(已有多重外部审计):
Direct
2. RiskLimits: 静态规则¶
RiskLimits 是下单前的第二道防线,包含 4 个静态规则,任意一条失败立刻拒绝。
2.1 规则清单¶
| 规则 | 字段 | 检查逻辑 | 失败信息示例 |
|---|---|---|---|
| 单笔最大名义 | max_order_notional | quantity * price <= max_order_notional | "order notional 60000.0 exceeds limit 50000.0" |
| 单日最大订单数 | max_daily_orders | daily_count < max_daily_orders | "daily order count 101 exceeds limit 100" |
| 单 symbol 最大持仓绝对值 | max_position_abs | |current_qty + side_delta| <= max_position_abs | "projected position 0.6 exceeds max abs 0.5" |
| 允许的 symbol 白名单 | allowed_symbols | symbol ∈ allowed_symbols | "symbol 'DOGE-USDT' not in allowed list" |
2.2 规则组合示例¶
risk = RiskLimits(
max_order_notional=50_000.0, # 单笔 ≤ 5 万 USDT
max_daily_orders=100, # 每天 ≤ 100 单
max_position_abs=10.0, # 单 symbol ≤ 10 单位
allowed_symbols={"BTC-USDT", "ETH-USDT"}, # 只能交易这两个
)
2.3 max_position_abs 详解¶
计算公式:projected = current_position + side_delta,其中: - current_position:从 backend.get_positions() 查询的当前持仓 - side_delta:Buy → +quantity,Sell → -quantity - 失败条件:|projected| > max_position_abs
多场景示例:
初始:position = 0, max_abs = 0.5
├── Buy 0.3 -> projected = 0.3, |0.3| = 0.3 ≤ 0.5 ✅ 通过
├── Buy 0.3 -> projected = 0.6, |0.6| = 0.6 > 0.5 ❌ 拒绝
├── Sell 0.3 -> projected = 0, |0.0| = 0.0 ≤ 0.5 ✅ 通过
└── Sell 0.8 -> projected = -0.8, |-0.8| = 0.8 > 0.5 ❌ 拒绝
注意:max_position_abs 是 单 symbol 隔离 的,每个 symbol 独立计算。允许 BTC-USDT 持仓 10 + ETH-USDT 持仓 10,互不影响。
3. RiskGate: 动态闸门¶
RiskGate 是第三道防线,处理"运行中状态"(连续失败次数、日内 PnL 突破阈值等)。
3.1 内置实现¶
| 类型 | 触发逻辑 | 依赖 |
|---|---|---|
AlwaysOpenGate | 永远放行(默认) | 无 |
RejectionCircuitBreaker | 连续 N 次风控拒绝后开闸(冷却期后自动恢复) | 无(core lib 内置) |
RiskPnLCircuitBreaker | 日 PnL 突破阈值后开闸 | axon-risk(feature = trading-risk-extra) |
3.2 RejectionCircuitBreaker 详解¶
let gate = RejectionCircuitBreaker::new(
threshold: 5, // 连续 5 次风控拒绝
cooldown_ms: 60_000, // 冷却 60 秒
);
状态机:
┌──────────────────────────────────────┐
↓ │
[Closed] ──连续 N 次拒绝──> [Open] ──cooldown 结束──> [HalfOpen] ──一次成功──> [Closed]
↑ │
└────────────────失败(回到 Open)────────────┘
- Closed:正常放行
- Open:拒绝所有下单,返回
CircuitBreakerOpen错误 - HalfOpen:放行一次试单,成功则关闸,失败则重新进入 Open
3.3 RiskPnLCircuitBreaker 详解¶
与 RejectionCircuitBreaker 的区别:
| 维度 | RejectionCircuitBreaker | RiskPnLCircuitBreaker |
|---|---|---|
| 触发指标 | 连续风控拒绝次数 | 日 PnL 净值 |
| 适用场景 | LLM 决策出现异常重复拒绝 | 真实亏损触底保护 |
| 依赖 | 零(core lib) | axon-risk(feature gate) |
| 冷却 | 固定时间 | 跨日自动重置(UTC 0 点) |
4. 失败模式与恢复¶
4.1 失败分类¶
| 失败类型 | 失败位置 | 恢复策略 |
|---|---|---|
RiskLimitsViolation | RiskLimits::check | 修改 args,重新下单 |
CircuitBreakerOpen | RiskGate | 等待 cooldown / 半开试单成功 |
BackendError::Network | 后端 | 指数退避重试(应用方负责) |
BackendError::Rejected | 后端 | 修正 args,重新下单 |
BackendError::InsufficientFunds | 后端 | 减仓后再下单 |
BackendError::SymbolNotFound | 后端 | 检查 symbol 拼写 |
4.2 错误响应统一格式¶
所有 tool 的失败都通过 ToolError::ExecutionFailed(msg) 返回,msg 包含机器可读前缀 + 人类可读描述:
{
"error_type": "ExecutionFailed",
"source": "RiskLimits",
"message": "RiskLimits: order notional 60000.0 exceeds limit 50000.0"
}
{
"error_type": "ExecutionFailed",
"source": "RiskGate",
"message": "RiskGate: circuit breaker open (rejections=5, cooldown_remaining_ms=42137)"
}
LLM agent 可以根据 source 字段决定重试 / 询问用户 / 改参数。
5. 安全最佳实践¶
5.1 启用顺序¶
- 生产前必启用:
RiskLimits(基础规则) - 强烈推荐启用:
RejectionCircuitBreaker(防止 LLM 决策死循环) - 高敏场景启用:
TwoPhase(人工 in-the-loop 审批) - 可选启用:
RiskPnLCircuitBreaker(需trading-risk-extrafeature)
5.2 选型矩阵¶
| 场景 | SafetyMode | RiskLimits | RiskGate | TwoPhase |
|---|---|---|---|---|
| 单元测试 | DryRun | 关闭 | AlwaysOpen | 关 |
| 集成测试 | DryRun | 关闭 | AlwaysOpen | 关 |
| 回测评估 | Direct | 按需 | AlwaysOpen | 关 |
| 灰度 LLM agent | TwoPhase | 严 | RejectionCB | 开 |
| 生产实盘 | Direct | 严 | RejectionCB | 按需 |
| 高敏实盘 | TwoPhase | 严 | RiskPnLCB | 开 |
5.3 审计与日志¶
所有风控决策都会输出 tracing 日志:
INFO axon_llm::trading::tools::place_order: RiskLimits check passed order_id=ord-xxx
WARN axon_llm::trading::tools::place_order: RiskLimits rejected reason="notional exceeds" order_id=ord-yyy
ERROR axon_llm::trading::tools::place_order: RiskGate blocked reason="circuit breaker open" order_id=ord-zzz
应用方应把这些日志接入自己的 ELK / Loki / Datadog 等日志后端,作为合规审计的输入。