Skip to content
Snippets Groups Projects
README.Rmd 11 KiB
Newer Older
Bergant's avatar
Bergant committed
---
Bergant's avatar
Bergant committed
title: "finstr - Financial Statements in R"
Bergant's avatar
Bergant committed
output:
Bergant's avatar
Bergant committed
  md_document:
    variant: markdown_github
Bergant's avatar
Bergant committed

Bergant's avatar
Bergant committed
---

Bergant's avatar
Bergant committed
```{r, echo = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
Bergant's avatar
Bergant committed
  fig.path = "img/README-"
Bergant's avatar
Bergant committed
)
```
Bergant's avatar
Bergant committed

Bergant's avatar
Bergant committed
![finstr](img/logo.png)
Bergant's avatar
Bergant committed

Bergant's avatar
Bergant committed
The purpose of **finstr** package is to create an environment for 
reproducible financial statement analysis. 
The package will not cover specific types of 
analysis (except in examples and package vignettes) but will provide
a domain language to write them. 
Bergant's avatar
Bergant committed
With other functions in basic R and existing R packages it anables users 
to store, share, reuse and reproduce the results of their analitic work.
Bergant's avatar
Bergant committed

Bergant's avatar
Bergant committed
For now it is offering:
Bergant's avatar
Bergant committed

Bergant's avatar
Bergant committed
**1. Data structure for financial statements**
Bergant's avatar
Bergant committed

Bergant's avatar
Bergant committed
  - Reading from data parsed with XBRL package
Bergant's avatar
Bergant committed
  - Statements in tidy format with accounting taxonomy concepts as columns
Bergant's avatar
Bergant committed
  - Encapsulates calculation hierarchy of variables
Bergant's avatar
Bergant committed
  - Default printing in transposed format and with visible hierarchy

Bergant's avatar
Bergant committed
**2. Statement calculation validation**
Bergant's avatar
Bergant committed

  - Calculation of higher order elements
Bergant's avatar
Bergant committed
  - Check if calculated values match original values
Bergant's avatar
Bergant committed
  
**3. Merge statements** 

  - Merge different periods of equal statement type
  - Merge statements of a different type 
  
**4. Calculate and reveal**

  - Custom financial ratio calculations definitions 
Bergant's avatar
Bergant committed
  - Exposing data by rearranging the statament hierarchy
Bergant's avatar
Bergant committed
  - Time lagged difference
Bergant's avatar
Bergant committed


## Installation
To install finstr from github use `install_github` from devtools package: 
Bergant's avatar
Bergant committed
```{r, eval=FALSE}
library(devtools)
install_github("bergant/finstr")
```
Bergant's avatar
Bergant committed

Bergant's avatar
Bergant committed
```{r, echo=FALSE, results='hide', message=FALSE }
library(dplyr)
library(tidyr)
library(finstr)
data(xbrl_data_aapl2013)
data(xbrl_data_aapl2014)
```


Bergant's avatar
Bergant committed
## Get data
Bergant's avatar
Bergant committed
Use XBRL package to parse XBRL files. For example:
Bergant's avatar
Bergant committed
```{r xbrl_parse_min, eval=FALSE, echo=TRUE}
Bergant's avatar
Bergant committed
library(XBRL)
Bergant's avatar
Bergant committed
# parse XBRL (Apple 10-K report)
xbrl_url2014 <- 
  "http://edgar.sec.gov/Archives/edgar/data/320193/000119312514383437/aapl-20140927.xml"
xbrl_url2013 <- 
  "http://edgar.sec.gov/Archives/edgar/data/320193/000119312513416534/aapl-20130928.xml"
Bergant's avatar
Bergant committed
old_o <- options(stringsAsFactors = FALSE)
Bergant's avatar
Bergant committed
xbrl_data_aapl2014 <- xbrlDoAll(xbrl_url2014)
xbrl_data_aapl2013 <- xbrlDoAll(xbrl_url2013)
Bergant's avatar
Bergant committed
options(old_o)
Bergant's avatar
Bergant committed
```

Bergant's avatar
Bergant committed
## Prepare statements
Bergant's avatar
Bergant committed
With `xbrl_get_statements` convert XBRL data to *statements* object. 
```{r xbrl_get_statements}
Bergant's avatar
Bergant committed
library(finstr)

Bergant's avatar
Bergant committed
st2013 <- xbrl_get_statements(xbrl_data_aapl2013)
st2014 <- xbrl_get_statements(xbrl_data_aapl2014)
st2014
```

Statements object is a list of 
several statement objects (ballance sheets, income and cash 
Bergant's avatar
Bergant committed
flow statements).

Bergant's avatar
Bergant committed
To get a single *statement* use *statements* object as a regular R list:
```{r statement}
balance_sheet2013 <- st2013$StatementOfFinancialPositionClassified
balance_sheet2014 <- st2014$StatementOfFinancialPositionClassified
Bergant's avatar
Bergant committed
income2013 <- st2013$StatementOfIncome
income2014 <- st2014$StatementOfIncome
Bergant's avatar
Bergant committed
balance_sheet2014
Bergant's avatar
Bergant committed
tail(income2014, 2)
Bergant's avatar
Bergant committed
```

Bergant's avatar
Bergant committed

Bergant's avatar
Bergant committed
## Validate statement calculation hierarchy
Recalculate higher order concepts from basic values and check for errors.

```{r check}
Bergant's avatar
Bergant committed
check <- check_statement(balance_sheet2014)
check
```

Bergant's avatar
Bergant committed
In case of error the numbers with errors will be presented along with elements:
```{r check_error}
check_statement(
  within(balance_sheet2014, InventoryNet <- InventoryNet * 2)
)
Bergant's avatar
Bergant committed
  
```


Bergant's avatar
Bergant committed
Validation returns all calculation results in a readable data frame.
Lets check only operating income from income statement:
Bergant's avatar
Bergant committed

Bergant's avatar
Bergant committed
```{r check_results check}
check <- check_statement(income2014, element_id = "OperatingIncomeLoss")
check
check$expression[1]
check$calculated / 10^6
Bergant's avatar
Bergant committed

Bergant's avatar
Bergant committed
```


Bergant's avatar
Bergant committed
## Merge statements from different periods
Use `merge` function to create single financial statement data from two 
statements. 
```{r merge_statement}
balance_sheet <- merge( balance_sheet2013, balance_sheet2014 )
```

Bergant's avatar
Bergant committed
The structure of merged balance sheets may differ if XBRL
Bergant's avatar
Bergant committed
taxonomy changes. 
Bergant's avatar
Bergant committed
Function `merge` takes care of it by expanding the elements 
Bergant's avatar
Bergant committed
hierarchy to fit both statements. 
Bergant's avatar
Bergant committed
The values of any missing elements in different periods is set to 0.
Bergant's avatar
Bergant committed

To merge all statements from *statements* object use merge on statements objects:
```{r merge_statements}
# merge all statements
st_all <- merge( st2013, st2014 )
Bergant's avatar
Bergant committed
# check if balance sheets are merged:
Bergant's avatar
Bergant committed
balance_sheet <- st_all$StatementOfFinancialPositionClassified
Bergant's avatar
Bergant committed
balance_sheet$endDate
Bergant's avatar
Bergant committed
```

Bergant's avatar
Bergant committed
## Merge different types of statements
If there are no matching elements between the two statements 
`merge` joins statements by matching their periods.
For some financial ratio calculations the combined statement may be  
a better starting point.

```{r merge_types, eval=FALSE}
  merge.statement(
    st_all$StatementOfFinancialPositionClassified, 
    st_all$StatementOfIncome )
```


## Calculate financial ratios
Statement object (in our case `balance_sheet`) is also a data frame object
with statement elements as columns and time periods as rows.
Bergant's avatar
Bergant committed
It is possible then to use statement as a data frame.
Bergant's avatar
Bergant committed

Lets calculate current ratio which is defined by

$$ Current Ratio = \frac{Current Assets}{Current Liabilities} $$

Bergant's avatar
Bergant committed
With dplyr package we can use `mutate`, `select` or `transmute` functions:

Bergant's avatar
Bergant committed
```{r dplyr}
Bergant's avatar
Bergant committed
library(dplyr)

Bergant's avatar
Bergant committed
balance_sheet %>% transmute(
  date = endDate, 
  CurrentRatio = AssetsCurrent / LiabilitiesCurrent
)
Bergant's avatar
Bergant committed

```

Bergant's avatar
Bergant committed
By using `finstr::calculate` function we can achieve the same result but
don't have to handle the date field and there is a rounding parameter. 
Lets calculate for example two ratios:
Bergant's avatar
Bergant committed

```{r calculate}

Bergant's avatar
Bergant committed
balance_sheet %>% calculate( digits = 2,
Bergant's avatar
Bergant committed
  
    Current_Ratio = AssetsCurrent / LiabilitiesCurrent,
    
    Quick_Ratio =  
      ( CashAndCashEquivalentsAtCarryingValue + 
          AvailableForSaleSecuritiesCurrent +
          AccountsReceivableNetCurrent
        ) / LiabilitiesCurrent
    
)
```


Bergant's avatar
Bergant committed
If we need a period average value we can use a `lag` function.
For example, to calculate *DSO* (days sales outstanding) over longer periods
the average of account receivable is compared to net sales.

Bergant's avatar
Bergant committed
We will use the formula for yearly preiods:
Bergant's avatar
Bergant committed

$$ DSO = \frac{Average Accounts Receivable}{Sales Revenue} \times 365 $$

In this case we need to connect two type of statements: balance sheets and
Bergant's avatar
Bergant committed
income statements. With matching reporting periods it can be accomplished 
with joining two data frames:
Bergant's avatar
Bergant committed

```{r DaysSalesOutstanding}

Bergant's avatar
Bergant committed
merge(balance_sheet, st_all$StatementOfIncome ) %>% calculate( digits = 2,
                                                               
Bergant's avatar
Bergant committed
    .AccountReceivableLast = lag(AccountsReceivableNetCurrent),
    .AccountReceivableAvg = (.AccountReceivableLast + AccountsReceivableNetCurrent)/2,
Bergant's avatar
Bergant committed
    
Bergant's avatar
Bergant committed
    DaysSalesOutstanding = .AccountReceivableAvg / SalesRevenueNet * 365 
  )
Bergant's avatar
Bergant committed

```
Bergant's avatar
Bergant committed

Bergant's avatar
Bergant committed
The leading dot instructs the calculate function to hide the value. In our case
Bergant's avatar
Bergant committed
only DaysSalesOutstanding is selected in final result. Use `digits` parameter to control rounding.
Bergant's avatar
Bergant committed

Bergant's avatar
Bergant committed
## Reusing calculations
When running same calculation for different statements, define the
Bergant's avatar
Bergant committed
calculation with `calculation` and call `calculate` with argument
`calculations`:

Bergant's avatar
Bergant committed
```{r calculation}
# define calculation
Bergant's avatar
Bergant committed
profit_margins <- calculation(
Bergant's avatar
Bergant committed
  
  Gross_Margin = 
    (SalesRevenueNet -  CostOfGoodsAndServicesSold) / SalesRevenueNet,
  
  Operating_Margin =
    OperatingIncomeLoss / SalesRevenueNet,
  
  Net_Margin = 
Bergant's avatar
Bergant committed
    NetIncomeLoss / SalesRevenueNet
Bergant's avatar
Bergant committed
  
Bergant's avatar
Bergant committed
)

# run profit margins for two different statements
Bergant's avatar
Bergant committed
income2013 %>% calculate(calculations = profit_margins, digits = 2)
income2014 %>% calculate(calculations = profit_margins, digits = 2)
Bergant's avatar
Bergant committed

```

Bergant's avatar
Bergant committed
## Rearranging statement hierarchy
Calculations gives us freedom to use any formula with any data from 
financial statements.
Bergant's avatar
Bergant committed
Most of the time this is not necessary as we can get useful information just by
Bergant's avatar
Bergant committed
regrouping calculation hierarchy.

There are many additional reasons why is rearranging statements useful step before
actual calculations:

   * We can avoid errors in formulas with many variables
   * Accounting taxonomies do change and using many formulas on original statement is
   harder to support than using custom hierarchy for analysis starting point
   * When sharing analysis it is much easier to print 6 values instead of 30
      
To rearrange the statement to simple 2-level hierarchy use `expose` function.

Bergant's avatar
Bergant committed
```{r expose}
Bergant's avatar
Bergant committed
expose( balance_sheet,
  
  # Assets
  `Current Assets` = "AssetsCurrent",
  `Noncurrent Assets` = other("Assets"),

  # Liabilites and equity
  `Current Liabilities` = "LiabilitiesCurrent",
  `Noncurrent Liabilities` = other(c("Liabilities", "CommitmentsAndContingencies")),
  `Stockholders Equity` = "StockholdersEquity"
)

```

Balance sheet stays divided by *Assets* and *Liabilities and Equity*.
Bergant's avatar
Bergant committed
For the second level we are exposing *current assets* from *noncurrent* and 
similar is done for the *liabilities*. We choose to separate *equity*.
Bergant's avatar
Bergant committed

Function `expose` expects a list of vectors with element names.
Function `other` helps us identify elements without enumerating every single element.
Using `other` reduces a lot of potential errors as the function "knows" which
elements are not specified and keeps the balance sheet complete.


Sometimes it is easier to define a complement than a list of elements.
Bergant's avatar
Bergant committed
In this case we can use the `%without%` operator. Lets expose for example
*tangible* and then *intangible* assets:
Bergant's avatar
Bergant committed

```{r expose_without}
expose( balance_sheet,
  
  # Assets
  `Tangible Assets` = 
    "Assets" %without% c("Goodwill", "IntangibleAssetsNetExcludingGoodwill"),
  `Intangible Assets` = other("Assets"),

  # Liabilites and equity
  `Liabilities` = c("Liabilities", "CommitmentsAndContingencies"),
  `Stockholders Equity` = "StockholdersEquity"
)

```
Bergant's avatar
Bergant committed

Bergant's avatar
Bergant committed
##Lagged difference
To calculate lagged difference for entire statement use `diff` function.
The result is statement of changes between successive years:

```{r diff}

Bergant's avatar
Bergant committed
diff(balance_sheet)
Bergant's avatar
Bergant committed

```
Bergant's avatar
Bergant committed

Bergant's avatar
Bergant committed

Bergant's avatar
Bergant committed
# Balance sheet visualization

Bergant's avatar
Bergant committed
## Prepare custom hierarchy
Bergant's avatar
Bergant committed
The only way to visualize a balance sheet is by exposing a limited number of values. 
Bergant's avatar
Bergant committed
The first step is then to aggregate a balance sheet by selected concepts.
Bergant's avatar
Bergant committed
We can use `expose` to specify these groups of elements. For example:
Bergant's avatar
Bergant committed

Bergant's avatar
Bergant committed
```{r expose_graph}
Bergant's avatar
Bergant committed
bs_simple <- expose( balance_sheet,
  
  # Assets
  `Current Assets` = "AssetsCurrent",
  `Noncurrent Assets` = other("Assets"),
  # Liabilites and equity
  `Current Liabilities` = "LiabilitiesCurrent",
  `Noncurrent Liabilities` = other(c("Liabilities", "CommitmentsAndContingencies")),
  `Stockholders Equity` = "StockholdersEquity"
)
Bergant's avatar
Bergant committed

Bergant's avatar
Bergant committed
```
Bergant's avatar
Bergant committed

Bergant's avatar
Bergant committed
## Print as a table
```{r htmlTable, warning=FALSE}
library(htmlTable)
print(bs_simple, html = TRUE, big.mark = ",", dateFormat = "%Y")
```

Bergant's avatar
Bergant committed

Bergant's avatar
Bergant committed
## Double stacked graph
Using ggplot2 package we can plot a simplified balance sheet:
Bergant's avatar
Bergant committed

Bergant's avatar
Bergant committed
```{r graph_byside, fig.width=7.0}
Bergant's avatar
Bergant committed
library(ggplot2)

plot_double_stacked_bar(bs_simple)
Bergant's avatar
Bergant committed

```

Bergant's avatar
Bergant committed
Another option is to group by faceting balance sheet side instead of date:
Bergant's avatar
Bergant committed

Bergant's avatar
Bergant committed
```{r graph_bydate, fig.width=7.0}
Bergant's avatar
Bergant committed

Bergant's avatar
Bergant committed
plot_double_stacked_bar(bs_simple, by_date = FALSE)
Bergant's avatar
Bergant committed

```
Bergant's avatar
Bergant committed

Using **proportional** form we reveal the changes in balance sheet structure:

```{r graph_prop, fig.width=7.0}

bs_simple_prop <- proportional(bs_simple)
plot_double_stacked_bar(bs_simple_prop)

```