Ebben a bejegyzésben pénzügyi befektetések elemzéséhez és értékeléséhez készítünk néhány függvényt. Ahhoz, hogy megértsük, hogy mit és hogyan számolnak e függvények, az alábbiakban a befektetések megtérüléselemzéséről, és annak pénzügyi alapeszközeiről adunk egy kis összefoglalót.
Amikor befektetjük a pénzünket valamibe, pl. részvény, kötvény, ingatlan, akkor most, azaz a jelenben kifizetünk egy összeget és azt várjuk, hogy a befektetésünk tárgya a jövőben hozamot termeljen, amely befolyik a bankszámlánkra osztalék, kamat, vagy bérleti díj formájában. A befektetett összeg tehát mindig kiáramlik tőlünk, míg a hozamok általában befolynak. Azért általában, mert néha az is előfordulhat, hogy a befektetést követően egy időpontban nem befolyik jövedelem, hanem nekünk kell kifizetést teljesíteni. Ez a helyzet például akkor, ha egy ingatlant bérbeadunk, de időközben javítást, karbantartást kell végezni. Ha ennek összege nagyobb az adott hónapban befolyó bérleti díjnál, akkor eredőben kifizetőkké válunk abban a hónapban. Egy ilyen helyzetet mutat a következő ábra, ahol a kifizetések negatív, a befolyó összegek pozitív előjellel szerepelnek.

A befektetéseket megtérülés szempontjából tudni kell értékelni. Arra kell valamilyen módszer szerint választ adni, hogy jó befektetést csináltunk-e, vagyis azon idő alatt, amíg a befektetésünk hozamot termel, többet kapunk-e vissza, mint amennyit beinvesztáltunk. Az a módszer, hogy egyszerűen összeadjuk a befolyó hozamokat és levonjuk a befektetett összegből, és megnézzük, hogy pozitív-e, nem jó eljárás, mert ekkor figyelmen kívül hagytuk a pénz időértékét. A pénz időértéke azt jelenti, hogy ma, a jelenben megkapott összeg többet ér egy ugyanakkora összegnél, amelyet később, mondjuk egy év múlva kapunk meg. Ez azért van, mert ha ma kapjuk az összeget, akkor azon, a piacon éppen aktuálisan elérhető és biztosnak tekintett befektetési eszközt vehetünk, ami a most aktuális kamatfeltételekkel egy év múlva a kamat összegével többet fog érni. Ha egy év múlva a kamattal növelt összeget újra befektetjük az akkor aktuális éves kamattal, akkor a mához, azaz a jelenhez képest még többet fog érni a kezdeti tőkénk. Ez a kamatos kamat hatása.
Ha pedig az egy év múlva kapott összeget vesszük alapul, akkor az előbbi gondolatmenetet követve ilyenkor azt kérdezzük, hogy az akkori összeg vajon mennyit ér ma, azaz a jelenben. Ezért ezt az értéket jelenértéknek (present value) nevezik. A fenti logika alapján nem nehéz a kérdésre válaszolni, mert csak a fordított műveletet kell elvégezni: amennyivel – amilyen szorzótényezővel – a mai értékből megkaptuk a későbbi időszak összegét, annyival kell osztani a későbbi időpontban előálló értéket ahhoz, hogy megkapjuk a jelenértékét. Ezt a műveletet diszkontálásnak nevezik. Azt a kamatlábat, amellyel a diszkontálás történik leszámítolási kamatlábnak, diszkontlábnak vagy idegen eredetű szóval diszkontrátának (discount rate) nevezik. A diszkontráta általában az amit fentebb említettünk, vagyis a piacon éppen aktuálisan elérhető és biztosnak tekintett befektetési eszköz által kínált kamatláb (pl. állampapír esetén) vagy hozamráta (pl. ingatlan bérbeadás esetén).
Ha egymást követő több időszakban is kapunk befolyó összegeket (pl. kötvény kamatát évente vagy bérleti díjat havonta), akkor adott számú periódusban kapott összegek jelenértéke az egyes időszakok hozamai jelenértékének összege.
Egy adott befektetésből származó pénzáramlás (cashflow) belső megtérülési rátájának (internal rate of return) nevezik azt az adott időszakokra konstans kamatlábat vagy diszkontrátát, amelynél a nettó jelenérték zérus lesz.
A jelenérték, a nettó jelenérték és a belső megtérülési ráta a befektetések megtérülésének elemzésében alapvető mutatók, amelyek segítenek a befektetési döntésekben, az alternatívák közül az adott pillanatban legkedvezőbb kiválasztásában. E mutatók számítási képleteit foglalja össze az alábbi ábra, ahol, hogy könnyebben megérthetők legyenek a képletek, vizuálisan is próbáltuk illusztrálni a diszkontálás elvét.

A következő ábrán pedig számszerű példát is láthatunk egy kötvénybefektetésre, illetve annak eldöntésére, hogy vegyünk-e az 5 éves futamidejű, a névértékre vetített évi 5% kamatot fizető kötvényből névértéken. A bal felső táblázatban a diszkontráták (pl. bizonyos államkötvények által kinált kamat) állandóak a futamidő alatt, és magasabbak a kínált 5%-nál. Látható, hogy ebben az esetben a PV kisebb, mint a befektetett összeg, amiből következik, hogy az NPV negatív. Tehát névértéken megvenni ezt a kötvényt nem érdemes. A jobb felső ábra azt mutatja, hogy ha ugyanezt a kötvény a névérték 81%-nál kevesebbért lehet megvenni, akkor jó befektetést csinálunk, mert ekkor az NPV pozitív lesz és az IRR is magasabb lesz az aktuális diszkontrátánál.

Az ábra jobb alsó táblázata annyiban tér el a felette levőtől, hogy itt a diszkontráták időszakonként változnak, mégpedig csökkennek, és az időszak vége felé az 5% alá is mennek. Mivel itt is negatív az NPV ezért névértéken most sem érdemes megvenni a kötvényt. De, ha a névérték 95,1%-nál alacsonyabb áron kínálják, akkor már igen.
Azért írtuk le és szemléltettük ábrán és táblázatban is e pénzügyi mutatók szerepét és számítását, mert ezen elvek világos megértése nem csak ahhoz fontos, hogy a számításukat lehető tevő online felületek vagy táblázatkezelő programok megfelelő függvényeit helyesen tudjuk használni, hanem ahhoz is szükséges, hogy ha magunk akarunk ilyen függvényeket Pythonban írni.
A fogalmak, elvek és képletek ismeretében a jelenértéket és nettó jelenértéket számoló függvények megalkotása nem nehéz, mert lényegében a matematikai képleteket kell lekódolni. Ezek így néznek ki:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
from typing import Iterable from math import prod def pv(discount_rate, *cashflow): """A periodikus időközönként, az időszakok végén előálló 'cashflow' értékek jelenértékét adja vissza a periódusokra állandó 'discount_rate' diszkontráta mellett. """ return sum(cf / (1 + discount_rate) ** n for n, cf in enumerate(cashflow, 1)) def npv(discount_rate, investment, *cashflow): """A periodikus időközönként, az időszakok végén előálló 'cashflow' értékek nettó jelenértékét adja vissza a periódusokra állandó 'discount_rate' diszkontráta mellett számított jelenérték és a jelenben érvényes 'investment' érték különbségeként. """ return investment + pv(discount_rate, *cashflow) def pv_var_drs(discount_rates: Iterable, *cashflow): """A periodikus időközönként, az időszakok végén előálló cashflow értékek jelenértékét adja vissza a 'discount_rates' sorozatban megadott, egymást követő időszakokban érvényes diszkontráták mellett. """ discount_rates = tuple(discount_rates) if len(discount_rates) < len(cashflow): raise ValueError('A diszkontráták száma legalább annyi kell, hogy legyen, mint a cashflow értékek száma.') discount_factors = [1 / (1 + r) for r in discount_rates] return sum(cf * prod(discount_factors[:n]) for n, cf in enumerate(cashflow, 1)) def npv_var_drs(discount_rates: Iterable, investment, *cashflow): """A periodikus időközönként, az időszakok végén előálló 'cashflow' értékek nettó jelenértékét adja vissza a 'discount_rates' sorozatban megadott, egymást követő időszakokban érvényes diszkontráták mellett számított jelenérték és a jelenben érvényes 'investment' érték különbségeként. """ return investment + pv_var_drs(discount_rates, *cashflow) |
A belső megtérülési ráta számolása azonban már némi átgondolást igényel. Ugyanis itt nincs képlet, hanem meg kell keresni azt a diszkontrátát, amelynél az NPV zérus lesz. Ez matematikailag egy gyökkeresést jelent, amire számos algoritmus létezik. Mi most az intervallumfelezéses eljárást alkalmazzuk némi kiegészítéssel. Ugyanis az NPV meglehetősen érzékeny a diszkontráta kis változásaira és ha túl tág intervallumból indulunk, akkor a futási idő nagyon megnőhet. Ezért fordított stratégiát alkalmazunk: egy viszonylag kicsi, de a gyakorlatban reális rátával indulunk, és ha az bizonyos iteráció szám után nem vezet eredményre, akkor tágítjuk az induló intervallumot, és így hajtjuk végre az intervallumfelezéses keresést. Az így kialakított függvény definícióját láthatjuk alább. Ugyanitt láthatók a fentebbi ábra példáira vonatkozó tesztszámítások is.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
def irr(investment, *cashflow) -> float | None: """A periodikus időközönként, az időszakok végén előálló 'cashflow' értékek és a jelenben érvényes 'investment' érték belső megtérülési rátáját adja vissza. Ha ez nem határozható meg, akkor None lesz a visszatérési érték. """ def _irr(r1, r2): r = (r1 + r2) / 2 for cnt in range(100): if abs(npv_value := npv(r, investment, *cashflow)) > 1e-7: if npv_value < 0: r2 = r else: r1 = r r = (r1 + r2) / 2 else: return r return None rate_low, rate_high = -0.1, 0.1 for cntr in range(100): try: if (y := _irr(rate_low, rate_high)) is not None: return y else: rate_high *= 2 rate_low = -rate_high except (ZeroDivisionError, OverflowError): return None return None if __name__ == '__main__': # Jelenérték (PV) teszt az időszakokra állandó diszkontrátával. print(round(pv(0.1, 50000, 50000, 50000, 50000, 1050000))) # Eredmény: 810461 # Nettó jelenérték (NPV) teszt az időszakokra állandó diszkontrátával. print(round(npv(0.1, -1000000, 50000, 50000, 50000, 50000, 1050000))) # Eredmény: -189539 print(round(npv(0.1, -810460.7, 50000, 50000, 50000, 50000, 1050000))) # Eredmény: 0 # Jelenérték (PV) teszt időszakonként eltérő diszkontrátákkal. print(round(pv_var_drs([0.1, 0.08, 0.06, 0.04, 0.02], 50000, 50000, 50000, 50000, 1050000))) # Eredmény: 951445 # Nettó jelenérték (NPV) teszt időszakonként eltérő diszkontrátákkal. print(round(npv_var_drs([0.1, 0.08, 0.06, 0.04, 0.02], -1000000, 50000, 50000, 50000, 50000, 1050000))) # Eredmény: -48555 print(round(npv_var_drs([0.1, 0.08, 0.06, 0.04, 0.02], -951445, 50000, 50000, 50000, 50000, 1050000))) # Eredmény: 0 # Belső megtérülési ráta (IRR) teszt. print('{:.1%}'.format(irr(-1000000, 50000, 50000, 50000, 50000, 1050000))) # Eredmény: 5.0% print('{:.1%}'.format(irr(-810460.7, 50000, 50000, 50000, 50000, 1050000))) # Eredmény: 10.0% |
E bejegyzésben elsődlegesen a függvények készítése volt a középpontban. Ezzel a Python tudásépítés lépésről lépésre című e-könyvben részletesen a „Egymáshoz rendelve – függvények” fejezet valamint a „Különleges függvénydefiníciók” fejezet foglalkozik. A függvénydefiníciókon belül használtunk listaépítő és generátor kifejezést, amelyről a „Műveletek” fejezeten belül a „Konténerépítés” és „Iterátorépítés – generátor kifejezés” alcímek szólnak. A beépített függvényeket és matematikai függvényeket pedig a „Beépített függvények” fejezet, valamint a „Készétel fogyasztás – a szabványos könyvtár moduljainak használata” fejezet „Matematikai számítások támogatása” alfejezete ismerteti.