1110 lines
65 KiB
C#
1110 lines
65 KiB
C#
using Qdp.Foundation.Implementations;
|
||
using Qdp.Pricing.Base.Implementations;
|
||
using Qdp.Pricing.Library.Options.Products.Autocall.Phoenix;
|
||
using Qdp.Pricing.Library.Options.Products.Autocall.Snowball;
|
||
using YLErp.BLL;
|
||
using YLErp.BLL.Calculation.V2;
|
||
using YLErp.BLL.EodSettlement;
|
||
using YLErp.DBModels;
|
||
using YLErp.DBModels.Helpers;
|
||
using YLErp.Models;
|
||
using YLErp.Modules.CalculationModule;
|
||
using YLErp.Modules.TradeModule;
|
||
using YLErp.Modules.TradeModule.DealModule;
|
||
using YLErp.Modules.TradeModule.ExoticOptionModule;
|
||
using YLErp.QdpModule;
|
||
|
||
namespace YLErp.Modules.RiskModule
|
||
{
|
||
/// <summary>
|
||
/// 资金流动性预测
|
||
/// </summary>
|
||
public class FlowRiskService : YLBaseService
|
||
{
|
||
public FlowRiskService(OptUserInfo userInfo) : base(userInfo)
|
||
{
|
||
}
|
||
|
||
public FlowRiskService(YLBaseService baseService) : base(baseService)
|
||
{
|
||
}
|
||
|
||
/// <summary>
|
||
/// 价格
|
||
/// </summary>
|
||
private Dictionary<string, double> _prices = new Dictionary<string, double>();
|
||
/// <summary>
|
||
/// 障碍期权
|
||
/// </summary>
|
||
private BarrierOptionKnockioService barrierService = null;
|
||
/// <summary>
|
||
/// 二元期权
|
||
/// </summary>
|
||
private BinaryOptionDealService binaryService = null;
|
||
/// <summary>
|
||
/// 双鲨期权
|
||
/// </summary>
|
||
private DoubleSharkOptionKnockoutService doubleSharkService = null;
|
||
/// <summary>
|
||
/// 区间累积期权
|
||
/// </summary>
|
||
private TradeRangeAccrualService rangeAccrualService = null;
|
||
|
||
/// <summary>
|
||
/// 凤凰期权
|
||
/// </summary>
|
||
private TradeAutocallBLL autocallService = null;
|
||
/// <summary>
|
||
/// 雪球期权
|
||
/// </summary>
|
||
private TradeSnowballBLL snowballService = null;
|
||
|
||
public List<FlowRiskModel> QueryFlowRiskList(DateTime startDate, DateTime endDate)
|
||
{
|
||
if (endDate < startDate)
|
||
{
|
||
return new List<FlowRiskModel>();
|
||
}
|
||
var flowRiskList = new List<FlowRiskModel>();
|
||
var dates = QdpCalendarHelper.AllBizDays(startDate, endDate);
|
||
var list =
|
||
(from t in DbContext.trade
|
||
where
|
||
ConsTrade.PositionTradeStatusList.Contains(t.TradeStatus) &&
|
||
t.ExerciseDate >= startDate && t.ValidState != "InValid"
|
||
select t).ToArray();
|
||
#if DEBUG
|
||
//string temp = "";
|
||
//list = DbContext.trade.Where(O => O.TradeNumber == temp).ToArray();
|
||
#endif
|
||
tradeBLL.SetFieldsByTradeType(list);
|
||
var subTradeDict = list.Where(O => O.ParentTradeId > 0).GroupBy(O => O.ParentTradeId).ToDictionary(K => K.Key, V => V.ToList());
|
||
list = list.Where(O => O.ParentTradeId == 0).ToArray();
|
||
foreach (var item in list)
|
||
{
|
||
if (subTradeDict.ContainsKey(item.id))
|
||
{
|
||
item.SubTrades = subTradeDict[item.id];
|
||
}
|
||
if (item.UnderlyingCode == null)
|
||
{
|
||
item.UnderlyingCode = "";
|
||
}
|
||
if (!_prices.ContainsKey(item.UnderlyingCode))
|
||
{
|
||
_prices[item.UnderlyingCode] = DataCacheProvider.GetUnderlyingDataSource().GetPrice(item.UnderlyingCode);
|
||
}
|
||
if (item.SubTrades != null && item.SubTrades.Any())
|
||
{
|
||
var endAll = true;
|
||
DateTime? settledate = null;
|
||
var trades = item.SubTrades.Where(O => O.TradeType != "现金流交易");
|
||
foreach (var sItem in trades)
|
||
{
|
||
if (!_prices.ContainsKey(sItem.UnderlyingCode))
|
||
{
|
||
_prices[sItem.UnderlyingCode] = DataCacheProvider.GetUnderlyingDataSource().GetPrice(sItem.UnderlyingCode);
|
||
}
|
||
var obj = CheckTradeStatus(sItem, startDate, endDate);
|
||
flowRiskList.Add(obj);
|
||
settledate = obj?.ValueDate ?? sItem.ExerciseDate ?? endDate;
|
||
if (!ConsTrade.TradeCompleteStatus.Contains(sItem.TradeStatus))
|
||
{
|
||
endAll = false;
|
||
}
|
||
}
|
||
if (endAll)
|
||
{
|
||
//结构化交易中所有非现金流交易全部了结时,现金流交易也应该了结;
|
||
trades = item.SubTrades.Where(O => O.TradeType == "现金流交易");
|
||
foreach (var sItem in trades)
|
||
{
|
||
var obj = new FlowRiskModel();
|
||
var udm = new underlying_manager();
|
||
udm.QuotationDate = sItem.ExerciseDate;
|
||
var ttm = TradeCalcHelper.CalculateTTMDays(sItem.ExerciseDate.Value, sItem.ExerciseDate.Value, udm.UnderlyingTypeId, false);
|
||
var OptionValue = ValueCalculator.GetOptionValueResultV2(
|
||
UserInfo.UserId.ToString(),
|
||
udm,
|
||
sItem,
|
||
new double[] { 0.0 },
|
||
new double[] { 0.0 },
|
||
null,
|
||
timeToMaturityDays: ttm,
|
||
request: QdpPricingRequest.BASIC_PRICING);
|
||
obj.Execute -= OptionValue.Pv;
|
||
obj.ValueDate = settledate ?? startDate;
|
||
obj.TradeType = "现金流交易";
|
||
obj.TradeNumber = sItem.TradeNumber;
|
||
obj.EncryptId = sItem.EncryptId;
|
||
flowRiskList.Add(obj);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
var obj = CheckTradeStatus(item, startDate, endDate);
|
||
flowRiskList.Add(obj);
|
||
}
|
||
}
|
||
var dict = flowRiskList.Where(O => O != null && O.TotleCash != 0).GroupBy(O => O.ValueDate).ToDictionary(K => K.Key, V => V.ToList()).OrderBy(O => O.Key);
|
||
var result = new List<FlowRiskModel>();
|
||
var total = new FlowRiskModel() { ParentLevel = 2 };
|
||
foreach (var item in dict)
|
||
{
|
||
var parent = new FlowRiskModel() { ParentLevel = 1, ValueDate = item.Key, KO = item.Value.Sum(O => O.KO), Execute = item.Value.Sum(O => O.Execute) };
|
||
total.KO += parent.KO;
|
||
total.Execute += parent.Execute;
|
||
result.Add(parent);
|
||
result.AddRange(item.Value);
|
||
}
|
||
result.Add(total);
|
||
return result;
|
||
}
|
||
|
||
private FlowRiskModel CheckTradeStatus(trade td, DateTime startDate, DateTime endDate)
|
||
{
|
||
var result = new FlowRiskModel();
|
||
result.TradeNumber = td.TradeNumber;
|
||
result.TradeType = td.TradeType;
|
||
result.EncryptId = td.EncryptId;
|
||
switch (td.TradeType)
|
||
{
|
||
case "香草期权":
|
||
case "亚式期权":
|
||
case "合成价差期权":
|
||
case "收益增强结构":
|
||
case "自定义交易":
|
||
if (td.ExerciseDate > endDate)
|
||
{
|
||
result = null;
|
||
break;
|
||
}
|
||
trade_cash r = new trade_cash();
|
||
SettlementCalcCommons.SetPossibleExec(r, td, _prices[td.UnderlyingCode]);
|
||
td.TradeStatus = "已到期";
|
||
result.Execute = -r.Amount;
|
||
result.ValueDate = td.ExerciseDate.Value;
|
||
break;
|
||
case "收益互换":
|
||
if (td.ExerciseDate > endDate)
|
||
{
|
||
result = null;
|
||
break;
|
||
}
|
||
var tradeCashList = DbContext.trade_cash.Where(y => y.TradeId == td.id && y.Action == "系统操作-互换" && y.ValidState != "InValid" && !y.IsDeleted && y.ValueDate <= td.ExerciseDate);
|
||
var tradeCashIds = tradeCashList.Select(x => x.id);
|
||
var tradeCash = tradeCashList.OrderByDescending(y => y.id).FirstOrDefault();
|
||
var cashSwaps = DbContext.trade_cash_swap.Where(x => x.TradeId == td.id && tradeCashIds.Contains(x.TradeCashId)).ToArray();
|
||
var tradeCashSwap = tradeCash != null ? cashSwaps.FirstOrDefault(x => x.TradeCashId == tradeCash.id) : null;
|
||
//取最后一次手动收益;
|
||
var lastManualCashSwap = cashSwaps.OrderByDescending(o => o.StartDate).FirstOrDefault(x => !x.IsAuto);
|
||
var lastManualCash = lastManualCashSwap != null ? tradeCashList.FirstOrDefault(x => x.id == lastManualCashSwap.TradeCashId) : null;
|
||
DateTime endDateSwap;
|
||
var preSwapDate = PayoffSwapCalcService.GetSwapRateStartDate(td, td.trade_swap, td.ExerciseDate.Value, tradeCash, lastManualCash, td.trade_swap.IsGetFloatingProfit, out endDateSwap);
|
||
var initialAmountPay = PayoffSwapCalcService.GetInitialAmountSwapPay(td, td.trade_swap, tradeCashSwap?.PayFinalPrice ?? td.trade_swap.PaySpotPrice ?? 0
|
||
, _prices[td.UnderlyingCode], td.StockEqvNotional, td.ExerciseDate.Value, tradeCash?.ValueDate);
|
||
var extraAmountGet = PayoffSwapCalcService.GetExtraAmountBySwapRate(td.ClientId, td.TradeDate, td.trade_swap.GetSwapTimeAndRate, preSwapDate, endDateSwap, td.trade_swap.AnnualDays ?? 0, td.StockEqvNotional);
|
||
|
||
preSwapDate = PayoffSwapCalcService.GetSwapRateStartDate(td, td.trade_swap, td.ExerciseDate.Value, tradeCash, lastManualCash, td.trade_swap.IsPayFloatingProfit, out endDateSwap);
|
||
var initialAmountGet = PayoffSwapCalcService.GetInitialAmountSwapGet(td, td.trade_swap, tradeCashSwap?.GetFinalPrice ?? td.trade_swap.GetSpotPrice ?? 0
|
||
, _prices[td.UnderlyingCode], td.StockEqvNotional, td.ExerciseDate.Value, tradeCash?.ValueDate);
|
||
var extraAmountPay = PayoffSwapCalcService.GetExtraAmountBySwapRate(td.ClientId, td.TradeDate, td.trade_swap.PaySwapTimeAndRate, preSwapDate, endDateSwap, td.trade_swap.AnnualDays ?? 0, td.StockEqvNotional);
|
||
|
||
td.TradeStatus = "已到期";
|
||
result.Execute = -((initialAmountGet + extraAmountGet) - (initialAmountPay + extraAmountPay));
|
||
result.ValueDate = td.ExerciseDate.Value;
|
||
break;
|
||
case "现金流交易":
|
||
if (td.ExerciseDate > endDate)
|
||
{
|
||
result = null;
|
||
break;
|
||
}
|
||
var udm = new underlying_manager();
|
||
udm.QuotationDate = td.ExerciseDate;
|
||
var ttm = TradeCalcHelper.CalculateTTMDays(td.ExerciseDate.Value, td.ExerciseDate.Value, udm.UnderlyingTypeId, false);
|
||
var OptionValue = ValueCalculator.GetOptionValueResultV2(
|
||
UserInfo.UserId.ToString(),
|
||
udm,
|
||
td,
|
||
new double[] { 0.0 },
|
||
new double[] { 0.0 },
|
||
null,
|
||
timeToMaturityDays: ttm,
|
||
request: QdpPricingRequest.BASIC_PRICING);
|
||
result.Execute -= OptionValue.Pv;
|
||
result.ValueDate = td.ExerciseDate.Value;
|
||
if ((td.SettlementDate ?? td.ExerciseDate.GetValueOrDefault()) > endDate)
|
||
{
|
||
result = null;
|
||
}
|
||
break;
|
||
case "凤凰期权":
|
||
{
|
||
autocallService = autocallService ?? new TradeAutocallBLL(UserInfo);
|
||
var observDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(
|
||
new Date(td.TradeDate), new Date(td.ExerciseDate.Value)).Where(O => O.DateTime != td.TradeDate).ToArray();
|
||
//敲入转期权和到期支付票息同时存在时,若设置观察价格页面设置了结算金额,作为票息处理,敲入了结金额维持系统计算逻辑不变
|
||
bool hasUseSettlementAmount = false;
|
||
var tradeCashs = DbContext.trade_cash.Where(x => x.ValidState != ConsGlobal.InValid && !x.IsDeleted && x.TradeId == td.id && x.Action == "系统操作-平仓费" && (x.ValueDate > startDate && (x.ConfirmDate > startDate || x.ConfirmDate == DateTime.MinValue)) && x.UnwindNotional < x.Notional).ToList();
|
||
var notional = (ConsTrade.TradeCompleteStatus.Contains(td.TradeStatus) && td.UnWindDate <= startDate ? 0 : td.Notional) + tradeCashs.Sum(x => x.UnwindNotional).Value;
|
||
var optionTrade = QdpTradeBuilder.GetAutocallOptionTrade(td, td.trade_autocall,
|
||
new OptionTradeParamRequest(valuedateBLL.SysRiskFreeRate()) { ParamOverride = x => { x.notional = notional; } });
|
||
var autocall = (AutoCall)optionTrade.Instrument;
|
||
var kiBarrier = td.IsMoneynessOptionData ? td.trade_autocall.KIBarrier * td.SpotPrice : td.trade_autocall.KIBarrier;
|
||
var happenedObservations = new List<autocall_observation>();
|
||
foreach (var date in observDates)
|
||
{
|
||
if (date.DateTime < startDate || date.DateTime > endDate)
|
||
{
|
||
continue;
|
||
}
|
||
var isKnockedOut = false;
|
||
//只在敲出观察日检查敲出和票息情况
|
||
//如果交易已经是敲出状态了,不用再做票息和敲出检查
|
||
if (autocall.KOObsDates.Select(x => x.DateTime).Contains(date.DateTime)
|
||
&& td.trade_autocall.KnockInOutStatus != ConsTrade.KnockState.KnockedOut)
|
||
{
|
||
double koBarrier;
|
||
if (autocall.CustomizedKOBarriers != null && autocall.CustomizedKOBarriers.Length > 0)
|
||
{
|
||
var index = autocall.KOObsDates.Select(x => x.DateTime).ToList().IndexOf(date.DateTime);
|
||
koBarrier = autocall.CustomizedKOBarriers[index];
|
||
}
|
||
else
|
||
{
|
||
koBarrier = td.trade_autocall.KOBarrier;
|
||
}
|
||
|
||
if (td.IsMoneynessOptionData)
|
||
{
|
||
koBarrier *= td.SpotPrice ?? 1.0;
|
||
}
|
||
|
||
#region 票息检查
|
||
var couponBarrier =
|
||
td.IsMoneynessOptionData ?
|
||
td.trade_autocall.CouponBarrier * td.SpotPrice :
|
||
td.trade_autocall.CouponBarrier;
|
||
|
||
isKnockedOut = true;// _prices[td.UnderlyingCode] >= koBarrier;
|
||
|
||
//有票息
|
||
//if (_prices[td.UnderlyingCode] >= couponBarrier)
|
||
{
|
||
//利息计算时,当autocall的Notional包含了符号,则GetEffectiveObservation考虑了买卖方向了
|
||
var observation = autocall.GetEffectiveObservation(date);
|
||
|
||
if (observation != null)
|
||
{
|
||
td.trade_autocall = td.trade_autocall;
|
||
//观察日页面设置的结算金额
|
||
if (td.SettlementAmount != null)
|
||
{
|
||
td.SettlementAmount = td.SettlementAmount;
|
||
hasUseSettlementAmount = true;
|
||
//到期敲入且未敲出情况
|
||
if (date.DateTime == autocall.ExerciseDates.Last().DateTime && !isKnockedOut)
|
||
{
|
||
//当前满足敲入或者已经敲入了
|
||
if (autocall.KIObsDates.Select(x => x.DateTime).Contains(date.DateTime) && _prices[td.UnderlyingCode] <= kiBarrier || td.trade_autocall.KnockInOutStatus == ConsTrade.KnockState.KnockedIn)
|
||
{
|
||
var optionPayoffPayment = autocall.GetPayoff(new double[] { _prices[td.UnderlyingCode] });
|
||
var paymentAmount = TradeHelper.GetAmountByPaymentAmount(optionPayoffPayment[0].PaymentAmount, td.PrincipalSum(), td.BuySell);
|
||
td.SettlementAmount -= paymentAmount;
|
||
}
|
||
}
|
||
}
|
||
|
||
happenedObservations.Add(autocallService.SaveCouponObservation(td, td.trade_autocall, observation, date.DateTime, _prices[td.UnderlyingCode], isKnockedOut, td.SettlementAmount, false));
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region 敲出检查
|
||
|
||
// 发生敲出事件
|
||
if (isKnockedOut)
|
||
{
|
||
// 更新观察状态
|
||
td.trade_autocall.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
|
||
td.trade_autocall.KnockInOutDate = date.DateTime;
|
||
// 更新交易状态
|
||
td.TradeStatus = ConsTrade.已平仓;
|
||
td.UnWindDate = date.DateTime;
|
||
happenedObservations.ForEach(O =>
|
||
{
|
||
result.KO -= O.PaymentAmount;
|
||
});
|
||
result.ValueDate = date;
|
||
if (td.trade_autocall.CouponPayType != CouponPayTypeEnum.AtMaturity)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
#endregion
|
||
}
|
||
|
||
#region 敲入检查
|
||
//在当前结算日之前未敲出且未敲入:
|
||
if (autocall.KIObsDates.Select(x => x.DateTime).Contains(date.DateTime) && !isKnockedOut)
|
||
{
|
||
//看涨 - 向下敲入,看跌 - 向上敲入
|
||
var knockedin = ConsGlobal.CallPut.IsCall(td.CallPut) ? _prices[td.UnderlyingCode] <= kiBarrier : _prices[td.UnderlyingCode] >= kiBarrier;
|
||
|
||
// 发生敲入事件
|
||
if (knockedin)
|
||
{
|
||
// 更新观察状态
|
||
td.trade_autocall.KnockInOutStatus = ConsTrade.KnockState.KnockedIn;
|
||
td.trade_autocall.KnockInOutDate = date.DateTime;
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region 到期检查
|
||
if (date.DateTime == autocall.ExerciseDates.Last().DateTime)
|
||
{
|
||
td.UnWindDate = date.DateTime;
|
||
if (td.trade_autocall.KnockInOutStatus == ConsTrade.KnockState.KnockedIn)
|
||
{
|
||
td.TradeStatus = ConsTrade.已执行;
|
||
if (!autocall.IncludeCouponAfterKI)
|
||
{
|
||
// 敲入不支付票息,则要将之前累积的票息删除掉
|
||
happenedObservations.Clear();
|
||
hasUseSettlementAmount = false;
|
||
}
|
||
double paymentAmount = 0;
|
||
|
||
if (td.SettlementAmount != null && !hasUseSettlementAmount)
|
||
{
|
||
paymentAmount = td.SettlementAmount ?? 0;
|
||
}
|
||
else
|
||
{
|
||
//已敲入,到期时计算期权收益
|
||
var optionPayoffPayment = autocall.GetPayoff(new double[] { _prices[td.UnderlyingCode] });
|
||
//optionPayoffPayment[0].PaymentAmount包含了买卖方向的处理了
|
||
paymentAmount = TradeHelper.GetAmountByPaymentAmount(optionPayoffPayment[0].PaymentAmount, td.PrincipalSum(), td.BuySell);
|
||
|
||
}
|
||
|
||
if (td.trade_autocall.CouponPayAtMaturity && td.trade_autocall.IncludeCouponAfterKI)
|
||
{
|
||
happenedObservations.AddRange(DbContext.autocall_observation.Where(o => o.TradeId == td.id).ToList());
|
||
}
|
||
}
|
||
else if (td.trade_autocall.KnockInOutStatus != ConsTrade.KnockState.KnockedOut)
|
||
{
|
||
// 更新交易状态
|
||
td.TradeStatus = ConsTrade.已到期;
|
||
if (td.trade_autocall.CouponPayAtMaturity)
|
||
{
|
||
happenedObservations.AddRange(DbContext.autocall_observation.Where(o => o.TradeId == td.id).ToList());
|
||
}
|
||
if ((td.SettlementDate ?? td.ExerciseDate.GetValueOrDefault()) > endDate)
|
||
{
|
||
result = null;
|
||
}
|
||
}
|
||
else if (td.trade_autocall.KnockInOutStatus == ConsTrade.KnockState.KnockedOut)
|
||
{
|
||
if (td.trade_autocall.CouponPayAtMaturity)
|
||
{
|
||
happenedObservations.AddRange(DbContext.autocall_observation.Where(o => o.TradeId == td.id).ToList());
|
||
}
|
||
}
|
||
if (result != null)
|
||
{
|
||
happenedObservations.ForEach(O =>
|
||
{
|
||
result.Execute -= O.PaymentAmount;
|
||
});
|
||
result.ValueDate = date;
|
||
}
|
||
break;
|
||
}
|
||
#endregion
|
||
}
|
||
}
|
||
break;
|
||
case "雪球期权":
|
||
{
|
||
snowballService = snowballService ?? new TradeSnowballBLL(UserInfo);
|
||
var observDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(
|
||
new Date(td.TradeDate), new Date(td.ExerciseDate.Value)).Where(O => O.DateTime != td.TradeDate).ToArray();
|
||
var tradeCashs = DbContext.trade_cash.Where(x => x.ValidState != ConsGlobal.InValid && !x.IsDeleted && x.TradeId == td.id && x.Action == "系统操作-平仓费" && (x.ValueDate > startDate && (x.ConfirmDate > startDate || x.ConfirmDate == DateTime.MinValue)) && x.UnwindNotional < x.Notional).ToList();
|
||
|
||
var request = new OptionTradeParamRequest(valuedateBLL.SysRiskFreeRate())
|
||
{
|
||
ParamOverride = p => p.notional = (ConsTrade.TradeCompleteStatus.Contains(td.TradeStatus) && td.UnWindDate <= startDate ? 0 : td.Notional) + tradeCashs.Sum(x => x.UnwindNotional).Value
|
||
};
|
||
|
||
var optionTrade = QdpTradeBuilder.GetSnowballOptionTrade(td, td.trade_snowball, request);
|
||
var datesStr = td.trade_snowball.KOObservationSettleDates;
|
||
var KOObsSettleDates = string.IsNullOrWhiteSpace(datesStr) ? null : datesStr.Split(new char[] { ',', ';', ',', ';' }, StringSplitOptions.RemoveEmptyEntries).Select(x => DateTime.Parse(x)).ToArray();
|
||
|
||
var snowball = (SimpleSnowball)optionTrade.Instrument;
|
||
var happenedObservations = new List<autocall_observation>();
|
||
foreach (var date in observDates)
|
||
{
|
||
if (date.DateTime < startDate || date.DateTime > endDate)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
//只在敲出观察日检查敲出和票息情况
|
||
//如果交易已经是敲出状态了,不用再做票息和敲出检查
|
||
if (snowball.KOObsDates.Select(x => x.DateTime).Contains(date.DateTime)
|
||
&& td.trade_snowball.KnockInOutStatus != ConsTrade.KnockState.KnockedOut)
|
||
{
|
||
#region 敲出检查
|
||
double koBarrier;
|
||
var koSettleDate = date.DateTime;
|
||
if (snowball.CustomizedKOBarriers != null && snowball.CustomizedKOBarriers.Length > 0)
|
||
{
|
||
var index = snowball.KOObsDates.Select(x => x.DateTime).ToList().IndexOf(date.DateTime);
|
||
koBarrier = snowball.CustomizedKOBarriers[index];
|
||
if (KOObsSettleDates != null && KOObsSettleDates.Length > index)
|
||
{
|
||
koSettleDate = KOObsSettleDates[index];
|
||
}
|
||
}
|
||
else
|
||
{
|
||
koBarrier = td.trade_snowball.KOBarrier;
|
||
if (KOObsSettleDates != null && KOObsSettleDates.Any())
|
||
{
|
||
koSettleDate = KOObsSettleDates[0];
|
||
}
|
||
}
|
||
|
||
if (td.trade_snowball.KORebateType == RebateTypeEnum.AtEnd)
|
||
{
|
||
koSettleDate = td.ExerciseDate.Value;
|
||
}
|
||
else if (koSettleDate < date.DateTime)
|
||
{
|
||
koSettleDate = date.DateTime;
|
||
}
|
||
if (koSettleDate > endDate)
|
||
{
|
||
result = null;
|
||
break;
|
||
}
|
||
|
||
if (td.IsMoneynessOptionData)
|
||
{
|
||
koBarrier *= td.SpotPrice ?? 1.0;
|
||
}
|
||
|
||
// 发生敲出事件
|
||
//if (_prices[td.UnderlyingCode] >= koBarrier)
|
||
{
|
||
// 更新观察状态
|
||
td.trade_snowball.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
|
||
td.trade_snowball.KnockInOutDate = date.DateTime;
|
||
// 更新交易状态
|
||
td.TradeStatus = ConsTrade.已平仓;
|
||
td.UnWindDate = date.DateTime;
|
||
|
||
if (snowball.UseOptionPayoffAtKO)
|
||
{
|
||
double paymentAmount = 0;
|
||
var koOptionCashflows = snowball.GetKOPayoff(date, _prices[td.UnderlyingCode]);
|
||
if (td.SettlementAmount != null)
|
||
{
|
||
paymentAmount = td.SettlementAmount ?? 0;
|
||
}
|
||
else
|
||
{
|
||
paymentAmount = TradeHelper.GetAmountByPaymentAmount(koOptionCashflows[0].PaymentAmount, td.PrincipalSum(), td.BuySell);
|
||
|
||
}
|
||
result.KO -= paymentAmount;
|
||
result.ValueDate = koSettleDate;
|
||
break;
|
||
//koOptionCashflows[0].PaymentAmount包含了买卖方向的处理了
|
||
|
||
}
|
||
else
|
||
{
|
||
if (td.SettlementAmount != null)
|
||
{
|
||
double tradePrice = 0;
|
||
var settlementAmount = td.SettlementAmount ?? 0;
|
||
//观察日价格页面的结算金额包含了年化期权费,该处逻辑需要先按照扣除年化期权费来算,后面逻辑会补上年化期权费,否则会重复运算
|
||
if (td.trade_snowball.AnnualizedPremiumRate.HasValue && td.trade_snowball.AnnualizedPremiumRate != 0)
|
||
{
|
||
tradePrice = td.StockEqvNotional * td.ParticipationRate * td.trade_snowball.AnnualizedPremiumRate * snowball.CouponDayCount.CalcDayCountFraction(snowball.StartDate, date) ?? 0;
|
||
if (tradePrice != 0)
|
||
{
|
||
settlementAmount -= (td.BuySell == "买入" ? -1 : 1) * tradePrice;
|
||
}
|
||
}
|
||
result.KO -= settlementAmount;
|
||
result.ValueDate = date;
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
var couponPayment = TradeHelper.GetAmountByPaymentAmount(snowball.CouponPayment(date), td.PrincipalSum(), td.BuySell);
|
||
result.KO -= couponPayment;
|
||
result.ValueDate = date;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
#endregion
|
||
}
|
||
|
||
#region 敲入检查
|
||
//在当前结算日之前未敲出且未敲入:
|
||
// !((KO || KI) && KOKIDate < valueDate)
|
||
if (!((td.trade_snowball.KnockInOutStatus == ConsTrade.KnockState.KnockedOut
|
||
|| td.trade_snowball.KnockInOutStatus == ConsTrade.KnockState.KnockedIn)
|
||
&& td.trade_snowball.KnockInOutDate < date.DateTime)
|
||
&& snowball.KIObsDates.Select(x => x.DateTime).Contains(date.DateTime)
|
||
&& td.trade_snowball.KIPayoffType != KIPayoffTypeEnum.None)
|
||
{
|
||
var kiBarrier =
|
||
td.IsMoneynessOptionData ?
|
||
td.trade_snowball.KIBarrier * td.SpotPrice :
|
||
td.trade_snowball.KIBarrier;
|
||
|
||
// 发生敲入事件
|
||
if (_prices[td.UnderlyingCode] <= kiBarrier)
|
||
{
|
||
// 更新观察状态
|
||
td.trade_snowball.KnockInOutStatus = ConsTrade.KnockState.KnockedIn;
|
||
td.trade_snowball.KnockInOutDate = date.DateTime;
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region 到期检查
|
||
if (date.DateTime == snowball.ExerciseDates.Last().DateTime)
|
||
{
|
||
if (snowball.UseOptionPayoffAtMaturity &&
|
||
(td.trade_snowball.KnockInOutStatus == ConsTrade.KnockState.KnockedIn || td.trade_snowball.KIBarrier <= 0))
|
||
{
|
||
td.TradeStatus = ConsTrade.已执行;
|
||
td.UnWindDate = date.DateTime;
|
||
double paymentAmount = 0;
|
||
//已敲入,到期时计算期权收益
|
||
var optionPayoffPayment = snowball.GetPayoff(new double[] { _prices[td.UnderlyingCode] });
|
||
if (td.SettlementAmount != null)
|
||
{
|
||
paymentAmount = td.SettlementAmount ?? 0;
|
||
}
|
||
else
|
||
{
|
||
paymentAmount = TradeHelper.GetAmountByPaymentAmount(optionPayoffPayment[0].PaymentAmount, td.PrincipalSum(), td.BuySell);
|
||
}
|
||
//optionPayoffPayment[0].PaymentAmount包含了买卖方向的处理了
|
||
result.Execute -= paymentAmount;
|
||
result.ValueDate = date;
|
||
break;
|
||
}
|
||
else if (td.trade_snowball.KnockInOutStatus != ConsTrade.KnockState.KnockedOut)
|
||
{
|
||
td.TradeStatus = ConsTrade.已到期;
|
||
td.UnWindDate = date.DateTime;
|
||
var maturityCouponRate = snowball.Coupon * snowball.InitialSpotPrice;
|
||
var maturityCouponPayment =
|
||
snowball.FixedCoupon ?
|
||
maturityCouponRate * snowball.Notional :
|
||
maturityCouponRate * snowball.Notional * snowball.CouponDayCount.CalcDayCountFraction(snowball.StartDate, snowball.ExerciseDates.Last());
|
||
maturityCouponPayment = TradeHelper.GetAmountByPaymentAmount(maturityCouponPayment, td.PrincipalSum(), td.BuySell);
|
||
|
||
double tradePrice = 0;
|
||
if (td.trade_snowball.AnnualizedPremiumRate.HasValue && td.trade_snowball.AnnualizedPremiumRate != 0)
|
||
{
|
||
tradePrice = td.StockEqvNotional * td.ParticipationRate * td.trade_snowball.AnnualizedPremiumRate * snowball.CouponDayCount.CalcDayCountFraction(snowball.StartDate, date) ?? 0;
|
||
}
|
||
|
||
tradeCash = new trade_cash();
|
||
if (td.SettlementAmount != null)
|
||
{
|
||
//观察日价格页面的结算金额包含了年化期权费,该处逻辑需要先按照扣除年化期权费来算,后面逻辑会补上年化期权费,否则会重复运算
|
||
var settlementAmount = td.SettlementAmount ?? 0;
|
||
if (tradePrice != 0)
|
||
{
|
||
settlementAmount -= (td.BuySell == "买入" ? -1 : 1) * tradePrice;
|
||
}
|
||
result.Execute -= settlementAmount;
|
||
result.ValueDate = date;
|
||
}
|
||
else
|
||
{
|
||
result.Execute -= maturityCouponPayment;
|
||
result.ValueDate = date;
|
||
}
|
||
if ((td.SettlementDate ?? td.ExerciseDate.GetValueOrDefault()) > endDate)
|
||
{
|
||
result = null;
|
||
}
|
||
}
|
||
}
|
||
#endregion
|
||
}
|
||
}
|
||
break;
|
||
//case "气囊结构":
|
||
// //气囊结构没有计算赔付;
|
||
// airbagService = airbagService ?? new TradeAirbagService(UserInfo);
|
||
// airbagService.CheckAirbagKnockInStatus(td, td.trade_airbag, startDate, _prices[td.UnderlyingCode]);
|
||
// break;
|
||
case "区间累积期权":
|
||
{
|
||
if (td.ExerciseDate > endDate)
|
||
{
|
||
result = null;
|
||
break;
|
||
}
|
||
rangeAccrualService = rangeAccrualService ?? new TradeRangeAccrualService(UserInfo);
|
||
var observDates = QdpHelper.ParseObservationDate(td.trade_rangeaccrual.ObservationDates)?.ToArray();
|
||
|
||
if (observDates == null)
|
||
{
|
||
observDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(
|
||
new Date(td.TradeDate.Value), new Date(td.ExerciseDate.Value)).Where(O => O.DateTime != td.TradeDate).ToArray();
|
||
}
|
||
observDates = observDates.Where(O => O.DateTime >= startDate).ToArray();
|
||
var happenedObservations = new List<autocall_observation>();
|
||
if (observDates != null)
|
||
{
|
||
foreach (var date in observDates)
|
||
{
|
||
var upperRange = td.IsMoneynessOptionData ? td.trade_rangeaccrual.UpperRange * td.SpotPrice.Value : td.trade_rangeaccrual.UpperRange;
|
||
var lowerRange = td.IsMoneynessOptionData ? td.trade_rangeaccrual.LowerRange * td.SpotPrice.Value : td.trade_rangeaccrual.LowerRange;
|
||
|
||
//有区间收益
|
||
if (_prices[td.UnderlyingCode] < upperRange && _prices[td.UnderlyingCode] > lowerRange)
|
||
{
|
||
var couponCash = td.trade_rangeaccrual.BonusRate * td.Notional * (td.SpotPrice ?? 0) / observDates.Length * (td.BuySell == "卖出" ? -1 : 1);
|
||
|
||
happenedObservations.Add(rangeAccrualService.SaveObservation(td, td.trade_rangeaccrual, date, couponCash, td.Notional, false));
|
||
}
|
||
}
|
||
}
|
||
|
||
//到期日生成票息资金记录
|
||
td.TradeStatus = ConsTrade.已到期;
|
||
|
||
happenedObservations.AddRange(DbContext.autocall_observation.Where(o => o.TradeId == td.id).ToList());
|
||
|
||
//happenedObservations?.ForEach(o =>
|
||
//{
|
||
// var paymentAmount = TradeHelper.GetAmountByPaymentAmount(o.PaymentAmount, td.PrincipalSum(), td.BuySell);
|
||
// result.Execute -= paymentAmount;
|
||
//});
|
||
result.Execute -= happenedObservations.Sum(n => n.PaymentAmount) + td.PrincipalSum();
|
||
result.ValueDate = td.ExerciseDate.Value;
|
||
if ((td.SettlementDate ?? td.ExerciseDate.GetValueOrDefault()) > endDate)
|
||
{
|
||
result = null;
|
||
}
|
||
}
|
||
break;
|
||
case "双鲨期权":
|
||
{
|
||
var observDates = QdpHelper.ParseObservationDate(td.trade_double_sharkfin_option.ObservationDates)?.ToArray();
|
||
if (observDates == null)
|
||
{
|
||
observDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(
|
||
new Date(td.TradeDate), new Date(td.ExerciseDate.Value)).Where(O => O.DateTime != td.TradeDate).ToArray();
|
||
}
|
||
var lastDate = observDates.Where(O => O >= new Date(startDate) && O <= new Date(endDate)).OrderBy(O => (O)).FirstOrDefault();
|
||
if (lastDate != default)
|
||
{
|
||
doubleSharkService = doubleSharkService ?? new DoubleSharkOptionKnockoutService(UserInfo);
|
||
var barrierHigh = td.IsMoneynessOptionData ? td.trade_double_sharkfin_option.BarrierHigh * td.SpotPrice : td.trade_double_sharkfin_option.BarrierHigh;
|
||
var barrierLow = td.IsMoneynessOptionData ? td.trade_double_sharkfin_option.BarrierLow * td.SpotPrice : td.trade_double_sharkfin_option.BarrierLow;
|
||
|
||
var useRebate = _prices[td.UnderlyingCode] <= barrierLow;
|
||
var price = _prices[td.UnderlyingCode];
|
||
td.trade_double_sharkfin_option.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
|
||
td.trade_double_sharkfin_option.KnockInOutDate = startDate;
|
||
td.TradeStatus = ConsTrade.已平仓;
|
||
td.UnWindDate = startDate;
|
||
r = doubleSharkService.SaveDoubleSharkFinRebateCash(td, td.trade_double_sharkfin_option, price, startDate, useRebate, false);
|
||
result.KO = -r?.Amount;
|
||
if (td.trade_double_sharkfin_option.RebateType == "AtHit")
|
||
{
|
||
result.ValueDate = lastDate;
|
||
}
|
||
else if (td.trade_double_sharkfin_option.RebateType == "AtEnd" && td.ExerciseDate <= endDate)
|
||
{
|
||
result.ValueDate = td.ExerciseDate.Value;
|
||
}
|
||
else
|
||
{
|
||
result = null;
|
||
}
|
||
}
|
||
else if (td.ExerciseDate <= endDate)
|
||
{
|
||
td.TradeStatus = ConsTrade.已到期;
|
||
td.UnWindDate = td.ExerciseDate;
|
||
r = new trade_cash();
|
||
SettlementCalcCommons.SetPossibleExec(r, td, _prices[td.UnderlyingCode]);
|
||
result.Execute = -r?.Amount;
|
||
result.ValueDate = td.ExerciseDate.Value;
|
||
if ((td.SettlementDate ?? td.ExerciseDate.GetValueOrDefault()) > endDate)
|
||
{
|
||
result = null;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
result = null;
|
||
}
|
||
}
|
||
break;
|
||
case "二元期权":
|
||
{
|
||
if (td.ExerciseMode == "American")
|
||
{
|
||
r = null;
|
||
binaryService = binaryService ?? new BinaryOptionDealService(UserInfo);
|
||
var isObservationDate = true;
|
||
double upPrice = _prices[td.UnderlyingCode], lowPrice = _prices[td.UnderlyingCode];
|
||
|
||
var strike = td.IsMoneynessOptionData ? td.Strike * td.SpotPrice : td.Strike;
|
||
|
||
var UpperBarrier = new Lazy<double?>(() =>
|
||
td.IsMoneynessOptionData ? td.trade_binary_option.UpperBarrier * td.SpotPrice : td.trade_binary_option.UpperBarrier);
|
||
if (isObservationDate)
|
||
{
|
||
switch (td.trade_binary_option.PayoffType)
|
||
{
|
||
case "UpOneTouch":
|
||
//触碰,买方获得盈利,交易结束
|
||
var cash = BinaryOptionDealService.GetCashOrNothingAmount(td, td.trade_binary_option, startDate, useHighAmount: false);
|
||
r = binaryService.SaveBinarySettleCash(td, td.trade_binary_option, startDate, cash, true, upPrice, false);
|
||
td.TradeStatus = ConsTrade.已执行;
|
||
td.UnWindDate = startDate;
|
||
break;
|
||
case "DownOneTouch":
|
||
//触碰,买方获得盈利,交易结束
|
||
cash = BinaryOptionDealService.GetCashOrNothingAmount(td, td.trade_binary_option, startDate, useHighAmount: false);
|
||
r = binaryService.SaveBinarySettleCash(td, td.trade_binary_option, startDate, cash, true, lowPrice, false);
|
||
td.TradeStatus = ConsTrade.已执行;
|
||
td.UnWindDate = startDate;
|
||
break;
|
||
case "UpNoTouch":
|
||
//触碰,买方无盈利,交易结束
|
||
r = binaryService.SaveBinarySettleCash(td, td.trade_binary_option, startDate, 0, false, upPrice, false);
|
||
td.TradeStatus = ConsTrade.已到期;
|
||
td.UnWindDate = startDate;
|
||
break;
|
||
case "DownNoTouch":
|
||
//触碰,买方无盈利,交易结束
|
||
r = binaryService.SaveBinarySettleCash(td, td.trade_binary_option, startDate, 0, false, lowPrice, false);
|
||
td.TradeStatus = ConsTrade.已到期;
|
||
td.UnWindDate = startDate;
|
||
break;
|
||
case "DoubleOneTouch":
|
||
var breachHighBarrier = upPrice >= UpperBarrier.Value;
|
||
var price = breachHighBarrier ? upPrice : lowPrice;
|
||
cash = BinaryOptionDealService.GetCashOrNothingAmount(td, td.trade_binary_option, startDate, useHighAmount: breachHighBarrier);
|
||
//触碰上限或下限,买方获得盈利,交易结束
|
||
r = binaryService.SaveBinarySettleCash(td, td.trade_binary_option, startDate, cash, true, price, false);
|
||
td.TradeStatus = ConsTrade.已执行;
|
||
td.UnWindDate = startDate;
|
||
break;
|
||
case "DoubleNoTouch":
|
||
price = upPrice >= td.trade_binary_option.UpperBarrier ? upPrice : lowPrice;
|
||
//触碰上限或下限,买方无盈利,交易结束
|
||
r = binaryService.SaveBinarySettleCash(td, td.trade_binary_option, startDate, 0, false, price, false);
|
||
td.TradeStatus = ConsTrade.已到期;
|
||
td.UnWindDate = startDate;
|
||
break;
|
||
}
|
||
if (td.trade_binary_option.RebateType == "AtHit")
|
||
{
|
||
result.ValueDate = startDate;
|
||
}
|
||
else if (td.trade_binary_option.RebateType == "AtEnd" && td.ExerciseDate <= endDate)
|
||
{
|
||
result.ValueDate = td.ExerciseDate.Value;
|
||
}
|
||
else
|
||
{
|
||
result = null;
|
||
}
|
||
}
|
||
|
||
//OneTouch类型,到期仍未触碰,则交易结束
|
||
if (td.trade_binary_option.PayoffType.Contains("OneTouch") && td.ExerciseDate <= startDate)
|
||
{
|
||
binaryService.SaveBinarySettleCash(td, td.trade_binary_option, startDate, 0, false, _prices[td.UnderlyingCode], false);
|
||
td.TradeStatus = ConsTrade.已到期;
|
||
td.UnWindDate = startDate;
|
||
}
|
||
|
||
//NoTouch类型,到期仍未触碰,买方获得盈利,交易结束
|
||
if (td.trade_binary_option.PayoffType.Contains("NoTouch") && td.ExerciseDate <= endDate)
|
||
{
|
||
var cash = BinaryOptionDealService.GetCashOrNothingAmount(td, td.trade_binary_option, startDate, useHighAmount: false);
|
||
r = binaryService.SaveBinarySettleCash(td, td.trade_binary_option, startDate, cash, false, _prices[td.UnderlyingCode], false);
|
||
td.TradeStatus = ConsTrade.已执行;
|
||
td.UnWindDate = endDate;
|
||
}
|
||
result.KO = -r?.Amount;
|
||
}
|
||
else if (td.ExerciseDate <= endDate)
|
||
{
|
||
r = new trade_cash();
|
||
SettlementCalcCommons.SetPossibleExec(r, td, _prices[td.UnderlyingCode]);
|
||
result.Execute = -r.Amount;
|
||
result.ValueDate = td.ExerciseDate.Value;
|
||
if ((td.SettlementDate ?? td.ExerciseDate.GetValueOrDefault()) > endDate)
|
||
{
|
||
result = null;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
result = null;
|
||
}
|
||
}
|
||
break;
|
||
case "障碍期权":
|
||
{
|
||
r = null;
|
||
barrierService = barrierService ?? new BarrierOptionKnockioService(UserInfo);
|
||
var observDates = QdpHelper.ParseObservationDate(td.trade_barrier_option.ObservationDates)?.ToArray();
|
||
if (observDates == null)
|
||
{
|
||
observDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(
|
||
new Date(td.TradeDate), new Date(td.ExerciseDate.Value)).Where(O => O.DateTime != td.TradeDate).ToArray();
|
||
}
|
||
var lastDate = observDates.Where(O => O >= new Date(startDate) && O <= new Date(endDate)).OrderBy(O => (O)).FirstOrDefault();
|
||
if (lastDate != default)
|
||
{
|
||
switch (td.trade_barrier_option.Discrete)
|
||
{
|
||
case "离散":
|
||
switch (td.trade_barrier_option.BarrierType)
|
||
{
|
||
case "上升敲出":
|
||
td.trade_barrier_option.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
|
||
td.trade_barrier_option.KnockInOutDate = lastDate;
|
||
td.trade_barrier_option.KnockInOutNotional = td.Notional;
|
||
td.TradeStatus = ConsTrade.已平仓;
|
||
td.UnWindDate = lastDate;
|
||
r = barrierService.SetTradeCash(td, td.trade_barrier_option, lastDate, _prices[td.UnderlyingCode], td.trade_barrier_option.BarrierPrice, false);
|
||
break;
|
||
case "下降敲出":
|
||
td.trade_barrier_option.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
|
||
td.trade_barrier_option.KnockInOutDate = lastDate;
|
||
td.trade_barrier_option.KnockInOutNotional = td.Notional;
|
||
td.TradeStatus = ConsTrade.已平仓;
|
||
td.UnWindDate = lastDate;
|
||
r = barrierService.SetTradeCash(td, td.trade_barrier_option, lastDate, _prices[td.UnderlyingCode], td.trade_barrier_option.BarrierPrice, false);
|
||
break;
|
||
case "双障碍敲出":
|
||
td.trade_barrier_option.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
|
||
td.trade_barrier_option.KnockInOutDate = lastDate;
|
||
td.trade_barrier_option.KnockInOutNotional = td.Notional;
|
||
td.TradeStatus = ConsTrade.已平仓;
|
||
td.UnWindDate = lastDate;
|
||
r = barrierService.SetTradeCash(td, td.trade_barrier_option, lastDate, _prices[td.UnderlyingCode], td.trade_barrier_option.UpperBarrierPrice, false);
|
||
break;
|
||
}
|
||
break;
|
||
case "连续":
|
||
switch (td.trade_barrier_option.BarrierType)
|
||
{
|
||
case "上升敲出":
|
||
td.trade_barrier_option.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
|
||
td.trade_barrier_option.KnockInOutDate = lastDate;
|
||
td.trade_barrier_option.KnockInOutNotional = td.Notional;
|
||
td.TradeStatus = ConsTrade.已平仓;
|
||
td.UnWindDate = lastDate;
|
||
r = barrierService.SetTradeCash(td, td.trade_barrier_option, lastDate, _prices[td.UnderlyingCode], td.trade_barrier_option.BarrierPrice, false);
|
||
break;
|
||
case "下降敲出":
|
||
td.trade_barrier_option.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
|
||
td.trade_barrier_option.KnockInOutDate = lastDate;
|
||
td.trade_barrier_option.KnockInOutNotional = td.Notional;
|
||
td.TradeStatus = ConsTrade.已平仓;
|
||
td.UnWindDate = lastDate;
|
||
r = barrierService.SetTradeCash(td, td.trade_barrier_option, lastDate, _prices[td.UnderlyingCode], td.trade_barrier_option.BarrierPrice, false);
|
||
break;
|
||
case "双障碍敲出":
|
||
td.trade_barrier_option.KnockInOutStatus = ConsTrade.KnockState.KnockedOut;
|
||
td.trade_barrier_option.KnockInOutDate = lastDate;
|
||
td.trade_barrier_option.KnockInOutNotional = td.Notional;
|
||
td.TradeStatus = ConsTrade.已平仓;
|
||
td.UnWindDate = lastDate;
|
||
r = barrierService.SetTradeCash(td, td.trade_barrier_option, lastDate, _prices[td.UnderlyingCode], td.trade_barrier_option.UpperBarrierPrice, false);
|
||
break;
|
||
}
|
||
break;
|
||
}
|
||
result.KO = -r?.Amount;
|
||
if (td.trade_barrier_option.RebateType == "AtHit")
|
||
{
|
||
result.ValueDate = lastDate;
|
||
}
|
||
else if (td.trade_barrier_option.RebateType == "AtEnd" && td.ExerciseDate <= endDate)
|
||
{
|
||
result.ValueDate = td.ExerciseDate.Value;
|
||
}
|
||
else
|
||
{
|
||
result = null;
|
||
}
|
||
}
|
||
else if (td.ExerciseDate <= endDate)
|
||
{
|
||
td.TradeStatus = ConsTrade.已到期;
|
||
td.UnWindDate = td.ExerciseDate;
|
||
r = new trade_cash();
|
||
SettlementCalcCommons.SetPossibleExec(r, td, _prices[td.UnderlyingCode]);
|
||
result.Execute = -r.Amount;
|
||
result.ValueDate = td.ExerciseDate.Value;
|
||
if ((td.SettlementDate ?? td.ExerciseDate.GetValueOrDefault()) > endDate)
|
||
{
|
||
result = null;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
result = null;
|
||
}
|
||
}
|
||
break;
|
||
case "远期":
|
||
case "掉期":
|
||
{
|
||
if (td.ExerciseDate > endDate)
|
||
{
|
||
result = null;
|
||
break;
|
||
}
|
||
var variety = DataCacheProvider.GetVariety(td.UnderlyingCode);
|
||
|
||
result.Execute = ((td.Strike - _prices[td.UnderlyingCode]) * td.Notional * variety.CountRatio) ?? 0;
|
||
if ((td.BuySell == "卖出" && td.OptionType == "看涨")
|
||
|| (td.BuySell == "买入" && td.OptionType == "看跌"))
|
||
{
|
||
result.Execute *= -1;
|
||
}
|
||
result.ValueDate = td.ExerciseDate.Value;
|
||
result.TradeType = "远期";
|
||
if ((td.SettlementDate ?? td.ExerciseDate.GetValueOrDefault()) > endDate)
|
||
{
|
||
result = null;
|
||
}
|
||
}
|
||
break;
|
||
case "累计期权":
|
||
{
|
||
var payment = 0d;
|
||
var cashAtEnd = td.trade_accumulator_option.SettlementMode == "现金期末";
|
||
_prices.TryGetValue(td.UnderlyingCode, out var price);
|
||
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode);
|
||
var notional = td.trade_accumulator_option.AccumuTradeAmount * underlying.CountRatio;
|
||
if (cashAtEnd || td.trade_accumulator_option.SettlementMode == "实物交割")
|
||
{
|
||
td.trade_accumulator_option.SettlementMode = "现金当日";
|
||
}
|
||
result.ValueDate = startDate;
|
||
for (var dt = startDate; dt <= endDate; dt = dt.AddDays(1))
|
||
{
|
||
var checkResult = TradeModule.AccumulatorOptionModule.TradeAccumulatorService.CheckAccumulatorPayoff(td, td.trade_accumulator_option, dt, price, notional);
|
||
if (checkResult == null) continue;
|
||
if (checkResult.SettlementMode == "票息" || checkResult.SettlementMode == "现金当日")
|
||
{
|
||
payment += checkResult.PaymentAmount;
|
||
}
|
||
else if (checkResult.SettlementMode == "敲出")
|
||
{
|
||
if (td.trade_accumulator_option.EarlyTerminate)
|
||
{
|
||
result.ValueDate = dt;
|
||
result.KO = cashAtEnd || payment >= 0 ? 0 : -payment;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (dt >= td.ExerciseDate.Value)
|
||
{
|
||
result.ValueDate = dt;
|
||
result.Execute = payment >= 0 ? 0 : -payment;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
default:
|
||
result = null;
|
||
break;
|
||
}
|
||
if (result != null && result.TotleCash != 0)
|
||
{
|
||
if (td.PremiumPayDate.GetValueOrDefault() > td.TradeDate && td.PremiumPayDate.GetValueOrDefault() >= startDate && td.PremiumPayDate.GetValueOrDefault() <= endDate)
|
||
{
|
||
var tradeprice = td.TradePrice * (td.BuySell == "卖出" ? -1 : 1);
|
||
if (result.KO != 0)
|
||
{
|
||
result.KO += tradeprice;
|
||
}
|
||
if (result.Execute != 0)
|
||
{
|
||
result.Execute += tradeprice;
|
||
}
|
||
}
|
||
}
|
||
if (result == null)
|
||
{
|
||
//避免结构化交易装填判断错误;
|
||
td.TradeStatus = "确认成交";
|
||
}
|
||
return result;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 流动性风险
|
||
/// </summary>
|
||
public class FlowRiskModel
|
||
{
|
||
public int ParentLevel { get; set; }
|
||
public string EncryptId { get; set; }
|
||
public string TradeType { get; set; }
|
||
/// <summary>
|
||
/// 日期
|
||
/// </summary>
|
||
public DateTime? ValueDate { get; set; }
|
||
/// <summary>
|
||
/// 准备金
|
||
/// </summary>
|
||
public double TotleCash { get { return (KO ?? 0) + (Execute ?? 0); } }
|
||
/// <summary>
|
||
/// 交易编号
|
||
/// </summary>
|
||
public string TradeNumber { get; set; }
|
||
/// <summary>
|
||
/// 敲出
|
||
/// </summary>
|
||
public double? KO { get; set; }
|
||
/// <summary>
|
||
/// 到期
|
||
/// </summary>
|
||
public double? Execute { get; set; }
|
||
|
||
public FlowRiskModel()
|
||
{
|
||
this.KO = 0;
|
||
this.Execute = 0;
|
||
}
|
||
}
|
||
}
|