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

379 lines
17 KiB
C#

using CxxCalcLib;
using Qdp.Foundation.Implementations;
using Qdp.Pricing.Base.Enums;
using Qdp.Pricing.Base.Utilities;
using YLErp.BLL.Calculation.V2;
using YLErp.Commons;
using YLErp.Enums;
using YLErp.Modules.CalculationLogModule;
using YLErp.QdpModule;
namespace YLErp.Modules.CalculationModule
{
internal class SnowballSpecialistOptionCalculator
{
/// <summary>
/// 计算Greeks
/// </summary>
/// <param name="valueDate"></param>
/// <param name="vol"></param>
/// <param name="td"></param>
/// <returns></returns>
public TradeValueResult CalcOptionValue(DateTime valueDate, double spotPrice, double vol, CalcScenarioEnum calcScenario, trade td)
{
return new TradeValueResult { Succeeded = false, ErrorMessage = "不支持专业版雪球" };
//using var wrapper = new CxxCalcApi();
//if (valueDate > td.ExerciseDate)
//{
// //OTC-5920:对于定价日期>到期日的交易,无需给定价引擎计算,PV和其他希腊字母等直接为0即可。
// return new TradeValueResult(true)
// {
// UnderlyingCode = td.UnderlyingCode,
// Pv = 0,
// Delta = 0,
// Gamma = 0,
// Vega = 0,
// Rho = 0,
// CalendarDayTheta = 0,
// TradingDayTheta = 0,
// DeltaCash = 0,
// GammaCash = 0,
// VegaCash = 0,
// Vol = vol,
// SpotPrice = spotPrice
// };
//}
//var calcIn = BuildCalcInParams(valueDate, spotPrice, vol, td);
//AddCalculationLog(calcIn, calcScenario);
//Dictionary<double, SnowballScenarioResult> result1;
//try
//{
// result1 = wrapper.CalcPlatform_SnowballScenario(calcIn);
//}
//catch
//{
// throw new ServiceException("存在不符合定价条件的交易,无法完成定价,请检查交易要素是否完备");
//}
//var result = result1.Values.FirstOrDefault();
//var direction = td.BuySell == "买入" ? 1 : -1;
//var valueResult = new TradeValueResult(true)
//{
// UnderlyingCode = td.UnderlyingCode,
// Pv = result.PV * direction,
// Delta = result.DELTA * direction,
// Gamma = result.GAMMA * direction,
// Vega = result.VEGA * direction,
// Rho = result.RHO * direction,
// CalendarDayTheta = result.THETA * direction,
// TradingDayTheta = result.THETA * direction,
// DeltaCash = result.DELTA * spotPrice * direction,
// GammaCash = result.GAMMA * spotPrice * spotPrice * 0.01 * direction,
// VegaCash = result.VEGA * spotPrice * direction,
// Vol = vol,
// SpotPrice = spotPrice
//};
//// 计算 RoundedPv
//valueResult.RoundedPv = OtcFormatHelper.FormatValue(valueResult.Pv / td.Notional, 2) * td.Notional;
////计算DeltaInLots
//var underlying = DataCacheModule.DataCacheManager.GetUnderlyingDataSource().GetData(td.UnderlyingCode);
//valueResult.DeltaInLots = TradeLotsCalc.CalcDeltaInLots(result.DELTA, null, underlying);
////if (!(td.trade_snowball?.PrepaymentAddedToPv ?? false))
////{
//// // 计算引擎返回的PV是包含预付金,如果pv不想包含预付金,需要减掉预付金
//// var advanceAmount = td.StockEqvNotional * (td.trade_snowball.PrepaymentRatio ?? 0);
//// valueResult.Pv -= advanceAmount;
////}
//ValueCalculator.ConvertTradeValueResultOfCompany(valueResult, td.TradeType);
//return valueResult;
}
/// <summary>
/// 根据目标PV反算票息、波动率等
/// </summary>
/// <param name="valueDate">定价日</param>
/// <param name="vol">波动率</param>
/// <param name="calcTarget">计算的目标</param>
/// <param name="td">交易信息</param>
/// <returns></returns>
public double CalcTargets(DateTime valueDate, double spotPrice, double vol, int calcTarget, trade td)
{
return 0;
//using var wrapper = new CxxCalcApi();
//var calcIn = BuildCalcInParams(valueDate, spotPrice, vol, td);
//var targetPv = td.TradePrice ?? 0.0;
//var clacTarget = (CalcTarget)calcTarget;
//var isNan = false;
//if (clacTarget == CalcTarget.COUPON)
//{
// //calcIn.koCoupons = calcIn.koCoupons.Select(g => double.NaN).ToArray();
// calcIn.maturityCoupon = double.NaN;
// isNan = false;
//}
//else if (clacTarget == CalcTarget.KO_COUPON)
//{
// //calcIn.koCoupons = calcIn.koCoupons.Select(g => double.NaN).ToArray();
// isNan = true;
//}
//else if (clacTarget == CalcTarget.MATURITY_COUPON)
//{
// calcIn.maturityCoupon = double.NaN;
// isNan = true;
//}
//AddCalculationLog(calcIn, CalcScenarioEnum.Pricing, targetPv, isNan);
//double targetValue = 0;
//try
//{
// targetValue = wrapper.CalcPlatform_Infer(calcIn, targetPv, isNan, CalcTarget.COUPON);
//}
//catch
//{
// throw new ServiceException("存在不符合定价条件的交易,无法完成反算,请检查交易要素是否完备");
//}
//return targetValue;
}
/// <summary>
/// 根据原始专业版雪球拆分为两个雪球
/// 此处获取拆分的专业版雪球
/// </summary>
/// <param name="td"></param>
/// <returns></returns>
public trade GetSpecialTrade(trade td)
{
//预付金比例设置为0
var tdClone = td.Clone();
tdClone.trade_snowball = td.trade_snowball.Clone();
tdClone.trade_snowball.PrepaymentRatio = 0;
return tdClone;
}
/// <summary>
/// 根据原始专业版雪球拆分为两个雪球
/// 此处获取拆分的保本雪球
/// </summary>
/// <param name="td"></param>
/// <returns></returns>
public trade GetBreakevenTrade(trade td)
{
//无敲入条款,敲出票息与红利票息(不年化)=原始雪球的预付金比例
//无风险利率=原始雪球的预付金折现率
var tdClone = td.Clone();
tdClone.trade_snowball = td.trade_snowball.Clone();
tdClone.trade_snowball.KnockInOutDate = null;
tdClone.trade_snowball.KnockInOutStatus = null;
tdClone.trade_snowball.KIBarrier = 0;
tdClone.trade_snowball.SpreadStrikeAtMaturity1 = 0;
tdClone.trade_snowball.KIPayoffType = KIPayoffTypeEnum.None;
tdClone.trade_snowball.KORebate = td.trade_snowball.PrepaymentRatio ?? 0;
tdClone.trade_snowball.Coupon = td.trade_snowball.PrepaymentRatio ?? 0;
tdClone.trade_snowball.IsFixedCoupon = true;
tdClone.trade_snowball.PrepaymentUsed = false;
var customizedResults = QdpHelper.ParseAutocallCustomizedInfo(td.trade_snowball.KOObservationDates);
var koObservationDates = customizedResults.Item1;
var customizedKOBarriers = customizedResults.Item2;
tdClone.trade_snowball.KOObservationDates = $"{string.Join(",", koObservationDates.Select(O => O.DateTime.OtcFormatDate()))};" +
$"{string.Join(",", customizedKOBarriers)};" +
$"{string.Join(",", koObservationDates.Select(x => (td.trade_snowball.PrepaymentRatio ?? 0).OtcFormatFlex(6)))}";
tdClone.NoRiskRate = td.trade_snowball.PrepaymentConvertCashRate ?? 0;
return tdClone;
}
/// <summary>
/// 加和result结果
/// </summary>
/// <param name="result1"></param>
/// <param name="result2"></param>
/// <returns></returns>
public TradeValueResult MergeTradeValueResult(TradeValueResult specialSnowballResult, TradeValueResult breakevenSnowballResult)
{
specialSnowballResult.Pv += breakevenSnowballResult.Pv;
specialSnowballResult.RoundedPv += breakevenSnowballResult.RoundedPv;
specialSnowballResult.Delta += breakevenSnowballResult.Delta;
specialSnowballResult.DeltaCash += breakevenSnowballResult.DeltaCash;
specialSnowballResult.Gamma += breakevenSnowballResult.Gamma;
specialSnowballResult.GammaCash += breakevenSnowballResult.GammaCash;
specialSnowballResult.CalendarDayTheta += breakevenSnowballResult.CalendarDayTheta;
specialSnowballResult.TradingDayTheta += breakevenSnowballResult.TradingDayTheta;
specialSnowballResult.Rho += breakevenSnowballResult.Rho;
specialSnowballResult.Vega += breakevenSnowballResult.Vega;
specialSnowballResult.VegaCash += breakevenSnowballResult.VegaCash;
specialSnowballResult.TimeValue += breakevenSnowballResult.TimeValue;
return specialSnowballResult;
}
/// <summary>
/// 构建计算引擎需要的参数
/// </summary>
/// <param name="valueDate">定价日</param>
/// <param name="vol">波动率</param>
/// <param name="td">交易信息</param>
/// <returns></returns>
private CalcIn BuildCalcInParams(DateTime valueDate, double spotPrice, double vol, trade td)
{
var trade_snowball = td.trade_snowball;
// 敲入观察日
if (string.IsNullOrEmpty(trade_snowball?.KOObservationDates?.Trim(new[] { ' ', ';' })))
{
throw new ServiceException("敲出观察日列表不能为空,请生成敲出观察日列表");
}
if (vol <= 0)
{
throw new ServiceException("波动率必须大于0,请调整波动率");
}
var koParams = trade_snowball.KOObservationDates.Split(";");
var koObsDateStr = koParams[0];
var koObsDates = koObsDateStr.Split(",").Select(g => new Date(DateTime.Parse(g))).ToArray();
var koBarriers = koParams[1].Split(",").Select(g => double.Parse(g)).ToArray();
var koCoupons = koParams[2].Split(",").Select(g => string.IsNullOrEmpty(g) ? double.NaN : double.Parse(g)).ToArray();
var kiBarrier = trade_snowball.KIBarrier;
var strike = trade_snowball.SpreadStrikeAtMaturity1 ?? 0;
if (!td.IsMoneynessOptionData)
{
//转为相对价格比例
koBarriers = koBarriers.Select(g => g / td.InitialSpotPrice ?? 0).ToArray();
kiBarrier = kiBarrier / td.InitialSpotPrice ?? 0;
strike = strike / td.InitialSpotPrice ?? 0;
}
// 计算票息年化系数(外部有不同的计算方式)
var iFixedCoupon = string.IsNullOrEmpty(trade_snowball.CouponDayCount); //是否年化,约定为空表示非年化
var dayCount = iFixedCoupon ? null : trade_snowball.CouponDayCount.ToDayCountImpl();
var koFractions = koObsDateStr.Split(",").Select(g =>
{
var koObsDate = DateTime.Parse(g);
var koFraction = iFixedCoupon ? 1 : YLErp.QdpModule.QdpHelper.AnnualizeFactor(new Date(td.StartDate), koObsDate, dayCount);
return koFraction;
}).ToArray();
var market = GetMarket(td.UnderlyingCode);
// 计算入参
var calcIn = new CalcIn()
{
// 市场(日历)名称,如China
market = market,
// 开始日期
startDate = new Date(td.StartDate).ToString(),
// 估值日期
valueDate = valueDate.ToString("yyyy-MM-dd"),
// 5是向上敲出的雪球,6是向下敲出的雪球
barrierType = QdpConverter.ConvertOptionType(td.CallPut) == OptionType.Call ? CxxCalcLib.BarrierType.UP_OUT_DOWN_IN : CxxCalcLib.BarrierType.UP_IN_DOWN_OUT,
// 期初价格
initialSpot = td.InitialSpotPrice ?? 0,
// 敲出观察日列表,以逗号分隔
koObsDateStr = koObsDateStr,
// 敲出障碍价格列表,与敲出观察日对应
koBarriers = koBarriers,
// 敲出票息列表??
koCoupons = koCoupons,
// 敲出票息的年化系数列表
koFractions = koFractions,
// 敲出观察日个数
activeKoObsCount = koObsDates.Count(),//koObsDates.Where(g => g > new Date(td.StartDate)).Count(),
// 敲出增强收益的参与率,无增强收益填0
koPayoffParticipation = trade_snowball.EnhancedParticipationRate ?? 0,
// 敲入障碍价格
kiBarrier = kiBarrier,
// 敲入后的行权价
kiStrike = strike,
// 是否仅在到期日进行敲入观察,否则为每日观察
kiObsOnlyAtMaturity = trade_snowball.KIObservationType == KIObservationType.OnlyEndDate,
// 敲入后的期权参与率??
kiParticipationRate = trade_snowball.KIParticipationRate ?? 0,
// 红利票息
maturityCoupon = trade_snowball.Coupon,
// 保本比例,如0.8表示敲入后亏损封顶20%
protectionRatio = trade_snowball.PrincipalProtectionRate ?? 0,
// 初始预付金比例
initialMarginRatio = trade_snowball.PrepaymentRatio ?? 0,
// 预付金利率
marginInterestRate = trade_snowball.PrepaymentInterestRate ?? 0,
// 数量??
amount = td.Notional,
// 是否已敲入
isKnockedIn = trade_snowball.IsInitialKnockedIn,
// 一组标的资产价格,每个价格都是一个情景
spots = new double[] { spotPrice },
// 情景个数??
scenarioCount = 1,
// 无风险利率
r = td.NoRiskRate ?? 0,
// 分红率
q = td.DividendRate ?? 0,
// 波动率
v = vol,
// 计算指标,逗号隔开
greekType = "PV,DELTA,GAMMA,VEGA,THETA,RHO"
};
return calcIn;
}
/// <summary>
/// 添加日志
/// </summary>
/// <param name="trade"></param>
/// <param name="scenarioEnum"></param>
/// <param name="tradeNumber"></param>
private static void AddCalculationLog(CalcIn CalcParams, CalcScenarioEnum calcScenario, double? targetPv = null, bool? isNan = null)
{
try
{
// 添加计算日志
var log = new CalculationLog()
{
CreateTime = DateTime.Now,
Scenario = calcScenario,
LogObject = CalcParams,
//TradeNumber = tradeNumber,
//Notional = CalcParams.amount,
//Exercise = CalcParams..ToString(),
//OptionType = CalcParams?.OptionType.ToString(),
//TradeDate = trade.TradeDate?.ToString(),
//MaturityDate = trade.MaturityDate?.ToString(),
//InitialSpotPrice = option?.InitialSpotPrice.ToString(),
//Strike = option?.Strike.ToString(),
targetPv = targetPv,
isNan = isNan
};
ICalculationLogService calcLogService = new CalculationLogService();
calcLogService.AddLog(log, calcScenario);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
/// <summary>
/// 获取交易日历名称
/// </summary>
/// <param name="underlyingCode"></param>
/// <returns></returns>
private static string GetMarket(string underlyingCode)
{
var underlyingManager = DataCacheModule.DataCacheManager.GetUnderlyingDataSource().GetData(underlyingCode);
var market = DataCacheModule.DataCacheManager.GetMarketDataSource().AsQueryable(g => g.MarketName == underlyingManager.MarketName)?.FirstOrDefault();
return market?.CalendarName ?? "chn";
}
}
}