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>
1. hyperliquid.open_position now returns effective_leverage. HL clips
the requested leverage to each asset's max (e.g. memes capped at 3×)
and silently applied the lower value; we were discarding that. For
System-2 this meant sys2_protective_stop_pct was computed against the
REQUESTED leverage, so the "inside-liquidation" full-close rung was
actually well inside an 8.5% stop while the real liquidation sat ~33%
away — strategy intent broken. bot_engine now reads the effective
value back, recomputes the protective stop + derisk ladder against
it, and freezes the correct values into BotTrade.eff_* / .leverage.
2. circuit_breaker.clear_trip now clears BOTH systems by default. The
sys2 column was never reachable from any reset path — a sys2 trip
forced users to wait the full 24h lockout even after explicit
human re-arm.
3. /api/user/.../manual-window and /auto-trade re-arm endpoints now
clear sys1 AND sys2 CB on the same human ack. Symmetry with
check_and_trip / is_tripped which already supported both systems.
4. telegram.py deep-link map: btc_bottom_reversal + funding_reversal
alerts now point at /en/macro instead of the now-404 /en/btc.
5. (Already landed: _sys2_mode UnboundLocalError fix in bot_engine —
restated here for the audit trail.)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
New backend pipeline: 8 free public macro signals fetched in parallel,
upserted once per calendar day, served via /api/macro/{snapshot,history}.
- AHR999 (computed from Binance 200d klines)
- Altcoin Season Index (CoinGecko top-50 30d)
- Fear & Greed (alternative.me)
- BTC dominance, ETH/BTC ratio
- Stablecoin supply (DeFiLlama)
- Spot BTC ETF net flow (Farside)
- BTC perp open interest (Binance fapi)
Each fetcher is independently @_none_on_fail decorated so one outage
can't take down the snapshot; scoring renormalises across whichever
indicators returned a value. Daily cron at 03:00 UTC; on startup a
fire-and-forget bootstrap fills today's row if missing.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Three plumbing fixes + one ops doc that close the gaps from the audit.
scripts/rescore_v5.py
Was overwriting only signal/conf/reasoning/sentiment/relevant/
prefilter_reason/analysis_version. Now also persists target_asset,
category, expected_move_pct — without these the bot can't route
rescored posts correctly (would silently fall back to BTC).
app/schemas.py + app/api/posts.py
TrumpPost response model didn't expose target_asset/category/
expected_move_pct, so the frontend had no way to display "this
signal will trade SOL". Added the three fields + mapping in
_post_to_schema(). Pre-v5 posts return null. No frontend changes
yet — display work is a follow-up.
app/services/hyperliquid.py
HL caps max leverage per asset (BTC/ETH 50×, SOL 20×, memes 3-5×).
set_leverage() always tried to push self._leverage — if user set
30× and bot routed to TRUMP, HL rejected the order and the trade
silently dropped. Added _get_max_leverage() (queries meta()'s
maxLeverage field) and _clip_leverage() that caps to HL's max.
set_leverage now returns the effective leverage so callers can
use it for notional sizing if needed.
deploy/ENCRYPTION_KEY_BACKUP.md
Documented mandatory backup procedure for the symmetric key that
encrypts every user's HL API key. Lost key = all users' bots dead
with no recovery. Includes rotation procedure + quarterly test
step + things-not-to-do list.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- New migrations for daily_budget, active_window, and bottrade snapshot
- Add trumpstruth scraper and price_impact_monitor service
- Expand bot_engine, hyperliquid, recovery, and tp_sl_monitor logic
- Update API/schemas/models for new features
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>