Report delle performance di una strategia in Python

Report delle performance di una strategia in Python – parte 2

Sommario

Questo articolo è la seconda parte dell’attuale “mini-serie” che descrivere come implementare un report delle performance di una strategia in Python per verificare i risultati di un backtest. In particolare offre una panoramica su come creare un generatore HTML personalizzabile per un report di una strategia di trading.

Come descritto nel precedente articolo, l’obiettivo è costruire uno strumento grazie al quale è sufficiente un clic per produrre report approfonditi e interattivi relativi alle prestazioni di una strategia. Questo report è fondamentale per analizzare e filtrare il “valore” in termini di risultati del backtest di una strategia di trading. E’ sufficiente inserire nel formato corretto i dati dell’equity della strategia e (facoltativamente) i dati di un’equity benchmark all’interno di un file csv, senza dover ricreare i calcoli e grafici ogni volta.

Il codice descritto nel precedente articolo produce un file “report.html” che contiene i grafici delle equity e dei drawdown.

Indicatori delle prestazioni

Iniziamo con alcune modifiche. Invece di avere due grafici in alto, vogliamo ampliare il “Grafico delle prestazioni” ed aggiungere una tabella “Indicatori delle prestazioni” alla sua destra. In basso vogliamo creare e posizionare una tabella dei rendimenti mensili.

Innanzitutto aggiungiamo la tabella dei rendimenti mensili. Modifichiamo il file main.py come segue.

				
					
import os
import pandas as pd
import numpy as np
import plotly
import plotly.graph_objs as go
import ffn
from jinja2 import Environment, FileSystemLoader


class PerformanceReport:
    """ Report con le statistiche delle performance stats per una data strategia
    """

    def __init__(self, infilename):
        self.infilename = infilename
        self.get_data()

    def get_data(self):
        basedir = os.path.abspath(os.path.dirname('__file__'))
        data_folder = os.path.join(basedir, 'data')
        data = pd.read_csv(os.path.join(data_folder, self.infilename), index_col='date',
                           parse_dates=True, dayfirst=True)
        self.equity_curve = data['equity_curve']

        if len(data.columns) > 1:
            self.benchmark_curve = data['benchmark']

    def generate_html(self):
        env = Environment(loader=FileSystemLoader('.'))
        template = env.get_template("templates/template.html")
        perf_chart = self.plot_performance_chart()
        drawdown_chart = self.plot_drawdown_chart()
        monthly_table = self.create_monthly_table(self.equity_curve.pct_change().dropna(), 1)
        html_out = template.render(perf_chart=perf_chart, drawdown_chart=drawdown_chart, monthly_table=monthly_table)
        return html_out

    def generate_html_report(self):
        """ Restitusice un report HTML con le analisi
        """
        html = self.generate_html()
        outputdir = "output"
        outfile = os.path.join(outputdir, 'report.html')
        file = open(outfile, "w")
        file.write(html)
        file.close()

    def rebase_series(self, series):
        return (series / series.iloc[0]) * 100

    def plot_performance_chart(self):

        trace_equity = go.Scatter(
            x=self.equity_curve.index.tolist(),
            y=self.rebase_series(self.equity_curve).values.tolist(),
            name='strategy',
            yaxis='y2',
            line=dict(color=('rgb(22, 96, 167)')))

        trace_benchmark = go.Scatter(
            x=self.benchmark_curve.index.tolist(),
            y=self.rebase_series(self.benchmark_curve).values.tolist(),
            name='benchmark',
            yaxis='y2',
            line=dict(color=('rgb(22, 96, 0)')))

        layout = go.Layout(
            autosize=True,
            legend=dict(orientation="h"),
            title='Performance Chart',
            yaxis=dict(
                title='Performance'))

        perf_chart = plotly.offline.plot({"data": [trace_equity, trace_benchmark],
                                          "layout": layout}, include_plotlyjs=False,
                                         output_type='div')

        return (perf_chart)

    def plot_drawdown_chart(self):

        trace_equity_drawdown = go.Scatter(
            x=self.equity_curve.to_drawdown_series().index.tolist(),
            y=self.equity_curve.to_drawdown_series().values.tolist(),
            name='strategy drawdown',
            yaxis='y2',
            line=dict(color=('rgb(22, 96, 167)')))

        trace_benchmark_drawdown = go.Scatter(
            x=self.benchmark_curve.to_drawdown_series().index.tolist(),
            y=self.benchmark_curve.to_drawdown_series().values.tolist(),
            name='benchmark drawdown',
            yaxis='y2',
            line=dict(color=('rgb(22, 96, 0)')))

        layout = go.Layout(
            autosize=True,
            legend=dict(orientation="h"),
            title='Drawdown Chart',
            yaxis=dict(
                title='Drawdown'))

        drawdown_chart = plotly.offline.plot({"data": [trace_equity_drawdown, trace_benchmark_drawdown],
                                              "layout": layout}, include_plotlyjs=False,
                                             output_type='div')

        return (drawdown_chart)

    def create_monthly_table(self, return_series, num_of_compenents):
        return_series.rename('weighted rets', inplace=True)
        return_series = (return_series / float(num_of_compenents))
        returns_df_m = pd.DataFrame((return_series + 1).resample('M').prod() - 1)
        returns_df_m['Month'] = returns_df_m.index.month
        monthly_table = returns_df_m[['weighted rets', 'Month']].pivot_table(returns_df_m[['weighted rets', 'Month']], index=returns_df_m.index, columns='Month', aggfunc=np.sum).resample('A')
        monthly_table = monthly_table.aggregate('sum')
        monthly_table.columns = monthly_table.columns.droplevel()

        monthly_table.index = monthly_table.index.year
        monthly_table['YTD'] = ((monthly_table + 1).prod(axis=1) - 1)
        monthly_table = monthly_table * 100

        monthly_table.columns = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec',
                                 'YTD']
        return monthly_table.round(2).fillna("").to_html(classes="table table-hover table-bordered table-striped")


if __name__ == "__main__":
    report = PerformanceReport('data.csv')
    report.generate_html_report()
    
				
			
Modifichiamo il file template.html come segue:
				
					<!DOCTYPE HTML>
<html>
<head>
    <meta charset="utf-8">
    <title>App</title>
    
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css" integrity="sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4" crossorigin="anonymous">
    
    <link rel="stylesheet" href="static/app.css">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    
    <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
    
    <script charset="utf-8" src="static/app.js"></script>
</head>