Type/to search
8
Follow
1369
Followers
Einige Gedanken zur Logik des digitalen Währungsterminhandels
Discussions
Created 2020-06-01 09:52:45  Updated 2023-10-08 19:41:25
 6
 2787

img

Einige Gedanken zur Logik des digitalen Währungsterminhandels

Problemszenario

Das Problem der Datenverzögerung der API-Schnittstelle für digitale Währungsumtauschpositionen hat mich lange Zeit gestört. Da ich noch keine passende Lösung gefunden habe, werde ich das Problem reproduzieren. Normalerweise entspricht die von der Vertragsbörse bereitgestellte Marktorder tatsächlich dem Preis der Gegenpartei. Daher ist die Verwendung dieser sogenannten „Marktorder“ manchmal unzuverlässig. Daher verwenden die meisten von uns beim Schreiben von Handelsstrategien für digitale Währungs-Futures Limit-Orders. Nach der Platzierung jeder Bestellung müssen wir die Position überprüfen, um festzustellen, ob die Bestellung ausgeführt wurde und ob die entsprechende Position gehalten wird. Das Problem liegt in diesen Positionsinformationen. Wenn der Auftrag ausgeführt wird, sollten die von der Börsenschnittstelle für Positionsinformationen zurückgegebenen Daten (d. h. die Börsenschnittstelle, auf die tatsächlich zugegriffen wird, wenn wir exchange.GetPosition aufrufen) die neu eröffneten Positionsinformationen enthalten. Wenn jedoch Wenn es sich bei den von der Börse zurückgegebenen Daten um alte Daten handelt, also um die Positionsinformationen vor der Ausführung der gerade erteilten Bestellung, liegt ein Problem vor. Die Handelslogik könnte davon ausgehen, dass die Order nicht ausgeführt wurde und weiterhin mit der Platzierung der Order fortfahren. An der Schnittstelle zur Auftragserteilung der Börse kommt es jedoch zu keinen Verzögerungen. Stattdessen werden Transaktionen sehr schnell abgeschlossen und Aufträge ausgeführt, sobald sie erteilt werden. Dies wird die schwerwiegende Konsequenz haben, dass die Strategie wiederholt Aufträge erteilt, wenn eine Eröffnungsoperation ausgelöst wird.

Tatsächliche Erfahrung

Aufgrund dieses Problems habe ich eine Strategie gesehen, die eine verrückte volle Long-Position eröffnete. Glücklicherweise boomte der Markt zu dieser Zeit und der schwebende Gewinn überstieg einmal 10 BTC. Glücklicherweise boomt der Markt. Wäre er eingebrochen, wäre das Ergebnis vorhersehbar gewesen.

Versuchen Sie zu lösen

  • Lösung 1
    Die Auftragslogik der Strategie kann so gestaltet werden, dass nur eine Bestellung aufgegeben wird und der Auftragspreis dem aktuellen Preis des Gegners zuzüglich eines größeren Slippages entspricht, um Aufträge des Gegners mit einer bestimmten Tiefe anzunehmen. Der Vorteil hierbei ist, dass Sie nur eine Bestellung aufgeben und diese nicht auf Positionsdaten basiert. Dadurch kann das Problem doppelter Aufträge vermieden werden. Manchmal kann jedoch die Platzierung eines Auftrags bei starken Preisschwankungen den Preislimitmechanismus der Börse auslösen, und es ist möglich, dass der Auftrag selbst bei einem großen Slippage nicht ausgeführt wird und somit die Gelegenheit verpasst wird. .

  • Lösung 2
    Verwenden Sie die Market-Order-Funktion der Börse und geben Sie -1 an FMZ als Preis weiter, was eine Market-Order ist. Derzeit wurde die OKEX-Futures-Schnittstelle aktualisiert, um echte Market-Orders zu unterstützen.

  • Lösung 3
    Wir verwenden noch immer die vorherige Handelslogik und platzieren Aufträge mithilfe von Limit-Orders, aber wir fügen der Handelslogik einige Erkennungen hinzu, um zu versuchen, das durch die Verzögerung der Positionsdaten verursachte Problem zu lösen. Überprüfen Sie, ob die Bestellung aus der Liste der ausstehenden Bestellungen verschwindet, ohne dass sie nach der Bestellung storniert wird (es gibt zwei Möglichkeiten für das Verschwinden aus der Liste der ausstehenden Bestellungen: 1 Stornierung und 2 Erfüllung). Wenn eine solche Situation erkannt wird und die Bestellmenge und die Bestellung Das Volumen ist das gleiche wie beim letzten Mal. Zu diesem Zeitpunkt sollten Sie darauf achten, ob die Positionsdaten verzögert sind. Lassen Sie das Programm in die Wartelogik wechseln und die Positionsinformationen erneut abrufen. Sie können die Anzahl sogar weiter optimieren und erhöhen von Auslösewartezeiten. Wenn sie eine bestimmte Zahl überschreitet, bedeutet dies, dass die Positionsschnittstellendaten verzögert sind. Das Problem ist schwerwiegend und die Logik dieser Transaktion wird beendet.

Entwurf nach Schema 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") }

Vorlagenadresse: https://www.fmz.com/strategy/203258

Die Vorgehensweise zum Aufrufen der Vorlagenschnittstelle ist die gleiche wie in der Hauptfunktion oben.$.OpenLong$.CoverLong
Die Vorlage ist eine Beta-Version. Vorschläge und Kommentare sind willkommen. Wir werden sie weiter optimieren, um das Problem der Positionsdatenverzögerung zu lösen.

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)