Notebook
In [1]:
bt = get_backtest('586600541456ae6293a7dfd7') #
100% Time: 0:00:19|###########################################################|
In [3]:
import matplotlib.pyplot as plt
import matplotlib
import datetime
from datetime import datetime
import pytz
from pytz import timezone
import pyfolio as pf
from __future__ import division
import pandas as pd
import seaborn as sns
import scipy as sp
import numpy as np
from math import copysign
from collections import OrderedDict, defaultdict, deque
from quantopian.pipeline import Pipeline
from quantopian.pipeline.data.builtin import USEquityPricing
from quantopian.research import run_pipeline
from quantopian.pipeline.filters.morningstar import Q500US, Q1500US
from quantopian.pipeline.data import morningstar as mstar
from quantopian.pipeline.classifiers.morningstar import Sector
import scipy.stats as stats


returns = bt.daily_performance.returns



def cum_returns(df, withStartingValue=None):
    if withStartingValue is None:
        return np.exp(np.log(1 + df).cumsum()) - 1
    else:
        return np.exp(np.log(1 + df).cumsum()) * withStartingValue

def aggregate_returns(df_daily_rets, convert_to):
    cumulate_returns = lambda x: cum_returns(x)[-1]
    
    
    if convert_to == 'daily':
        return df_daily_rets
    elif convert_to == 'weekly':
        return df_daily_rets.groupby([lambda x: x.year, lambda x: x.month, lambda x: x.isocalendar()[1]]).apply(cumulate_returns)
    elif convert_to == 'monthly':
        return df_daily_rets.groupby([lambda x: x.year, lambda x: x.month]).apply(cumulate_returns)
    elif convert_to == 'yearly':
        return df_daily_rets.groupby([lambda x: x.year]).apply(cumulate_returns)
    else:
        ValueError('convert_to must be daily, weekly, monthly or yearly')
    
def plot_calendar_returns_info_graphic(daily_rets_ts, x_dim=15, y_dim=6): 
    
    month_names = {
        1: 'Jan',
        2: 'Feb',
        3: 'Mar',
        4: 'Apr',
        5: 'May',
        6: 'Jun',
        7: 'Jul',
        8: 'Aug',
        9: 'Sept',
        10: 'Oct',
        11: 'Nov',
        12: 'Dec'
    }    
    
    ann_ret_df = pd.DataFrame( aggregate_returns(daily_rets_ts, 'yearly') )

    monthly_ret_table = aggregate_returns(daily_rets_ts, 'monthly')
    monthly_ret_table = monthly_ret_table.unstack()
    monthly_ret_table = np.round(monthly_ret_table, 3)
    monthly_ret_table.columns = monthly_ret_table.columns.map(lambda col: month_names[col])
    
    annual_ret_table = aggregate_returns(daily_rets_ts, 'yearly')
    annual_ret_table = np.round(annual_ret_table, 4) 
    
    vmin = np.round(min(monthly_ret_table.min(axis = 1)) - 0.02, 2)
    vmax = np.round(max(monthly_ret_table.max(axis = 1)) + 0.02, 2) 
    monthly_ret_table = monthly_ret_table.assign(Annual=annual_ret_table)
       

    fig,ax1 = plt.subplots(figsize=(15,15))
    sns.heatmap(monthly_ret_table.fillna(0)*100.0, annot=True, annot_kws={"size": 13}, alpha=1.0, center=3.0, cbar=True, cmap=matplotlib.cm.PiYG,
                linewidths=2.75, xticklabels =True, cbar_kws={"orientation": "horizontal"}, robust=True, vmin=vmin*100, 
                vmax = vmax*100, fmt='g' )

   
    for text in ax1.get_xticklabels():
        text.set_size(14)
        text.set_weight('bold')
        if str(text.get_unitless_position()[:-1]) == '(12.5,)':
            text.set_size(22)
            text.set_weight('bold')
            text.set_style('italic')        
    for text in ax1.get_yticklabels():
        text.set_size(14)
        text.set_weight('bold')
        
    for text in ax1.texts:
        text.set_size(14)
        if str(text.get_unitless_position()[:-1]) == '(12.5,)':
            text.set_size(22)
            text.set_weight('bold')

    ax1.axvline(x=12, color='#4D032D',linewidth=5)
    ax1.axvspan(12, 13, facecolor='0.05', alpha=0.4)
    
    ax1.set_ylabel("Year")
    ax1.set_xlabel("Monthly Returns (%)")
    ax1.set_title("Monthly Returns (%), " + \
            "\nFrom: " + str(bt.start_date.date()) + " to " + str(bt.end_date.date())); 
    plt.show()
    
pd.options.display.float_format = '{0:.3f}%'.format 
ytu = bt.cumulative_performance.ending_portfolio_value
ytu = pd.DataFrame(ytu)
ytu = ytu.groupby([ytu.index.year]).last()
ytu["opening"] = ytu.ending_portfolio_value.shift(1)
ytu = ytu.fillna(bt.capital_base)
ytu["annual_return"] = (ytu.ending_portfolio_value / ytu.opening)-1
ytu["total_return"] = 0
for i in range(len(ytu.index)):
    ytu.total_return[0:1] = ytu.annual_return[0:1]
    ytu["total_return"] =  ((1 + ytu.total_return.shift(1)) * (1 + ytu.annual_return)) - 1

ytu.total_return = ytu.total_return.fillna(ytu.annual_return)
ytu["CasdAGR"]=range(len(ytu.index))
ytu["CAGR"] = ((1 + ytu.total_return) ** (1 / (ytu.CasdAGR+1))) - 1
ytu.pop('CasdAGR')
ytu.pop('ending_portfolio_value')
ytu.pop('opening')
ytu.pop('total_return')
ytu.annual_return = ytu.annual_return * 100
ytu.CAGR = ytu.CAGR * 100
ytu = ytu.sort_index(ascending=False)
print ytu
print "note: CAGR assumes full year trading"
ytu.plot(x=None, y=None, kind='barh', ax=None, subplots=False, sharex=False, sharey=False, layout=None, figsize=(15,10), 
           use_index=True, title='Annual Return % & CAGR % at the end of each of Year', grid=None, legend=True, style=None, logx=False, logy=False, loglog=False, 
           xticks=None, yticks=None, xlim=None, ylim=None, rot=None, fontsize=12, colormap=None, table=False, 
           yerr=None, xerr=None, secondary_y=False, sort_columns=False)
plt.axvline(float(ytu.CAGR.mean()), color='g', linestyle='dashed', linewidth=2, label='CAGR Mean')
plt.axvline(float(ytu.annual_return.mean()), color='b', linestyle='dashed', linewidth=2, label='Annual Return Mean')
plt.legend()

matplotlib.pyplot.figure()
plot_calendar_returns_info_graphic(returns)  
matplotlib.pyplot.figure()
pf.plot_monthly_returns_dist(returns)
matplotlib.pyplot.figure()
pf.timeseries.cum_returns(returns).plot()
plt.ylabel('Cumulative Returns')
matplotlib.pyplot.figure()

ytu = pf.timeseries.rolling_sharpe(returns, rolling_sharpe_window=252)
ytu = pd.DataFrame(ytu)
ytu = ytu.groupby([ytu.index.year]).last()
ytu.plot(x=None, y=None, kind='barh', ax=None, subplots=False, sharex=False, sharey=False, layout=None, figsize=(15,10), 
           use_index=True, title='ROLLING 12 MONTH SHARPE AT YEAR END', grid=None, legend=False, style=None, logx=False, logy=False, loglog=False, 
           xticks=None, yticks=None, xlim=None, ylim=None, rot=None, fontsize=12, colormap=None, table=False, 
           yerr=None, xerr=None, secondary_y=False, sort_columns=False)
plt.axvline(float(ytu.mean()), color='m', linestyle='dashed', linewidth=2, label='Mean')
      annual_return    CAGR
2016        18.173% 15.309%
2015         9.655% 15.091%
2014         1.330% 15.556%
2013        19.009% 16.945%
2012        10.964% 16.740%
2011         9.795% 17.400%
2010         2.832% 18.387%
2009        38.180% 20.794%
2008         4.785% 18.117%
2007         2.356% 20.980%
2006        35.509% 26.143%
2005        12.556% 23.168%
2004        29.661% 28.843%
2003        28.030% 28.030%
note: CAGR assumes full year trading
/build/src/ipython/IPython/kernel/__main__.py:121: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-view-versus-copy
<matplotlib.figure.Figure at 0x7f67f181ae90>
Out[3]:
<matplotlib.lines.Line2D at 0x7f67f0ed58d0>
<matplotlib.figure.Figure at 0x7f67f20d6dd0>

================================================================

Top 15 most important features for predicting Sharpe ratio OOS as determined by a random forest regressor.

Source: Wiecki, Thomas and Campbell, Andrew and Lent, Justin and Stauth, Jessica, All that Glitters Is Not Gold: Comparing Backtest and Out-of-Sample Performance on a Large Cohort of Trading Algorithms (March 9, 2016). Available at SSRN: https://ssrn.com/abstract=2745220 or http://dx.doi.org/10.2139/ssrn.2745220

In [193]:
print "------------------------------------------------------------------"
print("\n"'ALL DATA BELOW IS OVER FULL BACKTEST OF {:2,.0f} days'.format(((bt.end_date.date() - bt.start_date.date()).days)))
print "------------------------------------------------------------------"
print("\n"'\x1b[1;31m'+'TAIL RATIO'+'\x1b[0m'"\n")
print "Determines the ratio between the right (95%) and left tail (5%). For example, a ratio of 0.25 means that losses are four times as bad as profits."
print("\n"'Backtest tail ratio = {:4,.3f}'.format((pf.capacity.empyrical.tail_ratio(returns))))
print "------------------------------------------------------------------"

print("\n"'\x1b[1;31m'+'KURTOSIS'+'\x1b[0m'"\n")
print('Characterizes the relative ‘peakedness’ or flatness of an investment’s return distribution compared with the normal distribution. The higher the kurtosis, the more peaked the return distribution is; the lower the kurtosis, the more rounded the return distribution is. A normal distribution has a kurtosis of 3. Higher kurtosis indicates a return distribution with a more acute peak around the mean (higher probability than a normal distribution of more returns clustered around the mean) and a greater chance of extremely large deviations from the expected return (fatter tails, more big surprises). Investors view a greater percentage of extremely large deviations from the expected return as an increase in risk. Lower kurtosis has a smaller peak (lower probability of returns around the mean) and a lower probability than a normal distribution of extreme returns.')
print('source: Greenwich Alternative Investments: http://www.greenwichai.com/index.php/hf-essentials/measure-of-risk - retrieved Dec 27, 2016')
print("\n"'Kurtosis over entire test period = {:4,.3f}'.format((sp.stats.stats.kurtosis(returns))))  
xyz = sp.stats.stats.kurtosis(returns)-3
print("\n"'Excess kurtosis over entire test period = {:4,.3f}'.format(xyz))  
print "------------------------------------------------------------------"
print("\n"'\x1b[1;31m'+'SKEWNESS'+'\x1b[0m'"\n")    
print("\n"'A distribution with no tail to the right or to the left is one that is not skewed in any direction. This is the same as a normal distribution i.e. a distribution which has zero skewness. If there is a large frequency of occurrence of negative returns compared to positive returns then the distribution displays a fat left tail or negative skewness. In case the frequency of positive returns exceeds that of negative returns then the distribution displays a fat right tail or positive skewness.')
print("\n"'Skewness over entire test period = {:4,.3f}'.format((sp.stats.stats.skew(returns))))     
print "------------------------------------------------------------------"
print("\n"'\x1b[1;31m'+'SHARPE RATIO'+'\x1b[0m'"\n")
print("\n"'Sharpe ratio last year = {:4,.3f}'"\n".format((float(ytu.returns[-1:]))))
print "------------------------------------------------------------------"
print("\n"'\x1b[1;31m'+'ROLLING SHARPE RATIO'+'\x1b[0m'"\n")
print("\n"'Rolling Monthly Sharpe Ratio over entire test period has a mean of {:4,.4f} and standard deviation of {:4,.4f}'
      .format((pf.timeseries.rolling_sharpe(returns, rolling_sharpe_window=(pf.capacity.empyrical.stats.APPROX_BDAYS_PER_MONTH)).mean()),
              pf.timeseries.rolling_sharpe(returns, rolling_sharpe_window=(pf.capacity.empyrical.stats.APPROX_BDAYS_PER_MONTH)).std()))    
print("\n"'Rolling 6 Month Sharpe Ratio over entire test period has a mean of {:4,.4f} and standard deviation of {:4,.4f}'
      .format((pf.timeseries.rolling_sharpe(returns, rolling_sharpe_window=(pf.capacity.empyrical.stats.APPROX_BDAYS_PER_MONTH * 6)).mean()),
              pf.timeseries.rolling_sharpe(returns, rolling_sharpe_window=(pf.capacity.empyrical.stats.APPROX_BDAYS_PER_MONTH * 6)).std()))   
print("\n"'Rolling 12 Month Sharpe Ratio over entire test period has a mean of {:4,.4f} and standard deviation of {:4,.4f}'
      .format((pf.timeseries.rolling_sharpe(returns, rolling_sharpe_window=(pf.capacity.empyrical.stats.APPROX_BDAYS_PER_MONTH * 12)).mean()),
              pf.timeseries.rolling_sharpe(returns, rolling_sharpe_window=(pf.capacity.empyrical.stats.APPROX_BDAYS_PER_MONTH * 12)).std()))   
print "------------------------------------------------------------------"
spy = get_pricing(symbols = bt.benchmark_security, start_date=bt.start_date.date(), end_date=bt.end_date.date(), symbol_reference_date=None, 
                 frequency='daily', fields={"price"}, handle_missing='ignore')
spy = pd.Series(spy.price.pct_change())
alpla_beta= pf.capacity.empyrical.alpha_beta(returns, spy, risk_free=0.0, period='daily', annualization=None)

print("\n"'\x1b[1;31m'+'ALPHA + BETA'+'\x1b[0m'"\n")
print("\n""\n"'Annualized Alpha is annualized returns in excess of returns resulting from correlations with the benchmark. Beta is the correlation between daily returns of algo and daily returns of the benchmark')  
print("\n"'Annualized Alpha over entire test period = {:10.5}'.format((alpla_beta[0])))   
print('Beta over entire test period = {:10.5}'.format((alpla_beta[1])))   
print "------------------------------------------------------------------"
print("\n"'\x1b[1;31m'+'COMMON SENSE RATIO'+'\x1b[0m'"\n")
print"\n""Above 1: make money, below 1: lose money"
print"\n""Common Sense Ratio = Tail ratio * Gain to Pain Ratio"
print"Common Sense Ratio = [percentile(returns, 95%) * Sum(profits)] / [percentile(returns, 5%) * Sum(losses)]"
print"Trend following strategies have low win rate. Aggregate losses kill profitability. Therefore risk is in the aggregate which is measured by the profit ratio or Gain to Pain Ratio (Jack Schwager favourite measure btw). No surprise since GPR is the ratio version of gain expectancy or trading edge. Mean reverting strategies fail on knockouts. LTCM best case. A few blow-ups and game over. So the risk is in the tail. Therefore, use tail ratio. Now, a bit of arithmetical magic clled transitivity, or multiply GPR by tail and voila CSR. It recaptures risk on both sides."
print"source:http://alphasecurecapital.com/regardless-of-the-asset-class-there-are-only-two-types-of-strategies/"
print("\n"'Common Sense ratio over entire test period = {:4,.3f}'.format((pf.capacity.empyrical.tail_ratio(returns) * (1 + pf.capacity.empyrical.annual_return(returns)))))
print "------------------------------------------------------------------"
print("\n"'\x1b[1;31m'+'VALUE AT RISK(VaR)'+'\x1b[0m'"\n")
print "\n""\n""a statistical technique used to measure and quantify the possibility of losing money within an investment portfolio over a specific time frame to determine the extent and occurrence ratio of potential losses in their institutional portfolios. VaR quantifies market risk while it is being taken. It measures the odds of losing money but does not indicate certainty. VAR summarizes the predicted maximum loss (or worst loss) over a target horizon within a given confidence interval."
print("\n"'\x1b[1;34m'+'HISTORICAL METHOD VAR'+'\x1b[0m')
print("\n"'\x1b[1;31m'+'DAILY VAR - HISTORICAL'+'\x1b[0m'"\n")
yui = pd.DataFrame((returns*100).quantile([.01, .05, 0.1, 0.5]))
yui["percent"] = yui.index
yui.index = range(len(yui))
yui2 = pd.DataFrame({'percent':[stats.percentileofscore(returns*100,0)/100],
                     'returns':['0.0']})
yui = yui.append(yui2, ignore_index=True)
print('With 99% confidence, we expect that our worst daily loss will not exceed {:4,.3f}%, or in other words we can expect'
      ' that with a probability of 1% the daily loss of the portfolio will decrease by more than {:4,.3f}%.'
      .format(yui.returns[0],yui.returns[0]))
print("\n"'With 95% confidence, we expect that our worst daily loss will not exceed {:4,.3f}%, or in other words we can expect'
      ' that with a probability of 5% the daily loss of the portfolio will decrease by more than {:4,.3f}%.'
      .format(yui.returns[1],yui.returns[1]))
print("\n"'With 90% confidence, we expect that our worst daily loss will not exceed {:4,.3f}%, or in other words we can expect'
      ' that with a probability of 10% the daily loss of the portfolio will decrease by more than {:4,.3f}%.'
      .format(yui.returns[2],yui.returns[2]))
print("\n"'With 50% confidence, we expect that our worst daily loss will not exceed {:4,.3f}%, or in other words we can expect'
      ' that with a probability of 50% the daily loss of the portfolio will decrease by more than {:4,.3f}%.'
      .format(yui.returns[3],yui.returns[3]))
print("\n"'With {:4,.1f}% confidence, we expect that our worst daily loss will not exceed 0%, or in other words we can expect'
      ' that with a probability of {:4,.1f}% the daily loss of the portfolio will decrease by more than 0%.'
      .format((1-yui.percent[4])*100,yui.percent[4]*100))

print("\n"'\x1b[1;31m'+'MONTHLY VAR - HISTORICAL'+'\x1b[0m'"\n")
yui = pd.DataFrame((pf.capacity.empyrical.aggregate_returns(returns, convert_to='monthly')*100).quantile([.01, .05, 0.1, 0.5]))
yui["percent"] = yui.index
yui.index = range(len(yui))
yui2 = pd.DataFrame({'percent':[stats.percentileofscore(pf.capacity.empyrical.aggregate_returns(returns, convert_to='monthly')*100,0)/100],
                     'returns':['0.0']})
yui = yui.append(yui2, ignore_index=True)
print('With 99% confidence, we expect that our worst monthly loss will not exceed {:4,.3f}%, or in other words we can expect'
      ' that with a probability of 1% the monthly loss of the portfolio will decrease by more than {:4,.3f}%.'
      .format(yui.returns[0],yui.returns[0]))
print("\n"'With 95% confidence, we expect that our worst monthly loss will not exceed {:4,.3f}%, or in other words we can expect'
      ' that with a probability of 5% the monthly loss of the portfolio will decrease by more than {:4,.3f}%.'
      .format(yui.returns[1],yui.returns[1]))
print("\n"'With 90% confidence, we expect that our worst monthly loss will not exceed {:4,.3f}%, or in other words we can expect'
      ' that with a probability of 10% the monthly loss of the portfolio will decrease by more than {:4,.3f}%.'
      .format(yui.returns[2],yui.returns[2]))
print("\n"'With 50% confidence, we expect that our worst monthly loss will not exceed {:4,.3f}%, or in other words we can expect'
      ' that with a probability of 50% the monthly loss of the portfolio will decrease by more than {:4,.3f}%.'
      .format(yui.returns[3],yui.returns[3]))
print("\n"'With {:4,.1f}% confidence, we expect that our worst monthly loss will not exceed 0%, or in other words we can expect'
      ' that with a probability of {:4,.1f}% the monthly loss of the portfolio will decrease by more than 0%.'
      .format((1-yui.percent[4])*100,yui.percent[4]*100))

print("\n"'\x1b[1;31m'+'ANNUAL VAR - HISTORICAL'+'\x1b[0m'"\n")
yui = pd.DataFrame((pf.capacity.empyrical.aggregate_returns(returns, convert_to='yearly')*100).quantile([.01, .05, 0.1, 0.5]))
yui["percent"] = yui.index
yui.index = range(len(yui))
yui2 = pd.DataFrame({'percent':[stats.percentileofscore(pf.capacity.empyrical.aggregate_returns(returns, convert_to='yearly')*100,0)/100],
                     'returns':['0.0']})
yui = yui.append(yui2, ignore_index=True)
print('With 99% confidence, we expect that our worst annual loss will not exceed {:4,.3f}%, or in other words we can expect'
      ' that with a probability of 1% the annual loss of the portfolio will decrease by more than {:4,.3f}%.'
      .format(yui.returns[0],yui.returns[0]))
print("\n"'With 95% confidence, we expect that our worst annual loss will not exceed {:4,.3f}%, or in other words we can expect'
      ' that with a probability of 5% the annual loss of the portfolio will decrease by more than {:4,.3f}%.'
      .format(yui.returns[1],yui.returns[1]))
print("\n"'With 90% confidence, we expect that our worst annual loss will not exceed {:4,.3f}%, or in other words we can expect'
      ' that with a probability of 10% the annual loss of the portfolio will decrease by more than {:4,.3f}%.'
      .format(yui.returns[2],yui.returns[2]))
print("\n"'With 50% confidence, we expect that our worst annual loss will not exceed {:4,.3f}%, or in other words we can expect'
      ' that with a probability of 50% the annual loss of the portfolio will decrease by more than {:4,.3f}%.'
      .format(yui.returns[3],yui.returns[3]))
print("\n"'With {:4,.1f}% confidence, we expect that our worst annual loss will not exceed 0%, or in other words we can expect'
      ' that with a probability of {:4,.1f}% the annual loss of the portfolio will decrease by more than 0%.'
      .format((1-yui.percent[4])*100,yui.percent[4]*100))
------------------------------------------------------------------

ALL DATA BELOW IS OVER FULL BACKTEST OF 5,104 days
------------------------------------------------------------------

TAIL RATIO

Determines the ratio between the right (95%) and left tail (5%). For example, a ratio of 0.25 means that losses are four times as bad as profits.

Backtest tail ratio = 1.162
------------------------------------------------------------------

KURTOSIS

Characterizes the relative ‘peakedness’ or flatness of an investment’s return distribution compared with the normal distribution. The higher the kurtosis, the more peaked the return distribution is; the lower the kurtosis, the more rounded the return distribution is. A normal distribution has a kurtosis of 3. Higher kurtosis indicates a return distribution with a more acute peak around the mean (higher probability than a normal distribution of more returns clustered around the mean) and a greater chance of extremely large deviations from the expected return (fatter tails, more big surprises). Investors view a greater percentage of extremely large deviations from the expected return as an increase in risk. Lower kurtosis has a smaller peak (lower probability of returns around the mean) and a lower probability than a normal distribution of extreme returns.
source: Greenwich Alternative Investments: http://www.greenwichai.com/index.php/hf-essentials/measure-of-risk - retrieved Dec 27, 2016

Kurtosis over entire test period = 4.775

Excess kurtosis over entire test period = 1.775
------------------------------------------------------------------

SKEWNESS


A distribution with no tail to the right or to the left is one that is not skewed in any direction. This is the same as a normal distribution i.e. a distribution which has zero skewness. If there is a large frequency of occurrence of negative returns compared to positive returns then the distribution displays a fat left tail or negative skewness. In case the frequency of positive returns exceeds that of negative returns then the distribution displays a fat right tail or positive skewness.

Skewness over entire test period = 0.053
------------------------------------------------------------------

SHARPE RATIO


Sharpe ratio last year = 1.547

------------------------------------------------------------------

ROLLING SHARPE RATIO


Rolling Monthly Sharpe Ratio over entire test period has a mean of 1.8645 and standard deviation of 3.8545

Rolling 6 Month Sharpe Ratio over entire test period has a mean of 1.7176 and standard deviation of 1.6619

Rolling 12 Month Sharpe Ratio over entire test period has a mean of 1.6494 and standard deviation of 1.2094
------------------------------------------------------------------

ALPHA + BETA



Annualized Alpha is annualized returns in excess of returns resulting from correlations with the benchmark. Beta is the correlation between daily returns of algo and daily returns of the benchmark

Annualized Alpha over entire test period =    0.12576
Beta over entire test period =    0.25789
------------------------------------------------------------------

COMMON SENSE RATIO


Above 1: make money, below 1: lose money

Common Sense Ratio = Tail ratio * Gain to Pain Ratio
Common Sense Ratio = [percentile(returns, 95%) * Sum(profits)] / [percentile(returns, 5%) * Sum(losses)]
Trend following strategies have low win rate. Aggregate losses kill profitability. Therefore risk is in the aggregate which is measured by the profit ratio or Gain to Pain Ratio (Jack Schwager favourite measure btw). No surprise since GPR is the ratio version of gain expectancy or trading edge. Mean reverting strategies fail on knockouts. LTCM best case. A few blow-ups and game over. So the risk is in the tail. Therefore, use tail ratio. Now, a bit of arithmetical magic clled transitivity, or multiply GPR by tail and voila CSR. It recaptures risk on both sides.
source:http://alphasecurecapital.com/regardless-of-the-asset-class-there-are-only-two-types-of-strategies/

Common Sense ratio over entire test period = 1.340
------------------------------------------------------------------

VALUE AT RISK(VaR)



a statistical technique used to measure and quantify the possibility of losing money within an investment portfolio over a specific time frame to determine the extent and occurrence ratio of potential losses in their institutional portfolios. VaR quantifies market risk while it is being taken. It measures the odds of losing money but does not indicate certainty. VAR summarizes the predicted maximum loss (or worst loss) over a target horizon within a given confidence interval.

HISTORICAL METHOD VAR

DAILY VAR - HISTORICAL

With 99% confidence, we expect that our worst daily loss will not exceed -1.465%, or in other words we can expect that with a probability of 1% the daily loss of the portfolio will decrease by more than -1.465%.

With 95% confidence, we expect that our worst daily loss will not exceed -0.848%, or in other words we can expect that with a probability of 5% the daily loss of the portfolio will decrease by more than -0.848%.

With 90% confidence, we expect that our worst daily loss will not exceed -0.605%, or in other words we can expect that with a probability of 10% the daily loss of the portfolio will decrease by more than -0.605%.

With 50% confidence, we expect that our worst daily loss will not exceed 0.050%, or in other words we can expect that with a probability of 50% the daily loss of the portfolio will decrease by more than 0.050%.

With 53.9% confidence, we expect that our worst daily loss will not exceed 0%, or in other words we can expect that with a probability of 46.1% the daily loss of the portfolio will decrease by more than 0%.

MONTHLY VAR - HISTORICAL

With 99% confidence, we expect that our worst monthly loss will not exceed -5.321%, or in other words we can expect that with a probability of 1% the monthly loss of the portfolio will decrease by more than -5.321%.

With 95% confidence, we expect that our worst monthly loss will not exceed -3.556%, or in other words we can expect that with a probability of 5% the monthly loss of the portfolio will decrease by more than -3.556%.

With 90% confidence, we expect that our worst monthly loss will not exceed -2.748%, or in other words we can expect that with a probability of 10% the monthly loss of the portfolio will decrease by more than -2.748%.

With 50% confidence, we expect that our worst monthly loss will not exceed 1.237%, or in other words we can expect that with a probability of 50% the monthly loss of the portfolio will decrease by more than 1.237%.

With 71.4% confidence, we expect that our worst monthly loss will not exceed 0%, or in other words we can expect that with a probability of 28.6% the monthly loss of the portfolio will decrease by more than 0%.

ANNUAL VAR - HISTORICAL

With 99% confidence, we expect that our worst annual loss will not exceed 1.463%, or in other words we can expect that with a probability of 1% the annual loss of the portfolio will decrease by more than 1.463%.

With 95% confidence, we expect that our worst annual loss will not exceed 1.997%, or in other words we can expect that with a probability of 5% the annual loss of the portfolio will decrease by more than 1.997%.

With 90% confidence, we expect that our worst annual loss will not exceed 2.499%, or in other words we can expect that with a probability of 10% the annual loss of the portfolio will decrease by more than 2.499%.

With 50% confidence, we expect that our worst annual loss will not exceed 11.760%, or in other words we can expect that with a probability of 50% the annual loss of the portfolio will decrease by more than 11.760%.

With 100.0% confidence, we expect that our worst annual loss will not exceed 0%, or in other words we can expect that with a probability of  0.0% the annual loss of the portfolio will decrease by more than 0%.
In [130]:
print("\n"'With 99% confidence, we expect that our worst daily loss will not exceed {:4,.3f}%, or in other words we can expect'
      ' that with a probability of 1% the daily loss of the portfolio will decrease by more than {:4,.3f}%.'
      .format(yui.returns[0],yui.returns[0]))
print("\n"'With 95% confidence, we expect that our worst daily loss will not exceed {:4,.3f}%, or in other words we can expect'
      ' that with a probability of 5% the daily loss of the portfolio will decrease by more than {:4,.3f}%.'
      .format(yui.returns[1],yui.returns[1]))
print("\n"'With 90% confidence, we expect that our worst daily loss will not exceed {:4,.3f}%, or in other words we can expect'
      ' that with a probability of 10% the daily loss of the portfolio will decrease by more than {:4,.3f}%.'
      .format(yui.returns[2],yui.returns[2]))
print("\n"'With 50% confidence, we expect that our worst daily loss will not exceed {:4,.3f}%, or in other words we can expect'
      ' that with a probability of 50% the daily loss of the portfolio will decrease by more than {:4,.3f}%.'
      .format(yui.returns[3],yui.returns[3]))
print("\n"'With {:4,.1f}% confidence, we expect that our worst daily loss will not exceed 0%, or in other words we can expect'
      ' that with a probability of {:4,.1f}% the daily loss of the portfolio will decrease by more than 0%.'
      .format((1-yui.percent[4])*100,yui.percent[4]*100))
With 99% confidence, we expect that our worst daily loss will not exceed -5.321%, or in other words we can expect that with a probability of 1% the daily loss of the portfolio will decrease by more than -5.321%.

With 95% confidence, we expect that our worst daily loss will not exceed -3.556%, or in other words we can expect that with a probability of 5% the daily loss of the portfolio will decrease by more than -3.556%.

With 90% confidence, we expect that our worst daily loss will not exceed -2.748%, or in other words we can expect that with a probability of 10% the daily loss of the portfolio will decrease by more than -2.748%.

With 50% confidence, we expect that our worst daily loss will not exceed 1.237%, or in other words we can expect that with a probability of 50% the daily loss of the portfolio will decrease by more than 1.237%.

With 71.4% confidence, we expect that our worst daily loss will not exceed 0%, or in other words we can expect that with a probability of 28.6% the daily loss of the portfolio will decrease by more than 0%.
In [ ]:
 
In [18]:
def vwap(transaction):
    return (transaction.amount * transaction.price).sum() / transaction.amount.sum()

def _groupby_consecutive(txn, max_delta=pd.Timedelta('8h')):

    def vwap(transaction):
        if transaction.amount.sum() == 0:
            #warnings.warn('Zero transacted shares, setting vwap to nan.')
            print "Zero transacted shares, setting vwap to nan."
            return np.nan
        return (transaction.amount * transaction.price).sum() / \
            transaction.amount.sum()

    out = []
    for sym, t in txn.groupby('symbol'):
        t = t.sort_index()
        t.index.name = 'dt'
        t = t.reset_index()

        t['order_sign'] = t.amount > 0
        t['block_dir'] = (t.order_sign.shift(
            1) != t.order_sign).astype(int).cumsum()
        t['block_time'] = ((t.dt - t.dt.shift(1)) >
                           max_delta).astype(int).cumsum()
        grouped_price = (t.groupby(('block_dir',
                                   'block_time'))
                          .apply(vwap))
        grouped_price.name = 'price'
        grouped_rest = t.groupby(('block_dir', 'block_time')).agg({
            'amount': 'sum',
            'symbol': 'first',
            'dt': 'first'})

        grouped = grouped_rest.join(grouped_price)

        out.append(grouped)

    out = pd.concat(out)
    out = out.set_index('dt')
    return out

def extract_round_trips_stack(transactions,
                        starting_capital=None):

    transactions = _groupby_consecutive(transactions)
    roundtrips = []

    for sym, trans_sym in transactions.groupby('symbol'):
        trans_sym = trans_sym.sort_index()
        price_stack = deque()
        dt_stack = deque()
        trans_sym['signed_price'] = trans_sym.price * \
            np.sign(trans_sym.amount)
        trans_sym['abs_amount'] = trans_sym.amount.abs().astype(int)
        for dt, t in trans_sym.iterrows():
            if t.price < 0:
                #warnings.warn('Negative price detected, ignoring for'
                              #'round-trip.')
                print "Negative price detected, ignoring for round-trip."
                continue

            indiv_prices = [t.signed_price] * t.abs_amount
            if (len(price_stack) == 0) or \
               (copysign(1, price_stack[-1]) == copysign(1, t.amount)):
                price_stack.extend(indiv_prices)
                dt_stack.extend([dt] * len(indiv_prices))
            else:
                # Close round-trip
                pnl = 0
                invested = 0
                cur_open_dts = []

                for price in indiv_prices:
                    if len(price_stack) != 0 and \
                       (copysign(1, price_stack[-1]) != copysign(1, price)):
                        # Retrieve first dt, stock-price pair from
                        # stack
                        prev_price = price_stack.popleft()
                        prev_dt = dt_stack.popleft()

                        pnl += -(price + prev_price)
                        cur_open_dts.append(prev_dt)
                        invested += abs(prev_price)

                    else:
                        # Push additional stock-prices onto stack
                        price_stack.append(price)
                        dt_stack.append(dt)

                roundtrips.append({'pnl': pnl,
                                   'open_dt': cur_open_dts[0],
                                   'close_dt': dt,
                                   'long': price < 0,
                                   'rt_returns': pnl / invested,
                                   'symbol': sym,
                                   'invested': invested,
                                   })

    roundtrips = pd.DataFrame(roundtrips)

    roundtrips['duration'] = roundtrips['close_dt'] - roundtrips['open_dt']
    
    if starting_capital is not None:
        roundtrips['returns'] = roundtrips['pnl'] / starting_capital    
    '''
    if starting_capital is not None:
        # Need to normalize so that we can join
        pv = pd.DataFrame(starting_capital,
                          columns=['starting_capital'])\
            .assign(date=starting_capital.index)
                

        roundtrips['date'] = roundtrips.close_dt.apply(lambda x:
                                                       x.replace(hour=0,
                                                                 minute=0,
                                                                 second=0))

        tmp = roundtrips.join(pv, on='date', lsuffix='_')

        roundtrips['returns'] = tmp.pnl / tmp.starting_capital
        roundtrips = roundtrips.drop('date', axis='columns')
    '''
    return roundtrips

Application to algorithm

In [19]:
pnl_stats = OrderedDict([('Total profit', lambda x: np.round(x.sum(),0)),
                         ('Gross profit', lambda x: x[x>0].sum()),
                         ('Gross loss', lambda x: x[x<0].sum()),
                         ('Profit factor', lambda x: x[x>0].sum() / x[x<0].abs().sum() if x[x<0].abs().sum() != 0 else np.nan),
                         ('Avg. trade net profit', 'mean'),
                         ('Avg. winning trade', lambda x: x[x>0].mean()),
                         ('Avg. losing trade', lambda x: x[x<0].mean()),
                         ('Ratio Avg. Win:Avg. Loss', lambda x: x[x>0].mean() / x[x<0].abs().mean() if x[x<0].abs().mean() != 0 else np.nan),
                         ('Largest winning trade', 'max'),
                         ('Largest losing trade', 'min')])

summary_stats = OrderedDict([('Total number of trades', 'count'),
                          ('Percent profitable', lambda x: len(x[x>0]) / float(len(x))),
                          ('Winning trades', lambda x: len(x[x>0])),
                          ('Losing trades', lambda x: len(x[x<0])),
                          ('Even trades', lambda x: len(x[x==0])),])

return_stats = OrderedDict([('Avg returns all trades', lambda x: x.mean()),
                            ('Avg returns winning', lambda x: x[x>0].mean()),
                            ('Avg returns losing', lambda x: x[x<0].mean()),
                            ('Median returns all trades', lambda x: x.median()),
                            ('Median returns winning', lambda x: x[x>0].median()),
                            ('Median returns losing', lambda x: x[x<0].median()),
                            ('Profit factor', lambda x: x[x>0].mean() / x[x<0].abs().mean() if x[x<0].abs().mean() != 0 else np.nan),
                            ('Percent profitable', lambda x: len(x[x>0]) / float(len(x))),
                            ('Ratio Avg. Win:Avg. Loss', lambda x: x[x>0].mean() / x[x<0].abs().mean() if x[x<0].abs().mean() != 0 else np.nan),
                            ('Largest winning trade', 'max'),
                            ('Largest losing trade', 'min')])

duration_stats = OrderedDict([('Avg duration', lambda x: x.mean()),
                              ('Median duration', lambda x: x.median()),
                              ('Avg # trades per day', lambda x: (float(len(x)) / (x.max() - x.min()).days) if (x.max() - x.min()).days != 0 else np.nan),
                              ('Avg # trades per month', lambda x: (float(len(x)) / (((x.max() - x.min()).days) / pf.APPROX_BDAYS_PER_MONTH)) if (x.max() - x.min()).days != 0 else np.nan)
                             ])

First Map out Sectors to Backtest Results

In [20]:
# Add symbols
transactions = bt.transactions
transactions['symbol'] = map(lambda x: x.symbol, symbols(transactions.sid))
trades = extract_round_trips_stack(transactions, starting_capital=bt.daily_performance.starting_cash.iloc[0])
def get_SID(row):
    temp_ticker = row['symbol']
    start_date = row['open_dt']

    row['SID'] = symbols(temp_ticker)
    return row
            
trades = trades.apply(get_SID, axis=1)

sector = mstar.asset_classification.morningstar_sector_code.latest

pipe = Pipeline(
    columns={'Close': USEquityPricing.close.latest,
            'Sector Code': sector},
    screen=Q1500US()
)

#run_pipeline(pipe, bt.end_date.date(), bt.end_date.date()).head(10)  # head(10) shows just the first 10 rows
In [21]:
SECTOR_CODE_NAMES = {
    Sector.BASIC_MATERIALS: 'Basic Materials',
    Sector.CONSUMER_CYCLICAL: 'Consumer Cyclical',
    Sector.FINANCIAL_SERVICES: 'Financial Services',
    Sector.REAL_ESTATE: 'Real Estate',
    Sector.CONSUMER_DEFENSIVE: 'Consumer Defensive',
    Sector.HEALTHCARE: 'Healthcare',
    Sector.UTILITIES: 'Utilities',
    Sector.COMMUNICATION_SERVICES: 'Communication Services',
    Sector.ENERGY: 'Energy',
    Sector.INDUSTRIALS: 'Industrials',
    Sector.TECHNOLOGY: 'Technology',
    -1: 'Unknown'
}

def get_q1500_sector_codes(day1, day2):
    pipe = Pipeline(columns={'Sector': Sector()})#, screen=Q1500US())
    # Drop the datetime level of the index, since we only have one day of data.
    return run_pipeline(pipe, day1, day2).reset_index(level=0, drop=True)
def calculate_sector_counts(sectors):
    counts = (sectors.groupby('Sector').size())

    # Replace numeric sector codes with human-friendly names.
    counts.index = counts.index.map(lambda code: SECTOR_CODE_NAMES[code])
    return counts
In [22]:
#INSTEAD OF RUNNING FULL PIPELINE, MAYBE CUSTOM FACTOR ONLY ON SID IN TRADES??
run_pipeline(pipe, bt.end_date.date(), bt.end_date.date()).head(10) 
Out[22]:
Close Sector Code
2016-12-23 00:00:00+00:00 Equity(24 [AAPL]) 116.2900 311
Equity(53 [ABMD]) 111.5100 206
Equity(62 [ABT]) 38.2900 206
Equity(67 [ADSK]) 74.5800 311
Equity(76 [TAP]) 96.8600 205
Equity(114 [ADBE]) 104.7300 311
Equity(122 [ADI]) 73.4500 311
Equity(128 [ADM]) 45.1800 205
Equity(161 [AEP]) 63.2500 207
Equity(166 [AES]) 11.7800 207
In [23]:
zz = get_q1500_sector_codes(bt.end_date.date(),bt.end_date.date())
zz["sector_name"] = zz.Sector.map(lambda code: SECTOR_CODE_NAMES[code])
trades['sector']=trades.SID.map(zz.sector_name)
trades['sector']=trades.sector.fillna('Unknown')

================================================================

Summary trade stats

In [24]:
pd.options.display.float_format = '{:20,.2f}'.format
stats = trades.assign(ones=1).groupby('ones')['pnl'].agg(summary_stats).T.rename_axis({1.0: 'All trades'}, axis='columns')
stats2 = trades.groupby('long')['pnl'].agg(summary_stats).T.rename_axis({False: 'Short trades', True: 'Long trades'}, axis='columns')
stats = stats.join(stats2)


print "+++SHOULD SHOW UNIQUE TRADES ALSO!!+++"
stats[['All trades', 'Long trades', 'Short trades']]
+++SHOULD SHOW UNIQUE TRADES ALSO!!+++
Out[24]:
All trades Long trades Short trades
Total number of trades 3,157.00 2,880.00 277.00
Percent profitable 0.62 0.65 0.39
Winning trades 1,969.00 1,861.00 108.00
Losing trades 1,188.00 1,019.00 169.00
Even trades 0.00 0.00 0.00
In [25]:
ts = trades.groupby('sector')['pnl'].agg(summary_stats).T.rename_axis({1.0: 'All trades'}, axis='columns')
ts
Out[25]:
sector Basic Materials Communication Services Consumer Cyclical Consumer Defensive Energy Financial Services Healthcare Industrials Real Estate Technology Unknown Utilities
Total number of trades 258.00 86.00 573.00 176.00 220.00 137.00 132.00 546.00 13.00 296.00 717.00 3.00
Percent profitable 0.74 0.57 0.65 0.69 0.63 0.63 0.73 0.64 0.46 0.60 0.52 1.00
Winning trades 192.00 49.00 375.00 122.00 139.00 86.00 96.00 350.00 6.00 179.00 372.00 3.00
Losing trades 66.00 37.00 198.00 54.00 81.00 51.00 36.00 196.00 7.00 117.00 345.00 0.00
Even trades 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
In [26]:
ts2 = trades.groupby(['sector','long'])['pnl'].agg(summary_stats).T.rename_axis({False: 'Short trades', True: 'Long trades'}, axis='columns')
ts2
Out[26]:
sector Basic Materials Communication Services Consumer Cyclical Consumer Defensive Energy Financial Services Healthcare Industrials Real Estate Technology Unknown Utilities
long Short trades Long trades Long trades Short trades Long trades Short trades Long trades Short trades Long trades Long trades Short trades Long trades Short trades Long trades Long trades Short trades Long trades Short trades Long trades Long trades
Total number of trades 5.00 253.00 86.00 19.00 554.00 10.00 166.00 2.00 218.00 137.00 3.00 129.00 11.00 535.00 13.00 2.00 294.00 225.00 492.00 3.00
Percent profitable 0.60 0.75 0.57 0.42 0.66 0.40 0.71 0.50 0.63 0.63 1.00 0.72 0.18 0.65 0.46 0.00 0.61 0.39 0.58 1.00
Winning trades 3.00 189.00 49.00 8.00 367.00 4.00 118.00 1.00 138.00 86.00 3.00 93.00 2.00 348.00 6.00 0.00 179.00 87.00 285.00 3.00
Losing trades 2.00 64.00 37.00 11.00 187.00 6.00 48.00 1.00 80.00 51.00 0.00 36.00 9.00 187.00 7.00 2.00 115.00 138.00 207.00 0.00
Even trades 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
In [27]:
tsx = trades[trades.sector == 'Financial Services']
tsxa = trades[trades.sector == 'Healthcare']
ts3a = tsxa.groupby(['sector','long'])['pnl'].agg(summary_stats).T.rename_axis({False: 'Short trades', True: 'Long trades'}, axis='columns')
ts3b = tsx.groupby(['sector','long'])['pnl'].agg(summary_stats).T.rename_axis({False: 'Short trades', True: 'Long trades'}, axis='columns')
ts3 = ts3a.join(ts3b)
ts3
Out[27]:
sector Healthcare Financial Services
long Short trades Long trades Long trades
Total number of trades 3.00 129.00 137.00
Percent profitable 1.00 0.72 0.63
Winning trades 3.00 93.00 86.00
Losing trades 0.00 36.00 51.00
Even trades 0.00 0.00 0.00

================================================================

PnL

In [28]:
pd.options.display.float_format = '{:20,.2f}'.format
stats = trades.assign(ones=1).groupby('ones')['pnl'].agg(pnl_stats).T.rename_axis({1.0: 'All trades'}, axis='columns')
stats2 = trades.groupby('long')['pnl'].agg(pnl_stats).T.rename_axis({False: 'Short trades', True: 'Long trades'}, axis='columns')
stats = stats.join(stats2)
stats[['All trades', 'Long trades', 'Short trades']]
Out[28]:
All trades Long trades Short trades
Total profit 639,770.00 755,744.00 -115,974.00
Gross profit 1,788,339.00 1,670,567.28 117,771.72
Gross loss -1,148,569.25 -914,823.35 -233,745.90
Profit factor 1.56 1.83 0.50
Avg. trade net profit 202.65 262.41 -418.68
Avg. winning trade 908.25 897.67 1,090.48
Avg. losing trade -966.81 -897.77 -1,383.11
Ratio Avg. Win:Avg. Loss 0.94 1.00 0.79
Largest winning trade 21,390.30 21,390.30 13,303.58
Largest losing trade -21,290.59 -11,813.16 -21,290.59
In [29]:
ts = trades.groupby('sector')['pnl'].agg(pnl_stats).T.rename_axis({1.0: 'All trades'}, axis='columns')
ts
Out[29]:
sector Basic Materials Communication Services Consumer Cyclical Consumer Defensive Energy Financial Services Healthcare Industrials Real Estate Technology Unknown Utilities
Total profit 116,905.00 4,376.00 113,273.00 54,363.00 27,714.00 26,706.00 40,158.00 158,423.00 -3,186.00 106,519.00 -8,295.00 2,814.00
Gross profit 185,085.85 30,278.32 246,748.71 118,462.02 174,712.02 59,544.42 72,752.93 301,537.03 13,979.91 199,941.70 382,482.08 2,814.02
Gross loss -68,180.57 -25,902.68 -133,475.88 -64,099.02 -146,997.90 -32,838.63 -32,594.75 -143,113.88 -17,165.44 -93,423.17 -390,777.34 0.00
Profit factor 2.71 1.17 1.85 1.85 1.19 1.81 2.23 2.11 0.81 2.14 0.98 nan
Avg. trade net profit 453.12 50.88 197.68 308.88 125.97 194.93 304.23 290.15 -245.04 359.86 -11.57 938.01
Avg. winning trade 963.99 617.92 658.00 971.00 1,256.92 692.38 757.84 861.53 2,329.98 1,116.99 1,028.18 938.01
Avg. losing trade -1,033.04 -700.07 -674.12 -1,187.02 -1,814.79 -643.89 -905.41 -730.17 -2,452.21 -798.49 -1,132.69 nan
Ratio Avg. Win:Avg. Loss 0.93 0.88 0.98 0.82 0.69 1.08 0.84 1.18 0.95 1.40 0.91 nan
Largest winning trade 13,017.40 4,520.24 11,591.24 13,013.69 21,250.92 7,038.14 9,097.78 13,188.00 10,307.13 15,078.25 21,390.30 1,283.82
Largest losing trade -7,162.82 -6,878.89 -7,352.97 -4,780.58 -9,122.47 -9,281.67 -4,396.59 -7,042.74 -7,284.31 -6,032.79 -21,290.59 359.88
In [30]:
ts2 = trades.groupby(['sector','long'])['pnl'].agg(pnl_stats).T.rename_axis({False: 'Short trades', True: 'Long trades'}, axis='columns')
ts2
Out[30]:
sector Basic Materials Communication Services Consumer Cyclical Consumer Defensive Energy Financial Services Healthcare Industrials Real Estate Technology Unknown Utilities
long Short trades Long trades Long trades Short trades Long trades Short trades Long trades Short trades Long trades Long trades Short trades Long trades Short trades Long trades Long trades Short trades Long trades Short trades Long trades Long trades
Total profit 497.00 116,409.00 4,376.00 -443.00 113,716.00 -2,779.00 57,142.00 13.00 27,701.00 26,706.00 1,835.00 38,323.00 -471.00 158,894.00 -3,186.00 -23.00 106,542.00 -114,601.00 106,306.00 2,814.00
Gross profit 1,377.21 183,708.64 30,278.32 1,137.06 245,611.64 4,220.55 114,241.47 16.26 174,695.75 59,544.42 1,834.79 70,918.14 398.30 301,138.73 13,979.91 0.00 199,941.70 108,787.55 273,694.52 2,814.02
Gross loss -880.62 -67,299.95 -25,902.68 -1,580.50 -131,895.37 -6,999.98 -57,099.04 -2.87 -146,995.03 -32,838.63 0.00 -32,594.75 -869.61 -142,244.27 -17,165.44 -23.32 -93,399.85 -223,389.01 -167,388.33 0.00
Profit factor 1.56 2.73 1.17 0.72 1.86 0.60 2.00 5.67 1.19 1.81 nan 2.18 0.46 2.12 0.81 0.00 2.14 0.49 1.64 nan
Avg. trade net profit 99.32 460.11 50.88 -23.34 205.26 -277.94 344.23 6.70 127.07 194.93 611.60 297.08 -42.85 297.00 -245.04 -11.66 362.39 -509.34 216.07 938.01
Avg. winning trade 459.07 972.00 617.92 142.13 669.24 1,055.14 968.15 16.26 1,265.91 692.38 611.60 762.56 199.15 865.34 2,329.98 nan 1,116.99 1,250.43 960.33 938.01
Avg. losing trade -440.31 -1,051.56 -700.07 -143.68 -705.32 -1,166.66 -1,189.56 -2.87 -1,837.44 -643.89 nan -905.41 -96.62 -760.66 -2,452.21 -11.66 -812.17 -1,618.76 -808.64 nan
Ratio Avg. Win:Avg. Loss 1.04 0.92 0.88 0.99 0.95 0.90 0.81 5.67 0.69 1.08 nan 0.84 2.06 1.14 0.95 nan 1.38 0.77 1.19 nan
Largest winning trade 1,297.01 13,017.40 4,520.24 1,111.55 11,591.24 2,349.80 13,013.69 16.26 21,250.92 7,038.14 1,558.56 9,097.78 317.99 13,188.00 10,307.13 -1.71 15,078.25 13,303.58 21,390.30 1,283.82
Largest losing trade -473.64 -7,162.82 -6,878.89 -1,127.37 -7,352.97 -3,710.06 -4,780.58 -2.87 -9,122.47 -9,281.67 115.22 -4,396.59 -309.47 -7,042.74 -7,284.31 -21.61 -6,032.79 -21,290.59 -11,813.16 359.88
In [31]:
tsx = trades[trades.sector == 'Financial Services']
tsxa = trades[trades.sector == 'Healthcare']
ts3a = tsxa.groupby(['sector','long'])['pnl'].agg(pnl_stats).T.rename_axis({False: 'Short trades', True: 'Long trades'}, axis='columns')
ts3b = tsx.groupby(['sector','long'])['pnl'].agg(pnl_stats).T.rename_axis({False: 'Short trades', True: 'Long trades'}, axis='columns')
ts3 = ts3a.join(ts3b)
ts3
Out[31]:
sector Healthcare Financial Services
long Short trades Long trades Long trades
Total profit 1,835.00 38,323.00 26,706.00
Gross profit 1,834.79 70,918.14 59,544.42
Gross loss 0.00 -32,594.75 -32,838.63
Profit factor nan 2.18 1.81
Avg. trade net profit 611.60 297.08 194.93
Avg. winning trade 611.60 762.56 692.38
Avg. losing trade nan -905.41 -643.89
Ratio Avg. Win:Avg. Loss nan 0.84 1.08
Largest winning trade 1,558.56 9,097.78 7,038.14
Largest losing trade 115.22 -4,396.59 -9,281.67

================================================================

Duration

In [32]:
d_stats = trades.assign(ones=1).groupby('ones')['duration'].agg(duration_stats).T.rename_axis({1.0: 'All trades'}, axis='columns')
d_stats_wl = trades.groupby('long')['duration'].agg(duration_stats).T.rename_axis({False: 'Short trades', True: 'Long trades'}, axis='columns')
d_stats = d_stats.join(d_stats_wl)
d_stats[['All trades', 'Long trades', 'Short trades']]
Out[32]:
All trades Long trades Short trades
Avg duration 75 days 21:24:00.228064 64 days 06:05:30 196 days 21:24:02.599277
Median duration 40 days 00:00:00 34 days 00:00:00 126 days 00:00:00
Avg # trades per day 1.14 5.54 0.10
Avg # trades per month 23.92 116.31 2.10
In [33]:
ts = trades.groupby('sector')['duration'].agg(duration_stats).T.rename_axis({1.0: 'All trades'}, axis='columns')
ts
Out[33]:
sector Basic Materials Communication Services Consumer Cyclical Consumer Defensive Energy Financial Services Healthcare Industrials Real Estate Technology Unknown Utilities
Avg duration 82 days 08:27:54.418604 83 days 19:32:05.581395 57 days 00:30:09.424083 90 days 06:40:54.545454 56 days 23:40:21.818181 102 days 12:36:47.299270 57 days 14:00:00 63 days 04:02:38.241758 95 days 01:50:46.153846 72 days 04:12:58.378378 99 days 01:36:24.100418 89 days 08:00:00
Median duration 58 days 00:00:00 58 days 12:00:00 33 days 00:00:00 59 days 00:00:00 33 days 00:00:00 63 days 00:00:00 31 days 00:00:00 33 days 00:00:00 58 days 00:00:00 59 days 00:00:00 61 days 00:00:00 90 days 00:00:00
Avg # trades per day 0.40 0.28 0.30 0.06 0.73 0.41 0.07 1.05 0.05 0.57 0.86 1.50
Avg # trades per month 8.45 5.98 6.39 1.33 15.30 8.59 1.52 22.05 1.00 12.02 18.05 31.50
In [34]:
ts2 = trades.groupby(['sector','duration'])['duration'].agg(duration_stats).T.rename_axis({False: 'Short trades', True: 'Long trades'}, axis='columns')    
ts2
Out[34]:
sector Basic Materials ... Unknown Utilities
duration 0 days 2 days 13 days 17 days 20 days 21 days 25 days 26 days 27 days 28 days ... 596 days 628 days 686 days 749 days 769 days 776 days 822 days 834 days 88 days 90 days
Avg duration 0 days 2 days 13 days 17 days 20 days 21 days 25 days 26 days 27 days 28 days ... 596 days 628 days 686 days 749 days 769 days 776 days 822 days 834 days 88 days 90 days
Median duration 0 days 2 days 13 days 17 days 20 days 21 days 25 days 26 days 27 days 28 days ... 596 days 628 days 686 days 749 days 769 days 776 days 822 days 834 days 88 days 90 days
Avg # trades per day NaT NaT NaT NaT NaT NaT NaT NaT NaT NaT ... NaT NaT NaT NaT NaT NaT NaT NaT NaT NaT
Avg # trades per month NaT NaT NaT NaT NaT NaT NaT NaT NaT NaT ... NaT NaT NaT NaT NaT NaT NaT NaT NaT NaT

4 rows × 860 columns

In [35]:
tsx = trades[trades.sector == 'Financial Services']
tsxa = trades[trades.sector == 'Healthcare']
ts3a = tsxa.groupby(['sector','duration'])['duration'].agg(duration_stats).T.rename_axis({False: 'Short trades', True: 'Long trades'}, axis='columns')
ts3b = tsx.groupby(['sector','duration'])['duration'].agg(duration_stats).T.rename_axis({False: 'Short trades', True: 'Long trades'}, axis='columns')
ts3 = ts3a.join(ts3b)


ts3
Out[35]:
sector Healthcare ... Financial Services
duration 0 days 1 days 2 days 4 days 8 days 13 days 14 days 15 days 18 days 19 days ... 244 days 245 days 246 days 247 days 249 days 272 days 273 days 276 days 333 days 335 days
Avg duration 0 days 1 days 2 days 4 days 8 days 13 days 14 days 15 days 18 days 19 days ... 244 days 245 days 246 days 247 days 249 days 272 days 273 days 276 days 333 days 335 days
Median duration 0 days 1 days 2 days 4 days 8 days 13 days 14 days 15 days 18 days 19 days ... 244 days 245 days 246 days 247 days 249 days 272 days 273 days 276 days 333 days 335 days
Avg # trades per day NaT NaT NaT NaT NaT NaT NaT NaT NaT NaT ... NaT NaT NaT NaT NaT NaT NaT NaT NaT NaT
Avg # trades per month NaT NaT NaT NaT NaT NaT NaT NaT NaT NaT ... NaT NaT NaT NaT NaT NaT NaT NaT NaT NaT

4 rows × 115 columns

================================================================

Returns-based

In [36]:
pd.options.display.float_format = '{0:.5f}%'.format
stats = trades.assign(ones=1).groupby('ones')['returns'].agg(return_stats).T.rename_axis({1.0: 'All trades'}, axis='columns')
stats2 = trades.groupby('long')['returns'].agg(return_stats).T.rename_axis({False: 'Short trades', True: 'Long trades'}, axis='columns')
stats = stats.join(stats2)
print "CAUTION-MAX WIN/LOSING TRADE AT 100X"
(stats[['All trades', 'Long trades', 'Short trades']] * 100)
CAUTION-MAX WIN/LOSING TRADE AT 100X
Out[36]:
All trades Long trades Short trades
Avg returns all trades 0.20265% 0.26241% -0.41868%
Avg returns winning 0.90825% 0.89767% 1.09048%
Avg returns losing -0.96681% -0.89777% -1.38311%
Median returns all trades 0.02443% 0.03241% -0.05388%
Median returns winning 0.24286% 0.23106% 0.43619%
Median returns losing -0.25973% -0.22602% -0.47933%
Profit factor 93.94278% 99.98953% 78.84243%
Percent profitable 62.36934% 64.61806% 38.98917%
Ratio Avg. Win:Avg. Loss 93.94278% 99.98953% 78.84243%
Largest winning trade 21.39030% 21.39030% 13.30358%
Largest losing trade -21.29059% -11.81316% -21.29059%
In [37]:
ts = trades.groupby('sector')['returns'].agg(return_stats).T.rename_axis({1.0: 'All trades'}, axis='columns')
ts
Out[37]:
sector Basic Materials Communication Services Consumer Cyclical Consumer Defensive Energy Financial Services Healthcare Industrials Real Estate Technology Unknown Utilities
Avg returns all trades 0.00453% 0.00051% 0.00198% 0.00309% 0.00126% 0.00195% 0.00304% 0.00290% -0.00245% 0.00360% -0.00012% 0.00938%
Avg returns winning 0.00964% 0.00618% 0.00658% 0.00971% 0.01257% 0.00692% 0.00758% 0.00862% 0.02330% 0.01117% 0.01028% 0.00938%
Avg returns losing -0.01033% -0.00700% -0.00674% -0.01187% -0.01815% -0.00644% -0.00905% -0.00730% -0.02452% -0.00798% -0.01133% nan%
Median returns all trades 0.00085% 0.00018% 0.00007% 0.00066% 0.00023% 0.00039% 0.00011% 0.00050% -0.00002% 0.00041% 0.00002% 0.01170%
Median returns winning 0.00302% 0.00136% 0.00122% 0.00332% 0.00350% 0.00252% 0.00099% 0.00313% 0.00727% 0.00340% 0.00334% 0.01170%
Median returns losing -0.00485% -0.00019% -0.00075% -0.00629% -0.00563% -0.00138% -0.00297% -0.00261% -0.00373% -0.00232% -0.00357% nan%
Profit factor 0.93316% 0.88266% 0.97608% 0.81802% 0.69260% 1.07530% 0.83702% 1.17990% 0.95016% 1.39888% 0.90773% nan%
Percent profitable 0.74419% 0.56977% 0.65445% 0.69318% 0.63182% 0.62774% 0.72727% 0.64103% 0.46154% 0.60473% 0.51883% 1.00000%
Ratio Avg. Win:Avg. Loss 0.93316% 0.88266% 0.97608% 0.81802% 0.69260% 1.07530% 0.83702% 1.17990% 0.95016% 1.39888% 0.90773% nan%
Largest winning trade 0.13017% 0.04520% 0.11591% 0.13014% 0.21251% 0.07038% 0.09098% 0.13188% 0.10307% 0.15078% 0.21390% 0.01284%
Largest losing trade -0.07163% -0.06879% -0.07353% -0.04781% -0.09122% -0.09282% -0.04397% -0.07043% -0.07284% -0.06033% -0.21291% 0.00360%
In [38]:
ts2 = trades.groupby(['sector','long'])['returns'].agg(return_stats).T.rename_axis({False: 'Short trades', True: 'Long trades'}, axis='columns')
ts2
Out[38]:
sector Basic Materials Communication Services Consumer Cyclical Consumer Defensive Energy Financial Services Healthcare Industrials Real Estate Technology Unknown Utilities
long Short trades Long trades Long trades Short trades Long trades Short trades Long trades Short trades Long trades Long trades Short trades Long trades Short trades Long trades Long trades Short trades Long trades Short trades Long trades Long trades
Avg returns all trades 0.00099% 0.00460% 0.00051% -0.00023% 0.00205% -0.00278% 0.00344% 0.00007% 0.00127% 0.00195% 0.00612% 0.00297% -0.00043% 0.00297% -0.00245% -0.00012% 0.00362% -0.00509% 0.00216% 0.00938%
Avg returns winning 0.00459% 0.00972% 0.00618% 0.00142% 0.00669% 0.01055% 0.00968% 0.00016% 0.01266% 0.00692% 0.00612% 0.00763% 0.00199% 0.00865% 0.02330% nan% 0.01117% 0.01250% 0.00960% 0.00938%
Avg returns losing -0.00440% -0.01052% -0.00700% -0.00144% -0.00705% -0.01167% -0.01190% -0.00003% -0.01837% -0.00644% nan% -0.00905% -0.00097% -0.00761% -0.02452% -0.00012% -0.00812% -0.01619% -0.00809% nan%
Median returns all trades 0.00000% 0.00091% 0.00018% -0.00001% 0.00009% -0.00092% 0.00068% 0.00007% 0.00028% 0.00039% 0.00161% 0.00009% -0.00016% 0.00054% -0.00002% -0.00012% 0.00043% -0.00109% 0.00012% 0.01170%
Median returns winning 0.00080% 0.00303% 0.00136% 0.00002% 0.00127% 0.00732% 0.00285% 0.00016% 0.00361% 0.00252% 0.00161% 0.00088% 0.00199% 0.00313% 0.00727% nan% 0.00340% 0.00501% 0.00252% 0.01170%
Median returns losing -0.00440% -0.00512% -0.00019% -0.00024% -0.00077% -0.00504% -0.00629% -0.00003% -0.00596% -0.00138% nan% -0.00297% -0.00057% -0.00280% -0.00373% -0.00012% -0.00241% -0.00761% -0.00111% nan%
Profit factor 1.04261% 0.92434% 0.88266% 0.98922% 0.94884% 0.90441% 0.81387% 5.67251% 0.68895% 1.07530% nan% 0.84223% 2.06108% 1.13761% 0.95016% nan% 1.37531% 0.77246% 1.18759% nan%
Percent profitable 0.60000% 0.74704% 0.56977% 0.42105% 0.66245% 0.40000% 0.71084% 0.50000% 0.63303% 0.62774% 1.00000% 0.72093% 0.18182% 0.65047% 0.46154% 0.00000% 0.60884% 0.38667% 0.57927% 1.00000%
Ratio Avg. Win:Avg. Loss 1.04261% 0.92434% 0.88266% 0.98922% 0.94884% 0.90441% 0.81387% 5.67251% 0.68895% 1.07530% nan% 0.84223% 2.06108% 1.13761% 0.95016% nan% 1.37531% 0.77246% 1.18759% nan%
Largest winning trade 0.01297% 0.13017% 0.04520% 0.01112% 0.11591% 0.02350% 0.13014% 0.00016% 0.21251% 0.07038% 0.01559% 0.09098% 0.00318% 0.13188% 0.10307% -0.00002% 0.15078% 0.13304% 0.21390% 0.01284%
Largest losing trade -0.00474% -0.07163% -0.06879% -0.01127% -0.07353% -0.03710% -0.04781% -0.00003% -0.09122% -0.09282% 0.00115% -0.04397% -0.00309% -0.07043% -0.07284% -0.00022% -0.06033% -0.21291% -0.11813% 0.00360%
In [39]:
tsx = trades[trades.sector == 'Financial Services']
tsxa = trades[trades.sector == 'Healthcare']
ts3a = tsxa.groupby(['sector','long'])['returns'].agg(return_stats).T.rename_axis({False: 'Short trades', True: 'Long trades'}, axis='columns')
ts3b = tsx.groupby(['sector','long'])['returns'].agg(return_stats).T.rename_axis({False: 'Short trades', True: 'Long trades'}, axis='columns')
ts3 = ts3a.join(ts3b)
ts3
Out[39]:
sector Healthcare Financial Services
long Short trades Long trades Long trades
Avg returns all trades 0.00612% 0.00297% 0.00195%
Avg returns winning 0.00612% 0.00763% 0.00692%
Avg returns losing nan% -0.00905% -0.00644%
Median returns all trades 0.00161% 0.00009% 0.00039%
Median returns winning 0.00161% 0.00088% 0.00252%
Median returns losing nan% -0.00297% -0.00138%
Profit factor nan% 0.84223% 1.07530%
Percent profitable 1.00000% 0.72093% 0.62774%
Ratio Avg. Win:Avg. Loss nan% 0.84223% 1.07530%
Largest winning trade 0.01559% 0.09098% 0.07038%
Largest losing trade 0.00115% -0.04397% -0.09282%

================================================================

Symbol-wise

In [40]:
pd.options.display.float_format = '{0:.3f}%'.format 
lp = trades.groupby('symbol')['returns'].agg(return_stats) * 100
lp = lp.sort_index()
qwe = pd.DataFrame(data = {'symbol':[], 'number of trades':[]})
for i in lp.index.values:
    yu = len(trades[trades.symbol == i])
    qwe = qwe.append({'symbol':i, 'number of trades':yu}, ignore_index=True)
qwe = qwe.set_index('symbol')  
lp = lp.merge(qwe, left_index=True, right_index=True, how='left')
cols = lp.columns.tolist()
cols = cols[-1:] + cols[:-1]
lp = lp[cols]
lp = lp.sort('Avg returns all trades',ascending=False)
lp['number of trades'] = lp['number of trades'].map('{:,.0f}'.format)
lp['Profit factor'] = (lp['Profit factor'] / 100 ).map('{:,.3f}'.format) 

lp
    
    
    
Out[40]:
number of trades Avg returns all trades Avg returns winning Avg returns losing Median returns all trades Median returns winning Median returns losing Profit factor Percent profitable Ratio Avg. Win:Avg. Loss Largest winning trade Largest losing trade
symbol
OIS 1 7.771% 7.771% nan% 7.771% 7.771% nan% nan 100.000% nan% 7.771% 7.771%
GDOT 1 7.038% 7.038% nan% 7.038% 7.038% nan% nan 100.000% nan% 7.038% 7.038%
MPC 1 6.735% 6.735% nan% 6.735% 6.735% nan% nan 100.000% nan% 6.735% 6.735%
SHO 2 6.090% 6.090% nan% 6.090% 6.090% nan% nan 100.000% nan% 10.307% 1.873%
MOH 1 5.437% 5.437% nan% 5.437% 5.437% nan% nan 100.000% nan% 5.437% 5.437%
HA 1 5.418% 5.418% nan% 5.418% 5.418% nan% nan 100.000% nan% 5.418% 5.418%
FSLR 3 5.389% 8.734% -1.302% 2.390% 8.734% -1.302% 6.711 66.667% 671.057% 15.078% -1.302%
SCL 1 5.177% 5.177% nan% 5.177% 5.177% nan% nan 100.000% nan% 5.177% 5.177%
PATK 2 4.738% 4.738% nan% 4.738% 4.738% nan% nan 100.000% nan% 9.001% 0.474%
UFI 1 4.679% 4.679% nan% 4.679% 4.679% nan% nan 100.000% nan% 4.679% 4.679%
IQNT 1 4.520% 4.520% nan% 4.520% 4.520% nan% nan 100.000% nan% 4.520% 4.520%
ADUS 3 4.286% 4.286% nan% 2.460% 2.460% nan% nan 100.000% nan% 9.098% 1.300%
NUS 1 4.250% 4.250% nan% 4.250% 4.250% nan% nan 100.000% nan% 4.250% 4.250%
MTZ 1 4.241% 4.241% nan% 4.241% 4.241% nan% nan 100.000% nan% 4.241% 4.241%
AIR 1 4.158% 4.158% nan% 4.158% 4.158% nan% nan 100.000% nan% 4.158% 4.158%
FOSL 1 4.124% 4.124% nan% 4.124% 4.124% nan% nan 100.000% nan% 4.124% 4.124%
VEC 2 4.054% 4.054% nan% 4.054% 4.054% nan% nan 100.000% nan% 5.031% 3.077%
HMA 4 3.936% 3.936% nan% 1.762% 1.762% nan% nan 100.000% nan% 12.209% 0.012%
CRNT 3 3.751% 3.751% nan% 0.268% 0.268% nan% nan 100.000% nan% 10.804% 0.180%
ONE 3 3.468% 3.468% nan% 2.825% 2.825% nan% nan 100.000% nan% 7.255% 0.324%
RIGP 4 3.434% 3.434% nan% 1.068% 1.068% nan% nan 100.000% nan% 11.448% 0.152%
MRC 5 3.342% 5.270% -4.372% 3.792% 4.006% -4.372% 1.205 80.000% 120.532% 12.731% -4.372%
VNCE 2 3.310% 3.310% nan% 3.310% 3.310% nan% nan 100.000% nan% 3.556% 3.063%
RYAM 1 3.291% 3.291% nan% 3.291% 3.291% nan% nan 100.000% nan% 3.291% 3.291%
GRMN 2 3.169% 3.169% nan% 3.169% 3.169% nan% nan 100.000% nan% 3.607% 2.732%
ENTA 1 3.047% 3.047% nan% 3.047% 3.047% nan% nan 100.000% nan% 3.047% 3.047%
CACC 2 2.991% 2.991% nan% 2.991% 2.991% nan% nan 100.000% nan% 5.796% 0.187%
AN 1 2.819% 2.819% nan% 2.819% 2.819% nan% nan 100.000% nan% 2.819% 2.819%
GVA 1 2.737% 2.737% nan% 2.737% 2.737% nan% nan 100.000% nan% 2.737% 2.737%
WNR 2 2.642% 2.642% nan% 2.642% 2.642% nan% nan 100.000% nan% 4.864% 0.420%
... ... ... ... ... ... ... ... ... ... ... ... ...
NAFC 1 -1.449% nan% -1.449% -1.449% nan% -1.449% nan 0.000% nan% -1.449% -1.449%
KFRC 1 -1.561% nan% -1.561% -1.561% nan% -1.561% nan 0.000% nan% -1.561% -1.561%
IHR 2 -1.593% 0.000% -3.187% -1.593% 0.000% -3.187% 0.000 50.000% 0.014% 0.000% -3.187%
MTGE 3 -1.686% nan% -1.686% -0.037% nan% -0.037% nan 0.000% nan% -0.005% -5.017%
NE 7 -1.708% 1.526% -4.134% -2.921% 2.035% -4.332% 0.369 42.857% 36.922% 2.399% -4.952%
PRX 2 -1.853% nan% -1.853% -1.853% nan% -1.853% nan 0.000% nan% -0.002% -3.705%
GCI 1 -1.955% nan% -1.955% -1.955% nan% -1.955% nan 0.000% nan% -1.955% -1.955%
PEIX 7 -1.981% 3.883% -4.327% -0.001% 3.883% -6.486% 0.898 28.571% 89.756% 4.109% -8.059%
LZB 1 -2.059% nan% -2.059% -2.059% nan% -2.059% nan 0.000% nan% -2.059% -2.059%
URS 4 -2.060% nan% -2.060% -1.658% nan% -1.658% nan 0.000% nan% -0.001% -4.925%
OME 5 -2.236% 0.006% -2.796% -2.449% 0.006% -3.197% 0.002 20.000% 0.219% 0.006% -4.781%
ARC 3 -2.271% nan% -2.271% -2.768% nan% -2.768% nan 0.000% nan% -0.025% -4.020%
GT 1 -2.344% nan% -2.344% -2.344% nan% -2.344% nan 0.000% nan% -2.344% -2.344%
CPA 3 -2.428% nan% -2.428% -2.361% nan% -2.361% nan 0.000% nan% -0.909% -4.013%
CTCM 1 -2.441% nan% -2.441% -2.441% nan% -2.441% nan 0.000% nan% -2.441% -2.441%
VPHM 2 -2.551% nan% -2.551% -2.551% nan% -2.551% nan 0.000% nan% -0.358% -4.744%
AHG 1 -2.625% nan% -2.625% -2.625% nan% -2.625% nan 0.000% nan% -2.625% -2.625%
XRTX 2 -2.787% 0.216% -5.790% -2.787% 0.216% -5.790% 0.037 50.000% 3.732% 0.216% -5.790%
FRO 1 -3.063% nan% -3.063% -3.063% nan% -3.063% nan 0.000% nan% -3.063% -3.063%
GIFI 2 -3.085% nan% -3.085% -3.085% nan% -3.085% nan 0.000% nan% -2.328% -3.842%
CALL 3 -3.297% nan% -3.297% -2.321% nan% -2.321% nan 0.000% nan% -0.693% -6.879%
MITT 2 -3.643% nan% -3.643% -3.643% nan% -3.643% nan 0.000% nan% -0.002% -7.284%
SPN 3 -3.768% nan% -3.768% -2.923% nan% -2.923% nan 0.000% nan% -1.881% -6.502%
HGG 2 -4.218% nan% -4.218% -4.218% nan% -4.218% nan 0.000% nan% -1.762% -6.674%
SB 3 -4.253% 0.274% -6.517% -5.991% 0.274% -6.517% 0.042 33.333% 4.203% 0.274% -7.043%
VRA 1 -4.269% nan% -4.269% -4.269% nan% -4.269% nan 0.000% nan% -4.269% -4.269%
JOE 1 -4.447% nan% -4.447% -4.447% nan% -4.447% nan 0.000% nan% -4.447% -4.447%
BTH 1 -4.925% nan% -4.925% -4.925% nan% -4.925% nan 0.000% nan% -4.925% -4.925%
NR 1 -5.506% nan% -5.506% -5.506% nan% -5.506% nan 0.000% nan% -5.506% -5.506%
FU 1 -5.605% nan% -5.605% -5.605% nan% -5.605% nan 0.000% nan% -5.605% -5.605%

465 rows × 12 columns

================================================================

Median holding time per symbol

In [41]:
med_hold = trades.groupby('symbol')['duration'].agg(lambda x: x.median())
#not super helpful ... for me atleast when using larger universe size
med_hold.sort(axis=0, ascending=True, kind='quicksort', na_position='last', inplace=True)
med_hold
Out[41]:
symbol
PERY       0 days 00:00:00
BBOX       0 days 00:00:00
HNI        0 days 00:00:00
DEPO       1 days 00:00:00
CITP       2 days 00:00:00
SPAR       3 days 12:00:00
PKD        4 days 00:00:00
SFN       10 days 00:00:00
CVI       14 days 00:00:00
RCII      14 days 12:00:00
STRL      17 days 12:00:00
BTH       19 days 00:00:00
ITG       19 days 00:00:00
PEIX      26 days 00:00:00
DLA       26 days 12:00:00
VPHM      27 days 00:00:00
CTCM      27 days 00:00:00
DW        27 days 00:00:00
KFRC      27 days 00:00:00
EBS       27 days 00:00:00
CAS       27 days 00:00:00
NR        27 days 00:00:00
DFZ       27 days 12:00:00
SHLO      28 days 00:00:00
UFI       28 days 00:00:00
UTMD      28 days 00:00:00
LYTS      28 days 00:00:00
GOGL      28 days 00:00:00
ICFI      28 days 00:00:00
CLF       28 days 00:00:00
                ...       
AEO       95 days 00:00:00
ODC       97 days 00:00:00
NHC       99 days 00:00:00
WSTC     103 days 12:00:00
WCG      104 days 00:00:00
ALG      104 days 12:00:00
WCRX     106 days 00:00:00
KND      106 days 12:00:00
RYI      108 days 00:00:00
RTI      109 days 00:00:00
VSEC     119 days 00:00:00
CMC      122 days 00:00:00
NCIT     123 days 12:00:00
GSOL     124 days 00:00:00
TECD     124 days 00:00:00
UDF      133 days 00:00:00
INT      136 days 12:00:00
IWM      150 days 00:00:00
MITT     151 days 12:00:00
NUE      153 days 00:00:00
NHTC     153 days 00:00:00
HNNA     154 days 00:00:00
AGX      165 days 00:00:00
XEC      166 days 12:00:00
CLW      215 days 00:00:00
WDC      230 days 00:00:00
BELF_A   239 days 00:00:00
MTGE     240 days 00:00:00
HALL     261 days 12:00:00
FU       268 days 00:00:00
Name: duration, dtype: timedelta64[ns]

================================================================

Regular Tearsheet

In [42]:
returns = bt.daily_performance.returns
cash = bt.daily_performance['ending_cash']
positions = pf.capacity.pos.extract_pos(bt.positions, cash)
#transactions = _groupby_consecutive(bt.transactions)
gross_lev=bt.recorded_vars.leverage
In [43]:
print "Downside deviation is a measure of downside risk that focuses on returns that fall below a minimum threshold or minimum acceptable return (MAR)."
print("\n"'Annualized downside deviation below 0% per month = {:3}%'.format((pf.capacity.empyrical.stats.downside_risk(returns, required_return=0.0, period='monthly'))* 100))
print("\n"'Annualized downside deviation below 1% per month = {:3}%'.format((pf.capacity.empyrical.stats.downside_risk(returns, required_return=0.01, period='monthly'))* 100))
print("\n"'Annualized downside deviation below 2% per month = {:3}%'.format((pf.capacity.empyrical.stats.downside_risk(returns, required_return=0.02, period='monthly'))* 100))
print("\n"'Annualized downside deviation below 3% per month = {:3}%'.format((pf.capacity.empyrical.stats.downside_risk(returns, required_return=0.03, period='monthly'))* 100))
print "\n"
print("\n"'Annualized downside deviation below 0% per year = {:3}%'.format((pf.capacity.empyrical.stats.downside_risk(returns, required_return=0.0, period='yearly'))* 100))
print("\n"'Annualized downside deviation below 1% per year = {:3}%'.format((pf.capacity.empyrical.stats.downside_risk(returns, required_return=0.01, period='yearly'))* 100))
print("\n"'Annualized downside deviation below 2% per year = {:3}%'.format((pf.capacity.empyrical.stats.downside_risk(returns, required_return=0.02, period='yearly'))* 100))
print("\n"'Annualized downside deviation below 3% per year = {:3}%'.format((pf.capacity.empyrical.stats.downside_risk(returns, required_return=0.03, period='yearly'))* 100))
print("\n"'Annualized downside deviation below 5% per year = {:3}%'.format((pf.capacity.empyrical.stats.downside_risk(returns, required_return=0.05, period='yearly'))* 100))
Downside deviation is a measure of downside risk that focuses on returns that fall below a minimum threshold or minimum acceptable return (MAR).

Annualized downside deviation below 0% per month = 1.37701878364%

Annualized downside deviation below 1% per month = 3.84147751727%

Annualized downside deviation below 2% per month = 7.04333770968%

Annualized downside deviation below 3% per month = 10.4045265056%



Annualized downside deviation below 0% per year = 0.397511082706%

Annualized downside deviation below 1% per year = 1.10893903934%

Annualized downside deviation below 2% per year = 2.03323646134%

Annualized downside deviation below 3% per year = 3.00352808939%

Annualized downside deviation below 5% per year = 4.97872372413%
In [44]:
# Run model that assumes returns to be T-distributed
#returns = bt.daily_performance.returns
trace = pf.bayesian.run_model('t', returns)
print("\n"'Probability of Sharpe ratio > 0 = {:3}%'.format((trace['sharpe'] > 0).mean() * 100))
print("\n"'Probability of Sharpe ratio > 0.25 = {:3}%'.format((trace['sharpe'] > 0.25).mean() * 100))
print("\n"'Probability of Sharpe ratio > 0.5 = {:3}%'.format((trace['sharpe'] > 0.5).mean() * 100))
print("\n"'Probability of Sharpe ratio > 0.75 = {:3}%'.format((trace['sharpe'] > 0.75).mean() * 100))
print("\n"'Probability of Sharpe ratio > 1 = {:3}%'.format((trace['sharpe'] > 1).mean() * 100))
print("\n"'Probability of Sharpe ratio > 1.25 = {:3}%'.format((trace['sharpe'] > 1.25).mean() * 100))
print("\n"'Probability of Sharpe ratio > 1.5 = {:3}%'.format((trace['sharpe'] > 1.5).mean() * 100))
#import pymc3 as pm #IMPORT RESTRICTION!!!
#pm.traceplot(trace);
 [-----------------100%-----------------] 500 of 500 complete in 4.9 sec
Probability of Sharpe ratio > 0 = 100.0%

Probability of Sharpe ratio > 0.25 = 100.0%

Probability of Sharpe ratio > 0.5 = 100.0%

Probability of Sharpe ratio > 0.75 = 100.0%

Probability of Sharpe ratio > 1 = 95.4%

Probability of Sharpe ratio > 1.25 = 71.8%

Probability of Sharpe ratio > 1.5 = 34.8%
In [45]:
pf.plot_prob_profit_trade(trades)
matplotlib.pyplot.figure()
pf.plot_slippage_sensitivity(returns, positions = positions, transactions = bt.transactions)
matplotlib.pyplot.figure()
pf.plot_slippage_sweep(returns, positions = positions, transactions = bt.transactions, slippage_params=(3, 8, 12, 18, 30, 50))
matplotlib.pyplot.figure()
plt.plot(bt.risk.alpha.index, bt.risk.alpha.values);
plt.ylabel('Single Factor Market Alpha');
In [46]:
lolo = trades.sort('close_dt').copy()
gen = (x for i,x in enumerate(list((lolo.close_dt))))
ty = list(gen)
yuio=[]
for i in range(len(ty)):
    year = ty[i].year
    yuio.append(year)
lolo["year"] = yuio
pd.options.display.float_format = '{0:.3f}%'.format 
total_pnl = lolo['pnl'].sum()
pct_profit_attribution = (lolo.groupby(['sector'])['pnl'].sum() / total_pnl)*100
pct_profit_attribution =pd.DataFrame(pct_profit_attribution.sort(inplace=False, ascending=False))
print("\n"'Profitability (PnL / PnL total) per Sector over entire backtest period of {:2,.0f} days'.format(((bt.end_date.date() - bt.start_date.date()).days)))
pct_profit_attribution
Profitability (PnL / PnL total) per Sector over entire backtest period of 5,104 days
Out[46]:
pnl
sector
Industrials 24.763%
Basic Materials 18.273%
Consumer Cyclical 17.705%
Technology 16.650%
Consumer Defensive 8.497%
Healthcare 6.277%
Energy 4.332%
Financial Services 4.174%
Communication Services 0.684%
Utilities 0.440%
Real Estate -0.498%
Unknown -1.297%
In [47]:
lolo1 = lolo.ix[:, [4, 10,12]]
rtyy= range(bt.start_date.year, bt.end_date.year+1)
rtyy2 = pd.DataFrame()
for i in rtyy:
    rtyy1 = (lolo1[lolo1.year == i].groupby(['sector', 'year']).sum() / lolo1.groupby('year').sum())*100
    print rtyy1
    rtyy2 = rtyy2.append(rtyy1)
rtyy3 = rtyy2.sortlevel(level=0, axis=0, ascending=True, inplace=False, sort_remaining=True)
rtyy4 = rtyy3.unstack(level=-1).T
print "\n""\n""+++++ Profit attribution % per Sector Per Year (PnL for the Sector per yr / PnL total per yr) +++++""\n"
print "...yes I know, not exactly profit attribution, but still insightful...""\n""\n"
rtyy4.plot(x=None, y=None, kind='bar', ax=None, subplots=True, sharex=False, sharey=False, layout=None, figsize=(15,55), 
           use_index=True, title=None, grid=None, legend=True, style=None, logx=False, logy=False, loglog=False, 
           xticks=None, yticks=None, xlim=None, ylim=None, rot=None, fontsize=12, colormap=None, table=False, 
           yerr=None, xerr=None, secondary_y=False, sort_columns=False)
                                pnl
sector                 year        
Basic Materials        2003  1.708%
Communication Services 2003  0.611%
Consumer Cyclical      2003 22.908%
Consumer Defensive     2003 -2.076%
Energy                 2003  8.016%
Financial Services     2003  2.636%
Healthcare             2003 -4.632%
Industrials            2003  4.978%
Real Estate            2003  1.160%
Technology             2003 47.778%
Unknown                2003 16.914%
                                pnl
sector                 year        
Basic Materials        2004 15.427%
Communication Services 2004  3.890%
Consumer Cyclical      2004 23.117%
Consumer Defensive     2004  3.129%
Energy                 2004 16.178%
Healthcare             2004  0.208%
Industrials            2004 37.211%
Unknown                2004  0.840%
                                pnl
sector                 year        
Basic Materials        2005 24.783%
Communication Services 2005  0.455%
Consumer Cyclical      2005 -6.857%
Consumer Defensive     2005  0.341%
Energy                 2005 17.896%
Financial Services     2005 18.990%
Healthcare             2005 -2.906%
Industrials            2005 15.355%
Real Estate            2005 -1.245%
Technology             2005  1.276%
Unknown                2005 31.912%
                                pnl
sector                 year        
Basic Materials        2006 32.464%
Communication Services 2006  2.542%
Consumer Cyclical      2006 12.086%
Consumer Defensive     2006  1.696%
Energy                 2006  3.701%
Financial Services     2006 -0.757%
Healthcare             2006  0.717%
Industrials            2006 22.280%
Technology             2006  2.498%
Unknown                2006 22.771%
                                   pnl
sector                 year           
Basic Materials        2007   112.715%
Communication Services 2007  -156.642%
Consumer Cyclical      2007  -330.119%
Consumer Defensive     2007   169.463%
Energy                 2007   997.578%
Financial Services     2007   -39.774%
Healthcare             2007    74.854%
Industrials            2007   309.605%
Technology             2007    64.973%
Unknown                2007 -1102.655%
                                  pnl
sector                 year          
Basic Materials        2008  -73.569%
Communication Services 2008   15.481%
Consumer Cyclical      2008 -110.186%
Consumer Defensive     2008  -29.651%
Energy                 2008   48.600%
Healthcare             2008   27.061%
Industrials            2008  100.072%
Technology             2008  137.027%
Unknown                2008  -14.836%
                            pnl
sector             year        
Basic Materials    2009 19.934%
Consumer Cyclical  2009 13.708%
Consumer Defensive 2009  0.157%
Energy             2009  6.310%
Financial Services 2009 -1.518%
Healthcare         2009  1.652%
Industrials        2009 19.474%
Real Estate        2009 10.071%
Technology         2009 15.236%
Unknown            2009 14.977%
                                  pnl
sector                 year          
Basic Materials        2010  158.070%
Communication Services 2010   28.576%
Consumer Cyclical      2010   62.870%
Consumer Defensive     2010   -8.231%
Energy                 2010  -59.068%
Healthcare             2010   41.927%
Industrials            2010  178.873%
Technology             2010    2.244%
Unknown                2010 -305.261%
                                 pnl
sector                 year         
Basic Materials        2011  15.331%
Communication Services 2011  -0.282%
Consumer Cyclical      2011   2.498%
Consumer Defensive     2011 -35.564%
Energy                 2011   3.167%
Healthcare             2011  22.459%
Industrials            2011  36.281%
Technology             2011  -4.193%
Unknown                2011  60.302%
                                 pnl
sector                 year         
Basic Materials        2012  23.420%
Communication Services 2012  -0.534%
Consumer Cyclical      2012  32.640%
Consumer Defensive     2012  13.033%
Energy                 2012  18.607%
Healthcare             2012  17.693%
Industrials            2012  14.163%
Real Estate            2012   2.483%
Technology             2012  12.971%
Unknown                2012 -34.474%
                                pnl
sector                 year        
Basic Materials        2013  1.363%
Communication Services 2013 -0.609%
Consumer Cyclical      2013 24.186%
Consumer Defensive     2013  1.283%
Energy                 2013  9.260%
Financial Services     2013  8.552%
Healthcare             2013  8.254%
Industrials            2013 14.873%
Technology             2013 37.700%
Unknown                2013 -4.861%
                                  pnl
sector                 year          
Basic Materials        2014 -171.346%
Communication Services 2014  138.572%
Consumer Cyclical      2014 -366.075%
Consumer Defensive     2014  425.224%
Energy                 2014 -158.511%
Financial Services     2014  105.712%
Healthcare             2014    2.582%
Industrials            2014  359.511%
Real Estate            2014  -85.807%
Technology             2014  428.239%
Unknown                2014 -578.101%
                             pnl
sector             year         
Basic Materials    2015   1.198%
Consumer Cyclical  2015  27.769%
Consumer Defensive 2015  39.961%
Energy             2015 -56.515%
Financial Services 2015  33.859%
Healthcare         2015  -0.458%
Industrials        2015  49.693%
Real Estate        2015 -26.863%
Technology         2015  26.807%
Unknown            2015  -1.577%
Utilities          2015   6.125%
                                pnl
sector                 year        
Basic Materials        2016 19.953%
Communication Services 2016 -4.251%
Consumer Cyclical      2016 22.896%
Consumer Defensive     2016 12.771%
Energy                 2016 14.264%
Financial Services     2016 -7.387%
Healthcare             2016  8.318%
Industrials            2016  8.294%
Technology             2016  1.372%
Unknown                2016 23.769%


+++++ Profit attribution % per Sector Per Year (PnL for the Sector per yr / PnL total per yr) +++++

...yes I know, not exactly profit attribution, but still insightful...


Out[47]:
array([<matplotlib.axes._subplots.AxesSubplot object at 0x7fbbbb836fd0>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x7fbbc852b1d0>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x7fbbbf9cb650>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x7fbbc34dfbd0>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x7fbbc3c7e590>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x7fbbc80a2b90>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x7fbbc164dd90>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x7fbbc2927490>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x7fbbc2ace890>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x7fbbcb49e1d0>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x7fbbc9b09490>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x7fbbc24c2cd0>], dtype=object)
In [48]:
lolo = trades.sort('close_dt').copy()
gen = (x for i,x in enumerate(list((lolo.close_dt))))
ty = list(gen)
yuio=[]
for i in range(len(ty)):
    year = ty[i].year
    yuio.append(year)
lolo["year"] = yuio
pd.options.display.float_format = '${:2,.0f}'.format 
profit_attribution = lolo.groupby(['sector'])['pnl'].sum()
profit_attribution =pd.DataFrame(profit_attribution.sort(inplace=False, ascending=False))
print("\n"'Profit ($ PnL) per Sector over entire backtest period of {:2,.0f} days'.format(((bt.end_date.date() - bt.start_date.date()).days)))
profit_attribution
Profit ($ PnL) per Sector over entire backtest period of 5,104 days
Out[48]:
pnl
sector
Industrials $158,423
Basic Materials $116,905
Consumer Cyclical $113,273
Technology $106,519
Consumer Defensive $54,363
Healthcare $40,158
Energy $27,714
Financial Services $26,706
Communication Services $4,376
Utilities $2,814
Real Estate $-3,186
Unknown $-8,295
In [49]:
lolo1 = lolo.ix[:, [4, 10,12]]
rtyy= range(bt.start_date.year, bt.end_date.year+1)
rtyy2 = pd.DataFrame()
for i in rtyy:
    rtyy1 = lolo1[lolo1.year == i].groupby(['sector', 'year']).sum()
    print rtyy1
    rtyy2 = rtyy2.append(rtyy1)
rtyy3 = rtyy2.sortlevel(level=0, axis=0, ascending=True, inplace=False, sort_remaining=True)
rtyy4 = rtyy3.unstack(level=-1).T
print "\n""\n""+++++ Profit $ per Sector Per Year +++++""\n""\n"
rtyy4.plot(x=None, y=None, kind='bar', ax=None, subplots=True, sharex=False, sharey=False, layout=None, figsize=(15,55), 
           use_index=True, title=None, grid=None, legend=True, style=None, logx=False, logy=False, loglog=False, 
           xticks=None, yticks=None, xlim=None, ylim=None, rot=None, fontsize=12, colormap=None, table=False, 
           yerr=None, xerr=None, secondary_y=False, sort_columns=False)
                                pnl
sector                 year        
Basic Materials        2003    $511
Communication Services 2003    $183
Consumer Cyclical      2003  $6,850
Consumer Defensive     2003   $-621
Energy                 2003  $2,397
Financial Services     2003    $788
Healthcare             2003 $-1,385
Industrials            2003  $1,488
Real Estate            2003    $347
Technology             2003 $14,286
Unknown                2003  $5,058
                                pnl
sector                 year        
Basic Materials        2004  $4,532
Communication Services 2004  $1,143
Consumer Cyclical      2004  $6,792
Consumer Defensive     2004    $919
Energy                 2004  $4,753
Healthcare             2004     $61
Industrials            2004 $10,933
Unknown                2004    $247
                                pnl
sector                 year        
Basic Materials        2005  $7,424
Communication Services 2005    $136
Consumer Cyclical      2005 $-2,054
Consumer Defensive     2005    $102
Energy                 2005  $5,361
Financial Services     2005  $5,689
Healthcare             2005   $-870
Industrials            2005  $4,600
Real Estate            2005   $-373
Technology             2005    $382
Unknown                2005  $9,560
                                pnl
sector                 year        
Basic Materials        2006 $20,839
Communication Services 2006  $1,632
Consumer Cyclical      2006  $7,758
Consumer Defensive     2006  $1,089
Energy                 2006  $2,376
Financial Services     2006   $-486
Healthcare             2006    $460
Industrials            2006 $14,302
Technology             2006  $1,603
Unknown                2006 $14,617
                                 pnl
sector                 year         
Basic Materials        2007   $1,546
Communication Services 2007  $-2,148
Consumer Cyclical      2007  $-4,527
Consumer Defensive     2007   $2,324
Energy                 2007  $13,679
Financial Services     2007    $-545
Healthcare             2007   $1,026
Industrials            2007   $4,245
Technology             2007     $891
Unknown                2007 $-15,120
                                 pnl
sector                 year         
Basic Materials        2008   $7,160
Communication Services 2008  $-1,507
Consumer Cyclical      2008  $10,724
Consumer Defensive     2008   $2,886
Energy                 2008  $-4,730
Healthcare             2008  $-2,634
Industrials            2008  $-9,740
Technology             2008 $-13,336
Unknown                2008   $1,444
                            pnl
sector             year        
Basic Materials    2009 $24,109
Consumer Cyclical  2009 $16,579
Consumer Defensive 2009    $190
Energy             2009  $7,631
Financial Services 2009 $-1,836
Healthcare         2009  $1,998
Industrials        2009 $23,553
Real Estate        2009 $12,180
Technology         2009 $18,427
Unknown            2009 $18,113
                                 pnl
sector                 year         
Basic Materials        2010  $17,552
Communication Services 2010   $3,173
Consumer Cyclical      2010   $6,981
Consumer Defensive     2010    $-914
Energy                 2010  $-6,559
Healthcare             2010   $4,656
Industrials            2010  $19,862
Technology             2010     $249
Unknown                2010 $-33,895
                                 pnl
sector                 year         
Basic Materials        2011   $6,065
Communication Services 2011    $-112
Consumer Cyclical      2011     $988
Consumer Defensive     2011 $-14,069
Energy                 2011   $1,253
Healthcare             2011   $8,885
Industrials            2011  $14,353
Technology             2011  $-1,659
Unknown                2011  $23,855
                                 pnl
sector                 year         
Basic Materials        2012  $13,707
Communication Services 2012    $-313
Consumer Cyclical      2012  $19,103
Consumer Defensive     2012   $7,628
Energy                 2012  $10,890
Healthcare             2012  $10,355
Industrials            2012   $8,289
Real Estate            2012   $1,453
Technology             2012   $7,591
Unknown                2012 $-20,176
                                pnl
sector                 year        
Basic Materials        2013  $1,524
Communication Services 2013   $-681
Consumer Cyclical      2013 $27,046
Consumer Defensive     2013  $1,434
Energy                 2013 $10,355
Financial Services     2013  $9,563
Healthcare             2013  $9,230
Industrials            2013 $16,632
Technology             2013 $42,158
Unknown                2013 $-5,436
                                 pnl
sector                 year         
Basic Materials        2014  $-8,889
Communication Services 2014   $7,189
Consumer Cyclical      2014 $-18,991
Consumer Defensive     2014  $22,060
Energy                 2014  $-8,223
Financial Services     2014   $5,484
Healthcare             2014     $134
Industrials            2014  $18,651
Real Estate            2014  $-4,452
Technology             2014  $22,216
Unknown                2014 $-29,991
                             pnl
sector             year         
Basic Materials    2015     $551
Consumer Cyclical  2015  $12,757
Consumer Defensive 2015  $18,358
Energy             2015 $-25,963
Financial Services 2015  $15,555
Healthcare         2015    $-211
Industrials        2015  $22,829
Real Estate        2015 $-12,341
Technology         2015  $12,315
Unknown            2015    $-724
Utilities          2015   $2,814
                                pnl
sector                 year        
Basic Materials        2016 $20,276
Communication Services 2016 $-4,319
Consumer Cyclical      2016 $23,266
Consumer Defensive     2016 $12,977
Energy                 2016 $14,495
Financial Services     2016 $-7,506
Healthcare             2016  $8,453
Industrials            2016  $8,428
Technology             2016  $1,394
Unknown                2016 $24,153


+++++ Profit $ per Sector Per Year +++++


Out[49]:
array([<matplotlib.axes._subplots.AxesSubplot object at 0x7fbbcb27bb50>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x7fbbc9799150>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x7fbbcb223550>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x7fbbca129c50>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x7fbbc91f5a10>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x7fbbcb55ef90>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x7fbbc30f28d0>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x7fbbbe038bd0>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x7fbbbd3af3d0>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x7fbbc1986b90>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x7fbbbc6dd7d0>,
       <matplotlib.axes._subplots.AxesSubplot object at 0x7fbbbe0db790>], dtype=object)
In [50]:
rrty = trades.ix[:, [6, 10]]
rrty = rrty.set_index('symbol')
rrty = rrty.groupby(level=0).last()
rrty1 = pd.Series(rrty.ix[:,0])


pf.create_round_trip_tear_sheet(returns, positions = positions, transactions = bt.transactions, sector_mappings = rrty1) 
Summary stats All trades Short trades Long trades
Total number of round_trips 3157.00 277.00 2880.00
Percent profitable 0.62 0.39 0.65
Winning round_trips 1969.00 108.00 1861.00
Losing round_trips 1188.00 169.00 1019.00
Even round_trips 0.00 0.00 0.00
PnL stats All trades Short trades Long trades
Total profit $639769.75 $-115974.18 $755743.92
Gross profit $1788339.00 $117771.72 $1670567.28
Gross loss $-1148569.25 $-233745.90 $-914823.35
Profit factor $1.56 $0.50 $1.83
Avg. trade net profit $202.65 $-418.68 $262.41
Avg. winning trade $908.25 $1090.48 $897.67
Avg. losing trade $-966.81 $-1383.11 $-897.77
Ratio Avg. Win:Avg. Loss $0.94 $0.79 $1.00
Largest winning trade $21390.30 $13303.58 $21390.30
Largest losing trade $-21290.59 $-21290.59 $-11813.16
Duration stats All trades Short trades Long trades
Avg duration 75 days 21:24:00.228064 196 days 21:24:02.599277 64 days 06:05:30
Median duration 40 days 00:00:00 126 days 00:00:00 34 days 00:00:00
Avg # round_trips per day 1.14 0.10 5.54
Avg # round_trips per month 23.92 2.10 116.31
Return stats All trades Short trades Long trades
Avg returns all round_trips 0.06% -0.08% 0.07%
Avg returns winning 0.25% 0.37% 0.25%
Avg returns losing -0.26% -0.36% -0.24%
Median returns all round_trips 0.01% -0.02% 0.01%
Median returns winning 0.08% 0.16% 0.07%
Median returns losing -0.08% -0.14% -0.06%
Largest winning trade 4.39% 4.39% 3.96%
Largest losing trade -5.47% -5.47% -2.05%
Symbol stats AAN ABM ACIW ADUS ADY AE AEIS AEO AET AFG ... WSTC WSTL WTI X XEC XLS XRTX YRCW ZEUS ZINC
Avg returns all round_trips 0.04% 0.38% 0.07% 0.79% 0.06% 0.05% -0.04% 0.09% -0.04% 0.11% ... 0.01% -0.18% -0.34% 0.05% -0.02% 0.20% -0.64% 0.06% 0.38% 0.48%
Avg returns winning 0.04% 0.38% 0.49% 0.79% 0.13% 0.09% 0.22% 0.24% 0.00% 0.11% ... 0.08% nan% 0.08% 0.30% 0.02% 0.20% 0.05% 0.14% 0.64% 0.48%
Avg returns losing nan% nan% -0.55% nan% -0.10% -0.01% -0.55% -0.75% -0.09% nan% ... -0.09% -0.18% -1.16% -0.24% -0.08% nan% -1.34% -0.16% -0.96% nan%
Median returns all round_trips 0.04% 0.05% 0.00% 0.45% 0.01% 0.00% 0.20% 0.04% -0.04% 0.01% ... 0.00% -0.18% 0.01% 0.03% 0.00% 0.20% -0.64% 0.02% 0.23% 0.55%
Median returns winning 0.04% 0.05% 0.00% 0.45% 0.09% 0.03% 0.22% 0.07% 0.00% 0.01% ... 0.01% nan% 0.07% 0.16% 0.00% 0.20% 0.05% 0.03% 0.30% 0.55%
Median returns losing nan% nan% -0.55% nan% -0.10% -0.00% -0.55% -0.75% -0.09% nan% ... -0.00% -0.18% -1.16% -0.08% -0.08% nan% -1.34% -0.16% -0.96% nan%
Largest winning trade 0.04% 1.08% 1.46% 1.67% 0.44% 0.44% 0.24% 0.83% 0.00% 0.31% ... 0.32% -0.09% 0.16% 1.04% 0.05% 0.37% 0.05% 0.38% 1.83% 0.88%
Largest losing trade 0.04% 0.02% -0.98% 0.25% -0.14% -0.02% -0.55% -1.00% -0.09% 0.00% ... -0.28% -0.27% -1.20% -1.10% -0.16% 0.03% -1.34% -0.16% -0.96% 0.01%

8 rows × 465 columns

Profitability (PnL / PnL total) per name pnl
symbol
NHTC 0.05%
VSEC 0.04%
DDS 0.03%
TA 0.03%
MRC 0.03%
NCIT 0.03%
PPC 0.03%
BAMM 0.03%
FSLR 0.03%
HMA 0.02%
RS 0.02%
VA 0.02%
CMC 0.02%
KLIC 0.02%
REX 0.02%
WDC 0.02%
RIGP 0.02%
GTLS 0.02%
SYX 0.02%
ADUS 0.02%
LCUT 0.02%
TSO 0.02%
SHO 0.02%
EME 0.02%
PRIM 0.02%
IIIN 0.02%
CRNT 0.02%
CRD_A 0.02%
ALG 0.02%
SANM 0.02%
... ...
IVAC -0.01%
TGNA -0.01%
WTI -0.01%
NR -0.01%
XRTX -0.01%
FU -0.01%
DEPO -0.01%
FET -0.01%
CSH -0.01%
SHLO -0.01%
GIFI -0.01%
GPRE -0.01%
ARC -0.01%
CPA -0.01%
MITT -0.01%
IPSU -0.01%
URS -0.01%
HGG -0.01%
BELF_B -0.01%
GSVC -0.01%
GME -0.01%
CITP -0.01%
CALL -0.02%
OME -0.02%
SPN -0.02%
NE -0.02%
SB -0.02%
HOS -0.02%
PEIX -0.02%
IWM -0.18%

465 rows × 1 columns

Profitability (PnL / PnL total) per name pnl
symbol
Industrials 0.25%
Basic Materials 0.18%
Consumer Cyclical 0.18%
Technology 0.17%
Consumer Defensive 0.08%
Healthcare 0.06%
Energy 0.04%
Financial Services 0.04%
Communication Services 0.01%
Utilities 0.00%
Real Estate -0.00%
Unknown -0.01%
<matplotlib.figure.Figure at 0x7fbbc2291e50>
In [51]:
#FOR LATEr AGGREGATION AT SOME POINT ... MAYBE AVG PER MONTH EXPOSURE ETC. ... LEAVING IN FOR NOW

rrty = trades.ix[:, [9, 10]]
rrty = rrty.set_index('SID')
rrty = rrty.groupby(level=0).last()
rrty1 = pd.Series(rrty.ix[:,0])

drt = pf.capacity.pos.get_sector_exposures(positions = positions,symbol_sector_map = dict(rrty1))
drt
/usr/local/lib/python2.7/dist-packages/pyfolio/pos.py:202: UserWarning: Warning: Symbols 209, 377, 384, 4373, 7211, 15024, 19922, 20662, 21809, 24851, 29470 have no sector mapping.
        They will not be included in sector allocations
  warnings.warn(warn_message, UserWarning)
Out[51]:
Basic Materials Communication Services Consumer Cyclical Consumer Defensive Energy Financial Services Healthcare Industrials Real Estate Technology Unknown Utilities cash
index
2003-01-02 00:00:00+00:00 $8,457 $ 0 $8,528 $3,985 $ 0 $ 0 $4,077 $8,763 $ 0 $12,678 $-17,244 $ 0 $71,443
2003-01-03 00:00:00+00:00 $8,380 $ 0 $8,505 $4,109 $ 0 $ 0 $4,151 $8,493 $ 0 $12,832 $-17,363 $ 0 $71,332
2003-01-06 00:00:00+00:00 $8,087 $ 0 $8,603 $4,013 $ 0 $ 0 $4,149 $8,452 $ 0 $13,084 $-17,935 $ 0 $71,332
2003-01-07 00:00:00+00:00 $8,272 $ 0 $8,681 $3,826 $ 0 $ 0 $3,985 $8,316 $ 0 $13,080 $-17,705 $ 0 $71,332
2003-01-08 00:00:00+00:00 $8,130 $ 0 $8,799 $3,978 $ 0 $ 0 $3,962 $8,260 $ 0 $13,018 $-17,306 $ 0 $71,332
2003-01-09 00:00:00+00:00 $8,295 $ 0 $8,767 $3,920 $ 0 $ 0 $4,077 $8,321 $ 0 $13,523 $-17,710 $ 0 $71,332
2003-01-10 00:00:00+00:00 $8,449 $ 0 $9,121 $3,804 $ 0 $ 0 $4,077 $8,279 $ 0 $13,680 $-17,958 $ 0 $71,332
2003-01-13 00:00:00+00:00 $8,312 $ 0 $8,917 $3,743 $ 0 $ 0 $4,070 $8,181 $ 0 $13,754 $-17,820 $ 0 $71,332
2003-01-14 00:00:00+00:00 $8,518 $ 0 $9,075 $3,952 $ 0 $ 0 $4,100 $8,142 $ 0 $13,413 $-18,051 $ 0 $71,332
2003-01-15 00:00:00+00:00 $8,402 $ 0 $9,088 $4,042 $ 0 $ 0 $4,128 $8,142 $ 0 $13,053 $-17,745 $ 0 $71,332
2003-01-16 00:00:00+00:00 $8,594 $ 0 $9,068 $4,362 $ 0 $ 0 $4,077 $8,229 $ 0 $13,246 $-17,664 $ 0 $71,332
2003-01-17 00:00:00+00:00 $8,435 $ 0 $8,854 $4,410 $ 0 $ 0 $4,070 $8,075 $ 0 $13,096 $-17,096 $ 0 $71,332
2003-01-21 00:00:00+00:00 $8,442 $ 0 $8,793 $4,253 $ 0 $ 0 $3,973 $7,907 $ 0 $13,329 $-17,048 $ 0 $71,332
2003-01-22 00:00:00+00:00 $8,259 $ 0 $8,247 $4,071 $ 0 $ 0 $4,163 $8,139 $ 0 $12,558 $-16,846 $ 0 $72,369
2003-01-23 00:00:00+00:00 $8,357 $ 0 $8,365 $4,108 $ 0 $ 0 $4,224 $8,252 $ 0 $12,889 $-16,834 $ 0 $72,369
2003-01-24 00:00:00+00:00 $8,217 $ 0 $8,141 $4,065 $ 0 $ 0 $4,175 $8,125 $ 0 $12,726 $-16,337 $ 0 $72,369
2003-01-27 00:00:00+00:00 $8,050 $ 0 $7,828 $3,936 $ 0 $ 0 $4,120 $7,964 $ 0 $12,499 $-15,844 $ 0 $72,369
2003-01-28 00:00:00+00:00 $8,118 $ 0 $7,861 $3,889 $ 0 $ 0 $4,246 $7,956 $ 0 $12,691 $-16,096 $ 0 $72,369
2003-01-29 00:00:00+00:00 $8,166 $ 0 $7,770 $3,901 $ 0 $ 0 $4,213 $7,952 $ 0 $13,490 $-15,985 $ 0 $72,369
2003-01-30 00:00:00+00:00 $8,044 $ 0 $7,535 $3,969 $ 0 $ 0 $4,151 $7,856 $ 0 $13,142 $-15,596 $ 0 $72,369
2003-01-31 00:00:00+00:00 $8,097 $ 0 $7,701 $4,244 $ 0 $ 0 $4,184 $8,113 $ 0 $13,257 $-15,445 $ 0 $72,369
2003-02-03 00:00:00+00:00 $8,065 $ 0 $7,701 $4,162 $ 0 $ 0 $4,141 $8,118 $ 0 $13,635 $-15,810 $ 0 $72,369
2003-02-04 00:00:00+00:00 $7,851 $ 0 $7,682 $4,133 $ 0 $ 0 $4,165 $8,097 $ 0 $13,682 $-15,526 $ 0 $72,369
2003-02-05 00:00:00+00:00 $7,939 $ 0 $7,693 $4,215 $ 0 $ 0 $4,141 $8,185 $ 0 $13,322 $-15,491 $ 0 $72,369
2003-02-06 00:00:00+00:00 $7,878 $ 0 $7,491 $4,215 $ 0 $ 0 $4,141 $7,945 $ 0 $13,063 $-15,266 $ 0 $72,369
2003-02-07 00:00:00+00:00 $7,775 $ 0 $7,446 $4,250 $ 0 $ 0 $4,296 $7,796 $ 0 $12,789 $-15,180 $ 0 $72,369
2003-02-10 00:00:00+00:00 $7,904 $ 0 $3,874 $4,358 $ 0 $ 0 $4,277 $7,873 $ 0 $13,257 $-17,676 $ 0 $77,748
2003-02-11 00:00:00+00:00 $7,937 $ 0 $3,860 $4,086 $ 0 $ 0 $4,344 $7,927 $ 0 $9,367 $-15,191 $ 0 $78,707
2003-02-12 00:00:00+00:00 $7,864 $ 0 $3,855 $3,954 $ 0 $ 0 $4,248 $7,898 $ 0 $9,263 $-14,882 $ 0 $78,707
2003-02-13 00:00:00+00:00 $7,825 $ 0 $3,791 $3,969 $ 0 $ 0 $4,260 $7,946 $ 0 $8,980 $-14,877 $ 0 $78,707
... ... ... ... ... ... ... ... ... ... ... ... ... ...
2016-11-11 00:00:00+00:00 $34,366 $30,744 $33,212 $26,596 $26,789 $ 0 $60,254 $87,862 $ 0 $60,440 $-193,897 $ 0 $515,769
2016-11-14 00:00:00+00:00 $35,854 $30,834 $33,189 $26,694 $28,084 $ 0 $62,585 $89,544 $ 0 $62,460 $-196,999 $ 0 $515,769
2016-11-15 00:00:00+00:00 $35,573 $31,231 $32,005 $26,450 $29,172 $ 0 $62,443 $88,704 $ 0 $61,616 $-197,737 $ 0 $515,769
2016-11-16 00:00:00+00:00 $35,407 $31,833 $32,462 $26,694 $28,328 $ 0 $57,298 $87,919 $ 0 $62,628 $-197,985 $ 0 $515,769
2016-11-17 00:00:00+00:00 $35,382 $32,051 $32,858 $26,645 $28,440 $ 0 $56,465 $87,926 $ 0 $61,838 $-199,274 $ 0 $515,769
2016-11-18 00:00:00+00:00 $ 0 $28,125 $2,482 $27,773 $55,863 $28,021 $28,256 $125,313 $28,042 $50,937 $-172,002 $ 0 $475,967
2016-11-21 00:00:00+00:00 $ 0 $28,249 $6,924 $27,875 $57,723 $27,810 $28,420 $112,574 $28,566 $28,032 $-170,799 $ 0 $506,633
2016-11-22 00:00:00+00:00 $ 0 $28,485 $19,069 $27,913 $58,270 $28,069 $28,831 $114,820 $28,926 $28,387 $-171,206 $ 0 $494,541
2016-11-23 00:00:00+00:00 $ 0 $28,260 $28,415 $27,926 $59,553 $28,197 $28,667 $116,734 $28,533 $28,893 $-171,845 $ 0 $485,436
2016-11-25 00:00:00+00:00 $ 0 $28,462 $28,573 $27,798 $58,086 $28,400 $28,749 $116,207 $28,778 $28,994 $-172,988 $ 0 $485,436
2016-11-28 00:00:00+00:00 $ 0 $28,001 $28,448 $27,977 $57,606 $28,290 $28,462 $114,436 $29,106 $29,044 $-170,737 $ 0 $485,436
2016-11-29 00:00:00+00:00 $ 0 $27,878 $30,221 $28,182 $54,843 $28,445 $29,078 $115,052 $29,810 $29,196 $-171,606 $ 0 $485,436
2016-11-30 00:00:00+00:00 $ 0 $26,842 $27,959 $28,131 $64,710 $28,359 $28,092 $115,946 $28,811 $28,741 $-165,570 $ 0 $485,436
2016-12-01 00:00:00+00:00 $ 0 $26,989 $29,116 $28,041 $68,350 $28,583 $27,205 $117,062 $28,713 $28,083 $-162,966 $ 0 $485,595
2016-12-02 00:00:00+00:00 $ 0 $26,302 $28,529 $28,514 $65,921 $28,390 $27,435 $117,979 $28,500 $28,640 $-162,992 $ 0 $485,595
2016-12-05 00:00:00+00:00 $ 0 $27,022 $27,485 $28,718 $68,804 $28,621 $29,776 $119,037 $29,220 $29,399 $-165,369 $ 0 $485,595
2016-12-06 00:00:00+00:00 $ 0 $27,191 $28,853 $29,638 $72,644 $28,990 $29,119 $121,973 $29,728 $29,449 $-166,810 $ 0 $485,595
2016-12-07 00:00:00+00:00 $ 0 $27,428 $29,643 $30,379 $73,186 $29,242 $29,858 $125,594 $30,268 $29,550 $-168,118 $ 0 $485,940
2016-12-08 00:00:00+00:00 $ 0 $28,339 $30,116 $30,941 $75,910 $29,453 $30,724 $128,298 $30,235 $29,905 $-171,064 $ 0 $485,940
2016-12-09 00:00:00+00:00 $ 0 $28,406 $29,871 $31,094 $83,368 $29,667 $28,995 $128,090 $30,841 $29,905 $-171,427 $ 0 $485,940
2016-12-12 00:00:00+00:00 $ 0 $28,260 $29,923 $30,967 $83,590 $29,680 $28,708 $127,546 $30,104 $29,955 $-168,973 $ 0 $485,940
2016-12-13 00:00:00+00:00 $ 0 $28,204 $29,879 $31,375 $84,885 $29,667 $28,297 $126,191 $29,032 $29,803 $-169,003 $ 0 $485,940
2016-12-14 00:00:00+00:00 $ 0 $27,765 $29,660 $31,018 $81,097 $29,529 $28,010 $125,337 $29,089 $29,753 $-205,302 $ 0 $525,229
2016-12-15 00:00:00+00:00 $ 0 $28,148 $30,362 $31,503 $82,708 $29,549 $28,379 $127,633 $27,125 $30,259 $-207,116 $ 0 $525,229
2016-12-16 00:00:00+00:00 $ 0 $27,922 $30,800 $32,040 $82,056 $29,818 $28,010 $127,037 $27,796 $30,056 $-206,598 $ 0 $525,229
2016-12-19 00:00:00+00:00 $ 0 $28,508 $30,800 $32,448 $83,006 $30,041 $27,887 $128,756 $28,140 $30,360 $-208,136 $ 0 $525,229
2016-12-20 00:00:00+00:00 $ 0 $30,516 $60,470 $30,472 $30,739 $1,853 $20,303 $158,559 $30,546 $30,733 $-246,343 $ 0 $575,869
2016-12-21 00:00:00+00:00 $ 0 $30,780 $59,966 $30,042 $29,892 $6,748 $3,705 $180,068 $30,457 $30,934 $-244,771 $ 0 $550,734
2016-12-22 00:00:00+00:00 $ 0 $30,660 $57,731 $29,827 $29,257 $8,669 $ 0 $177,462 $30,333 $30,784 $-241,664 $ 0 $552,516
2016-12-23 00:00:00+00:00 $ 0 $30,468 $57,875 $30,078 $29,257 $12,133 $ 0 $177,244 $30,359 $30,934 $-242,985 $ 0 $549,250

3521 rows × 13 columns

In [52]:
drt.groupby([drt.index.year]).last()
Out[52]:
Basic Materials Communication Services Consumer Cyclical Consumer Defensive Energy Financial Services Healthcare Industrials Real Estate Technology Unknown Utilities cash
2003 $10,809 $5,330 $20,735 $10,637 $5,824 $ 0 $ 0 $16,477 $ 0 $ 0 $-32,906 $ 0 $91,126
2004 $20,801 $ 0 $21,022 $ 0 $20,091 $ 0 $6,504 $21,470 $6,767 $ 0 $-48,008 $ 0 $117,359
2005 $31,203 $ 0 $23,423 $7,559 $15,834 $ 0 $ 0 $15,141 $ 0 $ 0 $-45,385 $ 0 $139,075
2006 $ 0 $21,344 $20,820 $21,959 $20,321 $11,758 $ 0 $30,824 $ 0 $10,351 $-73,905 $ 0 $179,281
2007 $32,445 $10,846 $20,827 $32,565 $10,875 $ 0 $11,245 $12,942 $ 0 $21,568 $-104,597 $ 0 $210,447
2008 $30,941 $ 0 $14,342 $ 0 $35,268 $ 0 $ 0 $21,128 $ 0 $ 0 $-48,291 $ 0 $218,174
2009 $61,184 $ 0 $ 0 $32,238 $14,889 $ 0 $ 0 $46,604 $ 0 $15,538 $-60,751 $ 0 $265,546
2010 $30,913 $15,549 $30,249 $ 0 $15,479 $ 0 $31,146 $32,309 $ 0 $16,453 $-78,503 $ 0 $276,116
2011 $18,412 $16,963 $53,993 $ 0 $36,255 $ 0 $35,350 $17,067 $ 0 $34,414 $-106,119 $ 0 $317,334
2012 $ 0 $18,139 $55,830 $ 0 $ 0 $ 0 $18,955 $77,817 $ 0 $58,312 $-97,819 $ 0 $338,886
2013 $ 0 $23,919 $90,470 $47,117 $23,873 $ 0 $ 0 $71,537 $ 0 $45,900 $-127,691 $ 0 $384,359
2014 $23,669 $ 0 $25,190 $23,169 $43,978 $23,981 $ 0 $96,813 $22,281 $23,341 $-119,539 $ 0 $404,041
2015 $25,774 $ 0 $24,908 $25,812 $52,948 $81,980 $ 0 $102,170 $ 0 $25,364 $-196,258 $ 0 $478,959
2016 $ 0 $30,468 $57,875 $30,078 $29,257 $12,133 $ 0 $177,244 $30,359 $30,934 $-242,985 $ 0 $549,250
In [53]:
drt.mean()
Out[53]:
Basic Materials           $21,791
Communication Services     $8,161
Consumer Cyclical         $29,196
Consumer Defensive        $13,645
Energy                    $17,833
Financial Services        $12,661
Healthcare                 $8,085
Industrials               $38,718
Real Estate                $3,318
Technology                $26,555
Unknown                  $-90,609
Utilities                    $453
cash                     $274,543
dtype: float64
In [54]:
drt.groupby([drt.index.year]).mean()
Out[54]:
Basic Materials Communication Services Consumer Cyclical Consumer Defensive Energy Financial Services Healthcare Industrials Real Estate Technology Unknown Utilities cash
2003 $4,840 $2,528 $14,102 $4,490 $2,694 $830 $2,434 $10,829 $820 $9,765 $-25,242 $ 0 $79,200
2004 $18,966 $311 $21,856 $9,483 $4,393 $ 0 $1,084 $16,918 $790 $ 0 $-37,166 $ 0 $106,405
2005 $21,898 $1,198 $17,766 $2,643 $17,353 $3,906 $6,349 $19,031 $304 $1,850 $-44,942 $ 0 $127,740
2006 $26,334 $7,371 $19,513 $7,415 $15,632 $1,552 $619 $31,800 $ 0 $7,219 $-61,738 $ 0 $165,907
2007 $12,108 $9,854 $37,714 $9,030 $14,085 $675 $1,280 $12,513 $ 0 $31,131 $-64,594 $ 0 $197,900
2008 $15,433 $1,302 $31,004 $9,474 $10,639 $ 0 $8,349 $21,558 $ 0 $13,869 $-68,923 $ 0 $227,095
2009 $36,638 $ 0 $12,379 $3,867 $22,605 $1,638 $6,238 $47,084 $2,270 $24,153 $-77,466 $ 0 $250,007
2010 $59,128 $13,088 $44,728 $16,495 $4,451 $ 0 $5,123 $28,855 $ 0 $4,941 $-98,820 $ 0 $297,423
2011 $23,855 $10,381 $23,506 $12,457 $5,157 $ 0 $27,079 $30,627 $ 0 $32,057 $-65,711 $ 0 $315,157
2012 $19,422 $7,245 $56,823 $11,090 $15,831 $ 0 $14,667 $33,900 $1,567 $44,762 $-98,745 $ 0 $337,966
2013 $5,168 $16,732 $47,409 $19,757 $28,532 $17,326 $17,025 $59,250 $ 0 $70,960 $-141,406 $ 0 $389,487
2014 $3,416 $26,987 $26,111 $31,084 $8,761 $26,591 $2,064 $93,936 $8,696 $68,204 $-132,948 $ 0 $414,085
2015 $24,470 $ 0 $18,512 $25,781 $49,307 $76,490 $4,414 $59,516 $29,021 $24,447 $-169,088 $6,324 $452,017
2016 $33,576 $17,429 $37,660 $28,155 $50,734 $48,684 $16,590 $76,739 $2,948 $38,738 $-183,139 $ 0 $486,539
In [55]:
drt.last('1D')
Out[55]:
Basic Materials Communication Services Consumer Cyclical Consumer Defensive Energy Financial Services Healthcare Industrials Real Estate Technology Unknown Utilities cash
index
2016-12-23 00:00:00+00:00 $ 0 $30,468 $57,875 $30,078 $29,257 $12,133 $ 0 $177,244 $30,359 $30,934 $-242,985 $ 0 $549,250
In [56]:
bt.positions.last('1D')
Out[56]:
amount cost_basis last_sale_price sid
2016-12-23 00:00:00+00:00 903 $29 $32 523
2016-12-23 00:00:00+00:00 1006 $19 $31 856
2016-12-23 00:00:00+00:00 562 $54 $50 4313
2016-12-23 00:00:00+00:00 1512 $15 $19 6392
2016-12-23 00:00:00+00:00 478 $60 $63 7885
2016-12-23 00:00:00+00:00 1023 $12 $12 10615
2016-12-23 00:00:00+00:00 697 $32 $43 11645
2016-12-23 00:00:00+00:00 1721 $16 $17 16341
2016-12-23 00:00:00+00:00 1888 $16 $16 19922
2016-12-23 00:00:00+00:00 -1786 $114 $136 21519
2016-12-23 00:00:00+00:00 2583 $12 $12 27529
2016-12-23 00:00:00+00:00 406 $75 $72 28450
2016-12-23 00:00:00+00:00 782 $34 $37 32433
2016-12-23 00:00:00+00:00 1778 $17 $17 41669
2016-12-23 00:00:00+00:00 1200 $22 $25 44346
2016-12-23 00:00:00+00:00 1213 $25 $24 47708
In [57]:
trades[trades.SID ==19922]
Out[57]:
close_dt invested long open_dt pnl rt_returns symbol duration returns SID sector profitable
In [ ]:
 
In [ ]:
 
In [ ]:
 
In [58]:
pd.options.display.float_format = '{:20,.2f}'.format
iuh= get_pricing(symbols = trades.symbol.unique(), start_date=bt.start_date.date(), end_date=bt.end_date.date(), symbol_reference_date=None, 
                 frequency='daily', fields={"price", "volume"}, handle_missing='ignore')

pf.create_capacity_tear_sheet(returns, positions = positions, transactions = bt.transactions,
                               market_data = iuh,
                               liquidation_daily_vol_limit=0.2,
                               trade_daily_vol_limit=0.05,
                               last_n_days=pf.APPROX_BDAYS_PER_MONTH * 6,
                               days_to_liquidate_limit=1)
Max days to liquidation is computed for each traded name assuming a 20% limit on daily bar consumption 
and trailing 5 day mean volume as the available bar volume.

Tickers with >1 day liquidation time at a constant $1m capital base:
Whole backtest:
date days_to_liquidate pos_alloc_pct
symbol
153 2007-02-13 00:00:00+00:00 4.92 4.27
504 2004-09-16 00:00:00+00:00 4.71 4.01
610 2003-10-17 00:00:00+00:00 9.76 4.63
680 2004-08-25 00:00:00+00:00 38.53 4.23
717 2005-10-24 00:00:00+00:00 4.81 4.25
924 2004-08-24 00:00:00+00:00 2.91 4.22
1842 2007-01-18 00:00:00+00:00 12.01 4.12
1872 2013-07-11 00:00:00+00:00 1.11 4.27
1926 2003-04-25 00:00:00+00:00 4.40 4.17
1984 2004-04-14 00:00:00+00:00 1.79 4.67
2114 2003-09-09 00:00:00+00:00 2.63 4.39
2292 2005-10-11 00:00:00+00:00 3.32 3.94
2295 2003-10-24 00:00:00+00:00 1.45 4.07
2605 2007-12-26 00:00:00+00:00 8.42 4.11
3402 2003-12-23 00:00:00+00:00 1.63 4.22
3468 2004-03-01 00:00:00+00:00 25.57 4.05
3849 2005-12-27 00:00:00+00:00 1.24 4.08
4029 2013-01-18 00:00:00+00:00 1.45 4.38
4112 2010-02-25 00:00:00+00:00 1.97 4.11
4392 2013-02-28 00:00:00+00:00 6.20 3.22
4402 2006-10-27 00:00:00+00:00 1.78 4.09
5107 2003-03-13 00:00:00+00:00 1.26 4.38
5219 2003-05-23 00:00:00+00:00 1.70 4.01
5299 2003-01-16 00:00:00+00:00 7.91 4.03
5631 2004-08-19 00:00:00+00:00 14.14 3.88
5824 2003-05-22 00:00:00+00:00 2.32 4.27
6267 2003-04-03 00:00:00+00:00 1.35 4.09
6436 2016-05-23 00:00:00+00:00 3.26 6.40
6447 2012-09-04 00:00:00+00:00 1.83 4.17
6579 2003-04-17 00:00:00+00:00 1.18 4.14
... ... ... ...
18417 2004-02-11 00:00:00+00:00 1.61 4.05
18427 2006-12-05 00:00:00+00:00 3.80 3.96
18656 2012-12-10 00:00:00+00:00 1.69 4.72
18759 2003-11-19 00:00:00+00:00 1.24 4.04
18764 2007-02-14 00:00:00+00:00 1.69 3.96
19043 2007-02-09 00:00:00+00:00 11.20 4.07
19044 2016-03-03 00:00:00+00:00 1.10 4.38
21114 2016-09-01 00:00:00+00:00 15.35 1.45
21398 2012-11-29 00:00:00+00:00 4.06 4.32
21610 2005-06-30 00:00:00+00:00 4.89 3.43
21993 2016-06-24 00:00:00+00:00 1.08 3.93
22166 2003-06-24 00:00:00+00:00 4.67 3.99
24721 2004-08-26 00:00:00+00:00 2.41 4.13
24800 2003-12-04 00:00:00+00:00 1.18 4.24
25519 2012-08-29 00:00:00+00:00 1.16 4.08
26146 2012-08-15 00:00:00+00:00 1.20 4.10
26236 2011-01-31 00:00:00+00:00 2.82 4.09
26327 2009-12-29 00:00:00+00:00 2.00 4.97
26678 2008-10-31 00:00:00+00:00 1.60 4.45
27193 2007-07-18 00:00:00+00:00 3.33 4.08
27640 2015-07-14 00:00:00+00:00 3.87 4.02
27721 2014-07-31 00:00:00+00:00 4.38 4.16
30759 2008-06-25 00:00:00+00:00 1.13 4.07
31103 2015-07-09 00:00:00+00:00 2.43 4.19
32784 2009-06-23 00:00:00+00:00 4.18 5.08
34246 2013-02-13 00:00:00+00:00 1.20 4.27
34530 2015-11-30 00:00:00+00:00 12.07 0.93
35846 2012-10-16 00:00:00+00:00 10.38 1.63
36749 2015-05-08 00:00:00+00:00 1.14 4.05
47372 2015-09-11 00:00:00+00:00 1.00 3.95

87 rows × 3 columns

Last 126 trading days:
date days_to_liquidate pos_alloc_pct
symbol
21114 2016-09-01 00:00:00+00:00 15.35 1.45
Tickers with daily transactions consuming >5.0% of daily bar 
all backtest:
date max_pct_bar_consumed
symbol
last 126 trading days:
date max_pct_bar_consumed
symbol
In [59]:
import datetime
bt_days = (bt.end_date.date() - bt.start_date.date()).days
if bt_days > 500:
    live_start_date= bt.end_date.date() - datetime.timedelta(days=bt_days*0.25)
else:
    live_start_date= bt.end_date.date() - datetime.timedelta(days=bt_days / 2)

 
pf.create_full_tear_sheet(returns=returns, positions = positions, transactions = bt.transactions,
                          market_data = iuh, round_trips=False, bayesian=True, gross_lev=gross_lev, live_start_date=live_start_date, 
                          slippage=0, sector_mappings=rrty1)
Entire data start date: 2003-01-02
Entire data end date: 2016-12-23


Out-of-Sample Months: 42
Backtest Months: 125
Performance statistics All history Backtest Out of sample
annual_return 0.15 0.18 0.09
annual_volatility 0.10 0.10 0.08
sharpe_ratio 1.53 1.67 1.05
calmar_ratio 0.87 1.00 0.94
stability_of_timeseries 0.96 0.96 0.81
max_drawdown -0.18 -0.18 -0.09
omega_ratio 1.31 1.34 1.20
sortino_ratio 2.34 2.55 1.60
skew 0.05 0.03 0.12
kurtosis 4.78 4.93 2.93
tail_ratio 1.16 1.15 1.08
common_sense_ratio 1.34 1.35 1.18
information_ratio 0.02 0.03 -0.01
alpha 0.13 0.15 0.06
beta 0.26 0.26 0.25
Worst Drawdown Periods net drawdown in % peak date valley date recovery date duration
0 17.57 2008-09-12 2008-11-20 2009-04-16 155
1 13.59 2007-07-13 2007-11-26 2008-05-13 218
2 10.73 2011-07-07 2011-10-03 2012-01-25 145
3 10.58 2010-04-29 2010-08-30 2011-04-26 259
4 9.47 2014-11-06 2015-01-15 2015-08-10 198

[-0.012 -0.024]
/usr/local/lib/python2.7/dist-packages/numpy/lib/function_base.py:3834: RuntimeWarning: Invalid value encountered in percentile
  RuntimeWarning)
Stress Events mean min max
Lehmann -0.01% -1.61% 1.90%
US downgrade/European Debt Crisis 0.01% -1.11% 1.70%
Fukushima 0.15% -0.51% 1.14%
US Housing 0.08% -0.55% 1.04%
EZB IR Event 0.04% -0.91% 1.11%
Aug07 -0.19% -1.81% 0.88%
Mar08 0.23% -1.16% 3.48%
Sept08 -0.13% -1.61% 1.02%
2009Q1 -0.08% -3.11% 2.80%
2009Q2 0.46% -4.13% 3.66%
Flash Crash -0.05% -0.90% 1.40%
Apr14 0.12% -0.67% 0.68%
Oct14 0.18% -0.49% 1.62%
Fall2015 0.03% -0.93% 2.02%
Low Volatility Bull Market 0.08% -2.03% 1.81%
GFC Crash 0.01% -3.23% 3.86%
Recovery 0.06% -4.13% 3.66%
New Normal 0.05% -2.85% 2.55%
Top 10 long positions of all time max
sid
26728 7.31%
32433 7.25%
48091 7.23%
26497 7.06%
15230 6.79%
12798 6.74%
18595 6.58%
6436 6.57%
15365 6.54%
624 6.50%
Top 10 short positions of all time max
sid
21519 -83.50%
26678 -2.45%
9268 -1.17%
8023 -0.62%
Top 10 positions of all time max
sid
21519 83.50%
26728 7.31%
32433 7.25%
48091 7.23%
26497 7.06%
15230 6.79%
12798 6.74%
18595 6.58%
6436 6.57%
15365 6.54%
All positions ever held max
sid
21519 83.50%
26728 7.31%
32433 7.25%
48091 7.23%
26497 7.06%
15230 6.79%
12798 6.74%
18595 6.58%
6436 6.57%
15365 6.54%
624 6.50%
42786 6.49%
3596 6.48%
8023 6.27%
20877 6.19%
717 6.15%
14028 6.11%
2671 6.05%
27998 6.03%
3849 6.01%
39796 6.00%
20153 5.99%
8370 5.91%
5907 5.91%
32902 5.87%
5824 5.86%
7844 5.85%
10009 5.84%
6267 5.83%
15024 5.77%
... ...
300 4.19%
11120 4.18%
1195 4.18%
9094 4.18%
34277 4.18%
27497 4.17%
27908 4.17%
31271 4.17%
7277 4.17%
5631 4.17%
6949 4.17%
5181 4.16%
13154 4.15%
22231 4.15%
49203 4.14%
14192 4.14%
6453 4.14%
14617 4.13%
27193 4.13%
13906 4.12%
1833 4.12%
19922 4.11%
23178 4.11%
33027 4.10%
13450 4.10%
12350 4.09%
11258 4.09%
18764 4.07%
32877 4.02%
21114 1.63%

467 rows × 1 columns

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-59-5d6daca8688c> in <module>()
      9 pf.create_full_tear_sheet(returns=returns, positions = positions, transactions = bt.transactions,
     10                           market_data = iuh, round_trips=False, bayesian=True, gross_lev=gross_lev, live_start_date=live_start_date,
---> 11                           slippage=0, sector_mappings=rrty1)

/usr/local/lib/python2.7/dist-packages/pyfolio/tears.pyc in create_full_tear_sheet(returns, positions, transactions, market_data, benchmark_rets, gross_lev, slippage, live_start_date, sector_mappings, bayesian, round_trips, hide_positions, cone_std, bootstrap, unadjusted_returns, set_context)
    194                 create_capacity_tear_sheet(returns, positions, transactions,
    195                                            market_data, daily_vol_limit=0.2,
--> 196                                            last_n_days=125)
    197 
    198     if bayesian:

/usr/local/lib/python2.7/dist-packages/pyfolio/plotting.pyc in call_w_context(*args, **kwargs)
     50         if set_context:
     51             with context():
---> 52                 return func(*args, **kwargs)
     53         else:
     54             return func(*args, **kwargs)

TypeError: create_capacity_tear_sheet() got an unexpected keyword argument 'daily_vol_limit'

================================================================

In [ ]:
 
In [ ]:
 
In [ ]:
 
In [ ]: