數字貨幣合約簡易跟單機器人


往期文章中我們實現了一個簡單的現貨跟單機器人,今天我們實現一個合約版的簡易跟單機器人。

設計思路

合約版的跟單機器人和現貨版的有很大區別了,現貨跟單主要可以通過監控賬戶資產變化實現。期貨版的則需要監控賬戶持倉變動情況。所以期貨版的情況就復雜一些,因為期貨有多頭持倉,空頭持倉,有不同的合約。需要對這一系列的細節處理。核心思路就是監控持倉變動量。根據持倉變動量去觸發跟單動作。最初設計時是計划把多頭、空頭一起處理,發現這樣處理會變得很復雜。分析問題之后決定對於持倉的多頭、空頭分開處理。

策略實現

策略參數:

支持回測,可以直接使用默認設置回測觀察。

策略源碼

/*backtest
start: 2021-03-18 00:00:00
end: 2021-04-07 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Futures_OKCoin","currency":"BTC_USD"},{"eid":"Futures_OKCoin","currency":"BTC_USD"},{"eid":"Futures_OKCoin","currency":"BTC_USD"}]
*/

function test() {
    // 測試函數
    var ts = new Date().getTime()    
    if (ts % (1000 * 60 * 60 * 6) > 1000 * 60 * 60 * 5.5) {
        Sleep(1000 * 60 * 10)
        var nowPosAmount = getPosAmount(_C(exchange.GetPosition), refCt)
        var longPosAmount = nowPosAmount.long
        var shortPosAmount = nowPosAmount.short
        var x = Math.random()
        if (x > 0.7) {
            exchange.SetDirection("buy")
            exchange.Buy(-1, _N(Math.max(1, x * 10), 0), "參考賬戶測試開單#FF0000")
        } else if(x < 0.2) {
            exchange.SetDirection("sell")
            exchange.Sell(-1, _N(Math.max(1, x * 10), 0), "參考賬戶測試開單#FF0000")
        } else if(x >= 0.2 && x <= 0.5 && longPosAmount > 4) {
            exchange.SetDirection("closebuy")
            exchange.Sell(-1, longPosAmount, "參考賬戶測試平倉#FF0000")
        } else if(shortPosAmount > 4) {
            exchange.SetDirection("closesell")
            exchange.Buy(-1, _N(shortPosAmount / 2, 0), "參考賬戶測試平倉#FF0000")
        }
    }
}

function getPosAmount(pos, ct) {
    var longPosAmount = 0
    var shortPosAmount = 0
    _.each(pos, function(ele) {
        if (ele.ContractType == ct && ele.Type == PD_LONG) {
            longPosAmount = ele.Amount
        } else if (ele.ContractType == ct && ele.Type == PD_SHORT) {
            shortPosAmount = ele.Amount
        }
    })
    return {long: longPosAmount, short: shortPosAmount}
}

function trade(e, ct, type, delta) {
    var nowPosAmount = getPosAmount(_C(e.GetPosition), ct)
    var nowAmount = type == PD_LONG ? nowPosAmount.long : nowPosAmount.short
    if (delta > 0) {
        // 開倉
        var tradeFunc = type == PD_LONG ? e.Buy : e.Sell
        e.SetDirection(type == PD_LONG ? "buy" : "sell")
        tradeFunc(-1, delta)
    } else if (delta < 0) {
        // 平倉
        var tradeFunc = type == PD_LONG ? e.Sell : e.Buy
        e.SetDirection(type == PD_LONG ? "closebuy" : "closesell")
        if (nowAmount <= 0) {
            Log("未檢測到持倉")
            return 
        }
        tradeFunc(-1, Math.min(nowAmount, Math.abs(delta)))
    } else {
        throw "錯誤"
    }
}

function main() {
    LogReset(1)
    if (exchanges.length < 2) {
        throw "沒有跟單的交易所"
    }
    var exName = exchange.GetName()
    // 檢測參考交易所
    if (!exName.includes("Futures_")) {
        throw "僅支持期貨跟單"
    }
    Log("開始監控", exName, "交易所", "#FF0000")
    
    // 檢測跟單交易所
    for (var i = 1 ; i < exchanges.length ; i++) {
        if (exchanges[i].GetName() != exName) {
            throw "跟單的期貨交易所和參考交易所不同!"
        }
    }
    
    // 設置交易對、合約
    _.each(exchanges, function(e) {
        if (!IsVirtual()) {
            e.SetCurrency(refCurrency)
            if (isSimulate) {
                if (e.GetName() == "Futures_OKCoin") {
                    e.IO("simulate", true)
                }
            }
        }
        e.SetContractType(refCt)
    })

    var initRefPosAmount = getPosAmount(_C(exchange.GetPosition), refCt)
    while(true) {
        if (IsVirtual()) {    // 回測時才模擬
            test()            // 測試函數,模擬參考賬戶主動交易,觸發跟單賬戶跟單        
        }
        Sleep(5000)
        var nowRefPosAmount = getPosAmount(_C(exchange.GetPosition), refCt)
        var tbl = {
            type : "table", 
            title : "持倉",
            cols : ["名稱", "標簽", "多倉", "空倉", "賬戶資產(Stocks)", "賬戶資產(Balance)"],
            rows : []
        }
        _.each(exchanges, function(e) {
            var pos = getPosAmount(_C(e.GetPosition), refCt)
            var acc = _C(e.GetAccount)
            tbl.rows.push([e.GetName(), e.GetLabel(), pos.long, pos.short, acc.Stocks, acc.Balance])
        })
        LogStatus(_D(), "\n`" + JSON.stringify(tbl) + "`")
        
        // 計算倉位變動量
        var longPosDelta = nowRefPosAmount.long - initRefPosAmount.long
        var shortPosDelta = nowRefPosAmount.short - initRefPosAmount.short

        // 檢測變動
        if (longPosDelta == 0 && shortPosDelta == 0) {
            continue
        } else {
            // 檢測到倉位變動
            for (var i = 1 ; i < exchanges.length ; i++) {
                // 執行多頭動作
                if (longPosDelta != 0) {
                    Log(exchanges[i].GetName(), exchanges[i].GetLabel(), "執行多頭跟單,變動量:", longPosDelta)
                    trade(exchanges[i], refCt, PD_LONG, longPosDelta)
                }
                // 執行空頭動作
                if (shortPosDelta != 0) {
                    Log(exchanges[i].GetName(), exchanges[i].GetLabel(), "執行空頭跟單,變動量:", shortPosDelta)
                    trade(exchanges[i], refCt, PD_SHORT, shortPosDelta)
                }
            }
        }

        // 執行跟單操作后,更新
        initRefPosAmount = nowRefPosAmount
    }
}

測試

鑒於OKEX更新V5接口之后,可以使用OKEX的模擬盤,我就使用兩個OKEX的模擬盤API KEY非常方便的進行測試。第一個添加的交易所對象為參考交易所,跟單交易所都是跟着這個交易所賬戶去操作的。在OKEX模擬盤頁面,參考交易所賬戶上手動下3張ETH的季度幣本位合約。

可以看到實盤就檢測到了參考交易所賬戶持倉的變動,進而跟隨操作。

我們再來平掉2張剛才開的合約倉位試下,平倉之后的持倉如圖:

實盤跟隨操作,平掉2張合約。

本策略設計本着簡單易懂的方式,沒有做優化,完善的部分還需要處理跟單時的資產檢測等細節,為了設計簡便,跟單用了市價單。策略僅僅提供學習思路,實盤根據需求自行優化。策略地址:https://www.fmz.com/strategy/270012

歡迎留言。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM