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

310 lines
14 KiB
C#

using Qdp.Foundation.Implementations;
using Qdp.Pricing.Base.Implementations;
using YLErp.BLL;
using YLErp.BLL.Calculation;
using YLErp.BLL.Calculation.V2;
using YLErp.BLL.Calculation.V2.Parameter;
using YLErp.Modules.CalculationModule;
using YLErp.Modules.ClientModule;
using YLErp.Modules.VolatilityModule;
using YLErp.QdpModule;
namespace YLErp.Modules.TQuoteModule
{
/// <summary>
/// 自定义手机报价
/// </summary>
public class CustomizedQuoteService
{
public static HandleResult<List<DateTime>> GetHolidaysBetween(DateTime startDate, DateTime endDate)
{
var result = new List<DateTime>();
var days = (endDate - startDate).Days;
var calendar = CalendarImpl.Get("chn");
for (var i = 0; i < days; i++)
{
var date = startDate.AddDays(i);
if (date >= endDate) { break; }
if (calendar.IsHoliday(new Date(date)))
{
result.Add(date);
}
}
return new HandleResult<List<DateTime>>(result);
}
/// <summary>
///
/// </summary>
public static CustomizedQuoteResult CustomizedQuoteV2(CustomizedQuoteRequest request, string userGroup = "")
{
if (request.MaturityDate < request.ValueDate)
{
return new CustomizedQuoteResult() { Info = "到期日小于估值日", StatusCode = -1 };
}
var qdpMarketId = Guid.NewGuid().ToString();
var marketProxy = QdpMarketManager.Instance.GetPrebuiltMarketProxy(qdpMarketId);
var valueDate = request.ValueDate;
var maturityDate = request.MaturityDate;
volatility bidVol = null, askVol = null;
underlying_manager underlying = null;
Variety variety = null;
using (var db = new YLContext())
{
underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(request.UnderlyingCode);
if (underlying != null)
{
variety = db.variety.FirstOrDefault(x => x.VarietyCode != null && x.id == underlying.UnderlyingTypeId);
if (request.BidVol <= 0 && request.AskVol <= 0)
{
underlying.QuotationDate = request.ValueDate;
if (PS.Config.ErpElement.SkewMapVolConstruction)
{
bidVol = askVol = VolatilityHelper.GetVol(DateTime.Today, "交易", underlying.UnderlyingCode, userGroup ?? "");
}
else
{
bidVol = VolatilityHelper.GetVol(DateTime.Today, "报价Bid", underlying.UnderlyingCode, userGroup ?? "");
askVol = VolatilityHelper.GetVol(DateTime.Today, "报价Ask", underlying.UnderlyingCode, userGroup ?? "");
}
}
}
}
//如果用户未指定标的,构造一个虚拟标的
if (string.IsNullOrWhiteSpace(request.UnderlyingCode))
{
underlying = new underlying_manager()
{
UnderlyingCode = "dummy_code",
UnderlyingInstrumentType = "CommodityFutures",
QuotationDate = request.ValueDate,
Price = request.SpotPrice,
PrevClosePrice = request.SpotPrice,
MaturityDate = request.MaturityDate
};
}
//根据用户设置的bid/ask天数调整规则来分别调整到期日
//TODO: bidMaturityShift和askMaturityShift应该从某数据库表读取
var bidMaturityShift = 0;
var askMaturityShift = 0;
var otherInfo = "";
var client_param = ClientPricingParamService.GetPricingParam(request.ValueDate.Date, request.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(maturityDate, dayCount, bidMaturityShift).DateTime;
var askMaturityDate = QdpCalendarHelper.ShiftDate(maturityDate, dayCount, askMaturityShift).DateTime;
if (underlying == null)
{
return null;
}
// 如果未传入最新价格,则使用系统里的最新价格; 如果未传入无风险利率,则使用系统里的无风险利率
var spotPrice = (request.SpotPrice <= 0) ? (underlying.Price ?? 0) : request.SpotPrice;
var riskFreeRate = (request.RiskFreeRate <= 0) ? (valuedateBLL.RiskFreeRate / 100.0) : request.RiskFreeRate;
var strike = (request.Strike <= 0) ? spotPrice : request.Strike;
//使用全局的DiscountCurve以提高计算效率
var discountCurveName = Guid.NewGuid().ToString();
var discountCurve = CalculatorHelper.CreateConstantRiskFreeCurve(discountCurveName, riskFreeRate);
marketProxy.AddYieldCurve(discountCurveName, valueDate, discountCurve);
var trade = new trade()
{
TradeType = "香草期权",
TradeDate = request.ValueDate,
ExerciseMode = request.Exercise,
Strike = strike,
Notional = request.Notional
};
var parameter = new VanillaOptionParameter()
{
ValueDate = request.ValueDate,
DiscountCurveName = discountCurveName,
SpotPrices = new Dictionary<string, double>() { { underlying.UnderlyingCode, spotPrice } },
HasNightMarket = variety.HasNightMarket,
PreciseTimeMode = request.commodityFuturesPreciseTimeMode
};
trade.OptionType = "看涨";
trade.MaturityDate = bidMaturityDate;
trade.ExerciseDate = bidMaturityDate;
parameter.Volatility =
bidVol == null ?
request.BidVol :
VolatilityHelper.GetInterpolatedVol(
volConstructionType: PS.Config.ErpElement.SkewMapVolConstruction ? VolConstructionType.SkewMap : VolConstructionType.Normal,
volSurface: bidVol,
valueDate: valueDate,
underlyingCode: underlying.UnderlyingCode,
exerciseDate: bidMaturityDate,
strike: strike,
isBuy: true,
isCall: true,
spotPrice: spotPrice,
isMoneynessOption: false);
var callBidResult = ValueCalculator.CalculateTradeValue(qdpMarketId, trade, underlying, parameter);
trade.OptionType = "看跌";
parameter.Volatility =
bidVol == null ?
request.BidVol :
VolatilityHelper.GetInterpolatedVol(
volConstructionType: PS.Config.ErpElement.SkewMapVolConstruction ? VolConstructionType.SkewMap : VolConstructionType.Normal,
volSurface: bidVol,
valueDate: valueDate,
underlyingCode: underlying.UnderlyingCode,
exerciseDate: bidMaturityDate,
strike: strike,
isBuy: true,
isCall: false,
spotPrice: spotPrice,
isMoneynessOption: false);
var putBidResult = ValueCalculator.CalculateTradeValue(qdpMarketId, trade, underlying, parameter);
trade.OptionType = "看涨";
trade.MaturityDate = askMaturityDate;
trade.ExerciseDate = askMaturityDate;
parameter.Volatility =
askVol == null ?
request.AskVol :
VolatilityHelper.GetInterpolatedVol(
volConstructionType: PS.Config.ErpElement.SkewMapVolConstruction ? VolConstructionType.SkewMap : VolConstructionType.Normal,
volSurface: askVol,
valueDate: valueDate,
underlyingCode: underlying.UnderlyingCode,
exerciseDate: askMaturityDate,
strike: strike,
isBuy: false,
isCall: true,
spotPrice: spotPrice,
isMoneynessOption: false);
var callAskResult = ValueCalculator.CalculateTradeValue(qdpMarketId, trade, underlying, parameter);
trade.OptionType = "看跌";
parameter.Volatility =
askVol == null ?
request.AskVol :
VolatilityHelper.GetInterpolatedVol(
volConstructionType: PS.Config.ErpElement.SkewMapVolConstruction ? VolConstructionType.SkewMap : VolConstructionType.Normal,
volSurface: askVol,
valueDate: valueDate,
underlyingCode: underlying.UnderlyingCode,
exerciseDate: askMaturityDate,
strike: strike,
isBuy: false,
isCall: false,
spotPrice: spotPrice,
isMoneynessOption: false);
var putAskResult = ValueCalculator.CalculateTradeValue(qdpMarketId, trade, underlying, parameter);
var valueResult = new CustomizedQuoteResult()
{
CallAskPrice = callAskResult.Pv / request.Notional,
CallBidPrice = callBidResult.Pv / request.Notional,
Strike = strike,
PutAskPrice = putAskResult.Pv / request.Notional,
PutBidPrice = putBidResult.Pv / request.Notional,
BidVol = callBidResult.Vol,
AskVol = callAskResult.Vol,
RiskFreeRate = riskFreeRate
};
valueResult.LatestPrice = underlying.Price.Value;
if (underlying.PrevClosePrice.HasValue && underlying.PrevClosePrice.Value > 0.0)
{
valueResult.Change = underlying.Price.Value - underlying.PrevClosePrice.Value;
valueResult.ChangePercent = valueResult.Change / underlying.PrevClosePrice.Value;
}
else
{
valueResult.Change = 0.0;
valueResult.ChangePercent = 0.0;
}
QdpMarketManager.Instance.RemovePrebuiltMarketProxy(qdpMarketId);
return valueResult;
}
/// <summary>
///
/// </summary>
public static double Pricing(OptionStrategyCodeParts part, string userGroup)
{
var valueDate = DateTime.Today;
var maturityDate = QdpHelper.getMaturityDate(valueDate, part.Maturity);
var exercisType = "European";
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(part.UnderlyingCode);
if (underlying == null)
{
throw new Exception("未找到标的" + part.UnderlyingCode);
}
var variety = DataCacheProvider.GetVarietyDataSource().GetData(underlying.UnderlyingTypeId);
if (variety == null)
{
throw new Exception("未找到标的品种" + underlying.UnderlyingType);
}
var vol = VolatilityHelper.GetVol(DateTime.Today, part.IsSell ? "报价Bid" : "报价Ask", underlying.UnderlyingCode, userGroup);
if (vol == null)
{
throw new Exception("未找到波动率曲面");
}
var riskFreeRate = valuedateBLL.RiskFreeRate / 100.0;
using (var mp = new MarketProxy(valueDate, riskFreeRate))
{
var volsurfaceName = mp.SaveVolSurface(vol);
var tdParam = new VanillaOptionTradeParam
{
underlyingTickers = new[] { part.UnderlyingCode },
underlyingInstrumentType = underlying.UnderlyingInstrumentType,
strike = part.Strike,
startDate = valueDate,
endDate = maturityDate,
optionType = QdpConverter.ConvertOptionType(part.OptionType),
exerciseType = exercisType,
initialSpotPrice = underlying.Price ?? 0,
notional = part.Notional,
volSurfaceNames = new[] { volsurfaceName },
riskFreeRate = riskFreeRate,
buysell = part.IsSell ? "Sell" : "Buy",
exerciseDate = maturityDate,
hasNightMarket = variety.HasNightMarket,
preciseTimeMode = true,
participationRate = 1.0,
principalRate = 0.0,
isAnnualized = false,
annualizedFactor = 1.0
};
var result = TradeRiskCalcUtil.GetVanillaOptionValue(mp, new OptionCalcParam<VanillaOptionTradeParam>(tdParam)
{
spotPrices = new[] { underlying.Price ?? 0 },
pricingRequest = PricingRequest.Pv
});
return result.Pv;
}
}
}
}