Pengujian Kembali yang Dikendalikan Acara dengan Python - Bahagian I

Penulis:Kebaikan, Dicipta: 2019-03-22 11:53:50, Dikemas kini:

Kami telah menghabiskan beberapa bulan terakhir di QuantStart backtesting pelbagai strategi perdagangan menggunakan Python dan panda. Sifat vektor panda memastikan bahawa operasi tertentu pada set data yang besar sangat cepat. Walau bagaimanapun, bentuk backtester vektor yang telah kami kaji setakat ini mengalami beberapa kelemahan dalam cara pelaksanaan perdagangan disimulasikan. Dalam siri artikel ini kami akan membincangkan pendekatan yang lebih realistik untuk simulasi strategi sejarah dengan membina persekitaran backtesting yang didorong oleh peristiwa menggunakan Python.

Perisian yang Dikendalikan Acara

Sebelum kita menggali dalam pembangunan backtester seperti itu, kita perlu memahami konsep sistem yang didorong oleh peristiwa. Permainan video menyediakan kes penggunaan semula jadi untuk perisian yang didorong oleh peristiwa dan memberikan contoh yang mudah untuk diterokai. Permainan video mempunyai beberapa komponen yang berinteraksi antara satu sama lain dalam tetapan masa nyata pada kadar bingkai yang tinggi. Ini dikendalikan dengan menjalankan keseluruhan set pengiraan dalam gelung tidak berkesudahan yang dikenali sebagai gelung peristiwa atau gelung permainan.

Pada setiap klik gelung permainan, fungsi dipanggil untuk menerima peristiwa terkini, yang akan dihasilkan oleh beberapa tindakan sebelumnya yang sepadan dalam permainan. Bergantung pada sifat peristiwa, yang mungkin termasuk menekan kekunci atau klik tetikus, beberapa tindakan berikutnya diambil, yang akan menamatkan gelung atau menghasilkan beberapa peristiwa tambahan. Proses itu kemudian akan diteruskan. Berikut adalah beberapa contoh kod palsu:

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

Kod ini sentiasa memeriksa peristiwa baru dan kemudian melakukan tindakan berdasarkan peristiwa ini. Khususnya, ia membolehkan ilusi pengendalian tindak balas masa nyata kerana kod itu terus-menerus dilipat dan peristiwa diperiksa. Seperti yang akan menjadi jelas ini adalah tepat apa yang kita perlukan untuk menjalankan simulasi perdagangan frekuensi tinggi.

Mengapa Ujian Kembali yang Dikendalikan Acara?

Sistem yang didorong peristiwa memberikan banyak kelebihan berbanding pendekatan vektor:

  • Penggunaan semula kod - Penguji balik yang didorong oleh peristiwa, oleh reka bentuk, boleh digunakan untuk kedua-dua pengujian balik sejarah dan perdagangan langsung dengan pertukaran komponen yang minimum.
  • Bias Lookahead - Dengan backtester yang didorong oleh peristiwa tidak ada bias lookahead kerana penerimaan data pasaran dianggap sebagai peristiwa yang mesti ditindaklanjuti.
  • Realisme - Uji balik yang didorong oleh peristiwa membolehkan penyesuaian yang signifikan mengenai bagaimana pesanan dilaksanakan dan kos transaksi yang dikeluarkan. Ia mudah untuk mengendalikan pesanan pasaran dan had asas, serta pasaran terbuka (MOO) dan pasaran dekat (MOC), kerana pengendali pertukaran tersuai boleh dibina.

Walaupun sistem yang didorong oleh peristiwa mempunyai banyak faedah, mereka mengalami dua kelemahan utama berbanding sistem vektor yang lebih mudah. Pertama, mereka jauh lebih kompleks untuk dilaksanakan dan diuji. Terdapat lebih banyak bahagian bergerak yang membawa kepada peluang yang lebih besar untuk memperkenalkan bug.

Kedua, ia lebih lambat untuk dilaksanakan berbanding sistem vektor. Operasi vektor yang optimum tidak dapat digunakan semasa menjalankan pengiraan matematik.

Ringkasan Ujian Kembali yang Dikendalikan Acara

Untuk menggunakan pendekatan yang didorong oleh peristiwa kepada sistem backtesting adalah perlu untuk menentukan komponen (atau objek) kami yang akan menangani tugas tertentu:

  • Acara - Acara adalah unit kelas asas sistem yang didorong oleh peristiwa. Ia mengandungi jenis (seperti MARKET, SIGNAL, ORDER atau FILL) yang menentukan bagaimana ia akan ditangani dalam gelung acara.
  • Antrian Acara - Antrian Acara adalah objek Antrian Python dalam memori yang menyimpan semua objek subkelas Acara yang dihasilkan oleh perisian yang lain.
  • DataHandler - DataHandler adalah kelas asas abstrak (ABC) yang membentangkan antara muka untuk mengendalikan kedua-dua data pasaran sejarah atau langsung. Ini memberikan fleksibiliti yang ketara kerana modul Strategi dan Portfolio dapat digunakan semula di antara kedua-dua pendekatan. DataHandler menghasilkan MarketEvent baru pada setiap denyutan jantung sistem (lihat di bawah).
  • Strategi - Strategi juga merupakan ABC yang membentangkan antara muka untuk mengambil data pasaran dan menjana SignalEvents yang sepadan, yang akhirnya digunakan oleh objek Portfolio.
  • Portfolio - Ini adalah ABC yang mengendalikan pengurusan pesanan yang berkaitan dengan kedudukan semasa dan seterusnya untuk strategi. Ia juga menjalankan pengurusan risiko di seluruh portfolio, termasuk pendedahan sektor dan saiz kedudukan. Dalam pelaksanaan yang lebih canggih ini boleh diarahkan kepada kelas RiskManagement. Portfolio mengambil SignalEvents dari Antrian dan menghasilkan OrderEvents yang ditambahkan ke Antrian.
  • ExecutionHandler - ExecutionHandler mensimulasikan sambungan ke broker. Tugas pengendali adalah untuk mengambil OrderEvents dari antrian dan melaksanakan mereka, sama ada melalui pendekatan yang disimulasikan atau sambungan sebenar ke broker hati. Setelah pesanan dilaksanakan pengendali membuat FillEvents, yang menerangkan apa yang sebenarnya ditransaksi, termasuk yuran, komisen dan slippage (jika dimodelkan).
  • Loop - Semua komponen ini dibungkus dalam gelung peristiwa yang menangani semua jenis peristiwa dengan betul, mengarahkan mereka ke komponen yang sesuai.

Ini adalah model yang cukup asas dari enjin perdagangan. Terdapat ruang yang besar untuk pengembangan, terutamanya berkaitan dengan bagaimana Portfolio digunakan. Di samping itu, model kos transaksi yang berbeza juga mungkin diringkaskan ke dalam hierarki kelas mereka sendiri. Pada peringkat ini ia memperkenalkan kerumitan yang tidak perlu dalam siri artikel ini jadi kita tidak akan membincangkannya lebih lanjut. Dalam tutorial seterusnya kita mungkin akan mengembangkan sistem untuk memasukkan realisme tambahan.

Berikut adalah petikan kod Python yang menunjukkan bagaimana backtester berfungsi dalam amalan. Terdapat dua gelung yang berlaku dalam kod. Gelung luar digunakan untuk memberi denyutan jantung kepada backtester. Untuk perdagangan langsung ini adalah kekerapan di mana data pasaran baru disurvei. Untuk strategi backtesting ini tidak semestinya diperlukan kerana backtester menggunakan data pasaran yang disediakan dalam bentuk drip-feed (lihat baris bars.update_bars)).

Loop dalaman sebenarnya mengendalikan peristiwa dari objek Event Queue. Acara tertentu diarahkan kepada komponen masing-masing dan kemudian acara baru ditambahkan ke antrian. Apabila Event Queue kosong, gelung denyutan jantung berterusan:

# 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 asas bagaimana backtester yang didorong oleh peristiwa direka.


Lebih lanjut