从山证v2.3.0拷贝

This commit is contained in:
吴方海
2024-05-09 14:06:26 +08:00
parent 566ff33259
commit f9d8a256a6
4471 changed files with 1203456 additions and 9 deletions
@@ -0,0 +1,118 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using YLErp.BLL.Calculation;
namespace YLErp.Modules.CalculationModule
{
/// <summary>
///
/// </summary>
[TestClass]
public class ForwardradeCalcServiceTest
{
[TestMethod]
public void TestCalcValueOld()
{
var testItems = new[] {
new{ BuySell = "买入", CallPut = "Call", Strike = 3500,Notional = 100,SpotPrice= 3550 },
new{ BuySell = "买入", CallPut = "Call", Strike = 3500, Notional = 100, SpotPrice= 3450 },
new{ BuySell = "买入", CallPut = "Put", Strike = 3500, Notional = 100, SpotPrice= 3550 },
new{ BuySell = "买入", CallPut = "Put", Strike = 3500, Notional = 100, SpotPrice= 3450 },
new{ BuySell = "卖出", CallPut = "Call", Strike = 3500,Notional = 100,SpotPrice= 3550 },
new{ BuySell = "卖出", CallPut = "Call", Strike = 3500, Notional = 100, SpotPrice= 3450 },
new{ BuySell = "卖出", CallPut = "Put", Strike = 3500, Notional = 100, SpotPrice= 3550 },
new{ BuySell = "卖出", CallPut = "Put", Strike = 3500, Notional = 100, SpotPrice= 3450 },
};
foreach (var item in testItems)
{
var r1 = CalcValueV1(item.Strike, item.SpotPrice, item.Notional, item.CallPut, item.BuySell);
var r2 = CalcValueV2(item.Strike, item.SpotPrice, item.Notional, item.CallPut, item.BuySell);
Console.WriteLine(item.ToJson());
Console.WriteLine($"pv1: {r1.Pv}, pv2: {r2.Pv}, delta1: {r1.Delta}, delta2: {r2.Delta}");
Assert.IsTrue(r1.Pv == r2.Pv && r1.Delta == r2.Delta);
}
}
/// <summary>
/// 计算远期交易(买方角度)ValueCalculator.CalculateForward
/// </summary>
static TradeValueResult CalculateForwardV1(double strike, double spotPrice, double notional, string callPut)
{
var pv = 0.0;
if (callPut == "Call")
{
pv = (spotPrice - strike) * notional;
}
else if (callPut == "Put")
{
pv = (strike - spotPrice) * notional;
}
return new TradeValueResult()
{
Pv = pv,
Delta = callPut == "Call" ? notional : -notional,
DeltaCash = callPut == "Call" ? spotPrice * notional : -spotPrice * notional
};
}
public static TradeValueResult CalcValueV1(double strike, double spotPrice, double notional, string callput, string buysell)
{
var result = CalculateForwardV1(strike, spotPrice, notional, callput);
result.Pv *= TradeCalcHelper.GetSign(buysell);
result.Delta *= TradeCalcHelper.GetSign(buysell);
//买入看跌和卖出看涨取反
//var flag = (IsBuy(buysell) ? 1 : 2) | (callput == "Call" ? 1 : 2);
//if (flag == 3)
//{
// result.Delta = -result.Delta;
//}
return result;
}
/// <summary>
/// 计算PV/Risk(交易员角度)
/// </summary>
public static TradeValueResult CalcValueV2(double strike, double spotPrice, double notional, string callput, string buysell)
{
var isCall = callput == "Call";
var pv = (spotPrice - strike) * notional;
//买入看跌和卖出看涨取负值
var flag = (TradeCalcHelper.IsBuy(buysell) ? 1 : 2) | (isCall ? 1 : 2);
TradeValueResult result;
if (flag == 3)
{
result = new TradeValueResult
{
Pv = -pv,
Delta = -notional,
DeltaCash = -spotPrice * notional
};
}
else
{
result = new TradeValueResult
{
Pv = pv,
Delta = notional,
DeltaCash = spotPrice * notional
};
}
result.RoundedPv = result.Pv;
return result;
}
static bool IsBuy(string tradeType)
{
return tradeType == "Buy" || tradeType == "买入" || string.IsNullOrWhiteSpace(tradeType);
}
}
}
@@ -0,0 +1,108 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YLErp.Abstract.DataProviders;
using YLErp.BLL;
using YLErp.DBModels;
using YLErp.Helpers;
using YLErp.Modules.CalculationModule.Abstract;
namespace YLErp.Modules.CalculationModule
{
[TestClass]
public class HedgePnlCalcTest : YLUnitTestBase
{
[TestMethod]
public void TestCalculate()
{
var valueDate = DateTime.Today;
var calc = new InnerHedgePnlCalcContext(true, valueDate, "对冲"
, new InnerUnderlyingPriceProvider()
, new InnerExchangeOptionPriceProvider(), OptUser).GetHedgePnlCalc();
var exchangeTrades = GetExchangeTrades();
var eodTradePositions = GetEodTradePositions();
var results = calc.Calculate(exchangeTrades, eodTradePositions);
Assert.AreEqual(results.Count(), 3);
}
private IEnumerable<ExchangeTrade> GetExchangeTrades()
{
var valueDate = valuedateBLL.ValueDate;
var un = GetUnderlyingManager(true);
var baseTrade = new ExchangeTrade
{
AssetBookId = 1,
Comments = "单元测试",
Commission = 0,
CommissionType = DBModels.Enums.CommissionType.,
CreateTime = DateTime.Now,
ExchangeAccountCode = "TEST",
ExchangeAccountId = 1,
ExerciseMode = "European",
InstrumentType = ConsGlobal.InstrumentType.CommodityFutures,
IsValid = true,
MaturityDate = valueDate.AddMonths(1),
Notional = 100,
OptionCode = "RB00-C-3400",
OptionStrike = 3400,
TradeAmount = 100,
TradeDate = valueDate.AddDays(-10),
TradeLots = 10,
TradeNumber = Guid.NewGuid().ToString("N"),
TraderId = 1,
TraderName = "",
TradeSide = "多头开仓",
TradeSinglePrice = 16,
TradeSource = "",
OptId = 0,
OptDate = valueDate,
OptionType = "看涨",
OptName = "",
TradeType = "场内期权",
UnderlyingCode = "RB00",
UnderlyingId = un.id,
id = 1
};
var td1 = baseTrade.Clone();
td1.TradeType = "商品期权";
td1.TradeSinglePrice = 3233;
var td2 = baseTrade.Clone();
td2.id = 2;
var td3 = baseTrade.Clone();
td3.id = 3;
td3.OptionCode = "RB00-P-3400";
td3.OptionType = "看跌";
return new[] { td1, td2, td3 };
}
private IEnumerable<EodTradePosition> GetEodTradePositions()
{
return null;
}
}
class InnerHedgePnlCalcContext : HedgePnlCalcContext
{
public InnerHedgePnlCalcContext(bool isEodSettle, DateTime valueDate, string volType
, IPriceProvider underlyingPriceProvider, IPriceProvider exchangeOptionPriceProvider, OptUserInfo optUser)
: base(isEodSettle, valueDate, volType, underlyingPriceProvider, exchangeOptionPriceProvider, optUser)
{
CommissionCalc = new InnerExchangeTradeCommissionCalc();
ExchangeOptionPriceUseFlag = ExchangeOptionPriceUseFlag.CalcPv;
UnderlyingDataProvider = new InnerUnderlyingDataProvider();
}
}
}
@@ -0,0 +1,144 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.IO;
using System.Linq;
using YLErp.Abstract;
using YLErp.Abstract.DataProviders;
using YLErp.BLL;
using YLErp.DBModels;
using YLErp.Models;
using YLErp.Modules.CalculationModule.Abstract;
using YLErp.Modules.DataProviderModule;
using YLErp.Modules.VolatilityModule;
using YLErp.QdpModule;
namespace YLErp.Modules.CalculationModule
{
[TestClass]
public class OptionCaclTest : YLUnitTestBase
{
[TestMethod("测试雪球期权PV")]
public void TestSnowball()
{
var td = DbContext.trade.FirstOrDefault(n => n.TradeNumber == "CW20200053E0008");
Assert.IsNotNull(td);
var calcDataProvider = new CalcDataProvider(this)
{
UnderlyingPriceProvider = new InnerUnderlyingPriceProvider(),
VolatilityDataProvider = new InnerVolatilityDataProvider()
};
var context = new OptionValueCalcContext("对冲", valuedateBLL.ValueDate, valuedateBLL.RiskFreeRate / 100, calcDataProvider)
{
AddingVolRate = 0,
IsEodSettle = false,
IsUseTradeVol = true
};
context.IsPreciseTimeMode = context.IsUseTradeVol || !context.IsEodSettle;
var result = TradeRiskCalcUtil.CalcTradeRisk(td, context, out var underlyings);
Console.WriteLine($"PV:{result.Pv}; Delta:{result.Delta}");
}
[TestMethod("测试二元期权计算")]
public void TestBinaryOption()
{
var path = Path.Combine(AppContext.BaseDirectory, "Resources\\期权计算\\二元期权.json");
var json = File.ReadAllText(path);
var td = JsonHelper.Deserialize<trade>(json);
var result = OptionCalculatorV2.GetOptionValueResult(DateTime.Today, td, new OptionValueCalcRequest( 0.025)
{
spotPrices = new[] { 6105.8249 },
vols = new[] { 0.23 }
}, out _);
Console.WriteLine(result.Delta);
}
[TestMethod("测试二元期权计算")]
public void TestBinaryOption2()
{
var path = Path.Combine(AppContext.BaseDirectory, "Resources\\期权计算\\二元期权计算参数.json");
var json = File.ReadAllText(path);
var calcParam = JsonHelper.Deserialize<OptionCalcParam<BinaryOptionTradeParam>>(json);
using (var mp = new MarketProxy(DateTime.Today, 0.025))
{
var vols = QdpVolHelper.GetDefaultVolatility(0.23);
mp.SaveVolSurface(calcParam.volSurfaceNames[0], vols);
var result = TradeRiskCalcUtil.GetBinaryOptionValue(mp, calcParam);
Console.WriteLine(result.Gamma);
}
}
class CalcDataProvider : IOptionCalcDataProvider
{
public CalcDataProvider(YLBaseService baseService)
{
UnderlyingDataProvider = new UnderlyingDataProvider();
TradeExtendDataProvider = new TradeExtendDataProvider(baseService);
}
public IPriceProvider UnderlyingPriceProvider { get; set; }
public IUnderlyingDataProvider UnderlyingDataProvider { get; }
public ITradeExtendDataProvider TradeExtendDataProvider { get; }
public IVolatilityDataProvider VolatilityDataProvider { get; set; }
}
class InnerUnderlyingPriceProvider : IPriceProvider
{
public double GetPrice(string instrumentCode)
{
return 15.45;
}
public bool TryGetPrice(string instrumentCode, out double price)
{
price = 15.45;
return true;
}
}
class InnerVolatilityDataProvider : IVolatilityDataProvider
{
public double? GetExchangeOptionTradeHedgeVol(string optionCode, DateTime valueDate)
{
return 0.25;
}
public double? GetOtcOptionTradeEodVol(int tradeId, DateTime valueDate)
{
return 0.25;
}
public double? GetOtcOptionTradeHedgeVol(int tradeId, DateTime valueDate)
{
return 0.25;
}
public IOtcTradeVolatility GetOtcOptionTradeVol(int tradeId, DateTime valueDate)
{
return new OtcTradeVolatility
{
OpenVol = 0.25,
CloseVol = 0.25,
SmoothingDays = 1,
IsFirst = false,
ValueDate = valueDate
};
}
public IVolatility GetUnderlyingVolatility(string voltype, string contractCode, string userGroup)
{
return VolatilityHelper.GetDefaultVol(new SingleVolatilityRequest
{
QuotationDate = DateTime.Today,
TradeVolWithBidAsk = false,
UnderlyingCode = contractCode,
UnderlyingId = 0,
UserGroup = userGroup,
VolType = voltype
}, 0.25);
}
}
}
}
@@ -0,0 +1,251 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Qdp.Pricing.Base.Enums;
using System;
using YLErp.BLL;
using YLErp.BLL.Calculation;
using YLErp.DBModels;
using YLErp.QdpModule;
namespace YLErp.Modules.CalculationModule
{
/// <summary>
/// 期权计算比较(新版本和老版本)
/// </summary>
[TestClass]
public class OptionCalculatorCompare
{
const double ConstVol = 0.3;
const double Notional = 1;
const double SpotPrice = 3000;
const double RiskFreeRate = 0.03;
const string ExerciseType = "European";
const string UnderlyingCode = "RB00";
const int TTMDays = 36;
const string InstrumentType = "CommodityFutures";
static readonly string QdpMarketID = Guid.NewGuid().ToString();
static readonly DateTime TradeDate = new DateTime(2021, 1, 4);
static readonly DateTime ExerciseDate = new DateTime(2021, 3, 1);
[TestMethod]
public void TestVanillaOption()
{
var vols = QdpVolHelper.GetDefaultVolatility(ConstVol);
var valueDateStr = TradeDate.ToString("yyyy-MM-dd");
var marketProxy = QdpMarketManager.Instance.GetPrebuiltMarketProxy(QdpMarketID);
var underlying = new
{
UnderlyingCode = UnderlyingCode,
UnderlyingInstrumentType = InstrumentType,
Price = SpotPrice
};
//使用全局的DiscountCurve以提高计算效率
var discountCurveName = Guid.NewGuid().ToString();
var discountCurve = CalculatorHelper.CreateConstantRiskFreeCurve(discountCurveName, valuedateBLL.RiskFreeRate / 100.0);
marketProxy.AddYieldCurve(discountCurveName, valueDateStr, discountCurve);
var initParam = new VolSurfaceInitParamsBuilder(QdpMarketID)
.SetValueDate(TradeDate)
.SetUnderlying(0, UnderlyingCode, UnderlyingCode)
.SetVolatility(vols).Build(QdpMarketID);
VolSurfaceInitializerSingleton.GetInitializer(false).InitializeMarketProxy(initParam);
var bidMaturityDate = ExerciseDate.ToString(ConsGlobal.DateFormat);
var tv1 = OptionCalculatorV1.ValueVanillaOptionTrade(
marketProxy: marketProxy,
valueDate: valueDateStr,
underlyingTicker: underlying.UnderlyingCode,
underlyingInstrumentType: underlying.UnderlyingInstrumentType,
strike: SpotPrice,
startDate: valueDateStr,
endDate: bidMaturityDate,
optionType: "Call",
exerciseType: ExerciseType,
spotPrice: underlying.Price,
notional: Notional,
volSurfaceName: initParam.volSurfaceNameKey,
riskFreeRate: RiskFreeRate,
modelName: null,
tradeType: "Buy",
exerciseDate: bidMaturityDate,
hasNightMarket: false,
commodityFuturesPreciseTimeMode: true,
discountCurveName: discountCurveName,
participationRate: 1.0,
principalRate: 0.0,
isAnnualized: false,
annualizeFactor: 1.0,
timeToMaturityDays: TTMDays);
System.Diagnostics.Debug.WriteLine("V1 PV:" + tv1.Pv);
var vtParam = new VanillaOptionTradeParam
{
annualizedFactor = 0,
isAnnualized = false,
buysell = "买入",
commodityFuturesPreciseTimeMode = true,
dividendRate = 0,
dividends = null,
endDate = ExerciseDate,
exerciseDate = ExerciseDate,
exerciseType = ExerciseType,
hasNightMarket = false,
initialSpotPrice = SpotPrice,
isForwardTrade = false,
isMoneynessOption = false,
notional = Notional,
optionType = OptionType.Call,
participationRate = 1,
principalRate = 0,
riskFreeRate = RiskFreeRate,
settlementDate = ExerciseDate,
startDate = TradeDate,
strike = SpotPrice,
timeToMaturityDays = TTMDays,
tradeDate = TradeDate,
tradeId = QdpMarketID,
underlyingInstrumentType = InstrumentType,
underlyingTickers = new[] { UnderlyingCode },
volSurfaceNames = new[] { QdpMarketID }
};
TradeValueResult tv2;
using (var mp = new MarketProxy(TradeDate, 0.03))
{
mp.SaveVolSurface(QdpMarketID, vols);
tv2 = TradeRiskCalcUtil.GetVanillaOptionValue(mp, new OptionCalcParam<VanillaOptionTradeParam>(vtParam)
{
pricingRequest = QdpPricingRequest.BASIC_GREEKS,
spotPrices = new[] { 3000d },
});
}
System.Diagnostics.Debug.WriteLine("V2 PV:" + tv2.Pv);
Assert.AreEqual(tv1.Pv, tv2.Pv, 1e-6);
Assert.AreEqual(tv1.Delta, tv2.Delta, 1e-6);
Assert.AreEqual(tv1.Gamma, tv2.Gamma, 1e-6);
Assert.AreEqual(tv1.Theta, tv2.Theta, 1e-6);
Assert.AreEqual(tv1.Rho, tv2.Rho, 1e-6);
Assert.AreEqual(tv1.Vega, tv2.Vega, 1e-6);
}
[TestMethod]
public void TestAsiaOption()
{
var vols = QdpVolHelper.GetDefaultVolatility(ConstVol);
var valueDateStr = TradeDate.ToString("yyyy-MM-dd");
var marketProxy = QdpMarketManager.Instance.GetPrebuiltMarketProxy(QdpMarketID);
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(UnderlyingCode);
//使用全局的DiscountCurve以提高计算效率
var discountCurveName = Guid.NewGuid().ToString();
var discountCurve = CalculatorHelper.CreateConstantRiskFreeCurve(discountCurveName, valuedateBLL.RiskFreeRate / 100.0);
marketProxy.AddYieldCurve(discountCurveName, valueDateStr, discountCurve);
var initParam = new VolSurfaceInitParamsBuilder(QdpMarketID)
.SetValueDate(TradeDate)
.SetUnderlying(0, UnderlyingCode, UnderlyingCode)
.SetVolatility(vols).Build(QdpMarketID);
VolSurfaceInitializerSingleton.GetInitializer(false).InitializeMarketProxy(initParam);
underlying.Price = SpotPrice;
underlying.QuotationDate = TradeDate;
var td = new trade()
{
TradeType = "亚式期权",
UnderlyingCode = underlying.UnderlyingCode,
UnderlyingInstrumentType = InstrumentType,
TradeDate = TradeDate,
StartDate = TradeDate,
MaturityDate = ExerciseDate,
ExerciseDate = ExerciseDate,
OptionType = "看涨",
ExerciseMode = ExerciseType,
Strike = SpotPrice,
SpotPrice = SpotPrice,
Notional = Notional,
NoRiskRate = RiskFreeRate,
BuySell = "Buy",
QuotationType = "波动率调整",
TradeOpenVolatility = ConstVol,
TTMDays = TTMDays,
trade_asian_option = new trade_asian_option()
{
PayoffType = "ArithmeticAverage",
StrikeType = "Fixed",
AveragingPeriodStartDate = TradeDate
}
};
var tv1 = OptionCalculatorV1.GetOptionValueResult(QdpMarketID, underlying, td, new[] { SpotPrice },
useTradeVolMode: true, volSurfaceNames: new[] { initParam.volSurfaceNameKey },
fixing: $"{TradeDate:yyyy-MM-dd},{SpotPrice}", commodityFuturesPreciseTimeMode: false);
System.Diagnostics.Debug.WriteLine("V1 PV:" + tv1.Pv);
var vtParam = new AsianOptionTradeParam
{
annualizedFactor = 0,
isAnnualized = false,
buysell = "买入",
commodityFuturesPreciseTimeMode = false,
dividendRate = 0,
dividends = null,
endDate = ExerciseDate,
exerciseDate = ExerciseDate,
exerciseType = ExerciseType,
hasNightMarket = false,
initialSpotPrice = SpotPrice,
isForwardTrade = false,
isMoneynessOption = false,
notional = 1,
optionType = OptionType.Call,
participationRate = 1,
principalRate = 0,
riskFreeRate = RiskFreeRate,
settlementDate = ExerciseDate,
startDate = TradeDate,
strike = SpotPrice,
timeToMaturityDays = TTMDays,
tradeDate = TradeDate,
tradeId = QdpMarketID,
underlyingInstrumentType = InstrumentType,
underlyingTickers = new[] { UnderlyingCode },
volSurfaceNames = new[] { QdpMarketID },
payoffType = "ArithmeticAverage",
strikeStyle = "Fixed",
averagingPeriodStartDate = TradeDate
};
TradeValueResult tv2;
using (var mp = new MarketProxy(TradeDate, RiskFreeRate))
{
mp.SaveVolSurface(QdpMarketID, vols);
tv2 = TradeRiskCalcUtil.GetAsianOptionValue(mp, new OptionCalcParam<AsianOptionTradeParam>(vtParam)
{
pricingRequest = QdpPricingRequest.BASIC_GREEKS,
spotPrices = new[] { SpotPrice },
});
}
System.Diagnostics.Debug.WriteLine("V2 PV:" + tv2.Pv);
Assert.AreEqual(tv1.Pv, tv2.Pv, 1e-6);
Assert.AreEqual(tv1.Delta, tv2.Delta, 1e-6);
Assert.AreEqual(tv1.Gamma, tv2.Gamma, 1e-6);
Assert.AreEqual(tv1.Theta, tv2.Theta, 1e-6);
Assert.AreEqual(tv1.Rho, tv2.Rho, 1e-6);
Assert.AreEqual(tv1.Vega, tv2.Vega, 1e-6);
}
}
}
@@ -0,0 +1,401 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Qdp.ComputeService.Data.CommonModels.ValuationParams.Equity;
using Qdp.ComputeServiceV2.Data.CommonModels.TradeInfos.Options;
using Qdp.Foundation.Implementations;
using Qdp.Pricing.Base.Enums;
using Qdp.Pricing.Base.Implementations;
using Qdp.Pricing.Base.Utilities;
using Qdp.Pricing.Library.Options.Products.SyntheticSpread;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using YLErp.BLL.Calculation;
using YLErp.DBModels;
using YLErp.DBModels.Helpers;
using YLErp.QdpModule;
namespace YLErp.Modules.CalculationModule
{
[TestClass]
public class SSpreadOptionCalcTest
{
[TestMethod("测试雪球期权PV")]
public void TestSSpreadOptionCalcCrossGammas()
{
var spotPrice = 3200d;
var coefficients = new double[] { -1, 1 };
var td = new trade
{
id = 1,
AssetBookName = "test",
AssetId = 1,
BasisGap = 0,
BasisUnderlyingCode = null,
BasisUnderlyingId = 0,
BuySell = "卖出",
CalcFlag = 1,
ClientId = 1,
ClientName = "客户名称",
Comments = null,
CreateDate = DateTime.Now,
UnderlyingCode = "RB00-TA00",
UnderlyingInstrumentType = ConsGlobal.InstrumentType.CommodityFutures,
Strike = 3000,
StartDate = new DateTime(2020, 12, 1),
ExerciseDate = new DateTime(2020, 12, 31),
MaturityDate = new DateTime(2023, 12, 31),
OptionType = "看涨",
ExerciseMode = ConsGlobal.ExerciseMode.American,
Notional = 100,
NoRiskRate = 0.05,
ParticipationRate = 1,
PrincipalRate = 0,
IsAnnualized = false,
AnnualizeFactor = 1,
DividendRate = 0,
IsMoneynessOption = "否",
SpotPrice = 3200
};
var marketProxy = QdpMarketManager.Instance.GetPrebuiltMarketProxy("111");
var crossGammas = SSpreadOptionCalc.CalculateSyntheticNormalSpreadCrossGammas(
marketProxy,
"2020-12-25",
td.UnderlyingCode,
td.UnderlyingInstrumentType,
td.Strike ?? 0,
td.StartDate.Value.ToString("yyyy-MM-dd"),
td.MaturityDate.Value.ToString("yyyy-MM-dd"),
td.CallPut,
td.ExerciseMode,
spotPrice,
coefficients.ToArray(),
td.Notional,
"1111111",
td.NoRiskRate ?? 0.0,
td.BuySell,
td.ExerciseDate.Value.ToString("yyyy-MM-dd"),
td.ParticipationRate ?? 1.0,
td.PrincipalRate ?? 0.0,
td.IsAnnualized,
td.AnnualizeFactor ?? 1.0,
td.DividendRate ?? 0,
td.IsMoneynessOptionData,
td.SpotPrice ?? 0,
hasNightMarket: false,
commodityFuturesPreciseTimeMode: false,
riskFreeRateOverride: td.NoRiskRate ?? double.NaN,
dividendRateOverride: td.DividendRate ?? double.NaN);
Assert.IsNotNull(crossGammas);
var crossGammas2 = OptionCalculatorV2.CalcSSpreadCrossGammas(new DateTime(2020, 12, 25), td, new OptionValueCalcRequest(0.05)
{
spotPrices = new[] { spotPrice },
vols = new[] { 1.3 },
}, coefficients);
Assert.IsNotNull(crossGammas);
}
}
static class SSpreadOptionCalc
{
/// <summary>
/// 股指期货类型转换为商品期货类型
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static string ConvertInstrumentType(string instrumentType)
{
return ConsGlobal.InstrumentType.IsStockIF(instrumentType) ? ConsGlobal.InstrumentType.CommodityFutures : instrumentType;
}
public static OptionExercise ConvertExerciseType(string exerciseType)
{
if (exerciseType != null)
{
switch (exerciseType.ToUpper())
{
case "美式":
case "AMERICAN":
return OptionExercise.American;
default:
return OptionExercise.European;
}
}
else
{
return OptionExercise.European;
}
}
public static SyntheticNormalSpreadOptionTrade CreateSyntheticNormalSpreadOptionTrade(
string tradeId,
string volSurfaceName,
string tradeDate,
string underlyingTicker,
string underlyingInstrumentType,
double strike,
string startDate,
string endDate,
string optionType,
string exerciseType,
double notional,
string tradeType,
string exerciseDate,
double participationRate,
double principalRate,
bool isAnnualized,
double annualizeFactor,
double[] coefficients = null,
bool isMoneynessOption = false,
double initialSpotPrice = 0.0,
Dictionary<Date, double> dividends = null,
bool hasNightMarket = false,
bool commodityFuturesPreciseTimeMode = false,
double timeToMaturityDays = double.NaN,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN)
{
underlyingInstrumentType = ConvertInstrumentType(underlyingInstrumentType);
var exercise = ConvertExerciseType(exerciseType);
var optionStartDate = startDate.ToDate();
var underlyingMaturityDate = string.IsNullOrWhiteSpace(endDate) ? null : endDate.ToDate();
var temp_exerciseDate = exerciseDate.ToDate();
if (temp_exerciseDate < optionStartDate)
{
return null;
}
Date[] exerciseDates = null;
Date[] observationDates = null;
if (exercise == OptionExercise.American)
{
exerciseDates = CalendarImpl.Get("chn").BizDaysBetweenDatesInclEndDay(optionStartDate, temp_exerciseDate).ToArray();
observationDates = CalendarImpl.Get("chn").BizDaysBetweenDatesExcluStartDay(optionStartDate, temp_exerciseDate).ToArray();
}
else
{
exerciseDates = new Date[] { temp_exerciseDate };
observationDates = new Date[] { temp_exerciseDate };
}
if (underlyingInstrumentType == null)
{
throw new Exception("标的资产类型不能为空");
}
var optionDayCount = CalculatorHelper.GetTradeDayCount();
var pricingTOverride = double.IsNaN(timeToMaturityDays) ? double.NaN : QdpCalendarHelper.CalculateTFromDays(timeToMaturityDays, optionDayCount, optionStartDate.DateTime);
var syntheticNormalSpreadOption =
new SyntheticNormalSpreadOption(
optionStartDate,
exercise,
(OptionType)Enum.Parse(typeof(OptionType), optionType),
strike,
(InstrumentType)Enum.Parse(typeof(InstrumentType), underlyingInstrumentType),
CalendarImpl.Get("chn"),
optionDayCount.ToDayCountImpl(),
CurrencyCode.CNY,
CurrencyCode.CNY,
exerciseDates,
observationDates,
coefficients, // coefficients
notional,
null,
null,
0.0,
isMoneynessOption,
initialSpotPrice,
dividends,
hasNightMarket: hasNightMarket,
commodityFuturesPreciseTimeMode: commodityFuturesPreciseTimeMode,
pricingToverride: pricingTOverride,
riskFreeRateOverride: riskFreeRateOverride,
dividendRateOverride: dividendRateOverride,
participationRate: participationRate,
isAnnualized: isAnnualized,
annualizedFactor: annualizeFactor)
{
UnderlyingTickers = new string[] { underlyingTicker }
};
if (string.IsNullOrWhiteSpace(tradeId))
{
tradeId = Guid.NewGuid().ToString();
}
return new SyntheticNormalSpreadOptionTrade(
tradeId,
tradeDate.ToDate(),
syntheticNormalSpreadOption.StartDate,
syntheticNormalSpreadOption.ExerciseDates.Last(),
QdpConverter.ConvertTradeType(tradeType),
syntheticNormalSpreadOption.Notional,
0.0,
syntheticNormalSpreadOption)
{
ValuationParameters = new OptionValuationParameters("RiskFreeDiscountCurve", MarketProxy.ConstantZeroCurve, volSurfaceName, syntheticNormalSpreadOption.UnderlyingTickers[0]),
ProtectionRate = principalRate,
ParticipationRate = participationRate,
AnnualizedFactor = annualizeFactor,
OriginalNotional = TradeHelper.GetStockEqvNotional(notional * initialSpotPrice, participationRate, annualizeFactor)
};
}
public static double[] CalculateSyntheticNormalSpreadCrossGammas(
IQdpMarketProxy marketProxy,
string valueDate,
string underlyingTicker,
string underlyingInstrumentType,
double strike,
string startDate,
string endDate,
string optionType,
string exerciseType,
double spotPrice,
double[] coefficients,
double notional,
string volSurfaceName,
double riskFreeRate,
string tradeType,
string exerciseDate,
double participationRate,
double principalRate,
bool isAnnualized,
double annualizeFactor,
double dividendRate = 0.0,
bool isMoneynessOption = false,
double initialSpotPrice = 0.0,
Dictionary<Date, double> dividends = null,
bool hasNightMarket = false,
bool commodityFuturesPreciseTimeMode = false,
string engineName = null,
string discountCurveName = null,
bool ignoreSkewMap = false,
bool isForwardTrade = false,
double timeToMaturityDays = double.NaN,
double riskFreeRateOverride = double.NaN,
double dividendRateOverride = double.NaN)
{
if (coefficients == null || coefficients.Length == 1)
{
return null;
}
string tradeId = null;
var trade = CreateSyntheticNormalSpreadOptionTrade(
tradeId,
volSurfaceName,
valueDate,
underlyingTicker,
underlyingInstrumentType,
strike,
startDate,
endDate,
optionType,
exerciseType,
notional,
tradeType,
exerciseDate,
participationRate,
principalRate,
isAnnualized,
annualizeFactor,
coefficients,
isMoneynessOption,
initialSpotPrice,
dividends,
hasNightMarket,
commodityFuturesPreciseTimeMode,
timeToMaturityDays,
riskFreeRateOverride,
dividendRateOverride);
var market = marketProxy.GetQdpMarket(valueDate);
if (market == null)
{
marketProxy.CreateMarket(valueDate);
market = marketProxy.GetQdpMarket(valueDate);
if (market == null)
{
return null;
}
var volPart = QdpVolHelper.GetDefaultVolatility(1.3);
var volSurfaceWrap = new VolSurfaceBuilder
{
volSurfaceName = volSurfaceName,
volSurfaceType = "MoneynessVol",
interpolation = "BiLinear"
}.SetVectors(volPart.VolTable).Build(valueDate);
marketProxy.SaveVolSurface(volSurfaceWrap);
}
var useLocalDiscountCurve = string.IsNullOrWhiteSpace(discountCurveName);
//设置DiscountCurve
if (useLocalDiscountCurve)
{
discountCurveName = Guid.NewGuid().ToString();
var discountCurve = CalculatorHelper.CreateConstantRiskFreeCurve(discountCurveName, riskFreeRate);
marketProxy.AddYieldCurve(discountCurveName, valueDate, discountCurve);
}
//设置标的价格
marketProxy.AddStockPrice(underlyingTicker, valueDate, spotPrice);
OptionValuationParameters parameters;
if (underlyingInstrumentType == "Stock")
{
//设置DividendCurve
var dividendCurveName = Guid.NewGuid().ToString();
var dividendCurve = CalculatorHelper.CreateConstantRiskFreeCurve(dividendCurveName, dividendRate);
marketProxy.AddYieldCurve(dividendCurveName, valueDate, dividendCurve);
parameters = new OptionValuationParameters(
isForwardTrade ? MarketProxy.ConstantZeroCurve : discountCurveName,
dividendCurveName,
volSurfaceName,
underlyingTicker);
}
else
{
parameters = new OptionValuationParameters(
isForwardTrade ? MarketProxy.ConstantZeroCurve : discountCurveName,
MarketProxy.ConstantZeroCurve,
volSurfaceName,
underlyingTicker);
}
var result = trade.CalcCrossGammas(marketProxy.GetQdpMarket(valueDate), parameters);
if (result == null)
{
return null;
}
var crossGammas = new List<double>();
// 先获取返回的结果矩阵中的对角线上的元素,对应的是每个标的的Gamma
for (var i = 0; i < coefficients.Length; ++i)
{
crossGammas.Add(result[i, i]);
}
// 再获取两两对应的Cross Gamma
for (var i = 0; i < coefficients.Length - 1; ++i)
{
for (var j = i + 1; j < coefficients.Length; ++j)
{
crossGammas.Add(result[i, j]);
}
}
return crossGammas.ToArray();
}
}
}
File diff suppressed because it is too large Load Diff