从山证v2.3.0拷贝
This commit is contained in:
@@ -0,0 +1,753 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user