Les traders qui utilisent souvent TradingView savent que TradingView peut pousser des messages vers d'autres plateformes. Dans notre
Certains débutants peuvent être confus par le titre de cet article et la description ci-dessus, peu importe! Commençons par une description claire des scénarios et des principes de la demande.
Scénarios de la demande: Donc, quel type de travail nous voulons qu'il fasse? Pour le dire simplement, nous avons beaucoup d'indicateurs, de stratégies, de codes, etc. que nous pouvons choisir d'utiliser sur TradingView, qui peuvent être exécutés directement sur TradingView pour dessiner des lignes, calculer et afficher des signaux de trading. En outre, TradingView dispose de données de prix en temps réel et de données de ligne K suffisantes pour faciliter le calcul de divers indicateurs. Ces codes de script sur TradingView sont appelés langage PINE. La seule chose qui n'est pas pratique est que le vrai bot négocie sur TradingView. Bien que le langage PINE soit pris en charge sur FMZ, il peut également être utilisé pour le vrai trading bot. Cependant, il y a quelques fans de TradingView qui veulent toujours passer des ordres en utilisant les signaux des graphiques sur TradingView, ce qui peut être résolu par FMZ. Dans cet article, nous allons donc expliquer les détails de la solution.
Principe:
L'ensemble du programme comporte quatre sujets, qui sont, en résumé:
Donc si vous voulez l'utiliser de cette façon, vous avez besoin de ces préparations:
La conception de la stratégie d'exécution de signal de
Ensuite, le TradingView peut être configuré comme indiqué sur la figure pour écrire le message dans le corps de la demande et l'envoyer à l'interface API étendue de FMZ.
Dans une série d'interfaces API étendues de FMZ, nous devons utiliser leCommandRobot
interface, qui est généralement appelée comme suit:
https://www.fmz.com/api/v1?access_key=xxx&secret_key=yyyy&method=CommandRobot&args=[186515,"ok12345"]
Leaccess_key
etsecret_key
dans lequery
de cette demande url est l'extensionAPI KEY
de la plate-forme FMZ, ici la démo mise àxxx
etyyyy
Alors comment créer cette CLAVE?https://www.fmz.com/m/account
, créez dessus, gardez-le correctement, ne le divulguez pas.
Retour au sujet, continuons à parler du problème de l'interface deCommandRobot
Si vous avez besoin d' accéder auCommandRobot
l'interface, lemethod
dans la demande sera réglée sur:CommandRobot
. La fonction duCommandRobot
l'interface est d'envoyer un message interactif à un vrai bot avec un ID à travers la plate-forme FMZ, donc le paramètreargs
L'exemple de l'url de demande ci-dessus est pour envoyer le messageok12345
à un vrai programme bot avec un ID de 186515.
Auparavant, cette méthode était utilisée pour demander l'interface CommandRobot de l'API étendue FMZ. Les messages ne peuvent être écrits que dans l'exemple ci-dessus, comme leok12345
Si le message est dans l'organisme demandé, vous devez utiliser une autre méthode:
https://www.fmz.com/api/v1?access_key=xxx&secret_key=yyyy&method=CommandRobot&args=[130350,+""]
De cette façon, la demande peut envoyer le contenu du corps dans la demande comme un message interactif au vrai bot avec ID130350
Si le message sur TradingView est réglé sur:{"close": {{close}}, "name": "aaa"}
, alors le vrai robot avec l' ID de130350
recevra des instructions interactives:{"close": 39773.75, "name": "aaa"}
Afin que la stratégie d'exécution du signal de
{
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 stratégie est conçue comme une architecture multi-échange, de sorte que plusieurs objets d'échange peuvent être configurés sur cette stratégie, c'est-à-dire que l'opération de placement d'ordres de plusieurs comptes différents peut être contrôlée. Seul l'échange dans la structure du signal spécifie l'échange à exploiter. Le réglage 1 est de permettre à ce signal d'opérer le compte d'échange correspondant au premier objet d'échange ajouté. Si le spot ContractType est réglé sur spot, les contrats à terme écriront des contrats spécifiques, tels que le swap pour les contrats perpétuels. La liste de prix du marché peut passer en -1.
Ensuite, vous pouvez concevoir le code de stratégie.
//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)
}
}
}
Paramètres et interactions de la stratégie:
L'adresse complète de la stratégie de la stratégie d'exécution des signaux de la vue de négociation:https://www.fmz.com/strategy/392048
Avant d'exécuter la stratégie, l'objet d'échange doit être configuré et les deux paramètres
Il imprimera l'adresse WebHook, les commandes d'action prises en charge et le format du message qui doivent être remplis sur le TradingView.
https://www.fmz.com/api/v1?access_key=22903bab96b26584dc5a22522984df42&secret_key=73f8ba01014023117cbd30cb9d849bfc&method=CommandRobot&args=[505628,+""]
Il suffit de copier et coller directement à l'emplacement correspondant sur le TradingView.
Si vous voulez simuler un signal envoyé par TradingView, vous pouvez cliquer sur le bouton TestSignal sur l'interaction de stratégie.
Cette stratégie envoie sa propre demande (simulant une demande de signal envoyée par TradingView), appelant l'interface API étendue de FMZ
{"Flag":"45M103Buy","Exchange":1,"Currency":"BTC_USDT","ContractType":"swap","Price":"16000","Action":"buy","Amount":"1"}
La stratégie actuelle recevra un autre message interactif et exécutera et placera un ordre de transaction.
L'utilisation du test TradingView nécessite que le compte TradingView soit au niveau Pro. Avant le test, vous devez connaître certaines connaissances préalables.
Prenez un script PINE simple (trouvé et modifié au hasard sur TradingView) comme exemple
//@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)
Voici les marqueurs de place.{{strategy.order.contracts}}
dans la zone
{{strategy.position_size}}
- Renvoie la valeur du même mot clé dans Pine, c'est-à-dire la taille de la position actuelle.{{strategy.order.action}}
- Retournez la chaîne {{strategy.order.contracts}}
- Indiquer le nombre de contrats pour lesquels des ordres ont été exécutés.{{strategy.order.price}}
- Retournez le prix de l'ordre exécuté.{{strategy.order.id}}
- Renvoie l'ID de l'ordre exécuté (la chaîne utilisée comme premier paramètre dans l'une des appels de fonction qui génèrent l'ordre: strategy.entry,strategy.exitou stratégie.ordre).{{strategy.order.comment}}
- Renvoie le commentaire de l'ordre exécuté (la chaîne utilisée dans le paramètre commentaire dans l'une des appels de fonction qui génèrent l'ordre: strategy.entry,strategy.exitSi aucun commentaire n'est spécifié, la valeur destrategy.order.idseront utilisés.{{strategy.order.alert_message}}
- Renvoie la valeur du paramètre alert_message qui peut être utilisé dans le code Pine de la stratégie lors de l'appel d'une des fonctions utilisées pour passer une commande: strategy.entry,strategy.exit, ou strategy.order. Ceci n'est pris en charge que dans Pine v4.{{strategy.market_position}}
- Renvoie la position actuelle de la stratégie sous forme de chaîne: {{strategy.market_position_size}}
- Renvoie la taille de la position en cours sous forme de valeur absolue (c'est-à-dire un nombre non négatif).{{strategy.prev_market_position}}
- Renvoie la position précédente de la stratégie sous forme de chaîne: {{strategy.prev_market_position_size}}
- Renvoie la taille de la position précédente sous forme de valeur absolue (c'est-à-dire un nombre non négatif).
{
"Flag":"{{strategy.order.id}}",
"Exchange":1,
"Currency":"BTC_USDT",
"ContractType":"swap",
"Price":"-1",
"Action":"{{strategy.order.comment}}",
"Amount":"{{strategy.order.contracts}}"
}
Lorsque le script PINE sur TradingView déclenche une transaction, une demande d'url webhook est envoyée.
Le vrai robot FMZ exécutera ce signal.
Le code de cet article est à titre indicatif et vous pouvez le modifier et l'agrandir vous-même.