Skip to content

Commit d2d2ecf

Browse files
BitMEX: added Position websocket (#533)
* Add LastPrice, TimeStamp fields * Add public method GetPositionWebSocketAsync()
1 parent 0ababfc commit d2d2ecf

File tree

2 files changed

+75
-11
lines changed

2 files changed

+75
-11
lines changed

src/ExchangeSharp/API/Exchanges/BitMEX/ExchangeBitMEXAPI.cs

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,50 @@ protected override async Task<IWebSocket> OnGetDeltaOrderBookWebSocketAsync(Acti
355355
});
356356
}
357357

358+
public async Task<IWebSocket> GetPositionWebSocketAsync(Action<ExchangePosition> callback)
359+
{
360+
/*
361+
"{\"info\":\"Welcome to the BitMEX Realtime API.\",\"version\":\"2020-04-08T01:10:16.000Z\",\"timestamp\":\"2020-04-11T11:43:31.856Z\",\"docs\":\"https://testnet.bitmex.com/app/wsAPI\",\"limit\":{\"remaining\":39}}"
362+
"{\"success\":true,\"request\":{\"op\":\"authKeyExpires\",\"args\":[\"Foo123\",1586605471,\"Bar456\"]}}"
363+
"{\"success\":true,\"subscribe\":\"position\",\"request\":{\"op\":\"subscribe\",\"args\":\"position\"}}"
364+
"{\"table\":\"position\",\"action\":\"partial\",\"keys\":[\"account\",\"symbol\"],\"types\":{\"account\":\"long\",\"symbol\":\"symbol\",\"currency\":\"symbol\",\"underlying\":\"symbol\",\"quoteCurrency\":\"symbol\",\"commission\":\"float\",\"initMarginReq\":\"float\",\"maintMarginReq\":\"float\",\"riskLimit\":\"long\",\"leverage\":\"float\",\"crossMargin\":\"boolean\",\"deleveragePercentile\":\"float\",\"rebalancedPnl\":\"long\",\"prevRealisedPnl\":\"long\",\"prevUnrealisedPnl\":\"long\",\"prevClosePrice\":\"float\",\"openingTimestamp\":\"timestamp\",\"openingQty\":\"long\",\"openingCost\":\"long\",\"openingComm\":\"long\",\"openOrderBuyQty\":\"long\",\"openOrderBuyCost\":\"long\",\"openOrderBuyPremium\":\"long\",\"openOrderSellQty\":\"long\",\"openOrderSellCost\":\"long\",\"openOrderSellPremium\":\"long\",\"execBuyQty\":\"long\",\"execBuyCost\":\"long\",\"execSellQty\":\"long\",\"execSellCost\":\"long\",\"execQty\":\"long\",\"execCost\":\"long\",\"execComm\":\"long\",\"currentTimestamp\":\"timestamp\",\"currentQty\":\"long\",\"currentCost\":\"long\",\"currentComm\":\"long\",\"realisedCost\":\"long\",\"unrealisedCost\":\"long\",\"grossOpenCost\":\"long\",\"grossOpenPremium\":\"long\",\"grossExecCost\":\"long\",\"isOpen\":\"boolean\",\"markPrice\":\"float\",\"markValue\":\"long\",\"riskValue\":\"long\",\"homeNotional\":\"float\",\"foreignNotional\":\"float\",\"posState\":\"symbol\",\"posCost\":\"long\",\"posCost2\":\"long\",\"posCross\":\"long\",\"posInit\":\"long\",\"posComm\":\"long\",\"posLoss\":\"long\",\"posMargin\":\"long\",\"posMaint\":\"long\",\"posAllowance\":\"long\",\"taxableMargin\":\"long\",\"initMargin\":\"long\",\"maintMargin\":\"long\",\"sessionMargin\":\"long\",\"targetExcessMargin\":\"long\",\"varMargin\":\"long\",\"realisedGrossPnl\":\"long\",\"realisedTax\":\"long\",\"realisedPnl\":\"long\",\"unrealisedGrossPnl\":\"long\",\"longBankrupt\":\"long\",\"shortBankrupt\":\"long\",\"taxBase\":\"long\",\"indicativeTaxRate\":\"float\",\"indicativeTax\":\"long\",\"unrealisedTax\":\"long\",\"unrealisedPnl\":\"long\",\"unrealisedPnlPcnt\":\"float\",\"unrealisedRoePcnt\":\"float\",\"simpleQty\":\"float\",\"simpleCost\":\"float\",\"simpleValue\":\"float\",\"simplePnl\":\"float\",\"simplePnlPcnt\":\"float\",\"avgCostPrice\":\"float\",\"avgEntryPrice\":\"float\",\"breakEvenPrice\":\"float\",\"marginCallPrice\":\"float\",\"liquidationPrice\":\"float\",\"bankruptPrice\":\"float\",\"timestamp\":\"timestamp\",\"lastPrice\":\"float\",\"lastValue\":\"long\"},\"foreignKeys\":{\"symbol\":\"instrument\"},\"attributes\":{\"account\":\"sorted\",\"symbol\":\"grouped\",\"underlying\":\"grouped\"},\"filter\":{\"account\":12345678},\"data\":[{\"account\":12345678,\"symbol\":\"XBTUSD\",\"currency\":\"XBt\",\"underlying\":\"XBT\",\"quoteCurrency\":\"USD\",\"commission\":0.00075,\"initMarginReq\":0.01,\"maintMarginReq\":0.005,\"riskLimit\":20000000000,\"leverage\":100,\"crossMargin\":true,\"deleveragePercentile\":null,\"rebalancedPnl\":1234,\"prevRealisedPnl\":678,\"prevUnrealisedPnl\":0,\"prevClosePrice\":6905.23,\"openingTimestamp\":\"2020-04-11T11:00:00.000Z\",\"openingQty\":0,\"openingCost\":9876,\"openingComm\":6543,\"openOrderBuyQty\":0,\"openOrderBuyCost\":0,\"openOrderBuyPremium\":0,\"openOrderSellQty\":0,\"openOrderSellCost\":0,\"openOrderSellPremium\":0,\"execBuyQty\":0,\"execBuyCost\":0,\"execSellQty\":0,\"execSellCost\":0,\"execQty\":0,\"execCost\":0,\"execComm\":0,\"currentTimestamp\":\"2020-04-11T11:00:00.330Z\",\"currentQty\":0,\"currentCost\":8765,\"currentComm\":564542,\"realisedCost\":9876,\"unrealisedCost\":0,\"grossOpenCost\":0,\"grossOpenPremium\":0,\"grossExecCost\":0,\"isOpen\":false,\"markPrice\":null,\"markValue\":0,\"riskValue\":0,\"homeNotional\":0,\"foreignNotional\":0,\"posState\":\"\",\"posCost\":0,\"posCost2\":0,\"posCross\":0,\"posInit\":0,\"posComm\":0,\"posLoss\":0,\"posMargin\":0,\"posMaint\":0,\"posAllowance\":0,\"taxableMargin\":0,\"initMargin\":0,\"maintMargin\":0,\"sessionMargin\":0,\"targetExcessMargin\":0,\"varMargin\":0,\"realisedGrossPnl\":-7654,\"realisedTax\":0,\"realisedPnl\":-7654,\"unrealisedGrossPnl\":0,\"longBankrupt\":0,\"shortBankrupt\":0,\"taxBase\":0,\"indicativeTaxRate\":null,\"indicativeTax\":0,\"unrealisedTax\":0,\"unrealisedPnl\":0,\"unrealisedPnlPcnt\":0,\"unrealisedRoePcnt\":0,\"simpleQty\":null,\"simpleCost\":null,\"simpleValue\":null,\"simplePnl\":null,\"simplePnlPcnt\":null,\"avgCostPrice\":null,\"avgEntryPrice\":null,\"breakEvenPrice\":null,\"marginCallPrice\":null,\"liquidationPrice\":null,\"bankruptPrice\":null,\"timestamp\":\"2020-04-11T11:00:00.330Z\",\"lastPrice\":null,\"lastValue\":0}]}"
365+
*/
366+
367+
return await ConnectWebSocketAsync(string.Empty, (_socket, msg) =>
368+
{
369+
var str = msg.ToStringFromUTF8();
370+
JToken token = JToken.Parse(str);
371+
if (token["error"] != null)
372+
{
373+
Logger.Info(token["error"].ToStringInvariant());
374+
return Task.CompletedTask;
375+
}
376+
else if (token["table"] == null)
377+
{
378+
return Task.CompletedTask;
379+
}
380+
381+
JArray data = token["data"] as JArray;
382+
foreach (var d in data)
383+
{
384+
var position = ParsePosition(d);
385+
callback(position);
386+
}
387+
return Task.CompletedTask;
388+
389+
}, async (_socket) =>
390+
{
391+
long nonce = (await GenerateNonceAsync()).ConvertInvariant<long>();
392+
var authPayload = $"GET/realtime{nonce}";
393+
string signature = CryptoUtility.SHA256Sign(authPayload, CryptoUtility.ToUnsecureBytesUTF8(PrivateApiKey));
394+
395+
var authArgs = new object[]{PublicApiKey.ToUnsecureString(), nonce, signature};
396+
await _socket.SendMessageAsync(new { op = "authKeyExpires", args = authArgs });
397+
await _socket.SendMessageAsync(new { op = "subscribe", args = "position" });
398+
});
399+
}
400+
401+
358402
protected override async Task<IEnumerable<MarketCandle>> OnGetCandlesAsync(string marketSymbol, int periodSeconds, DateTime? startDate = null, DateTime? endDate = null, int? limit = null)
359403
{
360404
/*
@@ -527,20 +571,12 @@ public async Task<IEnumerable<ExchangePosition>> GetCurrentPositionsAsync()
527571
{
528572
var payload = await GetNoncePayloadAsync();
529573
string url = "/position?";
530-
url += "&columns=[\"symbol\", \"currentQty\", \"avgEntryPrice\", \"liquidationPrice\", \"leverage\"]";
574+
url += "&columns=[\"symbol\", \"currentQty\", \"avgEntryPrice\", \"liquidationPrice\", \"leverage\", \"lastPrice\", \"currentTimestamp\"]";
531575
JToken token = await MakeJsonRequestAsync<JToken>(url, BaseUrl, payload);
532576
List<ExchangePosition> positions = new List<ExchangePosition>();
533577
foreach (var item in token)
534578
{
535-
ExchangePosition position = new ExchangePosition
536-
{
537-
MarketSymbol = item["symbol"].ToStringUpperInvariant(),
538-
Amount = item["currentQty"].ConvertInvariant<decimal>(),
539-
AveragePrice = item["avgEntryPrice"].ConvertInvariant<decimal>(),
540-
LiquidationPrice = item["liquidationPrice"].ConvertInvariant<decimal>(),
541-
Leverage = item["leverage"].ConvertInvariant<decimal>(),
542-
};
543-
positions.Add(position);
579+
positions.Add(ParsePosition(item));
544580
}
545581
return positions;
546582
}
@@ -663,6 +699,21 @@ private void AddOrderToPayload(ExchangeOrderRequest order, Dictionary<string, ob
663699
}
664700
}
665701

702+
private ExchangePosition ParsePosition(JToken token)
703+
{
704+
ExchangePosition result = new ExchangePosition
705+
{
706+
MarketSymbol = token["symbol"].ToStringUpperInvariant(),
707+
Amount = token["currentQty"].ConvertInvariant<decimal>(),
708+
AveragePrice = token["avgEntryPrice"].ConvertInvariant<decimal>(),
709+
LiquidationPrice = token["liquidationPrice"].ConvertInvariant<decimal>(),
710+
Leverage = token["leverage"].ConvertInvariant<decimal>(),
711+
LastPrice = token["lastPrice"].ConvertInvariant<decimal>(),
712+
TimeStamp = CryptoUtility.ParseTimestamp(token["currentTimestamp"], TimestampType.Iso8601)
713+
};
714+
return result;
715+
}
716+
666717
private ExchangeOrderResult ParseOrder(JToken token)
667718
{
668719
/*

src/ExchangeSharp/Model/ExchangePosition.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
/*
1+
using System;
2+
3+
/*
24
MIT LICENSE
35
46
Copyright 2017 Digital Ruby, LLC - http://www.digitalruby.com
@@ -41,5 +43,16 @@ public class ExchangePosition
4143
/// Leverage
4244
/// </summary>
4345
public decimal Leverage { get; set; }
46+
47+
/// <summary>
48+
/// Last Price
49+
/// Last Price on Exchange
50+
/// </summary>
51+
public decimal LastPrice { get; set; }
52+
53+
/// <summary>
54+
/// TimeStamp
55+
/// </summary>
56+
public DateTime TimeStamp { get; set; }
4457
}
4558
}

0 commit comments

Comments
 (0)