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

790 lines
42 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.Snowball;
using YLErp.Abstract.DataProviders;
using YLErp.BLL;
using YLErp.Commons;
using YLErp.DBModels.Consts;
using YLErp.DBModels.Helpers;
using YLErp.Modules.CalculationModule;
using YLErp.Modules.DataProviderModule;
using YLErp.Modules.SalesModule;
using YLErp.Modules.TradeModule.DealModule;
using YLErp.Modules.TradeModule.ExoticOptionModule;
namespace YLErp.Modules.TradeModule
{
public class TradeSnowballBLL : ExoticOptionModule.TradeCashServiceEx
{
public TradeSnowballBLL(YLBaseService baseService) : base(baseService)
{
}
public TradeSnowballBLL(OptUserInfo userInfo) : base(userInfo)
{
}
public void CheckStatus(DateTime valueDate, IEodPriceProviderV2 priceProvider, DateTime? startDate = null
, Action<OtcTrade, trade_snowball> afterKnowInOut = null, IEnumerable<int> clienIds = null)
{
priceProvider ??= new EodPriceProvider(valueDate);
if (startDate == null)
{
startDate = valueDate.AddYears(-5);
}
var query = from trade in DbContext.trade
join snowball in DbContext.trade_snowball on trade.id equals snowball.TradeId
join underlying in DbContext.underlying_manager on trade.UnderlyingId equals underlying.id
where trade.TradeDate > startDate.Value && trade.TradeDate <= valueDate
&& trade.ExerciseDate >= valueDate
&& ConsTrade.确认成交 == trade.TradeStatus && (trade.TradeType == "雪球期权")
&& trade.ValidState != ConsGlobal.InValid
&& (snowball.KnockInOutStatus != ConsTrade.KnockState.KnockedOut || (snowball.KnockInOutStatus == ConsTrade.KnockState.KnockedOut && snowball.KnockInOutDate >= valueDate))
&& trade.DividendDate < valueDate
select new
{
underlying = underlying,
trade = trade,
trade_snowball = snowball
};
#region 增加客户筛选 tw
if (clienIds != null)
{
query = query.Where(l => clienIds.Contains(l.trade.ClientId));
}
#endregion
//未敲出的,以及已敲出但敲出日期大于等于当前收盘日的(为了历史收盘)
var trades = query.ToList();
var tradeIds = trades.Select(x => x.trade.id).ToList();
var manuallyTradeObservationPrices = DbContext.manually_trade_observation_price
.Where(x => tradeIds.Contains(x.TradeId) && x.ValueDate == valueDate).ToDictionary(n => n.TradeId);
trades.ForEach(tr =>
{
if (tr.trade.ExerciseDate < valueDate)
{
return;//已到期交易不再观察;
}
var tradeStatus = tr.trade.TradeStatus;
double closePrice;
double? SettlementAmount = null;
if (manuallyTradeObservationPrices.TryGetValue(tr.trade.id, out var manuallyTradeObservationPrice))
{
closePrice = manuallyTradeObservationPrice.Price ?? 0;
SettlementAmount = manuallyTradeObservationPrice.SettlementAmount;
}
else if (!priceProvider.TryGetEodPrice(tr.trade.UnderlyingCode, out var eodPrice))
{
throw new Exception($"[{tr.trade.TradeType}:{tr.trade.TradeNumber},标的:{tr.trade.UnderlyingCode}]未找到结算价");
}
else
{
closePrice = eodPrice.ClosePrice;
}
var oldKnockInOutStatus = tr.trade_snowball.KnockInOutStatus;
CheckSnowballKnockInOutStatus(tr.trade, tr.trade_snowball, valueDate, closePrice, SettlementAmount);
if (oldKnockInOutStatus != tr.trade_snowball.KnockInOutStatus)
{
var KnockInOutStatus = tr.trade_snowball.KnockInOutStatus == ConsTrade.KnockState.KnockedIn ? "敲入" : "敲出";
LogFactory.GetLogger("收盘检查雪球").Info($"{tr.trade.TradeNumber}--{KnockInOutStatus}--调试");
AddTradeOperationHistoryAndSetParentTradeInfo(false, tr.trade, KnockInOutStatus, KnockInOutStatus);
}
if (tradeStatus != tr.trade.TradeStatus || oldKnockInOutStatus != tr.trade_snowball.KnockInOutStatus)
{
//删除E/Bod_Trade记录
RemoveEodTradeAndFutureInfo(false, tr.trade.id, valueDate);
}
if (afterKnowInOut != null && DbContext.Entry(tr.trade_snowball).State == EntityState.Modified)
{
afterKnowInOut(tr.trade, tr.trade_snowball);
}
});
DbContext.SaveChanges();
}
//获取雪球期权敲出时要准备的信息
public (DateTime koSettleDate, double koBarrier) GetKoSettleInfo(DateTime valueDate, OtcTradeBase otcTrade, trade_snowball tradeSnowball, SimpleSnowball snowball)
{
double koBarrier;
var koSettleDate = valueDate;
var datesStr = tradeSnowball.KOObservationSettleDates;
var KOObsSettleDates = string.IsNullOrWhiteSpace(datesStr) ? null : datesStr.Split(new char[] { ',', ';', '', '' }, StringSplitOptions.RemoveEmptyEntries).Select(x => DateTime.Parse(x)).ToArray();
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 = tradeSnowball.KOBarrier;
if (KOObsSettleDates != null && KOObsSettleDates.Any())
{
koSettleDate = KOObsSettleDates[0];
}
}
if (tradeSnowball.KORebateType == RebateTypeEnum.AtEnd)
{
koSettleDate = otcTrade.ExerciseDate.Value;
}
else if (koSettleDate < valueDate)
{
koSettleDate = valueDate;
}
if (otcTrade.IsMoneynessOptionData)
{
koBarrier *= otcTrade.SpotPrice ?? 1.0;
}
return (koSettleDate, koBarrier);
}
public SnowballObservationResult GetDefaultAmountForSpecialSnowball(OtcTradeBase otcTrade, trade_snowball tradeSnowball, DateTime valueDate, double closePrice)
{
var tradeCashs = DbContext.trade_cash.Where(x => x.ValidState != ConsGlobal.InValid && !x.IsDeleted && x.TradeId == otcTrade.id && x.Action == "系统操作-平仓费" && x.ValueDate > valueDate && (x.ConfirmDate > valueDate || x.ConfirmDate == DateTime.MinValue) && x.UnwindNotional < x.Notional).ToList();
var tradeNotional = (ConsTrade.TradeCompleteStatus.Contains(otcTrade.TradeStatus) && otcTrade.UnWindDate <= valueDate ? 0 : otcTrade.Notional) + tradeCashs.Sum(x => x.UnwindNotional).Value;
return tradeSnowball.PrepaymentUsed
? new SpecialSnowballObservationHelper(otcTrade, tradeSnowball).GetObservationResultForTraderSide(valueDate, closePrice, tradeNotional)
: throw new ServiceFaultException("不支持非预付金形式的雪球");
}
public double GetDefaultAmount(OtcTradeBase otcTrade, trade_snowball tradeSnowball, DateTime valueDate, double closePrice)
{
var tradeCashs = DbContext.trade_cash.Where(x => x.ValidState != ConsGlobal.InValid && !x.IsDeleted && x.TradeId == otcTrade.id && x.Action == "系统操作-平仓费" && x.ValueDate > valueDate && (x.ConfirmDate > valueDate || x.ConfirmDate == DateTime.MinValue) && x.UnwindNotional < x.Notional).ToList();
var tradeNotional = (ConsTrade.TradeCompleteStatus.Contains(otcTrade.TradeStatus) && otcTrade.UnWindDate <= valueDate ? 0 : otcTrade.Notional) + tradeCashs.Sum(x => x.UnwindNotional).Value;
if (tradeSnowball.PrepaymentUsed)
{
var result = new SpecialSnowballObservationHelper(otcTrade, tradeSnowball).GetObservationResultForTraderSide(valueDate, closePrice, tradeNotional);
return result.PaymentAmount;
}
var defaultAmount = 0d;
var request = new OptionTradeParamRequest(valuedateBLL.SysRiskFreeRate())
{
ParamOverride = p => p.notional = tradeNotional
};
var optionTrade = QdpTradeBuilder.GetSnowballOptionTrade(otcTrade, tradeSnowball, request);
var snowball = (SimpleSnowball)optionTrade.Instrument;
var isCall = ConsGlobal.CallPut.IsCall(otcTrade.CallPut);
//只在敲出观察日检查敲出和票息情况
//如果交易已经是敲出状态了,不用再做票息和敲出检查
if (snowball.KOObsDates.Select(x => x.DateTime).Contains(valueDate)
&& tradeSnowball.KnockInOutStatus != ConsTrade.KnockState.KnockedOut)
{
#region 敲出检查
(var koSettleDate, var koBarrier) = GetKoSettleInfo(valueDate, otcTrade, tradeSnowball, snowball);
// 发生敲出事件(看涨 - 向上敲出支付票息,看跌 - 向下敲出支付票息)
if (isCall ? closePrice >= koBarrier : closePrice <= koBarrier)
{
tradeSnowball.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
if (snowball.UseOptionPayoffAtKO)
{
var koOptionCashflows = snowball.GetKOPayoff(new Date(valueDate), closePrice);
//koOptionCashflows[0]..PaymentAmount包含了买卖方向的处理了
defaultAmount = TradeHelper.GetAmountByPaymentAmount(koOptionCashflows[0].PaymentAmount, otcTrade.PrincipalSum(), otcTrade.BuySell);
}
else
{
var CouponPayment = snowball.CouponPayment(valueDate, includeStartDate: tradeSnowball.CouponIncludeStartDate == true && tradeSnowball.CouponDayCount != "Monthly");
defaultAmount = TradeHelper.GetAmountByPaymentAmount(CouponPayment, otcTrade.PrincipalSum(), otcTrade.BuySell);
if (tradeSnowball.AnnualizedPremiumRate.HasValue && tradeSnowball.AnnualizedPremiumRate != 0)
{
var tradePrice = (otcTrade.StockEqvNotional * otcTrade.ParticipationRate * tradeSnowball.AnnualizedPremiumRate * snowball.CouponDayCount.CalcDayCountFraction(snowball.StartDate, new Date(valueDate))) ?? 0;
if (tradePrice != 0)
{
defaultAmount += (otcTrade.BuySell == "买入" ? -1 : 1) * tradePrice;
}
}
}
return defaultAmount; //已经敲出了,不需要再继续走下去了
}
#endregion
}
#region 敲入检查
//在当前结算日之前未敲出且未敲入:
// !((KO || KI) && KOKIDate < valueDate)
if (!((tradeSnowball.KnockInOutStatus == ConsTrade.KnockState.KnockedOut
|| tradeSnowball.KnockInOutStatus == ConsTrade.KnockState.KnockedIn) && tradeSnowball.KnockInOutDate < valueDate)
&& snowball.KIObsDates.Select(x => x.DateTime).Contains(valueDate) && tradeSnowball.KIPayoffType != KIPayoffTypeEnum.None)
{
var kiBarrier = otcTrade.IsMoneynessOptionData ? tradeSnowball.KIBarrier * otcTrade.SpotPrice : tradeSnowball.KIBarrier;
// 发生敲入事件(看涨 - 向下敲入,看跌 - 向上敲入)
if (isCall ? closePrice <= kiBarrier : closePrice >= kiBarrier)
{
// 更新观察状态
tradeSnowball.KnockInOutStatus = ConsTrade.KnockState.KnockedIn;
}
}
#endregion
#region 到期检查
if (valueDate == snowball.ExerciseDates.Last().DateTime)
{
if (snowball.UseOptionPayoffAtMaturity &&
(tradeSnowball.KnockInOutStatus == ConsTrade.KnockState.KnockedIn || tradeSnowball.KIBarrier <= 0))
{
//已敲入,到期时计算期权收益
var optionPayoffPayment = snowball.GetPayoff(new double[] { closePrice });
//optionPayoffPayment[0].PaymentAmount包含了买卖方向的处理了
defaultAmount = TradeHelper.GetAmountByPaymentAmount(optionPayoffPayment[0].PaymentAmount, otcTrade.PrincipalSum(), otcTrade.BuySell);
}
else if (tradeSnowball.KnockInOutStatus != ConsTrade.KnockState.KnockedOut)
{
var startDate = tradeSnowball.CouponIncludeStartDate == true ? snowball.StartDate.AddDays(-1) : snowball.StartDate;
var maturityCouponRate = snowball.Coupon * snowball.InitialSpotPrice;
var maturityCouponPayment =
snowball.FixedCoupon ?
maturityCouponRate * snowball.Notional :
maturityCouponRate * snowball.Notional * snowball.CouponDayCount.CalcDayCountFraction(startDate, snowball.ExerciseDates.Last());
maturityCouponPayment = TradeHelper.GetAmountByPaymentAmount(maturityCouponPayment, otcTrade.PrincipalSum(), otcTrade.BuySell);
defaultAmount = maturityCouponPayment;
if (tradeSnowball.AnnualizedPremiumRate.HasValue && tradeSnowball.AnnualizedPremiumRate != 0)
{
var tradePrice = (otcTrade.StockEqvNotional * otcTrade.ParticipationRate * tradeSnowball.AnnualizedPremiumRate * snowball.CouponDayCount.CalcDayCountFraction(snowball.StartDate, new Date(valueDate))) ?? 0;
if (tradePrice != 0)
{
defaultAmount += (otcTrade.BuySell == "买入" ? -1 : 1) * tradePrice;
}
}
}
}
#endregion
return defaultAmount;
}
public void CheckSnowballKnockInOutStatus(OtcTradeBase otcTrade, trade_snowball tradeSnowball, DateTime valueDate, double closePrice, double? settlementAmount)
{
if (otcTrade is null)
{
throw new ArgumentNullException(nameof(otcTrade));
}
if (tradeSnowball is null)
{
throw new ArgumentNullException(nameof(tradeSnowball));
}
try
{
var tradeStatusOld = otcTrade.TradeStatus;
InnerCheckSnowballKnockInOutStatus(otcTrade, tradeSnowball, valueDate, closePrice, settlementAmount);
//生成确认书
if (PS.Config.IsAutoGenerateContracts && tradeStatusOld == ConsTrade.确认成交 && ConsTrade.TradeCompleteStatus.Contains(otcTrade.TradeStatus))
{
//修改销售提成的状态
new SalesCommissionDataService(this).SetCommissionVaild(otcTrade.id);
new TradeContractGenerateService(this).GenerateContractsAsync(new List<int> { otcTrade.id });
}
}
catch (Exception ex)
{
throw new ServiceException($"[检查敲入敲出]交易编号:{otcTrade.TradeNumber},{ex.Message}", ex);
}
}
private void InnerCheckSnowballKnockInOutStatus(
OtcTradeBase otcTrade, trade_snowball tradeSnowball, DateTime valueDate, double closePrice, double? SettlementAmount)
{
var tradeCashs = DbContext.trade_cash.Where(x => x.ValidState != ConsGlobal.InValid && !x.IsDeleted && x.TradeId == otcTrade.id && x.Action == "系统操作-平仓费" && x.ValueDate > valueDate && (x.ConfirmDate > valueDate || x.ConfirmDate == DateTime.MinValue) && x.UnwindNotional < x.Notional).ToList();
var tradeNotional = (ConsTrade.TradeCompleteStatus.Contains(otcTrade.TradeStatus) && otcTrade.UnWindDate <= valueDate ? 0 : otcTrade.Notional) + tradeCashs.Sum(x => x.UnwindNotional).Value;
if (tradeSnowball.PrepaymentUsed)
{
ProcessSpecialSnowball(otcTrade, tradeSnowball, valueDate, closePrice, tradeNotional, SettlementAmount);
return;
}
var request = new OptionTradeParamRequest(valuedateBLL.SysRiskFreeRate())
{
ParamOverride = p => p.notional = tradeNotional
};
var optionTrade = QdpTradeBuilder.GetSnowballOptionTrade(otcTrade, tradeSnowball, request);
var snowball = (SimpleSnowball)optionTrade.Instrument;
var isCall = ConsGlobal.CallPut.IsCall(otcTrade.CallPut);
//只在敲出观察日检查敲出和票息情况
//如果交易已经是敲出状态了,不用再做票息和敲出检查
if (snowball.KOObsDates.Select(x => x.DateTime).Contains(valueDate)
&& tradeSnowball.KnockInOutStatus != ConsTrade.KnockState.KnockedOut)
{
#region 敲出检查
(var koSettleDate, var koBarrier) = GetKoSettleInfo(valueDate, otcTrade, tradeSnowball, snowball);
// 发生敲出事件(看涨 - 向上敲出支付票息,看跌 - 向下敲出支付票息)
if (isCall ? closePrice >= koBarrier : closePrice <= koBarrier)
{
// 更新观察状态
tradeSnowball.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
tradeSnowball.KnockInOutDate = valueDate;
// 更新交易状态
otcTrade.TradeStatus = ConsTrade.已平仓;
otcTrade.UnWindDate = valueDate;
if (snowball.UseOptionPayoffAtKO)
{
double paymentAmount = 0;
var koOptionCashflows = snowball.GetKOPayoff(new Date(valueDate), closePrice);
if (SettlementAmount != null)
{
paymentAmount = SettlementAmount.Value;
}
else
{
paymentAmount = TradeHelper.GetAmountByPaymentAmount(koOptionCashflows[0].PaymentAmount, otcTrade.PrincipalSum(), otcTrade.BuySell);
}
var parentTradeId = 0;
var parentTradeCashId = 0;
if (otcTrade.IsGroup == 2 && otcTrade.ParentTradeId > 0)
{
var groupAction = DbContext.trade_cash_group_action.FirstOrDefault(x => x.TradeId == otcTrade.id && x.Status != "已完成");
if (groupAction != null)
{
groupAction.Status = "已完成";
parentTradeCashId = groupAction.ParentTradeCashId;
parentTradeId = groupAction.ParentTradeId;
}
else
{
parentTradeId = otcTrade.ParentTradeId;
parentTradeCashId = SaveGroupUnwindCash(otcTrade, valueDate, paymentAmount, closePrice, out var continueTradeCashHandle).id;
}
}
SaveOptionPayoffCash(otcTrade, paymentAmount, koSettleDate, closePrice, ClientCashInCashOut.系统操作_平仓费, TradeCashExerciseWayEnum.提前终止行权, valueDate, true, parentTradeId, parentTradeCashId);
//koOptionCashflows[0].PaymentAmount包含了买卖方向的处理了
}
else
{
if (SettlementAmount != null)
{
double tradePrice = 0;
var _settlementAmount = SettlementAmount.Value;
var parentTradeId = 0;
var parentTradeCashId = 0;
if (otcTrade.IsGroup == 2 && otcTrade.ParentTradeId > 0)
{
var groupAction = DbContext.trade_cash_group_action.FirstOrDefault(x => x.TradeId == otcTrade.id && x.Status != "已完成");
if (groupAction != null)
{
groupAction.Status = "已完成";
parentTradeCashId = groupAction.ParentTradeCashId;
parentTradeId = groupAction.ParentTradeId;
}
else
{
parentTradeId = otcTrade.ParentTradeId;
parentTradeCashId = SaveGroupUnwindCash(otcTrade, valueDate, _settlementAmount, closePrice, out var continueTradeCashHandle).id;
}
}
//观察日价格页面的结算金额包含了年化期权费,该处逻辑需要先按照扣除年化期权费来算,后面逻辑会补上年化期权费,否则会重复运算
if (tradeSnowball.AnnualizedPremiumRate.HasValue && tradeSnowball.AnnualizedPremiumRate != 0)
{
tradePrice = otcTrade.StockEqvNotional * otcTrade.ParticipationRate * tradeSnowball.AnnualizedPremiumRate * snowball.CouponDayCount.CalcDayCountFraction(snowball.StartDate, new Date(valueDate)) ?? 0;
if (tradePrice != 0)
{
_settlementAmount -= (otcTrade.BuySell == "买入" ? -1 : 1) * tradePrice;
}
}
var tradeCash = SaveCouponAmountCash(otcTrade, _settlementAmount, koSettleDate, closePrice, valueDate, parentTradeId, parentTradeCashId);
if (tradePrice != 0 && otcTrade.PremiumPayDate <= valueDate)
{
SaveTradePrice(otcTrade, tradeCash, tradePrice, koSettleDate);
}
}
else
{
var CouponPayment = snowball.CouponPayment(valueDate, includeStartDate: tradeSnowball.CouponIncludeStartDate == true && tradeSnowball.CouponDayCount != "Monthly");
var couponPayment = TradeHelper.GetAmountByPaymentAmount(CouponPayment, otcTrade.PrincipalSum(), otcTrade.BuySell);
var parentTradeId = 0;
var parentTradeCashId = 0;
var hasUnfinishedGroupAction = false;
var continueTradeCashHandle = false;
var groupTradeCash = new trade_cash();
if (otcTrade.IsGroup == 2 && otcTrade.ParentTradeId > 0)
{
var groupAction = DbContext.trade_cash_group_action.FirstOrDefault(x => x.TradeId == otcTrade.id && x.Status != "已完成");
if (groupAction != null)
{
hasUnfinishedGroupAction = true;
groupAction.Status = "已完成";
parentTradeCashId = groupAction.ParentTradeCashId;
parentTradeId = groupAction.ParentTradeId;
}
else
{
parentTradeId = otcTrade.ParentTradeId;
groupTradeCash = SaveGroupUnwindCash(otcTrade, valueDate, couponPayment, closePrice, out continueTradeCashHandle);
parentTradeCashId = groupTradeCash.id;
}
}
var tradeCash = SaveCouponAmountCash(otcTrade, couponPayment, koSettleDate, closePrice, valueDate, parentTradeId, parentTradeCashId);
if (tradeSnowball.AnnualizedPremiumRate.HasValue && tradeSnowball.AnnualizedPremiumRate != 0)
{
var tradePrice = (otcTrade.StockEqvNotional * otcTrade.ParticipationRate * tradeSnowball.AnnualizedPremiumRate * snowball.CouponDayCount.CalcDayCountFraction(snowball.StartDate, new Date(valueDate))) ?? 0;
if (tradePrice != 0 && otcTrade.PremiumPayDate <= valueDate)
{
SaveTradePrice(otcTrade, tradeCash, tradePrice, koSettleDate);
if (otcTrade.IsGroup == 2 && otcTrade.ParentTradeId > 0 && !hasUnfinishedGroupAction && continueTradeCashHandle)
{
groupTradeCash.Amount += (otcTrade.BuySell == "买入" ? -1 : 1) * tradePrice;
DbContext.SaveChanges();
}
}
}
}
}
LogFactory.GetLogger("收盘检查雪球").Info($"{otcTrade.TradeNumber}--敲出");
return; //已经敲出了,不需要再继续走下去了
}
#endregion
}
#region 敲入检查
//在当前结算日之前未敲出且未敲入:
// !((KO || KI) && KOKIDate < valueDate)
if (!((tradeSnowball.KnockInOutStatus == ConsTrade.KnockState.KnockedOut
|| tradeSnowball.KnockInOutStatus == ConsTrade.KnockState.KnockedIn)
&& tradeSnowball.KnockInOutDate < valueDate)
&& snowball.KIObsDates.Select(x => x.DateTime).Contains(valueDate) && tradeSnowball.KIPayoffType != KIPayoffTypeEnum.None)
{
var kiBarrier =
otcTrade.IsMoneynessOptionData ?
tradeSnowball.KIBarrier * otcTrade.SpotPrice :
tradeSnowball.KIBarrier;
// 发生敲入事件(看涨时向下敲入,看跌时向上敲入)
if (isCall ? closePrice <= kiBarrier : closePrice >= kiBarrier)
{
// 更新观察状态
tradeSnowball.KnockInOutStatus = ConsTrade.KnockState.KnockedIn;
tradeSnowball.KnockInOutDate = valueDate;
}
}
#endregion
#region 到期检查
if (valueDate == snowball.ExerciseDates.Last().DateTime)
{
if (snowball.UseOptionPayoffAtMaturity &&
(tradeSnowball.KnockInOutStatus == ConsTrade.KnockState.KnockedIn || tradeSnowball.KIBarrier <= 0))
{
otcTrade.TradeStatus = ConsTrade.已执行;
otcTrade.UnWindDate = valueDate;
double paymentAmount = 0;
//已敲入,到期时计算期权收益
var optionPayoffPayment = snowball.GetPayoff(new double[] { closePrice });
if (SettlementAmount != null)
{
paymentAmount = SettlementAmount.Value;
}
else
{
paymentAmount = TradeHelper.GetAmountByPaymentAmount(optionPayoffPayment[0].PaymentAmount, otcTrade.PrincipalSum(), otcTrade.BuySell);
}
var parentTradeId = 0;
var parentTradeCashId = 0;
if (otcTrade.IsGroup == 2 && otcTrade.ParentTradeId > 0)
{
var groupAction = DbContext.trade_cash_group_action.FirstOrDefault(x => x.TradeId == otcTrade.id && x.Status != "已完成");
if (groupAction != null)
{
groupAction.Status = "已完成";
parentTradeCashId = groupAction.ParentTradeCashId;
parentTradeId = groupAction.ParentTradeId;
}
else
{
parentTradeId = otcTrade.ParentTradeId;
parentTradeCashId = SaveGroupUnwindCash(otcTrade, valueDate, paymentAmount, closePrice, out var continueTradeCashHandle).id;
}
}
//optionPayoffPayment[0].PaymentAmount包含了买卖方向的处理了
SaveOptionPayoffCash(otcTrade, paymentAmount, valueDate, closePrice, ClientCashInCashOut.系统操作_行权费, TradeCashExerciseWayEnum.到期行权, valueDate, false, parentTradeId, parentTradeCashId);
}
else if (tradeSnowball.KnockInOutStatus != ConsTrade.KnockState.KnockedOut)
{
var startDate = tradeSnowball.CouponIncludeStartDate == true ? snowball.StartDate.AddDays(-1) : snowball.StartDate;
otcTrade.TradeStatus = ConsTrade.已到期;
otcTrade.UnWindDate = valueDate;
var maturityCouponRate = snowball.Coupon * snowball.InitialSpotPrice;
var maturityCouponPayment =
snowball.FixedCoupon ?
maturityCouponRate * snowball.Notional :
maturityCouponRate * snowball.Notional * snowball.CouponDayCount.CalcDayCountFraction(startDate, snowball.ExerciseDates.Last());
maturityCouponPayment = TradeHelper.GetAmountByPaymentAmount(maturityCouponPayment, otcTrade.PrincipalSum(), otcTrade.BuySell);
double tradePrice = 0;
if (tradeSnowball.AnnualizedPremiumRate.HasValue && tradeSnowball.AnnualizedPremiumRate != 0)
{
tradePrice = otcTrade.StockEqvNotional * otcTrade.ParticipationRate * tradeSnowball.AnnualizedPremiumRate * snowball.CouponDayCount.CalcDayCountFraction(snowball.StartDate, new Date(valueDate)) ?? 0;
}
var parentTradeId = 0;
var parentTradeCashId = 0;
var groupTradeCash = new trade_cash();
if (otcTrade.IsGroup == 2 && otcTrade.ParentTradeId > 0)
{
var groupAction = DbContext.trade_cash_group_action.FirstOrDefault(x => x.TradeId == otcTrade.id && x.Status != "已完成");
if (groupAction != null)
{
groupAction.Status = "已完成";
parentTradeCashId = groupAction.ParentTradeCashId;
parentTradeId = groupAction.ParentTradeId;
}
else
{
var paymentAmount = SettlementAmount != null ? SettlementAmount.Value : (maturityCouponPayment + ((otcTrade.BuySell == "买入" ? -1 : 1) * tradePrice));
parentTradeId = otcTrade.ParentTradeId;
parentTradeCashId = SaveGroupUnwindCash(otcTrade, valueDate, paymentAmount, closePrice, out var continueTradeCashHandle).id;
}
}
var tradeCash = new trade_cash();
if (SettlementAmount != null)
{
//观察日价格页面的结算金额包含了年化期权费,该处逻辑需要先按照扣除年化期权费来算,后面逻辑会补上年化期权费,否则会重复运算
var _settlementAmount = SettlementAmount.Value;
if (tradePrice != 0)
{
_settlementAmount -= (otcTrade.BuySell == "买入" ? -1 : 1) * tradePrice;
}
tradeCash = SaveOptionPayoffCash(otcTrade, _settlementAmount, valueDate, closePrice, ClientCashInCashOut.系统操作_行权费, TradeCashExerciseWayEnum.到期行权, valueDate, false, parentTradeId, parentTradeCashId);
}
else
{
tradeCash = SaveOptionPayoffCash(otcTrade, maturityCouponPayment, valueDate, closePrice, ClientCashInCashOut.系统操作_行权费, TradeCashExerciseWayEnum.到期行权, valueDate, false, parentTradeId, parentTradeCashId);
}
if (tradePrice != 0 && otcTrade.PremiumPayDate <= valueDate)
{
SaveTradePrice(otcTrade, tradeCash, tradePrice, valueDate);
}
}
}
#endregion
}
private trade_cash SaveOptionPayoffCash(OtcTradeBase trade, double paymentAmount, DateTime valueDate, double underlyingPrice, string cashAction, string exerciseWay, DateTime happenDate, bool isKnockOut, int parentTradeId, int parentTradeCashId)
{
return SaveCash(trade, cashAction, exerciseWay, paymentAmount, valueDate, underlyingPrice, happenDate, isKnockOut, isLastAction: true, parentTradeId: parentTradeId, parentTradeCashId: parentTradeCashId);
}
private trade_cash SaveCouponAmountCash(OtcTradeBase trade, double couponAmount, DateTime valueDate, double underlyingPrice, DateTime happenDate, int parentTradeId, int parentTradeCashId)
{
return SaveCash(trade, ClientCashInCashOut.系统操作_票息, null, couponAmount, valueDate, underlyingPrice, happenDate, true, isLastAction: true, parentTradeId: parentTradeId, parentTradeCashId: parentTradeCashId);
}
public void SaveTradePrice(OtcTradeBase trade, trade_cash tradeCash, double tradePrice, DateTime valueDate)
{
const string cashAction = ClientCashInCashOut.系统操作_期权费;
// 保存trade_cash
var tcd = new trade_cash_detail();
DbContext.trade_cash_detail.Add(tcd);
tcd.TradeId = tradeCash.TradeId;
tcd.TradeCashId = tradeCash.id;
tcd.Amount = (trade.BuySell == "买入" ? -1 : 1) * tradePrice;
tcd.Action = cashAction;
tcd.ValueDate = tradeCash.ValueDate;
tcd.OptDate = DateTime.Now;
tcd.OptId = tradeCash.OptId;
tcd.OptName = tradeCash.OptName;
var tradeCashUpDate = DbContext.trade_cash.Find(tradeCash.id);
tradeCashUpDate.Amount += tcd.Amount ?? 0;
// 保存ClientCashInCashOut
var client = DbContextFactory.GetClientDbContext(UserInfo).client.Where(n => n.id == trade.ClientId)
.Select(n => new { n.id, n.Number, n.Name }).FirstOrDefault();
if (client == null)
{
throw new Exception($"{trade.TradeType}'{trade.TradeNumber}'找不到客户信息,客户id:{trade.ClientId}");
}
var cashInOutRecord = DbContext.ClientCashInCashOut.FirstOrDefault(c => c.TradeId == trade.id && c.Action == cashAction && c.HappenDate == valueDate);
if (cashInOutRecord == null)
{
cashInOutRecord = new ClientCashInCashOut();
cashInOutRecord.CreateDate = tradeCash.OptDate;
cashInOutRecord.CreatorId = tradeCash.OptId;
cashInOutRecord.CreatorName = tradeCash.OptName;
DbContext.ClientCashInCashOut.Add(cashInOutRecord);
}
cashInOutRecord.Direction = "应收";
cashInOutRecord.Number = UniqueTimeId.GetStr();
cashInOutRecord.ClientId = client.id;
cashInOutRecord.ClientNumber = client.Number;
cashInOutRecord.ClientName = client.Name;
cashInOutRecord.Money = -tcd.Amount;
cashInOutRecord.HappenDate = valueDate;
cashInOutRecord.ValidState = "Valid";
cashInOutRecord.State = ClientCashInCashOut.已确认;
cashInOutRecord.OptDate = DateTime.Now;
cashInOutRecord.OptId = tradeCash.OptId;
cashInOutRecord.OptName = tradeCash.OptName;
cashInOutRecord.TradeId = trade.id;
cashInOutRecord.TradeCashId = tradeCash.id;
cashInOutRecord.Action = cashAction;
cashInOutRecord.TradeNumber = trade.TradeNumber;
cashInOutRecord.IsGroup = trade.IsGroup;
DbContext.SaveChanges();
}
/// <summary>
/// 处理专业版雪球
/// </summary>
private void ProcessSpecialSnowball(OtcTradeBase otcTrade, trade_snowball snowball
, DateTime valueDate, double closePrice, double tradeNotional, double? settlementAmount)
{
var obResult = new SpecialSnowballObservationHelper(otcTrade, snowball)
.GetObservationResultForTraderSide(valueDate, closePrice, tradeNotional);
if (obResult.ResultType == SnowballObservationResultType.NonObservationDay
|| obResult.ResultType == SnowballObservationResultType.Monitoring)
{
return;
}
//期权敲入
if (obResult.ResultType == SnowballObservationResultType.KnockedIn)
{
// 更新期权敲入状态
if (snowball.KnockInOutDate == null)
{
snowball.KnockInOutDate = snowball.IsInitialKnockedIn ? otcTrade.StartDate.Value : valueDate;
}
snowball.KnockInOutStatus = ConsTrade.KnockState.KnockedIn;
return;
}
var paymentAmount = settlementAmount != null
? settlementAmount.Value
: TradeHelper.GetAmountByPaymentAmount(obResult.PaymentAmount, otcTrade.PrincipalSum(), otcTrade.BuySell);
if (obResult.ResultType == SnowballObservationResultType.KoPayoff)
{
// 更新期权敲出状态
snowball.KnockInOutDate = valueDate;
snowball.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
// 更新交易了结状态
otcTrade.UnWindDate = valueDate;
otcTrade.TradeStatus = ConsTrade.已平仓;
//写入资金记录
(var parentTradeId, var parentTradeCashId) = SaveGroupCash(otcTrade, obResult.PaymentDate, paymentAmount, closePrice);
SaveCouponAmountCash(otcTrade, paymentAmount, obResult.PaymentDate, closePrice, valueDate, parentTradeId, parentTradeCashId);
LogFactory.GetLogger("收盘检查雪球").Info($"{otcTrade.TradeNumber}--敲出");
}
else if (obResult.ResultType == SnowballObservationResultType.KiPayoffAtEndDate)
{
// 更新期权敲入状态
if (snowball.KnockInOutDate == null)
{
snowball.KnockInOutDate = snowball.IsInitialKnockedIn ? otcTrade.StartDate.Value : valueDate;
}
snowball.KnockInOutStatus = ConsTrade.KnockState.KnockedIn;
// 更新交易了结状态
otcTrade.UnWindDate = valueDate;
otcTrade.TradeStatus = ConsTrade.已执行;
(var parentTradeId, var parentTradeCashId) = SaveGroupCash(otcTrade, obResult.PaymentDate, paymentAmount, closePrice);
SaveOptionPayoffCash(otcTrade, paymentAmount, obResult.PaymentDate, closePrice, ClientCashInCashOut.系统操作_行权费, TradeCashExerciseWayEnum.到期行权, valueDate, false, parentTradeId, parentTradeCashId);
}
else if (obResult.ResultType == SnowballObservationResultType.NkiPayoffAtEndDate)
{
otcTrade.UnWindDate = valueDate;
otcTrade.TradeStatus = ConsTrade.已到期;
(var parentTradeId, var parentTradeCashId) = SaveGroupCash(otcTrade, obResult.PaymentDate, paymentAmount, closePrice);
SaveOptionPayoffCash(otcTrade, paymentAmount, obResult.PaymentDate, closePrice, ClientCashInCashOut.系统操作_行权费, TradeCashExerciseWayEnum.到期行权, valueDate, false, parentTradeId, parentTradeCashId);
}
else
{
throw new ServiceException("系统错误,未处理的观察结果类型:" + obResult.ResultType) { IsFaultError = true };
}
}
//保存组合交易资金
private (int parentTradeId, int parentTradeCashId) SaveGroupCash(OtcTradeBase otcTrade
, DateTime paymentDate, double paymentAmount, double closePrice)
{
var parentTradeId = 0;
var parentTradeCashId = 0;
if (otcTrade.IsGroup == 2 && otcTrade.ParentTradeId > 0)
{
var groupAction = DbContext.trade_cash_group_action.FirstOrDefault(x => x.TradeId == otcTrade.id && x.Status != "已完成");
if (groupAction != null)
{
groupAction.Status = "已完成";
parentTradeCashId = groupAction.ParentTradeCashId;
parentTradeId = groupAction.ParentTradeId;
}
else
{
parentTradeId = otcTrade.ParentTradeId;
parentTradeCashId = SaveGroupUnwindCash(otcTrade, paymentDate, paymentAmount, closePrice, out var continueTradeCashHandle).id;
}
}
return (parentTradeId, parentTradeCashId);
}
}
}