优化跟单过滤逻辑和卖出价格计算

- 优化订单簿请求:仅在需要时请求,避免不必要的API调用
- 卖出价格改为市价卖出:优先使用订单簿bestBid,失败则使用Leader价格,固定按90%计算
- 价格容忍度默认值:如果为0,自动使用5%
- 使用枚举和数据类优化checkFilters方法:
  - 新增FilterResult数据类和FilterStatus枚举
  - 移除无用字段isBuyOrder参数
  - 使用类型安全的枚举替代字符串判断
- 优化代码结构:减少if-else嵌套,使用链式调用
- 移除最小订单簿深度功能(minOrderbookDepth)
- 优化最小订单深度逻辑:检查所有方向(买盘+卖盘)的总深度
This commit is contained in:
WrBug
2025-12-09 00:52:50 +08:00
parent d393bd7b40
commit f0c40533cf
18 changed files with 235 additions and 257 deletions
@@ -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表示不限制最高价
// 新增配置字段
@@ -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,
@@ -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表示不限制最低价
@@ -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表示不限制最低价
@@ -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<Boolean, String> {
): 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<Boolean, String> {
): 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<Boolean, String> {
): 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<Boolean, String> {
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<Boolean, String> {
// 如果未启用最小订单簿深度过滤,直接通过
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()
}
}
@@ -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?
)
@@ -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
)
}
}
@@ -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"
}
}
@@ -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,
+8 -8
View File
@@ -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",
+8 -8
View File
@@ -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表示不启用此过滤",
+8 -8
View File
@@ -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表示不啟用此過濾",
+2 -18
View File
@@ -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 = () => {
<Form.Item
label={t('copyTradingAdd.minOrderDepth') || '最小订单深度 (USDC)'}
name="minOrderDepth"
tooltip={t('copyTradingAdd.minOrderDepthTooltip') || '最小订单深度(USDC金额),NULL表示不启用此过滤。确保市场有足够的流动性'}
tooltip={t('copyTradingAdd.minOrderDepthTooltip') || '检查订单簿的总订单金额(买盘+卖盘),确保市场有足够的流动性。不填写则不启用此过滤'}
>
<InputNumber
min={0}
@@ -426,7 +424,7 @@ const CopyTradingAdd: React.FC = () => {
<Form.Item
label={t('copyTradingAdd.maxSpread') || '最大价差(绝对价格)'}
name="maxSpread"
tooltip={t('copyTradingAdd.maxSpreadTooltip') || '最大价差(绝对价格)NULL表示不启用此过滤。避免在价差过大的市场跟单'}
tooltip={t('copyTradingAdd.maxSpreadTooltip') || '最大价差(绝对价格)。避免在价差过大的市场跟单。不填写则不启用此过滤'}
>
<InputNumber
min={0}
@@ -437,20 +435,6 @@ const CopyTradingAdd: React.FC = () => {
/>
</Form.Item>
<Form.Item
label={t('copyTradingAdd.minOrderbookDepth') || '最小订单簿深度 (USDC)'}
name="minOrderbookDepth"
tooltip={t('copyTradingAdd.minOrderbookDepthTooltip') || '最小订单簿深度(USDC金额),NULL表示不启用此过滤。检查前 N 档的深度'}
>
<InputNumber
min={0}
step={0.0001}
precision={4}
style={{ width: '100%' }}
placeholder={t('copyTradingAdd.minOrderbookDepthPlaceholder') || '例如:50(可选,不填写表示不启用)'}
/>
</Form.Item>
<Divider>{t('copyTradingAdd.priceRangeFilter') || '价格区间过滤'}</Divider>
<Form.Item
+2 -18
View File
@@ -56,7 +56,6 @@ const CopyTradingEdit: React.FC = () => {
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 = () => {
<Form.Item
label={t('copyTradingEdit.minOrderDepth') || '最小订单深度 (USDC)'}
name="minOrderDepth"
tooltip={t('copyTradingEdit.minOrderDepthTooltip') || '最小订单深度(USDC金额),NULL表示不启用此过滤'}
tooltip={t('copyTradingEdit.minOrderDepthTooltip') || '检查订单簿的总订单金额(买盘+卖盘),确保市场有足够的流动性。不填写则不启用此过滤'}
>
<InputNumber
min={0}
@@ -395,7 +393,7 @@ const CopyTradingEdit: React.FC = () => {
<Form.Item
label={t('copyTradingEdit.maxSpread') || '最大价差(绝对价格)'}
name="maxSpread"
tooltip={t('copyTradingEdit.maxSpreadTooltip') || '最大价差(绝对价格)NULL表示不启用此过滤'}
tooltip={t('copyTradingEdit.maxSpreadTooltip') || '最大价差(绝对价格)。避免在价差过大的市场跟单。不填写则不启用此过滤'}
>
<InputNumber
min={0}
@@ -406,20 +404,6 @@ const CopyTradingEdit: React.FC = () => {
/>
</Form.Item>
<Form.Item
label={t('copyTradingEdit.minOrderbookDepth') || '最小订单簿深度 (USDC)'}
name="minOrderbookDepth"
tooltip={t('copyTradingEdit.minOrderbookDepthTooltip') || '最小订单簿深度(USDC金额),NULL表示不启用此过滤'}
>
<InputNumber
min={0}
step={0.0001}
precision={4}
style={{ width: '100%' }}
placeholder={t('copyTradingEdit.minOrderbookDepthPlaceholder') || '例如:50(可选,不填写表示不启用)'}
/>
</Form.Item>
<Divider>{t('copyTradingEdit.priceRangeFilter') || '价格区间过滤'}</Divider>
<Form.Item
+2 -17
View File
@@ -54,7 +54,6 @@ const TemplateAdd: 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()
})
@@ -258,7 +257,7 @@ const TemplateAdd: React.FC = () => {
<Form.Item
label={t('templateAdd.minOrderDepth') || '最小订单深度 (USDC)'}
name="minOrderDepth"
tooltip={t('templateAdd.minOrderDepthTooltip') || '最小订单深度(USDC金额),NULL表示不启用此过滤。确保市场有足够的流动性'}
tooltip={t('templateAdd.minOrderDepthTooltip') || '检查订单簿的总订单金额(买盘+卖盘),确保市场有足够的流动性。不填写则不启用此过滤'}
>
<InputNumber
min={0}
@@ -272,7 +271,7 @@ const TemplateAdd: React.FC = () => {
<Form.Item
label={t('templateAdd.maxSpread') || '最大价差(绝对价格)'}
name="maxSpread"
tooltip={t('templateAdd.maxSpreadTooltip') || '最大价差(绝对价格)NULL表示不启用此过滤。避免在价差过大的市场跟单'}
tooltip={t('templateAdd.maxSpreadTooltip') || '最大价差(绝对价格)。避免在价差过大的市场跟单。不填写则不启用此过滤'}
>
<InputNumber
min={0}
@@ -283,20 +282,6 @@ const TemplateAdd: React.FC = () => {
/>
</Form.Item>
<Form.Item
label={t('templateAdd.minOrderbookDepth') || '最小订单簿深度 (USDC)'}
name="minOrderbookDepth"
tooltip={t('templateAdd.minOrderbookDepthTooltip') || '最小订单簿深度(USDC金额),NULL表示不启用此过滤。检查前 N 档的深度'}
>
<InputNumber
min={0}
step={0.0001}
precision={4}
style={{ width: '100%' }}
placeholder={t('templateAdd.minOrderbookDepthPlaceholder') || '例如:50(可选,不填写表示不启用)'}
/>
</Form.Item>
<Divider>{t('templateAdd.priceRangeFilter') || '价格区间过滤'}</Divider>
<Form.Item
+2 -18
View File
@@ -40,7 +40,6 @@ const TemplateEdit: React.FC = () => {
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 = () => {
<Form.Item
label={t('templateEdit.minOrderDepth') || '最小订单深度 (USDC)'}
name="minOrderDepth"
tooltip={t('templateEdit.minOrderDepthTooltip') || '最小订单深度(USDC金额),NULL表示不启用此过滤。确保市场有足够的流动性'}
tooltip={t('templateEdit.minOrderDepthTooltip') || '检查订单簿的总订单金额(买盘+卖盘),确保市场有足够的流动性。不填写则不启用此过滤'}
>
<InputNumber
min={0}
@@ -309,7 +307,7 @@ const TemplateEdit: React.FC = () => {
<Form.Item
label={t('templateEdit.maxSpread') || '最大价差(绝对价格)'}
name="maxSpread"
tooltip={t('templateEdit.maxSpreadTooltip') || '最大价差(绝对价格)NULL表示不启用此过滤。避免在价差过大的市场跟单'}
tooltip={t('templateEdit.maxSpreadTooltip') || '最大价差(绝对价格)。避免在价差过大的市场跟单。不填写则不启用此过滤'}
>
<InputNumber
min={0}
@@ -320,20 +318,6 @@ const TemplateEdit: React.FC = () => {
/>
</Form.Item>
<Form.Item
label={t('templateEdit.minOrderbookDepth') || '最小订单簿深度 (USDC)'}
name="minOrderbookDepth"
tooltip={t('templateEdit.minOrderbookDepthTooltip') || '最小订单簿深度(USDC金额),NULL表示不启用此过滤。检查前 N 档的深度'}
>
<InputNumber
min={0}
step={0.0001}
precision={4}
style={{ width: '100%' }}
placeholder={t('templateEdit.minOrderbookDepthPlaceholder') || '例如:50(可选,不填写表示不启用)'}
/>
</Form.Item>
<Divider>{t('templateEdit.priceRangeFilter') || '价格区间过滤'}</Divider>
<Form.Item
+2 -18
View File
@@ -74,7 +74,6 @@ const TemplateList: 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
})
@@ -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 = () => {
<Form.Item
label="最小订单深度 (USDC)"
name="minOrderDepth"
tooltip="最小订单深度(USDC金额),NULL表示不启用此过滤。确保市场有足够的流动性"
tooltip="检查订单簿的总订单金额(买盘+卖盘),确保市场有足够的流动性。不填写则不启用此过滤"
>
<InputNumber
min={0}
@@ -627,7 +625,7 @@ const TemplateList: React.FC = () => {
<Form.Item
label="最大价差(绝对价格)"
name="maxSpread"
tooltip="最大价差(绝对价格)NULL表示不启用此过滤。避免在价差过大的市场跟单"
tooltip="最大价差(绝对价格)。避免在价差过大的市场跟单。不填写则不启用此过滤"
>
<InputNumber
min={0}
@@ -638,20 +636,6 @@ const TemplateList: React.FC = () => {
/>
</Form.Item>
<Form.Item
label="最小订单簿深度 (USDC)"
name="minOrderbookDepth"
tooltip="最小订单簿深度(USDC金额),NULL表示不启用此过滤。检查前 N 档的深度"
>
<InputNumber
min={0}
step={0.0001}
precision={4}
style={{ width: '100%' }}
placeholder="例如:50(可选,不填写表示不启用)"
/>
</Form.Item>
<Divider></Divider>
<Form.Item
-4
View File
@@ -112,7 +112,6 @@ export interface CopyTradingTemplate {
// 过滤条件
minOrderDepth?: string
maxSpread?: string
minOrderbookDepth?: string
minPrice?: string // 最低价格(可选),NULL表示不限制最低价
maxPrice?: string // 最高价格(可选),NULL表示不限制最高价
createdAt: number
@@ -204,7 +203,6 @@ export interface CopyTrading {
// 过滤条件
minOrderDepth?: string
maxSpread?: string
minOrderbookDepth?: string
minPrice?: string // 最低价格(可选),NULL表示不限制最低价
maxPrice?: string // 最高价格(可选),NULL表示不限制最高价
// 新增配置字段
@@ -248,7 +246,6 @@ export interface CopyTradingCreateRequest {
// 过滤条件
minOrderDepth?: string
maxSpread?: string
minOrderbookDepth?: string
minPrice?: string // 最低价格(可选),NULL表示不限制最低价
maxPrice?: string // 最高价格(可选),NULL表示不限制最高价
// 新增配置字段
@@ -280,7 +277,6 @@ export interface CopyTradingUpdateRequest {
// 过滤条件
minOrderDepth?: string
maxSpread?: string
minOrderbookDepth?: string
minPrice?: string // 最低价格(可选),NULL表示不限制最低价
maxPrice?: string // 最高价格(可选),NULL表示不限制最高价
// 新增配置字段