Friday, December 29, 2017

Monte Carlo Techniques: Modeling Stock Price Action




A random walk is a stochastic process that describes a path as a succession of random steps.  To understand the basics of random walks, we will consider the case where we start off at position $y = 0$ and take a step in the vertical direction if we flip a heads on a coin toss, and one step down if we throw a tails.  The animated image in Fig 1 shows this process for four different trials.  Any given path will, of course, be random, but repeating this process numerous times allows us to calculate statistics on the collection of paths and draw some conclusions.  For example with a large number of simulations, we could estimate the probability of ending up at a given point after a specified number of steps.  This is a useful technique when it is inconvenient or impossible to work out such details by hand, and in the age of fast computers is being used more and more.

Figure 1:  A random walk along the integers.  Four different walks are shown with the position at each step being shown on the Y-axis.


Stock returns-- the percent change (we'll use day-to-day changes)-- are assumed to be normally distributed.  The assumption is there an upward drift that depends on the risk-free interest rates at the time, plus some sort of volatility term.  This volatility term is the piece that is assumed to be normally distributed.  As an aside, we can assume any type of distribution we'd like.  That's an advantage of Monte Carlo techniques--  we can do these calculations quickly with a variety of assumptions just by tweaking a bit of code.  But for this article, we'll assume a normal distribution which is not far from what is actually observed looking at historical stock data.  We will implement a random walk to simulate stock behavior using Python 2.7 along with the Numpy and Scipy libraries.

Expressed as an equation, we have,
\begin{equation} \frac{S_{i+1} - S_i}{S_i} = r\Delta t + \sqrt{\Delta t}\sigma \epsilon_i. \label{daily_returns} \end{equation}

Here, $S_i$ is the stock price on the $i$th day, $r$ is the annualized risk-free ratem that has been scaled down to whatever time step we're using, $\Delta t$ is the time step (in our case it will be one day), $\sigma$ will be the volatility that has also been scaled down to the proper time step,  $\epsilon$ is a random number sampled from the standard normal distribution (mean = 0 and standard deviation = 1). Numpy provides a built-in function that does this sampling for us, np.random.normal

While we could rearrange Eq. \ref{daily_returns} as,
\begin{equation} S_{i+1} = S_i \left(r\Delta t + \sqrt{\Delta t}\sigma \epsilon_i\right) + S_i, \label{next_day} \end{equation}
and loop over the desired number of days, we will eventually want to reuse this code for more complicated problems where we might need to do millions of simulations.  Thus, speed is of the essence.  Therefore, we'll write a vectorized implementation instead of one with loops.  At first glance, the math invoked in doing so seems a bit abstract, but it just amounts to solving a bunch of algebraic equations. 

We will introduce a bit of notation just to keep things neater and set the right-hand side of Eq. \ref{daily_returns} to
$$r\Delta t + \sqrt{\Delta t}\sigma \epsilon_i = \Lambda_i.$$

If we do a bit of algebra and write out the first few iterations of Eq.  \ref{next_day} , we see
that
\begin{equation}\begin{array}{lcl}S_0                     & = & S_0 \\S_1 - S_0 \Lambda_1 - S_0 & = &  0\\\S_2 - S_1 \Lambda_2 - S_1 & = & 0 \\S_3 - S_2 \Lambda_3 - S_2 & = & 0 \\& \vdots & \\S_i - _{i-} \Lambda_i - S_{i-1} & = & 0 .\\\end{array}\label{unrolled}\end{equation}
We can rewrite Eq. \ref{unrolled} in matrix form as,
\begin{equation} \left( \begin{matrix} 1 & 0 & 0 & 0 & \cdots & 0 \\ \Lambda_1 + 1 & -1 & 0 & 0 & \cdots & 0 \\ 0 & \Lambda_2 + 1 & -1 & 0 & \cdots & 0 \\ 0 & 0 & \Lambda_3 + 1 & -1 & \cdots & 0 \\ \vdots & \vdots & \vdots & \vdots & \ddots & \vdots \\ 0 & 0 & 0 & 0 & \Lambda_5 + 1 & -1 \end{matrix} \right) \left( \begin{array}{c} S_0 \\ S_1 \\ S_2 \\ S_3 \\ \vdots \\ S_i \\ \end{array} \right) = \left( \begin{array}{c} S_0 \\ 0 \\ 0 \\ 0 \\ \vdots \\ 0 \\ \end{array} \right). \label{simple_matrix} \end{equation}

So now we've reduced our entire problem to solving one equation.  The solution to Eq. \ref{simple_matrix} amounts to finding the inverse of a matrix.  Notice also that almost all of the entries in this matrix are zeros.  Only the entries along the main diagonal and the one immediately below are non-zero.  This is known as a sparse matrix.

Sparse matrices have several advantages.  First, since most of the entries are zero, we can store only the non-zero values in memory.  In our case, we have finite values only on the main diagonal and diagonal immediately below.  For our specific problem then, we have $2N_{\mbox{days}} + 1$ numbers to story in memory as opposed to $(N_{\mbox{days}}+1)^2$ if every entry have to be accounted for.  The second advantage is we can use sparse matrix algorithms to calculate the inverse.  This speeds up the computation immensely.

Many numerical methods libraries have have these sparse matrix algorithms included in the package and Python is no expectation with the scipy.sparse and scipy.sparse.linalg packages containing what we need.

A couple of notes and assumptions before we begin to code this:

  1.   We'll ignore the effect of weekends.  We won't try to account for the weekend by bumping up volatility for the simulated Mondays or try to account for it in some other way.
  2.  We will take the number of days per year as 252, the number of trading days rather than 365, the number of calendar days.  It is a bit ambiguous as which number to use and in practice should probably be determined by testing each against actual market data.  Since we are ignoring weekends, however, I thought it would be clearer to use the number of trading days.
  3.   We will state without justification that to scale the volatility from yearly to daily we use the formula, $\sigma_{\mbox{day}} = \sigma_{\mbox{year}} / \sqrt{252}$.
  4.  We will assume the volatility $\sigma$ remains constant over our time period.  In reality, this is not true.
  5.  For the most part, determining the values of the parameters that go into the model is not a problem, but deciding what to use for volatility is not immediately obvious.  Since we are assuming returns are normally distributed, we could look at historical price data and calculate the standard deviation of returns and use that value.  This is referred to as historical volatility.  In practice however,  it is best to use the implied volatility derived from option prices.  This is the market's estimate of what volatility will be in the time period until the expiration of the option contract.  This number is given by your trading platform or can be calculated directly from the option prices.  I have an article on how to do this calculation here.

Let's begin to translate this into Python code.  We will need the numpy and scipy libraries mentioned above as well as some basic math functions.  We will also load in the matplotlib library to plot the results.

import numpy as np
from math import sqrt
import matplotlib.pyplot as plt
import scipy.sparse
import scipy.sparse.linalg

We'll set the random seed to a constant for debugging purposes.  This makes the random number generator use the same numbers each time the program is run, which will allow one to discern whether odd numerical values are caused by fluctuating random numbers or a bug in the code.  We will also set the parameters of our simulation:  the initial stock price, the risk-free rate, time-step , and volatility.

np.random.seed(0)               #  Set a constant random seed for debugging
                                #  purposes

N_days = 30                     #  Number of days to simulate

S0 = 100.0                      #  Initial stock price
r = 0.01 / 252.0                #  Risk-free rate (daily percent)
dt = 1.0                        #  Time step (days)
sigma = 0.15 / sqrt(252.0)      #  Volatility (daily percent)

Next, we create a vector of normally distributed numbers and calculate the daily percent change of the stock.

#  Create a vector of normally distributed entries with each entry
#  corresponding to  one day
epsilon = np.random.normal( size = (N_days) )

ds_s = r * dt + sigma * sqrt(dt) * epsilon  #  Vector of daily percent
                                            #  change in the stock price

Next, we set up our sparse matrix.  The list $d$ will contain our two vectors which will form the diagonals, and the list $k$ will designate which of these vector goes along which diagonal.  Then we create the matrix $M$ with the sparse.diags command.  The option format = 'csc' tells the code to use compressed sparse column format to represent the matrix internally and is not really important for the purpose of this article.

#  We need to build the diagonals of our sparse matrix
#  All of the main diagonal is equal to -1 expect the first entry which is
#  equal to one
ones = -np.ones( (N_days + 1) ); ones[0] = 1.;

#  Define our two diagonals,  the lower and main
d = [ds_s + 1, ones]

#  the K vector tells Python which of the vectors defined in 'd' go in
#  which diagonal.  Zero corresponds to the main, and -1 to the diag
#  immediately below.
K = [-1, 0]

#  Define the sparse matrix
M = scipy.sparse.diags(d, K, format = 'csc')

Lastly, we'll define the vector $p$ which corresponds to the right-hand side of Eq. \ref{simple_matrix}.  Then we will solve for our vector of stock prices, $s$.

#  Define a column vector off all zeros expect for the first entry which is
#  our initial stock price.
p = np.zeros( (N_days + 1, 1) )
p[0] = S0

#  Solve the system  M * s = p for the vector s
s = scipy.sparse.linalg.spsolve(M, p)

Now that we have the solution, let's visualize the results.

#  Plot the results
t = np.arange(0, N_days + 1)
plt.plot(t, s, 'k')
plt.xlabel('Time (days)')
plt.ylabel('Stock Price ($)')
plt.grid(True)
plt.show()

The code above should produces the plot shown in Fig. 1.
Figure 1:  The results of a single simulation using the parameters noted above.
In and of itself, the above isn't very useful.  It returns only one random walk simulated run of a stock.  To get something useful we would need to do a large number of such simulations.  Again, we could loop over the above code to do so but we will do this in a fully vectorized way by "stacking" up our vectors in Eq. \ref{simple_matrix}.   An explicit example might be clearer.  Let's adopt the notation where $S_{i,j}$ is the price on the $i$th day in the $j$th simulation.  The if we have two simulations for five days, the column vectors in \ref{simple_matrix} would be,
\begin{equation} \left( \begin{array}{c} s_0 \\ s_{1,1} \\ s_{2,1} \\ s_{3,1} \\ s_{4,1} \\ s_{5,1} \\ s_0 \\ s_{1,2} \\ s_{2,2} \\ s_{3,2} \\ s_{4,2} \\ s_{5,2} \\ \end{array} \right), \mbox{ and} \left( \begin{array}{c} s_0 \\ 0 \\ 0 \\ 0 \\ 0 \\ 0 \\ s_0 \\ 0 \\ 0 \\ 0 \\ 0 \\ 0 \\ \end{array} \right).\end{equation}
The matrix is constructed similarly, stacking a matrix for each day along the diagonal.  In the example above, this would be,
$$\left( \begin{array}{rrrrrrrrrrrr} 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ \Lambda_{1,1} + 1 & -1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & \Lambda_{2,1} + 1 & -1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & \Lambda_{3,1} + 1 & -1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & \Lambda_{4,1} + 1 & -1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & \Lambda_{5,1} + 1 & -1 & 0 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & \Lambda_{1,2} + 1 & -1 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & \Lambda_{2,2} + 1 & -1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & \Lambda_{3,2} + 1 & -1 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & \Lambda_{4,2} + 1 & -1 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & \Lambda_{5,2} + 1 & -1 \end{array} \right).$$

Since we are going to want to use this code in other models, we will encapsulate it all in a function.   The code below constructs the vectors and matrix for multiple simulations according to the user's input.

def stock_monte_carlo(init_price, N_days, N_sims, r, sigma):

    #  Scale interest rates and volatility.  Define time-step.
    r = r / 252.
    dt = 1.0
    sigma = sigma / sqrt(252.0)

    #  Calculate vector of normally distributed numbers and use it to
    #  calculate the daily percent change.
    epsilon = np.random.normal( size = (N_sims * N_days + N_sims - 1) )
    ds_s = r * dt + sigma * sqrt(dt) * epsilon

    #  Step up matrix diagonals
    ones = -np.ones( (N_sims * N_days + N_sims) )
    ones[0:-1:N_days+1] = 1.

    ds_s[N_days:N_days * N_sims + N_sims:N_days+1] = -1
    d = [ds_s + 1, ones]
    K = [-1, 0]

    #  Solve the system of equations
    M = scipy.sparse.diags(d, K, format = 'csc')
    p = np.zeros( (N_sims * N_days + N_sims, 1) )
    p[0:-1:N_days+1] = init_price
    s = scipy.sparse.linalg.spsolve(M, p)

    #  Reshape the column vector so the function returns a matrix where
    #  each row is a single simulation with each day corresponding the the
    #  columns
    return np.reshape(s, (N_sims, N_days+1))

Let's make use of this and generate a plot of all the simulated runs.


num_sims = 1000
N = 30

r = 0.01
sigma = 0.15
S0 = 100.0

s = stock_monte_carlo(S0, N, num_sims,  r, sigma)

t = np.arange(0, N + 1)
for i in range(num_sims):
    plt.plot(t, s[i,:], 'k', alpha = 0.05)

plt.xlabel('Time (days)')
plt.ylabel('Stock Price ($)')
plt.grid(True)
plt.show()

The above code should produce the following plot.  Since we're plotting 1000 different runs, we  make each plot transparent with an alpha of 0.05 to avoid the whole thing being a giant mess.  The results are what we'd intuitively expect --  the most frequent outcome is the stock drifting slightly higher or lower than the initial price.  The outlier moves, either substantially up or down, are rather rare.
Figure 2:  A plot of 1000 different stock simulations.
We can use a histogram to visualize the price on any given day:


plt.hist( s[:,-1], bins = 50, edgecolor = 'k', linewidth = 0.5 )
plt.xlabel('Stock Price ($)')
plt.ylabel('Count' )
plt.show()
Figure 4:  Histogram showing price distribution on day 30 for 1000 simulations.
The histogram confirms what we'd expect by looking at Fig. 3, the bulk of the prices end up a bit above or below our initial stock price of $100.   If we calculate the mean and median price on day 30, we find they are 100.20 and 100.04, respectively.

We've constructed a simple Monte Carlo simulation for the price action of a stock and written a function that outputs the values for all runs.  The next article in this series will show how to use the above code to calculate the probability of making 50% (or any specified percentage) of maximum profit on a short options position.

Articles in this series

Part I:  Monte Carlo Techniques: Estimating the Value of Pi with Random Numbers
Part III:  Monte Carlo Techniques: Calculating the Probability of Making 50% of Max Profit on Short Option Positions
Part IV:  Monte Carlo Techniques:  Estimating Correlations between Option Positions and  and Their Underlying Stock

Sunday, December 17, 2017

MATLAB GUI Programming: Dynamically Sizing Text Uicontrols

A number of years ago, I was putting together a battery simulation tool written in MATLAB.  Cycling parameters could vary depending on the mode (charge, discharge, rest, or loop) and which of several parameters were to be held constant such as current, power, voltage, or some function of those.  Because of the different possibilities, text labels that described which value should go in a given entry box would change.

Maybe a simple example will show what I mean.  Here is a quick script to produce a window with a popup menu allowing the user to select one of three questions.  The code then writes the question as a text uicontrol and places an edit uicontrol next to it.  One question is long containing many characcters, the other is of medium length, and the last is short.


function static_labels
clear all; close all; clc

f = figure();

%  List of question that will appear to the left of an edit uicontrol.
q = {'How much wood could a woodchuck chuck if a woodchuck could chuck wood? ', ...
     'What is the airspeed velocity of an unladen swallow? ', ...
     'What is the meaning of the universe? '};

%  Create the popup uicontrol and make its entries out list of questions
%  The menu's callback function changes the text in the text uicontrol
%  defined below
pulldown_menu = uicontrol('Parent', f, ...
 'Style', 'popupmenu', ...
 'String', {'Question 1', 'Question 2', 'Question 3'}, ...
 'Callback', @change_question, ...
 'Position', [10, 400, 100, 20]);

%  Create a string variable str which contains the question corresponding
%  to which value is currently set in the popup menu
switch pulldown_menu.Value
 case 1
  str = q{1};
 case 2
  str = q{2};
 case 3
  str = q{3};
end

%  Create a text label whose string is set to str.
label = uicontrol('Parent', f, ...
 'Style', 'text', ...
 'String', str, ...
 'HorizontalAlignment', 'left', ...
 'Position', [10, 300, 400, 17]);

%  Create an entry widget positioned so that it is just to the right of the
%  longest question.  Its position is static not changing if the question
%  changes.
entry = uicontrol('Parent', f, ...
 'Style', 'edit', ...
 'Position', [420, 300, 50, 20]);

%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%  Callback function  %%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
function change_question(source, events)
 %  Get the selected field of the pulldown menu
 N = source.Value;

 %  Assign text depending on which value was chosen
 switch N
  case 1
   str = q{1};
  case 2
   str = q{2};
  case 3
   str = q{3};
 end

 %  Adjust the string of the text uicontrol
 label.String = str;

end

end

Figure 1 shows the pulldown menu.  Question 1, the longest, is selected and an edit uicontrol is just to the right of the text.

Figure 1:  Pulldown menu selects question displayed below.

We can see what happens when the question is changed.  This is shown below in Figs 2a and 2b.  The edit uicontrol stays in the same place while the length of the text changes.  Depending on the project and complexity of the interface being built, this may lead to a less than aesthetic-looking design.


Figure 2a:  Entry edit box next to a long block of text


Figure 2b:  Entry widget is in the same place, though
the length of text in the question is shorter


In my real-world case, there were perhaps 15 different possible configurations, and since this was developed over a period of several months, I handled them manually.  But I always found this unsatisfying.

Unfortunately, MATLAB itself doesn't have a way to estimate the size of a uicontrol based on the text contained.  We need first to get the handle to the Java object using the utility findjobj.   Then, the workaround for the sizing issue is to use the getPreferredSize method.  This method essentially gives hints to the layout manager as to the best size to make the widget

So I added a function,  calculate_widget_positions, that gets the preferred size of the text uicontrol, and returns this number along with the position of the edit control calculated from the text size plus some amount of padding.  This function is shown below.


function [width_label, x_entry] = calculate_widget_positions()

        %  Get the text uicontrol suggested width
        width_label = jlabel.getPreferredSize().getWidth();

        %  Add in some padding.  The edit control starts this amount after
        %  the text control ends
        padding = 15;

        %  Horizontal starting position of the edit uicontrol
        x_entry = label.Position(1) + width_label + padding;
end

The callback function is then modified to adjust the position of the edit uicontrol after setting the text.


function change_question(source, events)
 %  Get the selected field of the pulldown menu
 N = source.Value;

 %  Assign text depending on which value was chosen
 switch N
  case 1
   str = q{1};
  case 2
   str = q{2};
  case 3
   str = q{3};
 end

 %  Adjust the string of the text uicontrol
 label.String = str;

 drawnow;
 [width_label, x_entry] = calculate_widget_positions()
 p = label.Position;
 p(3) = width_label;
 label.Position = p;

 p = entry.Position;
 p(1) = x_entry;
 entry.Position = p;

end

Note also the use of the drawnow function after changing the string of the text control.  This needs to be here so that MATLAB is forced to draw the widget. We can then get its new size for positioning the entry box.   Perhaps if I were a little more clever in coding this, we could avoid this step, but this is more a proof-of-concept thing than something I am about to drop into production code.  Now, for example when the third question is selected, the resulting window looks like Fig. 3 below.

Figure 3.  The position of the edit entry box is determined from the size of the preceding text.

So though MATLAB doesn't naively have a way to address this issue, we can work around it by fiddling under the hood with the Java methods that the uicontrols themselves use.

For the sake of completeness, here is the full code including the dynamic positioning.


function dynamic_labels
clear all; close all; clc

f = figure();

%  List of question that will appear to the left of an edit uicontrol.
q = {'How much wood could a woodchuck chuck if a woodchuck could chuck wood? ', ...
     'What is the airspeed velocity of an unladen swallow? ', ...
     'What is the meaning of the universe? '};

%  Create the popup uicontrol and make its entries out list of questions
%  The menu's callback function changes the text in the text uicontrol
%  defined below
pulldown_menu = uicontrol('Parent', f, ...
 'Style', 'popupmenu', ...
 'String', {'Question 1', 'Question 2', 'Question 3'}, ...
 'Callback', @change_question, ...
 'Position', [10, 400, 100, 20]);

%  Create a string variable str which contains the question corresponding
%  to which value is currently set in the popup menu
switch pulldown_menu.Value
 case 1
  str = q{1};
 case 2
  str = q{2};
 case 3
  str = q{3};
end

%  Create a text label whose string is set to str.
label = uicontrol('Parent', f, ...
 'Style', 'text', ...
 'String', str, ...
 'HorizontalAlignment', 'left', ...
 'Visible', 'on', ...
 'Position', [10, 300, 100, 17]);

%  Get the handle to the Java object
jlabel = findjobj(label);

%  Create an entry widget positioned so that it is just to the right of the
entry = uicontrol('Parent', f, ...
 'Style', 'edit', ...
 'Visible', 'on', ...
 'Position', [420, 300, 50, 20]);

%  Set the initial position of the edit uicontrol
[width_label, x_entry] = calculate_widget_positions();
p = label.Position;
p(3) = width_label;
label.Position = p;

p = entry.Position;
p(1) = x_entry;
entry.Position = p;

%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%  Callback function  %%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%
function change_question(source, events)
 %  Get the selected field of the pulldown menu
 N = source.Value;

 %  Assign text depending on which value was chosen
 switch N
  case 1
   str = q{1};
  case 2
   str = q{2};
  case 3
   str = q{3};
 end

 %  Adjust the string of the text uicontrol
 label.String = str;

 drawnow;
 [width_label, x_entry] = calculate_widget_positions()
 p = label.Position;
 p(3) = width_label;
 label.Position = p;

 p = entry.Position;
 p(1) = x_entry;
 entry.Position = p;

end

function [width_label, x_entry] = calculate_widget_positions()
 %  Get the text uicntol suggested width
 width_label = jlabel.getPreferredSize().getWidth();

 %  Add in some padding.  The edit control starts this amount after
 %  the text control ends
 padding = 15;

 %  Horizontal starting position of the edit uicontrol
 x_entry = label.Position(1) + width_label + padding;
end

end

Monday, December 11, 2017

Monte Carlo Techniques: Estimating the Value of Pi with Random Numbers

I want to write a series of articles on using Monte Carlo methods in computational finance.  Monte Carlo algorithms are numerical techniques that use repeated sampling of random numbers to perform the desired calculation.  Such techniques are becoming increasingly frequent even down to the level of the individual trader with some brokerages using Monte Carlo simulations to estimate the probability of reaching a given percentage of maximum profit on short-premium options trades.

While that is not as complicated as one might think, I wanted to start out with a very simple article first.  In this article, we will write a simple Python script to estimate the value of $\pi$. In a follow-up post, we will introduce the concept of a random walk and use it to model a stock's price action. In the last article of this series, we will show how to predict the probability of reaching a given percentage of profit on an option trade.

So how can we use random numbers to estimate a constant like $\pi$?  We start with a unit circle circumscribed by a square.  Since the circle has a radius of one, the side of the square must have a length of two.
Figure 1:  A unit circle centered at the origin circumscribed by a square.
For the sake of coding convinces, we'll only look at the upper right quadrant as shown below in Fig 2.  This is because we'll use the built in rand function in numpy, which selects random values between zero and one. If we restrict ourselves in this way, we won't have to rescale this range to between -1 and 1.  Rescaling is no big deal, but if we can simply avoid doing so, we will take that opportunity.
Figure 2:  We will only consider the upper left quadrant.
The square shaded above in light gray now has sides $L = 1$ and an area of $L^2 = 1$.  The quadrant in the shaded region has an area of one-quarter the original, in other words, $\pi R^2 / 4$.  If we throw a large number of darts that hit randomly in that upper right quadrant,  we would expect the ratio of the number that hit within the circle to the total number of darts thrown to approach the ratio of the circle's area to that of the square.  In other words,
$$\frac{N_{inside}}{N_{total}} \approx \frac{A_{circle}}{A_{square}} = \frac{ \frac{1}{4} \pi R^2}{1}.$$

Fig. 3 shows an animation of this process.  For the sake of clarity, we only include 25 points in this animation whereas the actual calculation we will use significantly more.
Figure 3:  25 XY-pairs drawn at random.  For large numbers of pairs, the ratio of those within the circle to the total number of points should converge to the ration of the circle's area to that of the square.
Finding if a given point is within the circle is straightforward.  Since the circle has a radius of 1, the point is inside if
$$\sqrt{ x^2 + y^2} \leq 1^2$$.
The code below implements this calculation using the Python's Numpy library.  It should print "pi =  3.140936" to the screen.


# Python 2.7 script to estimate the value of pi using Monte Carlo techniques
import numpy as np

#  Use a constant random seed for debugging purposes
np.random.seed(0)

#  Number of random points to choose
N = 1000000

#  Create a 2-dimensional numpy array.  Each row will correspond to one
#  point with the entry in the first column being the X value and the
#  second column providing the Y values.
p = np.random.rand(N, 2)

#  Use the built-in norm function to calculate the distance from the origin
n = np.linalg.norm(p, axis = 1, keepdims = True)

#  Sum up the number of entries in the circle.  That is count the number of
#  points who have a norm less than or equal to one
number_in_circle =  np.sum(n <= 1)

#  Print out the results
print 'pi = ', 4 * float(number_in_circle) / float(N)

While this example borders on the trivial, it serves as a good starting point for more sophisticated calculations.  As noted in the introduction, I've used Monte Carlo methods in financial modeling (which we will discuss in later posts), as well as in complex physical systems such as modeling the loss of active material from cycling batteries.

Some comments on vectorization

The code above uses a "vectorized" implementation.  If you are familiar with the concept, there is no need to read further.  If not, I'd like to take a moment to explain the concept because we will use it heavily in future articles.

Numpy is a package for numerical calculations in Python.  It provides a significant speed increase over writing code in straight Python because it is written in C rather than Python itself.  It also provides the ability to "vectorize" calculations meaning operation can be performed on whole vectors or matrices without having to explicitly loop through the elements.  This can further enhance speed considerably.
Figure 4:  Snapshot of a Jupyter notebook.  We load in the numpy and math libraries (the math functionality will be used later), create two numpy arrays, add them together, and finally print out the results.
As an example, in Fig 4, we create two arrays p1, and p2, and compute their elementwise sum.  There is no need to loop over each index and add them in a piecemeal manner.  Numpy also provides a number of functions that can perform vectorized operation on arrays.  Notice in the code to estimate $\pi$,  we use the functions np.linalg.norm and np.sum to calculate the distance of the point from the origin and count the number of points with the circle, respectively.

Figure 5:  Compute the L2 norm of a vector $p$, and compare the result to that of calculating the distance between $p$ and the point (0, 0) using the Pythagorean theorem.
The function np.linalg.norm calculates the length of a vector (L2 norm).  In the code snippet shown in Fig. 5 we show that calculating the L2 norm via this  functionality is the same as explicitly calculating the length between the origin and a point $p$.  Lastly in Fig 6. we show the equivalence of looping over an array and calculating the distance of each point in that array to simply using the built in norm function.

Figure 6:  Combining the notions from Figs 4 & 5, we compare the vectorized implementation of the norm of an array of points to that of explicitly looping over the array and calculating the L2 norm via the Pythagorean theorem.

The vectorization used here is straightforward and pretty intuitive, but it is important to have a handle on the concept.  In subsequent articles where we discuss ransom walks in the context of modeling stock behavior, we will lean heavily on this as well as SciPy's ability to deal with sparse matrices  rapidly.  This is a bit more abstract, and thus it is a good idea to make sure one understands the simple case before jumping into the deep end.


Articles in this series

Part II:  Monte Carlo Techniques: Modeling Stock Price Action
Part III:  Monte Carlo Techniques:  Calculating the Probability of Making 50% of Max Profit on Short Option Positions
Part IV:  Monte Carlo Techniques:  Estimating Correlations between Option Positions and  and Their Underlying Stock


Thursday, November 23, 2017

MATLAB GUI Programming: Disabling Edit Uicontrols without Changing Their Appearance

Recently, I have been developing a MATLAB GUI for one of our offices that will allow them to solve complex optimization problems.  There are many inputs for the problem depending on the engineering details of that particular project, and a lot of quantities calculated from those entries  that need to be displayed near those inputs.

In previous projects, I just used the text uicontrol to handle this, but I always found the result aesthetically unappealing when a large amount of data needed to be summarized close to the user-entered data.  Figure 1 shows an example of this.  While it looks adequate for this particular case, I don't like the way it looks scaled up when there are dozens of user inputs.

Figure 1:  Using a text uicontrol to summarize information derived from user-entered data

I wanted to use the edit uicontrols to hold this information,  but by its nature the information in that box can be changed by the user.  Disabling the edit box grays out the box and text inside, which, in my opinion, doesn't look particularly good.  See figure 2.

Figure 2:  Examples of enabled and disabled edit uicontrols
Unfortunately, MATLAB doesn't have a way to disable the control without being grayed-out.  MATLAB's GUI capabilities are derived from Java Swing, and MATLAB only makes certain parts of the underlying Java object visible to the programmer.  Fortunately, one can access the handle to the Java object using the utility findjobj available in the Mathworks File Exchange.   Findjobj was written by Yair Altman who runs the excellent site Undocumented MATLAB, which largely deals, unsurprisingly, with undocumented features of MATLAB.

Once we have the object's Java handle, we can use its setEditable method to make the control uneditable.  An example is shown in the code below.


function edit_box
clear all; clc; close all;

f = figure();

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%  Main entry uipanel  %%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
entry_panel = uipanel('Parent', f, 'Title', 'Parameters', ...
 'Position', [0.05, 0.55, 0.5, 0.4]);

number_label = uicontrol('Parent', entry_panel, ...
 'Style', 'text', ...
 'String', 'Number of spheres', ...
 'HorizontalAlignment', 'left', ...
 'Position', [10 120, 100, 17]);
number_entry = uicontrol('Parent', entry_panel', ...
 'Style', 'edit', ...
 'Position', [120 120, 75, 20]);

radius_label = uicontrol('Parent', entry_panel, ...
 'Style', 'text', ...
 'String', 'Radii of spheres', ...
 'HorizontalAlignment', 'left', ...
 'Position', [10 90, 100, 17]);
radius_entry = uicontrol('Parent', entry_panel', ...
 'Style', 'edit', ...
 'Position', [120 90, 75, 20]);
radius_unit_label = uicontrol('Parent', entry_panel, ...
 'Style', 'text', ...
 'String', 'cm', ...
 'HorizontalAlignment', 'left', ...
 'Position', [200 90, 40, 17]);

density_label = uicontrol('Parent', entry_panel, ...
 'Style', 'text', ...
 'String', 'Density of spheres', ...
 'HorizontalAlignment', 'left', ...
 'Position', [10 60, 100, 17]);
density_entry = uicontrol('Parent', entry_panel', ...
 'Style', 'edit', ...
 'Position', [120 60, 75, 20]);
density_units_jlabel = javaObjectEDT('javax.swing.JLabel', ...
 '<HTML>g &#47; cm<SUP>3</SUP></HTML>');
[density_units_label, hcontainer] = javacomponent(density_units_jlabel, ...
 [200 60, 40, 22], entry_panel);

%  Create a font for the JLabel that matches the default MATLAB font for text
%  uicontrols
label_font = java.awt.Font('MS Sans Serif', java.awt.Font.PLAIN, 11);
density_units_label.setFont(label_font);

update_button = uicontrol('Parent', entry_panel, ...
 'String', 'Update', ...
 'Callback', @update_fields, ...
 'Position', [10 15 75 30]);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%  Main entry uipanel  %%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
summary_panel = uipanel('Parent', f, 'Title', 'Summary', ...
 'Position', [0.05, 0.05, 0.5, 0.4]);

volume_per_sphere_label = uicontrol('Parent', summary_panel, ...
 'Style', 'text', ...
 'String', 'Volume per sphere', ...
 'HorizontalAlignment', 'left', ...
 'Position', [10 120, 100, 17]);
volume_per_sphere_entry = uicontrol('Parent', summary_panel', ...
 'Style', 'edit', ...
 'Position', [120 120, 75, 20]);
volume_per_sphere_units_jlabel= javaObjectEDT('javax.swing.JLabel', ...
 '<HTML>cm<SUP>3</SUP></HTML>');
[volume_per_sphere_units_label, hcontainer] = javacomponent( ...
 volume_per_sphere_units_jlabel, ...
 [200 120, 40, 22], summary_panel);
volume_per_sphere_units_jlabel.setFont(label_font);

total_volume_label = uicontrol('Parent', summary_panel, ...
 'Style', 'text', ...
 'String', 'Total volume', ...
 'HorizontalAlignment', 'left', ...
 'Position', [10 90, 100, 17]);
total_volume_entry = uicontrol('Parent', summary_panel', ...
 'Style', 'edit', ...
 'Position', [120 90, 75, 20]);
total_volume_units_jlabel = javaObjectEDT('javax.swing.JLabel', ...
 '<HTML>cm<SUP>3</SUP></HTML>');
[total_volume_units_label, hcontainer] = javacomponent( ...
 total_volume_units_jlabel, ...
 [200 90, 40, 22], summary_panel);
total_volume_units_jlabel.setFont(label_font);

total_mass_label = uicontrol('Parent', summary_panel, ...
 'Style', 'text', ...
 'String', 'Total mass', ...
 'HorizontalAlignment', 'left', ...
 'Position', [10 60, 100, 17]);
total_mass_entry = uicontrol('Parent', summary_panel', ...
 'Style', 'edit', ...
 'Position', [120 60, 75, 20]);
total_mass_unit_label = uicontrol('Parent', summary_panel, ...
 'Style', 'text', ...
 'String', 'g', ...
 'HorizontalAlignment', 'left', ...
 'Position', [200 60, 40, 17]);

%  Make the edit entries in this section uneditable by accessing their Java
%  handle and setting editable to false.
jvolume_per_sphere_entry = findjobj(volume_per_sphere_entry);
jvolume_per_sphere_entry.setEditable(false);

jtotal_volume_entry = findjobj(total_volume_entry);
jtotal_volume_entry.setEditable(false);

jtotal_mass_entry = findjobj(total_mass_entry);
jtotal_mass_entry.setEditable(false);


%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%  Calback function  %%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 function update_fields(source, events)
  %  Read inputs from edit boxes
  N = str2num( number_entry.String );
  r = str2num( radius_entry.String );
  rho = str2num( density_entry.String );

  %  Calculate the needed values
  V_sphere = 4/3 * pi * r^3;
  V_total = N * V_sphere;
  M = rho * V_total;

  %  Update the edit boxes
  volume_per_sphere_entry.String = num2str( V_sphere );
  total_volume_entry.String = num2str( V_total );
  total_mass_entry.String = num2str( M );
end

end

This code produces the window shown if Fig. 3.  The edit uicontrols in the Summary pane are not changeable by the user, though one can highlight the values and copy them to the clipboard if need be.

Figure 3.  Example of window where several edit uicontrols are set to be non-editable while not being grayed out.
Another annoying quirk of MATLAB GUIs is the inability to customize the text in text uicontrols.  I am not sure what classes the underlying Java object derives its properties from, but whatever it is, it is very limited.  In cases where we need to format the text-- in the above example we have units raised to a power-- we can just substitute a Java JLabel in place of the text uicontrol.  MATLAB and Java default to different fonts, so I changed the font of the JLabel to match that of the MATLAB control by creating a Java font object with the appropriate settings and passing it to the setFont method of the JLabel.  The first instance of this in the above code occurs at the lines:


label_font = java.awt.Font('MS Sans Serif', java.awt.Font.PLAIN, 11);
density_units_label.setFont(label_font);

This is an example of using Java directly in MATLAB which is more a forte of the Undocumented MATLAB site so I won't dwell on it here.

Saturday, November 4, 2017

Simulating physical systems - Do not blindly trust computer output

In putting together some code to bring myself up to speed on a software library, I ran into an issue that I've seen many times over the years. You can write down the proper equations that describe the physics of your system, have an algorithm capable of solving the math involved, code it correctly, and unless you're very careful and know what's going on under the hood, still get the wrong answer.

My initial intent was to write a series of posts detailing some of the functionality of the Sundials package released by Lawrence Livermore National Laboratory (https://computation.llnl.gov/projects/sundials).  Sundials is a package for numerically solving ordinary differential equations and differential algebraic equations.  It also comes with a library for the solution of nonlinear algebraic equations.  We will go into detail on this at a later date.

I've used Sundials once or twice, but some current projects I'm working on require the speed and horsepower of C++ as opposed to MATLAB or Python.  I was making more of effort to familiarize myself with the full capabilities of the package.  To that end I was working through a couple of simple problems: a projectile motion problem with air resistance, and a damped driven harmonic oscillator.  The driven oscillator problem is nonlinear and is often used as an introduction to chaos in courses on computational physics.  Small changes in initial conditions or other parameters can lead to vastly different behaviors.

I started on the oscillator problem and grabbed an old book from my undergraduate days, Computational Physics by Nicholas J. Giordano.  I noticed something when reworking the example presented by the author.  As mentioned, the problem is a driven pendulum consisting of a mass hanging from a rigid rod of length $L$.  The angle between the rod and the vertical is denoted by $\theta$.  This is shown below in Fig. 1.

Figure 1:  Schematic of pendulum

The author considers the case where the pendulum is driven by a  periodic force with amplitude $F_d$ and frequency $\Omega$,
$$F_\mbox{drive} = F_d \sin (\Omega_d t).$$
We will also include a damping force proportional to the angular velocity,
$$F_{\mbox{damping}} = -q \omega = -q \frac{d\theta}{dt},$$
where $q$ is the constant of proportionality and $\omega$ is the pendulum's angular velocity.

The equation of motion of the pendulum is given by,
\begin{equation}
\frac{d^2 \theta}{dt^2} = -\frac{g}{L}\sin{(\theta)} - q \frac{d\theta}{dt} + F_d\sin{(\Omega_D t)}
\label{Main_de}
\end{equation}
subject to the initial conditions for our starting angle and angular velocity, $\theta(0) = \theta_0$ and $d\theta(0)/dt = \omega_0$.

We can rewrite Eq. \ref{Main_de} as two coupled first-order differential equations,
$$\frac{d \omega}{dt} = -\frac{g}{L}\sin{(\theta)} - q \frac{d\theta}{dt} + F_d\sin{(\Omega_D t)} $$ and
$$\frac{d\theta}{dt} = \omega.$$

The author solves these equations using an Euler-Cromer algorithm where we step a small time interval  $\Delta t$, and calculate approximate solutions at time  $t_{i+1} = t_i + \Delta t$ as follows,
$$\omega_{i+1} = \omega_i - \frac{g}{L}\sin(\theta_i)\Delta t - q\omega_i \Delta t + F_d\sin(\Omega_d t) \Delta t$$
$$\theta_{i+1} = \theta_i + \omega_{i+1}\Delta t.$$

Below is C++ code (a translation of the code in the textbook) implementing the above algorithm for the following model parameters.


  • g = 9.8 $\mbox{m}/s^2$
  • L = 9.8 m
  • $F_d = 1.2 \;\mbox{s}^{-2}$
  • $q_d = 0.5 \;\mbox{s}^{-1}$
  • $\Omega_d = 2/3 \;\mbox{s}^{-1}$
  • $\theta(0) = 0.2$
  • $\omega(0) = 0\;\mbox{s}^{-1}$



#include <iostream>
#include <math.h>

using namespace std;

int main()
{
        double g       = 9.8;
        double L       = 9.8;
        double F_d     = 1.2;
        double q_d     = 0.5;
        double Omega_d = 2.0/3.0;

        double period = 2 * M_PI * sqrt(g/L);
        int i = 0;
        double t = 0;
        double dt = 0.04;

        double theta = 0.2;
        double omega = 0.0;

        while (t <= 10 * period)
        {
                i = i + 1;
                t = t + dt;
                omega = omega - g/L * sin(theta) * dt - q_d * omega * dt +
                        F_d * sin( Omega_d * t) * dt;
                theta = theta + omega * dt;
                cout << t << "\t" << "\t" << theta << "\t" << omega << endl;
        }

        return 0;
}

Fig. 2 shows the results of this integration.
Figure 2.  Results of an Euler-Cromer integration of the pendulum's equations of motion with a time step of 0.04 seconds.


I mentioned this came about in an attempt to write an article detailing some of the functionality of Sundials.  For the sake of argument, let's take the output of Sundials as correct and overlay those results on the data obtained from Euler-Cromer.
Figure 3.  Euler-Cromer integration with the results from Sundials overlay ed.  The two methods agree up to around 30 seconds, then begin to drastically diverge.


OK, there is an obvious discrepancy here.  After starting out the same, the two different simulations diverge considerably as time goes on.  Is it the algorithm itself?  Well, let's consider the case with no driving force and dissipation.  Let's also take the initial displacement $\theta$ to be small such that we can make use of the approximation $\sin(\theta) \approx \theta$.  In other words, we want to solve the equation,
\begin{equation}
\frac{d^2 \theta}{dt^2} = -\frac{g}{L}\theta.
\label{simple_de}
\end{equation}
This does have an analytical solution,
$$\theta(t) = \theta_0 \sin\left(\sqrt{\frac{g}{L}} t + \frac{\pi}{2}\right).$$
Let's rerun our code with the driving force and dissipation set to zero and plot Eq. \ref{simple_de} on top of it.
Figure 4:  Comparison between analytical results fro a simple harmonic oscillator and the numerical solution using the Euler-Cromer algorithm.

Well, OK.  Those two curves are identical so the issue probably isn't with the code itself.  The issue is the time step used in the Euler-Cromer code.  As I mentioned earlier, the pendulum problem is often used as an introduction to chaotic behavior-- slight differences in parameters or initial conditions can lead to radically different behavior.  We can see that by running the sundials code for slightly differnt initial starting angles.  In the plot below, we start with our original initial position and solve for three cases where we increment the initial starting angle by 2%.  This works out to be slightly less than 0.25 degrees each time.  That isn't a big change, so we shouldn't see much of a change.  Indeed, we don't see that big of a difference between the plots until roughly 35 seconds as the four curves begin to diverge considerably.
Figure 5:  Solution to the damped driven harmonic oscillator problem for four slightly different starting angles.  The solutions are similar until around 35 seconds at which point they begin to differ significantly.

The Euler-Cromer algorithm isn't particularly accurate, so errors in the results at each time step can accumulate faster than in other, more accurate integration schemes.  This isn't really important in linear problems-- the simple harmonic oscillator plotted above is just fine-- but when we work with the nonlinear problems those small errors change the predicted results entirely.  We can confirm this is indeed what is happening by reducing the time step in the Euler-Cromer integration.  In Figure 6, we take this time step to be 0.0001 seconds.  As can be seen, this now matches well with the results out of Sundials, although some discrepancy is creeping in towards the end of the simulation.
Figure 6:  Comparison between Sundials and Euler-Cromer algorithm with the time step in Euler-Cromer set to 0.0001 S
Other issues can arise in nonlinear systems that produce erroneous results.  Changing the integration tolerance can affect the output.  Consider Figure 7 below.  These simulations were written in MATLAB using its built-in  ode45 differential equation solver routine.  Simply changing the relative tolerance the solver uses can give very different answers.  Roughly speaking, the relative tolerance sets accuracy of the solution to a given number of digits.  Figure 7 plots the solutions for relative tolerances of $10^{-3}$ and $10^{-6}$ as well as the solution out of Sundials.
Figure 7: Plot of solutions using relative tolerances of 1e-3 and 1e-6 as well as the solution as calculated by Sundials.  For clarity the density of plotted points is lower than in previous plots.

The bottom line: Even simple looking systems can produce unexpected results and be very sensitive to the algorithms (and internal settings of the algorithms) used to solve them.  Don't just blindly trust the output.  Always double check and build in various means of sanity checking your results.