Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
List of all members
EquityVolCurve Class Reference

Wrapper class for building Equity volatility structures. More...

#include <ored/marketdata/equityvolcurve.hpp>

+ Collaboration diagram for EquityVolCurve:

Public Member Functions

Constructors
 EquityVolCurve ()
 Default constructor. More...
 
 EquityVolCurve (Date asof, EquityVolatilityCurveSpec spec, const Loader &loader, const CurveConfigurations &curveConfigs, const QuantLib::Handle< QuantExt::EquityIndex2 > &eqIndex, const std::map< std::string, QuantLib::ext::shared_ptr< EquityCurve > > &requiredEquityCurves={}, const std::map< std::string, QuantLib::ext::shared_ptr< EquityVolCurve > > &requiredEquityVolCurves={}, const std::map< std::string, QuantLib::ext::shared_ptr< FXVolCurve > > &requiredFxVolCurves={}, const std::map< std::string, QuantLib::ext::shared_ptr< CorrelationCurve > > &requiredCorrelationCurves={}, const Market *market=nullptr, const bool buildCalibrationInfo=true)
 Detailed constructor. More...
 

Inspectors

EquityVolatilityCurveSpec spec_
 
QuantLib::ext::shared_ptr< BlackVolTermStructurevol_
 
QuantLib::Calendar calendar_
 
QuantLib::Currency currency_
 
QuantLib::DayCounter dayCounter_
 
QuantLib::Date maxExpiry_
 
QuantLib::ext::shared_ptr< VolatilityConfigvolatilityConfig_
 
QuantLib::ext::shared_ptr< FxEqCommVolCalibrationInfocalibrationInfo_
 
const EquityVolatilityCurveSpecspec () const
 
void buildVolatility (const QuantLib::Date &asof, const EquityVolatilityCurveConfig &vc, const ConstantVolatilityConfig &cvc, const Loader &loader)
 Build a volatility structure from a single constant volatility quote. More...
 
void buildVolatility (const QuantLib::Date &asof, const EquityVolatilityCurveConfig &vc, const VolatilityCurveConfig &vcc, const Loader &loader)
 Build a volatility curve from a 1-D curve of volatility quotes. More...
 
void buildVolatility (const QuantLib::Date &asof, EquityVolatilityCurveConfig &vc, const VolatilityStrikeSurfaceConfig &vssc, const Loader &loader, const QuantLib::Handle< QuantExt::EquityIndex2 > &eqIndex)
 Build a volatility surface from a collection of expiry and absolute strike pairs. More...
 
void buildVolatility (const QuantLib::Date &asof, EquityVolatilityCurveConfig &vc, const VolatilityMoneynessSurfaceConfig &vmsc, const Loader &loader, const QuantLib::Handle< QuantExt::EquityIndex2 > &eqIndex)
 Build a volatility surface from a collection of expiry and moneyness strike pairs. More...
 
void buildVolatility (const QuantLib::Date &asof, EquityVolatilityCurveConfig &vc, const VolatilityDeltaSurfaceConfig &vdsc, const Loader &loader, const QuantLib::Handle< QuantExt::EquityIndex2 > &eqIndex)
 Build a volatility surface from a collection of expiry and strike delta pairs. More...
 
void buildVolatility (const QuantLib::Date &asof, const EquityVolatilityCurveSpec &spec, const CurveConfigurations &curveConfigs, const ProxyVolatilityConfig &epvc, const map< string, QuantLib::ext::shared_ptr< EquityCurve > > &eqCurves, const map< string, QuantLib::ext::shared_ptr< EquityVolCurve > > &eqVolCurves, const map< string, QuantLib::ext::shared_ptr< FXVolCurve > > &fxVolCurves, const map< string, QuantLib::ext::shared_ptr< CorrelationCurve > > &requiredCorrelationCurves, const Market *fxIndices=nullptr)
 Build a volatility surface as a proxy from another volatility surface. More...
 
void buildCalibrationInfo (const QuantLib::Date &asof, const CurveConfigurations &curveConfigs, const EquityVolatilityCurveConfig &config, const Handle< QuantExt::EquityIndex2 > &eqIndex)
 Build the calibration info. More...
 
const QuantLib::ext::shared_ptr< BlackVolTermStructure > & volTermStructure () const
 
const QuantLib::ext::shared_ptr< FxEqCommVolCalibrationInfo > & calibrationInfo () const
 

Detailed Description

Wrapper class for building Equity volatility structures.

Definition at line 44 of file equityvolcurve.hpp.

Constructor & Destructor Documentation

◆ EquityVolCurve() [1/2]

Default constructor.

Definition at line 49 of file equityvolcurve.hpp.

49{}

◆ EquityVolCurve() [2/2]

EquityVolCurve ( Date  asof,
EquityVolatilityCurveSpec  spec,
const Loader loader,
const CurveConfigurations curveConfigs,
const QuantLib::Handle< QuantExt::EquityIndex2 > &  eqIndex,
const std::map< std::string, QuantLib::ext::shared_ptr< EquityCurve > > &  requiredEquityCurves = {},
const std::map< std::string, QuantLib::ext::shared_ptr< EquityVolCurve > > &  requiredEquityVolCurves = {},
const std::map< std::string, QuantLib::ext::shared_ptr< FXVolCurve > > &  requiredFxVolCurves = {},
const std::map< std::string, QuantLib::ext::shared_ptr< CorrelationCurve > > &  requiredCorrelationCurves = {},
const Market market = nullptr,
const bool  buildCalibrationInfo = true 
)

Detailed constructor.

Member Function Documentation

◆ spec()

const EquityVolatilityCurveSpec & spec ( ) const

Definition at line 62 of file equityvolcurve.hpp.

62{ return spec_; }
EquityVolatilityCurveSpec spec_
+ Here is the caller graph for this function:

◆ buildVolatility() [1/6]

void buildVolatility ( const QuantLib::Date &  asof,
const EquityVolatilityCurveConfig vc,
const ConstantVolatilityConfig cvc,
const Loader loader 
)

Build a volatility structure from a single constant volatility quote.

◆ buildVolatility() [2/6]

void buildVolatility ( const QuantLib::Date &  asof,
const EquityVolatilityCurveConfig vc,
const VolatilityCurveConfig vcc,
const Loader loader 
)

Build a volatility curve from a 1-D curve of volatility quotes.

◆ buildVolatility() [3/6]

void buildVolatility ( const QuantLib::Date &  asof,
EquityVolatilityCurveConfig vc,
const VolatilityStrikeSurfaceConfig vssc,
const Loader loader,
const QuantLib::Handle< QuantExt::EquityIndex2 > &  eqIndex 
)

Build a volatility surface from a collection of expiry and absolute strike pairs.

◆ buildVolatility() [4/6]

void buildVolatility ( const QuantLib::Date &  asof,
EquityVolatilityCurveConfig vc,
const VolatilityMoneynessSurfaceConfig vmsc,
const Loader loader,
const QuantLib::Handle< QuantExt::EquityIndex2 > &  eqIndex 
)

Build a volatility surface from a collection of expiry and moneyness strike pairs.

◆ buildVolatility() [5/6]

void buildVolatility ( const QuantLib::Date &  asof,
EquityVolatilityCurveConfig vc,
const VolatilityDeltaSurfaceConfig vdsc,
const Loader loader,
const QuantLib::Handle< QuantExt::EquityIndex2 > &  eqIndex 
)

Build a volatility surface from a collection of expiry and strike delta pairs.

Definition at line 768 of file equityvolcurve.cpp.

770 {
771
772 using boost::adaptors::transformed;
773 using boost::algorithm::join;
774
775 DLOG("EquityVolCurve: start building 2-D volatility delta strike surface");
776
777 QL_REQUIRE(vdsc.quoteType() == MarketDatum::QuoteType::RATE_LNVOL,
778 "EquityVolCurve: only quote type"
779 << " RATE_LNVOL is currently supported for a 2-D volatility delta strike surface.");
780
781 // Parse, sort and check the vector of configured put deltas
782 vector<Real> putDeltas = parseVectorOfValues<Real>(vdsc.putDeltas(), &parseReal);
783 sort(putDeltas.begin(), putDeltas.end(), [](Real x, Real y) { return !close(x, y) && x < y; });
784 QL_REQUIRE(adjacent_find(putDeltas.begin(), putDeltas.end(), [](Real x, Real y) { return close(x, y); }) ==
785 putDeltas.end(),
786 "EquityVolCurve: The configured put deltas contain duplicates");
787 DLOG("EquityVolCurve: Parsed " << putDeltas.size() << " unique configured put deltas");
788 DLOG("EquityVolCurve: Put deltas are: " << join(putDeltas | transformed([](Real d) { return ore::data::to_string(d); }), ","));
789
790 // Parse, sort descending and check the vector of configured call deltas
791 vector<Real> callDeltas = parseVectorOfValues<Real>(vdsc.callDeltas(), &parseReal);
792 sort(callDeltas.begin(), callDeltas.end(), [](Real x, Real y) { return !close(x, y) && x > y; });
793 QL_REQUIRE(adjacent_find(callDeltas.begin(), callDeltas.end(), [](Real x, Real y) { return close(x, y); }) ==
794 callDeltas.end(),
795 "EquityVolCurve: The configured call deltas contain duplicates");
796 DLOG("EquityVolCurve: Parsed " << callDeltas.size() << " unique configured call deltas");
797 DLOG("EquityVolCurve: Call deltas are: " << join(callDeltas | transformed([](Real d) { return ore::data::to_string(d); }), ","));
798
799 // Expiries may be configured with a wildcard or given explicitly
800 bool expWc = false;
801 if (find(vdsc.expiries().begin(), vdsc.expiries().end(), "*") != vdsc.expiries().end()) {
802 expWc = true;
803 QL_REQUIRE(vdsc.expiries().size() == 1, "Wild card expiry specified but more expiries also specified.");
804 DLOG("EquityVolCurve: Have expiry wildcard pattern " << vdsc.expiries()[0]);
805 }
806
807 // Map to hold the rows of the equity volatility matrix. The keys are the expiry dates and the values are the
808 // vectors of volatilities, one for each configured delta.
809 map<Date, vector<Real>> surfaceData;
810
811 // Number of strikes = number of put deltas + ATM + number of call deltas
812 Size numStrikes = putDeltas.size() + 1 + callDeltas.size();
813
814 // Count the number of quotes added. We check at the end that we have added all configured quotes.
815 Size quotesAdded = 0;
816
817 // Configured delta and Atm types.
818 DeltaVolQuote::DeltaType deltaType = parseDeltaType(vdsc.deltaType());
819 DeltaVolQuote::AtmType atmType = parseAtmType(vdsc.atmType());
820 boost::optional<DeltaVolQuote::DeltaType> atmDeltaType;
821 if (!vdsc.atmDeltaType().empty()) {
822 atmDeltaType = parseDeltaType(vdsc.atmDeltaType());
823 }
824
825 // Populate the configured strikes.
826 vector<QuantLib::ext::shared_ptr<BaseStrike>> strikes;
827 for (const auto& pd : putDeltas) {
828 strikes.push_back(QuantLib::ext::make_shared<DeltaStrike>(deltaType, Option::Put, pd));
829 }
830 strikes.push_back(QuantLib::ext::make_shared<AtmStrike>(atmType, atmDeltaType));
831 for (const auto& cd : callDeltas) {
832 strikes.push_back(QuantLib::ext::make_shared<DeltaStrike>(deltaType, Option::Call, cd));
833 }
834
835 // Read the quotes to fill the expiry dates and vols matrix.
836 std::ostringstream ss;
837 ss << MarketDatum::InstrumentType::EQUITY_OPTION << "/" << vdsc.quoteType() << "/" << vc.equityId() << "/"
838 << vc.ccy() << "/*";
839 Wildcard w(ss.str());
840 for (const auto& md : loader.get(w, asof)) {
841
842 QL_REQUIRE(md->asofDate() == asof, "MarketDatum asofDate '" << md->asofDate() << "' <> asof '" << asof << "'");
843
844 auto q = QuantLib::ext::dynamic_pointer_cast<EquityOptionQuote>(md);
845 QL_REQUIRE(q, "Internal error: could not downcast MarketDatum '" << md->name() << "' to EquityOptionQuote");
846 QL_REQUIRE(q->eqName() == vc.equityId(), "EquityOptionQuote eqName '"
847 << q->eqName() << "' <> EquityVolatilityCurveConfig equityId '"
848 << vc.equityId() << "'");
849 QL_REQUIRE(q->ccy() == vc.ccy(),
850 "EquityOptionQuote ccy '" << q->ccy() << "' <> EquityVolatilityCurveConfig ccy '" << vc.ccy() << "'");
851 QL_REQUIRE(q->quoteType() == vdsc.quoteType(),
852 "EquityOptionQuote quoteType '" << q->quoteType() << "' <> VolatilityMoneynessSurfaceConfig quoteType '" << vdsc.quoteType() << "'");
853
854 // Iterator to one of the configured strikes.
855 vector<QuantLib::ext::shared_ptr<BaseStrike>>::iterator strikeIt;
856
857 if (expWc) {
858 // Check if quote's strike is in the configured strikes and continue if it is not.
859 strikeIt = find_if(strikes.begin(), strikes.end(),
860 [&q](QuantLib::ext::shared_ptr<BaseStrike> s) { return *s == *q->strike(); });
861 if (strikeIt == strikes.end())
862 continue;
863 } else {
864 // If we have explicitly configured expiries and the quote is not in the configured quotes continue.
865 auto it = find(vc.quotes().begin(), vc.quotes().end(), q->name());
866 if (it == vc.quotes().end())
867 continue;
868
869 // Check if quote's strike is in the configured strikes.
870 // It should be as we have selected from the explicitly configured quotes in the last step.
871 strikeIt = find_if(strikes.begin(), strikes.end(),
872 [&q](QuantLib::ext::shared_ptr<BaseStrike> s) { return *s == *q->strike(); });
873 QL_REQUIRE(strikeIt != strikes.end(),
874 "EquityVolCurve: The quote '"
875 << q->name()
876 << "' is in the list of configured quotes but does not match any of the configured strikes");
877 }
878
879 // Position of quote in vector of strikes
880 Size pos = std::distance(strikes.begin(), strikeIt);
881
882 // Process the quote
883 Date eDate;
884 QuantLib::ext::shared_ptr<Expiry> expiry = parseExpiry(q->expiry());
885 if (auto expiryDate = QuantLib::ext::dynamic_pointer_cast<ExpiryDate>(expiry)) {
886 eDate = expiryDate->expiryDate();
887 } else if (auto expiryPeriod = QuantLib::ext::dynamic_pointer_cast<ExpiryPeriod>(expiry)) {
888 // We may need more conventions here eventually.
889 eDate = calendar_.adjust(asof + expiryPeriod->expiryPeriod());
890 }
891
892 // Add quote to surface
893 if (surfaceData.count(eDate) == 0)
894 surfaceData[eDate] = vector<Real>(numStrikes, Null<Real>());
895
896 QL_REQUIRE(surfaceData[eDate][pos] == Null<Real>(),
897 "EquityVolCurve: Quote " << q->name() << " provides a duplicate quote for the date " << io::iso_date(eDate)
898 << " and strike " << *q->strike());
899 surfaceData[eDate][pos] = q->quote()->value();
900 quotesAdded++;
901
902 TLOG("EquityVolCurve: Added quote " << q->name() << ": (" << io::iso_date(eDate) << "," << *q->strike() << "," << fixed
903 << setprecision(9) << "," << q->quote()->value() << ")");
904 }
905
906 DLOG("EquityVolCurve: EquityVolCurve: added " << quotesAdded << " quotes in building delta strike surface.");
907
908 // Check the data gathered.
909 if (expWc) {
910 // If the expiries were configured via a wildcard, check that no surfaceData element has a Null<Real>().
911 for (const auto& kv : surfaceData) {
912 for (Size j = 0; j < numStrikes; j++) {
913 QL_REQUIRE(kv.second[j] != Null<Real>(), "EquityVolCurve: Volatility for expiry date "
914 << io::iso_date(kv.first) << " and strike " << *strikes[j]
915 << " not found. Cannot proceed with a sparse matrix.");
916 }
917 }
918 } else {
919 // If expiries were configured explicitly, the number of configured quotes should equal the
920 // number of quotes added.
921 QL_REQUIRE(vc.quotes().size() == quotesAdded,
922 "EquityVolCurve: Found " << quotesAdded << " quotes, but " << vc.quotes().size() << " quotes required by config.");
923 }
924
925 // Populate the matrix of volatilities and the expiry dates.
926 vector<Date> expiryDates;
927 Matrix vols(surfaceData.size(), numStrikes);
928 for (const auto row : surfaceData | boost::adaptors::indexed(0)) {
929 expiryDates.push_back(row.value().first);
930 copy(row.value().second.begin(), row.value().second.end(), vols.row_begin(row.index()));
931 }
932
933 // Need to multiply each put delta value by -1 before passing it to the BlackVolatilitySurfaceDelta ctor
934 // i.e. a put delta of 0.25 that is passed in to the config must be -0.25 when passed to the ctor.
935 transform(putDeltas.begin(), putDeltas.end(), putDeltas.begin(), [](Real pd) { return -1.0 * pd; });
936 DLOG("EquityVolCurve: Multiply put deltas by -1.0 before creating BlackVolatilitySurfaceDelta object.");
937 DLOG("EquityVolCurve: Put deltas are: " << join(putDeltas | transformed([](Real d) { return ore::data::to_string(d); }), ","));
938
939 // Set the strike extrapolation which only matters if extrapolation is turned on for the whole surface.
940 // BlackVolatilitySurfaceDelta time extrapolation is hard-coded to constant in volatility.
941 bool flatExtrapolation = true;
942 if (vdsc.extrapolation()) {
943
944 auto strikeExtrapType = parseExtrapolation(vdsc.strikeExtrapolation());
945 if (strikeExtrapType == Extrapolation::UseInterpolator) {
946 TLOG("EquityVolCurve: Strike extrapolation switched to using interpolator.");
947 flatExtrapolation = false;
948 } else if (strikeExtrapType == Extrapolation::None) {
949 TLOG("EquityVolCurve: Strike extrapolation cannot be turned off on its own so defaulting to flat.");
950 } else if (strikeExtrapType == Extrapolation::Flat) {
951 TLOG("EquityVolCurve: Strike extrapolation has been set to flat.");
952 } else {
953 TLOG("EquityVolCurve: Strike extrapolation " << strikeExtrapType << " not expected so default to flat.");
954 }
955
956 auto timeExtrapType = parseExtrapolation(vdsc.timeExtrapolation());
957 if (timeExtrapType != Extrapolation::Flat) {
958 TLOG("EquityVolCurve: BlackVolatilitySurfaceDelta only supports flat volatility extrapolation in the time direction");
959 }
960 } else {
961 TLOG("EquityVolCurve: Extrapolation is turned off for the whole surface so the time and"
962 << " strike extrapolation settings are ignored");
963 }
964
965 // Time interpolation
966 if (vdsc.timeInterpolation() != "Linear") {
967 TLOG("EquityVolCurve: BlackVolatilitySurfaceDelta only supports linear time interpolation.");
968 }
969
970 // Strike interpolation
972 if (vdsc.strikeInterpolation() == "Linear") {
973 im = InterpolatedSmileSection::InterpolationMethod::Linear;
974 } else if (vdsc.strikeInterpolation() == "NaturalCubic") {
975 im = InterpolatedSmileSection::InterpolationMethod::NaturalCubic;
976 } else if (vdsc.strikeInterpolation() == "FinancialCubic") {
977 im = InterpolatedSmileSection::InterpolationMethod::FinancialCubic;
978 } else if (vdsc.strikeInterpolation() == "CubicSpline") {
979 im = InterpolatedSmileSection::InterpolationMethod::CubicSpline;
980 } else {
981 im = InterpolatedSmileSection::InterpolationMethod::Linear;
982 DLOG("EquityVolCurve: BlackVolatilitySurfaceDelta does not support strike interpolation '" << vdsc.strikeInterpolation()
983 << "' so setting it to linear.");
984 }
985
986 // set max expiry date (used in buildCalibrationInfo())
987 if (!expiryDates.empty())
988 maxExpiry_ = expiryDates.back();
989
990 DLOG("EquityVolCurve: Creating BlackVolatilitySurfaceDelta object");
991 bool hasAtm = true;
992 vol_ = QuantLib::ext::make_shared<BlackVolatilitySurfaceDelta>(
993 asof, expiryDates, putDeltas, callDeltas, hasAtm, vols, dayCounter_, calendar_, eqIndex->equitySpot(),
994 eqIndex->equityForecastCurve(), eqIndex->equityDividendCurve(), deltaType, atmType, atmDeltaType, 0 * Days,
995 deltaType, atmType, atmDeltaType, im, flatExtrapolation);
996
997 DLOG("EquityVolCurve: Setting BlackVolatilitySurfaceDelta extrapolation to " << to_string(vdsc.extrapolation()));
998 vol_->enableExtrapolation(vdsc.extrapolation());
999
1000 DLOG("EquityVolCurve: finished building 2-D volatility delta strike surface");
1001}
QuantLib::ext::shared_ptr< BlackVolTermStructure > vol_
QuantLib::Calendar calendar_
QuantLib::DayCounter dayCounter_
DeltaVolQuote::AtmType parseAtmType(const std::string &s)
Convert text to QuantLib::DeltaVolQuote::AtmType.
Definition: parsers.cpp:746
DeltaVolQuote::DeltaType parseDeltaType(const std::string &s)
Convert text to QuantLib::DeltaVolQuote::DeltaType.
Definition: parsers.cpp:763
#define DLOG(text)
Logging Macro (Level = Debug)
Definition: log.hpp:554
#define TLOG(text)
Logging Macro (Level = Data)
Definition: log.hpp:556
std::string to_string(const LocationInfo &l)
Definition: ast.cpp:28
Extrapolation parseExtrapolation(const string &s)
Parse Extrapolation from string.
Definition: parsers.cpp:778
QuantLib::ext::shared_ptr< Expiry > parseExpiry(const string &strExpiry)
Parse an Expiry from its string representation, strExpiry.
Definition: expiry.cpp:110
vector< Real > strikes
+ Here is the call graph for this function:

◆ buildVolatility() [6/6]

void buildVolatility ( const QuantLib::Date &  asof,
const EquityVolatilityCurveSpec spec,
const CurveConfigurations curveConfigs,
const ProxyVolatilityConfig epvc,
const map< string, QuantLib::ext::shared_ptr< EquityCurve > > &  eqCurves,
const map< string, QuantLib::ext::shared_ptr< EquityVolCurve > > &  eqVolCurves,
const map< string, QuantLib::ext::shared_ptr< FXVolCurve > > &  fxVolCurves,
const map< string, QuantLib::ext::shared_ptr< CorrelationCurve > > &  requiredCorrelationCurves,
const Market fxIndices = nullptr 
)

Build a volatility surface as a proxy from another volatility surface.

Definition at line 1003 of file equityvolcurve.cpp.

1009 {
1010
1011 DLOG("EquityVolCurve: start building proxy vol surface");
1012 // get all the configurations and the curve needed for proxying
1013 auto config = *curveConfigs.equityVolCurveConfig(spec.curveConfigID());
1014
1015 auto proxy = epvc.proxyVolatilityCurve();
1016 auto eqConfig = *curveConfigs.equityCurveConfig(spec.curveConfigID());
1017 auto proxyConfig = *curveConfigs.equityCurveConfig(proxy);
1018 auto proxyVolConfig = *curveConfigs.equityVolCurveConfig(proxy);
1019
1020 // create dummy specs to look up the required curves
1021 EquityCurveSpec eqSpec(eqConfig.currency(), spec.curveConfigID());
1022 EquityCurveSpec proxySpec(proxyConfig.currency(), proxy);
1023 EquityVolatilityCurveSpec proxyVolSpec(proxyVolConfig.ccy(), proxy);
1024
1025 // Get all necessary curves
1026 auto curve = eqCurves.find(eqSpec.name());
1027 QL_REQUIRE(curve != eqCurves.end(), "EquityVolCurve: Failed to find equity curve, when building equity vol curve " << spec.name());
1028 auto proxyCurve = eqCurves.find(proxySpec.name());
1029 QL_REQUIRE(proxyCurve != eqCurves.end(), "EquityVolCurve: Failed to find equity curve for proxy "
1030 << proxySpec.name() << ", when building equity vol curve "
1031 << spec.name());
1032 auto proxyVolCurve = eqVolCurves.find(proxyVolSpec.name());
1033 QL_REQUIRE(proxyVolCurve != eqVolCurves.end(), "EquityVolCurve: Failed to find equity vol curve for proxy "
1034 << proxyVolSpec.name() << ", when building equity vol curve "
1035 << spec.name());
1036
1037 // check the currency against the proxy surface currrency
1038
1039 QuantLib::ext::shared_ptr<BlackVolTermStructure> fxSurface;
1040 QuantLib::ext::shared_ptr<FxIndex> fxIndex;
1041 QuantLib::ext::shared_ptr<QuantExt::CorrelationTermStructure> correlation;
1042 if (config.ccy() != proxyVolConfig.ccy() && fxIndices != nullptr) {
1043 QL_REQUIRE(!epvc.fxVolatilityCurve().empty(), "EquityVolCurve: FXVolatilityCurve must be provided for Equity vol config " <<
1044 spec.curveConfigID() << " as proxy currencies if different from equity currency.");
1045 QL_REQUIRE(!epvc.correlationCurve().empty(), "EquityVolCurve: CorrelationCurve must be provided for Equity vol config " <<
1046 spec.curveConfigID() << " as proxy currencies if different from equity currency.");
1047
1048 // get the fx vol surface
1049 QL_REQUIRE(epvc.fxVolatilityCurve().size() == 6, "EquityVolCurve: FXVolatilityCurve provided " << epvc.fxVolatilityCurve() <<
1050 " for Equity vol config " << spec.curveConfigID() << " must be of length 6, and of form CC1CCY2 e.g EURUSD");
1051 string proxyVolForCcy = epvc.fxVolatilityCurve().substr(0, 3);
1052 string proxyVolDomCcy = epvc.fxVolatilityCurve().substr(3, 3);
1053 FXVolatilityCurveSpec fxSpec(proxyVolForCcy, proxyVolDomCcy, epvc.fxVolatilityCurve());
1054 auto volIt = fxVolCurves.find(fxSpec.name());
1055 if (volIt == fxVolCurves.end())
1056 QL_FAIL("EquityVolCurve: cannot find required Fx volatility surface " << fxSpec.name() << " to build proxy vol surface for " << eqSpec.name());
1057 fxSurface = volIt->second->volTermStructure();
1058
1059 // check if the fx vol surface needs to be inverted
1060 if (proxyVolForCcy != proxyVolConfig.ccy()) {
1061 Handle<BlackVolTermStructure> hFx(fxSurface);
1062 fxSurface = QuantLib::ext::make_shared<QuantExt::BlackInvertedVolTermStructure>(hFx);
1063 fxSurface->enableExtrapolation();
1064 }
1065
1066 fxIndex = fxIndices->fxIndex(proxyVolConfig.ccy() + config.ccy()).currentLink();
1067
1068 CorrelationCurveSpec corrSpec(epvc.correlationCurve());
1069 auto corrIt = requiredCorrelationCurves.find(corrSpec.name());
1070 if (corrIt == requiredCorrelationCurves.end())
1071 QL_FAIL("EquityVolCurve: cannot find required correlation curve " << epvc.correlationCurve() << " to build proxy vol surface for " << eqSpec.name());
1072 correlation = corrIt->second->corrTermStructure();
1073 }
1074
1075 vol_ = QuantLib::ext::make_shared<BlackVolatilitySurfaceProxy>(proxyVolCurve->second->volTermStructure(), curve->second->equityIndex(),
1076 proxyCurve->second->equityIndex(), fxSurface, fxIndex, correlation);
1077}
const std::string & curveConfigID() const
Definition: curvespec.hpp:83
string name() const
returns the unique curve name
Definition: curvespec.hpp:78
const EquityVolatilityCurveSpec & spec() const
vector< string > curveConfigs
+ Here is the call graph for this function:

◆ buildCalibrationInfo()

void buildCalibrationInfo ( const QuantLib::Date &  asof,
const CurveConfigurations curveConfigs,
const EquityVolatilityCurveConfig config,
const Handle< QuantExt::EquityIndex2 > &  eqIndex 
)

Build the calibration info.

Definition at line 1079 of file equityvolcurve.cpp.

1081 {
1082
1083 DLOG("EquityVolCurve: Building calibration info for eq vol surface");
1084
1085 try {
1086
1087 ReportConfig rc = effectiveReportConfig(curveConfigs.reportConfigEqVols(), config.reportConfig());
1088
1089 bool reportOnDeltaGrid = *rc.reportOnDeltaGrid();
1090 bool reportOnMoneynessGrid = *rc.reportOnMoneynessGrid();
1091 std::vector<Real> moneyness = *rc.moneyness();
1092 std::vector<std::string> deltas = *rc.deltas();
1093 std::vector<Period> expiries = *rc.expiries();
1094
1095 calibrationInfo_ = QuantLib::ext::make_shared<FxEqCommVolCalibrationInfo>();
1096
1097 DeltaVolQuote::AtmType atmType = DeltaVolQuote::AtmType::AtmDeltaNeutral;
1098 DeltaVolQuote::DeltaType deltaType = DeltaVolQuote::DeltaType::Fwd;
1099
1100 if (auto vdsc = QuantLib::ext::dynamic_pointer_cast<VolatilityDeltaSurfaceConfig>(volatilityConfig_)) {
1101 atmType = parseAtmType(vdsc->atmType());
1102 deltaType = parseDeltaType(vdsc->deltaType());
1103 }
1104
1105 calibrationInfo_->dayCounter = config.dayCounter().empty() ? "na" : config.dayCounter();
1106 calibrationInfo_->calendar = config.calendar().empty() ? "na" : config.calendar();
1107 calibrationInfo_->atmType = ore::data::to_string(atmType);
1108 calibrationInfo_->deltaType = ore::data::to_string(deltaType);
1109 calibrationInfo_->longTermAtmType = ore::data::to_string(atmType);
1110 calibrationInfo_->longTermDeltaType = ore::data::to_string(deltaType);
1111 calibrationInfo_->switchTenor = "na";
1112 calibrationInfo_->riskReversalInFavorOf = "na";
1113 calibrationInfo_->butterflyStyle = "na";
1114
1115 std::vector<Real> times, forwards, rfDisc, divDisc;
1116 for (auto const& p : expiries) {
1117 Date d = vol_->optionDateFromTenor(p);
1118 calibrationInfo_->expiryDates.push_back(d);
1119 times.push_back(vol_->dayCounter().empty() ? Actual365Fixed().yearFraction(asof, d)
1120 : vol_->timeFromReference(d));
1121 forwards.push_back(eqIndex->forecastFixing(d));
1122 rfDisc.push_back(eqIndex->equityForecastCurve()->discount(d));
1123 divDisc.push_back(eqIndex->equityDividendCurve()->discount(d));
1124 }
1125
1126 calibrationInfo_->times = times;
1127 calibrationInfo_->forwards = forwards;
1128
1129 std::vector<std::vector<Real>> callPricesDelta(times.size(), std::vector<Real>(deltas.size(), 0.0));
1130 std::vector<std::vector<Real>> callPricesMoneyness(times.size(), std::vector<Real>(moneyness.size(), 0.0));
1131
1132 calibrationInfo_->isArbitrageFree = true;
1133
1134 if (reportOnDeltaGrid) {
1135 calibrationInfo_->deltas = deltas;
1136 calibrationInfo_->deltaCallPrices =
1137 std::vector<std::vector<Real>>(times.size(), std::vector<Real>(deltas.size(), 0.0));
1138 calibrationInfo_->deltaPutPrices =
1139 std::vector<std::vector<Real>>(times.size(), std::vector<Real>(deltas.size(), 0.0));
1140 calibrationInfo_->deltaGridStrikes =
1141 std::vector<std::vector<Real>>(times.size(), std::vector<Real>(deltas.size(), 0.0));
1142 calibrationInfo_->deltaGridProb =
1143 std::vector<std::vector<Real>>(times.size(), std::vector<Real>(deltas.size(), 0.0));
1144 calibrationInfo_->deltaGridImpliedVolatility =
1145 std::vector<std::vector<Real>>(times.size(), std::vector<Real>(deltas.size(), 0.0));
1146 calibrationInfo_->deltaGridCallSpreadArbitrage =
1147 std::vector<std::vector<bool>>(times.size(), std::vector<bool>(deltas.size(), true));
1148 calibrationInfo_->deltaGridButterflyArbitrage =
1149 std::vector<std::vector<bool>>(times.size(), std::vector<bool>(deltas.size(), true));
1150 TLOG("EquityVolCurve: Delta surface arbitrage analysis result (no calendar spread arbitrage included):");
1151 Real maxTime = QL_MAX_REAL;
1152 if (maxExpiry_ != Date()) {
1153 if (vol_->dayCounter().empty())
1154 maxTime = Actual365Fixed().yearFraction(asof, maxExpiry_);
1155 else
1156 maxTime = vol_->timeFromReference(maxExpiry_);
1157 }
1158 DeltaVolQuote::AtmType at;
1159 DeltaVolQuote::DeltaType dt;
1160 for (Size i = 0; i < times.size(); ++i) {
1161 Real t = times[i];
1162 at = atmType;
1163 dt = deltaType;
1164 // for times after the last quoted expiry we use artificial conventions to avoid problems with strike
1165 // from delta conversions: we use fwd delta always and ATM DNS
1166 if (t > maxTime) {
1167 at = DeltaVolQuote::AtmDeltaNeutral;
1168 dt = DeltaVolQuote::Fwd;
1169 }
1170 bool validSlice = true;
1171 for (Size j = 0; j < deltas.size(); ++j) {
1172 DeltaString d(deltas[j]);
1173 try {
1174 Real strike;
1175 if (d.isAtm()) {
1176 strike = QuantExt::getAtmStrike(dt, at, eqIndex->equitySpot()->value(), rfDisc[i],
1177 divDisc[i], vol_, t);
1178 } else if (d.isCall()) {
1179 strike = QuantExt::getStrikeFromDelta(Option::Call, d.delta(), dt,
1180 eqIndex->equitySpot()->value(), rfDisc[i], divDisc[i],
1181 vol_, t);
1182 } else {
1183 strike =
1184 QuantExt::getStrikeFromDelta(Option::Put, d.delta(), dt, eqIndex->equitySpot()->value(),
1185 rfDisc[i], divDisc[i], vol_, t);
1186 }
1187 Real stddev = std::sqrt(vol_->blackVariance(t, strike));
1188 callPricesDelta[i][j] = blackFormula(Option::Call, strike, forwards[i], stddev);
1189
1190 if (d.isPut()) {
1191 calibrationInfo_->deltaPutPrices[i][j] = blackFormula(Option::Put, strike, forwards[i], stddev, rfDisc[i]);
1192 } else {
1193 calibrationInfo_->deltaCallPrices[i][j] = blackFormula(Option::Call, strike, forwards[i], stddev, rfDisc[i]);
1194 }
1195
1196 calibrationInfo_->deltaGridStrikes[i][j] = strike;
1197 calibrationInfo_->deltaGridImpliedVolatility[i][j] = stddev / std::sqrt(t);
1198 } catch (const std::exception& e) {
1199 validSlice = false;
1200 TLOG("EquityVolCurve: error for time " << t << " delta " << deltas[j] << ": " << e.what());
1201 }
1202 }
1203 if (validSlice) {
1204 try {
1205 QuantExt::CarrMadanMarginalProbability cm(calibrationInfo_->deltaGridStrikes[i], forwards[i],
1206 callPricesDelta[i]);
1207 calibrationInfo_->deltaGridCallSpreadArbitrage[i] = cm.callSpreadArbitrage();
1208 calibrationInfo_->deltaGridButterflyArbitrage[i] = cm.butterflyArbitrage();
1209 if (!cm.arbitrageFree())
1210 calibrationInfo_->isArbitrageFree = false;
1211 calibrationInfo_->deltaGridProb[i] = cm.density();
1213 } catch (const std::exception& e) {
1214 TLOG("error for time " << t << ": " << e.what());
1215 calibrationInfo_->isArbitrageFree = false;
1216 TLOGGERSTREAM("..(invalid slice)..");
1217 }
1218 } else {
1219 calibrationInfo_->isArbitrageFree = false;
1220 TLOGGERSTREAM("..(invalid slice)..");
1221 }
1222 }
1223 TLOG("EquityVolCurve: Delta surface arbitrage analysis completed.");
1224 }
1225
1226 if (reportOnMoneynessGrid) {
1227 calibrationInfo_->moneyness = moneyness;
1228 calibrationInfo_->moneynessCallPrices =
1229 std::vector<std::vector<Real>>(times.size(), std::vector<Real>(moneyness.size(), 0.0));
1230 calibrationInfo_->moneynessPutPrices =
1231 std::vector<std::vector<Real>>(times.size(), std::vector<Real>(moneyness.size(), 0.0));
1232 calibrationInfo_->moneynessGridStrikes =
1233 std::vector<std::vector<Real>>(times.size(), std::vector<Real>(moneyness.size(), 0.0));
1234 calibrationInfo_->moneynessGridProb =
1235 std::vector<std::vector<Real>>(times.size(), std::vector<Real>(moneyness.size(), 0.0));
1236 calibrationInfo_->moneynessGridImpliedVolatility =
1237 std::vector<std::vector<Real>>(times.size(), std::vector<Real>(moneyness.size(), 0.0));
1238 calibrationInfo_->moneynessGridCallSpreadArbitrage =
1239 std::vector<std::vector<bool>>(times.size(), std::vector<bool>(moneyness.size(), true));
1240 calibrationInfo_->moneynessGridButterflyArbitrage =
1241 std::vector<std::vector<bool>>(times.size(), std::vector<bool>(moneyness.size(), true));
1242 calibrationInfo_->moneynessGridCalendarArbitrage =
1243 std::vector<std::vector<bool>>(times.size(), std::vector<bool>(moneyness.size(), true));
1244 for (Size i = 0; i < times.size(); ++i) {
1245 Real t = times[i];
1246 for (Size j = 0; j < moneyness.size(); ++j) {
1247 try {
1248 Real strike = moneyness[j] * forwards[i];
1249 calibrationInfo_->moneynessGridStrikes[i][j] = strike;
1250 Real stddev = std::sqrt(vol_->blackVariance(t, strike));
1251 callPricesMoneyness[i][j] = blackFormula(Option::Call, strike, forwards[i], stddev);
1252 calibrationInfo_->moneynessGridImpliedVolatility[i][j] = stddev / std::sqrt(t);
1253 if (moneyness[j] >= 1) {
1254 calibrationInfo_->moneynessCallPrices[i][j] = blackFormula(Option::Call, strike, forwards[i], stddev, rfDisc[i]);
1255 } else {
1256 calibrationInfo_->moneynessPutPrices[i][j] = blackFormula(Option::Put, strike, forwards[i], stddev, rfDisc[i]);
1257 };
1258 } catch (const std::exception& e) {
1259 TLOG("EquityVolCurve: error for time " << t << " moneyness " << moneyness[j] << ": " << e.what());
1260 }
1261 }
1262 }
1263 if (!times.empty() && !moneyness.empty()) {
1264 try {
1265 QuantExt::CarrMadanSurface cm(times, moneyness, eqIndex->equitySpot()->value(), forwards,
1266 callPricesMoneyness);
1267 for (Size i = 0; i < times.size(); ++i) {
1268 calibrationInfo_->moneynessGridProb[i] = cm.timeSlices()[i].density();
1269 }
1270 calibrationInfo_->moneynessGridCallSpreadArbitrage = cm.callSpreadArbitrage();
1271 calibrationInfo_->moneynessGridButterflyArbitrage = cm.butterflyArbitrage();
1272 calibrationInfo_->moneynessGridCalendarArbitrage = cm.calendarArbitrage();
1273 if (!cm.arbitrageFree())
1274 calibrationInfo_->isArbitrageFree = false;
1275 TLOG("EquityVolCurve: Moneyness surface Arbitrage analysis result:");
1277 } catch (const std::exception& e) {
1278 TLOG("EquityVolCurve: error: " << e.what());
1279 calibrationInfo_->isArbitrageFree = false;
1280 }
1281 TLOG("EquityVolCurve: Moneyness surface Arbitrage analysis completed:");
1282 }
1283 }
1284
1285 DLOG("EquityVolCurve: Building calibration info for eq vol surface completed.");
1286
1287 } catch (std::exception& e) {
1288 QL_FAIL("EquityVolCurve: calibration info building failed: " << e.what());
1289 } catch (...) {
1290 QL_FAIL("EquityVolCurve: calibration info building failed: unknown error");
1291 }
1292}
QuantLib::ext::shared_ptr< VolatilityConfig > volatilityConfig_
QuantLib::ext::shared_ptr< FxEqCommVolCalibrationInfo > calibrationInfo_
#define TLOGGERSTREAM(text)
Definition: log.hpp:633
Real getAtmStrike(DeltaVolQuote::DeltaType dt, DeltaVolQuote::AtmType at, Real spot, Real domDiscount, Real forDiscount, boost::shared_ptr< BlackVolTermStructure > vol, Real t, Real accuracy, Size maxIterations)
std::string arbitrageAsString(const CarrMadanMarginalProbabilityClass &cm)
Real getStrikeFromDelta(Option::Type optionType, Real delta, DeltaVolQuote::DeltaType dt, Real spot, Real domDiscount, Real forDiscount, boost::shared_ptr< BlackVolTermStructure > vol, Real t, Real accuracy, Size maxIterations)
ReportConfig effectiveReportConfig(const ReportConfig &globalConfig, const ReportConfig &localConfig)
+ Here is the call graph for this function:

◆ volTermStructure()

const QuantLib::ext::shared_ptr< BlackVolTermStructure > & volTermStructure ( ) const

Definition at line 100 of file equityvolcurve.hpp.

100{ return vol_; }

◆ calibrationInfo()

const QuantLib::ext::shared_ptr< FxEqCommVolCalibrationInfo > & calibrationInfo ( ) const

Definition at line 101 of file equityvolcurve.hpp.

101{ return calibrationInfo_; }

Member Data Documentation

◆ spec_

Definition at line 106 of file equityvolcurve.hpp.

◆ vol_

QuantLib::ext::shared_ptr<BlackVolTermStructure> vol_
private

Definition at line 107 of file equityvolcurve.hpp.

◆ calendar_

QuantLib::Calendar calendar_
private

Definition at line 108 of file equityvolcurve.hpp.

◆ currency_

QuantLib::Currency currency_
private

Definition at line 109 of file equityvolcurve.hpp.

◆ dayCounter_

QuantLib::DayCounter dayCounter_
private

Definition at line 110 of file equityvolcurve.hpp.

◆ maxExpiry_

QuantLib::Date maxExpiry_
private

Definition at line 111 of file equityvolcurve.hpp.

◆ volatilityConfig_

QuantLib::ext::shared_ptr<VolatilityConfig> volatilityConfig_
private

Definition at line 112 of file equityvolcurve.hpp.

◆ calibrationInfo_

QuantLib::ext::shared_ptr<FxEqCommVolCalibrationInfo> calibrationInfo_
private

Definition at line 113 of file equityvolcurve.hpp.