Alguns pensamentos sobre a lógica da negociação de futuros de moeda digital

Autora:Lydia., Criado: 2022-11-30 17:12:20, Atualizado: 2023-09-11 19:59:55

img

Alguns pensamentos sobre a lógica da negociação de futuros de moeda digital

Scenário de problema

Durante muito tempo, o problema de atraso de dados da interface API de posição da troca de moeda digital sempre me incomodou. Eu não encontrei uma maneira adequada de lidar com esse problema. Deixe-me reproduzir a cena do problema. Normalmente, a ordem de preço de mercado fornecida pela troca de contratos é na verdade o preço da contraparte, então às vezes não é confiável usar essa chamada ordem de preço de mercado. No entanto, quando escrevemos estratégias de negociação de futuros de moeda digital, usamos a ordem de limite mais. Depois de cada ordem ser colocada, precisamos verificar a posição para ver se a ordem é fechada e a posição correspondente é mantida. O problema está nesta posição de informação. Se a ordem for fechada, a ordem de dados retornada pela interface de informações de posição da troca (isto é, a interface realmente acessada pela camada inferior quando preenchemos a troca.GetPosition) deve incluir a nova informação de posição. No entanto, se a chamada de informação pela posição antiga for feita, uma vez que a troca for concluída, a ordem não será devolvida rapidamente. Quando a

Experiência prática

Por causa deste problema, eu vi uma estratégia aberta cheia de posições longas de forma louca. Felizmente, o mercado subiu naquele momento, e o lucro flutuante excedeu 10BTC. Felizmente, o mercado aumentou acentuadamente. Se ele caiu acentuadamente, podemos imaginar o resultado.

Soluções

  • Solução 1 A estratégia pode ser projetada para colocar apenas uma ordem, e o preço da ordem é o preço do oponente comercial atual mais um grande preço deslizante, de modo a tomar uma certa profundidade da ordem do oponente. A vantagem disso é que apenas uma ordem será colocada e não será julgada com base nas informações da posição. Isso pode evitar o problema de ordens repetidas, mas às vezes colocar uma ordem pode acionar o mecanismo de limite de preço da bolsa quando a mudança de preço é relativamente grande, e é possível aumentar o preço deslizante e ainda não conseguir fazer um acordo, perdendo assim a oportunidade.

  • Solução 2 Com a função de ordem de preço de mercado da bolsa, o preço é transferido para - 1 na FMZ como a ordem de preço de mercado.

  • Solução 3 Ainda usamos a lógica de negociação anterior e colocamos ordens com ordens de limite de preço, mas adicionamos alguma detecção na lógica de negociação para tentar resolver o problema causado pelo atraso dos dados de posição. Verifique se a ordem desapareceu diretamente da lista de ordens pendentes sem cancelamento (existem duas possibilidades de desaparecimento da lista de ordens pendentes: 1. Cancelamento e 2. Preenchido). Se tal situação for detectada, e a quantidade da ordem colocada novamente é a mesma da última ordem, é importante notar se os dados de posição são atrasados. Deixe o programa entrar na lógica de espera para recuperar as informações de posição, ou mesmo continuar a otimizar e aumentar o número de vezes de espera para desencadear os tempos de espera, se exceder um certo número de vezes, isso indica que o atraso da interface de dados de posição é grave, o que faz com que a lógica de negociação termine.

Projeto baseado na Solução 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")
}

Endereço do modelo:https://www.fmz.com/strategy/203258

A interface do modelo é chamada da mesma forma que$.OpenLong, $.CoverLongna função principal acima. O modelo é uma versão beta, e você é bem-vindo a fazer sugestões.


Relacionados

Mais.