# 趋势回调策略说明

本文描述 **「趋势回调」** 自动交易计划的业务规则与实现口径。

**三所主站**（Binance / Gate / OKX）均在顶栏 **策略交易 → `/strategy`** 左栏提供同一套逻辑（共用 `strategy_trend_register.py`）；各所使用各自 API 与 `crypto.db`。

**检阅备忘**（中控平仓、交易记录、补仓展示、漏记补录）：[trend-hub-close-and-trade-records.md](./trend-hub-close-and-trade-records.md)

---

## 1. 适用场景

- 各 **USDT 永续** 实例独立部署，使用各自 API 与 `crypto.db`。
- 你已明确：**方向、止损价、补仓区间边界价、止盈价、杠杆**，并接受程序按风险预算拆分 **首仓 50% + 多档补仓 50%**。

---

## 2. 名词与参数

| 名称 | 含义 |
|------|------|
| **合约 USDT 可用余额** | **生成预览**时通过 API 读取的 **swap 账户 USDT `free`** 快照；**确认执行**时再次读取并与快照比对偏差。 |
| **风险比例** | 默认 **5%**：指「若整笔计划在 **补仓区间远侧边界**（做多=上沿、做空=下沿）这一侧的最坏价格结构下触及止损」，目标亏损上限约为 **可用余额快照 × 风险比例**（实现上用 `calc_risk_fraction` 与 `prepare_order_amount` 反推总张数，受交易所最小张数与精度约束）。 |
| **止损价** | 用户填写；开仓后挂 **交易所仓位类止损触发单**（全平）。 |
| **补仓区间边界**（库字段 `add_upper`） | 用户填写；**仅在该价位与止损价构成的区间内** 才允许程序触发剩余 50% 的市价补仓。**界面文案**：做多显示「补仓上沿」，做空显示「补仓下沿」。校验：做多 `止损 < 边界价`；做空 `止损 > 边界价`。 |
| **止盈价** | 用户填写的 **固定价格**；**不由交易所条件止盈单触发**，由应用后台 **按标记价/行情价轮询**，达到后 **市价全平**。 |
| **杠杆** | 计划内固定写入；用于 `set_leverage` 与名义换算。 |
| **补仓档位数** | 默认 **5** 档（环境变量 `TREND_PULLBACK_DCA_LEGS` 可调）；程序在满足最小张数前提下可能 **自动减少档数**。 |

---

## 3. 执行流程（时间顺序）

### 3.0 列表时间窗（交易记录 / 计划历史）

- **交易记录**、**计划历史**（含预览快照）列表与 **交易记录 CSV 导出** 支持 **UTC** 时间筛选（默认 UTC 当日；可选近 24h、近 7d、自定义起止）。
- 查询参数：`win_preset`（`utc_today` / `utc_last24h` / `utc_last7d` / `custom`）、自定义时另传 `from_utc`、`to_utc`。
- **统计分析**页仍按北京时间 `TRADING_DAY_RESET_HOUR` 切日，不受列表窗影响。

### 3.1 预览阶段（不下单）

1. **风控**：与「机器人下单监控」**互斥**——存在活跃机器人持仓或运行中趋势计划时，不可生成预览。
2. **读取可用余额快照** `get_available_trading_usdt()`，失败则拒绝。
3. **计算**（写入表 `trend_pullback_previews`，并跳转带 `preview_id`）：  
   - 在 **补仓区间边界 ↔ 止损** 区间内生成 `N` 个补仓触发价（做多从上沿向止损、做空从下沿向止损）；  
   - 将 **剩余 50% 计划张数** 拆成 `N` 份写入 `leg_amounts_json`。  
4. **预览有效期**：默认 **120 秒**（`TREND_PULLBACK_PREVIEW_TTL_SECONDS`），超时须重新点「生成预览」。

### 3.2 确认执行（实盘）

5. 再次校验：预览未过期；**当前可用余额**与预览快照相对偏差 ≤ `TREND_PREVIEW_MAX_BALANCE_DRIFT_PCT`（默认 **5%**），否则拒绝执行并要求重新预览。  
6. **首仓**：**立即市价** 开立 **总计划张数 × 50%**（不附带交易所止盈单）。  
7. **止损**：撤销旧条件单后，挂 **仅止损** 的仓位触发单；之后每次补仓成交会 **刷新** 止损挂单。  
7b. **保本移交下单监控**（可选）：首仓完成且交易所有持仓后，可点击「保本移交下单监控」——将止损移至 **持仓均价 ± 偏移%**（默认 **+0.3%** 多 / **−0.3%** 空），仅当新止损 **优于** 当前止损时生效；**本次趋势计划随即结束**，持仓写入 **下单监控**（备注 **趋势回调计划**），交易所在 **同一时刻挂保本止损 + 计划止盈**；后续无论中控平仓或交易所手动平仓，均经下单监控轮询 **`reconcile_external_closes` / `check_order_monitors`** 写入 **交易记录**（含 `trend_plan_id`、开仓类型「趋势回调」），供人工核对。  
8. **补仓**：当价格 **穿越** 下一档触发价（做多为自上向下穿越，做空为自下向上穿越）时，按该档张数 **市价加仓**；直至 `N` 档执行完毕或计划结束。  
9. **止盈监控**：后台线程若发现价格触及止盈，则 **市价全平**。  
10. **止损触发**：若仓位被交易所止损打光，本地检测到 **持仓为 0** 后记账为 **止损** 并结束计划。  
11. **计划结束**：任一结束路径（止盈 / 止损 / 用户手动结束）均会 **撤单**（条件单 + 普通挂单，尽力而为）。

### 3.3 取消预览

用户可「取消预览」删除 `trend_pullback_previews` 中对应记录；过期记录会在新预览或页面加载时清理。

### 3.4 界面：计划历史与运行中浮动盈亏

- **计划历史（页顶卡片）**  
  - 仅展示 **`trend_pullback_plans` 中已结束的计划**（`status != 'active'`，如止盈结束、止损结束、手动结束）。  
  - **不包含**仅存在于 `trend_pullback_previews`、从未「确认执行」的预览。  
  - 每行提供 **删除**：删除该计划行，并删除 `trade_records` 中 **`trend_plan_id` 与之相同** 且类型为「趋势回调」的记录（用于与计划一一对应的新数据；历史旧行若无 `trend_plan_id` 则不会随删）。  
- **运行中的计划（交易执行页）**  
  - 在计划摘要下方展示 **浮盈亏（交易所）**：来自 Gate 当前持仓接口的 **未实现盈亏**（及标记价，若可得）；与本地按均价估算可能略有差异，以交易所为准便于对照。  
  - **补仓边界**按方向显示「补仓上沿」或「补仓下沿」（数值仍为 `add_upper` 字段）。  
  - **手动保本**：表单可改偏移 %（默认见 `TREND_PULLBACK_MANUAL_BREAKEVEN_OFFSET_PCT`）；成功后显示「已保本」时间与原止损（若与当前不同）。

### 3.5 交易记录与交易所「已实现盈亏」对齐

- 平仓时仍会写入一条 **`trade_records`**（`monitor_type=趋势回调`），其中的 **`pnl_amount` 等为本地估算**（`calc_pnl`，不含手续费、资金费等完整账单口径）。  
- 打开 **「交易执行」或「交易记录」** 页面时，若已配置 **`GATE_API_KEY` / `GATE_API_SECRET`**（不要求 `LIVE_TRADING_ENABLED=true`，只读即可），应用会按节流策略（同进程约 **25 秒**内最多一次）调用 Gate **`fetch_positions_history`（平仓历史）**，为尚未写入 `exchange_sync_key` 的趋势回调记录 **匹配一条平仓记录**，并回填：  
  - **`exchange_realized_pnl`**：交易所口径已实现盈亏（与 App「历史仓位」更接近）；  
  - **`exchange_opened_at` / `exchange_closed_at`**：换算为应用时区（默认北京）下的开、平时间字符串。  
- **交易记录表**展示列「开仓(展示) / 平仓(展示) / 盈亏U(展示)」：对「趋势回调」行，若已同步则优先显示交易所字段（界面小字 **「所」**）；未同步前仍显示本地复盘字段（小字 **「估」**）。  
- 匹配规则概要：同品种、同方向、平仓时间与本地 `closed_at` 接近，并结合 **`trend_plan_id`** 对应计划的 `opened_at` 收窄时间窗；极端情况下若短时间多笔同向同品种，仍存在错配可能，可对照 `exchange_sync_key` 与交易所记录。

---

## 4. 与「机器人下单监控」的差异

| 项目 | 机器人下单监控 | 趋势回调 |
|------|------------------|----------|
| 开仓 | 单次市价 + 条件止盈+止损 | 首仓 50% 市价 + 多档补仓 + **仅止损在交易所** |
| 止盈 | 条件单 + 本地监控 | **仅本地监控市价止盈** |
| 仓位基数 | 以损定仓（表单/会话基数） | **可用余额快照 × 风险比例** 推导 |
| 移动保本 | 支持（按 R 自动上移） | **保本移交**（结束计划→下单监控；交易所 TP+SL；**无**自动 R 保本） |

---

## 5. 风险声明（必读）

- 市价单存在 **滑点**；极端行情下实际亏损可能 **大于** 理论 5%。  
- 补仓触发依赖应用 **轮询间隔**（`MONITOR_POLL_SECONDS`），非毫秒级高频。  
- 交易所 **最小张数 / 精度** 可能导致计划张数被截断，实际风险略低于或偏离纸面计算。  
- 请使用 **单独 API Key / 子账户**，并先在 `LIVE_TRADING_ENABLED=false` 环境验证流程（若需沙盒请自行对接测试网，本仓库默认实盘接口）。

---

## 6. 相关环境变量

| 变量 | 说明 | 默认 |
|------|------|------|
| `TREND_PULLBACK_MANUAL_BREAKEVEN_OFFSET_PCT` | 手动保本默认偏移（相对持仓均价，%） | `0.3` |
| `TREND_PULLBACK_DCA_LEGS` | 剩余 50% 拆档数量上限 | `5` |
| `TREND_PULLBACK_PREVIEW_TTL_SECONDS` | 预览有效时间（秒） | `120` |
| `TREND_PREVIEW_MAX_BALANCE_DRIFT_PCT` | 确认执行时允许「当前可用 / 预览快照」最大相对偏差（%） | `5` |
| `MONITOR_POLL_SECONDS` | 监控轮询间隔（秒） | `3` |
| `LIVE_TRADING_ENABLED` | 是否允许真实下单 | `false` |
| `FULL_MARGIN_BUFFER_RATIO` | 计划保证金相对可用余额上限比例 | `0.98` |
| `APP_TIMEZONE` | 应用墙钟与「北京日期」同步起点时区（如 `Asia/Shanghai`） | `Asia/Shanghai` |
| `EXCHANGE_POSITION_SYNC_FROM_BJ` | 拉取 Gate **平仓历史** 的最早日期（`YYYY-MM-DD`，按 `APP_TIMEZONE` 当日 **00:00** 起算）。**留空**则从近 **90 天** 起拉取 | 空 |
| `EXCHANGE_POSITION_HISTORY_LIMIT` | 单次拉取平仓历史条数上限（50–1000） | `200` |

---

## 7. 数据库

- **`trend_pullback_previews`**：未执行的预览行（含 `expires_at_ms`），执行成功或取消后删除；过期可被清理。  
- **`trend_pullback_plans`**：趋势回调计划。执行后写入一行，`status='active'` 表示运行中；止盈 / 止损 / 手动结束后变为 **`stopped_tp` / `stopped_sl` / `stopped_manual`** 等非 `active` 状态，并出现在页顶 **计划历史**。字段含快照可用余额、计划保证金、总张数、首仓张数、补仓 JSON、网格价 JSON、已补仓档数、均价、`opened_at`、`message`（结束说明）等；**`add_upper`** 存补仓区间远侧边界价（做多=上沿、做空=下沿）。  
- **`trade_records`**（`monitor_type=趋势回调`）：每次计划结束插入一行；含本地估算盈亏等。新写入行带 **`trend_plan_id`** 指向 `trend_pullback_plans.id`。另含 **`exchange_realized_pnl`、`exchange_opened_at`、`exchange_closed_at`、`exchange_sync_key`**，由页面触发的交易所平仓历史同步填充（见 3.5）。  

**CSV 导出**：交易记录导出为 **v3**，包含上述交易所对齐字段及 `trend_plan_id`。
