Event-Driven Backtesting dengan Python - Bagian VII

Penulis:Kebaikan, Dibuat: 2019-03-26 10:52:49, Diperbarui:

Dalam artikel terakhir tentang seri Backtester Event-Driven kami mempertimbangkan hierarki ExecutionHandler dasar. Dalam artikel ini kita akan membahas bagaimana menilai kinerja strategi pasca backtest menggunakan kurva ekuitas DataFrame yang dibangun sebelumnya dalam objek Portfolio.

Metrik Kinerja

Kami telah membahas rasio Sharpe dalam artikel sebelumnya.Di artikel itu saya menguraikan bahwa rasio Sharpe (taunan) dihitung melalui:img

Di mana Ra adalah aliran pengembalian kurva ekuitas dan Rb adalah patokan, seperti suku bunga atau indeks ekuitas yang sesuai.

Pendapatan maksimum dan durasi penarikan adalah dua ukuran tambahan yang sering digunakan investor untuk menilai risiko dalam portofolio.

Dalam artikel ini kita akan menerapkan rasio Sharpe, penarikan maksimum dan durasi penarikan sebagai ukuran kinerja portofolio untuk digunakan dalam Suite Backtesting Event-Driven berbasis Python.

Implementasi Python

Tugas pertama adalah untuk membuat file baruperformance.py, yang menyimpan fungsi untuk menghitung rasio Sharpe dan informasi drawdown.

# performance.py

import numpy as np
import pandas as pd

Perhatikan bahwa rasio Sharpe adalah ukuran risiko untuk imbalan (pada kenyataannya ini adalah salah satu dari banyak!)

Biasanya nilai ini ditetapkan menjadi 252, yang merupakan jumlah hari perdagangan di AS per tahun. Namun, jika strategi Anda diperdagangkan dalam satu jam, Anda perlu menyesuaikan Sharpe untuk mempertahunikannya dengan benar. Dengan demikian, Anda perlu menetapkan periode menjadi 2526.5=1638, yang merupakan jumlah jam perdagangan AS dalam satu tahun. Jika Anda berdagang secara rutin, maka faktor ini harus ditetapkan menjadi 2526.560=98280.

Fungsi create_sharpe_ratio beroperasi pada obyek Pandas Series yang disebut pengembalian dan hanya menghitung rasio rata-rata pengembalian persentase periode dan deviasi standar pengembalian persentase periode yang diskalakan oleh faktor periode:

# 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)

Sementara rasio Sharpe mencirikan berapa banyak risiko (seperti yang didefinisikan oleh deviasi standar jalur aset) yang diambil per unit pengembalian, drawdown didefinisikan sebagai penurunan terbesar dari puncak ke bawah di sepanjang kurva ekuitas.

Fungsi create_drawdowns di bawah ini sebenarnya memberikan kedua penarikan maksimum dan durasi penarikan maksimum. Yang pertama adalah penurunan puncak-ke-rendah terbesar yang disebutkan di atas, sementara yang terakhir didefinisikan sebagai jumlah periode di mana penurunan ini terjadi.

Ada beberapa kehalusan yang diperlukan dalam interpretasi durasi penarikan karena menghitung periode perdagangan dan karenanya tidak dapat diterjemahkan langsung ke dalam satuan waktu seperti hari.

Fungsi ini dimulai dengan membuat dua objek seri panda yang mewakili penurunan dan durasi pada setiap perdagangan bar. Kemudian tanda air tinggi saat ini (HWM) ditetapkan dengan menentukan apakah kurva ekuitas melebihi semua puncak sebelumnya.

Penarikan kemudian hanya perbedaan antara HWM saat ini dan kurva ekuitas. Jika nilai ini negatif maka durasi ditingkatkan untuk setiap bar yang terjadi sampai HWM berikutnya tercapai. Fungsi kemudian hanya mengembalikan maksimum masing-masing dari dua seri:

# 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()

Untuk menggunakan ukuran kinerja ini, kita membutuhkan cara untuk menghitungnya setelah backtest dilakukan, yaitu ketika kurva ekuitas yang sesuai tersedia!

Kita juga perlu menghubungkan perhitungan dengan hierarki objek tertentu. Mengingat bahwa ukuran kinerja dihitung berdasarkan portofolio, masuk akal untuk melampirkan perhitungan kinerja ke metode pada hierarki kelas portofolio yang kita bahas dalam artikel ini.

Tugas pertama adalah membukaportfolio.pyseperti yang dibahas dalam artikel sebelumnya dan mengimpor fungsi kinerja:

# portfolio.py

..  # Other imports

from performance import create_sharpe_ratio, create_drawdowns

Karena Portfolio adalah kelas dasar abstrak kita ingin melampirkan metode ke salah satu kelas turunan, yang dalam hal ini akan NaivePortfolio. Oleh karena itu kita akan membuat metode yang disebut output_summary_stats yang akan bertindak pada kurva ekuitas portofolio untuk menghasilkan informasi Sharpe dan penarikan.

Metode ini sederhana. hanya menggunakan dua ukuran kinerja dan menerapkannya langsung ke kurva ekuitas panda DataFrame, output statistik sebagai daftar tuples dengan cara yang ramah format:

# 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

Jelas ini adalah analisis kinerja yang sangat sederhana untuk portofolio. tidak mempertimbangkan analisis tingkat perdagangan atau ukuran risiko / imbalan lainnya.performance.pydan kemudian menggabungkannya ke output_summary_stat seperti yang diperlukan.


Lebih banyak