Window aggregation

18 functions

HHV(X, N)

Highest value of X over the last N bars. N = 0 means since the first bar.

X(series)
The series to scan.
N(integer)
Window length in bars, including the current one. 0 scans the whole history.
// Donchian channel
DONCHIAN_UP: HHV(HIGH, 20), COLORYELLOW;
DONCHIAN_DN: LLV(LOW, 20),  COLORYELLOW;
MID: (DONCHIAN_UP + DONCHIAN_DN) / 2, COLORGRAY, DOTLINE;

// breakout of the previous 20-bar high — REF avoids comparing
// the high against a window that already contains it
BREAK := HIGH > REF(HHV(HIGH, 20), 1);
DRAWICON(BREAK, LOW, 85);
LLV(X, N)

Lowest value of X over the last N bars.

X(series)
The series to scan.
N(integer)
Window length in bars, including the current one.
// the raw stochastic — where price sits inside its 9-bar range
LOW9  := LLV(LOW, 9);
HIGH9 := HHV(HIGH, 9);
RSV: (CLOSE - LOW9) / (HIGH9 - LOW9) * 100;

// trailing stop for a long position
STOP: LLV(LOW, 10), COLORRED, STEPLINE;
HV(X, N)

Highest value excluding the current bar.

X(series)
The series to scan.
N(integer)
Window length in bars, ending on the previous bar.
// HV is the breakout comparison you usually want:
// the current high is not part of the window it is measured against
BREAK: HIGH > HV(HIGH, 20);
// equivalent to REF(HHV(HIGH, 20), 1), just shorter
SAME:  HIGH > REF(HHV(HIGH, 20), 1);
LV(X, N)

Lowest value excluding the current bar.

X(series)
The series to scan.
N(integer)
Window length in bars, ending on the previous bar.
// breakdown through the prior 20-bar low
DOWN := LOW < LV(LOW, 20);
DRAWICON(DOWN, HIGH, 97);
HHVBARS(X, N)

Number of bars since the highest value in the last N bars.

X(series)
The series to scan.
N(integer)
Window length in bars.
// 0 means the high is on this very bar
AGE: HHVBARS(HIGH, 60);
// fresh 60-bar high
DRAWICON(AGE = 0, LOW, 85);

// how long the market has gone without a new high
STALE: AGE > 20;
LLVBARS(X, N)

Number of bars since the lowest value in the last N bars.

X(series)
The series to scan.
N(integer)
Window length in bars.
// bars since the 60-bar low — a rough measure of how long
// the recovery has been running
SINCELOW: LLVBARS(LOW, 60);
DRAWTEXT(ISLASTBAR, LOW, VARCAT('bars from low: ', VAR2STR(SINCELOW, 0)));
SUM(X, N)

Rolling sum of X over N bars. N = 0 sums from the first bar.

X(series)
The series to add up.
N(integer)
Window length in bars. 0 accumulates from the very first bar.
// 20-bar turnover
TURNOVER: SUM(AMOUNT, 20);

// OBV — a running total that never resets, hence N = 0
DELTA := IF(CLOSE > REF(CLOSE, 1), VOL, IF(CLOSE < REF(CLOSE, 1), -VOL, 0));
OBV: SUM(DELTA, 0), COLORYELLOW;
SUMBARS(X, A)

How many bars back you must go for the sum of X to first reach A.

X(series)
The series to accumulate backwards.
A(series)
The target the running sum has to reach.
// how many bars it takes to accumulate today's volume —
// a small number means today traded unusually heavily
BARSNEEDED: SUMBARS(VOL, VOL);

// average price over the bars that make up 10x today's volume
N := SUMBARS(VOL, VOL * 10);
VWAP10: SUM(CLOSE * VOL, N) / SUM(VOL, N), COLORYELLOW;
COUNT(COND, N)

How many of the last N bars satisfy COND.

COND(condition)
The condition to count.
N(integer)
Window length in bars. 0 counts the whole history.
// PSY — share of up bars in the last 12
PSY: COUNT(CLOSE > REF(CLOSE, 1), 12) / 12 * 100;
PSYMA: MA(PSY, 6), COLORYELLOW;

// three or more heavy-volume bars in the last 10
CLUSTER: COUNT(VOL > MA(VOL, 20) * 2, 10) >= 3;
MULAR(X, N)

Rolling product of X over N bars.

X(series)
The series to multiply together.
N(integer)
Window length in bars. 0 multiplies from the first bar.
// compounded return over 20 bars — multiply the growth
// factors rather than adding the percentages
GROWTH := CLOSE / REF(CLOSE, 1);
COMPOUND: (MULAR(GROWTH, 20) - 1) * 100, COLORSTICK;
HOD(X, N)

Rank of the current X among the highest values of the last N bars.

X(series)
The series to rank within.
N(integer)
Window length in bars.
// 1 means today is the highest of the last 60 bars,
// 2 the second highest, and so on
RANK: HOD(HIGH, 60);
DRAWICON(RANK <= 3, LOW, 85);   // among the three highest
LOD(X, N)

Rank of the current X among the lowest values of the last N bars.

X(series)
The series to rank within.
N(integer)
Window length in bars.
// today is among the three lowest closes of the last 60
OVERSOLD: LOD(CLOSE, 60) <= 3;
STICKLINE(OVERSOLD, HIGH, LOW, 8, 1), COLORLIGREEN;
TOPRANGE(X)

How many bars back you can go before X was last higher than it is now.

X(series)
The series to measure.
// the window is not fixed — it answers "a high of how many bars?"
SPAN: TOPRANGE(HIGH);
// a 250-bar high without having to guess the number first
DRAWICON(SPAN >= 250, LOW, 85);
LOWRANGE(X)

How many bars back you can go before X was last lower than it is now.

X(series)
The series to measure.
// how deep this low reaches into the past
SPAN: LOWRANGE(LOW);
DRAWTEXT(SPAN >= 120, LOW, 'multi-month low'), COLORRED, VALIGN2;
FINDHIGH(X, N, M, T)

The T-th highest value of X inside the M bars that end N bars ago.

X(series)
The series to scan.
N(integer)
Where the window ends, counted back from the current bar.
M(integer)
Length of that window, in bars.
T(integer)
Which ranked value to take — 1 is the highest, 2 the second highest.
// second-highest high of the 60 bars that ended 10 bars ago —
// a resistance level that ignores the most recent action
RES: FINDHIGH(HIGH, 10, 60, 2), COLORGRAY, DOTLINE;
FINDHIGHBARS(X, N, M, T)

Bars from that T-th highest value to the current bar.

X(series)
The series to scan.
N(integer)
Where the window ends, counted back from the current bar.
M(integer)
Length of that window, in bars.
T(integer)
Which ranked value to locate.
// where that resistance came from, and how long ago
AGE: FINDHIGHBARS(HIGH, 10, 60, 2);
DRAWTEXT(ISLASTBAR, HIGH, VARCAT('res set ', VAR2STR(AGE, 0), ' bars ago'));
FINDLOW(X, N, M, T)

The T-th lowest value of X inside the M bars that end N bars ago.

X(series)
The series to scan.
N(integer)
Where the window ends, counted back from the current bar.
M(integer)
Length of that window, in bars.
T(integer)
Which ranked value to take — 1 is the lowest.
// support from the 60 bars that ended 10 bars ago
SUP: FINDLOW(LOW, 10, 60, 1), COLORGRAY, DOTLINE;
// price coming back down to it
DRAWICON(LOW <= SUP AND CLOSE > SUP, LOW, 85);
FINDLOWBARS(X, N, M, T)

Bars from that T-th lowest value to the current bar.

X(series)
The series to scan.
N(integer)
Where the window ends, counted back from the current bar.
M(integer)
Length of that window, in bars.
T(integer)
Which ranked value to locate.
// distance back to the support low, used to fade
// levels that are getting old
AGE: FINDLOWBARS(LOW, 10, 60, 1);
FRESH: AGE < 40;