목차
Script structure
- Pine Script는 아래와 같은 구조를 따릅니다.
-
<version> <declaration_statement> <code>
-
Version
- 다음 형식의 컴파일러 주석은 스크립트가 작성된 Pine Script 버전을 컴파일러에게 알려줍니다.
- //@version=5
- 현재는 Pine Script v5이기 때문에 위와 같이 작성합니다.
- 작성하지 않고 생략한다면 버전1로 간주됩니다. 따라서 필수적으로 작성해주어야합니다.
- 커뮤니티 코드들을 보면 다른 버전의 코드들도 있기때문에 이를 생각하면서 코드를 확인해야합니다.
- 만약 현재 버전보다 낮은 코드들이 있다면 스크립트 우측 상단에 ...을 누른뒤 v5으로 컨버트하기를 눌러주게 되면 코드 버전이 업데이트 된 버전으로 나타냐게 됩니다.
Declaration statement
- 모든 Pine Script는 다음 함수 중 하나를 호출하는 하나의 declaration statement를 포함해야합니다.
- indicator()
- strategy()
- library()
- 이는 스크립트 유형을 식별하는데 사용됩니다. 스크립트 유형에 따라 다르기때문에 반드시 작성해주어야 합니다.
- 또한 다양한 입력인자들이 존재하여 초기 설정등을 진행할 수 있습니다.
-
indicator(title, shorttitle, overlay, format, precision, scale, max_bars_back, timeframe, timeframe_gaps, explicit_plot_zorder, max_lines_count, max_labels_count, max_boxes_count, max_polylines_count)
-
strategy(title, shorttitle, overlay, format, precision, scale, pyramiding, calc_on_order_fills, calc_on_every_tick, max_bars_back, backtest_fill_limits_assumption, default_qty_type, default_qty_value, initial_capital, currency, slippage, commission_type, commission_value, process_orders_on_close, close_entries_rule, margin_long, margin_short, explicit_plot_zorder, max_lines_count, max_labels_count, max_boxes_count, risk_free_rate, use_bar_magnifier, max_polylines_count)
-
library(title, overlay) → void
-
- 각 스크립트 유형별로 고유한 요구 사항이 존재합니다.
- 지표(Indicator)의 경우 차트에 출력을 생성하는 함수 호출(ex. plot(), plotshape(), barcolor(), line 등)을 하나이상 포함해야 합니다.
- 전략(Strategy)의 경우 strategy.*() 호출이 하나 이상 포함되어야 합니다. (ex. strategy.entry())
- 라이브러리(Libaryr)의 경우 내보낸 함수 또는 사용자 정의 유형이 하나 이상 포함되어 있어야 합니다.
Code
- 이 부분부터 이제 코드를 작성하는 부분입니다. 프로그래밍 하듯이 Pine Script 코드를 작성해주시면 됩니다.
- 대부분 파이썬과 코드 규칙은 비슷하지만 몇몇 다른 부분을 살펴보도록 하겠습니다.
- global scope에서는 띄어쓰기 없이 바로 코드를 작성하지만 local scope에서는 탭이나 4번 띄어쓰기를 이용해야합니다.
-
//@version=5 indicator("", "", true) // Declaration statement (global scope) barIsUp() => // Function declaration (global scope) close > open // Local block (local scope) plotColor = if barIsUp() // Variable declaration (global scope) color.green // Local block (local scope) else color.red // Local block (local scope) bgcolor(color.new(plotColor, 70)) // Call to a built-in function (global scope)
-
- , (쉼표)를 이용하여 여러 줄의 코드를 한줄로 작성할 수 있습니다.
-
plot(bar_index, "Bar index", color = color.green), plot(customIndex, "Custom index", color = color.red, style = plot.style_cross)
-
a=1, b=2, c=3
-
- 코드가 길어서 한 눈에 안들어올 경우에는 다음줄에서 이어서 작성하는데 대신 한칸을 띄어주고 작성합니다. (꼭 한 칸이 아니여도 됩니다. 4의 공백만 아니라면 문제 없습니다. 즉 두칸이나 세칸을 띄는것도 가능합니다.)
-
indicator("My script")
-
a = open + high + low + close a = open + high + low + close
-
plot(ta.correlation(src, ovr, length), color = color.new(color.purple, 40), style = plot.style_area, trackprice = true)
- 만약 기본적으로 4칸을 들여써야하는 경우라면 그거보다 한칸을 더 띄어서 5칸을 띄우면 됩니다.
-
- 주석의 경우 // 를 작성한 뒤 입력하면 됩니다.
- 많은 내용을 한 번에 주석처리를 하고 싶을 경우, 드래그 후에 ctrl + / 를 해주면 됩니다.
- 주석의 경우, 특별한 기능을 가진 주석들이 있습니다.
- //@version
- 사용할 PineScript 버전을 지정합니다. (위에서 설명한 내용)
- //@description
- library() 선언문을 사용하는 스크립트에 대한 사용자 정의 설명을 설정합니다.
- 단순히 주석과 같은 기능이지만, library에서 가독성을 위해 따로 표시해줍니다.
- //@function, //@param, //@returns
- 사용자 정의 함수, 그 매개변수, 그리고 결과에 대한 사용자 정의 설명을 추가합니다. 이들은 함수 선언 위에 위치해야 합니다.
- 딱히 작성해주지 않더라도 함수가 작동하는데 아무런 문제가 없지만 함수에 대해 설명을 해주면 가독성이 향상됩니다.
- //@type 및 //@field
- 사용자 정의 타입(UDT) 및 그 필드에 대한 사용자 정의 설명을 추가합니다. 이들은 타입 선언 위에 위치해야 합니다.
- type과 field는 일종의 클래스와 그 안에 있는 변수같은 존재입니다.
-
더보기
// @type Used to represent the coordinates and color to draw a triangle. // @field time1 Time of first point. // @field time2 Time of second point. // @field time3 Time of third point. // @field price1 Price of first point. // @field price2 Price of second point. // @field price3 Price of third point. // @field lineColor Color to be used to draw the triangle lines. type Triangle int time1 int time2 int time3 float price1 float price2 float price3 color lineColor //@function Draws a triangle using the coordinates of the `t` object. //@param t (Triangle) Object representing the triangle to be drawn. //@returns The ID of the last line drawn. drawTriangle(Triangle t) => line.new(t.time1, t.price1, t.time2, t.price2, xloc = xloc.bar_time, color = t.lineColor) line.new(t.time2, t.price2, t.time3, t.price3, xloc = xloc.bar_time, color = t.lineColor) line.new(t.time1, t.price1, t.time3, t.price3, xloc = xloc.bar_time, color = t.lineColor) // Draw the triangle only once on the last historical bar. if barstate.islastconfirmedhistory //@variable Used to hold the Triangle object to be drawn. Triangle triangle = Triangle.new() triangle.time1 := x1Input triangle.time2 := x2Input triangle.time3 := x3Input triangle.price1 := y1Input triangle.price2 := y2Input triangle.price3 := y3Input triangle.lineColor := color.purple drawTriangle(triangle)
-
- // 로 그냥 표시해도 딱히 문제가 되지 않습니다.
- //@variable
- 변수 선언 위에 위치시켜 해당 변수에 대한 사용자 정의 설명을 추가합니다.
- // 로 그냥 표시해도 딱히 문제가 되지 않습니다.
- //@strategy_alert_message
- 전략 스크립트에 대해 경고 생성 대화 상자에서 "메시지" 필드를 사전 채우기 위한 기본 메시지를 제공합니다.
- 예를 들어 //@strategy_alert_message {{strategy.order.alert_message}} 처럼 지정해두면, strategy.entry()의 입력인자중 alert_message가 있는데 여기에 해당하는 alert_message가 alert_message로 전송되게 됩니다.
- 즉 실시간 매매에서 매매신호가 발생할때마다 발생되는 메시지를 alert_message에 지정된 값을 보내줄 수 있습니다.
- //#region and //#endregion
- 이 주석을 사용하면 Pine Editor에서 접을 수 있는 코드 영역을 생성합니다.
- 이 주석을 사용하면 Pine Editor에서 접을 수 있는 코드 영역을 생성합니다.
- //@version
- global scope에서는 띄어쓰기 없이 바로 코드를 작성하지만 local scope에서는 탭이나 4번 띄어쓰기를 이용해야합니다.
Identifiers
- 변수나 함수 이름은 반드시 대문자(A-Z)나 소문자(a-z)나 밑줄(_)로 시작해야 합니다.
- 다음 문자부터는 문자,숫자,밑줄 모두 들어갈 수 있습니다.
- 변수나 함수 이름은 대소문자를 구분합니다.
- 예시
- myVar
- _myVar
- my123Var
- functionName
- MAX_LEN
- max_len
- maxLen
- 3barsDown -> 안되는 경우 입니다!
- constant(변하지 않는 값)같은 경우는 SNAKE_CASE(대문자)를 권장하고, 다른 식별자(변수나 함수명)에는 camelCase를 사용하는 것을 권장합니다.
- SNAKE_CASE
- 상수에 사용되며, 모든 글자를 대문자로 작성하고 단어 사이에 밑줄(_)을 사용합니다.
- ex : MAX_LIMIT , DEFAULT_VALUE
- camelCase
- 변수나 함수 이름에 사용되며, 첫 단어는 소문자로 시작하고 이후의 각 단어의 첫 글자는 대문자로 작성합니다.
- ex : startingValue, fastLength
- SNAKE_CASE
Operators
Arthmetic operators(산술 연산자)
- 종류
- + : 덧셈과 문자열 연결
- - : 뺄셈
- * : 곱셈
- / : 나누기
- % : 나누기 후 나머지
- 두개의 피연산자 타입에 따른 결과 타입
- int 와 float -> float
- int 와 int -> int
- na가 하나라도 있으면 -> na
Comparison operators(비교 연산자)
- 종류
- < : 작다
- <= : 작거나 같다
- != : 같지 않다
- == : 같다
- > : 크다
- >= : 크거나 같다
- 두개의 피연산자 타입에 따른 결과 타입
- 모두 숫자일 경우 -> bool (true or false)
- na가 하나라도 있으면 -> na
Logical operators(논리 연산자)
- not : 부정(단항 연산자입니다. 즉 하나의 피연산자가 존재)
- and
- or
'?:' ternary operator (삼항 연산자)
- 삼항 연산자는 다음과 같이 작성됩니다.
-
condition ? valueWhenConditionIsTrue : valueWhenConditionIsFalse
- condition이 true이면 valueWhenConditionIsTrue를 반환하고, false(또는 na)이면 valueWhenConditionIsFalse를 반환합니다.
-
- 아래 예시처럼 삼항 연산자를 조합해서 사용이 가능합니다.
-
timeframe.isintraday ? color.red : timeframe.isdaily ? color.green : timeframe.ismonthly ? color.blue : na
- timeframe.isintraday가 true이면 color.red가 반환되고, false일 경우 timeframe.isdaily로 넘어가게 됩니다.
- 다시 timeframe.isdaily가 true이면 color.green이 반환되고, false이면 timeframe.ismonthly로 넘어가게 됩니다.
- 또 다시 timeframe.ismonthly가 true이면 color.blue가 반환되고, false이면 na가 반환됩니다.
-
'[ ]' history-referencing operator (기록 참조 연산자)
- [ ] 를 사용하면 time series의 과거 값을 참조할 수 있습니다.
- 여기서 과거 값은 스크립트가 현재 실행 중인 막대, 즉 현재 막대 앞의 막대에 있는 변수의 값입니다.
- 만약 open[1]이라고 한다면 이전 막대의 open 값을 현재 bar에서 가져와서 사용할 수 있습니다.
- open[5]이면 5개전 막대의 open 값을 가져오는 것입니다.
- close[0]이면 현재 막대의 close값을 의미합니다.
- 만약 해당하는 데이터가 존재하지 않는다면 na를 반환합니다.
- 이러한 경우를 조심해야합니다. 앞에 값들을 사용하는 경우라면 특정 bar 이후에 실행하거나 nz를 사용하여 처리를 해줘야합니다. (nz는 그 값이 nan일 경우 0이나 지정한 값으로 반환해주는 함수입니다.)
- close > nz(close[1], open)
- 즉, 데이터세트의 제일 첫부분만 조심해주면 됩니다. 아니면 백테스팅 시작지점을 제일 처음이 아닌 데이터세트가 수집된지 조금 지난시점 이후부터 테스트를 진행하는것도 하나의 방법입니다.
- 응용해서 사용할 경우 다음처럼 사용해볼수도 있습니다.
- highhest를 이용하여 지난 10개의 바의 최고점을 알 수 있습니다. 이를 이용해서 현재 가격이 지난 봉 10개의 종가중 최대값을 돌파했는지 체크해볼 수 있습니다.
- breach = close > highest(close, 10)[1]
- [1]을 지정해줘야지 현재봉을 포함하지 않고 이전봉부터 10개의 데이터중 가장 종가가 높은 값을 가져올 수 있습니다.
- 만약 [1]을 지정해주지 않는다면 close가 계속해서 변화하기 때문에 만약 현재봉이 고점을 돌파했다고 할 경우 close = highest(close,10)이 되게 됩니다.
- 즉, 정리하면 [ ] 는 과거데이터에서 스크립트를 통해 계산된 값을 가져오는 것입니다.
- Pine Script에서는 스크립트가 실행되는 바의 번호를 나타내는 변수 bar_index가 있습니다.
- 첫번째 막대에서 bar_index는 0이고, 다음 막대로 갈수록 1씩 증가하게 됩니다.
- 따라서 마지막 막대에서 bar_index는 데이터세트의 막대 수에서 1을 뺀것과 같습니다.
- 이를 사용해 특정 바 이후부터 특정 계산을 적용하거나 로직을 적용하도록 할 수 있습니다.
Operator precedence(연산자 우선순위)
- 우선순위
- [ ]
- 단항 +, 단항 -, not
- *, /, %
- +, -
- >, <, >=, <=
- ==, !=
- and
- or
- ?:
- 위에 있는것들이 아래 있는 것들보다 먼저 실행되게 됩니다. 즉 ?:가 가장 나중에 실행되고 [ ] 가 가장 먼저 실행됩니다.
- 동일한 우선순위인 경우에는 왼쪽에서 오른쪽으로 계산됩니다.
'=' assignment operator(할당 연산자)
- = 는 변수가 초기화되거나 선언될 때, 즉 처음 사용할 때 변수를 할당하는데 사용됩니다.
- var 로 변수를 선언할 경우 그 변수를 선언하는 코드는 첫번째 막대에서만 실행됩니다.
':=' reassignment operator(재할당 연산자)
- := 는 기존 변수에 값을 다시 할당하는데 사용됩니다.
- 처음 선언된 후 :=를 사용하여 다시 할당된 변수를 가변 변수라고 합니다. 이러한 변수는 var을 이용하여 표현해야합니다.
- 예시1
-
//@version=5 indicator("= test", overlay=true) start_time = input.time(timestamp("2023-12-24T00:00:00+09:00")) end_time = input.time(timestamp("2099-01-01T00:00:00+09:00")) var float v = 0 if (time >= start_time and time <= end_time) v := v+1 plot(v, color=color.blue)
- var로 선언된 v는 첫번째 스크립트가 실행되는 bar에서만 v=0 코드가 실행되고 다음 막대에서부터는 실행되지 않습니다. 계속해서 이전값을 유지하면서 1씩 증가하는 것을 확인할 수 있습니다.
-
- 예시2
-
//@version=5 indicator("", "", true) // Declare `pHi` and initilize it on the first bar only. var float pHi = na // Reassign a value to `pHi` pHi := nz(ta.pivothigh(5, 5), pHi) plot(pHi)
- plot에서 nan 값이 존재할 경우에는 그냥 이전값에서 다음값이 생성되는 부분을 그냥 이어주게 됩니다. 이러한것을 방지하기 위해 값이 존재했던 이전값들을 유지하기 위해서는 nz 같은 함수를 이용하여 위처럼 표현할 수 있습니다.
- 만약 nz를 사용하지 않을 경우 다음과 같이 표현됩니다.
-
//@version=5 indicator("", "", true) plot(ta.pivothigh(5, 5))
- 값이 존재하지 않는 부분에서는 그냥 값이 존재하는 부분을 그대로 이어줍니다.
-
-
Variable declarations
Introduction
- 변수는 값을 보유하는 식별자입니다.
- 다음과 같은 형태로 표현이 가능합니다.
-
[<declaration_mode>] [<type>] <identifier> = <expression> | <structure>
- | 는 or를 의미합니다.
- [ ] 로 둘러쌓인 부분은 선택적으로 사용이 가능합니다. (있을수도 있고, 없을수도 있습니다.)
- declaration_mode는 var,varip 또는 아무것도 없을수 있습니다.
- type은 이제 변수 타입, 즉 float, int, bool, string 와 같은 것을 의미합니다.
- identifier는 변수 이름을 작성해주면 됩니다.
- expression에는 이제 문자나 변수 표현식 또는 함수 호출(function call)등이 사용될 수 있습니다.
- structure은 if, for, while, switch 와 같은 구조가 사용됩니다.
-
- 예시
- float f = 10.5
- var barRange = float(na)
var firstBarOpen = open
varip float lastClose = na
Initialization with 'na'
- 원래같은 경우는 특정 값으로 초기화하면 자동으로 값을 보고 타입을 추측할 수 있지만, na로 초기화할때는 na가 어떤 타입인지 유추할수 없기 때문에 반드시 타입을 지정해주어야합니다.
- 예시
- baseLine0 = na // compile time error!
- float baseLine1 = na // OK
- baseLine2 = float(na) // OK
Tuple declarations
-
<tuple_declaration> = <function_call> | <structure>
- tuple_declaration은 리스트로 여러 변수를 묶어서 받을 수 있습니다.
- [ma, upperBand, lowerBand] = 3개의 결과를 반환하는 어떤 함수()
- [bbMiddle, bbUpper, bbLower] = ta.bb(close, 5, 4)
- [macdLine, signalLine, histLine] = ta.macd(close, 12, 26, 9)
Declaration modes
- Declaration modes에 들어갈 수 있는 값은 var, varip 또는 아무것도 지정안하는 것입니다.
- 아무것도 지정안한경우 로컬(local) 변수로 동작하여서 변수는 매 bar마다 지정된 값으로 초기화하게 됩니다.
- 즉 만약 v = 0 이라고 초기화한다면, 매 막대마다 스크립트가 실행될텐데 그때마다 v=0 이라고 다시 초기화가 되는 것입니다.
- var 이나 varip인 경우는 첫번째 막대에서만 v=0으로 초기화 하고 다음부터 그 초기화 코드는 작동하지 않습니다.
- var이나 varip으로 지정하게 되면 초기화하는 코드는 데이터세트에서 제일 처음 바에서만 한번만 초기화되게 됩니다.
- var과 varip 의 차이점
- var과 varip는 백테스팅에서는 동일하게 작동하지만 실시간 매매에서는 다르게 동작합니다.
- var의 경우는 실시간 매매에서는 하나의 bar에 대해서 값의 업데이트가 딱 한번만 진행되게 됩니다. 하지만 varip의 경우는 하나의 bar에서도 거래량이나 현재가격이 변화하면 해당 변수의 값이 계속해서 업데이트를 진행하게 됩니다.
- var과 varip 의 차이 예시
- var
-
//@version=5 indicator("var") var int v = -1 v := v + 1 plot(v)
- var의 경우 실시간 봉에 대해서 해당하는 변수는 처음 딱 봉이 생성될때 값이 업데이트 되고 그 다음 봉이 생길때까지 값이 업데이트 되지 않습니다.
- 하지만 if문등 조건이 있는 경우 그 조건에 맞게 변할때마다 var 변수더라도 그 조건에 맞는 값으로 변화하게 됩니다.
- 예를 들어 아래 코드와 같을때 var 변수라고 하더라도 처음 봉이 만들어질때 평가된 if문 조건이 아닌 계속해서 가격이 변화할때마다 if문을 체크하고 다른 조건이 되면 그 값으로 표시되게 됩니다.
- (아래 두 코드를 비교하면 varip와 var을 정확하게 이해할 수 있을것입니다.)
- 과거 데이터에 대해서는 동일하게 작동하지만 실시간 데이터에서는 하나의 봉에서 가격이 변화할때마다 var인 경우는 if문중에서 조건에 맞는 결과를 표시하고 varip인 경우 가격이 변화할때마다 조건문에 맞는 결과를 실행하게 됩니다.
- 즉 varip는 중복해서 값이 더해지고, var은 값이 하나의 봉에서 딱 한번만 변화하게 됩니다.(근데 그 변화가 1이 더해질지 2가 더해질지 close가 open보다 큰지 작은지에 따라서 실시간으로 변화하게되는것입니다.)
-
//@version=5 indicator("", "", true) var float v = 0 if close>open v:=v+1 else v:=v+2 plot(v)
-
//@version=5 indicator("", "", true) varip float v = 0 if close>open v:=v+1 else v:=v+2 plot(v)
- (아래 두 코드를 비교하면 varip와 var을 정확하게 이해할 수 있을것입니다.)
-
- varip
-
//@version=5 indicator("varip") varip int v = -1 v := v + 1 plot(v)
- 과거에서는 var과 동일하게 작동하지만 실시간 봉에서는 틱이 바뀔때마다 계속해서 v가 같이 업데이트 되는것을 확인할 수 있습니다.
-
- var
- var과 varip 의 차이점
- 아무것도 지정안한경우 로컬(local) 변수로 동작하여서 변수는 매 bar마다 지정된 값으로 초기화하게 됩니다.
'Algorithm Trading > Pine Script v5 기본' 카테고리의 다른 글
Pine Script v5 기본 개념 - 2 (Strategies) (1) | 2024.01.05 |
---|---|
Pine Script v5 기본 개념 - 1 (Alerts) (1) | 2023.12.26 |
Pine Script v5 기본 문법 - 3 (Qualifiers, Types, Tuple) (0) | 2023.12.26 |
Pine Script v5 기본 문법 - 2 (조건문, 반복문) (0) | 2023.12.25 |
Pine Script v5 기초 (1) | 2023.12.24 |