發(fā)明者量化PINE語言入門教程-初探與Pine模型執(zhí)行
發(fā)明者量化交易平臺支持Pine語言編寫策略,支持回測、實盤運行Pine語言策略,兼容Pine語言的較低版本。在發(fā)明者量化交易平臺(FMZ.COM)上的策略廣場中有搜集、移植的眾多Pine策略(腳本)。
FMZ不僅支持了Pine語言,同時也支持Pine語言強大的畫圖功能。FMZ平臺上的各項功能、豐富實用的工具、高效便捷的管理,也進一步增強了Pine策略(腳本)的實用性。FMZ基于對Pine語言的兼容,同時也對Pine語言進行了一定程度的擴展、優(yōu)化、裁剪。在正式進入教程之前,我們一起來看下FMZ上的Pine語言和原版的Pine有哪些改動。
簡單概述一些比較明顯的不同:
1、FMZ上的Pine策略,代碼開頭的版本標識//@version
和代碼開始的strategy
、indicator
語句并不強制要求編寫,F(xiàn)MZ暫時不支持import
導入library
的功能。
可能看到有些策略是這樣寫的:
或者是這樣寫的:
在FMZ上可以簡化為:
或者:
2、策略(腳本)一些交易相關的設置由FMZ策略界面上的「Pine語言交易類庫」參數(shù)設置。
其它設置例如,最小下單量、默認下單量等可以參看Pine語言文檔中關于「Pine語言交易類庫」參數(shù)的介紹。
收盤價模型與實時價模型
在trading view上,我們可以通過strategy
函數(shù)的calc_on_every_tick
參數(shù)去設置策略腳本在價格每次變動時實時執(zhí)行策略邏輯,此時calc_on_every_tick
參數(shù)應當設置為true
。默認calc_on_every_tick
參數(shù)是false
,即在策略當前K線BAR完全走完時才去執(zhí)行策略邏輯。
在FMZ上則是通過,「Pine語言交易類庫」模板的參數(shù)去設置。

策略執(zhí)行時的價格、下單量等數(shù)值精度控制在FMZ上是需要指定的
在trading view上因為只能模擬測試,所以沒有實盤下單時的精度問題。在FMZ上是可以實盤運行Pine策略的。那么就需要策略可以靈活指定交易品種的價格精度、下單數(shù)量精度。這些精度設置即控制相關數(shù)據(jù)的小數(shù)位數(shù),避免數(shù)據(jù)不符合交易所報單要求從而無法下單。期貨合約代碼
在FMZ上交易品種如果是合約,是有2個屬性的。分別為「交易對」、「合約代碼」,在實盤和回測時除了需要明確設置交易對,也需要在「Pine語言交易類庫」模板的參數(shù)「品種代碼」中設置具體的合約代碼。例如永續(xù)合約就填寫swap
,合約代碼要具體看操作的交易所是否有這種合約。例如有的交易所有季度合約,這里就可以填寫quarter
。這些合約代碼和FMZ的Javascript/python/c++語言API文檔上定義的期貨合約代碼一致。3、
runtime.debug
?、runtime.log
、runtime.error
FMZ擴展的函數(shù),用于調試。FMZ平臺上增加了3個函數(shù)用于調試。
runtime.debug
:在控制臺打印變量信息,一般來說用不到該函數(shù)。runtime.log
:在日志輸出內容。FMZ PINE語言特有函數(shù)。runtime.log(1, 2, 3, close, high, ...),可以傳多個參數(shù)。
runtime.error
:調用時,會導致運行時錯誤,并帶有在message參數(shù)中指定的錯誤消息。runtime.error(message)
4、部分畫圖函數(shù)中擴展了
overlay
參數(shù)在FMZ上的Pine語言,畫圖函數(shù)
plot
、plotshape
、plotchar
等增加了overlay
參數(shù)支持,允許指定畫在主圖或者副圖。overlay
設置true
畫在主圖,設置為false
畫在副圖。使得FMZ上的Pine策略運行時可以主圖、副圖同時畫圖。5、
syminfo.mintick
內置變量的取值syminfo.mintick
內置變量的定義為當前品種的最小刻度值。在FMZ實盤/回測界面上「Pine語言交易類庫」中的模板參數(shù)定價貨幣精度可以控制該值。定價貨幣精度設置2即交易時價格精確到小數(shù)點第二位,此時價格最小變動單位為0.01。syminfo.mintick
的值即為0.01。6、FMZ PINE Script中的均價均為包含手續(xù)費的價格
例如:下單價格為8000,賣出方向,數(shù)量1手(個、張),成交后均價不是8000,低于8000(成本中包含了手續(xù)費)。
Pine語言基礎
開始學習Pine語言基礎時,可能有些例子中的指令、代碼語法我們并不熟悉。看不懂沒關系,我們可以先熟悉概念,理解測試目的,也可以查詢FMZ的Pine語言文檔查看說明。然后跟隨教程一步一步循序漸進熟悉各種語法、指令、函數(shù)、內置變量。
模型執(zhí)行
在入門學習Pine語言時,是非常有必要了解Pine語言腳本程序執(zhí)行過程等相關概念的。Pine語言策略是基于圖表運行的,可以理解為Pine語言策略為一系列的計算和操作,在圖表上以時間序列的先后順序從圖表已經(jīng)加載的最早數(shù)據(jù)開始執(zhí)行。圖表初始加載的數(shù)據(jù)量是有限的。實盤時通常這個數(shù)據(jù)量上限是基于交易所接口返回的最大數(shù)據(jù)量決定,回測時數(shù)據(jù)量上限是基于回測系統(tǒng)數(shù)據(jù)源提供的數(shù)據(jù)決定。圖表上最左邊的第一個K線Bar,即圖表數(shù)據(jù)集的第一個數(shù)據(jù),其索引值為0??梢酝ㄟ^Pine語言的內置變量bar_index
引用到Pine腳本執(zhí)行時當前的K線Bar的索引值。

plot
函數(shù)是我們將來使用較多的函數(shù)之一。用途很簡單,就是根據(jù)傳入的參數(shù)在圖表上畫線,傳入的數(shù)據(jù)是bar_index
,線命名為bar_index
??梢钥吹皆诘谝桓鵅ar上名稱為bar_index的線的值為0,隨著Bar增加向右依次增加1。
根據(jù)策略的設置不同,策略的模型執(zhí)行方式也不同,分為收盤價模型
和實時價模型
。收盤價模型、實時價模型的概念在之前我們也簡單介紹過。
收盤價模型
策略代碼執(zhí)行時,當前K線Bar的周期完全執(zhí)行完成,K線閉合時即K線周期已經(jīng)走完。此時執(zhí)行一遍Pine策略邏輯,觸發(fā)的交易信號將在下一根K線Bar開始時執(zhí)行。
實時價模型
策略代碼執(zhí)行時,當前K線Bar不論是否閉合,每次行情變動就執(zhí)行一遍Pine策略邏輯,觸發(fā)的交易信號立即執(zhí)行。
當Pine語言策略在圖表上從左至右執(zhí)行時,圖表上的K線Bar是分為歷史Bar
和實時Bar
的:
歷史Bar
策略設置為「實盤價模型」開始執(zhí)行時,圖表上除了最右側的那一根K線Bar之外所有K線Bar都是
歷史Bar
。策略邏輯在每根歷史Bar
上僅執(zhí)行一次。
策略設置為「收盤價模型」開始執(zhí)行時,圖表上所有Bar都是歷史Bar
。策略邏輯在每根歷史Bar
上僅執(zhí)行一次。基于歷史Bar的計算:
策略代碼在歷史Bar收盤狀態(tài)下執(zhí)行一次,然后策略代碼繼續(xù)在下一個歷史Bar執(zhí)行,直到所有歷史Bar都執(zhí)行一次。實時Bar
當策略執(zhí)行到最右邊的最后一根K線Bar上時,該Bar為實時Bar。當實時Bar閉合之后,這根Bar就變成了一個經(jīng)過的實時Bar(變成了歷史Bar)。圖表最右側會產(chǎn)生新的實時Bar。
策略設置為「實時價模型」開始執(zhí)行時,在實時Bar上每次行情變動都會執(zhí)行一次策略邏輯。
策略設置為「收盤價模型」開始執(zhí)行時,圖表上不顯示實時Bar。基于實時Bar的計算:
如果設置策略為「收盤價模型」圖表不顯示實時Bar,策略代碼只在當前Bar收盤時執(zhí)行一次。
如果設置策略為「實盤價模型」在實時Bar上的計算和歷史Bar就完全不同了,在實盤Bar上每次行情變動都會執(zhí)行一次策略代碼。例如內置變量high
、low
、close
在歷史Bar上是確定的,在實時Bar上可能每次行情變動時這些值是會發(fā)生變化的。所以基于這些值計算的指標等數(shù)據(jù)也是會實時變動的。在實時Bar上close
始終代表當前最新價格,high
和low
始終代表自當前實時Bar開始以來達到的最高高點和最低低點。這些內置變量代表實時Bar最后一次更新時的最終值。實時Bar上執(zhí)行策略時的回滾機制(實時價模型):
在實時Bar執(zhí)行時,策略的每次新迭代執(zhí)行前重置用戶定義的變量稱為回滾。我們來以一個例子理解回滾機制,如下測試代碼。注意:
包裹的內容為FMZ平臺上以代碼形式保存的回測配置信息。


我們只考察在實時Bar時執(zhí)行的場景,所以用了not barstate.ishistory
表達式限制只在實時Bar時對變量n累加,并且在執(zhí)行累加操作前后使用runtime.log
函數(shù)輸出信息在策略日志中。從使用畫圖函數(shù)plot
畫出的曲線n可以看到在策略處于歷史Bar運行時n一直是0。當執(zhí)行到實時Bar時觸發(fā)了n累加1的操作,并且在實時Bar上每輪執(zhí)行策略時都執(zhí)行了n累加1的操作。可以從日志信息中觀察到每輪重新執(zhí)行策略代碼時n都被重置為前一個Bar執(zhí)行策略最終提交的值。當實時Bar上最后一次執(zhí)行策略代碼時會提交n值更新,所以可以看到圖表上從實時Bar開始,曲線n隨著每次Bar增加時曲線n的值增加1。
總結一下:
1、策略在實時Bar開始執(zhí)行時,每次行情更新就執(zhí)行一次策略代碼。
2、在實時Bar上執(zhí)行時,每次執(zhí)行策略代碼之前都會回滾變量。
3、在實時Bar上執(zhí)行時,變量在收盤更新時提交一次。
由于數(shù)據(jù)回滾,所以圖表上的曲線等畫圖操作也是可能引起重繪的,例如我們修改一下剛才的測試代碼,實盤測試:
時刻A的截圖

時刻B的截圖

我們只修改了這句:
n := open > close ? n + 1 : n
,當前實時Bar為陰線(即開盤價高于收盤價)時才給n累加1??梢钥吹皆诘谝粡垐D(時刻A)中由于當時開盤價格高于收盤價格(陰線)所以n累加了1,圖表曲線n顯示的值為5。然后行情變動、價格更新如同第二張圖(時刻B)中顯示。此時開盤價格低于收盤價格(陽線),n值回滾并且也沒有累加1。圖表中曲線n也立即重繪,此時曲線上的n值為4。所以在實時Bar上顯示的金叉、死叉等信號都是不確定的,有可能會變化的。函數(shù)中的變量上下文
下面我們來一起研究一下Pine語言函數(shù)中的變量。根據(jù)一些Pine教程上的描述,函數(shù)中的變量與函數(shù)外的變量有這樣的差異:
Pine函數(shù)中使用的系列變量的歷史是通過對函數(shù)的每次連續(xù)調用創(chuàng)建的。如果沒有在腳本運行的每個柱上調用函數(shù),這將導致函數(shù)本地塊內部與外部系列的歷史值之間存在差異。因此,如果沒有在每個柱上調用函數(shù),則使用相同索引值在函數(shù)內部和外部引用的系列將不會引用相同的歷史點。
是不是有些難以讀懂?沒關系,我們通過一個在FMZ上運行的測試代碼來弄明白這個問題:
回測運行截圖

測試代碼比較簡單,主要是來考察兩種方式引用的數(shù)據(jù),即:
f(a) => a[1]
和f2() => close[1]
。[]
符號用于對數(shù)據(jù)系列變量歷史值的引用操作,close[1]即引用當前收盤價前一個Bar上的收盤價數(shù)據(jù)。我們的測試代碼一共在圖表上畫出4種數(shù)據(jù):通過策略回測運行截圖可以看到,雖然畫A標記使用的函數(shù)
f(a) => a[1]
和畫B標記使用的函數(shù)f2() => close[1]
都是使用[1]來引用數(shù)據(jù)系列上的歷史數(shù)據(jù),但是圖表上"A"和"B"的標記位置是完全不同的。"A"標記的位置總是落在紅色的線上,也就是策略中代碼plot(close[2], title = "close[2]", color = color.red, overlay = true)
畫出的線上,其畫線使用的數(shù)據(jù)是close[2]
。

原因就是通過K線Bar的索引,即內置變量
bar_index
計算是否畫"A"和"B"標記。"A"和"B"標記并不是在每根K線Bar上都畫圖(畫圖時調用函數(shù)計算)。函數(shù)f(a) => a[1]
這種方式引用的值,如果函數(shù)不是每根Bar上都調用就會與函數(shù)f2() => close[1]
這種方式引用的值不相同(即使都使用[1]這樣相同的索引)。plotchar(oneBarInTwo ? f(close) : na, title = "f(close)", color = color.red, location = location.absolute, style = shape.xcross, overlay = true, char = "A")
畫一個字符“A”,顏色為紅色,當oneBarInTwo為真時才畫出,畫出的位置(Y軸上)為:f(close)
返回的值。plotchar(oneBarInTwo ? f2() : na, title = "f2()", color = color.green, location = location.absolute, style = shape.circle, overlay = true, char = "B")
畫一個字符“B”,顏色為綠色,當oneBarInTwo為真時才畫出,畫出的位置(Y軸上)為:f2()
返回的值。plot(close[2], title = "close[2]", color = color.red, overlay = true)
畫線,顏色為紅色,畫出的位置(Y軸上)為:close[2]
即當前Bar前數(shù)第2根(向左數(shù)2根)Bar上的收盤價。plot(close[1], title = "close[1]", color = color.green, overlay = true)
畫線,顏色為綠色,畫出的位置(Y軸上)為:close[1]
即當前Bar前數(shù)第1根(向左數(shù)1根)Bar上的收盤價。f(a) => a[1]
:使用傳參數(shù)的方式,函數(shù)最后返回a[1]
。f2() => close[1]
:直接使用內置變量close
,函數(shù)最后返回close[1]
。一些內置函數(shù)需要在每個Bar上計算才能正確計算其結果
以一個簡單例子說明這種情況:
我們將函數(shù)調用代碼ta.barssince(close < close[1])
寫在一個三元操作符condition ? value1 : value2
中。這就導致了只在close > close[1]
時去調用ta.barssince函數(shù)??善?code>ta.barssince函數(shù)是計算從最近一次close < close[1]
成立時的K線數(shù)量。調用ta.barssince函數(shù)時都是close > close[1],即當前收盤價大于上一根Bar的收盤價,函數(shù)ta.barssince被調用時其條件close < close[1]都不成立,也就沒有最近一次成立的位置。
ta.barssince : 調用時,如果在當前K線之前從未滿足該條件,則該函數(shù)返回na。
如圖:

所以畫圖時,只畫出了res變量有值時的數(shù)據(jù)(-1)。
要避免這個問題,我們只用把ta.barssince(close < close[1])
函數(shù)調用從三元操作符中拿出來,寫在任何可能的條件分支外部。使其在每根K線Bar上都執(zhí)行計算。
