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
{
///
/// 波动率操作帮助类
///
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);
}
///
/// 获取结算波动率
///
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);
}
///
/// 获取隐含波动率
///
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);
}
///
/// 获取交易波动率
///
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);
}
///
/// 获取交易波动率曲面
///
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----获取默认波动率----
///
/// 获取默认波动率(不考虑标的是否过期)
///
public static IEnumerable 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();
}
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();
}
///
/// 获取默认波动率
///
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
};
}
///
/// 获取默认波动率
///
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
///
/// 导出用的矩阵
///
public static DataTable GetMatrix(IEnumerable 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(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;
}
///
/// 获取曲面波动率类型
///
/// 用于计算的波动率类型
///
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,
};
}
}
}