refactor: 尾盘策略统一更名为加密价差策略
- 前端:菜单父级「加密价差策略」,子项「策略配置」「实时监控」;i18n 与类型注释全部替换 - 后端:i18n、ErrorCode、DTO/实体/服务注释与日志「尾盘」→「加密价差策略」;TG 通知文案 - 文档:README、中英文用户指南、UI 规格、任务、流程、价差流程、市场数据、动态系数等全部更新 - 不修改 API 路径、路由、表名与类名,保持兼容 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -6,7 +6,7 @@ import jakarta.annotation.PostConstruct
|
||||
import org.springframework.context.annotation.Configuration
|
||||
|
||||
/**
|
||||
* 尾盘监控服务配置
|
||||
* 加密价差策略监控服务配置
|
||||
* 处理 WebSocketSubscriptionService 和 CryptoTailMonitorService 之间的循环依赖
|
||||
*/
|
||||
@Configuration
|
||||
|
||||
+11
-11
@@ -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 {
|
||||
|
||||
+1
-1
@@ -3,6 +3,6 @@ package com.wrbug.polymarketbot.event
|
||||
import org.springframework.context.ApplicationEvent
|
||||
|
||||
/**
|
||||
* 尾盘策略创建/更新/启用状态变更后发布,用于立即触发一轮执行检查。
|
||||
* 加密价差策略创建/更新/启用状态变更后发布,用于立即触发一轮执行检查。
|
||||
*/
|
||||
class CryptoTailStrategyChangedEvent(source: Any) : ApplicationEvent(source)
|
||||
|
||||
+1
-1
@@ -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?
|
||||
|
||||
/** 轮询发 TG:status=success、orderId 非空、未发过通知,按创建时间正序 */
|
||||
|
||||
+1
-1
@@ -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 }
|
||||
|
||||
|
||||
+1
-1
@@ -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()}"
|
||||
)
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+10
-10
@@ -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"
|
||||
|
||||
+7
-7
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -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
|
||||
}
|
||||
|
||||
|
||||
+17
-17
@@ -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)
|
||||
|
||||
+11
-11
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
+9
-9
@@ -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("加密价差策略执行服务已清理缓存和锁")
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -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 {
|
||||
|
||||
+1
-1
@@ -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("、")
|
||||
|
||||
+4
-4
@@ -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=回測管理
|
||||
|
||||
Reference in New Issue
Block a user