3020 lines
123 KiB
C#
3020 lines
123 KiB
C#
using Qdp.Pricing.Base.Enums;
|
|
using Qdp.Pricing.Base.Implementations;
|
|
using YLErp.BLL.Calculation;
|
|
using YLErp.BLL.Calculation.V2;
|
|
using YLErp.BLL.Calculation.V2.Parameter;
|
|
using YLErp.DBModels.Enums;
|
|
using YLErp.Modules.CalculationModule;
|
|
using YLErp.Modules.SkewMapVolModule;
|
|
using YLErp.OldVersion;
|
|
using YLErp.QdpModule;
|
|
using YLErp.QdpModule.Constants;
|
|
|
|
namespace YLErp.Modules.CalcModules
|
|
{
|
|
[TestClass]
|
|
public class CalculationTest
|
|
{
|
|
[TestMethod]
|
|
public void QuoteMaturityDateTest()
|
|
{
|
|
var maturity = CalculatorHelper.GetContractMaturityDateForQuote(new DateTime(2019, 11, 15), "RB1911");
|
|
Assert.AreEqual(new DateTime(2019, 11, 15), maturity, "RB1911");
|
|
maturity = CalculatorHelper.GetContractMaturityDateForQuote(new DateTime(2019, 12, 15), "RB1912");
|
|
Assert.AreEqual(new DateTime(2019, 12, 15), maturity, "RB1912");
|
|
maturity = CalculatorHelper.GetContractMaturityDateForQuote(new DateTime(2020, 01, 15), "RB2001");
|
|
Assert.AreEqual(new DateTime(2020, 1, 15), maturity, "RB2001");
|
|
maturity = CalculatorHelper.GetContractMaturityDateForQuote(new DateTime(2020, 02, 15), "RB2002");
|
|
Assert.AreEqual(new DateTime(2020, 2, 15), maturity, "RB2002");
|
|
|
|
maturity = CalculatorHelper.GetContractMaturityDateForQuote(new DateTime(2019, 10, 20), "SC1911");
|
|
Assert.AreEqual(new DateTime(2019, 11, 20), maturity, "SC1911");
|
|
maturity = CalculatorHelper.GetContractMaturityDateForQuote(new DateTime(2019, 11, 20), "SC1912");
|
|
Assert.AreEqual(new DateTime(2019, 12, 20), maturity, "SC1912");
|
|
maturity = CalculatorHelper.GetContractMaturityDateForQuote(new DateTime(2019, 12, 20), "SC2001");
|
|
Assert.AreEqual(new DateTime(2020, 1, 20), maturity, "SC2001");
|
|
maturity = CalculatorHelper.GetContractMaturityDateForQuote(new DateTime(2020, 1, 20), "SC2002");
|
|
Assert.AreEqual(new DateTime(2020, 2, 20), maturity, "SC2002");
|
|
|
|
|
|
}
|
|
|
|
[TestMethod]
|
|
public void CalendarTest()
|
|
{
|
|
var date = QdpCalendarHelper.BizDayShift(new DateTime(2018, 6, 15), 0);
|
|
Assert.AreEqual(new DateTime(2018, 6, 15), date, "");
|
|
|
|
date = QdpCalendarHelper.BizDayShift(new DateTime(2018, 6, 15), 1);
|
|
Assert.AreEqual(new DateTime(2018, 6, 19), date, "");
|
|
|
|
date = QdpCalendarHelper.BizDayShift(new DateTime(2018, 6, 16), 0);
|
|
Assert.AreEqual(new DateTime(2018, 6, 19), date, "");
|
|
|
|
date = QdpCalendarHelper.BizDayShift(new DateTime(2018, 6, 17), 0);
|
|
Assert.AreEqual(new DateTime(2018, 6, 19), date, "");
|
|
|
|
date = QdpCalendarHelper.BizDayShift(new DateTime(2018, 6, 18), 0);
|
|
Assert.AreEqual(new DateTime(2018, 6, 19), date, "");
|
|
|
|
date = QdpCalendarHelper.BizDayShift(new DateTime(2018, 6, 19), 0);
|
|
Assert.AreEqual(new DateTime(2018, 6, 19), date, "");
|
|
|
|
date = QdpCalendarHelper.BizDayShift(new DateTime(2018, 6, 16), 1);
|
|
Assert.AreEqual(new DateTime(2018, 6, 20), date, "");
|
|
|
|
date = QdpCalendarHelper.BizDayShift(new DateTime(2018, 6, 17), 1);
|
|
Assert.AreEqual(new DateTime(2018, 6, 20), date, "");
|
|
|
|
date = QdpCalendarHelper.BizDayShift(new DateTime(2018, 6, 18), 1);
|
|
Assert.AreEqual(new DateTime(2018, 6, 20), date, "");
|
|
|
|
date = QdpCalendarHelper.BizDayShift(new DateTime(2018, 6, 19), 1);
|
|
Assert.AreEqual(new DateTime(2018, 6, 20), date, "");
|
|
|
|
date = QdpCalendarHelper.BizDayShift(new DateTime(2018, 6, 16), -1);
|
|
Assert.AreEqual(new DateTime(2018, 6, 15), date, "");
|
|
|
|
date = QdpCalendarHelper.BizDayShift(new DateTime(2018, 6, 17), -1);
|
|
Assert.AreEqual(new DateTime(2018, 6, 15), date, "");
|
|
|
|
date = QdpCalendarHelper.BizDayShift(new DateTime(2018, 6, 18), -1);
|
|
Assert.AreEqual(new DateTime(2018, 6, 15), date, "");
|
|
|
|
date = QdpCalendarHelper.BizDayShift(new DateTime(2018, 6, 19), -1);
|
|
Assert.AreEqual(new DateTime(2018, 6, 15), date, "");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void CalendarBLLTest()
|
|
{
|
|
var date20190705 = new DateTime(2019, 07, 05);
|
|
var date20190706 = new DateTime(2019, 07, 06);
|
|
var date20190707 = new DateTime(2019, 07, 07);
|
|
var date20190708 = new DateTime(2019, 07, 08);
|
|
|
|
#region IsHoliday
|
|
Assert.IsFalse(QdpCalendarHelper.IsHoliday(date20190705));
|
|
Assert.IsTrue(QdpCalendarHelper.IsHoliday(date20190706));
|
|
Assert.IsTrue(QdpCalendarHelper.IsHoliday(date20190707));
|
|
Assert.IsFalse(QdpCalendarHelper.IsHoliday(date20190708));
|
|
#endregion
|
|
|
|
#region GetWorkDayCount
|
|
Assert.AreEqual(0, QdpCalendarHelper.GetWorkDayCount(date20190705, date20190705));
|
|
Assert.AreEqual(1, QdpCalendarHelper.GetWorkDayCount(date20190705, date20190708));
|
|
#endregion
|
|
|
|
#region GetWorkingDay
|
|
Assert.AreEqual(new DateTime(2019, 7, 10), QdpCalendarHelper.GetWorkingDay(date20190705, 3));
|
|
Assert.AreEqual(new DateTime(2019, 6, 28), QdpCalendarHelper.GetWorkingDay(date20190705, -5));
|
|
#endregion
|
|
|
|
#region GetWrokingDatesBeforeDate
|
|
var dates = QdpCalendarHelper.GetWorkingDatesBeforeDate(date20190708, 7);
|
|
Assert.AreEqual(7, dates.Count);
|
|
Assert.AreEqual(date20190708, dates[0]);
|
|
Assert.AreEqual(new DateTime(2019, 6, 28), dates[6]);
|
|
#endregion
|
|
|
|
#region GetNonHolidayDaysBetween
|
|
Assert.AreEqual(0, QdpCalendarHelper.GetNonHolidayDaysBetween(date20190705, date20190705));
|
|
Assert.AreEqual(1, QdpCalendarHelper.GetNonHolidayDaysBetween(date20190705, date20190708));
|
|
#endregion
|
|
|
|
#region GetNonHoliday
|
|
Assert.AreEqual(date20190705, QdpCalendarHelper.GetNonHoliday(date20190705));
|
|
Assert.AreEqual(date20190708, QdpCalendarHelper.GetNonHoliday(date20190706));
|
|
Assert.AreEqual(date20190708, QdpCalendarHelper.GetNonHoliday(date20190707));
|
|
#endregion
|
|
|
|
#region GetNonHolidayBefore
|
|
Assert.AreEqual(date20190705, QdpCalendarHelper.GetNonHolidayDefore(date20190705));
|
|
Assert.AreEqual(date20190705, QdpCalendarHelper.GetNonHolidayDefore(date20190706));
|
|
Assert.AreEqual(date20190705, QdpCalendarHelper.GetNonHolidayDefore(date20190707));
|
|
Assert.AreEqual(date20190708, QdpCalendarHelper.GetNonHolidayDefore(date20190708));
|
|
#endregion
|
|
}
|
|
|
|
[TestMethod]
|
|
public void GetObservationDateStringTest()
|
|
{
|
|
var str = QdpObservationHelper.GetObservationDateString(new DateTime(2019, 7, 23), new DateTime(2019, 8, 23), "1W", BusinessDayConvention.None);
|
|
Assert.AreEqual("2019-07-30,2019-08-06,2019-08-13,2019-08-20,2019-08-23", str);
|
|
|
|
str = QdpObservationHelper.GetObservationDateString(new DateTime(2019, 7, 23), new DateTime(2019, 8, 23), "11D", BusinessDayConvention.Following);
|
|
Assert.AreEqual("2019-08-05,2019-08-14,2019-08-23", str);
|
|
|
|
str = QdpObservationHelper.GetObservationDateString(new DateTime(2019, 7, 23), new DateTime(2019, 7, 30), "1W", BusinessDayConvention.ModifiedFollowing);
|
|
Assert.AreEqual("2019-07-30", str);
|
|
|
|
str = QdpObservationHelper.GetObservationDateString(new DateTime(2019, 7, 23), new DateTime(2019, 7, 31), "1W", BusinessDayConvention.ModifiedFollowing);
|
|
Assert.AreEqual("2019-07-30,2019-07-31", str);
|
|
|
|
str = QdpObservationHelper.GetObservationDateString(new DateTime(2019, 7, 23), new DateTime(2019, 8, 6), "1W", BusinessDayConvention.ModifiedFollowing);
|
|
Assert.AreEqual("2019-07-30,2019-08-06", str);
|
|
|
|
str = QdpObservationHelper.GetObservationDateString(new DateTime(2019, 7, 23), new DateTime(2019, 8, 7), "1W", BusinessDayConvention.ModifiedFollowing);
|
|
Assert.AreEqual("2019-07-30,2019-08-06,2019-08-07", str);
|
|
|
|
str = QdpObservationHelper.GetObservationDateString(new DateTime(2019, 7, 23), new DateTime(2019, 7, 29), "1D", BusinessDayConvention.Previous);
|
|
Assert.AreEqual("2019-07-24,2019-07-25,2019-07-26,2019-07-29", str);
|
|
|
|
str = QdpObservationHelper.GetObservationDateString(new DateTime(2019, 7, 23), new DateTime(2019, 7, 29), "2D", BusinessDayConvention.Previous);
|
|
Assert.AreEqual("2019-07-25,2019-07-26,2019-07-29", str);
|
|
|
|
str = QdpObservationHelper.GetObservationDateString(new DateTime(2019, 7, 23), new DateTime(2019, 7, 29), "3D", BusinessDayConvention.Previous);
|
|
Assert.AreEqual("2019-07-26,2019-07-29", str);
|
|
|
|
str = QdpObservationHelper.GetObservationDateString(new DateTime(2019, 9, 11), new DateTime(2019, 12, 26), "1M", BusinessDayConvention.None);
|
|
Assert.AreEqual("2019-10-11,2019-11-11,2019-12-11,2019-12-26", str);
|
|
|
|
str = QdpObservationHelper.GetObservationDateString(new DateTime(2019, 9, 11), new DateTime(2019, 12, 26), "1M", BusinessDayConvention.None, true);
|
|
Assert.AreEqual("2019-09-26,2019-10-26,2019-11-26,2019-12-26", str);
|
|
}
|
|
|
|
|
|
#region GetOptionValueResultTest
|
|
/// <summary>
|
|
/// 在Bus244情况下测试通过
|
|
/// </summary>
|
|
[TestMethod]
|
|
public void GetOptionValueResultTest()
|
|
{
|
|
PS.ResetConfig("{\"SkewMapVolConstruction\":\"false\"}");
|
|
//var privateType = new PrivateType(typeof(VolSurfaceInitializerSingleton));
|
|
//privateType.SetStaticField("_volConstructionType", VolConstructionType.Normal);
|
|
|
|
//1. Normal mode, vol Surface
|
|
NormalVolWithSurfaceTest();
|
|
|
|
//2. Normal mode, trade vol
|
|
NormalVolWithTradeVolTest();
|
|
|
|
PS.ResetConfig("{\"SkewMapVolConstruction\":\"true\"}");
|
|
//privateType = new PrivateType(typeof(VolSurfaceInitializerSingleton));
|
|
//privateType.SetStaticField("_volConstructionType", VolConstructionType.SkewMap);
|
|
|
|
PrepareGlobalSkewMap();
|
|
|
|
//3. SkewMap mode, vol surface
|
|
SkewMapVolWithSurfaceTest();
|
|
|
|
//4. SkewMap mode, trade vol
|
|
SkewMapVolWithTradeVolTest();
|
|
|
|
PS.ResetConfig("{\"SkewMapVolConstruction\":\"false\"}");
|
|
//privateType = new PrivateType(typeof(VolSurfaceInitializerSingleton));
|
|
//privateType.SetStaticField("_volConstructionType", VolConstructionType.Normal);
|
|
}
|
|
|
|
private underlying_manager CreateTestUnderlying()
|
|
{
|
|
return new underlying_manager()
|
|
{
|
|
UnderlyingInstrumentType = "CommodityFutures",
|
|
UnderlyingCode = "RB1909",
|
|
MaturityDate = new DateTime(2019, 9, 15)
|
|
};
|
|
}
|
|
|
|
private trade CreateTestTrade()
|
|
{
|
|
return new trade()
|
|
{
|
|
TradeType = "香草期权",
|
|
Strike = 3050,
|
|
OptionType = "看涨",
|
|
ExerciseMode = "European",
|
|
ExerciseDate = new DateTime(2019, 7, 12),
|
|
UnderlyingCode = "RB1909",
|
|
UnderlyingInstrumentType = "CommodityFutures",
|
|
BuySell = "卖出",
|
|
MaturityDate = new DateTime(2019, 7, 12),
|
|
Notional = 1.0,
|
|
TTMDays = 22
|
|
};
|
|
}
|
|
|
|
private volatility CreateTestVolSurface()
|
|
{
|
|
return new volatility()
|
|
{
|
|
VolSurfaceMode = "MoneynessVol",
|
|
InterpolationMethod = ConsVolMethod.BiLinear,
|
|
Data = "[{\"Strike\":0.9,\"Expire\":\"1W\",\"Vol\":0.2152},{\"Strike\":0.95,\"Expire\":\"1W\",\"Vol\":0.2106},{\"Strike\":1.0,\"Expire\":\"1W\",\"Vol\":0.2077},{\"Strike\":1.05,\"Expire\":\"1W\",\"Vol\":0.2064},{\"Strike\":1.1,\"Expire\":\"1W\",\"Vol\":0.2066},{\"Strike\":0.9,\"Expire\":\"2W\",\"Vol\":0.2152},{\"Strike\":0.95,\"Expire\":\"2W\",\"Vol\":0.2106},{\"Strike\":1.0,\"Expire\":\"2W\",\"Vol\":0.2077},{\"Strike\":1.05,\"Expire\":\"2W\",\"Vol\":0.2064},{\"Strike\":1.1,\"Expire\":\"2W\",\"Vol\":0.2066},{\"Strike\":0.9,\"Expire\":\"1M\",\"Vol\":0.2152},{\"Strike\":0.95,\"Expire\":\"1M\",\"Vol\":0.2106},{\"Strike\":1.0,\"Expire\":\"1M\",\"Vol\":0.2077},{\"Strike\":1.05,\"Expire\":\"1M\",\"Vol\":0.2064},{\"Strike\":1.1,\"Expire\":\"1M\",\"Vol\":0.2066}]"
|
|
};
|
|
}
|
|
|
|
private void NormalVolWithSurfaceTest()
|
|
{
|
|
var underlying = CreateTestUnderlying();
|
|
var trade = CreateTestTrade();
|
|
var vol = CreateTestVolSurface();
|
|
|
|
var valueDateStr = "2019-06-13";
|
|
underlying.QuotationDate = DateTime.Parse(valueDateStr);
|
|
trade.StartDate = DateTime.Parse(valueDateStr);
|
|
var userId = Guid.NewGuid().ToString();
|
|
var marketProxy = QdpMarketManager.Instance.GetPrebuiltMarketProxy(userId);
|
|
|
|
var initParam = new VolSurfaceInitParamsBuilder(userId)
|
|
.SetUnderlying(underlying).SetVolatility(vol).SetValueDate(valueDateStr)
|
|
.Build(underlying.UnderlyingCode);
|
|
VolSurfaceInitializerSingleton.GetInitializer(false).InitializeMarketProxy(initParam);
|
|
|
|
var discountCurveName = Guid.NewGuid().ToString();
|
|
var discountCurve = CalculatorHelper.CreateConstantRiskFreeCurve(discountCurveName, 0.04);
|
|
marketProxy.AddYieldCurve(discountCurveName, valueDateStr, discountCurve);
|
|
|
|
var result = OptionCalculatorV1.GetOptionValueResult(
|
|
userId,
|
|
underlying,
|
|
trade,
|
|
new double[] { 3000.0 },
|
|
fixing: null,
|
|
useTradeVolMode: false,
|
|
preciseTimeMode: false,
|
|
discountCurveName: discountCurveName);
|
|
|
|
QdpMarketManager.Instance.RemovePrebuiltMarketProxy(userId);
|
|
|
|
Assert.AreEqual(0.20726666666666666, result.Vol, 1E-8, "NormalVolWithSurfaceTest Vol");
|
|
Assert.AreEqual(-52.53732559515695, result.Pv, 1E-8, "NormalVolWithSurfaceTest Pv");
|
|
Assert.AreEqual(-0.40892373614411781, result.Delta, 1E-8, "NormalVolWithSurfaceTest Delta");
|
|
Assert.AreEqual(-0.0020790010069049458, result.Gamma, 1E-8, "NormalVolWithSurfaceTest Gamma");
|
|
Assert.AreEqual(-3.4837223134672968, result.Vega, 1E-8, "NormalVolWithSurfaceTest Vega");
|
|
Assert.AreEqual(0.00047367584343049884, result.Rho, 1E-8, "NormalVolWithSurfaceTest Rho");
|
|
Assert.AreEqual(1.6504286616814952, result.Theta, 1E-8, "NormalVolWithSurfaceTest Theta");
|
|
}
|
|
|
|
private void NormalVolWithTradeVolTest()
|
|
{
|
|
var underlying = CreateTestUnderlying();
|
|
var trade = CreateTestTrade();
|
|
const double vol = 0.2075;
|
|
var volTable = new List<SingleVol>
|
|
{
|
|
new SingleVol
|
|
{
|
|
Strike = 1,
|
|
Expire = "1D",
|
|
Vol = vol
|
|
},
|
|
new SingleVol
|
|
{
|
|
Strike = 1,
|
|
Expire = "1Y",
|
|
Vol = vol
|
|
}
|
|
};
|
|
|
|
var valueDateStr = "2019-06-13";
|
|
underlying.QuotationDate = DateTime.Parse(valueDateStr);
|
|
trade.StartDate = DateTime.Parse(valueDateStr);
|
|
var userId = Guid.NewGuid().ToString();
|
|
var marketProxy = QdpMarketManager.Instance.GetPrebuiltMarketProxy(userId);
|
|
|
|
var initParam = new VolSurfaceInitParamsBuilder(userId)
|
|
.SetUnderlying(underlying).SetValueDate(valueDateStr)
|
|
.Build("MoneynessVol", volTable, trade.id.ToString());
|
|
VolSurfaceInitializerSingleton.GetInitializer(true).InitializeMarketProxy(initParam);
|
|
|
|
var discountCurveName = Guid.NewGuid().ToString();
|
|
var discountCurve = CalculatorHelper.CreateConstantRiskFreeCurve(discountCurveName, 0.04);
|
|
marketProxy.AddYieldCurve(discountCurveName, valueDateStr, discountCurve);
|
|
|
|
var result = OptionCalculatorV1.GetOptionValueResult(
|
|
userId,
|
|
underlying,
|
|
trade,
|
|
new double[] { 3000.0 },
|
|
fixing: null,
|
|
useTradeVolMode: true,
|
|
preciseTimeMode: false,
|
|
discountCurveName: discountCurveName);
|
|
|
|
QdpMarketManager.Instance.RemovePrebuiltMarketProxy(userId);
|
|
|
|
Assert.AreEqual(0.2075, result.Vol, 1E-8, "NormalVolWithTradeVolTest Vol");
|
|
Assert.AreEqual(-52.618615629338535, result.Pv, 1E-8, "NormalVolWithTradeVolTest Pv");
|
|
Assert.AreEqual(-0.40598319079236944, result.Delta, 1E-8, "NormalVolWithTradeVolTest Delta");
|
|
Assert.AreEqual(-0.0020691189206445415, result.Gamma, 1E-8, "NormalVolWithTradeVolTest Gamma");
|
|
Assert.AreEqual(-3.483994693558401, result.Vega, 1E-8, "NormalVolWithTradeVolTest Vega");
|
|
Assert.AreEqual(0.00047440875331972165, result.Rho, 1E-8, "NormalVolWithTradeVolTest Rho");
|
|
Assert.AreEqual(1.6524160238749488, result.Theta, 1E-8, "NormalVolWithTradeVolTest Theta");
|
|
}
|
|
|
|
private volatility CreateSkewMapBaseVolSurface()
|
|
{
|
|
return new volatility()
|
|
{
|
|
VolSurfaceMode = "MoneynessVol",
|
|
InterpolationMethod = ConsVolMethod.BiLinear,
|
|
Data = "[{\"Strike\":\"1.0000\",\"Expire\":\"1M\",\"Vol\":0.21},{\"Strike\":\"1.0000\",\"Expire\":\"3M\",\"Vol\":0.1937},{\"Strike\":\"1.0000\",\"Expire\":\"6M\",\"Vol\":0.1605},{\"Strike\":1,\"Expire\":\"AskVar\",\"Vol\":30},{\"Strike\":1,\"Expire\":\"BidVar\",\"Vol\":30}]"
|
|
};
|
|
}
|
|
|
|
private void PrepareGlobalSkewMap()
|
|
{
|
|
var data = File.ReadAllText("GlobalSkewMap.csv");
|
|
VolSkewMapInitializerSingleton.Instance.SetSkewMapData(data, isBuy: false);
|
|
}
|
|
|
|
private void SkewMapVolWithSurfaceTest()
|
|
{
|
|
var underlying = CreateTestUnderlying();
|
|
var trade = CreateTestTrade();
|
|
var vol = CreateSkewMapBaseVolSurface();
|
|
|
|
var valueDateStr = "2019-06-13";
|
|
underlying.QuotationDate = DateTime.Parse(valueDateStr);
|
|
trade.StartDate = DateTime.Parse(valueDateStr);
|
|
var userId = Guid.NewGuid().ToString();
|
|
var marketProxy = QdpMarketManager.Instance.GetPrebuiltMarketProxy(userId);
|
|
|
|
var initParam = new VolSurfaceInitParamsBuilder(userId)
|
|
.SetUnderlying(underlying).SetVolatility(vol).SetValueDate(valueDateStr)
|
|
.Build(underlying.UnderlyingCode);
|
|
VolSurfaceInitializerSingleton.GetInitializer(false).InitializeMarketProxy(initParam);
|
|
|
|
var discountCurveName = Guid.NewGuid().ToString();
|
|
var discountCurve = CalculatorHelper.CreateConstantRiskFreeCurve(discountCurveName, 0.04);
|
|
marketProxy.AddYieldCurve(discountCurveName, valueDateStr, discountCurve);
|
|
|
|
var result = OptionCalculatorV1.GetOptionValueResult(
|
|
userId,
|
|
underlying,
|
|
trade,
|
|
new double[] { 3000.0 },
|
|
fixing: null,
|
|
useTradeVolMode: false,
|
|
preciseTimeMode: false,
|
|
discountCurveName: discountCurveName);
|
|
|
|
QdpMarketManager.Instance.RemovePrebuiltMarketProxy(userId);
|
|
|
|
Assert.AreEqual(0.27703706146327278, result.Vol, 1E-8, "SkewMapVolWithSurfaceTest Vol");
|
|
Assert.AreEqual(-77.05603142631152, result.Pv, 1E-8, "SkewMapVolWithSurfaceTest Pv");
|
|
Assert.AreEqual(-0.43601254203338158, result.Delta, 1E-8, "SkewMapVolWithSurfaceTest Delta");
|
|
Assert.AreEqual(-0.0015732894098619, result.Gamma, 1E-8, "SkewMapVolWithSurfaceTest Gamma");
|
|
Assert.AreEqual(-3.5368906374252163, result.Vega, 1E-8, "SkewMapVolWithSurfaceTest Vega");
|
|
Assert.AreEqual(0.000694736175923083, result.Rho, 1E-8, "SkewMapVolWithSurfaceTest Rho");
|
|
Assert.AreEqual(2.2395635945358663, result.Theta, 1E-8, "SkewMapVolWithSurfaceTest Theta");
|
|
}
|
|
|
|
private void SkewMapVolWithTradeVolTest()
|
|
{
|
|
var underlying = CreateTestUnderlying();
|
|
var trade = CreateTestTrade();
|
|
const double vol = 0.2075;
|
|
var volTable = new List<SingleVol>
|
|
{
|
|
new SingleVol
|
|
{
|
|
Strike = 1,
|
|
Expire = "1D",
|
|
Vol = vol
|
|
},
|
|
new SingleVol
|
|
{
|
|
Strike = 1,
|
|
Expire = "1Y",
|
|
Vol = vol
|
|
}
|
|
};
|
|
|
|
var valueDateStr = "2019-06-13";
|
|
underlying.QuotationDate = DateTime.Parse(valueDateStr);
|
|
trade.StartDate = DateTime.Parse(valueDateStr);
|
|
var userId = Guid.NewGuid().ToString();
|
|
var marketProxy = QdpMarketManager.Instance.GetPrebuiltMarketProxy(userId);
|
|
|
|
var initParam = new VolSurfaceInitParamsBuilder(userId)
|
|
.SetUnderlying(underlying).SetValueDate(valueDateStr)
|
|
.Build("MoneynessVol", volTable, trade.id.ToString());
|
|
VolSurfaceInitializerSingleton.GetInitializer(true).InitializeMarketProxy(initParam);
|
|
|
|
var discountCurveName = Guid.NewGuid().ToString();
|
|
var discountCurve = CalculatorHelper.CreateConstantRiskFreeCurve(discountCurveName, 0.04);
|
|
marketProxy.AddYieldCurve(discountCurveName, valueDateStr, discountCurve);
|
|
|
|
var result = OptionCalculatorV1.GetOptionValueResult(
|
|
userId,
|
|
underlying,
|
|
trade,
|
|
new double[] { 3000.0 },
|
|
fixing: null,
|
|
useTradeVolMode: true,
|
|
preciseTimeMode: false,
|
|
discountCurveName: discountCurveName);
|
|
|
|
QdpMarketManager.Instance.RemovePrebuiltMarketProxy(userId);
|
|
|
|
Assert.AreEqual(0.2075, result.Vol, 1E-8, "SkewMapVolWithSurfaceTest Vol");
|
|
Assert.AreEqual(-52.618615629338535, result.Pv, 1E-8, "SkewMapVolWithSurfaceTest Pv");
|
|
Assert.AreEqual(-0.40598319079236944, result.Delta, 1E-8, "SkewMapVolWithSurfaceTest Delta");
|
|
Assert.AreEqual(-0.0020691189206445415, result.Gamma, 1E-8, "SkewMapVolWithSurfaceTest Gamma");
|
|
Assert.AreEqual(-3.483994693558401, result.Vega, 1E-8, "SkewMapVolWithSurfaceTest Vega");
|
|
Assert.AreEqual(0.00047440875331972165, result.Rho, 1E-8, "SkewMapVolWithSurfaceTest Rho");
|
|
Assert.AreEqual(1.6524160238749488, result.Theta, 1E-8, "SkewMapVolWithSurfaceTest Theta");
|
|
}
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// 商品期货交易手续费测试
|
|
/// </summary>
|
|
[TestMethod]
|
|
public void CommodityFuturesCommissionTest()
|
|
{
|
|
underlying_manager rb1910 = null, m1908 = null, ru1907 = null;
|
|
using (var db = new YLContext())
|
|
{
|
|
rb1910 = db.underlying_manager.FirstOrDefault(x => x.UnderlyingCode == "RB1910");
|
|
m1908 = db.underlying_manager.FirstOrDefault(x => x.UnderlyingCode == "M1908");
|
|
ru1907 = db.underlying_manager.FirstOrDefault(x => x.UnderlyingCode == "RU1907");
|
|
}
|
|
|
|
Assert.IsNotNull(rb1910, "rb1910 null");
|
|
Assert.IsNotNull(rb1910, "m1908 null");
|
|
Assert.IsNotNull(ru1907, "RU1907 null");
|
|
|
|
#region 按比例收取手续费
|
|
var trades = new List<ExchangeTrade>() {
|
|
new ExchangeTrade() {
|
|
id = 1,
|
|
UnderlyingCode = "RB1910",
|
|
TradeSide = "多头开仓",
|
|
Notional = 1000,
|
|
TradeSinglePrice = 4000,
|
|
CommissionType = CommissionType.系统计算
|
|
},
|
|
new ExchangeTrade() {
|
|
id = 2,
|
|
UnderlyingCode = "RB1910",
|
|
TradeSide = "多头平仓",
|
|
Notional = 800,
|
|
TradeSinglePrice = 4100,
|
|
CommissionType = CommissionType.系统计算
|
|
},
|
|
new ExchangeTrade() {
|
|
id = 3,
|
|
UnderlyingCode = "RB1910",
|
|
TradeSide = "多头开仓",
|
|
Notional = 1000,
|
|
TradeSinglePrice = 4200,
|
|
CommissionType = CommissionType.系统计算
|
|
},
|
|
new ExchangeTrade() {
|
|
id = 4,
|
|
UnderlyingCode = "RB1910",
|
|
TradeSide = "多头平仓",
|
|
Notional = 100,
|
|
TradeSinglePrice = 4250,
|
|
CommissionType = CommissionType.不收取 //这笔不会计算手续费
|
|
},
|
|
new ExchangeTrade() {
|
|
id = 5,
|
|
UnderlyingCode = "RB1910",
|
|
TradeSide = "多头平仓",
|
|
Notional = 1300,
|
|
TradeSinglePrice = 4150,
|
|
CommissionType = CommissionType.系统计算
|
|
}
|
|
};
|
|
|
|
var commissions = CommissionCalcHelper.GetCommissionForTrade(rb1910, trades);
|
|
|
|
Assert.AreEqual(4, commissions.Count, "RB1910 commissions count");
|
|
Assert.AreEqual(2000, commissions[1], 1e-8, "RB1910 trade 1 commission");
|
|
Assert.AreEqual(1640, commissions[2], 1e-8, "RB1910 trade 2 commission");
|
|
Assert.AreEqual(2100, commissions[3], 1e-8, "RB1910 trade 3 commission");
|
|
Assert.AreEqual(2697.5, commissions[5], 1e-8, "RB1910 trade 3 commission");
|
|
#endregion
|
|
|
|
#region 按固定收取手续费
|
|
trades = new List<ExchangeTrade>() {
|
|
new ExchangeTrade() {
|
|
id = 1,
|
|
UnderlyingCode = "M1908",
|
|
TradeSide = "多头开仓",
|
|
Notional = 1000,
|
|
TradeSinglePrice = 2800,
|
|
CommissionType = CommissionType.系统计算
|
|
},
|
|
new ExchangeTrade() {
|
|
id = 2,
|
|
UnderlyingCode = "M1908",
|
|
TradeSide = "多头平仓",
|
|
Notional = 800,
|
|
TradeSinglePrice = 2800,
|
|
CommissionType = CommissionType.系统计算
|
|
},
|
|
new ExchangeTrade() {
|
|
id = 3,
|
|
UnderlyingCode = "M1908",
|
|
TradeSide = "多头开仓",
|
|
Notional = 1000,
|
|
TradeSinglePrice = 2800,
|
|
CommissionType = CommissionType.系统计算
|
|
},
|
|
new ExchangeTrade() {
|
|
id = 4,
|
|
UnderlyingCode = "M1908",
|
|
TradeSide = "多头平仓",
|
|
Notional = 100,
|
|
TradeSinglePrice = 2800,
|
|
CommissionType = CommissionType.不收取 //这笔不会计算手续费
|
|
},
|
|
new ExchangeTrade() {
|
|
id = 5,
|
|
UnderlyingCode = "M1908",
|
|
TradeSide = "多头平仓",
|
|
Notional = 1300,
|
|
TradeSinglePrice = 2800,
|
|
CommissionType = CommissionType.系统计算
|
|
}
|
|
};
|
|
|
|
commissions = CommissionCalcHelper.GetCommissionForTrade(m1908, trades);
|
|
|
|
Assert.AreEqual(4, commissions.Count, "M1908 commissions count");
|
|
Assert.AreEqual(150, commissions[1], 1e-8, "M1908 trade 1 commission");
|
|
Assert.AreEqual(120, commissions[2], 1e-8, "M1908 trade 2 commission");
|
|
Assert.AreEqual(150, commissions[3], 1e-8, "M1908 trade 3 commission");
|
|
Assert.AreEqual(195, commissions[5], 1e-8, "M1908 trade 3 commission");
|
|
|
|
#endregion
|
|
|
|
#region 带有平今仓手续费规则
|
|
trades = new List<ExchangeTrade>() {
|
|
new ExchangeTrade() {
|
|
id = 1,
|
|
UnderlyingCode = "RU1909",
|
|
TradeSide = "多头开仓",
|
|
Notional = 1000,
|
|
TradeSinglePrice = 12000,
|
|
CommissionType = CommissionType.系统计算
|
|
},
|
|
new ExchangeTrade() {
|
|
id = 2,
|
|
UnderlyingCode = "RU1909",
|
|
TradeSide = "多头平仓",
|
|
Notional = 800,
|
|
TradeSinglePrice = 11000,
|
|
CommissionType = CommissionType.系统计算
|
|
},
|
|
new ExchangeTrade() {
|
|
id = 3,
|
|
UnderlyingCode = "RU1909",
|
|
TradeSide = "多头开仓",
|
|
Notional = 1000,
|
|
TradeSinglePrice = 11500,
|
|
CommissionType = CommissionType.系统计算
|
|
},
|
|
new ExchangeTrade() {
|
|
id = 4,
|
|
UnderlyingCode = "RU1909",
|
|
TradeSide = "多头平仓",
|
|
Notional = 100,
|
|
TradeSinglePrice = 13000,
|
|
CommissionType = CommissionType.不收取 //这笔不会计算手续费
|
|
},
|
|
new ExchangeTrade() {
|
|
id = 5,
|
|
UnderlyingCode = "RU1909",
|
|
TradeSide = "多头平仓",
|
|
Notional = 1300,
|
|
TradeSinglePrice = 12500,
|
|
CommissionType = CommissionType.系统计算
|
|
}
|
|
};
|
|
|
|
commissions = CommissionCalcHelper.GetCommissionForTrade(ru1907, trades);
|
|
|
|
Assert.AreEqual(4, commissions.Count, "RU1909 commissions count");
|
|
Assert.AreEqual(540, commissions[1], 1e-8, "RU1909 trade 1 commission");
|
|
Assert.AreEqual(160, commissions[2], 1e-8, "RU1909 trade 2 commission");
|
|
Assert.AreEqual(517.5, commissions[3], 1e-8, "RU1909 trade 3 commission");
|
|
Assert.AreEqual(296.25, commissions[5], 1e-8, "RU1909 trade 3 commission");
|
|
#endregion
|
|
}
|
|
|
|
[TestMethod]
|
|
public void CommodityFutureOptionMarginTest()
|
|
{
|
|
double premium = 45,
|
|
amount = 3,
|
|
strike = 4000,
|
|
futureMarginRatio = 0.09;
|
|
var optionType = "Call";
|
|
#region Call
|
|
//InTheMoney
|
|
double spotPrice = 4100;
|
|
var benchmark = premium + spotPrice * amount * futureMarginRatio;
|
|
var margin = OptionCalculatorV1.CommodityFutureOptionMargin(premium, amount, strike, spotPrice, futureMarginRatio, optionType);
|
|
Assert.AreEqual(benchmark, margin, double.Epsilon);
|
|
|
|
//AtTheMoney
|
|
spotPrice = 4000;
|
|
benchmark = premium + (spotPrice * amount * futureMarginRatio);
|
|
margin = OptionCalculatorV1.CommodityFutureOptionMargin(premium, amount, strike, spotPrice, futureMarginRatio, optionType);
|
|
Assert.AreEqual(benchmark, margin, double.Epsilon);
|
|
|
|
//OutTheMoney and Value < futureMargin
|
|
spotPrice = 3900;
|
|
benchmark = premium + (spotPrice * amount * futureMarginRatio) - ((strike - spotPrice) * amount * 0.5);
|
|
margin = OptionCalculatorV1.CommodityFutureOptionMargin(premium, amount, strike, spotPrice, futureMarginRatio, optionType);
|
|
Assert.AreEqual(benchmark, margin, double.Epsilon);
|
|
|
|
//OutTheMoney and Value > futureMargin
|
|
spotPrice = 3500;
|
|
benchmark = premium + (spotPrice * amount * futureMarginRatio * 0.5);
|
|
margin = OptionCalculatorV1.CommodityFutureOptionMargin(premium, amount, strike, spotPrice, futureMarginRatio, optionType);
|
|
Assert.AreEqual(benchmark, margin, double.Epsilon);
|
|
|
|
#endregion
|
|
|
|
#region Put
|
|
optionType = "Put";
|
|
|
|
//InTheMoney
|
|
spotPrice = 3900;
|
|
benchmark = premium + (spotPrice * amount * futureMarginRatio);
|
|
margin = OptionCalculatorV1.CommodityFutureOptionMargin(premium, amount, strike, spotPrice, futureMarginRatio, optionType);
|
|
Assert.AreEqual(benchmark, margin, double.Epsilon);
|
|
|
|
//AtTheMoney
|
|
spotPrice = 4000;
|
|
benchmark = premium + (spotPrice * amount * futureMarginRatio);
|
|
margin = OptionCalculatorV1.CommodityFutureOptionMargin(premium, amount, strike, spotPrice, futureMarginRatio, optionType);
|
|
Assert.AreEqual(benchmark, margin, double.Epsilon);
|
|
|
|
//OutTheMoney and Value < futureMargin
|
|
spotPrice = 4100;
|
|
benchmark = premium + (spotPrice * amount * futureMarginRatio) - ((spotPrice - strike) * amount * 0.5);
|
|
margin = OptionCalculatorV1.CommodityFutureOptionMargin(premium, amount, strike, spotPrice, futureMarginRatio, optionType);
|
|
Assert.AreEqual(benchmark, margin, double.Epsilon);
|
|
|
|
//OutTheMoney and Value > futureMargin
|
|
spotPrice = 4500;
|
|
benchmark = premium + (spotPrice * amount * futureMarginRatio * 0.5);
|
|
margin = OptionCalculatorV1.CommodityFutureOptionMargin(premium, amount, strike, spotPrice, futureMarginRatio, optionType);
|
|
Assert.AreEqual(benchmark, margin, double.Epsilon);
|
|
|
|
#endregion
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ValueVanillaOptionWithVolSettingTest()
|
|
{
|
|
var valueDate = new DateTime(2019, 4, 4);
|
|
var underlyingTicker = "RB00";
|
|
|
|
var tenors = new string[]
|
|
{
|
|
"1D",
|
|
"2M",
|
|
"1Y"
|
|
};
|
|
|
|
var strikes = new double[]
|
|
{
|
|
0.95,
|
|
1.0,
|
|
1.05,
|
|
};
|
|
|
|
var vols = new double[3, 3];
|
|
var singleVols = new List<SingleVol>();
|
|
for (var i = 0; i < vols.GetLength(0); ++i)
|
|
{
|
|
for (var j = 0; j < vols.GetLength(1); ++j)
|
|
{
|
|
vols[i, j] = 0.2;
|
|
singleVols.Add(new SingleVol()
|
|
{
|
|
Strike = strikes[j],
|
|
Expire = tenors[i],
|
|
Vol = 0.2
|
|
});
|
|
}
|
|
}
|
|
|
|
PS.ResetConfig("{\"SkewMapVolConstruction\":\"false\"}");
|
|
|
|
TradeValueResult result = null;
|
|
|
|
using (var mp = new MarketProxy(valueDate, 0.035))
|
|
{
|
|
var tradeParam = new VanillaOptionTradeParam
|
|
{
|
|
underlyingTickers = new[] { underlyingTicker },
|
|
underlyingInstrumentType = "CommodityFutures",
|
|
strike = 2900.0,
|
|
startDate = valueDate,
|
|
endDate = new DateTime(2019, 05, 06),
|
|
optionType = OptionType.Call,// "Call",
|
|
exerciseType = "European",
|
|
initialSpotPrice = 2900.0,
|
|
notional = 100.0,
|
|
volSurfaceNames = new[] { underlyingTicker + "_VolSurface" },
|
|
riskFreeRate = 0.035,
|
|
buysell = "Buy",
|
|
exerciseDate = new DateTime(2019, 05, 06),
|
|
dividendRate = 0,
|
|
timeToMaturityDays = 18,
|
|
participationRate = 1.0,
|
|
principalRate = 0.0,
|
|
isAnnualized = false,
|
|
annualizedFactor = 1.0,
|
|
preciseTimeMode = false,
|
|
};
|
|
|
|
mp.SetVolSurface(tradeParam.volSurfaceNames[0], new VolatilityImpl { VolTable = singleVols });
|
|
|
|
result = TradeRiskCalcUtil.GetVanillaOptionValue(mp, new OptionCalcParam<VanillaOptionTradeParam>(tradeParam)
|
|
{
|
|
spotPrices = new[] { 2900.0 }
|
|
});
|
|
}
|
|
|
|
//var initParam = new VolSurfaceInitParamsBuilder()
|
|
// .SetUnderlying(underlying).SetValueDate(valueDate).Build("MoneynessVol", singleVols);
|
|
//new NormalVolSurfaceInitializer().InitializeMarketProxy(marketProxy, initParam);
|
|
|
|
//计算
|
|
//result = OptionCalculator.ValueVanillaOptionTrade(
|
|
// marketProxy: marketProxy,
|
|
// valueDate: valueDate + "",
|
|
// underlyingTicker: underlyingTicker,
|
|
// underlyingInstrumentType: "CommodityFutures",
|
|
// strike: 2900.0,
|
|
// startDate: valueDate + "",
|
|
// endDate: "2019-05-06",
|
|
// optionType: "Call",
|
|
// exerciseType: "European",
|
|
// spotPrice: 2900.0,
|
|
// notional: 100.0,
|
|
// volSurfaceName: underlyingTicker + "_VolSurface",
|
|
// riskFreeRate: 0.035,
|
|
// modelName: null,
|
|
// tradeType: "Buy",
|
|
// exerciseDate: "2019-05-06",
|
|
// dividendRate: 0,
|
|
// timeToMaturityDays: 18,
|
|
// ignoreSkewMap: true,
|
|
// participationRate: 1.0,
|
|
// principalRate: 0.0,
|
|
// isAnnualized: false,
|
|
// annualizeFactor: 1.0);
|
|
|
|
Assert.AreEqual(6267.6490467250824, result.Pv, 1E-8, "");
|
|
Assert.AreEqual(50.9516973058509, result.Delta, 1E-8, "");
|
|
Assert.AreEqual(0.25249779355362989, result.Gamma, 1E-8, "");
|
|
Assert.AreEqual(313.305398649345, result.Vega, 1E-8, "");
|
|
Assert.AreEqual(-175.67364449584602, result.Theta, 1E-8, "");
|
|
//Assert.AreEqual(-0.04623504985065665, result.Rho, 1E-8, "");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ValueVanillaOptionWithVolSettingTestV2()
|
|
{
|
|
var valueDate = "2019-04-04";
|
|
var underlyingTicker = "RB00";
|
|
var underlying = new underlying_manager()
|
|
{
|
|
QuotationDate = DateTime.Parse(valueDate),
|
|
UnderlyingCode = underlyingTicker,
|
|
UnderlyingInstrumentType = "CommodityFutures",
|
|
MaturityDate = new DateTime(2019, 5, 6)
|
|
};
|
|
|
|
var userId = Guid.NewGuid().ToString();
|
|
|
|
PS.ResetConfig("{\"SkewMapVolConstruction\":\"false\"}");
|
|
|
|
var trade = new trade()
|
|
{
|
|
TradeType = "香草期权",
|
|
TradeDate = new DateTime(2019, 4, 4),
|
|
MaturityDate = new DateTime(2019, 5, 6),
|
|
ExerciseDate = new DateTime(2019, 5, 6),
|
|
OptionType = "看涨",
|
|
ExerciseMode = "European",
|
|
BuySell = "Buy",
|
|
Strike = 2900,
|
|
Notional = 100
|
|
};
|
|
|
|
var parameter = new VanillaOptionParameter()
|
|
{
|
|
ValueDate = new DateTime(2019, 4, 4),
|
|
Volatility = 0.2,
|
|
SpotPrices = new Dictionary<string, double>() { { underlying.UnderlyingCode, 2900 } },
|
|
OverrideTTM = 18,
|
|
RiskFreeRate = 0.035
|
|
};
|
|
|
|
var result = ValueCalculator.CalculateTradeValue(
|
|
userId,
|
|
trade,
|
|
underlying,
|
|
parameter);
|
|
|
|
Assert.AreEqual(6267.6490467250824, result.Pv, 1E-8, "");
|
|
Assert.AreEqual(50.95295979454022, result.Delta, 1E-8, "");
|
|
Assert.AreEqual(0.25249779355362989, result.Gamma, 1E-8, "");
|
|
Assert.AreEqual(313.305398649345, result.Vega, 1E-8, "");
|
|
Assert.AreEqual(-175.67364449584602, result.Theta, 1E-8, "");
|
|
Assert.AreEqual(-0.04623504985065665, result.Rho, 1E-8, "");
|
|
}
|
|
|
|
/// <summary>
|
|
/// 在期权定价时调整定价日
|
|
/// </summary>
|
|
private DateTime AdjustQuotationDate(DateTime quotationDate, string tradeType)
|
|
{
|
|
// 对凤凰雪球不做报价日调整
|
|
if (tradeType == "雪球期权" || tradeType == "凤凰期权")
|
|
{
|
|
return quotationDate;
|
|
}
|
|
|
|
//为了暂时修复一个QDP计算方式与客户需求不匹配的情况
|
|
//在非精确模式下,QDP不包括交易日当天的时间价值,但根据报价需求,需要将交易日当天的时间价值计算在内,
|
|
//所以要将交易日向前移动一天。未来QDP支持传递TTM来计算时,可以直接在TTM上加1,而不用移动交易日
|
|
//注意:当前这个临时修改必须在波动率插值之前调用,这样才能正确设置波动率日期
|
|
var calendar = CalendarImpl.Get("chn");
|
|
return calendar.PrevBizDay(quotationDate).DateTime;
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ValueBarrierOptionTest()
|
|
{
|
|
var valueDate = new DateTime(2019, 04, 04);
|
|
var underlyingTicker = "RB00";
|
|
|
|
TradeValueResult result;
|
|
|
|
if (valueDate != valuedateBLL.ValueDate.Date)
|
|
{
|
|
valueDate = AdjustQuotationDate(valueDate, "障碍期权");
|
|
}
|
|
|
|
var tradeId = Guid.NewGuid().ToString();
|
|
|
|
using (var mp = new MarketProxy(valueDate, 0.035))
|
|
{
|
|
var vols = QdpVolHelper.GetDefaultVolatility(0.2);
|
|
|
|
mp.SetVolSurface(tradeId, vols);
|
|
|
|
var tdParam = new BarrierOptionTradeParam
|
|
{
|
|
underlyingTickers = new[] { underlyingTicker },
|
|
underlyingInstrumentType = "CommodityFutures",
|
|
strike = 2900.0,
|
|
startDate = valueDate,
|
|
endDate = new DateTime(2024, 2, 5),
|
|
optionType = OptionType.Call,
|
|
exerciseType = "European",
|
|
barrierType = "UpAndOut",
|
|
barrierPrice = 3000.0,
|
|
upperBarrierPrice = 0.0,
|
|
isDiscrete = true,
|
|
initialSpotPrice = 2900.0,
|
|
rebate = 20.0,
|
|
notional = 100.0,
|
|
volSurfaceNames = new[] { tradeId },
|
|
riskFreeRate = 0.035,
|
|
buysell = "Buy",
|
|
exerciseDate = new DateTime(2019, 05, 06),
|
|
rebateType = "AtHit",
|
|
observationDateStr = "2019-04-09,2019-04-16,2019-04-23,2019-04-29,2019-05-06",
|
|
dividendRate = 0.0,
|
|
barrierShift = 0.0,
|
|
participationRate = 1.0,
|
|
principalRate = 0.0,
|
|
isAnnualized = false,
|
|
annualizedFactor = 1.0,
|
|
rebateAnnualizedAtKO = false,
|
|
rebateDayCount = "",
|
|
timeToMaturityDays = 19
|
|
};
|
|
|
|
result = TradeRiskCalcUtil.GetBarrierOptionValue(mp, new OptionCalcParam<BarrierOptionTradeParam>(tdParam)
|
|
{
|
|
spotPrices = new[] { tdParam.initialSpotPrice },
|
|
quadratureFastMode = true
|
|
});
|
|
}
|
|
|
|
Assert.AreEqual(1439.9127764086413, result.Pv, 1E-8, "");
|
|
Assert.AreEqual(5.1646784276044855, result.Delta, 1E-8, "");
|
|
Assert.AreEqual(-0.016520966710231733, result.Gamma, 1E-8, "");
|
|
Assert.AreEqual(-17.501027350181175, result.Vega, 1E-8, "");
|
|
Assert.AreEqual(10.403561845246486, result.Theta, 1E-8, "");
|
|
Assert.AreEqual(0.12190782245161244, result.Rho, 1E-8, "");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ValueBarrierOptionTestV2()
|
|
{
|
|
var valueDate = "2019-04-04";
|
|
var underlyingTicker = "RB00";
|
|
var underlying = new underlying_manager()
|
|
{
|
|
QuotationDate = DateTime.Parse(valueDate),
|
|
UnderlyingCode = underlyingTicker,
|
|
UnderlyingInstrumentType = "CommodityFutures",
|
|
MaturityDate = new DateTime(2019, 5, 6)
|
|
};
|
|
|
|
var userId = Guid.NewGuid().ToString();
|
|
|
|
PS.ResetConfig("{\"SkewMapVolConstruction\":\"false\"}");
|
|
|
|
var trade = new trade()
|
|
{
|
|
TradeType = "障碍期权",
|
|
TradeDate = new DateTime(2019, 4, 4),
|
|
MaturityDate = new DateTime(2019, 5, 6),
|
|
ExerciseDate = new DateTime(2019, 5, 6),
|
|
OptionType = "看涨",
|
|
ExerciseMode = "European",
|
|
BuySell = "Buy",
|
|
Strike = 2900.0,
|
|
Notional = 100,
|
|
|
|
trade_barrier_option = new trade_barrier_option()
|
|
{
|
|
BarrierType = "上升敲出",
|
|
BarrierPrice = 3000.0,
|
|
BarrierShift = 0.0,
|
|
Discrete = "离散",
|
|
Rebate = 20.0,
|
|
KnockInOutStatus = "Monitoring"
|
|
}
|
|
};
|
|
|
|
var parameter = new BarrierOptionParameter()
|
|
{
|
|
ValueDate = new DateTime(2019, 4, 4),
|
|
Volatility = 0.2,
|
|
SpotPrices = new Dictionary<string, double>() { { underlying.UnderlyingCode, 2900.0 } },
|
|
RiskFreeRate = 0.035
|
|
};
|
|
|
|
var result = ValueCalculator.CalculateTradeValue(
|
|
userId,
|
|
trade,
|
|
underlying,
|
|
parameter);
|
|
|
|
Assert.AreEqual(1353.9894515364406, result.Pv, 1E-8, "");
|
|
Assert.AreEqual(5.6636792255631008, result.Delta, 1E-8, "");
|
|
Assert.AreEqual(-0.0058651176004786976, result.Gamma, 1E-8, "");
|
|
Assert.AreEqual(-2.900013280566327, result.Vega, 1E-8, "");
|
|
Assert.AreEqual(4.7292177906094821, result.Theta, 1E-8, "");
|
|
Assert.AreEqual(-0.0035508358797642359, result.Rho, 1E-8, "");
|
|
|
|
//双障碍
|
|
trade = new trade()
|
|
{
|
|
TradeType = "障碍期权",
|
|
TradeDate = new DateTime(2019, 4, 4),
|
|
MaturityDate = new DateTime(2019, 5, 6),
|
|
ExerciseDate = new DateTime(2019, 5, 6),
|
|
OptionType = "看涨",
|
|
ExerciseMode = "European",
|
|
BuySell = "Buy",
|
|
Strike = 130.0,
|
|
Notional = 100,
|
|
|
|
trade_barrier_option = new trade_barrier_option()
|
|
{
|
|
BarrierType = "DoubleTouchOut",
|
|
BarrierPrice = 100.0,
|
|
UpperBarrierPrice = 144.0,
|
|
BarrierShift = 0.0,
|
|
Discrete = "离散",
|
|
Rebate = 20.0,
|
|
KnockInOutStatus = "Monitoring"
|
|
}
|
|
};
|
|
|
|
parameter = new BarrierOptionParameter()
|
|
{
|
|
ValueDate = new DateTime(2019, 4, 4),
|
|
Volatility = 0.2,
|
|
SpotPrices = new Dictionary<string, double>() { { underlying.UnderlyingCode, 120.0 } },
|
|
RiskFreeRate = 0.035
|
|
};
|
|
|
|
result = ValueCalculator.CalculateTradeValue(
|
|
userId,
|
|
trade,
|
|
underlying,
|
|
parameter);
|
|
|
|
Assert.AreEqual(31.52299607431015, result.Pv, 1E-8, "");
|
|
Assert.AreEqual(8.7249908156710632, result.Delta, 1E-8, "");
|
|
Assert.AreEqual(2.989637326322736, result.Gamma, 1E-8, "");
|
|
Assert.AreEqual(7.0000209372153677, result.Vega, 1E-8, "");
|
|
Assert.AreEqual(-3.570910197936211, result.Theta, 1E-8, "");
|
|
Assert.AreEqual(0.00835458118292216, result.Rho, 1E-8, "");
|
|
|
|
//KIKO
|
|
trade = new trade()
|
|
{
|
|
TradeType = "障碍期权",
|
|
TradeDate = new DateTime(2019, 4, 4),
|
|
MaturityDate = new DateTime(2019, 5, 6),
|
|
ExerciseDate = new DateTime(2019, 5, 6),
|
|
OptionType = "看涨",
|
|
ExerciseMode = "European",
|
|
BuySell = "Buy",
|
|
Strike = 145,
|
|
Notional = 100,
|
|
|
|
trade_barrier_option = new trade_barrier_option()
|
|
{
|
|
BarrierType = "KIKO",
|
|
BarrierPrice = 146,
|
|
UpperBarrierPrice = 148,
|
|
BarrierShift = 0.0,
|
|
Discrete = "离散",
|
|
Rebate = 20.0,
|
|
KnockInOutStatus = "Monitoring"
|
|
}
|
|
};
|
|
|
|
parameter = new BarrierOptionParameter()
|
|
{
|
|
ValueDate = new DateTime(2019, 4, 4),
|
|
Volatility = 0.2,
|
|
SpotPrices = new Dictionary<string, double>() { { underlying.UnderlyingCode, 146.9 } },
|
|
RiskFreeRate = 0.035
|
|
};
|
|
|
|
result = ValueCalculator.CalculateTradeValue(
|
|
userId,
|
|
trade,
|
|
underlying,
|
|
parameter);
|
|
|
|
Assert.AreEqual(-414.20440415099324, result.Pv, 1E-8, "");
|
|
Assert.AreEqual(186.12515246873045, result.Delta, 1E-8, "");
|
|
Assert.AreEqual(5.5092195839279157, result.Gamma, 1E-8, "");
|
|
Assert.AreEqual(9.4165577108014986, result.Vega, 1E-8, "");
|
|
Assert.AreEqual(-10.397275659495108, result.Theta, 1E-8, "");
|
|
Assert.AreEqual(0.01632015110478733, result.Rho, 1E-8, "");
|
|
|
|
//自定义观察日
|
|
trade = new trade()
|
|
{
|
|
TradeType = "障碍期权",
|
|
TradeDate = new DateTime(2019, 4, 4),
|
|
MaturityDate = new DateTime(2019, 5, 6),
|
|
ExerciseDate = new DateTime(2019, 5, 6),
|
|
OptionType = "看涨",
|
|
ExerciseMode = "European",
|
|
BuySell = "Buy",
|
|
Strike = 2900.0,
|
|
Notional = 100,
|
|
|
|
trade_barrier_option = new trade_barrier_option()
|
|
{
|
|
BarrierType = "上升敲出",
|
|
BarrierPrice = 3000.0,
|
|
BarrierShift = 0.0,
|
|
Discrete = "离散",
|
|
Rebate = 20.0,
|
|
KnockInOutStatus = "Monitoring",
|
|
ObservationDates = "2019-04-09,2019-04-16,2019-04-23,2019-04-29,2019-05-06"
|
|
}
|
|
};
|
|
|
|
parameter = new BarrierOptionParameter()
|
|
{
|
|
ValueDate = new DateTime(2019, 4, 4),
|
|
Volatility = 0.2,
|
|
SpotPrices = new Dictionary<string, double>() { { underlying.UnderlyingCode, 2900.0 } },
|
|
RiskFreeRate = 0.035
|
|
};
|
|
|
|
result = ValueCalculator.CalculateTradeValue(
|
|
userId,
|
|
trade,
|
|
underlying,
|
|
parameter);
|
|
|
|
Assert.AreEqual(1439.9127764086413, result.Pv, 1E-8, "");
|
|
Assert.AreEqual(5.1646784276044855, result.Delta, 1E-8, "");
|
|
Assert.AreEqual(-0.016520966710231733, result.Gamma, 1E-8, "");
|
|
Assert.AreEqual(-17.501027350181175, result.Vega, 1E-8, "");
|
|
Assert.AreEqual(10.403561845246486, result.Theta, 1E-8, "");
|
|
Assert.AreEqual(0.12190782245161244, result.Rho, 1E-8, "");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ValueAsianOptionTest()
|
|
{
|
|
var valueDate = "2019-04-04";
|
|
var underlyingTicker = "RB00";
|
|
var underlying = new underlying_manager()
|
|
{
|
|
QuotationDate = DateTime.Parse(valueDate),
|
|
UnderlyingCode = underlyingTicker
|
|
};
|
|
|
|
//创建默认市场,并设置波动率曲面
|
|
var marketProxy = new PrebuiltQdpMarketProxy();
|
|
|
|
var tenors = new string[]
|
|
{
|
|
"1D",
|
|
"2M",
|
|
"1Y"
|
|
};
|
|
|
|
var strikes = new double[]
|
|
{
|
|
0.95,
|
|
1.0,
|
|
1.05,
|
|
};
|
|
|
|
var vols = new double[3, 3];
|
|
var singleVols = new List<SingleVol>();
|
|
for (var i = 0; i < vols.GetLength(0); ++i)
|
|
{
|
|
for (var j = 0; j < vols.GetLength(1); ++j)
|
|
{
|
|
vols[i, j] = 0.2;
|
|
singleVols.Add(new SingleVol()
|
|
{
|
|
Strike = strikes[j],
|
|
Expire = tenors[i],
|
|
Vol = 0.2
|
|
});
|
|
}
|
|
}
|
|
|
|
PS.ResetConfig("{\"SkewMapVolConstruction\":\"false\"}");
|
|
|
|
var initParam = new VolSurfaceInitParamsBuilder()
|
|
.SetUnderlying(underlying).SetValueDate(valueDate).Build("MoneynessVol", singleVols);
|
|
new NormalVolSurfaceInitializer().InitializeMarketProxy(marketProxy, initParam);
|
|
|
|
var result = OptionCalculatorV1.ValueAsianOptionTrade(
|
|
marketProxy,
|
|
valueDate,
|
|
underlyingTicker,
|
|
"CommodityFutures",
|
|
2900.0,
|
|
valueDate,
|
|
"2019-05-06",
|
|
"Call",
|
|
"European",
|
|
"ArithmeticAverage",
|
|
"Fixed",
|
|
2900.0,
|
|
100.0,
|
|
underlyingTicker + "_VolSurface",
|
|
0.035,
|
|
"Buy",
|
|
"2019-05-06",
|
|
isAnnualized: false,
|
|
annualizeFactor: 1.0,
|
|
dividendRate: 0.0,
|
|
fixings: "2019-04-01,2850;2019-04-02,2855;2019-04-03,2899",
|
|
averagingPeriodStartDate: "2019-04-01",
|
|
ignoreSkewMap: true);
|
|
|
|
Assert.AreEqual(3164.92251559125, result.Pv, 1E-8, "");
|
|
Assert.AreEqual(44.185302306550511, result.Delta, 1E-8, "");
|
|
Assert.AreEqual(0.38820497138658538, result.Gamma, 1E-8, "");
|
|
Assert.AreEqual(165.84751675402458, result.Vega, 1E-8, "");
|
|
Assert.AreEqual(-294.4723867871885, result.Theta, 1E-8, "");
|
|
Assert.AreEqual(-0.02594092466551956, result.Rho, 1E-8, "");
|
|
|
|
//增强型
|
|
result = OptionCalculatorV1.ValueAsianOptionTrade(
|
|
marketProxy,
|
|
valueDate,
|
|
underlyingTicker,
|
|
"CommodityFutures",
|
|
2900.0,
|
|
valueDate,
|
|
"2019-05-06",
|
|
"Call",
|
|
"European",
|
|
"EnhancedArithmeticAverage",
|
|
"Fixed",
|
|
2900.0,
|
|
100.0,
|
|
underlyingTicker + "_VolSurface",
|
|
0.035,
|
|
"Buy",
|
|
"2019-05-06",
|
|
isAnnualized: false,
|
|
annualizeFactor: 1.0,
|
|
dividendRate: 0.0,
|
|
fixings: "",
|
|
averagingPeriodStartDate: "2019-04-04",
|
|
ignoreSkewMap: true);
|
|
|
|
Assert.AreEqual(4341.7462907276122, result.Pv, 1E-8, "");
|
|
Assert.AreEqual(53.059818666679348, result.Delta, 1E-8, "");
|
|
Assert.AreEqual(476.57855693614692, result.Gamma, 1E-8, "");
|
|
Assert.AreEqual(217.05074145074832, result.Vega, 1E-8, "");
|
|
Assert.AreEqual(-314.50937525089466, result.Theta, 1E-8, "");
|
|
Assert.AreEqual(-0.021944944954884704, result.Rho, 1E-8, "");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ValueAsianOptionTestV2()
|
|
{
|
|
var valueDate = "2019-04-04";
|
|
var underlyingTicker = "RB00";
|
|
var underlying = new underlying_manager()
|
|
{
|
|
QuotationDate = DateTime.Parse(valueDate),
|
|
UnderlyingCode = underlyingTicker,
|
|
UnderlyingInstrumentType = "CommodityFutures",
|
|
MaturityDate = new DateTime(2019, 5, 6)
|
|
};
|
|
|
|
var userId = Guid.NewGuid().ToString();
|
|
|
|
PS.ResetConfig("{\"SkewMapVolConstruction\":\"false\"}");
|
|
|
|
var trade = new trade()
|
|
{
|
|
TradeType = "亚式期权",
|
|
TradeDate = new DateTime(2019, 4, 4),
|
|
MaturityDate = new DateTime(2019, 5, 6),
|
|
ExerciseDate = new DateTime(2019, 5, 6),
|
|
OptionType = "看涨",
|
|
ExerciseMode = "European",
|
|
BuySell = "Buy",
|
|
Strike = 2900.0,
|
|
Notional = 100,
|
|
|
|
trade_asian_option = new trade_asian_option()
|
|
{
|
|
PayoffType = "ArithmeticAverage",
|
|
StrikeType = "Fixed",
|
|
AveragingPeriodStartDate = new DateTime(2019, 4, 1)
|
|
}
|
|
};
|
|
|
|
var parameter = new AsianOptionParameter()
|
|
{
|
|
ValueDate = new DateTime(2019, 4, 4),
|
|
Volatility = 0.2,
|
|
SpotPrices = new Dictionary<string, double>() { { underlying.UnderlyingCode, 2900.0 } },
|
|
RiskFreeRate = 0.035,
|
|
Fixings = "2019-04-01,2850;2019-04-02,2855;2019-04-03,2899"
|
|
};
|
|
|
|
var result = ValueCalculator.CalculateTradeValue(
|
|
userId,
|
|
trade,
|
|
underlying,
|
|
parameter);
|
|
|
|
Assert.AreEqual(3164.92251559125, result.Pv, 1E-8, "");
|
|
Assert.AreEqual(44.185302306550511, result.Delta, 1E-8, "");
|
|
Assert.AreEqual(0.38820497138658538, result.Gamma, 1E-8, "");
|
|
Assert.AreEqual(165.84751675402458, result.Vega, 1E-8, "");
|
|
Assert.AreEqual(-294.4723867871885, result.Theta, 1E-8, "");
|
|
Assert.AreEqual(-0.02594092466551956, result.Rho, 1E-8, "");
|
|
|
|
//增强型
|
|
trade = new trade()
|
|
{
|
|
TradeType = "亚式期权",
|
|
TradeDate = new DateTime(2019, 4, 4),
|
|
MaturityDate = new DateTime(2019, 5, 6),
|
|
ExerciseDate = new DateTime(2019, 5, 6),
|
|
OptionType = "看涨",
|
|
ExerciseMode = "European",
|
|
BuySell = "Buy",
|
|
Strike = 2900.0,
|
|
Notional = 100,
|
|
|
|
trade_asian_option = new trade_asian_option()
|
|
{
|
|
PayoffType = "EnhancedArithmeticAverage",
|
|
StrikeType = "Fixed",
|
|
AveragingPeriodStartDate = new DateTime(2019, 4, 4)
|
|
}
|
|
};
|
|
|
|
parameter = new AsianOptionParameter()
|
|
{
|
|
ValueDate = new DateTime(2019, 4, 4),
|
|
Volatility = 0.2,
|
|
SpotPrices = new Dictionary<string, double>() { { underlying.UnderlyingCode, 2900.0 } },
|
|
RiskFreeRate = 0.035,
|
|
Fixings = ""
|
|
};
|
|
|
|
result = ValueCalculator.CalculateTradeValue(
|
|
userId,
|
|
trade,
|
|
underlying,
|
|
parameter);
|
|
|
|
Assert.AreEqual(4341.7462907276122, result.Pv, 1E-8, "");
|
|
Assert.AreEqual(53.059818666679348, result.Delta, 1E-8, "");
|
|
Assert.AreEqual(476.57855693614692, result.Gamma, 1E-8, "");
|
|
Assert.AreEqual(217.05074145074832, result.Vega, 1E-8, "");
|
|
Assert.AreEqual(-314.50937525089466, result.Theta, 1E-8, "");
|
|
Assert.AreEqual(-0.021944944954884704, result.Rho, 1E-8, "");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ValueBinaryOptionTest()
|
|
{
|
|
TradeValueResult result;
|
|
|
|
var valueDate = new DateTime(2019, 04, 04);
|
|
var underlyingTicker = "RB00";
|
|
var vol = QdpVolHelper.GetDefaultVolatility(0.2);
|
|
|
|
var valueDateStr = "2019-04-04";
|
|
var underlying = new underlying_manager()
|
|
{
|
|
QuotationDate = DateTime.Parse(valueDateStr),
|
|
UnderlyingCode = underlyingTicker
|
|
};
|
|
|
|
//创建默认市场,并设置波动率曲面
|
|
var marketProxy = new PrebuiltQdpMarketProxy();
|
|
|
|
var tenors = new string[]
|
|
{
|
|
"1D",
|
|
"2M",
|
|
"1Y"
|
|
};
|
|
|
|
var strikes = new double[]
|
|
{
|
|
0.95,
|
|
1.0,
|
|
1.05,
|
|
};
|
|
|
|
var vols = new double[3, 3];
|
|
var singleVols = new List<SingleVol>();
|
|
for (var i = 0; i < vols.GetLength(0); ++i)
|
|
{
|
|
for (var j = 0; j < vols.GetLength(1); ++j)
|
|
{
|
|
vols[i, j] = 0.2;
|
|
singleVols.Add(new SingleVol()
|
|
{
|
|
Strike = strikes[j],
|
|
Expire = tenors[i],
|
|
Vol = 0.2
|
|
});
|
|
}
|
|
}
|
|
|
|
PS.ResetConfig("{\"SkewMapVolConstruction\":\"false\"}");
|
|
|
|
var initParam = new VolSurfaceInitParamsBuilder()
|
|
.SetUnderlying(underlying).SetValueDate(valueDate).Build("MoneynessVol", singleVols);
|
|
new NormalVolSurfaceInitializer().InitializeMarketProxy(marketProxy, initParam);
|
|
|
|
result = OptionCalculatorV1.ValueBinaryOptionTrade(
|
|
marketProxy,
|
|
valueDateStr,
|
|
underlyingTicker,
|
|
"CommodityFutures",
|
|
2900.0,
|
|
valueDateStr,
|
|
"2019-05-06",
|
|
"Call",
|
|
"European",
|
|
"CashOrNothing", //payoffType
|
|
200.0,
|
|
2900.0,
|
|
100.0,
|
|
underlyingTicker + "_VolSurface",
|
|
0.035,
|
|
"Buy",
|
|
"2019-05-06",
|
|
dividendRate: 0.0,
|
|
binaryRebateType: "AtHit",
|
|
binaryOptionReplicationStrategy: "None",
|
|
replicationShiftSize: 0.0,
|
|
ignoreSkewMap: true,
|
|
participationRate: 1.0,
|
|
principalRate: 0.0,
|
|
isAnnualized: false,
|
|
annualizeFactor: 1.0);
|
|
|
|
Assert.AreEqual(9743.6044193724756, result.Pv, 1E-8, "");
|
|
Assert.AreEqual(47.892520332243294, result.Delta, 1E-8, "");
|
|
Assert.AreEqual(-0.0082573751569725573, result.Gamma, 1E-8, "");
|
|
Assert.AreEqual(-11.384297440145019, result.Vega, 1E-8, "");
|
|
Assert.AreEqual(7.1637717615885776, result.Theta, 1E-8, "");
|
|
Assert.AreEqual(-0.079862336903534017, result.Rho, 1E-8, "");
|
|
|
|
var tradeId = Guid.NewGuid().ToString();
|
|
|
|
using var mp = new MarketProxy(valueDate, 0.035);
|
|
mp.SetVolSurface(tradeId, vol);
|
|
|
|
var tdParam = new BinaryOptionTradeParam
|
|
{
|
|
underlyingTickers = new[] { underlyingTicker },
|
|
underlyingInstrumentType = "CommodityFutures",
|
|
strike = 2900.0,
|
|
startDate = valueDate,
|
|
endDate = new DateTime(2019, 05, 06),
|
|
optionType = OptionType.Call,
|
|
exerciseType = "European",
|
|
payoffType = "CashOrNothing", //payoffType
|
|
cashOrNothingAmount = 200.0,
|
|
initialSpotPrice = 2900.0,
|
|
notional = 100.0,
|
|
volSurfaceNames = new[] { tradeId },
|
|
riskFreeRate = 0.035,
|
|
buysell = "Buy",
|
|
exerciseDate = new DateTime(2019, 05, 06),
|
|
dividendRate = 0.0,
|
|
binaryRebateType = "AtHit",
|
|
binaryOptionReplicationStrategy = "None",
|
|
replicationShiftSize = 0.0,
|
|
participationRate = 1.0,
|
|
principalRate = 0.0,
|
|
isAnnualized = false,
|
|
annualizedFactor = 1.0
|
|
};
|
|
|
|
result = TradeRiskCalcUtil.GetBinaryOptionValue(mp, new OptionCalcParam<BinaryOptionTradeParam>(tdParam)
|
|
{
|
|
spotPrices = new[] { 2900d }
|
|
});
|
|
|
|
Assert.AreEqual(9743.6044193724756, result.Pv, 1E-8, "");
|
|
Assert.AreEqual(47.892520332243294, result.Delta, 1E-8, "");
|
|
Assert.AreEqual(-0.0082573751569725573, result.Gamma, 1E-8, "");
|
|
Assert.AreEqual(-11.384297440145019, result.Vega, 1E-8, "");
|
|
Assert.AreEqual(7.1637717615885776, result.Theta, 1E-8, "");
|
|
Assert.AreEqual(-0.079862336903534017, result.Rho, 1E-8, "");
|
|
|
|
tdParam = new BinaryOptionTradeParam
|
|
{
|
|
underlyingTickers = new[] { underlyingTicker },
|
|
underlyingInstrumentType = "CommodityFutures",
|
|
strike = 2900.0,
|
|
startDate = valueDate,
|
|
endDate = new DateTime(2019, 05, 06),
|
|
optionType = OptionType.Call,
|
|
exerciseType = "European",
|
|
payoffType = "CashOrNothing", //payoffType
|
|
cashOrNothingAmount = 200.0,
|
|
initialSpotPrice = 2900.0,
|
|
notional = 100.0,
|
|
volSurfaceNames = new[] { tradeId },
|
|
riskFreeRate = 0.035,
|
|
buysell = "Buy",
|
|
exerciseDate = new DateTime(2019, 05, 06),
|
|
dividendRate = 0.0,
|
|
binaryRebateType = "AtHit",
|
|
binaryOptionReplicationStrategy = "Down",
|
|
replicationShiftSize = 0.0,
|
|
participationRate = 1.0,
|
|
principalRate = 0.0,
|
|
isAnnualized = false,
|
|
annualizedFactor = 1.0
|
|
};
|
|
|
|
result = TradeRiskCalcUtil.GetBinaryOptionValue(mp, new OptionCalcParam<BinaryOptionTradeParam>(tdParam)
|
|
{
|
|
spotPrices = new[] { 2900d },
|
|
engineName = "AnalyticalBinaryEuropeanOptionReplicationEngine"
|
|
});
|
|
|
|
Assert.AreEqual(10439.754311151559, result.Pv, 1E-8, "");
|
|
Assert.AreEqual(47.767642553136, result.Delta, 1E-8, "");
|
|
Assert.AreEqual(-0.033387487778311659, result.Gamma, 1E-8, "");
|
|
Assert.AreEqual(-46.042248515481454, result.Vega, 1E-8, "");
|
|
Assert.AreEqual(0, result.Theta, 1E-8, "");
|
|
Assert.AreEqual(-0.085568249705202437, result.Rho, 1E-8, "");
|
|
|
|
//美式二元
|
|
|
|
tdParam = new BinaryOptionTradeParam
|
|
{
|
|
underlyingTickers = new[] { underlyingTicker },
|
|
underlyingInstrumentType = "CommodityFutures",
|
|
strike = 950.0,
|
|
startDate = valueDate,
|
|
endDate = new DateTime(2019, 05, 06),
|
|
optionType = OptionType.Call,
|
|
exerciseType = "American",
|
|
payoffType = "DoubleOneTouch", //payoffType
|
|
cashOrNothingAmount = 200.0,
|
|
initialSpotPrice = 990.0,
|
|
notional = 100.0,
|
|
volSurfaceNames = new[] { tradeId },
|
|
riskFreeRate = 0.035,
|
|
buysell = "Buy",
|
|
exerciseDate = new DateTime(2019, 05, 06),
|
|
upperBarrier = 1100.0,
|
|
dividendRate = 0.0,
|
|
binaryRebateType = "AtHit",
|
|
binaryOptionReplicationStrategy = "Down",
|
|
replicationShiftSize = 0.0,
|
|
participationRate = 1.0,
|
|
principalRate = 0.0,
|
|
isAnnualized = false,
|
|
annualizedFactor = 1.0
|
|
};
|
|
|
|
result = TradeRiskCalcUtil.GetBinaryOptionValue(mp, new OptionCalcParam<BinaryOptionTradeParam>(tdParam)
|
|
{
|
|
spotPrices = new[] { 990.0 }
|
|
});
|
|
|
|
Assert.AreEqual(10835.184736140083, result.Pv, 1E-8, "");
|
|
Assert.AreEqual(-165.90143058310787, result.Delta, 1E-8, "");
|
|
Assert.AreEqual(4.3446263953228481, result.Gamma, 1E-8, "");
|
|
Assert.AreEqual(696.36722659533916, result.Vega, 1E-8, "");
|
|
Assert.AreEqual(-354.58732062354829, result.Theta, 1E-8, "");
|
|
Assert.AreEqual(-0.040437084736549875, result.Rho, 1E-8, "");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ValueBinaryOptionTestV2()
|
|
{
|
|
var valueDate = "2019-04-04";
|
|
var underlyingTicker = "RB00";
|
|
var underlying = new underlying_manager()
|
|
{
|
|
QuotationDate = DateTime.Parse(valueDate),
|
|
UnderlyingCode = underlyingTicker,
|
|
UnderlyingInstrumentType = "CommodityFutures",
|
|
MaturityDate = new DateTime(2019, 5, 6)
|
|
};
|
|
|
|
var userId = Guid.NewGuid().ToString();
|
|
|
|
PS.ResetConfig("{\"SkewMapVolConstruction\":\"false\"}");
|
|
|
|
var trade = new trade()
|
|
{
|
|
TradeType = "二元期权",
|
|
TradeDate = new DateTime(2019, 4, 4),
|
|
MaturityDate = new DateTime(2019, 5, 6),
|
|
ExerciseDate = new DateTime(2019, 5, 6),
|
|
OptionType = "看涨",
|
|
ExerciseMode = "European",
|
|
BuySell = "Buy",
|
|
Strike = 2900.0,
|
|
Notional = 100,
|
|
|
|
trade_binary_option = new trade_binary_option()
|
|
{
|
|
PayoffType = "CashOrNothing",
|
|
CashOrNothingAmount = 200,
|
|
RebateType = "AtHit"
|
|
}
|
|
};
|
|
|
|
var parameter = new BinaryOptionParameter()
|
|
{
|
|
ValueDate = new DateTime(2019, 4, 4),
|
|
Volatility = 0.2,
|
|
SpotPrices = new Dictionary<string, double>() { { underlying.UnderlyingCode, 2900.0 } },
|
|
RiskFreeRate = 0.035
|
|
};
|
|
|
|
var result = ValueCalculator.CalculateTradeValue(
|
|
userId,
|
|
trade,
|
|
underlying,
|
|
parameter);
|
|
|
|
Assert.AreEqual(9743.6044193724756, result.Pv, 1E-8, "");
|
|
Assert.AreEqual(47.892520332243294, result.Delta, 1E-8, "");
|
|
Assert.AreEqual(-0.0082573751569725573, result.Gamma, 1E-8, "");
|
|
Assert.AreEqual(-11.384297440145019, result.Vega, 1E-8, "");
|
|
Assert.AreEqual(7.1637717615885776, result.Theta, 1E-8, "");
|
|
Assert.AreEqual(-0.079862336903534017, result.Rho, 1E-8, "");
|
|
|
|
parameter = new BinaryOptionParameter()
|
|
{
|
|
ValueDate = new DateTime(2019, 4, 4),
|
|
Volatility = 0.2,
|
|
SpotPrices = new Dictionary<string, double>() { { underlying.UnderlyingCode, 2900.0 } },
|
|
RiskFreeRate = 0.035,
|
|
EngineName = "AnalyticalBinaryEuropeanOptionReplicationEngine",
|
|
BinaryReplicationStrategy = "Down",
|
|
ReplicationShiftSize = 0.0
|
|
};
|
|
|
|
result = ValueCalculator.CalculateTradeValue(
|
|
userId,
|
|
trade,
|
|
underlying,
|
|
parameter);
|
|
|
|
Assert.AreEqual(10439.754311151559, result.Pv, 1E-8, "");
|
|
Assert.AreEqual(47.767642553136, result.Delta, 1E-8, "");
|
|
Assert.AreEqual(-0.033387487778311659, result.Gamma, 1E-8, "");
|
|
Assert.AreEqual(-46.042248515481454, result.Vega, 1E-8, "");
|
|
Assert.AreEqual(0, result.Theta, 1E-8, "");
|
|
Assert.AreEqual(-0.085568249705202437, result.Rho, 1E-8, "");
|
|
|
|
//美式二元
|
|
trade = new trade()
|
|
{
|
|
TradeType = "二元期权",
|
|
TradeDate = new DateTime(2019, 4, 4),
|
|
MaturityDate = new DateTime(2019, 5, 6),
|
|
ExerciseDate = new DateTime(2019, 5, 6),
|
|
OptionType = "看涨",
|
|
ExerciseMode = "American",
|
|
BuySell = "Buy",
|
|
Strike = 950,
|
|
Notional = 100,
|
|
|
|
trade_binary_option = new trade_binary_option()
|
|
{
|
|
PayoffType = "DoubleOneTouch",
|
|
CashOrNothingAmount = 200,
|
|
RebateType = "AtHit",
|
|
UpperBarrier = 1100.0,
|
|
MonitorType = "连续",
|
|
}
|
|
};
|
|
|
|
parameter = new BinaryOptionParameter()
|
|
{
|
|
ValueDate = new DateTime(2019, 4, 4),
|
|
Volatility = 0.2,
|
|
SpotPrices = new Dictionary<string, double>() { { underlying.UnderlyingCode, 990.0 } },
|
|
RiskFreeRate = 0.035
|
|
};
|
|
|
|
result = ValueCalculator.CalculateTradeValue(
|
|
userId,
|
|
trade,
|
|
underlying,
|
|
parameter);
|
|
|
|
Assert.AreEqual(10835.184736140083, result.Pv, 1E-8, "");
|
|
Assert.AreEqual(-165.90143058310787, result.Delta, 1E-8, "");
|
|
Assert.AreEqual(4.3446263953228481, result.Gamma, 1E-8, "");
|
|
Assert.AreEqual(696.36722659533916, result.Vega, 1E-8, "");
|
|
Assert.AreEqual(-354.58732062354829, result.Theta, 1E-8, "");
|
|
Assert.AreEqual(-0.040437084736549875, result.Rho, 1E-8, "");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ValueRainbowOptionTest()
|
|
{
|
|
var valueDate = "2019-04-04";
|
|
var underlyingTicker1 = "RB00";
|
|
var underlying1 = new underlying_manager()
|
|
{
|
|
QuotationDate = DateTime.Parse(valueDate),
|
|
UnderlyingCode = underlyingTicker1
|
|
};
|
|
|
|
var underlyingTicker2 = "RB01";
|
|
var underlying2 = new underlying_manager()
|
|
{
|
|
QuotationDate = DateTime.Parse(valueDate),
|
|
UnderlyingCode = underlyingTicker2
|
|
};
|
|
|
|
//创建默认市场,并设置波动率曲面
|
|
var marketProxy = new PrebuiltQdpMarketProxy();
|
|
|
|
var tenors = new string[]
|
|
{
|
|
"1D",
|
|
"2M",
|
|
"1Y"
|
|
};
|
|
|
|
var strikes = new double[]
|
|
{
|
|
0.95,
|
|
1.0,
|
|
1.05,
|
|
};
|
|
|
|
var vols = new double[3, 3];
|
|
var singleVols = new List<SingleVol>();
|
|
for (var i = 0; i < vols.GetLength(0); ++i)
|
|
{
|
|
for (var j = 0; j < vols.GetLength(1); ++j)
|
|
{
|
|
vols[i, j] = 0.2;
|
|
singleVols.Add(new SingleVol()
|
|
{
|
|
Strike = strikes[j],
|
|
Expire = tenors[i],
|
|
Vol = 0.2
|
|
});
|
|
}
|
|
}
|
|
|
|
PS.ResetConfig("{\"SkewMapVolConstruction\":\"false\"}");
|
|
|
|
var initParam = new VolSurfaceInitParamsBuilder()
|
|
.SetUnderlying(underlying1).SetValueDate(valueDate).Build("MoneynessVol", singleVols);
|
|
new NormalVolSurfaceInitializer().InitializeMarketProxy(marketProxy, initParam);
|
|
|
|
var singleVols2 = new List<SingleVol>();
|
|
for (var i = 0; i < vols.GetLength(0); ++i)
|
|
{
|
|
for (var j = 0; j < vols.GetLength(1); ++j)
|
|
{
|
|
vols[i, j] = 0.25;
|
|
singleVols2.Add(new SingleVol()
|
|
{
|
|
Strike = strikes[j],
|
|
Expire = tenors[i],
|
|
Vol = 0.25
|
|
});
|
|
}
|
|
}
|
|
|
|
var initParam2 = new VolSurfaceInitParamsBuilder()
|
|
.SetUnderlying(underlying2).SetValueDate(valueDate).Build("MoneynessVol", singleVols2);
|
|
new NormalVolSurfaceInitializer().InitializeMarketProxy(marketProxy, initParam2);
|
|
|
|
PS.ResetConfig("{\"SkewMapVolConstruction\":\"false\"}");
|
|
|
|
var result = OptionCalculatorV1.ValueRainbowOptionTrade(
|
|
marketProxy,
|
|
valueDate,
|
|
"CommodityFutures",
|
|
new string[] { underlyingTicker1, underlyingTicker2 },
|
|
new string[] { underlyingTicker1 + "_VolSurface", underlyingTicker2 + "_VolSurface" },
|
|
new double[] { 2900.0, 2950.0 },
|
|
valueDate,
|
|
"2019-05-06",
|
|
"Call",
|
|
"European",
|
|
"BestOfAssetsOrCash", //rainbowType
|
|
150.0, // cashAmount
|
|
new double[] { 2900.0, 2950.0 },
|
|
100.0,
|
|
0.035,
|
|
0.85, //correlation
|
|
"Buy",
|
|
"2019-05-06",
|
|
dividendRate: 0.0,
|
|
ignoreSkewMap: true,
|
|
participationRate: 1.0,
|
|
principalRate: 0.0,
|
|
isAnnualized: false,
|
|
annualizeFactor: 1.0);
|
|
|
|
Assert.AreEqual(3010.0301061974196, result.Pv, 1E-8, "");
|
|
Assert.AreEqual(0.15599592643411597, result.Delta, 1E-8, "");
|
|
Assert.AreEqual(0.0022282620193436742, result.Gamma, 1E-8, "");
|
|
Assert.AreEqual(0.959972953476381, result.Vega, 1E-8, "");
|
|
Assert.AreEqual(0, result.Theta, 1E-8, "");
|
|
Assert.AreEqual(-0.024672276803812565, result.Rho, 1E-8, "");
|
|
Assert.AreEqual(0.50413404551363783, result.Delta2.Value, 1E-8, "");
|
|
Assert.AreEqual(0.0027284841053187847, result.Gamma2.Value, 1E-8, "");
|
|
Assert.AreEqual(2.9983269828480843, result.Vega2.Value, 1E-8, "");
|
|
Assert.AreEqual(-0.0016143530956469476, result.CrossGamma, 1E-8, "");
|
|
Assert.AreEqual(-0.062994161204528609, result.CrossVogga, 1E-8, "");
|
|
Assert.AreEqual(-0.56289229188678291, result.CorrVega, 1E-8, "");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ValueRainbowOptionTestV2()
|
|
{
|
|
var valueDate = "2019-04-04";
|
|
var underlyingTicker1 = "RB00";
|
|
var underlying1 = new underlying_manager()
|
|
{
|
|
QuotationDate = DateTime.Parse(valueDate),
|
|
UnderlyingCode = underlyingTicker1,
|
|
UnderlyingInstrumentType = "CommodityFutures",
|
|
MaturityDate = new DateTime(2019, 5, 6)
|
|
};
|
|
|
|
var underlyingTicker2 = "RB01";
|
|
var underlying2 = new underlying_manager()
|
|
{
|
|
QuotationDate = DateTime.Parse(valueDate),
|
|
UnderlyingCode = underlyingTicker2,
|
|
UnderlyingInstrumentType = "CommodityFutures",
|
|
MaturityDate = new DateTime(2019, 5, 6)
|
|
};
|
|
|
|
var userId = Guid.NewGuid().ToString();
|
|
|
|
PS.ResetConfig("{\"SkewMapVolConstruction\":\"false\"}");
|
|
|
|
var trade = new trade()
|
|
{
|
|
TradeType = "彩虹期权",
|
|
TradeDate = new DateTime(2019, 4, 4),
|
|
MaturityDate = new DateTime(2019, 5, 6),
|
|
ExerciseDate = new DateTime(2019, 5, 6),
|
|
OptionType = "看涨",
|
|
ExerciseMode = "European",
|
|
BuySell = "Buy",
|
|
Strike = 2900.0,
|
|
Notional = 100,
|
|
|
|
trade_rainbow_option = new trade_rainbow_option()
|
|
{
|
|
RainbowType = "BestOfAssetsOrCash",
|
|
CashAmount = 150.0,
|
|
UnderlyingAssetCode = underlyingTicker1,
|
|
UnderlyingAssetCode2 = underlyingTicker2,
|
|
Strike2 = 2950.0
|
|
}
|
|
};
|
|
|
|
var parameter = new RainbowOptionParameter()
|
|
{
|
|
ValueDate = new DateTime(2019, 4, 4),
|
|
Volatility = 0.2,
|
|
Volatility2 = 0.25,
|
|
SpotPrices = new Dictionary<string, double>() { { underlying1.UnderlyingCode, 2900.0 }, { underlying2.UnderlyingCode, 2950.0 } },
|
|
Correlation = 0.85,
|
|
RiskFreeRate = 0.035
|
|
};
|
|
|
|
var result = ValueCalculator.CalculateTradeValue(
|
|
userId,
|
|
trade,
|
|
underlying1,
|
|
parameter);
|
|
|
|
Assert.AreEqual(3010.0301061974196, result.Pv, 1E-8, "");
|
|
Assert.AreEqual(0.15599592643411597, result.Delta, 1E-8, "");
|
|
Assert.AreEqual(0.0022282620193436742, result.Gamma, 1E-8, "");
|
|
Assert.AreEqual(0.959972953476381, result.Vega, 1E-8, "");
|
|
Assert.AreEqual(0, result.Theta, 1E-8, "");
|
|
Assert.AreEqual(-0.024672276803812565, result.Rho, 1E-8, "");
|
|
Assert.AreEqual(0.50413404551363783, result.Delta2.Value, 1E-8, "");
|
|
Assert.AreEqual(0.0027284841053187847, result.Gamma2.Value, 1E-8, "");
|
|
Assert.AreEqual(2.9983269828480843, result.Vega2.Value, 1E-8, "");
|
|
Assert.AreEqual(-0.0016143530956469476, result.CrossGamma, 1E-8, "");
|
|
Assert.AreEqual(-0.062994161204528609, result.CrossVogga, 1E-8, "");
|
|
Assert.AreEqual(-0.56289229188678291, result.CorrVega, 1E-8, "");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ValueSyntheticNormalSpreadOptionTest()
|
|
{
|
|
var valueDate = "2019-04-04";
|
|
var underlyingTicker = "RB00";
|
|
var underlying = new underlying_manager()
|
|
{
|
|
QuotationDate = DateTime.Parse(valueDate),
|
|
UnderlyingCode = underlyingTicker
|
|
};
|
|
|
|
//创建默认市场,并设置波动率曲面
|
|
var marketProxy = new PrebuiltQdpMarketProxy();
|
|
|
|
var tenors = new string[]
|
|
{
|
|
"1D",
|
|
"2M",
|
|
"1Y"
|
|
};
|
|
|
|
var strikes = new double[]
|
|
{
|
|
0.95,
|
|
1.0,
|
|
1.05,
|
|
};
|
|
|
|
var vols = new double[3, 3];
|
|
var singleVols = new List<SingleVol>();
|
|
for (var i = 0; i < vols.GetLength(0); ++i)
|
|
{
|
|
for (var j = 0; j < vols.GetLength(1); ++j)
|
|
{
|
|
vols[i, j] = 0.2;
|
|
singleVols.Add(new SingleVol()
|
|
{
|
|
Strike = strikes[j],
|
|
Expire = tenors[i],
|
|
Vol = 0.2
|
|
});
|
|
}
|
|
}
|
|
|
|
PS.ResetConfig("{\"SkewMapVolConstruction\":\"false\"}");
|
|
|
|
var initParam = new VolSurfaceInitParamsBuilder()
|
|
.SetUnderlying(underlying).SetValueDate(valueDate).Build("MoneynessVol", singleVols);
|
|
new NormalVolSurfaceInitializer().InitializeMarketProxy(marketProxy, initParam);
|
|
|
|
//计算
|
|
var result = OptionCalculatorV1.ValueSyntheticNormalSpreadOptionTrade(
|
|
marketProxy,
|
|
valueDate,
|
|
underlyingTicker,
|
|
"CommodityFutures",
|
|
2900.0,
|
|
valueDate,
|
|
"2019-05-06",
|
|
"Call",
|
|
"European",
|
|
2900.0,
|
|
100.0,
|
|
underlyingTicker + "_VolSurface",
|
|
0.035,
|
|
null,
|
|
"Buy",
|
|
"2019-05-06",
|
|
dividendRate: 0,
|
|
ignoreSkewMap: true,
|
|
participationRate: 1.0,
|
|
principalRate: 0.0,
|
|
isAnnualized: false,
|
|
annualizeFactor: 1.0);
|
|
|
|
Assert.AreEqual(2.2777928184782521, result.Pv, 1E-8, "");
|
|
Assert.AreEqual(53.321595022577874, result.Delta, 1E-8, "");
|
|
Assert.AreEqual(692.96641664031711, result.Gamma, 1E-8, "");
|
|
Assert.AreEqual(0.11388964092384768, result.Vega, 1E-8, "");
|
|
Assert.AreEqual(-0.057356519934982586, result.Theta, 1E-8, "");
|
|
Assert.AreEqual(-1.8669667777526655E-05, result.Rho, 1E-8, "");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ValueSyntheticNormalSpreadOptionTestV2()
|
|
{
|
|
var valueDate = "2019-04-04";
|
|
var underlyingTicker = "RB00";
|
|
var underlying = new underlying_manager()
|
|
{
|
|
QuotationDate = DateTime.Parse(valueDate),
|
|
UnderlyingCode = underlyingTicker,
|
|
UnderlyingInstrumentType = "CommodityFutures",
|
|
MaturityDate = new DateTime(2019, 5, 6)
|
|
};
|
|
|
|
var userId = Guid.NewGuid().ToString();
|
|
|
|
PS.ResetConfig("{\"SkewMapVolConstruction\":\"false\"}");
|
|
|
|
var trade = new trade()
|
|
{
|
|
TradeType = "合成价差期权",
|
|
TradeDate = new DateTime(2019, 4, 4),
|
|
MaturityDate = new DateTime(2019, 5, 6),
|
|
ExerciseDate = new DateTime(2019, 5, 6),
|
|
OptionType = "看涨",
|
|
ExerciseMode = "European",
|
|
BuySell = "Buy",
|
|
Strike = 2900,
|
|
Notional = 100
|
|
};
|
|
|
|
var parameter = new SyntheticNormalSpreadOptionParameter()
|
|
{
|
|
ValueDate = new DateTime(2019, 4, 4),
|
|
Volatility = 0.2,
|
|
SpotPrices = new Dictionary<string, double>() { { underlying.UnderlyingCode, 2900 } },
|
|
RiskFreeRate = 0.035
|
|
};
|
|
|
|
var result = ValueCalculator.CalculateTradeValue(
|
|
userId,
|
|
trade,
|
|
underlying,
|
|
parameter);
|
|
|
|
Assert.AreEqual(2.2777928184782521, result.Pv, 1E-8, "");
|
|
Assert.AreEqual(53.321595022577874, result.Delta, 1E-8, "");
|
|
Assert.AreEqual(692.96641664031711, result.Gamma, 1E-8, "");
|
|
Assert.AreEqual(0.11388964092384768, result.Vega, 1E-8, "");
|
|
Assert.AreEqual(-0.057356519934982586, result.Theta, 1E-8, "");
|
|
Assert.AreEqual(-1.8669667777526655E-05, result.Rho, 1E-8, "");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ValueAutocallTest()
|
|
{
|
|
var valueDate = "2019-07-30";
|
|
var underlyingTicker = "RB00";
|
|
var underlying = new underlying_manager()
|
|
{
|
|
QuotationDate = DateTime.Parse(valueDate),
|
|
UnderlyingCode = underlyingTicker
|
|
};
|
|
|
|
//创建默认市场,并设置波动率曲面
|
|
var marketProxy = new PrebuiltQdpMarketProxy();
|
|
|
|
var tenors = new string[]
|
|
{
|
|
"1D",
|
|
"2M",
|
|
"1Y"
|
|
};
|
|
|
|
var strikes = new double[]
|
|
{
|
|
0.95,
|
|
1.0,
|
|
1.05,
|
|
};
|
|
|
|
var vols = new double[3, 3];
|
|
var singleVols = new List<SingleVol>();
|
|
for (var i = 0; i < vols.GetLength(0); ++i)
|
|
{
|
|
for (var j = 0; j < vols.GetLength(1); ++j)
|
|
{
|
|
vols[i, j] = 0.35;
|
|
singleVols.Add(new SingleVol()
|
|
{
|
|
Strike = strikes[j],
|
|
Expire = tenors[i],
|
|
Vol = 0.35
|
|
});
|
|
}
|
|
}
|
|
|
|
PS.ResetConfig("{\"SkewMapVolConstruction\":\"false\"}");
|
|
|
|
var initParam = new VolSurfaceInitParamsBuilder()
|
|
.SetUnderlying(underlying).SetValueDate(valueDate).Build("MoneynessVol", singleVols);
|
|
new NormalVolSurfaceInitializer().InitializeMarketProxy(marketProxy, initParam);
|
|
|
|
//计算
|
|
var result = OptionCalculatorV1.ValueAutocallTrade(
|
|
marketProxy: marketProxy,
|
|
valueDate: valueDate,
|
|
underlyingCode: underlyingTicker,
|
|
underlyingInstrumentType: "CommodityFutures",
|
|
startDate: valueDate,
|
|
endDate: "2019-09-08",
|
|
callPut: "Call",
|
|
koBarrier: 100.0,
|
|
kiBarrier: 80.0,
|
|
couponBarrier: 80.0,
|
|
koObservationDateStr: "2019-08-30,2019-09-27,2019-10-31,2019-11-29,2019-12-27,2020-01-31",
|
|
observationDateStr: null,
|
|
coupon: 0.12,
|
|
couponPayAtMaturity: false,
|
|
includeCouponAfterKI: true,
|
|
isFixedCoupon: false,
|
|
annualizedOptionPayoff: false,
|
|
participationRate: 0.75,
|
|
principalRate: 0.0,
|
|
spotPrice: 100.0,
|
|
notional: 12.0,
|
|
volSurfaceName: underlyingTicker + "_VolSurface",
|
|
riskFreeRate: 0.035,
|
|
buySell: "Buy",
|
|
exerciseDate: "2020-02-01",
|
|
annualizeFactor: 1.0,
|
|
couponDayCount: "",
|
|
strike: 100.0,
|
|
spreadStrike: 0.0,
|
|
happenedObservations: null,
|
|
initialSpotPrice: 100.0);
|
|
|
|
Assert.AreEqual(8.5343, result.Pv, 0.01);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ValueAsianSyntheticNormalSpreadOptionTest()
|
|
{
|
|
var valueDate = "2019-04-04";
|
|
var underlyingTicker = "RB00";
|
|
var underlying = new underlying_manager()
|
|
{
|
|
QuotationDate = DateTime.Parse(valueDate),
|
|
UnderlyingCode = underlyingTicker
|
|
};
|
|
|
|
//创建默认市场,并设置波动率曲面
|
|
var marketProxy = new PrebuiltQdpMarketProxy();
|
|
|
|
var tenors = new string[]
|
|
{
|
|
"1D",
|
|
"2M",
|
|
"1Y"
|
|
};
|
|
|
|
var strikes = new double[]
|
|
{
|
|
0.95,
|
|
1.0,
|
|
1.05,
|
|
};
|
|
|
|
var vols = new double[3, 3];
|
|
var singleVols = new List<SingleVol>();
|
|
for (var i = 0; i < vols.GetLength(0); ++i)
|
|
{
|
|
for (var j = 0; j < vols.GetLength(1); ++j)
|
|
{
|
|
vols[i, j] = 0.3;
|
|
singleVols.Add(new SingleVol()
|
|
{
|
|
Strike = strikes[j],
|
|
Expire = tenors[i],
|
|
Vol = 0.3
|
|
});
|
|
}
|
|
}
|
|
|
|
PS.ResetConfig("{\"SkewMapVolConstruction\":\"false\"}");
|
|
|
|
var initParam = new VolSurfaceInitParamsBuilder()
|
|
.SetUnderlying(underlying).SetValueDate(valueDate).Build("MoneynessVol", singleVols);
|
|
new NormalVolSurfaceInitializer().InitializeMarketProxy(marketProxy, initParam);
|
|
|
|
var result = OptionCalculatorV1.ValueAsianSyntheticNormalSpreadOptionTrade(
|
|
marketProxy,
|
|
valueDate,
|
|
underlyingTicker,
|
|
"CommodityFutures",
|
|
100,
|
|
valueDate,
|
|
"2019-04-30",
|
|
"Call",
|
|
"European",
|
|
"ArithmeticAverage",
|
|
"Fixed",
|
|
100.0,
|
|
1000.0,
|
|
underlyingTicker + "_VolSurface",
|
|
0.03,
|
|
"Buy",
|
|
"2019-04-30",
|
|
isAnnualized: false,
|
|
annualizeFactor: 1.0,
|
|
dividendRate: 0.0,
|
|
averagingPeriodStartDate: null,
|
|
ignoreSkewMap: true);
|
|
|
|
Assert.AreEqual(18.200884513861684, result.Pv, 1E-8, "");
|
|
Assert.AreEqual(542.322618892204, result.Delta, 1E-8, "");
|
|
Assert.AreEqual(8673.3218846805466, result.Gamma, 1E-8, "");
|
|
Assert.AreEqual(0.606696150462227, result.Vega, 1E-8, "");
|
|
Assert.AreEqual(-0.54126177511056994, result.Theta, 1E-8, "");
|
|
Assert.AreEqual(-0.00012680502384547764, result.Rho, 1E-8, "");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ValueAsianSyntheticNormalSpreadOptionTestV2()
|
|
{
|
|
var valueDate = "2019-04-04";
|
|
var underlyingTicker = "RB00";
|
|
var underlying = new underlying_manager()
|
|
{
|
|
QuotationDate = DateTime.Parse(valueDate),
|
|
UnderlyingCode = underlyingTicker,
|
|
UnderlyingInstrumentType = "CommodityFutures",
|
|
MaturityDate = new DateTime(2019, 5, 6)
|
|
};
|
|
|
|
var userId = Guid.NewGuid().ToString();
|
|
|
|
PS.ResetConfig("{\"SkewMapVolConstruction\":\"false\"}");
|
|
|
|
var trade = new trade()
|
|
{
|
|
TradeType = "亚式合成价差期权",
|
|
TradeDate = new DateTime(2019, 4, 4),
|
|
MaturityDate = new DateTime(2019, 4, 30),
|
|
ExerciseDate = new DateTime(2019, 4, 30),
|
|
ExerciseMode = "European",
|
|
OptionType = "看涨",
|
|
BuySell = "Buy",
|
|
Strike = 100,
|
|
Notional = 1000,
|
|
|
|
trade_asian_option = new trade_asian_option()
|
|
{
|
|
PayoffType = "ArithmeticAverage",
|
|
StrikeType = "Fixed"
|
|
}
|
|
};
|
|
|
|
var parameter = new AsianSyntheticNormalSpreadOptionParameter()
|
|
{
|
|
ValueDate = new DateTime(2019, 4, 4),
|
|
Volatility = 0.3,
|
|
SpotPrices = new Dictionary<string, double>() { { underlying.UnderlyingCode, 100 } },
|
|
RiskFreeRate = 0.03,
|
|
Fixings = ""
|
|
};
|
|
|
|
var result = ValueCalculator.CalculateTradeValue(
|
|
userId,
|
|
trade,
|
|
underlying,
|
|
parameter);
|
|
|
|
Assert.AreEqual(18.200884513861684, result.Pv, 1E-8, "");
|
|
Assert.AreEqual(542.322618892204, result.Delta, 1E-8, "");
|
|
Assert.AreEqual(8673.3218846805466, result.Gamma, 1E-8, "");
|
|
Assert.AreEqual(0.606696150462227, result.Vega, 1E-8, "");
|
|
Assert.AreEqual(-0.54126177511056994, result.Theta, 1E-8, "");
|
|
Assert.AreEqual(-0.00012680502384547764, result.Rho, 1E-8, "");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ValueAutocallTestV2()
|
|
{
|
|
var valueDate = "2019-07-30";
|
|
var underlyingTicker = "RB00";
|
|
var underlying = new underlying_manager()
|
|
{
|
|
QuotationDate = DateTime.Parse(valueDate),
|
|
UnderlyingCode = underlyingTicker,
|
|
UnderlyingInstrumentType = "CommodityFutures",
|
|
MaturityDate = new DateTime(2019, 5, 6)
|
|
};
|
|
|
|
var userId = Guid.NewGuid().ToString();
|
|
|
|
PS.ResetConfig("{\"SkewMapVolConstruction\":\"false\"}");
|
|
|
|
var trade = new trade()
|
|
{
|
|
TradeType = "凤凰期权",
|
|
TradeDate = new DateTime(2019, 7, 30),
|
|
MaturityDate = new DateTime(2020, 9, 8),
|
|
ExerciseDate = new DateTime(2020, 2, 1),
|
|
OptionType = "看涨",
|
|
ExerciseMode = "European",
|
|
BuySell = "Buy",
|
|
Strike = 100,
|
|
Notional = 12,
|
|
SpotPrice = 100,
|
|
ParticipationRate = 0.75,
|
|
|
|
trade_autocall = new trade_autocall()
|
|
{
|
|
KOBarrier = 100,
|
|
KIBarrier = 80,
|
|
Coupon = 0.12,
|
|
CouponPayType = CouponPayTypeEnum.AtMaturity,
|
|
KOObservationDates = "2019-08-30,2019-09-27,2019-10-31,2019-11-29,2019-12-27,2020-01-31"
|
|
}
|
|
};
|
|
|
|
var parameter = new AutocallParameter()
|
|
{
|
|
ValueDate = new DateTime(2019, 7, 30),
|
|
Volatility = 0.35,
|
|
SpotPrices = new Dictionary<string, double>() { { underlying.UnderlyingCode, 100 } },
|
|
RiskFreeRate = 0.035
|
|
};
|
|
|
|
var result = ValueCalculator.CalculateTradeValue(
|
|
userId,
|
|
trade,
|
|
underlying,
|
|
parameter);
|
|
|
|
Assert.AreEqual(8.5343, result.Pv, 0.01);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ValueSnowballTest()
|
|
{
|
|
var valueDate = new DateTime(2019, 07, 30);
|
|
var underlyingTicker = "RB00";
|
|
|
|
var vols = QdpVolHelper.GetDefaultVolatility(0.35);
|
|
|
|
var tradeId = Guid.NewGuid().ToString();
|
|
|
|
using var mp = new MarketProxy(valueDate, 0.035);
|
|
mp.SetVolSurface(tradeId, vols);
|
|
|
|
var tdparam = new SnowballOptionTradeParam
|
|
{
|
|
underlyingTickers = new[] { underlyingTicker },
|
|
underlyingInstrumentType = "CommodityFutures",
|
|
startDate = valueDate,
|
|
endDate = new DateTime(2019, 09, 08),
|
|
optionType = OptionType.Call,
|
|
koBarrier = 100.0,
|
|
kiBarrier = 80.0,
|
|
koObservationDateStr = "2019-08-30,2019-09-27,2019-10-31,2019-11-29,2019-12-27,2020-01-31",
|
|
observationDateStr = null,
|
|
coupon = 0.12,
|
|
participationRate = 0.75,
|
|
principalRate = 0.0,
|
|
initialSpotPrice = 100.0,
|
|
notional = 12.0,
|
|
volSurfaceNames = new[] { tradeId },
|
|
riskFreeRate = 0.035,
|
|
buysell = "Buy",
|
|
exerciseDate = new DateTime(2020, 1, 31),
|
|
isAnnualized = false,
|
|
annualizedFactor = 1.0,
|
|
strike = 100.0,
|
|
spreadStrikeAtKO = 0.0,
|
|
spreadStrikeAtMaturity = 0.0,
|
|
isFixedCoupon = false,
|
|
koRebate = 12.0,
|
|
koBarrierAdjustStep = 0.0,
|
|
useOptionPayoffAtKO = false,
|
|
useOptionPayoffAtMaturity = true,
|
|
annualizedOptionPayoff = false,
|
|
couponDayCount = "Act365",
|
|
kiOptionType = OptionType.Coupon,
|
|
koOptionType = OptionType.Call
|
|
};
|
|
|
|
var result = TradeRiskCalcUtil.GetSnowballOptionValue(mp, new OptionCalcParam<SnowballOptionTradeParam>(tdparam)
|
|
{
|
|
spotPrices = new[] { 100d },
|
|
pricingRequest = PricingRequest.Pv,
|
|
quadratureFastMode = true
|
|
});
|
|
|
|
Assert.AreEqual(8.4353, result.Pv, 0.01);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ValueSnowballTestV2()
|
|
{
|
|
var valueDate = "2019-07-30";
|
|
var underlyingTicker = "RB00";
|
|
var underlying = new underlying_manager()
|
|
{
|
|
QuotationDate = DateTime.Parse(valueDate),
|
|
UnderlyingCode = underlyingTicker,
|
|
UnderlyingInstrumentType = "CommodityFutures",
|
|
MaturityDate = new DateTime(2019, 5, 6)
|
|
};
|
|
|
|
var userId = Guid.NewGuid().ToString();
|
|
|
|
PS.ResetConfig("{\"SkewMapVolConstruction\":\"false\"}");
|
|
|
|
var trade = new trade()
|
|
{
|
|
TradeType = "雪球期权",
|
|
TradeDate = new DateTime(2019, 7, 30),
|
|
MaturityDate = new DateTime(2020, 9, 8),
|
|
ExerciseDate = new DateTime(2020, 2, 1),
|
|
OptionType = "看涨",
|
|
ExerciseMode = "European",
|
|
BuySell = "Buy",
|
|
Strike = 100,
|
|
Notional = 12,
|
|
SpotPrice = 100,
|
|
ParticipationRate = 0.75,
|
|
|
|
trade_snowball = new trade_snowball()
|
|
{
|
|
KOBarrier = 100,
|
|
KIBarrier = 80,
|
|
Coupon = 0.12,
|
|
KOObservationDates = "2019-08-30,2019-09-27,2019-10-31,2019-11-29,2019-12-27,2020-01-31"
|
|
}
|
|
};
|
|
|
|
var parameter = new SnowballParameter()
|
|
{
|
|
ValueDate = new DateTime(2019, 7, 30),
|
|
Volatility = 0.35,
|
|
SpotPrices = new Dictionary<string, double>() { { underlying.UnderlyingCode, 100 } },
|
|
RiskFreeRate = 0.035
|
|
};
|
|
|
|
var result = ValueCalculator.CalculateTradeValue(
|
|
userId,
|
|
trade,
|
|
underlying,
|
|
parameter);
|
|
|
|
Assert.AreEqual(8.4353, result.Pv, 0.01);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ValueDoubleSharkFinOptionTest()
|
|
{
|
|
var valueDate = "2019-04-04";
|
|
var underlyingTicker = "RB00";
|
|
var underlying = new underlying_manager()
|
|
{
|
|
QuotationDate = DateTime.Parse(valueDate),
|
|
UnderlyingCode = underlyingTicker
|
|
};
|
|
|
|
//创建默认市场,并设置波动率曲面
|
|
var marketProxy = new PrebuiltQdpMarketProxy();
|
|
|
|
var tenors = new string[]
|
|
{
|
|
"1D",
|
|
"2M",
|
|
"1Y"
|
|
};
|
|
|
|
var strikes = new double[]
|
|
{
|
|
0.95,
|
|
1.0,
|
|
1.05,
|
|
};
|
|
|
|
var vols = new double[3, 3];
|
|
var singleVols = new List<SingleVol>();
|
|
for (var i = 0; i < vols.GetLength(0); ++i)
|
|
{
|
|
for (var j = 0; j < vols.GetLength(1); ++j)
|
|
{
|
|
vols[i, j] = 0.2;
|
|
singleVols.Add(new SingleVol()
|
|
{
|
|
Strike = strikes[j],
|
|
Expire = tenors[i],
|
|
Vol = 0.2
|
|
});
|
|
}
|
|
}
|
|
|
|
PS.ResetConfig("{\"SkewMapVolConstruction\":\"false\"}");
|
|
|
|
var initParam = new VolSurfaceInitParamsBuilder()
|
|
.SetUnderlying(underlying).SetValueDate(valueDate).Build("MoneynessVol", singleVols);
|
|
new NormalVolSurfaceInitializer().InitializeMarketProxy(marketProxy, initParam);
|
|
|
|
var result = OptionCalculatorV1.ValueDoubleSharkFinOptionTrade(
|
|
marketProxy,
|
|
valueDate,
|
|
underlyingTicker,
|
|
"CommodityFutures",
|
|
125,
|
|
110,
|
|
valueDate,
|
|
"2019-05-06",
|
|
"Call",
|
|
"European",
|
|
144,
|
|
100,
|
|
true,
|
|
120,
|
|
20,
|
|
20,
|
|
"AtHit",
|
|
100,
|
|
underlyingTicker + "_VolSurface",
|
|
0.035,
|
|
"Buy",
|
|
"2019-05-06",
|
|
participationRate: 1.0,
|
|
principalRate: 0.0,
|
|
isAnnualized: false,
|
|
annualizeFactor: 1.0,
|
|
callParticipationRate: 1.0,
|
|
putParticipationRate: 1.0);
|
|
|
|
Assert.AreEqual(125.79272737448922, result.Pv, 1E-8, "");
|
|
Assert.AreEqual(20.046036888621188, result.Delta, 1E-8, "");
|
|
Assert.AreEqual(7.002952211507818, result.Gamma, 1E-8, "");
|
|
Assert.AreEqual(16.58915982588951, result.Vega, 1E-8, "");
|
|
Assert.AreEqual(-8.5410348523357555, result.Theta, 1E-8, "");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ValueDoubleSharkFinOptionTestV2()
|
|
{
|
|
var valueDate = "2019-04-04";
|
|
var underlyingTicker = "RB00";
|
|
var underlying = new underlying_manager()
|
|
{
|
|
QuotationDate = DateTime.Parse(valueDate),
|
|
UnderlyingCode = underlyingTicker,
|
|
UnderlyingInstrumentType = "CommodityFutures",
|
|
MaturityDate = new DateTime(2019, 5, 6)
|
|
};
|
|
|
|
var userId = Guid.NewGuid().ToString();
|
|
|
|
PS.ResetConfig("{\"SkewMapVolConstruction\":\"false\"}");
|
|
|
|
var trade = new trade()
|
|
{
|
|
TradeType = "双鲨期权",
|
|
TradeDate = new DateTime(2019, 4, 4),
|
|
MaturityDate = new DateTime(2019, 5, 6),
|
|
ExerciseDate = new DateTime(2019, 5, 6),
|
|
OptionType = "看涨",
|
|
ExerciseMode = "European",
|
|
BuySell = "Buy",
|
|
Strike = 110,
|
|
Notional = 100,
|
|
|
|
trade_double_sharkfin_option = new trade_double_sharkfin_option()
|
|
{
|
|
BarrierHigh = 144,
|
|
BarrierLow = 100,
|
|
StrikeHigh = 125,
|
|
StrikeLow = 110,
|
|
Discrete = "离散",
|
|
Rebate = 20.0,
|
|
KnockInOutStatus = "Monitoring"
|
|
}
|
|
};
|
|
|
|
var parameter = new DoubleSharkFinOptionParameter()
|
|
{
|
|
ValueDate = new DateTime(2019, 4, 4),
|
|
Volatility = 0.2,
|
|
SpotPrices = new Dictionary<string, double>() { { underlying.UnderlyingCode, 120.0 } },
|
|
RiskFreeRate = 0.035
|
|
};
|
|
|
|
var result = ValueCalculator.CalculateTradeValue(
|
|
userId,
|
|
trade,
|
|
underlying,
|
|
parameter);
|
|
|
|
Assert.AreEqual(125.79272737448922, result.Pv, 1E-8, "");
|
|
Assert.AreEqual(20.046036888621188, result.Delta, 1E-8, "");
|
|
Assert.AreEqual(7.002952211507818, result.Gamma, 1E-8, "");
|
|
Assert.AreEqual(16.58915982588951, result.Vega, 1E-8, "");
|
|
Assert.AreEqual(-8.5410348523357555, result.Theta, 1E-8, "");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ValueSpreadOptionTest()
|
|
{
|
|
var valueDate = "2019-04-04";
|
|
var underlyingTicker1 = "RB00";
|
|
var underlying1 = new underlying_manager()
|
|
{
|
|
QuotationDate = DateTime.Parse(valueDate),
|
|
UnderlyingCode = underlyingTicker1
|
|
};
|
|
|
|
var underlyingTicker2 = "RB01";
|
|
var underlying2 = new underlying_manager()
|
|
{
|
|
QuotationDate = DateTime.Parse(valueDate),
|
|
UnderlyingCode = underlyingTicker2
|
|
};
|
|
|
|
var underlyingTicker3 = "RB02";
|
|
var underlying3 = new underlying_manager()
|
|
{
|
|
QuotationDate = DateTime.Parse(valueDate),
|
|
UnderlyingCode = underlyingTicker3
|
|
};
|
|
|
|
//创建默认市场,并设置波动率曲面
|
|
var marketProxy = new PrebuiltQdpMarketProxy();
|
|
|
|
var tenors = new string[]
|
|
{
|
|
"1D",
|
|
"2M",
|
|
"1Y"
|
|
};
|
|
|
|
var strikes = new double[]
|
|
{
|
|
0.95,
|
|
1.0,
|
|
1.05,
|
|
};
|
|
|
|
var vols1 = new double[3, 3];
|
|
var singleVols1 = new List<SingleVol>();
|
|
for (var i = 0; i < vols1.GetLength(0); ++i)
|
|
{
|
|
for (var j = 0; j < vols1.GetLength(1); ++j)
|
|
{
|
|
vols1[i, j] = 0.2;
|
|
singleVols1.Add(new SingleVol()
|
|
{
|
|
Strike = strikes[j],
|
|
Expire = tenors[i],
|
|
Vol = 0.2
|
|
});
|
|
}
|
|
}
|
|
|
|
var vols2 = new double[3, 3];
|
|
var singleVols2 = new List<SingleVol>();
|
|
for (var i = 0; i < vols2.GetLength(0); ++i)
|
|
{
|
|
for (var j = 0; j < vols2.GetLength(1); ++j)
|
|
{
|
|
vols2[i, j] = 0.23;
|
|
singleVols2.Add(new SingleVol()
|
|
{
|
|
Strike = strikes[j],
|
|
Expire = tenors[i],
|
|
Vol = 0.23
|
|
});
|
|
}
|
|
}
|
|
|
|
var vols3 = new double[3, 3];
|
|
var singleVols3 = new List<SingleVol>();
|
|
for (var i = 0; i < vols3.GetLength(0); ++i)
|
|
{
|
|
for (var j = 0; j < vols3.GetLength(1); ++j)
|
|
{
|
|
vols3[i, j] = 0.25;
|
|
singleVols3.Add(new SingleVol()
|
|
{
|
|
Strike = strikes[j],
|
|
Expire = tenors[i],
|
|
Vol = 0.25
|
|
});
|
|
}
|
|
}
|
|
|
|
PS.ResetConfig("{\"SkewMapVolConstruction\":\"false\"}");
|
|
|
|
var initParam = new VolSurfaceInitParamsBuilder()
|
|
.SetUnderlying(underlying1).SetValueDate(valueDate).Build("MoneynessVol", singleVols1);
|
|
new NormalVolSurfaceInitializer().InitializeMarketProxy(marketProxy, initParam);
|
|
|
|
var initParam2 = new VolSurfaceInitParamsBuilder()
|
|
.SetUnderlying(underlying2).SetValueDate(valueDate).Build("MoneynessVol", singleVols2);
|
|
new NormalVolSurfaceInitializer().InitializeMarketProxy(marketProxy, initParam2);
|
|
|
|
var initParam3 = new VolSurfaceInitParamsBuilder()
|
|
.SetUnderlying(underlying3).SetValueDate(valueDate).Build("MoneynessVol", singleVols3);
|
|
new NormalVolSurfaceInitializer().InitializeMarketProxy(marketProxy, initParam3);
|
|
|
|
var payoff = new SpreadOptionPayoff("S1+S2-S3-K");
|
|
//计算
|
|
var result = OptionCalculatorV1.ValueSpreadOption(
|
|
marketProxy,
|
|
valueDate,
|
|
"CommodityFutures",
|
|
new string[] { underlyingTicker1, underlyingTicker2, underlyingTicker3 },
|
|
payoff.Weights,
|
|
payoff.SpreadType,
|
|
new string[] { underlyingTicker1 + "_VolSurface", underlyingTicker2 + "_VolSurface", underlyingTicker3 + "_VolSurface" },
|
|
2900.0,
|
|
valueDate,
|
|
"2019-05-06",
|
|
"Call",
|
|
"European",
|
|
new double[] { 2850, 2930, 2987 },
|
|
100.0,
|
|
0.035,
|
|
new double[] { 0.85, 0.9, 0.75 },
|
|
"Buy",
|
|
"2019-05-06",
|
|
1.0,
|
|
false,
|
|
1.0);
|
|
|
|
Assert.AreEqual(3712.3651708468392, result.Pv, 1E-8, "");
|
|
Assert.AreEqual(29.766132038275828, result.Delta, 1E-8, "");
|
|
Assert.AreEqual(0.17244019545614719, result.Gamma, 1E-8, "");
|
|
Assert.AreEqual(198.39776415513484, result.Vega, 1E-8, "");
|
|
Assert.AreEqual(0, result.Theta, 1E-8, "");
|
|
Assert.AreEqual(-0.030429098001604871, result.Rho, 1E-8, "");
|
|
|
|
Assert.AreEqual(30.391497575692483, result.Delta2.Value, 1E-8, "");
|
|
Assert.AreEqual(0.17016645870171487, result.Gamma2.Value, 1E-8, "");
|
|
Assert.AreEqual(257.01927094713324, result.Vega2.Value, 1E-8, "");
|
|
|
|
Assert.AreEqual(-29.403113571788708, result.Delta3.Value, 1E-8, "");
|
|
Assert.AreEqual(0.17698766896501184, result.Gamma3.Value, 1E-8, "");
|
|
Assert.AreEqual(-120.56156531622924, result.Vega3.Value, 1E-8, "");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ValueSpreadOptionTestV2()
|
|
{
|
|
var valueDate = "2019-04-04";
|
|
var underlyingTicker1 = "RB00";
|
|
var underlying1 = new underlying_manager()
|
|
{
|
|
QuotationDate = DateTime.Parse(valueDate),
|
|
UnderlyingCode = underlyingTicker1,
|
|
UnderlyingInstrumentType = "CommodityFutures",
|
|
MaturityDate = new DateTime(2019, 5, 6)
|
|
};
|
|
var underlyingTicker2 = "RB01";
|
|
var underlying2 = new underlying_manager()
|
|
{
|
|
QuotationDate = DateTime.Parse(valueDate),
|
|
UnderlyingCode = underlyingTicker2,
|
|
UnderlyingInstrumentType = "CommodityFutures",
|
|
MaturityDate = new DateTime(2019, 5, 6)
|
|
};
|
|
var underlyingTicker3 = "RB02";
|
|
var underlying3 = new underlying_manager()
|
|
{
|
|
QuotationDate = DateTime.Parse(valueDate),
|
|
UnderlyingCode = underlyingTicker3,
|
|
UnderlyingInstrumentType = "CommodityFutures",
|
|
MaturityDate = new DateTime(2019, 5, 6)
|
|
};
|
|
|
|
var userId = Guid.NewGuid().ToString();
|
|
|
|
PS.ResetConfig("{\"SkewMapVolConstruction\":\"false\"}");
|
|
|
|
var trade = new trade()
|
|
{
|
|
TradeType = "价差期权",
|
|
TradeDate = new DateTime(2019, 4, 4),
|
|
MaturityDate = new DateTime(2019, 5, 6),
|
|
ExerciseDate = new DateTime(2019, 5, 6),
|
|
OptionType = "看涨",
|
|
ExerciseMode = "European",
|
|
BuySell = "Buy",
|
|
Strike = 2900,
|
|
Notional = 100,
|
|
|
|
trade_spread_option = new trade_spread_option()
|
|
{
|
|
Payoff = "S1+S2-S3-K",
|
|
UnderlyingAssetCode1 = underlyingTicker1,
|
|
UnderlyingAssetCode2 = underlyingTicker2,
|
|
UnderlyingAssetCode3 = underlyingTicker3,
|
|
}
|
|
};
|
|
|
|
var parameter = new SpreadOptionParameter()
|
|
{
|
|
ValueDate = new DateTime(2019, 4, 4),
|
|
Volatilities = new double[] { 0.2, 0.23, 0.25 },
|
|
SpotPrices = new Dictionary<string, double>() { { underlying1.UnderlyingCode, 2850 }, { underlying2.UnderlyingCode, 2930 }, { underlying3.UnderlyingCode, 2987 } },
|
|
RiskFreeRate = 0.035,
|
|
Correlations = new double[] { 0.85, 0.9, 0.75 }
|
|
};
|
|
|
|
var result = ValueCalculator.CalculateTradeValue(
|
|
userId,
|
|
trade,
|
|
underlying1,
|
|
parameter);
|
|
|
|
Assert.AreEqual(3712.3651708468392, result.Pv, 1E-8, "");
|
|
Assert.AreEqual(29.766132038275828, result.Delta, 1E-8, "");
|
|
Assert.AreEqual(0.17244019545614719, result.Gamma, 1E-8, "");
|
|
Assert.AreEqual(198.39776415513484, result.Vega, 1E-8, "");
|
|
Assert.AreEqual(0, result.Theta, 1E-8, "");
|
|
Assert.AreEqual(-0.030429098001604871, result.Rho, 1E-8, "");
|
|
|
|
Assert.AreEqual(30.391497575692483, result.Delta2.Value, 1E-8, "");
|
|
Assert.AreEqual(0.17016645870171487, result.Gamma2.Value, 1E-8, "");
|
|
Assert.AreEqual(257.01927094713324, result.Vega2.Value, 1E-8, "");
|
|
|
|
Assert.AreEqual(-29.403113571788708, result.Delta3.Value, 1E-8, "");
|
|
Assert.AreEqual(0.17698766896501184, result.Gamma3.Value, 1E-8, "");
|
|
Assert.AreEqual(-120.56156531622924, result.Vega3.Value, 1E-8, "");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void ValueCashFlowTradeTest()
|
|
{
|
|
var valueDate = "2020-01-04";
|
|
var underlying = new underlying_manager()
|
|
{
|
|
QuotationDate = DateTime.Parse(valueDate),
|
|
MaturityDate = new DateTime(2020, 5, 6)
|
|
};
|
|
|
|
var trade = new trade()
|
|
{
|
|
TradeType = "现金流交易",
|
|
StartDate = new DateTime(2020, 1, 4),
|
|
TradeDate = new DateTime(2020, 1, 4),
|
|
MaturityDate = new DateTime(2020, 5, 6),
|
|
BuySell = "Buy",
|
|
StockEqvNotional = 1000000,
|
|
|
|
trade_cashflow = new trade_cashflow()
|
|
{
|
|
ProfitRate = 0.03,
|
|
RateType = CashFlowRateTypeEnum.年化利率,
|
|
ProfitDayCount = "Act365",
|
|
}
|
|
};
|
|
|
|
var userId = Guid.NewGuid().ToString();
|
|
|
|
PS.ResetConfig("{\"SkewMapVolConstruction\":\"false\"}");
|
|
|
|
var parameter = new CashFlowTradeParameter()
|
|
{
|
|
ValueDate = new DateTime(2020, 1, 4),
|
|
RiskFreeRate = 0.035
|
|
};
|
|
|
|
var result = ValueCalculator.CalculateTradeValue(
|
|
userId,
|
|
trade,
|
|
underlying,
|
|
parameter);
|
|
|
|
Assert.AreEqual(999300.87674743345, result.Pv, 1E-8, "");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void GetAddDaysWithPrecisionOfMinuteTest()
|
|
{
|
|
//var privateType = new PrivateType(typeof(OptionCalculatorV1));
|
|
|
|
////c1: after day close, before night market
|
|
//var t = new DateTime(2019, 6, 10, 20, 0, 0);
|
|
//var ttm = Convert.ToDouble(privateType.InvokeStatic("GetAddDaysWithPrecisionOfMinute", t, true));
|
|
//Assert.AreEqual(0, ttm, DELTA, "c1: true");
|
|
//ttm = Convert.ToDouble(privateType.InvokeStatic("GetAddDaysWithPrecisionOfMinute", t, false));
|
|
//Assert.AreEqual(0, ttm, DELTA, "c1: false");
|
|
|
|
////c2: after night market open
|
|
//t = new DateTime(2019, 6, 10, 21, 30, 0);
|
|
//ttm = Convert.ToDouble(privateType.InvokeStatic("GetAddDaysWithPrecisionOfMinute", t, true));
|
|
//Assert.AreEqual((1 - 0.5 / 6), ttm, DELTA, "c2: true");
|
|
//ttm = Convert.ToDouble(privateType.InvokeStatic("GetAddDaysWithPrecisionOfMinute", t, false));
|
|
//Assert.AreEqual(1, ttm, DELTA, "c1: false");
|
|
|
|
////c3: after night market active hour
|
|
//t = new DateTime(2019, 6, 10, 23, 30, 0);
|
|
//ttm = Convert.ToDouble(privateType.InvokeStatic("GetAddDaysWithPrecisionOfMinute", t, true));
|
|
//Assert.AreEqual((1 - 2.0 / 6), ttm, DELTA, "c3: true");
|
|
//ttm = Convert.ToDouble(privateType.InvokeStatic("GetAddDaysWithPrecisionOfMinute", t, false));
|
|
//Assert.AreEqual(1, ttm, DELTA, "c1: false");
|
|
|
|
////c4: before AM trading
|
|
//t = new DateTime(2019, 6, 10, 8, 30, 0);
|
|
//ttm = Convert.ToDouble(privateType.InvokeStatic("GetAddDaysWithPrecisionOfMinute", t, true));
|
|
//Assert.AreEqual((1 - 2.0 / 6), ttm, DELTA, "c4: true");
|
|
//ttm = Convert.ToDouble(privateType.InvokeStatic("GetAddDaysWithPrecisionOfMinute", t, false));
|
|
//Assert.AreEqual(1, ttm, DELTA, "c4: false");
|
|
|
|
////c5: AM trading
|
|
//t = new DateTime(2019, 6, 10, 9, 30, 0);
|
|
//ttm = Convert.ToDouble(privateType.InvokeStatic("GetAddDaysWithPrecisionOfMinute", t, true));
|
|
//Assert.AreEqual((1 - 2.5 / 6), ttm, DELTA, "c5: true");
|
|
//ttm = Convert.ToDouble(privateType.InvokeStatic("GetAddDaysWithPrecisionOfMinute", t, false));
|
|
//Assert.AreEqual(1 - 0.5 / 4, ttm, DELTA, "c5: false");
|
|
|
|
////c6: lunch break
|
|
//t = new DateTime(2019, 6, 10, 11, 40, 0);
|
|
//ttm = Convert.ToDouble(privateType.InvokeStatic("GetAddDaysWithPrecisionOfMinute", t, true));
|
|
//Assert.AreEqual((1 - 4.5 / 6), ttm, DELTA, "c6-1: true");
|
|
//ttm = Convert.ToDouble(privateType.InvokeStatic("GetAddDaysWithPrecisionOfMinute", t, false));
|
|
//Assert.AreEqual(1 - 2.5 / 4, ttm, DELTA, "c6-1: false");
|
|
|
|
//t = new DateTime(2019, 6, 10, 13, 00, 0);
|
|
//ttm = Convert.ToDouble(privateType.InvokeStatic("GetAddDaysWithPrecisionOfMinute", t, true));
|
|
//Assert.AreEqual((1 - 4.5 / 6), ttm, DELTA, "c6-2: true");
|
|
//ttm = Convert.ToDouble(privateType.InvokeStatic("GetAddDaysWithPrecisionOfMinute", t, false));
|
|
//Assert.AreEqual(1 - 2.5 / 4, ttm, DELTA, "c6-2: false");
|
|
|
|
////c7: pm trading
|
|
//t = new DateTime(2019, 6, 10, 13, 30, 0);
|
|
//ttm = Convert.ToDouble(privateType.InvokeStatic("GetAddDaysWithPrecisionOfMinute", t, true));
|
|
//Assert.AreEqual((1 - 4.5 / 6), ttm, DELTA, "c7-1: true");
|
|
//ttm = Convert.ToDouble(privateType.InvokeStatic("GetAddDaysWithPrecisionOfMinute", t, false));
|
|
//Assert.AreEqual(1 - 2.5 / 4, ttm, DELTA, "c7-1: false");
|
|
|
|
//t = new DateTime(2019, 6, 10, 14, 30, 0);
|
|
//ttm = Convert.ToDouble(privateType.InvokeStatic("GetAddDaysWithPrecisionOfMinute", t, true));
|
|
//Assert.AreEqual((1 - 5.5 / 6), ttm, DELTA, "c7-2: true");
|
|
//ttm = Convert.ToDouble(privateType.InvokeStatic("GetAddDaysWithPrecisionOfMinute", t, false));
|
|
//Assert.AreEqual(1 - 3.5 / 4, ttm, DELTA, "c7-2: false");
|
|
|
|
////c8: after pm trading
|
|
//t = new DateTime(2019, 6, 10, 15, 00, 0);
|
|
//ttm = Convert.ToDouble(privateType.InvokeStatic("GetAddDaysWithPrecisionOfMinute", t, true));
|
|
//Assert.AreEqual(0, ttm, DELTA, "c8-1: true");
|
|
//ttm = Convert.ToDouble(privateType.InvokeStatic("GetAddDaysWithPrecisionOfMinute", t, false));
|
|
//Assert.AreEqual(0, ttm, DELTA, "c8-1: false");
|
|
|
|
//t = new DateTime(2019, 6, 10, 20, 00, 0);
|
|
//ttm = Convert.ToDouble(privateType.InvokeStatic("GetAddDaysWithPrecisionOfMinute", t, true));
|
|
//Assert.AreEqual(0, ttm, DELTA, "c8-2: true");
|
|
//ttm = Convert.ToDouble(privateType.InvokeStatic("GetAddDaysWithPrecisionOfMinute", t, false));
|
|
//Assert.AreEqual(0, ttm, DELTA, "c8-2: false");
|
|
|
|
////c9: night market just open
|
|
//t = new DateTime(2019, 6, 10, 21, 00, 0);
|
|
//ttm = Convert.ToDouble(privateType.InvokeStatic("GetAddDaysWithPrecisionOfMinute", t, true));
|
|
//Assert.AreEqual(1, ttm, DELTA, "c9: true");
|
|
//ttm = Convert.ToDouble(privateType.InvokeStatic("GetAddDaysWithPrecisionOfMinute", t, false));
|
|
//Assert.AreEqual(1, ttm, DELTA, "c9: false");
|
|
}
|
|
|
|
[TestMethod]
|
|
public void GetHistoricalPricesTest()
|
|
{
|
|
var priceCount = 20;
|
|
var volCurve = VolCaculator.Instance.GetHistoricalVolCurvePointsEx("I2012", DateTime.Now, priceCount, priceCount, 0.3, 0.7);
|
|
Assert.IsTrue(volCurve.Points.Count == priceCount);
|
|
}
|
|
|
|
[TestMethod]
|
|
public void GetSpotPriceFromBasisSpreadTest()
|
|
{
|
|
var valueDate = new DateTime(2020, 7, 24);
|
|
var maturityDate = new DateTime(2020, 9, 1);
|
|
var codelist = new List<string> { "Ab00", "Un00", "Ub22" };
|
|
var tenors = new List<string> { "1M", "2M", "3M" };
|
|
var spreads = new List<double> { 5.0, -3.0, 7.0 };
|
|
var isClosePrice = true;
|
|
var spotPrice = CalculatorHelper.GetSpotPriceFromBasisSpread(valueDate, maturityDate, isClosePrice, codelist, tenors, spreads);
|
|
Assert.AreEqual(4002.93548387, spotPrice, 1e-8);
|
|
|
|
maturityDate = new DateTime(2020, 8, 24);
|
|
spotPrice = CalculatorHelper.GetSpotPriceFromBasisSpread(valueDate, maturityDate, isClosePrice, codelist, tenors, spreads);
|
|
Assert.AreEqual(4005.0, spotPrice, 1e-8);
|
|
|
|
maturityDate = new DateTime(2020, 9, 24);
|
|
spotPrice = CalculatorHelper.GetSpotPriceFromBasisSpread(valueDate, maturityDate, isClosePrice, codelist, tenors, spreads);
|
|
Assert.AreEqual(3997.0, spotPrice, 1e-8);
|
|
|
|
maturityDate = new DateTime(2020, 10, 24);
|
|
spotPrice = CalculatorHelper.GetSpotPriceFromBasisSpread(valueDate, maturityDate, isClosePrice, codelist, tenors, spreads);
|
|
Assert.AreEqual(4007.0, spotPrice, 1e-8);
|
|
|
|
maturityDate = new DateTime(2020, 11, 24);
|
|
spotPrice = CalculatorHelper.GetSpotPriceFromBasisSpread(valueDate, maturityDate, isClosePrice, codelist, tenors, spreads);
|
|
Assert.AreEqual(4007.0, spotPrice, 1e-8);
|
|
|
|
maturityDate = new DateTime(2020, 7, 31);
|
|
spotPrice = CalculatorHelper.GetSpotPriceFromBasisSpread(valueDate, maturityDate, isClosePrice, codelist, tenors, spreads);
|
|
Assert.AreEqual(4005.0, spotPrice, 1e-8);
|
|
|
|
}
|
|
|
|
[TestMethod]
|
|
public void CCRService()
|
|
{
|
|
var tradeList = DbContextFactory.GetYLDbContext().trade.Where(O => O.TradeStatus == "确认成交" && O.ValidState != "InValid" && O.ExerciseDate >= valuedateBLL.ValueDate).ToList();
|
|
new CCRService(OptUserInfo.UnitTestUser).CalculationCCR(tradeList, 2, 485);
|
|
}
|
|
}
|
|
}
|