Home Basic Data Analysis Trading Strategy Analysis using Python and the FFN Package – Part 1

# Trading Strategy Analysis using Python and the FFN Package – Part 1

In this post I will be reviewing and running through examples of using the brilliant python module, “ffn – Financial Functions for Python“, which has been created by Philippe Morissette and released on the MIT license. The github page can be found here (http://pmorissette.github.io/ffn/index.html)

The module helps quickly carry out analysis of trading strategies and financial asset price series/history. It can deal with single series using the “PerformanceStats” class, or multiple combined assets simultaneously using the “GroupStats” class.

Let’s begin with the simple, one asset/strategy case and run through some examples.

We begin by importing the relevant modules (and if working in a Jupyter notebook, the call to allow matplotlib objects to be rendered in the browser).

```import pandas as pd
import numpy as np
import ffn
%matplotlib inline
```

Now lets generate a set of random performance data, spanning a 1000 day period. We start by setting the number of days we want (i.e. 1000), then we use the numpy “randn” call to generate an array of 1000 numbers drawn from the normal distribution. To give our data a slight upward bias, I have then added to these figures a random value chosen from the uniform distribution with a maximum value of 0.2 and a minimum value of 0.0. These random figures represent the daily returns of our trading strategy, or stock price.

I have then created a Pandas DataFrame from this “returns” data and added a column of the cumulative sum of those returns, adding 100 to represent starting capital – these represent the strategy “equity” or the amount of cash we have in our trading account.

The ffn package does allow the direct download of historic stock price data from Yahoo Finance but thought i would use random data in the first instance. I will go over the download of data later in the post.

```num_days = 1000
data = (np.random.randn(num_days) + np.random.uniform(low=0.0, high=0.2, size=num_days))
index = pd.date_range('01/01/2010',periods=num_days, freq='D')
data = pd.DataFrame(data,index=index,columns=['Returns'])
data['Equity'] = data.cumsum() + 100
data.iloc = 100
```

So the “Equity” column of our DataFrame is the one we are most interested in as this is our theoretical trading account equity. We start by using the “calc_stats” method on this data and assigning that to a variable called “perf”

```perf = data['Equity'].calc_stats()
```

If we lok at what “perf” actually is by looking at its “type”, we can see it is a ffn.core.PerformanceStats object. This means we can use any of the PerformanceStats object methods on it.

```type(perf)
```

“ffn.core.PerformanceStats”

Let’s start by plotting the equity curve. This is very similar to just calling the usual “plot()” syntax from Pandas.

```perf.plot()
```

Great stuff, so we now have a visual representation of our equity curve. Let’s move on to some statistics; we can very easily do this using ffn by calling the “display()” method. This will print out the following set of comprehensive statistics.

```perf.display()
```

That is a pretty comprehensive set of statistics – the Sharpe Ratio, Calmar Ratio, Total Returns, CAGR, Max Drawdown, Periodic Returns plus more…that’s not a bad time saver – if we were to have to calculate these all ourselves it could take quite some time.

The next ffn method we might be interested in is that to represent a table of monthly returns. Again, it’s as easy as this:

```perf.display_monthly_returns()
```

How about plotting a visual representation of our strategy drawdown series…it can be carried out with a single line of code:

```ffn.to_drawdown_series(data['Equity']).plot(figsize=(15,7),grid=True)
```

And plotting a histogram of returns is again as simple as:

```perf.plot_histogram()
```

If we want to get hold of the set of comprehensive analysis statistics mentioned above, but this time in a Pandas series, rather than printed out as a table, we can get hold of it as follows:

```perf.stats
```

This Series object is index-able, just like any other Pandas Series so we can pick out any relevant items we need using the following syntax – for example if we wanted the Yearly Sharpe Ratio:

```perf.stats['yearly_sharpe']
```

“1.9283569892842471”

Finally for the “PerformanceStats” object, we can extract the series of “Lookback Returns” as simply as follows (this can also be indexed similarly to the “perf.stats” object mentioned above).

```perf.display_lookback_returns()
```

I’ll leave it here for this post, and in the upcoming post will deal with using the FFN package when considering multiple price/equity series simultaneously using the “GroupStats” object.

Until next time!

#### 17 comments

14 March 2018 - 03:39

so nice thanks

20 June 2018 - 09:57

If I use monthly return, how can I use ffn package to do the similar analysis? Thanks.

20 June 2018 - 17:18

Hi Tian – what I tend to do if I have monthly returns/price series is just feed in that data as normal and ignore any stats that are presented at less than monthly frequency – so disregard stats calculated on a “daily” basis and just accept the stats named as “monthly” or longer time period.

20 June 2018 - 09:58

If I use monthly return, how can I use ffn package to do the similar analysis? Thank you.

29 June 2018 - 14:10

Hi, thanks for your blog and all your articles. I am combing the moving average strategy part 1 with this library and I am getting equity curve but the stats is incomplete. I guess that’s because return of this strategy is very low. Would you just indicate how to adapt the strategy to be combined with the FFN ?
Thanks
LS

1 July 2018 - 09:09

Hi there – I can definitely try to help. What parts of the stats are incomplete? Do you have enough history of the equity curve to calculate all the stats?

10 July 2018 - 18:00

Hi, thanks for your help. I am sharing the jupyter file here: http://nbviewer.jupyter.org/gist/Ludek1234/57283adcb4d00467f5fc90a847fbd71c

10 January 2019 - 21:57

Hi, nice article and superb blog. I was trying to use this package but, when using calc_stats() I consistently get the error:

negative_returns = np.minimum(returns, 0.)
RuntimeWarning: invalid value encountered in minimum

even when I replicated your code above. Any guidance? Thanks

10 January 2019 - 22:14

That’s strange – have you replicated the code exactly? What about if you use the exact code below:

``````import pandas as pd
import numpy as np
import ffn
%matplotlib inline

num_days = 1000
data = (np.random.randn(num_days) + np.random.uniform(low=0.0, high=0.2, size=num_days))
index = pd.date_range('01/01/2010',periods=num_days, freq='D')
data = pd.DataFrame(data,index=index,columns=['Returns'])
data['Equity'] = data.cumsum() + 100
data.iloc = 100

perf = data['Equity'].calc_stats()

perf.display()
``````
11 January 2019 - 07:35

Copied and pasted that exact code (except for “%matplotlib inline” as I am running Python 3.6.8 under IDLE environment). This is the annoying warning I am always getting:

Warning (from warnings module):
File “C:\Python36-64\lib\site-packages\ffn\core.py”, line 2054
negative_returns = np.minimum(returns, 0.)
RuntimeWarning: invalid value encountered in minimum

Warning (from warnings module):
File “C:\Python36-64\lib\site-packages\ffn\core.py”, line 2056
res = np.divide(er.mean(), std)
RuntimeWarning: divide by zero encountered in true_divide

Should I switch from IDLE to Anaconda environment?

11 January 2019 - 10:37

After some research and trials on your exact code, I’ve managed to get rid of the first of the two warnings (the one connected with np.minimum(returns, 0.)) by using the ffn package from github instead of the one coming from pip install. As far as the second warning is concerned (np.divide(er.mean(), std)), the only solution was to suppress the warning by adding the line np.seterr(all=’ignore’) to your code. Looks like a bug in the ffn package still unresolved.

11 January 2019 - 17:37

Interesting – so now you have suppressed the warning, the code can be run and the calculations are being made/displayed?

11 January 2019 - 18:14

Yes, the entire replicated code could be run and calculations were displayed. Thanks for you reply.

14 January 2019 - 17:23

Great stuff – glad to hear it’s working!!

3 December 2019 - 13:05

Hi – Why don’t the monthly returns add up to the YTD in your table above ?

6 December 2019 - 06:23

Hi dirk, rather than simply adding the returns in a “simple arithmetic return” approach, the “proper” method is actually to calculate the monthly compounded, time weighted return by adding 1 to each monthly return figure and then multiplying all the monthly values together, resulting in a final value, which we then subtract the 1 from to end at the monthly compounded time weighted return.

13 December 2019 - 13:46

Thanks for taking the time to answer.