币安量化交易
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.

316 lines
19 KiB

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<long, decimal> 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<long, decimal>();
}
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<ExecutionLog>();
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<SpotOrder> updateSpotOrder = fsql.Update<SpotOrder>(spotOrderPublishInfo.OrderId).Set(o => o.State, spotOrderPublishInfo.SpotOrderState);
IUpdate<RobotAccount> updateRobotAccount = null;
IList<IUpdate<User>> updateUserList = null;
List<UserAccountProfitLossRecord> insertUserAccountProfitLossRecordList = null;
IUpdate<D21Policy> updateD21Policy = null;
List<SpotOrder> 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<RobotAccount>(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<SpotOrder>();
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<IUpdate<User>>();
insertUserAccountProfitLossRecordList = new List<UserAccountProfitLossRecord>();
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<RobotAccount>(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<D21Policy>(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>(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);
}
}
/// <summary>
/// 挂止损单
/// </summary>
/// <param name="d21Robot"></param>
/// <param name="symbolInfo"></param>
/// <param name="avgTradePrice"></param>
/// <param name="buyQuantity"></param>
/// <param name="insertStopLossOrderList"></param>
/// <param name="logList"></param>
/// <param name="baseAPIClient"></param>
/// <param name="isFirstStopLoss"></param>
private void StopLossOrderPlace(D21PolicyRobotResponse d21Robot,
SymbolInfo symbolInfo,
decimal avgTradePrice,
decimal buyQuantity,
IList<SpotOrder> insertStopLossOrderList,
IList<ExecutionLog> 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}"
});
}
}
}