Другая стратегия исполнения сигналов TradingView

Автор:Маленькие мечты, Создано: 2022-11-30 10:52:07, Обновлено: 2023-09-18 20:01:09

[TOC]

img

Другая стратегия исполнения сигналов TradingView

Трейдеры, которые часто используют TradingView, знают, что TradingView может продвигать сообщения на другие платформы. Ранее в библиотеке была также опубликована стратегия продвижения сигналов TradingView, которая была написана в URL запроса и была немного негибкой. В этой статье мы перепроектировали стратегию выполнения сигналов TradingView в одном из способов.

Сценарий и принципы

Может быть, некоторые из ваших новых учеников начнут замечать, что название статьи и описание здесь немного странные, но это нормально!

Сценарий спроса: В общем, в TradingView у нас есть множество индикаторов, стратегий, кодов и т.д., которые можно использовать, и они могут работать прямо в TradingView, можно рисовать линии, рассчитывать, показывать торговые сигналы и т.д. И TradingView имеет реальные данные о ценах, достаточное количество K-линий для вычисления различных индикаторов.

Принцип 2:

img

В целом программа включает в себя четыре субъекта, которые в общем и целом:

Номерация Субъекты Описание
1 TradingView (Традиционный вид на рисунке) В TradingView работает сценарий PINE, который позволяет отправлять сигналы и получать доступ к расширенному API-интерфейсу FMZ
2 Платформа FMZ (WEB Управление диском, можно отправлять интерактивные инструкции на дисковой странице, а также через расширенный API-интерфейс позволить платформе FMZ отправлять интерактивные инструкции хостерам.
3 Программа на диске в программном обеспечении хостера (см. рисунок FMZ strategy robot) Процедуры, которые фактически запускаются в стратегии выполнения сигналов TradingView
4 Биржа (exchange на рисунке) Конфигурированные на реальном рынке биржи, операторы на реальном рынке отправляют запрос на заказ напрямую на биржи

Поэтому, если вы хотите играть в эту игру, вам нужно подготовиться к следующему: Скрипт, работающий на TradingView, отвечающий за отправку запросов сигналов на расширенный API-интерфейс FMZ, требует, чтобы учетная запись TradingView была как минимум членом PRO. 2, Для развертывания на FMZ администратора необходимо иметь доступ к интерфейсу биржи (например, сервер в Сингапуре, Японии, Гонконге и т.д.). 3, на FMZ настраивается API KEY биржи, на которой необходимо "заказать" при отправке сигналов TradingView. 4, вам нужна стратегия исполнения сигналов TradingView, о которой идет речь в этой статье.

Стратегия исполнения сигналов TradingView

В предыдущих версиях "Традиционный вид" был менее гибким, сообщения могли быть написаны только в URL-адресе запроса, отправленного в Традиционный вид. Если мы хотим, чтобы Традиционный вид писал информацию о некоторых переменных в Боди при отправке сообщения, это не работает.

img

Так что в TradingView можно настроить, как показано на рисунке, сообщение, написанное в Боди запроса, для отправки на расширенный API-интерфейс FMZ. Как будет вызван этот расширенный API-интерфейс FMZ?

В FMZ мы будем использовать расширенный интерфейс API.CommandRobotЭтот интерфейс обычно называется так:

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

Это запрос на URLqueryВнутриaccess_keyиsecret_keyЭто расширение платформы FMZAPI KEYЗдесь мы покажем, как это сделатьxxxиyyyyНа этой странице: "Как создать KEY?https://www.fmz.com/m/account"Я не хочу, чтобы кто-то, кто создает, создает, а кто-то хранит, а кто-то не разглашает.

img

Возвращаясь к теме, продолжаем:CommandRobotВопрос об интерфейсе.CommandRobotИнтерфейс, запросmethodНастройка:CommandRobotCommandRobotФункция этого интерфейса заключается в том, чтобы отправить интерактивное сообщение через платформу FMZ на дискету определенного ID, поэтому параметрыargsВ ней есть ID диска и сообщение, и этот пример запроса url, который я показывал выше, - это ID.186515Процесс на диске, отправляющий сообщенияok12345

Раньше таким образом запрашивался CommandRobot интерфейс FMZ для расширения API, сообщение можно было записывать только мертвым, например в примере выше.ok12345Если сообщение находится в запрошенном Боди, нужно использовать другой способ:

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

В этом случае запрос может быть отправлен через платформу FMZ в качестве интерактивного сообщения.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, вы можете записать конкретные контракты, такие как постоянные контракты.

В следующий раз мы сможем разработать стратегический код, полный стратегический код:

//信号结构
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)
        }
    }
}

Параметры стратегии и взаимодействие:

img

Полный адрес стратегии выполнения сигналов TradingView:https://www.fmz.com/strategy/392048

Простой тест

Перед запуском политики необходимо настроить объект биржи, установив в параметрах политики параметры "AccessKey для платформы FMZ" и "SecretKey для платформы FMZ", не устанавливая их неправильно.

img

В TradingView нужно заполнить адрес WebHook, поддерживаемые инструкции Action, формат сообщения.

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

Например, вы можете скопировать и наклеить в TradingView соответствующее местоположение.

Если вы хотите имитировать сигнал, отправляемый в TradingView, вы можете нажать на кнопку TestSignal на взаимодействии с политикой:

img

Политика сама отправляет запрос (прикладное обращение TradeView для отправки сигналов), вызывает расширенный API-интерфейс FMZ и отправляет сообщение самой политике:

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

В этом случае текущая политика получает другое сообщение об взаимодействии и выполняет следующее:

img

В этом случае мы не можем позволить себе ничего плохого.

Тестирование с использованием TradingView в реальных ситуациях

Требуется учетная запись TradingView на уровне Pro для тестирования.

В качестве примера используем простой сценарий 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, Пине-скрипты могут сопровождать информацию при выпуске следующих команд

Ниже приведены символы, которые я, например, записываю в поле "Сообщение" в сигнале.{{strategy.order.contracts}}В случае, если заказ запускается, сообщение будет отправлено (в зависимости от настроек на сигнализации, рассылки электронной почты, запроса веб-хука, pop-up window и т. д.), в котором будет указано количество выполненных заказов.

{{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, который можно использовать в Pine-коде политики при вызове одной из функций, используемых для следующего заказа:strategy.exit、 или strategy.order‬ поддерживается только в Pine v4‬{{strategy.market_position}}- возвращение текущей позиции стратегии в виде строки: long, flat, или short.{{strategy.market_position_size}}- возвращает величину текущей позиции в виде абсолютного значения (т.е. не-отрицательного числа);{{strategy.prev_market_position}}- вернуть стратегию в виде строки на предыдущую позицию: long, flat, или short.{{strategy.prev_market_position_size}}- возвращает величину предыдущей позиции в виде абсолютного значения (т.е. не-отрицательного числа);

2) Создание сообщений с помощью "Стратегии исполнения сигналов TradingView"

{
    "Flag":"{{strategy.order.id}}",
    "Exchange":1,
    "Currency":"BTC_USDT",
    "ContractType":"swap",
    "Price":"-1",
    "Action":"{{strategy.order.comment}}",
    "Amount":"{{strategy.order.contracts}}"
}

3, чтобы TradingView сигнализировал при запуске сценария PINE, необходимо установить сигнализацию при загрузке сценария в TradingView

img

Когда сценарий PINE в TradingView запускает действие, отправляется запрос webhook url.

img

img

В этом случае FMZ будет выполнять этот сигнал.

img

img

Адрес видео

Видео о водяной омуте:https://www.ixigua.com/7172134169580372513?utm_source=xiguastudioСтанция Б:https://www.bilibili.com/video/BV1BY411d7c6/Известно:https://www.zhihu.com/zvideo/1581722694294487040

Код в статье является справочным и может быть изменен или расширен для практического использования.


Связанные

Больше

wbe3- маленький фрикадельникМанго, как же работать с дисковой средой?

ГуохуаУ меня вопрос, может ли сообщение предупреждения в tradingview содержать сообщение о последнем заказе? Я хочу получить последний заказ, если он был прибыльным или убыточным, если последний заказ был убыточным, робот не будет выполнять заказ до тех пор, пока последний заказ, который я получил, не будет прибыльным. Вы можете это сделать, пожалуйста? Спасибо!

13811047519/upload/asset/2a5a9fa2b97561c42c027.jpg Прошу Бога, что это за ошибка и как ее устранить?

ПрекрасноЯ добавил 6 и 7 счетов, чтобы использовать этот сигнал для торговли, но временно он довольно большой, сигнал одного счета для биржи будет выполнен только после того, как будет выполнен сигнал следующего счета, это последовательное выполнение, есть ли способ сделать параллельное выполнение сигналов для торговли одновременно?

wbe3- маленький фрикадельникВ политике получения сигнала, как будто никаких печатных доходов, а публичное не будет создано, поэтому, пожалуйста, добавьте соответствующий шаблон формы информации об аккаунте, чтобы увидеть, как политика работает.

Маленькие мечтыЭто открытый обзор политики, который автоматически добавляется к странице.

wbe3- маленький фрикадельникСпасибо, братец, я проверил, но после сделки нет стратегии оценки обзора, нужно ли добавлять самостоятельно

Маленькие мечтыOKX-интерфейс, с помощью которого можно переключаться на среду тестирования на дискете OKX с помощью exchange.IO (от англ. "simulate", true), или на дискету.

ГуохуаСпасибо за ответ, у меня есть два вопроса: 1, что я немного не понимаю, это то, что fmz может написать свой собственный скрипт pine, почему этот текст должен отправлять сигналы в fmz через TradingView, а затем обрабатывать их, а затем торговать? 2, я нашел сейчас очень хорошую стратегию, но без использования исходного кода, я хотел избежать ошибок с помощью моего метода выше, вы сказали, что в сообщении о продвижении увеличить {{стратегия.order.price Я также добавил, но это продвижение, по-видимому, цена при заказе, а затем в fmz, как по этой цене судить о прибыли или убытке, я немного не понимаю.

Маленькие мечтыЕсли это возможно, вы можете продвигать контент {{strategy.order.price}} во время продвижения сообщения, а затем стратегия на FMZ обрабатывает эту информацию, чтобы решить, стоит ли размещать заказ на основе текущей ценовой контрастности.

Маленькие мечтыЯ прошел тест, и он был нормальным.

ПрекрасноХорошо. Спасибо, господин директор.

Маленькие мечтыFMZ добавляет функции параллелизации, которые должны быть изменены в параллельные, хотя в стратегическом коде могут быть значительные изменения.