From db16dea33365fa02c46e09c1294714610eb09f7e Mon Sep 17 00:00:00 2001 From: WrBug Date: Tue, 9 Dec 2025 01:12:53 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=B8=82=E5=9C=BA?= =?UTF-8?q?=E7=BB=93=E7=AE=97=E5=88=A4=E6=96=AD=E9=80=BB=E8=BE=91=EF=BC=8C?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E8=B5=A2=E4=BA=86=E5=92=8C=E8=BE=93=E4=BA=86?= =?UTF-8?q?=E7=9A=84=E4=BB=B7=E6=A0=BC=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加市场已关闭时的结算判断逻辑 - 如果市场已关闭且该 outcome 赢了(价格 >= 0.99),返回价格为 1 - 如果市场已关闭且该 outcome 输了(价格 <= 0.01),返回价格为 0 - 优先使用 outcomePrices(结算价格数组)判断 - 如果没有 outcomePrices,使用 bestBid 判断 - 支持多元市场的价格转换 - 当没有仓位但有未完成订单时,正确判断市场结算状态并设置价格 --- .../service/accounts/PositionCheckService.kt | 101 +++++++++++++++++- 1 file changed, 100 insertions(+), 1 deletion(-) diff --git a/backend/src/main/kotlin/com/wrbug/polymarketbot/service/accounts/PositionCheckService.kt b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/accounts/PositionCheckService.kt index 00f0615..189c44e 100644 --- a/backend/src/main/kotlin/com/wrbug/polymarketbot/service/accounts/PositionCheckService.kt +++ b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/accounts/PositionCheckService.kt @@ -21,6 +21,8 @@ import org.springframework.context.i18n.LocaleContextHolder import com.wrbug.polymarketbot.service.system.SystemConfigService import com.wrbug.polymarketbot.service.system.RelayClientService import com.wrbug.polymarketbot.service.system.TelegramNotificationService +import com.wrbug.polymarketbot.util.RetrofitFactory +import com.wrbug.polymarketbot.util.JsonUtils import org.springframework.stereotype.Service import java.math.BigDecimal import java.util.concurrent.ConcurrentHashMap @@ -42,7 +44,8 @@ class PositionCheckService( private val relayClientService: RelayClientService, private val telegramNotificationService: TelegramNotificationService?, private val accountRepository: AccountRepository, - private val messageSource: MessageSource + private val messageSource: MessageSource, + private val retrofitFactory: RetrofitFactory ) { private val logger = LoggerFactory.getLogger(PositionCheckService::class.java) @@ -391,9 +394,40 @@ class PositionCheckService( /** * 获取当前市场最新价(用于更新订单卖出价) * 优先使用 bestBid(最优买价),如果没有则使用 midpoint(中间价) + * 如果市场已关闭: + * - 该 outcome 赢了,返回 1 + * - 该 outcome 输了,返回 0 */ private suspend fun getCurrentMarketPrice(marketId: String, outcomeIndex: Int): BigDecimal { return try { + // 先获取市场信息,检查市场是否已关闭 + val gammaApi = retrofitFactory.createGammaApi() + val marketResponse = gammaApi.listMarkets(conditionIds = listOf(marketId)) + + if (marketResponse.isSuccessful && marketResponse.body() != null) { + val markets = marketResponse.body()!! + val market = markets.firstOrNull() + + if (market != null && market.closed == true) { + // 市场已关闭,检查该 outcome 是赢了还是输了 + val outcomeResult = checkOutcomeResult(market, outcomeIndex) + when (outcomeResult) { + OutcomeResult.WON -> { + logger.info("市场已关闭且该 outcome 赢了,返回价格为 1: marketId=$marketId, outcomeIndex=$outcomeIndex") + return BigDecimal.ONE + } + OutcomeResult.LOST -> { + logger.info("市场已关闭且该 outcome 输了,返回价格为 0: marketId=$marketId, outcomeIndex=$outcomeIndex") + return BigDecimal.ZERO + } + OutcomeResult.UNKNOWN -> { + // 无法判断,继续使用正常价格逻辑 + } + } + } + } + + // 如果市场未关闭或无法判断输赢,获取正常价格 val priceResult = accountService.getMarketPrice(marketId, outcomeIndex) val marketPrice = priceResult.getOrNull() if (marketPrice != null) { @@ -409,6 +443,71 @@ class PositionCheckService( } } + /** + * Outcome 结果枚举 + */ + private enum class OutcomeResult { + WON, // 赢了 + LOST, // 输了 + UNKNOWN // 无法判断 + } + + /** + * 检查该 outcome 的结果(赢了、输了或无法判断) + * @param market 市场信息 + * @param outcomeIndex outcome 索引 + * @return OutcomeResult + */ + private fun checkOutcomeResult(market: com.wrbug.polymarketbot.api.MarketResponse, outcomeIndex: Int): OutcomeResult { + return try { + // 优先使用 outcomePrices(结算价格数组) + val outcomePrices = market.outcomePrices + if (outcomePrices != null && outcomePrices.isNotBlank()) { + val prices = JsonUtils.parseStringArray(outcomePrices) + if (outcomeIndex < prices.size) { + val price = prices[outcomeIndex].toSafeBigDecimal() + // 如果价格 >= 0.99,认为赢了 + if (price >= BigDecimal("0.99")) { + return OutcomeResult.WON + } + // 如果价格 <= 0.01,认为输了 + if (price <= BigDecimal("0.01")) { + return OutcomeResult.LOST + } + // 其他情况,无法判断 + return OutcomeResult.UNKNOWN + } + } + + // 如果没有 outcomePrices,使用 bestBid 和 bestAsk 判断 + val bestBid = market.bestBid ?: 0.0 + val bestAsk = market.bestAsk ?: 0.0 + + // 如果目标 outcome 不是第一个(index != 0),需要转换价格 + val targetBid = if (outcomeIndex > 0) { + // 第二个 outcome 的 bestBid = 1 - 第一个 outcome 的 bestAsk + BigDecimal.ONE.subtract(BigDecimal.valueOf(bestAsk)) + } else { + BigDecimal.valueOf(bestBid) + } + + // 如果 bestBid >= 0.99,认为赢了 + if (targetBid >= BigDecimal("0.99")) { + return OutcomeResult.WON + } + // 如果 bestBid <= 0.01,认为输了 + if (targetBid <= BigDecimal("0.01")) { + return OutcomeResult.LOST + } + // 其他情况,无法判断 + OutcomeResult.UNKNOWN + } catch (e: Exception) { + logger.warn("检查 outcome 结果失败: marketId=${market.conditionId}, outcomeIndex=$outcomeIndex, error=${e.message}", e) + OutcomeResult.UNKNOWN + } + } + + /** * 在仓位赎回成功后,更新订单状态为已卖出 * 使用卖出逻辑更新所有订单状态(未卖出订单的)