리소스 로딩... 로딩...

초보자, 확인해 보세요 암호화폐 양적 거래에 안내 (6)

저자:니나바다스, 창작: 2022-04-21 18:13:03, 업데이트: 2022-04-22 12:00:05

초보자, 확인해 보세요 암호화폐 양적 거래에 안내 (6)

지난 기사에서 우리는 간단한 그리드 전략을 함께 만들었습니다. 이 기사에서는 이 전략을 멀티 심볼 스팟 그리드 전략으로 업그레이드하고 확장했으며, 이 전략을 실무에서 테스트하도록 했습니다. 목적은 "성스러운 잔"을 찾는 것이 아니라 전략을 설계하는 과정에서 여러 가지 문제와 해결책을 논의하는 것입니다. 이 기사는 전략을 설계하는 데에 대한 저의 일부 경험을 설명합니다. 이 기사의 내용은 약간 복잡하며 프로그래밍에 대한 특정 기초가 필요합니다.

전략적 요구에 기초한 디자인 사고

이 기사에서는 이전과 마찬가지로 FMZ 양자 거래 플랫폼을 기반으로 한 디자인에 대해 논의합니다 (FMZ.COM).

  • 복수 기호 솔직히 말해서, 전력망 전략은BTC_USDT, 또한LTC_USDT/EOS_USDT/DOGE_USDT/ETC_USDT/ETH_USDT어쨌든, 현금 거래 쌍을 위해, 동시에 거래하려는 모든 기호의 그리드 거래를 실행합니다.

    그래, 여러 개의 기호의 진동하는 시장 코트를 포착하는 것이 좋습니다.
    단순하게 들리지만, 디자인을 시작하면 어려워집니다.

      1. 먼저, 여러 기호의 시장 코트를 얻습니다. 그것은 해결해야 할 첫 번째 문제입니다. 플랫폼 API 문서를 읽은 후, 플랫폼은 일반적으로 집계 인터페이스를 제공한다는 것을 발견했습니다. 좋습니다. 데이터를 얻기 위해 집계 시장 데이터 인터페이스를 사용합니다.
      1. 두 번째 문제는 계정 자산입니다. 우리는 다중 기호 전략을 수행하려면 각 거래 쌍의 자산을 별도로 관리하고 모든 자산 데이터를 얻고 한 번 기록하는 것을 고려해야합니다. 왜 우리는 계정 자산 데이터를 얻어야합니까? 그리고 왜 각 거래 쌍을 별도로 기록해야합니까?

    주문을 할 때 사용 가능한 자산을 판단해야하기 때문에 판단 전에 데이터를 얻는 것이 필요하지 않습니까? 또한 수익률을 계산해야 합니다. 먼저 초기 자산 데이터를 기록해야 할까요? 그리고 그 다음 현금 계좌의 자산 데이터를 얻고 초기 데이터와 비교하여 이익과 손실을 계산해야합니까? 다행히도, 플랫폼의 자산 계정 인터페이스는 일반적으로 모든 통화 자산 데이터를 반환합니다. 그래서 우리는 단지 한 번만 데이터를 얻어야 합니다. 그리고 데이터를 처리합니다.

      1. 전략 매개 변수 설계. 멀티 심볼 전략의 매개 변수 디자인은 단일 심볼 전략의 매개 변수 디자인과 상당히 다릅니다. 멀티 심볼 전략의 각 기호의 거래 논리도 같기 때문에 거래 도중 각 기호의 매개 변수가 다를 수 있습니다. 예를 들어, 그리드 전략에서는 BTC_USDT 거래 쌍을 할 때 매번 0.01 BTC를 거래하고 싶을 수 있지만, DOGE_USDT를 할 때 이 매개 변수 (0.01 통화 거래) 를 사용하는 것은 분명히 부적절합니다. 물론, USDT의 양으로 처리할 수도 있습니다. 그러나 여전히 문제가 발생할 것입니다. BTC_USDT에 의해 1000U와 DOGE_USDT에 의해 10U를 거래하고 싶다면 어떻게 될까요? 수요는 결코 만족할 수 없습니다. 아마도, 어떤 학생들은 질문을 생각할 수 있습니다, 그리고 제안, 나는 매개 변수 더 많은 그룹을 설정할 수 있습니다, 그리고 개별적으로 작동 할 다른 거래 쌍의 매개 변수를 제어. 그것은 여전히 유연하게 요구 사항을 충족 할 수 없습니다, 몇 개의 그룹을 설정해야 합니다? 우리는 세 그룹을 설정; 우리는 4 기호를 작동하고 싶다면 어떻게? 우리는 전략을 수정하고 매개 변수를 증가해야합니까? 따라서, 다중 기호 전략 매개 변수를 설계 할 때 차별화 필요성에 대해 완전히 생각하십시오. 하나의 해결책은 매개 변수를 일반적인 문자열 또는 JSON 문자열로 설계하는 것입니다.
        예를 들어:
      ETHUSDT:100:0.002|LTCUSDT:20:0.1
      

      은 각 기호의 데이터를 나누기 위해 사용되며,ETHUSDT:100:0.002거래 쌍 ETH_USDT를 제어하고,LTCUSDT:20:0.1LTC_USDT 트레이딩 쌍을 제어합니다. 중간에 있는 은 세분화 역할을 합니다. 들어와ETHUSDT:100:0.002, ETHUSDT는 거래하려는 거래 쌍을 나타냅니다. 100는 그리드 간격입니다. 0.002는 각 그리드의 거래 된 ETH 금액입니다.
      이 문자열은 이미 당신이 작동해야 하는 각 기호의 매개 변수 정보를 포함하고 있습니다. 당신은 문자열을 분석하고 각 기호의 거래 논리를 제어하기 위해 전략의 변수에 값을 할당할 수 있습니다. 어떻게 분석합니까? 위에서 언급한 예를 사용해보죠.

      function main() {
          var net = []  // the recorded grid parameters; when specifically running the grid trading logic, use the data from here 
          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 amount 
              net.push({symbol : symbol, diff : diff, amount : amount})
          })
          Log("Grid parameter data:", net)
      }
      

      img

      자, 우리는 매개 변수를 분석했습니다. 물론, 당신은 직접 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; when specifically running the grid trading logic, use the data from here         
          _.each(net, function(pair) {
              Log("Trading pair:", pair.symbol, pair)
          })
      }
      

      img

      1. 데이터 지속성 실제 전략과 교육 전략에는 큰 차이가 있습니다. 지난 기사에서 가르치는 전략은 전략 논리와 설계의 초기 테스트를위한 것입니다. 실제로 봇에서 전략을 실행할 때 걱정해야 할 문제가 더 많습니다. 봇을 실행할 때 봇을 시작하고 중지 할 수 있습니다. 그 때 봇을 실행하는 동안의 모든 데이터가 손실됩니다. 그렇다면 봇이 중지 된 후 봇을 다시 시작할 때 이전 상태를 어떻게 계속할 수 있습니까? 여기서, bot가 실행될 때 키 데이터를 지속적으로 저장해야 합니다. 그래서 데이터를 읽을 수 있고 bot가 다시 시작될 때 bot가 계속 실행될 수 있습니다. 당신은_G()FMZ Quant에 있는 함수, 또는 동작 함수를 사용DBExec()데이터베이스에서, 당신은 FMZ API 문서를 검색 할 수 있습니다. 자세한 사항.

      예를 들어, 우리는 그 함수를 사용하여 청소 함수를 설계하고자 합니다._G(), 그리드 데이터를 저장하기 위해.

      var net = null 
      function main() {  // strategy main function 
          // first read the stored net 
          net = _G("net")
          
          // ...
      }
      
      function onExit() {
          _G("net", net)
          Log("Execute the clean-up processing, and save the data", "#FF0000")
      }
      
      function onexit() {    // the onexit function defined by the platform system, which will be triggered when clicking the bot to stop 
          onExit()
      }
      
      function onerror() {   // the onerror function defined by the platform system, which will be triggered when the program exception occurs 
          onExit()
      }
      
      1. 주문 금액 정확성, 주문 가격 정확성, 최소 주문량, 최소 주문 금액 등에 대한 제한

      백테스트 시스템은 주문량과 주문 정밀도에 대한 엄격한 제한이 없지만, 봇에서는 각 플랫폼이 주문 가격과 주문량에 대한 엄격한 기준을 가지고 있으며, 다른 거래 쌍에는 다른 제한이 있습니다. 따라서 초보자는 종종 백테스트 시스템에서 OKEX를 테스트합니다. 전략이 봇에서 실행되면 거래가 시작되면 다양한 문제가 발생하고 오류 메시지의 내용이 읽히지 않아 다양한 미친 현상이 나타납니다.

      여러 기호의 경우, 요구 사항은 더 복잡합니다. 단일 기호 전략의 경우, 정밀도와 같은 정보를 지정하기 위해 매개 변수를 설계할 수 있습니다. 그러나, 여러 기호 전략을 설계할 때, 매개 변수에 정보를 작성하면 매개 변수가 매우 지루해질 것이 분명합니다.

      이 시점에서, 당신은 문서에 거래 쌍 관련 정보에 대한 인터페이스가 있는지 확인하기 위해 플랫폼의 API 문서를 확인해야합니다. 이러한 인터페이스가있는 경우, 정확성과 같은 정보를 얻기 위해 전략에서 자동 액세스 인터페이스를 설계하고 거래의 거래 쌍 정보로 구성 할 수 있습니다. (단순히, 정확성은 플랫폼에서 자동으로 얻어지고 전략 매개 변수에 따라 조정됩니다.)

      1. 다른 플랫폼의 적응 왜 이 문제가 마지막에 언급되었을까요? 앞서 언급한 모든 문제들을 처리하는 것은 마지막 문제로 이어질 것입니다. 우리는 전략에서 집계된 시장 인터페이스를 사용할 계획입니다. 우리의 전략은 집계된 시장 인터페이스를 사용할 계획이기 때문에 플랫폼 거래 쌍 정밀성 및 기타 데이터 적응에 액세스하는 것뿐만 아니라 각 거래 쌍을 개별적으로 처리하기 위해 계정 정보에 액세스하는 것 등과 같은 솔루션은 다른 플랫폼으로 인해 큰 차이를 가져올 것입니다. 인터페이스 호출과 메커니즘의 차이점이 있습니다. 스팟 플랫폼의 경우, 그리드 전략이 미래에 대한 버전으로 확장되면 차이가 비교적 작습니다. 각 플랫폼의 메커니즘의 차이점은 더욱 크습니다. 한 해결책은 FMZ 템플릿 라이브러리를 설계하는 것입니다. 전략 자체와 플랫폼 사이의 결합을 줄이기 위해 라이브러리에서 차별화를 구현하는 디자인을 작성하십시오. 그 단점은 템플릿 라이브러리를 작성해야 한다는 것입니다. 이 템플릿에서는 각각의 플랫폼에 기반한 차별화를 구체적으로 구현해야 합니다.

템플릿 라이브러리를 설계

위의 분석을 바탕으로, 우리는 전략, 플랫폼 메커니즘 및 인터페이스 사이의 결합을 줄이기 위해 템플릿 라이브러리를 설계합니다.
템플릿 라이브러리를 이렇게 디자인할 수 있습니다 (코드의 일부가 생략되었습니다):

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()
    
    // the interfaces that need to be implemented 
    self.interfaceGetTickers = null   // create a function that asynchronously obtains the aggregated market quote threads
    self.interfaceGetAcc = null       // create a function that asynchronously obtains the account data threads 
    self.interfaceGetPos = null       // obtain positions 
    self.interfaceTrade = null        // create concurrent orders 
    self.waitTickers = null           // wait for the concurrent market quote data  
    self.waitAcc = null               // wait for the account concurrent data 
    self.waitTrade = null             // wait for order concurrent data
    self.calcAmount = null            // calculate the order amount according to the trading pair precision and other data 
    self.init = null                  // initialization; obtain the precision and other data 
    
    // execute the configuration function, to configure objects 
    funcConfigure(self)

    // detect whether all the interfaces arranged by configList can be implemented 
    _.each(configList, function(funcName) {
        if (!self[funcName]) {
            throw "interface" + funcName + "not implemented"
        }
    })
    
    return self
}

$.createBaseEx = createBaseEx
$.getConfigureFunc = function(exName) {
    dicRegister = {
        "Futures_OKCoin" : funcConfigure_Futures_OKCoin,    //  the implementation of OKEX Futures 
        "Huobi" : funcConfigure_Huobi,
        "Futures_Binance" : funcConfigure_Futures_Binance,
        "Binance" : funcConfigure_Binance,
        "WexApp" : funcConfigure_WexApp,                    // the implementation of wexApp
    }
    return dicRegister
}

템플릿에서 특정 플레이 형식을 대상으로 한 코드 작성을 구현하십시오. 예를 들어 FMZ 시뮬레이션 봇 WexApp를 참조하십시오.

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 the trading pair information 
        var symbolInfo = self.getSymbolInfo(symbol)
        if (!symbol) {
            throw symbol + ",trading pair information not found"
        }
        var tradeAmount = null 
        var equalAmount = null  // record the symbol amount  
        if (type == self.OPEN_LONG || type == self.COVER_SHORT) {
            tradeAmount = _N(amount * price, parseFloat(symbolInfo.pricePrecision))
            // detect the minimum trading amount 
            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))
            // detect the minimum trading amount 
            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() {   // the function that automatically processes conditions like precision, etc.  
        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 obtain the market quotes 
    ex.goGetTickers()
    var tickers = ex.getTickers()
    Log("tickers:", tickers)
    
    // test to obtain the account information 
    ex.goGetAcc(symbol, ts)
    
    _.each(arrTestSymbol, function(symbol) {        
        _.each(tickers, function(ticker) {
            if (symbol == ticker.originalSymbol) {
                // print the market quote data 
                Log(symbol, ticker)
            }
        })

        // print asset data 
        var acc = ex.getAcc(symbol, ts)
        Log("acc:", acc.symbol, acc)
    })
}

전략 봇

위 템플릿을 기반으로 전략을 설계하고 작성하는 것은 매우 간단합니다. 전체 전략에는 약 300 줄 이상의 코드가 있습니다. 이는 암호화폐 스팟 멀티 심볼 그리드 전략을 구현합니다.

img

img

지금, 손실이 있습니다T_T, 그래서 소스 코드는 제공되지 않습니다.

여러 가지 등록 코드가 있습니다. 관심이 있다면 wexApp에서 시도해 볼 수 있습니다.

Purchase Address: https://www.fmz.com/m/s/284507
Registration Code:
adc7a2e0a2cfde542e3ace405d216731
f5db29d05f57266165ce92dc18fd0a30
1735dca92794943ddaf277828ee04c27
0281ea107935015491cda2b372a0997d
1d0d8ef1ea0ea1415eeee40404ed09cc

단지 200 달러 이상이었고, 보트가 시작되었을 때, 그것은 큰 일방적인 시장에 맞닥뜨렸습니다. 손실을 덮기 위해 시간이 필요합니다. 스팟 그리드 전략의 가장 큰 장점은: 안전하게 잠을 자십시오! 전략의 안정성은 괜찮고 5월 27일 이후로 수정하지 않았습니다.


더 많은