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

1875 lines
83 KiB
C#

using BaseOUDAL;
using Qdp.Foundation.Implementations;
using Qdp.Pricing.Base.Implementations;
using Qdp.Pricing.Base.Utilities;
using System.Linq;
using YLErp.Abstract.DataProviders;
using YLErp.Configuration.Enums;
using YLErp.CustomizedBizLogic;
using YLErp.DBModels.Consts;
using YLErp.Modules.DataProviderModule;
using YLErp.Modules.DictionaryModule;
using YLErp.Modules.SalesModule;
using YLErp.Modules.TradeModule.DealModule;
using YLErp.QdpModule;
namespace YLErp.Modules.TradeModule.AccumulatorOptionModule
{
/// <summary>
/// 累计期权
/// </summary>
public class TradeAccumulatorService : ExoticOptionModule.TradeCashServiceEx
{
public TradeAccumulatorService(OptUserInfo userInfo) : base(userInfo)
{
}
public TradeAccumulatorService(YLBaseService baseService) : base(baseService)
{
}
/// <summary>
/// 检查累计期权观察状态
/// 这个方法会还原累计换月数据
/// </summary>
public void CheckAccumulatorKnockInStatus(OtcTradeBase trade, trade_accumulator_option tradeAcc, DateTime valueDate, double closePrice)
{
if (trade is null)
{
throw new ArgumentNullException(nameof(trade));
}
if (tradeAcc is null)
{
throw new ArgumentNullException(nameof(tradeAcc));
}
//todo:远期无效以后应把关联的源交易关联数据删除掉
var query = from m in DbContext.autocall_observation
join t in DbContext.trade on m.ConvTradeNumber equals t.TradeNumber
where m.TradeId == trade.id && m.EndDate == valueDate && (m.SettlementMode == "转远期" || m.SettlementMode == "转掉期" || m.SettlementMode == "现金当日&转远期")
&& t.ValidState != ConsGlobal.InValid
select new { m.ConvTradeNumber };
var observation = query.FirstOrDefault();
if (observation != null)
{
throw new ServiceException($"操作失败,当前累计期权交易({trade.TradeNumber})在'{valueDate:yyyy-MM-dd}'已结算为远期交易({observation.ConvTradeNumber})");
}
if (trade.UnWindDate > valueDate)
{
throw new ServiceException("部分平仓或到期的累计期权不支持历史观察处理");
}
var tradeStatus = trade.TradeStatus;
var tdForwardList = new List<trade>();
var changeData = GetChangeDataOfSepecialDay(trade.id, valueDate);
CheckAccumulatorPayoff(trade, tradeAcc, valueDate, closePrice, tdForwardList, changeData);
if (tradeStatus != trade.TradeStatus)
{
//删除E/Bod_Trade记录
RemoveEodTradeAndFutureInfo(false, trade.id, valueDate);
var obs = DbContext.autocall_observation.Where(x => x.TradeId == trade.id && x.SettlementMode != "转远期" && x.SettlementMode != "转掉期" && x.EndDate > valueDate);
DbContext.autocall_observation.RemoveRange(obs);
}
}
/// <summary>
/// 检查结算状态
/// </summary>
public void CheckSettleStatus(DateTime valueDate, IEodPriceProviderV2 priceProvider, out List<trade> tdForwardList, DateTime? startDate = null, Action<OtcTrade, trade_accumulator_option> afterKnowInOut = null, IEnumerable<int> clienIds = null)
{
tdForwardList = null;
priceProvider ??= new EodPriceProvider(valueDate);
EodReferencePriceProvider eodReferencePriceProvider = null;
if (PS.Config.Is厦门象屿)
{
eodReferencePriceProvider = new EodReferencePriceProvider(valueDate);
}
if (startDate == null)
{
startDate = valueDate.AddYears(-5);
}
var query = from trade in DbContext.trade
join acc in DbContext.trade_accumulator_option on trade.id equals acc.TradeId
where trade.TradeDate > startDate.Value && trade.TradeDate <= valueDate && trade.ExerciseDate >= valueDate
&& trade.TradeType == "累计期权"
&& ConsTrade.确认成交 == trade.TradeStatus
&& trade.ValidState != ConsGlobal.InValid
&& trade.DividendDate < valueDate
&& !(
from ob in DbContext.autocall_observation
join tf in DbContext.trade on ob.ConvTradeNumber equals tf.TradeNumber
where ob.TradeId == trade.id && ob.EndDate == valueDate && (ob.SettlementMode == "转远期" || ob.SettlementMode == "转掉期" || ob.SettlementMode == "现金当日&转远期")
&& tf.ValidState != ConsGlobal.InValid
select ob
).Any()
select new
{
trade,
tradeAcc = acc
};
#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);
var cashList = (from cash in DbContext.ClientCashInCashOut
join update in DbContext.clientcashincashout_update
on cash.id equals update.ClientcashincashoutId
where
tradeIds.Contains(cash.TradeId ?? 0) &&
cash.HappenDate == valueDate &&
cash.Action != ClientCashInCashOut.系统操作_期权费 &&
update.State == "已确认"
select
cash.TradeId).ToHashSet();
tdForwardList = new List<trade>();
foreach (var tr in trades)
{
if (cashList.Contains(tr.trade.id))
{
continue;//已观察过且修改过资金的交易不再观察;
}
if (tr.trade.ExerciseDate < valueDate)
{
continue;//已到期交易不再观察;
}
//部分平仓或到期的累计期权历史收盘不需要再进行观察处理
if (!(tr.trade.UnWindDate > SystemValueDate))
{
tr.trade.trade_accumulator_option = tr.tradeAcc;
var tradeStatus = tr.trade.TradeStatus;
var changeData = GetChangeDataOfSepecialDay(tr.trade.id, valueDate);
var closePrice = double.NaN;
if (manuallyTradeObservationPrices.TryGetValue(tr.trade.id, out var manuallyTradeObservationPrice))
{
closePrice = manuallyTradeObservationPrice.Price ?? 0;
}
else
{
var UnderlyingCode = changeData?.UnderlyingCode ?? tr.trade.UnderlyingCode;
if (PS.Config.Is厦门象屿)
{
if (tr.trade.SettlementType == SettlementTypeEnum.ReferencePrice)
{//2022-05-17 象屿希望以参考价结算的交易,观察价格也优先使用参考价,不存在则使用收盘价;
if (eodReferencePriceProvider.TryGetPrice(UnderlyingCode, out var price))
{
closePrice = price;
}
else
{
throw new Exception($"[{tr.trade.TradeType}:{tr.trade.TradeNumber}]标的:{UnderlyingCode} 未找到参考价");
}
}
}
if (double.IsNaN(closePrice))
{
if (!priceProvider.TryGetEodPrice(UnderlyingCode, out var eodprice))
{
throw new Exception($"[{tr.trade.TradeType}:{tr.trade.TradeNumber}]标的:{UnderlyingCode} 未找到结算价");
}
else
{
closePrice = eodprice.ClosePrice;
}
}
}
CheckAccumulatorPayoff(tr.trade, tr.tradeAcc, valueDate, closePrice, tdForwardList, changeData);
if (tradeStatus != tr.trade.TradeStatus)
{
//删除E/Bod_Trade记录
RemoveEodTradeAndFutureInfo(false, tr.trade.id, valueDate);
}
if (afterKnowInOut != null)
{
afterKnowInOut(tr.trade, tr.tradeAcc);
}
DbContext.SaveChanges();
}
}
}
/// <summary>
/// 检查累计期权payoff
/// </summary>
private void CheckAccumulatorPayoff(OtcTradeBase td, trade_accumulator_option tdAcc, DateTime valueDate, double closePrice
, List<trade> tdForwardList, TradeAccumulatorChangeData changeData)
{
//验证上一个观察日是否已经观察过
var customObservDates = QdpHelper.ParseAutocallCustomizedInfo(tdAcc.KOObservationDates);
var koObservationDates = customObservDates.Item1;
koObservationDates ??= CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(td.TradeDate.Value, td.ExerciseDate.Value).ToArray();
var valueDateIndex = Array.IndexOf(koObservationDates, new Date(valueDate));
if (valueDateIndex >= 1)
{
var lastValueDate = koObservationDates[valueDateIndex - 1];
if (!DbContext.autocall_observation.Any(x => x.TradeId == td.id && x.EndDate == lastValueDate.DateTime))
{
throw new ServiceException($"{td.TradeNumber}累计期权需要按顺序观察,请先在{lastValueDate.DateTime.ToString("yyyy-MM-dd")}进行观察或收盘处理");
}
}
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode);
var notional = tdAcc.AccumuTradeAmount * underlying.CountRatio;
CheckResult result = null;
if (changeData != null)
{
//关键点:还原换月设置到交易时不要更新当前传入的trade和tradeAcc,因为这两个数据涉及到数据库更新
var tdClone = new trade();
YLAutoMapper.Map(td, tdClone);
var tdAccClone = tdAcc.Clone();
tdClone.trade_accumulator_option = tdAccClone;
TradeAccumulatorHelper.RestoreAccumulatorChangeData(tdClone, tdAccClone, changeData);
result = CheckAccumulatorPayoff(tdClone, tdAccClone, valueDate, closePrice, notional);
}
else
{
result = CheckAccumulatorPayoff(td, tdAcc, valueDate, closePrice, notional);
}
if (result != null)
{
using (var trans = DbContext.Database.BeginTransaction())
{
trade_cash tc = null;
var action = "部分行权";
var exerciseWay = string.Empty;
if (valueDate.Date == td.ExerciseDate.Value.Date)
{
action = "到期行权";
exerciseWay = TradeCashExerciseWayEnum.到期行权;
}
if (result.SettlementMode == "敲出")
{
tdAcc.KnockOutDate = valueDate;
if (tdAcc.EarlyTerminate)
{
var req = new OptionTradeKnockoutRequest
{
ValueDate = valueDate,
IgnoreLaterCashRecords = false,
PaymentAmount = 0,
UnderlyingPrice = closePrice
};
var exeResult = new OptionTradeKnockoutService(this).Execute(td, tdAcc, req, trans);
tc = exeResult.TradeCash;
td.SetFullClosedStatus(valueDate, ConsTrade.已平仓);
//生成确认书
if (PS.Config.IsAutoGenerateContracts)
{
//修改销售提成的状态
new SalesCommissionDataService(this).SetCommissionVaild(td.id);
new TradeContractGenerateService(this).GenerateContractsAsync(new List<int> { td.id });
}
}
else
{
if (valueDate.Date != td.ExerciseDate.Value.Date || tdAcc.SettlementMode != "现金期末")
{
tc = InnerSaveCash(trade: td, valueDate: valueDate, happenDate: valueDate,
cashAction: action, exerciseWay: exerciseWay,
closePrice: closePrice, paymentAmount: 0, isKnockOut: true, isLastAction: valueDate.Date == td.ExerciseDate.Value.Date);
}
}
}
else
{
tdAcc.KnockOutDate = null;
if (result.ForwardTrade != null) //转远期
{
SaveForwardTrade(result.ForwardTrade);
tdForwardList.Add(result.ForwardTrade);
tc = InnerSaveCash(trade: td, valueDate: result.PaymentDate, happenDate: result.PaymentDate,
cashAction: action, exerciseWay: exerciseWay,
closePrice: closePrice, paymentAmount: result.SettlementMode == "现金当日&转远期" ? result.PaymentAmount : 0, isKnockOut: false, isLastAction: valueDate.Date == td.ExerciseDate.Value.Date);
}
else if (result.SettlementMode == "票息" || result.SettlementMode == "现金当日")
{
tc = InnerSaveCash(trade: td, valueDate: result.PaymentDate, happenDate: result.PaymentDate,
cashAction: action, exerciseWay: exerciseWay,
closePrice: closePrice, paymentAmount: result.PaymentAmount, isKnockOut: false, isLastAction: valueDate.Date == td.ExerciseDate.Value.Date);
}
else if ((result.SettlementMode == "现金期末" || result.SettlementMode == "未知区间") && valueDate.Date != td.ExerciseDate.Value.Date)
{
tc = InnerSaveCash(trade: td, valueDate: result.PaymentDate, happenDate: result.PaymentDate,
cashAction: action, exerciseWay: exerciseWay,
closePrice: closePrice, paymentAmount: 0, isKnockOut: false, isLastAction: false);
}
}
DbContext.SaveChanges();
SaveObservation(trade: td, cashId: tc?.id, valueDate: valueDate, result: result);
//这里要保存,否则下面查询的时候,查不到最后一天的票息记录.
DbContext.SaveChanges();
trans.Commit();
}
if (td.TradeStatus == ConsTrade.已平仓)
{
return;
}
}
//到期日生成票息资金记录
if (valueDate.Date == td.ExerciseDate.Value.Date)
{
if (tdAcc.SettlementMode == "现金期末")
{
var paymentAmount = DbContext.autocall_observation.Where(n => n.TradeId == td.id && n.SettlementMode == "现金期末")
.Select(n => n.PaymentAmount).AsEnumerable().DefaultIfEmpty(0).Sum();
//根据买方角度计算盈亏
paymentAmount *= ConsGlobal.CallPut.IsCall(td.OptionType) ? closePrice - result.Strike : result.Strike - closePrice;
paymentAmount = Commons.OtcFormatHelper.FormatValue(paymentAmount, 2);
if (td.BuySell != "买入")
{
paymentAmount = -paymentAmount;
}
InnerSaveCash(trade: td, valueDate: valueDate, happenDate: valueDate
, cashAction: ClientCashInCashOut.系统操作_行权费, exerciseWay: TradeCashExerciseWayEnum.到期行权
, closePrice: closePrice, paymentAmount: paymentAmount, isKnockOut: false, isLastAction: true);
}
td.SetFullClosedStatus(valueDate, ConsTrade.已到期);
//生成确认书
if (PS.Config.IsAutoGenerateContracts)
{
//修改销售提成的状态
new SalesCommissionDataService(this).SetCommissionVaild(td.id);
new TradeContractGenerateService(this).GenerateContractsAsync(new List<int> { td.id });
}
}
}
/// <summary>
/// 结算方式选择“现金结算”,在每个观察日,系统自动计算收益并派发资金
/// </summary>
public static CheckResult CheckAccumulatorPayoff(OtcTradeBase td, trade_accumulator_option tdAcc, DateTime valueDate, double closePrice, double notional)
{
if (td is null || tdAcc is null)
{
return null;
}
if (tdAcc.AccumulatorStructureType == AccumulatorStructureTypeEnum.Segmented)
{
return new InnerCheck2(td, tdAcc, valueDate, closePrice, notional).CheckOption();
}
return new InnerCheck(td, tdAcc, valueDate, closePrice, notional).CheckOption();
}
/// <summary>
/// 保存
/// </summary>
/// <param name="ftd"></param>
private void SaveForwardTrade(trade ftd)
{
ftd.CreateDate = DateTime.Now;
ftd.CreatorId = UserId;
ftd.CreatorName = UserName;
DbContext.trade.Add(ftd);
ftd.TradeNumber = BizLogicSingleton.Instance.GenerateTradeNumberBeforeConfirm(ftd, DbContext);
var OptionType = ftd.OptionType;
var BuySell = ftd.BuySell;
if (PS.Config.ErpElement.OptionTypeAndBuySell == OptionTypeAndBuySell.固定多头判断买卖)
{
if (ftd.BuySell == "卖出" && ftd.OptionType == "看跌")
{
BuySell = "买入";
}
if (ftd.BuySell == "买入" && ftd.OptionType == "看跌")
{
BuySell = "卖出";
}
OptionType = "看涨";
}
else if (PS.Config.ErpElement.OptionTypeAndBuySell == OptionTypeAndBuySell.固定买入判断多空)
{
if (ftd.BuySell == "卖出" && ftd.OptionType == "看涨")
{
OptionType = "看跌";
}
if (ftd.BuySell == "卖出" && ftd.OptionType == "看跌")
{
OptionType = "看涨";
}
BuySell = "买入";
}
else if (PS.Config.ErpElement.OptionTypeAndBuySell == OptionTypeAndBuySell.固定卖出判断多空)
{
if (ftd.BuySell == "买入" && ftd.OptionType == "看涨")
{
OptionType = "看跌";
}
if (ftd.BuySell == "买入" && ftd.OptionType == "看跌")
{
OptionType = "看涨";
}
BuySell = "卖出";
}
ftd.OptionType = OptionType;
ftd.BuySell = BuySell;
DbContext.SaveChanges();
InnerSaveCash(trade: ftd, valueDate: ftd.TradeDate.Value, happenDate: ftd.TradeDate.Value
, cashAction: ClientCashInCashOut.系统操作_期权费, exerciseWay: null
, closePrice: 0, paymentAmount: 0, isKnockOut: false, isLastAction: false);
//生成确认书
if (PS.Config.IsAutoGenerateContracts)
{
//修改销售提成的状态
new SalesCommissionDataService(this).SetCommissionVaild(ftd.id);
new TradeContractGenerateService(this).GenerateContractsAsync(new List<int> { ftd.id }, "确认书");
}
var forward = ftd.trade_forward;
forward.TradeId = ftd.id;
DbContext.trade_forward.Add(forward);
foreach (var kv in ftd.MetaDic)
{
DbContext.TradeMeta.Add(new TradeMeta { TradeId = ftd.id, MetaKey = kv.Key, MetaValue = kv.Value, CreateTime = DateTime.Now });
}
}
/// <summary>
/// 保存观察数据
/// </summary>
private void SaveObservation(OtcTradeBase trade, int? cashId, DateTime valueDate, CheckResult result)
{
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,
};
DbContext.autocall_observation.Add(observationRecord);
}
observationRecord.CashId = cashId;
observationRecord.StartDate = result.StartDate;
observationRecord.EndDate = valueDate.Date;
observationRecord.StockEqvNotional = result.PaymentNotional * (trade.SpotPrice ?? 0);
observationRecord.CouponRate = 0;
observationRecord.PaymentDate = valueDate;
observationRecord.ConvTradeNumber = null;
observationRecord.SettlementMode = result.SettlementMode;
observationRecord.PaymentAmount = result.PaymentAmount;
observationRecord.PaymentNotional = result.PaymentNotional;
if (result.ForwardTrade != null)
{
if (result.SettlementMode == "现金当日&转远期")
{
observationRecord.SettlementMode = "现金当日&转远期";
observationRecord.ConvTradeNumber = result.ForwardTrade.TradeNumber;
}
else
{
observationRecord.SettlementMode = "转远期";
observationRecord.ConvTradeNumber = result.ForwardTrade.TradeNumber;
}
}
else
{
observationRecord.CouponRate = result.PaymentNotional != 0 ? result.PaymentAmount / result.PaymentNotional : 0;
observationRecord.PaymentDate = result.PaymentDate;
}
}
/// <summary>
/// 保存资金记录
/// </summary>
private trade_cash InnerSaveCash(OtcTradeBase trade, DateTime happenDate, DateTime valueDate, string cashAction, string exerciseWay,
double closePrice, double paymentAmount, bool isLastAction, bool isKnockOut)
{
return SaveCash(trade: trade, cashAction: cashAction,
exerciseWay: exerciseWay, paymentAmount: paymentAmount,
valueDate: valueDate, underlyingPrice: closePrice,
happenDate: happenDate, isKnockOut: isKnockOut, isLastAction: isLastAction);
}
public TradeAccumulatorChangeData GetChangeDataOfSepecialDay(int tradeId, DateTime valueDate)
{
var query = from a in DbContext.TradeAction.AsNoTracking()
where a.IsValid && a.TradeId == tradeId && a.ValueDate <= valueDate && a.ActionType == TradeActionType.AccumulatorChange
orderby a.ValueDate descending
select a.ActionData;
var actionData = query.FirstOrDefault();
return JsonHelper.Deserialize<TradeAccumulatorChangeData>(actionData);
}
}
public class CheckResult
{
/// <summary>
/// 结算方式(敲出|转远期|现金当日|现金期末)
/// </summary>
public string SettlementMode { get; set; }
/// <summary>
/// 观察开始日
/// </summary>
public DateTime StartDate { get; set; }
/// <summary>
/// 转远期
/// </summary>
public trade ForwardTrade { get; set; }
/// <summary>
/// 支付日
/// </summary>
public DateTime PaymentDate { get; set; }
/// <summary>
/// 交易员角度的支付金额或数量(实际是份额)
/// </summary>
public double PaymentAmount { get; set; }
/// <summary>
/// 结算份额(现在只有 累计期权 现金实物合并结算 结算方式有在用)
/// </summary>
public double PaymentNotional { get; set; }
/// <summary>
/// 执行价格
/// </summary>
public double Strike { get; set; }
/// <summary>
/// 障碍价格
/// </summary>
public double? Barrier { get; set; }
/// <summary>
/// 数量和份额的乘积因子
/// </summary>
public int CountRatio { get; set; }
}
//------------------------------------------------------------
// 2021-10-28:以下转远期的交割价和期初标的价格先按照中粮的需求来(交割价和期初标的价格取累积期权的执行价)
// 如果不按照中粮的需求来,期初标的价格取日终结算价格
//------------------------------------------------------------
public class InnerChecksBase
{
protected OtcTradeBase _td;
protected trade_accumulator_option _tdAcc;
protected DateTime _valueDate;
protected double _closePrice;
protected double _notional;
protected bool _updownPriceMode;
private readonly ErpBaseContext basedb = new();
protected InnerChecksBase(OtcTradeBase td, trade_accumulator_option tdAcc, DateTime valueDate, double closePrice, double notional)
{
_td = td ?? throw new ArgumentNullException(nameof(td));
_tdAcc = tdAcc ?? throw new ArgumentNullException(nameof(tdAcc));
_valueDate = valueDate;
_closePrice = closePrice;
_notional = notional;
//累计三段式 暂时不使用该配置(原因与需求临界值有冲突)
_updownPriceMode = GlobalConfig.AccumulatorCriticalMode == "updown";
}
//票息率(绝对数值)
protected (double, double) getCouponRate(double[] customCoupons, int valueDateIndex)
{
var couponRate = _tdAcc.Coupon;
var couponRate2 = _tdAcc.Coupon2;
if (customCoupons != null && customCoupons.Length > valueDateIndex)
{
couponRate = customCoupons[valueDateIndex];
}
return (_tdAcc.CouponPercent ? couponRate * (_td.SpotPrice ?? 0) : couponRate, _tdAcc.CouponPercent ? (couponRate2 ?? 0) * (_td.SpotPrice ?? 0) : (couponRate2 ?? 0));
}
//年化比例
protected double getDayCountFraction(DateTime startDate, DateTime settleDate, bool includeStartDate)
{
if (_tdAcc.IsFixedCoupon)
{
return 1;
}
var couponDayCount = (_tdAcc.CouponDayCount?.Trim() ?? "Act365").ToDayCountImpl();
if (startDate == settleDate)
{
return couponDayCount.OneDayFraction();
}
var dayCountFraction = couponDayCount.CalcDayCountFraction(startDate, settleDate);
if (includeStartDate && _td.StartDate == startDate)
{
return dayCountFraction + couponDayCount.OneDayFraction();
}
return dayCountFraction;
}
//结算日期
protected DateTime GetSettleDate(int valueDateIndex)
{
var settleDates = QdpHelper.ParseObservationDate(_tdAcc.KOObservationSettleDates);
if (settleDates != null && settleDates.Length > valueDateIndex)
{
var date = settleDates[valueDateIndex];
return QdpCalendarHelper.GetNonHoliday(date);
}
return _valueDate;
}
/// <summary>
/// 转远期交易
/// </summary>
/// <param name="forwardStrike">执行价格</param>
/// <param name="notional">数量</param>
/// <param name="ForwardDateTypeStr">远期到期日类型</param>
/// <param name="ForwardPriceTypeStr">远期期初价类型</param>
protected trade ConvertToForward(double forwardStrike, double notional, string ForwardDateTypeStr, string ForwardPriceTypeStr)
{
var ftd = new trade();
YLAutoMapper.Map(_td, ftd);
//标的信息
var um = DataCacheProvider.GetUnderlyingDataSource().GetData(ftd.UnderlyingCode);
if (um == null)
{
throw new Exception($"累计期权'{_td.TradeNumber}'找不到标的信息:{ftd.UnderlyingCode},(转远期)失败");
}
ftd.id = 0;
ftd.ParentTradeId = 0;
ftd.IsGroup = 0;
ftd.TradeNumber = null;
ftd.TradeType = "远期";
ftd.StructureType = "远期";
ftd.StructureIntroduction = null;
ftd.ExtendInfo = null;
ftd.ExerciseMode = null;
ftd.TradeStatus = ConsTrade.确认成交;
ftd.Comments += "累计期权:" + _td.TradeNumber;
var date = um.MaturityDate.GetValueOrDefault(DateTime.MaxValue);
//交易日期
switch (ForwardDateTypeStr)
{
case "标的到期日":
ftd.ExerciseDate = um.MaturityDate;
break;
case "期权到期日":
ftd.ExerciseDate = _td.ExerciseDate;
break;
default:
var dictionarySerivice = new DictionarySerivice();
var dic = dictionarySerivice.GetForwardDateDic();
var dicModel = dic.FirstOrDefault(l => l.Value == ForwardDateTypeStr);
if (dicModel.Value != null)
{
var model = basedb.DictionaryItems.Find(dicModel.Key);
var lastMonth = Convert.ToInt32(model.Name);
if (model.ShortName.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries).Any())
{
var lastDay = Convert.ToInt32(model.ShortName.Split(",")[0]);
var typeEnum = (ForwardDateTypeEnum)Convert.ToInt32(model.ShortName.Split(",")[1]);
var isbefore = true;
if (Convert.ToInt32(model.ShortName.Split(",")[1]) > 0)
{
isbefore = model.ShortName.Split(",")[2] == "0";
}
ftd.ExerciseDate = dictionarySerivice.GetForwardDateType(date, lastMonth, lastDay, typeEnum, isbefore);
}
}
else
{
ftd.ExerciseDate = _td.ExerciseDate;
}
break;
}
////远期交易到期日(中粮需求:转远期时交易到期日取标的到期日)
//if (PS.Config.Company == Configuration.CompanyEnum.中粮)
//{
// ftd.ExerciseDate = um.MaturityDate;
// ftd.SettlementDate = um.MaturityDate;
//}
ftd.SettlementDate = ftd.ExerciseDate;
ftd.TradeDate = _valueDate;
ftd.StartDate = _valueDate;
ftd.CreateDate = DateTime.Now;
//价格
ftd.IsMoneynessOption = null;
ftd.Strike = forwardStrike;
//期初价格
switch (ForwardPriceTypeStr)
{
case "期权期初价格":
ftd.SpotPrice = _td.SpotPrice;
break;
case "远期开仓现价":
ftd.SpotPrice = _closePrice;
break;
case "期权成交日收盘价":
//标的收盘价
var dataSource = new EodPriceProvider(_td.StartDate.Value);
ftd.SpotPrice = dataSource.GetPrice(ftd.UnderlyingCode, SettlementTypeEnum.ClosePrice);
break;
case "期权行权价格":
default:
ftd.SpotPrice = ftd.Strike;
break;
}
if (_tdAcc.SettlementMode == "现金实物合并结算")
{
ftd.Strike = ftd.SpotPrice;
}
//开仓费用
ftd.TradePrice = 0;
ftd.TradeSinglePrice = 0;
ftd.PremiumRate = 0;
ftd.PremiumPayDate = null;
ftd.IsUsePremiumRate = false;
ftd.IsTradePricePayType = false;
ftd.IsAnnualized = false;
ftd.AnnualizeFactor = null;
ftd.ParticipationRate = null;
ftd.PrincipalRate = null;
//todo:保底收益率
//ftd.PrincipalSum = null;
//ftd.PrincipalRateWrite = null;
//ftd.SinglePrincipalWrite = null;
//ftd.OriginalPrincipalSum = null;
//ftd.OriginalPrincipalSum = null;
//预付金
ftd.InitialMargin = null;
ftd.InitialMarginRatio = null;
ftd.MarginRate = 0;
ftd.MarginTemplateName = null;
ftd.MarginType = DBModels.Enums.MarginTypeEnum.DEFAULT;
ftd.PositionMarginRate = 0;
ftd.CalcFlag = 0;
//基差
ftd.BasisGap = null;
ftd.BasisUnderlyingCode = null;
ftd.BasisUnderlyingId = null;
//交易数量及名义本金
ftd.Notional = notional;
ftd.Lots = ftd.Notional / um.ContractSize;
ftd.TradeAmount = notional / um.CountRatio;
ftd.OriginalNotional = ftd.Notional;
ftd.StockEqvNotionalMax = null;
ftd.OriginalStockEqvNotional = ftd.StockEqvNotionalReal = ftd.StockEqvNotional = Math.Abs(ftd.Notional * (ftd.SpotPrice ?? 0));
if (_td.MetaDic == null || _td.MetaDic.Count == 0)
{
_td.MetaDic = new TradeMetaService(OptUserInfo.SystemUser).GetTradeMeta(_td.id);
}
if (_td.MetaDic.TryGetValue("组合标的", out var value) && value?.Length > 2)
{
var synthetic = JsonHelper.Deserialize<SyntheticPriceModel>(value);
if (PS.Config.Company == Configuration.CompanyEnum.浙期)
{
var arr = synthetic.SuList.OrderByDescending(O => O.Price).Select(O => O).ToArray();
var price = arr[0].Price;
for (var i = 1; i < arr.Length - 1; i++)
{
price -= arr[i].Price * (arr[i].Coefficient > 0 ? 1 : -1);
}
arr[arr.Length - 1].Price = price + ftd.SpotPrice.Value * (arr[arr.Length - 1].Coefficient > 0 ? 1 : -1);
synthetic.Price = ftd.SpotPrice.Value;
}
else
{
var suList = new Dictionary<string, double>();
var suList2 = new Dictionary<string, double>();
foreach (var item in synthetic.SuList)
{
if (ForwardPriceTypeStr == "期权行权价格" || ForwardPriceTypeStr == "远期开仓现价")
{
//标的收盘价
var dataSource = new EodPriceProvider(ftd.StartDate.Value);
item.Price = dataSource.GetPrice(item.UnderlyingCode, SettlementTypeEnum.ClosePrice);
}
if (ForwardPriceTypeStr == "期权成交日收盘价")
{
//标的收盘价
var dataSource = new EodPriceProvider(_td.StartDate.Value);
item.Price = dataSource.GetPrice(item.UnderlyingCode, SettlementTypeEnum.ClosePrice);
}
suList.Add(item.UnderlyingCode, item.Price);
suList2.Add(item.UnderlyingCode, item.Price * item.Coefficient);
}
suList = suList.OrderBy(n => n.Value).ToDictionary(p => p.Key, o => o.Value);
var minUnderlyingCode = suList.FirstOrDefault().Key;
suList.Remove(minUnderlyingCode);
suList2.Remove(minUnderlyingCode);
var totalPrice = suList2.Sum(n => n.Value);
if (ftd.SpotPrice.Value > totalPrice)
{
synthetic.SuList.FirstOrDefault(n => n.UnderlyingCode == minUnderlyingCode).Price = ftd.SpotPrice.Value + (suList2.Sum(n => n.Value) * -1);
}
else
{
synthetic.SuList.FirstOrDefault(n => n.UnderlyingCode == minUnderlyingCode).Price = (ftd.SpotPrice.Value * -1) + suList2.Sum(n => n.Value);
}
}
ftd.MetaDic["组合标的"] = synthetic.ToJson();
}
if (_td.MetaDic.TryGetValue("中央对手方清算", out var value2) && value2?.Length > 0)
{
ftd.MetaDic["中央对手方清算"] = value2;
}
if (_td.MetaDic.TryGetValue("中央清算平台", out var value3) && value3?.Length > 0)
{
ftd.MetaDic["中央清算平台"] = value3;
}
if (_td.MetaDic.TryGetValue("交易平台", out var value4) && value4?.Length > 0)
{
ftd.MetaDic["交易平台"] = value4;
}
if (_td.MetaDic.TryGetValue("ExchangeRate", out var value5) && value5?.Length > 0)
{
ftd.MetaDic["ExchangeRate"] = value5;
}
if (PS.Config.Is浙期 && um.IsCommoditySpot())
{
ftd.MetaDic["ActualExerciseDate"] = ftd.ExerciseDate?.ToString("yyyy-MM-dd");
}
//标的价格1,标的价格2
ftd.MetaDic["期初信息"] = $"[{ftd.SpotPrice:0.0###}]";
if (PS.Config.ErpElement.ForwardValueIsSupplyOrPay == YLErp.Configuration.Enums.ForwardValueIsSupplyOrPay.Type2)
{
ftd.MetaDic.Add("IsSupplyForwardValue", "-1");
}
else
{
ftd.MetaDic.Add("IsSupplyForwardValue", "0");
}
//累计转远期时,存一下累计期权的编号
ftd.MetaDic.Add("累计期权交易编号", _td.TradeNumber);
//波动率/分红率
ftd.Vol = null;
ftd.VolType = null;
ftd.TradeOpenVolatility = null;
ftd.TradeCloseVolatility = null;
ftd.TradeSavedVol = null;
ftd.YesterDayTradeSavedVol = null;
ftd.NumOfSmoothingDays = null;
ftd.DividendRatio = 0;
ftd.DividendDate = DateTime.MinValue;
//确认书
ftd.ContractCode = null;
ftd.ContractVersion = null;
ftd.IsSingleContract = null;
ftd.SentMailCount = null;
//审核
ftd.CheckStatus = null;
ftd.CheckTradeUpdate = null;
ftd.ProcessOptDate = null;
ftd.ProcessOrderId = 0;
ftd.ProcessStatus = null;
//unwind
ftd.UnWindDate = null;
ftd.UnWindFee = ftd.UnWindNotional = ftd.UnWindPrice = null;
ftd.UnWindTimes = 0;
ftd.UnWindTotalAmount = null;
ftd.UnWindTradePrice = null;
ftd.UnWindUnderlyingPrice = null;
ftd.FinalPrice = null;
ftd.HasPartialUnWind = null;
//配对交易
ftd.PairTrade = ftd.PairTradeNumber = null;
var forward = new trade_forward
{
AnnualMarginRate = 0,
AnnualStoragePrice = 0,
ForwardValue = ((ftd.SpotPrice - ftd.Strike) * ftd.Notional * ((ftd.BuySell == "买入" && ftd.OptionType == "看涨") || (ftd.BuySell == "卖出" && ftd.OptionType == "看跌") ? 1 : -1)) ?? 0,
ObservationDates = null,
OpenCommission = 0,
OptDate = DateTime.Now,
OptId = 0,
OptName = "系统",
TradeId = 0
};
//forward.ForwardValue = new ForwardTradeImportService(null).GetForwardValue(forwardSpotPrice, forwardStrike, ftd.TradeAmount, um.CountRatio, ftd.OptionType, ftd.BuySell);
ftd.trade_forward = forward;
return ftd;
}
}
/// <summary>
/// 内部检查封装类,这个类不涉及到对交易数据的修改仅获取检查结果
/// </summary>
public class InnerCheck : InnerChecksBase
{
public InnerCheck(OtcTradeBase td, trade_accumulator_option tdAcc, DateTime valueDate, double closePrice, double notional) : base(td, tdAcc, valueDate, closePrice, notional)
{
}
/// <summary>
/// 累购期权
/// </summary>
/// <param name="strike">行权价</param>
/// <param name="barrier">障碍价</param>
/// <param name="koObservationDates">观察日</param>
/// <param name="couponCalcInfo">票息</param>
/// <param name="settleMode">观察状态</param>
/// <param name="paymentDate">支付日期</param>
/// <param name="paymentNotional">支付数量</param>
/// <param name="paymentAmount">支付总额</param>
/// <param name="forwardTrade">远期交易</param>
private void CheckCallOption(double strike, double barrier, Date[] koObservationDates, Lazy<CouponCalcInfo> lazyCouponCalcInfo,
out string settleMode, out DateTime paymentDate, out double paymentNotional, out double paymentAmount, out trade forwardTrade)
{
forwardTrade = null;
paymentDate = _valueDate;
//超出上限则敲出
if (_closePrice > barrier || (_updownPriceMode && Math.Abs(_closePrice - barrier) < 1e-6))
{
settleMode = "敲出";
paymentAmount = paymentNotional = 0;
}
//低于下限(执行价格)则买方赔付(使用看跌乘数)
else if (_closePrice < strike || (_updownPriceMode && Math.Abs(_closePrice - strike) < 1e-6))
{
paymentNotional = _tdAcc.PutMultiplier * _notional;
//子弹类型到期日当天赔付数量膨胀
if (_tdAcc.AccumuType == "子弹" && _td.ExerciseDate == _valueDate)
{
paymentNotional *= koObservationDates.Length;
}
if (string.IsNullOrWhiteSpace(_tdAcc.SettlementMode) || _tdAcc.SettlementMode == "现金当日")
{
settleMode = "现金当日";
//赔付现金,当标的资产价格低于行权价时,赔付看跌乘数倍的看跌期权损失
paymentAmount = (_closePrice - strike) * paymentNotional;
}
else if (_tdAcc.SettlementMode == "现金期末")
{
settleMode = "现金期末";
paymentAmount = paymentNotional;
}
else if (_tdAcc.SettlementMode == "实物交割")
{
settleMode = "转远期";
paymentAmount = paymentNotional;
//赔付的时候交割价使用执行价即可
forwardTrade = ConvertToForward(forwardStrike: strike, notional: paymentNotional, ForwardDateTypeStr: _tdAcc.ForwardDateType, ForwardPriceTypeStr: _tdAcc.ForwardPriceType);
}
else if (_tdAcc.SettlementMode == "现金实物合并结算")
{
settleMode = "现金当日&转远期";
paymentAmount = (_closePrice - strike) * paymentNotional;
//赔付的时候交割价使用执行价即可
forwardTrade = ConvertToForward(forwardStrike: strike, notional: paymentNotional, ForwardDateTypeStr: _tdAcc.ForwardDateType, ForwardPriceTypeStr: _tdAcc.ForwardPriceType);
}
else
{
throw new ServiceException("未知的结算方式:" + _tdAcc.SettlementMode);
}
}
//在上下限之间则买方获得收益(使用看涨乘数)
else
{
paymentNotional = _tdAcc.CallMultiplier * _notional;
if (_tdAcc.SettlementMode == "实物交割")
{
if (_tdAcc.AccumuType == "区间现金结算")
{
if (_tdAcc.PayoffType == "固定")
{
settleMode = "票息";
//固定:当标的资产价格高于行权价且低于障碍价格时,获得票息收益
paymentAmount = lazyCouponCalcInfo.Value.singleCoupon * paymentNotional;
paymentDate = lazyCouponCalcInfo.Value.settleDate;
}
else
{
settleMode = "现金当日";
//当标的资产价格高于行权价且低于障碍价格时,买方获得看涨乘数倍的看涨期权收益
paymentAmount = (_closePrice - strike) * paymentNotional;
}
}
else
{
settleMode = "转远期";
paymentAmount = paymentNotional;
forwardTrade = ConvertToForward(forwardStrike: strike, notional: paymentNotional, ForwardDateTypeStr: _tdAcc.ForwardDateType, ForwardPriceTypeStr: _tdAcc.ForwardPriceType);
}
}
else if (_tdAcc.SettlementMode == "现金实物合并结算")
{
if (_tdAcc.AccumuType == "区间现金结算")
{
if (_tdAcc.PayoffType == "固定")
{
settleMode = "票息";
//固定:当标的资产价格高于行权价且低于障碍价格时,获得票息收益
paymentAmount = lazyCouponCalcInfo.Value.singleCoupon * paymentNotional;
paymentDate = lazyCouponCalcInfo.Value.settleDate;
}
else
{
settleMode = "现金当日";
//当标的资产价格高于行权价且低于障碍价格时,买方获得看涨乘数倍的看涨期权收益
paymentAmount = (_closePrice - strike) * paymentNotional;
}
}
else
{
settleMode = "现金当日&转远期";
if (_tdAcc.PayoffType == "固定")
{
paymentAmount = lazyCouponCalcInfo.Value.singleCoupon * paymentNotional;
}
else
{
paymentAmount = (_closePrice - strike) * paymentNotional;
}
forwardTrade = ConvertToForward(forwardStrike: strike, notional: paymentNotional, ForwardDateTypeStr: _tdAcc.ForwardDateType, ForwardPriceTypeStr: _tdAcc.ForwardPriceType);
}
}
//现金-固定票息
else if (_tdAcc.PayoffType == "固定")
{
settleMode = "票息";
//固定:当标的资产价格高于行权价且低于障碍价格时,获得票息收益
paymentAmount = lazyCouponCalcInfo.Value.singleCoupon * paymentNotional;
paymentDate = lazyCouponCalcInfo.Value.settleDate;
}
//现金-浮动收益
else if (_tdAcc.SettlementMode == "现金期末")
{
settleMode = "现金期末";
paymentAmount = paymentNotional;
}
else if (string.IsNullOrWhiteSpace(_tdAcc.SettlementMode) || _tdAcc.SettlementMode == "现金当日")
{
settleMode = "现金当日";
//当标的资产价格高于行权价且低于障碍价格时,买方获得看涨乘数倍的看涨期权收益
paymentAmount = (_closePrice - strike) * paymentNotional;
}
else
{
throw new ServiceException("未知的结算方式:" + _tdAcc.SettlementMode);
}
}
}
/// <summary>
/// 累沽期权
/// </summary>
/// <param name="strike">行权价</param>
/// <param name="barrier">障碍价</param>
/// <param name="koObservationDates">观察日</param>
/// <param name="couponCalcInfo">票息</param>
/// <param name="settleMode">观察状态</param>
/// <param name="paymentDate">支付日期</param>
/// <param name="paymentNotional">支付数量</param>
/// <param name="paymentAmount">支付总额</param>
/// <param name="forwardTrade">远期交易</param>
private void CheckPutOption(double strike, double barrier, Date[] koObservationDates, Lazy<CouponCalcInfo> couponCalcInfo,
out string settleMode, out DateTime paymentDate, out double paymentNotional, out double paymentAmount, out trade forwardTrade)
{
forwardTrade = null;
paymentDate = _valueDate;
//低于下限则敲出
if (_closePrice < barrier || (_updownPriceMode && Math.Abs(_closePrice - barrier) < 1e-6))
{
settleMode = "敲出";
paymentAmount = paymentNotional = 0;
}
//超过上限(执行价)则买方赔付(使用看涨乘数)
else if (_closePrice > strike || (_updownPriceMode && Math.Abs(_closePrice - strike) < 1e-6))
{
paymentNotional = _tdAcc.CallMultiplier * _notional;
//子弹类型到期日当天赔付数量膨胀
if (_tdAcc.AccumuType == "子弹" && _td.ExerciseDate == _valueDate)
{
paymentNotional *= koObservationDates.Length;
}
if (string.IsNullOrWhiteSpace(_tdAcc.SettlementMode) || _tdAcc.SettlementMode == "现金当日")
{
settleMode = "现金当日";
//赔付现金,当标的资产价格高于行权价时,赔付看涨乘数倍的看涨期权损失
paymentAmount = (strike - _closePrice) * paymentNotional;
}
else if (_tdAcc.SettlementMode == "现金期末")
{
settleMode = "现金期末";
paymentAmount = paymentNotional;
}
else if (_tdAcc.SettlementMode == "实物交割")
{
settleMode = "转远期";
paymentAmount = paymentNotional;
forwardTrade = ConvertToForward(forwardStrike: strike, notional: paymentNotional, ForwardDateTypeStr: _tdAcc.ForwardDateType, ForwardPriceTypeStr: _tdAcc.ForwardPriceType);
}
else if (_tdAcc.SettlementMode == "现金实物合并结算")
{
settleMode = "现金当日&转远期";
//赔付现金,当标的资产价格高于行权价时,赔付看涨乘数倍的看涨期权损失
paymentAmount = (strike - _closePrice) * paymentNotional;
forwardTrade = ConvertToForward(forwardStrike: strike, notional: paymentNotional, ForwardDateTypeStr: _tdAcc.ForwardDateType, ForwardPriceTypeStr: _tdAcc.ForwardPriceType);
}
else
{
throw new ServiceException("未知的结算方式:" + _tdAcc.SettlementMode);
}
}
//在上下限之间则买方获得收益(使用看跌乘数)
else
{
paymentNotional = _tdAcc.PutMultiplier * _notional;
if (_tdAcc.SettlementMode == "实物交割")
{
if (_tdAcc.AccumuType == "区间现金结算")
{
if (_tdAcc.PayoffType == "固定")
{
settleMode = "票息";
//固定:当标的资产价格高于行权价且低于障碍价格时,获得票息收益
paymentAmount = couponCalcInfo.Value.singleCoupon * paymentNotional;
paymentDate = couponCalcInfo.Value.settleDate;
}
else
{
settleMode = "现金当日";
//当标的资产价格低于行权价且高于障碍价格时,获得看跌乘数倍的看跌期权收益
paymentAmount = (strike - _closePrice) * paymentNotional;
}
}
else
{
settleMode = "转远期";
paymentAmount = paymentNotional;
forwardTrade = ConvertToForward(forwardStrike: strike, notional: paymentNotional, ForwardDateTypeStr: _tdAcc.ForwardDateType, ForwardPriceTypeStr: _tdAcc.ForwardPriceType);
}
}
else if (_tdAcc.SettlementMode == "现金实物合并结算")
{
if (_tdAcc.AccumuType == "区间现金结算")
{
if (_tdAcc.PayoffType == "固定")
{
settleMode = "票息";
//固定:当标的资产价格高于行权价且低于障碍价格时,获得票息收益
paymentAmount = couponCalcInfo.Value.singleCoupon * paymentNotional;
paymentDate = couponCalcInfo.Value.settleDate;
}
else
{
settleMode = "现金当日";
//当标的资产价格低于行权价且高于障碍价格时,获得看跌乘数倍的看跌期权收益
paymentAmount = (strike - _closePrice) * paymentNotional;
}
}
else
{
settleMode = "现金当日&转远期";
if (_tdAcc.PayoffType == "固定")
{
paymentAmount = couponCalcInfo.Value.singleCoupon * paymentNotional;
}
else
{
paymentAmount = (strike - _closePrice) * paymentNotional;
}
forwardTrade = ConvertToForward(forwardStrike: strike, notional: paymentNotional, ForwardDateTypeStr: _tdAcc.ForwardDateType, ForwardPriceTypeStr: _tdAcc.ForwardPriceType);
}
}
//现金-固定票息
else if (_tdAcc.PayoffType == "固定")
{
settleMode = "票息";
//当标的资产价格低于行权价且高于障碍价格时,获得票息收益
paymentAmount = couponCalcInfo.Value.singleCoupon * paymentNotional;
paymentDate = couponCalcInfo.Value.settleDate;
}
//现金-浮动收益
else if (_tdAcc.SettlementMode == "现金期末")
{
settleMode = "现金期末";
paymentAmount = paymentNotional;
}
else if (string.IsNullOrWhiteSpace(_tdAcc.SettlementMode) || _tdAcc.SettlementMode == "现金当日")
{
settleMode = "现金当日";
//当标的资产价格低于行权价且高于障碍价格时,获得看跌乘数倍的看跌期权收益
paymentAmount = (strike - _closePrice) * paymentNotional;
}
else
{
throw new ServiceException("未知的结算方式:" + _tdAcc.SettlementMode);
}
}
}
/// <summary>
/// 累计期权结算检查(交易员角度)
/// </summary>
public CheckResult CheckOption()
{
//解析观测日期
var customObservDates = QdpHelper.ParseAutocallCustomizedInfo(_tdAcc.KOObservationDates);
var koObservationDates = customObservDates.Item1;
var customKOBarriers = customObservDates.Item2;
double[] customCoupons = null;// customObservDates.Item3;
koObservationDates ??= CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(_td.TradeDate.Value, _td.ExerciseDate.Value).ToArray();
var valueDateIndex = Array.IndexOf(koObservationDates, new Date(_valueDate));
if (valueDateIndex < 0)
{
return null;
}
//执行价和障碍价格
var strike = _td.Strike ?? 0;
double? barrier = _tdAcc.KOBarrier, barrier2 = null;
if (customKOBarriers != null && customKOBarriers.Length > valueDateIndex)
{
barrier = customKOBarriers[valueDateIndex];
}
if (_td.IsMoneynessOptionData)
{
strike *= _td.SpotPrice.Value;
barrier *= _td.SpotPrice.Value;
}
if (barrier.HasValue)
{
barrier2 = barrier;
}
else
{
barrier = ConsGlobal.CallPut.IsCall(_td.OptionType) ? 1e10 : -1e10;
}
//观察开始日
var startDate = valueDateIndex > 0 ? koObservationDates[valueDateIndex - 1] : new Date(_td.StartDate.Value);
//以买方角度计算金额或数量
string settleMode = null;
trade forwardTrade = null;
DateTime paymentDate;
double paymentAmount, paymentNotional;
var lazyCouponCalcInfo = new Lazy<CouponCalcInfo>(() =>
{
var settleDate = GetSettleDate(valueDateIndex);
//var includeStartDate = valueDateIndex == 0;
return new CouponCalcInfo
{
settleDate = settleDate,
singleCoupon = getCouponRate(customCoupons, valueDateIndex).Item1 * getDayCountFraction(startDate: startDate, settleDate: settleDate, includeStartDate: false)
};
});
//累购期权
if (_td.OptionType == "看涨")
{
CheckCallOption(strike, barrier.Value, koObservationDates, lazyCouponCalcInfo, out settleMode, out paymentDate, out paymentNotional, out paymentAmount, out forwardTrade);
}
//累沽期权
else
{
CheckPutOption(strike, barrier.Value, koObservationDates, lazyCouponCalcInfo, out settleMode, out paymentDate, out paymentNotional, out paymentAmount, out forwardTrade);
}
var result = new CheckResult
{
SettlementMode = settleMode,
StartDate = startDate,
ForwardTrade = forwardTrade,
PaymentDate = paymentDate,
PaymentAmount = paymentAmount,
PaymentNotional = paymentNotional,
Strike = strike,
Barrier = barrier2
};
if (result.SettlementMode == "现金期末" || result.SettlementMode == "转远期" || result.SettlementMode == "转掉期")
{
result.PaymentAmount = paymentNotional;
}
else
{
//支付票息时精确到分
result.PaymentAmount = Commons.OtcFormatHelper.FormatValue(paymentAmount, 2);
if (_td.BuySell != "买入")
{
result.PaymentAmount = -result.PaymentAmount; //转换为交易员角度
}
}
result.CountRatio = DataCacheProvider.GetUnderlyingDataSource().GetData(_td.UnderlyingCode)?.CountRatio ?? 1;
return result;
}
}
/// <summary>
/// 累计三段式
/// </summary>
public class InnerCheck2 : InnerChecksBase
{
public InnerCheck2(OtcTradeBase td, trade_accumulator_option tdAcc, DateTime valueDate, double closePrice, double notional) : base(td, tdAcc, valueDate, closePrice, notional)
{
}
/// <summary>
/// 累购期权 三段式
/// </summary>
/// <param name="strike">行权价</param>
/// <param name="barrier">障碍价</param>
/// <param name="koObservationDates">观察日</param>
/// <param name="couponCalcInfo">票息</param>
/// <param name="settleMode">观察状态</param>
/// <param name="paymentDate">支付日期</param>
/// <param name="paymentNotional">支付数量</param>
/// <param name="paymentAmount">支付总额</param>
/// <param name="forwardTrade">远期交易</param>
private void CheckCallOption(double strike, double strike2, double strike3, double barrier, Date[] koObservationDates, Lazy<CouponCalcInfo> lazyCouponCalcInfo,
out string settleMode, out DateTime paymentDate, out double paymentNotional, out double paymentAmount, out trade forwardTrade)
{
forwardTrade = null;
paymentDate = _valueDate;
//腿一
if (_closePrice < strike)//0<=S<K1 || UpdownPriceMode && 0<=S<=K1 payoff -Aput*(K1-S)
{
paymentNotional = _notional * (_tdAcc.Multiplier ?? 0);
if (string.IsNullOrWhiteSpace(_tdAcc.SettlementMode) || _tdAcc.SettlementMode == "现金当日")
{
settleMode = "现金当日";
paymentAmount = (_closePrice - strike) * paymentNotional;
}
//转远期则直接返回结果
else if (_tdAcc.SettlementMode == "实物交割")
{
settleMode = "转远期";
paymentAmount = paymentNotional;
//赔付的时候交割价使用执行价即可
forwardTrade = ConvertToForward(forwardStrike: strike, notional: paymentNotional, ForwardDateTypeStr: _tdAcc.ForwardDateType, ForwardPriceTypeStr: _tdAcc.ForwardPriceType);
}
else if (_tdAcc.SettlementMode == "现金实物合并结算")
{
settleMode = "现金当日&转远期";
paymentAmount = (_closePrice - strike) * paymentNotional;
//赔付的时候交割价使用执行价即可
forwardTrade = ConvertToForward(forwardStrike: strike, notional: paymentNotional, ForwardDateTypeStr: _tdAcc.ForwardDateType, ForwardPriceTypeStr: _tdAcc.ForwardPriceType);
}
else
{
throw new ServiceException("未知的结算方式:" + _tdAcc.SettlementMode);
}
}
else if ((Math.Abs(_closePrice - strike) < 1e-6) || _closePrice < strike2 || (Math.Abs(_closePrice - strike2) < 1e-6 && _tdAcc.PayoffType == "浮动"))//K1<=S<=K2 || UpdownPriceMode && K1<=S<K2 payoff 0
{
settleMode = "现金当日";
paymentAmount = paymentNotional = 0;
}
//腿二
else if (_closePrice < barrier || Math.Abs(_closePrice - barrier) < 1e-6 || (Math.Abs(_closePrice - strike2) < 1e-6 && _tdAcc.PayoffType == "固定"))//K2<S<B || UpdownPriceMode && K2<S<=B payoff Acall1*(S-K2) or c1
{
paymentNotional = _notional * (_tdAcc.Multiplier2 ?? 0);
//转远期则直接返回结果
if (_tdAcc.SettlementMode2 == "实物交割")
{
settleMode = "转远期";
paymentAmount = paymentNotional;
//赔付的时候交割价使用执行价即可
forwardTrade = ConvertToForward(forwardStrike: strike2, notional: paymentNotional, ForwardDateTypeStr: _tdAcc.ForwardDateType2, ForwardPriceTypeStr: _tdAcc.ForwardPriceType2);
}
else if (_tdAcc.SettlementMode2 == "现金实物合并结算")
{
settleMode = "现金当日&转远期";
if (_tdAcc.PayoffType == "固定")
{
paymentAmount = lazyCouponCalcInfo.Value.singleCoupon * paymentNotional;
}
else
{
paymentAmount = (_closePrice - strike2) * paymentNotional;
}
//赔付的时候交割价使用执行价即可
forwardTrade = ConvertToForward(forwardStrike: strike2, notional: paymentNotional, ForwardDateTypeStr: _tdAcc.ForwardDateType2, ForwardPriceTypeStr: _tdAcc.ForwardPriceType2);
}
else if (_tdAcc.PayoffType == "固定")
{
//固定:当标的资产价格高于行权价且低于障碍价格时,获得票息收益
settleMode = "票息";
paymentAmount = lazyCouponCalcInfo.Value.singleCoupon * paymentNotional;
paymentDate = lazyCouponCalcInfo.Value.settleDate;
}
else if (string.IsNullOrWhiteSpace(_tdAcc.SettlementMode2) || _tdAcc.SettlementMode2 == "现金当日")
{
settleMode = "现金当日";
//当标的资产价格高于行权价且低于障碍价格时,买方获得看涨乘数倍的看涨期权收益
paymentAmount = (_closePrice - strike2) * paymentNotional;
}
else
{
throw new ServiceException("未知的结算方式:" + _tdAcc.SettlementMode2);
}
}
else if (_closePrice > strike3 || (Math.Abs(_closePrice - strike3) < 1e-6 && _tdAcc.PayoffType == "固定"))
{
paymentNotional = _notional * (_tdAcc.Multiplier3 ?? 0);
//转远期则直接返回结果
if (_tdAcc.SettlementMode3 == "实物交割")
{
settleMode = "转远期";
paymentAmount = paymentNotional;
//赔付的时候交割价使用执行价即可
forwardTrade = ConvertToForward(forwardStrike: strike3, notional: paymentNotional, ForwardDateTypeStr: _tdAcc.ForwardDateType3, ForwardPriceTypeStr: _tdAcc.ForwardPriceType3);
}
else if (_tdAcc.SettlementMode3 == "现金实物合并结算")
{
settleMode = "现金当日&转远期";
if (_tdAcc.PayoffType == "固定")
{
paymentAmount = lazyCouponCalcInfo.Value.singleCoupon2 * paymentNotional;
}
else
{
paymentAmount = (_closePrice - strike3) * paymentNotional;
}
//赔付的时候交割价使用执行价即可
forwardTrade = ConvertToForward(forwardStrike: strike3, notional: paymentNotional, ForwardDateTypeStr: _tdAcc.ForwardDateType3, ForwardPriceTypeStr: _tdAcc.ForwardPriceType3);
}
else if (_tdAcc.PayoffType == "固定")//票息支付
{
settleMode = "票息";
paymentAmount = lazyCouponCalcInfo.Value.singleCoupon2 * paymentNotional;
paymentDate = lazyCouponCalcInfo.Value.settleDate;
}
else if (_tdAcc.SettlementMode3 == "现金当日")//Aca112*(S-K3)
{
settleMode = "现金当日";
paymentAmount = (_closePrice - strike3) * paymentNotional;
}
else
{
throw new ServiceException("未知的结算方式:" + _tdAcc.SettlementMode3);
}
}
else
{
settleMode = "现金当日";
paymentAmount = paymentNotional = 0;
}
}
/// <summary>
/// 累沽期权 三段式
/// </summary>
/// <param name="strike">行权价</param>
/// <param name="barrier">障碍价</param>
/// <param name="koObservationDates">观察日</param>
/// <param name="couponCalcInfo">票息</param>
/// <param name="settleMode">观察状态</param>
/// <param name="paymentDate">支付日期</param>
/// <param name="paymentNotional">支付数量</param>
/// <param name="paymentAmount">支付总额</param>
/// <param name="forwardTrade">远期交易</param>
private void CheckPutOption(double strike, double strike2, double strike3, double barrier, Date[] koObservationDates, Lazy<CouponCalcInfo> couponCalcInfo,
out string settleMode, out DateTime paymentDate, out double paymentNotional, out double paymentAmount, out trade forwardTrade)
{
forwardTrade = null;
paymentDate = _valueDate;
if (_closePrice < strike || (Math.Abs(_closePrice - strike) < 1e-6 && _tdAcc.PayoffType == "固定"))//0<=S<K1 || UpdownPriceMode && 0<=S<=K1 payoff Aput1*(K1-S) or c1
{
paymentNotional = _notional * (_tdAcc.Multiplier ?? 0);
//转远期则直接返回结果
if (_tdAcc.SettlementMode == "实物交割")
{
settleMode = "转远期";
paymentAmount = paymentNotional;
//赔付的时候交割价使用执行价即可
forwardTrade = ConvertToForward(forwardStrike: strike, notional: paymentNotional, ForwardDateTypeStr: _tdAcc.ForwardDateType, ForwardPriceTypeStr: _tdAcc.ForwardPriceType);
}
else if (_tdAcc.SettlementMode == "现金实物合并结算")
{
settleMode = "现金当日&转远期";
if (_tdAcc.PayoffType == "固定")
{
paymentAmount = couponCalcInfo.Value.singleCoupon * paymentNotional;
}
else
{
paymentAmount = (strike - _closePrice) * paymentNotional;
}
//赔付的时候交割价使用执行价即可
forwardTrade = ConvertToForward(forwardStrike: strike, notional: paymentNotional, ForwardDateTypeStr: _tdAcc.ForwardDateType, ForwardPriceTypeStr: _tdAcc.ForwardPriceType);
}
else if (_tdAcc.PayoffType == "固定")//票息支付
{
settleMode = "票息";
paymentAmount = couponCalcInfo.Value.singleCoupon * paymentNotional;
paymentDate = couponCalcInfo.Value.settleDate;
}
else if (_tdAcc.SettlementMode == "现金当日")//Aca112*(S-K3)
{
settleMode = "现金当日";
paymentAmount = (strike - _closePrice) * paymentNotional;
}
else
{
throw new ServiceException("未知的结算方式:" + _tdAcc.SettlementMode);
}
}
else if ((_closePrice < barrier && Math.Abs(_closePrice - barrier) > 1e-6) || (Math.Abs(_closePrice - strike) < 1e-6 && _tdAcc.PayoffType == "浮动"))//K1<=S<=B || UpdownPriceMode && K1<S<B payoff 0
{
settleMode = "现金当日";
paymentAmount = paymentNotional = 0;
}
else if (Math.Abs(barrier - strike2) < 1e-6 || _closePrice < strike2 || (Math.Abs(_closePrice - strike2) < 1e-6 && _tdAcc.PayoffType == "固定"))//B<S<K2 || UpdownPriceMode && B<=S<=K2 payoff Aput1*(K1-S) or c2
{
paymentNotional = _notional * (_tdAcc.Multiplier2 ?? 0);
//转远期则直接返回结果
if (_tdAcc.SettlementMode2 == "实物交割")
{
settleMode = "转远期";
paymentAmount = paymentNotional;
//赔付的时候交割价使用执行价即可
forwardTrade = ConvertToForward(forwardStrike: strike2, notional: paymentNotional, ForwardDateTypeStr: _tdAcc.ForwardDateType2, ForwardPriceTypeStr: _tdAcc.ForwardPriceType2);
}
else if (_tdAcc.SettlementMode2 == "现金实物合并结算")
{
settleMode = "现金当日&转远期";
if (_tdAcc.PayoffType == "固定")
{
paymentAmount = couponCalcInfo.Value.singleCoupon2 * paymentNotional;
}
else
{
paymentAmount = (strike2 - _closePrice) * paymentNotional;
}
//赔付的时候交割价使用执行价即可
forwardTrade = ConvertToForward(forwardStrike: strike2, notional: paymentNotional, ForwardDateTypeStr: _tdAcc.ForwardDateType2, ForwardPriceTypeStr: _tdAcc.ForwardPriceType2);
}
else if (_tdAcc.PayoffType == "固定")//票息支付
{
settleMode = "票息";
paymentAmount = couponCalcInfo.Value.singleCoupon2 * paymentNotional;
paymentDate = couponCalcInfo.Value.settleDate;
}
else if (_tdAcc.SettlementMode2 == "现金当日")//Aca112*(S-K3)
{
settleMode = "现金当日";
paymentAmount = (strike2 - _closePrice) * paymentNotional;
}
else
{
throw new ServiceException("未知的结算方式:" + _tdAcc.SettlementMode2);
}
}
else if (_closePrice > strike3)//S>K3 || UpdownPriceMode && S>=K3 payoff -Acall*(S-K3)
{
paymentNotional = _notional * (_tdAcc.Multiplier3 ?? 0);
if (string.IsNullOrWhiteSpace(_tdAcc.SettlementMode3) || _tdAcc.SettlementMode3 == "现金当日")
{
settleMode = "现金当日";
paymentAmount = (strike3 - _closePrice) * paymentNotional;
}
//转远期则直接返回结果
else if (_tdAcc.SettlementMode3 == "实物交割")
{
settleMode = "转远期";
paymentAmount = paymentNotional;
//赔付的时候交割价使用执行价即可
forwardTrade = ConvertToForward(forwardStrike: strike3, notional: paymentNotional, ForwardDateTypeStr: _tdAcc.ForwardDateType3, ForwardPriceTypeStr: _tdAcc.ForwardPriceType3);
}
else if (_tdAcc.SettlementMode3 == "现金实物合并结算")
{
settleMode = "现金当日&转远期";
paymentAmount = (strike3 - _closePrice) * paymentNotional;
//赔付的时候交割价使用执行价即可
forwardTrade = ConvertToForward(forwardStrike: strike3, notional: paymentNotional, ForwardDateTypeStr: _tdAcc.ForwardDateType3, ForwardPriceTypeStr: _tdAcc.ForwardPriceType3);
}
else
{
throw new ServiceException("未知的结算方式:" + _tdAcc.SettlementMode3);
}
}
else//K2<=S<=K3 || UpdownPriceMode && K2<=S<K3 payoff 0
{
settleMode = "现金当日";
paymentAmount = paymentNotional = 0;
}
}
/// <summary>
/// 累计期权结算检查(交易员角度) 三段式
/// </summary>
public CheckResult CheckOption()
{
//解析观测日期
var customObservDates = QdpHelper.ParseAutocallCustomizedInfo(_tdAcc.KOObservationDates);
var koObservationDates = customObservDates.Item1;
var customKOBarriers = customObservDates.Item2;
double[] customCoupons = null;// customObservDates.Item3;
koObservationDates ??= CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(_td.TradeDate.Value, _td.ExerciseDate.Value).ToArray();
var valueDateIndex = Array.IndexOf(koObservationDates, new Date(_valueDate));
if (valueDateIndex < 0)
{
return null;
}
//执行价和障碍价格
var strike = _td.Strike ?? 0;
var strike2 = _tdAcc.Strike2 ?? 0;
var strike3 = _tdAcc.Strike3 ?? 0;
double? barrier = _tdAcc.KOBarrier, barrier2 = null;
if (customKOBarriers != null && customKOBarriers.Length > valueDateIndex)
{
barrier = customKOBarriers[valueDateIndex];
}
if (_td.IsMoneynessOptionData)
{
strike *= _td.SpotPrice.Value;
strike2 *= _td.SpotPrice.Value;
strike3 *= _td.SpotPrice.Value;
barrier *= _td.SpotPrice.Value;
}
if (barrier.HasValue)
{
barrier2 = barrier;
}
else
{
barrier = ConsGlobal.CallPut.IsCall(_td.OptionType) ? 1e10 : -1e10;
}
//观察开始日
var startDate = valueDateIndex > 0 ? koObservationDates[valueDateIndex - 1] : new Date(_td.StartDate.Value);
//以买方角度计算金额或数量
string settleMode = null;
trade forwardTrade = null;
DateTime paymentDate;
double paymentAmount, paymentNotional;
var lazyCouponCalcInfo = new Lazy<CouponCalcInfo>(() =>
{
var settleDate = GetSettleDate(valueDateIndex);
var couponModle = getCouponRate(customCoupons, valueDateIndex);
//var includeStartDate = valueDateIndex == 0;
return new CouponCalcInfo
{
settleDate = settleDate,
singleCoupon = couponModle.Item1 * getDayCountFraction(startDate: startDate, settleDate: settleDate, includeStartDate: false),
singleCoupon2 = couponModle.Item2 * getDayCountFraction(startDate: startDate, settleDate: settleDate, includeStartDate: false)
};
});
//累购期权
if (_td.OptionType == "看涨")
{
CheckCallOption(strike, strike2, strike3, barrier.Value, koObservationDates, lazyCouponCalcInfo, out settleMode, out paymentDate, out paymentNotional, out paymentAmount, out forwardTrade);
}
//累沽期权
else
{
CheckPutOption(strike, strike2, strike3, barrier.Value, koObservationDates, lazyCouponCalcInfo, out settleMode, out paymentDate, out paymentNotional, out paymentAmount, out forwardTrade);
}
var result = new CheckResult
{
SettlementMode = settleMode,
StartDate = startDate,
ForwardTrade = forwardTrade,
PaymentDate = paymentDate,
PaymentAmount = paymentAmount,
PaymentNotional = paymentNotional,
Strike = strike,
Barrier = barrier2
};
if (result.SettlementMode == "现金期末" || result.SettlementMode == "转远期" || result.SettlementMode == "转掉期")
{
result.PaymentAmount = paymentNotional;
}
else
{
//支付票息时精确到分
result.PaymentAmount = Commons.OtcFormatHelper.FormatValue(paymentAmount, 2);
if (_td.BuySell != "买入")
{
result.PaymentAmount = -result.PaymentAmount; //转换为交易员角度
}
}
result.CountRatio = DataCacheProvider.GetUnderlyingDataSource().GetData(_td.UnderlyingCode)?.CountRatio ?? 1;
return result;
}
}
struct CouponCalcInfo
{
public double singleCoupon { get; set; }
public double singleCoupon2 { get; set; }
public DateTime settleDate { get; set; }
}
}