Outro esquema de estratégia de execução de sinais TradingView

Autora:Lydia., Criado: 2022-12-15 21:23:24, Atualizado: 2023-09-18 20:01:55

img

Outro esquema de estratégia de execução de sinais TradingView

Os traders que costumam usar o TradingView sabem que o TradingView pode empurrar mensagens para outras plataformas. Em nossa plataforma Digest da FMZ, havia uma estratégia de empurrão de sinal do TradingView publicada na biblioteca, onde o conteúdo das mensagens empurradas era escrito no url de solicitação, o que era um pouco inflexível.

Cenários e Princípios

Alguns novatos podem ficar confusos com o título deste artigo e a descrição acima, não importa! Vamos começar com uma descrição clara dos cenários e princípios de demanda.

  1. Cenários de procura: Então, que tipo de trabalho queremos que ele faça? Simplificando, temos muitos indicadores, estratégias, códigos, etc. que podemos escolher usar no TradingView, que podem ser executados diretamente no TradingView para desenhar linhas, calcular e exibir sinais de negociação. Além disso, o TradingView tem dados de preços em tempo real e dados de linha K suficientes para facilitar o cálculo de vários indicadores. Estes códigos de script no TradingView são chamados de linguagem PINE. A única coisa não conveniente é que o bot real negocia no TradingView. Embora a linguagem PINE seja suportada no FMZ, também pode ser usada para negociação de bots reais. No entanto, há alguns fãs do TradingView que ainda querem colocar ordens usando os sinais dos gráficos no TradingView, então isso pode ser resolvido pelo FMZ. Então, neste artigo, explicaremos os detalhes da solução.

  2. Princípio:

img

O conjunto do programa abrange quatro domínios, que são, em suma, os seguintes:

img

Então, se quiser usá-lo desta forma, precisa destes preparativos:

  1. O script executado no TradingView é responsável por enviar solicitações de sinal para a interface API estendida do FMZ. A conta TradingView deve ser um membro PRO pelo menos.
  2. Para implantar um programa docker no FMZ, ele precisa ser do tipo que possa acessar a interface de troca (como servidores em Cingapura, Japão, Hong Kong, etc.).
  3. Configurar a API KEY da bolsa para uma operação (colocação de uma ordem) quando o sinal TradingView for enviado na FMZ.
  4. Você precisa ter uma TradingView Signal Execution Strategy, que é discutida principalmente neste artigo.

TradingView Estratégia de Execução de Sinais

O design da TradingView Signal Execution Strategy na versão anterior não é muito flexível. As mensagens só podem ser escritas no url da solicitação enviada pelo TradingView. Se quisermos que o TradingView escreva alguma informação variável no corpo ao empurrar mensagens, não podemos fazer nada neste momento. Por exemplo, o conteúdo da mensagem no TradingView:

img

Em seguida, o TradingView pode ser definido como mostrado na figura para escrever a mensagem no corpo do pedido e enviá-lo para a interface API estendida do FMZ.

Em uma série de interfaces API estendidas de FMZ, precisamos usar oCommandRobotinterface, que é geralmente denominada do seguinte modo:

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

Oaccess_keyesecret_keyemquerydo url deste pedido é a extensãoAPI KEYda plataforma FMZ, aqui a demonstração definida paraxxxeyyyyEntão, como criar esta chave? Nesta página:https://www.fmz.com/m/account, criar sobre ele, mantê-lo corretamente, não divulgá-lo.

img

Voltando ao ponto, vamos continuar a falar sobre o problema de interface deCommandRobotSe precisar de aceder aoCommandRobotinterfaces, omethodno pedido será definido como:CommandRobotA função doCommandRobotinterface é enviar uma mensagem interativa para um bot real com uma ID através da plataforma FMZ, por isso o parâmetroargsO exemplo de url de solicitação acima é para enviar a mensagemok12345para um programa de robô real com uma identificação de 186515.

Anteriormente, este método era usado para solicitar a interface CommandRobot da API estendida FMZ. Mensagens só podem ser escritas no exemplo acima, como ook12345Se a mensagem estiver no corpo solicitado, é necessário utilizar outro método:

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

Desta forma, a solicitação pode enviar o conteúdo do corpo na solicitação como uma mensagem interativa para o bot real com ID130350Se a mensagem no TradingView estiver definida como:{"close": {{close}}, "name": "aaa"}, então o robô real com a identificação de130350receberá instruções interativas:{"close": 39773.75, "name": "aaa"}

A fim de que a Estratégia de Execução de Sinais do TradingView compreenda correctamente o comando enviado pelo TradingView ao receber o comando interativo, os seguintes formatos de mensagem devem ser acordados antecipadamente:

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

A estratégia é projetada como uma arquitetura multi-exchange, de modo que vários objetos de troca podem ser configurados nesta estratégia, ou seja, a operação de colocação de ordens de várias contas diferentes pode ser controlada. Somente a Exchange na estrutura do sinal especifica a troca a ser operada. A configuração 1 é permitir que este sinal opere a conta de troca correspondente ao primeiro objeto de troca adicionado. Se o spot ContractType for definido em spot, os futuros escreverão contratos específicos, como swap para contratos perpétuos. A lista de preços do mercado pode passar em -1. As configurações de ação são diferentes para futuros, spot, posições de abertura e fechamento, e não pode ser definida incorretamente.

Em seguida, pode desenhar o código da estratégia.

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

Parâmetros e interações da estratégia:

img

O endereço da estratégia completa da Estratégia de Execução de Sinais de Visualização de Negociação:https://www.fmz.com/strategy/392048

Teste simples

Antes de executar a estratégia, o objeto de troca deve ser configurado e os dois parâmetros AccessKey on FMZ Platform e SecretKey on FMZ Platform devem ser definidos nos parâmetros da estratégia.

img

Ele irá imprimir o endereço WebHook, comandos de ação suportados e formato de mensagem que precisam ser preenchidos no TradingView.

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

Basta copiar e colar diretamente no local correspondente no TradingView.

Se você quiser simular um sinal enviado pelo TradingView, você pode clicar no botão TestSignal na interação de estratégia.

img

Esta estratégia envia uma solicitação própria (simulando um TradingView enviando uma solicitação de sinal), chamando a interface API estendida do FMZ para enviar uma mensagem para a própria estratégia:

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

A estratégia atual receberá outra mensagem interativa e executará e colocará uma ordem para a transação.

O teste do uso do TradingView na cena real

Usar o teste TradingView requer que a conta TradingView esteja no nível Pro. Antes do teste, você precisa ter algum conhecimento prévio.

Tome um simples script PINE (atropelado e modificado aleatoriamente no TradingView) como exemplo

//@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. O script PINE pode anexar algumas informações quando o script envia instruções de ordem

Os seguintes são marcadores de espaço.{{strategy.order.contracts}}na caixa mensagem da alerta, uma mensagem será enviada quando a ordem for acionada (de acordo com as configurações da alerta, email push, webhook url request, pop-up, etc.), e a mensagem conterá o número de ordens executadas desta vez.

{{strategy.position_size}}- Retorna o valor da mesma palavra-chave em Pine, ou seja, o tamanho da posição atual.{{strategy.order.action}}- Retorna a cadeia buy ou sell para a ordem executada.{{strategy.order.contracts}}- Retorna o número de contratos para os quais as ordens foram executadas.{{strategy.order.price}}- Devolva o preço da ordem executada.{{strategy.order.id}}- Retorna o ID da ordem executada (a cadeia usada como o primeiro parâmetro em uma das chamadas de função que geram a ordem: strategy.entry,strategy.exitou estratégia. ordem).{{strategy.order.comment}}- Retorna o comentário da ordem executada (a cadeia usada no parâmetro comentário em uma das chamadas de função que geram a ordem: strategy.entry,strategy.exitSe não for especificado nenhum comentário, o valor destrategy.order.idserão utilizados.{{strategy.order.alert_message}}- Retorna o valor do parâmetro alert_message que pode ser utilizado no código Pine da estratégia ao chamar uma das funções utilizadas para fazer uma encomenda: strategy.entry,strategy.exit, ou strategy.order. Isto só é suportado em Pine v4.{{strategy.market_position}}- Retorna a posição atual da estratégia como uma cadeia: long, flat, ou short.{{strategy.market_position_size}}- Retorna o tamanho da posição corrente sob a forma de um valor absoluto (ou seja, um número não negativo).{{strategy.prev_market_position}}- Retorna a posição anterior da estratégia como uma cadeia: long, flat, ou short.{{strategy.prev_market_position_size}}- Retorna o tamanho da posição anterior sob a forma de um valor absoluto (ou seja, um número não negativo).

  1. Construir mensagens em combinação com a TradingView Signal Execution Strategy
{
    "Flag":"{{strategy.order.id}}",
    "Exchange":1,
    "Currency":"BTC_USDT",
    "ContractType":"swap",
    "Price":"-1",
    "Action":"{{strategy.order.comment}}",
    "Amount":"{{strategy.order.contracts}}"
}
  1. Deixe o TradingView enviar um sinal de acordo com a execução do script PINE.

Quando o script PINE no TradingView desencadeia uma transação, uma solicitação de url do webhook será enviada.

img

O verdadeiro robô FMZ vai executar este sinal.

img

O código neste artigo é apenas para referência, e pode ser ajustado e expandido por si mesmo no uso real.


Relacionados

Mais.