def plot_strategy_group_benchmark_pnl_yoy(self, strip = None, silent_plot = False): style = self.create_style("", "Group Benchmark PnL YoY - cumulative") keys = self._strategy_group_benchmark_ret_stats.keys() yoy = [] for key in keys: col = self._strategy_group_benchmark_ret_stats[key].yoy_rets() col.columns = [key] yoy.append(col) calculations = Calculations() ret_stats = calculations.pandas_outer_join(yoy) ret_stats.index = ret_stats.index.year if strip is not None: ret_stats.columns = [k.replace(strip, '') for k in ret_stats.columns] # ret_stats = ret_stats.sort_index() style.file_output = self.DUMP_PATH + self.FINAL_STRATEGY + ' (Group Benchmark PnL - YoY) ' + str(style.scale_factor) + '.png' style.html_file_output = self.DUMP_PATH + self.FINAL_STRATEGY + ' (Group Benchmark PnL - YoY) ' + str(style.scale_factor) + '.html' style.display_brand_label = False style.date_formatter = "%Y" chart = Chart(ret_stats * 100, engine=self.DEFAULT_PLOT_ENGINE, chart_type='bar', style=style) if not (silent_plot): chart.plot() return chart
def plot_strategy_group_leverage(self, silent_plot = False): style = self.create_style("Leverage", "Group Leverage") chart = Chart(self.reduce_plot(self._strategy_group_leverage), engine=self.DEFAULT_PLOT_ENGINE, chart_type='line', style=style) if not (silent_plot): chart.plot() return chart
def plot_strategy_signal_proportion(self, strip = None, silent_plot = False): signal = self._strategy_signal ####### count number of long, short and flat periods in our sample long = signal[signal > 0].count() short = signal[signal < 0].count() flat = signal[signal == 0].count() df = pandas.DataFrame(index = long.index, columns = ['Long', 'Short', 'Flat']) df['Long'] = long df['Short'] = short df['Flat'] = flat if strip is not None: df.index = [k.replace(strip, '') for k in df.index] style = self.create_style("", "") try: style.file_output = self.DUMP_PATH + self.FINAL_STRATEGY + ' (Strategy signal proportion).png' style.html_file_output = self.DUMP_PATH + self.FINAL_STRATEGY + ' (Strategy signal proportion).html' chart = Chart(self.reduce_plot(df), engine=self.DEFAULT_PLOT_ENGINE, chart_type='bar', style=style) if not(silent_plot): chart.plot() return chart except: pass
def plot_strategy_pnl(self, silent_plot = False): style = self.create_style("", "Strategy PnL") try: chart = Chart(self.reduce_plot(self._strategy_pnl), engine=self.DEFAULT_PLOT_ENGINE, chart_type='line', style=style) if not(silent_plot): chart.plot() return chart except: pass
def plot_individual_leverage(self, silent_plot=False): style = self.create_style("Leverage", "Individual Leverage") try: chart = Chart(self.reduce_plot(self._individual_leverage), engine=self.DEFAULT_PLOT_ENGINE, chart_type='line', style=style) if not(silent_plot): chart.plot() return chart except: pass
def _plot_signal(self, sig, label = ' ', caption = '', date = None, strip = None, silent_plot = False): ######## plot signals strategy_signal = 100 * (sig) last_day = self.grab_signals(strategy_signal, date=date, strip=strip) style = self.create_style(label, caption) chart = Chart(last_day, engine=self.DEFAULT_PLOT_ENGINE, chart_type='bar', style=style) if not (silent_plot): chart.plot() return chart
def plot_strategy_group_benchmark_annualised_pnl(self, cols = None, silent_plot = False): # TODO - unfinished, needs checking! if cols is None: cols = self._strategy_group_benchmark_annualised_pnl.columns style = self.create_style("", "Group Benchmark Annualised PnL") style.color = ['red', 'blue', 'purple', 'gray', 'yellow', 'green', 'pink'] chart = Chart(self.reduce_plot(self._strategy_group_benchmark_annualised_pnl[cols]), engine=self.DEFAULT_PLOT_ENGINE, chart_type='line', style=style) if not (silent_plot): chart.plot() return chart
def plot_strategy_group_benchmark_pnl(self, silent_plot = False): style = self.create_style("", "Group Benchmark PnL - cumulative") strat_list = self._strategy_group_benchmark_pnl.columns #.sort_values() for line in strat_list: self.logger.info(line) # plot cumulative line of returns chart = Chart(self.reduce_plot(self._strategy_group_benchmark_pnl), engine=self.DEFAULT_PLOT_ENGINE, chart_type='line', style=style) if not (silent_plot): chart.plot() return chart
def plot_strategy_group_pnl_trades(self, silent_plot = False): style = self.create_style("(bp)", "Individual Trade PnL") # zero when there isn't a trade exit # strategy_pnl_trades = self._strategy_pnl_trades * 100 * 100 # strategy_pnl_trades = strategy_pnl_trades.dropna() # note only works with single large basket trade try: strategy_pnl_trades = self._strategy_pnl_trades.fillna(0) * 100 * 100 chart = Chart(self.reduce_plot(strategy_pnl_trades), engine=self.DEFAULT_PLOT_ENGINE, chart_type='line', style=style) if not(silent_plot): chart.plot() return chart except: pass
def plot_single_var_regression(self, y, x, y_variable_names, x_variable_names, statistic, tag = 'stats', title = None, pretty_index = None, output_path = None, scale_factor = None, silent_plot = False, shift=[0]): if not(isinstance(statistic, list)): statistic = [statistic] # TODO optimise loop so that we are calculating each regression *once* at present calculating it # for each statistic, which is redundant for st in statistic: stats_df = [] for sh in shift: x_sh = x.shift(sh) stats_temp = self.report_single_var_regression(y, x_sh, y_variable_names, x_variable_names, st, pretty_index) stats_temp.columns = [ x + "_" + str(sh) for x in stats_temp.columns] stats_df.append(stats_temp) stats_df = pandas.concat(stats_df, axis=1) stats_df = stats_df.dropna(how='all') if silent_plot: return stats_df chart = Chart() style = Style() if title is None: title = statistic style.title = title style.display_legend = True style.scale_factor = scale_factor # style.color = ['red', 'blue', 'purple', 'gray', 'yellow', 'green', 'pink'] if output_path is not None: style.file_output = output_path + ' (' + tag + ' ' + st + ').png' chart.plot(stats_df, style=style) return stats_df
def plot_strategy_group_benchmark_pnl_ir(self, strip = None, silent_plot = False): style = self.create_style("", "Group Benchmark PnL IR - cumulative") keys = self._strategy_group_benchmark_ret_stats.keys() ir = [] for key in keys: ir.append(self._strategy_group_benchmark_ret_stats[key].inforatio()[0]) if strip is not None: keys = [k.replace(strip, '') for k in keys] ret_stats = pandas.DataFrame(index=keys, data=ir, columns=['IR']) # ret_stats = ret_stats.sort_index() style.file_output = self.DUMP_PATH + self.FINAL_STRATEGY + ' (Group Benchmark PnL - IR) ' + str(style.scale_factor) + '.png' style.html_file_output = self.DUMP_PATH + self.FINAL_STRATEGY + ' (Group Benchmark PnL - IR) ' + str(style.scale_factor) + '.html' style.display_brand_label = False chart = Chart(ret_stats, engine=self.DEFAULT_PLOT_ENGINE, chart_type='bar', style=style) if not (silent_plot): chart.plot() return chart
def plot_strategy_trade_no(self, strip = None, silent_plot = False): signal = self._strategy_signal ####### how many trades have there been (ignore size of the trades) trades = abs(signal - signal.shift(-1)) trades = trades[trades > 0].count() df_trades = pandas.DataFrame(index=trades.index, columns=['Trades'], data=trades) if strip is not None: df_trades.index = [k.replace(strip, '') for k in df_trades.index] style = self.create_style("", "") try: style.file_output = self.DUMP_PATH + self.FINAL_STRATEGY + ' (Strategy trade no).png' style.html_file_output = self.DUMP_PATH + self.FINAL_STRATEGY + ' (Strategy trade no).html' chart = Chart(self.reduce_plot(df_trades), engine=self.DEFAULT_PLOT_ENGINE, chart_type='bar', style=style) if not(silent_plot): chart.plot() return chart except: pass
es = EventStudy() # work out cumulative asset price moves moves over the event df_event = es.get_intraday_moves_over_custom_event(df, df_event_times) # create an average move df_event['Avg'] = df_event.mean(axis=1) # plotting spot over economic data event style = Style() style.scale_factor = 3 style.file_output = 'usdjpy-nfp.png' style.title = 'USDJPY spot moves over recent NFP' # plot in shades of blue (so earlier releases are lighter, later releases are darker) style.color = 'Blues'; style.color_2 = [] style.y_axis_2_series = [] style.display_legend = False # last release will be in red, average move in orange style.color_2_series = [df_event.columns[-2], df_event.columns[-1]] style.color_2 = ['red', 'orange'] # red, pink style.linewidth_2 = 2 style.linewidth_2_series = style.color_2_series chart = Chart(engine='matplotlib') chart.plot(df_event * 100, style=style)
df_hedged = df_hedged.fillna(method='ffill') df_hedged = df_hedged.pct_change() df_hedged['Spot + 2*option put hedge'] = df_hedged[ cross + '-tot.close-bbg'] + df_hedged[cross + '-option-tot-with-tc.close'] df_hedged.columns = RetStats(returns_df=df_hedged, ann_factor=252).summary() # Plot everything # P&L from call chart.plot( calculations.create_mult_index_from_prices( prepare_indices(cross=cross, df_option_tot=df_cuemacro_option_call_tot, df_option_tc=df_cuemacro_option_call_tc, df_spot_tot=df_bbg_tot))) # P&L from put option, put option + TC and total returns from spot chart.plot( calculations.create_mult_index_from_prices( prepare_indices(cross=cross, df_option_tot=df_cuemacro_option_put_tot, df_option_tc=df_cuemacro_option_put_tc, df_spot_tot=df_bbg_tot))) # P&L from put option + TC and total returns from spot chart.plot( calculations.create_mult_index_from_prices( prepare_indices(cross=cross,
cache_algo = 'internet_load_return') # how to return data df = market.fetch_market(md_request) df_ret = calc.calculate_returns(df) day_of_month_seasonality = seasonality.bus_day_of_month_seasonality(df_ret, partition_by_month = False) day_of_month_seasonality = calc.convert_month_day_to_date_time(day_of_month_seasonality) style = Style() style.date_formatter = '%b' style.title = 'Gold seasonality' style.scale_factor = 3 style.file_output = "gold-seasonality.png" chart.plot(day_of_month_seasonality, style=style) ###### Calculate seasonal moves in FX vol (using Bloomberg data) if run_example == 2 or run_example == 0: tickers = ['EURUSDV1M', 'USDJPYV1M', 'GBPUSDV1M', 'AUDUSDV1M'] md_request = MarketDataRequest( start_date = "01 Jan 1996", # start date data_source = 'bloomberg', # use Bloomberg as data source tickers = tickers, fields = ['close'], # which fields to download vendor_tickers = [x + ' Curncy' for x in tickers], # ticker (Bloomberg) vendor_fields = ['PX_LAST'], # which Bloomberg fields to download cache_algo = 'internet_load_return') # how to return data df = market.fetch_market(md_request)
# Download the whole all market data for GBPUSD for pricing options (vol surface) md_request = MarketDataRequest(start_date='01 May 2016', finish_date='01 Aug 2016', data_source='bloomberg', cut='LDN', category='fx-vol-market', tickers=['GBPUSD'], cache_algo='cache_algo_return') df = market.fetch_market(md_request) style = Style() style.title = 'GBPUSD 1M Implied Vol' style.scale_factor = 3 style.source = 'Bloomberg' chart.plot(df['GBPUSDV1M.close'], style=style) ###### Fetch market data for pricing GBPUSD FX options over Brexit vote (ie. FX spot, FX forwards, FX deposits and FX vol quotes) ###### Construct volatility surface using FinancePy library underneath, using polynomial interpolation if run_example == 2 or run_example == 0: horizon_date = '23 Jun 2016' cross = 'GBPUSD' # Download the whole all market data for GBPUSD for pricing options (vol surface) md_request = MarketDataRequest(start_date=horizon_date, finish_date=horizon_date, data_source='bloomberg', cut='LDN', category='fx-vol-market', tickers=cross, cache_algo='cache_algo_return') df = market.fetch_market(md_request)
class TradeAnalysis(object): def __init__(self, engine=ChartConstants().chartfactory_default_engine): self.logger = LoggerManager().getLogger(__name__) self.DUMP_PATH = 'output_data/' + datetime.date.today().strftime( "%Y%m%d") + ' ' self.SCALE_FACTOR = 3 self.DEFAULT_PLOT_ENGINE = engine self.chart = Chart(engine=self.DEFAULT_PLOT_ENGINE) return def run_strategy_returns_stats(self, trading_model): """ run_strategy_returns_stats - Plots useful statistics for the trading strategy (using PyFolio) Parameters ---------- trading_model : TradingModel defining trading strategy """ pnl = trading_model.get_strategy_pnl() tz = Timezone() calculations = Calculations() # PyFolio assumes UTC time based DataFrames (so force this localisation) try: pnl = tz.localise_index_as_UTC(pnl) except: pass # set the matplotlib style sheet & defaults # at present this only works in Matplotlib engine try: matplotlib.rcdefaults() plt.style.use( ChartConstants().chartfactory_style_sheet['chartpy-pyfolio']) except: pass # TODO for intraday strategies, make daily # convert DataFrame (assumed to have only one column) to Series pnl = calculations.calculate_returns(pnl) pnl = pnl.dropna() pnl = pnl[pnl.columns[0]] fig = pf.create_returns_tear_sheet(pnl, return_fig=True) try: plt.savefig(trading_model.DUMP_PATH + "stats.png") except: pass plt.show() def run_tc_shock(self, strategy, tc=None): if tc is None: tc = [0, 0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2.0] parameter_list = [{'spot_tc_bp': x} for x in tc] pretty_portfolio_names = [str(x) + 'bp' for x in tc] # names of the portfolio parameter_type = 'TC analysis' # broad type of parameter name return self.run_arbitrary_sensitivity( strategy, parameter_list=parameter_list, pretty_portfolio_names=pretty_portfolio_names, parameter_type=parameter_type) ###### Parameters and signal generations (need to be customised for every model) def run_arbitrary_sensitivity(self, trading_model, parameter_list=None, parameter_names=None, pretty_portfolio_names=None, parameter_type=None): asset_df, spot_df, spot_df2, basket_dict = trading_model.fill_assets() port_list = None ret_stats_list = [] for i in range(0, len(parameter_list)): br = trading_model.fill_backtest_request() current_parameter = parameter_list[i] # for calculating P&L for k in current_parameter.keys(): setattr(br, k, current_parameter[k]) trading_model.br = br # for calculating signals signal_df = trading_model.construct_signal(spot_df, spot_df2, br.tech_params, br) backtest = Backtest() self.logger.info("Calculating... " + str(pretty_portfolio_names[i])) backtest.calculate_trading_PnL(br, asset_df, signal_df) ret_stats_list.append(backtest.get_portfolio_pnl_ret_stats()) stats = str(backtest.get_portfolio_pnl_desc()[0]) port = backtest.get_cumportfolio().resample('B').mean() port.columns = [str(pretty_portfolio_names[i]) + ' ' + stats] if port_list is None: port_list = port else: port_list = port_list.join(port) # reset the parameters of the strategy trading_model.br = trading_model.fill_backtest_request() style = Style() ir = [t.inforatio()[0] for t in ret_stats_list] # if we have too many combinations remove legend and use scaled shaded colour # if len(port_list) > 10: # style.color = 'Blues' # style.display_legend = False # plot all the variations style.resample = 'B' style.file_output = self.DUMP_PATH + trading_model.FINAL_STRATEGY + ' ' + parameter_type + '.png' style.html_file_output = self.DUMP_PATH + trading_model.FINAL_STRATEGY + ' ' + parameter_type + '.html' style.scale_factor = self.SCALE_FACTOR style.title = trading_model.FINAL_STRATEGY + ' ' + parameter_type self.chart.plot(port_list, chart_type='line', style=style) # plot all the IR in a bar chart form (can be easier to read!) style = Style() style.file_output = self.DUMP_PATH + trading_model.FINAL_STRATEGY + ' ' + parameter_type + ' IR.png' style.html_file_output = self.DUMP_PATH + trading_model.FINAL_STRATEGY + ' ' + parameter_type + ' IR.html' style.scale_factor = self.SCALE_FACTOR style.title = trading_model.FINAL_STRATEGY + ' ' + parameter_type summary = pandas.DataFrame(index=pretty_portfolio_names, data=ir, columns=['IR']) self.chart.plot(summary, chart_type='bar', style=style) return port_list ###### Parameters and signal generations (need to be customised for every model) ###### Plot all the output seperately def run_arbitrary_sensitivity_separately(self, trading_model, parameter_list=None, pretty_portfolio_names=None, strip=None): # asset_df, spot_df, spot_df2, basket_dict = strat.fill_assets() final_strategy = trading_model.FINAL_STRATEGY for i in range(0, len(parameter_list)): br = trading_model.fill_backtest_request() current_parameter = parameter_list[i] # for calculating P&L for k in current_parameter.keys(): setattr(br, k, current_parameter[k]) trading_model.FINAL_STRATEGY = final_strategy + " " + pretty_portfolio_names[ i] self.logger.info("Calculating... " + pretty_portfolio_names[i]) trading_model.br = br trading_model.construct_strategy(br=br) trading_model.plot_strategy_pnl() trading_model.plot_strategy_leverage() trading_model.plot_strategy_group_benchmark_pnl(strip=strip) # reset the parameters of the strategy trading_model.br = trading_model.fill_backtest_request() trading_model.FINAL_STRATEGY = final_strategy def run_day_of_month_analysis(self, trading_model): from finmarketpy.economics.seasonality import Seasonality calculations = Calculations() seas = Seasonality() trading_model.construct_strategy() pnl = trading_model.get_strategy_pnl() # get seasonality by day of the month pnl = pnl.resample('B').mean() rets = calculations.calculate_returns(pnl) bus_day = seas.bus_day_of_month_seasonality(rets, add_average=True) # get seasonality by month pnl = pnl.resample('BM').mean() rets = calculations.calculate_returns(pnl) month = seas.monthly_seasonality(rets) self.logger.info("About to plot seasonality...") style = Style() # Plotting spot over day of month/month of year style.color = 'Blues' style.scale_factor = self.SCALE_FACTOR style.file_output = self.DUMP_PATH + trading_model.FINAL_STRATEGY + ' seasonality day of month.png' style.html_file_output = self.DUMP_PATH + trading_model.FINAL_STRATEGY + ' seasonality day of month.html' style.title = trading_model.FINAL_STRATEGY + ' day of month seasonality' style.display_legend = False style.color_2_series = [bus_day.columns[-1]] style.color_2 = ['red'] # red, pink style.linewidth_2 = 4 style.linewidth_2_series = [bus_day.columns[-1]] style.y_axis_2_series = [bus_day.columns[-1]] self.chart.plot(bus_day, chart_type='line', style=style) style = Style() style.scale_factor = self.SCALE_FACTOR style.file_output = self.DUMP_PATH + trading_model.FINAL_STRATEGY + ' seasonality month of year.png' style.html_file_output = self.DUMP_PATH + trading_model.FINAL_STRATEGY + ' seasonality month of year.html' style.title = trading_model.FINAL_STRATEGY + ' month of year seasonality' self.chart.plot(month, chart_type='line', style=style) return month
from chartpy import Chart, Style # choose run_example = 0 for everything # run_example = 1 - plot volatility surface run_example = 0 if run_example == 1 or run_example == 0: import pandas # get sample volatility surface for GBP/USD df = pandas.read_csv('volsurface.csv') # set the style of the plot style = Style(title="GBP/USD vol surface", source="chartpy", color='Blues') style.file_output = 'volsurface.png' # Chart object is initialised with the dataframe and our chart style chart = Chart(df=df, chart_type='surface', style=style) # chart.plot(engine='matplotlib') TODO matplotlib implementation # chart.plot(engine='bokeh') TODO bokeh surface implementation chart.plot(engine='plotly')
# Get spot data md_request.abstract_curve = None df_spot = market.fetch_market(md_request=md_request) df_spot.columns = [x + '-spot' for x in df_spot.columns] # Get Bloomberg calculated total return indices (for spot) md_request.category = 'fx-tot' df_bbg_tot = market.fetch_market(md_request) df_bbg_tot.columns = [x + '-bbg' for x in df_bbg_tot.columns] # Get Bloomberg calculated total return indices (for 1M forwards rolled) md_request.category = 'fx-tot-forwards' df_bbg_tot_forwards = market.fetch_market(md_request) df_bbg_tot_forwards.columns = [ x + '-bbg' for x in df_bbg_tot_forwards.columns ] # Combine into a single data frame and plot, we note that the Cuemacro constructed indices track the Bloomberg # indices relatively well (both from spot and 1M forwards). Also note the large difference with spot indices # CAREFUL to fill down, before reindexing because 1M forwards indices are likely to have different publishing dates df = calculations.pandas_outer_join( [df_tot, df_bbg_tot, df_spot, df_bbg_tot_forwards]).fillna(method='ffill') df = calculations.create_mult_index_from_prices(df) chart.plot(df)
class TradeAnalysis(object): """Applies some basic trade analysis for a trading strategy (as defined by TradingModel). Use PyFolio to create some basic trading statistics. Also allows you test multiple parameters for a specific strategy (like TC). """ def __init__(self, engine = ChartConstants().chartfactory_default_engine): self.logger = LoggerManager().getLogger(__name__) self.DUMP_PATH = 'output_data/' + datetime.date.today().strftime("%Y%m%d") + ' ' self.SCALE_FACTOR = 3 self.DEFAULT_PLOT_ENGINE = engine self.chart = Chart(engine=self.DEFAULT_PLOT_ENGINE) return def run_strategy_returns_stats(self, trading_model, index = None, engine = 'pyfolio'): """Plots useful statistics for the trading strategy (using PyFolio) Parameters ---------- trading_model : TradingModel defining trading strategy index: DataFrame define strategy by a time series """ if index is None: pnl = trading_model.get_strategy_pnl() else: pnl = index tz = Timezone() calculations = Calculations() if engine == 'pyfolio': # PyFolio assumes UTC time based DataFrames (so force this localisation) try: pnl = tz.localise_index_as_UTC(pnl) except: pass # set the matplotlib style sheet & defaults # at present this only works in Matplotlib engine try: matplotlib.rcdefaults() plt.style.use(ChartConstants().chartfactory_style_sheet['chartpy-pyfolio']) except: pass # TODO for intraday strategies, make daily # convert DataFrame (assumed to have only one column) to Series pnl = calculations.calculate_returns(pnl) pnl = pnl.dropna() pnl = pnl[pnl.columns[0]] fig = pf.create_returns_tear_sheet(pnl, return_fig=True) try: plt.savefig (trading_model.DUMP_PATH + "stats.png") except: pass plt.show() elif engine == 'finmarketpy': # assume we have TradingModel # to do to take in a time series from chartpy import Canvas, Chart pnl = trading_model.plot_strategy_pnl(silent_plot=True) # plot the final strategy individual = trading_model.plot_strategy_group_pnl_trades(silent_plot=True) # plot the individual trade P&Ls pnl_comp = trading_model.plot_strategy_group_benchmark_pnl(silent_plot=True) # plot all the cumulative P&Ls of each component ir_comp = trading_model.plot_strategy_group_benchmark_pnl_ir(silent_plot=True) # plot all the IR of each component leverage = trading_model.plot_strategy_leverage(silent_plot=True) # plot the leverage of the portfolio ind_lev = trading_model.plot_strategy_group_leverage(silent_plot=True) # plot all the individual leverages canvas = Canvas([[pnl, individual], [pnl_comp, ir_comp], [leverage, ind_lev]] ) canvas.generate_canvas(silent_display=False, canvas_plotter='plain') def run_excel_trade_report(self, trading_model, excel_file = 'model.xlsx'): """ run_excel_trade_report - Creates an Excel spreadsheet with model returns and latest trades Parameters ---------- trading_model : TradingModel defining trading strategy (can be a list) """ trading_model_list = trading_model if not(isinstance(trading_model_list, list)): trading_model_list = [trading_model] writer = pandas.ExcelWriter(excel_file, engine='xlsxwriter') for tm in trading_model_list: strategy_name = tm.FINAL_STRATEGY returns = tm.get_strategy_group_benchmark_pnl() returns.to_excel(writer, sheet_name=strategy_name + ' rets', engine='xlsxwriter') # write raw position/trade sizes self.save_positions_trades(tm, tm.get_strategy_signal(),tm.get_strategy_trade(), 'pos', 'trades', writer) if hasattr(tm, '_strategy_signal_notional'): # write position/trade sizes scaled by notional self.save_positions_trades(tm, tm.get_strategy_signal_notional(), tm.get_strategy_trade_notional(), 'pos - Not', 'trades - Not', writer) if hasattr(tm, '_strategy_signal_contracts'): # write position/trade sizes in terms of contract sizes self.save_positions_trades(tm, tm.get_strategy_signal_contracts(), tm.get_strategy_trade_contracts(), 'pos - Cont', 'trades - Cont', writer) # TODO Add summary sheet comparing return statistics for all the different models in the list writer.save() writer.close() def save_positions_trades(self, tm, signals, trades, signal_caption, trade_caption, writer): signals.to_excel(writer, sheet_name=tm.FINAL_STRATEGY + ' hist ' + signal_caption, engine='xlsxwriter') if hasattr(tm, 'STRIP'): strip = tm.STRIP recent_signals = tm.grab_signals(signals, date=[-1, -2, -5, -10, -20], strip=strip) recent_trades = tm.grab_signals(trades, date=[-1, -2, -5, -10, -20], strip=strip) recent_signals.to_excel(writer, sheet_name=tm.FINAL_STRATEGY + ' ' + signal_caption, engine='xlsxwriter') recent_trades.to_excel(writer, sheet_name=tm.FINAL_STRATEGY + ' ' + trade_caption, engine='xlsxwriter') def run_tc_shock(self, strategy, tc = None): if tc is None: tc = [0, 0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2.0] parameter_list = [{'spot_tc_bp' : x } for x in tc] pretty_portfolio_names = [str(x) + 'bp' for x in tc] # names of the portfolio parameter_type = 'TC analysis' # broad type of parameter name return self.run_arbitrary_sensitivity(strategy, parameter_list=parameter_list, pretty_portfolio_names=pretty_portfolio_names, parameter_type=parameter_type) ###### Parameters and signal generations (need to be customised for every model) def run_arbitrary_sensitivity(self, trading_model, parameter_list = None, parameter_names = None, pretty_portfolio_names = None, parameter_type = None): asset_df, spot_df, spot_df2, basket_dict = trading_model.load_assets() port_list = None ret_stats_list = [] for i in range(0, len(parameter_list)): br = trading_model.load_parameters() current_parameter = parameter_list[i] # for calculating P&L for k in current_parameter.keys(): setattr(br, k, current_parameter[k]) trading_model.br = br # for calculating signals signal_df = trading_model.construct_signal(spot_df, spot_df2, br.tech_params, br) backtest = Backtest() self.logger.info("Calculating... " + str(pretty_portfolio_names[i])) backtest.calculate_trading_PnL(br, asset_df, signal_df) ret_stats_list.append(backtest.get_portfolio_pnl_ret_stats()) stats = str(backtest.get_portfolio_pnl_desc()[0]) port = backtest.get_cumportfolio().resample('B').mean() port.columns = [str(pretty_portfolio_names[i]) + ' ' + stats] if port_list is None: port_list = port else: port_list = port_list.join(port) # reset the parameters of the strategy trading_model.br = trading_model.load_parameters() style = Style() ir = [t.inforatio()[0] for t in ret_stats_list] # if we have too many combinations remove legend and use scaled shaded colour # if len(port_list) > 10: # style.color = 'Blues' # style.display_legend = False # plot all the variations style.resample = 'B' style.file_output = self.DUMP_PATH + trading_model.FINAL_STRATEGY + ' ' + parameter_type + '.png' style.html_file_output = self.DUMP_PATH + trading_model.FINAL_STRATEGY + ' ' + parameter_type + '.html' style.scale_factor = self.SCALE_FACTOR style.title = trading_model.FINAL_STRATEGY + ' ' + parameter_type self.chart.plot(port_list, chart_type='line', style=style) # plot all the IR in a bar chart form (can be easier to read!) style = Style() style.file_output = self.DUMP_PATH + trading_model.FINAL_STRATEGY + ' ' + parameter_type + ' IR.png' style.html_file_output = self.DUMP_PATH + trading_model.FINAL_STRATEGY + ' ' + parameter_type + ' IR.html' style.scale_factor = self.SCALE_FACTOR style.title = trading_model.FINAL_STRATEGY + ' ' + parameter_type summary = pandas.DataFrame(index = pretty_portfolio_names, data = ir, columns = ['IR']) self.chart.plot(summary, chart_type='bar', style=style) return port_list ###### Parameters and signal generations (need to be customised for every model) ###### Plot all the output seperately def run_arbitrary_sensitivity_separately(self, trading_model, parameter_list = None, pretty_portfolio_names = None, strip = None): # asset_df, spot_df, spot_df2, basket_dict = strat.fill_assets() final_strategy = trading_model.FINAL_STRATEGY for i in range(0, len(parameter_list)): br = trading_model.fill_backtest_request() current_parameter = parameter_list[i] # for calculating P&L for k in current_parameter.keys(): setattr(br, k, current_parameter[k]) trading_model.FINAL_STRATEGY = final_strategy + " " + pretty_portfolio_names[i] self.logger.info("Calculating... " + pretty_portfolio_names[i]) trading_model.br = br trading_model.construct_strategy(br = br) trading_model.plot_strategy_pnl() trading_model.plot_strategy_leverage() trading_model.plot_strategy_group_benchmark_pnl(strip = strip) # reset the parameters of the strategy trading_model.br = trading_model.fill_backtest_request() trading_model.FINAL_STRATEGY = final_strategy def run_day_of_month_analysis(self, trading_model): from finmarketpy.economics.seasonality import Seasonality calculations = Calculations() seas = Seasonality() trading_model.construct_strategy() pnl = trading_model.get_strategy_pnl() # get seasonality by day of the month pnl = pnl.resample('B').mean() rets = calculations.calculate_returns(pnl) bus_day = seas.bus_day_of_month_seasonality(rets, add_average = True) # get seasonality by month pnl = pnl.resample('BM').mean() rets = calculations.calculate_returns(pnl) month = seas.monthly_seasonality(rets) self.logger.info("About to plot seasonality...") style = Style() # Plotting spot over day of month/month of year style.color = 'Blues' style.scale_factor = self.SCALE_FACTOR style.file_output = self.DUMP_PATH + trading_model.FINAL_STRATEGY + ' seasonality day of month.png' style.html_file_output = self.DUMP_PATH + trading_model.FINAL_STRATEGY + ' seasonality day of month.html' style.title = trading_model.FINAL_STRATEGY + ' day of month seasonality' style.display_legend = False style.color_2_series = [bus_day.columns[-1]] style.color_2 = ['red'] # red, pink style.linewidth_2 = 4 style.linewidth_2_series = [bus_day.columns[-1]] style.y_axis_2_series = [bus_day.columns[-1]] self.chart.plot(bus_day, chart_type='line', style=style) style = Style() style.scale_factor = self.SCALE_FACTOR style.file_output = self.DUMP_PATH + trading_model.FINAL_STRATEGY + ' seasonality month of year.png' style.html_file_output = self.DUMP_PATH + trading_model.FINAL_STRATEGY + ' seasonality month of year.html' style.title = trading_model.FINAL_STRATEGY + ' month of year seasonality' self.chart.plot(month, chart_type='line', style=style) return month
fx_forwards_tenor=fx_forwards_tenors, depo_tenor='1M') print(implied_depo_df) market_cols = [cross + ".close"] for f in fx_forwards_to_print: market_cols.append(cross + f + '.close') market_cols.append("USD1M.close") print(market_df[market_cols]) # Download implied deposits (via FX forwards) for base currency (AUD) from Bloomberg # Note: some implied deposits tenors might have no data in it tickers = [] for t in fx_forwards_tenors: tickers.append(cross[0:3] + "I" + t) md_request = MarketDataRequest(start_date='01 Jan 2020', finish_date='01 Feb 2020', data_source='bloomberg', cut='NYC', tickers=tickers, vendor_tickers= [x + " CMPN Curncy" for x in tickers], cache_algo='cache_algo_return') implied_depo_bbg_df = market.fetch_market(md_request=md_request) chart.plot(calculations.join([implied_depo_bbg_df, implied_depo_df]), how='outer')
class ComputationReport(ABC): """Converts ComputationResults (largely consisting of Plotly based Figures and HTML tables) into self contained HTML pages. Can also render these HTML pages into PDFs. Uses chartpy's "Canvas" object extensively """ def __init__(self, computation_results, title='Cuemacro Computation'): """Initialize class, with the computation results we wish to convert into a report like format Parameters ---------- computation_results : ComputationResults The results of a large scale computation, which contains charts and DataFrames title : str Title of webpage to be rendered """ self._util_func = UtilFunc() self._computation_results = computation_results self._title = title self._canvas_plotter = 'plain' self._chart = Chart(engine='plotly') def create_report(self, output_filename=None, output_format='html', offline_js=False): """Creates an HTML/PDF report from a ComputationResult object, which can (optionally) be written to disk, alternatively returns a binary representation of the HTML or PDF. Parameters ---------- output_filename : str (optional) File output, if this is not specified a binary object is returned output_format : str 'html' (default) - output an HTML page offline_js : bool False (default) - download's Plotly.js in webpage to be rendered True - includes Plotly.js in web page to be rendered Returns ------- pdf or HTML binary """ # extra code to put in the <head> part of HTML extra_head_code = '' if output_format == 'html': # embed plotly.js in HTML (makes it bigger, but then doesn't require web connection) if offline_js: embed_chart = 'offline_embed_js_div' else: # otherwise put web link to plotly.js (but this means we need to download every time) embed_chart = 'offline_div' extra_head_code = '<head><script src="https://cdn.plot.ly/plotly-latest.min.js"></script></head>' elif output_format == 'pdf': # for PDFs we need to create static PNGs of plotly charts embed_chart = 'offline_image_png_in_html' # get a list of the HTML to render elements_to_render = self._layout_computation_results_to_html( embed_chart) canvas = Canvas(elements_to_render=elements_to_render) # should we return a binary string containing the HTML/PDF (this can be displayed by a web server for example) # or later be written to disk return_binary = False # return a binary string, if we haven't specified a filename output if output_filename is None: return_binary = True # generate the HTML or PDF with chartpy's Canvas object if output_format == 'html': html, _ = canvas.generate_canvas( output_filename=output_filename, silent_display=True, canvas_plotter=self._canvas_plotter, page_title=self._title, render_pdf=False, return_html_binary=return_binary, extra_head_code=extra_head_code) return html elif output_format == 'pdf': _, pdf = canvas.generate_canvas( output_filename=output_filename, silent_display=True, canvas_plotter=self._canvas_plotter, page_title=self._title, render_pdf=True, return_pdf_binary=return_binary, extra_head_code=extra_head_code) return pdf else: raise Exception("Invalid output format selected") def _generate_filename(self, extension): return (self._get_time_stamp() + "." + extension) def _get_time_stamp(self): return str(datetime.datetime.now()).replace(':', '-').replace( ' ', '-').replace(".", "-") def _create_text_html(self, text): """Takes text and then creates the appropriate HTML to represent it, split by horizontal HTML bars Parameters ---------- text : str (list) Text to be added in HTML Returns ------- list (of HTML) """ if text != [] and text is not None: html_output = [['<hr>']] else: html_output = [] if not (isinstance(text, list)): text = [text] for t in text: html_output.append([t]) return html_output def _create_table_html(self, table): """Takes tables in HTML and then creates the appropriate HTML to represent it, split by horizontal HTML bars Parameters ---------- text : str (list) Tables in HTML format Returns ------- list (of HTML) """ if table != {} and table is not None: html_output = [['<hr>']] else: html_output = [] for t in self._util_func.dict_key_list(table.keys()): html_output.append(table[t]) return html_output def _create_chart_html(self, chart, embed_chart): if chart != {} and chart is not None: html_output = [['<hr>']] else: html_output = [] style = Style(plotly_plot_mode=embed_chart) for c in self._util_func.dict_key_list(chart.keys()): html_output.append([self._chart.plot(chart[c], style=style)]) return html_output @abc.abstractmethod def _layout_computation_results_to_html(self, embed_chart='offline_embed_js_div' ): """Converts the computation results to a list containing HTML, primarily of the charts. Should be implemented by concrete subclasses, where we can select the order of the charts (and which charts are converted) Parameters ---------- embed_chart : str 'offline_embed_js_div' (default) - converts Plotly Figures into HTML + includes Plotly.js script 'offline_div' - converts Plotly Figures into HTML (but excludes Plotly.js script) Returns ------- list (containing HTML) """ pass
if run_example == 1 or run_example == 0: df = Quandl.get("FRED/GDP", authtoken=quandl_api_key) # we can use the charting tools in several ways chart = Chart() # chart.plot(df = df) # set the style of the plot style = Style(title="US GDP", source="Quandl/Fred") # Chart object is initialised with the dataframe and our chart style chart = Chart(df=df, chart_type='line', style=style) # we now plot using multiple plotting libraries, with the same dataframe chart.plot(engine='matplotlib') chart.plot(engine='bokeh') chart.plot(engine='plotly') if run_example == 2 or run_example == 0: # download US and Texas unemployment rate df = Quandl.get(["FRED/UNRATE", "FRED/TXUR"], authtoken=quandl_api_key, trim_start="2015-12-01") import pandas df.index = pandas.to_datetime(df.index, format='%Y-%m-%d') # first plot without any parameters (will use defaults) - note how we can it assign the dataframe to either Chart # or the plot method
es = EventStudy() # Work out cumulative asset price moves moves over the event df_event = es.get_intraday_moves_over_custom_event(df, df_event_times) # Create an average move df_event['Avg'] = df_event.mean(axis=1) # Plotting spot over economic data event style = Style() style.scale_factor = 3 style.file_output = 'usdjpy-nfp.png' style.title = 'USDJPY spot moves over recent NFP' # Plot in shades of blue (so earlier releases are lighter, later releases are darker) style.color = 'Blues' style.color_2 = [] style.y_axis_2_series = [] style.display_legend = False # Last release will be in red, average move in orange style.color_2_series = [df_event.columns[-2], df_event.columns[-1]] style.color_2 = ['red', 'orange'] # red, pink style.linewidth_2 = 2 style.linewidth_2_series = style.color_2_series chart = Chart(engine='matplotlib') chart.plot(df_event * 100, style=style)
implied_vol = pd.DataFrame(market_df['GBPUSDVON.close']) vrp = vol_stats.calculate_vol_risk_premium('GBPUSD', tenor_label='ON', implied_vol=implied_vol, realized_vol=realized_vol) style = Style() style.title = 'GBPUSD ON volatility over Brexit' style.scale_factor = 3 style.source = 'Bloomberg' # Plot all the volatility metrics chart.plot(vrp, style=style) # Plot the implied volatility bumped forward a day against the realized volatility calculated over that day chart.plot(vrp[['GBPUSDUON.close', 'GBPUSDHON.close']], style=style) ###### Calculating realized volatility over Brexit vote in GBPUSD in ON/overnight tenor # Showing the difference between time frequencies if run_example == 2 or run_example == 0: # Download FX tick data for GBPUSD over Brexit vote and then convert into 1 minute data (open/high/low/close) # which are necessary for calculating realised volatility md_request = MarketDataRequest(start_date='01 Jun 2016', finish_date='02 Jul 2016', data_source='dukascopy', freq='tick', category='fx',
# choose run_example = 0 for everything # run_example = 1 - download BoE data from quandl run_example = 0 ###### fetch data from Quandl for BoE rate (using Bloomberg data) if run_example == 1 or run_example == 0: # Monthly average of UK resident monetary financial institutions' (excl. Central Bank) sterling # Weighted average interest rate, other loans, new advances, on a fixed rate to private non-financial corporations (in percent) # not seasonally adjusted md_request = MarketDataRequest( start_date = "01 Jan 2000", # start date data_source = 'quandl', # use Quandl as data source tickers = ['Weighted interest rate'], fields = ['close'], # which fields to download vendor_tickers = ['BOE/CFMBJ84'], # ticker (Bloomberg) vendor_fields = ['close'], # which Bloomberg fields to download cache_algo = 'internet_load_return') # how to return data df = market.fetch_market(md_request) style = Style() style.title = 'BoE weighted interest rate' style.scale_factor = 3 style.file_output = "boe-rate.png" style.source = 'Quandl/BoE' chart.plot(df, style=style)
except: # if import fails use Quandl 2.x.x import Quandl from chartpy import Chart, Style # get your own free bQuandl API key from https://www.quandl.com/ try: from chartpy.chartcred import ChartCred cred = ChartCred() quandl_api_key = cred.quandl_api_key except: quandl_api_key = "x" # choose run_example = 0 for everything # run_example = 1 - xkcd example run_example = 0 if run_example == 1 or run_example == 0: df = Quandl.get(["FRED/A191RL1Q225SBEA"], authtoken=quandl_api_key) df.columns = ["Real QoQ"] # set the style of the plot style = Style(title="US GDP", source="Quandl/Fred", xkcd=True) # Chart object is initialised with the dataframe and our chart style chart = Chart(df=df, chart_type='line', style=style, engine='matplotlib') chart.plot()
class ComputationReport(ABC): """Converts ComputationResults (largely consisting of Plotly based Figures and HTML tables) into self contained HTML pages. Can also render these HTML pages into PDFs. Uses Renderer objects to create the HTML including BasicRenderer (which uses chartpy's "Canvas" object extensively) and JinjaRenderer (uses Jinja templating for HTML and WeasyPrint for PDF conversion). """ def __init__(self, computation_results, title='Cuemacro Computation', renderer=CanvasRenderer(), chart_report_height=constants.chart_report_height, chart_report_width=constants.chart_report_width): """Initialize class, with the computation results we wish to convert into a report like format Parameters ---------- computation_results : ComputationResults The results of a large scale computation, which contains charts and DataFrames title : str Title of webpage to be rendered """ self._util_func = UtilFunc() self._computation_results = computation_results self._title = title self._chart = Chart(engine='plotly') self._renderer = renderer self._computation_request = computation_results.computation_request self._chart_report_width = chart_report_width self._chart_report_height = chart_report_height def create_report(self, output_filename=None, output_format='html', offline_js=False): """Creates an HTML/PDF report from a ComputationResult object, which can (optionally) be written to disk, alternatively returns a binary representation of the HTML or PDF. Parameters ---------- output_filename : str (optional) File output, if this is not specified a binary object is returned output_format : str 'html' (default) - output an HTML page offline_js : bool False (default) - download's Plotly.js in webpage to be rendered True - includes Plotly.js in web page to be rendered (results in much bigger file sizes) Returns ------- pdf or HTML binary """ extra_head_code = '' if output_format == 'html': # Embed plotly.js in HTML (makes it bigger, but then doesn't require web connection) if offline_js: embed_chart = 'offline_embed_js_div' else: # Otherwise put web link to plotly.js (but this means we need to download every time) embed_chart = 'offline_div' extra_head_code = '<head><script src="https://cdn.plot.ly/plotly-latest.min.js"></script></head>' elif output_format == 'pdf': # For PDFs we need to create static SVGs of plotly charts embed_chart = 'offline_image_svg_in_html' elif output_format == 'xlwings': embed_chart = 'leave_as_fig' # Get a list of the HTML to render elements_to_render_dict = self._layout_computation_results_to_html( embed_chart) return self._renderer.render_elements(elements_to_render_dict, title=self._title, output_filename=output_filename, output_format=output_format, extra_head_code=extra_head_code) def _generate_filename(self, extension): return (self._get_time_stamp() + "." + extension) def _get_time_stamp(self): return str(datetime.datetime.now()).replace(':', '-').replace( ' ', '-').replace(".", "-") def _create_text_html(self, text, add_hr=True): """Takes text and then creates the appropriate HTML to represent it, split by horizontal HTML bars Parameters ---------- text : str (list) Text to be added in HTML Returns ------- list (of HTML) """ if text != [] and text is not None and add_hr: html_output = [['<hr>']] else: html_output = [] if not (isinstance(text, list)): text = [text] for t in text: html_output.append([t]) return html_output def _create_table_html(self, table): """Takes tables in HTML and then creates the appropriate HTML to represent it, split by horizontal HTML bars Parameters ---------- text : str (list) Tables in HTML format Returns ------- list (of HTML) """ if table != {} and table is not None: html_output = [['<hr>']] else: html_output = [] for t in self._util_func.dict_key_list(table.keys()): html_output.append(table[t]) return html_output def _create_chart_html(self, chart, embed_chart): if chart != {} and chart is not None: html_output = [['<hr>']] else: html_output = [] style = Style(plotly_plot_mode=embed_chart) for c in self._util_func.dict_key_list(chart.keys()): # Update chart size and padding (if it's Plotly), so it fits well on PDF try: chart[c].update_layout( autosize=False, width=self._chart_report_width, height=self._chart_report_height, margin=dict(l=10, r=10, b=10, t=60, pad=4), ) except: pass if embed_chart == 'leave_as_fig': html_output.append([chart[c]]) else: html_output.append([self._chart.plot(chart[c], style=style)]) return html_output @abc.abstractmethod def _layout_computation_results_to_html(self, embed_chart='offline_embed_js_div' ): """Converts the computation results to a list containing HTML, primarily of the charts. Should be implemented by concrete subclasses, where we can select the order of the charts (and which charts are converted) Parameters ---------- embed_chart : str 'offline_embed_js_div' (default) - converts Plotly Figures into HTML + includes Plotly.js script 'offline_div' - converts Plotly Figures into HTML (but excludes Plotly.js script) Returns ------- list (containing HTML), list (containing HTML of descriptions) """ pass
class TradeAnalysis(object): """Applies some basic trade analysis for a trading strategy (as defined by TradingModel). Use PyFolio to create some basic trading statistics. Also allows you test multiple parameters for a specific strategy (like TC). """ def __init__(self, engine=ChartConstants().chartfactory_default_engine): self.DUMP_PATH = 'output_data/' + datetime.date.today().strftime( "%Y%m%d") + ' ' self.DEFAULT_PLOT_ENGINE = engine self.chart = Chart(engine=self.DEFAULT_PLOT_ENGINE) return def run_strategy_returns_stats(self, trading_model, index=None, engine='finmarketpy'): """Plots useful statistics for the trading strategy using various backends Parameters ---------- trading_model : TradingModel defining trading strategy engine : str 'pyfolio' - use PyFolio as a backend 'finmarketpy' - use finmarketpy as a backend index: DataFrame define strategy by a time series """ if index is None: pnl = trading_model.strategy_pnl() else: pnl = index tz = Timezone() calculations = Calculations() if engine == 'pyfolio': # PyFolio assumes UTC time based DataFrames (so force this localisation) try: pnl = tz.localise_index_as_UTC(pnl) except: pass # set the matplotlib style sheet & defaults # at present this only works in Matplotlib engine try: import matplotlib import matplotlib.pyplot as plt matplotlib.rcdefaults() plt.style.use(ChartConstants(). chartfactory_style_sheet['chartpy-pyfolio']) except: pass # TODO for intraday strategies, make daily # convert DataFrame (assumed to have only one column) to Series pnl = calculations.calculate_returns(pnl) pnl = pnl.dropna() pnl = pnl[pnl.columns[0]] fig = pf.create_returns_tear_sheet(pnl, return_fig=True) try: plt.savefig(trading_model.DUMP_PATH + "stats.png") except: pass plt.show() elif engine == 'finmarketpy': # assume we have TradingModel # to do to take in a time series from chartpy import Canvas, Chart # temporarily make scale factor smaller so fits the window old_scale_factor = trading_model.SCALE_FACTOR trading_model.SCALE_FACTOR = 0.75 pnl = trading_model.plot_strategy_pnl( silent_plot=True) # plot the final strategy individual = trading_model.plot_strategy_group_pnl_trades( silent_plot=True) # plot the individual trade P&Ls pnl_comp = trading_model.plot_strategy_group_benchmark_pnl( silent_plot=True ) # plot all the cumulative P&Ls of each component ir_comp = trading_model.plot_strategy_group_benchmark_pnl_ir( silent_plot=True) # plot all the IR of each component leverage = trading_model.plot_strategy_leverage( silent_plot=True) # plot the leverage of the portfolio ind_lev = trading_model.plot_strategy_group_leverage( silent_plot=True) # plot all the individual leverages canvas = Canvas([[pnl, individual], [pnl_comp, ir_comp], [leverage, ind_lev]]) canvas.generate_canvas( page_title=trading_model.FINAL_STRATEGY + ' Return Statistics', silent_display=False, canvas_plotter='plain', output_filename=trading_model.FINAL_STRATEGY + ".html", render_pdf=False) trading_model.SCALE_FACTOR = old_scale_factor def run_excel_trade_report(self, trading_model, excel_file='model.xlsx'): """ run_excel_trade_report - Creates an Excel spreadsheet with model returns and latest trades Parameters ---------- trading_model : TradingModel defining trading strategy (can be a list) """ trading_model_list = trading_model if not (isinstance(trading_model_list, list)): trading_model_list = [trading_model] writer = pandas.ExcelWriter(excel_file, engine='xlsxwriter') for tm in trading_model_list: strategy_name = tm.FINAL_STRATEGY returns = tm.strategy_group_benchmark_pnl() returns.to_excel(writer, sheet_name=strategy_name + ' rets', engine='xlsxwriter') # write raw position/trade sizes self.save_positions_trades(tm, tm.strategy_signal(), tm.strategy_trade(), 'pos', 'trades', writer) if hasattr(tm, '_strategy_signal_notional'): signal_notional = tm.strategy_signal_notional() trading_notional = tm.strategy_signal_notional() if signal_notional is not None and trading_notional is not None: # write position/trade sizes scaled by notional self.save_positions_trades(tm, signal_notional, trading_notional, 'pos - Not', 'trades - Not', writer) if hasattr(tm, '_strategy_signal_contracts'): signal_contracts = tm.strategy_signal_contracts() trade_contracts = tm.strategy_trade_contracts() if signal_contracts is not None and trade_contracts is not None: # write position/trade sizes in terms of contract sizes self.save_positions_trades(tm, signal_contracts, trade_contracts, 'pos - Cont', 'trades - Cont', writer) # TODO Add summary sheet comparing return statistics for all the different models in the list writer.save() writer.close() def save_positions_trades(self, tm, signals, trades, signal_caption, trade_caption, writer): signals.to_excel(writer, sheet_name=tm.FINAL_STRATEGY + ' hist ' + signal_caption, engine='xlsxwriter') if hasattr(tm, 'STRIP'): strip = tm.STRIP else: strip = '' recent_signals = tm._grab_signals(signals, date=[-1, -2, -5, -10, -20], strip=strip) recent_trades = tm._grab_signals(trades, date=[-1, -2, -5, -10, -20], strip=strip) recent_signals.to_excel(writer, sheet_name=tm.FINAL_STRATEGY + ' ' + signal_caption, engine='xlsxwriter') recent_trades.to_excel(writer, sheet_name=tm.FINAL_STRATEGY + ' ' + trade_caption, engine='xlsxwriter') def run_tc_shock(self, strategy, tc=None, run_in_parallel=False, reload_market_data=True): if tc is None: tc = [0.0, 0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0] parameter_list = [{'spot_tc_bp': x} for x in tc] pretty_portfolio_names = [str(x) + 'bp' for x in tc] # names of the portfolio parameter_type = 'TC analysis' # broad type of parameter name return self.run_arbitrary_sensitivity( strategy, parameter_list=parameter_list, pretty_portfolio_names=pretty_portfolio_names, parameter_type=parameter_type, run_in_parallel=run_in_parallel, reload_market_data=reload_market_data) ###### Parameters and signal generations (need to be customised for every model) def run_arbitrary_sensitivity(self, trading_model, parameter_list=None, pretty_portfolio_names=None, parameter_type=None, run_in_parallel=False, reload_market_data=True): if not (reload_market_data): asset_df, spot_df, spot_df2, basket_dict, contract_value_df = self._load_assets( trading_model) port_list = [] ret_stats_list = [] if market_constants.backtest_thread_no[ market_constants.generic_plat] > 1 and run_in_parallel: swim_pool = SwimPool(multiprocessing_library=market_constants. multiprocessing_library) pool = swim_pool.create_pool( thread_technique=market_constants.backtest_thread_technique, thread_no=market_constants.backtest_thread_no[ market_constants.generic_plat]) mult_results = [] for i in range(0, len(parameter_list)): # reset all parameters br = copy.copy(trading_model.load_parameters()) current_parameter = parameter_list[i] # for calculating P&L, change the assets for k in current_parameter.keys(): setattr(br, k, current_parameter[k]) setattr(br.tech_params, k, current_parameter[k]) # should specify reloading the data, if our parameters impact which assets we are fetching if reload_market_data: asset_df, spot_df, spot_df2, basket_dict, contract_value_df = self._load_assets( trading_model, br=br) mult_results.append( pool.apply_async(self._run_strategy, args=( trading_model, asset_df, spot_df, spot_df2, br, contract_value_df, pretty_portfolio_names[i], ))) for p in mult_results: port, ret_stats = p.get() port_list.append(port) ret_stats_list.append(ret_stats) try: swim_pool.close_pool(pool) except: pass else: for i in range(0, len(parameter_list)): # reset all parameters br = copy.copy(trading_model.load_parameters()) current_parameter = parameter_list[i] # for calculating P&L for k in current_parameter.keys(): setattr(br, k, current_parameter[k]) setattr(br.tech_params, k, current_parameter[k]) # should specify reloading the data, if our parameters impact which assets we are fetching if reload_market_data: asset_df, spot_df, spot_df2, basket_dict, contract_value_df = self._load_assets( trading_model, br=br) port, ret_stats = self._run_strategy(trading_model, asset_df, spot_df, spot_df2, br, contract_value_df, pretty_portfolio_names[i]) port_list.append(port) ret_stats_list.append(ret_stats) port_list = Calculations().pandas_outer_join(port_list) # reset the parameters of the strategy trading_model.br = trading_model.load_parameters() style = Style() ir = [t.inforatio()[0] for t in ret_stats_list] rets = [t.ann_returns()[0] for t in ret_stats_list] # careful with plotting labels, may need to convert to strings pretty_portfolio_names = [str(p) for p in pretty_portfolio_names] # plot all the variations style.resample = 'B' style.file_output = self.DUMP_PATH + trading_model.FINAL_STRATEGY + ' ' + parameter_type + '.png' style.html_file_output = self.DUMP_PATH + trading_model.FINAL_STRATEGY + ' ' + parameter_type + '.html' style.scale_factor = trading_model.SCALE_FACTOR style.title = trading_model.FINAL_STRATEGY + ' ' + parameter_type self.chart.plot(port_list, chart_type='line', style=style) # plot all the IR in a bar chart form (can be easier to read!) style = Style() style.file_output = self.DUMP_PATH + trading_model.FINAL_STRATEGY + ' ' + parameter_type + ' IR.png' style.html_file_output = self.DUMP_PATH + trading_model.FINAL_STRATEGY + ' ' + parameter_type + ' IR.html' style.scale_factor = trading_model.SCALE_FACTOR style.title = trading_model.FINAL_STRATEGY + ' ' + parameter_type summary_ir = pandas.DataFrame(index=pretty_portfolio_names, data=ir, columns=['IR']) self.chart.plot(summary_ir, chart_type='bar', style=style) # plot all the rets style.file_output = self.DUMP_PATH + trading_model.FINAL_STRATEGY + ' ' + parameter_type + ' Rets.png' style.html_file_output = self.DUMP_PATH + trading_model.FINAL_STRATEGY + ' ' + parameter_type + ' Rets.html' summary_rets = pandas.DataFrame(index=pretty_portfolio_names, data=rets, columns=['Rets (%)']) * 100 self.chart.plot(summary_rets, chart_type='bar', style=style) return port_list, summary_ir, summary_rets def _load_assets(self, trading_model, br): assets = trading_model.load_assets(br=br) asset_df = assets[0] spot_df = assets[1] spot_df2 = assets[2] basket_dict = assets[3] if len(assets) == 5: # for future use contract_value_df = assets[4] contract_value_df = None return asset_df, spot_df, spot_df2, basket_dict, contract_value_df def _run_strategy(self, trading_model, asset_df, spot_df, spot_df2, br, contract_value_df, pretty_portfolio_name): logger = LoggerManager().getLogger(__name__) logger.info("Calculating... " + str(pretty_portfolio_name)) signal_df = trading_model.construct_signal(spot_df, spot_df2, br.tech_params, br, run_in_parallel=False) backtest = Backtest() backtest.calculate_trading_PnL(br, asset_df, signal_df, contract_value_df, False) ret_stats = backtest.portfolio_pnl_ret_stats() stats = str(backtest.portfolio_pnl_desc()[0]) port = backtest.portfolio_cum().resample('B').mean() port.columns = [str(pretty_portfolio_name) + ' ' + stats] return port, ret_stats ###### Parameters and signal generations (need to be customised for every model) ###### Plot all the output seperately def run_arbitrary_sensitivity_separately(self, trading_model, parameter_list=None, pretty_portfolio_names=None, strip=None): # asset_df, spot_df, spot_df2, basket_dict = strat.fill_assets() final_strategy = trading_model.FINAL_STRATEGY for i in range(0, len(parameter_list)): br = trading_model.fill_backtest_request() current_parameter = parameter_list[i] # for calculating P&L for k in current_parameter.keys(): setattr(br, k, current_parameter[k]) trading_model.FINAL_STRATEGY = final_strategy + " " + pretty_portfolio_names[ i] self.logger.info("Calculating... " + pretty_portfolio_names[i]) trading_model.br = br trading_model.construct_strategy(br=br) trading_model.plot_strategy_pnl() trading_model.plot_strategy_leverage() trading_model.plot_strategy_group_benchmark_pnl(strip=strip) # reset the parameters of the strategy trading_model.br = trading_model.fill_backtest_request() trading_model.FINAL_STRATEGY = final_strategy def run_day_of_month_analysis(self, trading_model, resample_freq='B'): from finmarketpy.economics.seasonality import Seasonality logger = LoggerManager().getLogger(__name__) calculations = Calculations() seas = Seasonality() trading_model.construct_strategy() pnl = trading_model.strategy_pnl() # get seasonality by day of the month pnl = pnl.resample('B').mean() rets = calculations.calculate_returns(pnl) bus_day = seas.bus_day_of_month_seasonality( rets, add_average=True, resample_freq=resample_freq) # get seasonality by month pnl = pnl.resample('BM').mean() rets = calculations.calculate_returns(pnl) month = seas.monthly_seasonality(rets) logger.info("About to plot seasonality...") style = Style() # Plotting spot over day of month/month of year style.color = 'Blues' style.scale_factor = trading_model.SCALE_FACTOR style.file_output = self.DUMP_PATH + trading_model.FINAL_STRATEGY + ' seasonality day of month.png' style.html_file_output = self.DUMP_PATH + trading_model.FINAL_STRATEGY + ' seasonality day of month.html' style.title = trading_model.FINAL_STRATEGY + ' day of month seasonality' style.display_legend = False style.color_2_series = [bus_day.columns[-1]] style.color_2 = ['red'] # red, pink style.linewidth_2 = 4 style.linewidth_2_series = [bus_day.columns[-1]] style.y_axis_2_series = [bus_day.columns[-1]] self.chart.plot(bus_day, chart_type='line', style=style) style = Style() style.scale_factor = trading_model.SCALE_FACTOR style.file_output = self.DUMP_PATH + trading_model.FINAL_STRATEGY + ' seasonality month of year.png' style.html_file_output = self.DUMP_PATH + trading_model.FINAL_STRATEGY + ' seasonality month of year.html' style.title = trading_model.FINAL_STRATEGY + ' month of year seasonality' self.chart.plot(month, chart_type='line', style=style) return month
# run_example = 1 - plot US GDP QoQ (real) and nominal with Plotly/Bokeh/Matplotlib with subplots for each line # run_example = 2 - plot US GDP QoQ (real + nominal) in two double plots (passing an array of dataframes) run_example = 0 if run_example == 1 or run_example == 0: df = Quandl.get(["FRED/A191RL1Q225SBEA", "FRED/A191RP1Q027SBEA"], authtoken=quandl_api_key) df.columns = ["Real QoQ", "Nominal QoQ"] # set the style of the plot style = Style(title="US GDP", source="Quandl/Fred", subplots=True) # Chart object is initialised with the dataframe and our chart style chart = Chart(df=df, chart_type='line', style=style) chart.plot(engine='matplotlib') chart.plot(engine='bokeh') chart.plot(engine='plotly') if run_example == 2 or run_example == 0: df = Quandl.get(["FRED/A191RL1Q225SBEA", "FRED/A191RP1Q027SBEA"], authtoken=quandl_api_key) df.columns = ["Real QoQ", "Nominal QoQ"] df = [df, df] # set the style of the plot style = Style(title="US GDP double plot", source="Quandl/Fred", subplots=True)
cache_algo = 'internet_load_return') # how to return data df = market.fetch_market(md_request) df_ret = calc.calculate_returns(df) day_of_month_seasonality = seasonality.bus_day_of_month_seasonality(df_ret, partition_by_month = False) day_of_month_seasonality = calc.convert_month_day_to_date_time(day_of_month_seasonality) style = Style() style.date_formatter = '%b' style.title = 'Gold seasonality' style.scale_factor = 3 style.file_output = "gold-seasonality.png" chart.plot(day_of_month_seasonality, style=style) ###### calculate seasonal moves in FX vol (using Bloomberg data) if run_example == 2 or run_example == 0: tickers = ['EURUSDV1M', 'USDJPYV1M', 'GBPUSDV1M', 'AUDUSDV1M'] md_request = MarketDataRequest( start_date = "01 Jan 1996", # start date data_source = 'bloomberg', # use Bloomberg as data source tickers = tickers, fields = ['close'], # which fields to download vendor_tickers = [x + ' Curncy' for x in tickers], # ticker (Bloomberg) vendor_fields = ['PX_LAST'], # which Bloomberg fields to download cache_algo = 'internet_load_return') # how to return data df = market.fetch_market(md_request)
class QuickChart(object): """Displays charts from downloaded data, in a single line code call. Ideal for quickly generating charts from sources including Bloomberg, Quandl, ALFRED/FRED etc. """ def __init__(self, engine='plotly', data_source='bloomberg', market_data_generator=MarketDataGenerator()): self._chart = Chart(engine=engine) self._market = Market(market_data_generator=market_data_generator) self._data_source = data_source def plot_chart(self, tickers=None, tickers_rhs=None, start_date=None, finish_date=None, chart_file=None, chart_type='line', title='', fields={'close': 'PX_LAST'}, freq='daily', source='Web', brand_label='Cuemacro', display_brand_label=True, reindex=False, additive_index=False, yoy=False, plotly_plot_mode='offline_png', quandl_api_key=dataconstants.quandl_api_key, fred_api_key=dataconstants.fred_api_key, alpha_vantage_api_key=dataconstants.alpha_vantage_api_key, df=None): if start_date is None: start_date = datetime.datetime.utcnow().date() - timedelta(days=60) if finish_date is None: finish_date = datetime.datetime.utcnow() if isinstance(tickers, str): tickers = {tickers: tickers} elif isinstance(tickers, list): tickers_dict = {} for t in tickers: tickers_dict[t] = t tickers = tickers_dict if tickers_rhs is not None: if isinstance(tickers_rhs, str): tickers_rhs = {tickers_rhs: tickers_rhs} elif isinstance(tickers, list): tickers_rhs_dict = {} for t in tickers_rhs: tickers_rhs_dict[t] = t tickers_rhs = tickers_rhs_dict tickers.update(tickers_rhs) else: tickers_rhs = {} if df is None: md_request = MarketDataRequest( start_date=start_date, finish_date=finish_date, freq=freq, data_source=self._data_source, tickers=list(tickers.keys()), vendor_tickers=list(tickers.values()), fields=list(fields.keys()), vendor_fields=list(fields.values()), quandl_api_key=quandl_api_key, fred_api_key=fred_api_key, alpha_vantage_api_key=alpha_vantage_api_key) df = self._market.fetch_market(md_request=md_request) df = df.fillna(method='ffill') df.columns = [x.split('.')[0] for x in df.columns] style = Style(title=title, chart_type=chart_type, html_file_output=chart_file, scale_factor=-1, height=400, width=600, file_output=datetime.date.today().strftime("%Y%m%d") + " " + title + ".png", plotly_plot_mode=plotly_plot_mode, source=source, brand_label=brand_label, display_brand_label=display_brand_label) if reindex: df = Calculations().create_mult_index_from_prices(df) style.y_title = 'Reindexed from 100' if additive_index: df = (df - df.shift(1)).cumsum() style.y_title = 'Additive changes from 0' if yoy: if freq == 'daily': obs_in_year = 252 elif freq == 'intraday': obs_in_year = 1440 df_rets = Calculations().calculate_returns(df) df = Calculations().average_by_annualised_year( df_rets, obs_in_year=obs_in_year) * 100 style.y_title = 'Annualized % YoY' if list(tickers_rhs.keys()) != []: style.y_axis_2_series = list(tickers_rhs.keys()) style.y_axis_2_showgrid = False style.y_axis_showgrid = False return self._chart.plot(df, style=style), df
# choose run_example = 0 for everything # run_example = 1 - download BoE data from quandl run_example = 0 ###### fetch data from Quandl for BoE rate (using Bloomberg data) if run_example == 1 or run_example == 0: # Monthly average of UK resident monetary financial institutions' (excl. Central Bank) sterling # Weighted average interest rate, other loans, new advances, on a fixed rate to private non-financial corporations (in percent) # not seasonally adjusted md_request = MarketDataRequest( start_date="01 Jan 2000", # start date data_source='quandl', # use Quandl as data source tickers=['Weighted interest rate'], fields=['close'], # which fields to download vendor_tickers=['BOE/CFMBJ84'], # ticker (Bloomberg) vendor_fields=['close'], # which Bloomberg fields to download cache_algo='internet_load_return') # how to return data df = market.fetch_market(md_request) style = Style() style.title = 'BoE weighted interest rate' style.scale_factor = 3 style.file_output = "boe-rate.png" style.source = 'Quandl/BoE' chart.plot(df, style=style)
finish_date='01 Aug 2016', data_source='bloomberg', cut='LDN', category='fx-vol-market', tickers=['GBPUSD'], cache_algo='cache_algo_return') df = market.fetch_market(md_request) style = Style() style.title = 'GBPUSD 1M Implied Vol' style.scale_factor = 3 style.source = 'Bloomberg' chart.plot(df['GBPUSDV1M.close'], style=style) ###### Fetch market data for pricing GBPUSD FX options over Brexit vote (ie. FX spot, FX forwards, FX deposits and FX vol quotes) ###### Construct volatility surface using FinancePy library underneath, using polynomial interpolation if run_example == 2 or run_example == 0: # Download the whole all market data for GBPUSD for pricing options (vol surface) md_request = MarketDataRequest(start_date='20 Jun 2016', finish_date='20 Jun 2016', data_source='bloomberg', cut='LDN', category='fx-vol-market', tickers=['GBPUSD'], cache_algo='cache_algo_return') df = market.fetch_market(md_request)