События, обусловленные обратным тестированием с помощью Python - часть VII

Автор:Доброта, Создано: 2019-03-26 10:52:49, Обновлено:

В последней статье по серии Event-Driven Backtester мы рассмотрели основную иерархию ExecutionHandler. В этой статье мы обсудим, как оценить производительность стратегии после обратного теста с использованием ранее построенной кривой собственности DataFrame в объекте Portfolio.

Показатели производительности

В предыдущей статье мы уже рассматривали коэффициент Шарпа.В этой статье я описываю, что (годовой) коэффициент Шарпа рассчитывается с помощью:img

где Ra - поток доходности кривой собственного капитала, а Rb - эталон, например, соответствующая процентная ставка или индекс собственного капитала.

Максимальное снижение и продолжительность снижения - это две дополнительные меры, которые инвесторы часто используют для оценки риска в портфеле.

В этой статье мы будем реализовывать коэффициент Шарпа, максимальный вывод и длительность вывода в качестве показателей производительности портфеля для использования в на базе Python Suite Event-Driven Backtesting.

Использование Python

Первая задача - создать новый файлperformance.py, который хранит функции для расчета коэффициента Шарпа и информацию о снижении.

# performance.py

import numpy as np
import pandas as pd

Следует отметить, что коэффициент Шарпа является мерой риска к вознаграждению (на самом деле, это один из многих!).

Обычно это значение устанавливается на 252, что является числом торговых дней в США в год. Однако, если ваша стратегия торгуется в течение часа, вам нужно скорректировать Шарп, чтобы правильно годовать его. Таким образом, вам нужно установить периоды на 2526.5=1638, что является числом торговых часов в США в течение года. Если вы торгуете на минутной основе, то этот фактор должен быть установлен на 2526.560=98280.

Функция create_sharpe_ratio работает на объекте Pandas Series под названием returns и просто вычисляет соотношение среднего показателя процентного возврата периода и стандартных отклонений процентного возврата периода, скалированных по фактору периодов:

# performance.py

def create_sharpe_ratio(returns, periods=252):
    """
    Create the Sharpe ratio for the strategy, based on a 
    benchmark of zero (i.e. no risk-free rate information).

    Parameters:
    returns - A pandas Series representing period percentage returns.
    periods - Daily (252), Hourly (252*6.5), Minutely(252*6.5*60) etc.
    """
    return np.sqrt(periods) * (np.mean(returns)) / np.std(returns)

В то время как коэффициент Шарпа характеризует, сколько риска (как определено стандартным отклонением пути активов) принимается на единицу доходности, drawdown определяется как самое большое падение от пика до минимума вдоль кривой собственного капитала.

Функция create_drawdowns ниже на самом деле предоставляет как максимальный вывод, так и максимальную продолжительность вывода. Первый является вышеупомянутым самым большим падением от пика до дна, в то время как последний определяется как количество периодов, в течение которых происходит это падение.

Некоторые тонкости требуются при интерпретации длительности использования, поскольку она учитывает периоды торговли и, следовательно, не может быть прямо переведена в временную единицу, такую как days.

Функция начинается с создания двух объектов серии панды, представляющих снижение и длительность на каждой торговле bar. Затем текущая высокая водная отметка (HWM) устанавливается путем определения того, превышает ли кривая акций все предыдущие пики.

Если это значение отрицательное, то длительность увеличивается для каждого бара, который происходит до достижения следующего HWM. Функция затем просто возвращает максимум каждого из двух рядов:

# performance.py

def create_drawdowns(equity_curve):
    """
    Calculate the largest peak-to-trough drawdown of the PnL curve
    as well as the duration of the drawdown. Requires that the 
    pnl_returns is a pandas Series.

    Parameters:
    pnl - A pandas Series representing period percentage returns.

    Returns:
    drawdown, duration - Highest peak-to-trough drawdown and duration.
    """

    # Calculate the cumulative returns curve 
    # and set up the High Water Mark
    # Then create the drawdown and duration series
    hwm = [0]
    eq_idx = equity_curve.index
    drawdown = pd.Series(index = eq_idx)
    duration = pd.Series(index = eq_idx)

    # Loop over the index range
    for t in range(1, len(eq_idx)):
        cur_hwm = max(hwm[t-1], equity_curve[t])
        hwm.append(cur_hwm)
        drawdown[t]= hwm[t] - equity_curve[t]
        duration[t]= 0 if drawdown[t] == 0 else duration[t-1] + 1
    return drawdown.max(), duration.max()

Для использования этих показателей эффективности нам нужен способ их расчета после проведения обратного теста, т.е. когда доступна подходящая кривая собственности!

Учитывая, что показатели эффективности рассчитываются на основе портфеля, имеет смысл привязать расчеты эффективности к методу по иерархии класса портфеля, который мы обсудили в этой статье.

Первая задача - открыться.portfolio.pyкак обсуждалось в предыдущей статье и импортировать функции производительности:

# portfolio.py

..  # Other imports

from performance import create_sharpe_ratio, create_drawdowns

Поскольку Portfolio является абстрактным базовым классом, мы хотим прикрепить метод к одному из его производных классов, который в данном случае будет NaivePortfolio.

Метод прост: он просто использует два показателя производительности и применяет их непосредственно к кривой собственности DataFrame, выводя статистику в виде списка tuples в формате:

# portfolio.py

..
..

class NaivePortfolio(object):

    ..
    ..

    def output_summary_stats(self):
        """
        Creates a list of summary statistics for the portfolio such
        as Sharpe Ratio and drawdown information.
        """
        total_return = self.equity_curve['equity_curve'][-1]
        returns = self.equity_curve['returns']
        pnl = self.equity_curve['equity_curve']

        sharpe_ratio = create_sharpe_ratio(returns)
        max_dd, dd_duration = create_drawdowns(pnl)

        stats = [("Total Return", "%0.2f%%" % ((total_return - 1.0) * 100.0)),
                 ("Sharpe Ratio", "%0.2f" % sharpe_ratio),
                 ("Max Drawdown", "%0.2f%%" % (max_dd * 100.0)),
                 ("Drawdown Duration", "%d" % dd_duration)]
        return stats

Очевидно, что это очень простой анализ эффективности для портфеля. Он не учитывает анализ на уровне торговли или другие меры риска/вознаграждения.performance.pyи затем включить их в output_summary_stat по мере необходимости.


Больше