21#include <ql/errors.hpp>
22#include <ql/math/comparison.hpp>
30 const std::vector<Real>& callPrices,
31 const VolatilityType volType,
const Real shift)
32 : strikes_(
strikes), forward_(forward), callPrices_(callPrices), volType_(volType), shift_(shift) {
37 "CarrMadanMarginalProbability: shift (" <<
shift_ <<
") must be non-negative");
40 <<
strikes_.size() <<
") inconsistent to callPrices ("
43 QL_REQUIRE(!
strikes_.empty(),
"CarrMadanMarginalProbability: input moneyness is empty");
47 std::vector<Size> perm(
strikes.size());
48 std::iota(perm.begin(), perm.end(), 0);
49 std::sort(perm.begin(), perm.end(), [
this](Size a, Size b) { return strikes_[a] < strikes_[b]; });
53 for (Size i = 1; i <
strikes_.size(); ++i) {
55 "CarrMadanMarginalProbability: duplicate strikes at "
56 << perm[i - 1] <<
", " << perm[i] <<
": " <<
strikes[perm[i - 1]] <<
", " <<
strikes[perm[i]]);
60 "CarrMadanMarginalProbability: all input strikes (" <<
strikes_[perm[0]] <<
") plus shift (" <<
shift
61 <<
") must be positive or zero, got "
66 bool minusShiftStrikeAdded =
false;
71 perm.insert(perm.begin(),
strikes_.size() - 1);
72 minusShiftStrikeAdded =
true;
75 "CarrMadanMarginalProbability: call price ("
76 <<
callPrices_.front() <<
") for strike -shift (" << -
shift_ <<
") should match forward ("
84 "CarrMadanMarginalProbability: at least two strikes levels required (after adding -shift)");
88 std::vector<Real> Q(
strikes_.size() - 1);
89 for (Size i = 1; i <
strikes_.size(); ++i) {
95 std::vector<Real> BS(
strikes_.size() - 2);
96 for (Size i = 1; i <
strikes_.size() - 1; ++i) {
112 for (Size i = 0; i < Q.size(); ++i) {
113 if (Q[i] < -1.0E-10 || Q[i] > 1.0 + 1.0E-10) {
122 for (Size i = 0; i < BS.size(); ++i) {
123 if (BS[i] < -1.0E-10) {
134 q_[perm[0]] = 1.0 - Q.front();
135 for (Size i = 0; i < Q.size() - 1; ++i) {
136 q_[perm[i + 1]] = Q[i] - Q[i + 1];
138 q_[perm.back()] = Q.back();
142 if (minusShiftStrikeAdded) {
147 q_.erase(std::next(
q_.begin(), perm.front()));
163template <
class CarrMadanMarginalProbabilityClass>
165 std::ostringstream out;
166 for (Size i = 0; i < cm.strikes().size(); ++i) {
168 if (cm.callSpreadArbitrage()[i])
170 if (cm.butterflyArbitrage()[i])
172 out << (code > 0 ? std::to_string(code) :
".");
182 const std::vector<Real>& forwards,
const std::vector<std::vector<Real>>& callPrices)
183 : times_(times), moneyness_(moneyness), spot_(spot), forwards_(forwards), callPrices_(callPrices) {
188 "CarrMadanSurface: times size (" <<
times_.size() <<
") does not match callPrices outer vector size ("
191 <<
") does not match forwards size ("
194 QL_REQUIRE(!
times_.empty(),
"CarrMadanSurface: times are empty");
196 for (Size i = 1; i <
times_.size(); ++i) {
198 "CarrMadanSurface: times not increasing at index " << (i - 1) <<
", " << i <<
": " <<
times_[i - 1]
203 "CarrMadanSurface: all input times must be positive or zero, got " <<
times_.front());
205 for (Size i = 0; i <
times_.size(); ++i) {
208 <<
") should match moneyness size ("
215 for (Size i = 0; i <
times_.size(); ++i) {
228 for (Size i = 0; i <
moneyness_.size(); ++i) {
229 for (Size j = 0; j <
times_.size() - 1; ++j) {
257 std::ostringstream out;
258 for (Size j = 0; j < cm.
times().size(); ++j) {
259 for (Size i = 0; i < cm.
moneyness().size(); ++i) {
261 if (cm.
timeSlices()[j].callSpreadArbitrage()[i])
263 if (cm.
timeSlices()[j].butterflyArbitrage()[i])
267 out << (code > 0 ? std::to_string(code) :
".");
276 const std::vector<Real>& callPrices,
277 const VolatilityType volType,
279 : strikes_(
strikes), forward_(forward), callPrices_(callPrices), volType_(volType), shift_(shift) {
281 QL_REQUIRE(
strikes_.size() ==
callPrices_.size(),
"CarrMadanMarginalProbabilitySafeStrikes: strike size ("
282 <<
strikes_.size() <<
") must match callPrices size ("
303 for (Size i = 0; i <
strikes_.size(); ++i) {
311 std::vector<Real> regStrikes;
312 std::vector<Real> regCallPrices;
323 bool haveNonBoundaryStrike =
false;
324 if (volType == Normal) {
325 haveNonBoundaryStrike =
true;
327 for (
auto const& k : regStrikes) {
329 haveNonBoundaryStrike =
true;
336 if (!haveNonBoundaryStrike) {
340 q_ = std::vector<Real>(1, 1.0);
arbitrage checks based on Carr, Madan, A note on sufficient conditions for no arbitrage (2005)
std::vector< Real > callPrices_
const std::vector< bool > & butterflyArbitrage() const
bool arbitrageFree() const
std::vector< bool > callSpreadArbitrage_
std::vector< Real > strikes_
CarrMadanMarginalProbability(const std::vector< Real > &strikes, const Real forward, const std::vector< Real > &callPrices, const VolatilityType volType=ShiftedLognormal, const Real shift=0.0)
const std::vector< Real > & strikes() const
std::vector< bool > butterflyArbitrage_
const std::vector< bool > & callSpreadArbitrage() const
VolatilityType volatilityType() const
const std::vector< Real > & density() const
bool smileIsArbitrageFree_
const std::vector< Real > & callPrices() const
std::vector< Real > callPrices_
const std::vector< bool > & butterflyArbitrage() const
bool arbitrageFree() const
CarrMadanMarginalProbabilitySafeStrikes(const std::vector< Real > &strikes, const Real forward, const std::vector< Real > &callPrices, const VolatilityType volType=ShiftedLognormal, const Real shift=0.0)
std::vector< bool > callSpreadArbitrage_
std::vector< Real > strikes_
const std::vector< Real > & strikes() const
std::vector< bool > butterflyArbitrage_
const std::vector< bool > & callSpreadArbitrage() const
VolatilityType volatilityType() const
const std::vector< Real > & density() const
bool smileIsArbitrageFree_
const std::vector< Real > & callPrices() const
std::vector< bool > validStrike_
const std::vector< std::vector< bool > > & butterflyArbitrage() const
std::vector< CarrMadanMarginalProbability > timeSlices_
const std::vector< Real > & times() const
std::vector< std::vector< bool > > butterflyArbitrage_
bool arbitrageFree() const
std::vector< std::vector< bool > > calendarArbitrage_
const std::vector< std::vector< bool > > & calendarArbitrage() const
std::vector< std::vector< Real > > callPrices_
bool surfaceIsArbitrageFree_
const std::vector< std::vector< Real > > & callPrices() const
std::vector< Real > forwards_
CarrMadanSurface(const std::vector< Real > ×, const std::vector< Real > &moneyness, const Real spot, const std::vector< Real > &forwards, const std::vector< std::vector< Real > > &callPrices)
std::vector< std::vector< bool > > callSpreadArbitrage_
std::vector< Real > times_
const std::vector< Real > & forwards() const
const std::vector< std::vector< bool > > & callSpreadArbitrage() const
std::vector< Real > moneyness_
const std::vector< CarrMadanMarginalProbability > & timeSlices() const
const std::vector< Real > & moneyness() const
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
std::string arbitrageAsString(const CarrMadanMarginalProbabilityClass &cm)