Die Ressourcen sind geladen. Beförderung...

Einführung in den Quellcode der Digital Currency Pair Trading Strategy und die neueste API der FMZ-Plattform

Schriftsteller:FMZ~Lydia, Erstellt: 2024-07-11 14:57:15, aktualisiert: 2024-07-12 15:55:53

img

Vorwort

Der vorherige Artikel führte das Prinzip und das Backtesting des Paarhandels ein.https://www.fmz.com/bbs-topic/10459. Hier ist ein praktischer Quellcode, der auf der FMZ-Plattform basiert. Die Strategie ist einfach und klar, geeignet für Anfänger. Die FMZ-Plattform hat kürzlich einige APIs aktualisiert, um für Multi-Trading-Paar-Strategien freundlicher zu sein. In diesem Artikel wird der JavaScript-Quellcode dieser Strategie im Detail vorgestellt. Obwohl der Strategie-Code nur hundert Zeilen beträgt, enthält er alle Aspekte, die für eine vollständige Strategie erforderlich sind. Die spezifische API kann im API-Dokument angezeigt werden, das sehr detailliert ist.https://www.fmz.com/strategy/456143kann direkt kopiert werden.

FMZ-Plattformnutzung

Wenn Sie mit der FMZ-Plattform nicht vertraut sind, empfehle ich Ihnen dringend, dieses Tutorial zu lesen:https://www.fmz.com/bbs-topic/4145Er stellt die grundlegenden Funktionen der Plattform im Detail vor und zeigt, wie man einen Roboter von Grund auf einsetzt.

Strategischer Rahmen

Die unendliche Schleife sorgt dafür, dass die Strategie kontinuierlich ausgeführt wird, und eine kleine Schlafzeit wird hinzugefügt, um zu verhindern, dass die Zugriffsfrequenz das Austauschlimit überschreitet.

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

Aufzeichnen historischer Daten

Der Roboter wird aus verschiedenen Gründen wiederholt neu gestartet, z. B. Fehler, Parameteraktualisierungen, Strategieaktualisierungen usw., und einige Daten müssen für den nächsten Start gespeichert werden. Hier ist eine Demonstration, wie man das anfängliche Eigenkapital für die Berechnung der Rendite speichert. Die _G() -Funktion kann verschiedene Daten speichern. _G(key, value) kann den Wert des Wertes speichern und mit _G(key ausrufen, wobei key eine Zeichenfolge ist.

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
}

Strategie Fehlerverträglichkeit

Bei der Erfassung von Daten wie Positionen und Marktbedingungen über die API können aus verschiedenen Gründen Fehler zurückgegeben werden. Der direkte Aufruf der Daten führt dazu, dass die Strategie aufgrund von Fehlern gestoppt wird, so dass ein fehlertoleranter Mechanismus benötigt wird. Die _C() -Funktion wird nach einem Fehler automatisch erneut versucht, bis die richtigen Daten zurückgegeben werden. Oder überprüfen, ob die Daten nach der Rückgabe verfügbar sind.

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.
}

Mehrwährungs-kompatible API

Funktionen wie GetPosition, GetTicker und GetRecords können einen Handelspaarparameter hinzufügen, um die entsprechenden Daten zu erhalten, ohne das exchangegebundene Handelspaar festlegen zu müssen, was die Kompatibilität mehrerer Handelspaarstrategien erheblich erleichtert.https://www.fmz.com/bbs-topic/10456Wenn Ihr Docker zu alt ist, müssen Sie ein Upgrade durchführen.

Strategieparameter

  • Pair_A: Handelspaar A, das für den Handel gepaart werden muss. Sie müssen das Handelspaar selbst auswählen. Sie können sich auf die Einführung und das Backtesting im vorherigen Artikel beziehen.
  • Pair_B: Handelspaar B, das gepaart werden muss.
  • Quote: Die Marginwährung der Futures-Börse, in der Regel in USDT.
  • Pct: Wie viel Abweichung zur Zugabe von Positionen, siehe Artikel über Strategieprinzipien für Details, aufgrund von Handling-Gebühren und Ausrutschgründe, sollte es nicht zu klein sein.
  • Handelswert: Der Handelswert der Zugabe von Positionen für jede Abweichung von der Gittergröße.
  • Ice_Value: Wenn der Transaktionswert zu groß ist, können Sie den Eisberg-Provisionswert verwenden, um eine Position zu eröffnen.
  • Max_Value: Höchstbestand an einer einzigen Währung, um das Risiko zu vermeiden, zu viele Positionen zu halten.
  • N: Der Parameter, der zur Berechnung des durchschnittlichen Preisverhältnisses verwendet wird, ist die Stundeneinheit, z. B. 100 entspricht dem Durchschnitt von 100 Stunden.
  • Intervall: Die Schlafzeit zwischen jedem Zyklus der Strategie.

Vollständige Strategieerläuterungen

Wenn Sie immer noch nicht verstehen, können Sie FMZs API-Dokumentation, Debugging-Tools und allgemein verwendete KI-Dialog-Tools auf dem Markt verwenden, um Ihre Fragen zu lösen.

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

Mehr