feat: 添加推送已过滤订单功能并修复相关问题

- 新增推送已过滤订单功能(pushFilteredOrders),默认关闭
  - 数据库迁移:添加 pushFilteredOrders 字段到模板表和跟单配置表
  - 后端:在实体、DTO、Service 中添加 pushFilteredOrders 字段支持
  - 前端:在模板新增、编辑、推送页面和跟单配置新增、编辑页面添加开关
  - 多语言:添加中文、繁体中文、英文翻译
  - 通知逻辑:发送过滤订单通知时检查 pushFilteredOrders 字段

- 修复新建跟单配置时 pushFilteredOrders 字段未生效的问题
  - 修复手动输入模式下 pushFilteredOrders 被硬编码为 false 的问题
  - 修复从模板填充表单时未加载 pushFilteredOrders 的问题
  - 添加 CopyTradingTemplate 接口的 pushFilteredOrders 字段定义

- 修复新建和编辑页面的截止时间输入框交互问题
  - 修复删除后失去焦点自动填充1的问题
  - 优化 onChange 和 onBlur 处理逻辑,支持清空操作
This commit is contained in:
WrBug
2026-01-12 14:29:12 +08:00
parent cb8e46919f
commit 9c303e0a82
17 changed files with 183 additions and 40 deletions
@@ -43,6 +43,7 @@ data class CopyTradingCreateRequest(
// 新增配置字段
val configName: String? = null, // 配置名(可选)
val pushFailedOrders: Boolean? = null, // 推送失败订单(可选)
val pushFilteredOrders: Boolean? = null, // 推送已过滤订单(可选)
val maxMarketEndDate: Long? = null // 市场截止时间限制(毫秒时间戳),仅跟单截止时间小于此时间的订单,NULL表示不启用
)
@@ -81,6 +82,7 @@ data class CopyTradingUpdateRequest(
// 新增配置字段
val configName: String? = null, // 配置名(可选,但提供时必须非空)
val pushFailedOrders: Boolean? = null, // 推送失败订单(可选)
val pushFilteredOrders: Boolean? = null, // 推送已过滤订单(可选)
val maxMarketEndDate: Long? = null // 市场截止时间限制(毫秒时间戳),仅跟单截止时间小于此时间的订单,NULL表示不启用
)
@@ -156,6 +158,7 @@ data class CopyTradingDto(
// 新增配置字段
val configName: String? = null, // 配置名(可选)
val pushFailedOrders: Boolean = false, // 推送失败订单(默认关闭)
val pushFilteredOrders: Boolean = false, // 推送已过滤订单(默认关闭)
val maxMarketEndDate: Long? = null, // 市场截止时间限制(毫秒时间戳),仅跟单截止时间小于此时间的订单,NULL表示不启用
val createdAt: Long,
val updatedAt: Long
@@ -23,7 +23,8 @@ data class TemplateCreateRequest(
val minOrderDepth: String? = null, // 最小订单深度(USDC金额),NULL表示不启用
val maxSpread: String? = null, // 最大价差(绝对价格),NULL表示不启用
val minPrice: String? = null, // 最低价格(可选),NULL表示不限制最低价
val maxPrice: String? = null // 最高价格(可选),NULL表示不限制最高价
val maxPrice: String? = null, // 最高价格(可选),NULL表示不限制最高价
val pushFilteredOrders: Boolean? = null // 推送已过滤订单(默认关闭)
)
/**
@@ -50,7 +51,8 @@ data class TemplateUpdateRequest(
val minOrderDepth: String? = null, // 最小订单深度(USDC金额),NULL表示不启用
val maxSpread: String? = null, // 最大价差(绝对价格),NULL表示不启用
val minPrice: String? = null, // 最低价格(可选),NULL表示不限制最低价
val maxPrice: String? = null // 最高价格(可选),NULL表示不限制最高价
val maxPrice: String? = null, // 最高价格(可选),NULL表示不限制最高价
val pushFilteredOrders: Boolean? = null // 推送已过滤订单(默认关闭)
)
/**
@@ -84,7 +86,8 @@ data class TemplateCopyRequest(
val minOrderDepth: String? = null, // 最小订单深度(USDC金额),NULL表示不启用
val maxSpread: String? = null, // 最大价差(绝对价格),NULL表示不启用
val minPrice: String? = null, // 最低价格(可选),NULL表示不限制最低价
val maxPrice: String? = null // 最高价格(可选),NULL表示不限制最高价
val maxPrice: String? = null, // 最高价格(可选),NULL表示不限制最高价
val pushFilteredOrders: Boolean? = null // 推送已过滤订单(默认关闭)
)
/**
@@ -119,6 +122,7 @@ data class TemplateDto(
val maxSpread: String?,
val minPrice: String?, // 最低价格(可选),NULL表示不限制最低价
val maxPrice: String?, // 最高价格(可选),NULL表示不限制最高价
val pushFilteredOrders: Boolean, // 推送已过滤订单(默认关闭)
val createdAt: Long,
val updatedAt: Long
)
@@ -100,6 +100,9 @@ data class CopyTrading(
@Column(name = "push_failed_orders", nullable = false)
val pushFailedOrders: Boolean = false, // 推送失败订单(默认关闭)
@Column(name = "push_filtered_orders", nullable = false)
val pushFilteredOrders: Boolean = false, // 推送已过滤订单(默认关闭)
@Column(name = "max_market_end_date")
val maxMarketEndDate: Long? = null, // 市场截止时间限制(毫秒时间戳),仅跟单截止时间小于此时间的订单,NULL表示不启用
@@ -72,6 +72,9 @@ data class CopyTradingTemplate(
@Column(name = "max_price", precision = 20, scale = 8)
val maxPrice: BigDecimal? = null, // 最高价格(可选),NULL表示不限制最高价
@Column(name = "push_filtered_orders", nullable = false)
val pushFilteredOrders: Boolean = false, // 推送已过滤订单(默认关闭)
@Column(name = "created_at", nullable = false)
val createdAt: Long = System.currentTimeMillis(),
@@ -87,7 +87,8 @@ class CopyTradingService(
maxPositionCount = request.maxPositionCount,
keywordFilterMode = request.keywordFilterMode ?: "DISABLED",
keywords = convertKeywordsToJson(request.keywords),
maxMarketEndDate = request.maxMarketEndDate
maxMarketEndDate = request.maxMarketEndDate,
pushFilteredOrders = request.pushFilteredOrders ?: template.pushFilteredOrders
)
} else {
// 手动输入(所有字段必须提供)
@@ -118,7 +119,8 @@ class CopyTradingService(
maxPositionCount = request.maxPositionCount,
keywordFilterMode = request.keywordFilterMode ?: "DISABLED",
keywords = convertKeywordsToJson(request.keywords),
maxMarketEndDate = request.maxMarketEndDate
maxMarketEndDate = request.maxMarketEndDate,
pushFilteredOrders = request.pushFilteredOrders ?: false // 手动输入时使用请求中的值,默认为 false
)
}
@@ -151,7 +153,8 @@ class CopyTradingService(
keywords = config.keywords,
configName = configName,
pushFailedOrders = request.pushFailedOrders ?: false,
maxMarketEndDate = config.maxMarketEndDate
maxMarketEndDate = config.maxMarketEndDate,
pushFilteredOrders = config.pushFilteredOrders
)
val saved = copyTradingRepository.save(copyTrading)
@@ -283,6 +286,7 @@ class CopyTradingService(
},
configName = configName,
pushFailedOrders = request.pushFailedOrders ?: copyTrading.pushFailedOrders,
pushFilteredOrders = request.pushFilteredOrders ?: copyTrading.pushFilteredOrders,
// 处理 maxMarketEndDate-1 表示要清空(设置为 null),null 表示不更新
maxMarketEndDate = if (request.maxMarketEndDate != null) {
if (request.maxMarketEndDate == -1L) {
@@ -505,6 +509,7 @@ class CopyTradingService(
keywords = convertJsonToKeywords(copyTrading.keywords),
configName = copyTrading.configName,
pushFailedOrders = copyTrading.pushFailedOrders,
pushFilteredOrders = copyTrading.pushFilteredOrders,
maxMarketEndDate = copyTrading.maxMarketEndDate,
createdAt = copyTrading.createdAt,
updatedAt = copyTrading.updatedAt
@@ -567,6 +572,7 @@ class CopyTradingService(
val maxPositionCount: Int?,
val keywordFilterMode: String,
val keywords: String?, // JSON 字符串
val maxMarketEndDate: Long? // 市场截止时间限制(毫秒时间戳)
val maxMarketEndDate: Long?, // 市场截止时间限制(毫秒时间戳)
val pushFilteredOrders: Boolean // 推送已过滤订单(默认关闭)
)
}
@@ -347,27 +347,29 @@ open class CopyOrderTrackingService(
logger.error("保存被过滤订单失败: ${e.message}", e)
}
// 发送 Telegram 通知
val locale = try {
org.springframework.context.i18n.LocaleContextHolder.getLocale()
} catch (e: Exception) {
java.util.Locale("zh", "CN") // 默认简体中文
}
// 发送 Telegram 通知(仅在 pushFilteredOrders 为 true 时发送)
if (copyTrading.pushFilteredOrders) {
val locale = try {
org.springframework.context.i18n.LocaleContextHolder.getLocale()
} catch (e: Exception) {
java.util.Locale("zh", "CN") // 默认简体中文
}
telegramNotificationService?.sendOrderFilteredNotification(
marketTitle = marketTitle,
marketId = trade.market,
marketSlug = marketSlug,
side = "BUY",
outcome = trade.outcome,
price = trade.price,
size = trade.size,
filterReason = filterResult.reason,
filterType = filterType,
accountName = account.accountName,
walletAddress = account.walletAddress,
locale = locale
)
telegramNotificationService?.sendOrderFilteredNotification(
marketTitle = marketTitle,
marketId = trade.market,
marketSlug = marketSlug,
side = "BUY",
outcome = trade.outcome,
price = trade.price,
size = trade.size,
filterReason = filterResult.reason,
filterType = filterType,
accountName = account.accountName,
walletAddress = account.walletAddress,
locale = locale
)
}
} catch (e: Exception) {
logger.error("处理被过滤订单通知失败: ${e.message}", e)
}
@@ -62,7 +62,8 @@ class CopyTradingTemplateService(
minOrderDepth = request.minOrderDepth?.toSafeBigDecimal(),
maxSpread = request.maxSpread?.toSafeBigDecimal(),
minPrice = request.minPrice?.toSafeBigDecimal(),
maxPrice = request.maxPrice?.toSafeBigDecimal()
maxPrice = request.maxPrice?.toSafeBigDecimal(),
pushFilteredOrders = request.pushFilteredOrders ?: false
)
val saved = templateRepository.save(template)
@@ -121,6 +122,7 @@ class CopyTradingTemplateService(
maxSpread = request.maxSpread?.toSafeBigDecimal() ?: template.maxSpread,
minPrice = request.minPrice?.toSafeBigDecimal() ?: template.minPrice,
maxPrice = request.maxPrice?.toSafeBigDecimal() ?: template.maxPrice,
pushFilteredOrders = request.pushFilteredOrders ?: template.pushFilteredOrders,
updatedAt = System.currentTimeMillis()
)
@@ -186,7 +188,8 @@ class CopyTradingTemplateService(
minOrderDepth = request.minOrderDepth?.toSafeBigDecimal() ?: sourceTemplate.minOrderDepth,
maxSpread = request.maxSpread?.toSafeBigDecimal() ?: sourceTemplate.maxSpread,
minPrice = request.minPrice?.toSafeBigDecimal() ?: sourceTemplate.minPrice,
maxPrice = request.maxPrice?.toSafeBigDecimal() ?: sourceTemplate.maxPrice
maxPrice = request.maxPrice?.toSafeBigDecimal() ?: sourceTemplate.maxPrice,
pushFilteredOrders = request.pushFilteredOrders ?: sourceTemplate.pushFilteredOrders
)
val saved = templateRepository.save(newTemplate)
@@ -260,6 +263,7 @@ class CopyTradingTemplateService(
maxSpread = template.maxSpread?.toPlainString(),
minPrice = template.minPrice?.toPlainString(),
maxPrice = template.maxPrice?.toPlainString(),
pushFilteredOrders = template.pushFilteredOrders,
createdAt = template.createdAt,
updatedAt = template.updatedAt
)
@@ -0,0 +1,13 @@
-- ============================================
-- V24: 添加推送已过滤订单字段到模板表和跟单配置表
-- 用于配置是否推送被过滤的订单通知,默认关闭
-- ============================================
-- 添加推送已过滤订单字段到模板表
ALTER TABLE copy_trading_templates
ADD COLUMN push_filtered_orders BOOLEAN NOT NULL DEFAULT FALSE COMMENT '推送已过滤订单(默认关闭)';
-- 添加推送已过滤订单字段到跟单配置表
ALTER TABLE copy_trading
ADD COLUMN push_filtered_orders BOOLEAN NOT NULL DEFAULT FALSE COMMENT '推送已过滤订单(默认关闭)';
+10
View File
@@ -571,6 +571,8 @@
"deleteConfirmDesc": "This action cannot be undone. Please ensure no copy trading relationships are using this template",
"copySuccess": "Template copied successfully",
"copyFailed": "Failed to copy template",
"pushFilteredOrders": "Push Filtered Orders",
"pushFilteredOrdersTooltip": "When enabled, filtered orders will be pushed to Telegram",
"minAmountError": "Minimum amount must be >= 1",
"fixedAmountRequired": "Please enter fixed copy trading amount",
"invalidNumber": "Please enter a valid number",
@@ -623,6 +625,8 @@
"maxPricePlaceholder": "Max Price (leave empty for no limit)",
"supportSell": "Support Sell",
"supportSellTooltip": "Whether to copy Leader's sell orders. Enabled: copy both Leader's buy and sell orders; Disabled: only copy Leader's buy orders, ignore sell orders.",
"pushFilteredOrders": "Push Filtered Orders",
"pushFilteredOrdersTooltip": "When enabled, filtered orders will be pushed to Telegram",
"create": "Create Template",
"createSuccess": "Template created successfully",
"createFailed": "Failed to create template",
@@ -687,6 +691,8 @@
"maxPositionCountPlaceholder": "For example: 10 (optional, leave empty to disable)",
"supportSell": "Support Sell",
"supportSellTooltip": "Whether to copy Leader's sell orders. Enabled: copy both Leader's buy and sell orders; Disabled: only copy Leader's buy orders, ignore sell orders.",
"pushFilteredOrders": "Push Filtered Orders",
"pushFilteredOrdersTooltip": "When enabled, filtered orders will be pushed to Telegram",
"invalidNumber": "Please enter a valid number"
},
"copyTradingAdd": {
@@ -783,6 +789,8 @@
"advancedSettings": "Advanced Settings",
"pushFailedOrders": "Push Failed Orders",
"pushFailedOrdersTooltip": "When enabled, failed orders will be pushed to Telegram",
"pushFilteredOrders": "Push Filtered Orders",
"pushFilteredOrdersTooltip": "When enabled, filtered orders will be pushed to Telegram",
"autoRedeem": "Auto Redeem",
"autoRedeemTooltip": "When enabled, the system will automatically redeem redeemable positions. Requires Builder API Key configuration to take effect",
"builderApiKeyNotConfigured": "Builder API Key Not Configured",
@@ -888,6 +896,8 @@
"advancedSettings": "Advanced Settings",
"pushFailedOrders": "Push Failed Orders",
"pushFailedOrdersTooltip": "When enabled, failed orders will be pushed to Telegram",
"pushFilteredOrders": "Push Filtered Orders",
"pushFilteredOrdersTooltip": "When enabled, filtered orders will be pushed to Telegram",
"autoRedeem": "Auto Redeem",
"autoRedeemTooltip": "When enabled, the system will automatically redeem redeemable positions. Requires Builder API Key configuration to take effect",
"builderApiKeyNotConfigured": "Builder API Key Not Configured",
+10
View File
@@ -571,6 +571,8 @@
"deleteConfirmDesc": "删除后无法恢复,请确保没有跟单关系在使用该模板",
"copySuccess": "复制模板成功",
"copyFailed": "复制模板失败",
"pushFilteredOrders": "推送已过滤订单",
"pushFilteredOrdersTooltip": "开启后,被过滤的订单会推送到 Telegram",
"minAmountError": "最小金额必须 >= 1",
"fixedAmountRequired": "请输入固定跟单金额",
"invalidNumber": "请输入有效的数字",
@@ -623,6 +625,8 @@
"maxPricePlaceholder": "最高价(留空不限制)",
"supportSell": "跟单卖出",
"supportSellTooltip": "是否跟单 Leader 的卖出订单。开启:跟单 Leader 的买入和卖出订单;关闭:只跟单 Leader 的买入订单,忽略卖出订单。",
"pushFilteredOrders": "推送已过滤订单",
"pushFilteredOrdersTooltip": "开启后,被过滤的订单会推送到 Telegram",
"create": "创建模板",
"createSuccess": "创建模板成功",
"createFailed": "创建模板失败",
@@ -687,6 +691,8 @@
"maxPositionCountPlaceholder": "例如:10(可选,不填写表示不启用)",
"supportSell": "跟单卖出",
"supportSellTooltip": "是否跟单 Leader 的卖出订单。开启:跟单 Leader 的买入和卖出订单;关闭:只跟单 Leader 的买入订单,忽略卖出订单。",
"pushFilteredOrders": "推送已过滤订单",
"pushFilteredOrdersTooltip": "开启后,被过滤的订单会推送到 Telegram",
"invalidNumber": "请输入有效的数字"
},
"copyTradingAdd": {
@@ -699,6 +705,8 @@
"advancedSettings": "高级设置",
"pushFailedOrders": "推送失败订单",
"pushFailedOrdersTooltip": "开启后,失败的订单会推送到 Telegram",
"pushFilteredOrders": "推送已过滤订单",
"pushFilteredOrdersTooltip": "开启后,被过滤的订单会推送到 Telegram",
"autoRedeem": "自动赎回",
"autoRedeemTooltip": "开启后,系统会自动赎回可赎回的仓位。需要配置 Builder API Key 才能生效",
"builderApiKeyNotConfigured": "Builder API Key 未配置",
@@ -812,6 +820,8 @@
"advancedSettings": "高级设置",
"pushFailedOrders": "推送失败订单",
"pushFailedOrdersTooltip": "开启后,失败的订单会推送到 Telegram",
"pushFilteredOrders": "推送已过滤订单",
"pushFilteredOrdersTooltip": "开启后,被过滤的订单会推送到 Telegram",
"autoRedeem": "自动赎回",
"autoRedeemTooltip": "开启后,系统会自动赎回可赎回的仓位。需要配置 Builder API Key 才能生效",
"builderApiKeyNotConfigured": "Builder API Key 未配置",
+10
View File
@@ -571,6 +571,8 @@
"deleteConfirmDesc": "刪除後無法恢復,請確保沒有跟單關係在使用該模板",
"copySuccess": "複製模板成功",
"copyFailed": "複製模板失敗",
"pushFilteredOrders": "推送已過濾訂單",
"pushFilteredOrdersTooltip": "開啟後,被過濾的訂單會推送到 Telegram",
"minAmountError": "最小金額必須 >= 1",
"fixedAmountRequired": "請輸入固定跟單金額",
"invalidNumber": "請輸入有效的數字",
@@ -623,6 +625,8 @@
"maxPricePlaceholder": "最高價(留空不限制)",
"supportSell": "跟單賣出",
"supportSellTooltip": "是否跟單 Leader 的賣出訂單。開啟:跟單 Leader 的買入和賣出訂單;關閉:只跟單 Leader 的買入訂單,忽略賣出訂單。",
"pushFilteredOrders": "推送已過濾訂單",
"pushFilteredOrdersTooltip": "開啟後,被過濾的訂單會推送到 Telegram",
"create": "創建模板",
"createSuccess": "創建模板成功",
"createFailed": "創建模板失敗",
@@ -687,6 +691,8 @@
"maxPositionCountPlaceholder": "例如:10(可選,不填寫表示不啟用)",
"supportSell": "跟單賣出",
"supportSellTooltip": "是否跟單 Leader 的賣出訂單。開啟:跟單 Leader 的買入和賣出訂單;關閉:只跟單 Leader 的買入訂單,忽略賣出訂單。",
"pushFilteredOrders": "推送已過濾訂單",
"pushFilteredOrdersTooltip": "開啟後,被過濾的訂單會推送到 Telegram",
"invalidNumber": "請輸入有效的數字"
},
"copyTradingAdd": {
@@ -783,6 +789,8 @@
"advancedSettings": "高級設置",
"pushFailedOrders": "推送失敗訂單",
"pushFailedOrdersTooltip": "開啟後,失敗的訂單會推送到 Telegram",
"pushFilteredOrders": "推送已過濾訂單",
"pushFilteredOrdersTooltip": "開啟後,被過濾的訂單會推送到 Telegram",
"autoRedeem": "自動贖回",
"autoRedeemTooltip": "開啟後,系統會自動贖回可贖回的倉位。需要配置 Builder API Key 才能生效",
"builderApiKeyNotConfigured": "Builder API Key 未配置",
@@ -888,6 +896,8 @@
"advancedSettings": "高級設置",
"pushFailedOrders": "推送失敗訂單",
"pushFailedOrdersTooltip": "開啟後,失敗的訂單會推送到 Telegram",
"pushFilteredOrders": "推送已過濾訂單",
"pushFilteredOrdersTooltip": "開啟後,被過濾的訂單會推送到 Telegram",
"autoRedeem": "自動贖回",
"autoRedeemTooltip": "開啟後,系統會自動贖回可贖回的倉位。需要配置 Builder API Key 才能生效",
"builderApiKeyNotConfigured": "Builder API Key 未配置",
@@ -120,7 +120,8 @@ const AddModal: React.FC<AddModalProps> = ({
minPrice: template.minPrice ? parseFloat(template.minPrice) : undefined,
maxPrice: template.maxPrice ? parseFloat(template.maxPrice) : undefined,
maxPositionValue: (template as any).maxPositionValue ? parseFloat((template as any).maxPositionValue) : undefined,
maxPositionCount: (template as any).maxPositionCount
maxPositionCount: (template as any).maxPositionCount,
pushFilteredOrders: template.pushFilteredOrders ?? false
})
setCopyMode(template.copyMode)
setTemplateModalVisible(false)
@@ -255,6 +256,7 @@ const AddModal: React.FC<AddModalProps> = ({
: undefined,
configName: values.configName?.trim(),
pushFailedOrders: values.pushFailedOrders ?? false,
pushFilteredOrders: values.pushFilteredOrders ?? false,
maxMarketEndDate
}
@@ -307,6 +309,7 @@ const AddModal: React.FC<AddModalProps> = ({
websocketMaxRetries: 10,
supportSell: true,
pushFailedOrders: false,
pushFilteredOrders: false,
keywordFilterMode: 'DISABLED'
}}
>
@@ -804,18 +807,35 @@ const AddModal: React.FC<AddModalProps> = ({
>
<Input.Group compact style={{ display: 'flex' }}>
<InputNumber
min={1}
min={0}
max={9999}
step={1}
precision={0}
value={maxMarketEndDateValue}
onChange={(value) => setMaxMarketEndDateValue(value !== null && value !== undefined ? Math.floor(value) : undefined)}
onChange={(value) => {
// 允许设置为 null 或 undefined(清空)
if (value === null || value === undefined) {
setMaxMarketEndDateValue(undefined)
} else {
const num = Math.floor(value)
// 如果值为 0,也设置为 undefined(表示清空)
setMaxMarketEndDateValue(num > 0 ? num : undefined)
}
}}
onBlur={(e) => {
// 失去焦点时,如果值为 0 或空,设置为 undefined
const input = e.target as HTMLInputElement
const value = input.value
if (!value || value === '0') {
setMaxMarketEndDateValue(undefined)
}
}}
style={{ width: '60%' }}
placeholder={t('copyTradingAdd.maxMarketEndDatePlaceholder') || '输入时间值(可选)'}
parser={(value) => {
if (!value) return 0
if (!value) return ''
const num = parseInt(value.replace(/\D/g, ''), 10)
return isNaN(num) ? 0 : num
return isNaN(num) ? '' : num.toString()
}}
formatter={(value) => {
if (!value && value !== 0) return ''
@@ -862,6 +882,16 @@ const AddModal: React.FC<AddModalProps> = ({
<Switch />
</Form.Item>
{/* 推送已过滤订单 */}
<Form.Item
label={t('copyTradingAdd.pushFilteredOrders') || '推送已过滤订单'}
name="pushFilteredOrders"
tooltip={t('copyTradingAdd.pushFilteredOrdersTooltip') || '开启后,被过滤的订单会推送到 Telegram'}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item>
<Space>
<Button
@@ -91,7 +91,8 @@ const EditModal: React.FC<EditModalProps> = ({
maxPositionCount: found.maxPositionCount,
keywordFilterMode: found.keywordFilterMode || 'DISABLED',
configName: found.configName || '',
pushFailedOrders: found.pushFailedOrders ?? false
pushFailedOrders: found.pushFailedOrders ?? false,
pushFilteredOrders: found.pushFilteredOrders ?? false
})
// 设置关键字列表
setKeywords(found.keywords || [])
@@ -214,6 +215,7 @@ const EditModal: React.FC<EditModalProps> = ({
: undefined,
configName: values.configName?.trim() || undefined,
pushFailedOrders: values.pushFailedOrders,
pushFilteredOrders: values.pushFilteredOrders,
maxMarketEndDate
}
@@ -802,6 +804,15 @@ const EditModal: React.FC<EditModalProps> = ({
<Switch />
</Form.Item>
<Form.Item
label={t('copyTradingEdit.pushFilteredOrders') || '推送已过滤订单'}
name="pushFilteredOrders"
tooltip={t('copyTradingEdit.pushFilteredOrdersTooltip') || '开启后,被过滤的订单会推送到 Telegram'}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item>
<Space>
<Button
+13 -2
View File
@@ -55,7 +55,8 @@ const TemplateAdd: React.FC = () => {
minOrderDepth: values.minOrderDepth?.toString(),
maxSpread: values.maxSpread?.toString(),
minPrice: values.minPrice?.toString(),
maxPrice: values.maxPrice?.toString()
maxPrice: values.maxPrice?.toString(),
pushFilteredOrders: values.pushFilteredOrders ?? false
})
if (response.data.code === 0) {
@@ -96,7 +97,8 @@ const TemplateAdd: React.FC = () => {
minOrderSize: 1,
maxDailyOrders: 100,
priceTolerance: 5,
supportSell: true
supportSell: true,
pushFilteredOrders: false
}}
>
<Form.Item
@@ -373,6 +375,15 @@ const TemplateAdd: React.FC = () => {
<Switch />
</Form.Item>
<Form.Item
label={t('templateAdd.pushFilteredOrders') || '推送已过滤订单'}
name="pushFilteredOrders"
tooltip={t('templateAdd.pushFilteredOrdersTooltip') || '开启后,被过滤的订单会推送到 Telegram'}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item shouldUpdate>
{({ getFieldsError }) => {
const errors = getFieldsError()
+13 -2
View File
@@ -41,7 +41,8 @@ const TemplateEdit: React.FC = () => {
minOrderDepth: template.minOrderDepth ? parseFloat(template.minOrderDepth) : undefined,
maxSpread: template.maxSpread ? parseFloat(template.maxSpread) : undefined,
minPrice: template.minPrice ? parseFloat(template.minPrice) : undefined,
maxPrice: template.maxPrice ? parseFloat(template.maxPrice) : undefined
maxPrice: template.maxPrice ? parseFloat(template.maxPrice) : undefined,
pushFilteredOrders: template.pushFilteredOrders ?? false
})
} else {
message.error(response.data.msg || t('templateEdit.fetchFailed') || '获取模板详情失败')
@@ -99,7 +100,8 @@ const TemplateEdit: React.FC = () => {
minOrderDepth: values.minOrderDepth?.toString(),
maxSpread: values.maxSpread?.toString(),
minPrice: values.minPrice?.toString(),
maxPrice: values.maxPrice?.toString()
maxPrice: values.maxPrice?.toString(),
pushFilteredOrders: values.pushFilteredOrders
})
if (response.data.code === 0) {
@@ -409,6 +411,15 @@ const TemplateEdit: React.FC = () => {
<Switch />
</Form.Item>
<Form.Item
label={t('templateEdit.pushFilteredOrders') || '推送已过滤订单'}
name="pushFilteredOrders"
tooltip={t('templateEdit.pushFilteredOrdersTooltip') || '开启后,被过滤的订单会推送到 Telegram'}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Form.Item shouldUpdate>
{({ getFieldsError }) => {
const errors = getFieldsError()
+12 -1
View File
@@ -72,6 +72,7 @@ const TemplateList: React.FC = () => {
maxDailyOrders: template.maxDailyOrders,
priceTolerance: parseFloat(template.priceTolerance),
supportSell: template.supportSell,
pushFilteredOrders: template.pushFilteredOrders ?? false,
minOrderDepth: template.minOrderDepth ? parseFloat(template.minOrderDepth) : undefined,
maxSpread: template.maxSpread ? parseFloat(template.maxSpread) : undefined,
minPrice: template.minPrice ? parseFloat(template.minPrice) : undefined,
@@ -122,7 +123,8 @@ const TemplateList: React.FC = () => {
minOrderDepth: values.minOrderDepth?.toString(),
maxSpread: values.maxSpread?.toString(),
minPrice: values.minPrice?.toString(),
maxPrice: values.maxPrice?.toString()
maxPrice: values.maxPrice?.toString(),
pushFilteredOrders: values.pushFilteredOrders ?? false
})
if (response.data.code === 0) {
@@ -631,6 +633,15 @@ const TemplateList: React.FC = () => {
<Switch />
</Form.Item>
<Form.Item
label={t('templateList.pushFilteredOrders') || '推送已过滤订单'}
name="pushFilteredOrders"
tooltip={t('templateList.pushFilteredOrdersTooltip') || '开启后,被过滤的订单会推送到 Telegram'}
valuePropName="checked"
>
<Switch />
</Form.Item>
<Divider></Divider>
<Form.Item
+1
View File
@@ -116,6 +116,7 @@ export interface CopyTradingTemplate {
maxSpread?: string
minPrice?: string // 最低价格(可选),NULL表示不限制最低价
maxPrice?: string // 最高价格(可选),NULL表示不限制最高价
pushFilteredOrders?: boolean // 推送已过滤订单(默认关闭)
createdAt: number
updatedAt: number
}