docs: 尾盘策略文档归集至 docs/crypto-tail-strategy/,前端链接同步

- 将尾盘策略相关文档移至 docs/crypto-tail-strategy/{zh,en}/
- 新增 docs/crypto-tail-strategy/README.md 目录说明
- 前端配置指南链接改为 docs/crypto-tail-strategy/${lang}/crypto-tail-strategy-user-guide.md

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
WrBug
2026-02-18 03:25:30 +08:00
co-authored by Cursor
parent cbcebf6e28
commit 5bb46ebb97
10 changed files with 143 additions and 77 deletions
-204
View File
@@ -1,204 +0,0 @@
# 加密市场尾盘策略 - 流程图
## 一、整体架构
```
┌─────────────────┐ 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 → 事件)
```mermaid
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` 订阅。 |
```mermaid
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 订单簿监听流程(主路径)
```mermaid
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
无论来自 **runCycleHTTP** 还是 **tryTriggerWithPriceFromWsWS**,最终都走同一套下单逻辑。
```mermaid
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()`(统一下单与写触发记录)。 |
-178
View File
@@ -1,178 +0,0 @@
# 加密市场尾盘策略 - 5/15 分钟市场数据获取说明
> 前端 UI 与交互详见 `crypto-tail-strategy-ui-spec.md`。
## 1. 数据源
- **Gamma API**`https://gamma-api.polymarket.com`
- 用于获取市场元数据:conditionId、开始/结束时间、标题、clobTokenIds 等。
- 无需鉴权。
## 2. 市场类型与 Slug 规则
| 类型 | Event Slug 规则 | 周期长度 | 说明 |
|------|-----------------|----------|------|
| Bitcoin 5 分钟 | `btc-updown-5m-{periodStartUnix}` | 5 min | periodStartUnix 为 5 分钟边界的 Unix 时间戳(秒) |
| Bitcoin 15 分钟 | `btc-updown-15m-{periodStartUnix}` | 15 min | periodStartUnix 为 15 分钟边界:`(now // 900) * 900` |
| Ethereum 5 分钟 | `eth-updown-5m-{ts}` | 5 min | 暂未验证是否在平台上线;如有可按相同规则推导 |
| Ethereum 15 分钟 | `eth-updown-15m-{ts}` | 15 min | 已验证存在 |
- 5 分钟周期:按 **300 秒** 对齐;当前周期起点可用 `(nowUnix // 300) * 300`,下一周期为 `+300`
- 15 分钟周期:按 **900 秒** 对齐;当前周期起点可用 `(nowUnix // 900) * 900`。slug 中的时间戳即为周期起始 Unix 秒;周期结束以 API 的 endDate 为准。
## 3. 获取单个周期市场(开始时间、结束时间)
### 3.1 请求
```bash
# 5 分钟 - 当前周期(示例时间戳需替换为当前周期起点)
curl -s "https://gamma-api.polymarket.com/events/slug/btc-updown-5m-1771007100"
# 15 分钟 - 需使用实际存在的时间戳(可从前端或历史 slug 得知)
curl -s "https://gamma-api.polymarket.com/events/slug/btc-updown-15m-1770882300"
```
### 3.2 响应结构(与开始/结束时间相关)
- **Event 层**`startDate``endDate`ISO 8601)。
- **markets[]**:每个市场有 `conditionId``question``startDate``endDate``clobTokenIds` 等。
**周期本身**:例如 5 分钟市场 "1:30PM-1:35PM ET",理应是 **startDate = 1:30 PM**、**endDate = 1:35 PM**。
**API 返回值与周期起止的对应关系(已用脚本验证)**
| 字段 | 是否等于周期起止 | 说明 |
|------|------------------|------|
| **endDate**Event / Market | **是**,等于周期结束时间(如 1:35 PM | API 的 endDate 即周期终点,可直接用。 |
| **startDate**Event / Market | **否**,不等于周期开始时间(1:30 PM | API 的 startDate 是市场创建/开放时间,不是周期起点,故**不能**当 1:30 PM 用。 |
**正确做法**:周期起点(1:30 PM)用 **slug 中的时间戳** 推导;周期终点(1:35 PM)用 API 的 **endDate**
- **5 分钟**:周期开始 = `slug_ts`(即 slug 中的 Unix 秒),周期结束 = `endDate`(或 `slug_ts + 300`)。
- **15 分钟**:周期开始 = `slug_ts`,周期结束 = `endDate`(或 `slug_ts + 900`)。
**示例(脚本输出解读)**:若 current 5m slug 为 `btc-updown-5m-1771007400`、title 为 "1:30PM-1:35PM ET"、endDate 为 `2026-02-13T18:35:00Z`,则 1771007400 = 18:30 UTC = 1:30 PM ET,即周期起点;endDate 18:35 UTC = 1:35 PM ET = 周期终点。next 5m slug 为 1771007700 = 1771007400 + 300,即下一周期起点。15m 同理:current slug 17710074001:301:45 PM ET),next 1771008300 = 1771007400 + 9001:452:00 PM ET)。
## 4. 如何列出“当前及未来”5/15 分钟市场
- Gamma 未提供按“5 分钟 / 15 分钟”或“Up or Down”的 tag 筛选;`tag_id=744`cryptocurrency)未返回这些短期市场。
- **可行方式**
1. **按周期时间戳生成 slug 并逐个请求**
- 5 分钟:当前周期 `ts = (nowUnix // 300) * 300`,下一周期 `ts + 300`,再下一周期 `ts + 600`
- 15 分钟:`ts = (nowUnix // 900) * 900`,然后 `ts + 900``ts + 1800`
- 请求 `GET /events/slug/btc-updown-5m-{ts}``btc-updown-15m-{ts}`;若返回 404 表示该周期尚未创建或已过期,可跳过。
2. **用户选择“市场”时**:若前端/后端已知“系列”(如 Bitcoin 5 minute),则只需约定 slug 前缀(`btc-updown-5m``btc-updown-15m`)与周期长度(300/900),按当前时间计算周期起点并请求对应 slug 即可得到当前周期的 conditionId、startDate、endDate;下一周期同理。
## 5. 周期边界与“每周期监听”
- **周期开始**:使用 **slug 中的时间戳** `periodStartUnix`(即请求 slug 时的 `btc-updown-5m-{ts}` 里的 `ts`),不要用 API 返回的 startDate。
- **周期结束**:使用 API 返回的 **event.endDate 或 market.endDate**(与 slug_ts + 300/900 一致)。
- 判断“当前是否在该周期内”:`periodStartUnix <= nowUnix < endDateUnix`,其中 `periodStartUnix` 从 slug 得到,`endDateUnix` 由 endDate 解析。
- 策略“每周期开始时开始监听”:当 `now` 跨过当前周期的 endDate(或下一周期的 periodStartUnix)时,视为新周期开始,重置“本周期是否已触发”等状态。
## 6. 如何保证每个周期的市场都能正确处理
### 6.1 用“当前时间”唯一确定当前周期
- 服务端只用**当前 Unix 时间**推导周期,不依赖 API 的 startDate。
- **5 分钟**`periodStartUnix = (nowUnix / 300) * 300`(整除)。
- **15 分钟**`periodStartUnix = (nowUnix / 900) * 900`
- 同一时刻算出的 `periodStartUnix` 唯一,对应唯一 slug(如 `btc-updown-5m-{periodStartUnix}`),从而对应唯一市场(conditionId、tokenIds、endDate)。
### 6.2 按周期拉取市场并切换
- **首次进入或策略启用**:用当前的 `periodStartUnix` 拼 slug,请求 Gamma `GET /events/slug/{slug}`,拿到该周期的 conditionId、endDate、clobTokenIds;用 endDate 解析得到 `endDateUnix`
- **每次需要判断“是否还在本周期”或“是否该下单”时**:先算当前 `currentPeriodStart = (nowUnix / interval) * interval`interval 为 300 或 900)。若 `currentPeriodStart` 大于上一笔使用的 `periodStartUnix`,说明已进入**下一周期**
- 用新的 `currentPeriodStart` 拼 slug,重新请求 Gamma,拿到**新周期**的 conditionId、endDate、clobTokenIds
- 用新周期的 tokenIds 订阅/拉取订单簿,用新 endDate 作为本周期结束时间;
- 重置本周期“是否已触发”等状态,避免把上一周期的状态带到新周期。
- **周期内**:始终用**本周期**的 conditionId、tokenIds、endDate 做价格监听与下单,不要混用上一周期的数据。
### 6.3 周期切换时机与 404 处理
- **切换时机**:以 `nowUnix >= endDateUnix``(nowUnix / interval) * interval > periodStartUnix` 作为“本周期已结束”,立刻按 6.2 用新 `periodStartUnix` 拉新周期市场。
- **新周期市场尚未创建(404)**:Gamma 可能稍晚才创建下一周期 event。若请求 slug 返回 404,可短间隔重试(如 5–15 秒)或等到下一整点/对齐点再试;重试时仍用**同一** `periodStartUnix`,避免用错周期。若长时间 404,可记录日志并跳过该周期,下一周期再正常拉取。
### 6.4 下单失败重试规则(每周期最多下单一次)
- 市价单提交失败时,**最多重试 2 次**(即 1 次初始 + 2 次重试,共 3 次尝试)。
- 若 3 次均失败:
- 本周期**不再**对该 outcome 下单;
- 记录失败原因与状态(便于审计与前端展示触发记录)。
- 周期切换时(6.2)重置为“未下单”,仅对新周期做新的判断与尝试。
### 6.5 去重与幂等(每周期最多触发一次)
- 以「策略 + 周期」唯一标识一次执行,例如 `(strategyId, periodStartUnix)``(accountId, slugPrefix, periodStartUnix)`
- 在数据库或内存中记录:本周期是否已触发、是否已下单。若已触发,同一周期内不再根据价格区间下单。
- 周期切换时(6.2)清空或更新为“新周期未触发”,只对新周期的 conditionId/tokenIds 做监听与下单。
### 6.6 时间区间(窗口)内才触发
- 策略可配置**时间区间**:从周期起点起算的「开始秒数」与「结束秒数」,例如 5 分钟市场可选 0~300 秒内的一段,15 分钟市场可选 0~900 秒内的一段(对应前端“分+秒”下拉,如 3 分 0 秒~12 分 0 秒即 180720 秒)。
- **执行规则**:仅当 `periodStartUnix + windowStartSeconds <= nowUnix < periodStartUnix + windowEndSeconds` 时,才根据 7.1 判断价格是否进入 [minPrice, maxPrice] 并执行下单;**区间外不进行价格判断与下单**。
- 存储:策略表(或配置)中保存 `windowStartSeconds``windowEndSeconds`(整数,单位秒);校验:`windowStartSeconds <= windowEndSeconds`,且不超过周期长度(5min 市场 ≤ 300,15min 市场 ≤ 900)。详见 [UI 规格 - 时间区间](crypto-tail-strategy-ui-spec.md)。
### 6.7 小结
| 要点 | 做法 |
|------|------|
| 周期唯一性 | 用 `(nowUnix / interval) * interval` 得到 periodStartUnix,再拼 slug,不依赖 API startDate。 |
| 周期数据 | 每周期用**该周期**的 slug 请求 Gamma,使用返回的 conditionId、endDate、clobTokenIds。 |
| 切换 | 当 `nowUnix >= endDateUnix` 或当前算出的 periodStartUnix 变化时,拉取新周期并重置状态。 |
| 404 | 同一 periodStartUnix 重试;长时间 404 可跳过该周期并打日志。 |
| 下单失败 | 失败后最多重试 2 次;仍失败则本周期不再下单并记录状态。 |
| 每周期只触发一次 | 用 (策略, periodStartUnix) 做去重,周期切换时重置“已触发”状态。 |
| 时间区间 | 仅当 periodStartUnix + windowStartSeconds ≤ now < periodStartUnix + windowEndSeconds 时做价格判断与下单;区间外不处理。 |
按上述方式,每个周期都会对应到正确的 slug、正确的市场与 endDate,并在周期结束时切换到下一周期;仅在配置的时间窗口内才根据价格触发下单,避免混周期或漏周期。
## 7. 与订单簿 / 价格的关系
- 价格由 **CLOB 订单簿**(或 WebSocket)获取,不依赖 GammaGamma 仅提供市场元数据。
- 使用 market.conditionId 与 markets[].clobTokenIds 解析出 tokenId,再订阅或请求该 token 的订单簿即可得到实时价格,用于区间判断与市价下单。
### 7.1 价格区间与「反方向」判断(如 minPrice = 0.92
二元市场(Up or Down)有两个 outcome:通常 outcomeIndex 0 = Up1 = Down,各对应一个 tokenId 和订单簿。
- **配置含义**:用户配置 minPrice = 0.92(及可选 maxPrice,默认 1)表示「当**某个 outcome 的价格**落在 [0.92, 1] 时触发市价买入**该** outcome」。
- **不预先选方向**:不需要用户选「买 Up 还是买 Down」;谁的价格先进入区间就买谁。
- **订单簿取价方式(与现有市价单逻辑一致)**:
- 对每个 outcome,取该 tokenId 订单簿的 **bestBid**(最高买入价)作为当前价格用于区间判断;若取价规则与现有市价买入逻辑不同,请以系统现有规则为准并在实现文档中写明。
- **判断方式**
- 同时取**两个 outcome** 的当前价格(按上述取价规则)。
-**outcome 0**:若 `price0 >= minPrice && price0 <= maxPrice` → 满足触发条件,买入 outcome 0(Up)。
-**outcome 1**:若 `price1 >= minPrice && price1 <= maxPrice` → 满足触发条件,买入 outcome 1Down)。
- **反方向**:「反方向」即另一个 outcome。例如若本轮已因 outcome 0 进入 [0.92, 1] 而买入 Up,则本周期内**不再**检查 outcome 1 是否也进入区间、也不再买 Down;反之若先触发的是 outcome 1(Down),则本周期不再买 Up。实现上:一旦本周期已对**任意一个** outcome 触发并下单,即标记本周期已触发,不再对**另一个 outcome(反方向)**做区间判断与下单。
- **同一时刻两边都进区间**:若同一时刻 Up 和 Down 的价格都在 [0.92, 1](理论上二元市场 Up+Down≈1 时不会同时 ≥0.92,但若出现),可约定按 outcomeIndex 优先(如先判 0 再判 1)或先到先得,只执行一笔买入,本周期不再买反方向。
总结:配置 0.92 时,对**两个方向**都做同一区间判断;先满足区间的那一侧触发买入,另一侧即为反方向,本周期不再触发。
## 8. 验证方式
**startDate/endDate 验证结论**:已用脚本对比 slug 时间戳与 API 返回的 startDate/endDate。**endDate 等于当前周期结束时间****startDate 不等于周期起始点**(为市场创建/开放时间),周期起始点应以 slug 中的时间戳为准。详见上文 3.2、5 节。
### 8.1 脚本(推荐)
项目内脚本,会请求当前/下一 5 分钟与 15 分钟 BTC 市场并打印 conditionId、startDate、endDate、clobTokenIds
```bash
python3 scripts/fetch_crypto_minute_markets.py
```
### 8.2 curl 示例
```bash
# 5 分钟 - 当前或下一周期(时间戳需替换为实际周期起点)
curl -s "https://gamma-api.polymarket.com/events/slug/btc-updown-5m-1771007100"
# 15 分钟 - 当前周期(时间戳需替换为实际周期起点)
curl -s "https://gamma-api.polymarket.com/events/slug/btc-updown-15m-1771006500"
# 15 分钟 - 历史存在的事件
curl -s "https://gamma-api.polymarket.com/events/slug/btc-updown-15m-1770882300"
curl -s "https://gamma-api.polymarket.com/events/slug/eth-updown-15m-1770801300"
```
若返回 403,可加 User-Agent`curl -s -H "User-Agent: PolymarketBot/1.0" "https://gamma-api.polymarket.com/events/slug/btc-updown-5m-1771007100"`
@@ -1,247 +0,0 @@
# 尾盘策略 - 最小价差参数流程分析
## 一、需求摘要
在现有尾盘策略上增加**最小价差**参数:当策略条件(时间窗、价格区间)满足时,再判断**当前周期 Binance K 线的开盘价与收盘价价差**是否满足最小价差;满足才下单,不满足则等待,直到价差满足再下单。
- **后端**:需订阅币安对应币对(如 BTC/USDC)的 K 线,维护当前周期的**开盘价**与**实时收盘价**,并在触发时做价差校验。
- **前端**:可配置三种场景——无、固定、自动(见下)。
---
## 二、前端配置场景
| 场景 | 配置方式 | 校验逻辑 |
|------|----------|----------|
| **无** | 不进行价差校验 | 与现有一致:仅判断时间窗 + 价格区间,满足即下单。 |
| **固定** | 用户输入一个固定价差(如 30) | 当 \|收盘价 − 开盘价\| ≥ 该固定值时,校验通过,再下单。 |
| **自动** | 由系统根据历史数据计算最小价差 | 见下文「自动模式计算逻辑」;得到数值后,后续与固定模式一致:\|收盘价 − 开盘价\| ≥ 计算值 则通过。 |
### 自动模式计算逻辑
- 通过币安 API 获取**历史 20 根** K 线(与策略周期一致:5m 取 5m K 线,15m 取 15m K 线)。
- **下单方向 = Down**outcomeIndex = 1):只取「收盘价 < 开盘价」的 K 线,得到价差序列(开盘价 − 收盘价)。
- **下单方向 = Up**outcomeIndex = 0):只取「收盘价 > 开盘价」的 K 线,得到价差序列(收盘价 − 开盘价)。
- **异常值剔除**:对上述价差序列做异常值过滤(见下文「异常值剔除」),再用**剩余样本**求平均价差,乘以系数 **80%** 得到最小价差;后续用该值做 \|收盘价 − 开盘价\| ≥ 该值 的校验。
- **历史数据获取时机**:**在该周期开始时就拉取并计算**,不在保存策略时计算。订单簿 WS 在周期开始时刷新订阅(含每 25 秒或周期切换时的 refreshAndSubscribe),此时对当前周期内所有启用且为 AUTO 的策略,按 (intervalSeconds, periodStartUnix) 预拉该周期前 20 根已收盘 K 线并计算 minSpreadUp/minSpreadDown 写入缓存;该周期内触发时直接用缓存,无需在触发时再调 REST。
### 异常值剔除
- **目的**:避免少数极端 K 线(如 14 组价差在 50 以内、1 组价差 200)拉高平均价差,导致最小价差偏大、难以触发。
- **做法**:在按方向得到价差序列后,先**剔除异常值**,再对剩余价差求平均并 × 0.8。
- **推荐方法:IQR(四分位距)**
- 对价差序列排序,计算 Q1(25% 分位)、Q3(75% 分位)、IQR = Q3 Q1。
- 保留区间 **[Q1 1.5×IQR, Q3 + 1.5×IQR]** 内的价差,剔除该区间外的点。
- 示例:15 组价差,14 组在 50 以内、1 组为 200 → 200 会超出上界被剔除,只用 14 组参与平均。
- **边界与降级**
- 若剔除后剩余样本数过少(如 &lt; 3),则**不剔除**:用全部价差样本求平均 × 0.8。
- 若无满足方向的 K 线(如 20 根里没有 close &lt; open),仍按原文档降级处理(全量 \|close−open\| 或返回 0)。
---
## 三、整体流程(含价差校验)
```
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 1. 数据源与订阅 │
├─────────────────────────────────────────────────────────────────────────────────┤
│ • CLOB 订单簿 WS(现有):Polymarket 订单簿 → bestBid。 │
│ • 币安 K 线 WS(新增):订阅 BTCUSDC 对应周期(5m/15m),维护「当前周期」的开盘价 │
│ open、实时收盘价 close(每根 K 线未收盘前 close 会持续更新)。 │
└─────────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 2. 触发入口(与现有一致) │
├─────────────────────────────────────────────────────────────────────────────────┤
│ • 入口 ACryptoTailOrderbookWsService.onBestBid(tokenId, bestBid) │
│ • 入口 BCryptoTailStrategyExecutionService.runCycle()HTTP 拉订单簿) │
│ 两者在「时间窗 + 价格区间 + 本周期未触发」通过后,都会调用执行层「尝试下单」。 │
└─────────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 3. 执行层增加「价差校验」 │
├─────────────────────────────────────────────────────────────────────────────────┤
│ 在现有 tryTriggerWithPriceFromWs / runCycle → placeOrderForTrigger 之前增加: │
│ │
│ if (策略.minSpreadMode == NONE) → 直接进入 placeOrderForTrigger。 │
│ else: │
│ • 从「币安 K 线服务」取当前周期(与 strategy.intervalSeconds 对齐)的 open、 │
│ close(实时)。 │
│ • 若取不到 open/close(例如该周期尚未有数据)→ 本轮不下单,等待下次 WS 更新。 │
│ • 计算 effectiveMinSpread
│ - FIXEDeffectiveMinSpread = 策略.minSpreadValue(用户填的固定值) │
│ - AUTOeffectiveMinSpread = 按当前下单方向(outcomeIndex)取「自动计算 │
│ 的最小价差」(见下节;若尚未计算则先拉 20 根历史 K 线并计算、缓存)。 │
│ • 若 |close open| < effectiveMinSpread → 本轮不下单,等待价差满足。 │
│ • 若 |close open| >= effectiveMinSpread → 通过价差校验,进入 │
│ placeOrderForTrigger(与现有逻辑一致:预签/签名、提交 CLOB 订单、写触发记录)。│
└─────────────────────────────────────────────────────────────────────────────────┘
```
- **「等待价差满足」**:不主动轮询;下次 CLOB 订单簿或币安 K 线有推送时,会再次进入上述判断,此时 close 可能已更新,价差可能已满足,再决定是否下单。
- **每周期最多触发一次**:仍由现有「本周期是否已触发」保证;价差不满足时**不写触发记录**,也不占「已触发」名额,直到某次检查同时满足价格区间与价差后才下单并标记已触发。
---
## 四、自动模式:何时拉历史、如何算、如何用
- **何时拉 20 根历史 K 线并计算**
- **在该周期开始时就预计算**,不在保存策略时计算。
- 订单簿 WS 在**周期开始时**会刷新订阅(`refreshAndSubscribe`:每 25 秒或检测到周期切换时),此时对当前周期内所有启用且 minSpreadMode=AUTO 的策略,按 `(intervalSeconds, periodStartUnix)` 异步拉取该周期前 20 根已收盘 K 线(REST `endTime = periodStartUnix * 1000`),按 Up/Down 分别算 avgSpread × 0.8(含 IQR 剔除)并写入缓存。该周期内后续触发时直接用缓存,**不在触发时再调 REST**。
- 若某周期未做预计算(如服务刚启动且尚未到刷新时机),触发时仍会按需调用 `computeAndCache` 并缓存,保证逻辑正确。
- 前端「自动最小价差」接口仅作**预览**,实际下单校验不依赖该接口。
- **计算细节**
- 历史 20 根:币安 REST `GET /api/v3/klines?symbol=BTCUSDC&interval=5m|15m&limit=20`(或 21 取前 20 根已收盘),每根格式为 [openTime, open, high, low, close, ...]。
- **DownoutcomeIndex=1**:筛选 close < open,价差 = open close,得到价差序列 → **异常值剔除(IQR** → 对剩余价差求平均,再 × 0.8 → minSpreadDown。
- **UpoutcomeIndex=0**:筛选 close > open,价差 = close open,得到价差序列 → **异常值剔除(IQR** → 对剩余价差求平均,再 × 0.8 → minSpreadUp。
- **异常值剔除**:见上文「异常值剔除」;剔除后再平均。若剔除后剩余样本 &lt; 3,则不剔除,用全部价差样本求平均。
- 若无满足方向的 K 线(例如 20 根里没有一根 close < open),可降级:用全部 20 根的 |close−open| 平均 × 0.8,或返回 0/不校验,具体产品可定。
- **触发时使用**
- 当前要下单的是 outcomeIndex0=Up, 1=Down),取对应的 minSpreadUp 或 minSpreadDown 作为 effectiveMinSpread,再与 |close open| 比较。
---
## 五、后端模块与数据流
| 模块 | 职责 |
|------|------|
| **BinanceKlineService(新)** | 1)订阅币安 WSBTCUSDC 的 5m、15m K 线流(可按需只订阅有策略使用的周期)。<br>2)维护「当前周期」数据:以 periodStartUnix(或 K 线 t 对齐)为 key,存 (open, close)K 线 WS 推送时更新 close,新周期首条推送时更新 open。<br>3)提供 getCurrentOpenClose(symbol, intervalSeconds, periodStartUnix) → (open, close)?,供执行层价差校验使用。 |
| **BinanceKlineAutoSpreadService 或合入上者(新)** | 1)按**周期**拉取:以 periodStartUnix 为界,REST 拉取该周期前的 20 根已收盘 K 线。<br>2)按 Up/Down 得到价差序列 → **IQR 异常值剔除** → 对剩余价差求平均 × 0.8,缓存 (intervalSeconds, periodStartUnix) → (minSpreadUp, minSpreadDown)。<br>3)提供 getAutoMinSpread(intervalSeconds, periodStartUnix, outcomeIndex) 与 computeAndCache(intervalSeconds, periodStartUnix)。**周期开始时**由 CryptoTailOrderbookWsService 在 refreshAndSubscribe 后对当前周期内 AUTO 策略预调 computeAndCache;触发时直接用缓存,未命中时再按需计算。 |
| **CryptoTailStrategy(实体)** | 新增字段建议:minSpreadModeNONE/FIXED/AUTO)、minSpreadValue(固定时使用;AUTO 时可为空或存上次计算值用于展示)。 |
| **CryptoTailStrategyExecutionService(现有)** | 在 tryTriggerWithPriceFromWs 与 runCycle 分支中,在调用 placeOrderForTrigger 前:若 minSpreadMode != NONE,则取 open/close 与 effectiveMinSpread,校验 \|closeopen\| >= effectiveMinSpread;不通过则 return,不调用 placeOrderForTrigger。 |
| **CryptoTailOrderbookWsService(现有)** | 仍只根据 CLOB bestBid 触发;价差校验在执行层统一做。**新增**refreshAndSubscribe 完成后,对当前周期内所有启用且 minSpreadMode=AUTO 的策略,异步调用 BinanceKlineAutoSpreadService.computeAndCache,在周期开始即预计算最小价差。 |
- **币安 K 线与周期对齐**
- 策略周期:periodStartUnix 为秒(如 5m 周期 = 300 的倍数,15m = 900 的倍数)。
- 币安 K 线:t 为毫秒,同一周期:t_ms = periodStartUnix * 1000。
- 用 (intervalSeconds, periodStartUnix) 或 (interval, t_ms) 对齐即可从 BinanceKlineService 取到「当前周期」的 open 和实时 close。
---
## 六、固定(FIXED)与自动(AUTO)时序图
### 6.1 固定(FIXED)时序图
固定模式:用户保存策略时写入 `minSpreadValue`(如 30);触发时直接用该值与当前周期 \|close−open\| 比较,不拉历史 K 线。
```mermaid
sequenceDiagram
participant User as 用户
participant API as Controller
participant Svc as CryptoTailStrategyService
participant DB as 数据库
participant CLOB_WS as CLOB 订单簿 WS
participant Orderbook as CryptoTailOrderbookWsService
participant Exec as CryptoTailStrategyExecutionService
participant BinanceWS as BinanceKlineService
participant CLOB as Polymarket CLOB
User->>API: 保存策略 minSpreadMode=FIXED, minSpreadValue=30
API->>Svc: create/update
Svc->>DB: 写入 min_spread_mode, min_spread_value
Svc-->>API: 成功
API-->>User: 成功
Note over BinanceWS: 后台持续:币安 K 线 WS 更新当前周期 (open, close)
CLOB_WS->>Orderbook: onMessage(book/price_change) → bestBid
Orderbook->>Orderbook: 时间窗内?价格在 [min,max]?本周期未触发?
Orderbook->>Exec: tryTriggerWithPriceFromWs(strategy, periodStartUnix, ..., bestBid)
Exec->>Exec: mutex 锁
Exec->>Exec: 本周期已触发?→ 是则 return
Exec->>Exec: passMinSpreadCheck(strategy, periodStartUnix, outcomeIndex)
Exec->>Exec: mode==FIXED → effectiveMinSpread = strategy.minSpreadValue (30)
Exec->>BinanceWS: getCurrentOpenClose(intervalSeconds, periodStartUnix)
BinanceWS-->>Exec: (open, close) 来自内存
Exec->>Exec: |closeopen| >= 30 ? 否 → return,不下单
Exec->>Exec: 是 → 通过价差校验
Exec->>Exec: ensurePeriodContext → placeOrderForTrigger
Exec->>CLOB: 提交订单
CLOB-->>Exec: orderId
Exec->>DB: 写入触发记录 (本周期已触发)
```
---
### 6.2 自动(AUTO)时序图
自动模式:不在保存策略时计算。**在该周期开始时就预计算**(订单簿 WS 刷新订阅时对该周期内 AUTO 策略异步拉 20 根历史 K 线并计算、缓存);触发时直接用缓存,同一周期内复用。
```mermaid
sequenceDiagram
participant User as 用户
participant API as Controller
participant Svc as CryptoTailStrategyService
participant DB as 数据库
participant CLOB_WS as CLOB 订单簿 WS
participant Orderbook as CryptoTailOrderbookWsService
participant Exec as CryptoTailStrategyExecutionService
participant BinanceWS as BinanceKlineService
participant AutoSpread as BinanceKlineAutoSpreadService
participant BinanceREST as 币安 REST API
participant CLOB as Polymarket CLOB
User->>API: 保存策略 minSpreadMode=AUTO(不填 minSpreadValue
API->>Svc: create/update
Svc->>DB: 写入 min_spread_mode=AUTO
Svc-->>API: 成功
API-->>User: 成功
Note over BinanceWS: 后台持续:币安 K 线 WS 更新当前周期 (open, close)
CLOB_WS->>Orderbook: onMessage → bestBid
Orderbook->>Orderbook: 时间窗 + 价格区间 + 本周期未触发 ✓
Orderbook->>Exec: tryTriggerWithPriceFromWs(strategy, periodStartUnix, ..., bestBid)
Exec->>Exec: mutex 锁
Exec->>Exec: passMinSpreadCheck(strategy, periodStartUnix, outcomeIndex)
Exec->>BinanceWS: getCurrentOpenClose(intervalSeconds, periodStartUnix)
BinanceWS-->>Exec: (open, close)
Note over Orderbook,AutoSpread: 周期开始时 refreshAndSubscribe 已对该周期预计算(见下)
Exec->>AutoSpread: getAutoMinSpread(intervalSeconds, periodStartUnix, outcomeIndex)
AutoSpread->>AutoSpread: 查缓存 (intervalSeconds, periodStartUnix) → 命中(周期开始已预计算)
AutoSpread-->>Exec: effectiveMinSpread
Exec->>Exec: |closeopen| >= effectiveMinSpread ? 否 → return
Exec->>Exec: 是 → 通过价差校验
Exec->>Exec: placeOrderForTrigger → CLOB 下单
Exec->>DB: 写入触发记录
Note over Orderbook,AutoSpread: 周期开始时(refreshAndSubscribe 或周期切换)
Orderbook->>Orderbook: refreshAndSubscribe() → buildSubscriptionMap() → newMap
Orderbook->>Orderbook: precomputeAutoMinSpreadForCurrentPeriods(newMap)
Orderbook->>AutoSpread: computeAndCache(intervalSeconds, periodStartUnix) [异步]
AutoSpread->>BinanceREST: GET /api/v3/klines?symbol=BTCUSDC&interval=15m&limit=20&endTime=periodStart*1000
BinanceREST-->>AutoSpread: 20 根已收盘 K 线
AutoSpread->>AutoSpread: 按 Up/Down 拆价差 → IQR 剔除 → 平均×0.8 → 缓存
Note over CLOB_WS,Exec: 同一周期内再次触发(如另一 outcome 或再次 bestBid
CLOB_WS->>Orderbook: onMessage → bestBid
Orderbook->>Exec: tryTriggerWithPriceFromWs(...)
Exec->>AutoSpread: getAutoMinSpread(intervalSeconds, periodStartUnix, outcomeIndex)
AutoSpread->>AutoSpread: 查缓存 → 命中
AutoSpread-->>Exec: effectiveMinSpread(不再调 REST
Exec->>Exec: 价差校验 → 通过则下单(或本周期已触发则跳过)
```
---
## 七、流程小结(按执行顺序)
1. **策略配置**
- 用户选择:无 / 固定(输入数值)/ 自动。
- 固定:必填 minSpreadValue,保存到 DB。
- 自动:不填 minSpreadValue,**不在保存时计算**;按周期在首次需要时计算并缓存。
2. **运行时**
- 币安 WS 持续更新当前周期的 (open, close)。
- CLOB 订单簿(或 HTTP)带来 bestBid;若时间窗 + 价格区间 + 本周期未触发 均满足:
- 若 minSpreadMode == NONE → 直接 placeOrderForTrigger。
- 否则取当前周期 open/close 与 effectiveMinSpread(固定值或自动缓存值),若 \|closeopen\| >= effectiveMinSpread → placeOrderForTrigger;否则本轮不下单,等后续推送再判。
3. **下单与去重**
- 仍保持「每周期最多触发一次」;价差不满足时不写触发记录,直到某次同时满足价格与价差后才下单并写记录。
按上述流程即可在现有尾盘策略上接入「最小价差」参数,并由后端订阅币安 K 线、在触发前做价差校验;固定与自动的时序差异见**第六节时序图**。
-150
View File
@@ -1,150 +0,0 @@
# 加密市场尾盘策略 - 任务梳理
> 需求与 UI 见 `crypto-tail-strategy-ui-spec.md`,市场数据与执行规则见 `crypto-tail-strategy-market-data.md`。
以下按**文档 / 数据库 / 后端 / 前端**拆分为可执行任务,便于排期与验收。
---
## 一、文档(已完成)
| 任务 | 状态 | 说明 |
|------|------|------|
| PRD 与需求 | ✅ | 周期、价格区间、每周期最多触发一次、重试 2 次等 |
| 市场数据文档 | ✅ | `crypto-tail-strategy-market-data.md`:Gamma slug、周期、时间区间、价格判断 |
| UI 规格 | ✅ | `crypto-tail-strategy-ui-spec.md`:列表、表单、时间区间、触发记录、赎回前置检查 |
---
## 二、数据库
| 序号 | 任务 | 说明 |
|------|------|------|
| D1 | 策略表 migration | 新建表,字段建议:id, account_id, name, market_slug_prefix(如 btc-updown-5m), interval_seconds(300/900), window_start_seconds, window_end_seconds, min_price, max_price, amount_mode(ratio/fixed), amount_value(比例或 USDC 字符串), enabled, created_at, updated_at。唯一/外键按现有规范。 |
| D2 | 触发记录表 migration | 新建表,字段建议:id, strategy_id, period_start_unix, market_title, outcome_index(0=Up/1=Down), trigger_price, amount_usdc, order_id(可空), status(success/fail), fail_reason(可空), created_at。便于列表与筛选。 |
---
## 三、后端(Kotlin
### 3.1 实体与 Repository
| 序号 | 任务 | 说明 |
|------|------|------|
| B1 | 策略实体 Entity | 对应策略表;ID 用 Long?;时间 Long 时间戳;金额 BigDecimal;遵守 backend.mdc 实体规范。 |
| B2 | 触发记录实体 Entity | 对应触发记录表。 |
| B3 | JpaRepository | 策略、触发记录的 Repository;按 strategyId、时间等查记录。 |
### 3.2 外部依赖与领域
| 序号 | 任务 | 说明 |
|------|------|------|
| B4 | Gamma API 按 slug 拉市场 | 已有或扩展 PolymarketGammaApiGET /events/slug/{slug},返回 conditionId、endDate、clobTokenIds 等;与 market-data 文档 3、4 节一致。 |
| B5 | 周期与 slug 推导 | 工具或 Service:根据 interval(300/900)、当前时间算 periodStartUnix;拼 slug(如 btc-updown-5m-{ts});解析 endDate 得 endDateUnix。 |
| B6 | 订单簿价格 | 使用现有 CLOB/订单簿能力,按 conditionId、clobTokenIds 取各 outcome 的 bestBid;与 market-data 7.1 一致。 |
| B7 | 市价单与重试 | 按策略的 amount 计算下单金额;市价买入指定 outcome;失败时最多重试 2 次(共 3 次),仍失败则写触发记录状态为失败并记原因。 |
### 3.3 策略执行核心逻辑(按 market-data 第 6、7 节)
| 序号 | 任务 | 说明 |
|------|------|------|
| B8 | 周期内时间窗口判断 | 仅当 `periodStartUnix + windowStartSeconds <= nowUnix < periodStartUnix + windowEndSeconds` 时,才做价格区间判断与下单;区间外不处理。 |
| B9 | 价格区间与「先满足先买」 | 对两个 outcome 取价,若某 outcome 价格 ∈ [minPrice, maxPrice],则触发买该 outcome;另一 outcome 本周期不再触发(7.1)。 |
| B10 | 每周期只触发一次 | 以 (strategyId, periodStartUnix) 去重;周期切换时重置「本周期已触发」状态;结合 B8、B9 实现。 |
| B11 | 周期切换与 404 | 当 now >= endDateUnix 或新 periodStartUnix 时,用新 periodStartUnix 拉新 slug404 时同 periodStartUnix 短间隔重试,长时间 404 可跳过本周期并打日志。 |
### 3.4 API 与 DTO
| 序号 | 任务 | 说明 |
|------|------|------|
| B12 | 策略 CRUD API | 列表(分页/筛选)、创建、更新、删除、启用/停用;请求/响应为 DTO,不用 Map;统一 ApiResponse;错误码与 MessageSource。 |
| B13 | 策略 DTO | 创建/更新包含:accountId, name, marketSlugPrefix, intervalSeconds, windowStartSeconds, windowEndSeconds, minPrice, maxPrice(可选默认 1), amountMode, amountValue;校验 windowStart <= windowEnd,且不超过周期长度。 |
| B14 | 触发记录 API | 按 strategyId 分页查询触发记录;返回列表 DTO(时间、市场、方向、价格、金额、订单 ID、状态)。 |
| B15 | 5/15 分钟市场列表 API(可选) | 若前端需要「可选市场」列表:可按当前/下一周期拼 slug 调 Gamma 返回市场信息,供前端选择;或前端直接按 slug 规则+周期展示。 |
### 3.5 自动赎回与调度
| 序号 | 任务 | 说明 |
|------|------|------|
| B16 | 自动赎回包含尾盘策略仓位 | 尾盘策略产生的仓位与跟单/手动一视同仁,纳入现有自动赎回逻辑,不排除(见 UI 规格附录 A)。 |
| B17 | 调度/定时或常驻 | 对已启用策略按周期(如每 10–30 秒)检查:当前周期、是否在时间窗口内、是否已触发、价格是否进区间;满足则执行下单并写触发记录。 |
---
## 四、前端(React + TypeScript
### 4.1 路由与导航
| 序号 | 任务 | 说明 |
|------|------|------|
| F1 | 路由 | App.tsx 增加 `/crypto-tail-strategy`、可选 `/crypto-tail-strategy/records/:id`。 |
| F2 | 菜单 | Layout 中增加「尾盘策略」菜单项,与跟单同级或在其下;key 与路由一致。 |
### 4.2 列表页
| 序号 | 任务 | 说明 |
|------|------|------|
| F3 | 列表页组件 | 如 CryptoTailStrategyList.tsx;页面标题、钱包提示 Alert、新增按钮、筛选(账户、状态)。 |
| F4 | 列表展示 | 桌面 Table / 移动 Card:策略名、关联市场、时间区间、价格区间、投入方式、状态、最近触发、操作(编辑、启用/停用、删除、查看触发记录);删除 Popconfirm。 |
| F5 | 创建前检查 | 点击「新增策略」先调接口判断是否已配置自动赎回(如 builderApiKeyConfigured);未配置则弹出「请先配置自动赎回」Modal(去配置 → /system-settings,取消),不打开表单。 |
### 4.3 新增/编辑表单
| 序号 | 任务 | 说明 |
|------|------|------|
| F6 | 表单弹窗 | 策略名、选择账户、选择市场、时间区间、minPrice、maxPrice、投入方式(比例/固定)、启用状态。 |
| F7 | 时间区间控件 | 区间开始/结束:下拉选「分钟」+「秒」;5min 市场 0–5 分+059 秒(总≤5min),15min 市场 015 分+059 秒(总≤15min);校验**开始 ≤ 结束**;提交时转为 windowStartSeconds、windowEndSeconds。 |
| F8 | 市场选择器 | 仅展示 5/15 分钟加密市场;支持搜索;展示市场标题+周期;选后用于校验时间区间上界(5min 结束≤300s15min≤900s)。 |
| F9 | 表单校验与提交 | 市场类型、时间区间 start≤end 且不超周期、minPrice/maxPrice、比例或固定金额合法;提交后刷新列表、成功提示。 |
### 4.4 触发记录
| 序号 | 任务 | 说明 |
|------|------|------|
| F10 | 触发记录展示 | 弹窗或独立页:触发时间、市场、方向(Up/Down)、触发价格、投入金额、订单 ID、状态;支持按时间、状态筛选;formatUSDC;移动端 Card/折叠。 |
### 4.5 通用
| 序号 | 任务 | 说明 |
|------|------|------|
| F11 | 类型定义 | 策略、触发记录等 TypeScript 类型;无 any。 |
| F12 | API 封装 | apiService 中 cryptoTailStrategy.list/create/update/delete/toggle、records(strategyId) 等。 |
| F13 | 多语言 | locales 中 zh-CN、zh-TW、en 的 cryptoTailStrategy.*list.title、list.walletTip、form.walletTip、redeemRequiredModal.*、时间区间/价格区间等文案。 |
---
## 五、依赖关系简图
```
文档 ✅
D1,D2 数据库
B1B3 实体与 Repository
B4–B7 外部 API、周期、价格、下单
B8–B11 执行逻辑(时间窗口+价格+去重+周期切换)
B12B15 API 与 DTO
B16 自动赎回
B17 调度
F1F2 路由与菜单
F11F12 类型与 API 封装
F13 多语言
F3F5 列表与创建前检查
F6F9 表单(含时间区间)
F10 触发记录
```
---
## 六、验收要点
- **时间区间**:仅当周期内当前时间落在 [windowStartSeconds, windowEndSeconds] 时才判断价格并下单;前端区间开始 ≤ 结束,且不超出 5min/15min。
- **每周期一次**:同一策略同一周期只触发一次(先满足价格的 outcome 买入,反方向不买)。
- **重试**:下单失败最多重试 2 次,共 3 次;仍失败记入触发记录为失败。
- **自动赎回**:尾盘策略产生的仓位可被自动赎回,无排除逻辑。
- **创建前检查**:未配置自动赎回时点击新增策略弹出「去配置」弹窗,不打开表单。
-177
View File
@@ -1,177 +0,0 @@
# 加密市场尾盘策略 - 前端 UI 规格
> 周期推导与市场数据获取详见 `crypto-tail-strategy-market-data.md`。
与现有跟单/回测保持同一风格(Ant Design、响应式、多语言),以下为页面结构及所含元素。
---
## 1. 导航与路由
| 项目 | 说明 |
|------|------|
| **菜单** | 在「跟单管理」同级或其下增加一项,如「尾盘策略」,key 建议 `/crypto-tail-strategy`。 |
| **路由** | 列表页 `/crypto-tail-strategy`;可选详情/触发记录 `/crypto-tail-strategy/records/:id`。 |
参考:`Layout.tsx``/copy-trading``/backtest` 的配置;`App.tsx` 中对应 `Route`
---
## 2. 列表页(主页面)
**路径**`/crypto-tail-strategy`
**组件**:如 `CryptoTailStrategyList.tsx`(或 `TailStrategyList.tsx`)。
### 2.1 顶部操作区
| 元素 | 类型 | 说明 |
|------|------|------|
| 页面标题 | 标题文案 | 如「加密尾盘策略」,用 `t('cryptoTailStrategy.list.title')`。 |
| **钱包使用提示** | **AlertWarning** | **必须**在页面顶部或标题下方展示:提示用户**使用单独/专用钱包**运行本策略,避免该钱包用于手动交易、跟单等其他操作,否则可能导致余额或仓位变化,进而造成策略执行异常(如余额不足、下单失败等)。文案走多语言 `t('cryptoTailStrategy.list.walletTip')`,可带 `showIcon`。 |
| 新增策略 | ButtonPrimary) | 点击时**先检查自动赎回相关配置**(见 2.4);若未配置则弹出「去配置」简易弹窗,若已配置则打开「新增策略」表单弹窗。图标可用 `PlusOutlined`。 |
| 筛选(可选) | Select / 筛选项 | 按账户、启用状态筛选;移动端可收起到抽屉或折叠。 |
### 2.2 列表内容(桌面端:Table,移动端:Card 列表)
| 列/卡片项 | 说明 |
|-----------|------|
| 策略名称 | 用户填的配置名或自动生成名。 |
| 关联市场 | 展示市场标题 + 周期,如「Bitcoin Up or Down - 5 minute」。 |
| 时间区间 | 如「3 分 0 秒 ~ 12 分 0 秒」(与周期类型一致:5min 为 0–5 分,15min 为 015 分)。 |
| 价格区间 | 如 `[0.92, 1]` 或「0.92 ~ 1」(maxPrice 为空时显示为 1)。 |
| 投入方式 | 「比例 10%」或「固定 100 USDC」,用 `formatUSDC` 格式化金额。 |
| 状态 | Tag 或 Switch:启用 / 停用。 |
| 最近触发 | 最近一次触发时间(若有);无则「-」。 |
| 操作 | 编辑、启用/停用、删除、查看触发记录。删除前 Popconfirm 二次确认。 |
### 2.3 与现有风格对齐
- 加载态:`Spin` 包裹列表。
- 空状态:无数据时展示空状态插画 + 引导「新增策略」。
- 响应式:`useMediaQuery({ maxWidth: 768 })`,桌面用 Table,移动用 Card + 操作折叠/抽屉。
参考:`CopyTradingList.tsx` 的 Table 列、Card 布局、筛选与 Modal 打开方式。
### 2.4 创建前检查:自动赎回配置(必须)
策略依赖**自动赎回**(需通过 Relayer/Builder API 提交链上赎回)。用户点击「新增策略」时:
1. **检查**:请求系统配置(如 `apiService.systemConfig.getConfig()` 或已有接口),判断是否已配置 Builder API Key(及可选:自动赎回已开启)。若 `builderApiKeyConfigured === false`(或后端约定之「未配置」状态),视为未配置。
2. **未配置时**:不打开新增策略表单,改为弹出**简易弹窗**(Modal),内容建议:
- **标题**:如「请先配置自动赎回」,`t('cryptoTailStrategy.redeemRequiredModal.title')`
- **正文**:简短说明尾盘策略依赖自动赎回,需要先在「系统设置」中配置 Builder API Key 及自动赎回。文案 `t('cryptoTailStrategy.redeemRequiredModal.description')`
- **操作**
- **去配置**:主按钮,点击后关闭弹窗并跳转到系统设置页(如 `/system-settings`,该页含 Relayer 配置与自动赎回开关)。
- **取消**:次按钮或关闭图标,仅关闭弹窗。
3. **已配置时**:正常打开新增策略表单弹窗。
弹窗保持简易,无需表单,仅提示 + 跳转;多语言键示例:`cryptoTailStrategy.redeemRequiredModal.title``cryptoTailStrategy.redeemRequiredModal.description``cryptoTailStrategy.redeemRequiredModal.goToSettings``cryptoTailStrategy.redeemRequiredModal.cancel`
---
## 3. 新增 / 编辑策略弹窗(Modal)
**组件**:如 `CryptoTailStrategyFormModal.tsx` 或内嵌在列表页的 Modal。
### 3.1 表单字段
| 表单项 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| **钱包提示(简短)** | **AlertWarning** | - | 在「选择账户」上方或表单单列顶部展示简短提示:建议使用**专用钱包**,避免手动操作等导致异常。文案如 `t('cryptoTailStrategy.form.walletTip')`。 |
| 策略名称 | Input | 否 | 用于列表展示,可占位「自动生成」。 |
| 选择账户 | Select | 是 | 下拉已导入账户(与跟单一致,来自 `useAccountStore()` 或接口)。 |
| 选择市场 | 市场选择器 | 是 | 仅展示 5/15 分钟加密市场;支持搜索;展示市场标题 + 周期(5min/15min);一个策略绑一个市场。 |
| **时间区间** | **开始 / 结束** | 是 | 仅在本周期内的该时间窗口内,价格满足时才下单;区间外不处理。见下方说明。 |
| 区间开始 | 下拉(分 + 秒) | 是 | 从周期起点起算的「开始」偏移。5 分钟市场可选 0~5 分 + 0~59 秒(总不超过 5 分钟);15 分钟市场可选 0~15 分 + 0~59 秒(总不超过 15 分钟)。 |
| 区间结束 | 下拉(分 + 秒) | 是 | 从周期起点起算的「结束」偏移。范围同上,且**区间开始不得大于区间结束**(前端校验)。 |
| 最低价 minPrice | InputNumber | 是 | 01,精度 24 位小数;校验 minPrice ≤ 1。 |
| 最高价 maxPrice | InputNumber | 否 | 0~1,占位「不填默认为 1」;若填则校验 minPrice ≤ maxPrice ≤ 1。 |
| 投入方式 | Radio.Group | 是 | 选项:「按比例」「固定金额」。 |
| 比例 % | InputNumber | 条件必填 | 选「按比例」时显示;0~100;可展示当前账户 USDC 余额与预估金额。 |
| 固定金额 (USDC) | InputNumber | 条件必填 | 选「固定金额」时显示;≥ 最小下单额,≤ 账户余额;用 `formatUSDC` 展示。 |
| 启用状态 | Switch | 否 | 新增默认开启;编辑可切换。 |
**时间区间说明**:例如 15 分钟市场配置「3 分 0 秒」~「12 分 0 秒」,表示从周期开始后第 3 分钟到第 12 分钟之间,若价格进入 [minPrice, maxPrice] 才下单;第 0~3 分钟、第 12~15 分钟即使价格满足也不下单。5 分钟市场同理,可选 0~5 分钟内的一段(如 0~2、2~5)。前端用下拉选择「分钟」+「秒」,后端存为相对周期起点的秒数(如 windowStartSeconds、windowEndSeconds)。
### 3.2 校验与提交
- 提交前:市场为 5/15 分钟、**时间区间开始 ≤ 时间区间结束**、时间区间不超出周期长度(5min 市场结束 ≤ 5 分 0 秒,15min 市场结束 ≤ 15 分 0 秒)、minPrice 合法、maxPrice 若填则 ≥ minPrice、余额/比例合法。
- 提交后:关闭弹窗、刷新列表、`message.success`;失败在表单上展示接口错误信息。
参考:`CopyTradingOrders/AddModal.tsx` 的 Form 布局、`Form.Item` + `rules`、条件显示(比例/固定金额)。
---
## 4. 触发记录
**入口**:列表行操作「查看触发记录」或单独 Tab/页。
### 4.1 展示方式(二选一或并存)
- **弹窗**Modal 内 Table,按策略 ID 拉取该策略的触发记录。
- **独立页**:路由如 `/crypto-tail-strategy/records/:strategyId`,页面内 Table 或 Card 列表。
### 4.2 记录列表字段
| 列/项 | 说明 |
|-------|------|
| 触发时间 | 时间戳格式化为本地时间。 |
| 市场 | 市场标题 + 周期。 |
| 方向 (outcome) | Up / Down。 |
| 触发价格 | 当时进入区间的价格。 |
| 投入金额 | USDC,用 `formatUSDC`。 |
| 订单 ID | 若有;可截断 + Tooltip 全量。 |
| 状态 | 成功 / 失败。 |
支持按时间范围、状态筛选;移动端用 Card 或折叠列表。
---
## 5. 组件与技术要点
| 要点 | 说明 |
|------|------|
| **钱包提示** | 列表页与新增/编辑表单**必须**包含「使用单独钱包」的 Alert 提示,避免用户用混用钱包导致异常;文案走多语言。 |
| **创建前检查** | 点击「新增策略」时先检查自动赎回/Builder API 是否已配置;未配置则弹出简易「去配置」弹窗,引导用户到系统设置配置 API Key 与自动赎回,不打开策略表单。 |
| 多语言 | 所有文案 `t('cryptoTailStrategy.xxx')`,在 `locales/zh-CN``zh-TW``en``common.json` 中增加键。需包含:`cryptoTailStrategy.list.walletTip``cryptoTailStrategy.form.walletTip`,以及 `cryptoTailStrategy.redeemRequiredModal.title``cryptoTailStrategy.redeemRequiredModal.description``cryptoTailStrategy.redeemRequiredModal.goToSettings``cryptoTailStrategy.redeemRequiredModal.cancel`。文案示例:列表页 `walletTip`:「请使用单独的钱包运行尾盘策略,避免该钱包用于手动交易、跟单等其他操作,否则可能导致余额或仓位变化,造成策略执行异常。」表单内 `walletTip`:「建议使用专用钱包,避免手动操作等导致余额或下单异常。」未配置赎回弹窗 `title`:「请先配置自动赎回」;`description`:「尾盘策略依赖自动赎回功能,请先在系统设置中配置 Builder API Key 并开启自动赎回。」;`goToSettings`:「去配置」;`cancel`:「取消」。 |
| 金额 | 统一 `formatUSDC`(见 frontend.mdc)。 |
| 响应式 | `useMediaQuery`;按钮触摸目标 ≥ 44px;移动端主操作突出。 |
| 类型 | 不用 `any`;为策略、触发记录定义 TypeScript 类型。 |
| API | 通过 `apiService` 封装(如 `apiService.cryptoTailStrategy.list/create/update/delete/records`)。 |
---
## 6. 页面与文件建议对应
| 功能 | 建议路径/文件 |
|------|----------------|
| 列表页 | `frontend/src/pages/CryptoTailStrategyList.tsx` |
| 未配置赎回时的简易弹窗 | 内嵌在列表页的 Modal,或 `CryptoTailStrategyList/RedeemRequiredModal.tsx` |
| 新增/编辑弹窗 | `frontend/src/pages/CryptoTailStrategyList/FormModal.tsx` 或内嵌 Modal |
| 触发记录 | `frontend/src/pages/CryptoTailStrategyList/TriggerRecordsModal.tsx``CryptoTailStrategyRecords.tsx` |
| 路由 | `App.tsx``/crypto-tail-strategy`、可选 `/crypto-tail-strategy/records/:id` |
| 菜单 | `Layout.tsx` 中增加「尾盘策略」菜单项 |
| 类型 | `frontend/src/types/index.ts``types/cryptoTailStrategy.ts` 中增加策略与触发记录类型 |
| 多语言 | `frontend/src/locales/{zh-CN,zh-TW,en}/common.json` 中增加 `cryptoTailStrategy.*` |
---
## 7. 小结:UI 包含的主要元素
- **导航**:主导航中「尾盘策略」入口。
- **列表页**:标题、钱包提示 Alert、新增按钮(点击前先检查赎回配置,未配置则弹「去配置」简易弹窗)、筛选、表格/卡片(策略名、市场、价格区间、投入方式、状态、最近触发、操作)、加载与空状态。
- **未配置赎回弹窗**:简易 Modal,提示依赖自动赎回、需先配置 Builder API Key 与自动赎回;按钮「去配置」(跳转 `/system-settings`)、「取消」。
- **表单弹窗**:策略名、账户、市场选择、minPrice/maxPrice、投入方式(比例/固定)、启用开关、提交/取消。
- **触发记录**:时间、市场、outcome、触发价格、金额、订单 ID、状态;支持弹窗或独立页。
- **通用**Ant Design 组件、响应式、多语言、formatUSDC、TypeScript 类型。
---
## 附录 A 后端/产品要求:自动赎回须支持本策略仓位
自动赎回逻辑**必须支持赎回由尾盘策略产生的订单所对应的仓位**。即:本策略触发的市价买入会形成仓位,这些仓位在满足「可赎回」条件时,应被纳入现有自动赎回流程并正常发起赎回,不得因来源为「尾盘策略」而被排除。后端实现时需保证:
- 尾盘策略下单产生的仓位,与跟单/手动下单等来源的仓位一视同仁,参与可赎回查询与批量赎回;
- 若当前自动赎回按账户或仓位类型过滤,需将「尾盘策略订单产生的仓位」包含在内。
这样前端所依赖的「自动赎回」对该策略才完整有效。
-452
View File
@@ -1,452 +0,0 @@
# 尾盘策略配置指南
## 一、什么是尾盘策略?
尾盘策略是一种自动化交易策略,专门用于 Polymarket 加密市场的 **5分钟****15分钟** "Up or Down" 市场。
**核心逻辑**:在指定时间窗口内,当市场价格进入您设定的价格区间时,系统会自动以固定价格(0.99)买入,无需手动操作。
**适用场景**
- 您希望捕捉市场在周期末段的价格波动
- 您想自动化执行交易,避免手动盯盘
- 您对市场走势有一定判断,希望设置条件自动触发
---
## 二、策略工作原理
### 2.1 基本流程
```
周期开始 → 时间窗口内 → 价格进入区间 → 自动下单
```
1. **周期**:每个市场按固定周期运行(5分钟或15分钟)
- 5分钟市场:每5分钟为一个周期(如 10:00、10:05、10:10...
- 15分钟市场:每15分钟为一个周期(如 10:00、10:15、10:30...
2. **时间窗口**:您可以在周期内设置一个时间段
- 例如:15分钟市场,设置窗口为「3分钟~12分钟」
- 表示:从周期开始后第3分钟到第12分钟之间才会触发
3. **价格区间**:设置触发价格范围
- 例如:最低价 0.50,最高价 0.80
- 表示:当市场价格在 0.50~0.80 之间时才会触发
4. **自动下单**:满足条件后,系统自动以 0.99 的价格买入
### 2.2 重要限制
- **每周期最多触发一次**:同一个周期内,即使多次满足条件,也只下单一次
- **固定下单价格**:所有订单都以 0.99 的价格提交
- **需要单独钱包**:建议使用专门的钱包运行尾盘策略,避免与其他操作(手动交易、跟单等)冲突
---
## 三、参数详细说明
### 3.1 基础参数
| 参数 | 说明 | 必填 | 示例 |
|------|------|------|------|
| **账户** | 选择用于交易的钱包账户 | ✅ | 账户A |
| **策略名称** | 给策略起个名字,方便识别 | ❌ | "BTC 15分钟尾盘策略" |
| **市场** | 选择要交易的市场(5分钟或15分钟) | ✅ | btc-updown-15m |
### 3.2 周期设置
| 参数 | 说明 | 必填 | 示例 |
|------|------|------|------|
| **周期长度** | 由选择的市场自动确定 | ✅ | 15分钟(900秒) |
| **时间窗口开始** | 从周期起点算起,多少分钟后开始监听 | ✅ | 3分0秒 |
| **时间窗口结束** | 从周期起点算起,多少分钟后停止监听 | ✅ | 12分0秒 |
**时间窗口说明**
- 5分钟市场:可选 0~5 分钟内的任意时间段
- 15分钟市场:可选 0~15 分钟内的任意时间段
- **开始时间必须 ≤ 结束时间**
- 窗口外的时间即使价格满足也不会触发
**示例**
- 15分钟市场,窗口「3分0秒 ~ 12分0秒」
- 周期开始后 03 分钟:不监听
- 周期开始后 3~12 分钟:监听价格,满足条件即触发
- 周期开始后 12~15 分钟:不监听
### 3.3 价格区间
| 参数 | 说明 | 必填 | 取值范围 | 示例 |
|------|------|------|----------|------|
| **最低价 (minPrice)** | 触发的最低价格 | ✅ | 01 | 0.50 |
| **最高价 (maxPrice)** | 触发的最高价格 | ❌ | 01,默认1 | 0.80 |
**价格区间说明**
- 价格范围是 01 之间的小数
- 当市场价格在 [最低价, 最高价] 区间内时才会触发
- 如果不填最高价,默认使用 1.0(即只要价格 ≥ 最低价就触发)
**示例**
- 最低价 0.50,最高价 0.80
- 价格 0.45:不触发(低于最低价)
- 价格 0.60:触发 ✅(在区间内)
- 价格 0.85:不触发(高于最高价)
### 3.4 投入金额
| 参数 | 说明 | 必填 | 示例 |
|------|------|------|------|
| **投入方式** | 选择按比例或固定金额 | ✅ | 按比例 / 固定金额 |
| **比例 (%)** | 按账户余额的百分比投入 | 条件必填 | 10%(账户有100 USDC,投入10 USDC |
| **固定金额 (USDC)** | 每次固定投入的金额 | 条件必填 | 50 USDC |
**投入方式说明**
**方式一:按比例 (RATIO)**
- 每次触发时,按账户当前可用余额的百分比投入
- 例如:账户有 100 USDC,设置比例 10%
- 第1次触发:投入 10 USDC
- 第2次触发:如果余额变为 90 USDC,投入 9 USDC
- **优点**:自动适应账户余额变化
- **缺点**:每次投入金额可能不同
**方式二:固定金额 (FIXED)**
- 每次触发时,固定投入指定金额
- 例如:设置固定金额 50 USDC
- 每次触发都投入 50 USDC
- **优点**:投入金额稳定,便于管理
- **缺点**:需要确保账户余额充足
**注意事项**
- 最小下单金额:至少 1 USDC
- 如果账户余额不足,下单会失败并记录失败原因
### 3.5 最小价差(高级功能)
最小价差功能用于过滤市场波动,只在币安 BTC/USDC 价格波动达到一定幅度时才触发。
| 参数 | 说明 | 必填 | 示例 |
|------|------|------|------|
| **最小价差模式** | 选择价差校验方式 | ✅ | 无 / 固定 / 自动 |
| **最小价差值** | 固定模式时填写 | 条件必填 | 30 |
**三种模式说明**
**模式一:无 (NONE)**
- 不进行价差校验
- 只要时间窗口和价格区间满足就触发
- **适合**:不关心币安价格波动,只看 Polymarket 价格
**模式二:固定 (FIXED)**
- 设置一个固定的最小价差值(单位:USDC)
- 当币安 BTC/USDC 的 K 线价差(|收盘价 - 开盘价|)≥ 设定值时,才触发
- **示例**:设置 30
- 周期内币安 K 线价差 ≥ 30:触发 ✅
- 周期内币安 K 线价差 < 30:不触发
- **适合**:您知道期望的价差阈值
**模式三:自动 (AUTO)**
- 系统根据历史 20 根 K 线自动计算最小价差
- 计算逻辑:
1. 获取最近 20 根 K 线(与策略周期一致)
2. 按方向筛选(Up 方向只看上涨的 K 线,Down 方向只看下跌的 K 线)
3. 剔除异常值(使用 IQR 方法)
4. 计算平均价差 × 0.8 作为最小价差
- **适合**:希望根据历史数据自动调整,无需手动设置
**价差说明**
- 价差 = |收盘价 - 开盘价|
- 例如:开盘价 50000,收盘价 50030,价差 = 30
- 价差越大,说明该周期内价格波动越大
---
## 四、配置示例
### 示例1:简单策略(5分钟市场)
**场景**:在 5 分钟市场的最后 2 分钟,如果价格低于 0.60,自动买入 10 USDC
**配置**
```
账户:账户A
策略名称:BTC 5分钟简单策略
市场:btc-updown-5m
时间窗口:3分0秒 5分0秒
最低价:0.00
最高价:0.60
投入方式:固定金额
固定金额:10 USDC
最小价差模式:无
启用状态:开启
```
**说明**
- 周期开始后 03 分钟:不监听
- 周期开始后 3~5 分钟:如果价格 ≤ 0.60,自动买入 10 USDC
---
### 示例2:比例投入策略(15分钟市场)
**场景**:在 15 分钟市场的中段(5~10分钟),如果价格在 0.40~0.70 之间,投入账户余额的 15%
**配置**
```
账户:账户B
策略名称:BTC 15分钟比例策略
市场:btc-updown-15m
时间窗口:5分0秒 10分0秒
最低价:0.40
最高价:0.70
投入方式:按比例
比例:15%
最小价差模式:无
启用状态:开启
```
**说明**
- 假设账户余额 100 USDC
- 周期开始后 5~10 分钟:如果价格在 0.40~0.70 之间,自动买入约 15 USDC100 × 15%
---
### 示例3:带价差过滤的策略(15分钟市场)
**场景**:在 15 分钟市场的后段(10~14分钟),如果价格在 0.50~0.80 之间,且币安价差 ≥ 50,投入 20 USDC
**配置**
```
账户:账户C
策略名称:BTC 15分钟价差策略
市场:btc-updown-15m
时间窗口:10分0秒 14分0秒
最低价:0.50
最高价:0.80
投入方式:固定金额
固定金额:20 USDC
最小价差模式:固定
最小价差值:50
启用状态:开启
```
**说明**
- 周期开始后 10~14 分钟:同时满足以下条件才触发
1. 价格在 0.500.80 之间 ✅
2. 币安 BTC/USDC 价差 ≥ 50 ✅
- 如果价差只有 30,即使价格满足也不会触发
---
### 示例4:自动价差策略(15分钟市场)
**场景**:在 15 分钟市场的前段(2~8分钟),如果价格在 0.30~0.90 之间,投入账户余额的 20%,价差由系统自动计算
**配置**
```
账户:账户D
策略名称:BTC 15分钟自动价差策略
市场:btc-updown-15m
时间窗口:2分0秒 8分0秒
最低价:0.30
最高价:0.90
投入方式:按比例
比例:20%
最小价差模式:自动
启用状态:开启
```
**说明**
- 系统会根据历史 20 根 K 线自动计算最小价差
- 周期开始后 2~8 分钟:同时满足以下条件才触发
1. 价格在 0.300.90 之间 ✅
2. 币安 BTC/USDC 价差 ≥ 系统计算的最小价差 ✅
---
## 五、常见问题
### Q1:策略什么时候会触发?
**A**:需要同时满足以下条件:
1. ✅ 当前时间在时间窗口内
2. ✅ 市场价格在 [最低价, 最高价] 区间内
3. ✅ 本周期尚未触发过(每周期最多触发一次)
4. ✅ 如果设置了最小价差,币安价差也需要满足条件
### Q2:为什么我的策略没有触发?
**可能原因**
1. **时间窗口不对**:当前时间不在设定的时间窗口内
2. **价格不在区间**:市场价格不在 [最低价, 最高价] 范围内
3. **本周期已触发**:该周期已经触发过一次,不会再触发
4. **价差不满足**:如果设置了最小价差,币安价差未达到要求
5. **账户余额不足**:账户余额小于设定的投入金额
6. **策略未启用**:检查策略的启用状态是否为"开启"
### Q3:每周期最多触发一次是什么意思?
**A**:每个周期(5分钟或15分钟)内,即使多次满足条件,也只下单一次。
**示例**
- 15分钟市场,周期从 10:00 开始
- 10:05 时价格满足条件,触发下单 ✅
- 10:08 时价格再次满足条件,但不会再次下单(本周期已触发)
- 10:15 开始新周期,可以再次触发
### Q4:固定金额和按比例有什么区别?
**固定金额**
- 每次触发都投入相同金额
- 例如:设置 50 USDC,每次都是 50 USDC
- 需要确保账户余额充足
**按比例**
- 每次触发时按账户余额的百分比投入
- 例如:设置 10%,账户有 100 USDC 时投入 10 USDC,余额变为 90 USDC 后下次投入 9 USDC
- 自动适应余额变化
### Q5:最小价差功能有什么用?
**A**:最小价差用于过滤市场波动,只在币安 BTC/USDC 价格波动达到一定幅度时才触发。
**场景**
- 如果市场波动很小(价差 < 设定值),可能不值得交易
- 通过设置最小价差,可以避免在波动过小时触发
**三种模式选择建议**
- **无**:不关心币安价格波动,只看 Polymarket 价格
- **固定**:您知道期望的价差阈值(例如:至少波动 30 USDC 才值得交易)
- **自动**:希望根据历史数据自动调整,无需手动设置
### Q6:为什么建议使用单独的钱包?
**A**:避免以下问题:
1. **余额变化**:如果钱包同时用于手动交易,余额变化可能影响策略执行
2. **仓位冲突**:手动交易和策略交易可能产生冲突
3. **管理混乱**:难以区分哪些订单是策略产生的,哪些是手动产生的
**建议**:创建一个专门的钱包,只用于尾盘策略。
### Q7:下单价格为什么是固定的 0.99?
**A**:这是策略的设计特点:
- 0.99 是市场中的最高价格(接近 1.0)
- 以最高价买入可以确保订单快速成交
- 虽然买入价格较高,但策略的核心是捕捉市场波动,而非追求最优价格
### Q8:策略需要依赖自动赎回功能吗?
**A**:是的,尾盘策略依赖自动赎回功能。
**原因**
- 策略下单后会形成仓位
- 这些仓位需要在市场结算后自动赎回
- 如果未配置自动赎回,仓位可能无法及时赎回
**配置要求**
- 在「系统设置」中配置 Builder API Key
- 开启自动赎回功能
---
## 六、注意事项
### 6.1 账户要求
- ✅ 账户必须配置 API Key、API Secret、API Passphrase
- ✅ 账户必须有足够的 USDC 余额
- ✅ 建议使用专门的钱包,避免与其他操作冲突
### 6.2 时间窗口设置
- ⚠️ 开始时间必须 ≤ 结束时间
- ⚠️ 时间窗口不能超出周期长度(5分钟市场 ≤ 5分钟,15分钟市场 ≤ 15分钟)
- ⚠️ 建议设置合理的时间窗口,避免在周期开始或结束时触发
### 6.3 价格区间设置
- ⚠️ 最低价必须 ≤ 最高价
- ⚠️ 价格范围是 0~1 之间的小数
- ⚠️ 建议根据市场情况设置合理的价格区间
### 6.4 投入金额设置
- ⚠️ 最小下单金额:至少 1 USDC
- ⚠️ 确保账户余额充足,避免下单失败
- ⚠️ 按比例模式:注意账户余额变化对投入金额的影响
### 6.5 最小价差设置
- ⚠️ 固定模式:需要填写合理的价差值(单位:USDC)
- ⚠️ 自动模式:系统会在周期开始时自动计算,无需手动设置
- ⚠️ 价差设置过大可能导致策略难以触发
### 6.6 其他注意事项
- ⚠️ 策略创建后默认启用,如需暂停可以关闭"启用状态"
- ⚠️ 每周期最多触发一次,请合理设置触发条件
- ⚠️ 策略依赖自动赎回功能,请确保已配置 Builder API Key
- ⚠️ 建议定期查看触发记录,了解策略执行情况
---
## 七、策略管理
### 7.1 查看策略列表
在「尾盘策略」页面可以查看所有策略:
- 策略名称
- 市场信息
- 时间窗口
- 价格区间
- 投入方式
- 启用状态
- 最后触发时间
- 总收益、胜率等统计信息
### 7.2 查看触发记录
点击策略可以查看详细的触发记录:
- 触发时间
- 市场价格
- 投入金额
- 订单ID
- 订单状态(成功/失败)
- 结算信息(盈亏、胜率等)
### 7.3 编辑策略
可以随时修改策略参数:
- 时间窗口
- 价格区间
- 投入方式
- 最小价差设置
- 启用状态
**注意**:修改后的策略会在下一个周期生效。
### 7.4 删除策略
删除策略后:
- 策略配置会被删除
- 历史触发记录会保留
- 已下单的订单不受影响
---
## 八、总结
尾盘策略是一个强大的自动化交易工具,可以帮助您:
1. **自动化交易**:无需手动盯盘,系统自动执行
2. **精准控制**:通过时间窗口和价格区间精确控制触发条件
3. **灵活配置**:支持比例和固定金额两种投入方式
4. **风险过滤**:通过最小价差功能过滤市场波动
**使用建议**
- 初次使用建议从简单策略开始(无价差过滤)
- 熟悉后再尝试添加价差过滤功能
- 定期查看触发记录,根据实际情况调整策略参数
- 使用专门的钱包,避免与其他操作冲突
**祝您交易顺利!** 🚀