前回の記事では,シンプルなグリッド戦略を一緒に作りました. この記事では,この戦略をマルチシンボルスポットグリッド戦略にアップグレードし,拡張し,この戦略を実践でテストしてみましょう. 目的は"聖杯"を見つけるのではなく,戦略の設計過程で様々な問題や解決策を議論することです. この記事では,戦略の設計における私の経験の一部を説明します. この記事の内容は少し複雑で,プログラミングの基礎が必要です.
この記事では,前回の記事と同様に,FMZ量子取引プラットフォーム (FMZ.COM).
複数のシンボル
ネットワーク戦略は 単にBTC_USDT
,しかしまたLTC_USDT
/EOS_USDT
/DOGE_USDT
/ETC_USDT
/ETH_USDT
取引したいすべてのシンボルのグリッド取引を同時に実行します.
複数のシンボルの 振動する市場コートを 捉えることが良い気分です
シンプルに聞こえるかもしれませんが デザインを始めると 難しくなります
注文をするときに利用可能な資産を判断する必要があるので,判断する前にデータを入手する必要はありませんか? また,収益を計算する必要があります. まず最初の資産データを記録すべきですか? そして,現在口座の資産データを取得し,最初のものと比較して利益と損失を計算すべきですか? 幸運にも プラットフォームの資産アカウントインターフェイスは 通常すべての通貨資産データを返します だから一度だけ取得し データを処理する必要があります
ETHUSDT:100:0.002|LTCUSDT:20:0.1
ETHUSDT:100:0.002
取引ペア ETH_USDT を制御し,LTCUSDT:20:0.1
中央のETHUSDT:100:0.002
,
この文字列には,既に操作に必要な各シンボルのパラメータ情報が含まれています.文字列を解析し,各シンボルの取引論理を制御するために戦略内の変数に値を割り当てることができます. どのように解析しますか?上記の例を使用しましょう.
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)
}
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)
})
}
_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()
}
バックテストシステムには,注文量と注文精度にそのような厳格な制限はありません.しかし,ボットでは,各プラットフォームは注文価格と注文量に対する厳格な基準を持ち,異なる取引ペアには異なる制限があります.したがって,初心者はしばしばバックテストシステムでOKEXをテストします.ボットで戦略を実行すると,取引が起動するとさまざまな問題が発生し,エラーメッセージの内容は読み取れないし,さまざまな狂気の現象が現れます.
複数のシンボルの場合,要求はより複雑である.単一のシンボルの戦略では,精度などの情報を指定するパラメータを設計することができます.しかし,複数のシンボルの戦略を設計する場合は,パラメータに情報を書き込むことがパラメータを非常に退屈なものにするのは明らかです.
この時点で,プラットフォームのAPIドキュメントをチェックして,ドキュメントに取引ペアに関連する情報にインターフェースがあるかどうかを確認する必要があります.これらのインターフェースがある場合は,精度などの情報を取得するために戦略に自動アクセスインターフェースを設計し,取引中の取引ペア情報に設定できます (要するに,精度はプラットフォームから自動的に取得され,戦略パラメータに関連する変数に適応します).
上記の分析に基づいて 戦略,プラットフォームメカニズム,インターフェースの結合を減らすために テンプレートライブラリを設計しました
テンプレートライブラリはこのように設計できます (コードの一部は省略されています):
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行以上のコードがあります. それは仮想通貨スポットマルチシンボルグリッド戦略を実装します.
今,それは損失を持っているT_T
ソースコードは提供されません.
登録コードはいくつかあります.興味がある場合は,wexAppで試してみてください:
Purchase Address: https://www.fmz.com/m/s/284507
Registration Code:
adc7a2e0a2cfde542e3ace405d216731
f5db29d05f57266165ce92dc18fd0a30
1735dca92794943ddaf277828ee04c27
0281ea107935015491cda2b372a0997d
1d0d8ef1ea0ea1415eeee40404ed09cc
スポット・グリッド戦略の最大の利点は: 眠るのに安心して! スポット・グリッド戦略の最大の利点は: 眠るのに安心して! 戦略の安定性は良好で 5月27日以降は変更していません 臨時的にフューチャー・グリッド戦略を試してみる気はありません