Files
PolyHermes/docs/zh/position-sell-order-design.md
WrBug a222c9a52f docs: 重构文档结构,使用 zh/ 和 en/ 目录区分中英文文档
- 创建 docs/zh/ 和 docs/en/ 目录结构
- 将所有中文文档移动到 docs/zh/
- 创建主要文档的英文版本:
  - DEPLOYMENT.md (651行)
  - DEVELOPMENT.md (514行)
  - VERSION_MANAGEMENT.md (已有)
- 更新所有文档中的内部链接
- 更新 README.md 和 README_EN.md 中的文档链接
- 在文档中添加中英文版本互链
2025-12-07 18:01:11 +08:00

358 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 仓位出售订单功能设计文档
## 1. 功能概述
为仓位管理页面添加出售功能,支持用户对当前仓位进行市价或限价卖出操作。
## 2. 后端接口设计
### 2.1 创建卖出订单接口
**接口路径**: `POST /api/copy-trading/positions/sell`
**请求体**:
```kotlin
data class PositionSellRequest(
val accountId: Long, // 账户ID(必需)
val marketId: String, // 市场ID(必需)
val side: String, // 方向:YES 或 NO(必需)
val orderType: String, // 订单类型:MARKET(市价)或 LIMIT(限价)(必需)
val quantity: String, // 卖出数量(必需,BigDecimal字符串)
val price: String? = null // 限价价格(限价订单必需,市价订单不需要)
)
```
**响应体**:
```kotlin
data class PositionSellResponse(
val orderId: String, // 订单ID
val marketId: String, // 市场ID
val side: String, // 方向
val orderType: String, // 订单类型
val quantity: String, // 订单数量
val price: String?, // 订单价格(限价订单)
val status: String, // 订单状态
val createdAt: Long // 创建时间戳
)
```
**业务逻辑**:
1. 验证账户是否存在且已配置API凭证
2. 验证仓位是否存在且数量足够
3. 验证订单参数(数量、价格等)
4. 市价订单:获取当前最优卖价(bestBid)作为价格
5. 限价订单:验证价格是否合理
6. 调用Polymarket CLOB API创建订单
7. 返回订单信息
**错误处理**:
- 账户不存在或未配置API凭证:返回错误码 2001
- 仓位不存在或数量不足:返回错误码 4001
- 价格或数量格式错误:返回错误码 1001
- API调用失败:返回错误码 5001
### 2.2 获取市场当前价格接口(可选,用于显示参考价格)
**接口路径**: `POST /api/copy-trading/markets/price`
**请求体**:
```kotlin
data class MarketPriceRequest(
val marketId: String // 市场ID
)
```
**响应体**:
```kotlin
data class MarketPriceResponse(
val marketId: String,
val lastPrice: String?, // 最新成交价
val bestBid: String?, // 最优买价(用于卖出参考)
val bestAsk: String?, // 最优卖价(用于买入参考)
val midpoint: String? // 中间价
)
```
## 3. 前端交互设计
### 3.1 UI组件设计
#### 3.1.1 出售按钮
- **位置**: 每个仓位卡片/列表项的操作区域
- **样式**:
- 卡片视图:卡片底部或操作区域
- 列表视图:操作列
- **显示条件**: 仅当前仓位显示(历史仓位不显示)
- **按钮文本**: "卖出" 或 "出售"
#### 3.1.2 出售模态框
**布局结构**:
```
┌─────────────────────────────────┐
│ 出售仓位 - [市场标题] │
├─────────────────────────────────┤
│ 账户: [账户名称] │
│ 方向: [YES/NO标签] │
│ 当前持仓: [数量] │
│ 平均价格: [平均买入价格] │
│ 当前价格: [当前市场价格] │
├─────────────────────────────────┤
│ 订单类型: │
│ ○ 市价出售 ○ 限价出售 │
├─────────────────────────────────┤
│ 卖出数量: │
│ [输入框] │
│ [20%] [50%] [80%] [100%] │
├─────────────────────────────────┤
│ 限价价格: (限价时显示) │
│ [输入框] │
│ 参考价格: [当前最优买价] │
├─────────────────────────────────┤
│ 预计平仓收益: │
│ 收益金额: [+/-XXX.XX USDC] │
│ 收益率: [+/-XX.XX%] │
│ (实时计算,根据数量和价格更新) │
├─────────────────────────────────┤
│ [取消] [确认卖出] │
└─────────────────────────────────┘
```
**字段说明**:
1. **订单类型选择**:
- 单选按钮:市价出售 / 限价出售
- 默认:限价出售
- 切换时显示/隐藏限价输入框
2. **卖出数量**:
- 输入框:支持手动输入
- 快捷按钮:20%, 50%, 80%, 100%
- 点击快捷按钮自动填充到输入框
- 验证:不能超过当前持仓数量,不能为0
3. **限价价格**(限价订单时显示):
- 输入框:支持手动输入
- 显示参考价格:当前最优买价(bestBid)
- 验证:价格必须大于0
4. **按钮**:
- 取消:关闭模态框
- 确认卖出:提交订单(加载状态)
### 3.2 交互流程
1. **打开模态框**:
- 点击"卖出"按钮
- 加载市场当前价格(用于显示参考价格)
- 初始化表单(默认限价,数量为空)
2. **选择订单类型**:
- 切换市价/限价
- 市价:隐藏限价输入框
- 限价:显示限价输入框和参考价格
3. **设置数量**:
- 点击快捷按钮(20%, 50%, 80%, 100%
- 自动计算并填充到输入框
- 实时验证数量是否有效
4. **设置限价**(限价订单):
- 手动输入价格
- 显示参考价格提示
- 实时更新平仓收益
5. **查看平仓收益**(实时计算):
- 根据卖出数量和价格实时计算
- 计算公式:
- 收益金额 = (卖出价格 - 平均买入价格) × 卖出数量
- 收益率 = (卖出价格 - 平均买入价格) / 平均买入价格 × 100%
- 市价订单:使用当前最优买价计算
- 限价订单:使用输入的限价计算
- 颜色显示:盈利为绿色,亏损为红色
6. **提交订单**:
- 点击"确认卖出"
- 显示加载状态
- 调用后端接口
- 成功:显示成功提示,关闭模态框,刷新仓位列表
- 失败:显示错误提示
### 3.3 数据验证
**前端验证**:
- 数量:必填,大于0,不超过当前持仓数量
- 限价价格:限价订单必填,大于0
- 账户:必须已配置API凭证
**后端验证**:
- 账户存在且已配置API凭证
- 仓位存在且数量足够
- 价格和数量格式正确
- 市价订单自动获取最优价格
## 4. 类型定义
### 4.1 前端TypeScript类型
```typescript
/**
* 仓位卖出请求
*/
export interface PositionSellRequest {
accountId: number
marketId: string
side: 'YES' | 'NO'
orderType: 'MARKET' | 'LIMIT'
quantity: string
price?: string // 限价订单必需
}
/**
* 仓位卖出响应
*/
export interface PositionSellResponse {
orderId: string
marketId: string
side: string
orderType: string
quantity: string
price?: string
status: string
createdAt: number
}
/**
* 市场价格请求
*/
export interface MarketPriceRequest {
marketId: string
}
/**
* 市场价格响应
*/
export interface MarketPriceResponse {
marketId: string
lastPrice?: string
bestBid?: string
bestAsk?: string
midpoint?: string
}
```
## 5. API服务方法
### 5.1 前端API服务
```typescript
// frontend/src/services/api.ts
export const apiService = {
positions: {
/**
* 卖出仓位
*/
sell: (data: PositionSellRequest) =>
apiClient.post<ApiResponse<PositionSellResponse>>('/copy-trading/positions/sell', data),
/**
* 获取市场价格
*/
getMarketPrice: (data: MarketPriceRequest) =>
apiClient.post<ApiResponse<MarketPriceResponse>>('/copy-trading/markets/price', data)
}
}
```
### 5.2 后端Controller方法
```kotlin
@PostMapping("/positions/sell")
suspend fun sellPosition(@RequestBody request: PositionSellRequest): ResponseEntity<ApiResponse<PositionSellResponse>>
@PostMapping("/markets/price")
suspend fun getMarketPrice(@RequestBody request: MarketPriceRequest): ResponseEntity<ApiResponse<MarketPriceResponse>>
```
## 6. 实现细节
### 6.1 市价订单处理
- 市价订单需要获取当前最优买价(bestBid)作为卖出价格
- 如果无法获取最优买价,使用最新成交价(lastPrice)
- 如果都没有,返回错误提示
### 6.2 数量计算
- 快捷按钮计算:`数量 = 当前持仓数量 × 百分比`
- 保留4位小数(与仓位数量精度一致)
- 验证:不能超过当前持仓数量
### 6.3 平仓收益实时计算
**计算逻辑**:
```typescript
// 获取仓位信息
const avgPrice = parseFloat(position.avgPrice) // 平均买入价格
const quantity = parseFloat(sellQuantity) // 卖出数量
const sellPrice = orderType === 'MARKET'
? parseFloat(marketPrice.bestBid) // 市价:使用最优买价
: parseFloat(limitPrice) // 限价:使用输入价格
// 计算收益
const pnl = (sellPrice - avgPrice) * quantity
const percentPnl = ((sellPrice - avgPrice) / avgPrice) * 100
// 显示格式
const pnlDisplay = `${pnl >= 0 ? '+' : ''}${pnl.toFixed(2)} USDC`
const percentPnlDisplay = `${percentPnl >= 0 ? '+' : ''}${percentPnl.toFixed(2)}%`
```
**更新时机**:
- 数量输入框值变化时
- 限价价格输入框值变化时(限价订单)
- 订单类型切换时(市价/限价)
- 市场价格更新时(市价订单,如果支持实时更新)
**显示样式**:
- 盈利:绿色文字(#52c41a
- 亏损:红色文字(#f5222d
- 字体:加粗显示,突出重要性
### 6.4 错误处理
- 网络错误:显示"网络错误,请重试"
- API错误:显示后端返回的错误信息
- 验证错误:显示具体的验证失败原因
### 6.5 用户体验优化
- 提交订单时禁用按钮,显示加载状态
- 成功后自动刷新仓位列表
- 提供清晰的成功/失败提示
- 模态框支持ESC键关闭
## 7. 安全考虑
1. **权限验证**: 验证账户是否属于当前用户
2. **数量验证**: 确保卖出数量不超过持仓数量
3. **价格验证**: 限价订单验证价格合理性
4. **API凭证**: 确保账户已配置有效的API凭证
## 8. 测试要点
1. 市价订单创建成功
2. 限价订单创建成功
3. 数量快捷按钮功能
4. 数量验证(超过持仓、为0等)
5. 价格验证(限价订单)
6. **平仓收益实时计算**:
- 数量变化时收益更新
- 限价变化时收益更新
- 订单类型切换时收益更新
- 收益金额和收益率计算正确
- 盈利/亏损颜色显示正确
7. 账户未配置API凭证的错误处理
8. 仓位不存在的错误处理
9. 网络错误处理