Type/to search
8
Follow
1369
Followers
Quelques réflexions sur la logique du trading à terme de devises numériques
Discussions
Created 2020-06-01 09:52:45  Updated 2023-10-08 19:41:25
 6
 2787

img

Quelques réflexions sur la logique du trading à terme de devises numériques

Scénario du problème

Pendant longtemps, le problème de retard des données de l'interface API de position de change de devises numériques m'a toujours dérangé. Je n'ai pas encore trouvé de solution appropriée, je vais donc reproduire ce problème. Habituellement, l'ordre de marché fourni par la bourse de contrats est en fait le prix de la contrepartie, donc parfois l'utilisation de ce soi-disant « ordre de marché » n'est pas fiable. Par conséquent, lorsque nous rédigeons des stratégies de trading de contrats à terme sur devises numériques, la plupart d’entre nous utilisons des ordres limités. Après avoir passé chaque commande, nous devons vérifier la position pour voir si la commande a été exécutée et si la position correspondante est maintenue. Le problème réside dans ces informations de position. Si l'ordre est exécuté, les données renvoyées par l'interface d'informations de position de la bourse (c'est-à-dire l'interface de bourse à laquelle nous avons effectivement accédé lorsque nous appelons exchange.GetPosition) doivent inclure les informations de position nouvellement ouvertes. Cependant, si les données renvoyées par l'échange sont des données anciennes, c'est-à-dire les informations de position avant que l'ordre qui vient d'être passé ne soit exécuté, alors il y aura un problème. La logique de trading peut penser que l’ordre n’a pas été exécuté et continuer à passer l’ordre. Cependant, l'interface de passation des commandes de la bourse ne connaît aucun retard. Au contraire, les transactions sont réalisées très rapidement et les commandes sont exécutées dès qu'elles sont passées. Cela entraînera une conséquence grave : la stratégie placera des ordres à plusieurs reprises lorsqu'une opération d'ouverture est déclenchée.

Expérience réelle

À cause de ce problème, j'ai vu une stratégie qui ouvrait une position longue complètement folle. Heureusement, le marché était en plein essor à ce moment-là et le profit flottant a dépassé une fois les 10 BTC. Heureusement, le marché est en plein boom, s’il avait chuté, le résultat aurait été prévisible.

Essayez de résoudre

  • Solution 1
    La logique d'ordre de la stratégie peut être conçue pour ne placer qu'un seul ordre, et le prix de l'ordre est le prix de l'adversaire à ce moment-là plus un glissement plus important, de manière à prendre des ordres de l'adversaire d'une certaine profondeur. L’avantage de cette méthode est que vous ne passez qu’une seule commande et qu’elle n’est pas basée sur des informations de position. Cela peut éviter le problème des commandes en double, mais parfois, passer une commande lorsque le prix change considérablement peut déclencher le mécanisme de limite de prix de la bourse, et il est possible que même avec un glissement important, la commande ne soit toujours pas exécutée, manquant ainsi l'opportunité .

  • Solution 2
    Utilisez la fonction d'ordre de marché de la bourse et transmettez -1 sur FMZ comme prix, ce qui correspond à un ordre de marché. Actuellement, l'interface des contrats à terme OKEX a été mise à niveau pour prendre en charge les ordres de marché réels.

  • Solution 3
    Nous utilisons toujours la logique de trading précédente et passons des commandes à l'aide d'ordres limités, mais nous ajoutons quelques détections à la logique de trading pour tenter de résoudre le problème causé par le retard des données de position. Vérifiez si la commande disparaît de la liste des commandes en attente sans être annulée après avoir passé la commande (il existe deux possibilités de disparition de la liste des commandes en attente : 1 annulation et 2 exécution). Si une telle situation est détectée et que la quantité de commande et la commande le volume est le même que la dernière fois. À ce stade, vous devez faire attention à ce que les données de position ne soient pas retardées. Laissez le programme entrer dans la logique d'attente pour réacquérir les informations de position. Vous pouvez même continuer à optimiser et à augmenter le nombre de déclenchement des attentes. Si ce nombre dépasse un certain nombre, cela signifie que les données de l'interface de position sont retardées. Le problème est grave et la logique de cette transaction est terminée.

Conception basée sur le schéma 3

javascript
// 参数 /* var MinAmount = 1 var SlidePrice = 5 var Interval = 500 */ function GetPosition(e, contractType, direction) { e.SetContractType(contractType) var positions = _C(e.GetPosition); for (var i = 0; i < positions.length; i++) { if (positions[i].ContractType == contractType && positions[i].Type == direction) { return positions[i] } } return null } function Open(e, contractType, direction, opAmount) { var initPosition = GetPosition(e, contractType, direction); var isFirst = true; var initAmount = initPosition ? initPosition.Amount : 0; var nowPosition = initPosition; var directBreak = false var preNeedOpen = 0 var timeoutCount = 0 while (true) { var ticker = _C(e.GetTicker) var needOpen = opAmount; if (isFirst) { isFirst = false; } else { nowPosition = GetPosition(e, contractType, direction); if (nowPosition) { needOpen = opAmount - (nowPosition.Amount - initAmount); } // 检测directBreak 并且持仓未变的情况 if (preNeedOpen == needOpen && directBreak) { Log("疑似仓位数据延迟,等待30秒", "#FF0000") Sleep(30000) nowPosition = GetPosition(e, contractType, direction); if (nowPosition) { needOpen = opAmount - (nowPosition.Amount - initAmount); } /* timeoutCount++ if (timeoutCount > 10) { Log("连续10次疑似仓位延迟,下单失败!", "#FF0000") break } */ } else { timeoutCount = 0 } } if (needOpen < MinAmount) { break; } var amount = needOpen; preNeedOpen = needOpen e.SetDirection(direction == PD_LONG ? "buy" : "sell"); var orderId; if (direction == PD_LONG) { orderId = e.Buy(ticker.Sell + SlidePrice, amount, "开多仓", contractType, ticker); } else { orderId = e.Sell(ticker.Buy - SlidePrice, amount, "开空仓", contractType, ticker); } directBreak = false var n = 0 while (true) { Sleep(Interval); var orders = _C(e.GetOrders); if (orders.length == 0) { if (n == 0) { directBreak = true } break; } for (var j = 0; j < orders.length; j++) { e.CancelOrder(orders[j].Id); if (j < (orders.length - 1)) { Sleep(Interval); } } n++ } } var ret = { price: 0, amount: 0, position: nowPosition }; if (!nowPosition) { return ret; } if (!initPosition) { ret.price = nowPosition.Price; ret.amount = nowPosition.Amount; } else { ret.amount = nowPosition.Amount - initPosition.Amount; ret.price = _N(((nowPosition.Price * nowPosition.Amount) - (initPosition.Price * initPosition.Amount)) / ret.amount); } return ret; } function Cover(e, contractType, opAmount, direction) { var initPosition = null; var position = null; var isFirst = true; while (true) { while (true) { Sleep(Interval); var orders = _C(e.GetOrders); if (orders.length == 0) { break; } for (var j = 0; j < orders.length; j++) { e.CancelOrder(orders[j].Id); if (j < (orders.length - 1)) { Sleep(Interval); } } } position = GetPosition(e, contractType, direction) if (!position) { break } if (isFirst == true) { initPosition = position; opAmount = Math.min(opAmount, initPosition.Amount) isFirst = false; } var amount = opAmount - (initPosition.Amount - position.Amount) if (amount <= 0) { break } var ticker = _C(exchange.GetTicker) if (position.Type == PD_LONG) { e.SetDirection("closebuy"); e.Sell(ticker.Buy - SlidePrice, amount, "平多仓", contractType, ticker); } else if (position.Type == PD_SHORT) { e.SetDirection("closesell"); e.Buy(ticker.Sell + SlidePrice, amount, "平空仓", contractType, ticker); } Sleep(Interval) } return position } $.OpenLong = function(e, contractType, amount) { if (typeof(e) == "string") { amount = contractType contractType = e e = exchange } return Open(e, contractType, PD_LONG, amount); } $.OpenShort = function(e, contractType, amount) { if (typeof(e) == "string") { amount = contractType contractType = e e = exchange } return Open(e, contractType, PD_SHORT, amount); }; $.CoverLong = function(e, contractType, amount) { if (typeof(e) == "string") { amount = contractType contractType = e e = exchange } return Cover(e, contractType, amount, PD_LONG); }; $.CoverShort = function(e, contractType, amount) { if (typeof(e) == "string") { amount = contractType contractType = e e = exchange } return Cover(e, contractType, amount, PD_SHORT); }; function main() { Log(exchange.GetPosition()) var info = $.OpenLong(exchange, "quarter", 100) Log(info, "#FF0000") Log(exchange.GetPosition()) info = $.CoverLong(exchange, "quarter", 30) Log(exchange.GetPosition()) Log(info, "#FF0000") info = $.CoverLong(exchange, "quarter", 80) Log(exchange.GetPosition()) Log(info, "#FF0000") }

Adresse du modèle : https://www.fmz.com/strategy/203258

La manière d'appeler l'interface du modèle est la même que dans la fonction principale ci-dessus$.OpenLong$.CoverLong
Le modèle est une version bêta. Les suggestions et commentaires sont les bienvenus. Nous continuerons à l'optimiser pour résoudre le problème de retard des données de position.

Related Recommendations
Comment
All comments (6)

    最好的办法应该是用ws,订单一有更新让服务器马上通知你,而不是一次次去询问

    6 years ago

    然鹅,WS接口也有不靠谱的时候,惹气麻烦比REST协议更甚。。

    6 years ago

    ws基本不会出问题,就算关键时候断线了,建立好重连机制也不会有问题;当然也有完全的解决方法,这你得自己摸索了

    6 years ago

    遇到过不稳定的WS ,各种故障。各有利弊吧。

    6 years ago

    碰到过这个问题,我的解决办法是记录一下下单前的持仓,然后下ioc订单后记录id,循环获取订单状态,如果是成交/部分成交,循环对比当前持仓和记录的持仓,当这两个值不同就跳出循环。

    6 years ago

    好的感谢, 学习下。

    6 years ago
  • 1
Forums
PINE Language
Get the app
iPhone Download
© 2015 - ∞ INVENTOR PTE LTD (SG)