Depuis longtemps, le problème de retard de données de l'interface API de l'échange de crypto-monnaie m'a toujours troublé. Je n'ai pas trouvé de moyen approprié de le gérer. Je vais reproduire la scène de ce problème.
Habituellement, l'ordre de marché fourni par l'échange de contrats est en fait le prix de la contrepartie, donc parfois le soi-disant
Le problème réside dans cette information de position. Si l'ordre est fermé, les données renvoyées par l'interface d'information de position de change (c'est-à-dire l'interface d'échange que la couche inférieure accède réellement lorsque nous appelonsexchange.GetPosition
Si les données renvoyées par l'échange sont des données anciennes, c'est-à-dire les informations de position de l'ordre qui vient d'être passé avant la fin de la transaction, cela pose problème.
La logique de négociation peut considérer que l'ordre n'a pas été rempli et continuer à passer l'ordre. Cependant, l'interface de placement des ordres de l'échange n'est pas retardée, mais la transaction est rapide et l'ordre est exécuté.
En raison de ce problème, j'ai vu une stratégie pour remplir une position longue folle, heureusement, le marché était en hausse à ce moment-là, et le bénéfice flottant dépassait une fois 10BTC. Heureusement, le marché a grimpé en flèche. Si c'est une chute, la fin peut être imaginée.
Il est possible de concevoir la logique de placement des ordres pour que la stratégie ne place qu'un seul ordre. Le prix de placement des ordres est un grand glissement pour l'écart de prix du prix de l'adversaire à ce moment-là, et une certaine profondeur d'ordres de l'adversaire peut être exécutée. L'avantage de cela est que seul un ordre est placé, et il n'est pas jugé sur la base des informations de position. Cela peut éviter le problème de placement répété des ordres, mais parfois, lorsque le prix change relativement grand, l'ordre déclenchera le mécanisme de limite de prix de l'échange, et cela peut conduire à ce que le grand ordre de glissement ne soit toujours pas terminé, et a manqué l'opportunité de trading.
En utilisant la fonction
Nous utilisons toujours la logique de négociation précédente et plaçons un ordre limite, mais nous ajoutons une certaine détection à la logique de négociation pour essayer de résoudre le problème causé par le retard des données de position. Une fois l'ordre placé, si l'ordre n'est pas annulé, il disparaît directement dans la liste des ordres en attente (la liste des ordres en attente disparaît de deux manières possibles: 1 retrait d'ordre, 2 exécutés), détectez une telle situation et placez à nouveau le montant de l'ordre. Le montant du dernier ordre est le même. À ce moment-là, il est nécessaire de faire attention à savoir si les données de position sont retardées. Laissez le programme entrer dans la logique d'attente pour récupérer les informations de position. Vous pouvez même continuer à optimiser et augmenter le nombre d'attente de déclenchement. Si elle dépasse un certain nombre de fois, les données d'interface de position sont retardées. Le problème est grave, laissez la logique de transaction se terminer.
// Parameter
/*
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);
}
// Detect directBreak and the position has not changed
if (preNeedOpen == needOpen && directBreak) {
Log("Suspected position data is delayed, wait 30 seconds", "#FF0000")
Sleep(30000)
nowPosition = GetPosition(e, contractType, direction);
if (nowPosition) {
needOpen = opAmount - (nowPosition.Amount - initAmount);
}
/*
timeoutCount++
if (timeoutCount > 10) {
Log("Suspected position delay for 10 consecutive times, placing order fails!", "#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 position", contractType, ticker);
} else {
orderId = e.Sell(ticker.Buy - SlidePrice, amount, "Open short position", 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 position", contractType, ticker);
} else if (position.Type == PD_SHORT) {
e.SetDirection("closesell");
e.Buy(ticker.Sell + SlidePrice, amount, "Close short position", 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")
}
Adresse du modèle:https://www.fmz.com/strategy/203258
La façon d'appeler l'interface du modèle est exactement comme$.OpenLong
et$.CoverLong
dans lemain
fonctionne au-dessus.
Le modèle est une version bêta, toutes suggestions sont les bienvenues, je vais continuer à optimiser pour faire face au problème des retards dans les données de position.