2
konzentrieren Sie sich auf
319
Anhänger

Eine kurze Analyse von Arbitragestrategien: So können Sie risikoarme Gelegenheiten mit kurzen Zeitverzögerungen nutzen

Erstellt in: 2025-03-07 14:11:55, aktualisiert am: 2025-03-10 17:38:06
comments   0
hits   1386

Eine kurze Analyse von Arbitragestrategien: So können Sie risikoarme Gelegenheiten mit kurzen Zeitverzögerungen nutzen

Die Inspiration für diese Strategie stammt aus dem Opportunity-Beitrag des Zhihu-Autors „Dream Dealer“ – „TRUMP und MELANIA Low-Risk-Korrelationsarbitragemodell“. Der Artikel untersucht die Preiskorrelation zwischen zwei auf BN aufgelegten Kontrakten (TRUMP und MELANIA) und nutzt die subtile Zeitverzögerung zwischen den beiden, um zu versuchen, kurzfristige Marktschwankungen zu erfassen und Arbitrage mit geringem Risiko zu erzielen. Als Nächstes erläutern wir die Prinzipien dieser Strategie, die Logik der Codeimplementierung und erkunden mögliche Optimierungsrichtungen.

Muss im Voraus seinBeachtenDas Problem besteht darin, dass diese Strategie einem manuellen Handelsvorgang entspricht. Sie bietet nur dann eine gewisse Gewinnchance, wenn zwei geeignete Handelspaare gefunden wurden, und die Gewinndauer des Handelspaares kann kurz sein. Wenn festgestellt wird, dass keine Gewinnchance besteht, muss die Strategie rechtzeitig gestoppt werden, um Gewinneinbußen oder sogar Verluste zu vermeiden.


1. Strategiegrundsätze und Marktrelevanz

1.1 Strategischer Hintergrund

Sowohl die TRUMP- als auch die MELANIA-Verträge werden vom gleichen Emissionsteam herausgegeben und verfügen über die gleichen kontrollierenden Fonds, sodass ihre Preistrends die meiste Zeit über stark synchronisiert sind. Aufgrund von Faktoren wie Vertragsgestaltung oder Marktausführung hinkt der Preis von MELANIA jedoch tendenziell 1-2 Sekunden hinter dem von TRUMP her. Diese winzige Verzögerung bietet Arbitrageuren die Möglichkeit, Preisunterschiede auszunutzen und hochfrequentes Copy-Trading durchzuführen. Einfach ausgedrückt: Wenn TRUMP schnell schwankt, neigt MELANIA dazu, sehr bald nachzuziehen. Durch Ausnutzung dieser Verzögerung können Transaktionen mit geringerem Risiko abgeschlossen werden.

Eine kurze Analyse von Arbitragestrategien: So können Sie risikoarme Gelegenheiten mit kurzen Zeitverzögerungen nutzen

Eine kurze Analyse von Arbitragestrategien: So können Sie risikoarme Gelegenheiten mit kurzen Zeitverzögerungen nutzen

1.2 Verbreitung im Kryptomarkt

Ähnliche Korrelationsphänomene sind im Kryptomarkt keine Seltenheit:

  • Verschiedene Verträge oder Derivate desselben Projekts: Aufgrund derselben zugrunde liegenden Vermögenswerte oder des gleichen Teamhintergrunds weisen die Preise verschiedener Produkte häufig starke Verknüpfungen auf.
  • Börsenübergreifende Arbitrage: Derselbe Vermögenswert kann an verschiedenen Börsen aufgrund von Unterschieden in der Liquidität und den Matching-Mechanismen leichte Preisunterschiede aufweisen.
  • Stablecoins und an Fiatgeld gekoppelte Produkte: Bei diesen Produkten kommt es häufig zu erwarteten Wechselkursabweichungen, und Arbitrageure können von geringfügigen Schwankungen profitieren.

Diese Korrelation bietet Hochfrequenzhändlern und Arbitrageuren stabile Handelssignale und risikoärmere Geschäftsmöglichkeiten, erfordert aber auch Handelsstrategien, die sehr sensibel auf subtile Marktveränderungen reagieren und in Echtzeit reagieren können.


2. Detaillierte Erklärung der Code-Logik

Der Code besteht im Wesentlichen aus mehreren Teilen, wobei jedes Modul den wichtigsten Schritten der Arbitrage-Strategie entspricht.

2.1 Beschreibung der Zusatzfunktionen

Positionsinformationen abrufen

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
    }
}
  • Funktionalität: Mit dieser Funktion werden die Positionsinformationen des angegebenen Handelspaares einheitlich abgerufen und die Long- und Short-Positionen in positive und negative Zahlen umgewandelt.
  • Logik: Wenn die Position leer ist, geben Sie die Standardnullposition zurück. Wenn mehr als eine Bestellung vorhanden ist, wird ein Fehler gemeldet (um eine Einwegposition sicherzustellen). Andernfalls geben Sie die Richtung, den Preis und den variablen Gewinn und Verlust der aktuellen Position zurück.

Konto initialisieren

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
}
  • Funktionalität: Mit dieser Funktion wird das Eigenkapital eines Kontos als Grundlage für nachfolgende Gewinn- und Verlustberechnungen initialisiert und aufgezeichnet.

Ausstehende Bestellung stornieren

function CancelPendingOrders() {
    orders = exchange.GetOrders();  // 获取订单
    for (let order of orders) {
        if (order.Status == ORDER_STATE_PENDING) {  // 只取消未完成的订单
            exchange.CancelOrder(order.Id);  // 取消挂单
        }
    }
}
  • Funktionalität: Denken Sie vor der Aufgabe einer Bestellung daran, alle nicht abgeschlossenen Bestellungen zu stornieren, um Bestellkonflikte oder doppelte Bestellungen zu vermeiden.

2.2 Haupttransaktionslogik

Die Hauptfunktion verwendet eine Endlosschleife, um die folgenden Schritte kontinuierlich auszuführen:

  1. Datenerfassung und Marktkalkulation
    Jeder Zyklus beginnt mitexchange.GetRecords Holen Sie sich die Marktdaten von Pair_A bzw. Pair_B.

    • Berechnungsformel: [ ratio = \frac{Close{A} - Open{A}}{Open{A}} - \frac{Close{B} - Open{B}}{Open{B}} ] Durch den Vergleich des Anstiegs und des Abfalls der beiden können wir feststellen, ob ein abnormaler Preisunterschied vorliegt. Wenn die Preisdifferenz den voreingestellten DiffLevel überschreitet, wird die Eröffnungsbedingung ausgelöst.
  2. Eröffnungsbedingungen ermitteln und Bestellung aufgeben
    Wenn keine aktuelle Position vorhanden ist (position_B.amount == 0) und Handel erlaubt ist (afterTrade==1):

    • Wenn das Verhältnis größer als der DiffLevel ist, wird davon ausgegangen, dass der Markt im Begriff ist zu steigen, daher wird eine Kauforder für Pair_B ausgegeben (Long-Position kaufen).
    • Wenn das Verhältnis kleiner als -diffLevel ist, wird davon ausgegangen, dass der Markt im Begriff ist zu fallen, und es wird ein Verkaufsauftrag erteilt (eine Short-Position wird eröffnet). Vor der Aufgabe einer Bestellung wird zunächst die Funktion zur Bestellstornierung aufgerufen, um sicherzustellen, dass der aktuelle Bestellstatus gelöscht wird.
  3. Stop-Profit- und Stop-Loss-Logik
    Sobald eine Position etabliert ist, setzt die Strategie entsprechende Take-Profit- und Stop-Loss-Orders entsprechend der Positionsrichtung:

    • Long-Position (Kauf): Setzen Sie den Take-Profit-Preis auf den Positionspreis multipliziert mit (1 + stopProfitLevel) und den Stop-Loss-Preis auf den Positionspreis multipliziert mit (1 - stopLossLevel).
    • Short-Position (Verkauf): Der Take-Profit-Preis wird auf den Positionspreis multipliziert mit (1 - stopProfitLevel) festgelegt, und der Stop-Loss-Preis wird auf den Positionspreis multipliziert mit (1 + stopLossLevel) festgelegt. Das System überwacht den Marktpreis in Echtzeit. Sobald die Take-Profit- oder Stop-Loss-Bedingungen ausgelöst werden, wird die ursprüngliche ausstehende Order storniert und eine Order zum Schließen der Position platziert.
  4. Gewinnstatistiken und Protokollaufzeichnungen nach dem Schließen einer Position
    Nachdem jede Position geschlossen wurde, ermittelt das System die Änderungen im Eigenkapital des Kontos und berechnet die Anzahl der Gewinne, die Anzahl der Verluste und den kumulierten Gewinn-/Verlustbetrag.
    Gleichzeitig werden Tabellen und Grafiken verwendet, um aktuelle Positionsinformationen, Transaktionsstatistiken und Zyklusverzögerungen in Echtzeit anzuzeigen, was für eine spätere Analyse der Strategiewirkung praktisch ist.


3. Strategieoptimierung und Expansionsmethoden

Diese Strategie nutzt zwar die subtile Verzögerung zwischen zwei stark korrelierten Verträgen, es gibt jedoch noch viele Bereiche, die verbessert werden können:

3.1 Parameteroptimierung und dynamische Anpassung

  • Schwellenwertanpassung: Parameter wie diffLevel, stopProfitLevel und stopLossLevel müssen möglicherweise in unterschiedliche Marktumgebungen angepasst werden. Diese Parameter können durch Backtesting historischer Daten oder durch dynamische Anpassung von Modellen in Echtzeit (z. B. Algorithmen des maschinellen Lernens) automatisch optimiert werden.
  • Positionsverwaltung:Die aktuelle Strategie verwendet eine feste Handelsnummer, um eine Position zu eröffnen. In Zukunft können wir die Einführung eines dynamischen Positionsmanagements oder eines Mechanismus zum Eröffnen von Positionen in Stapeln und schrittweisen Gewinnmitnahmen in Betracht ziehen, um das Risiko einer einzelnen Transaktion zu verringern.

3.2 Filterung von Handelssignalen

  • Multifaktorielles Signal: Die Berechnung des Verhältnisses, das nur auf Preissteigerungen und -senkungen basiert, kann durch Rauschen beeinflusst werden. Sie können die Einführung von Handelsvolumen, Orderbuchtiefe und technischen Indikatoren (wie RSI, MACD usw.) in Betracht ziehen, um falsche Signale noch weiter herauszufiltern.
  • Verspätungsentschädigung: Angesichts der Tatsache, dass MELANIA eine Verzögerung von 1–2 Sekunden aufweist, wird die Entwicklung eines genaueren Mechanismus zur Zeitsynchronisierung und Signalvorhersage dazu beitragen, die Genauigkeit des Eintrittszeitpunkts zu verbessern.

3.3 Systemrobustheit und Risikokontrolle

  • Fehlerbehandlung: Fügen Sie Ausnahmebehandlung und Protokollierung hinzu, um eine rechtzeitige Reaktion bei Netzwerkverzögerungen oder Anomalien an der Austauschschnittstelle sicherzustellen und unerwartete Verluste durch Systemausfälle zu verhindern.
  • Risikokontrollstrategie: Kombinieren Sie Kapitalmanagement und Kontrolle des maximalen Drawdowns, um ein tägliches oder einzelnes Transaktionsverlustlimit festzulegen und so aufeinanderfolgende Verluste in extremen Marktumgebungen zu verhindern.

3.4 Code- und Architekturoptimierung

  • Asynchrone Verarbeitung: Derzeit wird die Strategieschleife alle 100 Millisekunden ausgeführt. Durch asynchrone Verarbeitung und Multithread-Optimierung können das Risiko von Verzögerungen und Ausführungsblockierungen verringert werden.
  • Strategie-Backtesting und Simulation:Einführung eines vollständigen Backtesting-Systems und einer simulierten Echtzeit-Handelsumgebung, um die Leistung von Strategien unter verschiedenen Marktbedingungen zu überprüfen und dazu beizutragen, dass die Strategien im realen Handel stabiler laufen.

IV. Fazit

In diesem Artikel werden die Grundprinzipien und der Implementierungscode einer Korrelationsarbitragestrategie für kurzfristig verzögerte Kontrakte ausführlich vorgestellt. Von der Ausnutzung der Unterschiede bei Preissteigerungen und -rückgängen über die Nutzung von Einstiegsgelegenheiten bis hin zur Festlegung von Stop-Profit und Stop-Loss für das Positionsmanagement nutzt diese Strategie die hohe Korrelation zwischen den Vermögenswerten auf dem Kryptomarkt voll aus. Gleichzeitig haben wir eine Reihe von Optimierungsvorschlägen unterbreitet, darunter dynamische Parameteranpassung, Signalfilterung, Systemrobustheit und Codeoptimierung, um die Stabilität und Rentabilität der Strategie in Echtzeitanwendungen weiter zu verbessern.

Obwohl die Strategie einzigartig inspiriert und einfach umzusetzen ist, müssen Arbitragegeschäfte auf dem hochfrequenten und volatilen Kryptomarkt mit Vorsicht behandelt werden. Ich hoffe, dass dieser Artikel wertvolle Referenzen und Inspirationen für Freunde bieten kann, die sich für quantitative Handels- und Arbitragestrategien interessieren.


Hinweis: Die Strategietestumgebung ist der OKX-Simulationshandel, und die spezifischen Details können für verschiedene Börsen geändert werden

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