CODESCRIPT

Conditions and comparison

A strategy is really a list of "if this happens, do that" rules. This page shows how you build a condition, how you combine several of them, and how to get past the one thing that trips up almost everyone: a cross is a moment, not a state.

What a condition is

Your script runs again from the top on every bar. So a comparison does not give you one answer; it gives you one answer per bar. A condition is a series too. Where the close series carries a number on each bar, a condition series carries true or false.

Once that clicks, the rest follows. Drawing functions place a mark on the bars where the condition holds and draw nothing on the rest. Strategy functions send their order on the bar where it holds.

Comparison operators

There are six operators, and each compares its two sides bar by bar. Both sides can be series, or one side can be a plain number.

  • a > b — true when a is greater than b
  • a < b — true when a is less than b
  • a >= b — true when a is greater than or equal to b
  • a <= b — true when a is less than or equal to b
  • a == b — true when the two sides are equal (a single equals sign assigns, it does not compare)
  • a != b — true when the two sides differ
CODESCRIPT
ortalama = sma(close, 20)

// bu satir tek bir cevap degil, bar basina bir cevap uretir
ustunde = close > ortalama

plot(ortalama, "SMA20", "#787b86")
plotshape(ustunde, style="circle", location="belowbar", color="#26a69a")

A grey SMA20 line on the price panel. A green dot under EVERY bar whose close sits above that line, and nothing on the bars below it.

TIP

Give the condition a name. A short label like "above" makes the rule readable and saves you from repeating the same comparison in three places.

Combining conditions with and, or, not

A single comparison is rarely enough. A real rule usually wants several things to line up at once. Three keywords handle that, and all three work bar by bar.

  • and — the result is true only when both sides hold. It adds a filter, so it thins out your signals.
  • or — the result is true when either side holds. It admits alternatives, so it produces more signals.
  • not — flips a condition. What was true becomes false, and the other way around.
CODESCRIPT
trendYukari = close > sma(close, 50)
hacimli = volume > sma(volume, 20)
asiriAlim = rsi(close, 14) > 70

// uc sart birden: trend yukari, hacim ortalama ustu, henuz asiri alimda degil
girisSarti = trendYukari and hacimli and not asiriAlim

plotshape(girisSarti, style="triangleup", location="belowbar", color="#26a69a")

A green triangle only under the bars where all three hold at once. If even one of them fails, that bar gets no mark.

CAUTION

and binds tighter than or. Written as is, "a or b and c" means "a or (b and c)". If that is not what you meant, add parentheses — they also make your intent obvious when you come back to the script later.

A cross is an event, not a state

This is where most people get stuck. In "buy when the fast average crosses above the slow one", the word "crosses" points at a single moment. But if you translate that into "fast > slow", you have written a state that stays true on every bar after the cross, not the moment itself.

See the difference on the chart

CODESCRIPT
hizli = sma(close, 10)
yavas = sma(close, 30)

// DURUM: hizli, yavasin ustunde oldugu SURECE dogru
durum = hizli > yavas

// OLAY: yalnizca ustune GECTIGI barda dogru
olay = crossover(hizli, yavas)

plot(hizli, "SMA10", "#26a69a")
plot(yavas, "SMA30", "#ef5350")
plotshape(durum, style="circle", location="belowbar", color="#787b86")
plotshape(olay, style="triangleup", location="abovebar", color="#26a69a")

An unbroken run of grey dots under the bars for as long as the fast average stays on top. The green triangle appears just once, above the first bar of that run. The difference is visible at a glance.

CAUTION

Hook your entry to the state and a forty-bar uptrend hands you forty signals. Your signal count explodes and your backtest results stop meaning anything. When you want a moment, reach for crossover or crossunder.

What crossover actually does

A cross is not one comparison but two joined together: above on this bar, not above on the one before. You can spell that out yourself — crossover is only a shorthand for it. Drawing both and watching them land on the same bars is the quickest way to make the idea stick.

CODESCRIPT
hizli = sma(close, 10)
yavas = sma(close, 30)

// bu barda ustunde VE onceki barda ustunde degil
elle = hizli > yavas and prev(hizli, 1) <= prev(yavas, 1)

plotshape(elle, style="triangleup", location="belowbar", color="#2962ff")
plotshape(crossover(hizli, yavas), style="triangledown", location="abovebar", color="#ff9800")

A blue triangle below the bar and an orange one above it — always on the same bars. It shows that crossover is nothing but a shorthand.

Crossing a fixed threshold

The second side does not have to be a series; a plain number works too. The same distinction applies here. "RSI is below 70" is a state; "RSI dropped through 70" is an event.

CODESCRIPT
guc = rsi(close, 14)

plot(guc, "RSI", "#2962ff", "rsi")

// asiri alimdan CIKIS ani: sadece 70'in altina dustugu bar
plotshape(crossunder(guc, 70), style="arrowdown", location="abovebar", color="#ef5350")

The RSI line in the lower "rsi" panel. On the price panel, a single red arrow on the bar where RSI drops back under 70. It does not repeat while RSI stays above 70.

Wiring a cross to an order

The rule carries over: entries and exits hang off events. The skeleton below sits underneath most trend-following strategies — swap the entry and exit conditions and a lot of other strategies grow out of it.

CODESCRIPT
hizli = sma(close, 10)
yavas = sma(close, 30)

// giris ve cikis olaya bagli: her yukselis barinda degil, donus barinda
enterLong(crossover(hizli, yavas))
exit(crossunder(hizli, yavas))

plot(hizli, "SMA10", "#26a69a")
plot(yavas, "SMA30", "#ef5350")

A buy mark on every up-cross and an exit mark on every down-cross. Between the two marks you see one long trade, and no new entry opens on the bars in between.

NOTE

When you add a filter, keep the shape intact: the event stays at the core and states hang off it with and. Write crossover(hizli, yavas) and close > sma(close, 200) and the entry still fires on a single bar, but only while the longer trend points up.

Choosing a value with iff

So far you have used a condition to draw a mark or to place an order. Sometimes that is not what you want: you want to look at the condition and pick one of two values. That is exactly what iff does. It takes three things — the condition, the value for true, the value for false — and returns one of them per bar.

Colour is its most common use. The other one is picking a number: a threshold, a distance, a multiplier.

CODESCRIPT
// yukselis barinda yesil, dusus barinda kirmizi cizgi
plot(close, color=iff(close > open, "#26a69a", "#ef5350"))

// iff sayi da secer: trend zayifken zarar-kes mesafesi genisler
mesafe = iff(rsi(close, 14) > 50, atr(14), atr(14) * 2)
plot(close - mesafe, "zarar-kes seviyesi", "#ff9800")

The close line changes colour bar by bar: green on up bars, red on down bars. Below it an orange stop-loss line, which visibly backs away from price whenever RSI slips under 50.

Need more than two outcomes? Nest one iff inside another — put a second iff on the false branch. For readability it is worth stopping after the third level.

CODESCRIPT
guc = rsi(close, 14)

// uc durum: guclu, zayif, arada
renk = iff(guc > 60, "#26a69a", iff(guc < 40, "#ef5350", "#787b86"))

plot(guc, "RSI", renk, "rsi")

// na verirsen o bar hic boyanmaz — sessiz bolgeyi bos birakmanin yolu
bgcolor(iff(guc > 60, rgb(38, 166, 154, 0.12), iff(guc < 40, rgb(239, 83, 80, 0.12), na)))

The RSI line in the lower panel turns green above 60, red below 40 and grey in between. The background echoes the same three zones in faint tones and stays clear between 40 and 60.

TIP

iff re-evaluates its condition on every bar; it remembers no earlier decision. When you need something like "since the last signal", barssince is the tool, and valuewhen gives you the value recorded at that signal.

Function reference

crossover(a, b)

A condition series that is true on the bar where a crosses above b and false on every other bar.

a
series
The side moving up. Usually the fast average, the price, or an indicator.
b
series or number
The side being crossed. Either a series or a fixed threshold such as 70.

crossunder(a, b)

A condition series that is true on the bar where a crosses below b. It is the downward counterpart of crossover.

a
series
The side moving down.
b
series or number
The side being crossed, or a fixed threshold.

cross(a, b)

A condition series that is true on the crossing bar, whichever way the two series cross.

a
series
The first series.
b
series
The second series. When direction matters, use crossover or crossunder instead.

iff(condition, ifTrue, ifFalse)

A series carrying ifTrue on the bars where the condition holds and ifFalse on the rest.

condition
condition
The true/false series, evaluated bar by bar.
ifTrue
number or colour
The value taken while the condition holds.
ifFalse
number or colour
The value taken while it does not. Pass na instead of a colour and that bar is left undrawn.

nz(source, replacement=0)

The source series with its na values swapped for a number, everything else untouched.

source
series
A series that may hold empty values. On the first bars an indicator has not produced a value yet.
replacement
number
The number that stands in for an empty value. Defaults to 0.

Common mistakes

  • Writing the state where you meant the cross. enterLong(hizli > yavas) fires on every bar of the uptrend; if you meant the turn, write enterLong(crossover(hizli, yavas)).
  • Using a single equals sign to compare. One equals sign binds a value to a name; comparing needs two.
  • Mixing and with or and no parentheses. Because and binds first, the rule quietly says something other than what you read.
  • Forgetting the empty values on the early bars. An indicator produces nothing until it warms up, so comparisons on those bars come out wrong. Wrap them in nz to keep the rule solid.
  • Repeating the same comparison in three places. Bind it to a name — then you change it in one spot and it is computed once.
  • Stacking too many ands. Every filter cuts your signal count, and with five conditions piled up the rule barely fires at all in a backtest.