[TOC]
Hỗ trợ video hướng dẫn:https://www.youtube.com/watch?v=CA3SwJQb_1g
FMZ Quant Trading Platform hỗ trợ viết chiến lược ngôn ngữ Pine, backtesting và giao dịch trực tiếp của các chiến lược ngôn ngữ Pine, và nó tương thích với các phiên bản thấp hơn của ngôn ngữ Pine.Quảng trường chiến lượctrên nền tảng giao dịch lượng tử FMZ (FMZ.COM).
FMZ không chỉ hỗ trợ ngôn ngữ Pine, mà còn hỗ trợ chức năng vẽ mạnh mẽ của ngôn ngữ Pine. Các chức năng khác nhau, các công cụ phong phú và thiết thực, quản lý hiệu quả và thuận tiện trên nền tảng FMZ làm tăng thêm khả năng thực tế của chiến lược Pine (script). Dựa trên tính tương thích với ngôn ngữ Pine, FMZ cũng mở rộng, tối ưu hóa và cắt giảm ngôn ngữ Pine đến một mức độ nhất định. Trước khi tham gia hướng dẫn chính thức, hãy xem những thay đổi nào đã được thực hiện cho ngôn ngữ Pine trên FMZ so với phiên bản gốc.
Một cái nhìn tổng quan ngắn gọn về một số sự khác biệt rõ ràng:
//@version
vàstrategy
, indicator
tuyên bố ở đầu mã không bắt buộc phải viết, FMZ không hỗ trợimport
nhập khẩulibrary
hoạt động cho bây giờ.Có thể thấy rằng một số chiến lược được viết như thế này:
//@version=5
indicator("My Script", overlay = true)
src = close
a = ta.sma(src, 5)
b = ta.sma(src, 50)
c = ta.cross(a, b)
plot(a, color = color.blue)
plot(b, color = color.black)
plotshape(c, color = color.red)
Hoặc viết như thế này:
//@version=5
strategy("My Strategy", overlay=true)
longCondition = ta.crossover(ta.sma(close, 14), ta.sma(close, 28))
if (longCondition)
strategy.entry("My Long Entry Id", strategy.long)
shortCondition = ta.crossunder(ta.sma(close, 14), ta.sma(close, 28))
if (shortCondition)
strategy.entry("My Short Entry Id", strategy.short)
Trên FMZ nó có thể được đơn giản hóa thành:
src = close
a = ta.sma(src, 5)
b = ta.sma(src, 50)
c = ta.cross(a, b)
plot(a, color = color.blue, overlay=true)
plot(b, color = color.black, overlay=true)
plotshape(c, color = color.red, overlay=true)
Hoặc:
longCondition = ta.crossover(ta.sma(close, 14), ta.sma(close, 28))
if (longCondition)
strategy.entry("My Long Entry Id", strategy.long)
shortCondition = ta.crossunder(ta.sma(close, 14), ta.sma(close, 28))
if (shortCondition)
strategy.entry("My Short Entry Id", strategy.short)
Mô hình giá đóng cửa và mô hình giá thời gian thực
Trong giao dịch xem, chúng ta có thể sử dụng cáccalc_on_every_tick
tham số củastrategy
chức năng để thiết lập kịch bản chiến lược để thực thi chiến lược logic trong thời gian thực khi giá thay đổi mỗi lần.calc_on_every_tick
tham số nên được thiết lập thànhtrue
.calc_on_every_tick
tham số mặc định làfalse
, tức là, logic chiến lược chỉ được thực hiện khi K-line BAR hiện tại của chiến lược được hoàn thành hoàn toàn.
Trên FMZ, nó được thiết lập bởi các tham số của mẫu
Kiểm soát số chính xác, chẳng hạn như giá và số tiền đặt hàng khi chiến lược được thực hiện cần phải được xác định trên FMZ Theo quan điểm giao dịch, không có vấn đề về độ chính xác khi đặt lệnh giao dịch thực, bởi vì nó chỉ có thể được thử nghiệm trong mô phỏng. Trên FMZ, có thể chạy chiến lược Pine trong giao dịch thực. Sau đó chiến lược cần có thể xác định độ chính xác giá và độ chính xác số tiền đặt hàng của loại giao dịch một cách linh hoạt. Các thiết lập độ chính xác kiểm soát số lượng chữ số thập phân trong dữ liệu có liên quan để ngăn chặn dữ liệu không đáp ứng các yêu cầu đặt hàng của sàn giao dịch và do đó không thể đặt lệnh.
Mã hợp đồng tương lai
Nếu sản phẩm giao dịch trên FMZ là một hợp đồng, nó có hai thuộc tính, chúng là swap
Ví dụ: một số sàn giao dịch có hợp đồng hàng quý, bạn có thể điền vàoquarter
Các mã hợp đồng này phù hợp với các mã hợp đồng tương lai được xác định trong tài liệu API ngôn ngữ Javascript / python / c ++ của FMZ.
Đối với các thiết lập khác, chẳng hạn như số tiền đặt hàng tối thiểu, số tiền đặt hàng mặc định, vv, vui lòng tham khảo phần giới thiệu tham số trên
runtime.debug
, runtime.log
, runtime.error
được sử dụng để gỡ lỗi.3 chức năng đã được thêm vào nền tảng FMZ để gỡ lỗi.
runtime.debug
: in thông tin biến trên bảng điều khiển, thường không được sử dụng với chức năng này.
runtime.log
Các chức năng ngôn ngữ cụ thể của PINE trên FMZ
runtime.log(1, 2, 3, close, high, ...), Multiple parameters can be passed.
runtime.error
: Nó sẽ dẫn đến một lỗi thời gian chạy với thông báo lỗi được chỉ định trong thông số thông báo khi được gọi.
runtime.error(message)
overlay
tham số được mở rộng trong một số các chức năng vẽTrong ngôn ngữ Pine trên FMZ, các chức năng vẽplot
, plotshape
, plotchar
, v.v. đã thêm vàooverlay
hỗ trợ tham số, cho phép xác định bản vẽ trên biểu đồ chính hoặc biểu đồ phụ.overlay
được thiết lập thànhtrue
để vẽ trên biểu đồ chính, vàfalse
được thiết lập để vẽ trên biểu đồ phụ, cho phép chiến lược Pine trên FMZ vẽ biểu đồ chính và biểu đồ phụ cùng một lúc.
syminfo.mintick
biến tích hợpCác biến tích hợp củasyminfo.mintick
được định nghĩa là giá trị tick tối thiểu cho biểu tượng hiện tại. Giá trị này có thể được kiểm soát bởi các tham số giá mẫu chính xác tiền tệ trong syminfo.mintick
là 0,01.
Ví dụ: giá đặt hàng là 8000, hướng bán hàng, số lượng là 1 lô (phần, tấm), giá trung bình sau giao dịch không phải là 8000, nhưng thấp hơn 8000 (chi phí bao gồm phí xử lý).
Khi bắt đầu học những điều cơ bản của ngôn ngữ Pine, có thể có một số ví dụ về hướng dẫn và ngữ pháp mã mà chúng ta không quen thuộc. Không quan trọng nếu bạn không hiểu nó, chúng ta có thể làm quen với các khái niệm trước và hiểu mục đích của bài kiểm tra, hoặc bạn có thể kiểm tra tài liệu ngôn ngữ Pine trên FMZ để biết hướng dẫn. Sau đó làm theo hướng dẫn từng bước để làm quen với các ngữ pháp, hướng dẫn, chức năng và biến tích hợp khác nhau.
Khi bắt đầu học ngôn ngữ Pine, rất cần phải hiểu các khái niệm liên quan như quá trình thực thi của chương trình kịch bản ngôn ngữ Pine. Chiến lược ngôn ngữ Pine chạy dựa trên biểu đồ. Có thể hiểu rằng chiến lược ngôn ngữ Pine là một loạt các tính toán và hoạt động, được thực hiện trên biểu đồ theo thứ tự chuỗi thời gian từ dữ liệu sớm nhất đã được tải lên biểu đồ. Số lượng dữ liệu mà biểu đồ ban đầu tải được giới hạn. Trong giao dịch thực, số lượng dữ liệu tối đa thường được xác định dựa trên khối lượng dữ liệu tối đa được trả về bởi giao diện trao đổi, và số lượng dữ liệu tối đa trong quá trình kiểm tra lại được xác định dựa trên dữ liệu được cung cấp bởi nguồn dữ liệu của hệ thống kiểm tra lại.bar_index
trong ngôn ngữ Pine.
plot(bar_index, "bar_index")
Cácplot
sử dụng rất đơn giản, nó là để vẽ một đường trên biểu đồ theo các thông số đầu vào, dữ liệu đầu vào làbar_index
, và dòng được đặt tên làbar_index
. Có thể thấy rằng giá trị của dòng có tên là bar_index trên Bar đầu tiên là 0, và nó tăng 1 bên phải khi Bar tăng.
Bởi vì các thiết lập của chiến lược là khác nhau, các phương pháp thực hiện mô hình của chiến lược là khác nhau, họ có thể được chia thànhclosing price model
vàreal-time price model
Chúng tôi cũng đã giới thiệu ngắn gọn các khái niệm của họ trước đây.
Mô hình giá đóng cửa
Khi mã chiến lược được thực hiện, thời gian của K-line Bar hiện tại được thực hiện hoàn toàn, và khi K-line được đóng, thời gian K-line đã hoàn thành.
Mô hình giá thời gian thực
Khi mã chiến lược được thực hiện, bất kể thanh K-line hiện tại đã đóng hay chưa, logic chiến lược Pine sẽ được thực hiện khi thị trường thay đổi mỗi lần, và tín hiệu giao dịch được kích hoạt sẽ được thực hiện ngay lập tức.
Khi chiến lược ngôn ngữ Pine được thực hiện từ trái sang phải trên biểu đồ, các K-line Bars trên biểu đồ được chia thànhHistorical Bars
vàReal-time Bars
:
Cửa hàng lịch sử
Khi chiến lược được thiết lập thành Historical Bars
. Chiến lược logic được thực hiện chỉ một lần trên mỗihistorical bar
.
Khi chiến lược được đặt thành historical bars
. Chiến lược logic được thực hiện chỉ một lần trên mỗihistorical bar
.
Tính toán dựa trên Bars lịch sử: Mã chiến lược được thực hiện một lần trong trạng thái đóng của thanh lịch sử, và sau đó mã chiến lược tiếp tục được thực hiện trong thanh lịch sử tiếp theo cho đến khi tất cả các thanh lịch sử được thực hiện một lần.
Bar thời gian thực
Khi chiến lược được thực hiện đến thanh K-line cuối cùng ở bên phải, thanh là thanh thời gian thực. Sau khi thanh thời gian thực đóng, thanh trở thành thanh thời gian thực vượt qua (trở thành thanh lịch sử). Một thanh thời gian thực mới sẽ được tạo ra ở bên phải của biểu đồ.
Khi chiến lược được đặt thành
Tính toán dựa trên thời gian thực Bar:
Nếu chiến lược được đặt thành high
, low
, close
được xác định trên các thanh lịch sử, và các giá trị này có thể thay đổi mỗi khi thị trường thay đổi trên các thanh thời gian thực. do đó, dữ liệu như các chỉ số được tính trên cơ sở các giá trị này cũng sẽ thay đổi trong thời gian thực. trên một thanh thời gian thực,close
luôn luôn đại diện cho giá hiện tại mới nhất, vàhigh
vàlow
luôn đại diện cho điểm cao nhất và điểm thấp nhất đạt được kể từ khi bắt đầu thanh thời gian thực hiện. Các biến tích hợp này đại diện cho giá trị cuối cùng của thanh thời gian thực khi nó được cập nhật lần cuối.
Cơ chế khôi phục khi thực hiện các chiến lược trên thời gian thực Bar (mô hình giá thời gian thực): Trong quá trình thực hiện Bar thời gian thực, việc thiết lập lại các biến xác định bởi người dùng trước mỗi lần lặp lại mới của chiến lược được gọi là rollback.
Chú ý:
/*backtest
...
..
.
*/
Nội dung của gói là thông tin cấu hình backtest được lưu dưới dạng mã trên nền tảng FMZ.
/*backtest
start: 2022-06-03 09:00:00
end: 2022-06-08 15:00:00
period: 1m
basePeriod: 1m
exchanges: [{"eid":"Bitfinex","currency":"BTC_USD"}]
*/
var n = 0
if not barstate.ishistory
runtime.log("before n + 1, n:", n, " current bar_index:", bar_index)
n := n + 1
runtime.log("after n + 1, n:", n, " current bar_index:", bar_index)
plot(n, title="n")
Chúng tôi chỉ kiểm tra cảnh thực hiện trong thời gian thực Bars, vì vậy chúng tôi sử dụng cácnot barstate.ishistory
biểu thức để giới hạn sự tích lũy của biến n chỉ trong thời gian thực Bar, và sử dụngruntime.log
từ đường cong n được vẽ bằng cách sử dụng hàm vẽplot
, có thể thấy rằng n luôn luôn là 0 khi chiến lược đang chạy trong các Bars lịch sử. Khi thực thi Bar thời gian thực, hoạt động thêm 1 vào n được kích hoạt, và hoạt động thêm 1 vào n được thực hiện khi chiến lược được thực thi trong mỗi vòng của Bar thời gian thực. Có thể quan sát được từ thông báo nhật ký rằng n sẽ được đặt lại giá trị cuối cùng được gửi bởi chiến lược thực thi Bar trước đó khi mã chiến lược được thực thi lại trong mỗi vòng. Cập nhật giá trị n sẽ được gửi khi mã chiến lược được thực thi trên Bar thời gian thực lần cuối cùng, vì vậy bạn có thể thấy rằng giá trị của đường cong n tăng 1 với mỗi sự gia tăng Bar bắt đầu từ thanh thời gian thực trên biểu.
Tóm lại:
Do sự đảo ngược dữ liệu, các hoạt động vẽ, chẳng hạn như đường cong trên biểu đồ cũng có thể gây ra việc vẽ lại. Ví dụ: hãy sửa đổi mã thử nghiệm ngay bây giờ cho giao dịch trực tiếp:
var n = 0
if not barstate.ishistory
runtime.log("before n + 1, n:", n, " current bar_index:", bar_index)
n := open > close ? n + 1 : n
runtime.log("after n + 1, n:", n, " current bar_index:", bar_index)
plot(n, title="n")
Ảnh chụp màn hình thời gian A
Ảnh chụp màn hình thời gian B
Chúng tôi chỉ sửa đổi câu:n := open > close ? n + 1 : n
, chỉ thêm 1 vào n khi giá thực thời gian hiện tại là một đường âm (tức là giá mở cao hơn giá đóng). Có thể thấy rằng trong biểu đồ đầu tiên (thời gian A), vì giá mở cao hơn giá đóng (dòng âm) vào thời điểm đó, n đã tích lũy lên 1, và giá trị của n được hiển thị trên đường cong biểu đồ là 5. Sau đó thị trường thay đổi và giá được cập nhật như được hiển thị trong biểu đồ thứ hai (thời gian B). Tại thời điểm này, giá mở thấp hơn giá đóng (dòng dương), và giá trị n quay trở lại mà không tăng thêm 1.
Variable context trong các hàm
Theo một số mô tả trên các hướng dẫn của Pine, các biến trong hàm có những khác biệt sau đây so với các biến bên ngoài hàm:
Lịch sử của các biến chuỗi được sử dụng trong hàm Pine được tạo ra với mỗi cuộc gọi liên tiếp đến hàm. Nếu hàm không được gọi trên mọi thanh mà kịch bản chạy, điều này sẽ dẫn đến sự khác biệt giữa các giá trị lịch sử của chuỗi bên trong và bên ngoài khối cục bộ của hàm. Do đó, nếu hàm không được gọi trên mỗi thanh, các chuỗi được tham chiếu bên trong và bên ngoài hàm với cùng một giá trị chỉ mục sẽ không tham chiếu đến cùng một điểm lịch sử.
Không quan trọng, chúng ta sẽ tìm ra nó bằng một mã thử nghiệm chạy trên FMZ:
/*backtest
start: 2022-06-03 09:00:00
end: 2022-06-08 15:00:00
period: 1m
basePeriod: 1m
exchanges: [{"eid":"Bitfinex","currency":"BTC_USD"}]
*/
f(a) => a[1]
f2() => close[1]
oneBarInTwo = bar_index % 2 == 0
plotchar(oneBarInTwo ? f(close) : na, title = "f(close)", color = color.red, location = location.absolute, style = shape.xcross, overlay = true, char = "A")
plotchar(oneBarInTwo ? f2() : na, title = "f2()", color = color.green, location = location.absolute, style = shape.circle, overlay = true, char = "B")
plot(close[2], title = "close[2]", color = color.red, overlay = true)
plot(close[1], title = "close[1]", color = color.green, overlay = true)
Ảnh chụp màn hình chạy backtest
Mã thử nghiệm tương đối đơn giản, chủ yếu để kiểm tra dữ liệu được tham chiếu bằng hai phương pháp, cụ thể là:f(a) => a[1]
vàf2() => close[1]
.
f(a) => a[1]
: Sử dụng phương pháp truyền tham số, hàm trả vềa[1]
finally.
f2() => close[1]
: Sử dụng biến tích hợpclose
trực tiếp, và chức năng trở lạiclose[1]
finally.
Các[]
biểu tượng được sử dụng để chỉ giá trị lịch sử của biến chuỗi dữ liệu, và đóng [1] đề cập đến dữ liệu giá đóng trên thanh trước giá đóng hiện tại. mã thử nghiệm của chúng tôi vẽ tổng cộng 4 loại dữ liệu trên biểu đồ:
plotchar(oneBarInTwo ? f(close) : na, title = "f(close)", color = color.red, location = location.absolute, style = shape.xcross, overlay = true, char = "A")
Kéo một ký tự f(close)
.
plotchar(oneBarInTwo ? f2() : na, title = "f2()", color = color.green, location = location.absolute, style = shape.circle, overlay = true, char = "B")
Kéo một ký tự f2()
.
plot(close[2], title = "close[2]", color = color.red, overlay = true)
Kéo một đường, màu đỏ, và vị trí được vẽ (trên trục Y) là:close[2]
, đó là giá đóng của thanh thứ hai trước thanh hiện tại (đếm 2 thanh bên trái).
plot(close[1], title = "close[1]", color = color.green, overlay = true)
Kéo một đường, màu xanh lá cây, và vị trí được vẽ (trên trục Y) là:close[1]
, đó là giá đóng của thanh đầu tiên trước thanh hiện tại (đếm 1 thanh bên trái).
Nó có thể được nhìn thấy từ ảnh chụp màn hình của chiến lược backtesting rằng mặc dù cả hai chức năngf(a) => a[1]
được sử dụng để vẽ dấu A và chức năngf2() => close[1]
được sử dụng để vẽ dấu B [1] để tham khảo các dữ liệu lịch sử trên chuỗi dữ liệu, vị trí dấu plot(close[2], title = "close[2]", color = color.red, overlay = true)
, dữ liệu được sử dụng để vẽ đường làclose[2]
.
Lý do là để tính toán liệu để vẽ các dấu hiệu bar_index
. Các dấu f(a) => a[1]
sẽ không giống với giá trị được tham chiếu bởi hàmf2() => close[1]
nếu hàm không được gọi trên mọi Bar (ngay cả khi cả hai đều sử dụng cùng một chỉ số như [1]).
Một số chức năng tích hợp cần phải được tính trên mỗi Bar để tính toán kết quả của họ một cách chính xác
Để minh họa tình hình này bằng một ví dụ đơn giản:
res = close > close[1] ? ta.barssince(close < close[1]) : -1
plot(res, style = plot.style_histogram, color=res >= 0 ? color.red : color.blue)
Chúng ta viết mã gọi hàmta.barssince(close < close[1])
trong một toán tử bacondition ? value1 : value2
. Điều này làm cho hàm ta.barssince được gọi chỉ khiclose > close[1]
Nhưng...ta.barssince
chức năng là để tính toán số lượng K-đường từ lần cuối cùngclose < close[1]
Khi gọi hàm ta.barssince, nó luôn luôn close > close[1], nghĩa là giá đóng cửa hiện tại lớn hơn giá đóng cửa của Bar trước đó. Khi gọi hàm ta.barssince, điều kiện close < close [1] không được thiết lập và không có vị trí gần đây mà nó giữ.
ta.barssince: Khi được gọi, hàm trả về na nếu điều kiện chưa bao giờ được đáp ứng trước khi dòng K hiện tại.
Như trong biểu đồ:
Vì vậy, khi biểu đồ được vẽ, chỉ có dữ liệu với một giá trị cho biến res (-1) được vẽ.
Để tránh vấn đề này, chúng ta chỉ cần lấyta.barssince(close < close[1])
chức năng gọi ra từ toán tử ba và viết nó bên ngoài bất kỳ nhánh có thể có điều kiện, làm cho nó thực hiện tính toán trên mỗi K-line Bar.
a = ta.barssince(close < close[1])
res = close > close[1] ? a : -1
plot(res, style = plot.style_histogram, color=res >= 0 ? color.red : color.blue)
Khái niệm chuỗi thời gian rất quan trọng trong ngôn ngữ Pine, và nó là một khái niệm mà chúng ta phải hiểu khi chúng ta học ngôn ngữ Pine. Dòng thời gian không phải là một kiểu mà là một cấu trúc cơ bản để lưu trữ các giá trị liên tục của các biến theo thời gian. Chúng ta biết rằng các kịch bản Pine dựa trên biểu đồ, và nội dung cơ bản nhất được hiển thị trong biểu đồ là biểu đồ đường K. Dòng thời gian nơi mỗi giá trị được liên kết với dấu thời gian của thanh đường K.open
là một biến tích hợp (được tích hợp trong ngôn ngữ Pine), và cấu trúc của nó là để lưu trữ chuỗi thời gian của giá mở của mỗi K-line Bar.open
đại diện cho giá mở của tất cả các K-line Bars từ thanh đầu tiên ở đầu biểu đồ K-line hiện tại đến thanh nơi kịch bản hiện tại được thực hiện. Nếu biểu đồ K-line hiện tại là một khoảng thời gian 5 phút, khi chúng tôi trích dẫn (hoặc sử dụng)open
trong mã chiến lược Pine, đó là giá mở của thanh K-line khi mã chiến lược được thực hiện hiện tại. Nếu bạn muốn tham chiếu đến các giá trị lịch sử trong một chuỗi thời gian, bạn cần sử dụng[]
Khi chiến lược Pine được thực hiện trên một K-line Bar nhất định, sử dụngopen[1]
để tham khảo giá mở của K-line Bar trước đó (tức là giá mở của K-line trước đó) tham chiếu đếnopen
chuỗi thời gian mà trên đó K-line Bar này đang được thực hiện hiện bởi kịch bản.
Các biến trên chuỗi thời gian rất thuận tiện để tính toán
Chúng ta hãy lấy hàm tích hợpta.cum
ví dụ:
ta.cum
Cumulative (total) sum of `source`. In other words it's a sum of all elements of `source`.
ta.cum(source) → series float
RETURNS
Total sum series.
ARGUMENTS
source (series int/float)
SEE ALSO
math.sum
Mã thử nghiệm:
v1 = 1
v2 = ta.cum(v1)
plot(v1, title="v1")
plot(v2, title="v2")
plot(bar_index+1, title="bar_index")
Có rất nhiều chức năng tích hợp nhưta.cum
có thể xử lý dữ liệu về chuỗi thời gian trực tiếp.ta.cum
là sự tích lũy của các giá trị tương ứng với các biến được truyền vào mỗi K-line Bar, và tiếp theo chúng tôi sử dụng một biểu đồ để làm cho nó dễ hiểu hơn.
Quá trình vận hành chiến lược | Biến đổi tích hợp bar_index | v1 | v2 |
---|---|---|---|
Chiến lược chạy trên đầu tiên K-line Bar | 0 | 1 | 1 |
Chiến lược chạy trên K-line Bar thứ hai | 1 | 1 | 2 |
Chiến lược chạy trên K-line Bar thứ ba | 2 | 1 | 3 |
… | … | … | … |
Chiến lược chạy trên N + 1th K-line Bar | N | 1 | N+1 |
Có thể thấy rằng v1, v2 và thậm chí bar_index đều là cấu trúc chuỗi thời gian, và có dữ liệu tương ứng trên mỗi thanh. Cho dù mã thử nghiệm sử dụng mô hình
Bởi vì biến v1 là 1 trên mỗi Bar, khita.cum(v1)
hàm được thực hiện trên K-line Bar đầu tiên, chỉ có Bar đầu tiên, do đó kết quả tính toán là 1 và được gán cho biến v2.
Khi nàota.cum(v1)
là thực hiện trên K-line Bar thứ hai, đã có 2 K-line Bars (bar_index biến tích hợp tương ứng với cái đầu tiên là 0, và thứ hai tương ứng với bar_index biến tích hợp là 1), do đó kết quả tính toán là 2, được gán cho biến v2, và vân vân.bar_index
được tăng từ 0, sau đóbar_index + 1
Trên biểu đồ, chúng ta cũng có thể thấy rằng các đường thẳngv2
vàbar_index
thực sự chồng chéo.
Tương tự như vậy, tôi cũng có thể sử dụngta.cum
Tất cả tôi cần làm là viết nó như thế này:ta.cum(close)
, Khi chiến lược chạy đến thanh thời gian thực ở bên phải, kết quả được tính bằngta.cum(close)
là tổng giá đóng của tất cả các thanh trên biểu đồ (nếu không chạy sang bên phải, nó chỉ tích lũy lên đến thanh hiện tại).
Các biến trên chuỗi thời gian cũng có thể được tính bằng các toán tử, chẳng hạn như mã:ta.sma(high - low, 14)
, trừ biến tích hợphigh
(giá cao nhất của K-line Bar) từlow
(giá thấp nhất của K-line Bar), và cuối cùng sử dụngta.sma
chức năng để tính giá trị trung bình.
Kết quả của một cuộc gọi hàm cũng sẽ để lại dấu vết của các giá trị trong chuỗi thời gian.
v1 = ta.highest(high, 10)[1]
v2 = ta.highest(high[1], 10)
plot(v1, title="v1", overlay=true)
plot(v2, title="v2", overlay=true)
Mã thử nghiệm chạy trong quá trình backtesting, và nó có thể được quan sát thấy rằng các giá trị củav1
vàv2
Kết quả được tính bằng cách gọi hàm sẽ để lại dấu vết của giá trị trong chuỗi thời gian, chẳng hạn nhưta.highest(high, 10)
trong mãta.highest(high, 10)[1]
. Kết quả được tính bằng cách gọi chức năng cũng có thể sử dụng [1] để tham khảo giá trị lịch sử của nó. Dựa trênta.highest(high, 10)
tương ứng với thanh trước của thanh hiện tại, kết quả tính toán làta.highest(high[1], 10)
Vậy.ta.highest(high[1], 10)
vàta.highest(high, 10)[1]
là chính xác tương đương.
Sử dụng chức năng vẽ khác để xuất thông tin xác minh:
a = ta.highest(close, 10)[1]
b = ta.highest(close[1], 10)
plotchar(true, title="a", char=str.tostring(a), location=location.abovebar, color=color.red, overlay=true)
plotchar(true, title="b", char=str.tostring(b), location=location.belowbar, color=color.green, overlay=true)
Chúng ta có thể thấy rằng các giá trị của biến a và biến b trong chuỗi thời gian được hiển thị trên và dưới các Bars tương ứng. Chúng ta có thể giữ mã vẽ này trong quá trình học tập, bởi vì chúng ta thường có thể cần phải xuất thông tin trên biểu đồ để quan sát trong quá trình kiểm tra và thử nghiệm.
Trong phần đầu của hướng dẫn, chúng tôi đã tóm tắt một số sự khác biệt trong việc sử dụng ngôn ngữ Pine trên FMZ và Trading View.indicator()
, strategy()
, vàlibrary()
Tất nhiên, để tương thích với các phiên bản trước của Pine scripts, các chiến lược như://@version=5
, indicator()
, strategy()
Một số thiết lập chiến lược cũng có thể được thiết lập bằng cách vượt qua các tham số trongstrategy()
function.
<version>
<declaration_statement>
<code>
Các<version>
Thông tin kiểm soát phiên bản có thể bị bỏ qua.
Ngôn ngữ Pine sử dụng//
như một biểu tượng bình luận một dòng, vì ngôn ngữ Pine không có biểu tượng bình luận nhiều dòng. FMZ mở rộng biểu tượng bình luận/**/
cho các bình luận nhiều dòng.
Các dòng trong kịch bản không phải là bình luận hoặc chỉ thị trình biên dịch là các câu lệnh, thực hiện thuật toán của kịch bản. Một câu lệnh có thể là một trong những nội dung này.
if
, for
, while
hoặcswitch
cấu trúcBáo cáo có thể được sắp xếp theo nhiều cách khác nhau
space
hoặc ```tab` (bấm tab). ký tự đầu tiên của chúng cũng phải là ký tự đầu tiên của dòng. Các dòng bắt đầu ở vị trí đầu tiên, theo định nghĩa, trở thành một phần của phạm vi toàn cầu của kịch bản.local block
Một khối cục bộ phải được nhấp một tab hoặc bốn khoảng trống (nếu không, nó sẽ được phân tích như là mã liên kết của dòng trước, tức là được đánh giá là nội dung liên tục của dòng mã trước), và mỗi khối cục bộ xác định phạm vi cục bộ khác nhau.Ví dụ: nó bao gồm ba khối cục bộ, một trong tuyên bố hàm tùy chỉnh và hai trong tuyên bố biến sử dụng cấu trúc if, như sau:
indicator("", "", true) // declaration statement (global scope), can be omitted
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)
runtime.log("color", color = plotColor) // Call a built-in function to output the log (global scope)
Các đường dài có thể được chia thành nhiều đường, hoặc
a = open + high + low + close
Nó có thể được bọc như sau: (lưu ý rằng số lượng khoảng trống được nhấp vào mỗi dòng không thể là số nhân của 4):
a = open +
high +
low +
close
Một cuộc gọi dài có thể được gói như sau:
close1 = request.security(syminfo.tickerid, "D", close) // syminfo.tickerid daily level closing price data series for the current trading pair
close2 = request.security(syminfo.tickerid, "240", close) // syminfo.tickerid 240-minute level closing price data series for the current trading pair
plot(ta.correlation(close, open, 100), // line-long plot() calls can be wrapped
color = color.new(color.purple, 40),
style = plot.style_area,
trackprice = true)
Tuy nhiên, vì một khối cục bộ phải bắt đầu với một dấu nhọn trong ngữ pháp (4 khoảng trống hoặc 1 tab), khi chia nó vào dòng tiếp theo, sự tiếp tục của một câu phải bắt đầu với nhiều hơn một dấu nhọn (không bằng 4 số nhân khoảng trống). Ví dụ:
test(c, o) =>
ret = c > o ?
(c > o+5000 ?
1 :
0):
(c < o-5000 ?
-1 :
0)
a = test(close, open)
plot(a, title="a")
Trước khi nhận ra các biến, chúng ta phải hiểu khái niệm của
(A-Z)
hoặc chữ viết nhỏ(a-z)
thư hoặc dấu chân dưới(_)
là ký tự đầu tiên của dấu hiệu.Ví dụ như các dấu hiệu có tên sau:
fmzVar
_fmzVar
fmz666Var
funcName
MAX_LEN
max_len
maxLen
3barsDown // Wrong naming! It used a numeric character as the leading character of the marker
Giống như hầu hết các ngôn ngữ lập trình, ngôn ngữ Pine cũng có các đề xuất viết.
// name variables, constants
GREEN_COLOR = #4CAF50
MAX_LOOKBACK = 100
int fastLength = 7
// name functions
zeroOne(boolValue) => boolValue ? 1 : 0
Các toán tử là một số biểu tượng hoạt động được sử dụng trong các ngôn ngữ lập trình để xây dựng biểu thức, và các biểu thức là các quy tắc tính toán được thiết kế cho các mục đích tính toán nhất định khi chúng ta viết chiến lược.
Các toán tử phân công, toán tử số học, toán tử so sánh, toán tử logic,? :
các nhà khai thác thứ ba,[]
các toán tử tham chiếu lịch sử.
Lấy toán tử số học*
ví dụ, nó khác với vấn đề loại do kết quả trả về của toán tử ngôn ngữ Pine trên Trading View. Mã thử nghiệm sau đây được cung cấp:
//@version=5
indicator("")
lenInput = input.int(14, "Length")
factor = year > 2020 ? 3 : 1
adjustedLength = lenInput * factor
ma = ta.ema(close, adjustedLength) // Compilation error!
plot(ma)
Khi thực hiện kịch bản này trên Trading View, một lỗi biên dịch sẽ xảy ra.adjustedLength = lenInput * factor
, kết quả làseries int
type (series), nhưng các tham số thứ hai của hàmta.ema
nhưng không có những hạn chế nghiêm ngặt như vậy trên FMZ, mã trên có thể chạy bình thường.
Hãy xem xét việc sử dụng các toán tử khác nhau cùng nhau.
Có 2 loại toán tử phân bổ:=
, :=
, mà chúng ta đã thấy trong một số ví dụ ở phần đầu của hướng dẫn.
Các=
Các biến được khởi tạo, tuyên bố và gán với=
sẽ bắt đầu với giá trị đó trên mỗi Bar tiếp theo.
a = close // Use built-in variables to assign values to a
b = 10000 // Use numerical assignment
c = "test" // Use string assignment
d = color.green // Use color value assignment
plot(a, title="a")
plot(b, title="b")
plotchar(true, title="c", char=str.tostring(c), color=d, overlay=true)
Lưu ý rằng tuyên bố giao nhiệm vụa = close
, biến a trên mỗi thanh là giá đóng cửa hiện tại (khép lại) của thanh. Các biến khácb
, c
, d
không thay đổi và có thể được thử nghiệm trong hệ thống backtest trên FMZ, và kết quả có thể được nhìn thấy trên biểu đồ.
:=
được sử dụng để gán lại các giá trị cho các biến hiện có.:=
Điều khiển được sử dụng để sửa đổi các giá trị của các biến đã được khai báo và khởi tạo.
Nếu chúng ta sử dụng:=
để gán một giá trị cho một biến chưa khởi tạo hoặc được tuyên bố, nó sẽ gây ra một lỗi, ví dụ:
a := 0
Do đó,:=
toán tử gán thường được sử dụng để gán lại các biến hiện có, ví dụ:
a = close > open
b = 0
if a
b := b + 1
plot(b)
Đánh giá nếuclose > open
Mã trong khối địa phương của câu lệnh ifb := b + 1
được thực hiện, và các toán tử phân bổ:=
Sau đó, chúng ta sử dụng hàm biểu đồ để vẽ giá trị của biến b trên mỗi BAR của chuỗi thời gian trên biểu đồ, và kết nối chúng thành một đường thẳng.
Chúng ta có nghĩ rằng khi một đường dương BAR xuất hiện, b sẽ tiếp tục tích lũy bởi 1? tất nhiên không, ở đây chúng ta tuyên bố và khởi tạo biến b là 0 mà không cần sử dụng bất kỳ từ khóa nào. câu nàyb=0
được thực hiện trên mỗi BAR, vì vậy chúng ta có thể thấy rằng kết quả của mã này là để thiết lập lại biến b để 0 mỗi lần, nếu biến a là đúng, đó là, phù hợp vớiclose > open
, thì b sẽ tăng thêm 1 khi mã được thực thi trong vòng này, và b là 1 khi hàm phác thảo vẽ, nhưng b được gán lại cho 0 khi mã được thực hiện trong vòng tiếp theo.
Khi nói đến các toán tử phân công, chúng ta phải mở rộng về hai từ khóa:var
, varip
var
Trong thực tế, chúng ta đã thấy và sử dụng từ khóa này trong các hướng dẫn trước đây, nhưng chúng ta không thảo luận chi tiết về nó vào thời điểm đó.
var là một từ khóa được sử dụng để phân bổ và khởi tạo một lần các biến. Nói chung, ngữ pháp gán biến mà không chứa từ khóa var làm cho giá trị của biến sẽ được ghi đè mỗi khi dữ liệu được cập nhật. Ngược lại, khi các biến được gán bằng cách sử dụng từ khóa var, chúng có thể
giữ trạng thái mặc dù cập nhật dữ liệu.
Chúng tôi vẫn sử dụng ví dụ này, nhưng chúng tôi sử dụngvar
từ khóa khi gán giá trị cho b ở đây.
a = close > open
var b = 0
if a
b := b + 1
plot(b)
Cácvar
từ khóa cho phép biến b thực hiện chỉ việc gán ban đầu, và sau đó nó sẽ không đặt lại b đến 0 mỗi khi logic chiến lược được thực hiện, vì vậy nó có thể được quan sát từ đường vẽ tại thời gian chạy rằng b là số lượng BAR đường dương xuất hiện khi đường K hiện tại BAR đã được kiểm tra lại.
Các biến được tuyên bố bởi var có thể được viết không chỉ trong phạm vi toàn cầu, mà còn trong các khối mã, chẳng hạn như ví dụ này:
strategy(overlay=true)
var a = close
var b = 0.0
var c = 0.0
var green_bars_count = 0
if close > open
var x = close
b := x
green_bars_count := green_bars_count + 1
if green_bars_count >= 10
var y = close
c := y
plot(a, title = "a")
plot(b, title = "b")
plot(c, title = "c")
Các biến
khác nhau
Chúng ta thấy từ khóavarip
lần đầu tiên, chúng ta có thể nhìn vào mô tả của từ khóa này:
varp (var intrabar persist) là một từ khóa để gán và khởi tạo một lần các biến. Nó tương tự như từ khóa var, nhưng các biến được tuyên bố bằng varip giữ lại giá trị của chúng khi cập nhật K-line thời gian thực.
Có khó hiểu không? Không quan trọng, chúng tôi giải thích thông qua một ví dụ, nó dễ hiểu.
strategy(overlay=true)
// test var varip
var i = 0
varip ii = 0
// Print the i and ii changed in each round of the strategy logic on the chart
plotchar(true, title="ii", char=str.tostring(ii), location=location.abovebar, color=color.red)
plotchar(true, title="i", char=str.tostring(i), location=location.belowbar, color=color.green)
// Increment i and ii by 1 for each round of logic execution
i := i + 1
ii := ii + 1
Mã thử nghiệm này có hiệu suất khác nhau trên
Mô hình thanh:
Bạn có nhớ rằng việc thực hiện chiến lược mà chúng tôi đã giải thích trước đây được chia thành giai đoạn BAR lịch sử và giai đoạn BAR thời gian thực?i
, ii
được khai báo trongvar
, varip
thực hiện các hoạt động gia tăng tại mỗi vòng thực thi mã chiến lược. Do đó, có thể thấy rằng các số hiển thị trên K-line BAR của kết quả backtest được gia tăng 1 một lần. Khi giai đoạn K-line lịch sử kết thúc, giai đoạn K-line thời gian thực bắt đầu. Các biến được tuyên bố bởi var và varip bắt đầu trải qua những thay đổi khác nhau. Bởi vì nó là Bar Model, mã chiến lược sẽ được thực hiện một lần cho mỗi thay đổi giá trong một K-line BAR,i := i + 1
vàii := ii + 1
Sự khác biệt là ii sẽ được sửa đổi mỗi lần. Mặc dù i được sửa đổi mỗi lần, giá trị trước đó sẽ được khôi phục khi logic chiến lược được thực thi trong vòng tiếp theo (hãy nhớ cơ chế đảo ngược mà chúng tôi đã giải thích trong chương trước
Mô hình dấu hiệu: Vì mô hình Tick chỉ thực hiện logic chiến lược một lần mỗi K-line BAR. Vì vậy, trong mô hình giá đóng, các biến được tuyên bố bởi var và varip cư xử giống hệt nhau trong ví dụ trên tăng thêm 1 cho mỗi K-line BAR trong giai đoạn K-line lịch sử và giai đoạn K-line thời gian thực.
Các nhà khai thác | Mô tả |
---|---|
+ | Thêm |
- | Phân trừ |
* | Xử nhân |
/ | Phân khúc |
% | Mô-đun |
Các+
và-
Các toán tử số học khác chỉ có thể được sử dụng như các toán tử nhị phân và nó sẽ báo cáo lỗi nếu được sử dụng như các toán tử đơn.
+
, kết quả tính toán là một chuỗi, giá trị sẽ được chuyển đổi thành dạng chuỗi, và sau đó các chuỗi được nối lại với nhau. Nếu đó là toán tử khác, nó sẽ cố gắng chuyển đổi chuỗi thành một giá trị và sau đó thực hiện hoạt động.a = 1 + 1
b = 1 + 1.1
c = 1 + "1.1"
d = "1" + "1.1"
e = 1 + na
runtime.log("a:", a, ", b:", b, ", c:", c, ", d:", d, ", e:", e)
// a: 2 , b: 2.1 , c: 11.1 , d: 11.1 , e: NaN
Ngôn ngữ Pine trên FMZ hơi khác với ngôn ngữ Pine trên Trading View, ngôn ngữ Pine trên FMZ không quá nghiêm ngặt về các loại biến. Ví dụ:
a = 1 * "1.1"
b = "1" / "1.1"
c = 5 % "A"
plot(a)
plot(b)
plot(c)
Nó hoạt động trên FMZ, nhưng nó báo cáo một lỗi kiểu trên Trading View. Nếu cả hai toán tử của toán tử toán học là chuỗi, hệ thống chuyển đổi các chuỗi thành các giá trị số và sau đó tính toán chúng. Nếu một chuỗi phi số không thể được tính toán, kết quả của hoạt động hệ thống là một giá trị null
Các toán tử so sánh đều là các toán tử nhị phân.
Các nhà khai thác | Mô tả |
---|---|
< | < |
> | > |
<= | <= |
>= | >= |
== | == |
!= | != |
Ví dụ thử nghiệm:
a = 1 > 2
b = 1 < 2
c = "1" <= 2
d = "1" >= 2
e = 1 == 1
f = 2 != 1
g = open > close
h = na > 1
i = 1 > na
runtime.log("a:", a, ", b:", b, ", c:", c, ", d:", d, ", e:", e, ", f:", f, ", g:", g, ", h:", h, ", i:", i)
// a: false , b: true , c: true , d: false , e: true , f: true , g: false , h: false , i: false
Như chúng ta có thể thấy, các toán tử so sánh rất đơn giản để sử dụng, nhưng đây cũng là các toán tử chúng tôi sử dụng nhiều nhất khi viết chiến lược. cả số lượng giá trị và biến tích hợp có thể được so sánh, chẳng hạn nhưclose
, open
, vv
Như với các nhà điều hành, có một sự khác biệt về ngôn ngữ Pine giữa FMZ và Trading View.d = "1" >= 2
sẽ không báo cáo lỗi trên FMZ, và nó sẽ được thực hiện bằng cách chuyển đổi chuỗi thành một giá trị trước và sau đó so sánh hoạt động. Trên Trading View, nó sẽ báo cáo lỗi.
Các nhà khai thác | Biểu tượng mã | Mô tả |
---|---|---|
không | không | Động tác viên đơn, không phải là các hoạt động |
và | và | Các toán tử nhị phân và các hoạt động |
hoặc | hoặc | Các toán tử nhị phân, hoặc các hoạt động |
Khi nói đến các toán tử logic, thì chúng ta phải nói về các bảng giá trị thực.
a = 1 == 1 // An expression formed by using comparison operators, the result is a Boolean value
b = 1 != 1
c = not b // Logical not operators
d = not a // Logical not operators
runtime.log("test the logical operator:and", "#FF0000")
runtime.log("a:", a, ", c:", c, ", a and c:", a and c)
runtime.log("a:", a, ", b:", b, ", a and b:", a and b)
runtime.log("b:", b, ", c:", c, ", b and c:", b and c)
runtime.log("d:", d, ", b:", b, ", d and b:", d and b)
runtime.log("test the logical operator:or", "#FF0000")
runtime.log("a:", a, ", c:", c, ", a or c:", a or c)
runtime.log("a:", a, ", b:", b, ", a or b:", a or b)
runtime.log("b:", b, ", c:", c, ", b or c:", b or c)
runtime.log("d:", d, ", b:", b, ", d or b:", d or b)
runtime.error("stop")
Để không được in quá nhiều tin nhắn, chúng tôi ném một lỗi với cácruntime.error("stop")
Sau đó, chúng ta có thể quan sát thông tin đầu ra, và chúng ta có thể thấy rằng nội dung in thực sự giống với bảng giá trị thực.
Các biểu thức thứ ba sử dụng toán tử thứ ba? :
kết hợp với các toán tửcondition ? valueWhenConditionIsTrue : valueWhenConditionIsFalse
Chúng ta cũng đã sử dụng chúng trong các bài học trước đây. cái gọi là biểu thức ba, toán tử ba có nghĩa là có ba toán tử trong đó.
Trongcondition ? valueWhenConditionIsTrue : valueWhenConditionIsFalse
, condition
là điều kiện phán đoán. Nếu đúng, giá trị của biểu thức là:valueWhenConditionIsTrue
Nếu.condition
là false, sau đó giá trị của biểu thức làvalueWhenConditionIsFalse
.
Ví dụ về một minh chứng thuận tiện, mặc dù ít sử dụng thực tế:
a = close > open
b = a ? "positive line" : "negative line"
c = not a ? "negative line" : "positive line"
plotchar(a, location=location.abovebar, color=color.red, char=b, overlay=true)
plotchar(not a, location=location.belowbar, color=color.green, char=c, overlay=true)
Điều gì sẽ xảy ra nếu chúng ta gặp một doji? Nó không quan trọng! biểu thức ba cũng có thể được lồng nhau, như chúng ta đã làm trong hướng dẫn trước.
a = close > open
b = a ? math.abs(close-open) > 30 ? "positive line" : "doji" : math.abs(close-open) > 30 ? "negative line" : "doji"
c = not a ? math.abs(close-open) > 30 ? "negative line" : "doji" : math.abs(close-open) > 30 ? "positive line" : "doji"
plotchar(a, location=location.abovebar, color=color.red, char=b, overlay=true)
plotchar(not a, location=location.belowbar, color=color.green, char=c, overlay=true)
Trên thực tế, nó tương đương với việc thay thếvalueWhenConditionIsTrue
vàvalueWhenConditionIsFalse
trongcondition ? valueWhenConditionIsTrue : valueWhenConditionIsFalse
với một biểu thức thứ ba khác.
Sử dụng toán tử lịch sử[]
để tham chiếu đến các giá trị lịch sử trên một chuỗi thời gian. các giá trị lịch sử này là các giá trị của biến trên thanh K-line trước khi thanh K-line hiện tại khi kịch bản đang chạy.[]
Động số được sử dụng sau các biến, biểu thức và các cuộc gọi hàm.[]
Các dấu ngoặc vuông là sự dịch chuyển của dữ liệu lịch sử mà chúng ta muốn tham chiếu từ K-line BAR hiện tại Ví dụ, nếu tôi muốn trích dẫn giá đóng của K-line BAR cuối cùng, chúng ta viết nó như sau:close[1]
.
Chúng ta đã thấy điều gì đó như thế này trong các bài học trước:
high[10]
ta.sma(close, 10)[1]
ta.highest(high, 10)[20]
close > nz(close[1], open)
Các[]
toán tử chỉ có thể được sử dụng một lần trên cùng một giá trị, vì vậy nó là sai để viết nó như thế này, và một lỗi sẽ được báo cáo:
a = close[1][2] // error
Ở đây, ai đó có thể nói rằng người điều hành[]
được sử dụng cho cấu trúc chuỗi, dường như cấu trúc chuỗi (series) tương tự như mảng!
Hãy sử dụng một ví dụ để minh họa sự khác biệt giữa chuỗi và mảng trong ngôn ngữ Pine.
strategy("test", overlay=true)
a = close
b = close[1]
c = b[1]
plot(a, title="a")
plot(b, title="b")
plot(c, title="c")
a = close[1][2]
sẽ báo cáo một lỗi, nhưng:
b = close[1]
c = b[1]
Nhưng nếu được viết riêng, nó sẽ không báo cáo một lỗi.b = close [1]
, b nên là một giá trị, nhưngc = b[1]
, b vẫn có thể được sử dụng để tham chiếu đến giá trị lịch sử một lần nữa bằng cách sử dụng toán tử lịch sử. Có thể thấy rằng khái niệm về chuỗi trong ngôn ngữ Pine không đơn giản như một mảng. Nó có thể được hiểu như giá trị lịch sử trên thanh cuối cùng của gần (được gán cho b), b cũng là một cấu trúc chuỗi thời gian (sâu chuỗi thời gian), và h của nó