Kiểm tra ngược dựa trên sự kiện với Python - Phần I

Tác giả:Tốt, Tạo: 2019-03-22 11:53:50, Cập nhật:

Chúng tôi đã dành vài tháng qua trên QuantStart backtesting các chiến lược giao dịch khác nhau sử dụng Python và panda. Bản chất vectorized của panda đảm bảo rằng một số hoạt động trên các bộ dữ liệu lớn là cực kỳ nhanh chóng. Tuy nhiên, các hình thức backtester vectorized mà chúng tôi đã nghiên cứu cho đến nay bị một số nhược điểm trong cách thực hiện giao dịch được mô phỏng. Trong loạt bài viết này chúng tôi sẽ thảo luận về một cách tiếp cận thực tế hơn để mô phỏng chiến lược lịch sử bằng cách xây dựng một môi trường backtesting dựa trên sự kiện bằng Python.

Phần mềm điều khiển sự kiện

Trước khi chúng ta đi sâu vào sự phát triển của một backtester như vậy, chúng ta cần hiểu khái niệm về các hệ thống dựa trên sự kiện. Trò chơi video cung cấp một trường hợp sử dụng tự nhiên cho phần mềm dựa trên sự kiện và cung cấp một ví dụ đơn giản để khám phá. Trò chơi video có nhiều thành phần tương tác với nhau trong thiết lập thời gian thực ở tốc độ khung hình cao. Điều này được xử lý bằng cách chạy toàn bộ bộ các tính toán trong một vòng lặp vô hạn được gọi là vòng lặp sự kiện hoặc vòng lặp trò chơi.

Mỗi lần nhấn vào vòng lặp trò chơi, một hàm được gọi để nhận sự kiện mới nhất, sẽ được tạo ra bởi một số hành động tương ứng trước đó trong trò chơi. Tùy thuộc vào bản chất của sự kiện, có thể bao gồm nhấn phím hoặc nhấp chuột, một số hành động tiếp theo sẽ được thực hiện, sẽ chấm dứt vòng lặp hoặc tạo ra một số sự kiện bổ sung. Quá trình sau đó sẽ tiếp tục. Dưới đây là một số ví dụ mã giả:

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

Mã liên tục kiểm tra các sự kiện mới và sau đó thực hiện các hành động dựa trên các sự kiện này. Đặc biệt nó cho phép ảo giác xử lý phản ứng thời gian thực vì mã liên tục được lặp lại và các sự kiện được kiểm tra. Như sẽ trở nên rõ ràng đây chính xác là những gì chúng ta cần để thực hiện mô phỏng giao dịch tần số cao.

Tại sao lại là một nhà kiểm tra hậu quả dựa trên sự kiện?

Các hệ thống dựa trên sự kiện cung cấp nhiều lợi thế so với cách tiếp cận theo vector:

  • Sử dụng lại mã - Một trình kiểm tra ngược dựa trên sự kiện, theo thiết kế, có thể được sử dụng cho cả kiểm tra ngược lịch sử và giao dịch trực tiếp với việc chuyển đổi tối thiểu các thành phần.
  • Lookahead Bias - Với một backtester dựa trên sự kiện, không có sự thiên vị về lookahead vì việc nhận dữ liệu thị trường được coi là một sự kiện phải được hành động.
  • Thực tế - Các backtester dựa trên sự kiện cho phép tùy chỉnh đáng kể về cách thực hiện lệnh và chi phí giao dịch. Nó rất đơn giản để xử lý lệnh thị trường cơ bản và giới hạn, cũng như thị trường mở (MOO) và thị trường đóng (MOC), vì có thể xây dựng một trình xử lý trao đổi tùy chỉnh.

Mặc dù các hệ thống dựa trên sự kiện có nhiều lợi ích, nhưng chúng có hai nhược điểm lớn so với các hệ thống vector hóa đơn giản hơn. Thứ nhất, chúng phức tạp hơn đáng kể trong việc triển khai và thử nghiệm. Có nhiều phần chuyển động hơn dẫn đến cơ hội giới thiệu lỗi lớn hơn. Để giảm thiểu phương pháp thử nghiệm phần mềm thích hợp như phát triển dựa trên thử nghiệm có thể được sử dụng.

Thứ hai, chúng hoạt động chậm hơn so với một hệ vector. Các hoạt động vector tối ưu không thể được sử dụng khi thực hiện các phép tính toán học. Chúng tôi sẽ thảo luận về cách khắc phục những hạn chế này trong các bài viết sau.

Tổng quan về Backtester điều khiển bởi sự kiện

Để áp dụng một phương pháp tiếp cận dựa trên sự kiện cho một hệ thống backtesting, cần phải xác định các thành phần (hoặc đối tượng) của chúng tôi sẽ xử lý các nhiệm vụ cụ thể:

  • Sự kiện - Sự kiện là đơn vị lớp cơ bản của hệ thống điều khiển sự kiện. Nó chứa một loại (như MARKET, SIGNAL, ORDER hoặc FILL) xác định cách nó sẽ được xử lý trong vòng lặp sự kiện.
  • Event Queue - The Event Queue là một đối tượng Python Queue trong bộ nhớ lưu trữ tất cả các đối tượng lớp con Event được tạo bởi phần mềm còn lại.
  • DataHandler - DataHandler là một lớp cơ sở trừu tượng (ABC) trình bày một giao diện để xử lý cả dữ liệu thị trường lịch sử hoặc trực tiếp. Điều này cung cấp sự linh hoạt đáng kể vì các mô-đun Chiến lược và Cổ phiếu có thể được sử dụng lại giữa cả hai phương pháp tiếp cận. DataHandler tạo ra một MarketEvent mới trên mỗi nhịp tim của hệ thống (xem bên dưới).
  • Chiến lược - Chiến lược cũng là một ABC trình bày một giao diện để lấy dữ liệu thị trường và tạo ra các SignalEvents tương ứng, mà cuối cùng được sử dụng bởi đối tượng Portfolio.
  • Portfolio - Đây là một ABC xử lý quản lý lệnh liên quan đến các vị trí hiện tại và tiếp theo cho một chiến lược. Nó cũng thực hiện quản lý rủi ro trên toàn danh mục đầu tư, bao gồm việc tiếp xúc với lĩnh vực và kích thước vị trí. Trong một thực hiện phức tạp hơn, điều này có thể được ủy quyền cho một lớp RiskManagement. Portfolio lấy SignalEvents từ Queue và tạo ra OrderEvents được thêm vào Queue.
  • ExecutionHandler - The ExecutionHandler mô phỏng kết nối với một công ty môi giới. Công việc của người xử lý là lấy OrderEvents từ hàng đợi và thực hiện chúng, hoặc thông qua một cách tiếp cận mô phỏng hoặc kết nối thực tế với một công ty môi giới gan. Một khi các lệnh được thực hiện, người xử lý tạo ra FillEvents, mô tả những gì thực sự được giao dịch, bao gồm phí, hoa hồng và trượt (nếu được mô hình hóa).
  • Loop - Tất cả các thành phần này được bọc trong một vòng lặp sự kiện xử lý chính xác tất cả các loại sự kiện, định tuyến chúng đến thành phần thích hợp.

Đây là một mô hình khá cơ bản của một công cụ giao dịch. Có một phạm vi mở rộng đáng kể, đặc biệt là liên quan đến cách sử dụng danh mục đầu tư. Ngoài ra, các mô hình chi phí giao dịch khác nhau cũng có thể được trừu tượng hóa vào phân cấp lớp riêng của họ. Ở giai đoạn này, nó giới thiệu sự phức tạp không cần thiết trong loạt bài viết này vì vậy chúng tôi sẽ không thảo luận thêm về nó. Trong các hướng dẫn tiếp theo, chúng tôi có thể sẽ mở rộng hệ thống để bao gồm sự thực tế bổ sung.

Dưới đây là một đoạn mã Python chứng minh cách backtester hoạt động trong thực tế. Có hai vòng lặp xảy ra trong mã. Vòng lặp bên ngoài được sử dụng để cung cấp cho backtester một nhịp tim. Đối với giao dịch trực tiếp, đây là tần suất mà dữ liệu thị trường mới được thăm dò. Đối với các chiến lược backtesting, điều này không cần thiết vì backtester sử dụng dữ liệu thị trường được cung cấp dưới dạng drip-feed (xem dòng bars.update_bars)).

Vòng lặp bên trong thực sự xử lý các sự kiện từ đối tượng Event Queue. Các sự kiện cụ thể được ủy quyền cho thành phần tương ứng và sau đó các sự kiện mới được thêm vào hàng đợi. Khi Event Queue trống, vòng lặp nhịp tim tiếp tục:

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

Đây là phác thảo cơ bản về cách thiết kế backtester dựa trên sự kiện.


Thêm nữa