21 changed files with 470 additions and 125 deletions
@ -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}"); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -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; } |
||||
|
} |
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -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 = "") |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
@ -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> |
Loading…
Reference in new issue