TradingViewを頻繁に使用するトレーダーは,TradingViewがメッセージを他のプラットフォームにプッシュできることを知っています. FMZプラットフォームの
この記事のタイトルと上記の説明は,初心者にとって混乱を招くかもしれません.それは問題ではありません! 需要シナリオと原則の明確な説明から始めましょう.OK,ここで話題に入ります.
需要シナリオ: では,どのような仕事をしたいのでしょうか? 簡単に言うと,TradingViewで選択できる多くのインジケーター,戦略,コードなどがあります. これらのインジケーターは,直接TradingViewで実行してラインを描き,計算し,取引シグナルを表示することができます. さらに,TradingViewにはリアルタイム価格データと,さまざまなインジケーターの計算を容易にするのに十分なKラインデータがあります. TradingView上のこれらのスクリプトコードはPINE言語と呼ばれます.唯一便利でないのは,TradingView上の実際のボット取引です.PINE言語はFMZでサポートされているものの,実際のボット取引にも使用できます.しかし,TradingView上のチャートからのシグナルを使用して注文をしたいTradingViewのファンがまだいくつかいるので,これはFMZで解決できます.この記事では,解決策の詳細を説明します.
原則:
計画全体には4つのテーマが関わっています. 概要は以下の通りです.
薬の使い方はこうです
前のバージョンの"TradingView Signal Execution Strategy"のデザインは柔軟ではありません.メッセージは,TradingViewから送信された要求のURLにのみ書き込むことができます.メッセージをプッシュする際にTradingViewがボディにいくつかの変数情報を書き込むことを望む場合は,現時点では何もできません.例えば,TradingView上のメッセージのコンテンツは:
要求ボディにメッセージを書いて FMZ の拡張 API インターフェースに送信する TradeView を設定できます. FMZ の拡張 API インターフェースを呼び出すにはどうすればよいですか?
FMZの拡張 API インターフェースのシリーズでは,我々は使用する必要がありますCommandRobot
インターフェースは通常,以下のように呼ばれます.
https://www.fmz.com/api/v1?access_key=xxx&secret_key=yyyy&method=CommandRobot&args=[186515,"ok12345"]
についてaccess_key
そしてsecret_key
についてquery
このリクエストのURLは拡張されています.API KEY
試行錯誤を表示します.xxx
そしてyyyy
このページでは,この鍵を入力します.https://www.fmz.com/m/account
作り出し 適切に保管し 公開しないでください
インタフェースの問題について話を続けましょう.CommandRobot
サイトにアクセスしたい場合はCommandRobot
インターフェース,method
要求の設定は次のようになります.CommandRobot
機能についてCommandRobot
FMZプラットフォームを通じてIDを持つ本物のボットにインタラクティブなメッセージを送信します.args
上記のリクエスト url の例は,メッセージを送信します.ok12345
本物のボットプログラムに 186515のIDで
以前,この方法は FMZ 拡張 API の CommandRobot インターフェースをリクエストするために使用されていました. メッセージは上記の例でのみ記述できます.ok12345
メッセージが要求されたボディにある場合は,別の方法を使用する必要があります:
https://www.fmz.com/api/v1?access_key=xxx&secret_key=yyyy&method=CommandRobot&args=[130350,+""]
インタラクティブなメッセージとして,IDを持つ実際のボットに送信することができます.130350
FMZ プラットフォームを通してです.もし TradingView のメッセージが:{"close": {{close}}, "name": "aaa"}
真のボットです130350
インタラクティブな指示を受けます{"close": 39773.75, "name": "aaa"}
{
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
}
この戦略はマルチ交換アーキテクチャとして設計されているため,この戦略で複数の交換オブジェクトを構成することができる.つまり,複数の異なるアカウントのオーダー配置操作を制御することができる.シグナル構造内の交換のみが操作される交換を指定する.設定1は,この信号が最初の追加された交換オブジェクトに対応する交換アカウントを操作できるようにすることです.スポットコントラクトタイプがスポットに設定されている場合,先行は永久契約のスワップなどの特定の契約を書きます.市場価格リストは -1で通過できます.アクション設定は先行,スポット,開設および閉じるポジションには異なります.正しく設定することはできません.
次に戦略コードを設計します 戦略コードを完了します
//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)
}
}
}
戦略パラメータと相互作用
戦略を実行する前に,交換オブジェクトを構成し,戦略パラメータに2つのパラメータ"FMZプラットフォーム上のAccessKey"と"FMZプラットフォーム上のSecretKey"を設定する必要があります.実行すると,以下のように表示されます:
WebHook アドレス,サポートされているアクション コマンド,および TradingView で記入する必要があるメッセージ形式を印刷します.重要なのは WebHook アドレスです:
https://www.fmz.com/api/v1?access_key=22903bab96b26584dc5a22522984df42&secret_key=73f8ba01014023117cbd30cb9d849bfc&method=CommandRobot&args=[505628,+""]
トレーディングビューの対応位置に直接コピーして貼り付けます
トレーディングビューから送信された信号をシミュレートしたい場合は,戦略インタラクションのテストシグナルボタンをクリックできます.
この戦略は独自のリクエストを送信 (信号リクエストを送信するTradingViewをシミュレート) し,FMZ
{"Flag":"45M103Buy","Exchange":1,"Currency":"BTC_USDT","ContractType":"swap","Price":"16000","Action":"buy","Amount":"1"}
現在の戦略は別のインタラクティブなメッセージを受け取り,実行し,取引の注文をします.
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)
記号の位置記号は次のとおりです.{{strategy.order.contracts}}
警告の"メッセージ"欄では,命令が起動するとメッセージが送信されます (警告の設定,メールプッシュ,ウェブフックURL要求,ポップアップなどに応じて),メッセージには,この時に実行された命令の数が含まれます.
{{strategy.position_size}}
- パインで同じキーワードの値,つまり現在のポジションのサイズを返します.{{strategy.order.action}}
- 実行されたオーダーのために {{strategy.order.contracts}}
- 命令が実行された契約の数を返します.{{strategy.order.price}}
- 実行命令の価格を返してください.{{strategy.order.id}}
- 実行命令のIDを返します (命令を生成する関数呼び出しの1つの最初のパラメータとして使用される文字列: strategy.entry,strategy.exitまたは戦略.オーダー).{{strategy.order.comment}}
- 実行命令のコメントを返します (命令を生成する関数呼び出しの1つでコメントパラメータで使用された文字列: strategy.entry,strategy.exitコメントが指定されていない場合,strategy.order.id使用されます.{{strategy.order.alert_message}}
- 命令を出すために使用される関数の一つを呼び出すときに,戦略のPineコードで使用できる alert_messageパラメータの値を返します: strategy.entry,strategy.exitこれはPine v4 でのみサポートされます.{{strategy.market_position}}
- ストラテジーの現在の位置を文字列として返します: {{strategy.market_position_size}}
- 絶対値 (つまり,負でない数) の形式で現在のポジションのサイズを返します.{{strategy.prev_market_position}}
- ストラテジーの前の位置を文字列として返します: {{strategy.prev_market_position_size}}
絶対値 (つまり,負でない数) の形式で前の位置のサイズを返します.
{
"Flag":"{{strategy.order.id}}",
"Exchange":1,
"Currency":"BTC_USDT",
"ContractType":"swap",
"Price":"-1",
"Action":"{{strategy.order.comment}}",
"Amount":"{{strategy.order.contracts}}"
}
TradingViewの PINE スクリプトがトランザクションを起動すると,webhook url リクエストが送信されます.
FMZの本物のロボットは この信号を実行します
この記事 の コード は 参考 に 向け られ て いる だけ で あり,実際 に 使用 する とき に 自分 で 調整 や 拡張 する こと が でき ます.