Costruire una Strategia – Creare un Trading System Parte 4

costruire una strategia di trading algoritmico

In questo corso dedicato alla creazione di un trading system event-driven abbiamo già approfondito l’architettura degli event-loop, la struttura della classe Event e il modulo per la gestione dei dati di mercato. In questa lezione vedremo come costruire una strategia efficace per un trading system event-driven. A questo scopo, implementeremo la gerarchia della classe Strategy. Gli oggetti Strategy analizzano i dati di mercato e generano eventi di tipo Signal Trading, fondamentali per l’operatività del sistema.

Ogni oggetto Strategy effettua i calcoli necessari sui dati di mercato per fornire segnali advisory al componente Portfolio. In questa fase dello sviluppo del sistema di backtesting event-driven, non includiamo indicatori o filtri tecnici. Tuttavia, questi strumenti rappresentano ottime soluzioni per costruire una strategia più sofisticata, anche se esulano dallo scopo di questa specifica lezione.

La classe Strategy

Dobbiamo implementare la gerarchia della classe Strategy. Definiamo una classe base astratta che include un singolo metodo puro virtuale, il quale si occupa di generare gli oggetti SignalEvent. Per strutturare correttamente la gerarchia della strategia, importiamo le librerie NumPy e Pandas, l’oggetto Queue, gli strumenti della classe base astratta e il modulo SignalEvent:

				
					# strategy.py

import datetime
import numpy as np
import pandas as pd
import queue

from abc import ABCMeta, abstractmethod

from event import SignalEvent
				
			

La classe base astratta Strategy definisce semplicemente il metodo virtuale calculate_signals. Usiamo questo metodo nelle classi derivate per gestire la creazione di oggetti SignalEvent a seconda degli aggiornamenti dei dati di mercato:

				
					# strategy.py


class Strategy(object):
    """
    Strategy è una classe base astratta che fornisce un'interfaccia per
    tutti i successivi oggetti (ereditati) di gestione della strategia.

    L'obiettivo di un oggetto (derivato da) Strategy è generare un oggetto
    Signal per specifici simboli basati sugli input di Bars
    (OLHCVI) generati da un oggetto DataHandler.

    Questo è progettato per funzionare sia con dati storici che in tempo reale
    quindi l'oggetto Strategy è agnostico rispetto all'origine dati,
    poiché ricava le tuple di barre da un oggetto Queue (coda).
    """

    __metaclass__ = ABCMeta

    @abstractmethod
    def calculate_signals(self):
        """
        Fornisce il meccanismo per calcolare la lista di segnali.
        """
        raise NotImplementedError("Should implement calculate_signals()")
				
			

Come mostrato nel codice precedente, la definizione della classe astratta Strategy è semplice.

Costruire una strategia

Un primo esempio di come costruire una strategia implementando una sottoclasse dell’oggetto Strategy è la classe BuyAndHoldStrategy, che implementa la classica strategia buy and hold. Questa strategia compra un asset ad una certo istante e lo conserva all’interno del portafoglio. La strategia genera un solo segnale per ogni asset.

Il costruttore (__init__) prevede, come input, il gestore dei dati di mercato e l’oggetto della coda degli eventi Events:

				
					# strategy.py

class BuyAndHoldStrategy(Strategy):
    """
    Questa è una strategia estremamente semplice che va LONG su tutti
    i simboli non appena viene ricevuta una barra. Non uscirà mai da una posizione.

    Viene utilizzato principalmente come meccanismo di test per la classe Strategy
    nonché un benchmark con cui confrontare le altre strategie.
    """

    def __init__(self, bars, events):
        """
        Inizializza la strategia "buy and hold".

        Parametri:
        bars - L'oggetto DataHandler che fornisce le informazioni sui prezzi
        events - L'oggetto Event Queue (coda di eventi).
        """
        self.bars = bars
        self.symbol_list = self.bars.symbol_list
        self.events = events

        # Quando il segnale "buy & hold" viene inviato, questi sono impostati a True
        self.bought = self._calculate_initial_bought()
				
			

Nell’inizializzazione di BuyAndHoldStrategy, istanziamo l’attributo bought con un dictionary (una struttura dati nativa di Python) di chiavi per ogni simbolo, tutte impostate con False. Quando apriamo una posizione “long” su un asset, la relativa chiave viene impostata su True. In questo modo la strategia “conosce” su quali asset è investita o meno:

            # strategy.py

     def _calculate_initial_bought(self):
        """
        Aggiunge le chiavi di tutti i simboli al dizionario "bought"
        e li imposta su False.
        """
        bought = {}
        for s in self.symbol_list:
            bought[s] = False
        return bought
        

In questa classe implementiamo il metodo virtuale calculate_signals. Il metodo analizza tutti i simboli dell’elenco e recupera i dati OLHCV più recenti dal gestore dei dati di mercato. Verifica quindi se abbiamo già acquistato quel simbolo (cioè se possediamo una posizione aperta a mercato per quel titolo) e, se non lo abbiamo fatto, genera un singolo oggetto SignalEvent. Infine, inserisce l’evento nella coda e aggiorna correttamente il dizionario bought con True per quel simbolo specifico:

				
					# strategy.py

def calculate_signals(self, event):
    """
    For "Buy and Hold" generiamo un singolo segnale per simbolo
    e quindi nessun segnale aggiuntivo. Ciò significa che siamo
    costantemente LONG sul mercato a partire dalla data di
    inizializzazione della strategia.

    Parametri
    event - Un oggetto MarketEvent.
    """
    if event.type == 'MARKET':
        for s in self.symbol_list:
            bars = self.bars.get_latest_bars(s, N=1)
            if bars is not None and bars != []:
                if self.bought[s] == False:
                    # (Symbol, Datetime, Type = LONG, SHORT or EXIT)
                    signal = SignalEvent(bars[0][0], bars[0][1], 'LONG')
                    self.events.put(signal)
                    self.bought[s] = True
				
			

Conclusione

Questa strategia semplice mostra chiaramente come funziona una gerarchia strutturata sugli eventi. Nelle prossime lezioni analizzeremo strategie più avanzate come il pairs trading.

Infine, nella lezione successiva spiegheremo come costruire la gerarchia della classe Portfolio per monitorare le posizioni insieme a profitti e perdite (“PnL”).

Il codice completo presentato in questa lezione, basato sul sistema di trading event-driven TQTradingSystem, è disponibile nel seguente repository GitHub: https://github.com/tradingquant-it/TQTradingSystem.”

Torna in alto