avatar of 发明者量化-小小梦 发明者量化-小小梦
focar em Mensagem privada
4
focar em
1271
Seguidores

Algumas reflexões sobre a lógica da negociação de futuros de moeda digital

Criado em: 2020-06-01 09:52:45, atualizado em: 2023-10-08 19:41:25
comments   6
hits   2406

Algumas reflexões sobre a lógica da negociação de futuros de moeda digital

Algumas reflexões sobre a lógica da negociação de futuros de moeda digital

Cenário do problema

Por muito tempo, o problema de atraso de dados da interface da API de posição de câmbio de moeda digital sempre me incomodou. Ainda não encontrei uma solução adequada, então reproduzirei este problema. Normalmente, a ordem de mercado fornecida pela bolsa de contratos é, na verdade, o preço da contraparte, portanto, às vezes, usar essa chamada “ordem de mercado” não é confiável. Portanto, quando escrevemos estratégias de negociação de futuros de moeda digital, a maioria de nós usa ordens limitadas. Depois de fazer cada ordem, precisamos verificar a posição para ver se a ordem foi executada e se a posição correspondente foi mantida. O problema está nessas informações de posição. Se a ordem for executada, os dados retornados pela interface de informações de posição de câmbio (ou seja, a interface de câmbio realmente acessada quando chamamos exchange.GetPosition) devem incluir as informações de posição recém-abertas. No entanto, se os dados retornados pela exchange são dados antigos, ou seja, as informações de posição antes da ordem recém-emitida ser executada, então haverá um problema. A lógica de negociação pode pensar que a ordem não foi executada e continuar a colocá-la. No entanto, a interface de colocação de ordens da exchange não sofre atrasos. Em vez disso, as transações são concluídas muito rapidamente, e as ordens são executadas assim que são colocadas. Isso resultará em uma consequência séria: a estratégia emitirá ordens repetidamente quando uma operação de abertura for acionada.

Experiência real

Por causa desse problema, vi uma estratégia que abriu uma posição longa cheia louca. Felizmente, o mercado estava crescendo naquela época e o lucro flutuante uma vez excedeu 10 BTC. Felizmente, o mercado está em alta. Se tivesse despencado, o resultado seria previsível.

Tentar resolver

  • Solução 1 A lógica de ordem da estratégia pode ser projetada para colocar apenas uma ordem, e o preço da ordem é o preço do oponente no momento mais um deslizamento maior, de modo a aceitar ordens do oponente de uma certa profundidade. A vantagem de fazer isso é que você só faz uma ordem e ela não é baseada em informações de posição. Isso pode evitar o problema de ordens duplicadas, mas às vezes, colocar uma ordem quando o preço muda muito pode acionar o mecanismo de limite de preço da bolsa, e é possível que mesmo com um grande deslizamento, a ordem ainda não seja executada, perdendo assim a oportunidade .

  • Solução 2 Use a função de ordem de mercado da bolsa e passe -1 em FMZ como o preço, que é uma ordem de mercado. Atualmente, a interface de futuros da OKEX foi atualizada para suportar ordens de mercado reais.

  • Solução 3 Ainda usamos a lógica de negociação anterior e colocamos ordens usando ordens limitadas, mas adicionamos algumas detecções à lógica de negociação para tentar resolver o problema causado pelo atraso nos dados de posição. Verifique se o pedido desaparece da lista de pedidos pendentes sem ser cancelado após a realização do pedido (há duas possibilidades de desaparecimento da lista de pedidos pendentes: 1 cancelamento e 2 cumprimento). Se tal situação for detectada e a quantidade do pedido e o pedido o volume é o mesmo da última vez. Neste momento, você deve prestar atenção se os dados de posição estão atrasados. Deixe o programa entrar na lógica de espera para readquirir as informações de posição. Você pode até continuar a otimizar e aumentar o número de esperas de disparo. Se exceder um certo número, significa que os dados da interface de posição estão atrasados. O problema é sério e a lógica desta transação é encerrada.

Projeto baseado no Esquema 3

// 参数
/*
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")
}

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

A maneira de chamar a interface do modelo é a mesma da função principal acima$.OpenLong$.CoverLong。 O modelo é uma versão beta. Sugestões e comentários são bem-vindos. Continuaremos a otimizá-lo para resolver o problema de atraso de dados de posição.