Maths

24 functions

ABS(X)

Absolute value.

X(series)
The value to take the magnitude of.
// size of the gap, regardless of direction
GAP: ABS(OPEN - REF(CLOSE, 1));

// total distance travelled over 10 bars, used as a
// denominator for trend-efficiency style measures
TRAVEL: SUM(ABS(CLOSE - REF(CLOSE, 1)), 10);
MAX(A, B, …)

The largest of up to six arguments, compared bar by bar.

A(series)
First value to compare.
B(series)
Second value. Up to six may be given in total.

Also written as MAX6

// true range, spelled out — the built-in TR does the same
MTR: MAX(HIGH - LOW,
         ABS(REF(CLOSE, 1) - HIGH),
         ABS(REF(CLOSE, 1) - LOW));

// clamp a value at a floor of zero
POSITIVE: MAX(CLOSE - MA(CLOSE, 20), 0);
MIN(A, B, …)

The smallest of up to six arguments, compared bar by bar.

A(series)
First value to compare.
B(series)
Second value. Up to six may be given in total.

Also written as MIN6

// the body of the candle, whichever way it closed
BODYLOW:  MIN(OPEN, CLOSE);
BODYHIGH: MAX(OPEN, CLOSE);

// cap an oscillator at 100
CAPPED: MIN(RSI, 100);
MOD(A, B)

Remainder of A divided by B, with sign handling for mixed signs.

A(series)
The dividend.
B(series)
The divisor.
// every 5th bar — useful for thinning out labels
EVERY5 := MOD(BARPOS, 5) = 0;
DRAWNUMBER(EVERY5, HIGH, CLOSE, 2), DRAWABOVE;
POW(X, N)

X raised to the power N.

X(series)
The base.
N(float)
The exponent — not a bar count.
// squared deviation, the building block of variance
DEV2: POW(CLOSE - MA(CLOSE, 20), 2);

// weight recent bars more aggressively than a linear ramp
W := POW(0.9, CURRBARSCOUNT);
SQRT(X)

Square root.

X(series)
The value to take the root of. Negative input is invalid.
// annualise a daily standard deviation (250 trading days)
DAILY := STD(CLOSE / REF(CLOSE, 1) - 1, 20);
ANNUAL: DAILY * SQRT(250) * 100, COLORYELLOW;
EXP(X)

e raised to the power X.

X(series)
The exponent.
// undo a log transform: average in log space, then come back
LOGMA := MA(LN(CLOSE), 20);
GEOMEAN: EXP(LOGMA), COLORYELLOW;
LN(X)

Natural logarithm.

X(series)
The value. Must be positive.
// log returns add up cleanly across bars, unlike percentages
LOGRET: LN(CLOSE / REF(CLOSE, 1)) * 100, COLORSTICK;
// so a 20-bar cumulative return is just their sum
CUM: SUM(LOGRET, 20);
LOG(X)

Base-10 logarithm.

X(series)
The value. Must be positive.
// compress a volume series that spans several orders of magnitude
LOGVOL: LOG(VOL), COLORSTICK;
CEILING(X)

Round up to the next integer.

X(series)
The value to round up.
// round a price up to the next whole unit — a conservative
// resistance level to draw
RES: CEILING(HHV(HIGH, 20)), COLORGRAY, DOTLINE;
FLOOR(X)

Round down to the previous integer.

X(series)
The value to round down.
// conservative support, rounded down to a whole unit
SUP: FLOOR(LLV(LOW, 20)), COLORGRAY, DOTLINE;
INTPART(X)

Integer part, truncating toward zero. INTPART(−3.5) is −3.

X(series)
The value to truncate.
// FLOOR and INTPART agree on positives and differ on negatives:
// FLOOR(-3.5) is -4, INTPART(-3.5) is -3
STEPS: INTPART((CLOSE - OPEN) / OPEN * 100);

// derive a whole-number lookback from a ratio
LEN := 10 + INTPART(STD(CLOSE, 20) / CLOSE * 200);
FRACPART(X)

Fractional part of X.

X(series)
The value whose decimals are kept.
// how far price sits above the whole unit below it —
// prices clustering near .00 often mark round-number levels
TICKPOS: FRACPART(CLOSE);
NEARROUND: TICKPOS < 0.05 OR TICKPOS > 0.95;
ROUND(X)

Round to the nearest integer.

X(series)
The value to round.
// nearest whole-number level to the current price
LEVEL: ROUND(CLOSE), COLORGRAY, STEPLINE;
ROUND2(X, N)

Round to N decimal places.

X(series)
The value to round.
N(integer)
Number of decimal places to keep — not a bar count.
// keep two decimals for display
PCT: ROUND2((CLOSE - OPEN) / OPEN * 100, 2);

// rounding for display only — VAR2STR already formats,
// so use ROUND2 when the rounded number feeds further maths
DRAWTEXT(ISLASTBAR, HIGH, VARCAT(VAR2STR(PCT, 2), '%'));
SIGN(X)

1, 0 or −1 according to the sign of X.

X(series)
The value whose sign is taken.
// direction of the bar, as +1 / 0 / -1
DIR: SIGN(CLOSE - OPEN);

// signed volume in one line, no nested IF
FLOW: SUM(SIGN(CLOSE - REF(CLOSE, 1)) * VOL, 0), COLORYELLOW;
RAND(N)

A random integer in 1…N.

N(integer)
Upper bound of the range — not a bar count.
// a random walk to compare a strategy against —
// redraws differently on every run, so never use it in a signal
STEP := IF(RAND(2) = 1, 1, -1);
WALK: SUM(STEP, 0), COLORGRAY;
NOT(X)

1 where X is 0, otherwise 0.

X(condition)
The condition to invert.
// the first bar of a run — true now, false on the bar before
ABOVE := CLOSE > MA(CLOSE, 20);
ENTRY := ABOVE AND NOT(REF(ABOVE, 1));
DRAWICON(ENTRY, LOW, 85);
SIN(X)

Sine of X in radians.

X(series)
Angle in radians.
// a 20-bar cycle wave, for eyeballing rhythm against price
WAVE: SIN(BARPOS / 20 * 6.2832) * 100, COLORGRAY;
COS(X)

Cosine of X in radians.

X(series)
Angle in radians.
// quarter-cycle ahead of the sine wave above
WAVE2: COS(BARPOS / 20 * 6.2832) * 100, COLORLIBLUE;
TAN(X)

Tangent of X in radians.

X(series)
Angle in radians.
// slope of a trend line, expressed as a price change per bar
ANGLE := ATAN(SLOPE(CLOSE, 20));
PERBAR: TAN(ANGLE);
ASIN(X)

Arcsine. Values outside −1…1 are invalid.

X(series)
A value between −1 and 1.
// map a normalised oscillator back onto an angle
NORM := (CLOSE - MA(CLOSE, 20)) / (2 * STD(CLOSE, 20));
ANG: ASIN(MAX(MIN(NORM, 1), -1));   // clamp first, or it goes invalid
ACOS(X)

Arccosine. Values outside −1…1 are invalid.

X(series)
A value between −1 and 1.
// angle between two series, from their correlation
R := RELATE(CLOSE, VOL, 20);
ANG: ACOS(MAX(MIN(R, 1), -1));
ATAN(X)

Arctangent.

X(series)
Any value — unlike ASIN and ACOS there is no valid range to respect.
// trend angle in degrees: steeper slope, larger angle,
// but always bounded, which makes it comparable across symbols
SLP := SLOPE(CLOSE, 20) / CLOSE * 100;
DEG: ATAN(SLP) * 57.2958, COLORYELLOW;