310 lines
14 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|
|
}
|