avatar of 发明者量化-小小梦 发明者量化-小小梦
Suivre Messages privés
4
Suivre
1271
Abonnés

Conception et mise en œuvre d'un suivi des différences de prix de change DEX-CEX basé sur la quantification FMZ

Créé le: 2025-02-21 10:40:52, Mis à jour le: 2025-02-21 13:53:00
comments   0
hits   1236

Conception et mise en œuvre d’un suivi des différences de prix de change DEX-CEX basé sur la quantification FMZ

Plusieurs bourses DEX, dont dydx_v4, hyperliquid, vertex et aevo, ont été encapsulées et connectées sur FMZ. Alors que la concurrence pour l’arbitrage des prix dans les bourses centralisées devient de plus en plus féroce, de nombreux traders quantitatifs ont tourné leur attention vers les bourses décentralisées. Dans cet article, nous discuterons de la conception et de la mise en œuvre de la surveillance des différences de prix entre DEX et CEX.

La première étape de la stratégie d’arbitrage de couverture consiste à calculer la différence de prix du portefeuille cible et à observer et analyser s’il existe des opportunités de trading. La conception et la mise en œuvre d’une stratégie de surveillance des différences de prix constituent donc la première tâche fondamentale. Nos exigences de conception sont les suivantes :

  • Le langage de programmation utilisé est Javascript.
  • Utilisez l’interface REST encapsulée.
  • Sélection DEX : hyperliquide, vertex.
  • Sélection CEX : binance, bybit.
  • La demande de données du carnet de commandes utilise des requêtes simultanées multithread.
  • Les produits de test doivent être autant que possible les produits courants partagés par toutes les bourses : paires de trading spot ETH, BTC/contrats perpétuels
  • Essayez de simplifier la conception et de fournir une implémentation de base en utilisant un code simple et facile à comprendre.

Implémentation du code

Le code comporte moins de 200 lignes et sa fonction est uniquement de calculer la différence de prix en temps réel d’un certain produit dans différentes bourses. Lors de l’exécution initiale de la stratégie, tous les objets d’échange configurés pour la stratégie seront classés dans le groupe DEX et le groupe CEX. Chaque sondage est effectué via la fonction multithread Thread encapsulée par la plateforme FMZ, et sollicite simultanément l’interface REST : l’interface du carnet d’ordresGetDepth, enregistrez les données de la liste des ordres d’achat et de la liste des ordres de vente demandées des variétés requises. Ensuite, le groupe DEX et le groupe CEX sont combinés dans une combinaison de différence (combinaison DEX-CEX, c’est-à-dire une paire d’arbitrage), et la différence de prix est calculée.

Détermination du type d’échange : Lors de l’initialisation, l’objet d’échange ajouté sera évalué pour déterminer s’il s’agit d’un spot ou d’un contrat à terme.

Différentes bourses peuvent avoir des noms différents pour un certain actif sous-jacent, le nom du produit doit donc être ajusté : Le programme doit être ajusté en fonction des règles de dénomination des symboles des différentes bourses. Par exemple, les paires de trading des contrats Vertex sont nommées comme suit :XXX_USD, qui est en fait un contrat perpétuel basé sur l’USDC. Le nom de l’ETH dans le spot de Vertex est WETH.

Obtenez de la précision : Lors de l’initialisation, toutes les informations du marché sont obtenues. Selon le symbole spécifique demandé, la précision correspondante peut être interrogée pour les opérations de traitement de précision des données ultérieures.

Demande de données Avant que toutes les données de profondeur ne soient obtenues, le programme attendra et continuera à vérifier s’il existe des échanges dont les données n’ont pas été mises à jour. Si les données n’ont pas été obtenues, le programme restera en veille pendant 100 millisecondes. Les données du carnet de commandes demandées sont enregistrées dans un objet créé par threading.Dict() (utilisé pour l’interaction avec les threads simultanés), et les données sont réinitialisées à la fin de chaque interrogation.

Résumer Cette mise en œuvre de stratégie montre comment surveiller les différences de prix sur plusieurs bourses en temps réel et calculer les opportunités d’arbitrage possibles. Grâce à une correction raisonnable des symboles, une capture approfondie des données, un contrôle de précision et un fonctionnement multithread, le système peut surveiller efficacement les différences de prix en temps réel. Pour les apprenants en stratégie, la compréhension des idées de mise en œuvre de ce code peut les aider à maîtriser comment utiliser l’API pour obtenir des données de transaction, comment traiter les données de plusieurs échanges, comment calculer et générer des spreads de transaction et comment appliquer ces technologies dans des transactions réelles.

let symbolList = []

function createEx(idx, exs) {
    let self = {}
    
    let cexEidList = ["Binance", "Bybit", "Futures_Binance", "Futures_Bybit"]
    let dexEidList = ["Vertex", "Hyperliquid", "Futures_Hyperliquid", "Futures_Vertex"]

    self.name = exs[idx].GetName()
    self.idx = idx
    self.e = exs[idx]
    self.depths = threading.Dict()
    self.markets = self.e.GetMarkets()

    if (!self.markets) {
        throw "GetMarkets error"
    }

    if (dexEidList.includes(self.name)) {
        self.type = "DEX"
    } else if (cexEidList.includes(self.name)) {
        self.type = "CEX"
    } else {
        throw "not support " + self.name
    }

    if (self.name.startsWith("Futures_")) {
        self.isFutures = true
    } else {
        self.isFutures = false
    }

    self.correctSymbol = function(symbol) {        
        if (self.name == "Vertex") {
            let correctList = {"BTC_USDC": "WBTC_USDC", "ETH_USDC": "WETH_USDC"}
            if (typeof(correctList[symbol]) != "undefined") {
                return correctList[symbol]
            }
        } else if (self.name == "Hyperliquid") {
            let correctList = {"BTC_USDC": "UBTC_USDC"}
            if (typeof(correctList[symbol]) != "undefined") {
                return correctList[symbol]
            }
        } else if (self.name == "Futures_Hyperliquid") {
            return symbol.replace("_USDC", "_USD")
        }
        
        return symbol
    }

    self.reqDepth = function(symbol) {
        symbol = self.correctSymbol(symbol)
        threading.Thread(function(idx, symbol, threadingDict) {
            let depth = exchanges[idx].GetDepth(symbol)
            if (depth) {
                threadingDict.set(symbol, depth)
            } else {
                threadingDict.set(symbol, null)
            }
        }, self.idx, symbol, self.depths)
    }
    
    self.getPrecision = function(symbol) {
        symbol = self.correctSymbol(symbol)
        let marketInfo = self.markets[symbol]
        if (marketInfo) {
            return [marketInfo.PricePrecision, marketInfo.AmountPrecision]
        } else {
            return [8, 8]
        }
    }

    self.init = function() {
        self.depths = threading.Dict()
    }

    self.getDepth = function(symbol) {
        symbol = self.correctSymbol(symbol)
        return self.depths.get(symbol)
    }

    return self
}

function createManager(symbolList, exs) {
    let self = {}

    self.symbolList = symbolList
    self.exchanges = []
    self.hedgePair = []

    self.initHedgePair = function () {
        for (let i in exs) {
            let ex = createEx(i, exs)
            self.exchanges.push(ex)
        }

        let arrDEX = self.exchanges.filter(item => item.type == "DEX")
        let arrCEX = self.exchanges.filter(item => item.type == "CEX")

        for (let dex of arrDEX) {
            for (let cex of arrCEX) {
                self.hedgePair.push({"dex": dex, "cex": cex})
            }
        }
    }

    self.calcHedgeData = function () {
        let beginTimestamp = new Date().getTime()
        for (let e of self.exchanges) {
            for (let symbol of self.symbolList) {
                e.reqDepth(symbol)
            }
        }

        while (true) {
            let isWait = false
            for (let e of self.exchanges) {
                for (let symbol of self.symbolList) {
                    let depth = e.getDepth(symbol)
                    if (depth == null || typeof(depth) == "undefined") {
                        isWait = true
                    }
                }
            }
            if (isWait) {
                Sleep(100)
            } else {
                break
            }
        }

        let tbls = []
        for (let symbol of self.symbolList) {
            let tbl = {"type": "table", "title": symbol + "差价", "cols": ["pair", "bid-ask", "ask-bid", "dex ask", "dex bid", "cex ask", "cex bid"], "rows": []}
            for (let p of self.hedgePair) {
                let dex = p["dex"]
                let cex = p["cex"]

                let pricePrecision = Math.max(dex.getPrecision(symbol)[0], cex.getPrecision(symbol)[0])

                let dexDepth = dex.getDepth(symbol)
                let cexDepth = cex.getDepth(symbol)
                if (dexDepth && cexDepth) {
                    p["bid-ask"] = _N(dexDepth.Bids[0].Price - cexDepth.Asks[0].Price, pricePrecision)
                    p["ask-bid"] = _N(dexDepth.Asks[0].Price - cexDepth.Bids[0].Price, pricePrecision)

                    // 输出信息、观察测试
                    Log(dex.name, cex.name, symbol, "bid-ask:", p["bid-ask"], ", ask-bid", p["ask-bid"])

                    p[dex.name + "-ask"] = dexDepth.Asks[0].Price + "/" + dexDepth.Asks[0].Amount
                    p[dex.name + "-bid"] = dexDepth.Bids[0].Price + "/" + dexDepth.Bids[0].Amount
                    p[cex.name + "-ask"] = cexDepth.Asks[0].Price + "/" + cexDepth.Asks[0].Amount
                    p[cex.name + "-bid"] = cexDepth.Bids[0].Price + "/" + cexDepth.Bids[0].Amount
                } else {
                    p["bid-ask"] = "--"
                    p["ask-bid"] = "--"
                    p[dex.name + "-ask"] = "--"
                    p[dex.name + "-bid"] = "--"
                    p[cex.name + "-ask"] = "--"
                    p[cex.name + "-bid"] = "--"
                }

                let pairName = dex.name + "-" + cex.name
                tbl["rows"].push([pairName, p["bid-ask"], p["ask-bid"], p[dex.name + "-ask"], p[dex.name + "-bid"], p[cex.name + "-ask"], p[cex.name + "-bid"]])
            }
            tbls.push(tbl)
        }
                
        for (let e of self.exchanges) {
            e.init()
        }

        let endTimestamp = new Date().getTime()
        return [tbls, (endTimestamp - beginTimestamp) + "毫秒"]
    }

    self.initHedgePair()
    return self
}

function main() {
    LogReset(1)
    let loopCount = 0

    symbolList = strSymbolList.split(",")
    let m = createManager(symbolList, exchanges)
    while (true) {
        let ret = m.calcHedgeData()
        loopCount++
        LogStatus(_D(), "耗时:", ret[1], ", 轮询次数:", loopCount, "\n", "`" + JSON.stringify(ret[0]) + "`")
        Sleep(1000)
    }
}

Conception des paramètres :

Conception et mise en œuvre d’un suivi des différences de prix de change DEX-CEX basé sur la quantification FMZ

Marché au comptant

Surveiller un produit spot :

  • BTC_USDC Cours Bitcoin en USDC au comptant

Conception et mise en œuvre d’un suivi des différences de prix de change DEX-CEX basé sur la quantification FMZ

Marché des contrats

Surveiller deux variétés :

  • ETH_USDC.swap Contrat perpétuel Ethereum
  • BTC_USDC.swap Contrat perpétuel Bitcoin

Conception et mise en œuvre d’un suivi des différences de prix de change DEX-CEX basé sur la quantification FMZ

END

Sens d’expansion :

  • Surveillance de seuil et encapsulation de la logique de transaction.
  • Calculez les frais et les coûts, et calculez la fourchette de spread de couverture raisonnable.
  • Utilisez l’interface websocket pour obtenir des données de marché.

La plateforme FMZ continuera d’améliorer son support technique pour les échanges décentralisés (DEX) et la finance décentralisée (DeFi), et d’itérer et de mettre à jour en permanence les fonctions et les produits.

Merci de votre lecture.