
Da die Absicherungsfrequenz der Spot-Futures-Absicherungsstrategie nicht besonders hoch ist, kann sie tatsächlich manuell durchgeführt werden. Es ist jedoch umständlich, zwischen Börsenseiten zu wechseln, Preise zu beobachten und Preisunterschiede manuell zu berechnen. Darüber hinaus möchten Sie manchmal möglicherweise mehr Varianten anzeigen, sodass es nicht erforderlich ist, mehrere Monitore zu haben, um die Marktbedingungen anzuzeigen. Kann auf diese Weise eine halbautomatische Strategie verwendet werden, um die Anforderung des manuellen Betriebs zu erfüllen? Es ist besser, eine Vielzahl von Sorten zu haben, oh! Ja, am besten wäre es, wenn Sie Positionen mit einem Klick eröffnen und schließen könnten. Oh! Ja, eine Positionsanzeige muss auch vorhanden sein…
Wenn Sie Bedarf haben, handeln Sie sofort!
Es ist mit weniger als 600 Codezeilen etwas wortreich.
function createManager(fuEx, spEx, symbolPairs, cmdHedgeAmount, fuMarginLevel, fuMarginReservedRatio) {
var self = {}
self.fuEx = fuEx
self.spEx = spEx
self.symbolPairs = symbolPairs
self.pairs = []
self.fuExTickers = null
self.spExTickers = null
self.tickerUpdateTS = 0
self.fuMarginLevel = fuMarginLevel
self.fuMarginReservedRatio = fuMarginReservedRatio
self.cmdHedgeAmount = cmdHedgeAmount
self.preUpdateAccTS = 0
self.accAndPosUpdateCount = 0
self.profit = []
self.allPairs = []
self.PLUS = 0
self.MINUS = 1
self.COVER_PLUS = 2
self.COVER_MINUS = 3
self.arrTradeTypeDesc = ["正套", "反套", "平正套", "平反套"]
self.updateTickers = function() {
self.fuEx.goGetTickers()
self.spEx.goGetTickers()
var fuExTickers = self.fuEx.getTickers()
var spExTickers = self.spEx.getTickers()
if (!fuExTickers || !spExTickers) {
return null
}
self.fuExTickers = fuExTickers
self.spExTickers = spExTickers
self.tickerUpdateTS = new Date().getTime()
return true
}
self.hedge = function(index, fuSymbol, spSymbol, tradeType, amount) {
var fe = self.fuEx
var se = self.spEx
var pair = self.pairs[index]
var timeStamp = new Date().getTime()
var fuDirection = null
var spDirection = null
var fuPrice = null
var spPrice = null
if (tradeType == self.PLUS) {
fuDirection = fe.OPEN_SHORT
spDirection = se.OPEN_LONG
fuPrice = pair.fuTicker.bid1
spPrice = pair.spTicker.ask1
} else if (tradeType == self.MINUS) {
fuDirection = fe.OPEN_LONG
spDirection = se.OPEN_SHORT
fuPrice = pair.fuTicker.ask1
spPrice = pair.spTicker.bid1
} else if (tradeType == self.COVER_PLUS) {
fuDirection = fe.COVER_SHORT
spDirection = se.COVER_LONG
fuPrice = pair.fuTicker.ask1
spPrice = pair.spTicker.bid1
} else if (tradeType == self.COVER_MINUS) {
fuDirection = fe.COVER_LONG
spDirection = se.COVER_SHORT
fuPrice = pair.fuTicker.bid1
spPrice = pair.spTicker.ask1
} else {
throw "unknow tradeType!"
}
fe.goGetAcc(fuSymbol, timeStamp)
se.goGetAcc(spSymbol, timeStamp)
var nowFuAcc = fe.getAcc(fuSymbol, timeStamp)
var nowSpAcc = se.getAcc(spSymbol, timeStamp)
if (!nowFuAcc || !nowSpAcc) {
Log(fuSymbol, spSymbol, ",获取账户数据失败")
return
}
pair.nowFuAcc = nowFuAcc
pair.nowSpAcc = nowSpAcc
var nowFuPos = fe.getFuPos(fuSymbol, timeStamp)
var nowSpPos = se.getSpPos(spSymbol, spPrice, pair.initSpAcc, pair.nowSpAcc)
if (!nowFuPos || !nowSpPos) {
Log(fuSymbol, spSymbol, ",获取持仓数据失败")
return
}
pair.nowFuPos = nowFuPos
pair.nowSpPos = nowSpPos
var fuAmount = amount
var spAmount = amount
if (tradeType == self.PLUS || tradeType == self.MINUS) {
if (nowFuAcc.Balance < (pair.initFuAcc.Balance + pair.initFuAcc.FrozenBalance) * self.fuMarginReservedRatio + (fuAmount * fuPrice / self.fuMarginLevel)) {
Log(pair.fuSymbol, "保证金不足!", "本次计划使用", (fuAmount * fuPrice / self.fuMarginLevel), "当前可用:", nowFuAcc.Balance,
"计划预留:", (pair.initFuAcc.Balance + pair.initFuAcc.FrozenBalance) * self.fuMarginReservedRatio)
return
}
if ((tradeType == self.PLUS && nowSpAcc.Balance < spAmount * spPrice)) {
Log(pair.spSymbol, "资金不足!", "本次买入计划使用", spAmount * spPrice, "当前可用:", nowSpAcc.Balance)
return
} else if (tradeType == self.MINUS && nowSpAcc.Stocks < spAmount) {
Log(pair.spSymbol, "资金不足!", "本次卖出计划使用", spAmount, "当前可用:", nowSpAcc.Stocks)
return
}
} else {
var fuLongPos = self.getLongPos(nowFuPos)
var fuShortPos = self.getShortPos(nowFuPos)
var spLongPos = self.getLongPos(nowSpPos)
var spShortPos = self.getShortPos(nowSpPos)
if ((tradeType == self.COVER_PLUS && !fuShortPos) || (tradeType == self.COVER_MINUS && !fuLongPos)) {
Log(fuSymbol, spSymbol, ",期货没有对应持仓!")
return
} else if (tradeType == self.COVER_PLUS && Math.abs(fuShortPos.amount) < fuAmount) {
fuAmount = Math.abs(fuShortPos.amount)
} else if (tradeType == self.COVER_MINUS && Math.abs(fuLongPos.amount) < fuAmount) {
fuAmount = Math.abs(fuLongPos.amount)
}
if ((tradeType == self.COVER_PLUS && !spLongPos) || (tradeType == self.COVER_MINUS && !spShortPos)) {
Log(fuSymbol, spSymbol, ",现货没有对应持仓!")
return
} else if (tradeType == self.COVER_PLUS && Math.min(Math.abs(spLongPos.amount), nowSpAcc.Stocks) < spAmount) {
spAmount = Math.min(Math.abs(spLongPos.amount), nowSpAcc.Stocks)
} else if (tradeType == self.COVER_MINUS && Math.min(Math.abs(spShortPos.amount), nowSpAcc.Balance / spPrice) < spAmount) {
spAmount = Math.min(Math.abs(spShortPos.amount), nowSpAcc.Balance / spPrice)
}
}
fuAmount = fe.calcAmount(fuSymbol, fuDirection, fuPrice, fuAmount)
spAmount = se.calcAmount(spSymbol, spDirection, spPrice, spAmount)
if (!fuAmount || !spAmount) {
Log(fuSymbol, spSymbol, "下单量计算错误:", fuAmount, spAmount)
return
} else {
fuAmount = fe.calcAmount(fuSymbol, fuDirection, fuPrice, fuAmount[1])
spAmount = se.calcAmount(spSymbol, spDirection, spPrice, Math.min(fuAmount[1], spAmount[1]))
if (!fuAmount || !spAmount) {
Log(fuSymbol, spSymbol, "下单量计算错误:", fuAmount, spAmount)
return
}
}
Log("合约代码:", fuSymbol + "/" + spSymbol, "方向:", self.arrTradeTypeDesc[tradeType], "差价:", fuPrice - spPrice, "期货数量:", fuAmount, "现货数量:", spAmount, "@")
fe.goGetTrade(fuSymbol, fuDirection, fuPrice, fuAmount[0])
se.goGetTrade(spSymbol, spDirection, spPrice, spAmount[0])
var feIdMsg = fe.getTrade()
var seIdMsg = se.getTrade()
return [feIdMsg, seIdMsg]
}
self.process = function() {
var nowTS = new Date().getTime()
if(!self.updateTickers()) {
return
}
_.each(self.pairs, function(pair, index) {
var fuTicker = null
var spTicker = null
_.each(self.fuExTickers, function(ticker) {
if (ticker.originalSymbol == pair.fuSymbol) {
fuTicker = ticker
}
})
_.each(self.spExTickers, function(ticker) {
if (ticker.originalSymbol == pair.spSymbol) {
spTicker = ticker
}
})
if (fuTicker && spTicker) {
pair.canTrade = true
} else {
pair.canTrade = false
}
fuTicker = fuTicker ? fuTicker : {}
spTicker = spTicker ? spTicker : {}
pair.fuTicker = fuTicker
pair.spTicker = spTicker
pair.plusDiff = fuTicker.bid1 - spTicker.ask1
pair.minusDiff = fuTicker.ask1 - spTicker.bid1
if (pair.plusDiff && pair.minusDiff) {
pair.plusDiff = _N(pair.plusDiff, Math.max(self.fuEx.judgePrecision(fuTicker.bid1), self.spEx.judgePrecision(spTicker.ask1)))
pair.minusDiff = _N(pair.minusDiff, Math.max(self.fuEx.judgePrecision(fuTicker.ask1), self.spEx.judgePrecision(spTicker.bid1)))
}
if (nowTS - self.preUpdateAccTS > 1000 * 60 * 5) {
self.fuEx.goGetAcc(pair.fuSymbol, nowTS)
self.spEx.goGetAcc(pair.spSymbol, nowTS)
var fuAcc = self.fuEx.getAcc(pair.fuSymbol, nowTS)
var spAcc = self.spEx.getAcc(pair.spSymbol, nowTS)
if (fuAcc) {
pair.nowFuAcc = fuAcc
}
if (spAcc) {
pair.nowSpAcc = spAcc
}
var nowFuPos = self.fuEx.getFuPos(pair.fuSymbol, nowTS)
var nowSpPos = self.spEx.getSpPos(pair.spSymbol, (pair.spTicker.ask1 + pair.spTicker.bid1) / 2, pair.initSpAcc, pair.nowSpAcc)
if (nowFuPos && nowSpPos) {
pair.nowFuPos = nowFuPos
pair.nowSpPos = nowSpPos
self.keepBalance(pair)
} else {
Log(pair.fuSymbol, pair.spSymbol, "组合仓位更新失败,nowFuPos:", nowFuPos, " nowSpPos:", nowSpPos)
}
self.accAndPosUpdateCount++
}
})
if (nowTS - self.preUpdateAccTS > 1000 * 60 * 5) {
self.preUpdateAccTS = nowTS
self.profit = self.calcProfit()
LogProfit(self.profit[0], "期货:", self.profit[1], "现货:", self.profit[2], "&") // 打印总收益曲线,使用&字符不打印收益日志
}
var cmd = GetCommand()
if(cmd) {
Log("交互命令:", cmd)
var arr = cmd.split(":")
if(arr[0] == "plus") {
var pair = self.pairs[parseFloat(arr[1])]
self.hedge(parseFloat(arr[1]), pair.fuSymbol, pair.spSymbol, self.PLUS, self.cmdHedgeAmount)
} else if (arr[0] == "cover_plus") {
var pair = self.pairs[parseFloat(arr[1])]
self.hedge(parseFloat(arr[1]), pair.fuSymbol, pair.spSymbol, self.COVER_PLUS, self.cmdHedgeAmount)
}
}
LogStatus("当前时间:", _D(), " 数据更新时间:", _D(self.tickerUpdateTS), "持仓账户更新计数:", self.accAndPosUpdateCount, "\n", "盈亏:", self.profit[0], " 期货盈亏:", self.profit[1],
" 现货盈亏:", self.profit[2], "\n`" + JSON.stringify(self.returnTbl()) + "`", "\n`" + JSON.stringify(self.returnPosTbl()) + "`")
}
self.keepBalance = function (pair) {
var nowFuPos = pair.nowFuPos
var nowSpPos = pair.nowSpPos
var fuLongPos = self.getLongPos(nowFuPos)
var fuShortPos = self.getShortPos(nowFuPos)
var spLongPos = self.getLongPos(nowSpPos)
var spShortPos = self.getShortPos(nowSpPos)
if (fuLongPos || spShortPos) {
Log("不支持反套")
}
if (fuShortPos || spLongPos) {
var fuHoldAmount = fuShortPos ? fuShortPos.amount : 0
var spHoldAmount = spLongPos ? spLongPos.amount : 0
var sum = fuHoldAmount + spHoldAmount
if (sum > 0) {
var spAmount = self.spEx.calcAmount(pair.spSymbol, self.spEx.COVER_LONG, pair.spTicker.bid1, Math.abs(sum), true)
if (spAmount) {
Log(pair.fuSymbol, pair.spSymbol, "现货头寸多出", Math.abs(sum), "fuShortPos:", fuShortPos, "spLongPos:", spLongPos)
self.spEx.goGetTrade(pair.spSymbol, self.spEx.COVER_LONG, pair.spTicker.bid1, spAmount[0])
var seIdMsg = self.spEx.getTrade()
}
} else if (sum < 0) {
var fuAmount = self.fuEx.calcAmount(pair.fuSymbol, self.fuEx.COVER_SHORT, pair.fuTicker.ask1, Math.abs(sum), true)
if (fuAmount) {
Log(pair.fuSymbol, pair.spSymbol, "期货头寸多出", Math.abs(sum), "fuShortPos:", fuShortPos, "spLongPos:", spLongPos)
self.fuEx.goGetTrade(pair.fuSymbol, self.fuEx.COVER_SHORT, pair.fuTicker.ask1, fuAmount[0])
var feIdMsg = self.fuEx.getTrade()
}
}
}
}
self.getLongPos = function (positions) {
return self.getPosByDirection(positions, PD_LONG)
}
self.getShortPos = function (positions) {
return self.getPosByDirection(positions, PD_SHORT)
}
self.getPosByDirection = function (positions, direction) {
var ret = null
if (positions.length > 2) {
Log("持仓错误,检测到三个持仓:", JSON.stringify(positions))
return ret
}
_.each(positions, function(pos) {
if ((direction == PD_LONG && pos.amount > 0) || (direction == PD_SHORT && pos.amount < 0)) {
ret = pos
}
})
return ret
}
self.calcProfit = function() {
var arrInitFuAcc = []
var arrNowFuAcc = []
_.each(self.pairs, function(pair) {
arrInitFuAcc.push(pair.initFuAcc)
arrNowFuAcc.push(pair.nowFuAcc)
})
var fuProfit = self.fuEx.calcProfit(arrInitFuAcc, arrNowFuAcc)
var spProfit = 0
var deltaBalance = 0
_.each(self.pairs, function(pair) {
var nowSpAcc = pair.nowSpAcc
var initSpAcc = pair.initSpAcc
var stocksDiff = nowSpAcc.Stocks + nowSpAcc.FrozenStocks - (initSpAcc.Stocks + initSpAcc.FrozenStocks)
var price = stocksDiff > 0 ? pair.spTicker.bid1 : pair.spTicker.ask1
spProfit += stocksDiff * price
deltaBalance = nowSpAcc.Balance + nowSpAcc.FrozenBalance - (initSpAcc.Balance + initSpAcc.FrozenBalance)
})
spProfit += deltaBalance
return [fuProfit + spProfit, fuProfit, spProfit]
}
self.returnPosTbl = function() {
var posTbl = {
type : "table",
title : "positions",
cols : ["索引", "期货", "期货杠杆", "数量", "现货", "数量"],
rows : []
}
_.each(self.pairs, function(pair, index) {
var nowFuPos = pair.nowFuPos
var nowSpPos = pair.nowSpPos
for (var i = 0 ; i < nowFuPos.length ; i++) {
if (nowSpPos.length > 0) {
posTbl.rows.push([index, nowFuPos[i].symbol, nowFuPos[i].marginLevel, nowFuPos[i].amount, nowSpPos[0].symbol, nowSpPos[0].amount])
} else {
posTbl.rows.push([index, nowFuPos[i].symbol, nowFuPos[i].marginLevel, nowFuPos[i].amount, "--", "--"])
}
}
})
return posTbl
}
self.returnTbl = function() {
var fuExName = "[" + self.fuEx.getExName() + "]"
var spExName = "[" + self.spEx.getExName() + "]"
var combiTickersTbl = {
type : "table",
title : "combiTickersTbl",
cols : ["期货", "代码" + fuExName, "卖一", "买一", "现货", "代码" + spExName, "卖一", "买一", "正对冲差价", "反对冲差价", "正对冲", "正对冲平仓"],
rows : []
}
_.each(self.pairs, function(pair, index) {
var spSymbolInfo = self.spEx.getSymbolInfo(pair.spTicker.originalSymbol)
combiTickersTbl.rows.push([
pair.fuTicker.symbol,
pair.fuTicker.originalSymbol,
pair.fuTicker.ask1,
pair.fuTicker.bid1,
pair.spTicker.symbol,
pair.spTicker.originalSymbol,
pair.spTicker.ask1,
pair.spTicker.bid1,
pair.plusDiff,
pair.minusDiff,
{'type':'button', 'cmd': 'plus:' + String(index), 'name': '正套'},
{'type':'button', 'cmd': 'cover_plus:' + String(index), 'name': '平正套'}
])
})
var accsTbl = {
type : "table",
title : "accs",
cols : ["代码" + fuExName, "初币", "初冻币", "初钱", "初冻钱", "币", "冻币", "钱", "冻钱",
"代码" + spExName, "初币", "初冻币", "初钱", "初冻钱", "币", "冻币", "钱", "冻钱"],
rows : []
}
_.each(self.pairs, function(pair) {
var arr = [pair.fuTicker.originalSymbol, pair.initFuAcc.Stocks, pair.initFuAcc.FrozenStocks, pair.initFuAcc.Balance, pair.initFuAcc.FrozenBalance, pair.nowFuAcc.Stocks, pair.nowFuAcc.FrozenStocks, pair.nowFuAcc.Balance, pair.nowFuAcc.FrozenBalance,
pair.spTicker.originalSymbol, pair.initSpAcc.Stocks, pair.initSpAcc.FrozenStocks, pair.initSpAcc.Balance, pair.initSpAcc.FrozenBalance, pair.nowSpAcc.Stocks, pair.nowSpAcc.FrozenStocks, pair.nowSpAcc.Balance, pair.nowSpAcc.FrozenBalance]
for (var i = 0 ; i < arr.length ; i++) {
if (typeof(arr[i]) == "number") {
arr[i] = _N(arr[i], 6)
}
}
accsTbl.rows.push(arr)
})
var symbolInfoTbl = {
type : "table",
title : "symbolInfos",
cols : ["合约代码" + fuExName, "量精度", "价格精度", "乘数", "最小下单量", "现货代码" + spExName, "量精度", "价格精度", "乘数", "最小下单量"],
rows : []
}
_.each(self.pairs, function(pair) {
var fuSymbolInfo = self.fuEx.getSymbolInfo(pair.fuTicker.originalSymbol)
var spSymbolInfo = self.spEx.getSymbolInfo(pair.spTicker.originalSymbol)
symbolInfoTbl.rows.push([fuSymbolInfo.symbol, fuSymbolInfo.amountPrecision, fuSymbolInfo.pricePrecision, fuSymbolInfo.multiplier, fuSymbolInfo.min,
spSymbolInfo.symbol, spSymbolInfo.amountPrecision, spSymbolInfo.pricePrecision, spSymbolInfo.multiplier, spSymbolInfo.min])
})
var allPairs = []
_.each(self.fuExTickers, function(fuTicker) {
_.each(self.spExTickers, function(spTicker) {
if (fuTicker.symbol == spTicker.symbol) {
allPairs.push({symbol: fuTicker.symbol, fuSymbol: fuTicker.originalSymbol, spSymbol: spTicker.originalSymbol, plus: fuTicker.bid1 - spTicker.ask1})
}
})
})
_.each(allPairs, function(pair) {
var findPair = null
_.each(self.allPairs, function(selfPair) {
if (pair.fuSymbol == selfPair.fuSymbol && pair.spSymbol == selfPair.spSymbol) {
findPair = selfPair
}
})
if (findPair) {
findPair.minPlus = pair.plus < findPair.minPlus ? pair.plus : findPair.minPlus
findPair.maxPlus = pair.plus > findPair.maxPlus ? pair.plus : findPair.maxPlus
pair.minPlus = findPair.minPlus
pair.maxPlus = findPair.maxPlus
} else {
self.allPairs.push({symbol: pair.symbol, fuSymbol: pair.fuSymbol, spSymbol: pair.spSymbol, plus: pair.plus, minPlus: pair.plus, maxPlus: pair.plus})
pair.minPlus = pair.plus
pair.maxPlus = pair.plus
}
})
return [combiTickersTbl, accsTbl, symbolInfoTbl]
}
self.onexit = function() {
_G("pairs", self.pairs)
_G("allPairs", self.allPairs)
Log("执行扫尾处理,数据保存", "#FF0000")
}
self.init = function() {
var fuExName = self.fuEx.getExName()
var spExName = self.spEx.getExName()
var gFuExName = _G("fuExName")
var gSpExName = _G("spExName")
if ((gFuExName && gFuExName != fuExName) || (gSpExName && gSpExName != spExName)) {
throw "交易所对象发生变化,需要重置数据"
}
if (!gFuExName) {
_G("fuExName", fuExName)
}
if (!gSpExName) {
_G("spExName", spExName)
}
self.allPairs = _G("allPairs")
if (!self.allPairs) {
self.allPairs = []
}
var arrPair = _G("pairs")
if (!arrPair) {
arrPair = []
}
var arrStrPair = self.symbolPairs.split(",")
var timeStamp = new Date().getTime()
_.each(arrStrPair, function(strPair) {
var arrSymbol = strPair.split("|")
var recoveryPair = null
_.each(arrPair, function(pair) {
if (pair.fuSymbol == arrSymbol[0] && pair.spSymbol == arrSymbol[1]) {
recoveryPair = pair
}
})
if (!recoveryPair) {
var pair = {
fuSymbol : arrSymbol[0],
spSymbol : arrSymbol[1],
fuTicker : {},
spTicker : {},
plusDiff : null,
minusDiff : null,
canTrade : false,
initFuAcc : null,
initSpAcc : null,
nowFuAcc : null,
nowSpAcc : null,
nowFuPos : null,
nowSpPos : null,
fuMarginLevel : null
}
self.pairs.push(pair)
Log("初始化:", pair)
} else {
self.pairs.push(recoveryPair)
Log("恢复:", recoveryPair)
}
self.fuEx.pushSubscribeSymbol(arrSymbol[0])
self.spEx.pushSubscribeSymbol(arrSymbol[1])
if (!self.pairs[self.pairs.length - 1].initFuAcc) {
self.fuEx.goGetAcc(arrSymbol[0], timeStamp)
var nowFuAcc = self.fuEx.getAcc(arrSymbol[0], timeStamp)
self.pairs[self.pairs.length - 1].initFuAcc = nowFuAcc
self.pairs[self.pairs.length - 1].nowFuAcc = nowFuAcc
}
if (!self.pairs[self.pairs.length - 1].initSpAcc) {
self.spEx.goGetAcc(arrSymbol[1], timeStamp)
var nowSpAcc = self.spEx.getAcc(arrSymbol[1], timeStamp)
self.pairs[self.pairs.length - 1].initSpAcc = nowSpAcc
self.pairs[self.pairs.length - 1].nowSpAcc = nowSpAcc
}
Sleep(300)
})
Log("self.pairs:", self.pairs)
_.each(self.pairs, function(pair) {
var fuSymbolInfo = self.fuEx.getSymbolInfo(pair.fuSymbol)
if (!fuSymbolInfo) {
throw pair.fuSymbol + ",品种信息获取失败!"
} else {
Log(pair.fuSymbol, fuSymbolInfo)
}
var spSymbolInfo = self.spEx.getSymbolInfo(pair.spSymbol)
if (!spSymbolInfo) {
throw pair.spSymbol + ",品种信息获取失败!"
} else {
Log(pair.spSymbol, spSymbolInfo)
}
})
_.each(self.pairs, function(pair) {
pair.fuMarginLevel = self.fuMarginLevel
var ret = self.fuEx.setMarginLevel(pair.fuSymbol, self.fuMarginLevel)
Log(pair.fuSymbol, "杠杆设置:", ret)
if (!ret) {
throw "初始设置杠杆失败!"
}
})
}
self.init()
return self
}
var manager = null
function main() {
if(isReset) {
_G(null)
LogReset(1)
LogProfitReset()
LogVacuum()
Log("重置所有数据", "#FF0000")
}
if (isOKEX_V5_Simulate) {
for (var i = 0 ; i < exchanges.length ; i++) {
if (exchanges[i].GetName() == "Futures_OKCoin" || exchanges[i].GetName() == "OKEX") {
var ret = exchanges[i].IO("simulate", true)
Log(exchanges[i].GetName(), "切换模拟盘")
}
}
}
var fuConfigureFunc = null
var spConfigureFunc = null
if (exchanges.length != 2) {
throw "需要添加两个交易所对象!"
} else {
var fuName = exchanges[0].GetName()
if (fuName == "Futures_OKCoin" && isOkexV5) {
fuName += "_V5"
Log("使用OKEX V5接口")
}
var spName = exchanges[1].GetName()
fuConfigureFunc = $.getConfigureFunc()[fuName]
spConfigureFunc = $.getConfigureFunc()[spName]
if (!fuConfigureFunc || !spConfigureFunc) {
throw (fuConfigureFunc ? "" : fuName) + " " + (spConfigureFunc ? "" : spName) + " not support!"
}
}
var fuEx = $.createBaseEx(exchanges[0], fuConfigureFunc)
var spEx = $.createBaseEx(exchanges[1], spConfigureFunc)
manager = createManager(fuEx, spEx, symbolPairs, cmdHedgeAmount, fuMarginLevel, fuMarginReservedRatio)
while(true) {
manager.process()
Sleep(interval)
}
}
function onerror() {
if (manager) {
manager.onexit()
}
}
function onexit() {
if (manager) {
manager.onexit()
}
}
Da sich Mehrvariantenstrategien besser für die Entwicklung mit IO eignen, verwendet der Code eineMultiSymbolCtrlLibEine Template-Klassenbibliothek (Kapselung, Aufruf der Austauschschnittstelle über IO). Daher kann die Strategie nicht einem Backtesting unterzogen werden, sie kann jedoch in einem simulierten Handel getestet werden (obwohl der reale Handel bereits seit 2 Monaten läuft, ist es dennoch notwendig, während der Test- und Einarbeitungsphase einen simulierten Handel durchzuführen).
Bevor wir mit dem Test beginnen, sprechen wir zunächst über das Parameterdesign.

Es gibt nicht viele Strategieparameter, die wichtigeren sind:
LTC-USDT-211231|LTC_USDT,BTC-USDT-211231|BTC_USDT
Hier müssen Sie die Strategie festlegen, um welche Kombinationen zu überwachen. Die obige Einstellung dient beispielsweise zur Überwachung des Litecoin-Vertrags (LTC-USDT-211231) der Terminbörse und des Litecoin (LTC_USDT) der Spotbörse. Eine Kombination aus Futures-Kontrakten und Spot-Trading-Paaren|Symbole werden getrennt, um eine Gruppe zu bilden. Verwendung in verschiedenen Kombinationen,Symbole getrennt. Beachten Sie, dass die Symbole hier alle den Status der englischen Eingabemethode haben!
Dann fragen Sie mich vielleicht, wie Sie den Vertragscode finden. Diese Vertragscodes und Spot-Handelspaare werden alle von der Börse definiert. Es ist auf der FMZ-Plattform nicht definiert.
Zum BeispielLTC-USDT-211231Bei diesem Vertrag handelt es sich derzeit um einen Unterquartalsvertrag, genanntnext_quarterDas Schnittstellensystem von OKEX heißtLTC-USDT-211231. WexApp-DemoLitecoin/USDTDas Handelspaar wird geschrieben alsLTC_USDT. Wie die Informationen hier eingetragen werden, hängt also von dem im Austausch definierten Namen ab.

Der Rest sind Funktionen wie das Einrichten einer simulierten Festplatte, das Zurücksetzen von Daten, die Verwendung der OKEX V5-Schnittstelle (da diese auch mit V3 kompatibel ist) usw., die nicht besonders wichtig sind.
Das erste Börsenobjekt fügt die Terminbörse hinzu, und das zweite fügt das Spotbörsenobjekt hinzu.
Terminbörsen verwenden die V5-Schnittstelle von OKEX zur Handelssimulation und Spotbörsen verwenden wexApp zur Handelssimulation.
Ich habe auf den positiven Deckungsbutton der BTC-Kombination geklickt und eine Position eröffnet.



Anschließend habe ich auf „Position schließen“ geklickt.


Verlust! ! ! Es scheint, dass bei einer geringen Gewinnspanne die Schließung der Position die Bearbeitungsgebühr nicht abdecken kann. Es ist notwendig, die Bearbeitungsgebühr zu berechnen, den Schlupf zu schätzen und dann die Schließspanne vernünftig zu planen, bevor die Position geschlossen wird.
Quellcode der Strategie: https://www.fmz.com/strategy/314352
Interessierte können es nutzen und modifizieren.