파이썬으로 이벤트 기반 백테스팅 - VII부

저자:선함, 2019-03-26 10:52:49, 업데이트:

이벤트 주도 백테스터 시리즈의 마지막 기사에서 우리는 기본 실행 핸들러 계층을 고려했습니다. 이 기사에서는 포트폴리오 객체에서 이전에 구축 된 주식 곡선 DataFrame을 사용하여 백테스트 후 전략의 성능을 평가하는 방법을 논의 할 것입니다.

성능 측정

우리는 이미 이전 기사에서 샤르프 비율을 고려했습니다. 그 기사에서 나는 (년화 된) 샤르프 비율이 다음과 같이 계산된다는 것을 설명합니다.img

여기서 Ra는 주식 곡선의 수익 흐름이고 Rb는 적절한 금리 또는 주식 지수와 같은 기준입니다.

최대 마감과 마감 기간은 투자자들이 종종 포트폴리오의 위험을 평가하기 위해 사용하는 두 가지 추가 측정입니다. 첫 번째 양은 주식 곡선 성과에서 가장 높은 최고에서 최저 감소, 후자는 발생하는 거래 기간의 수로 정의됩니다.

이 문서에서는 파이썬 기반 이벤트 구동 백테스팅 제품군에서 사용할 수 있는 포트폴리오 성능의 측정으로 샤프 비율, 최대 드래운 및 드래운 기간을 구현할 것입니다.

파이썬 구현

첫 번째 작업은 새로운 파일을 만드는 것입니다.performance.py, 샤프 비율을 계산하는 함수와 드라우다운 정보를 저장합니다. 계산이 무거운 클래스 대부분과 마찬가지로 우리는 NumPy와 판다를 가져와야 합니다:

# performance.py

import numpy as np
import pandas as pd

샤르프 비율은 위험과 보상 (사실 많은 것 중 하나) 의 척도라는 점에 유의하십시오.

일반적으로 이 값은 연간 미국 내 거래일 수인 252로 설정된다. 그러나 만약 당신의 전략이 한 시간 내에 거래된다면, 당신은 그것을 올바르게 연간화하기 위해 샤르프를 조정해야 한다. 따라서 연간을 2526.5=1638로 설정해야 한다. 이는 1년 내 미국 내 거래시간 수이다. 만약 당신이 분별적으로 거래한다면, 이 인수는 2526.560=98280으로 설정되어야 한다.

create_sharpe_ratio 함수는 반다 시리즈 객체에서 작동합니다. 반수라고 불리며 단순히 기간 비율 반수와 기간 비율 반수 표준편차의 비율을 계산합니다.

# 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과 같은 시간 단위로 직접 번역 할 수 없기 때문입니다.

이 함수는 각 거래에서 마감량과 기간을 나타내는 두 개의 판다 시리즈 객체를 생성하여 시작됩니다. 그 다음 주식 곡선이 이전 모든 피크를 초과하는지 여부를 결정하여 현재 고수표 (HWM) 를 설정합니다.

드라우다운은 단순히 현재 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

포트폴리오는 추상적인 기본 클래스이기 때문에 우리는 이 경우 NaivePortfolio가 되는 파생 클래스 중 하나에 메소드를 첨부하고자 합니다. 따라서 우리는 Sharpe와 마감 정보를 생성하기 위해 포트폴리올 주식 곡선에 작용하는 output_summary_stats라는 메소드를 만들 것입니다.

이 방법은 간단합니다. 단순히 두 가지 성능 척도를 활용하고 직접 펀드 곡선 DataFrame에 적용하여 형식 친화적인 방식으로 통계를 튜플 목록으로 출력합니다.

# 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에 포함합니다.


더 많은