Los operadores que a menudo usan TradingView saben que TradingView puede enviar mensajes a otras plataformas. En nuestra plataforma
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.
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.
Principio:
En el conjunto del programa se incluyen cuatro temas, que son, en pocas palabras, los siguientes:
Así que si quieres usarlo de estas maneras, necesitas estas preparaciones:
El diseño de la
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 elCommandRobot
interfaz, 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_key
ysecret_key
En elquery
de esta solicitud url es el extendidoAPI KEY
de la plataforma FMZ, aquí la demostración se establece paraxxx
yyyyy
Entonces, ¿cómo crear esta llave?https://www.fmz.com/m/account
, crear en él, mantenerlo correctamente, no lo divulgue.
Volviendo al punto, continuemos hablando del problema de interfaz deCommandRobot
Si necesita acceder a laCommandRobot
la interfaz, elmethod
en la solicitud se establecerá en:CommandRobot
La función de laCommandRobot
Interfaz es enviar un mensaje interactivo a un bot real con un ID a través de la plataforma FMZ, por lo que el parámetroargs
El ejemplo de url de solicitud anterior es para enviar el mensajeok12345
a 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 elok12345
Si 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 ID130350
Si el mensaje en TradingView está configurado para:{"close": {{close}}, "name": "aaa"}
, entonces el bot real con la identificación de130350
recibirá instrucciones interactivas:{"close": 39773.75, "name": "aaa"}
Para que la
{
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:
La dirección completa de la estrategia de la
Antes de ejecutar la estrategia, el objeto de intercambio debe configurarse y los dos parámetros
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.
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
{"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.
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)
Los siguientes son marcadores de posición. Por ejemplo, si escribo{{strategy.order.contracts}}
en el cuadro
{{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 {{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: {{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: {{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).
{
"Flag":"{{strategy.order.id}}",
"Exchange":1,
"Currency":"BTC_USDT",
"ContractType":"swap",
"Price":"-1",
"Action":"{{strategy.order.comment}}",
"Amount":"{{strategy.order.contracts}}"
}
Cuando el script PINE en el TradingView activa una transacción, se enviará una solicitud de URL de webhook.
El robot real FMZ ejecutará esta señal.
El código de este artículo es solo para referencia, y puede ajustarse y ampliarse usted mismo en el uso real.