Type/to search
8
Follow
1369
Followers
Algumas reflexões sobre a lógica da negociação de futuros de moeda digital
Discussions
Created 2020-06-01 09:52:45  Updated 2023-10-08 19:41:25
 6
 2787

img

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

javascript
// 参数 /* 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.

Related Recommendations
Comment
All comments (6)

    最好的办法应该是用ws,订单一有更新让服务器马上通知你,而不是一次次去询问

    6 years ago

    然鹅,WS接口也有不靠谱的时候,惹气麻烦比REST协议更甚。。

    6 years ago

    ws基本不会出问题,就算关键时候断线了,建立好重连机制也不会有问题;当然也有完全的解决方法,这你得自己摸索了

    6 years ago

    遇到过不稳定的WS ,各种故障。各有利弊吧。

    6 years ago

    碰到过这个问题,我的解决办法是记录一下下单前的持仓,然后下ioc订单后记录id,循环获取订单状态,如果是成交/部分成交,循环对比当前持仓和记录的持仓,当这两个值不同就跳出循环。

    6 years ago

    好的感谢, 学习下。

    6 years ago
  • 1
Forums
PINE Language
Get the app
iPhone Download
© 2015 - ∞ INVENTOR PTE LTD (SG)