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

754 lines
39 KiB
C#

using Qdp.Pricing.Library.Options.Products.Autocall.Phoenix;
using YLErp.Abstract.DataProviders;
using YLErp.BLL;
using YLErp.Commons;
using YLErp.DBModels.Consts;
using YLErp.DBModels.Enums;
using YLErp.DBModels.Helpers;
using YLErp.Modules.CalculationModule;
using YLErp.Modules.DataProviderModule;
namespace YLErp.Modules.TradeModule
{
public class TradeAutocallBLL : ExoticOptionModule.TradeCashServiceEx
{
public TradeAutocallBLL(OptUserInfo userInfo) : base(userInfo)
{
}
public TradeAutocallBLL(YLBaseService baseService) : base(baseService)
{
}
public void CheckStatus(DateTime valueDate, IEodPriceProviderV2 priceProvider, DateTime? startDate = null
, Action<OtcTrade, trade_autocall> afterKnowInOut = null, IEnumerable<int> clienIds = null)
{
if (priceProvider is null)
{
priceProvider = new EodPriceProvider(valueDate);
}
if (startDate == null)
{
startDate = valueDate.AddYears(-5);
}
//未敲出的,以及已敲出但敲出日期大于等于当前收盘日的(为了历史收盘)
var query = from trade in DbContext.trade
join autocall in DbContext.trade_autocall on trade.id equals autocall.TradeId
join underlying in DbContext.underlying_manager on trade.UnderlyingId equals underlying.id
where trade.TradeDate > startDate.Value && trade.TradeDate <= valueDate && trade.ExerciseDate >= valueDate
&& (trade.TradeType == "凤凰期权")
&& (ConsTrade.确认成交 == trade.TradeStatus)
&& trade.ValidState != ConsGlobal.InValid
&& (autocall.KnockInOutStatus != ConsTrade.KnockState.KnockedOut || (autocall.KnockInOutStatus == ConsTrade.KnockState.KnockedOut && autocall.KnockInOutDate >= valueDate))
&& trade.DividendDate < valueDate
select new
{
underlying = underlying,
trade = trade,
trade_autocall = autocall
};
#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);
foreach (var tr in trades)
{
if (tr.trade.ExerciseDate < valueDate)
{
continue;//已到期交易不再观察;
}
var tradeStatus = tr.trade.TradeStatus;
var knockInOutStatus = tr.trade_autocall.KnockInOutStatus;
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;
}
CheckAutocallKnockInOutStatus(tr.trade, tr.trade_autocall, valueDate, closePrice, SettlementAmount);
if (tradeStatus != tr.trade.TradeStatus || knockInOutStatus != tr.trade_autocall.KnockInOutStatus)
{
//删除E/Bod_Trade记录
RemoveEodTradeAndFutureInfo(false, tr.trade.id, valueDate);
}
if (afterKnowInOut != null && DbContext.Entry(tr.trade_autocall).State == EntityState.Modified)
{
afterKnowInOut(tr.trade, tr.trade_autocall);
}
}
DbContext.SaveChanges();
}
public double GetDefaultAmount(OtcTradeBase otcTrade, trade_autocall tradeAutoCall, DateTime valueDate, double closePrice)
{
var defaultAmount = 0d;
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 notional = (ConsTrade.TradeCompleteStatus.Contains(otcTrade.TradeStatus) && otcTrade.UnWindDate <= valueDate ? 0 : otcTrade.Notional) + tradeCashs.Sum(x => x.UnwindNotional).Value;
var optionTrade = QdpTradeBuilder.GetAutocallOptionTrade(otcTrade, tradeAutoCall,
new OptionTradeParamRequest(valuedateBLL.SysRiskFreeRate()) { ParamOverride = x => { x.notional = notional; } });
var autocall = (AutoCall)optionTrade.Instrument;
var isCall = ConsGlobal.CallPut.IsCall(otcTrade.CallPut);
//只在敲出观察日检查敲出和票息情况
//如果交易已经是敲出状态了,不用再做票息和敲出检查
if (autocall.KOObsDates.Select(x => x.DateTime).Contains(valueDate)
&& tradeAutoCall.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 = tradeAutoCall.KOBarrier;
}
if (otcTrade.IsMoneynessOptionData)
{
koBarrier *= otcTrade.SpotPrice ?? 1.0;
}
#region 票息检查
var couponBarrier =
otcTrade.IsMoneynessOptionData ?
tradeAutoCall.CouponBarrier * otcTrade.SpotPrice :
tradeAutoCall.CouponBarrier;
//看涨 - 向上敲出,看跌 - 向下敲出
var isKnockedOut = isCall ? closePrice >= koBarrier : closePrice <= koBarrier;
//有票息
if (isCall ? closePrice >= couponBarrier : closePrice <= couponBarrier)
{
//利息计算时,当autocall的Notional包含了符号,则CouponPayment考虑了买卖方向了
defaultAmount = autocall.CouponPayment(valueDate, includeTradeStartDate: tradeAutoCall.CouponIncludeStartDate == true && tradeAutoCall.CouponDayCount != "Monthly");
if (isKnockedOut)
{
tradeAutoCall.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
defaultAmount = TradeHelper.GetAmountByPaymentAmount(defaultAmount, otcTrade.PrincipalSum(), otcTrade.BuySell);
}
}
#endregion
}
#region 敲入检查
//在当前结算日之前未敲出且未敲入:
if (!((tradeAutoCall.KnockInOutStatus == ConsTrade.KnockState.KnockedOut
|| tradeAutoCall.KnockInOutStatus == ConsTrade.KnockState.KnockedIn)
&& tradeAutoCall.KnockInOutDate < valueDate)
&& autocall.KIObsDates.Select(x => x.DateTime).Contains(valueDate))
{
var kiBarrier =
otcTrade.IsMoneynessOptionData ?
tradeAutoCall.KIBarrier * otcTrade.SpotPrice :
tradeAutoCall.KIBarrier;
//看涨 - 向下敲入,看跌 - 向上敲入
var knockedin = isCall ? closePrice <= kiBarrier : closePrice >= kiBarrier;
// 发生敲入事件
if (knockedin)
{
// 更新观察状态
tradeAutoCall.KnockInOutStatus = ConsTrade.KnockState.KnockedIn;
}
}
#endregion
#region 到期检查
if (valueDate == autocall.ExerciseDates.Last().DateTime)
{
if (tradeAutoCall.KnockInOutStatus == ConsTrade.KnockState.KnockedIn)
{
//已敲入,到期时计算期权收益
var optionPayoffPayment = autocall.GetPayoff(new double[] { closePrice });
//敲入是否支付票息
if (tradeAutoCall.IncludeCouponAfterKI)
{
//optionPayoffPayment[0].PaymentAmount包含了买卖方向的处理了
defaultAmount += TradeHelper.GetAmountByPaymentAmount(optionPayoffPayment[0].PaymentAmount, otcTrade.PrincipalSum(), otcTrade.BuySell);
}
else
{
//optionPayoffPayment[0].PaymentAmount包含了买卖方向的处理了
defaultAmount = TradeHelper.GetAmountByPaymentAmount(optionPayoffPayment[0].PaymentAmount, otcTrade.PrincipalSum(), otcTrade.BuySell);
}
}
else if(tradeAutoCall.KnockInOutStatus != ConsTrade.KnockState.KnockedOut)
{
defaultAmount = TradeHelper.GetAmountByPaymentAmount(defaultAmount, otcTrade.PrincipalSum(), otcTrade.BuySell);
}
}
#endregion
return defaultAmount;
}
public void CheckAutocallKnockInOutStatus(OtcTradeBase otcTrade, trade_autocall tradeAutoCall, DateTime valueDate, double closePrice, double? SettlementAmount)
{
try
{
var oldKnockInOutStatus = tradeAutoCall.KnockInOutStatus;
InnerCheckAutocallKnockInOutStatus(otcTrade, tradeAutoCall, valueDate, closePrice, SettlementAmount);
if (oldKnockInOutStatus != tradeAutoCall.KnockInOutStatus)
{
var KnockInOutStatus = tradeAutoCall.KnockInOutStatus == ConsTrade.KnockState.KnockedIn ? "敲入" : "敲出";
AddTradeOperationHistoryAndSetParentTradeInfo(false, otcTrade, KnockInOutStatus, KnockInOutStatus);
if (KnockInOutStatus == "敲出" && tradeAutoCall.CouponPayType == CouponPayTypeEnum.AtKnockout)
{
var observations = DbContext.autocall_observation.Where(n => n.TradeId == otcTrade.id).ToArray();
foreach (var item in observations)
{
item.PaymentDate = valueDate;
}
DbContext.SaveChanges();
}
}
}
catch (Exception ex)
{
throw new ServiceException($"[检查敲入敲出]交易编号:{otcTrade.TradeNumber},{ex.Message}", ex);
}
}
private void InnerCheckAutocallKnockInOutStatus(OtcTradeBase otcTrade, trade_autocall tradeAutoCall, DateTime valueDate, double closePrice, double? SettlementAmount)
{
var hasUseSettlementAmount = false;
double notional = 0;
var eodTrade = DbContext.eod_trade.FirstOrDefault(x => x.TradeId == otcTrade.id && x.ValueDate == valueDate);
if (eodTrade != null)
{
notional = eodTrade.trade.Notional;
}
else
{
var bodTrade = DbContext.bod_Trade.FirstOrDefault(x => x.TradeId == otcTrade.id && x.ValueDate == valueDate);
notional = bodTrade != null ? bodTrade.trade.Notional : otcTrade.Notional;
var tradeCashs = DbContext.trade_cash.Where(x => x.ValidState != ConsGlobal.InValid && !x.IsDeleted && x.TradeId == otcTrade.id && x.Action != "系统操作-行权费" && x.Action != "系统操作-票息" && x.ValueDate == valueDate).ToList();
notional -= tradeCashs.Sum(x => x.UnwindNotional ?? 0);
}
//敲出到期支付,或者敲入到期支付票息时,交易可能已经敲出了或者到起执行了,这个时候到期日收盘,需要补上到期票息,这里处理该票息对应的持仓份额
if (tradeAutoCall.CouponPayType == CouponPayTypeEnum.AtMaturity && valueDate == otcTrade.ExerciseDate && ConsTrade.TradeCompleteStatus.Contains(otcTrade.TradeStatus))
{
var tradeCash = DbContext.trade_cash.Where(x => x.ValidState != ConsGlobal.InValid && !x.IsDeleted && x.TradeId == otcTrade.id && x.IsLastAction).FirstOrDefault();
if (tradeCash != null)
{
notional = tradeCash.Notional;
}
}
//敲入转期权和到期支付票息同时存在时,若设置观察价格页面设置了结算金额,作为票息处理,敲入了结金额维持系统计算逻辑不变
var optionTrade = QdpTradeBuilder.GetAutocallOptionTrade(otcTrade, tradeAutoCall,
new OptionTradeParamRequest(valuedateBLL.SysRiskFreeRate()) { ParamOverride = x => { x.notional = notional; } });
var autocall = (AutoCall)optionTrade.Instrument;
var kiBarrier = otcTrade.IsMoneynessOptionData ? tradeAutoCall.KIBarrier * otcTrade.SpotPrice : tradeAutoCall.KIBarrier;
var isCall = ConsGlobal.CallPut.IsCall(otcTrade.CallPut);
//只在敲出观察日检查敲出和票息情况
//如果交易已经是敲出状态了,不用再做票息和敲出检查
if (autocall.KOObsDates.Select(x => x.DateTime).Contains(valueDate)
&& tradeAutoCall.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 = tradeAutoCall.KOBarrier;
}
if (otcTrade.IsMoneynessOptionData)
{
koBarrier *= otcTrade.SpotPrice ?? 1.0;
}
#region 票息检查
var couponBarrier =
otcTrade.IsMoneynessOptionData ?
tradeAutoCall.CouponBarrier * otcTrade.SpotPrice :
tradeAutoCall.CouponBarrier;
//看涨 - 向上敲出,看跌 - 向下敲出
var isKnockedOut = isCall ? closePrice >= koBarrier : closePrice <= koBarrier;
//有票息
if (isCall ? closePrice >= couponBarrier : closePrice <= couponBarrier)
{
//利息计算时,当autocall的Notional包含了符号,则GetEffectiveObservation考虑了买卖方向了
var observation = autocall.GetEffectiveObservation(valueDate, includeTradeStartDate: tradeAutoCall.CouponIncludeStartDate == true && tradeAutoCall.CouponDayCount != "Monthly");
if (observation != null)
{
//otcTrade.trade_autocall = tradeAutocall;
var _settlementAmount = SettlementAmount;
//观察日页面设置的结算金额
if (SettlementAmount != null)
{
hasUseSettlementAmount = true;
//到期敲入且未敲出情况
if (valueDate == autocall.ExerciseDates.Last().DateTime && !isKnockedOut)
{
//当前满足敲入或者已经敲入了
if (autocall.KIObsDates.Select(x => x.DateTime).Contains(valueDate) && closePrice <= kiBarrier || tradeAutoCall.KnockInOutStatus == ConsTrade.KnockState.KnockedIn)
{
tradeAutoCall.KnockInOutStatus = ConsTrade.KnockState.KnockedIn;
var optionPayoffPayment = autocall.GetPayoff(new double[] { closePrice });
var paymentAmount = TradeHelper.GetAmountByPaymentAmount(optionPayoffPayment[0].PaymentAmount, otcTrade.PrincipalSum(), otcTrade.BuySell);
//在记录票息时将敲入部分的payoff先减掉,在后面到期处理时会再添加一笔敲入的资金记录
_settlementAmount -= paymentAmount;
}
}
}
SaveCouponObservation(otcTrade, tradeAutoCall, observation, valueDate, closePrice, isKnockedOut, _settlementAmount);
}
}
#endregion
#region 敲出检查
// 发生敲出事件
if (isKnockedOut)
{
// 更新观察状态
tradeAutoCall.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
tradeAutoCall.KnockInOutDate = valueDate;
// 更新交易状态
otcTrade.TradeStatus = ConsTrade.已平仓;
otcTrade.UnWindDate = valueDate;
var parentTradeId = 0;
var parentTradeCashId = 0;
//敲出时支付的票息在敲出日写入资金记录
if (tradeAutoCall.CouponPayType != CouponPayTypeEnum.AtCreated)
{
var hasUnfinishedGroupAction = false;
var continueTradeCashHandle = false;
var tradeCash = 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;
tradeCash = SaveGroupUnwindCash(otcTrade, valueDate, 0, closePrice, out continueTradeCashHandle);
parentTradeCashId = tradeCash.id;
}
}
var amount = SaveCouponCashOnEnd(otcTrade, tradeAutoCall.CouponPayType == CouponPayTypeEnum.AtMaturity ? otcTrade.ExerciseDate.Value : valueDate, parentTradeId, parentTradeCashId, closePrice);
if (otcTrade.IsGroup == 2 && otcTrade.ParentTradeId > 0 && !hasUnfinishedGroupAction && continueTradeCashHandle)
{
tradeCash.Amount += amount;
DbContext.SaveChanges();
}
}
SaveCash(otcTrade, ClientCashInCashOut.系统操作_票息, null, 0, tradeAutoCall.CouponPayType == CouponPayTypeEnum.AtMaturity ? otcTrade.ExerciseDate.Value : valueDate, closePrice, valueDate, true, isLastAction: true, parentTradeId: parentTradeId, parentTradeCashId: parentTradeCashId);
}
#endregion
}
#region 敲入检查
//在当前结算日之前未敲出且未敲入:
if (!((tradeAutoCall.KnockInOutStatus == ConsTrade.KnockState.KnockedOut
|| tradeAutoCall.KnockInOutStatus == ConsTrade.KnockState.KnockedIn)
&& tradeAutoCall.KnockInOutDate < valueDate)
&& autocall.KIObsDates.Select(x => x.DateTime).Contains(valueDate))
{
//看涨 - 向下敲入,看跌 - 向上敲入
var knockedin = isCall ? closePrice <= kiBarrier : closePrice >= kiBarrier;
// 发生敲入事件
if (knockedin)
{
// 更新观察状态
tradeAutoCall.KnockInOutStatus = ConsTrade.KnockState.KnockedIn;
tradeAutoCall.KnockInOutDate = valueDate;
}
}
#endregion
#region 到期检查
if (valueDate == autocall.ExerciseDates.Last().DateTime)
{
if (tradeAutoCall.KnockInOutStatus == ConsTrade.KnockState.KnockedIn)
{
otcTrade.TradeStatus = ConsTrade.已执行;
otcTrade.UnWindDate = valueDate;
if (!autocall.IncludeCouponAfterKI)
{
// 敲入不支付票息,则要将之前累积的票息删除掉
RemoveAccumulatedCoupon(otcTrade.id);
hasUseSettlementAmount = false;
}
double paymentAmount = 0;
if (SettlementAmount != null && !hasUseSettlementAmount)
{
paymentAmount = SettlementAmount ?? 0;
}
else
{
//已敲入,到期时计算期权收益
var optionPayoffPayment = autocall.GetPayoff(new double[] { closePrice });
//optionPayoffPayment[0].PaymentAmount包含了买卖方向的处理了
paymentAmount = TradeHelper.GetAmountByPaymentAmount(optionPayoffPayment[0].PaymentAmount, otcTrade.PrincipalSum(), otcTrade.BuySell);
}
var parentTradeId = 0;
var parentTradeCashId = 0;
var hasUnfinishedGroupAction = false;
var continueTradeCashHandle = false;
var tradeCash = 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;
tradeCash = SaveGroupUnwindCash(otcTrade, valueDate, paymentAmount, closePrice, out continueTradeCashHandle);
parentTradeCashId = tradeCash.id;
}
}
SaveOptionPayoffCash(otcTrade, paymentAmount, valueDate, closePrice, valueDate, parentTradeId, parentTradeCashId);
//期末支付的票息在到期日写入资金记录
if (tradeAutoCall.CouponPayType == CouponPayTypeEnum.AtMaturity && tradeAutoCall.IncludeCouponAfterKI)
{
var amount = SaveCouponCashOnEnd(otcTrade, valueDate, parentTradeId, parentTradeCashId, closePrice);
if (otcTrade.IsGroup == 2 && otcTrade.ParentTradeId > 0 && !hasUnfinishedGroupAction && continueTradeCashHandle)
{
tradeCash.Amount += amount;
DbContext.SaveChanges();
}
}
}
else
{
if (tradeAutoCall.KnockInOutStatus != ConsTrade.KnockState.KnockedOut)
{
// 更新交易状态
otcTrade.TradeStatus = ConsTrade.已到期;
otcTrade.UnWindDate = valueDate;
}
var parentTradeId = 0;
var parentTradeCashId = 0;
var hasUnfinishedGroupAction = false;
var continueTradeCashHandle = false;
var tradeCash = 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;
tradeCash = SaveGroupUnwindCash(otcTrade, valueDate, 0, closePrice, out continueTradeCashHandle);
parentTradeCashId = tradeCash.id;
}
}
//期末支付的票息在到期日写入资金记录
if (tradeAutoCall.CouponPayType != CouponPayTypeEnum.AtCreated && tradeAutoCall.KnockInOutStatus != ConsTrade.KnockState.KnockedOut)
{
var amount = SaveCouponCashOnEnd(otcTrade, valueDate, parentTradeId, parentTradeCashId, closePrice);
if (otcTrade.IsGroup == 2 && otcTrade.ParentTradeId > 0 && !hasUnfinishedGroupAction && continueTradeCashHandle)
{
tradeCash.Amount += amount;
DbContext.SaveChanges();
}
}
if (tradeAutoCall.KnockInOutStatus != ConsTrade.KnockState.KnockedOut)
{
SaveCash(otcTrade, ClientCashInCashOut.系统操作_票息, "到期行权", 0, valueDate, closePrice, valueDate, false, isLastAction: true, parentTradeId: parentTradeId, parentTradeCashId: parentTradeCashId);
}
}
}
#endregion
}
/// <summary>
/// 当票息在到期支付时,从autocall_observation表记录的票息累积数据生成对应的资金记录
/// </summary>
private double SaveCouponCashOnEnd(OtcTradeBase trade, DateTime valueDate, int parentTradeId, int parentTradeCashId, double closePrice)
{
double amount = 0;
var tradeCashes = DbContext.trade_cash.Where(t => t.TradeId == trade.id && !t.IsDeleted
&& t.Action == ClientCashInCashOut.系统操作_票息).ToList();
if (tradeCashes != null)
{
DbContext.trade_cash.RemoveRange(tradeCashes);
var tradeCashIds = tradeCashes.Select(x => x.id);
var tradeCashDetials = DbContext.trade_cash_detail.Where(x => tradeCashIds.Contains(x.TradeCashId));
DbContext.trade_cash_detail.RemoveRange(tradeCashDetials);
}
var clientCashes = DbContext.ClientCashInCashOut.Where(c => c.TradeId == trade.id
&& c.Action == ClientCashInCashOut.系统操作_票息).ToList();
if (clientCashes != null)
{
DbContext.ClientCashInCashOut.RemoveRange(clientCashes);
}
var observations = DbContext.autocall_observation.Where(o => o.TradeId == trade.id).OrderBy(x => x.EndDate).ToList();
var maxEndDate = observations.Max(x => (DateTime?)x.EndDate) ?? DateTime.MinValue;
observations.ForEach(o =>
{
amount += o.PaymentAmount;
// 保存trade_cash
var tc = new trade_cash
{
OptId = UserId,
OptName = UserName,
OptDate = DateTime.Now,
ExceciseType = "现金",
TradeType = trade.BuySell,
CallPut = trade.CallPut,
Notional = trade.Notional,
TradeAmount = trade.TradeAmount,
UnwindNotional = maxEndDate == o.EndDate ? trade.Notional : 0,
UnwindTradeAmount = maxEndDate == o.EndDate ? trade.TradeAmount : 0,
UnwindPercentRate = maxEndDate == o.EndDate ? (trade.OriginalNotional != 0 ? trade.Notional / trade.OriginalNotional : 0) : 0,
Amount = o.PaymentAmount,
UnwindPrice = Math.Abs(trade.Notional != 0 ? o.PaymentAmount / trade.Notional : 0),
UnwindPricePercentRate = Math.Abs(trade.Notional != 0 && trade.SpotPrice != null && trade.SpotPrice != 0 ? o.PaymentAmount / trade.Notional / trade.SpotPrice.Value : 0),
FinalPrice = closePrice,
TradeId = trade.id,
HappenedDate = o.EndDate,
Action = ClientCashInCashOut.系统操作_票息,
ExerciseWay = "到期行权",
Status = TradeCashStatusEnum.已执行,
ValueDate = valueDate,
ParentTradeId = parentTradeId,
ParentTradeCashId = parentTradeCashId
};
DbContext.trade_cash.Add(tc);
DbContext.SaveChanges();
SaveTradeCashDetail(tc);
// 保存ClientCashInCashOut
var client = DataCacheProvider.GetClientDataSource().GetData(trade.ClientId);
if (client != null)
{
var cashInOutRecord = new ClientCashInCashOut
{
Direction = "应收",
Number = UniqueTimeId.GetStr(),
ClientId = client.id,
ClientName = client.Name,
ClientNumber = client.Number,
Money = -tc.Amount,
HappenDate = valueDate,
State = ClientCashInCashOut.已确认,
OptDate = tc.OptDate,
OptId = tc.OptId,
CreatorName = tc.OptName,
CreateDate = tc.OptDate,
CreatorId = tc.OptId,
OptName = tc.OptName,
TradeId = trade.id,
TradeCashId = tc.id,
Action = ClientCashInCashOut.系统操作_票息,
TradeNumber = trade.TradeNumber,
IsGroup = trade.IsGroup
};
DbContext.ClientCashInCashOut.Add(cashInOutRecord);
}
});
return amount;
}
/// <summary>
/// 适用于票息到期支付,但敲入后不支付票息的情况下,将已经累计的票息信息删除
/// </summary>
private void RemoveAccumulatedCoupon(int tradeId)
{
var observations = DbContext.autocall_observation.Where(o => o.TradeId == tradeId).ToList();
if (observations.Count > 0)
{
DbContext.autocall_observation.RemoveRange(observations);
}
}
private trade_cash SaveOptionPayoffCash(OtcTradeBase trade, double amount, DateTime valueDate, double underlyingPrice, DateTime happenDate, int parentTradeId, int parentTradeCashId)
{
return SaveCash(trade, ClientCashInCashOut.系统操作_行权费, TradeCashExerciseWayEnum.到期行权, amount, valueDate, underlyingPrice, happenDate, false, isLastAction: true, parentTradeId: parentTradeId, parentTradeCashId: parentTradeCashId);
}
public autocall_observation SaveCouponObservation(OtcTradeBase trade, trade_autocall tradeAutoCall
, ObservationPayment observation, DateTime happenDate, double underlyingPrice, bool isKnockedOut, double? SettlementAmount, bool saveChanges = true)
{
//保存autocall_observation
var observationRecord = DbContext.autocall_observation.FirstOrDefault(o => o.TradeId == trade.id && o.EndDate == happenDate);
if (observationRecord == null)
{
observationRecord = new autocall_observation()
{
TradeId = trade.id,
StartDate = observation.StartDate.DateTime.Date,
EndDate = observation.EndDate.DateTime.Date,
CouponRate = observation.CouponRate,
StockEqvNotional = observation.Notional,
PaymentAmount = SettlementAmount == null ? observation.PaymentAmount : SettlementAmount.Value,
PaymentDate = observation.PaymentDate.DateTime.Date
};
DbContext.autocall_observation.Add(observationRecord);
}
else
{
observationRecord.StartDate = observation.StartDate.DateTime.Date;
observationRecord.EndDate = observation.EndDate.DateTime.Date;
observationRecord.CouponRate = observation.CouponRate;
observationRecord.StockEqvNotional = observation.Notional;
if (SettlementAmount != null)
{
observationRecord.PaymentAmount = SettlementAmount.Value;
}
else
{
// 系统操作_票息 修改 功能暂时禁掉
//bool isupdate = true;
//var trade_cash = DbContext.trade_cash.Where(x => x.id == observationRecord.CashId && x.ValidState != "InValid" && x.Action == ClientCashInCashOut.系统操作_票息).FirstOrDefault();
//if (trade_cash != null)
//{
// var cashInOutRecord = DbContext.ClientCashInCashOut.FirstOrDefault(c => c.TradeCashId == trade_cash.id);
// if (cashInOutRecord != null)
// {
// if (DbContext.clientcashincashout_update.Where(x => x.ClientcashincashoutId == cashInOutRecord.id && x.ValidState != "InValid" && x.State == "已确认").Any())
// {
// isupdate = false;
// }
// }
//}
//if (isupdate)
//{
// observationRecord.PaymentAmount = observation.PaymentAmount;
//}
observationRecord.PaymentAmount = observation.PaymentAmount;
}
observationRecord.PaymentDate = observation.PaymentDate.DateTime.Date;
}
if (saveChanges)
{
// 票息当期付,立即产生资金记录
if (tradeAutoCall.CouponPayType == CouponPayTypeEnum.AtCreated)
{
var parentTradeId = 0;
var parentTradeCashId = 0;
if (trade.IsGroup == 2 && trade.ParentTradeId > 0)
{
//已经存在票息记录的说明该票息已经和组合主交易有关联了,不需要再做处理
if (DbContext.trade_cash.Any(t => t.TradeId == trade.id && t.Action == ClientCashInCashOut.系统操作_票息 && !t.IsDeleted && t.HappenedDate == happenDate))
{
return observationRecord;
}
var groupAction = DbContext.trade_cash_group_action.FirstOrDefault(x => x.TradeId == trade.id && x.Status != "已完成");
if (groupAction != null)
{
groupAction.Status = "已完成";
parentTradeCashId = groupAction.ParentTradeCashId;
parentTradeId = groupAction.ParentTradeId;
}
else
{
parentTradeId = trade.ParentTradeId;
var paymentAmount = SettlementAmount != null ? SettlementAmount.Value : observation.PaymentAmount;
parentTradeCashId = SaveGroupCouponCash(trade, happenDate, paymentAmount, underlyingPrice);
}
}
bool isLastAction = isKnockedOut || (happenDate == trade.ExerciseDate && tradeAutoCall.KnockInOutStatus != ConsTrade.KnockState.KnockedIn);
var tradeCash = new trade_cash();
if (SettlementAmount != null)
{
tradeCash = SaveCash(trade, ClientCashInCashOut.系统操作_票息, null, SettlementAmount ?? 0, happenDate, underlyingPrice, happenDate, isKnockedOut, isLastAction: isLastAction, parentTradeId: parentTradeId, parentTradeCashId: parentTradeCashId);
}
else
{
var paymentAmount = observation.PaymentAmount;
if (isKnockedOut)
{
paymentAmount = TradeHelper.GetAmountByPaymentAmount(paymentAmount, trade.PrincipalSum(), trade.BuySell);
}
tradeCash = SaveCash(trade, ClientCashInCashOut.系统操作_票息, happenDate == trade.ExerciseDate ? "到期行权" : null, paymentAmount, happenDate, underlyingPrice, happenDate, isKnockedOut, isLastAction: isLastAction, parentTradeId: parentTradeId, parentTradeCashId: parentTradeCashId);
}
observationRecord.CashId = tradeCash.id;
}
DbContext.SaveChanges();
}
return observationRecord;
}
public List<autocall_observation> QueryHappenedObservations(int tradeId, DateTime valueDate)
{
using (var db = new YLContext())
{
return db.autocall_observation.AsNoTracking().Where(o => o.TradeId == tradeId && o.EndDate <= valueDate).ToList();
}
}
}
}