Sumber daya yang dimuat... Pemuatan...

Pikirkan Logika Trading Futures Mata Uang Digital

Penulis:Penemu Kuantitas - Mimpi Kecil, Dibuat: 2020-06-01 09:52:45, Diperbarui: 2023-10-08 19:41:25

img

Pikirkan Logika Trading Futures Mata Uang Digital

Skenario Masalah

长久以来,数字货币交易所持仓API接口的数据延迟问题总是困扰着我。一直没有找到合适的处理方式,这个问题的场景我来复现下。通常合约交易所提供的市价单其实为对手价,所以有时候用这个所谓的“市价单”有些不靠谱。因此我们在写数字货币期货交易策略时,大部分用的是限价单。在每次下单后,我们要检查持仓,看看下单是不是成交了,并且持有了对应的仓位。问题就出在这个持仓信息上,如果订单成交了,交易所持仓信息接口(就是我们调用exchange.GetPosition时底层实际去访问的交易所接口)返回的数据应当是包含新开仓持仓信息的,但是交易所返回的数据如果是旧数据,即刚才下单的订单成交前的持仓信息,这样就出问题了。交易逻辑可能认为订单没有成交,继续下单。但是交易所下单接口并不延迟,反而成交很快,下单就成交。这样会造成一种严重的后果就是策略在一次触发开仓的操作时会不停的重复下单。

Pengalaman nyata

Karena masalah ini, saya telah melihat strategi yang gila untuk membuka banyak posisi, untungnya saat itu pasar terpuruk, naik lebih dari 10 BTC; untungnya pasar terpuruk, jika jatuh, hasilnya bisa dilihat.

Cobalah

  • Solusi 1 Strategi ini dapat dirancang untuk membuat suborder logis untuk hanya order berikutnya, harga suborder ditambah harga geser yang lebih besar untuk harga lawan saat itu, dan makan order lawan yang memiliki kedalaman tertentu. Manfaatnya adalah hanya order berikutnya, dan tidak diputuskan berdasarkan informasi yang dimiliki. Ini dapat menghindari masalah pengulangan suborder, tetapi kadang-kadang suborder dapat memicu mekanisme harga yang terbatas di bursa ketika perubahan harga relatif besar, dan mungkin menambahkan harga geser besar yang masih belum selesai, kehilangan kesempatan.

  • Solusi 2 Dengan fungsi satuan harga pasar di bursa, transfer harga-1 pada FMZ adalah satuan harga pasar, dan saat ini antarmuka futures OKEX telah ditingkatkan untuk mendukung satuan harga pasar yang sebenarnya.

  • Solusi 3 Kami masih menggunakan logika transaksi sebelumnya, menggunakan pesanan dengan harga terbatas, tetapi kami menambahkan beberapa pengujian ke dalam logika transaksi untuk mencoba menyelesaikan masalah yang disebabkan oleh keterlambatan data posisi ini. Memeriksa apakah pesanan setelah pesanan hilang langsung dari daftar tanggungan tanpa dibatalkan (dua kemungkinan hilang dari daftar tanggungan: 1: penarikan, 2 pembagian), mendeteksi situasi seperti itu dan mengirim pesanan lagi dengan jumlah yang sama dengan pesanan sebelumnya, pada saat ini perlu diperhatikan apakah data penyimpanan tertunda, membiarkan program masuk ke logika tunggu, mendapatkan kembali informasi penyimpanan, atau bahkan dapat terus dioptimalkan, meningkatkan jumlah penundaan yang dipicu, melebihi jumlah tertentu menunjukkan bahwa masalah keterlambatan data antarmuka penyimpanan serius, sehingga logika transaksi ini berakhir.

Desain berdasarkan skema 3

// 参数
/*
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);
            }
            // 检测directBreak 并且持仓未变的情况
            if (preNeedOpen == needOpen && directBreak) {
                Log("疑似仓位数据延迟,等待30秒", "#FF0000")
                Sleep(30000)
                nowPosition = GetPosition(e, contractType, direction);
                if (nowPosition) {
                    needOpen = opAmount - (nowPosition.Amount - initAmount);
                }
                /*
                timeoutCount++
                if (timeoutCount > 10) {
                    Log("连续10次疑似仓位延迟,下单失败!", "#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, "开多仓", contractType, ticker);
        } else {
            orderId = e.Sell(ticker.Buy - SlidePrice, amount, "开空仓", 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, "平多仓", contractType, ticker);
        } else if (position.Type == PD_SHORT) {
            e.SetDirection("closesell");
            e.Buy(ticker.Sell + SlidePrice, amount, "平空仓", 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 templat adalah seperti pada fungsi main di atas.$.OpenLong$.CoverLongSaya tidak tahu. Templat ini adalah versi uji coba, dan kami akan terus mengoptimalkannya untuk mengatasi masalah keterlambatan data gudang.


Berkaitan

Lebih banyak

excmCara terbaik adalah dengan menggunakan ws, perintah untuk segera memberi tahu Anda jika ada pembaruan, dan tidak meminta satu kali.

Xueqiu BotSaat mengalami masalah ini, solusi saya adalah mencatat kepemilikan sebelum pesanan, kemudian mencatat id setelah pesanan ioc, berputar untuk mendapatkan status pesanan, jika transaksi / transaksi parsial, berputar untuk membandingkan kepemilikan saat ini dan kepemilikan catatan, keluar dari lingkaran ketika kedua nilai berbeda.

Penemu Kuantitas - Mimpi KecilSaya pernah mengalami WS yang tidak stabil, berbagai kegagalan.

excmWS tidak akan menjadi masalah, bahkan jika Anda terputus pada saat-saat penting, tidak akan ada masalah untuk membangun mekanisme koneksi yang baik; tentu saja ada solusi lengkap, yang harus Anda rasakan sendiri.

Penemu Kuantitas - Mimpi KecilNamun, ketika antarmuka WS juga tidak dapat diandalkan, itu lebih menjengkelkan daripada protokol REST.

Penemu Kuantitas - Mimpi KecilTerima kasih banyak, belajarlah.