从山证v2.3.0拷贝

This commit is contained in:
吴方海
2024-05-09 14:06:26 +08:00
parent 566ff33259
commit f9d8a256a6
4471 changed files with 1203456 additions and 9 deletions
@@ -0,0 +1,411 @@
using Qdp.Foundation.Implementations;
using Qdp.Pricing.Base.Implementations;
using Qdp.Pricing.Base.Utilities;
using YLErp.Abstract.DataProviders;
using YLErp.BLL;
using YLErp.DBModels.Consts;
using YLErp.Model;
using YLErp.Modules.DataProviderModule;
using YLErp.QdpModule;
namespace YLErp.Modules.TradeModule.ExoticOptionModule
{
/// <summary>
/// 障碍期权敲入敲出操作
/// 迁移自:trade_barrier_optionBLL
/// </summary>
public class BarrierOptionKnockioService : TradeCashServiceEx
{
public BarrierOptionKnockioService(YLBaseService baseService) : base(baseService)
{
}
public BarrierOptionKnockioService(OptUserInfo userInfo) : base(userInfo)
{
}
/// <summary>
/// 设置障碍期权敲入敲出 ,返回error
/// </summary>
public void SetKnockInOut(DateTime valueDate, IEodPriceProviderV2 priceProvider, DateTime? startDate = null,
Action<OtcTrade, trade_barrier_option> afterKnowInOut = null, IEnumerable<int> clienIds = null)
{
if (priceProvider is null)
{
priceProvider = new EodPriceProvider(valueDate);
}
if (startDate == null)
{
startDate = valueDate.AddYears(-5);
}
var query = from td in DbContext.trade
join tb in DbContext.trade_barrier_option on td.id equals tb.TradeId
where td.TradeDate > startDate.Value && td.TradeDate <= valueDate
&& string.IsNullOrEmpty(tb.KnockInOutStatus)
&& td.TradeType == "障碍期权"
&& ConsTrade. == td.TradeStatus
&& td.ValidState != ConsGlobal.InValid
&& td.DividendDate < valueDate
select new
{
trade = td,
tradeBarrier = tb
};
#region tw
if (clienIds != null)
{
query = query.Where(l => clienIds.Contains(l.trade.ClientId));
}
#endregion
var trades = query.ToList();
if (trades == null || !trades.Any())
{
return;
}
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;//已到期交易不再观察;
}
double? closePrice, highPrice, lowPrice;
if (manuallyTradeObservationPrices.TryGetValue(tr.trade.id, out var manuallyTradeObservationPrice))
{
closePrice = manuallyTradeObservationPrice.Price;
highPrice = manuallyTradeObservationPrice.Price;
lowPrice = manuallyTradeObservationPrice.Price;
}
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;
highPrice = eodprice.HighPrice;
lowPrice = eodprice.LowPrice;
}
var tradeStatus = tr.trade.TradeStatus;
var oldKnockInOutStatus = tr.tradeBarrier.KnockInOutStatus;
CheckBarrierKnockInOutStatus(tr.trade, tr.tradeBarrier, valueDate, closePrice, highPrice, lowPrice);
if (oldKnockInOutStatus != tr.tradeBarrier.KnockInOutStatus)
{
var KnockInOutStatus = tr.tradeBarrier.KnockInOutStatus == ConsTrade.KnockState.KnockedIn ? "敲入" : "敲出";
AddTradeOperationHistoryAndSetParentTradeInfo(false, tr.trade, KnockInOutStatus, KnockInOutStatus);
}
//到期仍未敲入的情况
if (tr.tradeBarrier.BarrierType.Contains("敲入")
&& (string.IsNullOrWhiteSpace(tr.tradeBarrier.KnockInOutStatus) || ConsTrade.KnockState.IsMonitoring(tr.tradeBarrier.KnockInOutStatus))
&& tr.trade.ExerciseDate <= valueDate)
{
tr.trade.TradeStatus = ConsTrade.;
tr.trade.UnWindDate = valueDate;
SaveBarrierRebateCash(tr.trade, tr.tradeBarrier, valueDate, closePrice, knockOutBarrierPrice: null);
}
if (tradeStatus != tr.trade.TradeStatus || oldKnockInOutStatus != tr.tradeBarrier.KnockInOutStatus)
{
//删除E/Bod_Trade记录
RemoveEodTradeAndFutureInfo(false, tr.trade.id, valueDate);
}
if (afterKnowInOut != null && DbContext.Entry(tr.tradeBarrier).State == EntityState.Modified)
{
afterKnowInOut(tr.trade, tr.tradeBarrier);
}
//更新,不能放到循环外,黑箱交易的子交易相互有依赖关系
DbContext.SaveChanges();
}
}
/// <summary>
///
/// </summary>
public void CheckBarrierKnockInOutStatus(OtcTradeBase td, trade_barrier_option tradeBarrier
, DateTime valuedate, double? closePrice, double? highPrice, double? lowPrice)
{
var BarrierPrice = td.IsMoneynessOptionData ? tradeBarrier.BarrierPrice * td.SpotPrice : tradeBarrier.BarrierPrice;
var UpperBarrierPrice = td.IsMoneynessOptionData ? tradeBarrier.UpperBarrierPrice * td.SpotPrice : tradeBarrier.UpperBarrierPrice;
switch (tradeBarrier.Discrete)
{
case "离散":
var observationDates = QdpHelper.GetObservationDatesFromString(tradeBarrier.ObservationDates);
//每日观察或者当前结算日是观察日的时候,才检查是否会敲入敲出
if (observationDates == null || observationDates.Contains(valuedate))
{
switch (tradeBarrier.BarrierType)
{
case "上升敲入":
if (closePrice >= BarrierPrice)
{
tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedIn;
tradeBarrier.KnockInOutDate = valuedate;
tradeBarrier.KnockInOutNotional = td.Notional;
}
break;
case "上升敲出":
if (closePrice >= BarrierPrice)
{
tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
tradeBarrier.KnockInOutDate = valuedate;
tradeBarrier.KnockInOutNotional = td.Notional;
td.TradeStatus = ConsTrade.;
td.UnWindDate = valuedate;
SaveBarrierRebateCash(td, tradeBarrier, valuedate, closePrice, tradeBarrier.BarrierPrice);
}
break;
case "下降敲入":
if (closePrice <= BarrierPrice)
{
tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedIn;
tradeBarrier.KnockInOutDate = valuedate;
tradeBarrier.KnockInOutNotional = td.Notional;
}
break;
case "下降敲出":
if (closePrice <= BarrierPrice)
{
tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
tradeBarrier.KnockInOutDate = valuedate;
tradeBarrier.KnockInOutNotional = td.Notional;
td.TradeStatus = ConsTrade.;
td.UnWindDate = valuedate;
SaveBarrierRebateCash(td, tradeBarrier, valuedate, closePrice, tradeBarrier.BarrierPrice);
}
break;
case "双障碍敲出":
if (closePrice >= UpperBarrierPrice || closePrice <= BarrierPrice)
{
var barrierPrice = closePrice >= UpperBarrierPrice ?
tradeBarrier.UpperBarrierPrice : tradeBarrier.BarrierPrice;
tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
tradeBarrier.KnockInOutDate = valuedate;
tradeBarrier.KnockInOutNotional = td.Notional;
td.TradeStatus = ConsTrade.;
td.UnWindDate = valuedate;
var upDown = closePrice >= UpperBarrierPrice ? true : false;
SaveBarrierRebateCash(td, tradeBarrier, valuedate, closePrice, barrierPrice, upDown);
}
break;
case "双障碍敲入":
if (closePrice >= UpperBarrierPrice || closePrice <= BarrierPrice)
{
tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedIn;
tradeBarrier.KnockInOutDate = valuedate;
tradeBarrier.KnockInOutNotional = td.Notional;
}
break;
}
}
break;
case "连续":
switch (tradeBarrier.BarrierType)
{
case "上升敲入":
if (highPrice >= BarrierPrice)
{
tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedIn;
tradeBarrier.KnockInOutDate = valuedate;//UpdateDate
tradeBarrier.KnockInOutNotional = td.Notional;
}
break;
case "上升敲出":
if (highPrice >= BarrierPrice)
{
tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
tradeBarrier.KnockInOutDate = valuedate;
tradeBarrier.KnockInOutNotional = td.Notional;
td.TradeStatus = ConsTrade.;
td.UnWindDate = valuedate;
SaveBarrierRebateCash(td, tradeBarrier, valuedate, highPrice, tradeBarrier.BarrierPrice);
}
break;
case "下降敲入":
if (lowPrice <= BarrierPrice)
{
tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedIn;
tradeBarrier.KnockInOutDate = valuedate;
tradeBarrier.KnockInOutNotional = td.Notional;
}
break;
case "下降敲出":
if (lowPrice <= BarrierPrice)
{
tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
tradeBarrier.KnockInOutDate = valuedate;
tradeBarrier.KnockInOutNotional = td.Notional;
td.TradeStatus = ConsTrade.;
td.UnWindDate = valuedate;
SaveBarrierRebateCash(td, tradeBarrier, valuedate, lowPrice, tradeBarrier.BarrierPrice);
}
break;
case "双障碍敲出":
if (highPrice >= UpperBarrierPrice || lowPrice <= BarrierPrice)
{
var price = highPrice >= UpperBarrierPrice ? highPrice : lowPrice;
var barrierPrice = highPrice >= UpperBarrierPrice ?
tradeBarrier.UpperBarrierPrice : tradeBarrier.BarrierPrice;
tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
tradeBarrier.KnockInOutDate = valuedate;
tradeBarrier.KnockInOutNotional = td.Notional;
td.TradeStatus = ConsTrade.;
td.UnWindDate = valuedate;
var upDown = highPrice >= UpperBarrierPrice ? true : false;
SaveBarrierRebateCash(td, tradeBarrier, valuedate, price, barrierPrice, upDown);
}
break;
case "双障碍敲入":
if (highPrice >= UpperBarrierPrice || lowPrice <= BarrierPrice)
{
tradeBarrier.KnockInOutStatus = ConsTrade.KnockState.KnockedIn;
tradeBarrier.KnockInOutDate = valuedate;
tradeBarrier.KnockInOutNotional = td.Notional;
}
break;
}
break;
}
}
/// <summary>
/// 障碍期权到期时仍未敲入,或者已经敲出,应获得rebate,保存相应的资金信息
/// </summary>
public void SaveBarrierRebateCash(OtcTradeBase td, trade_barrier_option tradeBarrier, DateTime settleDate, double? closePrice, double? knockOutBarrierPrice, bool upDown = false)
{
var tc = SetTradeCash(td, tradeBarrier, settleDate, closePrice, knockOutBarrierPrice, upDown);
SaveTradeCashDetail(tc);
//增加出入金记录
new ClientCashinCashoutBLL(this).CloseTrade_ClientCashInCashOutSave(td, tc, tc.ValueDate);
}
/// <summary>
/// 生成TradeCash
/// </summary>
/// <param name="td">交易</param>
/// <param name="tradeBarrier">子对象</param>
/// <param name="settleDate">敲出/了结日期</param>
/// <param name="closePrice">收盘价</param>
/// <param name="knockOutBarrierPrice">障碍价</param>
/// <param name="saveChanges">是否保存</param>
/// <returns></returns>
public trade_cash SetTradeCash(OtcTradeBase td, trade_barrier_option tradeBarrier, DateTime settleDate, double? closePrice, double? knockOutBarrierPrice, bool upDown = false, bool saveChanges = true)
{
var spotPrice = td.SpotPrice ?? 0;
double rebate, rebateRate;
if (upDown)
{
if (td.IsUsePremiumRate == true)
{
rebateRate = tradeBarrier.RebateHighRate ?? 0;
rebate = rebateRate * spotPrice;
}
else
{
rebate = tradeBarrier.RebateHigh ?? 0;
rebateRate = spotPrice > 0 ? rebate / spotPrice : 0;
}
}
else
{
if (td.IsUsePremiumRate == true)
{
rebateRate = tradeBarrier.RebateRate ?? 0;
rebate = rebateRate * spotPrice;
}
else
{
rebate = tradeBarrier.Rebate ?? 0;
rebateRate = spotPrice > 0 ? rebate / spotPrice : 0;
}
}
if (tradeBarrier.RebateAnnualizedAtKO)
{
var rebateDayCountImpl = string.IsNullOrWhiteSpace(tradeBarrier.RebateDayCount) ? new Act365() : tradeBarrier.RebateDayCount.ToDayCountImpl();
var fraction = rebateDayCountImpl.CalcDayCountFraction(new Date(td.StartDate.Value), new Date(settleDate));
rebate *= fraction;
rebate += Math.Abs((td.TradeSinglePrice - td.TradeSinglePrice * fraction) ?? 0);
}
var req = new TradeCashReq
{
UnwindNotional = td.Notional,
UnwindPrice = rebate,
UnwindPricePercentRate = rebateRate,
FinalPrice = closePrice,
UnwindPercentRate = td.OriginalNotional > 0 ? td.Notional / td.OriginalNotional.Value : 0,
Notional = td.Notional,
TradeAmount = td.TradeAmount,
ValueDate = settleDate,
HappenedDate = settleDate,//记录流水记录的结算日
BarrierPrice = knockOutBarrierPrice
};
req.UnwindFee = req.UnwindNotional * (req.UnwindPrice ?? 0) + ((req.UnwindNotional / td.OriginalNotional * td.OriginalPrincipalSum) ?? 0);
if (valuedateBLL.SystemDate.UnwindAmountAngle == 1)
{
req.UnwindFee = req.UnwindFee * (td.BuySell == "卖出" ? -1 : 1);
}
if (valuedateBLL.SystemDate.UnwindSinglePriceAngle == 1)
{
req.UnwindPrice = req.UnwindPrice * (td.BuySell == "卖出" ? -1 : 1);
}
var tc = CloseTrade_TradeCashSave(td, req, isFromRecheckOrKO: true, isLastAction: true, saveChanges: saveChanges);
if (tradeBarrier.RebateType == "AtEnd")
{
tc.ValueDate = td.ExerciseDate.Value;
tc.HappenedDate = settleDate;
}
if (td.IsGroup == 2 && td.ParentTradeId > 0)
{
var groupAction = DbContext.trade_cash_group_action.FirstOrDefault(x => x.TradeId == td.id && x.Status != "已完成");
if (groupAction != null)
{
groupAction.Status = "已完成";
tc.ParentTradeCashId = groupAction.ParentTradeCashId;
tc.ParentTradeId = groupAction.ParentTradeId;
}
else
{
tc.ParentTradeId = td.ParentTradeId;
tc.ParentTradeCashId = SaveGroupUnwindCash(td, tc.ValueDate, tc.Amount, closePrice ?? 0, out var continueTradeCashHandle).id;
}
}
tc.ValidState = "Valid";
//敲出价格为null,代表该交易为敲入类型的交易,但未敲入,到期后需要返还补偿金额的一条tradecash记录,归为到期行为
if (knockOutBarrierPrice == null)
{
tc.ExerciseWay = TradeCashExerciseWayEnum.;
}
else
{
tc.ExerciseWay = TradeCashExerciseWayEnum.;
}
return tc;
}
}
}
@@ -0,0 +1,314 @@
using Qdp.Foundation.Implementations;
using Qdp.Pricing.Base.Implementations;
using Qdp.Pricing.Base.Utilities;
using YLErp.Abstract.DataProviders;
using YLErp.BLL;
using YLErp.BLL.Eod;
using YLErp.DBModels.Consts;
using YLErp.DBModels.Enums;
using YLErp.Modules.DataProviderModule;
using YLErp.Modules.TradeModule.ExoticOptionModule;
using YLErp.QdpModule;
namespace YLErp.Modules.TradeModule.DealModule
{
/// <summary>
/// 二元期权操作
/// 迁移自:trade_binary_optionBLL
/// </summary>
public class BinaryOptionDealService : TradeCashServiceEx
{
public BinaryOptionDealService(YLBaseService baseService) : base(baseService)
{
}
public BinaryOptionDealService(OptUserInfo userInfo) : base(userInfo)
{
}
/// <summary>
///
/// </summary>
public void CheckTouchStatus(DateTime valueDate, IEodPriceProviderV2 priceProvider, DateTime? startDate = null,
Action<OtcTrade, trade_binary_option> afterKnowInOut = null, System.Collections.Generic.IEnumerable<int> clienIds = null)
{
if (priceProvider is null)
{
priceProvider = new EodPriceProvider(valueDate);
}
if (startDate == null)
{
startDate = valueDate.AddYears(-5);
}
var query = from td in DbContext.trade
join tb in DbContext.trade_binary_option on td.id equals tb.TradeId
where td.TradeDate > startDate.Value && td.TradeDate <= valueDate
&& td.ExerciseDate >= valueDate
&& ConsTrade. == td.TradeStatus
&& td.ValidState != ConsGlobal.InValid
&& td.TradeType == "二元期权" && td.ExerciseMode == "American"
&& td.DividendDate < valueDate
select new
{
trade = td,
tradeBinary = tb
};
#region tw
if (clienIds != null)
{
query = query.Where(l => clienIds.Contains(l.trade.ClientId));
}
#endregion
var trades = query.ToList();
if (trades == null || !trades.Any())
{
return;
}
// 美式二元检查
foreach (var tr in trades)
{
if (tr.trade.ExerciseDate < valueDate)
{
continue;//已到期交易不再观察;
}
var tradeStatus = tr.trade.TradeStatus;
if (!priceProvider.TryGetEodPrice(tr.trade.UnderlyingCode, out var eodprice))
{
throw new Exception($"[{tr.trade.TradeType}:{tr.trade.TradeNumber}]标的:{tr.trade.UnderlyingCode} 未找到结算价");
}
double? upPrice = 0, lowPrice = 0;
var isObservationDate = false;
//根据是否为离散观察来确定用来比较的价格
//如果是离散观察,只用收盘价比较
//如果是连续观察,使用最高价和最低价
if (tr.tradeBinary.IsDiscreteMonitored)
{
var observationDates = QdpHelper.GetObservationDatesFromString(tr.tradeBinary.ObservationDates);
if (observationDates == null || observationDates.Contains(valueDate))
{
isObservationDate = true;
upPrice = lowPrice = eodprice.ClosePrice;
}
}
else
{
isObservationDate = true;
upPrice = eodprice.HighPrice;
lowPrice = eodprice.LowPrice;
}
var strike = tr.trade.IsMoneynessOptionData ? tr.trade.Strike * tr.trade.SpotPrice : tr.trade.Strike;
var UpperBarrier = new Lazy<double?>(() =>
tr.trade.IsMoneynessOptionData ? tr.tradeBinary.UpperBarrier * tr.trade.SpotPrice : tr.tradeBinary.UpperBarrier);
if (isObservationDate)
{
switch (tr.tradeBinary.PayoffType)
{
case "UpOneTouch":
if (upPrice > strike)
{
//触碰,买方获得盈利,交易结束
var cash = GetCashOrNothingAmount(tr.trade, tr.tradeBinary, valueDate, useHighAmount: false);
SaveBinarySettleCash(tr.trade, tr.tradeBinary, valueDate, cash, true, upPrice);
tr.trade.TradeStatus = ConsTrade.;
tr.trade.UnWindDate = valueDate;
}
else if (tr.trade.ExerciseDate <= valueDate)
{
SaveBinarySettleCash(tr.trade, tr.tradeBinary, valueDate, 0, false, upPrice);
}
break;
case "DownOneTouch":
if (lowPrice < strike)
{
//触碰,买方获得盈利,交易结束
var cash = GetCashOrNothingAmount(tr.trade, tr.tradeBinary, valueDate, useHighAmount: false);
SaveBinarySettleCash(tr.trade, tr.tradeBinary, valueDate, cash, true, lowPrice);
tr.trade.TradeStatus = ConsTrade.;
tr.trade.UnWindDate = valueDate;
}
else if (tr.trade.ExerciseDate <= valueDate)
{
SaveBinarySettleCash(tr.trade, tr.tradeBinary, valueDate, 0, false, lowPrice);
}
break;
case "UpNoTouch":
if (upPrice > strike)
{
//触碰,买方无盈利,交易结束
SaveBinarySettleCash(tr.trade, tr.tradeBinary, valueDate, 0, false, upPrice);
tr.trade.TradeStatus = ConsTrade.;
tr.trade.UnWindDate = valueDate;
}
break;
case "DownNoTouch":
if (lowPrice < strike)
{
//触碰,买方无盈利,交易结束
SaveBinarySettleCash(tr.trade, tr.tradeBinary, valueDate, 0, false, lowPrice);
tr.trade.TradeStatus = ConsTrade.;
tr.trade.UnWindDate = valueDate;
}
break;
case "DoubleOneTouch":
if (upPrice >= UpperBarrier.Value || lowPrice <= strike)
{
var breachHighBarrier = upPrice >= UpperBarrier.Value;
var price = breachHighBarrier ? upPrice : lowPrice;
var cash = GetCashOrNothingAmount(tr.trade, tr.tradeBinary, valueDate, useHighAmount: breachHighBarrier);
//触碰上限或下限,买方获得盈利,交易结束
SaveBinarySettleCash(tr.trade, tr.tradeBinary, valueDate, cash, true, price);
tr.trade.TradeStatus = ConsTrade.;
tr.trade.UnWindDate = valueDate;
}
else if (tr.trade.ExerciseDate <= valueDate)
{
SaveBinarySettleCash(tr.trade, tr.tradeBinary, valueDate, 0, false, eodprice.ClosePrice);
}
break;
case "DoubleNoTouch":
if (upPrice > UpperBarrier.Value || lowPrice < strike)
{
var price = upPrice >= tr.tradeBinary.UpperBarrier ? upPrice : lowPrice;
//触碰上限或下限,买方无盈利,交易结束
SaveBinarySettleCash(tr.trade, tr.tradeBinary, valueDate, 0, false, price);
tr.trade.TradeStatus = ConsTrade.;
tr.trade.UnWindDate = valueDate;
}
break;
}
}
//OneTouch类型,到期仍未触碰,则交易结束
if (tr.tradeBinary.PayoffType.Contains("OneTouch") && tr.trade.ExerciseDate <= valueDate)
{
//SaveBinarySettleCash(tr.trade, tr.tradeBinary, valueDate, 0, false, eodprice.ClosePrice);
tr.trade.TradeStatus = ConsTrade.;
tr.trade.UnWindDate = valueDate;
}
//NoTouch类型,到期仍未触碰,买方获得盈利,交易结束
if (tr.tradeBinary.PayoffType.Contains("NoTouch") && tr.trade.ExerciseDate <= valueDate && !ConsTrade.TradeCompleteStatus.Contains(tr.trade.TradeStatus))
{
var cash = GetCashOrNothingAmount(tr.trade, tr.tradeBinary, valueDate, useHighAmount: false);
SaveBinarySettleCash(tr.trade, tr.tradeBinary, valueDate, cash, false, eodprice.ClosePrice);
tr.trade.TradeStatus = ConsTrade.;
tr.trade.UnWindDate = valueDate;
}
if (tradeStatus != tr.trade.TradeStatus)
{
//删除E/Bod_Trade记录
RemoveEodTradeAndFutureInfo(false, tr.trade.id, valueDate);
}
if (afterKnowInOut != null && DbContext.Entry(tr.tradeBinary).State == EntityState.Modified)
{
afterKnowInOut(tr.trade, tr.tradeBinary);
}
//更新,不能放到循环外,黑箱交易的子交易相互有依赖关系
DbContext.SaveChanges();
}
}
/// <summary>
///
/// </summary>
public static double GetCashOrNothingAmount(OtcTradeBase trade, trade_binary_option tradeBinary, DateTime settleDate, bool useHighAmount = false)
{
var result = (trade.Notional / trade.OriginalNotional * (trade.OriginalPrincipalSum ?? 0)) ?? 0;
double refund;
if (trade.IsUsePremiumRate == true)
{
var rate = useHighAmount ? tradeBinary.CashOrNothingAmountHighRate : tradeBinary.CashOrNothingAmountRate;
refund = Math.Abs((rate ?? 0) * (trade.SpotPrice ?? 0) * trade.Notional);
}
else
{
var amount = useHighAmount ? tradeBinary.CashOrNothingAmountHigh : tradeBinary.CashOrNothingAmount;
refund = Math.Abs((amount ?? 0) * trade.Notional);
}
if (tradeBinary.RebateAnnualizedAtKO)
{
var rebateDayCountImpl = string.IsNullOrWhiteSpace(tradeBinary.RebateDayCount) ? new Act365() : tradeBinary.RebateDayCount.ToDayCountImpl();
var fraction = rebateDayCountImpl.CalcDayCountFraction(new Date(trade.StartDate.Value), new Date(settleDate));
refund *= fraction;
refund += Math.Abs((trade.TradePrice - trade.TradePrice * fraction) ?? 0);
}
return result + refund;
}
/// <summary>
/// 美式二元期权触碰后,买方获得盈利,记录资金信息
/// </summary>
public trade_cash SaveBinarySettleCash(OtcTradeBase td, trade_binary_option tradeBinary, DateTime valuedate, double cash, bool earlyExecute, double? price, bool saveChanges = true)
{
var tradeCash = new trade_cash
{
ValidState = "Valid",
OptDate = DateTime.Now,
OptId=UserId,
OptName=UserName,
Action = ClientCashInCashOut._行权费,
IsLastAction = true,
ValueDate = valuedate,
Strike = td.Strike,
CallPut = td.CallPut,
Amount = cash * EodOperationBase.GetSign(td.BuySell),
UnwindPrice = cash / td.Notional,
UnwindPricePercentRate = cash / td.OriginalStockEqvNotional,
Status = TradeCashStatusEnum.,
TradeType = td.BuySell,
TradeId = td.id,
Notional = td.Notional,
TradeAmount = td.TradeAmount,
UnwindPercentRate = td.OriginalNotional > 0 ? td.Notional / td.OriginalNotional : 0,
FinalPrice = price,
ExerciseWay = earlyExecute ? TradeCashExerciseWayEnum. : TradeCashExerciseWayEnum.
};
if (tradeBinary.RebateType == "AtEnd")
{
tradeCash.ValueDate = td.ExerciseDate.Value;
tradeCash.HappenedDate = valuedate;
}
if (saveChanges)
{
if (td.IsGroup == 2 && td.ParentTradeId > 0)
{
var groupAction = DbContext.trade_cash_group_action.FirstOrDefault(x => x.TradeId == td.id && x.Status != "已完成");
if (groupAction != null)
{
groupAction.Status = "已完成";
tradeCash.ParentTradeCashId = groupAction.ParentTradeCashId;
tradeCash.ParentTradeId = groupAction.ParentTradeId;
}
else
{
tradeCash.ParentTradeId = td.ParentTradeId;
tradeCash.ParentTradeCashId = SaveGroupUnwindCash(td, tradeCash.ValueDate, tradeCash.Amount, price ?? 0, out bool continueTradeCashHandle).id;
}
}
DbContext.trade_cash.Add(tradeCash);
DbContext.SaveChanges();
SaveTradeCashDetail(tradeCash);
new ClientCashinCashoutBLL(this).CloseTrade_ClientCashInCashOutSave(td, tradeCash, tradeCash.ValueDate);
}
return tradeCash;
}
}
}
@@ -0,0 +1,229 @@
using YLErp.Abstract.DataProviders;
using YLErp.BLL;
using YLErp.DBModels.Consts;
using YLErp.Model;
using YLErp.Modules.DataProviderModule;
using YLErp.QdpModule;
namespace YLErp.Modules.TradeModule.ExoticOptionModule
{
/// <summary>
/// 双鲨期权敲出操作
/// 迁移自:trade_double_sharkfin_optionBLL
/// </summary>
public class DoubleSharkOptionKnockoutService : TradeCashServiceEx
{
public DoubleSharkOptionKnockoutService(YLBaseService baseService) : base(baseService)
{
}
public DoubleSharkOptionKnockoutService(OptUserInfo userInfo) : base(userInfo)
{
}
/// <summary>
/// 检出敲入敲出
/// </summary>
public void CheckKnockoutStatus(DateTime valueDate, IEodPriceProviderV2 priceProvider, DateTime? startDate = null
, Action<OtcTrade, trade_double_sharkfin_option> afterKnowInOut = null, System.Collections.Generic.IEnumerable<int> clienIds = null)
{
if (priceProvider is null)
{
priceProvider = new EodPriceProvider(valueDate);
}
if (startDate == null)
{
startDate = valueDate.AddYears(-5);
}
var query = from td in DbContext.trade
join tb in DbContext.trade_double_sharkfin_option on td.id equals tb.TradeId
where td.TradeDate > startDate.Value && td.TradeDate <= valueDate
&& string.IsNullOrEmpty(tb.KnockInOutStatus)
&& td.TradeType == "双鲨期权"
&& ConsTrade. == td.TradeStatus
&& td.ValidState != ConsGlobal.InValid
&& td.DividendDate < valueDate
select new
{
trade = td,
tradeDbShark = tb
};
#region tw
if (clienIds != null)
{
query = query.Where(l => clienIds.Contains(l.trade.ClientId));
}
#endregion
var trades = query.ToList();
if (trades == null || !trades.Any())
{
return;
}
//检查是否敲出
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 observationDates = QdpHelper.GetObservationDatesFromString(tr.tradeDbShark.ObservationDates);
//每日观察或者当前结算日是观察日的时候,才检查是否会敲敲出
if (observationDates == null || observationDates.Contains(valueDate) || !tr.tradeDbShark.IsDiscrete)
{
//根据是否为离散观察来确定用来比较的价格
//如果是离散观察,只用收盘价比较
//如果是连续观察,使用最高价和最低价
double? upPrice, lowPrice;
if (manuallyTradeObservationPrices.TryGetValue(tr.trade.id, out var manuallyTradeObservationPrice))
{
upPrice = manuallyTradeObservationPrice.Price;
lowPrice = manuallyTradeObservationPrice.Price;
}
else if (!priceProvider.TryGetEodPrice(tr.trade.UnderlyingCode, out var eodprice))
{
throw new Exception($"[{tr.trade.TradeType}:{tr.trade.TradeNumber},标的:{tr.trade.UnderlyingCode}]未找到结算价");
}
else if (tr.tradeDbShark.IsDiscrete)
{
upPrice = eodprice.ClosePrice;
lowPrice = eodprice.ClosePrice;
}
else
{
upPrice = eodprice.HighPrice;
lowPrice = eodprice.LowPrice;
}
var oldKnockInOutStatus = tr.tradeDbShark.KnockInOutStatus;
CheckDoubleSharkFinKnockOutStatus(tr.trade, tr.tradeDbShark, valueDate, upPrice, lowPrice);
if (oldKnockInOutStatus != tr.tradeDbShark.KnockInOutStatus)
{
var KnockInOutStatus = tr.tradeDbShark.KnockInOutStatus == ConsTrade.KnockState.KnockedIn ? "敲入" : "敲出";
AddTradeOperationHistoryAndSetParentTradeInfo(false, tr.trade, KnockInOutStatus, KnockInOutStatus);
}
if (tradeStatus != tr.trade.TradeStatus|| oldKnockInOutStatus != tr.tradeDbShark.KnockInOutStatus)
{
//删除E/Bod_Trade记录
RemoveEodTradeAndFutureInfo(false, tr.trade.id, valueDate);
}
if (afterKnowInOut != null && DbContext.Entry(tr.tradeDbShark).State == EntityState.Modified)
{
afterKnowInOut(tr.trade, tr.tradeDbShark);
}
}
//更新,不能放到循环外,黑箱交易的子交易相互有依赖关系
DbContext.SaveChanges();
}
}
public void CheckDoubleSharkFinKnockOutStatus(OtcTradeBase td, trade_double_sharkfin_option tradeDbShark, DateTime valuedate, double? upPrice, double? lowPrice)
{
var barrierHigh = td.IsMoneynessOptionData ? tradeDbShark.BarrierHigh * td.SpotPrice : tradeDbShark.BarrierHigh;
var barrierLow = td.IsMoneynessOptionData ? tradeDbShark.BarrierLow * td.SpotPrice : tradeDbShark.BarrierLow;
if (upPrice >= barrierHigh || lowPrice <= barrierLow)
{
var useRebate = lowPrice <= barrierLow;
var price = upPrice >= barrierHigh ? upPrice : lowPrice;
tradeDbShark.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
tradeDbShark.KnockInOutDate = valuedate;
td.TradeStatus = ConsTrade.;
td.UnWindDate = valuedate;
SaveDoubleSharkFinRebateCash(td, tradeDbShark, price, valuedate, useRebate);
}
}
/// <summary>
/// 双鲨期权敲出,应获得rebate,保存相应的资金信息
/// </summary>
public trade_cash SaveDoubleSharkFinRebateCash(OtcTradeBase td, trade_double_sharkfin_option tradeDbShark, double? closePrice, DateTime valueDate, bool useRebate, bool saveChanges = true)
{
double rebate, rebateRate;
var spotPrice = td.SpotPrice ?? 0;
if (td.IsUsePremiumRate == true)
{
rebateRate = (useRebate ? tradeDbShark.RebateRate : tradeDbShark.RebateHighRate) ?? 0;
rebate = rebateRate * spotPrice;
}
else
{
rebate = (useRebate ? tradeDbShark.Rebate : tradeDbShark.RebateHigh) ?? 0;
rebateRate = spotPrice > 0 ? rebate / spotPrice : 0;
}
var req = new TradeCashReq
{
UnwindNotional = td.Notional,
UnwindPercentRate = td.OriginalNotional > 0 ? td.Notional / td.OriginalNotional.Value : 0,
FinalPrice = closePrice,
UnwindPrice = rebate,
UnwindPricePercentRate = rebateRate,
Notional = td.Notional,
TradeAmount = td.TradeAmount,
ValueDate = valueDate,
HappenedDate = valueDate,
BarrierPrice = useRebate ? tradeDbShark.BarrierLow : tradeDbShark.BarrierHigh
};
req.UnwindFee = req.UnwindNotional * (req.UnwindPrice ?? 0) + ((req.UnwindNotional / td.OriginalNotional * td.OriginalPrincipalSum) ?? 0);
if(valuedateBLL.SystemDate.UnwindAmountAngle == 1)
{
req.UnwindFee = req.UnwindFee * (td.BuySell == "卖出" ? -1 : 1);
}
if (valuedateBLL.SystemDate.UnwindSinglePriceAngle == 1)
{
req.UnwindPrice = req.UnwindPrice * (td.BuySell == "卖出" ? -1 : 1);
}
var tc = CloseTrade_TradeCashSave(td, req, isFromRecheckOrKO: true, isLastAction: true, saveChanges: false);
tc.ValidState = ConsGlobal.Valid;
tc.ExerciseWay = TradeCashExerciseWayEnum.;
if (tradeDbShark.RebateType == "AtEnd")
{
tc.ValueDate = td.ExerciseDate.Value;
tc.HappenedDate = valueDate;
}
//加入平仓份额和平仓日期
td.UnWindDate = tc.ValueDate;
if (td.IsGroup == 2 && td.ParentTradeId > 0)
{
var groupAction = DbContext.trade_cash_group_action.FirstOrDefault(x => x.TradeId == td.id && x.Status != "已完成");
if (groupAction != null)
{
groupAction.Status = "已完成";
tc.ParentTradeCashId = groupAction.ParentTradeCashId;
tc.ParentTradeId = groupAction.ParentTradeId;
}
else
{
tc.ParentTradeId = td.ParentTradeId;
tc.ParentTradeCashId = SaveGroupUnwindCash(td, tc.ValueDate, tc.Amount, closePrice ?? 0, out var continueTradeCashHandle).id;
}
}
if (saveChanges)
{
SaveTradeCashDetail(tc);
//增加出入金记录
new ClientCashinCashoutBLL(this).CloseTrade_ClientCashInCashOutSave(td, tc, tc.ValueDate);
}
return tc;
}
}
}
@@ -0,0 +1,184 @@
namespace YLErp.Modules.TradeModule.ExoticOptionModule
{
/// <summary>
/// 预付金雪球赔付
/// </summary>
public abstract class PrepaymentSnowballPayoff
{
/// <summary>
/// 利息金额(买方角度)
/// </summary>
public double InterestAmount { get; set; }
/// <summary>
/// 预付金金额(买方角度)
/// </summary>
public double PrepaymentAmount { get; set; }
/// <summary>
/// 倒置符号(将正值倒置为负值,负值倒置为正值)
/// </summary>
public virtual void InvertSign()
{
InterestAmount = -InterestAmount;
PrepaymentAmount = -PrepaymentAmount;
}
}
/// <summary>
/// 雪球敲出赔付
/// </summary>
public class PrepaymentSnowballKoPayoff : PrepaymentSnowballPayoff
{
/// <summary>
/// 票息金额(买方金额)
/// </summary>
public double CouponPaymentAmount { get; set; }
/// <summary>
/// 增强收益金额(买方金额)
/// </summary>
public double EnhancedPaymentAmount { get; set; }
/// <summary>
/// 敲出票息开始日
/// </summary>
public DateTime CouponStartDate { get; set; }
/// <summary>
/// 敲出票息结束日
/// </summary>
public DateTime CouponEndDate { get; set; }
/// <summary>
/// 票息率
/// </summary>
public double CouponRate { get; set; }
public double TotalAmount()
{
return CouponPaymentAmount + EnhancedPaymentAmount + InterestAmount;
}
public override void InvertSign()
{
base.InvertSign();
CouponPaymentAmount = -CouponPaymentAmount;
EnhancedPaymentAmount = -EnhancedPaymentAmount;
}
}
/// <summary>
/// 雪球到期日敲入赔付
/// </summary>
public class PrepaymentSnowballKiPayoff : PrepaymentSnowballPayoff
{
/// <summary>
/// 期权赔付金额(买方角度)
/// </summary>
public double OptionPaymentAmount { get; set; }
public double TotalAmount()
{
return OptionPaymentAmount + InterestAmount;
}
public override void InvertSign()
{
base.InvertSign();
OptionPaymentAmount = -OptionPaymentAmount;
}
}
/// <summary>
/// 雪球到期日未敲入赔付
/// </summary>
public class PrepaymentSnowballNkiPayoff : PrepaymentSnowballPayoff
{
/// <summary>
/// 红利票息赔付金额(买方角度)
/// </summary>
public double CouponPaymentAmount { get; set; }
public double TotalAmount()
{
return CouponPaymentAmount + InterestAmount;
}
public override void InvertSign()
{
base.InvertSign();
CouponPaymentAmount = -CouponPaymentAmount;
}
}
/// <summary>
/// 雪球观察结果类型
/// </summary>
public enum SnowballObservationResultType
{
/// <summary>
/// 非观察日
/// </summary>
NonObservationDay,
/// <summary>
/// 观察中
/// </summary>
Monitoring,
/// <summary>
/// 敲入状态
/// </summary>
KnockedIn,
/// <summary>
/// 敲出赔付
/// </summary>
KoPayoff,
/// <summary>
/// 到期日敲入赔付
/// </summary>
KiPayoffAtEndDate,
/// <summary>
/// 到期日非敲入赔付
/// </summary>
NkiPayoffAtEndDate
}
/// <summary>
/// 雪球观察结果
/// </summary>
public class SnowballObservationResult
{
/// <summary>
/// 观察结果类型
/// </summary>
public SnowballObservationResultType ResultType { get; set; }
/// <summary>
/// 支付日期
/// </summary>
public DateTime PaymentDate { get; set; }
/// <summary>
/// 支付总金额(买方角度)
/// </summary>
public double PaymentAmount { get; set; }
/// <summary>
/// 敲出障碍价格
/// </summary>
public double KoBarrier { get; set; }
/// <summary>
/// 赔付明细
/// </summary>
public PrepaymentSnowballPayoff Payoff { get; set; }
}
}
@@ -0,0 +1,495 @@
using Qdp.Pricing.Base.Utilities;
using YLErp.DBModels;
using YLErp.QdpModule;
namespace YLErp.Modules.TradeModule.ExoticOptionModule
{
/// <summary>
/// 专业版雪球观察帮助类
/// </summary>
public class SpecialSnowballObservationHelper
{
private readonly OtcTradeBase _otcTrade;
private readonly trade_snowball _snowball;
public SpecialSnowballObservationHelper(OtcTradeBase otcTrade, trade_snowball snowball)
{
_otcTrade = otcTrade ?? throw new ArgumentNullException(nameof(otcTrade));
_snowball = snowball ?? throw new ArgumentNullException(nameof(snowball));
}
/// <summary>
/// 获取雪球观察结果(买方)
/// </summary>
public SnowballObservationResult GetObservationResultForBuySide(DateTime valueDate, double closePrice, double tradeNotional)
{
System.Diagnostics.Debug.Assert(_snowball.KnockInOutStatus != ConsTrade.KnockState.KnockedOut);
//敲出检查
var koResult = GetKoPayoff(valueDate, closePrice, tradeNotional, out var isKoObservationDay, out var koBarrier);
if (koResult != null)
{
return koResult;
}
var resultType = _snowball.IsInitialKnockedIn || _snowball.KnockInOutStatus == ConsTrade.KnockState.KnockedIn
? SnowballObservationResultType.KnockedIn
: (isKoObservationDay ? SnowballObservationResultType.Monitoring : SnowballObservationResultType.NonObservationDay);
//敲入检查
if (resultType != SnowballObservationResultType.KnockedIn && IsNeedCheckKnockIn(valueDate))
{
var kiBarrier = _otcTrade.IsMoneynessOptionData ? _snowball.KIBarrier * (_otcTrade.SpotPrice ?? 0) : _snowball.KIBarrier;
// 发生敲入事件(看涨 - 向下敲入,看跌 - 向上敲入)
resultType = (ConsGlobal.CallPut.IsCall(_otcTrade.CallPut) ? closePrice <= kiBarrier : closePrice >= kiBarrier)
? SnowballObservationResultType.KnockedIn
: SnowballObservationResultType.Monitoring;
}
SnowballObservationResult result;
//到期检查
if (valueDate == _otcTrade.ExerciseDate.Value)
{
result = resultType == SnowballObservationResultType.KnockedIn
? GetKiPayoffAtEndDate(closePrice, tradeNotional) : GetNKiPayoffAtEndDate(tradeNotional);
}
else
{
result = new SnowballObservationResult { ResultType = resultType };
}
result.KoBarrier = koBarrier ??
(_otcTrade.IsMoneynessOptionData ? _snowball.KOBarrier * (_otcTrade.SpotPrice ?? 0) : _snowball.KOBarrier);
return result;
}
/// <summary>
/// 获取雪球观察结果(交易员角度)
/// </summary>
public SnowballObservationResult GetObservationResultForTraderSide(DateTime valueDate, double closePrice, double tradeNotional)
{
var result = GetObservationResultForBuySide(valueDate, closePrice, tradeNotional);
if (_otcTrade.BuySell == "卖出")
{
result.PaymentAmount = -result.PaymentAmount;
result.Payoff?.InvertSign();
}
return result;
}
/// <summary>
/// 获取累积票息(tradeNotional为负值时是卖方角度)
/// </summary>
public PrepaymentSnowballKoPayoff GetEffectiveObservation(DateTime valueDate, double tradeNotional)
{
var koObservationParseResult = ParseKoObservation(valueDate);
//非敲出观察日
if (koObservationParseResult == null)
{
return null;
}
var payoff = new PrepaymentSnowballKoPayoff
{
CouponStartDate = _otcTrade.StartDate.Value,
CouponEndDate = valueDate,
CouponRate = koObservationParseResult.KoRebate,
//票息支付金额
CouponPaymentAmount = koObservationParseResult.KoRebate * tradeNotional * (_otcTrade.SpotPrice ?? 0)
};
//票息计息年化方式处理
if (!string.IsNullOrWhiteSpace(_snowball.CouponDayCount))
{
//首日是否计息
var startDate = _snowball.CouponIncludeStartDate == true
? _otcTrade.StartDate.Value.AddDays(-1) : _otcTrade.StartDate.Value;
//是否支付日计息
var endDate = valueDate;
if (_snowball.CouponUsePaymentDate == true)
{
endDate = GetKoPaymentDate(_snowball.KOObservationSettleDates, koObservationParseResult.KoDateIndex, valueDate);
}
//终日是否计息
if (_snowball.CouponIncludeEndDate == false)
{
endDate = endDate.AddDays(-1);
}
//票息年化计息
var couponDayCount = _snowball.CouponDayCount.ToDayCountImpl();
var dayFraction = couponDayCount.CalcDayCountFraction(startDate, endDate);
payoff.CouponPaymentAmount *= dayFraction;
}
return payoff;
}
#region--------
/// <summary>
/// 获取到期日敲入收益
/// </summary>
private SnowballObservationResult GetKiPayoffAtEndDate(double closePrice, double tradeNotional)
{
var strike = _otcTrade.IsMoneynessOption == "是"
? (_snowball.SpreadStrikeAtMaturity1 ?? 0) * (_otcTrade.SpotPrice ?? 0)
: _snowball.SpreadStrikeAtMaturity1 ?? 0;
//敲入转香草期权,买卖方向需要反一下,通过notional体现
var notional = -tradeNotional * (_snowball.KIParticipationRate ?? 1);
var isCall = ConsGlobal.CallPut.IsCall(_otcTrade.OptionType);
var payoff = new PrepaymentSnowballKiPayoff();
//期权支付
if (_snowball.PrincipalProtectionRate.HasValue && _snowball.PrincipalProtectionRate.Value != 0)
{
var spread = (1 - _snowball.PrincipalProtectionRate.Value) * (_otcTrade.SpotPrice ?? 0);
if (isCall)
{
//熊市价差
payoff.OptionPaymentAmount = Math.Min(Math.Max(strike - closePrice, 0), spread) * notional;
}
else
{
//牛市价差
payoff.OptionPaymentAmount = Math.Min(Math.Max(closePrice - strike, 0), spread) * notional;
}
}
else if (isCall)
{
//看跌期权
payoff.OptionPaymentAmount = Math.Max(strike - closePrice, 0) * notional;
}
else
{
//看涨期权
payoff.OptionPaymentAmount = Math.Max(closePrice - strike, 0) * notional;
}
//预付金返还
if (_snowball.PrepaymentRatio.HasValue)
{
payoff.PrepaymentAmount = tradeNotional * (_otcTrade.SpotPrice ?? 0) * _snowball.PrepaymentRatio.Value;
//利息金额
if (_snowball.PrepaymentInterestRate.HasValue)
{
payoff.InterestAmount = payoff.PrepaymentAmount * _snowball.PrepaymentInterestRate.Value;
//年化计息
if (!string.IsNullOrWhiteSpace(_snowball.CouponDayCount))
{
//首日是否计息
var startDate = _snowball.CouponIncludeStartDate == true
? _otcTrade.StartDate.Value.AddDays(-1) : _otcTrade.StartDate.Value;
//终日是否计息
var endDate = _snowball.CouponIncludeEndDate == true
? _otcTrade.ExerciseDate.Value : _otcTrade.ExerciseDate.Value.AddDays(-1);
var couponDayCount = _snowball.CouponDayCount.ToDayCountImpl();
var dayFraction = couponDayCount.CalcDayCountFraction(startDate, endDate);
payoff.InterestAmount *= dayFraction;
}
}
}
return new SnowballObservationResult
{
Payoff = payoff,
PaymentAmount = payoff.TotalAmount(),
PaymentDate = _otcTrade.ExerciseDate.Value,
ResultType = SnowballObservationResultType.KiPayoffAtEndDate
};
}
#endregion
#region--------
/// <summary>
/// 获取到期日未敲入收益
/// </summary>
private SnowballObservationResult GetNKiPayoffAtEndDate(double tradeNotional)
{
var notionalValue = tradeNotional * (_otcTrade.SpotPrice ?? 0);
var payoff = new PrepaymentSnowballNkiPayoff();
//红利票息支付金额
payoff.CouponPaymentAmount = _snowball.Coupon * notionalValue;
//预付金返还
if (_snowball.PrepaymentRatio.HasValue)
{
payoff.PrepaymentAmount = notionalValue * _snowball.PrepaymentRatio.Value;
//利息金额
if (_snowball.PrepaymentInterestRate.HasValue)
{
payoff.InterestAmount = payoff.PrepaymentAmount * _snowball.PrepaymentInterestRate.Value;
}
}
//年化计息
if (!string.IsNullOrWhiteSpace(_snowball.CouponDayCount))
{
//首日是否计息
var startDate = _snowball.CouponIncludeStartDate == true
? _otcTrade.StartDate.Value.AddDays(-1) : _otcTrade.StartDate.Value;
//终日是否计息
var endDate = _snowball.CouponIncludeEndDate == true
? _otcTrade.ExerciseDate.Value : _otcTrade.ExerciseDate.Value.AddDays(-1);
var couponDayCount = _snowball.CouponDayCount.ToDayCountImpl();
var dayFraction = couponDayCount.CalcDayCountFraction(startDate, endDate);
payoff.InterestAmount *= dayFraction;
payoff.CouponPaymentAmount *= dayFraction;
}
return new SnowballObservationResult
{
Payoff = payoff,
PaymentAmount = payoff.TotalAmount(),
PaymentDate = _otcTrade.ExerciseDate.Value,
ResultType = SnowballObservationResultType.NkiPayoffAtEndDate
};
}
#endregion
#region--------
/// <summary>
/// 获取敲出收益
/// </summary>
private SnowballObservationResult GetKoPayoff(DateTime valueDate, double closePrice, double tradeNotional
, out bool isObservationDay, out double? koBarrier)
{
var koObservationParseResult = ParseKoObservation(valueDate);
koBarrier = koObservationParseResult?.KoBarrier;
isObservationDay = koObservationParseResult != null;
//非敲出观察日
if (!isObservationDay)
{
return null;
}
var isCall = ConsGlobal.CallPut.IsCall(_otcTrade.CallPut);
// 发生敲出事件(看涨 - 向上敲出支付票息,看跌 - 向下敲出支付票息)
if (isCall ? closePrice < koObservationParseResult.KoBarrier : closePrice > koObservationParseResult.KoBarrier)
{
return null;
}
var payoff = new PrepaymentSnowballKoPayoff
{
CouponStartDate = _otcTrade.StartDate.Value,
CouponEndDate = valueDate,
CouponRate = koObservationParseResult.KoRebate
};
var notionalValue = tradeNotional * (_otcTrade.SpotPrice ?? 0);
//增强收益支付金额
if (_snowball.EnhancedParticipationRate.HasValue)
{
payoff.EnhancedPaymentAmount = (closePrice - koObservationParseResult.KoBarrier) * tradeNotional * _snowball.EnhancedParticipationRate.Value;
if (!isCall)
{
payoff.EnhancedPaymentAmount = -payoff.EnhancedPaymentAmount;
}
}
//票息支付金额
payoff.CouponPaymentAmount = koObservationParseResult.KoRebate * notionalValue;
//预付金返还
if (_snowball.PrepaymentRatio.HasValue)
{
payoff.PrepaymentAmount = notionalValue * _snowball.PrepaymentRatio.Value;
//利息金额
if (_snowball.PrepaymentInterestRate.HasValue)
{
payoff.InterestAmount = payoff.PrepaymentAmount * _snowball.PrepaymentInterestRate.Value;
}
}
//票息支付日
var paymentDate = GetKoPaymentDate(_snowball.KOObservationSettleDates, koObservationParseResult.KoDateIndex, valueDate);
//票息计息年化方式处理
if (!string.IsNullOrWhiteSpace(_snowball.CouponDayCount))
{
//首日是否计息
var startDate = _snowball.CouponIncludeStartDate == true
? _otcTrade.StartDate.Value.AddDays(-1) : _otcTrade.StartDate.Value;
//是否支付日计息
var endDate = valueDate;
if (_snowball.CouponUsePaymentDate == true)
{
endDate = paymentDate;
}
//终日是否计息
if (_snowball.CouponIncludeEndDate == false)
{
endDate = endDate.AddDays(-1);
}
payoff.CouponEndDate = endDate;
//票息年化计息
var couponDayCount = _snowball.CouponDayCount.ToDayCountImpl();
var dayFraction = couponDayCount.CalcDayCountFraction(startDate, endDate);
payoff.CouponPaymentAmount *= dayFraction;
//年化利息金额
payoff.InterestAmount *= dayFraction;
}
//返回敲出观察结果
var result = new SnowballObservationResult
{
Payoff = payoff,
PaymentDate = paymentDate,
ResultType = SnowballObservationResultType.KoPayoff,
PaymentAmount = payoff.TotalAmount(),
KoBarrier = koObservationParseResult.KoBarrier
};
if (_snowball.KORebateType == RebateTypeEnum.AtEnd)
{
result.PaymentDate = _otcTrade.ExerciseDate.Value;
}
return result;
}
/// <summary>
/// 解析敲出观察频率
/// </summary>
private KoObservationParseResult ParseKoObservation(DateTime valueDate)
{
(var koDates, var koBarries, var koCoupons)
= QdpHelper.ParseAutocallCustomizedInfo(_snowball.KOObservationDates);
koDates ??= QdpObservationHelper.GetDefaultKoObservationDatesForSnowbalV2(_otcTrade.StartDate.Value, _otcTrade.ExerciseDate.Value);
var koDateIndex = koDates.Select(x => x.DateTime).ToList().IndexOf(valueDate);
if (koDateIndex < 0)
{
return null;
}
var result = new KoObservationParseResult
{
KoDateIndex = koDateIndex,
KoRebate = koCoupons != null && koCoupons.Length > koDateIndex ? koCoupons[koDateIndex] : _snowball.KORebate,
KoBarrier = koBarries != null && koBarries.Length > koDateIndex ? koBarries[koDateIndex] : _snowball.KOBarrier
};
if (_otcTrade.IsMoneynessOptionData)
{
result.KoBarrier *= _otcTrade.SpotPrice ?? 1.0;
}
return result;
}
/// <summary>
/// 获取票息支付日
/// </summary>
private static DateTime GetKoPaymentDate(string paymentDatesStr, int koDateIndex, DateTime koObservationDate)
{
var paymentDates = string.IsNullOrWhiteSpace(paymentDatesStr) ? null
: paymentDatesStr.Split(new char[] { ',', ';', '', '' }).Select(x => DateTime.Parse(x)).ToArray();
if (paymentDates != null && paymentDates.Length > koDateIndex)
{
var paymentDate = paymentDates[koDateIndex];
//预防错误的支付日数据
if (paymentDate < koObservationDate)
{
paymentDate = koObservationDate;
}
return paymentDate;
}
return koObservationDate;
}
/// <summary>
/// 是否需要观察敲入
/// </summary>
private bool IsNeedCheckKnockIn(DateTime observationDate)
{
//已在观察日之前敲入则不需要观察是否敲入
if (_snowball.KnockInOutStatus == ConsTrade.KnockState.KnockedIn && _snowball.KnockInOutDate < observationDate)
{
return false;
}
//仅在到期日观察
if (_snowball.KIObservationType == KIObservationType.OnlyEndDate)
{
return observationDate == _otcTrade.ExerciseDate;
}
//每日观察
return true;
}
class KoObservationParseResult
{
/// <summary>
/// 观察日在敲出观察频率中的索引
/// </summary>
public int KoDateIndex { get; set; }
/// <summary>
/// 敲出障碍价格
/// </summary>
public double KoBarrier { get; set; }
/// <summary>
/// 敲出支付票息率
/// </summary>
public double KoRebate { get; set; }
}
#endregion
}
}
@@ -0,0 +1,147 @@
using YLErp.Abstract.DataProviders;
using YLErp.Modules.DataProviderModule;
namespace YLErp.Modules.TradeModule
{
/// <summary>
/// 气囊结构服务
/// </summary>
public class TradeAirbagService : TradeServiceBase
{
public TradeAirbagService(OptUserInfo userInfo) : base(userInfo)
{
}
public TradeAirbagService(YLBaseService baseService) : base(baseService)
{
}
public void SetKnockIn(DateTime valueDate, IEodPriceProviderV2 priceProvider, DateTime? startDate = null
, Action<OtcTrade, trade_airbag> afterKnowInOut = null, System.Collections.Generic.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 airbag in DbContext.trade_airbag on trade.id equals airbag.TradeId
where trade.TradeDate > startDate.Value && trade.TradeDate <= valueDate
&& string.IsNullOrEmpty(airbag.KnockInOutStatus)
&& trade.TradeType == "气囊结构"
&& ConsTrade. == trade.TradeStatus
&& trade.ValidState != ConsGlobal.InValid
&& trade.DividendDate < valueDate
select new
{
trade,
tradeAirbag = airbag
};
#region tw
if (clienIds != null)
{
query = query.Where(l => clienIds.Contains(l.trade.ClientId));
}
#endregion
var trades = query.ToList();
if (trades == null || !trades.Any())
{
return;
}
var tradeIds = trades.Select(x => x.trade.id).ToArray();
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;
double? closePrice;
if (manuallyTradeObservationPrices.TryGetValue(tr.trade.id, out var manuallyTradeObservationPrice))
{
closePrice = manuallyTradeObservationPrice.Price;
}
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.tradeAirbag.KnockInOutStatus;
CheckAirbagKnockInStatus(tr.trade, tr.tradeAirbag, valueDate, closePrice);
if (oldKnockInOutStatus != tr.tradeAirbag.KnockInOutStatus)
{
var KnockInOutStatus = tr.tradeAirbag.KnockInOutStatus == ConsTrade.KnockState.KnockedIn ? "敲入" : "敲出";
AddTradeOperationHistoryAndSetParentTradeInfo(false, tr.trade, KnockInOutStatus, KnockInOutStatus);
}
if (tradeStatus != tr.trade.TradeStatus|| oldKnockInOutStatus != tr.tradeAirbag.KnockInOutStatus)
{
//删除E/Bod_Trade记录
RemoveEodTradeAndFutureInfo(false, tr.trade.id, valueDate);
}
if (afterKnowInOut != null && DbContext.Entry(tr.tradeAirbag).State == EntityState.Modified)
{
afterKnowInOut(tr.trade, tr.tradeAirbag);
}
}
//更新
DbContext.SaveChanges();
}
public void CheckAirbagKnockInStatus(OtcTradeBase td, trade_airbag tradeAirbag, DateTime valueDate, double? closePrice)
{
//气囊结构暂时都是每日连续观察,不用考虑观察周期问题
//气囊结构暂时都是向下敲入
if (closePrice <= (td.IsMoneynessOptionData ? tradeAirbag.Barrier * td.SpotPrice : tradeAirbag.Barrier))
{
tradeAirbag.KnockInOutStatus = ConsTrade.KnockState.KnockedIn;
tradeAirbag.KnockInOutDate = valueDate;
tradeAirbag.KnockInOutNotional = td.Notional;
}
}
/// <summary>
/// 设置敲入敲出状态
/// </summary>
public trade_airbag SaveKnockInOutStatus(int tradeId, DateTime? KnockInOutDate, string KnockInOutStatus)
{
var airbag = DbContext.trade_airbag.FirstOrDefault(x => x.TradeId == tradeId);
if (airbag != null)
{
airbag.KnockInOutDate = KnockInOutDate;
airbag.KnockInOutStatus = KnockInOutStatus;
airbag.OptId = UserId;
airbag.OptName = UserName;
airbag.OptDate = DateTime.Now;
DbContext.SaveChanges();
var td = DbContext.trade.Find(tradeId);
SaveTradeOperationHistory(td, airbag.KnockInOutStatusCn);
}
return airbag;
}
}
}
@@ -0,0 +1,199 @@
using Qdp.Foundation.Implementations;
using Qdp.Pricing.Base.Utilities;
using YLErp.Models;
using YLErp.Modules;
using YLErp.Modules.CalculationModule;
namespace YLErp.BLL
{
public class trade_asian_optionBLL
{
/// <summary>
/// 计算亚式期权Floating类型交易的浮动行权价
/// </summary>
public static double? GetAsianStrikePrice(DateTime valueDate, trade trade)
{
var baseReq = AsianOptionFixingService.GetRequest(valueDate, trade);
var strikeReq = new AsianOptionStrikeRequest(baseReq)
{
IsMoneynessOption = trade.IsMoneynessOptionData,
SpotPrice = trade.SpotPrice,
Strike = trade.Strike,
};
tradeBLL.SetFieldsByTradeType(trade);
return GetAsianStrikePrice(strikeReq, trade.trade_asian_option);
}
/// <summary>
/// 获取亚式期权行权价
/// </summary>
public static double? GetAsianStrikePrice(AsianOptionStrikeRequest request, trade_asian_option asianOption)
{
if (request is null)
{
return null;
}
if (asianOption is null)
{
using (var db = DbContextFactory.GetYLDbContext())
{
asianOption = request.TradeId > 0 ? db.trade_asian_option.AsNoTracking().FirstOrDefault(n => n.TradeId == request.TradeId) : null;
}
if (asianOption is null)
{
return null;
}
}
if ("Floating".Equals(asianOption.StrikeType, StringComparison.OrdinalIgnoreCase))
{
var fixings = AsianOptionFixingService.GetFixingString(request, asianOption);
if (string.IsNullOrWhiteSpace(fixings))
{
return null;
}
var fixingValues = fixings.Split(QdpConsts.Semilicon).Select(x =>
{
var splits = x.Split(QdpConsts.Comma);
return Tuple.Create(splits[0].ToDate(), double.Parse(splits[1]));
}).ToDictionary(x => x.Item1, x => x.Item2);
if ("GeometricAverage".Equals(asianOption.PayoffType))
{
var n = fixingValues.Count;
return Math.Pow(fixingValues.Select(x => x.Value).Aggregate(func: (result, item) => result * item), 1.0 / n);
}
else if ("ArithmeticAverage".Equals(asianOption.PayoffType)
|| "DiscreteArithmeticAverage".Equals(asianOption.PayoffType)
|| "EnhancedArithmeticAverage".Equals(asianOption.PayoffType))
{
return fixingValues.Select(x => x.Value).Average();
}
}
return request.IsMoneynessOption ? (request.SpotPrice * request.Strike) : request.Strike;
}
//TODO:除权除息
/// <summary>
/// 计算亚式期权Fix类型交易的浮动行权价
/// </summary>
public static double? GetAsianFinalPrice(trade trade, DateTime? valueDate = null)
{
if (trade is null)
{
return null;
}
if (valueDate == null)
{
valueDate = valuedateBLL.ValueDate;
}
tradeBLL.SetFieldsByTradeType(trade);
var asianOption = trade.trade_asian_option;
if (asianOption is null)
{
using (var db = DbContextFactory.GetYLDbContext())
{
trade.trade_asian_option = asianOption = db.trade_asian_option.AsNoTracking().FirstOrDefault(n => n.TradeId == trade.id);
}
if (asianOption is null)
{
return null;
}
}
if ("Fixed".Equals(asianOption.StrikeType, StringComparison.OrdinalIgnoreCase))
{
var fixings = AsianOptionFixingService.GetFixingString(valueDate.Value, trade);
if (string.IsNullOrWhiteSpace(fixings))
{
return null;
}
var fixingValues = fixings.Split(QdpConsts.Semilicon).Select(x =>
{
var splits = x.Split(QdpConsts.Comma);
return Tuple.Create(splits[0].ToDate(), double.Parse(splits[1]));
}).ToDictionary(x => x.Item1, x => x.Item2);
if ("GeometricAverage".Equals(asianOption.PayoffType))
{
var n = fixingValues.Count;
return Math.Pow(fixingValues.Select(x => x.Value).Aggregate(func: (result, item) => result * item), 1.0 / n);
}
else if ("ArithmeticAverage".Equals(asianOption.PayoffType)
|| "DiscreteArithmeticAverage".Equals(asianOption.PayoffType))
{
return fixingValues.Select(x => x.Value).Average();
}
}
return null;
}
/// <summary>
/// 获取亚式期权 均价起算日之后的均价 未到均价起算日则返回Null
/// </summary>
public static double? GetAsianAveragePrice(trade trade)
{
if (trade.trade_asian_option == null)
{
tradeBLL.SetFieldsByTradeType(trade);
}
if (null != trade.trade_asian_option)
{
var fixings = AsianOptionFixingService.GetFixingString(valuedateBLL.ValueDate, trade);
if (!string.IsNullOrWhiteSpace(fixings))
{
var fixingValues = string.IsNullOrEmpty(fixings)
? new Dictionary<Date, double>() :
fixings.Split(QdpConsts.Semilicon)
.Select(x =>
{
var splits = x.Split(QdpConsts.Comma);
return Tuple.Create(splits[0].ToDate(), double.Parse(splits[1]));
}).ToDictionary(x => x.Item1, x => x.Item2);
if ("GeometricAverage".Equals(trade.trade_asian_option.PayoffType))
{
var n = fixingValues.Count;
return Math.Pow(fixingValues.Select(x => x.Value).Aggregate(func: (result, item) => result * item), 1.0 / n);
}
else if ("ArithmeticAverage".Equals(trade.trade_asian_option.PayoffType) || "DiscreteArithmeticAverage".Equals(trade.trade_asian_option.PayoffType))
{
return fixingValues.Select(x => x.Value).Average();
}
}
}
return null;
}
}
public class AsianOptionStrikeRequest : AsianFixingRequest
{
public AsianOptionStrikeRequest(FixingRequestBase baseReq) : base(baseReq)
{
}
public AsianOptionStrikeRequest(DateTime valueDate, int tradeId, string instrumentType, string underlyingCode, DateTime exerciseDate, SettlementTypeEnum settlementType)
: base(valueDate, tradeId, instrumentType, underlyingCode, exerciseDate, settlementType)
{
}
public bool IsMoneynessOption { get; set; }
public double? Strike { get; set; }
public double? SpotPrice { get; set; }
}
}
@@ -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();
}
}
}
}
@@ -0,0 +1,534 @@
using Org.BouncyCastle.Ocsp;
using Qdp.Foundation.Utilities;
using YLErp.Commons;
using YLErp.CustomizedBizLogic;
using YLErp.DBModels.Consts;
using YLErp.DBModels.Enums;
using YLErp.DBModels.Helpers;
using YLErp.Modules.ClientModule;
using YLErp.Modules.TradeModule.DealModule;
using static NPOI.HSSF.Util.HSSFColor;
namespace YLErp.Modules.TradeModule.ExoticOptionModule
{
public class TradeCashServiceEx : TradeCashService
{
public TradeCashServiceEx(YLBaseService baseService) : base(baseService)
{
}
public TradeCashServiceEx(OptUserInfo userInfo) : base(userInfo)
{
}
public trade_cash SaveGroupUnwindCash(OtcTradeBase otcTrade, DateTime valueDate, double paymentAmount, double closePrice, out bool continueTradeCashHandle)
{
var childrenTrades = DbContext.trade.Where(x => x.ParentTradeId == otcTrade.ParentTradeId && x.ValidState != "InValid");
//子交易观察调用该方法处理主交易时,当前子交易会了结,但还没存数据库,所以只需要判断其他子交易是否已了结即可
var isAllCompleted = childrenTrades.Where(x => x.id != otcTrade.id).All(x => ConsTrade.TradeCompleteStatus.Contains(x.TradeStatus));
var parentTrade = DbContext.trade.Find(otcTrade.ParentTradeId);
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(parentTrade.UnderlyingCode);
var tradeCashIds = DbContext.trade_cash.Where(x => x.TradeId == otcTrade.ParentTradeId && x.ValueDate == valueDate).Select(x => x.id);
var tradeCashGroupAction = DbContext.trade_cash_group_action.FirstOrDefault(x => tradeCashIds.Contains(x.ParentTradeCashId) && x.IsEodSettle);
var tradeCash = new trade_cash();
if (tradeCashGroupAction != null)
{
tradeCash = DbContext.trade_cash.Find(tradeCashGroupAction.ParentTradeCashId);
var action = DbContext.trade_cash_group_action.FirstOrDefault(x => x.ParentTradeCashId == tradeCashGroupAction.ParentTradeCashId && x.TradeId == otcTrade.id);
if (action != null)
{
if (!action.IsFinishedUnwindPercent)
{
tradeCash.Amount += paymentAmount;
tradeCash.OptId = UserId;
tradeCash.OptName = UserName;
tradeCash.OptDate = DateTime.Now;
if (isAllCompleted)
{
tradeCash.IsLastAction = true;
}
action.IsFinishedUnwindPercent = true;
}
else
{
continueTradeCashHandle = false;
return tradeCash;
}
}
else
{
tradeCash.Amount += paymentAmount;
tradeCash.OptId = UserId;
tradeCash.OptName = UserName;
tradeCash.OptDate = DateTime.Now;
if (isAllCompleted)
{
tradeCash.IsLastAction = true;
tradeCash.UnwindNotional = tradeCash.Notional;
tradeCash.UnwindTradeAmount = tradeCash.TradeAmount;
tradeCash.UnwindPercentRate = 1;
}
tradeCashGroupAction = new trade_cash_group_action()
{
IsEodSettle = true,
TradeId = otcTrade.id,
ParentTradeId = otcTrade.ParentTradeId,
ParentTradeCashId = tradeCash.id,
Status = "已完成",
OptId = UserId,
OptName = UserName,
OptDate = DateTime.Now,
IsFinishedUnwindPercent = true
};
DbContext.trade_cash_group_action.Add(tradeCashGroupAction);
}
}
else
{
tradeCash = new trade_cash()
{
Action = ClientCashInCashOut._票息,
IsLastAction = false,
TradeType = parentTrade.BuySell,
Notional = parentTrade.Notional,
TradeAmount = parentTrade.TradeAmount,
TradeId = otcTrade.ParentTradeId,
HappenedDate = valueDate,
ValueDate = valueDate,
FinalPrice = closePrice,
Status = TradeCashStatusEnum.,
OptId = UserId,
OptName = UserName,
OptDate = DateTime.Now
};
tradeCash.Amount += paymentAmount;
if (isAllCompleted)
{
tradeCash.IsLastAction = true;
tradeCash.UnwindNotional = tradeCash.Notional;
tradeCash.UnwindTradeAmount = tradeCash.TradeAmount;
tradeCash.UnwindPercentRate = 1;
}
DbContext.trade_cash.Add(tradeCash);
DbContext.SaveChanges();
#region---ClientCashInCashOut---
var cl = ClientDataQueryService.GetClient(parentTrade.ClientId, true);
var ee = new ClientCashInCashOut
{
Direction = "应收",
Number = UniqueTimeId.GetStr(),
ClientId = cl.id,
ClientNumber = cl.Number,
ClientName = cl.Name,
HappenDate = tradeCash.ValueDate,
State = ClientCashInCashOut.,
OptId = tradeCash.OptId,
OptName = tradeCash.OptName,
OptDate = tradeCash.OptDate,
CreatorId = tradeCash.OptId,
CreatorName = tradeCash.OptName,
CreateDate = tradeCash.OptDate,
TradeId = tradeCash.TradeId,
TradeCashId = tradeCash.id,
Action = tradeCash.Action,
TradeNumber = tradeCash.TradeNumber,
IsGroup = parentTrade.IsGroup
};
DbContext.ClientCashInCashOut.Add(ee);
#endregion
tradeCashGroupAction = new trade_cash_group_action()
{
IsEodSettle = true,
TradeId = otcTrade.id,
ParentTradeId = otcTrade.ParentTradeId,
ParentTradeCashId = tradeCash.id,
Status = "已完成",
OptId = UserId,
OptName = UserName,
OptDate = DateTime.Now,
IsFinishedUnwindPercent = true
};
DbContext.trade_cash_group_action.Add(tradeCashGroupAction);
}
if (isAllCompleted)
{
parentTrade.UnWindNotional = parentTrade.Notional;
parentTrade.UnWindDate = tradeCash.ValueDate;
parentTrade.FinalPrice = tradeCash.FinalPrice;
}
parentTrade.OptId = UserId;
parentTrade.OptName = UserName;
parentTrade.OptDate = DateTime.Now;
DbContext.SaveChanges();
if (BLL.valuedateBLL.SystemDate.CloseReApprove != 1 || !HasTradeProcess())
{
if (PS.Config.SalesCommissionCalculation == "公式1")
{
//销售提成
new SalesModule.SalesCommissionDetailDataService(UserInfo).CalcuSalesCommissionDetail(tradeCash);
}
if (PS.Config.Company == Configuration.CompanyEnum.)
{
new BizLogicZhaoZheng().GenerateZhaoZhengDealNumber(parentTrade, tradeCash);
}
if (PS.Config.Company == Configuration.CompanyEnum.)
{
new BizLogicWCZD().GenerateWCZDNumber(DbContext, parentTrade, tradeCash.ValueDate, tradeCash.id);
}
}
if (tradeCash.IsLastAction)
{
var tradeIds = DbContext.trade_cash_group_action.Where(x => x.ParentTradeCashId == tradeCash.id).Select(x => x.TradeId).ToList();
var tradeStatusList = DbContext.trade.Where(x => tradeIds.Contains(x.id)).Select(x => x.TradeStatus).ToList();
if (tradeStatusList.Contains(ConsTrade.))
{
parentTrade.TradeStatus = ConsTrade.;
}
else if (tradeStatusList.Contains(ConsTrade.))
{
parentTrade.TradeStatus = ConsTrade.;
}
else
{
parentTrade.TradeStatus = ConsTrade.;
}
}
DbContext.SaveChanges();
continueTradeCashHandle = true;
return tradeCash;
}
public int SaveGroupCouponCash(OtcTradeBase trade, DateTime happenDate, double paymentAmount, double underlyingPrice)
{
var parentTradeCashId = 0;
var childrenTrades = DbContext.trade.Where(x => x.ParentTradeId == trade.ParentTradeId && x.ValidState != "InValid");
var parentTrade = DbContext.trade.Find(trade.ParentTradeId);
var tradeCashIds = DbContext.trade_cash.Where(x => x.TradeId == trade.ParentTradeId && x.ValueDate == happenDate).Select(x => x.id);
var tradeCashGroupAction = DbContext.trade_cash_group_action.FirstOrDefault(x => tradeCashIds.Contains(x.ParentTradeCashId) && x.IsEodSettle);
var tradeCash = new trade_cash();
if (tradeCashGroupAction != null)
{
parentTradeCashId = tradeCashGroupAction.ParentTradeCashId;
if (DbContext.trade_cash_group_action.Any(x => x.ParentTradeCashId == tradeCashGroupAction.ParentTradeCashId && x.TradeId == trade.id))
{
return parentTradeCashId;
}
else
{
tradeCash = DbContext.trade_cash.Find(tradeCashGroupAction.ParentTradeCashId);
tradeCash.Amount += paymentAmount;
tradeCash.OptId = UserId;
tradeCash.OptName = UserName;
tradeCash.OptDate = DateTime.Now;
tradeCashGroupAction = new trade_cash_group_action()
{
IsEodSettle = true,
TradeId = trade.id,
ParentTradeId = trade.ParentTradeId,
ParentTradeCashId = tradeCash.id,
Status = "已完成",
OptId = UserId,
OptName = UserName,
OptDate = DateTime.Now,
IsFinishedUnwindPercent = false
};
DbContext.trade_cash_group_action.Add(tradeCashGroupAction);
}
}
else
{
tradeCash = new trade_cash()
{
Action = ClientCashInCashOut._票息,
IsLastAction = false,
TradeType = parentTrade.BuySell,
Notional = parentTrade.Notional,
TradeAmount = parentTrade.TradeAmount,
Amount = paymentAmount,
TradeId = trade.ParentTradeId,
HappenedDate = happenDate,
ValueDate = happenDate,
FinalPrice = underlyingPrice,
Status = TradeCashStatusEnum.,
OptId = UserId,
OptName = UserName,
OptDate = DateTime.Now
};
DbContext.trade_cash.Add(tradeCash);
DbContext.SaveChanges();
parentTradeCashId = tradeCash.id;
tradeCashGroupAction = new trade_cash_group_action()
{
IsEodSettle = true,
TradeId = trade.id,
ParentTradeId = trade.ParentTradeId,
ParentTradeCashId = tradeCash.id,
Status = "已完成",
OptId = UserId,
OptName = UserName,
OptDate = DateTime.Now,
IsFinishedUnwindPercent = false
};
DbContext.trade_cash_group_action.Add(tradeCashGroupAction);
DbContext.SaveChanges();
#region---ClientCashInCashOut---
var cl = ClientDataQueryService.GetClient(parentTrade.ClientId, true);
var ee = new ClientCashInCashOut
{
Direction = "应收",
Number = UniqueTimeId.GetStr(),
ClientId = cl.id,
ClientNumber = cl.Number,
ClientName = cl.Name,
HappenDate = tradeCash.ValueDate,
State = ClientCashInCashOut.,
OptId = tradeCash.OptId,
OptName = tradeCash.OptName,
OptDate = tradeCash.OptDate,
CreatorId = tradeCash.OptId,
CreatorName = tradeCash.OptName,
CreateDate = tradeCash.OptDate,
TradeId = tradeCash.TradeId,
TradeCashId = tradeCash.id,
Action = tradeCash.Action,
TradeNumber = tradeCash.TradeNumber,
IsGroup = parentTrade.IsGroup
};
DbContext.ClientCashInCashOut.Add(ee);
#endregion
}
parentTrade.OptId = UserId;
parentTrade.OptName = UserName;
parentTrade.OptDate = DateTime.Now;
DbContext.SaveChanges();
if (BLL.valuedateBLL.SystemDate.CloseReApprove != 1 || !HasTradeProcess())
{
if (PS.Config.SalesCommissionCalculation == "公式1")
{
//销售提成
new SalesModule.SalesCommissionDetailDataService(UserInfo).CalcuSalesCommissionDetail(tradeCash);
}
if (PS.Config.Company == Configuration.CompanyEnum.)
{
new BizLogicZhaoZheng().GenerateZhaoZhengDealNumber(parentTrade, tradeCash);
}
if (PS.Config.Company == Configuration.CompanyEnum.)
{
new BizLogicWCZD().GenerateWCZDNumber(DbContext, parentTrade, tradeCash.ValueDate, tradeCash.id);
}
}
return parentTradeCashId;
}
//如果两个票息的结算日设为同一天的情况下,用这个方法保存票息有问题
public trade_cash SaveCash(OtcTradeBase trade, string cashAction, string exerciseWay, double paymentAmount, DateTime valueDate, double underlyingPrice, DateTime happenDate, bool isKnockOut = false, bool isLastAction = false, int parentTradeId = 0, int parentTradeCashId = 0)
{
var notional = trade.Notional;
var tradeAmount = trade.TradeAmount;
// 保存trade_cash
var tc = DbContext.trade_cash.FirstOrDefault(t => t.TradeId == trade.id && t.Action == cashAction && !t.IsDeleted && t.HappenedDate == happenDate);
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(trade.UnderlyingCode);
//暂时只应用于累计期权的观察部分行权处理
trade_accumulator_option accumulator = new trade_accumulator_option();
if (trade.TradeType == "累计期权")
{
accumulator = DbContext.trade_accumulator_option.FirstOrDefault(x => x.TradeId == trade.id);
tc = DbContext.trade_cash.FirstOrDefault(t => t.TradeId == trade.id && t.Action == "系统操作-平仓费" && t.UnwindType == "部分行权" && !t.IsDeleted && t.HappenedDate == happenDate);
if (tc == null)
{
trade.TradeAmount -= accumulator.AccumuTradeAmount;
trade.Notional = trade.TradeAmount * underlying.CountRatio;
trade.StockEqvNotional = TradeHelper.GetStockEqvNotional(trade.Notional * trade.SpotPrice, trade.ParticipationRate, trade.AnnualizeFactor);
}
}
if (tc == null)
{
tc = new trade_cash();
DbContext.trade_cash.Add(tc);
tc.Notional = notional;
tc.TradeAmount = tradeAmount;
}
else
{
if (paymentAmount == 0)
{
//如果是最后一笔资金记录但是查出来的tc没有设置最后一笔资金标识则重新设置
if (isLastAction && !tc.IsLastAction)
{
tc.IsLastAction = true;
tc.UnwindNotional = trade.Notional;
tc.UnwindTradeAmount = trade.TradeAmount;
tc.UnwindPercentRate = trade.Notional / trade.OriginalNotional;
DbContext.SaveChanges();
}
return tc;
}
if (trade.TradeType != "累计期权")
{
tc.Notional = notional;
tc.TradeAmount = tradeAmount;
}
}
tc.ValidState = null;
tc.OptId = UserId;
tc.OptName = UserName;
tc.OptDate = DateTime.Now;
tc.ExceciseType = "现金";
tc.TradeType = trade.BuySell;
tc.CallPut = trade.CallPut;
tc.Amount = paymentAmount;
tc.TradeId = trade.id;
tc.ParentTradeId = parentTradeId;
tc.ParentTradeCashId = parentTradeCashId;
tc.HappenedDate = happenDate;
if (trade.TradeType == "累计期权" && (cashAction == "部分行权" || cashAction == "到期行权"))
{
if (cashAction == "部分行权")
{
tc.Action = ClientCashInCashOut._平仓费;
tc.UnwindType = "部分行权";
tc.ExerciseWay = TradeCashExerciseWayEnum.;
}
else if (cashAction == "到期行权")
{
tc.Action = ClientCashInCashOut._行权费;
tc.UnwindType = "到期";
tc.ExerciseWay = TradeCashExerciseWayEnum.;
}
tc.UnwindTradeAmount = accumulator.AccumuTradeAmount;
tc.UnwindNotional = accumulator.AccumuTradeAmount * underlying.CountRatio;
tc.UnwindPrice = Math.Abs(accumulator.AccumuTradeAmount != 0 ? paymentAmount / accumulator.AccumuTradeAmount : 0);
tc.UnwindPricePercentRate = Math.Abs(accumulator.AccumuTradeAmount != 0 && trade.SpotPrice != null && trade.SpotPrice != 0 ? paymentAmount / accumulator.AccumuTradeAmount / trade.SpotPrice.Value : 0);
tc.UnwindPercentRate = accumulator.AccumuTradeAmount * underlying.CountRatio / trade.OriginalNotional;
tc.NotionalPercentRate = tc.UnwindPercentRate;
}
else
{
tc.Action = cashAction;
tc.ExerciseWay = exerciseWay;
tc.UnwindPrice = Math.Abs(trade.Notional != 0 ? paymentAmount / trade.Notional : 0);
tc.UnwindPricePercentRate = Math.Abs(trade.Notional != 0 && trade.SpotPrice != null && trade.SpotPrice != 0 ? paymentAmount / trade.Notional / trade.SpotPrice.Value : 0);
tc.NotionalPercentRate = trade.Notional / trade.OriginalNotional;
}
tc.IsLastAction = isLastAction;
if (exerciseWay == "到期行权")
{
tc.UnwindType = "到期";
if (cashAction == ClientCashInCashOut._票息 && tc.Amount == 0)
{
tc.Action = ClientCashInCashOut._行权费;
}
}
tc.Status = TradeCashStatusEnum.;
tc.ValueDate = valueDate;
tc.FinalPrice = underlyingPrice;
if (isKnockOut)
{
tc.BarrierPrice = trade.IsMoneynessOptionData && trade.SpotPrice != 0 ? (underlyingPrice / trade.SpotPrice) : underlyingPrice;
}
if (isLastAction && !(cashAction == "部分行权" && trade.TradeType == "累计期权"))
{
tc.UnwindNotional = notional;
tc.UnwindTradeAmount = tradeAmount;
tc.UnwindPercentRate = notional / trade.OriginalNotional;
}
tc.NotionalPercentRate = trade.Notional / trade.OriginalNotional;
DbContext.SaveChanges();
var tradeCashDetails = DbContext.trade_cash_detail.Where(x => x.TradeCashId == tc.id);
DbContext.trade_cash_detail.RemoveRange(tradeCashDetails);
SaveTradeCashDetail(tc);
if ((BLL.valuedateBLL.SystemDate.CloseReApprove != 1 || !HasTradeProcess()) || isKnockOut || isLastAction)
{
if (PS.Config.SalesCommissionCalculation == "公式1")
{
//销售提成
new SalesModule.SalesCommissionDetailDataService(UserInfo).CalcuSalesCommissionDetail(tc);
}
if (PS.Config.Company == Configuration.CompanyEnum.)
{
new BizLogicZhaoZheng().GenerateZhaoZhengDealNumber(trade, tc);
}
if (PS.Config.Company == Configuration.CompanyEnum. && cashAction!=ClientCashInCashOut._期权费)
{
new BizLogicWCZD().GenerateWCZDNumber(DbContext, trade, tc.ValueDate, tc.id);
}
}
// 保存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.TradeCashId == tc.id);
if (cashInOutRecord == null)
{
cashInOutRecord = new ClientCashInCashOut();
cashInOutRecord.CreateDate = tc.OptDate;
cashInOutRecord.CreatorId = tc.OptId;
cashInOutRecord.CreatorName = tc.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 = -tc.Amount;
cashInOutRecord.HappenDate = valueDate;
cashInOutRecord.State = ClientCashInCashOut.;
cashInOutRecord.OptDate = tc.OptDate;
cashInOutRecord.OptId = tc.OptId;
cashInOutRecord.OptName = tc.OptName;
cashInOutRecord.TradeId = trade.id;
cashInOutRecord.TradeCashId = tc.id;
cashInOutRecord.Action = tc.Action;
cashInOutRecord.TradeNumber = trade.TradeNumber;
cashInOutRecord.IsGroup = trade.IsGroup;
DbContext.SaveChanges();
return tc;
}
}
}
@@ -0,0 +1,271 @@
using Qdp.Foundation.Implementations;
using Qdp.Pricing.Base.Implementations;
using YLErp.Abstract.DataProviders;
using YLErp.Modules.DataProviderModule;
using YLErp.Modules.SalesModule;
using YLErp.Modules.TradeModule.DealModule;
using YLErp.QdpModule;
namespace YLErp.Modules.TradeModule
{
/// <summary>
///
/// </summary>
public class TradeRangeAccrualService : ExoticOptionModule.TradeCashServiceEx
{
public TradeRangeAccrualService(OptUserInfo userInfo) : base(userInfo)
{
}
public TradeRangeAccrualService(YLBaseService baseService) : base(baseService)
{
}
public void CheckStatus(DateTime valueDate, IEodPriceProviderV2 priceProvider, DateTime? startDate = null
, Action<OtcTrade, trade_rangeaccrual> 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 rangeaccrual in DbContext.trade_rangeaccrual on trade.id equals rangeaccrual.TradeId
where trade.TradeDate > startDate.Value && trade.TradeDate <= valueDate && trade.ExerciseDate >= valueDate
&& (trade.TradeType == "区间累积期权")
&& ConsTrade. == trade.TradeStatus
&& trade.ValidState != ConsGlobal.InValid
&& trade.DividendDate < valueDate
select new
{
trade,
tradeRange = rangeaccrual
};
#region tw
if (clienIds != null)
{
query = query.Where(l => clienIds.Contains(l.trade.ClientId));
}
#endregion
var trades = query.ToList();
if (trades == null || !trades.Any())
{
return;
}
var tradeIds = trades.Select(x => x.trade.id).ToArray();
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;
double closePrice;
if (manuallyTradeObservationPrices.TryGetValue(tr.trade.id, out var manuallyTradeObservationPrice))
{
closePrice = manuallyTradeObservationPrice.Price ?? 0;
}
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;
}
CheckRangeAccrualBonus(tr.trade, tr.tradeRange, valueDate, closePrice);
if (tradeStatus != tr.trade.TradeStatus)
{
//删除E/Bod_Trade记录
RemoveEodTradeAndFutureInfo(false, tr.trade.id, valueDate);
}
if (afterKnowInOut != null && DbContext.Entry(tr.tradeRange).State == EntityState.Modified)
{
afterKnowInOut(tr.trade, tr.tradeRange);
}
}
DbContext.SaveChanges();
}
public void CheckRangeAccrualBonus(OtcTradeBase trade, trade_rangeaccrual tradeRange, DateTime valueDate, double closePrice)
{
var tcQuery = from x in DbContext.trade_cash
where x.TradeId == trade.id && x.ValidState != ConsGlobal.InValid
&& !x.IsDeleted && x.Action == "系统操作-平仓费"
&& x.ValueDate > valueDate && (x.ConfirmDate > valueDate || x.ConfirmDate == DateTime.MinValue)
&& x.UnwindNotional < x.Notional
select x;
var notional = (ConsTrade.TradeCompleteStatus.Contains(trade.TradeStatus) && trade.UnWindDate <= valueDate
? 0 : trade.Notional) + (tcQuery.Sum(x => x.UnwindNotional) ?? 0);
if (CheckRangeAccrualBonus(trade, tradeRange, valueDate, closePrice, notional, out var couponCash))
{
SaveObservation(trade, tradeRange, valueDate, couponCash, notional);
//这里要保存,否则下面查询的时候,查不到最后一天的票息记录.
DbContext.SaveChanges();
}
//到期日生成票息资金记录
if (valueDate.Date == trade.ExerciseDate.Value.Date)
{
trade.UnWindDate = valueDate.Date;
trade.TradeStatus = ConsTrade.;
var happenedObservations = DbContext.autocall_observation.Where(o => o.TradeId == trade.id).ToList();
var totalPaymentAmount = happenedObservations?.Sum(x => x.PaymentAmount) ?? 0;
totalPaymentAmount += trade.PrincipalSum() * (trade.BuySell == "卖出" ? -1 : 1);
var parentTradeId = 0;
var parentTradeCashId = 0;
if (trade.IsGroup == 2 && trade.ParentTradeId > 0)
{
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;
parentTradeCashId = SaveGroupUnwindCash(trade, valueDate, totalPaymentAmount, closePrice, out bool continueTradeCashHandle).id;
}
}
SaveCash(trade, ClientCashInCashOut._票息, null, totalPaymentAmount, valueDate, closePrice, valueDate, isLastAction: true, parentTradeId: parentTradeId, parentTradeCashId: parentTradeCashId);
//生成确认书
if (PS.Config.IsAutoGenerateContracts && ConsTrade.TradeCompleteStatus.Contains(trade.TradeStatus))
{
//修改销售提成的状态
new SalesCommissionDataService(this).SetCommissionVaild(trade.id);
new TradeContractGenerateService(this).GenerateContractsAsync(new List<int> { trade.id });
}
}
}
public static bool CheckRangeAccrualBonus(OtcTradeBase trade, trade_rangeaccrual tradeRange, DateTime valueDate, double closePrice, double notional, out double couponCash)
{
couponCash = 0;
if (trade is null || tradeRange is null)
{
return false;
}
var observDates = QdpHelper.ParseObservationDate(tradeRange.ObservationDates)?.ToArray();
if (observDates == null)
{
observDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(trade.TradeDate.Value, trade.ExerciseDate.Value).ToArray();
}
if (observDates != null && observDates.Contains(new Date(valueDate)))
{
var upperRange = trade.IsMoneynessOptionData ? tradeRange.UpperRange * trade.SpotPrice.Value : tradeRange.UpperRange;
var lowerRange = trade.IsMoneynessOptionData ? tradeRange.LowerRange * trade.SpotPrice.Value : tradeRange.LowerRange;
//有区间收益
if (closePrice < upperRange && closePrice > lowerRange)
{
couponCash = tradeRange.BonusRate * notional * (trade.SpotPrice ?? 0) / observDates.Length * (trade.BuySell == "卖出" ? -1 : 1);
return true;
}
}
return false;
}
public autocall_observation SaveObservation(OtcTradeBase trade, trade_rangeaccrual rangeaccrual, DateTime valueDate, double couponCash, double notional, bool saveChanges = true)
{
var observationRecord = DbContext.autocall_observation.FirstOrDefault(o => o.TradeId == trade.id && o.EndDate == valueDate.Date);
if (observationRecord == null)
{
observationRecord = new autocall_observation()
{
TradeId = trade.id,
StartDate = valueDate.Date,
EndDate = valueDate.Date,
CouponRate = rangeaccrual.BonusRate,
StockEqvNotional = notional * (trade.SpotPrice ?? 0),
PaymentAmount = couponCash,
PaymentDate = trade.ExerciseDate.Value.Date
};
if (saveChanges)
{
DbContext.autocall_observation.Add(observationRecord);
}
}
else
{
observationRecord.StartDate = valueDate.Date;
observationRecord.EndDate = valueDate.Date;
observationRecord.CouponRate = rangeaccrual.BonusRate;
observationRecord.StockEqvNotional = notional * (trade.SpotPrice ?? 0);
observationRecord.PaymentAmount = couponCash;
observationRecord.PaymentDate = trade.ExerciseDate.Value.Date;
}
return observationRecord;
}
public double GetRangeCoupon(int tradeId, DateTime valueDate, double price)
{
var trade = DbContext.trade.AsNoTracking().FirstOrDefault(t => t.id == tradeId);
var tradeRangeAccrual = DbContext.trade_rangeaccrual.AsNoTracking().FirstOrDefault(t => t.TradeId == tradeId);
return GetRangeCoupon(trade, tradeRangeAccrual, valueDate, price);
}
public double GetRangeCoupon(OtcTradeBase trade, trade_rangeaccrual tradeRangeAccrual, DateTime valueDate, double price)
{
if (trade == null || tradeRangeAccrual == null)
{
return 0.0;
}
var tradeCashs = DbContext.trade_cash.AsNoTracking().Where(x => x.ValidState != ConsGlobal.InValid && !x.IsDeleted && x.TradeId == trade.id && x.Action == "系统操作-平仓费" && (x.ValueDate > valueDate && (x.ConfirmDate > valueDate || x.ConfirmDate == DateTime.MinValue)) && x.UnwindNotional < x.Notional).ToList();
var notional = (ConsTrade.TradeCompleteStatus.Contains(trade.TradeStatus) && trade.UnWindDate <= valueDate ? 0 : trade.Notional) + tradeCashs.Sum(x => x.UnwindNotional).Value;
var observDates = QdpHelper.ParseObservationDate(tradeRangeAccrual.ObservationDates)?.ToArray();
if (observDates == null)
{
observDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(
new Date(trade.TradeDate.Value), new Date(trade.ExerciseDate.Value)).ToArray();
}
var upperRange = trade.IsMoneynessOptionData ? tradeRangeAccrual.UpperRange * (trade.SpotPrice ?? 0) : tradeRangeAccrual.UpperRange;
var lowerRange = trade.IsMoneynessOptionData ? tradeRangeAccrual.LowerRange * (trade.SpotPrice ?? 0) : tradeRangeAccrual.LowerRange;
if (price < upperRange && price > lowerRange)
{
return (observDates != null && observDates.Length > 0) ?
tradeRangeAccrual.BonusRate * notional * (trade.SpotPrice ?? 0) / observDates.Length :
tradeRangeAccrual.BonusRate * notional * (trade.SpotPrice ?? 0);
}
else
{
return 0.0;
}
}
}
}
@@ -0,0 +1,789 @@
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);
}
}
}
@@ -0,0 +1,129 @@
using NPOI.SS.Formula.Functions;
using Qdp.Foundation.Implementations;
using Qdp.Pricing.Library.Options.Products.Autocall.Snowball;
using YLErp.Modules.CalculationModule;
using YLErp.Modules.TradeModule.ExoticOptionModule;
namespace YLErp.Modules.TradeModule
{
public class TradeSnowballService : YLBaseService
{
public TradeSnowballService(OptUserInfo userInfo) : base(userInfo)
{
}
public TradeSnowballService(YLBaseService baseService) : base(baseService)
{
}
public List<autocall_observation> GetObservations(int tradeId, DateTime valueDate)
{
var trade = DbContext.trade.FirstOrDefault(t => t.id == tradeId);
var tradeSnowball = DbContext.trade_snowball.FirstOrDefault(t => t.TradeId == tradeId);
if (trade == null || tradeSnowball == null)
{
return null;
}
// 如果是敲出转期权,则不支付票息
if (tradeSnowball.KOPayoffType != KOPayoffTypeEnum.Rebate)
{
return null;
}
if (trade.TradeStatus == ConsTrade. || trade.TradeStatus == ConsTrade. || trade.TradeStatus.Contains("待确认"))
{
return null;
}
var optionTrade = QdpTradeBuilder.GetSnowballOptionTrade(trade, tradeSnowball);
if (optionTrade == null)
{
return null;
}
var snowball = (SimpleSnowball)optionTrade.Instrument;
var observationEndDate =
tradeSnowball.KnockInOutStatus == ConsTrade.KnockState.KnockedOut ?
tradeSnowball.KnockInOutDate.Value :
valueDate;
var qdpEndDate = new Date(observationEndDate);
qdpEndDate = snowball.KOObsDates.Where(d => d <= qdpEndDate).Max();
var observation = snowball.GetEffectiveObservation(qdpEndDate, tradeSnowball.CouponIncludeStartDate ?? false);
if (tradeSnowball.PrepaymentUsed)
{
var payoff = new SpecialSnowballObservationHelper(trade, tradeSnowball).GetEffectiveObservation(qdpEndDate, optionTrade.Notional);
observation = payoff == null ? null : new Qdp.Pricing.Library.Options.Products.Autocall.Phoenix.ObservationPayment
{
CouponRate = payoff.CouponRate,
EndDate = payoff.CouponEndDate,
PaymentAmount = payoff.CouponPaymentAmount,
PaymentDate = qdpEndDate,
StartDate = payoff.CouponStartDate,
Notional = Math.Abs(optionTrade.Notional * (trade.SpotPrice ?? 0))
};
}
if (observation == null)
{
return null;
}
else
{
var tradeCashCoupon = DbContext.trade_cash.FirstOrDefault(t => t.TradeId == tradeId && t.Action == "系统操作-票息" && t.ValidState != "InValid" && !t.IsDeleted);
var tradeCashDetial = tradeCashCoupon != null ? DbContext.trade_cash_detail.FirstOrDefault(x => x.TradeCashId == tradeCashCoupon.id && x.Action == "系统操作-期权费") : null;
return new List<autocall_observation>()
{
new autocall_observation {
StartDate = observation.StartDate.DateTime,
EndDate = observation.EndDate.DateTime,
CouponRate = observation.CouponRate,
StockEqvNotional = Math.Abs(observation.Notional),
PaymentAmount = observation.PaymentAmount,
AnnualizedPremiumRate = tradeSnowball.AnnualizedPremiumRate,
AnnualizedTradePrice = tradeCashDetial?.Amount ?? 0,
PaymentDate =
tradeSnowball.KnockInOutStatus == ConsTrade.KnockState.KnockedOut ?
tradeCashCoupon?.ValueDate ?? observationEndDate:
observation.PaymentDate.DateTime
}
};
}
}
public double GetKOOptionPayoff(int tradeId, DateTime valueDate, double price)
{
var trade = DbContext.trade.AsNoTracking().FirstOrDefault(t => t.id == tradeId);
var tradeSnowball = DbContext.trade_snowball.AsNoTracking().FirstOrDefault(t => t.TradeId == tradeId);
return GetKOOptionPayoff(trade, tradeSnowball, valueDate, price);
}
public double GetKOOptionPayoff(OtcTradeBase trade, trade_snowball tradeSnowball, DateTime valueDate, double price)
{
if (trade == null || tradeSnowball == null)
{
return 0.0;
}
if (tradeSnowball.KOPayoffType == KOPayoffTypeEnum.Rebate)
{
return 0.0;
}
var optionTrade = QdpTradeBuilder.GetSnowballOptionTrade(trade, tradeSnowball);
var snowball = (SimpleSnowball)optionTrade.Instrument;
var payoffs = snowball.GetKOPayoff(new Date(valueDate), price);
if (payoffs != null && payoffs.Length > 0)
{
return payoffs[0].PaymentAmount;
}
else
{
return 0.0;
}
}
}
}
@@ -0,0 +1,151 @@
using YLErp.BLL;
using YLErp.CustomizedBizLogic;
using YLErp.DBModels.Consts;
using YLErp.Enums;
using YLErp.Modules.EodModule;
using YLErp.Modules.TradeModule.ExoticOptionModule;
namespace YLErp.Modules.TradeModule.DealModule
{
/// <summary>
/// 二元期权操作
/// 迁移自:trade_binary_optionBLL
/// </summary>
public class TradeSwapService : TradeCashServiceEx
{
public TradeSwapService(YLBaseService baseService) : base(baseService)
{
}
public TradeSwapService(OptUserInfo userInfo) : base(userInfo)
{
}
/// <summary>
///
/// </summary>
public void HandleSwapTradeCashPre(DateTime valueDate, System.Collections.Generic.IEnumerable<int> clienIds)
{
#region #region tw
var tradeCashPres = DbContext.trade_cash_pre.Where(x => x.ValueDate == valueDate && x.ValidState != "InValid" && !x.IsFinished).ToList();
var trades = new List<trade>();
var tradeIds = new List<int>();
if (clienIds != null)
{
trades = DbContext.trade.Where(l => clienIds.Contains(l.ClientId)).ToList();
tradeIds = trades.Select(l => l.id).ToList();
tradeCashPres = tradeCashPres.Where(l => tradeIds.Contains(l.TradeId)).ToList();
}
else
{
tradeIds = tradeCashPres.Select(x => x.TradeId).ToList();
trades = DbContext.trade.Where(x => tradeIds.Contains(x.id)).ToList();
tradeIds = trades.Select(x => x.id).ToList();
tradeCashPres = tradeCashPres.Where(l => tradeIds.Contains(l.TradeId)).ToList();
}
#endregion
tradeCashPres.ForEach(x =>
{
var trade = trades.FirstOrDefault(y => y.id == x.TradeId);
if (trade.ExerciseDate < valueDate)
{
return;//已到期交易不再观察;
}
var client = DataCacheProvider.GetClientDataSource().GetData(trade.ClientId);
var currencyRate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(trade.QuoteCurrency, trade.SettlementCurrency, x.ValueDate);
var currencyRateTradeDate = new EodCurrencyRateService(UserInfo).GetCurrencyRate(trade.QuoteCurrency, trade.SettlementCurrency, trade.TradeDate.Value);
var tradeCashDetails = DbContext.trade_cash_detail.Where(y => y.TradeCashPreId == x.id).ToList();
var costFeeGet = 0.0;
var costTradePriceGet = 0.0;
var coupon = 0.0;
var winloss = 0.0;
tradeCashDetails.ForEach(y =>
{
if (y.TradeCashType == TradeCashTypeEnum..ToString())
{
y.Amount = y.QuoteAmount * (PS.Config.Company == Configuration.CompanyEnum. && client.BoundSide == BoundSideEnum. ? currencyRateTradeDate : currencyRate);
}
else
{
y.Amount = y.QuoteAmount * currencyRate;
}
if (y.TradeCashType == TradeCashTypeEnum..ToString())
{
costTradePriceGet = y.Amount ?? 0;
}
else if (y.TradeCashType == TradeCashTypeEnum..ToString())
{
costFeeGet = y.Amount ?? 0;
}
else if (y.TradeCashType == TradeCashTypeEnum..ToString())
{
coupon = y.Amount ?? 0;
}
else if (y.TradeCashType == TradeCashTypeEnum..ToString())
{
winloss = y.Amount ?? 0;
}
});
var tradeCashSwap = DbContext.trade_cash_swap.FirstOrDefault(y => y.TradeCashPreId == x.id);
tradeCashSwap.PayInitialAmount = -winloss;
tradeCashSwap.PayAmount = -winloss;
tradeCashSwap.GetExtraAmount = coupon;
tradeCashSwap.GetCostFee = costFeeGet + costTradePriceGet;
tradeCashSwap.GetAmount = costFeeGet + costTradePriceGet + coupon;
var tradeCash = new trade_cash()
{
OptId = x.OptId,
OptName = x.OptName,
OptDate = DateTime.Now,
ExceciseType = x.ExceciseType,
TradeType = x.TradeType,
CallPut = x.CallPut,
Notional = x.Notional,
TradeAmount = x.TradeAmount,
IsLastAction = x.IsLastAction,
TradeId = x.TradeId,
FinalPrice = x.FinalPrice,
UnwindType = x.UnwindType,
UnwindNotional = x.UnwindNotional,
UnwindTradeAmount = x.UnwindTradeAmount,
UnwindPercentRate = x.UnwindPercentRate,
NotionalPercentRate = x.NotionalPercentRate,
Number = x.Number,
Amount = winloss + coupon + costFeeGet + costTradePriceGet,
QuoteAmount = tradeCashDetails.Sum(y => y.QuoteAmount ?? 0),
CurrencyRate = currencyRate,
Action = x.Action,
Status = x.Status,
ValueDate = x.ValueDate,
HappenedDate = x.HappenedDate,
ValidState = "Valid",
ExerciseWay = TradeCashExerciseWayEnum.
};
DbContext.trade_cash.Add(tradeCash);
x.IsFinished = true;
DbContext.SaveChanges();
if (PS.Config.Company == Configuration.CompanyEnum.)
{
new BizLogicZhaoZheng().GenerateZhaoZhengDealNumber(trade, tradeCash);
}
if (PS.Config.Company == Configuration.CompanyEnum.)
{
new BizLogicWCZD().GenerateWCZDNumber(DbContext, trade, tradeCash.ValueDate, tradeCash.id);
}
tradeCashDetails.ForEach(y => y.TradeCashId = tradeCash.id);
tradeCashSwap.TradeCashId = tradeCash.id;
//增加出入金记录
new ClientCashinCashoutBLL(this).CloseTrade_ClientCashInCashOutSave(trade, tradeCash, tradeCash.ValueDate);
});
//更新数据库
DbContext.SaveChanges();
}
}
}