306 lines
10 KiB
C#
306 lines
10 KiB
C#
using Qdp.Foundation.Implementations;
|
|
using Qdp.Pricing.Base.Implementations;
|
|
using Qdp.Pricing.Base.Utilities;
|
|
using Qdp.Pricing.Library.Common.MathMethods.VolTermStructure;
|
|
using Qdp.Pricing.Library.Equity.Engines.Analytical;
|
|
using YLErp.Abstract;
|
|
using YLErp.BLL.Calculation;
|
|
using YLErp.Commons;
|
|
using YLErp.Models;
|
|
using YLErp.Modules.CalculationModule;
|
|
using YLErp.QdpModule.Constants;
|
|
|
|
namespace YLErp.QdpModule
|
|
{
|
|
/// <summary>
|
|
/// 波动率曲面帮助类
|
|
/// </summary>
|
|
public static class QdpVolHelper
|
|
{
|
|
public const string VOL_SURFACE_SUFFIX = "_VolSurface";
|
|
|
|
/// <summary>
|
|
/// 从交易的TradeVol构造一个波动率曲面
|
|
/// </summary>
|
|
public static IVolatility GetSurfaceFromTradeVol(DateTime valueDate, ITradeVolLinearParam para)
|
|
{
|
|
if (para == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(para));
|
|
}
|
|
|
|
double constVol;
|
|
|
|
if (valueDate < para.GetStartDate())
|
|
{
|
|
constVol = para.GetTradeOpenVol();
|
|
}
|
|
else if (valueDate > para.GetMaturityDate())
|
|
{
|
|
constVol = para.GetTradeCloseVol();
|
|
}
|
|
else
|
|
{
|
|
var daycountMode = PS.Config.ErpElement.SmoothingDaycountMode == Configuration.Enums.SmoothingDaycountMode.CalendarDay
|
|
? Qdp.Pricing.Base.Enums.DayCountMode.CalendarDay
|
|
: Qdp.Pricing.Base.Enums.DayCountMode.TradingDay;
|
|
|
|
constVol = AnalyticalOptionTradeVolInterp.tradeVolLinearInterp(
|
|
new Date(valueDate),
|
|
para.GetTradeOpenVol(), para.GetTradeCloseVol(),
|
|
new Date(para.GetStartDate()),
|
|
new Date(para.GetMaturityDate()),
|
|
para.GetNumOfSmoothingDays(),
|
|
daycountMode,
|
|
CalendarImpl.Get("chn"),
|
|
para.IsIncludeStartDate());
|
|
}
|
|
|
|
//生成3*3水平的波动率曲面
|
|
return GenerateFlatSurface(OtcFormatHelper.FormatValue(constVol, 4));
|
|
}
|
|
|
|
/// <summary>
|
|
/// 生成平面波动率
|
|
/// </summary>
|
|
public static IVolatility GenerateFlatSurface(double constVol)
|
|
{
|
|
return GenerateFlatSurface(constVol, ConsVolInfos.DefaultVolStrikeList, ConsVolInfos.DefaultVolTenorList);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 生成平面波动率
|
|
/// </summary>
|
|
public static IVolatility GenerateFlatSurface(double constVol, IEnumerable<double> strikes, IEnumerable<string> expires)
|
|
{
|
|
if (strikes == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(strikes));
|
|
}
|
|
|
|
if (expires == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(expires));
|
|
}
|
|
|
|
var volTable = new List<SingleVol>();
|
|
|
|
foreach (var strike in strikes)
|
|
{
|
|
foreach (var expire in expires)
|
|
{
|
|
volTable.Add(new SingleVol { Expire = expire, Strike = strike, Vol = constVol });
|
|
}
|
|
}
|
|
|
|
return new VolatilityImpl()
|
|
{
|
|
VolSurfaceMode = "StrikeVol",
|
|
InterpolationMethod = ConsVolMethod.BiLinear,
|
|
VolTable = volTable
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public static List<SingleVol> GenerateFlatSingleVols(double vol)
|
|
{
|
|
var volTable = new List<SingleVol>(20);
|
|
|
|
foreach (var strike in ConsVolInfos.DefaultMoneynessVolStrikeList)
|
|
{
|
|
foreach (var expire in ConsVolInfos.DefaultVolTenorList)
|
|
{
|
|
volTable.Add(new SingleVol(strike, expire, vol));
|
|
}
|
|
}
|
|
|
|
return volTable;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 根据一组波动率,算出所有的strike
|
|
/// </summary>
|
|
public static List<double> GetStrikes(IEnumerable<SingleVol> vols)
|
|
{
|
|
if (vols == null)
|
|
{
|
|
return new List<double>(0);
|
|
}
|
|
return vols.GroupBy(v => v.Strike).OrderBy(v => v.Key).Select(v => v.Key).ToList();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 从波动率曲面上找到某点的波动率
|
|
/// </summary>
|
|
/// <param name="exerciseDate">某点的到期日</param>
|
|
/// <param name="strike">某点的行权价</param>
|
|
/// <returns>波动率</returns>
|
|
public static double GetInterpolatedVol(
|
|
ImpliedVolSurface volSurface,
|
|
DateTime exerciseDate,
|
|
double strike,
|
|
bool isMoneynessOption,
|
|
bool isEodCalc,
|
|
double spot = 0.0,
|
|
double? timeFraction = null)
|
|
{
|
|
double vol;
|
|
if (PS.Config.Is润和 && !isEodCalc)
|
|
{
|
|
vol = volSurface.GetValue(timeFraction.Value, strike);
|
|
}
|
|
else
|
|
{
|
|
if (isMoneynessOption)
|
|
{
|
|
vol = volSurface.GetValue(new Date(exerciseDate), strike * spot, spot);
|
|
}
|
|
else
|
|
{
|
|
vol = volSurface.GetValue(new Date(exerciseDate), strike, spot);
|
|
}
|
|
}
|
|
|
|
return PS.Config.Is润和 ? OtcFormatHelper.FormatValue(vol, 4) : vol;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 从波动率曲面上找到某点的波动率
|
|
/// </summary>
|
|
/// <param name="exerciseDate">某点的到期日</param>
|
|
/// <param name="strike">某点的行权价</param>
|
|
/// <returns>波动率</returns>
|
|
public static double GetInterpolatedVol(
|
|
ImpliedVolSurface volSurface,
|
|
Date exerciseDate,
|
|
double strike,
|
|
string isMoneynessOption,
|
|
double spot = 0.0)
|
|
{
|
|
if (isMoneynessOption == "是" || isMoneynessOption == "True")
|
|
{
|
|
return volSurface.GetValue(exerciseDate, strike * spot, spot);
|
|
}
|
|
else
|
|
{
|
|
return volSurface.GetValue(exerciseDate, strike, spot);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// interpolatevol qdp中 根据波动率曲面得到一个波动率数值
|
|
/// </summary>
|
|
public static double GetInterpolatedVolFromNormalSurface(IEnumerable<SingleVol> vols, InterpolatedVolReq volReq, string interpolationMethod = ConsVolMethod.Default)
|
|
{
|
|
if (vols == null || !vols.Any())
|
|
{
|
|
return double.NaN;
|
|
}
|
|
|
|
var surfaceWrap = new VolSurfaceBuilder
|
|
{
|
|
interpolation = interpolationMethod,
|
|
volSurfaceName = "tempVolSurface",
|
|
volSurfaceType = volReq.volSurfaceType
|
|
}.SetVectors(vols).Build(volReq.valueDate);
|
|
|
|
double? timeFraction = null;
|
|
double strike = 0;
|
|
if (PS.Config.Is润和 && !volReq.isEodCalc)
|
|
{
|
|
var days = TradeCalcHelper.CalculateTTMDays(volReq.valueDate, volReq.exerciseDate, 0, false);
|
|
var daysInYear = (int)CalculatorHelper.GetTradeDayCount().ToDayCountImpl().DaysInYear();
|
|
timeFraction = days / daysInYear;
|
|
strike = volReq.volSurfaceType == "StrikeVol" ? (volReq.isMoneynessOption ? (volReq.strike * volReq.spot) : volReq.strike) : (volReq.isMoneynessOption ? volReq.strike : (volReq.strike / volReq.spot));
|
|
}
|
|
else
|
|
{
|
|
strike = volReq.strike;
|
|
}
|
|
|
|
return GetInterpolatedVol(
|
|
surfaceWrap.VolSurface,
|
|
volReq.exerciseDate,
|
|
strike,
|
|
volReq.isMoneynessOption,
|
|
volReq.isEodCalc,
|
|
volReq.spot,
|
|
timeFraction);
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public static string GetVolSurfaceName(string referenceId, string secondUnderlyingCode = null)
|
|
{
|
|
if (string.IsNullOrEmpty(referenceId))
|
|
{
|
|
throw new ArgumentException($"“{nameof(referenceId)}”不能是 Null 或为空。", nameof(referenceId));
|
|
}
|
|
|
|
return string.IsNullOrWhiteSpace(secondUnderlyingCode)
|
|
? $"{referenceId}{VOL_SURFACE_SUFFIX}"
|
|
: $"{referenceId}_{secondUnderlyingCode}{VOL_SURFACE_SUFFIX}";
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public static string GenerateVolSurfaceKey(this string underlyingCode)
|
|
{
|
|
return underlyingCode + VOL_SURFACE_SUFFIX;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 用自定义波动率值构造一个水平的波动率曲面
|
|
/// </summary>
|
|
public static IVolatility GetDefaultVolatility(double vol)
|
|
{
|
|
var vols = new List<SingleVol>
|
|
{
|
|
new SingleVol
|
|
{
|
|
Strike = 1,
|
|
Expire = "1D",
|
|
Vol = vol
|
|
},
|
|
new SingleVol
|
|
{
|
|
Strike = 1,
|
|
Expire = "1Y",
|
|
Vol = vol
|
|
}
|
|
};
|
|
|
|
return new VolatilityImpl
|
|
{
|
|
VolTable = vols,
|
|
VolSurfaceMode = ConsVolInfos.defVolMode,
|
|
InterpolationMethod = ConsVolInfos.defInterpolationMethod
|
|
};
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public class InterpolatedVolReq
|
|
{
|
|
public DateTime valueDate { get; set; }
|
|
|
|
public DateTime exerciseDate { get; set; }
|
|
|
|
public double strike { get; set; }
|
|
|
|
public bool isMoneynessOption { get; set; }
|
|
|
|
public string volSurfaceType { get; set; } = "StrikeVol";
|
|
|
|
public double spot { get; set; }
|
|
|
|
public bool isEodCalc { get; set; } = false;
|
|
}
|
|
}
|