Novato, verifique Leve-o para o Comércio Quantitativo de Criptomoedas (6)

Autora:Ninabadass, Criado: 2021-04-21 18:13:03, Atualizado: 2021-04-22 12:00:05

Novato, verifique Leve-o para o Comércio Quantitativo de Criptomoedas (6)

No último artigo, fizemos uma estratégia de grade simples juntos. Neste artigo, atualizamos e expandimos essa estratégia em uma estratégia de grade de pontos de vários símbolos, e deixamos essa estratégia ser testada na prática. O objetivo não é encontrar um "santo graal", mas discutir vários problemas e soluções no processo de projeto de estratégias. Este artigo explicará parte da minha experiência no projeto da estratégia. O conteúdo deste artigo é ligeiramente complicado e requer uma certa base em programação.

Pensamento do design baseado na necessidade de estratégia

Neste artigo, tal como no anterior, discutimos sobre o projeto baseado na FMZ Quant Trading Platform (FMZ.COM).

  • Símbolo múltipla Para ser honesto, quero que a estratégia da rede não sóBTC_USDT, mas tambémLTC_USDT/EOS_USDT/DOGE_USDT/ETC_USDT/ETH_USDTDe qualquer forma, para os pares de negociação spot, operar negociação de grade de todos os símbolos que você quer negociar ao mesmo tempo.

    Sim, é bom capturar as cotações vibratórias de vários símbolos.
    Embora o requisito pareça simples, torna-se difícil quando se começa a desenhar.

      1. Primeiro, obter as cotações de mercado de vários símbolos. É o primeiro problema a ser resolvido. Depois de ler a documentação da API da plataforma, descobri que as plataformas geralmente fornecem as interfaces agregadas.
      1. O segundo problema é os ativos da conta. Para que possamos executar estratégias de símbolos múltiplos, devemos considerar gerenciar separadamente os ativos de cada par de negociação e obter todos os dados de ativos e registrá-los uma vez. Por que devemos obter os dados de ativos da conta? E por que também registrar cada par de negociação separadamente?

    Uma vez que é necessário avaliar os activos disponíveis quando se fazem encomendas, não é necessário obter os dados antes de se proceder ao julgamento? Além disso, é necessário calcular o rendimento.Deveríamos registar primeiro os dados iniciais dos activos da conta corrente e, em seguida, obter os dados dos activos da conta corrente e calcular o lucro e a perda comparando-os com o inicial? Felizmente, a interface da conta de ativos de uma plataforma normalmente retorna todos os dados de ativos de moeda, então só precisamos obtê-los uma vez, e depois processar os dados.

      1. Design de parâmetros de estratégia. O design de parâmetros da estratégia de símbolos múltiplos é bastante diferente do design de parâmetros de um único símbolo, porque mesmo a lógica de negociação de cada símbolo na estratégia de símbolos múltiplos é a mesma, é possível que os parâmetros de cada símbolo durante a negociação sejam diferentes. Por exemplo, na estratégia de grade, você pode querer negociar 0,01 BTC cada vez quando fazendo o par de negociação BTC_USDT, mas é obviamente inapropriado usar esse parâmetro (negociando 0,01 moeda) quando fazendo DOGE_USDT. Claro, você também pode processar pela quantidade de USDT. Mas ainda haverá problemas. E se você quiser negociar 1000U por BTC_USDT e 10U por DOGE_USDT? A demanda nunca pode ser satisfeita. Provavelmente, alguns estudantes podem pensar na questão, e propor que, Eu posso definir mais grupos de parâmetros, e separadamente controlar os parâmetros de diferentes pares de negociação a serem operados. O que ainda não pode satisfazer flexivelmente a necessidade, para quantos grupos de parâmetros devem ser definidos? Portanto, pense completamente na necessidade de diferenciação ao projetar parâmetros de estratégia de vários símbolos.
        Por exemplo:
      ETHUSDT:100:0.002|LTCUSDT:20:0.1
      

      O símbolo é utilizado para dividir os dados de cada símbolo, indicando que:ETHUSDT:100:0.002Controla o par de negociação ETH_USDT, eLTCUSDT:20:0.1O símbolo de segmentação é o símbolo de segmentação que controla o par LTC_USDT. Em...ETHUSDT:100:0.002, ETHUSDT representa o par de negociação que você deseja operar; 100 é o espaçamento da grade; 0.002 é o valor negociado de ETH de cada grade; : é usado para dividir os dados mencionados acima (certamente, as regras dos parâmetros são feitas pelo designer de estratégia; você pode projetar o que quiser com base em suas necessidades).
      Essas cadeias já contêm as informações de parâmetros de cada símbolo que você precisa operar. Você pode analisar as cadeias e atribuir valores às variáveis na estratégia, para controlar a lógica de negociação de cada símbolo. Como analisar? Vamos usar o exemplo mencionado acima.

      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

      Claro, você pode usar diretamente strings JSON, que é mais fácil.

      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. Persistência dos dados Há uma grande diferença entre uma estratégia prática e uma estratégia de ensino. A estratégia de ensino no último artigo é apenas para o teste inicial da lógica e da concepção da estratégia. Há mais problemas para se preocupar quando realmente executar uma estratégia em um bot. Ao executar um bot, o bot pode ser iniciado e interrompido. Nesse momento, todos os dados durante a execução do bot serão perdidos. Então, como continuar o status anterior ao reiniciar o bot, depois que o bot é interrompido? Aqui, é necessário salvar persistentemente os dados-chave quando o bot está sendo executado, para que os dados possam ser lidos e o bot continue a ser executado quando o bot é reiniciado. Pode usar o_G()Função em FMZ Quant, ou utilizar a função de operaçãoDBExec()na base de dados, e você pode consultar a documentação FMZ API para detalhes.

      Por exemplo, queremos projetar uma função de limpeza usando a função_G(), para salvar os dados da rede.

      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. Limite de precisão do montante da encomenda, preço da encomenda, volume mínimo da encomenda, montante mínimo da encomenda, etc.

      O sistema de backtest não tem limites tão rígidos no volume de ordens e precisão de ordens; mas em um bot, cada plataforma tem padrões rígidos para o preço de ordem e volume de ordem, e diferentes pares de negociação têm limites diferentes.

      Para casos de vários símbolos, o requisito é mais complicado. Para uma estratégia de símbolo único, você pode projetar um parâmetro para especificar informações como precisão. No entanto, quando você projeta uma estratégia de vários símbolos, é óbvio que escrever a informação em um parâmetro tornará o parâmetro muito tedioso.

      Neste momento, você precisa verificar a documentação da API da plataforma para ver se há interfaces para informações relacionadas a pares de negociação na documentação. Se houver essas interfaces, você pode projetar uma interface de acesso automático na estratégia para obter informações como precisão e configurá-la na informação do par de negociação no comércio (em suma, a precisão é obtida automaticamente da plataforma e, em seguida, adaptada à variável relacionada ao parâmetro de estratégia).

      1. Adaptação de uma plataforma diferente Por que o problema é mencionado no final? O tratamento de todos os problemas que mencionámos acima conduzirá ao último problema, pois pretendemos utilizar a interface de mercado agregada na estratégia, a adaptação do acesso à informação, a adaptação do acesso à informação, a adaptação do acesso à informação, a adaptação do acesso à informação, a adaptação do acesso à informação, a adaptação do acesso à informação, a adaptação do acesso à informação, a adaptação do acesso à informação, a adaptação do acesso à informação, a adaptação do acesso à informação, a adaptação do acesso à informação, a adaptação do acesso à informação, a adaptação do acesso à informação, a adaptação do acesso à informação, a adaptação do acesso à informação, a adaptação do acesso à informação, a adaptação do acesso à informação, a adaptação do acesso à informação, a adaptação do acesso à informação, a adaptação do acesso à informação, a adaptação do acesso à informação, a adaptação do acesso à informação, a adaptação do acesso à informação, a adaptação do acesso à informação e a informação. Uma vez que a nossa estratégia prevê utilizar a interface de mercado agregada, soluções como o acesso à precisão do par de negociação da plataforma e outras adaptações de dados, bem como o acesso às informações da conta para processar cada par de negociação separadamente, etc., trarão grandes diferenças devido a diferentes plataformas. Existem diferenças na chamada de interface e diferenças no mecanismo. Para plataformas spot, a diferença é comparativamente pequena se a estratégia de grade for estendida para a versão de futuros. As diferenças no mecanismo de cada plataforma são ainda maiores. Uma solução é projetar uma biblioteca de modelos FMZ; escrever o projeto de implementação da diferenciação na biblioteca para reduzir o acoplamento entre a própria estratégia e a plataforma. A desvantagem disso é que você precisa escrever uma biblioteca de modelos, e neste modelo, implementar especificamente a diferenciação com base em cada plataforma.

Projetar uma biblioteca de modelos

Com base na análise acima, concebemos uma biblioteca de modelos para reduzir a ligação entre estratégia, mecanismo de plataforma e interface.
Podemos projetar a biblioteca de modelos assim (parte do código é omitido):

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
}

No modelo, implemente a escrita de código voltada para uma forma de jogo específica; tome o bot simulado FMZ WexApp como exemplo:

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
            })
        })        
    }
}

Será muito fácil utilizar o modelo na estratégia:

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)
    })
}

Bot de Estratégia

É muito simples projetar e escrever a estratégia com base no modelo acima. Toda a estratégia tem cerca de mais de 300 linhas de código.

img

img

Neste momento, tem perdas.T_T, por isso o código fonte não será fornecido.

Existem vários códigos de registo; se estiver interessado, pode experimentá-los no wexApp:

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

A maior vantagem da estratégia da rede spot é: sentir-se seguro para dormir! A estabilidade da estratégia está bem, e não a modifiquei desde 27 de Maio.


Mais.