Tornado (analysis.tornado)¶
Ordina gli input di qualsiasi metodo registrato in base a quanto muovono un output decimale. Ogni input varia da solo tra un valore basso e un valore alto espliciti, gli altri restano al valore del caso base, e gli input sono ordinati per escursione assoluta dell'output: sono i dati alla base di un grafico a tornado. Domanda tipica: quale ipotesi del DCF incide di più sull'equity value.
Il tornado è un metodo di analisi: valuta un altro metodo tramite il registro (vedi Metodi di analisi). Non può valutare un altro metodo di analisi.
Formula¶
Per ogni variabile \(x_k\) con valori \(\text{low}_k < \text{high}_k\), con gli altri input al valore del caso base:
L'escursione è nell'unità di misura dell'output e mantiene il segno: è negativa quando l'output diminuisce all'aumentare dell'input. A parità di escursione assoluta le variabili mantengono l'ordine in cui sono state indicate.
Esempio¶
Il DCF a cinque anni di dcf.fcff: flussi di cassa operativi disponibili di 100,
110, 120, 130 e 140 unità monetarie, tasso di attualizzazione del 9%, tasso di crescita di lungo
periodo del 2% e posizione finanziaria netta di 300. Quattro determinanti dell'equity value:
posizione finanziaria netta da 250 a 350, flusso del primo anno da 90 a 110, tasso di crescita
di lungo periodo dall'1% al 3% e tasso di attualizzazione dall'8% al 10%.
from decimal import Decimal
import fairmark as fm
base = {
"stages": [{"periods": 5, "projection": "given", "flows": [100, 110, 120, 130, 140]}],
"cash_flow_terms": "nominal",
"discount_rate_terms": "nominal",
"wacc": "0.09",
"timing": "end_of_period",
"terminal": {"tv.gordon": {"growth": "0.02", "first_flow": "last_flow_grown"}},
"net_financial_position": {"gross_debt": 300, "cash": 0},
}
t = fm.analysis.tornado(
"dcf.fcff",
base,
output="/equity_value",
variables={
"/net_financial_position/gross_debt": ("250", "350"),
"/stages/0/flows/0": ("90", "110"),
"/terminal/tv.gordon/growth": ("0.01", "0.03"),
"/wacc": ("0.08", "0.10"),
},
)
print(t.base) # 1485.9356357015730744749501235
for bar in t.ranking:
print(bar["variable"], bar["swing"])
# /wacc -536.7420151241019796250302455
# /terminal/tv.gordon/growth 413.2480397880313369436240625
# /net_financial_position/gross_debt -100.0000000000000000000000000
# /stages/0/flows/0 18.3486238532110091743119266
assert [bar["variable"] for bar in t.ranking] == [
"/wacc", "/terminal/tv.gordon/growth", "/net_financial_position/gross_debt", "/stages/0/flows/0",
]
# Escursioni attese da un calcolo indipendente a 50 cifre (scripts/reference_analysis.py).
expected = ["-536.74201512410197962503024533", "413.24803978803133694362406247",
"-100", "18.348623853211009174311926606"]
for bar, swing in zip(t.ranking, expected):
assert abs(bar["swing"] - Decimal(swing)) < Decimal("0.000000001")
assert bar["abs_swing"] == abs(bar["swing"])
# La stessa richiesta tramite il punto di accesso generico, come la invia un agente o la CLI.
same = fm.run("analysis.tornado", {
"method": "dcf.fcff",
"base_input": base,
"output": "/equity_value",
"variables": [
{"variable": "/net_financial_position/gross_debt", "low": "250", "high": "350"},
{"variable": "/stages/0/flows/0", "low": "90", "high": "110"},
{"variable": "/terminal/tv.gordon/growth", "low": "0.01", "high": "0.03"},
{"variable": "/wacc", "low": "0.08", "high": "0.10"},
],
})
assert same.ranking == t.ranking
Il tasso di attualizzazione è la variabile dominante: due punti percentuali muovono l'equity value di circa 537 unità monetarie, più di cinque volte l'effetto di 100 di posizione finanziaria netta. I passaggi del risultato elencano il caso base, poi, per ogni variabile nell'ordine indicato, l'output al valore basso, l'output al valore alto e l'escursione.
Input¶
| Campo | Significato | Default |
|---|---|---|
method |
Identificativo del metodo da valutare, ad esempio dcf.fcff; i metodi di analisi non sono ammessi |
obbligatorio |
base_input |
Input completo di quel metodo, esattamente come il metodo lo accetta: il caso base; ogni variabile cambia da sola, le altre mantengono il valore del caso base | obbligatorio |
output |
JSON Pointer RFC 6901 all'output decimale da ordinare, nel value del risultato interno, ad esempio /equity_value |
obbligatorio |
variables |
Gli input da far variare; almeno uno; i puntatori non si ripetono | obbligatorio |
variables[].variable |
JSON Pointer RFC 6901 a un input decimale in base_input, ad esempio /discount_rate |
obbligatorio |
variables[].low |
Valore basso dell'input, nella sua unità di misura, inferiore a high |
obbligatorio |
variables[].high |
Valore alto dell'input, nella sua unità di misura | obbligatorio |
I valori basso e alto sono valori espliciti, non variazioni percentuali: "±10% su un tasso del
9%" potrebbe significare dall'8,1% al 9,9% oppure dall'8% al 10%, quindi è il chiamante a
indicare i valori. In Python, variables associa a ogni puntatore la coppia (low, high);
l'ordine è mantenuto.
Output¶
| Campo | Significato |
|---|---|
inner_method |
id e version del metodo valutato, nella versione eseguita da questo motore |
output |
Puntatore dell'output ordinato |
base |
Output per base_input senza modifiche, nell'unità di misura dell'output |
ranking |
Le barre per abs_swing decrescente; a parità di escursione è mantenuto l'ordine di variables |
ranking[].variable, low, high |
L'input fatto variare e i suoi due valori, come indicati |
ranking[].output_low, output_high |
L'output con l'input a low e a high, nell'unità di misura dell'output |
ranking[].swing |
output_high − output_low, nell'unità di misura dell'output; negativo quando l'output diminuisce all'aumentare dell'input |
ranking[].abs_swing |
\(\lvert\)swing\(\rvert\): la lunghezza della barra e il criterio di ordinamento |
Convenzioni (esplicite, riportate in explain.conventions)¶
| Convenzione | Valore | Default |
|---|---|---|
| Metodo interno | identificativo e versione, valutato tramite il registro | obbligatorio |
| Output | puntatore misurato dalle barre | obbligatorio |
| Variazione | una variabile alla volta; gli altri input mantengono il valore del caso base | fisso |
| Valori di variazione | valori basso e alto espliciti; nessuna variazione percentuale | obbligatorio |
| Ordine di valutazione | caso base, poi ogni variabile nell'ordine indicato, il valore basso prima di quello alto | fisso |
| Ordinamento | per escursione assoluta, dalla maggiore; a parità di escursione è mantenuto l'ordine indicato | fisso |
| Valutazioni in errore | qualsiasi valutazione rifiutata dal metodo interno interrompe la richiesta con il relativo errore | fisso |
| Codifica delle variabili | scritte nell'input del metodo interno come stringhe decimali | fissa |
L'ultima convenzione riportata è il numero di valutazioni del metodo interno: due per variabile più il caso base.
Errori¶
| Codice | Quando |
|---|---|
FM-E001 |
variables è vuoto, una variabile si ripete, low ≥ high, oppure un campo è mancante o sconosciuto |
FM-E007 |
method non corrisponde ad alcun metodo registrato |
FM-E015 |
Una variabile non si risolve in un decimale in base_input, oppure output non si risolve in un decimale nel risultato del caso base; il messaggio indica il puntatore |
FM-E016 |
Due valutazioni per variabile più il caso base superano 10.000 oppure valutazioni × valori in base_input superano 1.000.000; restituito prima della prima valutazione |
FM-E017 |
method indica un metodo di analisi |
I puntatori delle variabili, i valori basso e alto e la sintassi di output sono verificati
prima di qualsiasi valutazione; output è risolto sul caso base. A differenza delle tabelle di
sensitività e dell'analisi di scenario, un errore del metodo interno in qualsiasi valutazione,
compreso il caso base, interrompe la richiesta ed è restituito invariato con il proprio codice:
un ordinamento con una barra mancante non è definito. Ad esempio, un valore alto del tasso di
crescita di lungo periodo pari o superiore al tasso di attualizzazione di dcf.fcff restituisce
FM-E004; scegli valori basso e alto per i quali il metodo interno sia valido.
Precisione¶
Gli output sono precisi quanto il metodo interno (vedi la relativa pagina); le escursioni sono differenze esatte degli output riportati. I casi di riferimento sono verificati entro \(10^{-9}\) nell'unità di misura dell'output; i casi di parità sono verificati in modo esatto.
Verificato con¶
- Casi di riferimento (golden case) in
tests/golden/analysis.tornado/: quattro determinanti del DCF, i cui ordinamento ed escursioni attesi provengono dascripts/reference_analysis.py(dcf.fcffricostruito dalla formula documentata con il modulodecimaldi Python a 50 cifre; non condivide codice con il motore), e un caso di parità costruito sutvm.npvcon tasso 0, in cui una variazione di ±1 su uno qualsiasi di due flussi di cassa muove il VAN di 2 e l'ordine di dichiarazione è mantenuto.