379 lines
17 KiB
C#
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";
|
|
}
|
|
}
|
|
}
|