using NPOI.SS.Formula.Functions; using System.Linq.Expressions; using System.Reflection; using YLErp.Commons; using YLErp.Modules.TradeModule.AccumulatorOptionModule; namespace YLErp.Modules.TradeModule { /// /// 交易复核 /// public class TradeCheckUtil { private static readonly OtcOptionTradeFull tf; static TradeCheckUtil() { tf = null; } #region---交易要比较差异的字段---- /// /// 交易要比较差异的字段 /// public static IEnumerable GetCheckFields(trade td) { var fields = new List() { nameof(td.ClientId), nameof(td.SettlementDate), nameof(td.BuySell), nameof(td.ExerciseMode), nameof(td.TradeSinglePrice), nameof(td.TradeType), nameof(td.ParticipationRate), nameof(td.OriginalPrincipalSum), nameof(td.PrincipalRateWrite), nameof(td.SinglePrincipalWrite), nameof(td.OptionType), nameof(td.SettlementType), nameof(td.TradeDate), nameof(td.ExerciseDate), nameof(td.IsAnnualized), nameof(td.AnnualizeFactor), nameof(td.IsMoneynessOption), nameof(td.Strike), nameof(td.PremiumRate), nameof(td.SpotPrice), nameof(td.TradePrice), nameof(td.Notional), nameof(td.TradeAmount), nameof(td.StockEqvNotional), nameof(td.StockEqvNotionalReal), nameof(td.ExtendInfo), "TradeAmountV", }; var tradeType = td.TradeType == "结构化交易" ? td.StructureType : td.TradeType; switch (tradeType) { case "亚式期权": { trade_asian_option ag; fields.AddRange(new[] { nameof(ag.PayoffType), nameof(ag.SettleMode), nameof(ag.StrikeType), nameof(ag.AveragingPeriodStartDate), nameof(ag.EnhancedPrice) }); } break; case "二元期权": { trade_binary_option ag; fields.AddRange(new[] { nameof(ag.PayoffType), nameof(ag.CashOrNothingAmount), nameof(ag.CashOrNothingAmountRate), nameof(tf.BarrierHigh), nameof(ag.CashOrNothingAmountHigh), nameof(ag.CashOrNothingAmountHighRate), nameof(ag.MonitorType), nameof(ag.RebateType), }); } break; case "障碍期权": { trade_barrier_option ag; fields.AddRange(new[] { nameof(ag.BarrierType), nameof(tf.BarrierLow), nameof(tf.BarrierHigh), nameof(ag.Rebate), nameof(ag.RebateRate), nameof(ag.RebateHigh), nameof(ag.RebateHighRate), nameof(ag.BarrierShift), nameof(tf.MonitorType), nameof(ag.RebateType), }); } break; case "双鲨期权": { trade_double_sharkfin_option ag; fields.AddRange(new[] { nameof(ag.BarrierLow), nameof(ag.BarrierHigh), nameof(ag.StrikeHigh), nameof(ag.Rebate), nameof(ag.RebateHigh), nameof(ag.RebateRate), nameof(ag.RebateHighRate), nameof(tf.MonitorType), nameof(ag.RebateType), nameof(ag.CallParticipationRate), nameof(ag.PutParticipationRate), }); } break; case "区间累积期权": { trade_rangeaccrual ag; fields.AddRange(new[] { nameof(ag.LowerRange), nameof(ag.UpperRange), nameof(ag.BonusRate) }); } break; case "收益增强结构": { trade_underlying_enhance ag; fields.AddRange(new[] { nameof(ag.AnnualizedEnhanceRate) }); } break; case "气囊结构": { trade_airbag ag; fields.AddRange(new[] { nameof(tf.BarrierLow), nameof(ag.IsDiscreteMonitored), nameof(ag.KIParticipationRate), nameof(ag.NotKIParticipationRate), nameof(ag.HasPayoffLimit), nameof(ag.HighStrike) }); } break; case "雪球期权": { trade_snowball ag; fields.AddRange(new[] { nameof(ag.KOBarrier), nameof(ag.AnnualizedPremiumRate), nameof(ag.KIBarrier), nameof(ag.KORebate), nameof(ag.Coupon), nameof(ag.SpreadStrikeAtKO1), nameof(ag.SpreadStrikeAtKO), nameof(tf.SpreadStrike1), nameof(tf.SpreadStrike), nameof(ag.IsFixedCoupon), nameof(ag.KORebateType), nameof(ag.KOPayoffType), nameof(ag.KIPayoffType), nameof(ag.IsAnnualized2), nameof(ag.AnnualizeFactor2), nameof(ag.IsInitialKnockedIn), }); } break; case "凤凰期权": { trade_autocall ag; fields.AddRange(new[] { nameof(ag.KOBarrier), nameof(ag.KIBarrier), nameof(ag.CouponBarrier), nameof(ag.Coupon), nameof(ag.SpreadStrike1), nameof(ag.SpreadStrike), nameof(ag.IsFixedCoupon), nameof(ag.KIPayoffType), nameof(ag.CouponPayType), nameof(ag.IncludeCouponAfterKI), nameof(ag.IsAnnualized2), nameof(ag.AnnualizeFactor2), }); } break; case "累计期权": { trade_accumulator_option ag; fields.AddRange(new[] { nameof(ag.KOBarrier), nameof(ag.AccumuType), nameof(ag.PayoffType), nameof(ag.Coupon), nameof(ag.Coupon2), nameof(ag.CouponDayCount), nameof(ag.IsFixedCoupon), nameof(ag.CallMultiplier), nameof(ag.PutMultiplier), nameof(ag.EarlyTerminate), nameof(ag.SettlementMode), nameof(tf.StrikeGearingFactor), nameof(ag.AccumuTradeAmount), nameof(ag.ForwardPriceType), nameof(ag.ForwardDateType), nameof(ag.Strike2), nameof(ag.Strike3), nameof(ag.Multiplier), nameof(ag.Multiplier2), nameof(ag.Multiplier3), nameof(ag.SettlementMode2), nameof(ag.ForwardPriceType2), nameof(ag.ForwardDateType2), nameof(ag.SettlementMode3), nameof(ag.ForwardPriceType3), nameof(ag.ForwardDateType3), nameof(ag.AccumulatorStructureType), }); } break; case "现金流交易": { trade_cashflow ag; fields.AddRange(new[] { nameof(ag.ProfitRate), nameof(ag.DepositType), nameof(ag.RateType), nameof(tf.CouponDayCount), nameof(ag.PrepayRatio), }); } break; case "Risky期权": { trade_risky_option ridky; fields.AddRange(new[] { nameof(ridky.Strike1), nameof(ridky.Strike2), nameof(ridky.Strike3), }); } break; } return fields; } #endregion /// /// 获取差异 /// public static HashSet GetDiffs(trade sourceTrade, trade reviewTrade) { BLL.tradeBLL.SetFieldsByTradeType(sourceTrade); var diffList = new HashSet(StringComparer.OrdinalIgnoreCase); var tradeType = sourceTrade.TradeType == "结构化交易" ? sourceTrade.StructureType : sourceTrade.TradeType; if (tradeType == "自定义交易") { CustomTradeCheck(sourceTrade, reviewTrade, diffList); } else if (tradeType == "远期") { ForwardTradeCheck(sourceTrade, reviewTrade, diffList); } else { BaseTradeCheck(sourceTrade, reviewTrade, diffList); switch (tradeType) { case "香草期权": case "合成价差期权": break; case "亚式期权": AsianTradeCheck(sourceTrade, reviewTrade, diffList); break; case "二元期权": BinaryTradeCheck(sourceTrade, reviewTrade, diffList); break; case "障碍期权": BarrierTradeCheck(sourceTrade, reviewTrade, diffList); break; case "双鲨期权": DoubleSharkTradeCheck(sourceTrade, reviewTrade, diffList); break; case "区间累积期权": RangeAccrualTradeCheck(sourceTrade, reviewTrade, diffList); break; case "收益增强结构": UnderlyingEnhanceTradeCheck(sourceTrade, reviewTrade, diffList); break; case "气囊结构": AirBagTradeCheck(sourceTrade, reviewTrade, diffList); break; case "雪球期权": SnowballTradeCheck(sourceTrade, reviewTrade, diffList); break; case "凤凰期权": AutocallTradeCheck(sourceTrade, reviewTrade, diffList); break; case "累计期权": AccumulatorTradeCheck(sourceTrade, reviewTrade, diffList); break; case "现金流交易": CashflowTradeCheck(sourceTrade, reviewTrade, diffList); break; case "Risky期权": RiskyTradeCheck(sourceTrade, reviewTrade, diffList); break; default: if (sourceTrade.IsGroup == 1) { OtherGroupCheck(sourceTrade, reviewTrade, diffList); } break; } } if (diffList.Contains(nameof(trade.ExtendInfo))) { foreach (var item in sourceTrade.Propertys) { var obj = reviewTrade.Propertys.FirstOrDefault(O => O.name == item.name); if (obj == null || obj.value != item.value) { diffList.Add($"{item.name}"); } } } return diffList; } #region----期权基本要素---- /// /// 期权基本要素 /// private static void BaseTradeCheck(trade sourceTrade, trade reviewTrade, HashSet diffList) { var innerCheck = new InnerCheck(sourceTrade, reviewTrade); var t = sourceTrade; innerCheck.AddCheckFields(new CheckField[] { nameof(t.ClientId), nameof(t.ParticipationRate), nameof(t.BuySell), nameof(t.ExerciseMode), nameof(t.OptionType), nameof(t.SettlementType), nameof(t.TradeDate), nameof(t.ExerciseDate), nameof(t.SettlementDate), nameof(t.SpotPrice), nameof(t.IsAnnualized), }); if (!string.IsNullOrEmpty(sourceTrade.ExtendInfo) && sourceTrade.ExtendInfo != "[]") { innerCheck.AddCheckFields(nameof(t.ExtendInfo)); } if (reviewTrade.TradeType != "现金流交易") { innerCheck.AddCheckFields(new CheckField(nameof(t.IsMoneynessOption)) { RecheckIfDiff = () => sourceTrade.IsMoneynessOption == "是" || reviewTrade.IsMoneynessOption == "是" }); } if (reviewTrade.TradeType != "雪球期权" && reviewTrade.TradeType != "凤凰期权") { innerCheck.AddCheckFields(nameof(t.Strike)); } if (reviewTrade.TradeType != "累计期权") { innerCheck.AddCheckFields(nameof(t.TradePrice)); if (reviewTrade.IsUsePremiumRate == true) { innerCheck.AddCheckFields(new CheckField(nameof(t.StockEqvNotional)) { NumberPrecision = OtcFormatHelper.GetMaxPrecision(OtcFormatFlag.StockEqvNotional) } , new CheckField(nameof(t.StockEqvNotionalReal)) { NumberPrecision = OtcFormatHelper.GetMaxPrecision(OtcFormatFlag.StockEqvNotional) }); } else { innerCheck.AddCheckFields(nameof(t.Notional), nameof(t.TradeAmount)); } } else { if (sourceTrade.IsTradePricePayType) { innerCheck.AddCheckFields(nameof(t.TradePrice)); } else { innerCheck.AddCheckFields(nameof(t.TradeSinglePrice)); } } if (reviewTrade.IsTradePricePayType == true || reviewTrade.TradeType == "现金流交易") { innerCheck.AddCheckFields(nameof(t.TradePrice)); } else { innerCheck.AddCheckFields(reviewTrade.IsUsePremiumRate == true ? nameof(t.PremiumRate) : nameof(t.TradeSinglePrice)); } if (sourceTrade.IsAnnualized) { innerCheck.AddCheckFields(nameof(t.AnnualizeFactor)); } innerCheck.AddCheckFields(new CheckField(nameof(t.OriginalPrincipalSum)) { NumberPrecision = 2 }); innerCheck.GetDiffs(diffList); #region 对TradeAmountV 进行验证 判断diffList列表是否含有 有效成交数量 tw 2021/12/22 if (diffList.Contains(nameof(t.TradeAmount))) { diffList.Add("TradeAmountV"); } if (diffList.Contains(nameof(t.AnnualizeFactor))) { if (reviewTrade.IsAnnualized) { var arr1 = sourceTrade.MetaDic["AnnualizeFactor"].Split('/'); var arr2 = reviewTrade.MetaDic["AnnualizeFactor"].Split('/'); if (arr1.Length == 2 && arr2.Length == 2) { if (arr1[0] != arr2[0]) { diffList.Add("AnnualizeFactorL"); } if (arr1[1] != arr2[1]) { diffList.Add("AnnualizeFactorR"); } } diffList.Remove("AnnualizeFactor"); } } #endregion } #endregion #region----二元期权---- private static void BinaryTradeCheck(trade sourceTrade, trade reviewTrade, HashSet diffList) { var src = sourceTrade.trade_binary_option; var rev = reviewTrade.trade_binary_option; var innerCheck = new InnerCheck(src, rev); innerCheck.AddCheckFields(new CheckField[] { nameof(src.PayoffType) }); if (sourceTrade.ExerciseMode == "American") { diffList.Remove(nameof(sourceTrade.OptionType)); innerCheck.AddCheckFields(nameof(src.MonitorType)); if (!src.PayoffType.Contains("NoTouch")) { innerCheck.AddCheckFields(nameof(src.RebateType)); } } if (reviewTrade.IsUsePremiumRate == true) { innerCheck.AddCheckFields(nameof(src.CashOrNothingAmountRate)); } else { innerCheck.AddCheckFields(nameof(src.CashOrNothingAmount)); } if (src.PayoffType.IndexOf("Double") >= 0) { innerCheck.AddCheckFields(new CheckField(nameof(src.UpperBarrier), nameof(tf.BarrierHigh))); if (reviewTrade.IsUsePremiumRate == true) { innerCheck.AddCheckFields(nameof(src.CashOrNothingAmountHighRate)); } else { innerCheck.AddCheckFields(nameof(src.CashOrNothingAmountHigh)); } } innerCheck.GetDiffs(diffList); } #endregion #region----亚式期权---- private static void AsianTradeCheck(trade sourceTrade, trade reviewTrade, HashSet diffList) { var src = sourceTrade.trade_asian_option; var rev = reviewTrade.trade_asian_option; var innerCheck = new InnerCheck(src, rev); innerCheck.AddCheckFields(new CheckField[] { nameof(src.AveragingPeriodStartDate), nameof(src.StrikeType), nameof(src.PayoffType), }); if (rev.PayoffType == "EnhancedArithmeticAverage") { innerCheck.AddCheckFields(nameof(src.SettleMode)); innerCheck.AddCheckFields(nameof(src.EnhancedPrice)); } innerCheck.GetDiffs(diffList); } #endregion #region----障碍期权---- private static void BarrierTradeCheck(trade sourceTrade, trade reviewTrade, HashSet diffList) { var src = sourceTrade.trade_barrier_option; var rev = reviewTrade.trade_barrier_option; var innerCheck = new InnerCheck(src, rev); innerCheck.AddCheckFields(new CheckField[] { new CheckField(nameof(src.BarrierPrice),nameof(tf.BarrierLow)), new CheckField(nameof(src.UpperBarrierPrice),nameof(tf.BarrierHigh)), nameof(src.BarrierShift), nameof(src.BarrierType), new CheckField(nameof(src.Discrete), nameof(tf.MonitorType)), //new CheckField(nameof(src.ObservationDates), ObservationRate), nameof(src.RebateType), }); if (reviewTrade.IsUsePremiumRate == true) { innerCheck.AddCheckFields(nameof(src.RebateRate), nameof(src.RebateHighRate)); } else { innerCheck.AddCheckFields(nameof(src.Rebate), nameof(src.RebateHigh)); } innerCheck.GetDiffs(diffList); } #endregion #region----双鲨期权---- private static void DoubleSharkTradeCheck(trade sourceTrade, trade reviewTrade, HashSet diffList) { var src = sourceTrade.trade_double_sharkfin_option; var rev = reviewTrade.trade_double_sharkfin_option; var innerCheck = new InnerCheck(src, rev); innerCheck.AddCheckFields(new CheckField[] { nameof(src.BarrierLow), nameof(src.BarrierHigh), nameof(src.StrikeHigh), nameof(src.RebateType), nameof(src.CallParticipationRate), nameof(src.PutParticipationRate), new CheckField(nameof(src.Discrete),nameof(tf.MonitorType)) }); if (reviewTrade.IsUsePremiumRate == true) { innerCheck.AddCheckFields(nameof(src.RebateRate), nameof(src.RebateHighRate)); } else { innerCheck.AddCheckFields(nameof(src.Rebate), nameof(src.RebateHigh)); } innerCheck.GetDiffs(diffList); } #endregion #region----区间累积---- private static void RangeAccrualTradeCheck(trade sourceTrade, trade reviewTrade, HashSet diffList) { var src = sourceTrade.trade_rangeaccrual; var rev = reviewTrade.trade_rangeaccrual; var innerCheck = new InnerCheck(src, rev); innerCheck.AddCheckFields(new CheckField[] { nameof(src.LowerRange), nameof(src.UpperRange), nameof(src.BonusRate), }); innerCheck.GetDiffs(diffList); //remove diff diffList.Remove(nameof(tf.OptionType)); } #endregion #region----收益增强结构---- private static void UnderlyingEnhanceTradeCheck(trade sourceTrade, trade reviewTrade, HashSet diffList) { var src = sourceTrade.trade_underlying_enhance; var rev = reviewTrade.trade_underlying_enhance; var innerCheck = new InnerCheck(src, rev); innerCheck.AddCheckFields(new CheckField[] { nameof(src.AnnualizedEnhanceRate), }); innerCheck.GetDiffs(diffList); //remove diff diffList.Remove(nameof(tf.OptionType)); } #endregion #region----气囊结构---- private static void AirBagTradeCheck(trade sourceTrade, trade reviewTrade, HashSet diffList) { var src = sourceTrade.trade_airbag; var rev = reviewTrade.trade_airbag; var innerCheck = new InnerCheck(src, rev); innerCheck.AddCheckFields(new CheckField[] { nameof(src.IsDiscreteMonitored), nameof(src.KIParticipationRate), nameof(src.NotKIParticipationRate), nameof(src.HasPayoffLimit), nameof(src.HighStrike), new CheckField(nameof(src.Barrier), nameof(tf.BarrierLow)) }); innerCheck.GetDiffs(diffList); //remove diff diffList.Remove(nameof(tf.OptionType)); } #endregion #region----雪球期权---- private static void SnowballTradeCheck(trade sourceTrade, trade reviewTrade, HashSet diffList) { var src = sourceTrade.trade_snowball; var rev = reviewTrade.trade_snowball; var innerCheck = new InnerCheck(src, rev); innerCheck.AddCheckFields(new CheckField[] { nameof(src.KOBarrier), nameof(src.Coupon), nameof(src.KORebateType), nameof(src.KOPayoffType), nameof(src.KIPayoffType), nameof(src.IsAnnualized2), nameof(src.AnnualizeFactor2), }); if (src.KOPayoffType == KOPayoffTypeEnum.Rebate) { innerCheck.AddCheckFields(nameof(src.AnnualizedPremiumRate), nameof(src.KORebate), nameof(src.IsFixedCoupon)); } else { innerCheck.AddCheckFields(nameof(src.SpreadStrikeAtKO1)); } if (src.KOPayoffType == KOPayoffTypeEnum.ToSpreadOption) { innerCheck.AddCheckFields(nameof(src.SpreadStrikeAtKO)); } if (src.KIPayoffType != KIPayoffTypeEnum.None) { innerCheck.AddCheckFields(new CheckField(nameof(src.SpreadStrikeAtMaturity1), nameof(tf.SpreadStrike1))); } if (src.KIPayoffType == KIPayoffTypeEnum.ToPutSpreadOption || src.KIPayoffType == KIPayoffTypeEnum.ToCallSpreadOption) { innerCheck.AddCheckFields(new CheckField(nameof(src.SpreadStrikeAtMaturity), nameof(tf.SpreadStrike))); } innerCheck.GetDiffs(diffList); //remove diff diffList.Remove(nameof(tf.IsAnnualized)); diffList.Remove(nameof(tf.AnnualizeFactor)); if (sourceTrade.OptionType != reviewTrade.OptionType && !diffList.Contains(nameof(tf.KOPayoffType)) && (src.KOPayoffType == KOPayoffTypeEnum.ToSpreadOption || src.KOPayoffType == KOPayoffTypeEnum.ToOption)) { diffList.Add("KOPayoffType"); } if (diffList.Contains(nameof(tf.AnnualizeFactor2))) { var arr1 = sourceTrade.MetaDic["AnnualizeFactor2"].Split('/'); var arr2 = reviewTrade.MetaDic["AnnualizeFactor2"].Split('/'); if (arr1.Length == 2 && arr2.Length == 2) { if (arr1[0] != arr2[0]) { diffList.Add("AnnualizeFactor2L"); } if (arr1[1] != arr2[1]) { diffList.Add("AnnualizeFactor2R"); } } diffList.Remove("AnnualizeFactor2"); } } #endregion #region----凤凰期权---- private static void AutocallTradeCheck(trade sourceTrade, trade reviewTrade, HashSet diffList) { var src = sourceTrade.trade_autocall; var rev = reviewTrade.trade_autocall; var innerCheck = new InnerCheck(src, rev); innerCheck.AddCheckFields(new CheckField[] { nameof(src.KOBarrier), nameof(src.KIBarrier), nameof(src.CouponBarrier), nameof(src.Coupon), nameof(src.IsFixedCoupon), nameof(src.KIPayoffType), nameof(src.CouponPayType), nameof(src.IncludeCouponAfterKI), nameof(src.IsAnnualized2), nameof(src.AnnualizeFactor2), }); if (src.KIPayoffType != KIPayoffTypeEnum.None) { innerCheck.AddCheckFields(nameof(src.SpreadStrike1)); } if (src.KIPayoffType == KIPayoffTypeEnum.ToPutSpreadOption) { innerCheck.AddCheckFields(nameof(src.SpreadStrike)); } innerCheck.GetDiffs(diffList); //remove diff diffList.Remove(nameof(tf.IsAnnualized)); diffList.Remove(nameof(tf.AnnualizeFactor)); } #endregion #region----累计期权---- private static void AccumulatorTradeCheck(trade sourceTrade, trade reviewTrade, HashSet diffList) { var src = sourceTrade.trade_accumulator_option; var rev = reviewTrade.trade_accumulator_option; var innerCheck = new InnerCheck(src, rev); innerCheck.AddCheckFields(new CheckField[] { nameof(src.KOBarrier), nameof(src.AccumulatorStructureType), nameof(src.PayoffType), nameof(src.EarlyTerminate), nameof(src.SettlementMode), nameof(src.AccumuTradeAmount) }); if (src.AccumulatorStructureType == AccumulatorStructureTypeEnum.Segmented) { innerCheck.AddCheckFields(new CheckField[] { nameof(src.Strike2), nameof(src.Strike3), nameof(src.SettlementMode2), nameof(src.SettlementMode3), }); if (src.PayoffType == "固定") { innerCheck.AddCheckFields(new CheckField[] { nameof(src.Coupon), nameof(src.Coupon2), nameof(src.CouponDayCount), nameof(src.IsFixedCoupon), }); if (sourceTrade.OptionType == "看涨") { innerCheck.AddCheckFields(new CheckField[] { nameof(src.Multiplier), }); } else { innerCheck.AddCheckFields(new CheckField[] { nameof(src.Multiplier3), }); } } else { innerCheck.AddCheckFields(new CheckField[] { nameof(src.Multiplier), nameof(src.Multiplier2), nameof(src.Multiplier3), }); } if (src.SettlementMode == "实物交割") { innerCheck.AddCheckFields(new CheckField[] { nameof(src.ForwardPriceType), nameof(src.ForwardDateType) }); } if (src.SettlementMode2 == "实物交割") { innerCheck.AddCheckFields(new CheckField[] { nameof(src.ForwardPriceType2), nameof(src.ForwardDateType2) }); } if (src.SettlementMode3 == "实物交割") { innerCheck.AddCheckFields(new CheckField[] { nameof(src.ForwardPriceType3), nameof(src.ForwardDateType3) }); } } else { innerCheck.AddCheckFields(new CheckField[] { nameof(src.AccumuType) }); if (PS.Config.ErpElement.AccumulatorShowMultiplier1) { if (ConsGlobal.CallPut.IsCall(reviewTrade.CallPut)) { src.StrikeGearingFactor = src.PutMultiplier; } else { src.StrikeGearingFactor = src.CallMultiplier; } innerCheck.AddCheckFields(new CheckField[] { nameof(src.StrikeGearingFactor) }); } else { innerCheck.AddCheckFields(new CheckField[] { nameof(src.CallMultiplier), nameof(src.PutMultiplier) }); } if (src.PayoffType == "固定") { innerCheck.AddCheckFields(new CheckField[] { nameof(src.Coupon), nameof(src.CouponDayCount), nameof(src.IsFixedCoupon), }); } if (src.SettlementMode == "实物交割") { innerCheck.AddCheckFields(new CheckField[] { nameof(src.ForwardPriceType), nameof(src.ForwardDateType) }); } } innerCheck.GetDiffs(diffList); } #endregion #region----现金流交易---- private static void CashflowTradeCheck(trade sourceTrade, trade reviewTrade, HashSet diffList) { var src = sourceTrade.trade_cashflow; var rev = reviewTrade.trade_cashflow; var innerCheck = new InnerCheck(src, rev); innerCheck.AddCheckFields(new CheckField[] { nameof(src.ProfitRate), nameof(src.DepositType), nameof(src.PrepayRatio), nameof(src.RateType), new CheckField(nameof(src.ProfitDayCount), nameof(tf.CouponDayCount)) }); innerCheck.GetDiffs(diffList); //remove diff diffList.Remove(nameof(tf.ExerciseMode)); diffList.Remove(nameof(tf.StockEqvNotionalReal)); } #endregion #region----Risky交易---- private static void RiskyTradeCheck(trade sourceTrade, trade reviewTrade, HashSet diffList) { var src = sourceTrade.trade_risky_option; var rev = reviewTrade.trade_risky_option; var innerCheck = new InnerCheck(src, rev); innerCheck.AddCheckFields(new CheckField[] { nameof(src.Strike1), nameof(src.Strike2), nameof(src.Strike3) }); innerCheck.GetDiffs(diffList); //remove diff } #endregion #region----自定义交易---- private static void CustomTradeCheck(trade sourceTrade, trade reviewTrade, HashSet diffList) { var src = sourceTrade; var rev = reviewTrade; var innerCheck = new InnerCheck(src, rev); innerCheck.AddCheckFields(new CheckField[] { nameof(src.ClientId), nameof(src.BuySell), nameof(src.TradeDate), nameof(src.ExerciseDate), nameof(src.SpotPrice), nameof(src.ExtendInfo), nameof(src.ParticipationRate), nameof(src.OriginalPrincipalSum), nameof(src.SettlementDate) }); if (src.IsUsePremiumRate == true) { innerCheck.AddCheckFields(new CheckField(nameof(src.StockEqvNotional)) { NumberPrecision = OtcFormatHelper.GetMaxPrecision(OtcFormatFlag.StockEqvNotional) }); } else { innerCheck.AddCheckFields(nameof(src.TradeAmount), nameof(src.Notional)); } if (src.IsTradePricePayType == true) { innerCheck.AddCheckFields(nameof(src.TradePrice)); } else { if (src.IsUsePremiumRate == true) { innerCheck.AddCheckFields(nameof(src.PremiumRate)); } else { innerCheck.AddCheckFields(nameof(src.TradeSinglePrice)); } } innerCheck.GetDiffs(diffList); #region 对TradeAmountV 进行验证 判断错误列表是否含有 有效成交数量 tw 2021/12/22 if (diffList.Contains(nameof(src.TradeAmount))) { diffList.Add("TradeAmountV"); } #endregion } #endregion #region----远期交易---- private static void ForwardTradeCheck(trade sourceTrade, trade reviewTrade, HashSet diffList) { var innerCheck = new InnerCheck(sourceTrade, reviewTrade); innerCheck.AddCheckFields(new CheckField[] { nameof(sourceTrade.BuySell), nameof(sourceTrade.TradeDate), nameof(sourceTrade.ExerciseDate), nameof(sourceTrade.SettlementDate), nameof(sourceTrade.OptionType), nameof(sourceTrade.Lots), new CheckField(nameof(sourceTrade.SpotPrice),nameof(sourceTrade.InitialSpotPrice)), nameof(sourceTrade.Strike), nameof(sourceTrade.TradePrice), }); if (PS.Config.Is浙期 && sourceTrade.UnderlyingInstrumentType == "CommoditySpot" && sourceTrade.TradeType == "远期") { if (sourceTrade.MetaDic.TryGetValue("ActualExerciseDate", out var ActualExerciseDateStr)) { sourceTrade.ActualExerciseDate = Convert.ToDateTime(ActualExerciseDateStr); innerCheck.AddCheckFields(new CheckField[] { nameof(sourceTrade.ActualExerciseDate) }); } } innerCheck.GetDiffs(diffList); //forward var src = sourceTrade.trade_forward; var rev = reviewTrade.trade_forward; var innerCheck2 = new InnerCheck(src, rev); innerCheck2.AddCheckFields(new CheckField[] { new CheckField(nameof(src.OpenCommission),"trade_forward.OpenCommission"), }); innerCheck2.GetDiffs(diffList); } #endregion #region----组合主交易---- private static void OtherGroupCheck(trade sourceTrade, trade reviewTrade, HashSet diffList) { if (sourceTrade.ExtendInfo != reviewTrade.ExtendInfo && sourceTrade.StructureType != "气囊结构") { diffList.Add(nameof(sourceTrade.ExtendInfo)); } //remove diff diffList.Remove(nameof(sourceTrade.OptionType)); diffList.Remove(nameof(sourceTrade.ExerciseMode)); } #endregion #region----保存交易复核---- /// /// 保存交易复核 /// public static void SaveTradeCheck(trade sourceTrade, trade reviewTrade, int optId, string optName) { if (reviewTrade is null) { throw new ArgumentNullException(nameof(reviewTrade)); } using (var db = DbContextFactory.GetYLDbContext()) { var tradeCheck = db.trade_check.FirstOrDefault(t => t.TradeId == sourceTrade.id && t.ValidState != ConsGlobal.InValid); if (tradeCheck == null) { tradeCheck = new trade_check { TradeId = sourceTrade.id }; db.trade_check.Add(tradeCheck); } tradeCheck.OptId = optId; tradeCheck.OptName = optName; tradeCheck.OptDate = DateTime.Now; tradeCheck.ValidState = ConsGlobal.Valid; tradeCheck.TradeObject = reviewTrade.ToJson(); db.SaveChanges(); } } #endregion #region----内部类---- class CheckField { public CheckField(string fieldName, string diffName = null) { FieldName = fieldName ?? throw new ArgumentNullException(nameof(fieldName)); DiffName = diffName; } /// /// 字段名 /// public string FieldName { get; } /// /// 差异时使用的名称 /// public string DiffName { get; } /// /// 差异时重新检查并以返回的结果为准 /// public Func RecheckIfDiff { get; set; } /// /// 数字小数精度 /// public int? NumberPrecision { get; set; } public static implicit operator CheckField(string fieldName) { return new CheckField(fieldName); } public override string ToString() { return $"{FieldName}--precision:{NumberPrecision}--diff name: {DiffName}"; } } class InnerCheck where T : class { readonly T _src, _rev; readonly ParameterExpression _instance; readonly List _checkFields; public InnerCheck(T src, T rev) { _src = src ?? throw new ArgumentNullException(nameof(src)); _rev = rev ?? throw new ArgumentNullException(nameof(rev)); _checkFields = new List(); _instance = Expression.Parameter(typeof(T), "instance"); } /// /// 浮点数最小差额,默认1e-6 /// public double DoubleMinDiff { get; set; } = 1e-6; public void AddCheckFields(params CheckField[] fields) { _checkFields.AddRange(fields); } /// /// 检查多个字段不同 /// /// public void GetDiffs(HashSet diffList) { if (_checkFields is null) { return; } foreach (var f in _checkFields) { CheckDiff(f, diffList); } } /// /// 检查单个字段不同 /// private void CheckDiff(CheckField field, HashSet diffList) { if (field is null) { throw new ArgumentNullException(nameof(field)); } var property = Expression.Property(_instance, field.FieldName); var convert = Expression.Convert(property, typeof(object)); var getter = Expression.Lambda>(convert, _instance).Compile(); var srcValue = getter(_src); var revValue = getter(_rev); var propInfo = (PropertyInfo)property.Member; if (!IsEquals(propInfo.PropertyType, srcValue, revValue, field.NumberPrecision)) { if (field.RecheckIfDiff != null && field.RecheckIfDiff()) { diffList.Add(field.DiffName ?? field.FieldName); } else { diffList.Add(field.DiffName ?? field.FieldName); } } } private bool IsEquals(Type propType, object srcValue, object revValue, int? precision) { if (propType == typeof(string)) { var srcStr = (srcValue?.ToString()?.TrimToNull()) ?? string.Empty; var revStr = (revValue?.ToString()?.TrimToNull()) ?? string.Empty; return string.Equals(srcStr, revStr); } if (propType == typeof(double?)) { return IsEquals((double?)srcValue, (double?)revValue, precision); } if (propType == typeof(double)) { return IsEquals((double)srcValue, (double)revValue, precision); } return string.Equals(srcValue?.ToString(), revValue?.ToString()); } /// /// 判断两个double?的值是否相同 /// private bool IsEquals(double? d1, double? d2, int? precision) { var minDiff = precision.HasValue ? 1 / Math.Pow(10, precision.Value) : DoubleMinDiff; return Math.Abs((d1 ?? 0) - (d2 ?? 0)) < minDiff; } /// /// 判断两个double的值是否相同 /// private bool IsEquals(double d1, double d2, int? precision) { var minDiff = precision.HasValue ? 1 / Math.Pow(10, precision.Value) : DoubleMinDiff; return Math.Abs(d1 - d2) < minDiff; } } #endregion } }