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

Hi all, this is the second part to the “Trading Strategy Analysis using Python and the FFN Package” post (the first part can be found here).

Last time we went over the use of the “PerformanceStats” object in ffn, whereas this time I want to concentrate on the “GroupStats” object. The former is for use with single series of data, whereas the latter can accept a full DataFrame with multiple columns representing multiple price/equity series simultaneously.

Let’s now create some new data – this time lets set up a DataFrame containing 4 columns of data, each representing a trading strategy (or asset price) ‘equity curve’n and display the first 5 rows of that data.

1 2 3 4 5 6 7 8 9 10 11 |
num_days = 1000 data1 = pd.DataFrame((np.random.randn(num_days) + np.random.uniform(low=0.0, high=0.2, size=num_days)),index=index,columns=['Data1']) data2 = pd.DataFrame((np.random.randn(num_days) + np.random.uniform(low=0.0, high=0.2, size=num_days)),index=index,columns=['Data2']) data3 = pd.DataFrame((np.random.randn(num_days) + np.random.uniform(low=0.0, high=0.2, size=num_days)),index=index,columns=['Data3']) data4 = pd.DataFrame((np.random.randn(num_days) + np.random.uniform(low=0.0, high=0.2, size=num_days)),index=index,columns=['Data4']) data = pd.concat([data1,data2,data3,data4],axis=1) data = data.cumsum() + 100 data.iloc[0] = 100 data.head() |

The first step is identical to the first step for a single series of returns that we showed in part one of this tutorial – we change it into an ffn object, this time a “GroupStats” objectand assign that to a variable.

1 |
perf = data.calc_stats() |

If we print the type of the variable we have just created – we can indeed see it is an “ffn.core.GroupStats” object – when dealing with a single series of returns we created an “ffn.core.PerformanceStats” object, which is worth noting.

1 |
print(type(perf)) |

<class ‘ffn.core.GroupStats’>

Plotting the multiple returns series is similar also:

1 |
perf.plot() |

We can also run the same commands on a pandas DataFrame of stock price series we have downloaded from Yahoo Finance:

1 2 3 4 5 6 7 8 9 |
import pandas_datareader.data as web stocks = ['AAPL','AMZN','MSFT','NFLX'] data = web.DataReader(stocks,data_source='yahoo',start='01/01/2010')['Adj Close'] data.sort_index(ascending=True,inplace=True) perf = data.calc_stats() |

If we were to plot the data now as follows – you will notice that the prices are automatically rebased to 100 at the start, in order to make them more comparable:

1 |
perf.plot() |

Let’s stick with using opur downloaded real stock price data from now on – we can display stats very quickly for them all at the same time using the same call as for a single series:

1 |
perf.display() |

This table of information can be accessed as a normal Pandas DataFrame using the folkowing syntax (it is also index-able as normal):

1 |
perf.stats |

And can be indexed, for example, as follows:

1 |
perf.stats.loc['cagr'] |

Up until now the GroupStats object has generated output very similar to the PerformanceStats object – so you may be wondering why have them both if they both just do the same things! Well let’s move onto some “GroupStats” specific functionality.

Let’s begin by creating a DataFrame containing a the series of log returns for each of the stock price curves in our “data” DataFrame.

1 2 3 |
returns = data.to_log_returns().dropna() print(returns.head()) |

We can quickly create a multiplot of each of the series’ log returns histograms along with screting a DataFrame containing the correlation matrix for them also.

1 |
ax = returns.hist(figsize=(20, 10),bins=30) |

1 |
returns.corr().as_format('.2f') |

If you want something more visually appealing than the above DataFrame, then it’s just as easy to create a heatmap of the correlation matrix instead:

1 |
returns.plot_corr_heatmap() |

There we go – that heatmap shows an output that is a little more relevant than our previous randomly generated data. Let’s stick with using this data downloaded from Yahoo Finance for the moment rather than the random data. We can very quickly output the “optimal portolio” based on classic Markowitz Mean/Variance Optimisation methods:

1 |
returns.calc_mean_var_weights().as_format('.2%') |

The lookback returns info can be accessed and indexed as follows:

1 |
perf.display_lookback_returns().loc['mtd'] |

A nice looking scatterplot matrix can easily be produced as follows:

1 |
perf.plot_scatter_matrix() |

We can also plot drawdown series for multiple assets simultaneously as follows:

1 |
ffn.to_drawdown_series(data).plot(figsize=(15,10)) |

I’ll leave it here for this post, I hope some of you found the above useful…there is further functionality available from the FFN package, but I’ll leave that up to you all to explore the more obscure parts of the module.

Until next time…

Hi S666 just wanted to say love the blog and articles. They are pitched at just the right level for people like me.. strong python programers with weak quant skills 😉

I pick up much better with examples and this intro to ffn is perfect.

I notice that those guys have a backtesting framework ‘bt’ that leverages ffn.

Have you used that?

its only recently I’m ashamed to say that I’ve been using pandas and all that stuff for my python systems. I’ve always crafted loops/ event based back testers.

Hi Stephen, thanks for the kind words – glad you like the blog!

In terms of “bt” backtester framework – I have heard of it, but never actually dug into it for use…perhaps I should take a look and make that the subject of my next few posts – would you find that helpful at all?

If you find the bt stuff interesting and don’t have a backlog of other topics I would find it very interesting.

Of course, look forward. To your next post anyway whatever it is. Cheers

Clear demo of the FFN package. Thank you S666 :).

is the wheel file available for ffn?

Hi Kannan – I had a quick search around Google and couldn’t find anything. The package can be downloaded from the relevant Github – or installed using pip….are you not able to just use the “pip” command to install it?