|
From: Luigi B. <lui...@gm...> - 2023-05-22 14:27:08
|
Hello Lukasz,
I think you're probably right, the derivative should include that
factor. This said, as Francois mentioned, the "can't bracket" message is
probably raised before the solver starts using the derivative; it's still
checking that the minimum and maximum value for the yield bracket the
solution, that is, that one gives a negative error and the other gives a
positive one, otherwise the solution can't be found in that range. We'd
have the same message even if we used a solver (e.g. Brent) that doesn't
require the derivative.
I guess the workaround in C++ would be to use the overload of the
BondFunctions::yield method that takes an explicit solver (so you can avoid
Newton) and to pass an explicit xMin and xMax so your range can extend over
1376%.
Would you be so kind as to open an issue on GitHub so we can check and fix
the derivative? Thanks!
Luigi
On Mon, May 22, 2023 at 3:41 PM Lukasz Skowronek <
luk...@su...> wrote:
> Hi,
>
> Could anyone here comment on the non-inclusion of
>
> * CashFlows.npv(bond.cashflows(),...
>
> in the derivative of the npv function, as I described
>
> (in lenght, I admit) in my email some time ago?
>
> QuantLib does not seem to include this factor,
>
> but for Newton method to work for my example (independently),
>
> I needed to define the derivative with the additional
>
> CashFlows.npv factor included.
>
> Am I missing some important point or reading QuantLib
>
> code incorrectly?
>
> Best regards,
>
> Łukasz
>
>
> On 8.05.2023 10:48, Lukasz Skowronek wrote:
>
> *Warning: This message originated from outside the organization. Use
> caution when following links or opening attachments.*
>
> Hi,
>
> My name is Lukasz and I'm currently working on a small summary screen for
> various bonds
>
> that uses QuantLib under the hood.
>
> I got an error report from our testers for the following inputs:
> request.CouponPaymentsPerYear = 1;
> request.Coupon = 0.0;
> request.RedemptionPrice = 100.0;
> request.CleanPrice = 1376.0;
> request.FaceValue = 1000.0;
>
> ...
> request.ExpirationDate = new DateTime(2024, 6, 27);
> request.InvestmentDate = new DateTime(2023, 2, 9);
>
> In short, this input corresponds to a zero coupon bond priced extremely
> high (at 1376%).
>
> The error I'm getting says the solver cannot bracket the root of the
> target function.
>
> After shifting the dot in the clean price by one decimal place, I get the
> code to work
>
> as expected. Still, even if the testers did not intend to challenge my
> code with
>
> bonds priced at a 85% markup (that is, -85% yield), I would expect the
> solver to
>
> find the root for the original input.
>
> Digging into QuantLib's source code, I traced a few pieces related to how
> yield
>
> calculation is really done:
>
> In *bondfunctions.cpp (line 360):*
>
> Rate BondFunctions::yield(const Bond& bond, ...
>
> in *bondfunctions.hpp (line 160):*
>
> static Rate yield(const Solver& solver, ...
>
> in *cashflows.hpp (line 276):*
>
> static Rate yield(const Solver& solver, ...
>
> IrrFinder objFunction(leg, npv, dayCounter, compounding,
> frequency, includeSettlementDateFlows,
> settlementDate, npvDate);
> return solver.solve(objFunction, accuracy, guess, guess/10.0);
>
> and finally, in *cashflows.cpp (line 728):*
>
> CashFlows::IrrFinder::IrrFinder(const Leg& leg, ...
>
> Real CashFlows::IrrFinder::operator()(Rate y) const {
> InterestRate yield(y, dayCounter_, compounding_, frequency_);
> Real *NPV = CashFlows::npv(leg_, yield,*
> * includeSettlementDateFlows_,*
> * settlementDate_, npvDate_);*
> return *npv_ - NPV*;
> }
>
> Real CashFlows::IrrFinder::derivative(Rate y) const {
> InterestRate yield(y, dayCounter_, compounding_, frequency_);
> return *modifiedDuration(leg_, yield,*
> * includeSettlementDateFlows_,*
> * settlementDate_, npvDate_)*;
> }
>
> Is this target function + derivative correct?
>
> What worked on my side, when simulating the Newton method for finding the
> root
>
> separately, was:
> double f(double rate) {
> InterestRate ir = new InterestRate(rate, dayCounter, Compounding.
> Compounded, frequency);
> return (cleanPrice + bond.accruedAmount(investmentDate)) * bond.notional(
> investmentDate) / 100.0 -
> CashFlows.npv(bond.cashflows(), ir, false, investmentDate, investmentDate);
>
> }
> // <-- target function
> double d(double rate) {
> InterestRate ir = new InterestRate(rate, dayCounter, Compounding.
> Compounded, frequency);
> return CashFlows.modifiedDuration(bond.cashflows(), ir, false,
> investmentDate, investmentDate) * CashFlows.npv(bond.cashflows(), ir,
> false, investmentDate, investmentDate);
> }
> // <-- derivative
> (this is C# code that has QLNet as dependency)
>
> Hence, there is an additional multiplication by CashFlows.npv(..., which
> plays well with the
>
> definition of modified duration as -dPdy / P
>
> There does not seem to be a similar factor in QuantLib.
>
> Am I mistaken? Is the multiplication by NPV in the derivative handled some
> other way?
>
> Best regards,
>
> Lukasz Skowronek
>
>
>
>
> _______________________________________________
> QuantLib-users mailing lis...@li...://lists.sourceforge.net/lists/listinfo/quantlib-users
>
> _______________________________________________
> QuantLib-users mailing list
> Qua...@li...
> https://lists.sourceforge.net/lists/listinfo/quantlib-users
>
|