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.
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
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.
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:
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.
Để á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ể:
Đâ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.