diff --git a/ANNOUNCEMENT_V1.1.7.md b/ANNOUNCEMENT_V1.1.7.md deleted file mode 100644 index 96e67f3..0000000 --- a/ANNOUNCEMENT_V1.1.7.md +++ /dev/null @@ -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 - ---- - -**祝您交易顺利!** 🚀 - - diff --git a/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/statistics/CopyTradingStatisticsService.kt b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/statistics/CopyTradingStatisticsService.kt index d0a8b0f..c47b837 100644 --- a/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/statistics/CopyTradingStatisticsService.kt +++ b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/statistics/CopyTradingStatisticsService.kt @@ -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): Map { - val prices = mutableMapOf() - - // 获取所有不同的 (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 { - val positions = mutableMapOf() - - 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, - currentPrices: Map, - actualPositions: Map - ): String { - var totalUnrealizedPnl = BigDecimal.ZERO - - // 按市场聚合订单,计算加权平均买入价格 - val marketAggregates = mutableMapOf>() // 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, - currentPrices: Map, - actualPositions: Map - ): 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() }