package jamel.basicModel.banks; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.function.Consumer; import jamel.Jamel; import jamel.basicModel.households.Shareholder; import jamel.util.Agent; import jamel.util.AgentDataset; import jamel.util.JamelObject; import jamel.util.Sector; /** * A basic bank. */ public class BasicBank extends JamelObject implements Agent, Bank { /** * Returns the specified action. * * @param phaseName * the name of the action. * @return the specified action. */ static public Consumer<? super Agent> getAction(final String phaseName) { final Consumer<? super Agent> action; switch (phaseName) { case "opening": action = (agent) -> { ((BasicBank) agent).open(); }; break; case "payDividends": action = (agent) -> { ((BasicBank) agent).payDividends(); }; break; case "debtRecovery": action = (agent) -> { ((BasicBank) agent).debtRecovery(); }; break; case "closure": action = (agent) -> { ((BasicBank) agent).close(); }; break; default: throw new IllegalArgumentException(phaseName); } return action; } /** * The collection of accounts. */ private final List<BasicAccount> accounts = new ArrayList<>(); /** * The dataset. */ final private AgentDataset dataset; /** * The id of this bank. */ final private int id; /** * A flag that indicates whether this bank is open or not. */ private boolean open = false; /** * The amount of outstanding deposits. */ private final Amount outstandindDeposits = new Amount(); /** * The amount of outstanding loans. */ private final Amount outstandingLoans = new Amount(); /** * The owners of the firm. */ private List<Shareholder> owners = new LinkedList<>(); /** * The penalty rate, ie the interest rate for overdue debts. */ private final double penaltyRate = 0.02; /** * The interest rate. */ private final double rate = 0.01; /** * The parent sector. */ final private Sector sector; /** * Creates a new basic agent. * * @param sector * the parent sector. * @param id * the id of the agent. */ public BasicBank(final Sector sector, final int id) { super(sector.getSimulation()); this.sector = sector; this.id = id; this.dataset = new AgentDataset(this); } /** * For debugging purposes. */ @SuppressWarnings("unused") private void checkConsistency() { long sumDebts = 0; long sumDeposits = 0; for (BasicAccount account : this.accounts) { sumDebts += account.getDebt(); sumDeposits += account.getAmount(); } if (sumDebts != this.outstandingLoans.getAmount() || sumDeposits != this.outstandindDeposits.getAmount()) { Jamel.println(); Jamel.println("Debts", sumDebts, this.outstandingLoans.getAmount()); Jamel.println("Deposits", sumDeposits, this.outstandindDeposits.getAmount()); Jamel.println(); throw new RuntimeException("Inconsistency"); } } /** * Closes this agent. */ private void close() { if (!this.open) { throw new RuntimeException("Already closed."); } // this.checkConsistency(); // TODO debugging purposes / to be removed this.dataset.put("countAgent", 1); this.dataset.put("assets", this.outstandingLoans.getAmount()); this.dataset.put("liabilities", this.outstandindDeposits.getAmount()); this.open = false; } /** * Recovers due debts. */ private void debtRecovery() { // TODO Il serait bon que seuls les comptes entreprises soient passés en // revue ici... // checkConsistency(); for (int i = 0; i < this.accounts.size(); i++) { this.accounts.get(i).debtRecovery(this.penaltyRate); } // checkConsistency(); } /** * Initializes the owners of this bank. */ private void initOwners() { final Agent[] selection = this.getSimulation().getSector("Shareholders").select(10); for (int i = 0; i < selection.length; i++) { if (selection[i] != null) { this.owners.add((Shareholder) selection[i]); } } } /** * Opens this agent. */ private void open() { if (this.open) { throw new RuntimeException("Already open."); } if (this.owners.isEmpty()) { initOwners(); } this.open = true; } /** * The dividend payment phase. */ private void payDividends() { if (!this.open) { throw new RuntimeException("Closed."); } if (this.owners.isEmpty()) { throw new RuntimeException("No owners."); } final long assets = this.outstandingLoans.getAmount(); final long liabilities = this.outstandindDeposits.getAmount(); final long capital = assets - liabilities; final long capitalTarget = (long) (assets * 0.1); // TODO 0.1 should be a parameter final long capitalExcess = Math.max(capital - capitalTarget, 0); if (capitalExcess > this.owners.size()) { final long newDividend = capitalExcess / this.owners.size(); for (final Shareholder shareholder : this.owners) { shareholder.acceptDividendCheque(new BankCheque(this, shareholder, newDividend, this.getPeriod())); } } } /** * Creates and returns a new deposit account. * * @return a new deposit account. */ DepositAccount getNewDepositAccount() { return new DepositAccount(this.outstandindDeposits); } /** * Creates and returns a new loan account. * * @param deposit * a deposit account that will be linked with this loan account. * * @return a new loan account. */ LoanAccount getNewLoanAccount(DepositAccount deposit) { return new LoanAccount(deposit, this.outstandingLoans); } /** * Returns the penalty rate, ie the interest rate for overdue debts. * * @return the penalty rate. */ double getPenaltyRate() { return this.penaltyRate; } /** * Returns the nominal interest rate. * * @return the nominal interest rate. */ double getRate() { return this.rate; } @Override public Long getAssetTotalValue() { Jamel.notYetImplemented(); return null; } @Override public int getBorrowerStatus() { Jamel.notYetImplemented(); return 0; } @Override public Double getData(String dataKey, String period) { return this.dataset.getData(dataKey); } @Override public String getName() { return "Bank " + this.id; } @Override public Sector getSector() { return this.sector; } @Override public void goBankrupt() { Jamel.notYetImplemented(); } @Override public boolean isBankrupted() { Jamel.notYetImplemented(); return false; } @Override public boolean isSolvent() { Jamel.notYetImplemented(); return false; } @Override public Account openAccount(final AccountHolder accountHolder) { // TODO: il serait bon, selon la nature de AccountHolder, de délivrer // des comptes de nature différente. // Pour les ménages, un compte de dépôt simple. // pour les entreprises, un compte pouvant être débiteur. // Ils seraient classés dans deux listes différentes, ce qui permettrait // à la banque de ne passer en revue que les comptes débiteurs lors de // la phase de remboursement. final BasicAccount account = new BasicAccount(this, accountHolder); this.accounts.add(account); return account; } }