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

132 lines
6.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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,避免秒级舍入误差。
- 需要策略的 `windowStartSeconds``windowEndSeconds` 传入计算处;若窗口长度为 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
```
**优点**:只依赖 `intervalSeconds``periodStartUnix``nowSeconds`,不依赖窗口配置。
**缺点**:若窗口只占周期后半段,周期前半段也会在算系数,语义上不如按窗口精确。
---
## 实现要点
### 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/windowEndSeconds``System.currentTimeMillis()`(毫秒)算 `progress`(按毫秒)→ `coefficient``effectiveMinSpread = 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% 线性递减 |
| 推荐区间 | 按触发窗口 `windowStartSeconds``windowEndSeconds` 计算进度(方案一) |
| progress | **按毫秒计算**`(nowMs - windowStartMs) / windowLenMs`,避免秒级舍入误差 |
| 计算位置 | 触发时在 `passMinSpreadCheck` 中算 progress → coefficient → effectiveMinSpread |
按上述实现后,AUTO 模式即为「预加载提供 100% 数值 + 随区间时间点从 100% 递减到 50%」的动态方案。