# Candlestick Patterns for 24/7 Crypto — Implementation Spec

## Why Classical Patterns Don't Translate to Crypto

Most classical candlestick patterns (Morning Star, Three White Soldiers, Abandoned Baby, etc.) were developed for exchange-traded equities that close overnight. Their defining feature is the **gap**: an open that is materially above or below the prior close. On a 24/7 market like BTC/Coinbase, the open of each bar equals (or is within microseconds of) the prior bar's close. Gap-dependent conditions will either never fire or fire spuriously on the tiny bid-ask spread movement between bars.

Patterns that work on crypto must be redefined purely in terms of **body size, shadow ratios, and relative position** — no gap required. The existing `f_calculateBearishEngulfingScore()` already does this correctly by dropping the `open > close[1]` gap requirement.

---

## Top 3 Patterns for a BTC Long Strategy

Selection criteria: (1) well-documented empirical edge on crypto daily/12H bars, (2) usable for both entry signal (bullish reversal) and exit signal (bearish reversal), (3) gap-free definition, (4) parameterisable for optimisation.

### 1. Bullish Hammer / Pin Bar (entry signal)

**What it looks like**

A single candle with a small body at the top of the range and a long lower shadow. Direction of the body is not the primary requirement — a slightly bearish close still counts if the lower shadow dominates. No upper shadow, or a very short one.

Conditions (no gap required):
- `lower_shadow = open > close ? (close - low) : (open - low)`
- `upper_shadow = open > close ? (high - open) : (high - close)`
- `body = math.abs(close - open)`
- `range = high - low`
- `lower_shadow / range >= min_lower_shadow_ratio` (e.g. 0.6)
- `body / range <= max_body_ratio` (e.g. 0.35)
- `upper_shadow / range <= max_upper_shadow_ratio` (e.g. 0.15)

**Signal**

Bullish reversal. Buyers rejected a deep sell-off intrabar and pushed price back up. Strong entry signal after a downtrend or oversold condition. Use with a **positive weight** in the activation score.

**Configurable parameters**

| Param | Purpose | Suggested default |
|---|---|---|
| `min_lower_shadow_ratio` | Minimum lower shadow as fraction of total range | 0.60 |
| `max_body_ratio` | Maximum body as fraction of total range | 0.35 |
| `max_upper_shadow_ratio` | Maximum upper shadow as fraction of total range | 0.15 |

**Confidence score (0.0–1.0)**

Scale on lower shadow dominance beyond the minimum threshold:

```
excess = lower_shadow / range - min_lower_shadow_ratio
max_excess = 1.0 - min_lower_shadow_ratio          // theoretical max
score = math.min(1.0, excess / max_excess)
```

Returns 0.0 if any qualifier fails.

---

### 2. Bullish Engulfing (entry signal)

**What it looks like**

A two-candle pattern: prior candle bearish, current candle bullish, and the current body fully covers the prior body. No gap required — the engulfing condition is purely body overlap.

Conditions:
- `is_prev_bearish = close[1] < open[1]`
- `is_curr_bullish = close > open`
- `is_engulfing = open <= close[1] and close >= open[1]`  (current body wraps prior body)
- Both candles pass `body / range >= body_quality_ratio`

**Signal**

Bullish reversal. Mirror image of the existing bearish engulfing. Strong entry signal, especially after a multi-bar decline. Use with a **positive weight**.

**Configurable parameters**

| Param | Purpose | Suggested default |
|---|---|---|
| `body_quality_ratio` | Min body/range for both candles | 0.60 |
| `confidence_scaling_factor` | `current_body / prior_body` value that maps to score 1.0 | 2.5 |

**Confidence score**

Identical formula to `f_calculateBearishEngulfingScore()`:

```
body_ratio = current_body / previous_body   // >= 1.0 by construction if engulfing
score = math.min(1.0, (body_ratio - 1.0) / (confidence_scaling_factor - 1.0))
```

Note: `body_quality_ratio` can share the same `i_cs_body_quality_ratio` input already in the strategy, but `confidence_scaling_factor` should be a separate input since bullish engulfings tend to be less extreme than bearish ones in a downtrend.

---

### 3. Bearish Shooting Star / Inverted Pin Bar (exit signal)

**What it looks like**

Single candle with a small body at the bottom of the range and a long upper shadow. The inverse of the hammer. Sellers rejected an intrabar rally and drove price back down.

Conditions:
- `upper_shadow = open > close ? (high - open) : (high - close)`
- `lower_shadow = open > close ? (close - low) : (open - low)`
- `body = math.abs(close - open)`
- `range = high - low`
- `upper_shadow / range >= min_upper_shadow_ratio` (e.g. 0.6)
- `body / range <= max_body_ratio` (e.g. 0.35)
- `lower_shadow / range <= max_lower_shadow_ratio` (e.g. 0.15)

**Signal**

Bearish reversal. Strong exit signal when already long, especially near a resistance level or when the score is approaching the exit threshold. Use with a **negative weight** in the activation score (penalises the score, pushing it toward the exit threshold).

**Configurable parameters**

| Param | Purpose | Suggested default |
|---|---|---|
| `min_upper_shadow_ratio` | Minimum upper shadow as fraction of total range | 0.60 |
| `max_body_ratio` | Maximum body as fraction of total range | 0.35 |
| `max_lower_shadow_ratio` | Maximum lower shadow as fraction of total range | 0.15 |

**Confidence score**

```
excess = upper_shadow / range - min_upper_shadow_ratio
max_excess = 1.0 - min_upper_shadow_ratio
score = math.min(1.0, excess / max_excess)
```

---

## Implementation Plan

### Pine Types

Add to `LibraryCandlestickPatterns.pine`:

```pine
export type HammerSettings
    float min_lower_shadow_ratio   // default 0.60
    float max_body_ratio           // default 0.35
    float max_upper_shadow_ratio   // default 0.15

export type BullishEngulfingSettings
    float body_quality_ratio        // default 0.60  (can share i_cs_body_quality_ratio)
    float confidence_scaling_factor // default 2.5

export type ShootingStarSettings
    float min_upper_shadow_ratio   // default 0.60
    float max_body_ratio           // default 0.35
    float max_lower_shadow_ratio   // default 0.15
```

### Function Signatures

```pine
export f_calculateBullishHammerScore(HammerSettings settings) => float
export f_calculateBullishEngulfingScore(BullishEngulfingSettings settings) => float
export f_calculateShootingStarScore(ShootingStarSettings settings) => float
```

All return `float` in `[0.0, 1.0]`.

### Strategy Integration

**New inputs** in `strategy_activation_scores.pine`, under the existing `group_candlestick_patterns` group:

```pine
// Hammer
i_w_bullish_hammer               = input.float(0.0, ...)
i_cs_hammer_min_lower_shadow     = input.float(0.60, ...)
i_cs_hammer_max_body_ratio       = input.float(0.35, ...)
i_cs_hammer_max_upper_shadow     = input.float(0.15, ...)

// Bullish Engulfing
i_w_bullish_engulfing            = input.float(0.0, ...)
i_cs_bull_eng_body_quality       = input.float(0.60, ...)
i_cs_bull_eng_scaling_factor     = input.float(2.5, ...)

// Shooting Star
i_w_shooting_star                = input.float(0.0, ...)
i_cs_star_min_upper_shadow       = input.float(0.60, ...)
i_cs_star_max_body_ratio         = input.float(0.35, ...)
i_cs_star_max_lower_shadow       = input.float(0.15, ...)
```

Start weights at 0.0 so existing backtest results are unaffected until optimisation.

**Score calculation** (alongside the existing `bearish_engulfing_score` block):

```pine
bullish_hammer_score   = libCandlePatterns.f_calculateBullishHammerScore(hammerSettings)
bullish_engulfing_score = libCandlePatterns.f_calculateBullishEngulfingScore(bullEngulfingSettings)
shooting_star_score    = libCandlePatterns.f_calculateShootingStarScore(shootingStarSettings)
```

**ActivationPocWeights** (in `LibraryLongEntry.pine`): add three new fields `w_bullish_hammer`, `w_bullish_engulfing`, `w_shooting_star`. Add corresponding terms to `f_calculateActivation_poc`.

**TV Export / Python parity**: add `plotchar` lines for the three new scores so they appear in the data window and can be included in a future `TV_Export.csv` re-export. Add matching columns to `library_activation_scores.py` and entries to `params_strategy_activation_scores.json` using the `i_w_*` naming convention.

### params JSON additions

```json
"i_w_bullish_hammer":    {"min": -50.0, "max": 50.0, "step": 1.0},
"i_w_bullish_engulfing": {"min": -50.0, "max": 50.0, "step": 1.0},
"i_w_shooting_star":     {"min": -50.0, "max": 50.0, "step": 1.0},
"i_cs_hammer_min_lower_shadow": {"min": 0.40, "max": 0.80, "step": 0.05},
"i_cs_hammer_max_body_ratio":   {"min": 0.20, "max": 0.50, "step": 0.05},
"i_cs_star_min_upper_shadow":   {"min": 0.40, "max": 0.80, "step": 0.05},
"i_cs_star_max_body_ratio":     {"min": 0.20, "max": 0.50, "step": 0.05},
"i_cs_bull_eng_body_quality":   {"min": 0.40, "max": 0.80, "step": 0.05},
"i_cs_bull_eng_scaling_factor": {"min": 1.5,  "max": 5.0,  "step": 0.5}
```
