Feed-health pass over KOL_FEEDS:
- raoulpal: stale Substack (last 2024-05) → Real Vision podcast feed
- dampedspring: paywalled (0 entries) → free "Damped Spring 101" Substack
- unchained: Cloudflare 403 → canonical Megaphone podcast feed
- lynalden: Cloudflare 202 → FeedBurner mirror
- glassnode: recovered via httpx http2=True (was 403 on HTTP/1.1)
- browser User-Agent + Accept headers on feed fetch
- removed dead feeds with no active replacement: placeholder,
dragonfly, niccarter, eugene
- pin h2==4.3.0 (required by http2=True)
All 25 remaining feeds verified fetching real body content; newest
post per feed ≤88d. Bundles in-flight KOL-module work already in the
working tree (kol_x ingest, migration 027, tests).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Three pre-launch audit findings:
launch_seed.py — destructive cleanup is now OPT-IN
Previously `launch_seed.py --yes` truncated KOL history (divergence /
holdings / posts older than 30d) by default — an accidental run erased
unrecoverable data, and abort_if_live_user_state() only guards on
subscriptions/bindings, not KOL history. Now nothing is deleted unless
--wipe is passed; the safe path is --seed-only (pure fetch). Bare/--yes
without --wipe refuses and prints guidance.
/api/health/deep — monitor the price feeds, not just scrapers
The deep healthcheck only watched the (redundant) Trump scrapers + DB, so a
dead Binance/HL price feed — which silently stops ALL tp_sl_monitor stop-loss
/ take-profit firing on live trades — left health green. Added per-feed
liveness (binance.last_tick_at, hl_price_feed.last_tick_at) with a 180s boot
grace so startup doesn't false-503. Body now includes price_feeds[].
Single-process enforcement (multi-worker safety)
The backend is single-process by design (in-memory scheduler, replay cache,
tp_sl table, price_store). systemd unit lacked the --workers 1 + rationale
that supervisor.conf already had; added it. Added a runtime advisory file
lock (app.main._acquire_singleton_lock): only the leader starts background
tasks; extra workers serve HTTP reads only and log CRITICAL. health/deep now
reports is_leader so the misconfig is visible to monitors.
72 tests pass.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The frontend proxy fix alone was incomplete. Backend slowapi used the default
get_remote_address (request.client.host), which is the proxy's IP because
uvicorn runs without --proxy-headers — so the relayed x-forwarded-for was
ignored and all users still shared one rate-limit bucket.
- Add app/ratelimit.py: shared `limiter` + `client_ip_key` that reads
x-forwarded-for[0] → x-real-ip → peer. Replaces the three independent
Limiter(get_remote_address) instances in main.py / posts.py / prices.py
(which also had separate, non-shared storage).
- Register SlowAPIMiddleware so default_limits ("60/minute") applies to EVERY
route. Previously only the 2 decorated read endpoints were limited; all
signed-mutation routes had no rate limit at all (the "20/min per-route"
comment was aspirational — no such decorator existed).
- Add tests/test_ratelimit.py (7 tests): XFF precedence, fallbacks, two users
behind one proxy get distinct keys, middleware-registered guard.
72 tests pass.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Batch of the pre-launch audit campaign (BUG-01…14 plus three new features):
Pricing / TP-SL protection
- Add app/services/hl_price_feed.py: supplemental HL allMids poller for
HL-native assets (HYPE, PURR) not listed on Binance. Pumps price_store +
tp_sl_monitor.on_price_tick so bot trades on these assets keep full
stop-loss / take-profit / trailing protection instead of max-hold only.
- Wire feed into main.py lifespan (startup task + graceful shutdown cancel).
Telegram
- Add format_trump_mention + PATH B in _dispatch: crypto-relevant Trump
posts with no directional signal (relevant=True, signal=hold) now alert
the public channel only (no per-subscriber noise).
- Rate limiter (slowapi) on the API; assorted bot/digest fixes.
KOL on-chain
- seed_kol_wallets.py: KOL_FEEDS coverage cross-check; reversibly deactivate
orphaned wallets (handle not in KOL_FEEDS → can never produce divergence)
so the scanner stops burning cycles on them.
Tests / misc
- Fix brittle test_macro_ahr999_uses_same_formula_as_scanner: mock now uses
realistic ms timestamps so the in-progress-day drop fires, matching the
fetcher's bar count (was 0.3179 vs 0.3178 off-by-one).
- Refresh stale notify_signal comment in truth_social.py.
Frontend reduce-action type fix lives in the sibling repo.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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>
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>
next_run_time=None means "paused", not "default". The fallback poller was
registered but never triggered, leaving the system on CNN archive only.
Set explicit start time = now + half the poll interval so the two pollers
offset and don't hit upstream simultaneously.
Verified via /api/health/deep: trumpstruth.last_poll was null on the live
server before this fix despite the poller having been deployed.
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>