Die Ressourcen sind geladen. Beförderung...

Ein weiteres TradingView-Signal-Exekutionsstrategieschema

Schriftsteller:FMZ~Lydia, Erstellt: 2022-12-15 21:23:24, Aktualisiert: 2023-09-18 20:01:55

img

Ein weiteres TradingView-Signal-Exekutionsstrategieschema

Händler, die TradingView häufig verwenden, wissen, dass TradingView Nachrichten auf andere Plattformen verschieben kann. In unserer Digest der FMZ-Plattform wurde eine TradingView-Signal-Push-Strategie in der Bibliothek veröffentlicht, in der der Inhalt der geschobenen Nachrichten in der Anforderungs-URL geschrieben wurde, die etwas unflexibel war. In diesem Artikel gestalten wir eine TradingView-Signal-Ausführungsstrategie auf eine neue Weise neu.

Szenarien und Grundsätze

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.

  1. 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.

  2. Grundsatz:

img

Die Gesamtregelung umfaßt vier Themen:

img

Wenn Sie es also auf diese Weise verwenden wollen, brauchen Sie diese Vorbereitungen:

  1. Das im TradingView ausgeführte Skript ist für das Senden von Signalanfragen an die erweiterte API-Schnittstelle des FMZ verantwortlich.
  2. Um ein Docker-Programm auf FMZ zu implementieren, muss es die Art sein, die auf die Austauschoberfläche zugreifen kann (z. B. Server in Singapur, Japan, Hongkong usw.).
  3. Konfigurieren Sie den API-Key der Börse auf (Order) -Operation, wenn das TradingView-Signal auf der FMZ gesendet wird.
  4. Sie müssen eine TradingView Signal Execution Strategy haben, die hauptsächlich in diesem Artikel diskutiert wird.

Strategie zur Ausführung des TradingView Signals

Das Design der TradingView Signal Execution Strategy in der vorherigen Version ist nicht sehr flexibel. Nachrichten können nur an die URL der von der TradingView gesendeten Anfrage geschrieben werden. Wenn wir wollen, dass TradingView beim Verschieben von Nachrichten einige variable Informationen im Körper schreibt, können wir zu diesem Zeitpunkt nichts tun. Zum Beispiel kann der Nachrichteninhalt in der TradingView:

img

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 dieCommandRobotSchnittstelle, 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_keyundsecret_keyin derqueryDie URL dieser Anfrage ist die erweiterteAPI KEYder FMZ-Plattform, hier ist die Demo aufxxxundyyyyWie erstellt man dann diesen Schlüssel?https://www.fmz.com/m/account, auf sie zu kreieren, sie ordnungsgemäß zu halten, sie nicht zu verbreiten.

img

Zurück zum Punkt, lasst uns weiter über das Schnittstellenproblem vonCommandRobot. Wenn Sie auf dieCommandRobotSchnittstellemethodin der Anfrage auf:CommandRobot. Die Funktion derCommandRobotInterface ist eine interaktive Nachricht an einen echten Bot mit einer ID über die FMZ-Plattform zu senden, so dass der ParameterargsDas obige Beispiel ist die Botsendung.ok12345zu 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 senden130350Wenn die Nachricht auf der TradingView auf:{"close": {{close}}, "name": "aaa"}, dann der echte Bot mit der ID von130350Sie erhalten interaktive Anweisungen:{"close": 39773.75, "name": "aaa"}

Damit die TradingView Signal Execution Strategy den von TradingView beim Empfang des interaktiven Befehls gesendeten Befehl korrekt verstehen kann, sollten im Voraus folgende Nachrichtenformate vereinbart werden:

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

img

Die vollständige Strategieadresse der Trading View Signal Execution Strategy:https://www.fmz.com/strategy/392048

Einfacher Test

Bevor die Strategie ausgeführt wird, sollte das Exchange-Objekt konfiguriert werden und die beiden Parameter AccessKey auf der FMZ-Plattform und SecretKey auf der FMZ-Plattform in den Strategieparametern festgelegt werden.

img

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.

img

Diese Strategie sendet eine eigene Anfrage (simuliert eine TradingView, die eine Signalanfrage sendet) und ruft FMZs erweiterte API-Schnittstelle an, um eine Nachricht an die Strategie selbst zu senden:

{"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.

Test der Verwendung von TradingView in der tatsächlichen Szene

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)
  1. Das PINE-Skript kann einige Informationen anhängen, wenn das Skript Befehlsanweisungen sendet

Die folgenden sind Platzhalter.{{strategy.order.contracts}}in der Box message der Benachrichtigung wird eine Nachricht gesendet, wenn der Auftrag ausgelöst wird (gemäß den Einstellungen der Benachrichtigung, Mail Push, Webhook-URL-Anfrage, Pop-up usw.), und die Nachricht enthält die Anzahl der diesmal ausgeführten Aufträge.

{{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 buy oder sell für den ausgeführten Auftrag zurück.{{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 Strategies Pine-Code verwendet werden kann, wenn eine der Funktionen, die zur Bestellung verwendet werden, aufgerufen wird: strategy.entry,strategy.exit, oder strategy.order. Dies wird nur in Pine v4 unterstützt.{{strategy.market_position}}- Gibt die aktuelle Position der Strategie als Zeichenfolge zurück: long, flat oder short.{{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: long, flat oder short.{{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.

  1. Erstellen Sie Nachrichten in Kombination mit der TradingView Signal Execution Strategy
{
    "Flag":"{{strategy.order.id}}",
    "Exchange":1,
    "Currency":"BTC_USDT",
    "ContractType":"swap",
    "Price":"-1",
    "Action":"{{strategy.order.comment}}",
    "Amount":"{{strategy.order.contracts}}"
}
  1. Lassen Sie TradingView ein Signal gemäß der Ausführung des PINE-Skriptes senden.

Wenn das PINE-Skript in der TradingView eine Transaktion auslöst, wird eine Webhook-URL-Anfrage gesendet.

img

Der echte FMZ-Bot wird dieses Signal ausführen.

img

Der Code in diesem Artikel dient nur als Referenz und kann selbst angepasst und erweitert werden.


Verwandt

Mehr