Bài hướng dẫn giới thiệu về ngôn ngữ PINE của FMZ Quant

Tác giả:Lydia., Tạo: 2022-09-23 15:23:34, Cập nhật: 2024-02-27 16:47:41

[TOC]

img

Bài hướng dẫn giới thiệu về ngôn ngữ PINE của FMZ Quant

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:

    1. Chiến lược Pine trên FMZ, mã nhận dạng phiên bản ở đầu mã//@versionstrategy, indicatortuyên bố ở đầu mã không bắt buộc phải viết, FMZ không hỗ trợimportnhập khẩulibraryhoạ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)
    
    1. Một số cài đặt liên quan đến giao dịch của chiến lược (script) được đặt bởi các tham số Pine Language Trading Class Library trên giao diện chiến lược FMZ.
    • 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_ticktham số củastrategychứ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_ticktham số nên được thiết lập thànhtrue.calc_on_every_ticktham 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 Pine Language Trading Class Library.

      img

    • 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à Trading PairContract Code. Ngoài việc đặt cặp giao dịch rõ ràng, cũng cần thiết phải đặt mã hợp đồng cụ thể trong tham số Variety Code của mẫu Pine Language Trading Class Library trong giao dịch thực và backtesting. Ví dụ: đối với hợp đồng vĩnh viễn, hãy điền vàoswapVí dụ: một số sàn giao dịch có hợp đồng hàng quý, bạn có thể điền vàoquarterCá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ênMẫu lập luận của thư viện ngành ngôn ngữ thôngtrong tài liệu ngôn ngữ Pine.

    1. Chức năng cho các phần mở rộng FMZ: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.logCá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)
      
    1. Cácoverlaytham 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àooverlayhỗ 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.

    1. Giá trị củasyminfo.mintickbiến tích hợp

    Cá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 Pine Language Trading Class Library trên FMZbot/backtestĐịnh giá chính xác tiền tệ 2 có nghĩa là giá chính xác đến vị trí thập phân thứ hai khi giao dịch, và đơn vị thay đổi giá tối thiểu là 0,01.syminfo.minticklà 0,01.

    1. Giá trung bình trong FMZ PINE Script đều bao gồm hoa hồng

    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ý).

Những điều cơ bản về ngôn ngữ thông

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.

Thực hiện mô hình

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_indextrong ngôn ngữ Pine.

plot(bar_index, "bar_index")

img

Cácplotsử 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 modelreal-time price modelChú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 BarsReal-time Bars:

  • Cửa hàng lịch sử

    Khi chiến lược được thiết lập thành Tick Model và bắt đầu thực hiện, tất cả các K-line Bars trên biểu đồ ngoại trừ một bên phải nhất là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 Bar Model và bắt đầu thực hiện, tất cả các thanh trên biểu đồ là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 Tick Model và bắt đầu thực thi, logic chiến lược sẽ được thực hiện một lần cho mỗi thay đổi thị trường trên thanh thời gian thực. Khi chiến lược được đặt thành Bar Model và bắt đầu thực hiện, thanh thời gian thực sẽ không được hiển thị trên biểu đồ.

    Tính toán dựa trên thời gian thực Bar: Nếu chiến lược được đặt thành Bar Model và biểu đồ không hiển thị các thanh thời gian thực, mã chiến lược sẽ chỉ được thực hiện một lần khi thanh hiện tại đóng. Nếu chiến lược được đặt thành Tick Model, tính toán trên thanh thời gian thực hoàn toàn khác với thanh lịch sử, và mã chiến lược sẽ được thực hiện một lần cho mỗi thay đổi thị trường trên các thanh giao dịch trực tiếp.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,closeluôn luôn đại diện cho giá hiện tại mới nhất, vàhighlowluô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")
    

    img

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.ishistorybiể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.logtừ đườ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:

  1. Mã chiến lược được thực hiện một lần mỗi khi thị trường được cập nhật khi chiến lược bắt đầu thực hiện trong một thanh thời gian thực.
  2. Khi được thực thi trong thời gian thực Bar, các biến được lăn trở lại mỗi lần trước khi mã chiến lược được thực thi.
  3. Khi được thực hiện trong một thanh thời gian thực, các biến được gửi một lần khi đóng được cập nhật.

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 Aimg

Ảnh chụp màn hình thời gian Bimg

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

    img

    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]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ợpclosetrự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ự A, màu đỏ, nó được vẽ khi oneBarInTwo là đúng, và vị trí được vẽ (trên trục Y là: giá trị được trả về bởif(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ự B, màu xanh lá cây, nó chỉ được vẽ khi oneBarInTwo là đúng, và vị trí được vẽ (trên trục Y là: giá trị được trả về bởif2().

  • 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 AB trên biểu đồ hoàn toàn khác nhau. vị trí của dấu A luôn rơi vào đường đỏ, đó là đường được rút ra bởi mã trong chiến lượcplot(close[2], title = "close[2]", color = color.red, overlay = true), dữ liệu được sử dụng để vẽ đường làclose[2].

img

Lý do là để tính toán liệu để vẽ các dấu hiệu AB thông qua chỉ số của K-line Bar, đó là biến tích hợpbar_index. Các dấu AB không được vẽ trên mỗi K-line Bar (giá trị hàm được gọi khi vẽ).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.barssincechứ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 đồ:

img

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)

Dòng thời gian

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.openlà 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)opentrong 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 đếnopenchuỗ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.cumví 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.cumcó thể xử lý dữ liệu về chuỗi thời gian trực tiếp.ta.cumlà 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 Bar hoặc mô hình Tick, sự khác biệt duy nhất là liệu Bar thời gian thực có được hiển thị trên biểu đồ hay không. Để kiểm tra nhanh, chúng tôi sử dụng mô hình Tick để kiểm tra.

    img

    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 + 1Trên biểu đồ, chúng ta cũng có thể thấy rằng các đường thẳngv2bar_indexthực sự chồng chéo.

    img

    Tương tự như vậy, tôi cũng có thể sử dụngta.cumTấ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.smachứ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ủav1v2Kế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)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.

    img

Cấu trúc kịch bản

Cấu trúc chung

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.

Các ý kiến

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.

  • Tuyên bố biến thể
  • Việc phân bổ lại các biến
  • Tuyên bố chức năng
  • Các cuộc gọi hàm tích hợp, các cuộc gọi hàm được xác định bởi người dùng
  • if, for, whilehoặcswitchcấu trúc

Báo cáo có thể được sắp xếp theo nhiều cách khác nhau

  • Một số câu lệnh có thể được thể hiện trong một dòng, chẳng hạn như hầu hết các tuyên bố biến, các dòng chỉ chứa một cuộc gọi hàm hoặc các tuyên bố hàm một dòng.
  • Các lệnh trong phạm vi toàn cầu của một kịch bản (tức là các phần không phải là một phần của một khối cục bộ) không thể bắt đầu với mộtspacehoặ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.
  • Một cấu trúc hoặc tuyên bố chức năng nhiều dòng luôn đòi hỏi mộtlocal blockMộ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.
  • Nhiều câu chỉ dẫn một dòng có thể được liên kết trong một dòng bằng cách sử dụng dấu phẩy (,) làm phân tách.
  • Một dòng có thể chứa bình luận hoặc chỉ có bình luận.
  • Các đường cũng có thể được bọc (tiếp tục trên nhiều đường).

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)

Mã ngắt dòng

Các đường dài có thể được chia thành nhiều đường, hoặc bọc lên. Một đường bị bọc phải được nhét vào bất kỳ số khoảng trống nào, miễn là nó không phải là số nhân của 4 (các ranh giới này được sử dụng để nhét vào các khối cục bộ).

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")

Đánh dấu và người vận hành

Các dấu hiệu

Trước khi nhận ra các biến, chúng ta phải hiểu khái niệm của marker trước tiên.chức năngbiến số(được sử dụng để đặt tên các biến và hàm).Chức năngsẽ được nhìn thấy trong các hướng dẫn sau này của chúng tôi, hãy học về marker đầu tiên.

    1. Đánh dấu phải bắt đầu bằng chữ cái lớn(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.
    1. Các ký tự tiếp theo sau ký tự đầu tiên của một dấu hiệu có thể là mộtthư, Đánh dấu, hoặc mộtsố.
    1. Việc đặt tên cho các dấu hiệu là nhạy cảm với chữ cái lớn.

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.

    1. Tất cả các chữ cái lớn được sử dụng để đặt tên các hằng số.
    1. Sử dụngvỏ lạc đà dướicho các tên đánh dấu khác.
// name variables, constants
GREEN_COLOR = #4CAF50
MAX_LOOKBACK = 100
int fastLength = 7

// name functions
zeroOne(boolValue) => boolValue ? 1 : 0

Các nhà khai thác

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 inttype (series), nhưng các tham số thứ hai của hàmta.emanhư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ác nhà điều hành giao nhiệm vụ

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, dkhô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 > openMã 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ụngvartừ khóa khi gán giá trị cho b ở đây.

    a = close > open 
    var b = 0 
    if a
        b := b + 1
    
    plot(b)
    

    Cácvartừ 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 a giữ giá đóng của thanh đầu tiên trong loạt. Chất biến b giữ giá đóng của thanh giá green đầu tiên trong chuỗi. Chất biến c giữ giá đóng của thanh green thứ mười trong chuỗi.

  • khác nhau

    Chúng ta thấy từ khóavariplầ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 Bar ModelTick Model:

    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, varipthự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 + 1ii := ii + 1Sự 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 Model Execution?), và giá trị của i sẽ không được cập nhật cho đến khi BAR K-line hiện tại được hoàn thành (tức là giá trị trước đó sẽ không được khôi phục khi logic chiến lược được thực hiện trong vòng tiếp theo). Vì vậy, có thể thấy rằng biến i vẫn tăng 1 cho mỗi BAR. Nhưng biến ii được tích lũy nhiều lần cho mỗi BAR.

    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 toán tử số họ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+-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.

  1. Cả hai mặt của toán tử số học đều là kiểu số, kết quả là kiểu số, số nguyên hoặc dấu phẩy động tùy thuộc vào kết quả của hoạt động.
  2. Nếu một trong những operands là một chuỗi và các toán tử là+, 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.
  3. Nếu một trong các toán tử là na, kết quả của phép tính là giá trị nullna, và nó sẽ hiển thị NaN khi in trên FMZ.
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ị nullna.

Các toán tử so sánh

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" >= 2sẽ 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 toán tử logic
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
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.

Nhà khai thác thứ ba

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 : valueWhenConditionIsFalseChú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, conditionlà điều kiện phán đoán. Nếu đúng, giá trị của biểu thức là:valueWhenConditionIsTrueNếu.conditionlà 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ếvalueWhenConditionIsTruevalueWhenConditionIsFalsetrongcondition ? valueWhenConditionIsTrue : valueWhenConditionIsFalsevới một biểu thức thứ ba khác.

Nhà khai thác lịch sử

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ó


Thêm nữa