Cálculo de la tasa de interés anual efectiva y la tasa de porcentaje anual con tarifas (en R)

Problema

¿Cómo calcular la Tasa de Interés Anual Efectiva (EAR) y la Tasa de Porcentaje Anual (APR) con tarifas (en R)?

Estoy interesado en saber cuánto estoy pagando realmente de interés cuando se toman en cuenta las tarifas y asumo que EAR es el camino a seguir, pero corríjame si me equivoco.

Traté de implementar la APR a continuación como en la descripción de investopedia, pero la APR es más baja que el interés nominal, por lo que ciertamente no es correcta (debería estar alrededor del 3,5%).

lo que he probado

Debajo de un MVE que también explica el problema con más detalle:

rm(list=ls())
library(reprex)

# define costs, fees and interests
price <- 24800
monthly_payment <- 280
deposit <- 4000
loan_term <- 5*12 #in months
initial_fee <- 300
monthly_fee <- 12
nominal_interest <- 2/100
monthly_interest <- nominal_interest/12

# initiate fixed costs, interest fees and total costs for the loop
handling_charges <- initial_fee
interest_fees <- 0
total_costs <- handling_charges

# substracting initial payment from the what is left variable
left <- price - deposit

#calculating how much of dept is left after loan period and how much interest has accumulated
for (i in 1:loan_term) {
  left_last_month   <- left
  left              <- left*(1+monthly_interest)
  interest_fees_mo  <- (left-left_last_month)
  interest_fees     <- interest_fees + interest_fees_mo 
  handling_charges  <- handling_charges + monthly_fee
  total_costs       <- total_costs+ interest_fees_mo + monthly_fee
  left              <- left+monthly_fee-monthly_payment
}

#https://www.investopedia.com/terms/a/apr.asp
#https://www.investopedia.com/terms/e/effectiveinterest.asp
 
apr_100 <- 
  ((
    (total_costs/price)
    /(loan_term*30.4375) # number of days in the loan term
    )
   *365.25)             # number of days in a year

apr <- apr_100*100

# ear ???


share_of_loan_100 <- (total_costs/price)*100
share_of_loan_100
#> [1] 9.632864

left # after loan period ends
#> [1] 6088.95
interest_fees
#> [1] 1368.95
handling_charges
#> [1] 1020
total_costs
#> [1] 2388.95
apr   # wrong
#> [1] 1.926573
# ear # ????

Creado el 2022-01-20 por el paquete reprex (v2.0.1)

Definiciones de investopedia:

ABR

ABR

EAR, no se tienen en cuenta las tasas

EAR, no se tienen en cuenta las tasas

EDIT2:

Según las sugerencias de base64 , creo que ahora está funcionando, aunque el código es feo. ¿Crees que ahora da apr & apy correctos?

Agregué una opción para predefinir el pago mensual o calcularlo en función del pago final (al establecer el pago mensual en FALSO, se aproximará). También existe la opción de elegir cuándo se capitaliza el interés (p. ej., 1:12 para mensual, 1 para el primer mes del año, c(6,12) para el 6.° y 12.° mes del año, desde el comienzo del periodo de préstamo).

Después de ejecutar el código, puede simplemente ingresar los valores en la interest.Ffunción. Ver los dos ejemplos al final ( stack_example_monthly& stack_example_annual)

rm(list=ls())
library(reprex)
library(gtools)

# We need 3 helper functions
# 1. a precise enough binary search function
# 2. Payments function to calculate payments during the loan period
# 3. Apr to calculate the real interest rate

##########################################################
# 1. Binary search - helper function
##########################################################
# gtools binsearch-function modified for more accurate search results
# source: Gregory R. Warnes, Ben Bolker and Thomas Lumley (2021). gtools: Various R Programming Tools. R package version 3.9.2. https://CRAN.R-project.org/package=gtools

binsearch_decimal <- function(fun, range, ..., target = 0, lower = ceiling(min(range)),
                             upper = floor(max(range)), maxiter = 1000, showiter =FALSE) 
{
 lo <- lower
 hi <- upper
 counter <- 0
 val.lo <- fun(lo, ...)
 val.hi <- fun(hi, ...)
 if (val.lo > val.hi) {
   sign <- -1
 }
 else {
   sign <- 1
 }
 if (target * sign < val.lo * sign) {
   outside.range <- TRUE
 }
 else if (target * sign > val.hi * sign) {
   outside.range <- TRUE
 }
 else {
   outside.range <- FALSE
 }
 while (counter < maxiter && !outside.range) {
   counter <- counter + 1
   if (hi - lo <= 0.00001 || lo < lower || hi > upper) # 1 -> 0.00001
     break
   center <- round((hi - lo)/2 + lo, 6) # 0 -> 6
   val <- fun(center, ...)
   if (showiter) {
     cat("--------------\n")
     cat("Iteration #", counter, "\n")
     cat("lo=", lo, "\n")
     cat("hi=", hi, "\n")
     cat("center=", center, "\n")
     cat("fun(lo)=", val.lo, "\n")
     cat("fun(hi)=", val.hi, "\n")
     cat("fun(center)=", val, "\n")
   }
   if (val == target) {
     val.lo <- val.hi <- val
     lo <- hi <- center
     break
   }
   else if (sign * val < sign * target) {
     lo <- center
     val.lo <- val
   }
   else {
     hi <- center
     val.hi <- val
   }
   if (showiter) {
     cat("new lo=", lo, "\n")
     cat("new hi=", hi, "\n")
     cat("--------------\n")
   }
 }
 retval <- list()
 retval$call <- match.call()
 retval$numiter <- counter
 if (outside.range) {
   if (target * sign < val.lo * sign) {
     warning("Reached lower boundary")
     retval$flag <- "Lower Boundary"
     retval$where <- lo
     retval$value <- val.lo
   }
   else {
     warning("Reached upper boundary")
     retval$flag <- "Upper Boundary"
     retval$where <- hi
     retval$value <- val.hi
   }
 }
 else if (counter >= maxiter) {
   warning("Maximum number of iterations reached")
   retval$flag <- "Maximum number of iterations reached"
   retval$where <- c(lo, hi)
   retval$value <- c(val.lo, val.hi)
 }
 else if (val.lo == target) {
   retval$flag <- "Found"
   retval$where <- lo
   retval$value <- val.lo
 }
 else if (val.hi == target) {
   retval$flag <- "Found"
   retval$where <- hi
   retval$value <- val.hi
 }
 else {
   retval$flag <- "Between Elements"
   retval$where <- c(lo, hi)
   retval$value <- c(val.lo, val.hi)
 }
 return(retval)
}


##############################################3
# 2.payments - helper function 
##############################################
#calculating how much of dept is left after loan period and how much interest has accumulated
# includes an option to calculate monthly payment with binary search
payments.F <- function(monthly_payment, #
                      binary_search, 
                      monthly_fee, #
                      nominal_interest,
                      price, #
                      deposit, #
                      initial_fee,
                      loan_term,
                      compounding) {#
 
 # substracting initial payment and initial fees to get principal    
 principal <- price - deposit                     
 left <- principal                                      
 
 
 # initiate parameters for the loop
 handling_charges <- initial_fee
 interest_fees <- 0
 total_costs <- handling_charges
 monthly_interest <- nominal_interest/12
 month <- 1
 no_of_compoundings <- length(compounding)
 compounding_period_interest <- nominal_interest/length(compounding)
 
 #defining a "not in" operator
 `%!in%` <- Negate(`%in%`)
 
 if ( (sum(compounding %in% 1:12) >0) & (sum(compounding %!in% 1:12) ==0) ) {
   #print('--a valid compounding period set--')
 } else {
   stop("Error: Invalid compoumding value. Set it to a vector of months when you want it to compound, e.g. 1:12, c(6,12),5")
 }
 
 for (i in 1:loan_term) {
   left_last_month   <- left
   
   if (month %in% compounding) {
     left <-left*(1+compounding_period_interest) 
   }
   interest_fees_mo  <- (left-left_last_month)
   interest_fees     <- interest_fees + interest_fees_mo
   handling_charges  <- handling_charges + monthly_fee
   total_costs       <- total_costs+ interest_fees_mo + monthly_fee
   left              <- left+monthly_fee-monthly_payment
   
   # incrementing or resetting monthly counter
   month <- month+1
   if (i %% 12 == 0) {
     month <- 1
   }
   
 }
 if (binary_search==T) {
   return(left)
 } else {
   return(list("principal"=principal,
               "left"=left,
               "total_costs"=total_costs,
               "interest_fees"=interest_fees,
               "handling_charges"=handling_charges
   ))
 }
}


############################################################################
# 3. APR - helper function
############################################################################
#apr will be later solved with binary search

apr.F <- function(APR,PMT,N,FV,PV) {
 (PMT*((1-(1/(1+(APR/12))^N))/(APR/12)))+(FV/(1+(APR/12))^N)-(PV)
}


###########################################################################
# main function where we plug in the values

interest.F <- function(price,
                      loan_term=5*12,
                      initial_fee=0,
                      nominal_interest=2/100,
                      monthly_fee=12,
                      deposit=4000,
                      monthly_payment=F, # Set to false if you want to calculate monthly payment
                      final_payment=0, # If monthly payment is set, it will overrun this
                      compounding=1:12
) {
 
 
 
 # running binary search to monthly cost if we have not defined it
 if (monthly_payment==F) {
   print('--Calculating monthly payment by binary search--')
   (binsearch_monthly_payment <- binsearch_decimal(function(x) 
                                                   payments.F(x, #
                                                   binary_search=T, 
                                                   monthly_fee=monthly_fee, #
                                                   nominal_interest=nominal_interest,
                                                   price=price, #
                                                   deposit=deposit, #
                                                   initial_fee=initial_fee,
                                                   loan_term=loan_term,
                                                   compounding=compounding), 
                                 range = c(0,2000),
                                 target=final_payment,
                                 showiter = F))
   
   
   monthly_payment <- mean(binsearch_monthly_payment$where)
   
 } else {
   print('--Using pre-fixed monthly payment amount--')
 }

 # assigning a payment scheme based on monthly payment (binary search or predefined)
 (payments <- payments.F(monthly_payment,
                         binary_search = F,
                         monthly_fee=monthly_fee, #
                         nominal_interest=nominal_interest,
                         price=price, #
                         deposit=deposit, #
                         initial_fee=initial_fee,
                         loan_term=loan_term,
                         compounding=compounding))
 
 
 paid_excluding_fees <- payments$principal-payments$left

 # running a binary search to find apr given that we know how much we have repayed the loan (excluding fees)
 (binsearch_apr <- binsearch_decimal(function(x) 
                                     apr.F(x,
                                     PMT=monthly_payment,
                                     N=loan_term,
                                     FV=payments$left,
                                     PV=payments$principal-initial_fee)
                   , range = c(-1,100),target=0))
 
 #taking the mean value of the binary search to approximate apr
 apr <- mean(binsearch_apr$where)

 apr <- apr
 
 #calculating apy based on apr
 #https://www.investopedia.com/terms/a/apy.asp
 apy <- (1+apr/12)^12-1
 
 
 
 share_of_price <- (payments$total_costs/(price-initial_fee))*100
 #payments$paid_excluding_fees <- paid_excluding_fees
 payments$all_payments <- payments$total_costs + paid_excluding_fees +deposit
 payments$monthly_payments <- monthly_payment
 payments$share_of_price <- share_of_price
 payments$apr <- apr*100
 payments$apy <- apy*100

 return(payments)
}


(stack_example_monthly <- interest.F(
 price=24800,
 loan_term=5*12,
 initial_fee=300,
 nominal_interest=2/100,
 monthly_fee=12,
 deposit=4000,
 monthly_payment=F, # 280 # Set to false to estimate monthly payment
 final_payment=6088.95, #  6088.95 If monthly payment is set, it will overrun this
 compounding=1:12 # a vector, eg. 1:12, c(6,12), 5.
))
#> [1] "--Calculating monthly payment by binary search--"
#> $principal
#> [1] 20800
#> 
#> $left
#> [1] 6088.95
#> 
#> $total_costs
#> [1] 2388.95
#> 
#> $interest_fees
#> [1] 1368.95
#> 
#> $handling_charges
#> [1] 1020
#> 
#> $all_payments
#> [1] 21100
#> 
#> $monthly_payments
#> [1] 280
#> 
#> $share_of_price
#> [1] 9.750817
#> 
#> $apr
#> [1] 3.5069
#> 
#> $apy
#> [1] 3.56382
 
(stack_example_annual <- interest.F(
   price=24800,
   loan_term=5*12,
   initial_fee=300,
   nominal_interest=2/100,
   monthly_fee=12,
   deposit=4000,
   monthly_payment=280, # 280 # Set to false to estimate monthly payment
   final_payment=NA, #  6088.95 If monthly payment is set, it will overrun this
   compounding=12 # a vector, eg. 1:12, c(6,12), 5.
 ))
#> [1] "--Using pre-fixed monthly payment amount--"
#> $principal
#> [1] 20800
#> 
#> $left
#> [1] 5921.857
#> 
#> $total_costs
#> [1] 2221.857
#> 
#> $interest_fees
#> [1] 1201.857
#> 
#> $handling_charges
#> [1] 1020
#> 
#> $all_payments
#> [1] 21100
#> 
#> $monthly_payments
#> [1] 280
#> 
#> $share_of_price
#> [1] 9.068805
#> 
#> $apr
#> [1] 3.2841
#> 
#> $apy
#> [1] 3.333986

Creado el 2022-02-13 por el paquete reprex (v2.0.1)

Respuestas (2)

En primer lugar, puede tirar por la borda la fórmula APR de Investopedia. Esa fórmula es solo para un pago único de menos de 1 año, por ejemplo, pago con tarjeta de crédito.

En segundo lugar, para el código actual, puedo confirmar que lo siguiente es correcto:

  • La Tasa de Interés Nominal del 2% por año, compuesta mensualmente, resulta en un Pago Mensual de $268 (Excluyendo el Cargo) en un Préstamo de 24800-4000=20800. $268 + $12 de tarifa mensual = $280, lo cual es consistente.
  • Después de 60 meses, tomando en cuenta el Interés Mensual, pero excluyendo el Cargo Inicial e ignorando el Cargo Mensual, resulta en $6088.95 de capital restante para calcular el próximo interés mensual.

Lo que salió mal es que (1) usó la fórmula incorrecta para la APR (en realidad no hay fórmula) y (2) ignoró la tarifa inicial de $300.

  • (1): APR para un préstamo de pago mensual (especialmente superior a 12 meses), de acuerdo con diferentes regulaciones de préstamos de consumo en todo el mundo, se calcula encontrando la APR correcta (conocida como I/Y) que da como resultado Principal = Valor actual de todos Pagos, es decir, la tabla de amortización. Esta respuesta explica por qué no se puede "resolver a mano" o mediante fórmula. Debe hacerse usando bibliotecas financieras (bastante seguro que R tiene un paquete de terceros), "fuerza bruta", "búsqueda binaria" o RATE() de Excel .

  • (2): La tarifa inicial de $ 300 debe deducirse del capital inicial de $ 20800, porque mientras el banco le dio $ 20800 el día 0, el banco también le quitó $ 300 el día 0, aunque de otra entrada de transacción. Por lo tanto, la cantidad real que salió del banco fue de $20500. Sin embargo, no hay necesidad de cambiar "izquierda <- precio - depósito" ya que distorsionará su "izquierda # después de que finalice el período del préstamo". Como la Tasa de Interés Nominal del 2% se aplica al Préstamo Bruto de 20800, la Comisión Inicial de $300 no se capitaliza como principal.

Considerando (1) y (2), el resultado de la APR a lo largo de 60 meses usando una calculadora financiera es 3.507% , asumiendo que la tarifa inicial de $300 se amortiza solo en 60 meses (es decir, terminación anticipada del préstamo donde el banco acuerda que el principal pendiente es 6088.95), en lugar de más de 83.181 meses (Meses Totales de Ingeniería Inversa desde 2% Nominal y Pago Mensual de $268). Si amortiza los $300 en 83.181 meses, la APR es de 3.723 % en 83.181 meses, que es un % más alto debido a una tarifa mensual de $12 más frecuente. Aquí hay una prueba más de que el 3.723% APR según la regulación de EE. UU. es correcto asumiendo 83.181 meses.

La APR efectiva (que incluye la tarifa inicial y la tarifa mensual), también conocida como APY, es (1+0.03507/12)^12-1 = 3.564 % para amortizar $300 solo durante 60 meses, y es (1+0.03723/12)^ 12-1 = 3.787% si amortiza $300 en 83.181 meses.

Editar

En mi respuesta anterior, dije explícitamente que no hay necesidad de cambiar "left <- price - deposit" .

Hay 2 mesas en el universo.

  • Uno según banco , donde el "Principal Pendiente" (ie FV) depende de la Tasa de Interés Nominal del 2%, no depende de la Cuota Inicial de $300, y la Cuota Mensual se fija en $12 independientemente del "Principal Pendiente".

  • Otro según la ley , donde la gente de finanzas dice que todos los intereses y cargos deben depender del "Principal pendiente". A medida que disminuye el "Principal pendiente", los "intereses y cargos" también deben disminuir con el tiempo. Todo lo que no es Principal se llama Interés (no Comisiones, Cargos, Costos, etc.).

Si te fijas bien en el enlace del 3,507% , la página decía Interés total: $2.388,95 . Esto "casualmente" es igual a su primer intento de código:

interest_fees
#> [1] 1368.95
handling_charges
#> [1] 1020
total_costs
#> [1] 2388.95

Sólo existe una tabla de amortización para apropiar (es decir, asignar) la parte de Principal y la parte de Intereses (incluidas las Comisiones) de cada Pago Fijo Mensual. Es solo una forma uniforme de apropiación que todas las personas de finanzas aceptan. La amortización nunca cambió el Pago Total ni el Interés Total (incluidas las Comisiones) durante el período.

Por lo tanto, insisto nuevamente, no cambie la sección del código "# restando el pago inicial de la variable que queda", porque lo necesita para calcular el "Principal pendiente después de 60 meses" según el banco, no según la ley .

Una vez que obtuvo este "Principal pendiente después de 60 meses" correcto:

left # after loan period ends
#> [1] 6088.95

Intentaría todos los valores posibles (o selectivamente mediante búsqueda binaria) de r de 0,0000000 a 1,0000000 para:

(280*((1-(1/(1+(r/12))^60))/(r/12)))+(6088.95/(1+(r/12))^60)-20500

El objetivo es encontrar la r que da como resultado 0 en esta fórmula. Tenga en cuenta que incluso Wolfram Alpha no sabe cómo resolverlo a menos que especifique 0 < r < 1 . Pero si inserta r = 0.03507 arriba (0.035068 para ser más exactos), comprenderá que esta fórmula es correcta .

Todos esos enlaces asociados con cada porcentaje de la respuesta asumen que comprende completamente el valor del dinero en el tiempo, incluido cuándo incluir las tarifas en el pago (es decir, PMT), cuándo deducir las tarifas por adelantado del capital inicial (es decir, PV).

La ecuación general es:

(PMT*((1-(1/(1+(r/12))^N))/(r/12)))+(FV/(1+(r/12))^N)-PV=0

Dónde:

PMT = Fixed Monthly Payment
      (Including fixed and variable interest and fees, whatever "costs" you call doesn't matter, as long as entire monthly payment is FIXED)
r = APR
N = Months paid so far
FV = Outstanding Principal according to the Bank at the end of N ($0 if fully repaid)
PV = Initial Principal - Upfront Fees paid by the borrower

Tenga en cuenta la diferencia entre el uso de series geométricas (es decir, arriba) o la suma simple.

(PMT*((1-(1/(1+(r/12))^N))/(r/12))) = Sum of PMT/(1+(r/12))^n where n = 1 to N

Luego de obtener la r (APR), si desea conocer la porción de Capital y la porción de Intereses (incluidas las Cuotas) que le corresponden a cada Pago Fijo Mensual de acuerdo a la ley , simplemente ejecute su código original con:

# define costs, fees and interests
**upfront_interest <- 300**
price <- 24800 **-upfront_interest**
monthly_payment <- 280
deposit <- 4000
loan_term <- 5*12 #in months
initial_fee <- **0**
monthly_fee <- **0**
nominal_interest <- **r**
monthly_interest <- nominal_interest/12

share_of_loan_100 <- (total_costs/(price**+upfront_interest-deposit**))*100

El resultado que hace eco "Todo lo que no es Principal se llama Interés (no Comisiones, Cargos, Costos, etc.)" y "La amortización nunca cambió el Pago Total ni el Interés Total (incluidas las Comisiones) durante el período".

share_of_loan_100
#> [1] 11.48533
left # after loan period ends
#> [1] 6088.948
interest_fees
#> [1] 2388.948
handling_charges
#> [1] 0
total_costs
#> [1] 2388.948

El "interés inicial" de $ 300 ya se distribuyó en 60 meses al incorporarse en APR. No es necesario agregar $300 al interés total manualmente.

Editar 2

En realidad, para la UE, parece que la capitalización es anual en decimales en lugar de mensual, a pesar de que el pago es mensual. Entonces, todo lo que necesita hacer es llegar primero a 6088.95, luego modificar en la búsqueda binaria como:

D=280
target=0
apr_100: ... + (6088.95*(1+APR/(100*100))^-(MONTHS/12)) - 20500

Sin embargo, al conocer esta APR de la UE, no puede realmente hacer directamente una tabla de amortización significativa y apropiarse del interés mensual. Incluso los europeos tendrían que volver a convertir esto en una tasa de interés de más de 1 mes para crear una tabla de amortización mensual.

“Siempre que los períodos de pago y de capitalización difieran entre sí, se recomienda calcular la tasa de interés efectiva por período de pago. La razón es que, para proceder con el análisis de equivalencia, los períodos de capitalización y de pago deben ser iguales”.

https://wps.prenhall.com/ecs_park_fee_2/87/22279/5703599.cw/content/index.html

De todos modos, APR de la UE = APY de EE. UU. con 12 períodos de pago por año y 1 capitalización por año .

Convierta US APY en US APR y tendrá la misma tabla de amortización que mi Edición inicial.

$call
binsearch(fun = apr_100, range = c(0, 100 * 100), target = 0)
$numiter
[1] 14
$flag
[1] "Between Elements"
$where
[1] 3.56 3.57
$value
[1]  2.282236 -3.853403
[1] 3.565

Sin embargo, 2 decimales para la tasa de interés no son suficientes.

Se agregó una sección de código editada arriba basada en su respuesta más útil. En cuanto a (2), ¿puede explicar cómo deducir la tarifa inicial del capital inicial mientras se mantiene left <- price - deposittal como indica? Intenté primero restar la tarifa inicial del capital y luego agregarla a la leftvariable, pero eso dio una tasa de interés muy alta.
@SamuelSaari Editar de nuevo.
debería funcionar ahora. ¿Me las arreglé para implementar todas las sugerencias de la manera que pretendías? Puede verificar la función simplemente ingresando los valores

Bastante seguro de que esta es la respuesta después de revisar estos cálculos. En su cálculo de apr_100, el 'precio' es incorrecto. Debe ser igual al capital inicial de la deuda, que en su ejemplo es 'precio menos depósito'. La cantidad prestada es de $ 20,800, que es contra lo que se deben calcular los intereses + las tarifas, no la deuda + el capital (es decir, el "precio").

Si ajusta eso, entonces la APR es igual al 2.3% con las tarifas tomadas en cuenta. Si luego conecta ese 2,3% en la fórmula EAR, debería dar como resultado un ajuste mensual del 2,32% anual.