- 尾盘策略 CRUD、订单簿 WS 订阅、周期内触发下单 - 订单簿订阅日志增加市场 slug,便于排查 - 移除轮询,完全依赖 WebSocket(删除 CryptoTailStrategyScheduler) - FIXED 模式数量改为小数、向上取整,与签名服务一致 - 前端策略列表页、多语言与 API 对接 Co-authored-by: Cursor <cursoragent@cursor.com>
11 KiB
11 KiB
加密市场尾盘策略 - 流程图
一、整体架构
┌─────────────────┐ POST 创建/更新 ┌──────────────────────────┐
│ 前端 / API │ ──────────────────────►│ CryptoTailStrategyController│
└─────────────────┘ └──────────────┬─────────────┘
│
▼
┌──────────────────────────┐
│ CryptoTailStrategyService │
│ create / update │
│ save → publishEvent │
└──────────────┬─────────────┘
│
┌─────────────────────────────────────────┼─────────────────────────────────────────┐
│ CryptoTailStrategyChangedEvent │ │
▼ ▼ ▼
┌──────────────────────────────┐ ┌──────────────────────────────┐ ┌──────────────────────────────┐
│ CryptoTailStrategyScheduler │ │ CryptoTailOrderbookWsService │ │ (其他监听方,如有) │
│ @EventListener │ │ @EventListener │ └──────────────────────────────┘
│ → runCycle() 一次(补充) │ │ → refreshAndSubscribe() │
└──────────────┬───────────────┘ └──────────────┬───────────────┘
│ │
▼ │
┌──────────────────────────────┐ │
│ CryptoTailStrategyExecution │ │ 每 25 秒 + 事件时
│ runCycle() │ │ refreshAndSubscribe()
│ (HTTP 拉订单簿,满足则下单) │ ▼
└──────────────────────────────┘ ┌──────────────────────────────┐
│ CLOB Market WebSocket │
│ wss://.../ws/market │
│ subscribe assets_ids │
└──────────────┬───────────────┘
│ book / price_change
▼
┌──────────────────────────────┐
│ onBestBid(tokenId, bestBid) │
│ → tryTriggerWithPriceFromWs │
└──────────────┬───────────────┘
│
▼
┌──────────────────────────────┐
│ CryptoTailStrategyExecution │
│ placeOrderForTrigger │
│ → CLOB 下单 + 写触发记录 │
└──────────────────────────────┘
二、策略创建/更新流程(API → 事件)
sequenceDiagram
participant API as Controller
participant Svc as CryptoTailStrategyService
participant DB as DB
participant Event as ApplicationEventPublisher
API->>Svc: create(request) / update(request)
Svc->>Svc: 参数校验(账户、窗口、价格、金额模式等)
Svc->>DB: save(entity)
Svc->>Event: publishEvent(CryptoTailStrategyChangedEvent)
Svc->>API: Result.success(dto)
- 创建:校验通过后落库,发布
CryptoTailStrategyChangedEvent,返回 DTO。 - 更新:同上,更新实体后发布同一事件。
- 删除:不发布事件(策略已移除,WS 下次刷新订阅时会自然不再包含该策略)。
三、策略变更后:双路响应
事件发出后,两个监听方并行执行,互不阻塞:
| 监听方 | 动作 | 说明 |
|---|---|---|
| CryptoTailStrategyScheduler | onStrategyChanged → runCycle() 一次 |
用 HTTP 拉订单簿做一轮检查,作为 WS 未就绪时的补充。 |
| CryptoTailOrderbookWsService | onStrategyChanged → refreshAndSubscribe() |
按当前启用策略重新算 token 列表,向 WS 发送新的 assets_ids 订阅。 |
flowchart LR
subgraph 事件
E[CryptoTailStrategyChangedEvent]
end
subgraph 调度器
S[Scheduler.onStrategyChanged]
R[executionService.runCycle]
S --> R
end
subgraph WS服务
W[OrderbookWsService.onStrategyChanged]
Ref[refreshAndSubscribe]
W --> Ref
end
E --> S
E --> W
四、WebSocket 订单簿监听流程(主路径)
flowchart TB
subgraph 启动与连接
A[PostConstruct init] --> B[connect]
B --> C[OkHttp WebSocket 连接 wss://.../ws/market]
C --> D[onOpen: refreshAndSubscribe]
end
subgraph 订阅维护
D --> E[buildSubscriptionMap]
E --> F[遍历 enabled 策略]
F --> G[当前周期 periodStartUnix]
G --> H[slug = prefix-periodStartUnix]
H --> I[Gamma getEventBySlug]
I --> J[得到 tokenIds]
J --> K[tokenId → List of WsBookEntry]
K --> L[发送 type=MARKET, assets_ids=[...]]
T[每 25 秒 @Scheduled] --> E
EV[onStrategyChanged] --> E
end
subgraph 收消息与触发
M[onMessage: book / price_change]
M --> N[解析 asset_id, best_bid]
N --> O[onBestBid tokenId, bestBid]
O --> P[查 tokenToEntries 得到策略列表]
P --> Q[筛时间窗内]
Q --> R[scope.launch tryTriggerWithPriceFromWs]
R --> S[placeOrderForTrigger]
end
L --> M
- buildSubscriptionMap:只包含「当前时间仍在窗口内」的策略(
nowSeconds < windowEnd),并只订阅这些策略对应周期的 token。 - onBestBid:再按当前时间过滤一次时间窗,对每个命中策略在协程里调用
tryTriggerWithPriceFromWs,内部会查「本周期是否已触发」和价格区间,通过则placeOrderForTrigger。
五、执行层:下单条件与顺序(ExecutionService)
无论来自 runCycle(HTTP) 还是 tryTriggerWithPriceFromWs(WS),最终都走同一套下单逻辑。
flowchart TB
subgraph runCycle 入口
A[runCycle] --> B[findAllByEnabledTrue]
B --> C[processStrategy 每个策略]
C --> D[在时间窗? 本周期已触发?]
D --> E[Gamma getEventBySlug]
E --> F[HTTP getOrderbook 两个 token]
F --> G[第一个 bestBid 在 minPrice~maxPrice?]
G --> H[placeOrderForTrigger]
end
subgraph tryTriggerWithPriceFromWs 入口
I[WS onBestBid] --> J[tryTriggerWithPriceFromWs]
J --> K[本周期已触发? bestBid 在区间?]
K --> H
end
subgraph placeOrderForTrigger 统一
H --> L[账户、API 凭证]
L --> M[余额、下单金额]
M --> N[最优价、数量]
N --> O[签名、CLOB 下单]
O --> P[保存 CryptoTailStrategyTrigger]
end
- 每周期最多触发一次:由
triggerRepository.findByStrategyIdAndPeriodStartUnix保证。 - 价格区间:
minPrice ≤ bestBid ≤ maxPrice才触发。 - 时间窗:仅当
windowStart ≤ now < windowEnd(以当前周期的periodStartUnix为基准)才参与检查/下单。
六、关键数据流小结
| 阶段 | 输入 | 输出/动作 |
|---|---|---|
| 创建/更新策略 | API 请求体 | 落库 + 发布 CryptoTailStrategyChangedEvent |
| 事件 → 调度器 | 事件 | 执行一次 runCycle()(HTTP 拉订单簿,满足则下单) |
| 事件 → WS 服务 | 事件 | refreshAndSubscribe(),更新订阅的 assets_ids |
| 定时刷新订阅 | 每 25 秒 | refreshAndSubscribe(),保证新周期、新策略被订阅 |
| WS 收 book/price_change | asset_id, best_bid | onBestBid → 时间窗内策略 → tryTriggerWithPriceFromWs → 未触发且价格在区间则 placeOrderForTrigger |
| placeOrderForTrigger | 策略、周期、token、outcome、价格 | 账户/余额/价格/签名 → CLOB 下单 → 写触发记录 |
七、涉及类与职责
| 类 | 职责 |
|---|---|
| CryptoTailStrategyController | 接收 list/create/update/delete/triggers/marketOptions 的 POST。 |
| CryptoTailStrategyService | 策略 CRUD、校验、发布 CryptoTailStrategyChangedEvent。 |
| CryptoTailStrategyScheduler | 监听策略变更事件,执行一次 runCycle()。 |
| CryptoTailOrderbookWsService | 连接 CLOB Market WS、维护订阅(事件 + 每 25 秒)、处理 book/price_change、调用 tryTriggerWithPriceFromWs。 |
| CryptoTailStrategyExecutionService | runCycle()(HTTP 路径)、tryTriggerWithPriceFromWs()(WS 路径)、placeOrderForTrigger()(统一下单与写触发记录)。 |