پچھلے مضمون میںایف ایم زیڈ پلیٹ فارم کی بیرونی سگنل وصولی پر تبادلہ خیال: توسیع شدہ API بمقابلہ حکمت عملی بلٹ ان HTTP سروس، ہم نے پروگرامٹک ٹریڈنگ کے لئے بیرونی سگنل وصول کرنے کے دو مختلف طریقوں کا موازنہ کیا اور تفصیلات کا تجزیہ کیا۔ بیرونی سگنل وصول کرنے کے لئے ایف ایم زیڈ پلیٹ فارم توسیع شدہ API کا استعمال کرنے کا حل پلیٹ فارم کی حکمت عملی لائبریری میں ایک مکمل حکمت عملی ہے۔ اس مضمون میں ، آئیے سگنل وصول کرنے کے لئے حکمت عملی بلٹ ان ایچ ٹی پی سروس کا استعمال کرنے کا ایک مکمل حل نافذ کریں۔
ٹریڈنگ ویو سگنلز تک رسائی کے لئے ایف ایم زیڈ توسیع API کا استعمال کرنے کی پچھلی حکمت عملی کے بعد ، ہم پچھلے پیغام کی شکل ، پیغام پروسیسنگ کا طریقہ وغیرہ استعمال کرتے ہیں اور حکمت عملی میں آسان ترمیم کرتے ہیں۔
چونکہ حکمت عملی میں شامل خدمات Http یا HTTPS استعمال کرسکتی ہیں ، لہذا ایک سادہ مظاہرے کے لئے ، ہم Http پروٹوکول کا استعمال کرتے ہیں ، IP وائٹ لسٹ کی توثیق شامل کرتے ہیں ، اور پاس ورڈ کی توثیق شامل کرتے ہیں۔ اگر سیکیورٹی کو مزید بڑھانے کی ضرورت ہو تو ، حکمت عملی میں شامل خدمت کو Https سروس کے طور پر ڈیزائن کیا جاسکتا ہے۔
//Signal structure
var Template = {
Flag: "45M103Buy", // Logo, can be specified at will
Exchange: 1, // Designated exchange trading pairs
Currency: "BTC_USDT", // Trading pairs
ContractType: "spot", // Contract type, swap, quarter, next_quarter, spot fill in spot
Price: "{{close}}", // Opening or closing price, -1 is the market price
Action: "buy", // Transaction type [buy: spot buy, sell: spot sell, long: futures long, short: futures short, closesell: futures buy to close short, closebuy: futures sell to close long]
Amount: "1", // Trading volume
}
var Success = "#5cb85c" // Success color
var Danger = "#ff0000" // Danger color
var Warning = "#f0ad4e" // Warning color
var buffSignal = []
// Http service
function serverFunc(ctx, ipWhiteList, passPhrase) {
var path = ctx.path()
if (path == "/CommandRobot") {
// Verify IP address
var fromIP = ctx.remoteAddr().split(":")[0]
if (ipWhiteList && ipWhiteList.length > 0) {
var ipList = ipWhiteList.split(",")
if (!ipList.includes(fromIP)) {
ctx.setStatus(500)
ctx.write("IP address not in white list")
Log("500 Error: IP address not in white list", "#FF0000")
return
}
}
// Verify password
var pass = ctx.rawQuery().length > 0 ? ctx.query("passPhrase") : ""
if (passPhrase && passPhrase.length > 0) {
if (pass != passPhrase) {
ctx.setStatus(500)
ctx.write("Authentication failed")
Log("500 Error: Authentication failed", "#FF0000")
return
}
}
var body = JSON.parse(ctx.body())
threading.mainThread().postMessage(JSON.stringify(body))
ctx.write("OK")
// 200
} else {
ctx.setStatus(404)
}
}
// 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 exchange number is 1 and is an integer.", Danger)
return
}
if (Signal.Amount <= 0 || typeof(Signal.Amount) != "number") {
Log("The trading volume cannot be less than 0 and must be a numeric type.", typeof(Signal.Amount), Danger)
return
}
if (typeof(Signal.Price) != "number") {
Log("Price must be a numeric value", Danger)
return
}
if (Signal.ContractType == "spot" && Signal.Action != "buy" && Signal.Action != "sell") {
Log("The instruction is to operate spot goods, and the Action is wrong, Action:", Signal.Action, Danger)
return
}
if (Signal.ContractType != "spot" && Signal.Action != "long" && Signal.Action != "short" && Signal.Action != "closesell" && Signal.Action != "closebuy") {
Log("The instruction is to operate futures, and the Action is wrong, Action:", Signal.Action, Danger)
return
}
return true
}
// Signal processing object
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 a 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) {
// If it is not spot, set up a contract
e.SetContractType(task.ContractType)
} else if (task.ContractType == "spot" && name.indexOf("Futures_") == -1) {
isFutures = false
} else {
task.error = "The ContractType in the instruction does not match the configured exchange object type"
return
}
var depth = e.GetDepth()
if (!depth || !depth.Bids || !depth.Asks) {
task.error = "Abnormal order book data"
return
}
if (depth.Bids.length == 0 && depth.Asks.length == 0) {
task.error = "No orders on the market"
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 get precision"
return
}
e.SetPrecision(self.pricePrecision, self.amountPrecision)
// buy: spot purchase, sell: spot sell, long: futures long, short: futures short, closesell: futures buy to close short, closebuy: futures sell to close long
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 = "Order failed"
}
task.finished = true
}
return self
}
function main() {
// Reset log information
if (isResetLog) {
LogReset(1)
}
Log("Transaction type [buy: spot buy, sell: spot sell, long: futures long, short: futures short, closesell: futures buy to close short, closebuy: futures sell to close long]", Danger)
Log("Instruction templates:", JSON.stringify(Template), Danger)
if (!passPhrase || passPhrase.length == 0) {
Log("webhook url:", `http://${serverIP}:${port}/CommandRobot`)
} else {
Log("webhook url:", `http://${serverIP}:${port}/CommandRobot?passPhrase=${passPhrase}`)
}
// Creating an Http built-in service
__Serve("http://0.0.0.0:" + port, serverFunc, ipWhiteList, passPhrase)
// Initialize the code to execute
if (initCode && initCode.length > 0) {
try {
Log("Execute the initialization code:", initCode)
eval(initCode)
} catch(error) {
Log("e.name:", error.name, "e.stack:", error.stack, "e.message:", error.message)
}
}
// Create a signal management object
var manager = createManager()
while (true) {
try {
// Detect interactive controls for testing
var cmd = GetCommand()
if (cmd) {
// Send Http request, simulate test
var arrCmd = cmd.split(":", 2)
if (arrCmd[0] == "TestSignal") {
// {"Flag":"TestSignal","Exchange":1,"Currency":"BTC_USDT","ContractType":"swap","Price":"10000","Action":"long","Amount":"1"}
var signal = cmd.replace("TestSignal:", "")
if (!passPhrase || passPhrase.length == 0) {
var ret = HttpQuery(`http://${serverIP}:${port}/CommandRobot`, {"method": "POST", "body": JSON.stringify(signal)})
Log("Test request response:", ret)
} else {
var ret = HttpQuery(`http://${serverIP}:${port}/CommandRobot?passPhrase=${passPhrase}`, {"method": "POST", "body": JSON.stringify(signal)})
Log("Test request response:", ret)
}
}
}
// Detect the message that the built-in Http service notifies the main thread after receiving the request, and writes it to the task queue of the manager object
var msg = threading.mainThread().peekMessage(-1)
if (msg) {
Log("Receive message msg:", msg)
var objSignal = JSON.parse(msg)
if (DiffObject(Template, objSignal)) {
Log("Receive trading signal instructions:", objSignal)
buffSignal.push(objSignal)
// Check trading volume, exchange ID
if (!CheckSignal(objSignal)) {
continue
}
// Create a task
if (objSignal["Flag"] == "TestSignal") {
Log("Received test message:", JSON.stringify(objSignal))
} else {
manager.newTask(objSignal)
}
} else {
Log("Command not recognized", signal)
}
} else {
Sleep(1000 * SleepInterval)
}
// Processing tasks
manager.process()
// Status bar displays signal
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) + "`")
} catch (error) {
Log("e.name:", error.name, "e.stack:", error.stack, "e.message:", error.message)
}
}
}
بیرونی سگنل تک رسائی کے لئے توسیع شدہ API کے استعمال کی حکمت عملی کے مقابلے میں، حکمت عملی بہت زیادہ تبدیل نہیں ہوتی ہے. یہ صرف ایک اضافہ کرتا ہےserverFunc
ایچ ٹی پی سروس پروسیسنگ فنکشن اور ایم ایف زیڈ پلیٹ فارم کے ذریعہ نئے شامل کردہ ملٹی تھریڈ میسج پاسنگ طریقہ استعمال کرتا ہے۔postMessage
/ peekMessage
. باقی کوڈز تقریبا unchanged ہیں.
چونکہ ٹریڈنگ ویو
52.89.214.238
34.212.75.30
54.218.53.128
52.32.178.7
لہذا، ہم ایک پیرامیٹر شاملipWhiteList
آئی پی ایڈریس وائٹ لسٹ قائم کرنے کی حکمت عملی پر عمل کریں۔ آئی پی ایڈریس وائٹ لسٹ میں شامل نہ ہونے والی تمام درخواستوں کو نظر انداز کردیا جائے گا۔
// Verify IP address
var fromIP = ctx.remoteAddr().split(":")[0]
if (ipWhiteList && ipWhiteList.length > 0) {
var ipList = ipWhiteList.split(",")
if (!ipList.includes(fromIP)) {
ctx.setStatus(500)
ctx.write("IP address not in white list")
Log("500 Error: IP address not in white list", "#FF0000")
return
}
}
ایک پیرامیٹر شامل کریںpassPhrase
تصدیق کا پاس ورڈ مقرر کرنے کے لئے حکمت عملی پر. یہ پاس ورڈ ٹریڈنگ ویو پر ویب ہک یو آر ایل کی ترتیبات میں ترتیب دیا گیا ہے. درخواستیں جو تصدیق کا پاس ورڈ سے مماثل نہیں ہیں نظر انداز کر دیا جائے گا.
مثال کے طور پر، ہم مقرر:test123456
.
// Verify password
var pass = ctx.rawQuery().length > 0 ? ctx.query("passPhrase") : ""
if (passPhrase && passPhrase.length > 0) {
if (pass != passPhrase) {
ctx.setStatus(500)
ctx.write("Authentication failed")
Log("500 Error: Authentication failed", "#FF0000")
return
}
}
بیرونی سگنل ٹرگر ماخذ کے طور پر ٹریڈنگ ویو پلیٹ فارم کے PINE اسکرپٹ کا استعمال کریں ، اور ٹریڈنگ ویو کے ذریعہ سرکاری طور پر بے ترتیب طور پر جاری کردہ PINE اسکرپٹس میں سے ایک کو منتخب کریں:
//@version=6
strategy("MovingAvg Cross", overlay=true)
length = input(9)
confirmBars = input(1)
price = close
ma = ta.sma(price, length)
bcond = price > ma
bcount = 0
bcount := bcond ? nz(bcount[1]) + 1 : 0
if (bcount == confirmBars)
strategy.entry("MACrossLE", strategy.long, comment="long")
scond = price < ma
scount = 0
scount := scond ? nz(scount[1]) + 1 : 0
if (scount == confirmBars)
strategy.entry("MACrossSE", strategy.short, comment="short")
یقینا، آپ براہ راست ایف ایم زیڈ پلیٹ فارم پر پائن اسکرپٹ کو چلانے کے لئے براہ راست تجارت کر سکتے ہیں، لیکن اگر آپ چاہتے ہیں کہ ٹریڈنگ ویو پلیٹ فارم سگنل بھیجنے کے لئے پائن اسکرپٹ چلائیں، تو آپ صرف ان حلوں کا استعمال کرسکتے ہیں جن پر ہم نے تبادلہ خیال کیا ہے.
ہمیں اس اسکرپٹ کے آرڈر رکھنے کے فنکشن پر توجہ مرکوز کرنے کی ضرورت ہے۔ ہماری ویب ہک کی درخواست میں پیغامات کے لئے اس PINE اسکرپٹ کو اپنانے کے لئے ، ہمیں تجارتی فنکشن میں ترمیم کرنے کی ضرورت ہے۔comment
، جس کا ذکر ہم بعد میں کریں گے۔
WebhookUrl اور درخواست جسم کی ترتیبات بنیادی طور پر بیرونی سگنلز تک رسائی حاصل کرنے کے لئے پچھلے توسیع شدہ API طریقہ کار کی طرح ہیں۔ اسی حصے کو اس مضمون میں نہیں دہرایا جائے گا۔ آپ پچھلے مضمون کا حوالہ دے سکتے ہیں۔
ٹریڈنگ ویو پر ہم نے اس PINE اسکرپٹ کو کسی مارکیٹ کے چارٹ میں شامل کرنے کے بعد (ہم ٹیسٹنگ کے لئے بائننس
ویب ہک یو آر ایل کی ترتیبات: اسٹریٹی کوڈ کو خود بخود ویب ہک یو آر ایل پیدا کرنے کے لئے ڈیزائن کیا گیا ہے۔ ہمیں صرف اسٹریٹیجی آپریشن کے آغاز میں لاگ سے کاپی کرنے کی ضرورت ہے۔
http://xxx.xxx.xxx.xxx:80/CommandRobot?passPhrase=test123456
ٹریڈنگ ویو کا کہنا ہے کہ ویب ہک یو آر ایل صرف HTTP درخواستوں کے لیے پورٹ 80 استعمال کر سکتا ہے، لہذا ہم بھی حکمت عملی میں پورٹ پیرامیٹر 80 پر مقرر، تاکہ ہم دیکھ سکتے ہیں کہ ویب ہک یو آر ایل کی حکمت عملی کی طرف سے پیدا لنک پورٹ بھی 80 ہے.
اس کے بعد ہم
{
"Flag":"{{strategy.order.id}}",
"Exchange":1,
"Currency":"ETH_USDT",
"ContractType":"swap",
"Price":"-1",
"Action":"{{strategy.order.comment}}",
"Amount":"{{strategy.order.contracts}}"
}
کیا آپ کو یاد ہے کہ PINE اسکرپٹ میں آرڈر رکھنے کا کوڈ جس کے بارے میں ہم نے ابھی بات کی تھی؟ آئیے مثال کے طور پر لمبی پوزیشن کھولنے کا کوڈ لیتے ہیں:
strategy.entry("MACrossLE", strategy.long, comment="long")
لہذا ترتیبات مستقل ہونی چاہئیں۔ یہاں ہم آرڈر فنکشن کے لئے
PINE اسکرپٹ ہر آرڈر کے لئے آرڈر کی مقدار کی وضاحت نہیں کرتا ہے ، لہذا جب ٹریڈنگ ویو انتباہی پیغام بھیجتا ہے تو ، یہ
جب ٹریڈنگ ویو پر چلنے والی پائن اسکرپٹ ٹریڈنگ فنکشن کو انجام دیتی ہے ، کیونکہ ہم نے ویب ہک یو آر ایل الرٹ مرتب کیا ہے ، ٹریڈنگ ویو پلیٹ فارم ہماری حکمت عملی کی بلٹ ان ایچ ٹی پی سروس کو پی او ایس ٹی کی درخواست بھیجے گا۔ اس درخواست کی استفسار میں پاس ورڈ پیرامیٹر شامل ہے۔passPhrase
تصدیق کے لئے۔ اصل درخواست دینے والا ادارہ اسی طرح کا ہے:
اس کے بعد ہماری حکمت عملی اس جسم میں پیغام کی بنیاد پر متعلقہ تجارتی کارروائیوں کو انجام دیتا ہے.
یہ دیکھا جا سکتا ہے کہ حکمت عملی ٹریڈنگ ویو پر PINE اسکرپٹ کے مطابق OKX تخروپن ماحول میں مطابقت پذیر سگنل ٹریڈنگ انجام دیتا ہے.
ایف ایم زیڈ کوانٹ پر آپ کی توجہ کا شکریہ، اور پڑھنے کا شکریہ۔