Event-Driven Backtesting dengan Python - Bagian I

Penulis:Kebaikan, Dibuat: 2019-03-22 11:53:50, Diperbarui:

Kami telah menghabiskan beberapa bulan terakhir pada QuantStart backtesting berbagai strategi perdagangan menggunakan Python dan panda. Sifat vektorisasi panda memastikan bahwa operasi tertentu pada set data besar sangat cepat. Namun bentuk backtester vektorisasi yang telah kami pelajari sejauh ini menderita beberapa kelemahan dalam cara eksekusi perdagangan disimulasikan. Dalam seri artikel ini kami akan membahas pendekatan yang lebih realistis untuk simulasi strategi historis dengan membangun lingkungan backtesting berbasis peristiwa menggunakan Python.

Perangkat Lunak yang Didorong Acara

Sebelum kita menyelidiki pengembangan backtester semacam itu, kita perlu memahami konsep sistem yang didorong oleh peristiwa. Video game memberikan kasus penggunaan alami untuk perangkat lunak yang didorong oleh peristiwa dan memberikan contoh yang mudah untuk dijelajahi. Sebuah video game memiliki beberapa komponen yang berinteraksi satu sama lain dalam pengaturan waktu nyata pada framerate tinggi. Hal ini ditangani dengan menjalankan seluruh set perhitungan dalam lingkaran tak terbatas yang dikenal sebagai event-loop atau game-loop.

Pada setiap klik game-loop sebuah fungsi dipanggil untuk menerima peristiwa terbaru, yang akan dihasilkan oleh beberapa tindakan sebelumnya yang sesuai dalam permainan. Tergantung pada sifat peristiwa, yang dapat mencakup menekan tombol atau mengklik mouse, beberapa tindakan berikutnya diambil, yang akan mengakhiri loop atau menghasilkan beberapa peristiwa tambahan. Prosesnya kemudian akan berlanjut. Berikut adalah beberapa contoh pseudo-kode:

while True:  # Run the loop forever
    new_event = get_new_event()   # Get the latest event

    # Based on the event type, perform an action
    if new_event.type == "LEFT_MOUSE_CLICK":
        open_menu()
    elif new_event.type == "ESCAPE_KEY_PRESS":
        quit_game()
    elif new_event.type == "UP_KEY_PRESS":
        move_player_north()
    # ... and many more events

    redraw_screen()   # Update the screen to provide animation
    tick(50)   # Wait 50 milliseconds

Kode terus-menerus memeriksa untuk peristiwa baru dan kemudian melakukan tindakan berdasarkan peristiwa ini. Secara khusus memungkinkan ilusi penanganan respon real-time karena kode terus-menerus dilipat dan peristiwa diperiksa. Seperti yang akan menjadi jelas ini adalah persis apa yang kita butuhkan untuk melakukan simulasi perdagangan frekuensi tinggi.

Mengapa Sebuah Backtester Event-Driven?

Sistem yang didorong oleh peristiwa memberikan banyak keuntungan dibandingkan dengan pendekatan vektor:

  • Code Reuse - Sebuah backtester event-driven, oleh desain, dapat digunakan untuk backtesting historis dan perdagangan langsung dengan switching-out komponen minimal.
  • Lookahead Bias - Dengan backtester event-driven tidak ada bias lookahead karena penerimaan data pasar diperlakukan sebagai event yang harus ditindaklanjuti.
  • Realisme - backtesters event-driven memungkinkan kustomisasi yang signifikan tentang bagaimana order dieksekusi dan biaya transaksi yang dikeluarkan.

Meskipun sistem event-driven memiliki banyak manfaat, mereka menderita dua kelemahan utama dibandingkan dengan sistem vektorisasi yang lebih sederhana. Pertama, mereka jauh lebih kompleks untuk diimplementasikan dan diuji. Ada lebih banyak bagian bergerak yang mengarah pada peluang lebih besar untuk memperkenalkan bug. Untuk mengurangi metodologi pengujian perangkat lunak yang tepat seperti pengembangan test-driven dapat digunakan.

Operasi vektor optimal tidak dapat digunakan saat melakukan perhitungan matematika. Kami akan membahas cara mengatasi keterbatasan ini di artikel berikutnya.

Events-Driven Backtester Ringkasan

Untuk menerapkan pendekatan yang didorong oleh peristiwa untuk sistem backtesting, perlu untuk mendefinisikan komponen (atau objek) kami yang akan menangani tugas tertentu:

  • Event - The Event adalah unit kelas dasar dari sistem event-driven. Ini berisi tipe (seperti MARKET, SIGNAL, ORDER atau FILL) yang menentukan bagaimana hal itu akan ditangani dalam event-loop.
  • Event Queue - The Event Queue adalah obyek Queue Python in-memory yang menyimpan semua objek sub-kelas Event yang dihasilkan oleh perangkat lunak lainnya.
  • DataHandler - DataHandler adalah kelas dasar abstrak (ABC) yang menyajikan antarmuka untuk menangani data pasar historis atau langsung. Ini memberikan fleksibilitas yang signifikan karena modul Strategi dan Portofolio dapat digunakan kembali antara kedua pendekatan. DataHandler menghasilkan MarketEvent baru pada setiap detak jantung sistem (lihat di bawah).
  • Strategi - Strategi juga merupakan ABC yang menyajikan antarmuka untuk mengambil data pasar dan menghasilkan SignalEvents yang sesuai, yang pada akhirnya digunakan oleh objek Portfolio.
  • Portfolio - Ini adalah ABC yang menangani manajemen pesanan yang terkait dengan posisi saat ini dan berikutnya untuk strategi. Ini juga melakukan manajemen risiko di seluruh portofolio, termasuk eksposur sektor dan ukuran posisi. Dalam implementasi yang lebih canggih ini dapat didelegasikan ke kelas RiskManagement. Portfolio mengambil SignalEvents dari antrian dan menghasilkan OrderEvents yang ditambahkan ke antrian.
  • ExecutionHandler - The ExecutionHandler mensimulasikan koneksi ke broker. Tugas pengendali adalah untuk mengambil OrderEvents dari antrian dan mengeksekusi mereka, baik melalui pendekatan simulasi atau koneksi aktual ke broker hati. Setelah perintah dieksekusi pengendali membuat FillEvents, yang menggambarkan apa yang sebenarnya ditransaksi, termasuk biaya, komisi dan slippage (jika dimodelkan).
  • The Loop - Semua komponen ini dibungkus dalam event-loop yang dengan benar menangani semua jenis Event, mengarahkan mereka ke komponen yang sesuai.

Ini adalah model yang cukup dasar dari mesin perdagangan. Ada ruang yang signifikan untuk perluasan, terutama dalam hal bagaimana portofolio digunakan. Selain itu, model biaya transaksi yang berbeda juga dapat di abstrak ke dalam hirarki kelas mereka sendiri. Pada tahap ini, ini memperkenalkan kompleksitas yang tidak perlu dalam seri artikel ini sehingga kami tidak akan membahasnya lebih lanjut.

Di sini adalah cuplikan kode Python yang menunjukkan bagaimana backtester bekerja dalam praktek. Ada dua loop yang terjadi dalam kode. loop luar digunakan untuk memberi backtester detak jantung. Untuk perdagangan langsung ini adalah frekuensi di mana data pasar baru disurvei. Untuk strategi backtesting ini tidak benar-benar diperlukan karena backtester menggunakan data pasar yang disediakan dalam bentuk drop-feed (lihat baris bars.update_bars)).

The internal loop sebenarnya menangani Event dari event Queue object. Event tertentu didelegasikan ke komponen masing-masing dan kemudian event baru ditambahkan ke queue.

# Declare the components with respective parameters
bars = DataHandler(..)
strategy = Strategy(..)
port = Portfolio(..)
broker = ExecutionHandler(..)

while True:
    # Update the bars (specific backtest code, as opposed to live trading)
    if bars.continue_backtest == True:
        bars.update_bars()
    else:
        break
    
    # Handle the events
    while True:
        try:
            event = events.get(False)
        except Queue.Empty:
            break
        else:
            if event is not None:
                if event.type == 'MARKET':
                    strategy.calculate_signals(event)
                    port.update_timeindex(event)

                elif event.type == 'SIGNAL':
                    port.update_signal(event)

                elif event.type == 'ORDER':
                    broker.execute_order(event)

                elif event.type == 'FILL':
                    port.update_fill(event)

    # 10-Minute heartbeat
    time.sleep(10*60)

Ini adalah garis besar dasar bagaimana backtester event-driven dirancang.


Lebih banyak