diff --git a/backend/src/main/kotlin/com/wrbug/polymarketbot/dto/CopyTradingDto.kt b/backend/src/main/kotlin/com/wrbug/polymarketbot/dto/CopyTradingDto.kt index 7a7bf6c..aa9466b 100644 --- a/backend/src/main/kotlin/com/wrbug/polymarketbot/dto/CopyTradingDto.kt +++ b/backend/src/main/kotlin/com/wrbug/polymarketbot/dto/CopyTradingDto.kt @@ -32,7 +32,6 @@ data class CopyTradingCreateRequest( // 过滤条件 val minOrderDepth: String? = null, // 最小订单深度(USDC金额),NULL表示不启用 val maxSpread: String? = null, // 最大价差(绝对价格),NULL表示不启用 - val minOrderbookDepth: String? = null, // 最小订单簿深度(USDC金额),NULL表示不启用 val minPrice: String? = null, // 最低价格(可选),NULL表示不限制最低价 val maxPrice: String? = null, // 最高价格(可选),NULL表示不限制最高价 // 新增配置字段 @@ -64,7 +63,6 @@ data class CopyTradingUpdateRequest( // 过滤条件 val minOrderDepth: String? = null, val maxSpread: String? = null, - val minOrderbookDepth: String? = null, val minPrice: String? = null, // 最低价格(可选),NULL表示不限制最低价 val maxPrice: String? = null, // 最高价格(可选),NULL表示不限制最高价 // 新增配置字段 @@ -133,7 +131,6 @@ data class CopyTradingDto( // 过滤条件 val minOrderDepth: String?, val maxSpread: String?, - val minOrderbookDepth: String?, val minPrice: String?, // 最低价格(可选),NULL表示不限制最低价 val maxPrice: String?, // 最高价格(可选),NULL表示不限制最高价 // 新增配置字段 diff --git a/backend/src/main/kotlin/com/wrbug/polymarketbot/dto/CopyTradingTemplateDto.kt b/backend/src/main/kotlin/com/wrbug/polymarketbot/dto/CopyTradingTemplateDto.kt index 8259736..0f9a26f 100644 --- a/backend/src/main/kotlin/com/wrbug/polymarketbot/dto/CopyTradingTemplateDto.kt +++ b/backend/src/main/kotlin/com/wrbug/polymarketbot/dto/CopyTradingTemplateDto.kt @@ -22,7 +22,6 @@ data class TemplateCreateRequest( // 过滤条件 val minOrderDepth: String? = null, // 最小订单深度(USDC金额),NULL表示不启用 val maxSpread: String? = null, // 最大价差(绝对价格),NULL表示不启用 - val minOrderbookDepth: String? = null, // 最小订单簿深度(USDC金额),NULL表示不启用 val minPrice: String? = null, // 最低价格(可选),NULL表示不限制最低价 val maxPrice: String? = null // 最高价格(可选),NULL表示不限制最高价 ) @@ -50,7 +49,6 @@ data class TemplateUpdateRequest( // 过滤条件 val minOrderDepth: String? = null, // 最小订单深度(USDC金额),NULL表示不启用 val maxSpread: String? = null, // 最大价差(绝对价格),NULL表示不启用 - val minOrderbookDepth: String? = null, // 最小订单簿深度(USDC金额),NULL表示不启用 val minPrice: String? = null, // 最低价格(可选),NULL表示不限制最低价 val maxPrice: String? = null // 最高价格(可选),NULL表示不限制最高价 ) @@ -85,7 +83,6 @@ data class TemplateCopyRequest( // 过滤条件 val minOrderDepth: String? = null, // 最小订单深度(USDC金额),NULL表示不启用 val maxSpread: String? = null, // 最大价差(绝对价格),NULL表示不启用 - val minOrderbookDepth: String? = null, // 最小订单簿深度(USDC金额),NULL表示不启用 val minPrice: String? = null, // 最低价格(可选),NULL表示不限制最低价 val maxPrice: String? = null // 最高价格(可选),NULL表示不限制最高价 ) @@ -120,7 +117,6 @@ data class TemplateDto( // 过滤条件 val minOrderDepth: String?, val maxSpread: String?, - val minOrderbookDepth: String?, val minPrice: String?, // 最低价格(可选),NULL表示不限制最低价 val maxPrice: String?, // 最高价格(可选),NULL表示不限制最高价 val createdAt: Long, diff --git a/backend/src/main/kotlin/com/wrbug/polymarketbot/entity/CopyTrading.kt b/backend/src/main/kotlin/com/wrbug/polymarketbot/entity/CopyTrading.kt index 3b82768..0fa1922 100644 --- a/backend/src/main/kotlin/com/wrbug/polymarketbot/entity/CopyTrading.kt +++ b/backend/src/main/kotlin/com/wrbug/polymarketbot/entity/CopyTrading.kt @@ -78,9 +78,6 @@ data class CopyTrading( @Column(name = "max_spread", precision = 20, scale = 8) val maxSpread: BigDecimal? = null, // 最大价差(绝对价格),NULL表示不启用 - @Column(name = "min_orderbook_depth", precision = 20, scale = 8) - val minOrderbookDepth: BigDecimal? = null, // 最小订单簿深度(USDC金额),NULL表示不启用 - @Column(name = "min_price", precision = 20, scale = 8) val minPrice: BigDecimal? = null, // 最低价格(可选),NULL表示不限制最低价 diff --git a/backend/src/main/kotlin/com/wrbug/polymarketbot/entity/CopyTradingTemplate.kt b/backend/src/main/kotlin/com/wrbug/polymarketbot/entity/CopyTradingTemplate.kt index 7bd650a..9a3d355 100644 --- a/backend/src/main/kotlin/com/wrbug/polymarketbot/entity/CopyTradingTemplate.kt +++ b/backend/src/main/kotlin/com/wrbug/polymarketbot/entity/CopyTradingTemplate.kt @@ -66,9 +66,6 @@ data class CopyTradingTemplate( @Column(name = "max_spread", precision = 20, scale = 8) val maxSpread: BigDecimal? = null, // 最大价差(绝对价格),NULL表示不启用 - @Column(name = "min_orderbook_depth", precision = 20, scale = 8) - val minOrderbookDepth: BigDecimal? = null, // 最小订单簿深度(USDC金额),NULL表示不启用 - @Column(name = "min_price", precision = 20, scale = 8) val minPrice: BigDecimal? = null, // 最低价格(可选),NULL表示不限制最低价 diff --git a/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/configs/CopyTradingFilterService.kt b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/configs/CopyTradingFilterService.kt index 452a831..e3eb97d 100644 --- a/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/configs/CopyTradingFilterService.kt +++ b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/configs/CopyTradingFilterService.kt @@ -25,85 +25,86 @@ class CopyTradingFilterService( * 检查过滤条件 * @param copyTrading 跟单配置 * @param tokenId token ID(用于获取订单簿) - * @param isBuyOrder 是否为买入订单(true=买入,false=卖出) - * @return Pair<是否通过, 失败原因> + * @param tradePrice Leader 交易价格,用于价格区间检查 + * @return 过滤结果 */ suspend fun checkFilters( copyTrading: CopyTrading, tokenId: String, - isBuyOrder: Boolean, tradePrice: BigDecimal? = null // Leader 交易价格,用于价格区间检查 - ): Pair { + ): FilterResult { // 1. 价格区间检查(如果配置了价格区间) if (tradePrice != null) { val priceRangeCheck = checkPriceRange(copyTrading, tradePrice) - if (!priceRangeCheck.first) { - return priceRangeCheck + if (!priceRangeCheck.isPassed) { + return FilterResult.priceRangeFailed(priceRangeCheck.reason) } } - // 2. 价格合理性检查(基础检查,无需配置) - // 这个检查在获取订单簿时进行,如果价格不在 0.01-0.99 范围内,订单簿获取会失败 + // 2. 检查是否需要获取订单簿 + // 只有在配置了需要订单簿的过滤条件时才获取 + val needOrderbook = copyTrading.maxSpread != null || copyTrading.minOrderDepth != null - // 3. 获取订单簿 + if (!needOrderbook) { + // 不需要订单簿,直接通过 + return FilterResult.passed() + } + + // 3. 获取订单簿(仅在需要时,只请求一次) val orderbookResult = clobService.getOrderbookByTokenId(tokenId) if (!orderbookResult.isSuccess) { val error = orderbookResult.exceptionOrNull() - return Pair(false, "获取订单簿失败: ${error?.message ?: "未知错误"}") + return FilterResult.orderbookError("获取订单簿失败: ${error?.message ?: "未知错误"}") } val orderbook = orderbookResult.getOrNull() - if (orderbook == null) { - return Pair(false, "订单簿为空") + ?: return FilterResult.orderbookEmpty() + + // 4. 买一卖一价差过滤(如果配置了) + if (copyTrading.maxSpread != null) { + val spreadCheck = checkSpread(copyTrading, orderbook) + if (!spreadCheck.isPassed) { + return FilterResult.spreadFailed(spreadCheck.reason, orderbook) + } } - // 4. 买一卖一价差过滤 - val spreadCheck = checkSpread(copyTrading, orderbook) - if (!spreadCheck.first) { - return spreadCheck + // 5. 订单深度过滤(如果配置了,检查所有方向) + if (copyTrading.minOrderDepth != null) { + val depthCheck = checkOrderDepth(copyTrading, orderbook) + if (!depthCheck.isPassed) { + return FilterResult.orderDepthFailed(depthCheck.reason, orderbook) + } } - // 5. 订单深度过滤 - val depthCheck = checkOrderDepth(copyTrading, orderbook, isBuyOrder) - if (!depthCheck.first) { - return depthCheck - } - - // 6. 最小订单簿深度过滤(可选) - val orderbookDepthCheck = checkOrderbookDepth(copyTrading, orderbook, isBuyOrder) - if (!orderbookDepthCheck.first) { - return orderbookDepthCheck - } - - return Pair(true, "") + return FilterResult.passed(orderbook) } /** * 检查价格区间 * @param copyTrading 跟单配置 * @param tradePrice Leader 交易价格 - * @return Pair<是否通过, 失败原因> + * @return 过滤结果 */ private fun checkPriceRange( copyTrading: CopyTrading, tradePrice: BigDecimal - ): Pair { + ): FilterResult { // 如果未配置价格区间,直接通过 if (copyTrading.minPrice == null && copyTrading.maxPrice == null) { - return Pair(true, "") + return FilterResult.passed() } // 检查最低价格 if (copyTrading.minPrice != null && tradePrice.lt(copyTrading.minPrice)) { - return Pair(false, "价格低于最低限制: $tradePrice < ${copyTrading.minPrice}") + return FilterResult.priceRangeFailed("价格低于最低限制: $tradePrice < ${copyTrading.minPrice}") } // 检查最高价格 if (copyTrading.maxPrice != null && tradePrice.gt(copyTrading.maxPrice)) { - return Pair(false, "价格高于最高限制: $tradePrice > ${copyTrading.maxPrice}") + return FilterResult.priceRangeFailed("价格高于最高限制: $tradePrice > ${copyTrading.maxPrice}") } - return Pair(true, "") + return FilterResult.passed() } /** @@ -114,10 +115,10 @@ class CopyTradingFilterService( private fun checkSpread( copyTrading: CopyTrading, orderbook: OrderbookResponse - ): Pair { + ): FilterResult { // 如果未启用价差过滤,直接通过 if (copyTrading.maxSpread == null) { - return Pair(true, "") + return FilterResult.passed() } // 获取买盘中的最高价格(bestBid = bids 中的最大值) @@ -131,84 +132,57 @@ class CopyTradingFilterService( .minOrNull() if (bestBid == null || bestAsk == null) { - return Pair(false, "订单簿缺少买一或卖一价格") + return FilterResult.spreadFailed("订单簿缺少买一或卖一价格", orderbook) } // 计算价差(绝对价格) val spread = bestAsk.subtract(bestBid) if (spread.gt(copyTrading.maxSpread)) { - return Pair(false, "价差过大: $spread > ${copyTrading.maxSpread}") + return FilterResult.spreadFailed("价差过大: $spread > ${copyTrading.maxSpread}", orderbook) } - return Pair(true, "") + return FilterResult.passed() } /** - * 检查订单深度 + * 检查订单深度(检查所有方向:买盘和卖盘的总深度) */ private fun checkOrderDepth( copyTrading: CopyTrading, - orderbook: OrderbookResponse, - isBuyOrder: Boolean - ): Pair { + orderbook: OrderbookResponse + ): FilterResult { // 如果未启用订单深度过滤,直接通过 if (copyTrading.minOrderDepth == null) { - return Pair(true, "") + return FilterResult.passed() } - // 对于买入订单,检查卖盘(asks)深度 - // 对于卖出订单,检查买盘(bids)深度 - val orders = if (isBuyOrder) orderbook.asks else orderbook.bids - - // 计算总深度(累计订单金额) - var totalDepth = BigDecimal.ZERO - for (order in orders) { + // 计算买盘(bids)总深度 + var bidsDepth = BigDecimal.ZERO + for (order in orderbook.bids) { val price = order.price.toSafeBigDecimal() val size = order.size.toSafeBigDecimal() val orderAmount = price.multi(size) - totalDepth = totalDepth.add(orderAmount) + bidsDepth = bidsDepth.add(orderAmount) } + // 计算卖盘(asks)总深度 + var asksDepth = BigDecimal.ZERO + for (order in orderbook.asks) { + val price = order.price.toSafeBigDecimal() + val size = order.size.toSafeBigDecimal() + val orderAmount = price.multi(size) + asksDepth = asksDepth.add(orderAmount) + } + + // 计算总深度(买盘 + 卖盘) + val totalDepth = bidsDepth.add(asksDepth) + if (totalDepth.lt(copyTrading.minOrderDepth)) { - return Pair(false, "订单深度不足: $totalDepth < ${copyTrading.minOrderDepth}") + return FilterResult.orderDepthFailed("订单深度不足: $totalDepth < ${copyTrading.minOrderDepth}", orderbook) } - return Pair(true, "") - } - - /** - * 检查最小订单簿深度(前 N 档深度) - */ - private fun checkOrderbookDepth( - copyTrading: CopyTrading, - orderbook: OrderbookResponse, - isBuyOrder: Boolean - ): Pair { - // 如果未启用最小订单簿深度过滤,直接通过 - if (copyTrading.minOrderbookDepth == null) { - return Pair(true, "") - } - - // 对于买入订单,检查卖盘(asks)前 3 档深度 - // 对于卖出订单,检查买盘(bids)前 3 档深度 - val orders = if (isBuyOrder) orderbook.asks else orderbook.bids - val topNOrders = orders.take(3) // 前 3 档 - - // 计算前 N 档总深度 - var totalDepth = BigDecimal.ZERO - for (order in topNOrders) { - val price = order.price.toSafeBigDecimal() - val size = order.size.toSafeBigDecimal() - val orderAmount = price.multi(size) - totalDepth = totalDepth.add(orderAmount) - } - - if (totalDepth.lt(copyTrading.minOrderbookDepth)) { - return Pair(false, "订单簿深度不足: $totalDepth < ${copyTrading.minOrderbookDepth}") - } - - return Pair(true, "") + return FilterResult.passed() } } diff --git a/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/configs/CopyTradingService.kt b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/configs/CopyTradingService.kt index 7ed6128..2ed420f 100644 --- a/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/configs/CopyTradingService.kt +++ b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/configs/CopyTradingService.kt @@ -85,7 +85,6 @@ class CopyTradingService( supportSell = request.supportSell ?: template.supportSell, minOrderDepth = request.minOrderDepth?.toSafeBigDecimal() ?: template.minOrderDepth, maxSpread = request.maxSpread?.toSafeBigDecimal() ?: template.maxSpread, - minOrderbookDepth = request.minOrderbookDepth?.toSafeBigDecimal() ?: template.minOrderbookDepth, minPrice = request.minPrice?.toSafeBigDecimal() ?: template.minPrice, maxPrice = request.maxPrice?.toSafeBigDecimal() ?: template.maxPrice ) @@ -112,7 +111,6 @@ class CopyTradingService( supportSell = request.supportSell ?: true, minOrderDepth = request.minOrderDepth?.toSafeBigDecimal(), maxSpread = request.maxSpread?.toSafeBigDecimal(), - minOrderbookDepth = request.minOrderbookDepth?.toSafeBigDecimal(), minPrice = request.minPrice?.toSafeBigDecimal(), maxPrice = request.maxPrice?.toSafeBigDecimal() ) @@ -139,7 +137,6 @@ class CopyTradingService( supportSell = config.supportSell, minOrderDepth = config.minOrderDepth, maxSpread = config.maxSpread, - minOrderbookDepth = config.minOrderbookDepth, minPrice = config.minPrice, maxPrice = config.maxPrice, configName = configName, @@ -205,7 +202,6 @@ class CopyTradingService( supportSell = request.supportSell ?: copyTrading.supportSell, minOrderDepth = request.minOrderDepth?.toSafeBigDecimal() ?: copyTrading.minOrderDepth, maxSpread = request.maxSpread?.toSafeBigDecimal() ?: copyTrading.maxSpread, - minOrderbookDepth = request.minOrderbookDepth?.toSafeBigDecimal() ?: copyTrading.minOrderbookDepth, minPrice = request.minPrice?.toSafeBigDecimal() ?: copyTrading.minPrice, maxPrice = request.maxPrice?.toSafeBigDecimal() ?: copyTrading.maxPrice, configName = configName, @@ -411,7 +407,6 @@ class CopyTradingService( supportSell = copyTrading.supportSell, minOrderDepth = copyTrading.minOrderDepth?.toPlainString(), maxSpread = copyTrading.maxSpread?.toPlainString(), - minOrderbookDepth = copyTrading.minOrderbookDepth?.toPlainString(), minPrice = copyTrading.minPrice?.toPlainString(), maxPrice = copyTrading.maxPrice?.toPlainString(), configName = copyTrading.configName, @@ -441,7 +436,6 @@ class CopyTradingService( val supportSell: Boolean, val minOrderDepth: BigDecimal?, val maxSpread: BigDecimal?, - val minOrderbookDepth: BigDecimal?, val minPrice: BigDecimal?, val maxPrice: BigDecimal? ) diff --git a/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/configs/FilterResult.kt b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/configs/FilterResult.kt new file mode 100644 index 0000000..c4041e8 --- /dev/null +++ b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/configs/FilterResult.kt @@ -0,0 +1,78 @@ +package com.wrbug.polymarketbot.service.copytrading.configs + +import com.wrbug.polymarketbot.api.OrderbookResponse + +/** + * 过滤结果状态枚举 + */ +enum class FilterStatus { + /** 通过 */ + PASSED, + /** 失败:价格区间 */ + FAILED_PRICE_RANGE, + /** 失败:订单簿获取失败 */ + FAILED_ORDERBOOK_ERROR, + /** 失败:订单簿为空 */ + FAILED_ORDERBOOK_EMPTY, + /** 失败:价差过大 */ + FAILED_SPREAD, + /** 失败:订单深度不足 */ + FAILED_ORDER_DEPTH +} + +/** + * 过滤结果 + */ +data class FilterResult( + /** 过滤状态 */ + val status: FilterStatus, + /** 失败原因(仅在失败时有效) */ + val reason: String = "", + /** 订单簿(仅在需要时返回) */ + val orderbook: OrderbookResponse? = null +) { + /** 是否通过 */ + val isPassed: Boolean + get() = status == FilterStatus.PASSED + + companion object { + /** 通过 */ + fun passed(orderbook: OrderbookResponse? = null) = FilterResult( + status = FilterStatus.PASSED, + orderbook = orderbook + ) + + /** 价格区间失败 */ + fun priceRangeFailed(reason: String) = FilterResult( + status = FilterStatus.FAILED_PRICE_RANGE, + reason = reason + ) + + /** 订单簿获取失败 */ + fun orderbookError(reason: String) = FilterResult( + status = FilterStatus.FAILED_ORDERBOOK_ERROR, + reason = reason + ) + + /** 订单簿为空 */ + fun orderbookEmpty() = FilterResult( + status = FilterStatus.FAILED_ORDERBOOK_EMPTY, + reason = "订单簿为空" + ) + + /** 价差过大 */ + fun spreadFailed(reason: String, orderbook: OrderbookResponse) = FilterResult( + status = FilterStatus.FAILED_SPREAD, + reason = reason, + orderbook = orderbook + ) + + /** 订单深度不足 */ + fun orderDepthFailed(reason: String, orderbook: OrderbookResponse) = FilterResult( + status = FilterStatus.FAILED_ORDER_DEPTH, + reason = reason, + orderbook = orderbook + ) + } +} + diff --git a/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/statistics/CopyOrderTrackingService.kt b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/statistics/CopyOrderTrackingService.kt index 6bfeb97..c308990 100644 --- a/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/statistics/CopyOrderTrackingService.kt +++ b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/statistics/CopyOrderTrackingService.kt @@ -11,8 +11,10 @@ import kotlinx.coroutines.* import org.slf4j.LoggerFactory import org.springframework.dao.DataIntegrityViolationException import com.wrbug.polymarketbot.service.copytrading.configs.CopyTradingFilterService +import com.wrbug.polymarketbot.service.copytrading.configs.FilterStatus import com.wrbug.polymarketbot.service.copytrading.orders.OrderSigningService import com.wrbug.polymarketbot.service.common.BlockchainService +import com.wrbug.polymarketbot.service.common.PolymarketClobService import com.wrbug.polymarketbot.service.system.TelegramNotificationService import com.wrbug.polymarketbot.util.CryptoUtils import org.springframework.stereotype.Service @@ -38,6 +40,7 @@ class CopyOrderTrackingService( private val leaderRepository: LeaderRepository, private val orderSigningService: OrderSigningService, private val blockchainService: BlockchainService, + private val clobService: PolymarketClobService, private val retrofitFactory: RetrofitFactory, private val cryptoUtils: CryptoUtils, private val telegramNotificationService: TelegramNotificationService? = null // 可选,避免循环依赖 @@ -215,10 +218,12 @@ class CopyOrderTrackingService( // 过滤条件检查(在计算订单参数之前) // 传入 Leader 交易价格,用于价格区间检查 + // 订单簿只请求一次,返回给后续逻辑使用 val tradePrice = trade.price.toSafeBigDecimal() - val filterCheck = filterService.checkFilters(copyTrading, tokenId, isBuyOrder = true, tradePrice = tradePrice) - if (!filterCheck.first) { - logger.warn("过滤条件检查失败,跳过创建订单: copyTradingId=${copyTrading.id}, reason=${filterCheck.second}") + val filterResult = filterService.checkFilters(copyTrading, tokenId, tradePrice = tradePrice) + val orderbook = filterResult.orderbook // 获取订单簿(如果需要) + if (!filterResult.isPassed) { + logger.warn("过滤条件检查失败,跳过创建订单: copyTradingId=${copyTrading.id}, reason=${filterResult.reason}") // 记录被过滤的订单并发送通知(异步,不阻塞) notificationScope.launch { @@ -242,8 +247,8 @@ class CopyOrderTrackingService( val marketTitle = marketInfo?.question ?: trade.market val marketSlug = marketInfo?.slug - // 从 filterReason 中提取 filterType - val filterType = extractFilterType(filterCheck.second) + // 从过滤结果中提取 filterType + val filterType = extractFilterType(filterResult.status, filterResult.reason) // 计算买入数量(用于记录,即使被过滤也记录) val calculatedQuantity = try { @@ -268,7 +273,7 @@ class CopyOrderTrackingService( price = trade.price.toSafeBigDecimal(), size = trade.size.toSafeBigDecimal(), calculatedQuantity = calculatedQuantity, - filterReason = filterCheck.second, + filterReason = filterResult.reason, filterType = filterType ) @@ -294,7 +299,7 @@ class CopyOrderTrackingService( outcome = trade.outcome, price = trade.price, size = trade.size, - filterReason = filterCheck.second, + filterReason = filterResult.reason, filterType = filterType, accountName = account.accountName, walletAddress = account.walletAddress, @@ -736,8 +741,16 @@ class CopyOrderTrackingService( } val tokenId = tokenIdResult.getOrNull() ?: return - // 6. 计算卖出价格(应用价格容忍度) - val sellPrice = calculateAdjustedPrice(leaderSellTrade.price.toSafeBigDecimal(), copyTrading, isBuy = false) + // 6. 计算卖出价格(优先使用订单簿 bestBid,失败则使用 Leader 价格,固定按90%计算) + val leaderPrice = leaderSellTrade.price.toSafeBigDecimal() + val sellPrice = runCatching { + clobService.getOrderbookByTokenId(tokenId) + .getOrNull() + ?.let { calculateMarketSellPrice(it) } + } + .onFailure { e -> logger.warn("获取订单簿或计算 bestBid 失败,使用 Leader 价格: tokenId=$tokenId, error=${e.message}") } + .getOrNull() + ?: calculateFallbackSellPrice(leaderPrice) // 7. 解密私钥(在方法开始时解密一次,后续复用) val decryptedPrivateKey = decryptPrivateKey(account) @@ -1127,19 +1140,22 @@ class CopyOrderTrackingService( /** * 计算调整后的价格(应用价格容忍度) + * 如果价格容忍度为0,使用默认值5% */ private fun calculateAdjustedPrice( originalPrice: BigDecimal, copyTrading: CopyTrading, isBuy: Boolean ): BigDecimal { - // 如果价格容忍度为0,直接返回原价格 - if (copyTrading.priceTolerance.eq(BigDecimal.ZERO)) { - return originalPrice + // 如果价格容忍度为0,使用默认值5% + val tolerance = if (copyTrading.priceTolerance.eq(BigDecimal.ZERO)) { + BigDecimal("5") + } else { + copyTrading.priceTolerance } // 计算价格调整范围(百分比) - val tolerancePercent = copyTrading.priceTolerance.div(100) + val tolerancePercent = tolerance.div(100) val adjustment = originalPrice.multi(tolerancePercent) return if (isBuy) { @@ -1152,23 +1168,39 @@ class CopyOrderTrackingService( } /** - * 从过滤原因中提取过滤类型 + * 计算市价卖出价格(使用订单簿的 bestBid,固定按90%计算) */ - private fun extractFilterType(filterReason: String): String { - return when { - filterReason.contains("价格低于最低限制", ignoreCase = true) || filterReason.contains("价格高于最高限制", ignoreCase = true) -> "PRICE_RANGE" - filterReason.contains("订单深度不足", ignoreCase = true) -> "ORDER_DEPTH" - filterReason.contains("价差过大", ignoreCase = true) -> "SPREAD" - filterReason.contains("订单簿深度不足", ignoreCase = true) -> "ORDERBOOK_DEPTH" - filterReason.contains("价格", ignoreCase = true) && filterReason.contains( - "合理", - ignoreCase = true - ) -> "PRICE_VALIDITY" + private fun calculateMarketSellPrice( + orderbook: com.wrbug.polymarketbot.api.OrderbookResponse + ): BigDecimal { + // 获取 bestBid(最高买入价) + val bestBid = orderbook.bids + .mapNotNull { it.price.toSafeBigDecimal() } + .maxOrNull() + ?: throw IllegalStateException("订单簿 bids 为空,无法获取 bestBid") - filterReason.contains("市场状态", ignoreCase = true) -> "MARKET_STATUS" - filterReason.contains("获取订单簿失败", ignoreCase = true) -> "ORDERBOOK_ERROR" - filterReason.contains("订单簿为空", ignoreCase = true) -> "ORDERBOOK_EMPTY" - else -> "UNKNOWN" + // 卖出:bestBid * 0.9(固定按90%计算,确保能立即成交) + return calculateFallbackSellPrice(bestBid) + } + + /** + * 计算降级卖出价格(固定按90%计算) + */ + private fun calculateFallbackSellPrice(price: BigDecimal): BigDecimal { + return price.multi(BigDecimal("0.9")).coerceAtLeast(BigDecimal("0.01")) + } + + /** + * 从过滤结果中提取过滤类型 + */ + private fun extractFilterType(status: FilterStatus, reason: String): String { + return when (status) { + FilterStatus.PASSED -> "PASSED" + FilterStatus.FAILED_PRICE_RANGE -> "PRICE_RANGE" + FilterStatus.FAILED_ORDERBOOK_ERROR -> "ORDERBOOK_ERROR" + FilterStatus.FAILED_ORDERBOOK_EMPTY -> "ORDERBOOK_EMPTY" + FilterStatus.FAILED_SPREAD -> "SPREAD" + FilterStatus.FAILED_ORDER_DEPTH -> "ORDER_DEPTH" } } diff --git a/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/templates/CopyTradingTemplateService.kt b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/templates/CopyTradingTemplateService.kt index 18918b3..d5de87e 100644 --- a/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/templates/CopyTradingTemplateService.kt +++ b/backend/src/main/kotlin/com/wrbug/polymarketbot/service/copytrading/templates/CopyTradingTemplateService.kt @@ -61,7 +61,6 @@ class CopyTradingTemplateService( supportSell = request.supportSell ?: true, minOrderDepth = request.minOrderDepth?.toSafeBigDecimal(), maxSpread = request.maxSpread?.toSafeBigDecimal(), - minOrderbookDepth = request.minOrderbookDepth?.toSafeBigDecimal(), minPrice = request.minPrice?.toSafeBigDecimal(), maxPrice = request.maxPrice?.toSafeBigDecimal() ) @@ -120,7 +119,6 @@ class CopyTradingTemplateService( supportSell = request.supportSell ?: template.supportSell, minOrderDepth = request.minOrderDepth?.toSafeBigDecimal() ?: template.minOrderDepth, maxSpread = request.maxSpread?.toSafeBigDecimal() ?: template.maxSpread, - minOrderbookDepth = request.minOrderbookDepth?.toSafeBigDecimal() ?: template.minOrderbookDepth, minPrice = request.minPrice?.toSafeBigDecimal() ?: template.minPrice, maxPrice = request.maxPrice?.toSafeBigDecimal() ?: template.maxPrice, updatedAt = System.currentTimeMillis() @@ -187,7 +185,6 @@ class CopyTradingTemplateService( supportSell = request.supportSell ?: sourceTemplate.supportSell, minOrderDepth = request.minOrderDepth?.toSafeBigDecimal() ?: sourceTemplate.minOrderDepth, maxSpread = request.maxSpread?.toSafeBigDecimal() ?: sourceTemplate.maxSpread, - minOrderbookDepth = request.minOrderbookDepth?.toSafeBigDecimal() ?: sourceTemplate.minOrderbookDepth, minPrice = request.minPrice?.toSafeBigDecimal() ?: sourceTemplate.minPrice, maxPrice = request.maxPrice?.toSafeBigDecimal() ?: sourceTemplate.maxPrice ) @@ -261,7 +258,6 @@ class CopyTradingTemplateService( supportSell = template.supportSell, minOrderDepth = template.minOrderDepth?.toPlainString(), maxSpread = template.maxSpread?.toPlainString(), - minOrderbookDepth = template.minOrderbookDepth?.toPlainString(), minPrice = template.minPrice?.toPlainString(), maxPrice = template.maxPrice?.toPlainString(), createdAt = template.createdAt, diff --git a/frontend/src/locales/en/common.json b/frontend/src/locales/en/common.json index 24ccede..fc1333e 100644 --- a/frontend/src/locales/en/common.json +++ b/frontend/src/locales/en/common.json @@ -597,10 +597,10 @@ "priceToleranceTooltip": "Allowed adjustment range for copy price based on Leader price, used to adjust price within Leader price ± tolerance range to improve fill rate. For example: set to 5%, Leader price is 0.5, then copy price can be in 0.475-0.525 range.", "priceTolerancePlaceholder": "Default 5% (optional)", "minOrderDepth": "Min Order Depth (USDC)", - "minOrderDepthTooltip": "Minimum order depth (USDC amount), NULL means this filter is not enabled. Ensures market has sufficient liquidity", + "minOrderDepthTooltip": "Check total order amount (bids + asks) in orderbook to ensure sufficient liquidity. Leave empty to disable", "minOrderDepthPlaceholder": "For example: 100 (optional, leave empty to disable)", "maxSpread": "Max Spread (Absolute Price)", - "maxSpreadTooltip": "Maximum spread (absolute price), NULL means this filter is not enabled. Avoid copying in markets with excessive spreads", + "maxSpreadTooltip": "Maximum spread (absolute price). Avoid copying in markets with excessive spreads. Leave empty to disable", "maxSpreadPlaceholder": "For example: 0.05 (5 cents, optional, leave empty to disable)", "minOrderbookDepth": "Min Orderbook Depth (USDC)", "minOrderbookDepthTooltip": "Minimum orderbook depth (USDC amount), NULL means this filter is not enabled. Check depth of first N levels", @@ -654,10 +654,10 @@ "priceToleranceTooltip": "Allowed adjustment range for copy price based on Leader price, used to adjust price within Leader price ± tolerance range to improve fill rate. For example: set to 5%, Leader price is 0.5, then copy price can be in 0.475-0.525 range.", "priceTolerancePlaceholder": "Default 5% (optional)", "minOrderDepth": "Min Order Depth (USDC)", - "minOrderDepthTooltip": "Minimum order depth (USDC amount), NULL means this filter is not enabled. Ensures market has sufficient liquidity", + "minOrderDepthTooltip": "Check total order amount (bids + asks) in orderbook to ensure sufficient liquidity. Leave empty to disable", "minOrderDepthPlaceholder": "For example: 100 (optional, leave empty to disable)", "maxSpread": "Max Spread (Absolute Price)", - "maxSpreadTooltip": "Maximum spread (absolute price), NULL means this filter is not enabled. Avoid copying in markets with excessive spreads", + "maxSpreadTooltip": "Maximum spread (absolute price). Avoid copying in markets with excessive spreads. Leave empty to disable", "maxSpreadPlaceholder": "For example: 0.05 (5 cents, optional, leave empty to disable)", "minOrderbookDepth": "Min Orderbook Depth (USDC)", "minOrderbookDepthTooltip": "Minimum orderbook depth (USDC amount), NULL means this filter is not enabled. Check depth of first N levels", @@ -719,10 +719,10 @@ "delaySecondsPlaceholder": "Default 0 (copy immediately)", "filterConditions": "Filter Conditions (Optional)", "minOrderDepth": "Min Order Depth (USDC)", - "minOrderDepthTooltip": "Minimum order depth (USDC amount), NULL means this filter is not enabled. Ensures market has sufficient liquidity", + "minOrderDepthTooltip": "Check total order amount (bids + asks) in orderbook to ensure sufficient liquidity. Leave empty to disable", "minOrderDepthPlaceholder": "For example: 100 (optional, leave empty to disable)", "maxSpread": "Max Spread (Absolute Price)", - "maxSpreadTooltip": "Maximum spread (absolute price), NULL means this filter is not enabled. Avoid copying in markets with excessive spreads", + "maxSpreadTooltip": "Maximum spread (absolute price). Avoid copying in markets with excessive spreads. Leave empty to disable", "maxSpreadPlaceholder": "For example: 0.05 (5 cents, optional, leave empty to disable)", "minOrderbookDepth": "Min Orderbook Depth (USDC)", "minOrderbookDepthTooltip": "Minimum orderbook depth (USDC amount), NULL means this filter is not enabled. Check depth of first N levels", @@ -794,10 +794,10 @@ "delaySecondsPlaceholder": "Default 0 (copy immediately)", "filterConditions": "Filter Conditions (Optional)", "minOrderDepth": "Min Order Depth (USDC)", - "minOrderDepthTooltip": "Minimum order depth (USDC amount), NULL means this filter is not enabled", + "minOrderDepthTooltip": "Check total order amount (bids + asks) in orderbook to ensure sufficient liquidity. Leave empty to disable", "minOrderDepthPlaceholder": "For example: 100 (optional, leave empty to disable)", "maxSpread": "Max Spread (Absolute Price)", - "maxSpreadTooltip": "Maximum spread (absolute price), NULL means this filter is not enabled", + "maxSpreadTooltip": "Maximum spread (absolute price). Avoid copying in markets with excessive spreads. Leave empty to disable", "maxSpreadPlaceholder": "For example: 0.05 (5 cents, optional, leave empty to disable)", "minOrderbookDepth": "Min Orderbook Depth (USDC)", "minOrderbookDepthTooltip": "Minimum orderbook depth (USDC amount), NULL means this filter is not enabled", diff --git a/frontend/src/locales/zh-CN/common.json b/frontend/src/locales/zh-CN/common.json index 57b0236..9c3c3dc 100644 --- a/frontend/src/locales/zh-CN/common.json +++ b/frontend/src/locales/zh-CN/common.json @@ -512,10 +512,10 @@ "priceToleranceTooltip": "允许跟单价格在 Leader 价格基础上的调整范围,用于在 Leader 价格 ± 容忍度范围内调整价格,提高成交率。例如:设置为 5%,Leader 价格为 0.5,则跟单价格可在 0.475-0.525 范围内。", "priceTolerancePlaceholder": "默认 5%(可选)", "minOrderDepth": "最小订单深度 (USDC)", - "minOrderDepthTooltip": "最小订单深度(USDC金额),NULL表示不启用此过滤。确保市场有足够的流动性", + "minOrderDepthTooltip": "检查订单簿的总订单金额(买盘+卖盘),确保市场有足够的流动性。不填写则不启用此过滤", "minOrderDepthPlaceholder": "例如:100(可选,不填写表示不启用)", "maxSpread": "最大价差(绝对价格)", - "maxSpreadTooltip": "最大价差(绝对价格),NULL表示不启用此过滤。避免在价差过大的市场跟单", + "maxSpreadTooltip": "最大价差(绝对价格)。避免在价差过大的市场跟单。不填写则不启用此过滤", "maxSpreadPlaceholder": "例如:0.05(5美分,可选,不填写表示不启用)", "minOrderbookDepth": "最小订单簿深度 (USDC)", "minOrderbookDepthTooltip": "最小订单簿深度(USDC金额),NULL表示不启用此过滤。检查前 N 档的深度", @@ -569,10 +569,10 @@ "priceToleranceTooltip": "允许跟单价格在 Leader 价格基础上的调整范围,用于在 Leader 价格 ± 容忍度范围内调整价格,提高成交率。例如:设置为 5%,Leader 价格为 0.5,则跟单价格可在 0.475-0.525 范围内。", "priceTolerancePlaceholder": "默认 5%(可选)", "minOrderDepth": "最小订单深度 (USDC)", - "minOrderDepthTooltip": "最小订单深度(USDC金额),NULL表示不启用此过滤。确保市场有足够的流动性", + "minOrderDepthTooltip": "检查订单簿的总订单金额(买盘+卖盘),确保市场有足够的流动性。不填写则不启用此过滤", "minOrderDepthPlaceholder": "例如:100(可选,不填写表示不启用)", "maxSpread": "最大价差(绝对价格)", - "maxSpreadTooltip": "最大价差(绝对价格),NULL表示不启用此过滤。避免在价差过大的市场跟单", + "maxSpreadTooltip": "最大价差(绝对价格)。避免在价差过大的市场跟单。不填写则不启用此过滤", "maxSpreadPlaceholder": "例如:0.05(5美分,可选,不填写表示不启用)", "minOrderbookDepth": "最小订单簿深度 (USDC)", "minOrderbookDepthTooltip": "最小订单簿深度(USDC金额),NULL表示不启用此过滤。检查前 N 档的深度", @@ -646,10 +646,10 @@ "delaySecondsPlaceholder": "默认 0(立即跟单)", "filterConditions": "过滤条件(可选)", "minOrderDepth": "最小订单深度 (USDC)", - "minOrderDepthTooltip": "最小订单深度(USDC金额),NULL表示不启用此过滤。确保市场有足够的流动性", + "minOrderDepthTooltip": "检查订单簿的总订单金额(买盘+卖盘),确保市场有足够的流动性。不填写则不启用此过滤", "minOrderDepthPlaceholder": "例如:100(可选,不填写表示不启用)", "maxSpread": "最大价差(绝对价格)", - "maxSpreadTooltip": "最大价差(绝对价格),NULL表示不启用此过滤。避免在价差过大的市场跟单", + "maxSpreadTooltip": "最大价差(绝对价格)。避免在价差过大的市场跟单。不填写则不启用此过滤", "maxSpreadPlaceholder": "例如:0.05(5美分,可选,不填写表示不启用)", "minOrderbookDepth": "最小订单簿深度 (USDC)", "minOrderbookDepthTooltip": "最小订单簿深度(USDC金额),NULL表示不启用此过滤。检查前 N 档的深度", @@ -721,10 +721,10 @@ "delaySecondsPlaceholder": "默认 0(立即跟单)", "filterConditions": "过滤条件(可选)", "minOrderDepth": "最小订单深度 (USDC)", - "minOrderDepthTooltip": "最小订单深度(USDC金额),NULL表示不启用此过滤", + "minOrderDepthTooltip": "检查订单簿的总订单金额(买盘+卖盘),确保市场有足够的流动性。不填写则不启用此过滤", "minOrderDepthPlaceholder": "例如:100(可选,不填写表示不启用)", "maxSpread": "最大价差(绝对价格)", - "maxSpreadTooltip": "最大价差(绝对价格),NULL表示不启用此过滤", + "maxSpreadTooltip": "最大价差(绝对价格)。避免在价差过大的市场跟单。不填写则不启用此过滤", "maxSpreadPlaceholder": "例如:0.05(5美分,可选,不填写表示不启用)", "minOrderbookDepth": "最小订单簿深度 (USDC)", "minOrderbookDepthTooltip": "最小订单簿深度(USDC金额),NULL表示不启用此过滤", diff --git a/frontend/src/locales/zh-TW/common.json b/frontend/src/locales/zh-TW/common.json index 1a28fd2..6f69df8 100644 --- a/frontend/src/locales/zh-TW/common.json +++ b/frontend/src/locales/zh-TW/common.json @@ -597,10 +597,10 @@ "priceToleranceTooltip": "允許跟單價格在 Leader 價格基礎上的調整範圍,用於在 Leader 價格 ± 容忍度範圍內調整價格,提高成交率。例如:設置為 5%,Leader 價格為 0.5,則跟單價格可在 0.475-0.525 範圍內。", "priceTolerancePlaceholder": "默認 5%(可選)", "minOrderDepth": "最小訂單深度 (USDC)", - "minOrderDepthTooltip": "最小訂單深度(USDC金額),NULL表示不啟用此過濾。確保市場有足夠的流動性", + "minOrderDepthTooltip": "檢查訂單簿的總訂單金額(買盤+賣盤),確保市場有足夠的流動性。不填寫則不啟用此過濾", "minOrderDepthPlaceholder": "例如:100(可選,不填寫表示不啟用)", "maxSpread": "最大價差(絕對價格)", - "maxSpreadTooltip": "最大價差(絕對價格),NULL表示不啟用此過濾。避免在價差過大的市場跟單", + "maxSpreadTooltip": "最大價差(絕對價格)。避免在價差過大的市場跟單。不填寫則不啟用此過濾", "maxSpreadPlaceholder": "例如:0.05(5美分,可選,不填寫表示不啟用)", "minOrderbookDepth": "最小訂單簿深度 (USDC)", "minOrderbookDepthTooltip": "最小訂單簿深度(USDC金額),NULL表示不啟用此過濾。檢查前 N 檔的深度", @@ -654,10 +654,10 @@ "priceToleranceTooltip": "允許跟單價格在 Leader 價格基礎上的調整範圍,用於在 Leader 價格 ± 容忍度範圍內調整價格,提高成交率。例如:設置為 5%,Leader 價格為 0.5,則跟單價格可在 0.475-0.525 範圍內。", "priceTolerancePlaceholder": "默認 5%(可選)", "minOrderDepth": "最小訂單深度 (USDC)", - "minOrderDepthTooltip": "最小訂單深度(USDC金額),NULL表示不啟用此過濾。確保市場有足夠的流動性", + "minOrderDepthTooltip": "檢查訂單簿的總訂單金額(買盤+賣盤),確保市場有足夠的流動性。不填寫則不啟用此過濾", "minOrderDepthPlaceholder": "例如:100(可選,不填寫表示不啟用)", "maxSpread": "最大價差(絕對價格)", - "maxSpreadTooltip": "最大價差(絕對價格),NULL表示不啟用此過濾。避免在價差過大的市場跟單", + "maxSpreadTooltip": "最大價差(絕對價格)。避免在價差過大的市場跟單。不填寫則不啟用此過濾", "maxSpreadPlaceholder": "例如:0.05(5美分,可選,不填寫表示不啟用)", "minOrderbookDepth": "最小訂單簿深度 (USDC)", "minOrderbookDepthTooltip": "最小訂單簿深度(USDC金額),NULL表示不啟用此過濾。檢查前 N 檔的深度", @@ -719,10 +719,10 @@ "delaySecondsPlaceholder": "默認 0(立即跟單)", "filterConditions": "過濾條件(可選)", "minOrderDepth": "最小訂單深度 (USDC)", - "minOrderDepthTooltip": "最小訂單深度(USDC金額),NULL表示不啟用此過濾。確保市場有足夠的流動性", + "minOrderDepthTooltip": "檢查訂單簿的總訂單金額(買盤+賣盤),確保市場有足夠的流動性。不填寫則不啟用此過濾", "minOrderDepthPlaceholder": "例如:100(可選,不填寫表示不啟用)", "maxSpread": "最大價差(絕對價格)", - "maxSpreadTooltip": "最大價差(絕對價格),NULL表示不啟用此過濾。避免在價差過大的市場跟單", + "maxSpreadTooltip": "最大價差(絕對價格)。避免在價差過大的市場跟單。不填寫則不啟用此過濾", "maxSpreadPlaceholder": "例如:0.05(5美分,可選,不填寫表示不啟用)", "minOrderbookDepth": "最小訂單簿深度 (USDC)", "minOrderbookDepthTooltip": "最小訂單簿深度(USDC金額),NULL表示不啟用此過濾。檢查前 N 檔的深度", @@ -794,10 +794,10 @@ "delaySecondsPlaceholder": "默認 0(立即跟單)", "filterConditions": "過濾條件(可選)", "minOrderDepth": "最小訂單深度 (USDC)", - "minOrderDepthTooltip": "最小訂單深度(USDC金額),NULL表示不啟用此過濾", + "minOrderDepthTooltip": "檢查訂單簿的總訂單金額(買盤+賣盤),確保市場有足夠的流動性。不填寫則不啟用此過濾", "minOrderDepthPlaceholder": "例如:100(可選,不填寫表示不啟用)", "maxSpread": "最大價差(絕對價格)", - "maxSpreadTooltip": "最大價差(絕對價格),NULL表示不啟用此過濾", + "maxSpreadTooltip": "最大價差(絕對價格)。避免在價差過大的市場跟單。不填寫則不啟用此過濾", "maxSpreadPlaceholder": "例如:0.05(5美分,可選,不填寫表示不啟用)", "minOrderbookDepth": "最小訂單簿深度 (USDC)", "minOrderbookDepthTooltip": "最小訂單簿深度(USDC金額),NULL表示不啟用此過濾", diff --git a/frontend/src/pages/CopyTradingAdd.tsx b/frontend/src/pages/CopyTradingAdd.tsx index 4c151b0..e10f8ab 100644 --- a/frontend/src/pages/CopyTradingAdd.tsx +++ b/frontend/src/pages/CopyTradingAdd.tsx @@ -84,7 +84,6 @@ const CopyTradingAdd: React.FC = () => { supportSell: template.supportSell, minOrderDepth: template.minOrderDepth ? parseFloat(template.minOrderDepth) : undefined, maxSpread: template.maxSpread ? parseFloat(template.maxSpread) : undefined, - minOrderbookDepth: template.minOrderbookDepth ? parseFloat(template.minOrderbookDepth) : undefined, minPrice: template.minPrice ? parseFloat(template.minPrice) : undefined, maxPrice: template.maxPrice ? parseFloat(template.maxPrice) : undefined }) @@ -133,7 +132,6 @@ const CopyTradingAdd: React.FC = () => { supportSell: values.supportSell !== false, minOrderDepth: values.minOrderDepth?.toString(), maxSpread: values.maxSpread?.toString(), - minOrderbookDepth: values.minOrderbookDepth?.toString(), minPrice: values.minPrice?.toString(), maxPrice: values.maxPrice?.toString(), configName: values.configName?.trim(), @@ -412,7 +410,7 @@ const CopyTradingAdd: React.FC = () => { { { /> - - - - {t('copyTradingAdd.priceRangeFilter') || '价格区间过滤'} { supportSell: found.supportSell, minOrderDepth: found.minOrderDepth ? parseFloat(found.minOrderDepth) : undefined, maxSpread: found.maxSpread ? parseFloat(found.maxSpread) : undefined, - minOrderbookDepth: found.minOrderbookDepth ? parseFloat(found.minOrderbookDepth) : undefined, minPrice: found.minPrice ? parseFloat(found.minPrice) : undefined, maxPrice: found.maxPrice ? parseFloat(found.maxPrice) : undefined, configName: found.configName || '', @@ -122,7 +121,6 @@ const CopyTradingEdit: React.FC = () => { supportSell: values.supportSell, minOrderDepth: values.minOrderDepth?.toString(), maxSpread: values.maxSpread?.toString(), - minOrderbookDepth: values.minOrderbookDepth?.toString(), minPrice: values.minPrice?.toString(), maxPrice: values.maxPrice?.toString(), configName: values.configName?.trim() || undefined, @@ -381,7 +379,7 @@ const CopyTradingEdit: React.FC = () => { { { /> - - - - {t('copyTradingEdit.priceRangeFilter') || '价格区间过滤'} { supportSell: values.supportSell !== false, minOrderDepth: values.minOrderDepth?.toString(), maxSpread: values.maxSpread?.toString(), - minOrderbookDepth: values.minOrderbookDepth?.toString(), minPrice: values.minPrice?.toString(), maxPrice: values.maxPrice?.toString() }) @@ -258,7 +257,7 @@ const TemplateAdd: React.FC = () => { { { /> - - - - {t('templateAdd.priceRangeFilter') || '价格区间过滤'} { priceTolerance: parseFloat(template.priceTolerance), minOrderDepth: template.minOrderDepth ? parseFloat(template.minOrderDepth) : undefined, maxSpread: template.maxSpread ? parseFloat(template.maxSpread) : undefined, - minOrderbookDepth: template.minOrderbookDepth ? parseFloat(template.minOrderbookDepth) : undefined, minPrice: template.minPrice ? parseFloat(template.minPrice) : undefined, maxPrice: template.maxPrice ? parseFloat(template.maxPrice) : undefined }) @@ -99,7 +98,6 @@ const TemplateEdit: React.FC = () => { supportSell: values.supportSell, minOrderDepth: values.minOrderDepth?.toString(), maxSpread: values.maxSpread?.toString(), - minOrderbookDepth: values.minOrderbookDepth?.toString(), minPrice: values.minPrice?.toString(), maxPrice: values.maxPrice?.toString() }) @@ -295,7 +293,7 @@ const TemplateEdit: React.FC = () => { { { /> - - - - {t('templateEdit.priceRangeFilter') || '价格区间过滤'} { supportSell: template.supportSell, minOrderDepth: template.minOrderDepth ? parseFloat(template.minOrderDepth) : undefined, maxSpread: template.maxSpread ? parseFloat(template.maxSpread) : undefined, - minOrderbookDepth: template.minOrderbookDepth ? parseFloat(template.minOrderbookDepth) : undefined, minPrice: template.minPrice ? parseFloat(template.minPrice) : undefined, maxPrice: template.maxPrice ? parseFloat(template.maxPrice) : undefined }) @@ -122,7 +121,6 @@ const TemplateList: React.FC = () => { supportSell: values.supportSell !== false, minOrderDepth: values.minOrderDepth?.toString(), maxSpread: values.maxSpread?.toString(), - minOrderbookDepth: values.minOrderbookDepth?.toString(), minPrice: values.minPrice?.toString(), maxPrice: values.maxPrice?.toString() }) @@ -613,7 +611,7 @@ const TemplateList: React.FC = () => { { { /> - - - - 价格区间过滤