Sumber dimuat naik... memuat...

Beberapa Pemikiran mengenai Logik Dagangan Futures Mata Wang Kripto

Penulis:Kebaikan, Dicipta: 2020-06-10 09:31:57, Dikemas kini: 2023-11-01 20:25:32

img

Tempat masalah

Untuk masa yang lama, masalah kelewatan data antara muka API pertukaran mata wang kripto selalu mengganggu saya. Saya tidak menemui cara yang sesuai untuk mengatasinya. Saya akan menghasilkan semula adegan masalah ini.

Biasanya pesanan pasaran yang disediakan oleh bursa kontrak sebenarnya adalah harga pihak lawan, jadi kadang-kadang yang dipanggil perintah pasaran agak tidak boleh dipercayai. Oleh itu, apabila kita menulis strategi perdagangan niaga hadapan mata wang kripto, kebanyakan dari mereka menggunakan pesanan had. Selepas setiap pesanan diletakkan, kita perlu memeriksa kedudukan untuk melihat sama ada pesanan dipenuhi dan kedudukan yang sesuai dipegang.

Masalah terletak pada maklumat kedudukan ini. jika pesanan ditutup, data yang dikembalikan oleh antara muka maklumat kedudukan pertukaran (iaitu antara muka pertukaran yang lapisan bawah sebenarnya mengakses apabila kita memanggilexchange.GetPositionJika data yang dikembalikan oleh bursa adalah data lama, iaitu maklumat kedudukan pesanan yang baru diletakkan sebelum transaksi selesai, ini akan menyebabkan masalah.

Logik perdagangan mungkin menganggap bahawa pesanan tidak telah dipenuhi dan terus meletakkan pesanan. Walau bagaimanapun, antara muka penempatan pesanan bursa tidak tertunda, tetapi urus niaga cepat, dan pesanan dilaksanakan. Ini akan menyebabkan akibat yang serius bahawa strategi akan berulang kali meletakkan pesanan ketika mencetuskan operasi membuka kedudukan.

Pengalaman sebenar

Oleh kerana masalah ini, saya telah melihat strategi untuk mengisi kedudukan panjang gila, untungnya, pasaran telah meningkat pada masa itu, dan keuntungan terapung sekali melebihi 10BTC.

Cuba untuk Menyelesaikan

  • Pelan 1

Ia adalah mungkin untuk merancang logik penempatan pesanan untuk strategi untuk meletakkan hanya satu pesanan. Harga penempatan pesanan adalah seluncur besar untuk jurang harga harga lawan pada masa itu, dan kedalaman tertentu pesanan lawan boleh dilaksanakan. Kelebihan ini adalah bahawa hanya satu pesanan diletakkan, dan ia tidak dinilai berdasarkan maklumat kedudukan. Ini dapat mengelakkan masalah penempatan pesanan berulang, tetapi kadang-kadang apabila harga berubah agak besar, pesanan akan mencetuskan mekanisme had harga bursa, dan ia boleh menyebabkan pesanan seluncur besar masih tidak selesai, dan terlepas peluang perdagangan.

  • Pelan 2

Menggunakan harga pasaran fungsi bursa, harga lulus -1 pada FMZ adalah harga pasaran. Pada masa ini, antara muka niaga hadapan OKEX telah dinaik taraf untuk menyokong harga pasaran sebenar.

  • Pelan 3

Kami masih menggunakan logik dagangan sebelumnya dan meletakkan pesanan had, tetapi kami menambah beberapa pengesanan ke dalam logik dagangan untuk cuba menyelesaikan masalah yang disebabkan oleh kelewatan data kedudukan. Selepas pesanan diletakkan, jika pesanan tidak dibatalkan, ia hilang secara langsung dalam senarai pesanan yang tertunda (daftar pesanan yang tertunda hilang dengan dua cara yang mungkin: 1 menarik balik pesanan, 2 dilaksanakan), mengesan keadaan tersebut dan meletakkan jumlah pesanan lagi. Jumlah pesanan terakhir adalah sama. Pada masa ini, perlu memberi perhatian kepada sama ada data kedudukan tertunda. Biarkan program memasukkan logik menunggu untuk mendapatkan semula maklumat kedudukan. Anda bahkan boleh terus mengoptimumkan dan meningkatkan jumlah menunggu pemicu. Jika melebihi beberapa kali, data antara muka kedudukan tertunda. Masalahnya serius, biarkan logik urus niaga dihentikan.

Reka bentuk berdasarkan Pelan 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 untuk memanggil antara muka templat adalah seperti$.OpenLongdan$.CoverLongdalammainfungsi di atas.

Templat adalah versi beta, sebarang cadangan dialu-alukan, saya akan terus mengoptimumkan untuk menangani masalah kelewatan dalam data kedudukan.


Berkaitan

Lebih lanjut