Files
trumpsignal-backend/tests/test_bottom_reversal_strategy.py
k 5fb1d52026 Pre-launch hardening: KOL module, Telegram, scanners, WS resilience
Big-picture changes since b941223:

KOL pipeline (new) — Substack/podcast/blog RSS → AI ticker extraction →
on-chain wallet diff → talks-vs-trades divergence detection. Daily polls,
19 feeds, divergence emits Post + Telegram fan-out.

Telegram push (new) — walletless free tier + wallet-linked Pro upgrade,
in-bot preference commands (/trump /btc /funding /kol /conf /quiet),
signed-envelope API for dashboard. Disconnect-wallet keeps free
subscription.

BTC funding-rate reversal scanner (new) — hourly cron, 30d cumulative
funding threshold + mean-revert + 7d price confirm, emits via
/api/signals/ingest. BTC bottom-reversal scanner promoted to System 2.

WS broadcast rewrite — per-client send timeout + parallel fan-out
(asyncio.gather). Fixes "Binance WS no close frame" reconnect storms +
APScheduler 11-min job misses, both caused by one slow client stalling
the kline loop.

Error visibility — three silent-error sites (trumpstruth/truth_social
fetchers, funding_reversal scanner) now include exception type name so
httpx ConnectError-style empty-message errors stop logging blank lines.
Telegram bot loop now classifies ReadTimeout vs network vs unknown +
logger.exception for the unknown bucket.

Security hygiene — trumpsignal.db untracked from git (held subscriber
wallets + encrypted HL keys + 22 bot trades); .gitignore now blocks
*.db/.next/backups. CORS only allows FRONTEND_URL in production.

New ops scripts —
  - scripts/preflight.py: env/DB/Telegram/AI auth verification gate
  - scripts/backup_db.sh: cron-friendly daily DB backup (SQLite + Postgres)
  - scripts/seed_kol_wallets.py: idempotent KOL on-chain wallet seeder

15 new Alembic migrations (007-021) covering convex strategy fields,
phase-1 safety, two-system frozen exits, invalidation prices, dynamic
SYS2 leverage, staged de-risk + pyramiding, peak gain tracking, risk
mode, auto-trade + grow flags, KOL module, KOL on-chain, KOL divergence,
Telegram bindings + walletless.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 00:52:56 +08:00

96 lines
3.0 KiB
Python

from app.services.scanners.funding_reversal import evaluate_funding_reversal
from app.services.scanners.sma_reclaim import evaluate_sma_reclaim
from app.services.signal_categories import (
is_system_2,
is_supported_trading_source,
system2_display_name,
system2_min_confidence,
system2_size_multiplier,
)
from app.services.bot_engine import _confidence_floor_for, _should_apply_schedule
from app.services.tp_sl_monitor import register_trade, _watched
def _daily(close: float, volume: float = 100.0) -> dict:
return {"close": close, "volume": volume}
def test_sma_reclaim_no_longer_emits_short_breakdowns():
candles = [_daily(110.0) for _ in range(200)]
candles.extend(_daily(110.0) for _ in range(31))
candles.append(_daily(90.0, volume=200.0))
is_signal, debug = evaluate_sma_reclaim(candles)
assert is_signal is False
assert debug["reason"] == "shorts_disabled"
def test_funding_reversal_reports_boost_without_standalone_signal():
funding = [
{"time_ms": i * 3_600_000, "rate": -0.00005}
for i in range(24 * 29)
]
funding.extend(
{"time_ms": (24 * 29 + i) * 3_600_000, "rate": -0.00001}
for i in range(24)
)
candles = [_daily(100.0) for _ in range(8)]
candles[-1] = _daily(104.0)
is_signal, debug = evaluate_funding_reversal(funding, candles)
assert is_signal is True
assert debug["direction"] == "buy"
def test_bottom_reversal_source_routes_to_system_2():
assert is_system_2("btc_bottom_reversal")
def test_old_scanner_sources_no_longer_route_to_module_2():
for source in ("rsi_reversal", "sma_reclaim", "funding_reversal", "breakout", "vcp_breakout"):
assert not is_system_2(source)
def test_unknown_external_sources_are_not_supported_for_trading():
assert is_supported_trading_source("truth")
assert is_supported_trading_source("btc_bottom_reversal")
assert not is_supported_trading_source("manual")
assert not is_supported_trading_source("sma_reclaim")
def test_module_2_uses_own_confidence_floor_and_bypasses_schedule():
sys2_sub = {"_is_system_2": True, "min_confidence": 95}
sys1_sub = {"_is_system_2": False, "min_confidence": 95}
assert system2_display_name() == "Bitcoin Bottom"
assert _confidence_floor_for(sys2_sub) == system2_min_confidence()
assert _confidence_floor_for(sys1_sub) == 95
assert _should_apply_schedule(sys2_sub) is False
assert _should_apply_schedule(sys1_sub) is True
def test_register_trade_keeps_invalidation_price():
register_trade(
trade_id=99991,
wallet="0xabc",
api_key="key",
leverage=3,
asset="BTC",
side="long",
entry_price=100.0,
take_profit_pct=None,
stop_loss_pct=6.0,
trailing_stop_pct=5.0,
trailing_activate_at_pct=12.0,
invalidation="below_entry",
invalidation_price=92.5,
min_hold_until_ts=None,
)
try:
wt = _watched[99991]
assert wt.invalidation_price == 92.5
finally:
_watched.pop(99991, None)