Händler, die TradingView häufig verwenden, wissen, dass TradingView Nachrichten auf andere Plattformen verschieben kann. In unserer
Einige Anfänger werden vielleicht durch den Titel dieses Artikels und die obige Beschreibung verwirrt, es spielt keine Rolle! Lassen Sie uns mit einer klaren Beschreibung der Nachfrage-Szenarien und -Grundsätze beginnen.
Nachfrage-Szenarien: Also, welche Art von Arbeit wollen wir, dass es macht? Einfach ausgedrückt, wir haben viele Indikatoren, Strategien, Codes usw., die wir auf TradingView verwenden können, die direkt auf TradingView ausgeführt werden können, um Linien zu zeichnen, Handelssignale zu berechnen und anzuzeigen. Darüber hinaus verfügt TradingView über Echtzeitpreisdaten und ausreichende K-Line-Daten, um die Berechnung verschiedener Indikatoren zu erleichtern. Diese Skriptcodes auf TradingView werden PINE-Sprache genannt. Das Einzige, was nicht bequem ist, ist, dass der echte Bot auf TradingView handelt. Obwohl die PINE-Sprache auf FMZ unterstützt wird, kann sie auch für den echten Bot-Handel verwendet werden. Es gibt jedoch einige TradingView-Fans, die immer noch Aufträge platzieren möchten, indem sie die Signale aus den Charts auf TradingView verwenden, so dass dies durch FMZ gelöst werden kann. In diesem Artikel werden wir also die Details der Lösung erklären.
Grundsatz:
Die Gesamtregelung umfaßt vier Themen:
Wenn Sie es also auf diese Weise verwenden wollen, brauchen Sie diese Vorbereitungen:
Das Design der
Dann kann die TradingView so eingestellt werden, wie in der Abbildung gezeigt, um die Nachricht im Request Body zu schreiben und an die erweiterte API-Schnittstelle von FMZ zu senden.
In einer Reihe von erweiterten API-Schnittstellen von FMZ müssen wir dieCommandRobot
Schnittstelle, die in der Regel folgendermaßen bezeichnet wird:
https://www.fmz.com/api/v1?access_key=xxx&secret_key=yyyy&method=CommandRobot&args=[186515,"ok12345"]
Dieaccess_key
undsecret_key
in derquery
Die URL dieser Anfrage ist die erweiterteAPI KEY
der FMZ-Plattform, hier ist die Demo aufxxx
undyyyy
Wie erstellt man dann diesen Schlüssel?https://www.fmz.com/m/account
, auf sie zu kreieren, sie ordnungsgemäß zu halten, sie nicht zu verbreiten.
Zurück zum Punkt, lasst uns weiter über das Schnittstellenproblem vonCommandRobot
. Wenn Sie auf dieCommandRobot
Schnittstellemethod
in der Anfrage auf:CommandRobot
. Die Funktion derCommandRobot
Interface ist eine interaktive Nachricht an einen echten Bot mit einer ID über die FMZ-Plattform zu senden, so dass der Parameterargs
Das obige Beispiel ist die Botsendung.ok12345
zu einem echten Bot-Programm mit einer ID von 186515.
Früher wurde diese Methode verwendet, um die CommandRobot-Schnittstelle der FMZ erweiterten API anzufordern.ok12345
. Wenn die Nachricht in der angeforderten Stelle ist, müssen Sie eine andere Methode verwenden:
https://www.fmz.com/api/v1?access_key=xxx&secret_key=yyyy&method=CommandRobot&args=[130350,+""]
Auf diese Weise kann die Anfrage den Inhalt des Körpers in der Anfrage als eine interaktive Nachricht an den echten Bot mit ID senden130350
Wenn die Nachricht auf der TradingView auf:{"close": {{close}}, "name": "aaa"}
, dann der echte Bot mit der ID von130350
Sie erhalten interaktive Anweisungen:{"close": 39773.75, "name": "aaa"}
Damit die
{
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
}
Die Strategie ist als Multi-Exchange-Architektur konzipiert, so dass mehrere Austauschobjekte auf dieser Strategie konfiguriert werden können, d.h. die Bestelloperation mehrerer verschiedener Konten kann gesteuert werden. Nur die Börse in der Signalstruktur spezifiziert den zu betriebenden Austausch. Die Einstellung 1 soll dieses Signal ermöglichen, das Austauschkonto zu betreiben, das dem ersten hinzugefügten Austauschobjekt entspricht. Wenn der Spot ContractType auf Spot gesetzt ist, werden die Futures spezifische Verträge schreiben, z.B. Swap für ewige Verträge. Die Marktpreisliste kann in -1 übergeben werden. Die Aktions-Einstellungen sind für Futures, Spot, Eröffnungs- und Schließpositionen unterschiedlich und kann nicht falsch gesetzt werden.
Als nächstes können Sie den Strategiecode entwerfen.
//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)
}
}
}
Strategieparameter und Wechselwirkungen:
Die vollständige Strategieadresse der
Bevor die Strategie ausgeführt wird, sollte das Exchange-Objekt konfiguriert werden und die beiden Parameter
Es wird die WebHook-Adresse, unterstützte Action-Befehle und das Nachrichtenformat ausdrucken, die in der TradingView ausgefüllt werden müssen.
https://www.fmz.com/api/v1?access_key=22903bab96b26584dc5a22522984df42&secret_key=73f8ba01014023117cbd30cb9d849bfc&method=CommandRobot&args=[505628,+""]
Kopieren und einfügen Sie es direkt an den entsprechenden Ort in der TradingView.
Wenn Sie ein Signal simulieren möchten, das von der TradingView gesendet wird, können Sie auf die Schaltfläche TestSignal auf der Strategieinteraktion klicken.
Diese Strategie sendet eine eigene Anfrage (simuliert eine TradingView, die eine Signalanfrage sendet) und ruft FMZ
{"Flag":"45M103Buy","Exchange":1,"Currency":"BTC_USDT","ContractType":"swap","Price":"16000","Action":"buy","Amount":"1"}
Die aktuelle Strategie erhält eine weitere interaktive Nachricht und führt einen Auftrag zur Transaktion aus.
Die Verwendung des TradingView-Tests erfordert, dass das TradingView-Konto auf der Pro-Ebene ist.
Nehmen Sie zum Beispiel ein einfaches PINE-Skript (zufällig auf der TradingView gefunden und modifiziert)
//@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)
Die folgenden sind Platzhalter.{{strategy.order.contracts}}
in der Box
{{strategy.position_size}}
- Gibt den Wert des gleichen Schlüsselworts in Pine zurück, d.h. die Größe der aktuellen Position.{{strategy.order.action}}
- Gib die Zeichenfolge {{strategy.order.contracts}}
- Gibt die Anzahl der Verträge zurück, für die Aufträge ausgeführt wurden.{{strategy.order.price}}
- Gib den Preis des ausgeführten Auftrags zurück.{{strategy.order.id}}
- Gibt die ID des ausgeführten Auftrags zurück (die Zeichenfolge, die als erster Parameter in einem der Funktionsanrufe verwendet wird, die den Auftrag generieren: strategy.entry,strategy.exitoder Strategie.Order).{{strategy.order.comment}}
- Gibt den Kommentar des ausgeführten Auftrags zurück (die Zeichenfolge, die im Kommentarparameter in einem der Funktionsanrufe verwendet wird, die den Auftrag generieren: strategy.entry,strategy.exit, oder strategy.order). Wenn keine Bemerkung angegeben wird, wird der Wert vonstrategy.order.idwird verwendet.{{strategy.order.alert_message}}
- Gibt den Wert des Parameters alert_message zurück, der im Strategie{{strategy.market_position}}
- Gibt die aktuelle Position der Strategie als Zeichenfolge zurück: {{strategy.market_position_size}}
- Gibt die Größe der aktuellen Position in Form eines absoluten Wertes (d. h. einer nicht negativen Zahl) zurück.{{strategy.prev_market_position}}
- Gibt die vorherige Position der Strategie als Zeichenfolge zurück: {{strategy.prev_market_position_size}}
- Gibt die Größe der vorherigen Position in Form eines absoluten Wertes (d. h. einer nicht negativen Zahl) zurück.
{
"Flag":"{{strategy.order.id}}",
"Exchange":1,
"Currency":"BTC_USDT",
"ContractType":"swap",
"Price":"-1",
"Action":"{{strategy.order.comment}}",
"Amount":"{{strategy.order.contracts}}"
}
Wenn das PINE-Skript in der TradingView eine Transaktion auslöst, wird eine Webhook-URL-Anfrage gesendet.
Der echte FMZ-Bot wird dieses Signal ausführen.
Der Code in diesem Artikel dient nur als Referenz und kann selbst angepasst und erweitert werden.