Otro esquema de estrategia de ejecución de señales de TradingView

El autor:- ¿ Por qué?, Creado: 2022-12-15 21:23:24, Actualizado: 2023-09-18 20:01:55

img

Otro esquema de estrategia de ejecución de señales de TradingView

Los operadores que a menudo usan TradingView saben que TradingView puede enviar mensajes a otras plataformas. En nuestra plataforma Digest de FMZ, hubo una estrategia de envío de señales de TradingView publicada en la biblioteca, donde el contenido de los mensajes enviados se escribió en la url de solicitud, que era algo inflexible. En este artículo, rediseñamos una estrategia de ejecución de señales de TradingView de una nueva manera.

Escenarios y principios

Algunos principiantes pueden confundirse con el título de este artículo y la descripción anterior, no importa! Comencemos con una descripción clara de los escenarios y principios de la demanda.

  1. Escenarios de la demanda: Entonces, ¿qué tipo de trabajo queremos que haga? En pocas palabras, tenemos muchos indicadores, estrategias, códigos, etc. que podemos elegir usar en TradingView, que se pueden ejecutar directamente en TradingView para dibujar líneas, calcular y mostrar señales comerciales. Además, TradingView tiene datos de precios en tiempo real y suficientes datos de línea K para facilitar el cálculo de varios indicadores. Estos códigos de script en TradingView se llaman lenguaje PINE. Lo único que no es conveniente es que el bot real opere en TradingView. Aunque el lenguaje PINE es compatible con FMZ, también se puede usar para el trading de bots reales. Sin embargo, hay algunos fanáticos de TradingView que todavía quieren colocar órdenes usando las señales de los gráficos en TradingView, por lo que esto puede ser resuelto por FMZ. Así que en este artículo, explicaremos los detalles de la solución.

  2. Principio:

img

En el conjunto del programa se incluyen cuatro temas, que son, en pocas palabras, los siguientes:

img

Así que si quieres usarlo de estas maneras, necesitas estas preparaciones:

  1. El script que se ejecuta en TradingView es responsable de enviar solicitudes de señal a la interfaz API extendida de FMZ. La cuenta TradingView debe ser un miembro PRO al menos.
  2. Para implementar un programa docker en FMZ, necesita ser del tipo que pueda acceder a la interfaz de intercambio (como servidores en Singapur, Japón, Hong Kong, etc.).
  3. Configurar la clave API del exchange para realizar una operación de (posición de una orden) cuando se envíe la señal TradingView en FMZ.
  4. Necesita tener una Estrategia de Ejecución de señales de TradingView, que se discute principalmente en este artículo.

Estrategia de ejecución de la señal TradingView

El diseño de la TradingView Signal Execution Strategy en la versión anterior no es muy flexible. Los mensajes solo se pueden escribir a la url de la solicitud enviada por TradingView. Si queremos que TradingView escriba alguna información variable en el cuerpo al enviar mensajes, no podemos hacer nada en este momento. Por ejemplo, el contenido de dicho mensaje en TradingView:

img

Luego, el TradingView se puede configurar como se muestra en la figura para escribir el mensaje en el cuerpo de la solicitud y enviarlo a la interfaz API extendida de FMZ.

En una serie de interfaces API extendidas de FMZ, necesitamos utilizar elCommandRobotinterfaz, que generalmente se denomina de la siguiente manera:

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

Elaccess_keyysecret_keyEn elqueryde esta solicitud url es el extendidoAPI KEYde la plataforma FMZ, aquí la demostración se establece paraxxxyyyyyEntonces, ¿cómo crear esta llave?https://www.fmz.com/m/account, crear en él, mantenerlo correctamente, no lo divulgue.

img

Volviendo al punto, continuemos hablando del problema de interfaz deCommandRobotSi necesita acceder a laCommandRobotla interfaz, elmethoden la solicitud se establecerá en:CommandRobotLa función de laCommandRobotInterfaz es enviar un mensaje interactivo a un bot real con un ID a través de la plataforma FMZ, por lo que el parámetroargsEl ejemplo de url de solicitud anterior es para enviar el mensajeok12345a un programa de bot real con un ID de 186515.

Anteriormente, este método se utilizaba para solicitar la interfaz CommandRobot de la API ampliada FMZ. Los mensajes solo se pueden escribir en el ejemplo anterior, como elok12345Si el mensaje se encuentra en el cuerpo solicitado, debe utilizar otro método:

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

De esta manera, la solicitud puede enviar el contenido del cuerpo en la solicitud como un mensaje interactivo al bot real con ID130350Si el mensaje en TradingView está configurado para:{"close": {{close}}, "name": "aaa"}, entonces el bot real con la identificación de130350recibirá instrucciones interactivas:{"close": 39773.75, "name": "aaa"}

Para que la Estrategia de ejecución de señales de TradingView entienda correctamente el comando enviado por TradingView al recibir el comando interactivo, deben acordarse previamente los siguientes formatos de mensaje:

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

La estrategia está diseñada como una arquitectura de intercambio múltiple, por lo que se pueden configurar múltiples objetos de intercambio en esta estrategia, es decir, se puede controlar la operación de colocación de pedidos de múltiples cuentas diferentes. Solo el intercambio en la estructura de la señal especifica el intercambio a operar. La configuración 1 es permitir que esta señal opere la cuenta de intercambio correspondiente al primer objeto de intercambio agregado. Si el spot ContractType está configurado en spot, los futuros escribirán contratos específicos, como el intercambio por contratos perpetuos. La lista de precios del mercado puede pasar en -1.

A continuación, puede diseñar el código de estrategia.

//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 y interacciones de la estrategia:

img

La dirección completa de la estrategia de la Estrategia de ejecución de señales de vista de negociación:https://www.fmz.com/strategy/392048

Prueba sencilla

Antes de ejecutar la estrategia, el objeto de intercambio debe configurarse y los dos parámetros AccessKey en FMZ Platform y SecretKey en FMZ Platform deben establecerse en los parámetros de estrategia.

img

Imprimirá la dirección de WebHook, comandos de acción compatibles y formato de mensaje que debe rellenarse en TradingView.

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

Solo copia y pega directamente en la ubicación correspondiente en el TradingView.

Si desea simular una señal enviada por el TradingView, puede hacer clic en el botón TestSignal en la interacción de la estrategia.

img

Esta estrategia envía una solicitud propia (simulando un TradingView que envía una solicitud de señal), llamando a la interfaz API extendida de FMZ para enviar un mensaje a la propia estrategia:

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

La estrategia actual recibirá otro mensaje interactivo y ejecutará y colocará una orden para la transacción.

La prueba del uso de TradingView en la escena real

El uso de la prueba de TradingView requiere que la cuenta de TradingView esté en el nivel Pro.

Tomemos un sencillo script PINE (encontrado al azar y modificado en TradingView) como ejemplo

//@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. El script PINE puede adjuntar alguna información cuando el script envía instrucciones de orden

Los siguientes son marcadores de posición. Por ejemplo, si escribo{{strategy.order.contracts}}en el cuadro mensaje de la alerta, se enviará un mensaje cuando se active la orden (de acuerdo con los ajustes de la alerta, push de correo, solicitud de URL de webhook, ventana emergente, etc.), y el mensaje contendrá el número de órdenes ejecutadas esta vez.

{{strategy.position_size}}- Devuelve el valor de la misma palabra clave en Pine, es decir, el tamaño de la posición actual.{{strategy.order.action}}- Devuelve la cadena buy o sell para la orden ejecutada.{{strategy.order.contracts}}- Indicar el número de contratos para los que se han ejecutado órdenes.{{strategy.order.price}}- Devuelve el precio de la orden ejecutada.{{strategy.order.id}}- Devuelve el ID de la orden ejecutada (la cadena utilizada como primer parámetro en una de las llamadas de función que generan la orden: strategy.entry,strategy.exito estrategia. orden).{{strategy.order.comment}}- Devuelve el comentario de la orden ejecutada (la cadena utilizada en el parámetro comentario en una de las llamadas de función que generan la orden: strategy.entry,strategy.exitSi no se especifica ninguna observación, el valor destrategy.order.idse utilizará.{{strategy.order.alert_message}}- Devuelve el valor del parámetro alert_message que puede utilizarse en el código Pine de la estrategia cuando se llama a una de las funciones utilizadas para realizar un pedido: strategy.entry,strategy.exit, o strategy.order. Esto sólo es compatible en Pine v4.{{strategy.market_position}}- Devuelve la posición actual de la estrategia como una cadena: long, flat, o short.{{strategy.market_position_size}}- Devuelve el tamaño de la posición actual en forma de un valor absoluto (es decir, un número no negativo).{{strategy.prev_market_position}}- Devuelve la posición anterior de la estrategia como una cadena: long, flat, o short.{{strategy.prev_market_position_size}}- Devuelve el tamaño de la posición anterior en forma de un valor absoluto (es decir, un número no negativo).

  1. Construir mensajes en combinación con la Estrategia de ejecución de señales de TradingView
{
    "Flag":"{{strategy.order.id}}",
    "Exchange":1,
    "Currency":"BTC_USDT",
    "ContractType":"swap",
    "Price":"-1",
    "Action":"{{strategy.order.comment}}",
    "Amount":"{{strategy.order.contracts}}"
}
  1. Deja que TradingView envíe una señal de acuerdo con la ejecución del script PINE.

Cuando el script PINE en el TradingView activa una transacción, se enviará una solicitud de URL de webhook.

img

El robot real FMZ ejecutará esta señal.

img

El código de este artículo es solo para referencia, y puede ajustarse y ampliarse usted mismo en el uso real.


Relacionados

Más.