library(tidyverse) library(lubridate) source("LPModel.R") library(lpSolve) mdl = LPModel$new("min") ######################################################################## # Input parameters ######################################################################## # Some fictitious numbers, use your own numbers here. # we start computation in 2019 y0 = 2019 # mortage year starts 1st February m0 = 2 # still 18 years to go L = 18 # monthly percentage computed based on yearly percentage r = 1.05^(1/12) # total amount of the mortgage H = 200000 # total amount in savings account on 1st Feb 2019 S = 46000 # minimum amount for extra payments M = 500 # bandwidth limit enforced by the bank B = 7 Smax = 5500 Smin = 5000 savedbefore = list( c(2019, 4, 5000) ) # current year and month. Change this if you want the computation to assume some time in the # past. This is used for processing savedbefore. curYear = year(Sys.Date()) curMonth = month(Sys.Date()) ######################################################################## # end of input section ######################################################################## # gets index in model of the month # - year: 4 digit year # - month: 1-12 timeIndex = function(year, month) { 1 + (year-y0)*12 + (month-m0) } indexToYearMonth = function(i) { y = as.integer((m0-1 + i - 1)/12) + y0 m = i - timeIndex(y, 1) + 1 c(year = y,month = m) } y1 = y0 + L m1 = m0 -1 if (m0 < 1) { m1 = m0 + 12 y1 = y1 - 1 } nomonths = timeIndex(y1, m1) i0 = timeIndex(y0, m0) s = mdl$continuousVar("s", nomonths+1) # state of account at beginning of period i v = mdl$continuousVar("v", nomonths) # mandatory amount e = mdl$continuousVar("e", nomonths) # additional amount delta = mdl$binaryVar("delta", nomonths) # if an additional amount is entered savemax = mdl$continuousVar("savemax") savemin = mdl$continuousVar("savemin") costsFixed = mdl$continuousVar("costsFixed") costsExtra = mdl$continuousVar("costsExtra") costs = mdl$continuousVar("costs") # initial/terminating conditions # s_i0 = S, S_{12L+1} = H mdl$condition(1, s(1), "=", S) mdl$nextCondition() mdl$condition(1, s(12*L+1), "=", H) mdl$nextCondition() ################################################### # history # bandwidth mdl$condition(1, savemax(), ">=", Smax) mdl$nextCondition() mdl$condition(1, savemin(), "<=", Smin) mdl$nextCondition() # additional payments from the past getPaidAmount = function(year,month) { amount = 0 for (entry in savedbefore) { if (year == entry[[1]] && month == entry[[2]]) { amount = amount + entry[[3]] } } amount } currentMonthIndex = timeIndex(curYear, curMonth) if (currentMonthIndex > 1) { for (i in 1:(currentMonthIndex-1)) { ym = as.list(indexToYearMonth(i)) amount = getPaidAmount(ym$year, ym$month) mdl$condition(1, e(i), "=", amount) mdl$nextCondition() } } # interest for (i in 1:nomonths) { mdl$condition(1, s(i+1), "=", 0, r, s(i), 1, v(i), r, e(i)) mdl$nextCondition() } # computation of mandatory amount for (i in 1:nomonths) { mdl$condition(r^(12*L-i+1), s(i), (r^(12*L-i+1)-1)/(r-1), v(i), "=", H) mdl$nextCondition() } # minimum amount of M for (i in 1:nomonths) { mdl$condition(M, delta(i), "<=", 0, 1, e(i)) mdl$nextCondition() mdl$condition(1, e(i), "<=", 0, H, delta(i)) mdl$nextCondition() } # bandwidth for (y in 0:(L-1)) { mdl$condition(1, savemax(), ">=", 0) for (i in (12*y+1):(12*y+12)) { mdl$condition(1, v(i), 1, e(i)) } mdl$nextCondition() } for (y in 0:(L-1)) { mdl$condition(1, savemin(), "<=", 0) for (i in (12*y+1):(12*y+12)) { mdl$condition(1, v(i), 1, e(i)) } mdl$nextCondition() } # bandwidth condition mdl$condition(1, savemax(), "<=", 0, B, savemin()) mdl$nextCondition() # costs mdl$condition(1, costsFixed(), "=", 0) for (i in 1:nomonths) { mdl$condition(1, v(i)) } mdl$nextCondition() mdl$condition(1, costsExtra(), "=", 0) for (i in 1:nomonths) { mdl$condition(1, e(i)) } mdl$nextCondition() mdl$condition(1, costs(), '=', 0, 1, costsFixed(), 1, costsExtra()) mdl$nextCondition() mdl$cost(1, costs()) # solve it . solution = mdl$solve() extraPayments = solution$vars$e iExtraPayments = which(extraPayments > 0) extraPaymentsDf = bind_rows(lapply(iExtraPayments, function(i) { as.list(c(indexToYearMonth(i), extraPayment = round(extraPayments[[i]]), fixedPaymentBefore = round(solution$vars$v[[i]]), fixedPaymentAfter = round(solution$vars$v[[i+1]]) )) })) # deal with rounding extraPaymentsDf = extraPaymentsDf %>% filter(extraPayment > 0) extraPaymentsDf extraPaymentsFuture = sum(solution$vars$e[timeIndex(curYear, curMonth):length(solution$vars$e)]) fixedPaymentsFuture = sum(solution$vars$v[timeIndex(curYear, curMonth):length(solution$vars$e)]) cat('Total extra payments in the future: ', extraPaymentsFuture, '\n') cat('Total fixed payments in the future: ', fixedPaymentsFuture, '\n') cat('Total payments in the future : ', solution$vars$costs, '\n')