@@ -38,6 +38,7 @@ void BlackOvernightIndexedCouponPricer::initialize(const FloatingRateCoupon& cou
3838 }
3939 swapletRate_ = coupon_->underlying ()->rate ();
4040 effectiveIndexFixing_ = coupon_->underlying ()->effectiveIndexFixing ();
41+ effectiveCapletVolatility_ = effectiveFloorletVolatility_ = Null<Real>();
4142}
4243
4344Real BlackOvernightIndexedCouponPricer::optionletRateGlobal (Option::Type optionType, Real effStrike) const {
@@ -55,23 +56,35 @@ Real BlackOvernightIndexedCouponPricer::optionletRateGlobal(Option::Type optionT
5556 return gearing_ * std::max (a - b, 0.0 );
5657 } else {
5758 // not yet determined, use Black model
58- // for the standard deviation see Lyashenko, Mercurio, Looking forward to backward looking rates, section 6.3.
59- // the idea is to dampen the average volatility sigma between the fixing start and fixing end date by a
60- // linear function going from (fixing start, 1) to (fixing end, 0)
6159 QL_REQUIRE (!capletVolatility ().empty (), " BlackOvernightIndexedCouponPricer: missing optionlet volatility" );
6260 std::vector<Date> fixingDates = coupon_->underlying ()->fixingDates ();
6361 QL_REQUIRE (!fixingDates.empty (), " BlackOvernightIndexedCouponPricer: empty fixing dates" );
64- Real fixingStartTime = capletVolatility ()->timeFromReference (fixingDates.front ());
65- Real fixingEndTime = capletVolatility ()->timeFromReference (fixingDates.back ());
66- Real sigma = capletVolatility ()->volatility (
67- std::max (fixingDates.front (), capletVolatility ()->referenceDate () + 1 ), effStrike);
68- Real T = std::max (fixingStartTime, 0.0 );
69- if (!close_enough (fixingEndTime, T))
70- T += std::pow (fixingEndTime - T, 3.0 ) / std::pow (fixingEndTime - fixingStartTime, 2.0 ) / 3.0 ;
71- Real stdDev = sigma * std::sqrt (T);
72- Real shift = capletVolatility ()->displacement ();
7362 bool shiftedLn = capletVolatility ()->volatilityType () == ShiftedLognormal;
74- Rate fixing = shiftedLn ? blackFormula (optionType, effStrike, effectiveIndexFixing_, stdDev, 1.0 , shift)
63+ Real shift = capletVolatility ()->displacement ();
64+ Real stdDev;
65+ Real effectiveTime = capletVolatility ()->timeFromReference (fixingDates.back ());
66+ if (effectiveVolatilityInput ()) {
67+ // vol input is effective, i.e. we use a plain black model
68+ stdDev = capletVolatility ()->volatility (fixingDates.back (), effStrike) * std::sqrt (effectiveTime);
69+ } else {
70+ // vol input is not effective:
71+ // for the standard deviation see Lyashenko, Mercurio, Looking forward to backward looking rates,
72+ // section 6.3. the idea is to dampen the average volatility sigma between the fixing start and fixing end
73+ // date by a linear function going from (fixing start, 1) to (fixing end, 0)
74+ Real fixingStartTime = capletVolatility ()->timeFromReference (fixingDates.front ());
75+ Real fixingEndTime = capletVolatility ()->timeFromReference (fixingDates.back ());
76+ Real sigma = capletVolatility ()->volatility (
77+ std::max (fixingDates.front (), capletVolatility ()->referenceDate () + 1 ), effStrike);
78+ Real T = std::max (fixingStartTime, 0.0 );
79+ if (!close_enough (fixingEndTime, T))
80+ T += std::pow (fixingEndTime - T, 3.0 ) / std::pow (fixingEndTime - fixingStartTime, 2.0 ) / 3.0 ;
81+ stdDev = sigma * std::sqrt (T);
82+ }
83+ if (optionType == Option::Type::Call)
84+ effectiveCapletVolatility_ = stdDev / std::sqrt (effectiveTime);
85+ else
86+ effectiveFloorletVolatility_ = stdDev / std::sqrt (effectiveTime);
87+ Real fixing = shiftedLn ? blackFormula (optionType, effStrike, effectiveIndexFixing_, stdDev, 1.0 , shift)
7588 : bachelierBlackFormula (optionType, effStrike, effectiveIndexFixing_, stdDev, 1.0 );
7689 return gearing_ * fixing;
7790 }
@@ -88,6 +101,10 @@ Real cappedFlooredRate(Real r, Option::Type optionType, Real k) {
88101} // namespace
89102
90103Real BlackOvernightIndexedCouponPricer::optionletRateLocal (Option::Type optionType, Real effStrike) const {
104+
105+ QL_REQUIRE (!effectiveVolatilityInput (),
106+ " BlackAverageONIndexedCouponPricer::optionletRateLocal() does not support effective volatility input." );
107+
91108 // We compute a rate and a rawRate such that
92109 // rate * tau * nominal is the amount of the coupon with locally (i.e. daily) capped / floored rates
93110 // rawRate * tau * nominal is the amount of the coupon without capping / flooring the rate
@@ -262,6 +279,7 @@ void BlackAverageONIndexedCouponPricer::initialize(const FloatingRateCoupon& cou
262279 }
263280 swapletRate_ = coupon_->underlying ()->rate ();
264281 forwardRate_ = (swapletRate_ - coupon_->underlying ()->spread ()) / coupon_->underlying ()->gearing ();
282+ effectiveCapletVolatility_ = effectiveFloorletVolatility_ = Null<Real>();
265283}
266284
267285Real BlackAverageONIndexedCouponPricer::optionletRateGlobal (Option::Type optionType, Real effStrike) const {
@@ -285,24 +303,40 @@ Real BlackAverageONIndexedCouponPricer::optionletRateGlobal(Option::Type optionT
285303 QL_REQUIRE (!capletVolatility ().empty (), " BlackAverageONIndexedCouponPricer: missing optionlet volatility" );
286304 std::vector<Date> fixingDates = coupon_->underlying ()->fixingDates ();
287305 QL_REQUIRE (!fixingDates.empty (), " BlackAverageONIndexedCouponPricer: empty fixing dates" );
288- Real fixingStartTime = capletVolatility ()->timeFromReference (fixingDates.front ());
289- Real fixingEndTime = capletVolatility ()->timeFromReference (fixingDates.back ());
290- QL_REQUIRE (!close_enough (fixingEndTime, fixingStartTime),
291- " BlackAverageONIndexedCouponPricer: fixingStartTime = fixingEndTime = " << fixingStartTime);
292- Real sigma = capletVolatility ()->volatility (
293- std::max (fixingDates.front (), capletVolatility ()->referenceDate () + 1 ), effStrike);
294- Real stdDev = sigma * std::sqrt (std::max (fixingStartTime, 0.0 ) +
295- std::pow (fixingEndTime - std::max (fixingStartTime, 0.0 ), 3.0 ) /
296- std::pow (fixingEndTime - fixingStartTime, 2.0 ) / 3.0 );
297- Real shift = capletVolatility ()->displacement ();
298306 bool shiftedLn = capletVolatility ()->volatilityType () == ShiftedLognormal;
299- Rate fixing = shiftedLn ? blackFormula (optionType, effStrike, forwardRate_, stdDev, 1.0 , shift)
307+ Real shift = capletVolatility ()->displacement ();
308+ Real stdDev;
309+ Real effectiveTime = capletVolatility ()->timeFromReference (fixingDates.back ());
310+ if (effectiveVolatilityInput ()) {
311+ // vol input is effective, i.e. we use a plain black model
312+ stdDev = capletVolatility ()->volatility (fixingDates.back (), effStrike) * std::sqrt (effectiveTime);
313+ } else {
314+ // vol input is not effective:
315+ Real fixingStartTime = capletVolatility ()->timeFromReference (fixingDates.front ());
316+ Real fixingEndTime = capletVolatility ()->timeFromReference (fixingDates.back ());
317+ QL_REQUIRE (!close_enough (fixingEndTime, fixingStartTime),
318+ " BlackAverageONIndexedCouponPricer: fixingStartTime = fixingEndTime = " << fixingStartTime);
319+ Real sigma = capletVolatility ()->volatility (
320+ std::max (fixingDates.front (), capletVolatility ()->referenceDate () + 1 ), effStrike);
321+ stdDev = sigma * std::sqrt (std::max (fixingStartTime, 0.0 ) +
322+ std::pow (fixingEndTime - std::max (fixingStartTime, 0.0 ), 3.0 ) /
323+ std::pow (fixingEndTime - fixingStartTime, 2.0 ) / 3.0 );
324+ }
325+ if (optionType == Option::Type::Call)
326+ effectiveCapletVolatility_ = stdDev / std::sqrt (effectiveTime);
327+ else
328+ effectiveFloorletVolatility_ = stdDev / std::sqrt (effectiveTime);
329+ Real fixing = shiftedLn ? blackFormula (optionType, effStrike, forwardRate_, stdDev, 1.0 , shift)
300330 : bachelierBlackFormula (optionType, effStrike, forwardRate_, stdDev, 1.0 );
301331 return gearing_ * fixing;
302332 }
303333}
304334
305335Real BlackAverageONIndexedCouponPricer::optionletRateLocal (Option::Type optionType, Real effStrike) const {
336+
337+ QL_REQUIRE (!effectiveVolatilityInput (),
338+ " BlackAverageONIndexedCouponPricer::optionletRateLocal() does not support effective volatility input." );
339+
306340 // We compute a rate and a rawRate such that
307341 // rate * tau * nominal is the amount of the coupon with locally (i.e. daily) capped / floored rates
308342 // rawRate * tau * nominal is the amount of the coupon without capping / flooring the rate
0 commit comments