trading skills: 文档同步 (单币上限规则/平仓盈亏字段/静默规则/推送模板)
This commit is contained in:
@@ -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)
|
||||
Reference in New Issue
Block a user