وسائل لوڈ ہو رہے ہیں... لوڈنگ...

ایف ایم زیڈ پلیٹ فارم کی بیرونی سگنل وصولی پر بحث: حکمت عملی میں بلٹ ان ایچ ٹی پی سروس کے ساتھ سگنل وصول کرنے کے لئے ایک مکمل حل

مصنف:FMZ~Lydia, تخلیق: 2024-12-18 09:22:33, تازہ کاری: 2024-12-18 09:24:49

پیش لفظ

پچھلے مضمون میںایف ایم زیڈ پلیٹ فارم کی بیرونی سگنل وصولی پر تبادلہ خیال: توسیع شدہ 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)
        }        
    }
}

img

  • پورٹ پیرامیٹر: اگر آپ HTTP پروٹوکول استعمال کرتے ہیں تو ، آپ صرف پورٹ 80 کو ٹریڈنگ ویو پر ترتیب دے سکتے ہیں۔
  • سرور آئی پی پیرامیٹر: سرور کا عوامی آئی پی ایڈریس درج کریں۔
  • initCode پیرامیٹر: اس کا استعمال ایکسچینج ٹیسٹ ماحول میں ٹیسٹنگ کے لئے بیس ایڈریس کو سوئچ کرنے کے لئے کیا جاسکتا ہے۔

بیرونی سگنل تک رسائی کے لئے توسیع شدہ 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 اور Request جسم کی ترتیبات

WebhookUrl اور درخواست جسم کی ترتیبات بنیادی طور پر بیرونی سگنلز تک رسائی حاصل کرنے کے لئے پچھلے توسیع شدہ API طریقہ کار کی طرح ہیں۔ اسی حصے کو اس مضمون میں نہیں دہرایا جائے گا۔ آپ پچھلے مضمون کا حوالہ دے سکتے ہیں۔

ویب ہک یو آر ایل

img

ٹریڈنگ ویو پر ہم نے اس PINE اسکرپٹ کو کسی مارکیٹ کے چارٹ میں شامل کرنے کے بعد (ہم ٹیسٹنگ کے لئے بائننس کے ETH_USDT دائمی معاہدہ مارکیٹ کا انتخاب کرتے ہیں) ، ہم دیکھ سکتے ہیں کہ اسکرپٹ کام کرنا شروع ہوگیا ہے۔ پھر ہم اسکرین شاٹ میں دکھائے گئے اسکرپٹ میں انتباہ شامل کرتے ہیں۔

img

ویب ہک یو آر ایل کی ترتیبات: اسٹریٹی کوڈ کو خود بخود ویب ہک یو آر ایل پیدا کرنے کے لئے ڈیزائن کیا گیا ہے۔ ہمیں صرف اسٹریٹیجی آپریشن کے آغاز میں لاگ سے کاپی کرنے کی ضرورت ہے۔

img

http://xxx.xxx.xxx.xxx:80/CommandRobot?passPhrase=test123456

ٹریڈنگ ویو کا کہنا ہے کہ ویب ہک یو آر ایل صرف HTTP درخواستوں کے لیے پورٹ 80 استعمال کر سکتا ہے، لہذا ہم بھی حکمت عملی میں پورٹ پیرامیٹر 80 پر مقرر، تاکہ ہم دیکھ سکتے ہیں کہ ویب ہک یو آر ایل کی حکمت عملی کی طرف سے پیدا لنک پورٹ بھی 80 ہے.

جسم کا پیغام

img

اس کے بعد ہم Settings ٹیب میں درخواست جسم پیغام مقرر کریں جیسا کہ اسکرین شاٹ میں دکھایا گیا ہے.

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

MACrossLE میں بھرا ہوا مواد ہےstrategy.order.id}} جب مستقبل میں الرٹ ٹرگر کیا جاتا ہے.

long {{strategy.order.comment}} میں بھرا ہوا مواد ہے جب مستقبل میں انتباہ کو متحرک کیا جاتا ہے۔ حکمت عملی میں شناخت کردہ سگنل یہ ہے (نیچے اسکرین شاٹ):

img

لہذا ترتیبات مستقل ہونی چاہئیں۔ یہاں ہم آرڈر فنکشن کے لئے long اور short مقرر کرتے ہیں ، جو طویل اور مختصر کھولنے کے سگنل کی نشاندہی کرتے ہیں۔

PINE اسکرپٹ ہر آرڈر کے لئے آرڈر کی مقدار کی وضاحت نہیں کرتا ہے ، لہذا جب ٹریڈنگ ویو انتباہی پیغام بھیجتا ہے تو ، یہ {{strategy.order.contracts}} حصے کو پُر کرنے کے لئے ڈیفالٹ آرڈر کی مقدار کا استعمال کرتا ہے۔

لائیو ٹریڈنگ ٹیسٹ

img

img

جب ٹریڈنگ ویو پر چلنے والی پائن اسکرپٹ ٹریڈنگ فنکشن کو انجام دیتی ہے ، کیونکہ ہم نے ویب ہک یو آر ایل الرٹ مرتب کیا ہے ، ٹریڈنگ ویو پلیٹ فارم ہماری حکمت عملی کی بلٹ ان ایچ ٹی پی سروس کو پی او ایس ٹی کی درخواست بھیجے گا۔ اس درخواست کی استفسار میں پاس ورڈ پیرامیٹر شامل ہے۔passPhraseتصدیق کے لئے۔ اصل درخواست دینے والا ادارہ اسی طرح کا ہے:

img

اس کے بعد ہماری حکمت عملی اس جسم میں پیغام کی بنیاد پر متعلقہ تجارتی کارروائیوں کو انجام دیتا ہے.

یہ دیکھا جا سکتا ہے کہ حکمت عملی ٹریڈنگ ویو پر PINE اسکرپٹ کے مطابق OKX تخروپن ماحول میں مطابقت پذیر سگنل ٹریڈنگ انجام دیتا ہے.

حکمت عملی کا پتہ

https://www.fmz.com/strategy/475235

ایف ایم زیڈ کوانٹ پر آپ کی توجہ کا شکریہ، اور پڑھنے کا شکریہ۔


مزید