Files
PolyHermes/docs/crypto-tail-auto-spread-dynamic-coefficient.md
T
WrBug 5cdcc487d4 feat(cryptotail): AUTO 最小价差 100%→50% 动态系数,progress 按毫秒计算
- BinanceKlineAutoSpreadService: 缓存 100% 基准价差,新增 getAutoMinSpreadBase
- CryptoTailStrategyExecutionService: 按窗口内毫秒进度算 coefficient,effectiveMinSpread = baseSpread × (1 - 0.5×progress)
- 新增方案文档 docs/crypto-tail-auto-spread-dynamic-coefficient.md

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-14 20:48:08 +08:00

6.4 KiB
Raw Blame History

AUTO 最小价差:100%→50% 动态系数方案

现状

  • BinanceKlineAutoSpreadService:拉取历史 K 线 → IQR 剔除异常值 → 求平均得到「基础价差」→ 固定 ×0.7 后缓存。
  • 预加载(周期开始时):computeAndCache() 计算并缓存的是 已乘 0.7 的值。
  • 触发时:getAutoMinSpread() 直接返回缓存值,等价于始终用 70% 的系数。

问题:70% 固定,无法随周期内时间变化放宽或收紧。


目标

  1. 预加载提供 100% 数值:缓存里存「基础价差」(IQR 平均),不再乘 0.7,即预加载 = 100% 基准。
  2. 系数随区间时间点动态递减:从 100% 线性递减到 50%,根据「当前时间在区间内的进度」计算。

方案一:按「触发窗口」进度(推荐)

区间:策略的触发窗口 [periodStartUnix + windowStartSeconds, periodStartUnix + windowEndSeconds]

  • 窗口起始:系数 = 100%(最严,价差要求最高)。
  • 窗口内时间越靠后,系数越小;窗口结束:系数 = 50%(最松,更容易触发)。

公式(progress 按毫秒计算,保证精度):

windowStartMs = (periodStartUnix + windowStartSeconds) * 1000
windowEndMs   = (periodStartUnix + windowEndSeconds) * 1000
windowLenMs   = windowEndMs - windowStartMs
nowMs         = System.currentTimeMillis()

progress = (nowMs - windowStartMs) / windowLenMs
progress = clamp(progress, 0, 1)

// 比例系数 = progress × (100% - 50%),即已「消耗」的系数降幅
// 真正系数 = 100% - 比例系数
coefficient = 1.0 - progress × (1.0 - 0.5) = 1.0 - 0.5 × progress

effectiveMinSpread = baseSpread × coefficient

计算示例(时间区间 14分0秒~15分0秒,窗口 60 秒 = 60000 ms):

时刻 进入窗口的毫秒数 progress(按毫秒) 比例系数 真正系数
14:00 0 0/60000 = 0% 0% × 50% = 0% 100%
14:15 15000 15000/60000 = 25% 25% × 50% = 12.5% 87.5%
14:30 30000 30000/60000 = 50% 50% × 50% = 25% 75%
15:00 60000 60000/60000 = 100% 100% × 50% = 50% 50%

即:在 14分15秒 时,progress = 15000ms / 60000ms = 25%,比例系数 = 12.5%,真正系数 = 87.5%。实现时统一用毫秒计算 progress,避免秒级舍入误差。

  • 需要策略的 windowStartSecondswindowEndSeconds 传入计算处;若窗口长度为 0,可退化为系数 = 1.0 或 0.5(需约定)。

优点:与「尾盘只在窗口内触发」一致,时间语义清晰;毫秒级 progress 更精确。
缺点getAutoMinSpread 需要增加当前时间(毫秒)和窗口参数(或传整个 strategy)。


方案二:按「整周期」进度

区间:整个周期 [periodStartUnix, periodStartUnix + intervalSeconds]progress 按毫秒计算

periodStartMs = periodStartUnix * 1000
periodEndMs   = (periodStartUnix + intervalSeconds) * 1000
periodLenMs   = intervalSeconds * 1000L
nowMs         = System.currentTimeMillis()

progress = (nowMs - periodStartMs) / periodLenMs
progress = clamp(progress, 0, 1)

coefficient = 1.0 - 0.5 * progress
effectiveMinSpread = baseSpread × coefficient

优点:只依赖 intervalSecondsperiodStartUnixnowSeconds,不依赖窗口配置。
缺点:若窗口只占周期后半段,周期前半段也会在算系数,语义上不如按窗口精确。


实现要点

1. 缓存 100% 基准值

  • BinanceKlineAutoSpreadService
    • computeAndCache():缓存 不乘 0.7 的 (avgUp, avgDown),即 IQR 平均后的原始值(100% 基准)。
    • 可保留方法名与入参不变,仅去掉 autoSpreadCoefficient 的乘法;或新增 getBaseSpread() 语义,内部仍用同一缓存。

2. 动态系数计算位置

  • 系数依赖「当前时间」和「区间定义」,适合在 触发校验处 算,而不是在 AutoSpread 服务里写死。
  • CryptoTailStrategyExecutionService.passMinSpreadCheck()
    • 当前:getAutoMinSpread(intervalSeconds, periodStartUnix, outcomeIndex) 得到已乘系数的值。
    • 改为:
      • 取「基础价差」:getAutoMinSpreadBase(intervalSeconds, periodStartUnix, outcomeIndex) 或由现有缓存返回 100% 值。
      • passMinSpreadCheck 内根据 strategy.windowStartSeconds/windowEndSecondsSystem.currentTimeMillis()(毫秒)算 progress(按毫秒)→ coefficienteffectiveMinSpread = baseSpread × coefficient

3. 接口形态建议

  • BinanceKlineAutoSpreadService
    • computeAndCache(interval, periodStartUnix):只缓存 100% 基准 (baseUp, baseDown),不再乘 0.7。
    • getAutoMinSpreadBase(interval, periodStartUnix, outcomeIndex): BigDecimal?:仅返回缓存的基础价差;若需兼容旧名,可保留 getAutoMinSpread 但增加可选参数 coefficient,默认 1.0。
  • CryptoTailStrategyExecutionService
    • passMinSpreadCheck(strategy, periodStartUnix, outcomeIndex) 内:
      • baseSpread = getAutoMinSpreadBase(...)
      • 计算 progress(按方案一用 windowStart/End,或方案二用 interval)。
      • coefficient = 1.0 - 0.5 * progress,再 effectiveMinSpread = baseSpread * coefficient 做比较。

4. 边界与兼容

  • 窗口长度为 0:可约定 coefficient = 0.5 或 1.0,避免除零。
  • 已有策略未配置窗口(全 0):若用方案一,可退化为「整周期」或固定 0.5/1.0」。
  • 预加载逻辑(如 CryptoTailOrderbookWsService 的 precomputeAutoMinSpreadForCurrentPeriods)无需改,仍调用 computeAndCache,只是缓存内容变为 100% 基准。

小结

项目 内容
预加载 缓存 100% 基础价差(去掉固定 0.7)
系数范围 100% → 50% 线性递减
推荐区间 按触发窗口 windowStartSecondswindowEndSeconds 计算进度(方案一)
progress 按毫秒计算(nowMs - windowStartMs) / windowLenMs,避免秒级舍入误差
计算位置 触发时在 passMinSpreadCheck 中算 progress → coefficient → effectiveMinSpread

按上述实现后,AUTO 模式即为「预加载提供 100% 数值 + 随区间时间点从 100% 递减到 50%」的动态方案。