millionchart
فارسی

Samples: sessions, patterns and volume

Sample programs for sessions, candle patterns and volume. Session boxes, an opening range, inside bars, swings, OBV, volume spikes and a table.

These programs work with the time of day, with the shape of the candles and with volume. The session programs make most sense on an intraday chart.

Session range boxes#

A box over every finished Tokyo session, from its high to its low.

Cuprel
// A box over every finished session, from its high to its low. The members of a declared session tell where the bar stands in it:
// .start is true on its first bar, .end on its last, .active while the bar is inside, .index counts the sessions, and .high and .low are the extremes so far.
indicator "Session range boxes"
    edition = 1
    display = price
    max_boxes = 100

session asia = 00:00 .. 08:00 @ "Asia/Tokyo"
state first_bar = 0
if asia.start
    first_bar = bar.index
if asia.end
    box.new(
        first_bar,
        asia.high,
        bar.index,
        asia.low,
        border_color = color.purple,
        background = color.fade(color.purple, 90),
    )
plot(
    if asia.active then asia.high - asia.low else na,
    name = "Range so far",
    style = histogram,
    color = color.purple,
)
plot(asia.index, name = "Session number", display = none)

What it teaches: a named session with its hours and time zone, and its members: .start and .end on its first and last bar, .active while the bar is inside it, .high and .low so far, and .index.

Opening range#

The high and low of the first hour of the London session, and a mark where the price closes outside that range later in the day.

Cuprel
// The opening range: the high and low of the first hour of the London session, and a mark where the price closes outside it later that day.
// The crossing tests keep a state, so they are worked out on every bar first and only then combined with "not in the range hour".
indicator "Opening range"
    edition = 1
    display = price

session opening = 08:00 .. 09:00 @ "Europe/London"
state top: float = na
state bottom: float = na
if opening.start
    top = high
    bottom = low
else if opening.active
    top = math.max(top, high)
    bottom = math.min(bottom, low)
let crossed_up = tech.crosses_above(close, top)
let crossed_down = tech.crosses_below(close, bottom)
let after = not opening.active
plot(if after then top else na, name = "Range high", color = color.green, style = step_break)
plot(if after then bottom else na, name = "Range low", color = color.red, style = step_break)
mark.shape(
    crossed_up and after,
    name = "Break up",
    shape = triangle_up,
    place = below,
    color = color.green,
)
mark.shape(
    crossed_down and after,
    name = "Break down",
    shape = triangle_down,
    place = above,
    color = color.red,
)

What it teaches: state values that are set at the start of the session and widened while it lasts, and why the crossing tests are written on lines of their own before they are combined with a condition (see Reading the editor's messages).

Inside bars#

An inside bar has its high below and its low above the bar before it. The script marks each one, and marks a break when a later close leaves the range of the bar before it.

Cuprel
// A candlestick pattern from its definition: an inside bar has a high below and a low above the bar before it.
// A close above the mother bar's high after an inside bar is marked as a break.
indicator "Inside bars"
    edition = 1
    display = price

let inside = high < high[1] and low > low[1]
let broke_up = inside[1] and close > high[2]
let broke_down = inside[1] and close < low[2]
mark.char(inside, name = "Inside bar", char = "i", place = above, color = color.gray)
mark.shape(broke_up, name = "Break up", shape = triangle_up, place = below, color = color.green)
mark.shape(broke_down, name = "Break down", shape = triangle_down, place = above, color = color.red)

What it teaches: conditions on earlier bars with [1] and [2], mark.char with a letter and mark.shape with a triangle.

Swing pivots#

A label with the price at every swing high and swing low.

Cuprel
// Swing structure: a swing high is a bar whose high is the highest of `left` bars before it and `right` bars after it.
// The pivot is only known `right` bars later, so the label is placed back on the bar where the swing happened.
indicator "Swing pivots"
    edition = 1
    display = price
    max_labels = 200

input left: int = 5, name = "Bars on the left", min = 1
input right: int = 5, name = "Bars on the right", min = 1
let swing_high = tech.pivot_high(high, left, right)
let swing_low = tech.pivot_low(low, left, right)
if not is_na(swing_high)
    label.new(
        bar.index - right,
        swing_high,
        f"{swing_high:price}",
        style = label_down,
        color = color.red,
    )
if not is_na(swing_low)
    label.new(
        bar.index - right,
        swing_low,
        f"{swing_low:price}",
        style = label_up,
        color = color.green,
    )
plot(close, name = "Close", color = color.gray)

What it teaches: tech.pivot_high and tech.pivot_low, label.new, and max_labels in the header. A swing is only known right bars after it happened, so the label is placed back on the bar of the swing; Results that look too good explains what that means for a signal. The script keeps the newest 200 labels (max_labels = 200); on a chart with many bars the oldest are dropped and the engine says so with the warning W-OBJECT-LIMIT. Raise max_labels (the chart allows up to 500) or use a longer timeframe.

On-balance volume#

A running total that adds the volume on an up close and takes it away on a down close, with a smoothed signal line.

Cuprel
// On-Balance Volume: add the volume on an up close, subtract it on a down close, and smooth the running total.
indicator "On-balance volume"
    edition = 1
    display = pane

input length: int = 20, name = "Signal length", min = 1
let direction = math.sign(close - close[1])
let obv = tech.cum_sum(direction * volume)
plot(obv, name = "OBV", color = color.blue)
plot(tech.ema(obv, length), name = "Signal", color = color.orange)

What it teaches: math.sign, tech.cum_sum and volume.

Volume spikes#

Bars whose volume is far above its recent average are marked, and their columns are coloured.

Cuprel
// Volume spikes: a bar whose volume is far above its recent average is marked and its column is coloured.
indicator "Volume spikes"
    edition = 1
    display = pane

input length: int = 20, name = "Average length", min = 2
input factor: float = 2.0, name = "Spike factor", min = 1.0, step = 0.1
let average = tech.sma(volume, length)
let spike = volume > average * factor
plot(volume, name = "Volume", style = columns, color = if spike then color.orange else color.gray)
plot(average, name = "Average", color = color.blue)
mark.shape(spike, name = "Spike", shape = circle, place = top, color = color.orange)

What it teaches: the columns style with a colour per bar, and a mark at the top of the pane.

Status table#

A small table in the corner with the close, the RSI and the ATR, written on the newest bar only.

Cuprel
// A small table in the corner of the chart, written on the last bar only.
indicator "Status table"
    edition = 1
    display = price

input length: int = 14, name = "Length", min = 2
let strength = tech.rsi(close, length)
let volatility = tech.atr(length)
if bar.is_last
    let t = table.new(
        top_right,
        2,
        3,
        background = color.fade(color.black, 70),
        border_color = color.gray,
        border_width = 1,
    )
    table.cell(t, 0, 0, "Close", text_color = color.silver, h_align = left)
    table.cell(t, 1, 0, f"{close:price}", text_color = color.white)
    table.cell(t, 0, 1, "RSI", text_color = color.silver, h_align = left)
    table.cell(t, 1, 1, f"{strength:.1f?'n/a'}", text_color = color.white)
    table.cell(t, 0, 2, "ATR", text_color = color.silver, h_align = left)
    table.cell(t, 1, 2, f"{volatility:price?'n/a'}", text_color = color.white)
plot(close, name = "Close", color = color.gray)

What it teaches: table.new and table.cell, and f-texts such as f"{close:price}", which write a number with the symbol's own decimals; ?'n/a' is written while the value is na.