Mandate staged trader — backtest report   gpt-oss:20b-cloud

Variant comparison across BTC + WETH on identical window.

window: 2026-02-01T00:00:00Z → 2026-03-01T00:00:00Z tick: 1h initial vault: $10,000 model: gpt-oss:20b-cloud generated: 2026-05-11 08:10:55 UTC

Variant ranking — aggregate across asset

#VariantTradesΣ PnL ($)Avg PnL %Distribution
1 no-bear-pathB 4 +$103.95 +0.52%
2 base 2 +$10.91 +0.05%
3 full-counter-trend 0 +$0.00 +0.00%
4 full-plus-sizing 4 $-37.00 -0.18%

Per-asset breakdown

VariantPairTradesFinal $PnL ($)PnL %
base btcusdc 0 $10000.00 +$0.00 +0.00%
base wethusdc 2 $10010.91 +$10.91 +0.11%
full-counter-trend btcusdc 0 $10000.00 +$0.00 +0.00%
full-counter-trend wethusdc 0 $10000.00 +$0.00 +0.00%
full-plus-sizing btcusdc 2 $10021.21 +$21.21 +0.21%
full-plus-sizing wethusdc 2 $9941.79 $-58.21 -0.58%
no-bear-pathB btcusdc 2 $10162.16 +$162.16 +1.62%
no-bear-pathB wethusdc 2 $9941.79 $-58.21 -0.58%

Findings

Prompt variants — what each one changes vs base

For each non-base variant, the diff vs the production prompt (collapsed context). Click to expand the full text.

full-counter-trend overrides 1 stage: 02-decide
02-decide.md  diff vs base
You are the **decide** stage of the trading workflow.
… 16 unchanged lines …
Test exits in priority order. First match wins.
+ **Min-hold protection (counter-trend):** When `previous.open_position_entry_path ∈ {"B", "E"}` AND `previous.open_position_ticks_since_buy < 2`, the only exits allowed are `stop` (safety) and `tp` (full take-profit). Skip `scalp`, `trailing`, and `reversal` — counter-trend entries need at least 2 tick bars to let the rebound develop. If neither stop nor tp fires within this window → `hold`.
+
**Stop-loss:** `previous.price <= previous.open_position_stop_loss_usd` → `sell`, `path: "stop"`, full close.
… 4 unchanged lines …
**Trailing exit:** the orchestrator does not have a way to compute "NET PnL" without arithmetic, so this path requires `previous.tf_15m` flipping to `SELL` AND `previous.macd_15m_histogram < 0` AND `previous.price > previous.open_position_cost_basis_usd / previous.vault_position_token_amount`. (The position is up vs cost basis AND momentum is rolling over.) → `sell`, `path: "trailing"`.
- **Trend reversal:** `previous.tf_4h ∈ {SELL, STRONG_SELL}` AND `previous.tf_daily ∈ {SELL, STRONG_SELL}` → `sell`, `path: "reversal"`.
+ **Trend reversal:** `previous.tf_4h ∈ {SELL, STRONG_SELL}` AND `previous.tf_daily ∈ {SELL, STRONG_SELL}` AND `previous.open_position_entry_path ∉ {"B", "E"}` → `sell`, `path: "reversal"`. **Excluded for B/E entries**: those paths enter deliberately counter-trend, so a still-bearish HTF is the entry premise, not a reversal — let stop/tp/scalp/trailing handle the exit.
If none fire → `hold`.
… 42 unchanged lines …
- `previous.tf_4h ∈ {BUY, STRONG_BUY, NEUTRAL}` (4h reversing up)
- `previous.tf_1h ∈ {BUY, STRONG_BUY}` AND `previous.rsi_15m >= 50`
+ - `previous.macd_15m_flipped_positive == true` (15m momentum confirms — required to avoid catching a falling knife)
Size: 65% aggressive / 50% moderate / 30% conservative.
… 29 unchanged lines …
show full prompt (variant)
You are the **decide** stage of the trading workflow.

Apply the strict v30 rules to research's output and pick exactly one action. **NO arithmetic.** Compare values directly with the listed thresholds. Do not derive flags, do not recompute MACD/RSI/EMA, do not interpolate.

## Previous stage output

```json
{{previous}}
```

## Decision rules

**Default = HOLD.** Only emit `buy` / `sell` when an explicit rule fires below.

Risk thresholds come from `strategy.config` in the system prompt: `entryRsiThreshold`, `fullAlignmentBars`, `spotEntryPct`, `maxExposurePct`, `scalpRsiThreshold`. Defaults below assume the aggressive tier; moderate / conservative override via the config knobs.

### Position-already-open path (`previous.vault_position_token_amount > 0`)

Test exits in priority order. First match wins.

**Min-hold protection (counter-trend):** When `previous.open_position_entry_path ∈ {"B", "E"}` AND `previous.open_position_ticks_since_buy < 2`, the only exits allowed are `stop` (safety) and `tp` (full take-profit). Skip `scalp`, `trailing`, and `reversal` — counter-trend entries need at least 2 tick bars to let the rebound develop. If neither stop nor tp fires within this window → `hold`.

**Stop-loss:** `previous.price <= previous.open_position_stop_loss_usd` → `sell`, `path: "stop"`, full close.

**Full take-profit:** `previous.price >= previous.open_position_tp_target_usd` → `sell`, `path: "tp"`, full close.

**Quick scalp (33%):** `previous.price >= previous.open_position_scalp_target_usd` AND (`previous.rsi_1h >= scalpRsiThreshold` OR `previous.rsi_15m >= scalpRsiThreshold + 5`) → `sell`, `path: "scalp"`, `scalp_pct: 33`.

**Trailing exit:** the orchestrator does not have a way to compute "NET PnL" without arithmetic, so this path requires `previous.tf_15m` flipping to `SELL` AND `previous.macd_15m_histogram < 0` AND `previous.price > previous.open_position_cost_basis_usd / previous.vault_position_token_amount`. (The position is up vs cost basis AND momentum is rolling over.) → `sell`, `path: "trailing"`.

**Trend reversal:** `previous.tf_4h ∈ {SELL, STRONG_SELL}` AND `previous.tf_daily ∈ {SELL, STRONG_SELL}` AND `previous.open_position_entry_path ∉ {"B", "E"}` → `sell`, `path: "reversal"`. **Excluded for B/E entries**: those paths enter deliberately counter-trend, so a still-bearish HTF is the entry premise, not a reversal — let stop/tp/scalp/trailing handle the exit.

If none fire → `hold`.

### No-position path (`previous.vault_position_token_amount == 0`)

**HARD GUARD — read before everything else in this section.** When `previous.vault_position_token_amount == 0` you hold zero of the trading token, so you have NOTHING to sell. Exits (`stop`, `tp`, `scalp`, `trailing`, `reversal`) are FORBIDDEN here regardless of how bearish the timeframes look — they exist ONLY in the Position-already-open path above. The only valid emissions when position is 0 are: `hold`, or `buy` with one of the entry paths A / B / C / D / E. Emitting `{"decision":"sell", ...}` with `vault_position_token_amount == 0` is a hard contract violation: there is no inventory to dispose of and the orchestrator will reject it.

Test in priority order. First match wins. Use `config.entryRsiThreshold` (default 45 aggressive / 42 moderate / 40 conservative) where shown.

#### Path A — Short-timeframe mean-reversion

LONG fires when ALL hold:
- `previous.rsi_1h < config.entryRsiThreshold`
- `previous.rsi_15m_crossed_up_from_below_35 == true` (15m oversold reversal confirmed)
- `previous.macd_15m_flipped_positive == true` (15m momentum flipped within the last 2 bars)
- `previous.tf_daily ∉ {SELL, STRONG_SELL}` (1d not bearish — daily-flat-or-better)

Size: `config.spotEntryPct.A` (default 50% aggressive / 35% moderate / 20% conservative).

#### Path E — Capitulation oversold (BYPASS HTF wall, SPOT-ONLY)

LONG fires when ALL hold:
- `previous.rsi_1h < 22` (aggressive), `< 27` (moderate), `< 18` (conservative). DO NOT relax — this is the extreme-tail entry.
- `previous.rsi_15m > 30` (some recovery from the bottom)
- `previous.macd_15m_flipped_positive == true` (early momentum flip confirmed)
- `previous.close_above_low_12_pct >= 0.3` (proof the local bottom is in)

No HTF filter. Size: 30% aggressive / 20% moderate / 12% conservative. 60-min anti-wash window after this entry's exit (no Path A re-entry inside that window).

#### Path C — Trend continuation pullback

LONG fires when ALL hold:
- `previous.tf_buy_count >= 3` (3-of-4 timeframes BUY or STRONG_BUY)
- `previous.rsi_1h < 60` (not yet overbought — leaves room)
- `previous.macd_15m_histogram > 0` (15m is going up)
- `previous.atr_pct >= 1.0` (enough volatility for a meaningful continuation)
- `previous.regime != "bear"`

Catches the **mid-trend pullback** that Path A (oversold) and Path D (4/4 + breakout) both miss. Size: 35% aggressive / 25% moderate / 15% conservative. 30-min anti-wash window.

#### Path B — Deep-value counter-trend

LONG fires when ALL hold:
- `previous.rsi_daily < 30` AND `previous.rsi_weekly < 35`
- `previous.tf_4h ∈ {BUY, STRONG_BUY, NEUTRAL}` (4h reversing up)
- `previous.tf_1h ∈ {BUY, STRONG_BUY}` AND `previous.rsi_15m >= 50`
- `previous.macd_15m_flipped_positive == true` (15m momentum confirms — required to avoid catching a falling knife)

Size: 65% aggressive / 50% moderate / 30% conservative.

#### Path D — Confirmed momentum

LONG fires when ALL hold:
- `previous.tf_buy_count >= config.fullAlignmentBars` (default 4 = strict, 3 = looser)
- `previous.breakout_last_24_periods == true` (15m close above the 24-bar high)
- `previous.volume_confirm_15m == true` (15m vol_ratio ≥ 1.3)
- `previous.atr_pct >= 1.5`

Size: 65% aggressive / 50% moderate / 30% conservative.

### Default

If no path fires → `hold`.

## Anti-wash trade rule

After a full take-profit / final scalp / stop / trailing / reversal exit, the next 30 min ONLY allows Path A (oversold dip) or Path E (capitulation). Path B / Path C / Path D are forbidden in that window regardless of signal. The runtime SELL GUARD + 2h BUY cooldown back this up at the code level — your job is to not even propose a forbidden re-entry.

## Output schema (your last message MUST be a single-line JSON object — no markdown fences, just one line)

```json
{"decision":"<buy|sell|hold>","path":"<A|B|C|D|E|stop|tp|scalp|trailing|reversal|null>","size_pct":<number 0-100 or null>,"scalp_pct":<33|null>,"reasoning":"<one short sentence>"}
```

The orchestrator parses your LAST line as JSON. Emit it on a single line, no code fence, no trailing prose. The skip_condition `previous.decision == "hold"` requires a parseable JSON; if you wrap the line in markdown the orchestrator falls back to a string and execute spawns wastefully.

If `previous.unavailable == true` → `{"decision":"hold","path":null,"size_pct":null,"scalp_pct":null,"reasoning":"indicators unavailable"}`.
full-plus-sizing overrides 1 stage: 02-decide
02-decide.md  diff vs base
You are the **decide** stage of the trading workflow.
… 10 unchanged lines …
**Default = HOLD.** Only emit `buy` / `sell` when an explicit rule fires below.
- Risk thresholds come from `strategy.config` in the system prompt: `entryRsiThreshold`, `fullAlignmentBars`, `spotEntryPct`, `maxExposurePct`, `scalpRsiThreshold`. Defaults below assume the aggressive tier; moderate / conservative override via the config knobs.
+ **Tier override = MODERATE.** Use the moderate tier values everywhere a tier is mentioned (`entryRsiThreshold = 42`, sizing as listed below). This caps the exposure on counter-trend entries vs the aggressive default.
### Position-already-open path (`previous.vault_position_token_amount > 0`)
Test exits in priority order. First match wins.
+ **Min-hold protection (counter-trend):** When `previous.open_position_entry_path ∈ {"B", "E"}` AND `previous.open_position_ticks_since_buy < 2`, the only exits allowed are `stop` (safety) and `tp` (full take-profit). Skip `scalp`, `trailing`, and `reversal` — counter-trend entries need at least 2 tick bars to let the rebound develop. If neither stop nor tp fires within this window → `hold`.
+
**Stop-loss:** `previous.price <= previous.open_position_stop_loss_usd` → `sell`, `path: "stop"`, full close.
… 4 unchanged lines …
**Trailing exit:** the orchestrator does not have a way to compute "NET PnL" without arithmetic, so this path requires `previous.tf_15m` flipping to `SELL` AND `previous.macd_15m_histogram < 0` AND `previous.price > previous.open_position_cost_basis_usd / previous.vault_position_token_amount`. (The position is up vs cost basis AND momentum is rolling over.) → `sell`, `path: "trailing"`.
- **Trend reversal:** `previous.tf_4h ∈ {SELL, STRONG_SELL}` AND `previous.tf_daily ∈ {SELL, STRONG_SELL}` → `sell`, `path: "reversal"`.
+ **Trend reversal:** `previous.tf_4h ∈ {SELL, STRONG_SELL}` AND `previous.tf_daily ∈ {SELL, STRONG_SELL}` AND `previous.open_position_entry_path ∉ {"B", "E"}` → `sell`, `path: "reversal"`. **Excluded for B/E entries**: those paths enter deliberately counter-trend, so a still-bearish HTF is the entry premise, not a reversal — let stop/tp/scalp/trailing handle the exit.
If none fire → `hold`.
… 3 unchanged lines …
**HARD GUARD — read before everything else in this section.** When `previous.vault_position_token_amount == 0` you hold zero of the trading token, so you have NOTHING to sell. Exits (`stop`, `tp`, `scalp`, `trailing`, `reversal`) are FORBIDDEN here regardless of how bearish the timeframes look — they exist ONLY in the Position-already-open path above. The only valid emissions when position is 0 are: `hold`, or `buy` with one of the entry paths A / B / C / D / E. Emitting `{"decision":"sell", ...}` with `vault_position_token_amount == 0` is a hard contract violation: there is no inventory to dispose of and the orchestrator will reject it.
- Test in priority order. First match wins. Use `config.entryRsiThreshold` (default 45 aggressive / 42 moderate / 40 conservative) where shown.
+ Test in priority order. First match wins. Use **moderate tier**: `entryRsiThreshold = 42`.
#### Path A — Short-timeframe mean-reversion
LONG fires when ALL hold:
- - `previous.rsi_1h < config.entryRsiThreshold`
+ - `previous.rsi_1h < 42` (moderate threshold)
- `previous.rsi_15m_crossed_up_from_below_35 == true` (15m oversold reversal confirmed)
- `previous.macd_15m_flipped_positive == true` (15m momentum flipped within the last 2 bars)
- `previous.tf_daily ∉ {SELL, STRONG_SELL}` (1d not bearish — daily-flat-or-better)
- Size: `config.spotEntryPct.A` (default 50% aggressive / 35% moderate / 20% conservative).
+ Size: **35%** (moderate).
#### Path E — Capitulation oversold (BYPASS HTF wall, SPOT-ONLY)
LONG fires when ALL hold:
- - `previous.rsi_1h < 22` (aggressive), `< 27` (moderate), `< 18` (conservative). DO NOT relax — this is the extreme-tail entry.
+ - `previous.rsi_1h < 27` (moderate threshold). DO NOT relax — this is the extreme-tail entry.
- `previous.rsi_15m > 30` (some recovery from the bottom)
- `previous.macd_15m_flipped_positive == true` (early momentum flip confirmed)
- `previous.close_above_low_12_pct >= 0.3` (proof the local bottom is in)
- No HTF filter. Size: 30% aggressive / 20% moderate / 12% conservative. 60-min anti-wash window after this entry's exit (no Path A re-entry inside that window).
+ No HTF filter. Size: **20%** (moderate). 60-min anti-wash window after this entry's exit.
#### Path C — Trend continuation pullback
LONG fires when ALL hold:
- `previous.tf_buy_count >= 3` (3-of-4 timeframes BUY or STRONG_BUY)
- - `previous.rsi_1h < 60` (not yet overbought — leaves room)
+ - `previous.rsi_1h < 60` (not yet overbought)
- `previous.macd_15m_histogram > 0` (15m is going up)
- - `previous.atr_pct >= 1.0` (enough volatility for a meaningful continuation)
+ - `previous.atr_pct >= 1.0`
- `previous.regime != "bear"`
- Catches the **mid-trend pullback** that Path A (oversold) and Path D (4/4 + breakout) both miss. Size: 35% aggressive / 25% moderate / 15% conservative. 30-min anti-wash window.
+ Size: **25%** (moderate). 30-min anti-wash window.
#### Path B — Deep-value counter-trend
… 3 unchanged lines …
- `previous.tf_4h ∈ {BUY, STRONG_BUY, NEUTRAL}` (4h reversing up)
- `previous.tf_1h ∈ {BUY, STRONG_BUY}` AND `previous.rsi_15m >= 50`
+ - `previous.macd_15m_flipped_positive == true` (15m momentum confirms — required to avoid catching a falling knife)
- Size: 65% aggressive / 50% moderate / 30% conservative.
+ Size: **50%** (moderate, down from 65% aggressive).
#### Path D — Confirmed momentum
LONG fires when ALL hold:
- - `previous.tf_buy_count >= config.fullAlignmentBars` (default 4 = strict, 3 = looser)
- - `previous.breakout_last_24_periods == true` (15m close above the 24-bar high)
- - `previous.volume_confirm_15m == true` (15m vol_ratio ≥ 1.3)
+ - `previous.tf_buy_count >= 3` (moderate alignment, looser than aggressive 4/4)
+ - `previous.breakout_last_24_periods == true`
+ - `previous.volume_confirm_15m == true`
- `previous.atr_pct >= 1.5`
- Size: 65% aggressive / 50% moderate / 30% conservative.
+ Size: **50%** (moderate).
### Default
… 3 unchanged lines …
## Anti-wash trade rule
- After a full take-profit / final scalp / stop / trailing / reversal exit, the next 30 min ONLY allows Path A (oversold dip) or Path E (capitulation). Path B / Path C / Path D are forbidden in that window regardless of signal. The runtime SELL GUARD + 2h BUY cooldown back this up at the code level — your job is to not even propose a forbidden re-entry.
+ After a full take-profit / final scalp / stop / trailing / reversal exit, the next 30 min ONLY allows Path A (oversold dip) or Path E (capitulation). Path B / Path C / Path D are forbidden in that window regardless of signal.
## Output schema (your last message MUST be a single-line JSON object — no markdown fences, just one line)
… 3 unchanged lines …
```
- The orchestrator parses your LAST line as JSON. Emit it on a single line, no code fence, no trailing prose. The skip_condition `previous.decision == "hold"` requires a parseable JSON; if you wrap the line in markdown the orchestrator falls back to a string and execute spawns wastefully.
+ The orchestrator parses your LAST line as JSON. Emit it on a single line, no code fence, no trailing prose.
If `previous.unavailable == true` → `{"decision":"hold","path":null,"size_pct":null,"scalp_pct":null,"reasoning":"indicators unavailable"}`.
show full prompt (variant)
You are the **decide** stage of the trading workflow.

Apply the strict v30 rules to research's output and pick exactly one action. **NO arithmetic.** Compare values directly with the listed thresholds. Do not derive flags, do not recompute MACD/RSI/EMA, do not interpolate.

## Previous stage output

```json
{{previous}}
```

## Decision rules

**Default = HOLD.** Only emit `buy` / `sell` when an explicit rule fires below.

**Tier override = MODERATE.** Use the moderate tier values everywhere a tier is mentioned (`entryRsiThreshold = 42`, sizing as listed below). This caps the exposure on counter-trend entries vs the aggressive default.

### Position-already-open path (`previous.vault_position_token_amount > 0`)

Test exits in priority order. First match wins.

**Min-hold protection (counter-trend):** When `previous.open_position_entry_path ∈ {"B", "E"}` AND `previous.open_position_ticks_since_buy < 2`, the only exits allowed are `stop` (safety) and `tp` (full take-profit). Skip `scalp`, `trailing`, and `reversal` — counter-trend entries need at least 2 tick bars to let the rebound develop. If neither stop nor tp fires within this window → `hold`.

**Stop-loss:** `previous.price <= previous.open_position_stop_loss_usd` → `sell`, `path: "stop"`, full close.

**Full take-profit:** `previous.price >= previous.open_position_tp_target_usd` → `sell`, `path: "tp"`, full close.

**Quick scalp (33%):** `previous.price >= previous.open_position_scalp_target_usd` AND (`previous.rsi_1h >= scalpRsiThreshold` OR `previous.rsi_15m >= scalpRsiThreshold + 5`) → `sell`, `path: "scalp"`, `scalp_pct: 33`.

**Trailing exit:** the orchestrator does not have a way to compute "NET PnL" without arithmetic, so this path requires `previous.tf_15m` flipping to `SELL` AND `previous.macd_15m_histogram < 0` AND `previous.price > previous.open_position_cost_basis_usd / previous.vault_position_token_amount`. (The position is up vs cost basis AND momentum is rolling over.) → `sell`, `path: "trailing"`.

**Trend reversal:** `previous.tf_4h ∈ {SELL, STRONG_SELL}` AND `previous.tf_daily ∈ {SELL, STRONG_SELL}` AND `previous.open_position_entry_path ∉ {"B", "E"}` → `sell`, `path: "reversal"`. **Excluded for B/E entries**: those paths enter deliberately counter-trend, so a still-bearish HTF is the entry premise, not a reversal — let stop/tp/scalp/trailing handle the exit.

If none fire → `hold`.

### No-position path (`previous.vault_position_token_amount == 0`)

**HARD GUARD — read before everything else in this section.** When `previous.vault_position_token_amount == 0` you hold zero of the trading token, so you have NOTHING to sell. Exits (`stop`, `tp`, `scalp`, `trailing`, `reversal`) are FORBIDDEN here regardless of how bearish the timeframes look — they exist ONLY in the Position-already-open path above. The only valid emissions when position is 0 are: `hold`, or `buy` with one of the entry paths A / B / C / D / E. Emitting `{"decision":"sell", ...}` with `vault_position_token_amount == 0` is a hard contract violation: there is no inventory to dispose of and the orchestrator will reject it.

Test in priority order. First match wins. Use **moderate tier**: `entryRsiThreshold = 42`.

#### Path A — Short-timeframe mean-reversion

LONG fires when ALL hold:
- `previous.rsi_1h < 42` (moderate threshold)
- `previous.rsi_15m_crossed_up_from_below_35 == true` (15m oversold reversal confirmed)
- `previous.macd_15m_flipped_positive == true` (15m momentum flipped within the last 2 bars)
- `previous.tf_daily ∉ {SELL, STRONG_SELL}` (1d not bearish — daily-flat-or-better)

Size: **35%** (moderate).

#### Path E — Capitulation oversold (BYPASS HTF wall, SPOT-ONLY)

LONG fires when ALL hold:
- `previous.rsi_1h < 27` (moderate threshold). DO NOT relax — this is the extreme-tail entry.
- `previous.rsi_15m > 30` (some recovery from the bottom)
- `previous.macd_15m_flipped_positive == true` (early momentum flip confirmed)
- `previous.close_above_low_12_pct >= 0.3` (proof the local bottom is in)

No HTF filter. Size: **20%** (moderate). 60-min anti-wash window after this entry's exit.

#### Path C — Trend continuation pullback

LONG fires when ALL hold:
- `previous.tf_buy_count >= 3` (3-of-4 timeframes BUY or STRONG_BUY)
- `previous.rsi_1h < 60` (not yet overbought)
- `previous.macd_15m_histogram > 0` (15m is going up)
- `previous.atr_pct >= 1.0`
- `previous.regime != "bear"`

Size: **25%** (moderate). 30-min anti-wash window.

#### Path B — Deep-value counter-trend

LONG fires when ALL hold:
- `previous.rsi_daily < 30` AND `previous.rsi_weekly < 35`
- `previous.tf_4h ∈ {BUY, STRONG_BUY, NEUTRAL}` (4h reversing up)
- `previous.tf_1h ∈ {BUY, STRONG_BUY}` AND `previous.rsi_15m >= 50`
- `previous.macd_15m_flipped_positive == true` (15m momentum confirms — required to avoid catching a falling knife)

Size: **50%** (moderate, down from 65% aggressive).

#### Path D — Confirmed momentum

LONG fires when ALL hold:
- `previous.tf_buy_count >= 3` (moderate alignment, looser than aggressive 4/4)
- `previous.breakout_last_24_periods == true`
- `previous.volume_confirm_15m == true`
- `previous.atr_pct >= 1.5`

Size: **50%** (moderate).

### Default

If no path fires → `hold`.

## Anti-wash trade rule

After a full take-profit / final scalp / stop / trailing / reversal exit, the next 30 min ONLY allows Path A (oversold dip) or Path E (capitulation). Path B / Path C / Path D are forbidden in that window regardless of signal.

## Output schema (your last message MUST be a single-line JSON object — no markdown fences, just one line)

```json
{"decision":"<buy|sell|hold>","path":"<A|B|C|D|E|stop|tp|scalp|trailing|reversal|null>","size_pct":<number 0-100 or null>,"scalp_pct":<33|null>,"reasoning":"<one short sentence>"}
```

The orchestrator parses your LAST line as JSON. Emit it on a single line, no code fence, no trailing prose.

If `previous.unavailable == true` → `{"decision":"hold","path":null,"size_pct":null,"scalp_pct":null,"reasoning":"indicators unavailable"}`.
no-bear-pathB overrides 1 stage: 02-decide
02-decide.md  diff vs base
You are the **decide** stage of the trading workflow.
… 10 unchanged lines …
**Default = HOLD.** Only emit `buy` / `sell` when an explicit rule fires below.
- Risk thresholds come from `strategy.config` in the system prompt: `entryRsiThreshold`, `fullAlignmentBars`, `spotEntryPct`, `maxExposurePct`, `scalpRsiThreshold`. Defaults below assume the aggressive tier; moderate / conservative override via the config knobs.
+ **Tier override = MODERATE.** Use the moderate tier values everywhere a tier is mentioned (`entryRsiThreshold = 42`, sizing as listed below). This caps the exposure on counter-trend entries vs the aggressive default.
### Position-already-open path (`previous.vault_position_token_amount > 0`)
Test exits in priority order. First match wins.
+ **Min-hold protection (counter-trend):** When `previous.open_position_entry_path ∈ {"B", "E"}` AND `previous.open_position_ticks_since_buy < 2`, the only exits allowed are `stop` (safety) and `tp` (full take-profit). Skip `scalp`, `trailing`, and `reversal` — counter-trend entries need at least 2 tick bars to let the rebound develop. If neither stop nor tp fires within this window → `hold`.
+
**Stop-loss:** `previous.price <= previous.open_position_stop_loss_usd` → `sell`, `path: "stop"`, full close.
… 4 unchanged lines …
**Trailing exit:** the orchestrator does not have a way to compute "NET PnL" without arithmetic, so this path requires `previous.tf_15m` flipping to `SELL` AND `previous.macd_15m_histogram < 0` AND `previous.price > previous.open_position_cost_basis_usd / previous.vault_position_token_amount`. (The position is up vs cost basis AND momentum is rolling over.) → `sell`, `path: "trailing"`.
- **Trend reversal:** `previous.tf_4h ∈ {SELL, STRONG_SELL}` AND `previous.tf_daily ∈ {SELL, STRONG_SELL}` → `sell`, `path: "reversal"`.
+ **Trend reversal:** `previous.tf_4h ∈ {SELL, STRONG_SELL}` AND `previous.tf_daily ∈ {SELL, STRONG_SELL}` AND `previous.open_position_entry_path ∉ {"B", "E"}` → `sell`, `path: "reversal"`. **Excluded for B/E entries**: those paths enter deliberately counter-trend, so a still-bearish HTF is the entry premise, not a reversal — let stop/tp/scalp/trailing handle the exit.
If none fire → `hold`.
… 3 unchanged lines …
**HARD GUARD — read before everything else in this section.** When `previous.vault_position_token_amount == 0` you hold zero of the trading token, so you have NOTHING to sell. Exits (`stop`, `tp`, `scalp`, `trailing`, `reversal`) are FORBIDDEN here regardless of how bearish the timeframes look — they exist ONLY in the Position-already-open path above. The only valid emissions when position is 0 are: `hold`, or `buy` with one of the entry paths A / B / C / D / E. Emitting `{"decision":"sell", ...}` with `vault_position_token_amount == 0` is a hard contract violation: there is no inventory to dispose of and the orchestrator will reject it.
- Test in priority order. First match wins. Use `config.entryRsiThreshold` (default 45 aggressive / 42 moderate / 40 conservative) where shown.
+ Test in priority order. First match wins. Use **moderate tier**: `entryRsiThreshold = 42`.
#### Path A — Short-timeframe mean-reversion
LONG fires when ALL hold:
- - `previous.rsi_1h < config.entryRsiThreshold`
+ - `previous.rsi_1h < 42` (moderate threshold)
- `previous.rsi_15m_crossed_up_from_below_35 == true` (15m oversold reversal confirmed)
- `previous.macd_15m_flipped_positive == true` (15m momentum flipped within the last 2 bars)
- `previous.tf_daily ∉ {SELL, STRONG_SELL}` (1d not bearish — daily-flat-or-better)
- Size: `config.spotEntryPct.A` (default 50% aggressive / 35% moderate / 20% conservative).
+ Size: **35%** (moderate).
#### Path E — Capitulation oversold (BYPASS HTF wall, SPOT-ONLY)
LONG fires when ALL hold:
- - `previous.rsi_1h < 22` (aggressive), `< 27` (moderate), `< 18` (conservative). DO NOT relax — this is the extreme-tail entry.
+ - `previous.rsi_1h < 27` (moderate threshold). DO NOT relax — this is the extreme-tail entry.
- `previous.rsi_15m > 30` (some recovery from the bottom)
- `previous.macd_15m_flipped_positive == true` (early momentum flip confirmed)
- `previous.close_above_low_12_pct >= 0.3` (proof the local bottom is in)
- No HTF filter. Size: 30% aggressive / 20% moderate / 12% conservative. 60-min anti-wash window after this entry's exit (no Path A re-entry inside that window).
+ No HTF filter. Size: **20%** (moderate). 60-min anti-wash window after this entry's exit.
#### Path C — Trend continuation pullback
LONG fires when ALL hold:
- `previous.tf_buy_count >= 3` (3-of-4 timeframes BUY or STRONG_BUY)
- - `previous.rsi_1h < 60` (not yet overbought — leaves room)
+ - `previous.rsi_1h < 60` (not yet overbought)
- `previous.macd_15m_histogram > 0` (15m is going up)
- - `previous.atr_pct >= 1.0` (enough volatility for a meaningful continuation)
+ - `previous.atr_pct >= 1.0`
- `previous.regime != "bear"`
- Catches the **mid-trend pullback** that Path A (oversold) and Path D (4/4 + breakout) both miss. Size: 35% aggressive / 25% moderate / 15% conservative. 30-min anti-wash window.
+ Size: **25%** (moderate). 30-min anti-wash window.
#### Path B — Deep-value counter-trend
… 3 unchanged lines …
- `previous.tf_4h ∈ {BUY, STRONG_BUY, NEUTRAL}` (4h reversing up)
- `previous.tf_1h ∈ {BUY, STRONG_BUY}` AND `previous.rsi_15m >= 50`
+ - `previous.macd_15m_flipped_positive == true` (15m momentum confirms — required to avoid catching a falling knife)
+ - `previous.regime != "bear"` (DO NOT fire Path B inside a bear regime — historical evidence shows Path B + bear → reversal exit loop with net negative expectation)
- Size: 65% aggressive / 50% moderate / 30% conservative.
+ Size: **50%** (moderate, down from 65% aggressive).
#### Path D — Confirmed momentum
LONG fires when ALL hold:
- - `previous.tf_buy_count >= config.fullAlignmentBars` (default 4 = strict, 3 = looser)
- - `previous.breakout_last_24_periods == true` (15m close above the 24-bar high)
- - `previous.volume_confirm_15m == true` (15m vol_ratio ≥ 1.3)
+ - `previous.tf_buy_count >= 3` (moderate alignment, looser than aggressive 4/4)
+ - `previous.breakout_last_24_periods == true`
+ - `previous.volume_confirm_15m == true`
- `previous.atr_pct >= 1.5`
- Size: 65% aggressive / 50% moderate / 30% conservative.
+ Size: **50%** (moderate).
### Default
… 3 unchanged lines …
## Anti-wash trade rule
- After a full take-profit / final scalp / stop / trailing / reversal exit, the next 30 min ONLY allows Path A (oversold dip) or Path E (capitulation). Path B / Path C / Path D are forbidden in that window regardless of signal. The runtime SELL GUARD + 2h BUY cooldown back this up at the code level — your job is to not even propose a forbidden re-entry.
+ After a full take-profit / final scalp / stop / trailing / reversal exit, the next 30 min ONLY allows Path A (oversold dip) or Path E (capitulation). Path B / Path C / Path D are forbidden in that window regardless of signal.
## Output schema (your last message MUST be a single-line JSON object — no markdown fences, just one line)
… 3 unchanged lines …
```
- The orchestrator parses your LAST line as JSON. Emit it on a single line, no code fence, no trailing prose. The skip_condition `previous.decision == "hold"` requires a parseable JSON; if you wrap the line in markdown the orchestrator falls back to a string and execute spawns wastefully.
+ The orchestrator parses your LAST line as JSON. Emit it on a single line, no code fence, no trailing prose.
If `previous.unavailable == true` → `{"decision":"hold","path":null,"size_pct":null,"scalp_pct":null,"reasoning":"indicators unavailable"}`.
show full prompt (variant)
You are the **decide** stage of the trading workflow.

Apply the strict v30 rules to research's output and pick exactly one action. **NO arithmetic.** Compare values directly with the listed thresholds. Do not derive flags, do not recompute MACD/RSI/EMA, do not interpolate.

## Previous stage output

```json
{{previous}}
```

## Decision rules

**Default = HOLD.** Only emit `buy` / `sell` when an explicit rule fires below.

**Tier override = MODERATE.** Use the moderate tier values everywhere a tier is mentioned (`entryRsiThreshold = 42`, sizing as listed below). This caps the exposure on counter-trend entries vs the aggressive default.

### Position-already-open path (`previous.vault_position_token_amount > 0`)

Test exits in priority order. First match wins.

**Min-hold protection (counter-trend):** When `previous.open_position_entry_path ∈ {"B", "E"}` AND `previous.open_position_ticks_since_buy < 2`, the only exits allowed are `stop` (safety) and `tp` (full take-profit). Skip `scalp`, `trailing`, and `reversal` — counter-trend entries need at least 2 tick bars to let the rebound develop. If neither stop nor tp fires within this window → `hold`.

**Stop-loss:** `previous.price <= previous.open_position_stop_loss_usd` → `sell`, `path: "stop"`, full close.

**Full take-profit:** `previous.price >= previous.open_position_tp_target_usd` → `sell`, `path: "tp"`, full close.

**Quick scalp (33%):** `previous.price >= previous.open_position_scalp_target_usd` AND (`previous.rsi_1h >= scalpRsiThreshold` OR `previous.rsi_15m >= scalpRsiThreshold + 5`) → `sell`, `path: "scalp"`, `scalp_pct: 33`.

**Trailing exit:** the orchestrator does not have a way to compute "NET PnL" without arithmetic, so this path requires `previous.tf_15m` flipping to `SELL` AND `previous.macd_15m_histogram < 0` AND `previous.price > previous.open_position_cost_basis_usd / previous.vault_position_token_amount`. (The position is up vs cost basis AND momentum is rolling over.) → `sell`, `path: "trailing"`.

**Trend reversal:** `previous.tf_4h ∈ {SELL, STRONG_SELL}` AND `previous.tf_daily ∈ {SELL, STRONG_SELL}` AND `previous.open_position_entry_path ∉ {"B", "E"}` → `sell`, `path: "reversal"`. **Excluded for B/E entries**: those paths enter deliberately counter-trend, so a still-bearish HTF is the entry premise, not a reversal — let stop/tp/scalp/trailing handle the exit.

If none fire → `hold`.

### No-position path (`previous.vault_position_token_amount == 0`)

**HARD GUARD — read before everything else in this section.** When `previous.vault_position_token_amount == 0` you hold zero of the trading token, so you have NOTHING to sell. Exits (`stop`, `tp`, `scalp`, `trailing`, `reversal`) are FORBIDDEN here regardless of how bearish the timeframes look — they exist ONLY in the Position-already-open path above. The only valid emissions when position is 0 are: `hold`, or `buy` with one of the entry paths A / B / C / D / E. Emitting `{"decision":"sell", ...}` with `vault_position_token_amount == 0` is a hard contract violation: there is no inventory to dispose of and the orchestrator will reject it.

Test in priority order. First match wins. Use **moderate tier**: `entryRsiThreshold = 42`.

#### Path A — Short-timeframe mean-reversion

LONG fires when ALL hold:
- `previous.rsi_1h < 42` (moderate threshold)
- `previous.rsi_15m_crossed_up_from_below_35 == true` (15m oversold reversal confirmed)
- `previous.macd_15m_flipped_positive == true` (15m momentum flipped within the last 2 bars)
- `previous.tf_daily ∉ {SELL, STRONG_SELL}` (1d not bearish — daily-flat-or-better)

Size: **35%** (moderate).

#### Path E — Capitulation oversold (BYPASS HTF wall, SPOT-ONLY)

LONG fires when ALL hold:
- `previous.rsi_1h < 27` (moderate threshold). DO NOT relax — this is the extreme-tail entry.
- `previous.rsi_15m > 30` (some recovery from the bottom)
- `previous.macd_15m_flipped_positive == true` (early momentum flip confirmed)
- `previous.close_above_low_12_pct >= 0.3` (proof the local bottom is in)

No HTF filter. Size: **20%** (moderate). 60-min anti-wash window after this entry's exit.

#### Path C — Trend continuation pullback

LONG fires when ALL hold:
- `previous.tf_buy_count >= 3` (3-of-4 timeframes BUY or STRONG_BUY)
- `previous.rsi_1h < 60` (not yet overbought)
- `previous.macd_15m_histogram > 0` (15m is going up)
- `previous.atr_pct >= 1.0`
- `previous.regime != "bear"`

Size: **25%** (moderate). 30-min anti-wash window.

#### Path B — Deep-value counter-trend

LONG fires when ALL hold:
- `previous.rsi_daily < 30` AND `previous.rsi_weekly < 35`
- `previous.tf_4h ∈ {BUY, STRONG_BUY, NEUTRAL}` (4h reversing up)
- `previous.tf_1h ∈ {BUY, STRONG_BUY}` AND `previous.rsi_15m >= 50`
- `previous.macd_15m_flipped_positive == true` (15m momentum confirms — required to avoid catching a falling knife)
- `previous.regime != "bear"` (DO NOT fire Path B inside a bear regime — historical evidence shows Path B + bear → reversal exit loop with net negative expectation)

Size: **50%** (moderate, down from 65% aggressive).

#### Path D — Confirmed momentum

LONG fires when ALL hold:
- `previous.tf_buy_count >= 3` (moderate alignment, looser than aggressive 4/4)
- `previous.breakout_last_24_periods == true`
- `previous.volume_confirm_15m == true`
- `previous.atr_pct >= 1.5`

Size: **50%** (moderate).

### Default

If no path fires → `hold`.

## Anti-wash trade rule

After a full take-profit / final scalp / stop / trailing / reversal exit, the next 30 min ONLY allows Path A (oversold dip) or Path E (capitulation). Path B / Path C / Path D are forbidden in that window regardless of signal.

## Output schema (your last message MUST be a single-line JSON object — no markdown fences, just one line)

```json
{"decision":"<buy|sell|hold>","path":"<A|B|C|D|E|stop|tp|scalp|trailing|reversal|null>","size_pct":<number 0-100 or null>,"scalp_pct":<33|null>,"reasoning":"<one short sentence>"}
```

The orchestrator parses your LAST line as JSON. Emit it on a single line, no code fence, no trailing prose.

If `previous.unavailable == true` → `{"decision":"hold","path":null,"size_pct":null,"scalp_pct":null,"reasoning":"indicators unavailable"}`.

Trade-by-trade detail

Click any run to expand its trade list.

base  btcusdc 2026-05-08_base_btcusdc_gpt-oss-20b-cloud_842a13db   open ↗ 0 BUY · 0 SELL   +0.00%
sim_tssidepathamount $price $cost %realized $realized %
No trades — entry conditions never fired.
base  wethusdc 2026-05-08_base_wethusdc_gpt-oss-20b-cloud_cb9646e5   open ↗ 1 BUY · 1 SELL   +0.11%
sim_tssidepathamount $price $cost %realized $realized %
2026-02-01 18:00:00 BUY E $2000.00 $2313.96 0.52%
2026-02-01 19:00:00 SELL reversal $2010.91 $2350.97 0.52% +$10.91 +0.55%
full-counter-trend  btcusdc 2026-05-08_full-counter-trend_btcusdc_gpt-oss-20b-cloud_3a2d   open ↗ 0 BUY · 0 SELL   +0.00%
sim_tssidepathamount $price $cost %realized $realized %
No trades — entry conditions never fired.
full-counter-trend  wethusdc 2026-05-08_full-counter-trend_wethusdc_gpt-oss-20b-cloud_f14   open ↗ 0 BUY · 0 SELL   +0.00%
sim_tssidepathamount $price $cost %realized $realized %
No trades — entry conditions never fired.
full-plus-sizing  btcusdc 2026-05-08_full-plus-sizing_btcusdc_gpt-oss-20b-cloud_5c6463   open ↗ 1 BUY · 1 SELL   +0.21%
sim_tssidepathamount $price $cost %realized $realized %
2026-02-06 01:00:00 BUY E $2000.00 $63509.39 0.52%
2026-02-06 08:00:00 SELL trailing $2021.21 $64856.00 0.52% +$21.21 +1.06%
full-plus-sizing  wethusdc 2026-05-08_full-plus-sizing_wethusdc_gpt-oss-20b-cloud_4192a   open ↗ 1 BUY · 1 SELL   -0.58%
sim_tssidepathamount $price $cost %realized $realized %
2026-02-01 18:00:00 BUY E $2000.00 $2313.96 0.52%
2026-02-02 00:00:00 SELL stop $1941.79 $2270.15 0.52% $-58.21 -2.91%
no-bear-pathB  btcusdc 2026-05-08_no-bear-pathB_btcusdc_gpt-oss-20b-cloud_0cedeed8   open ↗ 1 BUY · 1 SELL   +1.62%
sim_tssidepathamount $price $cost %realized $realized %
2026-02-06 01:00:00 BUY E $2000.00 $63509.39 0.52%
2026-02-08 02:00:00 SELL tp $2162.16 $69379.57 0.52% +$162.16 +8.11%
no-bear-pathB  wethusdc 2026-05-08_no-bear-pathB_wethusdc_gpt-oss-20b-cloud_2ab0187e   open ↗ 1 BUY · 1 SELL   -0.58%
sim_tssidepathamount $price $cost %realized $realized %
2026-02-01 18:00:00 BUY E $2000.00 $2313.96 0.52%
2026-02-02 00:00:00 SELL stop $1941.79 $2270.15 0.52% $-58.21 -2.91%

Run config

VariantPairRun IDPrompts FPStartedFinished
base btcusdc 2026-05-08_base_btcusdc_gpt-oss-20b-cloud_842a13db 5f80dcb4d5d69586 2026-05-08 10:11:47 2026-05-08 16:40:54
base wethusdc 2026-05-08_base_wethusdc_gpt-oss-20b-cloud_cb9646e5 5f80dcb4d5d69586 2026-05-08 10:11:47 2026-05-08 19:23:02
full-counter-trend btcusdc 2026-05-08_full-counter-trend_btcusdc_gpt-oss-20b-cloud_3a2d8632 7a098dc44f79d94b 2026-05-08 10:11:47 2026-05-08 17:31:18
full-counter-trend wethusdc 2026-05-08_full-counter-trend_wethusdc_gpt-oss-20b-cloud_f14fe9f9 7a098dc44f79d94b 2026-05-08 10:11:47 2026-05-08 17:11:26
full-plus-sizing btcusdc 2026-05-08_full-plus-sizing_btcusdc_gpt-oss-20b-cloud_5c646356 c27d60d41e3a943f 2026-05-08 10:11:47 2026-05-08 17:32:00
full-plus-sizing wethusdc 2026-05-08_full-plus-sizing_wethusdc_gpt-oss-20b-cloud_4192a791 c27d60d41e3a943f 2026-05-08 10:11:47 2026-05-08 18:06:56
no-bear-pathB btcusdc 2026-05-08_no-bear-pathB_btcusdc_gpt-oss-20b-cloud_0cedeed8 1c4fe6a536c9d03a 2026-05-08 10:11:47 2026-05-08 18:11:35
no-bear-pathB wethusdc 2026-05-08_no-bear-pathB_wethusdc_gpt-oss-20b-cloud_2ab0187e 1c4fe6a536c9d03a 2026-05-08 10:11:47 2026-05-08 19:22:05

Generated by src/04-build-report.mjs · 8 runs · 2026-05-11 08:10:55 UTC