戦略設計の初心者にとって,ヘッジ戦略は実践するのに非常に良いものです.この記事は,初心者が設計経験を学ぶことを希望する,シンプルでも堅牢な仮想通貨スポットヘッジ戦略を実装しています.
まず,設計する戦略が仮想通貨スポットヘッジ戦略であることを確認する必要があります.最もシンプルなヘッジを設計します.私たちは2つのスポットプラットフォーム間の高い価格を持つプラットフォームでのみ販売し,価格のスプレッドを得るために低い価格を持つプラットフォームで購入します.高い価格を持つプラットフォームが引換通貨シンボルで満たされたとき (価格が高いので,すべての通貨シンボルが販売されます),または低い価格を持つプラットフォームが通貨シンボルで満たされたとき (価格が低いので,通貨シンボルはすべての資産によって購入されます),ヘッジすることはできません.この時点で,あなたは価格がヘッジに逆転するのを待つだけです.
ヘージング中のオーダー価格と金額については,各プラットフォームに精度制限があり,最低オーダー金額にも制限があります.最低制限に加えて,戦略はヘージングのための最大オーダー金額も考慮する必要があります.オーダー金額が大きすぎると,市場にはそれのための十分なオーダーボリュームがありません.また,2つのプラットフォームが異なるコート通貨を持っている場合,為替レートを変換する方法も考慮する必要があります.ヘージング中の処理手数料とオーダーテイカーの滑り込みはすべて取引コストです.価格差がある限り,ヘージングは常に起こらない.したがって,ヘージング価格・スプレッドにはトリガー値もあります.特定の価格・スプレッドを下回る場合は,ヘージングは損失を発生します.
その根拠に基づいて,戦略はいくつかのパラメータを考慮して設計する必要があります.
hedgeDiffPrice
差が値を超えると,ヘッジが起動します.minHedgeAmount
,ヘッジに利用可能な最小オーダー金額 (シンボルの金額)maxHedgeAmount
, Hedge に利用可能な最大オーダー金額 (シンボルの金額)pricePrecisionA
, プラットフォームAの注文価格精度 (小数点)amountPrecisionA
, プラットフォームAの注文金額精度 (小数位)pricePrecisionB
, Bプラットフォームの注文価格精度 (小数点)amountPrecisionB
, プラットフォームBの注文金額精度 (小数位)rateA
,最初の追加された交換対象物の換算為替レート;デフォルトは1,換算しないことを示す.rateB
,第2の追加された交換対象の換算為替レート;デフォルトは1で,換算しないことを示します.ヘッジ戦略は,2つのアカウントの通貨シンボルの金額を変化しないようにする必要があります (すなわち,方向的なポジションを保持せず,中立を維持します),したがって,バランスを常に検出するために戦略にバランス論理が必要です.バランスを確認する際には,2つのプラットフォームから資産データを入手することが避けられません.したがって,使用のために関数を書く必要があります.
function updateAccs(arrEx) {
var ret = []
for (var i = 0 ; i < arrEx.length ; i++) {
var acc = arrEx[i].GetAccount()
if (!acc) {
return null
}
ret.push(acc)
}
return ret
}
オーダーが実行されていない場合,そのオーダーを間に合ってキャンセルし,オーダーを待機状態に保つことはできません.この操作はバランスモジュールとヘッジロジックの両方で処理する必要があります.したがって,すべてのオーダーをキャンセルする関数を設計することも必要です.
function cancelAll() {
_.each(exchanges, function(ex) {
while (true) {
var orders = _C(ex.GetOrders)
if (orders.length == 0) {
break
}
for (var i = 0 ; i < orders.length ; i++) {
ex.CancelOrder(orders[i].Id, orders[i])
Sleep(500)
}
}
})
}
特定の値の値が ある程度の深さのデータに含まれているので このような関数が必要です
function getDepthPrice(depth, side, amount) {
var arr = depth[side]
var sum = 0
var price = null
for (var i = 0 ; i < arr.length ; i++) {
var ele = arr[i]
sum += ele.Amount
if (sum >= amount) {
price = ele.Price
break
}
}
return price
}
特定のヘージングオーダー操作を設計し,書き込む必要があります.
function hedge(buyEx, sellEx, price, amount) {
var buyRoutine = buyEx.Go("Buy", price, amount)
var sellRoutine = sellEx.Go("Sell", price, amount)
Sleep(500)
buyRoutine.wait()
sellRoutine.wait()
}
少し複雑なバランス関数の設計を完成させましょう.
function keepBalance(initAccs, nowAccs, depths) {
var initSumStocks = 0
var nowSumStocks = 0
_.each(initAccs, function(acc) {
initSumStocks += acc.Stocks + acc.FrozenStocks
})
_.each(nowAccs, function(acc) {
nowSumStocks += acc.Stocks + acc.FrozenStocks
})
var diff = nowSumStocks - initSumStocks
// calculate currency spread
if (Math.abs(diff) > minHedgeAmount && initAccs.length == nowAccs.length && nowAccs.length == depths.length) {
var index = -1
var available = []
var side = diff > 0 ? "Bids" : "Asks"
for (var i = 0 ; i < nowAccs.length ; i++) {
var price = getDepthPrice(depths[i], side, Math.abs(diff))
if (side == "Bids" && nowAccs[i].Stocks > Math.abs(diff)) {
available.push(i)
} else if (price && nowAccs[i].Balance / price > Math.abs(diff)) {
available.push(i)
}
}
for (var i = 0 ; i < available.length ; i++) {
if (index == -1) {
index = available[i]
} else {
var priceIndex = getDepthPrice(depths[index], side, Math.abs(diff))
var priceI = getDepthPrice(depths[available[i]], side, Math.abs(diff))
if (side == "Bids" && priceIndex && priceI && priceI > priceIndex) {
index = available[i]
} else if (priceIndex && priceI && priceI < priceIndex) {
index = available[i]
}
}
}
if (index == -1) {
Log("cannot balance")
} else {
// balanced ordering
var price = getDepthPrice(depths[index], side, Math.abs(diff))
if (price) {
var tradeFunc = side == "Bids" ? exchanges[index].Sell : exchanges[index].Buy
tradeFunc(price, Math.abs(diff))
} else {
Log("invalid price", price)
}
}
return false
} else if (!(initAccs.length == nowAccs.length && nowAccs.length == depths.length)) {
Log("error:", "initAccs.length:", initAccs.length, "nowAccs.length:", nowAccs.length, "depths.length:", depths.length)
return true
} else {
return true
}
}
これらの機能は 戦略の要件に従って設計され 戦略の主要な機能を設計し始めることができます
FMZでは,戦略はmain
機能.main
戦略の初期化を行う必要があります.
交換オブジェクト名 取引対象を使用する. 例えば,市場引上げ,注文など. だから,毎回長い名前を使うのは不便でしょう. 私の小さなトリックは,代わりに簡単な短い名前を使用することです. 例えば:
var exA = exchanges[0]
var exB = exchanges[1]
後にコードを書くのが楽になります.
換算率と精度
// settings of precision and exchange rate
if (rateA != 1) {
// set exchange rate A
exA.SetRate(rateA)
Log("Platform A sets exchange rate:", rateA, "#FF0000")
}
if (rateB != 1) {
// set exchange rate B
exB.SetRate(rateB)
Log("Platform B sets exchange rate:", rateB, "#FF0000")
}
exA.SetPrecision(pricePrecisionA, amountPrecisionA)
exB.SetPrecision(pricePrecisionB, amountPrecisionB)
通貨レートのパラメータの一つ,すなわちrateA
そしてrateB
, は 1 に設定されます (デフォルトは 1),つまり,rateA != 1
またはrateB != 1
発動していない,そして為替レートは変換できません.
すべての日付をリセットする
ストラテジーのインターフェースパラメータを設計することができます. ストラテジーのインターフェースは,すべてのログを削除し,データレコードを掃除する必要があります.isReset
戦略の初期化部分にリセットコードを設計します 例えば:
if (isReset) { // when "isReset" is true, reset the data
_G(null)
LogReset(1)
LogProfitReset()
LogVacuum()
Log("Reset all data", "#FF0000")
}
初期アカウントデータを復元し,現在のアカウントデータを更新する
バランスを判断するために,戦略は,初期口座の資産状態を継続的に記録し,現在の状態と比較する必要があります.nowAccs
現金口座のデータを記録するために使用されます.updateAccs
現在のプラットフォームのアカウントデータを取得するために設計しましたinitAccs
初期口座の状態を記録するために使用されます (AとBの両方の通貨シンボルの金額,引換通貨の金額など).initAccs
まず,_G()
復元する機能 ( _G 関数はデータを持続的に記録し,記録されたデータを再び返却することができます.詳細については API ドキュメンテーションを参照してください:リンク).
割り当ておよび使用する 経常口座情報を使用します_G()
記録する機能です
例えば,次のコード:
var nowAccs = _C(updateAccs, exchanges)
var initAccs = _G("initAccs")
if (!initAccs) {
initAccs = nowAccs
_G("initAccs", initAccs)
}
メインループのコードは,戦略論理実行の各ラウンドのプロセスであり,非停止で繰り返される実行は戦略メインループを構築します. メインループのプログラムの各実行フローを見てみましょう.
市場 の 報じ を 入手 し,その 妥当 性 を 判断 する
var ts = new Date().getTime()
var depthARoutine = exA.Go("GetDepth")
var depthBRoutine = exB.Go("GetDepth")
var depthA = depthARoutine.wait()
var depthB = depthBRoutine.wait()
if (!depthA || !depthB || depthA.Asks.length == 0 || depthA.Bids.length == 0 || depthB.Asks.length == 0 || depthB.Bids.length == 0) {
Sleep(500)
continue
}
この関数は,この関数と,この関数の両方をexchange.Go
FMZプラットフォームは,同時オブジェクトを作成するために使用されますdepthARoutine
そしてdepthBRoutine
その呼び出しGetDepth()
この2つの同時オブジェクトが作成されると,GetDepth()
両方の深度データへの要求がプラットフォームに送られます
じゃあ電話してwait()
対象の方法depthARoutine
そして物体depthBRoutine
深度データを得るため
深度データを取得した後,その有効性を判断するために深度データを検証する必要があります.continue
主ループを再実行するために命令が起動します.
使用price spread
またはspread ratio
?
var targetDiffPrice = hedgeDiffPrice
if (diffAsPercentage) {
targetDiffPrice = (depthA.Bids[0].Price + depthB.Asks[0].Price + depthB.Bids[0].Price + depthA.Asks[0].Price) / 4 * hedgeDiffPercentage
}
FMZのパラメータはショーまたは隠れるパラメータを使用するかどうかを決定するパラメータを作ることができます.price spread
,またはspread ratio
.
パラメータdiffAsPercentage
戦略インターフェースのパラメータに追加されています.パラメータに基づいて表示または隠す他の2つのパラメータは,以下のように設定されています.hedgeDiffPrice@!diffAsPercentage
■ いつdiffAsPercentage
偽りだと証明されるhedgeDiffPercentage@diffAsPercentage
■ いつdiffAsPercentage
真実だと証明されるでしょう
設計後,我々はdiffAsPercentage
条件としてスプレッド比率を使用します.diffAsPercentage
このパラメータがチェックされていない場合,価格・スプレッドがヘッジのトリガー条件として使用されます.
裁判官 ヘッジ・トリガー
if (depthA.Bids[0].Price - depthB.Asks[0].Price > targetDiffPrice && Math.min(depthA.Bids[0].Amount, depthB.Asks[0].Amount) >= minHedgeAmount) { // A -> B market condition satisfied
var price = (depthA.Bids[0].Price + depthB.Asks[0].Price) / 2
var amount = Math.min(depthA.Bids[0].Amount, depthB.Asks[0].Amount)
if (nowAccs[0].Stocks > minHedgeAmount && nowAccs[1].Balance / price > minHedgeAmount) {
amount = Math.min(amount, nowAccs[0].Stocks, nowAccs[1].Balance / price, maxHedgeAmount)
Log("triggerA->B:", depthA.Bids[0].Price - depthB.Asks[0].Price, price, amount, nowAccs[1].Balance / price, nowAccs[0].Stocks) // prompt message
hedge(exB, exA, price, amount)
cancelAll()
lastKeepBalanceTS = 0
isTrade = true
}
} else if (depthB.Bids[0].Price - depthA.Asks[0].Price > targetDiffPrice && Math.min(depthB.Bids[0].Amount, depthA.Asks[0].Amount) >= minHedgeAmount) { // B -> A market condition satisfied
var price = (depthB.Bids[0].Price + depthA.Asks[0].Price) / 2
var amount = Math.min(depthB.Bids[0].Amount, depthA.Asks[0].Amount)
if (nowAccs[1].Stocks > minHedgeAmount && nowAccs[0].Balance / price > minHedgeAmount) {
amount = Math.min(amount, nowAccs[1].Stocks, nowAccs[0].Balance / price, maxHedgeAmount)
Log("triggerB->A:", depthB.Bids[0].Price - depthA.Asks[0].Price, price, amount, nowAccs[0].Balance / price, nowAccs[1].Stocks) // prompt message
hedge(exA, exB, price, amount)
cancelAll()
lastKeepBalanceTS = 0
isTrade = true
}
}
ヘッジの誘発条件はいくつかあります. 1.まずは,ヘッジ・スペードを満たす.市場・スペードが設定されたスペードパラメータを満たすときのみ,ヘッジが可能になります.
2.市場のヘッジ金額は,パラメータに設定された最低ヘッジ金額を満たすべきである.異なるプラットフォームの最低オーダー金額が異なるため,そのうち最小額を選ばなければならない.
3.セール・オペレーションのプラットフォームの資産は販売に十分で,購入のオペレーションのプラットフォームの資産は購入に十分です.これらの条件が満たされると,ヘッジ・関数を実行してヘッジ・オーダーを配置します.メイン・関数の前に,変数を宣言します.isTrade
この場合は,ヘッジが起動した場合,変数はtrue
グローバル変数をリセットします.lastKeepBalanceTS
0 に (lastKeepBalanceTS は,最後のバランス操作のタイムスタンプをマークするために使用され,0 に設定すると,バランス操作を直ちに起動します),そしてすべての待機中のオーダーをキャンセルします.
バランス操作
if (ts - lastKeepBalanceTS > keepBalanceCyc * 1000) {
nowAccs = _C(updateAccs, exchanges)
var isBalance = keepBalance(initAccs, nowAccs, [depthA, depthB])
cancelAll()
if (isBalance) {
lastKeepBalanceTS = ts
if (isTrade) {
var nowBalance = _.reduce(nowAccs, function(sumBalance, acc) {return sumBalance + acc.Balance}, 0)
var initBalance = _.reduce(initAccs, function(sumBalance, acc) {return sumBalance + acc.Balance}, 0)
LogProfit(nowBalance - initBalance, nowBalance, initBalance, nowAccs)
isTrade = false
}
}
}
バランス関数は周期的に実行されるが,lastKeepBalanceTS
負債の負債の負債は,負債の負債の負債の負債の負債の負債の負債の負債の負債の負債の負債の負債の負債である.
ステータスバー情報
LogStatus(_D(), "A->B:", depthA.Bids[0].Price - depthB.Asks[0].Price, " B->A:", depthB.Bids[0].Price - depthA.Asks[0].Price, " targetDiffPrice:", targetDiffPrice, "\n",
"currentA,Stocks:", nowAccs[0].Stocks, "FrozenStocks:", nowAccs[0].FrozenStocks, "Balance:", nowAccs[0].Balance, "FrozenBalance", nowAccs[0].FrozenBalance, "\n",
"currentB,Stocks:", nowAccs[1].Stocks, "FrozenStocks:", nowAccs[1].FrozenStocks, "Balance:", nowAccs[1].Balance, "FrozenBalance", nowAccs[1].FrozenBalance, "\n",
"initialA,Stocks:", initAccs[0].Stocks, "FrozenStocks:", initAccs[0].FrozenStocks, "Balance:", initAccs[0].Balance, "FrozenBalance", initAccs[0].FrozenBalance, "\n",
"initialB,Stocks:", initAccs[1].Stocks, "FrozenStocks:", initAccs[1].FrozenStocks, "Balance:", initAccs[1].Balance, "FrozenBalance", initAccs[1].FrozenBalance)
ステータスバーは特に複雑ではないように設計されています.現在の時間,プラットフォームAからプラットフォームBへの価格・スプレッド,またBからAへの価格・スプレッドを表示します.また,現在のヘッジ目標・スプレッド,プラットフォームAの口座資産データ,およびプラットフォームBの口座資産データを表示します.
通貨レートの値の変換のパラメータを設計し,main
戦略の開始時に機能する.SetRate
通貨レートの変換機能が最初に実行される必要があります.
この機能は2つの側面に影響します
例えば,現在の取引対はBTC_USDT
価格単位はUSDT
口座の資産の可用コート通貨もUSDT
CNYに変換したい場合,設定exchange.SetRate(6.8)
すべての関数で取得されたデータを変換するコードexchange
CNYに換算する.
コート通貨に変換するには,輸入現行コート通貨から目標コート通貨への為替レート中に入りますSetRate
function.
完全な戦略:異なるコート通貨のスポットヘッジ戦略 (教学)