def plot_rets(self, ls=1, ax=None): """Plot each of the position returns :param ls: True, if positions should be broken into long/short :param ax: Axes :param regr: True, if regression line is shown """ import matplotlib.pyplot as plt from tia.util.mplot import AxesFormat if ax is None: ax = plt.gca() frame = self.frame if not ls: ax.scatter(frame.index, frame.ret, c="k", marker="o", label="All") else: if len(self.long_pids) > 0: lframe = frame.ix[frame.index.isin(self.long_pids)] ax.scatter(lframe.index, lframe.ret, c="k", marker="o", label="Long") if len(self.short_pids) > 0: sframe = frame.ix[frame.index.isin(self.short_pids)] ax.scatter(sframe.index, sframe.ret, c="r", marker="o", label="Short") # set some boundaries AxesFormat().Y.percent().apply() ax.set_xlim(0, frame.index.max() + 3) ax.set_xlabel("pid") ax.set_ylabel("return") ax.legend(loc="upper left") return ax
def plot_ltd(self, ax=None, style='k', label='ltd', show_dd=1, title=True, legend=1): ltd = self.ltd_rets ax = ltd.plot(ax=ax, style=style, label=label) if show_dd: dd = self.drawdowns dd.plot(style='r', label='drawdowns', alpha=.5, ax=ax) ax.fill_between(dd.index, 0, dd.values, facecolor='red', alpha=.25) fmt = PercentFormatter AxesFormat().Y.percent().X.label("").apply(ax) legend and ax.legend(loc='upper left', prop={'size': 12}) # show the actualy date and value mdt, mdd = self.maxdd_dt, self.maxdd bbox_props = dict(boxstyle="round", fc="w", ec="0.5", alpha=0.25) try: dtstr = '{0}'.format(mdt.to_period()) except: # assume daily dtstr = '{0}'.format(hasattr(mdt, 'date') and mdt.date() or mdt) ax.text(mdt, dd[mdt], "{1} \n {0}".format(fmt(mdd), dtstr).strip(), ha="center", va="top", size=8, bbox=bbox_props) if title is True: pf = new_percent_formatter(1, parens=False, trunc_dot_zeros=True) ff = new_float_formatter(precision=1, parens=False, trunc_dot_zeros=True) total = pf(self.ltd_ann) vol = pf(self.std_ann) sh = ff(self.sharpe_ann) mdd = pf(self.maxdd) title = 'ret$\mathregular{_{ann}}$ %s vol$\mathregular{_{ann}}$ %s sharpe %s maxdd %s' % ( total, vol, sh, mdd) title and ax.set_title(title, fontdict=dict(fontsize=10, fontweight='bold')) return ax
def plot_hist(self, ax=None, **histplot_kwargs): pf = new_percent_formatter(precision=1, parens=False, trunc_dot_zeros=1) ff = new_float_formatter(precision=1, parens=False, trunc_dot_zeros=1) ax = self.rets.hist(ax=ax, **histplot_kwargs) AxesFormat().X.percent(1).apply(ax) m, s, sk, ku = pf(self.mean), pf(self.std), ff(self.skew), ff( self.kurtosis) txt = ( "$\mathregular{\mu}$=%s $\mathregular{\sigma}$=%s skew=%s kurt=%s" % (m, s, sk, ku)) bbox = dict(facecolor="white", alpha=0.5) ax.text( 0, 1, txt, fontdict={"fontweight": "bold"}, bbox=bbox, ha="left", va="top", transform=ax.transAxes, ) return ax
def plot_ltd(self, ax=None, style="k", label="ltd", show_dd=1, guess_xlabel=1, title=True): ltd = self.ltd_frame.pl ax = ltd.plot(ax=ax, style=style, label=label) if show_dd: dd = self.drawdowns dd.plot(style="r", label="drawdowns", alpha=0.5) ax.fill_between(dd.index, 0, dd.values, facecolor="red", alpha=0.25) fmt = lambda x: x # guess the formatter if guess_xlabel: from tia.util.fmt import guess_formatter from tia.util.mplot import AxesFormat fmt = guess_formatter(ltd.abs().max(), precision=1) AxesFormat().Y.apply_format(fmt).apply(ax) ax.legend(loc="upper left", prop={"size": 12}) # show the actualy date and value mdt, mdd = self.maxdd_dt, self.maxdd bbox_props = dict(boxstyle="round", fc="w", ec="0.5", alpha=0.25) try: dtstr = "{0}".format(mdt.to_period()) except: # assume daily dtstr = "{0}".format( hasattr(mdt, "date") and mdt.date() or mdt) ax.text( mdt, dd[mdt], "{1} \n {0}".format(fmt(mdd), dtstr).strip(), ha="center", va="top", size=8, bbox=bbox_props, ) if title is True: df = new_dynamic_formatter(precision=1, parens=False, trunc_dot_zeros=True) total = df(ltd.iloc[-1]) vol = df(self.std) mdd = df(self.maxdd) title = "pnl %s vol %s maxdd %s" % (total, vol, mdd) title and ax.set_title(title, fontdict=dict(fontsize=10, fontweight="bold")) return ax
def plot_ret_range(self, ax=None, ls=0, dur=0): """Plot the return range for each position :param ax: Axes """ import matplotlib.pyplot as plt from tia.util.mplot import AxesFormat if ax is None: ax = plt.gca() frame = self.frame pids = frame.index min_rets = pd.Series( [self[pid].performance.ltd_txn.min() for pid in pids], index=pids ) max_rets = pd.Series( [self[pid].performance.ltd_txn.max() for pid in pids], index=pids ) if not ls: s = frame.duration + 20 if dur else 20 ax.scatter(frame.index, frame.ret, s=s, c="k", marker="o", label="All") ax.vlines(pids, min_rets, max_rets) else: if len(self.long_pids) > 0: lframe = frame.ix[frame.index.isin(self.long_pids)] s = lframe.duration + 20 if dur else 20 ax.scatter( lframe.index, lframe.ret, s=s, c="k", marker="o", label="Long" ) ax.vlines(lframe.index, min_rets[lframe.index], max_rets[frame.index]) if len(self.short_pids) > 0: sframe = frame.ix[frame.index.isin(self.short_pids)] s = sframe.duration + 20 if dur else 20 ax.scatter( sframe.index, sframe.ret, s=s, c="r", marker="o", label="Short" ) ax.vlines(sframe.index, min_rets[sframe.index], max_rets[sframe.index]) AxesFormat().Y.percent().apply() ax.axhline(color="k", linestyle="--") ax.set_xlim(0, frame.index.max() + 3) ax.set_xlabel("pid") ax.set_ylabel("return") ax.legend(loc="upper left") return ax
def plot_return_on_dollar(rets, title='Return on $1', show_maxdd=0, figsize=None, ax=None, append=0, label=None, **plot_args): """ Show the cumulative return of specified rets and max drawdowns if selected.""" crets = (1. + returns_cumulative(rets, expanding=1)) if isinstance(crets, pd.DataFrame): tmp = crets.copy() for c in tmp.columns: s = tmp[c] fv = s.first_valid_index() fi = s.index.get_loc(fv) if fi != 0: tmp.ix[fi - 1, c] = 1. else: if not s.index.freq: # no frequency set freq = guess_freq(s.index) s = s.asfreq(freq) first = s.index.shift(-1)[0] tmp = pd.concat([pd.DataFrame({c: [1.]}, index=[first]), tmp]) crets = tmp if append: toadd = crets.index.shift(1)[-1] crets = pd.concat([crets, pd.DataFrame(np.nan, columns=crets.columns, index=[toadd])]) else: fv = crets.first_valid_index() fi = crets.index.get_loc(fv) if fi != 0: crets = crets.copy() crets.iloc[fi - 1] = 1. else: if not crets.index.freq: first = crets.asfreq(guess_freq(crets.index)).index.shift(-1)[0] else: first = crets.index.shift(-1)[0] tmp = pd.Series([1.], index=[first]) tmp = tmp.append(crets) crets = tmp if append: toadd = pd.Series(np.nan, index=[crets.index.shift(1)[-1]]) crets = crets.append(toadd) ax = crets.plot(figsize=figsize, title=title, ax=ax, label=label, **plot_args) AxesFormat().Y.apply_format(new_float_formatter()).X.label("").apply(ax) #ax.tick_params(labelsize=14) if show_maxdd: # find the max drawdown available by using original rets if isinstance(rets, pd.DataFrame): iterator = rets.iteritems() else: iterator = iter([('', rets)]) for c, col in iterator: dd, dt = max_drawdown(col, inc_date=1) lbl = c and c + ' maxdd' or 'maxdd' # get cret to place annotation correctly if isinstance(crets, pd.DataFrame): amt = crets.ix[dt, c] else: amt = crets[dt] bbox_props = dict(boxstyle="round", fc="w", ec="0.5", alpha=0.7) # sub = lambda c: c and len(c) > 2 and c[:2] or c try: dtstr = '{0}'.format(dt.to_period()) except: dtstr = '{0}'.format(dt) ax.text(dt, amt, "mdd {0}".format(dtstr).strip(), ha="center", va="center", size=10, bbox=bbox_props) plt.tight_layout()
def add_position_page(self, result): def dofmt(t): t.apply_basic_style(cmap=self.table_style) [ row.guess_format(pcts=1, trunc_dot_zeros=1) for row in t.cells.iter_rows() ] ncols = len(t.formatted_values.columns) t.set_col_widths(pcts=[1. / ncols] * ncols) return t def do_rename(df): d = { 'consecutive_win_cnt_max': 'win_streak', 'consecutive_loss_cnt_max': 'lose_streak' } return df.rename(index=lambda c: d.get(c, c)) pdf = self.pdf figures = self.figures port = result.port buyhold = result.buyhold sframe = pd.DataFrame({ 'all': port.positions.stats.series, 'long': port.long.positions.stats.series, 'short': port.short.positions.stats.series }) tf = pdf.table_formatter(insert_level(sframe, 'Position', copy=True)) stable = dofmt(tf).build() s = PortfolioSummary() s.include_long_short().include_win_loss() dframe = s(port, PortfolioSummary.analyze_returns).T.ix['pos'] tf = pdf.table_formatter( do_rename(insert_level(dframe, 'Position', copy=True))) dtable = dofmt(tf).build() # Plot Position Returns f, ax = self.create_ax() port.positions.plot_rets(ax=ax) plt.tight_layout() figures.savefig(key='pos_ls', clear=1) # Plot Position Ranges f, ax = self.create_ax(figsize=(8, 3)) port.positions.plot_ret_range(ls=1, dur=1, ax=ax) plt.tight_layout() figures.savefig(key='pos_rng', clear=1) # Plot Long Short Positions with regression line tmp = port.position_frame[['side', 'ret']].reset_index() g = sns.lmplot("pid", "ret", col="side", hue="side", data=tmp, size=3) AxesFormat().Y.percent().apply() figures.savefig(key='pos_ls', clear=1) # Plot Return vs Duration tmp = port.position_frame[['ret', 'duration', 'side']] diag_kws = {} if len(port.position_frame.index) <= 1: diag_kws = {'range': (-100, 100)} sns.pairplot(tmp, hue="side", size=3, diag_kws=diag_kws) figures.savefig(key='pos_pair', clear=1) toimg = lambda path: rlab.new_dynamic_image(path) itms = { 'F1': toimg(figures['pos_rng']), 'F3': toimg(figures['pos_ls']), 'F2': toimg(figures['pos_pair']), 'F5': stable, 'F6': dtable, 'HDR': self.title_bar('{0} - {1} - position summary'.format( result.sid, result.desc)) } pdf.build_page('positions', itms)
def add_portfolio_page(self, result): def alpha_beta(p, bm): model = pd.ols(x=bm.rets, y=p.rets) beta = model.beta[0] alpha = p.total_ann - beta * bm.total_ann s = pd.Series({'alpha': alpha, 'beta': beta}) return s def rs(port1, port2, kind='dly_ret_stats'): stats = getattr(port1, kind) ab = alpha_beta(stats, getattr(port2, kind)) tmp = stats.series.append(ab) tmp.name = stats.series.name return tmp def dofmt(t): t.apply_basic_style(cmap=self.table_style) [ row.guess_format(pcts=1, trunc_dot_zeros=1) for row in t.cells.iter_rows() ] ncols = len(t.formatted_values.columns) t.set_col_widths(pcts=[1. / ncols] * ncols) def do_rename(df): d = { 'consecutive_win_cnt_max': 'win_streak', 'consecutive_loss_cnt_max': 'lose_streak' } return df.rename(index=lambda c: d.get(c, c)) # Build the pdf tables pdf = self.pdf figures = self.figures port = result.port buyhold = result.buyhold sframe = pd.DataFrame([ rs(port, buyhold, 'dly_ret_stats'), rs(port, buyhold, 'weekly_ret_stats'), rs(port, buyhold, 'monthly_ret_stats'), rs(port, buyhold, 'quarterly_ret_stats') ]).T tf = pdf.table_formatter(insert_level(sframe, 'Portfolio', copy=True)) dofmt(tf) stable = tf.build() s = PortfolioSummary() s.include_long_short().include_win_loss() dframe = s(port, PortfolioSummary.analyze_returns).T tf = pdf.table_formatter( do_rename(insert_level(dframe.ix['port'], 'Portfolio', copy=True))) dofmt(tf) dtable = tf.build() # Return on $1 image f, ax = self.create_ax() buyhold.plot_ret_on_dollar('B', label='Buy & Hold', ax=ax) port.plot_ret_on_dollar('B', label=result.desc, ax=ax, color='k') ax.legend(loc='upper left') ax.set_title('vs Buy & Hold') plt.tight_layout() figures.savefig(key='buyhold', clear=1) # Drawdown image f, ax = self.create_ax() port.dly_ret_stats.plot_ltd(ax=ax) plt.tight_layout() figures.savefig(key='dd', clear=1) # Long / Short Returns f, ax = self.create_ax() port.plot_ret_on_dollar('B', label='All', color='k', ax=ax) port.long.plot_ret_on_dollar('B', label='Long', ax=ax) port.short.plot_ret_on_dollar('B', label='Short', ax=ax) ax.legend(loc='upper left') figures.savefig(key='ls', clear=1) # Sharpe / Ann Vol f, ax = self.create_ax() perf.sharpe_annualized(port.monthly_rets, expanding=1).iloc[3:].plot(ax=ax, color='k', label='sharpe') ax.set_ylabel('sharpe ann', color='k') ax2 = ax.twinx() perf.std_annualized(port.monthly_rets, expanding=1).iloc[3:].plot(ax=ax2, label='vol', color='b', alpha=1) ax2.set_ylabel('vol ann', color='b') plt.tight_layout() figures.savefig(key='sharpe', clear=1) # Monthly Returns Bar Chart f, ax = self.create_ax() tmp = pd.DataFrame({ 'All': port.monthly_rets.to_period('M'), 'Long': port.long.monthly_rets.to_period('M'), 'Short': port.short.monthly_rets.to_period('M') }) tmp.plot(kind='bar', ax=ax, color=['k', self.long_color, self.short_color]) AxesFormat().Y.percent().X.rotate().apply() plt.tight_layout() ax.set_title('Monthly Returns') figures.savefig(key='mrets', clear=1) # Monthly Returns Box Plot f, ax = self.create_ax() sns.boxplot(tmp, ax=ax, color=['gray', self.long_color, self.short_color]) ax.set_title('Monthly Returns') AxesFormat().Y.percent().apply() plt.tight_layout() figures.savefig(key='mrets_box', clear=1) # Build the PDF Page toimg = lambda path: rlab.new_dynamic_image(path) itms = { 'F1': toimg(figures['buyhold']), 'F2': toimg(figures['dd']), 'F3': toimg(figures['ls']), 'F4': toimg(figures['mrets']), 'F5': toimg(figures['sharpe']), 'F6': toimg(figures['mrets_box']), 'F7': stable, 'F8': dtable, 'HDR': self.title_bar('{0} - {1} - portfolio summary'.format( result.sid, result.desc)) } pdf.build_page('portfolio', itms)
def add_position_page(self, result): def dofmt(t): t.apply_basic_style(cmap=self.table_style) [ row.guess_format(pcts=1, trunc_dot_zeros=1) for row in t.cells.iter_rows() ] ncols = len(t.formatted_values.columns) t.set_col_widths(pcts=[1.0 / ncols] * ncols) return t def do_rename(df): d = { "consecutive_win_cnt_max": "win_streak", "consecutive_loss_cnt_max": "lose_streak", } return df.rename(index=lambda c: d.get(c, c)) pdf = self.pdf figures = self.figures port = result.port buyhold = result.buyhold sframe = pd.DataFrame({ "all": port.positions.stats.series, "long": port.long.positions.stats.series, "short": port.short.positions.stats.series, }) tf = pdf.table_formatter(insert_level(sframe, "Position", copy=True)) stable = dofmt(tf).build() s = PortfolioSummary() s.include_long_short().include_win_loss() dframe = s(port, PortfolioSummary.analyze_returns).T.ix["pos"] tf = pdf.table_formatter( do_rename(insert_level(dframe, "Position", copy=True))) dtable = dofmt(tf).build() # Plot Position Returns f, ax = self.create_ax() port.positions.plot_rets(ax=ax) plt.tight_layout() figures.savefig(key="pos_ls", clear=1) # Plot Position Ranges f, ax = self.create_ax(figsize=(8, 3)) port.positions.plot_ret_range(ls=1, dur=1, ax=ax) plt.tight_layout() figures.savefig(key="pos_rng", clear=1) # Plot Long Short Positions with regression line tmp = port.position_frame[["side", "ret"]].reset_index() g = sns.lmplot("pid", "ret", col="side", hue="side", data=tmp, size=3) AxesFormat().Y.percent().apply() figures.savefig(key="pos_ls", clear=1) # Plot Return vs Duration tmp = port.position_frame[["ret", "duration", "side"]] diag_kws = {} if len(port.position_frame.index) <= 1: diag_kws = {"range": (-100, 100)} sns.pairplot(tmp, hue="side", size=3, diag_kws=diag_kws) figures.savefig(key="pos_pair", clear=1) toimg = lambda path: rlab.new_dynamic_image(path) itms = { "F1": toimg(figures["pos_rng"]), "F3": toimg(figures["pos_ls"]), "F2": toimg(figures["pos_pair"]), "F5": stable, "F6": dtable, "HDR": self.title_bar("{0} - {1} - position summary".format( result.sid, result.desc)), } pdf.build_page("positions", itms)
def add_portfolio_page(self, result): def alpha_beta(p, bm): model = pd.ols(x=bm.rets, y=p.rets) beta = model.beta[0] alpha = p.total_ann - beta * bm.total_ann s = pd.Series({"alpha": alpha, "beta": beta}) return s def rs(port1, port2, kind="dly_ret_stats"): stats = getattr(port1, kind) ab = alpha_beta(stats, getattr(port2, kind)) tmp = stats.series.append(ab) tmp.name = stats.series.name return tmp def dofmt(t): t.apply_basic_style(cmap=self.table_style) [ row.guess_format(pcts=1, trunc_dot_zeros=1) for row in t.cells.iter_rows() ] ncols = len(t.formatted_values.columns) t.set_col_widths(pcts=[1.0 / ncols] * ncols) def do_rename(df): d = { "consecutive_win_cnt_max": "win_streak", "consecutive_loss_cnt_max": "lose_streak", } return df.rename(index=lambda c: d.get(c, c)) # Build the pdf tables pdf = self.pdf figures = self.figures port = result.port buyhold = result.buyhold sframe = pd.DataFrame([ rs(port, buyhold, "dly_ret_stats"), rs(port, buyhold, "weekly_ret_stats"), rs(port, buyhold, "monthly_ret_stats"), rs(port, buyhold, "quarterly_ret_stats"), ]).T tf = pdf.table_formatter(insert_level(sframe, "Portfolio", copy=True)) dofmt(tf) stable = tf.build() s = PortfolioSummary() s.include_long_short().include_win_loss() dframe = s(port, PortfolioSummary.analyze_returns).T tf = pdf.table_formatter( do_rename(insert_level(dframe.ix["port"], "Portfolio", copy=True))) dofmt(tf) dtable = tf.build() # Return on $1 image f, ax = self.create_ax() buyhold.plot_ret_on_dollar("B", label="Buy & Hold", ax=ax) port.plot_ret_on_dollar("B", label=result.desc, ax=ax, color="k") ax.legend(loc="upper left") ax.set_title("vs Buy & Hold") plt.tight_layout() figures.savefig(key="buyhold", clear=1) # Drawdown image f, ax = self.create_ax() port.dly_ret_stats.plot_ltd(ax=ax) plt.tight_layout() figures.savefig(key="dd", clear=1) # Long / Short Returns f, ax = self.create_ax() port.plot_ret_on_dollar("B", label="All", color="k", ax=ax) port.long.plot_ret_on_dollar("B", label="Long", ax=ax) port.short.plot_ret_on_dollar("B", label="Short", ax=ax) ax.legend(loc="upper left") figures.savefig(key="ls", clear=1) # Sharpe / Ann Vol f, ax = self.create_ax() perf.sharpe_annualized(port.monthly_rets, expanding=1).iloc[3:].plot(ax=ax, color="k", label="sharpe") ax.set_ylabel("sharpe ann", color="k") ax2 = ax.twinx() perf.std_annualized(port.monthly_rets, expanding=1).iloc[3:].plot(ax=ax2, label="vol", color="b", alpha=1) ax2.set_ylabel("vol ann", color="b") plt.tight_layout() figures.savefig(key="sharpe", clear=1) # Monthly Returns Bar Chart f, ax = self.create_ax() tmp = pd.DataFrame({ "All": port.monthly_rets.to_period("M"), "Long": port.long.monthly_rets.to_period("M"), "Short": port.short.monthly_rets.to_period("M"), }) tmp.plot(kind="bar", ax=ax, color=["k", self.long_color, self.short_color]) AxesFormat().Y.percent().X.rotate().apply() plt.tight_layout() ax.set_title("Monthly Returns") figures.savefig(key="mrets", clear=1) # Monthly Returns Box Plot f, ax = self.create_ax() sns.boxplot(tmp, ax=ax, color=["gray", self.long_color, self.short_color]) ax.set_title("Monthly Returns") AxesFormat().Y.percent().apply() plt.tight_layout() figures.savefig(key="mrets_box", clear=1) # Build the PDF Page toimg = lambda path: rlab.new_dynamic_image(path) itms = { "F1": toimg(figures["buyhold"]), "F2": toimg(figures["dd"]), "F3": toimg(figures["ls"]), "F4": toimg(figures["mrets"]), "F5": toimg(figures["sharpe"]), "F6": toimg(figures["mrets_box"]), "F7": stable, "F8": dtable, "HDR": self.title_bar("{0} - {1} - portfolio summary".format( result.sid, result.desc)), } pdf.build_page("portfolio", itms)