305 lines
8.8 KiB
C#
305 lines
8.8 KiB
C#
using YLErp.DBModels.Consts;
|
|
using YLErp.Modules.VolatilityModule;
|
|
using YLErp.QdpModule;
|
|
|
|
namespace YLErp.Modules.EodModule.SettlementModule
|
|
{
|
|
/// <summary>
|
|
/// 结算波动率接口(暂未考虑多标的处理)
|
|
/// </summary>
|
|
public interface IEodVolProvider
|
|
{
|
|
double? GetVol(OtcTradeBase td, double spotPrice);
|
|
}
|
|
|
|
#region----持仓波动率----
|
|
|
|
/// <summary>
|
|
/// TradeVol模式持仓波动率提供
|
|
/// </summary>
|
|
class EodPositionVolProvider_Trade : TradeVolitalityProvider, IEodVolProvider
|
|
{
|
|
Dictionary<int, double> _overridVolDic;
|
|
|
|
public EodPositionVolProvider_Trade(DateTime valueDate) : base(valueDate)
|
|
{
|
|
|
|
}
|
|
|
|
public double? GetVol(OtcTradeBase td, double spotPrice)
|
|
{
|
|
if (td is null)
|
|
{
|
|
throw new ArgumentNullException(nameof(td));
|
|
}
|
|
|
|
if (td.id < 1)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (_overridVolDic == null)
|
|
{
|
|
using (var db = DbContextFactory.GetYLDbContext())
|
|
{
|
|
_overridVolDic = db.eod_trade_vol_override.Where(x => x.valuedate == _valueDate)
|
|
.Select(n => new { n.tradeid, n.vol }).ToDictionary(n => n.tradeid, m => m.vol);
|
|
}
|
|
}
|
|
|
|
if (_overridVolDic.TryGetValue(td.id, out var vol))
|
|
{
|
|
return vol;
|
|
}
|
|
|
|
if (TryGetVol(td.id, td.ExerciseDate.Value, out vol))
|
|
{
|
|
return vol;
|
|
}
|
|
|
|
return VolatilityHelper.GetTradeVol(_valueDate, td.TradeDate.Value, td.ExerciseDate.Value,
|
|
td.TradeOpenVolatility ?? 0, td.TradeCloseVolatility ?? 0, td.NumOfSmoothingDays ?? 0);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region----曲面波动率----
|
|
|
|
/// <summary>
|
|
/// 曲面波动率模式持仓波动率提供
|
|
/// </summary>
|
|
class EodUnderlyingVolProvider : UnderlyingVolitalityProvider
|
|
{
|
|
protected EodUnderlyingVolProvider(DateTime valueDate, IEnumerable<string> volTypes) : base(valueDate, volTypes)
|
|
{
|
|
|
|
}
|
|
|
|
protected double? InnerGetVol(OtcTradeBase td, string volType, double spotPrice)
|
|
{
|
|
if (td is null)
|
|
{
|
|
throw new ArgumentNullException(nameof(td));
|
|
}
|
|
|
|
var userGroup = string.Empty;
|
|
|
|
if (ConsUserGroup.HasGroup)
|
|
{
|
|
userGroup = DataCacheProvider.GetAssetUnitDataSource().GetData(td.AssetId)?.UserGroup;
|
|
|
|
if (string.IsNullOrEmpty(userGroup))
|
|
{
|
|
throw new Exception($"[结算交易'{td.TradeNumber}'][取{volType}波动率]未能获取用户组!");
|
|
}
|
|
}
|
|
|
|
var unVol = GetVol(volType, userGroup, td.UnderlyingCode);
|
|
|
|
if (unVol == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var constVol = VolatilityHelper.GetInterpolatedVol(
|
|
volConstructionType: PS.Config.ErpElement.SkewMapVolConstruction ? VolConstructionType.SkewMap : VolConstructionType.Normal,
|
|
volSurface: unVol,
|
|
valueDate: _valueDate,
|
|
underlyingCode: td.UnderlyingCode,
|
|
exerciseDate: td.ExerciseDate.Value,
|
|
strike: td.Strike ?? 0,
|
|
isBuy: td.BuySell == "买入",
|
|
isCall: td.CallPut == "Call",
|
|
spotPrice: spotPrice,
|
|
isMoneynessOption: td.IsMoneynessOption == "是",
|
|
isEodCalc: true);
|
|
|
|
return constVol;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 单个曲面波动率类型
|
|
/// </summary>
|
|
class EodUnderlyingSingleVolProvider : EodUnderlyingVolProvider, IEodVolProvider
|
|
{
|
|
readonly string _volType;
|
|
|
|
public EodUnderlyingSingleVolProvider(DateTime valueDate, string volType)
|
|
: base(valueDate, new[] { volType })
|
|
{
|
|
_volType = volType ?? throw new ArgumentNullException(nameof(volType));
|
|
}
|
|
|
|
public double? GetVol(OtcTradeBase td, double spotPrice)
|
|
{
|
|
return InnerGetVol(td, _volType, spotPrice);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 曲面波动率模式持仓波动率提供
|
|
/// </summary>
|
|
class EodPositionVolProvider_Underlying : EodUnderlyingVolProvider, IEodVolProvider
|
|
{
|
|
readonly string _volType;
|
|
|
|
Dictionary<int, double> _overridVolDic;
|
|
|
|
public EodPositionVolProvider_Underlying(DateTime valueDate, string volType)
|
|
: base(valueDate, new[] { volType })
|
|
{
|
|
_volType = volType ?? throw new ArgumentNullException(nameof(volType));
|
|
}
|
|
|
|
public double? GetVol(OtcTradeBase td, double spotPrice)
|
|
{
|
|
if (_overridVolDic == null)
|
|
{
|
|
using (var db = DbContextFactory.GetYLDbContext())
|
|
{
|
|
_overridVolDic = db.eod_trade_vol_override.Where(x => x.valuedate == _valueDate)
|
|
.Select(n => new { n.tradeid, n.vol }).ToDictionary(n => n.tradeid, m => m.vol);
|
|
}
|
|
}
|
|
|
|
if (_overridVolDic.TryGetValue(td.id, out var vol))
|
|
{
|
|
return vol;
|
|
}
|
|
|
|
return InnerGetVol(td, _volType, spotPrice);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region----对冲波动率----
|
|
|
|
/// <summary>
|
|
/// 对冲波动率提供
|
|
/// </summary>
|
|
class EodHedgeVolProvider : TradeHedgeVolProvider, IEodVolProvider
|
|
{
|
|
public EodHedgeVolProvider(DateTime valueDate) : base(valueDate)
|
|
{
|
|
|
|
}
|
|
|
|
public double? GetVol(OtcTradeBase td, double spotPrice)
|
|
{
|
|
if (td is null)
|
|
{
|
|
throw new ArgumentNullException(nameof(td));
|
|
}
|
|
|
|
if (TryGetVol(td.id, out var vol))
|
|
{
|
|
return vol;
|
|
}
|
|
|
|
return td.TradeSavedVol ?? 0;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 开仓波动率提供(开仓波动率只在TradeVol模式下结算)
|
|
/// </summary>
|
|
class EodOpenVolProvider : TradeHedgeVolProvider, IEodVolProvider
|
|
{
|
|
public EodOpenVolProvider(DateTime valueDate) : base(valueDate)
|
|
{
|
|
|
|
}
|
|
|
|
public double? GetVol(OtcTradeBase td, double spotPrice)
|
|
{
|
|
return td?.TradeOpenVolatility;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region----扩展波动率----
|
|
|
|
/// <summary>
|
|
/// 扩展波动率(光证)提供
|
|
/// </summary>
|
|
class EodExtendVolProvider_GZ : VarietyVolProvider, IEodVolProvider
|
|
{
|
|
public EodExtendVolProvider_GZ(DateTime valueDate) : base(valueDate)
|
|
{
|
|
|
|
}
|
|
|
|
public double? GetVol(OtcTradeBase td, double spotPrice)
|
|
{
|
|
if (td is null)
|
|
{
|
|
throw new ArgumentNullException(nameof(td));
|
|
}
|
|
|
|
if (TryGetVol(td.UnderlyingCode, out var vol))
|
|
{
|
|
return vol;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 扩展波动率(BidAskVol)提供
|
|
/// </summary>
|
|
class EodExtendVolProvider_BidAskVol : EodUnderlyingVolProvider, IEodVolProvider
|
|
{
|
|
public EodExtendVolProvider_BidAskVol(DateTime valueDate)
|
|
: base(valueDate, new[] { "报价Ask", "报价Bid" })
|
|
{
|
|
}
|
|
|
|
public double? GetVol(OtcTradeBase td, double spotPrice)
|
|
{
|
|
if (td is null)
|
|
{
|
|
throw new ArgumentNullException(nameof(td));
|
|
}
|
|
|
|
return InnerGetVol(td, td.BuySell == "买入" ? "报价Ask" : "报价Bid", spotPrice);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// 波动率提供者工厂
|
|
/// </summary>
|
|
public class EodVolProviderFactory
|
|
{
|
|
public static IEodVolProvider GetEodVolProvider(DateTime valueDate, string volType)
|
|
{
|
|
switch (volType)
|
|
{
|
|
case "持仓":
|
|
if (PS.Config.IsTradeVol)
|
|
{
|
|
return new EodPositionVolProvider_Trade(valueDate);
|
|
}
|
|
volType = BLL.valuedateBLL.SystemDate.EodSettleVolMode.TrimToNull() ?? "财务";
|
|
return new EodPositionVolProvider_Underlying(valueDate, volType);
|
|
case "开仓":
|
|
return new EodOpenVolProvider(valueDate);
|
|
case "对冲":
|
|
return new EodHedgeVolProvider(valueDate);
|
|
case "BidAskVol":
|
|
return new EodExtendVolProvider_BidAskVol(valueDate);
|
|
case "光证":
|
|
return new EodExtendVolProvider_GZ(valueDate);
|
|
default:
|
|
return new EodUnderlyingSingleVolProvider(valueDate, volType);
|
|
}
|
|
}
|
|
}
|
|
}
|