Quantifying the coin circle is a whole new thing - it brings you closer to quantifying the coin circle.

Author: The Little Dream, Created: 2021-06-04 10:08:48, Updated: 2023-09-21 21:01:06

img

The coin circle quantification transaction is a new look and brings you closer to the coin circle quantification.

In the last post we worked together on a simple grid strategy, and in this post we have upgraded and expanded this strategy to a multi-variety real-time grid strategy and have this strategy tested in real life. The goal is not to find a "holy grail", but to explore the various problems and solutions in the design strategy from the strategic design. This article will explain some of my experiences in designing this strategy.

Design thinking based on strategic needs

This article, like the previous one, is still based on inventor quantification.FMZ.COMI've been thinking about this for a while.

  • Multivariate I'm just saying that I think this grid strategy is not just aboutBTC_USDTI can do that.LTC_USDT/EOS_USDT/DOGE_USDT/ETC_USDT/ETH_USDTIn addition, the exchange rate for the stock exchange, the exchange rate for the stock exchange, the exchange rate for the stock exchange, the exchange rate for the stock exchange, the exchange rate for the stock exchange, the exchange rate for the stock exchange and the exchange rate for the stock exchange.

    I'm so happy to see that you're able to capture the vibration of many different kinds of music. The demand sounds simple, but the problem comes at the time of design.

    • 1, firstly, the acquisition of multiple markets. This is the first problem to be solved. After looking at the API documentation of the exchange, I found that the general exchange offers an aggregated market interface. OK, we'll get the data from the aggregated market interface.

    • 2, The second problem is the account assets. Since it is a multi-variety strategy, it is necessary to consider the individual management of each transaction for each asset. Because you need to judge the available assets at the time of ordering, or do you need to get them and judge again? Or do you need to calculate the gain, or do you need to record an initial account asset data first, then get the current account asset data and calculate the gain and loss against the initial comparison? It is good that the asset account interface of the exchange usually also returns all currency asset data, which we only need to retrieve once and then process the data.

    • 3, Strategy Parameter Design. The parameter design of multi-varieties and single-varieties is very different, because although the trading logic of the various varieties is the same, it is possible that the parameters when trading are different. For example, the grid strategy, it may be possible to do BTC_USDT trading pair when hoping for 0.01 BTC per trade, but DOGE_USDT is obviously not suitable if this parameter is still used. Maybe some of my classmates will think about it and then say: I can set several sets of parameters separately to control the parameters of the different pairs of transactions to be done. This is still not flexible enough to meet the needs, is it good to set several sets of parameters? Set three sets of parameters, what if I do four varieties? So when designing parameters for multi-variety strategies, one solution is to design the parameters as ordinary strings or JSON strings. For example:

      ETHUSDT:100:0.002|LTCUSDT:20:0.1
      

      The data for each of these varieties is divided into two parts, which means that the data for each of these varieties is divided into two parts.ETHUSDT:100:0.002It's the same with the ETH_USDT trading pair.LTCUSDT:20:0.1It is the controller of the LTC_USDT transaction pair.ETHUSDT:100:0.002In this case, the ETHUSDT indicates what pair of trades you're going to make, 100 is the grid spacing, 0.002 is the number of ETH coins traded per grid, and the nickname is the split of these data (of course, these parametric rules are made by the strategy designer, and you can design whatever you want). These strings contain the parameter information for each of the varieties you want to do, and you can parse these strings in the policy, assign specific variables to the policy, and use them to control the transaction logic for each variety.

      function main() {
          var net = []  // 记录的网格参数,具体运行到网格交易逻辑时,使用这里面的数据
          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]              // 交易对名称
              var diff = parseFloat(arr[1])    // 网格间距
              var amount = parseFloat(arr[2])  // 网格下单量
              net.push({symbol : symbol, diff : diff, amount : amount})
          })
          Log("网格参数数据:", net)
      }
      

      img

      If you want to see the parameters, you can also use JSON strings directly, which is easier.

      function main() {        
          var params = '[{"symbol":"ETHUSDT","diff":100,"amount":0.002},{"symbol":"LTCUSDT","diff":20,"amount":0.1}]'
          var net = JSON.parse(params)  // 记录的网格参数,具体运行到网格交易逻辑时,使用这里面的数据        
          _.each(net, function(pair) {
              Log("交易对:", pair.symbol, pair)
          })
      }
      

      img

    • 4, data permanence There is also a big difference between real-world tactics and teaching strategies. The previous teaching strategies are only a preliminary test of the tactical logic, design, and more questions to consider during real-world combat. In real-time, the real disk may be turned on or off. This is where you need to make a persistent save of the critical data when the disk is running, so that when you restart it, you can read the data and continue running. Inventors can use it on their quantitative trading platform._G()Functions, or using a database operation functionDBExec()You can check the FMZ API documentation here.

      For example, we designed a sweep function that uses_G()Functions that store grid data.

      var net = null 
      function main() {  // 策略主函数
          // 首先读取储存的net
          net = _G("net")
          
          // ...
      }
      
      function onExit() {
          _G("net", net)
          Log("执行扫尾处理,保存数据", "#FF0000")
      }
      
      function onexit() {    // 平台系统定义的退出扫尾函数,在点击实盘停止时触发执行
          onExit()
      }
      
      function onerror() {   // 平台系统定义的异常退出函数,在程序发生异常时触发执行
          onExit()
      }
      
    • 5 Limitations such as order precision, order price precision, minimum order quantity, minimum order amount

      The retesting system does not have such strict restrictions on the order quantity, order accuracy, etc., but in real time, each exchange can have strict standards for the price at the time of the order, order quantity, and these restrictions are not the same for each transaction pair. So there is often a lot of new in the retesting system.

      For multi-variety situations, this need is more complicated. For single-variety policies, you can design a parameter to specify precision and other information, but when designing multi-variety policies, obviously the information written in the parameters will appear very parametric.

      At this point, you need to check the API documentation of the exchange to see if there are any interfaces to the relevant information in the exchange documentation. If these interfaces are available, you can design the accuracy of the auto-access interface in the policy, configure it to the information of the transaction that is involved in the trade (simply put, what is the accuracy of the auto-request to the exchange, and then adapt it to the variables related to the policy parameters).

    • 6. Adaptation to different exchanges Why put this question at the end? The last problem arises because the solutions we have discussed above are based on the strategy of using aggregated market interfaces, accurate data adaptation, access to account information, and so on. These solutions vary greatly depending on the exchange. There are differences in interface calls, differences in mechanisms. For spot exchanges, the differences are slightly smaller if this grid strategy is extended to futures versions. There are greater differences in the mechanisms of the various exchanges. One solution is to design an FMZ template library. The disadvantage of doing this is that a template library needs to be written and implemented specifically for each exchange.

Design a template library

Based on the above analysis, a template library was designed to reduce the overlap between the strategy and the exchange mechanism and interface.

We can design this template class library like this (some code omitted):

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()
    
    // 需要实现的接口
    self.interfaceGetTickers = null   // 创建异步获取聚合行情数据线程的函数
    self.interfaceGetAcc = null       // 创建异步获取账户数据线程的函数
    self.interfaceGetPos = null       // 获取持仓
    self.interfaceTrade = null        // 创建并发下单
    self.waitTickers = null           // 等待并发行情数据 
    self.waitAcc = null               // 等待账户并发数据
    self.waitTrade = null             // 等待下单并发数据
    self.calcAmount = null            // 根据交易对精度等数据计算下单量
    self.init = null                  // 初始化工作,获取精度等数据
    
    // 执行配置函数,给对象配置
    funcConfigure(self)

    // 检测configList约定的接口是否都实现
    _.each(configList, function(funcName) {
        if (!self[funcName]) {
            throw "接口" + funcName + "未实现"
        }
    })
    
    return self
}

$.createBaseEx = createBaseEx
$.getConfigureFunc = function(exName) {
    dicRegister = {
        "Futures_OKCoin" : funcConfigure_Futures_OKCoin,    // OK期货的实现
        "Huobi" : funcConfigure_Huobi,
        "Futures_Binance" : funcConfigure_Futures_Binance,
        "Binance" : funcConfigure_Binance,
        "WexApp" : funcConfigure_WexApp,                    // wexApp的实现
    }
    return dicRegister
}

In the template, write to specific exchanges, for example with FMZ's 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) {
        // 获取交易对信息
        var symbolInfo = self.getSymbolInfo(symbol)
        if (!symbol) {
            throw symbol + ",交易对信息查询不到"
        }
        var tradeAmount = null 
        var equalAmount = null  // 记录币数
        if (type == self.OPEN_LONG || type == self.COVER_SHORT) {
            tradeAmount = _N(amount * price, parseFloat(symbolInfo.pricePrecision))
            // 检查最小交易量
            if (tradeAmount < symbolInfo.min) {
                Log(self.name, " tradeAmount:", tradeAmount, "小于", symbolInfo.min)
                return false 
            }
            equalAmount = tradeAmount / price
        } else {
            tradeAmount = _N(amount, parseFloat(symbolInfo.amountPrecision))
            // 检查最小交易量
            if (tradeAmount < symbolInfo.min / price) {
                Log(self.name, " tradeAmount:", tradeAmount, "小于", symbolInfo.min / price)
                return false 
            }
            equalAmount = tradeAmount
        }
        return [tradeAmount, equalAmount]
    }

    self.init = function init() {   // 自动处理精度等条件的函数
        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
            })
        })        
    }
}

Then the strategy is simple to use this template:

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()
    
    // 测试获取行情
    ex.goGetTickers()
    var tickers = ex.getTickers()
    Log("tickers:", tickers)
    
    // 测试获取账户信息
    ex.goGetAcc(symbol, ts)
    
    _.each(arrTestSymbol, function(symbol) {        
        _.each(tickers, function(ticker) {
            if (symbol == ticker.originalSymbol) {
                // 打印行情数据
                Log(symbol, ticker)
            }
        })

        // 打印资产数据
        var acc = ex.getAcc(symbol, ts)
        Log("acc:", acc.symbol, acc)
    })
}

Strategy of the real world

Based on the above templates, it is quite simple to write a strategy, the whole strategy is about 300+ lines, which implements a multi-variety grid strategy for digital currency spot.

img

img

Currently losing moneyT_TThe source code is not available for a while.

WexApp is a free app that allows users to download and play games from the WexApp app.

购买地址: https://www.fmz.com/m/s/284507
注册码: 
adc7a2e0a2cfde542e3ace405d216731
f5db29d05f57266165ce92dc18fd0a30
1735dca92794943ddaf277828ee04c27
0281ea107935015491cda2b372a0997d
1d0d8ef1ea0ea1415eeee40404ed09cc

When you run up to 200 U, you run into a big one-sided crowd, bleeding back slowly. The stability is back, it hasn't moved since May 27 and the futures grid is not even trying for a while.


Related

More

Quantitative measurements of small insectsThanks to the dam, there are lights on the curb.

Nine sunsI'm going to kill you.

The trajectory of lifeThank you.

The Little DreamThank you for your support!

The Little DreamThank you for your support!