从山证v2.3.0拷贝
This commit is contained in:
@@ -0,0 +1,401 @@
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Qdp.ComputeService.Data.CommonModels.ValuationParams.Equity;
|
||||
using Qdp.ComputeServiceV2.Data.CommonModels.TradeInfos.Options;
|
||||
using Qdp.Foundation.Implementations;
|
||||
using Qdp.Pricing.Base.Enums;
|
||||
using Qdp.Pricing.Base.Implementations;
|
||||
using Qdp.Pricing.Base.Utilities;
|
||||
using Qdp.Pricing.Library.Options.Products.SyntheticSpread;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using YLErp.BLL.Calculation;
|
||||
using YLErp.DBModels;
|
||||
using YLErp.DBModels.Helpers;
|
||||
using YLErp.QdpModule;
|
||||
|
||||
namespace YLErp.Modules.CalculationModule
|
||||
{
|
||||
[TestClass]
|
||||
public class SSpreadOptionCalcTest
|
||||
{
|
||||
[TestMethod("测试雪球期权PV")]
|
||||
public void TestSSpreadOptionCalcCrossGammas()
|
||||
{
|
||||
var spotPrice = 3200d;
|
||||
var coefficients = new double[] { -1, 1 };
|
||||
var td = new trade
|
||||
{
|
||||
id = 1,
|
||||
AssetBookName = "test",
|
||||
AssetId = 1,
|
||||
BasisGap = 0,
|
||||
BasisUnderlyingCode = null,
|
||||
BasisUnderlyingId = 0,
|
||||
BuySell = "卖出",
|
||||
CalcFlag = 1,
|
||||
ClientId = 1,
|
||||
ClientName = "客户名称",
|
||||
Comments = null,
|
||||
CreateDate = DateTime.Now,
|
||||
UnderlyingCode = "RB00-TA00",
|
||||
UnderlyingInstrumentType = ConsGlobal.InstrumentType.CommodityFutures,
|
||||
Strike = 3000,
|
||||
StartDate = new DateTime(2020, 12, 1),
|
||||
ExerciseDate = new DateTime(2020, 12, 31),
|
||||
MaturityDate = new DateTime(2023, 12, 31),
|
||||
OptionType = "看涨",
|
||||
ExerciseMode = ConsGlobal.ExerciseMode.American,
|
||||
Notional = 100,
|
||||
NoRiskRate = 0.05,
|
||||
ParticipationRate = 1,
|
||||
PrincipalRate = 0,
|
||||
IsAnnualized = false,
|
||||
AnnualizeFactor = 1,
|
||||
DividendRate = 0,
|
||||
IsMoneynessOption = "否",
|
||||
SpotPrice = 3200
|
||||
};
|
||||
|
||||
var marketProxy = QdpMarketManager.Instance.GetPrebuiltMarketProxy("111");
|
||||
|
||||
var crossGammas = SSpreadOptionCalc.CalculateSyntheticNormalSpreadCrossGammas(
|
||||
marketProxy,
|
||||
"2020-12-25",
|
||||
td.UnderlyingCode,
|
||||
td.UnderlyingInstrumentType,
|
||||
td.Strike ?? 0,
|
||||
td.StartDate.Value.ToString("yyyy-MM-dd"),
|
||||
td.MaturityDate.Value.ToString("yyyy-MM-dd"),
|
||||
td.CallPut,
|
||||
td.ExerciseMode,
|
||||
spotPrice,
|
||||
coefficients.ToArray(),
|
||||
td.Notional,
|
||||
"1111111",
|
||||
td.NoRiskRate ?? 0.0,
|
||||
td.BuySell,
|
||||
td.ExerciseDate.Value.ToString("yyyy-MM-dd"),
|
||||
td.ParticipationRate ?? 1.0,
|
||||
td.PrincipalRate ?? 0.0,
|
||||
td.IsAnnualized,
|
||||
td.AnnualizeFactor ?? 1.0,
|
||||
td.DividendRate ?? 0,
|
||||
td.IsMoneynessOptionData,
|
||||
td.SpotPrice ?? 0,
|
||||
hasNightMarket: false,
|
||||
commodityFuturesPreciseTimeMode: false,
|
||||
riskFreeRateOverride: td.NoRiskRate ?? double.NaN,
|
||||
dividendRateOverride: td.DividendRate ?? double.NaN);
|
||||
Assert.IsNotNull(crossGammas);
|
||||
|
||||
var crossGammas2 = OptionCalculatorV2.CalcSSpreadCrossGammas(new DateTime(2020, 12, 25), td, new OptionValueCalcRequest(0.05)
|
||||
{
|
||||
spotPrices = new[] { spotPrice },
|
||||
vols = new[] { 1.3 },
|
||||
}, coefficients);
|
||||
|
||||
Assert.IsNotNull(crossGammas);
|
||||
}
|
||||
}
|
||||
|
||||
static class SSpreadOptionCalc
|
||||
{
|
||||
/// <summary>
|
||||
/// 股指期货类型转换为商品期货类型
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static string ConvertInstrumentType(string instrumentType)
|
||||
{
|
||||
return ConsGlobal.InstrumentType.IsStockIF(instrumentType) ? ConsGlobal.InstrumentType.CommodityFutures : instrumentType;
|
||||
}
|
||||
|
||||
public static OptionExercise ConvertExerciseType(string exerciseType)
|
||||
{
|
||||
if (exerciseType != null)
|
||||
{
|
||||
switch (exerciseType.ToUpper())
|
||||
{
|
||||
case "美式":
|
||||
case "AMERICAN":
|
||||
return OptionExercise.American;
|
||||
default:
|
||||
return OptionExercise.European;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return OptionExercise.European;
|
||||
}
|
||||
}
|
||||
|
||||
public static SyntheticNormalSpreadOptionTrade CreateSyntheticNormalSpreadOptionTrade(
|
||||
string tradeId,
|
||||
string volSurfaceName,
|
||||
string tradeDate,
|
||||
string underlyingTicker,
|
||||
string underlyingInstrumentType,
|
||||
double strike,
|
||||
string startDate,
|
||||
string endDate,
|
||||
string optionType,
|
||||
string exerciseType,
|
||||
double notional,
|
||||
string tradeType,
|
||||
string exerciseDate,
|
||||
double participationRate,
|
||||
double principalRate,
|
||||
bool isAnnualized,
|
||||
double annualizeFactor,
|
||||
double[] coefficients = null,
|
||||
bool isMoneynessOption = false,
|
||||
double initialSpotPrice = 0.0,
|
||||
Dictionary<Date, double> dividends = null,
|
||||
bool hasNightMarket = false,
|
||||
bool commodityFuturesPreciseTimeMode = false,
|
||||
double timeToMaturityDays = double.NaN,
|
||||
double riskFreeRateOverride = double.NaN,
|
||||
double dividendRateOverride = double.NaN)
|
||||
{
|
||||
underlyingInstrumentType = ConvertInstrumentType(underlyingInstrumentType);
|
||||
|
||||
var exercise = ConvertExerciseType(exerciseType);
|
||||
var optionStartDate = startDate.ToDate();
|
||||
var underlyingMaturityDate = string.IsNullOrWhiteSpace(endDate) ? null : endDate.ToDate();
|
||||
var temp_exerciseDate = exerciseDate.ToDate();
|
||||
|
||||
if (temp_exerciseDate < optionStartDate)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Date[] exerciseDates = null;
|
||||
Date[] observationDates = null;
|
||||
if (exercise == OptionExercise.American)
|
||||
{
|
||||
exerciseDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(optionStartDate, temp_exerciseDate).ToArray();
|
||||
observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
exerciseDates = new Date[] { temp_exerciseDate };
|
||||
observationDates = new Date[] { temp_exerciseDate };
|
||||
}
|
||||
|
||||
if (underlyingInstrumentType == null)
|
||||
{
|
||||
throw new Exception("标的资产类型不能为空");
|
||||
}
|
||||
|
||||
var optionDayCount = CalculatorHelper.GetTradeDayCount();
|
||||
var pricingTOverride = double.IsNaN(timeToMaturityDays) ? double.NaN : QdpCalendarHelper.CalculateTFromDays(timeToMaturityDays, optionDayCount, optionStartDate.DateTime);
|
||||
|
||||
var syntheticNormalSpreadOption =
|
||||
new SyntheticNormalSpreadOption(
|
||||
optionStartDate,
|
||||
exercise,
|
||||
(OptionType)Enum.Parse(typeof(OptionType), optionType),
|
||||
strike,
|
||||
(InstrumentType)Enum.Parse(typeof(InstrumentType), underlyingInstrumentType),
|
||||
CalendarImpl.Get("chn"),
|
||||
optionDayCount.ToDayCountImpl(),
|
||||
CurrencyCode.CNY,
|
||||
CurrencyCode.CNY,
|
||||
exerciseDates,
|
||||
observationDates,
|
||||
coefficients, // coefficients
|
||||
notional,
|
||||
null,
|
||||
null,
|
||||
0.0,
|
||||
isMoneynessOption,
|
||||
initialSpotPrice,
|
||||
dividends,
|
||||
hasNightMarket: hasNightMarket,
|
||||
commodityFuturesPreciseTimeMode: commodityFuturesPreciseTimeMode,
|
||||
pricingToverride: pricingTOverride,
|
||||
riskFreeRateOverride: riskFreeRateOverride,
|
||||
dividendRateOverride: dividendRateOverride,
|
||||
participationRate: participationRate,
|
||||
isAnnualized: isAnnualized,
|
||||
annualizedFactor: annualizeFactor)
|
||||
{
|
||||
UnderlyingTickers = new string[] { underlyingTicker }
|
||||
};
|
||||
|
||||
if (string.IsNullOrWhiteSpace(tradeId))
|
||||
{
|
||||
tradeId = Guid.NewGuid().ToString();
|
||||
}
|
||||
return new SyntheticNormalSpreadOptionTrade(
|
||||
tradeId,
|
||||
tradeDate.ToDate(),
|
||||
syntheticNormalSpreadOption.StartDate,
|
||||
syntheticNormalSpreadOption.ExerciseDates.Last(),
|
||||
QdpConverter.ConvertTradeType(tradeType),
|
||||
syntheticNormalSpreadOption.Notional,
|
||||
0.0,
|
||||
syntheticNormalSpreadOption)
|
||||
{
|
||||
ValuationParameters = new OptionValuationParameters("RiskFreeDiscountCurve", MarketProxy.ConstantZeroCurve, volSurfaceName, syntheticNormalSpreadOption.UnderlyingTickers[0]),
|
||||
ProtectionRate = principalRate,
|
||||
ParticipationRate = participationRate,
|
||||
AnnualizedFactor = annualizeFactor,
|
||||
OriginalNotional = TradeHelper.GetStockEqvNotional(notional * initialSpotPrice, participationRate, annualizeFactor)
|
||||
};
|
||||
}
|
||||
|
||||
public static double[] CalculateSyntheticNormalSpreadCrossGammas(
|
||||
IQdpMarketProxy marketProxy,
|
||||
string valueDate,
|
||||
string underlyingTicker,
|
||||
string underlyingInstrumentType,
|
||||
double strike,
|
||||
string startDate,
|
||||
string endDate,
|
||||
string optionType,
|
||||
string exerciseType,
|
||||
double spotPrice,
|
||||
double[] coefficients,
|
||||
double notional,
|
||||
string volSurfaceName,
|
||||
double riskFreeRate,
|
||||
string tradeType,
|
||||
string exerciseDate,
|
||||
double participationRate,
|
||||
double principalRate,
|
||||
bool isAnnualized,
|
||||
double annualizeFactor,
|
||||
double dividendRate = 0.0,
|
||||
bool isMoneynessOption = false,
|
||||
double initialSpotPrice = 0.0,
|
||||
Dictionary<Date, double> dividends = null,
|
||||
bool hasNightMarket = false,
|
||||
bool commodityFuturesPreciseTimeMode = false,
|
||||
string engineName = null,
|
||||
string discountCurveName = null,
|
||||
bool ignoreSkewMap = false,
|
||||
bool isForwardTrade = false,
|
||||
double timeToMaturityDays = double.NaN,
|
||||
double riskFreeRateOverride = double.NaN,
|
||||
double dividendRateOverride = double.NaN)
|
||||
{
|
||||
if (coefficients == null || coefficients.Length == 1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
string tradeId = null;
|
||||
|
||||
var trade = CreateSyntheticNormalSpreadOptionTrade(
|
||||
tradeId,
|
||||
volSurfaceName,
|
||||
valueDate,
|
||||
underlyingTicker,
|
||||
underlyingInstrumentType,
|
||||
strike,
|
||||
startDate,
|
||||
endDate,
|
||||
optionType,
|
||||
exerciseType,
|
||||
notional,
|
||||
tradeType,
|
||||
exerciseDate,
|
||||
participationRate,
|
||||
principalRate,
|
||||
isAnnualized,
|
||||
annualizeFactor,
|
||||
coefficients,
|
||||
isMoneynessOption,
|
||||
initialSpotPrice,
|
||||
dividends,
|
||||
hasNightMarket,
|
||||
commodityFuturesPreciseTimeMode,
|
||||
timeToMaturityDays,
|
||||
riskFreeRateOverride,
|
||||
dividendRateOverride);
|
||||
|
||||
var market = marketProxy.GetQdpMarket(valueDate);
|
||||
if (market == null)
|
||||
{
|
||||
marketProxy.CreateMarket(valueDate);
|
||||
market = marketProxy.GetQdpMarket(valueDate);
|
||||
if (market == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var volPart = QdpVolHelper.GetDefaultVolatility(1.3);
|
||||
|
||||
var volSurfaceWrap = new VolSurfaceBuilder
|
||||
{
|
||||
volSurfaceName = volSurfaceName,
|
||||
volSurfaceType = "MoneynessVol",
|
||||
interpolation = "BiLinear"
|
||||
}.SetVectors(volPart.VolTable).Build(valueDate);
|
||||
marketProxy.SaveVolSurface(volSurfaceWrap);
|
||||
}
|
||||
|
||||
var useLocalDiscountCurve = string.IsNullOrWhiteSpace(discountCurveName);
|
||||
|
||||
//设置DiscountCurve
|
||||
if (useLocalDiscountCurve)
|
||||
{
|
||||
discountCurveName = Guid.NewGuid().ToString();
|
||||
var discountCurve = CalculatorHelper.CreateConstantRiskFreeCurve(discountCurveName, riskFreeRate);
|
||||
marketProxy.AddYieldCurve(discountCurveName, valueDate, discountCurve);
|
||||
}
|
||||
|
||||
//设置标的价格
|
||||
marketProxy.AddStockPrice(underlyingTicker, valueDate, spotPrice);
|
||||
|
||||
OptionValuationParameters parameters;
|
||||
if (underlyingInstrumentType == "Stock")
|
||||
{
|
||||
//设置DividendCurve
|
||||
var dividendCurveName = Guid.NewGuid().ToString();
|
||||
var dividendCurve = CalculatorHelper.CreateConstantRiskFreeCurve(dividendCurveName, dividendRate);
|
||||
marketProxy.AddYieldCurve(dividendCurveName, valueDate, dividendCurve);
|
||||
|
||||
parameters = new OptionValuationParameters(
|
||||
isForwardTrade ? MarketProxy.ConstantZeroCurve : discountCurveName,
|
||||
dividendCurveName,
|
||||
volSurfaceName,
|
||||
underlyingTicker);
|
||||
}
|
||||
else
|
||||
{
|
||||
parameters = new OptionValuationParameters(
|
||||
isForwardTrade ? MarketProxy.ConstantZeroCurve : discountCurveName,
|
||||
MarketProxy.ConstantZeroCurve,
|
||||
volSurfaceName,
|
||||
underlyingTicker);
|
||||
}
|
||||
|
||||
var result = trade.CalcCrossGammas(marketProxy.GetQdpMarket(valueDate), parameters);
|
||||
if (result == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var crossGammas = new List<double>();
|
||||
|
||||
// 先获取返回的结果矩阵中的对角线上的元素,对应的是每个标的的Gamma
|
||||
for (var i = 0; i < coefficients.Length; ++i)
|
||||
{
|
||||
crossGammas.Add(result[i, i]);
|
||||
}
|
||||
|
||||
// 再获取两两对应的Cross Gamma
|
||||
for (var i = 0; i < coefficients.Length - 1; ++i)
|
||||
{
|
||||
for (var j = i + 1; j < coefficients.Length; ++j)
|
||||
{
|
||||
crossGammas.Add(result[i, j]);
|
||||
}
|
||||
}
|
||||
|
||||
return crossGammas.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user