오랫동안, 암호 화폐 거래소의 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
위쪽의 기능입니다.
템플릿은 베타 버전입니다. 어떤 제안도 환영합니다. 위치 데이터의 지연 문제를 해결하기 위해 최적화를 계속할 것입니다.