perpetuity
Calculate present value of infinite payment streams for financial analysis of preferred stocks, endowments, and perpetual assets using level, growing, or due formulas.
Instructions
Calculate present value of a perpetuity (infinite series of payments).
A perpetuity is an annuity that continues forever. Common in: - Preferred stock dividends - Endowment funds - Real estate with infinite rental income - UK Consol bonds (historically)
Formulas: Level Ordinary: PV = C / r Level Due: PV = C / r × (1 + r) Growing: PV = C / (r - g), where r > g
Examples:
LEVEL PERPETUITY: £1000 annual payment at 5% payment=1000, rate=0.05 Result: PV = £20,000
GROWING PERPETUITY: £1000 payment growing 3% annually at 8% discount payment=1000, rate=0.08, growth_rate=0.03 Result: PV = £20,000
PERPETUITY DUE: £1000 at period start at 5% payment=1000, rate=0.05, when='begin' Result: PV = £21,000
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| context | No | Optional annotation to label this calculation (e.g., 'Bond A PV', 'Q2 revenue'). Appears in results for easy identification. | |
| output_mode | No | Output format: full (default), compact, minimal, value, or final. See batch_execute tool for details. | full |
| payment | Yes | Periodic payment amount (e.g., 1000) | |
| rate | Yes | Discount rate per period (e.g., 0.05) | |
| growth_rate | No | Payment growth rate (None or 0 for level, e.g., 0.03 for growing) | |
| when | No | Payment timing: 'end' or 'begin' | end |
Implementation Reference
- src/vibe_math_mcp/tools/financial.py:349-382 (registration)MCP tool registration decorator defining the perpetuity tool's name, description, examples, and metadata annotations.@mcp.tool( name="perpetuity", description="""Calculate present value of a perpetuity (infinite series of payments). A perpetuity is an annuity that continues forever. Common in: - Preferred stock dividends - Endowment funds - Real estate with infinite rental income - UK Consol bonds (historically) Formulas: Level Ordinary: PV = C / r Level Due: PV = C / r × (1 + r) Growing: PV = C / (r - g), where r > g Examples: LEVEL PERPETUITY: £1000 annual payment at 5% payment=1000, rate=0.05 Result: PV = £20,000 GROWING PERPETUITY: £1000 payment growing 3% annually at 8% discount payment=1000, rate=0.08, growth_rate=0.03 Result: PV = £20,000 PERPETUITY DUE: £1000 at period start at 5% payment=1000, rate=0.05, when='begin' Result: PV = £21,000""", annotations=ToolAnnotations( title="Perpetuity Calculations", readOnlyHint=True, idempotentHint=True, ), )
- Handler function implementing present value calculation for level perpetuity (PV=C/r), growing perpetuity (PV=C/(r-g)), and perpetuity due (PV=C/r * (1+r)), with input validation and metadata generation.async def perpetuity( payment: Annotated[float, Field(description="Periodic payment amount (e.g., 1000)")], rate: Annotated[float, Field(description="Discount rate per period (e.g., 0.05)", gt=0)], growth_rate: Annotated[ float | None, Field( description="Payment growth rate (None or 0 for level, e.g., 0.03 for growing)", ge=0 ), ] = None, when: Annotated[ Literal["end", "begin"], Field(description="Payment timing: 'end' or 'begin'") ] = "end", ) -> str: """Calculate present value of perpetuity.""" try: # Validate inputs if rate <= 0: raise ValueError("Discount rate must be positive") if growth_rate is not None: if growth_rate < 0: raise ValueError("Growth rate cannot be negative") if growth_rate >= rate: raise ValueError( f"Growth rate ({growth_rate}) must be less than discount rate ({rate}) " "for perpetuity to have finite value" ) # Calculate present value based on type if growth_rate is not None and growth_rate > 0: # Growing perpetuity: PV = C / (r - g) pv = payment / (rate - growth_rate) perpetuity_type = "growing" elif when == "begin": # Perpetuity due (payments at beginning): PV = C/r × (1+r) pv = (payment / rate) * (1 + rate) perpetuity_type = "level_due" else: # Ordinary perpetuity (payments at end): PV = C / r pv = payment / rate perpetuity_type = "level_ordinary" # Build metadata metadata: Dict[str, Any] = { "type": perpetuity_type, "payment": payment, "rate": rate, } if growth_rate is not None: metadata["growth_rate"] = growth_rate if when != "end": metadata["when"] = when return format_result(float(pv), metadata) except Exception as e: raise ValueError(f"Perpetuity calculation failed: {str(e)}")
- TOOL_CATEGORIES dictionary listing 'perpetuity' under Financial category for batch execution tool registry."Financial": ["financial_calcs", "compound_interest", "perpetuity"],