Durante mucho tiempo, el problema del retraso de datos de la interfaz API de posición del intercambio de divisas digitales siempre me ha molestado. No he encontrado una manera adecuada de lidiar con este problema. Permítanme reproducir la escena del problema. Por lo general, la orden de precio de mercado proporcionada por el intercambio de contratos es en realidad el precio de la contraparte, por lo que a veces no es confiable usar esta llamada orden de precio de mercado. Sin embargo, cuando escribimos estrategias de negociación de futuros de divisas digitales, usamos la orden de límite más. Después de cada orden realizada, necesitamos verificar la posición para ver si la orden se cierra y se mantiene la posición correspondiente. El problema radica en esta posición de información. Si la orden se cierra, la información devuelta por la interfaz de información de posición del intercambio (es decir, la interfaz a la que realmente accede la capa inferior cuando llenamos el intercambio.GetPosition) debe incluir la información de posición nueva. Sin embargo, si la posición anterior se completa, la estrategia de intercambio no se vuelve a colocar.
Debido a este problema, he visto una estrategia abierta llena de posiciones largas de manera loca. Afortunadamente, el mercado se disparó en ese momento, y la ganancia flotante excedió los 10BTC. Afortunadamente, el mercado aumentó bruscamente. Si disminuyó bruscamente, podemos imaginar el resultado.
Solución 1 La estrategia puede diseñarse para colocar solo una orden, y el precio de la orden es el precio del oponente comercial actual más un gran precio deslizante, para tomar una cierta profundidad de la orden del oponente. La ventaja de esto es que solo se colocará una orden, y no se juzgará en función de la información de la posición. Esto puede evitar el problema de órdenes repetidas, pero a veces colocar una orden puede activar el mecanismo de límite de precios del intercambio cuando el cambio de precio es relativamente grande, y es posible aumentar el precio deslizante y aún así no lograr hacer un trato, perdiendo así la oportunidad.
Solución 2 Con la función de orden de precio de mercado de la bolsa, el precio se transfiere a - 1 en la FMZ como la orden de precio de mercado.
Solución 3 Todavía usamos la lógica de negociación anterior y realizamos órdenes con órdenes de límite de precio, pero agregamos algo de detección en la lógica de negociación para tratar de resolver el problema causado por el retraso de los datos de posición. Compruebe si la orden ha desaparecido directamente de la lista de órdenes pendientes sin cancelación (hay dos posibilidades para desaparecer de la lista de órdenes pendientes: 1. Cancelación y 2. Relleno). Si se detecta tal situación, y la cantidad de la orden colocada de nuevo es la misma que la de la última orden, es importante tener en cuenta que 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, o incluso continúe optimizando y aumentando el número de veces de espera para activar los tiempos de espera, si excede un cierto número de veces, indica que el retraso de datos de la interfaz de posición es grave, lo que hace que termine la lógica de negociación.
// 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")
}
Dirección del modelo:https://www.fmz.com/strategy/203258
La interfaz de plantilla se llama de la misma manera que$.OpenLong
, $.CoverLong
en la función principal de arriba.
La plantilla es una versión beta, y son bienvenidos a hacer sugerencias. Seguiremos optimizándola para que podamos manejar el problema del retraso de datos de posición.