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

394 lines
18 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.Library.Options.Products.Autocall.Phoenix;
using Qdp.Pricing.Library.Options.Products.Autocall.Snowball;
using YLErp.BLL;
using YLErp.Modules.CalculationModule;
using YLErp.Modules.TradeModule;
namespace YLErp.Modules.EodModule
{
/// <summary>
/// 检查交易观察日状态和票息是否产生
/// </summary>
public class CheckTradeObservateStatusService : YLBaseService
{
public CheckTradeObservateStatusService(YLBaseService baseService) : base(baseService)
{
}
public CheckTradeObservateStatusService(OptUserInfo userInfo) : base(userInfo)
{
}
public KeyValuePair<string, bool> CheckTradeObservateStatus(trade td, DateTime valuedate, double[] price, double? SettlementAmount)
{
new TradeExtendService(OptUser, DbContext).SetTradeExtend(new[] { td }, tracking: true);
switch (td.TradeType)
{
case "障碍期权":
if (!string.IsNullOrEmpty(td.trade_barrier_option.KnockInOutStatus))
{
return new KeyValuePair<string, bool>();
}
if (td.TradeStatus != ConsTrade.确认成交)
{
return new KeyValuePair<string, bool>();
}
return checkBarrierObservateStatus(td, valuedate, price);
case "双鲨期权":
if (!string.IsNullOrEmpty(td.trade_double_sharkfin_option.KnockInOutStatus))
{
return new KeyValuePair<string, bool>();
}
if (td.TradeStatus != ConsTrade.确认成交)
{
return new KeyValuePair<string, bool>();
}
return checkDoubleSharkObservateStatus(td, valuedate, price);
case "区间累积期权":
return checkRangeAccrualObservateStatus(td, valuedate, price);
case "凤凰期权":
return checkAutocallObservateStatus(td, valuedate, price, SettlementAmount);
case "雪球期权":
return checkSnowballObservateStatus(td, valuedate, price, SettlementAmount);
case "气囊结构":
return checkAirbagObservateStatus(td, valuedate, price);
default:
return new KeyValuePair<string, bool>();
}
}
public KeyValuePair<string, bool> checkBarrierObservateStatus(trade td, DateTime valuedate, double[] price)
{
var tradeBarrier = td.trade_barrier_option;
var BarrierPrice = td.IsMoneynessOptionData ? tradeBarrier.BarrierPrice * td.SpotPrice : tradeBarrier.BarrierPrice;
var UpperBarrierPrice = td.IsMoneynessOptionData ? tradeBarrier.UpperBarrierPrice * td.SpotPrice : tradeBarrier.UpperBarrierPrice;
var highPrice = tradeBarrier.Discrete == "离散" ? price[0] : price[1];
var lowPrice = tradeBarrier.Discrete == "离散" ? price[0] : price[2];
switch (tradeBarrier.BarrierType)
{
case "上升敲入":
if (highPrice >= BarrierPrice)
{
return new KeyValuePair<string, bool>(ConsTrade.KnockState.KnockedIn, false);
}
break;
case "上升敲出":
if (highPrice >= BarrierPrice)
{
return new KeyValuePair<string, bool>(ConsTrade.KnockState.KnockedOut, true);
}
break;
case "下降敲入":
if (lowPrice <= BarrierPrice)
{
return new KeyValuePair<string, bool>(ConsTrade.KnockState.KnockedIn, false);
}
break;
case "下降敲出":
if (lowPrice <= BarrierPrice)
{
return new KeyValuePair<string, bool>(ConsTrade.KnockState.KnockedOut, true);
}
break;
case "双障碍敲出":
if (highPrice >= UpperBarrierPrice || lowPrice <= BarrierPrice)
{
return new KeyValuePair<string, bool>(ConsTrade.KnockState.KnockedOut, true);
}
break;
case "双障碍敲入":
if (highPrice >= UpperBarrierPrice || lowPrice <= BarrierPrice)
{
return new KeyValuePair<string, bool>(ConsTrade.KnockState.KnockedIn, false);
}
break;
}
return new KeyValuePair<string, bool>();
}
public KeyValuePair<string, bool> checkDoubleSharkObservateStatus(trade td, DateTime valuedate, double[] price)
{
var tradeDbShark = td.trade_double_sharkfin_option;
var barrierHigh = td.IsMoneynessOptionData ? tradeDbShark.BarrierHigh * td.SpotPrice : tradeDbShark.BarrierHigh;
var barrierLow = td.IsMoneynessOptionData ? tradeDbShark.BarrierLow * td.SpotPrice : tradeDbShark.BarrierLow;
var highPrice = price[1];
var lowPrice = price[2];
if (highPrice >= barrierHigh || lowPrice <= barrierLow)
{
return new KeyValuePair<string, bool>(ConsTrade.KnockState.KnockedOut, true);
}
return new KeyValuePair<string, bool>();
}
public KeyValuePair<string, bool> checkRangeAccrualObservateStatus(trade td, DateTime valuedate, double[] price)
{
var tradeRange = td.trade_rangeaccrual;
var upperRange = td.IsMoneynessOptionData ? tradeRange.UpperRange * td.SpotPrice.Value : tradeRange.UpperRange;
var lowerRange = td.IsMoneynessOptionData ? tradeRange.LowerRange * td.SpotPrice.Value : tradeRange.LowerRange;
//有区间收益
if (price[0] < upperRange && price[0] > lowerRange)
{
return new KeyValuePair<string, bool>("", true);
}
return new KeyValuePair<string, bool>("", false);
}
public KeyValuePair<string, bool> checkAutocallObservateStatus(trade td, DateTime valuedate, double[] price, double? SettlementAmount)
{
var tradeCashs = DbContext.trade_cash.Where(x => x.ValidState != ConsGlobal.InValid && !x.IsDeleted && x.TradeId == td.id && x.Action == "系统操作-平仓费" && (x.ValueDate > valuedate && (x.ConfirmDate > valuedate || x.ConfirmDate == DateTime.MinValue)) && x.UnwindNotional < x.Notional).ToList();
var notional = (ConsTrade.TradeCompleteStatus.Contains(td.TradeStatus) && td.UnWindDate <= valuedate ? 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;
if (autocall.KOObsDates.Select(x => x.DateTime).Contains(valuedate)
&& 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(valuedate);
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;
var isKnockedOut = price[0] >= koBarrier;
//有票息
if (price[0] >= couponBarrier)
{
//利息计算时,当autocall的Notional包含了符号,则GetEffectiveObservation考虑了买卖方向了
var observation = autocall.GetEffectiveObservation(new Date(valuedate));
if (observation != null)
{
//观察日页面设置的结算金额
if (SettlementAmount != null)
{
//到期敲入且未敲出情况
if (valuedate == autocall.ExerciseDates.Last().DateTime && !isKnockedOut)
{
//当前满足敲入或者已经敲入了
if (autocall.KIObsDates.Select(x => x.DateTime).Contains(valuedate) && price[0] <= kiBarrier || td.trade_autocall.KnockInOutStatus == ConsTrade.KnockState.KnockedIn)
{
return new KeyValuePair<string, bool>(ConsTrade.KnockState.KnockedIn, true);
}
}
}
//SaveCouponObservation(trade.trade, observation, valuedate, closePrice, isKnockedOut);
}
return new KeyValuePair<string, bool>(ConsTrade.KnockState.Monitoring, true);
}
#endregion
#region 敲出检查
// 发生敲出事件
if (isKnockedOut)
{
return new KeyValuePair<string, bool>(ConsTrade.KnockState.KnockedOut, false);
}
#endregion
}
#region 敲入检查
//在当前结算日之前未敲出且未敲入:
if (!((td.trade_autocall.KnockInOutStatus == ConsTrade.KnockState.KnockedOut
|| td.trade_autocall.KnockInOutStatus == ConsTrade.KnockState.KnockedIn)
&& td.trade_autocall.KnockInOutDate < valuedate)
&& autocall.KIObsDates.Select(x => x.DateTime).Contains(valuedate))
{
// 发生敲入事件
if (price[0] <= kiBarrier)
{
return new KeyValuePair<string, bool>(ConsTrade.KnockState.KnockedIn, false);
}
}
#endregion
#region 到期检查
if (valuedate == autocall.ExerciseDates.Last().DateTime)
{
td.UnWindDate = valuedate;
if (td.trade_autocall.KnockInOutStatus == ConsTrade.KnockState.KnockedIn)
{
return new KeyValuePair<string, bool>(ConsTrade.KnockState.KnockedIn, false);
}
else if (td.trade_autocall.KnockInOutStatus != ConsTrade.KnockState.KnockedOut)
{
return new KeyValuePair<string, bool>(ConsTrade.KnockState.Monitoring, false);
}
else if (td.trade_autocall.KnockInOutStatus == ConsTrade.KnockState.KnockedOut)
{
return new KeyValuePair<string, bool>(ConsTrade.KnockState.KnockedOut, false);
}
}
#endregion
return new KeyValuePair<string, bool>();
}
public KeyValuePair<string, bool> checkSnowballObservateStatus(trade td, DateTime valueDate, double[] price, double? SettlementAmount)
{
var tradeCashs = DbContext.trade_cash.Where(x => x.ValidState != ConsGlobal.InValid && !x.IsDeleted && x.TradeId == td.id && x.Action == "系统操作-平仓费" && (x.ValueDate > valueDate && (x.ConfirmDate > valueDate || 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 <= valueDate ? 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;
//只在敲出观察日检查敲出和票息情况
//如果交易已经是敲出状态了,不用再做票息和敲出检查
if (snowball.KOObsDates.Select(x => x.DateTime).Contains(valueDate)
&& td.trade_snowball.KnockInOutStatus != ConsTrade.KnockState.KnockedOut)
{
#region 敲出检查
double koBarrier;
var koSettleDate = valueDate;
if (snowball.CustomizedKOBarriers != null && snowball.CustomizedKOBarriers.Length > 0)
{
var index = snowball.KOObsDates.Select(x => x.DateTime).ToList().IndexOf(valueDate);
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 < valueDate)
{
koSettleDate = valueDate;
}
if (td.IsMoneynessOptionData)
{
koBarrier *= td.SpotPrice ?? 1.0;
}
// 发生敲出事件
if (price[0] >= koBarrier)
{
if (snowball.UseOptionPayoffAtKO)
{
return new KeyValuePair<string, bool>(ConsTrade.KnockState.KnockedOut, false);
}
else
{
if (SettlementAmount != null)
{
return new KeyValuePair<string, bool>(ConsTrade.KnockState.KnockedOut, false);
}
else
{
return new KeyValuePair<string, bool>(ConsTrade.KnockState.KnockedOut, true);
}
}
}
return new KeyValuePair<string, bool>(td.trade_snowball.KnockInOutStatus, true);
#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 < valueDate)
&& snowball.KIObsDates.Select(x => x.DateTime).Contains(valueDate)
&& td.trade_snowball.KIPayoffType != KIPayoffTypeEnum.None)
{
var kiBarrier =
td.IsMoneynessOptionData ?
td.trade_snowball.KIBarrier * td.SpotPrice :
td.trade_snowball.KIBarrier;
// 发生敲入事件
if (price[0] <= kiBarrier)
{
return new KeyValuePair<string, bool>(ConsTrade.KnockState.KnockedIn, false);
}
}
#endregion
#region 到期检查
if (valueDate == snowball.ExerciseDates.Last().DateTime)
{
if (snowball.UseOptionPayoffAtMaturity &&
(td.trade_snowball.KnockInOutStatus == ConsTrade.KnockState.KnockedIn || td.trade_snowball.KIBarrier <= 0))
{
return new KeyValuePair<string, bool>(ConsTrade.KnockState.KnockedIn, false);
}
else if (td.trade_snowball.KnockInOutStatus != ConsTrade.KnockState.KnockedOut)
{
return new KeyValuePair<string, bool>(ConsTrade.KnockState.Monitoring, false);
}
}
#endregion
return new KeyValuePair<string, bool>();
}
public KeyValuePair<string, bool> checkAirbagObservateStatus(trade td, DateTime valuedate, double[] price)
{
//气囊结构暂时都是每日连续观察,不用考虑观察周期问题
//气囊结构暂时都是向下敲入
var tradeAirbag = td.trade_airbag;
if (valuedate == td.ExerciseDate)
{
if (price[0] >= td.SpotPrice)
{
return new KeyValuePair<string, bool>(tradeAirbag.KnockInOutStatus, true);
}
else if (tradeAirbag.KnockInOutStatus == ConsTrade.KnockState.KnockedIn)
{
return new KeyValuePair<string, bool>(tradeAirbag.KnockInOutStatus, true);
}
else
{
return new KeyValuePair<string, bool>(tradeAirbag.KnockInOutStatus, false);
}
}
return new KeyValuePair<string, bool>(tradeAirbag.KnockInOutStatus, false);
}
}
}