Trong một thời gian dài, vấn đề trì hoãn dữ liệu của giao diện API vị trí của sàn giao dịch tiền kỹ thuật số luôn làm phiền tôi. Tôi chưa tìm thấy một cách thích hợp để giải quyết vấn đề này. Hãy để tôi tái tạo cảnh của vấn đề. Thông thường, lệnh giá thị trường được cung cấp bởi sàn giao dịch hợp đồng thực sự là giá đối tác, vì vậy đôi khi không đáng tin cậy để sử dụng cái gọi là lệnh giá thị trường. Tuy nhiên, khi viết các chiến lược giao dịch tương lai tiền kỹ thuật số, chúng tôi sử dụng lệnh giới hạn nhiều nhất. Sau mỗi lệnh được đặt, chúng tôi cần kiểm tra vị trí để xem lệnh đã được đóng và vị trí tương ứng được giữ. Vấn đề nằm ở vị trí thông tin này. Nếu lệnh được đóng, dữ liệu được trả lại bởi giao diện thông tin vị trí của sàn giao dịch (tức là giao diện thực sự được truy cập bởi lớp dưới khi chúng tôi mở giao dịch.GetPosition rất nghiêm trọng) nên bao gồm các thông tin vị trí mới. Tuy nhiên, nếu các dữ liệu được đưa ra bởi giao dịch vị trí cũ, một khi giao dịch hoàn thành, giao dịch sẽ được trả lại.
Vì vấn đề này, tôi đã thấy một chiến lược mở đầy các vị trí dài điên rồ. May mắn thay, thị trường đã tăng vọt vào thời điểm đó, và lợi nhuận nổi vượt quá 10BTC. May mắn thay, thị trường tăng mạnh. Nếu nó giảm mạnh, chúng ta có thể tưởng tượng kết quả.
Giải pháp 1 Chiến lược có thể được thiết kế để chỉ đặt một lệnh, và giá lệnh là giá của đối thủ giao dịch hiện tại cộng với giá trượt lớn, để có một độ sâu nhất định của lệnh đối thủ. Lợi thế của điều này là chỉ có một lệnh sẽ được đặt, và nó sẽ không được đánh giá dựa trên thông tin vị trí. Điều này có thể tránh được vấn đề của các lệnh lặp đi lặp lại, nhưng đôi khi đặt lệnh có thể kích hoạt cơ chế giới hạn giá của sàn giao dịch khi sự thay đổi giá tương đối lớn, và có thể tăng giá trượt và vẫn không thực hiện thỏa thuận, do đó bỏ lỡ cơ hội.
Giải pháp 2 Với chức năng lệnh giá thị trường của sàn giao dịch, giá được chuyển sang - 1 trên FMZ như lệnh giá thị trường.
Giải pháp 3 Chúng tôi vẫn sử dụng logic giao dịch trước đây và đặt lệnh với lệnh giới hạn giá, nhưng chúng tôi thêm một số phát hiện trong logic giao dịch để cố gắng giải quyết vấn đề gây ra bởi sự chậm trễ dữ liệu vị trí. Kiểm tra xem lệnh đã biến mất trực tiếp khỏi danh sách các lệnh đang chờ không hủy (có hai khả năng biến mất khỏi danh sách các lệnh đang chờ: 1. hủy và 2. được lấp đầy). Nếu tình huống như vậy được phát hiện, và số lượng lệnh được đặt lại giống như lệnh cuối cùng, điều quan trọng cần lưu ý là liệu dữ liệu vị trí bị trì hoãn. Hãy để chương trình nhập logic chờ để lấy lại thông tin vị trí, hoặc thậm chí tiếp tục tối ưu hóa và tăng số lần chờ để kích hoạt thời gian chờ, nếu nó vượt quá một số lần chờ nhất định, nó chỉ ra rằng sự chậm trễ dữ liệu giao diện vị trí là nghiêm trọng, khiến logic giao dịch chấm dứt.
// Parameters
/*
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);
}
// Check directBreak and the position remains unchanged
if (preNeedOpen == needOpen && directBreak) {
Log("Suspected position data is delayed, wait for 30 seconds", "#FF0000")
Sleep(30000)
nowPosition = GetPosition(e, contractType, direction);
if (nowPosition) {
needOpen = opAmount - (nowPosition.Amount - initAmount);
}
/*
timeoutCount++
if (timeoutCount > 10) {
Log("Suspected position is delayed for 10 consecutive times, and the order is failed!", "#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, "open long positions", contractType, ticker);
} else {
orderId = e.Sell(ticker.Buy - SlidePrice, amount, "open short positions", 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, "close long positions", contractType, ticker);
} else if (position.Type == PD_SHORT) {
e.SetDirection("closesell");
e.Buy(ticker.Sell + SlidePrice, amount, "close short positions", 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")
}
Địa chỉ mẫu:https://www.fmz.com/strategy/203258
Giao diện mẫu được gọi theo cùng một cách như$.OpenLong
, $.CoverLong
trong chức năng chính ở trên.
Mẫu là một phiên bản beta, và bạn được chào đón để đưa ra đề xuất. Chúng tôi sẽ tiếp tục tối ưu hóa nó để chúng tôi có thể xử lý vấn đề về sự chậm trễ dữ liệu vị trí.