def plottingMonthlyRetDist(returns, ax, title="Distribution of Monthly Returns"): x_axis_formatter = FuncFormatter(zero_dec_percentage) ax.xaxis.set_major_formatter(FuncFormatter(x_axis_formatter)) ax.tick_params(axis='x', which='major', labelsize=10) monthlyRetTable = aggregateReturns(returns, 'monthly') monthlyRetTable = np.exp(monthlyRetTable) - 1. if len(monthlyRetTable) > 1: ax.hist( monthlyRetTable, color='orange', alpha=0.8, bins=20 ) ax.axvline( monthlyRetTable.mean(), color='steelblue', linestyle='--', lw=4, alpha=1.0 ) ax.axvline(0.0, color='black', linestyle='-', lw=3, alpha=0.75) ax.legend(['mean'], loc='best') ax.set_ylabel('Number of months') ax.set_xlabel('Returns') ax.set_title(title) return ax
def plottingAnnualReturns(returns, ax, title='Annual Returns'): x_axis_formatter = FuncFormatter(zero_dec_percentage) ax.xaxis.set_major_formatter(FuncFormatter(x_axis_formatter)) ax.tick_params(axis='x', which='major', labelsize=10) annulaReturns = pd.DataFrame(aggregateReturns(returns, 'yearly')) annulaReturns = np.exp(annulaReturns) - 1. ax.axvline(annulaReturns.values.mean(), color='steelblue', linestyle='--', lw=4, alpha=0.7) annulaReturns.sort_index(ascending=False).plot( ax=ax, kind='barh', alpha=0.7 ) ax.axvline(0.0, color='black', linestyle='-', lw=3) ax.set_ylabel('Year') ax.set_xlabel('Returns') ax.set_title(title) ax.legend(['mean'], loc='best') return ax
def plottingMonthlyReturnsHeapmap(returns, ax, title='Monthly Returns (%)'): x_axis_formatter = FuncFormatter(integer_format) ax.xaxis.set_major_formatter(FuncFormatter(x_axis_formatter)) monthlyRetTable = pd.DataFrame(aggregateReturns(returns, 'monthly')) monthlyRetTable = monthlyRetTable.unstack() monthlyRetTable.columns = monthlyRetTable.columns.droplevel() sns.heatmap((np.exp(monthlyRetTable.fillna(0)) - 1.0) * 100.0, annot=True, fmt=".1f", annot_kws={"size": 9}, alpha=1.0, center=0.0, cbar=False, cmap=matplotlib.cm.RdYlGn_r, ax=ax) ax.set_ylabel('Year') ax.set_xlabel('Month') ax.set_title(title) return ax
def createPerformanceTearSheet(prices=None, returns=None, benchmark=None, benchmarkReturns=None, plot=True): if prices is not None and not isinstance(prices, pd.Series): raise TypeError("prices series should be a pandas time series.") elif returns is not None and prices is not None: raise ValueError("prices series and returns series can't be both set.") if benchmark is not None and not (isinstance(benchmark, pd.Series) or isinstance(benchmark, str)): raise TypeError("benchmark series should be a pandas time series or a string ticker.") if returns is None: returns = np.log(prices / prices.shift(1)) returns.fillna(0, inplace=True) returns = returns[~np.isinf(returns)] if benchmark is not None and isinstance(benchmark, str) and benchmarkReturns is None: startDate = advanceDateByCalendar("China.SSE", prices.index[0], '-1b', BizDayConventions.Preceding) benchmarkPrices = get_benchmark_data(benchmark, start_date=startDate.strftime('%Y-%m-%d'), end_data=returns.index[-1].strftime("%Y-%m-%d")) # do the linear interpolation on the target time line date_index = prices.index new_index = benchmarkPrices.index.union(date_index) benchmarkPrices = benchmarkPrices.reindex(new_index) benchmarkPrices = benchmarkPrices.interpolate().ix[date_index].dropna() benchmarkReturns = np.log(benchmarkPrices['closePrice'] / benchmarkPrices['closePrice'].shift(1)) benchmarkReturns.name = benchmark benchmarkReturns.fillna(0, inplace=True) benchmarkReturns.index = pd.to_datetime(benchmarkReturns.index.date) elif benchmark is not None and isinstance(benchmark, pd.Series): benchmarkReturns = np.log(benchmark / benchmark.shift(1)) try: benchmarkReturns.name = benchmark.name except AttributeError: benchmarkReturns.name = "benchmark" benchmarkReturns.dropna(inplace=True) benchmarkReturns.index = pd.to_datetime(benchmarkReturns.index.date) aggregateDaily = aggregateReturns(returns) drawDownDaily = drawDown(aggregateDaily) # perf metric annualRet = annualReturn(aggregateDaily) annualVol = annualVolatility(aggregateDaily) sortino = sortinoRatio(aggregateDaily) sharp = sharpRatio(aggregateDaily) maxDrawDown = np.min(drawDownDaily['draw_down']) winningDays = np.sum(aggregateDaily > 0.) lossingDays = np.sum(aggregateDaily < 0.) perf_metric = pd.DataFrame([annualRet, annualVol, sortino, sharp, maxDrawDown, winningDays, lossingDays], index=['annual_return', 'annual_volatiltiy', 'sortino_ratio', 'sharp_ratio', 'max_draw_down', 'winning_days', 'lossing_days'], columns=['metrics']) perf_df = pd.DataFrame(index=aggregateDaily.index) perf_df['daily_return'] = aggregateDaily perf_df['daily_cum_return'] = np.exp(aggregateDaily.cumsum()) - 1.0 perf_df['daily_draw_down'] = drawDownDaily['draw_down'] if benchmarkReturns is not None: perf_df['benchmark_return'] = benchmarkReturns perf_df['benchmark_cum_return'] = benchmarkReturns.cumsum() perf_df.dropna(inplace=True) perf_df['benchmark_cum_return'] = np.exp(perf_df['benchmark_cum_return'] - perf_df['benchmark_cum_return'][0]) - 1.0 perf_df['access_return'] = aggregateDaily - benchmarkReturns perf_df['access_cum_return'] = (1.0 + perf_df['daily_cum_return']) \ / (1.0 + perf_df['benchmark_cum_return']) - 1.0 perf_df.fillna(0.0, inplace=True) accessDrawDownDaily = drawDown(perf_df['access_return']) else: accessDrawDownDaily = None if 'benchmark_cum_return' in perf_df: benchmarkCumReturns = perf_df['benchmark_cum_return'] benchmarkCumReturns.name = benchmarkReturns.name accessCumReturns = perf_df['access_cum_return'] accessReturns = perf_df['access_return'] index = perf_df.index length1 = len(bizDatesList('China.SSE', index[0], index[-1])) length2 = len(perf_df) factor = length1 / float(length2) rb = RollingBeta(perf_df['daily_return'], perf_df['benchmark_return'], [1, 3, 6], factor=factor) rs = RollingSharp(perf_df['daily_return'], [1, 3, 6], factor=factor) else: benchmarkCumReturns = None accessReturns = None accessCumReturns = None if len(perf_df['daily_return']) > APPROX_BDAYS_PER_MONTH and benchmarkCumReturns is not None: rollingRisk = pd.concat([pd.concat(rs, axis=1), pd.concat(rb, axis=1)], axis=1) else: rollingRisk = None if plot: verticalSections = 2 plt.figure(figsize=(16, 7 * verticalSections)) gs = gridspec.GridSpec(verticalSections, 3, wspace=0.5, hspace=0.5) axRollingReturns = plt.subplot(gs[0, :]) axDrawDown = plt.subplot(gs[1, :], sharex=axRollingReturns) plottingRollingReturn(perf_df['daily_cum_return'], benchmarkCumReturns, axRollingReturns) plottingDrawdownPeriods(perf_df['daily_cum_return'], drawDownDaily, 5, axDrawDown) if rollingRisk is not None: plt.figure(figsize=(16, 7 * verticalSections)) gs = gridspec.GridSpec(verticalSections, 3, wspace=0.5, hspace=0.5) axRollingBeta = plt.subplot(gs[0, :]) axRollingSharp = plt.subplot(gs[1, :]) bmName = benchmarkReturns.name plottingRollingBeta(rb, bmName, ax=axRollingBeta) plottingRollingSharp(rs, ax=axRollingSharp) plt.figure(figsize=(16, 7 * verticalSections)) gs = gridspec.GridSpec(verticalSections, 3, wspace=0.5, hspace=0.5) axUnderwater = plt.subplot(gs[0, :]) axMonthlyHeatmap = plt.subplot(gs[1, 0]) axAnnualReturns = plt.subplot(gs[1, 1]) axMonthlyDist = plt.subplot(gs[1, 2]) plottingUnderwater(drawDownDaily['draw_down'], axUnderwater) plottingMonthlyReturnsHeapmap(returns, axMonthlyHeatmap) plottingAnnualReturns(returns, axAnnualReturns) plottingMonthlyRetDist(returns, axMonthlyDist) if accessReturns is not None and plot: plt.figure(figsize=(16, 7 * verticalSections)) gs = gridspec.GridSpec(verticalSections, 3, wspace=0.5, hspace=0.5) axRollingAccessReturns = plt.subplot(gs[0, :]) axAccessDrawDown = plt.subplot(gs[1, :], sharex=axRollingAccessReturns) plottingRollingReturn(accessCumReturns, None, axRollingAccessReturns, title='Access Cumulative Returns w.r.t. ' + benchmarkReturns.name) plottingDrawdownPeriods(accessCumReturns, accessDrawDownDaily, 5, axAccessDrawDown, title=('Top 5 Drawdown periods w.r.t. ' + benchmarkReturns.name)) plt.figure(figsize=(16, 7 * verticalSections)) gs = gridspec.GridSpec(verticalSections, 3, wspace=0.5, hspace=0.5) axAccessUnderwater = plt.subplot(gs[0, :]) axAccessMonthlyHeatmap = plt.subplot(gs[1, 0]) axAccessAnnualReturns = plt.subplot(gs[1, 1]) axAccessMonthlyDist = plt.subplot(gs[1, 2]) plottingUnderwater(accessDrawDownDaily['draw_down'], axAccessUnderwater, title='Underwater Plot w.r.t. ' + benchmarkReturns.name) plottingMonthlyReturnsHeapmap(accessReturns, ax=axAccessMonthlyHeatmap, title='Monthly Access Returns (%)') plottingAnnualReturns(accessReturns, ax=axAccessAnnualReturns, title='Annual Access Returns') plottingMonthlyRetDist(accessReturns, ax=axAccessMonthlyDist, title='Distribution of Monthly Access Returns') return perf_metric, perf_df, rollingRisk
def createPerformanceTearSheet(prices=None, returns=None, benchmark=None, benchmarkReturns=None, other_curves=None, turn_over=None, tc_cost=0., plot=True): if prices is not None and not isinstance(prices, pd.Series): raise TypeError("prices series should be a pandas time series.") elif returns is not None and prices is not None: raise ValueError("prices series and returns series can't be both set.") if benchmark is not None and not (isinstance(benchmark, pd.Series) or isinstance(benchmark, str)): raise TypeError("benchmark series should be a pandas time series or a string ticker.") if returns is None: returns = np.log(prices / prices.shift(1)) returns.fillna(0, inplace=True) returns = returns[~np.isinf(returns)] if benchmark is not None and isinstance(benchmark, str) and benchmarkReturns is None: startDate = advanceDateByCalendar("China.SSE", prices.index[0], '-1b', BizDayConventions.Preceding) benchmarkPrices = get_benchmark_data(benchmark, start_date=startDate.strftime('%Y-%m-%d'), end_data=returns.index[-1].strftime("%Y-%m-%d")) # do the linear interpolation on the target time line date_index = prices.index new_index = benchmarkPrices.index.union(date_index) benchmarkPrices = benchmarkPrices.reindex(new_index) benchmarkPrices = benchmarkPrices.interpolate().ix[date_index].dropna() benchmarkReturns = np.log(benchmarkPrices['closePrice'] / benchmarkPrices['closePrice'].shift(1)) benchmarkReturns.name = benchmark benchmarkReturns.fillna(0, inplace=True) benchmarkReturns.index = pd.to_datetime(benchmarkReturns.index.date) elif benchmark is not None and isinstance(benchmark, pd.Series): benchmarkReturns = np.log(benchmark / benchmark.shift(1)) try: benchmarkReturns.name = benchmark.name except AttributeError: benchmarkReturns.name = "benchmark" benchmarkReturns.dropna(inplace=True) benchmarkReturns.index = pd.to_datetime(benchmarkReturns.index.date) aggregateDaily, aggregateDailyAfterTC = aggregateReturns(returns, turn_over, tc_cost) if aggregateDailyAfterTC is not None: aggregateDailyBeforeTC = aggregateDaily aggregateDaily = aggregateDailyAfterTC else: aggregateDailyBeforeTC = aggregateDaily drawDownDaily = drawDown(aggregateDaily) # perf metric annualRet = annualReturn(aggregateDaily) annualVol = annualVolatility(aggregateDaily) sortino = sortinoRatio(aggregateDaily) sharp = sharpRatio(aggregateDaily) maxDrawDown = np.min(drawDownDaily['draw_down']) winningDays = np.sum(aggregateDaily > 0.) lossingDays = np.sum(aggregateDaily < 0.) perf_metric = pd.DataFrame([annualRet, annualVol, sortino, sharp, maxDrawDown, winningDays, lossingDays], index=['annual_return', 'annual_volatiltiy', 'sortino_ratio', 'sharp_ratio', 'max_draw_down', 'winning_days', 'lossing_days'], columns=['metrics']) perf_df = pd.DataFrame(index=aggregateDaily.index) perf_df['daily_return'] = aggregateDaily perf_df['daily_return (w/o tc)'] = aggregateDailyBeforeTC perf_df['daily_cum_return'] = np.exp(aggregateDaily.cumsum()) - 1.0 perf_df['daily_cum_return (w/o tc)'] = np.exp(aggregateDailyBeforeTC.cumsum()) - 1.0 perf_df['daily_draw_down'] = drawDownDaily['draw_down'] if benchmarkReturns is not None: perf_df['benchmark_return'] = benchmarkReturns perf_df['benchmark_cum_return'] = benchmarkReturns.cumsum() perf_df.fillna(0.0, inplace=True) perf_df['benchmark_cum_return'] = np.exp(perf_df['benchmark_cum_return'] - perf_df['benchmark_cum_return'][0]) - 1.0 perf_df['access_return'] = aggregateDaily - perf_df['benchmark_return'] perf_df['access_cum_return'] = (1.0 + perf_df['daily_cum_return']) \ / (1.0 + perf_df['benchmark_cum_return']) - 1.0 accessDrawDownDaily = drawDown(perf_df['access_return']) else: accessDrawDownDaily = None if 'benchmark_cum_return' in perf_df: benchmarkCumReturns = perf_df['benchmark_cum_return'] benchmarkCumReturns.name = benchmarkReturns.name accessCumReturns = perf_df['access_cum_return'] accessReturns = perf_df['access_return'] index = perf_df.index length1 = len(bizDatesList('China.SSE', index[0], index[-1])) length2 = len(perf_df) factor = length1 / float(length2) rb = RollingBeta(perf_df['daily_return'], perf_df['benchmark_return'], [1, 3, 6], factor=factor) rs = RollingSharp(perf_df['daily_return'], [1, 3, 6], factor=factor) else: benchmarkCumReturns = None accessReturns = None accessCumReturns = None if len(perf_df['daily_return']) > APPROX_BDAYS_PER_MONTH and benchmarkCumReturns is not None: rollingRisk = pd.concat([pd.concat(rs, axis=1), pd.concat(rb, axis=1)], axis=1) else: rollingRisk = None if plot: verticalSections = 2 plt.figure(figsize=(16, 7 * verticalSections)) gs = gridspec.GridSpec(verticalSections, 3, wspace=0.5, hspace=0.5) axRollingReturns = plt.subplot(gs[0, :]) axDrawDown = plt.subplot(gs[1, :]) plottingRollingReturn(perf_df['daily_cum_return'], perf_df['daily_cum_return (w/o tc)'], benchmarkCumReturns, other_curves, axRollingReturns) plottingDrawdownPeriods(perf_df['daily_cum_return'], drawDownDaily, 5, axDrawDown) if rollingRisk is not None: plt.figure(figsize=(16, 7 * verticalSections)) gs = gridspec.GridSpec(verticalSections, 3, wspace=0.5, hspace=0.5) axRollingBeta = plt.subplot(gs[0, :]) axRollingSharp = plt.subplot(gs[1, :]) bmName = benchmarkReturns.name plottingRollingBeta(rb, bmName, ax=axRollingBeta) plottingRollingSharp(rs, ax=axRollingSharp) plt.figure(figsize=(16, 7 * verticalSections)) gs = gridspec.GridSpec(verticalSections, 3, wspace=0.5, hspace=0.5) axUnderwater = plt.subplot(gs[0, :]) axMonthlyHeatmap = plt.subplot(gs[1, 0]) axAnnualReturns = plt.subplot(gs[1, 1]) axMonthlyDist = plt.subplot(gs[1, 2]) plottingUnderwater(drawDownDaily['draw_down'], axUnderwater) plottingMonthlyReturnsHeapmap(aggregateDaily, axMonthlyHeatmap) plottingAnnualReturns(aggregateDaily, axAnnualReturns) plottingMonthlyRetDist(aggregateDaily, axMonthlyDist) if accessReturns is not None and plot: plt.figure(figsize=(16, 7 * verticalSections)) gs = gridspec.GridSpec(verticalSections, 3, wspace=0.5, hspace=0.5) axRollingAccessReturns = plt.subplot(gs[0, :]) axAccessDrawDown = plt.subplot(gs[1, :], sharex=axRollingAccessReturns) plottingRollingReturn(accessCumReturns, None, None, None, axRollingAccessReturns, title='Access Cumulative Returns w.r.t. ' + benchmarkReturns.name) plottingDrawdownPeriods(accessCumReturns, accessDrawDownDaily, 5, axAccessDrawDown, title=('Top 5 Drawdown periods w.r.t. ' + benchmarkReturns.name)) plt.figure(figsize=(16, 7 * verticalSections)) gs = gridspec.GridSpec(verticalSections, 3, wspace=0.5, hspace=0.5) axAccessUnderwater = plt.subplot(gs[0, :]) axAccessMonthlyHeatmap = plt.subplot(gs[1, 0]) axAccessAnnualReturns = plt.subplot(gs[1, 1]) axAccessMonthlyDist = plt.subplot(gs[1, 2]) plottingUnderwater(accessDrawDownDaily['draw_down'], axAccessUnderwater, title='Underwater Plot w.r.t. ' + benchmarkReturns.name) plottingMonthlyReturnsHeapmap(accessReturns, ax=axAccessMonthlyHeatmap, title='Monthly Access Returns (%)') plottingAnnualReturns(accessReturns, ax=axAccessAnnualReturns, title='Annual Access Returns') plottingMonthlyRetDist(accessReturns, ax=axAccessMonthlyDist, title='Distribution of Monthly Access Returns') return perf_metric, perf_df, rollingRisk