Files
zszq-trs/YLErpDAL/BLL/Calculation/CalculatorHelper.cs
T
2024-05-09 14:06:26 +08:00

697 lines
31 KiB
C#

using Qdp.ComputeService.Data.CommonModels.MarketInfos;
using Qdp.ComputeService.Data.CommonModels.MarketInfos.CurveDefinitions;
using Qdp.Foundation.Implementations;
using Qdp.Pricing.Base.Enums;
using Qdp.Pricing.Base.Implementations;
using Qdp.Pricing.Library.Base.Curves.Interpolators;
using Qdp.Pricing.Library.Common.Market;
using Qdp.Pricing.Library.Equity.Utilities;
using Qdp.Pricing.Library.Options.Products.Vanilla;
using System.Text.RegularExpressions;
using YLErp.Abstract.DataProviders;
using YLErp.BLL.Calculation.V2;
using YLErp.BLL.Calculation.V2.Parameter;
using YLErp.Enums;
using YLErp.Model;
using YLErp.Models;
using YLErp.Modules;
using YLErp.Modules.CalculationModule;
using YLErp.Modules.ClientModule;
using YLErp.Modules.DataProviderModule;
using YLErp.Modules.VolatilityModule;
using YLErp.QdpModule;
using YLErp.QdpModule.Constants;
namespace YLErp.BLL.Calculation
{
public static class CalculatorHelper
{
static readonly IYcLogger logger;
static CalculatorHelper()
{
logger = LogFactory.GetLogger(nameof(CalculatorHelper));
}
public static InstrumentCurveDefinition CreateConstantRiskFreeCurve(string curveName, double riskFreeRate)
{
return QdpHelper.CreateRiskFreeCurve(curveName, riskFreeRate, valuedateBLL.SystemDate.CurveDayCount);
}
public static YieldCurve BuildIRCurve(string curveName, DateTime curveDate, List<SimpleIRCurvePoint> curveData)
{
if (curveData == null || curveData.Count < 2)
{
return null;
}
var rateMktData = curveData.Where(c => c.Rate > 0).Select(c => new RateMktData(c.Tenor, c.Rate / 100, "Spot", "None", curveName)).ToArray();
if (!rateMktData.Any())
{
return null;
}
var curveDayCount = valuedateBLL.SystemDate.CurveDayCount.TrimToNull() ?? "Act365";
var curveConvention = new CurveConvention(Guid.NewGuid().ToString(),
"CNY",
"ModifiedFollowing",
"chn",
curveDayCount,
"Continuous",
"Linear");
var curveDefinition = new InstrumentCurveDefinition(
curveName,
curveConvention,
rateMktData,
"SpotCurve");
return PrebuiltQdpMarketProxy.BuildYieldCurve(new Date(curveDate), curveDefinition);
}
public static string GetTradeDayCount()
{
return valuedateBLL.TradeDayCount == "" ? "Act365" : valuedateBLL.TradeDayCount;
}
/// <summary>
/// 计算一组交易的风险指标
/// </summary>
/// <param name="volType">波动率类型</param>
/// <param name="overrideVolsForTrade">用户自定义的交易波动率</param>
/// <param name="isUseTradeVol">是否使用交易波动率</param>
public static TradeRiskResult CalculateRisksForTrades(
DateTime valueDate,
IEnumerable<trade> tradeList,
CalcScenarioEnum calcScenario,
IPriceProvider priceProvider,
PricingRequest pricingRequest,
Dictionary<int, double> addVolRateDic = null,
string volType = "交易",
SettlementTypeEnum settlementType = SettlementTypeEnum.ClosePrice,
Dictionary<int, double> overrideVolsForTrade = null,
bool isUseTradeVol = false,
bool preciseTimeMode = true,
bool isAddVolPercent = true,
bool canUseManual = false)
{
var scalc = new SimpleOtcTradeRiskCalc
{
AddVolRateDic = addVolRateDic,
IsAddVolPercent = isAddVolPercent,
IsMarginCalc = false,
IsUseTradeVol = isUseTradeVol,
OverrideVolsForTrade = overrideVolsForTrade,
PreciseTimeMode = preciseTimeMode,
PricingRequest = pricingRequest,
VolType = volType,
SettlementType = settlementType,
CalcScenario = calcScenario,
CanUseManual = canUseManual
};
return scalc.CalculateRisksForTrades(valueDate, tradeList, priceProvider, calcScenario);
}
/// <summary>
/// 计算一组交易的风险指标
/// 暂时不处理多标的交易
/// </summary>
public static TradeRiskResult CalculateRisksForTrades(CalculateRisksForTradesReq req)
{
var scalc = new SimpleOtcTradeRiskCalc
{
AddVolRateDic = req.addVolRateDic,
IsAddVolPercent = req.isAddVolPercent,
IsMarginCalc = req.isMarginCalc,
IsUseTradeVol = req.isUseTradeVol,
OverrideVolsForTrade = req.overrideVolsForTrade,
PreciseTimeMode = req.PreciseTimeMode,
PricingRequest = req.pricingRequest,
VolType = req.volType,
CalcScenario = req.calcScenario,
CanUseManual = req.canUseManual
};
return scalc.CalculateRisksForTrades(req.valueDate, req.tradeList, req.priceProvider,req.calcScenario);
}
/// <summary>
/// 应通过api调用
/// </summary>
/// <param name="maturityDate"></param>
/// <param name="userGroup">波动率使用的用户组</param>
/// <param name="onlyMainContract"></param>
/// <returns></returns>
public static List<AtMoneyOptionQuoteRecord> AtMoneyOptionQuotesV2(string maturityDate, string userGroup, bool onlyMainContract = true)
{
var atMoneyQuotes = new List<AtMoneyOptionQuoteRecord>();
try
{
var valueDate = RealtimeQuoteValueDate();
var valueDateStr = valueDate.ToString("yyyy-MM-dd");
////最小的合约到期日,应晚于的到期日的前一个月的15日
//var oneMonthBefore = DateTime.Parse(maturityDate).AddMonths(1);
//var minMaturityDate = new DateTime(oneMonthBefore.Year, oneMonthBefore.Month, 15);
//根据查询valueDate查询合约列表
var maturityDateValue = DateTime.Parse(maturityDate);
//用户会从OTC系统中设置修改报价合约列表,这里要强制重新从数据库中读取
underlying_main_contractBLL.IsListOld = true;
var underlyings = underlying_main_contractBLL.GetDefaultAtMoneyQuoteList(maturityDateValue, onlyMainContract);
var varieties = VarietyBLL.GetAllvarietyModel();
underlyings.ForEach(u =>
{
u.Variety = varieties.FirstOrDefault(x => x.VarietyCode == u.CommodityCode);
});
//计算平值期权
var qdpMarketId = Guid.NewGuid().ToString();
var marketProxy = QdpMarketManager.Instance.GetPrebuiltMarketProxy(qdpMarketId);
//使用全局的DiscountCurve以提高计算效率
var discountCurveName = Guid.NewGuid().ToString();
var discountCurve = CreateConstantRiskFreeCurve(discountCurveName, valuedateBLL.RiskFreeRate / 100.0);
marketProxy.AddYieldCurve(discountCurveName, valueDateStr, discountCurve);
//获取当天所有波动率
var volitities = new VolatilityQueryService(OptUserInfo.SystemUser)
.GetVolatilities(new BatchVolatilityRequest
{
QuotationDate = valueDate.Date,
TradeVolWithBidAsk = true,
VolType = "交易",
UserGroup = userGroup,
UnderlyingIds = underlyings.Select(n => n.id).ToArray()
}, true);
//.GetVols(valueDate.Date, new List<string>() { "交易", "报价Bid", "报价Ask" }, underlyings.Select(O => O.UnderlyingCode).ToList(), userGroup);
//根据用户设置的bid/ask天数调整规则来分别调整到期日
//TODO: bidMaturityShift和askMaturityShift应该从某数据库表读取
var bidMaturityShift = 0;
var askMaturityShift = 0;
var client_param = ClientPricingParamService.GetPricingParam(valueDate.Date, maturityDateValue.Date);
if (client_param != null)
{
askMaturityShift = client_param.ask_tuning_day ?? 0;
bidMaturityShift = client_param.bid_tuning_day ?? 0;
}
var dayCount = CalculatorHelper.GetTradeDayCount();
var bidMaturityDate = QdpCalendarHelper.ShiftDate(maturityDateValue, dayCount, bidMaturityShift).DateTime;
var askMaturityDate = QdpCalendarHelper.ShiftDate(maturityDateValue, dayCount, askMaturityShift).DateTime;
var buyTrade = new trade()
{
TradeType = "香草期权",
TradeDate = valueDate,
ExerciseDate = bidMaturityDate,
OptionType = "看涨",
ExerciseMode = "European",
BuySell = "Buy",
Notional = 1.0
};
var sellTrade = new trade()
{
TradeType = "香草期权",
TradeDate = valueDate,
ExerciseDate = askMaturityDate,
OptionType = "看涨",
ExerciseMode = "European",
BuySell = "Sell",
Notional = 1.0
};
var parameter = new VanillaOptionParameter()
{
ValueDate = valueDate,
DiscountCurveName = discountCurveName,
PreciseTimeMode = true
};
foreach (var underlying in underlyings)
{
if (!underlying.Price.HasValue)
{
logger.Error($"{underlying.UnderlyingCode}缺少价格,无法报价");
continue;
}
underlying.QuotationDate = valueDate;
var variety = VarietyBLL.GetAllvarietyModel().FirstOrDefault(v => v.id == underlying.UnderlyingTypeId);
if (variety == null)
{
logger.Error($"{underlying.UnderlyingCode}没有品种,无法报价");
continue;
//throw new Exception($"{underlying.UnderlyingCode}没有品种,请去设置!");
}
buyTrade.SpotPrice = underlying.Price;
buyTrade.UnderlyingCode = underlying.UnderlyingCode;
buyTrade.UnderlyingInstrumentType = underlying.UnderlyingInstrumentType;
sellTrade.SpotPrice = underlying.Price;
sellTrade.UnderlyingCode = underlying.UnderlyingCode;
sellTrade.UnderlyingInstrumentType = underlying.UnderlyingInstrumentType;
volatility volsBid = null, volsAsk = null;
if (PS.Config.ErpElement.SkewMapVolConstruction)
{
volsBid = volsAsk = volitities.FirstOrDefault(v => v.VolType == "交易" && v.ContractCode.ToLower() == underlying.UnderlyingCode.ToLower() && v.VolSurfaceMode == "MoneynessVol");
}
else
{
volsBid = volitities.FirstOrDefault(v => v.VolType == "报价Bid" && v.ContractCode.ToLower() == underlying.UnderlyingCode.ToLower() && v.VolSurfaceMode == "MoneynessVol"); //volatilityBLL.GetVolsFromDb("报价Bid", underlying, "MoneynessVol");
volsAsk = volitities.FirstOrDefault(v => v.VolType == "报价Ask" && v.ContractCode.ToLower() == underlying.UnderlyingCode.ToLower() && v.VolSurfaceMode == "MoneynessVol");//volatilityBLL.GetVolsFromDb("报价Ask", underlying, "MoneynessVol");
if (volsBid == null)
{
volsBid = VolatilityBuilder.CreateMoneynessVolBuilder(valueDate, "报价Bid").SetUnderlying(underlying).Build(ConsVolInfos.defVol);
}
if (volsAsk == null)
{
volsAsk = VolatilityBuilder.CreateMoneynessVolBuilder(valueDate, "报价Ask").SetUnderlying(underlying).Build(ConsVolInfos.defVol);
}
}
if (volsBid == null || volsAsk == null)
{
logger.Error($"{underlying.UnderlyingCode}没有波动率,无法报价");
continue;
}
parameter.HasNightMarket = variety.HasNightMarket;
parameter.SpotPrices = new Dictionary<string, double>() { { underlying.UnderlyingCode, underlying.Price ?? 0.0 } };
buyTrade.MaturityDate = underlying.MaturityDate ?? DateTime.MaxValue;
buyTrade.Strike = underlying.Price ?? 0.0;
var bidVol = VolatilityHelper.GetInterpolatedVol(
volConstructionType: PS.Config.ErpElement.SkewMapVolConstruction ? VolConstructionType.SkewMap : VolConstructionType.Normal,
volSurface: volsBid,
valueDate: valueDate,
underlyingCode: underlying.UnderlyingCode,
exerciseDate: bidMaturityDate,
strike: buyTrade.Strike.Value,
isBuy: true,
isCall: true,
spotPrice: underlying.Price ?? 0,
isMoneynessOption: false);
parameter.Volatility = bidVol;
if (PS.Config.Is润和)
{
parameter.OverrideTTM = TradeCalcHelper.CalculateTTMDays(valueDate, buyTrade.ExerciseDate.Value, underlying?.UnderlyingTypeId ?? 0, false);
}
var result = ValueCalculator.CalculateTradeValue(qdpMarketId, buyTrade, underlying, parameter);
sellTrade.MaturityDate = underlying.MaturityDate ?? DateTime.MaxValue;
sellTrade.Strike = underlying.Price ?? 0.0;
var askVol = VolatilityHelper.GetInterpolatedVol(
volConstructionType: PS.Config.ErpElement.SkewMapVolConstruction ? VolConstructionType.SkewMap : VolConstructionType.Normal,
volSurface: volsAsk,
valueDate: valueDate,
underlyingCode: underlying.UnderlyingCode,
exerciseDate: askMaturityDate,
strike: sellTrade.Strike.Value,
isBuy: false,
isCall: true,
spotPrice: underlying.Price ?? 0,
isMoneynessOption: false);
parameter.Volatility = askVol;
if (PS.Config.Is润和)
{
parameter.OverrideTTM = TradeCalcHelper.CalculateTTMDays(valueDate, sellTrade.ExerciseDate.Value, underlying?.UnderlyingTypeId ?? 0, false);
}
var result2 = ValueCalculator.CalculateTradeValue(qdpMarketId, sellTrade, underlying, parameter);
if (result != null && result2 != null)
{
var record = new AtMoneyOptionQuoteRecord()
{
marketCode = underlying.MarketCode,
marketName = underlying.MarketName,
underlyingType = underlying.UnderlyingType,
underlyingTypeShortName = underlying.Variety != null ? underlying.Variety.ShortName : underlying.UnderlyingType,
underlyingCode = underlying.UnderlyingCode,
spotPrice = underlying.Price.Normalize(),
comparedToLastDayPrice = (underlying.PrevClosePrice.HasValue && underlying.Price.HasValue) ?
(underlying.Price.Normalize() > underlying.PrevClosePrice.Normalize()) ? 1 : ((underlying.Price.Normalize() < underlying.PrevClosePrice.Normalize()) ? -1 : 0) :
1,
buyOptionPrice = Math.Abs(result.Pv).Normalize(),
buyOptionVol = result.Vol.Normalize(),
sellOptionPrice = Math.Abs(result2.Pv).Normalize(),
sellOptionVol = result2.Vol.Normalize()
};
atMoneyQuotes.Add(record);
}
}
QdpMarketManager.Instance.RemovePrebuiltMarketProxy(qdpMarketId);
}
catch (Exception ex)
{
logger.Error("AtMoneyOptionQuotes", ex);
}
return atMoneyQuotes;
}
/// <summary>
/// 计算平值报价的默认到期日,仅用于手机实时报价。
/// 报价预览页面的到期日计算请参见函数FlatPriceQuotationBLL.GetAtmOptionQuotePreviewMaturity
/// </summary>
public static string RealtimeQuoteDefaultMaturityDate()
{
var valueDate = RealtimeQuoteValueDate();
// 1个月平值期权的到期日是报价日后一个月减一天
var maturityDate = valueDate.AddMonths(1).AddDays(-1);
return AtmQuoteMaturityDateShift(maturityDate).ToString();
}
/// <summary>
/// 计算平值报价的最大到期日,仅用于光大光子手机实时报价。
/// </summary>
/// <returns></returns>
public static string RealtimeQuoteMaxMaturityDate()
{
var date = DateTime.MaxValue;
var term = valuedateBLL.SystemDate.QuotationCycle;
if (string.IsNullOrWhiteSpace(term)) { term = "3M"; }
var m = Regex.Match(term, @"^(?<num>\d+)(?<unit>[D|W|M|Y|d|w|m|y])$");
if (m.Success)
{
var num = int.Parse(m.Groups["num"].Value);
switch (m.Groups["unit"].Value.ToUpper())
{
case "D":
date = DateTime.Now.AddDays(num).AddDays(-1);
break;
case "W":
date = DateTime.Now.AddDays(num * 7).AddDays(-1);
break;
case "M":
date = DateTime.Now.AddMonths(num).AddDays(-1);
break;
case "Y":
date = DateTime.Now.AddYears(num).AddDays(-1);
break;
}
date = QdpCalendarHelper.GetNonHolidayDefore(date);
}
return date.ToString("yyyy-MM-dd");
}
/// <summary>
/// 根据AtmQuotePreviousBizDayAdjust参数对报价到期日进行调整
/// </summary>
/// <param name="date">原到期日</param>
/// <returns>调整后的到期日</returns>
public static Date AtmQuoteMaturityDateShift(DateTime date)
{
return AtmQuoteMaturityDateShift(new Date(date));
}
/// <summary>
/// 根据AtmQuotePreviousBizDayAdjust参数对报价到期日进行调整
/// </summary>
/// <param name="date">原到期日</param>
/// <returns>调整后的到期日</returns>
private static Date AtmQuoteMaturityDateShift(Date date)
{
var calendar = CalendarImpl.Get("chn");
// 如果那天是非交易日,根据参数往前或往后移
return calendar.IsBizDay(date)
? date
: (PS.Config.ErpElement.AtmQuotePreviousBizDayAdjust ? calendar.PrevBizDay(date) : calendar.NextBizDay(date));
}
/// <summary>
/// 对实时报价,计算报价日
/// </summary>
/// <returns></returns>
public static DateTime RealtimeQuoteValueDate()
{
var calendar = CalendarImpl.Get("chn");
var now = DateTime.Now;
var qdpValueDate = new Date(now);
if (now.Hour >= 21 || QdpCalendarHelper.IsHoliday(now.Date))
{
// 如果已经是夜盘开盘或者当前为节假日,则认为报价日期是下一个交易日
qdpValueDate = calendar.NextBizDay(qdpValueDate);
}
return qdpValueDate.DateTime;
}
/// <summary>
/// 根据合约的到期日和合约代码来获取在报价中所使用的合约到期日
/// 主要是为了处理像SC2001这样的合约
/// 合约代码 | 实时到期日 | 代码月份 | 在报价中使用的到期日
/// RB1912 | 2019-12-15 | 12 | 2019-12-15
/// RB2001 | 2020-01-15 | 1 | 2020-01-15
/// SC1912 | 2019-11-20 | 12 | 2019-11-20
/// SC2001 | 2019-12-20 | 1 | 2020-01-20
/// </summary>
/// <param name="contractMaturityDate">合约实际到期日</param>
/// <param name="contractCode">合约代码</param>
/// <returns></returns>
public static DateTime GetContractMaturityDateForQuote(DateTime contractMaturityDate, string contractCode)
{
int.TryParse(contractCode.Substring(contractCode.Length - 2), out var monthInCode);
if (monthInCode == contractMaturityDate.Month)
{
return contractMaturityDate;
}
else if (monthInCode < 1 || monthInCode > 12)
{
LogFactory.GetLogger("GetContractMaturityDateForQuote").Error($"未能正确解析:{contractMaturityDate} -- {contractCode}");
return contractMaturityDate;
}
else
{
var year = contractMaturityDate.Year + (monthInCode == 1 ? 1 : 0);
var days = DateTime.DaysInMonth(year, monthInCode);
return new DateTime(year, monthInCode, days < contractMaturityDate.Day ? days : contractMaturityDate.Day);
}
}
/// <summary>
/// 为特定的波动率曲面设置自定义波动率
/// </summary>
/// <param name="valueDate"></param>
/// <param name="userId"></param>
/// <param name="volSurfaceNameKey"></param>
/// <param name="vol"></param>
public static VolSurfaceInitParams PrepareOverrideVol(DateTime valueDate, string userId, string volSurfaceNameKey, double vol)
{
//用自定义波动率值构造一个水平的波动率曲面
var vols = new List<SingleVol>
{
new SingleVol
{
Strike = 1,
Expire = "1D",
Vol = vol
},
new SingleVol
{
Strike = 1,
Expire = "1Y",
Vol = vol
}
};
var initParam = new VolSurfaceInitParamsBuilder(userId)
.SetValueDate(valueDate).Build("MoneynessVol", vols, volSurfaceNameKey);
VolSurfaceInitializerSingleton.GetInitializer(true).InitializeMarketProxy(initParam);
return initParam;
}
public static string GenerateVolSurfaceName(this trade trade, string secondUnderlyingCode = null)
{
return string.IsNullOrWhiteSpace(secondUnderlyingCode)
? $"{trade.id}{QdpVolHelper.VOL_SURFACE_SUFFIX}"
: $"{trade.id}_{secondUnderlyingCode}{QdpVolHelper.VOL_SURFACE_SUFFIX}";
}
public static string GenerateVolSurfaceName(this underlying_manager underlying)
{
return $"{underlying.UnderlyingCode}{QdpVolHelper.VOL_SURFACE_SUFFIX}";
}
/// <summary>
/// 根据传入的香草期权组合,生成Payoff图的关键点
/// </summary>
/// <param name="trades">期权交易组合</param>
/// <returns></returns>
public static List<PlotPoint> GetPayoffPlotPoints(IEnumerable<trade> trades)
{
var options = new List<OptionTradeForPayoff>();
foreach (var trade in trades)
{
var option = ConvertTradeForPayoffCalc(trade);
if (option == null)
{
throw new Exception("只支持香草期权组合的Payoff图形展示.");
}
options.Add(option);
}
return PayoffPlotHelper.GetPayoffPoints(options);
}
/// <summary>
/// 将trade对象转换为OptionTradeForPayoff对象,用作展现Payoff图
/// </summary>
/// <param name="trade"></param>
/// <returns></returns>
private static OptionTradeForPayoff ConvertTradeForPayoffCalc(trade trade)
{
if (trade.TradeType != "香草期权")
{
return null;
}
var startDate = new Date(trade.StartDate.Value);
var endDate = new Date(trade.ExerciseDate.Value);
var vanillaOption = new VanillaOption(
startDate,
endDate,
OptionExercise.European,
trade.CallPut == "Put" ? OptionType.Put : OptionType.Call,
trade.IsMoneynessOptionData ? (trade.Strike ?? 0.0) * (trade.SpotPrice ?? 0.0) : (trade.Strike ?? 0.0),
(InstrumentType)Enum.Parse(typeof(InstrumentType), trade.UnderlyingInstrumentType),
CalendarImpl.Get("chn"),
new Bus244(),
CurrencyCode.CNY,
CurrencyCode.CNY,
new Date[] { endDate },
new Date[] { endDate },
trade.Notional,
optionPremium: trade.TradePrice ?? 0.0);
return new OptionTradeForPayoff()
{
Option = vanillaOption,
BuySell = trade.BuySell == "卖出" ? Position.Sell : Position.Buy
};
}
/// <summary>
/// 根据UnderyingCode来判断获取商品现货价格
/// </summary>
/// <param name="valueDate"></param>
/// <param name="maturityDate"></param>
/// <param name="UnderlyingCode"></param>
/// <returns></returns>
public static double GetCommoditySpotPriceByValueDate(DateTime valueDate, DateTime maturityDate, bool isClosePrice, string UnderlyingCode)
{
double CommoditySpotPrice = 0;
using (var db = new YLContext())
{
var BasisCurveList = new List<Underlying_BasiscurveList>();
var underlyingcodelist = new List<string>();
var tenors = new List<string>();
var spreads = new List<double>();
var GetBasisCurve = db.underlying_basis_curve.FirstOrDefault(c => c.UnderlyingCode == UnderlyingCode);
if (GetBasisCurve == null)
{
if (isClosePrice)
{
EodPriceQueryService.TryGetClosePrice(valueDate, UnderlyingCode, out CommoditySpotPrice);
}
else
{
DataCacheProvider.GetUnderlyingDataSource().TryGetPrice(UnderlyingCode, out CommoditySpotPrice);
}
}
else
{
BasisCurveList = JsonHelper.Deserialize<List<Underlying_BasiscurveList>>(GetBasisCurve.InterpolationData);
foreach (var item in BasisCurveList)
{
underlyingcodelist.Add(item.UnderlyingCodes);
tenors.Add(item.term);
spreads.Add(item.BasisCurve);
}
CommoditySpotPrice = GetSpotPriceFromBasisSpread(valueDate, maturityDate, isClosePrice, underlyingcodelist, tenors, spreads);
}
}
return CommoditySpotPrice;
}
/// <summary>
/// 根据期货价格和基差曲线计算现货价格
/// </summary>
/// <param name="valueDate">估值日</param>
/// <param name="maturityDate">交易到期日</param>
/// <param name="futurePrice">期货价格</param>
/// <param name="tenors">基差曲线的关键期限列表</param>
/// <param name="spreads">基差曲线的基差列表</param>
/// <returns></returns>
public static double GetSpotPriceFromBasisSpread(DateTime valueDate, DateTime maturityDate, bool isClosePrice, List<string> underlyingcodelist, List<string> tenors, List<double> spreads)
{
if (tenors == null || spreads == null || tenors.Count != spreads.Count)
{
return 0;
}
var qdpStartDate = new Date(valueDate);
var keyTs = tenors.Select(t => (double)((new Term(t).Next(qdpStartDate)).DateTime - valueDate).Days).ToArray();
var keyPoints = keyTs.Zip(spreads, (x, y) => Tuple.Create(x, y)).ToArray();
var codelist = underlyingcodelist.ToArray();
var underlyingcode = "";
var days = (maturityDate - valueDate).Days;
for (var i = 0; i < keyPoints.Length; i++)
{
if (days <= keyPoints[i].Item1 && (i == 0 || i > 0 && days > keyPoints[i - 1].Item1))
{
underlyingcode = codelist[i];
}
}
double CommoditySpotPrice = 0;
if (isClosePrice)
{
EodPriceQueryService.TryGetClosePrice(valueDate, underlyingcode, out CommoditySpotPrice);
}
else
{
DataCacheProvider.GetUnderlyingDataSource().TryGetPrice(underlyingcode, out CommoditySpotPrice);
}
var interpolator = new LinearInterpolator(keyPoints);
return (CommoditySpotPrice + interpolator.GetValue(days));
}
}
public class SpreadOptionPricingInput
{
public underlying_manager[] Underlyings { get; set; }
public double[] SpotPrices { get; set; }
public double[] Correlations { get; set; }
}
#region T型报价的结果类
public class AtMoneyOptionQuoteRecord
{
public string marketCode { get; set; }
public string marketName { get; set; }
public string underlyingType { get; set; }
public string underlyingTypeShortName { get; set; }
public string underlyingCode { get; set; }
public double spotPrice { get; set; }
public int comparedToLastDayPrice { get; set; }
public double buyOptionPrice { get; set; }
public double sellOptionPrice { get; set; }
public double buyOptionVol { get; set; }
public double sellOptionVol { get; set; }
}
#endregion
[Serializable]
public class SimpleIRCurvePoint
{
public string Tenor { get; set; }
public double Rate { get; set; }
}
}