लंबे समय से, डिजिटल मुद्रा विनिमय स्थिति एपीआई इंटरफ़ेस की डेटा विलंब समस्या ने मुझे हमेशा परेशान किया है। मुझे अभी तक कोई उपयुक्त समाधान नहीं मिला है, इसलिए मैं इस समस्या को पुनः प्रस्तुत करूंगा। आमतौर पर अनुबंध एक्सचेंज द्वारा प्रदान किया गया बाजार आदेश वास्तव में प्रतिपक्ष मूल्य होता है, इसलिए कभी-कभी इस तथाकथित “बाजार आदेश” का उपयोग करना अविश्वसनीय होता है। इसलिए, जब हम डिजिटल मुद्रा वायदा व्यापार रणनीतियों को लिखते हैं, तो हम में से अधिकांश सीमा आदेशों का उपयोग करते हैं। प्रत्येक ऑर्डर देने के बाद, हमें यह देखने के लिए स्थिति की जांच करनी होगी कि क्या ऑर्डर निष्पादित हुआ है और क्या संबंधित स्थिति कायम है। समस्या इस स्थिति की जानकारी में निहित है। यदि ऑर्डर निष्पादित किया जाता है, तो एक्सचेंज स्थिति सूचना इंटरफ़ेस (यानी, एक्सचेंज इंटरफ़ेस वास्तव में एक्सेस किया जाता है जब हम एक्सचेंज.गेटपोजिशन को कॉल करते हैं) द्वारा लौटाए गए डेटा में नई खुली स्थिति की जानकारी शामिल होनी चाहिए। हालाँकि, यदि एक्सचेंज द्वारा लौटाया गया डेटा पुराना डेटा है, अर्थात, अभी दिए गए ऑर्डर के निष्पादित होने से पहले की स्थिति की जानकारी है, तो समस्या होगी। ट्रेडिंग लॉजिक यह सोच सकता है कि ऑर्डर निष्पादित नहीं हुआ है और ऑर्डर जारी रख सकता है। हालांकि, एक्सचेंज के ऑर्डर प्लेसमेंट इंटरफ़ेस में कोई देरी नहीं होती है। इसके बजाय, लेनदेन बहुत तेज़ी से पूरे होते हैं, और ऑर्डर दिए जाने के तुरंत बाद निष्पादित हो जाते हैं। इसका एक गंभीर परिणाम यह होगा कि जब कोई प्रारंभिक ऑपरेशन शुरू होगा तो रणनीति बार-बार ऑर्डर देगी।
इस समस्या के कारण, मैंने एक ऐसी रणनीति देखी है जिसने एक पागल पूर्ण लंबी स्थिति खोली। सौभाग्य से, उस समय बाजार में तेजी थी और फ्लोटिंग लाभ एक बार 10BTC से अधिक हो गया था। सौभाग्य से, बाजार तेजी से बढ़ रहा है, यदि इसमें गिरावट आती तो परिणाम पूर्वानुमानित होता।
समाधान 1 रणनीति का ऑर्डर लॉजिक केवल एक ऑर्डर देने के लिए डिज़ाइन किया जा सकता है, और ऑर्डर मूल्य उस समय प्रतिद्वंद्वी की कीमत के साथ-साथ एक बड़ा स्लिपेज होता है, ताकि एक निश्चित गहराई के प्रतिद्वंद्वी ऑर्डर ले सकें। ऐसा करने का लाभ यह है कि आप केवल एक ही ऑर्डर देते हैं और यह स्थिति संबंधी जानकारी पर आधारित नहीं होता है। इससे डुप्लिकेट ऑर्डर की समस्या से बचा जा सकता है, लेकिन कभी-कभी कीमत में बहुत अधिक परिवर्तन होने पर ऑर्डर देने से एक्सचेंज की मूल्य सीमा प्रणाली सक्रिय हो सकती है, और यह संभव है कि बड़ी स्लिपेज के बावजूद भी ऑर्डर निष्पादित नहीं होगा, जिससे अवसर चूक जाएगा। .
समाधान 2 एक्सचेंज के मार्केट ऑर्डर फ़ंक्शन का उपयोग करें और FMZ पर -1 को मूल्य के रूप में पास करें, जो एक मार्केट ऑर्डर है। वर्तमान में, OKEX फ्यूचर्स इंटरफ़ेस को वास्तविक मार्केट ऑर्डर का समर्थन करने के लिए अपग्रेड किया गया है।
समाधान 3 हम अभी भी पुराने ट्रेडिंग लॉजिक का उपयोग करते हैं और लिमिट ऑर्डर का उपयोग करके ऑर्डर प्लेस करते हैं, लेकिन हम पोजीशन डेटा विलंब के कारण उत्पन्न समस्या को हल करने के लिए ट्रेडिंग लॉजिक में कुछ डिटेक्शन जोड़ते हैं। जाँच करें कि क्या ऑर्डर देने के बाद ऑर्डर बिना रद्द किए लंबित ऑर्डर सूची से गायब हो जाता है (पेंडिंग ऑर्डर सूची से गायब होने की दो संभावनाएँ हैं: 1 रद्दीकरण और 2 पूर्ति)। यदि ऐसी स्थिति का पता चलता है और ऑर्डर की मात्रा और ऑर्डर वॉल्यूम पिछली बार जैसा ही है। इस समय, आपको इस बात पर ध्यान देना चाहिए कि क्या स्थिति डेटा में देरी हो रही है। प्रोग्राम को प्रतीक्षा तर्क में प्रवेश करने दें और स्थिति की जानकारी फिर से प्राप्त करें। आप संख्या को अनुकूलित और बढ़ाना भी जारी रख सकते हैं ट्रिगरिंग प्रतीक्षा की। यदि यह एक निश्चित संख्या से अधिक है, तो इसका मतलब है कि स्थिति इंटरफ़ेस डेटा में देरी हो रही है। समस्या गंभीर है, इसलिए इस लेनदेन का तर्क समाप्त हो गया है।
// 参数
/*
var MinAmount = 1
var SlidePrice = 5
var Interval = 500
*/
function GetPosition(e, contractType, direction) {
e.SetContractType(contractType)
var positions = _C(e.GetPosition);
for (var i = 0; i < positions.length; i++) {
if (positions[i].ContractType == contractType && positions[i].Type == direction) {
return positions[i]
}
}
return null
}
function Open(e, contractType, direction, opAmount) {
var initPosition = GetPosition(e, contractType, direction);
var isFirst = true;
var initAmount = initPosition ? initPosition.Amount : 0;
var nowPosition = initPosition;
var directBreak = false
var preNeedOpen = 0
var timeoutCount = 0
while (true) {
var ticker = _C(e.GetTicker)
var needOpen = opAmount;
if (isFirst) {
isFirst = false;
} else {
nowPosition = GetPosition(e, contractType, direction);
if (nowPosition) {
needOpen = opAmount - (nowPosition.Amount - initAmount);
}
// 检测directBreak 并且持仓未变的情况
if (preNeedOpen == needOpen && directBreak) {
Log("疑似仓位数据延迟,等待30秒", "#FF0000")
Sleep(30000)
nowPosition = GetPosition(e, contractType, direction);
if (nowPosition) {
needOpen = opAmount - (nowPosition.Amount - initAmount);
}
/*
timeoutCount++
if (timeoutCount > 10) {
Log("连续10次疑似仓位延迟,下单失败!", "#FF0000")
break
}
*/
} else {
timeoutCount = 0
}
}
if (needOpen < MinAmount) {
break;
}
var amount = needOpen;
preNeedOpen = needOpen
e.SetDirection(direction == PD_LONG ? "buy" : "sell");
var orderId;
if (direction == PD_LONG) {
orderId = e.Buy(ticker.Sell + SlidePrice, amount, "开多仓", contractType, ticker);
} else {
orderId = e.Sell(ticker.Buy - SlidePrice, amount, "开空仓", contractType, ticker);
}
directBreak = false
var n = 0
while (true) {
Sleep(Interval);
var orders = _C(e.GetOrders);
if (orders.length == 0) {
if (n == 0) {
directBreak = true
}
break;
}
for (var j = 0; j < orders.length; j++) {
e.CancelOrder(orders[j].Id);
if (j < (orders.length - 1)) {
Sleep(Interval);
}
}
n++
}
}
var ret = {
price: 0,
amount: 0,
position: nowPosition
};
if (!nowPosition) {
return ret;
}
if (!initPosition) {
ret.price = nowPosition.Price;
ret.amount = nowPosition.Amount;
} else {
ret.amount = nowPosition.Amount - initPosition.Amount;
ret.price = _N(((nowPosition.Price * nowPosition.Amount) - (initPosition.Price * initPosition.Amount)) / ret.amount);
}
return ret;
}
function Cover(e, contractType, opAmount, direction) {
var initPosition = null;
var position = null;
var isFirst = true;
while (true) {
while (true) {
Sleep(Interval);
var orders = _C(e.GetOrders);
if (orders.length == 0) {
break;
}
for (var j = 0; j < orders.length; j++) {
e.CancelOrder(orders[j].Id);
if (j < (orders.length - 1)) {
Sleep(Interval);
}
}
}
position = GetPosition(e, contractType, direction)
if (!position) {
break
}
if (isFirst == true) {
initPosition = position;
opAmount = Math.min(opAmount, initPosition.Amount)
isFirst = false;
}
var amount = opAmount - (initPosition.Amount - position.Amount)
if (amount <= 0) {
break
}
var ticker = _C(exchange.GetTicker)
if (position.Type == PD_LONG) {
e.SetDirection("closebuy");
e.Sell(ticker.Buy - SlidePrice, amount, "平多仓", contractType, ticker);
} else if (position.Type == PD_SHORT) {
e.SetDirection("closesell");
e.Buy(ticker.Sell + SlidePrice, amount, "平空仓", contractType, ticker);
}
Sleep(Interval)
}
return position
}
$.OpenLong = function(e, contractType, amount) {
if (typeof(e) == "string") {
amount = contractType
contractType = e
e = exchange
}
return Open(e, contractType, PD_LONG, amount);
}
$.OpenShort = function(e, contractType, amount) {
if (typeof(e) == "string") {
amount = contractType
contractType = e
e = exchange
}
return Open(e, contractType, PD_SHORT, amount);
};
$.CoverLong = function(e, contractType, amount) {
if (typeof(e) == "string") {
amount = contractType
contractType = e
e = exchange
}
return Cover(e, contractType, amount, PD_LONG);
};
$.CoverShort = function(e, contractType, amount) {
if (typeof(e) == "string") {
amount = contractType
contractType = e
e = exchange
}
return Cover(e, contractType, amount, PD_SHORT);
};
function main() {
Log(exchange.GetPosition())
var info = $.OpenLong(exchange, "quarter", 100)
Log(info, "#FF0000")
Log(exchange.GetPosition())
info = $.CoverLong(exchange, "quarter", 30)
Log(exchange.GetPosition())
Log(info, "#FF0000")
info = $.CoverLong(exchange, "quarter", 80)
Log(exchange.GetPosition())
Log(info, "#FF0000")
}
टेम्पलेट पता: https://www.fmz.com/strategy/203258
टेम्पलेट इंटरफ़ेस को कॉल करने का तरीका ऊपर दिए गए मुख्य फ़ंक्शन के समान ही है$.OpenLong
,$.CoverLong
。
यह टेम्पलेट एक बीटा संस्करण है। सुझाव और टिप्पणियाँ स्वागत योग्य हैं। हम स्थिति डेटा विलंब की समस्या को हल करने के लिए इसे अनुकूलित करना जारी रखेंगे।