/** * Materiality + cooldown gate for "Stop raised" (notifications 2.6) — Gate 4. * * The three existing gates (RaiseCorrelator, StopRaiseRatchet, persisted * announced-floor) all answer "is this a REAL raise?". On a trending winner every * 30s re-quote IS a real raise (~0.15% price / ~0.75% ROE per step on the reported * $SPCX 5× case), so every one of them notified: 4 pushes in 4 minutes, worst case * ~120/hour on a single winning position. This gate answers the question none of * them asked: "is it worth a push?" * * The ticket's whitelist is literal — "only emit a push on a material stop * event": Phase-1 armed, breakeven cross, tier upgrade, or a ≥threshold move, * plus a per-position cooldown/rate-limit. Armed and tier upgrades have their * own subtypes (DSL_ACTIVATED / DSL_PROFIT_LOCK), so this gate announces a * genuine raise in exactly two cases: * * 1. BREAKEVEN CROSS — the stop crosses entry (ROE-at-stop turns positive): * "you can no longer lose on this trade". Fires once per instance * (latched), immediately — it is its own whitelist entry, so the cooldown * does not apply to it. * 2. MATERIAL STEP, RATE-LIMITED — ROE-at-stop improved by * ≥ DSL_STOP_RAISE_MIN_ROE_STEP_PCT (default 10 ROE points ≈ "another 10% of * margin locked" — Jason's number, 2026-08-13) since the last announced floor, AND * ≥ DSL_STOP_RAISE_COOLDOWN_S (default 900s) since the last announcement. * The threshold defines material; the cooldown hard-caps the rate at one * trail push per position per window (worst case 4/hour). ROE-based so * leverage does not change the cadence; price-% fallback when * entry/leverage are unavailable. * * Everything else stays silent — including the first raise of a fresh instance * (the armed notification already covered "a stop exists") and sub-threshold * drift, however long it accumulates. Batching is free: a push announces the * CURRENT floor, so every suppressed micro-step is summarized by the next * announcement. * * Suppression NEVER advances the announced baseline — only announcements do * (mirroring the stamp-on-send semantics of the persisted floor store). That is * what makes accumulated micro-steps eventually cross the threshold. * * The gate is ON by default; DSL_STOP_RAISE_DEBOUNCE is the kill switch, and it * accepts any of false/0/off/no/disabled (case- and space-insensitive) — see * {@link KILL_SWITCH_OFF_VALUES}. Flipping per-agent env on the fleet is * operationally painful and the storm is a live prod bug, so the useful * direction for a flag here is emergency-off. With the switch off, `evaluate` * is never called and behaviour is byte-identical to pre-2.6. Env is read * call-time so tests and fleet config can flip it without re-imports. */ /** Call-time env config (mirrors the whyLine/enrichment convention). */ export declare function debounceConfig(): { enabled: boolean; minRoeStepPct: number; minPriceStepPct: number; cooldownMs: number; }; /** Announced-state snapshot persisted alongside the floor (see floor-store). */ export interface NotifiedStopMeta { floor?: number; atMs?: number; breakevenAnnounced?: boolean; } export type AnnounceReason = "breakeven" | "material-step"; export interface DebounceVerdict { announce: boolean; reason: AnnounceReason | null; /** Human-readable suppression detail for the debug log. */ suppressDetail?: string; /** Latch value to stamp with the announcement (true once the stop clears entry). */ breakevenAnnounced: boolean; } export interface StopRaiseDebounceOptions { now?: () => number; ttlMs?: number; maxEntries?: number; } /** * In-memory announced-state per position instance (same keying, TTL and LRU * discipline as StopRaiseRatchet). The persisted meta, when available, seeds a * missing entry so restarts/rebuilds do not reset the cadence. */ export declare class StopRaiseDebounce { private readonly entries; private readonly now; private readonly ttlMs; private readonly maxEntries; constructor(opts?: StopRaiseDebounceOptions); /** * Judge a genuine raise (gates 1–3 already passed) for materiality. Does NOT * mutate the announced baseline — call {@link recordAnnounced} after actually * sending. `persisted` (when the caller has a store) seeds a missing in-memory * entry so the cadence survives restarts. */ evaluate(key: string, ev: { newSLPrice: number; direction: "LONG" | "SHORT"; entryPrice?: number; leverage?: number; }, persisted: NotifiedStopMeta | undefined): DebounceVerdict; /** * Commit an announcement: advance the announced floor/timestamp and the * breakeven latch. Also called for "Profit locked" announcements — a tier * upgrade announces the same real-world progress, so it resets the trail * cooldown (and a lock > 0 implies the stop cleared entry). */ recordAnnounced(key: string, floor: number, breakevenAnnounced: boolean): void; /** Test/observability helper. */ size(): number; /** Refresh recency; drop an idle-expired entry. Returns the live entry, if any. */ private touch; private write; } /** Shared logger handle so the caller's suppress log matches the module family. */ export declare const debounceLogger: import("../../utils/logger.js").Logger; //# sourceMappingURL=stop-raise-debounce.d.ts.map