275 lines
13 KiB
C#
275 lines
13 KiB
C#
using Qdp.ComputeServiceV2.Data.CommonModels.TradeInfos;
|
|
using Qdp.Pricing.Base.Implementations;
|
|
using Qdp.Pricing.Library.Common.Interfaces;
|
|
using Qdp.Pricing.Library.Options.Products.Asian;
|
|
using Qdp.Pricing.Library.Options.Products.Barrier;
|
|
using Qdp.Pricing.Library.Options.Products.Binary;
|
|
using YLErp.Commons;
|
|
using YLErp.DBModels.Helpers;
|
|
using YLErp.Modules;
|
|
using YLErp.Modules.CalculationModule;
|
|
using YLErp.Modules.TradeModule.ExoticOptionModule;
|
|
|
|
namespace YLErp.BLL.EodSettlement
|
|
{
|
|
/// <summary>
|
|
/// 结算通用类
|
|
/// </summary>
|
|
public class SettlementCalcCommons
|
|
{
|
|
/// <summary>
|
|
/// 计算行权收益
|
|
/// </summary>
|
|
public static void SetPossibleExec(trade_cash tc, trade trade, double price, bool isRefreshFinalPrice = false
|
|
, DateTime? finalPriceSettleDate = null, double? strike = null)
|
|
{
|
|
string fixings = null;
|
|
|
|
tradeBLL.SetFieldsByTradeType(trade);
|
|
|
|
//var isBinaryOption = trade.TradeType == "二元期权";
|
|
var isAsianOption = trade.TradeType == "亚式期权";
|
|
if (isAsianOption && trade.trade_asian_option == null)
|
|
{
|
|
throw new ServiceException("缺少亚式期权交易信息");
|
|
}
|
|
|
|
//亚式期权浮动行权价有行权价传入的情况处理
|
|
if (isAsianOption)
|
|
{
|
|
var startDate = trade.trade_asian_option.AveragingPeriodStartDate ?? trade.TradeDate.Value;
|
|
var settleDate = finalPriceSettleDate ?? valuedateBLL.ValueDate;
|
|
//亚式浮动行权价并且行权价有输入值的情况下
|
|
if (strike.HasValue && trade.trade_asian_option.StrikeType == "Floating")
|
|
{
|
|
//均价起算日大于结算日的情况下取计值日
|
|
if (startDate > settleDate)
|
|
{
|
|
startDate = settleDate;
|
|
}
|
|
fixings = $"{startDate:yyyy-MM-dd},{strike}";
|
|
}
|
|
else
|
|
{
|
|
//均价起算日大于结算日的情况下取不到fixing则直接赋值
|
|
if (startDate > settleDate)
|
|
{
|
|
fixings = $"{settleDate:yyyy-MM-dd},{price}";
|
|
}
|
|
else
|
|
{
|
|
fixings = AsianOptionFixingService.GetFixingString(settleDate, trade);
|
|
if (string.IsNullOrEmpty(fixings))
|
|
{
|
|
//取不到fixing则直接赋值
|
|
fixings = $"{settleDate:yyyy-MM-dd},{price}";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (trade.TradeType == "Risky期权")
|
|
{
|
|
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(trade.UnderlyingCode);
|
|
if (underlying == null)
|
|
{
|
|
throw new Exception($"找不到标的资产{trade.UnderlyingCode}的信息");
|
|
}
|
|
var QdpTrades = GetToQdpOptionRisk(trade, underlying, finalPriceSettleDate, fixings);
|
|
tc.IsPossibleExec = true;
|
|
tc.UnwindPrice = tc.UnwindPrice ?? 0;
|
|
foreach (var item in QdpTrades)
|
|
{
|
|
if (item != null && item.Instrument != null && item.Instrument is IOption optitem)
|
|
{
|
|
Cashflow[] cashFlows;
|
|
cashFlows = optitem.GetPayoff(new double[] { price });
|
|
if (!(cashFlows != null && (cashFlows[0].PaymentAmount == 0 || double.IsNaN(cashFlows[0].PaymentAmount))))
|
|
{
|
|
//cashFlows[0].PaymentAmount包含了买卖方向的处理了
|
|
tc.UnwindPrice += cashFlows[0].PaymentAmount / trade.Notional;
|
|
tc.Amount += OtcFormatHelper.FormatValue(cashFlows[0].PaymentAmount, 2);
|
|
tc.InitialAmount += OtcFormatHelper.FormatValue(cashFlows[0].PaymentAmount, 2);
|
|
}
|
|
}
|
|
}
|
|
//risky保底是一个总的值
|
|
var amount = TradeHelper.GetAmountByPaymentAmount(0, trade.PrincipalSum(), trade.BuySell);
|
|
tc.Amount += OtcFormatHelper.FormatValue(amount, 2);
|
|
tc.InitialAmount += OtcFormatHelper.FormatValue(amount, 2);
|
|
}
|
|
else if (trade.TradeType == "雪球期权" && trade.trade_snowball.PrepaymentUsed)
|
|
{
|
|
var obResult = new SpecialSnowballObservationHelper(trade, trade.trade_snowball)
|
|
.GetObservationResultForTraderSide(finalPriceSettleDate ?? valuedateBLL.ValueDate, price, trade.Notional);
|
|
|
|
var amount = TradeHelper.GetAmountByPaymentAmount(obResult.PaymentAmount, trade.PrincipalSum(), trade.BuySell);
|
|
|
|
tc.IsPossibleExec = true;
|
|
tc.UnwindPrice = obResult.PaymentAmount / trade.Notional;
|
|
tc.Amount = OtcFormatHelper.FormatValue(amount, 2);
|
|
tc.InitialAmount = OtcFormatHelper.FormatValue(amount, 2);
|
|
}
|
|
else
|
|
{
|
|
var QdpTrade = TradeCalcHelper.GetQdpTrade(trade, finalPriceSettleDate, fixings);
|
|
if (QdpTrade != null && QdpTrade.Instrument != null && QdpTrade.Instrument is IOption opt)
|
|
{
|
|
if (isAsianOption)
|
|
{
|
|
var asianOpt = opt as AsianOption;
|
|
tc.StrikeType = trade.trade_asian_option.StrikeType;
|
|
if (trade.trade_asian_option.StrikeType == "Floating")
|
|
{
|
|
tc.Strike = asianOpt.ActualStrike;
|
|
}
|
|
else
|
|
{
|
|
tc.FinalPrice = asianOpt.FinalPrice();
|
|
|
|
//isRefreshFinalPrice为false代表非默认标的价格,则根据行权页面的标的价格计算行权收益
|
|
if (isRefreshFinalPrice)
|
|
{
|
|
//分段式亚式期权,行权时的默认标的价格需要通过QDP计算得出,而不是标的现价
|
|
//再根据该价格算出行权收益
|
|
price = asianOpt.FinalPrice();
|
|
if (finalPriceSettleDate != null)
|
|
{
|
|
price = asianOpt.FinalPrice(finalPriceSettleDate);
|
|
}
|
|
|
|
price = Convert.ToDouble(price.OtcFormatUmPrice());
|
|
}
|
|
}
|
|
}
|
|
|
|
Cashflow[] cashFlows;
|
|
if (QdpTrade.Instrument is BinaryOption)
|
|
{
|
|
var option = QdpTrade.Instrument as BinaryOption;
|
|
var valueDate = finalPriceSettleDate ?? valuedateBLL.ValueDate;
|
|
cashFlows = option.GetPayoff(new double[] { price }, valueDate);
|
|
}
|
|
else if (QdpTrade.Instrument is BarrierOption)
|
|
{
|
|
var option = QdpTrade.Instrument as BarrierOption;
|
|
var valueDate = finalPriceSettleDate ?? valuedateBLL.ValueDate;
|
|
cashFlows = option.GetPayoff(new double[] { price }, valueDate);
|
|
}
|
|
else
|
|
{
|
|
cashFlows = opt.GetPayoff(new double[] { price });
|
|
}
|
|
|
|
if (cashFlows != null && (cashFlows[0].PaymentAmount == 0 || double.IsNaN(cashFlows[0].PaymentAmount)))
|
|
{
|
|
var amount = TradeHelper.GetAmountByPaymentAmount(0, trade.PrincipalSum(), trade.BuySell);
|
|
tc.IsPossibleExec = true;
|
|
tc.UnwindPrice = 0;
|
|
tc.Amount = OtcFormatHelper.FormatValue(amount, 2);
|
|
tc.InitialAmount = OtcFormatHelper.FormatValue(amount, 2);
|
|
}
|
|
else
|
|
{
|
|
//cashFlows[0].PaymentAmount包含了买卖方向的处理了
|
|
var paymentAmount = trade.TradeType == "累计期权" ? cashFlows[0].PaymentAmount * trade.Notional / (trade.CountRatio ?? 1) : cashFlows[0].PaymentAmount;
|
|
var amount = TradeHelper.GetAmountByPaymentAmount(paymentAmount, trade.PrincipalSum(), trade.BuySell);
|
|
tc.IsPossibleExec = true;
|
|
tc.UnwindPrice = paymentAmount / trade.Notional;
|
|
tc.Amount = OtcFormatHelper.FormatValue(amount, 2);
|
|
tc.InitialAmount = OtcFormatHelper.FormatValue(amount, 2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public static List<TradeBase> GetToQdpOptionRisk(trade trade, underlying_manager underlying, DateTime? finalPriceSettleDate, string fixings)
|
|
{
|
|
var tradeclone = trade.Clone();
|
|
tradeclone.TradeAmount = tradeclone.TradeAmount = TradeCalcHelper.GetTradeAmountV(trade, trade.TradeAmount, 1);
|
|
tradeclone.Notional = tradeclone.Notional = TradeCalcHelper.GetTradeAmountV(trade, trade.Notional, underlying.CountRatio);
|
|
|
|
var options = new List<TradeBase>();
|
|
var td1 = tradeclone.Clone();
|
|
td1.Strike = trade.trade_risky_option.Strike1;
|
|
td1.ParticipationRate = trade.trade_risky_option.ParticipationRate1;
|
|
td1.TradeAmount = TradeCalcHelper.GetTradeAmount(td1, td1.TradeAmount, 1);
|
|
td1.Notional = TradeCalcHelper.GetTradeAmount(td1, td1.Notional, underlying.CountRatio);
|
|
td1.OptionType = "看跌";
|
|
td1.BuySell = trade.BuySell == "买入" ? "卖出" : "买入";
|
|
var option1 = TradeCalcHelper.GetQdpTrade(td1, finalPriceSettleDate, fixings);
|
|
if (option1 != null)
|
|
{
|
|
options.Add(option1);
|
|
}
|
|
|
|
var td2 = tradeclone.Clone();
|
|
td2.Strike = trade.trade_risky_option.Strike2;
|
|
td2.ParticipationRate = trade.trade_risky_option.ParticipationRate2;
|
|
td2.TradeAmount = TradeCalcHelper.GetTradeAmount(td2, td2.TradeAmount, 1);
|
|
td2.Notional = TradeCalcHelper.GetTradeAmount(td2, td2.Notional, underlying.CountRatio);
|
|
|
|
var option2 = TradeCalcHelper.GetQdpTrade(td2, finalPriceSettleDate, fixings);
|
|
if (option2 != null)
|
|
{
|
|
options.Add(option2);
|
|
}
|
|
|
|
var td3 = tradeclone.Clone();
|
|
td3.Strike = trade.trade_risky_option.Strike3;
|
|
//decimal 为了解决精度问题: 0.2-0.3=0.0999999999
|
|
var participationRate3 = (decimal)trade.trade_risky_option.ParticipationRate2 - (decimal)trade.trade_risky_option.ParticipationRate3;
|
|
td3.ParticipationRate = (double?)Math.Abs(participationRate3);
|
|
td3.TradeAmount = TradeCalcHelper.GetTradeAmount(td3, td3.TradeAmount, 1);
|
|
td3.Notional = TradeCalcHelper.GetTradeAmount(td3, td3.Notional, underlying.CountRatio);
|
|
if (participationRate3 < 0)
|
|
{
|
|
td3.BuySell = trade.BuySell == "买入" ? "卖出" : "买入";
|
|
}
|
|
|
|
var option3 = TradeCalcHelper.GetQdpTrade(td3, finalPriceSettleDate, fixings);
|
|
if (option3 != null)
|
|
{
|
|
options.Add(option3);
|
|
}
|
|
|
|
return options;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取根据当前价格行权的行权价
|
|
/// </summary>
|
|
public static double GetOTCTradeExerciseProfit(trade trade, double price)
|
|
{
|
|
//实值pv计算
|
|
var ActualPv = 0.0;
|
|
tradeBLL.SetFieldsByTradeType(trade);
|
|
var QdpTrade = TradeCalcHelper.GetQdpTrade(trade);
|
|
if (QdpTrade != null && QdpTrade.Instrument != null)
|
|
{
|
|
var opt = QdpTrade.Instrument as IOption;
|
|
if (opt != null)
|
|
{
|
|
try
|
|
{
|
|
var cashFlows = opt.GetPayoff(new double[] { price });
|
|
if (cashFlows != null && cashFlows[0].PaymentAmount == 0)
|
|
{
|
|
return ActualPv;
|
|
}
|
|
else
|
|
{
|
|
ActualPv = OtcFormatHelper.FormatValue(cashFlows[0].PaymentAmount, 2);
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
}
|
|
|
|
}
|
|
}
|
|
return ActualPv;
|
|
}
|
|
}
|
|
}
|