Một kế hoạch chiến lược thực hiện tín hiệu TradingView khác

Tác giả:Lydia., Tạo: 2022-12-15 21:23:24, Cập nhật: 2023-09-18 20:01:55

img

Một kế hoạch chiến lược thực hiện tín hiệu TradingView khác

Các nhà giao dịch thường sử dụng TradingView biết rằng TradingView có thể đẩy tin nhắn đến các nền tảng khác. Trong nền tảng Digest của FMZ, có một chiến lược đẩy tín hiệu TradingView được xuất bản trong thư viện, nơi nội dung của các tin nhắn được đẩy được viết trong url yêu cầu, có phần không linh hoạt.

Các kịch bản và nguyên tắc

Một số người mới có thể bị nhầm lẫn bởi tiêu đề của bài viết này và mô tả ở trên, nó không quan trọng! Hãy bắt đầu với một mô tả rõ ràng về các kịch bản và nguyên tắc nhu cầu.

  1. Các kịch bản nhu cầu: Vì vậy, chúng ta muốn nó làm công việc gì? Nói đơn giản, chúng ta có rất nhiều chỉ số, chiến lược, mã, v.v. mà chúng ta có thể chọn sử dụng trên TradingView, có thể chạy trực tiếp trên TradingView để vẽ đường, tính toán và hiển thị tín hiệu giao dịch. Ngoài ra, TradingView có dữ liệu giá thời gian thực và đủ dữ liệu đường K để tạo điều kiện cho việc tính toán các chỉ số khác nhau. Các mã kịch bản trên TradingView được gọi là ngôn ngữ PINE. Điều duy nhất không thuận tiện là giao dịch bot thực trên TradingView. Mặc dù ngôn ngữ PINE được hỗ trợ trên FMZ, nó cũng có thể được sử dụng cho giao dịch bot thực. Tuy nhiên, vẫn có một số người hâm mộ TradingView vẫn muốn đặt lệnh bằng cách sử dụng các tín hiệu từ các biểu đồ trên TradingView, vì vậy điều này có thể được giải quyết bởi FMZ. Vì vậy, trong bài viết này, chúng tôi sẽ giải thích chi tiết về giải pháp.

  2. Nguyên tắc:

img

Có 4 chủ đề liên quan đến toàn bộ chương trình này, ngắn gọn là như sau:

img

Vì vậy, nếu bạn muốn sử dụng nó theo những cách này, bạn cần những chuẩn bị này:

  1. Các kịch bản chạy trên TradingView chịu trách nhiệm gửi các yêu cầu tín hiệu đến giao diện API mở rộng của FMZ. Tài khoản TradingView phải là một thành viên PRO ít nhất.
  2. Để triển khai một chương trình docker trên FMZ, nó cần phải là loại có thể truy cập giao diện trao đổi (như máy chủ ở Singapore, Nhật Bản, Hồng Kông, v.v.).
  3. Thiết lập API KEY của sàn giao dịch để (đặt lệnh) hoạt động khi tín hiệu TradingView được gửi trên FMZ.
  4. Bạn cần có một chiến lược thực hiện tín hiệu TradingView, được thảo luận chủ yếu trong bài viết này.

Chiến lược thực thi tín hiệu TradingView

Thiết kế của TradingView Signal Execution Strategy trong phiên bản trước không phải là rất linh hoạt. Thông điệp chỉ có thể được viết vào url của yêu cầu được gửi bởi TradingView. Nếu chúng ta muốn TradingView viết một số thông tin biến trong Body khi đẩy tin nhắn, chúng ta không thể làm gì tại thời điểm này. Ví dụ như nội dung tin nhắn trên TradingView:

img

Sau đó, TradingView có thể được thiết lập như trong hình để viết thông điệp trong request Body và gửi nó đến giao diện API mở rộng của FMZ.

Trong một loạt các giao diện API mở rộng của FMZ, chúng ta cần sử dụng cácCommandRobotgiao diện, thường được gọi như sau:

https://www.fmz.com/api/v1?access_key=xxx&secret_key=yyyy&method=CommandRobot&args=[186515,"ok12345"]

Cácaccess_keysecret_keytrongquerycủa URL yêu cầu này là mở rộngAPI KEYcủa nền tảng FMZ, ở đây các bản demo thiết lập đểxxxyyyyVậy làm thế nào để tạo khóa này?https://www.fmz.com/m/account, tạo ra trên nó, giữ nó đúng cách, không tiết lộ nó.

img

Trở lại vấn đề, hãy tiếp tục nói về vấn đề giao diện củaCommandRobotNếu bạn cần truy cậpCommandRobotgiao diện,methodtrong yêu cầu sẽ được thiết lập là:CommandRobot. Chức năng củaCommandRobotgiao diện là để gửi một thông điệp tương tác đến một bot thực sự với một ID thông qua nền tảng FMZ, do đó, các tham sốargscó chứa ID bot thực sự và tin nhắn. ví dụ url yêu cầu ở trên là để gửi tin nhắnok12345đến một chương trình robot thực sự với ID 186515.

Trước đây, phương pháp này được sử dụng để yêu cầu giao diện CommandRobot của FMZ mở rộng API. Thông điệp chỉ có thể được viết trong ví dụ trên, chẳng hạn nhưok12345. Nếu thông điệp là trong cơ quan yêu cầu, bạn cần phải sử dụng một phương pháp khác:

https://www.fmz.com/api/v1?access_key=xxx&secret_key=yyyy&method=CommandRobot&args=[130350,+""]

Bằng cách này, yêu cầu có thể gửi nội dung của cơ thể trong yêu cầu như một thông điệp tương tác đến bot thực sự với ID130350thông qua nền tảng FMZ. Nếu thông báo trên TradingView được thiết lập là:{"close": {{close}}, "name": "aaa"}, thì robot thực sự với ID của130350sẽ nhận được hướng dẫn tương tác:{"close": 39773.75, "name": "aaa"}

Để chiến lược thực thi tín hiệu TradingView hiểu chính xác lệnh được gửi bởi TradingView khi nhận lệnh tương tác, các định dạng tin nhắn sau đây nên được thỏa thuận trước:

{
    Flag: "45M103Buy",     // Marker, which can be specified at will
    Exchange: 1,           // Specify exchange trading pairs
    Currency: "BTC_USDT",  // Trading pair
    ContractType: "swap",  // Contract type, swap, quarter, next_quarter, fill in spot for spot
    Price: "{{close}}",    // Opening position or closing position price, -1 is the market price
    Action: "buy",         // Transaction type [buy: spot buying, sell: spot selling, long: go long futures, short: go short futures, closesell: buy futures and close short positions, close buy: sell futures and close long positions]
    Amount: "0",           // Transaction amount
}

Chiến lược được thiết kế như một kiến trúc đa giao dịch, vì vậy nhiều đối tượng giao dịch có thể được cấu hình trên chiến lược này, tức là, hoạt động đặt lệnh của nhiều tài khoản khác nhau có thể được kiểm soát. Chỉ có trao đổi trong cấu trúc tín hiệu chỉ định giao dịch sẽ được vận hành. Cài đặt 1 là để cho phép tín hiệu này vận hành tài khoản giao dịch tương ứng với đối tượng giao dịch đầu tiên được thêm vào. Nếu Spot ContractType được thiết lập là Spot, các hợp đồng tương lai sẽ viết các hợp đồng cụ thể, chẳng hạn như trao đổi cho các hợp đồng vĩnh viễn. Danh sách giá thị trường có thể được truyền vào -1. Cài đặt hành động khác nhau cho các vị trí tương lai, Spot, mở và đóng, và nó không thể được đặt sai.

Tiếp theo, bạn có thể thiết kế mã chiến lược.

//Signal structure
var Template = {
    Flag: "45M103Buy",     // Marker, which can be specified at will
    Exchange: 1,           // Specify exchange trading pairs
    Currency: "BTC_USDT",  // Trading pair
    ContractType: "swap",  // Contract type, swap, quarter, next_quarter, fill in spot for spot
    Price: "{{close}}",    // Opening position or closing position price, -1 is the market price
    Action: "buy",         // Transaction type [buy: spot buying, sell: spot selling, long: go long futures, short: go short futures, closesell: buy futures and close short positions, close buy: sell futures and close long positions]
    Amount: "0",           // Transaction amount
}

var BaseUrl = "https://www.fmz.com/api/v1"   // FMZ extended API interface address
var RobotId = _G()                           // Current real bot ID
var Success = "#5cb85c"    // Color for success
var Danger = "#ff0000"     // Color for danger
var Warning = "#f0ad4e"    // Color for alert
var buffSignal = []

// Check signal message format
function DiffObject(object1, object2) {
    const keys1 = Object.keys(object1)
    const keys2 = Object.keys(object2)
    if (keys1.length !== keys2.length) {
        return false
    }
    for (let i = 0; i < keys1.length; i++) {
        if (keys1[i] !== keys2[i]) {
            return false
        }
    }
    return true
}

function CheckSignal(Signal) {
    Signal.Price = parseFloat(Signal.Price)
    Signal.Amount = parseFloat(Signal.Amount)
    if (Signal.Exchange <= 0 || !Number.isInteger(Signal.Exchange)) {
        Log("The minimum number of the exchange is 1 and it is an integer", Danger)
        return
    }
    if (Signal.Amount <= 0 || typeof(Signal.Amount) != "number") {
        Log("The transaction amount cannot be less than 0 and it is numerical type", typeof(Signal.Amount), Danger)
        return
    }
    if (typeof(Signal.Price) != "number") {
        Log("Price must be a value", Danger)
        return
    }
    if (Signal.ContractType == "spot" && Signal.Action != "buy" && Signal.Action != "sell") {
        Log("The command is to operate spot, Action error, Action:", Signal.Action, Danger)
        return 
    }
    if (Signal.ContractType != "spot" && Signal.Action != "long" && Signal.Action != "short" && Signal.Action != "closesell" && Signal.Action != "closebuy") {
        Log("The command is to operate future, Action error, Action:", Signal.Action, Danger)
        return 
    }
    return true
}

function commandRobot(url, accessKey, secretKey, robotId, cmd) {
    // https://www.fmz.com/api/v1?access_key=xxx&secret_key=xxx&method=CommandRobot&args=[xxx,+""]
    url = url + '?access_key=' + accessKey + '&secret_key=' + secretKey + '&method=CommandRobot&args=[' + robotId + ',+""]'
    var postData = {
        method:'POST', 
        data:cmd
    }
    var headers = "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36\nContent-Type: application/json"
    var ret = HttpQuery(url, postData, "", headers)
    Log("Simulate a webhook request from TradingView, sending a POST request for testing purposes:", url, "body:", cmd, "response:", ret)
}

function createManager() {
    var self = {}
    self.tasks = []
    
    self.process = function() {
        var processed = 0
        if (self.tasks.length > 0) {
            _.each(self.tasks, function(task) {
                if (!task.finished) {
                    processed++
                    self.pollTask(task)
                }
            })
            if (processed == 0) {
                self.tasks = []
            }
        }
    }
    
    self.newTask = function(signal) {
        // {"Flag":"45M103Buy","Exchange":1,"Currency":"BTC_USDT","ContractType":"swap","Price":"10000","Action":"buy","Amount":"0"}
        var task = {}
        task.Flag = signal["Flag"]
        task.Exchange = signal["Exchange"]
        task.Currency = signal["Currency"]
        task.ContractType = signal["ContractType"]
        task.Price = signal["Price"]
        task.Action = signal["Action"]
        task.Amount = signal["Amount"]
        task.exchangeIdx = signal["Exchange"] - 1
        task.pricePrecision = null
        task.amountPrecision = null 
        task.error = null 
        task.exchangeLabel = exchanges[task.exchangeIdx].GetLabel()
        task.finished = false 
        
        Log("Create task:", task)
        self.tasks.push(task)
    }
    
    self.getPrecision = function(n) {
        var precision = null 
        var arr = n.toString().split(".")
        if (arr.length == 1) {
            precision = 0
        } else if (arr.length == 2) {
            precision = arr[1].length
        } 
        return precision
    }
    
    self.pollTask = function(task) {
        var e = exchanges[task.exchangeIdx]
        var name = e.GetName()
        var isFutures = true
        e.SetCurrency(task.Currency)
        if (task.ContractType != "spot" && name.indexOf("Futures_") != -1) {
            // Non-spot, then set the contract
            e.SetContractType(task.ContractType)
        } else if (task.ContractType == "spot" && name.indexOf("Futures_") == -1) {
            isFutures = false 
        } else {
            task.error = "The ContractType in the command does not match the configured exchange object type"
            return 
        }
        
        var depth = e.GetDepth()
        if (!depth || !depth.Bids || !depth.Asks) {
            task.error = "Order book data exception"
            return 
        }
        
        if (depth.Bids.length == 0 && depth.Asks.length == 0) {
            task.error = "No orders on the market entry position"
            return 
        }
        
        _.each([depth.Bids, depth.Asks], function(arr) {
            _.each(arr, function(order) {
                var pricePrecision = self.getPrecision(order.Price)
                var amountPrecision = self.getPrecision(order.Amount)
                if (Number.isInteger(pricePrecision) && !Number.isInteger(self.pricePrecision)) {
                    self.pricePrecision = pricePrecision
                } else if (Number.isInteger(self.pricePrecision) && Number.isInteger(pricePrecision) && pricePrecision > self.pricePrecision) {
                    self.pricePrecision = pricePrecision
                }
                if (Number.isInteger(amountPrecision) && !Number.isInteger(self.amountPrecision)) {
                    self.amountPrecision = amountPrecision
                } else if (Number.isInteger(self.amountPrecision) && Number.isInteger(amountPrecision) && amountPrecision > self.amountPrecision) {
                    self.amountPrecision = amountPrecision
                }
            })
        })

        if (!Number.isInteger(self.pricePrecision) || !Number.isInteger(self.amountPrecision)) {
            task.err = "Failed to obtain precision"
            return 
        }
        
        e.SetPrecision(self.pricePrecision, self.amountPrecision)
        
        // buy: spot buying, sell: spot selling, long: go long futures, short: go short futures, closesell: buy futures and close short positions, close buy: sell futures and close long positions
        var direction = null 
        var tradeFunc = null 
        if (isFutures) {
            switch (task.Action) {
                case "long": 
                    direction = "buy"
                    tradeFunc = e.Buy 
                    break
                case "short": 
                    direction = "sell"
                    tradeFunc = e.Sell
                    break
                case "closesell": 
                    direction = "closesell"
                    tradeFunc = e.Buy 
                    break
                case "closebuy": 
                    direction = "closebuy"
                    tradeFunc = e.Sell
                    break
            }
            if (!direction || !tradeFunc) {
                task.error = "Wrong transaction direction:" + task.Action
                return 
            }
            e.SetDirection(direction)
        } else {
            if (task.Action == "buy") {
                tradeFunc = e.Buy 
            } else if (task.Action == "sell") {
                tradeFunc = e.Sell 
            } else {
                task.error = "Wrong transaction direction:" + task.Action
                return 
            }
        }
        var id = tradeFunc(task.Price, task.Amount)
        if (!id) {
            task.error = "Failed to place an order"
        }
        
        task.finished = true
    }
    
    return self
}

var manager = createManager()
function HandleCommand(signal) {
    // Detect whether interactive command is received
    if (signal) {
        Log("Receive interactive command:", signal)     // Receive the interactive command, print the interactive command
    } else {
        return                            // If it is not received, it will be returned directly without processing
    }
    
    // Check whether the interactive command is a test instruction. The test instruction can be sent out by the current strategy interaction control for testing
    if (signal.indexOf("TestSignal") != -1) {
        signal = signal.replace("TestSignal:", "")
        // Call the FMZ extended API interface to simulate the webhook of the TradingView, and the message sent by the interactive button TestSignal: {"Flag":"45M103Buy","Exchange":1,"Currency":"BTC_USDT","ContractType":"swap","Price":"10000","Action":"buy","Amount":"0"}
        commandRobot(BaseUrl, FMZ_AccessKey, FMZ_SecretKey, RobotId, signal)
    } else if (signal.indexOf("evalCode") != -1) {
        var js = signal.split(':', 2)[1]
        Log("Execute debug code:", js)
        eval(js)
    } else {
        // Process signal command
        objSignal = JSON.parse(signal)
        if (DiffObject(Template, objSignal)) {
            Log("Received transaction signal command:", objSignal)
            buffSignal.push(objSignal)
            
            // Check the trading volume and exchange number
            if (!CheckSignal(objSignal)) {
                return
            }
            
            // Create task
            manager.newTask(objSignal)
        } else {
            Log("Command cannot be recognized", signal)
        }
    }
}

function main() {
    Log("WebHook address:", "https://www.fmz.com/api/v1?access_key=" + FMZ_AccessKey + "&secret_key=" + FMZ_SecretKey + "&method=CommandRobot&args=[" + RobotId + ',+""]', Danger)
    Log("Transaction type [buy: spot buying, sell: spot selling, long: go long futures, short: go short futures, closesell: buy futures and close short positions, close buy: sell futures and close long positions]", Danger)
    Log("Command template:", JSON.stringify(Template), Danger)
    
    while (true) {
        try {
            // Process interactions
            HandleCommand(GetCommand())
            
            // Process tasks
            manager.process()
            
            if (buffSignal.length > maxBuffSignalRowDisplay) {
                buffSignal.shift()
            }
            var buffSignalTbl = {
                "type" : "table",
                "title" : "Signal recording",
                "cols" : ["Flag", "Exchange", "Currency", "ContractType", "Price", "Action", "Amount"],
                "rows" : []
            }
            for (var i = buffSignal.length - 1 ; i >= 0 ; i--) {
                buffSignalTbl.rows.push([buffSignal[i].Flag, buffSignal[i].Exchange, buffSignal[i].Currency, buffSignal[i].ContractType, buffSignal[i].Price, buffSignal[i].Action, buffSignal[i].Amount])
            }
            LogStatus(_D(), "\n", "`" + JSON.stringify(buffSignalTbl) + "`")
            Sleep(1000 * SleepInterval)
        } catch (error) {
            Log("e.name:", error.name, "e.stack:", error.stack, "e.message:", error.message)
            Sleep(1000 * 10)
        }
    }
}

Các thông số chiến lược và tương tác:

img

Địa chỉ chiến lược đầy đủ của Chiến lược thực hiện tín hiệu Trading View:https://www.fmz.com/strategy/392048

Thử nghiệm đơn giản

Trước khi chạy chiến lược, đối tượng trao đổi nên được cấu hình và hai tham số AccessKey trên nền tảng FMZSecretKey trên nền tảng FMZ nên được đặt trong các tham số chiến lược. Khi chạy, nó sẽ hiển thị:

img

Nó sẽ in ra địa chỉ WebHook, hỗ trợ lệnh hành động, và định dạng tin nhắn cần phải điền vào trên TradingView.

https://www.fmz.com/api/v1?access_key=22903bab96b26584dc5a22522984df42&secret_key=73f8ba01014023117cbd30cb9d849bfc&method=CommandRobot&args=[505628,+""]

Chỉ cần sao chép và dán nó trực tiếp vào vị trí tương ứng trên TradingView.

Nếu bạn muốn mô phỏng một tín hiệu được gửi bởi TradingView, bạn có thể nhấp vào nút TestSignal trên tương tác chiến lược.

img

Chiến lược này gửi một yêu cầu của riêng nó (giống như một TradingView gửi một yêu cầu tín hiệu), gọi giao diện API mở rộng FMZ để gửi một thông điệp đến chính chiến lược:

{"Flag":"45M103Buy","Exchange":1,"Currency":"BTC_USDT","ContractType":"swap","Price":"16000","Action":"buy","Amount":"1"}

Chiến lược hiện tại sẽ nhận được một thông điệp tương tác khác và thực hiện, và đặt một lệnh cho giao dịch.

Kiểm tra sử dụng TradingView trong hiện trường thực tế

Sử dụng thử nghiệm TradingView đòi hỏi rằng tài khoản TradingView ở cấp Pro. Trước khi thử nghiệm, bạn cần biết một số kiến thức trước.

Hãy lấy một tập lệnh PINE đơn giản (bất định vị được tìm thấy và sửa đổi trên TradingView) làm ví dụ

//@version=5
strategy("Consecutive Up/Down Strategy", overlay=true)
consecutiveBarsUp = input(3)
consecutiveBarsDown = input(3)
price = close
ups = 0.0
ups := price > price[1] ? nz(ups[1]) + 1 : 0
dns = 0.0
dns := price < price[1] ? nz(dns[1]) + 1 : 0
if (not barstate.ishistory and ups >= consecutiveBarsUp and strategy.position_size <= 0)
    action = strategy.position_size < 0 ? "closesell" : "long"
    strategy.order("ConsUpLE", strategy.long, 1, comment=action)
if (not barstate.ishistory and dns >= consecutiveBarsDown and strategy.position_size >= 0)
    action = strategy.position_size > 0 ? "closebuy" : "short"
    strategy.order("ConsDnSE", strategy.short, 1, comment=action)
  1. PINE kịch bản có thể đính kèm một số thông tin khi kịch bản gửi lệnh hướng dẫn

Sau đây là các vị trí giữ. Ví dụ, nếu tôi viết{{strategy.order.contracts}}trong hộp message của cảnh báo, một thông báo sẽ được gửi khi lệnh được kích hoạt (theo cài đặt trên cảnh báo, email push, webhook url request, pop-up, vv), và thông báo sẽ chứa số lệnh được thực hiện lần này.

{{strategy.position_size}}- Trả về giá trị của cùng một từ khóa trong Pine, tức là kích thước của vị trí hiện tại.{{strategy.order.action}}- Trả lại chuỗi buy hoặc sell cho lệnh được thực hiện.{{strategy.order.contracts}}- Trả về số lượng hợp đồng mà lệnh đã được thực hiện.{{strategy.order.price}}- Trả lại giá của lệnh thực hiện.{{strategy.order.id}}- Trả về ID của lệnh được thực thi (dòng chuỗi được sử dụng như là tham số đầu tiên trong một trong các cuộc gọi hàm tạo ra lệnh: strategy.entry,strategy.exithoặc chiến lược.{{strategy.order.comment}}- Trả lại bình luận của lệnh được thực thi (dòng chuỗi được sử dụng trong tham số bình luận trong một trong các cuộc gọi hàm tạo ra lệnh: strategy.entry,strategy.exitNếu không có bình luận nào được chỉ định, giá trị củastrategy.order.idsẽ được sử dụng.{{strategy.order.alert_message}}- Trả về giá trị của tham số alert_message có thể được sử dụng trong mã Pine của chiến lược khi gọi một trong những hàm được sử dụng để đặt lệnh: strategy.entry,strategy.exit, hoặc strategy.order. Điều này chỉ được hỗ trợ trong Pine v4.{{strategy.market_position}}- Trả lại vị trí hiện tại của chiến lược dưới dạng chuỗi: long, flat, hoặc short.{{strategy.market_position_size}}- Trả về kích thước của vị trí hiện tại dưới dạng giá trị tuyệt đối (tức là một số không âm).{{strategy.prev_market_position}}- Trả lại vị trí trước của chiến lược dưới dạng một chuỗi: long, flat, hoặc short.{{strategy.prev_market_position_size}}- Trả về kích thước của vị trí trước đó dưới dạng giá trị tuyệt đối (tức là một số không âm).

  1. Xây dựng tin nhắn kết hợp với Hệ thống thực thi tín hiệu TradingView
{
    "Flag":"{{strategy.order.id}}",
    "Exchange":1,
    "Currency":"BTC_USDT",
    "ContractType":"swap",
    "Price":"-1",
    "Action":"{{strategy.order.comment}}",
    "Amount":"{{strategy.order.contracts}}"
}
  1. Hãy để TradingView gửi một tín hiệu theo việc chạy kịch bản PINE. Bạn cần thiết lập cảnh báo khi tải kịch bản trên TradingView

Khi kịch bản PINE trên TradingView kích hoạt giao dịch, một yêu cầu url webhook sẽ được gửi.

img

Robot FMZ sẽ thực hiện tín hiệu này.

img

Mã trong bài viết này chỉ để tham khảo, và bạn có thể tự điều chỉnh và mở rộng nó khi sử dụng thực tế.


Có liên quan

Thêm nữa