refactor: 尾盘策略统一更名为加密价差策略

- 前端:菜单父级「加密价差策略」,子项「策略配置」「实时监控」;i18n 与类型注释全部替换
- 后端:i18n、ErrorCode、DTO/实体/服务注释与日志「尾盘」→「加密价差策略」;TG 通知文案
- 文档:README、中英文用户指南、UI 规格、任务、流程、价差流程、市场数据、动态系数等全部更新
- 不修改 API 路径、路由、表名与类名,保持兼容

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
WrBug
2026-02-25 21:30:50 +08:00
parent 4b277eaeab
commit c5d59dfdf9
39 changed files with 197 additions and 197 deletions
@@ -6,7 +6,7 @@ import jakarta.annotation.PostConstruct
import org.springframework.context.annotation.Configuration
/**
* 尾盘监控服务配置
* 加密价差策略监控服务配置
* 处理 WebSocketSubscriptionService 和 CryptoTailMonitorService 之间的循环依赖
*/
@Configuration
@@ -43,12 +43,12 @@ class CryptoTailStrategyController(
result.fold(
onSuccess = { ResponseEntity.ok(ApiResponse.success(it)) },
onFailure = { e ->
logger.error("查询尾盘策略列表失败: ${e.message}", e)
logger.error("查询加密价差策略列表失败: ${e.message}", e)
ResponseEntity.ok(ApiResponse.error(ErrorCode.SERVER_CRYPTO_TAIL_STRATEGY_LIST_FETCH_FAILED, e.message, messageSource))
}
)
} catch (e: Exception) {
logger.error("查询尾盘策略列表异常: ${e.message}", e)
logger.error("查询加密价差策略列表异常: ${e.message}", e)
ResponseEntity.ok(ApiResponse.error(ErrorCode.SERVER_CRYPTO_TAIL_STRATEGY_LIST_FETCH_FAILED, e.message, messageSource))
}
}
@@ -60,7 +60,7 @@ class CryptoTailStrategyController(
result.fold(
onSuccess = { ResponseEntity.ok(ApiResponse.success(it)) },
onFailure = { e ->
logger.error("创建尾盘策略失败: ${e.message}", e)
logger.error("创建加密价差策略失败: ${e.message}", e)
val code = when (e.message) {
ErrorCode.CRYPTO_TAIL_STRATEGY_WINDOW_INVALID.messageKey -> ErrorCode.CRYPTO_TAIL_STRATEGY_WINDOW_INVALID
ErrorCode.CRYPTO_TAIL_STRATEGY_WINDOW_EXCEED.messageKey -> ErrorCode.CRYPTO_TAIL_STRATEGY_WINDOW_EXCEED
@@ -72,7 +72,7 @@ class CryptoTailStrategyController(
}
)
} catch (e: Exception) {
logger.error("创建尾盘策略异常: ${e.message}", e)
logger.error("创建加密价差策略异常: ${e.message}", e)
ResponseEntity.ok(ApiResponse.error(ErrorCode.SERVER_CRYPTO_TAIL_STRATEGY_CREATE_FAILED, e.message, messageSource))
}
}
@@ -87,7 +87,7 @@ class CryptoTailStrategyController(
result.fold(
onSuccess = { ResponseEntity.ok(ApiResponse.success(it)) },
onFailure = { e ->
logger.error("更新尾盘策略失败: ${e.message}", e)
logger.error("更新加密价差策略失败: ${e.message}", e)
val code = when (e.message) {
ErrorCode.CRYPTO_TAIL_STRATEGY_NOT_FOUND.messageKey -> ErrorCode.CRYPTO_TAIL_STRATEGY_NOT_FOUND
ErrorCode.CRYPTO_TAIL_STRATEGY_WINDOW_INVALID.messageKey -> ErrorCode.CRYPTO_TAIL_STRATEGY_WINDOW_INVALID
@@ -99,7 +99,7 @@ class CryptoTailStrategyController(
}
)
} catch (e: Exception) {
logger.error("更新尾盘策略异常: ${e.message}", e)
logger.error("更新加密价差策略异常: ${e.message}", e)
ResponseEntity.ok(ApiResponse.error(ErrorCode.SERVER_CRYPTO_TAIL_STRATEGY_UPDATE_FAILED, e.message, messageSource))
}
}
@@ -115,12 +115,12 @@ class CryptoTailStrategyController(
result.fold(
onSuccess = { ResponseEntity.ok(ApiResponse.success(Unit)) },
onFailure = { e ->
logger.error("删除尾盘策略失败: ${e.message}", e)
logger.error("删除加密价差策略失败: ${e.message}", e)
ResponseEntity.ok(ApiResponse.error(ErrorCode.SERVER_CRYPTO_TAIL_STRATEGY_DELETE_FAILED, e.message, messageSource))
}
)
} catch (e: Exception) {
logger.error("删除尾盘策略异常: ${e.message}", e)
logger.error("删除加密价差策略异常: ${e.message}", e)
ResponseEntity.ok(ApiResponse.error(ErrorCode.SERVER_CRYPTO_TAIL_STRATEGY_DELETE_FAILED, e.message, messageSource))
}
}
@@ -194,7 +194,7 @@ class CryptoTailStrategyController(
}
/**
* 初始化尾盘策略监控
* 初始化加密价差策略监控
* 返回策略信息、开盘价、tokenIds等初始化数据
*/
@PostMapping("/monitor/init")
@@ -207,12 +207,12 @@ class CryptoTailStrategyController(
result.fold(
onSuccess = { ResponseEntity.ok(ApiResponse.success(it)) },
onFailure = { e ->
logger.error("初始化尾盘监控失败: ${e.message}", e)
logger.error("初始化加密价差策略监控失败: ${e.message}", e)
ResponseEntity.ok(ApiResponse.error(ErrorCode.SERVER_ERROR, e.message, messageSource))
}
)
} catch (e: Exception) {
logger.error("初始化尾盘监控异常: ${e.message}", e)
logger.error("初始化加密价差策略监控异常: ${e.message}", e)
ResponseEntity.ok(ApiResponse.error(ErrorCode.SERVER_ERROR, e.message, messageSource))
}
}
@@ -1,7 +1,7 @@
package com.wrbug.polymarketbot.dto
/**
* 尾盘策略监控初始化请求
* 加密价差策略监控初始化请求
*/
data class CryptoTailMonitorInitRequest(
/** 策略ID */
@@ -9,7 +9,7 @@ data class CryptoTailMonitorInitRequest(
)
/**
* 尾盘策略监控初始化响应
* 加密价差策略监控初始化响应
*/
data class CryptoTailMonitorInitResponse(
/** 策略ID */
@@ -59,7 +59,7 @@ data class CryptoTailMonitorInitResponse(
)
/**
* 尾盘策略监控实时推送数据
* 加密价差策略监控实时推送数据
*/
data class CryptoTailMonitorPushData(
/** 策略ID */
@@ -1,7 +1,7 @@
package com.wrbug.polymarketbot.dto
/**
* 尾盘策略创建请求
* 加密价差策略创建请求
* 金额与价格使用 String,后端转为 BigDecimal
*/
data class CryptoTailStrategyCreateRequest(
@@ -25,7 +25,7 @@ data class CryptoTailStrategyCreateRequest(
)
/**
* 尾盘策略更新请求
* 加密价差策略更新请求
*/
data class CryptoTailStrategyUpdateRequest(
val strategyId: Long = 0L,
@@ -46,7 +46,7 @@ data class CryptoTailStrategyUpdateRequest(
)
/**
* 尾盘策略列表请求
* 加密价差策略列表请求
*/
data class CryptoTailStrategyListRequest(
val accountId: Long? = null,
@@ -54,7 +54,7 @@ data class CryptoTailStrategyListRequest(
)
/**
* 尾盘策略 DTO(列表与详情)
* 加密价差策略 DTO(列表与详情)
*/
data class CryptoTailStrategyDto(
val id: Long = 0L,
@@ -90,14 +90,14 @@ data class CryptoTailStrategyDto(
)
/**
* 尾盘策略列表响应
* 加密价差策略列表响应
*/
data class CryptoTailStrategyListResponse(
val list: List<CryptoTailStrategyDto> = emptyList()
)
/**
* 尾盘策略删除请求
* 加密价差策略删除请求
*/
data class CryptoTailStrategyDeleteRequest(
val strategyId: Long = 0L
@@ -8,7 +8,7 @@ import jakarta.persistence.*
import java.math.BigDecimal
/**
* 加密市场尾盘策略实体
* 加密价差策略实体
* 5/15 分钟 Up or Down 市场,在周期内时间窗口、价格进入区间时市价买入
*/
@Entity
@@ -5,7 +5,7 @@ import java.math.BigDecimal
import com.wrbug.polymarketbot.util.toSafeBigDecimal
/**
* 尾盘策略触发记录
* 加密价差策略触发记录
*/
@Entity
@Table(name = "crypto_tail_strategy_trigger")
@@ -158,8 +158,8 @@ enum class ErrorCode(
ACCOUNT_BALANCE_FETCH_FAILED(4707, "查询账户余额失败", "error.account_balance_fetch_failed"),
ACCOUNT_POSITIONS_FETCH_FAILED(4708, "查询仓位列表失败", "error.account_positions_fetch_failed"),
// 尾盘策略 (4710-4729)
CRYPTO_TAIL_STRATEGY_NOT_FOUND(4710, "尾盘策略不存在", "error.crypto_tail_strategy_not_found"),
// 加密价差策略 (4710-4729)
CRYPTO_TAIL_STRATEGY_NOT_FOUND(4710, "加密价差策略不存在", "error.crypto_tail_strategy_not_found"),
CRYPTO_TAIL_STRATEGY_WINDOW_INVALID(4711, "时间区间开始不能大于结束", "error.crypto_tail_strategy_window_invalid"),
CRYPTO_TAIL_STRATEGY_WINDOW_EXCEED(4712, "时间区间不能超过周期长度", "error.crypto_tail_strategy_window_exceed"),
CRYPTO_TAIL_STRATEGY_INTERVAL_INVALID(4713, "周期仅支持 300 或 900 秒", "error.crypto_tail_strategy_interval_invalid"),
@@ -259,11 +259,11 @@ enum class ErrorCode(
SERVER_BACKTEST_RETRY_FAILED(5612, "重试回测任务失败", "error.server.backtest_retry_failed"),
SERVER_BACKTEST_RERUN_FAILED(5613, "按配置重新测试失败", "error.server.backtest_rerun_failed"),
// 尾盘策略服务 (5620-5629)
SERVER_CRYPTO_TAIL_STRATEGY_CREATE_FAILED(5620, "创建尾盘策略失败", "error.server.crypto_tail_strategy_create_failed"),
SERVER_CRYPTO_TAIL_STRATEGY_UPDATE_FAILED(5621, "更新尾盘策略失败", "error.server.crypto_tail_strategy_update_failed"),
SERVER_CRYPTO_TAIL_STRATEGY_DELETE_FAILED(5622, "删除尾盘策略失败", "error.server.crypto_tail_strategy_delete_failed"),
SERVER_CRYPTO_TAIL_STRATEGY_LIST_FETCH_FAILED(5623, "查询尾盘策略列表失败", "error.server.crypto_tail_strategy_list_fetch_failed"),
// 加密价差策略服务 (5620-5629)
SERVER_CRYPTO_TAIL_STRATEGY_CREATE_FAILED(5620, "创建加密价差策略失败", "error.server.crypto_tail_strategy_create_failed"),
SERVER_CRYPTO_TAIL_STRATEGY_UPDATE_FAILED(5621, "更新加密价差策略失败", "error.server.crypto_tail_strategy_update_failed"),
SERVER_CRYPTO_TAIL_STRATEGY_DELETE_FAILED(5622, "删除加密价差策略失败", "error.server.crypto_tail_strategy_delete_failed"),
SERVER_CRYPTO_TAIL_STRATEGY_LIST_FETCH_FAILED(5623, "查询加密价差策略列表失败", "error.server.crypto_tail_strategy_list_fetch_failed"),
SERVER_CRYPTO_TAIL_STRATEGY_TRIGGERS_FETCH_FAILED(5624, "查询触发记录失败", "error.server.crypto_tail_strategy_triggers_fetch_failed");
companion object {
@@ -3,6 +3,6 @@ package com.wrbug.polymarketbot.event
import org.springframework.context.ApplicationEvent
/**
* 尾盘策略创建/更新/启用状态变更后发布,用于立即触发一轮执行检查。
* 加密价差策略创建/更新/启用状态变更后发布,用于立即触发一轮执行检查。
*/
class CryptoTailStrategyChangedEvent(source: Any) : ApplicationEvent(source)
@@ -23,7 +23,7 @@ interface CryptoTailStrategyTriggerRepository : JpaRepository<CryptoTailStrategy
/** 轮询结算:仅处理下单成功的订单(status=success 且 orderId 非空)、且未结算的触发记录 */
fun findByStatusAndResolvedAndOrderIdIsNotNullOrderByCreatedAtAsc(status: String, resolved: Boolean): List<CryptoTailStrategyTrigger>
/** 根据订单 ID 查询尾盘触发记录 */
/** 根据订单 ID 查询加密价差策略触发记录 */
fun findByOrderId(orderId: String): CryptoTailStrategyTrigger?
/** 轮询发 TGstatus=success、orderId 非空、未发过通知,按创建时间正序 */
@@ -396,7 +396,7 @@ class PositionCheckService(
val positionsByAccount = redeemablePositions.groupBy { it.accountId }
for ((accountId, positions) in positionsByAccount) {
// 查找该账户下所有启用的跟单配置(仅用于赎回成功后更新跟单订单状态;无跟单配置的账户如尾盘策略账户也会执行赎回)
// 查找该账户下所有启用的跟单配置(仅用于赎回成功后更新跟单订单状态;无跟单配置的账户如加密价差策略账户也会执行赎回)
val copyTradings = copyTradingRepository.findByAccountId(accountId)
.filter { it.enabled }
@@ -94,7 +94,7 @@ class BinanceKlineAutoSpreadService(
val baseDown = averageAfterIqr(spreadsDown).setScale(8, RoundingMode.HALF_UP)
cache[cacheKey(marketSlugPrefix, intervalSeconds, periodStartUnix)] = baseUp to baseDown
logger.info(
"尾盘自动价差已计算并缓存(100%基准): market=$marketSlugPrefix symbol=$symbol interval=${intervalSeconds}s periodStartUnix=$periodStartUnix | " +
"加密价差策略自动价差已计算并缓存(100%基准): market=$marketSlugPrefix symbol=$symbol interval=${intervalSeconds}s periodStartUnix=$periodStartUnix | " +
"Up方向: 样本数=${spreadsUp.size}, baseSpreadUp=${baseUp.toPlainString()} | " +
"Down方向: 样本数=${spreadsDown.size}, baseSpreadDown=${baseDown.toPlainString()}"
)
@@ -19,7 +19,7 @@ import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicReference
/**
* 币安 K 线 WebSocket:按需订阅尾盘策略使用的币种 5m/15m,维护当前周期 (open, close),供价差校验使用。
* 币安 K 线 WebSocket:按需订阅加密价差策略使用的币种 5m/15m,维护当前周期 (open, close),供价差校验使用。
* 仅当存在启用策略且策略使用到某市场时才订阅对应币种,无策略时不建立连接。
*/
@Service
@@ -46,7 +46,7 @@ class BinanceKlineService {
/** 已连接的 WebSocket: wsKey (symbol-interval) -> WebSocket */
private val connectedWebSockets = ConcurrentHashMap<String, WebSocket>()
/** 当前需要订阅的完整市场集合(如 btc-updown-5m、btc-updown-15m),由尾盘策略刷新时更新 */
/** 当前需要订阅的完整市场集合(如 btc-updown-5m、btc-updown-15m),由加密价差策略刷新时更新 */
private val requiredMarketPrefixes = AtomicReference<Set<String>>(emptySet())
private val subscriptionLock = Any()
private var reconnectJob: Job? = null
@@ -40,14 +40,14 @@ class WebSocketSubscriptionService(
// 存储 order 频道的订阅回调:sessionId -> callback(用于取消订阅)
private val orderChannelCallbacks = ConcurrentHashMap<String, (OrderPushMessage) -> Unit>()
// 存储尾盘监控频道的订阅回调:sessionId -> (strategyId -> callback)
// 存储加密价差策略监控频道的订阅回调:sessionId -> (strategyId -> callback)
private val monitorChannelCallbacks = ConcurrentHashMap<String, MutableMap<Long, (CryptoTailMonitorPushData) -> Unit>>()
// 尾盘监控服务(延迟注入,避免循环依赖)
// 加密价差策略监控服务(延迟注入,避免循环依赖)
private var cryptoTailMonitorService: CryptoTailMonitorService? = null
/**
* 设置尾盘监控服务(由 Spring 在初始化后调用)
* 设置加密价差策略监控服务(由 Spring 在初始化后调用)
*/
fun setCryptoTailMonitorService(service: CryptoTailMonitorService) {
cryptoTailMonitorService = service
@@ -75,7 +75,7 @@ class WebSocketSubscriptionService(
// 清理 order 频道的回调
orderChannelCallbacks.remove(sessionId)
// 清理尾盘监控频道的回调
// 清理加密价差策略监控频道的回调
val monitorCallbacks = monitorChannelCallbacks.remove(sessionId)
monitorCallbacks?.keys?.forEach { strategyId ->
cryptoTailMonitorService?.unsubscribe(sessionId, strategyId)
@@ -127,7 +127,7 @@ class WebSocketSubscriptionService(
orderPushService.subscribeAllEnabled(callback)
}
channel.startsWith("crypto_tail_monitor_") -> {
// 尾盘策略监控频道
// 加密价差策略监控频道
val strategyId = channel.removePrefix("crypto_tail_monitor_").toLongOrNull()
if (strategyId != null && cryptoTailMonitorService != null) {
val callback: (CryptoTailMonitorPushData) -> Unit = { message ->
@@ -136,7 +136,7 @@ class WebSocketSubscriptionService(
monitorChannelCallbacks.getOrPut(sessionId) { mutableMapOf() }[strategyId] = callback
cryptoTailMonitorService!!.subscribe(sessionId, strategyId, callback)
} else {
logger.warn("无效的尾盘监控频道或服务未初始化: $channel")
logger.warn("无效的加密价差策略监控频道或服务未初始化: $channel")
sendSubscribeAck(sessionId, channel, false, "无效的策略ID")
}
}
@@ -167,7 +167,7 @@ class WebSocketSubscriptionService(
}
}
channel.startsWith("crypto_tail_monitor_") -> {
// 取消尾盘监控订阅
// 取消加密价差策略监控订阅
val strategyId = channel.removePrefix("crypto_tail_monitor_").toLongOrNull()
if (strategyId != null) {
monitorChannelCallbacks[sessionId]?.remove(strategyId)
@@ -178,21 +178,21 @@ class WebSocketSubscriptionService(
}
/**
* 注册尾盘监控回调(由 CryptoTailMonitorService 调用)
* 注册加密价差策略监控回调(由 CryptoTailMonitorService 调用)
*/
fun registerMonitorCallback(sessionId: String, strategyId: Long, callback: (CryptoTailMonitorPushData) -> Unit) {
monitorChannelCallbacks.getOrPut(sessionId) { mutableMapOf() }[strategyId] = callback
}
/**
* 注销尾盘监控回调(由 CryptoTailMonitorService 调用)
* 注销加密价差策略监控回调(由 CryptoTailMonitorService 调用)
*/
fun unregisterMonitorCallback(sessionId: String, strategyId: Long) {
monitorChannelCallbacks[sessionId]?.remove(strategyId)
}
/**
* 推送尾盘监控数据(由 CryptoTailMonitorService 调用)
* 推送加密价差策略监控数据(由 CryptoTailMonitorService 调用)
*/
fun pushMonitorData(strategyId: Long, data: CryptoTailMonitorPushData) {
val channel = "crypto_tail_monitor_$strategyId"
@@ -41,7 +41,7 @@ import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicReference
/**
* 尾盘策略监控服务
* 加密价差策略监控服务
* 负责实时推送监控数据到前端
*/
@Service
@@ -526,7 +526,7 @@ class CryptoTailMonitorService(
val msg = """{"type":"MARKET","assets_ids":${tokenIds.toJson()}}"""
try {
webSocket.send(msg)
logger.info("尾盘监控 WebSocket(当前周期)已连接并订阅: ${tokenIds.size} 个 token")
logger.info("加密价差策略监控 WebSocket(当前周期)已连接并订阅: ${tokenIds.size} 个 token")
} catch (e: Exception) {
logger.warn("发送当前周期订阅失败: ${e.message}")
}
@@ -563,9 +563,9 @@ class CryptoTailMonitorService(
try {
webSocket.send(msg)
if (tokenIds.isEmpty()) {
logger.info("尾盘监控 WebSocket(下一周期)已连接,暂无 token 订阅,等待周期切换后更新")
logger.info("加密价差策略监控 WebSocket(下一周期)已连接,暂无 token 订阅,等待周期切换后更新")
} else {
logger.info("尾盘监控 WebSocket(下一周期)已连接并订阅: ${tokenIds.size} 个 token")
logger.info("加密价差策略监控 WebSocket(下一周期)已连接并订阅: ${tokenIds.size} 个 token")
}
} catch (e: Exception) {
logger.warn("发送下一周期订阅失败: ${e.message}")
@@ -593,7 +593,7 @@ class CryptoTailMonitorService(
private fun closeCurrentPeriodWebSocket() {
currentPeriodWebSocket?.close(1000, "period_ended")
currentPeriodWebSocket = null
logger.info("尾盘监控 WebSocket(当前周期)已关闭")
logger.info("加密价差策略监控 WebSocket(当前周期)已关闭")
}
private fun closeAllWebSockets() {
@@ -604,7 +604,7 @@ class CryptoTailMonitorService(
currentPeriodWebSocket = null
nextPeriodWebSocket?.close(1000, "no_subscribers")
nextPeriodWebSocket = null
logger.info("尾盘监控 WebSocket 已全部关闭(无订阅者)")
logger.info("加密价差策略监控 WebSocket 已全部关闭(无订阅者)")
}
private fun handleMessage(webSocket: WebSocket, text: String) {
@@ -802,7 +802,7 @@ class CryptoTailMonitorService(
delay(reconnectDelayMs)
reconnectJob = null
if (strategySubscribers.isNotEmpty()) {
logger.info("尾盘监控 WebSocket 尝试重连")
logger.info("加密价差策略监控 WebSocket 尝试重连")
refreshSubscription()
}
}
@@ -22,7 +22,7 @@ import org.springframework.transaction.annotation.Transactional
import jakarta.annotation.PreDestroy
/**
* 尾盘策略订单 TG 通知轮询服务(与跟单一致)
* 加密价差策略订单 TG 通知轮询服务(与跟单一致)
* 定时查询「下单成功且未发 TG」的触发记录,通过 CLOB getOrder 获取订单详情后发送 TG 并标记已发。
*/
@Service
@@ -57,14 +57,14 @@ class CryptoTailOrderNotificationPollingService(
@Scheduled(fixedDelay = 5000)
fun scheduledSendPendingNotifications() {
if (notificationJob != null && notificationJob!!.isActive) {
logger.debug("上一轮尾盘 TG 通知任务仍在执行,跳过本次")
logger.debug("上一轮加密价差策略 TG 通知任务仍在执行,跳过本次")
return
}
notificationJob = scope.launch {
try {
getSelf().sendPendingNotifications()
} catch (e: Exception) {
logger.error("尾盘 TG 通知轮询异常: ${e.message}", e)
logger.error("加密价差策略 TG 通知轮询异常: ${e.message}", e)
} finally {
notificationJob = null
}
@@ -88,7 +88,7 @@ class CryptoTailOrderNotificationPollingService(
triggerRepository.save(trigger)
}
} catch (e: Exception) {
logger.warn("尾盘 TG 通知单条失败: triggerId=${trigger.id}, orderId=${trigger.orderId}, ${e.message}", e)
logger.warn("加密价差策略 TG 通知单条失败: triggerId=${trigger.id}, orderId=${trigger.orderId}, ${e.message}", e)
}
}
}
@@ -142,7 +142,7 @@ class CryptoTailOrderNotificationPollingService(
walletAddress = account.walletAddress,
orderTime = orderTimeMs
)
logger.info("尾盘订单 TG 通知已发送: orderId=$orderId, strategyId=${strategy.id}, triggerId=${trigger.id}")
logger.info("加密价差策略订单 TG 通知已发送: orderId=$orderId, strategyId=${strategy.id}, triggerId=${trigger.id}")
return true
}
@@ -35,7 +35,7 @@ import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicReference
/**
* 尾盘策略订单簿 WebSocket 监听:订阅 CLOB Market 频道,收到订单簿/价格变更时若满足条件立即触发下单。
* 加密价差策略订单簿 WebSocket 监听:订阅 CLOB Market 频道,收到订单簿/价格变更时若满足条件立即触发下单。
*/
@Service
class CryptoTailOrderbookWsService(
@@ -103,7 +103,7 @@ class CryptoTailOrderbookWsService(
try {
webSocket?.close(1000, "shutdown")
} catch (e: Exception) {
logger.debug("关闭尾盘策略 WebSocket 时异常: ${e.message}")
logger.debug("关闭加密价差策略 WebSocket 时异常: ${e.message}")
}
webSocket = null
scopeJob.cancel()
@@ -116,7 +116,7 @@ class CryptoTailOrderbookWsService(
val request = Request.Builder().url(wsUrl).build()
webSocket = client.newWebSocket(request, object : WebSocketListener() {
override fun onOpen(webSocket: WebSocket, response: okhttp3.Response) {
logger.info("尾盘策略订单簿 WebSocket 已连接")
logger.info("加密价差策略订单簿 WebSocket 已连接")
refreshAndSubscribe(fromConnect = true)
}
@@ -130,13 +130,13 @@ class CryptoTailOrderbookWsService(
}
override fun onFailure(webSocket: WebSocket, t: Throwable, response: okhttp3.Response?) {
logger.warn("尾盘策略订单簿 WebSocket 异常: ${t.message}")
logger.warn("加密价差策略订单簿 WebSocket 异常: ${t.message}")
this@CryptoTailOrderbookWsService.webSocket = null
scheduleReconnect()
}
})
} catch (e: Exception) {
logger.error("尾盘策略订单簿 WebSocket 连接失败: ${e.message}", e)
logger.error("加密价差策略订单簿 WebSocket 连接失败: ${e.message}", e)
scheduleReconnect()
}
}
@@ -150,7 +150,7 @@ class CryptoTailOrderbookWsService(
delay(reconnectDelayMs)
reconnectJob = null
if (strategyRepository.findAllByEnabledTrue().isEmpty()) return@launch
logger.info("尾盘策略订单簿 WebSocket 尝试重连")
logger.info("加密价差策略订单簿 WebSocket 尝试重连")
connect()
}
}
@@ -244,7 +244,7 @@ class CryptoTailOrderbookWsService(
synchronized(refreshLock) {
// 如果正在刷新,直接返回,避免重复调用
if (isRefreshing.get()) {
logger.debug("尾盘策略订阅刷新已在进行中,跳过本次调用")
logger.debug("加密价差策略订阅刷新已在进行中,跳过本次调用")
return
}
isRefreshing.set(true)
@@ -281,7 +281,7 @@ class CryptoTailOrderbookWsService(
val msg = """{"type":"MARKET","assets_ids":${tokenIds.toJson()}}"""
try {
webSocket?.send(msg)
logger.info("尾盘策略订单簿订阅: ${tokenIds.size} 个 token, 市场: $marketSlugs")
logger.info("加密价差策略订单簿订阅: ${tokenIds.size} 个 token, 市场: $marketSlugs")
} catch (e: Exception) {
logger.warn("发送订阅失败: ${e.message}")
return
@@ -303,9 +303,9 @@ class CryptoTailOrderbookWsService(
try {
ws.close(1000, "subscription_change")
} catch (e: Exception) {
logger.debug("关闭尾盘策略 WebSocket 时异常: ${e.message}")
logger.debug("关闭加密价差策略 WebSocket 时异常: ${e.message}")
}
logger.info("尾盘策略订单簿 WebSocket 已关闭(订阅更新,将重连)")
logger.info("加密价差策略订单簿 WebSocket 已关闭(订阅更新,将重连)")
}
}
@@ -357,9 +357,9 @@ class CryptoTailOrderbookWsService(
try {
ws.close(1000, "no_enabled_strategies")
} catch (e: Exception) {
logger.debug("关闭尾盘策略 WebSocket 时异常: ${e.message}")
logger.debug("关闭加密价差策略 WebSocket 时异常: ${e.message}")
}
logger.info("尾盘策略订单簿 WebSocket 已关闭(无启用策略)")
logger.info("加密价差策略订单簿 WebSocket 已关闭(无启用策略)")
}
}
@@ -377,7 +377,7 @@ class CryptoTailOrderbookWsService(
periodEndCountdownJob = null
refreshAndSubscribe()
}
logger.debug("尾盘策略订单簿订阅倒计时: ${delayMs / 1000}s 后刷新")
logger.debug("加密价差策略订单簿订阅倒计时: ${delayMs / 1000}s 后刷新")
}
private fun buildSubscriptionMap(): Pair<List<String>, Map<String, List<WsBookEntry>>> {
@@ -391,23 +391,23 @@ class CryptoTailOrderbookWsService(
val periodStartUnix = (nowSeconds / interval) * interval
val windowEnd = periodStartUnix + strategy.windowEndSeconds
if (nowSeconds >= windowEnd) {
logger.debug("尾盘策略跳过(已过时间窗口): strategyId=${strategy.id}, slug=${strategy.marketSlugPrefix}, windowEnd=$windowEnd")
logger.debug("加密价差策略跳过(已过时间窗口): strategyId=${strategy.id}, slug=${strategy.marketSlugPrefix}, windowEnd=$windowEnd")
continue
}
val slug = "${strategy.marketSlugPrefix}-$periodStartUnix"
val event = runBlocking { fetchEventBySlugWithRetry(slug).getOrNull() }
if (event == null) {
logger.warn("尾盘策略跳过(拉取事件失败): strategyId=${strategy.id}, slug=$slug,请确认 Gamma 是否存在该 slug 或稍后重试")
logger.warn("加密价差策略跳过(拉取事件失败): strategyId=${strategy.id}, slug=$slug,请确认 Gamma 是否存在该 slug 或稍后重试")
continue
}
val market = event.markets?.firstOrNull()
if (market == null) {
logger.warn("尾盘策略跳过(事件无市场): strategyId=${strategy.id}, slug=$slug")
logger.warn("加密价差策略跳过(事件无市场): strategyId=${strategy.id}, slug=$slug")
continue
}
val tokenIds = parseClobTokenIds(market.clobTokenIds)
if (tokenIds.size < 2) {
logger.warn("尾盘策略跳过(token 数量不足): strategyId=${strategy.id}, slug=$slug, tokenCount=${tokenIds.size}")
logger.warn("加密价差策略跳过(token 数量不足): strategyId=${strategy.id}, slug=$slug, tokenCount=${tokenIds.size}")
continue
}
tokenIdSet.addAll(tokenIds)
@@ -27,7 +27,7 @@ import java.math.BigDecimal
import java.math.RoundingMode
/**
* 尾盘策略结算轮询服务
* 加密价差策略结算轮询服务
* 定时扫描「状态成功但未结算」的触发记录,通过 Gamma 获取 conditionId、链上查询结算结果,计算收益并回写。
* 实际成交价与成交量使用 Data API 的 activity 接口获取(getUserActivity),比 CLOB getOrder 更准确;失败时回退为触发时的 amountUsdc + 固定价 0.99。
*/
@@ -60,14 +60,14 @@ class CryptoTailSettlementService(
fun scheduledPollAndSettle() {
val previousJob = settlementJob
if (previousJob != null && previousJob.isActive) {
logger.debug("上一轮尾盘结算任务仍在执行,跳过本次调度")
logger.debug("上一轮加密价差策略结算任务仍在执行,跳过本次调度")
return
}
settlementJob = settlementScope.launch {
try {
doPollAndSettle()
} catch (e: Exception) {
logger.error("尾盘策略结算定时任务异常: ${e.message}", e)
logger.error("加密价差策略结算定时任务异常: ${e.message}", e)
} finally {
settlementJob = null
}
@@ -91,11 +91,11 @@ class CryptoTailSettlementService(
try {
if (settleOne(trigger)) settledCount++
} catch (e: Exception) {
logger.warn("尾盘结算单条失败: triggerId=${trigger.id}, ${e.message}", e)
logger.warn("加密价差策略结算单条失败: triggerId=${trigger.id}, ${e.message}", e)
}
}
if (settledCount > 0) {
logger.info("尾盘策略结算轮询完成: 处理=${pending.size}, 新结算=$settledCount")
logger.info("加密价差策略结算轮询完成: 处理=${pending.size}, 新结算=$settledCount")
}
return settledCount
}
@@ -154,7 +154,7 @@ class CryptoTailSettlementService(
settledAt = now
)
triggerRepository.save(updated)
logger.debug("尾盘结算已更新: triggerId=${trigger.id}, winnerOutcomeIndex=$winnerIndex, won=$won, pnl=$pnl")
logger.debug("加密价差策略结算已更新: triggerId=${trigger.id}, winnerOutcomeIndex=$winnerIndex, won=$won, pnl=$pnl")
return true
}
@@ -201,7 +201,7 @@ class CryptoTailSettlementService(
conditionId: String
): ActivityFill? {
val account = accountRepository.findById(strategy.accountId).orElse(null) ?: run {
logger.warn("尾盘结算未拉取 activity: 账户不存在, triggerId=${trigger.id}, accountId=${strategy.accountId}")
logger.warn("加密价差策略结算未拉取 activity: 账户不存在, triggerId=${trigger.id}, accountId=${strategy.accountId}")
return null
}
val user = account.proxyAddress
@@ -220,7 +220,7 @@ class CryptoTailSettlementService(
sortDirection = "DESC"
)
if (!response.isSuccessful || response.body() == null) {
logger.warn("尾盘结算拉取 activity 失败: triggerId=${trigger.id}, code=${response.code()}")
logger.warn("加密价差策略结算拉取 activity 失败: triggerId=${trigger.id}, code=${response.code()}")
return null
}
val activities = response.body()!!
@@ -234,7 +234,7 @@ class CryptoTailSettlementService(
a.price != null && a.price > 0 &&
a.size != null && a.size > 0
} ?: run {
logger.debug("尾盘结算 activity 无匹配成交: triggerId=${trigger.id}, conditionId=$conditionId, outcomeIndex=${trigger.outcomeIndex}, 条数=${activities.size}")
logger.debug("加密价差策略结算 activity 无匹配成交: triggerId=${trigger.id}, conditionId=$conditionId, outcomeIndex=${trigger.outcomeIndex}, 条数=${activities.size}")
return null
}
val price = match.price!!.toSafeBigDecimal()
@@ -243,11 +243,11 @@ class CryptoTailSettlementService(
if (price.gt(BigDecimal.ZERO) && size.gt(BigDecimal.ZERO)) {
ActivityFill(price = price, size = size, usdcSize = usdcSize)
} else {
logger.debug("尾盘结算 activity 成交数据无效: triggerId=${trigger.id}, price=$price, size=$size")
logger.debug("加密价差策略结算 activity 成交数据无效: triggerId=${trigger.id}, price=$price, size=$size")
null
}
} catch (e: Exception) {
logger.warn("尾盘结算拉取 activity 异常,触发价/投入金额不会更新: triggerId=${trigger.id}, error=${e.message}")
logger.warn("加密价差策略结算拉取 activity 异常,触发价/投入金额不会更新: triggerId=${trigger.id}, error=${e.message}")
null
}
}
@@ -34,7 +34,7 @@ import java.math.RoundingMode
import java.util.concurrent.ConcurrentHashMap
import java.util.regex.Pattern
/** 尾盘策略固定下单价格(最高价 0.99),不再在触发时拉取最优价 */
/** 加密价差策略固定下单价格(最高价 0.99),不再在触发时拉取最优价 */
private const val TRIGGER_FIXED_PRICE = "0.99"
/** 最大价差模式(MAX)时,买入价格调整系数(加在触发价格上) */
@@ -62,7 +62,7 @@ private data class PeriodContext(
)
/**
* 尾盘策略执行服务:按周期与时间窗口检查价格并下单,每周期最多触发一次。
* 加密价差策略执行服务:按周期与时间窗口检查价格并下单,每周期最多触发一次。
* 周期开始预置账户、解密、费率、签名类型、CLOB 客户端;触发时按 outcomeIndex 计算 size 并签名提交。
*/
@Service
@@ -131,7 +131,7 @@ class CryptoTailStrategyExecutionService(
val decryptedKey = try {
cryptoUtils.decrypt(account.privateKey) ?: return null
} catch (e: Exception) {
logger.warn("尾盘策略周期上下文解密私钥失败: accountId=${account.id}", e)
logger.warn("加密价差策略周期上下文解密私钥失败: accountId=${account.id}", e)
return null
}
val apiSecret = try {
@@ -230,11 +230,11 @@ class CryptoTailStrategyExecutionService(
)
val openPrice = oc?.first?.toPlainString() ?: "-"
val closePrice = oc?.second?.toPlainString() ?: "-"
val strategyName = strategy.name?.takeIf { it.isNotBlank() } ?: "尾盘策略-${strategy.marketSlugPrefix}"
val strategyName = strategy.name?.takeIf { it.isNotBlank() } ?: "加密价差策略-${strategy.marketSlugPrefix}"
val direction = if (outcomeIndex == 0) "Up" else "Down"
val modeStr = if (strategy.spreadDirection == SpreadDirection.MAX) "最大价差" else "最小价差"
logger.info(
"尾盘策略首次满足条件: strategyName=$strategyName, strategyId=${strategy.id}, " +
"加密价差策略首次满足条件: strategyName=$strategyName, strategyId=${strategy.id}, " +
"openPrice=$openPrice, closePrice=$closePrice, marketPrice=${bestBid.toPlainString()}, " +
"direction=$direction, outcomeIndex=$outcomeIndex, spreadMode=$modeStr"
)
@@ -446,7 +446,7 @@ class CryptoTailStrategyExecutionService(
"success",
null
)
logger.info("尾盘策略下单成功: strategyId=${strategy.id}, periodStartUnix=$periodStartUnix, outcomeIndex=$outcomeIndex, orderId=${body.orderId}")
logger.info("加密价差策略下单成功: strategyId=${strategy.id}, periodStartUnix=$periodStartUnix, outcomeIndex=$outcomeIndex, orderId=${body.orderId}")
return
}
failReason = body.errorMsg ?: "unknown"
@@ -456,7 +456,7 @@ class CryptoTailStrategyExecutionService(
}
} catch (e: Exception) {
failReason = e.message ?: e.toString()
logger.error("尾盘策略下单异常: strategyId=${strategy.id}, periodStartUnix=$periodStartUnix", e)
logger.error("加密价差策略下单异常: strategyId=${strategy.id}, periodStartUnix=$periodStartUnix", e)
}
saveTriggerRecord(
strategy,
@@ -469,7 +469,7 @@ class CryptoTailStrategyExecutionService(
"fail",
failReason
)
logger.error("尾盘策略下单失败: strategyId=${strategy.id}, periodStartUnix=$periodStartUnix, reason=$failReason")
logger.error("加密价差策略下单失败: strategyId=${strategy.id}, periodStartUnix=$periodStartUnix, reason=$failReason")
}
/** 无预置上下文时的完整流程:固定价格 0.99,账户/解密/费率/签名在触发时执行 */
@@ -672,6 +672,6 @@ class CryptoTailStrategyExecutionService(
periodContextCache.clear()
// 清理所有锁,避免内存泄漏
triggerMutexMap.clear()
logger.debug("尾盘策略执行服务已清理缓存和锁")
logger.debug("加密价差策略执行服务已清理缓存和锁")
}
}
@@ -104,7 +104,7 @@ class CryptoTailStrategyService(
} catch (e: IllegalArgumentException) {
Result.failure(e)
} catch (e: Exception) {
logger.error("创建尾盘策略失败: ${e.message}", e)
logger.error("创建加密价差策略失败: ${e.message}", e)
Result.failure(e)
}
}
@@ -179,7 +179,7 @@ class CryptoTailStrategyService(
} catch (e: IllegalArgumentException) {
Result.failure(e)
} catch (e: Exception) {
logger.error("更新尾盘策略失败: ${e.message}", e)
logger.error("更新加密价差策略失败: ${e.message}", e)
Result.failure(e)
}
}
@@ -194,7 +194,7 @@ class CryptoTailStrategyService(
eventPublisher.publishEvent(CryptoTailStrategyChangedEvent(this))
Result.success(Unit)
} catch (e: Exception) {
logger.error("删除尾盘策略失败: ${e.message}", e)
logger.error("删除加密价差策略失败: ${e.message}", e)
Result.failure(e)
}
}
@@ -215,7 +215,7 @@ class CryptoTailStrategyService(
val dtos = list.map { entityToDto(it, lastTriggerMap[it.id]) }
Result.success(CryptoTailStrategyListResponse(list = dtos))
} catch (e: Exception) {
logger.error("查询尾盘策略列表失败: ${e.message}", e)
logger.error("查询加密价差策略列表失败: ${e.message}", e)
Result.failure(e)
}
}
@@ -263,7 +263,7 @@ class CryptoTailStrategyService(
private fun generateStrategyName(marketSlugPrefix: String): String {
val suffix = Instant.now().atZone(ZoneId.systemDefault())
.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))
return "尾盘策略-${marketSlugPrefix}-$suffix"
return "加密价差策略-${marketSlugPrefix}-$suffix"
}
private fun entityToDto(e: CryptoTailStrategy, lastTriggerAt: Long?): CryptoTailStrategyDto {
@@ -250,7 +250,7 @@ class ApiHealthCheckService(
name = "币安 WebSocket",
url = binanceWsUrl,
status = "success",
message = "尾盘策略,未订阅"
message = "加密价差策略,未订阅"
)
} else if (connected > 0) {
val which = statuses.filter { it.value }.keys.joinToString("")
@@ -304,7 +304,7 @@ class TelegramNotificationService(
}
/**
* 发送尾盘策略下单成功通知(与跟单一致:在收到 WS 订单推送时匹配尾盘订单后调用)
* 发送加密价差策略下单成功通知(与跟单一致:在收到 WS 订单推送时匹配价差策略订单后调用)
*/
suspend fun sendCryptoTailOrderSuccessNotification(
orderId: String?,
@@ -324,7 +324,7 @@ class TelegramNotificationService(
if (orderId != null) {
val lastSentTime = sentOrderIds[orderId]
if (lastSentTime != null && System.currentTimeMillis() - lastSentTime < 5 * 60 * 1000) {
logger.info("尾盘订单通知已发送过(5分钟内),跳过: orderId=$orderId")
logger.info("加密价差策略订单通知已发送过(5分钟内),跳过: orderId=$orderId")
return
}
sentOrderIds[orderId] = System.currentTimeMillis()
@@ -894,7 +894,7 @@ class TelegramNotificationService(
}
/**
* 构建尾盘策略下单成功消息(与订单成功格式一致,增加「尾盘策略」标题与策略名)
* 构建加密价差策略下单成功消息(与订单成功格式一致,增加「加密价差策略」标题与策略名)
*/
private fun buildCryptoTailOrderSuccessMessage(
orderId: String?,
@@ -912,7 +912,7 @@ class TelegramNotificationService(
locale: java.util.Locale,
orderTime: Long?
): String {
val tailOrderSuccess = messageSource.getMessage("notification.tail.order.success", null, "尾盘策略下单成功", locale)
val tailOrderSuccess = messageSource.getMessage("notification.tail.order.success", null, "加密价差策略下单成功", locale)
val strategyLabel = messageSource.getMessage("notification.tail.strategy", null, "策略", locale)
val orderInfo = messageSource.getMessage("notification.order.info", null, "订单信息", locale)
val orderIdLabel = messageSource.getMessage("notification.order.id", null, "订单ID", locale)
@@ -16,7 +16,7 @@ notification.order.time=Time
notification.order.error_info=Error Information
notification.order.unknown_account=Unknown Account
notification.order.calculate_failed=Calculation Failed
notification.tail.order.success=Tail Session Order Success
notification.tail.order.success=Crypto spread strategy order success
notification.tail.strategy=Strategy
notification.redeem.success=Position Redeemed Successfully
notification.redeem.info=Redeem Information
@@ -277,16 +277,16 @@ error.server.backtest_stop_failed=Failed to stop backtest task
error.server.backtest_retry_failed=Failed to retry backtest task
error.server.backtest_rerun_failed=Failed to re-run backtest with same config
# Crypto tail strategy
error.crypto_tail_strategy_not_found=Crypto tail strategy not found
# Crypto spread strategy
error.crypto_tail_strategy_not_found=Crypto spread strategy not found
error.crypto_tail_strategy_window_invalid=Window start must not be greater than window end
error.crypto_tail_strategy_window_exceed=Time window must not exceed period length
error.crypto_tail_strategy_interval_invalid=Interval must be 300 or 900 seconds
error.crypto_tail_strategy_amount_mode_invalid=Amount mode must be RATIO or FIXED
error.server.crypto_tail_strategy_create_failed=Failed to create crypto tail strategy
error.server.crypto_tail_strategy_update_failed=Failed to update crypto tail strategy
error.server.crypto_tail_strategy_delete_failed=Failed to delete crypto tail strategy
error.server.crypto_tail_strategy_list_fetch_failed=Failed to fetch crypto tail strategy list
error.server.crypto_tail_strategy_create_failed=Failed to create crypto spread strategy
error.server.crypto_tail_strategy_update_failed=Failed to update crypto spread strategy
error.server.crypto_tail_strategy_delete_failed=Failed to delete crypto spread strategy
error.server.crypto_tail_strategy_list_fetch_failed=Failed to fetch crypto spread strategy list
error.server.crypto_tail_strategy_triggers_fetch_failed=Failed to fetch trigger records
# Backtest Management
backtest.title=Backtest Management
@@ -16,7 +16,7 @@ notification.order.time=时间
notification.order.error_info=错误信息
notification.order.unknown_account=未知账户
notification.order.calculate_failed=计算失败
notification.tail.order.success=尾盘策略下单成功
notification.tail.order.success=加密价差策略下单成功
notification.tail.strategy=策略
notification.redeem.success=仓位赎回成功
notification.redeem.info=赎回信息
@@ -277,16 +277,16 @@ error.server.backtest_stop_failed=停止回测任务失败
error.server.backtest_retry_failed=重试回测任务失败
error.server.backtest_rerun_failed=按配置重新测试失败
# 尾盘策略
error.crypto_tail_strategy_not_found=尾盘策略不存在
# 加密价差策略
error.crypto_tail_strategy_not_found=加密价差策略不存在
error.crypto_tail_strategy_window_invalid=时间区间开始不能大于结束
error.crypto_tail_strategy_window_exceed=时间区间不能超过周期长度
error.crypto_tail_strategy_interval_invalid=周期仅支持 300 或 900 秒
error.crypto_tail_strategy_amount_mode_invalid=投入方式仅支持 RATIO 或 FIXED
error.server.crypto_tail_strategy_create_failed=创建尾盘策略失败
error.server.crypto_tail_strategy_update_failed=更新尾盘策略失败
error.server.crypto_tail_strategy_delete_failed=删除尾盘策略失败
error.server.crypto_tail_strategy_list_fetch_failed=查询尾盘策略列表失败
error.server.crypto_tail_strategy_create_failed=创建加密价差策略失败
error.server.crypto_tail_strategy_update_failed=更新加密价差策略失败
error.server.crypto_tail_strategy_delete_failed=删除加密价差策略失败
error.server.crypto_tail_strategy_list_fetch_failed=查询加密价差策略列表失败
error.server.crypto_tail_strategy_triggers_fetch_failed=查询触发记录失败
# 回测管理
backtest.title=回测管理
@@ -16,7 +16,7 @@ notification.order.time=時間
notification.order.error_info=錯誤信息
notification.order.unknown_account=未知賬戶
notification.order.calculate_failed=計算失敗
notification.tail.order.success=尾盤策略下單成功
notification.tail.order.success=加密價差策略下單成功
notification.tail.strategy=策略
notification.redeem.success=倉位贖回成功
notification.redeem.info=贖回信息
@@ -277,16 +277,16 @@ error.server.backtest_stop_failed=停止回測任務失敗
error.server.backtest_retry_failed=重試回測任務失敗
error.server.backtest_rerun_failed=依配置重新測試失敗
# 尾盤策略
error.crypto_tail_strategy_not_found=尾盤策略不存在
# 加密價差策略
error.crypto_tail_strategy_not_found=加密價差策略不存在
error.crypto_tail_strategy_window_invalid=時間區間開始不能大於結束
error.crypto_tail_strategy_window_exceed=時間區間不能超過週期長度
error.crypto_tail_strategy_interval_invalid=週期僅支援 300 或 900 秒
error.crypto_tail_strategy_amount_mode_invalid=投入方式僅支援 RATIO 或 FIXED
error.server.crypto_tail_strategy_create_failed=創建尾盤策略失敗
error.server.crypto_tail_strategy_update_failed=更新尾盤策略失敗
error.server.crypto_tail_strategy_delete_failed=刪除尾盤策略失敗
error.server.crypto_tail_strategy_list_fetch_failed=查詢尾盤策略列表失敗
error.server.crypto_tail_strategy_create_failed=創建加密價差策略失敗
error.server.crypto_tail_strategy_update_failed=更新加密價差策略失敗
error.server.crypto_tail_strategy_delete_failed=刪除加密價差策略失敗
error.server.crypto_tail_strategy_list_fetch_failed=查詢加密價差策略列表失敗
error.server.crypto_tail_strategy_triggers_fetch_failed=查詢觸發記錄失敗
# 回測管理
backtest.title=回測管理
+2 -2
View File
@@ -1,6 +1,6 @@
# 尾盘策略文档 (Crypto Tail Strategy)
# 加密价差策略文档 (Crypto Spread Strategy)
本目录集中存放与 Polymarket 加密市场尾盘策略相关的文档。
本目录集中存放与 Polymarket 加密市场加密价差策略相关的文档。
## 目录结构
@@ -55,7 +55,7 @@ effectiveMinSpread = baseSpread × coefficient
- 需要策略的 `windowStartSeconds``windowEndSeconds` 传入计算处;若窗口长度为 0,可退化为系数 = 1.0 或 0.5(需约定)。
**优点**:与「尾盘只在窗口内触发」一致,时间语义清晰;毫秒级 progress 更精确。
**优点**:与「加密价差策略只在窗口内触发」一致,时间语义清晰;毫秒级 progress 更精确。
**缺点**`getAutoMinSpread` 需要增加当前时间(毫秒)和窗口参数(或传整个 strategy)。
---
@@ -1,8 +1,8 @@
# Crypto Tail Strategy Configuration Guide
# Crypto Spread Strategy Configuration Guide
## Part 1: What is Crypto Tail Strategy?
## Part 1: What is Crypto Spread Strategy?
Crypto Tail Strategy is an automated trading strategy designed specifically for Polymarket crypto markets' **5-minute** or **15-minute** "Up or Down" markets.
Crypto Spread Strategy is an automated trading strategy designed specifically for Polymarket crypto markets' **5-minute** or **15-minute** "Up or Down" markets.
**Core Logic**: Within a specified time window, when the market price enters your set price range, the system will automatically buy at a fixed price (0.99) without manual operation.
@@ -39,7 +39,7 @@ Cycle Start → Within Time Window → Price Enters Range → Auto Order
- **Maximum one trigger per cycle**: Within the same cycle, even if conditions are met multiple times, only one order is placed
- **Fixed order price**: All orders are submitted at price 0.99
- **Requires separate wallet**: It's recommended to use a dedicated wallet for tail strategies to avoid conflicts with other operations (manual trading, copy trading, etc.)
- **Requires separate wallet**: It's recommended to use a dedicated wallet for crypto spread strategies to avoid conflicts with other operations (manual trading, copy trading, etc.)
---
@@ -50,7 +50,7 @@ Cycle Start → Within Time Window → Price Enters Range → Auto Order
| Parameter | Description | Required | Example |
|-----------|-------------|----------|---------|
| **Account** | Select the wallet account for trading | ✅ | Account A |
| **Strategy Name** | Name your strategy for easy identification | ❌ | "BTC 15min Tail Strategy" |
| **Strategy Name** | Name your strategy for easy identification | ❌ | "BTC 15min Crypto Spread Strategy" |
| **Market** | Select the market to trade (5-minute or 15-minute) | ✅ | btc-updown-15m |
### 3.2 Cycle Settings
@@ -341,7 +341,7 @@ Enabled: On
2. **Position conflicts**: Manual trading and strategy trading may conflict
3. **Management confusion**: Difficult to distinguish which orders are from strategy vs manual
**Recommendation**: Create a dedicated wallet, only for tail strategies.
**Recommendation**: Create a dedicated wallet, only for crypto spread strategies.
### Q7: Why is the order price fixed at 0.99?
@@ -352,7 +352,7 @@ Enabled: On
### Q8: Does the strategy depend on auto-redeem functionality?
**A**: Yes, tail strategy depends on auto-redeem functionality.
**A**: Yes, crypto spread strategy depends on auto-redeem functionality.
**Reasons**:
- Strategy orders create positions after execution
@@ -411,7 +411,7 @@ Enabled: On
### 7.1 View Strategy List
On the "Crypto Tail Strategy" page, you can view all strategies:
On the "Crypto Spread Strategy" page, you can view all strategies:
- Strategy name
- Market information
- Time window
@@ -453,7 +453,7 @@ After deleting a strategy:
## Part 8: Summary
Crypto Tail Strategy is a powerful automated trading tool that can help you:
Crypto Spread Strategy is a powerful automated trading tool that can help you:
1. **Automated Trading**: No need for manual monitoring, system executes automatically
2. **Precise Control**: Precisely control trigger conditions through time windows and price ranges
@@ -1,4 +1,4 @@
# 加密市场尾盘策略 - 流程图
# 加密价差策略 - 流程图
## 一、整体架构
@@ -1,4 +1,4 @@
# 加密市场尾盘策略 - 5/15 分钟市场数据获取说明
# 加密价差策略 - 5/15 分钟市场数据获取说明
> 前端 UI 与交互详见 `crypto-tail-strategy-ui-spec.md`。
@@ -1,8 +1,8 @@
# 尾盘策略 - 最小价差参数流程分析
# 加密价差策略 - 最小价差参数流程分析
## 一、需求摘要
在现有尾盘策略上增加**最小价差**参数:当策略条件(时间窗、价格区间)满足时,再判断**当前周期 Binance K 线的开盘价与收盘价价差**是否满足最小价差;满足才下单,不满足则等待,直到价差满足再下单。
在现有加密价差策略上增加**最小价差**参数:当策略条件(时间窗、价格区间)满足时,再判断**当前周期 Binance K 线的开盘价与收盘价价差**是否满足最小价差;满足才下单,不满足则等待,直到价差满足再下单。
- **后端**:需订阅币安对应币对(如 BTC/USDC)的 K 线,维护当前周期的**开盘价**与**实时收盘价**,并在触发时做价差校验。
- **前端**:可配置三种场景——无、固定、自动(见下)。
@@ -244,4 +244,4 @@ sequenceDiagram
3. **下单与去重**
- 仍保持「每周期最多触发一次」;价差不满足时不写触发记录,直到某次同时满足价格与价差后才下单并写记录。
按上述流程即可在现有尾盘策略上接入「最小价差」参数,并由后端订阅币安 K 线、在触发前做价差校验;固定与自动的时序差异见**第六节时序图**。
按上述流程即可在现有加密价差策略上接入「最小价差」参数,并由后端订阅币安 K 线、在触发前做价差校验;固定与自动的时序差异见**第六节时序图**。
@@ -1,4 +1,4 @@
# 加密市场尾盘策略 - 任务梳理
# 加密价差策略 - 任务梳理
> 需求与 UI 见 `crypto-tail-strategy-ui-spec.md`,市场数据与执行规则见 `crypto-tail-strategy-market-data.md`
@@ -66,7 +66,7 @@
| 序号 | 任务 | 说明 |
|------|------|------|
| B16 | 自动赎回包含尾盘策略仓位 | 尾盘策略产生的仓位与跟单/手动一视同仁,纳入现有自动赎回逻辑,不排除(见 UI 规格附录 A)。 |
| B16 | 自动赎回包含加密价差策略仓位 | 加密价差策略产生的仓位与跟单/手动一视同仁,纳入现有自动赎回逻辑,不排除(见 UI 规格附录 A)。 |
| B17 | 调度/定时或常驻 | 对已启用策略按周期(如每 10–30 秒)检查:当前周期、是否在时间窗口内、是否已触发、价格是否进区间;满足则执行下单并写触发记录。 |
---
@@ -78,7 +78,7 @@
| 序号 | 任务 | 说明 |
|------|------|------|
| F1 | 路由 | App.tsx 增加 `/crypto-tail-strategy`、可选 `/crypto-tail-strategy/records/:id`。 |
| F2 | 菜单 | Layout 中增加「尾盘策略」菜单项,与跟单同级或在其下;key 与路由一致。 |
| F2 | 菜单 | Layout 中增加「加密价差策略」菜单项,与跟单同级或在其下;key 与路由一致。 |
### 4.2 列表页
@@ -146,5 +146,5 @@ F10 触发记录
- **时间区间**:仅当周期内当前时间落在 [windowStartSeconds, windowEndSeconds] 时才判断价格并下单;前端区间开始 ≤ 结束,且不超出 5min/15min。
- **每周期一次**:同一策略同一周期只触发一次(先满足价格的 outcome 买入,反方向不买)。
- **重试**:下单失败最多重试 2 次,共 3 次;仍失败记入触发记录为失败。
- **自动赎回**尾盘策略产生的仓位可被自动赎回,无排除逻辑。
- **自动赎回**加密价差策略产生的仓位可被自动赎回,无排除逻辑。
- **创建前检查**:未配置自动赎回时点击新增策略弹出「去配置」弹窗,不打开表单。
@@ -1,4 +1,4 @@
# 加密市场尾盘策略 - 前端 UI 规格
# 加密价差策略 - 前端 UI 规格
> 周期推导与市场数据获取详见 `crypto-tail-strategy-market-data.md`
@@ -10,7 +10,7 @@
| 项目 | 说明 |
|------|------|
| **菜单** | 在「跟单管理」同级或其下增加一项,如「尾盘策略」,key 建议 `/crypto-tail-strategy`。 |
| **菜单** | 在「跟单管理」同级或其下增加一项,如「加密价差策略」,key 建议 `/crypto-tail-strategy`。 |
| **路由** | 列表页 `/crypto-tail-strategy`;可选详情/触发记录 `/crypto-tail-strategy/records/:id`。 |
参考:`Layout.tsx``/copy-trading``/backtest` 的配置;`App.tsx` 中对应 `Route`
@@ -26,7 +26,7 @@
| 元素 | 类型 | 说明 |
|------|------|------|
| 页面标题 | 标题文案 | 如「加密尾盘策略」,用 `t('cryptoTailStrategy.list.title')`。 |
| 页面标题 | 标题文案 | 如「加密价差策略」,用 `t('cryptoTailStrategy.list.title')`。 |
| **钱包使用提示** | **AlertWarning** | **必须**在页面顶部或标题下方展示:提示用户**使用单独/专用钱包**运行本策略,避免该钱包用于手动交易、跟单等其他操作,否则可能导致余额或仓位变化,进而造成策略执行异常(如余额不足、下单失败等)。文案走多语言 `t('cryptoTailStrategy.list.walletTip')`,可带 `showIcon`。 |
| 新增策略 | ButtonPrimary) | 点击时**先检查自动赎回相关配置**(见 2.4);若未配置则弹出「去配置」简易弹窗,若已配置则打开「新增策略」表单弹窗。图标可用 `PlusOutlined`。 |
| 筛选(可选) | Select / 筛选项 | 按账户、启用状态筛选;移动端可收起到抽屉或折叠。 |
@@ -59,7 +59,7 @@
1. **检查**:请求系统配置(如 `apiService.systemConfig.getConfig()` 或已有接口),判断是否已配置 Builder API Key(及可选:自动赎回已开启)。若 `builderApiKeyConfigured === false`(或后端约定之「未配置」状态),视为未配置。
2. **未配置时**:不打开新增策略表单,改为弹出**简易弹窗**(Modal),内容建议:
- **标题**:如「请先配置自动赎回」,`t('cryptoTailStrategy.redeemRequiredModal.title')`
- **正文**:简短说明尾盘策略依赖自动赎回,需要先在「系统设置」中配置 Builder API Key 及自动赎回。文案 `t('cryptoTailStrategy.redeemRequiredModal.description')`
- **正文**:简短说明加密价差策略依赖自动赎回,需要先在「系统设置」中配置 Builder API Key 及自动赎回。文案 `t('cryptoTailStrategy.redeemRequiredModal.description')`
- **操作**
- **去配置**:主按钮,点击后关闭弹窗并跳转到系统设置页(如 `/system-settings`,该页含 Relayer 配置与自动赎回开关)。
- **取消**:次按钮或关闭图标,仅关闭弹窗。
@@ -133,7 +133,7 @@
|------|------|
| **钱包提示** | 列表页与新增/编辑表单**必须**包含「使用单独钱包」的 Alert 提示,避免用户用混用钱包导致异常;文案走多语言。 |
| **创建前检查** | 点击「新增策略」时先检查自动赎回/Builder API 是否已配置;未配置则弹出简易「去配置」弹窗,引导用户到系统设置配置 API Key 与自动赎回,不打开策略表单。 |
| 多语言 | 所有文案 `t('cryptoTailStrategy.xxx')`,在 `locales/zh-CN``zh-TW``en``common.json` 中增加键。需包含:`cryptoTailStrategy.list.walletTip``cryptoTailStrategy.form.walletTip`,以及 `cryptoTailStrategy.redeemRequiredModal.title``cryptoTailStrategy.redeemRequiredModal.description``cryptoTailStrategy.redeemRequiredModal.goToSettings``cryptoTailStrategy.redeemRequiredModal.cancel`。文案示例:列表页 `walletTip`:「请使用单独的钱包运行尾盘策略,避免该钱包用于手动交易、跟单等其他操作,否则可能导致余额或仓位变化,造成策略执行异常。」表单内 `walletTip`:「建议使用专用钱包,避免手动操作等导致余额或下单异常。」未配置赎回弹窗 `title`:「请先配置自动赎回」;`description`:「尾盘策略依赖自动赎回功能,请先在系统设置中配置 Builder API Key 并开启自动赎回。」;`goToSettings`:「去配置」;`cancel`:「取消」。 |
| 多语言 | 所有文案 `t('cryptoTailStrategy.xxx')`,在 `locales/zh-CN``zh-TW``en``common.json` 中增加键。需包含:`cryptoTailStrategy.list.walletTip``cryptoTailStrategy.form.walletTip`,以及 `cryptoTailStrategy.redeemRequiredModal.title``cryptoTailStrategy.redeemRequiredModal.description``cryptoTailStrategy.redeemRequiredModal.goToSettings``cryptoTailStrategy.redeemRequiredModal.cancel`。文案示例:列表页 `walletTip`:「请使用单独的钱包运行加密价差策略,避免该钱包用于手动交易、跟单等其他操作,否则可能导致余额或仓位变化,造成策略执行异常。」表单内 `walletTip`:「建议使用专用钱包,避免手动操作等导致余额或下单异常。」未配置赎回弹窗 `title`:「请先配置自动赎回」;`description`:「加密价差策略依赖自动赎回功能,请先在系统设置中配置 Builder API Key 并开启自动赎回。」;`goToSettings`:「去配置」;`cancel`:「取消」。 |
| 金额 | 统一 `formatUSDC`(见 frontend.mdc)。 |
| 响应式 | `useMediaQuery`;按钮触摸目标 ≥ 44px;移动端主操作突出。 |
| 类型 | 不用 `any`;为策略、触发记录定义 TypeScript 类型。 |
@@ -150,7 +150,7 @@
| 新增/编辑弹窗 | `frontend/src/pages/CryptoTailStrategyList/FormModal.tsx` 或内嵌 Modal |
| 触发记录 | `frontend/src/pages/CryptoTailStrategyList/TriggerRecordsModal.tsx``CryptoTailStrategyRecords.tsx` |
| 路由 | `App.tsx``/crypto-tail-strategy`、可选 `/crypto-tail-strategy/records/:id` |
| 菜单 | `Layout.tsx` 中增加「尾盘策略」菜单项 |
| 菜单 | `Layout.tsx` 中增加「加密价差策略」菜单项 |
| 类型 | `frontend/src/types/index.ts``types/cryptoTailStrategy.ts` 中增加策略与触发记录类型 |
| 多语言 | `frontend/src/locales/{zh-CN,zh-TW,en}/common.json` 中增加 `cryptoTailStrategy.*` |
@@ -158,7 +158,7 @@
## 7. 小结:UI 包含的主要元素
- **导航**:主导航中「尾盘策略」入口。
- **导航**:主导航中「加密价差策略」入口。
- **列表页**:标题、钱包提示 Alert、新增按钮(点击前先检查赎回配置,未配置则弹「去配置」简易弹窗)、筛选、表格/卡片(策略名、市场、价格区间、投入方式、状态、最近触发、操作)、加载与空状态。
- **未配置赎回弹窗**:简易 Modal,提示依赖自动赎回、需先配置 Builder API Key 与自动赎回;按钮「去配置」(跳转 `/system-settings`)、「取消」。
- **表单弹窗**:策略名、账户、市场选择、minPrice/maxPrice、投入方式(比例/固定)、启用开关、提交/取消。
@@ -169,9 +169,9 @@
## 附录 A 后端/产品要求:自动赎回须支持本策略仓位
自动赎回逻辑**必须支持赎回由尾盘策略产生的订单所对应的仓位**。即:本策略触发的市价买入会形成仓位,这些仓位在满足「可赎回」条件时,应被纳入现有自动赎回流程并正常发起赎回,不得因来源为「尾盘策略」而被排除。后端实现时需保证:
自动赎回逻辑**必须支持赎回由加密价差策略产生的订单所对应的仓位**。即:本策略触发的市价买入会形成仓位,这些仓位在满足「可赎回」条件时,应被纳入现有自动赎回流程并正常发起赎回,不得因来源为「加密价差策略」而被排除。后端实现时需保证:
- 尾盘策略下单产生的仓位,与跟单/手动下单等来源的仓位一视同仁,参与可赎回查询与批量赎回;
- 若当前自动赎回按账户或仓位类型过滤,需将「尾盘策略订单产生的仓位」包含在内。
- 加密价差策略下单产生的仓位,与跟单/手动下单等来源的仓位一视同仁,参与可赎回查询与批量赎回;
- 若当前自动赎回按账户或仓位类型过滤,需将「加密价差策略订单产生的仓位」包含在内。
这样前端所依赖的「自动赎回」对该策略才完整有效。
@@ -1,8 +1,8 @@
# 尾盘策略配置指南
# 加密价差策略配置指南
## 一、什么是尾盘策略?
## 一、什么是加密价差策略?
尾盘策略是一种自动化交易策略,专门用于 Polymarket 加密市场的 **5分钟****15分钟** "Up or Down" 市场。
加密价差策略是一种自动化交易策略,专门用于 Polymarket 加密市场的 **5分钟****15分钟** "Up or Down" 市场。
**核心逻辑**:在指定时间窗口内,当市场价格进入您设定的价格区间时,系统会自动以固定价格(0.99)买入,无需手动操作。
@@ -39,7 +39,7 @@
- **每周期最多触发一次**:同一个周期内,即使多次满足条件,也只下单一次
- **固定下单价格**:所有订单都以 0.99 的价格提交
- **需要单独钱包**:建议使用专门的钱包运行尾盘策略,避免与其他操作(手动交易、跟单等)冲突
- **需要单独钱包**:建议使用专门的钱包运行加密价差策略,避免与其他操作(手动交易、跟单等)冲突
---
@@ -50,7 +50,7 @@
| 参数 | 说明 | 必填 | 示例 |
|------|------|------|------|
| **账户** | 选择用于交易的钱包账户 | ✅ | 账户A |
| **策略名称** | 给策略起个名字,方便识别 | ❌ | "BTC 15分钟尾盘策略" |
| **策略名称** | 给策略起个名字,方便识别 | ❌ | "BTC 15分钟加密价差策略" |
| **市场** | 选择要交易的市场(5分钟或15分钟) | ✅ | btc-updown-15m |
### 3.2 周期设置
@@ -341,7 +341,7 @@
2. **仓位冲突**:手动交易和策略交易可能产生冲突
3. **管理混乱**:难以区分哪些订单是策略产生的,哪些是手动产生的
**建议**:创建一个专门的钱包,只用于尾盘策略。
**建议**:创建一个专门的钱包,只用于加密价差策略。
### Q7:下单价格为什么是固定的 0.99?
@@ -352,7 +352,7 @@
### Q8:策略需要依赖自动赎回功能吗?
**A**:是的,尾盘策略依赖自动赎回功能。
**A**:是的,加密价差策略依赖自动赎回功能。
**原因**
- 策略下单后会形成仓位
@@ -411,7 +411,7 @@
### 7.1 查看策略列表
在「尾盘策略」页面可以查看所有策略:
在「加密价差策略」页面可以查看所有策略:
- 策略名称
- 市场信息
- 时间窗口
@@ -453,7 +453,7 @@
## 八、总结
尾盘策略是一个强大的自动化交易工具,可以帮助您:
加密价差策略是一个强大的自动化交易工具,可以帮助您:
1. **自动化交易**:无需手动盯盘,系统自动执行
2. **精准控制**:通过时间窗口和价格区间精确控制触发条件
+1 -1
View File
@@ -168,7 +168,7 @@ const Layout: React.FC<LayoutProps> = ({ children }) => {
{
key: '/crypto-tail-management',
icon: <LineChartOutlined />,
label: t('menu.cryptoTail'),
label: t('menu.cryptoSpreadStrategy'),
children: [
{
key: '/crypto-tail-strategy',
+8 -8
View File
@@ -312,9 +312,9 @@
"leaders": "Leader Management",
"templates": "Templates",
"copyTradingConfig": "Copy Trading Config",
"cryptoTail": "Crypto Tail",
"cryptoTailStrategy": "Tail Strategy",
"cryptoTailMonitor": "Tail Monitor",
"cryptoSpreadStrategy": "Crypto Spread Strategy",
"cryptoTailStrategy": "Strategy Config",
"cryptoTailMonitor": "Real-time Monitor",
"positions": "Position Management",
"backtest": "Backtest",
"statistics": "Statistics",
@@ -1486,12 +1486,12 @@
"cryptoTailStrategy": {
"binanceApiAlert": {
"title": "Cannot connect to Binance API — strategy cannot run for now",
"description": "Tail strategy needs Binance market data to work. The connection failed; this may be a network issue or Binance outage. Try again later by clicking the button below.",
"description": "Crypto spread strategy needs Binance market data to work. The connection failed; this may be a network issue or Binance outage. Try again later by clicking the button below.",
"recheck": "Re-check"
},
"list": {
"title": "Crypto Tail Strategy",
"walletTip": "Use a dedicated wallet for tail strategy. Do not use it for manual trading or copy trading to avoid balance/position issues.",
"title": "Crypto Spread Strategy",
"walletTip": "Use a dedicated wallet for crypto spread strategy. Do not use it for manual trading or copy trading to avoid balance/position issues.",
"addStrategy": "Add Strategy",
"strategyName": "Strategy Name",
"account": "Account",
@@ -1548,7 +1548,7 @@
},
"redeemRequiredModal": {
"title": "Configure Auto Redeem First",
"description": "Tail strategy requires auto redeem. Please configure Builder API Key and enable auto redeem in System Settings.",
"description": "Crypto spread strategy requires auto redeem. Please configure Builder API Key and enable auto redeem in System Settings.",
"goToSettings": "Go to Settings",
"cancel": "Cancel"
},
@@ -1579,7 +1579,7 @@
}
},
"cryptoTailMonitor": {
"title": "Tail Strategy Monitor",
"title": "Crypto Spread Strategy Monitor",
"selectStrategy": "Strategy",
"selectStrategyPlaceholder": "Select a strategy to monitor",
"direction": "Direction",
+8 -8
View File
@@ -312,9 +312,9 @@
"leaders": "Leader 管理",
"templates": "跟单模板",
"copyTradingConfig": "跟单配置",
"cryptoTail": "尾盘",
"cryptoTailStrategy": "尾盘策略",
"cryptoTailMonitor": "尾盘监控",
"cryptoSpreadStrategy": "加密价差策略",
"cryptoTailStrategy": "策略配置",
"cryptoTailMonitor": "实时监控",
"positions": "仓位管理",
"backtest": "回测",
"statistics": "统计信息",
@@ -1486,12 +1486,12 @@
"cryptoTailStrategy": {
"binanceApiAlert": {
"title": "无法连接币安 API,策略暂时不能运行",
"description": "尾盘策略需要从币安获取行情数据才能工作。当前连接失败,可能是网络问题或币安服务异常,请稍后点击下方按钮重新检测。",
"description": "加密价差策略需要从币安获取行情数据才能工作。当前连接失败,可能是网络问题或币安服务异常,请稍后点击下方按钮重新检测。",
"recheck": "重新检测"
},
"list": {
"title": "加密尾盘策略",
"walletTip": "请使用单独的钱包运行尾盘策略,避免该钱包用于手动交易、跟单等其他操作,否则可能导致余额或仓位变化,造成策略执行异常。",
"title": "加密价差策略",
"walletTip": "请使用单独的钱包运行加密价差策略,避免该钱包用于手动交易、跟单等其他操作,否则可能导致余额或仓位变化,造成策略执行异常。",
"addStrategy": "新增策略",
"strategyName": "策略名称",
"account": "账户",
@@ -1548,7 +1548,7 @@
},
"redeemRequiredModal": {
"title": "请先配置自动赎回",
"description": "尾盘策略依赖自动赎回功能,请先在系统设置中配置 Builder API Key 并开启自动赎回。",
"description": "加密价差策略依赖自动赎回功能,请先在系统设置中配置 Builder API Key 并开启自动赎回。",
"goToSettings": "去配置",
"cancel": "取消"
},
@@ -1579,7 +1579,7 @@
}
},
"cryptoTailMonitor": {
"title": "尾盘策略监控",
"title": "加密价差策略监控",
"selectStrategy": "选择策略",
"selectStrategyPlaceholder": "请选择要监控的策略",
"direction": "监控方向",
+8 -8
View File
@@ -312,9 +312,9 @@
"leaders": "Leader 管理",
"templates": "跟單模板",
"copyTradingConfig": "跟單配置",
"cryptoTail": "尾盤",
"cryptoTailStrategy": "尾盤策略",
"cryptoTailMonitor": "尾盤監控",
"cryptoSpreadStrategy": "加密價差策略",
"cryptoTailStrategy": "策略配置",
"cryptoTailMonitor": "即時監控",
"positions": "倉位管理",
"backtest": "回測",
"statistics": "統計信息",
@@ -1486,12 +1486,12 @@
"cryptoTailStrategy": {
"binanceApiAlert": {
"title": "無法連接幣安 API,策略暫時不能運行",
"description": "尾盤策略需要從幣安取得行情資料才能運作。目前連線失敗,可能是網路問題或幣安服務異常,請稍後點擊下方按鈕重新檢測。",
"description": "加密價差策略需要從幣安取得行情資料才能運作。目前連線失敗,可能是網路問題或幣安服務異常,請稍後點擊下方按鈕重新檢測。",
"recheck": "重新檢測"
},
"list": {
"title": "加密尾盤策略",
"walletTip": "請使用單獨的錢包運行尾盤策略,避免該錢包用於手動交易、跟單等其他操作,否則可能導致餘額或倉位變化,造成策略執行異常。",
"title": "加密價差策略",
"walletTip": "請使用單獨的錢包運行加密價差策略,避免該錢包用於手動交易、跟單等其他操作,否則可能導致餘額或倉位變化,造成策略執行異常。",
"addStrategy": "新增策略",
"strategyName": "策略名稱",
"account": "賬戶",
@@ -1548,7 +1548,7 @@
},
"redeemRequiredModal": {
"title": "請先配置自動贖回",
"description": "尾盤策略依賴自動贖回功能,請先在系統設置中配置 Builder API Key 並開啟自動贖回。",
"description": "加密價差策略依賴自動贖回功能,請先在系統設置中配置 Builder API Key 並開啟自動贖回。",
"goToSettings": "去配置",
"cancel": "取消"
},
@@ -1579,7 +1579,7 @@
}
},
"cryptoTailMonitor": {
"title": "尾盤策略監控",
"title": "加密價差策略監控",
"selectStrategy": "選擇策略",
"selectStrategyPlaceholder": "請選擇要監控的策略",
"direction": "監控方向",
+1 -1
View File
@@ -446,7 +446,7 @@ export const apiService = {
},
/**
* API
* API
*/
cryptoTailStrategy: {
list: (data: { accountId?: number; enabled?: boolean } = {}) =>
+5 -5
View File
@@ -1036,7 +1036,7 @@ export interface BacktestTaskDto {
}
/**
*
*
*/
export interface CryptoTailStrategyDto {
id: number
@@ -1076,7 +1076,7 @@ export interface CryptoTailAutoMinSpreadResponse {
}
/**
*
*
*/
export interface CryptoTailStrategyTriggerDto {
id: number
@@ -1098,7 +1098,7 @@ export interface CryptoTailStrategyTriggerDto {
}
/**
*
*
*/
export interface CryptoTailMarketOptionDto {
slug: string
@@ -1109,7 +1109,7 @@ export interface CryptoTailMarketOptionDto {
}
/**
*
*
*/
export interface CryptoTailMonitorInitResponse {
/** 策略ID */
@@ -1159,7 +1159,7 @@ export interface CryptoTailMonitorInitResponse {
}
/**
*
*
*/
export interface CryptoTailMonitorPushData {
/** 策略ID */