def writejson(back_data, budget, baseline_data, base_index): daily_return = back_data['DAILY_PNL'] / budget total_return = back_data['TOTAL_PNL'] / budget stats = metrics(daily_return, total_return, baseline_data, base_index) # multiply by 100 for readability purposes daily_return_percent = daily_return * 100 total_return_percent = total_return * 100 d = {'dates': back_data['DAILY_PNL'].index.format(), 'daily_pnl': daily_return_percent.sum(axis=1).values.tolist(), 'total_pnl': total_return_percent.sum(axis=1).values.tolist(), 'stocks': back_data['DAILY_PNL'].columns.tolist(), 'stock_pnl': daily_return_percent.values.tolist(), 'stock_position': back_data['POSITION'].values.tolist(), 'metrics': stats.keys(), 'metrics_values': stats.values()} return d
def loadgui(back_data, exchange, base_index, budget, logger): ###################### # Setup data ###################### position = back_data['POSITION'] close = back_data['CLOSE'] # position as % of total portfolio long_position = (position * close).div(back_data['VALUE'], axis=0) short_position = long_position.copy() long_position[long_position < 0] = 0 short_position[short_position > 0] = 0 daily_pnl = back_data['DAILY_PNL'] / budget total_pnl = back_data['TOTAL_PNL'] / budget if base_index: baseline_data = baseline(exchange, base_index, total_pnl.index, logger) stats = metrics(daily_pnl, total_pnl, baseline_data, base_index) else: baseline_data = {} stats = metrics(daily_pnl, total_pnl, {}, base_index) daily_return = daily_pnl.sum(axis=1) total_return = total_pnl.sum(axis=1) long_exposure = long_position.sum(axis=1) short_exposure = short_position.sum(axis=1) zero_line = np.zeros(daily_pnl.index.size) # print to logger for x in stats.keys(): logger.info('%s : %0.2f' % (x, stats[x])) def isDate(val): # Function to validate if a given entry is valid date try: d = pd.to_datetime(val) if d > daily_pnl.index[0] and d < daily_pnl.index[-1]: return True else: return False except ValueError: raise ValueError("Not a Valid Date") return False def newselection(event): # Function to autoupdate chart on new selection from dropdown i = dropdown.current() market = ['TOTAL PORTFOLIO'] + daily_pnl.columns.values.tolist() plot(daily_pnl, total_pnl, long_position, short_position, baseline_data, base_index, market[i], box_value2.get(), box_value3.get()) def plot(daily_pnl, total_pnl, long_position, short_position, baseline_data, base_index, market='TOTAL PORTFOLIO', start=daily_pnl.index.format()[0], end=daily_pnl.index.format()[-1]): # New plot when custom fields are changed plt.clf() # plt.style.use("seaborn-whitegrid") daily_pnl = daily_pnl.loc[start:end] total_pnl = total_pnl.loc[start:end] long_position = long_position.loc[start:end] short_position = short_position.loc[start:end] if market == 'TOTAL PORTFOLIO': daily_return = daily_pnl.sum(axis=1) total_return = total_pnl.sum(axis=1) long_exposure = long_position.sum(axis=1) short_exposure = short_position.sum(axis=1) else: daily_return = daily_pnl[market] total_return = total_pnl[market] long_exposure = long_position[market] short_exposure = short_position[market] zero_line = np.zeros(daily_pnl.index.size) # f, plot_arr = plt.subplots(3, sharex=True) total_plot = plt.subplot2grid((10, 8), (0, 0), colspan=12, rowspan=4) daily_plot = plt.subplot2grid((10, 8), (5, 0), colspan=12, rowspan=2, sharex=total_plot) position_plot = plt.subplot2grid((10, 8), (8, 0), colspan=12, rowspan=2, sharex=total_plot) ind = np.arange(len(daily_pnl.index)) total_plot.set_title('Total PnL') total_plot.plot(ind, zero_line, 'k') total_plot.plot(ind, total_return.values, 'b', linewidth=0.5, label='strategy') total_plot.legend(loc='upper left') total_plot.autoscale(tight=True) plt.setp(total_plot.get_xticklabels(), visible=False) total_plot.yaxis.set_major_formatter(mtick.FuncFormatter(format_perc)) total_plot.set_ylabel('Cumulative Performance') total_plot.legend(bbox_to_anchor=(0.03, 0.97), loc='lower left', borderaxespad=0.) if base_index: total_plot.plot(ind, baseline_data['TOTAL_PNL'], 'g', linewidth=0.5, label=base_index) daily_plot.set_title('Daily PnL') daily_plot.plot(ind, zero_line, 'k') daily_plot.bar(ind, daily_return.values, 0.2, align='center', color='c', label='strategy') daily_plot.legend(loc='upper left') daily_plot.autoscale(tight=True) plt.setp(daily_plot.get_xticklabels(), visible=False) daily_plot.yaxis.set_major_formatter(mtick.FuncFormatter(format_perc)) daily_plot.set_ylabel('Daily Performance') daily_plot.legend(bbox_to_anchor=(0.03, 0.97), loc='lower left', borderaxespad=0.) position_plot.set_title('Daily Exposure') position_plot.plot(ind, zero_line, 'k') position_plot.bar(ind, short_exposure.values, 0.3, linewidth=0, align='center', color='r', label='short') position_plot.bar(ind, long_exposure.values, 0.3, linewidth=0, align='center', color='b', label='long') position_plot.legend(loc='upper left') position_plot.autoscale(tight=True) position_plot.xaxis.set_major_formatter( mtick.FuncFormatter(format_date)) position_plot.yaxis.set_major_formatter( mtick.FuncFormatter(format_perc)) position_plot.set_ylabel('Long/Short %') position_plot.legend(bbox_to_anchor=(0.03, 0.97), loc='lower left', borderaxespad=0.) plt.gcf().canvas.draw() def update_plot(): # Callback Function for plot button try: d1 = pd.to_datetime(box_value2.get()) d2 = pd.to_datetime(box_value3.get()) if d1 >= daily_pnl.index[0] and d2 <= daily_pnl.index[-1]: plot(daily_pnl, total_pnl, long_position, short_position, baseline_data, base_index, box_value.get(), box_value2.get(), box_value3.get()) else: tkMessageBox.showinfo( "Date out of Range", "Please enter a date from %s to %s" % (daily_pnl.index[0].strftime('%Y-%m-%d'), daily_pnl.index[-1].strftime('%Y-%m-%d'))) except ValueError: raise ValueError("Not a Valid Date") def close_window(): # Callback function for Quit Button GUI.destroy() GUI.quit() def format_date(x, pos=None): # Format axis ticklabels to dates thisind = np.clip(int(x + 0.5), 0, len(daily_pnl.index) - 1) return daily_pnl.index[thisind].strftime('%b-%y') def format_perc(y, pos=None): # Format axis ticklabels to % if budget > 1: return '{percent:.2%}'.format(percent=y) else: return y def onFrameConfigure(canvas): canvas.configure(scrollregion=canvas.bbox("all")) ###################### # GUI mainloop ###################### # Create widget GUI = tk.Tk() GUI.title('Backtest Results') winCanvas = tk.Canvas(GUI, borderwidth=0, background="#ffffff", width=1500, height=1000) frame = tk.Frame(winCanvas, background="#ffffff") vsb = tk.Scrollbar(GUI, orient="vertical", command=winCanvas.yview) hsb = tk.Scrollbar(GUI, orient="horizontal", command=winCanvas.xview) winCanvas.configure(yscrollcommand=vsb.set) winCanvas.configure(xscrollcommand=hsb.set) vsb.pack(side="left", fill="y") hsb.pack(side="bottom", fill="x") winCanvas.pack(side="right", fill="both", expand=True) winCanvas.create_window((50, 50), window=frame, anchor="nw") frame.bind("<Configure>", lambda event, canvas=winCanvas: onFrameConfigure(winCanvas)) # Create dropdown for market Label_1 = tk.Label(frame, text="Trading Performance:") Label_1.grid(row=0, column=0, sticky=tk.EW) box_value = tk.StringVar() dropdown = ttk.Combobox(frame, textvariable=box_value, state='readonly') dropdown['values'] = ['TOTAL PORTFOLIO' ] + daily_pnl.columns.values.tolist() dropdown.grid(row=0, column=1, sticky=tk.EW) dropdown.current(0) dropdown.bind('<<ComboboxSelected>>', newselection) # Create entry field for start date Label_2 = tk.Label(frame, text="Start Date") Label_2.grid(row=0, column=2, sticky=tk.EW) box_value2 = tk.StringVar(frame, value=daily_pnl.index.format()[0]) start = tk.Entry(frame, textvariable=box_value2, validate='key', validatecommand=(GUI.register(isDate), '%P')) start.grid(row=0, column=3, sticky=tk.EW) # Create entry field for end date Label_3 = tk.Label(frame, text="End Date") Label_3.grid(row=0, column=4, sticky=tk.EW) box_value3 = tk.StringVar(frame, value=daily_pnl.index.format()[-1]) end = tk.Entry(frame, textvariable=box_value3, validate='key', validatecommand=(GUI.register(isDate), '%P')) end.grid(row=0, column=5, sticky=tk.EW) # Create Plot button to reload chart button1 = tk.Button(frame, text='PLOT', command=update_plot) button1.grid(row=0, column=6, sticky=tk.EW) # Create text widget with backtest results customFont1 = tkFont.Font(family="Helvetica", size=9, weight="bold") customFont2 = tkFont.Font(family="Helvetica", size=12) text = tk.Text(frame, height=3, width=50, wrap=tk.WORD, bd=5, padx=10, pady=5) text.grid(row=1, column=0, columnspan=7, sticky=tk.EW) String1 = '' String2 = '' for y in stats.keys(): String1 = String1 + y + '\t\t' x = stats[y] if budget > 1 and 'Ratio' not in y: String2 = String2 + '{percent:.2%}'.format(percent=x) + '\t\t' else: String2 = String2 + '%0.2f' % x + '\t\t' text.insert(tk.END, String1) text.insert(tk.END, '\n') text.insert(tk.END, String2) text.tag_add("keys", "1.0", "1.end") text.tag_config("keys", font=customFont1) text.tag_add("values", "2.0", "2.end") text.tag_config("values", foreground="red", font=customFont2) # Create canvas to plot chart f = plt.figure(figsize=(16, 8)) canvas = FigureCanvasTkAgg(f, master=frame) canvas.get_tk_widget().grid(row=2, column=0, columnspan=7, rowspan=1, sticky=tk.NSEW) toolbar_frame = tk.Frame(frame) toolbar_frame.grid(row=4, column=0, columnspan=7) # plot 3 subplots for total position, daily position and exposure plt.style.use("seaborn-whitegrid") total_plot = plt.subplot2grid((10, 8), (0, 0), colspan=12, rowspan=4) daily_plot = plt.subplot2grid((10, 8), (5, 0), colspan=12, rowspan=2, sharex=total_plot) position_plot = plt.subplot2grid((10, 8), (8, 0), colspan=12, rowspan=2, sharex=total_plot) ind = np.arange(len(daily_pnl.index)) total_plot.set_title('Total PnL') total_plot.plot(ind, zero_line, 'k') total_plot.plot(ind, total_return.values, 'b', linewidth=0.5, label='strategy') total_plot.legend(loc='upper left') total_plot.autoscale(tight=True) plt.setp(total_plot.get_xticklabels(), visible=False) total_plot.yaxis.set_major_formatter(mtick.FuncFormatter(format_perc)) total_plot.set_ylabel('Cumulative Performance') total_plot.legend(bbox_to_anchor=(0.03, 0.97), loc='lower left', borderaxespad=0.) if base_index: total_plot.plot(ind, baseline_data['TOTAL_PNL'], 'g', linewidth=0.5, label=base_index) daily_plot.set_title('Daily PnL') daily_plot.plot(ind, zero_line, 'k') daily_plot.bar(ind, daily_return.values, 0.2, align='center', color='c', label='strategy') daily_plot.legend(loc='upper left') daily_plot.autoscale(tight=True) plt.setp(daily_plot.get_xticklabels(), visible=False) daily_plot.yaxis.set_major_formatter(mtick.FuncFormatter(format_perc)) daily_plot.set_ylabel('Daily Performance') daily_plot.legend(bbox_to_anchor=(0.03, 0.97), loc='lower left', borderaxespad=0.) position_plot.set_title('Daily Exposure') position_plot.plot(ind, zero_line, 'k') position_plot.bar(ind, short_exposure.values, 0.3, linewidth=0, align='center', color='r', label='short') position_plot.bar(ind, long_exposure.values, 0.3, linewidth=0, align='center', color='b', label='long') position_plot.legend(loc='upper left') position_plot.autoscale(tight=True) position_plot.xaxis.set_major_formatter(mtick.FuncFormatter(format_date)) position_plot.yaxis.set_major_formatter(mtick.FuncFormatter(format_perc)) position_plot.set_ylabel('Long/Short') position_plot.legend(bbox_to_anchor=(0.03, 0.97), loc='lower left', borderaxespad=0.) plt.gcf().canvas.draw() # Create Quit Button button2 = tk.Button(frame, text='QUIT', command=close_window) button2.grid(row=4, column=6, sticky=tk.EW) GUI.mainloop()