지난 기사에서 우리는 간단한 그리드 전략을 함께 만들었습니다. 이 기사에서는 이 전략을 멀티스펙트 스팟 그리드 전략으로 업그레이드하고 확장했으며, 이 전략을 실무에서 테스트하도록 했습니다. 목적은 "성스러운 잔"을 찾는 것이 아니라 전략을 설계할 때 다양한 문제와 해결책을 논의하는 것입니다. 이 기사는 이 전략을 설계하는 내 경험의 일부를 설명할 것입니다. 이 기사의 내용은 약간 복잡하며 프로그래밍에 대한 특정 기초가 필요합니다.
이 기사에서는 이전 기사와 마찬가지로 FMZ Quant (FMZ.COM).
다종
솔직히 말해서, 저는 이 네트워크 전략이BTC_USDT
, 또한LTC_USDT
/EOS_USDT
/DOGE_USDT
/ETC_USDT
/ETH_USDT
어쨌든, 현금 거래 쌍과 실행하려는 품종은 모두 동시에 그리드에서 거래됩니다.
여러 종의 변동적인 시장을 포착하는 것이 좋습니다. 이 요구 사항은 매우 간단해 보이지만, 문제는 설계할 때 발생합니다.
ETHUSDT:100:0.002|LTCUSDT:20:0.1
그 중에서도, ETHUSDT:100:0.002
거래 쌍 ETH_USDT를 제어하고,LTCUSDT:20:0.1
LTC_USDT 거래 쌍을 제어합니다. 중간에 있는 ETHUSDT:100:0.002
, 여기서 ETHUSDT는 당신이 하고 싶은 거래 쌍을 나타냅니다. 100은 그리드 간격입니다. 0.002는 각 그리드에서 거래되는 ETH 동전의 수입니다.
이 문자열은 당신이 하고 싶은 각 종의 매개 변수 정보를 포함합니다. 전략에서 이러한 문자열을 분석하고 각 종의 거래 논리를 제어하기 위해 전략의 변수에 값을 할당합니다. 어떻게 분석합니까? 여전히 위의 예를 사용합니다.
function main() {
var net = [] // The recorded grid parameters, use the data when running to the grid trading logic
var params = "ETHUSDT:100:0.002|LTCUSDT:20:0.1"
var arrPair = params.split("|")
_.each(arrPair, function(pair) {
var arr = pair.split(":")
var symbol = arr[0] // Trading pair name
var diff = parseFloat(arr[1]) // Grid spacing
var amount = parseFloat(arr[2]) // Grid order volume
net.push({symbol : symbol, diff : diff, amount : amount})
})
Log("Grid parameter data:", net)
}
이렇게 보면 매개 변수가 분석됩니다. 물론 JSON 문자열을 직접 사용할 수도 있습니다.
function main() {
var params = '[{"symbol":"ETHUSDT","diff":100,"amount":0.002},{"symbol":"LTCUSDT","diff":20,"amount":0.1}]'
var net = JSON.parse(params) // The recorded grid parameters, use the data when running to the grid trading logic
_.each(net, function(pair) {
Log("Trading pairs:", pair.symbol, pair)
})
}
_G()
FMZ 양적 거래 플랫폼의 기능 또는 데이터베이스 운영 기능을 사용DBExec()
, 그리고 FMZ API 문서를 확인해 보세요.예를 들어, 우리는 꼬리 sweep 함수를 설계하고_G()
그리드 데이터를 저장하는 기능
var net = null
function main() { // Strategy main functions
// Read the stored net first
net = _G("net")
// ...
}
function onExit() {
_G("net", net)
Log("Perform tail-sweeping processing and save data", "#FF0000")
}
function onexit() { // The exit sweep function defined by the platform system, triggered the execution when the real bot is clicked to stop
onExit()
}
function onerror() { // The abnormal exit function defined by the platform system, triggered the execution when the program is abnormal
onExit()
}
백테스팅 시스템은 주문 금액과 주문 정확성에 대해 그러한 엄격한 제한을 두지 않지만 각 거래소는 실제 봇에 주문을 할 때 가격과 주문 금액에 대한 엄격한 기준을 가질 수 있으며 이러한 제한은 다른 거래소에서 동일하지 않습니다. 따라서 문제없이 백테스팅 시스템에서 테스트하는 초보자도 있습니다. 실제 봇이 시작되면 거래가 활성화되면 다양한 문제가 발생하고 오류 메시지의 내용이 읽히지 않아 다양한 미친 현상이 나타납니다.
여러 종의 경우,이 요구 사항은 더 복잡합니다. 단일 종 전략의 경우, 정확성과 같은 정보를 지정하기 위해 매개 변수를 설계할 수 있습니다. 그러나 여러 종의 전략을 설계할 때,이 정보를 매개 변수에 작성하면 매개 변수가 매우 부풀어 오르는 것이 분명합니다.
이 시점에서, 교환 문서에 거래 쌍과 관련된 인터페이스 정보가 있는지 확인하기 위해 거래소의 API 문서를 확인해야합니다. 만약 그렇다면, 정확성과 같은 정보를 얻기 위해 전략에 자동 액세스 인터페이스를 설계하고 거래에 관련된 거래 쌍 정보로 구성할 수 있습니다. (단순히, 정확성 또는 무언가가 자동으로 거래소에서 얻어지고 전략 매개 변수와 관련된 변수에 적응됩니다.)
위의 분석을 바탕으로, 템플릿 클래스 라이브러리는 전략과 교환 메커니즘과 인터페이스 사이의 결합을 줄이기 위해 설계되었습니다.
이 템플릿 클래스 라이브러리를 이렇게 디자인할 수 있습니다 (코드의 일부가 생략되었습니다):
function createBaseEx(e, funcConfigure) {
var self = {}
self.e = e
self.funcConfigure = funcConfigure
self.name = e.GetName()
self.type = self.name.includes("Futures_") ? "Futures" : "Spot"
self.label = e.GetLabel()
// Interfaces to be implemented
self.interfaceGetTickers = null // Create a function to asynchronously obtain a thread of aggregated market data
self.interfaceGetAcc = null // Create a function that asynchronously obtains account data thread
self.interfaceGetPos = null // Get a position
self.interfaceTrade = null // Create concurrent orders
self.waitTickers = null // Waiting for concurrent market data
self.waitAcc = null // Waiting for account concurrent data
self.waitTrade = null // Waiting for order concurrent data
self.calcAmount = null // Calculate the order volume based on data such as trading pair accuracy
self.init = null // Initialization work, obtaining data such as accuracy
// Execute the configuration function to configure the object
funcConfigure(self)
// Check whether the interfaces agreed by configList are implemented
_.each(configList, function(funcName) {
if (!self[funcName]) {
throw "interface" + funcName + "unimplemented"
}
})
return self
}
$.createBaseEx = createBaseEx
$.getConfigureFunc = function(exName) {
dicRegister = {
"Futures_OKCoin" : funcConfigure_Futures_OKCoin, // Implementation of OK futures
"Huobi" : funcConfigure_Huobi,
"Futures_Binance" : funcConfigure_Futures_Binance,
"Binance" : funcConfigure_Binance,
"WexApp" : funcConfigure_WexApp, // Implementation of wexApp
}
return dicRegister
}
템플릿에서는 특정 거래소를 위해 작성되어 있습니다. 예를 들어 FMZ
function funcConfigure_WexApp(self) {
var formatSymbol = function(originalSymbol) {
// BTC_USDT
var arr = originalSymbol.split("_")
var baseCurrency = arr[0]
var quoteCurrency = arr[1]
return [originalSymbol, baseCurrency, quoteCurrency]
}
self.interfaceGetTickers = function interfaceGetTickers() {
self.routineGetTicker = HttpQuery_Go("https://api.wex.app/api/v1/public/tickers")
}
self.waitTickers = function waitTickers() {
var ret = []
var arr = JSON.parse(self.routineGetTicker.wait()).data
_.each(arr, function(ele) {
ret.push({
bid1: parseFloat(ele.buy),
bid1Vol: parseFloat(-1),
ask1: parseFloat(ele.sell),
ask1Vol: parseFloat(-1),
symbol: formatSymbol(ele.market)[0],
type: "Spot",
originalSymbol: ele.market
})
})
return ret
}
self.interfaceGetAcc = function interfaceGetAcc(symbol, updateTS) {
if (self.updateAccsTS != updateTS) {
self.routineGetAcc = self.e.Go("GetAccount")
}
}
self.waitAcc = function waitAcc(symbol, updateTS) {
var arr = formatSymbol(symbol)
var ret = null
if (self.updateAccsTS != updateTS) {
ret = self.routineGetAcc.wait().Info
self.bufferGetAccRet = ret
} else {
ret = self.bufferGetAccRet
}
if (!ret) {
return null
}
var acc = {symbol: symbol, Stocks: 0, FrozenStocks: 0, Balance: 0, FrozenBalance: 0, originalInfo: ret}
_.each(ret.exchange, function(ele) {
if (ele.currency == arr[1]) {
// baseCurrency
acc.Stocks = parseFloat(ele.free)
acc.FrozenStocks = parseFloat(ele.frozen)
} else if (ele.currency == arr[2]) {
// quoteCurrency
acc.Balance = parseFloat(ele.free)
acc.FrozenBalance = parseFloat(ele.frozen)
}
})
return acc
}
self.interfaceGetPos = function interfaceGetPos(symbol, price, initSpAcc, nowSpAcc) {
var symbolInfo = self.getSymbolInfo(symbol)
var sumInitStocks = initSpAcc.Stocks + initSpAcc.FrozenStocks
var sumNowStocks = nowSpAcc.Stocks + nowSpAcc.FrozenStocks
var diffStocks = _N(sumNowStocks - sumInitStocks, symbolInfo.amountPrecision)
if (Math.abs(diffStocks) < symbolInfo.min / price) {
return []
}
return [{symbol: symbol, amount: diffStocks, price: null, originalInfo: {}}]
}
self.interfaceTrade = function interfaceTrade(symbol, type, price, amount) {
var tradeType = ""
if (type == self.OPEN_LONG || type == self.COVER_SHORT) {
tradeType = "bid"
} else {
tradeType = "ask"
}
var params = {
"market": symbol,
"side": tradeType,
"amount": String(amount),
"price" : String(-1),
"type" : "market"
}
self.routineTrade = self.e.Go("IO", "api", "POST", "/api/v1/private/order", self.encodeParams(params))
}
self.waitTrade = function waitTrade() {
return self.routineTrade.wait()
}
self.calcAmount = function calcAmount(symbol, type, price, amount) {
// Obtain trading pair information
var symbolInfo = self.getSymbolInfo(symbol)
if (!symbol) {
throw symbol + ", the trading pair information cannot be checked"
}
var tradeAmount = null
var equalAmount = null // Number of coins recorded
if (type == self.OPEN_LONG || type == self.COVER_SHORT) {
tradeAmount = _N(amount * price, parseFloat(symbolInfo.pricePrecision))
// Check the minimum trading volume
if (tradeAmount < symbolInfo.min) {
Log(self.name, " tradeAmount:", tradeAmount, "less than", symbolInfo.min)
return false
}
equalAmount = tradeAmount / price
} else {
tradeAmount = _N(amount, parseFloat(symbolInfo.amountPrecision))
// Check the minimum trading volume
if (tradeAmount < symbolInfo.min / price) {
Log(self.name, " tradeAmount:", tradeAmount, "less than", symbolInfo.min / price)
return false
}
equalAmount = tradeAmount
}
return [tradeAmount, equalAmount]
}
self.init = function init() { // Functions that deal with conditions such as accuracy automatically
var ret = JSON.parse(HttpQuery("https://api.wex.app/api/v1/public/markets"))
_.each(ret.data, function(symbolInfo) {
self.symbolsInfo.push({
symbol: symbolInfo.pair,
amountPrecision: parseFloat(symbolInfo.basePrecision),
pricePrecision: parseFloat(symbolInfo.quotePrecision),
multiplier: 1,
min: parseFloat(symbolInfo.minQty),
originalInfo: symbolInfo
})
})
}
}
이 템플릿을 전략에 사용하는 것은 간단합니다.
function main() {
var fuExName = exchange.GetName()
var fuConfigureFunc = $.getConfigureFunc()[fuExName]
var ex = $.createBaseEx(exchange, fuConfigureFunc)
var arrTestSymbol = ["LTC_USDT", "ETH_USDT", "EOS_USDT"]
var ts = new Date().getTime()
// Test to get tickers
ex.goGetTickers()
var tickers = ex.getTickers()
Log("tickers:", tickers)
// Test to obtain account information
ex.goGetAcc(symbol, ts)
_.each(arrTestSymbol, function(symbol) {
_.each(tickers, function(ticker) {
if (symbol == ticker.originalSymbol) {
// print ticker data
Log(symbol, ticker)
}
})
// print asset data
var acc = ex.getAcc(symbol, ts)
Log("acc:", acc.symbol, acc)
})
}
위 템플릿을 기반으로 전략을 설계하고 작성하는 것은 매우 간단합니다. 전체 전략은 약 300 + 라인이며 디지털 통화 스팟 다종 격자 전략을 구현합니다.
현재는 돈을 잃고 있습니다.T_T
, 소스 코드는 아직 공개되지 않습니다.
여기 몇 개의 등록 코드가 있습니다. 관심 있으시면 wexApp를 이용해서 시도해 볼 수 있습니다.
Buy address: https://www.fmz.com/m/s/284507
Registration code:
adc7a2e0a2cfde542e3ace405d216731
f5db29d05f57266165ce92dc18fd0a30
1735dca92794943ddaf277828ee04c27
0281ea107935015491cda2b372a0997d
1d0d8ef1ea0ea1415eeee40404ed09cc
200U를 조금 넘었을 때, 제가 달리기 시작했을 때, 저는 큰 일방적인 시장에 부딪혔습니다. 하지만 저는 천천히 회복했습니다. 안정성은 나쁘지 않습니다. 5월 27일 이후로 변경되지 않았습니다.