Pedagang yang sering menggunakan TradingView tahu bahwa TradingView dapat mendorong pesan ke platform lain. Di
Beberapa pemula mungkin bingung dengan judul artikel ini dan deskripsi di atas, tidak masalah! Mari kita mulai dengan deskripsi yang jelas tentang skenario dan prinsip permintaan. OK, mari kita masuk ke topik di sini.
Skenario permintaan: Jadi apa jenis pekerjaan yang ingin kita lakukan? Sederhananya, kita memiliki banyak indikator, strategi, kode, dll yang dapat kita pilih untuk digunakan di TradingView, yang dapat dijalankan langsung di TradingView untuk menggambar garis, menghitung dan menampilkan sinyal perdagangan. Selain itu, TradingView memiliki data harga real-time dan data K-line yang cukup untuk memfasilitasi perhitungan berbagai indikator. Kode skrip ini di TradingView disebut bahasa PINE. Satu-satunya hal yang tidak nyaman adalah bahwa perdagangan bot nyata di TradingView. Meskipun bahasa PINE didukung di FMZ, itu juga dapat digunakan untuk perdagangan bot nyata. Namun, ada beberapa penggemar TradingView yang masih ingin menempatkan pesanan menggunakan sinyal dari grafik di TradingView, sehingga ini dapat diselesaikan oleh FMZ. Jadi dalam artikel ini, kita akan menjelaskan rincian solusi.
Prinsipnya:
Ada empat subjek yang terlibat dalam seluruh skema, yang, secara singkat adalah sebagai berikut:
Jadi jika Anda ingin menggunakannya dengan cara ini, Anda perlu persiapan ini:
Desain
Kemudian TradingView dapat diatur seperti yang ditunjukkan dalam gambar untuk menulis pesan di request Body dan mengirimkannya ke antarmuka API diperpanjang FMZ. Bagaimana untuk memanggil antarmuka API diperpanjang FMZ?
Dalam serangkaian antarmuka API diperpanjang FMZ, kita perlu menggunakanCommandRobot
interface, yang biasanya disebut sebagai berikut:
https://www.fmz.com/api/v1?access_key=xxx&secret_key=yyyy&method=CommandRobot&args=[186515,"ok12345"]
Peraturanaccess_key
dansecret_key
dalamquery
dari url permintaan ini adalah yang diperpanjangAPI KEY
dari platform FMZ, di sini demo diatur untukxxx
danyyyy
. Lalu bagaimana membuat kunci ini? Di halaman ini:https://www.fmz.com/m/account
, membuat di atasnya, menyimpannya dengan benar, tidak mengungkapkan itu.
Kembali ke titik, mari kita lanjutkan berbicara tentang masalah antarmuka dariCommandRobot
. Jika Anda perlu mengaksesCommandRobot
antarmukamethod
dalam permintaan akan ditetapkan untuk:CommandRobot
. Fungsi dariCommandRobot
antarmuka adalah untuk mengirim pesan interaktif ke bot nyata dengan ID melalui platform FMZ, sehingga parameterargs
berisi ID bot yang sebenarnya dan pesan. contoh url permintaan di atas adalah untuk mengirim pesanok12345
ke program bot nyata dengan ID 186515.
Sebelumnya, metode ini digunakan untuk meminta antarmuka CommandRobot dari FMZ diperluas API.ok12345
. Jika pesan berada di Badan yang diminta, Anda perlu menggunakan metode lain:
https://www.fmz.com/api/v1?access_key=xxx&secret_key=yyyy&method=CommandRobot&args=[130350,+""]
Dengan cara ini permintaan dapat mengirim isi dari Badan dalam permintaan sebagai pesan interaktif ke bot nyata dengan ID130350
melalui platform FMZ. Jika pesan pada TradingView diatur untuk:{"close": {{close}}, "name": "aaa"}
, maka bot asli dengan ID dari130350
akan menerima instruksi interaktif:{"close": 39773.75, "name": "aaa"}
Agar
{
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
}
Strategi ini dirancang sebagai arsitektur multi-exchange, sehingga beberapa objek pertukaran dapat dikonfigurasi pada strategi ini, yaitu, operasi penempatan pesanan dari beberapa akun yang berbeda dapat dikendalikan. Hanya Exchange dalam struktur sinyal yang menentukan pertukaran yang akan dioperasikan. Pengaturan 1 adalah untuk memungkinkan sinyal ini untuk mengoperasikan akun pertukaran yang sesuai dengan objek pertukaran pertama yang ditambahkan. Jika spot ContractType diatur ke spot, futures akan menulis kontrak tertentu, seperti, swap untuk kontrak abadi. Daftar harga pasar dapat dilewatkan di -1. Pengaturan tindakan berbeda untuk futures, spot, posisi pembukaan dan penutupan, dan tidak dapat ditetapkan dengan salah.
Selanjutnya, Anda dapat merancang kode strategi. kode strategi lengkap:
//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)
}
}
}
Parameter dan interaksi strategi:
Alamat strategi lengkap dari
Sebelum menjalankan strategi, objek pertukaran harus dikonfigurasi, dan dua parameter
Ini akan mencetak alamat WebHook, didukung perintah tindakan, dan format pesan yang perlu diisi di TradingView.
https://www.fmz.com/api/v1?access_key=22903bab96b26584dc5a22522984df42&secret_key=73f8ba01014023117cbd30cb9d849bfc&method=CommandRobot&args=[505628,+""]
Hanya menyalin dan menempelkannya langsung ke lokasi yang sesuai di TradingView.
Jika Anda ingin mensimulasikan sinyal yang dikirim oleh TradingView, Anda dapat mengklik tombol TestSignal pada interaksi strategi.
Strategi ini mengirim permintaan sendiri (mensimulasikan TradingView mengirim permintaan sinyal), memanggil antarmuka API diperluas FMZ
{"Flag":"45M103Buy","Exchange":1,"Currency":"BTC_USDT","ContractType":"swap","Price":"16000","Action":"buy","Amount":"1"}
Strategi saat ini akan menerima pesan interaktif lain dan melaksanakan, dan menempatkan pesanan untuk transaksi.
Menggunakan tes TradingView mengharuskan akun TradingView berada pada tingkat Pro. Sebelum tes, Anda perlu mengetahui beberapa pengetahuan sebelumnya.
Ambil skrip PINE sederhana (secara acak ditemukan dan dimodifikasi di TradingView) sebagai contoh
//@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)
Berikut ini adalah placeholder.{{strategy.order.contracts}}
dalam kotak
{{strategy.position_size}}
- Mengembalikan nilai kata kunci yang sama di Pine, yaitu ukuran posisi saat ini.{{strategy.order.action}}
- Kembali string {{strategy.order.contracts}}
- Mengembalikan jumlah kontrak yang telah dieksekusi{{strategy.order.price}}
- Kembalikan harga perintah yang dilaksanakan.{{strategy.order.id}}
- Mengembalikan ID perintah yang dieksekusi (string yang digunakan sebagai parameter pertama dalam salah satu panggilan fungsi yang menghasilkan perintah: strategi.entry,strategy.exitatau strategi.order).{{strategy.order.comment}}
- Mengembalikan komentar dari perintah yang dieksekusi (string yang digunakan dalam parameter komentar dalam salah satu panggilan fungsi yang menghasilkan perintah: strategi.entry,strategy.exitJika tidak ada komentar yang ditentukan, nilai daristrategy.order.idakan digunakan.{{strategy.order.alert_message}}
- Mengembalikan nilai parameter alert_message yang dapat digunakan dalam kode strategi{{strategy.market_position}}
- Kembalikan posisi strategi saat ini sebagai string: {{strategy.market_position_size}}
- Mengembalikan ukuran posisi saat ini dalam bentuk nilai absolut (yaitu, angka non-negatif).{{strategy.prev_market_position}}
- Kembali posisi sebelumnya dari strategi sebagai string: {{strategy.prev_market_position_size}}
- Mengembalikan ukuran posisi sebelumnya dalam bentuk nilai absolut (yaitu, bilangan non-negatif).
{
"Flag":"{{strategy.order.id}}",
"Exchange":1,
"Currency":"BTC_USDT",
"ContractType":"swap",
"Price":"-1",
"Action":"{{strategy.order.comment}}",
"Amount":"{{strategy.order.contracts}}"
}
Ketika skrip PINE pada TradingView memicu transaksi, permintaan url webhook akan dikirim.
Robot FMZ yang sebenarnya akan mengeksekusi sinyal ini.
Kode dalam artikel ini hanya untuk referensi, dan dapat disesuaikan dan diperluas oleh Anda sendiri dalam penggunaan yang sebenarnya.