長い間,デジタル通貨取引所のポジション API インターフェイスのデータ遅延問題は常に私を悩ませてきた.この問題を解決する適切な方法を見つけることができなかった.問題のシーンを再現してみよう.通常,契約取引所によって提供される市場価格オーダーは実際には相手側価格であるため,時にはこのいわゆる"市場価格オーダー"を使用することは信頼性がない.しかし,デジタル通貨先物取引戦略を書いているとき,私たちは最も制限オーダーを使用します.各オーダーが発注された後,オーダーが閉鎖され,対応するポジションが保持されているかどうかを確認するためにポジションをチェックする必要があります.問題はこの情報位置にあります.オーダーが閉鎖された場合,取引所のポジション情報インターフェースによって返されるデータ (つまり,取引所を開くときに底層が実際にアクセスしたインターフェースです.ポジションを非常に深刻に満たす) は新しいポジション情報を含めるべきです.しかし,古いポジションが完了すると,取引所が戻る場所がすぐにありません.この戦略は,取引が完了する前に,取引が繰り返され,取引が完了すると,取引が継続する場所がなくなる可能性があります.その代わりに,取引が完了すると,取引が終了する場所がなくなる.
この問題のために,私は長ポジションで溢れた戦略を狂って開いているのを見た. 幸い,その時点で市場は急上昇し,浮動利益は10BTCを超えました. 幸い,市場は急激に上昇しました.急激に減少した場合,結果は想像できます.
解決法 1 戦略は1つのオーダーのみを出すように設計され,オーダー価格は,現在の取引相手の価格プラス大きなスライディング価格で,相手のオーダーの一定の深さを取ることができる.このメリットは1つのオーダーのみが与えられ,ポジション情報に基づいて判断されないことである.これは繰り返されるオーダーの問題を回避できるが,時には価格変動が比較的大きいとき,オーダーを出すことで取引所の価格制限メカニズムが起動し,スライディング価格を増加させ,それでも取引を失敗させることができ,機会を逃す可能性がある.
解決法2 取引所の市場価格オーダー機能により,価格は市場価格オーダーとしてFMZの-1に転送されます.現在,OKEX先物インターフェースは,実際の市場価格オーダーをサポートするためにアップグレードされています.
解決策3 我々は依然として以前の取引論理を使用し,価格制限オーダーで注文を置くが,ポジションデータ遅延によって引き起こされた問題を解決するために取引論理にいくつかの検出を加える. 注文がキャンセルなしに待機中の注文リストから直接消えたかどうかを確認する (待機中の注文リストから消える可能性は2つあります. 1.キャンセルと 2.満たされた).そのような状況が検出され,再び配置された注文の量は最後の注文と同じである場合は,位置データが遅れているかどうかを注意することが重要です. ポジション情報を再取得するために,プログラムが待機論理を入力させ,または待機時間を誘発するために待ち時間を最適化し,増加させ続ける場合,一定の数のポジション時間を超えると,インターフェースデータ遅延が深刻であることを示し,取引論理が終了する可能性があります.
// 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")
}
模様の住所:https://www.fmz.com/strategy/203258
テンプレートインターフェースは,$.OpenLong
, $.CoverLong
上記のメイン機能で
テンプレートはベータバージョンで,提案を歓迎します.位置データ遅延の問題を解決するために,それを最適化し続けます.