|
From: Michael H. <Mic...@gm...> - 2009-04-25 15:17:01
|
Hallo Klaus, I checked out your new version of the localvolsurface.cpp from the SVN and run some extensive tests. First of all I found some minor errors in your code which are: 1. (Line 116): Real strikept = strike*dr*dq/(drpt*dqpt); This should be: Real strikept = strike*dr*dqpt/(drpt*dq); 2. (same in line 130 and 131): Real strikept = strike*dr*dq/(drpt*dqpt); Real strikemt = strike*dr*dq/(drmt*dqmt); This should be: Real strikept = strike*dr*dqpt/(drpt*dq); Real strikemt = strike*dr*dqmt/(drmt*dq); Just minor things, but think it over. You divide through the risk free discount and multiply with the dividend discount. And if you do this from t to t+dt then this is what you get. Now a few words what I figured out by extensive testing. I ran tests with MC with 100 thousand of paths. And your Bug-Fix brought some slight improvements but the problem with the second derivative still remains. It happens very often that the program crashes and tells me "negative local vol^2 at ..." The black vol surface I am using is very smooth. So this is not the reason for the problems. Much more is the problem again the numerical instability when taking the second derivative of the implied variance surface with respect to the log-moneyness. Let me introduce 2 more variables: Real z1,z2; Where z1=wp-wm; z2=wp-2.0*w+wm; Then the first derivative of w with respect to log-moneyness is: dwdy = (z1)/(2.0*dy); And the second is: d2wdy2 = (z2)/(dy*dy); It happens at different strikes and moneyness under some conditions that the d2wdy2 blows up and becomes something like -4231,12 Like a really big negative number. This obviously makes the denominator which consists of den1+den2+den3 negative and the whole program crashes. The numerator is always positive since we make sure that the implied variance is monotone increasing. So the only thing we have to take care about is that our denominator is not getting negative. I figured out (by extensive testing and only empirically) that 0.4 < den1+den2<1.3 So at least for the surfaces I was testing, this was always given. So basically we have to make sure that den3 is not getting smaller then -0.4 in order for the program not to crash. But my tests showed that den3 is either in the green zone or it blows up dramatically which gives me the suppression that we encounter some numerical problems under certain conditions. The only way how I managed for the program to run stable is by controlling z1,z2 by setting: if ((std::abs(z1) < std::abs(dy)/1000 && z1!=0.0) || std::abs(z1) > 2*std::abs(dy)) z1=0; and if ((std::abs(z2) < dy*dy/1000 && z2!=0.0) || std::abs(z2) > dy*dy) z2=0; So what I basically do is setting the first derivative to zero when it is getting so small that it doesn't really affect our result anymore anyways. Same with the second derivative. But the more critical part is setting the derivative to zero when it blows up. I just cut off all critical values. This makes (obviously) the program run stable. On the off-side I cant really say for sure (at least till now) how big the impacts of this manipulation are for the precision of our results. I figured that we basically never get problems with z1 which means with the first derivative. But we set the second derivative which means z2 quit often to zero when it falls out of the good range. For some reason I don't get rid of the feeling that we would be better of to work completely without the second derivative since it seems to be impossible to get this numerically under control. But would that probably make the dupire formula useless to us? Do you have any Ideas? Or can I help you with this issue any further? Greetings, Michael PS: is it possible to write a paper which examines under which conditions (what range for what variables and what interdependencies of the variables and what restrictions to the shape of the black vol surface) the formula is theoretically/mathematically possible (i.e. doesn't get negative) and then use this new gained knowledge to make our code stable with the smallest impact possible to the precision of the result? -----Original Message----- From: Klaus Spanderen [mailto:kl...@sp...] Sent: Freitag, 24. April 2009 00:07 To: qua...@li... Cc: Michael Heckl; Ferdinando Ametrano Subject: Re: [Quantlib-dev] LocalvolSurface.cpp Hi Michael, you wrote > To be a bit more precise the problem lies in the second derivative of the > black variance with respect to the strike. Even if I take surfaces without > Smile/Skew, i.e. flat ones, I still get the problem there. This is because > (wp-2.0*w+wm) is not exactly zero but very very small. And this gets > devided by 0. 000000000001. To me the root of the problem was the line dy = ((y!=0.0) ? y*0.000001 : 0.000001); For ve y small y this code leads to unrealistic small dy and to numerical problems during the calculation of the difference quotient. Therfore I've changed it into dy = ((std::fabs(y) > 0.001) ? y*0.0001 : 0.000001); and at least for my tests the numerical problems with the difference quotient disappeared. (pls see the latest version of localvolsurface.cpp in the SVN repository). Could you test this fix using your test cases? regards Klaus |