Beispiel #1
0
    def create_tickers_analysis(self, ticker: Ticker):
        """
        For the given ticker add candlestick charts representing OHLC prices with highlighted signals generated by each
        of the alpha models. In case if the model generates a BUY signal the background for the given day is highlighted
        with green color, in case of a SELL signal - red.
        """
        prices_df = self.get_prices(ticker)
        alpha_model_signals: Dict[AlphaModel, QFSeries] = {}

        self.document.add_element(HeadingElement(level=2, text=ticker.name))

        for alpha_model in self.alpha_models:
            exposures = []
            dates = []

            prev_exposure = Exposure.OUT

            for date in date_range(self.start_date, self.end_date, freq="B"):
                try:
                    self.timer.set_current_time(date)
                    new_exposure = alpha_model.get_signal(
                        ticker, prev_exposure).suggested_exposure
                    exposures.append(new_exposure.value)
                    dates.append(date)

                    if not self.only_entry_signals:
                        prev_exposure = new_exposure
                except NoValidTickerException as e:
                    print(e)

            exposures_series = QFSeries(data=exposures, index=dates)
            alpha_model_signals[alpha_model] = exposures_series

            candlestick_chart = CandlestickChart(prices_df,
                                                 title=str(alpha_model))
            candlestick_chart.add_highlight(exposures_series)
            self.document.add_element(
                ChartElement(candlestick_chart,
                             figsize=self.full_image_size,
                             dpi=self.dpi))

        candlestick_chart = CandlestickChart(prices_df,
                                             title="All models summary")
        for model, exposures_series in alpha_model_signals.items():
            candlestick_chart.add_highlight(exposures_series)

        self.document.add_element(
            ChartElement(candlestick_chart,
                         figsize=self.full_image_size,
                         dpi=self.dpi))
    def _add_up_and_down_trend_strength(self, prices_df: QFDataFrame):
        def _down_trend_fun(df):
            return down_trend_strength(df, self.use_next_open_instead_of_close)

        def _up_trend_fun(df):
            return up_trend_strength(df, self.use_next_open_instead_of_close)

        up_trend_strength_tms = prices_df.rolling_time_window(window_length=self.window_len, step=1,
                                                              func=_down_trend_fun)
        down_trend_strength_tms = prices_df.rolling_time_window(window_length=self.window_len, step=1,
                                                                func=_up_trend_fun)
        chart = LineChart()
        up_trend_elem = DataElementDecorator(up_trend_strength_tms)
        down_trend_elem = DataElementDecorator(down_trend_strength_tms)
        chart.add_decorator(up_trend_elem)
        chart.add_decorator(down_trend_elem)
        legend = LegendDecorator(legend_placement=Location.BEST, key='legend')
        legend.add_entry(up_trend_elem, 'Up trend strength')
        legend.add_entry(down_trend_elem, 'Down trend strength')
        chart.add_decorator(legend)
        title = "Strength of the up and down trend - rolling {} days".format(self.window_len)
        title_decorator = TitleDecorator(title, key="title")
        chart.add_decorator(title_decorator)
        self._add_axes_position_decorator(chart)
        self.document.add_element(ChartElement(chart, figsize=self.image_size, dpi=self.dpi))
Beispiel #3
0
    def _add_rolling_return_chart(self, timeseries_list):
        def tot_return(window):
            return PricesSeries(window).total_cumulative_return()

        chart = self._get_rolling_chart(timeseries_list, tot_return, "Return")
        self.document.add_element(
            ChartElement(chart, figsize=self.full_image_size, dpi=self.dpi))
    def _add_gini_coefficient_chart(self):
        chart = LineChart(rotate_x_axis=False)

        position_decorator = AxesPositionDecorator(*self.full_image_axis_position)
        chart.add_decorator(position_decorator)

        # Get the assets history
        assets_history = self.backtest_result.portfolio.positions_history()
        assets_history = assets_history.applymap(
            lambda x: x.total_exposure if isinstance(x, BacktestPositionSummary) else 0)

        def gini(group):
            # Function computing the Gini coefficients for each row
            group = group.abs()
            ranks = group.rank(ascending=True)
            mean_value = group.mean()
            samples = group.size
            group = group * (2 * ranks - samples - 1)
            return group.sum() / (samples * samples * mean_value)

        assets_history = assets_history.stack(dropna=False).groupby(level=0).apply(gini).to_frame(name="Gini "
                                                                                                       "coefficient")

        assets_history_decorator = DataElementDecorator(assets_history)
        chart.add_decorator(assets_history_decorator)

        # Add title
        title_decorator = TitleDecorator("Concentration of assets - Gini coefficient")
        chart.add_decorator(title_decorator)

        self.document.add_element(ChartElement(chart, figsize=self.full_image_size, dpi=self.dpi))
    def _add_line_chart_element(self, series, title):
        chart = self._get_line_chart(series, title)

        position_decorator = AxesPositionDecorator(*self.full_image_axis_position)
        chart.add_decorator(position_decorator)

        self.document.add_element(ChartElement(chart, figsize=self.full_image_size, dpi=self.dpi))
Beispiel #6
0
    def _add_assets_number_in_portfolio_chart(self):
        chart = LineChart(rotate_x_axis=False)
        chart.add_decorator(AxesPositionDecorator(*self.full_image_axis_position))
        legend = LegendDecorator(key="legend_decorator")

        positions_history = self.backtest_result.portfolio.positions_history()

        # Find all not NaN values (not NaN values indicate that the position was open for this contract at that time)
        # and count their number for each row (for each of the dates)
        number_of_contracts = positions_history.notna().sum(axis=1)
        number_of_contracts_decorator = DataElementDecorator(number_of_contracts)
        chart.add_decorator(number_of_contracts_decorator)
        legend.add_entry(number_of_contracts_decorator, "Contracts")

        # Group tickers by name and for each name and date check if there was at least one position open with any
        # of the corresponding tickers. Finally sum all the assets that had a position open on a certain date.
        number_of_assets = positions_history.groupby(by=lambda ticker: ticker.name, axis='columns') \
            .apply(lambda x: x.notna().any(axis=1)).sum(axis=1)
        number_of_assets_decorator = DataElementDecorator(number_of_assets)
        chart.add_decorator(number_of_assets_decorator)
        legend.add_entry(number_of_assets_decorator, "Assets")

        chart.add_decorator(TitleDecorator("Number of assets in the portfolio"))
        chart.add_decorator(legend)
        chart.add_decorator(AxesLabelDecorator(y_label="Number of contracts / assets"))

        self.document.add_element(ChartElement(chart, figsize=self.full_image_size, dpi=self.dpi))
Beispiel #7
0
 def _add_leverage_chart(self):
     lev_chart = self._get_leverage_chart(self.leverage)
     position_decorator = AxesPositionDecorator(
         *self.full_image_axis_position)
     lev_chart.add_decorator(position_decorator)
     self.document.add_element(
         ChartElement(lev_chart, figsize=self.full_image_size,
                      dpi=self.dpi))
Beispiel #8
0
 def _add_chart(self, df: QFDataFrame, title: str):
     chart = self._create_chart(df, title)
     self.document.add_element(
         ChartElement(
             chart=chart,
             figsize=self.full_image_size,
             dpi=self.dpi,
         ))
Beispiel #9
0
 def _add_perf_chart(self, series_list: List[QFSeries]):
     """
     series_list
         List of compared series. The strategy should always be the first element in the list
     """
     self.document.add_element(
         ChartElement(self._get_large_perf_chart(series_list),
                      figsize=self.full_image_size,
                      dpi=self.dpi))
    def _add_concentration_of_portfolio_chart(self, top_assets_numbers: tuple = (1, 5)):
        chart = LineChart(rotate_x_axis=False)

        position_decorator = AxesPositionDecorator(*self.full_image_axis_position)
        chart.add_decorator(position_decorator)

        # Define a legend
        legend = LegendDecorator(key="legend_decorator")

        # Add y label
        label_decorator = AxesLabelDecorator(y_label="Mean total exposure of top assets")
        chart.add_decorator(label_decorator)

        # Add top asset contribution
        positions_history = self.backtest_result.portfolio.positions_history()
        if positions_history.empty:
            raise ValueError("No positions found in positions history")

        positions_history = positions_history.applymap(
            lambda x: x.total_exposure if isinstance(x, BacktestPositionSummary) else 0)

        # Map all the single contracts onto tickers (including future tickers) and take the maximal total exposure for
        # each of the groups - in case if two contracts for a single asset will be included in the open positions in
        # the portfolio at any point of time, only one (with higher total exposure) will be considered while generating
        # the top assets plot
        def contract_to_ticker(c: Contract):
            return self.backtest_result.portfolio.contract_ticker_mapper. \
                contract_to_ticker(c, strictly_to_specific_ticker=False)

        assets_history = positions_history.rename(columns=contract_to_ticker)
        assets_history = assets_history.groupby(level=0, axis=1).apply(func=(
            lambda x: x.abs().max(axis=1).astype(float)
        ))

        for assets_number in top_assets_numbers:
            # For each date (row), find the top_assets largest assets and compute the mean value of their market value
            top_assets_mean_values = assets_history.stack(dropna=False).groupby(level=0).apply(
                lambda group: group.nlargest(assets_number).mean()
            ).resample('D').last()
            # Divide the computed mean values by the portfolio value, for each of the dates
            top_assets_percentage_value = top_assets_mean_values / self.backtest_result.portfolio.portfolio_eod_series()

            concentration_top_asset = DataElementDecorator(top_assets_percentage_value)
            chart.add_decorator(concentration_top_asset)

            # Add to legend
            legend.add_entry(concentration_top_asset, "TOP {} assets".format(assets_number))

        # Add title
        title_decorator = TitleDecorator("Concentration of assets")
        chart.add_decorator(title_decorator)

        # Add legend
        chart.add_decorator(legend)

        self.document.add_element(ChartElement(chart, figsize=self.full_image_size, dpi=self.dpi))
Beispiel #11
0
    def _add_cone_chart(self):
        cone_chart = ConeChartOOS(self.strategy_series,
                                  is_mean_return=self.is_mean_return,
                                  is_sigma=self.is_sigma)

        position_decorator = AxesPositionDecorator(*self.full_image_axis_position)
        cone_chart.add_decorator(position_decorator)

        chart_element = ChartElement(cone_chart, self.full_image_size, self.dpi, False)
        self.document.add_element(chart_element)
Beispiel #12
0
    def _add_rolling_vol_chart(self, timeseries_list):
        def volatility(window):
            return get_volatility(PricesSeries(window),
                                  Frequency.DAILY,
                                  annualise=True)

        chart = self._get_rolling_chart(timeseries_list, volatility,
                                        "Volatility")
        self.document.add_element(
            ChartElement(chart, figsize=self.full_image_size, dpi=self.dpi))
    def _add_leverage_chart(self):
        lev_chart = self._get_leverage_chart(self.backtest_result.portfolio.leverage_series())

        position_decorator = AxesPositionDecorator(*self.full_image_axis_position)
        lev_chart.add_decorator(position_decorator)

        # Add y label
        label_decorator = AxesLabelDecorator(y_label="Leverage")
        lev_chart.add_decorator(label_decorator)

        self.document.add_element(ChartElement(lev_chart, figsize=self.full_image_size, dpi=self.dpi))
Beispiel #14
0
    def _add_rolling_alpha_and_beta(self, timeseries_list):
        freq = timeseries_list[0].get_frequency()
        timeseries_list = [
            tms.dropna().to_simple_returns() for tms in timeseries_list
        ]
        df = pd.concat(timeseries_list, axis=1).fillna(0)

        rolling_window_len = int(freq.value / 2)  # 6M rolling
        step = round(freq.value / 6)  # 2M shift

        legend = LegendDecorator()
        chart = LineChart(start_x=df.index[0], end_x=df.index[-1])
        line_decorator = HorizontalLineDecorator(0, key="h_line", linewidth=1)
        chart.add_decorator(line_decorator)

        def alpha_function(df_in_window):
            strategy_returns = df_in_window.iloc[:, 0]
            benchmark_returns = df_in_window.iloc[:, 1]
            beta, alpha, _, _, _ = stats.linregress(benchmark_returns,
                                                    strategy_returns)
            return beta, alpha

        rolling = df.rolling_time_window(rolling_window_len, step,
                                         alpha_function)
        rolling = pd.DataFrame([[b, a] for b, a in rolling.values],
                               columns=["beta", "alpha"],
                               index=rolling.index)

        rolling_element = DataElementDecorator(rolling["beta"])
        chart.add_decorator(rolling_element)
        legend.add_entry(rolling_element, "beta")

        rolling_element = DataElementDecorator(rolling["alpha"],
                                               use_secondary_axes=True)
        chart.add_decorator(rolling_element)
        legend.add_entry(rolling_element, "alpha")

        chart.add_decorator(legend)
        chart.add_decorator(
            AxesFormatterDecorator(use_secondary_axes=True,
                                   y_major=PercentageFormatter(".1f")))

        # modify axes position to make secondary scale visible
        axes_position = list(self.full_image_axis_position)
        axes_position[2] = axes_position[2] - 0.07
        position_decorator = AxesPositionDecorator(*axes_position)
        chart.add_decorator(position_decorator)

        title_str = "Rolling alpha and beta [{} {} samples]".format(
            rolling_window_len, freq)
        title_decorator = TitleDecorator(title_str, key="title")
        chart.add_decorator(title_decorator)
        self.document.add_element(
            ChartElement(chart, figsize=self.full_image_size, dpi=self.dpi))
Beispiel #15
0
    def _add_assets_number_in_portfolio_chart(self):
        chart = LineChart(rotate_x_axis=False)
        legend = LegendDecorator(key="legend_decorator")

        position_decorator = AxesPositionDecorator(
            *self.full_image_axis_position)
        chart.add_decorator(position_decorator)

        positions_history = self.backtest_result.portfolio.positions_history()

        # Find all not NaN values (not NaN values indicate that the position was open for this contract at that time)
        # and count their number for each row (for each of the dates)
        number_of_contracts = positions_history.notnull().sum(axis=1)
        number_of_contracts_decorator = DataElementDecorator(
            number_of_contracts)

        chart.add_decorator(number_of_contracts_decorator)
        legend.add_entry(number_of_contracts_decorator, "Contracts")

        # Count number of assets in the portfolio (if on two contracts from same future family exist in the same time,
        # e.g. during rolling day, in the portfolio, they are counted as one asset)
        def contract_to_ticker(c: Contract):
            return self.backtest_result.portfolio.contract_ticker_mapper. \
                contract_to_ticker(c, strictly_to_specific_ticker=False)

        assets_history = positions_history.rename(columns=contract_to_ticker)
        assets_history = assets_history.groupby(level=0, axis=1).apply(func=(
            # For each asset, group all of the corresponding columns (each of which corresponds to one contract),
            # and check if at any given timestamp the "value" of any of the contracts was different than None - this
            # indicates that at this point of time a position concerning the given asset was open in the portfolio
            # (which in the resulting series will be denoted as 1, otherwise - 0, so that it will be possible to
            # sum positions of all open assets at any given point of time)
            lambda x: x.notna().any(axis=1).astype(int)))

        number_of_assets = assets_history.sum(axis=1)
        number_of_assets_decorator = DataElementDecorator(number_of_assets)
        chart.add_decorator(number_of_assets_decorator)
        legend.add_entry(number_of_assets_decorator, "Assets")

        # Add title
        title_decorator = TitleDecorator("Number of assets in the portfolio")
        chart.add_decorator(title_decorator)

        # Add legend
        chart.add_decorator(legend)

        # Add y label
        label_decorator = AxesLabelDecorator(
            y_label="Number of contracts / assets")
        chart.add_decorator(label_decorator)

        self.document.add_element(
            ChartElement(chart, figsize=self.full_image_size, dpi=self.dpi))
Beispiel #16
0
    def _add_simulation_results(self):
        """
        Generate a data frame consisting of a certain number of "scenarios" (each scenario denotes one single equity
        curve).
        """
        self.document.add_element(NewPageElement())
        self.document.add_element(HeadingElement(level=1, text="Monte Carlo simulations\n"))
        self.document.add_element(HeadingElement(level=2, text="Average number of trades per year: {}\n".format(
            int(self._average_number_of_trades_per_year()))))
        if self.initial_risk is not None:
            self.document.add_element(HeadingElement(level=2, text="Initial risk: {:.2%}".format(self.initial_risk)))

        scenarios_df, total_returns = self._get_scenarios()

        # Plot all the possible paths on a chart
        all_paths_chart = self._get_simulation_plot(scenarios_df)
        self.document.add_element(ChartElement(all_paths_chart, figsize=self.full_image_size, dpi=self.dpi))

        # Plot the distribution plot
        distribution_plot = self._get_distribution_plot(
            total_returns, title="Monte Carlo Simulations Distribution (one year % return)", bins=200, crop=True)
        # Format the x-axis so that its labels are shown as a percentage in case of percentage returns

        axes_formatter_decorator = AxesFormatterDecorator(x_major=PercentageFormatter(), key="axes_formatter")
        distribution_plot.add_decorator(axes_formatter_decorator)

        self.document.add_element(ChartElement(distribution_plot, figsize=self.full_image_size, dpi=self.dpi))

        simulations_summary_table = self._get_monte_carlos_simulator_outputs(scenarios_df, total_returns)
        self.document.add_element(simulations_summary_table)

        # Extract the results of each of the scenarios and summarize the data in the tables
        dist_summary_tables = self._get_distribution_summary_table(total_returns)
        self.document.add_element(dist_summary_tables)

        # Add the "Chances of dropping below" and "Simulations summary" tables
        ruin_chances_table = self._get_chances_of_dropping_below_table(scenarios_df)
        self.document.add_element(ruin_chances_table)
Beispiel #17
0
    def _add_returns_distribution(self):
        if self.initial_risk is not None:
            returns = SimpleReturnsSeries(data=[t.percentage_pnl / self.initial_risk for t in self.trades])
            title = "Distribution of R multiples, Initial risk = {:.2%}".format(self.initial_risk)
            returns_histogram = self._get_distribution_plot(returns, title)
        else:
            returns = SimpleReturnsSeries(data=[t.percentage_pnl for t in self.trades])
            title = "Distribution of returns [%]"
            returns_histogram = self._get_distribution_plot(returns, title)

            # Format the x-axis so that its labels are shown as a percentage in case of percentage returns
            axes_formatter_decorator = AxesFormatterDecorator(x_major=PercentageFormatter(), key="axes_formatter")
            returns_histogram.add_decorator(axes_formatter_decorator)

        self.document.add_element(ChartElement(returns_histogram, figsize=self.full_image_size, dpi=self.dpi))
Beispiel #18
0
 def _add_price_chart(self, prices_df: QFDataFrame):
     close_tms = prices_df[PriceField.Close]
     price_tms = close_tms.to_prices(1)
     chart = LineChart(start_x=price_tms.index[0], end_x=price_tms.index[-1])
     price_elem = DataElementDecorator(price_tms)
     chart.add_decorator(price_elem)
     line_decorator = HorizontalLineDecorator(1, key="h_line", linewidth=1)
     chart.add_decorator(line_decorator)
     legend = LegendDecorator()
     legend.add_entry(price_elem, "Close Price")
     chart.add_decorator(legend)
     title_decorator = TitleDecorator("Price of the instrument", key="title")
     chart.add_decorator(title_decorator)
     self._add_axes_position_decorator(chart)
     self.document.add_element(ChartElement(chart, figsize=self.image_size, dpi=self.dpi))
    def _plot_ticker_performance(self, ticker_name: str, performance):
        self.document.add_element(HeadingElement(level=2, text="PnL of {}".format(ticker_name)))

        chart = LineChart()
        legend = LegendDecorator(key="legend_decorator")
        line_colors = iter(("#add8e6", "#000000", "#fa8072"))

        for title, pnl_series in performance.iteritems():
            # Plot series only in case if it consist anything else then 0
            if (pnl_series != 0).any():
                data_series = DataElementDecorator(pnl_series, **{"color": next(line_colors)})
                legend.add_entry(data_series, title)
                chart.add_decorator(data_series)

        chart.add_decorator(legend)
        self.document.add_element(ChartElement(chart, figsize=self.full_image_size, dpi=self.dpi))
Beispiel #20
0
    def _add_trend_strength_chart(self, prices_df: QFDataFrame):

        def _fun(df):
            return trend_strength(df, self.use_next_open_instead_of_close)

        trend_strength_tms = prices_df.rolling_time_window(window_length=self.window_len, step=1, func=_fun)

        chart = LineChart()
        trend_elem = DataElementDecorator(trend_strength_tms, color='black')
        chart.add_decorator(trend_elem)
        legend = LegendDecorator(legend_placement=Location.BEST, key='legend')
        legend.add_entry(trend_elem, 'Trend strength')
        chart.add_decorator(legend)
        title_decorator = TitleDecorator("Strength of the trend - rolling {} days".format(self.window_len), key="title")
        chart.add_decorator(title_decorator)
        self._add_axes_position_decorator(chart)
        self.document.add_element(ChartElement(chart, figsize=self.image_size, dpi=self.dpi))
Beispiel #21
0
    def _add_perf_chart_for_factor(self,
                                   series_list: List[QFSeries],
                                   title: str = "Factor Index Performance"):
        """ Add performance chart for factor

        Parameters
        ----------
        series_list: List[QFSeries]
            list of compared series
        title: str
            chart title
        """
        self.document.add_element(
            ChartElement(self._get_perf_chart(series_list,
                                              is_large_chart=True,
                                              title=title),
                         figsize=self.full_image_size,
                         dpi=self.dpi))
Beispiel #22
0
    def add_chart(self, chart: Chart, grid_proportion=GridProportion.Eight) -> ChartElement:
        """
        Add a chart to this grid. Plot settings such as ``figsize``, ``dpi`` and ``optimise`` will be based on
        the ones specified to this GridElement's constructor.

        Parameters
        ----------
        chart
        grid_proportion
            The proportion of the grid that this chart should use up (out of 16). As an example, to get 4 charts side
            by side set this to ``4``. This is currently only used in the ``Web`` plotting mode.


        Returns
        -------
        The ChartElement instance that was constructed for this chart.
        """
        result = ChartElement(chart, self.figsize, self.dpi, self.optimise, grid_proportion)
        self._elements.append(result)
        return result
Beispiel #23
0
    def _add_rolling_chart(self):
        days_rolling = int(252 / 2)  # 6M rolling
        step = round(days_rolling / 5)

        strategy = self.strategy_series.to_prices(1)
        chart = LineChart(start_x=strategy.index[0], end_x=strategy.index[-1])
        line_decorator = HorizontalLineDecorator(0, key="h_line", linewidth=1)
        chart.add_decorator(line_decorator)

        legend = LegendDecorator()

        def tot_return(window):
            return PricesSeries(window).total_cumulative_return()

        def volatility(window):
            return get_volatility(PricesSeries(window), Frequency.DAILY)

        functions = [tot_return, volatility]
        names = ['Rolling Return', 'Rolling Volatility']
        for func, name in zip(functions, names):
            rolling = strategy.rolling_window(days_rolling, func, step=step)
            rolling_element = DataElementDecorator(rolling)
            chart.add_decorator(rolling_element)
            legend.add_entry(rolling_element, name)

        chart.add_decorator(legend)

        chart.add_decorator(
            AxesFormatterDecorator(y_major=PercentageFormatter(".0f")))

        left, bottom, width, height = self.full_image_axis_position
        position_decorator = AxesPositionDecorator(left, bottom, width, height)
        chart.add_decorator(position_decorator)

        title_decorator = TitleDecorator(
            "Rolling Statistics [6 Months]".format(days_rolling), key="title")
        chart.add_decorator(title_decorator)

        self.document.add_element(
            ChartElement(chart, figsize=self.full_image_size, dpi=self.dpi))
Beispiel #24
0
    def _add_concentration_of_portfolio_chart(self, top_assets_numbers: tuple = (1, 5)):
        chart = LineChart(rotate_x_axis=False)
        chart.add_decorator(AxesPositionDecorator(*self.full_image_axis_position))
        chart.add_decorator(AxesLabelDecorator(y_label="Mean total exposure of top assets"))
        chart.add_decorator(TitleDecorator("Concentration of assets"))

        legend = LegendDecorator(key="legend_decorator")
        chart.add_decorator(legend)

        # Add top asset contribution
        positions_history = self.backtest_result.portfolio.positions_history()
        if positions_history.empty:
            raise ValueError("No positions found in positions history")

        positions_history = positions_history.applymap(
            lambda x: x.total_exposure if isinstance(x, BacktestPositionSummary) else 0)

        # Group all the tickers by their names and take the maximal total exposure for each of the groups - in case
        # if two contracts for a single asset will be included in the open positions in the portfolio at any point of
        # time, only one (with higher total exposure) will be considered while generating the top assets plot
        assets_history = positions_history.groupby(by=lambda ticker: ticker.name, axis='columns').apply(
            lambda x: x.abs().max(axis=1))

        for assets_number in top_assets_numbers:
            # For each date (row), find the top_assets largest assets and compute the mean value of their market value
            top_assets_mean_values = assets_history.stack(dropna=False).groupby(level=0).apply(
                lambda group: group.nlargest(assets_number).mean()
            ).resample('D').last()
            # Divide the computed mean values by the portfolio value, for each of the dates
            top_assets_percentage_value = top_assets_mean_values / self.backtest_result.portfolio.portfolio_eod_series()

            concentration_top_asset = DataElementDecorator(top_assets_percentage_value)
            chart.add_decorator(concentration_top_asset)
            legend.add_entry(concentration_top_asset, "TOP {} assets".format(assets_number))

        self.document.add_element(ChartElement(chart, figsize=self.full_image_size, dpi=self.dpi))
Beispiel #25
0
    def _add_relative_performance_chart(
            self,
            strategy_tms: QFSeries,
            benchmark_tms: QFSeries,
            chart_title: str = "Relative Performance",
            legend_subtitle: str = "Strategy - Benchmark"):
        diff = strategy_tms.to_simple_returns().subtract(
            benchmark_tms.to_simple_returns(), fill_value=0)
        diff = diff.to_prices(1) - 1

        chart = LineChart(start_x=diff.index[0],
                          end_x=diff.index[-1],
                          log_scale=False)
        position_decorator = AxesPositionDecorator(
            *self.full_image_axis_position)
        chart.add_decorator(position_decorator)

        line_decorator = HorizontalLineDecorator(0, key="h_line", linewidth=1)
        chart.add_decorator(line_decorator)
        legend = LegendDecorator()

        series_elem = DataElementDecorator(diff)
        chart.add_decorator(series_elem)
        legend.add_entry(series_elem, legend_subtitle)

        chart.add_decorator(legend)
        title_decorator = TitleDecorator(chart_title, key="title")
        chart.add_decorator(title_decorator)

        chart.add_decorator(
            AxesFormatterDecorator(y_major=PercentageFormatter(".0f")))

        fill_decorator = FillBetweenDecorator(diff)
        chart.add_decorator(fill_decorator)
        self.document.add_element(
            ChartElement(chart, figsize=self.full_image_size, dpi=self.dpi))
Beispiel #26
0
 def _add_rolling_ret_and_vol_chart(self, timeseries):
     chart = self._get_rolling_ret_and_vol_chart(timeseries)
     self.document.add_element(
         ChartElement(chart, figsize=self.full_image_size, dpi=self.dpi))
Beispiel #27
0
    def _add_line_plots(self, tickers: Sequence[Ticker]):
        parameters_list = sorted(
            self.backtest_evaluator.params_backtest_summary_elem_dict.keys())

        title_to_plot = defaultdict(lambda: LineChart())
        title_to_legend = defaultdict(
            lambda: LegendDecorator(key="legend_decorator"))

        for start_time, end_time in [
            (self.backtest_summary.start_date, self.out_of_sample_start_date),
            (self.out_of_sample_start_date, self.backtest_summary.end_date)
        ]:
            results = []

            for param_tuple in parameters_list:
                trades_eval_result = self.backtest_evaluator.evaluate_params_for_tickers(
                    param_tuple, tickers, start_time, end_time)
                results.append(trades_eval_result)

            sqn_avg_nr_trades = DataElementDecorator(
                [x.sqn_per_avg_nr_trades for x in results])
            avg_nr_of_trades = DataElementDecorator(
                [x.avg_nr_of_trades_1Y for x in results])
            annualised_return = DataElementDecorator(
                [x.annualised_return for x in results])

            adjusted_start_time = min([x.start_date for x in results])
            adjusted_end_time = max([x.end_date for x in results])
            if adjusted_start_time >= adjusted_end_time:
                adjusted_end_time = adjusted_start_time if adjusted_start_time <= self.backtest_summary.end_date \
                    else end_time
                adjusted_start_time = start_time
            title = "{} - {} ".format(adjusted_start_time.strftime("%Y-%m-%d"),
                                      adjusted_end_time.strftime("%Y-%m-%d"))

            title_to_plot["SQN (Arithmetic return) per year"].add_decorator(
                sqn_avg_nr_trades)
            title_to_legend["SQN (Arithmetic return) per year"].add_entry(
                sqn_avg_nr_trades, title)

            title_to_plot["Avg # trades 1Y"].add_decorator(avg_nr_of_trades)
            title_to_legend["Avg # trades 1Y"].add_entry(
                sqn_avg_nr_trades, title)

            if len(tickers) == 1:
                title_to_plot["Annualised return"].add_decorator(
                    annualised_return)
                title_to_legend["Annualised return"].add_entry(
                    annualised_return, title)

        tickers_used = "Many tickers" if len(tickers) > 1 else (
            tickers[0].name)

        for description, line_chart in title_to_plot.items():
            self.document.add_element(
                HeadingElement(3, "{} - {}".format(description, tickers_used)))
            line_chart.add_decorator(
                AxesLabelDecorator(x_label=self._get_param_names()[0],
                                   y_label=title))
            position_decorator = AxesPositionDecorator(
                *self.image_axis_position)
            line_chart.add_decorator(position_decorator)
            legend = title_to_legend[description]
            line_chart.add_decorator(legend)
            self.document.add_element(
                ChartElement(line_chart, figsize=self.full_image_size))