리소스 로딩... 로딩...

암호화폐 선물 거래의 논리에 대한 몇 가지 생각

저자:선함, 2020-06-10 09:31:57로 제작, 2023-11-01 20:25:32로 업데이트

img

문제 현장

오랫동안, 암호 화폐 거래소의 API 인터페이스의 데이터 지연 문제는 항상 나를 괴롭혔다. 나는 그것을 처리하는 적절한 방법을 찾지 못했습니다. 나는이 문제의 장면을 재생할 것입니다.

일반적으로 계약 거래소에서 제공하는 시장 주문은 실제로 상대방의 가격이기 때문에 때때로 이른바 시장 주문은 다소 신뢰할 수 없습니다. 따라서 암호화폐 선물 거래 전략을 작성할 때, 대부분은 제한 주문을 사용합니다. 각 주문이 배치된 후, 주문이 채워지고 해당 위치가 유지되는지 확인하기 위해 위치를 확인해야합니다.

문제는 이 위치 정보에 있습니다. 주문이 닫히면, 교환 위치 정보 인터페이스 (즉, 아래 계층이 실제로 액세스 하는 교환 인터페이스) 에 의해 반환 된 데이터는exchange.GetPosition그러나 거래소에서 반환되는 데이터가 오래된 데이터, 즉 거래가 완료되기 전에 배치 된 주문의 위치 정보라면, 이것은 문제를 일으킬 것입니다.

거래 논리는 주문이 완료되지 않았다고 간주하고 주문을 계속 할 수 있습니다. 그러나 거래소의 주문 배치 인터페이스는 지연되지 않지만 거래는 빠르고 주문이 실행됩니다. 이것은 전략이 포지션 개시 작업을 유발할 때 반복적으로 주문을 할 심각한 결과를 초래할 것입니다.

실제 경험

이 문제 때문에, 나는 미친 긴 포지션을 채우기 위한 전략을 보았다, 다행히, 시장은 그 때 상승했고, 부동의 이익은 10BTC를 초과했다. 다행히도, 시장은 급증했다. 만약 그것이 몰락한다면, 끝은 상상할 수 있다.

해결 하려고 노력 하십시오

  • 계획 1

주문 배치 논리를 설계하여 전략에서 하나의 주문만을 할 수 있습니다. 주문 배치 가격은 당시에 상대 가격의 가격 격차에 대한 큰 미끄러움이며, 특정 깊이의 상대 주문이 실행 될 수 있습니다. 이의 장점은 하나의 주문만 배치되고 위치 정보에 따라 판단되지 않는다는 것입니다. 이것은 반복된 주문 배치 문제를 피할 수 있지만 때로는 가격이 상대적으로 크게 변하면 주문이 거래소의 가격 제한 메커니즘을 유발하고 큰 미끄러움 주문이 여전히 완료되지 않고 거래 기회를 놓칠 수 있습니다.

  • 계획 2

거래소의 시장 가격 함수를 사용하여 FMZ의 가격 패스 -1은 시장 가격입니다. 현재 OKEX 선물 인터페이스는 실제 시장 가격을 지원하도록 업그레이드되었습니다.

  • 계획 3

우리는 여전히 이전 거래 논리를 사용하여 제한 주문을 배치하지만 위치 데이터의 지연으로 인한 문제를 해결하기 위해 거래 논리에 약간의 탐지 기능을 추가합니다. 주문이 배치된 후, 주문이 취소되지 않으면 대기 주문 목록에서 직접 사라집니다. (회계 주문 목록은 두 가지 가능한 방법으로 사라집니다: 1 주문을 철회, 2 실행), 그러한 상황을 감지하고 다시 주문 금액을 배치합니다. 마지막 주문의 금액은 동일합니다. 이 시점에서 위치 데이터가 지연되는지 주목해야합니다. 프로그램으로 대기 논리를 입력하여 위치 정보를 다시 획득하십시오. 심지어 트리거 대기 수를 최적화하고 증가시킬 수 있습니다. 일정 수를 초과하면 위치 인터페이스 데이터가 지연됩니다. 문제가 심각하면 거래 논리를 종료하십시오.

계획 3에 기초한 설계

// 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그리고$.CoverLongmain위쪽의 기능입니다.

템플릿은 베타 버전입니다. 어떤 제안도 환영합니다. 위치 데이터의 지연 문제를 해결하기 위해 최적화를 계속할 것입니다.


관련

더 많은