Giao dịch số lượng tiền điện tử cho người mới bắt đầu - đưa bạn đến gần số lượng tiền điện tử (8)

Tác giả:Lydia., Tạo: 2022-08-10 15:02:37, Cập nhật: 2023-09-19 21:38:29

img

Giao dịch số lượng tiền điện tử cho người mới bắt đầu - đưa bạn đến gần số lượng tiền điện tử (8)

Trong bài viết trước, chúng tôi đã thiết kế một chiến lược giám sát phân tán hợp đồng đa loài cùng nhau. Trong bài viết này, chúng tôi sẽ tiếp tục cải thiện ý tưởng này. Hãy xem ý tưởng này có khả thi hay không, và chạy nó với bot mô phỏng OKEX V5 để xác minh thiết kế chiến lược. Các quy trình này cũng cần phải được trải nghiệm trong quá trình giao dịch tiền điện tử theo chương trình và giao dịch định lượng. Tôi hy vọng rằng người mới bắt đầu có thể tích lũy kinh nghiệm có giá trị.

Cảnh báo spoiler, chiến lược đang chạy, và tôi hơi phấn khích!

img

img

img

Thiết kế tổng thể của chiến lược được thực hiện theo cách đơn giản nhất. Mặc dù các chi tiết không quá đòi hỏi, bạn vẫn có thể học một số mẹo từ mã. Mã chiến lược tổng thể ít hơn 400 dòng, vì vậy nó sẽ không nhàm chán để đọc và hiểu. Tất nhiên, đây chỉ là một bản demo thử nghiệm, mất một thời gian để kiểm tra nó. Vì vậy, những gì tôi muốn nói là: chiến lược hiện tại chỉ thành công trong việc mở các vị trí, và các tình huống khác nhau như đóng một vị trí cần được kiểm tra và xác minh. BUG trong thiết kế chương trình là không thể tránh khỏi, vì vậy kiểm tra và DEBUG rất quan trọng!

Trở lại thiết kế chiến lược, dựa trên mã trong bài viết trước, chiến lược được thêm vào:

  • Thiết kế độ bền dữ liệu (sử dụng chức năng _G để lưu dữ liệu và khôi phục dữ liệu sau khi khởi động lại)
  • Cấu trúc dữ liệu lưới được thêm cho mỗi cặp CFD được theo dõi (được sử dụng để kiểm soát các vị trí mở và đóng phòng ngừa rủi ro)
  • Thực hiện một chức năng phòng ngừa đơn giản để phòng ngừa các vị trí mở và đóng
  • Thêm chức năng mua lại tổng vốn chủ sở hữu để tính toán lợi nhuận và lỗ thay đổi
  • Thêm hiển thị dữ liệu xuất thanh trạng thái.

Trong khi đó, các hợp đồng dài hạn (gần dài hạn) có tỷ lệ phí âm, mà chỉ có thể mong đợi hợp đồng dài hạn để xem liệu nó có thể tăng lợi nhuận tỷ lệ hay không.

Hãy để chiến lược chạy một thời gian ~

Sau khi thử nghiệm trong khoảng 3 ngày, sự biến động lây lan vẫn có thể.

img

img

img

Ở đây chúng ta có thể thấy lợi nhuận của một số tỷ lệ tài trợ.

img

Chia sẻ mã nguồn của chiến lược dưới đây:

var arrNearContractType = strNearContractType.split(",")
var arrFarContractType = strFarContractType.split(",")

var nets = null
var initTotalEquity = null 
var OPEN_PLUS = 1
var COVER_PLUS = 2


function createNet(begin, diff, initAvgPrice, diffUsagePercentage) {
    if (diffUsagePercentage) {
        diff = diff * initAvgPrice
    }
    var oneSideNums = 3
    var up = []
    var down = []
    for (var i = 0 ; i < oneSideNums ; i++) {
        var upObj = {
            sell : false, 
            price : begin + diff / 2 + i * diff
        }
        up.push(upObj)

        var j = (oneSideNums - 1) - i
        var downObj = {
            sell : false,
            price : begin - diff / 2 - j * diff
        }
        if (downObj.price <= 0) {  // Price cannot be less than or equal to 0 
            continue
        }
        down.push(downObj)
    }
    return down.concat(up)
}

function createCfg(symbol) {
    var cfg = {
        extension: {
            layout: 'single', 
            height: 300,
            col: 6
        },
        title: {
            text: symbol
        },
        xAxis: {
            type: 'datetime'
        },
        series: [{
            name: 'plus',
            data: []
        }]
    }
    return cfg
}

function formatSymbol(originalSymbol) {
    var arr = originalSymbol.split("-")
    return [arr[0] + "_" + arr[1], arr[0], arr[1]]
}

function main() {	
    if (isSimulate) {
    	exchange.IO("simulate", true)  // Switch to simulation environment
    	Log("Only OKEX V5 API is supported, switch to OKEX V5 simulation bot:")
    } else {
    	exchange.IO("simulate", false)  // Switch to real bot
    	Log("Only OKEX V5 API is supported, switch to OKEX V5 simulation bot:")
    }    
    if (exchange.GetName() != "Futures_OKCoin") {
    	throw "Support OKEX futures"
    }

    // Initialization
    if (isReset) {
        _G(null)
        LogReset(1)
        LogProfitReset()
        LogVacuum()
        Log("Reset all data", "#FF0000")
    }

    // Initialization marker
    var isFirst = true 

    // Profit print period
    var preProfitPrintTS = 0
    // Total equity
    var totalEquity = 0
    var posTbls = []   // Position table array

    // Declare arrCfg
    var arrCfg = []
    _.each(arrNearContractType, function(ct) {
        arrCfg.push(createCfg(formatSymbol(ct)[0]))
    })
    var objCharts = Chart(arrCfg)
    objCharts.reset()
    
    // Create object
    var exName = exchange.GetName() + "_V5"
    var nearConfigureFunc = $.getConfigureFunc()[exName]
    var farConfigureFunc = $.getConfigureFunc()[exName]
    var nearEx = $.createBaseEx(exchange, nearConfigureFunc)
    var farEx = $.createBaseEx(exchange, farConfigureFunc)

    // Pre-write the contract that require subscriptions
    _.each(arrNearContractType, function(ct) {
        nearEx.pushSubscribeSymbol(ct)
    })
    _.each(arrFarContractType, function(ct) {
        farEx.pushSubscribeSymbol(ct)
    })

    while (true) {
        var ts = new Date().getTime()
        // Obtain market data
        nearEx.goGetTickers()
        farEx.goGetTickers()
        var nearTickers = nearEx.getTickers()
        var farTickers = farEx.getTickers()  
        if (!farTickers || !nearTickers) {
            Sleep(2000)
            continue
        }

        var tbl = {
            type : "table",
            title : "Long term-near term spread",
            cols : ["Trading pair", "long term", "near term", "positive hedging", "negative hedging"],
            rows : []
        }        
        
        var subscribeFarTickers = []
        var subscribeNearTickers = []
        _.each(farTickers, function(farTicker) {
            _.each(arrFarContractType, function(symbol) {
                if (farTicker.originalSymbol == symbol) {
                    subscribeFarTickers.push(farTicker)
                }
            })
        })

        _.each(nearTickers, function(nearTicker) {
            _.each(arrNearContractType, function(symbol) {
                if (nearTicker.originalSymbol == symbol) {
                    subscribeNearTickers.push(nearTicker)
                }
            })
        })

        var pairs = []        
        _.each(subscribeFarTickers, function(farTicker) {
            _.each(subscribeNearTickers, function(nearTicker) {                
                if (farTicker.symbol == nearTicker.symbol) {
                    var pair = {symbol: nearTicker.symbol, nearTicker: nearTicker, farTicker: farTicker, plusDiff: farTicker.bid1 - nearTicker.ask1, minusDiff: farTicker.ask1 - nearTicker.bid1}
                    pairs.push(pair)
                    tbl.rows.push([pair.symbol, farTicker.originalSymbol, nearTicker.originalSymbol, pair.plusDiff, pair.minusDiff])
                    for (var i = 0 ; i < arrCfg.length ; i++) {
                        if (arrCfg[i].title.text == pair.symbol) {
                            objCharts.add([i, [ts, pair.plusDiff]])
                        }                        
                    }
                }
            })
        })

        // Initialization
        if (isFirst) {
            isFirst = false 
            var recoveryNets = _G("nets")
            var recoveryInitTotalEquity = _G("initTotalEquity")
            if (!recoveryNets) {
                // Check positions
                _.each(subscribeFarTickers, function(farTicker) {
                    var pos = farEx.getFuPos(farTicker.originalSymbol, ts)
                    if (pos.length != 0) {
                        Log(farTicker.originalSymbol, pos)
                        throw "Initialized with a position"
                    }
                })
                _.each(subscribeNearTickers, function(nearTicker) {
                    var pos = nearEx.getFuPos(nearTicker.originalSymbol, ts)
                    if (pos.length != 0) {
                        Log(nearTicker.originalSymbol, pos)
                        throw "Initialized with a position"
                    }
                })                
                // Construct nets
                nets = []
                _.each(pairs, function (pair) {
                    farEx.goGetAcc(pair.farTicker.originalSymbol, ts)
                    nearEx.goGetAcc(pair.nearTicker.originalSymbol, ts)
                    var obj = {
                        "symbol" : pair.symbol, 
                        "farSymbol" : pair.farTicker.originalSymbol,
                        "nearSymbol" : pair.nearTicker.originalSymbol,
                        "initPrice" : (pair.nearTicker.ask1 + pair.farTicker.bid1) / 2, 
                        "prePlus" : pair.farTicker.bid1 - pair.nearTicker.ask1,                        
                        "net" : createNet((pair.farTicker.bid1 - pair.nearTicker.ask1), diff, (pair.nearTicker.ask1 + pair.farTicker.bid1) / 2, true), 
                        "initFarAcc" : farEx.getAcc(pair.farTicker.originalSymbol, ts), 
                        "initNearAcc" : nearEx.getAcc(pair.nearTicker.originalSymbol, ts),
                        "farTicker" : pair.farTicker,
                        "nearTicker" : pair.nearTicker,
                        "farPos" : null, 
                        "nearPos" : null,
                    }
                    nets.push(obj)
                })
                var currTotalEquity = getTotalEquity()
                if (currTotalEquity) {
                	initTotalEquity = currTotalEquity
                } else {
                	throw "Initialization to obtain total equity failed!"
                }                
            } else {
                // Recovery
                nets = recoveryNets
                initTotalEquity = recoveryInitTotalEquity
            }
        }

        // Retrieve the grid and check if the trading is triggered
        _.each(nets, function(obj) {
            var currPlus = null
            _.each(pairs, function(pair) {
                if (pair.symbol == obj.symbol) {
                    currPlus = pair.plusDiff
                    obj.farTicker = pair.farTicker
                    obj.nearTicker = pair.nearTicker
                }
            })
            if (!currPlus) {
                Log("Not found", obj.symbol, " 's spread")
                return 
            }

            // Check grid, add dynamically
            while (currPlus >= obj.net[obj.net.length - 1].price) {
                obj.net.push({
                    sell : false,
                    price : obj.net[obj.net.length - 1].price + diff * obj.initPrice,
                })
            }
            while (currPlus <= obj.net[0].price) {
                var price = obj.net[0].price - diff * obj.initPrice
                if (price <= 0) {
                    break
                }
                obj.net.unshift({
                    sell : false,
                    price : price,
                })
            }
            
            // Search grid
            for (var i = 0 ; i < obj.net.length - 1 ; i++) {
                var p = obj.net[i]
                var upP = obj.net[i + 1]
                if (obj.prePlus <= p.price && currPlus > p.price && !p.sell) {
                    if (hedge(nearEx, farEx, obj.nearSymbol, obj.farSymbol, obj.nearTicker, obj.farTicker, hedgeAmount, OPEN_PLUS)) {   // Positive hedging opening position
                        p.sell = true 
                    }
                } else if (obj.prePlus >= p.price && currPlus < p.price && upP.sell) {
                    if (hedge(nearEx, farEx, obj.nearSymbol, obj.farSymbol, obj.nearTicker, obj.farTicker, hedgeAmount, COVER_PLUS)) {   // Positive hedging closing position
                        upP.sell = false 
                    }
                }
            }
            obj.prePlus = currPlus  // Record the current spread as a cache, and use it to judge whether it's above the SMA or below the SMA next time
            // Add other chart outputs
        })        

        if (ts - preProfitPrintTS > 1000 * 60 * 5) {   // Print every 5 minutes       
        	var currTotalEquity = getTotalEquity()
        	if (currTotalEquity) {
        		totalEquity = currTotalEquity
        		LogProfit(totalEquity - initTotalEquity, "&")   // Print dynamic equity profits
        	}

        	// Check positions
        	posTbls = []  // Reset, update
            _.each(nets, function(obj) {
                var currFarPos = farEx.getFuPos(obj.farSymbol)
                var currNearPos = nearEx.getFuPos(obj.nearSymbol)
                if (currFarPos && currNearPos) {
                	obj.farPos = currFarPos
                	obj.nearPos = currNearPos
                }
                var posTbl = {
                	"type" : "table", 
                	"title" : obj.symbol, 
                	"cols" : ["contract code", "amount", "price"], 
                	"rows" : [] 
                }
                _.each(obj.farPos, function(pos) {
                    posTbl.rows.push([pos.symbol, pos.amount, pos.price])
                })  
                _.each(obj.nearPos, function(pos) {
                	posTbl.rows.push([pos.symbol, pos.amount, pos.price])
                })
                posTbls.push(posTbl)
            })

            preProfitPrintTS = ts
        }

        // Show grid
        var netTbls = []
        _.each(nets, function(obj) {
            var netTbl = {
            	"type" : "table",
            	"title" : obj.symbol,
            	"cols" : ["grid"],
            	"rows" : []
            }
            _.each(obj.net, function(p) {
            	var color = ""
            	if (p.sell) {
            		color = "#00FF00"
            	}
            	netTbl.rows.push([JSON.stringify(p) + color])
            })
            netTbl.rows.reverse()
            netTbls.push(netTbl)
        })

        LogStatus(_D(), "total equity:", totalEquity, "initial total equity:", initTotalEquity, "floating profit and loss:", totalEquity - initTotalEquity, 
        	"\n`" + JSON.stringify(tbl) + "`" + "\n`" + JSON.stringify(netTbls) + "`" + "\n`" + JSON.stringify(posTbls) + "`")
        Sleep(interval)
    }
}

function getTotalEquity() {
    var totalEquity = null 
    var ret = exchange.IO("api", "GET", "/api/v5/account/balance", "ccy=USDT")
    if (ret) {
        try {
        	totalEquity = parseFloat(ret.data[0].details[0].eq)
        } catch(e) {
        	Log("Failed to obtain the total equity of the account!")
        	return null
        }
    }
    return totalEquity
}

function hedge(nearEx, farEx, nearSymbol, farSymbol, nearTicker, farTicker, amount, tradeType) {
    var farDirection = null
    var nearDirection = null
    if (tradeType == OPEN_PLUS) {
        farDirection = farEx.OPEN_SHORT
        nearDirection = nearEx.OPEN_LONG
    } else {
        farDirection = farEx.COVER_SHORT
        nearDirection = nearEx.COVER_LONG
    }
    var nearSymbolInfo = nearEx.getSymbolInfo(nearSymbol) 
    var farSymbolInfo = farEx.getSymbolInfo(farSymbol)
    nearAmount = nearEx.calcAmount(nearSymbol, nearDirection, nearTicker.ask1, amount * nearSymbolInfo.multiplier)
    farAmount = farEx.calcAmount(farSymbol, farDirection, farTicker.bid1, amount * farSymbolInfo.multiplier)
    if (!nearAmount || !farAmount) {
        Log(nearSymbol, farSymbol, "Order amount calculation error:", nearAmount, farAmount)
        return 
    }
    nearEx.goGetTrade(nearSymbol, nearDirection, nearTicker.ask1, nearAmount[0])
    farEx.goGetTrade(farSymbol, farDirection, farTicker.bid1, farAmount[0])
    var nearIdMsg = nearEx.getTrade()
    var farIdMsg = farEx.getTrade()
    return [nearIdMsg, farIdMsg]
}

function onexit() {
	Log("Execute the tail function", "#FF0000")
    _G("nets", nets)
    _G("initTotalEquity", initTotalEquity)
    Log("save the data:", _G("nets"), _G("initTotalEquity"))
}

img

Phát biểu công khai về chiến lược:https://www.fmz.com/strategy/288559

Chiến lược sử dụng thư viện lớp mẫu được viết bởi chính tôi, không công khai vì nó không quá tốt. Mã nguồn chiến lược trên có thể được sửa đổi mà không cần sử dụng mẫu này.

Nếu bạn quan tâm, bạn có thể sử dụng một OKEX V5 robot mô phỏng để kiểm tra. Nhân tiện, chiến lược này không thể kiểm tra lại được.


Có liên quan

Thêm nữa