Strategie für den doppelten gleitenden Durchschnitt bei digitalen Spotwährungen mit vielen Varianten (Unterricht)

Erstellt in: 2021-12-06 17:53:51, aktualisiert am: 2023-09-15 20:56:36
comments   9
hits   2635

Strategie für den doppelten gleitenden Durchschnitt bei digitalen Spotwährungen mit vielen Varianten (Unterricht)

Strategie für den doppelten gleitenden Durchschnitt bei digitalen Spotwährungen mit vielen Varianten (Unterricht)

Als Reaktion auf die Anfragen der Community-Benutzer hoffen wir, eine vielfältige Strategie mit dualen gleitenden Durchschnitten als Designreferenz bereitstellen zu können. In dieser Ausgabe implementieren wir eine duale gleitende Durchschnittsstrategie für mehrere Sorten. Um das Verständnis der Strategie und das Lernen zu erleichtern, werden Kommentare zum Strategiecode geschrieben. Ermöglichen Sie mehr neuen Studierenden des programmatischen und quantitativen Handels einen schnellen Einstieg.

Strategie für den doppelten gleitenden Durchschnitt bei digitalen Spotwährungen mit vielen Varianten (Unterricht)

Strategisches Denken

Die Logik der Strategie des doppelten gleitenden Durchschnitts ist sehr einfach: Es gibt zwei gleitende Durchschnitte. Ein gleitender Durchschnitt mit einer kleinen Parameterperiode (schnelle Linie) und der andere gleitende Durchschnitt mit einer großen Parameterperiode (langsame Linie). Wenn die beiden Linien ein goldenes Kreuz bilden (die schnelle Linie kreuzt die langsame Linie von unten nach oben), kaufen Sie Long, und wenn die beiden Linien ein totes Kreuz bilden (die schnelle Linie kreuzt die langsame Linie von oben nach unten), verkaufen Sie Short . Für den gleitenden Durchschnitt verwenden wir den gleitenden Durchschnitt EMA.

Wenn die Strategie jedoch für mehrere Sorten entwickelt werden soll, können die Parameter für jede Sorte unterschiedlich sein (verschiedene Sorten verwenden unterschiedliche gleitende Durchschnittsparameter), sodass die Parameter in Form einer „Parametergruppe“ entwickelt werden müssen.

Strategie für den doppelten gleitenden Durchschnitt bei digitalen Spotwährungen mit vielen Varianten (Unterricht)

Die Parameter werden in diesem Zeichenfolgenformat entworfen, wobei jeder Parameter durch Kommas getrennt ist. Diese Zeichenfolgen werden analysiert, wenn die Richtlinie ausgeführt wird. Passen Sie die Ausführungslogik für jedes Produkt (Handelspaar) an. Strategieumfragen erkennen die Marktbedingungen verschiedener Produkte, lösen Handelsbedingungen aus, drucken Diagramme usw. Nachdem alle Sorten abgefragt wurden, werden die Daten zusammengefasst und die Tabelleninformationen in der Statusleiste angezeigt.

Das Strategiedesign ist mit insgesamt nur über 200 Codezeilen sehr einfach und für Anfänger geeignet.

Strategiecode

// 函数作用:取消当前交易对的所有挂单
function cancelAll(e) {
    while (true) {
        var orders = _C(e.GetOrders)
        if (orders.length == 0) {
            break
        } else {
            for (var i = 0 ; i < orders.length ; i++) {
                e.CancelOrder(orders[i].Id, orders[i])
                Sleep(500)
            }
        }
        Sleep(500)
    }
}

// 函数作用:计算实时收益盈亏
function getProfit(account, initAccount, lastPrices) {
    // account为当前账户信息,initAccount为初始账户信息,lastPrices为当前所有品种的最新价格
    var sum = 0
    _.each(account, function(val, key) {
        // 遍历当前所有资产,计算除了USDT以外的资产币差,以及金额差
        if (key != "USDT" && typeof(initAccount[key]) == "number" && lastPrices[key + "_USDT"]) {
            sum += (account[key] - initAccount[key]) * lastPrices[key + "_USDT"]
        }        
    })
    // 返回根据当前价格计算得出的资产盈亏
    return account["USDT"] - initAccount["USDT"] + sum
}

// 函数作用:生成图表配置
function createChartConfig(symbol, ema1Period, ema2Period) {
    // symbol为交易对,ema1Period为第一根EMA均线周期,ema2Period为第二根EMA均线周期
    var chart = {                                        
        __isStock: true,    
        extension: {
                layout: 'single', 
                height: 600, 
        },
        title : { text : symbol},                       
        xAxis: { type: 'datetime'},           
        series : [                                          
            {                                      
                type: 'candlestick',    // K线数据系列                         
                name: symbol,   
                id: symbol,
                data: []                                           
            }, {                                      
                type: 'line',           // EMA数据系列
                name: symbol + ',EMA1:' + ema1Period,          
                data: [],               
            }, {
                type: 'line',           // EMA数据系列
                name: symbol + ',EMA2:' + ema2Period,
                data: []
            }
        ]
    }
    return chart    
}

function main() {
    // 重置所有数据
    if (isReset) {
        _G(null)            // 清空所有持久化记录的数据
        LogReset(1)         // 清空所有日志
        LogProfitReset()    // 清空所有收益日志
        LogVacuum()         // 释放实盘数据库占用的资源
        Log("重置所有数据", "#FF0000")   // 打印信息
    }
    
    // 解析参数
    var arrSymbols = symbols.split(",")             // 逗号分割交易品种字符串
    var arrEma1Periods = ema1Periods.split(",")     // 分割第一根EMA均线的参数字符串
    var arrEma2Periods = ema2Periods.split(",")     // 分割第二根EMA均线的参数字符串
    var arrAmounts = orderAmounts.split(",")        // 分割每个品种的下单量
    var account = {}                                // 用于记录当前资产信息的变量
    var initAccount = {}                            // 用于记录最初资产信息的变量
    var currTradeMsg = {}                           // 用于记录当前BAR是否交易的变量
    var lastPrices = {}                             // 用于记录监控的品种最新价格的变量
    var lastBarTime = {}                            // 用于记录最近一根BAR的时间的变量,用于画图时BAR的更新判断
    var arrChartConfig = []                         // 用于记录图表配置信息,用于画图

    if (_G("currTradeMsg")) {                       // 例如重启时,恢复currTradeMsg数据
        currTradeMsg = _G("currTradeMsg")
        Log("恢复记录", currTradeMsg)
    }

    // 初始化account
    _.each(arrSymbols, function(symbol, index) {
        exchange.SetCurrency(symbol)
        var arrCurrencyName = symbol.split("_")
        var baseCurrency = arrCurrencyName[0]
        var quoteCurrency = arrCurrencyName[1]
        if (quoteCurrency != "USDT") {
            throw "only support quoteCurrency: USDT"
        }
        if (!account[baseCurrency] || !account[quoteCurrency]) {
            cancelAll(exchange)
            var acc = _C(exchange.GetAccount)
            account[baseCurrency] = acc.Stocks
            account[quoteCurrency] = acc.Balance
        }
        
        // 初始化图表相关的数据
        lastBarTime[symbol] = 0
        arrChartConfig.push(createChartConfig(symbol, arrEma1Periods[index], arrEma2Periods[index]))
    })
    if (_G("initAccount")) {
        initAccount = _G("initAccount")
        Log("恢复初始账户记录", initAccount)
    } else {
        // 用当前资产信息,初始化initAccount变量
        _.each(account, function(val, key) {
            initAccount[key] = val
        })
    }
    Log("account:", account, "initAccount:", initAccount)   // 打印资产信息

    // 初始化图表对象
    var chart = Chart(arrChartConfig)
    // 图表重置
    chart.reset()
        
    // 策略主循环逻辑
    while (true) {
        // 遍历所有品种,逐个执行双均线逻辑
        _.each(arrSymbols, function(symbol, index) {
            exchange.SetCurrency(symbol)               // 切换交易对为symbol字符串记录的交易对
            var arrCurrencyName = symbol.split("_")    // 以“_”符号分割交易对
            var baseCurrency = arrCurrencyName[0]      // 交易币的字符串
            var quoteCurrency = arrCurrencyName[1]     // 计价币的字符串

            // 根据index索引,获取当前交易对的EMA均线参数
            var ema1Period = parseFloat(arrEma1Periods[index])
            var ema2Period = parseFloat(arrEma2Periods[index])
            var amount = parseFloat(arrAmounts[index])
            
            // 获取当前交易对的K线数据
            var r = exchange.GetRecords()
            if (!r || r.length < Math.max(ema1Period, ema2Period)) {  // K线长度不足时直接返回
                Sleep(1000)
                return 
            }
            var currBarTime = r[r.length - 1].Time         // 记录当前BAR时间戳
            lastPrices[symbol] = r[r.length - 1].Close     // 记录当前最新价格

            var ema1 = TA.EMA(r, ema1Period)    // 计算EMA指标
            var ema2 = TA.EMA(r, ema2Period)    // 计算EMA指标
            if (ema1.length < 3 || ema2.length < 3) {    // EMA指标数组长度过短,直接返回
                Sleep(1000)
                return 
            }
            var ema1Last2 = ema1[ema1.length - 2]   // 倒数第二BAR上的EMA
            var ema1Last3 = ema1[ema1.length - 3]   // 倒数第三BAR上的EMA
            var ema2Last2 = ema2[ema2.length - 2]
            var ema2Last3 = ema2[ema2.length - 3]

            // 写入图表数据
            var klineIndex = index + 2 * index
            // 遍历K线数据
            for (var i = 0 ; i < r.length ; i++) {
                if (r[i].Time == lastBarTime[symbol]) {         // 画图,更新当前BAR以及指标
                    // 更新
                    chart.add(klineIndex, [r[i].Time, r[i].Open, r[i].High, r[i].Low, r[i].Close], -1)  
                    chart.add(klineIndex + 1, [r[i].Time, ema1[i]], -1)
                    chart.add(klineIndex + 2, [r[i].Time, ema2[i]], -1)
                } else if (r[i].Time > lastBarTime[symbol]) {   // 画图,添加BAR以及指标
                    // 添加
                    lastBarTime[symbol] = r[i].Time             // 更新时间戳
                    chart.add(klineIndex, [r[i].Time, r[i].Open, r[i].High, r[i].Low, r[i].Close])  
                    chart.add(klineIndex + 1, [r[i].Time, ema1[i]])   
                    chart.add(klineIndex + 2, [r[i].Time, ema2[i]])   
                }
            }
            
            if (ema1Last3 < ema2Last3 && ema1Last2 > ema2Last2 && currTradeMsg[symbol] != currBarTime) {
                // 金叉
                var depth = exchange.GetDepth()   // 获取当前订单薄深度数据
                var price = depth.Asks[Math.min(takeLevel, depth.Asks.length)].Price   // 取第10档价格,吃单
                if (depth && price * amount <= account[quoteCurrency]) {   // 获取深度数据正常,有足够资产下单
                    exchange.Buy(price, amount, ema1Last3, ema2Last3, ema1Last2, ema2Last2)   // 下单买入
                    cancelAll(exchange)     // 取消所有挂单
                    var acc = _C(exchange.GetAccount)   // 获取账户资产信息
                    if (acc.Stocks != account[baseCurrency]) {  // 检测账户资产发生变动
                        account[baseCurrency] = acc.Stocks      // 更新资产
                        account[quoteCurrency] = acc.Balance    // 更新资产
                        currTradeMsg[symbol] = currBarTime      // 记录当前BAR已经交易
                        _G("currTradeMsg", currTradeMsg)        // 持久化记录
                        var profit = getProfit(account, initAccount, lastPrices)  // 计算收益
                        if (profit) {
                            LogProfit(profit, account, initAccount)    // 打印收益
                        }
                    }
                }
            } else if (ema1Last3 > ema2Last3 && ema1Last2 < ema2Last2 && currTradeMsg[symbol] != currBarTime) {
                // 死叉
                var depth = exchange.GetDepth()
                var price = depth.Bids[Math.min(takeLevel, depth.Bids.length)].Price
                if (depth && amount <= account[baseCurrency]) {
                    exchange.Sell(price, amount, ema1Last3, ema2Last3, ema1Last2, ema2Last2)
                    cancelAll(exchange)
                    var acc = _C(exchange.GetAccount)
                    if (acc.Stocks != account[baseCurrency]) {
                        account[baseCurrency] = acc.Stocks
                        account[quoteCurrency] = acc.Balance
                        currTradeMsg[symbol] = currBarTime
                        _G("currTradeMsg", currTradeMsg)
                        var profit = getProfit(account, initAccount, lastPrices)
                        if (profit) {
                            LogProfit(profit, account, initAccount)
                        }
                    }
                }
            }            
            Sleep(1000)
        })

        // 状态栏表格变量
        var tbl = {
            type : "table", 
            title : "账户信息",
            cols : [], 
            rows : []
        }
        // 将数据写入状态栏表格结构
        tbl.cols.push("--")
        tbl.rows.push(["初始"])
        tbl.rows.push(["当前"])
        _.each(account, function(val, key) {
            if (typeof(initAccount[key]) == "number") {
                tbl.cols.push(key)
                tbl.rows[0].push(initAccount[key])   // 初始
                tbl.rows[1].push(val)                // 当前
            }            
        })
        // 显示状态栏表格
        LogStatus(_D(), "\n", "profit:", getProfit(account, initAccount, lastPrices), "\n", "`" + JSON.stringify(tbl) + "`")
    }
}

Strategie-Backtesting

Strategie für den doppelten gleitenden Durchschnitt bei digitalen Spotwährungen mit vielen Varianten (Unterricht)

Strategie für den doppelten gleitenden Durchschnitt bei digitalen Spotwährungen mit vielen Varianten (Unterricht)

Strategie für den doppelten gleitenden Durchschnitt bei digitalen Spotwährungen mit vielen Varianten (Unterricht)

Strategie für den doppelten gleitenden Durchschnitt bei digitalen Spotwährungen mit vielen Varianten (Unterricht)

Es ist ersichtlich, dass Transaktionen bei ETH, LTC und ETC basierend auf dem Goldenen Kreuz und dem Toten Kreuz des gleitenden Durchschnitts stattfanden.

Strategie für den doppelten gleitenden Durchschnitt bei digitalen Spotwährungen mit vielen Varianten (Unterricht)

Sie können es auch testen, indem Sie eine simulierte Festplatte ausführen.

Strategie-Quellcode: https://www.fmz.com/strategy/333783

Die Strategie wird nur zum Backtesting und Erlernen der Strategiegestaltung verwendet und sollte im realen Handel mit Vorsicht eingesetzt werden.