Hi fellow developers,
here's some source code I wrote for generating time series using Geom. Brownian Motion (GBM):
You can find it also in the attached zip file together with a sample output.
Enjoy!
PS: let me know pls if you find any bugs. Thx.
.
here's some source code I wrote for generating time series using Geom. Brownian Motion (GBM):
Code:
/*
GBM.cpp - Geom. Brownian Motion (GBM)
2016-01-23-Sa: v0.99: init
Author: U.M. in Germany (user botpro at www.elitetrader.com)
What-it-does:
Create timeseries data using Geom. Brownian Motion (GBM)
Can generate bars of any size in time. By default it generates 30-sec bars (ie. 780 bars/day @ 23400 seconds/day)
You can modify it easily to create OHLC-data (intraday and EOD) to be used in trading systems like AmiBroker etc.
Compile using a C++11 conformant compiler like GNU g++:
g++ -Wall -O2 -std=gnu++11 GBM.cpp
Run (here on Linux):
./a.out >file.csv
Analyse:
Import file.csv into Excel or LibreOffice-Calc and do some analysis (calcs, charts etc).
Remember: the changes are normally distributed, but the resulting timeseries is log-normally distributed
because there are no negative stock prices.
By default it uses the normal-distribution. t-distribution can be used optionally (see "#if 1" in code).
For normally distributed changes the quality of the data can be verified with the following formula:
ObservedAnnualVolaPct = BarVolaPct * sqrt(252 * nBarsPerDays)
ie. in Excel/LibreOffice-Calc for the sample data the pgm creates do this:
=STDEV(D2:D16381)*100*SQRT(252*780)
It should give approximately the same VolaPct as was specified as the input volatility (ie. here 30).
FYI: Doing the same calc over data that was created using the t-distribution gives a higher number (about 38),
so using the default normal-distribution is the stochastic correct method for research.
For the difference see [2], sections "Normally Distributed Model of Asset Returns" and "Leptokurtic Model of Asset Returns".
Misc:
- You can modify the code easily to create OHLC-data (intraday and EOD) to be used in trading systems like AmiBroker etc.
- It works with trading days instead of calendar days, and a year is defined as 252 trading days (can be chgd in ctor)
- This code is a stripped down standalone usable version of my TCIntradaySpotGenerator
See also / References:
[1] https://en.wikipedia.org/wiki/Geometric_Brownian_motion
[2] https://mhittesdorf.wordpress.com/2013/12/29/introducing-quantlib-modeling-asset-prices-with-geometric-brownian-motion/
[3] https://people.sc.fsu.edu/~jburkardt/cpp_src/brownian_motion_simulation/brownian_motion_simulation.html
[4] http://www.javaquant.net/books/MCBook-1.2.pdf
[5] http://investexcel.net/geometric-brownian-motion-excel
*/
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <random>
#include <chrono>
using namespace std;
default_random_engine randgen(chrono::system_clock::now().time_since_epoch().count());
normal_distribution<double> n_dist(0.0, 1.0); // mu=0, s=1
student_t_distribution<double> t_dist(5); // 5 degrees of freedom
class GBM
{
private:
double sigma, r, q, u, t, dt, R, SD, S_t, S_tPrev;
size_t cGen;
public:
const size_t uDays, uDailyBars;
const double dbSpot0, dbAnnDriftPct, dbAnnDividPct, dbAnnVolaPct, dbTradeDaysInYear;
GBM(const double AdbSpot0 = 100.0, const double AdbAnnVolaPct = 30.0,
const size_t AuDailyBars = 780, const size_t AuDays = 252,
const double AdbTradeDaysInYear = 252.0,
const double AdbAnnDriftPct = 0.0, const double AdbAnnDividPct = 0.0
)
: uDays(AuDays), uDailyBars(AuDailyBars),
dbSpot0(AdbSpot0), dbAnnDriftPct(AdbAnnDriftPct), dbAnnDividPct(AdbAnnDividPct),
dbAnnVolaPct(AdbAnnVolaPct), dbTradeDaysInYear(AdbTradeDaysInYear)
{
r = dbAnnDriftPct / 100.0;
q = dbAnnDividPct / 100.0; // dividend yield
u = r - q;
t = double(uDays) / dbTradeDaysInYear;
dt = t / double(uDays * uDailyBars);
sigma = AdbAnnVolaPct / 100.0;
SD = sigma * sqrt(dt);
R = (u - 0.5 * sigma * sigma) * dt; // Ito's lemma
S_t = S_tPrev = dbSpot0;
cGen = 0;
}
double generate()
{ // convention: the very first spot is the initial spot
S_tPrev = S_t;
#if 1
// normal distribution (gauss)
if (cGen++) S_t *= exp(R + SD * n_dist(randgen));
#else
// t-distribution ("fat tails")
if (cGen++) S_t *= exp(R + SD * t_dist(randgen));
#endif
return S_t;
}
double get_cur() const { return S_t; }
double get_prev() const { return S_tPrev; }
};
int main()
{
// define the input params to use in GBM:
const double dbSpot0 = 100;
const double dbVolaPct = 30;
const size_t nBarsPerDay = 780; // ie. 30-sec bars @ 23400 trading seconds per day
GBM G(dbSpot0, dbVolaPct, nBarsPerDay);
// create bar data for 21 days (= 1 trading month) and print as CSV:
printf("Day,Bar,Spot,logOfChg\n");
for (size_t d = 1; d <= 21; ++d)
for (size_t b = 1; b <= nBarsPerDay; ++b)
{
const double dbCur = G.generate();
const double dbPrev = G.get_prev();
printf("%zu,%zu,%.5f,%f\n", d, b, dbCur, log(dbCur / dbPrev));
}
return 0;
}
You can find it also in the attached zip file together with a sample output.
Enjoy!
PS: let me know pls if you find any bugs. Thx.
.

