using Binance.TradeRobot.Common.DI; using Binance.TradeRobot.Common.Extensions; using Binance.TradeRobot.Model.Base; using Binance.TradeRobot.Model.Db; using Binance.TradeRobot.Model.Dto; using FreeSql; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.DependencyInjection; using SDKAdapter.APIClient; using SDKAdapter.Model; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using Yitter.IdGenerator; namespace Binance.TradeRobot.Business { [BatchRegistration(ServiceLifetime.Singleton, RegistrationType.Interface)] internal class D21OrderPublishBusiness : BaseSpotOrderPublishBusiness, ISpotOrderPublishBusiness { private ConcurrentDictionary spotFeeDictionary; public Enums.TradePolicy TradePolicy => Enums.TradePolicy.D21; public D21OrderPublishBusiness(IFreeSql fsql, NLogManager logManager, IIdGenerator idGenerator, IMemoryCache memoryCache, DingBusiness dingBusiness, RobotBusiness robotBusiness, UserBusiness userBusiness, ExchangeBusiness exchangeBusiness) : base(fsql, logManager, idGenerator, memoryCache, dingBusiness, robotBusiness, userBusiness, exchangeBusiness) { spotFeeDictionary = new ConcurrentDictionary(); } private decimal AddSpotFee(long orderId, decimal currentTradeFee, bool deleteKey) { if (!spotFeeDictionary.TryGetValue(orderId, out decimal fee)) { fee = 0M; spotFeeDictionary.TryAdd(orderId, fee); } fee += currentTradeFee; spotFeeDictionary[orderId] = fee; if (deleteKey) spotFeeDictionary.TryRemove(orderId, out _); return fee; } public void OnSpotOrderPublish(SpotOrderPublishInfo spotOrderPublishInfo) { 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($"未找到机器人"); var symbolInfo = exchangeBusiness.GetSymbol(spotOrderPublishInfo.Exchange, spotOrderPublishInfo.Symbol); var apiClient = GetBaseAPIClient(robot.ExchangeId, robot.ExchangeAPIKey.AccountId, robot.ExchangeAPIKey.APIKey, robot.ExchangeAPIKey.SecretKey); IUpdate updateSpotOrder = fsql.Update(spotOrderPublishInfo.OrderId).Set(o => o.State, spotOrderPublishInfo.SpotOrderState); IUpdate updateRobotAccount = null; IList> updateUserList = null; List insertUserAccountProfitLossRecordList = null; IUpdate updateD21Policy = null; List insertStopLossOrderList = null; if (spotOrderPublishInfo.SpotOrderState == Enums.SpotOrderState.Rejected || spotOrderPublishInfo.SpotOrderState == Enums.SpotOrderState.Expired || spotOrderPublishInfo.SpotOrderState == Enums.SpotOrderState.Canceled) { logList.Add(new ExecutionLog() { Id = idGenerator.NewLong(), CreateTime = DateTime.Now, 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}" : string.Empty)}" }); } if (spotOrderPublishInfo.SpotOrderState == Enums.SpotOrderState.PartiallyFilled) { logList.Add(new ExecutionLog() { Id = idGenerator.NewLong(), CreateTime = DateTime.Now, 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}" }); updateSpotOrder = null; _ = AddSpotFee(spotOrderPublishInfo.OrderId, spotOrderPublishInfo.Fee, false); } if (spotOrderPublishInfo.SpotOrderState == Enums.SpotOrderState.Filled) { var fee = AddSpotFee(spotOrderPublishInfo.OrderId, spotOrderPublishInfo.Fee, true); var avgTradePrice = spotOrderPublishInfo.CummulativeTradeAmount / spotOrderPublishInfo.CummulativeTradeQuantity; //计算成交均价 logList.Add(new ExecutionLog() { Id = idGenerator.NewLong(), CreateTime = DateTime.Now, 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},总手续费:{fee}" }); //更新交易信息 updateSpotOrder = updateSpotOrder.Set(o => o.TradeAmount, spotOrderPublishInfo.CummulativeTradeAmount) .Set(o => o.TradeQuantity, spotOrderPublishInfo.CummulativeTradeQuantity) .Set(o => o.TradePrice, avgTradePrice) .Set(o => o.TradeFee, fee) .Set(o => o.TradeFeeUnit, spotOrderPublishInfo.FeeUnit) .Set(o => o.LastTradeTime, spotOrderPublishInfo.LastTradeTime); //查询交易所真实资产 var currentAsset = apiClient.GetIsolatedMarginAccountAssets().FirstOrDefault(x => x.Symbol == spotOrderPublishInfo.Symbol); if (spotOrderPublishInfo.TradeDirection == Enums.TradeDirection.Buy) { var quantity = spotOrderPublishInfo.CummulativeTradeQuantity - fee; //扣除基础币手续费,得到真实购买数量 //基础币可用资产,交易所账户持币数量 //更新机器人账户 updateRobotAccount = fsql.Update(robot.RobotAccount.Id).Set(ra => ra.SpotCurrencyQuantity, currentAsset.BaseFree) .Set(ra => ra.SpotCurrencyAmount + spotOrderPublishInfo.CummulativeTradeAmount); //交易所账户真实持币数量 updateSpotOrder = updateSpotOrder.Set(o => o.RobotAccountSpotCurrencyQuantity, currentAsset.BaseFree); if (spotOrderPublishInfo.OrderType == Enums.OrderType.MARKET) { #region 市价买单挂止损 insertStopLossOrderList = new List(); try { if (robot.D21Policy.Level1PositionStopLossRatio > 0 && robot.D21Policy.Level1PriceStopLossRatio > 0) StopLossOrderPlace(robot, symbolInfo, avgTradePrice, quantity, insertStopLossOrderList, logList, apiClient, true); if (robot.D21Policy.Level2PositionStopLossRatio > 0 && robot.D21Policy.Level2PriceStopLossRatio > 0 && robot.D21Policy.Level2PositionStopLossRatio + robot.D21Policy.Level1PositionStopLossRatio == 100) StopLossOrderPlace(robot, symbolInfo, avgTradePrice, quantity, insertStopLossOrderList, logList, apiClient, false); } catch (Exception ex) { logList.Add(new ExecutionLog() { Id = idGenerator.NewLong(), SourceSingal = Enums.SingalType.订单推送, RobotId = robot.Id, CreateTime = DateTime.Now, Content = $"止损单挂单失败,{ex.Message}" }); } #endregion } } else if (spotOrderPublishInfo.TradeDirection == Enums.TradeDirection.Sell) { updateUserList = new List>(); insertUserAccountProfitLossRecordList = new List(); var interest = 0M; //借币利息 var loanAmount = currentAsset.QuoteBorrowed; //借币金额 if (loanAmount > 0M) { //还币 interest = apiClient.IsolatedMarginRepay(robot.Symbol, loanAmount); logList[0].Content = $"{logList[0].Content},借币金额:{loanAmount},还币利息:{interest}"; } var buyAmount = spotOrderPublishInfo.CummulativeTradeQuantity * robot.RobotAccount.SpotCurrencyAvgPrice; //本次卖出对应的持仓金额 var profit = spotOrderPublishInfo.CummulativeTradeQuantity * (avgTradePrice - robot.RobotAccount.SpotCurrencyAvgPrice) - fee - interest; //计算利润 updateRobotAccount = fsql.Update(robot.RobotAccount.Id).Set(ra => ra.SpotCurrencyQuantity, currentAsset.BaseFree) .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, 0M); updateSpotOrder = updateSpotOrder.SetIf(interest > 0M, o => o.LoanInterest, interest) .Set(o => o.Profit, profit) .Set(o => o.HistoryTotalProfit, robot.RobotAccount.TotalProfit + profit) .Set(o => o.RobotAccountSpotCurrencyQuantity, currentAsset.BaseFree); if (profit > 0) //盈利复投 updateD21Policy = fsql.Update(robot.D21Policy.Id).Set(p => p.Position + profit); 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.TotalAssets, user.TotalAssets); 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, CumulativeProfitAndLoss = user.CumulativeProfitAndLoss }); } } } fsql.Transaction(() => { fsql.Insert(logList).ExecuteAffrows(); updateSpotOrder?.ExecuteAffrows(); updateRobotAccount?.ExecuteAffrows(); updateD21Policy?.ExecuteAffrows(); if (insertUserAccountProfitLossRecordList != null && insertUserAccountProfitLossRecordList.Count() > 0) fsql.Insert(insertUserAccountProfitLossRecordList).ExecuteAffrows(); if (updateUserList != null && updateUserList.Count() > 0) foreach (var u in updateUserList) u.ExecuteAffrows(); if (insertStopLossOrderList != null && insertStopLossOrderList.Count() > 0) fsql.Insert(insertStopLossOrderList).ExecuteAffrows(); }); } catch (Exception ex) { HandleError(ex, logList, spotOrderPublishInfo.LoggerName, spotOrderPublishInfo.RobotId, spotOrderPublishInfo.OrderId, step); } } /// /// 挂止损单 /// /// /// /// /// /// /// /// /// private void StopLossOrderPlace(D21PolicyRobotResponse d21Robot, SymbolInfo symbolInfo, decimal avgTradePrice, decimal buyQuantity, IList insertStopLossOrderList, IList logList, BaseAPIClient baseAPIClient, bool isFirstStopLoss) { var positionStopLossRatio = (isFirstStopLoss ? d21Robot.D21Policy.Level1PositionStopLossRatio : d21Robot.D21Policy.Level2PositionStopLossRatio) / 100; var priceStopLossRatio = (isFirstStopLoss ? d21Robot.D21Policy.Level1PriceStopLossRatio : d21Robot.D21Policy.Level2PriceStopLossRatio) / 100; var stopPrice = (avgTradePrice - avgTradePrice * priceStopLossRatio).CutDecimal(symbolInfo.PriceAccuracy); var stopQuantity = (buyQuantity * positionStopLossRatio).CutDecimal(symbolInfo.SaleQuantityAccuracy); var stopLossClientOrderId = CreateClientOrderId(d21Robot.Id, d21Robot.TradePolicy); var stopOrderId = baseAPIClient.IsolatedMarginPlaceOrder(d21Robot.Symbol, Enums.TradeDirection.Sell, Enums.OrderType.STOP_LOSS_LIMIT, stopPrice: stopPrice, price: stopPrice, quantity: stopQuantity, newClientOrderId: stopLossClientOrderId); var stopLossOrder = new SpotOrder() { Id = stopOrderId, ClientOrderId = stopLossClientOrderId, CreateTime = DateTime.Now, ExchangeId = d21Robot.ExchangeId, LoanAmount = 0M, OrderType = Enums.OrderType.STOP_LOSS_LIMIT, PolicyType = Enums.TradePolicy.D21, RobotId = d21Robot.Id, State = Enums.SpotOrderState.Created, Symbol = d21Robot.Symbol, TradeDirection = Enums.TradeDirection.Sell }; insertStopLossOrderList.Add(stopLossOrder); logList.Add(new ExecutionLog() { Id = idGenerator.NewLong(), SourceSingal = Enums.SingalType.订单推送, RobotId = d21Robot.Id, OrderId = stopOrderId, CreateTime = DateTime.Now, Content = $"{(isFirstStopLoss ? 1 : 2)}级止损挂单成功,订单号:{stopOrderId},挂单数量:{stopQuantity}" }); } } }