# 止损/止盈的两种实现方式（双通道）

当前确实存在**两种**止损（以及止盈）执行方式，互为备份；逻辑如下。

---

## 一、两种方式分别是什么

### 1. 币安条件单（交易所侧，自动执行）

- **做法**：开仓或补挂时，在币安挂 **STOP_MARKET**（止损）和 **TAKE_PROFIT_MARKET**（止盈）条件单，触发价为策略算出的 `stop_loss` / `take_profit` 价格。
- **触发**：价格到达触发价时，**由币安自动撮合**，无需本机程序在线。
- **代码位置**：`position_manager._ensure_exchange_sltp_orders` → `client.place_trigger_close_position_order(trigger_type="STOP_MARKET" / "TAKE_PROFIT_MARKET", stop_price=...)`。
- **特点**：服务重启、断网、本机崩溃后，只要仓位和条件单还在，仍能按价止损/止盈。

### 2. 系统 WebSocket 监控（本机侧，到价后市价平仓）

- **做法**：本机对每个持仓订阅该交易对价格（WebSocket），在 `_check_single_position` 里用**当前价**和**目标止损/止盈**比较（按保证金的亏损/盈利比例），到价则调用 `close_position(symbol, reason='stop_loss'/'take_profit'/...)`，即**市价平仓**。
- **触发**：程序判定「当前盈亏已到止损或止盈目标」时，**本机主动发市价平仓单**。
- **代码位置**：`position_manager._monitor_position_price` → `_check_single_position` → 比较 `pnl_percent_margin` 与 `stop_loss_pct_margin` / 止盈目标 → `close_position(...)`。
- **特点**：可以做**移动止损、分步止盈（TP1 部分平仓）**等复杂逻辑，这些无法用交易所单一张单实现。

---

## 二、当前整体流程（开仓后）

1. 开仓成交后（或补建/修复后）调用 **`_ensure_exchange_sltp_orders`**：
   - 先取消该 symbol 上已有的 STOP_MARKET / TAKE_PROFIT_MARKET / TRAILING_STOP_MARKET；
   - 再按当前 `position_info` 的止损价、止盈价挂**一张止损 + 一张止盈**（交易所条件单）。
2. 同时对该 symbol 启动 **WebSocket 监控**（`_monitor_position_price`）：
   - 每次收到价格，用**保证金盈亏比例**判断是否到止损/到第一止盈/到第二止盈/到移动止损；
   - 到则 **`close_position(..., reason=...)`**（市价平仓）。

因此：**同一笔仓位既有交易所条件单，又有本机监控**，两条路都能触发平仓，形成双通道。

---

## 三、设计意图与分工

- **交易所条件单**：保证在**本机不在线**时（重启、断网、崩溃）仍有止损/止盈保护。
- **本机 WebSocket**：  
  - 实现**移动止损、TP1 部分平仓**等（交易所只挂固定止损/止盈，不会自动“移动”或“分步”）；  
  - 在**挂单失败**时作为兜底（见下）。

---

## 四、可能的问题与现状

| 问题 | 说明 | 当前处理 |
|------|------|----------|
| **重复平仓** | 交易所先触发平仓后，本机再判“到价”可能再发平仓。 | `close_position` 会先查币安是否还有仓位；无仓位则不再下单，仅同步 DB，避免重复市价单。 |
| **挂单失败只剩 WS** | 若 STOP_MARKET 挂单失败（如 -2021、网络错误），则**只有** WebSocket 能止损。 | 代码已写：挂单失败时打日志「将依赖 WebSocket 监控」，并再次检查当前价是否已穿止损，若已穿则立即市价平仓；未穿则仅靠 WS，进程若挂则无交易所保护。 |
| **价格源不一致** | 交易所条件单多用 **MARK_PRICE**，本机 WS 多用 last/mark。 | 可能有毫秒级差异，一般先触发的一方完成平仓，另一方发现无仓位即不再操作。 |
| **移动止损仅本机** | 移动止损（盈利到 X% 上移止损）只能由本机逻辑实现。 | **已改**：在移动止损**激活**或**更新**时，会调用 `_ensure_exchange_sltp_orders`，取消原止损/止盈条件单并按新止损价重挂，使移动止损也有交易所保护。 |

---

## 五、小结

- **是，止损（和止盈）有两种**：  
  1）**币安条件单**：到价由交易所自动止损/止盈；  
  2）**系统 WebSocket 监控**：到价由本机市价平仓。
- **两者并存**：同一仓位既有交易所单，又有本机监控，互为备份；本机还负责移动止损、TP1 部分平仓等。
- **主要风险点**：交易所止损/止盈**挂单失败**时，只剩 WebSocket，进程挂了就无交易所保护。  
- **移动止损**：已在「移动止损激活」与「移动止损更新」时同步至交易所（取消原条件单并按新止损价重挂），本机宕机后交易所仍能按最新移动止损价执行。
