Outra estratégia de execução de sinais do TradingView

Autora:Sonhos pequenos, Criado: 2022-11-30 10:52:07, Atualizado: 2023-09-18 20:01:09

[TOC]

img

Outra estratégia de execução de sinais do TradingView

Os traders que costumam usar o TradingView sabem que o TradingView pode empurrar mensagens para outras plataformas. Anteriormente, uma estratégia de empurrão de sinais para o TradingView também foi divulgada no repositório.

Cenas e princípios

Talvez alguns alunos novos vejam o título e a descrição acima como um pouco estranho, mas não importa! Vamos explicar o cenário e o princípio das necessidades primeiro. Deixe-nos entender o que estou falando.

O primeiro cenário de demanda: O que é que isso vai fazer? Simplificando, temos um monte de indicadores, estratégias, códigos, etc. que podemos escolher para usar no TradingView, que podem ser executados diretamente no TradingView, podem desenhar linhas, calcular, mostrar sinais de negociação, etc. E o TradingView tem dados de preços em tempo real, muitos dados de linhas K para facilitar o cálculo de vários indicadores. O código do script no TradingView é chamado de linguagem PINE, a única coisa que não é muito conveniente é negociar em tempo real no TradingView.

2o Princípio:

img

O projeto envolve quatro temas, que são:

Número Subjetivo Descrição
1 TradingView (Trading View no gráfico) O script PINE é executado no TradingView para enviar sinais e acessar a interface API de extensão do FMZ
2 Plataforma FMZ (WEB em imagem) Gerenciar o disco, enviar instruções de interação na página do disco, ou enviar instruções de interação para o servidor através de uma interface API estendida para que a plataforma FMZ.
3 Programa de disco real no software do administrador (foto FMZ strategy robot) A estratégia de execução de sinais TradingView é executada
4 Bolsa (exchange no gráfico) As trocas configuradas em disco, os procedimentos em disco no administrador enviam diretamente o pedido para as trocas

A partir daí, o jogo começou a se tornar um jogo de azar, e o que você precisa fazer para jogar é fazer estas coisas: O script, que é executado no TradingView, é responsável por enviar solicitações de sinal para a interface API de extensão do FMZ, e requer que a conta do TradingView seja pelo menos um membro do PRO. 2, Para implementar um programa de administrador no FMZ, é necessário acessar a interface da bolsa (servidores em Singapura, Japão, Hong Kong, etc.). 3. Configure no FMZ a API KEY da bolsa para operar quando o sinal TradingView for enviado. 4, você precisa ter uma "estratégia de execução de sinais TradingView", que é a principal questão deste artigo.

Política de execução do sinal TradingView

A última versão do "TradingView Signal Execution Policy" foi projetada para ser menos flexível, e as mensagens só podiam ser escritas no url do pedido enviado pelo TradingView.

img

Então, no TradingView, você pode configurar a mensagem para ser escrita no corpo da solicitação e enviada para a interface API de extensão do FMZ.

O FMZ é uma série de extensões de interfaces de APIs que nós vamos usar paraCommandRobotA interface é geralmente chamada assim:

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

O URL solicitadoqueryO que é?access_keyesecret_keyA plataforma é uma extensão da FMZ.API KEYAqui, a demonstração é definida comoxxxeyyyyComo é que o KEY foi criado?https://www.fmz.com/m/accountO site do Facebook do Facebook, que é o site oficial do Facebook, afirma que os usuários podem criar um e guardá-lo devidamente e nunca divulgá-lo.

img

Mas não é a primeira vez.CommandRobotO problema da interface.CommandRobotInterface, no pedidomethodO que é que ele tem a dizer?CommandRobotCommandRobotA função desta interface é enviar uma mensagem de interação através da plataforma FMZ para um disco rígido de um ID, por isso o parâmetroargsO exemplo acima do url de solicitação é o que o ID deve ser.186515O programa do disco real, enviando mensagensok12345

Anteriormente, era esta a forma de solicitar a interface CommandRobot da FMZ Extension API, e a mensagem só podia ser escrita para morrer, como no exemplo acima.ok12345Se a mensagem estiver no corpo solicitado, é necessário usar outro método:

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

O conteúdo do corpo da solicitação pode ser enviado como uma mensagem de interação para o ID através da plataforma FMZ.130350A mensagem no TradingView é definida como:{"close": {{close}}, "name": "aaa"}Então o ID é:130350O disco real recebe instruções de interação:{"close": 39773.75, "name": "aaa"}

Para que a política de execução de sinais do TradingView possa entender corretamente a instrução enviada pelo TradingView quando recebe uma instrução de interação, convém concordar com o formato da mensagem:

{
    Flag: "45M103Buy",     // 标识,可随意指定
    Exchange: 1,           // 指定交易所交易对
    Currency: "BTC_USDT",  // 交易对
    ContractType: "swap",  // 合约类型,swap,quarter,next_quarter,现货填写spot
    Price: "{{close}}",    // 开仓或者平仓价格,-1为市价
    Action: "buy",         // 交易类型[ buy:现货买入 , sell:现货卖出 , long:期货做多 , short:期货做空 , closesell:期货买入平空 , closebuy:期货卖出平多]
    Amount: "0",           // 交易量
}

A política foi projetada para ser uma arquitetura multi-exchanges, de modo que pode configurar vários objetos de negociação nesta política, ou seja, pode controlar a operação de transferência de várias contas diferentes. Basta usar a estrutura de sinais do Exchange para especificar a troca a ser operada, a configuração 1 é para que o sinal seja operado pela primeira conta de negociação que o objeto de negociação adicionado corresponda. Se a configuração de operação for o tipo de contrato em tempo real, os futuros são escritos em contratos específicos, como contratos permanentes de swaps.

A seguir, você pode criar o código da estratégia, o código completo da estratégia:

//信号结构
var Template = {
    Flag: "45M103Buy",     // 标识,可随意指定
    Exchange: 1,           // 指定交易所交易对
    Currency: "BTC_USDT",  // 交易对
    ContractType: "swap",  // 合约类型,swap,quarter,next_quarter,现货填写spot
    Price: "{{close}}",    // 开仓或者平仓价格,-1为市价
    Action: "buy",         // 交易类型[ buy:现货买入 , sell:现货卖出 , long:期货做多 , short:期货做空 , closesell:期货买入平空 , closebuy:期货卖出平多]
    Amount: "0",           // 交易量
}

var BaseUrl = "https://www.fmz.com/api/v1"   // FMZ扩展API接口地址 
var RobotId = _G()                           // 当前实盘ID
var Success = "#5cb85c"    // 成功颜色
var Danger = "#ff0000"     // 危险颜色
var Warning = "#f0ad4e"    // 警告颜色
var buffSignal = []

// 校验信号消息格式
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("交易所最小编号为1,并且为整数", Danger)
        return
    }
    if (Signal.Amount <= 0 || typeof(Signal.Amount) != "number") {
        Log("交易量不能小于0,并且为数值类型", typeof(Signal.Amount), Danger)
        return
    }
    if (typeof(Signal.Price) != "number") {
        Log("价格必须是数值", Danger)
        return
    }
    if (Signal.ContractType == "spot" && Signal.Action != "buy" && Signal.Action != "sell") {
        Log("指令为操作现货,Action错误,Action:", Signal.Action, Danger)
        return 
    }
    if (Signal.ContractType != "spot" && Signal.Action != "long" && Signal.Action != "short" && Signal.Action != "closesell" && Signal.Action != "closebuy") {
        Log("指令为操作期货,Action错误,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("模拟TradingView的webhook请求,发送用于测试的POST请求:", url, "body:", cmd, "应答:", 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("创建任务:", 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) {
            // 非现货,则设置合约
            e.SetContractType(task.ContractType)
        } else if (task.ContractType == "spot" && name.indexOf("Futures_") == -1) {
            isFutures = false 
        } else {
            task.error = "指令中的ContractType与配置的交易所对象类型不匹配"
            return 
        }
        
        var depth = e.GetDepth()
        if (!depth || !depth.Bids || !depth.Asks) {
            task.error = "订单薄数据异常"
            return 
        }
        
        if (depth.Bids.length == 0 && depth.Asks.length == 0) {
            task.error = "盘口无订单"
            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 = "获取精度失败"
            return 
        }
        
        e.SetPrecision(self.pricePrecision, self.amountPrecision)
        
        // buy:现货买入 , sell:现货卖出 , long:期货做多 , short:期货做空 , closesell:期货买入平空 , closebuy:期货卖出平多
        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 = "交易方向错误:" + 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 = "交易方向错误:" + task.Action
                return 
            }
        }
        var id = tradeFunc(task.Price, task.Amount)
        if (!id) {
            task.error = "下单失败"
        }
        
        task.finished = true
    }
    
    return self
}

var manager = createManager()
function HandleCommand(signal) {
    // 检测是否收到交互指令
    if (signal) {
        Log("收到交互指令:", signal)     // 收到交互指令,打印交互指令
    } else {
        return                            // 没有收到时直接返回,不做处理
    }
    
    // 检测交互指令是否是测试指令,测试指令可以由当前策略交互控件发出来进行测试
    if (signal.indexOf("TestSignal") != -1) {
        signal = signal.replace("TestSignal:", "")
        // 调用FMZ扩展API接口,模拟Trading View的webhook,交互按钮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("执行调试代码:", js)
        eval(js)
    } else {
        // 处理信号指令
        objSignal = JSON.parse(signal)
        if (DiffObject(Template, objSignal)) {
            Log("接收到交易信号指令:", objSignal)
            buffSignal.push(objSignal)
            
            // 检查交易量、交易所编号
            if (!CheckSignal(objSignal)) {
                return
            }
            
            // 创建任务
            manager.newTask(objSignal)
        } else {
            Log("指令无法识别", signal)
        }
    }
}

function main() {
    Log("WebHook地址:", "https://www.fmz.com/api/v1?access_key=" + FMZ_AccessKey + "&secret_key=" + FMZ_SecretKey + "&method=CommandRobot&args=[" + RobotId + ',+""]', Danger)
    Log("交易类型[ buy:现货买入 , sell:现货卖出 , long:期货做多 , short:期货做空 , closesell:期货买入平空 , closebuy:期货卖出平多]", Danger)
    Log("指令模板:", JSON.stringify(Template), Danger)
    
    while (true) {
        try {
            // 处理交互
            HandleCommand(GetCommand())
            
            // 处理任务
            manager.process()
            
            if (buffSignal.length > maxBuffSignalRowDisplay) {
                buffSignal.shift()
            }
            var buffSignalTbl = {
                "type" : "table",
                "title" : "信号记录",
                "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 estratégicos e interação:

img

A política completa do TradingView para a execução do sinal é:https://www.fmz.com/strategy/392048

Testes simples

Antes de executar a política, configure o objeto de troca e configure os dois parâmetros "AccessKey para a plataforma FMZ" e "SecretKey para a plataforma FMZ" nos parâmetros da política.

img

Imprimirá o seguinte: endereço do WebHook que precisa ser preenchido no TradingView, instruções de ação suportadas, formato de mensagem. O endereço do WebHook é importante:

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

A partir daí, você pode copiar e colar diretamente o que está escrito no TradingView.

Para simular o envio de sinais no TradingView, clique no botão TestSignal na interação da política:

img

Esta política envia uma solicitação (simulação de solicitação de sinal de envio do TradingView) que chama a interface API de extensão do FMZ e envia uma mensagem para a própria política:

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

A política atual recebe outra mensagem de interação e executa:

img

O que é que ele está a fazer?

Testes com o TradingView em cenários reais

Para usar o teste do TradingView, é necessária uma conta do TradingView Pro. Há alguns conhecimentos prévios que precisam ser explicados de forma simples antes do teste.

Por exemplo, um simples script do PINE (um pouco modificado que você encontrou no 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, O script do PINE pode conter algumas informações quando o script emite instruções para seguir

Aqui estão os símbolos que eu escrevi na caixa "Mensagem" do alerta.{{strategy.order.contracts}}A mensagem é enviada no momento em que o pedido é desencadeado (dependendo da configuração do alerta, de email push, de solicitação de url do webhook, de janela pop-up, etc.) e contém o número de pedidos executados.

{{strategy.position_size}}- Retorna o valor da mesma palavra-chave no Pine, ou seja, o tamanho da posição atual.{{strategy.order.action}}- Retornar a string para a ordem executada com a barra buy ou barra sell.{{strategy.order.contracts}}- Retornar o número de contratos executados.{{strategy.order.price}}- Retorno do preço da execução da encomenda.{{strategy.order.id}}- Retorna o ID da ordem executada (string usada como o primeiro parâmetro em uma das chamadas de função que gerou a ordem: estrategy.entry, strategy.exit ou strategy.order){{strategy.order.comment}}- Retorna a notação de uma ordem executada (string usada no parâmetro de comentário em uma das chamadas de função que gerou a ordem:strategy.entry,strategy.exit、 ou estrategy.order) ─ se não for especificado o comentário, o valor da estrategy.order.id será usado ─{{strategy.order.alert_message}}- Retorna o valor do parâmetro alert_message, que pode ser usado no código Pine da política quando uma das funções para a ordem seguinte é chamada:strategy.entry,strategy.exitA função é suportada apenas no Pine v4.{{strategy.market_position}}- Estratégias de retorno de posições atuais em formato de cadeia: "long", "flat" ou "short";{{strategy.market_position_size}}- Retorna o tamanho da posição atual em forma de valor absoluto (ou seja, não negativo).{{strategy.prev_market_position}}- Retorno da estratégia para o último posicionamento na forma de uma cadeia de caracteres: long, flat, ou short.{{strategy.prev_market_position_size}}- Retorna o tamanho da posição anterior em forma de valor absoluto (ou seja, não negativo).

2o. Construir mensagens com a "TradingView Signal Execution Policy"

{
    "Flag":"{{strategy.order.id}}",
    "Exchange":1,
    "Currency":"BTC_USDT",
    "ContractType":"swap",
    "Price":"-1",
    "Action":"{{strategy.order.comment}}",
    "Amount":"{{strategy.order.contracts}}"
}

3, para que o TradingView faça um sinal quando executado com base neste script PINE, é necessário definir um alarme quando este script for carregado no TradingView

img

Quando o script PINE no TradingView desencadeia uma ação de negociação, é enviada uma solicitação de url do webhook.

img

img

O disco real do FMZ executa o sinal.

img

img

Endereço do vídeo

Vídeo da guaxinim:https://www.ixigua.com/7172134169580372513?utm_source=xiguastudioEstação B:https://www.bilibili.com/video/BV1BY411d7c6/O que é conhecido:https://www.zhihu.com/zvideo/1581722694294487040

O código do artigo é apenas para referência e pode ser modificado e expandido para uso real.


Relacionados

Mais.

wbe3- pequenos fritosMonk, como é que se executa a operação do ambiente do disco analógico?

GuohwaPor favor, uma pergunta: as mensagens de alerta do tradingview podem conter mensagens de pedidos anteriores? Eu quero obter a última ordem é um lucro ou um prejuízo, se a última ordem é um prejuízo, o robô não executará a operação de substituição até que a última ordem obtida é um lucro. Por favor, podem fazer isso?

13811047519/upload/asset/2a5a9fa2b97561c42c027.jpg Por favor, meu Deus, o que significa esse erro e como removê-lo?

O melhor de tudoSonho grande, eu adicionei 6 a 7 contas para fazer esse sinal de negociação, mas temporariamente é muito grande, um sinal de conta de troca terminará antes do sinal da próxima conta de negociação, é uma execução serial, há uma maneira de fazer com que o sinal de negociação seja executado em paralelo?

wbe3- pequenos fritosA receção de um sinal de política parece não ter ganhos de impressão e a publicação parece não ser gerada, por isso, por favor, adicione um modelo de formulário de informações de conta relacionado.

Sonhos pequenosO que é uma visão geral de política pública, que é adicionada automaticamente à página.

wbe3- pequenos fritosObrigado, amigo, já testei bem, mas não há estratégia de avaliação após o negócio, é necessário adicionar automaticamente

Sonhos pequenosInterface OKX, que pode ser trocado para o ambiente de teste de disco analógico de OKX, usando o exchange.IO ("simulate", true), que pode ser trocado para o ambiente de disco analógico.

GuohwaObrigado pela resposta, mas tenho duas perguntas: 1, o que eu não entendo é que o fmz pode escrever o próprio script pine, por que este artigo também deve enviar um alerta para o fmz através do TradingView e processá-lo e negociar? 2, eu agora encontrei uma estratégia que é muito boa em si, mas sem o código fonte tem o direito de uso, eu quero evitar erros através do método que eu disse acima, você disse em aumentar o {{strategy.order.price}} na mensagem de push, eu também adicionei, mas este push parece ser o preço no momento do pedido, depois no fmz como usar esse preço para julgar se é um lucro ou um prejuízo, eu não entendo um pouco.

Sonhos pequenosDeveria ser possível que você colocasse o conteúdo {{strategy.order.price}} no momento da mensagem, e então a estratégia no FMZ processasse essa informação e decidisse se encomendaria ou não, com base na comparação de preços atual.

Sonhos pequenosO teste está normal agora?

O melhor de tudoMuito bem, obrigado, patrão.

Sonhos pequenosO FMZ adicionou funções de concomitância e deve ser possível transformá-lo em concomitante, embora o código da estratégia possa sofrer alterações significativas. Recentemente, se tiver tempo, atualize um exemplo de concomitância.