Logic của giao dịch tương lai tiền điện tử

Tác giả:Tốt, Tạo: 2020-07-18 13:31:34, Cập nhật: 2023-10-26 20:05:28

img

Vị trí gặp vấn đề

Trong một thời gian dài, vấn đề trì hoãn dữ liệu của giao diện API của sàn giao dịch tiền điện tử luôn làm tôi lo lắng. Tôi chưa tìm thấy cách thích hợp để giải quyết nó. Tôi sẽ tái tạo cảnh của vấn đề này.

Thông thường lệnh thị trường được cung cấp bởi sàn giao dịch hợp đồng thực sự là giá đối tác, vì vậy đôi khi cái gọi là lệnh thị trường là khá không đáng tin cậy. Do đó, khi chúng ta viết các chiến lược giao dịch tương lai tiền điện tử, hầu hết chúng đều sử dụng lệnh giới hạn. Sau mỗi lệnh được đặt, chúng ta cần kiểm tra vị trí để xem lệnh đã được thực hiện và vị trí tương ứng đã được giữ.

Vấn đề nằm trong thông tin vị trí này. Nếu lệnh được đóng, dữ liệu được trả về bởi giao diện thông tin vị trí trao đổi (tức là giao diện trao đổi mà lớp dưới thực sự truy cập khi chúng ta gọiexchange.GetPositionNếu dữ liệu được trả về bởi sàn giao dịch là dữ liệu cũ, tức là thông tin vị trí của lệnh vừa được đặt trước khi giao dịch được hoàn thành, điều này sẽ gây ra một vấn đề.

Logic giao dịch có thể xem xét rằng lệnh chưa được thực hiện và tiếp tục đặt lệnh. Tuy nhiên, giao diện đặt lệnh của sàn giao dịch không bị trì hoãn, nhưng giao dịch nhanh chóng và lệnh được thực hiện. Điều này sẽ gây ra hậu quả nghiêm trọng là chiến lược sẽ nhiều lần đặt lệnh khi kích hoạt hoạt động mở vị trí.

Kinh nghiệm thực tế

Vì vấn đề này, tôi đã thấy một chiến lược để lấp đầy một vị trí dài điên rồ, may mắn thay, thị trường đã tăng vào thời điểm đó, và lợi nhuận nổi một lần vượt quá 10BTC. May mắn thay, thị trường đã tăng vọt. Nếu đó là một sự sụt giảm, kết thúc có thể được tưởng tượng.

Cố gắng giải quyết

  • Kế hoạch 1

Có thể thiết kế logic đặt lệnh cho chiến lược chỉ đặt một lệnh. Giá đặt lệnh là sự trượt lớn cho khoảng cách giá của giá đối thủ tại thời điểm đó, và một độ sâu nhất định của các lệnh đối thủ có thể được thực hiện. Lợi thế của điều này là chỉ có một lệnh được đặt, và nó không được đánh giá dựa trên thông tin vị trí. Điều này có thể tránh được vấn đề đặt lệnh lặp đi lặp lại, nhưng đôi khi khi giá thay đổi tương đối lớn, lệnh sẽ kích hoạt cơ chế giới hạn giá của sàn giao dịch, và nó có thể dẫn đến việc lệnh trượt lớn vẫn chưa hoàn thành và bỏ lỡ cơ hội giao dịch.

  • Kế hoạch 2

Sử dụng hàm giá thị trường của sàn giao dịch, giá vượt qua -1 trên FMZ là giá thị trường.

  • Kế hoạch 3

Chúng tôi vẫn sử dụng logic giao dịch trước đây và đặt lệnh giới hạn, nhưng chúng tôi thêm một số phát hiện vào logic giao dịch để cố gắng giải quyết vấn đề gây ra bởi sự chậm trễ của dữ liệu vị trí. Sau khi đặt lệnh, nếu lệnh không bị hủy, nó biến mất trực tiếp trong danh sách các lệnh đang chờ (danh sách các lệnh đang chờ biến mất theo hai cách có thể: 1 rút lệnh, 2 thực hiện), phát hiện tình huống như vậy và đặt lại số tiền lệnh. Số tiền của lệnh cuối cùng là như nhau. Tại thời điểm này, cần phải chú ý đến việc liệu dữ liệu vị trí có bị trì hoãn hay không. Hãy để chương trình nhập logic chờ để lấy lại thông tin vị trí. Bạn thậm chí có thể tiếp tục tối ưu hóa và tăng số lượng chờ kích hoạt. Nếu vượt quá một số lần nhất định, giao diện dữ liệu vị trí bị trì hoãn. Vấn đề nghiêm trọng, hãy để logic giao dịch chấm dứt.

Thiết kế dựa trên Kế hoạch 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")
}

Địa chỉ mẫu:https://www.fmz.com/strategy/203258

Cách gọi giao diện mẫu là giống như$.OpenLong$.CoverLongtrongmainchức năng ở trên.

Mẫu là một phiên bản beta, bất kỳ đề xuất được hoan nghênh, tôi sẽ tiếp tục tối ưu hóa để đối phó với vấn đề chậm trễ trong dữ liệu vị trí.


Có liên quan

Thêm nữa