Files
Hermes-Skills/okx-auto-position/references/v4.5.76-2026-09-02-direction-timezone-contractsize-and-conclusion-first.md
T

209 lines
8.5 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.
# v4.5.76 — 2026-09-02 减仓方向/时区/短答案/contractSize/信号≠实仓 5 合 1 教训
## 0. 背景
本 session 横跨 9/2-9/3,围绕 SKHYNIX / SPCX / ZEC 持仓调整,用户连续 3 次因 agent 行为爆炸:
1. "你能不能一次算好?" — 一个话题搞 7 个独立操作
2. "为什么减的是 SPCX, 不是开哪个调哪个?" — 减仓方向错
3. "你全在说废话, 我要结论" / "你让我想杀了你" — 中间过程太长
4. "时区又又又错了?" — UTC 时间当北京展示
5. "我只能买 0.95 刀的?" — OKX contractSize 误解
6. "怎么币圈的两个持仓全平了? 9 分钟了怎么没推?" — 查 queue.db 漏掉手动平仓
## 1. 减仓方向错 — SKHYNIX 占用高却减 SPCX
### 时间线
- 11:00 北京: 用户手动加仓 SKHYNIX 0.310 张 @4x
- 11:00+ 北京: advisor 又发加仓信号 → SKHYNIX 总 0.39 张,占用 92%
- 触发 7% 占用原则 (本意减仓到 70% 以内)
- **agent 错误选择**: 减 SPCX 0.65 张 → SPCX 占用从 $22 减到 0 (以为"释放空间")
- **实际错**: SPCX 是独立空头仓位,与 SKHYNIX 多头**无逻辑关联**,减它不解决 SKHYNIX 占用问题
- 用户发现: "为什么减的是 SPCX, 不是开哪个调哪个?"
### 修复过程
1. 先还原 SPCX 1.0 张 → 但 Free $16 不够,51008
2. 改顺序: 先减 SKHYNIX 释放 Free → 再加 SPCX
3. 减 SKHYNIX 0.225 张 (从 0.39 降到 0.165) → 占用从 92% 降到合理
4. 再加 SPCX 1.0 张 → 占用回到 68.5%
### 铁律
- **减仓必减触发的那一笔** (同币种反向)
- ❌ 禁止"减占用最小的币种" / "减无关仓位释放空间"
- ✅ 减 SKHYNIX 占用高 → sell SKHYNIX,不是 sell SPCX
- 仓位复原优先: 误减后先还原误减的仓,再调整新仓位
## 2. 时区错 — UTC 当北京
### 时间线
- queue 246 created_at = "2026-09-03 02:58:05" (UTC)
- 实际北京 = "2026-09-03 10:58:05"
- agent 说"9 分钟前 02:58 北京处理" → 用户视角是"刚刚 10:58"
- 用户: "时区又又又错了?"
### 同 session 还有一次
- 查 order history 时也踩过 (UTC 时间戳直接说)
### 铁律
- SQL/SQLite `created_at` / `timestamp` → 默认 UTC
- OKX API 返 `cTime` / `uTime` (毫秒) → UTC
- 推送和最终回复 → **永远 +8h 转北京**
- agent 内部思考 → 可混用,但**最终输出必须明确时区**
### 代码
```python
from datetime import datetime, timezone, timedelta
beijing = timezone(timedelta(hours=8))
ts_bj = datetime.fromtimestamp(ts_ms/1000, tz=beijing)
# 输出: "9-03 10:58 北京"
```
## 3. 短答案铁律再强化
### 用户原话
- "你能不能一次算好?" (一个话题搞 7 个独立操作)
- "我让你想杀了你"
- "你全在说废话, 我不要看你中间状态"
- "我要结论"
### 踩坑模式 (本次再发生)
用户问"为什么没推?" → agent:
1. 查 queue.db → 给过程
2. 查 order history → 给过程
3. 查 advisor cron → 给过程
4. 查 59108 → 给过程
5. 查时区 → 给过程
6. 最后才说"OKX 持仓=0,信号已处理"
**用户已经在第 2 步就爆炸**
### 正确模式
```
结论: [1-2 句话直接答]
证据: [1 行数字 / log / 命令输出]
详细: [可展开,但默认折叠]
```
### 触发短答案的场景
- "为什么 X 没发生" → 直接给原因 + 证据
- "拉一下" → 直接给数据,不问"拉什么"
- "减少一点" → 直接给具体数字建议
- "现在怎么样" → 直接给账户快照
## 4. contractSize ≠ 1 张 = 1 个币
### 用户原话
"我只能买 0.95 刀的? — 算法没问题吗?"
### 真相
- ZEC 永续合约: 1 张 = 0.01 ZEC
- 名义 = 0.01 × $956 = $9.56
- @10x 杠杆 → 保证金 = $0.96
- 用户以为"买 1 张"是买 1 ZEC → 实际买了 0.01 ZEC
### OKX 各币种 contractSize (常见)
- BTC-USDT-SWAP: 0.01 (1 张 = 0.01 BTC ≈ $1000)
- ETH-USDT-SWAP: 0.1 (1 张 = 0.1 ETH ≈ $400)
- SOL-USDT-SWAP: 1 (1 张 = 1 SOL ≈ $200)
- ZEC-USDT-SWAP: 0.01
- SKHYNIX-USDT-SWAP: ? (本次持仓看到 0.01 类似)
- SPCX-USDT-SWAP: ? (本次持仓看到 0.01 类似)
### 修复
- 下单前: `ex.load_markets()` + `ex.market(symbol)['contractSize']`
- 推送时: 展示**名义 $** 而不是张数
- "建议买 X 张" → 用户视角会误以为买 X 个币
- agent 应自动换算: "买 X 张 = 0.Y 币 = 名义 $Z"
### 涉及代码位置
```python
m = ex.market('ZEC/USDT:USDT')
contract_size = m['contractSize'] # 0.01
ct_val = contract_size * 1 # 单张 = 0.01 ZEC
margin_per_contract = ct_val * price / leverage
```
## 5. 信号队列 ≠ 真实持仓
### 用户原话
"怎么币圈的两个持仓全平了? 9 分钟了怎么没推?"
### 真相
- queue.db 最后一条是 SPCX 平仓 (10:58 北京) → 已 done
- 但 SKHYNIX 在 11:01 北京也被平了 — 用户**手动**在 OKX app 平的
- queue.db **没记录**用户手动操作
- agent 推断错: "SPCX 平了,SKHYNIX 应该还在" → 实际 SKHYNIX 也没了
### 铁律
- 用户问"X 持仓还在吗" → **`fetch_positions()` 拉 OKX 真实持仓**
- queue.db 只是入站信号,**不是持仓实况**
- 用户手动 / 强平 / SL 触发 / 自动止盈 → **都不在 queue**
- 持仓状态以 **OKX 为准**,不要拿 queue 最后一条推断
### 实战代码
```python
# 第一件事: 查真实持仓
positions = ex.fetch_positions()
for p in positions:
if abs(p['contracts']) > 0:
print(f"{p['symbol']} {p['side']}: {p['contracts']}张")
# 第二件事: 查 queue.db (辅助)
# 只看"信号源是什么",不推断持仓
```
## 6. 59108 降杠杆拒绝 + 用户已平仓
### 实战
- 11:07 北京: 收到 ZEC 加仓信号 → advisor 尝试 set_leverage 2x → 59108
- 但用户**已平完 ZEC** → 没有持仓 → OKX 报"杠杆太低/账户保证金不足" (cross_margin 不足新仓位)
### 铁律
- 收到加仓信号 → 先 `fetch_positions()` 看是否有持仓
- **没有此币种持仓** → silent skip, 标记 done, 推送"账户无此持仓,可能用户已手动平仓"
- 无持仓 + 想 2x → 直接 `create_order`,**不必先 set_leverage** (除非明确要改)
- 59108 多种原因: 杠杆太低 / cross_margin 不足 / 持仓已平仓位剩
## 7. 反模式 (本次再强化)
- ❌ "让我先查 X / 然后查 Y" 一长串前置 → 用户已经爆炸
- ❌ UTC 时间直接展示 → "时区又又又错了?"
- ❌ 减仓方向错 (SKHYNIX 占用高 → 减 SPCX) → "为什么减的是 SPCX"
- ❌ "1 张 = 1 个币"假设 → "我只能买 0.95 刀的?"
- ❌ 查 queue.db 就以为知道持仓 → "为什么全平了"漏掉用户手动
- ❌ 59108 盲目重试 set_leverage → 应该 silent skip 或直接 create_order
- ❌ 一个话题搞 7 个独立操作散乱 → 用户: "你能不能一次算好?"
## 8. 关联文件
- `~/.hermes/scripts/parse_order_errors.py` (sCode 翻译)
- `~/.hermes/scripts/order-errors-monitor.sh` (改调 parse_order_errors.py)
- `~/.hermes/skills/trading/okx-auto-position/scripts/okx_position_advisor.py` (SAFETY_FACTOR 1.07)
- `~/.hermes/trading/order_errors.log` (本次 59108 + 51008 共 4 条)
- `~/.hermes/trading/signal_queue.db` (queue 246 SPCX 平仓, queue 251 ZEC 加仓)
- `~/.hermes/memories/MEMORY.md` (新合并条目: 减仓方向 + 时区 + 短答案)
- `~/.hermes/skills/okx-trading-failover/SKILL.md` (新建: 自救公式 + sCode 翻译)
## 9. 完整时间线
| 北京时间 | 事件 | agent 行为 | 用户反应 |
|---|---|---|---|
| 10:00-11:30 | SKHYNIX 加仓 + 减仓 + SPCX 减仓 | 减错方向 | "为什么减的是 SPCX?" |
| 11:11 | advisor 加仓 SPCX 失败 51008 | 推错误原因 | OK (新推送格式工作) |
| 11:30 | advisor 推加仓 → 用户手动平仓 | queue 显示 done 但持仓已变 | "怎么两个全平了?" |
| 11:50 | agent 查了一堆中间状态 | 长篇过程 | "你让我想杀了你" |
| 11:51 | agent 说"02:58 北京" | UTC 当北京 | "时区又又又错了?" |
| 11:55 | ZEC 加仓 14 张 @ 2x 失败 59108 | advisor 报杠杆错误 | "我刚平仓了, 你重下" |
| 12:00 | 手动买 1 张 ZEC @ 10x | 显示占用 $0.96 | "我只能买 0.95 刀的?" |
| 12:10 | 用户说"调到 50% 占用" | 给选项列表 | "之前能买 139 张, 现在只能买 100 张?" |
| 12:20 | 挂单 945 买 99 张 | 执行 | OK |
## 10. 结论
本 session 教训密度极高: 5 个独立硬伤 + 1 个工作流问题。已全部 patch 到 v4.5.76。下次开 session 应该:
1. **结论先行** (绝不展示中间过程除非用户问)
2. **所有时间戳 +8h 转北京再展示**
3. **减仓必减同币种** (同方向反向操作)
4. **OKX 持仓状态以 fetch_positions() 为准**, queue.db 只是信号源
5. **contractSize 不是 1, 推送必须展示名义 $ 而非张数**
6. **59108 不一定是杠杆问题, 先查持仓再判断**