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

1819 lines
82 KiB
C#

using BaseOUDAL;
using ClosedXML.Report.Options;
using Confluent.Kafka;
using CsvHelper;
using MoreLinq;
using Newtonsoft.Json;
using NPOI.POIFS.NIO;
using Org.BouncyCastle.Ocsp;
using Qdp.ComputeServiceV2.Data.CommonModels.TradeInfos;
using Qdp.Foundation.Implementations;
using Qdp.Pricing.Ecosystem.Trade.FixedIncome;
using Qdp.Pricing.Library.Base.Utilities;
using Qdp.Pricing.Library.Common.Products.Rates;
using System;
using System.Data;
using System.Linq.Expressions;
using System.Text;
using YLErp.BLL;
using YLErp.Configuration;
using YLErp.Configuration.Enums;
using YLErp.CustomizedBizLogic;
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.DataProviderModule;
using YLErp.Modules.EodModule;
using YLErp.Modules.RiskModule;
using YLErp.Modules.SalesModule;
using YLErp.Modules.TradeModule;
using YLErp.Modules.TradeModule.DealModule;
using YLErp.Modules.TradeModule.QueryModule;
using YLErp.Modules.UnderlyingModule;
using YLErp.QdpModule;
using static YLErp.ConsGlobal;
namespace YLErp.Modules.SwapModule
{
/// <summary>
/// 新版互换交易服务
/// </summary>
public class SwapTradeService : SwapTradeBaseService
{
public SwapTradeService(OptUserInfo optUser) : base(optUser)
{
}
public SwapTradeService(YLBaseService baseService) : base(baseService)
{
}
#region 互换交易保存
/// <summary>
/// 新版收益互换预付金校验
/// </summary>
public string ValidateMargin(trade req)
{
var errorMsg = string.Empty;
var client = DataCacheProvider.GetClientDataSource().GetData(req.ClientId);
if (!client.DerivativesInvestmentVarieties.Contains((int)DerivativesInvestmentVarietiesEnum.场外互换 + ""))
{
return $"客户:{client.Name}未设置交易种类“场外互换”,无法生成互换交易!";
}
//收益互换初始预付金校验
errorMsg = checkMarginRate(req);
return errorMsg;
}
/// <summary>
/// 互换交易保存
/// </summary>
/// <param name="td"></param>
/// <returns></returns>
public trade SaveTrade(trade req)
{
var um = checkUnderlying(req);
trade dbTrade = new trade();
//交易保存处理
var tradeNumberGenerated = PrepareTrade(req, TradeSourceEnum.系统交易, um);
var trans = DbContext.Database.BeginTransaction();
try
{
var isAddNew = req.id == 0;
dbTrade = isAddNew ? InnerSaveNewTrade(req, tradeNumberGenerated) : InnerSaveEditTrade(req, out var changeConfirmStatus);
if (req.SalesCommission != null)
{
req.SalesCommission.ClientId = dbTrade.ClientId;
}
if (PS.Config.ErpElement.SalesCommissionCalculation == "默认" || PS.Config.ErpElement.SalesCommissionCalculation == "" || PS.Config.ErpElement.SalesCommissionCalculation == null)
{
new SalesCommissionDataService(this).SaveTradeCommission(dbTrade.id, req.SalesCommission, false);
}
else if (PS.Config.ErpElement.SalesCommissionCalculation == "公式1")
{
if (req.SalesCommission?.SalesIds != null && req.SalesCommission.SalesIds.Any())
{
new SalesCommissionDetailDataService(this).SaveTradeCommissionDetail(dbTrade.id, req.SalesCommission.ClientId, req.SalesCommission.Commission, req.SalesCommission.CommissionFixed, req.SalesCommission.SalesIds, false);
}
else
{
new SalesCommissionDetailDataService(this).RemoveTradeCommissionDetail(dbTrade.id);
}
}
trans.Commit();
}
finally
{
trans.Dispose();
}
return dbTrade;
}
/// <summary>
/// 生成推送记录给衡泰
/// </summary>
/// <param name="add"></param>
/// <param name="td"></param>
public void AddPushLog(trade td)
{
bool longShort = td.StructureType == "多空组合";
if (!longShort)
{
AddSinglePushLog(td);
}
}
/// <summary>
/// 生成非多空组合记录
/// </summary>
/// <param name="add"></param>
/// <param name="td"></param>
/// <param name="posiPositions"></param>
public void AddSinglePushLog(trade td)
{
var marginModes = new int[] { (int)InterestModeEnum.追加预付金, (int)InterestModeEnum.初始预付金 };
var positions = DbContext.swap_position.Where(x => x.SwapTradeId == td.id && x.IsInitial && !x.Invalid).ToList();
td.swap_positions = positions.Where(x => marginModes.Contains(x.InterestMode)).ToList();
var posiPositions = positions.Where(x => x.PosiDirection > 0).ToList();
}
/// <summary>
/// 校验 定义文件型债券收益互换 持仓标的只能存在一笔
/// </summary>
/// <param name="td"></param>
/// <exception cref="ServiceException"></exception>
public void ExistLongShortTrade(trade td)
{
var longshortName = "定义文件型债券收益互换";
if (td.StructureType == longshortName)
{
if (DbContext.trade.Any(a => a.ClientId == td.ClientId && a.TradeStatus == "确认成交" && a.ValidState != ConsGlobal.InValid && a.StructureType == longshortName && a.UnderlyingCode == td.UnderlyingCode && a.id != td.id))
{
throw new ServiceException($"{td.ClientName}已经存在标的为{td.UnderlyingCode}的定义文件型债券收益互换交易");
}
}
}
/// <summary>
/// 单标的生成开仓事件
/// </summary>
/// <param name="td"></param>
public void AddPositionEvent(trade td, string optLog)
{
var initials = DbContext.swap_position.Where(x => x.SwapTradeId == td.id && x.IsInitial && !x.Invalid).ToList();
if (td.StructureType == "多空组合")
{
initials = initials.Where(x => x.PosiDirection == 0).ToList();
}
new SwapFlowEventService(this).InitEvent(initials, td, optLog);
}
/// <summary>
/// 删除互换交易
/// </summary>
/// <param name="id"></param>
public void deleteTrade(int id, bool addLog = true)
{
var dbTrade = DbContext.trade.Find(id);
if (dbTrade == null)
{
throw new ServiceException("找不到交易信息");
}
dbTrade.OptId = UserId;
dbTrade.OptName = UserName;
dbTrade.OptDate = OptDate;
dbTrade.ValidState = ConsGlobal.InValid;
var tradeNumber = dbTrade.TradeNumber;
if (!string.IsNullOrWhiteSpace(dbTrade.TradeNumber))
{
dbTrade.TradeNumber = dbTrade.TradeNumber.Insert(0, "XX");
var contractBll = new TradeContractBLL(UserId, UserName);
contractBll.DeleteConfirmBook(dbTrade.id);
}
var swapPositions = DbContext.swap_position.Where(x => x.SwapTradeId == id).ToList();
swapPositions.ForEach(x =>
{
x.Invalid = true;
x.OptTime = OptDate;
});
new SwapEventService(this).AddSwapEventDate(DateTime.Now.Date, id, (int)SwapEventTypeEnum.删除, string.Empty, 0, false);
DeleteTradeDetials(id);
DeleteTradeCashInCashOut(id);
DbContext.SaveChanges();
}
private void DeleteTradeDetials(int tradeId)
{
var eodSwapPosiSql = $"{nameof(eod_swap_position.SwapTradeId)}={tradeId}";
var eodSwapSql = $"{nameof(eod_swap.SwapTradeId)}={tradeId}";
DbContext.BulkDelete<eod_swap_position>(eodSwapPosiSql);
DbContext.BulkDelete<eod_swap>(eodSwapSql);
}
public void DeleteTradeCashInCashOut(int tradeId)
{
var predicate_cashIncashOut = PredicateBuilder.Create<ClientCashInCashOut>(x => x.TradeId == tradeId);
var delCashInCashOutArr = DbContext.ClientCashInCashOut.Where(predicate_cashIncashOut);
DbContext.ClientCashInCashOut.RemoveRange(delCashInCashOutArr);
}
/// <summary>
/// 单标的初始化实时持仓
/// </summary>
/// <param name="tradeId"></param>
public void InitialPosition(trade td, bool save = false)
{
var swapPostions = DbContext.swap_position.Where(x => x.SwapTradeId == td.id);
var initials = swapPostions.Where(x => x.IsInitial);
var positions = swapPostions.Where(x => !x.IsInitial);
if (td.StructureType == "多空组合")
{
initials = initials.Where(x => x.PosiDirection == 0);
positions = swapPostions.Where(x => !x.IsInitial && x.PosiDirection == 0);
}
List<swap_position> _Positions = new List<swap_position>(positions);
DbContext.swap_position.RemoveRange(positions);
foreach (var item in initials)
{
if (string.IsNullOrEmpty(item.PosiNumber))
{
item.PosiNumber = $"{td.TradeNumber}-{item.id}";
}
if (td.StructureType == "多空组合")
{
var position = _Positions.FirstOrDefault(x => x.PositionId == item.id);
if (position == null)
{
position = item.Clone();
position.id = 0;
position.PositionId = item.id;
position.IsInitial = false;
DbContext.swap_position.Add(position);
}
}
else
{
var position = item.Clone();
position.id = 0;
position.PositionId = item.id;
position.IsInitial = false;
DbContext.swap_position.Add(position);
}
}
if (save)
{
DbContext.SaveChanges();
}
}
//新增交易保存
private trade InnerSaveNewTrade(trade req, bool tradeNumberGenerated)
{
var dbTrade = req.Clone();
DbContext.trade.Add(dbTrade);
//申万交易编号特殊处理(如果交易编号是用户手动输入时)
if (!tradeNumberGenerated && PS.Config.Is申万)
{
var client = DataCacheProvider.GetClientDataSource().GetData(dbTrade.ClientId);
if (client?.Code != null && dbTrade.TradeNumber.StartsWith(client.Code))
{
var canParseInt = int.TryParse(dbTrade.TradeNumber.Substring(client.Code.Length), out var contractNo);
if (canParseInt && contractNo > (client.CurrentContractNo ?? 0))
{
client.CurrentContractNo = contractNo;
}
}
}
InnerSaveTrade(true, dbTrade, "");
return dbTrade;
}
/// <summary>
/// 新增互换交易并确认开仓
/// </summary>
/// <param name="flowMerge"></param>
/// <param name="matuirityDate"></param>
/// <param name="client"></param>
/// <param name="asset"></param>
/// <param name="underlying"></param>
/// <param name="swapFloatRate"></param>
/// <returns></returns>
public trade NewSwapTrade(swap_flow_merge flowMerge,
Client client,
AssetUnit asset,
underlying_manager underlying,
SwapFloatRate swapFloatRate,
client_marginrate clientMarginTemplate,
string clearingAgency,
string structureType = "普通债券类收益互换",
bool cashNeedAfter = false)
{
var td = PrepareTrade(flowMerge, client, asset, underlying, structureType);
PrepareTradeExtend(flowMerge, td, underlying, swapFloatRate, clientMarginTemplate);
td.TradeNumber = BizLogicSingleton.Instance.GenerateTradeNumberBeforeConfirm(td, DbContext);
flowMerge.SwapTradeNo = td.TradeNumber;
PrepareSwapTrade(td, TradeSourceEnum.系统交易, underlying);
DbContext.trade.Add(td);
DbContext.SaveChanges();
td.trade_Initial_Margin = new trade_initial_margin()
{
TradeId = td.id,
MarginType = 1,
Direction = (int)SwapDirectionEnum.收取,
MarginValue = 0,
};
AddTradeMeta(false, td.id, YLErp.DBModels.Consts.ConsTradeMetaKey.MainProtocolCode, client.MainProtocolCode);
AddTradeMeta(false, td.id, YLErp.DBModels.Consts.ConsTradeMetaKey.SupProtocolCode, client.SupProtocolCode);
AddTradeMeta(false, td.id, YLErp.DBModels.Consts.ConsTradeMetaKey.TradingPlace, "柜台市场");
if (!string.IsNullOrEmpty(clearingAgency))
{
AddTradeMeta(false, td.id, YLErp.DBModels.Consts.ConsTradeMetaKey.ClearingAgency, clearingAgency);
}
TradeExtendJson tradeExtendJson = new TradeExtendJson()
{
FlowBookMode = (int)FlowBookModeEnum.重置,
FloatingPnlAnnualized = false,
NeedOpenFee = true,
OpenFeeType = 1,
Direction = 1,
InterestCalcMode = "10",
SettlementRules = cashNeedAfter ? 1 : 0,
};
if (structureType != "普通债券类收益互换")
{
tradeExtendJson.FlowBookMode = (int)FlowBookModeEnum.加权平均;
}
td.trade_extend = new trade_extend()
{
TradeId = td.id,
ExtendJson = JsonHelper.Serialize(tradeExtendJson)
};
DbContext.trade_initial_margin.Add(td.trade_Initial_Margin);
DbContext.trade_extend.Add(td.trade_extend);
foreach (var item in td.swap_positions)
{
item.SwapTradeId = td.id;
DbContext.swap_position.Add(item);
}
DbContext.SaveChanges();
new TradeConfirmService(UserInfo).SwapTradeConfirm(td, "流水自动簿记确认交易", true, flowMerge.OccurTime, "流水自动");
return td;
}
/// <summary>
/// 准备框架合约数据
/// </summary>
/// <param name="flowMerge"></param>
/// <param name="client"></param>
/// <param name="asset"></param>
/// <param name="underlying"></param>
/// <returns></returns>
private trade PrepareTrade(swap_flow_merge flowMerge, Client client, AssetUnit asset, underlying_manager underlying, string structureType = "普通债券类收益互换")
{
trade td = new trade()
{
TradeType = "收益互换",
BuySell = "卖出",
StartDate = flowMerge.OccurTime,
TradeDate = flowMerge.OccurTime,
TraderId = asset.TraderIdsInt.FirstOrDefault(),
TraderName = asset.TraderNamesList.FirstOrDefault(),
MarginTemplateName = "系统默认",
OpponentRole = "乙方",
StructureType = structureType,
InitialMargin = 0,
ClientId = client.id,
ClientNumber = client.Number,
ClientName = client.Name,
AssetId = asset.id,
AssetBookName = asset.Name,
Notional = Convert.ToDouble(flowMerge.TradingQtyAbs),
TradeAmount = Convert.ToDouble(flowMerge.TradingQtyAbs),
StockEqvNotional = Convert.ToDouble(flowMerge.TradingAmount),
IsAutoGenerate = true,
};
if (flowMerge.SettleDate.HasValue)
{
td.StartDate = flowMerge.SettleDate.Value;
}
td.ExerciseDate = QdpCalendarHelper.GetNonHolidayDefore(td.StartDate.Value.AddDays(14));
PrepareUnderlying(td, underlying);
PrepareSwapTradeClient(td, underlying);
td.TradeOldStatus = ConsTrade.新增待确认;
td.OptId = UserId;
td.OptName = UserName;
td.OptDate = OptDate;
td.CreateDate = OptDate;
td.ValidState = "Valid";
td.TradeSource = "系统交易";
td.TradeStatus = ConsTrade.确认成交;
return td;
}
/// <summary>
/// 准备框架合约明细数据
/// </summary>
/// <param name="flowMerge"></param>
/// <param name="td"></param>
/// <param name="underlying"></param>
/// <param name="swapFloatRate"></param>
private void PrepareTradeExtend(swap_flow_merge flowMerge, trade td, underlying_manager underlying, SwapFloatRate swapFloatRate, client_marginrate clientMarginTemplate)
{
swap_position floatPosition = new swap_position()
{
PosiDirection = (int)SwapDirectionEnum.支付,
PositionType = flowMerge.BsType,
UnderlyingCode = flowMerge.UnderlyingCode,
CountRatio = underlying.CountRatio,
ContractSize = Convert.ToDecimal(underlying.ContractSize),
PosiNetPrice = flowMerge.TradingAmountFeeAvgAbs,
PosiGrossPrice = flowMerge.TradingAmountAvg,
PosiNetFeePrice = flowMerge.TradingAmountNetFeeAvg ?? 0,
PosiNetNoFeePrice = flowMerge.TradingAmountNetAvg ?? 0,
PosiQuantity = flowMerge.TradingQtyAbs,
PosiNotionalValue = flowMerge.TradingQtyAbs * flowMerge.ContractSize,
PosiTradingFeePending = flowMerge.TradingFeePending,
//PosiTradingFee = flowMerge.TradingFee,
//PosiTradingFeeUnit = flowMerge.TradingQtyAbs == 0 ? 0 : flowMerge.TradingFee * flowMerge.ContractSize / flowMerge.TradingQtyAbs,
PosiTradingFee = 0,
PosiTradingFeeUnit = 0,
SwapTradeId = td.id,
PosiMatuirityDate = td.ExerciseDate.Value,
PosiStartDate = td.StartDate.Value,
IsInitial = true,
OptTime = DateTime.Now,
OptId = UserInfo.UserId,
OptName = UserInfo.UserName,
UnderlyingInstrumentType = underlying.UnderlyingInstrumentType
};
td.swap_positions.Add(floatPosition);
if (clientMarginTemplate != null)
{
decimal initMarginRate = Convert.ToDecimal(clientMarginTemplate.InitMarginRate);
if (td.StructureType != "普通债券类收益互换")
{
if (floatPosition.PositionType == (int)PositionTypeFlag.Long)
{
initMarginRate = clientMarginTemplate.LongInitMarginRate ?? 0;
}
else
{
initMarginRate = clientMarginTemplate.ShortInitMarginRate ?? 0;
}
}
bool year20 = true;
if (underlying.MaturityDate.HasValue && underlying.MaturityDate < DateTime.Now.AddDays(20 * 365))
{
year20 = false;
}
initMarginRate = year20 ? 0.05m : initMarginRate;
swap_position marginPosition = new swap_position()
{
InterestDirection = (int)SwapDirectionEnum.收取,
InterestRateDefault = clientMarginTemplate.InitMarginRebateRate ?? 0,
InterestMode = (int)InterestModeEnum.初始预付金,
InterestPrincipalFix = initMarginRate * Convert.ToDecimal(td.StockEqvNotional),
HappenDate = td.TradeDate.Value,
PosiMatuirityDate = td.ExerciseDate.Value,
PosiStartDate = td.StartDate.Value,
Currency = "CNY",
IsAnnualized = true,
SwapTradeId = td.id,
IsInitial = true,
OptTime = DateTime.Now,
OptId = UserInfo.UserId,
OptName = UserInfo.UserName,
};
var marginInterval = new IntervalModel()
{
Date = td.ExerciseDate.Value,
Rate = marginPosition.InterestRateDefault,
Settlement = 0
};
List<IntervalModel> marginIntervalModels = new List<IntervalModel>() { marginInterval };
marginPosition.InterestSwapInterval = JsonConvert.SerializeObject(marginIntervalModels);
td.swap_positions.Add(marginPosition);
}
swap_position interestPosition = new swap_position()
{
InterestDirection = flowMerge.BsType == (int)PositionTypeFlag.Long ? (int)SwapDirectionEnum.收取 : (int)SwapDirectionEnum.支付,
InterestRateDefault = 0,
InterestMode = (int)InterestModeEnum.标的期初全价,
InterestPrincipalFix = 0,
InterestType = 1,
IsAnnualized = true,
SwapTradeId = td.id,
PosiMatuirityDate = td.ExerciseDate.Value,
PosiStartDate = td.StartDate.Value,
IsInitial = true,
OptTime = DateTime.Now,
OptId = UserInfo.UserId,
OptName = UserInfo.UserName,
};
if (swapFloatRate != null)
{
interestPosition.InterestRateDefault = flowMerge.BsType == (int)PositionTypeFlag.Long ? (swapFloatRate.LongPricePoint ?? 0) : (swapFloatRate.ShortPricePoint ?? 0) * -1;
interestPosition.InterestRateDefault = interestPosition.InterestRateDefault * 0.0001m;
interestPosition.FloatRateUnderlyingCode = swapFloatRate.BaseUnderlyingCode;
}
var interval = new IntervalModel()
{
Date = td.ExerciseDate.Value,
Rate = interestPosition.InterestRateDefault,
Settlement = 0
};
List<IntervalModel> intervalModels = new List<IntervalModel>() { interval };
interestPosition.InterestSwapInterval = JsonConvert.SerializeObject(intervalModels);
td.swap_positions.Add(interestPosition);
}
//修改交易保存
private trade InnerSaveEditTrade(trade req, out bool changeConfirmStatus)
{
var dbTrade = DbContext.trade.Find(req.id);
if (dbTrade == null)
{
throw new ServiceException("保存失败,数据不存在");
}
dbTrade.SalesCommission = new SalesCommissionDataService(OptUser).GetTradeCommissionInfo(dbTrade.id);
if (dbTrade.SalesCommission?.Commission == null && req.SalesCommission == null)
{
dbTrade.SalesCommission = null;
}
if (!ConsGlobal.IsValid(dbTrade.ValidState))
{
throw new NotSupportedException("不支持对已无效的交易进行修改:" + dbTrade.TradeNumber);
}
if (valuedateBLL.SystemDate.EditTradeNeedRemark)
{
req.MetaDic.TryGetValue("remark", out var remark);
req.MetaDic.Remove("remark");
}
//使用明确的IsUsePremiumRate布尔值
if (!req.IsUsePremiumRate.HasValue && dbTrade.IsUsePremiumRate.HasValue)
{
req.IsUsePremiumRate = false;
}
req.CalcFlag = dbTrade.CalcFlag;
var oldClientId = dbTrade.ClientId;
var oldTradeStatus = dbTrade.TradeStatus;
var tradeDateChanged = dbTrade.TradeDate != req.TradeDate;
//移除价格观察
if (dbTrade.UnderlyingId != req.UnderlyingId)
{
RemoveEntities<manually_trade_observation_price>(x => x.TradeId == req.id);
}
//交易编号调整
var canGenerateTradeNumber = false;
if (req.ValidState == "InValid")
{
if (!string.IsNullOrWhiteSpace(dbTrade.TradeNumber))
{
new TradeDocumentDataService(this).DeleteConfirmBook(dbTrade.id);
if (dbTrade.TradeNumber.StartsWith("CW"))
{
req.TradeNumber = dbTrade.TradeNumber.Replace("CW", "XX");
}
else // if (PS.Config.Is光大光子)
{
req.TradeNumber = dbTrade.TradeNumber.Insert(0, "XX");
}
}
}
else
{
canGenerateTradeNumber = string.IsNullOrWhiteSpace(req.TradeNumber);
if (canGenerateTradeNumber)
{
req.TradeNumber = dbTrade.TradeNumber;
}
}
req.ContractVersion = dbTrade.ContractVersion;
req.PairTrade = dbTrade.PairTrade;
req.CreateDate = dbTrade.CreateDate;
//确认成交状态的交易修改后变为原始标记
req.DividendDate = new DateTime(2000, 1, 1);
//记录交易变更信息
var changsStr = string.Empty;
changeConfirmStatus = true;
var changes = DataChangeHelper.GetDataChanges(dbTrade, req);
var dbTradeMeta = DbContext.TradeMeta.Where(a => a.TradeId == dbTrade.id).ToDictionary(a => a.MetaKey, b => b.MetaValue);
var changMetas = new List<List<string>>();
var keys = new string[] { "交易场所", "清算机构", "主协议编号", "补充协议编号" };
foreach (var key in keys)
{
bool isdbMeta = dbTradeMeta.TryGetValue(key, out string oldVal);
bool isreqMeta = req.MetaDic.TryGetValue(key, out string newVal);
if (!isdbMeta && !isreqMeta || oldVal == newVal || (string.IsNullOrWhiteSpace(oldVal) && string.IsNullOrWhiteSpace(newVal)))
{
continue;
}
var changeMeta = new List<string>() { "", key, oldVal, newVal };
changMetas.Add(changeMeta);
}
if (changMetas.Any())
{
changes.AddRange(changMetas);
}
changsStr = changes.ToJson();
if (dbTrade.TradeStatus != ConsTrade.新增待确认)
{
//修改备注和簿记不需要提交审核和审批
changeConfirmStatus = !changes.All(n => string.IsNullOrEmpty(n[0]) || n[0] == nameof(trade.Comments)
|| n[0] == nameof(trade.AssetId) || n[0] == nameof(trade.AssetBookName));
}
//更新数据库实体
UpdateEntity(dbTrade, req);
//交易状态处理
if (dbTrade.CheckTradeUpdate == Convert.ToInt32(TradeCheckEnum.StatusOfNew) || dbTrade.TradeStatus == ConsTrade.确认成交)
{
if (changeConfirmStatus)
{
dbTrade.TradeStatus = ConsTrade.修改待确认;
dbTrade.CheckTradeUpdate = Convert.ToInt32(TradeCheckEnum.StatusOfOld);
dbTrade.CheckStatus = null;
}
}
else if (dbTrade.TradeStatus != ConsTrade.修改待确认)
{
dbTrade.TradeStatus = ConsTrade.新增待确认;
dbTrade.CheckTradeUpdate = Convert.ToInt32(TradeCheckEnum.StatusOfOld);
dbTrade.CheckStatus = null;
//如果是新增待确认并且修改了客户,需要重新生成交易编号
if (oldClientId != dbTrade.ClientId && canGenerateTradeNumber)
{
dbTrade.TradeNumber = BizLogicSingleton.Instance.GenerateTradeNumberBeforeConfirm(dbTrade, DbContext);
}
}
dbTrade.TradeOldStatus = oldTradeStatus;
//重置历史数据(20210928:如果是当天修改也支持重置)
var resetHisData = dbTrade.TradeStatus == "新增待确认" || tradeDateChanged || valuedateBLL.ValueDate == dbTrade.TradeDate;
if (resetHisData)
{
dbTrade.TradeSavedVol = PS.Config.Is厦门象屿 ? dbTrade.TradeCloseVolatility : dbTrade.TradeOpenVolatility;
if (PS.Config.ErpElement.VolMode == Configuration.VolModeEnum.VolSurface)
{
dbTrade.TradeSavedVol = dbTrade.Vol;
}
}
dbTrade.trade_Initial_Margin = req.trade_Initial_Margin;
dbTrade.trade_extend = req.trade_extend;
dbTrade.swap_positions = req.swap_positions;
dbTrade.MetaDic = req.MetaDic;
InnerSaveTrade(false, dbTrade, changsStr, changeConfirmStatus);
return dbTrade;
}
//用于交易(不能是结构化主交易)入库后续处理
//changeConfirmStatus:保存交易修改时需要
private void InnerSaveTrade(bool isAddNew, trade dbTrade, string changsStr, bool changeConfirmStatus = false)
{
//保存修改
DbContext.SaveChanges();
SaveTradeExend(dbTrade);
SaveTradeMargin(dbTrade);
SaveSwapPositions(dbTrade.swap_positions, dbTrade);
ClearSwapPositions(dbTrade);
//交易元数据
if (dbTrade.MetaDic.Any())
{
foreach (var kv in dbTrade.MetaDic)
{
if (isAddNew)
{
if (!string.IsNullOrEmpty(kv.Value))
{
AddTradeMeta(false, dbTrade.id, kv.Key, kv.Value);
}
}
else
{
AddOrUpdateTradeMeta(false, dbTrade.id, kv.Key, kv.Value);
}
}
}
else if (changeConfirmStatus)
{
//删除交易确认书 TODO
new TradeDocumentDataService(this).DeleteBooksAfterEditTrade(dbTrade);
}
int eventType = isAddNew ? (int)SwapEventTypeEnum.新增交易 : (int)SwapEventTypeEnum.修改交易;
if (!string.IsNullOrEmpty(changsStr))
{
changsStr = new DatatraceQueryService(UserInfo).ReplaceDataChanges(new trade(), changsStr);
}
new SwapEventService(UserInfo).AddSwapEventDate(dbTrade.TradeDate.Value, dbTrade.id, eventType, "", 0, true, "", true);
DbContext.SaveChanges();
}
/// <summary>
/// 添加交易元数据
/// </summary>
private void AddTradeMeta(bool saveChanges, int tradeId, string metaKey, string metaValue)
{
DbContext.TradeMeta.Add(new TradeMeta
{
TradeId = tradeId,
MetaKey = metaKey,
MetaValue = metaValue,
CreateTime = DateTime.Now
});
if (saveChanges)
{
DbContext.SaveChanges();
}
}
/// <summary>
/// 添加或更新交易元数据
/// </summary>
private void AddOrUpdateTradeMeta(bool saveChanges, int tradeId, string metaKey, string metaValue)
{
new TradeMetaService(this).AddTradeMeta(tradeId, metaKey, metaValue, saveChanges);
}
/// <summary>
/// 保存交易扩展信息
/// </summary>
/// <param name="dbTrade"></param>
private void SaveTradeExend(trade dbTrade)
{
RemoveEntities<trade_extend>(x => x.TradeId == dbTrade.id);
var trade_extend = dbTrade.trade_extend.Clone();
trade_extend.TradeId = dbTrade.id;
DbContext.trade_extend.Add(trade_extend);
}
/// <summary>
/// 保存交易预付金信息
/// </summary>
public void SaveTradeMargin(trade dbTrade)
{
RemoveEntities<trade_initial_margin>(x => x.TradeId == dbTrade.id);
var trade_Initial_Margin = dbTrade.trade_Initial_Margin.Clone();
trade_Initial_Margin.TradeId = dbTrade.id;
DbContext.trade_initial_margin.Add(trade_Initial_Margin);
}
private bool PrepareTrade(trade req, TradeSourceEnum dataSource, underlying_manager um)
{
bool tradeNumberGenerated = false;
req.InitialMargin = Convert.ToDouble(req.trade_Initial_Margin.MarginValue);
if (req.trade_Initial_Margin.MarginType == 0)
{
req.InitialMargin = req.StockEqvNotional == 0 ? 0 : Convert.ToDouble(req.trade_Initial_Margin.MarginValue) * req.StockEqvNotional;
};
var isAddNew = req.id == 0;
if (isAddNew)
{
req.TradeStatus = ConsTrade.新增待确认;
req.TradeSource = dataSource.ToString();
if (string.IsNullOrWhiteSpace(req.TradeNumber) || (PS.Config.Company == CompanyEnum.光大光子 && (req.TradeNumber == "STG" || req.TradeNumber == "STD" || req.TradeNumber == "FWD" || req.TradeNumber == "SPD")))
{
tradeNumberGenerated = true;
if (req.UnderlyingCode != null && req.UnderlyingCode != "")
{
PrepareUnderlying(req, um);
}
req.TradeNumber = BizLogicSingleton.Instance.GenerateTradeNumberBeforeConfirm(req, DbContext);
}
else if (DbContext.trade.Any(n => n.TradeNumber == req.TradeNumber))
{
throw new ServiceException($"交易编号'{req.TradeNumber}'已经存在");
}
else if (PS.Config.ErpElement.UpperTradeNumber)
{
req.TradeNumber = req.TradeNumber.ToUpperInvariant();
}
}
else if (!string.IsNullOrWhiteSpace(req.TradeNumber))
{
if (DbContext.trade.Any(n => n.id != req.id && n.TradeNumber == req.TradeNumber))
{
throw new ServiceException($"交易编号'{req.TradeNumber}'已经存在");
}
if (PS.Config.ErpElement.UpperTradeNumber)
{
req.TradeNumber = req.TradeNumber.ToUpperInvariant();
}
}
PrepareSingleTrade(req, dataSource, isAddNew, um);
req.PrincipalRateWrite = req.PrincipalRateWrite.IsNormalize() ? req.PrincipalRateWrite : null;
return tradeNumberGenerated;
}
//准备单个交易
private trade PrepareSingleTrade(trade req, TradeSourceEnum dataSource, bool isAddNew, underlying_manager um)
{
//准备交易基础信息
PrepareBaseInfo(req, um);
PrepareSwapTrade(req, dataSource, um);
req.OptId = UserId;
req.OptName = UserName;
req.OptDate = OptDate;
if (isAddNew)
{
req.ValidState = ConsGlobal.Valid;
req.CreateDate = DateTime.Now;
req.TradeSavedVol = PS.Config.Is厦门象屿 ? req.TradeCloseVolatility : req.TradeOpenVolatility;
if (PS.Config.ErpElement.VolMode == Configuration.VolModeEnum.VolSurface)
{
req.TradeSavedVol = req.Vol;
}
}
return req;
}
//准备互换交易
private void PrepareSwapTrade(trade req, TradeSourceEnum dataSource, underlying_manager um)
{
PrepareSwapTradeClient(req, um);
req.OriginalNotional = req.Notional;
req.BuySell = "买入";
if (req.StructureType == ClientMarginTypeEnum.多空组合.ToString())
{
req.BuySell = req.trade_extend.ExtendObj.Direction == 1 ? "买入" : "卖出";
}
else
{
var swapPosition = req.swap_positions.FirstOrDefault(x => !string.IsNullOrEmpty(x.UnderlyingCode));
req.BuySell = swapPosition.PosiDirection == 1 ? "买入" : "卖出";
var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(req.QuoteCurrency, req.SettlementCurrency, req.TradeDate.Value
, seekPreday: dataSource == TradeSourceEnum.系统交易);
var tradingFees = req.swap_positions.Where(x => !string.IsNullOrEmpty(x.UnderlyingCode)).Sum(x => x.PosiTradingFee * (x.PosiDirection == 1 ? -1 : 1));
req.TradePrice = Convert.ToDouble(tradingFees) * currencyRate;
req.SpotPrice = Convert.ToDouble(swapPosition.PosiNetPrice);
}
req.Strike = null;
req.OriginalStockEqvNotional = req.StockEqvNotional;
req.StockEqvNotionalReal = req.StockEqvNotional;
}
//互换交易
private void PrepareSwapTradeClient(trade req, underlying_manager _underlying)
{
var client = PrepareBaseTradeClient(req);
if (req.StructureType != ClientMarginTypeEnum.多空组合.ToString() && _underlying.CalcTypeIsStock() && ((client.TradingInstType & TradingInstTypeEnum.Equity) != TradingInstTypeEnum.Equity))
{
throw new ServiceException($"客户'{client.Name}'的'交易资产'属性不包括'权益',不能进行收益互换交易");
}
req.ClientName = client.Name;
req.SettlementCurrency = client.SettlementCurrency;
}
//客户信息
private Client PrepareBaseTradeClient(trade req)
{
if (req.ClientId < 1)
{
throw new ServiceException("客户信息不存在");
}
var client = DbContextFactory.GetClientDbContext(OptUser).client.FirstOrDefault(n => n.id == req.ClientId);
if (client == null)
{
throw new ServiceException("客户信息不存在");
}
if (client.ProcessStatus == "已休眠" || client.ProcessStatus == "已销户")
{
if (!client.ProcessOptDate.HasValue || client.ProcessOptDate.Value < req.TradeDate.Value)
{
var tradeDateStr = req.TradeDate.Value.ToString("yyyy-MM-dd");
var processDateStr = client.ProcessOptDate?.ToString("yyyy-MM-dd HH:mm:ss");
throw new ServiceException($"客户'{client.Name}'{client.ProcessStatus}并且处理日期{processDateStr}早于交易日期'{tradeDateStr}',无法保存交易!");
}
}
else if (client.ProcessStatus != "已开户")
{
throw new ServiceException($"客户'{client.Name}'尚未完成开户!");
}
return client;
}
//准备交易基础信息(标的、簿记、交易日期)
private void PrepareBaseInfo(trade req, underlying_manager um)
{
if (!req.TradeDate.HasValue)
{
throw new ServiceException("交易日期 必须填写");
}
if (req.TradeDate.Value.Year < 2000)
{
throw new ServiceException("交易日期 填写错误:" + req.TradeDate.Value.ToString("yyyy-MM-dd"));
}
if (QdpCalendarHelper.IsHoliday(req.TradeDate.Value) && req.TradeType != "现金流交易")
{
throw new ServiceException("交易日期不能是假日:" + req.TradeDate.Value.ToString("yyyy-MM-dd"));
}
//如果起始日没有值,默认赋值交易日
if (!req.StartDate.HasValue)
{
req.StartDate = req.TradeDate;
}
//--------------------------------------
// 标的信息
//--------------------------------------
if (req.StructureType != ClientMarginTypeEnum.多空组合.ToString())
{
PrepareUnderlying(req, um);
}
//--------------------------------------
// 簿记帐户
//--------------------------------------
if (req.AssetId < 1)
{
throw new ServiceException("请选择簿记账户");
}
var _assetUnit = DbContext.assetunit.Where(n => n.id == req.AssetId)
.Select(n => new AssetUnitDto { id = n.id, Name = n.Name, TraderIds = n.TraderIds, UserGroup = n.UserGroup }).FirstOrDefault();
if (_assetUnit == null)
{
throw new ServiceException("簿记账户不存在");
}
req.AssetBookName = _assetUnit.Name;
//--------------------------------------
// 交易员信息(如果没有交易员,查找簿记账户中的交易员)
//--------------------------------------
if (string.IsNullOrWhiteSpace(req.TraderName))
{
//有交易员id,通过id查名字
if (req.TraderId > 0)
{
req.TraderName = UserBLL.GetNameById(req.TraderId);
}
else
{
//当前登录用户作为交易员
req.TraderId = UserId;
req.TraderName = UserName;
}
}
req.TradeAmount = Convert.ToDouble(req.swap_positions.Sum(s => s.PosiQuantity));
}
//准备交易标的信息
public underlying_manager PrepareUnderlying(trade req, underlying_manager _underlying)
{
var underlyingCode = req.UnderlyingCode ?? string.Empty;
if (_underlying == null)
{
_underlying = UnderlyingDataProvider.GetUnderlying(underlyingCode);
}
if (_underlying == null)
{
throw new ServiceException("标的信息不存在:" + req.UnderlyingCode);
}
if (_underlying.IsFutures() && _underlying.MaturityDate == null)
{
throw new ServiceException("标的到期日不存在:" + req.UnderlyingCode);
}
req.UnderlyingId = _underlying.id;
req.UnderlyingCode = _underlying.UnderlyingCode;
req.VarietyId = _underlying.UnderlyingTypeId;
req.UnderlyingAssetClass = _underlying.UnderlyingType;
req.UnderlyingAssetName = _underlying.UnderlyingName;
req.MaturityDate = _underlying.IsFutures() ? _underlying.MaturityDate : null;
req.UnderlyingInstrumentType = _underlying.GetMainType();
req.CountRatio = _underlying.CountRatio;
//权益类标的关联的交易如果没有设置分红率则取标的上的分红率,如果标的分红率为空则取0
if (!req.DividendRate.HasValue && _underlying.CalcTypeIsStock())
{
req.DividendRate = _underlying.DividendRate ?? 0;
}
return _underlying;
}
/// <summary>
/// 收益互换初始预付金校验
/// </summary>
/// <param name="trade"></param>
/// <returns></returns>
private string checkMarginRate(trade trade)
{
var errorMsg = "";
var marginValue = trade.trade_Initial_Margin.MarginValue;
if (trade.trade_Initial_Margin.MarginType == (int)TradeMarginTypeEnum.固定金额)
{
marginValue = trade.StockEqvNotional == 0 ? 0 : trade.trade_Initial_Margin.MarginValue / Convert.ToDecimal(trade.StockEqvNotional);
}
//多空组合
if (ClientMarginTypeEnum.多空组合.ToString().Equals(trade.StructureType))
{
if (marginValue < 0.25m)
{
errorMsg = $"根据监管要求权益类多空组合型收益互换预付金不得低于25%,当前交易预付金率为:{marginValue * 100}%,不符合监管要求!";
}
}
else
{
//获取标的品种
var underlingData = new UnderlyingDataProvider();
var swapposition = trade.swap_positions.FirstOrDefault(x => !string.IsNullOrEmpty(x.UnderlyingCode));
trade.UnderlyingCode = swapposition == null ? "" : swapposition.UnderlyingCode;
var underlying = underlingData.GetUnderlying(trade.UnderlyingCode);
var variety = underlingData.GetVariety(underlying.UnderlyingTypeId);
if (variety == null)
{
errorMsg = $"所设置标的未对应品种,请设置对应品种后再进行交易!";
}
else
{
//股票、窄基股票指数及其产品、信用债 初始预付金比率 必须大于等于100%
if ((variety.AssetType == "个股" || (variety.AssetType == "指数" && "窄基指数".Equals(variety.IndexType))) && marginValue < 1)
{
errorMsg = $"所交易标的为股票或窄基指数及其产品、信用债,初始预付金比例不得低于100%!,当前交易预付金率为:{marginValue * 100}%,不符合监管要求!";
}
else if ((variety.AssetType == "指数" && "宽基指数".Equals(variety.IndexType)) && Convert.ToDecimal(marginValue) < 0.5m)
{
//挂钩宽基股票指数及其产品,无对应期货品种的 向单一交易对手方收取的预付金比例不得低于50%
errorMsg = $"所交易标的挂钩宽基股票指数及其产品,无对应期货品种的,初始预付金比例不得低于50%!,当前交易预付金率为:{marginValue * 100}%,不符合监管要求!";
}
else if (marginValue < Convert.ToDecimal(variety.Margin ?? 0.0))
{
//有对应期货或集中交易品种的 不得低于品种设置的最低预付金比例
errorMsg = $"所交易标的对应期货或集中交易品种的,初始预付金比例不得低于品种[{variety.VarietyName}]所设置最低预付金比例{variety.Margin * 100}%!,当前交易预付金率为:{marginValue * 100}%,不符合监管要求!";
}
}
}
return errorMsg;
}
/// <summary>
/// 标的校验
/// </summary>
/// <param name="trade"></param>
/// <exception cref="ServiceException"></exception>
private underlying_manager checkUnderlying(trade trade)
{
if (trade.StructureType == ClientMarginTypeEnum.多空组合.ToString())
{
return null;
}
var underlingData = new UnderlyingDataProvider();
var posi = trade.swap_positions.Where(x => x.PosiDirection > 0).FirstOrDefault();
if (posi == null)
{
throw new ServiceException("缺少标的信息");
}
var underlying = underlingData.GetUnderlying(posi.UnderlyingCode);
// 标的代码
if (underlying == null)
{
throw new ServiceException("标的代码 必须在系统中存在");
}
if (underlying.UnderlyingInstrumentType == "Stock" && !underlying.IsCombined())
{
var limit = LimitRangeEnum.Swap;
var limitStatus = new StockBlackWhiteService(UserInfo).GetStockBlackWhiteList(limit, out var Codes);
if (Codes != null)
{
if (limitStatus == Codes.Contains(underlying.UnderlyingCode))
{
if (limitStatus)
{
throw new ServiceException("标的代码 填写错误,存在于黑名单中,请求值:" + underlying.UnderlyingCode);
}
else
{
throw new ServiceException("标的代码 填写错误,不存在于白名单中,请求值:" + underlying.UnderlyingCode);
}
}
}
}
trade.UnderlyingCode = underlying.UnderlyingCode;
trade.UnderlyingId = underlying.id;
trade.UnderlyingAssetClass = underlying.UnderlyingType;
trade.UnderlyingInstrumentType = underlying.UnderlyingInstrumentType;
trade.UnderlyingAssetName = underlying.UnderlyingName;
return underlying;
}
#endregion
#region 互换交易查询
/// <summary>
/// 互换交易列表查询
/// </summary>
/// <param name="req"></param>
/// <param name="gsum"></param>
/// <returns></returns>
public SearchListResult<trade> SearchList(SwapTradeQueryRequest req, out tradeGridSum gsum)
{
var query = CreateTradeQuery(req);
var retListResult = new SearchListResult<trade>();
if (req.ValueDate != null)
{
var dateStr = req.ValueDate?.ToString("yyyy-MM-dd");
var _query = from t in query
join sp in DbContext.swap_position on t.id equals sp.SwapTradeId
where sp.InterestSwapInterval.Contains(dateStr) && !sp.Invalid
select t;
retListResult = _query.ToSearchList(req);
}
else
{
retListResult = query.ToSearchList(req);
}
gsum = new tradeGridSum();
if (query.Any())
{
gsum.TradePriceSum = query.Sum(q => q.TradePrice);
}
//获取销售提成信息
foreach (var item in retListResult.rows)
{
item.SalesCommission = new SalesModule.SalesCommissionDataService(OptUser).GetTradeCommissionInfo(item.id);
if (PS.Config.Is方顿 && item.SalesCommission?.Commission != null)
{
item.SalesCommission.Commission = item.SalesCommission.Commission * item.SalesCommission.SalesIds.Count();
}
}
retListResult.Sum = gsum;
return retListResult;
}
private Expression<Func<trade, bool>> BuildTradeQuery(TradeReq req)
{
var predicate = PredicateBuilder.Create<trade>(n => n.ValidState != "InValid" && n.TradeType == "收益互换");
if (req.UserAssets != null && req.UserClients != null)
{
predicate = predicate.And(n => req.UserAssets.Contains(n.AssetId) || req.UserClients.Contains(n.ClientId));
}
if (req.AssetIdList.Any())
{
predicate = predicate.And(d => req.AssetIdList.Contains(d.AssetId));
}
if (!string.IsNullOrEmpty(req.TraderNames))
{
predicate = predicate.And(d => req.TraderNamesList.Contains(d.TraderId));
}
if (!string.IsNullOrEmpty(req.TradeNumber))
{
predicate = predicate.And(d => d.TradeNumber.Contains(req.TradeNumber));
}
if (req.TradeStatusList != null)
{
predicate = predicate.And(d => req.TradeStatusList.Contains(d.TradeStatus));
}
if (req.AssetIdList.Any())
{
predicate = predicate.And(d => req.AssetIdList.Contains(d.AssetId));
}
if (req.ClientIdsInt.Any())
{
predicate = predicate.And(d => req.ClientIdsInt.Contains(d.ClientId));
}
if (req.StartDateStart != DateTime.MinValue)
{
predicate = predicate.And(d => d.StartDate >= req.StartDateStart);
}
if (req.StartDateEnd != DateTime.MinValue)
{
var StartDateTemp = req.StartDateEnd.AddDays(1);
predicate = predicate.And(d => d.StartDate < StartDateTemp);
}
if (req.ExerciseDateEnd == null)
{
req.ExerciseDateEnd = DateTime.MaxValue;
}
if (req.ExerciseDateStart == null)
{
req.ExerciseDateStart = DateTime.MinValue;
}
predicate = predicate.And(d => d.ExerciseDate >= req.ExerciseDateStart && d.ExerciseDate <= req.ExerciseDateEnd);
return predicate;
}
/// <summary>
/// 获取互换交易详情
/// </summary>
/// <param name="intid"></param>
/// <returns></returns>
public trade GetSwapTrade(int intid)
{
var tradeObj = DbContext.trade.Find(intid);
if (tradeObj == null)
{
return null;
}
//需要审批或者复核的交易都会显示行权审核提交按钮
tradeObj.MetaDic = new TradeMetaService(UserInfo).GetTradeMeta(tradeObj.id);
tradeObj.trade_Initial_Margin = DbContext.trade_initial_margin.FirstOrDefault(x => x.TradeId == intid);
if (tradeObj.trade_Initial_Margin == null)
{
tradeObj.trade_Initial_Margin = new trade_initial_margin();
}
tradeObj.trade_extend = DbContext.trade_extend.FirstOrDefault(x => x.TradeId == intid);
tradeObj.swap_positions = DbContext.swap_position.Where(x => x.SwapTradeId == intid && !x.Invalid).ToList();
tradeObj.swap_positions = tradeObj.swap_positions.Where(x => x.PosiQuantity > 0 || x.InterestDirection > 0).ToList();
var intervalPositions = tradeObj.swap_positions.Where(x => string.IsNullOrEmpty(x.UnderlyingCode) && x.IsInitial).ToList();
var intervalPositionIds = intervalPositions.Select(s => s.id).ToList();
var tradeObervations = DbContext.trade_obervation.Where(x => intervalPositionIds.Contains(x.PositionId));
foreach (var position in intervalPositions)
{
position.Obervation = tradeObervations.FirstOrDefault(s => s.PositionId == position.id);
}
var positionIds = tradeObj.swap_positions.Select(x => x.id);
tradeObj.ClientCashInCashOutList = DbContext.ClientCashInCashOut.Where(x => x.ValidState != ConsGlobal.InValid && x.TradeId == intid).OrderBy(o => o.HappenDate).ToList();
List<int> eventTyps = new List<int>() { (int)SwapEventTypeEnum.平仓, (int)SwapEventTypeEnum.互换, (int)SwapEventTypeEnum.自动互换 };
var allSwapEvents = GetSwapEvents(intid, eventTyps).OrderBy(o => o.id).ToList();//历史平仓/互换记录
tradeObj.swap_Events.AddRange(allSwapEvents);
if (tradeObj.swap_Events.Count > 0)
{
tradeObj.eod_swaps = DbContext.eod_swap.Where(x => x.SwapTradeId == intid).ToList();
tradeObj.eod_swaps.ForEach(x =>
{
var eod = tradeObj.eod_swaps.Where(t => t.ValueDate < x.ValueDate && t.id != x.id).OrderBy(o => o.ValueDate).FirstOrDefault();
if (eod != null)
{
x.CloseStartDate = eod.ValueDate;
}
else
{
x.CloseStartDate = x.ValueDate;
}
});
}
foreach (var item in tradeObj.swap_positions.Where(x => x.IsInitial))
{
if (!string.IsNullOrEmpty(item.UnderlyingCode))
{
item.underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(item.UnderlyingCode);
if (item.underlying != null && item.underlying.IsBond() && !string.IsNullOrEmpty(item.underlying.ExJson))
{
var bond = JsonHelper.Deserialize<UnderlyingBond>(item.underlying.ExJson);
item.underlying.UnderlyingFullName = bond.UnderlyingFullName;
item.underlying.UnderlyingIssuer = bond.UnderlyingIssuer;
item.underlying.IssueSize = bond.IssueSize;
}
}
}
return tradeObj;
}
/// <summary>
/// 撤回审批
/// </summary>
/// <param name="intid"></param>
public void TradeRevoke(int intid)
{
var tradeObj = DbContext.trade.Find(intid);
if (tradeObj == null)
{
return;
}
var eventTypes = new List<int>() { (int)SwapEventTypeEnum.修改交易, (int)SwapEventTypeEnum.新增交易, (int)SwapEventTypeEnum.平仓, (int)SwapEventTypeEnum.互换 };
var lastEvent = DbContext.swap_event.Where(x => x.SwapTradeId == intid && eventTypes.Contains(x.EventType) && !x.Invalid).OrderByDescending(o => o.OptTime).FirstOrDefault();
if (tradeObj.TradeStatus == ConsTrade.平仓待复核 || tradeObj.TradeStatus == ConsTrade.互换待复核)
{
tradeObj.TradeStatus = ConsTrade.确认成交;
}
else
{
if (lastEvent != null && lastEvent.EventType == (int)SwapEventTypeEnum.修改交易)
{
tradeObj.TradeStatus = ConsTrade.修改待确认;
}
else
{
tradeObj.TradeStatus = ConsTrade.新增待确认;
}
}
tradeObj.CheckStatus = null;
tradeObj.ProcessOrderId = 0;
tradeObj.IsApproval = false;
if (lastEvent != null)
{
lastEvent.Invalid = true;
}
DbContext.SaveChanges();
return;
}
private IQueryable<trade> CreateTradeQuery(SwapTradeQueryRequest req)
{
var predicate = PredicateBuilder.Create<trade>(n => n.ValidState != "InValid" && n.TradeType == "收益互换");
var query = DbContext.trade.Where(predicate);
if (req.ClientIds != null && req.ClientIds.Count > 0)
{
query = query.Where(d => req.ClientIds.Contains(d.ClientId));
}
if (req.TradeIds != null && req.TradeIds.Count > 0)
{
query = query.Where(d => req.TradeIds.Contains(d.TraderId));
}
if (req.AssetIds != null && req.AssetIds.Count > 0)
{
query = query.Where(d => req.AssetIds.Contains(d.AssetId));
}
if (req.TradeDateStart.HasValue)
{
query = query.Where(d => d.TradeDate >= req.TradeDateStart);
}
if (req.TradeDateEnd.HasValue)
{
var TradeDateTemp = req.TradeDateEnd.Value.AddDays(1);
query = query.Where(d => d.TradeDate < TradeDateTemp);
}
if (req.ExerciseDateEnd.HasValue)
{
query = query.Where(d => d.ExerciseDate <= req.ExerciseDateEnd);
}
if (req.ExerciseDateStart.HasValue)
{
query = query.Where(d => d.ExerciseDate >= req.ExerciseDateStart);
}
if (!string.IsNullOrEmpty(req.TradeNumber))
{
query = query.Where(d => d.TradeNumber.Contains(req.TradeNumber));
}
if (req.TradeStatus != null && req.TradeStatus.Count > 0)
{
query = query.Where(d => req.TradeStatus.Contains(d.TradeStatus));
}
if (string.IsNullOrEmpty(req.sidx))
{
req.sidx = "id";
req.sord = "desc";
};
query = query.OrderByDescending(s => s.OptDate);
return query;
}
#endregion
/// <summary>
/// 获取互换交易对手方 交易编码
/// </summary>
/// <param name="clientId"></param>
/// <returns></returns>
public List<SelectItem> GetSwapTradeNumbers(int clientId)
{
var tradeNumbers = DbContext.trade.Where(t => t.ClientId == clientId
&& t.TradeType == "收益互换"
&& t.TradeStatus == ConsTrade.确认成交
&& t.StructureType == ClientMarginTypeEnum.多空组合.ToString()
&& t.ValidState != "InValid").Select(s => new SelectItem { Text = s.TradeNumber, Value = s.id.ToString() }).ToList();
return tradeNumbers;
}
/// <summary>
/// 保存交易腿信息
/// </summary>
/// <param name="swap_positions">持仓腿信息</param>
/// <param name="tradeId">交易id</param>
/// <param name="startDate">成交日期</param>
public void SaveSwapPositions(List<swap_position> swap_positions, trade td)
{
var swapPositionList = DbContext.swap_position.Where(x => x.SwapTradeId == td.id);
var swapPositionIds = swap_positions.Select(s => s.id).ToList();
var intervalPositionIds = swapPositionList.Where(x => string.IsNullOrEmpty(x.UnderlyingCode) && x.IsInitial).Select(s => s.id);
var obervations = DbContext.trade_obervation.Where(o => intervalPositionIds.Contains(o.PositionId));
var originalSwapPositions = swapPositionList.Where(x => x.IsInitial);
var realTimeSwapPositions = swapPositionList.Where(x => !x.IsInitial);
var deleteSwapPositions = swapPositionList.Where(x => !swapPositionIds.Contains(x.id));
if (td.StructureType == "多空组合")
{
deleteSwapPositions = swapPositionList.Where(x => !swapPositionIds.Contains(x.id) && x.PosiDirection == 0);
realTimeSwapPositions = swapPositionList.Where(x => !swapPositionIds.Contains(x.id) && x.PosiDirection == 0 && !x.IsInitial);
var positions = swapPositionList.Where(x => x.PosiDirection > 0);
var direction = td.trade_extend.ExtendObj.Direction;
foreach (var item in positions)
{
item.PosiDirection = direction;
}
}
DbContext.swap_position.RemoveRange(realTimeSwapPositions);
DbContext.swap_position.RemoveRange(deleteSwapPositions);
DbContext.trade_obervation.RemoveRange(obervations);
foreach (var swap in swap_positions)
{
var position = originalSwapPositions.FirstOrDefault(x => x.id == swap.id);
if (position == null)
{
position = new swap_position();
}
position.ContractSize = swap.ContractSize;
position.CountRatio = swap.CountRatio;
var ratio = swap.PositionType == (int)PositionTypeFlag.Short ? -1 : 1;
position.id = swap.id;
position.PositionType = swap.PositionType;
position.PosiTradingFee = swap.PosiTradingFee;
position.PosiTradingFeeUnit = swap.PosiTradingFeeUnit;
position.PosiTradingFeePending = swap.PosiTradingFeePending;
position.UnderlyingCode = swap.UnderlyingCode;
position.UnderlyingInstrumentType = swap.UnderlyingInstrumentType;
position.PosiDirection = swap.PosiDirection;
position.PosiGrossPrice = swap.PosiGrossPrice;
position.PosiNetPrice = swap.PosiQuantity == 0 ? 0 : (swap.PosiGrossPrice + (position.PosiTradingFeePending / swap.PosiQuantity) * ratio);
position.PosiNetNoFeePrice = swap.PosiNetNoFeePrice;
position.PosiNetFeePrice = swap.PosiQuantity == 0 ? 0 : (swap.PosiNetNoFeePrice + (position.PosiTradingFeePending / swap.PosiQuantity) * ratio);
position.PosiNotionalValue = swap.PosiQuantity * swap.ContractSize;
position.PosiQuantity = swap.PosiQuantity;
position.InterestDirection = swap.InterestDirection;
position.InterestMode = swap.InterestMode;
position.InterestRateDefault = swap.InterestRateDefault;
position.InterestPrincipalFix = swap.InterestPrincipalFix;
position.InterestSwapInterval = swap.InterestSwapInterval;
position.PosiStartDate = td.StartDate.Value;
position.PosiMatuirityDate = td.ExerciseDate.Value;
position.SwapTradeId = td.id;
position.IsInitial = true;
position.OptTime = DateTime.Now;
position.OptId = UserInfo.UserId;
position.OptName = UserInfo.UserName;
position.IsAnnualized = swap.IsAnnualized;
position.HappenDate = swap.HappenDate;
position.Currency = swap.Currency;
position.InterestType = swap.InterestType;
position.FloatRate = swap.FloatRate;
position.FloatRateUnderlyingCode = swap.FloatRateUnderlyingCode;
if (position.id == 0)
{
DbContext.swap_position.Add(position);
DbContext.SaveChanges();
position.PosiNumber = $"{td.TradeNumber}-{position.id}";
}
if (swap.Obervation != null)
{
swap.Obervation.OptTime = DateTime.Now;
swap.Obervation.OptId = UserInfo.UserId;
swap.Obervation.OptName = UserInfo.UserName;
swap.Obervation.PositionId = position.id;
DbContext.trade_obervation.Add(swap.Obervation);
}
}
}
/// <summary>
/// 修改清除互换持仓所有信息
/// </summary>
/// <param name="tradeId"></param>
public void ClearSwapPositions(trade td)
{
var tradeId = td.id;
var swapFlowEvents = DbContext.swap_flow_event.Where(x => x.SwapTradeId == tradeId && x.DataState != (int)SwapFlowDateStateEnum.废弃);
var eodSwapPositions = DbContext.eod_swap_position.Where(x => x.SwapTradeId == tradeId);
var eodSwaps = DbContext.eod_swap.Where(x => x.SwapTradeId == tradeId);
var clientcashinouts = DbContext.ClientCashInCashOut.Where(x => x.TradeId == tradeId && x.Action == ClientCashInCashOut.系统操作_应付预付金);
swapFlowEvents.ForEach(x =>
{
x.DataState = (int)SwapFlowDateStateEnum.废弃;
});
//DbContext.swap_flow_event.RemoveRange(swapFlowEvents);
DbContext.eod_swap_position.RemoveRange(eodSwapPositions);
DbContext.eod_swap.RemoveRange(eodSwaps);
DbContext.ClientCashInCashOut.RemoveRange(clientcashinouts);
}
/// <summary>
/// 修改展期信息,互换观察日到期日期修改
/// </summary>
/// <param name="tradeId">互换交易id</param>
/// <param name="oldExerciseDate">旧到期日</param>
/// <param name="exerciseDate">新到期日</param>
public void UpdateObservationDay(int tradeId, DateTime oldExerciseDate, DateTime exerciseDate)
{
var dateStr = exerciseDate.ToString("yyyy-MM-dd");
var oldDateStr = oldExerciseDate.ToString("yyyy-MM-dd");
var interestPostions = DbContext.swap_position.Where(x => x.SwapTradeId == tradeId && string.IsNullOrEmpty(x.UnderlyingCode) && !x.Invalid).ToList();
interestPostions.ForEach(x =>
{
if (x.SwapIntervalList.Count == 1 && x.SwapIntervalList.Any(t => t.Date == oldExerciseDate))
{
x.InterestSwapInterval = x.InterestSwapInterval.Replace(oldDateStr, dateStr);
}
});
}
/// <summary>
/// 交易回退
/// </summary>
/// <param name="tradeId"></param>
/// <returns></returns>
public trade GetTrade(int tradeId)
{
var td = DbContext.trade.Find(tradeId);
return td;
}
/// <summary>
/// 互换交易回退
/// </summary>
/// <param name="tradeId">交易id</param>
/// <param name="valueDate">回退日期</param>
/// <param name="backToBegin">回到开仓</param>
public void TradeBack(int tradeId, DateTime valueDate, bool del = false)
{
var td = DbContext.trade.Find(tradeId);
if (td == null)
{
throw new ServiceException("交易不存在");
}
bool backToBegin = td.TradeDate == valueDate;
var swapPositions = DbContext.swap_position.Where(x => x.SwapTradeId == tradeId && !x.Invalid).ToList();
td.trade_extend = DbContext.trade_extend.FirstOrDefault(x => x.TradeId == td.id);
//展期
var swapEvent = DbContext.swap_event.Where(x => x.SwapTradeId == tradeId && x.EventType == (int)SwapEventTypeEnum.展期 && !x.Invalid && x.ValueDate <= valueDate).OrderByDescending(o => o.ValueDate).FirstOrDefault();
var trans = DbContext.Database.BeginTransaction();
try
{
var swapEventService = new SwapEventService(this);
td.TradeStatus = "确认成交";
td.ProcessOrderId = 0;
td.ProcessStatus = null;
if (backToBegin)
{
td.StockEqvNotional = td.OriginalStockEqvNotional ?? 0;
td.UnWindDate = null;
td.HasPartialUnWind = null;
SingleTradeBackToBegin(td, swapPositions);
}
else
{
TradeBackByDate(td, valueDate, swapPositions);
}
if (swapEvent != null)//展期
{
swapEventService.DeleteExtensionTime(swapEvent.id);
}
InvalidTradeOptionDatasByDate(tradeId, valueDate, backToBegin);
swapEventService.AddSwapEventDate(valueDate, tradeId, (int)SwapEventTypeEnum.回退, string.Empty, 0, false, $"交易回退至{valueDate:yyyy年MM月dd日}");
DbContext.SaveChanges();
AddPushLog(td);
if (del)
{
new TradeRiskCheckLogService(UserInfo).DeleteLog(td.TradeNumber, valueDate);//重置风控记录
}
if (backToBegin) //重置开仓记录
{
DeleteTradeCashInCashOut(tradeId);
DeleteTradeDetials(tradeId);
DbContext.SaveChanges();
if (del)
{
swapPositions.ForEach(x =>
{
x.Invalid = true;
x.OptTime = OptDate;
});
td.ValidState = "InValid";
var tradeNumber = td.TradeNumber;
td.TradeNumber = td.TradeNumber.Insert(0, "XX");
td.OptDate = OptDate;
swapEventService.AddSwapEventDate(valueDate, tradeId, (int)SwapEventTypeEnum.删除, string.Empty, 0, false);
DbContext.SaveChanges();
trans.Commit();
return;
}
//new SwapEodPositionService(this).InitSaveEodSwapPosition(td);
var happenDate = td.TradeDate.Value;
if (td.trade_extend != null && !td.trade_extend.ExtendObj.NeedOpenFee)
{
happenDate = td.ExerciseDate.Value;
}
ResetMarginAmount(td, happenDate, swapPositions);
}
//new SwapFlowService(this).DeleteSwapFlow(tradeId, valueDate, backToBegin);
trans.Commit();
}
catch (Exception ex)
{
trans.Rollback();
throw ex;
}
finally
{
trans.Dispose();
}
}
/// <summary>
/// 重新补录预付金记录
/// </summary>
/// <param name="td"></param>
/// <param name="happenDate"></param>
/// <param name="swapPositions"></param>
private void ResetMarginAmount(trade td, DateTime happenDate, List<swap_position> swapPositions)
{
new ClientCashInCashOutService(this).SaveSwapTradeClientCash(td, td.TradePrice ?? 0, happenDate, 0);
foreach (var marginPositions in swapPositions.Where(x => x.HappenDate != null && x.IsInitial && x.InterestMode != (int)InterestModeEnum.追加预付金).GroupBy(g => g.HappenDate))
{
var marginHappenDate = marginPositions.Key;
foreach (var marginPosition in marginPositions)
{
marginPosition.Obervation = DbContext.trade_obervation.FirstOrDefault(x => x.PositionId == marginPosition.id);
bool generateMargin = true;
if (marginPosition.Obervation != null)
{
if (!marginPosition.Obervation.IsDeductPrincipal)
{
generateMargin = false;
}
}
if (generateMargin)
{
var marginAmount = marginPosition.InterestPrincipalFix * (marginPosition.InterestDirection == 1 ? -1 : 1);
new ClientCashInCashOutService(this).SaveSwapTradeClientCash(td, Convert.ToDouble(marginAmount), marginHappenDate.HasValue ? marginHappenDate.Value : td.TradeDate.Value, marginPosition.id, ClientCashInCashOut.系统操作_应付预付金);
}
}
}
}
/// <summary>
/// 获取持仓腿信息
/// </summary>
/// <param name="tradeIds">叫一起集合</param>
/// <param name="_underlyingCodeFilterSet">标的代码</param>
/// <returns></returns>
public IEnumerable<swap_position> GetSwapPositions(List<int> tradeIds, HashSet<string> _underlyingCodeFilterSet)
{
Expression<Func<swap_position, bool>> expression = x => tradeIds.Contains(x.SwapTradeId) && !string.IsNullOrEmpty(x.UnderlyingCode) && x.IsInitial && !x.Invalid;
if (_underlyingCodeFilterSet != null)
{
expression = expression.And(x => _underlyingCodeFilterSet.Contains(x.UnderlyingCode));
}
return DbContext.swap_position.Where(expression);
}
/// <summary>
/// 单标的回退到初始
/// </summary>
/// <param name="td"></param>
/// <param name="swapPositions"></param>
private void SingleTradeBackToBegin(trade td, List<swap_position> swapPositions)
{
var positions = swapPositions.Where(x => !x.IsInitial).ToList();
var marginPositions = swapPositions.Where(x => x.IsInitial && x.InterestMode == (int)InterestModeEnum.追加预付金).ToList();
var orignPositions = swapPositions.Where(x => x.IsInitial && x.InterestMode != (int)InterestModeEnum.追加预付金).ToList();
DbContext.swap_position.RemoveRange(positions);
DbContext.swap_position.RemoveRange(marginPositions);
td.TradeAmount = 0;
foreach (var item in orignPositions)
{
var interest = item.Clone();
interest.id = 0;
interest.PositionId = item.id;
interest.IsInitial = false;
td.TradeAmount = td.TradeAmount + Convert.ToDouble(interest.PosiQuantity);
DbContext.swap_position.Add(interest);
}
}
/// <summary>
/// 回到某个事件之前
/// </summary>
/// <param name="td"></param>
/// <param name="swapEvent"></param>
private void TradeBackByDate(trade td, DateTime valueDate, List<swap_position> swapPositions)
{
SwapEodPositionService eodPositionService = new SwapEodPositionService(this);
SwapDealService swapDealService = new SwapDealService(this);
var preDay = valueDate.AddDays(-1);
var eodSwapPositionList = DbContext.eod_swap_position.Where(x => x.ValueDate == preDay && x.SwapTradeId == td.id).ToList();
var swapFlowEvents = DbContext.swap_flow_event.Where(x => x.SwapTradeId == td.id && x.EventDate >= valueDate && x.DataState > (int)SwapFlowDateStateEnum.废弃).ToList();
var positions = swapPositions.Where(x => x.PosiDirection > 0 && !x.IsInitial).ToList();
foreach (var posi in positions)
{
var eodPosi = eodSwapPositionList.FirstOrDefault(x => x.PositionId == posi.PositionId);
if (eodPosi != null)
{
posi.InterestAmount = eodPosi.RealizedInterest;
posi.InterestFeePending = eodPosi.InterestFeePending;
posi.PosiTradingFeePending = eodPosi.PosiFeePending;
posi.PosiDividendIncome = eodPosi.PosiDividendSum;
posi.PosiQuantity = eodPosi.PosiQuantity;
posi.PosiNetPrice = eodPosi.PosiNetPrice;
posi.PosiGrossPrice = eodPosi.PosiGrossPrice;
posi.PosiNetFeePrice = eodPosi.PosiNetFeePrice;
posi.PosiNetNoFeePrice = eodPosi.PosiNetNoFeePrice;
posi.PosiNotionalValue = eodPosi.PosiNotionalValue;
if (posi.PosiDirection > 0)
{
td.StockEqvNotional = Convert.ToDouble(posi.PosiNotionalValue);
td.TradeAmount = Convert.ToDouble(posi.PosiQuantity);
}
}
}
td.UnWindDate = null;
td.UnWindNotional = null;
//DbContext.swap_position.RemoveRange(positions);
//swapDealService.UpdateInitalPosition(valueDate,td, swapPositions, swapFlowEvents);
DbContext.swap_flow_event.RemoveRange(swapFlowEvents);
var clientCashs = DbContext.ClientCashInCashOut.Where(x => x.TradeId == td.id && x.HappenDate >= valueDate).ToList();
DbContext.ClientCashInCashOut.RemoveRange(clientCashs);
DbContext.SaveChanges();
var marginPosis = swapPositions.Where(x => x.HappenDate <= valueDate || x.InterestMode == (int)InterestModeEnum.初始预付金).ToList();
ResetMarginAmount(td, valueDate, marginPosis);
}
/// <summary>
/// 多空组合回到初始
/// </summary>
/// <param name="swapPositions"></param>
private void LongshortTradeBackToBegin(List<swap_position> swapPositions)
{
var positions = swapPositions.Where(x => !x.IsInitial || x.InterestDirection == 0).ToList();
var orignPositions = swapPositions.Where(x => x.IsInitial && x.InterestDirection > 0).ToList();
foreach (var item in positions)
{
item.Invalid = true;
}
//DbContext.swap_position.RemoveRange(positions);
foreach (var item in orignPositions)
{
var interest = item.Clone();
interest.id = 0;
interest.PositionId = item.id;
interest.IsInitial = false;
DbContext.swap_position.Add(interest);
}
}
/// <summary>
/// 废弃或删除回退日期之后的数据
/// </summary>
/// <param name="tradeId"></param>
/// <param name="valueDate"></param>
private void InvalidTradeOptionDatasByDate(int tradeId, DateTime valueDate, bool backToBegin)
{
var swapEvents = DbContext.swap_event.Where(x => !x.Invalid && x.SwapTradeId == tradeId && x.ValueDate >= valueDate).ToList();
var swapEodPositions = DbContext.eod_swap_position.Where(x => x.SwapTradeId == tradeId && x.ValueDate >= valueDate);
var swapEods = DbContext.eod_swap.Where(x => x.SwapTradeId == tradeId && x.ValueDate >= valueDate);
var swapFlowEvents = DbContext.swap_flow_event.Where(x => x.SwapTradeId == tradeId && x.EventDate >= valueDate);
var firstConfirm = false;
swapEvents.ForEach(x =>
{
if (backToBegin && !firstConfirm && x.EventType == (int)SwapEventTypeEnum.确认交易)
{
firstConfirm = true;
}
else
{
x.Invalid = true;
}
});
swapFlowEvents.ForEach(x =>
{
if (backToBegin && x.EventType == (int)SwapFlowEventTypeEnum.开仓)
{
}
else
{
x.DataState = (int)SwapFlowDateStateEnum.废弃;
}
});
//DbContext.swap_flow_event.RemoveRange(swapFlowEvents);
DbContext.eod_swap_position.RemoveRange(swapEodPositions);
DbContext.eod_swap.RemoveRange(swapEods);
}
}
}