
रणनीति डिजाइन में शुरुआती लोगों के लिए, हेजिंग रणनीति एक बहुत अच्छी प्रशिक्षण रणनीति है। यह लेख एक सरल लेकिन वास्तविक दुनिया डिजिटल मुद्रा स्पॉट हेजिंग रणनीति को लागू करता है, उम्मीद है कि शुरुआती लोग कुछ डिजाइन अनुभव सीख सकते हैं।
सबसे पहले, यह स्पष्ट है कि तैयार की जाने वाली रणनीति एक डिजिटल मुद्रा स्पॉट हेजिंग रणनीति है। हम सबसे सरल हेजिंग रणनीति तैयार करते हैं, जो दो स्पॉट एक्सचेंजों के बीच उच्च मूल्य वाले एक्सचेंज पर बेचना और कम कीमत वाले एक्सचेंज पर खरीदना है। लाभ कमाने के लिए। अंतर ले लो। जब उच्च मूल्य वाले सभी एक्सचेंज सिक्कों में होते हैं (क्योंकि उच्च मूल्य वाले सभी सिक्के बेचे जाते हैं), और कम मूल्य वाले सभी एक्सचेंज सिक्कों में होते हैं (कम मूल्य वाले सभी सिक्के खरीदे जाते हैं), तो हेजिंग करना असंभव है। इस समय, आप केवल कीमत के उलट होने और हेजिंग का इंतजार कर सकते हैं।
हेजिंग करते समय, एक्सचेंज में ऑर्डर मूल्य और मात्रा पर सटीकता प्रतिबंध होते हैं, और न्यूनतम ऑर्डर मात्रा सीमा भी होती है। न्यूनतम सीमा के अलावा, रणनीति को एक समय में हेजिंग के लिए अधिकतम ऑर्डर वॉल्यूम पर भी विचार करना चाहिए। यदि ऑर्डर वॉल्यूम बहुत बड़ा है, तो बाजार में पर्याप्त ऑर्डर नहीं होंगे। आपको यह भी विचार करना होगा कि यदि दोनों एक्सचेंजों की मुद्राएं अलग-अलग हों तो विनिमय दर का उपयोग करके रूपांतरण कैसे किया जाए। हेजिंग करते समय, हैंडलिंग फीस और ऑर्डर पर स्लिपेज दोनों ही ट्रांजेक्शन लागत हैं। जब तक कीमत में अंतर है, तब तक हेजिंग करना संभव नहीं है। इसलिए, कीमत अंतर को हेज करने के लिए एक ट्रिगर वैल्यू भी है। जब कीमत अंतर से कम होता है एक निश्चित स्तर पर, हेजिंग के परिणामस्वरूप हानि होगी।
इन विचारों के आधार पर, रणनीति में कई मापदंडों को डिजाइन करने की आवश्यकता है:
hedgeDiffPriceजब मूल्य अंतर इस मूल्य से अधिक हो जाता है, तो हेजिंग ऑपरेशन शुरू हो जाता है।minHedgeAmount, न्यूनतम आदेश मात्रा (सिक्कों में) जिसे हेज किया जा सकता है।maxHedgeAmount, एक हेज के लिए अधिकतम ऑर्डर मात्रा (सिक्कों की संख्या)।pricePrecisionA, एक्सचेंज ए का ऑर्डर मूल्य परिशुद्धता (दशमलव स्थानों की संख्या)।amountPrecisionA, विनिमय ए की आदेश मात्रा परिशुद्धता (दशमलव स्थानों की संख्या)।pricePrecisionB, एक्सचेंज बी का ऑर्डर मूल्य परिशुद्धता (दशमलव स्थानों की संख्या)।amountPrecisionB, विनिमय बी की आदेश मात्रा परिशुद्धता (दशमलव स्थानों की संख्या)।rateA, पहले जोड़े गए विनिमय ऑब्जेक्ट का विनिमय दर रूपांतरण, डिफ़ॉल्ट मान 1 है, कोई रूपांतरण नहीं।rateB, दूसरे जोड़े गए विनिमय ऑब्जेक्ट का विनिमय दर रूपांतरण, डिफ़ॉल्ट मान 1 है और कोई रूपांतरण नहीं किया जाता है।हेजिंग रणनीति में दोनों खातों में सिक्कों की संख्या अपरिवर्तित रखने की आवश्यकता होती है (अर्थात, कोई दिशात्मक स्थिति नहीं रखना और तटस्थता बनाए रखना), इसलिए हमेशा शेष राशि की जांच करने के लिए रणनीति में संतुलन तर्क की आवश्यकता होती है। शेष राशि की जांच करते समय, दो एक्सचेंजों से परिसंपत्ति डेटा प्राप्त करना अपरिहार्य है। इसका उपयोग करने के लिए हमें एक फ़ंक्शन लिखना होगा।
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()
}
अंत में, आइए संतुलन फ़ंक्शन का डिज़ाइन पूरा करें, जो थोड़ा जटिल है।
keepBalance
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
// 计算币差
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("无法平衡")
} else {
// 平衡下单
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("价格无效", price)
}
}
return false
} else if (!(initAccs.length == nowAccs.length && nowAccs.length == depths.length)) {
Log("错误:", "initAccs.length:", initAccs.length, "nowAccs.length:", nowAccs.length, "depths.length:", depths.length)
return true
} else {
return true
}
}
अब चूंकि ये कार्य रणनीति की आवश्यकताओं के अनुसार डिजाइन कर लिए गए हैं, हम रणनीति के मुख्य कार्य को डिजाइन करना शुरू कर सकते हैं।
एफएमजेड पर रणनीति यह है किmainफ़ंक्शन निष्पादित होना शुरू हो जाता है. अस्तित्वmainफ़ंक्शन की शुरुआत में हमें कुछ रणनीति आरंभीकरण करने की आवश्यकता है।
var exA = exchanges[0]
var exB = exchanges[1]
इससे बाद में कोड लिखना बहुत आसान हो जाता है।
// 精度,汇率设置
if (rateA != 1) {
// 设置汇率A
exA.SetRate(rateA)
Log("交易所A设置汇率:", rateA, "#FF0000")
}
if (rateB != 1) {
// 设置汇率B
exB.SetRate(rateB)
Log("交易所B设置汇率:", rateB, "#FF0000")
}
exA.SetPrecision(pricePrecisionA, amountPrecisionA)
exB.SetPrecision(pricePrecisionB, amountPrecisionB)
यदि विनिमय दर पैरामीटरrateA、rateBकुछ 1 पर सेट हैं (डिफ़ॉल्ट 1 है), अर्थातrateA != 1याrateB != 1ट्रिगर नहीं होगा, इसलिए कोई विनिमय दर रूपांतरण सेट नहीं किया जाएगा।

कभी-कभी जब कोई पॉलिसी शुरू की जाती है, तो सभी लॉग्स को हटाना और रिकॉर्ड किए गए डेटा को साफ़ करना आवश्यक होता है। आप एक रणनीति इंटरफ़ेस पैरामीटर डिज़ाइन कर सकते हैंisReset, और फिर रणनीति के आरंभीकरण अनुभाग में रीसेट कोड डिज़ाइन करें, उदाहरण के लिए:
if (isReset) { // 当isReset为真时重置数据
_G(null)
LogReset(1)
LogProfitReset()
LogVacuum()
Log("重置所有数据", "#FF0000")
}
nowAccsइस वेरिएबल का उपयोग हमारे द्वारा अभी डिज़ाइन किए गए फ़ंक्शन का उपयोग करके चालू खाता डेटा रिकॉर्ड करने के लिए किया जाता है।updateAccsवर्तमान एक्सचेंज का खाता डेटा प्राप्त करें।initAccsप्रारंभिक खाता स्थिति (डेटा जैसे कि सिक्कों की संख्या और एक्सचेंज ए और एक्सचेंज बी के मूल्यवर्गित सिक्कों की संख्या) रिकॉर्ड करने के लिए उपयोग किया जाता है। के लिए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
}
यहां आप FMZ प्लेटफॉर्म के समवर्ती कार्यों को देख सकते हैं।exchange.Go, कॉल बनायाGetDepth()इंटरफ़ेस का समवर्ती ऑब्जेक्टdepthARoutine、depthBRoutine. जब ये दो समवर्ती ऑब्जेक्ट बनाए जाते हैं, तो कॉल करेंGetDepth()इंटरफ़ेस भी तुरंत ही स्थापित हो गया, तथा गहराई से डेटा प्राप्त करने के लिए एक्सचेंज को दो अनुरोध भेजे गए।
फिर कॉल करोdepthARoutine、depthBRoutineवस्तुwait()गहराई डेटा प्राप्त करने की विधि.
गहराई संबंधी डेटा प्राप्त करने के बाद, इसकी वैधता निर्धारित करने के लिए गहराई संबंधी डेटा की जांच करना आवश्यक है। डेटा विसंगतियों के लिए ट्रिगर निष्पादनcontinueकथन मुख्य लूप को पुनः निष्पादित करता है।
价差值पैरामीटर या差价比例पैरामीटर? 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 के पैरामीटर एक निश्चित पैरामीटर पर आधारित हो सकते हैंदिखाओयाछिपाना, इसलिए हम यह तय करने के लिए एक पैरामीटर बना सकते हैं कि इसका उपयोग करना है या नहीं价格差,फिर भी差价比例。

रणनीति इंटरफ़ेस पैरामीटर में एक पैरामीटर जोड़ा गया हैdiffAsPercentage. इस पैरामीटर के आधार पर दिखाए या छिपाए जाने वाले दो अन्य पैरामीटर इस प्रकार सेट किए गए हैं:
hedgeDiffPrice@!diffAsPercentage,कबdiffAsPercentageFalse इस पैरामीटर को प्रदर्शित करता है.
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 盘口条件满足
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("触发A->B:", depthA.Bids[0].Price - depthB.Asks[0].Price, price, amount, nowAccs[1].Balance / price, nowAccs[0].Stocks) // 提示信息
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 盘口条件满足
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("触发B->A:", depthB.Bids[0].Price - depthA.Asks[0].Price, price, amount, nowAccs[0].Balance / price, nowAccs[1].Stocks) // 提示信息
hedge(exA, exB, price, amount)
cancelAll()
lastKeepBalanceTS = 0
isTrade = true
}
}
हेजिंग ट्रिगर की कई स्थितियां हैं:
1. सबसे पहले, हेजिंग मूल्य अंतर को संतुष्ट करें। हेजिंग केवल तभी की जा सकती है जब बाजार का मूल्य अंतर निर्धारित मूल्य अंतर मापदंडों को पूरा करता है।
2. बाजार पर हेजिंग वॉल्यूम को मापदंडों में निर्धारित न्यूनतम हेजिंग वॉल्यूम से मेल खाना चाहिए। क्योंकि अलग-अलग एक्सचेंजों में अलग-अलग न्यूनतम ऑर्डर वॉल्यूम हो सकते हैं, इसलिए दोनों में से सबसे छोटा वॉल्यूम लिया जाना चाहिए।
3. एक्सचेंज में बिक्री परिचालन हेतु पर्याप्त परिसंपत्तियां हैं, तथा एक्सचेंज में खरीद परिचालन हेतु पर्याप्त परिसंपत्तियां हैं।
जब ये शर्तें पूरी हो जाती हैं, तो हेजिंग ऑर्डर देने के लिए हेजिंग फ़ंक्शन निष्पादित किया जाता है। मुख्य फ़ंक्शन से पहले हमने एक चर घोषित कर दिया थाisTradeयह चिह्नित करने के लिए उपयोग किया जाता है कि क्या हेजिंग होती है। यदि हेजिंग ट्रिगर होती है, तो यह चर सेट हो जाता हैtrue. और वैश्विक चर को रीसेट करेंlastKeepBalanceTSlastKeepBalanceTS को 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यदि 0 पर रीसेट किया जाता है, तो संतुलन ऑपरेशन तुरंत शुरू हो जाएगा। संतुलन सफल होने के बाद, लाभ की गणना की जाएगी।
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",
"当前A,Stocks:", nowAccs[0].Stocks, "FrozenStocks:", nowAccs[0].FrozenStocks, "Balance:", nowAccs[0].Balance, "FrozenBalance", nowAccs[0].FrozenBalance, "\n",
"当前B,Stocks:", nowAccs[1].Stocks, "FrozenStocks:", nowAccs[1].FrozenStocks, "Balance:", nowAccs[1].Balance, "FrozenBalance", nowAccs[1].FrozenBalance, "\n",
"初始A,Stocks:", initAccs[0].Stocks, "FrozenStocks:", initAccs[0].FrozenStocks, "Balance:", initAccs[0].Balance, "FrozenBalance", initAccs[0].FrozenBalance, "\n",
"初始B,Stocks:", initAccs[1].Stocks, "FrozenStocks:", initAccs[1].FrozenStocks, "Balance:", initAccs[1].Balance, "FrozenBalance", initAccs[1].FrozenBalance)
स्टेटस बार का डिज़ाइन बहुत जटिल नहीं है। यह वर्तमान समय, एक्सचेंज A से एक्सचेंज B तक के मूल्य अंतर और एक्सचेंज B से एक्सचेंज A तक के मूल्य अंतर को प्रदर्शित करता है। वर्तमान हेज लक्ष्य प्रसार प्रदर्शित करता है। A एक्सचेंज खाते और B एक्सचेंज खाते का परिसंपत्ति डेटा प्रदर्शित करता है।
मापदंडों के संदर्भ में, हमने रणनीति की शुरुआत में रूपांतरण दर मूल्य पैरामीटर तैयार कियाmainहमने फ़ंक्शन के प्रारंभिक संचालन के लिए विनिमय दर रूपांतरण भी डिज़ाइन किया। इस बात पे ध्यान दिया जाना चाहिए किSetRateविनिमय दर रूपांतरण फ़ंक्शन को पहले निष्पादित किया जाना चाहिए।
क्योंकि यह फ़ंक्शन दो स्तरों को प्रभावित करता है:
BTC_USDT, मूल्य इकाइयाँ हैंUSDTखाता परिसंपत्तियों में उपलब्ध मुद्रा हैUSDT. अगर मैं इसे CNY में बदलना चाहता हूं, तो इसे कोड में सेट करेंexchange.SetRate(6.8)अभीexchangeइस एक्सचेंज ऑब्जेक्ट के अंतर्गत सभी कार्यों द्वारा प्राप्त डेटा को CNY में परिवर्तित किया जाता है।
मुद्रा रूपांतरण के लिए इसका उपयोग क्यों किया जाता है?SetRateफ़ंक्शन पासिंगवर्तमान मुद्रा से लक्ष्य मुद्रा तक विनिमय दर。सम्पूर्ण रणनीति:विभिन्न मुद्राओं के लिए स्पॉट हेजिंग रणनीतियाँ (ट्यूटोरियल)