trading-algoritmico-DataInvestor-parte8

DataInvestor – La simulazione delle Commissioni

Sommario

Nel precedente articolo di questa serie abbiamo descritto la gerarchia di classi Asset che implementa  la base degli strumenti che si possono negoziare all’interno del framework di backtesting DataInvestor.

In questo articolo iniziamo ad esaminare le classi che implementano il componente di broker simulato del framework. Questo componente di DataInvestor modella un broker e il suo compito consiste nel tenere traccia di tutta la contabilità associata alle transazioni di asset. È anche responsabile di tenere traccia delle remunerazioni societarie conseguenti al possesso materiale dei titoli azionari, come esborsi in contanti o dividendi su azioni ordinarie.

Un altro vantaggio dell’implementazione di questa logica all’interno di una classe consiste, se l’interfaccia è definita in modo appropriato, nella possibilità di sostituire il componente di broker simulato con un componente di trading dal vivo, senza la necessità di modificare alcuna delle restanti logiche di quant trading. In questo modo l’implementazione dell’algoritmo della strategia di trading non ha bisogno di modifiche tra il backtesting e il trading live.

Il broker è un componente complesso e per funzionare si basa su vari sottocomponenti, cioè una gerarchia di classi FeeModel, un componente Portfolio e un componente Transaction. In questo articolo ci concentriamo sulla classe FeeModel.

Costi di transazione

Uno degli aspetti più importanti nella simulazione di una strategia di trading sistematico è modellare e tenere traccia dei costi di transazione. Questi costi possono assumere molte forme, comprese le commissioni e le tasse di intermediazione, nonché i costi di negoziazione indiretti come lo slippage e l’impatto sul mercato. Modellare questi aspetti è un argomento  complesso e sarà oggetto di articoli futuri. In questo articolo esaminiamo le commissioni e le tasse di intermediazione, entrambe modellabili utilizzando la gerarchia delle classi FeeModel.

Il FeeModel è progettato per implementare la struttura dei costi di un broker utilizzato per il trading. Questi possono diventare piuttosto complessi a seconda dell’area geografica, della classe di asset, del volume scambiato e del periodo temporale considerato per la simulazione del trading .

Oltre alle commissioni del broker, devono essere prese in considerazioni le imposte e le tasse che dipendono dalle arre geografiche e dal tipo di broker utilizzato.

Nella sua forma attuale, DataInvestor supporta due semplici modelli commissionali. Il primo è il ZeroFeeModel, che semplicemente non applica alcuna commissione o tassa alle transazioni. E’ utile per creare una simulazione di base che può essere confrontata con altri modelli commissionali più realistici. Il secondo è PercentFeeModel, che applica una commissione percentuale e/o una tassazione percentuale all’importo negoziato (noto come “corrispettivo”). Descriviamo i dettagli di entrambi questi modelli.

FeeModel

FeeModel è la classe base astratta da cui ereditano tutti le sottoclassi derivate di modelli commissionali. Fornisce tre specifiche di metodi astratti: _calc_commission_calc_taxcalc_total_cost. Il carattere di sottolineatura che precede i primi due metodi indica che sono metodi pseudo-privati ​​con funzionalità necessarie alla classe stessa. L’ultimo metodo è il metodo dell’interfaccia pseudo-pubblica e viene utilizzato per calcolare il costo totale della transazione a partire dal corrispettivo negoziato.

Si noti che il linguaggio Python non ha il concetto di metodi privati ​​o pubblici, a differenza di C++ o Java. In Python il singolo carattere di sottolineatura che precede un metodo è progettato per indicare agli altri client dei moduli quali metodi dovrebbero essere chiamati come interfaccia (nessun carattere di sottolineatura), mentre quali sono metodi specifici dell’implementazione (preceduti da un carattere di sottolineatura).

I metodi _calc_commission_calc_tax sono separati per consentire il calcolo separato per ciascun tipo di costo. Questo sarebbe il caso, ad esempio, quando può essere utilizzata una commissione “decrescente”, ma l’imposta stessa potrebbe essere una  specifica percentuale fissa  indipendentemente dall’importo del corrispettivo negoziato.

Il metodo calc_total_cost ha tre argomenti obbligatori e un argomento facoltativo. I primi tre sono assetquantityconsideration. L’asset è necessario dato che diverse classi di asset possono avere diverse valori di commissione. Il parametro consideration è il prezzo  unitario dell’asset moltiplicato per la quantità e fornisce un’idea di ‘importo negoziato’ in valuta, indipendentemente dalla classe di asset.

Sebbene possa sembrare che siano necessari solo i parametri dell’asset e del corrispettivo (poiché la commissione è generalmente calcolata sull’importo negoziato), ci sono alcuni broker possono basare la loro commissione sulla quantità scambiata di un asset (ovvero, non necessariamente sul suo prezzo) piuttosto rispetto al suo “valore in dollari”. Per questo motivo è stato incluso nel design di DataInvestor in modo da implementare questo tipo di  logiche.

Il parametro finale è un handle per l’istanza del broker (simulata o attiva). Questo è necessario per ottenere ulteriori informazioni che potrebbero essere rilevanti per quanto riguarda il calcolo della commissione. Un esempio potrebbe essere quello di ottenere la data  di effettiva della negoziazione, poiché i broker cambiano la loro struttura di commissioni nel tempo e quindi per modellarla realisticamente nel tempo sarebbe necessario conoscere la data effettiva della negoziazione.

Di seguito il codice per FeeModel. È un’implementazione abbastanza semplice della classe base astratta:

				
					from abc import ABCMeta, abstractmethod


class FeeModel(object):
    """
    Classe astratta per gestire il calcolo delle
    commissioni del broker e delle tasse.
    """

    __metaclass__ = ABCMeta

    @abstractmethod
    def _calc_commission(self, asset, quantity, consideration, broker=None):
        raise NotImplementedError(
            "Should implement _calc_commission()"
        )

    @abstractmethod
    def _calc_tax(self, asset, quantity, consideration, broker=None):
        raise NotImplementedError(
            "Should implement _calc_tax()"
        )

    @abstractmethod
    def calc_total_cost(self, asset, quantity, consideration, broker=None):
        raise NotImplementedError(
            "Should implement calc_total_cost()"
        )

				
			

ZeroFeeModel

La prima classe che implementa questa interfaccia astratta è la sottoclasse derivata ZeroFeeModel. È una classe estremamente semplice che restituisce 0.0 per entrambi _calc_tax e _calc_commission. Questi due valori vengono quindi aggiunti al calc_total_cost per produrre 0.0 come costo totale dell’operazione.

Perché una classe del genere dovrebbe essere utilizzata in una simulazione reale? Il motivo principale è permettere il confronto tra vari modelli di commissioni all’interno di una simulazione di backtest. L’utilizzo di ZeroFeeModel permette a un ricercatore quantitativo di analizzare i risultati del backtest senza costi e confrontarlo con vari modelli di commissioni di intermediazione e tasse. In questo modo è possibile  verificare se una strategia rimane redditizia anche dopo aver applicato i costi di transazione.

Il codice della classe ZeroFeeModel è riportato di seguito. La maggior parte delle righe di codice per questo modello sono docstring. L’implementazione effettiva è minima:

				
					from datainvestor.broker.fee_model.fee_model import FeeModel


class ZeroFeeModel(FeeModel):
    """
    Una sottoclasse di FeeModel che produce nessuna commisione o
    tasse. Questo è il modello default delle commission per
    simulare il brokerages con datainvestor.
    """

    def _calc_commission(self, asset, quantity, consideration, broker=None):
        """
        Returns zero commission.

        Parameters
        ----------
        asset : `str`
            Stringa del simbolo dell'asset.
        quantity : `int`
            La quantità di asset (necessaria per i calcoli
            in stile InteractiveBrokers).
        consideration : `float`
            Prezzo moltiplicato per quantità dell'ordine.
        broker : `Broker`, optional
            Riferimento ad un broker (opzionale).

        Returns
        -------
        `float`
            Le commissione a costo zero.
        """
        return 0.0

    def _calc_tax(self, asset, quantity, consideration, broker=None):
        """
        Restituisce le tasse a zero.

        Parameters
        ----------
        asset : `str`
            Stringa del simbolo dell'asset.
        quantity : `int`
            La quantità di asset (necessaria per i calcoli
            in stile InteractiveBrokers).
        consideration : `float`
            Prezzo moltiplicato per quantità dell'ordine.
        broker : `Broker`, optional
            Riferimento ad un broker (opzionale).

        Returns
        -------
        `float`
            Le tasse a costo zero.
        """
        return 0.0

    def calc_total_cost(self, asset, quantity, consideration, broker=None):
        """
        Calcola il totale di qualsiasi commissione e/o tassa
        per il trade della dimensione 'corrispettiva'.

        Parameters
        ----------
        asset : `str`
            Stringa del simbolo dell'asset.
        quantity : `int`
            La quantità di asset (necessaria per i calcoli
            in stile InteractiveBrokers).
        consideration : `float`
            Prezzo moltiplicato per quantità dell'ordine.
        broker : `Broker`, optional
            Riferimento ad un broker (opzionale).

        Returns
        -------
        `float`
            Totale delle commission e tasse a costo zero.
        """
        commission = self._calc_commission(asset, quantity, consideration, broker)
        tax = self._calc_tax(asset, quantity, consideration, broker)
        return commission + tax

				
			

PercentFeeModel

Un modello leggermente più realistico dei costi di transazione è fornito dalla classe PercentFeeModel. Questa classe prevede un  metodo __init__ di inizializzazione con due parametri. Il primo è il commission_pct, che corrisponde alla commissione a percentuale fissa del broker da applicare al corrispettivo. Il secondo è tax_pct, che è la percentuale fissa di tassazione da applicare al corrispettivo. Entrambi questi valori sono specificati nell’intervallo [0.0, 1.0]. Cioè, 1.0 equivale a 100%. Entrambi i metodi si limitano ad applicare tale percentuale al valore assoluto del corrispettivo. Questo evita commissioni negative quando si vendono asset! Ad esempio, il calcolo per la commissione del broker è simile a: return self.commission_pct * abs(consideration). L’implementazione è quasi identica per il metodo della tassazione. Il codice della classe PercentFeeModel è riportato di seguito. Come per ZeroFeeModel la maggior parte delle righe di codice per questo modello sono docstring. L’implementazione effettiva è minima:
				
					from datainvestor.broker.fee_model.fee_model import FeeModel


class PercentFeeModel(FeeModel):
    """
    Una sottoclasse FeeModel che produce un costo percentuale
    per tasse e commissioni.

    Parameters
    ----------
    commission_pct : `float`, optional
        La commissione percentuale applicata al corrispettivo.
        0-100% è nell'intervallo [0,0, 1,0]. Quindi, ad es. 0,1% è 0,001
    tax_pct : `float`, optional
        L'imposta percentuale applicata al corrispettivo.
        0-100% è nell'intervallo [0,0, 1,0]. Quindi, ad es. 0,1% è 0,001
    """

    def __init__(self, commission_pct=0.0, tax_pct=0.0):
        super().__init__()
        self.commission_pct = commission_pct
        self.tax_pct = tax_pct

    def _calc_commission(self, asset, quantity, consideration, broker=None):
        """
        Restituisce la commissione percentuale dal corrispettivo.

        Parameters
        ----------
        asset : `str`
            Stringa del simbolo dell'asset.
        quantity : `int`
            La quantità di asset (necessaria per i calcoli
            in stile InteractiveBrokers).
        consideration : `float`
            Prezzo moltiplicato per quantità dell'ordine.
        broker : `Broker`, optional
            Riferimento ad un broker (opzionale).

        Returns
        -------
        `float`
            La percentuale di commssione.
        """
        return self.commission_pct * abs(consideration)

    def _calc_tax(self, asset, quantity, consideration, broker=None):
        """
        Restituisce la tassa percentuale dal corrispettivo.

        Parameters
        ----------
        asset : `str`
            Stringa del simbolo dell'asset.
        quantity : `int`
            La quantità di asset (necessaria per i calcoli
            in stile InteractiveBrokers).
        consideration : `float`
            Prezzo moltiplicato per quantità dell'ordine.
        broker : `Broker`, optional
            Riferimento ad un broker (opzionale).

        Returns
        -------
        `float`
            La percentuale di tasse.
        """
        return self.tax_pct * abs(consideration)

    def calc_total_cost(self, asset, quantity, consideration, broker=None):
        """
        Calcola il totale di qualsiasi commissione e/o tassa
        per il trade della dimensione 'corrispettiva'.

        Parameters
        ----------
        asset : `str`
            Stringa del simbolo dell'asset.
        quantity : `int`
            La quantità di asset (necessaria per i calcoli
            in stile InteractiveBrokers).
        consideration : `float`
            Prezzo moltiplicato per quantità dell'ordine.
        broker : `Broker`, optional
            Riferimento ad un broker (opzionale).

        Returns
        -------
        `float`
            Totale di commissioni e tasse.
        """
        commission = self._calc_commission(asset, quantity, consideration, broker)
        tax = self._calc_tax(asset, quantity, consideration, broker)
        return commission + tax

				
			

Notiamo che questa classe, per come è implementata, non consente una percentuale “scala mobile” basata sul valore del corrispettivo. Nella maggior parte dei broker del mondo reale si applicherebbero percentuali diverse all’aumentare del corrispettivo. Questa scala mobile  può variare anche in base alla classe di asset e alla regione geografica.

Questa logica è solitamente altamente specifica per ogni diverso broker, quindi tentare di creare tutte le possibili strutture commissionali dei broker sarebbe un compito molto impegnativo. Tuttavia, la logica della classe è abbastanza generica da supportare un’ampia gamma di strutture dei costi di intermediazione. Gli articoli futuri riguarderanno l’implementazione di costi di trading più realistici utilizzando la stessa gerarchia di classi di cui sopra.

Nota: tutto il codice per questi modelli commissionali sono disponibili nella relativa sezione del codice sorgente di DataInvestor su GitHub.

Benvenuto su DataTrading!

Sono Gianluca, ingegnere software e data scientist. Sono appassionato di coding, finanza e trading. Leggi la mia storia.

Ho creato DataTrading per aiutare le altre persone ad utilizzare nuovi approcci e nuovi strumenti, ed applicarli correttamente al mondo del trading.

DataTrading vuole essere un punto di ritrovo per scambiare esperienze, opinioni ed idee.

SCRIVIMI SU TELEGRAM

Per informazioni, suggerimenti, collaborazioni...

Torna in alto
Scroll to Top