← Library
S3

Level Reaction & Doji Strategy

Project Black-Box — Iron Sight Developer Guide — REACTION-DOJI-V1

model_id: REACTION-DOJI-V1

What does this strategy do?

This Pine Script identifies high-probability reversal points on any chart. It combines three independent signals that must all line up at the same time before it fires a trade entry. Think of it like a three-step checklist — the trade only executes when every box is ticked.

1
Key Level
Price is near a known Support or Resistance zone
2
Doji Candle
A candle of indecision forms exactly at that level
3
Reaction
The next candle breaks decisively in the reversal direction
1

Strategy Declaration

Pine Script — Header & Strategy Setup
// @S3 Development | Project Black-Box
// Level Reaction & Doji Strategy v1.3
//@version=6
strategy("Level Reaction & Doji Strategy", overlay=true,
  initial_capital=100000, default_qty_type=strategy.cash,
  currency=currency.USD)
What it does

Tells TradingView this is a backtest-capable strategy (not just an indicator). overlay=true draws everything directly on the price chart instead of a separate panel.

Why it matters

The strategy() call unlocks strategy.entry() and strategy.exit() — the commands that simulate (and ultimately trigger) live trade orders through the NIGHTHAWK pipeline.

2

Inputs & Configuration

Pine Script — User-Adjustable Parameters
// Level Settings
lookback    = input.int(20, "Swing Lookback (Bars)")
show_levels = input.bool(true, "Show Support/Resistance Lines")

// Doji Settings
doji_pct = input.float(10.0, "Doji Body % of Total Range",
             minval=1.0, maxval=30.0)

// Risk Management (Sentinel)
tp_ticks = input.int(100, "Take Profit (Ticks)")
sl_ticks = input.int(50,  "Stop Loss (Ticks)")
What it does

Creates a settings panel inside TradingView where you can adjust the strategy without touching the code. Each input.* call adds a row to the strategy's Settings dialog.

lookback (default 20)

How many bars left and right the script looks when hunting for a swing high or low. 20 means the pivot must be the highest/lowest point over a 41-bar window (20 + 1 + 20).

doji_pct (default 10%)

A candle qualifies as a Doji only if its body is smaller than 10% of its full wick-to-wick range. Lower values = stricter, rarer signals. Higher values = looser, more frequent.

tp_ticks (default 100)

Take Profit distance in ticks. SENTINEL closes the trade automatically once price moves this many ticks in your favour.

sl_ticks (default 50)

Stop Loss distance in ticks. SENTINEL ejects the trade if price moves this many ticks against you.

3

Key Level Detection — Support & Resistance

Visual — How Pivot Highs & Lows are Identified
R S Pivot High (ta.pivothigh) Pivot Low (ta.pivotlow) lookback window
Pine Script — Key Level Detection
// Find the highest point that has 'lookback' lower bars on both sides
high_level = ta.pivothigh(high, lookback, lookback)
low_level  = ta.pivotlow(low,  lookback, lookback)

var float last_res = na   // Last known Resistance
var float last_sup = na   // Last known Support

if not na(high_level)
    last_res := high_level
if not na(low_level)
    last_sup := low_level

// Draw the levels on screen
plot(show_levels ? last_res : na, "Resistance", color.new(color.red,   40), linewidth=2)
plot(show_levels ? last_sup : na, "Support",    color.new(color.green, 40), linewidth=2)
What it does

ta.pivothigh() scans back lookback bars to the left and right. If the current bar's high is the tallest in that whole window, it's a confirmed Pivot High = Resistance. Same logic applies in reverse for ta.pivotlow() = Support.

var float makes the variables persistent — they keep their last value until a new pivot is found.

Why it matters

Markets respect price memory. Institutional traders place large orders at previous swing highs/lows. By detecting these levels algorithmically, the strategy knows exactly where to watch for a reaction — the same areas other traders are watching.

4

Doji Candle Detection

Visual — Anatomy of a Doji vs. Normal Candle
Normal Candle High Close Open Low Body Range Body ~50% of Range Doji Candle High Open ≈ Close Low Body <10% Indecision — buyers & sellers equal
Pine Script — Doji Detection Logic
// How big is the candle's body (open vs close)?
body_size   = math.abs(close - open)

// How wide is the candle's full wick-to-wick range?
total_range = high - low

// Doji = body is tiny relative to the full range
is_doji = total_range > 0 and body_size <= (total_range * (doji_pct / 100))
What it does

Measures the candle's body size as a percentage of its full range. If the body is smaller than doji_pct (default 10%), is_doji becomes true for that bar. The total_range > 0 guard prevents divide-by-zero on flat bars.

Why it matters

A Doji means buyers and sellers were exactly matched during that candle — neither side won. When this happens at a key level, it tells us the level is being respected. The indecision is a warning that a reversal may be loading.

5

Reaction / Rejection Logic

Visual — The Complete 3-Condition Setup (Bullish Example at Support)
S Condition 1: Near Support Condition 2: Doji at Level ↓ bar[1] Condition 3: Reaction — breaks above Doji high & closes bullish BUY TP (+100 ticks) SL (-50 ticks) 2:1 R:R
Pine Script — Proximity Check & Reaction Detection
// How far is current price from each key level? (as a % of level price)
dist_res = math.abs(high - last_res) / last_res
dist_sup = math.abs(low  - last_sup) / last_sup

// Are we within 0.2% of a level? (close enough to "touch" it)
at_resistance = dist_res < 0.002
at_support    = dist_sup < 0.002

// Reaction: the candle AFTER the Doji breaks out decisively
bear_reaction = is_doji[1] and close < low[1]  and close < open  // Break below Doji
bull_reaction = is_doji[1] and close > high[1] and close > open  // Break above Doji
What it does

dist_res and dist_sup measure how close price is to each level as a decimal fraction. If less than 0.2%, we say price is at that level.

The [1] notation means "one bar ago". So is_doji[1] asks: "was the previous bar a Doji?"

Why it matters

The Doji alone is not a signal — it's a setup. The reaction candle is the trigger. Requiring the next candle to close beyond the Doji's range confirms that one side won the battle. We only trade when there is confirmation, not just indecision.

6

Strategy Entry Signals

Pine Script — Combining All Conditions
// LONG: Support level + Doji appeared at support + Bull reaction candle
long_condition  = bull_reaction and at_support[1]

// SHORT: Resistance level + Doji appeared at resistance + Bear reaction candle
short_condition = bear_reaction and at_resistance[1]

The "and" Chain

Pine Script's and keyword means ALL conditions must be true simultaneously. If any one of them is false, the entire expression is false and no trade fires. This is the core of the confluence model — layering filters to reduce false signals.


bull_reaction = TRUE AND at_support[1] = TRUE = LONG ENTRY FIRES

bull_reaction = FALSE AND at_support[1] = TRUE = NO TRADE — WAIT

7

Debug Logging — SYSLOG Telemetry

Pine Script — Bar-by-Bar Console Logging
log.info("SYSLOG | {0} | TF: {1} | Price: {2} | SR: [{3}/{4}] | Doji: {5} | Near: [S:{6}/R:{7}] | Signal: {8}",
  syminfo.ticker, timeframe.period, str.tostring(close),
  str.tostring(math.round(last_sup, 2)), str.tostring(math.round(last_res, 2)),
  is_doji ? "Y" : "N", at_support ? "Y" : "N", at_resistance ? "Y" : "N",
  long_condition ? "LONG" : short_condition ? "SHORT" : "NONE")
What it does

Writes a structured log line to TradingView's Pine Logs console on every single bar. You can see this in the Strategy Tester under the "Pine Logs" tab. The {0}, {1}... placeholders are filled in with live values in order.

Example SYSLOG Output

SYSLOG | BTCUSD | TF: 5 | Price: 68240.5 | SR: [67800.00/68500.00] | Doji: Y | Near: [S:N/R:Y] | Signal: NONE
SYSLOG | BTCUSD | TF: 5 | Price: 67810.2 | SR: [67800.00/68500.00] | Doji: Y | Near: [S:Y/R:N] | Signal: NONE
SYSLOG | BTCUSD | TF: 5 | Price: 68120.0 | SR: [67800.00/68500.00] | Doji: N | Near: [S:N/R:N] | Signal: LONG

The third line shows a LONG signal firing — note how the Doji was on the previous bar (now N) but conditions aligned.

8

NIGHTHAWK Signal Payload

Pine Script — Webhook JSON Construction & Strategy Entry
if long_condition
    msg = '{"secret": "tac_com_alpha_9", "model_id": "REACTION-DOJI-V1", '
        + '"ticker": "' + syminfo.tickerid + '", "action": "BUY", '
        + '"price": ' + str.tostring(close) + ', '
        + '"metadata": {"type": "Support_Doji_Reversal", '
        + '"level": ' + str.tostring(last_sup)
        + ', "timestamp": ' + str.tostring(timenow) + '}}'

    log.error("SIGNAL: Bullish Reversal Detected. Payload: {0}", msg)
    strategy.entry("DOJI_LONG", strategy.long, alert_message=msg)
What it does

When long_condition is true, it builds a JSON string and passes it to strategy.entry() as the alert_message. When TradingView fires the strategy alert, this JSON is sent as the HTTP body of a webhook POST request to our NIGHTHAWK server.

The Signal Pipeline

TradingViewstrategy.entry() fires
Webhook POSTJSON payload to port 80
NIGHTHAWKvalidates secret + model_id
SENTINEL DBlogs signal to PostgreSQL

JSON Payload Structure

{
  "secret":   "tac_com_alpha_9",      // Auth token — NIGHTHAWK rejects if wrong
  "model_id": "REACTION-DOJI-V1",   // Which strategy fired
  "ticker":   "COINBASE:BTCUSD",     // TradingView symbol ID
  "action":   "BUY",                  // or "SELL"
  "price":    68240.5,                // Close price at signal bar
  "metadata": {
    "type":      "Support_Doji_Reversal",
    "level":     67800.00,            // The support level that was respected
    "timestamp": 1745000000000        // Unix ms timestamp from TradingView
  }
}
9

Exit Logic — SENTINEL Risk Management

Pine Script — Automatic Take Profit & Stop Loss
strategy.exit("EXIT_LONG",  "DOJI_LONG",  profit=tp_ticks, loss=sl_ticks, comment="TP/SL")
strategy.exit("EXIT_SHORT", "DOJI_SHORT", profit=tp_ticks, loss=sl_ticks, comment="TP/SL")
What it does

strategy.exit() attaches automatic closing rules to each open position. profit=100 means: close the trade if it moves 100 ticks in your favour. loss=50 means: close it if it moves 50 ticks against you.

Why it matters

Automated exits remove emotion from the equation. The system never hopes a losing trade recovers — it closes it and protects capital. These parameters feed directly into SENTINEL's backtesting P&L calculations.

Default R:R Ratio

Take Profit: +100 ticks Stop Loss: -50 ticks Risk-Reward: 2:1

At 2:1 R:R, you only need to win 34% of trades to break even. The goal is to set these values based on historical win rate from the backtester.

10

3-Point Scoring Dashboard

Pine Script — On-Chart Table
var dash = table.new(position.top_right, 2, 6, border_width=1, ...)

if barstate.islast
    // Score each condition independently
    p1 = at_support or at_resistance or at_support[1] or at_resistance[1]
    p2 = is_doji or is_doji[1]
    p3 = bull_reaction or bear_reaction
    total_score = (p1 ? 1 : 0) + (p2 ? 1 : 0) + (p3 ? 1 : 0)

    // Score 3/3 → EXECUTE, otherwise → WAIT
    scoreColor = total_score == 3 ? color.new(color.green, 50) : color.new(color.gray, 50)
What it does

Creates a 2-column, 6-row table in the top-right corner of the chart. It only updates on the most recent bar (barstate.islast). Each condition is scored independently — you can see how many have aligned even before a signal fires.

Why it matters

At a glance, a trader can see whether the market is setting up (score: 2/3) or already in signal condition (score: 3/3). It replaces manual visual scanning with automated confluence tracking.

Live Dashboard Preview (Score: 3/3)

Mission Condition
Status
Structural Level
Doji Signature
Reaction Breakout
Tactical Score: 3 / 3
EXECUTE
Current Bias
BULLISH

Dashboard Preview (Score: 1/3 — Waiting)

Mission Condition
Status
Structural Level
Doji Signature
Reaction Breakout
Tactical Score: 1 / 3
WAIT
Current Bias
NEUTRAL
11

Manual Test Block — Tactical Injection

Pine Script — Force-Fire Webhook for Testing
test_trigger = input.bool(false, "FORCE MANUAL TRIGGER", group=group_sr)

if test_trigger and barstate.islast
    test_msg = '{"secret": "tac_com_alpha_9", "model_id": "REACTION-DOJI-V1", '
             + '"action": "TEST", "price": ' + str.tostring(close) + ', '
             + '"metadata": {"note": "MANUAL_TRIGGER", ...}}'
    log.info("TACTICAL: Manual Test Triggered. Payload: {0}", test_msg)
    alert(test_msg, alert.freq_all)
What it does

Adds a toggle checkbox to the strategy settings. When switched ON, it fires a test webhook on the current bar using alert() — a different mechanism from strategy.entry(). This lets you verify the NIGHTHAWK pipeline is alive without waiting for a real signal to form.

Important: Two Alert Types in TradingView

strategy.entry() alerts

Fired by TradingView when a strategy order is triggered. Set alert condition to Order fills. Used for live production signals.

alert() function alerts

Fired explicitly in code. Set alert condition to Any alert() function call. Used by the manual test block for pipeline verification.


Quick Reference — Variables Cheat Sheet

Variable Type Meaning Used For
last_res float Most recent pivot high price (Resistance) Level proximity check, chart line
last_sup float Most recent pivot low price (Support) Level proximity check, chart line
is_doji bool True if current bar's body < doji_pct% of range Setup detection, bgcolor highlight
at_resistance bool True if price is within 0.2% of last_res short_condition gate, dashboard row
at_support bool True if price is within 0.2% of last_sup long_condition gate, dashboard row
bull_reaction bool Doji on prev bar, today closes above Doji high and bullish long_condition trigger
bear_reaction bool Doji on prev bar, today closes below Doji low and bearish short_condition trigger
long_condition bool All 3 bullish conditions aligned Fires BUY signal to NIGHTHAWK
short_condition bool All 3 bearish conditions aligned Fires SELL signal to NIGHTHAWK