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)
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
elsebranches. - 🔹 Variable reassignment rules are tightly coupled to conditional logic: variables declared with
varpersist across bars and require:=for reassignment, making them essential for stateful signal tracking insideifblocks. - 🔹 The ternary operator (
?:) and theif-elseexpression form are mathematically equivalent for two-branch logic, but multi-branch conditions are more readable and maintainable asif-else if-elsechains.
Ideas for Script Advancement
- 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 theifblock to enable real-time push notifications. - 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 anif-else ifchain based on the selected mode — creating a modular, user-configurable strategy framework.
Comments
Post a Comment