|
|
@ -1,9 +1,12 @@ |
|
|
|
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.Generic; |
|
|
@ -17,7 +20,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, UserBusiness userBusiness) : base(fsql, logManager, idGenerator, memoryCache, dingBusiness, robotBusiness, userBusiness) |
|
|
|
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) |
|
|
|
{ |
|
|
|
|
|
|
|
} |
|
|
@ -37,6 +40,8 @@ namespace Binance.TradeRobot.Business |
|
|
|
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); |
|
|
@ -44,6 +49,7 @@ namespace Binance.TradeRobot.Business |
|
|
|
IList<IUpdate<User>> updateUserList = null; |
|
|
|
List<UserAccountProfitLossRecord> insertUserAccountProfitLossRecordList = null; |
|
|
|
IUpdate<D21Policy> updateD21Policy = null; |
|
|
|
List<SpotOrder> insertStopLossOrderList = null; |
|
|
|
|
|
|
|
|
|
|
|
if (spotOrderPublishInfo.SpotOrderState == Enums.SpotOrderState.Rejected || |
|
|
@ -97,8 +103,30 @@ namespace Binance.TradeRobot.Business |
|
|
|
|
|
|
|
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) |
|
|
@ -109,7 +137,21 @@ namespace Binance.TradeRobot.Business |
|
|
|
if (spotOrderPublishInfo.OrderType == Enums.OrderType.MARKET) |
|
|
|
{ |
|
|
|
//市价卖单完全成交,取消尚未触发的限价止损单
|
|
|
|
|
|
|
|
try |
|
|
|
{ |
|
|
|
CancelStopLossOrder(robot, apiClient); |
|
|
|
} |
|
|
|
catch (Exception ex) |
|
|
|
{ |
|
|
|
logList.Add(new ExecutionLog() |
|
|
|
{ |
|
|
|
Id = idGenerator.NewLong(), |
|
|
|
SourceSingal = Enums.SingalType.订单推送, |
|
|
|
RobotId = robot.Id, |
|
|
|
CreateTime = DateTime.Now, |
|
|
|
Content = $"取消止损单失败,{ex.Message}" |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
var interest = 0M; //借币利息
|
|
|
@ -173,9 +215,6 @@ namespace Binance.TradeRobot.Business |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
fsql.Transaction(() => |
|
|
@ -189,6 +228,8 @@ namespace Binance.TradeRobot.Business |
|
|
|
if (updateUserList != null && updateUserList.Count() > 0) |
|
|
|
foreach (var u in updateUserList) |
|
|
|
u.ExecuteAffrows(); |
|
|
|
if (insertStopLossOrderList != null && insertStopLossOrderList.Count() > 0) |
|
|
|
fsql.Insert(insertStopLossOrderList).ExecuteAffrows(); |
|
|
|
}); |
|
|
|
|
|
|
|
} |
|
|
@ -197,5 +238,76 @@ namespace Binance.TradeRobot.Business |
|
|
|
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; |
|
|
|
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, |
|
|
|
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}" |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
private void CancelStopLossOrder(D21PolicyRobotResponse d21Robot, BaseAPIClient baseAPIClient) |
|
|
|
{ |
|
|
|
var stopLossOrderIdList = fsql.Select<SpotOrder>().Where(o => o.OrderType == Enums.OrderType.STOP_LOSS_LIMIT && |
|
|
|
o.State == Enums.SpotOrderState.Created && |
|
|
|
o.RobotId == d21Robot.Id).ToList(o => o.Id); |
|
|
|
if (stopLossOrderIdList == null || stopLossOrderIdList.Count() == 0) |
|
|
|
return; |
|
|
|
|
|
|
|
foreach (var stopLossOrderId in stopLossOrderIdList) |
|
|
|
baseAPIClient.CancelIsolateMarginOrder(d21Robot.Symbol, stopLossOrderId); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|