diff --git a/Binance.TradeRobot.Business/Binance.TradeRobot.Business.xml b/Binance.TradeRobot.Business/Binance.TradeRobot.Business.xml index 1d25885..f1e2e7c 100644 --- a/Binance.TradeRobot.Business/Binance.TradeRobot.Business.xml +++ b/Binance.TradeRobot.Business/Binance.TradeRobot.Business.xml @@ -12,6 +12,13 @@ + + + 检查订单是否存在 + + + 订单不存在异常 + 检查机器人注册条件 diff --git a/Binance.TradeRobot.Business/Business/OrderPublishBusiness/Spot/BaseSpotOrderPublishBusiness.cs b/Binance.TradeRobot.Business/Business/OrderPublishBusiness/Spot/BaseSpotOrderPublishBusiness.cs index 7684f1e..4310a8a 100644 --- a/Binance.TradeRobot.Business/Business/OrderPublishBusiness/Spot/BaseSpotOrderPublishBusiness.cs +++ b/Binance.TradeRobot.Business/Business/OrderPublishBusiness/Spot/BaseSpotOrderPublishBusiness.cs @@ -13,16 +13,19 @@ namespace Binance.TradeRobot.Business { protected DingBusiness dingBusiness; protected RobotBusiness robotBusiness; + protected UserBusiness userBusiness; public BaseSpotOrderPublishBusiness(IFreeSql fsql, NLogManager logManager, IIdGenerator idGenerator, IMemoryCache memoryCache, DingBusiness dingBusiness, - RobotBusiness robotBusiness) : base(fsql, logManager, idGenerator, memoryCache) + RobotBusiness robotBusiness, + UserBusiness userBusiness) : base(fsql, logManager, idGenerator, memoryCache) { this.dingBusiness = dingBusiness; this.robotBusiness = robotBusiness; + this.userBusiness = userBusiness; } /// diff --git a/Binance.TradeRobot.Business/Business/OrderPublishBusiness/Spot/D21OrderPublishBusiness.cs b/Binance.TradeRobot.Business/Business/OrderPublishBusiness/Spot/D21OrderPublishBusiness.cs index ba6bd25..d48e7c2 100644 --- a/Binance.TradeRobot.Business/Business/OrderPublishBusiness/Spot/D21OrderPublishBusiness.cs +++ b/Binance.TradeRobot.Business/Business/OrderPublishBusiness/Spot/D21OrderPublishBusiness.cs @@ -17,7 +17,7 @@ namespace Binance.TradeRobot.Business { public Enums.TradePolicy TradePolicy => Enums.TradePolicy.D21; - public D21OrderPublishBusiness(IFreeSql fsql, NLogManager logManager, IIdGenerator idGenerator, IMemoryCache memoryCache, DingBusiness dingBusiness, RobotBusiness robotBusiness) : base(fsql, logManager, idGenerator, memoryCache, dingBusiness, robotBusiness) + public D21OrderPublishBusiness(IFreeSql fsql, NLogManager logManager, IIdGenerator idGenerator, IMemoryCache memoryCache, DingBusiness dingBusiness, RobotBusiness robotBusiness, UserBusiness userBusiness) : base(fsql, logManager, idGenerator, memoryCache, dingBusiness, robotBusiness, userBusiness) { } @@ -26,19 +26,27 @@ namespace Binance.TradeRobot.Business public void OnSpotOrderPublish(SpotOrderPublishInfo spotOrderPublishInfo) { //var logger = logManager.GetLogger(spotOrderPublishInfo.LoggerName); - var step = ""; + var step = string.Empty; var logList = new List(); try { + //step = "检查订单是否入库"; CheckOrderExists(spotOrderPublishInfo.OrderId); + //step = "查询订单所属机器人"; var robot = robotBusiness.GetD21PolicyRobotList(spotOrderPublishInfo.RobotId, isLoadRecentTradeProfit: false, isLoadAPIKey: true).FirstOrDefault(); if (robot == null) throw new BusinessException($"未找到机器人"); IUpdate updateSpotOrder = fsql.Update(spotOrderPublishInfo.OrderId).Set(o => o.State, spotOrderPublishInfo.SpotOrderState); + IUpdate updateRobotAccount = null; + IList> updateUserList = null; + List insertUserAccountProfitLossRecordList = null; - if (spotOrderPublishInfo.SpotOrderState == Enums.SpotOrderState.Rejected || spotOrderPublishInfo.SpotOrderState == Enums.SpotOrderState.Expired) + + if (spotOrderPublishInfo.SpotOrderState == Enums.SpotOrderState.Rejected || + spotOrderPublishInfo.SpotOrderState == Enums.SpotOrderState.Expired || + spotOrderPublishInfo.SpotOrderState == Enums.SpotOrderState.Canceled) { logList.Add(new ExecutionLog() { @@ -47,12 +55,14 @@ namespace Binance.TradeRobot.Business OrderId = spotOrderPublishInfo.OrderId, RobotId = spotOrderPublishInfo.RobotId, SourceSingal = Enums.SingalType.订单推送, - Content = $"收到订单推送,订单号:{spotOrderPublishInfo.OrderId},订单方向:{spotOrderPublishInfo.TradeDirection},订单类型:{spotOrderPublishInfo.OrderType},订单状态:{spotOrderPublishInfo.SpotOrderState}{(spotOrderPublishInfo.SpotOrderState == Enums.SpotOrderState.Rejected ? spotOrderPublishInfo.RejectedReason : "")}" + Content = $"收到订单推送,订单号:{spotOrderPublishInfo.OrderId},订单方向:{spotOrderPublishInfo.TradeDirection},订单类型:{spotOrderPublishInfo.OrderType},订单状态:{spotOrderPublishInfo.SpotOrderState}{(spotOrderPublishInfo.SpotOrderState == Enums.SpotOrderState.Rejected ? $",拒绝原因:{spotOrderPublishInfo.RejectedReason}" : string.Empty)}" }); } if (spotOrderPublishInfo.SpotOrderState == Enums.SpotOrderState.Filled) { + + var avgTradePrice = spotOrderPublishInfo.CummulativeTradeAmount / spotOrderPublishInfo.CummulativeTradeQuantity; //计算成交均价 logList.Add(new ExecutionLog() { Id = idGenerator.NewLong(), @@ -60,15 +70,107 @@ namespace Binance.TradeRobot.Business OrderId = spotOrderPublishInfo.OrderId, RobotId = spotOrderPublishInfo.RobotId, SourceSingal = Enums.SingalType.订单推送, - Content = $"收到订单推送,订单号:{spotOrderPublishInfo.OrderId},订单方向:{spotOrderPublishInfo.TradeDirection},订单类型:{spotOrderPublishInfo.OrderType},订单状态:{spotOrderPublishInfo.SpotOrderState},成交额:{spotOrderPublishInfo.LastTradeAmount},成交量:{spotOrderPublishInfo.LastTradeQuantity},成交价:{spotOrderPublishInfo.LastTradePrice},手续费({spotOrderPublishInfo.FeeUnit}):{spotOrderPublishInfo.Fee}" + Content = $"收到订单" + + $"推送,订单号:{spotOrderPublishInfo.OrderId},订单方向:{spotOrderPublishInfo.TradeDirection},订单类型:{spotOrderPublishInfo.OrderType},订单状态:{spotOrderPublishInfo.SpotOrderState},成交额:{spotOrderPublishInfo.LastTradeAmount},成交量:{spotOrderPublishInfo.LastTradeQuantity},成交价:{spotOrderPublishInfo.LastTradePrice},手续费({spotOrderPublishInfo.FeeUnit}):{spotOrderPublishInfo.Fee}" }); + //更新交易信息 updateSpotOrder = updateSpotOrder.Set(o => o.TradeAmount, spotOrderPublishInfo.CummulativeTradeAmount) .Set(o => o.TradeQuantity, spotOrderPublishInfo.CummulativeTradeQuantity) - .Set(o => o.TradePrice, spotOrderPublishInfo.CummulativeTradeAmount / spotOrderPublishInfo.CummulativeTradeQuantity) + .Set(o => o.TradePrice, avgTradePrice) .Set(o => o.TradeFee, spotOrderPublishInfo.Fee) - .Set(o => o.TradeFeeUnit, spotOrderPublishInfo.FeeUnit); + .Set(o => o.TradeFeeUnit, spotOrderPublishInfo.FeeUnit) + .Set(o => o.LastTradeTime, spotOrderPublishInfo.LastTradeTime); + + if (spotOrderPublishInfo.TradeDirection == Enums.TradeDirection.Buy) + { + var quantity = spotOrderPublishInfo.CummulativeTradeQuantity - spotOrderPublishInfo.Fee; //扣除基础币手续费,得到真实购买数量 + updateRobotAccount = fsql.Update(robot.RobotAccount.Id).Set(ra => ra.SpotCurrencyQuantity + quantity) + .Set(ra => ra.SpotCurrencyAmount + spotOrderPublishInfo.CummulativeTradeAmount); + + if (spotOrderPublishInfo.OrderType == Enums.OrderType.MARKET) + { + //市价买单完全成交,根据策略挂止损单 + + } + } + else if (spotOrderPublishInfo.TradeDirection == Enums.TradeDirection.Sell) + { + updateUserList = new List>(); + insertUserAccountProfitLossRecordList = new List(); + + if (spotOrderPublishInfo.OrderType == Enums.OrderType.MARKET) + { + //市价卖单完全成交,取消尚未触发的限价止损单 + + } + + var interest = 0M; //借币利息 + var loanAmount = robot.RobotAccount.LoanAmount; //借币金额 + if (loanAmount > 0M) + { + //还币 + var apiClient = GetBaseAPIClient(robot.ExchangeId, robot.ExchangeAPIKey.AccountId, robot.ExchangeAPIKey.APIKey, robot.ExchangeAPIKey.SecretKey); + interest = apiClient.IsolatedMarginRepay(robot.Symbol, loanAmount); + } + + + var buyAmount = spotOrderPublishInfo.CummulativeTradeQuantity * robot.RobotAccount.SpotCurrencyAvgPrice; //本次卖出对应的持仓金额 + var profit = spotOrderPublishInfo.CummulativeTradeQuantity * (avgTradePrice - robot.RobotAccount.SpotCurrencyAvgPrice) - spotOrderPublishInfo.Fee - interest; //计算利润 + + + updateSpotOrder = updateSpotOrder.SetIf(interest > 0M, o => o.LoanInterest, interest) + .Set(o => o.Profit, profit) + .Set(o => o.HistoryTotalProfit, robot.RobotAccount.TotalProfit + profit); + + updateRobotAccount = fsql.Update(robot.RobotAccount.Id).Set(ra => ra.SpotCurrencyQuantity - spotOrderPublishInfo.CummulativeTradeQuantity) + .Set(ra => ra.SpotCurrencyAmount - buyAmount) + .Set(ra => ra.TotalProfit + profit) + .Set(ra => ra.ClosePositionCount + 1) + .SetIf(profit > 0M, ra => ra.WinCount + 1) + .SetIf(interest > 0M, ra => ra.LoanAmount - loanAmount); + + var capitalChangeType = profit > 0M ? Enums.CapitalChangeType.Add : Enums.CapitalChangeType.Reduce; + var userList = userBusiness.GetUserList(multiplyBy100: false); + foreach (var user in userList) + { + var changeAmount = profit * user.DividendRatio; //根据用户分红比例计算本次分红或亏损 + + user.ChangeAmount(capitalChangeType, Math.Abs(changeAmount), false); + + + var updateUser = fsql.Update(user.Id).Set(u => u.CostAmount, user.CostAmount) + .Set(u => u.Profit, user.Profit); + updateUserList.Add(updateUser); + insertUserAccountProfitLossRecordList.Add(new UserAccountProfitLossRecord() + { + Id = idGenerator.NewLong(), + BusinessType = robot.BusinessType, + ChangeAmount = changeAmount, + CreateTime = DateTime.Now, + ExchangeId = robot.ExchangeId, + OrderId = spotOrderPublishInfo.OrderId, + OrderProfit = profit, + UserId = user.Id, + RobotId = robot.Id, + DividendRatio = user.DividendRatio, + UserProfit = user.Profit + }); + } + } } + fsql.Transaction(() => + { + fsql.Insert(logList).ExecuteAffrows(); + updateSpotOrder.ExecuteAffrows(); + updateRobotAccount?.ExecuteAffrows(); + if (insertUserAccountProfitLossRecordList != null && insertUserAccountProfitLossRecordList.Count() > 0) + fsql.Insert(insertUserAccountProfitLossRecordList).ExecuteAffrows(); + if (updateUserList != null && updateUserList.Count() > 0) + foreach (var u in updateUserList) + u.ExecuteAffrows(); + }); + } catch (Exception ex) { diff --git a/Binance.TradeRobot.Business/Business/RobotBusiness.cs b/Binance.TradeRobot.Business/Business/RobotBusiness.cs index 2d02513..5ffe730 100644 --- a/Binance.TradeRobot.Business/Business/RobotBusiness.cs +++ b/Binance.TradeRobot.Business/Business/RobotBusiness.cs @@ -248,7 +248,7 @@ namespace Binance.TradeRobot.Business ClosePositionCount = ra.ClosePositionCount, WinCount = ra.WinCount, LoanAmount = ra.LoanAmount, - SoptCurrentcyAmount = ra.SoptCurrentcyAmount, + SpotCurrencyAmount = ra.SpotCurrencyAmount, SpotCurrencyQuantity = ra.SpotCurrencyQuantity, TotalProfit = ra.TotalProfit, diff --git a/Binance.TradeRobot.Business/Business/UserBusiness.cs b/Binance.TradeRobot.Business/Business/UserBusiness.cs index 8f1c32d..161341a 100644 --- a/Binance.TradeRobot.Business/Business/UserBusiness.cs +++ b/Binance.TradeRobot.Business/Business/UserBusiness.cs @@ -58,7 +58,7 @@ namespace Binance.TradeRobot.Business }; } - public IList GetUserList() + public IList GetUserList(bool multiplyBy100 = true) { var userList = fsql.Select().ToList(u => new UserResponse() { @@ -70,7 +70,7 @@ namespace Binance.TradeRobot.Business UserName = u.UserName, WithdrawAmount = u.WithdrawAmount }); - userList.CalculateRatio(); + userList.CalculateRatio(multiplyBy100); return userList; } @@ -102,30 +102,30 @@ namespace Binance.TradeRobot.Business //if (totalBalance < changeAmount) // throw new BusinessException("交易所总余额小于减持资金,不能提现"); } - - if (capitalChangeType == Enums.CapitalChangeType.Add) - { - changeUser.CostAmount += changeAmount; - } - else if (capitalChangeType == Enums.CapitalChangeType.Reduce) - { - if (changeUser.Profit > 0) - { - if (changeUser.Profit >= changeAmount) - changeUser.Profit -= changeAmount; //收益足够提现,只扣收益 - else - { - var lessChangeAmount = changeAmount; //收益不足提现,先扣收益,不足部分再扣本金 - lessChangeAmount -= changeUser.Profit; - changeUser.Profit = 0; - changeUser.CostAmount -= lessChangeAmount; - } - } - else - { - changeUser.CostAmount -= changeAmount; - } - } + changeUser.ChangeAmount(capitalChangeType, changeAmount, true); + //if (capitalChangeType == Enums.CapitalChangeType.Add) + //{ + // changeUser.CostAmount += changeAmount; + //} + //else if (capitalChangeType == Enums.CapitalChangeType.Reduce) + //{ + // if (changeUser.Profit > 0) + // { + // if (changeUser.Profit >= changeAmount) + // changeUser.Profit -= changeAmount; //收益足够提现,只扣收益 + // else + // { + // var lessChangeAmount = changeAmount; //收益不足提现,先扣收益,不足部分再扣本金 + // lessChangeAmount -= changeUser.Profit; + // changeUser.Profit = 0; + // changeUser.CostAmount -= lessChangeAmount; + // } + // } + // else + // { + // changeUser.CostAmount -= changeAmount; + // } + //} fsql.Transaction(() => { diff --git a/Binance.TradeRobot.Model/Base/MappingProfiles.cs b/Binance.TradeRobot.Model/Base/MappingProfiles.cs index 036b463..b7fa0db 100644 --- a/Binance.TradeRobot.Model/Base/MappingProfiles.cs +++ b/Binance.TradeRobot.Model/Base/MappingProfiles.cs @@ -18,7 +18,7 @@ namespace Binance.TradeRobot.Model.Base CreateMap().ForPath(t => t.RobotAccount.Id, opt => opt.MapFrom(f => f.RobotAccountId)) .ForPath(t => t.RobotAccount.RobotId, opt => opt.MapFrom(f => f.Id)) - .ForPath(t => t.RobotAccount.SoptCurrentcyAmount, opt => opt.MapFrom(f => f.SoptCurrentcyAmount)) + .ForPath(t => t.RobotAccount.SpotCurrencyAmount, opt => opt.MapFrom(f => f.SpotCurrencyAmount)) .ForPath(t => t.RobotAccount.SpotCurrencyQuantity, opt => opt.MapFrom(f => f.SpotCurrencyQuantity)) .ForPath(t => t.RobotAccount.ClosePositionCount, opt => opt.MapFrom(f => f.ClosePositionCount)) .ForPath(t => t.RobotAccount.LoanAmount, opt => opt.MapFrom(f => f.LoanAmount)) diff --git a/Binance.TradeRobot.Model/Binance.TradeRobot.Model.xml b/Binance.TradeRobot.Model/Binance.TradeRobot.Model.xml index 6ccf878..b87ee4a 100644 --- a/Binance.TradeRobot.Model/Binance.TradeRobot.Model.xml +++ b/Binance.TradeRobot.Model/Binance.TradeRobot.Model.xml @@ -354,7 +354,7 @@ 平仓次数 - + 现货/杠杆持仓金额 @@ -449,7 +449,7 @@ 平仓次数 - + 现货/杠杆持仓金额 @@ -790,6 +790,14 @@ 分红比例 + + + 用户资金改变算法 + + + + true:优先增加本金 false:优先增加利润 + 最近一次小趋势信号 diff --git a/Binance.TradeRobot.Model/Db/Robot/Robot.cs b/Binance.TradeRobot.Model/Db/Robot/Robot.cs index bfa2d91..3d1c4f4 100644 --- a/Binance.TradeRobot.Model/Db/Robot/Robot.cs +++ b/Binance.TradeRobot.Model/Db/Robot/Robot.cs @@ -62,7 +62,7 @@ namespace Binance.TradeRobot.Model.Db /// 现货/杠杆持仓金额 /// [Column(IsIgnore = true)] - public decimal SoptCurrentcyAmount { get; set; } = 0.0M; + public decimal SpotCurrencyAmount { get; set; } = 0.0M; /// /// 现货/杠杆持仓数量 diff --git a/Binance.TradeRobot.Model/Db/Robot/RobotAccount.cs b/Binance.TradeRobot.Model/Db/Robot/RobotAccount.cs index d1830d3..2763474 100644 --- a/Binance.TradeRobot.Model/Db/Robot/RobotAccount.cs +++ b/Binance.TradeRobot.Model/Db/Robot/RobotAccount.cs @@ -23,7 +23,7 @@ namespace Binance.TradeRobot.Model.Db /// 现货/杠杆持仓金额 /// [Column(DbType = "decimal(18,8)")] - public decimal SoptCurrentcyAmount { get; set; } = 0.0M; + public decimal SpotCurrencyAmount { get; set; } = 0.0M; /// /// 现货/杠杆持仓数量 @@ -57,7 +57,7 @@ namespace Binance.TradeRobot.Model.Db { get { - return SpotCurrencyQuantity == 0M ? 0M : SoptCurrentcyAmount / SpotCurrencyQuantity; + return SpotCurrencyQuantity == 0M ? 0M : SpotCurrencyAmount / SpotCurrencyQuantity; } } } diff --git a/Binance.TradeRobot.Model/Dto/Response/User/UserResponse.cs b/Binance.TradeRobot.Model/Dto/Response/User/UserResponse.cs index ad3ff46..11836ce 100644 --- a/Binance.TradeRobot.Model/Dto/Response/User/UserResponse.cs +++ b/Binance.TradeRobot.Model/Dto/Response/User/UserResponse.cs @@ -1,4 +1,6 @@ -namespace Binance.TradeRobot.Model.Dto +using Binance.TradeRobot.Model.Base; + +namespace Binance.TradeRobot.Model.Dto { public class UserResponse : Db.User { @@ -16,5 +18,41 @@ /// 分红比例 /// public decimal DividendRatio { get; set; } + + /// + /// 用户资金改变算法 + /// + /// + /// + /// true:优先增加本金 false:优先增加利润 + public void ChangeAmount(Enums.CapitalChangeType capitalChangeType, decimal changeAmount, bool priorityAddCost) + { + if (capitalChangeType == Enums.CapitalChangeType.Add) + { + if (priorityAddCost) + CostAmount += changeAmount; + else + Profit += changeAmount; + } + else if (capitalChangeType == Enums.CapitalChangeType.Reduce) + { + if (Profit > 0) + { + if (Profit >= changeAmount) + Profit -= changeAmount; //收益足够提现,只扣收益 + else + { + var lessChangeAmount = changeAmount; //收益不足提现,先扣收益,不足部分再扣本金 + lessChangeAmount -= Profit; + Profit = 0; + CostAmount -= lessChangeAmount; + } + } + else + { + CostAmount -= changeAmount; + } + } + } } }