def main(): for br, p in zip(BENCH_R, PORTFOLIOS): print(p) df = pd.DataFrame() # 计算组合分年收益率与最大回撤 returns = get_portfolio_return(p) returns_year = returns.resample('Y').apply( lambda x: empyrical.cum_returns_final(x)) mdd_year = returns.resample('Y').apply( lambda x: empyrical.max_drawdown(x)) # 扣除申购赎回费 filename = '%s/%s.xlsx' % (DATA_DIR, p) cost = pd.read_excel(filename, sheet_name='费率', index_col=0) df['组合收益率'] = returns_year - cost['申购费'] - cost['赎回费'] df['组合最大回撤'] = mdd_year # 计算基准分年收益率与最大回撤 returns = get_bench_return(br[0], br[1]) returns_year = returns.resample('Y').apply( lambda x: empyrical.cum_returns_final(x)) mdd_year = returns.resample('Y').apply( lambda x: empyrical.max_drawdown(x)) df['基准收益率'] = returns_year df['基准最大回撤'] = mdd_year df['超额收益率'] = df['组合收益率'] - df['基准收益率'] # 保存到结果 filename = '%s/%s分年统计.xlsx' % (DATA_DIR, p) df.to_excel(filename)
def create_perf_attrib_stats(perf_attrib, risk_exposures): """ Takes perf attribution data over a period of time and computes annualized multifactor alpha, multifactor sharpe, risk exposures. """ summary = OrderedDict() specific_returns = perf_attrib['specific_returns'] common_returns = perf_attrib['common_returns'] summary['Annual multi-factor alpha'] =\ ep.annual_return(specific_returns) summary['Multi-factor sharpe'] =\ ep.sharpe_ratio(specific_returns) # empty line between common/specific/total returns summary[' '] = ' ' summary['Cumulative specific returns'] =\ ep.cum_returns_final(specific_returns) summary['Cumulative common returns'] =\ ep.cum_returns_final(common_returns) summary['Total returns'] =\ ep.cum_returns_final(perf_attrib['total_returns']) summary = pd.Series(summary) risk_exposure_summary = risk_exposures.sum(axis='rows') return summary, risk_exposure_summary
def test_total(self): res_a = empyrical.cum_returns_final(ret['a']) res_b = empyrical.cum_returns_final(ret['b']) res_c = empyrical.cum_returns_final(ret['c']) assert isclose(ret['a'].vbt.returns.total(), res_a) pd.testing.assert_series_equal( ret.vbt.returns.total(), pd.Series([res_a, res_b, res_c], index=ret.columns))
def test_total_return(self): res_a = empyrical.cum_returns_final(ret['a']) res_b = empyrical.cum_returns_final(ret['b']) res_c = empyrical.cum_returns_final(ret['c']) assert isclose(ret['a'].vbt.returns.total(), res_a) pd.testing.assert_series_equal( ret.vbt.returns.total(), pd.Series([res_a, res_b, res_c], index=ret.columns).rename('total_return')) pd.testing.assert_series_equal( ret.vbt.returns.rolling_total(ret.shape[0], minp=1).iloc[-1], pd.Series([res_a, res_b, res_c], index=ret.columns).rename(ret.index[-1]))
def Analysis(results): """ 技术指标分析器 :param results: { 'returns':[0.1,0.1,0.1], 'benchmark':[0.1,0.1,0.1] 'trades':[[2020.01.01 01:00:00,'BUY',6234.10,1]] } :return: """ res = pnl_res(results["returns"]) bres = pnl_res(results["benchmark"]) return_ratio = empyrical.cum_returns_final(res) annual_return_ratio = empyrical.annual_return(res) sharp_ratio = empyrical.sharpe_ratio(res, 0.035 / 252) return_volatility = empyrical.annual_volatility(res) max_drawdown = empyrical.max_drawdown(res) alpha, beta = empyrical.alpha_beta_aligned(res, bres) pls, wr = pls_ws(results["trades"]) return { 'pls': pls, 'wr': wr, 'return_ratio': return_ratio, 'annual_return_ratio': annual_return_ratio, 'beta': beta, 'alpha': alpha, 'sharp_ratio': sharp_ratio, 'return_volatility': return_volatility, 'max_drawdown': max_drawdown, }
def create_perf_attrib_stats(perf_attrib, risk_exposures): """ Takes perf attribution data over a period of time and computes annualized multifactor alpha, multifactor sharpe, risk exposures. """ summary = OrderedDict() total_returns = perf_attrib['total_returns'] specific_returns = perf_attrib['specific_returns'] common_returns = perf_attrib['common_returns'] summary['Annualized Specific Return'] =\ ep.annual_return(specific_returns, annualization=APPROX_BDAYS_PER_YEAR) summary['Annualized Common Return'] =\ ep.annual_return(common_returns, annualization=APPROX_BDAYS_PER_YEAR) summary['Annualized Total Return'] =\ ep.annual_return(total_returns, annualization=APPROX_BDAYS_PER_YEAR) summary['Specific Sharpe Ratio'] =\ ep.sharpe_ratio(specific_returns, annualization=APPROX_BDAYS_PER_YEAR) summary['Cumulative Specific Return'] =\ ep.cum_returns_final(specific_returns) summary['Cumulative Common Return'] =\ ep.cum_returns_final(common_returns) summary['Total Returns'] =\ ep.cum_returns_final(total_returns) summary = pd.Series(summary, name='') annualized_returns_by_factor = [ ep.annual_return(perf_attrib[c], annualization=APPROX_BDAYS_PER_YEAR) for c in risk_exposures.columns ] cumulative_returns_by_factor = [ ep.cum_returns_final(perf_attrib[c]) for c in risk_exposures.columns ] risk_exposure_summary = pd.DataFrame( data=OrderedDict([ ('Average Risk Factor Exposure', risk_exposures.mean(axis='rows')), ('Annualized Return', annualized_returns_by_factor), ('Cumulative Return', cumulative_returns_by_factor), ]), index=risk_exposures.columns, ) return summary, risk_exposure_summary
def create_perf_attrib_stats(perf_attrib, risk_exposures): """ Takes perf attribution data over a period of time and computes annualized multifactor alpha, multifactor sharpe, risk exposures. """ summary = OrderedDict() total_returns = perf_attrib['total_returns'] specific_returns = perf_attrib['specific_returns'] common_returns = perf_attrib['common_returns'] summary['Annualized Specific Return'] =\ ep.annual_return(specific_returns) summary['Annualized Common Return'] =\ ep.annual_return(common_returns) summary['Annualized Total Return'] =\ ep.annual_return(total_returns) summary['Specific Sharpe Ratio'] =\ ep.sharpe_ratio(specific_returns) summary['Cumulative Specific Return'] =\ ep.cum_returns_final(specific_returns) summary['Cumulative Common Return'] =\ ep.cum_returns_final(common_returns) summary['Total Returns'] =\ ep.cum_returns_final(total_returns) summary = pd.Series(summary, name='') annualized_returns_by_factor = [ep.annual_return(perf_attrib[c]) for c in risk_exposures.columns] cumulative_returns_by_factor = [ep.cum_returns_final(perf_attrib[c]) for c in risk_exposures.columns] risk_exposure_summary = pd.DataFrame( data=OrderedDict([ ( 'Average Risk Factor Exposure', risk_exposures.mean(axis='rows') ), ('Annualized Return', annualized_returns_by_factor), ('Cumulative Return', cumulative_returns_by_factor), ]), index=risk_exposures.columns, ) return summary, risk_exposure_summary
def get_performance_summary(returns): stats = {'annualized_returns': ep.annual_return(returns), 'cumulative_returns': ep.cum_returns_final(returns), 'annual_volatility': ep.annual_volatility(returns), 'sharpe_ratio': ep.sharpe_ratio(returns), 'sortino_ratio': ep.sortino_ratio(returns), 'max_drawdown': ep.max_drawdown(returns)} return pd.Series(stats)
def create_perf_attrib_stats(perf_attrib, risk_exposures): """ Takes perf attribution data over a period of time and computes annualized multifactor alpha, multifactor sharpe, risk exposures. """ summary = OrderedDict() total_returns = perf_attrib['total_returns'] specific_returns = perf_attrib['specific_returns'] common_returns = perf_attrib['common_returns'] summary['年化特定收益率'] =\ ep.annual_return(specific_returns) summary['年化共同收益率'] =\ ep.annual_return(common_returns) summary['年化总收益率'] =\ ep.annual_return(total_returns) summary['特定夏普比率'] =\ ep.sharpe_ratio(specific_returns) summary['累积特定收益率'] =\ ep.cum_returns_final(specific_returns) summary['累积共同收益率'] =\ ep.cum_returns_final(common_returns) summary['总收益率'] =\ ep.cum_returns_final(total_returns) summary = pd.Series(summary, name='') annualized_returns_by_factor = [ ep.annual_return(perf_attrib[c]) for c in risk_exposures.columns ] cumulative_returns_by_factor = [ ep.cum_returns_final(perf_attrib[c]) for c in risk_exposures.columns ] risk_exposure_summary = pd.DataFrame( data=OrderedDict([ ('因子平均风险敞口', risk_exposures.mean(axis='rows')), ('年化收益率', annualized_returns_by_factor), ('累积收益率', cumulative_returns_by_factor), ]), index=risk_exposures.columns, ) return summary, risk_exposure_summary
def create_perf_attrib_stats(perf_attrib, risk_exposures): """ Takes perf attribution data over a period of time and computes annualized multifactor alpha, multifactor sharpe, risk exposures. """ summary = OrderedDict() total_returns = perf_attrib["total_returns"] specific_returns = perf_attrib["specific_returns"] common_returns = perf_attrib["common_returns"] summary["Annualized Specific Return"] = ep.annual_return(specific_returns) summary["Annualized Common Return"] = ep.annual_return(common_returns) summary["Annualized Total Return"] = ep.annual_return(total_returns) summary["Specific Sharpe Ratio"] = ep.sharpe_ratio(specific_returns) summary["Cumulative Specific Return"] = ep.cum_returns_final( specific_returns ) summary["Cumulative Common Return"] = ep.cum_returns_final(common_returns) summary["Total Returns"] = ep.cum_returns_final(total_returns) summary = pd.Series(summary, name="") annualized_returns_by_factor = [ ep.annual_return(perf_attrib[c]) for c in risk_exposures.columns ] cumulative_returns_by_factor = [ ep.cum_returns_final(perf_attrib[c]) for c in risk_exposures.columns ] risk_exposure_summary = pd.DataFrame( data=OrderedDict( [ ( "Average Risk Factor Exposure", risk_exposures.mean(axis="rows"), ), ("Annualized Return", annualized_returns_by_factor), ("Cumulative Return", cumulative_returns_by_factor), ] ), index=risk_exposures.columns, ) return summary, risk_exposure_summary
def get_all_comp_daily_return(): ''' 得到所有基金公司日收益率 ''' all_df = utils.get_all_funds() comps = all_df['mgrcomp'].unique() dic = {} for c in comps: print c df = all_df[all_df['mgrcomp'] == c] df = df[df['investtype'].str.contains(u'股')] daily_return = get_comp_daily_return(c, df['wind_code'].tolist()) if daily_return is None: continue dic[c] = daily_return print empyrical.cum_returns_final( daily_return[daily_return.index >= '2019-01-01']) df = pd.DataFrame(dic) df.to_excel('%s/comp_ret.xlsx' % (const.FOF_DIR), encoding='utf-8')
def _get_backtest_performance_metrics(ret, benchmark_ret): metrics = { 'alpha': empyrical.alpha(ret, benchmark_ret), 'beta': empyrical.beta(ret, benchmark_ret), 'return': empyrical.cum_returns_final(ret), 'cagr': empyrical.cagr(ret), 'sharpe': empyrical.sharpe_ratio(ret), 'max_drawdown': empyrical.max_drawdown(ret), 'var': empyrical.value_at_risk(ret), 'volatility': empyrical.annual_volatility(ret), } return metrics
def create_perf_attrib_stats(perf_attrib): """ Takes perf attribution data over a period of time and computes annualized multifactor alpha, multifactor sharpe, risk exposures. """ summary = {} specific_returns = perf_attrib['specific_returns'] common_returns = perf_attrib['common_returns'] summary['Annual multi-factor alpha'] =\ ep.annual_return(specific_returns) summary['Multi-factor sharpe'] =\ ep.sharpe_ratio(specific_returns) summary['Cumulative specific returns'] =\ ep.cum_returns_final(specific_returns) summary['Cumulative common returns'] =\ ep.cum_returns_final(common_returns) summary['Total returns'] =\ ep.cum_returns_final(perf_attrib['total_returns']) summary = pd.Series(summary) return summary
def performance(ret, benchmark, rf=0.04): #计算评价指标 import empyrical max_drawdown = empyrical.max_drawdown(ret) total_return = empyrical.cum_returns_final(ret) annual_return = empyrical.annual_return(ret) sharpe_ratio = empyrical.sharpe_ratio(ret, risk_free=((1 + rf)**(1 / 252) - 1)) alpha, beta = empyrical.alpha_beta(ret, benchmark) return { 'total_return': total_return, 'annual_return': annual_return, 'max_drawdown': max_drawdown, 'sharpe_ratio': sharpe_ratio, 'alpha': alpha, 'beta': beta }
def get_performance_summary(returns): ''' Calculate selected performance evaluation metrics using provided returns. Parameters ------------ returns : pd.Series Series of returns we want to evaluate Returns ----------- stats : pd.Series The calculated performance metrics ''' stats = { 'annualized_returns': ep.annual_return(returns), 'cumulative_returns': ep.cum_returns_final(returns), 'annual_volatility': ep.annual_volatility(returns), 'sharpe_ratio': ep.sharpe_ratio(returns), 'sortino_ratio': ep.sortino_ratio(returns), 'max_drawdown': ep.max_drawdown(returns) } return pd.Series(stats)
def test_perf_attrib_regression(self): positions = pd.read_csv('pyfolio/tests/test_data/positions.csv', index_col=0, parse_dates=True) positions.columns = [ int(col) if col != 'cash' else col for col in positions.columns ] returns = pd.read_csv('pyfolio/tests/test_data/returns.csv', index_col=0, parse_dates=True, header=None, squeeze=True) factor_loadings = pd.read_csv( 'pyfolio/tests/test_data/factor_loadings.csv', index_col=[0, 1], parse_dates=True) factor_returns = pd.read_csv( 'pyfolio/tests/test_data/factor_returns.csv', index_col=0, parse_dates=True) residuals = pd.read_csv('pyfolio/tests/test_data/residuals.csv', index_col=0, parse_dates=True) residuals.columns = [int(col) for col in residuals.columns] intercepts = pd.read_csv('pyfolio/tests/test_data/intercepts.csv', index_col=0, header=None, squeeze=True) risk_exposures_portfolio, perf_attrib_output = perf_attrib( returns, positions, factor_returns, factor_loadings, ) specific_returns = perf_attrib_output['specific_returns'] common_returns = perf_attrib_output['common_returns'] combined_returns = specific_returns + common_returns # since all returns are factor returns, common returns should be # equivalent to total returns, and specific returns should be 0 pd.util.testing.assert_series_equal(returns, common_returns, check_names=False) self.assertTrue(np.isclose(specific_returns, 0).all()) # specific and common returns combined should equal total returns pd.util.testing.assert_series_equal(returns, combined_returns, check_names=False) # check that residuals + intercepts = specific returns self.assertTrue(np.isclose((residuals + intercepts), 0).all()) # check that exposure * factor returns = common returns expected_common_returns = risk_exposures_portfolio.multiply( factor_returns, axis='rows').sum(axis='columns') pd.util.testing.assert_series_equal(expected_common_returns, common_returns, check_names=False) # since factor loadings are ones, portfolio risk exposures # should be ones pd.util.testing.assert_frame_equal( risk_exposures_portfolio, pd.DataFrame(np.ones_like(risk_exposures_portfolio), index=risk_exposures_portfolio.index, columns=risk_exposures_portfolio.columns)) perf_attrib_summary, exposures_summary = create_perf_attrib_stats( perf_attrib_output, risk_exposures_portfolio) self.assertEqual(ep.annual_return(specific_returns), perf_attrib_summary['Annualized Specific Return']) self.assertEqual(ep.annual_return(common_returns), perf_attrib_summary['Annualized Common Return']) self.assertEqual(ep.annual_return(combined_returns), perf_attrib_summary['Annualized Total Return']) self.assertEqual(ep.sharpe_ratio(specific_returns), perf_attrib_summary['Specific Sharpe Ratio']) self.assertEqual(ep.cum_returns_final(specific_returns), perf_attrib_summary['Cumulative Specific Return']) self.assertEqual(ep.cum_returns_final(common_returns), perf_attrib_summary['Cumulative Common Return']) self.assertEqual(ep.cum_returns_final(combined_returns), perf_attrib_summary['Total Returns']) avg_factor_exposure = risk_exposures_portfolio.mean().rename( 'Average Risk Factor Exposure') pd.util.testing.assert_series_equal( avg_factor_exposure, exposures_summary['Average Risk Factor Exposure']) cumulative_returns_by_factor = pd.Series( [ ep.cum_returns_final(perf_attrib_output[c]) for c in risk_exposures_portfolio.columns ], name='Cumulative Return', index=risk_exposures_portfolio.columns) pd.util.testing.assert_series_equal( cumulative_returns_by_factor, exposures_summary['Cumulative Return']) annualized_returns_by_factor = pd.Series( [ ep.annual_return(perf_attrib_output[c]) for c in risk_exposures_portfolio.columns ], name='Annualized Return', index=risk_exposures_portfolio.columns) pd.util.testing.assert_series_equal( annualized_returns_by_factor, exposures_summary['Annualized Return'])
def create_summary_tearsheet(self, x_performance, y_performance): """ Create a tear sheet of summary shortfall stats in a table. Parameters ---------- x_performance : AggregateDailyPerformance, required simulated or benchmark aggregate performance results y_performance : AggregateDailyPerformance, required actual aggregate performance results Returns ------- None """ stats = [] stats.append([ "Start Date", [ x_performance.returns.index.min().date().isoformat(), y_performance.returns.index.min().date().isoformat() ] ]) stats.append([ "End Date", [ x_performance.returns.index.max().date().isoformat(), y_performance.returns.index.max().date().isoformat() ] ]) stats.append([ 'Total Months', [ round((x_performance.returns.index.max() - x_performance.returns.index.min()) / pd.Timedelta(1, 'M')), round( (y_performance.returns.index.max() - y_performance.returns.index.min()) / pd.Timedelta(1, 'M')) ] ]) stats.append([" Risk and Returns", ["", ""]]) stats.append([ "CAGR", [ "{0}%".format(round(x_performance.cagr * 100, 1)), "{0}%".format(round(y_performance.cagr * 100, 1)) ] ]) stats.append([ "Sharpe Ratio", ['%.2f' % x_performance.sharpe, '%.2f' % y_performance.sharpe] ]) stats.append([ "Max Drawdown", [ "{0}%".format(round(x_performance.max_drawdown * 100, 1)), "{0}%".format(round(y_performance.max_drawdown * 100, 1)) ] ]) stats.append([ "Cumulative Return", [ "{0}%".format( round( ep.cum_returns_final(x_performance.returns) * 100, 1)), "{0}%".format( round( ep.cum_returns_final(y_performance.returns) * 100, 1)) ] ]) if x_performance.commissions is not None and y_performance.commissions is not None: stats.append([ "Cumulative Commissions", [ "{0}%".format( round( ep.cum_returns_final(x_performance.commissions) * 100, 1)), "{0}%".format( round( ep.cum_returns_final(y_performance.commissions) * 100, 1)) ] ]) stats.append([ "Annual Volatility", [ "{0}%".format( round( ep.annual_volatility(x_performance.returns) * 100, 1)), "{0}%".format( round( ep.annual_volatility(y_performance.returns) * 100, 1)) ] ]) stats.append([ "Sortino Ratio", [ '%.2f' % ep.sortino_ratio(x_performance.returns), '%.2f' % ep.sortino_ratio(y_performance.returns) ] ]) stats.append([ "Calmar Ratio", [ '%.2f' % ep.calmar_ratio(x_performance.returns), '%.2f' % ep.calmar_ratio(y_performance.returns) ] ]) stats.append([ "Skew", [ '%.2f' % scipy.stats.skew(x_performance.returns), '%.2f' % scipy.stats.skew(y_performance.returns) ] ]) stats.append([ "Kurtosis", [ '%.2f' % scipy.stats.kurtosis(x_performance.returns), '%.2f' % scipy.stats.kurtosis(y_performance.returns) ] ]) stats.append([" Positions and Exposure", ["", ""]]) if x_performance.abs_exposures is not None and y_performance.abs_exposures is not None: stats.append([ "Absolute Exposure (percentage of capital)", [ "{0}%".format( round(x_performance.abs_exposures.mean() * 100, 1)), "{0}%".format( round(y_performance.abs_exposures.mean() * 100, 1)) ] ]) if x_performance.net_exposures is not None and y_performance.net_exposures is not None: stats.append([ "Net Exposure (percentage of capital)", [ "{0}%".format( round(x_performance.net_exposures.mean() * 100, 1)), "{0}%".format( round(y_performance.net_exposures.mean() * 100, 1)) ] ]) if x_performance.total_holdings is not None and y_performance.total_holdings is not None: stats.append([ "Average Daily Holdings", [ round(x_performance.total_holdings.mean()), round(y_performance.total_holdings.mean()) ] ]) if x_performance.turnover is not None and y_performance.turnover is not None: stats.append([ "Average Daily Turnover (percentage of capital)", [ "{0}%".format(round(x_performance.turnover.mean() * 100, 1)), "{0}%".format(round(y_performance.turnover.mean() * 100, 1)) ] ]) if x_performance.abs_exposures is not None and y_performance.abs_exposures is not None: stats.append([ "Normalized CAGR (CAGR/Absolute Exposure)", [ "{0}%".format( round((x_performance.cagr / x_performance.abs_exposures.mean()) * 100, 1)), "{0}%".format( round((y_performance.cagr / y_performance.abs_exposures.mean()) * 100, 1)) ] ]) with sns.axes_style("white", {'axes.linewidth': 0}): fig = plt.figure("Performance Summary", figsize=(6, 6)) axis = fig.add_subplot(111) axis.get_xaxis().set_visible(False) axis.get_yaxis().set_visible(False) headings, values = zip(*stats) table = axis.table(cellText=values, rowLabels=headings, colLabels=[self.x_label, self.y_label], loc="center") for (row, col), cell in table.get_celld().items(): txt = cell.get_text().get_text() if row == 0 or txt.startswith(" "): cell.set_text_props(fontproperties=FontProperties( weight='bold')) table.scale(1, 2) table.set_fontsize("large") fig.tight_layout()
def return_calculation(returns): """ Calculates cumulative returns of a portfolio without any annualization """ ending_value = cum_returns_final(returns, starting_value=1) return ending_value - 1
def create_summary_tearsheet(self, performance, agg_performance=None): """ Create a tear sheet of summary performance stats in a table. Parameters ---------- performance : DailyPerformance, required a DailyPerformance instance agg_performance : AggregateDailyPerformance, optional an AggregateDailyPerformance instance. Constructed from performance if not provided. Returns ------- None Examples -------- >>> from moonchart import DailyPerformance, Tearsheet >>> perf = DailyPerformance.from_moonshot_csv("backtest_results.csv") >>> t = Tearsheet() >>> t.create_summary_tearsheet(perf) """ if agg_performance is None: agg_performance = AggregateDailyPerformance(performance) stats = [] if agg_performance.pnl is not None: stats.append(["PNL", round(agg_performance.pnl.sum(), 2)]) if agg_performance.commission_amounts is not None: stats.append([ "Commissions", round(agg_performance.commission_amounts.sum(), 2) ]) stats.append([ "Start Date", agg_performance.returns.index.min().date().isoformat() ]) stats.append([ "End Date", agg_performance.returns.index.max().date().isoformat() ]) stats.append([ 'Total Months', round((agg_performance.returns.index.max() - agg_performance.returns.index.min()) / pd.Timedelta(1, 'M')) ]) stats.append(["", " Risk and Returns"]) stats.append( ["CAGR", "{0}%".format(round(agg_performance.cagr * 100, 1))]) stats.append(["Sharpe Ratio", '%.2f' % agg_performance.sharpe]) stats.append([ "Max Drawdown", "{0}%".format(round(agg_performance.max_drawdown * 100, 1)) ]) stats.append([ "Cumulative Return", "{0}%".format( round(ep.cum_returns_final(agg_performance.returns) * 100, 1)) ]) stats.append([ "Annual Volatility", "{0}%".format( round(ep.annual_volatility(agg_performance.returns) * 100, 1)) ]) stats.append([ "Sortino Ratio", '%.2f' % ep.sortino_ratio(agg_performance.returns) ]) stats.append([ "Calmar Ratio", '%.2f' % ep.calmar_ratio(agg_performance.returns) ]) stats.append( ["Skew", '%.2f' % scipy.stats.skew(agg_performance.returns)]) stats.append([ "Kurtosis", '%.2f' % scipy.stats.kurtosis(agg_performance.returns) ]) if any([ field is not None for field in (agg_performance.abs_exposures, agg_performance.net_exposures, agg_performance.total_holdings, agg_performance.turnover) ]): stats.append(["", " Positions and Exposure"]) if agg_performance.abs_exposures is not None: avg_abs_exposures = agg_performance.abs_exposures.mean() stats.append([ "Absolute Exposure (percentage of capital)", "{0}%".format(round(avg_abs_exposures * 100, 1)) ]) if agg_performance.net_exposures is not None: avg_net_exposures = agg_performance.net_exposures.mean() stats.append([ "Net Exposure (percentage of capital)", "{0}%".format(round(avg_net_exposures * 100, 1)) ]) if agg_performance.total_holdings is not None: avg_daily_holdings = agg_performance.total_holdings.mean() stats.append(["Average Daily Holdings", round(avg_daily_holdings)]) if agg_performance.turnover is not None: avg_daily_turnover = agg_performance.turnover.mean() stats.append([ "Average Daily Turnover (percentage of capital)", "{0}%".format(round(avg_daily_turnover * 100, 1)) ]) if agg_performance.abs_exposures is not None: norm_cagr = agg_performance.cagr / avg_abs_exposures stats.append([ "Normalized CAGR (CAGR/Absolute Exposure)", "{0}%".format(round(norm_cagr * 100, 1)) ]) with sns.axes_style("white", {'axes.linewidth': 0}): fig = plt.figure("Performance Summary", figsize=(6, 6)) axis = fig.add_subplot(111) axis.get_xaxis().set_visible(False) axis.get_yaxis().set_visible(False) headings, values = zip(*stats) table = axis.table(cellText=[[v] for v in values], rowLabels=headings, colLabels=["Performance Summary"], loc="center") for (row, col), cell in table.get_celld().items(): txt = cell.get_text().get_text() if row == 0 or txt.startswith(" "): cell.set_text_props(fontproperties=FontProperties( weight='bold')) table.scale(1, 2) table.set_fontsize("large") fig.tight_layout()
def total_return(self, daily_change): perc = empyrical.cum_returns_final(daily_change) * 100 return round(perc, 0)
def test_perf_attrib_regression(self): positions = pd.read_csv('pyfolio/tests/test_data/positions.csv', index_col=0, parse_dates=True) positions.columns = [int(col) if col != 'cash' else col for col in positions.columns] returns = pd.read_csv('pyfolio/tests/test_data/returns.csv', index_col=0, parse_dates=True, header=None, squeeze=True) factor_loadings = pd.read_csv( 'pyfolio/tests/test_data/factor_loadings.csv', index_col=[0, 1], parse_dates=True ) factor_returns = pd.read_csv( 'pyfolio/tests/test_data/factor_returns.csv', index_col=0, parse_dates=True ) residuals = pd.read_csv('pyfolio/tests/test_data/residuals.csv', index_col=0, parse_dates=True) residuals.columns = [int(col) for col in residuals.columns] intercepts = pd.read_csv('pyfolio/tests/test_data/intercepts.csv', index_col=0, header=None, squeeze=True) risk_exposures_portfolio, perf_attrib_output = perf_attrib( returns, positions, factor_returns, factor_loadings, ) specific_returns = perf_attrib_output['specific_returns'] common_returns = perf_attrib_output['common_returns'] combined_returns = specific_returns + common_returns # since all returns are factor returns, common returns should be # equivalent to total returns, and specific returns should be 0 pd.util.testing.assert_series_equal(returns, common_returns, check_names=False) self.assertTrue(np.isclose(specific_returns, 0).all()) # specific and common returns combined should equal total returns pd.util.testing.assert_series_equal(returns, combined_returns, check_names=False) # check that residuals + intercepts = specific returns self.assertTrue(np.isclose((residuals + intercepts), 0).all()) # check that exposure * factor returns = common returns expected_common_returns = risk_exposures_portfolio.multiply( factor_returns, axis='rows' ).sum(axis='columns') pd.util.testing.assert_series_equal(expected_common_returns, common_returns, check_names=False) # since factor loadings are ones, portfolio risk exposures # should be ones pd.util.testing.assert_frame_equal( risk_exposures_portfolio, pd.DataFrame(np.ones_like(risk_exposures_portfolio), index=risk_exposures_portfolio.index, columns=risk_exposures_portfolio.columns) ) perf_attrib_summary, exposures_summary = create_perf_attrib_stats( perf_attrib_output, risk_exposures_portfolio ) self.assertEqual(ep.annual_return(specific_returns), perf_attrib_summary['Annualized Specific Return']) self.assertEqual(ep.annual_return(common_returns), perf_attrib_summary['Annualized Common Return']) self.assertEqual(ep.annual_return(combined_returns), perf_attrib_summary['Annualized Total Return']) self.assertEqual(ep.sharpe_ratio(specific_returns), perf_attrib_summary['Specific Sharpe Ratio']) self.assertEqual(ep.cum_returns_final(specific_returns), perf_attrib_summary['Cumulative Specific Return']) self.assertEqual(ep.cum_returns_final(common_returns), perf_attrib_summary['Cumulative Common Return']) self.assertEqual(ep.cum_returns_final(combined_returns), perf_attrib_summary['Total Returns']) avg_factor_exposure = risk_exposures_portfolio.mean().rename( 'Average Risk Factor Exposure' ) pd.util.testing.assert_series_equal( avg_factor_exposure, exposures_summary['Average Risk Factor Exposure'] ) cumulative_returns_by_factor = pd.Series( [ep.cum_returns_final(perf_attrib_output[c]) for c in risk_exposures_portfolio.columns], name='Cumulative Return', index=risk_exposures_portfolio.columns ) pd.util.testing.assert_series_equal( cumulative_returns_by_factor, exposures_summary['Cumulative Return'] ) annualized_returns_by_factor = pd.Series( [ep.annual_return(perf_attrib_output[c]) for c in risk_exposures_portfolio.columns], name='Annualized Return', index=risk_exposures_portfolio.columns ) pd.util.testing.assert_series_equal( annualized_returns_by_factor, exposures_summary['Annualized Return'] )
def cumulative_returns(portfolio_daily_returns): return ep.cum_returns_final(portfolio_daily_returns)