Parallel lives
In order to really appreciate the tools offered by QL, let us see the results first. Some simulated paths using Hull-White One-Factor model are shown in the picture below.
If one really want to start from the scratch, there are a lot of things to do in order to produce these paths on a flexible manner and handling all the complexities of the task at the same time. Thanks for QL, those days are finally over.
Legoland
Setting up desired Stochastic Process and Gaussian Sequence Generator are two main components needed in order to get this thing up and running.
Along with required process parameters (reversion speed and rate volatility), HullWhiteProcess needs Handle to YieldTermStructure object, such as PiecewiseYieldCurve, as an input.
// create Hull-White one-factor stochastic process Real reversionSpeed = 0.75; Real rateVolatility = 0.015; boost::shared_ptr<StochasticProcess1D> HW1F( new HullWhiteProcess(curveHandle, reversionSpeed, rateVolatility));
// type definition for complex declaration typedef RandomSequenceGenerator<CLGaussianRng<MersenneTwisterUniformRng>> GSG; // // create mersenne twister uniform random generator unsigned long seed = 28749; MersenneTwisterUniformRng generator(seed); // // create gaussian generator by using central limit transformation method CLGaussianRng<MersenneTwisterUniformRng> gaussianGenerator(generator); // // define maturity, number of steps per path and create gaussian sequence generator Time maturity = 5.0; Size nSteps = 1250; GSG gaussianSequenceGenerator(nSteps, gaussianGenerator); // // create path generator using Hull-White process and gaussian sequence generator PathGenerator<GSG> pathGenerator(HW1F, maturity, nSteps, gaussianSequenceGenerator, false);
Finally, PathGenerator object is created by feeding desired process and generator objects in constructor method, along with the other required parameters (maturity, number of steps). After this, PathGenerator object is ready for producing stochastic paths for its client.
The program
Example program will first create relinkable handle to PiecewiseYieldCurve object. Remember to include required files into your project from here. After this, the program creates HW1F process object and Gaussian Sequence Generator object, which are feeded into PathGenerator object. Finally, the program creates 20 stochastic paths, which are saved into Matrix object and ultimately being printed into text file for further analysis (Excel chart).
#include "PiecewiseCurveBuilder.cpp" #include <fstream> #include <string> // // type definition for complex declaration typedef RandomSequenceGenerator<CLGaussianRng<MersenneTwisterUniformRng>> GSG; // // function prototypes RelinkableHandle<YieldTermStructure> CreateCurveHandle(Date settlementDate); void PrintMatrix(const Matrix& matrix, std::string filePathName); // int main() { // request handle for piecewise USD Libor curve Date tradeDate(22, January, 2016); Settings::instance().evaluationDate() = tradeDate; Date settlementDate = UnitedKingdom().advance(tradeDate, 2, Days); RelinkableHandle<YieldTermStructure> curveHandle = CreateCurveHandle(settlementDate); // // create Hull-White one-factor stochastic process Real reversionSpeed = 0.75; Real rateVolatility = 0.015; boost::shared_ptr<StochasticProcess1D> HW1F( new HullWhiteProcess(curveHandle, reversionSpeed, rateVolatility)); // // create mersenne twister uniform random generator unsigned long seed = 28749; MersenneTwisterUniformRng generator(seed); // // create gaussian generator by using central limit transformation method CLGaussianRng<MersenneTwisterUniformRng> gaussianGenerator(generator); // // define maturity, number of steps per path and create gaussian sequence generator Time maturity = 5.0; Size nSteps = 1250; GSG gaussianSequenceGenerator(nSteps, gaussianGenerator); // // create path generator using Hull-White process and gaussian sequence generator PathGenerator<GSG> pathGenerator(HW1F, maturity, nSteps, gaussianSequenceGenerator, false); // // create matrix container for 20 generated paths Size nColumns = 20; Matrix paths(nSteps + 1, nColumns); for(unsigned int i = 0; i != paths.columns(); i++) { // request a new stochastic path from path generator QuantLib::Sample<Path> path = pathGenerator.next(); // // save generated path into container for(unsigned int j = 0; j != path.value.length(); j++) { paths[j][i] = path.value.at(j); } } // finally, print matrix content into text file PrintMatrix(paths, "C:\\temp\\HW1F.txt"); return 0; } // void PrintMatrix(const Matrix& matrix, std::string filePathName) { // open text file for input, loop through matrix rows std::ofstream file(filePathName); for(unsigned int i = 0; i != matrix.rows(); i++) { // concatenate column values into string separated by semicolon std::string stream; for(unsigned int j = 0; j != matrix.columns(); j++) { stream += (std::to_string(matrix[i][j]) + ";"); } // print string into text file file << stream << std::endl; } // close text file file.close(); } // RelinkableHandle<YieldTermStructure> CreateCurveHandle(Date settlementDate) { // create curve builder for piecewise USD Libor swap curve PiecewiseCurveBuilder<USDLibor> USDCurveBuilder(settlementDate, UnitedKingdom(), Annual, Thirty360()); // // add quotes directly into curve builder USDCurveBuilder.AddDeposit(0.0038975, 1 * Weeks); USDCurveBuilder.AddDeposit(0.004295, 1 * Months); USDCurveBuilder.AddDeposit(0.005149, 2 * Months); USDCurveBuilder.AddDeposit(0.006127, 3 * Months); USDCurveBuilder.AddFRA(0.008253, 3 * Months, 3 * Months); USDCurveBuilder.AddFRA(0.009065, 6 * Months, 3 * Months); USDCurveBuilder.AddFRA(0.01059, 9 * Months, 3 * Months); USDCurveBuilder.AddSwap(0.011459, 2 * Years); USDCurveBuilder.AddSwap(0.013745, 3 * Years); USDCurveBuilder.AddSwap(0.015475, 4 * Years); USDCurveBuilder.AddSwap(0.016895, 5 * Years); USDCurveBuilder.AddSwap(0.01813, 6 * Years); USDCurveBuilder.AddSwap(0.019195, 7 * Years); USDCurveBuilder.AddSwap(0.020115, 8 * Years); USDCurveBuilder.AddSwap(0.020905, 9 * Years); USDCurveBuilder.AddSwap(0.021595, 10 * Years); USDCurveBuilder.AddSwap(0.0222, 11 * Years); USDCurveBuilder.AddSwap(0.022766, 12 * Years); USDCurveBuilder.AddSwap(0.0239675, 15 * Years); USDCurveBuilder.AddSwap(0.025105, 20 * Years); USDCurveBuilder.AddSwap(0.025675, 25 * Years); USDCurveBuilder.AddSwap(0.026015, 30 * Years); USDCurveBuilder.AddSwap(0.026205, 40 * Years); USDCurveBuilder.AddSwap(0.026045, 50 * Years); // // return relinkable curve handle return USDCurveBuilder.GetCurveHandle(); }
Thanks for reading my blog.
-Mike
Thanks, Mikael. I've added a link to your blog on the QuantLib site at http://quantlib.org/docs.shtml
ReplyDeleteI am honored, Thank You.
ReplyDeleteHi Mikael. Just came across your blog looking for quantlib examples that calculate yield curves. Thanks a lot for posting this, it's been quite useful.
ReplyDeleteJust a question. As far as I can see your example generates short rates (yield curves with one value). The hull white model in principle allows one to build yield curves with the same number of tenors as the yield curve passed (which in your example is curveHandle). Do you know how to do this using the quantlib library?
Thanks, Mikael.
ReplyDeleteCan you give me some advice about using Quantlib in Python? My task is - create Monte Carlo simulation for two Hull_White process for correlated short rates. How should i use PathGenerator in this way?