Die Logik des Futures-Handels mit Kryptowährungen

Schriftsteller:Gutes, Erstellt: 2020-07-18 13:31:34, Aktualisiert: 2023-10-26 20:05:28

img

Problemstelle

Seit langer Zeit beunruhigt mich das Datenverzögerungsproblem der API-Schnittstelle der Kryptowährungsbörse. Ich habe keinen geeigneten Weg gefunden, damit umzugehen. Ich werde die Szene dieses Problems reproduzieren.

Normalerweise ist der Marktbefehl, der von der Kontraktbörse bereitgestellt wird, tatsächlich der Gegenparteipreis, so dass manchmal die sogenannte Marktbefehl etwas unzuverlässig ist. Daher verwenden die meisten von ihnen, wenn wir Kryptowährungs-Futures-Handelsstrategien schreiben, Limit-Orders. Nach jeder Bestellung müssen wir die Position überprüfen, um zu sehen, ob die Bestellung ausgefüllt ist und die entsprechende Position gehalten wird.

Das Problem liegt in dieser Position Informationen. Wenn die Bestellung geschlossen wird, die Daten zurückgegeben von der Exchange Position Information Interface (dh die Exchange-Schnittstelle, die die untere Schicht tatsächlich zugreift, wenn wir anrufenexchange.GetPosition) sollte die Informationen der neu eröffneten Position enthalten, aber wenn die von der Börse zurückgegebenen Daten alte Daten sind, d. h. die Positionsinformationen der Order, die gerade vor Abschluss der Transaktion platziert wurde, wird dies zu einem Problem führen.

Die Handelslogik kann davon ausgehen, dass die Bestellung nicht ausgeführt wurde und die Bestellung weiterhin platziert wird. Allerdings wird die Bestellplatzierungsoberfläche der Börse nicht verzögert, sondern die Transaktion ist schnell und die Bestellung wird ausgeführt. Dies führt zu einer schweren Konsequenz, dass die Strategie beim Auslösen der Operation der Eröffnung einer Position wiederholt Aufträge platziert.

Wirkliche Erfahrungen

Aufgrund dieses Problems habe ich eine Strategie gesehen, um eine lange Position verrückt zu füllen, glücklicherweise war der Markt zu dieser Zeit gestiegen, und der schwimmende Gewinn überschritt einmal 10BTC. Glücklicherweise ist der Markt explodiert. Wenn es ein Sturz ist, kann sich das Ende vorstellen.

Versuchen Sie, eine Lösung zu finden

  • Plan 1

Es ist möglich, die Logik der Auftragserteilung für die Strategie zu entwerfen, nur einen Auftrag zu erteilen. Der Auftragserteilungspreis ist eine große Verschiebung für die Preislücke des gegnerischen Preises zu diesem Zeitpunkt, und eine gewisse Tiefe der gegnerischen Aufträge kann ausgeführt werden. Der Vorteil ist, dass nur ein Auftrag erteilt wird, und es wird nicht auf der Grundlage von Positionsinformationen beurteilt. Dies kann das Problem der wiederholten Auftragserteilung vermeiden, aber manchmal, wenn der Preis relativ stark ändert, wird der Auftrag den Preislimitmechanismus der Börse auslösen, und es kann dazu führen, dass der große Schiebungsauftrag immer noch nicht abgeschlossen ist und die Handelsmöglichkeit verpasst wird.

  • Plan 2

Mit Hilfe der Marktpreis-Funktion der Börse ist der Preispass -1 auf der FMZ der Marktpreis. Derzeit wurde die OKEX-Futures-Schnittstelle aktualisiert, um realmarktpreis zu unterstützen.

  • Plan 3

Wir verwenden immer noch die vorherige Handelslogik und platzieren eine Limitorder, aber wir fügen der Handelslogik etwas Erkennung hinzu, um zu versuchen, das Problem zu lösen, das durch die Verzögerung der Positionsdaten verursacht wird. Nachdem der Auftrag platziert wurde, verschwindet er, wenn der Auftrag nicht storniert wird, direkt in der Liste der ausstehenden Aufträge (die Liste der ausstehenden Aufträge verschwindet auf zwei mögliche Arten: 1 Abholung der Bestellung, 2 ausgeführt), erkennt eine solche Situation und platziert den Auftragsbetrag erneut. Der Betrag der letzten Bestellung ist der gleiche. Zu diesem Zeitpunkt ist es notwendig, darauf zu achten, ob die Positionsdaten verzögert sind. Lassen Sie das Programm die Wartenlogik eingeben, um die Positionsinformationen wiederzuerlangen. Sie können sogar die Anzahl der Auslöschwartungen weiter optimieren und erhöhen. Wenn sie eine bestimmte Anzahl von Mal überschreitet, verzögert sich die Positionsinterface. Das Problem ist ernst, lassen Sie die

Entwurf auf der Grundlage des Plans 3

// Parameter
/*
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);
            }
            // Detect directBreak and the position has not changed
            if (preNeedOpen == needOpen && directBreak) {
                Log("Suspected position data is delayed, wait 30 seconds", "#FF0000")
                Sleep(30000)
                nowPosition = GetPosition(e, contractType, direction);
                if (nowPosition) {
                    needOpen = opAmount - (nowPosition.Amount - initAmount);
                }
                /*
                timeoutCount++
                if (timeoutCount > 10) {
                    Log("Suspected position delay for 10 consecutive times, placing order fails!", "#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, "Open long position", contractType, ticker);
        } else {
            orderId = e.Sell(ticker.Buy - SlidePrice, amount, "Open short position", 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, "Close long position", contractType, ticker);
        } else if (position.Type == PD_SHORT) {
            e.SetDirection("closesell");
            e.Buy(ticker.Sell + SlidePrice, amount, "Close short position", 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 der Vorlage:https://www.fmz.com/strategy/203258

Die Art und Weise, die Schablonenoberfläche aufzurufen, ist genau wie$.OpenLongund$.CoverLongin dermainFunktion oben.

Die Vorlage ist eine Beta-Version, jegliche Vorschläge sind willkommen, ich werde weiterhin optimieren, um mit dem Problem der Verzögerungen in Positionsdaten umzugehen.


Verwandt

Mehr