|
From: U.Mutlu <um...@mu...> - 2023-09-04 20:51:13
|
Hello, I've the following, possibly very old, code for generating GBM values. It practically requires that nIntervalls_in_t (ie. timeSteps) must be >= 2 b/c the first generated value always equals the passed startingPrice. Is this behavior hardcoded in QuantLib or can this be overridden via a setting or function call, so that it shall not return always the startingPrice as the first value : 2nd question is: can this code be simplified and modernized by using C++ methods of C++11, preferably w/o any Boost dependence, for example using std::normal_distribution instead of the Box-Muller kludge below? Cf. also the many distribtion types and the RNGs in C++11 at https://en.cppreference.com/w/cpp/numeric/random ... // instantiate Geometric Brownian Motion (GBM) stochastic process const auto& gbm = boost::shared_ptr<StochasticProcess>(new GeometricBrownianMotionProcess(startingPrice, drift_mu, vola_sigma)); // generate sequence of normally distributed random numbers from uniform distribution using Box-Muller transformation BigInteger seed = SeedGenerator::instance().get(); typedef BoxMullerGaussianRng<MersenneTwisterUniformRng> MersenneBoxMuller; MersenneTwisterUniformRng mersenneRng(seed); MersenneBoxMuller boxMullerRng(mersenneRng); RandomSequenceGenerator<MersenneBoxMuller> gsg(nIntervals_in_t, boxMullerRng); // generate simulated path of stock price PathGenerator<RandomSequenceGenerator<MersenneBoxMuller>> gbmPathGenerator(gbm, t, nIntervals_in_t, gsg, false); const Path& samplePath = gbmPathGenerator.next().value; for (size_t i = 1; i < nIntervals_in_t; ++i) // skipping 0th elem as it's startingPrice { const auto Sx = samplePath.at(i); ... |
|
From: Luigi B. <lui...@gm...> - 2023-09-05 12:47:38
|
Hello,
the first point of the path corresponds to t=0, therefore it contains
the starting price. Passing timeSteps = 1 to the generator should give you
sample paths with 2 points, that is, the starting point and the one after
the time step.
Modernizing the code would be nice, but it must be done without breaking
backward compatibility with the current code (i.e., user code that uses
QuantLib and that compiles today should still compile with the next
version) so it's probably not worth the effort. I'd be happy to be proven
wrong, though.
Hope this helps,
Luigi
On Mon, Sep 4, 2023 at 10:54 PM U.Mutlu <um...@mu...> wrote:
> Hello,
>
> I've the following, possibly very old, code for generating GBM values.
> It practically requires that nIntervalls_in_t (ie. timeSteps)
> must be >= 2 b/c the first generated value always equals the passed
> startingPrice.
> Is this behavior hardcoded in QuantLib or can this be overridden
> via a setting or function call, so that it shall not return always
> the startingPrice as the first value :
>
> 2nd question is: can this code be simplified and modernized by using
> C++ methods of C++11, preferably w/o any Boost dependence, for example
> using std::normal_distribution instead of the Box-Muller kludge below?
> Cf. also the many distribtion types and the RNGs in C++11 at
> https://en.cppreference.com/w/cpp/numeric/random
>
> ...
>
> // instantiate Geometric Brownian Motion (GBM) stochastic process
> const auto& gbm = boost::shared_ptr<StochasticProcess>(new
> GeometricBrownianMotionProcess(startingPrice, drift_mu, vola_sigma));
>
> // generate sequence of normally distributed random numbers from
> uniform
> distribution using Box-Muller transformation
> BigInteger seed = SeedGenerator::instance().get();
> typedef BoxMullerGaussianRng<MersenneTwisterUniformRng>
> MersenneBoxMuller;
> MersenneTwisterUniformRng mersenneRng(seed);
> MersenneBoxMuller boxMullerRng(mersenneRng);
>
> RandomSequenceGenerator<MersenneBoxMuller> gsg(nIntervals_in_t,
> boxMullerRng);
>
> // generate simulated path of stock price
> PathGenerator<RandomSequenceGenerator<MersenneBoxMuller>>
> gbmPathGenerator(gbm, t, nIntervals_in_t, gsg, false);
> const Path& samplePath = gbmPathGenerator.next().value;
>
> for (size_t i = 1; i < nIntervals_in_t; ++i) // skipping 0th elem
> as
> it's startingPrice
> {
> const auto Sx = samplePath.at(i);
> ...
>
>
>
>
> _______________________________________________
> QuantLib-users mailing list
> Qua...@li...
> https://lists.sourceforge.net/lists/listinfo/quantlib-users
>
|
|
From: Jonathan S. <sw...@gm...> - 2023-09-05 12:53:12
|
Hello, 1. Could you provide a bit more info on why you want a path containing a single step? The typical use case for PathGenerator is for many steps, with the value at the first step (i.e. at t_0) being equal to the starting price. Maybe what you're looking for isn't a path, but something else. 2. It might be possible to reimplement the RNG classes using the C++11 standard library <random> header but it's a risky change in my opinion because there might be numerical differences on different platforms, which users could be sensitive to. Also in terms of Boost dependence, the only part of your code snippet that depends on boost is boost::shared_ptr, which should be replaced with QuantLib::ext::shared_ptr. None of the RNG classes in your snippet themselves depend on Boost directly. On Tue, Sep 5, 2023 at 5:53 AM U.Mutlu <um...@mu...> wrote: > Hello, > > I've the following, possibly very old, code for generating GBM values. > It practically requires that nIntervalls_in_t (ie. timeSteps) > must be >= 2 b/c the first generated value always equals the passed > startingPrice. > Is this behavior hardcoded in QuantLib or can this be overridden > via a setting or function call, so that it shall not return always > the startingPrice as the first value : > > 2nd question is: can this code be simplified and modernized by using > C++ methods of C++11, preferably w/o any Boost dependence, for example > using std::normal_distribution instead of the Box-Muller kludge below? > Cf. also the many distribtion types and the RNGs in C++11 at > https://en.cppreference.com/w/cpp/numeric/random > > ... > > // instantiate Geometric Brownian Motion (GBM) stochastic process > const auto& gbm = boost::shared_ptr<StochasticProcess>(new > GeometricBrownianMotionProcess(startingPrice, drift_mu, vola_sigma)); > > // generate sequence of normally distributed random numbers from > uniform > distribution using Box-Muller transformation > BigInteger seed = SeedGenerator::instance().get(); > typedef BoxMullerGaussianRng<MersenneTwisterUniformRng> > MersenneBoxMuller; > MersenneTwisterUniformRng mersenneRng(seed); > MersenneBoxMuller boxMullerRng(mersenneRng); > > RandomSequenceGenerator<MersenneBoxMuller> gsg(nIntervals_in_t, > boxMullerRng); > > // generate simulated path of stock price > PathGenerator<RandomSequenceGenerator<MersenneBoxMuller>> > gbmPathGenerator(gbm, t, nIntervals_in_t, gsg, false); > const Path& samplePath = gbmPathGenerator.next().value; > > for (size_t i = 1; i < nIntervals_in_t; ++i) // skipping 0th elem > as > it's startingPrice > { > const auto Sx = samplePath.at(i); > ... > > > > > _______________________________________________ > QuantLib-users mailing list > Qua...@li... > https://lists.sourceforge.net/lists/listinfo/quantlib-users > |
|
From: U.Mutlu <um...@mu...> - 2023-09-09 23:39:59
|
Jonathan S. <sw...@gm...> on 2023-09-05 12:53:12 wrote: > > 1. Could you provide a bit more info on why you want a path containing a > single step? The typical use case for PathGenerator is for many steps, with > the value at the first step (i.e. at t_0) being equal to the starting > price. Maybe what you're looking for isn't a path, but something else. I'm doing a research on GBM implementations out there in the field by running Monte Carlo simulations to see how good the generated data is by comparing it to the table below of timeSteps and ExpectedHitRate%: I was not able to use timeSteps=1 in QuantLib for verifying the first case below. timeSteps >= 2 is doable, but as said the results are far from being correct. Recent postings here suggested to replace the use of Box-Muller class by some other classes; I'm now trying to test that too. Expected HitRate% for 1SD around initial stock price for varying GBM timeSteps This table is an extension of https://en.wikipedia.org/wiki/68–95–99.7_rule as there only timeStep=1 is given. These numbers are exact values, not the result of simulations. Params used: S=100 rPct=0 qPct=0 IV=30(s=0.3) DIY=365.00 DTE=365(t=1 dt=1 dd=365) zFm=-1(SxFm=74.081822) zTo=++1(SxTo=134.985881) timeSteps Expected_HitRate 1 68.2689% cf. https://en.wikipedia.org/wiki/68–95–99.7_rule 2 76.2695% 3 79.2918% 4 80.7919% 5 81.6648% 6 82.2304% 7 82.6265% 8 82.9197% 9 83.1461% 10 83.3265% 15 83.8653% 20 84.1337% 25 84.2942% 30 84.4010% 35 84.4771% 40 84.5341% 45 84.5785% 50 84.6139% 60 84.6671% 70 84.7050% 80 84.7334% 90 84.7555% 100 84.7732% 500 84.9003% 1000 84.9162% 10000 84.9305% 100000 84.9319% 1000000 84.9320% 10000000 84.9320% |
|
From: U.Mutlu <um...@mu...> - 2023-09-10 00:24:23
|
Hmm, are you sure Luigi? B/c it does not work.
I have such a code and using timeSteps = 1.
How do you mean one can get out 2 values from this path?
Is it possible at all? It seems not possible.
...
PathGenerator<RandomSequenceGenerator<MersenneBoxMuller>>
gbmPathGenerator(gbm, t, timeSteps, gsg, false);
const Path& samplePath = gbmPathGenerator.next().value;
for (size_t j = 0; j < timeSteps; ++j)
{
const auto Sx = samplePath.at(j);
...
Luigi B. <lui...@gm...> on 2023-09-05 12:47:38 wrote:
>
> the first point of the path corresponds to t=0, therefore it contains
> the starting price. Passing timeSteps = 1 to the generator should give you
> sample paths with 2 points, that is, the starting point and the one after
> the time step.
>
>
> > On Mon, Sep 4, 2023 at 10:54 PM U.Mutlu <um...@mu...> wrote:
> >
> > I've the following, possibly very old, code for generating GBM values.
> > It practically requires that nIntervalls_in_t (ie. timeSteps)
> > must be >= 2 b/c the first generated value always equals the passed
> > startingPrice.
> > Is this behavior hardcoded in QuantLib or can this be overridden
> > via a setting or function call, so that it shall not return always
> > the startingPrice as the first value
|
|
From: U.Mutlu <um...@mu...> - 2023-09-10 00:39:46
|
Finally found the bug:
The loop below must be so:
for (size_t j = 1; j <= timeSteps; ++j)
This of course invalidates the old test results.
Will see how good the results are now, but it looks much better now.
U.Mutlu wrote on 09/10/23 02:24:
> Hmm, are you sure Luigi? B/c it does not work.
> I have such a code and using timeSteps = 1.
> How do you mean one can get out 2 values from this path?
> Is it possible at all? It seems not possible.
>
> ...
> PathGenerator<RandomSequenceGenerator<MersenneBoxMuller>>
> gbmPathGenerator(gbm, t, timeSteps, gsg, false);
> const Path& samplePath = gbmPathGenerator.next().value;
>
> for (size_t j = 0; j < timeSteps; ++j)
> {
> const auto Sx = samplePath.at(j);
> ...
>
>
> Luigi B. <lui...@gm...> on 2023-09-05 12:47:38 wrote:
> >
> > the first point of the path corresponds to t=0, therefore it contains
> > the starting price. Passing timeSteps = 1 to the generator should give you
> > sample paths with 2 points, that is, the starting point and the one after
> > the time step.
> >
> >
> > > On Mon, Sep 4, 2023 at 10:54 PM U.Mutlu <um...@mu...> wrote:
> > >
> > > I've the following, possibly very old, code for generating GBM values.
> > > It practically requires that nIntervalls_in_t (ie. timeSteps)
> > > must be >= 2 b/c the first generated value always equals the passed
> > > startingPrice.
> > > Is this behavior hardcoded in QuantLib or can this be overridden
> > > via a setting or function call, so that it shall not return always
> > > the startingPrice as the first value
>
>
>
>
>
> _______________________________________________
> QuantLib-users mailing list
> Qua...@li...
> https://lists.sourceforge.net/lists/listinfo/quantlib-users
|