Une autre stratégie d'exécution de signaux TradingView

Auteur:Le petit rêve, Créé: 2022-11-30 10:52:07, Mis à jour: 2023-09-18 20:01:09

[TOC] Je vous en prie.

img

Une autre stratégie d'exécution de signaux TradingView

Les traders qui utilisent TradingView régulièrement savent que TradingView peut pousser des messages vers d'autres plateformes. Une stratégie de poussée de signal de TradingView a également été publiée dans la bibliothèque précédente.

Scènes et principes

Le titre de cet article et la description ci-dessus peuvent sembler un peu étranges à certains nouveaux élèves, mais ce n'est pas grave.

Le scénario de la demande: Qu'est-ce qu'il faut faire de cette chose? En termes simples, nous avons beaucoup d'indicateurs, de stratégies, de codes, etc. à choisir dans TradingView, qui peuvent être utilisés directement dans TradingView, peuvent dessiner des lignes, calculer, afficher des signaux de trading, etc. Et TradingView a des données de prix en temps réel, suffisamment de données de ligne K pour faciliter le calcul de tous les indicateurs.

Deuxième principe:

img

L'ensemble du programme est axé sur quatre thématiques:

Numérotation Le sujet Décrire
1 TradingView (en anglais seulement) Le script PINE est exécuté sur TradingView pour envoyer des signaux et accéder à l'API étendue de FMZ
2 La plateforme FMZ (voir le site web de la plateforme FMZ) Gérer le disque dur, envoyer des instructions d'interaction sur la page du disque dur, ou par l'interface API étendue pour permettre à la plate-forme FMZ d'envoyer des instructions d'interaction aux programmes de stratégie de disque dur sur l'hôte
3 Programme de disque dur sur le logiciel de l'hôte (voir le diagramme du robot de stratégie FMZ) TradingView stratégie d'exécution des signaux
4 Les échanges (exchange sur le graphique) Échange configuré sur le marché, le programme sur le marché sur le gestionnaire envoie directement la demande à l'échange.

Si vous voulez jouer, vous devez vous préparer comme suit: 1, un script fonctionnant sur TradingView qui est responsable de l'envoi de requêtes de signaux à l'API d'extension de l'interface FMZ et qui nécessite un compte TradingView avec au moins un membre PRO. Pour déployer un programme administrateur sur FMZ, vous devez avoir accès à l'interface de l'échange (par exemple, serveurs situés à Singapour, au Japon, à Hong Kong, etc.). 3° Configurer sur FMZ l'API KEY de l'échange à opérer lorsque le signal TradingView est envoyé. Vous avez besoin d'une stratégie d'exécution de signaux TradingView, qui est le sujet principal de cet article.

Politique d'exécution du signal TradingView

La stratégie d'exécution du signal TradingView de la dernière version était moins flexible, les messages ne pouvant être écrits que dans l'url de la demande envoyée par TradingView. Si nous voulions que TradingView écrive des informations variables dans le corps lorsque le message est envoyé, cela ne fonctionnerait pas.

img

Dans TradingView, il est possible de configurer le message dans le corps de la requête et de l'envoyer à l'API étendue de FMZ.

Dans la gamme d'interfaces d'API étendues FMZ, nous allons utiliserCommandRobotL'interface est généralement appelée comme suit:

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

Cette requête d'URLqueryDansaccess_keyetsecret_keyIl s'agit d'une extension de la plateforme FMZ.API KEYLa démonstration ici est donc définie commexxxetyyyyDans cette page, vous trouverez des informations sur la création de cette clé.https://www.fmz.com/m/accountIl est possible de créer un, de le conserver et de ne jamais le divulguer.

img

Retour sur le sujet, continuez.CommandRobotLe problème de l'interface.CommandRobotL'interface, dans la requêtemethodLe code est défini comme suit:CommandRobotCommandRobotLa fonction de cette interface est d'envoyer un message interactif via la plateforme FMZ vers le disque dur d'un ID, donc les paramètresargsIl contient l'ID du disque et le message, et l'exemple de l'url de demande ci-dessus est l'id pour le disque.186515Le programme de disque virtuel envoie des messagesok12345

Auparavant, c'était la façon de demander à FMZ d'étendre l'API de l'interface CommandRobot, le message ne pouvait être écrit que comme dans l'exemple ci-dessus.ok12345Si le message est dans le corps demandé, il faut utiliser une autre méthode:

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

Cette requête permet d'envoyer le contenu du corps de la requête à l'ID comme un message interactif via la plateforme FMZ.130350Les messages sur TradingView sont définis comme suit:{"close": {{close}}, "name": "aaa"}Donc, l'ID est130350Le disque virtuel reçoit une instruction d'interaction:{"close": 39773.75, "name": "aaa"}

Afin que la stratégie d'exécution de signaux TradingView puisse comprendre correctement les instructions envoyées par TradingView lors de la réception de l'instruction d'interaction, un format de 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-échanges, de sorte que vous pouvez configurer plusieurs objets d'échange sur cette stratégie, ce qui signifie que vous pouvez contrôler les opérations de sous-traitance de plusieurs comptes différents. Si vous utilisez uniquement l'échange spécifié par Exchange dans la structure des signaux, la configuration 1 consiste à faire fonctionner ce signal sur le compte correspondant au premier objet d'échange ajouté. Si vous utilisez la configuration ContractType pour le moment, les contrats à terme sont écrits sur des contrats spécifiques, tels que les contrats de perpétuité.

La première étape consiste à concevoir le code de stratégie, le code complet de la stratégie:

//信号结构
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 stratégiques et interactions:

img

La stratégie complète d'exécution du signal TradingView est ici:https://www.fmz.com/strategy/392048

Un test simple

Pour configurer l'objet de l'échange avant l'exécution de la stratégie, dans les paramètres de la politique, définissez correctement "AccessKey de la plate-forme FMZ" et "SecretKey de la plate-forme FMZ", les deux paramètres, ne pas les configurer mal.

img

Il est imprimé en suivant les étapes suivantes: l'adresse WebHook à remplir dans TradingView, les instructions d'action prises en charge, le format de message; l'adresse WebHook est importante:

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

Il est possible de copier et coller directement la position correspondante dans TradingView.

Pour simuler l'envoi de signaux dans TradingView, vous pouvez cliquer sur le bouton TestSignal sur l'interaction de stratégie:

img

Cette stratégie envoie elle-même une requête (simulation de la demande de signal de TradingView) qui appelle l'API d'extension de FMZ et envoie 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 reçoit un autre message d'interaction et exécute:

img

Je suis un homme qui a fait des efforts et qui a fait des affaires.

Test de TradingView dans des scénarios réels

L'utilisation de TradingView est au niveau Pro, il y a quelques connaissances préalables qui doivent être expliquées en quelques mots avant le test.

Prenons l'exemple d'un simple script PINE (qui a été modifié par hasard dans 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)

Les scripts PINE peuvent contenir des informations lors de l'émission des instructions suivantes.

Voici les symboles que j'ai écrits dans la case "message" de l'alerte, par exemple:{{strategy.order.contracts}}Une fois que l'ordre est déclenché, un message est envoyé (selon les paramètres d'alarme, de boost, de requête d'url, de fenêtre pop-up, etc.) qui indique le nombre d'exécutions de l'ordre.

{{strategy.position_size}}- Retourne la valeur du même mot-clé dans Pine, c'est-à-dire la taille de la position actuelle.{{strategy.order.action}}- Retourne la chaîne pour l'ordre exécuté avec la touche buy ou la touche sell.{{strategy.order.contracts}}- Retourner le nombre de contrats qui ont été exécutés.{{strategy.order.price}}- Retour au prix de l'exécution de la commande.{{strategy.order.id}}- Retourne l'ID de l'ordre exécuté ((utilisé comme la première chaîne de paramètres dans l'une des appels de la fonction qui génère l'ordre: stratégie.entry, stratégie.exit ou stratégie.order) ).{{strategy.order.comment}}- Retourne les annotations d'une commande qui a été exécutée (la chaîne utilisée dans le paramètre comment dans l'une des appels de la commande générée:strategy.entry,strategy.exit、 ou stratégie.order) 、 si aucune annotation n'est spécifiée, la valeur de stratégie.order.id est utilisée ‖{{strategy.order.alert_message}}- Renvoie la valeur de l'option alert_message, qui peut être utilisée dans le code Pine de la stratégie lors de l'appel d'une des fonctions utilisées pour commander:strategy.exitCette fonctionnalité n'est prise en charge que dans Pine v4.{{strategy.market_position}}- Retourner la stratégie de la position actuelle sous la forme d'une chaîne de caractères: Longue chaîne, Flat chaîne ou courte chaîne.{{strategy.market_position_size}}- Retourne la taille de la position actuelle sous forme de valeur absolue (c'est-à-dire non négative).{{strategy.prev_market_position}}- Retourner la dernière position de la stratégie sous la forme d'une chaîne: chapeau long, chapeau plat, ou chapeau court.{{strategy.prev_market_position_size}}- Retourne la taille de la position précédente sous forme de valeur absolue (c'est-à-dire non négative).

2° Construire des messages avec " stratégie d'exécution du signal TradingView "

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

3 ̊ Pour que TradingView émette des signaux lorsque ce script PINE est exécuté, il est nécessaire de mettre en place une alarme lorsque ce script est chargé dans TradingView

img

Lorsque le script PINE de TradingView déclenche une action de transaction, une demande d'url webhook est envoyée.

img

img

Le disque de FMZ exécute ce signal.

img

img

Adresse vidéo

Vidéo de la citrouille:https://www.ixigua.com/7172134169580372513?utm_source=xiguastudioLa station B:https://www.bilibili.com/video/BV1BY411d7c6/Je sais:https://www.zhihu.com/zvideo/1581722694294487040

Le code de l'article est à titre de référence et peut être modifié ou étendu.


Relationnée

Plus de

wbe3- petit déjeuner de crêpesMonk, comment faire pour faire fonctionner l'environnement d'analogie?

le guohwaUne question s'il vous plaît, est-ce que les messages d'alerte de Tradingview peuvent contenir les messages de la dernière commande? Je veux savoir si mon dernier ordre est un profit ou un perte, et si mon dernier ordre est un perte, le robot n'exécute pas l'opération de retrait jusqu'à ce que mon dernier ordre soit un profit. Pourriez-vous le faire s'il vous plaît?

13811047519/upload/asset/2a5a9fa2b97561c42c027.jpg S'il vous plaît, mon Dieu, que signifie cette erreur et comment l'éliminer?

Une situation agréableMon grand-père, j'ai ajouté 6 à 7 comptes pour effectuer des transactions avec ce signal, mais c'est assez grand pour l'instant, le signal d'un compte d'échange est terminé avant que le signal du prochain compte de transaction ne soit terminé, c'est une exécution en série, est-il possible d'exécuter des signaux de transaction en parallèle?

wbe3- petit déjeuner de crêpesDans la politique de réception du signal, il semble qu'il n'y ait pas de revenus d'impression et que la publication ne sera pas générée, alors veuillez ajouter un modèle de formulaire d'information de compte associé pour voir comment la politique fonctionne.

Le petit rêveIl s'agit d'un aperçu de la politique publique, ajouté automatiquement à la page.

wbe3- petit déjeuner de crêpesMerci mon frère, j'ai bien testé, mais il n'y a pas de stratégie de notation après la transaction.

Le petit rêveL'interface OKX, qui permet de passer à l'environnement de test de disque d'analogie OKX à l'aide de l'exchange.IO (pour "simuler", true), peut être utilisée pour passer à l'environnement de disque d'analogie.

le guohwaMerci pour votre réponse, j'ai deux questions à vous poser: 1, ce que je ne comprends pas un peu c'est que fmz peut écrire son propre script pine, pourquoi ce texte envoie-t-il des alertes à fmz via TradingView pour être traité et ensuite échangé? J'ai trouvé une stratégie qui est très bonne en soi, mais sans le droit d'utilisation du code source, je voulais éviter les erreurs par la méthode que j'ai mentionnée ci-dessus, vous avez dit d'augmenter le prix dans le message de push, je l'ai ajouté, mais ce push semble être le prix au moment de la commande, je ne comprends pas comment faire pour juger si c'est un profit ou un perte dans fmz.

Le petit rêveIl devrait être possible de pousser le contenu {{strategy.order.price}} au moment de la publication du message, puis la stratégie sur FMZ traite cette information et décide de passer une commande en fonction de la comparaison des prix actuels.

Le petit rêveLe test est-il normal maintenant?

Une situation agréableC'est très bien.

Le petit rêveFMZ a ajouté une fonctionnalité de parallélisation, qui devrait être convertible en parallélisation, mais le code de la stratégie pourrait être modifié de manière plus importante.