def create_qq_chart(strategy: QFSeries) -> Chart: colors = Chart.get_axes_colors() strategy = strategy.to_log_returns() # Normalize strategy = strategy - strategy.mean() strategy = strategy / strategy.std() # Sort strategy_values = sorted(strategy.values) # Create benchmark benchmark_values = list(range(1, len(strategy_values) + 1)) n = len(strategy_values) + 1 benchmark_values = map(lambda x: x / n, benchmark_values) benchmark_values = list(map(lambda x: norm.ppf(x), benchmark_values)) # Figure out the limits. maximum = max(max(benchmark_values), max(strategy_values)) result = LineChart(start_x=-maximum, end_x=maximum, upper_y=maximum, lower_y=-maximum) result.add_decorator(ScatterDecorator( benchmark_values, strategy_values, color=colors[0], alpha=0.6, edgecolors='black', linewidths=0.5)) result.add_decorator(VerticalLineDecorator(0, color='black', linewidth=1)) result.add_decorator(HorizontalLineDecorator(0, color='black', linewidth=1)) result.add_decorator(TitleDecorator("Normal Distribution Q-Q")) result.add_decorator(AxesLabelDecorator("Normal Distribution Quantile", "Observed Quantile")) # Add diagonal line. result.add_decorator(DiagonalLineDecorator(color=colors[1])) return result
def _get_histogram_chart(self): colors = Chart.get_axes_colors() chart = HistogramChart(self.returns_of_trades * 100) # expressed in % # Format the x-axis so that its labels are shown as a percentage. x_axis_formatter = FormatStrFormatter("%0.0f%%") axes_formatter_decorator = AxesFormatterDecorator(x_major=x_axis_formatter, key="axes_formatter") chart.add_decorator(axes_formatter_decorator) # Only show whole numbers on the y-axis. y_axis_locator = MaxNLocator(integer=True) axes_locator_decorator = AxesLocatorDecorator(y_major=y_axis_locator, key="axes_locator") chart.add_decorator(axes_locator_decorator) # Add an average line. avg_line = VerticalLineDecorator(self.returns_of_trades.values.mean(), color=colors[1], key="average_line_decorator", linestyle="--", alpha=0.8) chart.add_decorator(avg_line) # Add a legend. legend = LegendDecorator(key="legend_decorator") legend.add_entry(avg_line, "Mean") chart.add_decorator(legend) # Add a title. title = TitleDecorator("Distribution of Trades", key="title_decorator") chart.add_decorator(title) chart.add_decorator(AxesLabelDecorator("Return", "Occurrences")) return chart
def _get_distribution_plot(self, data_series: SimpleReturnsSeries, title: str, bins: Union[int, str] = 50, crop: bool = False): colors = Chart.get_axes_colors() if crop: start_x = np.quantile(data_series, 0.01) end_x = np.quantile(data_series, 0.99) chart = HistogramChart(data_series, bins=bins, start_x=start_x, end_x=end_x) else: chart = HistogramChart(data_series, bins=bins) # Only show whole numbers on the y-axis. y_axis_locator = MaxNLocator(integer=True) axes_locator_decorator = AxesLocatorDecorator(y_major=y_axis_locator, key="axes_locator") chart.add_decorator(axes_locator_decorator) # Add an average line. avg_line = VerticalLineDecorator(data_series.mean(), color=colors[1], key="average_line_decorator", linestyle="--", alpha=0.8) chart.add_decorator(avg_line) # Add a legend. legend = LegendDecorator(key="legend_decorator") legend.add_entry(avg_line, "Mean") chart.add_decorator(legend) # Add a title. title_decorator = TitleDecorator(title, key="title") chart.add_decorator(title_decorator) chart.add_decorator(AxesLabelDecorator(title, "Occurrences")) position_decorator = AxesPositionDecorator(*self.full_image_axis_position) chart.add_decorator(position_decorator) return chart
def __init__( self, x_data: Sequence, y_data: Sequence, size: int=40, color=None, key: str = None, **plot_settings: Any): """ Creates a scatter plot based on the data specified. Parameters ---------- x_data values of x coordinate y_data values of y coordinate size size in points^2; scalar or an array of the same length as x_data and y_data color *c* can be a single color format string, or a sequence of color specifications of length x_data and y_data, or a sequence of x_data and y_data numbers to be mapped to colors using the *cmap* and *norm* specified via kwargs (see below). Note that color should not be a single numeric RGB or RGBA sequence because that is indistinguishable from an array of values to be colormapped. color can be a 2-D array in which the rows are RGB or RGBA, however, including the case of a single row to specify the same color for all points. plot_settings other settings like for example: alpha, linewidths, verts, edgecolors """ ChartDecorator.__init__(self, key) SimpleLegendItem.__init__(self) self.x_data = x_data self.y_data = y_data self.size = size if color is None: self.color = Chart.get_axes_colors()[0] else: self.color = color self.plot_settings = plot_settings
def risk_contribution_chart(self) -> BarChart: colors_palette = Chart.get_axes_colors() tickers = self.model.input_data.regressors_df.columns.values names = [self._get_security_name(ticker) for ticker in tickers] risk_contributions = QFSeries(data=self.model.risk_contribution.values, index=pd.Index(names)) index_translator = self._get_index_translator(labels=names) bar_chart = BarChart(orientation=Orientation.Horizontal, index_translator=index_translator, thickness=self._bars_width, align='center') bar_chart.add_decorator( DataElementDecorator(risk_contributions, color=colors_palette[1])) bar_chart.add_decorator(TitleDecorator("Risk contribution")) bar_chart.add_decorator( AxesLabelDecorator(x_label="risk contribution [%]")) bar_chart.add_decorator( AxesFormatterDecorator(x_major=PercentageFormatter())) labels = ('{:.2f}'.format(value * 100) for value in risk_contributions) self._add_labels_for_bars(bar_chart, risk_contributions, labels, margin=0.001) return bar_chart
def cooks_distance_chart(self) -> LineChart: cooks_dist = self.model.cooks_distance_tms chart = LineChart() colors = cycle(Chart.get_axes_colors()) color = next(colors) marker_props = {'alpha': 0.7} stemline_props = {'linestyle': '-.', 'linewidth': 0.2} baseline_props = {'visible': False} marker_props['markeredgecolor'] = color marker_props['markerfacecolor'] = color stemline_props['color'] = color data_elem = StemDecorator(cooks_dist, marker_props=marker_props, stemline_props=stemline_props, baseline_props=baseline_props) chart.add_decorator(data_elem) chart.add_decorator(TitleDecorator("Cook's Distance")) chart.add_decorator( AxesLabelDecorator(y_label="max change of coefficients")) return chart
def apply_data_element_decorators( self, data_element_decorators: List[DataElementDecorator]) -> Any: default_colors = Chart.get_axes_colors() default_color_iter = cycle(default_colors) # Holds the positions of the bars that have been plotted most recently. It is used to stack # bars on top of each other and tracks where to place the bars so that they are on top of each other. last_data_element_positions = (None, None) # (Positive, Negative) for data_element in data_element_decorators: # copy the general plot settings and add DataElementDecorator-specific plot settings to the copy # (overwrite general plot settings if necessary) plot_settings = dict(self._plot_settings) plot_settings.update(data_element.plot_settings) # set color for the bars if it's not specified if "color" not in plot_settings: plot_settings["color"] = next(default_color_iter) data = self._trim_data(data_element.data) # Pick the axes to plot on. axes = self.axes if data_element.use_secondary_axes: self.setup_secondary_axes_if_necessary() axes = self.secondary_axes bars = self._plot_data(axes, data, last_data_element_positions, plot_settings) data_element.legend_artist = bars last_data_element_positions = self._calculate_last_data_positions( data, last_data_element_positions)
def plot(self, figsize: Tuple[float, float] = None): self._setup_axes_if_necessary(figsize) cone = AnalyticalCone(self.data) cone_data_frame = cone.calculate_aggregated_cone(self.nr_of_data_points, self.is_end_date, 0) strategy_tms = cone_data_frame['Strategy'] mean_tms = cone_data_frame['Expectation'] ax = self.axes ax.plot(strategy_tms) ax.plot(mean_tms) cone_colors = cycle(Chart.get_axes_colors()[2:4]) # fill areas for every standard deviation for cone_std in self.cone_stds: upper_df = cone.calculate_aggregated_cone(self.nr_of_data_points, self.is_end_date, cone_std) lower_df = cone.calculate_aggregated_cone(self.nr_of_data_points, self.is_end_date, -cone_std) upper_bound = upper_df['Expectation'] lower_bound = lower_df['Expectation'] ax.fill_between( cone_data_frame.index, lower_bound, upper_bound, color=next(cone_colors), alpha=self.cone_opacity) ax.set_xlabel('Days in the past') ax.set_ylabel('Current valuation') ax.set_title('Performance vs. Expectation') ax.set_xlim(0, self.nr_of_data_points)
def __init__(self, prices: QFSeries, count: int, colors: List[str] = None, key: str = None): """ Construct a new TopDrawdownDecorator. The top ``count`` amount of drawdowns will be highlighted. If ``colors`` is ``None`` then a default list of colours will be used, you can override it by specifying a list of strings containing color names or hex codes. Parameters ---------- prices A series from which drawdowns will be calculated. count The amount of longest drawdowns to highlight. colors A list of colours to use to highlight the drawdowns. """ super().__init__(key) if colors is None: self._color = Chart.get_axes_colors()[3] else: self._color = cycle(colors) self._current_color = 0 self._series = prices self._count = count
def create_returns_distribution(returns: QFSeries, frequency: Frequency = Frequency.MONTHLY, title: str = None) -> \ HistogramChart: """ Creates a new returns distribution histogram with the specified frequency. Parameters ---------- returns: QFSeries The returns series to use in the histogram. frequency: Frequency frequency of the returns after aggregation title title of the chart Returns ------- HistogramChart A new ``HistogramChart`` instance. """ colors = Chart.get_axes_colors() aggregate_returns = get_aggregate_returns(returns, frequency, multi_index=True).multiply(100) chart = HistogramChart(aggregate_returns) # Format the x-axis so that its labels are shown as a percentage. x_axis_formatter = FormatStrFormatter("%.0f%%") axes_formatter_decorator = AxesFormatterDecorator(x_major=x_axis_formatter, key="axes_formatter") chart.add_decorator(axes_formatter_decorator) # Only show whole numbers on the y-axis. y_axis_locator = MaxNLocator(integer=True) axes_locator_decorator = AxesLocatorDecorator(y_major=y_axis_locator, key="axes_locator") chart.add_decorator(axes_locator_decorator) # Add an average line. avg_line = VerticalLineDecorator(aggregate_returns.values.mean(), color=colors[1], key="average_line_decorator", linestyle="--", alpha=0.8) chart.add_decorator(avg_line) # Add a legend. legend = LegendDecorator(key="legend_decorator") legend.add_entry(avg_line, "Mean") chart.add_decorator(legend) # Add a title. if title is None: title = "Distribution of " + str(frequency).capitalize() + " Returns" title = TitleDecorator(title, key="title_decorator") chart.add_decorator(title) chart.add_decorator(AxesLabelDecorator("Returns", "Occurrences")) return chart
def plot(self, figsize: Tuple[float, float] = None): self._setup_axes_if_necessary(figsize) plot_kwargs = self.plot_settings # Plot the boxes. colors = Chart.get_axes_colors() sns.boxplot(ax=self.axes, data=self._data, palette=colors, **plot_kwargs) self._apply_decorators() self._adjust_style()
def decorate(self, chart: "Chart") -> None: prices_tms = self.series cone = AnalyticalCone(prices_tms) ax = chart.axes colors = Chart.get_axes_colors() mean_tms = cone.calculate_simple_cone(self._live_start_date, 0) ax.plot(mean_tms, color=colors[1]) for cone_std in self._cone_stds: upper_bound_tms = cone.calculate_simple_cone(self._live_start_date, cone_std) lower_bound_tms = cone.calculate_simple_cone(self._live_start_date, -cone_std) ax.fill_between(upper_bound_tms.index, upper_bound_tms, lower_bound_tms, alpha=self._colors_alpha)
def create_returns_bar_chart( returns: QFSeries, frequency: Frequency = Frequency.YEARLY) -> BarChart: """ Constructs a new returns bar chart based on the returns specified. By default a new annual returns bar chart will be created. """ colors = Chart.get_axes_colors() # Calculate data. aggregate_returns = get_aggregate_returns(returns, frequency, multi_index=True) data_series = QFSeries(aggregate_returns.sort_index(ascending=True)) chart = BarChart(Orientation.Horizontal, align="center") chart.add_decorator(DataElementDecorator(data_series)) chart.add_decorator(BarValuesDecorator(data_series)) # Format the x-axis so that its labels are shown as a percentage. chart.add_decorator(AxesFormatterDecorator(x_major=PercentageFormatter())) # Format Y axis to make sure we have a tick for each year or 2 years if len(data_series) > 10: y_labels = data_series[data_series.index % 2 == 1].index else: y_labels = data_series.index chart.add_decorator( AxisTickLabelsDecorator(labels=y_labels, axis=Axis.Y, tick_values=y_labels)) # Add an average line. avg_line = VerticalLineDecorator(aggregate_returns.values.mean(), color=colors[1], key="avg_line", linestyle="--", alpha=0.8) chart.add_decorator(avg_line) # Add a legend. legend = LegendDecorator(key="legend_decorator") legend.add_entry(avg_line, "Mean") chart.add_decorator(legend) # Add a title. title = TitleDecorator(str(frequency).capitalize() + " Returns", key="title_decorator") chart.add_decorator(title) chart.add_decorator(AxesLabelDecorator("Returns", "Year")) return chart
def _plot_tail_data(self, regression_line, beta, alpha, r_squared, max_ret): colors = Chart.get_axes_colors() self.axes.plot(regression_line.index.values, regression_line.values, axes=self.axes, color=colors[2]) self.axes.set_xlim([-max_ret, max_ret]) self.axes.set_ylim([-max_ret, max_ret]) props = dict(boxstyle='square', facecolor=colors[2], alpha=0.5) textstr = 'tail $\\beta={0:.2f}$\ntail $\\alpha={1:.2%}$$\%$\ntail $R^2={2:.2}$'.format(beta, alpha, r_squared) font_size = mpl.rcParams['legend.fontsize'] self.axes.text( 0.80, 0.35, textstr, transform=self.axes.transAxes, bbox=props, verticalalignment='top', fontsize=font_size)
def __init__(self, prices: QFSeries, count: int, colors: List[str] = None, key: str = None): super().__init__(key) if colors is None: self._color = Chart.get_axes_colors()[3] else: self._color = cycle(colors) self._current_color = 0 self._series = prices self._count = count
def apply_data_element_decorators( self, data_element_decorators: List["DataElementDecorator"]): colors = cycle(Chart.get_axes_colors()) for data_element in data_element_decorators: plot_settings = data_element.plot_settings.copy() plot_settings.setdefault("color", next(colors)) series = data_element.data trimmed_series = self._trim_data(series) drawdown_series = drawdown_tms(trimmed_series) drawdown_series *= -1 axes = self._ax axes.yaxis.set_major_formatter(PercentageFormatter()) axes.fill_between(drawdown_series.index, 0, drawdown_series.values) axes.set_ylim(top=0)
def beta_and_alpha_chart(self, benchmark_coefficients: Sequence[float] = None ) -> BarChart: colors_palette = Chart.get_axes_colors() coeff_names = [ self._get_security_name(ticker) for ticker in self.model.coefficients.index.values ] coeff_values = self.model.coefficients.values bars_colors = [colors_palette[0]] * len(self.model.coefficients) title = 'Coefficients of regressors' if self.model.input_data.is_fit_intercept: coeff_names = np.insert(coeff_names, 0, "intercept") coeff_values = np.insert(coeff_values, 0, self.model.intercept) bars_colors = ['gold'] + bars_colors if benchmark_coefficients is not None: raise ValueError( "Benchmark coefficients aren't used when model contains a bias value (constant)" ) elif benchmark_coefficients is not None: coeff_values -= benchmark_coefficients title = 'Relative coefficients of regressors' index_translator = self._get_index_translator(coeff_names) coefficients = QFSeries(index=pd.Index(coeff_names), data=coeff_values) bar_chart = BarChart(orientation=Orientation.Horizontal, index_translator=index_translator, thickness=self._bars_width, align='center') bar_chart.add_decorator( DataElementDecorator(coefficients, color=bars_colors)) bar_chart.add_decorator(TitleDecorator(title)) bar_chart.add_decorator(AxesLabelDecorator(x_label="sensitivity")) labels = ['{:.2f}'.format(value) for value in coeff_values] self._add_labels_for_bars(bar_chart, coefficients, labels) return bar_chart
def performance_attribution_chart(self) -> BarChart: colors_palette = Chart.get_axes_colors() unexplained_ret = self.model.unexplained_performance_attribution_ret factors_ret = self.model.factors_performance_attribution_ret fund_ret = self.model.fund_tms_analysis.cagr unexplained_name = "Unexplained" factors_names = [ self._get_security_name(ticker) for ticker in self.model.coefficients.index.values ] fund_name = self._get_security_name( self.model.input_data.analysed_tms.name) all_values = [unexplained_ret] + list(factors_ret) + [fund_ret] all_names = [unexplained_name] + list(factors_names) + [fund_name] all_returns = SimpleReturnsSeries(data=all_values, index=pd.Index(all_names)) colors = [ colors_palette[0] ] + [colors_palette[1]] * len(factors_names) + [colors_palette[2]] index_translator = self._get_index_translator(labels=all_names) bar_chart = BarChart(orientation=Orientation.Horizontal, index_translator=index_translator, thickness=self._bars_width, align='center') bar_chart.add_decorator(DataElementDecorator(all_returns, color=colors)) bar_chart.add_decorator( TitleDecorator("Attribution of Fund Annualised Return")) bar_chart.add_decorator( AxesLabelDecorator(x_label="annualised return [%]")) bar_chart.add_decorator( AxesFormatterDecorator(x_major=PercentageFormatter())) labels = ('{:.2f}'.format(value * 100) for value in all_returns) self._add_labels_for_bars(bar_chart, all_returns, labels) return bar_chart
def decorate(self, chart) -> None: cone = AnalyticalConeBase() ax = chart.axes colors = Chart.get_axes_colors() mean_tms = cone.calculate_simple_cone_for_process( self._mean, self._std, 0, self._steps, self._starting_value) ax.plot(mean_tms, color=colors[1]) for cone_std in self._cone_stds: upper_bound_tms = cone.calculate_simple_cone_for_process( self._mean, self._std, cone_std, self._steps, self._starting_value) lower_bound_tms = cone.calculate_simple_cone_for_process( self._mean, self._std, -cone_std, self._steps, self._starting_value) ax.fill_between(upper_bound_tms.index, upper_bound_tms, lower_bound_tms, alpha=self._colors_alpha)
def apply_data_element_decorators( self, data_element_decorators: List["DataElementDecorator"]): colors = cycle(Chart.get_axes_colors()) for data_element in data_element_decorators: plot_settings = data_element.plot_settings.copy() plot_settings.setdefault("color", next(colors)) series = data_element.data trimmed_series = self._trim_data(series) axes = self._ax if data_element.use_secondary_axes: mpl.rcParams[ 'axes.spines.right'] = True # Ensure that the right axes spine is shown. self.setup_secondary_axes_if_necessary() axes = self._secondary_axes handle = axes.plot(trimmed_series, **plot_settings)[0] data_element.legend_artist = handle
def regressors_and_explained_variable_chart(self) -> LineChart: regressors_df = self.model.input_data.regressors_df fund_tms = self.model.input_data.analysed_tms chart = LineChart() legend = LegendDecorator() # add data to the chart and the legend marker_props_template = {'alpha': 0.5} stemline_props_template = {'linestyle': '-.', 'linewidth': 0.2} baseline_props = {'visible': False} regressors_and_fund_df = pd.concat([regressors_df, fund_tms], axis=1) colors = cycle(Chart.get_axes_colors()) for ticker, series in regressors_and_fund_df.iteritems(): marker_props = marker_props_template.copy() stemline_props = stemline_props_template.copy() color = next(colors) marker_props['markeredgecolor'] = color marker_props['markerfacecolor'] = color stemline_props['color'] = color data_elem = StemDecorator(series, marker_props=marker_props, stemline_props=stemline_props, baseline_props=baseline_props) chart.add_decorator(data_elem) legend.add_entry(data_elem, self._get_security_name(ticker)) # add decorators to the chart chart.add_decorator(TitleDecorator("Returns")) chart.add_decorator(AxesLabelDecorator(y_label="return [%]")) chart.add_decorator(legend) chart.add_decorator( AxesFormatterDecorator(y_major=PercentageFormatter())) return chart
def plot(self, figsize: Tuple[float, float] = None): self._setup_axes_if_necessary(figsize) cone = AnalyticalConeOOS() cone_data_frame = cone.calculate_aggregated_cone_oos_only( self.oos_series, self.is_mean_return, self.is_sigma, 0) strategy_tms = cone_data_frame['Strategy'] mean_tms = cone_data_frame['Expectation'] ax = self.axes ax.plot(strategy_tms) ax.plot(mean_tms) cone_colors = cycle(Chart.get_axes_colors()[2:4]) # fill areas for every standard deviation for cone_std in self.cone_stds: upper_df = cone.calculate_aggregated_cone_oos_only( self.oos_series, self.is_mean_return, self.is_sigma, cone_std) lower_df = cone.calculate_aggregated_cone_oos_only( self.oos_series, self.is_mean_return, self.is_sigma, -cone_std) upper_bound = upper_df['Expectation'] lower_bound = lower_df['Expectation'] ax.fill_between(cone_data_frame.index, lower_bound, upper_bound, color=next(cone_colors), alpha=self.cone_opacity) ax.set_xlabel('Days in the past') ax.set_ylabel('Current valuation') ax.set_title('Performance vs. Expectation') ax.set_xlim(0, self.oos_series.size) self._insert_valuation_text_box(cone, strategy_tms) self._apply_decorators()
def _plot_data(self, datapoints_tms, regression_line, beta, alpha, r_squared, max_ret): colors = Chart.get_axes_colors() self.axes.scatter(x=datapoints_tms.iloc[:, 0], y=datapoints_tms.iloc[:, 1], c=colors[0], alpha=0.6, edgecolors='black', linewidths=0.5) self.axes.axhline(0, color='black', axes=self.axes, linewidth=1) self.axes.axvline(0, color='black', axes=self.axes, linewidth=1) self.axes.plot(regression_line.index.values, regression_line.values, axes=self.axes, color=colors[1]) self.axes.set_xlim([-max_ret, max_ret]) self.axes.set_ylim([-max_ret, max_ret]) props = dict(boxstyle='square', facecolor='white', alpha=0.5) textstr = '$\\beta={0:.2f}$\n$\\alpha={1:.2%}$$\%$\n$R^2={2:.2}$'.format(beta, alpha, r_squared) font_size = mpl.rcParams['legend.fontsize'] self.axes.text( 0.05, 0.95, textstr, transform=self.axes.transAxes, bbox=props, verticalalignment='top', fontsize=font_size) self.axes.xaxis.set_major_formatter(PercentageFormatter()) self.axes.yaxis.set_major_formatter(PercentageFormatter())
def main(): # GENERATE DATA regressors_and_fund_df = QFDataFrame(data=[[1, 3, 5], [2, 3, 1], [3, 1, 2], [4, 2, 3], [5, 3, 4]], index=pd.bdate_range( start='2015-01-01', periods=5), columns=['a', 'b', 'c']) # add data to the chart and the legend marker_props_template = {'alpha': 0.5} stemline_props_template = {'linestyle': '-.', 'linewidth': 0.2} baseline_props = {'visible': True} colors = cycle(Chart.get_axes_colors()) chart = LineChart(start_x=str_to_date('2014-12-31'), end_x=str_to_date('2015-01-08')) legend = LegendDecorator() for name, series in regressors_and_fund_df.iteritems(): marker_props = marker_props_template.copy() stemline_props = stemline_props_template.copy() color = next(colors) marker_props['markeredgecolor'] = color marker_props['markerfacecolor'] = color stemline_props['color'] = color data_elem = StemDecorator(series, marker_props=marker_props, stemline_props=stemline_props, baseline_props=baseline_props) chart.add_decorator(data_elem) legend.add_entry(data_elem, name) chart.add_decorator(legend) chart.plot() plt.show(block=True)
def create_returns_similarity(strategy: QFSeries, benchmark: QFSeries, mean_normalization: bool = True, std_normalization: bool = True, frequency: Frequency = None) -> KDEChart: """ Creates a new returns similarity chart. The frequency is determined by the specified returns series. Parameters ---------- strategy: QFSeries The strategy series to plot. benchmark: QFSeries The benchmark series to plot. mean_normalization: bool Whether to perform mean normalization on the series data. std_normalization: bool Whether to perform variance normalization on the series data. frequency: Frequency Returns can be aggregated in to specific frequency before plotting the chart Returns ------- KDEChart A newly created KDEChart instance. """ chart = KDEChart() colors = Chart.get_axes_colors() if frequency is not None: aggregate_strategy = get_aggregate_returns( strategy.to_simple_returns(), frequency) aggregate_benchmark = get_aggregate_returns( benchmark.to_simple_returns(), frequency) else: aggregate_strategy = strategy.to_simple_returns() aggregate_benchmark = benchmark.to_simple_returns() scaled_strategy = preprocessing.scale(aggregate_strategy, with_mean=mean_normalization, with_std=std_normalization) strategy_data_element = DataElementDecorator(scaled_strategy, bw="scott", shade=True, label=strategy.name, color=colors[0]) chart.add_decorator(strategy_data_element) scaled_benchmark = preprocessing.scale(aggregate_benchmark, with_mean=mean_normalization, with_std=std_normalization) benchmark_data_element = DataElementDecorator(scaled_benchmark, bw="scott", shade=True, label=benchmark.name, color=colors[1]) chart.add_decorator(benchmark_data_element) # Add a title. title = _get_title(mean_normalization, std_normalization, frequency) title_decorator = TitleDecorator(title, key="title") chart.add_decorator(title_decorator) chart.add_decorator(AxesLabelDecorator("Returns", "Similarity")) return chart
def historical_out_of_sample_performance_chart(self) -> LineChart: analysed_tms = self.model.input_data.analysed_tms frequency = self.model.input_data.frequency fund_cumulative_rets = analysed_tms.to_prices( initial_price=1.0, frequency=frequency) - 1 # type: PricesSeries fit_cumulative_rets = self.model.fitted_tms.to_prices( initial_price=1.0, frequency=frequency) - 1 # type: PricesSeries live_start_date = self.model.oos_start_date in_sample_fund_tms = fund_cumulative_rets.loc[:live_start_date] in_sample_fit_tms = fit_cumulative_rets.loc[:live_start_date] out_of_sample_fund_tms = fund_cumulative_rets.loc[live_start_date:] out_of_sample_fit_tms = fit_cumulative_rets.loc[live_start_date:] colors = Chart.get_axes_colors() in_sample_fund_data_elem = DataElementDecorator(in_sample_fund_tms, color=colors[0]) out_of_sample_fund_data_elem = DataElementDecorator( out_of_sample_fund_tms, color=colors[0]) in_sample_fit_data_elem = DataElementDecorator(in_sample_fit_tms, color=colors[1]) out_of_sample_fit_data_elem = DataElementDecorator( out_of_sample_fit_tms, color=colors[1]) legend_decorator = LegendDecorator( legend_placement=Location.LOWER_RIGHT) legend_decorator.add_entry(in_sample_fund_data_elem, self._get_security_name(analysed_tms.name)) legend_decorator.add_entry(in_sample_fit_data_elem, 'Fit') is_vs_oos_performance_chart = LineChart() is_vs_oos_performance_chart.add_decorator(in_sample_fund_data_elem) is_vs_oos_performance_chart.add_decorator(out_of_sample_fund_data_elem) is_vs_oos_performance_chart.add_decorator(in_sample_fit_data_elem) is_vs_oos_performance_chart.add_decorator(out_of_sample_fit_data_elem) is_vs_oos_performance_chart.add_decorator( AxesFormatterDecorator(y_major=PercentageFormatter())) is_vs_oos_performance_chart.add_decorator( AxesLabelDecorator(y_label="Cumulative return [%]")) is_vs_oos_performance_chart.add_decorator(legend_decorator) is_vs_oos_performance_chart.add_decorator( TextDecorator("In Sample ", x=DataCoordinate(live_start_date), y=AxesCoordinate(0.99), verticalalignment='top', horizontalalignment='right')) is_vs_oos_performance_chart.add_decorator( TextDecorator(" Out Of Sample", x=DataCoordinate(live_start_date), y=AxesCoordinate(0.99), verticalalignment='top', horizontalalignment='left')) last_date = fund_cumulative_rets.index[-1] is_vs_oos_performance_chart.add_decorator( VerticalSpanDecorator(x_min=live_start_date, x_max=last_date)) return is_vs_oos_performance_chart
def create_bar_chart(series_list: List[QFSeries], names_list, title: str, lines: List[QFSeries], recession_series: QFSeries = None, start_x: datetime = None, end_x: datetime = None, quarterly: bool = False, date_label_format: Tuple[str, str] = ("%Y", "%y Q{}"), recession_name: str = None) -> BarChart: """ Creates a new bar chart based on the settings specified. This function makes some assumptions about the type of bar chart to create, but it should cover >90% of cases. Parameters ---------- series_list names_list title lines One or more series representing the lines to draw on the bar chart. recession_series A series that will be used to highlight recession periods using gray boxes. start_x The first date to plot from the specified series. end_x The last date to plot from the specified series. quarterly Whether the bar chart should be formatted for quarterly frequency series. date_label_format The format for the date labels in the x-axis. It can contain a format parameter which will be replaced with the quarter. The first format is for labels that are not shown every quarter, whereas the second format is used for labels that are shown on every quarter. recession_name Example "US Recession" Returns ------- BarChart """ assert len(names_list) > len(lines) + 1, \ "Not all labels have been specified. Specify one in the list for each series and line." bar_chart = BarChart(orientation=Orientation.Vertical, start_x=start_x, end_x=end_x, thickness=60 if quarterly else 20, align="center") bar_chart.tick_fontweight = "bold" bar_chart.tick_color = "black" series_start = series_list[0].index.min() series_end = series_list[0].index.max() data_elements = [] for series in series_list: # Find the smallest series start and largest series end among all series. if series.index.min() < series_start: series_start = series.index.min() if series.index.max() > series_end: series_end = series.index.max() # Add the series to the bar chart. data_element = DataElementDecorator(series) data_elements.append(data_element) bar_chart.add_decorator(data_element) # Get the list of colors from the current stylesheet. style_colors = Chart.get_axes_colors() line_decorators = [] for i in range(0, len(lines)): # Grab colors from the end so that they do not clash with the bars. color = style_colors[(len(style_colors) - i % len(style_colors)) - 1] # Add a series line decorator for each line. line_decorator = SeriesLineDecorator(lines[i][start_x:end_x], key="series_line_" + str(i), linewidth=4, color=color) line_decorators.append(line_decorator) bar_chart.add_decorator(line_decorator) # Create a title. if title is not None: title_decorator = TitleDecorator(title, key="title") bar_chart.add_decorator(title_decorator) # Create a legend. legend_decorator = _create_legend(bar_chart, data_elements, line_decorators, names_list, quarterly) # Create spans (rectangles) to highlight the recession periods. if recession_series is not None: span_decorator = SpanDecorator.from_int_list(recession_series, key="span") bar_chart.add_decorator(span_decorator) if recession_name is not None: legend_decorator.add_entry(span_decorator, recession_name) if quarterly: # Format the ticks. # Determine (roughly) how many years passed between ``start`` and ``end``. display_start = series_start if start_x is None else start_x display_end = series_end if end_x is None else end_x years = (display_end - display_start).days // 365 # Determine how often to show the ticks. # N.B. The show_every value depends on the locator defined below. if years < 2: show_every = 1 # Every quarter. date_format = date_label_format[1] elif years > 10: show_every = 5 # Every 5 years. date_format = date_label_format[0] else: show_every = 4 # Every year (4 quarters). date_format = date_label_format[0] func = lambda x, pos: _quarterly_formatter(x, pos, show_every, date_format) axes_formatter = AxesFormatterDecorator(x_major=FuncFormatter(func), key="formatter") bar_chart.add_decorator(axes_formatter) # Set the tick locator. if years > 10: x_major = YearLocator() else: x_major = MonthLocator(range(1, 13), bymonthday=30, interval=3) axes_locator = AxesLocatorDecorator(x_major=x_major, key="locator") bar_chart.add_decorator(axes_locator) return bar_chart