Logika Perdagangan Berjangka Mata Uang Crypto

Penulis:Kebaikan, Dibuat: 2020-07-18 13:31:34, Diperbarui: 2023-10-26 20:05:28

img

Tempat masalah

Untuk waktu yang lama, masalah keterlambatan data dari antarmuka API dari pertukaran mata uang kripto selalu mengganggu saya. Saya belum menemukan cara yang tepat untuk mengatasinya. Saya akan mereproduksi adegan masalah ini.

Biasanya pesanan pasar yang diberikan oleh bursa kontrak sebenarnya adalah harga counterparty, sehingga terkadang yang disebut order pasar agak tidak dapat diandalkan. Oleh karena itu, ketika kita menulis strategi perdagangan berjangka mata uang kripto, sebagian besar dari mereka menggunakan pesanan batas. Setelah setiap pesanan ditempatkan, kita perlu memeriksa posisi untuk melihat apakah pesanan dipenuhi dan posisi yang sesuai dipegang.

Masalahnya terletak pada informasi posisi ini. jika pesanan ditutup, data yang dikembalikan oleh antarmuka informasi posisi pertukaran (yaitu antarmuka pertukaran yang lapisan bawah benar-benar mengakses ketika kita memanggilexchange.GetPositionJika data yang dikembalikan oleh bursa adalah data lama, yaitu informasi posisi pesanan yang baru saja ditempatkan sebelum transaksi selesai, ini akan menyebabkan masalah.

Logika trading dapat menganggap bahwa order belum dipenuhi dan terus menempatkan order. Namun, antarmuka penempatan order dari bursa tidak tertunda, tetapi transaksi cepat, dan order dieksekusi. Ini akan menyebabkan konsekuensi serius bahwa strategi akan berulang kali menempatkan order ketika memicu operasi membuka posisi.

Pengalaman Nyata

Karena masalah ini, saya telah melihat strategi untuk mengisi posisi panjang gila, untungnya, pasar naik pada saat itu, dan keuntungan mengambang sekali melebihi 10BTC. Untungnya, pasar telah meroket. Jika itu terjun, akhir dapat dibayangkan.

Cobalah untuk Menyelesaikan

  • Rencana 1

Hal ini dimungkinkan untuk merancang logika penempatan order untuk strategi untuk menempatkan hanya satu order. Harga penempatan order adalah slippage besar untuk kesenjangan harga harga lawan pada saat itu, dan kedalaman tertentu dari pesanan lawan dapat dieksekusi. Keuntungan dari ini adalah bahwa hanya satu order ditempatkan, dan tidak dinilai berdasarkan informasi posisi. Ini dapat menghindari masalah penempatan order berulang, tetapi kadang-kadang ketika harga berubah relatif besar, order akan memicu mekanisme batas harga bursa, dan itu dapat menyebabkan bahwa pesanan slippage besar masih belum selesai, dan kehilangan peluang perdagangan.

  • Rencana 2

Menggunakan fungsi harga pasar dari bursa, price pass -1 di FMZ adalah harga pasar. Saat ini, antarmuka berjangka OKEX telah ditingkatkan untuk mendukung harga pasar nyata.

  • Rencana 3

Kami masih menggunakan logika trading sebelumnya dan menempatkan limit order, tetapi kami menambahkan beberapa deteksi ke logika trading untuk mencoba menyelesaikan masalah yang disebabkan oleh keterlambatan data posisi. Setelah order ditempatkan, jika order tidak dibatalkan, itu langsung menghilang dalam daftar pending order (daftar pending order menghilang dengan dua cara yang mungkin: 1 withdraw order, 2 executed), mendeteksi situasi seperti itu dan menempatkan jumlah order lagi. Jumlah order terakhir sama. Pada saat ini, perlu memperhatikan apakah data posisi tertunda. Biarkan program masuk ke logika menunggu untuk mendapatkan kembali informasi posisi. Anda bahkan dapat terus mengoptimalkan dan meningkatkan jumlah pending trigger. Jika melebihi jumlah tertentu waktu, data antarmuka posisi tertunda. Masalahnya serius, biarkan logika transaksi diakhiri.

Desain berdasarkan rencana 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")
}

Alamat templat:https://www.fmz.com/strategy/203258

Cara memanggil antarmuka template adalah seperti$.OpenLongdan$.CoverLongdalammainfungsi di atas.

Template ini adalah versi beta, saran apa pun diterima, saya akan terus mengoptimalkan untuk mengatasi masalah keterlambatan dalam data posisi.


Berkaitan

Lebih banyak