605 lines
26 KiB
C#
605 lines
26 KiB
C#
using Org.BouncyCastle.Ocsp;
|
|
using Qdp.Pricing.Base.Implementations;
|
|
using YLErp.Abstract;
|
|
using YLErp.Abstract.DataProviders;
|
|
using YLErp.BLL;
|
|
using YLErp.Configuration;
|
|
using YLErp.DBModels.Consts;
|
|
using YLErp.DBModels.Enums;
|
|
using YLErp.Enums;
|
|
using YLErp.Model;
|
|
using YLErp.Modules.ApiModule;
|
|
using YLErp.Modules.DataProviderModule;
|
|
using YLErp.Modules.VolatilityModule;
|
|
using YLErp.QdpModule;
|
|
|
|
namespace YLErp.Modules.CalculationModule
|
|
{
|
|
//这个类的主要目标是代替CalculatorHelper.CalculateRisksForTrades
|
|
//并且不再支持日终结算的计算,不再支持场内期权
|
|
|
|
/// <summary>
|
|
/// 简单场外衍生品交易风险计算
|
|
/// </summary>
|
|
class SimpleOtcTradeRiskCalc
|
|
{
|
|
/// <summary>
|
|
/// 波动率类型,默认:交易
|
|
/// </summary>
|
|
public string VolType { get; set; } = "交易";
|
|
|
|
public SettlementTypeEnum SettlementType { get; set; } = SettlementTypeEnum.ClosePrice;
|
|
|
|
/// <summary>
|
|
/// 覆盖交易波动率
|
|
/// </summary>
|
|
public Dictionary<int, double> OverrideVolsForTrade { get; set; }
|
|
|
|
/// <summary>
|
|
/// 是否使用交易波动率,默认false
|
|
/// </summary>
|
|
public bool IsUseTradeVol { get; set; }
|
|
|
|
/// <summary>
|
|
/// 精确时间模式,默认true
|
|
/// </summary>
|
|
public bool PreciseTimeMode { get; set; } = true;
|
|
|
|
/// <summary>
|
|
/// 是否增加波动率为百分比格式
|
|
/// </summary>
|
|
public bool IsAddVolPercent { get; set; } = true;
|
|
|
|
/// <summary>
|
|
/// 增加波动率
|
|
/// </summary>
|
|
public Dictionary<int, double> AddVolRateDic { get; set; }
|
|
|
|
/// <summary>
|
|
/// 计算指标枚举,默认只计算PV
|
|
/// </summary>
|
|
public PricingRequest PricingRequest { get; set; } = PricingRequest.Pv;
|
|
|
|
/// <summary>
|
|
/// 是否在进行预付金计算
|
|
/// </summary>
|
|
public bool IsMarginCalc { get; set; } = false;
|
|
|
|
/// <summary>
|
|
/// 计算场景
|
|
/// </summary>
|
|
public CalcScenarioEnum CalcScenario { get; set; }
|
|
|
|
/// <summary>
|
|
/// 是否使用手动维护的风险值
|
|
/// </summary>
|
|
public bool CanUseManual { get; set; } = false;
|
|
|
|
DateTime _valueDate;
|
|
IPriceProvider _priceProvider;
|
|
List<string> _errorList;
|
|
readonly UnderlyingDataProvider _umProvider;
|
|
|
|
public SimpleOtcTradeRiskCalc()
|
|
{
|
|
_umProvider = new UnderlyingDataProvider();
|
|
}
|
|
|
|
bool IsEodSettle => CalcScenario == CalcScenarioEnum.EodSettlement;
|
|
bool IsInitialMargin => CalcScenario == CalcScenarioEnum.InitialMargin;
|
|
|
|
private void AppendError(trade td, string errorMsg, Exception ex = null)
|
|
{
|
|
if (ex != null)
|
|
{
|
|
errorMsg = string.IsNullOrEmpty(errorMsg) ? ex.Messages() : errorMsg + "," + ex.Messages();
|
|
}
|
|
|
|
errorMsg = $"[{td.TradeType},交易编号:{td.TradeNumber}]计算出错:{errorMsg}";
|
|
|
|
if (IsEodSettle)
|
|
{
|
|
throw new Exception(errorMsg, ex);
|
|
}
|
|
|
|
if (_errorList == null)
|
|
{
|
|
_errorList = new List<string>();
|
|
}
|
|
|
|
_errorList.Add(errorMsg);
|
|
}
|
|
|
|
public TradeRiskResult CalculateRisksForTrades(DateTime valueDate, IEnumerable<trade> tradeList, IPriceProvider priceProvider, CalcScenarioEnum calcScenario)
|
|
{
|
|
var results = new List<TradeRiskResultRecord>();
|
|
var trResult = new TradeRiskResult { Results = results };
|
|
|
|
if (tradeList == null || !tradeList.Any())
|
|
{
|
|
return trResult;
|
|
}
|
|
|
|
if (IsEodSettle)
|
|
{
|
|
PreciseTimeMode = false;
|
|
}
|
|
|
|
_valueDate = valueDate;
|
|
_priceProvider = priceProvider ?? throw new ArgumentNullException(nameof(priceProvider));
|
|
|
|
var sysRiskFreeRate = (VolType == "光证" ? valuedateBLL.RiskFreeRateExtend : valuedateBLL.RiskFreeRate) / 100;
|
|
|
|
var recordlist = DbContextFactory.GetYLDbContext().dividendrate_record.Where(x => valueDate >= x.ValueDate).ToList();
|
|
|
|
using (var marketProxy = new MarketProxy(_valueDate, sysRiskFreeRate))
|
|
{
|
|
using var db = DbContextFactory.GetYLDbContext();
|
|
var tradeIds = tradeList.Select(x => x.id).ToArray();
|
|
var manualDicByTypes = db.eod_trade_risk_manual.Where(x => x.ValueDate == _valueDate && tradeIds.Contains(x.TradeId) && x.VolType == VolType && x.SettlementType == SettlementType).ToDictionary(x => x.TradeId);
|
|
var manualDic = db.eod_trade_risk_manual.Where(x => x.ValueDate == _valueDate && tradeIds.Contains(x.TradeId) && string.IsNullOrEmpty(x.VolType)).ToDictionary(x => x.TradeId);
|
|
foreach (var td in tradeList)
|
|
{
|
|
if (VolType == "光证")
|
|
{
|
|
td.NoRiskRate = sysRiskFreeRate;
|
|
}
|
|
|
|
//国元固收默认从全局配置里取分红率,如果TradeHisData里有分红率,则根据生效时间取最优
|
|
if (recordlist != null && recordlist.Any())
|
|
{
|
|
var _record = recordlist.Where(x => x.UnderlyingCode.Split(',').Any(code => code == td.UnderlyingCode) && x.TradeType.Contains(td.TradeType) && (x.OptionType == td.OptionType || x.OptionType == "全部")).OrderByDescending(x => x.OptDate).OrderByDescending(x => x.ValueDate);
|
|
if (_record.Any())
|
|
{
|
|
td.DividendRate = _record.FirstOrDefault()?.DividendRate;
|
|
}
|
|
}
|
|
|
|
td.DividendRate = td.DividendRate ?? td.NoRiskRate ?? sysRiskFreeRate;
|
|
|
|
TradeRiskResultRecord record;
|
|
|
|
if (IsMarginCalc && td.TradeType == "自定义交易")
|
|
{
|
|
record = new TradeRiskResultRecord
|
|
{
|
|
Trade = td,
|
|
Underlyings = new[] { DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode) },
|
|
ValueResult = new TradeValueResult() //自定义交易的预付金不需要在这里处理
|
|
};
|
|
if (PS.Config.ErpElement.ExternalAPIForCustomCalcEnable)
|
|
{
|
|
td.StartDate = _valueDate;
|
|
var tradeVol = IsUseTradeVol;
|
|
IsUseTradeVol = false;
|
|
var price = _priceProvider.GetPrice(td.UnderlyingCode);
|
|
var um = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode);
|
|
var consVol = GetConstVol(td, price, IsEodSettle, um?.UnderlyingTypeId ?? 0);
|
|
//从风险对冲那边抄来的逻辑,波动率精度会导致计算结果和风险对冲有差异;
|
|
var volValue = Commons.OtcFormatHelper.FormatValue(consVol, PS.Config.ErpElement.VolMoreAccurate ? 6 : 4);
|
|
|
|
var apiValue = TradeCalcApiHelper.CalculateCustomizedTrade(_valueDate, td, price, volValue, CalcScenario == CalcScenarioEnum.EodSettlement, calcScenario, TradeCalcApiHelper.PV, TradeCalcApiHelper.DELTA, TradeCalcApiHelper.GAMMA, TradeCalcApiHelper.VEGA, TradeCalcApiHelper.THETA, TradeCalcApiHelper.RHO);
|
|
|
|
if (apiValue.Success)
|
|
{
|
|
record.ValueResult = apiValue.Content;
|
|
}
|
|
//异常
|
|
else
|
|
{
|
|
throw new Exception(apiValue.Msg);
|
|
}
|
|
}
|
|
}
|
|
else if (td.TradeType != "场内期权" && ConsTrade.TradeTypesForHedge.Contains(td.TradeType))
|
|
{
|
|
record = new TradeRiskResultRecord
|
|
{
|
|
Trade = td,
|
|
Underlyings = new[] { DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode) },
|
|
ValueResult = new TradeValueResult()
|
|
{
|
|
Pv = td.Notional * _priceProvider.GetPrice(td.UnderlyingCode),
|
|
Delta = td.Notional
|
|
}
|
|
};
|
|
}
|
|
else
|
|
{
|
|
//分类维护的风险数据优先级高于普通的风险数据
|
|
manualDicByTypes.TryGetValue(td.id, out var manual);
|
|
if (manual == null)
|
|
{
|
|
manualDic.TryGetValue(td.id, out manual);
|
|
}
|
|
else
|
|
{
|
|
//如果分类维护的数据不全,通过单一数据补充
|
|
manualDic.TryGetValue(td.id, out var singleManual);
|
|
if (singleManual != null)
|
|
{
|
|
manual.Pv = manual.Pv ?? singleManual.Pv;
|
|
manual.Delta = manual.Delta ?? singleManual.Delta;
|
|
manual.DeltaCash = manual.DeltaCash ?? singleManual.DeltaCash;
|
|
manual.Gamma = manual.Gamma ?? singleManual.Gamma;
|
|
manual.GammaCash = manual.GammaCash ?? singleManual.GammaCash;
|
|
manual.Theta = manual.Theta ?? singleManual.Theta;
|
|
manual.Vega = manual.Vega ?? singleManual.Vega;
|
|
manual.VegaCash = manual.VegaCash ?? singleManual.VegaCash;
|
|
manual.Rho = manual.Rho ?? singleManual.Rho;
|
|
}
|
|
}
|
|
record = CalcOtcTrade(marketProxy, td, CanUseManual ? manual : null);
|
|
}
|
|
|
|
if (record != null)
|
|
{
|
|
if (PS.Config.ErpElement.IsPVIncludePrincipal)
|
|
{
|
|
record.ValueResult.Pv = record.ValueResult.Pv + td.PrincipalSum() * (td.BuySell == "卖出" ? -1 : 1);
|
|
record.ValueResult.RoundedPv = record.ValueResult.RoundedPv + td.PrincipalSum() * (td.BuySell == "卖出" ? -1 : 1);
|
|
}
|
|
|
|
results.Add(record);
|
|
}
|
|
}
|
|
}
|
|
|
|
return trResult;
|
|
}
|
|
|
|
//计算衍生品交易价值
|
|
private TradeRiskResultRecord CalcOtcTrade(MarketProxy mp, trade td, eod_trade_risk_manual manual)
|
|
{
|
|
var record = new TradeRiskResultRecord { Trade = td, ValueResult = null, Underlyings = null };
|
|
|
|
try
|
|
{
|
|
var um = _umProvider.GetUnderlying(td.UnderlyingCode);
|
|
|
|
record.Underlyings = new[] { um };
|
|
|
|
if (um == null && td.HasUnderlying())
|
|
{
|
|
AppendError(td, "未找到标的信息:" + td.UnderlyingCode);
|
|
record.ValueResult = GetTradeValueResult(td, "未找到标的信息");
|
|
return record;
|
|
}
|
|
|
|
if (ConsTrade.TradeCompleteStatus.Contains(td.TradeStatus))
|
|
{
|
|
record.ValueResult = GetTradeValueResult(td);
|
|
return record;
|
|
}
|
|
|
|
TradeValueResult result = null;
|
|
|
|
if (manual != null)
|
|
{
|
|
var spotPrice = GetSpotPrice(td);
|
|
manual.DeltaCash = manual.Delta * spotPrice;
|
|
manual.GammaCash = manual.Gamma * Math.Pow(spotPrice, 2) / 100;
|
|
manual.VegaCash = manual.Vega * spotPrice;
|
|
result = new TradeValueResult
|
|
{
|
|
Pv = manual.Pv ?? 0,
|
|
RoundedPv = manual.Pv ?? 0,
|
|
Delta = manual.Delta ?? 0,
|
|
Gamma = manual.Gamma ?? 0,
|
|
Vega = manual.Vega ?? 0,
|
|
TradingDayTheta = manual.Theta ?? 0,
|
|
CalendarDayTheta = manual.Theta ?? 0,
|
|
Rho = manual.Rho ?? 0,
|
|
DeltaCash = manual.DeltaCash ?? 0,
|
|
GammaCash = manual.GammaCash ?? 0
|
|
};
|
|
}
|
|
|
|
//没有自定义风险维护值 或者 自定风险维护没有涵盖PV和所有希腊值(部分维护场景)
|
|
//部分维护场景下,需要系统计算出未赋值的属性,进行合并返还
|
|
if (manual == null || manual != null && !manual.IsPVAndAllGreek)
|
|
{
|
|
switch (td.TradeType)
|
|
{
|
|
case "彩虹期权":
|
|
case "价差期权":
|
|
case "结构化交易":
|
|
AppendError(td, "不支持此交易类型的计算");
|
|
result = GetTradeValueResult(td, "不支持此交易类型的计算");
|
|
break;
|
|
case ConsGlobal.TradeType.Custom:
|
|
result = CalcCustom(td, IsEodSettle, um?.UnderlyingTypeId ?? 0);
|
|
break;
|
|
case ConsGlobal.TradeType.PayoffSwap:
|
|
result = PayoffSwapCalcService.CalcValue(td, _valueDate, IsMarginCalc ? null : _priceProvider, IsEodSettle);
|
|
break;
|
|
case ConsGlobal.TradeType.Forward:
|
|
{
|
|
var spotPrice = GetSpotPrice(td);
|
|
result = ForwardradeCalcService.CalcValue(td, spotPrice);
|
|
}
|
|
break;
|
|
case "信用债":
|
|
case "商品期货":
|
|
_priceProvider.TryGetPrice(td.UnderlyingCode, out var spotPriceGet);
|
|
result = new TradeValueResult()
|
|
{
|
|
Pv = td.Notional * spotPriceGet,
|
|
};
|
|
break;
|
|
case "场内期权":
|
|
default:
|
|
result = CalcOption(mp, td, IsEodSettle, IsInitialMargin, um?.UnderlyingTypeId ?? 0); //计算场外期权
|
|
break;
|
|
}
|
|
|
|
//系统计算结果和手动维护值合并
|
|
TradeRiskCalcUtil.GetOptionValueWithManual(manual, result);
|
|
}
|
|
else
|
|
{
|
|
if (td.TradeType != ConsGlobal.TradeType.CashFlow && result.Vol == 0)
|
|
{
|
|
result.Vol = GetConstVol(td, GetSpotPrice(td), IsEodSettle, um?.UnderlyingTypeId ?? 0);
|
|
}
|
|
}
|
|
|
|
record.ValueResult = result;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
AppendError(td, null, ex);
|
|
record.ValueResult = GetTradeValueResult(td, ex.Message);
|
|
}
|
|
|
|
return record;
|
|
}
|
|
|
|
private TradeValueResult GetTradeValueResult(trade td, string errmsg = null)
|
|
{
|
|
return new TradeValueResult
|
|
{
|
|
TradeId = td.id,
|
|
UnderlyingCode = td.UnderlyingCode,
|
|
Strike = td.Strike,
|
|
UnderlyingId = td.UnderlyingId,
|
|
ErrorMessage = errmsg,
|
|
Succeeded = string.IsNullOrEmpty(errmsg)
|
|
};
|
|
}
|
|
|
|
//获取标的现价
|
|
private double GetSpotPrice(trade td)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(td.UnderlyingCode))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
//20210706:支持参考价处理 -- 远期没有参考价
|
|
if (td.SettlementType == SettlementTypeEnum.ReferencePrice
|
|
&& _priceProvider.TryGetPrice(ConsGlobal.RefPricePrefix + td.UnderlyingCode, out var price))
|
|
{
|
|
return price;
|
|
}
|
|
|
|
double BasiseodPrice = 0;
|
|
if (PS.Config.ErpElement.ForwardTradePriceModel == Configuration.Enums.ForwardTradePriceModel.STANDARD && !string.IsNullOrWhiteSpace(td.BasisUnderlyingCode) && td.TradeType == ConsGlobal.TradeType.Forward)
|
|
{
|
|
_priceProvider.TryGetPrice(td.BasisUnderlyingCode, out BasiseodPrice);
|
|
}
|
|
|
|
if (_priceProvider.TryGetPrice(td.UnderlyingCode, out price))
|
|
{
|
|
return price -= BasiseodPrice;
|
|
}
|
|
else
|
|
{
|
|
throw new Exception("未找到标的价格:" + td.UnderlyingCode);
|
|
}
|
|
|
|
}
|
|
|
|
//计算自定义交易
|
|
private TradeValueResult CalcCustom(trade td, bool isEodCalc, int underlyingTypeId)
|
|
{
|
|
var spotPrice = GetSpotPrice(td);
|
|
var volValue = GetConstVol(td, spotPrice, isEodCalc, underlyingTypeId);
|
|
//从风险对冲那边抄来的逻辑,波动率精度会导致计算结果和风险对冲有差异;
|
|
volValue = Commons.OtcFormatHelper.FormatValue(volValue, PS.Config.ErpElement.VolMoreAccurate ? 6 : 4);
|
|
|
|
var (manual, optionValue) = TradeRiskCalcUtil.GetManualOptionValue(_valueDate, td, spotPrice, volValue, td.TradeType == "自定义交易", CalcScenario, VolType, SettlementType, isSettle: CalcScenario == CalcScenarioEnum.EodSettlement);
|
|
|
|
//收盘时如果自定义交易还活着且没有维护当日风险,并且收的时系统日期当日的盘,抛出exception
|
|
if (IsEodSettle && !ConsTrade.TradeCompleteStatus.Contains(td.TradeStatus) && _valueDate == valuedateBLL.ValueDate && !PS.Config.IsMustRiskManual)
|
|
{
|
|
if ((manual == null || manual.ValueDate != _valueDate) && !PS.Config.Is润和)
|
|
{
|
|
var error = $"[{td.TradeType}] 交易'{td.TradeNumber}'在{_valueDate:yyyy-MM-dd}需先进行交易风险维护";
|
|
throw new Exception(error);
|
|
}
|
|
}
|
|
|
|
return optionValue;
|
|
}
|
|
|
|
//计算场外期权
|
|
private TradeValueResult CalcOption(MarketProxy mp, trade td, bool isEodCalc, bool isInitialMargin, int underlyingTypeId)
|
|
{
|
|
var spotPrice = GetSpotPrice(td);
|
|
|
|
var req = new OptionValueCalcRequest(mp.RiskFreeRate)
|
|
{
|
|
correlations = null, //因为没有处理多标的,所以这里为null
|
|
engineName = null,
|
|
maturityShift = 0,
|
|
preciseTimeMode = PreciseTimeMode,
|
|
ParamOverride = null,
|
|
pricingRequest = PricingRequest,
|
|
spotPrices = new[] { spotPrice },
|
|
vols = new[] { 0d },
|
|
calcScenario = CalcScenario
|
|
};
|
|
req.timeToMaturityDays = double.NaN;
|
|
//象屿最后一个交易日实时计算时TTM需要和平仓时算法一致
|
|
if (PS.Config.Is厦门象屿
|
|
&& (CalcScenario == CalcScenarioEnum.RealtimePosition || CalcScenario == CalcScenarioEnum.RealtimeRisk)
|
|
&& td.SettlementType == SettlementTypeEnum.ReferencePrice)
|
|
{
|
|
req.timeToMaturityDays = TradeCalcHelper.CalculateTTMDaysForXiangYu(valuedateBLL.ValueDate, td.ExerciseDate.Value, underlyingTypeId, false);
|
|
//20210706:支持厦门象屿参考价相关交易(这类交易不需要支持精确模式)
|
|
req.preciseTimeMode = false;
|
|
}
|
|
|
|
if (PS.Config.Is润和 && IsInitialMargin)
|
|
{
|
|
req.timeToMaturityDays = td.TTMDays;
|
|
}
|
|
|
|
if (td.TradeType != ConsGlobal.TradeType.CashFlow)
|
|
{
|
|
var consVol = GetConstVol(td, spotPrice, isEodCalc, underlyingTypeId);
|
|
//从风险对冲那边抄来的逻辑,波动率精度会导致计算结果和风险对冲有差异;
|
|
consVol = Commons.OtcFormatHelper.FormatValue(consVol, PS.Config.ErpElement.VolMoreAccurate ? 6 : 4);
|
|
req.vols = new[] { consVol };
|
|
if (IsMarginCalc && CalcScenario == CalcScenarioEnum.EodSettlement)
|
|
{
|
|
LogFactory.GetLogger("日终预付金计算").Info($"{td.TradeType}'{td.TradeNumber}',波动率:{consVol},spotPrice:{spotPrice}");
|
|
}
|
|
}
|
|
|
|
req.isEodCalc = IsEodSettle;
|
|
return OptionCalculatorV2.GetOptionValueResult(mp, td, req, out _);
|
|
}
|
|
|
|
#region----获取波动率----
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="td"></param>
|
|
/// <param name="spotPrice"></param>
|
|
/// <param name="isEodCalc"></param>
|
|
/// <param name="underlyingTypeId">只适用于光证波动率</param>
|
|
/// <returns></returns>
|
|
private double GetConstVol(trade td, double spotPrice, bool isEodCalc, int underlyingTypeId)
|
|
{
|
|
var vol = GetConstVolRaw(td, spotPrice, isEodCalc, underlyingTypeId);
|
|
|
|
if (AddVolRateDic != null && AddVolRateDic.TryGetValue(td.id, out var addVolRate))
|
|
{
|
|
vol += IsAddVolPercent ? addVolRate * vol : addVolRate;
|
|
}
|
|
|
|
return vol;
|
|
}
|
|
|
|
private double GetConstVolRaw(trade td, double spotPrice, bool isEodCalc, int underlyingTypeId)
|
|
{
|
|
if (td.TradeType == "场内期权")
|
|
{
|
|
var vol = new ExOptionSavedVolProvider(_valueDate).GetSavedVol(td.ExchangeOptionCode, _valueDate);
|
|
|
|
if (vol != null)
|
|
{
|
|
return vol.Value;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (OverrideVolsForTrade != null && OverrideVolsForTrade.TryGetValue(td.id, out var overrideVol))
|
|
{
|
|
return overrideVol;
|
|
}
|
|
|
|
if (IsUseTradeVol)
|
|
{
|
|
if (VolType == "交易" || VolType == "持仓")
|
|
{
|
|
return VolatilityHelper.GetTradeVol(td, _valueDate, IsEodSettle);
|
|
}
|
|
|
|
if (VolType == "对冲")
|
|
{
|
|
return TradeHedgeVolService.GetTradeHedgeVol(td, _valueDate);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (VolType == "光证")
|
|
{
|
|
var va = DataCacheProvider.GetVarietyDataSource().GetData(underlyingTypeId)
|
|
?? throw new Exception("未找到光证波动率:" + td.UnderlyingCode);
|
|
|
|
return VarietyVolService.GetVarietyVol(_valueDate, va.id)
|
|
?? throw new Exception("未找到光证波动率:" + td.UnderlyingCode);
|
|
}
|
|
|
|
if (VolType == "开仓")
|
|
{
|
|
return td.TradeOpenVolatility ?? 0;
|
|
}
|
|
|
|
var userGroup = string.Empty;
|
|
|
|
if (ConsUserGroup.HasGroup)
|
|
{
|
|
userGroup = DataCacheProvider.GetAssetUnitDataSource().GetData(td.AssetId)?.UserGroup;
|
|
if (string.IsNullOrEmpty(userGroup))
|
|
{
|
|
throw new Exception($"取波动率时未能获取UserGroup");
|
|
}
|
|
}
|
|
|
|
if (VolType == "BidAskVol")
|
|
{
|
|
//结算时应以平仓的交易方向来选择曲面
|
|
//如果交易是买入,平仓时是卖出,则应该用Ask曲面
|
|
|
|
var tempVolType = td.BuySell == "买入" ? "报价Ask" : "报价Bid";
|
|
|
|
var bidAskVol = VolatilityHelper.GetVol(_valueDate, tempVolType, td.UnderlyingCode, userGroup)
|
|
?? throw new Exception($"未找到'{tempVolType}'波动率:{td.UnderlyingCode}");
|
|
|
|
return GetInterpolatedVol(td, bidAskVol, spotPrice, isEodCalc);
|
|
}
|
|
|
|
//当需要去波动率曲面中查询时,所有公司的VolType都应该是交易;
|
|
//上面这句描述应该是错误,当曲面波动率模式时,应该用配置的结算波动率,todo
|
|
|
|
string tempVolType2 = VolType;
|
|
if (VolType == "对冲")
|
|
{
|
|
tempVolType2 = "交易";
|
|
}
|
|
else if (VolType == "持仓" || string.IsNullOrEmpty(tempVolType2))
|
|
{
|
|
tempVolType2 = IsUseTradeVol ? "交易" : valuedateBLL.SystemDate.EodSettleVolMode.TrimToNull() ?? "交易";
|
|
}
|
|
|
|
var underlyingVols = VolatilityHelper.GetVol(_valueDate, tempVolType2, td.UnderlyingCode, userGroup)
|
|
?? throw new Exception($"未找到'{tempVolType2}'波动率:{td.UnderlyingCode}");
|
|
|
|
return GetInterpolatedVol(td, underlyingVols, spotPrice, isEodCalc);
|
|
}
|
|
|
|
private double GetInterpolatedVol(trade td, IVolatility vols, double spotPrice, bool isEodCalc)
|
|
{
|
|
return VolatilityHelper.GetInterpolatedVol(
|
|
volConstructionType: PS.Config.ErpElement.SkewMapVolConstruction ? VolConstructionType.SkewMap : VolConstructionType.Normal,
|
|
volSurface: vols,
|
|
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: isEodCalc);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|