Day-extension entry guard + percentage breakeven stop
Day-extension guard (the H1 gap from the prior review): the trigger-extension ceiling only sees distance above the ORB high, so a name up huge intraday whose break is itself CLEAN slips through — real example ATPC, entered +1.7% above the trigger yet +32% above the session open, now a loser. New guard blocks when the entry is > day_extension_max_pct above the SESSION OPEN. Default 0.30 calibrated on 22 real fires: normal entries are <=14% above the open, the parabolic tail is 20-83% (ICCM +83/+41, BIRD +36, ATPC +34/+32, NEOV +29). day_open is computed in step_arm (first RTH bar open), carried on ArmedTrigger, checked in submit_breakout_now. Env TRADING_DAY_EXTENSION_MAX_PCT; 0 = off. Percentage breakeven (the "ratchet to a better stop once it works" request): the broker-side stop ratchet already exists (exit_manager replace_order), but its earliest move was +1R — a big move for a wide opening-range stop. breakeven_at_pct moves the stop to entry once price is +this fraction above entry, BEFORE 1R, so a pop that reverses can't round-trip to a loss (the documented small-cap failure). Lives in the shared strategy/exits._trail_stop (backtest + live agree), only ever ratchets up. Default 0.05 live (TRADING_EXIT_BREAKEVEN_PCT); _is_active updated so the manager runs when it's the only rule set. tests/unit: 160 passed (+ day-ext block/allow/no-open, + pct-breakeven move/hold/ saves-reverser). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
87186be949
commit
559083b4ab
7 files changed
+98
-2
No files matched your search
@@ -143,6 +143,14 @@ class ExecutionSettings:
|
||||
# ICCM @64%/26%, BIRD @30%). 15% sits in that dead zone — it blocks the
|
||||
# spike-top chases and passes every normal breakout.
|
||||
extension_max_pct: float = 0.15
|
||||
# Day-extension ceiling: block when the entry is more than this fraction ABOVE
|
||||
# the SESSION OPEN — i.e. the stock is already parabolic on the day. This
|
||||
# catches what extension_max_pct can't: a name up huge intraday whose ORB-high
|
||||
# break is itself clean (small trigger-extension) but is a chase of a parabolic
|
||||
# (e.g. ATPC entered +1.7% above trigger yet +32% above the open, and lost).
|
||||
# Default 0.30 calibrated on real fires: normal entries are <=14% above the
|
||||
# open, the parabolic tail is 20-83%. 0 = off. Needs day_open from the trigger.
|
||||
day_extension_max_pct: float = 0.30
|
||||
# Skip entries on a name that looks HALTED: in a trading halt you can't exit
|
||||
# and it gaps through your stop on resume. The caller passes a per-symbol
|
||||
# halt flag (a bar-gap heuristic over the liquid gappers we trade). This flag
|
||||
@@ -207,6 +215,9 @@ class ExecutionSettings:
|
||||
extension_max_pct=float(
|
||||
values.get("TRADING_EXTENSION_MAX_PCT", "0.15")
|
||||
),
|
||||
day_extension_max_pct=float(
|
||||
values.get("TRADING_DAY_EXTENSION_MAX_PCT", "0.30")
|
||||
),
|
||||
halt_guard_enabled=flag("TRADING_HALT_GUARD_ENABLED", "1"),
|
||||
halt_max_bar_gap_min=float(
|
||||
values.get("TRADING_HALT_MAX_BAR_GAP_MIN", "3")
|
||||
@@ -670,6 +681,7 @@ class TradingExecutionService:
|
||||
reason: str = "orb_live_break",
|
||||
cum_volume: float = 0.0,
|
||||
halted: bool = False,
|
||||
day_open: float | None = None,
|
||||
) -> dict:
|
||||
"""Fire a breakout entry immediately on a LIVE price cross.
|
||||
|
||||
@@ -720,6 +732,13 @@ class TradingExecutionService:
|
||||
ext_max = self.settings.extension_max_pct
|
||||
if ext_max > 0 and entry_ref > trigger_f * (1.0 + ext_max):
|
||||
return {"ok": False, "skipped": "over_extended"}
|
||||
# Day-extension ceiling: the stock is already parabolic on the DAY (far
|
||||
# above the session open) — chasing that is the "+100% then halts down"
|
||||
# trap even when the trigger break itself looks clean.
|
||||
day_max = self.settings.day_extension_max_pct
|
||||
if (day_max > 0 and day_open is not None and float(day_open) > 0
|
||||
and entry_ref > float(day_open) * (1.0 + day_max)):
|
||||
return {"ok": False, "skipped": "over_extended_day"}
|
||||
# Halt guard: you can't exit during a trading halt and it gaps through the
|
||||
# stop on resume — don't enter a name the caller flagged as halted.
|
||||
if halted and self.settings.halt_guard_enabled:
|
||||
|
||||
Reference in new issue
Block a user