You find the source code here on GitHub.
This post is not an investment advice. It is simply an experiment with historical data.
Introduction
This is a software engineering blog, but one of my hobbies is stock exchange and investing and these two actually fit together very well. This post follows this theme: I wanted to run some statistics on financial data and I used F# and .NET Core for that.
Problem description
At the time of writing it is 2017 July and markets reach all-time highs very frequently and this period started a long time ago, so we are in a big bull market.
Many theories say that an “average” investor should not try to time the market; the best way is just to invest into an index (e.g. with an ETF) and stay in it very long and it does not matter where the index is, the investor just should start to invest.
Other theories suggest anti-cyclic investing, meaning an investor (including long-term investors) should invest when the market is down and should sell when the market is up.
I created a small program that basically compares these approaches.
“The market is up” in this case is defined as all-time high. The program does not shoot for selling when the “Market is down”, in both cases it just holds the position for the same number of years.
The program solving this problem
I wanted to use F# and .NET Core to get answers to the problem stated above.
In the GitHub repo there is a small F# application with some functions which do the calculation. It basically maps every trading day into one of two lists:
- List of all-time highs*
- List of not all-time highs
*Smoothing:
The program works with end of the day data and it basically knows when the market closed at all-time high. But in reality you have a very little chance to be the last who trades on the market on a given day. Additionally, for this experiment it does not really make a difference whether the market is at it’s all time-high, or let’s say 2 days after the last all time high with 0.2% below that last all-time high value when a long-term investor enters the market. Both entry points are basically on the current top of a bull market. Therefore, every entry position that is a current all-time high OR within 3 days and 99% of the last all-time high (allowing a 1% correction) count as all-time high. This means when let’s say July 17 closed at all-time high and the lowest price until July 18 was below 0.4% of the last all-time high then an entry at July 18 also counts as all-time high.
After creating the two lists it remembers the price on that day, calculates the gain between that day and the trading day 5 years after the initial day. At the end for both lists it calculates the average gain.
Additionally, I also wanted to visualize the data. Now the problem was that none of the traditional F# charting libraries worked on .NET Core. Plus I used my MacBook with macOS (which I wanted to keep), so there was really no way to use any Windows stuff here.
So I decided to write the charting library myself. For this I used SkiaSharp, which is a cross platform graphics library from the Xamarin guys based on Skia. The cool thing about it is that there is a netstandard1.3 based SkiaSharp NuGet package (see here), which is a perfect fit for my .NET Core - F# application.
I did not want to code the SkiSharp logic in F#, so the charting itself is done in C#. That library can be found here. It's still in a very early stage! It's basically a prototype, but it does the job to visualize the data. The visualization is stored in .jpeg files.
Results
I ran the tests on these Indexes:
- Dow Jones (^DJI.csv)
- S&P500 (^GSPC.csv)
- NASDAQ (^IXIC.csv)
- DAX (^GDAXI.csv)
The charts show two data sets: The red-green plot is the index itself. The green points are the all-time highs, the red points are not all-time highs. The values can be seen on the left axis.
The black values are the gains: for every trading day it shows how much percent the value changed when you invested on that day and kept the money in the index for 5 years. It is centered on the y axis (date) -so that is 0% change- and the value range can be seen on the right axis.
Dow Jones
Processing ^DJI.csv
Historical Data Size: 8183
Date Range: 01/29/1985 - 07/14/2017
Number of transactions that were started at all-time high: 1069
Number of transactions that were started at not at all-time high: 5853
All-time high avg Gain: 64.419412817524026636163519599%
Not All-time high avg Gain:: 54.537834148644233936464592119%
S&P500
Processing ^GSPC.csv
Historical Data Size: 16998
Date Range: 01/03/1950 - 07/21/2017
Number of transactions that were started at all-time high: 2092
Number of transactions that were started at not at all-time high: 13645
All-time high avg Gain: 54.216484601020712535593668284%
Not All-time high avg Gain:: 49.006853908185915798452714764%
DAX
Processing ^GDAXI.csv
Historical Data Size: 7468
Date Range: 12/30/1987 - 07/21/2017
Number of transactions that were started at all-time high: 601
Number of transactions that were started at not at all-time high: 5593
All-time high avg Gain: 44.500410112352093853479183682%
Not All-time high avg Gain:: 63.122345602494611035971993199%
NASDAQ
Processing ^IXIC.csv
Historical Data Size: 11720
Date Range: 02/05/1971 - 07/21/2017
Number of transactions that were started at all-time high: 1258
Number of transactions that were started at not at all-time high: 9201
All-time high avg Gain: 61.698207737683027462731936261%
Not All-time high avg Gain:: 76.237962446772308946927115902%