Files
zszq-trs/YLErpDAL/Modules/RiskModule/FlowRiskService.cs
T
2024-05-09 14:06:26 +08:00

1110 lines
65 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Qdp.Foundation.Implementations;
using Qdp.Pricing.Base.Implementations;
using Qdp.Pricing.Library.Options.Products.Autocall.Phoenix;
using Qdp.Pricing.Library.Options.Products.Autocall.Snowball;
using YLErp.BLL;
using YLErp.BLL.Calculation.V2;
using YLErp.BLL.EodSettlement;
using YLErp.DBModels;
using YLErp.DBModels.Helpers;
using YLErp.Models;
using YLErp.Modules.CalculationModule;
using YLErp.Modules.TradeModule;
using YLErp.Modules.TradeModule.DealModule;
using YLErp.Modules.TradeModule.ExoticOptionModule;
using YLErp.QdpModule;
namespace YLErp.Modules.RiskModule
{
/// <summary>
/// 资金流动性预测
/// </summary>
public class FlowRiskService : YLBaseService
{
public FlowRiskService(OptUserInfo userInfo) : base(userInfo)
{
}
public FlowRiskService(YLBaseService baseService) : base(baseService)
{
}
/// <summary>
/// 价格
/// </summary>
private Dictionary<string, double> _prices = new Dictionary<string, double>();
/// <summary>
/// 障碍期权
/// </summary>
private BarrierOptionKnockioService barrierService = null;
/// <summary>
/// 二元期权
/// </summary>
private BinaryOptionDealService binaryService = null;
/// <summary>
/// 双鲨期权
/// </summary>
private DoubleSharkOptionKnockoutService doubleSharkService = null;
/// <summary>
/// 区间累积期权
/// </summary>
private TradeRangeAccrualService rangeAccrualService = null;
/// <summary>
/// 凤凰期权
/// </summary>
private TradeAutocallBLL autocallService = null;
/// <summary>
/// 雪球期权
/// </summary>
private TradeSnowballBLL snowballService = null;
public List<FlowRiskModel> QueryFlowRiskList(DateTime startDate, DateTime endDate)
{
if (endDate < startDate)
{
return new List<FlowRiskModel>();
}
var flowRiskList = new List<FlowRiskModel>();
var dates = QdpCalendarHelper.AllBizDays(startDate, endDate);
var list =
(from t in DbContext.trade
where
ConsTrade.PositionTradeStatusList.Contains(t.TradeStatus) &&
t.ExerciseDate >= startDate && t.ValidState != "InValid"
select t).ToArray();
#if DEBUG
//string temp = "";
//list = DbContext.trade.Where(O => O.TradeNumber == temp).ToArray();
#endif
tradeBLL.SetFieldsByTradeType(list);
var subTradeDict = list.Where(O => O.ParentTradeId > 0).GroupBy(O => O.ParentTradeId).ToDictionary(K => K.Key, V => V.ToList());
list = list.Where(O => O.ParentTradeId == 0).ToArray();
foreach (var item in list)
{
if (subTradeDict.ContainsKey(item.id))
{
item.SubTrades = subTradeDict[item.id];
}
if (item.UnderlyingCode == null)
{
item.UnderlyingCode = "";
}
if (!_prices.ContainsKey(item.UnderlyingCode))
{
_prices[item.UnderlyingCode] = DataCacheProvider.GetUnderlyingDataSource().GetPrice(item.UnderlyingCode);
}
if (item.SubTrades != null && item.SubTrades.Any())
{
var endAll = true;
DateTime? settledate = null;
var trades = item.SubTrades.Where(O => O.TradeType != "现金流交易");
foreach (var sItem in trades)
{
if (!_prices.ContainsKey(sItem.UnderlyingCode))
{
_prices[sItem.UnderlyingCode] = DataCacheProvider.GetUnderlyingDataSource().GetPrice(sItem.UnderlyingCode);
}
var obj = CheckTradeStatus(sItem, startDate, endDate);
flowRiskList.Add(obj);
settledate = obj?.ValueDate ?? sItem.ExerciseDate ?? endDate;
if (!ConsTrade.TradeCompleteStatus.Contains(sItem.TradeStatus))
{
endAll = false;
}
}
if (endAll)
{
//结构化交易中所有非现金流交易全部了结时,现金流交易也应该了结;
trades = item.SubTrades.Where(O => O.TradeType == "现金流交易");
foreach (var sItem in trades)
{
var obj = new FlowRiskModel();
var udm = new underlying_manager();
udm.QuotationDate = sItem.ExerciseDate;
var ttm = TradeCalcHelper.CalculateTTMDays(sItem.ExerciseDate.Value, sItem.ExerciseDate.Value, udm.UnderlyingTypeId, false);
var OptionValue = ValueCalculator.GetOptionValueResultV2(
UserInfo.UserId.ToString(),
udm,
sItem,
new double[] { 0.0 },
new double[] { 0.0 },
null,
timeToMaturityDays: ttm,
request: QdpPricingRequest.BASIC_PRICING);
obj.Execute -= OptionValue.Pv;
obj.ValueDate = settledate ?? startDate;
obj.TradeType = "现金流交易";
obj.TradeNumber = sItem.TradeNumber;
obj.EncryptId = sItem.EncryptId;
flowRiskList.Add(obj);
}
}
}
else
{
var obj = CheckTradeStatus(item, startDate, endDate);
flowRiskList.Add(obj);
}
}
var dict = flowRiskList.Where(O => O != null && O.TotleCash != 0).GroupBy(O => O.ValueDate).ToDictionary(K => K.Key, V => V.ToList()).OrderBy(O => O.Key);
var result = new List<FlowRiskModel>();
var total = new FlowRiskModel() { ParentLevel = 2 };
foreach (var item in dict)
{
var parent = new FlowRiskModel() { ParentLevel = 1, ValueDate = item.Key, KO = item.Value.Sum(O => O.KO), Execute = item.Value.Sum(O => O.Execute) };
total.KO += parent.KO;
total.Execute += parent.Execute;
result.Add(parent);
result.AddRange(item.Value);
}
result.Add(total);
return result;
}
private FlowRiskModel CheckTradeStatus(trade td, DateTime startDate, DateTime endDate)
{
var result = new FlowRiskModel();
result.TradeNumber = td.TradeNumber;
result.TradeType = td.TradeType;
result.EncryptId = td.EncryptId;
switch (td.TradeType)
{
case "香草期权":
case "亚式期权":
case "合成价差期权":
case "收益增强结构":
case "自定义交易":
if (td.ExerciseDate > endDate)
{
result = null;
break;
}
trade_cash r = new trade_cash();
SettlementCalcCommons.SetPossibleExec(r, td, _prices[td.UnderlyingCode]);
td.TradeStatus = "已到期";
result.Execute = -r.Amount;
result.ValueDate = td.ExerciseDate.Value;
break;
case "收益互换":
if (td.ExerciseDate > endDate)
{
result = null;
break;
}
var tradeCashList = DbContext.trade_cash.Where(y => y.TradeId == td.id && y.Action == "系统操作-互换" && y.ValidState != "InValid" && !y.IsDeleted && y.ValueDate <= td.ExerciseDate);
var tradeCashIds = tradeCashList.Select(x => x.id);
var tradeCash = tradeCashList.OrderByDescending(y => y.id).FirstOrDefault();
var cashSwaps = DbContext.trade_cash_swap.Where(x => x.TradeId == td.id && tradeCashIds.Contains(x.TradeCashId)).ToArray();
var tradeCashSwap = tradeCash != null ? cashSwaps.FirstOrDefault(x => x.TradeCashId == tradeCash.id) : null;
//取最后一次手动收益;
var lastManualCashSwap = cashSwaps.OrderByDescending(o => o.StartDate).FirstOrDefault(x => !x.IsAuto);
var lastManualCash = lastManualCashSwap != null ? tradeCashList.FirstOrDefault(x => x.id == lastManualCashSwap.TradeCashId) : null;
DateTime endDateSwap;
var preSwapDate = PayoffSwapCalcService.GetSwapRateStartDate(td, td.trade_swap, td.ExerciseDate.Value, tradeCash, lastManualCash, td.trade_swap.IsGetFloatingProfit, out endDateSwap);
var initialAmountPay = PayoffSwapCalcService.GetInitialAmountSwapPay(td, td.trade_swap, tradeCashSwap?.PayFinalPrice ?? td.trade_swap.PaySpotPrice ?? 0
, _prices[td.UnderlyingCode], td.StockEqvNotional, td.ExerciseDate.Value, tradeCash?.ValueDate);
var extraAmountGet = PayoffSwapCalcService.GetExtraAmountBySwapRate(td.ClientId, td.TradeDate, td.trade_swap.GetSwapTimeAndRate, preSwapDate, endDateSwap, td.trade_swap.AnnualDays ?? 0, td.StockEqvNotional);
preSwapDate = PayoffSwapCalcService.GetSwapRateStartDate(td, td.trade_swap, td.ExerciseDate.Value, tradeCash, lastManualCash, td.trade_swap.IsPayFloatingProfit, out endDateSwap);
var initialAmountGet = PayoffSwapCalcService.GetInitialAmountSwapGet(td, td.trade_swap, tradeCashSwap?.GetFinalPrice ?? td.trade_swap.GetSpotPrice ?? 0
, _prices[td.UnderlyingCode], td.StockEqvNotional, td.ExerciseDate.Value, tradeCash?.ValueDate);
var extraAmountPay = PayoffSwapCalcService.GetExtraAmountBySwapRate(td.ClientId, td.TradeDate, td.trade_swap.PaySwapTimeAndRate, preSwapDate, endDateSwap, td.trade_swap.AnnualDays ?? 0, td.StockEqvNotional);
td.TradeStatus = "已到期";
result.Execute = -((initialAmountGet + extraAmountGet) - (initialAmountPay + extraAmountPay));
result.ValueDate = td.ExerciseDate.Value;
break;
case "现金流交易":
if (td.ExerciseDate > endDate)
{
result = null;
break;
}
var udm = new underlying_manager();
udm.QuotationDate = td.ExerciseDate;
var ttm = TradeCalcHelper.CalculateTTMDays(td.ExerciseDate.Value, td.ExerciseDate.Value, udm.UnderlyingTypeId, false);
var OptionValue = ValueCalculator.GetOptionValueResultV2(
UserInfo.UserId.ToString(),
udm,
td,
new double[] { 0.0 },
new double[] { 0.0 },
null,
timeToMaturityDays: ttm,
request: QdpPricingRequest.BASIC_PRICING);
result.Execute -= OptionValue.Pv;
result.ValueDate = td.ExerciseDate.Value;
if ((td.SettlementDate ?? td.ExerciseDate.GetValueOrDefault()) > endDate)
{
result = null;
}
break;
case "凤凰期权":
{
autocallService = autocallService ?? new TradeAutocallBLL(UserInfo);
var observDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(
new Date(td.TradeDate), new Date(td.ExerciseDate.Value)).Where(O => O.DateTime != td.TradeDate).ToArray();
//敲入转期权和到期支付票息同时存在时,若设置观察价格页面设置了结算金额,作为票息处理,敲入了结金额维持系统计算逻辑不变
bool hasUseSettlementAmount = false;
var tradeCashs = DbContext.trade_cash.Where(x => x.ValidState != ConsGlobal.InValid && !x.IsDeleted && x.TradeId == td.id && x.Action == "系统操作-平仓费" && (x.ValueDate > startDate && (x.ConfirmDate > startDate || x.ConfirmDate == DateTime.MinValue)) && x.UnwindNotional < x.Notional).ToList();
var notional = (ConsTrade.TradeCompleteStatus.Contains(td.TradeStatus) && td.UnWindDate <= startDate ? 0 : td.Notional) + tradeCashs.Sum(x => x.UnwindNotional).Value;
var optionTrade = QdpTradeBuilder.GetAutocallOptionTrade(td, td.trade_autocall,
new OptionTradeParamRequest(valuedateBLL.SysRiskFreeRate()) { ParamOverride = x => { x.notional = notional; } });
var autocall = (AutoCall)optionTrade.Instrument;
var kiBarrier = td.IsMoneynessOptionData ? td.trade_autocall.KIBarrier * td.SpotPrice : td.trade_autocall.KIBarrier;
var happenedObservations = new List<autocall_observation>();
foreach (var date in observDates)
{
if (date.DateTime < startDate || date.DateTime > endDate)
{
continue;
}
var isKnockedOut = false;
//只在敲出观察日检查敲出和票息情况
//如果交易已经是敲出状态了,不用再做票息和敲出检查
if (autocall.KOObsDates.Select(x => x.DateTime).Contains(date.DateTime)
&& td.trade_autocall.KnockInOutStatus != ConsTrade.KnockState.KnockedOut)
{
double koBarrier;
if (autocall.CustomizedKOBarriers != null && autocall.CustomizedKOBarriers.Length > 0)
{
var index = autocall.KOObsDates.Select(x => x.DateTime).ToList().IndexOf(date.DateTime);
koBarrier = autocall.CustomizedKOBarriers[index];
}
else
{
koBarrier = td.trade_autocall.KOBarrier;
}
if (td.IsMoneynessOptionData)
{
koBarrier *= td.SpotPrice ?? 1.0;
}
#region 票息检查
var couponBarrier =
td.IsMoneynessOptionData ?
td.trade_autocall.CouponBarrier * td.SpotPrice :
td.trade_autocall.CouponBarrier;
isKnockedOut = true;// _prices[td.UnderlyingCode] >= koBarrier;
//有票息
//if (_prices[td.UnderlyingCode] >= couponBarrier)
{
//利息计算时,当autocall的Notional包含了符号,则GetEffectiveObservation考虑了买卖方向了
var observation = autocall.GetEffectiveObservation(date);
if (observation != null)
{
td.trade_autocall = td.trade_autocall;
//观察日页面设置的结算金额
if (td.SettlementAmount != null)
{
td.SettlementAmount = td.SettlementAmount;
hasUseSettlementAmount = true;
//到期敲入且未敲出情况
if (date.DateTime == autocall.ExerciseDates.Last().DateTime && !isKnockedOut)
{
//当前满足敲入或者已经敲入了
if (autocall.KIObsDates.Select(x => x.DateTime).Contains(date.DateTime) && _prices[td.UnderlyingCode] <= kiBarrier || td.trade_autocall.KnockInOutStatus == ConsTrade.KnockState.KnockedIn)
{
var optionPayoffPayment = autocall.GetPayoff(new double[] { _prices[td.UnderlyingCode] });
var paymentAmount = TradeHelper.GetAmountByPaymentAmount(optionPayoffPayment[0].PaymentAmount, td.PrincipalSum(), td.BuySell);
td.SettlementAmount -= paymentAmount;
}
}
}
happenedObservations.Add(autocallService.SaveCouponObservation(td, td.trade_autocall, observation, date.DateTime, _prices[td.UnderlyingCode], isKnockedOut, td.SettlementAmount, false));
}
}
#endregion
#region 敲出检查
// 发生敲出事件
if (isKnockedOut)
{
// 更新观察状态
td.trade_autocall.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
td.trade_autocall.KnockInOutDate = date.DateTime;
// 更新交易状态
td.TradeStatus = ConsTrade.已平仓;
td.UnWindDate = date.DateTime;
happenedObservations.ForEach(O =>
{
result.KO -= O.PaymentAmount;
});
result.ValueDate = date;
if (td.trade_autocall.CouponPayType != CouponPayTypeEnum.AtMaturity)
{
break;
}
}
#endregion
}
#region 敲入检查
//在当前结算日之前未敲出且未敲入:
if (autocall.KIObsDates.Select(x => x.DateTime).Contains(date.DateTime) && !isKnockedOut)
{
//看涨 - 向下敲入,看跌 - 向上敲入
var knockedin = ConsGlobal.CallPut.IsCall(td.CallPut) ? _prices[td.UnderlyingCode] <= kiBarrier : _prices[td.UnderlyingCode] >= kiBarrier;
// 发生敲入事件
if (knockedin)
{
// 更新观察状态
td.trade_autocall.KnockInOutStatus = ConsTrade.KnockState.KnockedIn;
td.trade_autocall.KnockInOutDate = date.DateTime;
}
}
#endregion
#region 到期检查
if (date.DateTime == autocall.ExerciseDates.Last().DateTime)
{
td.UnWindDate = date.DateTime;
if (td.trade_autocall.KnockInOutStatus == ConsTrade.KnockState.KnockedIn)
{
td.TradeStatus = ConsTrade.已执行;
if (!autocall.IncludeCouponAfterKI)
{
// 敲入不支付票息,则要将之前累积的票息删除掉
happenedObservations.Clear();
hasUseSettlementAmount = false;
}
double paymentAmount = 0;
if (td.SettlementAmount != null && !hasUseSettlementAmount)
{
paymentAmount = td.SettlementAmount ?? 0;
}
else
{
//已敲入,到期时计算期权收益
var optionPayoffPayment = autocall.GetPayoff(new double[] { _prices[td.UnderlyingCode] });
//optionPayoffPayment[0].PaymentAmount包含了买卖方向的处理了
paymentAmount = TradeHelper.GetAmountByPaymentAmount(optionPayoffPayment[0].PaymentAmount, td.PrincipalSum(), td.BuySell);
}
if (td.trade_autocall.CouponPayAtMaturity && td.trade_autocall.IncludeCouponAfterKI)
{
happenedObservations.AddRange(DbContext.autocall_observation.Where(o => o.TradeId == td.id).ToList());
}
}
else if (td.trade_autocall.KnockInOutStatus != ConsTrade.KnockState.KnockedOut)
{
// 更新交易状态
td.TradeStatus = ConsTrade.已到期;
if (td.trade_autocall.CouponPayAtMaturity)
{
happenedObservations.AddRange(DbContext.autocall_observation.Where(o => o.TradeId == td.id).ToList());
}
if ((td.SettlementDate ?? td.ExerciseDate.GetValueOrDefault()) > endDate)
{
result = null;
}
}
else if (td.trade_autocall.KnockInOutStatus == ConsTrade.KnockState.KnockedOut)
{
if (td.trade_autocall.CouponPayAtMaturity)
{
happenedObservations.AddRange(DbContext.autocall_observation.Where(o => o.TradeId == td.id).ToList());
}
}
if (result != null)
{
happenedObservations.ForEach(O =>
{
result.Execute -= O.PaymentAmount;
});
result.ValueDate = date;
}
break;
}
#endregion
}
}
break;
case "雪球期权":
{
snowballService = snowballService ?? new TradeSnowballBLL(UserInfo);
var observDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(
new Date(td.TradeDate), new Date(td.ExerciseDate.Value)).Where(O => O.DateTime != td.TradeDate).ToArray();
var tradeCashs = DbContext.trade_cash.Where(x => x.ValidState != ConsGlobal.InValid && !x.IsDeleted && x.TradeId == td.id && x.Action == "系统操作-平仓费" && (x.ValueDate > startDate && (x.ConfirmDate > startDate || x.ConfirmDate == DateTime.MinValue)) && x.UnwindNotional < x.Notional).ToList();
var request = new OptionTradeParamRequest(valuedateBLL.SysRiskFreeRate())
{
ParamOverride = p => p.notional = (ConsTrade.TradeCompleteStatus.Contains(td.TradeStatus) && td.UnWindDate <= startDate ? 0 : td.Notional) + tradeCashs.Sum(x => x.UnwindNotional).Value
};
var optionTrade = QdpTradeBuilder.GetSnowballOptionTrade(td, td.trade_snowball, request);
var datesStr = td.trade_snowball.KOObservationSettleDates;
var KOObsSettleDates = string.IsNullOrWhiteSpace(datesStr) ? null : datesStr.Split(new char[] { ',', ';', '', '' }, StringSplitOptions.RemoveEmptyEntries).Select(x => DateTime.Parse(x)).ToArray();
var snowball = (SimpleSnowball)optionTrade.Instrument;
var happenedObservations = new List<autocall_observation>();
foreach (var date in observDates)
{
if (date.DateTime < startDate || date.DateTime > endDate)
{
continue;
}
//只在敲出观察日检查敲出和票息情况
//如果交易已经是敲出状态了,不用再做票息和敲出检查
if (snowball.KOObsDates.Select(x => x.DateTime).Contains(date.DateTime)
&& td.trade_snowball.KnockInOutStatus != ConsTrade.KnockState.KnockedOut)
{
#region 敲出检查
double koBarrier;
var koSettleDate = date.DateTime;
if (snowball.CustomizedKOBarriers != null && snowball.CustomizedKOBarriers.Length > 0)
{
var index = snowball.KOObsDates.Select(x => x.DateTime).ToList().IndexOf(date.DateTime);
koBarrier = snowball.CustomizedKOBarriers[index];
if (KOObsSettleDates != null && KOObsSettleDates.Length > index)
{
koSettleDate = KOObsSettleDates[index];
}
}
else
{
koBarrier = td.trade_snowball.KOBarrier;
if (KOObsSettleDates != null && KOObsSettleDates.Any())
{
koSettleDate = KOObsSettleDates[0];
}
}
if (td.trade_snowball.KORebateType == RebateTypeEnum.AtEnd)
{
koSettleDate = td.ExerciseDate.Value;
}
else if (koSettleDate < date.DateTime)
{
koSettleDate = date.DateTime;
}
if (koSettleDate > endDate)
{
result = null;
break;
}
if (td.IsMoneynessOptionData)
{
koBarrier *= td.SpotPrice ?? 1.0;
}
// 发生敲出事件
//if (_prices[td.UnderlyingCode] >= koBarrier)
{
// 更新观察状态
td.trade_snowball.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
td.trade_snowball.KnockInOutDate = date.DateTime;
// 更新交易状态
td.TradeStatus = ConsTrade.已平仓;
td.UnWindDate = date.DateTime;
if (snowball.UseOptionPayoffAtKO)
{
double paymentAmount = 0;
var koOptionCashflows = snowball.GetKOPayoff(date, _prices[td.UnderlyingCode]);
if (td.SettlementAmount != null)
{
paymentAmount = td.SettlementAmount ?? 0;
}
else
{
paymentAmount = TradeHelper.GetAmountByPaymentAmount(koOptionCashflows[0].PaymentAmount, td.PrincipalSum(), td.BuySell);
}
result.KO -= paymentAmount;
result.ValueDate = koSettleDate;
break;
//koOptionCashflows[0].PaymentAmount包含了买卖方向的处理了
}
else
{
if (td.SettlementAmount != null)
{
double tradePrice = 0;
var settlementAmount = td.SettlementAmount ?? 0;
//观察日价格页面的结算金额包含了年化期权费,该处逻辑需要先按照扣除年化期权费来算,后面逻辑会补上年化期权费,否则会重复运算
if (td.trade_snowball.AnnualizedPremiumRate.HasValue && td.trade_snowball.AnnualizedPremiumRate != 0)
{
tradePrice = td.StockEqvNotional * td.ParticipationRate * td.trade_snowball.AnnualizedPremiumRate * snowball.CouponDayCount.CalcDayCountFraction(snowball.StartDate, date) ?? 0;
if (tradePrice != 0)
{
settlementAmount -= (td.BuySell == "买入" ? -1 : 1) * tradePrice;
}
}
result.KO -= settlementAmount;
result.ValueDate = date;
break;
}
else
{
var couponPayment = TradeHelper.GetAmountByPaymentAmount(snowball.CouponPayment(date), td.PrincipalSum(), td.BuySell);
result.KO -= couponPayment;
result.ValueDate = date;
break;
}
}
}
#endregion
}
#region 敲入检查
//在当前结算日之前未敲出且未敲入:
// !((KO || KI) && KOKIDate < valueDate)
if (!((td.trade_snowball.KnockInOutStatus == ConsTrade.KnockState.KnockedOut
|| td.trade_snowball.KnockInOutStatus == ConsTrade.KnockState.KnockedIn)
&& td.trade_snowball.KnockInOutDate < date.DateTime)
&& snowball.KIObsDates.Select(x => x.DateTime).Contains(date.DateTime)
&& td.trade_snowball.KIPayoffType != KIPayoffTypeEnum.None)
{
var kiBarrier =
td.IsMoneynessOptionData ?
td.trade_snowball.KIBarrier * td.SpotPrice :
td.trade_snowball.KIBarrier;
// 发生敲入事件
if (_prices[td.UnderlyingCode] <= kiBarrier)
{
// 更新观察状态
td.trade_snowball.KnockInOutStatus = ConsTrade.KnockState.KnockedIn;
td.trade_snowball.KnockInOutDate = date.DateTime;
}
}
#endregion
#region 到期检查
if (date.DateTime == snowball.ExerciseDates.Last().DateTime)
{
if (snowball.UseOptionPayoffAtMaturity &&
(td.trade_snowball.KnockInOutStatus == ConsTrade.KnockState.KnockedIn || td.trade_snowball.KIBarrier <= 0))
{
td.TradeStatus = ConsTrade.已执行;
td.UnWindDate = date.DateTime;
double paymentAmount = 0;
//已敲入,到期时计算期权收益
var optionPayoffPayment = snowball.GetPayoff(new double[] { _prices[td.UnderlyingCode] });
if (td.SettlementAmount != null)
{
paymentAmount = td.SettlementAmount ?? 0;
}
else
{
paymentAmount = TradeHelper.GetAmountByPaymentAmount(optionPayoffPayment[0].PaymentAmount, td.PrincipalSum(), td.BuySell);
}
//optionPayoffPayment[0].PaymentAmount包含了买卖方向的处理了
result.Execute -= paymentAmount;
result.ValueDate = date;
break;
}
else if (td.trade_snowball.KnockInOutStatus != ConsTrade.KnockState.KnockedOut)
{
td.TradeStatus = ConsTrade.已到期;
td.UnWindDate = date.DateTime;
var maturityCouponRate = snowball.Coupon * snowball.InitialSpotPrice;
var maturityCouponPayment =
snowball.FixedCoupon ?
maturityCouponRate * snowball.Notional :
maturityCouponRate * snowball.Notional * snowball.CouponDayCount.CalcDayCountFraction(snowball.StartDate, snowball.ExerciseDates.Last());
maturityCouponPayment = TradeHelper.GetAmountByPaymentAmount(maturityCouponPayment, td.PrincipalSum(), td.BuySell);
double tradePrice = 0;
if (td.trade_snowball.AnnualizedPremiumRate.HasValue && td.trade_snowball.AnnualizedPremiumRate != 0)
{
tradePrice = td.StockEqvNotional * td.ParticipationRate * td.trade_snowball.AnnualizedPremiumRate * snowball.CouponDayCount.CalcDayCountFraction(snowball.StartDate, date) ?? 0;
}
tradeCash = new trade_cash();
if (td.SettlementAmount != null)
{
//观察日价格页面的结算金额包含了年化期权费,该处逻辑需要先按照扣除年化期权费来算,后面逻辑会补上年化期权费,否则会重复运算
var settlementAmount = td.SettlementAmount ?? 0;
if (tradePrice != 0)
{
settlementAmount -= (td.BuySell == "买入" ? -1 : 1) * tradePrice;
}
result.Execute -= settlementAmount;
result.ValueDate = date;
}
else
{
result.Execute -= maturityCouponPayment;
result.ValueDate = date;
}
if ((td.SettlementDate ?? td.ExerciseDate.GetValueOrDefault()) > endDate)
{
result = null;
}
}
}
#endregion
}
}
break;
//case "气囊结构":
// //气囊结构没有计算赔付;
// airbagService = airbagService ?? new TradeAirbagService(UserInfo);
// airbagService.CheckAirbagKnockInStatus(td, td.trade_airbag, startDate, _prices[td.UnderlyingCode]);
// break;
case "区间累积期权":
{
if (td.ExerciseDate > endDate)
{
result = null;
break;
}
rangeAccrualService = rangeAccrualService ?? new TradeRangeAccrualService(UserInfo);
var observDates = QdpHelper.ParseObservationDate(td.trade_rangeaccrual.ObservationDates)?.ToArray();
if (observDates == null)
{
observDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(
new Date(td.TradeDate.Value), new Date(td.ExerciseDate.Value)).Where(O => O.DateTime != td.TradeDate).ToArray();
}
observDates = observDates.Where(O => O.DateTime >= startDate).ToArray();
var happenedObservations = new List<autocall_observation>();
if (observDates != null)
{
foreach (var date in observDates)
{
var upperRange = td.IsMoneynessOptionData ? td.trade_rangeaccrual.UpperRange * td.SpotPrice.Value : td.trade_rangeaccrual.UpperRange;
var lowerRange = td.IsMoneynessOptionData ? td.trade_rangeaccrual.LowerRange * td.SpotPrice.Value : td.trade_rangeaccrual.LowerRange;
//有区间收益
if (_prices[td.UnderlyingCode] < upperRange && _prices[td.UnderlyingCode] > lowerRange)
{
var couponCash = td.trade_rangeaccrual.BonusRate * td.Notional * (td.SpotPrice ?? 0) / observDates.Length * (td.BuySell == "卖出" ? -1 : 1);
happenedObservations.Add(rangeAccrualService.SaveObservation(td, td.trade_rangeaccrual, date, couponCash, td.Notional, false));
}
}
}
//到期日生成票息资金记录
td.TradeStatus = ConsTrade.已到期;
happenedObservations.AddRange(DbContext.autocall_observation.Where(o => o.TradeId == td.id).ToList());
//happenedObservations?.ForEach(o =>
//{
// var paymentAmount = TradeHelper.GetAmountByPaymentAmount(o.PaymentAmount, td.PrincipalSum(), td.BuySell);
// result.Execute -= paymentAmount;
//});
result.Execute -= happenedObservations.Sum(n => n.PaymentAmount) + td.PrincipalSum();
result.ValueDate = td.ExerciseDate.Value;
if ((td.SettlementDate ?? td.ExerciseDate.GetValueOrDefault()) > endDate)
{
result = null;
}
}
break;
case "双鲨期权":
{
var observDates = QdpHelper.ParseObservationDate(td.trade_double_sharkfin_option.ObservationDates)?.ToArray();
if (observDates == null)
{
observDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(
new Date(td.TradeDate), new Date(td.ExerciseDate.Value)).Where(O => O.DateTime != td.TradeDate).ToArray();
}
var lastDate = observDates.Where(O => O >= new Date(startDate) && O <= new Date(endDate)).OrderBy(O => (O)).FirstOrDefault();
if (lastDate != default)
{
doubleSharkService = doubleSharkService ?? new DoubleSharkOptionKnockoutService(UserInfo);
var barrierHigh = td.IsMoneynessOptionData ? td.trade_double_sharkfin_option.BarrierHigh * td.SpotPrice : td.trade_double_sharkfin_option.BarrierHigh;
var barrierLow = td.IsMoneynessOptionData ? td.trade_double_sharkfin_option.BarrierLow * td.SpotPrice : td.trade_double_sharkfin_option.BarrierLow;
var useRebate = _prices[td.UnderlyingCode] <= barrierLow;
var price = _prices[td.UnderlyingCode];
td.trade_double_sharkfin_option.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
td.trade_double_sharkfin_option.KnockInOutDate = startDate;
td.TradeStatus = ConsTrade.已平仓;
td.UnWindDate = startDate;
r = doubleSharkService.SaveDoubleSharkFinRebateCash(td, td.trade_double_sharkfin_option, price, startDate, useRebate, false);
result.KO = -r?.Amount;
if (td.trade_double_sharkfin_option.RebateType == "AtHit")
{
result.ValueDate = lastDate;
}
else if (td.trade_double_sharkfin_option.RebateType == "AtEnd" && td.ExerciseDate <= endDate)
{
result.ValueDate = td.ExerciseDate.Value;
}
else
{
result = null;
}
}
else if (td.ExerciseDate <= endDate)
{
td.TradeStatus = ConsTrade.已到期;
td.UnWindDate = td.ExerciseDate;
r = new trade_cash();
SettlementCalcCommons.SetPossibleExec(r, td, _prices[td.UnderlyingCode]);
result.Execute = -r?.Amount;
result.ValueDate = td.ExerciseDate.Value;
if ((td.SettlementDate ?? td.ExerciseDate.GetValueOrDefault()) > endDate)
{
result = null;
}
}
else
{
result = null;
}
}
break;
case "二元期权":
{
if (td.ExerciseMode == "American")
{
r = null;
binaryService = binaryService ?? new BinaryOptionDealService(UserInfo);
var isObservationDate = true;
double upPrice = _prices[td.UnderlyingCode], lowPrice = _prices[td.UnderlyingCode];
var strike = td.IsMoneynessOptionData ? td.Strike * td.SpotPrice : td.Strike;
var UpperBarrier = new Lazy<double?>(() =>
td.IsMoneynessOptionData ? td.trade_binary_option.UpperBarrier * td.SpotPrice : td.trade_binary_option.UpperBarrier);
if (isObservationDate)
{
switch (td.trade_binary_option.PayoffType)
{
case "UpOneTouch":
//触碰,买方获得盈利,交易结束
var cash = BinaryOptionDealService.GetCashOrNothingAmount(td, td.trade_binary_option, startDate, useHighAmount: false);
r = binaryService.SaveBinarySettleCash(td, td.trade_binary_option, startDate, cash, true, upPrice, false);
td.TradeStatus = ConsTrade.已执行;
td.UnWindDate = startDate;
break;
case "DownOneTouch":
//触碰,买方获得盈利,交易结束
cash = BinaryOptionDealService.GetCashOrNothingAmount(td, td.trade_binary_option, startDate, useHighAmount: false);
r = binaryService.SaveBinarySettleCash(td, td.trade_binary_option, startDate, cash, true, lowPrice, false);
td.TradeStatus = ConsTrade.已执行;
td.UnWindDate = startDate;
break;
case "UpNoTouch":
//触碰,买方无盈利,交易结束
r = binaryService.SaveBinarySettleCash(td, td.trade_binary_option, startDate, 0, false, upPrice, false);
td.TradeStatus = ConsTrade.已到期;
td.UnWindDate = startDate;
break;
case "DownNoTouch":
//触碰,买方无盈利,交易结束
r = binaryService.SaveBinarySettleCash(td, td.trade_binary_option, startDate, 0, false, lowPrice, false);
td.TradeStatus = ConsTrade.已到期;
td.UnWindDate = startDate;
break;
case "DoubleOneTouch":
var breachHighBarrier = upPrice >= UpperBarrier.Value;
var price = breachHighBarrier ? upPrice : lowPrice;
cash = BinaryOptionDealService.GetCashOrNothingAmount(td, td.trade_binary_option, startDate, useHighAmount: breachHighBarrier);
//触碰上限或下限,买方获得盈利,交易结束
r = binaryService.SaveBinarySettleCash(td, td.trade_binary_option, startDate, cash, true, price, false);
td.TradeStatus = ConsTrade.已执行;
td.UnWindDate = startDate;
break;
case "DoubleNoTouch":
price = upPrice >= td.trade_binary_option.UpperBarrier ? upPrice : lowPrice;
//触碰上限或下限,买方无盈利,交易结束
r = binaryService.SaveBinarySettleCash(td, td.trade_binary_option, startDate, 0, false, price, false);
td.TradeStatus = ConsTrade.已到期;
td.UnWindDate = startDate;
break;
}
if (td.trade_binary_option.RebateType == "AtHit")
{
result.ValueDate = startDate;
}
else if (td.trade_binary_option.RebateType == "AtEnd" && td.ExerciseDate <= endDate)
{
result.ValueDate = td.ExerciseDate.Value;
}
else
{
result = null;
}
}
//OneTouch类型,到期仍未触碰,则交易结束
if (td.trade_binary_option.PayoffType.Contains("OneTouch") && td.ExerciseDate <= startDate)
{
binaryService.SaveBinarySettleCash(td, td.trade_binary_option, startDate, 0, false, _prices[td.UnderlyingCode], false);
td.TradeStatus = ConsTrade.已到期;
td.UnWindDate = startDate;
}
//NoTouch类型,到期仍未触碰,买方获得盈利,交易结束
if (td.trade_binary_option.PayoffType.Contains("NoTouch") && td.ExerciseDate <= endDate)
{
var cash = BinaryOptionDealService.GetCashOrNothingAmount(td, td.trade_binary_option, startDate, useHighAmount: false);
r = binaryService.SaveBinarySettleCash(td, td.trade_binary_option, startDate, cash, false, _prices[td.UnderlyingCode], false);
td.TradeStatus = ConsTrade.已执行;
td.UnWindDate = endDate;
}
result.KO = -r?.Amount;
}
else if (td.ExerciseDate <= endDate)
{
r = new trade_cash();
SettlementCalcCommons.SetPossibleExec(r, td, _prices[td.UnderlyingCode]);
result.Execute = -r.Amount;
result.ValueDate = td.ExerciseDate.Value;
if ((td.SettlementDate ?? td.ExerciseDate.GetValueOrDefault()) > endDate)
{
result = null;
}
}
else
{
result = null;
}
}
break;
case "障碍期权":
{
r = null;
barrierService = barrierService ?? new BarrierOptionKnockioService(UserInfo);
var observDates = QdpHelper.ParseObservationDate(td.trade_barrier_option.ObservationDates)?.ToArray();
if (observDates == null)
{
observDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(
new Date(td.TradeDate), new Date(td.ExerciseDate.Value)).Where(O => O.DateTime != td.TradeDate).ToArray();
}
var lastDate = observDates.Where(O => O >= new Date(startDate) && O <= new Date(endDate)).OrderBy(O => (O)).FirstOrDefault();
if (lastDate != default)
{
switch (td.trade_barrier_option.Discrete)
{
case "离散":
switch (td.trade_barrier_option.BarrierType)
{
case "上升敲出":
td.trade_barrier_option.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
td.trade_barrier_option.KnockInOutDate = lastDate;
td.trade_barrier_option.KnockInOutNotional = td.Notional;
td.TradeStatus = ConsTrade.已平仓;
td.UnWindDate = lastDate;
r = barrierService.SetTradeCash(td, td.trade_barrier_option, lastDate, _prices[td.UnderlyingCode], td.trade_barrier_option.BarrierPrice, false);
break;
case "下降敲出":
td.trade_barrier_option.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
td.trade_barrier_option.KnockInOutDate = lastDate;
td.trade_barrier_option.KnockInOutNotional = td.Notional;
td.TradeStatus = ConsTrade.已平仓;
td.UnWindDate = lastDate;
r = barrierService.SetTradeCash(td, td.trade_barrier_option, lastDate, _prices[td.UnderlyingCode], td.trade_barrier_option.BarrierPrice, false);
break;
case "双障碍敲出":
td.trade_barrier_option.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
td.trade_barrier_option.KnockInOutDate = lastDate;
td.trade_barrier_option.KnockInOutNotional = td.Notional;
td.TradeStatus = ConsTrade.已平仓;
td.UnWindDate = lastDate;
r = barrierService.SetTradeCash(td, td.trade_barrier_option, lastDate, _prices[td.UnderlyingCode], td.trade_barrier_option.UpperBarrierPrice, false);
break;
}
break;
case "连续":
switch (td.trade_barrier_option.BarrierType)
{
case "上升敲出":
td.trade_barrier_option.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
td.trade_barrier_option.KnockInOutDate = lastDate;
td.trade_barrier_option.KnockInOutNotional = td.Notional;
td.TradeStatus = ConsTrade.已平仓;
td.UnWindDate = lastDate;
r = barrierService.SetTradeCash(td, td.trade_barrier_option, lastDate, _prices[td.UnderlyingCode], td.trade_barrier_option.BarrierPrice, false);
break;
case "下降敲出":
td.trade_barrier_option.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
td.trade_barrier_option.KnockInOutDate = lastDate;
td.trade_barrier_option.KnockInOutNotional = td.Notional;
td.TradeStatus = ConsTrade.已平仓;
td.UnWindDate = lastDate;
r = barrierService.SetTradeCash(td, td.trade_barrier_option, lastDate, _prices[td.UnderlyingCode], td.trade_barrier_option.BarrierPrice, false);
break;
case "双障碍敲出":
td.trade_barrier_option.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
td.trade_barrier_option.KnockInOutDate = lastDate;
td.trade_barrier_option.KnockInOutNotional = td.Notional;
td.TradeStatus = ConsTrade.已平仓;
td.UnWindDate = lastDate;
r = barrierService.SetTradeCash(td, td.trade_barrier_option, lastDate, _prices[td.UnderlyingCode], td.trade_barrier_option.UpperBarrierPrice, false);
break;
}
break;
}
result.KO = -r?.Amount;
if (td.trade_barrier_option.RebateType == "AtHit")
{
result.ValueDate = lastDate;
}
else if (td.trade_barrier_option.RebateType == "AtEnd" && td.ExerciseDate <= endDate)
{
result.ValueDate = td.ExerciseDate.Value;
}
else
{
result = null;
}
}
else if (td.ExerciseDate <= endDate)
{
td.TradeStatus = ConsTrade.已到期;
td.UnWindDate = td.ExerciseDate;
r = new trade_cash();
SettlementCalcCommons.SetPossibleExec(r, td, _prices[td.UnderlyingCode]);
result.Execute = -r.Amount;
result.ValueDate = td.ExerciseDate.Value;
if ((td.SettlementDate ?? td.ExerciseDate.GetValueOrDefault()) > endDate)
{
result = null;
}
}
else
{
result = null;
}
}
break;
case "远期":
case "掉期":
{
if (td.ExerciseDate > endDate)
{
result = null;
break;
}
var variety = DataCacheProvider.GetVariety(td.UnderlyingCode);
result.Execute = ((td.Strike - _prices[td.UnderlyingCode]) * td.Notional * variety.CountRatio) ?? 0;
if ((td.BuySell == "卖出" && td.OptionType == "看涨")
|| (td.BuySell == "买入" && td.OptionType == "看跌"))
{
result.Execute *= -1;
}
result.ValueDate = td.ExerciseDate.Value;
result.TradeType = "远期";
if ((td.SettlementDate ?? td.ExerciseDate.GetValueOrDefault()) > endDate)
{
result = null;
}
}
break;
case "累计期权":
{
var payment = 0d;
var cashAtEnd = td.trade_accumulator_option.SettlementMode == "现金期末";
_prices.TryGetValue(td.UnderlyingCode, out var price);
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode);
var notional = td.trade_accumulator_option.AccumuTradeAmount * underlying.CountRatio;
if (cashAtEnd || td.trade_accumulator_option.SettlementMode == "实物交割")
{
td.trade_accumulator_option.SettlementMode = "现金当日";
}
result.ValueDate = startDate;
for (var dt = startDate; dt <= endDate; dt = dt.AddDays(1))
{
var checkResult = TradeModule.AccumulatorOptionModule.TradeAccumulatorService.CheckAccumulatorPayoff(td, td.trade_accumulator_option, dt, price, notional);
if (checkResult == null) continue;
if (checkResult.SettlementMode == "票息" || checkResult.SettlementMode == "现金当日")
{
payment += checkResult.PaymentAmount;
}
else if (checkResult.SettlementMode == "敲出")
{
if (td.trade_accumulator_option.EarlyTerminate)
{
result.ValueDate = dt;
result.KO = cashAtEnd || payment >= 0 ? 0 : -payment;
break;
}
}
if (dt >= td.ExerciseDate.Value)
{
result.ValueDate = dt;
result.Execute = payment >= 0 ? 0 : -payment;
break;
}
}
}
break;
default:
result = null;
break;
}
if (result != null && result.TotleCash != 0)
{
if (td.PremiumPayDate.GetValueOrDefault() > td.TradeDate && td.PremiumPayDate.GetValueOrDefault() >= startDate && td.PremiumPayDate.GetValueOrDefault() <= endDate)
{
var tradeprice = td.TradePrice * (td.BuySell == "卖出" ? -1 : 1);
if (result.KO != 0)
{
result.KO += tradeprice;
}
if (result.Execute != 0)
{
result.Execute += tradeprice;
}
}
}
if (result == null)
{
//避免结构化交易装填判断错误;
td.TradeStatus = "确认成交";
}
return result;
}
}
/// <summary>
/// 流动性风险
/// </summary>
public class FlowRiskModel
{
public int ParentLevel { get; set; }
public string EncryptId { get; set; }
public string TradeType { get; set; }
/// <summary>
/// 日期
/// </summary>
public DateTime? ValueDate { get; set; }
/// <summary>
/// 准备金
/// </summary>
public double TotleCash { get { return (KO ?? 0) + (Execute ?? 0); } }
/// <summary>
/// 交易编号
/// </summary>
public string TradeNumber { get; set; }
/// <summary>
/// 敲出
/// </summary>
public double? KO { get; set; }
/// <summary>
/// 到期
/// </summary>
public double? Execute { get; set; }
public FlowRiskModel()
{
this.KO = 0;
this.Execute = 0;
}
}
}