Browse Source

对接订单

master
shanji 3 years ago
parent
commit
e02a00137f
  1. 6
      Binance.TradeRobot.Business/Business/BaseBusiness.cs
  2. 2
      Binance.TradeRobot.Business/Business/ExchangeBusiness.cs
  3. 2
      Binance.TradeRobot.Business/Business/RobotBusiness.cs
  4. 44
      Binance.TradeRobot.Business/Business/TradeBusiness/BaseTradeBusiness.cs
  5. 71
      Binance.TradeRobot.Business/Business/TradeBusiness/D21TradeBusiness.cs
  6. 4
      Binance.TradeRobot.Business/Extensions/RobotExtension.cs
  7. 46
      Binance.TradeRobot.Business/GlobalContext.cs
  8. 12
      Binance.TradeRobot.Model/Base/Enums.cs
  9. 9
      Binance.TradeRobot.Model/Binance.TradeRobot.Model.xml
  10. 35
      Binance.TradeRobot.Model/Dto/Response/Robot/RobotResponse.cs
  11. 8
      SDKAdapter/APIClient/BaseAPIClient.cs
  12. 4
      SDKAdapter/APIClient/BinanceAPIClient.cs
  13. 44
      SDKAdapter/Model/SpotOrderTradePublishInfo.cs
  14. 2
      SDKAdapter/SDKAdapter.csproj
  15. 17
      SDKAdapter/WebSockets/Market/BinanceSpotMarketWebSocketClient.cs
  16. 16
      SDKAdapter/WebSockets/Market/SpotMarketWebSocketClient.cs
  17. 112
      SDKAdapter/WebSockets/Order/SpotOrder/BinanceSpotOrderWebSocketClient.cs
  18. 45
      SDKAdapter/WebSockets/Order/SpotOrder/SpotOrderWebSocketClient.cs
  19. 14
      SDKTestConsole/NLog.config
  20. 88
      SDKTestConsole/Program.cs
  21. 14
      SDKTestConsole/SDKTestConsole.csproj

6
Binance.TradeRobot.Business/Business/BaseBusiness.cs

@ -27,12 +27,12 @@ namespace Binance.TradeRobot.Business
expirationTimeSpan = TimeSpan.FromDays(1);
}
protected BaseAPIClient GetBaseAPIClient(Enums.Exchange exchange, long uid, string apiKey, string secret)
protected BaseAPIClient GetBaseAPIClient(Enums.Exchange exchange, long accountId, string apiKey, string secret)
{
var cacheKey = exchange == Enums.Exchange.Binance ? uid.ToString() : apiKey;
var cacheKey = exchange == Enums.Exchange.Binance ? accountId.ToString() : apiKey;
if (!memoryCache.TryGetValue(cacheKey, out BaseAPIClient baseAPIClient))
{
baseAPIClient = BaseAPIClient.Create(exchange, uid, apiKey, secret);
baseAPIClient = BaseAPIClient.Create(exchange, accountId, apiKey, secret);
memoryCache.Set(cacheKey, baseAPIClient, expirationTimeSpan);
}
return baseAPIClient;

2
Binance.TradeRobot.Business/Business/ExchangeBusiness.cs

@ -176,7 +176,7 @@ namespace Binance.TradeRobot.Business
{
}
else if (exchangeAccount.BusinessType == Enums.BusinessType.Spot_Margin)
else if (exchangeAccount.BusinessType == Enums.BusinessType.IsolateMargin)
{
var isolatedMarginAccountAssetList = apiClient.GetIsolatedMarginAccountAssets();
foreach (var apiKey in exchangeAccount.ExchangeAPIKeyList)

2
Binance.TradeRobot.Business/Business/RobotBusiness.cs

@ -56,6 +56,7 @@ namespace Binance.TradeRobot.Business
//监听K线和订单
globalContext.SubscribeKLine(robot);
globalContext.SubscribeOrderPublish(robot);
}
public void StopRobot(long robotId)
@ -68,6 +69,7 @@ namespace Binance.TradeRobot.Business
{
//取消监听K线和订单
globalContext.UnSubscribeKLine(robot);
globalContext.UnSubscribeOrderPublish(robot);
}
}

44
Binance.TradeRobot.Business/Business/TradeBusiness/BaseTradeBusiness.cs

@ -0,0 +1,44 @@
using Binance.TradeRobot.Model.Base;
using Binance.TradeRobot.Model.Db;
using Binance.TradeRobot.Model.Dto;
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections.Generic;
using System.Text;
using Yitter.IdGenerator;
namespace Binance.TradeRobot.Business
{
public class BaseTradeBusiness : BaseBusiness
{
protected DingBusiness dingBusiness { get; private set; }
protected GlobalContext globalContext { get; private set; }
public BaseTradeBusiness(IFreeSql fsql, NLogManager logManager, IIdGenerator idGenerator, IMemoryCache memoryCache, DingBusiness dingBusiness, GlobalContext globalContext) : base(fsql, logManager, idGenerator, memoryCache)
{
this.dingBusiness = dingBusiness;
this.globalContext = globalContext;
}
public void HandleError(Exception ex,
Enums.SingalType singalType,
List<ExecutionLog> logList,
RobotResponse robot,
string step)
{
logList.Add(new ExecutionLog()
{
Id = idGenerator.NewLong(),
SourceSingal = singalType,
RobotId = robot.Id,
CreateTime = DateTime.Now,
Content = ex.Message
});
try { fsql.Insert(logList).ExecuteAffrows(); } catch { }
var errorMsg = $"交易警报,{singalType},{robot.ExecuteKey},{robot.Id},{step}";
logManager.GetLogger(robot.ExecuteKey).Error(ex, errorMsg);
dingBusiness.Send($"{errorMsg} {ex.Message}");
}
}
}

71
Binance.TradeRobot.Business/Business/TradeBusiness/D21TradeBusiness.cs

@ -17,53 +17,36 @@ using Yitter.IdGenerator;
namespace Binance.TradeRobot.Business
{
[BatchRegistration(ServiceLifetime.Singleton, RegistrationType.Interface)]
public class D21TradeBusiness : BaseBusiness, ITradeBusiness
public class D21TradeBusiness : BaseTradeBusiness, ITradeBusiness
{
private DingBusiness dingBusiness;
private GlobalContext globalContext;
private IList<Enums.SpotOrderState> validStateList;
public Enums.TradePolicy TradePolicy => Enums.TradePolicy.D21;
public D21TradeBusiness(IFreeSql fsql,
NLogManager logManager,
IIdGenerator idGenerator,
IMemoryCache memoryCache,
DingBusiness dingBusiness,
GlobalContext globalContext) : base(fsql, logManager, idGenerator, memoryCache)
public D21TradeBusiness(IFreeSql fsql, NLogManager logManager, IIdGenerator idGenerator, IMemoryCache memoryCache, DingBusiness dingBusiness, GlobalContext globalContext) : base(fsql, logManager, idGenerator, memoryCache, dingBusiness, globalContext)
{
this.dingBusiness = dingBusiness;
this.globalContext = globalContext;
validStateList = new List<Enums.SpotOrderState>()
{
Enums.SpotOrderState.Filled,
Enums.SpotOrderState.PartiallyFilled,
Enums.SpotOrderState.Expired
};
}
public void TrendChanged<T, T1>(T singalRequest, T1 robot, SymbolInfoResponse symbolInfo) where T : BaseSingalRequest where T1 : RobotResponse
{
var logList = new List<ExecutionLog>();
logList.Add(new ExecutionLog()
{
Id = idGenerator.NewLong(),
RobotId = robot.Id,
CreateTime = DateTime.Now,
SourceSingal = singalRequest.SingalType,
Content = $"收到信号{singalRequest.SingalType}"
});
try
{
var executionLog = new ExecutionLog()
{
Id = idGenerator.NewLong(),
RobotId = robot.Id,
CreateTime = DateTime.Now,
SourceSingal = singalRequest.SingalType,
Content = $"收到信号{singalRequest.SingalType}"
};
fsql.Insert(executionLog).ExecuteAffrows();
fsql.Insert(logList).ExecuteAffrows();
var d21RuningInfo = RedisHelper.Get<D21RuningInfo>(robot.Id.ToString()) ?? new D21RuningInfo() { RobotId = robot.Id };
d21RuningInfo.RecentSmallTrendSingal = singalRequest.SingalType;
RedisHelper.Set(robot.Id.ToString(), d21RuningInfo);
}
catch (Exception ex)
{
var stringBuilder = new StringBuilder("TrendChanged");
stringBuilder.AppendLine($"SingalRequest {JsonConvert.SerializeObject(singalRequest)}");
stringBuilder.AppendLine($"Robot {JsonConvert.SerializeObject(robot)}");
logManager.GetLogger(robot.ExecuteLogKey).Error(ex, stringBuilder.ToString());
HandleError(ex, singalRequest.SingalType, logList, robot, string.Empty);
}
}
@ -247,6 +230,7 @@ namespace Binance.TradeRobot.Business
fsql.Transaction(() =>
{
fsql.Insert(logList).ExecuteAffrows();
fsql.Insert(buyOrder).ExecuteAffrows();
if (previewTradeAmount != d21Robot.D21Policy.Position) //借币失败 仓位减少
fsql.Update<D21Policy>(d21Robot.D21Policy.Id).Set(d => d.Position, previewTradeAmount).ExecuteAffrows();
@ -256,18 +240,19 @@ namespace Binance.TradeRobot.Business
}
catch (Exception ex)
{
logList.Add(new ExecutionLog()
{
Id = idGenerator.NewLong(),
SourceSingal = Enums.SingalType.,
RobotId = robot.Id,
CreateTime = DateTime.Now,
Content = ex.Message
});
try { fsql.Insert(logList).ExecuteAffrows(); } catch { }
var errorMsg = $"交易警报 {robot.ExecuteLogKey} {robot.Id} {step}";
logManager.GetLogger(robot.ExecuteLogKey).Error(ex, errorMsg);
dingBusiness.Send($"{errorMsg} {ex.Message}");
HandleError(ex, singalRequest.SingalType, logList, robot, step);
//logList.Add(new ExecutionLog()
//{
// Id = idGenerator.NewLong(),
// SourceSingal = Enums.SingalType.多交叉,
// RobotId = robot.Id,
// CreateTime = DateTime.Now,
// Content = ex.Message
//});
//try { fsql.Insert(logList).ExecuteAffrows(); } catch { }
//var errorMsg = $"交易警报,{singalRequest.SingalType},{robot.ExecuteLogKey},{robot.Id},{step}";
//logManager.GetLogger(robot.ExecuteLogKey).Error(ex, errorMsg);
//dingBusiness.Send($"{errorMsg} {ex.Message}");
}
}

4
Binance.TradeRobot.Business/Extensions/RobotExtension.cs

@ -8,8 +8,8 @@ namespace Binance.TradeRobot.Business.Extensions
private static IDictionary<Enums.TradePolicy, Enums.BusinessType> BusinessTypeDic = new Dictionary<Enums.TradePolicy, Enums.BusinessType>()
{
{Enums.TradePolicy.Pyramid, Enums.BusinessType.UPrep },
{ Enums.TradePolicy.D2, Enums.BusinessType.Spot_Margin},
{ Enums.TradePolicy.D21, Enums.BusinessType.Spot_Margin}
{ Enums.TradePolicy.D2, Enums.BusinessType.IsolateMargin},
{ Enums.TradePolicy.D21, Enums.BusinessType.IsolateMargin}
};
/// <summary>

46
Binance.TradeRobot.Business/GlobalContext.cs

@ -2,6 +2,7 @@
using Binance.TradeRobot.Model.Dto;
using Microsoft.Extensions.DependencyInjection;
using SDKAdapter.WebSockets.Market;
using SDKAdapter.WebSockets.Order.SpotOrder;
using System.Collections.Generic;
namespace Binance.TradeRobot.Business
@ -11,11 +12,13 @@ namespace Binance.TradeRobot.Business
{
private NLogManager logManager;
private IDictionary<string, SpotMarketWebSocketClient> spotMarketWebSocketClientDictionary;
private IDictionary<string, SpotOrderWebSocketClient> spotOrderWebSocketClientDictionary;
public GlobalContext(NLogManager logManager)
{
this.logManager = logManager;
spotMarketWebSocketClientDictionary = new Dictionary<string, SpotMarketWebSocketClient>();
spotOrderWebSocketClientDictionary = new Dictionary<string, SpotOrderWebSocketClient>();
}
/// <summary>
@ -26,51 +29,62 @@ namespace Binance.TradeRobot.Business
{
if (!spotMarketWebSocketClientDictionary.TryGetValue(robot.KLineKey, out SpotMarketWebSocketClient spotMarketWebSocketClient))
{
var loggerName = $"SpotKLine-{robot.ExchangeId}-{robot.Symbol}";
spotMarketWebSocketClient = SpotMarketWebSocketClient.Create(robot.ExchangeId, robot.Symbol, logManager.GetLogger(loggerName));
spotMarketWebSocketClient = SpotMarketWebSocketClient.Create(robot.ExchangeId, robot.Symbol, logManager.GetLogger(robot.KLineKey));
spotMarketWebSocketClientDictionary.TryAdd(robot.KLineKey, spotMarketWebSocketClient);
}
if (!spotMarketWebSocketClient.IsConnected)
spotMarketWebSocketClient.Start();
spotMarketWebSocketClient.Start();
}
/// <summary>
/// 订阅订单推送
/// 取消订阅K线
/// </summary>
/// <param name="robot"></param>
public void SubscribeOrderPublish(RobotResponse robot)
public void UnSubscribeKLine(RobotResponse robot)
{
//停止订阅k线
if (spotMarketWebSocketClientDictionary.TryGetValue(robot.KLineKey, out SpotMarketWebSocketClient spotMarketWebSocketClient))
spotMarketWebSocketClient.Stop();
}
/// <summary>
/// 取消订阅K线
/// 订阅订单推送
/// </summary>
/// <param name="robot"></param>
public void UnSubscribeKLine(RobotResponse robot)
public void SubscribeOrderPublish(RobotResponse robot)
{
//停止订阅k线
if (spotMarketWebSocketClientDictionary.TryGetValue(robot.KLineKey, out SpotMarketWebSocketClient spotMarketWebSocketClient))
spotMarketWebSocketClient.Stop();
if (!spotOrderWebSocketClientDictionary.TryGetValue(robot.OrderPublishListenKey, out SpotOrderWebSocketClient spotOrderWebSocketClient))
{
spotOrderWebSocketClient = SpotOrderWebSocketClient.Create(robot.BusinessType,
robot.ExchangeId,
robot.ExchangeAPIKey.AccountId,
robot.ExchangeAPIKey.APIKey,
robot.ExchangeAPIKey.SecretKey,
logManager.GetLogger(robot.OrderPublishListenKey),
(e) => { });
}
spotOrderWebSocketClient.Start();
}
/// <summary>
/// 取消订阅订单推送
/// </summary>
/// <param name="robot"></param>
public void UnSubscribeOrderPublish(RobotResponse robot)
{
if (spotOrderWebSocketClientDictionary.TryGetValue(robot.OrderPublishListenKey, out SpotOrderWebSocketClient spotOrderWebSocketClient))
spotOrderWebSocketClient.Stop();
}
/// <summary>
/// 获取指定交易对现货最新成交价
/// </summary>
/// <param name="symbol"></param>
/// <param name="kLineKey"></param>
/// <returns></returns>
public decimal? GetSpotNewestPrice(string key)
public decimal? GetSpotNewestPrice(string kLineKey)
{
if (spotMarketWebSocketClientDictionary.TryGetValue(key, out SpotMarketWebSocketClient spotMarketWebSocketClient))
if (spotMarketWebSocketClientDictionary.TryGetValue(kLineKey, out SpotMarketWebSocketClient spotMarketWebSocketClient))
return spotMarketWebSocketClient.NewestPrice;
return null;
}

12
Binance.TradeRobot.Model/Base/Enums.cs

@ -42,13 +42,13 @@ namespace Binance.TradeRobot.Model.Base
public enum BusinessType
{
/// <summary>
/// 币币
/// 币币(现货)
/// </summary>
Spot = 0,
/// <summary>
/// 逐仓杠杆
/// </summary>
Spot_Margin = 1,
IsolateMargin = 1,
/// <summary>
/// U本位合约
/// </summary>
@ -135,9 +135,13 @@ namespace Binance.TradeRobot.Model.Base
/// </summary>
Rejected,
/// <summary>
/// 交易引擎取消 没有完全成交
/// 订单过期
/// </summary>
Expired
Expired,
/// <summary>
/// 未知状态
/// </summary>
Unknow
}
/// <summary>

9
Binance.TradeRobot.Model/Binance.TradeRobot.Model.xml

@ -49,7 +49,7 @@
币币
</summary>
</member>
<member name="F:Binance.TradeRobot.Model.Base.Enums.BusinessType.Spot_Margin">
<member name="F:Binance.TradeRobot.Model.Base.Enums.BusinessType.IsolateMargin">
<summary>
逐仓杠杆
</summary>
@ -126,7 +126,12 @@
</member>
<member name="F:Binance.TradeRobot.Model.Base.Enums.SpotOrderState.Expired">
<summary>
交易引擎取消 没有完全成交
订单过期
</summary>
</member>
<member name="F:Binance.TradeRobot.Model.Base.Enums.SpotOrderState.Unknow">
<summary>
未知状态
</summary>
</member>
<member name="T:Binance.TradeRobot.Model.Base.Enums.TradeDirection">

35
Binance.TradeRobot.Model/Dto/Response/Robot/RobotResponse.cs

@ -22,9 +22,40 @@ namespace Binance.TradeRobot.Model.Dto
public Enums.Exchange ExchangeId { get; set; }
public virtual string ExecuteLogKey { get { return $"Execute-{ExchangeId}-{TradePolicy}-{Symbol}"; } }
public virtual string ExecuteKey { get { return $"Execute-{ExchangeId}-{TradePolicy}-{Symbol}"; } }
public virtual string KLineKey { get { return $"KLine-{ExchangeId}-{Symbol}"; } }
public virtual string KLineKey { get { return $"KLine-{ExchangeId}-{BusinessType}-{Symbol}"; } }
/// <summary>
/// 订单推送监听实例Key
/// </summary>
public virtual string OrderPublishListenKey
{
get
{
string key = string.Empty;
if (ExchangeId == Enums.Exchange.Binance)
{
if (BusinessType == Enums.BusinessType.IsolateMargin)
key = $"{BusinessType}-{ExchangeAPIKey.AccountId}-{Symbol}"; //币安逐仓杠杆,同一个账户内的每个交易对需要区分websocket实例
else
key = $"{BusinessType}-{ExchangeAPIKey.AccountId}"; //币安现货,币安合约,同一个账户内不区分websocket实例
}
return $"OrderPublish(Origin)-{ExchangeId}-{key}";
}
}
/// <summary>
/// 订单推送日志Key
/// </summary>
public virtual string OrderPublishLogKey
{
get
{
return $"OrderPublish-{ExchangeId}-{BusinessType}-{Symbol}";
}
}
public SymbolInfoResponse SymbolInfo { get; set; }

8
SDKAdapter/APIClient/BaseAPIClient.cs

@ -7,10 +7,10 @@ namespace SDKAdapter.APIClient
{
public class BaseAPIClient
{
public static BaseAPIClient Create(Enums.Exchange exchange, long uid, string apiKey, string secret)
public static BaseAPIClient Create(Enums.Exchange exchange, long accountId, string apiKey, string secret)
{
if (exchange == Enums.Exchange.Binance)
return new BinanceAPIClient(uid, apiKey, secret);
return new BinanceAPIClient(accountId, apiKey, secret);
return null;
}
@ -18,9 +18,9 @@ namespace SDKAdapter.APIClient
protected string ApiKey { get; private set; }
protected string Secret { get; private set; }
public BaseAPIClient(long uid, string apiKey, string secret)
public BaseAPIClient(long accountId, string apiKey, string secret)
{
this.AccountId = uid;
this.AccountId = accountId;
this.ApiKey = apiKey;
this.Secret = secret;
}

4
SDKAdapter/APIClient/BinanceAPIClient.cs

@ -13,7 +13,7 @@ namespace SDKAdapter.APIClient
{
private BinanceClient binanceClient;
public BinanceAPIClient(long uid, string apiKey, string secret) : base(uid, apiKey, secret)
public BinanceAPIClient(long accountId, string apiKey, string secret) : base(accountId, apiKey, secret)
{
var spotClientOption = new BinanceApiClientOptions()
{
@ -116,7 +116,7 @@ namespace SDKAdapter.APIClient
var binanceOrderSite = (Binance.Net.Enums.OrderSide)(int)tradeDirection;
var binanceOrderType = (Binance.Net.Enums.SpotOrderType)(int)orderType;
Binance.Net.Enums.TimeInForce? timeInForce = null;
if (orderType == Enums.OrderType.STOP_LOSS_LIMIT)
if (orderType == Enums.OrderType.LIMIT || orderType == Enums.OrderType.STOP_LOSS_LIMIT)
timeInForce = Binance.Net.Enums.TimeInForce.GoodTillCanceled;
var r = binanceClient.SpotApi.Trading.PlaceMarginOrderAsync(symbol,

44
SDKAdapter/Model/SpotOrderTradePublishInfo.cs

@ -0,0 +1,44 @@
using Binance.TradeRobot.Model.Base;
using System;
using System.Collections.Generic;
using System.Text;
namespace SDKAdapter.Model
{
public class SpotOrderTradePublishInfo
{
public Enums.Exchange Exchange { get; set; }
public long AccountId { get; set; }
public long OrderId { get; set; }
public string ClientOrderId { get; set; }
public string Symbol { get; set; }
public Enums.TradeDirection TradeDirection { get; set; }
public Enums.OrderType OrderType { get; set; }
public Enums.SpotOrderState SpotOrderState { get; set; }
public decimal LastTradePrice { get; set; }
public decimal LastTradeAmount { get; set; }
public decimal LastTradeQuantity { get; set; }
public decimal Fee { get; set; }
public string FeeUnit { get; set; }
public decimal CummulativeTradeAmount { get; set; }
public decimal CummulativeTradeQuantity { get; set; }
public DateTime CreateTime { get; set; }
public DateTime LastTradeTime { get; set; }
}
}

2
SDKAdapter/SDKAdapter.csproj

@ -6,7 +6,7 @@
</PropertyGroup>
<ItemGroup>
<Folder Include="WebSockets\Order\" />
<Folder Include="WebSockets\Order\UPrepOrder\" />
</ItemGroup>
<ItemGroup>

17
SDKAdapter/WebSockets/Market/BinanceSpotMarketWebSocketClient.cs

@ -6,30 +6,33 @@ namespace SDKAdapter.WebSockets.Market
{
public class BinanceSpotMarketWebSocketClient : SpotMarketWebSocketClient
{
private BinanceSocketClient client;
private BinanceSocketClient binanceSocketClient;
private CancellationTokenSource cancellationTokenSource;
public BinanceSpotMarketWebSocketClient(string symbol, NLog.ILogger logger) : base(symbol, logger)
{
client = new BinanceSocketClient();
binanceSocketClient = new BinanceSocketClient();
}
public override void Start()
{
if (IsConnected)
return;
IsConnected = true;
cancellationTokenSource = new CancellationTokenSource();
client.SpotStreams.SubscribeToKlineUpdatesAsync(Symbol, KlineInterval.OneMinute, (e) =>
binanceSocketClient.SpotStreams.SubscribeToKlineUpdatesAsync(Symbol, KlineInterval.OneMinute, (e) =>
{
base.OnReceived(e.Data.Data.ClosePrice);
}, cancellationTokenSource.Token);
base.Start();
}
public override void Stop()
{
if (!IsConnected)
return;
IsConnected = false;
cancellationTokenSource.Cancel();
client.SpotStreams.Dispose();
base.Stop();
binanceSocketClient.SpotStreams.Dispose();
cancellationTokenSource = null;
}
}

16
SDKAdapter/WebSockets/Market/SpotMarketWebSocketClient.cs

@ -5,13 +5,6 @@ namespace SDKAdapter.WebSockets.Market
{
public class SpotMarketWebSocketClient
{
public static SpotMarketWebSocketClient Create(Enums.Exchange exchange, string symbol, NLog.ILogger logger)
{
if (exchange == Enums.Exchange.Binance)
return new BinanceSpotMarketWebSocketClient(symbol, logger);
return null;
}
/// <summary>
/// 更新间隔(ms)
/// </summary>
@ -34,7 +27,14 @@ namespace SDKAdapter.WebSockets.Market
public NLog.ILogger logger { get; private set; }
public bool IsConnected { get; protected set; }
protected bool IsConnected { get; set; }
public static SpotMarketWebSocketClient Create(Enums.Exchange exchange, string symbol, NLog.ILogger logger)
{
if (exchange == Enums.Exchange.Binance)
return new BinanceSpotMarketWebSocketClient(symbol, logger);
return null;
}
public SpotMarketWebSocketClient(string symbol, NLog.ILogger logger)
{

112
SDKAdapter/WebSockets/Order/SpotOrder/BinanceSpotOrderWebSocketClient.cs

@ -0,0 +1,112 @@
using Binance.Net.Clients;
using Binance.Net.Objects;
using Binance.TradeRobot.Model.Base;
using CryptoExchange.Net.Authentication;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Threading;
namespace SDKAdapter.WebSockets.Order.SpotOrder
{
public class BinanceSpotOrderWebSocketClient : SpotOrderWebSocketClient
{
private BinanceSocketClient binanceSocketClient;
private BinanceClient binanceClient;
private CancellationTokenSource cancellationTokenSource;
private string listenKey;
private IList<Binance.Net.Enums.OrderStatus> unSupportStateList;
public BinanceSpotOrderWebSocketClient(Enums.BusinessType businessType, long accountId, string apiKey, string secret, NLog.ILogger logger)
: base(businessType, accountId, apiKey, secret, logger)
{
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;
unSupportStateList = new List<Binance.Net.Enums.OrderStatus>()
{
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) =>
{
logger.Info(JsonConvert.SerializeObject(e.Data));
if (unSupportStateList.Contains(e.Data.Status))
return;
OnOrderUpdated?.Invoke(new Model.SpotOrderTradePublishInfo()
{
OrderId = e.Data.Id,
Symbol = e.Data.Symbol,
AccountId = this.AccountId,
OrderType = (Enums.OrderType)(int)e.Data.Type,
SpotOrderState = (Enums.SpotOrderState)(int)e.Data.Status,
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
});
},
(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;
}
}
}

45
SDKAdapter/WebSockets/Order/SpotOrder/SpotOrderWebSocketClient.cs

@ -0,0 +1,45 @@
using Binance.TradeRobot.Model.Base;
using SDKAdapter.Model;
using System;
namespace SDKAdapter.WebSockets.Order.SpotOrder
{
public class SpotOrderWebSocketClient
{
protected long AccountId { get; private set; }
protected string ApiKey { get; private set; }
protected string Secret { get; private set; }
protected NLog.ILogger logger { get; private set; }
protected bool IsConnected { get; set; }
protected Enums.BusinessType BusinessType { get; private set; }
public Action<SpotOrderTradePublishInfo> OnOrderUpdated { get; private set; }
public static SpotOrderWebSocketClient Create(Enums.BusinessType businessType, Enums.Exchange exchange, long accountId, string apiKey, string secret, NLog.ILogger logger, Action<SpotOrderTradePublishInfo> onOrderUpdated)
{
if (exchange == Enums.Exchange.Binance)
return new BinanceSpotOrderWebSocketClient(businessType, accountId, apiKey, secret, logger);
return null;
}
public SpotOrderWebSocketClient(Enums.BusinessType businessType, long accountId, string apiKey, string secret, NLog.ILogger logger)
{
this.BusinessType = businessType;
this.AccountId = accountId;
this.ApiKey = apiKey;
this.Secret = secret;
this.logger = logger;
}
public virtual void Start(string symbol = "")
{
}
public virtual void Stop(string symbol = "")
{
}
}
}

14
SDKTestConsole/NLog.config

@ -0,0 +1,14 @@
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<targets>
<target name="errorFile" xsi:type="File" fileName="${basedir}/logs/${logger}/error/${shortdate}.txt"
layout="${longdate} | ${level:uppercase=false} ${newline}${message} ${newline}${onexception:${exception:format=tostring} ${newline}${stacktrace} ${newline}${newline}"
autoFlush="true"/>
<target name="infoFile" xsi:type="File" fileName="${basedir}/logs/${logger}/info/${shortdate}.txt"
layout="${longdate} | ${level:uppercase=false} ${newline}${message} ${newline}"
autoFlush="true"/>
</targets>
<rules>
<logger name="*" level="Error" writeTo="errorFile"/>
<logger name="*" level="Info" writeTo="infoFile" />
</rules>
</nlog>

88
SDKTestConsole/Program.cs

@ -5,6 +5,9 @@ using CryptoExchange.Net.Authentication;
using Newtonsoft.Json;
using SDKAdapter.APIClient;
using System;
using Binance.TradeRobot.Common.Extensions;
using SDKAdapter.WebSockets.Order.SpotOrder;
using NLog;
namespace SDKTestConsole
{
@ -50,8 +53,14 @@ namespace SDKTestConsole
SpotApiOptions = spotClientOption
});
var binanceSocketClient = new BinanceSocketClient();
//市价买币
//var r = client.IsolatedMarginPlaceOrder("ETHUSDT",
// Enums.TradeDirection.Buy,
// Enums.OrderType.MARKET,
// quoteAmount: 20M);
//var r = binanceClient.SpotApi.Trading.PlaceMarginOrderAsync("ETHUSDT",
// Binance.Net.Enums.OrderSide.Buy,
// Binance.Net.Enums.SpotOrderType.Market,
@ -61,9 +70,15 @@ namespace SDKTestConsole
// //stopPrice: 1899M,
// isIsolated: true,
// orderResponseType: Binance.Net.Enums.OrderResponseType.Full).Result;
//var s = JsonConvert.SerializeObject(r);
//
//市价卖币
//var qty = 0.00985570M.CutDecimal(4);
//var r = client.IsolatedMarginPlaceOrder("ETHUSDT",
// Enums.TradeDirection.Sell,
// Enums.OrderType.MARKET,
// quantity: qty);
//var r = binanceClient.SpotApi.Trading.PlaceMarginOrderAsync("ETHUSDT",
// Binance.Net.Enums.OrderSide.Sell,
// Binance.Net.Enums.SpotOrderType.Market,
@ -74,38 +89,51 @@ namespace SDKTestConsole
// //stopPrice: 1899M,
// isIsolated: true,
// orderResponseType: Binance.Net.Enums.OrderResponseType.Full).Result;
//var s = JsonConvert.SerializeObject(r);
//止损卖币
var r = binanceClient.SpotApi.Trading.PlaceMarginOrderAsync("ETHUSDT",
Binance.Net.Enums.OrderSide.Sell,
Binance.Net.Enums.SpotOrderType.StopLossLimit,
quantity: 0.0149M,
price: 1699M,
//quoteQuantity: 20M, //报价币金额
//quantity: 100M,
timeInForce: Binance.Net.Enums.TimeInForce.GoodTillCanceled,
stopPrice: 1699M,
isIsolated: true,
orderResponseType: Binance.Net.Enums.OrderResponseType.Full).Result;
var s = JsonConvert.SerializeObject(r);
Console.WriteLine(s);
var r1 = binanceClient.SpotApi.Trading.PlaceMarginOrderAsync("ETHUSDT",
Binance.Net.Enums.OrderSide.Sell,
Binance.Net.Enums.SpotOrderType.StopLossLimit,
quantity: 0.0149M,
price: 1699M,
//quoteQuantity: 20M, //报价币金额
//quantity: 100M,
timeInForce: Binance.Net.Enums.TimeInForce.GoodTillCanceled,
stopPrice: 1699M,
isIsolated: true,
orderResponseType: Binance.Net.Enums.OrderResponseType.Full).Result;
var s1 = JsonConvert.SerializeObject(r1);
Console.WriteLine(s1);
//var qty = 0.01224350M.CutDecimal(4);
//var r = client.IsolatedMarginPlaceOrder("ETHUSDT",
// Enums.TradeDirection.Sell,
// Enums.OrderType.STOP_LOSS_LIMIT,
// quantity: qty,
// price: 2033M,
// stopPrice: 2035M);
//var r = binanceClient.SpotApi.Trading.PlaceMarginOrderAsync("ETHUSDT",
// Binance.Net.Enums.OrderSide.Sell,
// Binance.Net.Enums.SpotOrderType.StopLossLimit,
// quantity: 0.0149M,
// price: 1699M,
// //quoteQuantity: 20M, //报价币金额
// //quantity: 100M,
// timeInForce: Binance.Net.Enums.TimeInForce.GoodTillCanceled,
// stopPrice: 1699M,
// isIsolated: true,
// orderResponseType: Binance.Net.Enums.OrderResponseType.Full).Result;
//
//var s = JsonConvert.SerializeObject(r);
//Console.WriteLine(s);
//
//var r1 = binanceClient.SpotApi.Trading.PlaceMarginOrderAsync("ETHUSDT",
// Binance.Net.Enums.OrderSide.Sell,
// Binance.Net.Enums.SpotOrderType.StopLossLimit,
// quantity: 0.0149M,
// price: 1699M,
// //quoteQuantity: 20M, //报价币金额
// //quantity: 100M,
// timeInForce: Binance.Net.Enums.TimeInForce.GoodTillCanceled,
// stopPrice: 1699M,
// isIsolated: true,
// orderResponseType: Binance.Net.Enums.OrderResponseType.Full).Result;
//var s1 = JsonConvert.SerializeObject(r1);
//var s = JsonConvert.SerializeObject(r);
//Console.WriteLine(s);
//var orderClient = new BinanceSpotOrderWebSocketClient(Enums.BusinessType.IsolateMargin, 0, apiKey, secret, LogManager.GetCurrentClassLogger());
//orderClient.Start("ETHUSDT");
Console.ReadKey();
}
}

14
SDKTestConsole/SDKTestConsole.csproj

@ -5,11 +5,25 @@
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<None Remove="NLog.config" />
</ItemGroup>
<ItemGroup>
<Content Include="NLog.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Binance.Net" Version="8.0.13" />
<PackageReference Include="NLog" Version="4.7.13" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Binance.TradeRobot.Common\Binance.TradeRobot.Common.csproj" />
<ProjectReference Include="..\Binance.TradeRobot.Model\Binance.TradeRobot.Model.csproj" />
<ProjectReference Include="..\SDKAdapter\SDKAdapter.csproj" />
</ItemGroup>

Loading…
Cancel
Save