You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
184 lines
8.1 KiB
184 lines
8.1 KiB
3 years ago
|
using Binance.Net.Clients;
|
||
|
using Binance.Net.Objects;
|
||
|
using Binance.TradeRobot.Model.Base;
|
||
|
using CryptoExchange.Net.Authentication;
|
||
|
using Newtonsoft.Json;
|
||
|
using SDKAdapter.Model;
|
||
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Text;
|
||
|
using System.Text.RegularExpressions;
|
||
|
using System.Threading;
|
||
|
|
||
|
namespace SDKAdapter.WebSockets.Order.Spot
|
||
|
{
|
||
|
public class BinanceSpotOrderWebSocketClient : SpotOrderWebSocketClient
|
||
|
{
|
||
|
private BinanceSocketClient binanceSocketClient;
|
||
|
private BinanceClient binanceClient;
|
||
|
private CancellationTokenSource cancellationTokenSource;
|
||
|
private string listenKey;
|
||
|
private IList<Binance.Net.Enums.OrderStatus> ignoreOrderStateList;
|
||
|
|
||
|
public BinanceSpotOrderWebSocketClient(Enums.BusinessType businessType,
|
||
|
long accountId,
|
||
|
string apiKey,
|
||
|
string secret,
|
||
|
NLog.ILogger logger,
|
||
|
Action<SpotOrderTradePublishInfo> onOrderUpdated) : base(businessType,
|
||
|
accountId,
|
||
|
apiKey,
|
||
|
secret,
|
||
|
logger,
|
||
|
onOrderUpdated)
|
||
|
{
|
||
|
var spotClientOption = new BinanceApiClientOptions()
|
||
|
{
|
||
|
BaseAddress = "https://api.binance.com",
|
||
|
ApiCredentials = new ApiCredentials(apiKey, secret)
|
||
|
};
|
||
|
//var usdFuturesClientOption = new BinanceApiClientOptions()
|
||
|
//{
|
||
|
// BaseAddress = "https://fapi.binance.com",
|
||
|
// ApiCredentials = new ApiCredentials(apiKey, secret)
|
||
|
//};
|
||
|
binanceClient = new BinanceClient(new BinanceClientOptions()
|
||
|
{
|
||
|
//UsdFuturesApiOptions = usdFuturesClientOption,
|
||
|
SpotApiOptions = spotClientOption
|
||
|
});
|
||
|
binanceSocketClient = new BinanceSocketClient();
|
||
|
listenKey = string.Empty;
|
||
|
ignoreOrderStateList = new List<Binance.Net.Enums.OrderStatus>()
|
||
|
{
|
||
|
Binance.Net.Enums.OrderStatus.New,
|
||
|
Binance.Net.Enums.OrderStatus.PendingCancel,
|
||
|
Binance.Net.Enums.OrderStatus.Insurance,
|
||
|
Binance.Net.Enums.OrderStatus.Adl
|
||
|
};
|
||
|
}
|
||
|
|
||
|
public override void Start(string symbol = "")
|
||
|
{
|
||
|
if (IsConnected)
|
||
|
return;
|
||
|
IsConnected = true;
|
||
|
cancellationTokenSource = new CancellationTokenSource();
|
||
|
var getListenKeyResponse = binanceClient.SpotApi.Account.StartIsolatedMarginUserStreamAsync(symbol).Result;
|
||
|
if (!getListenKeyResponse.Success)
|
||
|
throw new Exception(getListenKeyResponse.Error?.Message ?? "");
|
||
|
listenKey = getListenKeyResponse.Data;
|
||
|
_ = binanceSocketClient.SpotStreams.SubscribeToUserDataUpdatesAsync(listenKey,
|
||
|
(e) =>
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
var originData = new StringBuilder();
|
||
|
var isStop = false;
|
||
|
if (ignoreOrderStateList.Contains(e.Data.Status))
|
||
|
{
|
||
|
originData.Append($"不支持的订单状态{e.Data.Status}");
|
||
|
isStop = true;
|
||
|
}
|
||
|
|
||
|
else if (string.IsNullOrEmpty(e.Data.ClientOrderId))
|
||
|
{
|
||
|
originData.Append($"缺少ClientOrderId");
|
||
|
isStop = true;
|
||
|
}
|
||
|
|
||
|
long? robotId = null;
|
||
|
Enums.TradePolicy? tradePolicy = null;
|
||
|
|
||
|
if (!isStop)
|
||
|
{
|
||
|
var match = Regex.Match(e.Data.ClientOrderId, @"^([a-z0-9]{5})_(\d{15,})_(\d{1,2})$");
|
||
|
if (!match.Success)
|
||
|
{
|
||
|
originData.Append($"非机器人交易的ClientOrderId");
|
||
|
isStop = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
robotId = long.Parse(match.Groups[2].Value);
|
||
|
tradePolicy = (Enums.TradePolicy)int.Parse(match.Groups[3].Value);
|
||
|
}
|
||
|
}
|
||
|
originData.Append($" {JsonConvert.SerializeObject(e.Data)}");
|
||
|
logger.Info(originData);
|
||
|
if (isStop)
|
||
|
return;
|
||
|
|
||
|
var orderState = Enums.SpotOrderState.Unknow;
|
||
|
switch (e.Data.Status)
|
||
|
{
|
||
|
case Binance.Net.Enums.OrderStatus.PartiallyFilled:
|
||
|
case Binance.Net.Enums.OrderStatus.Filled:
|
||
|
case Binance.Net.Enums.OrderStatus.Canceled:
|
||
|
orderState = (Enums.SpotOrderState)(int)e.Data.Status;
|
||
|
break;
|
||
|
case Binance.Net.Enums.OrderStatus.Rejected:
|
||
|
orderState = Enums.SpotOrderState.Rejected;
|
||
|
break;
|
||
|
case Binance.Net.Enums.OrderStatus.Expired:
|
||
|
orderState = Enums.SpotOrderState.Expired;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
OnOrderUpdated?.Invoke(new SpotOrderTradePublishInfo()
|
||
|
{
|
||
|
OrderId = e.Data.Id,
|
||
|
RobotId = robotId.Value,
|
||
|
TradePolicy = tradePolicy.Value,
|
||
|
Symbol = e.Data.Symbol,
|
||
|
AccountId = this.AccountId,
|
||
|
OrderType = (Enums.OrderType)(int)e.Data.Type,
|
||
|
SpotOrderState = orderState,
|
||
|
TradeDirection = (Enums.TradeDirection)(int)e.Data.Side,
|
||
|
ClientOrderId = e.Data.ClientOrderId,
|
||
|
CummulativeTradeAmount = e.Data.QuoteQuantityFilled,
|
||
|
CummulativeTradeQuantity = e.Data.QuantityFilled,
|
||
|
Exchange = Enums.Exchange.Binance,
|
||
|
Fee = e.Data.Fee,
|
||
|
FeeUnit = e.Data.FeeAsset,
|
||
|
LastTradeAmount = e.Data.LastQuoteQuantity,
|
||
|
LastTradePrice = e.Data.LastPriceFilled,
|
||
|
LastTradeQuantity = e.Data.LastQuantityFilled,
|
||
|
LastTradeTime = e.Data.UpdateTime,
|
||
|
CreateTime = e.Data.CreateTime
|
||
|
});
|
||
|
}
|
||
|
catch (Exception ex)
|
||
|
{
|
||
|
logger.Error(ex);
|
||
|
}
|
||
|
},
|
||
|
(e) =>
|
||
|
{
|
||
|
|
||
|
},
|
||
|
(e) =>
|
||
|
{
|
||
|
|
||
|
},
|
||
|
(e) =>
|
||
|
{
|
||
|
|
||
|
},
|
||
|
cancellationTokenSource.Token);
|
||
|
}
|
||
|
|
||
|
public override void Stop(string symbol = "")
|
||
|
{
|
||
|
if (!IsConnected)
|
||
|
return;
|
||
|
IsConnected = false;
|
||
|
cancellationTokenSource.Cancel();
|
||
|
binanceSocketClient.SpotStreams.Dispose();
|
||
|
cancellationTokenSource = null;
|
||
|
_ = binanceClient.SpotApi.Account.CloseIsolatedMarginUserStreamAsync(symbol, listenKey).Result;
|
||
|
listenKey = string.Empty;
|
||
|
}
|
||
|
}
|
||
|
}
|