#!/usr/local/bin/perl

use 5.040.3;

my $LOG = 1;

## REM: La versión 3 debería incluir _además_ la posibilidad de cambiar
## la proprción $alpha entre fijo y variable entre periodos. Esto lleva 
## a un abandono del esquema de la parte fija de la hipoteca (cuotas
## constantes) por otro similar pero mutable. (Es factible en tiempo,
## pero no muy 'elegante' en la programación o planificación de las 
## cuotas). dom 08 sep 2024 02:40:10 CEST Por eso, pendiente. 

# Por supuesto, esto se puede hacer recalculando cada periodo.
# Pero son muchos periodos!!! Puede pasar que alguien cambie 240-1 veces
# o una cantidad más normal (1,2) veces por año. Adicionalmente el 
# criterio para hacerlo se basa tanto en lo histórico como las 
# expectativas públicas de tipos de interés que proceden del BCE
# y constituyen el ingreso de la parte variable.

my $DEBUG = -1;

my $argv;
if (scalar @ARGV == 0){
    $argv = "help";
}else{
    $argv = join " ", @ARGV;
}

my $option1 = "noamort";
if ($argv =~ /noamort/i){ $option1 = "noamort"; }
else{ $option1 = "amort"; }

my $option2 = "nomora";
if ($argv =~ /nomora/i){ $option2 = "nomora"; }
else{ $option2 = "mora"; }

my $option3 = "notae";
if ($argv =~ /notae/i){ $option3 = "notae"; }
else{ $option3 = "tae"; }

if ($argv =~ /h/i){
    say "";
    say "Usage: $0 [[no]amort] [[no]mora] [[no]tae]";
    say "";
    say "noamort = don't simulate anticiped amortizations (default)";
    say "amort = simulate extra amortizations where it cost more";
    say "nomora = none of the periods are unpaid";
    say "mora = random unpaids happen";
    say "notae = simple interest is presented"; 
    say "tae = aditionally, tell what is the compound annual equivalent\n";
    say "Pedactic enough, it's only a TAE if approved by Central Bank";
    say "";
    say "Defaults are the _no_ options";
    say "";
    exit -3;
}

# MORTGAGE = Hipoteca

# Estrategia: simular de 0.1 a 0.9 el % dedicado a interés fijo ->
# préstamo francés
# simular mibor alto mediano o bajo cada 6 meses
# simular margen de interés variable
# simular costes de amortización anticipada (igual para cada tipo)

# automatizar respuesta del cliente para amortizar la parte mala

# calcular rentabilidad final total del préstamo. Se puede suponer
# de 20 años, tiempo fijo.

# Estructura: 
# - Configuración del préstamo
# - Entorno, cada 6 meses
# - Azar, las amortizaciones: + probables al final
# (la amortización anticipada es azarosa, pero la decisión de la parte
# en la que se hace no: podría ser la más ventajosa según histórico)
# - Resultados: análisis conjunto de flujos de caja e interes único
# implícito
# El tema de las comisiones se divide en dos: las de antes de la firma
# que van a cargo del banco, pero se deben tener en cuenta en resultados
# (min. 1% sin variar el principal) y las de amortización inesperada que
# se deducen a lo amortizado y por tanto, seguro que afectan al resultado.

my @PPAL;
$PPAL[0] = 300_000;          # €

my $PERIODO_DE_CARENCIA = 0;

my $N = 30 * 12;             # 30 AÑOS meses de pago, se suele sumar 1 por el periodo 0
                             # esos son muchos años, por lo que hay que mirar los costes
                             # de amort. anticipada y subrrogación -> incluso para cualquier tipo

my $intf = 0.055 / 12;        # interés fijo mensual   --- no se toca más

my $margen = 0.035 / 12;       # margen sobre variable mensual
my $puntoeuribor = 0.015 /12;       # + - semestral

my ($comisiones_iniciales, $comision_amortizacion) = ( 0.02, 0.02 ); # en las iniciales va todo

my $morosidad = 0.035;
my $nmora = 0;

# my $coste_depositos = 0.8 ;   # del euribor, se usará esto mejor
# duda de si deglosar en fijo y variable: va a ser que sí
# una solución de compromiso es poner 2 dimensiones [0][$N] total,
# [1][$N] tasa fija y [2][$N] tasa variable

#    my @coste = (0,0,0) x $N+1;    # supongo distinto el coste a tipo fijo (depósitos) que el variable (euribor)

my $error = 1.0e-6;

# REPORT PHASE
my $ITER = 10_000;  # simular esa cantidad de hipotecas
my %rdo;      # rdo. medio para 10 deciles de $alpha
my %cuenta;

my @int_periodo =();
my @cap_pend =();

my $z = 0;
for (1.. $ITER){
    $z++;
    
    my $alpha = rand() ;     # rand() o 0.5 o intervalo
    
    say "\nEL PORCENTAJE DE FIJO ES DE ", sprintf ("%.02f %%\n", 100*$alpha) if $DEBUG >= 1; 
    
    $PPAL[1] = $alpha * $PPAL[0];               # más = FIJO
    $PPAL[2] = (1-$alpha) * $PPAL[0];           # menos = VARIABLE
    
    my @cuota =();
    my @amort =();
    my @amortacum =();
    @cap_pend =();
    my @euribor =();    # parte mensual, revisable semestres o años
#    my @coste = (0,0,0) x $N+1;    # supongo distinto el coste a tipo fijo (depósitos) que el variable (euribor)
    @int_periodo = ();
    
    $amortacum[1][0] = 0;
    $cap_pend[1][0] = $PPAL[1];
#    $coste[1][0] = 0.667;          # 2/3
    $amortacum[2][0] = 0;
    $cap_pend[2][0] = $PPAL[2];
    
    $euribor[0] = 0.04 / 12;       # punto de partida
    $margen = 0.035 / 12;           # margen + riesgo realmente
    $puntoeuribor = 0.02 / 12;      # la referencia es lo anual siempre aunque pitufe semestralmente    
    
    my $j = 0;       # amortiz. anticipada
    $nmora = 0;
    my $tipo_mora = 0.06 / 12;  # tipo de la mora de una mensualidad por defecto. Será mayor si se conjura el interés variable
        
#    $coste[2][0] = $euribor[0];
    
    my @flujo = (0) x ($N+$N);  # normalmente será @{$cuota[0]} descontado y sumado -$PPAL
                               # con un 'unshift @flujo_gral, -$PPAL;' 0j0 a los índices
    $flujo[0] = -$PPAL[0] - $comisiones_iniciales * $PPAL[0]; # + $coste[1][0] * $PPAL[1] + $coste[2][0] * $PPAL[2]); 
    
    my $i = 0;
    my $correccion = 0;
    my $cual = 0;

    my ($tipo, $TAE);
    
    while ( ++$i <= $N + $nmora){
	# PARTE FIJA
	$cuota[1][$i] = cuotafija($i);
	$int_periodo[1][$i] += $intf * $cap_pend[1][$i-1];
	$amort[1][$i] += $cuota[1][$i] - $int_periodo[1][$i];
	$amortacum[1][$i] = $amortacum[1][$i-1] + $amort[1][$i];
	$cap_pend[1][$i] = $cap_pend[1][0] - $amortacum[1][$i];
 
	# PARTE VARIABLE
	$euribor[$i] = $euribor[$i-1];
		
	if ($i % 6 == 0){
	    $correccion = $puntoeuribor * rand();
	    $euribor[$i] += $correccion if ( $euribor[$i]+$correccion < 0.01/12 && $correccion < 0 );
	    $euribor[$i] -= $correccion if ( $euribor[$i]+$correccion > 0.05/12 && $correccion > 0 );
	    
	    if ($euribor[$i] + $margen > $tipo_mora){
		$tipo_mora = $euribor[$i] + $margen;
	    }else{
		$tipo_mora = 0.06 / 12;
	    }
	}	    
	
	$int_periodo[2][$i] += ($euribor[$i] + $margen) * $cap_pend[2][$i-1];         # también aquí puede haber mora de periodo anterior
	
	if ($cual >= 2 && $j > 0){    # caso amortiz. extra -> se mira a ver de qué parte se aplica según variable $cual
	    if ($amort[2][$i-1] - $cap_pend[2][0] /$N > 0) {
		$amort[2][$i] =  ($cap_pend[2][0] - $amortacum[$i-1]) / ($N - $j);         # no es necesario $amort[$cual][$i]*2 mínimo     -$j);
	    }
	    $amort[2][$i] = 0 if ($amort[2][$i] < 0);
	}else{
	    $amort[2][$i] = $cap_pend[2][0] / $N;                            
	}
	    
	$cuota[2][$i] = $int_periodo[2][$i] + $amort[2][$i];
	
	if ($DEBUG >= 1){
	    print "$i) Cuota parcial fija   $cuota[1][$i]            Cuota parcial variable   $cuota[2][$i]     Tipo = ", +$euribor[$i]+$margen, "\n";
	}
	
	$amortacum[2][$i] = $amort[2][$i] + $amortacum[2][$i-1];
	
	$cap_pend[2][$i] = $cap_pend[2][0] - $amortacum[2][$i]; 
	
	if ($option1 eq "amort"){
	
	    if ($i > $N/3 && $i % 24 == 0){        # amort anticipada, adicional
		my $ahorro =  (1+(rand()*5)) * 1000;                            # Esto está por debajo de lo esperado: puede interesar amortizar de [1]
	    
		$cual = decide ($i-1);
		
		$ahorro = $ahorro * (1 - $comision_amortizacion); 
	    
		if ($cap_pend[$cual][$i-1] > $amort[$cual][$i] + $ahorro){
		    $cuota[$cual][$i] += $ahorro;        # La amort anticipada ES un flujo de caja, por lo que sí puedo sumarlo a la cuota ($i)
		}else{
		    $cuota[$cual][$i] += $cap_pend[$cual][$i-1];           # Considero la comisión de CANCELACION igual que amort. anticipada
#		    if ($ahorro > $cap_pend[$cual][$i-1] - $amort[$cual][$i]){
			$ahorro = $cap_pend[$cual][$i-1] - $amort[$cual][$i];
#		    }
		}
		$j = $i;                                      # Se guarda el número de periodo en que se amortiza anticipado
		$amort[$cual][$i] += $ahorro;
		$cap_pend[$cual][$i] -= $ahorro;
		$amortacum[$cual][$i] += $ahorro; 
	    }
	
	}else{ ; }
	
	if ($option2 eq "mora"){
	    
	    # Con 'sin morosidad' debe de ser opcional
	    if ($i > $N/2 && int($i * $morosidad) > $nmora){
		# Complicado: distinguir si la morosidad es solo de amortización o también de intereses 
		$cuota[1][$i] = $cuota[2][$i] = 0;
		$amort[1][$i] = $amort[2][$i] = 0;
		$amortacum[1][$i] = $amortacum[1][$i-1];
		$amortacum[2][$i] = $amortacum[2][$i-1];
		$cap_pend[1][$i] = $cap_pend[1][$i-1];
		$cap_pend[2][$i] = $cap_pend[2][$i-1];
		$cap_pend[1][$i+1] = $cap_pend[1][$i] + $tipo_mora * $cap_pend[1][$i-1];
		$cap_pend[2][$i+1] = $cap_pend[2][$i] + $tipo_mora * $cap_pend[2][$i-1];
		$int_periodo[1][$i+1] = -0.0001;      # El periodo moroso puede ser el último. Hay que modificar el bucle para alargar lo que sea.
		$int_periodo[2][$i+1] = -0.0001;
		$nmora++;                                                                      # siempre suma a los periodos totales
	    }
	}else{ ; }	

#	$cual = 0;
	$flujo[$i] = $cuota[1][$i] + $cuota[2][$i];
    
	last if (abs($amortacum[1][$i] + $amortacum[2][$i] -$PPAL[0]) <= 1.0e-6); 
	last if (abs($cap_pend[1][$i]) <= 1.0e-6 && abs($cap_pend[2][$i]) <= 1.0e-6);
    }

    $tipo = busca_int(@flujo);

    printf "   El tipo real de lo dual (alpha = %.04f) es = %.6f %%\n", $alpha, 100*$tipo if $DEBUG >= 0;
    
    if ($option3 eq "tae"){
	$TAE = ((1+$tipo)**12)-1;    # equivalente anual, a interés compuesto
	if ($DEBUG >= 0){
	    say "Y a interés compuesto supone                             ", sprintf("%.6f",100*$TAE), " %\n";
	}	    
    }else{
	$tipo *= 12;                     # equivalente anual, a interés simple
    }
    die "Error gordo: interés negativo" if ($TAE < 0 || $tipo < 0);
    
    if ($LOG){   # quería deciles pero salieron centiles
	$rdo { int( $alpha * 100 ) / 100 } += (defined $TAE) ? $TAE : $tipo ;
	$cuenta { int( $alpha * 100 ) / 100 }++;
    }	
    sleep 1 if $DEBUG >=1;                  # tiempo para leerlo por alto
}

if ($LOG){
    say "\n\nFINAL REPORT:";
    for my $z (sort keys %rdo){
	say "mean alpha (part of fixed interest mortgage) = $z   ->    ", sprintf ("%.05f", $rdo{$z} / $cuenta{$z});
    }
    say "\n";
}

exit 3;             # faltaba un tornillo


sub decide {
    my $j = shift;
    my $sum1 = 0;
    my $sum2 = 0;
    for my $z ( 1 .. $j ){               # suma a lo bruto intereses de diferentes periodos
	$sum1 += $int_periodo[1][$z];
	$sum2 += $int_periodo[2][$z];
    }
    if ($sum1 / $PPAL[1] > $sum2 / $PPAL[2]){
	return 1;
    }else{
	return 2;
    }
    # Aquí no se tiene en cuenta de que puede no ser la mejor idea 
    # amortizar anticipadamente debido a que la comisión es alta.
}

sub cuotafija {
    my $ii = shift;
    my $factor = (1- (1+$intf)**(-$N+$nmora) ) / $intf;
    if (!defined $cap_pend[1][ $ii - 1]){
	return $PPAL[1] / $factor;
    }elsif ($nmora == 0){
	return $cap_pend[1][ 0 ] / $factor;
    }else{
	return $cap_pend[1][ $ii-1 ] / $factor;
    }	
}

sub busca_int {
    my @f = @_;       # el flujo de caja, lo que importa
    my $r = 0 ;       # el tipo, que intenta ser anual, pero a interés simple
    my ($sum, $c);
    do {
	$sum = $c = 0;
	for my $k (@f){
	    $sum += $k * ( (1+$r)**(-$c) );
	    $c++;
	}
	print "\r$0:    iter $z   error absoluto $sum   ";
	if ($sum < 0){
	    $r -= rand() * abs($sum) *1.0e-9;
	}elsif ($sum > 0){
	    $r += rand() * abs($sum) *1.0e-9;
	}    
    }until (abs($sum) < $error);
    return $r;
}


