O recurso está a ser carregado... Carregamento...

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 design baseado na plataforma de negociação quântica FMZ (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 API da plataforma, descobri que as plataformas geralmente fornecem as interfaces agregadas. Ok, usamos a interface de dados de mercado agregados para obter os dados.

  • O segundo problema é os ativos da conta. Para que possamos executar estratégias de símbolos múltiplos, devemos considerar a gestão separada dos ativos de cada par de negociação, e obter todos os dados de ativos e registrá-los uma vez.

Uma vez que é necessário avaliar os activos disponíveis quando se fazem encomendas, não é necessário obter os dados antes do 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)
    }
    

    Novice, Check it Out —— Take You to Cryptocurrency Quantitative Trading (6)

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

    Novice, Check it Out —— Take You to Cryptocurrency Quantitative Trading (6)

    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.

Novice, Check it Out —— Take You to Cryptocurrency Quantitative Trading (6)

Novice, Check it Out —— Take You to Cryptocurrency Quantitative Trading (6)

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 informações