Beispiel #1
0
    def _create_mse_debug_chart(self, alphas, chosen_solution, mean_square_errors, min_se_solution):
        mse_chart = LineChart()
        mean_square_errors_paths = QFDataFrame(data=mean_square_errors, index=pd.Index(alphas))
        mse_chart.add_decorator(TitleDecorator("Cross-validated avg. MSE of Elastic Net fit"))
        for _, path in mean_square_errors_paths.iteritems():
            mse_chart.add_decorator(DataElementDecorator(path))
        mse_chart.add_decorator(VerticalLineDecorator(x=min_se_solution, linestyle='-.'))
        mse_chart.add_decorator(VerticalLineDecorator(x=chosen_solution, linestyle='-'))
        mse_chart.plot()
        mse_chart.axes.invert_xaxis()

        return mse_chart
Beispiel #2
0
    def _create_coeffs_debug_chart(self, alphas, chosen_solution, coeffs_path, min_se_solution):
        coeffs_chart = LineChart()
        coefficients_paths = QFDataFrame(data=coeffs_path, index=pd.Index(alphas))
        for _, path in coefficients_paths.iteritems():
            coeffs_chart.add_decorator(DataElementDecorator(path))
        coeffs_chart.add_decorator(TitleDecorator("Elastic Net (using Cross Validation)"))
        coeffs_chart.add_decorator(VerticalLineDecorator(x=min_se_solution, linestyle='-.'))
        coeffs_chart.add_decorator(VerticalLineDecorator(x=chosen_solution, linestyle='-'))
        coeffs_chart.plot()
        coeffs_chart.axes.invert_xaxis()

        return coeffs_chart
Beispiel #3
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
Beispiel #4
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
Beispiel #5
0
def create_qq_chart(strategy: QFSeries) -> Chart:
    colors = Chart.get_axes_colors()

    strategy = strategy.to_log_returns()
    # Normalize
    strategy = strategy - strategy.mean()
    strategy = strategy / strategy.std()
    # Sort
    strategy_values = sorted(strategy.values)

    # Create benchmark
    benchmark_values = list(range(1, len(strategy_values) + 1))
    n = len(strategy_values) + 1
    benchmark_values = map(lambda x: x / n, benchmark_values)
    benchmark_values = list(map(lambda x: norm.ppf(x), benchmark_values))

    # Figure out the limits.
    maximum = max(max(benchmark_values), max(strategy_values))

    result = LineChart(start_x=-maximum, end_x=maximum, upper_y=maximum, lower_y=-maximum)
    result.add_decorator(ScatterDecorator(
        benchmark_values, strategy_values, color=colors[0], alpha=0.6, edgecolors='black', linewidths=0.5))

    result.add_decorator(VerticalLineDecorator(0, color='black', linewidth=1))
    result.add_decorator(HorizontalLineDecorator(0, color='black', linewidth=1))

    result.add_decorator(TitleDecorator("Normal Distribution Q-Q"))
    result.add_decorator(AxesLabelDecorator("Normal Distribution Quantile", "Observed Quantile"))

    # Add diagonal line.
    result.add_decorator(DiagonalLineDecorator(color=colors[1]))

    return result
def create_returns_distribution(returns: QFSeries, frequency: Frequency = Frequency.MONTHLY, title: str = None) -> \
        HistogramChart:
    """
    Creates a new returns distribution histogram with the specified frequency.

    Parameters
    ----------
    returns: QFSeries
        The returns series to use in the histogram.
    frequency: Frequency
        frequency of the returns after aggregation
    title
        title of the chart
    Returns
    -------
    HistogramChart
        A new ``HistogramChart`` instance.
    """
    colors = Chart.get_axes_colors()
    aggregate_returns = get_aggregate_returns(returns,
                                              frequency,
                                              multi_index=True).multiply(100)

    chart = HistogramChart(aggregate_returns)

    # Format the x-axis so that its labels are shown as a percentage.
    x_axis_formatter = FormatStrFormatter("%.0f%%")
    axes_formatter_decorator = AxesFormatterDecorator(x_major=x_axis_formatter,
                                                      key="axes_formatter")
    chart.add_decorator(axes_formatter_decorator)
    # Only show whole numbers on the y-axis.
    y_axis_locator = MaxNLocator(integer=True)
    axes_locator_decorator = AxesLocatorDecorator(y_major=y_axis_locator,
                                                  key="axes_locator")
    chart.add_decorator(axes_locator_decorator)

    # Add an average line.
    avg_line = VerticalLineDecorator(aggregate_returns.values.mean(),
                                     color=colors[1],
                                     key="average_line_decorator",
                                     linestyle="--",
                                     alpha=0.8)
    chart.add_decorator(avg_line)

    # Add a legend.
    legend = LegendDecorator(key="legend_decorator")
    legend.add_entry(avg_line, "Mean")
    chart.add_decorator(legend)

    # Add a title.
    if title is None:
        title = "Distribution of " + str(frequency).capitalize() + " Returns"
    title = TitleDecorator(title, key="title_decorator")
    chart.add_decorator(title)
    chart.add_decorator(AxesLabelDecorator("Returns", "Occurrences"))

    return chart
def 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
Beispiel #8
0
def create_line_chart(data_list: List[Union[QFSeries, DataElementDecorator]],
                      names_list,
                      title: str = None,
                      recession_series: QFSeries = None,
                      horizontal_lines_list: List[float] = None,
                      vertical_lines_list: List[float] = None,
                      disable_dot: bool = False,
                      start_x: datetime = None,
                      end_x: datetime = None,
                      upper_y: float = None,
                      lower_y: float = None,
                      dot_decimal_points: int = 2,
                      recession_name: str = None) -> LineChart:
    """
    Creates a new line chart based on the settings specified.

    This function makes certain assumptions about the line chart, it can be customised via the parameters which should
    cover >90% of use cases. For more customisation use the ``LineChart`` class directly.

    Parameters
    ----------
    data_list
        A list of ``QFSeries`` or ``DataElementDecorator``s to plot on the chart.
    names_list
        A list of strings specifying the labels for the series, horizontal and vertical lines respectively. ``None``
        can be specified for labels to not display it for a specific series, or line.
    title
        The title of the graph, specify ``None`` if you don't want the chart to show a title.
    recession_series
        A ``QFSeries`` specifying where recessions occurred on the chart, will be highlighted using grey rectangles
        on the graph.
    horizontal_lines_list
        An optional list of values where a horizontal line should be drawn.
    vertical_lines_list
        An optional list of values where a vertical line should be drawn.
    disable_dot
        Whether a marker on the last point should be disabled.
    start_x
        The date where plotting should begin.
    end_x
        The date where plotting should end.
    upper_y
        The upper bound y-axis value at which plotting should begin.
    lower_y
        The lower bound y-axis value at which plotting should begin.
    dot_decimal_points
        How many decimal places to show after the decimal points when drawing text for "dot".
    recession_name
        A string specifying the recession label. If "None" or missing, will not be included.

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

    # If `end_x` was not specified, use a heuristic to determine it.
    if end_x is None and start_x is not None:
        end_x = LineChart.determine_end_x(start_x, data_list)

    # Create a new Line Chart.
    line_chart = LineChart(start_x=start_x,
                           end_x=end_x,
                           upper_y=upper_y,
                           lower_y=lower_y)
    line_chart.tick_fontweight = "bold"
    line_chart.tick_color = "black"

    names_index = 0  # Current legend label.
    legend_decorator = LegendDecorator(key='legend')

    # Retrieve necessary data.
    for data in data_list:
        assert isinstance(data, (pandas.Series, DataElementDecorator))
        # Add the current series with a label taken from ``names_list``.
        data_element = data
        if isinstance(data_element, pandas.Series):
            data_element = DataElementDecorator(data)
        line_id = data_element.key
        line_chart.add_decorator(data_element)

        # Retrieve the last data point.
        point_to_emphasise = \
            (_get_last_valid_value(data_element.data.index), _get_last_valid_value(data_element.data.values))

        series_label = _get_name(names_list, names_index)
        if series_label is not None:
            legend_decorator.add_entry(
                data_element, series_label +
                " [{}]".format(point_to_emphasise[0].strftime("%b %y")))

        names_index += 1
        if not disable_dot:
            # Emphasise the last data point.
            point_emphasis = PointEmphasisDecorator(
                data_element,
                point_to_emphasise,
                decimal_points=dot_decimal_points,
                key="point_emphasis_{}".format(line_id),
                use_secondary_axes=data_element.use_secondary_axes)
            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)

    # Create spans (rectangles) to highlight the recession periods.
    if recession_series is not None:
        span_decorator = SpanDecorator.from_int_list(recession_series, "span")
        line_chart.add_decorator(span_decorator)
        if recession_name is not None:
            legend_decorator.add_entry(span_decorator, recession_name)

    # Create horizontal lines.
    if horizontal_lines_list is not None:
        for hline in horizontal_lines_list:
            line_decorator = HorizontalLineDecorator(hline,
                                                     key="hline" + str(hline))
            line_chart.add_decorator(line_decorator)
            series_label = _get_name(names_list, names_index)
            if series_label is not None:
                legend_decorator.add_entry(line_decorator, series_label)

            names_index += 1

    # Create vertical lines.
    if vertical_lines_list is not None:
        for vline in vertical_lines_list:
            line_decorator = VerticalLineDecorator(vline,
                                                   key="vline" + str(vline))
            line_chart.add_decorator(line_decorator)
            series_label = _get_name(names_list, names_index)
            if series_label is not None:
                legend_decorator.add_entry(line_decorator, series_label)
            names_index += 1

    # Add a legend.
    line_chart.add_decorator(legend_decorator)

    return line_chart
Beispiel #9
0
def create_event_comparison_chart(
        series: QFSeries, event_dates_list: Iterable[datetime], title: str, samples_before: int = 100,
        samples_after: int = 200, rebase_method: RebaseMethod = RebaseMethod.divide) -> LineChart:
    """
    Creates a new chart based on a line chart. The chart puts all events at date = 0 and than compares the evolution
    of the series after the event date.

    Parameters
    ----------
    series: QFSeries
        Series usually with values of an index or level of interest rates
    event_dates_list: Iterable[datetime]
        A list specifying the dates of the events that we would like to compare. Each date will create
        a new series in the chart
    title: str
        The title of the graph, specify ``None`` if you don't want the chart to show a title.
    samples_before: int
        Number of samples shown on the chart that are before the event date
    samples_after: int
        Number of samples after the event date that are plotted on the chart
    rebase_method: RebaseMethod
        Specifies the way in which the data is normalised at the date of the event.

        - 'divide' - will divide all the values by the value at the date of the event
        - 'subtract' - will subtract the value at the event from the whole sample
        - 'none - will show the value as is (no rebasing).

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

    # Create a chart
    line_chart = LineChart()
    legend_decorator = LegendDecorator(key='legend')

    # For each date create a rebased series with event date index = 0
    for event_date in event_dates_list:
        # calculate the integer index of the beginning and end of the sample
        event_closest_date = series.index.asof(event_date)

        if isinstance(event_closest_date, datetime):
            event_date_int_index = series.index.get_loc(event_closest_date)
            start_index = event_date_int_index - samples_before
            end_index = event_date_int_index + samples_after
            end_index = min(end_index, series.count())

            # to simplify the algorithm accept only samples that are within the series
            if start_index >= 0 and end_index <= series.count():
                series_sample = series.iloc[start_index:end_index]
                value_at_event = series.asof(event_date)

                if rebase_method == RebaseMethod.divide:
                    series_sample = series_sample.div(value_at_event)
                elif rebase_method == RebaseMethod.subtract:
                    series_sample = series_sample.subtract(value_at_event)
                elif rebase_method == RebaseMethod.norebase:
                    series_sample = series_sample
                else:
                    raise ValueError("Incorrect rebase_method. Use 'divide', 'subtract' or 'norebase' ")

                new_index = np.array(range(0, series_sample.count())) - samples_before
                reindexed_series = QFSeries(data=series_sample.values, index=new_index)

                data_element = DataElementDecorator(reindexed_series)
                line_chart.add_decorator(data_element)

                legend_decorator.add_entry(data_element, event_date.strftime('%Y-%m-%d'))

    # Put vertical line at x = 0
    line_decorator = VerticalLineDecorator(x=0, key='vline', linestyle="dashed")
    line_chart.add_decorator(line_decorator)

    # Create a title
    title_decorator = TitleDecorator(title, key="title")
    line_chart.add_decorator(title_decorator)

    # Add a legend.
    line_chart.add_decorator(legend_decorator)

    return line_chart
def create_rolling_chart_using_benchmark(series: Union[QFSeries,
                                                       List[QFSeries]],
                                         benchmark_series: QFSeries,
                                         func: RollingWindowFunction,
                                         func_name: str,
                                         window_size: int = 126,
                                         step: int = 20,
                                         oos_date: str = None) -> LineChart:
    """
    Creates a new line chart and adds the rolling window for each of the specified series to it. The `func`
    function is fed data for each window and whatever it returns is added to the resulting rolled series.

    For example:

    .. code-block::python
        create_rolling_chart_using_benchmark([strategy1_tms, strategy2_tms], benchmark_tms
                             lambda a, b: beta_and_alpha(PricesSeries(a), PricesSeries(b))[0],
                             "Sharpe Ratio", oos_date=oos_date)

    The example above will return beta of every rolling window

    Parameters
    ----------
    series
        One or more series to apply the rolling window transformation on add to the resulting chart.
    benchmark_series
        benchmark for every series passed as a first argument
    func
        Called for each window. Takes two arguments (series_window, benchmark_window). Returns a float.
    func_name
        Used in the title to specify the function that was called.
    window_size
    step
        determines by how many steps we shift the rolling window
    oos_date
        only the OOS date of the first series in the list will be taken into account

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

    chart = LineChart()

    # Add a legend.
    legend = LegendDecorator()
    chart.add_decorator(legend)

    series_list = series
    if isinstance(series_list, QFSeries):
        series_list = [series_list]
    assert isinstance(series_list, list)

    for tms in series_list:
        rolling = tms.rolling_window_with_benchmark(benchmark_series,
                                                    window_size,
                                                    func,
                                                    step=step)
        rolling_element = DataElementDecorator(rolling)
        chart.add_decorator(rolling_element)
        legend.add_entry(rolling_element, tms.name)

    # Add title
    chart.add_decorator(
        TitleDecorator("Rolling {} (window: {}, step: {}, BM: {}) ".format(
            func_name, window_size, step, benchmark_series.name)))

    # Add OOS line.
    new_oos_date = series_list[0].index.asof(pandas.to_datetime(oos_date))
    line = VerticalLineDecorator(new_oos_date,
                                 color='orange',
                                 linestyle="dashed")
    chart.add_decorator(line)

    return chart
Beispiel #11
0
def create_rolling_chart(series: Union[QFSeries, List[QFSeries]],
                         func: Callable[[Union[QFSeries, numpy.ndarray]],
                                        float],
                         func_name: str,
                         window_size: int = 126,
                         step: int = 20,
                         oos_date: str = None) -> LineChart:
    """
    Creates a new line chart and adds the rolling window for each of the specified series to it. The `func`
    function is fed data for each window and whatever it returns is added to the resulting rolled series.

    For example:

    .. code-block::python
        create_rolling_chart([strategy_tms, benchmark_tms],
                             lambda window: sharpe_ratio(PricesSeries(window), Frequency.DAILY),
                             "Sharpe Ratio", oos_date=oos_date)

    Parameters
    ----------
    series
        One or more series to apply the rolling window transformation on add to the resulting chart.
    func
        Called for each window. Takes one argument which is part of a series corresponding to a window. Returns a float.
    func_name
        Used in the title to specify the function that was called.
    window_size
    step
    oos_date
        only the OOS date of the first series in the list will be taken into account

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

    chart = LineChart()

    # Add a legend.
    legend = LegendDecorator()
    chart.add_decorator(legend)

    series_list = series
    if isinstance(series_list, QFSeries):
        series_list = [series_list]
    assert isinstance(series_list, list)

    for tms in series_list:
        rolling = tms.rolling_window(window_size, func, step=step)
        rolling_element = DataElementDecorator(rolling)
        chart.add_decorator(rolling_element)
        legend.add_entry(rolling_element, tms.name)

    # Add title
    chart.add_decorator(
        TitleDecorator("Rolling {} (window: {}, step: {}) ".format(
            func_name, window_size, step)))

    # Add OOS line.
    new_oos_date = series_list[0].index.asof(pandas.to_datetime(oos_date))
    line = VerticalLineDecorator(new_oos_date,
                                 color='orange',
                                 linestyle="dashed")
    chart.add_decorator(line)

    return chart