avatar of 发明者量化-小小梦 发明者量化-小小梦
フォロー ダイレクトメッセージ
4
フォロー
1271
フォロワー

別のTradingViewシグナル実行戦略ソリューション

作成日:: 2022-11-30 10:52:07, 更新日:: 2025-05-16 16:18:13
comments   17
hits   4333

[TOC]

別のTradingViewシグナル実行戦略ソリューション

別のTradingViewシグナル実行戦略ソリューション

TradingView を頻繁に使用するトレーダーは、TradingView が他のプラットフォームにメッセージをプッシュできることを知っています。以前は、TradingView シグナル プッシュ戦略がドキュメント ライブラリに公開されていました。プッシュされたメッセージの内容はリクエスト URL にハードコードされており、柔軟性に欠けていました。この記事では、TradingView シグナル実行戦略を別の方法で再設計します。

シナリオと原則

この記事のタイトルと上記の説明を見て、初心者の学生の中には混乱する人もいるかもしれませんが、それは問題ではありません。まず、需要のシナリオと原則を明確にしましょう。私が何を言っているのか教えてください。さて、本題に入りましょう。

  1. 需要シナリオ: ここまで話したのに、これは一体何をするのでしょうか?簡単に言うと、TradingView で使用できるインジケーター、戦略、コードなどが多数あります。これらは TradingView で直接実行でき、ラインを描画したり、計算したり、取引シグナルを表示したりできます。さらに、TradingView には、さまざまな指標の計算を容易にするリアルタイムの価格データと十分な K ライン データがあります。 TradingView 上のこれらのスクリプト コードは PINE 言語と呼ばれます。唯一の不便な点は、TradingView での実際の取引です。 PINE 言語は FMZ ですでにサポートされていますが、リアルタイムで実行することもできます。しかし、TradingView のチャートから送信されるシグナルに基づいて注文をしたいと今でも希望する、TradingView の熱狂的なファンもいます。この要求も FMZ によって解決できます。そこでこの記事では、このソリューションの具体的な詳細について説明します。

  2. 原則:

別のTradingViewシグナル実行戦略ソリューション

全体の計画には、簡単に言うと次の 4 つの主要機関が含まれます。

シリアルナンバー 本体 説明する
1 TradingView(写真はTrading View) TradingView は PINE スクリプトを実行し、シグナルを送信したり、FMZ の拡張 API インターフェースにアクセスしたりできます。
2 FMZプラットフォーム(写真はFMZプラットフォーム(ウェブサイト)) 実際の市場を管理し、実際の市場ページでインタラクティブな指示を送信し、拡張APIインターフェイスを使用して、FMZプラットフォームがカストディアンの実際の市場戦略プログラムにインタラクティブな指示を送信できるようにします。
3 ホスティングソフトウェア上の実際のプログラム(写真のFMZ戦略ロボット) TradingViewシグナル実行戦略を実行する実際のプログラム
4 交換(写真の交換) 実市場上に設定された取引所、カストディアンの実市場プログラムが注文のリクエストを直接送信する取引所

このようにプレイしたい場合は、次の準備が必要です。 1. TradingView で実行されるスクリプトは、FMZ の拡張 API インターフェースにシグナル要求を送信する役割を担います。TradingView アカウントは少なくとも PRO メンバーである必要があります。 2. FMZ に保管プログラムを展開します。保管プログラムは、取引所のインターフェース (シンガポール、日本、香港などのサーバーなど) にアクセスできる必要があります。 3. TradingView シグナルが送信されたときに操作(注文の配置)するように、FMZ 上の取引所の API KEY を設定します。 4. 「TradingView シグナル実行戦略」が必要です。この記事では主にこれについて説明します。

TradingViewシグナル実行戦略

以前のバージョンの「TradingView シグナル実行戦略」は柔軟性を考慮して設計されておらず、メッセージは TradingView によって送信されるリクエストの URL にハードコードすることしかできませんでした。 TradingView がメッセージをプッシュするときに本文に何らかの変数情報を書き込むようにしたい場合、現時点では何もできません。たとえば、TradingView 上のメッセージの内容は次のようになります。

別のTradingViewシグナル実行戦略ソリューション

次に、図に示すように TradingView を設定し、リクエスト本文にメッセージを書き込んで FMZ の拡張 API インターフェースに送信します。では、FMZ のこの拡張 API インターフェースをどのように呼び出すのでしょうか?

FMZの拡張APIインターフェースシリーズの中で、私たちはCommandRobotこのインターフェースは通常、次のように呼び出されます。

https://www.fmz.com/api/v1?access_key=xxx&secret_key=yyyy&method=CommandRobot&args=[186515,"ok12345"]

このリクエストURLqueryaccess_keyそしてsecret_keyこれはFMZプラットフォームの拡張ですAPI KEYなので、ここではxxxそしてyyyy。このKEYを作成するにはどうすればいいですか?このページの内容:https://www.fmz.com/m/account作成して適切に保管し、決して公開しないでください。

別のTradingViewシグナル実行戦略ソリューション

話を元に戻して続けましょうCommandRobotインターフェースの問題。アクセスする必要がある場合CommandRobotリクエスト内のインターフェースmethod次のように設定します:CommandRobotCommandRobotこのインターフェースの機能は、FMZプラットフォームを介して特定のIDの実際のディスクに対話型メッセージを送信することです。そのため、パラメータはargsリクエストの内容は実際のIDとメッセージです。上記のリクエストURLの例は、IDにリクエストを送信するためのものです。186515実際のプログラム、メッセージを送信ok12345

以前は、このメソッドは FMZ 拡張 API の CommandRobot インターフェイスを要求するために使用されていました。メッセージは、上記の例のようにハードコードすることしかできませんでした。ok12345。メッセージがリクエスト本文にある場合は、別のメソッドが必要です。

https://www.fmz.com/api/v1?access_key=xxx&secret_key=yyyy&method=CommandRobot&args=[130350,+""]

このようにして、リクエストはFMZプラットフォームを介して送信され、リクエスト本文の内容はIDを持つユーザーにインタラクティブメッセージとして送信されます。130350本物です。 TradingView のメッセージが次のように設定されている場合:{"close": {{close}}, "name": "aaa"}の場合、IDは130350実際のディスクは対話型の指示を受け取ります:{"close": 39773.75, "name": "aaa"}

「TradingView シグナル実行戦略」が対話型命令を受信したときに TradingView から送信された命令を正しく理解するには、メッセージ形式を事前に合意する必要があります。

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

この戦略はマルチ取引所アーキテクチャとして設計されているため、この戦略では複数の取引所オブジェクトを構成でき、複数の異なるアカウントの注文操作を制御できます。シグナル構造で Exchange を使用して、操作する取引所を指定します。これを 1 に設定すると、このシグナルは最初に追加された取引所オブジェクトに対応する取引所アカウントを操作することになります。スポット契約を運用する場合は、ContractType を spot に設定します。先物の場合は、特定の契約を記述します。たとえば、永久契約の場合は swap を記述します。成行注文価格の場合は、-1 を渡すだけです。アクション設定は先物、スポット、ポジションの開始と終了によって異なり、誤って設定することはできません。

次に、戦略コードを設計します。完全な戦略コードは次のとおりです。

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

var BaseUrl = "https://www.fmz.com/api/v1"   // FMZ扩展API接口地址 
var RobotId = _G()                           // 当前实盘ID
var Success = "#5cb85c"    // 成功颜色
var Danger = "#ff0000"     // 危险颜色
var Warning = "#f0ad4e"    // 警告颜色
var buffSignal = []

// 校验信号消息格式
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 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("模拟TradingView的webhook请求,发送用于测试的POST请求:", url, "body:", cmd, "应答:", 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("创建任务:", 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
}

var manager = createManager()
function HandleCommand(signal) {
    // 检测是否收到交互指令
    if (signal) {
        Log("收到交互指令:", signal)     // 收到交互指令,打印交互指令
    } else {
        return                            // 没有收到时直接返回,不做处理
    }
    
    // 检测交互指令是否是测试指令,测试指令可以由当前策略交互控件发出来进行测试
    if (signal.indexOf("TestSignal") != -1) {
        signal = signal.replace("TestSignal:", "")
        // 调用FMZ扩展API接口,模拟Trading View的webhook,交互按钮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("执行调试代码:", js)
        eval(js)
    } else {
        // 处理信号指令
        objSignal = JSON.parse(signal)
        if (DiffObject(Template, objSignal)) {
            Log("接收到交易信号指令:", objSignal)
            buffSignal.push(objSignal)
            
            // 检查交易量、交易所编号
            if (!CheckSignal(objSignal)) {
                return
            }
            
            // 创建任务
            manager.newTask(objSignal)
        } else {
            Log("指令无法识别", signal)
        }
    }
}

function main() {
    Log("WebHook地址:", "https://www.fmz.com/api/v1?access_key=" + FMZ_AccessKey + "&secret_key=" + FMZ_SecretKey + "&method=CommandRobot&args=[" + RobotId + ',+""]', Danger)
    Log("交易类型[ buy:现货买入 , sell:现货卖出 , long:期货做多 , short:期货做空 , closesell:期货买入平空 , closebuy:期货卖出平多]", Danger)
    Log("指令模板:", JSON.stringify(Template), Danger)
    
    while (true) {
        try {
            // 处理交互
            HandleCommand(GetCommand())
            
            // 处理任务
            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) + "`")
            Sleep(1000 * SleepInterval)
        } catch (error) {
            Log("e.name:", error.name, "e.stack:", error.stack, "e.message:", error.message)
            Sleep(1000 * 10)
        }
    }
}

戦略パラメータと相互作用:

別のTradingViewシグナル実行戦略ソリューション

「TradingView シグナル実行戦略」完全な戦略アドレス: https://www.fmz.com/strategy/392048

簡単なテスト

戦略を実行する前に、交換オブジェクトを構成し、戦略パラメータに「FMZ プラットフォームの AccessKey」と「FMZ プラットフォームの SecretKey」の 2 つのパラメータを設定する必要があります。これらを誤って設定しないようにしてください。実行すると次のようになります:

別のTradingViewシグナル実行戦略ソリューション

TradingView に入力する必要がある WebHook アドレス、サポートされているアクションの指示、およびメッセージ形式が順番に印刷されます。重要なのはWebHookアドレスです。

https://www.fmz.com/api/v1?access_key=22903bab96b26584dc5a22522984df42&secret_key=73f8ba01014023117cbd30cb9d849bfc&method=CommandRobot&args=[505628,+""]

それをコピーして貼り付け、TradingView 上の対応する位置に書き込むだけです。

TradingView がシグナルを送信することをシミュレートする場合は、戦略インタラクションの TestSignal ボタンをクリックします。

別のTradingViewシグナル実行戦略ソリューション

この戦略は、それ自体でリクエストを送信し(TradingView がシグナル リクエストを送信することをシミュレートします)、FMZ 拡張 API インターフェイスを呼び出して、戦略自体にメッセージを送信します。

{"Flag":"45M103Buy","Exchange":1,"Currency":"BTC_USDT","ContractType":"swap","Price":"16000","Action":"buy","Amount":"1"}

現在の戦略は別の対話型メッセージを受信して​​実行します。

別のTradingViewシグナル実行戦略ソリューション

そして注文してください。

実際のシナリオでTradingViewを使用したテスト

TradingView テストを使用するには、プロレベルの TradingView アカウントが必要です。テストを行う前に、簡単に説明する必要がある前提知識がいくつかあります。

簡単なPINEスクリプト(TradingViewで見つけて少し修正したもの)を例に挙げます。

//@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. PINEスクリプトは、スクリプトが注文指示を発行するときにいくつかの情報を添付することができます。

以下はプレースホルダーです。たとえば、アラームの「メッセージ」ボックスに次のように書き込みました。{{strategy.order.contracts}}注文がトリガーされると、メッセージが送信されます(アラーム、メールプッシュ、Webhook URLリクエスト、ポップアップウィンドウなどの設定に基づいて)。メッセージには、今回実行された注文の数量が含まれます。時間。

{{strategy.position_size}} - Pine 内の同じキーワードの値 (現在の位置のサイズ) を返します。 {{strategy.order.action}} - 実行された注文の文字列「buy」または「sell」を返します。 {{strategy.order.contracts}} - 実行された注文の契約数を返します。 {{strategy.order.price}} - 注文が実行された価格を返します。 {{strategy.order.id}} - 実行された注文の ID を返します (注文を生成した関数呼び出しのいずれかの最初の引数として使用される文字列: strategy.entry、strategy.exit、または strategy.order)。 {{strategy.order.comment}} - 実行された注文のコメントを返します (注文を生成した関数呼び出しのいずれかのコメント パラメータで使用される文字列: strategy.entry、strategy.exit、または strategy.order)。アノテーションが指定されていない場合は、strategy.order.id の値が使用されます。 {{strategy.order.alert_message}} - alert_message パラメータの値を返します。これは、注文を出すための関数の 1 つ (strategy.entry、strategy.exit、または strategy.order) を呼び出すときに、戦略の Pine コードで使用できます。この機能は Pine v4 でのみサポートされます。 {{strategy.market_position}} - 戦略の現在の位置を文字列「long」、「flat」、または「short」として返します。 {{strategy.market_position_size}} - 現在の位置のサイズを絶対値(つまり、負でない数)として返します。 {{strategy.prev_market_position}} - 戦略の最終ポジションを文字列として返します: 「long」、「flat」、または「short」。 {{strategy.prev_market_position_size}} - 前の位置のサイズを絶対値(つまり、負でない数)として返します。

  1. 「TradingViewシグナル実行戦略」を組み合わせてメッセージを構築する
{
    "Flag":"{{strategy.order.id}}",
    "Exchange":1,
    "Currency":"BTC_USDT",
    "ContractType":"swap",
    "Price":"-1",
    "Action":"{{strategy.order.comment}}",
    "Amount":"{{strategy.order.contracts}}"
}
  1. このPINEスクリプトが実行されているときにTradingViewがシグナルを発するようにします。このスクリプトをTradingViewにロードするときにアラームを設定する必要があります。

別のTradingViewシグナル実行戦略ソリューション

TradingView 上の PINE スクリプトが取引アクションをトリガーすると、Webhook URL リクエストが送信されます。

別のTradingViewシグナル実行戦略ソリューション

別のTradingViewシグナル実行戦略ソリューション

FMZ の実際の取引はこのシグナルを実行します。

別のTradingViewシグナル実行戦略ソリューション

別のTradingViewシグナル実行戦略ソリューション

ビデオURL

Xigua ビデオ: https://www.ixigua.com/7172134169580372513?utm_source=xiguastudio ステーションB: https://www.bilibili.com/video/BV1BY411d7c6/ 知乎: https://www.zhihu.com/zvideo/1581722694294487040

記事内のコードは参考用です。実際の使用に合わせて調整したり拡張したりできます。