Tabella di sensitività (analysis.sensitivity)¶
Riporta un output decimale di qualsiasi metodo registrato al variare di un input (tabella a una variabile) o di due input (tabella a due variabili): la tabella dati dei modelli costruiti su foglio di calcolo. Domanda tipica: come varia l'enterprise value del DCF su una griglia di tassi di attualizzazione e di tassi di crescita di lungo periodo. Una combinazione che il metodo interno rifiuta, come una crescita non inferiore al tasso di attualizzazione, è registrata nella propria cella e il resto della tabella è comunque restituito.
La tabella di sensitività è un metodo di analisi: valuta un altro metodo tramite il registro (vedi Metodi di analisi). Non può valutare un altro metodo di analisi.
Formula¶
Dati il metodo interno \(M\), la variabile di riga \(x_r\) con valori \(r_1, \dots, r_m\), la variabile di colonna facoltativa \(x_c\) con valori \(c_1, \dots, c_n\) e l'output \(y\), ogni cella è
Tutti gli altri input mantengono il valore del caso base. Una tabella a una variabile non ha
variabile di colonna e ha una sola colonna (\(n = 1\)). Il caso base, \(y(M(\text{base input}))\),
è valutato per primo e riportato come base.
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. La tabella riporta l'enterprise value per
tassi di attualizzazione dell'8%, del 9% e del 10% (righe) e tassi di crescita di lungo periodo
dell'1%, del 2% e dell'8% (colonne).
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},
}
table = fm.analysis.sensitivity(
"dcf.fcff",
base,
output="/enterprise_value",
rows=("/wacc", ["0.08", "0.09", "0.10"]),
columns=("/terminal/tv.gordon/growth", ["0.01", "0.02", "0.08"]),
)
print(table.base) # 1785.9356357015730744749501235
print(table.cells[0][0]["value"]) # 1847.7733182046548911356105383, tasso 8%, crescita 1%
print(table.cells[0][2]["error"]) # crescita 8% non inferiore al tasso 8%: FM-E004
print(table.failed_cells) # 1
# Valori attesi da un calcolo indipendente a 50 cifre (scripts/reference_analysis.py).
expected = {
(0, 0): "1847.7733182046548911356105381406",
(1, 1): "1785.9356357015730744749501235001",
(2, 2): "5141.8618946793251827060993101564",
}
for (i, j), value in expected.items():
assert abs(table.cells[i][j]["value"] - Decimal(value)) < Decimal("0.000000001")
assert table.cells[0][2]["error"]["code"] == "FM-E004"
assert table.cells[1][1]["value"] == table.base
# Una tabella a una variabile è uguale alla colonna corrispondente della tabella a due variabili.
one_way = fm.analysis.sensitivity(
"dcf.fcff",
base,
output="/enterprise_value",
rows=("/wacc", ["0.08", "0.09", "0.10"]),
)
assert [row[0] for row in one_way.cells] == [row[1] for row in table.cells]
# La stessa richiesta tramite il punto di accesso generico, come la invia un agente o la CLI.
same = fm.run("analysis.sensitivity", {
"method": "dcf.fcff",
"base_input": base,
"output": "/enterprise_value",
"rows": {"variable": "/wacc", "values": ["0.08", "0.09", "0.10"]},
"columns": {"variable": "/terminal/tv.gordon/growth", "values": ["0.01", "0.02", "0.08"]},
})
assert same.cells == table.cells
La cella all'8% e all'8% contiene FM-E004, perché un valore terminale di Gordon richiede una
crescita inferiore al tasso di attualizzazione; le altre otto celle contengono valori. I
passaggi del risultato elencano il caso base, poi un passaggio per ogni cella valutata con
successo, con etichetta Cell (i, j) (numerazione da 1) ed entrambe le variabili sostituite.
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 | obbligatorio |
output |
JSON Pointer RFC 6901 all'output decimale riportato in ogni cella, nel value del risultato interno, ad esempio /enterprise_value |
obbligatorio |
rows.variable |
JSON Pointer RFC 6901 all'input decimale che varia lungo le righe, in base_input, ad esempio /discount_rate |
obbligatorio |
rows.values |
Valori della variabile di riga, nella sua unità di misura, nell'ordine della tabella; almeno uno | obbligatorio |
columns.variable |
Puntatore a un secondo input decimale, diverso dalla variabile di riga | assente: tabella a una variabile |
columns.values |
Valori della variabile di colonna, nella sua unità di misura, nell'ordine della tabella; almeno uno | obbligatorio con columns |
I valori sono nell'unità di misura della variabile: frazione decimale per un tasso ("0.08"
corrisponde all'8%), unità monetarie per un importo. In Python, rows e columns sono coppie
(pointer, values).
Output¶
| Campo | Significato |
|---|---|
inner_method |
id e version del metodo valutato, nella versione eseguita da questo motore |
output |
Puntatore dell'output riportato in ogni cella |
base |
Output per base_input senza modifiche, nell'unità di misura dell'output |
rows |
Asse delle righe, come indicato |
columns |
Asse delle colonne, come indicato; assente per una tabella a una variabile |
cells |
cells[i][j]: {"value": ...} nell'unità di misura dell'output, oppure {"error": {"code", "message"}} quando il metodo interno ha rifiutato quella combinazione; una tabella a una variabile ha una sola colonna |
failed_cells |
Numero di celle che contengono un errore |
Convenzioni (esplicite, riportate in explain.conventions)¶
| Convenzione | Valore | Default |
|---|---|---|
| Metodo interno | identificativo e versione, valutato tramite il registro | obbligatorio |
| Output | puntatore riportato in ogni cella | obbligatorio |
| Ordine di valutazione | caso base, poi le celle riga per riga, con le colonne nell'ordine indicato | fisso |
| Celle in errore | una cella rifiutata dal metodo interno contiene il codice e il messaggio di errore e la tabella prosegue; FM-E012 (un difetto di Fairmark) interrompe la richiesta |
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 e il numero di celle in errore. Una tabella richiede righe × colonne + 1 valutazioni (compreso il caso base); il limite di 10.000 consente al massimo 9.999 celle.
Errori¶
| Codice | Quando |
|---|---|
FM-E001 |
Le variabili di riga e di colonna sono lo stesso puntatore, un asse non ha valori, oppure un campo è mancante o sconosciuto |
FM-E007 |
method non corrisponde ad alcun metodo registrato |
FM-E015 |
Una variabile di riga o di colonna 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 |
righe × colonne + 1 supera 10.000 valutazioni 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 e la sintassi di output sono verificati prima di qualsiasi
valutazione; output è risolto sul caso base. Un errore del metodo interno sul caso base è
restituito invariato, con il proprio codice: senza caso base la tabella non ha un riferimento.
Un errore del metodo interno in una cella (ad esempio FM-E004 in dcf.fcff, oppure FM-E001
quando un valore è fuori dall'intervallo accettato dal metodo interno) è registrato in quella
cella, salvo FM-E012, che interrompe la richiesta.
Precisione¶
Ogni cella è precisa quanto il metodo interno (vedi la relativa pagina); la tabella non aggiunge calcoli propri. Nell'esempio le celle differiscono da un calcolo indipendente a 50 cifre di meno di \(10^{-24}\) unità monetarie; i casi di riferimento sono verificati entro \(10^{-9}\) unità monetarie.
Verificato con¶
- Casi di riferimento (golden case) in
tests/golden/analysis.sensitivity/(una tabella 5 × 5 WACC per crescita con due celle rifiutate e una tabella a una variabile), i cui valori attesi provengono dascripts/reference_analysis.py:dcf.fcffricostruito dalla formula documentata con il modulodecimaldi Python a 50 cifre. Non condivide codice con il motore. - Un ricalcolo indipendente di ogni cella con numpy-financial (
npf.npvpiù il valore terminale di Gordon) concorda entro un errore relativo di \(10^{-9}\) (python/tests/test_cross_check.py). - Un test di proprietà richiede che ogni cella sia uguale a un'esecuzione diretta del metodo interno sull'input modificato, o al suo codice di errore, e che una tabella a una variabile sia uguale alla colonna corrispondente di una tabella a due variabili.