2
fokus pada
319
Pengikut

Analisis ringkas strategi arbitraj: cara menangkap peluang berisiko rendah dengan ketinggalan masa yang singkat

Dicipta dalam: 2025-03-07 14:11:55, dikemas kini pada: 2025-03-10 17:38:06
comments   0
hits   1386

Analisis ringkas strategi arbitraj: cara menangkap peluang berisiko rendah dengan ketinggalan masa yang singkat

Inspirasi untuk strategi ini datang daripada siaran peluang oleh pengarang Zhihu “Dream Dealer” - “model arbitraj korelasi TRUMP dan MELANIA berisiko rendah”. Artikel itu meneroka korelasi harga antara dua kontrak yang dilancarkan pada BN (TRUMP dan MELANIA), dan menggunakan kelewatan masa yang halus antara kedua-duanya untuk cuba menangkap turun naik pasaran jangka pendek dan mencapai arbitraj berisiko rendah. Seterusnya, kami akan menerangkan prinsip strategi ini, logik pelaksanaan kod, dan meneroka kemungkinan arah pengoptimuman.

Perlu lebih awalNotisMasalahnya ialah strategi ini bersamaan dengan kerja perdagangan manual Ia mempunyai peluang keuntungan tertentu hanya selepas mencari dua pasangan dagangan yang sesuai, dan hayat keuntungan pasangan dagangan mungkin pendek Apabila didapati tiada peluang keuntungan, adalah perlu untuk menghentikan strategi dalam masa untuk mengelakkan pengeluaran keuntungan atau kerugian.


1. Prinsip Strategi dan Perkaitan Pasaran

1.1 Latar Belakang Strategik

Kedua-dua kontrak TRUMP dan MELANIA dikeluarkan oleh pasukan pengeluar yang sama dan mempunyai dana kawalan yang sama, jadi aliran harga mereka sangat disegerakkan pada kebanyakan masa. Walau bagaimanapun, disebabkan oleh faktor seperti reka bentuk kontrak atau pelaksanaan pasaran, harga MELANIA cenderung ketinggalan daripada TRUMP sebanyak 1-2 saat. Kelewatan kecil ini memberi peluang kepada penimbangtara untuk menangkap perbezaan harga dan menjalankan perdagangan salinan frekuensi tinggi. Ringkasnya, apabila TRUMP turun naik dengan cepat, MELANIA cenderung untuk mengikutinya sejurus selepas itu. Dengan mengambil kesempatan daripada kelewatan ini, urus niaga boleh diselesaikan dengan risiko yang lebih rendah.

Analisis ringkas strategi arbitraj: cara menangkap peluang berisiko rendah dengan ketinggalan masa yang singkat

Analisis ringkas strategi arbitraj: cara menangkap peluang berisiko rendah dengan ketinggalan masa yang singkat

1.2 Kelaziman dalam Pasaran Kripto

Fenomena korelasi yang sama adalah perkara biasa dalam pasaran crypto:

  • Kontrak atau derivatif yang berbeza bagi projek yang sama: Disebabkan oleh aset asas atau latar belakang pasukan yang sama, harga produk yang berbeza selalunya mempunyai kaitan yang kukuh.
  • Arbitraj Pertukaran Silang:Aset yang sama pada bursa yang berbeza mungkin mempunyai sedikit perbezaan harga disebabkan oleh perbezaan dalam kecairan dan mekanisme pemadanan.
  • Stablecoin dan produk berpatok fiat: Produk ini selalunya menjangkakan penyelewengan kadar pertukaran, dan penimbang tara boleh mendapat keuntungan daripada sedikit turun naik.

Korelasi ini menyediakan pedagang dan penimbangtara frekuensi tinggi dengan isyarat dagangan yang stabil dan peluang operasi berisiko rendah, tetapi ia juga memerlukan strategi dagangan untuk menjadi sangat sensitif terhadap perubahan pasaran yang halus dan dapat bertindak balas dalam masa nyata.


2. Penjelasan terperinci tentang logik kod

Kod ini terutamanya terdiri daripada beberapa bahagian, setiap modul sepadan dengan langkah-langkah utama dalam strategi arbitraj.

2.1 Penerangan fungsi bantu

Dapatkan maklumat jawatan

function GetPosition(pair){
    let pos = exchange.GetPosition(pair)
    if(pos.length == 0){
        return {amount:0, price:0, profit:0}
    }else if(pos.length > 1){
        throw '不支持双向持仓'
    }else if(pos.length == 1){
        return {amount:pos[0].Type == 0 ? pos[0].Amount : -pos[0].Amount, price:pos[0].Price, profit:pos[0].Profit}
    }else{
        Log('未获取仓位数据')
        return null
    }
}
  • Fungsi: Fungsi ini digunakan untuk mendapatkan maklumat kedudukan pasangan dagangan yang ditentukan secara seragam dan menukar kedudukan beli dan pendek kepada nombor positif dan negatif.
  • Logik: Jika kedudukan kosong, kembalikan kedudukan sifar lalai; jika terdapat lebih daripada satu pesanan, ralat dilaporkan (untuk memastikan kedudukan sehala jika tidak, kembalikan arah, harga dan keuntungan dan kerugian terapung kedudukan semasa);

Mulakan akaun

function InitAccount(){
    let account = _C(exchange.GetAccount)
    let total_eq = account.Equity

    let init_eq = 0
    if(!_G('init_eq')){
        init_eq = total_eq
        _G('init_eq', total_eq)
    }else{
        init_eq = _G('init_eq')
    }

    return init_eq
}
  • Fungsi: Fungsi ini digunakan untuk memulakan dan merekodkan ekuiti akaun sebagai asas untuk pengiraan untung dan rugi seterusnya.

Batalkan pesanan belum selesai

function CancelPendingOrders() {
    orders = exchange.GetOrders();  // 获取订单
    for (let order of orders) {
        if (order.Status == ORDER_STATE_PENDING) {  // 只取消未完成的订单
            exchange.CancelOrder(order.Id);  // 取消挂单
        }
    }
}
  • Fungsi: Sebelum membuat pesanan, pastikan anda membatalkan sebarang pesanan yang belum selesai sebelum ini untuk mengelakkan konflik pesanan atau pesanan pendua.

2.2 Logik transaksi utama

Fungsi utama menggunakan gelung tak terhingga untuk terus melaksanakan langkah berikut:

  1. Pemerolehan data dan pengiraan pasaran
    Setiap kitaran bermula denganexchange.GetRecords Dapatkan data pasaran Pair_A dan Pair_B masing-masing.

    • Formula pengiraan: [ ratio = \frac{Close{A} - Open{A}}{Open{A}} - \frac{Close{B} - Open{B}}{Open{B}} ] Dengan membandingkan kenaikan dan kejatuhan kedua-duanya, kita boleh menentukan sama ada terdapat perbezaan harga yang tidak normal. Apabila perbezaan harga melebihi diffLevel pratetap, keadaan pembukaan dicetuskan.
  2. Tentukan syarat pembukaan dan buat pesanan
    Apabila tiada kedudukan semasa (position_B.amount == 0) dan perdagangan dibenarkan (afterTrade==1):

    • Jika nisbah lebih besar daripada diffLevel, adalah dipercayai bahawa pasaran akan meningkat, jadi pesanan beli dikeluarkan untuk Pair_B (kedudukan beli beli).
    • Jika nisbah kurang daripada -diffLevel, ia dianggap pasaran akan jatuh dan pesanan jual dikeluarkan (kedudukan jual dibuka). Sebelum membuat pesanan, fungsi pembatalan pesanan akan dipanggil untuk memastikan status pesanan semasa dikosongkan.
  3. Logik henti untung dan henti rugi
    Setelah kedudukan ditetapkan, strategi akan menetapkan pesanan ambil untung dan henti rugi yang sepadan mengikut arah kedudukan:

    • Kedudukan panjang (beli): Tetapkan harga ambil untung kepada harga kedudukan didarab dengan (1 + stopProfitLevel), dan harga stop loss kepada harga posisi didarab dengan (1 - stopLossLevel).
    • Kedudukan pendek (jual): Harga ambil untung ditetapkan kepada harga kedudukan didarab dengan (1 - stopProfitLevel), dan harga stop loss ditetapkan kepada harga kedudukan didarab dengan (1 + stopLossLevel). Sistem akan memantau harga pasaran masa nyata Setelah keadaan ambil untung atau henti rugi dicetuskan, pesanan belum selesai asal akan dibatalkan dan pesanan akan dibuat untuk menutup kedudukan.
  4. Statistik keuntungan dan rekod log selepas menutup kedudukan
    Selepas setiap kedudukan ditutup, sistem akan memperoleh perubahan dalam ekuiti akaun dan mengira bilangan keuntungan, bilangan kerugian, dan jumlah keuntungan/kerugian terkumpul.
    Pada masa yang sama, jadual dan graf digunakan untuk memaparkan maklumat kedudukan semasa, statistik transaksi dan kelewatan kitaran dalam masa nyata, yang sesuai untuk analisis kesan strategi berikutnya.


3. Pengoptimuman strategi dan kaedah pengembangan

Walaupun strategi ini mengeksploitasi kelewatan halus antara dua kontrak yang sangat berkorelasi, masih terdapat banyak bidang yang boleh diperbaiki:

3.1 Pengoptimuman Parameter dan Pelarasan Dinamik

  • Pelarasan Ambang: Parameter seperti diffLevel, stopProfitLevel dan stopLossLevel mungkin perlu dilaraskan dalam persekitaran pasaran yang berbeza. Parameter ini boleh dioptimumkan secara automatik melalui ujian balik data sejarah atau dengan melaraskan model secara dinamik dalam masa nyata (cth., algoritma pembelajaran mesin).
  • Pengurusan kedudukan:Strategi semasa menggunakan Trade_Number tetap untuk membuka kedudukan Pada masa hadapan, kami boleh mempertimbangkan untuk memperkenalkan pengurusan kedudukan dinamik atau mekanisme membuka kedudukan dalam kelompok dan mengambil keuntungan secara beransur-ansur untuk mengurangkan risiko satu transaksi.

3.2 Penapisan isyarat dagangan

  • Isyarat berbilang faktor: Pengiraan nisbah hanya berdasarkan kenaikan dan penurunan harga mungkin dipengaruhi oleh bunyi bising. Anda boleh mempertimbangkan untuk memperkenalkan volum dagangan, kedalaman buku pesanan dan penunjuk teknikal (seperti RSI, MACD, dll.) untuk menapis lebih lanjut isyarat palsu.
  • Pampasan kelewatan: Memandangkan MELANIA mempunyai kelewatan selama 1-2 saat, membangunkan mekanisme penyegerakan masa dan ramalan isyarat yang lebih tepat akan membantu meningkatkan ketepatan pemasaan kemasukan.

3.3 Kekukuhan sistem dan kawalan risiko

  • Ralat pengendalian: Tambahkan pengendalian pengecualian dan pengelogan untuk memastikan respons tepat pada masanya apabila menghadapi kelewatan rangkaian atau anomali antara muka pertukaran, dan mengelakkan kerugian yang tidak dijangka yang disebabkan oleh kegagalan sistem.
  • Strategi Kawalan Risiko: Menggabungkan pengurusan modal dan kawalan pengeluaran maksimum untuk menetapkan had kerugian transaksi harian atau tunggal untuk mengelakkan kerugian berturut-turut dalam persekitaran pasaran yang melampau.

3.4 Kod dan Pengoptimuman Seni Bina

  • Pemprosesan Asynchronous: Pada masa ini, gelung strategi dilaksanakan setiap 100 milisaat Melalui pemprosesan tak segerak dan pengoptimuman berbilang benang, kelewatan dan risiko penyekatan pelaksanaan dapat dikurangkan.
  • Ujian belakang strategi dan simulasi:Perkenalkan sistem ujian belakang yang lengkap dan persekitaran dagangan simulasi masa nyata untuk mengesahkan prestasi strategi di bawah keadaan pasaran yang berbeza dan membantu strategi berjalan dengan lebih stabil dalam perdagangan sebenar.

IV. Kesimpulan

Artikel ini memperkenalkan secara terperinci prinsip asas dan kod pelaksanaan strategi arbitraj korelasi kontrak ketinggalan masa pendek. Daripada mengeksploitasi perbezaan dalam kenaikan dan penurunan harga kepada menangkap peluang kemasukan, kepada menetapkan henti untung dan henti rugi untuk pengurusan kedudukan, strategi ini memanfaatkan sepenuhnya korelasi tinggi antara aset dalam pasaran kripto. Pada masa yang sama, kami juga telah mengemukakan beberapa cadangan pengoptimuman, termasuk pelarasan parameter dinamik, penapisan isyarat, kekukuhan sistem dan pengoptimuman kod, untuk meningkatkan lagi kestabilan dan keuntungan strategi dalam aplikasi masa nyata.

Walaupun strategi ini diilhamkan secara unik dan mudah untuk dilaksanakan, sebarang operasi arbitraj mesti ditangani dengan berhati-hati dalam pasaran kripto frekuensi tinggi dan tidak menentu. Saya harap artikel ini dapat memberikan rujukan dan inspirasi yang berharga untuk rakan-rakan yang berminat dengan strategi perdagangan kuantitatif dan arbitraj.


Nota: Persekitaran ujian strategi ialah perdagangan simulasi OKX, dan butiran khusus boleh diubah suai untuk pertukaran yang berbeza

function GetPosition(pair){
    let pos = exchange.GetPosition(pair)
    if(pos.length == 0){
        return {amount:0, price:0, profit:0}
    }else if(pos.length > 1){
        throw '不支持双向持仓'
    }else if(pos.length == 1){
        return {amount:pos[0].Type == 0 ? pos[0].Amount : -pos[0].Amount, price:pos[0].Price, profit:pos[0].Profit}
    }else{
        Log('未获取仓位数据')
        return null
    }
}

function InitAccount(){
    let account = _C(exchange.GetAccount)
    let total_eq = account.Equity

    let init_eq = 0
    if(!_G('init_eq')){
        init_eq = total_eq
        _G('init_eq', total_eq)
    }else{
        init_eq = _G('init_eq')
    }

    return init_eq
}

function CancelPendingOrders() {
    orders = exchange.GetOrders();  // 获取订单
    for (let order of orders) {
        if (order.Status == ORDER_STATE_PENDING) {  // 只取消未完成的订单
            exchange.CancelOrder(order.Id);  // 取消挂单
        }
    }
}

var pair_a = Pair_A + "_USDT.swap";
var pair_b = Pair_B + "_USDT.swap";


function main() {
    exchange.IO('simulate', true);
    LogReset(0);
    Log('策略开始运行')

    var precision = exchange.GetMarkets();
    var ratio = 0

    var takeProfitOrderId = null;
    var stopLossOrderId = null;
    var successCount = 0;
    var lossCount = 0;
    var winMoney = 0;
    var failMoney = 0;
    var afterTrade = 1;

    var initEq = InitAccount();

    var curEq = initEq

    var pricePrecision = precision[pair_b].PricePrecision;

    while (true) {
        try{
            let startLoopTime = Date.now();
            let position_B = GetPosition(pair_b);
            let new_r_pairB = exchange.GetRecords(pair_b, 1).slice(-1)[0];

            if (!new_r_pairB || !position_B) {
                Log('跳过当前循环');
                continue;
            }
            
            // 合并交易条件:检查是否可以开仓并进行交易
            if (afterTrade == 1 && position_B.amount == 0) {
                
                let new_r_pairA = exchange.GetRecords(pair_a, 1).slice(-1)[0];
                if (!new_r_pairA ) {
                    Log('跳过当前循环');
                    continue;
                }
                
                ratio = (new_r_pairA.Close - new_r_pairA.Open) / new_r_pairA.Open - (new_r_pairB.Close - new_r_pairB.Open) / new_r_pairB.Open;

                if (ratio > diffLevel) {
                    CancelPendingOrders();
                    Log('实时ratio:', ratio, '买入:', pair_b, position_B.amount);
                    exchange.CreateOrder(pair_b, "buy", -1, Trade_Number);
                    afterTrade = 0;
                } else if (ratio < -diffLevel) {
                    CancelPendingOrders();
                    Log('实时ratio:', ratio, '卖出:', pair_b, position_B.amount);
                    exchange.CreateOrder(pair_b, "sell", -1, Trade_Number);
                    afterTrade = 0;
                }            
            }

            

            // 判断止盈止损
            if (position_B.amount > 0 && takeProfitOrderId == null && stopLossOrderId == null && afterTrade == 0) {
                Log('多仓持仓价格:', position_B.price, '止盈价格:', position_B.price * (1 + stopProfitLevel), '止损价格:', position_B.price * (1 - stopLossLevel));
                takeProfitOrderId = exchange.CreateOrder(pair_b, "closebuy", position_B.price * (1 + stopProfitLevel), position_B.amount);
                Log('止盈订单:', takeProfitOrderId);
            }

            if (position_B.amount > 0 && takeProfitOrderId != null && stopLossOrderId == null && new_r_pairB.Close < position_B.price * (1 - stopLossLevel) && afterTrade == 0) {
                CancelPendingOrders();
                takeProfitOrderId = null
                Log('多仓止损');
                stopLossOrderId = exchange.CreateOrder(pair_b, "closebuy", -1, position_B.amount);
                Log('多仓止损订单:', stopLossOrderId);
            }

            if (position_B.amount < 0 && takeProfitOrderId == null && stopLossOrderId == null && afterTrade == 0) {
                Log('空仓持仓价格:', position_B.price, '止盈价格:', position_B.price * (1 - stopProfitLevel), '止损价格:', position_B.price * (1 + stopLossLevel));
                takeProfitOrderId = exchange.CreateOrder(pair_b, "closesell", position_B.price * (1 - stopProfitLevel), -position_B.amount);
                Log('止盈订单:', takeProfitOrderId, '当前价格:', new_r_pairB.Close );
            }

            if (position_B.amount < 0 && takeProfitOrderId != null && stopLossOrderId == null && new_r_pairB.Close > position_B.price * (1 + stopLossLevel) && afterTrade == 0) {
                CancelPendingOrders();
                takeProfitOrderId = null
                Log('空仓止损');
                stopLossOrderId = exchange.CreateOrder(pair_b, "closesell", -1, -position_B.amount);
                Log('空仓止损订单:', stopLossOrderId);
            }


            // 平市价单未完成
            if (takeProfitOrderId == null && stopLossOrderId != null && afterTrade == 0) {
                
                let stoplosspos = GetPosition(pair_b)
                if(stoplosspos.amount > 0){
                    Log('平多仓市价单未完成')
                    exchange.CreateOrder(pair_b, 'closebuy', -1, stoplosspos.amount)
                }
                if(stoplosspos.amount < 0){
                    Log('平空仓市价单未完成')
                    exchange.CreateOrder(pair_b, 'closesell', -1, -stoplosspos.amount)
                }
            }

            // 未平仓完毕
            if (Math.abs(position_B.amount) < Trade_Number && Math.abs(position_B.amount) > 0 && afterTrade == 0){
                Log('未平仓完毕')
                if(position_B.amount > 0){
                    exchange.CreateOrder(pair_b, 'closebuy', -1, position_B.amount)
                }else{
                    exchange.CreateOrder(pair_b, 'closesell', -1, -position_B.amount)
                }
            }

            // 计算盈亏
            if (position_B.amount == 0 && afterTrade == 0) {
                if (stopLossOrderId != null || takeProfitOrderId != null) {
                    stopLossOrderId = null;
                    takeProfitOrderId = null;

                    let afterEquity = exchange.GetAccount().Equity;
                    let curAmount = afterEquity - curEq;

                    curEq = afterEquity

                    if (curAmount > 0) {
                        successCount += 1;
                        winMoney += curAmount;
                        Log('盈利金额:', curAmount);
                    } else {
                        lossCount += 1;
                        failMoney += curAmount;
                        Log('亏损金额:', curAmount);
                    }
                    afterTrade = 1;
                }
            }

            if (startLoopTime % 10 == 0) {  // 每 10 次循环记录一次
                let curEquity = exchange.GetAccount().Equity

                // 输出交易信息表
                let table = {
                    type: "table",
                    title: "交易信息",
                    cols: [
                        "初始权益", "当前权益", Pair_B + "仓位", Pair_B + "持仓价", Pair_B + "收益", Pair_B + "价格", 
                        "盈利次数", "盈利金额", "亏损次数", "亏损金额", "胜率", "盈亏比"
                    ],
                    rows: [
                        [
                            _N(_G('init_eq'), 2),  // 初始权益
                            _N(curEquity, 2),  // 当前权益
                            _N(position_B.amount, 1),  // Pair B 仓位
                            _N(position_B.price, pricePrecision),  // Pair B 持仓价
                            _N(position_B.profit, 1),  // Pair B 收益
                            _N(new_r_pairB.Close, pricePrecision),  // Pair B 价格
                            _N(successCount, 0),  // 盈利次数
                            _N(winMoney, 2),  // 盈利金额
                            _N(lossCount, 0),  // 亏损次数
                            _N(failMoney, 2),  // 亏损金额
                            _N(successCount + lossCount === 0 ? 0 : successCount / (successCount + lossCount), 2),  // 胜率
                            _N(failMoney === 0 ? 0 : winMoney / failMoney * -1, 2)  // 盈亏比
                        ]
                    ]
                };

                $.PlotMultLine("ratio plot", "幅度变化差值", ratio, startLoopTime);
                $.PlotMultHLine("ratio plot", diffLevel, "差价上限", "red", "ShortDot");
                $.PlotMultHLine("ratio plot", -diffLevel, "差价下限", "blue", "ShortDot");

                LogStatus("`" + JSON.stringify(table) + "`");
                LogProfit(curEquity - initEq, '&')
            }
        }catch(e){
            Log('策略出现错误:', e)
        }

        
        Sleep(200);
    }
}