Долгое время меня всегда беспокоила проблема задержки передачи данных в интерфейсе API криптовалютной биржи. Я не нашел подходящего способа справиться с ней. Я воспроизнесу сцену этой проблемы.
Обычно рыночный заказ, предоставляемый биржей контрактов, на самом деле является ценой контрагента, поэтому иногда так называемый
Проблема заключается в этой информации о позиции. Если ордер закрыт, данные, возвращенные обменной информацией об интерфейсе позиции (то есть обменный интерфейс, который нижний слой фактически получает доступ, когда мы называемexchange.GetPosition
Если данные, возвращаемые биржей, являются старыми данными, то есть информацией о позиции ордера, размещенного до завершения сделки, это вызовет проблему.
Логика торговли может считать, что ордер не был выполнен и продолжать размещать ордер. Однако интерфейс размещения ордеров биржи не задерживается, но транзакция быстрая, и ордер выполняется. Это приведет к серьезному последствию того, что стратегия будет неоднократно размещать заказы при запуске операции открытия позиции.
Из-за этой проблемы я видел стратегию заполнения длинной позиции безумно, к счастью, рынок поднялся в то время, и плавающая прибыль однажды превысила 10BTC. К счастью, рынок взлетел вверх. Если это падение, конец можно представить.
Можно разработать логику размещения ордеров для стратегии размещения только одного ордера. Цена размещения ордера является большим скольжением для ценового разрыва цены оппонента в то время, и определенная глубина ордеров оппонента может быть выполнена. Преимущество этого заключается в том, что размещается только один ордер, и он не оценивается на основе информации о позиции. Это может избежать проблемы повторного размещения ордеров, но иногда, когда цена меняется относительно сильно, ордер запускает механизм ценового лимита биржи, и это может привести к тому, что большой ордер скольжения все еще не завершен, и упустил торговую возможность.
Используя функцию
Мы по-прежнему используем предыдущую логику торговли и размещаем лимитный ордер, но добавляем некоторое обнаружение в логику торговли, чтобы попытаться решить проблему, вызванную задержкой данных о позиции. После размещения ордера, если ордер не отменен, он исчезает непосредственно в списке ожидаемых ордеров (список ожидаемых ордеров исчезает двумя возможными способами: 1 вывод ордера, 2 исполнение), обнаруживаем такую ситуацию и снова размещаем сумму ордера. Сумма последнего ордера остается прежней. В это время необходимо обратить внимание на то, задерживаются ли данные о позиции. Пусть программа вводит логику ожидания, чтобы повторно получить информацию о позиции. Вы даже можете продолжать оптимизировать и увеличивать количество ожиданий запуска. Если он превышает определенное количество раз, интерфейс данных о позиции задерживается. Проблема серьезна, пусть логика транзакции прекратится.
// 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")
}
Адрес формы:https://www.fmz.com/strategy/203258
Способ вызова интерфейса шаблона такой же, как$.OpenLong
и$.CoverLong
вmain
функция выше.
Шаблон является бета-версией, любые предложения приветствуются, я буду продолжать оптимизировать для решения проблемы задержек в данных о положении.