audit: scheduler cleanup, WS-log secret hygiene, simulator test coverage, CLAUDE.md sync (Batch G)
2026-08-04 Fable 5 QA audit, final code batch. - Boundary math: removed the provably-dead `remaining <= buffer` guard from all three scheduler copies (518,400-instant frozen-clock sweep: never fired) and documented the exact-boundary-instant semantics. - websockets/websockets.client loggers pinned to INFO so a DEBUG run can never write the TD key (connect URI) or Tiingo auth frame into the 30-day rotating log. (.env also chmod 600, untracked.) - Cooldown pass factored into _apply_cooldown (pure, testable) — the only sequential replay rule finally has direct tests. - New simulator behavior tests (previously ZERO coverage of the trade resolver): same-bar SL+TP tie → SL-first, gap-through-SL fills at the open, TIMEOUT pricing + round-trip spread charging, trailing ratchet arms at bar close and fills next-bar-only, cooldown ordering. - CLAUDE.md synced to verified reality: 338 tests/21 modules; three weekend-implementation copies caveat; throttle claim corrected (single-flight, TD-only throttle); daemon trail/cooldown parity caveats (once-per-OBSERVED-bar, wall-clock cooldown) + OPEN_ONLY and reconcile notes; CP-path gate semantics documented as validated behavior; new "known open items" section for the owner-decision findings (weekend DST constant, spread-in-trigger rule-6 item, H4 seeding divergence, FB2_EMA_LO). Gates: suite 338/338 green; pinned M15 book and M5 HEAD baseline both reproduced identically after the cooldown refactor. Claude-Session: https://claude.ai/code/session_01UnZkzeFcTu9agFg8BDh2tn
This commit is contained in:
+11
-2
@@ -103,6 +103,12 @@ logging.basicConfig(level=getattr(logging, _APP.log_level, logging.INFO),
|
||||
force=True)
|
||||
logging.getLogger("httpx").setLevel(logging.WARNING)
|
||||
logging.getLogger("httpcore").setLevel(logging.WARNING)
|
||||
# Never let DEBUG runs write credentials into the 30-day rotating log:
|
||||
# the websockets client logger emits the connect URI (Twelve Data puts
|
||||
# the API key in the query string) and frame contents (Tiingo's auth
|
||||
# frame carries the key) — 2026-08-04 audit, finding K47.
|
||||
logging.getLogger("websockets").setLevel(logging.INFO)
|
||||
logging.getLogger("websockets.client").setLevel(logging.INFO)
|
||||
logger = logging.getLogger("telegram_bot")
|
||||
|
||||
TOKEN = telegram_token()
|
||||
@@ -667,8 +673,11 @@ def _seconds_until_boundary(interval_minutes: int, buffer: int = 10) -> float:
|
||||
elapsed_seconds = elapsed_minutes * 60 + now.second + now.microsecond / 1e6
|
||||
interval_seconds = interval_minutes * 60
|
||||
remaining = interval_seconds - elapsed_seconds + buffer
|
||||
if remaining <= buffer:
|
||||
remaining += interval_seconds
|
||||
# NOTE: remaining > buffer always (elapsed < interval by construction);
|
||||
# a call at the exact boundary instant targets the NEXT boundary —
|
||||
# steady-state loops wake at boundary+buffer, so only a process
|
||||
# started in the boundary microsecond skips one scan (2026-08-04
|
||||
# audit I42; the old `remaining <= buffer` guard was unreachable).
|
||||
return remaining
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user