Les ressources ont été chargées... Je charge...

Introduction au code source de la stratégie de négociation de paires de devises numériques et à la dernière API de la plateforme FMZ

Auteur:FMZ~Lydia, Créé: 2024-07-11 14:57:15, Mis à jour: 2024-07-12 15:55:53

img

Préface

L'article précédent a introduit le principe et le backtesting du trading par paire,https://www.fmz.com/bbs-topic/10459. Voici un code source pratique basé sur la plate-forme FMZ. La stratégie est simple et claire, adaptée aux débutants. La plate-forme FMZ a récemment mis à jour certaines API pour être plus conviviale pour les stratégies de paires multi-trades. Cet article présentera le code source JavaScript de cette stratégie en détail. Bien que le code de stratégie ne soit que d'une centaine de lignes, il contient tous les aspects nécessaires à une stratégie complète. L'API spécifique peut être consultée dans le document API, qui est très détaillé.https://www.fmz.com/strategy/456143peut être copié directement.

Utilisation de la plateforme FMZ

Si vous n'êtes pas familier avec la plateforme FMZ, je vous recommande fortement de lire ce tutoriel:https://www.fmz.com/bbs-topic/4145Il présente en détail les fonctions de base de la plateforme, ainsi que la manière de déployer un robot à partir de zéro.

Cadre stratégique

La fonction principale est le point d'entrée. La boucle infinie garantit que la stratégie est exécutée en continu, et un petit temps de sommeil est ajouté pour empêcher la fréquence d'accès de dépasser la limite d'échange.

function main(){
    while(true){
        //strategy content
        Sleep(Interval * 1000) //Sleep
    }
}

Données historiques enregistrées

Le robot redémarre à plusieurs reprises pour diverses raisons, telles que des erreurs, des mises à jour de paramètres, des mises à jour de stratégie, etc., et certaines données doivent être enregistrées pour le prochain démarrage. Voici une démonstration de la façon d'enregistrer le capital initial pour calculer les rendements.

let init_eq = 0 //defining initial equity
if(!_G('init_eq')){  //If there is no storage, _G('init_eq') returns null.
    init_eq = total_eq
    _G('init_eq', total_eq) //Since there is no storage, the initial equity is the current equity and is stored here
}else{
    init_eq = _G('init_eq') //If stored, read the value of the initial equity
}

Stratégie de tolérance aux erreurs

Lors de l'obtention de données telles que les positions et les conditions de marché via l'API, des erreurs peuvent être renvoyées pour diverses raisons. Appeler directement les données provoquera l'arrêt de la stratégie en raison d'erreurs, il est donc nécessaire d'avoir un mécanisme tolérant aux erreurs. La fonction _C() réessayera automatiquement après une erreur jusqu'à ce que les données correctes soient renvoyées. Ou vérifiez si les données sont disponibles après le retour.

let pos = _C(exchange.GetPosition, pair)

let ticker_A = exchange.GetTicker(pair_a)
let ticker_B = exchange.GetTicker(pair_b)
if(!ticker_A || !ticker_B){
    continue //If the data is not available, exit the loop.
}

API compatible avec plusieurs devises

Des fonctions comme GetPosition, GetTicker et GetRecords peuvent ajouter un paramètre de paire de négociation pour obtenir les données correspondantes, sans avoir à définir la paire de négociation liée à l'échange, ce qui facilite grandement la compatibilité de plusieurs stratégies de paire de négociation.https://www.fmz.com/bbs-topic/10456Bien sûr, le dernier docker est requis pour le prendre en charge. Si votre docker est trop vieux, vous devez mettre à niveau.

Paramètres de stratégie

  • Pair_A: Paire de trading A qui doit être jumelée pour le trading. Vous devez choisir la paire de trading vous-même. Vous pouvez vous référer à l'introduction et au backtesting dans l'article précédent.
  • Pair_B: Paire de négociation B qui doit être jumelée.
  • Citation: La monnaie de marge de l'échange de contrats à terme, généralement en USDT.
  • Pct: Combien d'écart à ajouter aux positions, voir l'article sur les principes de stratégie pour plus de détails, en raison des frais de traitement et des raisons de glissement, il ne devrait pas être trop petit.
  • La valeur de négociation de l'addition des positions pour chaque écart par rapport à la taille de la grille.
  • Ice_Value: si la valeur de la transaction est trop grande, vous pouvez utiliser la valeur de la commission de l'iceberg pour ouvrir une position.
  • Max_Value: Value maximale des avoirs d'une monnaie unique, afin d'éviter le risque de détention de trop de positions.
  • N: Le paramètre utilisé pour calculer le ratio de prix moyen, l'unité est l'heure, par exemple, 100 représente la moyenne de 100 heures.
  • Intervalle: le temps de sommeil entre chaque cycle de la stratégie.

Notes complètes sur la stratégie

Si vous ne comprenez toujours pas, vous pouvez utiliser la documentation de l'API FMZ, les outils de débogage et les outils de dialogue d'IA couramment utilisés sur le marché pour résoudre vos questions.

function GetPosition(pair){
    let pos = _C(exchange.GetPosition, pair)
    if(pos.length == 0){ //Returns null to indicate no position
        return {amount:0, price:0, profit:0}
    }else if(pos.length > 1){ //The strategy should be set to unidirectional position mode
        throw 'Bidirectional positions are not supported'
    }else{ //For convenience, long positions are positive and short positions are negative
        return {amount:pos[0].Type == 0 ? pos[0].Amount : -pos[0].Amount, price:pos[0].Price, profit:pos[0].Profit}
    }
}

function GetRatio(){
    let kline_A = exchange.GetRecords(Pair_A+"_"+Quote+".swap", 60*60, N) //Hourly K-line
    let kline_B = exchange.GetRecords(Pair_B+"_"+Quote+".swap", 60*60, N)
    let total = 0
    for(let i= Math.min(kline_A.length,kline_B.length)-1; i >= 0; i--){ //Calculate in reverse to avoid the K-line being too short.
        total += kline_A[i].Close / kline_B[i].Close
    }
    return total / Math.min(kline_A.length,kline_B.length)
}

function GetAccount(){
    let account = _C(exchange.GetAccount)
    let total_eq = 0
    if(exchange.GetName == 'Futures_OKCoin'){ //Since the API here is not compatible, only OKX Futures Exchange obtains the total equity currently.
        total_eq = account.Info.data[0].totalEq //The equity information of other exchanges is also included. You can look for it yourself in the exchange API documentation.
    }else{
        total_eq = account.Balance //Temporary use of available balances on other exchanges will cause errors in calculating returns, but will not affect the use of strategies.
    }
    let init_eq = 0
    if(!_G('init_eq')){
        init_eq = total_eq
        _G('init_eq', total_eq)
    }else{
        init_eq = _G('init_eq')
    }
    LogProfit(total_eq - init_eq)
    return total_eq
}

function main(){
    var precision = exchange.GetMarkets() //Get the precision here
    var last_get_ratio_time = Date.now()
    var ratio = GetRatio()
    var total_eq = GetAccount()
    while(true){
        let start_loop_time = Date.now()
        if(Date.now() - last_get_ratio_time > 10*60*1000){ //Update the average price and account information every 10 minutes
            ratio = GetRatio()
            total_eq = GetAccount()
            last_get_ratio_time = Date.now()
        }
        let pair_a = Pair_A+"_"+Quote+".swap" //The trading pair is set as BTC_USDT.swap
        let pair_b = Pair_B+"_"+Quote+".swap"
        let CtVal_a = "CtVal" in precision[pair_a] ? precision[pair_a].CtVal : 1 //Some exchanges use sheets to represent quantity, such as one sheet represents 0.01 coin, so you need to convert.
        let CtVal_b = "CtVal" in precision[pair_b] ? precision[pair_b].CtVal : 1 //No need to include this field
        let position_A = GetPosition(pair_a)
        let position_B = GetPosition(pair_b)
        let ticker_A = exchange.GetTicker(pair_a)
        let ticker_B = exchange.GetTicker(pair_b)
        if(!ticker_A || !ticker_B){ //If the returned data is abnormal, jump out of this loop
            continue
        }
        let diff = (ticker_A.Last / ticker_B.Last - ratio) / ratio //Calculate the ratio of deviation
        let aim_value = - Trade_Value * diff / Pct //Target holding position
        let id_A = null
        let id_B = null
        //The following is the specific logic of opening a position
        if( -aim_value + position_A.amount*CtVal_a*ticker_A.Last > Trade_Value && position_A.amount*CtVal_a*ticker_A.Last > -Max_Value){
            id_A = exchange.CreateOrder(pair_a, "sell", ticker_A.Buy, _N(Ice_Value / (ticker_A.Buy * CtVal_a), precision[pair_a].AmountPrecision))
        }
        if( -aim_value - position_B.amount*CtVal_b*ticker_B.Last > Trade_Value && position_B.amount*CtVal_b*ticker_B.Last < Max_Value){
            id_B = exchange.CreateOrder(pair_b, "buy", ticker_B.Sell, _N(Ice_Value / (ticker_B.Sell * CtVal_b), precision[pair_b].AmountPrecision))
        }
        if( aim_value - position_A.amount*CtVal_a*ticker_A.Last > Trade_Value && position_A.amount*CtVal_a*ticker_A.Last < Max_Value){
            id_A = exchange.CreateOrder(pair_a, "buy", ticker_A.Sell, _N(Ice_Value / (ticker_A.Sell * CtVal_a), precision[pair_a].AmountPrecision))
        }
        if( aim_value + position_B.amount*CtVal_b*ticker_B.Last > Trade_Value &&  position_B.amount*CtVal_b*ticker_B.Last > -Max_Value){
            id_B = exchange.CreateOrder(pair_b, "sell", ticker_B.Buy, _N(Ice_Value / (ticker_B.Buy * CtVal_b), precision[pair_b].AmountPrecision))
        }
        if(id_A){
            exchange.CancelOrder(id_A) //Cancel directly here
        }
        if(id_B){
            exchange.CancelOrder(id_B)
        }
        let table = {
            type: "table",
            title: "trading Information",
            cols: ["initial equity", "current equity", Pair_A+"position", Pair_B+"position", Pair_A+"holding price", Pair_B+"holding price", Pair_A+"profits", Pair_B+"profits", Pair_A+"price", Pair_B+"price", "current price comparison", "average price comparison", "deviation from average price", "loop delay"],
            rows: [[_N(_G('init_eq'),2), _N(total_eq,2), _N(position_A.amount*CtVal_a*ticker_A.Last, 1), _N(position_B.amount*CtVal_b*ticker_B.Last,1),
                _N(position_A.price, precision[pair_a].PircePrecision), _N(position_B.price, precision[pair_b].PircePrecision),
                _N(position_A.profit, 1), _N(position_B.profit, 1), ticker_A.Last, ticker_B.Last,
                _N(ticker_A.Last / ticker_B.Last,6), _N(ratio, 6), _N(diff, 4), (Date.now() - start_loop_time)+"ms"
            ]]
        }
        LogStatus("`" + JSON.stringify(table) + "`") //This function will display a table containing the above information on the robot page.
        Sleep(Interval * 1000) //Sleep time in ms
    }
}

Plus de