3
konzentrieren Sie sich auf
1444
Anhänger

Einführung in den Quellcode der Handelsstrategie für digitale Währungspaare und die neueste API der FMZ-Plattform

Erstellt in: 2024-07-10 16:36:54, aktualisiert am: 2024-07-12 15:53:41
comments   5
hits   2914

Einführung in den Quellcode der Handelsstrategie für digitale Währungspaare und die neueste API der FMZ-Plattform

Vorwort

Im vorherigen Artikel wurden die Prinzipien und das Backtesting des Paarhandels vorgestellt, https://www.fmz.com/digest-topic/10457. Hier ist der praktische Quellcode basierend auf der FMZ-Plattform. Die Strategie ist relativ einfach und klar und für Anfänger zum Lernen geeignet. Die FMZ-Plattform hat kürzlich einige ihrer APIs aktualisiert, um sie benutzerfreundlicher für Multi-Trading-Pair-Strategien zu machen. Dieser Artikel stellt den JavaScript-Quellcode dieser Strategie im Detail vor. Obwohl die Strategie nur hundert Zeilen umfasst, enthält sie alle Aspekte, die für eine vollständige Strategie erforderlich sind. Informationen zu bestimmten APIs finden Sie in der API-Dokumentation, in der diese sehr ausführlich beschrieben wird. Öffentliche Adresse der Strategie: https://www.fmz.com/strategy/456143 kann direkt kopiert werden.

Nutzung der FMZ-Plattform

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

Politischer Rahmen

Nachfolgend sehen Sie das einfachste Strategie-Framework. Die Hauptfunktion ist der Einstiegspunkt. Die Endlosschleife stellt sicher, dass die Strategie kontinuierlich ausgeführt wird, und es wird eine kleine Ruhezeit hinzugefügt, um zu verhindern, dass die Zugriffshäufigkeit das Austauschlimit zu schnell überschreitet.

function main(){
    while(true){
        //策略内容
        Sleep(Interval * 1000) //Sleep
    }
}

Historische Daten aufzeichnen

Der Roboter wird aus verschiedenen Gründen (z. B. aufgrund von Fehlern, Parameteraktualisierungen, Strategieaktualisierungen usw.) wiederholt neu gestartet und einige Daten müssen für die Verwendung beim nächsten Start gespeichert werden. Hier wird gezeigt, wie Sie das Anfangskapital sparen, um es bei der Berechnung der Rendite zu verwenden._Die Funktion G() kann verschiedene Daten speichern._G(Schlüssel, Wert) kann den Wert von Wert speichern und ihn mit _G(Schlüssel) aufrufen, wobei Schlüssel eine Zeichenfolge ist.

let init_eq = 0 //定义初始权益
if(!_G('init_eq')){  //如果没有储存_G('init_eq')返回null
    init_eq = total_eq
    _G('init_eq', total_eq) //由于没有储存,初始权益为当前权益,并在这里储存
}else{
    init_eq = _G('init_eq') //如果储存,读取初始权益的值
}

Strategie Fehlertoleranz

Beim Abrufen von Daten wie Positionen und Marktbedingungen über die API können aus verschiedenen Gründen Fehler zurückgegeben werden. Das direkte Aufrufen der darin enthaltenen Daten führt zu einem Fehler und zum Abbruch der Strategie. Daher ist ein fehlertoleranter Mechanismus erforderlich._Die Funktion C() führt nach einem Fehler automatisch einen erneuten Versuch durch, bis korrekte Daten zurückgegeben werden. Oder prüfen Sie, ob die Daten nach Ihrer Rückkehr 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 //如果数据不可用,就跳出这次循环
}

Mehrwährungskompatible API

Funktionen wie GetPosition, GetTicker und GetRecords können einen Handelspaarparameter hinzufügen, um die entsprechenden Daten zu erhalten, ohne das börsengebundene Handelspaar festlegen zu müssen, was die Kompatibilität mehrerer Handelspaarstrategien erheblich erleichtert. Detaillierte Upgrade-Informationen finden Sie im Artikel: https://www.fmz.com/digest-topic/10451. Natürlich benötigen Sie zur Unterstützung das neueste Hosting. Wenn Ihr Hosting zu alt ist, müssen Sie ein Upgrade durchführen.

Strategieparameter

  • Pair_A Handelswährung A: Handelspaar A, das für den Handel gepaart werden muss. Sie müssen das Handelspaar selbst auswählen. Sie können die Einführung und das Backtesting im vorherigen Artikel nachlesen.
  • Pair_B Handelswährung B: Handelspaar B, das gepaart werden muss
  • Basiswährung der Notierung: Die Marginwährung der Terminbörse, normalerweise USDT
  • Prozentuale Rastergröße: Wie groß ist die Abweichung zum Hinzufügen von Positionen? Weitere Einzelheiten finden Sie im Artikel zu den Strategieprinzipien. Aus Gebühren- und Slippagegründen sollte sie nicht zu klein sein.
  • Trade_Value: Der Transaktionswert beim Hinzufügen einer Position für jede Abweichung von der Rastergröße.
  • Ice_Value: Wenn der Transaktionswert zu groß ist, können Sie den Iceberg-Wert verwenden, um eine Position zu eröffnen. Im Allgemeinen kann er auf denselben Wert wie der Transaktionswert gesetzt werden.
  • Max_Value Maximale Bestände: Maximale Bestände einer einzelnen Währung, um das Risiko zu vermeiden, zu viele Positionen zu halten
  • N Durchschnittspreisparameter: Der Parameter zur Berechnung des Durchschnittspreisverhältnisses. Die Einheit ist Stunde. Beispielsweise stellt 100 den Durchschnitt von 100 Stunden dar.
  • Intervall-Schlafzeit (Sekunden): Die Schlafzeit zwischen jedem Zyklus der Strategie

Vollständige Richtlinienhinweise

Wenn Sie es immer noch nicht verstehen, können Sie zur Lösung Ihrer Fragen die API-Dokumentation, die Debugging-Tools und die häufig auf dem Markt verwendeten KI-Dialogtools von FMZ verwenden.

function GetPosition(pair){
    let pos = _C(exchange.GetPosition, pair)
    if(pos.length == 0){ //返回为空代表没有持仓
        return {amount:0, price:0, profit:0}
    }else if(pos.length > 1){ //策略要设置为单向持仓模式
        throw '不支持双向持仓'
    }else{ //为了方便,多仓持仓量为正,空仓持仓量为负
        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) //小时K线
    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--){ //反过来计算,避免K线长度不够
        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'){ //由于这里的API并不兼容,目前仅OKX期货交易所获取到总权益
        total_eq = account.Info.data[0].totalEq //其他交易所的宗权益Info中也包含,可以自己对着交易所API文档找找
    }else{
        total_eq = account.Balance //其它交易所暂时使用可用余额,会造成计算收益错误,但不影响策略使用
    }
    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() //这里获取精度
    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){ //每10分钟更新下均价和账户信息
            ratio = GetRatio()
            total_eq = GetAccount()
            last_get_ratio_time = Date.now()
        }
        let pair_a = Pair_A+"_"+Quote+".swap" //交易对的设置形如BTC_USDT.swap
        let pair_b = Pair_B+"_"+Quote+".swap"
        let CtVal_a = "CtVal" in precision[pair_a] ? precision[pair_a].CtVal : 1 //有的交易所用张来代表数量,如一张代表0.01个币,因此需要换算下
        let CtVal_b = "CtVal" in precision[pair_b] ? precision[pair_b].CtVal : 1 //不含这个字段的不用张
        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){ //如果返回数据异常,跳出这次循环
            continue
        }
        let diff = (ticker_A.Last / ticker_B.Last - ratio) / ratio //计算偏离的比例
        let aim_value = - Trade_Value * diff / Pct //目标持有的仓位
        let id_A = null
        let id_B = null
        //以下是具体的开仓逻辑
        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) //这里直接撤销
        }
        if(id_B){
            exchange.CancelOrder(id_B)
        }
        let table = {
            type: "table",
            title: "交易信息",
            cols: ["初始权益", "当前权益", Pair_A+"仓位", Pair_B+"仓位", Pair_A+"持仓价", Pair_B+"持仓价", Pair_A+"收益", Pair_B+"收益", Pair_A+"价格", Pair_B+"价格", "当前比价", "平均比价", "偏离均价", "循环延时"],
            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) + "`") //这个函数会在机器人页面显示包含以上信息的表格
        Sleep(Interval * 1000) //休眠时间为ms
    }
}