La logique du trading à terme de crypto-monnaie

Auteur:La bonté, Créé: 2020-07-18 13:31:34, Mis à jour: 2023-10-26 20:05:28

img

Scène problématique

Depuis longtemps, le problème de retard de données de l'interface API de l'échange de crypto-monnaie m'a toujours troublé. Je n'ai pas trouvé de moyen approprié de le gérer. Je vais reproduire la scène de ce problème.

Habituellement, l'ordre de marché fourni par l'échange de contrats est en fait le prix de la contrepartie, donc parfois le soi-disant ordre de marché est quelque peu peu peu peu fiable. Par conséquent, lorsque nous écrivons des stratégies de trading à terme de crypto-monnaie, la plupart d'entre elles utilisent des ordres à limite. Après chaque ordre, nous devons vérifier la position pour voir si l'ordre est rempli et la position correspondante est maintenue.

Le problème réside dans cette information de position. Si l'ordre est fermé, les données renvoyées par l'interface d'information de position de change (c'est-à-dire l'interface d'échange que la couche inférieure accède réellement lorsque nous appelonsexchange.GetPositionSi les données renvoyées par l'échange sont des données anciennes, c'est-à-dire les informations de position de l'ordre qui vient d'être passé avant la fin de la transaction, cela pose problème.

La logique de négociation peut considérer que l'ordre n'a pas été rempli et continuer à passer l'ordre. Cependant, l'interface de placement des ordres de l'échange n'est pas retardée, mais la transaction est rapide et l'ordre est exécuté.

Une expérience réelle

En raison de ce problème, j'ai vu une stratégie pour remplir une position longue folle, heureusement, le marché était en hausse à ce moment-là, et le bénéfice flottant dépassait une fois 10BTC. Heureusement, le marché a grimpé en flèche. Si c'est une chute, la fin peut être imaginée.

Essayez de résoudre

  • Le plan 1

Il est possible de concevoir la logique de placement des ordres pour que la stratégie ne place qu'un seul ordre. Le prix de placement des ordres est un grand glissement pour l'écart de prix du prix de l'adversaire à ce moment-là, et une certaine profondeur d'ordres de l'adversaire peut être exécutée. L'avantage de cela est que seul un ordre est placé, et il n'est pas jugé sur la base des informations de position. Cela peut éviter le problème de placement répété des ordres, mais parfois, lorsque le prix change relativement grand, l'ordre déclenchera le mécanisme de limite de prix de l'échange, et cela peut conduire à ce que le grand ordre de glissement ne soit toujours pas terminé, et a manqué l'opportunité de trading.

  • Le plan 2

En utilisant la fonction prix de marché de la bourse, le prix de passage -1 sur la FMZ est le prix de marché.

  • Le plan 3

Nous utilisons toujours la logique de négociation précédente et plaçons un ordre limite, mais nous ajoutons une certaine détection à la logique de négociation pour essayer de résoudre le problème causé par le retard des données de position. Une fois l'ordre placé, si l'ordre n'est pas annulé, il disparaît directement dans la liste des ordres en attente (la liste des ordres en attente disparaît de deux manières possibles: 1 retrait d'ordre, 2 exécutés), détectez une telle situation et placez à nouveau le montant de l'ordre. Le montant du dernier ordre est le même. À ce moment-là, il est nécessaire de faire attention à savoir si les données de position sont retardées. Laissez le programme entrer dans la logique d'attente pour récupérer les informations de position. Vous pouvez même continuer à optimiser et augmenter le nombre d'attente de déclenchement. Si elle dépasse un certain nombre de fois, les données d'interface de position sont retardées. Le problème est grave, laissez la logique de transaction se terminer.

Conception basée sur le plan 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 du modèle:https://www.fmz.com/strategy/203258

La façon d'appeler l'interface du modèle est exactement comme$.OpenLonget$.CoverLongdans lemainfonctionne au-dessus.

Le modèle est une version bêta, toutes suggestions sont les bienvenues, je vais continuer à optimiser pour faire face au problème des retards dans les données de position.


Relationnée

Plus de