millionchart
فارسی

Samples: candle patterns

Sample programs that find candle patterns from their definitions. Engulfing bars, doji, hammers, runs of bars, gaps, outside bars and the close position.

A candle pattern is a rule about the open, high, low and close of one bar and the bars before it. Each program here writes the rule out as a condition and marks the bars where it is true. A pattern describes what the price did; it does not forecast what it will do. Read Results that look too good before you trade on one.

Engulfing bars#

A bar whose body covers the whole body of the bar before it, closing the other way.

Cuprel
// Engulfing bars: a bar whose body covers the whole body of the bar before it, in the other direction.
// A bullish engulfing bar is up and its body runs from below the previous open to above the previous close.
indicator "Engulfing bars"
    edition = 1
    display = price

let body_top = math.max(open, close)
let body_bottom = math.min(open, close)
let bullish = close > open
    and close[1] < open[1]
    and body_top >= body_top[1]
    and body_bottom <= body_bottom[1]
let bearish = close < open
    and close[1] > open[1]
    and body_top >= body_top[1]
    and body_bottom <= body_bottom[1]
mark.shape(
    bullish,
    name = "Bullish engulfing",
    shape = triangle_up,
    place = below,
    color = color.green,
)
mark.shape(
    bearish,
    name = "Bearish engulfing",
    shape = triangle_down,
    place = above,
    color = color.red,
)

What it teaches: the body of a bar with math.max(open, close) and math.min(open, close), comparisons with the bar before through [1], and two marks from two conditions.

Doji#

A bar whose body is very small next to its whole range.

Cuprel
// Doji: a bar whose body is very small next to its whole range, so the open and the close are almost the same price.
indicator "Doji"
    edition = 1
    display = price

input ratio: float = 0.1,
    name = "Largest body (share of the range)",
    min = 0.01,
    max = 0.5,
    step = 0.01
let range = high - low
let body = math.abs(close - open)
let doji = range > 0 and body <= range * ratio
mark.char(doji, name = "Doji", char = "+", place = above, color = color.gray)

What it teaches: a float input used as a share, math.abs, and a guard (range > 0) so that a bar with no range is never counted.

Hammer and shooting star#

A small body at one end of the bar and a long shadow at the other.

Cuprel
// Hammer and shooting star: a small body at one end of the bar and a long shadow at the other end.
// The shadow must be at least `factor` times the body, and the shadow on the body's side must be short.
indicator "Hammer and shooting star"
    edition = 1
    display = price

input factor: float = 2.0, name = "Shadow over body", min = 1.0, step = 0.5
let body = math.abs(close - open)
let lower_shadow = math.min(open, close) - low
let upper_shadow = high - math.max(open, close)
let hammer = body > 0 and lower_shadow >= body * factor and upper_shadow <= body
let star = body > 0 and upper_shadow >= body * factor and lower_shadow <= body
mark.shape(hammer, name = "Hammer", shape = triangle_up, place = below, color = color.teal)
mark.shape(star, name = "Shooting star", shape = triangle_down, place = above, color = color.orange)

What it teaches: the upper and lower shadow of a bar, and how one input (factor) tunes two patterns at once.

Bars in a row#

A mark after a number of up closes in a row, or down closes in a row.

Cuprel
// Runs of bars: a mark after `count` up closes in a row, or `count` down closes in a row.
// tech.bars_since counts the bars since the last time the condition was true.
indicator "Bars in a row"
    edition = 1
    display = price

input count: int = 3, name = "Bars in a row", min = 2, max = 10
let up = close > close[1]
let down = close < close[1]
let up_run = tech.bars_since(not up)
let down_run = tech.bars_since(not down)
mark.shape(
    up_run == count,
    name = "Up run",
    shape = triangle_up,
    place = below,
    color = color.green,
)
mark.shape(
    down_run == count,
    name = "Down run",
    shape = triangle_down,
    place = above,
    color = color.red,
)

What it teaches: tech.bars_since, which counts the bars since a condition was last true. Here it counts since the last bar that did not close up, which is the length of the current up run.

Gaps#

A bar that opens above the previous high or below the previous low leaves a gap. A box marks the empty space.

Cuprel
// Gaps: the bar opens above the previous high or below the previous low, so no trade happened in between.
// A box marks the empty space; the boxes of the last gaps are kept.
indicator "Gaps"
    edition = 1
    display = price
    max_boxes = 50

let gap_up = low > high[1]
let gap_down = high < low[1]
if gap_up
    box.new(
        bar.index - 1,
        low,
        bar.index,
        high[1],
        border_color = color.green,
        background = color.fade(color.green, 80),
    )
if gap_down
    box.new(
        bar.index - 1,
        low[1],
        bar.index,
        high,
        border_color = color.red,
        background = color.fade(color.red, 80),
    )
plot(close, name = "Close", color = color.gray)

What it teaches: box.new with two corners (bar number and price), max_boxes in the header, and a drawing made inside an if. On a market that trades around the clock, gaps are rare inside a day and appear mostly after a weekend or a pause.

Outside bars#

A bar whose high is above and whose low is below the bar before it, coloured by the way it closed.

Cuprel
// Outside bar: a bar whose high is above and whose low is below the bar before it. Its colour says which way it closed.
indicator "Outside bars"
    edition = 1
    display = price

let outside = high > high[1] and low < low[1]
let tint = if close > open then color.green else color.red
chart.candles(if outside then tint else na, name = "Outside bars")
mark.char(outside, name = "Outside bar", char = "o", place = above, color = color.gray)

What it teaches: chart.candles to colour only some candles (na leaves the others as they are) and mark.char with a letter.

Close position in the bar#

Where the close sits inside the bar's range, from 0 at the low to 100 at the high, with a smoothed line.

Cuprel
// Close position in the bar: 100 when the bar closed at its high, 0 at its low.
// A long run of high readings means buyers kept the price near the top of each bar.
indicator "Close position in the bar"
    edition = 1
    display = pane

input length: int = 10, name = "Smoothing", min = 1
let range = high - low
let position = if range > 0 then 100 * (close - low) / range else 50
let smoothed = tech.sma(position, length)
plot(position, name = "Position", style = columns, color = color.fade(color.blue, 60))
plot(smoothed, name = "Smoothed", color = color.orange, width = 2)
level(50, name = "Middle", color = color.gray, style = dashed)

What it teaches: an if ... then ... else that protects a division (a bar with no range gets 50), the columns style and a faded colour.