Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ PV(rate, n, pmt, fv float64, when Timing) float64
PMT(rate, n, pv, fv float64, when Timing) float64
NPer(rate, pmt, pv, fv float64, when Timing) (float64, error)
Rate(n, pmt, pv, fv float64, when Timing) (float64, error)
SolveTVM(n, i, pv, pmt, fv float64, when Timing) (float64, error)
```

`SolveTVM` is the unified solver — pass `math.NaN()` for exactly one variable.

```go
i, _ := engine.SolveTVM(360, math.NaN(), 300000, -1798.65, 0, engine.End)
fmt.Printf("%.4f%%\n", i*12*100) // 6.00% APR
```

### Cash flows
Expand Down
39 changes: 39 additions & 0 deletions engine/finance.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,45 @@ func Rate(n, pmt, pv, fv float64, when Timing) (float64, error) {
return 0, ErrNoConvergence
}

// SolveTVM solves for the missing time-value-of-money variable. Pass
// math.NaN() for exactly one of n, i, pv, pmt, or fv. i is the periodic
// rate as a fraction (0.05 = 5%). The result is the solved value or an
// error if the solver fails to converge.
func SolveTVM(n, i, pv, pmt, fv float64, when Timing) (float64, error) {
var missing int
if math.IsNaN(n) {
missing++
}
if math.IsNaN(i) {
missing++
}
if math.IsNaN(pv) {
missing++
}
if math.IsNaN(pmt) {
missing++
}
if math.IsNaN(fv) {
missing++
}
if missing != 1 {
return 0, ErrNoSolution
}
switch {
case math.IsNaN(n):
return NPer(i, pmt, pv, fv, when)
case math.IsNaN(i):
return Rate(n, pmt, pv, fv, when)
case math.IsNaN(pv):
return PV(i, n, pmt, fv, when), nil
case math.IsNaN(pmt):
return PMT(i, n, pv, fv, when), nil
case math.IsNaN(fv):
return FV(i, n, pv, pmt, when), nil
}
return 0, ErrNoSolution
}

// NPV returns the net present value of a cash-flow stream discounted at rate
// (a fraction). cashflows[0] is the period-0 flow (typically the initial
// outlay); cashflows[k] occurs at the end of period k.
Expand Down
34 changes: 11 additions & 23 deletions engine/financial.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,45 +8,33 @@ func (e *Engine) SetPV() { e.FinPV = e.X }
func (e *Engine) SetPMT() { e.FinPMT = e.X }
func (e *Engine) SetFV() { e.FinFV = e.X }

func (e *Engine) SolveN() {
n, err := NPer(e.FinI/100, e.FinPMT, e.FinPV, e.FinFV, Timing(e.Flags.Begin))
func (e *Engine) SolveN() { e.solve(NaNf64(), e.FinI/100, e.FinPV, e.FinPMT, e.FinFV) }
func (e *Engine) SolveI() {
v, err := SolveTVM(e.FinN, NaNf64(), e.FinPV, e.FinPMT, e.FinFV, Timing(e.Flags.Begin))
if err != nil {
e.X = math.NaN()
return
}
e.Push()
e.X = clamp(n)
e.X = clamp(v * 100)
e.Flags.StackLift = true
}
func (e *Engine) SolvePV() { e.solve(e.FinN, e.FinI/100, NaNf64(), e.FinPMT, e.FinFV) }
func (e *Engine) SolvePMT() { e.solve(e.FinN, e.FinI/100, e.FinPV, NaNf64(), e.FinFV) }
func (e *Engine) SolveFV() { e.solve(e.FinN, e.FinI/100, e.FinPV, e.FinPMT, NaNf64()) }

func (e *Engine) SolveI() {
r, err := Rate(e.FinN, e.FinPMT, e.FinPV, e.FinFV, Timing(e.Flags.Begin))
func (e *Engine) solve(n, i, pv, pmt, fv float64) {
v, err := SolveTVM(n, i, pv, pmt, fv, Timing(e.Flags.Begin))
if err != nil {
e.X = math.NaN()
return
}
e.Push()
e.X = clamp(r * 100)
e.Flags.StackLift = true
}

func (e *Engine) SolvePV() {
e.Push()
e.X = clamp(tvmPV(e.FinI/100, e.FinN, e.FinPMT, e.FinFV, e.Flags.Begin))
e.Flags.StackLift = true
}

func (e *Engine) SolvePMT() {
e.Push()
e.X = clamp(tvmPMT(e.FinI/100, e.FinN, e.FinPV, e.FinFV, e.Flags.Begin))
e.X = clamp(v)
e.Flags.StackLift = true
}

func (e *Engine) SolveFV() {
e.Push()
e.X = clamp(tvmFV(e.FinI/100, e.FinN, e.FinPV, e.FinPMT, e.Flags.Begin))
e.Flags.StackLift = true
}
func NaNf64() float64 { return math.NaN() }

func tvmPV(i, n, pmt, fv float64, begin bool) float64 {
if isZero(i) {
Expand Down
Loading