파이썬으로 이벤트 기반 백테스팅 - 1부

저자:선함, 창작: 2019-03-22 11:53:50, 업데이트:

우리는 지난 몇 달 동안 퀀트 스타트 (QuantStart) 에서 파이썬과 판다를 사용하여 다양한 거래 전략을 백테스트했습니다. 판다의 벡터화 된 성격은 큰 데이터 세트에 대한 특정 작업이 매우 빠르다는 것을 보장합니다. 그러나 지금까지 연구한 벡터화 백테스터의 형태는 거래 실행 시뮬레이션 방식에 몇 가지 단점이 있습니다. 이 시리즈의 기사에서는 파이썬을 사용하여 이벤트 기반 백테스트 환경을 구축하여 역사적 전략 시뮬레이션에 대한 더 현실적인 접근 방식을 논의 할 것입니다.

이벤트 주도 소프트웨어

이러한 백테스터의 개발에 깊이 들어가기 전에 우리는 이벤트 기반 시스템의 개념을 이해해야합니다. 비디오 게임은 이벤트 기반 소프트웨어에 대한 자연스러운 사용 사례를 제공하고 탐구하기 쉬운 예를 제공합니다. 비디오 게임은 많은 구성 요소를 가지고 있으며 높은 프레임 레이트에서 실시간 설정에서 상호 작용합니다. 이것은 이벤트 루프 또는 게임 루프로 알려진 무한 루프 내에서 전체 계산 세트를 실행함으로써 처리됩니다.

게임 루프의 각 틱에서 함수가 최신 이벤트를 수신하도록 호출되며, 이는 게임 내에서 해당되는 이전 동작에 의해 생성됩니다. 키 누르기 또는 마우스 클릭을 포함 할 수있는 이벤트의 성격에 따라 다음 동작이 수행되며, 루프를 종료하거나 추가 이벤트를 생성합니다. 프로세스는 계속됩니다. 다음은 몇 가지 예시 사이비 코드입니다:

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

코드는 지속적으로 새로운 이벤트를 확인하고 그 다음이 이벤트에 기반한 행동을 수행합니다. 특히 코드가 지속적으로 루프되고 이벤트를 확인하기 때문에 실시간 응답 처리 착각을 허용합니다. 분명히 알 수 있듯이 이것은 고주파 거래 시뮬레이션을 수행하는 데 필요한 것입니다.

왜 사건 의 원동력 을 이용 한 역시험?

이벤트 기반 시스템은 벡터화된 접근 방식에 비해 많은 장점을 제공합니다.

  • 코드 재사용 - 이벤트 기반 백테스터는 구성 요소의 최소한의 전환과 함께 역사 백테스팅 및 라이브 트레이딩에 모두 사용할 수 있습니다. 통계 분석을 수행하기 위해 모든 데이터가 한꺼번에 사용되어야하는 벡터화 백테스터의 경우는 그렇지 않습니다.
  • 루카헤드 편향 - 이벤트에 의한 백테스터에서는 시장 데이터 수신이 행동해야 할 이벤트로 취급되므로 이벤트에 의한 백테스터와 시장 데이터를 "드립 피드"하여 주문 관리 및 포트폴리오 시스템이 어떻게 행동하는지 복제 할 수 있습니다.
  • 사실주의 - 이벤트 기반 백테스터는 주문 실행 방식과 거래 비용이 발생하는 방법에 대한 상당한 사용자 정의를 허용합니다. 사용자 지정 교환 핸들러가 구축 될 수 있기 때문에 기본 시장 및 제한 주문, 또한 시장 개방 (MOO) 및 시장 폐쇄 (MOC) 를 처리하는 것이 간단합니다.

이벤트 구동 시스템에는 많은 이점이 있지만, 단순 벡터화 시스템보다 두 가지 주요 단점이 있습니다. 첫째, 구현 및 테스트가 훨씬 복잡합니다. 더 많은 움직이는 부분이 있으며 버그의 도입 가능성이 높습니다. 테스트 구동 개발과 같은 적절한 소프트웨어 테스트 방법론을 완화하기 위해 고용 할 수 있습니다.

두 번째로, 그들은 벡터화된 시스템에 비해 실행이 느립니다. 최적의 벡터화된 연산은 수학적 계산을 수행할 때 활용할 수 없습니다. 우리는 나중에 이 한계를 극복하는 방법을 논의할 것입니다.

이벤트 기반 백테스터 개요

백테스팅 시스템에 이벤트 기반 접근을 적용하려면 특정 작업을 처리하는 구성 요소 (또는 객체) 를 정의해야합니다.

  • 이벤트 - 이벤트는 이벤트 기반 시스템의 기본 클래스 단위입니다. 이벤트 루프 내에서 처리되는 방법을 결정하는 타입 (MARKET, SIGNAL, ORDER 또는 FILL) 을 포함합니다.
  • 이벤트 큐 (Event Queue) - 이벤트 큐 (Event Queue) 는 메모리 내의 파이썬 큐 객체로 소프트웨어의 나머지 부분에서 생성되는 모든 이벤트 하위 클래스 객체를 저장합니다.
  • 데이터 핸들러 - 데이터 핸들러는 역사적인 데이터 또는 실시간 시장 데이터를 처리하는 인터페이스를 제공하는 추상 기본 클래스 (ABC) 이다. 이는 전략 및 포트폴리오 모듈이 두 가지 접근 방식 사이에서 재사용 될 수 있으므로 상당한 유연성을 제공합니다. 데이터 핸들러는 시스템의 모든 심장 박동에 새로운 시장 이벤트를 생성합니다.
  • 전략 - 전략은 또한 시장 데이터를 채취하고 해당 신호 이벤트를 생성하는 인터페이스를 제공하는 ABC입니다. 이는 궁극적으로 포트폴리오 객체에 의해 사용됩니다. 신호 이벤트에는 틱어 기호, 방향 (LONG 또는 SHORT) 및 시간표가 포함되어 있습니다.
  • 포트폴리오 - 이는 전략의 현재 및 후속 포지션과 관련된 주문 관리를 처리하는 ABC입니다. 또한 부문 노출 및 포지션 사이징을 포함하여 포트폴리오 전체에서 리스크 관리를 수행합니다. 더 정교한 구현에서 이것은 리스크 매니지먼트 클래스에 위임 될 수 있습니다. 포트폴리오는 큐에서 신호 이벤트를 받아 큐에 추가되는 OrderEvents를 생성합니다.
  • ExecutionHandler - ExecutionHandler는 중개회사와의 연결을 시뮬레이션합니다. 처리자의 임무는 시뮬레이션 접근법 또는 간 중개회사와의 실제 연결을 통해 대기열에서 OrderEvents를 받아 실행하는 것입니다. 주문이 실행되면 처리자는 수수료, 수수료 및 미끄러짐 (모델링된 경우) 을 포함하여 실제로 거래 된 것을 설명하는 FillEvents를 만듭니다.
  • 루프 - 이 모든 구성 요소는 모든 이벤트 유형을 올바르게 처리하고 적절한 구성 요소로 라우팅하는 이벤트 루프에 싸여 있습니다.

이 모델은 트레이딩 엔진의 매우 기본적인 모델이다. 특히 포트폴리오의 사용 방식에 있어서 확장할 수 있는 상당한 범위가 있다. 또한 다른 트랜잭션 비용 모델도 그들 자신의 클래스 계층에 추상화될 수 있다. 이 단계에서 이 시리즈의 기사 내에서 불필요한 복잡성을 도입하기 때문에 현재는 더 이상 논의하지 않을 것이다. 후기 튜토리얼에서는 추가적인 사실주의를 포함하도록 시스템을 확장할 가능성이 있다.

다음은 백테스터가 실제로 어떻게 작동하는지 보여주는 파이썬 코드의 단편입니다. 코드에 두 개의 루프가 발생합니다. 외부 루프는 백테스터에게 심장 박동을 주기 위해 사용됩니다. 라이브 트레이딩에서는 새로운 시장 데이터가 조사되는 빈도입니다. 백테스팅 전략에서는 백테스터가 방울 피드 형태로 제공되는 시장 데이터를 사용하기 때문에 이것이 엄격히 필요하지 않습니다.

내부 루프는 실제로 이벤트 큐 객체에서 이벤트를 처리합니다. 특정 이벤트는 해당 구성 요소에 위임되고 그 후 새로운 이벤트가 큐에 추가됩니다. 이벤트 큐가 비어있을 때 심장 박동 루프는 계속됩니다:

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

이것은 이벤트 주도 백테스터가 설계되는 기본적인 윤곽입니다. 다음 기사에서는 이벤트 클래스 계층에 대해 논의 할 것입니다.


더 많은