Files
zszq-trs/YLErpDAL/Modules/SwapModule/SwapTradeAutoService.cs
T

1517 lines
78 KiB
C#

using ClosedXML.Report.Utils;
using Confluent.Kafka;
using CsvHelper;
using Dapper;
using DocumentFormat.OpenXml.Drawing;
using MoreLinq;
using NPOI.SS.Formula.Functions;
using Qdp.ComputeServiceV2.Data.CommonModels.TradeInfos;
using Qdp.Pricing.Base.Enums;
using Qdp.Pricing.Base.Implementations;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using YLErp.BLL;
using YLErp.BLL.Calculation;
using YLErp.BLL.Eod;
using YLErp.DataBase;
using YLErp.DBModels;
using YLErp.DBModels.Enums;
using YLErp.Enums;
using YLErp.Helpers;
using YLErp.Model;
using YLErp.Model.Enum;
using YLErp.Models;
using YLErp.Modules.AppModule;
using YLErp.Modules.EodModule.QueryModule;
using YLErp.Modules.RiskModule;
using YLErp.Modules.TradeMsgOutputModule;
using YLErp.QdpModule;
using static alglib;
namespace YLErp.Modules.SwapModule
{
public class SwapTradeAutoService : YLBaseService
{
protected static IYcLogger Log = LogFactory.GetLogger(typeof(SwapTradeAutoService).FullName);
private static string LongShortStructType = "定义文件型债券收益互换";
public SwapTradeAutoService(OptUserInfo optUser) : base(optUser)
{
}
/// <summary>
/// 从db获取流水自动簿记
/// </summary>
public void GenerateSwapTradeFromDb(DateTime valueDate, bool reset)
{
using (var bondDb = new BondOmsDBContext())
{
var bondDbConn = bondDb.Database.GetDbConnection();
var clientDealSql = GetClientDealSql(valueDate);
var currentDeals = bondDbConn.Query<ClientDeal>(clientDealSql);
var swapFlows = DbContext.swap_flow.Where(x => x.OccurTime >= valueDate && x.OccurTime < valueDate.AddDays(1)).ToList();
var underlyingCodes = DbContext.underlying_manager.Where(x => x.LaunchState == "1");
foreach (var item in currentDeals)
{
var swapFlow = swapFlows.FirstOrDefault(x => x.trs_deal_id == item.id);
if (swapFlow != null && !reset)
{
continue;
}
else if (swapFlow == null)
{
swapFlow = new swap_flow();
}
var underlying = underlyingCodes.FirstOrDefault(x => x.UnderlyingCode == item.security_id);
swapFlow.ContractSize = underlying == null ? 1 : Convert.ToDecimal(underlying.ContractSize);
swapFlow.OccurTime = valueDate;
swapFlow.trs_deal_id = item.id;
swapFlow.BsType = item.side + 1;
swapFlow.UnderlyingCode = item.security_id;
swapFlow.TradingQty = (item.last_shares ?? 0) * 10000;
swapFlow.TradingFee = (item.commission ?? 0);
if (swapFlow.id == 0 || reset)
{
swapFlow.SwapTradeId = null;
swapFlow.SwapTradeNo = null;
swapFlow.DataState = (int)SwapFlowDateStateEnum.等待完成;
}
swapFlow.TradingAmountAvg = (item.deal_full_price ?? 0) * 0.01m;
swapFlow.TradingAmountFeeAvg = (item.deal_full_price_include_fee ?? 0) * 0.01m;
swapFlow.TradingAmount = swapFlow.TradingQty * swapFlow.ContractSize;
swapFlow.ClientId = Convert.ToInt32(item.client_id ?? 0);
swapFlow.ytm = (item.ytm ?? 0) * 0.01m;
swapFlow.TradingAmountNet = (item.deal_price ?? 0) * 0.01m;
swapFlow.TradingAmountNetFee = (item.deal_price_include_fee ?? 0) * 0.01m;
swapFlow.ClientName = item.client_name;
swapFlow.SetOpt(UserInfo);
swapFlow.OptTime = item.create_time.HasValue ? item.create_time.Value : DateTime.Now;
swapFlow.UnderlyingName = item.symbol;
swapFlow.DealType = item.deal_type;
swapFlow.SettleDate = valuedateBLL.GetNonHoliday(valueDate.AddDays(1)).Date;
if (swapFlow.id == 0)
{
DbContext.swap_flow.Add(swapFlow);
}
}
if (reset)
{
foreach (var flow in swapFlows)
{
flow.DataState = (int)SwapFlowDateStateEnum.等待完成;
}
}
DbContext.SaveChanges();
Task.Run(() =>
{
RealtimePnlCalc.RealtimeSwapPosition(new OptUserInfo(0, "互换实时持仓服务", OptUserFrom.Service));
});
}
}
private string GetClientDealSql(DateTime valueDate)
{
var tomorrowDate = valueDate.AddDays(1);
Type type = typeof(ClientDeal);
PropertyInfo[] properties = type.GetProperties();
string[] fieldNames = properties.Where(s => s.CustomAttributes.Count() == 0).Select(f => "cd." + f.Name).ToArray();
var selectStr = string.Join(",", fieldNames);
string sql = $"select cd.id,{selectStr},co.settl_type from client_deal cd inner join client_order co on co.id=cd.client_order_id where cd.create_time<'{tomorrowDate.ToString("yyyy-MM-dd")}' and cd.create_time>='{valueDate.ToString("yyyy-MM-dd")}'";
return sql;
}
public void ResetTradeByDate(DateTime valueDate, int? clientId, string underlyingCode, Action<decimal>? action, List<int> tradeIds)
{
var swapEvents = DbContext.swap_event.Where(x => x.EventReason.Contains("自动") && x.ValueDate == valueDate);
//bool resetSingle = false;
if (tradeIds == null || tradeIds.Count() == 0)
{
tradeIds = swapEvents.Select(s => s.SwapTradeId).ToList();
}
//else
//{
// resetSingle = true;
//}
var swaptrades = DbContext.trade.Where(x => tradeIds.Contains(x.id) && x.ValidState != ConsGlobal.InValid).ToList();
var swapflowMerges = DbContext.swap_flow_merge.Where(x => x.OccurTime == valueDate);
var swapflowDeals = DbContext.swap_flow_deal.Where(x => x.OccurDate == valueDate);
var swapFlows = DbContext.swap_flow.Where(x => x.OccurTime == valueDate);
if (clientId.HasValue)
{
swapFlows = swapFlows.Where(x => x.ClientId == clientId);
swapflowDeals = swapflowDeals.Where(x => x.ClientId == clientId);
swapflowMerges = swapflowMerges.Where(x => x.ClientId == clientId);
}
if (!string.IsNullOrEmpty(underlyingCode))
{
swapFlows = swapFlows.Where(x => x.UnderlyingCode == underlyingCode);
swapflowMerges = swapflowMerges.Where(x => x.UnderlyingCode == underlyingCode);
swapflowDeals = swapflowDeals.Where(x => x.UnderlyingCode == underlyingCode);
}
var trsDealIds = swapFlows.Where(x => x.trs_deal_id > 0).Select(s => s.trs_deal_id ?? 0).ToList();
var swaptradesCount = swaptrades.Count();
decimal backProcessedCount = 0;
if (swaptradesCount == 0)
{
backProcessedCount = 100;
}
foreach (var td in swaptrades)
{
var isAutoOpenTrade = td.IsAutoGenerate == true;
new SwapTradeService(UserInfo).TradeBack(td.id, valueDate, isAutoOpenTrade);
backProcessedCount++;
var processedPercent = backProcessedCount * 100 / swaptradesCount;
action?.Invoke(processedPercent);
}
swapFlows.ForEach(x => { x.DataState = (int)SwapFlowDateStateEnum.等待完成; });
new TradeRiskCheckLogService(UserInfo).DeleteLogs(trsDealIds);
DbContext.swap_flow_merge.RemoveRange(swapflowMerges);
DbContext.swap_flow_deal.RemoveRange(swapflowDeals);
DbContext.SaveChanges();
}
/// <summary>
/// 校验是否能重置流水
/// </summary>
/// <param name="clientId"></param>
/// <param name="underlyingCode"></param>
/// <param name="valueDate"></param>
/// <returns></returns>
public bool CheckFlowAfter(int? clientId, string underlyingCode, DateTime valueDate) {
Expression<Func<swap_flow, bool>> expression =x=> x.OccurTime > valueDate && x.DataState == (int)SwapFlowDateStateEnum.完成;
if (clientId.HasValue)
{
expression = expression.And(x=>x.ClientId==clientId);
}
if (!string.IsNullOrEmpty(underlyingCode))
{
expression = expression.And(x => x.UnderlyingCode == underlyingCode);
}
return DbContext.swap_flow.Any(expression);
}
/// <summary>
/// 获取需要重置的交易
/// </summary>
/// <param name="clientId"></param>
/// <param name="underlyingCode"></param>
/// <param name="valueDate"></param>
/// <returns></returns>
public List<int> GetNeedResetTradeIds(int? clientId, string underlyingCode, DateTime valueDate)
{
var swapEvents = DbContext.swap_event.Where(x => x.EventReason.Contains("自动") && x.ValueDate == valueDate);
var tradeIds = swapEvents.Select(s => s.SwapTradeId).ToList();
Expression<Func<trade, bool>> expression = x => tradeIds.Contains(x.id) && x.ValidState != ConsGlobal.InValid;
if (clientId.HasValue)
{
expression = expression.And(x => x.ClientId == clientId);
}
if (!string.IsNullOrEmpty(underlyingCode))
{
expression = expression.And(x => x.UnderlyingCode == underlyingCode);
}
var resetTradeIds = DbContext.trade.Where(expression).Select(s=>s.id).ToList();
return resetTradeIds;
}
/// <summary>
/// 校验交易是否收盘
/// </summary>
/// <param name="valueDate"></param>
/// <param name="mergeList"></param>
/// <exception cref="ServiceException"></exception>
public void CheckTradEods(DateTime valueDate, List<swap_flow_merge> mergeList)
{
var preSettleDate = QdpCalendarHelper.GetNonHolidayDefore(valueDate.AddDays(-1));
new BaseTradeAfterEodOutputService().CheckEodStatus(preSettleDate);//上日收盘校验
var clientIds = mergeList.Select(s => s.ClientId).Distinct().ToList();
var swaptrades = DbContext.trade.Where(t => t.TradeType == "收益互换"
&& t.TradeDate <= valueDate
&& t.ValidState != ConsGlobal.InValid
&& clientIds.Contains(t.ClientId)
&& !ConsTrade.TradeCompleteStatus.Contains(t.TradeStatus)).ToList();
var tradeIds = swaptrades.Select(s => s.id).ToList();
var swapEodTrades = new SwapEodPositionService(UserInfo).GetEodSwaps(tradeIds, preSettleDate);
foreach (var swap in swaptrades)
{
var eodTrade = swapEodTrades.FirstOrDefault(x => x.SwapTradeId == swap.id);
if (eodTrade == null && swap.StartDate.Value < valueDate)
{
throw new ServiceException($"交易{swap.TradeNumber}在{preSettleDate:yyyy-MM-dd}日未收盘");
}
}
}
/// <summary>
/// 检查当日是否流水合成簿记
/// </summary>
/// <param name="valueDate"></param>
/// <exception cref="ServiceException"></exception>
public void CheckBookByDate(DateTime valueDate)
{
if (DbContext.swap_flow.Any(x => x.OccurTime == valueDate && x.DataState == (int)SwapFlowDateStateEnum.完成))
{
throw new ServiceException($"{valueDate:yyyy-MM-dd}日已经处理过流水合成簿记,请重置再重新合成簿记!");
}
}
/// <summary>
/// 修改风控日志记录
/// </summary>
/// <param name="swapFlows"></param>
/// <param name="mergeList"></param>
public void UpdateRiskCheckLog(List<swap_flow> swapFlows, List<swap_flow_merge> mergeList)
{
var riskCheckLogService = new TradeRiskCheckLogService(UserInfo);
foreach (var item in mergeList)
{
var flows = swapFlows.Where(x => x.ClientId == item.ClientId && x.UnderlyingCode == item.UnderlyingCode && x.BsType == item.BsType && x.trs_deal_id > 0);
var trsDealIds = flows.Select(s => s.trs_deal_id ?? 0).ToList();
riskCheckLogService.UpdateLogTradeNumber(trsDealIds, item.SwapTradeNo);
}
}
public List<swap_flow> GetFlows(DateTime valueDate)
{
var swapFlows = DbContext.swap_flow.Where(x => x.OccurTime == valueDate && x.DataState == (int)SwapFlowDateStateEnum.等待完成 && x.ClientId > 0).ToList();
return swapFlows;
}
public List<swap_flow> GetFlows(List<long> ids)
{
var swapFlows = DbContext.swap_flow.Where(x => ids.Contains(x.id) && x.DataState == (int)SwapFlowDateStateEnum.等待完成 && x.ClientId > 0).ToList();
return swapFlows;
}
/// <summary>
/// 簿记前自动校验
/// </summary>
/// <param name="mergeList"></param>
public void BookingValidate(List<swap_flow_merge> mergeList)
{
foreach (var merge in mergeList)
{
var client = DataCacheProvider.GetClientDataSource().GetData(merge.ClientId ?? 0);
if (client == null)
{
throw new ServiceException($"找不到id为{merge.ClientId}的客户信息");
}
if (!client.DerivativesInvestmentVarieties.Contains((int)DerivativesInvestmentVarietiesEnum.场外互换 + ""))
{
throw new ServiceException($"客户:{client.Name}未设置交易种类“场外互换”,无法生成互换交易!");
}
merge.SwapTradeType = client.SwapTradeType ?? 0;
var etradeRule = new EtradingRuleService(UserInfo).GetEtradingRuleAccont(client.BoundSide, client.Number);
if (etradeRule == null || string.IsNullOrEmpty(etradeRule.AssetAccount_0))
{
throw new ServiceException($"{client.Name}未设置TRS对客簿记账户");
}
string clearingAgency = etradeRule.ClearingAgency_0;
var asset = DataCacheProvider.GetAssetUnitDataSource().AsQueryable(x => x.Name == etradeRule.AssetAccount_0).FirstOrDefault();//取对客簿记账户
if (asset == null)
{
throw new ServiceException($"找不到名为{etradeRule.AssetAccount_0}的簿记账户信息");
}
if (asset.TraderIdsInt.Count == 0)
{
throw new ServiceException($"{etradeRule.AssetAccount_0}的簿记账户未设置交易员");
}
var underlying = DbContext.underlying_manager.FirstOrDefault(x => x.UnderlyingCode == merge.UnderlyingCode);
if (underlying == null)
{
throw new ServiceException($"找不到标的代码为{merge.UnderlyingCode}的标的信息");
}
}
}
/// <summary>
/// 汇总流水
/// </summary>
/// <param name="valueDate"></param>
public List<swap_flow_merge> SummaryFlow(List<swap_flow> swapFlows, bool save = true)
{
var swapFlowGroup = swapFlows.GroupBy(g => new { g.ClientId, g.OccurTime, g.UnderlyingCode, g.BsType }).ToList();
List<swap_flow_merge> list = new List<swap_flow_merge>();
foreach (var gourpItem in swapFlowGroup)
{
var flowList = gourpItem.OrderBy(O => O.OptTime).ToList();
var swapflow = flowList.First();
swap_flow_merge swap_flow_summary = new swap_flow_merge()
{
OccurTime = swapflow.OccurTime.Value,
FundAccount = swapflow.FundAccount,
SwapTradeId = swapflow.SwapTradeId,
SwapTradeNo = swapflow.SwapTradeNo,
UnderlyingCode = swapflow.UnderlyingCode,
BsType = swapflow.BsType,
TradingQty = gourpItem.Sum(s => s.TradingQty),
TradingFeePending = gourpItem.Sum(s => s.TradingFee),
DataState = (int)SwapFlowDateStateEnum.等待完成,
ContractSize = swapflow.ContractSize,
ClientId = swapflow.ClientId,
};
int tradeSide = swap_flow_summary.BsType == (int)EnumDirection.Long ? 1 : -1;
swap_flow_summary.FirstFlowTime = swapflow.OptTime;
swap_flow_summary.SettleDate = gourpItem.Max(s => s.SettleDate);
swap_flow_summary.TradingAmount = swap_flow_summary.TradingQty * swap_flow_summary.ContractSize;
swap_flow_summary.TradingAmountAvg = swap_flow_summary.TradingQty == 0 ? 0 : gourpItem.Sum(s => s.TradingAmountAvg * s.TradingQty) / swap_flow_summary.TradingQty;
swap_flow_summary.TradingAmountFeeAvg = swap_flow_summary.TradingQty == 0 ? swap_flow_summary.TradingAmountAvg : swap_flow_summary.TradingAmountAvg + swap_flow_summary.TradingFeePending * tradeSide / swap_flow_summary.TradingQty;
swap_flow_summary.TradingAmountNetAvg = swap_flow_summary.TradingQty == 0 ? 0 : gourpItem.Sum(s => s.TradingAmountNet * s.TradingQty) / swap_flow_summary.TradingQty;
swap_flow_summary.TradingAmountNetFeeAvg = swap_flow_summary.TradingQty == 0 ? swap_flow_summary.TradingAmountNetAvg : swap_flow_summary.TradingAmountNetAvg + swap_flow_summary.TradingFeePending * tradeSide / swap_flow_summary.TradingQty;
swap_flow_summary.SetOpt(UserInfo);
if (save)
{
DbContext.swap_flow_merge.Add(swap_flow_summary);
}
list.Add(swap_flow_summary);
}
return list;
}
/// <summary>
/// 汇总流水
/// </summary>
/// <param name="valueDate"></param>
public List<swap_flow_merge> SummaryFlow(List<SwapFlowDeal> swapFlows)
{
var swapFlowGroup = swapFlows.GroupBy(g => new { g.ClientId, g.OccurDate, g.UnderlyingCode, g.BsType }).ToList();
List<swap_flow_merge> list = new List<swap_flow_merge>();
foreach (var gourpItem in swapFlowGroup)
{
var flowList = gourpItem.OrderBy(O => O.OptTime).ToList();
var swapflow = flowList.First();
swap_flow_merge swap_flow_summary = new swap_flow_merge()
{
OccurTime = swapflow.OccurDate,
SwapTradeId = swapflow.SwapTradeId,
SwapTradeNo = swapflow.SwapTradeNo,
UnderlyingCode = swapflow.UnderlyingCode,
BsType = swapflow.BsType,
TradingQty = gourpItem.Sum(s => s.TradingQty),
TradingFeePending = gourpItem.Sum(s => s.TradingFee),
DataState = (int)SwapFlowDateStateEnum.等待完成,
ContractSize = swapflow.ContractSize,
ClientId = swapflow.ClientId,
};
int tradeSide = swap_flow_summary.BsType == (int)EnumDirection.Long ? 1 : -1;
swap_flow_summary.FirstFlowTime = swapflow.OptTime;
swap_flow_summary.SettleDate = gourpItem.Max(s => s.SettleDate);
swap_flow_summary.TradingAmount = swap_flow_summary.TradingQty * swap_flow_summary.ContractSize;
swap_flow_summary.TradingAmountAvg = swap_flow_summary.TradingQty == 0 ? 0 : gourpItem.Sum(s => s.FullPrice * s.TradingQty) / swap_flow_summary.TradingQty;
swap_flow_summary.TradingAmountFeeAvg = swap_flow_summary.TradingQty == 0 ? swap_flow_summary.TradingAmountAvg : swap_flow_summary.TradingAmountAvg + swap_flow_summary.TradingFeePending * tradeSide / swap_flow_summary.TradingQty;
swap_flow_summary.TradingAmountNetAvg = swap_flow_summary.TradingQty == 0 ? 0 : gourpItem.Sum(s => s.NetPrice * s.TradingQty) / swap_flow_summary.TradingQty;
swap_flow_summary.TradingAmountNetFeeAvg = swap_flow_summary.TradingQty == 0 ? swap_flow_summary.TradingAmountNetAvg : swap_flow_summary.TradingAmountNetAvg + swap_flow_summary.TradingFeePending * tradeSide / swap_flow_summary.TradingQty;
swap_flow_summary.SetOpt(UserInfo);
DbContext.swap_flow_merge.Add(swap_flow_summary);
list.Add(swap_flow_summary);
}
return list;
}
/// <summary>
/// 重置法生成开平仓事件
/// </summary>
/// <param name="valueDate"></param>
public void MergeRestModeCompose(List<swap_flow_merge> mergeList, DateTime valueDate, Action<int>? action)
{
var flowquery = mergeList.GroupBy(g => g.ClientId);
var flowCount = flowquery.Count();
if (flowCount == 0)
{
return;
}
var swaptrades = DbContext.trade.Where(t => t.TradeType == "收益互换" && t.StructureType == "普通债券类收益互换"
&& t.TradeDate <= valueDate
&& t.ValidState != ConsGlobal.InValid
&& !ConsTrade.TradeCompleteStatus.Contains(t.TradeStatus)).ToList();
var swapTradeIds = swaptrades.Select(s => s.id);
var tradeExtends = DbContext.trade_extend.Where(x => swapTradeIds.Contains(x.TradeId));
var clientMarginTemplates = DbContext.client_marginrate.Where(x => x.Type == "品种" && x.ValueDate <= valueDate).OrderByDescending(o => o.ValueDate).ToList();
var restSwapTrades = new List<trade>();
foreach (var swaptrade in swaptrades)
{
swaptrade.trade_extend = tradeExtends.FirstOrDefault(x => x.TradeId == swaptrade.id);
if (swaptrade.trade_extend?.ExtendObj.FlowBookMode == (int)FlowBookModeEnum.重置)
{
restSwapTrades.Add(swaptrade);
}
}
var swapPositions = DbContext.swap_position.Where(x => swapTradeIds.Contains(x.SwapTradeId) && !x.IsInitial && x.PosiQuantity > 0 && !x.Invalid && x.PosiDirection == (int)SwapDirectionEnum.支付).ToList();
var matuirityDate = QdpCalendarHelper.GetNonHolidayDefore(valueDate.AddDays(14));
var floatRatePredicate = PredicateBuilder.Create<SwapFloatRate>(x => x.StartDate <= valueDate && x.EndDate >= matuirityDate);
var floatRateQuery = DbContext.swap_float_rate.Where(floatRatePredicate);
int dealCount = 0;
foreach (var groupItem in flowquery)
{
MergeRestModelItem(groupItem, restSwapTrades, swapPositions, floatRateQuery, clientMarginTemplates, ref dealCount, action);
}
}
/// <summary>
/// 加权平均法生成开平仓事件
/// </summary>
/// <param name="mergeList"></param>
/// <param name="valueDate"></param>
public void MergeAvgModeCompose(List<swap_flow> swapFlows, DateTime valueDate, Action<int>? action)
{
var flowquery = swapFlows.GroupBy(g => g.ClientId);
var flowCount = flowquery.Count();
if (flowCount == 0)
{
return;
}
var swaptrades = DbContext.trade.Where(t => t.StructureType == "定义文件型债券收益互换"
&& t.TradeDate <= valueDate
&& t.ValidState != ConsGlobal.InValid
&& !ConsTrade.TradeCompleteStatus.Contains(t.TradeStatus)).ToList();
var swapTradeIds = swaptrades.Select(s => s.id);
var swapPositions = DbContext.swap_position.Where(x => swapTradeIds.Contains(x.SwapTradeId) && x.PosiDirection > 0 && !x.IsInitial && x.PosiQuantity > 0 && !x.Invalid).ToList();
var matuirityDate = QdpCalendarHelper.GetNonHolidayDefore(valueDate.AddDays(14));
var floatRatePredicate = PredicateBuilder.Create<SwapFloatRate>(x => x.StartDate <= valueDate && x.EndDate >= matuirityDate);
var floatRateQuery = DbContext.swap_float_rate.Where(floatRatePredicate);
int dealCount = 0;
foreach (var groupItem in flowquery)
{
MergeAvgModelItem(groupItem, swaptrades, swapPositions, floatRateQuery, ref dealCount, action);
}
}
public void UpdateSwapFlowState(List<swap_flow> swapFlows)
{
foreach (var item in swapFlows)
{
item.DataState = (int)SwapFlowDateStateEnum.完成;
}
DbContext.SaveChanges();
}
/// <summary>
/// 重置法按客户合成持仓
/// </summary>
/// <param name="groupItem"></param>
/// <param name="swaptrades"></param>
/// <param name="swapPositions"></param>
/// <param name="floatRateQuery"></param>
/// <param name="direction"></param>
/// <param name="matuirityDate"></param>
/// <exception cref="ServiceException"></exception>
private void MergeRestModelItem(IGrouping<int?, swap_flow_merge> groupItem,
List<trade> swaptrades,
List<swap_position> swapPositions,
IQueryable<SwapFloatRate> floatRateQuery,
List<client_marginrate> client_Marginrates, ref int dealCount, Action<int>? action)
{
var clientId = groupItem.Key;
var client = DataCacheProvider.GetClientDataSource().GetData(clientId ?? 0);
if (client == null)
{
throw new ServiceException($"找不到id为{clientId}的客户信息");
}
if (!client.DerivativesInvestmentVarieties.Contains((int)DerivativesInvestmentVarietiesEnum.场外互换 + ""))
{
throw new ServiceException($"客户:{client.Name}未设置交易种类“场外互换”,无法生成互换交易!");
}
var etradeRule = new EtradingRuleService(UserInfo).GetEtradingRuleAccont(client.BoundSide, client.Number);
if (etradeRule == null || string.IsNullOrEmpty(etradeRule.AssetAccount_0))
{
throw new ServiceException($"{client.Number}未设置TRS对客簿记账户");
}
string clearingAgency = etradeRule.ClearingAgency_0;
var asset = DataCacheProvider.GetAssetUnitDataSource().AsQueryable(x => x.Name == etradeRule.AssetAccount_0).FirstOrDefault();//取对客簿记账户
if (asset == null)
{
throw new ServiceException($"找不到名为{etradeRule.AssetAccount_0}的簿记账户信息");
}
if (asset.TraderIdsInt.Count == 0)
{
throw new ServiceException($"{etradeRule.AssetAccount_0}的簿记账户未设置交易员");
}
var clientMarginTemplate = client_Marginrates.FirstOrDefault(x => x.ClientId == clientId);
if (clientMarginTemplate == null)
{
clientMarginTemplate = client_Marginrates.FirstOrDefault(x => x.ClientId == 0);
}
var underlyingGroup = groupItem.GroupBy(g => g.UnderlyingCode);
var clientSwapTrades = swaptrades.Where(x => x.ClientId == clientId).ToList();
var clientSwapTradeIds = clientSwapTrades.Select(s => s.id);
var clientSwapPositions = swapPositions.Where(x => clientSwapTradeIds.Contains(x.SwapTradeId));//现有客户持仓
var underlyingCodes = underlyingGroup.Select(s => s.Key).ToList();
var underlyings = DataCacheProvider.GetUnderlyingDataSource().AsQueryable(x => underlyingCodes.Contains(x.UnderlyingCode));
foreach (var underlyingGroupItem in underlyingGroup)
{
var underlyingCode = underlyingGroupItem.Key;
var underlying = underlyings.FirstOrDefault(x => x.UnderlyingCode == underlyingCode);
if (underlying == null)
{
throw new ServiceException($"找不到标的代码为{underlyingCode}的标的信息");
}
var floatRate = new SwapFloatRateService(UserInfo).GetSwapFloatRate(floatRateQuery, clientId ?? 0, underlyingCode);
var clientSwapPositionList = clientSwapPositions.Where(x => x.UnderlyingCode == underlyingCode).ToList();//现有标的持仓
var hasPayPosition = clientSwapPositionList.Any();
var mergeList = underlyingGroupItem.OrderByDescending(o => o.TradingQty).ToList();
var flowMerge = mergeList.First();
var flowMerge2 = mergeList.Last();
dealCount = dealCount + mergeList.Count();
action?.Invoke(dealCount);
flowMerge.DataState = 100;
flowMerge2.DataState = 100;
bool cashNeedAfter = false;//资金是否需要延后
if (mergeList.Count == 2)
{
var unwindQty = (flowMerge.TradingQty * (flowMerge.BsType == 1 ? 1 : -1)) + (flowMerge2.TradingQty * (flowMerge2.BsType == 1 ? 1 : -1));
if (unwindQty == 0)
{
cashNeedAfter = true;
}
}
if (cashNeedAfter)
{
var flowEvents = DbContext.swap_flow_event.Where(x => x.EventDate == flowMerge.OccurTime && x.PayDate > x.UnwindDate && x.EventType == (int)SwapFlowEventTypeEnum.平仓 && x.DataState == (int)SwapFlowDateStateEnum.完成 && x.ClientId == flowMerge.ClientId && x.PayDirection > 0);
var tradeIds = flowEvents.Select(s=>s.SwapTradeId).Distinct();
var trades = DbContext.trade.Where(x=> tradeIds.Contains(x.id)&&x.ValidState!=ConsGlobal.InValid);
cashNeedAfter = !trades.Any();
}
if (!hasPayPosition)//没有持仓
{
DealNoPosition(mergeList, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency, cashNeedAfter);
}
else
{
DealHasPosition(mergeList, client, asset, underlying, floatRate, clientSwapPositionList, clientSwapTrades, clientMarginTemplate, clearingAgency, cashNeedAfter);
}
}
DbContext.SaveChanges();
}
/// <summary>
/// 加权平均法按客户合成持仓
/// </summary>
/// <param name="groupItem"></param>
/// <param name="swaptrades"></param>
/// <param name="swapPositions"></param>
/// <param name="floatRateQuery"></param>
/// <param name="direction"></param>
/// <param name="matuirityDate"></param>
/// <exception cref="ServiceException"></exception>
private void MergeAvgModelItem(IGrouping<int?, swap_flow> groupItem,
List<trade> swaptrades,
List<swap_position> swapPositions,
IQueryable<SwapFloatRate> floatRateQuery, ref int dealCount, Action<int>? action)
{
var clientId = groupItem.Key;
var client = DataCacheProvider.GetClientDataSource().GetData(clientId ?? 0);
var etradeRule = new EtradingRuleService(UserInfo).GetEtradingRuleAccont(client.BoundSide, client.Number);
string clearingAgency = etradeRule.ClearingAgency_0;
var asset = DataCacheProvider.GetAssetUnitDataSource().AsQueryable(x => x.Name == etradeRule.AssetAccount_0).FirstOrDefault();//取对客簿记账户
var underlyingGroup = groupItem.GroupBy(g => g.UnderlyingCode);
var clientSwapTrades = swaptrades.Where(x => x.ClientId == clientId).ToList();
var clientSwapTradeIds = clientSwapTrades.Select(s => s.id);
var clientSwapPositions = swapPositions.Where(x => clientSwapTradeIds.Contains(x.SwapTradeId));//现有客户持仓
var underlyingCodes = underlyingGroup.Select(s => s.Key).ToList();
var underlyings = DataCacheProvider.GetUnderlyingDataSource().AsQueryable(x => underlyingCodes.Contains(x.UnderlyingCode));
foreach (var underlyingGroupItem in underlyingGroup)
{
var underlyingCode = underlyingGroupItem.Key;
var underlying = underlyings.FirstOrDefault(x => x.UnderlyingCode == underlyingCode);
if (underlying == null)
{
throw new ServiceException($"找不到标的代码为{underlyingCode}的标的信息");
}
var floatRate = new SwapFloatRateService(UserInfo).GetSwapFloatRate(floatRateQuery, clientId ?? 0, underlyingCode);
var clientSwapPositionList = clientSwapPositions.Where(x => x.UnderlyingCode == underlyingCode).ToList();//现有标的持仓
var hasPayPosition = clientSwapPositionList.Any();
var mergeList = underlyingGroupItem.OrderBy(o => o.OptTime).ToList();
dealCount = dealCount + mergeList.Count();
action?.Invoke(dealCount);
if (!hasPayPosition)//没有持仓
{
var bsType = mergeList.OrderBy(o => o.OptTime).First().BsType;
AvgDealNoPosition(mergeList, client, asset, underlying, floatRate, clearingAgency, bsType);
}
else
{
AvgDealHasPosition(mergeList, client, asset, underlying, floatRate, clientSwapPositionList, clientSwapTrades, clearingAgency);
}
}
DbContext.SaveChanges();
}
/// <summary>
/// 将上日没有持仓的流水自动簿记
/// </summary>
/// <param name="mergeList"></param>
/// <param name="matuirityDate"></param>
/// <param name="client"></param>
/// <param name="asset"></param>
/// <param name="underlying"></param>
/// <param name="floatRate"></param>
private void DealNoPosition(List<swap_flow_merge> mergeList,
Client client,
AssetUnit asset,
underlying_manager underlying,
SwapFloatRate floatRate,
client_marginrate clientMarginTemplate,
string clearingAgency,
bool cashNeedAfter)
{
var mergeOrderList = mergeList.OrderBy(o => o.FirstFlowTime);
swap_flow_merge flowMergeMax = mergeOrderList.First();//先开最早的一条
swap_flow_merge flowMergeMin = mergeOrderList.Last();
var swapTradeService = new SwapTradeService(UserInfo);
var trade = swapTradeService.NewSwapTrade(flowMergeMax, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency, cashNeedAfter: cashNeedAfter);
flowMergeMax.SwapTradeNo = trade.TradeNumber;
flowMergeMin.SwapTradeNo = trade.TradeNumber;
if (mergeList.Count == 2)//有两条流水
{
var qty = flowMergeMax.TradingQtyAbs - flowMergeMin.TradingQtyAbs;//平仓剩余数量
new SwapDealService(UserInfo).AuotoSwapUnwind(trade.id,
flowMergeMin.TradingAmountAvg,
flowMergeMin.TradingAmountFeeAvg,
flowMergeMin.TradingAmountNetFeeAvg ?? 0,
flowMergeMin.TradingAmountNetAvg ?? 0,
flowMergeMin.OccurTime,
flowMergeMax.TradingQtyAbs,
flowMergeMin.TradingQty,
flowMergeMin.TradingFeePending);
var amount = qty * flowMergeMax.ContractSize;//平仓剩余金额=(平仓流水的成交均价-平仓对象的期初价格不含费)*平仓流水的成交数量*合约乘数
if (qty != 0)//平仓有剩余,开仓
{
var qtyAbs = Math.Abs(qty);
var flowMergeClone = flowMergeMax.Clone();
if (qty < 0)
{
flowMergeClone = flowMergeMin.Clone();
}
flowMergeClone.TradingAmount = Math.Abs(amount);
flowMergeClone.TradingFeePending = flowMergeClone.TradingFeePending * qtyAbs / flowMergeClone.TradingQty;//剩余后付费用
flowMergeClone.TradingQty = qtyAbs;
if (qty > 0)//交易有剩余新开仓
{
var posi = DbContext.swap_position.FirstOrDefault(x => x.SwapTradeId == trade.id && x.PosiDirection > 0 && !x.IsInitial);
SetNewOpenData(flowMergeMin, flowMergeClone, posi);
}
var trade2 = swapTradeService.NewSwapTrade(flowMergeClone, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency);
flowMergeMax.SwapTradeNo = trade2.TradeNumber;
flowMergeMin.SwapTradeNo = trade2.TradeNumber;
}
}
}
/// <summary>
/// 将上日没有持仓的流水自动簿记
/// </summary>
/// <param name="swapFlows"></param>
/// <param name="matuirityDate"></param>
/// <param name="client"></param>
/// <param name="asset"></param>
/// <param name="underlying"></param>
/// <param name="floatRate"></param>
private trade AvgDealNoPosition(List<swap_flow> swapFlows,
Client client,
AssetUnit asset,
underlying_manager underlying,
SwapFloatRate floatRate,
string clearingAgency,
int byType)
{
var negativeFlows = swapFlows.Where(x => x.BsType != byType).OrderBy(o => o.OptTime).ToList();
var sameFlows = swapFlows.Where(x => x.BsType == byType).OrderBy(o => o.OptTime).ToList();
if (negativeFlows.Count == 0)
{
return DealOneDirectionFlows(sameFlows, client, asset, underlying, floatRate, clearingAgency);
}
return DealTwoDirectionFlows(sameFlows, negativeFlows, client, asset, underlying, floatRate, clearingAgency);
}
/// <summary>
/// 当前无持仓,且只有一个方向的流水合成簿记
/// </summary>
/// <param name="sameFlows"></param>
/// <param name="client"></param>
/// <param name="asset"></param>
/// <param name="underlying"></param>
/// <param name="floatRate"></param>
/// <param name="clearingAgency"></param>
private trade DealOneDirectionFlows(List<swap_flow> sameFlows,
Client client,
AssetUnit asset,
underlying_manager underlying,
SwapFloatRate floatRate,
string clearingAgency)
{
List<SwapFlowDeal> swapFlowDeals = new List<SwapFlowDeal>();
foreach (var flow in sameFlows)
{
SwapFlowDeal swapFlowDeal = GetSwapFlowDeal(flow, flow.TradingQty, (int)OpenCloseEnum.开仓, flow.TradingFee, null);
swapFlowDeals.Add(swapFlowDeal);
}
return NewSwapTrade(swapFlowDeals, client, asset, underlying, floatRate, clearingAgency);
}
/// <summary>
/// 当前无持仓,且有2个方向流水合成簿记
/// </summary>
/// <param name="sameFlows"></param>
/// <param name="negativeFlows"></param>
/// <param name="client"></param>
/// <param name="asset"></param>
/// <param name="underlying"></param>
/// <param name="floatRate"></param>
/// <param name="clearingAgency"></param>
private trade DealTwoDirectionFlows(List<swap_flow> sameFlows,
List<swap_flow> negativeFlows,
Client client,
AssetUnit asset,
underlying_manager underlying,
SwapFloatRate floatRate,
string clearingAgency)
{
var swapTradeService = new SwapTradeService(UserInfo);
var sameQty = sameFlows.Sum(s => s.TradingQty);
var negaQty = negativeFlows.Sum(s => s.TradingQty);
//第一次开仓流水拆分明细
List<SwapFlowDeal> openFirstFlows = new List<SwapFlowDeal>();
//第一次平仓流水拆分明细
List<SwapFlowDeal> unwindFirstFlows = new List<SwapFlowDeal>();
//第二次开仓流水拆分明细,最多可能存在第二次开仓
List<SwapFlowDeal> openLastFlows = new List<SwapFlowDeal>();
List<swap_flow> negaFlowClones = DataHelper.DeepCopyObject(negativeFlows);
List<swap_flow> sameFlowClones = DataHelper.DeepCopyObject(sameFlows);
unwindFirstFlows = GetRemainderFlows(negaFlowClones, sameQty, null);
var unwindQty = unwindFirstFlows.Sum(s => s.TradingQty);
//日内轧差全开全平
//同向总数量>=反向总数量,同向新开仓,反向流水全部平仓,同向剩余新开仓
if (sameQty >= negaQty)
{
openFirstFlows = GetRemainderFlows(sameFlowClones, negaQty, null);
openFirstFlows.ForEach(x => { x.OpenFlag = (int)OpenCloseEnum.开仓; });
var openQty = openFirstFlows.Sum(s => s.TradingQty);
if (openQty < negaQty)
{
var sameFlow = sameFlowClones.First();
var unwindFirstQty = negaQty - openQty;
var unwindFee = sameFlow.TradingFee * unwindFirstQty / sameFlow.TradingQty;
SwapFlowDeal firstOpenFlowDeal = GetSwapFlowDeal(sameFlow, unwindFirstQty, (int)OpenCloseEnum.开仓, unwindFee, null);
openFirstFlows.Add(firstOpenFlowDeal);
sameFlow.TradingQty = sameFlow.TradingQty - unwindFirstQty;
sameFlow.TradingFee = sameFlow.TradingFee - unwindFee;
sameFlow.TradingAmount = sameFlow.TradingQty;
}
//反向流水全部平仓
foreach (var negaFlow in negaFlowClones)
{
SwapFlowDeal swapFlowDeal = GetSwapFlowDeal(negaFlow, negaFlow.TradingQty, (int)OpenCloseEnum.平仓, negaFlow.TradingFee, null);
unwindFirstFlows.Add(swapFlowDeal);
}
foreach (var sameFlow in sameFlowClones)
{
SwapFlowDeal swapFlowDeal = GetSwapFlowDeal(sameFlow, sameFlow.TradingQty, (int)OpenCloseEnum.开仓, sameFlow.TradingFee, null);
openLastFlows.Add(swapFlowDeal);
}
}
else //同向总数量 < 反向总数量,同向先开仓,反向流水部分平仓,反向剩余新开仓
{
if (unwindQty < sameQty)
{
var negaFlow = negaFlowClones.First();
var unwindFirstQty = sameQty - unwindQty;
var unwindFee = negaFlow.TradingFee * unwindFirstQty / negaFlow.TradingQty;
SwapFlowDeal swapFirstFlowDeal = GetSwapFlowDeal(negaFlow, unwindFirstQty, (int)OpenCloseEnum.平仓, unwindFee, null);
unwindFirstFlows.Add(swapFirstFlowDeal);
negaFlow.TradingQty = negaFlow.TradingQty - unwindFirstQty;
negaFlow.TradingFee = negaFlow.TradingFee - unwindFee;
negaFlow.TradingAmount = negaFlow.TradingQty;
}
foreach (var flow in sameFlowClones)
{
SwapFlowDeal swapFlowDeal = GetSwapFlowDeal(flow, flow.TradingQty, (int)OpenCloseEnum.开仓, flow.TradingFee, null);
openFirstFlows.Add(swapFlowDeal);
}
foreach (var minFlow in negaFlowClones)
{
SwapFlowDeal swapFlowDeal = GetSwapFlowDeal(minFlow, minFlow.TradingQty, (int)OpenCloseEnum.开仓, minFlow.TradingFee, null);
openLastFlows.Add(swapFlowDeal);
}
}
var trade = NewSwapTrade(openFirstFlows, client, asset, underlying, floatRate, clearingAgency);
unwindFirstFlows.ForEach(x => { x.SwapTradeId = trade.id; x.SwapTradeNo = trade.TradeNumber; });
DbContext.swap_flow_deal.AddRange(unwindFirstFlows);
var mergeUnwindFlows = SummaryFlow(unwindFirstFlows);
var unwindMergeFlow = mergeUnwindFlows.First();
// 平仓
new SwapDealService(UserInfo).AuotoSwapUnwind(trade.id,
unwindMergeFlow.TradingAmountAvg,
unwindMergeFlow.TradingAmountFeeAvg,
unwindMergeFlow.TradingAmountNetFeeAvg ?? 0,
unwindMergeFlow.TradingAmountNetAvg ?? 0,
unwindMergeFlow.OccurTime,
unwindMergeFlow.TradingQty,
unwindMergeFlow.TradingQty,
unwindMergeFlow.TradingFeePending);
if (openLastFlows.Count > 0)
{
return NewSwapTrade(openLastFlows, client, asset, underlying, floatRate, clearingAgency);
}
return trade;
}
/// <summary>
/// 流水拆分后新开仓
/// </summary>
/// <param name="openFlows"></param>
/// <param name="client"></param>
/// <param name="asset"></param>
/// <param name="underlying"></param>
/// <param name="floatRate"></param>
/// <param name="clearingAgency"></param>
/// <returns></returns>
private trade NewSwapTrade(List<SwapFlowDeal> openFlows,
Client client,
AssetUnit asset,
underlying_manager underlying,
SwapFloatRate floatRate,
string clearingAgency)
{
var swapTradeService = new SwapTradeService(UserInfo);
var mergeFlows = SummaryFlow(openFlows);
swap_flow_merge flowMergeFirst = mergeFlows.First();
var trade = swapTradeService.NewSwapTrade(flowMergeFirst, client, asset, underlying, floatRate, null, clearingAgency, LongShortStructType);
flowMergeFirst.SwapTradeNo = trade.TradeNumber;
flowMergeFirst.SwapTradeId = trade.id;
openFlows.ForEach(x => { x.SwapTradeId = trade.id; x.SwapTradeNo = trade.TradeNumber; });
DbContext.swap_flow_deal.AddRange(openFlows);
DbContext.SaveChanges();
return trade;
}
/// <summary>
/// 获取剩余要开仓流水
/// </summary>
/// <param name="flows"></param>
/// <param name="unwindQty"></param>
private List<SwapFlowDeal> GetRemainderFlows(List<swap_flow> flows, decimal unwindQty, trade td)
{
decimal openQty = 0;
List<SwapFlowDeal> unwindFirstFlows = new List<SwapFlowDeal>();
for (int i = 0; i < flows.Count; i++)
{
var flow = flows[i];
openQty += flow.TradingQty;
if (openQty <= unwindQty)
{
SwapFlowDeal swapFlowDeal = GetSwapFlowDeal(flow, flow.TradingQty, (int)OpenCloseEnum.平仓, flow.TradingFee, td);
unwindFirstFlows.Add(swapFlowDeal);
flows.Remove(flow);
i--;
}
}
return unwindFirstFlows;
}
/// <summary>
/// 转换拆分流水
/// </summary>
/// <param name="swapFlow"></param>
/// <param name="qty"></param>
/// <param name="openFlag"></param>
/// <returns></returns>
private SwapFlowDeal GetSwapFlowDeal(swap_flow swapFlow, decimal qty, int openFlag, decimal fee, trade td)
{
SwapFlowDeal swapFlowDeal = new SwapFlowDeal();
swapFlowDeal.ContractSize = swapFlow.ContractSize;
swapFlowDeal.SetOpt(UserInfo);
swapFlowDeal.BsType = swapFlow.BsType;
swapFlowDeal.ClientId = swapFlow.ClientId ?? 0;
swapFlowDeal.ClientName = swapFlow.ClientName;
swapFlowDeal.FullPriceFee = swapFlow.TradingAmountFeeAvg;
swapFlowDeal.FullPrice = swapFlow.TradingAmountAvg;
swapFlowDeal.OccurDate = swapFlow.OccurTime ?? DateTime.Now.Date;
swapFlowDeal.HedgeDealType = swapFlow.DealType ?? 0;
swapFlowDeal.SettleDate = swapFlow.SettleDate;
swapFlowDeal.TrsDealId = swapFlow.trs_deal_id;
swapFlowDeal.FlowId = swapFlow.id;
swapFlowDeal.HedgeTime = swapFlow.OptTime;
swapFlowDeal.OpenFlag = openFlag;
swapFlowDeal.TradeingAmount = qty;
swapFlowDeal.TradingFee = fee;
swapFlowDeal.NetPrice = swapFlow.TradingAmountNet ?? 0;
swapFlowDeal.NetPriceFee = swapFlow.TradingAmountNetFee ?? 0;
swapFlowDeal.TradingQty = qty;
swapFlowDeal.UnderlyingCode = swapFlow.UnderlyingCode;
swapFlowDeal.UnderlyingName = swapFlow.UnderlyingName;
swapFlowDeal.Ytm = swapFlow.ytm;
if (td != null)
{
swapFlowDeal.SwapTradeId = td.id;
swapFlowDeal.SwapTradeNo = td.TradeNumber;
}
return swapFlowDeal;
}
/// <summary>
/// 将上日有持仓的流水自动簿记
/// </summary>
/// <param name="mergeList"></param>
/// <param name="matuirityDate"></param>
/// <param name="client"></param>
/// <param name="asset"></param>
/// <param name="underlying"></param>
/// <param name="floatRate"></param>
/// <param name="clientSwapPositionList"></param>
/// <param name="clientSwapTrades"></param>
private void DealHasPosition(List<swap_flow_merge> mergeList,
Client client,
AssetUnit asset,
underlying_manager underlying,
SwapFloatRate floatRate,
List<swap_position> clientSwapPositionList,
List<trade> clientSwapTrades,
client_marginrate clientMarginTemplate,
string clearingAgency,
bool cashNeedAfter)
{
if (mergeList.Count == 1)//只有一条流水情况
{
DealSingleFlow(mergeList, clientSwapPositionList, clientSwapTrades, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency);
}
else
{
DealDoubleFlow(mergeList, clientSwapPositionList, clientSwapTrades, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency, cashNeedAfter);
}
}
/// <summary>
/// 将上日有持仓的流水自动簿记
/// </summary>
/// <param name="flowList"></param>
/// <param name="matuirityDate"></param>
/// <param name="client"></param>
/// <param name="asset"></param>
/// <param name="underlying"></param>
/// <param name="floatRate"></param>
/// <param name="clientSwapPositionList"></param>
/// <param name="clientSwapTrades"></param>
private void AvgDealHasPosition(List<swap_flow> flowList,
Client client,
AssetUnit asset,
underlying_manager underlying,
SwapFloatRate floatRate,
List<swap_position> clientSwapPositionList,
List<trade> clientSwapTrades,
string clearingAgency)
{
var firstFlow = flowList.First();
var negaFlowExist = flowList.Any(x => x.BsType != firstFlow.BsType);
if (!negaFlowExist)//只有同向流水情况
{
AvgDealSingleFlow(flowList, clientSwapPositionList, clientSwapTrades, client, asset, underlying, floatRate, clearingAgency);
}
else
{
AvgDealDoubleFlow(flowList, clientSwapPositionList, clientSwapTrades, client, asset, underlying, floatRate, clearingAgency);
}
}
/// <summary>
/// 处理单条流水情况
/// </summary>
/// <param name="mergeList"></param>
/// <param name="clientSwapPositionList"></param>
/// <param name="clientSwapTrades"></param>
/// <param name="matuirityDate"></param>
/// <param name="client"></param>
/// <param name="asset"></param>
/// <param name="underlying"></param>
/// <param name="floatRate"></param>
private void DealSingleFlow(List<swap_flow_merge> mergeList,
List<swap_position> clientSwapPositionList,
List<trade> clientSwapTrades,
Client client,
AssetUnit asset,
underlying_manager underlying,
SwapFloatRate floatRate,
client_marginrate clientMarginTemplate,
string clearingAgency)
{
var swapTradeService = new SwapTradeService(UserInfo);
swap_flow_merge flowMergeMax = mergeList.First();
swap_flow_merge flowMergeMin = mergeList.Last();
var negativeDirectionPositions = clientSwapPositionList.Where(x => x.PositionType != flowMergeMax.BsType).ToList();//查找反方向交易
var sameDirectionPositions = clientSwapPositionList.Where(x => x.PositionType == flowMergeMax.BsType).ToList();//查找同方向交易
var tradeIds = clientSwapPositionList.Select(x => x.SwapTradeId).ToList();
var sameTradeIds = sameDirectionPositions.Select(x => x.SwapTradeId).ToList();
var sameTrades = clientSwapTrades.Where(x => sameTradeIds.Contains(x.id) && x.TradeDate == flowMergeMax.OccurTime).ToList();//只处理当前清算日期的交易
if (negativeDirectionPositions.Any())//存在反方向交易
{
List<int> unwindTradeIds = new List<int>();
var negativeTradeIds = negativeDirectionPositions.Select(x => x.SwapTradeId).ToList();
var negativeTrades = clientSwapTrades.Where(x => negativeTradeIds.Contains(x.id)).OrderBy(o => o.TradeDate).ToList();
var flowMergeClone = flowMergeMax.Clone();
flowMergeMax.SwapTradeNo = flowMergeClone.SwapTradeNo;
var dealResult = DealNegativeTrade(negativeTrades, flowMergeClone, negativeDirectionPositions, unwindTradeIds, true, false);
if (dealResult.Item3)// 处理完有开仓需求
{
flowMergeClone.TradingAmount = dealResult.Item1;
flowMergeClone.TradingQty = dealResult.Item2;
if (flowMergeClone.BsType != flowMergeMax.BsType)//交易有剩余新开仓
{
SetNewOpenData(flowMergeMax, flowMergeClone, dealResult.Item4);
}
var trade = swapTradeService.NewSwapTrade(flowMergeClone, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency);
flowMergeMax.SwapTradeNo = trade.TradeNumber;
}
}
else //只存在同向交易
{
var trade = swapTradeService.NewSwapTrade(flowMergeMax, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency);
flowMergeMax.SwapTradeNo = trade.TradeNumber;
}
}
/// <summary>
/// 交易平完有剩余重置法新开仓算价格等数据
/// </summary>
/// <param name="origin"></param>
/// <param name="flowMergeClone"></param>
/// <param name="position"></param>
private void SetNewOpenData(swap_flow_merge origin, swap_flow_merge flowMergeClone, swap_position position)
{
if (position == null)
{
return;
}
var ratio = flowMergeClone.BsType == 1 ? 1 : -1;
var oriRatio = flowMergeClone.BsType == 1 ? -1 : 1;
flowMergeClone.TradingFeePending = flowMergeClone.TradingQty / origin.TradingQty * origin.TradingFeePending;
flowMergeClone.TradingAmountAvg = origin.TradingAmountAvg + oriRatio * origin.TradingFeePending * 2 / origin.TradingQty;
flowMergeClone.TradingAmountNetAvg = origin.TradingAmountNetAvg + oriRatio * origin.TradingFeePending * 2 / origin.TradingQty;
flowMergeClone.TradingAmountFeeAvg = flowMergeClone.TradingAmountAvg + ratio * flowMergeClone.TradingFeePending / flowMergeClone.TradingQty;
flowMergeClone.TradingAmountNetFeeAvg = flowMergeClone.TradingAmountNetAvg + ratio * flowMergeClone.TradingFeePending / flowMergeClone.TradingQty;
}
/// <summary>
/// 加权平均处理单条流水情况
/// </summary>
/// <param name="flowList"></param>
/// <param name="clientSwapPositionList"></param>
/// <param name="clientSwapTrades"></param>
/// <param name="matuirityDate"></param>
/// <param name="client"></param>
/// <param name="asset"></param>
/// <param name="underlying"></param>
/// <param name="floatRate"></param>
private void AvgDealSingleFlow(List<swap_flow> flowList,
List<swap_position> clientSwapPositionList,
List<trade> clientSwapTrades,
Client client,
AssetUnit asset,
underlying_manager underlying,
SwapFloatRate floatRate,
string clearingAgency)
{
swap_flow flowMergeMax = flowList.First();
var negaSwapPositions = clientSwapPositionList.Where(x => x.PositionType != flowMergeMax.BsType).ToList();
List<swap_flow> flowClones = DataHelper.DeepCopyObject(flowList);
if (negaSwapPositions.Count > 0)//流水与持仓反向
{
AvgDealUnwind(flowClones, clientSwapTrades, negaSwapPositions, client, asset, underlying, floatRate, clearingAgency,true);
}
else //只存在同向交易,同向流水合并新开仓
{
DealOneDirectionFlows(flowClones, client, asset, underlying, floatRate, clearingAgency);
}
}
/// <summary>
/// 处理多条流水情况
/// </summary>
/// <param name="mergeList"></param>
/// <param name="clientSwapPositionList"></param>
/// <param name="clientSwapTrades"></param>
/// <param name="matuirityDate"></param>
/// <param name="client"></param>
/// <param name="asset"></param>
/// <param name="underlying"></param>
/// <param name="floatRate"></param>
public void DealDoubleFlow(List<swap_flow_merge> mergeList,
List<swap_position> clientSwapPositionList,
List<trade> clientSwapTrades,
Client client,
AssetUnit asset,
underlying_manager underlying,
SwapFloatRate floatRate,
client_marginrate clientMarginTemplate,
string clearingAgency,
bool cashNeedAfter)
{
var swapTradeService = new SwapTradeService(UserInfo);
mergeList = mergeList.OrderBy(o => o.FirstFlowTime).ToList();
var flowMergeFirst = mergeList.First();
var flowMergeLast = mergeList.Last();
var flowMergeFirstClone = flowMergeFirst.Clone();
var flowMergeLastClone = flowMergeLast.Clone();
var sameDirectionPositions = clientSwapPositionList.Where(x => x.PositionType == flowMergeFirstClone.BsType).ToList();
var negDirectionPositions = clientSwapPositionList.Where(x => x.PositionType == flowMergeLastClone.BsType).ToList();
var sameTradeIds = sameDirectionPositions.Select(x => x.SwapTradeId).ToList();
var sameTrades = clientSwapTrades.Where(x => sameTradeIds.Contains(x.id)).ToList();//取出与第一条流水方向相同的交易
var negTradeIds = negDirectionPositions.Select(x => x.SwapTradeId).ToList();
var negTrades = clientSwapTrades.Where(x => negTradeIds.Contains(x.id)).ToList();//取出与第一条流水方向相反的交易
//先处理第一条流水的反向持仓
var firstTrade = DealDoubleFlowDetial(negTrades, negDirectionPositions, flowMergeFirstClone, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency, true, cashNeedAfter);
flowMergeFirst.SwapTradeNo = flowMergeFirstClone.SwapTradeNo;
//再处理第二条流水的反向持仓
var lastTrade = DealDoubleFlowDetial(sameTrades, sameDirectionPositions, flowMergeLastClone, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency, false,false);
flowMergeLast.SwapTradeNo = flowMergeLastClone.SwapTradeNo;
if (flowMergeFirstClone.BsType != flowMergeLastClone.BsType && firstTrade != null)
{
var trades = new List<trade> { firstTrade };
var positions = DbContext.swap_position.Where(x => x.SwapTradeId == firstTrade.id && x.PosiDirection > 0 && !x.IsInitial && !x.Invalid).ToList();
DealDoubleFlowDetial(trades, positions, flowMergeLastClone, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency, true,false);
flowMergeLast.SwapTradeNo = flowMergeLastClone.SwapTradeNo;
}
else if (lastTrade==null)
{
swapTradeService.NewSwapTrade(flowMergeLastClone, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency);
}
}
/// <summary>
/// 加权平均处理当前有持仓,切两个方向多条流水情况
/// </summary>
/// <param name="flowList"></param>
/// <param name="clientSwapPositionList"></param>
/// <param name="clientSwapTrades"></param>
/// <param name="matuirityDate"></param>
/// <param name="client"></param>
/// <param name="asset"></param>
/// <param name="underlying"></param>
/// <param name="floatRate"></param>
public void AvgDealDoubleFlow(List<swap_flow> flowList,
List<swap_position> clientSwapPositionList,
List<trade> clientSwapTrades,
Client client,
AssetUnit asset,
underlying_manager underlying,
SwapFloatRate floatRate,
string clearingAgency)
{
var swapTradeService = new SwapTradeService(UserInfo);
var firstPosi = clientSwapPositionList.First();
var swapPositions = clientSwapPositionList.Where(x => x.PositionType == firstPosi.PositionType).ToList();
var posiQty = swapPositions.Sum(s => s.PosiQuantity);
var flowSames = flowList.Where(x => x.BsType == firstPosi.PositionType).ToList();
var flowNegs = flowList.Where(x => x.BsType != firstPosi.PositionType).ToList();
var negaBsType= flowNegs.First().BsType;
var sameQty = posiQty + flowSames.Sum(s => s.TradingQty);
var flowSameClones = DataHelper.DeepCopyObject(flowSames);
var flowNegClones = DataHelper.DeepCopyObject(flowNegs);
//先平反向
AvgDealUnwind(flowNegClones, clientSwapTrades, swapPositions, client, asset, underlying, floatRate, clearingAgency,false);
var newFlowList = new List<swap_flow>();
var openBystype = firstPosi.PositionType;
if (flowNegClones.Count > 0)
{
newFlowList.AddRange(flowNegClones);
openBystype = negaBsType;
}
newFlowList.AddRange(flowSameClones);
//有历史持仓按反方向先开仓
AvgDealNoPosition(newFlowList, client, asset, underlying, floatRate, clearingAgency, openBystype);
}
/// <summary>
/// 有持仓流水
/// </summary>
/// <param name="swapFlows"></param>
/// <param name="trades"></param>
/// <param name="swapPositions"></param>
/// <param name="client"></param>
/// <param name="asset"></param>
/// <param name="underlying"></param>
/// <param name="floatRate"></param>
/// <param name="clearingAgency"></param>
private void AvgDealUnwind(List<swap_flow> swapFlows,
List<trade> trades,
List<swap_position> swapPositions,
Client client,
AssetUnit asset,
underlying_manager underlying,
SwapFloatRate floatRate,
string clearingAgency,
bool needAdd)
{
var swapTradeService = new SwapTradeService(UserInfo);
foreach (var posi in swapPositions)
{
if (swapFlows.Count == 0)
{
break;
}
var td = trades.FirstOrDefault(p => p.id == posi.SwapTradeId);
if (td != null)
{
var posiQty = posi.PosiQuantity;
var flowQty = swapFlows.Sum(s => s.TradingQty);
var unwindSwapFlowDeals = GetRemainderFlows(swapFlows, posiQty, td);
var unwindQty = unwindSwapFlowDeals.Sum(x => x.TradingQty);
if (unwindQty < posiQty && swapFlows.Count > 0)
{
var unwindSwapFlow = swapFlows.First();
var flowUnindQty = posiQty - unwindQty;
var unwindFee = unwindSwapFlow.TradingQty == 0 ? 0 : unwindSwapFlow.TradingFee * flowUnindQty / unwindSwapFlow.TradingQty;
unwindSwapFlow.TradingQty = unwindSwapFlow.TradingQty - flowUnindQty;
unwindSwapFlow.TradingFee = unwindSwapFlow.TradingFee - unwindFee;
unwindSwapFlow.TradingAmount = unwindSwapFlow.TradingQty;
var unwindFlowDeal = GetSwapFlowDeal(unwindSwapFlow, flowUnindQty, (int)OpenCloseEnum.平仓, unwindFee, td);
unwindSwapFlowDeals.Add(unwindFlowDeal);
}
DbContext.swap_flow_deal.AddRange(unwindSwapFlowDeals);
var unwindMergeFlow = SummaryFlow(unwindSwapFlowDeals).First();
// 平仓
new SwapDealService(UserInfo).AuotoSwapUnwind(td.id,
unwindMergeFlow.TradingAmountAvg,
unwindMergeFlow.TradingAmountFeeAvg,
unwindMergeFlow.TradingAmountNetFeeAvg ?? 0,
unwindMergeFlow.TradingAmountNetAvg ?? 0,
unwindMergeFlow.OccurTime,
unwindMergeFlow.TradingQty,
posiQty,
unwindMergeFlow.TradingFeePending);
}
}
if (swapFlows.Count > 0 && needAdd)
{
var bsType = swapFlows.First().BsType;
AvgDealNoPosition(swapFlows, client, asset, underlying, floatRate, clearingAgency, bsType);
}
}
/// <summary>
/// 2条流水处理明细
/// </summary>
/// <param name="maxTrades"></param>
/// <param name="minTrades"></param>
/// <param name="clientSwapPositionList"></param>
/// <param name="flowMergeMax"></param>
/// <param name="flowMergeMin"></param>
/// <param name="matuirityDate"></param>
/// <param name="client"></param>
/// <param name="asset"></param>
/// <param name="underlying"></param>
/// <param name="floatRate"></param>
private trade DealDoubleFlowDetial(List<trade> negTrades,
List<swap_position> negDirectionPositions,
swap_flow_merge flowMergeSameClone,
Client client,
AssetUnit asset,
underlying_manager underlying,
SwapFloatRate floatRate,
client_marginrate clientMarginTemplate,
string clearingAgency,
bool needOpen,
bool cashNeedAfter
)
{
var swapTradeService = new SwapTradeService(UserInfo);
var flowMergeMax = flowMergeSameClone.Clone();
List<int> unwindTradeIds = new List<int>();
var dealResult = DealNegativeTrade(negTrades, flowMergeSameClone, negDirectionPositions, unwindTradeIds, needOpen, cashNeedAfter);
if (dealResult.Item3)// 处理完有开仓需求
{
flowMergeSameClone.TradingAmount = dealResult.Item1;
flowMergeSameClone.TradingQty = dealResult.Item2;
if (flowMergeSameClone.BsType != flowMergeMax.BsType)//交易有剩余新开仓
{
SetNewOpenData(flowMergeMax, flowMergeSameClone, dealResult.Item4);
}
return swapTradeService.NewSwapTrade(flowMergeSameClone, client, asset, underlying, floatRate, clientMarginTemplate, clearingAgency);
}
return null;
}
/// <summary>
/// 处理反方向流水簿记
/// </summary>
/// <param name="negativeTrades"></param>
/// <param name="flowMerge"></param>
/// <param name="floatPositions"></param>
/// <param name="matuirityDate"></param>
/// <param name="client"></param>
/// <param name="asset"></param>
/// <param name="underlying"></param>
/// <param name="floatRate"></param>
private (decimal, decimal, bool, swap_position) DealNegativeTrade(
List<trade> negativeTrades,
swap_flow_merge flowMerge,
List<swap_position> floatPositions,
List<int> unwindTradeIds,
bool needOpen,
bool cashNeedAfter
)
{
if (negativeTrades.Count == 0)
{
return (flowMerge.TradingAmount, flowMerge.TradingQty, needOpen, null);
}
var swapTradeService = new SwapTradeService(UserInfo);
List<trade> cloneNegativeTrades = new List<trade>(negativeTrades);
var first = true;
foreach (trade td in negativeTrades)
{
if (first&& cashNeedAfter)
{
cashNeedAfter = true;
}
else
{
cashNeedAfter = false;
}
var floatPosition = floatPositions.FirstOrDefault(x => x.SwapTradeId == td.id);
if (floatPosition == null)
{
cloneNegativeTrades.Remove(td);
if (cloneNegativeTrades.Count > 0)
{
return DealNegativeTrade(cloneNegativeTrades, flowMerge, floatPositions, unwindTradeIds, needOpen,false);//继续平下一个簿记
}
else //交易平完,流水有剩余
{
//新开
return (flowMerge.TradingAmount, flowMerge.TradingQty, needOpen, null);
}
}
//新开
var oldAmount = floatPosition.PosiNetPrice * floatPosition.PosiQuantity;
var newAmount = flowMerge.TradingAmountFeeAvg * flowMerge.TradingQty;
var newQty = floatPosition.PosiQuantity - flowMerge.TradingQtyAbs;
var newQtyAbs = Math.Abs(newQty);
flowMerge.SwapTradeNo = td.TradeNumber;
// 全平
new SwapDealService(UserInfo).AuotoSwapUnwind(td.id,
flowMerge.TradingAmountAvg,
flowMerge.TradingAmountFeeAvg,
flowMerge.TradingAmountNetFeeAvg ?? 0,
flowMerge.TradingAmountNetAvg ?? 0,
flowMerge.OccurTime,
floatPosition.PosiQuantity,
flowMerge.TradingQty,
flowMerge.TradingFeePending);
unwindTradeIds.Add(td.id);
flowMerge.TradingAmount = newQtyAbs * flowMerge.ContractSize;
if (newQty > 0)
{
flowMerge.TradingFeePending = floatPosition.PosiTradingFeePending * newQtyAbs / floatPosition.PosiQuantity;
}
else
{
flowMerge.TradingFeePending = flowMerge.TradingFeePending * newQtyAbs / flowMerge.TradingQty;
}
flowMerge.TradingQty = newQtyAbs;
if (newQty < 0)//交易不够平,继续平
{
cloneNegativeTrades.Remove(td);
if (cloneNegativeTrades.Count > 0)
{
return DealNegativeTrade(cloneNegativeTrades, flowMerge, floatPositions, unwindTradeIds, needOpen, false);//继续平下一个簿记
}
else //交易平完,流水有剩余
{
//新开
return (flowMerge.TradingAmount, flowMerge.TradingQty, needOpen, null);
}
}
else if (newQty > 0) //交易平完交易有剩余
{
flowMerge.BsType = flowMerge.BsType == 1 ? 2 : 1;
return (flowMerge.TradingAmount, flowMerge.TradingQty, true, floatPosition);
}
else //完全平仓
{
return (0, 0, false, null);
}
}
return (0, 0, false, null);
}
/// <summary>
/// 定时任务合成持仓
/// </summary>
/// <param name="flowList"></param>
/// <param name="swapPositions"></param>
/// <param name="clientPosition"></param>
/// <param name="multiplier"></param>
/// <param name="positionType"></param>
public void MergeSwapPositionAvg(List<swap_flow> flowList, List<swap_position> swapPositions, ClientPosition clientPosition, decimal multiplier, int positionType)
{
var hasPayPosition = swapPositions != null && swapPositions.Count > 0;
var posiQty = swapPositions == null ? 0 : swapPositions.Sum(x => x.PosiQuantity);
var flowSames = flowList.Where(x => x.BsType == positionType).OrderBy(o => o.OptTime).ToList();
var flowNegatives = flowList.Where(x => x.BsType != positionType).OrderBy(o => o.OptTime).ToList();
var sameQty = flowSames.Sum(s => s.TradingQty);
var negaQty = flowNegatives.Sum(s => s.TradingQty);
if (sameQty + posiQty < negaQty)
{
var negaPosiType = flowNegatives.First().BsType;
clientPosition.side = negaPosiType - 1;
}
var posiType = clientPosition.id == 0 ? 0 : clientPosition.side;
List<swap_flow> maxFlowClones = new List<swap_flow>(flowSames);
var mergeFlow = new swap_flow_merge();
if (hasPayPosition)//有日终持仓
{
var posiFlows = ConvertToSwapFlow(swapPositions, (int)clientPosition.client_id);
maxFlowClones.AddRange(posiFlows);
maxFlowClones = maxFlowClones.OrderBy(o => o.id).ToList();
sameQty = maxFlowClones.Sum(s => s.TradingQty);
}
var maxQty = sameQty;
var minQty = negaQty;
if (clientPosition.side + 1 != positionType)
{
maxQty = negaQty;
minQty = sameQty;
maxFlowClones = new List<swap_flow>(flowNegatives);
}
maxFlowClones.ForEach(x => { x.OccurTime = DateTime.Now.Date; });
var unwindDeals = GetRemainderFlows(maxFlowClones, minQty, null);
var unwindQty = unwindDeals.Sum(s => s.TradingQty);
if (minQty != 0 && unwindQty < maxQty)
{
var firstFlow = maxFlowClones.First();
var unwindFlowQty = minQty - unwindQty;
var unwindFee = firstFlow.TradingFee * unwindFlowQty / firstFlow.TradingQty;
firstFlow.TradingQty = firstFlow.TradingQty - unwindFlowQty;
firstFlow.TradingFee = firstFlow.TradingFee - unwindFee;
firstFlow.TradingAmount = firstFlow.TradingQty;
}
var mergeList = SummaryFlow(maxFlowClones, false);
mergeFlow = mergeList.Count>0? mergeList.First():new swap_flow_merge();
clientPosition.commission = mergeFlow.TradingFee;
clientPosition.position_qty = mergeFlow.TradingQty / 10000;
clientPosition.position_notional_principal = mergeFlow.TradingQty;
clientPosition.deal_full_price_avg = mergeFlow.TradingAmountAvg * multiplier;
clientPosition.deal_price_avg = mergeFlow.TradingAmountNetAvg * multiplier;
}
/// <summary>
/// 将持仓转换为流水
/// </summary>
/// <param name="positions"></param>
/// <param name="clientId"></param>
/// <returns></returns>
private List<swap_flow> ConvertToSwapFlow(List<swap_position> positions, int clientId)
{
List<swap_flow> flows = new List<swap_flow>();
foreach (var item in positions)
{
swap_flow flow = new swap_flow();
flow.TradingAmount = item.PosiNotionalValue;
flow.TradingAmountAvg = item.PosiGrossPrice;
flow.TradingAmountFeeAvg = item.PosiNetPrice;
flow.TradingAmountNet = item.PosiNetNoFeePrice;
flow.TradingAmountNetFee = item.PosiNetFeePrice;
flow.TradingFee = item.PosiTradingFeePending;
flow.TradingQty = item.PosiQuantity;
flow.UnderlyingCode = item.UnderlyingCode;
flow.OccurTime = DateTime.Now.Date;
flow.ContractSize = item.ContractSize;
flow.ClientId = clientId;
flow.BsType = item.PositionType;
flow.SettleDate = item.PosiStartDate;
flows.Add(flow);
}
return flows;
}
}
}