avatar of 发明者量化-小小梦 发明者量化-小小梦
Suivre Messages privés
4
Suivre
1271
Abonnés

Une autre solution de stratégie d'exécution de signal TradingView

Créé le: 2022-11-30 10:52:07, Mis à jour le: 2025-05-16 16:18:13
comments   17
hits   4333

[TOC]

Une autre solution de stratégie d’exécution de signal TradingView

Une autre solution de stratégie d’exécution de signal TradingView

Les traders qui utilisent fréquemment TradingView savent que TradingView peut envoyer des messages vers d’autres plateformes. Auparavant, une stratégie de push de signal TradingView était publiée dans la bibliothèque de documents. Le contenu du message envoyé était codé en dur dans l’URL de la demande, ce qui était quelque peu rigide. Dans cet article, nous allons repenser une stratégie d’exécution de signal TradingView d’une nouvelle manière.

Scénarios et principes

Certains étudiants novices peuvent être confus en voyant le titre de cet article et la description ci-dessus, cela n’a pas d’importance ! Clarifions d’abord les scénarios et les principes de la demande. Je vous fais savoir de quoi je parle. Bon, allons droit au but.

  1. Scénario de demande : Après toutes ces discussions, qu’est-ce que cette chose est censée faire ? En termes simples, nous disposons de nombreux indicateurs, stratégies, codes, etc. que nous pouvons choisir d’utiliser sur TradingView. Ceux-ci peuvent être exécutés directement sur TradingView et peuvent tracer des lignes, calculer, afficher des signaux de trading, etc. De plus, TradingView dispose de données de prix en temps réel et de données K-line suffisantes pour faciliter le calcul de divers indicateurs. Ces codes de script sur TradingView sont appelés langage PINE. Le seul inconvénient est le trading réel sur TradingView. Bien que le langage PINE soit déjà pris en charge sur FMZ, il peut également être exécuté en temps réel. Il existe cependant des fans inconditionnels de TradingView qui espèrent toujours pouvoir passer des commandes en fonction des signaux envoyés par les graphiques de TradingView. Cette demande peut également être satisfaite par FMZ. Cet article explique donc les détails spécifiques de cette solution.

  2. Principe :

Une autre solution de stratégie d’exécution de signal TradingView

L’ensemble du plan comprend quatre éléments principaux, qui sont en bref :

numéro de série corps principal décrire
1 TradingView (Vue de trading sur la photo) TradingView exécute le script PINE, qui peut envoyer des signaux et accéder à l’interface API étendue de FMZ.
2 Plateforme FMZ (plateforme FMZ (site Web) sur la photo) Gérez le marché réel, envoyez des instructions interactives sur la page du marché réel et utilisez également l’interface API étendue pour permettre à la plate-forme FMZ d’envoyer des instructions interactives au programme de stratégie du marché réel sur le dépositaire
3 Le vrai programme sur le logiciel d’hébergement (robot de stratégie FMZ sur la photo) Le programme réel qui exécute la stratégie d’exécution du signal TradingView
4 Échange (échange sur la photo) L’échange configuré sur le marché réel, l’échange auquel le programme du marché réel sur le dépositaire envoie directement la demande de passer une commande

Donc si vous voulez jouer comme ça, vous avez besoin des préparatifs suivants : 1. Le script exécuté sur TradingView est responsable de l’envoi de requêtes de signal à l’interface API étendue de FMZ. Le compte TradingView doit être au moins un membre PRO. 2. Déployez un programme de garde sur FMZ, qui doit pouvoir accéder à l’interface d’échange (comme les serveurs à Singapour, au Japon, à Hong Kong, etc.). 3. Configurez la CLÉ API de l’échange sur FMZ pour fonctionner (passer une commande) lorsque le signal TradingView est envoyé. 4. Vous devez disposer d’une « stratégie d’exécution de signal TradingView », dont parle principalement cet article.

Stratégie d’exécution du signal TradingView

La version précédente de « TradingView Signal Execution Strategy » n’était pas conçue pour être flexible et le message ne pouvait être codé en dur que dans l’URL de la requête envoyée par TradingView. Si nous voulons que TradingView écrive des informations variables dans le corps lors de l’envoi d’un message, nous ne pouvons rien faire pour le moment. Par exemple, le contenu du message sur TradingView ressemble à ceci :

Une autre solution de stratégie d’exécution de signal TradingView

TradingView peut ensuite être configuré comme indiqué dans la figure, en écrivant le message dans le corps de la requête et en l’envoyant à l’interface API étendue de FMZ. Alors, comment appelez-vous cette interface API étendue de FMZ ?

Parmi la série d’interfaces API étendues de FMZ, nous allons utiliserCommandRobotCette interface est généralement appelée ainsi :

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

Cette URL de demandequeryDansaccess_keyetsecret_keyIl s’agit d’une extension de la plateforme FMZAPI KEY, donc ici nous le mettons àxxxetyyyy. Comment créer cette CLÉ ? Sur cette page :https://www.fmz.com/m/account, créez-en un, conservez-le soigneusement et ne le divulguez jamais.

Une autre solution de stratégie d’exécution de signal TradingView

Revenons au sujet, continuonsCommandRobotProblème d’interface. Si vous avez besoin d’accéderCommandRobotInterface, dans la requêtemethodIl suffit de le régler sur :CommandRobotCommandRobotLa fonction de cette interface est d’envoyer un message interactif à un disque réel d’un certain ID via la plate-forme FMZ, donc les paramètresargsLe contenu de la requête est l’ID et le message réels. L’exemple d’URL de requête ci-dessus permet d’envoyer une requête à l’ID186515Programme réel, envoyer un messageok12345

Auparavant, cette méthode était utilisée pour demander l’interface CommandRobot de l’API d’extension FMZ. Le message ne pouvait être que codé en dur, comme dans l’exemple ci-dessus.ok12345. Si le message se trouve dans le corps de la requête, une autre méthode est nécessaire :

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

De cette façon, la demande peut être envoyée via la plateforme FMZ et le contenu du corps de la demande est envoyé sous forme de message interactif à l’utilisateur avec l’ID130350La vraie affaire. Si le message sur TradingView est défini sur :{"close": {{close}}, "name": "aaa"}, alors l’ID est130350Le disque réel recevra des instructions interactives :{"close": 39773.75, "name": "aaa"}

Pour que la « Stratégie d’exécution du signal TradingView » comprenne correctement l’instruction envoyée par TradingView lorsqu’elle reçoit l’instruction interactive, le format du message doit être convenu à l’avance :

{
    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",           // 交易量
}

La stratégie est conçue comme une architecture multi-échange, de sorte que plusieurs objets d’échange peuvent être configurés sur cette stratégie, ce qui signifie que les opérations de commande de plusieurs comptes différents peuvent être contrôlées. Il suffit d’utiliser Exchange dans la structure du signal pour spécifier l’échange à exploiter. Le définir sur 1 signifie que ce signal exploitera le compte d’échange correspondant au premier objet d’échange ajouté. Si le contrat spot doit être exploité, définissez ContractType sur spot ; pour les contrats à terme, écrivez le contrat spécifique, par exemple, écrivez swap pour les contrats perpétuels. Pour le prix de l’ordre du marché, passez simplement -1. Les paramètres d’action sont différents pour les positions à terme, au comptant, d’ouverture et de fermeture, et ne peuvent pas être définis de manière incorrecte.

Ensuite, vous pouvez concevoir le code de la stratégie. Le code de la stratégie complète est le suivant :

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

Paramètres de stratégie et interactions :

Une autre solution de stratégie d’exécution de signal TradingView

「Stratégie d’exécution du signal TradingView」Adresse complète de la stratégie : https://www.fmz.com/strategy/392048

Test simple

Avant d’exécuter la stratégie, vous devez configurer l’objet d’échange et définir les deux paramètres « AccessKey of FMZ platform » et « SecretKey of FMZ platform » dans les paramètres de la stratégie. Assurez-vous de ne pas les définir de manière incorrecte. En l’exécutant, il montre :

Une autre solution de stratégie d’exécution de signal TradingView

Les éléments suivants seront imprimés dans l’ordre : l’adresse WebHook qui doit être renseignée sur TradingView, les instructions d’action prises en charge et le format du message. L’important est l’adresse du WebHook :

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

Copiez-le, collez-le et écrivez-le à la position correspondante sur TradingView.

Si vous souhaitez simuler l’envoi d’un signal par TradingView, vous pouvez cliquer sur le bouton TestSignal dans l’interaction de stratégie :

Une autre solution de stratégie d’exécution de signal TradingView

Cette stratégie enverra une requête par elle-même (simulant TradingView envoyant une demande de signal), appellera l’interface API étendue FMZ et enverra un message à la stratégie elle-même :

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

La stratégie actuelle recevra un autre message interactif et exécutera :

Une autre solution de stratégie d’exécution de signal TradingView

Et passer une commande.

Tests avec TradingView dans des scénarios réels

Pour utiliser les tests TradingView, vous devez disposer d’un compte TradingView de niveau Pro. Avant de procéder au test, vous devez connaître certaines connaissances préalables qui doivent être brièvement expliquées.

Prenons comme exemple un script PINE simple (trouvé sur TradingView et modifié)

//@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. Le script PINE peut joindre certaines informations lorsque le script émet une instruction de commande

Les éléments suivants sont des espaces réservés. Par exemple, j’ai écrit dans la case « Message » de l’alarme :{{strategy.order.contracts}}, puis lorsqu’une commande est déclenchée, un message sera envoyé (en fonction des paramètres de l’alarme, du push e-mail, de la demande d’URL webhook, de la fenêtre contextuelle, etc.), et le message inclura la quantité de la commande exécutée cette temps.

{{strategy.position_size}} - Renvoie la valeur du même mot-clé dans Pine, qui correspond à la taille de la position actuelle. {{strategy.order.action}} - Renvoie la chaîne « acheter » ou « vendre » pour un ordre exécuté. {{strategy.order.contracts}} - Renvoie le nombre de contrats pour l’ordre exécuté. {{strategy.order.price}} - Renvoie le prix auquel l’ordre a été exécuté. {{strategy.order.id}} - Renvoie l’ID de l’ordre exécuté (une chaîne utilisée comme premier argument dans l’un des appels de fonction ayant généré un ordre : strategy.entry, strategy.exit ou strategy.order). {{strategy.order.comment}} - Renvoie le commentaire de l’ordre exécuté (la chaîne utilisée dans le paramètre commentaire dans l’un des appels de fonction qui ont généré l’ordre : strategy.entry, strategy.exit ou strategy.order). Si aucune annotation n’est spécifiée, la valeur de strategy.order.id sera utilisée. {{strategy.order.alert_message}} - Renvoie la valeur du paramètre alert_message, qui peut être utilisée dans le code Pine de la stratégie lors de l’appel de l’une des fonctions de passation de commandes : strategy.entry, strategy.exit ou strategy.order. Cette fonctionnalité est uniquement prise en charge dans Pine v4. {{strategy.market_position}} - Renvoie la position actuelle de la stratégie sous forme de chaîne : « long », « flat » ou « short ». {{strategy.market_position_size}} - Renvoie la taille de la position actuelle sous forme de valeur absolue (c’est-à-dire un nombre non négatif). {{strategy.prev_market_position}} - Renvoie la dernière position de la stratégie sous forme de chaîne : « long », « flat » ou « short ». {{strategy.prev_market_position_size}} - Renvoie la taille de la position précédente sous forme de valeur absolue (c’est-à-dire un nombre non négatif).

  1. Combinez la « Stratégie d’exécution du signal TradingView » pour créer des messages
{
    "Flag":"{{strategy.order.id}}",
    "Exchange":1,
    "Currency":"BTC_USDT",
    "ContractType":"swap",
    "Price":"-1",
    "Action":"{{strategy.order.comment}}",
    "Amount":"{{strategy.order.contracts}}"
}
  1. Laissez TradingView émettre un signal lorsque ce script PINE est en cours d’exécution. Vous devez définir une alarme lors du chargement de ce script sur TradingView

Une autre solution de stratégie d’exécution de signal TradingView

Lorsque le script PINE sur TradingView déclenche une action de trading, une demande d’URL webhook est envoyée.

Une autre solution de stratégie d’exécution de signal TradingView

Une autre solution de stratégie d’exécution de signal TradingView

Le trading réel de FMZ exécutera ce signal.

Une autre solution de stratégie d’exécution de signal TradingView

Une autre solution de stratégie d’exécution de signal TradingView

URL de la vidéo

Vidéo Xigua : https://www.ixigua.com/7172134169580372513?utm_source=xiguastudio Station B : https://www.bilibili.com/video/BV1BY411d7c6/ Zhihu : https://www.zhihu.com/zvideo/1581722694294487040

Les codes de l’article sont fournis à titre indicatif uniquement. Vous pouvez les ajuster et les étendre pour une utilisation réelle.