1. Overview of Pine Script Structure
A Pine Script follows a general structure, which includes the following components:
- Version: Specifies the script version.
- Declaration Statement: Defines the type of script and its properties.
- Code: Implements the algorithm using statements.
// This source code is subject to the terms of the Mozilla Public License 2.0 at <https://mozilla.org/MPL/2.0/>
// © protradingart
//@version=5
indicator("Pro Trading Art - Double Top & Bottom with alert", "PTA - Double Top & Bottom", overlay=true, max_lines_count=500, max_labels_count=500)
//////////////////////////////////////////////////////
////////////////////////////////////////////////////////
method maintainPivot(array<float> srcArray, float value) =>
srcArray.push(value)
srcArray.shift()
method maintainIndex(array<int> srcArray, int value) =>
srcArray.push(value)
srcArray.shift()
method middleIndex(array<int> srcArray)=>
srcArray.get(srcArray.size()-2)
method middlePrice(array<float> srcArray)=>
srcArray.get(srcArray.size()-2)
drawLL(start, end, price, startText, endText, COLOR, style=label.style_label_down)=>
Line = line.new(x1=start, y1=price, x2=end, y2=price, color=COLOR, width=2)
A = label.new(x=start, y=price, text=startText, color=COLOR, style=style, textcolor=color.black, size=size.normal)
B = label.new(x=end, y=price, text=endText, color=COLOR, style=style, textcolor=color.black, size=size.normal)
[Line, A, B]
////////////////////////////////////////////////////////
////////////////////////////////////////////////////////
pivotLeg = input.int(10, "Pivot Length")
extendSignal = input.bool(false, "Extend Signal")
var top = array.new_float(3)
var bottom = array.new_float(3)
var topIndex = array.new_int(3)
var bottomIndex = array.new_int(3)
ph = ta.pivothigh(pivotLeg, pivotLeg)
pl = ta.pivotlow(pivotLeg, pivotLeg)
////////////// Top ///////////////////
if not na(ph)
top.maintainPivot(ph)
topIndex.maintainIndex(bar_index-pivotLeg)
////////////// Bottom ///////////////////
if not na(pl)
bottom.maintainPivot(pl)
bottomIndex.maintainIndex(bar_index-pivotLeg)
inRange = not na(top.first()) and (not na(bottom.first()))
////////////////////////// Top Calculation //////////////////////////////////////////////////////
topPrice = 0.0
isTop = false
var line topLine = na
var label topA = na
var label topB = na
if inRange
topStart = topIndex.last()
topPrice := top.last()
if topPrice < top.middlePrice() and topIndex.middleIndex() > bottomIndex.first()
topPrice := top.middlePrice()
topStart := topIndex.middleIndex()
max_index = top.indexof(top.max())
if topPrice < top.max() and topIndex.get(max_index) > bottomIndex.first()
topPrice := top.max()
topStart := topIndex.get(max_index)
isTop := high >= topPrice and high[1] < topPrice and low < topPrice and bottom.last() > bottom.middlePrice()
var lastStart = 0
var topEnd = 0
if isTop and topStart != lastStart
lastStart := topStart
[Line, A, B] = drawLL(topStart, bar_index, topPrice, "Top 1", "Top 2", color.lime)
topLine := Line
topA := A
topB := B
alert("Double Top In: "+str.tostring(syminfo.ticker), alert.freq_once_per_bar_close)
if ta.crossunder(close, topLine.get_y2()) and extendSignal
topLine.set_x2(bar_index)
label.new(bar_index, high, style = label.style_label_down, color=color.red)
alert("Double Top Breakdown In: "+str.tostring(syminfo.ticker), alert.freq_once_per_bar_close)
if ta.crossover(close, topLine.get_y2()) and extendSignal
topLine.set_x2(bar_index)
label.new(bar_index, low, style = label.style_label_up, color=color.green)
alert("Double Top Breakout In: "+str.tostring(syminfo.ticker), alert.freq_once_per_bar_close)
// ////////////////////////// Bottom Calculation //////////////////////////////////////////////////////
bottomPrice = 0.0
isBottom = false
var line bottomLine = na
var label bottomA = na
var label bottomB = na
if inRange
bottomStart = bottomIndex.last()
bottomPrice := bottom.last()
if bottomPrice > bottom.middlePrice() and bottomIndex.middleIndex() > topIndex.first()
bottomPrice := bottom.middlePrice()
bottomStart := bottomIndex.middleIndex()
min_index = bottom.indexof(bottom.min())
if bottomPrice > bottom.min() and bottomIndex.get(min_index) > topIndex.first()
bottomPrice := bottom.min()
bottomStart := bottomIndex.get(min_index)
isBottom := close <= bottomPrice and close[1] > bottomPrice and high > bottomPrice and top.last() < top.middlePrice()
var bottomEnd = 0
var lastStart = 0
if isBottom and bottomStart != lastStart
lastStart := bottomStart
[Line, A, B] = drawLL(bottomStart, bar_index, bottomPrice, "Bottom 1", "Bottom 2", color.red, label.style_label_up)
bottomLine := Line
bottomA := A
bottomB := B
alert("Double Bottom In: "+str.tostring(syminfo.ticker), alert.freq_once_per_bar_close)
if ta.crossunder(close, bottomLine.get_y2()) and extendSignal
bottomLine.set_x2(bar_index)
label.new(bar_index, high, style = label.style_label_down, color=color.red)
alert("Double Bottom Breakdown In: "+str.tostring(syminfo.ticker), alert.freq_once_per_bar_close)
if ta.crossover(close, bottomLine.get_y2()) and extendSignal
bottomLine.set_x2(bar_index)
label.new(bar_index, low, style = label.style_label_up, color=color.green)
alert("Double Bottom Breakout In: "+str.tostring(syminfo.ticker), alert.freq_once_per_bar_close)
2. Version
-
The version of Pine Script is specified using the
//@version=
compiler annotation. -
Example:
//@version=6
-
The version number ranges from 1 to 6.
-
If omitted, version 1 is assumed. It is recommended to always use the latest version.
3. Declaration Statement
Every Pine Script must include one declaration statement. The statement determines:
-
Script Type:
- Indicator: Requires at least one visual output function (e.g.,
plot()
). - Strategy: Requires at least one
strategy.*()
function call. - Library: Must include exported functions or user-defined types.
- Indicator: Requires at least one visual output function (e.g.,
-
Properties:
- Title, runtime behavior, and backtesting parameters.
- Examples:
<aside> <img src="/icons/bell-notification_green.svg" alt="/icons/bell-notification_green.svg" width="40px" />
For an indicator:
indicator("My Indicator"))
</aside>
<aside> <img src="/icons/bell-notification_green.svg" alt="/icons/bell-notification_green.svg" width="40px" />
For a strategy:
strategy("My Strategy", overlay=true)
</aside>
4. Code
The algorithm implementation in Pine Script consists of statements. These include:
-
Variable declaration/reassignment.
-
Function declarations.
-
Built-in, user-defined, or library function calls.
-
Control structures:
if
,for
,while
,switch
, etc. -
Scope and Indentation:
- Global scope statements must begin without indentation.
- Local blocks require an indentation (4 spaces or 1 tab).
-
Example:
// This source code is subject to the terms of the Mozilla Public License 2.0 at <https://mozilla.org/MPL/2.0/> // © protradingart //@version=5 indicator("Pro Trading Art - Double Top & Bottom with alert", "PTA - Double Top & Bottom", overlay=true, max_lines_count=500, max_labels_count=500) ////////////////////////////////////////////////////// //////////////////////////////////////////////////////// method maintainPivot(array<float> srcArray, float value) => srcArray.push(value) srcArray.shift() method maintainIndex(array<int> srcArray, int value) => srcArray.push(value) srcArray.shift() method middleIndex(array<int> srcArray)=> srcArray.get(srcArray.size()-2) method middlePrice(array<float> srcArray)=> srcArray.get(srcArray.size()-2) drawLL(start, end, price, startText, endText, COLOR, style=label.style_label_down)=> Line = line.new(x1=start, y1=price, x2=end, y2=price, color=COLOR, width=2) A = label.new(x=start, y=price, text=startText, color=COLOR, style=style, textcolor=color.black, size=size.normal) B = label.new(x=end, y=price, text=endText, color=COLOR, style=style, textcolor=color.black, size=size.normal) [Line, A, B] //////////////////////////////////////////////////////// //////////////////////////////////////////////////////// pivotLeg = input.int(10, "Pivot Length") extendSignal = input.bool(false, "Extend Signal") var top = array.new_float(3) var bottom = array.new_float(3) var topIndex = array.new_int(3) var bottomIndex = array.new_int(3) ph = ta.pivothigh(pivotLeg, pivotLeg) pl = ta.pivotlow(pivotLeg, pivotLeg) ////////////// Top /////////////////// if not na(ph) top.maintainPivot(ph) topIndex.maintainIndex(bar_index-pivotLeg) ////////////// Bottom /////////////////// if not na(pl) bottom.maintainPivot(pl) bottomIndex.maintainIndex(bar_index-pivotLeg) inRange = not na(top.first()) and (not na(bottom.first())) ////////////////////////// Top Calculation ////////////////////////////////////////////////////// topPrice = 0.0 isTop = false var line topLine = na var label topA = na var label topB = na if inRange topStart = topIndex.last() topPrice := top.last() if topPrice < top.middlePrice() and topIndex.middleIndex() > bottomIndex.first() topPrice := top.middlePrice() topStart := topIndex.middleIndex() max_index = top.indexof(top.max()) if topPrice < top.max() and topIndex.get(max_index) > bottomIndex.first() topPrice := top.max() topStart := topIndex.get(max_index) isTop := high >= topPrice and high[1] < topPrice and low < topPrice and bottom.last() > bottom.middlePrice() var lastStart = 0 var topEnd = 0 if isTop and topStart != lastStart lastStart := topStart [Line, A, B] = drawLL(topStart, bar_index, topPrice, "Top 1", "Top 2", color.lime) topLine := Line topA := A topB := B alert("Double Top In: "+str.tostring(syminfo.ticker), alert.freq_once_per_bar_close) if ta.crossunder(close, topLine.get_y2()) and extendSignal topLine.set_x2(bar_index) label.new(bar_index, high, style = label.style_label_down, color=color.red) alert("Double Top Breakdown In: "+str.tostring(syminfo.ticker), alert.freq_once_per_bar_close) if ta.crossover(close, topLine.get_y2()) and extendSignal topLine.set_x2(bar_index) label.new(bar_index, low, style = label.style_label_up, color=color.green) alert("Double Top Breakout In: "+str.tostring(syminfo.ticker), alert.freq_once_per_bar_close) // ////////////////////////// Bottom Calculation ////////////////////////////////////////////////////// bottomPrice = 0.0 isBottom = false var line bottomLine = na var label bottomA = na var label bottomB = na if inRange bottomStart = bottomIndex.last() bottomPrice := bottom.last() if bottomPrice > bottom.middlePrice() and bottomIndex.middleIndex() > topIndex.first() bottomPrice := bottom.middlePrice() bottomStart := bottomIndex.middleIndex() min_index = bottom.indexof(bottom.min()) if bottomPrice > bottom.min() and bottomIndex.get(min_index) > topIndex.first() bottomPrice := bottom.min() bottomStart := bottomIndex.get(min_index) isBottom := close <= bottomPrice and close[1] > bottomPrice and high > bottomPrice and top.last() < top.middlePrice() var bottomEnd = 0 var lastStart = 0 if isBottom and bottomStart != lastStart lastStart := bottomStart [Line, A, B] = drawLL(bottomStart, bar_index, bottomPrice, "Bottom 1", "Bottom 2", color.red, label.style_label_up) bottomLine := Line bottomA := A bottomB := B alert("Double Bottom In: "+str.tostring(syminfo.ticker), alert.freq_once_per_bar_close) if ta.crossunder(close, bottomLine.get_y2()) and extendSignal bottomLine.set_x2(bar_index) label.new(bar_index, high, style = label.style_label_down, color=color.red) alert("Double Bottom Breakdown In: "+str.tostring(syminfo.ticker), alert.freq_once_per_bar_close) if ta.crossover(close, bottomLine.get_y2()) and extendSignal bottomLine.set_x2(bar_index) label.new(bar_index, low, style = label.style_label_up, color=color.green) alert("Double Bottom Breakout In: "+str.tostring(syminfo.ticker), alert.freq_once_per_bar_close)
5. Comments
-
Use
//
to add comments. -
Comments can be inline or on a separate line.
-
Example:
// This is a comment plot(close) // Inline comment
6. Line Wrapping
-
Long lines can be split across multiple lines.
-
Wrapped lines should not use indentation that is a multiple of 4 spaces.
-
Example:
plot(ta.correlation(src, ovr, length), color = color.new(color.purple, 40), style = plot.style_area)
7. Compiler Annotations
Special comments for instructions:
- Version:
//@version=6
- Description:
//@description
- Function Parameters:
//@param
,//@returns
- User-defined Types:
//@type
,//@field
8. Example Scripts
-
Indicator Example:
//@version=6 indicator("My Indicator") plot(close)
-
Strategy Example:
//@version=6 strategy("My Strategy", overlay=true) if ta.crossover(ta.sma(close, 14), ta.sma(close, 28)) strategy.entry("Long Entry", strategy.long)
9. Advanced Features
-
Collapsible Code Regions:
Use
//#region
and//#endregion
to group and collapse code sections. -
Example:
//#region Inputs input price = input.float(1.0, "Price") //#endregion