# Long Exit State Machine — Transition Reference

> Split from `long_entry_state_machine_v1_2_15.md` on 2026-03-24. Contains active-position management states 09–11 (Yellow / Green / Red).

> Source: `long_entry_state_machine_v1_2_15.md` v1.2.15 — states 09–11

> **Version:** v1.5.4 | **Updated:** 2026-04-20 | **Change:** Align all long exit transition IDs to `long_strategy_v1_13_3.py` canonical map (shared COUNT/EXIT IDs, OC/EPTC, session/hard-stop, and premarket exits).

<!-- Site: Pirate Strategies | Version: v1.5.4 | Updated: 2026-04-20 | Project: 20260420_TWM_long_hand_calc_comp -->

---

## Entry Point

This state machine is entered from the entry state machine when a position is opened:
- `7.04 TRADE_ENTRY_SUBMITTED` (t_110_trade_entry_submitted) → `long_09_YELLOW` (initial active state)
- `8.04 ENTRY_BLOCK_CLEARED` (t_120_entry_block_cleared) → `long_09_YELLOW` (after trading-prohibited hold)

See [`long_entry_state_machine_v2_7_0.md`](long_entry_state_machine_v2_7_0.md) for states 00�08 (entry setup path).

---

## States

- [**09 — long_09_YELLOW**](#long_09_yellow-initial-active-state)
  - [[XSE]](#xse-exit_session_close) EXIT_SESSION_CLOSE t_220_exit_session_close (220) → [`00 — long_00_IDLE`](long_entry_state_machine_v2_7_0.md#long_00_IDLE)
  - [[HS_E]](#hse-exit_hard_stop) EXIT_HARD_STOP t_221_exit_hard_stop (221) → `long_01_WAIT_BEAR_TREND` *(immediate exit — hard stop violation)*
  - [[HS_S]](#hss-count_stop_loss_soft) COUNT_STOP_LOSS_SOFT t_210_count_stop_loss_soft (210) → `long_09_YELLOW` *(self — increments exit_counter × 2, soft stop only)*
      - [[PEM]](#pem-count_ma) COUNT_MA t_211_count_ma (211) → `long_09_YELLOW` *(self — increments exit_counter × 2)*
                  - [[XMA]](#xma-exit_ma) EXIT_MA t_222_exit_ma (222) → [`01 — long_01_WAIT_BEAR_TREND`](long_entry_state_machine_v2_7_0.md#long_01_wait_bear_trend) *(immediate exit — bar close only)*
  - [[PEL]](#pel-count_lwr) COUNT_LWR t_212_count_lwr (212) → `long_09_YELLOW` *(self — increments exit_counter × 2)*
      - [[XLR]](#xlr-exit_lwr) EXIT_LWR t_223_exit_lwr (223) → [`01 — long_01_WAIT_BEAR_TREND`](long_entry_state_machine_v2_7_0.md#long_01_wait_bear_trend) *(immediate exit — bar close only)*
  - 9.05) [YELLOW_TO_GREEN](#t_240_yellow_to_green) (240) → [`10 — long_10_GREEN`](#long_10_green)
  - 9.06) [YELLOW_TO_RED](#t_241_yellow_to_red) (241) → [`11 — long_11_RED`](#long_11_red)
      - 9.07) [[PM]](#pm-exit_premarket_s09) EXIT_PREMARKET_S09 t_224_exit_premarket_s09 (224) → [`01 — long_01_WAIT_BEAR_TREND`](long_entry_state_machine_v2_7_0.md#long_01_wait_bear_trend) *(immediate market-sell exit — bar close only)*
  - [[OC]](#oc-offset_change) OFFSET_CHANGE t_230_offset_change (230) → `long_09_YELLOW` *(self — advances range_idx, sets type2_profit_limit)*
  - [[EPTC]](#eptc-exit_profit_target_count) EXIT_Profit_Target_Count t_226_exit_profit_target_count (226) → [`01 — long_01_WAIT_BEAR_TREND`](long_entry_state_machine_v2_7_0.md#long_01_wait_bear_trend)

- [**10 — long_10_GREEN**](#long_10_green)
  - [[XSE]](#xse-exit_session_close) EXIT_SESSION_CLOSE t_220_exit_session_close (220) → [`00 — long_00_IDLE`](long_entry_state_machine_v2_7_0.md#long_00_IDLE)
  - [[HS_E]](#hse-exit_hard_stop) EXIT_HARD_STOP t_221_exit_hard_stop (221) → `long_01_WAIT_BEAR_TREND` *(immediate exit — hard stop violation)*
  - [[HS_S]](#hss-count_stop_loss_soft) COUNT_STOP_LOSS_SOFT t_210_count_stop_loss_soft (210) → `long_10_GREEN` *(self — increments exit_counter × 1, soft stop only)*
      - [[PEM]](#pem-count_ma) COUNT_MA t_211_count_ma (211) → `long_10_GREEN` *(self — increments exit_counter × 1)*
                  - [[XMA]](#xma-exit_ma) EXIT_MA t_222_exit_ma (222) → [`01 — long_01_WAIT_BEAR_TREND`](long_entry_state_machine_v2_7_0.md#long_01_wait_bear_trend) *(immediate exit — bar close only)*
  - [[PEL]](#pel-count_lwr) COUNT_LWR t_212_count_lwr (212) → `long_10_GREEN` *(self — increments exit_counter × 1)*
      - [[XLR]](#xlr-exit_lwr) EXIT_LWR t_223_exit_lwr (223) → [`01 — long_01_WAIT_BEAR_TREND`](long_entry_state_machine_v2_7_0.md#long_01_wait_bear_trend) *(immediate exit — bar close only)*
  - 10.05) [GREEN_TO_YELLOW](#t_250_green_to_yellow) (250) → [`09 — long_09_YELLOW`](#long_09_yellow-initial-active-state)
  - [[OC]](#oc-offset_change) OFFSET_CHANGE t_230_offset_change (230) → `long_10_GREEN` *(self — advances range_idx, sets type2_profit_limit)*
  - [[EPTC]](#eptc-exit_profit_target_count) EXIT_Profit_Target_Count t_226_exit_profit_target_count (226) → [`01 — long_01_WAIT_BEAR_TREND`](long_entry_state_machine_v2_7_0.md#long_01_wait_bear_trend)

- [**11 — long_11_RED**](#long_11_red)
  - [[XSE]](#xse-exit_session_close) EXIT_SESSION_CLOSE t_220_exit_session_close (220) → [`00 — long_00_IDLE`](long_entry_state_machine_v2_7_0.md#long_00_IDLE)
  - [[HS_E]](#hse-exit_hard_stop) EXIT_HARD_STOP t_221_exit_hard_stop (221) → `long_01_WAIT_BEAR_TREND` *(immediate exit — hard stop violation)*
  - [[HS_S]](#hss-count_stop_loss_soft) COUNT_STOP_LOSS_SOFT t_210_count_stop_loss_soft (210) → `long_11_RED` *(self — increments exit_counter × 3, soft stop only)*
      - [[PEM]](#pem-count_ma) COUNT_MA t_211_count_ma (211) → `long_11_RED` *(self — increments exit_counter × 3)*
                  - [[XMA]](#xma-exit_ma) EXIT_MA t_222_exit_ma (222) → [`01 — long_01_WAIT_BEAR_TREND`](long_entry_state_machine_v2_7_0.md#long_01_wait_bear_trend) *(immediate exit — bar close only)*
  - [[PEL]](#pel-count_lwr) COUNT_LWR t_212_count_lwr (212) → `long_11_RED` *(self — increments exit_counter × 3)*
      - [[XLR]](#xlr-exit_lwr) EXIT_LWR t_223_exit_lwr (223) → [`01 — long_01_WAIT_BEAR_TREND`](long_entry_state_machine_v2_7_0.md#long_01_wait_bear_trend) *(immediate exit — bar close only)*
      - 11.06) [[PM]](#pm-exit_premarket_s11) EXIT_PREMARKET_S11 t_225_exit_premarket_s11 (225) → [`01 — long_01_WAIT_BEAR_TREND`](long_entry_state_machine_v2_7_0.md#long_01_wait_bear_trend) *(immediate market-sell exit — bar close only)*
  - 11.07) [RED_TO_YELLOW](#t_262_red_to_yellow) (262) → [`09 — long_09_YELLOW`](#long_09_yellow-initial-active-state)
  - [[OC]](#oc-offset_change) OFFSET_CHANGE t_230_offset_change (230) → `long_11_RED` *(self — advances range_idx, sets type2_profit_limit)*
  - [[EPTC]](#eptc-exit_profit_target_count) EXIT_Profit_Target_Count t_226_exit_profit_target_count (226) → [`01 — long_01_WAIT_BEAR_TREND`](long_entry_state_machine_v2_7_0.md#long_01_wait_bear_trend)

---

## Intra-bar Evaluation Model

The exit state machine inherits the same intra-bar chaining model as the entry state machine. See [`long_entry_state_machine_v2_7_0.md`](long_entry_state_machine_v2_7_0.md#intra-bar-evaluation-model) for full rule categories and chain constraints.

### Shared Exit Transition Timing

- **[XSE] EXIT_SESSION_CLOSE** — bar close only
- **[HS_E] EXIT_HARD_STOP** — intra-bar (Pass 0; immediate exit to S01 when hard stop condition met; fires before count criteria)
- **[HS_S] COUNT_STOP_LOSS_SOFT** — intra-bar (self-transition; increments exit_counter for soft violations only)
- **[PEM] COUNT_MA** - bar close only (self-transition; increments exit_counter; no immediate exit)
- **[XMA] EXIT_MA** - bar close only (immediate exit to S01)
- **[PEL] COUNT_LWR** − bar close only (self-transition; increments exit_counter; no immediate exit)
- **[XLR] EXIT_LWR** - bar close only (immediate exit to S01)
- **[OC] OFFSET_CHANGE** − intra-bar (self-transition; fires when exit_counter reaches next range min_count; tightens type2_profit_limit)
- **[EPTC] EXIT_Profit_Target_Count** − intra-bar (exits to S01 when bar_high >= type2_profit_limit)

---

## Shared Transitions

The following transitions appear in multiple states and use canonical shared IDs from the live strategy map. The full definition is here; each state section shows only a compact reference line.

### [XSE] EXIT_SESSION_CLOSE

100) **EXIT_SESSION_CLOSE**
      - ID: `t_220_exit_session_close` (220)
      - From: `long_09_YELLOW` | `long_10_GREEN` | `long_11_RED`
      - To: `long_00_IDLE`
      - Name: `EXIT_SESSION_CLOSE`
      - Indicator: `calcTradingAuthorized`
      - Guard: `tradingTime_isAuthorized == False`
      - Action: capture `exit_price = bar_close`; submit market close order; cancel all active orders; record `exit_price` + `exit_reason = EXIT_SESSION_CLOSE` in broker JSON
      - Comments: Position must be closed at bar close. Uses `tradingTime_isAuthorized` (not `tradeEntry_isAuthorized`). Applies to states 09, 10, 11. Approved 2026-03-21 11:55 EST.
      - Intra-bar Rule: `bar close only`

#### calcTradingAuthorized explanation

**Source:** `calcTradingAuthorized` class in indicator modules.

**Config references:**
- Primary timezone definitions: `config_3_time_zones_v5_0_0.json` — defines all 9 session zones, `approved_times`, `trade_approved_times`, and `entry_approved_times`
- Indicator properties: `config_2_indicators_v5_4_0.json`
- Timezone: `US/Eastern`

**All defined time zones:**

| Zone | Start | End | `isAuthorized` | In `trade_approved_times` | In `entry_approved_times` |
|------|-------|-----|:--------------:|:-------------------------:|:-------------------------:|
| `pre_session` | 18:00 | 09:30 | false | — | — |
| `open` | 09:30 | 09:50 | false | — | — |
| `trade_1` | 09:50 | 10:00 | true | ✓ | ✓ |
| `trade_2` | 10:00 | 12:05 | true | ✓ | ✓ |
| `lunch` | 12:05 | 12:35 | true | ✓ | ✓ |
| `trade_3` | 12:35 | 15:45 | true | ✓ | ✓ |
| `trade_4` | 15:45 | 15:56 | true | ✓ | — |
| `long_00_IDLE` | 15:56 | 17:00 | false | — | — |
| `closed` | 17:00 | 18:00 | false | — | — |

**Key distinction from [SE]:**
- `tradeEntry_isAuthorized` (used by [SE]) closes at **15:45** — excludes `trade_4`
- `tradingTime_isAuthorized` (used by [XSE]) closes at **15:56** — includes `trade_4`; all active positions must be closed before `long_00_IDLE`

**Step-by-step breakdown:**

1. `calcTradingAuthorized` evaluates the current bar timestamp against the zones in `config_3_time_zones_v5_0_0.json`.
2. `tradingTime_isAuthorized` is set to `False` when the current time falls outside `trade_approved_times` (i.e., in `long_00_IDLE`, `closed`, `pre_session`, or `open`). The authorized trading window ends at **15:56**.
3. Guard fires → strategy captures `bar_close` as the exit price, submits a market close order, cancels all active orders, and records the exit in the broker JSON log.

**Plain English:** when the active trading session ends at 15:56, any open position is forcibly closed at the bar close price and the machine returns to the after-hours idle state.

#### long_00_IDLE

The initial idle state. The machine waits here until the next valid session start (`SESSION_OPEN` transition fires when `tradingTime_isAuthorized == True`).

### [HS_E] EXIT_HARD_STOP

<a id="hse-exit_hard_stop"></a>

101a) **EXIT_HARD_STOP**
      - ID: `t_221_exit_hard_stop` (221)
      - From: `long_09_YELLOW` | `long_10_GREEN` | `long_11_RED`
      - To: `long_01_WAIT_BEAR_TREND`
      - Name: `EXIT_HARD_STOP`
      - Indicator: bar price + `stop_type` (context)
      - Guard: `bar_low < stop_loss AND stop_type in ('Type_1_Hard', 'Type_2_BreakEven')`
      - Action: `exit_price = stop_loss`; cancel all active orders; record `exit_price` + `exit_reason = EXIT_HARD_STOP` in broker JSON.
      - Comments: Restored in v1.5.1. Fires in Pass 0 (before count criteria) when a *hard* stop violation is detected. Hard condition 1: stop was clamped at entry (stop_type = 'Type_1_Hard') — represents the broker's maximum-loss ceiling. Hard condition 2: break-even ratchet fired (stop_type = 'Type_2_BreakEven') — stop has been moved to entry + sl_type_2_offset. Soft violations (Type_1_Initial) fall through to COUNT_STOP_LOSS_SOFT. Applies to states 09, 10, 11.
      - Intra-bar Rule: `intra-bar (Pass 0 — evaluated every 1-sec bar, before count criteria)`

### [HS_S] COUNT_STOP_LOSS_SOFT

<a id="hss-count_stop_loss_soft"></a>

101b) **COUNT_STOP_LOSS_SOFT**
      - ID: `t_210_count_stop_loss_soft` (210)
      - From: `long_09_YELLOW` | `long_10_GREEN` | `long_11_RED`
      - To: *(same state — self-transition)*
      - Name: `COUNT_STOP_LOSS_SOFT`
      - Indicator: bar price + `stop_type` (context)
      - Guard: `bar_low < stop_loss AND stop_type == 'Type_1_Initial'` (soft base-level stop only)
      - Action: `exit_counter += scale` (scale = _RYG_SCALE: 2 for Yellow, 1 for Green, 3 for Red); no immediate exit — OFFSET_CHANGE drives profit-limit tightening.
      - Comments: Uses canonical shared ID `t_210_count_stop_loss_soft`. Only fires when stop_type is 'Type_1_Initial' (unclamped soft stop). Hard stop violations are handled by EXIT_HARD_STOP (`t_221_exit_hard_stop`) in Pass 0. `stop_loss` set at entry by Type 1; may be tightened by Types 3/5. Applies to states 09, 10, 11.
      - Intra-bar Rule: `intra-bar (evaluates every 1-sec bar, after Pass 0)`

#### COUNT_STOP_LOSS explanation

**Source:** Strategy-level stop price comparison. No broker action; counter only.

**Step-by-step breakdown:**

1. **`stop_loss`** — the current stop price in context. Set at entry by Type 1 (Initial Stop). May be tightened by Types 2/3/5 each bar.
2. **`bar_low < stop_loss`** — price has dipped below the stop level at some point intra-bar. Guard fires.
3. **Action on fire:** `exit_counter += scale` where `scale = _RYG_SCALE[current_state]` (2 / 1 / 3 for Yellow/Green/Red). The counter accumulates pressure toward the next offset range.
4. **No exit issued.** Position continues. The counter will eventually trigger OFFSET_CHANGE (`t_230_offset_change`) which tightens `type2_profit_limit`.
5. Fires as a self-transition: `from_state == to_state`; `_execute_transition` applies context delta.

**Plain English:** when price dips below the stop level, the exit pressure counter increments (weighted by current state). No immediate exit is issued — the FSM accumulates the signal and uses it to progressively tighten the profit target via offset ranges.

#### long_01_WAIT_BEAR_TREND

The neutral holding state. The machine waits here for either `BEAR_TREND_MET` (to restart a new setup) or `ENTRY_WINDOW_CLOSED` (session close).

### [PEM] COUNT_MA

102) **COUNT_MA**
      - ID: `t_211_count_ma` (211)
      - From: `long_09_YELLOW` | `long_10_GREEN` | `long_11_RED`
      - To: *(same state — self-transition)*
      - Name: `COUNT_MA`
      - Indicator: `MAAC` (`MovingAverageArrayCalculator`)
      - Guard: `bar_close < ma_exit AND bar_close > entry_price`
      - Action: `exit_counter += scale` (scale = _RYG_SCALE: 2/1/3 for Yellow/Green/Red); no immediate exit.
      - Comments: Replaces EXIT_MA (v1.4.0). In v1.5.0 this is a counter increment, not an exit. `ma_exit` is the exit-MA period from config. `bar_close > entry_price` guard prevents firing at a loss. Applies to states 09, 10, 11.
      - Intra-bar Rule: `bar close only`

### [XMA] EXIT_MA

<a id="xma-exit_ma"></a>

102a) **EXIT_MA**
      - ID: `t_222_exit_ma` (222)
      - From: `long_09_YELLOW` | `long_10_GREEN` | `long_11_RED`
      - To: `long_01_WAIT_BEAR_TREND`
      - Name: `EXIT_MA`
      - Indicator: `MAAC` (`MovingAverageArrayCalculator`)
      - Guard: `is_bar_close == True AND bar_close < ma_exit AND bar_close > entry_price`
      - Action: capture `exit_price = bar_close`; submit market close order; cancel all active orders; record `exit_price` + `exit_reason = EXIT_MA` in broker JSON.
      - Comments: Additional immediate-exit transition restored in v1.5.2. Evaluated only at actual minute bar close and never on virtual/intra-bar ticks. Applies to states 09, 10, 11 while keeping [PEM] COUNT_MA as independent counter behavior.
      - Intra-bar Rule: `bar close only`

#### MAAC / ma_exit explanation

**Source:** `MovingAverageArrayCalculator` class in indicator modules.

**Config reference:** `config_2_indicators_v5_4_0.json → MovingAverageArrayCalculator.tickers.tradeTicker.[ticker].[tf]`

The config stores MA periods as a 3-element list: `[fast, slow, exit]`. Example for `@ES tf1`: `[3, 5, 8]` → `fast = 3`, `slow = 5`, `exit = 8`.

**Step-by-step breakdown:**

1. `MAAC` computes three moving averages each bar: `MA(fast)`, `MA(slow)`, `MA(exit)`.
2. `ma_exit` = the value of `MA(exit)` on the current bar.
3. **Guard part 1** — `bar_close < ma_exit`: the bar close has dropped below the exit MA line, indicating momentum has weakened.
4. **Guard part 2** — `bar_close > entry_price`: the close is still above the entry price, confirming the trade is in profit.
5. **Guard fires** → strategy captures `bar_close` as the exit price, cancels all resting stops and profit orders, and records the exit in the broker JSON log.

**Plain English:** when price closes below its exit moving average but is still in profit, the MA signals that momentum has faded — the exit pressure counter increments. No immediate exit; OFFSET_CHANGE will tighten the profit target as counter crosses range thresholds.

#### long_01_WAIT_BEAR_TREND

The neutral holding state. The machine waits here for either `BEAR_TREND_MET` (to restart a new setup) or `ENTRY_WINDOW_CLOSED` (session close).

### [PEL] COUNT_LWR

103) **COUNT_LWR**
      - ID: `t_212_count_lwr` (212)
      - From: `long_09_YELLOW` | `long_10_GREEN` | `long_11_RED`
      - To: *(same state — self-transition)*
      - Name: `COUNT_LWR`
      - Indicator: `LWR` (`LWR123Calculator`)
      - Guard: `lwr_reading == -1 AND bar_close > entry_price`
      - Action: `exit_counter += scale` (scale = _RYG_SCALE: 2/1/3 for Yellow/Green/Red); no immediate exit. Also triggers LWR counter reset (via `_update_active_position_state`) when `lwr_reading` transitions TO +1.
      - Comments: Replaces EXIT_LWR (v1.4.0). In v1.5.0 this is a counter increment. `bar_close > entry_price` prevents firing at a loss. The counter accumulates bearish LWR readings without forcing an exit. When LWR returns to +1 (bullish), `exit_counter` resets to 0 and `exit_counter_range_idx` resets to -1. Applies to states 09, 10, 11.
      - Intra-bar Rule: `bar close only`

#### LWR / lwr_reading explanation

**Source:** `LWR123Calculator` class in indicator modules.

`lwr_reading` is the output of the LWR (Larry Williams %R) oscillator, discretized into three zones:

| Value | Zone | Meaning |
|---|---|---|
| `1` | Upper channel | Bullish — price is overbought / in the upper zone |
| `0` | Middle channel | Neutral — price is in the middle zone |
| `-1` | Lower channel | Bearish — price is in the lower zone |

**Step-by-step breakdown:**

1. `LWR123Calculator` computes the LWR oscillator each bar and maps it to `{-1, 0, 1}`.
2. **Guard part 1** — `lwr_reading == -1`: LWR is in the lower channel zone, signaling the momentum of the move has turned bearish.
3. **Guard part 2** — `bar_close > entry_price`: the close is still above the entry price, confirming the trade is in profit.
4. **Guard fires** → strategy captures `bar_close` as the exit price, cancels all resting stops and profit orders, and records the exit in the broker JSON log.

**Plain English:** when the LWR indicator drops into the bearish zone while the trade is still in profit, the exit pressure counter increments. No immediate exit — the FSM accumulates the bearish signal and tightens the profit target via offset ranges. If LWR recovers to +1, the counter resets.

### [XLR] EXIT_LWR

<a id="xlr-exit_lwr"></a>

103a) **EXIT_LWR**
      - ID: `t_223_exit_lwr` (223)
      - From: `long_09_YELLOW` | `long_10_GREEN` | `long_11_RED`
      - To: `long_01_WAIT_BEAR_TREND`
      - Name: `EXIT_LWR`
      - Indicator: `LWR` (`LWR123Calculator`)
      - Guard: `is_bar_close == True AND lwr_reading == -1 AND bar_close > entry_price`
      - Action: capture `exit_price = bar_close`; submit market close order; cancel all active orders; record `exit_price` + `exit_reason = EXIT_LWR` in broker JSON.
      - Comments: Additional immediate-exit transition restored in v1.5.2. This transition is evaluated only at the actual minute bar close and does not fire on virtual open/intra-bar ticks. Applies to states 09, 10, 11.
      - Intra-bar Rule: `bar close only`

#### long_01_WAIT_BEAR_TREND

The neutral holding state. The machine waits here for either `BEAR_TREND_MET` (to restart a new setup) or `ENTRY_WINDOW_CLOSED` (session close).

---

### [OC] OFFSET_CHANGE

<a id="oc-offset_change"></a>

130) **OFFSET_CHANGE**
      - ID: `t_230_offset_change` (230)
      - From: `long_09_YELLOW` | `long_10_GREEN` | `long_11_RED`
      - To: *(same state — self-transition)*
      - Name: `OFFSET_CHANGE`
      - Indicator: `exit_counter` (context) + `exit_counter_config` (config)
      - Guard: `exit_counter >= next_range['min_count']` (where `next_range = ec_ranges[exit_counter_range_idx + 1]`)
      - Action: advance `exit_counter_range_idx += 1`; set `type2_profit_limit = bar_close + offset` (where `offset = next_range['profit_limit_offset']`); if `offset == 0` → `type2_profit_limit = bar_close` (market-sell semantics: EPTC fires immediately next bar).
      - Comments: Fires when the exit counter enters the next offset range. `exit_counter_range_idx` tracks how many ranges have been entered (-1 = none). The profit limit tightens with each range advance. Once set, EPTC (`t_226_exit_profit_target_count`) fires on any bar where `bar_high >= type2_profit_limit`.
      - Intra-bar Rule: `intra-bar (evaluates after count-criteria checks on any 1-sec bar)`

#### OFFSET_CHANGE explanation

**Source:** `SharedExit.offset_change()` — checks `exit_counter_config` for the current ticker, resolves sorted ranges, and fires when the counter enters the next range.

**Config reference:** `config_5_strategy.exit_counter_config[ticker]` — list of range dicts each with `min_count`, `max_count`, `profit_limit_offset`.

**Step-by-step breakdown:**

1. **`exit_counter_range_idx`** — initialized to -1 at trade entry. Incremented each time OC fires.
2. **`next_range = ec_ranges[exit_counter_range_idx + 1]`** — look up the next (unactivated) range.
3. **`exit_counter >= next_range['min_count']`** — guard fires: the accumulated counter pressure has crossed the range threshold.
4. **`profit_limit_offset`** — the tightening amount for this range. Positive value: `type2_profit_limit = bar_close + offset`. Zero: market-sell flag (`EPTC fires next bar since bar_high >= bar_close`).
5. Self-transition: `_execute_transition` updates `exit_counter_range_idx` and `type2_profit_limit` in context; current state unchanged.

**Plain English:** as exit-pressure criteria accumulate, the counter eventually crosses the threshold for the next offset range — the profit target tightens (or becomes a market sell if offset=0). This is the bridge between counter accumulation and actual position exit.

#### long_01_WAIT_BEAR_TREND

Not reached by this transition. OC returns to the same state. EPTC (`t_226_exit_profit_target_count`) exits to S01.

---

### [EPTC] EXIT_Profit_Target_Count

<a id="eptc-exit_profit_target_count"></a>

131) **EXIT_Profit_Target_Count**
      - ID: `t_226_exit_profit_target_count` (226)
      - From: `long_09_YELLOW` | `long_10_GREEN` | `long_11_RED`
      - To: `long_01_WAIT_BEAR_TREND`
      - Name: `EXIT_Profit_Target_Count`
      - Indicator: `bar_high` (price) + `type2_profit_limit` (context)
      - Guard: `type2_profit_limit is not None AND bar_high >= type2_profit_limit`
      - Action: capture `exit_price = type2_profit_limit`; cancel active stops; cancel active profit orders; record `exit_price` + `exit_reason = EXIT_PROFIT_TARGET_COUNT` in broker JSON.
      - Comments: Primary exit mechanism in v1.5.0 (replaces t_270–t_273 State12 exits). `type2_profit_limit` is set/tightened by OFFSET_CHANGE (`t_230_offset_change`). When `profit_limit_offset == 0`, the limit equals `bar_close_at_oc_bar`; since `bar_high >= bar_close` always, EPTC fires on the very next bar (effective market sell). Applies to states 09, 10, 11.
      - Intra-bar Rule: `intra-bar (evaluates on every 1-sec bar once type2_profit_limit is set)`

#### EXIT_Profit_Target_Count explanation

**Source:** `SharedExit.exit_profit_target_count()` — fires when bar_high reaches or exceeds the active profit-limit after counter-driven tightening.

**Step-by-step breakdown:**

1. **`type2_profit_limit`** — set by OFFSET_CHANGE (`t_230_offset_change`) when the counter enters an offset range. Initially `None` (no limit active).
2. **`bar_high >= type2_profit_limit`** — price has touched or exceeded the profit limit level intra-bar. Guard fires.
3. **Immediate exit:** `exit_price = type2_profit_limit`; cancel all stops and profit orders; record in broker JSON.
4. **Market sell path:** when `profit_limit_offset == 0`, `type2_profit_limit = bar_close` from the prior bar. On the next bar, `bar_high >= bar_close` is nearly always True → position exits promptly.
5. Transition goes to `long_01_WAIT_BEAR_TREND` (S01), not S00 — machine waits for next bear trend signal.

**Plain English:** when the accumulated exit pressure has tightened the profit target (via OFFSET_CHANGE), this transition exits the position as soon as price touches the profit limit. If the limit was set to bar_close (offset=0), the exit fires on the next bar as a market sell.

---

### long_09_YELLOW (Initial Active State)

[[XSE]](#xse-exit_session_close) EXIT_SESSION_CLOSE t_220_exit_session_close (220)

[[HS_E]](#hse-exit_hard_stop) EXIT_HARD_STOP t_221_exit_hard_stop (221)

[[HS_S]](#hss-count_stop_loss_soft) COUNT_STOP_LOSS_SOFT t_210_count_stop_loss_soft (210)

[[PEM]](#pem-count_ma) COUNT_MA t_211_count_ma (211)

[[XMA]](#xma-exit_ma) EXIT_MA t_222_exit_ma (222)

[[PEL]](#pel-count_lwr) COUNT_LWR t_212_count_lwr (212)

[[XLR]](#xlr-exit_lwr) EXIT_LWR t_223_exit_lwr (223)

<a id="t_240_yellow_to_green"></a>

104) **YELLOW_TO_GREEN**
      - ID: `t_240_yellow_to_green`
      - From: `long_09_YELLOW`
      - To: `long_10_GREEN`
      - Name: `YELLOW_TO_GREEN`
      - Indicator: `MAAC`
      - Guard: `ma_velocity >= 1.5 AND ma_velocity >= prev_ma_velocity` (Strong)
      - Action: n/a
      - Comments:
      - Intra-bar Rule: `bar close only`

#### MAAC / ma_velocity explanation

**Source:** `MovingAverageArrayCalculator` (MAAC) in indicator modules.

**Step-by-step breakdown:**

1. **`ma_velocity`** — `MA(fast) - MA(slow)` computed by MAAC each bar. A positive and growing value indicates increasing bullish momentum.
2. **`ma_velocity >= 1.5`** — velocity threshold for "Strong" classification. The fast EMA is at least 1.5 price points above the slow EMA.
3. **`ma_velocity >= prev_ma_velocity`** — velocity is not declining; the gap between fast and slow EMAs is stable or growing bar over bar. This filters out cases where velocity is above 1.5 but already decelerating.
4. **Both conditions met** — velocity is strong AND not weakening → classify as "Green" (high-conviction hold).
5. **Competing transition** — `YELLOW_TO_RED` (transition 105) fires if velocity is at or below 1.0 and not accelerating.

**Plain English:** when the EMA velocity is strong (≥ 1.5) and not decelerating, the position is reclassified to the high-confidence green state with unique exit criteria.

#### long_10_GREEN

Active position state with strong momentum. Stops active: Type 1 (initial hard), Type 5 (session trailing), Type 2 (break-even). Exits via `EXIT_Profit_Target_Count`, `GREEN_TO_YELLOW`, or `EXIT_SESSION_CLOSE`.

<a id="t_241_yellow_to_red"></a>

105) **YELLOW_TO_RED**
      - ID: `t_241_yellow_to_red`
      - From: `long_09_YELLOW`
      - To: `long_11_RED`
      - Name: `YELLOW_TO_RED`
      - Indicator: `MAAC`
      - Guard: `ma_velocity <= 1.0 AND ma_velocity <= prev_ma_velocity` (weak)
      - Action: n/a
      - Comments:
      - Intra-bar Rule: `bar close only`

#### MAAC / ma_velocity explanation

**Source:** `MovingAverageArrayCalculator` (MAAC) in indicator modules.

**Step-by-step breakdown:**

1. **`ma_velocity`** — `MA(fast) - MA(slow)` computed each bar.
2. **`ma_velocity <= 1.0`** — velocity is at or below the low-velocity threshold; momentum is weak.
3. **`ma_velocity <= prev_ma_velocity`** — velocity is not growing; momentum is flat or decelerating.
4. **Both conditions met** — sub-threshold velocity that is not accelerating → classify as "Red" (moderate/cautious hold).
5. **Competing transition** — `YELLOW_TO_GREEN` (transition 104) fires if velocity is ≥ 1.5 and growing.

**Plain English:** when the EMA velocity is at or below the low-velocity threshold and not accelerating, the position is reclassified to the cautious red state with its own exit criteria.

#### long_11_RED

Active position state with moderate or declining momentum. Stops active: Type 3 (LWR trailing), Type 2 (break-even if met). Exits via `EXIT_Profit_Target_Count`, `RED_TO_YELLOW`, or `EXIT_SESSION_CLOSE`.

<a id="pm-exit_premarket_s09"></a>

106) **EXIT_PREMARKET_S09**
      - ID: `t_224_exit_premarket_s09`
      - From: `long_09_YELLOW`
      - To: `long_01_WAIT_BEAR_TREND`
      - Name: `EXIT_PREMARKET_S09`
      - Indicator: `premarket limits`
      - Guard: `is_bar_close == True AND indicators.get('exit_premarket_limit_09', False) == True`
      - Action: `cancel_active_orders = True`; `market_sell = True`; record `exit_reason = EXIT_PREMARKET_S09` in broker JSON.
      - Comments: Canonical immediate premarket exit from S09 in `long_strategy_v1_13_3.py`. Fires only at bar close.
      - Intra-bar Rule: `bar close only`

#### premarket limits explanation (S09)

**Source:** Strategy-level pre-session price level tracking. Profit target snapshotted at trade entry.

`exit_premarket_limit_09` is set by the indicator layer when `bar_high >= ctx["premarket_profit_target"]` in State09.

**Plain English:** while holding in the yellow state, if the premarket exit condition is set and it is bar close, the strategy exits immediately with a market-sell and returns to S01.

#### long_01_WAIT_BEAR_TREND

Returns to neutral. No position open. Machine waits for `BEAR_TREND_MET` or `ENTRY_WINDOW_CLOSED`.

---

### long_10_GREEN

Stops active: Type 1 (initial hard), Type 5 (session trailing), Type 2 (break-even).

[[XSE]](#xse-exit_session_close) EXIT_SESSION_CLOSE t_220_exit_session_close (220)

[[HS_E]](#hse-exit_hard_stop) EXIT_HARD_STOP t_221_exit_hard_stop (221)

[[HS_S]](#hss-count_stop_loss_soft) COUNT_STOP_LOSS_SOFT t_210_count_stop_loss_soft (210)

[[PEM]](#pem-count_ma) COUNT_MA t_211_count_ma (211)

[[XMA]](#xma-exit_ma) EXIT_MA t_222_exit_ma (222)

[[PEL]](#pel-count_lwr) COUNT_LWR t_212_count_lwr (212)

[[XLR]](#xlr-exit_lwr) EXIT_LWR t_223_exit_lwr (223)

<a id="t_250_green_to_yellow"></a>

114) **GREEN_TO_YELLOW**
      - ID: `t_250_green_to_yellow`
      - From: `long_10_GREEN`
      - To: `long_09_YELLOW`
      - Name: `GREEN_TO_YELLOW`
      - Indicator: `MAAC`
      - Guard: `ma_velocity < 1.3 OR ma_velocity < prev_ma_velocity` (normal)  ? exit green at 1.5 - 0.2 buffer
      - Action: n/a
      - Comments:
      - Intra-bar Rule: `bar close only`

#### MAAC / ma_velocity explanation

**Source:** `MovingAverageArrayCalculator` (MAAC) in indicator modules.

**Step-by-step breakdown:**

1. **`ma_velocity`** — `MA(fast) - MA(slow)` per bar. Measures EMA separation.
2. **`ma_velocity < 1.3`** — velocity falls below the exit threshold (= `high_velocity_limit − velocity_hysteresis` = 1.5 − 0.2). Hysteresis buffer prevents oscillating back to Green immediately after entry.
3. **`ma_velocity < prev_ma_velocity`** — velocity is declining bar over bar. Note this uses **OR** — either condition alone is sufficient to trigger the downgrade.
4. **OR logic** — the guard is more sensitive than `YELLOW_TO_GREEN` (which requires BOTH conditions to be true). Any weakening triggers a downgrade from Green to Yellow.
5. **Competing transition** — this is the inverse of `YELLOW_TO_GREEN` (transition 104). The state machine evaluates both transitions each bar while in Green.

**Plain English:** when EMA velocity drops below 1.3 (= 1.5 − 0.2 hysteresis buffer) OR starts declining, the position is reclassified from the confident green state back to the cautious yellow state, where stop types are re-evaluated on the next bar.

#### long_09_YELLOW

Returns to the initial active position state. Stop types are re-evaluated; on the next bar the machine will route back to Green or Red based on velocity.

---

### long_11_RED

Stops active: Type 3 (LWR trailing), Type 2 (break-even if met).

[[XSE]](#xse-exit_session_close) EXIT_SESSION_CLOSE t_220_exit_session_close (220)

[[HS_E]](#hse-exit_hard_stop) EXIT_HARD_STOP t_221_exit_hard_stop (221)

[[HS_S]](#hss-count_stop_loss_soft) COUNT_STOP_LOSS_SOFT t_210_count_stop_loss_soft (210)

[[PEM]](#pem-count_ma) COUNT_MA t_211_count_ma (211)

[[XMA]](#xma-exit_ma) EXIT_MA t_222_exit_ma (222)

[[PEL]](#pel-count_lwr) COUNT_LWR t_212_count_lwr (212)

[[XLR]](#xlr-exit_lwr) EXIT_LWR t_223_exit_lwr (223)

<a id="pm-exit_premarket_s11"></a>

125) **EXIT_PREMARKET_S11**
      - ID: `t_225_exit_premarket_s11`
      - From: `long_11_RED`
      - To: `long_01_WAIT_BEAR_TREND`
      - Name: `EXIT_PREMARKET_S11`
      - Indicator: `premarket limits`
      - Guard: `is_bar_close == True AND indicators.get('exit_premarket_limit_11', False) == True`
      - Action: `cancel_active_orders = True`; `market_sell = True`; record `exit_reason = EXIT_PREMARKET_S11` in broker JSON.
      - Comments: Canonical immediate premarket exit from S11 in `long_strategy_v1_13_3.py`. Fires only at bar close.
      - Intra-bar Rule: `bar close only`

#### premarket limits explanation (S11)

**Source:** Strategy-level pre-session price level tracking.

`exit_premarket_limit_11` is set by the indicator layer when `bar_high >= ctx["premarket_profit_target"]` in State11.

**Plain English:** while holding in the red state, if the premarket exit condition is set and it is bar close, the strategy exits immediately with a market-sell and returns to S01.

<a id="t_262_red_to_yellow"></a>

126) **RED_TO_YELLOW**
      - ID: `t_262_red_to_yellow`
      - From: `long_11_RED`
      - To: `long_09_YELLOW`
      - Name: `RED_TO_YELLOW`
      - Indicator: `MAAC`
      - Guard: `ma_velocity > 1.2 OR ma_velocity > prev_ma_velocity` (normal)  ? exit red at 1.0 + 0.2 buffer
      - Action: n/a
      - Comments:
      - Intra-bar Rule: `bar close only`

#### MAAC / ma_velocity explanation

**Source:** `MovingAverageArrayCalculator` (MAAC) in indicator modules.

**Step-by-step breakdown:**

1. **`ma_velocity`** — `MA(fast) - MA(slow)` per bar. Measures EMA separation.
2. **`ma_velocity > 1.2`** — velocity rises above the Red exit threshold (`low_velocity_limit + velocity_hysteresis` = 1.0 + 0.2). Momentum is recovering toward Yellow.
3. **`ma_velocity > prev_ma_velocity`** — velocity is accelerating bar over bar; momentum is building upward.
4. **OR logic** — either condition alone is sufficient to trigger the upgrade from Red to Yellow. Symmetric to `GREEN_TO_YELLOW` (114) which uses OR in the downward direction.
5. **Competing transition** — this is the inverse of `YELLOW_TO_RED` (transition 105). The state machine evaluates both transitions each bar while in Red.

**Plain English:** when EMA velocity rises above 1.2 (= 1.0 + 0.2 hysteresis buffer) OR starts accelerating, the position is upgraded from the moderate red state back to the cautious yellow state, where stop types are re-evaluated on the next bar.

#### long_09_YELLOW

Returns to the initial active position state. Stop types are re-evaluated; on the next bar the machine will route back to Green or Red based on velocity.

---

## Stop Loss Types Reference

From `strategy_v9_5_6.py` / `long_strategy_v1_6_5.py`:

1. **Initial (hard)** — Green — `stop = shift_event_low - sl_type_1_offset`, clamped to max distance
2. **Break-even** — Both — `bar_high > entry + profit_limit` → queue `stop = entry + offset`
3. **LWR trailing** — Red — `stop = lohb + 1.0 - sl_type_3_offset`, ratchet only
4. **Exit-Counter Profit Limit** (OFFSET_CHANGE + EPTC) — Yellow/Green/Red — `type2_profit_limit = bar_close + offset_from_range`; fires when `exit_counter` crosses range `min_count` threshold; EPTC exits when `bar_high >= type2_profit_limit`

---

## Change Log

| Date | Version | Project | Change Summary |
|------|---------|---------|----------------|
| 2026-04-20 | v1.5.4 | 20260420_TWM_long_hand_calc_comp | Aligned all transition IDs to `long_strategy_v1_13_3.py` canonical map: COUNT_MA `t_211`, COUNT_LWR `t_212`, EXIT_MA `t_222`, EXIT_LWR `t_223`, EXIT_SESSION_CLOSE `t_220`, EXIT_HARD_STOP `t_221`, EXIT_PREMARKET_S09 `t_224`, EXIT_PREMARKET_S11 `t_225`, EXIT_Profit_Target_Count `t_226`, OFFSET_CHANGE `t_230`; removed legacy per-state ID variants from shared/state sections. |
| 2026-04-18 | v1.5.2 | 20260418_TWM_combine_Debug_higher_tf | Restored [XMA] EXIT_MA for states 09/10/11 with guard `is_bar_close == True AND bar_close < ma_exit AND bar_close > entry_price`; documented as immediate exit to `long_01_WAIT_BEAR_TREND` while preserving [PEM] COUNT_MA. Retained [XLR] EXIT_LWR immediate-exit behavior. |
| 2026-04-13 | v1.5.0 | 20260412_TWM_exit_optimization | Replace State12_EXIT_COUNTER hub with in-state self-transitions on S09/S10/S11. Rename EXIT_HARD_STOP/EXIT_MA/EXIT_LWR/EXIT_PREMARKET to COUNT_ variants (self-transitions, increment exit_counter). Delete EXIT_RED_TIMEOUT (t_260). Add OFFSET_CHANGE ([OC]) and EXIT_Profit_Target_Count ([EPTC]) as the shared exit path. Update timing table, state bullets, state-specific sections, Stop Loss Types Reference. |
| 2026-04-12 | v1.4.0 | 20260412_TWM_housekeeping | Added exit_counter_config context block; cross-ref to State12 exit logic. |
| 2026-04-12 | v1.3.0 | 20260412_TWM_housekeeping | Task 1: Renamed all exit transitions to ALL_CAPS standard; renumbered (200–262); updated state labels. |
| 2026-04-11 | v1.2.2 | 20260411_TWM_1sec_debug_try_v9_trial2 | Invert t106/t125 EXIT_PREMARKET guards: was downside `bar_high<=level` cross (wrong), now upside `bar_high>=ctx["premarket_profit_target"]` (correct profit target). Update intra-bar rule to fire on any 1-sec bar. Remove Type 4 stop reference from State11 context note. Update Stop Loss Types Reference (remove types 4/5, add premarket profit target). |
| 2026-04-07 | v1.2.1 | 20260407_TWM_1sec_strategyLogic_Debug | Fix anchor blank-line rendering: insert blank line after each <a id> anchor (7 locations); individual transitions now render as structured sub-bullets |
| 2026-04-07 09:00 | v1.2.0 | 20260407_TWM_1sec_strategyLogic_Debug | Reformat shared transition definitions ([XSE], [HS], [PEM], [PEL], EXIT_RED_TIMEOUT) to canonical Transition Definition table format; verify all 11 transitions |
| 2026-03-26 | v1.1.0 | 20260319_refactor_strategy | Reformat transitions to bullet layout; add Intra-bar Evaluation Model section + per-transition Intra-bar Rule; add intra-bar timing to shared transitions. |
| 2026-03-24 | v1.0.1 | 20260319_refactor_strategy | Renumber all exit transitions to 100-series; reorder within each state — standard transitions ([XSE],[HS],[PEM],[PEL]) first. |
| 2026-03-24 | v1.0.0 | 20260319_refactor_strategy | Initial document — exit states 09–11 split from `long_entry_state_machine_v1_2_15.md`. |

