Files
zszq-trs/YLErpDAL/Modules/TQuoteModule/QdpCalculationApiHelpere.cs
T
2024-05-09 14:06:26 +08:00

198 lines
10 KiB
C#

using YLErp.BLL;
using YLErp.BLL.Calculation;
using YLErp.BLL.Calculation.V2;
using YLErp.BLL.Calculation.V2.Parameter;
using YLErp.Modules.ClientModule;
using YLErp.Modules.VolatilityModule;
using YLErp.QdpModule;
namespace YLErp.Modules.TQuoteModule
{
/// <summary>
/// WebApi-QdpCalculationController-helper
/// </summary>
public static class QdpCalculationApiHelpere
{
/// <summary>
/// 计算某一个期权的买卖价格
/// </summary>
public static ClientOptionQuoteResult ValueOptionV2(OptionValueRequest req, string userGroup = null)
{
try
{
var valueDate = req.ValueDate ?? CalculatorHelper.RealtimeQuoteValueDate();
if (req.MaturityDate < valueDate)
{
return new ClientOptionQuoteResult() { Info = "到期日小于估值日", StatusCode = -1 };
}
var qdpMarketId = Guid.NewGuid().ToString();
var marketProxy = QdpMarketManager.Instance.GetPrebuiltMarketProxy(qdpMarketId);
volatility bidVol = null, askVol = null;
underlying_manager underlying = null;
Variety variety = null;
using (var db = DbContextFactory.GetYLDbContext())
{
underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(req.UnderlyingCode);
if (underlying != null)
{
underlying.QuotationDate = valueDate;
if (PS.Config.ErpElement.SkewMapVolConstruction)
{
bidVol = askVol = VolatilityHelper.GetVol(valueDate.Date, "交易", underlying.UnderlyingCode, userGroup ?? "");
}
else
{
bidVol = VolatilityHelper.GetVol(valueDate.Date, "报价Bid", underlying.UnderlyingCode, userGroup ?? "");
askVol = VolatilityHelper.GetVol(valueDate.Date, "报价Ask", underlying.UnderlyingCode, userGroup ?? "");
}
variety = db.variety.FirstOrDefault(x => x.VarietyCode != null && x.id == underlying.UnderlyingTypeId);
}
}
if (underlying != null && bidVol != null && askVol != null)
{
//var maturityDate = req.MaturityDate.ToString("yyyy-MM-dd");
//根据用户设置的bid/ask天数调整规则来分别调整到期日
//TODO: bidMaturityShift和askMaturityShift应该从某数据库表读取
var bidMaturityShift = 0;
var askMaturityShift = 0;
var otherInfo = "";
var client_param = ClientPricingParamService.GetPricingParam(valueDate.Date, req.MaturityDate.Date);
if (client_param != null)
{
askMaturityShift = client_param.ask_tuning_day ?? 0;
bidMaturityShift = client_param.bid_tuning_day ?? 0;
otherInfo = client_param.ToJson();//$"ask到期日偏离{askMaturityShift}天,bid到期日偏离{bidMaturityShift}天";
}
var dayCount = CalculatorHelper.GetTradeDayCount();
var bidMaturityDate = QdpCalendarHelper.ShiftDate(req.MaturityDate, dayCount, bidMaturityShift).DateTime;
var askMaturityDate = QdpCalendarHelper.ShiftDate(req.MaturityDate, dayCount, askMaturityShift).DateTime;
//使用全局的DiscountCurve以提高计算效率
var discountCurveName = Guid.NewGuid().ToString();
var discountCurve = CalculatorHelper.CreateConstantRiskFreeCurve(discountCurveName, valuedateBLL.RiskFreeRate / 100.0);
marketProxy.AddYieldCurve(discountCurveName, valueDate.ToString("yyyy-MM-dd"), discountCurve);
var volSurfaceName = underlying.GenerateVolSurfaceName();
var parameter = new VanillaOptionParameter()
{
ValueDate = valueDate,
SpotPrices = new Dictionary<string, double>() { { req.UnderlyingCode, req.SpotPrice } },
DiscountCurveName = discountCurveName,
HasNightMarket = variety.HasNightMarket,
PreciseTimeMode = req.commodityFuturesPreciseTimeMode
};
var trade = new trade()
{
TradeType = "香草期权",
TradeDate = valueDate,
MaturityDate = bidMaturityDate,
ExerciseDate = bidMaturityDate,
OptionType = req.OptionType,
ExerciseMode = req.Exercise,
BuySell = "Buy",
Strike = req.Strike,
Notional = req.Notional,
UnderlyingCode = req.UnderlyingCode
};
parameter.Volatility = VolatilityHelper.GetInterpolatedVol(
volConstructionType: PS.Config.ErpElement.SkewMapVolConstruction ? VolConstructionType.SkewMap : VolConstructionType.Normal,
volSurface: bidVol,
valueDate: valueDate,
underlyingCode: req.UnderlyingCode,
exerciseDate: bidMaturityDate,
strike: req.Strike,
isBuy: true,
isCall: req.OptionType == "Call",
spotPrice: req.SpotPrice,
isMoneynessOption: false);
var bidResult = ValueCalculator.CalculateTradeValue(qdpMarketId, trade, underlying, parameter);
trade.MaturityDate = askMaturityDate;
trade.ExerciseDate = askMaturityDate;
parameter.Volatility = VolatilityHelper.GetInterpolatedVol(
volConstructionType: PS.Config.ErpElement.SkewMapVolConstruction ? VolConstructionType.SkewMap : VolConstructionType.Normal,
volSurface: askVol,
valueDate: valueDate,
underlyingCode: req.UnderlyingCode,
exerciseDate: askMaturityDate,
strike: req.Strike,
isBuy: false,
isCall: req.OptionType == "Call",
spotPrice: req.SpotPrice,
isMoneynessOption: false);
var askResult = ValueCalculator.CalculateTradeValue(qdpMarketId, trade, underlying, parameter);
var valueResult = new ClientOptionQuoteResult()
{
BuyQuote = Math.Abs(bidResult.Pv) / req.Notional,
BuyVol = bidResult.Vol,
BuyPv = Math.Abs(bidResult.Pv),
BuyPercentageQuote = Math.Abs(bidResult.Pv) / req.Notional / req.SpotPrice,
SellQuote = Math.Abs(askResult.Pv) / req.Notional,
SellVol = askResult.Vol,
SellPv = Math.Abs(askResult.Pv),
SellPercentageQuote = Math.Abs(askResult.Pv) / req.Notional / req.SpotPrice,
SellMargin =
CommodityFutureOptionMargin(
Math.Abs(askResult.Pv),
req.Notional,
req.Strike,
req.SpotPrice,
variety.Margin ?? 0.0, //避免品种预付金未设置而导致计算出错
req.OptionType)
};
QdpMarketManager.Instance.RemovePrebuiltMarketProxy(qdpMarketId);
return valueResult;
}
else
{
LogFactory.GetLogger().Error($"ValueOptionV2: 未找到标的{req.UnderlyingCode}或其波动率为空!");
return null;
}
}
catch (Exception ex)
{
LogFactory.GetLogger().Error("ValueOptionV2", ex);
return null;
}
}
/// <summary>
/// 期货期权卖方交易预付金的收取标准为下列两者中较大者:
///(1)权利金(期权合约结算价×标的期货合约交易单位)+标的期货合约交易预付金-期权合约虚值额的一半
///(2)权利金(期权合约结算价×标的期货合约交易单位)+标的期货合约交易预付金的一半
///看涨虚值额 = max(期权合约执行价格 - 标的期货合约当日结算价,0)*合约乘数;
///看跌虚值额=max(标的期货合约当日结算价 - 期权合约执行价格,0)*合约乘数。
///豆粕期权的合约乘数是10、标的期货合约的交易单位是10.
///所以以上2个公式可以结合成为一个公式:
///预付金=权利金+MAX(期货预付金-1/2虚值额,1/2期货预付金)
/// </summary>
/// <param name="premium">权利金</param>
/// <param name="amount">交易量</param>
/// <param name="strike">行权价</param>
/// <param name="spotPrice">现价</param>
/// <param name="futureMarginRatio">期货预付金率</param>
/// <param name="optionType">看涨看跌</param>
/// <returns></returns>
private static double CommodityFutureOptionMargin(double premium, double amount, double strike, double spotPrice, double futureMarginRatio, string optionType = "Call")
{
var futureMargin = futureMarginRatio * amount * spotPrice;
var outTheMoney =
(optionType.ToUpper() == "PUT")
? Math.Max(spotPrice - strike, 0) * amount //看跌虚值额
: Math.Max(strike - spotPrice, 0) * amount; //看涨虚值额
return premium + Math.Max(futureMargin - outTheMoney * 0.5, futureMargin * 0.5);
}
}
}