refactor: 移除未实现盈亏计算以优化跟单关系统计性能
- 移除未实现盈亏和持仓价值的计算 - 总盈亏现在仅包含已实现盈亏 - 删除不再使用的 getCurrentMarketPrice、getActualPositions、calculateUnrealizedPnl、calculatePositionValue 方法 - 简化盈亏百分比计算,仅基于已实现盈亏 - 移除不必要的依赖注入(accountService、blockchainService、retrofitFactory) - 性能提升:从 1+N 次网络请求减少到 0 次
This commit is contained in:
@@ -1,137 +0,0 @@
|
||||
# 🎉 PolyHermes v1.1.7 发布公告
|
||||
|
||||
**发布日期:2026年1月7日**
|
||||
|
||||
---
|
||||
|
||||
## ✨ 新功能
|
||||
|
||||
### 💰 支持 Maker Rebates Program 费率
|
||||
|
||||
我们新增了对 Polymarket Maker Rebates Program 的支持!系统现在会自动获取并应用最新的费率,帮助您享受更优惠的交易成本。
|
||||
|
||||
**这意味着什么?**
|
||||
- 系统会自动查询并应用最新的费率
|
||||
- 所有订单(买入、卖出)都会使用正确的费率
|
||||
- 无需手动配置,系统会自动处理
|
||||
|
||||
### 🔧 Docker 部署更灵活
|
||||
|
||||
现在您可以通过环境变量轻松配置日志级别,无需修改配置文件!
|
||||
|
||||
**新增配置项:**
|
||||
- `LOG_LEVEL_ROOT` - 系统日志级别(默认:INFO)
|
||||
- `LOG_LEVEL_APP` - 应用日志级别(默认:DEBUG)
|
||||
|
||||
只需在 `.env` 文件中添加这些配置,重启服务即可生效。
|
||||
|
||||
---
|
||||
|
||||
## 🐛 问题修复
|
||||
|
||||
### 修复市场价格查询问题
|
||||
|
||||
修复了某些市场无法正确查询价格的问题。现在系统会:
|
||||
- 优先从链上查询市场价格
|
||||
- 如果链上查询失败,自动降级到其他数据源
|
||||
- 提高系统的稳定性和容错性
|
||||
|
||||
### 修复自动卖出误判问题
|
||||
|
||||
修复了在某些情况下系统会误判市场已卖出,导致创建错误记录的问题。现在系统会:
|
||||
- 更准确地判断市场状态
|
||||
- 避免误判导致的错误记录
|
||||
- 提高仓位管理的准确性
|
||||
|
||||
---
|
||||
|
||||
## 📝 文档更新
|
||||
|
||||
### 更新联系方式
|
||||
|
||||
- **Telegram 群组**:https://t.me/polyhermes
|
||||
- 欢迎加入我们的 Telegram 群组,获取最新资讯和技术支持!
|
||||
|
||||
### 版本信息更清晰
|
||||
|
||||
- README 中新增了 Docker 版本徽章
|
||||
- 可以一目了然地看到最新的 Docker 镜像版本
|
||||
|
||||
---
|
||||
|
||||
## 📊 本次更新统计
|
||||
|
||||
- **5 个提交**
|
||||
- **16 个文件变更**
|
||||
- **主要改进**:费率支持、错误修复、部署优化
|
||||
|
||||
---
|
||||
|
||||
## 🚀 如何升级
|
||||
|
||||
### Docker 部署用户(推荐)
|
||||
|
||||
```bash
|
||||
# 1. 停止当前服务
|
||||
docker-compose -f docker-compose.prod.yml down
|
||||
|
||||
# 2. 拉取最新镜像
|
||||
docker pull wrbug/polyhermes:latest
|
||||
|
||||
# 3. 重新启动服务
|
||||
docker-compose -f docker-compose.prod.yml up -d
|
||||
|
||||
# 4. 查看日志确认升级成功
|
||||
docker-compose -f docker-compose.prod.yml logs -f
|
||||
```
|
||||
|
||||
### 本地构建用户
|
||||
|
||||
```bash
|
||||
# 1. 拉取最新代码
|
||||
git pull origin dev
|
||||
|
||||
# 2. 切换到 v1.1.7 标签
|
||||
git checkout v1.1.7
|
||||
|
||||
# 3. 重新构建并启动
|
||||
./deploy.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 重要提示
|
||||
|
||||
### 数据库迁移
|
||||
|
||||
**本次更新无需数据库迁移**,可以直接升级,不会影响现有数据。
|
||||
|
||||
### 配置变更
|
||||
|
||||
- 新增的日志级别配置为**可选配置**
|
||||
- 如果不配置,系统会使用默认值(INFO/DEBUG)
|
||||
- 不影响现有功能
|
||||
|
||||
---
|
||||
|
||||
## 🙏 感谢
|
||||
|
||||
感谢所有用户的支持和反馈!如果您在使用过程中遇到任何问题,欢迎:
|
||||
|
||||
- 📧 提交 Issue:https://github.com/WrBug/PolyHermes/issues
|
||||
- 💬 加入 Telegram 群组:https://t.me/polyhermes
|
||||
- 🐦 关注 Twitter:@polyhermes
|
||||
|
||||
---
|
||||
|
||||
## 📚 相关链接
|
||||
|
||||
- **GitHub 仓库**:https://github.com/WrBug/PolyHermes
|
||||
- **完整更新日志**:https://github.com/WrBug/PolyHermes/releases/tag/v1.1.7
|
||||
- **Docker Hub**:https://hub.docker.com/r/wrbug/polyhermes
|
||||
|
||||
---
|
||||
|
||||
**祝您交易顺利!** 🚀
|
||||
|
||||
|
||||
+11
-204
@@ -31,8 +31,6 @@ class CopyTradingStatisticsService(
|
||||
private val sellMatchDetailRepository: SellMatchDetailRepository,
|
||||
private val accountRepository: AccountRepository,
|
||||
private val leaderRepository: LeaderRepository,
|
||||
private val accountService: AccountService,
|
||||
private val blockchainService: BlockchainService,
|
||||
private val marketService: com.wrbug.polymarketbot.service.common.MarketService
|
||||
) {
|
||||
|
||||
@@ -63,19 +61,12 @@ class CopyTradingStatisticsService(
|
||||
// 6. 计算统计信息
|
||||
val statistics = calculateStatistics(buyOrders, sellRecords, matchDetails)
|
||||
|
||||
// 7. 获取链上实际持仓(用于准确计算未实现盈亏,考虑手动卖出的情况)
|
||||
val actualPositions = getActualPositions(account)
|
||||
// 7. 不再计算未实现盈亏和持仓价值(优化性能)
|
||||
// 未实现盈亏计算需要查询链上持仓和市场价格,性能开销大
|
||||
val unrealizedPnl = "0"
|
||||
val positionValue = "0"
|
||||
|
||||
// 8. 获取当前市场价格(用于计算未实现盈亏)
|
||||
val currentPrice = getCurrentMarketPrice(buyOrders)
|
||||
|
||||
// 9. 计算未实现盈亏(使用链上实际持仓,而不是 remainingQuantity)
|
||||
val unrealizedPnl = calculateUnrealizedPnl(buyOrders, currentPrice, actualPositions)
|
||||
|
||||
// 10. 计算持仓价值(使用链上实际持仓和当前价格)
|
||||
val positionValue = calculatePositionValue(buyOrders, currentPrice, actualPositions)
|
||||
|
||||
// 11. 构建响应
|
||||
// 8. 构建响应(总盈亏 = 已实现盈亏)
|
||||
val response = CopyTradingStatisticsResponse(
|
||||
copyTradingId = copyTradingId,
|
||||
accountId = copyTrading.accountId,
|
||||
@@ -94,8 +85,8 @@ class CopyTradingStatisticsService(
|
||||
currentPositionValue = positionValue,
|
||||
totalRealizedPnl = statistics.totalRealizedPnl,
|
||||
totalUnrealizedPnl = unrealizedPnl,
|
||||
totalPnl = (statistics.totalRealizedPnl.toSafeBigDecimal().add(unrealizedPnl.toSafeBigDecimal())).toString(),
|
||||
totalPnlPercent = calculatePnlPercent(statistics.totalBuyAmount, statistics.totalRealizedPnl, unrealizedPnl)
|
||||
totalPnl = statistics.totalRealizedPnl,
|
||||
totalPnlPercent = calculatePnlPercentOnlyRealized(statistics.totalBuyAmount, statistics.totalRealizedPnl)
|
||||
)
|
||||
|
||||
Result.success(response)
|
||||
@@ -358,200 +349,16 @@ class CopyTradingStatisticsService(
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前市场价格
|
||||
* 按 (marketId, outcomeIndex) 组合获取价格,支持多元市场
|
||||
* 计算盈亏百分比(仅基于已实现盈亏)
|
||||
*/
|
||||
private suspend fun getCurrentMarketPrice(buyOrders: List<CopyOrderTracking>): Map<String, String> {
|
||||
val prices = mutableMapOf<String, String>()
|
||||
|
||||
// 获取所有不同的 (marketId, outcomeIndex) 组合
|
||||
val marketOutcomePairs = buyOrders
|
||||
.filter { it.outcomeIndex != null }
|
||||
.map { Pair(it.marketId, it.outcomeIndex!!) }
|
||||
.distinct()
|
||||
|
||||
for ((marketId, outcomeIndex) in marketOutcomePairs) {
|
||||
try {
|
||||
// 传递 outcomeIndex 参数,确保获取对应 outcome 的价格
|
||||
val result = accountService.getMarketPrice(marketId, outcomeIndex)
|
||||
result.onSuccess { response ->
|
||||
// 使用当前价格
|
||||
val price = response.currentPrice
|
||||
if (price.isNotBlank() && price != "0") {
|
||||
// 使用 "marketId:outcomeIndex" 作为 key
|
||||
val key = "$marketId:$outcomeIndex"
|
||||
prices[key] = price
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logger.warn("获取市场价格失败: marketId=$marketId, outcomeIndex=$outcomeIndex", e)
|
||||
}
|
||||
}
|
||||
|
||||
return prices
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取链上实际持仓
|
||||
* 按 (marketId, outcomeIndex) 组合返回实际持仓数量
|
||||
*/
|
||||
private suspend fun getActualPositions(account: Account?): Map<String, BigDecimal> {
|
||||
val positions = mutableMapOf<String, BigDecimal>()
|
||||
|
||||
if (account == null || account.proxyAddress.isBlank()) {
|
||||
return positions
|
||||
}
|
||||
|
||||
try {
|
||||
val positionsResult = blockchainService.getPositions(account.proxyAddress)
|
||||
if (positionsResult.isSuccess) {
|
||||
val positionList = positionsResult.getOrNull() ?: emptyList()
|
||||
for (pos in positionList) {
|
||||
// 只处理有 conditionId 和 outcomeIndex 的仓位
|
||||
if (pos.conditionId != null && pos.outcomeIndex != null && pos.size != null) {
|
||||
val key = "${pos.conditionId}:${pos.outcomeIndex}"
|
||||
val size = pos.size.toSafeBigDecimal()
|
||||
// 如果 size > 0,表示有持仓;如果 size < 0,表示做空(取绝对值)
|
||||
positions[key] = size.abs()
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logger.warn("获取链上持仓失败: accountId=${account.id}, error=${e.message}", e)
|
||||
}
|
||||
|
||||
return positions
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算未实现盈亏
|
||||
* 使用链上实际持仓数量,而不是 remainingQuantity(考虑手动卖出的情况)
|
||||
* 按市场聚合订单,计算加权平均买入价格,避免重复计算
|
||||
*/
|
||||
private fun calculateUnrealizedPnl(
|
||||
buyOrders: List<CopyOrderTracking>,
|
||||
currentPrices: Map<String, String>,
|
||||
actualPositions: Map<String, BigDecimal>
|
||||
): String {
|
||||
var totalUnrealizedPnl = BigDecimal.ZERO
|
||||
|
||||
// 按市场聚合订单,计算加权平均买入价格
|
||||
val marketAggregates = mutableMapOf<String, Pair<BigDecimal, BigDecimal>>() // key -> (总持仓, 总成本)
|
||||
|
||||
for (order in buyOrders) {
|
||||
// 如果没有 outcomeIndex,跳过(无法确定价格和持仓)
|
||||
if (order.outcomeIndex == null) {
|
||||
logger.warn("订单缺少 outcomeIndex,跳过未实现盈亏计算: orderId=${order.buyOrderId}, marketId=${order.marketId}")
|
||||
continue
|
||||
}
|
||||
|
||||
// 使用 "marketId:outcomeIndex" 作为 key
|
||||
val key = "${order.marketId}:${order.outcomeIndex}"
|
||||
|
||||
// 获取订单的持仓数量(使用 remainingQuantity,因为这是该订单的持仓)
|
||||
val orderQty = order.remainingQuantity.toSafeBigDecimal()
|
||||
|
||||
// 如果订单持仓 <= 0,跳过
|
||||
if (orderQty.lte(BigDecimal.ZERO)) continue
|
||||
|
||||
val buyPrice = order.price.toSafeBigDecimal()
|
||||
val orderCost = orderQty.multi(buyPrice)
|
||||
|
||||
// 聚合同一市场的订单
|
||||
val existing = marketAggregates[key]
|
||||
if (existing != null) {
|
||||
val totalQty = existing.first.add(orderQty)
|
||||
val totalCost = existing.second.add(orderCost)
|
||||
marketAggregates[key] = Pair(totalQty, totalCost)
|
||||
} else {
|
||||
marketAggregates[key] = Pair(orderQty, orderCost)
|
||||
}
|
||||
}
|
||||
|
||||
// 计算每个市场的未实现盈亏
|
||||
for ((key, aggregate) in marketAggregates) {
|
||||
val (totalQty, totalCost) = aggregate
|
||||
|
||||
// 获取链上实际持仓数量(如果存在),否则使用聚合的持仓数量
|
||||
val actualQty = actualPositions[key] ?: totalQty
|
||||
|
||||
// 如果实际持仓 <= 0,说明已全部卖出(包括手动卖出),跳过未实现盈亏计算
|
||||
if (actualQty.lte(BigDecimal.ZERO)) continue
|
||||
|
||||
// 获取当前市场价格
|
||||
val currentPrice = currentPrices[key]?.toSafeBigDecimal()
|
||||
?: continue // 如果没有当前价格,跳过
|
||||
|
||||
// 计算加权平均买入价格
|
||||
val avgBuyPrice = if (totalQty.gt(BigDecimal.ZERO)) {
|
||||
totalCost.div(totalQty)
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
|
||||
// 使用实际持仓数量和加权平均买入价格计算未实现盈亏
|
||||
val unrealizedPnl = currentPrice.subtract(avgBuyPrice).multi(actualQty)
|
||||
totalUnrealizedPnl = totalUnrealizedPnl.add(unrealizedPnl)
|
||||
}
|
||||
|
||||
return totalUnrealizedPnl.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算持仓价值
|
||||
* 使用链上实际持仓数量和当前市场价格计算
|
||||
* 按市场聚合,避免重复计算
|
||||
*/
|
||||
private fun calculatePositionValue(
|
||||
buyOrders: List<CopyOrderTracking>,
|
||||
currentPrices: Map<String, String>,
|
||||
actualPositions: Map<String, BigDecimal>
|
||||
): String {
|
||||
var totalPositionValue = BigDecimal.ZERO
|
||||
|
||||
// 按市场聚合,获取所有不同的市场
|
||||
val marketKeys = buyOrders
|
||||
.filter { it.outcomeIndex != null }
|
||||
.map { "${it.marketId}:${it.outcomeIndex}" }
|
||||
.distinct()
|
||||
|
||||
for (key in marketKeys) {
|
||||
// 获取链上实际持仓数量(如果存在)
|
||||
val actualQty = actualPositions[key]
|
||||
|
||||
// 如果没有链上持仓,计算该市场的总持仓(所有订单的 remainingQuantity 之和)
|
||||
val totalQty = actualQty ?: buyOrders
|
||||
.filter { it.outcomeIndex != null && "${it.marketId}:${it.outcomeIndex}" == key }
|
||||
.sumOf { it.remainingQuantity.toSafeBigDecimal() }
|
||||
|
||||
// 如果持仓 <= 0,跳过
|
||||
if (totalQty.lte(BigDecimal.ZERO)) continue
|
||||
|
||||
// 获取当前市场价格
|
||||
val currentPrice = currentPrices[key]?.toSafeBigDecimal()
|
||||
?: continue // 如果没有当前价格,跳过
|
||||
|
||||
// 计算持仓价值:持仓数量 × 当前价格
|
||||
val positionValue = totalQty.multi(currentPrice)
|
||||
totalPositionValue = totalPositionValue.add(positionValue)
|
||||
}
|
||||
|
||||
return totalPositionValue.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算盈亏百分比
|
||||
*/
|
||||
private fun calculatePnlPercent(
|
||||
private fun calculatePnlPercentOnlyRealized(
|
||||
totalBuyAmount: String,
|
||||
totalRealizedPnl: String,
|
||||
totalUnrealizedPnl: String
|
||||
totalRealizedPnl: String
|
||||
): String {
|
||||
val buyAmount = totalBuyAmount.toSafeBigDecimal()
|
||||
if (buyAmount.lte(BigDecimal.ZERO)) return "0"
|
||||
|
||||
val totalPnl = totalRealizedPnl.toSafeBigDecimal().add(totalUnrealizedPnl.toSafeBigDecimal())
|
||||
val percent = totalPnl.div(buyAmount).multi(100)
|
||||
val percent = totalRealizedPnl.toSafeBigDecimal().div(buyAmount).multi(100)
|
||||
|
||||
return percent.setScale(2, RoundingMode.HALF_UP).toString()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user