trading skills: 文档同步 (单币上限规则/平仓盈亏字段/静默规则/推送模板)

This commit is contained in:
2026-09-25 01:25:13 +08:00
parent c83161d87e
commit f46abb8ca3
111 changed files with 12151 additions and 224 deletions
@@ -0,0 +1,93 @@
# v4.5.5 Signal Timestamp via Gateway Hook (2026-08-10)
## Why this version exists
The v4.4.0 staleness pipeline (`references/signal-staleness-pipeline.md`) relied on
the forwarder's `info_filter.py` to append `⏱信号时间:` from `event.message.date`.
**Discovered dead code** (2026-09-21): the forwarder's `user_handler.py` uses
Telethon's native `client.forward_messages()` API — never invokes the FilterChain
(filter_chain logs `共 0 个过滤器`). So `is_original_time=1` in DB does literally
nothing. Time-stamped text never reaches the gateway.
## New approach: append timestamp at the gateway hook
Since the forwarder can't modify text, and switching to `send_message` would lose
the "Forwarded from X" indicator users rely on, the cleanest fix is to **append
the timestamp inside `signal_inbox.enqueue_if_signal()`** — the value stored in
`signal_queue.db.raw_text` and passed to `process_signal.py`. The text the user
sees in QQ will have the timestamp even if the TG forwarded message doesn't.
## Code (in `signal_inbox.py`)
```python
from zoneinfo import ZoneInfo
from datetime import datetime
_TZ_SH = ZoneInfo("Asia/Shanghai")
def get_now_str() -> str:
return datetime.now(_TZ_SH).strftime("%Y-%m-%d %H:%M:%S")
def append_timestamp(text: str) -> str:
"""Append ⏱信号时间: {...} at end of text. Idempotent (skips if already present)."""
if not text:
text = ""
stamp = f"\n\n⏱信号时间: {get_now_str()}"
if "⏱信号时间:" in text:
return text # already stamped
return text + stamp
async def enqueue_if_signal(source, text):
# ... chat_id check ...
text_with_ts = append_timestamp(text) # ← injects hook-receive timestamp
# ... enqueue + process_signal call using text_with_ts ...
```
## Why hook-receive time, not signal-issue time
- We have **no way to know** when the original TG signal was posted by the trader.
`forward_messages` doesn't preserve `message.date` into `event.message.date`
for the forwarder's user-side Update object in a queryable way; the bot receives
it as a `MessageService` (forwarded notice), not the original `Message`.
- The hook **receives** the message at hook execution time, which is when the agent
loop's `_handle_message_with_agent` runs — at most a few hundred ms after the TG
polling update.
- For staleness purposes (30 min window), this is **stricter** than the original
timestamp — so the v4.4.0 staleness check (signal_stale if age ≥ 30 min) still
works correctly. If the hook receives a 31-minute-old signal, it's stale.
## Idempotency guard
If the message already contains `⏱信号时间:` (forwarder appended it correctly in
the rare path where filter chain did run), `append_timestamp` skips. Prevents
double-stamping.
## Edge case: forwarder-with-info_filter somehow active
If the forwarder's `info_filter.py` ever does run (e.g. on a different deployment),
the TG forwarded message would have a `⏱信号时间: <forward_message_date>` appended.
Gateway hook sees that, skips its own stamp (idempotent), and uses the forwarder's
timestamp for staleness check — which is actually **better** (closer to true signal
age). This dual-path robustness is why the idempotency guard matters.
## Diff vs v4.4.0
| Aspect | v4.4.0 (forwarder-time) | v4.5.5 (gateway-time) |
|---|---|---|
| Where timestamp comes from | `event.message.date` (TG original) | `datetime.now()` at hook run |
| How it's appended | forwarder `info_filter.py` | `signal_inbox.append_timestamp()` |
| Survives forwarder restart | ❌ (filter chain dead code) | ✅ (hook always runs) |
| Staleness check (≥30 min) | Works when timestamp present | Always works (timestamp always present) |
## Migration
No action needed — `signal_inbox.py` updated to v4.5.5 in this commit. Forwarder
`is_original_time` config is now informational only.
## Files touched
- `~/.hermes/skills/trading/okx-auto-position/scripts/signal_inbox.py` — added
`get_now_str()`, `append_timestamp()`, called in `enqueue_if_signal()`
- No gateway changes (hook already calls `signal_inbox.enqueue_if_signal`)
- No `process_signal.py` changes (staleness check unchanged; v4.4.0 behavior
preserved when forwarder does append)