Learn how to generate and customize performance reports for your strategies.
A Primer notebook containing the code used in this page is available in the research environment. See Example notebooks for more information.
Pre-requisites
You have initialized a research environment. See Environment setup for instructions.
Importing Sigetch performance analytics
Copy import sigtech . framework as sig
from sigtech . framework . analytics . performance . performance_report \
import PerformanceReport , View , CustomView
import uuid
import datetime as dtm
import pandas as pd
import numpy as np
import seaborn as sns
sns . set (rc = { 'figure.figsize' : ( 18 , 6 )})
if not sig . config . is_initialised ():
date = dtm . datetime ( 2020 , 10 , 30 )
sig . config . init (data_date = date, env_date = date)
sig . config . set (sig.config.HISTORY_DATA_FILL_MISSING, True )
Create a basket of strategies
This step creates sample strategies for the performance report:
Copy from sigtech . framework . default_strategy_objects . rolling_futures import *
from sigtech . framework . default_strategy_objects . rolling_futures_fx_hedged import *
us_basket = sig . BasketStrategy (
currency = 'USD' ,
start_date = dtm. date ( 2017 , 1 , 4 ),
end_date = dtm. date ( 2020 , 1 , 4 ),
constituent_names = [
es_index_front ().name,
ty_comdty_front ().name,
],
weights = [ 0.5 , 0.5 ],
rebalance_frequency = 'EOM' ,
ticker = 'USD USA BASKET {} ' . format ( str (uuid. uuid4 ())[: 8 ]),
)
ge_basket = sig . BasketStrategy (
currency = 'USD' ,
start_date = dtm. date ( 2017 , 1 , 4 ),
end_date = dtm. date ( 2020 , 1 , 4 ),
constituent_names = [
usd_gx_index_front ().name,
usd_rx_comdty_front ().name,
],
weights = [ 0.6 , 0.4 ],
rebalance_frequency = 'EOM' ,
ticker = 'USD GERMANY BASKET {} ' . format ( str (uuid. uuid4 ())[: 8 ]),
)
jp_basket = sig . BasketStrategy (
currency = 'USD' ,
start_date = dtm. date ( 2017 , 1 , 4 ),
end_date = dtm. date ( 2020 , 1 , 4 ),
constituent_names = [
usd_nk_index_front ().name,
usd_jb_comdty_front ().name,
],
weights = [ 0.3 , 0.7 ],
rebalance_frequency = 'EOM' ,
ticker = 'USD JAPAN BASKET {} ' . format ( str (uuid. uuid4 ())[: 8 ]),
)
strategy1 = sig . BasketStrategy (
currency = 'USD' ,
start_date = dtm. date ( 2017 , 1 , 4 ),
end_date = dtm. date ( 2020 , 1 , 4 ),
constituent_names = [us_basket.name, ge_basket.name, jp_basket.name],
weights = [ 0.6 , 0.2 , 0.2 ],
rebalance_frequency = 'EOM' ,
ticker = 'USD WORLD TOP_LEVEL BASKET {} ' . format ( str (uuid. uuid4 ())[: 8 ])
)
strategy2 = sig . BasketStrategy (
currency = 'USD' ,
start_date = dtm. date ( 2017 , 1 , 4 ),
end_date = dtm. date ( 2020 , 1 , 4 ),
constituent_names = [ge_basket.name, jp_basket.name],
weights = [ 0.7 , 0.3 ],
rebalance_frequency = 'EOM' ,
ticker = 'USD WORLD TOP_LEVEL BASKET {} ' . format ( str (uuid. uuid4 ())[: 8 ])
)
strategy3 = sig . BasketStrategy (
currency = 'USD' ,
start_date = dtm. date ( 2017 , 1 , 4 ),
end_date = dtm. date ( 2020 , 1 , 4 ),
constituent_names = [ge_basket.name, jp_basket.name],
weights = [ 0.9 , 0.1 ],
rebalance_frequency = 'EOM' ,
ticker = 'USD WORLD TOP_LEVEL BASKET {} ' . format ( str (uuid. uuid4 ())[: 8 ])
)
Create a PerformanceReport from time series
A PerformanceReport can be created for a time series object. Method default_views()
shows the report views enabled by default for this object:
Python Output
Copy report0 = PerformanceReport (strategy1. history ())
report0 . default_views ()
Copy [View('SUMMARY_SINGLE'),
View('HISTORY'),
View('PRESENTATION_PERF_TABLE'),
View('SUMMARY_ROLLING_SERIES'),
View('ROLLING_RISK_VOL'),
View('ROLLING_RISK_SHARPE'),
View('ROLLING_PLOTS'),
View('MONTHLY_STATS'),
View('MONTHLY_STATS_HEATMAP'),
View('BENCHMARK_TABLE')]
Create a PerformanceReport from strategy
A PerformanceReport can also be created for a Strategy object. Method default_views()
shows an extended list of report views enabled by default:
Input Output
Copy report1 = PerformanceReport (strategy1)
report1 . default_views ()
Copy [View('SUMMARY_SINGLE'),
View('HISTORY'),
View('PRESENTATION_PERF_TABLE'),
View('SUMMARY_ROLLING_SERIES'),
View('ROLLING_RISK_VOL'),
View('ROLLING_RISK_SHARPE'),
View('POSITIONS_DF'),
View('TURNOVER'),
View('TURNOVER_STATS'),
View('ROLLING_PLOTS'),
View('MONTHLY_STATS'),
View('MONTHLY_STATS_HEATMAP'),
View('BENCHMARK_TABLE')]
Views
All built-in views can be accessed using the following syntax:
Customised views can be accessed using the following syntax:
Display a report
Method report()
will display the views enabled for the object used. Parameter dates
can be specified to reduce the output size for tables showing daily data.
Input Output
Copy few_dates = pd . date_range (dtm. date ( 2019 , 12 , 2 ),
dtm. date ( 2020 , 1 , 1 ), freq = '7d' )
report1 = PerformanceReport (strategy1, name = 'My Strategy' , dates = few_dates)
report1 . report ()
Specifying views when initializing PerformanceReport
Parameter views
can be used to select a subset of the default views to display:
Input Output
Copy report2 = PerformanceReport (strategy1,
name = 'My Strategy' ,
views = [View.MONTHLY_STATS,
View.PRESENTATION_PERF_TABLE],
dates = few_dates)
report2 . views ()
Copy [View('MONTHLY_STATS'), View('PRESENTATION_PERF_TABLE')]
Use of arrange_views()
Within the same report, the order of views can also be re-arranged:
Input Output
Copy report2 . arrange_views ([View.HISTORY, View.MONTHLY_STATS])
report2 . report ()
Add a benchmark strategy or time series
Parameter benchmark
can be used to add a strategy or time series to the performance report. The following example uses one strategy as input object:
Input Output
Copy s = es_index_front ()
b = z_index_front ()
report3 = PerformanceReport (s,
benchmark = b,
views = [View.POSITIONS_DF, View.TURNOVER_STATS],
dates = few_dates)
report3 . report ()
The same syntax is available for time series:
Input Output
Copy ts = s . history (). rename ( 'ES Rolling Future' ). loc [
dtm . date ( 2015 , 1 , 1 ): dtm . date ( 2020 , 1 , 1 )]
ts_b = b . history ()
report3 = PerformanceReport (ts,
benchmark = ts_b,
views = [View.HISTORY, View.MONTHLY_STATS,
View.BENCHMARK_TABLE])
report3 . report ()
Add customised views
Customized views can be added to PerformanceReport by simply defining a new function. The following example defines two functions handling a DataFrame and a Pyplot graph:
Copy # Define custom functions for view
def custom_table ( args ):
# This function expects 2 parameters (Strategy, Integer)
print ( 'Customising for: ' , args[ 0 ].name)
df = pd . DataFrame (np.random. randint ( 0 , args[ 1 ], size = ( 4 , 4 )),
columns = list ( 'ABCD' ))
return df
def custom_plot ( args ):
# This function expects 1 parameter (PerformanceReport)
import matplotlib . pyplot as plt
args . rolling_stat ( 'excess_return' ). plot (
title = "Rolling excess return" , figsize = [ 15 , 4 ])
plt . show ()
return
A CustomView
object helps wrapping the customised view and its parameters:
Copy custom_view1 = CustomView (custom_table, params = [strategy1, 99 ],
name = 'Custom Table 99' )
custom_view2 = CustomView (custom_table, params = [strategy1, 25 ],
name = 'Custom Table 25' )
custom_view3 = CustomView (custom_plot, params = report3,
name = 'Custom Plot' )
The customized views can now be added to the report:
Input Output
Copy report3 = PerformanceReport (strategy1, name = 'Customised Strategy' ,
views = [View.SUMMARY_SINGLE])
report3 . add_view (custom_view1)
report3 . add_views ([custom_view2, custom_view3])
report3 . views ()
Copy [View('SUMMARY_SINGLE'),
View('Custom Table 99'),
View('Custom Table 25'),
View('Custom Plot')]
Support for multiple strategies
Reports for multiple strategies can be visualized by adding them to a list:
Input Output
Copy report1 = PerformanceReport ([strategy1, strategy2],
benchmark = b, name = [ 'S1' , 'S2' ],
views = [View.SUMMARY_SINGLE, View.HISTORY,
View.TURNOVER_STATS, View.BENCHMARK_TABLE])
report1 . report ()
Support for multiple time series
Time series can be merged within a DataFrame object:
Input Output
Copy ts1 = es_index_front (). history (). rename ( 'ES Rolling Future' ). loc [
dtm . date ( 2015 , 1 , 1 ): dtm . date ( 2020 , 1 , 1 )]
ts2 = eo_index_front (). history (). rename ( 'EO Rolling Future' ). loc [
dtm . date ( 2015 , 1 , 1 ): dtm . date ( 2020 , 1 , 1 )]
b = z_index_front (). history ()
ts_df = pd . concat ({
'ES Rolling Future' : ts1,
'EO Rolling Future' : ts2,
}, axis = 1 )
rep = PerformanceReport (ts_df, benchmark = b,
views = [View.ROLLING_PLOTS, View.BENCHMARK_TABLE])
rep . report ()
Additional parameters for Performance Analytics
You can include currency based metrics in PerformanceReport
. The following code block demonstrates how to include these views.
You can include currency based metrics in PerformanceReport
. The following code block demonstrates how to include these views:
Copy rfs = sig.RollingFutureStrategy(
currency='USD',
start_date=dtm.date(2020,1,1),
contract_code='ES',
contract_sector='INDEX',
rolling_rule='front',
fixed_contracts=1
)
rfs.build()
dates = pd.date_range('2021-01-04', '2021-12-01', freq='1W-MON')
sig.PerformanceReport(rfs, views=[sig.View.SUMMARY_SINGLE_CCY, sig.View.SUMMARY_ROLLING_CCY_SERIES], dates=dates).report()
Copy rfs = sig . RollingFutureStrategy (
currency = 'USD' ,
start_date = dtm. date ( 2020 , 1 , 1 ),
contract_code = 'ES' ,
contract_sector = 'INDEX' ,
rolling_rule = 'front' ,
fixed_contracts = 1
)
rfs . build ()
dates = pd . date_range ( '2021-01-04' , '2021-12-01' , freq = '1W-MON' )
sig.PerformanceReport(rfs, views=[sig.View.SUMMARY_SINGLE_CCY, sig.View.SUMMARY_ROLLING_CCY_SERIES], dates=dates).report()
The aum
argument allows you to pass a representative AUM figure. This is useful in strategies where initial_cash=0
as it avoids dividing by zero. Following on from the above code block, the use of this new argument is demonstrated below.
The aum
argument allows you to pass a representative AUM figure. This is useful in strategies where initial_cash=0
as it avoids dividing by zero. The use of this argument is demonstrated in the following code block:
Copy sig.PerformanceReport(rfs, dates=dates, views=[sig.View.SUMMARY_SINGLE]).report()
first_contract = sig.obj.get(rfs.rolling_table.rolled_in_contract.iloc[0])
first_contract_value = first_contract.history().asof(rfs.start_date.strftime("%Y-%m-%d")) * first_contract.contract_size
sig.PerformanceReport(
rfs,
dates=dates,
views=[sig.View.SUMMARY_SINGLE],
aum=first_contract_value
).report()
Copy sig . PerformanceReport (rfs, dates = dates, views = [sig.View.SUMMARY_SINGLE]). report ()
first_contract = sig . obj . get (rfs.rolling_table.rolled_in_contract.iloc[ 0 ])
first_contract_value = first_contract.history().asof(rfs.start_date.strftime("%Y-%m-%d")) * first_contract.contract_size
sig . PerformanceReport (
rfs,
dates = dates,
views = [sig.View.SUMMARY_SINGLE],
aum = first_contract_value
). report ()
In addition, you can generate a PerformanceReport
with compounded metrics by passing compound_metrics=True
. The below code block demonstrates this feature by building on the examples provided above.
You can also generate a PerformanceReport
with compounded metrics by passing compound_metrics=True
. The following code block demonstrates this feature by building on the examples provided above:\
Copy sig.PerformanceReport(
rfs,
dates=dates[-5:],
views=[sig.View.SUMMARY_ROLLING_SERIES],
aum=first_contract_value,
compound_metrics=True
).report()
Copy sig . PerformanceReport (
rfs,
dates = dates[ - 5 :],
views = [sig.View.SUMMARY_ROLLING_SERIES],
aum = first_contract_value,
compound_metrics = True
). report ()