コード例 #1
0
ファイル: data_presenter.py プロジェクト: quarkfin/qf-lib
    def historical_performance_chart(self) -> LineChart:
        frequency = self.model.input_data.frequency
        analysed_tms = self.model.input_data.analysed_tms
        fitted_tms = self.model.fitted_tms

        cumulative_fund_rets = analysed_tms.to_prices(initial_price=1.0,
                                                      frequency=frequency) - 1
        cumulative_fit_rets = fitted_tms.to_prices(initial_price=1.0,
                                                   frequency=frequency) - 1

        hist_performance_chart = LineChart()
        fund_cummulative_rets_data_elem = DataElementDecorator(
            cumulative_fund_rets)
        fit_cummulative_rets_data_elem = DataElementDecorator(
            cumulative_fit_rets)

        legend_decorator = LegendDecorator(
            legend_placement=Location.LOWER_RIGHT)
        legend_decorator.add_entry(fund_cummulative_rets_data_elem,
                                   self._get_security_name(analysed_tms.name))
        legend_decorator.add_entry(fit_cummulative_rets_data_elem, 'Fit')

        hist_performance_chart.add_decorator(fund_cummulative_rets_data_elem)
        hist_performance_chart.add_decorator(fit_cummulative_rets_data_elem)

        hist_performance_chart.add_decorator(
            TitleDecorator("Historical Performance"))
        hist_performance_chart.add_decorator(
            AxesLabelDecorator(y_label="Cumulative return"))
        hist_performance_chart.add_decorator(legend_decorator)
        hist_performance_chart.add_decorator(
            AxesFormatterDecorator(y_major=PercentageFormatter()))

        return hist_performance_chart
コード例 #2
0
    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
コード例 #3
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))
コード例 #4
0
def create_gross_leverage_chart(gross_lev: QFSeries) -> LineChart:
    """
    Creates a line chart showing gross leverage based on the specified gross leverage values.

    Parameters
    ----------
    gross_lev: QFSeries
        Gross leverage as returned by the extract_rets_pos_txn_from_zipline function.

    Returns
    -------
    LineChart
        Created line chart
    """
    result = LineChart()

    result.add_decorator(
        DataElementDecorator(gross_lev, linewidth=0.5, color="g"))

    result.add_decorator(
        HorizontalLineDecorator(gross_lev.mean(),
                                color="g",
                                linestyle="--",
                                linewidth=3))

    result.add_decorator(TitleDecorator("Gross leverage"))
    result.add_decorator(AxesLabelDecorator(y_label="Gross leverage"))
    return result
コード例 #5
0
ファイル: data_presenter.py プロジェクト: quarkfin/qf-lib
    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
コード例 #6
0
ファイル: data_presenter.py プロジェクト: quarkfin/qf-lib
    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
コード例 #7
0
    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
コード例 #8
0
ファイル: create_qq_chart.py プロジェクト: mborraty/qf-lib
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
コード例 #9
0
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
コード例 #10
0
    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))
コード例 #11
0
 def _crete_single_line_chart(self, measure_name, parameters, values, tickers):
     result_series = QFSeries(data=values, index=parameters)
     line_chart = LineChart()
     data_element = DataElementDecorator(result_series)
     line_chart.add_decorator(data_element)
     line_chart.add_decorator(TitleDecorator(self._get_chart_title(tickers, measure_name)))
     param_names = self._get_param_names()
     line_chart.add_decorator(AxesLabelDecorator(x_label=param_names[0], y_label=measure_name))
     self._resize_chart(line_chart)
     return line_chart
コード例 #12
0
    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))
コード例 #13
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))
コード例 #14
0
def create_return_quantiles(returns: QFSeries, live_start_date: datetime = None, x_axis_labels_rotation: int = 20) \
        -> BoxplotChart:
    """
    Creates a new return quantiles boxplot chart based on the returns specified.

    A swarm plot is also rendered on the chart if the ``live_start_date`` is specified.

    Parameters
    ----------
    returns
        The returns series to plot on the chart.
    live_start_date
        The live start date that will determine whether a swarm plot should be rendered.
    x_axis_labels_rotation

    Returns
    -------
    A new ``BoxplotChart`` instance.
    """

    simple_returns = returns.to_simple_returns()

    # case when we can plot IS together with OOS
    if live_start_date is not None:
        oos_returns = simple_returns.loc[simple_returns.index >= live_start_date]
        if len(oos_returns) > 0:
            in_sample_returns = simple_returns.loc[simple_returns.index < live_start_date]
            in_sample_weekly = get_aggregate_returns(in_sample_returns, Frequency.WEEKLY, multi_index=True)
            in_sample_monthly = get_aggregate_returns(in_sample_returns, Frequency.MONTHLY, multi_index=True)

            oos_weekly = get_aggregate_returns(oos_returns, Frequency.WEEKLY, multi_index=True)
            oos_monthly = get_aggregate_returns(oos_returns, Frequency.MONTHLY, multi_index=True)

            chart = BoxplotChart([in_sample_returns, oos_returns, in_sample_weekly,
                                  oos_weekly, in_sample_monthly, oos_monthly], linewidth=1)

            x_labels = ["daily IS", "daily OOS", "weekly IS", "weekly OOS", "monthly IS", "monthly OOS"]
            tick_decorator = AxisTickLabelsDecorator(labels=x_labels, axis=Axis.X, rotation=x_axis_labels_rotation)
        else:
            chart, tick_decorator = _get_simple_quantile_chart(simple_returns)

    else:  # case where there is only one set of data
        chart, tick_decorator = _get_simple_quantile_chart(simple_returns)

    # fixed_format_decorator = AxesFormatterDecorator(x_major=fixed_formatter)
    chart.add_decorator(tick_decorator)

    # Set title.
    title = TitleDecorator("Return Quantiles")
    chart.add_decorator(title)
    chart.add_decorator(AxesLabelDecorator(y_label="Returns"))
    return chart
コード例 #15
0
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
コード例 #16
0
 def _create_single_heat_map(self, measure_name, result_df, tickers, min_v, max_v, third_param):
     chart = HeatMapChart(data=result_df, color_map=plt.get_cmap("coolwarm"), min_value=min_v, max_value=max_v)
     chart.add_decorator(AxisTickLabelsDecorator(labels=list(result_df.columns), axis=Axis.X))
     chart.add_decorator(AxisTickLabelsDecorator(labels=list(reversed(result_df.index)), axis=Axis.Y))
     chart.add_decorator(ValuesAnnotations())
     param_names = self._get_param_names()
     chart.add_decorator(AxesLabelDecorator(x_label=param_names[1], y_label=param_names[0]))
     if third_param is None:
         title = self._get_chart_title(tickers, measure_name)
     else:
         title = "{}, {} = {:0.2f}".format(self._get_chart_title(tickers, measure_name), param_names[2], third_param)
     chart.add_decorator(TitleDecorator(title))
     self._resize_chart(chart)
     return chart
コード例 #17
0
def create_holdings_chart(positions: QFDataFrame) -> LineChart:
    """
    Creates a line chart showing holdings per day based on the specified positions.

    Parameters
    ----------
    positions: QFDataFrame
        Positions as returned by the extract_rets_pos_txn_from_zipline function.

    Returns
    -------
    LineChart
        Created line chart
    """
    # Based on:
    # https://github.com/quantopian/pyfolio/blob/5d63df4ca6e0ead83f4bebf9860732d37f532026/pyfolio/plotting.py#L323
    result = LineChart()

    # Perform some calculations.
    positions = positions.copy().drop("cash", axis="columns")
    holdings = positions.apply(lambda x: np.sum(x != 0), axis="columns")
    holdings_by_month = holdings.resample("1M").mean()

    holdings_decorator = DataElementDecorator(holdings,
                                              color="steelblue",
                                              linewidth=1.5)
    result.add_decorator(holdings_decorator)
    holdings_by_month_decorator = DataElementDecorator(holdings_by_month,
                                                       color="orangered",
                                                       alpha=0.5,
                                                       linewidth=2)
    result.add_decorator(holdings_by_month_decorator)

    hline_decorator = HorizontalLineDecorator(holdings.values.mean(),
                                              linestyle="--")
    result.add_decorator(hline_decorator)

    legend = LegendDecorator()
    legend.add_entry(holdings_decorator, "Daily Holdings")
    legend.add_entry(holdings_by_month_decorator,
                     "Average Daily Holdings, by month")
    legend.add_entry(hline_decorator, "Average Daily Holdings, net")
    result.add_decorator(legend)

    result.add_decorator(TitleDecorator("Holdings per Day"))
    result.add_decorator(
        AxesLabelDecorator(y_label="Amount of holdings per Day"))

    return result
コード例 #18
0
ファイル: data_presenter.py プロジェクト: quarkfin/qf-lib
    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
コード例 #19
0
 def _create_single_heat_map(self, title, result_df, min_v, max_v):
     chart = HeatMapChart(data=result_df,
                          color_map=plt.get_cmap("coolwarm"),
                          min_value=min_v,
                          max_value=max_v)
     chart.add_decorator(
         AxisTickLabelsDecorator(labels=list(result_df.columns),
                                 axis=Axis.X))
     chart.add_decorator(
         AxisTickLabelsDecorator(labels=list(reversed(result_df.index)),
                                 axis=Axis.Y))
     chart.add_decorator(ValuesAnnotations())
     param_names = self._get_param_names()
     chart.add_decorator(
         AxesLabelDecorator(x_label=param_names[1], y_label=param_names[0]))
     chart.add_decorator(TitleDecorator(title))
     position_decorator = AxesPositionDecorator(*self.image_axis_position)
     chart.add_decorator(position_decorator)
     return chart
コード例 #20
0
ファイル: data_presenter.py プロジェクト: quarkfin/qf-lib
    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
コード例 #21
0
def main():
    tms = data_provider.get_price(QuandlTicker('AAPL', 'WIKI'), PriceField.Close, start_date, end_date)
    tms2 = data_provider.get_price(QuandlTicker('MSFT', 'WIKI'), PriceField.Close, start_date, end_date)

    line_chart = LineChart()
    data_element = DataElementDecorator(tms)
    line_chart.add_decorator(data_element)

    data_element2 = DataElementDecorator(tms2, use_secondary_axes=True)
    line_chart.add_decorator(data_element2)

    axes_decorator = AxesLabelDecorator(x_label='dates', y_label='primary', secondary_y_label='secondary')
    line_chart.add_decorator(axes_decorator)

    legend = LegendDecorator(legend_placement=Location.BEST)
    legend.add_entry(data_element, 'AAPL')
    legend.add_entry(data_element2, 'MSFT')
    line_chart.add_decorator(legend)

    line_chart.plot()
    plt.show(block=True)
コード例 #22
0
ファイル: data_presenter.py プロジェクト: quarkfin/qf-lib
    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
コード例 #23
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))
コード例 #24
0
def create_dot_plot(series1: QFSeries,
                    series2: QFSeries,
                    x_label: str,
                    y_label: str,
                    start_x: float = None,
                    end_x: float = None) -> LineChart:
    """Create a dot linechart.

    Parameters
    -----------
    series1: QFSeries
    series2: QFSeries
    x_label: str
    y_label: str
    start_x: float
    end_x: float

    Returns
    --------
    LineChart
    """

    # Combine the series.
    combined = pd.concat([series1, series2], axis=1)
    combined_series = QFSeries(data=combined.iloc[:, 0].values,
                               index=combined.iloc[:, 1].values)

    # Create a new line chart.
    line_chart = LineChart(start_x=start_x, end_x=end_x)
    line_chart.tick_fontweight = "bold"
    line_chart.tick_color = "black"

    # Add the data.
    data_element = DataElementDecorator(combined_series, marker="o")
    line_chart.add_decorator(data_element)

    # Add a title.
    title_decorator = TitleDecorator("US Beveridge Curve", key="title")
    line_chart.add_decorator(title_decorator)

    # Add axes labels.
    axes_label_decorator = AxesLabelDecorator(x_label=x_label, y_label=y_label)
    line_chart.add_decorator(axes_label_decorator)

    # Emphasise the last point.
    # This series has many NaNs, we want to retrieve the last non-NaN point.
    no_nans = combined_series.dropna()
    point_to_emphasise = (no_nans.index[len(no_nans) - 1],
                          no_nans.values[len(no_nans) - 1])
    point_emphasis_decorator = PointEmphasisDecorator(data_element,
                                                      point_to_emphasise,
                                                      color="#CC1414",
                                                      decimal_points=1,
                                                      label_format="")
    line_chart.add_decorator(point_emphasis_decorator)

    # Create a legend.
    legend_decorator = LegendDecorator()
    last_date = series1.dropna().index.max()
    legend_decorator.add_entry(
        point_emphasis_decorator,
        "Latest ({:g}, {:g}) [{}]".format(point_to_emphasise[0],
                                          point_to_emphasise[1],
                                          last_date.strftime("%b %y")))
    line_chart.add_decorator(legend_decorator)

    return line_chart
コード例 #25
0
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
コード例 #26
0
ファイル: data_presenter.py プロジェクト: quarkfin/qf-lib
    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
コード例 #27
0
 def _get_underwater_chart(self, series: QFSeries):
     underwater_chart = UnderwaterChart(series, rotate_x_axis=True)
     underwater_chart.add_decorator(TopDrawdownDecorator(series, 5))
     underwater_chart.add_decorator(AxesLabelDecorator(y_label="Drawdown"))
     underwater_chart.add_decorator(TitleDecorator("Drawdown"))
     return underwater_chart
コード例 #28
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))
コード例 #29
0
def create_dd_probability_chart(
        prices_tms: QFSeries,
        bear_market_definition: float = 0.2) -> Tuple[Chart, Chart]:
    """Creates drawdowns probability chart.

    Parameters
    ----------
    prices_tms: QFSeries
        timeseries of prices
    bear_market_definition: float
        definition of bear market threshold

    Returns
    -------
    Tuple[Chart, Chart]
        Returns two charts - one showing the probability of drawdowns going beyond a certain level and one showing the
        marginal increase of probability of drawdowns going beyond the given level.
    """
    def count_dd_above_threshold(drawdown_series: Sequence, threshold: float):
        return sum(1 for dd in drawdown_series if dd > threshold)

    drawdowns, duration_of_drawdowns = list_of_max_drawdowns(prices_tms)

    examined_dds = np.arange(0.01, bear_market_definition, 0.005)
    percentage_ending_in_bear_market = []
    nr_of_bear_markets = count_dd_above_threshold(drawdowns,
                                                  bear_market_definition)

    for examined_dd in examined_dds:
        number_of_dds_above = count_dd_above_threshold(drawdowns, examined_dd)
        percentage = nr_of_bear_markets / number_of_dds_above * 100
        percentage_ending_in_bear_market.append(percentage)

    chart = LineChart()
    chart.add_decorator(
        ScatterDecorator(examined_dds * 100,
                         percentage_ending_in_bear_market,
                         edgecolors='black'))
    chart.add_decorator(
        TitleDecorator("Percentage of drawdowns going beyond {:2.0f}%".format(
            bear_market_definition * 100)))
    axis_dec = AxesLabelDecorator(
        "examined drawdown [%]",
        "chance that drawdown will go beyond {:2.0f}% in [%]".format(
            bear_market_definition * 100))
    chart.add_decorator(axis_dec)
    x_axis_values = examined_dds * 100
    prob_of_dd_chart = LineChart()
    prob_of_dd_chart.add_decorator(
        ScatterDecorator(x_axis_values,
                         percentage_ending_in_bear_market,
                         edgecolors='black'))
    prob_of_dd_chart.add_decorator(
        TitleDecorator("Percentage of drawdowns going beyond {:2.0f}%".format(
            bear_market_definition * 100)))
    axis_dec = AxesLabelDecorator(
        "examined drawdown [%]",
        "chance that drawdown will go beyond {:2.0f}% in [%]".format(
            bear_market_definition * 100))
    prob_of_dd_chart.add_decorator(axis_dec)

    marginal_increase_in_prob_chart = LineChart()
    diff = np.diff([0] + percentage_ending_in_bear_market)
    marginal_increase_in_prob_chart.add_decorator(
        ScatterDecorator(x_axis_values, diff, edgecolors='black'))
    marginal_increase_in_prob_chart.add_decorator(
        TitleDecorator(
            "Marginal increase of probability of drawdowns going beyond {:2.0f}%"
            .format(bear_market_definition * 100)))
    axis_dec = AxesLabelDecorator(
        "examined drawdown [%]",
        "Marginal increase of chance that drawdown will go beyond {:2.0f}% in [%]"
        .format(bear_market_definition * 100))
    marginal_increase_in_prob_chart.add_decorator(axis_dec)

    return prob_of_dd_chart, marginal_increase_in_prob_chart
コード例 #30
0
def create_skewness_chart(series: QFSeries, title: str = None) -> LineChart:
    """
    Creates a new line chart showing the skewness of the distribution.
    It plots original series together with another series which contains sorted absolute value of the returns

    Parameters
    ----------
    series
        ``QFSeries`` to plot on the chart.
    title
        title of the graph, specify ``None`` if you don't want the chart to show a title.

    Returns
    -------
    The constructed ``LineChart``.
    """

    original_price_series = series.to_prices(1)

    # Construct a series with returns sorted by their amplitude
    returns_series = series.to_simple_returns()
    abs_returns_series = returns_series.abs()

    returns_df = pd.concat([returns_series, abs_returns_series],
                           axis=1,
                           keys=['simple', 'abs'])
    sorted_returns_df = returns_df.sort_values(by='abs')
    skewness_series = SimpleReturnsSeries(
        index=returns_series.index, data=sorted_returns_df['simple'].values)
    skewed_price_series = skewness_series.to_prices(1)

    # Create a new Line Chart.
    line_chart = LineChart(start_x=series.index[0], rotate_x_axis=True)

    # Add original series to the chart
    original_series_element = DataElementDecorator(original_price_series)
    line_chart.add_decorator(original_series_element)

    skewed_series_element = DataElementDecorator(skewed_price_series)
    line_chart.add_decorator(skewed_series_element)

    # Add a point at the end
    point = (skewed_price_series.index[-1], skewed_price_series[-1])
    point_emphasis = PointEmphasisDecorator(skewed_series_element,
                                            point,
                                            font_size=9)
    line_chart.add_decorator(point_emphasis)

    # Create a title.
    if title is not None:
        title_decorator = TitleDecorator(title, "title")
        line_chart.add_decorator(title_decorator)

    # Add a legend.
    legend_decorator = LegendDecorator(key='legend')
    legend_decorator.add_entry(original_series_element,
                               'Chronological returns')
    legend_decorator.add_entry(skewed_series_element,
                               'Returns sorted by magnitude')
    line_chart.add_decorator(legend_decorator)
    line_chart.add_decorator(AxesLabelDecorator(y_label="Profit/Loss"))

    return line_chart