Files
trumpsignal-backend/alembic/versions/023_telegram_digest_prefs.py
T
k 3754d2caf8 feat: Telegram daily digest + System-2 manage-only refactor
Two changes ship together — both reshape the Telegram-bot surface.

──── 1. Telegram daily digest (3-section brief)
Once-a-day push covering Macro Vibes / KOL talks-vs-trades / Trump 24h.
Body is rule-based templating (no LLM); each section reads structured DB
fields and picks a phrasing. Per-user opt-out + per-user UTC hour.

  - 023 migration: TelegramBinding gains digest_enabled, digest_hour_utc,
    last_digest_sent_at (idempotent against coalesced cron / restarts).
  - New services/telegram_digest.py: build_global_digest +
    format_digest + send_daily_digest + send_preview_for.
  - Hourly cron at :00 fans out to bindings whose digest_hour_utc matches.
  - New bot commands: /digest (preview), /digest on|off, /digest_time HH.
  - HELP_TEXT + /status updated to surface the new prefs.

──── 2. System-2 manage-only refactor
The Macro Vibes (BTC bottom + funding) signal no longer auto-opens
positions. Strategy is day-K — a 24h entry delay is irrelevant — but the
auto-open path carried real execution surface (leverage clipping, daily
budget split, concurrency caps, paper branches, key handling) for ~zero
alpha. The valuable part — multi-month exit management (5-rung stop
ladder + de-risk + pyramid + peak-trail) — is preserved and runs
against positions the user adopts.

Flow: scanner fires → Telegram alert with "/adopt" CTA → user opens
manually on Hyperliquid → /adopt picks the position via inline keyboard
→ picks Standard or Aggressive mode → bot creates BotTrade + registers
watchdog. Escape hatch: /release marks released_at, unregisters
watchdog, leaves the HL position open under user control.

  - 024 migration: BotTrade.released_at — "user took back control" marker.
  - bot_engine.process_post early-returns for sys2 (no auto-open path).
  - New services/adoption.py: list_hl_positions + adopt_position +
    release_management + AdoptionError. Per-wallet asyncio lock prevents
    dual-adopt race. Pre-flight checks: no_subscription / no_hl_key /
    paper_mode / circuit_breaker / already_adopted / concurrency_cap.
    Protective stop + de-risk + addon + peak-trail ladders all built
    against the ACTUAL HL leverage so the "inside liquidation" guarantee
    holds.
  - New API: GET /positions/hl/{wallet}, POST /positions/adopt,
    POST /positions/{id}/release (all signed).
  - telegram.py: send_message supports reply_markup; new edit_message +
    answer_callback for the inline-keyboard pickers; sys2 alert format
    now ends with the "/adopt" CTA.
  - telegram_bot.py: /adopt + /release commands with picker → confirm
    → execute flow via inline keyboards. New _handle_callback dispatches
    on "adopt:*" / "release:*" callback_data; run_bot_loop now consumes
    callback_query updates alongside messages.
  - recovery.py + reconciler.py skip released_at IS NOT NULL rows so a
    restart doesn't silently re-attach the watchdog to a released trade.
  - /positions/open, /positions/today, and telegram_digest's user-state
    line all filter released_at so they don't lie about what the bot is
    actually managing.

Tests: 26 new (8 digest snapshot + 14 adoption + 4 absorbed via existing
suites). All 64 pass.

Deploy: alembic upgrade head (runs 023 + 024) → restart backend. Existing
TelegramBindings get digest_enabled=true / digest_hour_utc=12 via server
defaults. In-flight auto-opened System-2 positions continue to be managed
(recovery rehydrates them) — no in-flight trade is abandoned.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-26 12:53:16 +08:00

54 lines
1.9 KiB
Python
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.
"""Telegram per-user digest preferences.
Adds three columns to telegram_bindings so each subscriber can opt in/out
of the daily three-section brief (Macro Vibes / KOL / Trump) and pick
which UTC hour it lands.
digest_enabled : bool, default True. Toggleable via /digest on|off.
digest_hour_utc : int 023, default 12. Toggleable via /digest_time HH.
12 UTC ≈ Asia evening / EU afternoon / US morning —
the only single hour that's reasonable for all three
big crypto-active zones, so it's our default.
last_digest_sent_at : nullable timestamp. Guards against double-send if the
cron coalesces or the worker restarts mid-firing.
Used as "skip if < 23h ago" in send_daily_digest.
Backfills are safe: every existing binding gets digest_enabled=True so users
who subscribed before this feature start receiving the brief automatically.
If they don't want it, one /digest off keeps them off.
Revision ID: 023
Revises: 022
Create Date: 2026-05-26
"""
from alembic import op
import sqlalchemy as sa
revision = "023"
down_revision = "022"
branch_labels = None
depends_on = None
def upgrade() -> None:
with op.batch_alter_table("telegram_bindings") as batch:
batch.add_column(sa.Column(
"digest_enabled", sa.Boolean, nullable=False,
server_default=sa.true(),
))
batch.add_column(sa.Column(
"digest_hour_utc", sa.Integer, nullable=False,
server_default="12",
))
batch.add_column(sa.Column(
"last_digest_sent_at", sa.DateTime, nullable=True,
))
def downgrade() -> None:
with op.batch_alter_table("telegram_bindings") as batch:
batch.drop_column("last_digest_sent_at")
batch.drop_column("digest_hour_utc")
batch.drop_column("digest_enabled")