2
fokus pada
319
Pengikut

Analisis singkat mengenai strategi arbitrase: cara menangkap peluang berisiko rendah dengan jeda waktu yang singkat

Dibuat di: 2025-03-07 14:11:55, diperbarui pada: 2025-03-10 17:38:06
comments   0
hits   1386

Analisis singkat mengenai strategi arbitrase: cara menangkap peluang berisiko rendah dengan jeda waktu yang singkat

Inspirasi untuk strategi ini datang dari postingan peluang oleh penulis Zhihu “Dream Dealer” - “Model arbitrase korelasi risiko rendah TRUMP dan MELANIA”. Artikel ini membahas korelasi harga antara dua kontrak yang diluncurkan di BN (TRUMP dan MELANIA), dan menggunakan penundaan waktu yang halus antara keduanya untuk mencoba menangkap fluktuasi pasar jangka pendek dan mencapai arbitrase berisiko rendah. Selanjutnya, kami akan menjelaskan prinsip-prinsip strategi ini, logika implementasi kode, dan mengeksplorasi kemungkinan arah pengoptimalan.

Perlu didahulukanMelihatMasalahnya adalah strategi ini setara dengan pekerjaan perdagangan manual. Strategi ini memiliki peluang keuntungan tertentu hanya setelah menemukan dua pasangan perdagangan yang cocok, dan masa keuntungan pasangan perdagangan tersebut mungkin singkat. Ketika ditemukan bahwa tidak ada peluang keuntungan, strategi tersebut perlu dihentikan tepat waktu untuk mencegah penurunan keuntungan atau bahkan kerugian.


1. Prinsip Strategi dan Relevansi Pasar

1.1 Latar Belakang Strategis

Baik kontrak TRUMP maupun MELANIA diterbitkan oleh tim penerbit yang sama dan memiliki dana pengendali yang sama, sehingga tren harga keduanya sangat tersinkronisasi hampir sepanjang waktu. Namun, karena faktor-faktor seperti desain kontrak atau eksekusi pasar, harga MELANIA cenderung tertinggal 1-2 detik di belakang harga TRUMP. Penundaan kecil ini memberikan peluang bagi pelaku arbitrase untuk menangkap perbedaan harga dan melakukan perdagangan salinan frekuensi tinggi. Sederhananya, ketika TRUMP berfluktuasi dengan cepat, MELANIA cenderung mengikutinya segera setelahnya. Dengan memanfaatkan penundaan ini, transaksi dapat diselesaikan dengan risiko yang lebih rendah.

Analisis singkat mengenai strategi arbitrase: cara menangkap peluang berisiko rendah dengan jeda waktu yang singkat

Analisis singkat mengenai strategi arbitrase: cara menangkap peluang berisiko rendah dengan jeda waktu yang singkat

1.2 Prevalensi di Pasar Kripto

Fenomena korelasi serupa tidak jarang terjadi di pasar kripto:

  • Kontrak atau derivatif yang berbeda dari proyek yang sama: Karena aset dasar atau latar belakang tim yang sama, harga berbagai produk sering kali memiliki keterkaitan yang kuat.
  • Arbitrase Lintas Bursa:Aset yang sama di bursa yang berbeda mungkin memiliki sedikit perbedaan harga karena perbedaan likuiditas dan mekanisme pencocokan.
  • Stablecoin dan produk yang dipatok pada mata uang fiat:Produk-produk ini sering kali memiliki penyimpangan nilai tukar yang diharapkan, dan pelaku arbitrase dapat memperoleh keuntungan dari sedikit fluktuasi.

Korelasi ini memberikan pedagang frekuensi tinggi dan pelaku arbitrase sinyal perdagangan yang stabil dan peluang operasi berisiko rendah, tetapi juga memerlukan strategi perdagangan yang sangat peka terhadap perubahan pasar yang halus dan mampu merespons dalam waktu nyata.


2. Penjelasan rinci tentang logika kode

Kode tersebut terutama terdiri dari beberapa bagian, setiap modul sesuai dengan langkah-langkah utama dalam strategi arbitrase.

2.1 Deskripsi fungsi tambahan

Dapatkan informasi posisi

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 memperoleh informasi posisi pasangan perdagangan yang ditentukan secara seragam dan mengubah posisi panjang dan pendek menjadi angka positif dan negatif.
  • Logika: Jika posisinya kosong, kembalikan posisi nol default; jika ada lebih dari satu pesanan, kesalahan dilaporkan (untuk memastikan posisi satu arah); jika tidak, kembalikan arah, harga, dan laba rugi mengambang dari posisi saat ini.

Inisialisasi akun

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 menginisialisasi dan mencatat ekuitas akun sebagai dasar untuk perhitungan laba rugi selanjutnya.

Batalkan pesanan yang tertunda

function CancelPendingOrders() {
    orders = exchange.GetOrders();  // 获取订单
    for (let order of orders) {
        if (order.Status == ORDER_STATE_PENDING) {  // 只取消未完成的订单
            exchange.CancelOrder(order.Id);  // 取消挂单
        }
    }
}
  • Fungsi:Sebelum melakukan pemesanan, pastikan untuk membatalkan pesanan yang belum selesai guna mencegah terjadinya konflik pesanan atau pesanan duplikat.

2.2 Logika transaksi utama

Fungsi utama menggunakan loop tak terbatas untuk terus menjalankan langkah-langkah berikut:

  1. Akuisisi data dan perhitungan pasar
    Setiap siklus dimulai denganexchange.GetRecords Dapatkan data pasar masing-masing untuk Pair_A dan Pair_B.

    • Rumus perhitungan: [ ratio = \frac{Close{A} - Open{A}}{Open{A}} - \frac{Close{B} - Open{B}}{Open{B}} ] Dengan membandingkan kenaikan dan penurunan keduanya, kita dapat menentukan apakah ada perbedaan harga yang tidak normal. Bila selisih harga melampaui diffLevel yang telah ditetapkan, kondisi pembukaan dipicu.
  2. Tentukan kondisi pembukaan dan tempatkan pesanan
    Ketika tidak ada posisi saat ini (position_B.amount == 0) dan perdagangan diizinkan (afterTrade==1):

    • Jika rasio lebih besar dari diffLevel, diyakini pasar akan naik, sehingga perintah beli dikeluarkan untuk Pair_B (posisi beli panjang).
    • Jika rasionya kurang dari -diffLevel, maka pasar dianggap akan jatuh dan perintah jual dikeluarkan (posisi short dibuka). Sebelum melakukan pemesanan, fungsi pembatalan pesanan akan dipanggil untuk memastikan bahwa status pesanan saat ini telah dihapus.
  3. Logika stop profit dan stop loss
    Setelah posisi ditetapkan, strategi akan menetapkan order take-profit dan stop-loss yang sesuai dengan arah posisi:

    • Posisi panjang (beli): Tetapkan harga take profit ke harga posisi dikalikan dengan (1 + stopProfitLevel), dan harga stop loss ke harga posisi dikalikan dengan (1 - stopLossLevel).
    • Posisi pendek (jual): Harga take profit ditetapkan pada harga posisi dikalikan dengan (1 - stopProfitLevel), dan harga stop loss ditetapkan pada harga posisi dikalikan dengan (1 + stopLossLevel). Sistem akan memantau harga pasar secara real-time. Setelah kondisi take-profit atau stop-loss dipicu, pending order awal akan dibatalkan dan order akan ditempatkan untuk menutup posisi.
  4. Statistik keuntungan dan catatan log setelah menutup posisi
    Setelah setiap posisi ditutup, sistem akan memperoleh perubahan dalam ekuitas akun dan menghitung jumlah keuntungan, jumlah kerugian, dan jumlah laba/rugi kumulatif.
    Pada saat yang sama, tabel dan grafik digunakan untuk menampilkan informasi posisi terkini, statistik transaksi, dan penundaan siklus secara real-time, yang berguna untuk analisis efek strategi selanjutnya.


3. Metode optimasi dan ekspansi strategi

Meskipun strategi ini memanfaatkan penundaan halus antara dua kontrak yang sangat berkorelasi, masih banyak area yang dapat ditingkatkan:

3.1 Optimasi Parameter dan Penyesuaian Dinamis

  • Penyesuaian Ambang Batas: Parameter seperti diffLevel, stopProfitLevel, dan stopLossLevel mungkin perlu disesuaikan di lingkungan pasar yang berbeda. Parameter ini dapat dioptimalkan secara otomatis melalui pengujian ulang data historis atau dengan menyesuaikan model secara dinamis dalam waktu nyata (misalnya, algoritma pembelajaran mesin).
  • Manajemen Posisi:Strategi saat ini menggunakan Trade_Number tetap untuk membuka posisi. Di masa mendatang, kami dapat mempertimbangkan untuk memperkenalkan manajemen posisi dinamis atau mekanisme pembukaan posisi secara berkelompok dan mengambil keuntungan secara bertahap untuk mengurangi risiko dari satu transaksi.

3.2 Penyaringan sinyal perdagangan

  • Sinyal multifaktorial: Perhitungan rasio yang hanya berdasarkan pada kenaikan dan penurunan harga dapat terpengaruh oleh noise. Anda dapat mempertimbangkan untuk memperkenalkan volume perdagangan, kedalaman buku pesanan, dan indikator teknis (seperti RSI, MACD, dll.) untuk menyaring sinyal palsu lebih lanjut.
  • Kompensasi keterlambatan: Mengingat MELANIA memiliki penundaan 1-2 detik, pengembangan mekanisme sinkronisasi waktu dan prediksi sinyal yang lebih akurat akan membantu meningkatkan akurasi waktu masuk.

3.3 Ketahanan sistem dan pengendalian risiko

  • Penanganan kesalahan: Tambahkan penanganan dan pencatatan pengecualian untuk memastikan respons tepat waktu saat mengalami penundaan jaringan atau anomali antarmuka pertukaran, dan mencegah kerugian tak terduga yang disebabkan oleh kegagalan sistem.
  • Strategi Pengendalian Risiko: Gabungkan manajemen modal dan kontrol penarikan maksimum untuk menetapkan batas kerugian transaksi harian atau tunggal guna mencegah kerugian berturut-turut dalam lingkungan pasar yang ekstrem.

3.4 Optimasi Kode dan Arsitektur

  • Pemrosesan Asinkron: Saat ini, strategi loop dijalankan setiap 100 milidetik. Melalui pemrosesan asinkron dan pengoptimalan multi-utas, penundaan dan risiko pemblokiran eksekusi dapat dikurangi.
  • Pengujian ulang dan simulasi strategi:Memperkenalkan sistem pengujian ulang yang lengkap dan lingkungan perdagangan simulasi waktu nyata untuk memverifikasi kinerja strategi dalam berbagai kondisi pasar dan membantu strategi berjalan lebih stabil dalam perdagangan nyata.

IV. Kesimpulan

Artikel ini memperkenalkan secara rinci prinsip dasar dan kode implementasi strategi arbitrase korelasi kontrak tertinggal jangka pendek. Mulai dari memanfaatkan selisih kenaikan dan penurunan harga hingga menangkap peluang masuk, hingga menetapkan stop-profit dan stop-loss untuk manajemen posisi, strategi ini memanfaatkan sepenuhnya korelasi tinggi antara aset di pasar kripto. Pada saat yang sama, kami juga telah mengajukan sejumlah saran pengoptimalan, termasuk penyesuaian parameter dinamis, penyaringan sinyal, ketahanan sistem, dan pengoptimalan kode, untuk lebih meningkatkan stabilitas dan profitabilitas strategi dalam aplikasi waktu nyata.

Meskipun strateginya unik dan mudah diimplementasikan, setiap operasi arbitrase harus diperlakukan dengan hati-hati di pasar kripto yang berfrekuensi tinggi dan mudah berubah. Saya berharap artikel ini dapat memberikan referensi dan inspirasi berharga bagi teman-teman yang tertarik dengan strategi perdagangan kuantitatif dan arbitrase.


Catatan: Lingkungan pengujian strategi adalah perdagangan simulasi OKX, dan detail spesifiknya dapat dimodifikasi untuk bursa yang berbeda

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);
    }
}