Durante mucho tiempo, el problema del retraso de datos de la interfaz API del intercambio de criptomonedas siempre me ha preocupado. No he encontrado una manera adecuada de lidiar con él. Reproduciré la escena de este problema.
Por lo general, la orden de mercado proporcionada por el intercambio de contratos es en realidad el precio de la contraparte, por lo que a veces la llamada
Si la orden se cierra, los datos devueltos por la interfaz de información de posición de intercambio (es decir, la interfaz de intercambio que la capa inferior realmente accede cuando llamamosexchange.GetPosition
Si los datos devueltos por la bolsa son datos antiguos, es decir, la información de la posición de la orden que acaba de colocarse antes de que se complete la transacción, esto causará un problema.
La lógica de negociación puede considerar que la orden no se ha cumplido y continuar colocando la orden. Sin embargo, la interfaz de colocación de órdenes del intercambio no se retrasa, pero la transacción es rápida y la orden se ejecuta. Esto causará una consecuencia grave de que la estrategia coloque órdenes repetidamente al desencadenar la operación de apertura de una posición.
Debido a este problema, he visto una estrategia para llenar una posición larga loco, afortunadamente, el mercado había aumentado en ese momento, y la ganancia flotante excedió una vez 10BTC.
Es posible diseñar la lógica de la colocación de órdenes para que la estrategia coloque solo un pedido. El precio de colocación de órdenes es un gran deslizamiento para la brecha de precio del precio del oponente en el momento, y se puede ejecutar una cierta profundidad de órdenes del oponente. La ventaja de esto es que solo se coloca un pedido, y no se juzga en función de la información de posición. Esto puede evitar el problema de la colocación repetida de pedidos, pero a veces cuando el precio cambia relativamente grande, el pedido activará el mecanismo de límite de precios del intercambio, y puede llevar a que la orden de gran deslizamiento aún no se complete y se pierda la oportunidad comercial.
Utilizando la función
Todavía usamos la lógica de negociación anterior y colocamos un pedido de límite, pero agregamos algo de detección a la lógica de negociación para tratar de resolver el problema causado por el retraso de los datos de posición. Después de que se coloca la orden, si la orden no se cancela, desaparece directamente en la lista de órdenes pendientes (la lista de órdenes pendientes desaparece de dos maneras posibles: 1 retira la orden, 2 ejecuta), detecta dicha situación y vuelve a colocar el monto de la orden. La cantidad de la última orden es la misma. En este momento, es necesario prestar atención a si los datos de posición se retrasan. Deja que el programa ingrese la lógica de espera para recuperar la información de posición. Incluso puedes continuar optimizando y aumentando el número de esperas de activación. Si supera un cierto número de veces, los datos de la interfaz de posición se retrasan. El problema es grave, deja que la lógica de transacción termine.
// 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")
}
Dirección del modelo:https://www.fmz.com/strategy/203258
La forma de llamar a la interfaz de la plantilla es como$.OpenLong
y$.CoverLong
En elmain
la función de arriba.
La plantilla es una versión beta, cualquier sugerencia es bienvenida, voy a seguir optimizando para hacer frente al problema de los retrasos en los datos de posición.