Pine Script 'if' Statements: A Complete Guide to Conditional Logic for Buy Signals and Color Changes

Conditional logic is the backbone of any algorithmic trading script — without it, indicators and strategies cannot respond dynamically to market conditions. In Pine Script v6, the if statement provides a structured, deterministic mechanism to evaluate boolean expressions and execute specific branches of code, enabling everything from simple color changes to complex multi-condition buy/sell signal generation.

1. The Anatomy of an if Statement in Pine Script

At its core, an if statement evaluates a boolean condition and executes a block of code only when that condition resolves to true. The general syntax follows this structure:

//@version=6
// Basic if-else structure (conceptual)
// if condition
//     // executed when condition is true
// else
//     // executed when condition is false

Pine Script uses indentation-based block scoping. Any statements belonging to an if branch must be consistently indented relative to the if keyword. Inconsistent nesting — not a specific space count — causes compilation errors.

2. Core Syntax Variants

Pine Script supports four primary forms of conditional branching:

Form Syntax Use Case
if only if condition Execute code on true; do nothing on false
if-else if condition ... else ... Binary branching logic
if-else if-else if ... else if ... else ... Multi-condition chaining
Ternary operator condition ? valueA : valueB Inline single-expression conditional

3. How Pine Script Evaluates Conditions: Boolean Logic

Every condition inside an if statement must resolve to a bool type. Pine Script supports the following comparison and logical operators:

  • Comparison: ==, !=, >, <, >=, <=
  • Logical AND: and
  • Logical OR: or
  • Logical NOT: not

A compound condition combining AND and OR follows standard boolean algebra precedence: not is evaluated first, then and, then or. Parentheses can override this order explicitly.

Mathematically, a compound condition such as:

$$C = (A \land B) \lor (\lnot D)$$

translates directly to Pine Script as:

C = (A and B) or (not D)
flowchart TD A["Bar Closes — New Data Available"] --> B{"Evaluate Boolean Condition"} B -- "true" --> C["Execute if-block statements"] B -- "false" --> D{"else if branch exists?"} D -- "yes" --> E{"Evaluate else-if Condition"} E -- "true" --> F["Execute else-if block statements"] E -- "false" --> G{"else branch exists?"} D -- "no" --> G G -- "yes" --> H["Execute else block statements"] G -- "no" --> I["Skip — no action taken"] C --> J["Continue to next Pine Script line"] F --> J H --> J I --> J

4. Practical Example 1 — Simple Buy Signal with if

The following script demonstrates a basic buy signal triggered when a fast EMA crosses above a slow EMA, using a plain if statement to plot a label on the chart.

🔽 [Click to expand] View Full Pine Script Code
//@version=6
indicator(title="EMA Crossover Buy Signal", overlay=true)

// --- Inputs ---
fastLen = input.int(9,  title="Fast EMA Length", minval=1)   // Fast EMA period
slowLen = input.int(21, title="Slow EMA Length", minval=1)   // Slow EMA period

// --- EMA Calculations ---
fastEMA = ta.ema(close, fastLen)   // Calculate fast EMA
slowEMA = ta.ema(close, slowLen)   // Calculate slow EMA

// --- Plot EMAs ---
plot(fastEMA, color=color.blue,   title="Fast EMA", linewidth=1)
plot(slowEMA, color=color.orange, title="Slow EMA", linewidth=1)

// --- Crossover Detection ---
// ta.crossover returns true on the exact bar where fastEMA crosses above slowEMA
bullishCross = ta.crossover(fastEMA, slowEMA)

// --- Conditional Buy Signal ---
// The if block executes ONLY when bullishCross is true
if bullishCross
    // Draw a label below the bar to mark the buy signal
    label.new(
         x       = bar_index,
         y       = low,
         text    = "BUY",
         style   = label.style_label_up,
         color   = color.green,
         textcolor = color.white,
         yloc    = yloc.belowbar
         )


5. Practical Example 2 — Dynamic Bar Color with if-else

The if-else construct is ideal for binary state assignments — for example, coloring bars based on whether the RSI is overbought or oversold. Note that in Pine Script, an if-else block can return a value directly when used as an expression.

🔽 [Click to expand] View Full Pine Script Code
//@version=6
indicator(title="RSI-Based Bar Coloring", overlay=true)

// --- RSI Calculation ---
rsiLen    = input.int(14, title="RSI Length", minval=1)   // RSI period
rsiValue  = ta.rsi(close, rsiLen)                         // Compute RSI

// --- Threshold Inputs ---
overbought = input.int(70, title="Overbought Level")   // Upper threshold
oversold   = input.int(30, title="Oversold Level")     // Lower threshold

// --- if-else used as an expression to assign a color ---
// The entire if-else block resolves to a single color.rgb value
barCol = if rsiValue >= overbought
    color.red                          // Overbought: color bar red
else if rsiValue <= oversold
    color.green                        // Oversold: color bar green
else
    color.white                         // Neutral zone: color bar gray
// --- Apply the computed color to bars --- barcolor(barCol, title="RSI Bar Color") // --- Optional: Plot RSI in a separate pane for reference --- // (Requires a separate indicator() call or use of a panel)

This pattern leverages a critical Pine Script feature: an if-else block used as an expression. The last evaluated statement in each branch becomes the return value of the entire block, which is then assigned to barCol. This is equivalent to a nested ternary:

$$\text{barCol} = \begin{cases} \text{red} & \text{if } RSI \geq 70 \\ \text{green} & \text{if } RSI \leq 30 \\ \text{gray} & \text{otherwise} \end{cases}$$

6. Practical Example 3 — Multi-Condition Strategy Entry with if and var

In strategy scripts, if statements are used to gate strategy.entry() and strategy.close() calls. The following example combines a trend filter (price above SMA) with a momentum filter (RSI crossing above 50) to produce a higher-quality entry signal. A var variable tracks the last signal state.

🔽 [Click to expand] View Full Pine Script Code
//@version=6
strategy(
     title     = "Trend + Momentum Strategy",
     overlay   = true,
     default_qty_type  = strategy.percent_of_equity,
     default_qty_value = 10
     )

// --- Inputs ---
smaLen = input.int(50,  title="SMA Trend Filter Length", minval=1)
rsiLen = input.int(14,  title="RSI Length",              minval=1)
rsiMid = input.int(50,  title="RSI Mid Level",           minval=1, maxval=99)

// --- Calculations ---
smaValue = ta.sma(close, smaLen)          // 50-period SMA for trend direction
rsiValue = ta.rsi(close, rsiLen)          // 14-period RSI for momentum

// --- Trend Filter: price must be above SMA ---
aboveTrend = close > smaValue

// --- Momentum Filter: RSI crosses above mid-level ---
rsiCrossUp = ta.crossover(rsiValue, rsiMid)

// --- var: initialized once, persists across all bars ---
// Tracks whether we are currently in a long position
var bool inLong = false

// --- Entry Condition: both filters must be true simultaneously ---
if aboveTrend and rsiCrossUp and not inLong
    strategy.entry("Long", strategy.long)   // Submit long entry order
    inLong := true                           // Update state flag (:= required for var reassignment)

// --- Exit Condition: RSI drops below mid-level ---
if inLong and rsiValue < rsiMid
    strategy.close("Long")                  // Close the long position
    inLong := false                          // Reset state flag

// --- Visuals ---
plot(smaValue, color=color.purple, title="SMA Trend Filter", linewidth=2)
bgcolor(aboveTrend ? color.new(color.green, 90) : na, title="Trend Zone")

Notice the use of := when reassigning inLong. Because inLong was declared with var, it is initialized only once on the first processed bar and retains its value across subsequent bars. Reassignment of a var-declared variable requires the := operator.



7. The Ternary Operator as an Inline if-else

For simple single-expression conditionals, the ternary operator condition ? valueA : valueB is more concise than a full if-else block. It is functionally equivalent to a two-branch if-else expression:

//@version=6
indicator("Ternary Example", overlay=true)

// Ternary: assign green if close > open (bullish bar), else red
candleColor = close > open ? color.green : color.red

// Apply color to candles
barcolor(candleColor)

Nested ternaries are syntactically valid but reduce readability significantly. For three or more branches, prefer the if-else if-else block form shown in Example 2.

8. Common Pitfalls and Anti-Patterns

Pitfall Incorrect Pattern Correct Pattern
Using = instead of := for var reassignment var x = 0 ... x = 1 var x = 0 ... x := 1
Omitting //@version=6 Script defaults to v1; indicator() fails Always include //@version=6 at line 1
Inconsistent indentation inside if block Mixing 2-space and 4-space indentation in the same block Use consistent indentation throughout each block
Calling strategy.entry() outside an if guard Entry fires on every bar unconditionally Always gate entries with an explicit if condition

9. Conclusion

  • 🔹 if statements in Pine Script are indentation-scoped, boolean-driven control structures that can be used both as standalone execution gates and as value-returning expressions when combined with else branches.
  • 🔹 Variable reassignment rules are tightly coupled to conditional logic: variables declared with var persist across bars and require := for reassignment, making them essential for stateful signal tracking inside if blocks.
  • 🔹 The ternary operator (?:) and the if-else expression form are mathematically equivalent for two-branch logic, but multi-branch conditions are more readable and maintainable as if-else if-else chains.

Ideas for Script Advancement

  1. Add alert conditions: Extend the buy signal example by adding alertcondition(bullishCross, title="EMA Cross Alert", message="Fast EMA crossed above Slow EMA") inside or after the if block to enable real-time push notifications.
  2. Parameterize condition logic: Use input.string() with a dropdown to let users select between multiple entry condition modes (e.g., "EMA Cross Only", "EMA + RSI Filter", "RSI Only"), then route execution through an if-else if chain based on the selected mode — creating a modular, user-configurable strategy framework.

Comments

Popular posts from this blog

Pine Script v6: indicator() vs strategy() — Core Functional Differences Explained

Pine Script Variable Declaration: Mastering var, varip, and Proper Initialization Across Historical Bars

Pine Script v6 plot() Function: Complete Guide to Lines, Histograms, and Circles