BERT Example Functions
Here are some useful functions you can use in Excel with BERT. If you’re not familiar with BERT or landed here via search engine, have a look at the front page for quick install and usage instructions.
These functions aren’t guaranteed to be efficient, correct, comprehensive, or necessarily useful. You are encouraged to modify them for your needs. Unless otherwise noted, all code on this page is in the public domain.
If you have a suggestion, comment, or request for a particular function, please let us know.
Using These Functions
To use any of these functions, you just need to save the source file into your BERT startup folder. Right-click on one of the Save links below and select Save link as…
Save the file into Documents\BERT\functions
. You’ll see a note in the BERT console when a new file is loaded.
You can also copy the code below and paste it into your own file. any file in the BERT startup folder will be automatically reloaded when you save changes.
Data Management
Sort
You can sort data with excel, but if that data changes it won’t re-sort. Also you lose the original order. Use this function to insert a sorted range that’s actively linked to the original range.
Sort <- function( m, ascending=TRUE, sort.column.index = 1, empty.last=TRUE){
sort.column.index = max( 1, min( ncol(m), sort.column.index ));
m<-cbind(m,0);
m[order(unlist(m[,sort.column.index]), decreasing=!ascending, na.last=empty.last ),];
}
Shuffle
Shuffling data can be useful for bootstrap analysis or for randomizing fixed data sets (dice, cards).
Shuffle <- function( m, keep.rows=FALSE ){
if( keep.rows ){
m[sample(nrow(m)),];
}
else {
matrix( m[sample(length(m))], nrow=nrow(m));
}
}
Data Analysis
Dimensionality Reduction
Dimensionality reduction using Pricipcal Components Analysis (PCA)
pca <- function( mat, dimensions ){
mat <- matrix( as.numeric( mat ), nrow=nrow(mat));
if( missing( dimensions )){
ref <- BERT$.Excel(89);
dimensions <- ncol(ref);
}
Sigma <- (t(mat) %*% mat)/nrow(mat);
pc <- svd(Sigma)$u;
mat %*% pc[,1:dimensions]
}
attr( pca, "description" ) <- list(
"Reduce dimensionality using principal components analysis (PCA)",
mat = "matrix of data",
dimensions = "(optional) number of dimensions in result"
);
Statistics
Matrix Correlation
Get a correlation matrix from multiple correlated data sets.
MatrixCorrelation <- function( m, data.in.columns=TRUE, full.matrix=FALSE ){
if( !data.in.columns ){
x <- cor(t(m));
if( !full.matrix ) x[lower.tri(x)] <- NA;
}
else {
x <- cor(m);
if( !full.matrix ) x[upper.tri(x)] <- NA;
}
return( x );
}
Test for Normality
Two tests for normality in a data set: the Shaprio-Wilk and Kolmogorov-Smirnov tests. In each case, the exported functions return the p-value from the test.
SWTest.p <- function(mat) {
shapiro.test(mat)$p.value;
}
KSTest.p <- function(mat) {
ks.test(mat, pnorm)$p.value;
}
Given a set of data, find lambda for the Box-Cox transform.
boxcox <- function( m, range.min = -3, range.max = 3, range.step = .1 ){
F <- function( x, lambda ){
if( lambda == 0 ){ xlambda <- log(x); }
else{ xlambda <- (x^lambda-1)/lambda; }
n <- length(x);
xhl <- sum(xlambda)/n;
-n/2 * log(sum(((xlambda-xhl )^2)/n)) + (lambda-1)*sum(log(x));
}
index <- which.max(sapply(
seq( range.min, range.max, by=range.step ), function(x){ F(m, x) } ))
return((index-1)*range.step+range.min);
}
Matrix Functions
Eigensystem
Functions for getting eigenvalues and eigenvectors from a matrix.
EigenValues <- function(x) {
eigen(x, only.values=T)$values
}
EigenVectors <- function(x) {
eigen(x)$vectors
}
Finance
Trinomial Lattice Option Pricing
Generate call & put values using a trinomial lattice.
TrinomialLattice.Option <- function(
time.in.years,
underlying.price,
strike.price,
risk.free.rate,
sigma,
number.of.steps,
call.option = TRUE,
early.exercise = FALSE ) {
dT <- time.in.years / number.of.steps;
up <- exp(sigma * sqrt(2*dT));
down <- 1/up;
m <- 1;
discount.factor <- exp( -risk.free.rate * dT );
up.probability <- (( exp(risk.free.rate*dT/2) - exp(-sigma*sqrt(dT/2)))
/ ( exp( sigma*sqrt(dT/2)) - exp( -sigma*sqrt(dT/2))))^2;
down.probability <- (( exp(sigma*sqrt(dT/2)) - exp(risk.free.rate*dT/2) )
/ ( exp( sigma*sqrt(dT/2)) - exp( -sigma*sqrt(dT/2))))^2;
m.probability = 1 - (up.probability + down.probability);
if( call.option )
{
p <- pmax( 0, (underlying.price * up^(number.of.steps:(-number.of.steps))) - strike.price);
}
else
{
p <- pmax( 0, strike.price - (underlying.price * up^(number.of.steps:(-number.of.steps))));
}
n <- number.of.steps * 2 + 1
mm<-matrix( c( up.probability, m.probability, down.probability, rep(0, n-2) ),
nrow=n, ncol=n-2);
if( early.exercise )
{
for( idx in 1:number.of.steps )
{
p <- pmax( p, c( 0, colSums( mm * p ), 0 ));
}
}
else
{
for( idx in 1:number.of.steps )
{
p <- c( 0, colSums( mm * p ), 0 );
}
}
return( p[number.of.steps+1] * discount.factor^number.of.steps);
}
Binomial Lattice Option Pricing
Generate call & put values using a binomial lattice.
BinomialLattice.Option <- function(
time.in.years,
underlying.price,
strike.price,
risk.free.rate,
sigma,
number.of.steps,
call.option = TRUE,
early.exercise = FALSE ) {
dT <- time.in.years / number.of.steps;
up <- exp(sigma * sqrt(dT));
discount.factor <- exp( -risk.free.rate * dT );
up.probability <- ( exp( risk.free.rate * dT ) - (1/up) ) / ( up - (1/up) );
down.probability <- 1 - up.probability;
p <- sapply( 0:number.of.steps, function(i){
underlying.price * up^(2*i - number.of.steps); });
p[p < 0] <- 0;
if( call.option ){ v <- pmax(0, (p - strike.price)); }
else { v <- pmax(0, (strike.price - p)); }
ee.discount <- discount.factor;
for( idx in number.of.steps:1 ){
v <- rev(sapply( idx:1, function(x){ v[x+1] * up.probability * discount.factor +
v[x] * down.probability * discount.factor }));
if( early.exercise )
{
if( call.option ){
v <- pmax( v, sapply( 0:(idx-1), function(i) {
( underlying.price * up^(2*i - (idx-1) ) - strike.price) * ee.discount; }));
}
else {
v <- pmax( v, sapply( 0:(idx-1), function(i){
( strike.price - underlying.price * up^(2*i - (idx-1) )) * ee.discount; }));
}
ee.discount <- ee.discount * discount.factor;
}
}
return(v[1]);
}
Binomial Lattice Tree
Generate the binomial lattice price matrix in a spreadsheet.
BinomialLattice.PriceMatrix <- function(
underlying.price, time.in.years, risk.free.rate, sigma, number.of.steps ) {
dT <- time.in.years / number.of.steps;
up <- exp(sigma * sqrt(dT));
mat <- diag( number.of.steps+1 );
mat[,] <- up^((1-row(mat))*2+col(mat)-1) * underlying.price;
mat[lower.tri(mat)] <- NA;
mat;
}
BinomialLattice.DisplayPriceMatrix <- function(
underlying.price, time.in.years, risk.free.rate, sigma, number.of.steps ) {
x <- BinomialLattice.PriceMatrix(
underlying.price, time.in.years, risk.free.rate, sigma, number.of.steps );
sapply( 1:ncol(x), function(a){
unlist(c(rep(NA,(nrow(x)-a)),Map(c,x[1:a,a],NA),rep(NA,max(0,(nrow(x)-a)))))})
}
Graphics
Histogram
This function is an example of using R graphics in Excel with the BERT graphics device. It generates a basic histogram of the input data.
graph.histogram <- function(data, main="Histogram", xlabel="Data"){
BERT.graphics.device(cell=T);
x <- unlist( as.numeric( data ));
hist( x, xlab=xlabel, main=main, col="pink", breaks=13, font.main=1);
dev.off();
T
}
Drawing Maps
Using the maps
package, it’s easy to create graphical maps from a list of countries (you could do the same thing for other geographic entities, like US states or French départments).
Before using this function, make sure to install the maps
package; use Packages > Install Packages from the BERT console menu.
draw.map <- function( countries, values, title ){
library(maps);
BERT.graphics.device(cell=T);
c2 <- unlist(countries[!is.na(countries)]);
values <- as.numeric(values);
values[is.na(values)] <- 0;
v2 <- unlist(values[!is.na(countries)]);
n <- 32;
scaled.values <- round((1-((v2-min(v2))/(max(v2) - min(v2))))*(n-1))+1;
heatcolors <- heat.colors(n);
margins = c(0, 0, 0, 0);
if( !missing(title)){ margins[3] <- .6; }
par(mai=margins);
map("world", c2, fill=F, lty=0);
sapply( c2, function(country){
map( "world", country, fill=T, lty=0, add=T,
col=heatcolors[scaled.values[[which(c2==country)]]] );
});
map("world", c2, fill=F, col="#cccccc", add=T );
if(!missing(title)){ title( main=title, font.main=1 ); }
dev.off();
T;
}
Utilities
Getting the Caller Reference
Not a function in and of itself, but an example of how to use the Excel API to get a reference to the calling cell (the cell containing the function).
How.Big <- function(){
ref <- BERT$.Excel(89);
n.rows <- nrow(ref);
n.cols <- ncol(ref);
return(paste(n.rows, "x", n.cols));
}