Conditions & logic
20 functions
IF(COND, A, B)A where COND is true, B elsewhere. Evaluated bar by bar.
- COND(condition)
- The test, evaluated on every bar.
- A(series)
- Value where the test holds.
- B(series)
- Value where it does not.
Also written as IFF, IFELSE
// signed volume
VA := IF(CLOSE > REF(CLOSE, 1), VOL, -VOL);
// IF nests, but past two levels a chain of := reads better
LEVEL: IF(CLOSE > MA(CLOSE, 60), 2,
IF(CLOSE > MA(CLOSE, 20), 1, 0));
// hide a line on bars you do not care about
ONLYUP: IF(CLOSE > OPEN, CLOSE, DRAWNULL), COLORRED;IFN(COND, A, B)The inverse of IF — B where COND is true, A elsewhere.
- COND(condition)
- The test, evaluated on every bar.
- A(series)
- Value where the test does NOT hold.
- B(series)
- Value where it holds.
// these two lines are identical — pick whichever
// keeps the condition readable
X1: IFN(CLOSE > OPEN, LOW, HIGH);
X2: IF(CLOSE > OPEN, HIGH, LOW);BETWEEN(A, B, C)1 where A lies between B and C in either order.
- A(series)
- The value being tested.
- B(series)
- One bound.
- C(series)
- The other bound. Order does not matter.
// price inside the Bollinger band, whichever way you
// happen to have written the two bounds
MID := MA(CLOSE, 20);
INSIDE: BETWEEN(CLOSE, MID + 2 * STD(CLOSE, 20),
MID - 2 * STD(CLOSE, 20));RANGE(A, B, C)1 where A > B and A < C — an ordered version of BETWEEN.
- A(series)
- The value being tested.
- B(series)
- Lower bound — A must be strictly above it.
- C(series)
- Upper bound — A must be strictly below it.
// RSI in the neutral zone; bounds must be in this order
NEUTRAL: RANGE(RSI, 40, 60);
// with BETWEEN the same call would work either way roundEXIST(COND, N)1 if COND held at least once in the last N bars.
- COND(condition)
- The condition to look for.
- N(integer)
- How many bars back to search, including the current one.
// entry requires a recent crossover, not one right now —
// this lets the signal wait for a pullback
RECENT := EXIST(CROSS(MA5, MA20), 10);
ENTRY := RECENT AND CLOSE > OPEN AND LOW <= MA5;
DRAWICON(ENTRY, LOW, 85);EXISTR(COND, N, M)1 if COND held between N bars ago and M bars ago.
- COND(condition)
- The condition to look for.
- N(integer)
- Start of the window, in bars back — the older edge.
- M(integer)
- End of the window, in bars back — the newer edge.
// a cross happened 5 to 20 bars ago, but not since —
// the setup has had time to settle
OLD := EXISTR(CROSS(MA5, MA20), 20, 5);
NONE := NOT(EXIST(CROSS(MA5, MA20), 5));
SETTLED: OLD AND NONE;EVERY(COND, N)1 only if COND held on every one of the last N bars.
- COND(condition)
- The condition that must hold throughout.
- N(integer)
- How many consecutive bars, including the current one.
// five straight bars above the average — a trend that has
// held, not a single bar poking through
STRONG: EVERY(CLOSE > MA(CLOSE, 20), 5);
STICKLINE(STRONG, HIGH, LOW, 8, 1), COLORLIGREEN;LAST(COND, N, M)1 if COND held continuously from N bars ago until M bars ago.
- COND(condition)
- The condition that must hold throughout the window.
- N(integer)
- Start of the window, in bars back.
- M(integer)
- End of the window, in bars back.
// held above the average from 10 bars ago to 3 bars ago,
// then broke down — a failed trend
WAS := LAST(CLOSE > MA(CLOSE, 20), 10, 3);
FAIL: WAS AND CLOSE < MA(CLOSE, 20);
DRAWICON(FAIL, HIGH, 97);ANY(COND, N)1 if COND held on any of the last N bars.
- COND(condition)
- The condition to look for.
- N(integer)
- Window length in bars.
// same idea as EXIST — use whichever name reads better
// alongside the ALL on the next line
HADVOL: ANY(VOL > MA(VOL, 20) * 2, 10);
ALLUP: ALL(CLOSE > OPEN, 3);ALL(COND, N)1 if COND held on all of the last N bars.
- COND(condition)
- The condition that must hold on every bar.
- N(integer)
- Window length in bars.
// three consecutive up bars on rising volume
THREEUP: ALL(CLOSE > OPEN, 3) AND ALL(VOL > REF(VOL, 1), 3);
DRAWICON(THREEUP, LOW, 85);NDAY(X, Y, N)1 if X stayed above Y for N consecutive bars.
- X(series)
- The series that must stay on top.
- Y(series)
- The series it must stay above.
- N(integer)
- How many consecutive bars.
// the fast average has led the slow one for 5 bars —
// shorter than EVERY(MA5 > MA20, 5), same meaning
LEAD: NDAY(MA(CLOSE, 5), MA(CLOSE, 20), 5);UPNDAY(X, N)1 if X rose for N consecutive bars.
- X(series)
- The series that must keep rising.
- N(integer)
- How many consecutive rising bars.
// three higher closes in a row, often an exhaustion warning
// when it lands on an extended move
RUN3: UPNDAY(CLOSE, 3);
DRAWICON(RUN3 AND CLOSE > MA(CLOSE, 60) * 1.1, HIGH, 97);DOWNNDAY(X, N)1 if X fell for N consecutive bars.
- X(series)
- The series that must keep falling.
- N(integer)
- How many consecutive falling bars.
// three lower closes into support — a pullback candidate
PULL := DOWNNDAY(CLOSE, 3) AND CLOSE > MA(CLOSE, 60);
DRAWICON(PULL, LOW, 85);CROSS(A, B)1 on the bar where A crosses above B.
- A(series)
- The series doing the crossing.
- B(series)
- The series being crossed. May be a constant.
// MACD golden cross
DIF := EMA(CLOSE, 12) - EMA(CLOSE, 26);
DEA := EMA(DIF, 9);
DRAWICON(CROSS(DIF, DEA), LOW, 85);
DRAWICON(CROSS(DEA, DIF), HIGH, 97);
// crossing a constant works too
OVERBOUGHT: CROSS(RSI, 70);CROSSUP(A, B)Explicit upward cross — same as CROSS.
- A(series)
- The series doing the crossing.
- B(series)
- The series being crossed.
// identical to CROSS; worth using when the line below it
// is a CROSSDOWN, so the pair reads symmetrically
BUY: CROSSUP(MA(CLOSE, 5), MA(CLOSE, 20));
SELL: CROSSDOWN(MA(CLOSE, 5), MA(CLOSE, 20));CROSSDOWN(A, B)1 on the bar where A crosses below B.
- A(series)
- The series doing the crossing.
- B(series)
- The series being crossed.
// death cross; CROSSDOWN(A, B) is the same as CROSS(B, A)
DEAD: CROSSDOWN(MA(CLOSE, 5), MA(CLOSE, 20));
SAME: CROSS(MA(CLOSE, 20), MA(CLOSE, 5));
DRAWICON(DEAD, HIGH, 97);LONGCROSS(A, B, N)1 where A crosses above B after having stayed below it for N bars — filters out noise crosses.
- A(series)
- The series doing the crossing.
- B(series)
- The series being crossed.
- N(integer)
- How many bars A must have stayed below B first.
// in a sideways market CROSS fires again and again;
// LONGCROSS demands the gap was real before the cross
NOISY: CROSS(MA(CLOSE, 5), MA(CLOSE, 20));
CLEAN: LONGCROSS(MA(CLOSE, 5), MA(CLOSE, 20), 5);
DRAWICON(CLEAN, LOW, 85);FILTER(COND, N)Keep the first true bar and suppress COND for the following N bars.
The standard way to stop one signal firing repeatedly through a trend.
- COND(condition)
- The raw signal.
- N(integer)
- How many bars to stay quiet after each kept signal.
// without FILTER this fires on every bar of the run
RAW := CLOSE > MA(CLOSE, 20);
ONCE := FILTER(RAW, 10); // at most one signal per 10 bars
DRAWICON(ONCE, LOW, 85);FILTERX(COND, N)Reverse filter — suppress the N bars before each true bar instead of after.
- COND(condition)
- The raw signal.
- N(integer)
- How many bars before each kept signal to clear.
// keeps the LAST signal of each cluster instead of the first;
// it reaches backwards, so the result repaints
LATE := FILTERX(CROSS(MA5, MA20), 10);
DRAWICON(LATE, LOW, 85);TFILTER(BUY, SELL, N)Filters a buy/sell pair so the two alternate: N = 0 keeps the first of each run, 1 the last.
- BUY(condition)
- The buy signal.
- SELL(condition)
- The sell signal.
- N(integer)
- 0 keeps the first signal of each run, 1 keeps the last.
// force buy and sell to alternate — no two buys in a row,
// which is what you need before counting round trips
B := CROSS(MA(CLOSE, 5), MA(CLOSE, 20));
S := CROSS(MA(CLOSE, 20), MA(CLOSE, 5));
DRAWICON(TFILTER(B, S, 0), LOW, 85);
DRAWICON(TFILTER(S, B, 0), HIGH, 97);