427 lines
16 KiB
C#
427 lines
16 KiB
C#
using BaseOUDAL;
|
|
using Qdp.Pricing.Base.Implementations;
|
|
using Qdp.Pricing.Library.Equity.Engines.Analytical;
|
|
using System.Data;
|
|
using YLErp.Abstract;
|
|
using YLErp.BLL;
|
|
using YLErp.Modules.CalculationModule;
|
|
using YLErp.QdpModule;
|
|
using YLErp.QdpModule.Constants;
|
|
|
|
namespace YLErp.Modules.VolatilityModule
|
|
{
|
|
/// <summary>
|
|
/// 波动率操作帮助类
|
|
/// </summary>
|
|
public static class VolatilityHelper
|
|
{
|
|
public static volatility GetVol(DateTime date, string volType, string underlyingCode, string userGroup)
|
|
{
|
|
return new VolatilityQueryService(OptUserInfo.SystemUser).GetVolatility(userGroup, date, volType, underlyingCode, true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取结算波动率
|
|
/// </summary>
|
|
public static volatility GetEodSettlementVols(OtcTradeBase trade, underlying_manager underlying, DateTime valueDate)
|
|
{
|
|
if (PS.Config.IsTradeVol)
|
|
{
|
|
return GetSurfaceFromTradeVol(trade, valueDate, isEodSettle: true);
|
|
}
|
|
//再非tradevol的情况下,默认就使用财务波动率
|
|
var userGroup = UserBLL.GetUserGroup(trade.TraderId);
|
|
return GetVol(valueDate, "财务", underlying.UnderlyingCode, userGroup);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取隐含波动率
|
|
/// </summary>
|
|
public static double GetImpliedVol(DateTime valueDate, OtcTradeBase trade, double? ttmdays, double underlyingPrice, bool isEod)
|
|
{
|
|
var riskFreeRate = trade.NoRiskRate ?? (valuedateBLL.GetRiskFreeRateFromCurve(valueDate, trade.ExerciseDate) / 100);
|
|
|
|
var strike = trade.Strike ?? 0;
|
|
|
|
var variety = DataCacheProvider.GetVariety(trade.UnderlyingCode);
|
|
|
|
if (!ttmdays.HasValue || double.IsNaN(ttmdays.Value))
|
|
{
|
|
ttmdays = TradeCalcHelper.CalculateTTMDays(valueDate, trade.ExerciseDate.Value, variety.id, false);
|
|
}
|
|
|
|
var volSurfaceName = Guid.NewGuid().ToString();
|
|
|
|
var paramReq = new OptionTradeParamRequest(riskFreeRate)
|
|
{
|
|
dividends = null,
|
|
fixings = null,
|
|
hasNightMarket = variety != null && variety.HasNightMarket,
|
|
maturityShift = 0,
|
|
ParamOverride = n =>
|
|
{
|
|
n.notional = 1;
|
|
n.riskFreeRate = riskFreeRate;
|
|
n.buysell = "买入"; //默认买入,如果填卖出会报错
|
|
},
|
|
preciseTimeMode = !isEod,
|
|
timeToMaturityDays = ttmdays.Value,
|
|
tradeId = null,
|
|
volSurfaceNames = new[] { volSurfaceName }
|
|
};
|
|
|
|
var tp = QdpTradeBuilder.GetVanillaOptionTradeParam(trade, paramReq, false);
|
|
|
|
if (trade.IsUsePremiumRate == true && trade.PremiumRate.HasValue && trade.SpotPrice.HasValue)
|
|
{
|
|
trade.TradeSinglePrice = trade.PremiumRate * trade.SpotPrice;
|
|
}
|
|
return ImpliedVolCalcService.ImpliedVolFromPremium(trade.TradeSinglePrice ?? 0, valueDate, tp, underlyingPrice);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取交易波动率
|
|
/// </summary>
|
|
public static double GetTradeVol(OtcTradeBase trade, DateTime valueDate, bool isEodSettle = false)
|
|
{
|
|
using (var db = DbContextFactory.GetYLDbContext())
|
|
{
|
|
if (isEodSettle && trade.id > 0)
|
|
{
|
|
var overrideVol = db.eod_trade_vol_override
|
|
.Where(x => x.valuedate == valueDate && x.tradeid == trade.id)
|
|
.Select(n => (double?)n.vol).FirstOrDefault();
|
|
|
|
if (overrideVol.HasValue)
|
|
{
|
|
return overrideVol.Value;
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
TradeVolatility tradeVol = null;
|
|
|
|
if (trade.id > 0)
|
|
{
|
|
tradeVol = db.TradeVolatility.AsNoTracking()
|
|
.Where(x => x.TradeId == trade.id && x.ValueDate <= valueDate)
|
|
.OrderByDescending(x => x.ValueDate).FirstOrDefault();
|
|
}
|
|
|
|
if (tradeVol == null || tradeVol.TradePositionVolatility == null || tradeVol.TradeCloseVolatility == null || tradeVol.NumOfSmoothingDays == null)
|
|
{
|
|
var openVol = trade.TradeOpenVolatility ?? 0;
|
|
var closeVol = trade.TradeCloseVolatility ?? openVol;
|
|
return GetTradeVol(valueDate, trade.StartDate.Value, trade.ExerciseDate.Value, openVol, closeVol, trade.NumOfSmoothingDays ?? 0);
|
|
}
|
|
|
|
//新增交易当天的持仓波动率需要划掉一天,修改后的持仓波动率不需要再划一天
|
|
|
|
return GetTradeVol(valueDate: valueDate,
|
|
valueStartDate: tradeVol.ValueDate,
|
|
exerciseDate: trade.ExerciseDate.Value,
|
|
openVol: tradeVol.TradePositionVolatility ?? 0,
|
|
closeVol: tradeVol.TradeCloseVolatility ?? 0,
|
|
mumOfSmoothingDays: tradeVol.NumOfSmoothingDays ?? 0,
|
|
includeStartDate: tradeVol.IsFromTradeAdd);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new Exception($"交易'{trade.TradeNumber}'获取TradeVol出错:{ex.Messages()}", ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static double GetTradeVol(DateTime valueDate, DateTime valueStartDate, DateTime exerciseDate
|
|
, double openVol, double closeVol, int mumOfSmoothingDays, bool includeStartDate = true)
|
|
{
|
|
if (valueDate < valueStartDate)
|
|
{
|
|
return openVol;
|
|
}
|
|
|
|
if (valueDate > exerciseDate)
|
|
{
|
|
return closeVol;
|
|
}
|
|
|
|
var daycountMode = PS.Config.ErpElement.SmoothingDaycountMode == Configuration.Enums.SmoothingDaycountMode.CalendarDay
|
|
? Qdp.Pricing.Base.Enums.DayCountMode.CalendarDay
|
|
: Qdp.Pricing.Base.Enums.DayCountMode.TradingDay;
|
|
|
|
return AnalyticalOptionTradeVolInterp.tradeVolLinearInterp(
|
|
new Qdp.Foundation.Implementations.Date(valueDate),
|
|
openVol, closeVol,
|
|
new Qdp.Foundation.Implementations.Date(valueStartDate),
|
|
new Qdp.Foundation.Implementations.Date(exerciseDate),
|
|
mumOfSmoothingDays,
|
|
daycountMode,
|
|
CalendarImpl.Get("chn"),
|
|
includeStartDate);
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取交易波动率曲面
|
|
/// </summary>
|
|
public static volatility GetSurfaceFromTradeVol(OtcTradeBase trade, DateTime valueDate, bool isEodSettle = false)
|
|
{
|
|
var constVol = GetTradeVol(trade, valueDate, isEodSettle);
|
|
var userGroup = UserBLL.GetUserGroup(trade.TraderId);
|
|
|
|
//生成3*3水平的波动率曲面
|
|
return GetDefaultVol(new SingleVolatilityRequest
|
|
{
|
|
UserGroup = userGroup,
|
|
QuotationDate = valueDate,
|
|
VolType = ConsVolInfos.defVolType,
|
|
UnderlyingCode = trade.UnderlyingCode,
|
|
UnderlyingId = trade.UnderlyingId,
|
|
TradeVolWithBidAsk = false
|
|
}, constVol);
|
|
}
|
|
|
|
#region----获取默认波动率----
|
|
|
|
/// <summary>
|
|
/// 获取默认波动率(不考虑标的是否过期)
|
|
/// </summary>
|
|
public static IEnumerable<VolatilityDefault> GetDefaultVols(SingleVolatilityRequest request, double volValue = ConsVolInfos.defVol)
|
|
{
|
|
if (request is null)
|
|
{
|
|
throw new ArgumentNullException(nameof(request));
|
|
}
|
|
|
|
if (PS.Config.Is光大光子 || !ConsVolInfos.TradeVolTypes.Contains(request.VolType))
|
|
{
|
|
return Enumerable.Empty<VolatilityDefault>();
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(request.UnderlyingCode))
|
|
{
|
|
if (!request.UnderlyingId.HasValue || request.UnderlyingId.Value < 1)
|
|
{
|
|
request.UnderlyingId = DataCacheProvider.GetUnderlyingDataSource().GetData(request.UnderlyingCode)?.id;
|
|
}
|
|
}
|
|
else if (request.UnderlyingId > 0)
|
|
{
|
|
request.UnderlyingCode = DataCacheProvider.GetUnderlyingDataSource().GetData(request.UnderlyingId.Value)?.UnderlyingCode;
|
|
}
|
|
|
|
var volaty = new VolatilityDefault
|
|
{
|
|
VolType = request.VolType,
|
|
QuotationDate = request.QuotationDate,
|
|
UserGroup = request.UserGroup,
|
|
|
|
UnderlyingId = request.UnderlyingId,
|
|
ContractCode = request.UnderlyingCode,
|
|
|
|
VolSurfaceMode = ConsVolInfos.defVolMode,
|
|
InterpolationMethod = ConsVolMethod.Default,
|
|
ReviewDownLimit = ConsVolInfos.defReviewDownLimit,
|
|
ReviewUpLimit = ConsVolInfos.defReviewUpLimit,
|
|
|
|
OptId = 0,
|
|
OptName = "系统",
|
|
OptDate = DateTime.Now
|
|
};
|
|
|
|
volaty.Data = QdpVolHelper.GenerateFlatSingleVols(volValue).ToJson();
|
|
|
|
return request.GetVolTypes().Select(n => (VolatilityDefault)volaty.Clone(n)).ToArray();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取默认波动率
|
|
/// </summary>
|
|
public static VolatilityDefault GetDefaultVol(SingleVolatilityRequest request, double volValue = ConsVolInfos.defVol)
|
|
{
|
|
var vols = GetDefaultVols(request, volValue);
|
|
|
|
if (vols != null & vols.Any())
|
|
{
|
|
return vols.First();
|
|
}
|
|
|
|
//一定要返回值否则某些计算会报错
|
|
return new VolatilityDefault
|
|
{
|
|
VolSurfaceMode = ConsVolInfos.defVolMode,
|
|
InterpolationMethod = ConsVolInfos.defInterpolationMethod,
|
|
|
|
VolType = request.VolType,
|
|
UserGroup = request.UserGroup,
|
|
QuotationDate = request.QuotationDate,
|
|
UnderlyingId = request.UnderlyingId,
|
|
ContractCode = request.UnderlyingCode,
|
|
Data = QdpVolHelper.GenerateFlatSingleVols(volValue).ToJson(),
|
|
|
|
OptId = 0,
|
|
OptName = "系统",
|
|
OptDate = DateTime.Now
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取默认波动率
|
|
/// </summary>
|
|
public static VolatilityDefault GetDefaultVol(string userGroup, DateTime quotationDate, string volType, string underlyingCode, int? underlyingId = null)
|
|
{
|
|
return GetDefaultVol(new SingleVolatilityRequest
|
|
{
|
|
QuotationDate = quotationDate,
|
|
TradeVolWithBidAsk = false,
|
|
UnderlyingCode = underlyingCode,
|
|
UnderlyingId = underlyingId,
|
|
UserGroup = userGroup,
|
|
VolType = volType
|
|
});
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region----从波动率曲面取一个点----
|
|
|
|
public static double GetInterpolatedVol(
|
|
VolConstructionType volConstructionType,
|
|
IVolatility volSurface,
|
|
DateTime valueDate,
|
|
string underlyingCode,
|
|
DateTime exerciseDate,
|
|
double strike,
|
|
bool isBuy,
|
|
bool isCall,
|
|
double spotPrice,
|
|
bool isMoneynessOption = true,
|
|
double timeToMaturityDays = double.NaN,
|
|
int? skewMapVolVar = null,
|
|
bool isEodCalc = false)
|
|
{
|
|
if (volConstructionType == VolConstructionType.SkewMap)
|
|
{
|
|
return SkewMapVolHelper.GetInterpolatedVol(
|
|
volSurface: volSurface,
|
|
valueDate: valueDate,
|
|
underlyingCode: underlyingCode,
|
|
exerciseDate: exerciseDate,
|
|
strikePrice: strike,
|
|
isBuy: isBuy,
|
|
isCall: isCall,
|
|
spotPrice: spotPrice,
|
|
timeToMaturityDays: timeToMaturityDays,
|
|
skewMapVolVar: skewMapVolVar);
|
|
}
|
|
else
|
|
{
|
|
return QdpVolHelper.GetInterpolatedVolFromNormalSurface(
|
|
volSurface.VolTable,
|
|
new InterpolatedVolReq()
|
|
{
|
|
valueDate = valueDate,
|
|
exerciseDate = exerciseDate,
|
|
strike = strike,
|
|
isMoneynessOption = isMoneynessOption,
|
|
isEodCalc = isEodCalc,
|
|
volSurfaceType = volSurface.VolSurfaceMode,
|
|
spot = spotPrice
|
|
},
|
|
volSurface.InterpolationMethod);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// 导出用的矩阵
|
|
/// </summary>
|
|
public static DataTable GetMatrix(IEnumerable<volatility> vols)
|
|
{
|
|
var dataTable = new DataTable();
|
|
|
|
var dataColumns = new DataColumn[10];
|
|
for (var i = 0; i < dataColumns.Length; i++)
|
|
{
|
|
dataColumns[i] = new DataColumn();
|
|
}
|
|
dataTable.Columns.AddRange(dataColumns);
|
|
|
|
var strikeSet = new HashSet<double>(10);
|
|
|
|
var volFormat = PS.Config.ErpElement.VolMoreAccurate ? "0.0###%" : "0.0%";
|
|
try
|
|
{
|
|
foreach (var vol in vols)
|
|
{
|
|
strikeSet.Clear();
|
|
|
|
//转换并按到期分组
|
|
var lookup = vol.VolTable.Select(n =>
|
|
{
|
|
strikeSet.Add(n.Strike);
|
|
return new { n.Expire, Strike = n.Strike.OtcFormatUmPrice(true), Vol = n.Vol.ToString(volFormat) };
|
|
}).OrderBy(n => new Term(n.Expire)).ToLookup(n => n.Expire);
|
|
|
|
//写入标的和执行价
|
|
var strikes = strikeSet.OrderBy(n => n).Select(n => n.OtcFormatUmPrice(true)).ToArray();
|
|
var values = strikes.Prepend(vol.ContractCode).ToArray();
|
|
while (values.Length > dataTable.Columns.Count)
|
|
{
|
|
dataTable.Columns.Add();
|
|
}
|
|
dataTable.Rows.Add(values);
|
|
|
|
//写入到期和波动率
|
|
foreach (var g in lookup)
|
|
{
|
|
var garr = g.ToArray();
|
|
var query = from s in strikes
|
|
join item in garr on s equals item.Strike into items
|
|
from item in items
|
|
select item?.Vol;
|
|
values = query.ToArray().Prepend(g.Key).ToArray();
|
|
dataTable.Rows.Add(values);
|
|
}
|
|
|
|
dataTable.Rows.Add();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
System.Diagnostics.Debug.WriteLine(ex);
|
|
}
|
|
|
|
return dataTable;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取曲面波动率类型
|
|
/// </summary>
|
|
/// <param name="calcVolType">用于计算的波动率类型</param>
|
|
/// <returns></returns>
|
|
public static string GetUnderlyingVolType(string calcVolType)
|
|
{
|
|
calcVolType = calcVolType.TrimToNull();
|
|
|
|
if (PS.Config.IsTradeVol)
|
|
{
|
|
return calcVolType switch
|
|
{
|
|
null or "持仓" or "对冲" or "交易曲面" => "交易",
|
|
_ => calcVolType
|
|
};
|
|
}
|
|
|
|
return calcVolType switch
|
|
{
|
|
null or "持仓" => valuedateBLL.SystemDate.EodSettleVolMode.TrimToNull() ?? "财务",
|
|
"对冲" or "交易曲面" => "交易",
|
|
_ => calcVolType,
|
|
};
|
|
}
|
|
}
|
|
}
|