Copy Trade(跟单交易)基于 Smart Account(SA)+ TEE 架构,允许用户自动跟随目标钱包地址的链上交易行为,实现跟买、跟卖、止盈止损等自动化策略。本文涵盖前端业务架构与后端技术方案。
sequenceDiagram
participant User as 用户
participant API as Strategy API
participant Trigger as Trigger 引擎
participant Analysis as 链上解析
participant Trade as Trade 服务
participant Vault as Vault 签名
Note over User,Vault: 创建跟单策略
User->>API: 创建 Copy Trade Order
API->>Trigger: 事件:创建跟单索引
Note over User,Vault: 链上信号触发跟买
Analysis->>Trigger: 链上交易事件
Trigger->>Trigger: 匹配跟单策略索引
Trigger->>Trade: 构建跟买交易
Trade->>Vault: TEE 签名 + 广播
Note over User,Vault: 跟买结果处理
Analysis->>Trade: 链上事件匹配跟买 TxHash
Trade->>Trigger: 回调跟买结果
Trigger->>Trigger: 创建 TP/SL + 跟卖订单
Note over User,Vault: 价格触发止盈止损
Trigger->>Trade: 构建 TP/SL 交易
Trade->>Vault: TEE 签名 + 广播
Copy Trade 表单字段定义在 FormItemEnum 枚举中,按功能分组:
| 分组 | 字段 | 说明 |
|---|---|---|
| 基础信息 | strategyName, trackedWalletAddress | 策略名称、跟单地址(必填) |
| 购买类型 | buyType | percentage(2) / fixedAmount(1) |
| 百分比模式 | buyPercent, maxBuyAmount, onlyBuyOnce | 百分比(1-1000)、最大金额、仅买一次 |
| 固定金额模式 | buyAmount, onlyBuyOnce | 固定金额、仅买一次 |
| 卖出设置 | copySell, isAutoSell | 跟单卖出、自动卖出 |
| Token 过滤器 | threshold, minMc/maxMc, minLiquidity/maxLiquidity, minAge/maxAge, blackCoinList | 目标金额、市值、流动性、代币年龄、黑名单 |
| 其他配置 | expireTime/expireTimeType, serviceFee | 过期时间(4选1)、服务费(只读) |
graph TB
A["打开弹窗"] --> B{"判断模式"}
B -->|创建| C["createInit()<br/>Storage 读取预设<br/>并行请求 defaultConfig + tokenList<br/>初始化 AutoSell"]
B -->|编辑/再跟单| D["editInit()<br/>并行请求 config + tokenList + detail<br/>接口→表单转换<br/>获取代币余额"]
C --> E["生成初始表单"]
D --> E
E --> F["用户点击提交"]
F -->|创建| G["getParams → 钱包签名 → POST /create"]
F -->|编辑| H["getParams → 钱包签名 → POST /modify"]
F -->|再跟单| I["getParams → 钱包签名 → POST /create"]
G --> J["关闭弹窗<br/>reset Store<br/>保存 Storage"]
H --> J
I --> K["保存 Storage<br/>打开成功弹窗"]
stateDiagram-v2
[*] --> ACTIVE: 用户创建跟单策略
ACTIVE --> PAUSE: 用户暂停
ACTIVE --> DELETED: 用户删除
PAUSE --> ACTIVE: 用户恢复
PAUSE --> DELETED: 用户删除
stateDiagram-v2
[*] --> IN_TRADING: 跟单策略触发执行
IN_TRADING --> SUCCESS: 执行成功
IN_TRADING --> FAILED: 执行失败
stateDiagram-v2
[*] --> ACTIVE: 跟买成功后创建
ACTIVE --> IN_TRADING: 价格触发
ACTIVE --> CANCELED: 用户删除 / 停止策略 / TEE SA 失效
IN_TRADING --> SUCCESS: 执行成功
IN_TRADING --> ACTIVE: 执行失败(可重试)
IN_TRADING --> FAILED: 执行失败(不可重试)
TP/SL 的 Open Order 增加了 CANCELING 中间状态:
ACTIVE → CANCELING → CANCELED (用户删除/停止策略)
ACTIVE → CANCELED (TEE SA 失效,直接取消)
ACTIVE → TRADING → SUCCESS/FAILED| 指标 | 公式 |
|---|---|
| 净持仓 | max(0, 净持仓_before + 本次交易量) |
| 虚拟持仓 | min(钱包实际余额, 净持仓) |
| 平均成本 | (本次买入量 × 本次价格 + 净持仓_before × 旧均价) / (本次买入量 + 净持仓_before) |
| 未实现 PnL (Token) | 虚拟持仓 × (当前价格 - 平均成本) |
| 已实现 PnL (Trade) | min(卖出量, 净持仓_before) × (卖出价格 - 平均成本) |
graph BT
A["Trade 级别<br/>单笔交易 PnL"] --> B["Token 级别<br/>策略内单币种汇总"]
B --> C["Strategy 级别<br/>单策略所有币种汇总"]
C --> D["Copy Trade 级别<br/>用户所有策略汇总"]
每个层级都有对应的 PnL 比例计算:%PnL = $PnL / $total_cost
graph TB
A["卖出交易结果"] --> B["生成 Strategy Order 消息"]
B --> C["发送 Kafka<br/>Partition Key: ChainId + WalletAddress"]
C --> D["Consumer 消费"]
D --> E["计算已实现 PnL"]
E --> F["更新 Rollup 聚合表<br/>pnl_sum += 计算结果"]
graph TB
A["创建跟单策略事件"] --> B["请求 Quote Token 授权"]
B --> C{"授权成功?"}
C -->|是| D["激活跟单策略"]
C -->|否| E["创建失败流程"]
graph TB
A["跟买交易结果"] --> B["请求 Base Token 授权"]
B --> C{"授权成功?"}
C -->|是| D["创建 Token Position"]
D --> E["创建 Copy Sell 订单"]
E --> F["创建 TP/SL 订单"]
C -->|否| G["处理授权失败"]
采用 BSC 延迟 2 秒 方案:
| 场景 | Solana | BSC |
|---|---|---|
| 订单状态流转 | 实时处理 | 实时处理,回滚后糾正 |
| 仓位 & Auto Sell 订单创建 | 实时 | 延迟 2 秒 |
| PnL 计算 | 实时 | 延迟 2 秒 |
BSC 出块时间短,延迟约 2 秒基本不会发生回滚。
新的 JWT 鉴权方式无需端上存储全量订单,改为每次分页查询后端。由于跨区域部署(EU 策略服务 ↔ HK 请求),数据量超过 100KB 时有性能问题,需分页缩小查询量。
以每页 5 条为例:
步骤一:分别从 Swap 库和 Strategy 库各查 5 条
Swap: [1, 2, 3, 4, 5]
Strategy: [A, B, C, D, E]
步骤二:合并排序,取前 5 条
排序后: [1, 2, A, B, C, 3, 4, 5, D, E]
返回: [1, 2, A, B, C]
游标: Swap→2, Strategy→C
步骤三:端上携带游标查下一页
Swap 从 2 之后查 5 条
Strategy 从 C 之后查 5 条
重复步骤二边界情况处理:
| Case | 说明 | 游标策略 |
|---|---|---|
| 两源都有 | 结果集包含两个数据源 | 各取最后一条点位 |
| 只有源A(源B有数据) | 全部来自源A | 源A取最后一条,源B取第一条+1 |
| 只有源A(源B无数据) | 源B已耗尽 | 源A取最后一条,源B=null |
| 都无数据 | 无下一页 | — |
| 页面 | 表格 | 数据级别 | 分页 |
|---|---|---|---|
| Dashboard | OpenCopyTrade | 策略级别 | limit:10, 无分页 |
| Dashboard | CopyTradeHistory | 策略级别 | pageSize:10, 有分页 |
| Detail | OpenOrder | 订单级别 | pageSize:20, 有分页 |
| Detail | History | 订单级别 | pageSize:20, 有分页 |
| Detail | Filtered | 订单级别 | pageSize:20, 有分页 |
| Detail | Trades | 订单级别 | pageSize:20, 有分页 |
graph TB
subgraph 用户交互层
A["index.tsx 入口"] --> B["Table 组件"]
B --> C["列定义 Columns"]
end
subgraph 状态管理层
D["MobX Store"]
D1["Observable<br/>list / loading / cursor / hasNext"]
D2["Computed<br/>computedList / computedIsEmpty / computedStatus"]
D --> D1
D --> D2
end
subgraph 业务逻辑层
E["Action"]
E1["init() / fetch() / refresh()"]
E2["WebSocket Handlers"]
E --> E1
E --> E2
end
subgraph 数据层
F["dataProxy → API"]
G["WebSocket 推送"]
end
B --> D
D -.-> E
E --> F
G -.-> D
graph TB
A["WebSocket 推送<br/>eventType + data"] --> B{"事件类型"}
B -->|CREATE| C["获取订单详情 → list.unshift()"]
B -->|CHANGE| D{"订单状态"}
B -->|STATUS_CHANGE| E{"策略状态"}
B -->|TRIGGER| F["更新 PnL 数据"]
D -->|已完成| G["从 list 移除"]
D -->|状态更新| H["获取最新数据 → 更新 list 对应项"]
E -->|已结束| I["从 list 移除"]
E -->|状态变更| J["更新 useStatus"]
| 操作 | 级别 | API | 说明 |
|---|---|---|---|
| Stop | 策略 | POST /updateCopyTradeStatus (useStatus=2) | 终止策略 |
| Pause | 策略 | POST /updateCopyTradeStatus (useStatus=0) | 暂停策略 |
| Restart | 策略 | POST /updateCopyTradeStatus (useStatus=1) | 需 SA 校验 |
| Edit | 策略 | 打开编辑抽屉 | 需 SA 校验 |
| Copy Again | 策略 | 打开创建抽屉 | 需 SA 校验 + 上限检查(10个) |
| Cancel | 订单 | POST /cancel | 需 JWT 认证 |
所有 API 操作失败返回 10026 时触发重新认证。
classDiagram
class StrategyAPI {
+createCopyTradeStrategy()
+retrievePnL()
}
class CopyTradeStrategyLifecycle {
+createCopyTradeIndex()
+manageStrategyState()
}
class CopyTradeOrderLifecycle {
+manageOrderState()
}
class CopyBuyTrigger {
+matchOrders()
+triggerExecution()
}
class CopySellTrigger {
+matchOrders()
+triggerExecution()
}
class CopyBuyResultHandler {
+handleCopyBuyResult()
}
class CopySellResultHandler {
+handleCopySellResult()
}
class PnLImpl {
+calculatePnL()
+retrievePnLData()
}
StrategyAPI --> CopyTradeStrategyLifecycle
StrategyAPI --> PnLImpl
CopyTradeStrategyLifecycle --> CopyTradeOrderLifecycle
CopyBuyTrigger --> CopyTradeOrderLifecycle
CopySellTrigger --> CopyTradeOrderLifecycle
CopyBuyResultHandler --> PnLImpl
CopySellResultHandler --> PnLImpl
graph TB
subgraph 策略管理
A["Copy Trade 策略<br/>跟单地址 + 规则配置"]
B["表单校验<br/>6组校验规则"]
C["策略操作<br/>创建/编辑/暂停/删除/再跟单"]
end
subgraph 执行引擎
D["链上信号<br/>DexAnalysis 事件"]
E["跟买触发<br/>CopyBuyTrigger"]
F["跟卖/TP/SL<br/>价格触发"]
end
subgraph 数据层
G["PnL 计算<br/>净持仓·虚拟持仓·平均成本"]
H["多源分页<br/>游标+内存排序"]
I["Rollup 聚合<br/>已实现PnL汇总"]
end
subgraph 前端架构
J["MobX Store<br/>Observable/Computed/Action"]
K["WebSocket<br/>实时订单更新"]
L["JWT 认证<br/>静默签名"]
end
A --> E
D --> E
E --> F
F --> G
G --> I
H --> J
K --> J
L --> J