1284 lines
46 KiB
C#
1284 lines
46 KiB
C#
using NPOI.SS.Formula.Functions;
|
|
using System.Linq.Expressions;
|
|
using System.Reflection;
|
|
using YLErp.Commons;
|
|
using YLErp.Modules.TradeModule.AccumulatorOptionModule;
|
|
|
|
namespace YLErp.Modules.TradeModule
|
|
{
|
|
/// <summary>
|
|
/// 交易复核
|
|
/// </summary>
|
|
public class TradeCheckUtil
|
|
{
|
|
private static readonly OtcOptionTradeFull tf;
|
|
|
|
static TradeCheckUtil()
|
|
{
|
|
tf = null;
|
|
}
|
|
|
|
#region---交易要比较差异的字段----
|
|
|
|
/// <summary>
|
|
/// 交易要比较差异的字段
|
|
/// </summary>
|
|
public static IEnumerable<string> GetCheckFields(trade td)
|
|
{
|
|
var fields = new List<string>() {
|
|
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
|
|
|
|
/// <summary>
|
|
/// 获取差异
|
|
/// </summary>
|
|
public static HashSet<string> GetDiffs(trade sourceTrade, trade reviewTrade)
|
|
{
|
|
BLL.tradeBLL.SetFieldsByTradeType(sourceTrade);
|
|
|
|
var diffList = new HashSet<string>(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----期权基本要素----
|
|
|
|
/// <summary>
|
|
/// 期权基本要素
|
|
/// </summary>
|
|
private static void BaseTradeCheck(trade sourceTrade, trade reviewTrade, HashSet<string> diffList)
|
|
{
|
|
var innerCheck = new InnerCheck<trade>(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<string> diffList)
|
|
{
|
|
var src = sourceTrade.trade_binary_option;
|
|
var rev = reviewTrade.trade_binary_option;
|
|
var innerCheck = new InnerCheck<trade_binary_option>(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<string> diffList)
|
|
{
|
|
var src = sourceTrade.trade_asian_option;
|
|
var rev = reviewTrade.trade_asian_option;
|
|
var innerCheck = new InnerCheck<trade_asian_option>(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<string> diffList)
|
|
{
|
|
var src = sourceTrade.trade_barrier_option;
|
|
var rev = reviewTrade.trade_barrier_option;
|
|
var innerCheck = new InnerCheck<trade_barrier_option>(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<string> diffList)
|
|
{
|
|
var src = sourceTrade.trade_double_sharkfin_option;
|
|
var rev = reviewTrade.trade_double_sharkfin_option;
|
|
var innerCheck = new InnerCheck<trade_double_sharkfin_option>(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<string> diffList)
|
|
{
|
|
var src = sourceTrade.trade_rangeaccrual;
|
|
var rev = reviewTrade.trade_rangeaccrual;
|
|
var innerCheck = new InnerCheck<trade_rangeaccrual>(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<string> diffList)
|
|
{
|
|
var src = sourceTrade.trade_underlying_enhance;
|
|
var rev = reviewTrade.trade_underlying_enhance;
|
|
var innerCheck = new InnerCheck<trade_underlying_enhance>(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<string> diffList)
|
|
{
|
|
var src = sourceTrade.trade_airbag;
|
|
var rev = reviewTrade.trade_airbag;
|
|
var innerCheck = new InnerCheck<trade_airbag>(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<string> diffList)
|
|
{
|
|
var src = sourceTrade.trade_snowball;
|
|
var rev = reviewTrade.trade_snowball;
|
|
var innerCheck = new InnerCheck<trade_snowball>(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<string> diffList)
|
|
{
|
|
var src = sourceTrade.trade_autocall;
|
|
var rev = reviewTrade.trade_autocall;
|
|
var innerCheck = new InnerCheck<trade_autocall>(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<string> diffList)
|
|
{
|
|
var src = sourceTrade.trade_accumulator_option;
|
|
var rev = reviewTrade.trade_accumulator_option;
|
|
var innerCheck = new InnerCheck<trade_accumulator_option>(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<string> diffList)
|
|
{
|
|
var src = sourceTrade.trade_cashflow;
|
|
var rev = reviewTrade.trade_cashflow;
|
|
var innerCheck = new InnerCheck<trade_cashflow>(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<string> diffList)
|
|
{
|
|
var src = sourceTrade.trade_risky_option;
|
|
var rev = reviewTrade.trade_risky_option;
|
|
var innerCheck = new InnerCheck<trade_risky_option>(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<string> diffList)
|
|
{
|
|
var src = sourceTrade;
|
|
var rev = reviewTrade;
|
|
|
|
var innerCheck = new InnerCheck<trade>(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<string> diffList)
|
|
{
|
|
var innerCheck = new InnerCheck<trade>(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<trade_forward>(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<string> 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----保存交易复核----
|
|
|
|
/// <summary>
|
|
/// 保存交易复核
|
|
/// </summary>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 字段名
|
|
/// </summary>
|
|
public string FieldName { get; }
|
|
|
|
/// <summary>
|
|
/// 差异时使用的名称
|
|
/// </summary>
|
|
public string DiffName { get; }
|
|
|
|
/// <summary>
|
|
/// 差异时重新检查并以返回的结果为准
|
|
/// </summary>
|
|
public Func<bool> RecheckIfDiff { get; set; }
|
|
|
|
/// <summary>
|
|
/// 数字小数精度
|
|
/// </summary>
|
|
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<T> where T : class
|
|
{
|
|
readonly T _src, _rev;
|
|
|
|
readonly ParameterExpression _instance;
|
|
|
|
readonly List<CheckField> _checkFields;
|
|
|
|
public InnerCheck(T src, T rev)
|
|
{
|
|
_src = src ?? throw new ArgumentNullException(nameof(src));
|
|
_rev = rev ?? throw new ArgumentNullException(nameof(rev));
|
|
|
|
_checkFields = new List<CheckField>();
|
|
_instance = Expression.Parameter(typeof(T), "instance");
|
|
}
|
|
|
|
/// <summary>
|
|
/// 浮点数最小差额,默认1e-6
|
|
/// </summary>
|
|
public double DoubleMinDiff { get; set; } = 1e-6;
|
|
|
|
public void AddCheckFields(params CheckField[] fields)
|
|
{
|
|
_checkFields.AddRange(fields);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 检查多个字段不同
|
|
/// </summary>
|
|
/// <param name="_checkFields"></param>
|
|
public void GetDiffs(HashSet<string> diffList)
|
|
{
|
|
if (_checkFields is null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
foreach (var f in _checkFields)
|
|
{
|
|
CheckDiff(f, diffList);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 检查单个字段不同
|
|
/// </summary>
|
|
private void CheckDiff(CheckField field, HashSet<string> 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<Func<T, object>>(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());
|
|
}
|
|
|
|
/// <summary>
|
|
/// 判断两个double?的值是否相同
|
|
/// </summary>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 判断两个double的值是否相同
|
|
/// </summary>
|
|
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
|
|
}
|
|
}
|