Cheat sheet
Comments
a + b; # This is a single line comment
// Another single line comment
/*
I am also a comment, but can span
multiple lines if I wan to
*/
/#
Do not forget me, another multipline
comment.
#/
c + d;
Raise to the power
x ^ y
Enter a series with observations
cpi = observe( date, obs1, obs2 );
e.g.
cpi = observe( dateIndex( 2020, 1, 1 ), 100, 100.1, 100.2 );
This loads 100 in 2020 Jan, 100.1 in Feb and so on.
Missing values
Use a . e.g.
cpi = observe( dateIndex( 2020, 1, 1 ), 100, ., ., 101, 102 );
This loads 100 in Jan, 101 in April and so on.
Merge series
cpi = merge( cpi1, cpi2, cpi3 ... );
Here cpi1
takes preference so if it shares an observation with cpi2
then the
observation in cpi1
will be used.
Index a series to 2015=100
avg2015 = avg( limit( dateIndex( 2015, 1, 1 ), dateIndex( 2015, 12, 31 ), cpi ) );
index = 100 * cpi / avg2015;
Merge two series with a common observation in 2015Q1
di = dateIndex( 2015, 1, 1 );
series = merge( series1, series2 / series2[ di ] * series2[ di ] );
Calculate logs
fdm does not want you to be confused between natural and base 10 logs, so there
is no log
function, only ln
for natural and log10
for base 10
calculations, thus
lcpi = ln( cpi );
is probably what you are looking for, but, if you want base 10, you can use
lcpi = log10( cpi );
Calculate percentage changes
From one period to the next
Depending on the frequency, this would be quarter on quarter or month on month growth rates.
growth = p( cpi );
The returned values are simply the percentage changes between consecutive observations. A value of 0.1 represents 10% so you may want to multiply by 100 to print it out nicely.
Year on year
growth = py( cpi );
Here the returned growth rates represents the growth from say January last year to January this year.
Annualised
growth = pa( cpi );
These values are month on month (or quarter on quarter) growth rates that have been annualised.
Lags
To lag a series use
ly = y(-1);
ly4 = y(-4);
Differences
Differencing time series is an important topic. fdm distinguishes between regular frequencies such as monthly and quarterly and irregular frequencies such as daily.
When you have a monthly series with observations as follows
2020 Jan 100.0
2020 Feb 100.1
2020 Mar 101.1
2020 Apr 101.2
and you difference it, you will get a new series with three observations
2020 Feb 0.1
2020 Mar 1.0
2020 Apr 0.1
If one observation was missing in the original series, e.g. if it was
2020 Jan 100.0
2020 Feb 100.1
2020 Mar <missing>
2020 Apr 101.2
then differencing it yields only a single observation
2020 Feb 0.1
This is because there is nothing to difference Apr 2020 to. This is how it works for regular series. Observations are made regularly and if there is no observation it means there is a missing value and differencing will yield gaps and actually be problematic.
Not so with irregular or daily data. Here dates do not follow a regular pattern. Weekends and holidays cause large gaps but it should not hamper the differencing of the series. So differencing e.g.
2019-Dec-31 (Tue) 100
2020-Jan-01 (Wed) <missing>
2020-Jan-02 (Thu) 100.1
2020-Jan-03 (Fri) 100.2
2020-Jan-04 (Sat) <missing>
2020-Jan-05 (Sun) <missing>
2020-Jan-06 (Mon) 100.3
2020-Jan-07 (Tue) 100.4
2020-Jan-08 (Wed) 100.5
should not be bothered by the holiday (on 2020-01-01) or by the weekend and should return
2020-Jan-02 (Thu) 0.1
2020-Jan-03 (Fri) 0.1
2020-Jan-06 (Mon) 0.1
2020-Jan-07 (Tue) 0.1
2020-Jan-08 (Wed) 0.1
Even though the input series contains three missing observations or gaps, the differenced series still has five observations as it simply ignored these gaps and used the previous available observation.
This is the distinguishing factor between regular and irregular time series. When differencing a regular time series, it can only difference observations made exactly one bar apart. An irregular time series will use all observations and will difference despite gaps. The only requirement to difference in a given bar is that there be a previous observation available.
fdm handles this with the previous functions. Thus to difference a regular time series use
dy = d( y ); # same as y - y(-1)
but to difference an irregular time series use
dy = dPrev( y ); # same as y - prev( y )
Likewise, for regular series, use
dy = d( y ); # y - y(-1) or y - lag( y )
dy = dLn( y ); # Change in logs
dy = dy( y ); # Change over a year, e.g. y - y(-4) for quarterly
and for irregular series use
dy = dPrev( y ); # y - prev( y )
dy = dLnPrev( y ); # Change in logs for daily observations
dy = dyPrev( y ); # Change over a year, ignores gaps and used most recent
# (earlier) value
Formatting output
Numbers are fixed format by default. The number of decimals to show after the decimal separator is four by default.
print( pi () ); # +3.1416
format( 10 );
print( pi () ); # +3.1415926536
Think of fdm as a programming language when dealing with numbers. Just as C or C++ is not locale aware, so with fdm. Numbers are separated with a decimal point in calculations. The output format can however be specified. Things like prefixes, postfixes, field lengths and so on can be set.
Please see the format function and its many friends for more details.
Matrices
fdm offers a lot of matrix functionality. To define a matrix, say a 10x5 matrix, use
A = matrix( 10, 5 );
Now A contains a lot of zeroes. You can also load, row by row, data when using
the matrix
function.
A = matrix( 3, 3, 1, 2, 3, 4, 5, 6, 7, 8, 9 );
Now A is
[ 1 2 3
4 5 6
7 8 9 ]
You can create matrices filled with random numbers
A = matrixRand( 10 ); # 10 x 10 matrix
A = matrixRand( 2, 3 ); # 2 x 3 matrix
A = matrixRand( 3, 3, 3 ); # 3 x 3 matrix with each random number containing 3 random digits
A = matrixRand( 3, 3, 100 ); # Go big or go home, here each number will have 100 random digits
Matrices can be added and multiplied and inverted just as in your textbook. See the matrix section in the function manual for more detail.
User defined functions
Define a new function
define( "square", "a", a * a );
define( "hypot", "a", "b", sqrt( a * a + b * b ) );
define( "diag", "A", "i", {
print( "Will return diagonal of A" ),
print( "Found at position i", i ),
print( "And also report on it" ),
A[ i; i ]
} );
Parent scope
x = 10;
define( "myFunc", "x", {
print( "My x is", x ),
print( "My parent's x is", parent( x ) )
} );
Optimise a function
There are numerous algorithms, using the downhill simplex method of Nelder and Mead, for example.
# Define a function
# Rosenbrock with a = 1 and b = 100
# Parameters
a = 1;
b = 100;
define( "rb", "A",
{
# Parameters taken from parent context
a = parent( a );
b = parent( b );
# Note A is a column vector
x = A[ 1; 1 ];
y = A[ 2; 1 ];
( a - x ) ^ 2 + b * ( y - x ^ 2 ) ^ 2;
} );
# Optimise
print( funcOptimNm( "rb", matrix( 2, 1, 0, 0 ) ) );