Пример #1
0
    def get_factor_return_attribution(cls, fund_tms: QFSeries,
                                      fit_tms: QFSeries,
                                      regressors_df: QFDataFrame,
                                      coefficients: QFSeries,
                                      alpha: float) -> Tuple[QFSeries, float]:
        """
        Returns performance attribution for each factor in given regressors and also calculates the unexplained return.
        """
        fund_returns = fund_tms.to_simple_returns()
        regressors_returns = regressors_df.to_simple_returns()
        annualised_fund_return = cagr(fund_returns)
        annualised_fit_return = cagr(fit_tms)

        total_nav = fit_tms.to_prices(initial_price=1.0)

        def calc_factors_profit(series) -> float:
            factor_ret = regressors_returns.loc[:, series.name].values
            return coefficients.loc[series.name] * (total_nav[:-1].values *
                                                    factor_ret).sum()

        factors_profits = regressors_returns.apply(calc_factors_profit)

        alpha_profit = total_nav[:-1].sum() * alpha
        total_profit = factors_profits.sum() + alpha_profit

        regressors_return_attribution = factors_profits * annualised_fit_return / total_profit
        regressors_return_attribution = cast_series(
            regressors_return_attribution, QFSeries)

        unexplained_return = annualised_fund_return - regressors_return_attribution.sum(
        )

        return regressors_return_attribution, unexplained_return
Пример #2
0
def cagr(qf_series: QFSeries, frequency=None):
    """
    Returns the Compound Annual Growth Rate (CAGR) calculated for the given series.

    Parameters
    ----------
    qf_series: QFSeries
        series of returns of an asset
    frequency: Frequency
        Frequency of the timeseries of returns;
        if it is None (by default) it is inferred from the timeseries of returns

    Returns
    -------
    float
        annual compound return for the given series of prices

    """
    prices_tms = qf_series.to_prices(frequency=frequency, initial_price=1.0)

    last_date = prices_tms.index[-1]
    first_date = prices_tms.index[0]
    period_length = last_date - first_date
    period_length_in_years = to_days(period_length) / DAYS_PER_YEAR_AVG

    total_return = prices_tms[-1] / prices_tms[0] - 1
    return annualise_total_return(
        total_return=total_return,
        period_length_in_years=period_length_in_years,
        returns_type=SimpleReturnsSeries)
Пример #3
0
    def calculate_analysis(cls, strategy_tms: QFSeries,
                           benchmark_tms: QFSeries):
        """
        Calculates the rolling table for provided timeseries
        """
        rows = list()
        windows = [(6 * 21, "6 Months"), (252, "1 Year"), (252 * 2, "2 Years"),
                   (252 * 5, "5 Years")]

        # Ensure that this data is daily.
        df = PricesDataFrame()
        strategy_name = strategy_tms.name
        benchmark_name = benchmark_tms.name
        df[strategy_name] = strategy_tms.to_prices()
        df[benchmark_name] = benchmark_tms.to_prices()
        df.fillna(method='ffill', inplace=True)

        for window_info in windows:
            window = window_info[0]

            # if window is too big for the strategy then skip it
            if window >= int(df.shape[0] / 2):
                continue

            step = int(window * 0.2)

            strategy_rolling = df[strategy_name].rolling_window(
                window, lambda x: x.total_cumulative_return(), step)
            benchmark_rolling = df[benchmark_name].rolling_window(
                window, lambda x: x.total_cumulative_return(), step)

            outperforming = strategy_rolling > benchmark_rolling
            percentage_outperforming = len(
                strategy_rolling[outperforming]) / len(strategy_rolling)

            dto = RollingAnalysisDTO(
                period=window_info[1],
                strategy_average=strategy_rolling.mean(),
                strategy_worst=strategy_rolling.min(),
                strategy_best=strategy_rolling.max(),
                benchmark_average=benchmark_rolling.mean(),
                benchmark_worst=benchmark_rolling.min(),
                benchmark_best=benchmark_rolling.max(),
                percentage_difference=percentage_outperforming)
            rows.append(dto)
        return rows
Пример #4
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