diff --git a/backend/src/main/kotlin/com/wrbug/polymarketbot/controller/markets/MarketController.kt b/backend/src/main/kotlin/com/wrbug/polymarketbot/controller/markets/MarketController.kt index 6deed2d..16ed5e2 100644 --- a/backend/src/main/kotlin/com/wrbug/polymarketbot/controller/markets/MarketController.kt +++ b/backend/src/main/kotlin/com/wrbug/polymarketbot/controller/markets/MarketController.kt @@ -4,8 +4,10 @@ import com.wrbug.polymarketbot.api.LatestPriceResponse import com.wrbug.polymarketbot.dto.* import com.wrbug.polymarketbot.enums.ErrorCode import com.wrbug.polymarketbot.service.accounts.AccountService +import com.wrbug.polymarketbot.service.common.MarketPriceService import com.wrbug.polymarketbot.service.common.PolymarketClobService import kotlinx.coroutines.runBlocking +import java.math.BigDecimal import org.slf4j.LoggerFactory import org.springframework.context.MessageSource import org.springframework.http.ResponseEntity @@ -20,14 +22,16 @@ import org.springframework.web.bind.annotation.* class MarketController( private val accountService: AccountService, private val clobService: PolymarketClobService, + private val marketPriceService: MarketPriceService, private val messageSource: MessageSource ) { private val logger = LoggerFactory.getLogger(MarketController::class.java) /** - * 获取市场价格(通过 Gamma API) - * 使用 Gamma API 获取价格信息,因为 Gamma API 支持 condition_ids 参数 + * 获取市场价格 + * 使用 MarketPriceService 获取当前市场价格(支持多数据源降级) + * 返回当前价格,前端接收后自行填充到 bestBid 字段 */ @PostMapping("/price") fun getMarketPrice(@RequestBody request: MarketPriceRequest): ResponseEntity> { @@ -36,16 +40,16 @@ class MarketController( return ResponseEntity.ok(ApiResponse.error(ErrorCode.PARAM_MARKET_ID_EMPTY, messageSource = messageSource)) } - val result = runBlocking { accountService.getMarketPrice(request.marketId, request.outcomeIndex) } - result.fold( - onSuccess = { response -> - ResponseEntity.ok(ApiResponse.success(response)) - }, - onFailure = { e -> - logger.error("获取市场价格失败: ${e.message}", e) - ResponseEntity.ok(ApiResponse.error(ErrorCode.SERVER_MARKET_PRICE_FETCH_FAILED, e.message, messageSource)) - } + val outcomeIndex = request.outcomeIndex ?: 0 + val price = runBlocking { + marketPriceService.getCurrentMarketPrice(request.marketId, outcomeIndex) + } + + val response = MarketPriceResponse( + marketId = request.marketId, + currentPrice = price.toString() ) + ResponseEntity.ok(ApiResponse.success(response)) } catch (e: Exception) { logger.error("获取市场价格异常: ${e.message}", e) ResponseEntity.ok(ApiResponse.error(ErrorCode.SERVER_MARKET_PRICE_FETCH_FAILED, e.message, messageSource)) diff --git a/backend/src/main/kotlin/com/wrbug/polymarketbot/dto/AccountDto.kt b/backend/src/main/kotlin/com/wrbug/polymarketbot/dto/AccountDto.kt index d7130d3..74d8629 100644 --- a/backend/src/main/kotlin/com/wrbug/polymarketbot/dto/AccountDto.kt +++ b/backend/src/main/kotlin/com/wrbug/polymarketbot/dto/AccountDto.kt @@ -195,14 +195,11 @@ data class LatestPriceRequest( ) /** - * 市场价格响应 + * 市场当前价格响应 */ data class MarketPriceResponse( val marketId: String, - val lastPrice: String?, // 最新成交价 - val bestBid: String?, // 最优买价(用于卖出参考) - val bestAsk: String?, // 最优卖价(用于买入参考) - val midpoint: String? // 中间价 + val currentPrice: String // 当前价格(通过 MarketPriceService 获取,支持多数据源降级) ) /** diff --git a/backend/src/main/kotlin/com/wrbug/polymarketbot/service/accounts/AccountService.kt b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/accounts/AccountService.kt index d82607c..4829b5f 100644 --- a/backend/src/main/kotlin/com/wrbug/polymarketbot/service/accounts/AccountService.kt +++ b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/accounts/AccountService.kt @@ -1154,13 +1154,13 @@ class AccountService( null } + // 优先使用 lastPrice(最近成交价),如果没有则使用 bestBid,最后使用 midpoint + val currentPrice = lastPrice ?: bestBid ?: midpoint ?: "0" + Result.success( MarketPriceResponse( marketId = marketId, - lastPrice = lastPrice, - bestBid = bestBid, - bestAsk = bestAsk, - midpoint = midpoint + currentPrice = currentPrice ) ) } else { 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 faee608..b33c395 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,12 +21,9 @@ 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 com.wrbug.polymarketbot.service.common.BlockchainService +import com.wrbug.polymarketbot.service.common.MarketPriceService import org.springframework.stereotype.Service import java.math.BigDecimal -import java.math.BigInteger import java.util.concurrent.ConcurrentHashMap /** @@ -47,9 +44,7 @@ class PositionCheckService( private val telegramNotificationService: TelegramNotificationService?, private val accountRepository: AccountRepository, private val messageSource: MessageSource, - private val retrofitFactory: RetrofitFactory, - private val blockchainService: BlockchainService, - private val jsonUtils: JsonUtils + private val marketPriceService: MarketPriceService ) { private val logger = LoggerFactory.getLogger(PositionCheckService::class.java) @@ -431,200 +426,10 @@ class PositionCheckService( /** * 获取当前市场最新价(用于更新订单卖出价) - * 优先使用链上查询获取市场结算结果,如果未结算则使用 API 查询 - * 如果市场已关闭: - * - 该 outcome 赢了,返回 1 - * - 该 outcome 输了,返回 0 + * 委托给 MarketPriceService 处理 */ private suspend fun getCurrentMarketPrice(marketId: String, outcomeIndex: Int): BigDecimal { - return try { - // 优先从链上查询市场结算结果(实时性高) - val chainResult = blockchainService.getCondition(marketId) - chainResult.fold( - onSuccess = { (payoutDenominator, payouts) -> - // 如果 payouts 不为空,说明市场已结算 - if (payouts.isNotEmpty() && outcomeIndex < payouts.size) { - val payout = payouts[outcomeIndex] - when { - payout > BigInteger.ZERO -> { - // payout > 0 表示赢了 - logger.info("从链上查询到市场已结算,该 outcome 赢了: marketId=$marketId, outcomeIndex=$outcomeIndex, payout=$payout") - return BigDecimal.ONE - } - payout == BigInteger.ZERO -> { - // payout == 0 表示输了 - logger.info("从链上查询到市场已结算,该 outcome 输了: marketId=$marketId, outcomeIndex=$outcomeIndex, payout=$payout") - return BigDecimal.ZERO - } - else -> { - logger.warn("从链上查询到异常的 payout 值: marketId=$marketId, outcomeIndex=$outcomeIndex, payout=$payout") - } - } - } else { - logger.debug("从链上查询到市场尚未结算: marketId=$marketId, payouts=${payouts.size}") - } - }, - onFailure = { e -> - logger.debug("链上查询市场条件失败,降级到 API 查询: marketId=$marketId, error=${e.message}") - } - ) - - // 链上查询失败或市场未结算,降级到 API 查询 - 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) { - // 检查市场是否已结束:1) closed == true 或 2) endDate 已过 - val isMarketEnded = checkIfMarketEnded(market) - - if (isMarketEnded) { - logger.debug("市场已结束: marketId=$marketId, closed=${market.closed}, endDate=${market.endDate}") - // 市场已结束,检查该 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 -> { - // 无法判断,记录警告并继续使用正常价格逻辑 - logger.warn("市场已结束但无法判断 outcome 结果,使用正常价格: marketId=$marketId, outcomeIndex=$outcomeIndex, closed=${market.closed}, endDate=${market.endDate}, outcomePrices=${market.outcomePrices}, bestBid=${market.bestBid}, bestAsk=${market.bestAsk}") - } - } - } - } - } - - // 如果市场未关闭或无法判断输赢,获取正常价格 - val priceResult = accountService.getMarketPrice(marketId, outcomeIndex) - val marketPrice = priceResult.getOrNull() - if (marketPrice != null) { - // 优先使用 bestBid(最优买价,用于卖出参考),如果没有则使用 midpoint - val priceStr = marketPrice.bestBid ?: marketPrice.midpoint ?: marketPrice.lastPrice - priceStr?.toSafeBigDecimal() ?: BigDecimal.ZERO - } else { - BigDecimal.ZERO - } - } catch (e: Exception) { - logger.error("获取市场最新价失败: marketId=$marketId, outcomeIndex=$outcomeIndex, error=${e.message}", e) - BigDecimal.ZERO - } - } - - /** - * 检查市场是否已结束 - * 判断条件: - * 1. closed == true - * 2. 或 endDate 已过(如果 endDate 不为空) - */ - private fun checkIfMarketEnded(market: com.wrbug.polymarketbot.api.MarketResponse): Boolean { - // 1. 检查 closed 字段 - if (market.closed == true) { - return true - } - - // 2. 检查 endDate 是否已过 - val endDateStr = market.endDate - if (endDateStr != null && endDateStr.isNotBlank()) { - try { - // endDate 可能是 ISO 8601 格式字符串或时间戳 - val endDate = if (endDateStr.matches(Regex("^\\d+$"))) { - // 时间戳(秒或毫秒) - val timestamp = endDateStr.toLong() - // 判断是秒还是毫秒(如果小于 10^10,认为是秒) - if (timestamp < 10000000000L) { - timestamp * 1000 // 转换为毫秒 - } else { - timestamp - } - } else { - // ISO 8601 格式,尝试解析 - java.time.Instant.parse(endDateStr).toEpochMilli() - } - - val now = System.currentTimeMillis() - if (now >= endDate) { - logger.debug("市场 endDate 已过: marketId=${market.conditionId}, endDate=$endDateStr, now=$now") - return true - } - } catch (e: Exception) { - logger.warn("解析 endDate 失败: marketId=${market.conditionId}, endDate=$endDateStr, error=${e.message}") - } - } - - return false - } - - /** - * 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 - } + return marketPriceService.getCurrentMarketPrice(marketId, outcomeIndex) } diff --git a/backend/src/main/kotlin/com/wrbug/polymarketbot/service/common/MarketPriceService.kt b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/common/MarketPriceService.kt new file mode 100644 index 0000000..9c94641 --- /dev/null +++ b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/common/MarketPriceService.kt @@ -0,0 +1,211 @@ +package com.wrbug.polymarketbot.service.common + +import com.wrbug.polymarketbot.api.PolymarketClobApi +import com.wrbug.polymarketbot.repository.AccountRepository +import com.wrbug.polymarketbot.util.CryptoUtils +import com.wrbug.polymarketbot.util.RetrofitFactory +import com.wrbug.polymarketbot.util.toSafeBigDecimal +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service +import java.math.BigDecimal +import java.math.BigInteger + +/** + * 市场价格服务 + * 统一封装从不同数据源获取市场价格的逻辑 + * 数据源包括: + * 1. 链上 RPC 查询(市场结算结果) + * 2. CLOB API(订单簿价格) + */ +@Service +class MarketPriceService( + private val blockchainService: BlockchainService, + private val retrofitFactory: RetrofitFactory, + private val accountRepository: AccountRepository, + private val cryptoUtils: CryptoUtils +) { + + private val logger = LoggerFactory.getLogger(MarketPriceService::class.java) + + /** + * 获取当前市场最新价 + * 优先级: + * 1. 链上查询市场结算结果(如果已结算,返回 1.0 或 0.0) + * 2. CLOB API 查询订单簿价格(最准确,使用 bestBid) + * + * 价格会被截位到 4 位小数(向下截断,不四舍五入),用于显示和后续计算 + * + * @param marketId 市场ID + * @param outcomeIndex 结果索引 + * @return 市场价格(已截位到 4 位小数) + * @throws IllegalStateException 如果所有数据源都失败 + */ + suspend fun getCurrentMarketPrice(marketId: String, outcomeIndex: Int): BigDecimal { + // 1. 优先从链上查询市场结算结果 + val chainPrice = getPriceFromChainCondition(marketId, outcomeIndex) + if (chainPrice != null) { + // 截位到 4 位小数(向下截断,不四舍五入) + return chainPrice.setScale(4, java.math.RoundingMode.DOWN) + } + + // 2. 从 CLOB API 查询订单簿价格(最准确) + val orderbookPrice = getPriceFromClobOrderbook(marketId, outcomeIndex) + if (orderbookPrice != null) { + // 截位到 4 位小数(向下截断,不四舍五入) + return orderbookPrice.setScale(4, java.math.RoundingMode.DOWN) + } + + // 如果所有数据源都失败,抛出异常 + val errorMsg = "无法获取市场价格: marketId=$marketId, outcomeIndex=$outcomeIndex (链上查询和订单簿查询均失败)" + logger.error(errorMsg) + throw IllegalStateException(errorMsg) + } + + /** + * 从链上查询市场结算结果获取价格 + * 如果市场已结算: + * - payout > 0(赢了)→ 返回 1.0 + * - payout == 0(输了)→ 返回 0.0 + * 如果市场未结算或查询失败,返回 null + */ + private suspend fun getPriceFromChainCondition(marketId: String, outcomeIndex: Int): BigDecimal? { + return try { + val chainResult = blockchainService.getCondition(marketId) + chainResult.fold( + onSuccess = { (_, payouts) -> + // 如果 payouts 不为空,说明市场已结算 + if (payouts.isNotEmpty() && outcomeIndex < payouts.size) { + val payout = payouts[outcomeIndex] + when { + payout > BigInteger.ZERO -> { + logger.info("从链上查询到市场已结算,该 outcome 赢了: marketId=$marketId, outcomeIndex=$outcomeIndex, payout=$payout") + return BigDecimal.ONE + } + payout == BigInteger.ZERO -> { + logger.info("从链上查询到市场已结算,该 outcome 输了: marketId=$marketId, outcomeIndex=$outcomeIndex, payout=$payout") + return BigDecimal.ZERO + } + else -> { + logger.warn("从链上查询到异常的 payout 值: marketId=$marketId, outcomeIndex=$outcomeIndex, payout=$payout") + null + } + } + } else { + logger.debug("从链上查询到市场尚未结算: marketId=$marketId, payouts=${payouts.size}") + null + } + }, + onFailure = { e -> + logger.debug("链上查询市场条件失败,降级到 API 查询: marketId=$marketId, error=${e.message}") + null + } + ) + } catch (e: Exception) { + logger.debug("链上查询市场条件异常: marketId=$marketId, outcomeIndex=$outcomeIndex, error=${e.message}") + null + } + } + + + /** + * 从 CLOB API 查询订单簿价格 + * 获取订单簿的 bestBid 和 bestAsk,计算 midpoint = (bestBid + bestAsk) / 2 + * 订单簿数据最准确,反映当前市场真实价格 + * 如果查询失败,返回 null + */ + private suspend fun getPriceFromClobOrderbook(marketId: String, outcomeIndex: Int): BigDecimal? { + return try { + // 获取 tokenId(用于查询特定 outcome 的订单簿) + val tokenIdResult = blockchainService.getTokenId(marketId, outcomeIndex) + if (!tokenIdResult.isSuccess) { + return null + } + + val tokenId = tokenIdResult.getOrNull() ?: return null + + // 尝试使用带鉴权的 CLOB API,如果没有则使用不带鉴权的 API + val clobApi = try { + getAuthenticatedClobApi() ?: retrofitFactory.createClobApiWithoutAuth() + } catch (e: Exception) { + logger.debug("获取带鉴权的 CLOB API 失败,使用不带鉴权的 API: ${e.message}") + retrofitFactory.createClobApiWithoutAuth() + } + + val orderbookResponse = clobApi.getOrderbook(tokenId = tokenId, market = null) + + if (!orderbookResponse.isSuccessful || orderbookResponse.body() == null) { + return null + } + + val orderbook = orderbookResponse.body()!! + + // 获取 bestBid(最高买入价):从 bids 中找到价格最大的 + // bids 表示买入订单列表,价格越高表示愿意出的价格越高 + val bestBid = orderbook.bids + .mapNotNull { it.price.toSafeBigDecimal() } + .maxOrNull() + + // 获取 bestAsk(最低卖出价):从 asks 中找到价格最小的 + // asks 表示卖出订单列表,价格越低表示愿意卖的价格越低 + val bestAsk = orderbook.asks + .mapNotNull { it.price.toSafeBigDecimal() } + .minOrNull() + + // 由于主要用于卖出场景,优先使用 bestBid(最高买入价,卖给愿意买入的人) + // 如果没有 bestBid,则使用 midpoint 或 bestAsk + if (bestBid != null) { + logger.debug("从订单簿获取价格(bestBid): marketId=$marketId, outcomeIndex=$outcomeIndex, bestBid=$bestBid, bestAsk=$bestAsk") + return bestBid + } else if (bestAsk != null && bestAsk > BigDecimal.ZERO) { + // 如果没有 bestBid,使用 bestAsk 作为备选 + logger.debug("从订单簿获取价格(bestAsk): marketId=$marketId, outcomeIndex=$outcomeIndex, bestAsk=$bestAsk") + return bestAsk + } + + null + } catch (e: Exception) { + logger.debug("CLOB API 查询订单簿失败: marketId=$marketId, outcomeIndex=$outcomeIndex, error=${e.message}") + null + } + } + + /** + * 获取带鉴权的 CLOB API 客户端 + * 使用第一个有 API 凭证的账户 + * 如果都没有,返回 null + */ + private fun getAuthenticatedClobApi(): PolymarketClobApi? { + return try { + // 使用第一个有 API 凭证的账户 + val account = accountRepository.findAllByOrderByCreatedAtAsc() + .firstOrNull { it.apiKey != null && it.apiSecret != null && it.apiPassphrase != null } + + if (account == null || account.apiKey == null || account.apiSecret == null || account.apiPassphrase == null) { + return null + } + + // 解密 API 凭证 + val apiKey = account.apiKey + val apiSecret = try { + cryptoUtils.decrypt(account.apiSecret) + } catch (e: Exception) { + logger.debug("解密 API Secret 失败: ${e.message}") + return null + } + val apiPassphrase = try { + cryptoUtils.decrypt(account.apiPassphrase) + } catch (e: Exception) { + logger.debug("解密 API Passphrase 失败: ${e.message}") + return null + } + + // 创建带鉴权的 CLOB API 客户端 + retrofitFactory.createClobApi(apiKey, apiSecret, apiPassphrase, account.walletAddress) + } catch (e: Exception) { + logger.debug("获取带鉴权的 CLOB API 失败: ${e.message}") + null + } + } + +} + 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 9bffd2a..c875847 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 @@ -341,9 +341,9 @@ class CopyTradingStatisticsService( // 传递 outcomeIndex 参数,确保获取对应 outcome 的价格 val result = accountService.getMarketPrice(marketId, outcomeIndex) result.onSuccess { response -> - // 使用中间价,如果没有则使用最后价格 - val price = response.midpoint ?: response.lastPrice - if (price != null) { + // 使用当前价格 + val price = response.currentPrice + if (price.isNotBlank() && price != "0") { // 使用 "marketId:outcomeIndex" 作为 key val key = "$marketId:$outcomeIndex" prices[key] = price diff --git a/frontend/src/pages/PositionList.tsx b/frontend/src/pages/PositionList.tsx index 273a916..969d656 100644 --- a/frontend/src/pages/PositionList.tsx +++ b/frontend/src/pages/PositionList.tsx @@ -399,10 +399,10 @@ const PositionList: React.FC = () => { }) if (response.data.code === 0 && response.data.data) { setMarketPrice(response.data.data) - // 默认使用最优买价作为限价 - if (response.data.data.bestBid) { - setLimitPrice(response.data.data.bestBid) - form.setFieldsValue({ limitPrice: response.data.data.bestBid }) + // 默认使用当前价格作为限价 + if (response.data.data.currentPrice) { + setLimitPrice(response.data.data.currentPrice) + form.setFieldsValue({ limitPrice: response.data.data.currentPrice }) } } } catch (error: any) { @@ -449,12 +449,10 @@ const PositionList: React.FC = () => { } // 获取当前卖出价格(市价或限价) - // 卖出操作应该使用 bestBid(最优买价),因为你要卖给愿意买入的人 const getCurrentSellPrice = (): string => { if (orderType === 'MARKET') { - // 市价订单(卖出):优先使用最优买价(bestBid),因为卖出是卖给买单 - // 如果没有 bestBid,则使用当前价格,最后使用最新成交价 - return marketPrice?.bestBid || selectedPosition?.currentPrice || marketPrice?.lastPrice || '0' + // 市价订单(卖出):使用当前价格 + return marketPrice?.currentPrice || selectedPosition?.currentPrice || '0' } return limitPrice || '0' } @@ -1377,7 +1375,7 @@ const PositionList: React.FC = () => { // 切换订单类型时重新计算收益 if (sellQuantity) { const price = e.target.value === 'MARKET' - ? (marketPrice?.bestBid || selectedPosition?.currentPrice || marketPrice?.lastPrice || '0') + ? (marketPrice?.currentPrice || selectedPosition?.currentPrice || '0') : limitPrice || '0' calculatePnl(sellQuantity, price) } @@ -1469,21 +1467,14 @@ const PositionList: React.FC = () => {
市价参考(卖出)
- {marketPrice?.bestBid ? ( - <>最优买价(卖出参考): {formatNumber(marketPrice.bestBid, 4)} + {marketPrice?.currentPrice ? ( + <>当前价格: {formatNumber(marketPrice.currentPrice, 4)} ) : selectedPosition?.currentPrice ? ( <>当前价格: {formatNumber(selectedPosition.currentPrice, 4)} - ) : marketPrice?.lastPrice ? ( - <>最新成交价: {formatNumber(marketPrice.lastPrice, 4)} ) : ( 暂无价格数据 )}
- {marketPrice?.bestAsk && ( -
- 最优卖价(买入参考): {formatNumber(marketPrice.bestAsk, 4)} -
- )}
)} diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index 8439858..a9472e2 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -434,14 +434,11 @@ export interface MarketPriceRequest { } /** - * 市场价格响应 + * 市场当前价格响应 */ export interface MarketPriceResponse { marketId: string - lastPrice?: string - bestBid?: string - bestAsk?: string - midpoint?: string + currentPrice: string } /**