В процессе загрузки ресурсов... загрузка...

Новичок, проверьте это Возьмём вас на криптовалютный количественный трейдинг (6)

Автор:Нинабадасс., Создано: 2021-04-21 18:13:03, Обновлено: 2021-04-22 12:00:05

Новичок, проверьте это Возьмём вас на криптовалютный количественный трейдинг (6)

В предыдущей статье мы вместе разработали простую стратегию сетки. В этой статье мы обновили и расширили эту стратегию в стратегию сетки с множеством символов, и позволили этой стратегии быть протестирована на практике. Цель не в том, чтобы найти "святой Грааль", а в том, чтобы обсудить различные проблемы и решения в процессе разработки стратегий. Эта статья объяснит некоторые из моих опытов в разработке стратегии. Содержание этой статьи немного сложно и требует определенной основы в программировании.

Мышление дизайна на основе стратегических требований

В этой статье, как и в предыдущей, мы обсуждаем дизайн на базе FMZ Quant Trading Platform (FMZ.COM).

  • Многочисленный символ Честно говоря, я хочу, чтобы стратегия сети не толькоBTC_USDT, но такжеLTC_USDT/EOS_USDT/DOGE_USDT/ETC_USDT/ETH_USDTВ любом случае, для спотовых торговых пар, выполните сетевую торговлю всеми символами, которые вы хотите торговать одновременно.

    Да, приятно запечатлеть вибрирующие котировки рынка с несколькими символами.
    Хотя требование звучит просто, оно становится сложным, когда вы начинаете проектировать.

      1. Во-первых, получение рыночных котировок нескольких символов. Это первая проблема, которую нужно решить. После прочтения документации API платформы, я обнаружил, что платформы обычно предоставляют агрегированные интерфейсы. Хорошо, мы используем агрегированный интерфейс данных рынка для получения данных.
      1. Вторая проблема - активы счета. Для того, чтобы выполнять стратегии с несколькими символами, мы должны рассмотреть возможность отдельного управления активами каждой торговой пары и получить все данные актива и записать их один раз. Почему мы должны получить данные актива счета? И почему также записывать каждую торговую пару отдельно?

    Поскольку вам необходимо оценить доступные активы при размещении заказов, разве не необходимо получить данные до вынесения решения? Кроме того, прибыль должна быть рассчитана.Прежде всего, следует ли записывать первоначальные данные о активах, а затем получать данные о активах текущего счета и рассчитывать прибыль и убыток, сравнивая с первоначальным? К счастью, интерфейс счета активов платформы обычно возвращает все данные валютных активов, поэтому нам нужно получить их только один раз, а затем обработать данные.

      1. Дизайн параметров стратегии. Дизайн параметров стратегии с несколькими символами довольно отличается от параметров стратегии с одним символом, поскольку даже логика торговли каждого символа в стратегии с несколькими символами одинакова, возможно, что параметры каждого символа во время торговли будут отличаться. Например, в стратегии сетки вы можете захотеть торговать 0,01 BTC каждый раз, когда делаете торговую пару BTC_USDT, но, очевидно, неуместно использовать этот параметр (торговля 0,01 валютой), когда делаете DOGE_USDT. Конечно, вы также можете обрабатывать по количеству USDT. Но все равно будут проблемы. Что делать, если вы хотите торговать 1000U BTC_USDT и 10U DOGE_USDT? Спрос никогда не может быть удовлетворен. Вероятно, некоторые студенты могут подумать о вопросе, и предложить, что я могу установить больше групп параметров, и отдельно контролировать параметры различных торговых пар, которые будут работать. Это все еще не может гибко удовлетворить потребность, сколько групп параметров должны быть установлены? мы установили 3 группы; что, если мы хотим работать 4 символов? нужно ли нам модифицировать стратегию и увеличить параметры? Следовательно, полностью подумайте о необходимости дифференциации при разработке параметров стратегии с несколькими символами.
        Например:
      ETHUSDT:100:0.002|LTCUSDT:20:0.1
      

      используется для разделения данных каждого символа, указывая, чтоETHUSDT:100:0.002контролирует торговую пару ETH_USDT, иLTCUSDT:20:0.1 в середине играет роль сегментации. Внутри.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. Устойчивость данных Существует большая разница между практической стратегией и стратегией обучения. Стратегия обучения в предыдущей статье предназначена только для первоначального тестирования логики и дизайна стратегии. Есть больше проблем, о которых нужно беспокоиться при фактическом запуске стратегии в боте. При запуске бота бот может быть запущен и остановлен. В это время все данные во время запуска бота будут потеряны. Итак, как продолжить предыдущий статус при перезагрузке бота после того, как бот будет остановлен? Здесь необходимо постоянно сохранять ключевые данные при запуске бота, чтобы данные могли быть прочитаны и бот продолжал работать при перезагрузке бота. Вы можете использовать_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 долларов США, и когда бот только начался, он столкнулся с большим односторонним рынком. Ему нужно время, чтобы покрыть убытки. Самое большое преимущество стратегии спотовой сетки заключается в следующем: чувствуйте себя в безопасности, чтобы спать! Стабильность стратегии в порядке, и я не менял ее с 27 мая.


Больше