Library · Pine Script v6 · Library

TheStratGrammar

by 345-strategies

This library calculates TheStrat price action states, classifying candles by directional break structure, close relative to open, and rejected break conditions.

View on GitHub

Repaints

GitHub stars5on the repository
Pine versionv6current
Lines of code322
Settings5inputs
LicenceMPL-2.0
Last update2 days ago26 Oct 2026

Add It to Your TradingView Chart

  1. 1Copy the scriptPress Copy script above, or the copy button on the code below.
  2. 2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
  3. 3Paste over the templateCreate a new library, select all of the template text and paste. Press Save and give it a name.
  4. 4Publish or import itA library draws nothing. Publish it from the editor, then import its functions in your own scripts.

Source Code

322 lines of Pine Script v6 from grammar/TheStratGrammar.pine, published under MPL-2.0. Keep the author's credit and licence lines when you reuse it.

TheStratGrammar.pine
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// https://mozilla.org/MPL/2.0/.
//
// TheStratGrammar - the Strat's price action states, defined once
// The Pine implementation of a written specification. The same definitions also ship
// as a reference implementation in another language, with differential tests between
// them, so the two cannot quietly disagree about what a candle is.

//@version=6

// @description Classification primitives for TheStrat: reduce any candle to the
// four-field state every notation projects from (structure, above-open, failed, live),
// then render it either as chart notation (2u, F2d, 1u - the u/d overload traders know)
// or as glyph notation (2u<, F2d>, 1>) where the token never carries sign and > / <
// carry close-versus-open exclusively. Includes hammer/shooter proportion patterns and
// Full Timeframe Continuity. Definitions only: nothing here decides what to trade.
library("TheStratGrammar", overlay = true)

// ============================================================================
// THE MODEL
// ============================================================================
// Every candle reduces to one tuple. Everything on a chart is a projection of it.
// structure and aboveOpen are orthogonal: a 2u can close red. That is why failed
// exists as its own field instead of being inferred from color.

// @type The four-field state every notation projects from.
// @field structure "1", "2u", "2d" or "3" - what this candle did to the prior range. The u/d here is the BREAK SIDE, never the sign.
// @field aboveOpen Close strictly above open. A close at the open is not above; it buckets down.
// @field failed A directional break got rejected. Only 2u/2d can fail.
// @field live The candle is still forming.
export type BarState
    string structure
    bool aboveOpen
    bool failed
    bool live

// ============================================================================
// CLASSIFICATION
// ============================================================================

// @function The sign channel: did a candle close above its own open?
// @param o Open.
// @param c Close.
// @returns true only for a strictly higher close. A doji buckets down.
export aboveOpen(float o, float c) =>
    c > o

// @function Classify a candle against the prior candle's range.
// Equal is not a break: a high exactly matching the prior high has not crossed it.
// Gaps get no special case - classification tests the range, never the open, so a
// gap-up open above the prior high is already at least a 2u.
// @param prevH Prior candle high.
// @param prevL Prior candle low.
// @param h Current candle high.
// @param l Current candle low.
// @returns "1", "2u", "2d" or "3". Empty string on invalid input (na, or high below low).
export structureOf(float prevH, float prevL, float h, float l) =>
    if na(prevH) or na(prevL) or na(h) or na(l) or h < l or prevH < prevL
        ""
    else
        brokeHigh = h > prevH
        brokeLow = l < prevL
        brokeHigh and brokeLow ? "3" : brokeHigh ? "2u" : brokeLow ? "2d" : "1"

// @function Did a directional break get rejected? Only 2u/2d can fail: an inside bar
// broke nothing and an outside bar broke both sides, so neither has one break to reject.
// @param prevH Prior candle high.
// @param prevL Prior candle low.
// @param o Current candle open.
// @param c Current candle close.
// @param structure The structure from structureOf().
// @param method "Reclaim", "Open", "Reclaim + Open" or "Reclaim OR Open" - these are the conventional option names, so an indicator can pass a settings input straight through. Reclaim: closed back inside the prior range. Open: closed against the break direction.
// @returns true when the break was rejected under the chosen method.
export isFailed(float prevH, float prevL, float o, float c, string structure, string method) =>
    if structure != "2u" and structure != "2d"
        false
    else
        reclaimed = c <= prevH and c >= prevL
        againstOpen = structure == "2u" ? not aboveOpen(o, c) : aboveOpen(o, c)
        switch method
            "Reclaim" => reclaimed
            "Open" => againstOpen
            "Reclaim + Open" => reclaimed and againstOpen
            "Reclaim OR Open" => reclaimed or againstOpen
            => reclaimed

// @function Reduce a candle to its BarState in one call.
// @param prevH Prior candle high.
// @param prevL Prior candle low.
// @param o Current open.
// @param h Current high.
// @param l Current low.
// @param c Current close.
// @param method Failed-break method, as in isFailed(). Pass "Reclaim" if unsure.
// @param live Is the candle still forming.
// @returns A BarState, or na when the inputs do not classify.
export classify(float prevH, float prevL, float o, float h, float l, float c, string method, bool live) =>
    s = structureOf(prevH, prevL, h, l)
    BarState result = na
    if s != ""
        result := BarState.new(s, aboveOpen(o, c), isFailed(prevH, prevL, o, c, s, method), live)
    result

// ============================================================================
// PROJECTIONS
// ============================================================================
// Two renderings of the same state. Chart notation is the community convention,
// where u/d means the break side on a 2 but close-vs-open on a 1 or 3 - the single
// most misread thing in the notation. Glyph notation removes the overload: the token
// never carries sign, and > / < carry close-versus-open exclusively.

// @function Chart-convention token: 1u, 2d, F2u, 3d. The u/d overload lives here, at
// the display boundary, and nowhere else.
// @param this The state.
// @returns The token, for example "F2u" or "1d". Empty string for na.
export method notation(BarState this) =>
    if na(this)
        ""
    else if this.structure == "1"
        this.aboveOpen ? "1u" : "1d"
    else if this.structure == "3"
        this.aboveOpen ? "3u" : "3d"
    else
        (this.failed ? "F" : "") + this.structure

// @function The sign glyph: ">" when the close is above the open, "<" otherwise.
// @param above The aboveOpen flag.
// @returns ">" or "<".
export signGlyph(bool above) =>
    above ? ">" : "<"

// @function Glyph-convention token: structure with no sign, then > or <. A red 2u is
// "2u<" - break side and sign visibly separate. Matches priceactionapi's display field.
// @param this The state.
// @returns For example "F2u<", "1>", "3<". Empty string for na.
export method display(BarState this) =>
    if na(this)
        ""
    else
        base = this.structure == "2u" or this.structure == "2d" ? (this.failed ? "F" : "") + this.structure : this.structure
        base + signGlyph(this.aboveOpen)

// @function Join state tokens into a combo, oldest to newest. The last token is the
// forming candle - position is what carries liveness, which is why notation is
// tense-free.
// @param tokens Tokens oldest to newest, from notation() or display().
// @param sep Separator, usually "-" or "".
// @param potential Prefix with "*" for a setup that has not triggered.
// @returns The combo string, for example "*2d-1-2u".
export combo(array<string> tokens, string sep, bool potential) =>
    body = array.join(tokens, sep)
    potential ? "*" + body : body

// ============================================================================
// PROPORTION PATTERNS
// ============================================================================
// Hammers and shooters are not structures. They describe where the body sits inside
// the range and layer on top of any structure.

// @function A candle that rejected its low.
// @param o Open.
// @param h High.
// @param l Low.
// @param c Close.
// @param method "Broad (Loose)", "Classic" or "Pin Bar (Strict)" - the conventional option names. Broad: open and close both strictly above the midpoint (a body centered exactly at the midpoint is neither pattern). Classic: small body, long rejection wick. Pin Bar: body confined to the top quarter.
// @param requireColor Additionally require a close above the open.
// @returns true when the candle qualifies.
export isHammer(float o, float h, float l, float c, string method, bool requireColor) =>
    r = h - l
    if r == 0 or na(r)
        false
    else if requireColor and not aboveOpen(o, c)
        false
    else
        openFromLow = (o - l) / r
        closeFromLow = (c - l) / r
        switch method
            "Classic" =>
                body = math.abs(c - o)
                bodyPct = body / r
                wickRatio = bodyPct > 0.001 ? (math.min(o, c) - l) / body : 0
                upperWickPct = (h - math.max(o, c)) / r
                bodyCenterFromHigh = (h - (o + c) / 2) / r
                closeFromHigh = (h - c) / r
                bodyPct <= 0.30 and wickRatio >= 3.0 and upperWickPct <= 0.35 and bodyCenterFromHigh <= 0.33 and closeFromHigh <= 0.25
            "Pin Bar (Strict)" => (h - c) / r <= 0.25 and (h - o) / r <= 0.25
            => openFromLow > 0.50 and closeFromLow > 0.50  // Broad (Loose); strict > so a centered doji is neither

// @function A candle that rejected its high. Mirror of isHammer.
// @param o Open.
// @param h High.
// @param l Low.
// @param c Close.
// @param method As in isHammer.
// @param requireColor Additionally require a close below the open.
// @returns true when the candle qualifies.
export isShooter(float o, float h, float l, float c, string method, bool requireColor) =>
    r = h - l
    if r == 0 or na(r)
        false
    else if requireColor and aboveOpen(o, c)
        false
    else
        openFromLow = (o - l) / r
        closeFromLow = (c - l) / r
        switch method
            "Classic" =>
                body = math.abs(c - o)
                bodyPct = body / r
                wickRatio = bodyPct > 0.001 ? (h - math.max(o, c)) / body : 0
                lowerWickPct = (math.min(o, c) - l) / r
                bodyCenterFromLow = ((o + c) / 2 - l) / r
                bodyPct <= 0.30 and wickRatio >= 3.0 and lowerWickPct <= 0.35 and bodyCenterFromLow <= 0.33 and closeFromLow <= 0.25
            "Pin Bar (Strict)" => (c - l) / r <= 0.25 and (o - l) / r <= 0.25
            => openFromLow < 0.50 and closeFromLow < 0.50  // Broad (Loose); strict < (see isHammer)

// ============================================================================
// FULL TIMEFRAME CONTINUITY
// ============================================================================

// @function Read continuity across timeframes, from the sign channel only. Do not
// reconstruct this from structure tokens: a 2u can close red. Pass the aboveOpen of
// each monitored timeframe's current candle, all sampled at the same moment.
// @param signs One aboveOpen per monitored timeframe.
// @returns "Up" when every sign is true, "Down" when every sign is false, else "Conflict". An empty array is "Conflict".
export continuity(array<bool> signs) =>
    n = array.size(signs)
    if n == 0
        "Conflict"
    else
        allUp = true
        allDown = true
        for i = 0 to n - 1
            if array.get(signs, i)
                allDown := false
            else
                allUp := false
        allUp ? "Up" : allDown ? "Down" : "Conflict"

// ============================================================================
// DEMO - what this library does, drawn on your chart
// ============================================================================
// Illustration only; none of it is exported. It labels recent bars with their
// classification and breaks the current bar into the four-field state plus both
// projections, so the difference between the two notations is visible rather than
// described.

grpDemo = "Demo"
demoOn      = input.bool(true,  "Show demo",                     group = grpDemo)
demoBars    = input.int(18,     "Bars to label", minval = 0, maxval = 40, group = grpDemo)
demoGlyph   = input.bool(true,  "Label with glyph notation (2u<)", group = grpDemo, tooltip = "On: the token carries no sign and > / < carry close-versus-open. Off: chart convention, where u/d means the break side on a 2 but close-versus-open on a 1 or 3.")
demoMethod  = input.string("Reclaim", "Failing 2 method", options = ["Reclaim", "Open", "Reclaim + Open", "Reclaim OR Open"], group = grpDemo)
demoPattern = input.string("Broad (Loose)", "Hammer / shooter", options = ["Broad (Loose)", "Classic", "Pin Bar (Strict)"], group = grpDemo)

// Color by the chart-notation token. The token already encodes structure, sign and
// failed, so one switch covers every state.
demoColor(string tok) =>
    switch tok
        "1u"  => #ffeb3b
        "1d"  => #ff9800
        "2u"  => #4caf50
        "2d"  => #f23645
        "F2u" => #f77c80
        "F2d" => #81c784
        "3u"  => #089981
        "3d"  => #e91e63
        => color.gray

// Dark fills need light text; the rest read better dark.
demoTextColor(string tok) =>
    tok == "2d" or tok == "3u" or tok == "3d" ? #ffffff : #101010

demoState = classify(high[1], low[1], open, high, low, close, demoMethod, barstate.isrealtime)
demoTok = na(demoState) ? "" : demoState.notation()
demoHam = isHammer(open, high, low, close, demoPattern, false)
demoSho = isShooter(open, high, low, close, demoPattern, false)
demoWindow = bar_index > last_bar_index - demoBars

if demoOn and demoWindow and not na(demoState)
    label.new(bar_index, high, demoGlyph ? demoState.display() : demoTok,
      style = label.style_label_down, color = demoColor(demoTok),
      textcolor = demoTextColor(demoTok), size = size.small)

// Gated to the same window as the labels. Ungated, Broad fires on roughly a third of
// all bars and litters the whole chart with triangles.
plotshape(demoOn and demoWindow and demoHam, "Hammer",  shape.triangleup,   location.belowbar, #4caf50, size = size.tiny)
plotshape(demoOn and demoWindow and demoSho, "Shooter", shape.triangledown, location.abovebar, #f23645, size = size.tiny)

// Continuity needs more than one timeframe, so the demo reads two higher ones. Only the
// OPEN is requested: an HTF bar's open is fixed the moment that bar starts, so
// lookahead_on reveals nothing that is not already known at this chart bar, while the
// close-so-far of the current HTF candle is simply the live price. Requesting the close
// with lookahead_off would hand back the PREVIOUS HTF candle whenever the last chart bar
// is historical, which is exactly the market-closed case.
o60 = request.security(syminfo.tickerid, "60", open, lookahead = barmerge.lookahead_on)
oD  = request.security(syminfo.tickerid, "D",  open, lookahead = barmerge.lookahead_on)

var table demo = table.new(position.top_right, 2, 8, bgcolor = #101010e6, frame_color = #4a4a4a, frame_width = 1, border_color = #2a2a2a, border_width = 1)

demoRow(int r, string k, string v, color vc) =>
    table.cell(demo, 0, r, k, text_color = #9e9e9e, text_size = size.small, text_halign = text.align_left)
    table.cell(demo, 1, r, v, text_color = vc, text_size = size.small, text_halign = text.align_right)

if demoOn and barstate.islast and not na(demoState)
    signs = array.new<bool>()
    array.push(signs, aboveOpen(open, close))
    array.push(signs, aboveOpen(o60, close))
    array.push(signs, aboveOpen(oD, close))
    ftfc = continuity(signs)

    // Both cells must exist before they can be merged.
    table.cell(demo, 0, 0, "TheStrat Grammar", text_color = #ffffff, text_size = size.small)
    table.cell(demo, 1, 0, "", text_color = #ffffff, text_size = size.small)
    table.merge_cells(demo, 0, 0, 1, 0)
    demoRow(1, "structure",  demoState.structure,                          demoColor(demoTok))
    demoRow(2, "above open", signGlyph(demoState.aboveOpen),               demoState.aboveOpen ? #26a69a : #ef5350)
    demoRow(3, "failed",     demoState.failed ? "yes" : "no",              demoState.failed ? #ff9800 : #9e9e9e)
    demoRow(4, "live",       demoState.live ? "yes" : "no",                #9e9e9e)
    demoRow(5, "chart",      demoState.notation(),                         #ffffff)
    demoRow(6, "glyph",      demoState.display(),                          #4dd0e1)
    demoRow(7, "continuity", ftfc + (demoHam ? "  HAM" : demoSho ? "  SHO" : ""), ftfc == "Up" ? #26a69a : ftfc == "Down" ? #ef5350 : #9e9e9e)

Overview

TheStratGrammar draws demo bar labels directly on the price chart using standard or glyph notation. User inputs configure the demo display, bar count, notation format, pattern definitions, and failing bar methods. The script functions as a foundational definition library rather than an execution strategy, providing classification primitives without built-in trade exits. Higher-timeframe calculations reference lookahead data, and the methodology requires standard price candles rather than smoothed Heiken-Ashi bars.

Details

Published by

345-strategies on GitHub

Type

Library, Pine Script v6

Draws

Library (no plots)

Markets

Any symbol

Timeframes

Any timeframe

Risk Notes

  • Reads higher-timeframe data with lookahead on, so the history can show signals that were not there in real time

Topics

indicatorpine-scriptthestrattradingtradingviewprice-actionstratpine-script-librarythestrat-suite

Settings

The inputs you can change from the gear icon on the chart. Defaults are the author's.

SettingTypeDefaultVariable
Show demobooltruedemoOn
Bars to labelint18demoBars
Label with glyph notation (2u<)booltruedemoGlyph
Failing 2 methodstringReclaimdemoMethod
Hammer / shooterstringBroad (Loose)demoPattern
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.

Similar Pine Scripts

FunctionLinearRegression

by ricardosantos79
261 stars

This Pine Script v5 library provides functions to calculate and plot linear regression channels using sample point arrays. It evaluates paired X and Y arrays to calculate the regression slope, intercept, fitted values, maximum deviation, minimum deviation, and

Library v5 Trend
Updated 4 years ago · None stated GitHub repository of FunctionLinearRegression

PositionSizing

by iamrichardD
57 stars

This Pine Script v6 library calculates trade position sizes and provides risk management functions for external scripts. It computes position sizes from account equity, percentage risk, and stop-loss distance, and offers dynamic sizing adjustments based on vol

Library v6
Updated 1 year ago · Apache-2.0 GitHub repository of PositionSizing

Chart Patterns

by deepentropy
13 stars

Chart Patterns is a Pine Script v6 library designed to scan for and render technical chart patterns directly on the price chart. It defines custom user-defined types to configure scanning parameters, such as Zigzag pivot offsets, error ratios, flat ratios, and

Library v6 Repaints
Updated 6 months ago · MIT GitHub repository of Chart Patterns

AuthLib

by alboogycOdR
6 stars

AuthLib is a Pine Script utility library providing access control and state management structures for technical indicators. It checks callers against a hardcoded array of authorized TradingView usernames before executing operations. The library exports user-de

Library v5 SMC / ICT / price actionSessions & time
Updated 7 months ago · None stated GitHub repository of AuthLib

WebhookMessageLibrary

by TradersPost
6 stars

WebhookMessageLibrary is a helper library designed to construct structured JSON payload messages for automated trading integrations like TradersPost. It defines custom exported types and constants for trade actions, market sentiments, quantities, limit prices,

Library v5
Updated 1 year ago · None stated GitHub repository of WebhookMessageLibrary

Standard Lib

by vitahoang
5 stars

Standard Lib is a utility Pine Script library designed to assist developers with chart debugging and dynamic text display. It exports a print function that creates and updates a single text label on the price chart. The label automatically positions itself at

Library v5
Updated 3 years ago · None stated GitHub repository of Standard Lib

More libraries

Trade Your Charts With a Regulated Broker

Compare brokers that connect to TradingView, with spreads, minimum deposits and leverage side by side.

Compare brokers