資源の読み込みに... 荷物...

初心者向け暗号通貨量的な取引 - 暗号通貨量的な取引に近づく (6)

作者: リン・ハーンFMZ~リディア, 作成日:2022-08-05 17:13:26, 更新日:2023-09-21 21:02:17

img

前回の記事では,私たちはシンプルなグリッド戦略を一緒に作りました. この記事では,この戦略をマルチ種スポットグリッド戦略にアップグレードし,拡張し,この戦略を実践でテストさせました. 目的は"聖杯"を見つけることではなく,戦略を設計する際のさまざまな問題と解決策を議論することです. この記事では,この戦略を設計する私の経験の一部を説明します. この記事の内容は少し複雑で,プログラミングの一定の基礎が必要です.

戦略的ニーズに基づくデザイン思考

この記事では,前回の記事と同様に,FMZ Quant (FMZ.COM).

  • 多種 このグリッド戦略はBTC_USDT,しかしまたLTC_USDT/EOS_USDT/DOGE_USDT/ETC_USDT/ETH_USDTスポット取引のペアと 実行したい品種は同時にグリッドで取引されます.

    多種多様性の 変動する市場を 捕まえることは良い気分です デザインの過程で 問題が生じます デザインの過程で

      1. まず,複数の品種の市場コートが得られます. これは解決すべき最初の問題です. 取引所のAPIドキュメントを閲覧した後,ほとんどの取引所が総合的な市場インターフェースを提供していることを発見しました. データを集計する市場インターフェースを使います
      1. 2つ目の問題は,口座資産です. 多種戦略であるため,それぞれの取引ペア資産の管理を別々に検討する必要があります. そして,すべての資産のデータを一度に取得し,記録する必要があります. なぜ口座資産データを取得する必要があるのか? なぜ各ペアの記録を分離する必要があるのか? オーダーをするときに 入手可能な資産を 判断する必要があるからです 判断する前に 入手する必要があるのでしょうか? 初期口座の資産データを記録し, 経常口座の資産データを取得し, 利益と損失を計算するために初期データと比較する必要があります. 取引所の資産アカウントインターフェースは 通常すべての通貨資産データを返します データを一度だけ取得し 処理します
      1. 戦略パラメータ設計.マルチ種のパラメータデザインは,単品種のパラメータデザインとはかなり異なります.マルチバリエーションの各種の取引論理が同じですが,取引中のパラメータが異なる可能性があります.例えば,グリッド戦略では,BTC_USDT取引ペアを行うときに毎回0.01 BTCを取引したいかもしれませんが,DOGE_USDTを行うときにこのパラメータ (0.01コインの取引) を使用することは明らかに不適切です.もちろん,USDT金額も扱えます.しかし,まだ問題があります.BTC_USDTに1000UとDOGE_USDTに10Uを取引したい場合はどうでしょうか.需要は決して満たされません. 異なる取引ペアのパラメータを個別に制御するために複数のパラメータを設定することができます. 異なる取引ペアのパラメータを個別に制御するために,いくつかのパラメータを設定することができます. これはまだ必要を満たすのに十分な柔軟性がない,パラメータの何セットを設定することが良いですか?パラメータの3セットが設定されています,私が4種類のしたい場合はどうですか?私は戦略を変更しパラメータを増やす必要があります?.. したがって,マルチ種の戦略のパラメータを設計する際には,このような差別化されたパラメータのニーズを完全に考慮する必要があります. 解決策の一つは,パラメータを通常の文字列またはJSON文字列として設計することです. 例えば:
      ETHUSDT:100:0.002|LTCUSDT:20:0.1
      

      は各種のデータに分けます.ETHUSDT:100:0.002ETH_USDTの取引ペアをコントロールし,LTCUSDT:20:0.1中央のは分割するために使用されます.ETHUSDT:100:0.002100 はグリッド間隔,0.002 は各グリッドで取引される ETH コインの数,そして: はこれらのデータを分割する (もちろん,これらのパラメータルルルールは戦略デザイナーによって作られています. この文字列には,あなたがしたい各種のパラメータ情報が含まれています. この文字列を戦略で解析し,各種の取引論理を制御するために戦略の変数に値を割り当てます. どのように解析しますか? それでも上記の例を使用します.

      function main() {
          var net = []  // The recorded grid parameters, use the data when running to the grid trading logic
          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 volume
              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, use the data when running to the grid trading logic        
          _.each(net, function(pair) {
              Log("Trading pairs:", pair.symbol, pair)
          })
      }
      

      img

      1. データの持続性 実践に応用できる戦略とチュートリアル戦略には大きな違いもあります. 前回の記事のチュートリアル戦略は,戦略の論理とデザインの予備的なテストに過ぎず,現実の世界では考慮すべき問題が多くあります. リアルボットでは,実際の取引を開始し,停止することが可能です. この時点で,実際のボット操作中のすべてのデータが失われます. では,実際のボットが停止した後も以前の状態で実行し続けるために再起動するにはどうすればよいですか? 本物のボットが実行しているときにキーデータを持続的に保存する必要があります. これにより,データが読み取られ,再起動すると実行を続けることができます. 薬剤師が_G()FMZ 定量取引プラットフォームの機能,またはデータベース操作機能を使用DBExec()詳細については FMZ API のドキュメントを確認できます

      尾のスイープ関数を設計し,_G()グリッドデータを保存する機能

      var net = null 
      function main() {  // Strategy main functions
          // Read the stored net first
          net = _G("net")
          
          // ...
      }
      
      function onExit() {
          _G("net", net)
          Log("Perform tail-sweeping processing and save data", "#FF0000")
      }
      
      function onexit() {    // The exit sweep function defined by the platform system, triggered the execution when the real bot is clicked to stop
          onExit()
      }
      
      function onerror() {   // The abnormal exit function defined by the platform system, triggered the execution when the program is abnormal
          onExit()
      }
      
      1. 注文量精度,注文価格精度,最低注文量,最低注文金額などなどの制限

      バックテストシステムは,注文金額や注文精度にそのような厳格な制限を課さないが,実際のボットに注文を出すとき,各取引所は価格と注文金額の厳格な基準を有することができ,これらの制限は異なる取引所で同じではない.したがって,問題なくバックテストシステムでテストする初心者がいます.実際のボットが起動すると,取引が起動するとさまざまな問題が発生し,エラーメッセージの内容が読み取られず,さまざまな狂気の現象が現れます.

      多種の場合では,この要件はより複雑です.単種戦略では,正確性などの情報を指定するパラメータを設計できますが,多種戦略を設計する場合は,この情報をパラメータに書き込むことがパラメータを非常に膨らませるのが明らかです.

      この時点で,取引所の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()
    
    // Interfaces to be implemented
    self.interfaceGetTickers = null   // Create a function to asynchronously obtain a thread of aggregated market data
    self.interfaceGetAcc = null       // Create a function that asynchronously obtains account data thread
    self.interfaceGetPos = null       // Get a position
    self.interfaceTrade = null        // Create concurrent orders
    self.waitTickers = null           // Waiting for concurrent market data 
    self.waitAcc = null               // Waiting for account concurrent data
    self.waitTrade = null             // Waiting for order concurrent data
    self.calcAmount = null            // Calculate the order volume based on data such as trading pair accuracy
    self.init = null                  // Initialization work, obtaining data such as accuracy
    
    // Execute the configuration function to configure the object
    funcConfigure(self)

    // Check whether the interfaces agreed by configList are implemented
    _.each(configList, function(funcName) {
        if (!self[funcName]) {
            throw "interface" + funcName + "unimplemented"
        }
    })
    
    return self
}

$.createBaseEx = createBaseEx
$.getConfigureFunc = function(exName) {
    dicRegister = {
        "Futures_OKCoin" : funcConfigure_Futures_OKCoin,    // Implementation of OK futures
        "Huobi" : funcConfigure_Huobi,
        "Futures_Binance" : funcConfigure_Futures_Binance,
        "Binance" : funcConfigure_Binance,
        "WexApp" : funcConfigure_WexApp,                    // 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 trading pair information
        var symbolInfo = self.getSymbolInfo(symbol)
        if (!symbol) {
            throw symbol + ", the trading pair information cannot be checked"
        }
        var tradeAmount = null 
        var equalAmount = null  // Number of coins recorded
        if (type == self.OPEN_LONG || type == self.COVER_SHORT) {
            tradeAmount = _N(amount * price, parseFloat(symbolInfo.pricePrecision))
            // Check the minimum trading volume
            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))
            // Check the minimum trading volume
            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() {   // Functions that deal with conditions such as accuracy automatically
        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 get tickers
    ex.goGetTickers()
    var tickers = ex.getTickers()
    Log("tickers:", tickers)
    
    // Test to obtain account information
    ex.goGetAcc(symbol, ts)
    
    _.each(arrTestSymbol, function(symbol) {        
        _.each(tickers, function(ticker) {
            if (symbol == ticker.originalSymbol) {
                // print ticker data
                Log(symbol, ticker)
            }
        })

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

戦略 リアルボット

上記のテンプレートに基づいて戦略を設計し書くことは非常に簡単です. 戦略全体が約300+行で,デジタル通貨スポット多種グリッド戦略を実装します.

img

img

今,お金を失っている.T_T,ソースコードは,今のところ公開されません.

登録コードがいくつかあります. 興味がある場合は,wexAppを使って試してみてください.

Buy address: https://www.fmz.com/m/s/284507
Registration code: 
adc7a2e0a2cfde542e3ace405d216731
f5db29d05f57266165ce92dc18fd0a30
1735dca92794943ddaf277828ee04c27
0281ea107935015491cda2b372a0997d
1d0d8ef1ea0ea1415eeee40404ed09cc

200Uを超えると 走り始めてから 大きな片側市場に出会いましたが ゆっくりと回復しました スポットグリッドの最大の利点は 5月27日以降 安定性が良くなっています


関連性

もっと