- 统一债券价格四舍五入规则,使用AwayFromZero模式 - 为债券类型添加特殊的精度处理逻辑 - 修复前端价格显示精度计算问题 - 优化价格存储精度控制,区分债券和其他产品类型 - 修复初始化YTM和净价精度处理问题 - 更新价格验证逻辑以支持动态精度设置
1459 lines
74 KiB
C#
1459 lines
74 KiB
C#
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);
|
||
}
|
||
}
|
||
}
|