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

Auteur:Je ne sais pas., Créé: 2022-11-30 17:12:20, Mis à jour: 2023-09-11 19:59:55

img

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

Scénario de problème

Pendant longtemps, le problème du délai de données de l'interface API de position de l'échange de monnaie numérique m'a toujours dérangé. Je n'ai pas trouvé de moyen approprié de traiter ce problème. Laissez-moi reproduire la scène du problème. Habituellement, l'ordre de prix de marché fourni par l'échange de contrat est en fait le prix de contrepartie, il n'est donc parfois pas fiable d'utiliser ce soi-disant ordre de prix de marché. Cependant, lorsque nous écrivons des stratégies de négociation de contrats à terme de monnaie numérique, nous utilisons le plus souvent l'ordre limite. Après chaque ordre passé, nous devons vérifier la position pour voir si l'ordre est fermé et la position correspondante est maintenue. Le problème réside dans cette position d'information. Si l'ordre est fermé, les données retournées par l'interface d'information de position de l'échange (c'est-à-dire l'interface réellement accessible par la couche inférieure lorsque nous remplons l'échange.

Expérience pratique

En raison de ce problème, j'ai vu une stratégie ouverte pleine de positions longues follement. Heureusement, le marché a grimpé en flèche à ce moment-là, et le bénéfice flottant a dépassé 10BTC. Heureusement, le marché a fortement augmenté. Si elle a fortement diminué, nous pouvons imaginer le résultat.

Les solutions

  • Solution 1 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 de négociation actuel plus un prix glissant important, afin de prendre une certaine profondeur de l'ordre de l'adversaire. L'avantage de cela est que seul un ordre sera placé, et il ne sera pas jugé en fonction des informations de position. Cela peut éviter le problème des ordres répétés, mais parfois, le placement d'un ordre peut déclencher le mécanisme de limite de prix de l'échange lorsque le changement de prix est relativement important, et il est possible d'augmenter le prix glissant et de ne pas réussir à conclure une transaction, manquant ainsi l'opportunité.

  • Résolution 2 Avec la fonction d'ordre de prix de marché de la bourse, le prix est transféré à - 1 sur la FMZ en tant qu'ordre de prix de marché.

  • Résolution 3 Nous utilisons toujours la logique de négociation précédente et passons des ordres avec des ordres de limite de prix, mais nous ajoutons une certaine détection dans la logique de négociation pour essayer de résoudre le problème causé par le retard des données de position. Vérifiez si l'ordre a disparu directement de la liste des ordres en attente sans annulation (il existe deux possibilités de disparition de la liste des ordres en attente: 1. annulation et 2. rempli). Si une telle situation est détectée, et la quantité de l'ordre placé à nouveau est la même que celle de la dernière commande, il est important de noter que 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, ou même continuer à optimiser et augmenter le nombre de fois d'attente pour déclencher les temps d'attente, si elle dépasse un certain nombre de fois, cela indique que le retard des données d'interface de position est grave, ce qui provoque la fin de la logique de négociation.

Conception basée sur la solution 3

// Parameters
/*
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);
            }
            // Check directBreak and the position remains unchanged
            if (preNeedOpen == needOpen && directBreak) {
                Log("Suspected position data is delayed, wait for 30 seconds", "#FF0000")
                Sleep(30000)
                nowPosition = GetPosition(e, contractType, direction);
                if (nowPosition) {
                    needOpen = opAmount - (nowPosition.Amount - initAmount);
                }
                /*
                timeoutCount++
                if (timeoutCount > 10) {
                    Log("Suspected position is delayed for 10 consecutive times, and the order is failed!", "#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 positions", contractType, ticker);
        } else {
            orderId = e.Sell(ticker.Buy - SlidePrice, amount, "open short positions", 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 positions", contractType, ticker);
        } else if (position.Type == PD_SHORT) {
            e.SetDirection("closesell");
            e.Buy(ticker.Sell + SlidePrice, amount, "close short positions", 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

L'interface du modèle est appelée de la même manière que$.OpenLong, $.CoverLongdans la fonction principale ci-dessus. Le modèle est une version bêta, et vous êtes les bienvenus pour faire des suggestions. Nous allons continuer à l'optimiser afin que nous puissions gérer le problème de retard de données de position.


Relationnée

Plus de