Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Branch: master
Fetching contributors…

Cannot retrieve contributors at this time

286 lines (213 sloc) 5.196 kB
use v6;
role Verbose::Level {
has Int $.ver;
}
class FinMag::Account {
has Str $.name is rw;
has Real $.balance is rw = 0;
has Date $!done_day;
has Real $!done_day_balance;
method reset_state(Date $start_day) {
$!done_day = $start_day - 1;
$!done_day_balance = 0;
}
method process_day( Date $day ) returns Real {
$!done_day = $day;
$!done_day_balance = 0;
return 0;
}
method transfer( Real $amount ) {
$.balance += $amount;
}
method get_balance {
return $.balance;
}
}
class FinMag::Account::Mortage is FinMag::Account {
has Real $.principal;
has Real $.interest;
has Real $.tax;
has Date $.from;
has Real $!day_interest;
has Date $!done_day;
has Real $!act_principal;
has Real $!interest_balance;
has Real $!tax_balance;
method init() {
$!day_interest = $.interest / 100 / 365;
}
method reset_state(Date $start_day) {
self.init;
$!done_day = $start_day - 1;
$!act_principal = $.principal;
$!interest_balance = 0;
$!tax_balance = 0;
}
method get_balance {
return -$!act_principal + $!interest_balance + $!tax_balance;
}
method process_day( Date $day ) returns Real {
return 0 if $.from > $day;
my Real $amount = 0;
# day interest
if $!done_day >= $day {
die "Can't compute back in time. The last day computed is $!done_day.";
}
if $.from == $day {
say " do not pay interest the first day of mortage";
} else {
my $int_amount = $!act_principal * $!day_interest;
say " day interest amount: $int_amount";
$!interest_balance -= $int_amount;
say " interest balance: $!interest_balance";
}
# tax
if $day.days-in-month == $day.day-of-month {
$!tax_balance -= $.tax;
say " tax balance: $!tax_balance";
$amount -= $.tax;
}
$!done_day = $day;
return $amount;
}
method transfer(
$amount is copy where { * > 0 }
) {
say " new transfer: $amount\n";
# Pay taxes.
if $amount <= -$!tax_balance {
$!tax_balance += $amount;
return 0;
}
$amount += $!tax_balance;
$!tax_balance = 0;
# Pay interests.
if $amount <= -$!interest_balance {
$!interest_balance += $amount;
return 0;
}
$amount += $!interest_balance;
$!interest_balance = 0;
# Put some money to principal.
if $amount <= $!act_principal {
$!act_principal -= $amount;
return 0;
}
$amount -= $!act_principal;
$!act_principal = 0;
return $amount;
}
}
class FinMag::Transfer
does Verbose::Level
{
has FinMag::Account $.from;
has FinMag::Account $.to;
has Real $.amount;
method process_transfer() {
my $availabel_amount = ( $.amount > $.from.balance )
?? $.from.balance
!! $.amount
;
$.from.transfer( -$availabel_amount );
$.to.transfer( +$availabel_amount );
return $availabel_amount;
}
}
class FinMag::Transfer::Regular is FinMag::Transfer {
has Date $.start_date;
has Date $.stop_date;
method process_day( Date $day ) {
return Real if $.start_date.defined && $day <= $.start_date;
return Real if $.stop_date.defined && $day >= $.stop_date;
# todo - no weekends
return Real unless self.is_pay_day( $day );
return self.process_transfer();
}
}
class FinMag::Transfer::Regular::Monthly is FinMag::Transfer::Regular {
has Int $.day_in_month;
method is_pay_day( Date $day ) {
return 0 if $.day_in_month != $day.day-of-month;
return 1;
}
}
class FinMag::Bag {
has Array @!items;
multi method add(FinMag::Account $item) {
push @!items, $item;
}
multi method add(FinMag::Transfer $item) {
push @!items, $item;
}
method get_items() {
return @!items;
}
method dump() {
for @!items -> $item {
say $item.perl;
}
}
}
class FinMag::Sim::Simulator
does Verbose::Level
{
has Date $.from;
has Date $.to;
has FinMag::Bag $.bag;
method compute() {
my @rule_items = $.bag.get_items();
my Date $day = $.from;
for @rule_items -> $rule_item {
next unless $rule_item.isa('FinMag::Account');
$rule_item.reset_state( $day );
}
while ( $day <= $.to ) {
say "date: $day" if $.ver >= 3;
for @rule_items -> $rule_item {
if $rule_item.isa('FinMag::Account') {
my Real $balance_update = $rule_item.process_day( $day );
say " {$rule_item.name} balance: {$rule_item.get_balance}" if $.ver >= 3;
say if $.ver >= 3;
}
if $rule_item.isa('FinMag::Transfer') {
$rule_item.process_day( $day );
}
}
$day += 1;
}
}
}
my $verbose_level = 10;
my FinMag::Bag $bag .= new;
my FinMag::Account $bank_account .= new(
name => 'bank account',
balance => 1_000_000,
);
$bag.add( $bank_account );
my FinMag::Account::Mortage $mortage .= new(
name => 'flat mortage',
principal => 860_201,
interest => 4.75, # percents
tax => 300, # monthly
from => Date.new('2009-01-16'),
);
$bag.add( $mortage );
my FinMag::Transfer::Regular::Monthly $pay_mortage .= new(
# todo name
start_date => Date.new('2009-02-21'),
stop_date => Date.new('2030-02-21'),
from => $bank_account,
to => $mortage,
day_in_month => 21,
amount => 8261,
);
$bag.add( $pay_mortage );
# $bag.dump;
my FinMag::Sim::Simulator $sim .= new(
bag => $bag,
from => Date.new('2009-01-01'),
to => Date.new('2012-03-31'),
ver => $verbose_level,
);
$sim.compute;
Jump to Line
Something went wrong with that request. Please try again.