Files
zszq-trs/YLErpDAL/Modules/SwapModule/SwapTradeAutoService.cs
T
张名锐 bc961c3411 fix(swap): 修复互换交易中债券价格精度处理问题,扩充到11位计算
- 统一债券价格四舍五入规则,使用AwayFromZero模式
- 为债券类型添加特殊的精度处理逻辑
- 修复前端价格显示精度计算问题
- 优化价格存储精度控制,区分债券和其他产品类型
- 修复初始化YTM和净价精度处理问题
- 更新价格验证逻辑以支持动态精度设置
2026-07-23 11:06:50 +08:00

1459 lines
74 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using ClosedXML.Report.Utils;
using Confluent.Kafka;
using CsvHelper;
using Dapper;
using DocumentFormat.OpenXml.Drawing;
using DocumentFormat.OpenXml.Drawing.Charts;
using DocumentFormat.OpenXml.Spreadsheet;
using MoreLinq;
using Newtonsoft.Json;
using NPOI.SS.Formula.Functions;
using Org.BouncyCastle.Asn1.Ocsp;
using Qdp.ComputeServiceV2.Data.CommonModels.TradeInfos;
using Qdp.Foundation.Utilities;
using Qdp.Pricing.Base.Enums;
using Qdp.Pricing.Base.Implementations;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Text.Json;
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.SwapModule.Dto;
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)
{
}
#region 可测试化接缝(Seams)——借鉴 refactor-swap-event-testable 分支,override 可在测试中替换 DB/外部调用,生产代码行为不变
protected virtual DateTime GetNextBusinessDay(DateTime date)
=> QdpCalendarHelper.GetNonHoliday(date);
protected virtual DateTime GetNextBusinessDayBefore(DateTime date)
=> QdpCalendarHelper.GetNonHolidayDefore(date);
protected virtual CalBondResult CalculateBondYtm(string underlyingCode, decimal avgPrice, DateTime settleDate)
=> BondCalcHepler.BondCalcByDate(underlyingCode, avgPrice * ConsGlobal.bondShowPriceMultiple, settleDate.ToString("yyyy-MM-dd"));
protected virtual void PersistMerge(swap_flow_merge merge)
=> DbContext.swap_flow_merge.Add(merge);
protected virtual void SetModelOpt(DBModelBaseV2 model)
=> model.SetOpt(UserInfo);
protected virtual List<trade> FindActiveSwapTrades(DateTime valueDate)
=> DbContext.trade.Where(t => t.TradeType == "收益互换" && t.StructureType == "普通债券类收益互换"
&& t.TradeDate <= valueDate
&& t.ValidState != ConsGlobal.InValid
&& !ConsTrade.TradeCompleteStatus.Contains(t.TradeStatus)).ToList();
protected virtual List<trade_extend> FindTradeExtends(IEnumerable<int> tradeIds)
=> DbContext.trade_extend.Where(x => tradeIds.Contains(x.TradeId)).ToList();
protected virtual List<swap_position> FindActivePositions(IEnumerable<int> tradeIds, int posiDirection)
=> DbContext.swap_position.Where(x => tradeIds.Contains(x.SwapTradeId) && !x.IsInitial && x.PosiQuantity > 0 && !x.Invalid && x.PosiDirection == posiDirection).ToList();
protected virtual List<swap_position> FindActivePositionsAll(IEnumerable<int> tradeIds)
=> DbContext.swap_position.Where(x => tradeIds.Contains(x.SwapTradeId) && x.PosiDirection > 0 && !x.IsInitial && x.PosiQuantity > 0 && !x.Invalid).ToList();
protected virtual IQueryable<SwapFloatRate> QueryFloatRates(DateTime valueDate, DateTime matuirityDate)
=> DbContext.swap_float_rate.Where(x => x.StartDate <= valueDate && x.EndDate >= matuirityDate);
protected virtual Client FindClient(int clientId)
=> DataCacheProvider.GetClientDataSource().GetData(clientId);
protected virtual AssetUnit FindAssetUnit(string assetAccountName)
=> DataCacheProvider.GetAssetUnitDataSource().AsQueryable(x => x.Name == assetAccountName).FirstOrDefault();
protected virtual underlying_manager FindUnderlying(string underlyingCode)
=> DataCacheProvider.GetUnderlyingDataSource().AsQueryable(x => x.UnderlyingCode == underlyingCode).FirstOrDefault();
protected virtual EtradingRule GetEtradingRule(BoundSideEnum boundSide, string clientNumber)
=> new EtradingRuleService(UserInfo).GetEtradingRuleAccont(boundSide, clientNumber);
protected virtual SwapFloatRate GetSwapFloatRate(IQueryable<SwapFloatRate> query, int clientId, string underlyingCode)
=> new SwapFloatRateService(UserInfo).GetSwapFloatRate(query, clientId, underlyingCode);
protected virtual List<swap_flow_event> FindFlowEventsForCashCheck(swap_flow_merge flowMerge)
=> 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).ToList();
protected virtual List<trade> FindValidTrades(IEnumerable<int> tradeIds)
=> DbContext.trade.Where(x => tradeIds.Contains(x.id) && x.ValidState != ConsGlobal.InValid).ToList();
protected virtual void SaveChanges()
=> DbContext.SaveChanges();
protected virtual trade CreateNewSwapTrade(swap_flow_merge flowMerge, Client client, AssetUnit asset, underlying_manager underlying, SwapFloatRate floatRate, string clearingAgency, bool cashNeedAfter = false)
=> new SwapTradeService(UserInfo).NewSwapTrade(flowMerge, client, asset, underlying, floatRate, clearingAgency, cashNeedAfter: cashNeedAfter);
protected virtual void AutoSwapUnwind(int tradeId, decimal tradingAmountAvg, decimal tradingAmountFeeAvg, decimal tradingAmountNetFeeAvg, decimal tradingAmountNetAvg, DateTime occurTime, decimal tradingQtyAbs, decimal tradingFeePending)
=> new SwapDealService(UserInfo).AuotoSwapUnwind(tradeId, tradingAmountAvg, tradingAmountFeeAvg, tradingAmountNetFeeAvg, tradingAmountNetAvg, occurTime, tradingQtyAbs, tradingFeePending);
#endregion
/// <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);
swapFlow.TradingFee = Math.Round(swapFlow.TradingFee, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
if (swapFlow.id == 0 || reset)
{
swapFlow.SwapTradeId = null;
swapFlow.SwapTradeNo = null;
swapFlow.DataState = (int)SwapFlowDateStateEnum.等待完成;
}
// 债券报价(×100)转入库小数(×0.01),统一走 BondPriceConverter
swapFlow.TradingAmountAvg = Math.Round(BondPriceConverter.ToStorage(item.deal_full_price ?? 0), ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
swapFlow.TradingAmountFeeAvg = BondPriceConverter.ToStorage(item.deal_full_price_include_fee ?? 0);
swapFlow.TradingAmount = swapFlow.TradingQty * swapFlow.ContractSize * swapFlow.TradingAmountAvg;
swapFlow.ClientId = Convert.ToInt32(item.client_id ?? 0);
swapFlow.ytm = BondPriceConverter.ToStorage(item.ytm ?? 0);
swapFlow.TradingAmountNet = BondPriceConverter.ToStorage(item.deal_price ?? 0);
swapFlow.TradingAmountNetFee = BondPriceConverter.ToStorage(item.deal_price_include_fee ?? 0);
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 Dictionary<long, List<string>> 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();
Dictionary<long, List<string>> clientUmsDic = new Dictionary<long, List<string>>();
var groupByClientIdDic = swapFlows.ToList().GroupBy(x => x.ClientId).ToDictionary(x => x.Key, x => x.ToList());
foreach (var item in groupByClientIdDic)
{
var clientUms = item.Value.Select(x => x.UnderlyingCode).Distinct().ToList();
clientUmsDic.Add(item.Key ?? 0, clientUms);
}
return clientUmsDic;
}
/// <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>
/// 检查FR007数据
/// </summary>
/// <param name="req"></param>
/// <exception cref="ServiceException"></exception>
public void CheckFR007Data(DateTime valueDate)
{
var existFr007 = DbContext.eod_commodity_future_price.Any(s => s.ValueDate == valueDate && s.UnderlyingCode == "FR007");
if (!existFr007)
{
throw new ServiceException($"{valueDate.ToString("yyyy-MM-dd")}没有FR007数据不能进行簿记");
}
}
/// <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, DateTime valueDate)
{
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, DateTime valueDate, bool save = true, Action<string> callback = null)
{
DateTime settleDate = QdpCalendarHelper.GetNonHoliday(valueDate.AddDays(1));
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,
TradingAmount = gourpItem.Sum(s => s.TradingAmount),
};
int tradeSide = swap_flow_summary.BsType == (int)EnumDirection.Long ? 1 : -1;
swap_flow_summary.TradingFeePending = Math.Round(swap_flow_summary.TradingFeePending, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
swap_flow_summary.FirstFlowTime = swapflow.OptTime;
swap_flow_summary.SettleDate = gourpItem.Max(s => s.SettleDate);
swap_flow_summary.TradingAmountAvg = swap_flow_summary.TradingQty == 0 ? 0 : gourpItem.Sum(s => s.TradingAmountAvg * s.TradingQty) / swap_flow_summary.TradingQty;
swap_flow_summary.TradingAmountAvg = Math.Round(swap_flow_summary.TradingAmountAvg, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
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.TradingAmountFeeAvg = Math.Round(swap_flow_summary.TradingAmountFeeAvg, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
swap_flow_summary.TradingAmountNetAvg = swap_flow_summary.TradingQty == 0 ? 0 : gourpItem.Sum(s => s.TradingAmountNet * s.TradingQty) / swap_flow_summary.TradingQty;
swap_flow_summary.TradingAmountNetAvg = Math.Round(swap_flow_summary.TradingAmountNetAvg ?? 0, 10);
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.TradingAmountNetFeeAvg = Math.Round(swap_flow_summary.TradingAmountNetFeeAvg ?? 0, 10);
// 计算收益率
CalBondResult result = BondCalcHepler.BondCalcByDate(gourpItem.Key.UnderlyingCode, swap_flow_summary.TradingAmountAvg * ConsGlobal.bondShowPriceMultiple, settleDate.ToString("yyyy-MM-dd"));
if (result != null)
{
swap_flow_summary.InitYtm = result.ytm * ConsGlobal.bondPriceMultiple;
}
else
{
callback?.Invoke($"{gourpItem.Key.UnderlyingCode} 标的收益率无法计算,已跳过!");
}
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<swap_flow> swapFlows1, DateTime tradeDate, 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.TradingAmountAvg = Math.Round(swap_flow_summary.TradingAmountAvg, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
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 = FindActiveSwapTrades(valueDate);
var swapTradeIds = swaptrades.Select(s => s.id);
var tradeExtends = FindTradeExtends(swapTradeIds);
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 = FindActivePositions(swapTradeIds, (int)SwapDirectionEnum.支付);
var matuirityDate = GetNextBusinessDayBefore(valueDate.AddDays(14));
var floatRateQuery = QueryFloatRates(valueDate, matuirityDate);
int dealCount = 0;
foreach (var groupItem in flowquery)
{
MergeRestModelItem(groupItem, restSwapTrades, swapPositions, floatRateQuery, ref dealCount, action);
}
}
/// <summary>
/// 加权平均法生成开平仓事件
/// </summary>
/// <param name="mergeList"></param>
/// <param name="valueDate"></param>
public Dictionary<long, List<string>> MergeAvgModeCompose(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 null;
}
var swaptrades = DbContext.trade.Where(t => t.TradeType == "收益互换"
&& 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;
Dictionary<long, List<string>> clientUmsDic = new Dictionary<long, List<string>>();
foreach (var groupItem in flowquery)
{
MergeAvgModelItem(groupItem, swaptrades, swapPositions, floatRateQuery, ref dealCount, action);
clientUmsDic.Add(groupItem.Key ?? 0, groupItem.Select(p => p.UnderlyingCode).Distinct().ToList());
}
return clientUmsDic;
}
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, ref int dealCount, Action<int>? action)
{
var clientId = groupItem.Key;
var client = FindClient(clientId ?? 0);
if (client == null)
{
throw new ServiceException($"找不到id为{clientId}的客户信息");
}
if (!client.DerivativesInvestmentVarieties.Contains((int)DerivativesInvestmentVarietiesEnum.场外互换 + ""))
{
throw new ServiceException($"客户:{client.Name}未设置交易种类“场外互换”,无法生成互换交易!");
}
var etradeRule = GetEtradingRule(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 = FindAssetUnit(etradeRule.AssetAccount_0);//取对客簿记账户
if (asset == null)
{
throw new ServiceException($"找不到名为{etradeRule.AssetAccount_0}的簿记账户信息");
}
if (asset.TraderIdsInt.Count == 0)
{
throw new ServiceException($"{etradeRule.AssetAccount_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();
foreach (var underlyingGroupItem in underlyingGroup)
{
var underlyingCode = underlyingGroupItem.Key;
var underlying = FindUnderlying(underlyingCode);
if (underlying == null)
{
throw new ServiceException($"找不到标的代码为{underlyingCode}的标的信息");
}
var floatRate = 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 = FindFlowEventsForCashCheck(flowMerge);
var tradeIds = flowEvents.Select(s => s.SwapTradeId).Distinct();
var trades = FindValidTrades(tradeIds);
cashNeedAfter = !trades.Any();
}
if (!hasPayPosition)//没有持仓
{
DealNoPosition(mergeList, client, asset, underlying, floatRate, clearingAgency, cashNeedAfter);
}
else
{
DealHasPosition(mergeList, client, asset, underlying, floatRate, clientSwapPositionList, clientSwapTrades, clearingAgency, cashNeedAfter);
}
}
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_merge> 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();
foreach (var underlyingGroupItem in underlyingGroup)
{
var underlyingCode = underlyingGroupItem.Key;
var underlying = FindUnderlying(underlyingCode);
if (underlying == null)
{
throw new ServiceException($"找不到标的代码为{underlyingCode}的标的信息");
}
var floatRate = 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,
string clearingAgency,
bool cashNeedAfter)
{
var mergeOrderList = mergeList.OrderBy(o => o.FirstFlowTime);
swap_flow_merge flowMergeMax = mergeOrderList.First();//先开最早的一条
swap_flow_merge flowMergeMin = mergeOrderList.Last();
var trade = CreateNewSwapTrade(flowMergeMax, client, asset, underlying, floatRate, clearingAgency, cashNeedAfter: cashNeedAfter);
flowMergeMax.SwapTradeNo = trade.TradeNumber;
flowMergeMin.SwapTradeNo = trade.TradeNumber;
if (mergeList.Count == 2)//有两条流水
{
var qty = flowMergeMax.TradingQtyAbs - flowMergeMin.TradingQtyAbs;//平仓剩余数量
AutoSwapUnwind(trade.id,
flowMergeMin.TradingAmountAvg,
flowMergeMin.TradingAmountFeeAvg,
flowMergeMin.TradingAmountNetFeeAvg ?? 0,
flowMergeMin.TradingAmountNetAvg ?? 0,
flowMergeMin.OccurTime,
flowMergeMax.TradingQtyAbs,
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.TradingFeePending = Math.Round(flowMergeClone.TradingFeePending, 4, MidpointRounding.AwayFromZero);
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 = CreateNewSwapTrade(flowMergeClone, client, asset, underlying, floatRate, 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_merge> swapFlows,
Client client,
AssetUnit asset,
underlying_manager underlying,
SwapFloatRate floatRate,
string clearingAgency,
int byType)
{
var negativeFlow = swapFlows.Where(x => x.BsType != byType).FirstOrDefault();
var sameFlow = swapFlows.Where(x => x.BsType == byType).FirstOrDefault();
if (negativeFlow == null)
{
return NewSwapTrade(sameFlow, client, asset, underlying, floatRate, clearingAgency);
}
return DealTwoDirectionFlows(sameFlow, negativeFlow, client, asset, underlying, floatRate, clearingAgency);
}
/// <summary>
/// 当前无持仓,且有2个方向流水合成簿记
/// </summary>
/// <param name="sameFlow"></param>
/// <param name="negativeFlow"></param>
/// <param name="client"></param>
/// <param name="asset"></param>
/// <param name="underlying"></param>
/// <param name="floatRate"></param>
/// <param name="clientMarginTemplate"></param>
/// <param name="clearingAgency"></param>
/// <returns></returns>
private trade DealTwoDirectionFlows(swap_flow_merge sameFlow,
swap_flow_merge negativeFlow,
Client client,
AssetUnit asset,
underlying_manager underlying,
SwapFloatRate floatRate,
string clearingAgency)
{
var sameQty = sameFlow.TradingQty;
var negaQty = negativeFlow.TradingQty;
swap_flow_merge negaFlowClone = DataHelper.DeepCopyObject(negativeFlow);
swap_flow_merge sameFlowClone = DataHelper.DeepCopyObject(sameFlow);
//先开数量大的,再用小的平仓
if (sameQty < negaQty)
{
sameFlowClone = DataHelper.DeepCopyObject(negativeFlow);
negaFlowClone = DataHelper.DeepCopyObject(sameFlow);
}
var trade = NewSwapTrade(sameFlowClone, client, asset, underlying, floatRate, clearingAgency);
// 平仓
AutoSwapUnwind(trade.id,
negaFlowClone.TradingAmountAvg,
negaFlowClone.TradingAmountFeeAvg,
negaFlowClone.TradingAmountNetFeeAvg ?? 0,
negaFlowClone.TradingAmountNetAvg ?? 0,
negaFlowClone.OccurTime,
negaFlowClone.TradingQty,
negaFlowClone.TradingFeePending);
return trade;
}
/// <summary>
/// 流水新开仓
/// </summary>
/// <param name="flowMergeFirst"></param>
/// <param name="client"></param>
/// <param name="asset"></param>
/// <param name="underlying"></param>
/// <param name="floatRate"></param>
/// <param name="clientMarginTemplate"></param>
/// <param name="clearingAgency"></param>
/// <returns></returns>
private trade NewSwapTrade(swap_flow_merge flowMergeFirst,
Client client,
AssetUnit asset,
underlying_manager underlying,
SwapFloatRate floatRate,
string clearingAgency)
{
var swapTradeService = new SwapTradeService(UserInfo);
var trade = swapTradeService.NewSwapTrade(flowMergeFirst, client, asset, underlying, floatRate, clearingAgency, LongShortStructType);
flowMergeFirst.SwapTradeNo = trade.TradeNumber;
flowMergeFirst.SwapTradeId = trade.id;
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,
string clearingAgency,
bool cashNeedAfter)
{
if (mergeList.Count == 1)//只有一条流水情况
{
DealSingleFlow(mergeList, clientSwapPositionList, clientSwapTrades, client, asset, underlying, floatRate, clearingAgency);
}
else
{
DealDoubleFlow(mergeList, clientSwapPositionList, clientSwapTrades, client, asset, underlying, floatRate, 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_merge> flowList,
Client client,
AssetUnit asset,
underlying_manager underlying,
SwapFloatRate floatRate,
List<swap_position> clientSwapPositionList,
List<trade> clientSwapTrades,
string clearingAgency)
{
var firstFlow = flowList.First();
if (flowList.Count == 1)//只有一条流水情况
{
AvgDealSingleFlow(firstFlow, 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,
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, clearingAgency);
flowMergeMax.SwapTradeNo = trade.TradeNumber;
}
}
else //只存在同向交易
{
var trade = swapTradeService.NewSwapTrade(flowMergeMax, client, asset, underlying, floatRate, 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 = Math.Round(origin.TradingAmountAvg + oriRatio * origin.TradingFeePending * 2 / origin.TradingQty, ConsGlobal.PriceRound, MidpointRounding.AwayFromZero);
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="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,
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, clearingAgency, true, cashNeedAfter);
flowMergeFirst.SwapTradeNo = flowMergeFirstClone.SwapTradeNo;
//再处理第二条流水的反向持仓
var lastTrade = DealDoubleFlowDetial(sameTrades, sameDirectionPositions, flowMergeLastClone, client, asset, underlying, floatRate, 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, clearingAgency, true, false);
flowMergeLast.SwapTradeNo = flowMergeLastClone.SwapTradeNo;
}
else if (lastTrade == null)
{
swapTradeService.NewSwapTrade(flowMergeLastClone, client, asset, underlying, floatRate, 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 AvgDealSingleFlow(swap_flow_merge flow,
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 flowClone = DataHelper.DeepCopyObject(flow);
// 同向新开
if (flow.BsType == firstPosi.PositionType)
{
NewSwapTrade(flowClone, client, asset, underlying, floatRate, clearingAgency);
}
else //反向先平仓,有剩余开仓
{
AvgDealUnwind(flowClone, clientSwapTrades, swapPositions, client, asset, underlying, floatRate, 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_merge> 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 flowSame = flowList.Where(x => x.BsType == firstPosi.PositionType).First();
var flowNeg = flowList.Where(x => x.BsType != firstPosi.PositionType).First();
var negaBsType = flowNeg.BsType;
var sameQty = posiQty + flowSame.TradingQty;
var flowSameClone = DataHelper.DeepCopyObject(flowSame);
var flowNegClone = DataHelper.DeepCopyObject(flowNeg);
//先平反向
var trade = AvgDealUnwind(flowNegClone, clientSwapTrades, swapPositions, client, asset, underlying, floatRate, clearingAgency);
var newFlowList = new List<swap_flow>();
if (trade != null)
{
var flowQty = flowSameClone.TradingQty;
var currentPosiQty = flowNegClone.TradingQty;
var newOpenQty = currentPosiQty - flowQty;
var unwindQty = newOpenQty > 0 ? flowQty : currentPosiQty;
var unwindFee = flowSameClone.TradingFeePending * unwindQty / currentPosiQty;
unwindFee = Math.Round(unwindFee, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
flowSameClone.TradingFeePending = flowSameClone.TradingFeePending - unwindFee;
flowQty = flowQty - unwindQty;
// 平仓
new SwapDealService(UserInfo).AuotoSwapUnwind(trade.id,
flowSameClone.TradingAmountAvg,
flowSameClone.TradingAmountFeeAvg,
flowSameClone.TradingAmountNetFeeAvg ?? 0,
flowSameClone.TradingAmountNetAvg ?? 0,
flowSameClone.OccurTime,
unwindQty,
unwindFee);
if (flowQty > 0)
{
flowSameClone.TradingQty = flowQty;
flowSameClone.TradingAmount = flowSameClone.TradingQty;
NewSwapTrade(flowSameClone, client, asset, underlying, floatRate, clearingAgency);
}
}
else
{
NewSwapTrade(flowSameClone, client, asset, underlying, floatRate, clearingAgency);
}
}
/// <summary>
/// 有持仓流水反向平仓
/// </summary>
/// <param name="swapFlow"></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="clientMarginTemplate"></param>
/// <param name="clearingAgency"></param>
/// <returns></returns>
private trade AvgDealUnwind(swap_flow_merge swapFlow,
List<trade> trades,
List<swap_position> swapPositions,
Client client,
AssetUnit asset,
underlying_manager underlying,
SwapFloatRate floatRate,
string clearingAgency)
{
var swapTradeService = new SwapTradeService(UserInfo);
var flowQty = swapFlow.TradingQty;
foreach (var posi in swapPositions)
{
if (swapFlow == null || flowQty == 0)
{
break;
}
var td = trades.FirstOrDefault(p => p.id == posi.SwapTradeId);
if (td != null)
{
var posiQty = posi.PosiQuantity;
var newOpenQty = posiQty - flowQty;
var unwindQty = newOpenQty > 0 ? flowQty : posiQty;
var unwindFee = swapFlow.TradingFeePending * unwindQty / flowQty;
unwindFee = Math.Round(unwindFee, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
flowQty = flowQty - unwindQty;
swapFlow.TradingFeePending = swapFlow.TradingFeePending - unwindFee;
// 平仓
new SwapDealService(UserInfo).AuotoSwapUnwind(td.id,
swapFlow.TradingAmountAvg,
swapFlow.TradingAmountFeeAvg,
swapFlow.TradingAmountNetFeeAvg ?? 0,
swapFlow.TradingAmountNetAvg ?? 0,
swapFlow.OccurTime,
unwindQty,
unwindFee);
}
}
if (flowQty > 0) //平仓完有剩余流水,
{
swapFlow.TradingQty = flowQty;
swapFlow.TradingAmount = swapFlow.TradingQty * swapFlow.TradingAmountAvg;
return NewSwapTrade(swapFlow, client, asset, underlying, floatRate, clearingAgency);
}
return null;
}
/// <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,
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, 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;
var unwindFee = newQty > 0 ? flowMerge.TradingFeePending : flowMerge.TradingFeePending * floatPosition.PosiQuantity / flowMerge.TradingQty;
unwindFee = Math.Round(unwindFee, 4, MidpointRounding.AwayFromZero);
// 全平
new SwapDealService(UserInfo).AuotoSwapUnwind(td.id,
flowMerge.TradingAmountAvg,
flowMerge.TradingAmountFeeAvg,
flowMerge.TradingAmountNetFeeAvg ?? 0,
flowMerge.TradingAmountNetAvg ?? 0,
flowMerge.OccurTime,
floatPosition.PosiQuantity,
unwindFee);
unwindTradeIds.Add(td.id);
flowMerge.TradingAmount = newQtyAbs * flowMerge.ContractSize;
if (newQty > 0)
{
flowMerge.TradingFeePending = 0;
}
else
{
flowMerge.TradingFeePending = flowMerge.TradingFeePending - unwindFee;
}
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);
}
}
}