또 다른 TradingView 신호 실행 전략 계획

저자:리디아, 창작: 2022-12-15 21:23:24, 업데이트: 2023-09-18 20:01:55

img

또 다른 TradingView 신호 실행 전략 계획

트레이딩뷰를 자주 사용하는 트레이더들은 트레이딩뷰가 다른 플랫폼으로 메시지를 푸시할 수 있다는 것을 알고 있습니다. FMZ 플랫폼의 우리의 Digest에서는 푸시 메시지의 내용이 요청 url에 기록되어있는 라이브러리에 게시된 트레이딩뷰 신호 푸시 전략이있었습니다. 이 기사에서는 새로운 방식으로 트레이딩뷰 신호 실행 전략을 재설계합니다.

시나리오 와 원칙

이 기사 제목과 위의 설명이 초보자들을 혼란스럽게 할 수 있습니다. 그건 중요하지 않습니다! 수요 시나리오와 원칙에 대한 명확한 설명으로 시작해 봅시다.

  1. 수요 시나리오: 그래서 우리는 어떤 종류의 작업을 수행하기를 원합니까? 간단히 말해서, 우리는 TradingView에서 사용할 수있는 많은 지표, 전략, 코드 등을 선택할 수 있습니다. 라인을 그리고 계산하고 거래 신호를 표시하기 위해 TradingView에서 직접 실행 할 수 있습니다. 또한, TradingView에는 실시간 가격 데이터와 다양한 지표의 계산을 용이하게하는 충분한 K-라인 데이터가 있습니다. TradingView의 이러한 스크립트 코드는 PINE 언어라고합니다. 유일한 것이 편리하지 않은 것은 실제 봇이 TradingView에서 거래한다는 것입니다. PINE 언어는 FMZ에서 지원되지만 실제 봇 거래에도 사용할 수 있습니다. 그러나, 여전히 TradingView의 차트에서 신호를 사용하여 주문을 주고 싶어하는 TradingView 팬이 있습니다. 따라서 이것은 FMZ에 의해 해결 될 수 있습니다. 이 기사에서 우리는 솔루션의 세부 사항을 설명 할 것입니다.

  2. 원칙:

img

전체 계획에는 4개의 주제가 포함되어 있습니다. 요약하면 다음과 같습니다.

img

그래서 이런 식으로 사용하려면 다음과 같은 준비가 필요합니다.

  1. 트레이딩뷰에서 실행되는 스크립트는 FMZ의 확장된 API 인터페이스에 신호 요청을 전송하는 데 책임이 있습니다. 트레이딩뷰 계정은 적어도 PRO 회원이어야 합니다.
  2. FMZ에 도커 프로그램을 배포하려면 교환 인터페이스 (싱가포르, 일본, 홍콩 등의 서버) 에 액세스 할 수있는 종류가 필요합니다.
  3. 거래소의 API 키를 FMZ에서 TradingView 신호가 전송될 때 (오더를 배치) 동작으로 구성합니다.
  4. 이 문서에서 주로 논의되는 TradingView 신호 실행 전략을 가지고 있어야 합니다.

TradingView 신호 실행 전략

이전 버전의 TradingView Signal Execution Strategy의 디자인은 매우 유연하지 않습니다. 메시지는 TradingView에서 전송된 요청의 url에만 작성할 수 있습니다. 메시지를 푸시할 때 TradingView가 메세지 본체에 변수 정보를 작성하기를 원한다면 현재 아무것도 할 수 없습니다. 예를 들어, TradingView의 이러한 메시지 콘텐츠는:

img

그 다음 TradingView는 그림에서 보여진 것처럼 메시지를 요청 본체에 작성하고 FMZ의 확장 API 인터페이스로 보낼 수 있습니다. FMZ의 확장 API 인터페이스를 호출하는 방법은 무엇입니까?

FMZ의 확장된 API 인터페이스의 일련에서, 우리는CommandRobot인터페이스, 일반적으로 다음과 같이 불립니다:

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

access_key그리고secret_keyquery이 요청 url는 확장API KEYFMZ 플랫폼, 여기 데모 설정xxx그리고yyyy그럼 이 키를 어떻게 만들까요? 이 페이지에서:https://www.fmz.com/m/account, 그것을 창조, 제대로 보관, 그것을 공개하지 마십시오.

img

다시 한 번, 인터페이스 문제에 대해 계속 이야기하자CommandRobot만약 당신이CommandRobot인터페이스,method요청에서 다음으로 설정됩니다.CommandRobot. 이 기관의 기능은CommandRobot인터페이스는 FMZ 플랫폼을 통해 ID를 가진 실제 봇에 대화형 메시지를 보내는 것입니다.args실제 봇 ID와 메시지를 포함합니다. 위의 요청 url 예제는 메시지를 전송하는 것입니다.ok12345186515이라는 아이디를 가진 실제 로봇 프로그램으로

이전에는 FMZ 확장 API의 CommandRobot 인터페이스를 요청하기 위해 이 방법을 사용하였다. 메시지는 위의 예제에서만 작성될 수 있다.ok12345메시지가 요청된 기관에 있는 경우, 당신은 다른 방법을 사용해야합니다:

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

이 방법으로 요청은 ID를 가진 실제 봇에 상호 작용 메시지로 요청의 신체의 내용을 보낼 수 있습니다.130350FMZ 플랫폼을 통해 트레이딩 뷰에서 메시지가 설정되면:{"close": {{close}}, "name": "aaa"}, 그럼 진짜 로봇이130350인터랙티브 지침을 받게 됩니다.{"close": 39773.75, "name": "aaa"}

TradingView Signal Execution Strategy가 인터랙티브 명령어를 수신할 때 TradingView가 보낸 명령을 올바르게 이해하려면 다음 메시지 형식을 미리 합의해야 합니다.

{
    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
}

이 전략은 멀티 거래소 아키텍처로 설계되어 있으므로 여러 교환 객체를 이 전략에 구성할 수 있으며, 즉 여러 다른 계정의 주문 배치 동작을 제어할 수 있다. 신호 구조의 교환에서만 동작할 교환을 지정한다. 설정 1은 이 신호가 첫 추가된 교환 객체에 대응하는 교환 계정을 작동하도록 하는 것이다. 스팟 계약 유형이 스팟으로 설정되면 선물은 영구 계약에 대한 교환과 같은 특정 계약을 작성할 것이다. 시장 가격 목록은 -1으로 전달될 수 있다. 액션 설정은 선물, 스팟, 개막 및 폐쇄 포지션에 대해 다르며, 잘못 설정할 수 없다.

다음으로 전략 코드를 설계할 수 있습니다.

//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)
        }
    }
}

전략 매개 변수와 상호 작용:

img

Trading View Signal Execution Strategy의 전체 전략 주소는:https://www.fmz.com/strategy/392048

간단한 테스트

전략을 실행하기 전에 교환 객체는 구성되어야 하며, 전략 매개 변수에서 두 개의 매개 변수 AccessKey on FMZ PlatformSecretKey on FMZ Platform을 설정해야 합니다. 실행 시 표시됩니다.

img

그것은 WebHook 주소를 인쇄합니다. 지원되는 액션 명령어, 그리고 트레이딩 뷰에서 채워야 하는 메시지 형식. 중요한 것은 WebHook 주소입니다:

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

트레이딩뷰의 해당 위치로 직접 복사하고 붙여주세요.

만약 당신이 TradingView에 의해 전송된 신호를 시뮬레이션하고 싶다면, 당신은 전략 상호 작용에 있는 TestSignal 버튼을 클릭할 수 있습니다.

img

이 전략은 FMZ의 확장 API 인터페이스를 호출하여 전략 자체에 메시지를 전송하여 자체 요청을 전송합니다.

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

현재 전략은 또 다른 대화형 메시지를 수신하고 실행하고 거래를 위한 명령을 할 것입니다.

실제 현장에서 TradingView를 사용하는 테스트

트레이딩뷰 테스트를 사용하려면 트레이딩뷰 계정이 프로 레벨에 있어야 합니다. 테스트 전에 사전 지식이 필요합니다.

예를 들어 간단한 PINE 스크립트를 (TradingView에서 무작위로 발견 및 수정)

//@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 스크립트는 스크립트가 명령어를 보낼 때 어떤 정보를 첨부할 수 있습니다.

다음의 글들은 홀더입니다. 예를 들어,{{strategy.order.contracts}}경고의 메시지 상자에서, 명령이 트리거되면 메시지가 전송됩니다 (주의, 메일 푸시, 웹후크 url 요청, 팝업, 등에 대한 설정에 따라) 그리고 메시지는이 시간에 실행 된 명령의 수를 포함합니다.

{{strategy.position_size}}- 파이인에서 같은 키워드의 값을 반환, 즉 현재 위치의 크기를.{{strategy.order.action}}- 실행된 명령에 대한 문자열 buy 또는 sell을 반환합니다.{{strategy.order.contracts}}- 주문이 실행된 계약의 수를 반환합니다.{{strategy.order.price}}- 실행된 명령의 가격을 반환합니다.{{strategy.order.id}}- 실행된 명령의 ID를 반환합니다 (계열을 생성하는 함수 호출의 첫 번째 매개 변수로 사용되는 문자열: strategy.entry,strategy.exit또는 전략 (order).{{strategy.order.comment}}- 실행된 명령의 코멘트를 반환합니다. (코멘트 매개 변수에서 사용되는 문자열은 명령어를 생성하는 함수 호출 중 하나입니다.strategy.exit, 또는 strategy.order) 를 입력하지 않으면strategy.order.id사용될 것입니다.{{strategy.order.alert_message}}- 명령어를 배치하는 데 사용되는 함수 중 하나를 호출 할 때 전략의 파인 코드에서 사용할 수 있는 alert_message 매개 변수의 값을 반환합니다: strategy.entrystrategy.exit, 또는 strategy.order. 이것은 Pine v4에서만 지원됩니다.{{strategy.market_position}}- 전략의 현재 위치를 문자열로 반환합니다: , , 또는 .{{strategy.market_position_size}}- 절대 값 (즉, 비 음수) 의 형태로 현재 위치의 크기를 반환합니다.{{strategy.prev_market_position}}- 전략의 이전 위치를 문자열로 반환합니다: , , 또는 .{{strategy.prev_market_position_size}}- 절대 값 (즉, 비 음수) 의 형태로 이전 위치의 크기를 반환합니다.

  1. TradingView 신호 실행 전략과 함께 메시지를 구축
{
    "Flag":"{{strategy.order.id}}",
    "Exchange":1,
    "Currency":"BTC_USDT",
    "ContractType":"swap",
    "Price":"-1",
    "Action":"{{strategy.order.comment}}",
    "Amount":"{{strategy.order.contracts}}"
}
  1. PINE 스크립트의 실행에 따라 신호를 보내도록 허용하십시오. 당신은 TradingView에 스크립트를 로드 할 때 경고를 설정해야합니다.

트레이딩뷰에 있는 PINE 스크립트가 트랜잭션을 트리거할 때, webhook url 요청이 전송됩니다.

img

FMZ의 실제 로봇이 이 신호를 실행합니다.

img

이 기사 에 나오는 코드 는 참조 를 위한 것 에 불과 하며, 실제 사용 중 에 직접 조정 하고 확장 할 수 있다.


관련

더 많은