avatar of 发明者量化-小小梦 发明者量化-小小梦
フォロー ダイレクトメッセージ
4
フォロー
1271
フォロワー

暗号通貨サークルでの定量取引の初心者の方は、こちらをぜひご覧ください - 暗号通貨サークルでの定量取引に近づく (VI)

作成日:: 2021-06-04 10:08:48, 更新日:: 2024-12-04 21:14:15
comments   6
hits   2596

暗号通貨サークルでの定量取引の初心者の方は、こちらをぜひご覧ください - 暗号通貨サークルでの定量取引に近づく (VI)

暗号通貨サークルでの定量取引の初心者の方は、こちらをぜひご覧ください - 暗号通貨サークルでの定量取引に近づく (VI)

前回の記事では、シンプルなグリッド戦略を共同で作成しました。この記事では、この戦略を多種多様なスポットグリッド戦略にアップグレードおよび拡張し、実際の戦闘でこの戦略をテストします。目的は「聖杯」を見つけることではなく、戦略を設計する際にさまざまな問題と解決策を探求することです。この記事では、この戦略を設計する際の私の経験の一部を説明します。この記事の内容は少し複雑で、プログラミングに関する一定の基礎知識が必要です。

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

この記事でも、前回の記事と同様に、Inventor Quantization (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の金額に応じて処理します。しかし、まだ問題はあります。1000U を BTC_USDT で、10U を DOGE_USDT で取引したいだけの場合はどうでしょうか? 需要を満たすことはできません。 学生の中には、この質問について考えた後、「複数のパラメータ グループを設定して、異なる取引ペアのパラメータを個別に制御できます」と言う人もいるかもしれません。 これでは、まだ柔軟にニーズを満たすことができません。パラメータのグループをいくつ設定すればよいでしょうか? 3セットのパラメータが設定されています。4種類の品種を取引したい場合はどうすればいいでしょうか?戦略を変更したりパラメータを追加したりすることは可能ですか? したがって、多品種戦略のパラメータを設計する際には、差別化されたパラメータの需要を十分に考慮する必要があります。 1 つの解決策は、パラメータを通常の文字列または JSON 文字列として設計することです。 例えば:
    ETHUSDT:100:0.002|LTCUSDT:20:0.1
    

    「|」は各品種のデータを区切っており、ETHUSDT:100:0.002ETH_USDT 取引ペアを制御します。LTCUSDT:20:0.1LTC_USDT 取引ペアを制御します。中央の「|」は区切り文字として機能します。 ETHUSDT:100:0.002ここで、ETHUSDTは取引したい取引ペアを示し、100はグリッド間隔、0.002は各グリッドで取引されるETHコインの数、そして「:」記号はこれらのデータを区切るために使用されます(もちろん、これらのパラメータルールは戦略設計者によって設定されます)。ニーズに合わせて設計できます。 これらの文字列には、取引する各商品のパラメータ情報が含まれています。戦略でこれらの文字列を解析し、戦略の変数に特定の値を割り当てて、各商品の取引ロジックを制御します。それで、どのように解析するのでしょうか?上記の例をもう一度使ってみましょう。

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

    暗号通貨サークルでの定量取引の初心者の方は、こちらをぜひご覧ください - 暗号通貨サークルでの定量取引に近づく (VI)

    このようにパラメータが解析されていることがわかります。もちろん、JSON 文字列を直接使用することもできます。その方が簡単です。

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

    暗号通貨サークルでの定量取引の初心者の方は、こちらをぜひご覧ください - 暗号通貨サークルでの定量取引に近づく (VI)

    1. データの永続性 また、実際に使える戦略と指導戦略の間には大きな違いがあります。前回の記事の指導戦略は、戦略のロジックと設計の予備テストにすぎません。実際の実践では、考慮すべき問題がさらにあります。実際の取引中に、実際の取引を開始および停止することができます。このとき、リアルタイム操作中のデータはすべて失われます。では、実際のディスクを停止してから再起動し、以前の状態で実行を継続するにはどうすればよいでしょうか? ここで、システムを再起動したときにデータを読み取って継続できるように、リアルタイム操作中にキーデータを永続化する必要があります。 Inventor定量取引プラットフォームで使用可能_G()関数、またはデータベース操作関数を使用するDBExec()詳細については、FMZ API ドキュメントを参照してください。

    例えば、スイープ関数を設計するには、_G()関数、グリッドデータを保存します。

    var net = null 
    function main() {  // 策略主函数
        // 首先读取储存的net
        net = _G("net")
    
    
        // ...
    }
    
    
    function onExit() {
        _G("net", net)
        Log("执行扫尾处理,保存数据", "#FF0000")
    }
    
    
    function onexit() {    // 平台系统定义的退出扫尾函数,在点击实盘停止时触发执行
        onExit()
    }
    
    
    function onerror() {   // 平台系统定义的异常退出函数,在程序发生异常时触发执行
        onExit()
    }
    
    1. 注文数量精度、注文価格精度、最小注文数量、最小注文金額等に関する制限

    バックテストシステムでは、注文数量や注文精度にそれほど厳しい制限は課せられませんが、実際の取引では、各取引所が注文価格や注文数量に厳しい基準を設けている場合があり、取引ペアごとのこれらの基準も非常に厳しいため、制限はそれほど厳しくありません。同じ。そのため、初心者はバックテスト システムをテストし、実際の市場で取引を開始するときにさまざまな問題に遭遇することがよくあります。その後、エラー メッセージを読むことさえなく、さまざまなクレイジーな問題を経験します [dog head]。

    品種が複数ある場合、この要件はさらに複雑になります。単一製品戦略の場合、精度などの情報を指定するためのパラメータを設計できます。ただし、複数製品戦略を設計する場合、この情報をパラメータに書き込むと、パラメータが非常に肥大化してしまうことは明らかです。

    この時点で、取引所の API ドキュメントをチェックして、取引所のドキュメントに取引ペア関連情報を含むインターフェースがあるかどうかを確認する必要があります。これらのインターフェースが利用可能であれば、戦略に自動アクセスインターフェースを設計して、精度などの情報を取得し、取引に関係する取引ペア情報に設定することができます(簡単に言えば、精度は取引所から自動的に要求され、次に、戦略パラメータ(変数)に適応します。

    1. さまざまな取引所への適応 なぜこの質問を最後にしたのでしょうか? 上記で述べた問題に対する解決策は、この最後の問題を引き起こすでしょう。なぜなら、私たちの戦略は、集約された市場インターフェースを使用し、取引所の取引ペアの精度やその他のデータ適応にアクセスし、アカウント情報にアクセスし、各取引ペアを個別に処理するなどを計画しているからです。これらのソリューションは取引所間で大きな違いがあります。 インターフェース呼び出しの違いとメカニズムの違いがあります。スポット取引所の場合、このグリッド戦略を先物バージョンに拡張すると、差は小さくなります。さまざまな取引所の仕組みの違いはさらに大きくなります。 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()
    
    // 需要实现的接口
    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
}

テンプレートでは、特定の交換実装用に記述します。たとえば、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) {
        // 获取交易对信息
        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
            })
        })        
    }
}

このテンプレートを戦略で使用するのは簡単です。

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

戦略リアルマーケット

上記のテンプレートに基づいて戦略を設計して記述するのは非常に簡単です。戦略全体は約 300 行以上で、デジタル通貨スポットの多種多様なグリッド戦略を実装します。

暗号通貨サークルでの定量取引の初心者の方は、こちらをぜひご覧ください - 暗号通貨サークルでの定量取引に近づく (VI)

暗号通貨サークルでの定量取引の初心者の方は、こちらをぜひご覧ください - 暗号通貨サークルでの定量取引に近づく (VI)

現在損失中T_T、当面ソースコードは公開されません。

登録コードをいくつか紹介します。ご興味があれば、wexApp で試してみてください。

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

ちょうど200台を超えたあたりで、走り始めたころに一方的な大きな市場に遭遇し、徐々に回復していきました。スポットグリッドの最大のメリットは、「ぐっすり眠れる!」 安定性は問題ありません。5月27日以降は触っていません。当分先物グリッドを試す勇気はありません。