4
ফোকাস
1271
অনুসারী

FMZ প্ল্যাটফর্মের বাহ্যিক সংকেত অভ্যর্থনা নিয়ে আলোচনা: কৌশলে অন্তর্নির্মিত Http পরিষেবা সহ সংকেত গ্রহণের জন্য একটি সম্পূর্ণ সমাধান

তৈরি: 2024-12-17 11:44:07, আপডেট করা হয়েছে: 2024-12-17 16:08:11
comments   0
hits   663

[TOC]

FMZ প্ল্যাটফর্মের বাহ্যিক সংকেত অভ্যর্থনা নিয়ে আলোচনা: কৌশলে অন্তর্নির্মিত Http পরিষেবা সহ সংকেত গ্রহণের জন্য একটি সম্পূর্ণ সমাধান

ভূমিকা

পূর্ববর্তী নিবন্ধে“এফএমজেড প্ল্যাটফর্মের বাহ্যিক সংকেত গ্রহণের বিষয়ে আলোচনা: বর্ধিত API বনাম কৌশল অন্তর্নির্মিত HTTP পরিষেবা”আলোচনায়, আমরা প্রোগ্রামেটিক ট্রেডিংয়ের জন্য বাহ্যিক সংকেত পাওয়ার দুটি ভিন্ন উপায় তুলনা করেছি এবং বিশদ বিশ্লেষণ করেছি। বাহ্যিক সংকেতগুলি পাওয়ার জন্য FMZ প্ল্যাটফর্ম এক্সটেনশন API ব্যবহার করার সমাধানটি ইতিমধ্যেই প্ল্যাটফর্ম নীতি লাইব্রেরিতে একটি সম্পূর্ণ কৌশল রয়েছে, এই নিবন্ধে, আমরা সংকেতগুলি গ্রহণ করার জন্য বিল্ট-ইন নীতি Http পরিষেবা ব্যবহার করার একটি সম্পূর্ণ সমাধান বাস্তবায়ন করব৷

কৌশল বাস্তবায়ন

ট্রেডিং ভিউ সিগন্যাল অ্যাক্সেস করার জন্য FMZ এক্সটেনশন API ব্যবহার করার পূর্ববর্তী কৌশল অনুসরণ করে, আমরা পূর্ববর্তী বার্তা বিন্যাস, বার্তা প্রক্রিয়াকরণ পদ্ধতি ইত্যাদি ব্যবহার করতে থাকি এবং কৌশলটিতে সহজ পরিবর্তন করেছি।

যেহেতু নীতির মধ্যে তৈরি পরিষেবাগুলি HTTP বা HTTPS ব্যবহার করতে পারে, একটি সাধারণ প্রদর্শনের জন্য, আমরা HTTP প্রোটোকল ব্যবহার করি, আইপি হোয়াইটলিস্ট যাচাইকরণ যোগ করি এবং পাসওয়ার্ড যাচাই যোগ করি। নিরাপত্তা আরও বাড়ানোর প্রয়োজন হলে, নীতির অন্তর্নির্মিত পরিষেবাটিকে একটি HTTPS পরিষেবা হিসাবে ডিজাইন করা যেতে পারে।

//信号结构
var Template = {
    Flag: "45M103Buy",     // 标识,可随意指定
    Exchange: 1,           // 指定交易所交易对
    Currency: "BTC_USDT",  // 交易对
    ContractType: "spot",  // 合约类型,swap,quarter,next_quarter,现货填写spot
    Price: "{{close}}",    // 开仓或者平仓价格,-1为市价
    Action: "buy",         // 交易类型[ buy:现货买入 , sell:现货卖出 , long:期货做多 , short:期货做空 , closesell:期货买入平空 , closebuy:期货卖出平多]
    Amount: "1",           // 交易量
}

var Success = "#5cb85c"    // 成功颜色
var Danger = "#ff0000"     // 危险颜色
var Warning = "#f0ad4e"    // 警告颜色
var buffSignal = []

// Http服务
function serverFunc(ctx, ipWhiteList, passPhrase) {
    var path = ctx.path()
    if (path == "/CommandRobot") {
        // 校验IP地址
        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 
            }
        }

        // 校验口令
        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)
    }
}

// 校验信号消息格式
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("交易所最小编号为1,并且为整数", Danger)
        return
    }
    if (Signal.Amount <= 0 || typeof(Signal.Amount) != "number") {
        Log("交易量不能小于0,并且为数值类型", typeof(Signal.Amount), Danger)
        return
    }
    if (typeof(Signal.Price) != "number") {
        Log("价格必须是数值", Danger)
        return
    }
    if (Signal.ContractType == "spot" && Signal.Action != "buy" && Signal.Action != "sell") {
        Log("指令为操作现货,Action错误,Action:", Signal.Action, Danger)
        return 
    }
    if (Signal.ContractType != "spot" && Signal.Action != "long" && Signal.Action != "short" && Signal.Action != "closesell" && Signal.Action != "closebuy") {
        Log("指令为操作期货,Action错误,Action:", Signal.Action, Danger)
        return 
    }
    return true
}

// 信号处理对象
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("创建任务:", 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) {
            // 非现货,则设置合约
            e.SetContractType(task.ContractType)
        } else if (task.ContractType == "spot" && name.indexOf("Futures_") == -1) {
            isFutures = false 
        } else {
            task.error = "指令中的ContractType与配置的交易所对象类型不匹配"
            return 
        }
        
        var depth = e.GetDepth()
        if (!depth || !depth.Bids || !depth.Asks) {
            task.error = "订单薄数据异常"
            return 
        }
        
        if (depth.Bids.length == 0 && depth.Asks.length == 0) {
            task.error = "盘口无订单"
            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 = "获取精度失败"
            return 
        }
        
        e.SetPrecision(self.pricePrecision, self.amountPrecision)
        
        // buy:现货买入 , sell:现货卖出 , long:期货做多 , short:期货做空 , closesell:期货买入平空 , closebuy:期货卖出平多
        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 = "交易方向错误:" + 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 = "交易方向错误:" + task.Action
                return 
            }
        }
        var id = tradeFunc(task.Price, task.Amount)
        if (!id) {
            task.error = "下单失败"
        }
        
        task.finished = true
    }
    
    return self
}

function main() {
    // 重置日志信息
    if (isResetLog) {
        LogReset(1)
    }

    Log("交易类型[ buy:现货买入 , sell:现货卖出 , long:期货做多 , short:期货做空 , closesell:期货买入平空 , closebuy:期货卖出平多]", Danger)
    Log("指令模板:", 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}`)
    }

    // 创建Http内置服务
    __Serve("http://0.0.0.0:" + port, serverFunc, ipWhiteList, passPhrase)

    // 初始化执行的代码
    if (initCode && initCode.length > 0) {
        try {
            Log("执行初始化代码:", initCode)
            eval(initCode)
        } catch(error) {
            Log("e.name:", error.name, "e.stack:", error.stack, "e.message:", error.message)
        }
    }    

    // 创建信号管理对象
    var manager = createManager()
    
    while (true) {
        try {
            // 检测交互控件,用于测试
            var cmd = GetCommand()
            if (cmd) {
                // 发送Http请求,模拟测试
                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": signal})
                        Log("测试请求的应答:", ret)
                    } else {
                        var ret = HttpQuery(`http://${serverIP}:${port}/CommandRobot?passPhrase=${passPhrase}`, {"method": "POST", "body": signal})
                        Log("测试请求的应答:", ret)
                    }                    
                }
            }

            // 检测内置Http服务收到请求之后通知主线程的消息,写入manager对象的任务队列
            var msg = threading.mainThread().peekMessage(-1)
            if (msg) {
                Log("收到消息 msg:", msg)
                var objSignal = JSON.parse(msg)
                if (DiffObject(Template, objSignal)) {
                    Log("接收到交易信号指令:", objSignal)
                    buffSignal.push(objSignal)
                    
                    // 检查交易量、交易所编号
                    if (!CheckSignal(objSignal)) {
                        continue
                    }
                    
                    // 创建任务
                    if (objSignal["Flag"] == "TestSignal") {
                        Log("收到测试消息:", JSON.stringify(objSignal))
                    } else {
                        manager.newTask(objSignal)
                    }                    
                } else {
                    Log("指令无法识别", signal)
                }
            } else {
                Sleep(1000 * SleepInterval)
            }

            // 处理任务
            manager.process()
            
            // 状态栏显示信号
            if (buffSignal.length > maxBuffSignalRowDisplay) {
                buffSignal.shift()
            }
            var buffSignalTbl = {
                "type" : "table",
                "title" : "信号记录",
                "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)
        }        
    }
}

FMZ প্ল্যাটফর্মের বাহ্যিক সংকেত অভ্যর্থনা নিয়ে আলোচনা: কৌশলে অন্তর্নির্মিত Http পরিষেবা সহ সংকেত গ্রহণের জন্য একটি সম্পূর্ণ সমাধান

  • পোর্ট প্যারামিটার: যদি HTTP প্রোটোকল ব্যবহার করা হয়, শুধুমাত্র পোর্ট 80 ট্রেডিং ভিউতে সেট করা যেতে পারে।
  • সার্ভারআইপি প্যারামিটার: সার্ভারের সর্বজনীন আইপি ঠিকানা পূরণ করুন।
  • initCode পরামিতি: বিনিময় পরীক্ষার পরিবেশ পরীক্ষার জন্য ভিত্তি ঠিকানা পরিবর্তন করতে ব্যবহার করা যেতে পারে।

বাহ্যিক সংকেতগুলি অ্যাক্সেস করার জন্য বর্ধিত API ব্যবহার করার কৌশলের তুলনায়, কৌশল পরিবর্তনটি বড় নয়, এটি কেবল একটি যোগ করেserverFuncHttp পরিষেবা প্রক্রিয়াকরণ ফাংশন FMZ প্ল্যাটফর্মের নতুন মাল্টি-থ্রেডেড মেসেজিং পদ্ধতি ব্যবহার করে:postMessage/peekMessage, অন্যান্য কোড প্রায় অপরিবর্তিত.

আইপি হোয়াইটলিস্ট

ট্রেডিং ভিউ-এর ওয়েবহুকের কারণে করা অনুরোধগুলি শুধুমাত্র নিম্নলিখিত আইপি ঠিকানাগুলি থেকে করা হয়েছে:

52.89.214.238
34.212.75.30
54.218.53.128
52.32.178.7

তাই আমরা কৌশলে একটি প্যারামিটার যোগ করিipWhiteList, আইপি হোয়াইটলিস্ট সেট করতে ব্যবহৃত হয় যেগুলি এই আইপি অ্যাড্রেস হোয়াইটলিস্টে নেই।

        // 校验IP地址
        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, যাচাইকরণ পাসওয়ার্ড সেট করতে ব্যবহৃত হয় এই পাসওয়ার্ডটি ট্রেডিং ভিউতে ওয়েবহুক url সেটিংসে কনফিগার করা হয়েছে যেগুলি যাচাইকরণ পাসওয়ার্ডের সাথে মেলে না।

উদাহরণস্বরূপ, আমরা সেট করেছি:test123456

        // 校验口令
        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")

অবশ্যই, সত্যিকারের ট্রেডিং করার জন্য আপনি সরাসরি FMZ প্ল্যাটফর্মে PINE স্ক্রিপ্ট চালাতে পারেন, কিন্তু আপনি যদি চান ট্রেডিং ভিউ প্ল্যাটফর্মটি সিগন্যাল পাঠানোর জন্য PINE স্ক্রিপ্ট চালাতে, তাহলে আপনি শুধুমাত্র আমাদের আলোচনা করা সমাধানগুলি ব্যবহার করতে পারেন।

আমাদের এই স্ক্রিপ্টের অর্ডার ফাংশনের দিকে মনোযোগ দিতে হবে আমাদের ওয়েবহুক অনুরোধে এই PINE স্ক্রিপ্টটিকে মানিয়ে নিতে, আমাদের লেনদেন ফাংশনটি পরিবর্তন করতে হবে।commentপরিবর্তন করুন, যা আমরা পরে নিবন্ধে উল্লেখ করব।

WebhookUrl এবং শরীরের সেটিংসের অনুরোধ করুন

WebhookUrl এর সেটিংস এবং রিকোয়েস্ট বডি বাহ্যিক সিগন্যাল অ্যাক্সেস করার জন্য পূর্ববর্তী বর্ধিত API পদ্ধতির মতোই এই নিবন্ধে আপনি উল্লেখ করতে পারেন।

Webhook Url

FMZ প্ল্যাটফর্মের বাহ্যিক সংকেত অভ্যর্থনা নিয়ে আলোচনা: কৌশলে অন্তর্নির্মিত Http পরিষেবা সহ সংকেত গ্রহণের জন্য একটি সম্পূর্ণ সমাধান

যখন আমরা এই PINE স্ক্রিপ্টটি ট্রেডিং ভিউতে একটি নির্দিষ্ট বাজারের চার্টে যুক্ত করি (আমরা পরীক্ষার জন্য Binance-এর ETH_USDT চিরস্থায়ী চুক্তির বাজার নির্বাচন করেছি), তখন আমরা দেখতে পাব যে স্ক্রিপ্টটি কাজ করা শুরু করেছে। তারপর স্ক্রিনশটে দেখানো হিসাবে আমরা স্ক্রিপ্টে একটি অ্যালার্ম যোগ করি।

FMZ প্ল্যাটফর্মের বাহ্যিক সংকেত অভ্যর্থনা নিয়ে আলোচনা: কৌশলে অন্তর্নির্মিত Http পরিষেবা সহ সংকেত গ্রহণের জন্য একটি সম্পূর্ণ সমাধান

ওয়েবহুক ইউআরএল সেটিংস: কৌশল কোডটি স্বয়ংক্রিয়ভাবে ওয়েবহুক ইউআরএল তৈরি করার জন্য ডিজাইন করা হয়েছে।

FMZ প্ল্যাটফর্মের বাহ্যিক সংকেত অভ্যর্থনা নিয়ে আলোচনা: কৌশলে অন্তর্নির্মিত Http পরিষেবা সহ সংকেত গ্রহণের জন্য একটি সম্পূর্ণ সমাধান

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

ট্রেডিং ভিউতে বলা হয়েছে যে ওয়েবহুক ইউআরএল শুধুমাত্র HTTP অনুরোধের জন্য পোর্ট 80 ব্যবহার করতে পারে, তাই আমরা কৌশলটিতে পোর্ট প্যারামিটারটি 80 তে সেট করেছি, যাতে আপনি দেখতে পারেন যে কৌশল দ্বারা তৈরি ওয়েবহুক ইউআরএলের লিঙ্ক পোর্টটিও 80।

শরীরের বার্তা

FMZ প্ল্যাটফর্মের বাহ্যিক সংকেত অভ্যর্থনা নিয়ে আলোচনা: কৌশলে অন্তর্নির্মিত Http পরিষেবা সহ সংকেত গ্রহণের জন্য একটি সম্পূর্ণ সমাধান

তারপরে স্ক্রিনশটের “সেটিংস” ট্যাবে অনুরোধ করা বডি মেসেজ সেট করুন।

{
    "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}}” এর ভরাট বিষয়বস্তু যখন ভবিষ্যতে অ্যালার্ম ট্রিগার হয়৷

তাদের মধ্যে, “লং” হল “{{strategy.order.comment}}” এর ভরাট বিষয়বস্তু যখন ভবিষ্যতে অ্যালার্ম ট্রিগার হয়৷ কৌশলে চিহ্নিত সংকেতগুলি হল (নীচের স্ক্রিনশট):

FMZ প্ল্যাটফর্মের বাহ্যিক সংকেত অভ্যর্থনা নিয়ে আলোচনা: কৌশলে অন্তর্নির্মিত Http পরিষেবা সহ সংকেত গ্রহণের জন্য একটি সম্পূর্ণ সমাধান

তাই সেটিংস অবশ্যই সামঞ্জস্যপূর্ণ হতে হবে। এখানে আমরা অর্ডার ফাংশনের জন্য “লং” এবং “শর্ট” সেট করি, লং খোলার এবং ছোট খোলার জন্য সংকেত নির্দেশ করে।

পাইন স্ক্রিপ্ট প্রতিটি অর্ডারের পরিমাণ নির্দিষ্ট করে না, তাই যখন ট্রেডিং ভিউ একটি অ্যালার্ম বার্তা পাঠায়, তখন এটি “{{strategy.order.contracts}}” অংশটি পূরণ করতে ডিফল্ট অর্ডারের পরিমাণ ব্যবহার করে।

বাস্তব অফার পরীক্ষা

FMZ প্ল্যাটফর্মের বাহ্যিক সংকেত অভ্যর্থনা নিয়ে আলোচনা: কৌশলে অন্তর্নির্মিত Http পরিষেবা সহ সংকেত গ্রহণের জন্য একটি সম্পূর্ণ সমাধান

FMZ প্ল্যাটফর্মের বাহ্যিক সংকেত অভ্যর্থনা নিয়ে আলোচনা: কৌশলে অন্তর্নির্মিত Http পরিষেবা সহ সংকেত গ্রহণের জন্য একটি সম্পূর্ণ সমাধান

যখন ট্রেডিং ভিউতে চলমান PINE স্ক্রিপ্টটি ট্রেডিং ফাংশন চালায়, কারণ আমরা Webhook url অ্যালার্ম সেট করি, তখন ট্রেডিং ভিউ প্ল্যাটফর্ম আমাদের কৌশলটির অন্তর্নির্মিত HTTP পরিষেবাতে একটি POST অনুরোধ পাঠাবে এই অনুরোধের প্রশ্নে প্রমাণীকরণের জন্য একটি পাসওয়ার্ড প্যারামিটার রয়েছেpassPhrase. প্রকৃত অনুরোধ বডি প্রাপ্ত এর অনুরূপ:

FMZ প্ল্যাটফর্মের বাহ্যিক সংকেত অভ্যর্থনা নিয়ে আলোচনা: কৌশলে অন্তর্নির্মিত Http পরিষেবা সহ সংকেত গ্রহণের জন্য একটি সম্পূর্ণ সমাধান

তারপরে আমাদের কৌশল এই বডিতে থাকা বার্তার উপর ভিত্তি করে সংশ্লিষ্ট লেনদেন অপারেশন চালায়।

এটা দেখা যায় যে কৌশলটি ট্রেডিং ভিউতে পাইন স্ক্রিপ্টের উপর ভিত্তি করে OKX সিমুলেশন পরিবেশে সিঙ্ক্রোনাস সিগন্যাল লেনদেন করে।

নীতি ঠিকানা

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

FMZ কোয়ান্টিটেটিভের প্রতি আপনার মনোযোগের জন্য ধন্যবাদ, এবং পড়ার জন্য আপনাকে ধন্যবাদ।