docs: 新增体育尾盘策略文档
- 新增 docs/sports-tail-strategy/ 目录 - 包含 README.md 目录说明 - 包含 zh/ 下的中文文档: - sports-tail-strategy-tasks.md (任务与验收) - sports-tail-strategy-ui-spec.md (UI 规格) - sports-tail-strategy-flow.md (流程说明) - sports-tail-strategy-market-data.md (市场数据与订阅) - sports-tail-strategy-api.md (后端 API 定义) Made-with: Cursor
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
# 体育尾盘策略文档 (Sports Tail Strategy)
|
||||
|
||||
本目录集中存放与 Polymarket 体育市场尾盘策略相关的文档。
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
sports-tail-strategy/
|
||||
├── README.md # 本说明
|
||||
└── zh/ # 中文文档
|
||||
├── sports-tail-strategy-tasks.md # 任务与验收
|
||||
├── sports-tail-strategy-ui-spec.md # UI 规格
|
||||
├── sports-tail-strategy-flow.md # 流程说明
|
||||
└── sports-tail-strategy-market-data.md # 市场数据与订阅
|
||||
```
|
||||
|
||||
## 文档说明
|
||||
|
||||
| 文档 | 说明 |
|
||||
|------|------|
|
||||
| **tasks** (zh) | 开发任务与验收项 |
|
||||
| **ui-spec** (zh) | 前端列表、表单、触发记录等 UI 规格 |
|
||||
| **flow** (zh) | 策略整体流程(创建→触发→止盈止损→完成) |
|
||||
| **market-data** (zh) | Gamma API 数据获取、WebSocket 订阅、价格监控 |
|
||||
|
||||
## 功能概述
|
||||
|
||||
体育尾盘策略用于在体育市场接近尾盘(胜率 90%+)时自动买入,利用高胜率市场低风险获利。
|
||||
|
||||
### 核心特性
|
||||
|
||||
1. **不区分方向**:只设置触发价格,系统自动监控两个方向,任意方向达到触发价即买入
|
||||
2. **实时订阅**:通过 WebSocket 订阅订单簿,实时监控价格变化
|
||||
3. **止盈止损**:支持设置止盈/止损价格,自动卖出
|
||||
4. **订阅管理**:同一市场多策略共享订阅,无策略时自动取消订阅
|
||||
|
||||
### 适用场景
|
||||
|
||||
- 体育比赛接近尾声,一方胜率 90%+ 时买入
|
||||
- 大小分市场接近尾盘时套利
|
||||
- 低风险稳定收益场景
|
||||
@@ -0,0 +1,356 @@
|
||||
# 体育尾盘策略 - API 设计
|
||||
|
||||
## 一、后端 API
|
||||
|
||||
### 1.1 策略管理
|
||||
|
||||
#### 列表
|
||||
```
|
||||
POST /api/sports-tail-strategy/list
|
||||
```
|
||||
|
||||
**请求**:
|
||||
```typescript
|
||||
interface StrategyListRequest {
|
||||
accountId?: number; // 筛选账户
|
||||
sport?: string; // 筛选类别
|
||||
}
|
||||
```
|
||||
|
||||
**响应**:
|
||||
```typescript
|
||||
interface StrategyListResponse {
|
||||
list: StrategyDto[];
|
||||
}
|
||||
|
||||
interface StrategyDto {
|
||||
id: number;
|
||||
accountId: number;
|
||||
accountName: string;
|
||||
conditionId: string;
|
||||
marketTitle: string;
|
||||
eventSlug: string;
|
||||
triggerPrice: string;
|
||||
amountMode: "FIXED" | "RATIO";
|
||||
amountValue: string;
|
||||
takeProfitPrice: string | null;
|
||||
stopLossPrice: string | null;
|
||||
|
||||
// 成交信息
|
||||
filled: boolean;
|
||||
filledPrice: string | null;
|
||||
filledOutcomeIndex: number | null;
|
||||
filledOutcomeName: string | null;
|
||||
filledAmount: string | null;
|
||||
filledShares: string | null;
|
||||
filledAt: number | null;
|
||||
|
||||
// 卖出信息
|
||||
sold: boolean;
|
||||
sellPrice: string | null;
|
||||
sellType: string | null;
|
||||
sellAmount: string | null;
|
||||
realizedPnl: string | null;
|
||||
soldAt: number | null;
|
||||
|
||||
// 实时价格(未成交时返回)
|
||||
realtimeYesPrice: string | null;
|
||||
realtimeNoPrice: string | null;
|
||||
|
||||
createdAt: number;
|
||||
updatedAt: number;
|
||||
}
|
||||
```
|
||||
|
||||
#### 创建
|
||||
```
|
||||
POST /api/sports-tail-strategy/create
|
||||
```
|
||||
|
||||
**请求**:
|
||||
```typescript
|
||||
interface StrategyCreateRequest {
|
||||
accountId: number; // 账户ID
|
||||
conditionId: string; // 市场ID
|
||||
marketTitle: string; // 市场标题
|
||||
eventSlug?: string; // 事件slug
|
||||
triggerPrice: string; // 触发价格
|
||||
amountMode: "FIXED" | "RATIO";
|
||||
amountValue: string; // 金额值
|
||||
takeProfitPrice?: string; // 止盈价格
|
||||
stopLossPrice?: string; // 止损价格
|
||||
}
|
||||
```
|
||||
|
||||
**响应**:
|
||||
```typescript
|
||||
interface StrategyCreateResponse {
|
||||
id: number;
|
||||
}
|
||||
```
|
||||
|
||||
#### 删除
|
||||
```
|
||||
POST /api/sports-tail-strategy/delete
|
||||
```
|
||||
|
||||
**请求**:
|
||||
```typescript
|
||||
interface StrategyDeleteRequest {
|
||||
id: number;
|
||||
}
|
||||
```
|
||||
|
||||
**响应**:
|
||||
```typescript
|
||||
interface StrategyDeleteResponse {
|
||||
success: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 1.2 市场数据
|
||||
|
||||
#### 体育类别列表
|
||||
```
|
||||
POST /api/sports-tail-strategy/sports-list
|
||||
```
|
||||
|
||||
**响应**:
|
||||
```typescript
|
||||
interface SportsListResponse {
|
||||
list: SportDto[];
|
||||
}
|
||||
|
||||
interface SportDto {
|
||||
sport: string; // 类别标识:nba, nfl, epl...
|
||||
image: string; // 图标URL
|
||||
tagId: number; // 主Tag ID
|
||||
name: string; // 显示名称(多语言)
|
||||
}
|
||||
```
|
||||
|
||||
#### 市场搜索
|
||||
```
|
||||
POST /api/sports-tail-strategy/market-search
|
||||
```
|
||||
|
||||
**请求**:
|
||||
```typescript
|
||||
interface MarketSearchRequest {
|
||||
sport?: string; // 体育类别
|
||||
endDateMin?: string; // 最小结束时间 ISO 8601
|
||||
endDateMax?: string; // 最大结束时间 ISO 8601
|
||||
minLiquidity?: string; // 最小流动性
|
||||
keyword?: string; // 搜索关键词
|
||||
limit?: number; // 返回数量,默认50
|
||||
}
|
||||
```
|
||||
|
||||
**响应**:
|
||||
```typescript
|
||||
interface MarketSearchResponse {
|
||||
list: MarketDto[];
|
||||
}
|
||||
|
||||
interface MarketDto {
|
||||
conditionId: string;
|
||||
question: string;
|
||||
outcomes: string[]; // ["Yes", "No"] 或 ["Over", "Under"]
|
||||
outcomePrices: string[]; // 当前价格
|
||||
endDate: string; // 结束时间 ISO 8601
|
||||
liquidity: string; // 流动性
|
||||
bestBid: number | null;
|
||||
bestAsk: number | null;
|
||||
yesTokenId: string;
|
||||
noTokenId: string;
|
||||
}
|
||||
```
|
||||
|
||||
#### 市场详情
|
||||
```
|
||||
POST /api/sports-tail-strategy/market-detail
|
||||
```
|
||||
|
||||
**请求**:
|
||||
```typescript
|
||||
interface MarketDetailRequest {
|
||||
conditionId: string;
|
||||
}
|
||||
```
|
||||
|
||||
**响应**:
|
||||
```typescript
|
||||
interface MarketDetailResponse {
|
||||
conditionId: string;
|
||||
question: string;
|
||||
outcomes: string[];
|
||||
outcomePrices: string[];
|
||||
endDate: string;
|
||||
liquidity: string;
|
||||
bestBid: number | null;
|
||||
bestAsk: number | null;
|
||||
yesTokenId: string;
|
||||
noTokenId: string;
|
||||
eventSlug: string | null;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 1.3 触发记录
|
||||
|
||||
#### 全局记录列表
|
||||
```
|
||||
POST /api/sports-tail-strategy/triggers
|
||||
```
|
||||
|
||||
**请求**:
|
||||
```typescript
|
||||
interface TriggerListRequest {
|
||||
accountId?: number; // 筛选账户
|
||||
status?: string; // 筛选状态: SUCCESS/FAIL
|
||||
startTime?: number; // 开始时间戳
|
||||
endTime?: number; // 结束时间戳
|
||||
page?: number; // 页码,默认1
|
||||
pageSize?: number; // 每页数量,默认20
|
||||
}
|
||||
```
|
||||
|
||||
**响应**:
|
||||
```typescript
|
||||
interface TriggerListResponse {
|
||||
total: number;
|
||||
list: TriggerDto[];
|
||||
}
|
||||
|
||||
interface TriggerDto {
|
||||
id: number;
|
||||
strategyId: number;
|
||||
|
||||
// 市场信息
|
||||
marketTitle: string;
|
||||
conditionId: string;
|
||||
|
||||
// 买入信息
|
||||
buyPrice: string;
|
||||
outcomeIndex: number;
|
||||
outcomeName: string | null;
|
||||
buyAmount: string;
|
||||
buyShares: string | null;
|
||||
buyStatus: "PENDING" | "SUCCESS" | "FAIL";
|
||||
|
||||
// 卖出信息
|
||||
sellPrice: string | null;
|
||||
sellType: string | null; // TAKE_PROFIT/STOP_LOSS/MANUAL
|
||||
sellAmount: string | null;
|
||||
sellStatus: string | null;
|
||||
|
||||
// 盈亏
|
||||
realizedPnl: string | null;
|
||||
|
||||
// 时间
|
||||
triggeredAt: number;
|
||||
soldAt: number | null;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 二、前端 API 封装
|
||||
|
||||
### 2.1 apiService 方法
|
||||
|
||||
```typescript
|
||||
// 策略管理
|
||||
sportsTailStrategyList(params: StrategyListRequest): Promise<StrategyListResponse>
|
||||
sportsTailStrategyCreate(data: StrategyCreateRequest): Promise<StrategyCreateResponse>
|
||||
sportsTailStrategyDelete(id: number): Promise<StrategyDeleteResponse>
|
||||
|
||||
// 市场数据
|
||||
sportsTailStrategySportsList(): Promise<SportsListResponse>
|
||||
sportsTailStrategyMarketSearch(params: MarketSearchRequest): Promise<MarketSearchResponse>
|
||||
sportsTailStrategyMarketDetail(conditionId: string): Promise<MarketDetailResponse>
|
||||
|
||||
// 触发记录
|
||||
sportsTailStrategyTriggers(params: TriggerListRequest): Promise<TriggerListResponse>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、多语言 Key
|
||||
|
||||
### 3.1 页面标题
|
||||
```
|
||||
sportsTailStrategy.list.title=体育尾盘策略
|
||||
sportsTailStrategy.list.addStrategy=新增策略
|
||||
sportsTailStrategy.list.filter.account=账户
|
||||
sportsTailStrategy.list.filter.sport=类别
|
||||
sportsTailStrategy.list.filter.all=全部
|
||||
```
|
||||
|
||||
### 3.2 表单字段
|
||||
```
|
||||
sportsTailStrategy.form.account=账户
|
||||
sportsTailStrategy.form.market=市场
|
||||
sportsTailStrategy.form.triggerPrice=触发价格
|
||||
sportsTailStrategy.form.amount=金额
|
||||
sportsTailStrategy.form.amountMode=金额模式
|
||||
sportsTailStrategy.form.fixed=固定金额
|
||||
sportsTailStrategy.form.ratio=余额比例
|
||||
sportsTailStrategy.form.takeProfit=止盈价格
|
||||
sportsTailStrategy.form.stopLoss=止损价格
|
||||
sportsTailStrategy.form.autoSell=自动卖出
|
||||
```
|
||||
|
||||
### 3.3 列表字段
|
||||
```
|
||||
sportsTailStrategy.list.triggerPrice=触发价
|
||||
sportsTailStrategy.list.amount=金额
|
||||
sportsTailStrategy.list.takeProfitStopLoss=止盈/止损
|
||||
sportsTailStrategy.list.filledPrice=成交价
|
||||
sportsTailStrategy.list.shares=份
|
||||
sportsTailStrategy.list.pnl=盈亏
|
||||
sportsTailStrategy.list.realtimePrice=实时价格
|
||||
sportsTailStrategy.list.pending=待结算
|
||||
sportsTailStrategy.list.viewRecords=查看记录
|
||||
sportsTailStrategy.list.delete=删除
|
||||
```
|
||||
|
||||
### 3.4 市场筛选
|
||||
```
|
||||
sportsTailStrategy.market.filter.sport=类别
|
||||
sportsTailStrategy.market.filter.allSports=全部类别
|
||||
sportsTailStrategy.market.filter.endTime=结束时间
|
||||
sportsTailStrategy.market.filter.today=今天
|
||||
sportsTailStrategy.market.filter.next24h=未来24小时
|
||||
sportsTailStrategy.market.filter.next7days=未来7天
|
||||
sportsTailStrategy.market.filter.minLiquidity=最小流动性
|
||||
sportsTailStrategy.market.filter.keyword=关键词
|
||||
sportsTailStrategy.market.filter.search=搜索
|
||||
sportsTailStrategy.market.select=选择市场
|
||||
```
|
||||
|
||||
### 3.5 触发记录
|
||||
```
|
||||
sportsTailStrategy.records.title=触发记录
|
||||
sportsTailStrategy.records.market=市场
|
||||
sportsTailStrategy.records.direction=方向
|
||||
sportsTailStrategy.records.buyPrice=买入价
|
||||
sportsTailStrategy.records.buyAmount=买入金额
|
||||
sportsTailStrategy.records.sellPrice=卖出价
|
||||
sportsTailStrategy.records.sellType=卖出类型
|
||||
sportsTailStrategy.records.pnl=盈亏
|
||||
sportsTailStrategy.records.time=时间
|
||||
sportsTailStrategy.records.status=状态
|
||||
```
|
||||
|
||||
### 3.6 消息提示
|
||||
```
|
||||
sportsTailStrategy.message.createSuccess=策略创建成功
|
||||
sportsTailStrategy.message.deleteSuccess=策略删除成功
|
||||
sportsTailStrategy.message.deleteConfirm=确定删除该策略吗?
|
||||
sportsTailStrategy.message.noMarketSelected=请选择市场
|
||||
sportsTailStrategy.message.invalidPrice=价格格式无效
|
||||
```
|
||||
@@ -0,0 +1,262 @@
|
||||
# 体育尾盘策略 - 流程说明
|
||||
|
||||
## 一、策略状态流转
|
||||
|
||||
```
|
||||
┌─────────────┐ 价格>=触发价 ┌─────────────┐
|
||||
│ 待触发 │ ──────────────────▶ │ 已成交 │
|
||||
│ (filled=F) │ │ (filled=T) │
|
||||
└─────────────┘ └─────────────┘
|
||||
│ │
|
||||
│ ┌─────────────────┼─────────────────┐
|
||||
│ │ │ │
|
||||
▼ │ 有止盈止损 │ 无止盈止损 │
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌─────────────┐ ┌─────────────────────────────────────┐
|
||||
│ 保持订阅 │ │ 检查同市场是否有其他未完成策略 │
|
||||
│ 监控卖出 │ │ - 有: 保持订阅 │
|
||||
└─────────────┘ │ - 无: 取消订阅 │
|
||||
│ └─────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────────┐
|
||||
│ 价格 >= 止盈价 │
|
||||
│ 或 价格 <= 止损价 │
|
||||
└──────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────┐
|
||||
│ 自动卖出 │
|
||||
│ (sold=T) │
|
||||
└─────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 二、订阅生命周期
|
||||
|
||||
### 2.1 策略创建时
|
||||
|
||||
```
|
||||
策略创建
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ SubscriptionManager.subscribeStrategy(strategy) │
|
||||
│ 1. 检查市场是否已有订阅(marketSubscriptions 计数) │
|
||||
│ 2. 如果市场已有订阅:仅增加计数,不新建连接 │
|
||||
│ 3. 如果市场无订阅:建立 WebSocket 连接,订阅订单簿频道 │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.2 实时监控
|
||||
|
||||
```
|
||||
WebSocket 订单簿推送
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ SportsTailWebSocketHandler.onOrderbookUpdate() │
|
||||
│ 1. 解析消息,获取当前价格 │
|
||||
│ 2. 查找该 Token ID 对应的所有未完成策略 │
|
||||
│ 3. 遍历策略,检查触发条件: │
|
||||
│ - 未成交:检查买入条件(价格 >= 触发价) │
|
||||
│ - 已成交未卖出:检查卖出条件(止盈/止损) │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.3 成交后处理
|
||||
|
||||
```
|
||||
策略成交
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ SubscriptionManager.onStrategyFilled(strategy) │
|
||||
│ 1. 更新策略状态为已成交 │
|
||||
│ 2. 检查是否需要保持订阅: │
|
||||
│ - 有止盈止损:保持订阅,继续监控卖出条件 │
|
||||
│ - 无止盈止损:检查同市场是否有其他未完成策略 │
|
||||
│ - 有:保持订阅 │
|
||||
│ - 无:取消订阅,关闭 WebSocket 连接 │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.4 卖出后处理
|
||||
|
||||
```
|
||||
策略卖出
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ SubscriptionManager.onStrategySold(strategy) │
|
||||
│ 1. 更新策略状态为已卖出 │
|
||||
│ 2. 计算并记录盈亏 │
|
||||
│ 3. 检查同市场是否有其他未完成策略: │
|
||||
│ - 有:保持订阅 │
|
||||
│ - 无:取消订阅,关闭 WebSocket 连接 │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.5 策略删除时
|
||||
|
||||
```
|
||||
策略删除
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ SubscriptionManager.unsubscribeStrategy(strategy) │
|
||||
│ 1. 减少市场的订阅计数 │
|
||||
│ 2. 如果计数归零:取消订阅,关闭 WebSocket 连接 │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、买入逻辑(不区分方向)
|
||||
|
||||
### 3.1 触发条件检查
|
||||
|
||||
```kotlin
|
||||
fun checkBuyTrigger(strategy: SportsTailStrategy, yesPrice: BigDecimal, noPrice: BigDecimal): BuyTrigger? {
|
||||
// 如果已成交,不检查
|
||||
if (strategy.filled) return null
|
||||
|
||||
// 检查 YES 方向
|
||||
if (yesPrice >= strategy.triggerPrice) {
|
||||
return BuyTrigger(outcomeIndex = 0, price = yesPrice)
|
||||
}
|
||||
|
||||
// 检查 NO 方向
|
||||
if (noPrice >= strategy.triggerPrice) {
|
||||
return BuyTrigger(outcomeIndex = 1, price = noPrice)
|
||||
}
|
||||
|
||||
// 都不满足
|
||||
return null
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 执行买入
|
||||
|
||||
```kotlin
|
||||
suspend fun executeBuy(strategy: SportsTailStrategy, trigger: BuyTrigger) {
|
||||
// 1. 创建市价单
|
||||
val order = createMarketOrder(
|
||||
tokenId = getTokenId(strategy.conditionId, trigger.outcomeIndex),
|
||||
side = "BUY",
|
||||
amount = calculateAmount(strategy)
|
||||
)
|
||||
|
||||
// 2. 提交订单
|
||||
val result = clobApi.createOrder(order)
|
||||
|
||||
// 3. 更新策略状态
|
||||
strategy.filled = true
|
||||
strategy.filledPrice = trigger.price
|
||||
strategy.filledOutcomeIndex = trigger.outcomeIndex
|
||||
strategy.filledAmount = order.amount
|
||||
strategy.filledShares = calculateShares(order.amount, trigger.price)
|
||||
strategy.filledAt = System.currentTimeMillis()
|
||||
strategyRepository.save(strategy)
|
||||
|
||||
// 4. 记录触发记录
|
||||
createTriggerRecord(strategy, trigger, result)
|
||||
|
||||
// 5. 通知订阅管理器
|
||||
subscriptionManager.onStrategyFilled(strategy)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、卖出逻辑(止盈止损)
|
||||
|
||||
### 4.1 止盈止损检查
|
||||
|
||||
```kotlin
|
||||
fun checkSellTrigger(strategy: SportsTailStrategy, currentPrice: BigDecimal): SellTrigger? {
|
||||
// 如果已卖出或未成交,不检查
|
||||
if (strategy.sold || !strategy.filled) return null
|
||||
|
||||
// 只检查已买入方向的价格
|
||||
val filledTokenId = getTokenId(strategy.conditionId, strategy.filledOutcomeIndex)
|
||||
if (currentTokenId != filledTokenId) return null
|
||||
|
||||
// 检查止盈
|
||||
if (strategy.takeProfitPrice != null && currentPrice >= strategy.takeProfitPrice) {
|
||||
return SellTrigger(type = "TAKE_PROFIT", price = currentPrice)
|
||||
}
|
||||
|
||||
// 检查止损
|
||||
if (strategy.stopLossPrice != null && currentPrice <= strategy.stopLossPrice) {
|
||||
return SellTrigger(type = "STOP_LOSS", price = currentPrice)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 执行卖出
|
||||
|
||||
```kotlin
|
||||
suspend fun executeSell(strategy: SportsTailStrategy, trigger: SellTrigger) {
|
||||
// 1. 创建市价单
|
||||
val order = createMarketOrder(
|
||||
tokenId = getTokenId(strategy.conditionId, strategy.filledOutcomeIndex),
|
||||
side = "SELL",
|
||||
shares = strategy.filledShares
|
||||
)
|
||||
|
||||
// 2. 提交订单
|
||||
val result = clobApi.createOrder(order)
|
||||
|
||||
// 3. 计算盈亏
|
||||
val sellAmount = calculateSellAmount(trigger.price, strategy.filledShares)
|
||||
val pnl = sellAmount - strategy.filledAmount
|
||||
|
||||
// 4. 更新策略状态
|
||||
strategy.sold = true
|
||||
strategy.sellPrice = trigger.price
|
||||
strategy.sellType = trigger.type
|
||||
strategy.sellAmount = sellAmount
|
||||
strategy.realizedPnl = pnl
|
||||
strategy.soldAt = System.currentTimeMillis()
|
||||
strategyRepository.save(strategy)
|
||||
|
||||
// 5. 更新触发记录
|
||||
updateTriggerRecord(strategy, trigger, result, pnl)
|
||||
|
||||
// 6. 通知订阅管理器
|
||||
subscriptionManager.onStrategySold(strategy)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、关键设计要点
|
||||
|
||||
### 5.1 订阅共享
|
||||
|
||||
- 同一市场(conditionId)多个策略共享一个 WebSocket 订阅
|
||||
- 使用计数器管理订阅生命周期
|
||||
- 避免重复连接和资源浪费
|
||||
|
||||
### 5.2 不区分方向
|
||||
|
||||
- 只设置触发价格,不选择 YES/NO
|
||||
- 系统自动监控两个方向的价格
|
||||
- 任意方向满足条件即买入该方向
|
||||
|
||||
### 5.3 实时订阅
|
||||
|
||||
- 使用 WebSocket 订阅订单簿,实时接收价格变化
|
||||
- 不使用轮询方式
|
||||
- 响应速度快,延迟低
|
||||
|
||||
### 5.4 智能取消
|
||||
|
||||
- 无策略时取消订阅
|
||||
- 策略完成且无止盈止损时检查是否需要取消
|
||||
- 有止盈止损时保持订阅直到卖出
|
||||
@@ -0,0 +1,215 @@
|
||||
# 体育尾盘策略 - 市场数据与订阅
|
||||
|
||||
> 本文档描述体育市场数据获取方式、 WebSocket 订阅管理逻辑。
|
||||
|
||||
## 一、Gamma API 数据获取
|
||||
|
||||
> 本文档描述如何从 Gamma API 获取体育市场数据。### 1.1 萜索条件
|
||||
### 1.1 获取体育类别列表
|
||||
> **前端选择体育类别时,需要获取可选的体育类别列表供用户选择。
|
||||
```
|
||||
GET https://gamma-api.polymarket.com/sports
|
||||
```
|
||||
> **返回示例**:
|
||||
```json
|
||||
[
|
||||
{"sport": "nba", "image": "https://...", "tags": "1,745,100639"},
|
||||
{"sport": "nfl", "image": "https://...", "tags": "1,450,100639"},
|
||||
{"sport": "epl", "image": "https://...", "tags": "1,82,306,100639"},
|
||||
...
|
||||
]
|
||||
```
|
||||
|
||||
**主要类别**:
|
||||
| sport | 名称 | tag_id |
|
||||
|-------|------|--------|
|
||||
| nba | 篮球 NBA | 745 |
|
||||
| nfl | 美式足球 NFL | 450 |
|
||||
| epl | 英超 | 82 |
|
||||
| lal | 西甲 | 780 |
|
||||
| mlb | 棒球 MLB | 100381 |
|
||||
| nhl | 冰球 NHL | 899 |
|
||||
|
||||
| ufc | 格斗 UFC | 100639 |
|
||||
|
||||
### 1.2 按类别筛选市场
|
||||
> 根据用户选择的体育类别,使用 `tag_id` 参数筛选市场。
|
||||
```
|
||||
GET https://gamma-api.polymarket.com/markets?tag_id=745&active=true&closed=false&limit=50&order=endDate&ascending=true
|
||||
```
|
||||
> **返回字段**:
|
||||
- `id` - 市场ID
|
||||
- `question` - 市场问题
|
||||
- `conditionId` - 市场 conditionId
|
||||
- `outcomes` - 结果选项 `["Yes", "No"]` 或 `["Over", "Under"]`
|
||||
- `outcomePrices` - 当前价格 `["0.92", "0.08"]`
|
||||
- `endDate` - 结束时间
|
||||
- `bestBid` - 最佳买价
|
||||
- `bestAsk` - 最佳卖价
|
||||
- `clobTokenIds` - Token IDs
|
||||
- `gameStartTime` - 比赛开始时间
|
||||
- `sportsMarketType` - 市场类型
|
||||
- `liquidityNum` - 流动性
|
||||
- `volumeNum` - 成交量
|
||||
|
||||
- `events` - 关联事件信息
|
||||
|
||||
```
|
||||
|
||||
> **请求参数**:
|
||||
```kotlin
|
||||
data class SportsMarketSearchRequest(
|
||||
val sport: String? = null, // 体育类别: nba, nfl, epl...
|
||||
val marketType: String? = null, // 市场类型: moneyline, spreads, totals
|
||||
val endDateMin: String? = null, // 最小结束时间
|
||||
val endDateMax: String? = null, // 最大结束时间
|
||||
val minLiquidity: BigDecimal? = null, // 最小流动性
|
||||
val keyword: String? = null, // 搜索关键词
|
||||
val limit: Int = 50 // 返回数量
|
||||
)
|
||||
```
|
||||
|
||||
> **marketType 说明**:
|
||||
- `moneyline` - 胜负市场(谁会赢)
|
||||
- `spreads` - 让分市场
|
||||
- `totals` - 大小分市场
|
||||
|
||||
### 1.3 获取单个市场详情
|
||||
```
|
||||
GET https://gamma-api.polymarket.com/markets?condition_ids={conditionId}
|
||||
```
|
||||
> **用于**:
|
||||
- 创建策略时获取 `clobTokenIds`
|
||||
- 监控价格时获取实时价格
|
||||
- 获取 Token ID 用于下单
|
||||
|
||||
---
|
||||
|
||||
## 二、WebSocket 订阅管理
|
||||
### 2.1 订阅策略
|
||||
> 同一市场可能有多个策略,但只维护一个 WebSocket 订阅。
|
||||
> 策略创建/删除时需要更新订阅计数。
|
||||
```kotlin
|
||||
// 市场订阅计数
|
||||
private val marketSubscriptions = ConcurrentHashMap<String, Int>()
|
||||
|
||||
// 市场对应的 Token IDs 缓存
|
||||
private val marketTokenIds = ConcurrentHashMap<String, Pair<String, String>>()
|
||||
|
||||
/**
|
||||
* 订阅策略(创建策略时调用)
|
||||
*/
|
||||
fun subscribeStrategy(strategy: SportsTailStrategy) {
|
||||
val conditionId = strategy.conditionId
|
||||
|
||||
marketSubscriptions.compute(conditionId) { _, count ->
|
||||
val newCount = (count ?: 0) + 1
|
||||
if (newCount == 1) {
|
||||
// 首次订阅,建立 WebSocket 连接
|
||||
subscribeMarket(conditionId)
|
||||
}
|
||||
newCount
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
> **订阅管理规则**:
|
||||
| 场景 | 操作 |
|
||||
|------|------|
|
||||
| 策略创建 | 如果市场无订阅,建立订阅;否则计数 +1 |
|
||||
| 策略删除 | 计数 -1;如果计数为 0,取消订阅 |
|
||||
| 策略成交(无止盈止损) | 检查同市场是否有其他未完成策略;无则取消订阅 |
|
||||
| 策略卖出 | 检查同市场是否有其他未完成策略;无则取消订阅 |
|
||||
|
||||
### 2.2 讣阅流程
|
||||
```
|
||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
│ 策略创建 │────▶│ 检查订阅计数 │────▶│ 首次? 建立连接 │
|
||||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ WebSocket 接收订单簿更新 │
|
||||
│ 1. 解析消息,获取 tokenId 和价格 │
|
||||
│ 2. 查找该 Token 对应的未完成策略 │
|
||||
│ 3. 检查触发条件 │
|
||||
│ 4. 满足条件则执行买入/卖出 │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
> **WebSocket 消息格式**:
|
||||
```json
|
||||
{
|
||||
"event_type": "book",
|
||||
"asset_id": "1234567890",
|
||||
"market": {
|
||||
"bids": [...],
|
||||
"asks": [...],
|
||||
"timestamp": 1234567890
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、价格监控与触发
|
||||
### 3.1 买入触发逻辑
|
||||
> 任意方向价格达到触发价即买入
|
||||
```kotlin
|
||||
fun checkBuyTrigger(
|
||||
strategy: SportsTailStrategy,
|
||||
yesPrice: BigDecimal,
|
||||
noPrice: BigDecimal
|
||||
): TriggerResult? {
|
||||
// 检查 YES 方向
|
||||
if (yesPrice >= strategy.triggerPrice) {
|
||||
return TriggerResult(outcomeIndex = 0, price = yesPrice)
|
||||
}
|
||||
|
||||
// 检查 NO 方向
|
||||
if (noPrice >= strategy.triggerPrice) {
|
||||
return TriggerResult(outcomeIndex = 1, price = noPrice)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
```
|
||||
> **注意**:不区分方向,系统自动选择价格满足的方向买入。
|
||||
### 3.2 止盈止损逻辑
|
||||
> 已成交的策略,监控持仓方向的价格变化
|
||||
```kotlin
|
||||
fun checkSellTrigger(
|
||||
strategy: SportsTailStrategy,
|
||||
currentPrice: BigDecimal
|
||||
): SellTrigger? {
|
||||
if (!strategy.filled || strategy.sold) {
|
||||
return null
|
||||
}
|
||||
|
||||
// 止盈:当前价格 >= 止盈价
|
||||
if (strategy.takeProfitPrice != null && currentPrice >= strategy.takeProfitPrice) {
|
||||
return SellTrigger(type = "TAKE_PROFIT", price = currentPrice)
|
||||
}
|
||||
|
||||
// 止损:当前价格 <= 止损价
|
||||
if (strategy.stopLossPrice != null && currentPrice <= strategy.stopLossPrice) {
|
||||
return SellTrigger(type = "STOP_LOSS", price = currentPrice)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
```
|
||||
> **注意**:只监控已买入方向的价格,不是两个方向都监控。
|
||||
### 3.3 订阅生命周期
|
||||
```
|
||||
┌─────────────┐ 价格>=触发价 ┌─────────────┐ 止盈/止损 ┌─────────────┐
|
||||
│ 监控两个方向 │ ────────────────▶ │ 已成交 │ ────────────────▶ │ 已完成 │
|
||||
└─────────────┘ └─────────────┘ └─────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ 成交后检查止盈止损 │
|
||||
│ - 有止盈止损:保持订阅,只监控买入方向 │
|
||||
│ - 无止盈止损:检查同市场是否有其他未完成策略,无则取消订阅 │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
@@ -0,0 +1,141 @@
|
||||
# 体育尾盘策略 - 任务梳理
|
||||
|
||||
> 需求与 UI 见 `sports-tail-strategy-ui-spec.md`,市场数据与订阅见 `sports-tail-strategy-market-data.md`。
|
||||
|
||||
以下按**数据库 / 后端 / 前端**拆分为可执行任务,便于排期与验收。
|
||||
|
||||
---
|
||||
|
||||
## 一、数据库
|
||||
|
||||
| 序号 | 任务 | 说明 |
|
||||
|------|------|------|
|
||||
| D1 | 策略表 migration | 新建表 `sports_tail_strategy`,字段:id, account_id, condition_id, market_title, event_slug, trigger_price, amount_mode(FIXED/RATIO), amount_value, take_profit_price, stop_loss_price, filled(BOOL), filled_price, filled_outcome_index, filled_amount, filled_shares, filled_at, sold(BOOL), sell_price, sell_type, sell_amount, realized_pnl, sold_at, created_at, updated_at |
|
||||
| D2 | 触发记录表 migration | 新建表 `sports_tail_strategy_trigger`,字段:id, strategy_id, market_title, condition_id, account_id, buy_order_id, buy_price, outcome_index, outcome_name, buy_amount, buy_shares, buy_status(PENDING/SUCCESS/FAIL), sell_order_id, sell_price, sell_type(TAKE_PROFIT/STOP_LOSS/MANUAL), sell_amount, sell_status, realized_pnl, triggered_at, sold_at |
|
||||
|
||||
---
|
||||
|
||||
## 二、后端(Kotlin)
|
||||
|
||||
### 2.1 实体与 Repository
|
||||
|
||||
| 序号 | 任务 | 说明 |
|
||||
|------|------|------|
|
||||
| B1 | 策略实体 Entity | 对应 `sports_tail_strategy` 表;ID 用 `Long?`;时间用 `Long` 时间戳;金额用 `BigDecimal`;遵守 backend.mdc 实体规范 |
|
||||
| B2 | 触发记录实体 Entity | 对应 `sports_tail_strategy_trigger` 表 |
|
||||
| B3 | JpaRepository | 策略、触发记录的 Repository;按 conditionId、accountId、filled、sold 等查询 |
|
||||
|
||||
### 2.2 Gamma API 扩展
|
||||
|
||||
| 序号 | 任务 | 说明 |
|
||||
|------|------|------|
|
||||
| B4 | 体育类别 API | `GET /sports` 获取体育元数据(sport, tags, image) |
|
||||
| B5 | 市场搜索 API | `GET /markets` 支持 tag_id、sports_market_types、end_date_min/max、liquidity_num_min 等筛选参数 |
|
||||
| B6 | 事件列表 API | `GET /events` 支持 tag_slug、active、live 等参数,获取比赛状态 |
|
||||
|
||||
### 2.3 订阅管理
|
||||
|
||||
| 序号 | 任务 | 说明 |
|
||||
|------|------|------|
|
||||
| B7 | 订阅管理器 SubscriptionManager | 维护市场订阅计数,同一市场多策略共享订阅;无策略时自动取消订阅 |
|
||||
| B8 | WebSocket 订单簿订阅 | 订阅订单簿 `channel: "book:<token_id>"`,接收实时价格更新 |
|
||||
| B9 | 订阅生命周期管理 | 策略创建时订阅,成交后检查是否需要保持(有止盈止损则保持),卖出后检查是否需要取消 |
|
||||
|
||||
### 2.4 策略执行核心逻辑
|
||||
|
||||
| 序号 | 任务 | 说明 |
|
||||
|------|------|------|
|
||||
| B10 | 买入触发判断 | 接收订单簿更新,检查未成交策略;当任意方向价格 >= triggerPrice 时买入该方向 |
|
||||
| B11 | 买入执行 | 调用 CLOB API 创建市价买单;记录成交价格、数量、方向;更新策略状态为已成交 |
|
||||
| B12 | 止盈判断 | 已成交策略,当前价格 >= takeProfitPrice 时执行卖出 |
|
||||
| B13 | 止损判断 | 已成交策略,当前价格 <= stopLossPrice 时执行卖出 |
|
||||
| B14 | 卖出执行 | 调用 CLOB API 创建市价卖单;计算盈亏;更新策略状态为已卖出 |
|
||||
|
||||
### 2.5 API 与 DTO
|
||||
|
||||
| 序号 | 任务 | 说明 |
|
||||
|------|------|------|
|
||||
| B15 | 策略 CRUD API | 列表(分页/筛选)、创建、删除;统一 ApiResponse;错误码与 MessageSource |
|
||||
| B16 | 策略 DTO | 创建请求:accountId, conditionId, marketTitle, eventSlug, triggerPrice, amountMode, amountValue, takeProfitPrice(可选), stopLossPrice(可选) |
|
||||
| B17 | 触发记录 API | 全局触发记录列表;支持 accountId、status、时间筛选;返回市场信息、成交价、数量、盈亏等 |
|
||||
| B18 | 市场搜索 API | 体育类别列表、市场搜索(支持筛选)、市场详情(含实时价格) |
|
||||
|
||||
---
|
||||
|
||||
## 三、前端(React + TypeScript)
|
||||
|
||||
### 3.1 路由与导航
|
||||
|
||||
| 序号 | 任务 | 说明 |
|
||||
|------|------|------|
|
||||
| F1 | 路由 | App.tsx 增加 `/sports-tail-strategy` |
|
||||
| F2 | 菜单 | Layout 中增加「体育尾盘策略」菜单项 |
|
||||
|
||||
### 3.2 列表页
|
||||
|
||||
| 序号 | 任务 | 说明 |
|
||||
|------|------|------|
|
||||
| F3 | 列表页组件 | SportsTailStrategyList.tsx;页面标题、新增按钮、筛选(账户、类别) |
|
||||
| F4 | 列表展示 | 桌面 Table / 移动 Card:市场标题、账户、触发价、金额、止盈止损、成交价/数量(未成交显示实时价格)、操作(查看记录、删除) |
|
||||
| F5 | 实时价格显示 | 未成交策略通过 WebSocket 获取实时价格并显示 |
|
||||
|
||||
### 3.3 新增/编辑表单
|
||||
|
||||
| 序号 | 任务 | 说明 |
|
||||
|------|------|------|
|
||||
| F6 | 表单弹窗 | 账户选择、市场搜索(支持筛选)、触发价格、下注金额、止盈止损(可选) |
|
||||
| F7 | 市场筛选器 | 体育类别、市场类型、结束时间、最小流动性、搜索关键词 |
|
||||
| F8 | 市场选择器 | 显示搜索结果列表,包含市场标题、当前价格、结束时间、流动性 |
|
||||
| F9 | 预估收益 | 根据触发价、金额计算预估份额和收益 |
|
||||
| F10 | 表单校验 | 触发价 0-1,止盈 > 触发价,止损 < 触发价 |
|
||||
|
||||
### 3.4 触发记录
|
||||
|
||||
| 序号 | 任务 | 说明 |
|
||||
|------|------|------|
|
||||
| F11 | 触发记录列表 | 全局记录列表(非单个市场);支持账户、状态、时间筛选 |
|
||||
| F12 | 记录详情 | 市场标题、成交价格/方向、数量、卖出价格/类型、盈亏 |
|
||||
|
||||
### 3.5 通用
|
||||
|
||||
| 序号 | 任务 | 说明 |
|
||||
|------|------|------|
|
||||
| F13 | 类型定义 | 策略、触发记录、市场、体育类别等 TypeScript 类型 |
|
||||
| F14 | API 封装 | apiService 中 sportsTailStrategy.* 方法 |
|
||||
| F15 | 多语言 | zh-CN、zh-TW、en 的 sportsTailStrategy.* 文案 |
|
||||
|
||||
---
|
||||
|
||||
## 四、依赖关系简图
|
||||
|
||||
```
|
||||
D1,D2 数据库
|
||||
↓
|
||||
B1-B3 实体与 Repository
|
||||
↓
|
||||
B4-B6 Gamma API 扩展
|
||||
↓
|
||||
B7-B9 订阅管理
|
||||
↓
|
||||
B10-B14 执行逻辑
|
||||
↓
|
||||
B15-B18 API 与 DTO
|
||||
↓
|
||||
F1-F2 路由与菜单
|
||||
F13-F15 类型与 API 封装
|
||||
↓
|
||||
F3-F5 列表与实时价格
|
||||
F6-F10 表单(含市场筛选)
|
||||
F11-F12 触发记录
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、验收要点
|
||||
|
||||
- **不区分方向**:只设置触发价格,系统自动监控两个方向,任意方向达到即买入
|
||||
- **实时订阅**:通过 WebSocket 订阅订单簿,实时监控价格,不使用轮询
|
||||
- **订阅管理**:同一市场多策略共享一个订阅;无策略或策略完成且无止盈止损时取消订阅
|
||||
- **止盈止损**:有止盈止损的策略成交后保持订阅,直到卖出
|
||||
- **列表显示**:已成交显示成交价和数量,未成交显示实时价格
|
||||
- **触发记录**:全局记录列表,每条记录包含完整市场信息
|
||||
@@ -0,0 +1,259 @@
|
||||
# 体育尾盘策略 - UI 规格
|
||||
|
||||
## 一、策略列表页
|
||||
|
||||
### 1.1 页面布局
|
||||
|
||||
**路由**:`/sports-tail-strategy`
|
||||
|
||||
**桌面端**:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ 体育尾盘策略 [+ 新增策略] │
|
||||
├─────────────────────────────────────────────────────────────────────┤
|
||||
│ 筛选: [账户选择▼] [类别: 全部▼] │
|
||||
├─────────────────────────────────────────────────────────────────────┤
|
||||
│ ┌───────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Lakers vs Bulls - Who will win? │ │
|
||||
│ │ 账户: Account 1 │ │
|
||||
│ │ 触发价: >=0.90 | 金额: 10 USDC │ │
|
||||
│ │ 止盈: 0.98 | 止损: 0.85 │ │
|
||||
│ │ ─────────────────────────────────────────────────────────── │ │
|
||||
│ │ 成交价: 0.91 YES | 数量: 10.99 份 | 盈亏: 待结算 │ │
|
||||
│ │ [查看记录] [删除] │ │
|
||||
│ └───────────────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**移动端**:使用卡片布局,信息折叠展示
|
||||
|
||||
### 1.2 列表字段
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| 市场标题 | `marketTitle`,可点击跳转 Polymarket |
|
||||
| 账户 | 关联的账户名称 |
|
||||
| 触发价 | `>= {triggerPrice}` |
|
||||
| 金额 | 固定金额 USDC 或 余额比例 % |
|
||||
| 止盈/止损 | 配置的止盈止损价格,未配置显示 `-` |
|
||||
| 成交信息 | 已成交:`{filledPrice} {YES/NO} \| {filledShares} 份`<br>未成交:`实时价格: YES {price} \| NO {price}` |
|
||||
| 盈亏 | 已卖出:`+{realizedPnl} USDC`<br>已成交未卖出:`待结算`<br>未成交:`-` |
|
||||
|
||||
### 1.3 操作按钮
|
||||
|
||||
| 按钮 | 说明 |
|
||||
|------|------|
|
||||
| 查看记录 | 打开该策略的触发记录详情 |
|
||||
| 删除 | 删除策略(需二次确认) |
|
||||
|
||||
**注意**:不支持启用/禁用功能,无状态列
|
||||
|
||||
---
|
||||
|
||||
## 二、新增策略表单
|
||||
|
||||
### 2.1 表单字段
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| 账户选择 | 下拉 | 是 | 选择账户 |
|
||||
| 选择市场 | 搜索选择 | 是 | 从市场列表中选择(支持筛选) |
|
||||
| 触发条件 | 复合输入 | 是 | `当价格 [>=] [0.90] 时触发买入` |
|
||||
| 下注金额 | 单选+输入 | 是 | 固定金额 USDC 或 余额比例 % |
|
||||
| 启用自动卖出 | 开关 | 否 | 开启后显示止盈止损 |
|
||||
| 止盈价格 | 输入 | 否 | 价格上涨到此值时自动卖出 |
|
||||
| 止损价格 | 输入 | 否 | 价格下跌到此值时自动卖出 |
|
||||
|
||||
**注意**:
|
||||
- 不选择方向(YES/NO),只设置触发价格
|
||||
- 系统自动监控两个方向,任意方向达到触发价即买入
|
||||
- 默认只触发一次
|
||||
|
||||
### 2.2 市场选择器
|
||||
|
||||
**筛选条件**:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 体育类别 │
|
||||
│ ┌─────────────────────────────────────────────────────┐ │
|
||||
│ │ [全部 ▼] │ │
|
||||
│ │ 选项: 全部 / NBA / NFL / 英超 / 西甲 / 棒球... │ │
|
||||
│ └─────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ 市场类型 │
|
||||
│ ┌─────────────────────────────────────────────────────┐ │
|
||||
│ │ [全部 ▼] │ │
|
||||
│ │ 选项: 全部 / 胜负 / 让分 / 大小分 │ │
|
||||
│ └─────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ 结束时间 │
|
||||
│ ○ 全部 ○ 今天 ○ 未来24小时 ○ 未来7天 │
|
||||
│ │
|
||||
│ 最小流动性 │
|
||||
│ ┌─────────────────────────────────────────────────────┐ │
|
||||
│ │ [ ] USDC(留空表示不限制) │ │
|
||||
│ └─────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ 搜索关键词 │
|
||||
│ ┌─────────────────────────────────────────────────────┐ │
|
||||
│ │ [搜索球队、比赛...] [搜索] │ │
|
||||
│ └─────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**市场列表**:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ ○ Lakers vs Bulls - Who will win? │
|
||||
│ YES: 0.92 NO: 0.08 | 流动性: 50,000 USDC │
|
||||
│ 结束: 2024-03-07 15:30 (剩余2小时30分) │
|
||||
│ │
|
||||
│ ○ Warriors @ Heat - O/U 220.5 │
|
||||
│ Over: 0.55 Under: 0.45 | 流动性: 30,000 USDC │
|
||||
│ 结束: 2024-03-07 18:00 (剩余5小时) │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 2.3 预估收益展示
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 预估收益 │
|
||||
│ ──────────────────────────────────────────────────────── │
|
||||
│ 买入价格: 0.90 | 买入金额: 10 USDC │
|
||||
│ 预计份额: 11.11 | 预计收益: +1.11 USDC (11.1%) │
|
||||
│ │
|
||||
│ 止盈 (0.98): 收益 +8.89 USDC (88.9%) │
|
||||
│ 止损 (0.85): 亏损 -1.67 USDC (-16.7%) │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、触发记录页(全局)
|
||||
|
||||
### 3.1 页面布局
|
||||
|
||||
**路由**:`/sports-tail-strategy/records`
|
||||
|
||||
**说明**:全局记录列表,不是单个策略的记录
|
||||
|
||||
**桌面端表格**:
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ 触发记录 │
|
||||
├────────────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ 筛选: [账户选择▼] [状态: 全部▼] [时间范围: 最近7天▼] │
|
||||
├────────────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ 时间 │ 市场标题 │ 方向 │ 买入价 │ 金额 │ 卖出价 │ 盈亏 │
|
||||
│ 03-07 15:30 │ Lakers vs Bulls │ YES │ 0.91 │ 10.00 │ 0.98 │ +7.89 USDC │
|
||||
│ 03-07 14:20 │ Warriors @ Heat │ Over │ 0.55 │ 5.00 │ - │ 待结算 │
|
||||
│ 03-06 18:45 │ Celtics vs Heat │ NO │ 0.92 │ 20.00 │ 0.85 │ -7.00 USDC │
|
||||
└────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**移动端**:使用卡片布局
|
||||
|
||||
### 3.2 记录字段
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| 时间 | `triggeredAt` 格式化显示 |
|
||||
| 市场标题 | `marketTitle`,可点击跳转 Polymarket |
|
||||
| 方向 | `outcomeName` 或 YES/NO/Over/Under |
|
||||
| 买入价 | `buyPrice` |
|
||||
| 金额 | `buyAmount` USDC |
|
||||
| 卖出价 | `sellPrice`,未卖出显示 `-` |
|
||||
| 盈亏 | `realizedPnl`,未结算显示 `待结算` |
|
||||
|
||||
### 3.3 筛选条件
|
||||
|
||||
| 条件 | 说明 |
|
||||
|------|------|
|
||||
| 账户 | 按账户筛选 |
|
||||
| 状态 | 全部 / 待结算 / 已止盈 / 已止损 / 已完成 |
|
||||
| 时间范围 | 最近24小时 / 最近7天 / 最近30天 / 全部 |
|
||||
|
||||
---
|
||||
|
||||
## 四、响应式适配
|
||||
|
||||
### 4.1 断点
|
||||
|
||||
- 移动端: < 768px
|
||||
- 桌面端: >= 768px
|
||||
|
||||
### 4.2 移动端适配
|
||||
|
||||
1. 列表使用卡片布局,信息可折叠
|
||||
2. 表单使用分步或滚动布局
|
||||
3. 触发记录使用卡片列表
|
||||
4. 按钮最小触摸目标 44x44px
|
||||
|
||||
---
|
||||
|
||||
## 五、多语言 Key
|
||||
|
||||
```
|
||||
sportsTailStrategy.list.title=体育尾盘策略
|
||||
sportsTailStrategy.list.addStrategy=新增策略
|
||||
sportsTailStrategy.list.filter.account=账户
|
||||
sportsTailStrategy.list.filter.category=类别
|
||||
sportsTailStrategy.list.filter.allCategory=全部
|
||||
sportsTailStrategy.list.triggerPrice=触发价
|
||||
sportsTailStrategy.list.amount=金额
|
||||
sportsTailStrategy.list.takeProfitStopLoss=止盈/止损
|
||||
sportsTailStrategy.list.filledPrice=成交价
|
||||
sportsTailStrategy.list.realtimePrice=实时价格
|
||||
sportsTailStrategy.list.shares=份
|
||||
sportsTailStrategy.list.pnl=盈亏
|
||||
sportsTailStrategy.list.pending=待结算
|
||||
sportsTailStrategy.list.viewRecords=查看记录
|
||||
sportsTailStrategy.list.delete=删除
|
||||
sportsTailStrategy.list.deleteConfirm=确定删除该策略吗?
|
||||
|
||||
sportsTailStrategy.form.title=新增体育尾盘策略
|
||||
sportsTailStrategy.form.account=账户
|
||||
sportsTailStrategy.form.selectAccount=选择账户
|
||||
sportsTailStrategy.form.selectMarket=选择市场
|
||||
sportsTailStrategy.form.triggerCondition=触发条件
|
||||
sportsTailStrategy.form.triggerPriceHelp=当任意方向价格达到触发价时买入
|
||||
sportsTailStrategy.form.amount=下注金额
|
||||
sportsTailStrategy.form.fixedAmount=固定金额
|
||||
sportsTailStrategy.form.ratio=余额比例
|
||||
sportsTailStrategy.form.autoSell=启用自动卖出
|
||||
sportsTailStrategy.form.takeProfitPrice=止盈价格
|
||||
sportsTailStrategy.form.takeProfitHelp=价格上涨到此值时自动卖出
|
||||
sportsTailStrategy.form.stopLossPrice=止损价格
|
||||
sportsTailStrategy.form.stopLossHelp=价格下跌到此值时自动卖出
|
||||
sportsTailStrategy.form.estimatedReturn=预估收益
|
||||
sportsTailStrategy.form.buyPrice=买入价格
|
||||
sportsTailStrategy.form.buyAmount=买入金额
|
||||
sportsTailStrategy.form.estimatedShares=预计份额
|
||||
sportsTailStrategy.form.estimatedPnl=预计收益
|
||||
|
||||
sportsTailStrategy.marketSearch.sport=体育类别
|
||||
sportsTailStrategy.marketSearch.marketType=市场类型
|
||||
sportsTailStrategy.marketSearch.endTime=结束时间
|
||||
sportsTailStrategy.marketSearch.minLiquidity=最小流动性
|
||||
sportsTailStrategy.marketSearch.keyword=搜索关键词
|
||||
sportsTailStrategy.marketSearch.search=搜索
|
||||
sportsTailStrategy.marketSearch.liquidity=流动性
|
||||
sportsTailStrategy.marketSearch.remaining=剩余
|
||||
|
||||
sportsTailStrategy.records.title=触发记录
|
||||
sportsTailStrategy.records.time=时间
|
||||
sportsTailStrategy.records.market=市场
|
||||
sportsTailStrategy.records.direction=方向
|
||||
sportsTailStrategy.records.buyPrice=买入价
|
||||
sportsTailStrategy.records.amount=金额
|
||||
sportsTailStrategy.records.sellPrice=卖出价
|
||||
sportsTailStrategy.records.pnl=盈亏
|
||||
sportsTailStrategy.records.pending=待结算
|
||||
sportsTailStrategy.records.takeProfit=已止盈
|
||||
sportsTailStrategy.records.stopLoss=已止损
|
||||
```
|
||||
Reference in New Issue
Block a user