从山证v2.3.0拷贝
This commit is contained in:
+21
@@ -0,0 +1,21 @@
|
||||
namespace YLErp.Modules.ApiModule.PricingModule.CustomizedAsianPricing
|
||||
{
|
||||
public class CustomizedAsianPricingModel : OptionPricingModelV2
|
||||
{
|
||||
public string ExoticAsianType { get; set; }
|
||||
|
||||
public double LockPrice { get; set; }
|
||||
|
||||
public DateTime LockObsStartDate { get; set; }
|
||||
public DateTime LockObsEndDate { get; set; }
|
||||
|
||||
public bool IsLocked { get; set; }
|
||||
public DateTime LockStartDate { get; set; }
|
||||
|
||||
public double? AvgPriceRate { get; set; }
|
||||
|
||||
public double? FinalPriceSpread { get; set; }
|
||||
|
||||
public string Fixings { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
using System.Reflection;
|
||||
|
||||
namespace YLErp.Modules.ApiModule.PricingModule
|
||||
{
|
||||
/// <summary>
|
||||
/// OptionPricingApiHelper
|
||||
/// </summary>
|
||||
public static class OptionPricingApiHelper
|
||||
{
|
||||
static readonly Dictionary<string, (Func<string, object> valueConv, Action<OptionPricingModelV2, object> valueSet)> _dic;
|
||||
|
||||
static OptionPricingApiHelper()
|
||||
{
|
||||
_dic = new Dictionary<string, (Func<string, object> valueConv, Action<OptionPricingModelV2, object> valueSet)>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据模型属性名设置属性值
|
||||
/// </summary>
|
||||
public static void SetValueByPropName(OptionPricingModelV2 model, string propName, string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_dic.TryGetValue(propName, out var tuple))
|
||||
{
|
||||
Func<string, object> valueConv = null;
|
||||
|
||||
var prop = typeof(OptionPricingModelV2).GetProperty(propName,
|
||||
BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.SetProperty);
|
||||
|
||||
if (prop != null)
|
||||
{
|
||||
if (prop.PropertyType == typeof(string))
|
||||
{
|
||||
valueConv = s => s;
|
||||
}
|
||||
else if (prop.PropertyType == typeof(int) || prop.PropertyType.IsEnum || prop.PropertyType == typeof(int?))
|
||||
{
|
||||
valueConv = s => int.TryParse(s, out var n) ? (object)n : throw new ServiceException("参数值不符合要求,参数名:" + propName);
|
||||
}
|
||||
else if (prop.PropertyType == typeof(double) || prop.PropertyType == typeof(double?))
|
||||
{
|
||||
valueConv = s => double.TryParse(s, out var d) ? (object)d : throw new ServiceException("参数值不符合要求,参数名:" + propName);
|
||||
}
|
||||
else if (prop.PropertyType == typeof(bool) || prop.PropertyType == typeof(bool?))
|
||||
{
|
||||
valueConv = s => s == "true";
|
||||
}
|
||||
else if (prop.PropertyType == typeof(DateTime) || prop.PropertyType == typeof(DateTime?))
|
||||
{
|
||||
valueConv = s => DateTime.TryParse(s, out var d) ? (object)d : throw new ServiceException("参数值不符合要求,参数名:" + propName);
|
||||
}
|
||||
else
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine("!!!ExtendSetter:没有处理值转换:" + propName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine("!!!ExtendSetter:没有找到对应属性信息:" + propName);
|
||||
}
|
||||
|
||||
_dic[propName] = tuple = (valueConv, (m, o) => prop?.SetValue(m, o));
|
||||
}
|
||||
|
||||
if (tuple.valueConv != null)
|
||||
{
|
||||
tuple.valueSet(model, tuple.valueConv(value));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查请求数据是否正确
|
||||
/// </summary>
|
||||
public static void CheckPricingRequestData(OptionPricingModelV2 req)
|
||||
{
|
||||
if (req == null)
|
||||
{
|
||||
throw new ServiceException("参数为null");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(req.UnderlyingCode))
|
||||
{
|
||||
throw new ServiceException("缺少标的代码");
|
||||
}
|
||||
|
||||
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(req.UnderlyingCode);
|
||||
if (underlying == null)
|
||||
{
|
||||
throw new ServiceException("没有找到标的信息:" + req.UnderlyingCode);
|
||||
}
|
||||
|
||||
if (!req.MaturityDate.HasValue || req.MaturityDate.Value == default)
|
||||
{
|
||||
req.MaturityDate = req.ExerciseDate;
|
||||
}
|
||||
|
||||
req.UnderlyingId = underlying.id;
|
||||
req.UnderlyingAssetClass = underlying.UnderlyingType;
|
||||
req.UnderlyingAssetName = underlying.UnderlyingName;
|
||||
req.UnderlyingInstrumentType = underlying.UnderlyingInstrumentType;
|
||||
|
||||
if (req.Notional <= 0)
|
||||
{
|
||||
throw new ServiceException("持仓份额必须大于0");
|
||||
}
|
||||
|
||||
if (req.TradeType != "香草期权" && req.ExtendFields != null && req.ExtendFields.Count > 0)
|
||||
{
|
||||
foreach (var kv in req.ExtendFields)
|
||||
{
|
||||
OptionPricingApiHelper.SetValueByPropName(req, kv.Key, kv.Value);
|
||||
}
|
||||
|
||||
var isAutoCall = req.TradeType == "雪球期权" || req.TradeType == "凤凰期权";
|
||||
|
||||
if (isAutoCall)
|
||||
{
|
||||
req.Strike = req.SpreadStrike1;
|
||||
}
|
||||
|
||||
if (!req.IsAnnualized)
|
||||
{
|
||||
req.AnnualizeFactor = 1;
|
||||
|
||||
if (isAutoCall)
|
||||
{
|
||||
req.IsAnnualized2 = false;
|
||||
req.AnnualizeFactor2 = 1;
|
||||
}
|
||||
}
|
||||
else if (isAutoCall)
|
||||
{
|
||||
req.IsAnnualized2 = true;
|
||||
req.AnnualizeFactor2 = req.AnnualizeFactor;
|
||||
|
||||
req.IsAnnualized = false;
|
||||
req.AnnualizeFactor = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//检查并尽可能修复数据
|
||||
req.CheckAndRepairValues();
|
||||
|
||||
if (!req.VolValue.HasValue)
|
||||
{
|
||||
req.VolValue = 0.3;
|
||||
}
|
||||
|
||||
req.Vol = req.VolValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
using Qdp.Foundation.Implementations;
|
||||
using Qdp.Pricing.Base.Enums;
|
||||
using Qdp.Pricing.Base.Implementations;
|
||||
using Qdp.Pricing.Base.Utilities;
|
||||
using Qdp.Pricing.Library.Base.Curves.Interfaces;
|
||||
using Qdp.Pricing.Library.Common.Interfaces;
|
||||
using Qdp.Pricing.Library.Common.Market;
|
||||
using Qdp.Pricing.Library.Options.MonteCarlo;
|
||||
using Qdp.Pricing.Library.Options.Products.Asian;
|
||||
using YLErp.BLL.Calculation;
|
||||
using YLErp.Enums;
|
||||
using YLErp.Modules.ApiModule.PricingModule.CustomizedAsianPricing;
|
||||
using YLErp.Modules.CalculationModule;
|
||||
using YLErp.Modules.PricingModule;
|
||||
using YLErp.QdpModule;
|
||||
|
||||
namespace YLErp.Modules.ApiModule.PricingModule
|
||||
{
|
||||
/// <summary>
|
||||
/// 期权定价api服务
|
||||
/// </summary>
|
||||
public partial class OptionPricingApiService
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取期权定价结果
|
||||
/// </summary>
|
||||
public CalcOptionPriceResult GetOptionPrice(OptionPricingModelV2 req)
|
||||
{
|
||||
if (req.ExtendFields != null && req.ExtendFields.Count > 0)
|
||||
{
|
||||
var dic = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var kv in req.ExtendFields)
|
||||
{
|
||||
dic[kv.Key] = req.ExtendFields[kv.Key];
|
||||
}
|
||||
req.ExtendFields = dic;
|
||||
}
|
||||
|
||||
OptionPricingApiHelper.CheckPricingRequestData(req);
|
||||
|
||||
string fixings = null;
|
||||
|
||||
if (req.ExtendFields != null)
|
||||
{
|
||||
req.ExtendFields.TryGetValue("fixings", out fixings);
|
||||
}
|
||||
|
||||
var result = new PriceCalcService(OptUserInfo.SystemUser).CalcOptionPrice(req, req.IsCalcMargin
|
||||
, CalcScenarioEnum.Pricing, req.IsCalcGreeks, fixings: fixings);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public CalcOptionPriceResult GetCustomizedAsianOptionPrice(CustomizedAsianPricingModel req)
|
||||
{
|
||||
var startDate = new Date(req.StartDate.Value);
|
||||
var maturityDate = new Date(req.ExerciseDate.Value);
|
||||
var callPut = req.CallPut.ToUpper() == "PUT" ? OptionType.Put : OptionType.Call;
|
||||
var lockObsStartDate = new Date(req.LockObsStartDate);
|
||||
var lockObsEndDate = new Date(req.LockObsEndDate);
|
||||
var lockStartDate = new Date(req.LockStartDate);
|
||||
|
||||
var calendar = CalendarImpl.Get("chn");
|
||||
var dayCount = CalculatorHelper.GetTradeDayCount().ToDayCountImpl();
|
||||
|
||||
var option = new EnhancedAsianOptionWithLockAndBarrier(
|
||||
startDate,
|
||||
maturityDate,
|
||||
OptionExercise.European,
|
||||
callPut,
|
||||
strike: req.Strike.Value,
|
||||
lockPrice: req.LockPrice,
|
||||
barrierPrice: req.BarrierPrice.HasValue ? req.BarrierPrice.Value : double.NaN, // 13659.26,
|
||||
isLocked: req.IsLocked,
|
||||
lockObsStartDate: lockObsStartDate,
|
||||
lockObsEndDate: lockObsEndDate,
|
||||
lockStartDate: lockStartDate,
|
||||
underlyingInstrumentType: InstrumentType.CommodityFutures,
|
||||
calendar: calendar,
|
||||
dayCount: dayCount,
|
||||
payoffCcy: CurrencyCode.CNY,
|
||||
settlementCcy: CurrencyCode.CNY,
|
||||
exerciseDates: new Date[] { maturityDate },
|
||||
observationDates: calendar.BizDaysBetweenDatesExcluStartDay(lockObsEndDate, maturityDate).ToArray(),
|
||||
fixings: QdpHelper.ParseFixingsFromString(req.Fixings),
|
||||
avgPriceRate: req.AvgPriceRate.HasValue ? req.AvgPriceRate.Value : 1.0,
|
||||
finalPriceSpread: req.FinalPriceSpread.HasValue ? req.FinalPriceSpread.Value : 0.0,
|
||||
notional: req.Notional);
|
||||
|
||||
|
||||
var mcEngine = new GbmMonteCarloEngine(5000, 1e-3, 1e-6, 1, true);
|
||||
|
||||
var vol = req.Vol.Value;
|
||||
var spot = req.SpotPrice.Value;
|
||||
var r = req.RiskFreeRate.Value;
|
||||
var q = req.DividendRate.Value;
|
||||
|
||||
var valueDate = new Date(req.ValueDate.Value);
|
||||
|
||||
//var market = TestHelper.CreateMarket(valueDate, vol: vol, spot: spot, riskFreeRate: r, dividendRate: q);
|
||||
|
||||
var marketProxy = new MarketProxy(valueDate, r);
|
||||
marketProxy.SetVolSurface("volsurf", vol);
|
||||
var volsurf = marketProxy.QdpMarket.VolSurfaces["volsurf"];
|
||||
|
||||
var dividendCurveName = Guid.NewGuid().ToString();
|
||||
var dividendCurve = CalculatorHelper.CreateConstantRiskFreeCurve(dividendCurveName, q);
|
||||
marketProxy.SetYieldCurve(dividendCurveName, dividendCurve);
|
||||
|
||||
var market = new MarketCondition(
|
||||
x => x.ValuationDate.Value = marketProxy.QdpMarket.ReferenceDate,
|
||||
x => x.DiscountCurve.Value = marketProxy.QdpMarket.YieldCurves[MarketProxy.RiskFreeDiscountCurve],
|
||||
x => x.DividendCurves.Value = new Dictionary<string, IYieldCurve> { { "", marketProxy.QdpMarket.YieldCurves[dividendCurveName] } },
|
||||
x => x.VolSurfaces.Value = new Dictionary<string, IVolSurface> { { "", volsurf } },
|
||||
x => x.SpotPrices.Value = new Dictionary<string, double> { { "", spot } }
|
||||
);
|
||||
|
||||
var result = mcEngine.Calculate(option, market, QdpPricingRequest.BASIC_GREEKS);
|
||||
|
||||
return new CalcOptionPriceResult()
|
||||
{
|
||||
CalcId = req.CalcId,
|
||||
calcResult = new TradeValueResult(result)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
namespace YLErp.Modules.ApiModule.PricingModule
|
||||
{
|
||||
public class OptionPricingModelV1
|
||||
{
|
||||
public IEnumerable<OtcOptionTradeFull> Trades { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否计算预付金
|
||||
/// </summary>
|
||||
public bool IsCalcMargin { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否计算greeks
|
||||
/// </summary>
|
||||
public bool IsCalcGreeks { get; set; }
|
||||
}
|
||||
|
||||
public class OptionPricingModelV2 : OtcOptionTradeFull
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否计算预付金
|
||||
/// </summary>
|
||||
public bool IsCalcMargin { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否计算greeks
|
||||
/// </summary>
|
||||
public bool IsCalcGreeks { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 交易方向: Buy | Sell
|
||||
/// </summary>
|
||||
public string TradeSide { get => BuySell; set => BuySell = value; }
|
||||
|
||||
/// <summary>
|
||||
/// 波动率值
|
||||
/// </summary>
|
||||
public double? VolValue { get => TradeOpenVolatility; set => TradeOpenVolatility = value; }
|
||||
|
||||
public Dictionary<string, string> ExtendFields { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 无风险利率别名
|
||||
/// </summary>
|
||||
public double? RiskFreeRate { get => NoRiskRate; set => NoRiskRate = value; }
|
||||
|
||||
/// <summary>
|
||||
/// 权利金
|
||||
/// </summary>
|
||||
public double Premium { get => TradeSinglePrice ?? 0; set => TradeSinglePrice = value; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user