Macroeconomy

Macroeconomy preview image

1 collaborator

Default-person Ander G (Author)

Tags

(This model has yet to be categorized with any tags)
Visible to everyone | Changeable by everyone
Model was written in NetLogo 6.1.1 • Viewed 66 times • Downloaded 11 times • Run 0 times
Download the 'Macroeconomy' modelDownload this modelEmbed this model

Do you have questions or comments about this model? Ask them here! (You'll first need to log in.)


Info tab cannot be displayed because of an encoding error

Comments and Questions

Please start the discussion about this model! (You'll first need to log in.)

Click to Run Model

;;;;;;;;;;;;;;;;;:;;;;;:;:;;;:::;:;:;:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; MACROECONOMY
;
;Copyright © 2020 Ander Genua Trullos
;
;This work is licensed under the Creative Commons Attribution 4.0 International License.
; To view a copy of this license, visit http://creativecommons.org/licenses/by/4.0/ or
; send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
;
;;;;;;;;;;;;;;;;;:;;;;;:;:;;;:::;:;:;:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

globals
[
  ; World parameters
  world::ticksPerMonth       ; Number of ticks in a month
  world::interactionRadius   ; Maximum distance at which agents can interact in the goods and jobs markets
  world::governInteractionRadius ; Maximum distance at which people can get a job in the govern
  world::initialPeopleCount  ; Initial number of people
  world::initialCompanyCount ; Initial number of companies
  world::initialBankCount    ; Initial number of banks
  world::initialMoneyForPeople       ; Initial amount of money assigned to the people
  world::initialMoneyForGovern       ; Initial amount of money assigned to the government
  world::initialMoneyForLoans        ; Initial amount of money that the central bank will use to loan
  world::salary              ; Amount of money paid by the companies to their employees each tick
  world::salaryIncrease      ; Amount that offered salaries will be increased each tick until the position has been filled
  world::unemploymentBenefit ; Amount of money paid by the govern to each unemployed person.
  world::stockPrice          ; Amount of money that each stock costs
  world::technology          ; Companies' efficiency to produce goods
  world::technologyImprovementPerGovernmentWorker  ; Efficiency increase per tick and per government worker due to the positive externalities of government jobs
  world::minCapital          ; Minimum amount of capital companies must have to run.
  world::capitalToWorkRatio  ; Most efficent amount of capital for each worker
  world::bondsPerWorker      ; Number of bonds a bank worker can handle
  world::baseConsumption     ; Minimum amount of goods that the people will try to consume each tick
  world::peopleReservesTargetTicks   ; Amount of ticks that the people's reserves are designed to last
  world::companyReservesTargetTicks  ; Amount of ticks that the companies' reserves are designed to last
  world::governReservesTargetTicks   ; Amount of ticks that the govern's reserves are designed to last
  world::bankReservesTargetTicks     ; Amount of ticks that the bank's reserves are designed to last
  world::corporationTax      ; Corporation tax rate
  world::incomeTax           ; Income tax rate
  world::consumptionTax      ; Consumption tax rate
  world::bondTerm            ; Time elapsed from the adquisition of a bond and its maturity in ticks
  world::bondFaceValue       ; Face value of each bond. All bond purchases and sales are done at this price.
  world::defaultCoupon       ; Initial value for bond coupons (per tick)
  world::couponIncrease      ; Amount that bond coupons will increase per tick when they cannot be sold
  world::loanTerm            ; Time elapsed from the adquisition of a loan and its maturity in ticks
  world::loanFaceValue       ; Face value of each loan. All loan purchases and sales are done at this price.
  world::centralBankPolicyMaturity   ; Amount of ticks that are required for the amount of money to reach is target value
  world::monthlyInflationTarget      ; Monthly inflation rate that the central bank tries to mantain
  world::centralBankPolicyFactor     ; Indicates how much will vary the amount of money each time the central bank takes action
  world::floatMax            ; A big number to calculate minimum values

  world::governId            ; Turtle id of the govern
  world::centralBankId       ; Turgle id of the central bank

  ; World styles: element colors and shapes
  style::personColor         ; Color of the person breed turtles
  style::personShape         ; Shape of the person breed turtles
  style::companyColor        ; Color of the company breed turtles
  style::companyShape        ; Shape of the company breed turtles
  style::governColor         ; Color of the govern breed turtles
  style::governShape         ; Shape of the govern breed turtles
  style::bankColor           ; Color of the bank breed turtles
  style::bankShape           ; Shape of the bank breed turtles
  style::centralbankColor    ; Color of the central bank breed turtles
  style::centralbankShape    ; Shape of the central bank breed turtles
  style::consumptionColor    ; Color of the consumption relationship lines
  style::jobColor            ; Color of the job relationship lines
  style::investmentColor     ; Color of the investment relationship lines
  style::bondColor           ; Color of the bond relationship lines
  style::loanColor           ; Color of the loan relationship lines

  ; World variables
  world::goodsPriceAvg          ; Average price paid for the goods in the last full month
  world::goodsPriceMinCurrent   ; Minimum price paid for the goods in the current month
  world::goodsPriceMaxCurrent   ; Maximum price paid for the goods in the current month

  world::bankCountValues                                 ; List containing all the historic values for the variables.
  world::coeRealValues                                   ; "
  world::coeNominalValues                                ; "
  world::companyCountValues                              ; "
  world::consumptionNominalValues                        ; "
  world::consumptionRealValues                           ; "
  world::gdpNominalValues                                ; "
  world::gdpRealValues                                   ; "
  world::goodsPriceAvgValues                             ; "
  world::goodsPriceMinValues                             ; "
  world::goodsPriceMaxValues                             ; "
  world::goodsProducedValues                             ; "
  world::goodsSoldValues                                 ; "
  world::gosNominalValues                                ; "
  world::gosRealValues                                   ; "
  world::governConsumptionTaxIncomeNominalValues         ; "
  world::governCorporationTaxIncomeNominalValues         ; "
  world::governFinancialExpenseValues                    ; "
  world::governIncomeNominalValues                       ; "
  world::governIncomeTaxIncomeNominalValues              ; "
  world::governSalariesNominalValues                     ; "
  world::governUnemploymentBenefitExpensesNominalValues  ; "
  world::investmentNominalValues                         ; "
  world::investmentRealValues                            ; "
  world::m1Values                                        ; "
  world::m1TargetValues                                  ; "
  world::priceIndexValues                                ; "
  world::publicExpenditureNominalValues                  ; "
  world::salaryAvgValues                                 ; "
  world::salaryMaxValues                                 ; "
  world::salaryMinValues                                 ; "
  world::technologyValues                                ; "
  world::unemploymentValues                              ; "

  world::bankCountBase          ; Base value used to make the index used to plot the variable.
  world::companyCountBase       ; "
  world::gdpBase                ; "
  world::goodsPriceBase         ; "
  world::goodsProducedBase      ; "
  world::m1Base                 ; "
  world::priceIndexBase         ; "
  world::salaryBase             ; "
  world::technologyBase         ; "


  ; Classify management
  people::currentClassifyBy     ; Current classify attribute for people
  companies::currentClassifyBy  ; Current classify attribute for companies
  banks::currentClassifyBy      ; Current classify attribute for banks

  ; Plot Management
  plot1::bluePen::variableCurrent   ; Variable currently being displayed with the blue pen in the plot 1.
  plot1::greenPen::variableCurrent  ; Variable currently being displayed with the green pen in the plot 1.
  plot1::pinkPen::variableCurrent   ; Variable currently being displayed with the pink pen in the plot 1.
  plot2::bluePen::variableCurrent   ; Variable currently being displayed with the blue pen in the plot 2.
  plot2::greenPen::variableCurrent  ; Variable currently being displayed with the green pen in the plot 2.
  plot2::pinkPen::variableCurrent   ; Variable currently being displayed with the pink pen in the plot 2.
  plot3::bluePen::variableCurrent   ; Variable currently being displayed with the blue pen in the plot 3.
  plot3::greenPen::variableCurrent  ; Variable currently being displayed with the green pen in the plot 3.
  plot3::pinkPen::variableCurrent   ; Variable currently being displayed with the pink pen in the plot 3.
  plot4::bluePen::variableCurrent   ; Variable currently being displayed with the blue pen in the plot 4.
  plot4::greenPen::variableCurrent  ; Variable currently being displayed with the green pen in the plot 4.
  plot4::pinkPen::variableCurrent   ; Variable currently being displayed with the pink pen in the plot 4.

  peoplePlot::name        ; Name of the plot used to show people classification
  companyPlot::name       ; Name of the plot used to show company classification
  bankPlot::name          ; Name of the plot used to show bank classification
  plot1::name             ; Name of plot 1
  plot2::name             ; Name of plot 1
  plot3::name             ; Name of plot 1
  plot4::name             ; Name of plot 1

  ; Selection management
  selection::selectedPatch           ; Currently selected patch
  selection::selectedAgent           ; Currently selected agent
  selection::currentPatch            ; Patch where the mouse is over
  selection::interactionRadius       ; Interaction radius of the current selected agent
  selection::currentShowConsumptions ; Indicates if currently the consumption relationships are shown for the selected agent.
  selection::currentShowJobs         ; Indicates if currently the employment relationships are shown for the selected agent.
  selection::currentShowInvestments  ; Indicates if currently the investment relationships are shown for the selected agent.
  selection::currentShowBonds        ; Indicates if currently the money loan relationships between companies and banks are shown for the selected agent.
  selection::currentShowLoans        ; Indicates if currently the money loan relationships between banks and the central bank are shown for the selected agent.

  selection::colors::selected        ; Color for selected patches
  selection::colors::mouseOver       ; Color for patches where the mouse is over
  selection::colors::selectedRadious ; Color for patches that can interact with the selected agent
  selection::colors::notSelected     ; Default patch color
]


breed [people person]
people-own
[
  person::actionTick                ; Tick of the month when the agent will make an strategic action.
  person::color                     ; Variable used to calculate the affinity between a person and a company.
  person::cash                      ; Amount of cash that the person has.
  person::targetCash                ; Amount of money in reserves desired by the person.
  person::employed                  ; Indicates if the person is employed (1) or not (0).
  person::propensityToConsume       ; Indicates how much of the income will be dedicated to consumption instead of savings
  person::stocks                    ; Number of stocks the person has.
  person::targetStocks              ; Number of stocks the person wants to have.

  person::last::consumedGoods       ; Amount of goods consumed in the previous full month.
  person::last::consumedGoodsValue  ; Value of the goods consumed in the previous full month.
  person::last::workIncome          ; Income received as salary in the previous full month.
  person::last::investmentIncome    ; Income received as dividend in the previous full month.
  person::last::transferIncome      ; Income received as government transfer in the previous full month.

  person::consumedGoods             ; Amount of goods already consumed in the current month.
  person::consumedGoodsValue        ; Value of the goods already consumed in the current month.
  person::workIncome                ; Income received as salary in the current month.
  person::investmentIncome          ; Income received as dividend in the current month.
  person::transferIncome            ; Income received as government transfer in the current month.
]


breed [companies company]
companies-own
[
  company::actionTick            ; Tick of the month when the agent will make an strategic action.
  company::age                   ; Age in months of the company.
  company::color                 ; Variable used to calculate the affinity between a person and a company.
  company::workers               ; Number of workers on the company.
  company::vacancies             ; Number of empty workplaces.
  company::salary                ; Salary offered to the new workers
  company::lastSalary            ; Salary paid to the last hired worker
  company::maxSalary             ; Maximun salary that the company can afford
  company::stocks                ; Number of stocks the company has sucessfully sold.
  company::stocksOnSale          ; Number of stocks the company wants to sell.
  company::cash                  ; Amount of money that the company has
  company::targetCash            ; The amount of cash the company wants to have
  company::nonCurrentAssets      ; Amount of capital that he company uses to produce
  company::inventory             ; Amount of goods in the company's warehouse.
  company::inventoryValue        ; Value of the goods in the company's warehouse.
  company::inventoryUnitaryValue ; Value of each good in the company's warehouse.
  company::salePrice             ; Price at which the company sells goods (including taxes).
  company::stockProfitability    ; Expected profitability of the company's stocks
  company::bonds                 ; Number of bonds currently active
  company::targetBonds           ; Number of bonds the company wants to have
  company::coupon                ; Value of the coupon per tick offered for the bonds on sale
  company::lastCoupon            ; Coupon paid in the last sold bond
  company::maxCoupon             ; Maximun coupon value that the company can afford
  company::risk                  ; Risk value of the company
  company::targetDebtRatio       ; Debt ratio that the company wants to have

  company::last::production        ; Amount of goods produced by the company in the last full month.
  company::last::productionValue   ; Value of the goods produced by the company in the last full month (value includes taxes).
  company::last::demand            ; Amount of goods demanded to the company in the last full month.
  company::last::sales             ; Amount of goods sold by the company in the last full month.
  company::last::salesValue        ; Value of the goods sold by the company in the last full month.
  company::last::laborCosts        ; Cost of labor in the last full month.
  company::last::operatingProfit   ; Operating result in the last full month.
  company::last::financialExpense  ; Cost of debt in the last full month
  company::last::profitBeforeTax   ; Earnings before taxes in the last full month
  company::last::result            ; Result of the company in the last full month

  company::last::nonCurrentAssets  ; Value of the capital used to produce in the previous month
  company::last::inventory         ; Amount of the stored goods in the previous month
  company::last::inventoryValue    ; Cost value of the stored goods in the previous month
  company::last::investment        ; Amount of money that has been invested in the previous month on non-current assets and inventory (with market value including taxes)

  company::production       ; Amount of goods already produced by the company in the current month.
  company::productionValue  ; Value of goods already produced by the company in the current month.
  company::demand           ; Amount of goods already demanded to the company in the current month.
  company::sales            ; Amount of goods already sold by the company in the current month.
  company::salesValue       ; Value of the goods already sold by the company in the current month.
  company::laborCosts       ; Cost of labor in the current month.
  company::financialExpense ; Cost of debt in the current month.
]


breed [governs govern]
governs-own
[
  govern::workers               ; Number of workers on the govern.
  govern::vacancies             ; Number of empty workplaces.
  govern::cash                  ; Amount of money that the govern has
  govern::targetCash            ; The amount of cash the govern wants to have
  govern::salary                ; Nominal amount of money that the govern pays its workers.
  govern::unemploymentBenefit   ; Nominal amount of money that the govern pays unemployed people.
  govern::bonds                 ; Number of bonds the govern has.
  govern::bondsOnSale           ; Number of bonds the govern wants to sale.
  govern::coupon                ; Currently offered coupon for the bonds.
  govern::lastCoupon            ; Coupon of the last sold bond.
  govern::loans                 ; Numer of rescue loans received from the central bank
  govern::risk                  ; Risk of the govern. It's always 1 (none) because the central bank guarantees the bond holder its money

  govern::incomeFromCorporationTax  ; Income received from the corporation tax in the current month.
  govern::incomeFromIncomeTax       ; Income received from the people income tax in the current month.
  govern::incomeFromConsumptionTax  ; Income received from the consumption tax in the current month.
  govern::laborCosts                ; Cost of labor in the current month.
  govern::transferCosts             ; Cost of transfers to unemployed people in the current month.
  govern::financialExpense            ; Cost of bonds in the current month.

  govern::last::incomeFromCorporationTax ; Income received from the corporation tax in the last full month
  govern::last::incomeFromIncomeTax      ; Income received from the people income tax in the last full month.
  govern::last::incomeFromConsumptionTax ; Income received from the consumption tax in the last full month.
  govern::last::laborCosts               ; Cost of labor in the last full month.
  govern::last::transferCosts            ; Cost of transfers to unemployed people in the last full month.
  govern::last::financialExpense           ; Cost of bonds in the last full month.
]


breed [banks bank]
banks-own
[
  bank::actionTick            ; Tick of the month when the agent will make an strategic action.
  bank::age                   ; Age in months of the bank.
  bank::workers               ; Number of workers on the bank.
  bank::vacancies             ; Number of empty workplaces.
  bank::salary                ; Salary offered to the new workers
  bank::lastSalary            ; Salary paid to the last hired worker
  bank::maxSalary             ; Maximun salary that the company can afford
  bank::stocks                ; Number of stocks the bank has sucessfully sold.
  bank::stocksOnSale          ; Number of stocks the bank wants to sell.
  bank::loans                 ; Amount of loans the bank has
  bank::targetLoans           ; Amount of loans the bank wants to have
  bank::cash                  ; Amount of money that the bank has
  bank::targetCash            ; Amount of cash the bank wants to have
  bank::bonds                 ; Amount of bonds the bank has
  bank::targetBonds           ; Amount of bonds the bank wants to have
  bank::minBondCoupon         ; Minimum coupon value for bonds
  bank::nonCurrentAssets      ; Value of the current bonds
  bank::risk                  ; Risk level of the bonds the bank has
  bank::targetRisk            ; Risk level that the bank wants to operate with
  bank::targetDebtRatio       ; Debt ratio that the bank wants to have
  bank::stockProfitability    ; Expected profitability of the bank's stocks

  bank::last::laborCosts        ; Cost of labor in the last full month.
  bank::last::operatingProfit   ; Operating result in the last full month.
  bank::last::financialIncome   ; Coupons received in the last full month
  bank::last::financialExpense  ; Unpaid bonds in the last full month
  bank::last::profitBeforeTax   ; Earnings before taxes in the last full month
  bank::last::result            ; Result of the bank in the last full month


  bank::laborCosts       ; Cost of labor in the current month.
  bank::financialIncome  ; Coupons received in the last full month
  bank::financialExpense ; Unpaid bonds in the last full month
]


breed [centralbanks centralbank]
centralbanks-own
[
  centralBank::loanOffer
  centralBank::M1
  centralBank::targetM1
]

; Relationship between a person and the companies from which it has consumed goods in the last full month
undirected-link-breed [consumptions consumption]
consumptions-own
[
  consumption::amount    ; Amount of goods consumed
  consumption::value     ; Value of the consumed goods
]


; Relationship between a person and the companies from which it has consumed goods in the last full month
undirected-link-breed [currentConsumptions currentConsumption]
currentConsumptions-own
[
  currentConsumption::amount    ; Amount of goods consumed
  currentConsumption::value     ; Value of the consumed goods
]


; Employment relationship between a person and the agent (company, government, bank) where it works (the end1 and end2 may vary)
undirected-link-breed [jobs job]
jobs-own
[
  job::salary                  ; Money paid by the employer to the employee
]

; Investor relationship between a company (end2) and its stockholders (end1)
undirected-link-breed [investments investment]
investments-own
[
  investment::stocks
]

; Bond relationships between companies and banks
undirected-link-breed [bonds bond]
bonds-own
[
  bond::bondCount     ; Number of bonds of a company that a bank has
  bond::maturities    ; Queue of maurity dates of all bonds in the relationship
  bond::coupons       ; Queue of coupon values for all bonds in the relationship.
]

; Loan relationships between banks and the central bank
undirected-link-breed [loans loan]
loans-own
[
  loan::loanCount     ; Number of loans the bank has
  loan::maturities    ; Queue of maurity dates of all loans in the relationship
]


; Initialize the world

to Setup
  clear-all

  random-seed 137  ; If uncommented the same execution can be repeated over and over

  ; Initialize world parameters
  set world::ticksPerMonth 30                                    ; Number of ticks in a month
  set world::interactionRadius 10                                ; Maximum distance at which agents can interact in the goods and jobs markets
  set world::governInteractionRadius 25                          ; Maximum distance at which people can get a job in the govern
  set world::initialPeopleCount 2150                             ; Initial number of people
  set world::initialCompanyCount 100                             ; Initial number of companies
  set world::initialBankCount 15                                 ; Initial number of banks
  set world::initialMoneyForPeople 16501250                      ; Initial amount of money assigned to the people
  set world::initialMoneyForGovern  2750000                      ; Initial amount of money assigned to the government
  set world::initialMoneyForLoans   4000000                      ; Initial amount of money that the central bank will use to loan
  set world::salary 50                                           ; Amount of money paid by the companies to their employees each tick
  set world::salaryIncrease 0.5                                  ; Amount that offered salaries will be increased each tick until the position has been filled
  set world::unemploymentBenefit 35                              ; Amount of money paid by the govern to each unemployed person.
  set world::stockPrice 500                                      ; Amount of money that each stock costs
  set world::technology 60                                       ; Companies' efficiency to produce goods
  set world::technologyImprovementPerGovernmentWorker 0.0000001  ; Efficiency increase per tick and per government worker due to the positive externalities of government jobs
  set world::minCapital 500                                      ; Minimum amount of capital companies must have to run.
  set world::CapitalToWorkRatio 5000                             ; Most efficent amount of capital for each worker
  set world::bondsPerWorker  75                                  ; Number of bonds a bank worker can handle
  set world::baseConsumption 35                                  ; Minimum amount of goods that the people will try to consume each tick
  set world::peopleReservesTargetTicks  90                       ; Amount of ticks that the people's reserves are designed to last
  set world::companyReservesTargetTicks 45                       ; Amount of ticks that the companies' reserves are designed to last
  set world::governReservesTargetTicks  30                       ; Amount of ticks that the govern's reserves are designed to last
  set world::bankReservesTargetTicks    45                       ; Amount of ticks that the bank's reserves are designed to last
  set world::corporationTax  0.20                                ; Corporation tax rate
  set world::incomeTax       0.10                                ; Income tax rate
  set world::consumptionTax  0.12                                ; Consumption tax rate
  set world::bondTerm 180                                        ; Time elapsed from the adquisition of a bond and its maturity in ticks
  set world::bondFaceValue 500                                   ; Face value of each bond. All bond purchases and sales are done at this price.
  set world::defaultCoupon  1.2                                  ; Initial value for bond coupons (per tick)
  set world::couponIncrease 0.02                                 ; Amount that bond coupons will increase per tick when they cannot be sold
  set world::loanTerm 180                                        ; Time elapsed from the adquisition of a loan and its maturity in ticks
  set world::loanFaceValue 500                                   ; Face value of each loan. All loan purchases and sales are done at this price.
  set world::centralBankPolicyMaturity 90                        ; Amount of ticks that are required for the amount of money to reach is target value
  set world::monthlyInflationTarget 0.02                         ; Monthly inflation rate that the central bank tries to mantain
  set world::centralBankPolicyFactor 0.05                        ; Indicates how much will vary the amount of money each time the central bank takes action


  set world::floatMax 1E32

  charts::Initialize

  selection::Initialize

  ; World styles: element colors and shapes
  set style::personColor      sky
  set style::personShape      "person"
  set style::companyColor     sky
  set style::companyShape     "plant"
  set style::governColor      pink
  set style::governShape      "govern"
  set style::bankColor        sky
  set style::bankShape        "bank"
  set style::centralbankColor pink
  set style::centralbankShape "central bank"
  set style::consumptionColor yellow
  set style::jobColor         magenta
  set style::investmentColor  orange
  set style::bondColor        brown
  set style::loanColor        violet

  ; Event initialization
  set event ""

  ; Create agents
  create-governs 1
  [
    set world::governId self
    govern::Create world::initialMoneyForGovern
  ]
  ; Create agents
  create-centralBanks 1
  [
    set world::centralBankId self
    centralBank::Create ( world::initialMoneyForPeople + world::initialMoneyForGovern ) ( world::initialMoneyForPeople + world::initialMoneyForGovern + world::initialMoneyForLoans )

  ]

  let personCash  world::Round2 ( world::initialMoneyForPeople / world::initialPeopleCount )
  create-people world::initialPeopleCount [ person::Create personCash ]

  create-companies world::initialCompanyCount [ company::Create ]

  create-banks world::initialBankCount [ bank::Create ]

  reset-ticks

  ; Initial assignment of loans, bonds, investors and workers
  banks::Initialize

  ask people
  [
    person::BuyStocks
    person::GetJob
  ]

  ask companies
  [ ; Update the corrent assests historical value to the current one
    set company::last::nonCurrentAssets company::nonCurrentAssets
  ]

  ask world::governId
  [ ; Update the govern target cash
    govern::CalculateTargetCash
  ]
end 


; Execute the simulation

to Go
  let tickOfMonth ticks mod world::ticksPerMonth
  let month floor( ticks / world::ticksPerMonth)

  ; Event management
  Event::HandleEvents month

  ; Make operational actions
  ask people
  [
    person::OperationalAction
  ]

  ask companies
  [
    company::OperationalAction
  ]

  ask world::governId
  [
    govern::OperationalAction
  ]

  ask banks
  [
    bank::OperationalAction
  ]

  ask world::centralBankId
  [
    centralBank::OperationalAction
  ]


  ; Make strategic actions (not in the first month, wait to see some results)
  if month > 0
  [
    ask people with [ person::actionTick = tickOfMonth ]
    [
      person::StrategicAction
    ]

    ask companies with [ company::actionTick = tickOfMonth ]
    [
      company::StrategicAction
    ]

    ask banks with [ bank::actionTick = tickOfMonth ]
    [
      bank::StrategicAction
    ]

    if tickOfMonth = 29
    [
      ask world::governId
      [
        govern::StrategicAction
      ]
      ask world::centralBankId
      [
        centralBank::StrategicAction
      ]
    ]
  ]

  ; Redraw plots if the user has changed the selection
  plots::Redraw

  ; A month has gone by, update monthly stats
  if tickOfMonth = 29
  [
    ask people
    [
      person::MonthlyAction
    ]

    ask companies
    [
      company::MonthlyAction
    ]

    ask world::governId
    [
      govern::MonthlyAction
    ]

    ask banks
    [
      bank::MonthlyAction
    ]

    ask world::centralBankId
    [
      centralBank::MonthlyAction
    ]

    world::UpdateVariables
    plots::Update
  ]

  ; Redraw histograms if the user has changed the selection
  people::Classify
  companies::Classify
  banks::Classify

  ; Check if the user has mouseclicked
  selection::MouseSelect true

  tick

  if ticks > 0 and ticks mod ( 50 * world::ticksPerMonth) = 0 ; Auto-stop the simulation every 50 months
  [
    stop
  ]
end 


; Interact with the model without running the simulation

to ShowInfo

  ; Redraw plots if the user has changed the selection
  plots::Redraw

  ; Redraw histograms if the user has changed the selection
  people::Classify
  companies::Classify
  banks::Classify

  ; Check if the user has mouseclicked
  selection::MouseSelect false

  ; Force the screen to be updated whithout a tick
  display
end 



;;;;;;;;;;;;;;;;;:;;;;;:;:;;;:::;:;:;:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; CONSUMPTION RELATIONSHIP MANAGEMENT
;

; Create a consumption relationship that represents the consumptions done in the last full month.

to consumption::Create [ amount value ]

  set hidden? not( selection::currentShowConsumptions and ( end1 = selection::selectedAgent or end2 = selection::selectedAgent ) )
  set color style::consumptionColor

  set consumption::amount amount
  set consumption::value  value
end 

; Create a current consumption relationship that represents the consumptions being done in the current month.

to currentConsumption::Create

  set hidden? true
  set color style::consumptionColor

  set currentConsumption::amount 0
  set currentConsumption::value  0
end 


;;;;;;;;;;;;;;;;;:;;;;;:;:;;;:::;:;:;:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; JOB RELATIONSHIP MANAGEMENT
;

; Create a job relationship

to job::Create [ salary ]


  set hidden? not ( selection::currentShowJobs and ( end1 = selection::selectedAgent or end2 = selection::selectedAgent ) )
  set color style::jobColor

  set job::salary salary
end 


;;;;;;;;;;;;;;;;;:;;;;;:;:;;;:::;:;:;:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; INVESTMENT RELATIONSHIP MANAGEMENT
;

; Create an investment relationship

to investment::Create

  set hidden? not ( selection::currentShowInvestments and ( end1 = selection::selectedAgent or end2 = selection::selectedAgent ) )
  set color style::investmentColor

  set investment::stocks 0
end 

;;;;;;;;;;;;;;;;;:;;;;;:;:;;;:::;:;:;:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; BOND RELATIONSHIP MANAGEMENT
;

to bond::Create

  set hidden? not ( selection::currentShowBonds and ( end1 = selection::selectedAgent or end2 = selection::selectedAgent ) )
  set color style::bondColor

  set bond::bondCount 0
  set bond::maturities []
  set bond::coupons []
end 

to bond::AddBond[ coupon ]

  set bond::bondCount bond::bondCount + 1

  set bond::maturities lput (ticks + world::bondTerm) bond::maturities
  set bond::coupons lput coupon bond::coupons
end 

to bond::AddInitialBond[ nBonds coupon ]

  set bond::bondCount bond::bondCount + nBonds


  while [nBonds > 0]
  [
    let maturity 30 + random world::bondTerm

    set bond::maturities lput maturity bond::maturities
    set bond::coupons lput coupon bond::coupons

    set nBonds nBonds - 1
  ]
end 

to bond::RemoveBond
  set bond::bondCount bond::bondCount - 1

  set bond::maturities but-first bond::maturities
  set bond::coupons but-first bond::coupons
end 

to-report bond::GetDebtCost [ reservesTargetTicks ]

  let term ticks + reservesTargetTicks
  let totalCoupon 0
  let totalPrincipal 0

  (foreach bond::maturities bond::coupons
    [ [mat coup] ->
      if-else mat < term
      [
        set totalPrincipal totalPrincipal + world::bondFaceValue
        set totalCoupon totalCoupon + coup * (term - mat )
      ]
      [
        set totalCoupon totalCoupon + coup * world::companyReservesTargetTicks
      ]
    ])

  report totalCoupon + totalPrincipal
end 

to-report bond::GetFinancialCost [ reservesTargetTicks ]
  let term ticks + reservesTargetTicks
  let totalCoupon 0

  (foreach bond::maturities bond::coupons
    [ [mat coup] ->
      if-else mat < term
      [
        set totalCoupon totalCoupon + coup * (term - mat )
      ]
      [
        set totalCoupon totalCoupon + coup * reservesTargetTicks
      ]
    ]
  )

  report totalCoupon
end 

;;;;;;;;;;;;;;;;;:;;;;;:;:;;;:::;:;:;:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; LOAN RELATIONSHIP MANAGEMENT
;

to loan::Create

  set hidden? not ( selection::currentShowLoans and ( end1 = selection::selectedAgent or end2 = selection::selectedAgent ) )
  set color style::loanColor

  set loan::loanCount 0
  set loan::maturities []
end 

to loan::AddLoan [ nLoans ]

  set loan::loanCount loan::loanCount + nLoans

  while [nLoans > 0]
  [
    set loan::maturities lput (ticks + world::loanTerm) loan::maturities
    set nLoans nLoans - 1
  ]
end 

to loan::AddInitialLoan [ nLoans ]

  set loan::loanCount loan::loanCount + nLoans

  while [nLoans > 0]
  [
    let maturity 30 + random world::loanTerm

    set loan::maturities lput maturity loan::maturities
    set nLoans nLoans - 1
  ]
end 

to loan::RemoveLoan

  set loan::loanCount loan::loanCount - 1
  set loan::maturities but-first loan::maturities
end 

to-report loan::GetDebtCost [ reservesTargetTicks ]

  let term ticks + reservesTargetTicks
  let totalPrincipal 0

  (foreach loan::maturities
    [ [mat] ->
      if mat < term [ set totalPrincipal totalPrincipal + world::loanFaceValue]
    ])

  report totalPrincipal
end 

;;;;;;;;;;;;;;;;;:;;;;;:;:;;;:::;:;:;:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; WORLD MANAGEMENT
;

; Updates the macroeconomic variables

to world::UpdateVariables
  let isFirstUpdate floor ( ticks / world::ticksPerMonth ) = 0


  ; Calculate first the price index which is needed for other variables too

  ; Price Index
  let priceIndex sum [ company::salePrice * company::last::production ] of companies / sum [company::last::production] of companies

  if isFirstUpdate
  [
    set world::PriceIndexBase priceIndex
  ]
  let nominalToRealPrices world::PriceIndexBase / priceIndex


  ;;; Calculate the values of the rest of the macroeconomic variables

  ; Company Count
  let bankCount count banks

  ; Compensation of employees Nominal
  let coeNominal sum [person::last::workIncome] of people

  ; Compensation of employees Real
  let coeReal coeNominal * nominalToRealPrices

  ; Company Count
  let companyCount count companies

  ; Consumption Nominal
  let consumptionNominal sum [person::last::consumedGoodsValue] of people

  ; Consumption Real
  let consumptionReal consumptionNominal * nominalToRealPrices

  ; GDP Nominal
  let gdpNominal sum [ company::last::production  * company::salePrice ] of companies

  ; GDP Real
  let gdpReal gdpNominal * nominalToRealPrices

  ; Govern Consumption Tax Income
  let governConsumptionTaxIncomeNominal [ govern::last::incomeFromConsumptionTax ] of world::governId

  ; Govern Corporation Tax Income
  let governCorporationTaxIncomeNominal [ govern::last::incomeFromCorporationTax ] of world::governId

  ; Govern Financial Expense
  let governFinancialExpenseNominal [ govern::last::financialExpense ] of world::governId

  ; Govern Income
  let governIncomeNominal [ govern::last::IncomeFromConsumptionTax + govern::last::IncomeFromIncomeTax + govern::last::IncomeFromCorporationTax ] of world::governId

  ; Govern Income Tax Income
  let governIncomeTaxIncomeNominal [ govern::last::IncomeFromIncomeTax ] of world::governId

  ; Govern Salaries
  let governSalariesNominal [ govern::last::laborCosts ] of world::governId

  ; Govern Unemployment Benefit Expenses
  let governUnemploymentBenefitExpensesNominal [ govern::last::transferCosts ] of world::governId

  ; Goods average consumption price
  set world::goodsPriceAvg sum [person::last::consumedGoodsValue] of people / sum [person::last::consumedGoods] of people

  ; Goods Minimum price
  let goodsPriceMin world::goodsPriceMinCurrent
  set world::goodsPriceMinCurrent world::floatMax

  ; Goods Maximum price
  let goodsPriceMax world::goodsPriceMaxCurrent
  set world::goodsPriceMaxCurrent 0

  ; Goods Produced
  let goodsProduced sum [ company::last::production ] of companies

  ; Goods Sold
  let goodsSold sum [ company::last::sales ] of companies

  ; Gross Operating surplus Nominal
  let gosNominal sum [person::last::investmentIncome] of people

  ; Gross Operating surplus Real
  let gosReal gosNominal * nominalToRealPrices

  ; Investment Nominal
  let investmentNominal sum [ company::last::investment ] of companies

  ; Investment Real
  let investmentReal investmentNominal * nominalToRealPrices

  ; M1
  let m1 [ centralBank::M1 ] of world::centralBankId

  ; Public Expenditure
  let publicExpenditureNominal [ govern::last::laborCosts + govern::last::transferCosts + govern::last::financialExpense ] of world::governId

  ; Salary Average
  let salaryAvg sum [ job::salary ] of jobs / count jobs

  ; Salary Maximum
  let salaryMax max [ job::salary ] of jobs

  ; Salary Minimum
  let salaryMin min[ job::salary ] of jobs

  ; Unemployment
  let unemployment count people with [ person::employed = 0 ] / count people


  ;;; Set base values
  if isFirstUpdate
  [
    set world::bankCountBase bankCount
    set world::companyCountBase companyCount
    set world::gdpBase gdpNominal
    set world::goodsPriceBase world::goodsPriceAvg
    set world::goodsProducedBase goodsProduced
    set world::m1Base m1
    set world::salaryBase salaryAvg
    set world::technologyBase world::technology
  ]


  ;;; Normalize values and add them to the list

  ; Bank Count
  let bankCountValue bankCount * 100 / world::bankCountBase
  set world::bankCountValues lput bankCountValue world::bankCountValues

  ; Compensation of employees Nominal
  let normalizedCOENominal coeNominal * 100 / world::gdpBase
  set world::coeNominalValues lput normalizedCOENominal world::coeNominalValues

  ; Compensation of employees Real
  let normalizedCOEReal coeReal * 100 / world::gdpBase
  set world::coeRealValues lput normalizedCOEReal world::coeRealValues

  ; Consumption Nominal
  let normalizedConsumptionNominal consumptionNominal * 100 / world::gdpBase
  set world::consumptionNominalValues lput normalizedConsumptionNominal world::consumptionNominalValues

  ; Consumption Real
  let normalizedConsumptionReal consumptionReal * 100 / world::gdpBase
  set world::consumptionRealValues lput normalizedConsumptionReal world::consumptionRealValues

  ; Company Count

  ; GDP Nominal
  let normalizedGDPNominal gdpNominal * 100 / world::gdpBase
  let companyCountValue companyCount * 100 / world::CompanyCountBase
  set world::companyCountValues lput companyCountValue world::companyCountValues
  set world::gdpNominalValues lput normalizedGDPNominal world::gdpNominalValues

  ; GDP Real
  let normalizedGdpReal gdpReal * 100 / world::gdpBase
  set world::gdpRealValues lput normalizedGdpReal world::gdpRealValues

  ; Goods Average price
  let normalizedGoodsPriceAvg world::goodsPriceAvg * 100 / world::goodsPriceBase
  set world::goodsPriceAvgValues lput normalizedGoodsPriceAvg world::goodsPriceAvgValues

  ; Goods Minimum price
  let normalizedGoodsPriceMin goodsPriceMin * 100 / world::goodsPriceBase
  set world::goodsPriceMinValues lput normalizedGoodsPriceMin world::goodsPriceMinValues

  ; Goods Maximum price
  let normalizedGoodsPriceMax goodsPriceMax * 100 / world::goodsPriceBase
  set world::goodsPriceMaxValues lput normalizedGoodsPriceMax world::goodsPriceMaxValues

  ; Goods Produced
  let normalizedGoodsProducedValues goodsProduced * 100 / world::goodsProducedBase
  set world::goodsProducedValues lput normalizedGoodsProducedValues world::goodsProducedValues

  ; Goods Sold
  let normalizedGoodsSoldValues goodsSold * 100 / world::goodsProducedBase
  set world::goodsSoldValues lput normalizedGoodsSoldValues world::goodsSoldValues

  ; Govern Consumption Tax Income
  let normalizedGovernConsumptionTaxIncomeNominal governConsumptionTaxIncomeNominal * 100 /  world::gdpBase
  set world::governConsumptionTaxIncomeNominalValues lput normalizedGovernConsumptionTaxIncomeNominal world::governConsumptionTaxIncomeNominalValues

  ; Govern Corporation Tax Income
  let normalizedGovernCorporationTaxIncomeNominal governCorporationTaxIncomeNominal * 100 /  world::gdpBase
  set world::governCorporationTaxIncomeNominalValues lput normalizedGovernCorporationTaxIncomeNominal world::governCorporationTaxIncomeNominalValues

  ; Govern Financial Expense
  let normalizedFinancialExpenseNominal governFinancialExpenseNominal * 100 /  world::gdpBase
  set world::governFinancialExpenseValues lput normalizedFinancialExpenseNominal world::governFinancialExpenseValues

  ; Govern Income
  let normalizedGovernIncomeNominal governIncomeNominal * 100 /  world::gdpBase
  set world::governIncomeNominalValues lput normalizedGovernIncomeNominal world::governIncomeNominalValues

  ; Govern Income Tax Income
  let normalizedGovernIncomeTaxIncomeNominal governIncomeTaxIncomeNominal * 100 /  world::gdpBase
  set world::governIncomeTaxIncomeNominalValues lput normalizedGovernIncomeTaxIncomeNominal world::governIncomeTaxIncomeNominalValues

  ; Govern Salaries
  let normalizedGovernSalariesNominal governSalariesNominal * 100 /  world::gdpBase
  set world::governSalariesNominalValues lput normalizedGovernSalariesNominal world::governSalariesNominalValues

  ; Govern Unemployment Benefit Expenses
  let normalizedGovernUnemploymentBenefitExpensesNominal governUnemploymentBenefitExpensesNominal * 100 /  world::gdpBase
  set world::governUnemploymentBenefitExpensesNominalValues lput normalizedGovernUnemploymentBenefitExpensesNominal world::governUnemploymentBenefitExpensesNominalValues

  ; Gross Operating surplus Nominal
  let normalizedGOSNominal gosNominal * 100 / world::gdpBase
  set world::gosNominalValues lput normalizedGOSNominal world::gosNominalValues

  ; Gross Operating surplus Real
  let normalizedGOSReal gosReal * 100 / world::gdpBase
  set world::gosRealValues lput normalizedGOSReal world::gosRealValues

  ; Investment Nominal
  let normalizedInvestmentNominal investmentNominal * 100 / world::gdpBase
  set world::investmentNominalValues lput normalizedInvestmentNominal world::investmentNominalValues

  ; Investment Real
  let normalizedInvestmentReal investmentReal * 100 / world::gdpBase
  set world::investmentRealValues lput normalizedInvestmentReal world::investmentRealValues

  ; M1
  let normalizedM1 m1 * 100 / world::m1Base
  set world::m1Values lput normalizedM1 world::m1Values

  ; M1 Target
  let normalizedTargetM1 ([ centralBank::targetM1 ] of world::centralBankId ) * 100 / world::m1Base
  set world::m1TargetValues lput normalizedTargetM1 world::m1Values

  ; Price Index
  let normalizedPriceIndex priceIndex * 100 / world::PriceIndexBase
  set world::priceIndexValues lput normalizedPriceIndex world::priceIndexValues

  ; Public Expenditure
  let normalizedPublicExpenditureNominal publicExpenditureNominal * 100 / world::gdpBase
  set world::publicExpenditureNominalValues lput normalizedPublicExpenditureNominal world::publicExpenditureNominalValues

  ; Salary Average
  let normalizedSalaryAvg salaryAvg * 100 / world::salaryBase
  set world::salaryAvgValues lput normalizedSalaryAvg world::salaryAvgValues

  ; Salary Maximum
  let normalizedSalaryMax salaryMax * 100 / world::salaryBase
  set world::salaryMaxValues lput normalizedSalaryMax world::salaryMaxValues

  ; Salary Minimum
  let normalizedSalaryMin salaryMin * 100 / world::salaryBase
  set world::salaryMinValues lput normalizedsalaryMin world::salaryMinValues

  ; Technology
  let normalizedTechnology world::technology * 100 / world::technologyBase
  set world::technologyValues lput normalizedTechnology world::technologyValues

  ; Unemployment
  let normalizedUnemployment unemployment * 100
  set world::unemploymentValues lput normalizedUnemployment world::unemploymentValues
end 




;;;;;;;;;;;;;;;;;:;;;;;:;:;;;:::;:;:;:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; EVENT MANAGEMENT
;

to event::HandleEvents [ month ]

  if event != ""
  [
    if event = "Technology improvement"
    [ ; Improve technology by 5%
      set world::technology world::technology * 1.05
    ]
    if event = "Technology deterioration"
    [ ; Deteriorate technology by 5%
      set world::technology world::technology * 0.95
    ]

    if event = "Govern salary increase"
    [ ; Increase govern salaries 5%
      set world::salary world::Round2( world::salary * 1.05 )
    ]
    if event = "Govern salary decrease"
    [ ; Decrease govern salaries 5%
      set world::salary world::Round2( world::salary * 0.95 )
    ]

    if event = "Unemployment benefit increase"
    [ ; Increase the unemployment benefit 5%
      set world::salary world::Round2( world::unemploymentBenefit * 1.05 )
    ]
    if event = "Unemployment benefit decrease"
    [ ; Decrease the unemployment benefit 5%
      set world::salary world::Round2( world::unemploymentBenefit * 0.95 )
    ]

    if event = "Consumption tax increase"
    [ ; Increase the consumption tax 2 points
      set world::consumptionTax world::consumptionTax + 0.02
    ]
    if event = "Consumption tax decrease"
    [ ; Decrease the consumption tax 2 points
      set world::consumptionTax world::consumptionTax - 0.02
    ]
    if event = "Income tax increase"
    [ ; Increase the consumption tax 2 points
      set world::incomeTax world::incomeTax + 0.02
    ]
    if event = "Income tax decrease"
    [ ; Decrease the consumption tax 2 points
      set world::incomeTax world::incomeTax - 0.02
    ]
    if event = "Corporation tax increase"
    [ ; Increase the consumption tax 2 points
      set world::corporationTax world::corporationTax + 0.02
    ]
    if event = "Corporation tax decrease"
    [ ; Decrease the consumption tax 2 points
      set world::corporationTax world::corporationTax - 0.02
    ]

    if event = "Bank risk target increase"
    [ ; Increase bank's target risk 10%
      ask banks
      [
        set bank::targetRisk world::Round2( bank::targetRisk * 1.1 )
      ]
    ]
    if event = "Bank risk target decrease"
    [ ; Decrease bank's target risk 10%
      ask banks
      [
        set bank::targetRisk world::Round2( bank::targetRisk * 0.9 )
      ]
    ]

    plots::DrawEvent month
    set event ""
  ]
end 


;;;;;;;;;;;;;;;;;:;;;;;:;:;;;:::;:;:;:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; UTILITY FUNCTIONS
;

; Rounds to the nearest number with two decimal values

to-report world::Round2 [ value ]
  report precision value 2
end 


; Rounds to the next number with two decimal values

to-report world::Round2up [ value ]

  report precision (value + 0.005) 2
end 


; Rounds to the next number with two decimal values

to-report world::Round2down [ value ]

  report precision (value - 0.005) 2
end 


;;;;;;;;;;;;;;;;;:;;;;;:;:;;;:::;:;:;:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; PERSON MANAGEMENT
;

; Assign the default properties to a newly created person

to person::Create [ cash ]

  set shape style::personShape
  set color style::personColor

  move-to one-of patches with [ count turtles-here = 0 ]

  set person::actionTick                random world::ticksPerMonth
  set person::color                     random-float 1
  set person::cash                      cash
  ; person::targetCash Assigned later
  set person::employed                  0
  set person::propensityToConsume       0.6 + random-float 0.2
  set person::last::consumedGoods       0
  set person::last::consumedGoodsValue  0
  set person::consumedGoods             0
  set person::consumedGoodsValue        0
  set person::stocks                    0
  set person::targetStocks              15 + random 8

  person::CalculateTargetCash
end 


; Calculates the amount of reserves desired by the person

to person::CalculateTargetCash

  set person::targetCash world::baseConsumption * world::goodsPriceAvg * world::peopleReservesTargetTicks
end 


; Operational action of the person

to person::OperationalAction

  ; Buy goods
  person::BuyGoods

  ; Invest
  if person::stocks > person::targetStocks
  [ ; Sell stocks
    person::SellStocks
  ]

  if person::stocks < person::targetStocks
  [
    person::BuyStocks
  ]

  ; Get a job
  if person::employed = 0
  [
    person::GetJob
  ]
end 


; Strategic action of the person

to person::StrategicAction

  ; Sell the less profitable stocks on the portfolio (10% chance) if there are more profitable options on the market
  if random-float 1 < 0.1 and person::stocks > 0
  [
    let companiesWithStocksOnSale companies with [ company::stocksOnSale > 0 ]

    if any? companiesWithStocksOnSale
    [
      let maxMarketProfit max [ company::stockProfitability ] of companiesWithStocksOnSale

      let lessProfitableInvestment one-of my-investments with-min [ [ifelse-value (is-company? self ) [company::stockProfitability][bank::stockProfitability]] of end2 ]

      if maxMarketProfit > [ifelse-value (is-company? self ) [company::stockProfitability][bank::stockProfitability]] of ([end2] of lessProfitableInvestment)
      [
        ; Sell max 20% of my stocks or the full investment
        let stocksToSell ceiling( person::stocks * 0.2)
        if stocksToSell > [investment::stocks] of lessProfitableInvestment [ set stocksToSell [investment::stocks] of lessProfitableInvestment ]


        person::SellStocksFromCompany lessProfitableInvestment stocksToSell
      ]
    ]
  ]


  ; Calculate the caution reserves an the number of stocks wanted
  person::CalculateTargetCash

  ;; Buy stocks if the person has excess cash and is not already trying to buy stocks unsuccessfully
  if person::cash > person::targetCash + world::stockPrice and person::targetStocks <= person::stocks
  [
    let stocksToBuy floor( ( person::cash - person::targetCash ) / world::stockPrice)
    set stocksToBuy ( random stocksToBuy ) + 1

    set person::targetStocks person::stocks + stocksToBuy
  ]
  ;; Sell stocks (or at least don't buy new stocks) if the reserves are not enough
  if person::cash < person::targetCash
  [
    let stocksToSell ceiling( ( person::targetCash - person::cash ) / world::stockPrice )
    if stocksToSell > person::stocks [ set stocksToSell person::stocks]

    set person::targetStocks person::stocks - stocksToSell
  ]


  ; Move to another patch (20% chance) or create a company (10 % chance)  if the person doesn't have a job or if the previous month hasn't been able to consume
  if ( ( person::employed = 0 ) or ( ticks > 30 and person::last::consumedGoods = 0 ) )
  [
    let probability random-float 1

    ifelse person::targetStocks > person::Stocks and probability < 0.10
    [
      ; If the person is employed leave the previous job
      if person::employed = 1
      [
        person::LeaveJob
      ]

      ; Create a company or bank
      let companyId nobody


      if-else random-float 1 < world::initialCompanyCount / (world::initialCompanyCount + world::initialBankCount)
      [ ; Create company
        hatch-companies 1
        [
          company::Create
          set companyId self
        ]
      ]
      [ ;Create bank
        hatch-banks 1
        [
          bank::Create
          set companyId self
        ]
      ]

      ; Become an investor of the new company
      let stocksBought person::BuyStocksFromCompany companyId (person::targetStocks - person::Stocks)

    ]
    [
      if probability < 0.20
      [
        move-to one-of patches with [ count turtles-here = 0 ]
      ]
    ]
  ]

  ; If the neighbours earn more than me, quit the job to find something better
  if ( person::employed = 1 and count people-on neighbors > 0 )
  [
    let avgNeigborSalary sum [person::last::workIncome] of people-on neighbors / count people-on neighbors
    let mySalary sum[ job::salary ] of my-jobs

    let probability ( avgNeigborSalary - mySalary ) / mySalary

    if ( probability > random-float 1 )
    [
      person::LeaveJob
    ]

  ]
end 


; Executes the action of buying goods

to person::BuyGoods

  ; Calculate the amount of goods to buy
  let excessCash person::cash - person::targetCash - world::stockPrice * (person::targetStocks - person::stocks + 1)
  if excessCash < 0 [ set excessCash 0 ]

  let income ( person::last::workIncome + person::last::investmentIncome + person::last::transferIncome ) / world::ticksPerMonth


  let personDemandUnits 0

  ifelse floor ( ticks / world::ticksPerMonth ) = 0
  [ ; In the first month do not take into account the previous month consumption
    set personDemandUnits floor ( ( person::propensityToConsume * income + 0.1 * excessCash ) / world::goodsPriceAvg )
  ]
  [
    set personDemandUnits floor ( 0.5 * ( person::propensityToConsume * income + 0.1 * excessCash ) / world::goodsPriceAvg + 0.5 * person::last::consumedGoods / world::ticksPerMonth )
  ]

  if personDemandUnits < world::baseConsumption [ set personDemandUnits world::baseConsumption]


  ; Get the company list from cheapest to most expensive based on their apparent price
  let myColor person::Color
  let companyList sort-on [company::salePrice + 0.2 * company::salePrice * abs( myColor - company::color) ] companies with [ distance myself < world::interactionRadius ]
  let fullfilled false


  ; Try to buy all the goods we want
  let n 0
  while [ not empty? companyList and not fullfilled ]
  [
    let companyId first companyList
    let goodsToBuy 0
    set n n + 1

    ; Calculate the amount of goods the person wants to buy from this company
    ifelse personDemandUnits > [company::inventory] of companyId
    [
      set goodsToBuy [company::inventory] of companyId
    ]
    [
      set goodsToBuy personDemandUnits
      set fullfilled true
    ]

    ; Can we pay them?
    let grossPurchaseValue goodsToBuy * [company::salePrice] of companyId

    if grossPurchaseValue > person::cash
    [
      set goodsToBuy floor( person::cash / [company::salePrice] of companyId )
      set grossPurchaseValue goodsToBuy * [company::salePrice] of companyId
      set fullfilled true
    ]

    ; Calculate how much would the user demand if they wasn't restricted by companies inventory
    let goodsToDemand personDemandUnits
    let demandValue goodsToDemand * [company::salePrice] of companyId

    if demandValue > person::cash
    [
      set goodsToDemand floor( person::cash / [company::salePrice] of companyId )
    ]

    ask companyId
    [
      set company::demand company::demand + round( goodsToDemand / n ) ; Divide between n not to over-generate demand
    ]

    ; Buy the goods
    if goodsToBuy > 0
    [
      let tax world::Round2( grossPurchaseValue * world::consumptionTax )
      let netPurchaseValue grossPurchaseValue - tax

      ask companyId
      [
        set company::cash company::cash + netPurchaseValue
        set company::inventory company::inventory - goodsToBuy
        set company::inventoryValue company::inventory * company::inventoryUnitaryValue
        set company::sales company::sales + goodsToBuy
        set company::salesValue company::salesValue + netPurchaseValue
      ]

      set person::cash person::cash - grossPurchaseValue
      set person::consumedGoods person::consumedGoods + goodsToBuy
      set person::consumedGoodsValue person::consumedGoodsValue + grossPurchaseValue

      ask world::governId
      [
        set govern::cash govern::cash + tax
        set govern::incomeFromConsumptionTax govern::incomeFromConsumptionTax + tax
      ]

      create-currentConsumption-with companyId [ currentConsumption::Create ]
      ask currentConsumption-with companyId
      [
        set currentConsumption::amount currentConsumption::amount + goodsToBuy
        set currentConsumption::value currentConsumption::value + grossPurchaseValue
      ]

      if [company::salePrice] of companyId < world::goodsPriceMinCurrent [ set world::goodsPriceMinCurrent [company::salePrice] of companyId]
      if [company::salePrice] of companyId > world::goodsPriceMaxCurrent [ set world::goodsPriceMaxCurrent [company::salePrice] of companyId]
    ]

    set personDemandUnits personDemandUnits - goodsToBuy
    set companyList butFirst companyList
  ]
end 


; Executes the action of selling stocks

to person::SellStocks
  let stocksToSell person::stocks - person::targetStocks

  while[ stocksToSell > 0 ]
  [
    let investmentId min-one-of my-investments [ (ifelse-value (is-company? end2) [[company::stockProfitability] of end2] [[bank::stockProfitability] of end2]) ]

    person::SellStocksFromCompany investmentId stocksToSell

    set stocksToSell person::stocks - person::targetStocks

  ]
end 


; Given an investment relationship tries to sell the specified number of stocks from that investment

to person::SellStocksFromCompany[ investmentId stocksToSell ]

  ; Number of stocks to sell
  if [investment::stocks] of investmentId < stocksToSell
  [
    set stocksToSell [investment::stocks] of investmentId
  ]

  let agentId [end2] of investmentId


  ( ifelse

    ( is-company? agentId and stocksToSell = [ company::stocks ] of agentId )
    [ ; No stockholders for this company
      ask agentId [ company::closeBussiness false ]
    ]

    (is-bank? agentId and stocksToSell = [ bank::stocks ] of agentId)
    [ ; No stockholders for this company
      ask agentId [ bank::closeBussiness false ]
    ]

    [
      ; Update investment relationship
      ifelse [investment::stocks] of investmentId > stocksToSell
      [
        ask investmentId [ set investment::stocks investment::stocks - stocksToSell ]
      ]
      [
        ask investmentId [die]
      ]

      ; Price of the stocks to sell
      let stockPrice 0
      ifelse (is-company? agentId)
      [
        set stockPrice [world::Round2Down( (company::nonCurrentAssets + company::cash - company::bonds * world::bondFaceValue) / company::stocks )] of agentId
      ]
      [; its a bank
        set stockPrice [world::Round2Down( (bank::nonCurrentAssets + bank::cash - bank::loans * world::loanFaceValue) / bank::stocks )] of agentId
      ]
      if ( stockPrice > world::stockPrice ) [ set stockPrice world::stockPrice ]

      let stocksPrice world::Round2Down( stocksToSell * stockPrice )

      ; Get the money from the agent
      let paid true

      ask agentId
      [
        ifelse (is-company? agentId)
        [
          set company::stocks company::stocks - stocksToSell

          if not company::PayCapital stocksPrice
          [
            set paid false
            company::closeBussiness false
          ]

        ]
        [
          set bank::stocks bank::stocks - stocksToSell

          if not bank::PayCapital stocksPrice
          [
            set paid false
            bank::closeBussiness false
          ]
        ]
      ]

      ; Receive the money
      set person::stocks person::stocks - stocksToSell
      if paid [ set person::cash person::cash + stocksPrice ]
    ]
  )
end 


; Executes the action of buying stocks from the market

to person::BuyStocks

  let stocksToBuy person::targetStocks - person::stocks
  let investmentCash person::cash - person::targetCash

  if stocksToBuy * world::stockPrice > investmentCash [ set stocksToBuy floor( investmentCash / world::stockPrice ) ]

  if stocksToBuy > 0
  [
    let companyList sort-on [ ifelse-value( is-company? self ) [( - company::stockProfitability )][ ( - bank::stockProfitability )] ] turtles with [ (is-company? self and company::stocksOnSale > 0 ) or (is-bank? self and bank::stocksOnSale > 0 ) ]

    while [stocksToBuy > 0 and not empty? companyList ]
    [
      let stocksToBuyFromCompany person::BuyStocksFromCompany first companyList stocksToBuy

      ; Continue with the buying process
      set stocksToBuy stocksToBuy - stocksToBuyFromCompany
      set companyList butFirst companyList
    ]
  ]
end 


; Executes the action of buying stocks from a company, returns the number of stocks bought

to-report person::BuyStocksFromCompany[ agentId stocksToBuy ]

  let personId self

  let affordableStocks floor( (person::cash - person::targetCash ) / world::stockPrice )
  if affordableStocks < 0 [ set affordableStocks 0]
  if affordableStocks < stocksToBuy [ set stocksToBuy affordableStocks]

  let offeredStocks ifelse-value (is-company? agentId) [[company::stocksOnSale] of agentId][[bank::stocksOnSale] of agentId]
  let stocksToBuyFromCompany stocksToBuy
  if offeredStocks < stocksToBuyFromCompany [ set stocksToBuyFromCompany offeredStocks ]

  if stocksToBuyFromCompany > 0
  [
    let stocksPrice stocksToBuyFromCompany * world::stockPrice

    ; Create or update the investment relationship
    create-investment-with agentId [ investment::Create ]
    ask investment-with agentId
    [
      set investment::Stocks investment::Stocks + stocksToBuyFromCompany
    ]

    ; update the person
    set person::stocks person::stocks + stocksToBuyFromCompany
    set person::cash person::cash - stocksPrice

    ; update the company
    ask agentId
    [
      ifelse (is-company? agentId)
      [
        set company::stocks company::stocks + stocksToBuyFromCompany
        set company::stocksOnSale company::stocksOnSale - stocksToBuyFromCompany
        set company::cash  company::cash + stocksPrice

        company::DistributeAssets
      ]
      [
        set bank::stocks bank::stocks + stocksToBuyFromCompany
        set bank::stocksOnSale bank::stocksOnSale - stocksToBuyFromCompany
        set bank::cash  bank::cash + stocksPrice
      ]
    ]
  ]

  report stocksToBuyFromCompany
end 


; Executes the action of getting a job for an unemployed person

to person::GetJob

  let turtleId max-one-of turtles
       with [
         (is-company? self and distance myself < world::interactionRadius and company::vacancies > 0 and company::nonCurrentAssets > 0 ) or
         (is-govern? self and distance myself < world::governInteractionRadius and govern::vacancies > 0 ) or
         (is-bank? self and distance myself < world::interactionRadius and bank::vacancies > 0 and bank::nonCurrentAssets > 0 )
       ]
       [ ( ifelse-value
           (is-company? self) [ company::salary ]
           (is-govern? self ) [ govern::salary ]
           [ bank::salary ]
         )
       ]

  if turtleId != nobody
  [
    let salary 0

    if is-company? turtleId
    [
      ask turtleId
      [
        set company::workers company::workers + 1
        set company::vacancies company::vacancies - 1

        set company::lastSalary company::salary
        set salary company::salary
      ]
    ]

    if is-govern? turtleId
    [
      ask turtleId
      [
        set govern::workers govern::workers + 1
        set govern::vacancies govern::vacancies - 1

        set salary govern::salary
      ]
    ]

    if is-bank? turtleId
    [
      ask turtleId
      [
        set bank::workers bank::workers + 1
        set bank::vacancies bank::vacancies - 1

        set bank::lastSalary bank::salary
        set salary bank::salary
      ]
    ]

    set person::employed 1

    create-job-with turtleId [ job::Create salary ]

  ]
end 


; Executes the action of leaving a job for an employed person

to person::LeaveJob

  ask my-jobs
  [
    ask end1 ; Govern / Person
    [
      if is-person? self
      [
        set person::employed 0
      ]
      if is-company? self
      [
        set company::workers company::workers - 1
        set company::vacancies company::vacancies + 1
      ]
      if is-govern? self
      [
        set govern::workers govern::workers - 1
        set govern::vacancies govern::vacancies + 1
      ]
      if is-bank? self
      [
        set bank::workers bank::workers - 1
        set bank::vacancies bank::vacancies + 1
      ]
    ]

    ask end2 ; Person / Company / Bank
    [
      if is-person? self
      [
        set person::employed 0
      ]
      if is-company? self
      [
        set company::workers company::workers - 1
        set company::vacancies company::vacancies + 1
      ]
      if is-govern? self
      [
        set govern::workers govern::workers - 1
        set govern::vacancies govern::vacancies + 1
      ]
      if is-bank? self
      [
        set bank::workers bank::workers - 1
        set bank::vacancies bank::vacancies + 1
      ]
    ]
    die
  ]
end 


; Updates the monthly statistical variables of the person

to person::MonthlyAction

  set person::last::consumedGoods      person::consumedGoods
  set person::last::consumedGoodsValue person::consumedGoodsValue
  set person::last::workIncome         person::workIncome
  set person::last::investmentIncome   person::investmentIncome
  set person::last::transferIncome     person::transferIncome

  set person::consumedGoods      0
  set person::consumedGoodsValue 0
  set person::workIncome         0
  set person::investmentIncome   0
  set person::transferIncome     0

  ; Update consumption relationships
  ask my-Consumptions [ die ]

  ask my-currentConsumptions
  [
    let companyId end2
    let amount currentConsumption::amount
    let value currentConsumption::value
    ask end1
    [
      create-Consumption-with companyId [ consumption::Create amount value ]
    ]
    die
  ]
end 


; Shows information about the currently selected person on the output

to person::ShowInfo

  output-print ( word "Person (id:" who ")" )
  output-print ( word "   Color:               " world::Round2 person::color )
  output-print ( word "   Cash:                " world::Round2 person::cash )
  output-print ( word "   Target cash:         " world::Round2 person::targetCash )
  output-print ( word "   Stocks:              " round person::stocks )
  output-print ( word "   Target stocks:       " round person::targetStocks )
  output-print ( word "   Consumed goods:      " round person::last::consumedGoods )
  output-print ( word "   Consumed goods value:" world::Round2 person::last::consumedGoodsValue )
  output-print ( word "   Work income:         " world::Round2 person::last::workIncome )
  output-print ( word "   Investment income:   " world::Round2 person::last::investmentIncome )
  output-print ( word "   Transfer income:     " world::Round2 person::last::transferIncome )


  if person::employed = 1
  [
    ask my-jobs
    [
      output-print ( word "   Works at:" end2 )
    ]
  ]

  output-print ( word "   Consumes (company, amount, value):" )
  ask my-consumptions
  [
    output-print ( word "      " end2 ", " round consumption::amount ", " world::Round2 consumption::value )
  ]
end 



;;;;;;;;;;;;;;;;;:;;;;;:;:;;;:::;:;:;:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; COMPANY MANAGEMENT
;

; Initializes a newly created company

to company::Create

  set shape style::companyShape
  set color style::companyColor

  move-to one-of patches with [ count turtles-here = 0 ]

  set company::actionTick            random world::ticksPerMonth
  set company::age                   0
  set company::color                 random-float 1
  set company::workers               0
  set company::vacancies             10 + random 10
  set company::salary                world::salary
  set company::lastSalary            world::salary
  set company::maxSalary             world::salary
  ;set company::stocks                0
  ;set company::stocksOnSale          company::vacancies * 19 + random 20 - 10
  set company::cash                  0
  ; company::targetCash

  company::CalculateTargetCash       ; Update the value

  set company::nonCurrentAssets      0
  set company::inventory             0
  set company::inventoryValue        0
  set company::inventoryUnitaryValue 0
  ;company::salePrice            calculated below
  ;company::stockProfitability   calculated below
  set company::bonds                 0
  ;company::bondsOnSale
  set company::coupon                world::defaultCoupon
  set company::lastCoupon            world::defaultCoupon
  set company::maxCoupon             world::defaultCoupon
  set company::risk                  1                          ; Set the initial risk to 1, otherwise companies won't be able to get any initial funds
  set company::targetDebtRatio       1 / ( 1 / ( 0.2 + random-float 0.4 ) - 1)

  company::CalculateNetWorthComposition ; Update stocksOnSale and bondsOnSale

  set company::salePrice             world::Round2( (company::CostPerUnit company::vacancies (company::stocksOnSale * world::stockPrice + company::targetBonds * world::bondFaceValue - company::targetCash) (company::vacancies * company::salary) (company::coupon * company::targetBonds) ) * (1 + random-float 0.10 + world::consumptionTax ) ); unitary cost + up-to 10% + tax

  set company::last::production       0
  set company::last::productionValue  0
  set company::last::demand           world::baseConsumption * world::ticksPerMonth * world::initialPeopleCount / world::initialCompanyCount ; Educated guess for the first month's demand
  set company::last::sales            0
  set company::last::salesValue       0
  set company::last::laborCosts       0
  set company::last::operatingProfit  0
  set company::last::financialExpense 0
  set company::last::profitBeforeTax  0
  set company::last::result           0
  set company::last::nonCurrentAssets 0
  set company::last::inventory        0

  set company::production       0
  set company::productionValue  0
  set company::demand           0
  set company::sales            0
  set company::salesValue       0
  set company::laborCosts       0
  set company::financialExpense 0

  company::CalculateProfitPerStock
end 


; Operational action of the company

to company::OperationalAction

  let stillAlive true

  ; Produce
  let producedGoods company::ProduceGoods company::workers company::nonCurrentAssets

  if producedGoods > 0
  [
    let producedGoodsCost producedGoods * (company::CostPerUnit company::workers company::nonCurrentAssets (sum [job::salary] of my-jobs) (sum [bond::GetFinancialCost 1] of my-bonds) )

    set company::production company::production + producedGoods
    set company::productionValue company::productionValue + producedGoodsCost
    set company::inventory company::inventory + producedGoods
    set company::inventoryUnitaryValue world::Round2up( (company::inventoryValue + producedGoodsCost) / company::inventory )
    set company::inventoryValue company::inventoryUnitaryValue * company::inventory ; Refresh the inventory value
  ]

  ; Pay salaries
  ifelse sum [job::salary] of my-jobs <= company::cash
  [
    ask my-jobs
    [
      let grossSalary job::salary
      let tax world::Round2( grossSalary *  world::incomeTax )
      let netSalary grossSalary - tax

      ask end1 ; Person
      [
        set person::cash person::cash + netSalary
        set person::workIncome person::workIncome + netSalary
      ]

      ask end2 ; Company
      [
        set company::cash company::cash - grossSalary
        set company::laborCosts company::laborCosts + grossSalary
      ]

      ask world::governId
      [
        set govern::cash govern::cash + tax
        set govern::incomeFromIncomeTax govern::incomeFromIncomeTax + tax
      ]
    ]
  ]
  [ ; Cannot pay salaries -> pay what it's possible and close the company
    company::CloseBussiness true
    set stillAlive false
  ]

  if stillAlive
  [
    ; Increase the offered salary if there are vacancies
    if company::vacancies > 0
    [
      set company::salary company::salary + world::salaryIncrease

      if company::salary > company::maxSalary
      [
        let minSalary [govern::unemploymentBenefit] of world::GovernId + 1
        set company::salary ifelse-value ( minSalary > company::maxSalary) [minSalary][ company::maxSalary]
      ]
    ]

    ; Increase the offered coupon if there are unsold bonds
    if company::targetBonds > company::bonds
    [
      set company::coupon company::coupon + world::couponIncrease

      if company::coupon > company::maxCoupon
      [
        set company::coupon company::maxCoupon
      ]
    ]
  ]

  ; Pay bonds
  let totalAmount sum [ sum bond::coupons ] of my-bonds
  if totalAmount > company::cash
  [
    set stillAlive false
    company::CloseBussiness false
  ]

  if stillAlive
  [
    ask my-bonds
    [
      let companyId nobody
      let bankId nobody

      ifelse (is-company? end1)
      [
        set companyId end1
        set bankId end2
      ]
      [
        set companyId end2
        set bankId end1
      ]

      ; Pay coupons
      foreach bond::coupons
      [ [ c ] ->

        ask companyId ; Company
        [
          set company::cash company::cash - c
          set company::financialExpense company::financialExpense + c
        ]

        ask bankId ; Bank
        [
          set bank::cash bank::cash + c
          set bank::financialIncome bank::financialIncome + c
        ]

      ]

      ; Pay face values
      while [ first bond::maturities <= ticks and stillAlive]
      [
        bond::RemoveBond

        ask companyID
        [
          set company::bonds company::bonds - 1

          ifelse not company::PayCapital world::bondFaceValue
          [
            ask bankId
            [
              set bank::bonds bank::bonds - 1
              set bank::financialExpense bank::financialExpense + world::bondFaceValue
              set bank::nonCurrentAssets bank::nonCurrentAssets - world::bondFaceValue
            ]

            set stillAlive false
            company::closeBussiness false
          ]
          [
            ask bankId
            [
              set bank::bonds bank::bonds - 1
              set bank::cash bank::cash + world::bondFaceValue
              set bank::nonCurrentAssets bank::nonCurrentAssets - world::bondFaceValue
            ]
          ]
        ]

        if bond::bondCount = 0 [ die ]
      ]
    ]
  ]
end 


; Strategic action of the company

to company::StrategicAction

  let vacanciesPercent company::vacancies / (company::workers + company::vacancies)

  if company::nonCurrentAssets > 0 ; Do not alter workforce or stocks until the company is running
  [
    ; Calculate variables to work with
    let factors company::OptimalFactors company::last::demand
    let neededWorkers first factors
    let neededCapital last factors

    let workforceExcess company::workers - neededWorkers

    ; if demand is higher than expected production increase the workforce
    if workforceExcess < 0
    [
      ; Set the salary for the new employees (only if there are no vacancies)(push the salary down)
      if company::vacancies = 0
      [
        set company::salary world::Round2( company::lastSalary * (0.5 + random-float 0.5) )

        let minSalary [govern::unemploymentBenefit] of world::GovernId + 1

        if company::salary > company::maxSalary
        [
          set company::salary company::maxSalary
        ]
        if company::salary < minSalary
        [
          set company::salary minSalary
        ]
      ]

      ; Workforce increase probability:
      ; 1) + More likely if there's extra capital for the current workers ( capital / world::CapitalToWorkRatio > workers )
      ; 2) - Less likely if there's not enough capital for the current workers ( capital / world::CapitalToWorkRatio < workers )
      ; 3) - Less likely if the company is not able to hire people
      let workforceIncreaseProbability 0.0 ; If there are no workers do not create new vacancies
      if company::workers > 0
      [
        ;                                [                         1 & 2                          ]   [          3             ]
        set workforceIncreaseProbability ( company::nonCurrentAssets / ( world::CapitalToWorkRatio * company::workers) ) - ( vacanciesPercent * 0.1 )
        if workforceIncreaseProbability < 0.1 [ set workforceIncreaseProbability 0.1] ; At least 10% chance
      ]

      if ( random-float 1 < workforceIncreaseProbability )
      [
        let jobOffers (- workforceExcess)
        let workers10perc ceiling company::workers * 0.1

        if jobOffers >= 3 and jobOffers > workers10perc ; Limit the growth of the company to ensure we don't break the reserves
        [
          set jobOffers max (list 3 workers10perc)
        ]

        set company::vacancies random ( jobOffers + 1)
      ]
    ]

    ; If the demand is not enough clear vacancies and fire some workers
    if workforceExcess >= 0 and company::workers > 0
    [
      set company::vacancies 0
    ]

    if workforceExcess > 0
    [
      let workforceDecreaseProbability workforceExcess / company::workers ; The greater the excess the greater the probability

      if ( random-float 1 < workforceDecreaseProbability )
      [
        let workersToBeFired random (workforceExcess + 1)

        if workersToBeFired > 0
        [
          set company::workers company::workers - workersToBeFired

          ask n-of workersToBeFired my-jobs
          [
            ask end1
            [
              set person::employed 0
            ]

            die
          ]
        ]
      ]
    ]

    ; Calculate the target cash with the expected employees
    company::CalculateTargetCash


    ; if demand is higher than expected production increase the capital
    let capitalShortage neededCapital - ( company::stocks * world::stockPrice + company::bonds * world::bondFaceValue - company::targetCash )

    if capitalShortage > 0
    [
      ; Divide the capital between stocks and bonds
      let stockCount round ( neededCapital / (1 + company::targetDebtRatio) / world::stockPrice )
      let bondCount round ( ( neededCapital - stockCount * world::stockPrice ) / world::bondFaceValue )

      ; Offer stocks
      if company::stocks < stockCount
      [ ; More stocks are needed

        ifelse company::stocks + company::stocksOnSale > stockCount
        [ ; Adjust the current stock offer
          set company::stocksOnSale stockCount - company::stocks
        ]
        [
          ; Send more stocks to the market

          let unsoldStocksPercent ifelse-value (company::stocks = 0) [1] [company::stocksOnSale / company::stocks]

          ; Capital increase probability:
          ; 1) + More likely if there's not enough capital
          ; 2) - Less likely if the company is not able to sell stocks
          ;                              [                 1               ]   [          3          ]
          let capitalIncreaseProbability ( capitalShortage / neededCapital ) - ( unsoldStocksPercent )
          if capitalIncreaseProbability < 0.1 [ set capitalIncreaseProbability 0.1] ; At least 10% chance

          if ( random-float 1 < capitalIncreaseProbability )
          [
            let stockOffer stockCount - company::stocks - company::stocksOnSale
            let stocks10perc ceiling company::stocks * 0.1

            if stockOffer >= 10 and stockOffer > stocks10perc ; Limit the growth of the company no to get too big too quickly
            [
              set stockOffer max (list 10 stocks10perc)
            ]

            set company::stocksOnSale random ( stockOffer + 1)

          ]
        ]
      ]


      ; Offer bonds
      ifelse company::targetBonds > bondCount
      [ ; Adjust the current bond offer
        set company::targetBonds bondCount
      ]
      [
        if company::targetBonds < bondCount
        [ ; More bonds are needed

          ; Set the price for the new bonds (only if there are not bonds on sale)(push the price down)
          if company::targetBonds = company::bonds
          [
            set company::coupon company::lastCoupon * (0.5 + random-float 0.5)
          ]

          ; Send more bonds to the market
          let unsoldBondsPercent ifelse-value (company::bonds = 0) [1] [company::targetBonds / company::bonds - 1]

          ; Capital increase probability:
          ; 1) + More likely if there's not enough capital
          ; 2) - Less likely if the company is not able to sell bonds
          ;                              [                 1               ]   [          3          ]
          let capitalIncreaseProbability ( capitalShortage / neededCapital ) - ( unsoldBondsPercent )
          if capitalIncreaseProbability < 0.1 [ set capitalIncreaseProbability 0.1] ; At least 10% chance

          if ( random-float 1 < capitalIncreaseProbability )
          [
            let bondOffer bondCount - company::targetBonds
            let bonds10perc ceiling company::bonds * 0.1

            if bondOffer >= 10 and bondOffer > bonds10perc ; Limit the growth of the company no to get too big too quickly
            [
              set bondOffer max (list 10 bonds10perc)
            ]

            set company::targetBonds company::targetBonds + random ( bondOffer + 1)

          ]
        ]

        ; Update the max value for salaries
        ifelse company::last::result > 0
        [
          let vacancies ifelse-value (company::vacancies > 0)[company::vacancies][1]
          set company::maxSalary world::Round2Up( company::last::result / ( vacancies * world::ticksPerMonth ) )
        ]
        [
          set company::maxSalary 0
        ]

        if company::salary > company::maxSalary
        [
          set company::salary company::maxSalary
        ]
        let minSalary [govern::unemploymentBenefit] of world::GovernId + 1
        if company::salary < minSalary
        [
          set company::salary minSalary
        ]

        ; Update the max value for coupons
        ifelse company::last::result > 0
        [
          let bondsOnSale ifelse-value (company::targetBonds - company::bonds > 0)[company::targetBonds - company::bonds][1]
          set company::maxCoupon world::Round2Up( company::last::result / ( bondsOnSale * world::ticksPerMonth ) )
        ]
        [
          set company::maxCoupon 0
        ]

        if company::coupon > company::maxCoupon
        [
          set company::coupon company::maxCoupon
        ]
      ]

    ]
  ]



  ; If the demand is higher than real production or costs are greater than sale price increase the price
  let netSalePrice world::Round2( company::salePrice * (1 - world::consumptionTax) )
  if company::last::demand > company::last::production or netSalePrice < company::inventoryUnitaryValue
  [
    ; It will increase the price if there are a lot of vacancies, if the cost is lower than the price and at least with a 20% chance
    let salePriceIncreaseProbability max (list 0.2 vacanciesPercent (ifelse-value(netSalePrice < company::inventoryUnitaryValue)[1][0]) )

    if ( random-float 1 < salePriceIncreaseProbability )
    [
      set netSalePrice world::Round2up( netSalePrice * 1.02 )

      if netSalePrice < company::inventoryUnitaryValue [ set netSalePrice company::inventoryUnitaryValue ]

      set company::salePrice world::Round2( netSalePrice * ( 1 + world::consumptionTax ) )

    ]
  ]

  ; Demand is too low, try to increase it lowering prices
  if ( company::last::demand = 0 or company::last::demand < company::last::production ) and company::last::production > 0
  [
    let salePriceDecreaseProbability 1 ; If demand is 0, decrease price!

    if company::last::demand != 0
    [
      set salePriceDecreaseProbability ( company::last::production - company::last::demand ) / company::last::production  ; The bigger the gap the more likely to reduce price
    ]


    if ( random-float 1 < salePriceDecreaseProbability )
    [
      set netSalePrice world::Round2( company::salePrice * (1 - world::consumptionTax) )

      ; Reduce the price by a 2% or reduce the gap between price and costs by a 20%
      set netSalePrice world::Round2down ( min ( list (netSalePrice * 0.98) (netSalePrice - (netSalePrice - company::inventoryUnitaryValue) * 0.2 ) )  )

      if netSalePrice < company::inventoryUnitaryValue [ set netSalePrice company::inventoryUnitaryValue ]

      set company::salePrice world::Round2( netSalePrice * ( 1 + world::consumptionTax ) )
    ]
  ]

  ; Calculate the expected profits
  company::CalculateProfitPerStock

  ; Adjust assets to the new target cash
  company::DistributeAssets



  ; The company has 3 months since birth to rise enough capital, if it's not successfull close it
  if company::nonCurrentAssets = 0 and company::age > 3
  [
    company::closeBussiness false
  ]
end 


; Monthly action of the company

to company::MonthlyAction

  let changeInInventory company::inventoryValue - company::last::inventoryValue

  ; Update the monthly statistical variables of the company
  set company::age                    company::age + 1
  set company::last::production       company::production
  set company::last::productionValue  company::last::production * company::salePrice
  set company::last::demand           company::demand
  set company::last::sales            company::sales
  set company::last::salesValue       company::salesValue
  set company::last::laborCosts       company::laborCosts
  set company::last::operatingProfit  world::Round2( company::salesValue - company::laborCosts + (company::inventoryValue - company::last::inventoryValue) )
  set company::last::financialExpense company::financialExpense
  set company::last::profitBeforeTax  company::last::operatingProfit - company::last::financialExpense

  set company::last::investment       ( company::nonCurrentAssets - company::last::nonCurrentAssets ) + ( company::inventory - company::last::inventory ) * company::salePrice

  set company::last::nonCurrentAssets company::nonCurrentAssets
  set company::last::inventory        company::inventory
  set company::last::inventoryValue   company::inventoryValue

  set company::production      0
  set company::productionValue 0
  set company::demand          0
  set company::sales           0
  set company::salesValue      0
  set company::laborCosts      0
  set company::financialExpense 0

  ; Pay taxes
  let tax 0

  if ( company::last::profitBeforeTax > 0 )
  [
    set tax world::Round2( company::last::profitBeforeTax * world::corporationTax )

    if company::cash < tax
    [ ; If not enough cash, pay what it is possible
      set tax company::cash
    ]

    set company::cash  company::cash - tax

    ask world::governId
    [
      set govern::cash govern::cash + tax
      set govern::incomeFromCorporationTax govern::incomeFromCorporationTax + tax
    ]
  ]

  set company::last::result company::last::profitBeforeTax - tax

  ; Update the max value for coupons
  ifelse company::last::result > 0
  [
    let pervMaxCoupon company::maxCoupon
    let bondsOnSale ifelse-value (company::targetBonds - company::bonds > 0)[company::targetBonds - company::bonds][1]

    set company::maxCoupon world::Round2Up( company::last::result / ( bondsOnSale * world::ticksPerMonth ) )

    if company::coupon = pervMaxCoupon
    [ ;Update the coupon value
      set company::coupon company::lastCoupon * (0.5 + random-float 0.5)
      if company::coupon > company::maxCoupon
      [
        set company::coupon company::maxCoupon
      ]
    ]
  ]
  [
    set company::maxCoupon 0
  ]

  ; Update the company's risk
  company::CalculateRisk

  ; Distribute dividend
  let totalAssets company::cash + company::inventoryValue + company::nonCurrentAssets
  let dividend totalAssets - company::targetCash - company::stocks * world::stockPrice - company::bonds * world::bondFaceValue

  if dividend > 0
  [
    if dividend > company::cash [ set dividend company::cash ]


    if dividend / 10 > company::stocks ; Distribute at least 10 cents to each stock
    [
      let cashLeft dividend
      let stocksLeft company::stocks

      ask my-investments
      [
        let grossInvestorCash world::Round2down( cashLeft / stocksLeft ) * investment::stocks
        set tax world::Round2( grossInvestorCash * world::incomeTax )
        let netInvestorCash grossInvestorCash - tax

        ask world::governId
        [
          set govern::cash govern::cash + tax
          set govern::incomeFromIncomeTax govern::incomeFromIncomeTax + tax
        ]

        ask end1
        [
          set person::cash person::cash + netInvestorCash
          set person::investmentIncome person::investmentIncome + netInvestorCash
        ]
        ask end2
        [
          set company::cash company::cash - grossInvestorCash
        ]

        set cashLeft cashLeft - grossInvestorCash
        set stocksLeft stocksLeft - investment::stocks
      ]
    ]
  ]
end 


; Calculate the amount of cash the company needs to have

to company::CalculateTargetCash

  set company::targetCash ( sum [job::salary] of my-jobs + ( company::vacancies * company::salary ) ) * world::companyReservesTargetTicks + sum [bond::GetFinancialCost world::companyReservesTargetTicks] of my-bonds
end 


; Distributes the amount of money between the cash and nonCurrentAssets. It's used when the company's stock number increases or then the target cash is changed.

to company::DistributeAssets

  ; If the company needs cash, convert some non current assets (but don't convert all of them)
  if company::cash < company::targetCash and company::nonCurrentAssets > world::minCapital
  [
    let amount company::targetCash - company::cash
    if company::nonCurrentAssets - amount < world::minCapital [ set amount company::nonCurrentAssets - world::minCapital]

    set company::nonCurrentAssets company::nonCurrentAssets - amount
    set company::cash company::cash + amount
  ]

  ; If the company has excess cash, add some non current assets if they are below the expected level.
  if company::cash > company::targetCash
  [
    let amount company::cash - company::targetCash

    set company::nonCurrentAssets company::nonCurrentAssets + amount
    set company::cash company::cash - amount
  ]
end 


; Pay an amount of money reducing the amount of non-current assegs if needed. It's used when selling stocks or distributing dividend, not for ordinary payments

to-report company::PayCapital [ amount ]

  ; Pay in cash if we have enough
  if company::cash - amount >= company::targetCash
  [
    set company::cash company::cash - amount
    report true
  ]

  ; There's not enough cash, convert some nonCurrentActives
  if company::cash + company::nonCurrentAssets - amount >= company::targetCash + world::minCapital
  [
    let total company::cash + company::nonCurrentAssets - amount

    set company::cash company::targetCash
    set company::nonCurrentAssets total - company::cash

    report true
  ]

  ;  There's enough cash but not enough to keep target reserves full
  if company::cash + company::nonCurrentAssets - amount >= world::minCapital
  [
    set company::cash company::cash + company::nonCurrentAssets - amount - world::minCapital
    set company::nonCurrentAssets world::minCapital

    report true
  ]

  ; There's not enough money
  report false
end 


; Given the production factor's values calculates how much te company will produce

to-report company::ProduceGoods[ workers capital ]

  let producedGoods 0
  if workers >= 1
  [
    set producedGoods round ( world::technology * (workers - 0.1) ^ 0.5 * (capital / world::CapitalToWorkRatio) ^ 0.5 )
  ]

  report producedGoods
end 


; Given the production factor's values calculates the cost per unit produced

to-report company::CostPerUnit[ workers capital workersCost debtCost]

  let producedGoods company::ProduceGoods workers capital

  let totalCost workersCost + debtCost

  report world::Round2Up (totalCost / producedGoods )
end 

; Given the units demanded by consumers, calculates the production factors to meet that demand

to-report company::OptimalFactors[ demand ]

  let goodsToProduce demand - company::inventory
  let goodsToProducePerTick ceiling (goodsToProduce / world::ticksPerMonth)

  let workers 1
  let capital world::minCapital

  if goodsToProducePerTick > 0
  [ ; P = A * (N - 0.1)^0.5 * (K/5000)^0.5
    ; For optimal purposes: N - 0.1 = K / 5000 ->  P = A * (N - 0.1)
    set workers ceiling( goodsToProducePerTick / world::technology + 0.1 )
    set capital workers * world::CapitalToWorkRatio
  ]

  report (list workers capital )
end 


; Calculate the expected profit per stock of the company for the next month

to company::CalculateProfitPerStock

  let workers company::workers + company::vacancies
  let stocks company::stocks + company::stocksOnSale
  let nBonds company::targetBonds

  let capital stocks * world::stockPrice + nBonds * world::bondFaceValue - company::targetCash
  if capital < world::minCapital [set capital world::minCapital]

  let totalIncome company::salePrice * (1 - world::consumptionTax) * company::ProduceGoods workers capital

  let totalCost sum [job::salary] of my-jobs + sum[bond::getFinancialCost world::ticksPerMonth ] of my-bonds / world::ticksPerMonth
         + ifelse-value (company::targetBonds > company::bonds) [ (company::targetBonds - company::bonds) * company::coupon ][0]

  set company::stockProfitability ( totalIncome - totalCost ) * world::ticksPerMonth / stocks
end 


; Close the company

to company::CloseBussiness [ payWorkers ]

  ; Fire the workers and pay them what the company owes them
  set company::cash company::cash + company::nonCurrentAssets
  set company::nonCurrentAssets 0


  ask my-jobs
  [
    let lastSalary ifelse-value ( payWorkers ) [ world::Round2down( [company::cash / company::workers] of end2 )][0]

    ask end1 ; Person
    [
      set person::cash person::cash + lastSalary
      set person::employed 0
    ]

    ask end2 ; Company
    [
      set company::cash company::cash - lastSalary
      set company::workers company::workers - 1
    ]
  ]


  ; Pay bonds
  ask my-bonds
  [
    let companyId nobody
    let bankId nobody
    ifelse is-company? end1
    [
      set companyId end1
      set bankId end2
    ]
    [
      set companyId end2
      set bankId end1
    ]

    while [ bond::bondCount > 0 ]
    [
      bond::RemoveBond

      if-else [company::cash] of myself >= world::bondFaceValue
      [
        ask companyId
        [
          set company::cash company::cash - world::bondFaceValue
          set company::bonds company::bonds - 1
        ]

        ask bankId
        [
          set bank::bonds bank::bonds - 1
          set bank::cash bank::cash + world::bondFaceValue
          set bank::nonCurrentAssets bank::nonCurrentAssets - world::bondFaceValue
        ]
      ]
      [ ; Can't pay
        ask companyId
        [
          set company::bonds company::bonds - 1
        ]

        ask bankId
        [
          set bank::bonds bank::bonds - 1
          set bank::financialExpense bank::financialExpense +  world::bondFaceValue
          set bank::nonCurrentAssets bank::nonCurrentAssets - world::bondFaceValue
        ]
      ]

      if bond::bondCount = 0 [ die ]
    ]
  ]


  ; End all investment relationships and return the remainings of their investment
  ask my-investments
  [
    let stocks investment::stocks

    let lastDividend ifelse-value (stocks < [company::stocks] of end2 ) [ world::Round2down( stocks * [company::cash / company::stocks] of end2 ) ][ [company::cash] of end2 ]

    ask end1 ; Person
    [
      set person::cash person::cash + lastDividend

      set person::stocks person::stocks - stocks
      set person::targetStocks person::targetStocks - stocks
      if person::targetStocks < 0 [ set person::targetStocks 0 ]
    ]

    ask end2 ; Company
    [
      set company::cash company::cash - lastDividend
      set company::stocks company::stocks - stocks
    ]
  ]

  ; if it still has some cash give it to the govern (it may occur if the company loses all its investors)
  ask world::governId
  [
    set govern::cash govern::cash + [company::cash] of myself
  ]
  set company::cash 0


  ; If the company was selected, deselect it
  if selection::selectedAgent = self
  [
    let patchId selection::selectedPatch

    set selection::selectedPatch nobody
    set selection::selectedAgent nobody

    selection::selectPatch patchId
  ]

  ; Kill the company
  die
end 


; Calculate the initial stocks and bonds on sale

to company::CalculateNetWorthComposition

  let capital company::vacancies * world::CapitalToWorkRatio + company::targetCash

  set company::stocksOnSale round ( capital / (1 + company::targetDebtRatio) / world::stockPrice )
  set company::targetBonds round ( ( capital - company::stocksOnSale * world::stockPrice ) / world::bondFaceValue )
end 


; Calculates the risk level the company has

to company::CalculateRisk

  let times 0

  ifelse company::lastCoupon > company::Coupon
  [
    set times company::last::result / ( company::lastCoupon * world::ticksPerMonth )
  ]
  [
    set times company::last::result / ( company::Coupon * world::ticksPerMonth )
  ]

  if times < -2 [set times -2 ] ; Avoid number overflow when making e^-times & risk value being too high

  set company::risk (exp (-1 * times) ) + 1
end 




; Shows information about the selected company in the output control

to company::ShowInfo

  output-print ( word "Company (id:" who ")" )

  output-print ( word "   Color:               " world::Round2 company::color )
  output-print ( word "   Age  :               " world::Round2 company::age )
  output-print ( word "   Cash:                " world::Round2 company::cash )
  output-print ( word "   Target Cash:         " world::Round2 company::targetCash )
  output-print ( word "   Non-current Assets:  " world::Round2 company::nonCurrentAssets )
  output-print ( word "   No. of Workers:      " round company::workers )
  output-print ( word "   No. of Vacancies:    " round company::vacancies )
  output-print ( word "   Salary:              " world::Round2 company::salary )
  output-print ( word "   Target Debt Ratio:   " world::Round2 company::targetDebtRatio )
  output-print ( word "   Stocks:              " round company::stocks )
  output-print ( word "   Stocks on sale:      " round company::stocksOnSale )
  output-print ( word "   Bonds:               " round company::bonds )
  output-print ( word "   Bonds target :       " round company::targetBonds )
  output-print ( word "   Bonds coupon:        " world::Round2 company::coupon )
  output-print ( word "   Production Units:    " round company::last::production )
  output-print ( word "   Demanded Units:      " round company::last::demand )
  output-print ( word "   Stored Goods:        " round company::inventory )
  output-print ( word "   Unitary Cost:        " world::Round2 company::inventoryUnitaryValue )
  output-print ( word "   Sale Price:          " world::Round2 company::salePrice )
  output-print ( word "   Net Sale Price:      " world::Round2 ( company::salePrice * ( 1 - world::consumptionTax ) ) )
  output-print ( word "   Sold Units:          " world::Round2 company::last::sales )
  output-print ( word "   Value of Sales:      " world::Round2 company::last::salesValue )
  output-print ( word "   Labor Costs:         " world::Round2 company::last::laborCosts )
  output-print ( word "   Operating Profit:    " world::Round2 company::last::operatingProfit )
  output-print ( word "   Financial Expense:   " world::Round2 company::last::financialExpense )
  output-print ( word "   Profit Before Tax:   " world::Round2 company::last::profitBeforeTax )
  output-print ( word "   Result:              " world::Round2 company::last::result )
  output-print ( word "   Risk:                " world::Round2 company::risk )
  output-print ( word "   Stock profitability: " world::Round2 company::stockProfitability )
end 


;;;;;;;;;;;;;;;;;:;;;;;:;:;;;:::;:;:;:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; GOVERN MANAGEMENT
;

; Initializes a newly created govern

to govern::Create[ cash ]

  set shape style::governShape
  set color style::governColor

  move-to patch -1 0

  set govern::workers     0
  set govern::vacancies   350
  set govern::cash        cash
  set govern::targetCash  0
  set govern::salary      world::salary
  set govern::unemploymentBenefit world::unemploymentBenefit
  set govern::bonds       0
  set govern::bondsOnSale 0
  set govern::coupon      world::defaultCoupon
  set govern::lastCoupon  world::defaultCoupon
  set govern::loans       0
  set govern::risk        0

  set govern::incomeFromCorporationTax 0
  set govern::incomeFromIncomeTax      0
  set govern::incomeFromConsumptionTax 0
  set govern::laborCosts               0
  set govern::transferCosts            0
  set govern::financialExpense           0

  set govern::last::incomeFromCorporationTax 0
  set govern::last::incomeFromIncomeTax      0
  set govern::last::incomeFromConsumptionTax 0
  set govern::last::laborCosts               0
  set govern::last::transferCosts            0
  set govern::last::financialExpense         0
end 


; Operational action of the govern

to govern::OperationalAction

  ; Pay salaries
  let laborCosts sum [job::salary] of my-jobs

  if govern::cash < laborCosts
  [
    govern::CentralBankRescue
  ]

  ask my-jobs
  [
    let grossSalary job::salary
    let tax world::Round2( grossSalary *  world::incomeTax )
    let netSalary grossSalary - tax

    ask end2 ; Person
    [
      set person::cash person::cash + netSalary
      set person::workIncome person::workIncome + netSalary
    ]

    ask end1 ; Govern
    [
      set govern::cash govern::cash - grossSalary
      set govern::laborCosts govern::laborCosts + grossSalary

      set govern::cash govern::cash + tax
      set govern::incomeFromIncomeTax govern::incomeFromIncomeTax + tax
    ]
  ]


  ; Pay unemployment benefits
  let unemployed count people with [ person::employed = 0 ]

  if govern::cash < unemployed * govern::unemploymentBenefit
  [
    govern::CentralBankRescue
  ]

  let grossUnemplymentBenefit  govern::unemploymentBenefit
  let tax world::Round2( grossUnemplymentBenefit *  world::incomeTax )
  let netUnemplymentBenefit grossUnemplymentBenefit - tax


  ask people with [ person::employed = 0 ]
  [
    set person::cash person::cash + netUnemplymentBenefit
    set person::transferIncome person::transferIncome + netUnemplymentBenefit
  ]

  set govern::cash govern::cash - world::Round2 ( netUnemplymentBenefit * unemployed )
  set govern::transferCosts govern::transferCosts + world::Round2 ( grossUnemplymentBenefit * unemployed )
  set govern::incomeFromIncomeTax govern::incomeFromIncomeTax + world::Round2 ( tax * unemployed )


  ; Increase the offered coupon if there are unsold bonds
  if govern::bondsOnSale > 0
  [
    set govern::coupon govern::coupon + world::couponIncrease
  ]

  ; Pay debt
  ask my-bonds
  [
    ; Pay coupons
    foreach bond::coupons
    [ [ c ] ->

      ask end1 ; Govern
      [
        if govern::cash < c
        [
          govern::CentralBankRescue
        ]

        set govern::cash govern::cash - c
        set govern::financialExpense govern::financialExpense + c
      ]

      ask end2 ; Bank
      [
        set bank::cash bank::cash + c
        set bank::financialIncome bank::financialIncome + c
      ]

    ]

    ; Pay face values
    while [ first bond::maturities <= ticks ]
    [
      bond::RemoveBond

      ask end1
      [
        if govern::cash < world::bondFaceValue
        [
          govern::CentralBankRescue
        ]

        set govern::bonds govern::bonds - 1
        set govern::cash govern::cash - world::bondFaceValue
      ]

      ask end2 ; Bank
      [
        set bank::bonds bank::bonds - 1
        set bank::cash bank::cash + world::bondFaceValue
        set bank::nonCurrentAssets bank::nonCurrentAssets - world::bondFaceValue
      ]

      if bond::bondCount = 0 [die]
    ]
  ]

  ; Return rescue loans to the central bank
  ask my-loans
  [
    ; Pay face values
    while [ first loan::maturities <= ticks ]
    [
      loan::RemoveLoan

      ask end1
      [
        if govern::cash < world::loanFaceValue
        [
          govern::CentralBankRescue
        ]

        set govern::cash govern::cash - world::loanFaceValue
        set govern::loans govern::loans - 1
      ]

      ask end2
      [
        set centralBank::M1 centralBank::M1 - world::loanFaceValue
      ]

      if loan::loanCount = 0 [ die ]
    ]
  ]

  ; Govern workers improve technology
  set world::technology world::technology * (1 + govern::workers * world::technologyImprovementPerGovernmentWorker)
end 

; Strategic action of the govern

to govern::StrategicAction

  govern::CalculateTargetCash

  ; Hire workers if cash superavit
  if govern::targetCash < govern::cash
  [
    ; The greater the cash reserves the greater the possibility of a workforce increase
    let workforceIncreaseProbability govern::cash / govern::targetCash


    if ( random-float 1 < workforceIncreaseProbability )
    [
      let workers10perc ceiling govern::workers * 0.1

      let jobOffers max (list 3 workers10perc)

      let maxJobOffers floor( govern::targetCash - govern::cash / ( govern::salary * world::ticksPerMonth ) )
      if jobOffers > maxJobOffers [ set jobOffers maxJobOffers ]

      set govern::vacancies govern::vacancies + random ( jobOffers + 1)
    ]
  ]

  ; Create bonds and lay off workers if cash deficit (or rescue)
  if govern::targetCash > govern::cash
  [
    ; Lay off workers
    let workforceExcess floor ( ( govern::targetCash - govern::cash ) / govern::salary )

    if workforceExcess >= govern::workers
    [
      set workforceExcess govern::workers - 1
    ]


    let workforceDecreaseProbability ifelse-value (govern::workers = 0)[0][workforceExcess / govern::workers]

    if random-float 1 < workforceDecreaseProbability
    [
      let workersToBeFired random workforceExcess + 1
      set govern::workers govern::workers - workersToBeFired

      ask n-of workersToBeFired my-jobs
      [
        ask end2
        [
          set person::employed 0
        ]

        die
      ]
    ]

    ifelse ( govern::last::financialExpense < govern::last::incomeFromCorporationTax + govern::last::incomeFromIncomeTax + govern::last::incomeFromConsumptionTax  )
    [
      ; Emit bonds for the difference between the target cash and the current cash
      let pervBondsOnSale govern::bondsOnSale
      set govern::bondsOnSale ceiling ( ( govern::targetCash - govern::cash ) / world::bondFaceValue )

      if pervBondsOnSale = 0 and govern::bondsOnSale > 0 and govern::coupon = govern::lastCoupon
      [
        set govern::coupon govern::lastCoupon * (0.5 + random-float 0.5)
      ]
    ]
    [
      ; The financial expenses are already more than the govern can handle
      govern::CentralBankRescue
    ]

  ]
end 


; Monthly action of the govern

to govern::MonthlyAction

  set govern::last::incomeFromCorporationTax govern::incomeFromCorporationTax
  set govern::last::incomeFromIncomeTax      govern::incomeFromIncomeTax
  set govern::last::incomeFromConsumptionTax govern::incomeFromConsumptionTax
  set govern::last::laborCosts               govern::laborCosts
  set govern::last::transferCosts            govern::transferCosts
  set govern::last::financialExpense         govern::financialExpense

  set govern::incomeFromCorporationTax  0
  set govern::incomeFromIncomeTax       0
  set govern::incomeFromConsumptionTax  0
  set govern::laborCosts                0
  set govern::transferCosts             0
  set govern::financialExpense          0

  if length world::priceIndexValues > 0
  [
    let updateFactor last world::priceIndexValues / 100
    set govern::salary              world::Round2( world::salary * updateFactor )
    set govern::unemploymentBenefit world::Round2( world::unemploymentBenefit * updateFactor )
  ]
end 


; Calculates the amount of cash the government wants to have

to govern::CalculateTargetCash

  let unemployedPeople count people with [ person::employed = 0 ]

  let expectedTransferCost unemployedPeople * govern::unemploymentBenefit

  let expectedWageCost ( govern::workers + govern::vacancies ) * govern::salary

  ;Increase rapidly, decrease slowly
  let newTargetCash (expectedTransferCost + expectedWageCost)  * world::governReservesTargetTicks
                    + sum [bond::GetDebtCost world::governReservesTargetTicks] of my-bonds
                    + sum [loan::GetDebtCost world::governReservesTargetTicks] of my-loans

  if-else newTargetCash >= govern::targetCash
  [
    set govern::targetCash newTargetCash
  ]
  [
    set govern::targetCash newTargetCash * 0.25 + govern::targetCash * 0.75
  ]
end 


; The govern has run out of money, ask for help to the central bank

to govern::CentralBankRescue

  govern::CalculateTargetCash

  let neededLoans ceiling( (govern::targetCash - govern::cash) / world::loanFaceValue )

  ; Create or update the bond relationship
  create-loan-with world::centralBankId [ loan::Create ]
  ask loan-with world::centralBankId
  [
    loan::AddLoan neededLoans
  ]

  ask world::centralBankId
  [
    set centralBank::M1 centralBank::M1 + neededLoans * world::loanFaceValue
    set centralBank::loanOffer ifelse-value (centralBank::loanOffer <= neededLoans) [0] [centralBank::loanOffer - neededLoans]
  ]

  set govern::cash govern::cash + neededLoans * world::loanFaceValue
  set govern::loans govern::loans + neededLoans
end 


; Shows information about the govern in the output control

to govern::ShowInfo

  output-print ( word "Govern (id:" who ")" )

  output-print ( word "   Cash:                " world::Round2 govern::cash )
  output-print ( word "   Target Cash:         " world::Round2 govern::targetCash )
  output-print ( word "   No. of Workers:      " round govern::workers )
  output-print ( word "   No. of Vacancies:    " round govern::vacancies )
  output-print ( word "   Salary:              " world::Round2 govern::salary )
  output-print ( word "   Bonds:               " round govern::bonds )
  output-print ( word "   Bonds on sale:       " round govern::bondsOnSale )
  output-print ( word "   Bonds coupon:        " world::Round2 govern::lastCoupon )
  output-print ( word "   Loans:               " round govern::loans )
  output-print ( word "   Total Income:        " world::Round2 ( govern::last::incomeFromCorporationTax + govern::last::incomeFromIncomeTax + govern::last::incomeFromConsumptionTax ) )
  output-print ( word "   Corp. Tax Income:    " world::Round2 govern::last::incomeFromCorporationTax )
  output-print ( word "   Income Tax Income:   " world::Round2 govern::last::incomeFromIncomeTax )
  output-print ( word "   Consumption Tax Inc.:" world::Round2 govern::last::incomeFromConsumptionTax )
  output-print ( word "   Total Expenses:      " world::Round2 ( govern::last::laborCosts + govern::last::transferCosts + govern::last::financialExpense) )
  output-print ( word "   Labor Costs:         " world::Round2 govern::last::laborCosts )
  output-print ( word "   Unemployment Benefit:" world::Round2 govern::last::transferCosts )
  output-print ( word "   Financial Expense:   " world::Round2 govern::last::financialExpense )
end 


;;;;;;;;;;;;;;;;;:;;;;;:;:;;;:::;:;:;:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; BANK MANAGEMENT
;

; Initializes a newly created bank

to bank::Create

  set shape style::bankShape
  set color style::bankColor

  move-to one-of patches with [ count turtles-here = 0 ]

  set bank::actionTick            random world::ticksPerMonth
  set bank::age                   0

  set bank::workers               0
  set bank::vacancies             5 + random 5
  set bank::salary                world::salary
  set bank::lastSalary            world::salary
  set bank::maxSalary             world::salary
  set bank::stocks                0
  ;set bank::stocksOnSale         ;set on bank::CalculateNetWorthComposition
  set bank::loans                 0
  ;set bank::targetLoans          ;set on bank::CalculateNetWorthComposition
  set bank::cash                  0
  ; bank::targetCash              ;set on bank::CalculateTargetCash

  bank::CalculateTargetCash       ; Update the value

  set bank::bonds                 0
  set bank::targetBonds           bank::vacancies * world::bondsPerWorker
  set bank::minBondCoupon         world::Round2Up( bank::vacancies * bank::salary / bank::targetBonds)
  set bank::nonCurrentAssets      0

  set bank::risk                  1
  set bank::targetRisk            1.37 - random-float 0.36        ; result/coupon at least 1 to aprox 4,5
  set bank::targetDebtRatio       1 / (0.1 + random-float 0.3) - 1; Debt ratio that the bank wants to have
  ;bank::stockProfitability

  bank::CalculateNetWorthComposition ; Update stocksOnSale and targetLoans

  set bank::last::laborCosts        0
  set bank::last::operatingProfit   0
  set bank::last::financialIncome   0
  set bank::last::financialExpense  0
  set bank::last::profitBeforeTax   0
  set bank::last::result            0


  set bank::laborCosts       0
  set bank::financialIncome  0
  set bank::financialExpense 0

  bank::CalculateProfitPerStock
end 


; Operational action of the bank

to bank::OperationalAction

  let stillAlive true

  ; Get Loans from the central bank
  let loanDemand bank::targetLoans - bank::loans
  if loanDemand > 0
  [
    if loanDemand > [centralBank::loanOffer] of world::centralBankId
    [
      set loanDemand [centralBank::loanOffer] of world::centralBankId
    ]

    if ( loanDemand > 0 )
    [
      ; Create or update the bond relationship
      create-loan-with world::centralBankId [ loan::Create ]
      ask loan-with world::centralBankId
      [
        loan::AddLoan loanDemand
      ]


      set bank::cash bank::cash + world::loanFaceValue * loanDemand
      set bank::loans bank::loans + loanDemand

      ask world::centralBankId
      [
        set centralBank::loanOffer centralBank::loanOffer - loanDemand
        set centralBank::M1 centralBank::M1 + world::loanFaceValue * loanDemand
      ]
    ]
  ]

  ; Return loans to the central bank
  ask my-loans
  [
    ; Pay face values
    while [ first loan::maturities <= ticks and stillAlive ]
    [
      loan::RemoveLoan

      ask end2
      [
        if-else bank::cash > world::loanFaceValue
        [
          set bank::cash bank::cash - world::loanFaceValue
          set bank::loans bank::loans - 1
        ]
        [; Cannot return loans -> pay what it's possible and close the company
          bank::closeBussiness false
          set stillAlive false
        ]
      ]

      if stillAlive
      [
        ask end1
        [
          set centralBank::M1 centralBank::M1 - world::loanFaceValue
        ]
      ]

      if loan::loanCount = 0 [ die ]
    ]
  ]

  if stillAlive
  [
    ; Pay salaries
    ifelse sum [job::salary] of my-jobs <= bank::cash
    [
      ask my-jobs
      [
        let grossSalary job::salary
        let tax world::Round2( grossSalary *  world::incomeTax )
        let netSalary grossSalary - tax

        ask end1 ; Person
        [
          set person::cash person::cash + netSalary
          set person::workIncome person::workIncome + netSalary
        ]

        ask end2 ; Bank
        [
          set bank::cash bank::cash - grossSalary
          set bank::laborCosts bank::laborCosts + grossSalary
        ]

        ask world::governId
        [
          set govern::cash govern::cash + tax
          set govern::incomeFromIncomeTax govern::incomeFromIncomeTax + tax
        ]
      ]
    ]
    [ ; Cannot pay salaries -> pay what it's possible and close the company
      bank::CloseBussiness true
      set stillAlive false
    ]
  ]

  ; Buy bonds
  if stillAlive
  [
    let bondsOnMarket true
    while [ bank::cash - bank::targetCash > world::bondFaceValue and bank::targetBonds > bank::bonds and bondsOnMarket]
    [
      let maxRisk (bank::targetRisk - bank::risk) * bank::bonds + bank::targetRisk
      let agent nobody

      ifelse ( maxRisk < 1 )
      [
        let targetRisk bank::targetRisk
        set agent min-one-of turtles with [ (is-company? self and company::targetBonds > company::bonds and company::risk < targetRisk and company::coupon >= [bank::minBondCoupon] of myself )
                                         or (is-govern? self and govern::bondsOnSale > 0 and govern::risk < targetRisk and govern::coupon >= [bank::minBondCoupon] of myself ) ]
                                                [ ifelse-value (is-company? self ) [company::risk][govern::risk] ]
      ]
      [
        set agent max-one-of turtles with [ (is-company? self and company::targetBonds > company::bonds and company::risk <= maxRisk and company::coupon >= [bank::minBondCoupon] of myself )
                                         or (is-govern? self and govern::bondsOnSale > 0 and govern::risk <= maxRisk and govern::coupon >= [bank::minBondCoupon] of myself ) ]
                                                [ ifelse-value (is-company? self ) [company::coupon][govern::coupon] ]
      ]

      set bondsOnMarket (agent != nobody)

      if (agent != nobody )
      [
        ; Create or update the bond relationship
        create-bond-with agent [ bond::Create ]
        ask bond-with agent
        [
          bond::AddBond ifelse-value (is-company? agent) [[company::coupon] of agent ] [[govern::coupon] of agent]
        ]

        let agentRisk 1

        ask agent
        [
          if-else(is-company? self)
          [
            set company::bonds company::bonds + 1
            set company::cash company::cash + world::bondFaceValue
            set company::lastCoupon company::coupon

            company::DistributeAssets

            set agentRisk company::risk
          ]
          [
            set govern::bonds govern::bonds + 1
            set govern::bondsOnSale govern::bondsOnSale - 1
            set govern::cash govern::cash + world::bondFaceValue
            set govern::lastCoupon govern::coupon
            set agentRisk govern::risk
          ]
        ]

        set bank::cash bank::cash - world::bondFaceValue
        set bank::nonCurrentAssets bank::nonCurrentAssets + world::bondFaceValue
        set bank::bonds bank::bonds + 1
        set bank::risk (bank::risk * (bank::bonds - 1) + agentRisk ) /  bank::bonds ; Update the bank's risk
      ]
    ]
  ]

  if stillAlive
  [
    ; Increase the offered salary if there are vacancies
    if bank::vacancies > 0
    [
      set bank::salary bank::salary + world::salaryIncrease

      if bank::salary > bank::maxSalary
      [
        let minSalary [govern::unemploymentBenefit] of world::GovernId + 1
        set bank::salary ifelse-value ( minSalary > bank::maxSalary) [minSalary][ bank::maxSalary]
      ]
    ]
  ]
end 


; Strategic action of the bank

to bank::StrategicAction

  let bondGap (bank::workers * world::bondsPerWorker) - bank::bonds

  ; Clear vacancies to recalculate them
  let prevVacancies bank::vacancies
  set bank::vacancies 0

  ; Fire workers if the current amount of bonds can be managed with fewer people
  if bank::workers > 1 and bondGap > world::bondsPerWorker
  [
   let workersToFire random ( floor (bondGap / world::bondsPerWorker )) + 1

    if workersToFire > 0
    [
      if workersToFire >= bank::workers [ set workersToFire bank::workers - 1]

      set bank::workers bank::workers - workersToFire

      ask n-of workersToFire my-jobs
      [
        ask end1 ; Person
        [
          set person::employed 0
        ]

        die
      ]
    ]
  ]


  ( ifelse
    ; If the bank needs more employees , increase workforce
    ( bondGap < 0 )
    [
      set bank::vacancies ceiling ( -1 *  bondGap / world::bondsPerWorker )
    ]

    ; if the bank is almost full capacity, increase workforce
    ( bondGap < world::bondsPerWorker * 0.2 )
    [
      let workersToHire random 4 ; Hire 0, 1, 2, or 3 workers each time

      set bank::vacancies workersToHire

      if (bank::workers = 0) [set bank::vacancies bank::vacancies + 1 ]
    ]
   )

  ; Set the salary for the new employees (only if there are no vacancies)(push the salary down)
  if prevVacancies = 0 and bank::vacancies > 0
  [
    set bank::salary world::Round2 ( bank::lastSalary * (0.5 + random-float 0.5) )

    let minSalary [govern::unemploymentBenefit] of world::GovernId + 1

    if bank::salary > bank::maxSalary
    [
      set bank::salary bank::maxSalary
    ]
    if bank::salary < minSalary
    [
      set bank::salary minSalary
    ]
  ]

  ; Calculate the new amount of bonds we want to have
  set bank::targetBonds (bank::workers + bank::vacancies) * world::bondsPerWorker


  set bank::minBondCoupon world::Round2Up( ( sum[job::salary] of my-jobs + (bank::vacancies * bank::salary) ) / bank::targetBonds)

  ; Get capital based on the amount of bonds we want to have
  bank::CalculateTargetCash

  let neededCapital bank::targetCash + bank::targetBonds * world::bondFaceValue

  ; Divide the capital between stocks and loans
  let stockCount round ( neededCapital / (1 + bank::targetDebtRatio) / world::stockPrice )
  let loanCount round ( ( neededCapital - stockCount * world::stockPrice ) / world::loanFaceValue )


  if stockCount > bank::stocks
  [
    set bank::stocksOnSale random ( stockCount - bank::stocks + 1)
  ]

  if stockCount < bank::stocks
  [ ; Excess stocks, reduce the loan demand
    let extraStockValue ( bank::stocks - stockCount ) * world::stockPrice

    set loanCount loanCount - floor( extraStockValue  / world::loanFaceValue )
  ]

  set bank::targetLoans loanCount
end 


; Monthly action of the bank

to bank::MonthlyAction

  set bank::age                     bank::age + 1

  set bank::last::laborCosts        bank::laborCosts
  set bank::last::operatingProfit   (- bank::last::laborCosts)
  set bank::last::financialIncome   bank::financialIncome
  set bank::last::financialExpense  bank::financialExpense
  set bank::last::profitBeforeTax   bank::last::operatingProfit + bank::last::financialIncome - bank::last::financialExpense

  set bank::laborCosts       0
  set bank::financialIncome  0
  set bank::financialExpense 0

  ; Pay taxes and calculate result
  let tax 0

  if ( bank::last::profitBeforeTax > 0 )
  [
    set tax world::Round2( bank::last::profitBeforeTax * world::corporationTax )

    if bank::cash < tax
    [ ; If not enough cash, pay what it is possible
      set tax bank::cash
    ]

    set bank::cash  bank::cash - tax

    ask world::governId
    [
      set govern::cash govern::cash + tax
      set govern::incomeFromCorporationTax govern::incomeFromCorporationTax + tax
    ]
  ]
  set bank::last::result bank::last::profitBeforeTax - tax

  ; Update the max value for salaries
  ifelse bank::last::result > 0
  [
    let vacancies ifelse-value (bank::vacancies > 0)[bank::vacancies][1]
    set bank::maxSalary world::Round2Up( bank::last::result / ( vacancies * world::ticksPerMonth ) )
  ]
  [
    set bank::maxSalary 0
  ]

  if bank::salary > bank::maxSalary
  [
    set bank::salary bank::maxSalary
  ]
  let minSalary [govern::unemploymentBenefit] of world::GovernId + 1
  if bank::salary < minSalary
  [
    set bank::salary minSalary
  ]


  ; Update the bank's risk
  bank::CalculateRisk

  ; Distribute dividend
  let totalAssets bank::cash + bank::nonCurrentAssets
  let dividend totalAssets - bank::targetCash - bank::stocks * world::stockPrice - bank::loans * world::loanFaceValue

  if dividend > 0
  [
    if dividend > bank::cash [ set dividend bank::cash ]

    if dividend / 10 > bank::stocks ; Distribute at least 10 cents to each stock
    [
      let cashLeft dividend
      let stocksLeft bank::stocks

      ask my-investments
      [
        let grossInvestorCash world::Round2down( cashLeft / stocksLeft ) * investment::stocks
        set tax world::Round2( grossInvestorCash * world::incomeTax )
        let netInvestorCash grossInvestorCash - tax

        ask world::governId
        [
          set govern::cash govern::cash + tax
          set govern::incomeFromIncomeTax govern::incomeFromIncomeTax + tax
        ]

        ask end1
        [
          set person::cash person::cash + netInvestorCash
          set person::investmentIncome person::investmentIncome + netInvestorCash
        ]
        ask end2
        [
          set bank::cash bank::cash - grossInvestorCash
        ]

        set cashLeft cashLeft - grossInvestorCash
        set stocksLeft stocksLeft - investment::stocks
      ]
    ]
  ]
end 


; Calculate the initial stocks on sale and loans target

to bank::CalculateNetWorthComposition

  let netWorth bank::vacancies * world::bondsPerWorker * world::bondFaceValue + bank::targetCash

  set bank::stocksOnSale round ( netWorth / (1 + bank::targetDebtRatio) / world::stockPrice )
  set bank::targetLoans round ( ( netWorth - bank::stocksOnSale * world::stockPrice ) / world::loanFaceValue )
end 


; Calculate the expected profit per stock of the company for the next month

to bank::CalculateProfitPerStock

  let stocks bank::stocks + bank::stocksOnSale


  let totalIncome sum[bond::getFinancialCost world::ticksPerMonth ] of my-bonds + (bank::targetBonds - bank::bonds) * world::defaultCoupon * world::ticksPerMonth
  let totalCost ( sum [job::salary] of my-jobs + bank::vacancies * bank::salary ) * world::ticksPerMonth

  set bank::stockProfitability ( totalIncome - totalCost ) / stocks
end 


; Calculate the amount of cash the bank needs to have

to bank::CalculateTargetCash

  set bank::targetCash ( sum [job::salary] of my-jobs + bank::vacancies * bank::salary ) * world::bankReservesTargetTicks + sum [loan::GetDebtCost world::bankReservesTargetTicks ] of my-loans
end 

to-report bank::PayCapital [ amount ]

  ; Pay in cash if we have enough
  if bank::cash >= amount
  [
    set bank::cash bank::cash - amount
    report true
  ]

  ; There's not enough money
  report false
end 

to bank::CloseBussiness [ payWorkers ]


  ; Recover all bond money
  ask my-bonds
  [
    let agentId nobody
    let bankId nobody
    ifelse is-company? end1 or is-govern? end1
    [
      set agentId end1
      set bankId end2
    ]
    [
      set agentId end2
      set bankId end1
    ]

    while [ bond::bondCount > 0 ]
    [
      bond::RemoveBond

      let paid true

      ask agentId
      [
        ifelse is-company? self
        [
          set company::bonds company::bonds - 1
          if not company::PayCapital world::bondFaceValue
          [
            set paid false
            company::closeBussiness false
          ]
        ]
        [
          if govern::cash < world::bondFaceValue
          [
            govern::CentralBankRescue
          ]

          set govern::bonds govern::bonds - 1
          set govern::cash govern::cash - world::bondFaceValue
        ]
      ]

      ask bankId ; Bank
      [
        set bank::bonds bank::bonds - 1
        if (paid) [ set bank::cash bank::cash + world::bondFaceValue ]
        set bank::nonCurrentAssets bank::nonCurrentAssets - world::bondFaceValue
      ]

      if bond::bondCount = 0 [die]
    ]
  ]


  ; Return loans
  ask my-loans
  [
    ; Pay face values
    while [ loan::loanCount > 0 ]
    [
      loan::RemoveLoan

      let paid  false
      ask end2
      [
        if bank::cash > world::loanFaceValue
        [
          set bank::cash bank::cash - world::loanFaceValue
          set bank::loans bank::loans - 1

          set paid  true
        ]
      ]

      if paid
      [
        ask end1
        [
          set centralBank::M1 centralBank::M1 - world::loanFaceValue
        ]
      ]

      if loan::loanCount = 0 [die]

    ]
  ]


  ; Fire the workers and pay them what the company owes them
  ask my-jobs
  [
    let lastSalary ifelse-value ( payWorkers ) [ world::Round2down( [bank::cash / bank::workers] of end2 )][0]

    ask end1 ; Person
    [
      set person::cash person::cash + lastSalary
      set person::employed 0
    ]

    ask end2 ; Company
    [
      set bank::cash bank::cash - lastSalary
      set bank::workers bank::workers - 1
    ]
  ]


  ; End all investment relationships and return the remainings of their investment
  ask my-investments
  [
    let stocks investment::stocks
    let lastDividend ifelse-value (stocks < [bank::stocks] of end2 ) [ world::Round2down( stocks * [bank::cash / bank::stocks] of end2 ) ][ [bank::cash] of end2 ]

    ask end1 ; Person
    [
      set person::cash person::cash + lastDividend

      set person::stocks person::stocks - stocks
      set person::targetStocks person::targetStocks - stocks
      if person::targetStocks < 0 [ set person::targetStocks 0 ]
    ]

    ask end2 ; Bank
    [
      set bank::cash bank::cash - lastDividend
      set bank::stocks bank::stocks - stocks
    ]
  ]


  ; If the company was selected, deselect it
  if selection::selectedAgent = self
  [
    let patchId selection::selectedPatch

    set selection::selectedPatch nobody
    set selection::selectedAgent nobody

    selection::selectPatch patchId
  ]

  ; Kill the company
  die
end 


; Calculate the bank's risk as the weighted average of the risk of its bonds

to bank::CalculateRisk
  let riskAggregate 0

  ifelse (bank::bonds > 0)
  [
    ask my-bonds
    [
      let risk 1
      (ifelse (is-company? end1) [ set risk [company::risk] of end1 ]
        (is-company? end2) [ set risk [company::risk] of end2 ]
        (is-govern? end1) [ set risk [govern::risk] of end1 ]
        (is-govern? end2) [ set risk [govern::risk] of end2 ]
      )

      set riskAggregate riskAggregate + bond::bondCount * risk
    ]

    set bank::risk riskAggregate / bank::bonds
  ]
  [
    set bank::risk 1 ; If the bank has no bonds it has no risk
  ]
end 


; Shows information about the selected bank in the output control

to bank::ShowInfo

  output-print ( word "Bank (id:" who ")" )

  output-print ( word "   Age  :               " world::Round2 bank::age )
  output-print ( word "   Cash:                " world::Round2 bank::cash )
  output-print ( word "   Target Cash:         " world::Round2 bank::targetCash )
  output-print ( word "   Non-current Assets:  " world::Round2 bank::nonCurrentAssets )
  output-print ( word "   No. of Workers:      " round bank::workers )
  output-print ( word "   No. of Vacancies:    " round bank::vacancies )
  output-print ( word "   Salary:              " world::Round2 bank::salary )
  output-print ( word "   Target Debt Ratio:   " world::Round2 bank::targetDebtRatio )
  output-print ( word "   Stocks:              " round bank::stocks )
  output-print ( word "   Stocks on sale:      " round bank::stocksOnSale )
  output-print ( word "   Loans:               " round bank::loans )
  output-print ( word "   Target Loans:        " round bank::targetLoans )
  output-print ( word "   Bonds:               " round bank::bonds )
  output-print ( word "   Target Bonds:        " round bank::targetBonds )
  output-print ( word "   Labor Costs:         " world::Round2 bank::last::laborCosts )
  output-print ( word "   Operating Profit:    " world::Round2 bank::last::operatingProfit )
  output-print ( word "   Financial Income:    " world::Round2 bank::last::financialIncome )
  output-print ( word "   Financial Expense:   " world::Round2 bank::last::financialExpense )
  output-print ( word "   Profit Before Tax:   " world::Round2 bank::last::profitBeforeTax )
  output-print ( word "   Result:              " world::Round2 bank::last::result )
  output-print ( word "   Risk:                " world::Round2 bank::risk )
  output-print ( word "   Target Risk:         " world::Round2 bank::targetRisk )
  output-print ( word "   Stock profitability: " world::Round2 bank::stockProfitability )
end 


; Make an initial distribution of loans and bonds

to banks::Initialize

  let initialLoanOffer floor ( [centralBank::TargetM1 - centralBank::M1] of world::centralBankId / world::loanFaceValue )


  ask companies with [ company::targetBonds > company::bonds]
  [
    let bankId max-one-of banks with [bank::targetBonds > bank::bonds ] [bank::targetBonds - bank::bonds]

    let nBonds ifelse-value ( (company::targetBonds - company::bonds) <= [bank::targetBonds - bank::bonds] of bankId)[company::targetBonds - company::bonds][[bank::targetBonds - bank::bonds] of bankId]
    let nLoans ceiling ( nBonds * world::bondFaceValue / world::loanFaceValue )

    if nLoans > initialLoanOffer
    [
      set nLoans initialLoanOffer
      set nBonds floor( nLoans * world::loanFaceValue / world::bondFaceValue )
    ]

    if nLoans > [bank::targetLoans - bank::loans] of bankId
    [
      set nLoans [bank::targetLoans - bank::loans] of bankId
      set nBonds floor( nLoans * world::loanFaceValue / world::bondFaceValue )
    ]

    if bankId != nobody and nBonds > 0 and nLoans > 0
    [
      set company::bonds company::bonds + nBonds
      set company::cash company::cash + nBonds * world::bondFaceValue

      company::DistributeAssets

      let agentRisk company::risk
      let companyCoupon company::coupon

      create-bond-with bankId [ bond::Create ]
      ask bond-with bankId
      [
        bond::AddInitialBond nBonds companyCoupon
      ]

      ask bankId
      [
        set bank::cash bank::cash - world::bondFaceValue * nBonds
        set bank::nonCurrentAssets bank::nonCurrentAssets + world::bondFaceValue * nBonds
        set bank::bonds bank::bonds + nBonds
        set bank::risk (bank::risk * (bank::bonds - nBonds) + agentRisk ) /  bank::bonds ; Update the bank's risk

        set bank::loans bank::loans + nLoans
        set bank::cash bank::cash + nLoans * world::loanFaceValue

        create-loan-with world::centralBankId [ loan::Create ]
        ask loan-with world::centralBankId
        [
          loan::AddInitialLoan nLoans
        ]
      ]

      ask world::centralBankId
      [
        set centralBank::M1 centralBank::M1 + world::loanFaceValue * nLoans
      ]

      set initialLoanOffer initialLoanOffer - nLoans
    ]

  ]

  ask banks
  [
    ; Update the target cash value
    bank::CalculateTargetCash
  ]

  ; Sort maturity by date because the program expects them to be sorted
  ask bonds
  [
    set bond::maturities sort bond::maturities
  ]
  ask loans
  [
    set loan::maturities sort loan::maturities
  ]
end 


;;;;;;;;;;;;;;;;;:;;;;;:;:;;;:::;:;:;:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; CENTRAL BANK MANAGEMENT
;


; Initializes a newly created central bank

to centralbank::Create [ distributedCash initialTargetM1 ]

  set shape style::centralBankShape
  set color style::centralBankColor

  move-to patch 1 0

  ;centralBank::loanOffer
  set centralBank::M1         distributedCash
  set centralBank::targetM1   initialTargetM1

  centralBank::UpdateLoanOffer
end 


; Operational action of the centralbank

to centralbank::OperationalAction

  centralBank::UpdateLoanOffer
end 


; Strategic action of the centralbank

to centralbank::StrategicAction

  if ticks >= 60 ; Do not make strategic action until it can calculate the inflation rate
  [
    let priceIndexT0 last but-last world::priceIndexValues
    let priceIndexT1 last world::priceIndexValues

    let priceIndexChange (priceIndexT1 - priceIndexT0) / priceIndexT0

    let deviation priceIndexChange - world::monthlyInflationTarget

    ; The central bank is more likely to act the greater the deviation is
    let actionTreshhold ( ( random-float deviation ) - 0.005 ) * centralBank::M1

    ( ifelse (abs deviation) < 0.005
      [ ; The amount of money seems right, don't change it
        set centralBank::targetM1 centralBank::M1
      ]
      ( deviation > 0 and centralBank::targetM1 >= centralBank::M1 - actionTreshhold)
      [ ;; Too much inflation, reduce the amount of money
        let newLoans ceiling( ( centralBank::M1 * world::centralBankPolicyFactor ) / world::loanFaceValue )
        set centralBank::targetM1 centralBank::M1 - newLoans * world::loanFaceValue
      ]
      ( deviation < 0 and centralBank::targetM1 <= centralBank::M1 - actionTreshhold )
      [ ;; Too few inflation, increase the amount of money
        let newLoans ceiling( ( centralBank::M1 * world::centralBankPolicyFactor ) / world::loanFaceValue )
        set centralBank::targetM1 centralBank::M1 + newLoans * world::loanFaceValue
      ]
    )
  ]
end 


; Monthly action of the centralbank

to centralbank::MonthlyAction
  ; none
end 


; Update the loan offer for this tick considering the current M1 target and the loan maturities

to centralBank::UpdateLoanOffer

  let nextTickM1 centralBank::M1 - ( sum [loan::GetDebtCost 1] of my-loans)

  let difference centralBank::targetM1 - nextTickM1

  if-else difference > 0
  [
    set centralBank::loanOffer ceiling( difference / (world::loanFaceValue * world::centralBankPolicyMaturity ) )
  ]
  [
    set centralBank::loanOffer 0
  ]
end 

; Shows information about the selected centralbank in the output control

to centralbank::ShowInfo

  output-print ( word "Central Bank (id:" who ")" )

  output-print ( word "   M1 :                 " centralBank::M1 )
  output-print ( word "   Target M1 :          " centralBank::targetM1 )
end 



;;;;;;;;;;;;;;;;;:;;;;;:;:;;;:::;:;:;:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; PLOT & HISTOGRAM MANAGEMENT
;

to charts::Initialize

  ; Classify management initialization
  set people::currentClassifyBy ""
  set companies::currentClassifyBy ""
  set banks::currentClassifyBy ""

  ; World variables initialization
  set world::goodsPriceAvg 1.20                      ; Educated guess for the first month based on expected production costs, is needed for people to calculate their target reserves
  set world::goodsPriceMinCurrent world::floatMax
  set world::goodsPriceMaxCurrent 0

  ; Variables containing the list of values for each economic variable

  set world::bankCountValues                                []
  set world::coeRealValues                                  []
  set world::coeNominalValues                               []
  set world::companyCountValues                             []
  set world::consumptionNominalValues                       []
  set world::consumptionRealValues                          []
  set world::gdpNominalValues                               []
  set world::gdpRealValues                                  []
  set world::goodsPriceAvgValues                            []
  set world::goodsPriceMinValues                            []
  set world::goodsPriceMaxValues                            []
  set world::goodsProducedValues                            []
  set world::goodsSoldValues                                []
  set world::gosRealValues                                  []
  set world::gosNominalValues                               []
  set world::governConsumptionTaxIncomeNominalValues        []
  set world::governCorporationTaxIncomeNominalValues        []
  set world::governFinancialExpenseValues                   []
  set world::governIncomeNominalValues                      []
  set world::governIncomeTaxIncomeNominalValues             []
  set world::governSalariesNominalValues                    []
  set world::governUnemploymentBenefitExpensesNominalValues []
  set world::investmentNominalValues                        []
  set world::investmentRealValues                           []
  set world::m1Values                                       []
  set world::m1TargetValues                                 []
  set world::priceIndexValues                               []
  set world::publicExpenditureNominalValues                 []
  set world::salaryAvgValues                                []
  set world::salaryMaxValues                                []
  set world::salaryMinValues                                []
  set world::technologyValues                               []
  set world::unemploymentValues                             []

  ; Plot management initialization
  set plot1::bluePen::variableCurrent ""
  set plot1::greenPen::variableCurrent ""
  set plot1::pinkPen::variableCurrent ""
  set plot2::bluePen::variableCurrent ""
  set plot2::greenPen::variableCurrent ""
  set plot2::pinkPen::variableCurrent ""
  set plot3::bluePen::variableCurrent ""
  set plot3::greenPen::variableCurrent ""
  set plot3::pinkPen::variableCurrent ""
  set plot4::bluePen::variableCurrent ""
  set plot4::greenPen::variableCurrent ""
  set plot4::pinkPen::variableCurrent ""

  ; Set plot names
  set peoplePlot::name  "People"
  set companyPlot::name "Companies"
  set bankPlot::name "Banks"
  set plot1::name "Plot 1"
  set plot2::name "Plot 2"
  set plot3::name "Plot 3"
  set plot4::name "Plot 4"
end 


; Updates the plot values

to plots::Update

  let x floor( ticks / world::ticksPerMonth )

  if plot1::bluePen::variableCurrent != ""
  [
    set-current-plot plot1::name
    set-current-plot-pen "bluePen"

    plots::UpdatePlotLine x plot1::bluePen::variable
  ]

  if plot1::greenPen::variableCurrent != ""
  [
    set-current-plot plot1::name
    set-current-plot-pen "greenPen"

    plots::UpdatePlotLine x plot1::greenPen::variable
  ]

  if plot1::pinkPen::variableCurrent != ""
  [
    set-current-plot plot1::name
    set-current-plot-pen "pinkPen"

    plots::UpdatePlotLine x plot1::pinkPen::variable
  ]

  if plot2::bluePen::variableCurrent != ""
  [
    set-current-plot plot2::name
    set-current-plot-pen "bluePen"

    plots::UpdatePlotLine x plot2::bluePen::variable
  ]

  if plot2::greenPen::variableCurrent != ""
  [
    set-current-plot plot2::name
    set-current-plot-pen "greenPen"

    plots::UpdatePlotLine x plot2::greenPen::variable
  ]

  if plot2::pinkPen::variableCurrent != ""
  [
    set-current-plot plot2::name
    set-current-plot-pen "pinkPen"

    plots::UpdatePlotLine x plot2::pinkPen::variable
  ]

  if plot3::bluePen::variableCurrent != ""
  [
    set-current-plot plot3::name
    set-current-plot-pen "bluePen"

    plots::UpdatePlotLine x plot3::bluePen::variable
  ]

  if plot3::greenPen::variableCurrent != ""
  [
    set-current-plot plot3::name
    set-current-plot-pen "greenPen"

    plots::UpdatePlotLine x plot3::greenPen::variable
  ]

  if plot3::pinkPen::variableCurrent != ""
  [
    set-current-plot plot3::name
    set-current-plot-pen "pinkPen"

    plots::UpdatePlotLine x plot3::pinkPen::variable
  ]

  if plot4::bluePen::variableCurrent != ""
  [
    set-current-plot plot4::name
    set-current-plot-pen "bluePen"

    plots::UpdatePlotLine x plot4::bluePen::variable
  ]

  if plot4::greenPen::variableCurrent != ""
  [
    set-current-plot plot4::name
    set-current-plot-pen "greenPen"

    plots::UpdatePlotLine x plot4::greenPen::variable
  ]

  if plot4::pinkPen::variableCurrent != ""
  [
    set-current-plot plot4::name
    set-current-plot-pen "pinkPen"

    plots::UpdatePlotLine x plot4::pinkPen::variable
  ]
end 


; Draws the last specified varaible value on the current plot with the current pen

to plots::UpdatePlotLine [ x  variable ]

  let valueList plots::GetValuesList variable

  plotxy x last valueList
end 



; Checks if any of the plots must be redrawn because the data to plot has changed

to plots::Redraw

  if plot1::bluePen::variable != plot1::bluePen::variableCurrent
  [
    set-current-plot plot1::name
    set-current-plot-pen "bluePen"
    set plot1::bluePen::variableCurrent plot1::bluePen::variable

    plots::DrawPlotLine plot1::bluePen::variable
  ]

  if plot1::greenPen::variable != plot1::greenPen::variableCurrent
  [
    set-current-plot plot1::name
    set-current-plot-pen "greenPen"
    set plot1::greenPen::variableCurrent plot1::greenPen::variable

    plots::DrawPlotLine plot1::greenPen::variable
  ]

  if plot1::pinkPen::variable != plot1::pinkPen::variableCurrent
  [
    set-current-plot plot1::name
    set-current-plot-pen "pinkPen"
    set plot1::pinkPen::variableCurrent plot1::pinkPen::variable

    plots::DrawPlotLine plot1::pinkPen::variable
  ]

  if plot2::bluePen::variable != plot2::bluePen::variableCurrent
  [
    set-current-plot plot2::name
    set-current-plot-pen "bluePen"
    set plot2::bluePen::variableCurrent plot2::bluePen::variable

    plots::DrawPlotLine plot2::bluePen::variable
  ]

  if plot2::greenPen::variable != plot2::greenPen::variableCurrent
  [
    set-current-plot plot2::name
    set-current-plot-pen "greenPen"
    set plot2::greenPen::variableCurrent plot2::greenPen::variable

    plots::DrawPlotLine plot2::greenPen::variable
  ]

  if plot2::pinkPen::variable != plot2::pinkPen::variableCurrent
  [
    set-current-plot plot2::name
    set-current-plot-pen "pinkPen"
    set plot2::pinkPen::variableCurrent plot2::pinkPen::variable

    plots::DrawPlotLine plot2::pinkPen::variable
  ]

  if plot3::bluePen::variable != plot3::bluePen::variableCurrent
  [
    set-current-plot plot3::name
    set-current-plot-pen "bluePen"
    set plot3::bluePen::variableCurrent plot3::bluePen::variable

    plots::DrawPlotLine plot3::bluePen::variable
  ]

  if plot3::greenPen::variable != plot3::greenPen::variableCurrent
  [
    set-current-plot plot3::name
    set-current-plot-pen "greenPen"
    set plot3::greenPen::variableCurrent plot3::greenPen::variable

    plots::DrawPlotLine plot3::greenPen::variable
  ]

  if plot3::pinkPen::variable != plot3::pinkPen::variableCurrent
  [
    set-current-plot plot3::name
    set-current-plot-pen "pinkPen"
    set plot3::pinkPen::variableCurrent plot3::pinkPen::variable

    plots::DrawPlotLine plot3::pinkPen::variable
  ]

  if plot4::bluePen::variable != plot4::bluePen::variableCurrent
  [
    set-current-plot plot4::name
    set-current-plot-pen "bluePen"
    set plot4::bluePen::variableCurrent plot4::bluePen::variable

    plots::DrawPlotLine plot4::bluePen::variable
  ]

  if plot4::greenPen::variable != plot4::greenPen::variableCurrent
  [
    set-current-plot plot4::name
    set-current-plot-pen "greenPen"
    set plot4::greenPen::variableCurrent plot4::greenPen::variable

    plots::DrawPlotLine plot4::greenPen::variable
  ]

  if plot4::pinkPen::variable != plot4::pinkPen::variableCurrent
  [
    set-current-plot plot4::name
    set-current-plot-pen "pinkPen"
    set plot4::pinkPen::variableCurrent plot4::pinkPen::variable

    plots::DrawPlotLine plot4::pinkPen::variable
  ]
end 


; Draws the specified varaible values on the current plot with the current pen

to plots::DrawPlotLine [ variable ]

  plot-pen-reset

  if variable != ""
  [
    let valueList plots::GetValuesList variable
    let x 0

    foreach valueList
    [
      y -> plotxy x y
      set x x + 1
    ]
  ]
end 


; Given a variable name returns the list that contains the value series of that variable

to-report plots::GetValuesList [ variable ]

  if variable = "Bank Count"                          [ report world::bankCountValues ]
  if variable = "Compensation of employees - nominal" [ report world::coeRealValues ]
  if variable = "Compensation of employees - real"    [ report world::coeNominalValues ]
  if variable = "Company Count"                       [ report world::companyCountValues ]
  if variable = "Consumption - nominal"               [ report world::consumptionNominalValues ]
  if variable = "Consumption - real"                  [ report world::consumptionRealValues ]
  if variable = "GDP - nominal"                       [ report world::gdpNominalValues ]
  if variable = "GDP - real"                          [ report world::gdpRealValues ]
  if variable = "Goods Average price"                 [ report world::goodsPriceAvgValues ]
  if variable = "Goods Minimum price"                 [ report world::goodsPriceMinValues ]
  if variable = "Goods Maximum price"                 [ report world::goodsPriceMaxValues ]
  if variable = "Goods Produced"                      [ report world::goodsProducedValues ]
  if variable = "Goods Sold"                          [ report world::goodsSoldValues ]
  if variable = "Govern Consumption Tax Income"       [ report world::governConsumptionTaxIncomeNominalValues ]
  if variable = "Govern Corporation Tax Income"       [ report world::governCorporationTaxIncomeNominalValues ]
  if variable = "Govern Financial Expense"            [ report world::governFinancialExpenseValues ]
  if variable = "Govern Income"                       [ report world::governIncomeNominalValues ]
  if variable = "Govern Income Tax Income"            [ report world::governIncomeTaxIncomeNominalValues ]
  if variable = "Govern Salaries"                     [ report world::governSalariesNominalValues ]
  if variable = "Govern Unemployment Benefit Expenses"[ report world::governUnemploymentBenefitExpensesNominalValues ]
  if variable = "M1"                                  [ report world::m1Values ]
  if variable = "M1 Target"                           [ report world::m1TargetValues ]
  if variable = "Gross Operating surplus - nominal"   [ report world::gosNominalValues ]
  if variable = "Gross Operating surplus - real"      [ report world::gosRealValues ]
  if variable = "Investment - nominal"                [ report world::investmentNominalValues ]
  if variable = "Investment - real"                   [ report world::investmentRealValues ]
  if variable = "Price Index"                         [ report world::priceIndexValues ]
  if variable = "Public Expenditure"                  [ report world::publicExpenditureNominalValues ]
  if variable = "Salary Average"                      [ report world::salaryAvgValues ]
  if variable = "Salary Maximum"                      [ report world::salaryMaxValues ]
  if variable = "Salary Minimum"                      [ report world::salaryMinValues ]
  if variable = "Technology"                          [ report world::technologyValues ]
  if variable = "Unemployment"                        [ report world::unemploymentValues ]
end 


; Draws a line to mark an event on all the plots

to plots::DrawEvent [ month ]

  set-current-plot plot1::name
  plots::DrawEventLine month

  set-current-plot plot2::name
  plots::DrawEventLine month

  set-current-plot plot3::name
  plots::DrawEventLine month

  set-current-plot plot4::name
  plots::DrawEventLine month
end 

; Draws a line to mark an event on the current plot

to plots::DrawEventLine[ month ]

  create-temporary-plot-pen "eventPen"
  set-plot-pen-color red

  plot-pen-up
  plotxy month plot-y-min
  plot-pen-down
  plotxy month plot-y-max
end 


; Set the people histogram and agent color according to the selected attribute

to people::Classify

  if ticks mod world::ticksPerMonth = world::ticksPerMonth - 1 or people::currentClassifyBy != people::classifyBy
  [
    set-current-plot peoplePlot::name

    ifelse people::classifyBy = ""
    [
      ask people
      [
        set color style::personColor
      ]

      clear-plot
    ]
    [
      let valueList []

      if people::classifyBy = "Cash"                 [ set valueList [ person::cash ] of people ]
      if people::classifyBy = "Cash target"          [ set valueList [ person::targetCash ] of people ]
      if people::classifyBy = "Color"                [ set valueList [ person::color ] of people ]
      if people::classifyBy = "Consumed goods"       [ set valueList [ person::last::consumedGoods ] of people ]
      if people::classifyBy = "Consumed goods value" [ set valueList [ person::last::consumedGoodsValue ] of people ]
      if people::classifyBy = "Employed"             [ set valueList [ person::employed ] of people ]
      if people::classifyBy = "Investment income"    [ set valueList [ person::last::investmentIncome ] of people ]
      if people::classifyBy = "Propensity to consume"[ set valueList [ person::propensityToConsume ] of people ]
      if people::classifyBy = "Stocks"               [ set valueList [ person::stocks ] of people ]
      if people::classifyBy = "Stocks wanted"        [ set valueList [ person::targetStocks ] of people ]
      if people::classifyBy = "Total income"         [ set valueList [ person::last::workIncome + person::last::investmentIncome + person::last::transferIncome] of people ]
      if people::classifyBy = "Transfer income"      [ set valueList [ person::last::transferIncome ] of people ]
      if people::classifyBy = "Work income"          [ set valueList [ person::last::workIncome ] of people ]

      let minValue min valueList
      let maxValue max valueList
      let valueRange maxValue - minValue

      set maxValue maxValue + 0.1 ; Ensure top values appear in the histogram

      ask people
      [
        let value 0

        if people::classifyBy = "Cash"                 [ set value person::cash ]
        if people::classifyBy = "Cash target"          [ set value person::targetCash ]
        if people::classifyBy = "Color"                [ set value person::color ]
        if people::classifyBy = "Consumed goods"       [ set value person::last::consumedGoods ]
        if people::classifyBy = "Consumed goods value" [ set value person::last::consumedGoodsValue ]
        if people::classifyBy = "Employed"             [ set value person::employed ]
        if people::classifyBy = "Investment income"    [ set value person::last::investmentIncome ]
        if people::classifyBy = "Propensity to consume"[ set value person::propensityToConsume ]
        if people::classifyBy = "Stocks"               [ set value person::stocks]
        if people::classifyBy = "Stocks wanted"        [ set value person::targetStocks]
        if people::classifyBy = "Total income"         [ set value person::last::workIncome + person::last::investmentIncome + person::last::transferIncome]
        if people::classifyBy = "Transfer income"      [ set value person::last::transferIncome]
        if people::classifyBy = "Work income"          [ set value person::last::workIncome]

        ; Normalize the value and set it in the 0..255 range to color the agent
        if valueRange != 0
        [
          set value ( ( value - minValue ) / valueRange )
        ]

        let iValue floor( value * 255 )

        set color rgb ( 255 - iValue) iValue 0
      ]

      set-plot-x-range world::round2(minValue) world::round2(maxValue)
      set-plot-y-range 0 count people
      set-histogram-num-bars 10

      histogram valueList
    ]
  ]
end 


; Set the companies histogram and agent color according to the selected attribute

to companies::Classify

  if ticks mod world::ticksPerMonth = world::ticksPerMonth - 1 or companies::currentClassifyBy != companies::classifyBy
  [
    set-current-plot companyPlot::name
    ifelse companies::classifyBy = "" or count companies = 0
    [
      ask companies
      [
        set color style::companyColor
      ]

      clear-plot
    ]
    [
      let valueList []

      if companies::classifyBy = "Age"                    [ set valueList [ company::age  ] of companies ]
      if companies::classifyBy = "Bonds"                  [ set valueList [ company::bonds ] of companies ]
      if companies::classifyBy = "Bonds Target"           [ set valueList [ company::targetBonds ] of companies ]
      if companies::classifyBy = "Cash"                   [ set valueList [ company::cash  ] of companies ]
      if companies::classifyBy = "Cash Target"            [ set valueList [ company::targetCash ] of companies ]
      if companies::classifyBy = "Color"                  [ set valueList [ company::color ] of companies ]
      if companies::classifyBy = "Coupon"                 [ set valueList [ company::lastCoupon ] of companies ]
      if companies::classifyBy = "Demand"                 [ set valueList [ company::last::demand ] of companies ]
      if companies::classifyBy = "Financial Expense"      [ set valueList [ company::last::financialExpense ] of companies ]
      if companies::classifyBy = "Inventory"              [ set valueList [ company::inventory ] of companies ]
      if companies::classifyBy = "Inventory Cost Per Unit"[ set valueList [ company::inventoryUnitaryValue ] of companies ]
      if companies::classifyBy = "Labor Costs"            [ set valueList [ company::last::laborCosts ] of companies ]
      if companies::classifyBy = "Non-current Assets"     [ set valueList [ company::nonCurrentAssets ] of companies ]
      if companies::classifyBy = "Operating Profit"       [ set valueList [ company::last::operatingProfit ] of companies ]
      if companies::classifyBy = "Production"             [ set valueList [ company::last::production ] of companies ]
      if companies::classifyBy = "Production Value"       [ set valueList [ company::last::productionValue ] of companies ]
      if companies::classifyBy = "Profit Before Tax"      [ set valueList [ company::last::profitBeforeTax ] of companies ]
      if companies::classifyBy = "Result"                 [ set valueList [ company::last::result ] of companies ]
      if companies::classifyBy = "Risk"                   [ set valueList [ company::risk ] of companies ]
      if companies::classifyBy = "Salary"                 [ set valueList [ company::salary ] of companies ]
      if companies::classifyBy = "Sale Price"             [ set valueList [ company::salePrice ] of companies ]
      if companies::classifyBy = "Sales"                  [ set valueList [ company::last::sales ] of companies ]
      if companies::classifyBy = "Sales Value"            [ set valueList [ company::last::salesValue ] of companies ]
      if companies::classifyBy = "Stock Profitability"    [ set valueList [ company::stockProfitability ] of companies ]
      if companies::classifyBy = "Stocks"                 [ set valueList [ company::stocks ] of companies ]
      if companies::classifyBy = "Stocks On Sale"         [ set valueList [ company::stocksOnSale ] of companies ]
      if companies::classifyBy = "Target Debt Ratio"      [ set valueList [ company::targetDebtRatio ] of companies ]
      if companies::classifyBy = "Vacancies"              [ set valueList [ company::vacancies ] of companies ]
      if companies::classifyBy = "Workers"                [ set valueList [ company::workers ] of companies ]

      let minValue min valueList
      let maxValue max valueList
      let valueRange maxValue - minValue
      set maxValue maxValue + 0.1 ; Ensure top values appear in the histogram

      ask companies
      [
        let value 0

        if companies::classifyBy = "Age"                    [ set value company::age ]
        if companies::classifyBy = "Bonds"                  [ set value company::bonds ]
        if companies::classifyBy = "Bonds Target"           [ set value company::targetBonds ]
        if companies::classifyBy = "Cash"                   [ set value company::cash ]
        if companies::classifyBy = "Cash Target"            [ set value company::targetCash ]
        if companies::classifyBy = "Color"                  [ set value company::color ]
        if companies::classifyBy = "Coupon"                 [ set value company::lastCoupon ]
        if companies::classifyBy = "Demand"                 [ set value company::last::demand ]
        if companies::classifyBy = "Financial Expense"      [ set value company::last::financialExpense ]
        if companies::classifyBy = "Inventory"              [ set value company::inventory ]
        if companies::classifyBy = "Inventory Cost Per Unit"[ set value company::inventoryUnitaryValue ]
        if companies::classifyBy = "Labor Costs"            [ set value company::last::laborCosts ]
        if companies::classifyBy = "Non-current Assets"     [ set value company::nonCurrentAssets ]
        if companies::classifyBy = "Operating Profit"       [ set value company::last::operatingProfit ]
        if companies::classifyBy = "Production"             [ set value company::last::production ]
        if companies::classifyBy = "Production Value"       [ set value company::last::productionValue ]
        if companies::classifyBy = "Profit Before Tax"      [ set value company::last::profitBeforeTax ]
        if companies::classifyBy = "Result"                 [ set value company::last::result ]
        if companies::classifyBy = "Risk"                   [ set value company::risk ]
        if companies::classifyBy = "Salary"                 [ set value company::salary ]
        if companies::classifyBy = "Sale Price"             [ set value company::salePrice ]
        if companies::classifyBy = "Sales"                  [ set value company::last::sales ]
        if companies::classifyBy = "Sales Value"            [ set value company::last::salesValue ]
        if companies::classifyBy = "Stock Profitability"    [ set value company::stockProfitability ]
        if companies::classifyBy = "Stocks"                 [ set value company::stocks ]
        if companies::classifyBy = "Stocks On Sale"         [ set value company::stocksOnSale ]
        if companies::classifyBy = "Target Debt Ratio"      [ set value company::targetDebtRatio ]
        if companies::classifyBy = "Vacancies"              [ set value company::vacancies ]
        if companies::classifyBy = "Workers"                [ set value company::workers ]

        ; Normalize the value and set it in the 0..255 range to color the agent
        ifelse valueRange != 0
        [
          set value ( ( value - minValue ) / valueRange )
        ]
        [
          set value value - minValue
        ]

        let iValue floor( value * 255 )

        set color rgb ( 255 - iValue) iValue 0
      ]

      set-plot-x-range world::round2(minValue) world::round2(maxValue)
      set-plot-y-range 0 count companies
      set-histogram-num-bars 10

      histogram valueList

    ]
  ]
end 


; Set the banks histogram and agent color according to the selected attribute

to banks::Classify

  if ticks mod world::ticksPerMonth = world::ticksPerMonth - 1 or banks::currentClassifyBy != banks::classifyBy
  [
    set-current-plot bankPlot::name
    ifelse banks::classifyBy = "" or count banks = 0
    [
      ask banks
      [
        set color style::bankColor
      ]

      clear-plot
    ]
    [
      let valueList []

      if banks::classifyBy = "Age"                    [ set valueList [ bank::age  ] of banks ]
      if banks::classifyBy = "Bonds"                  [ set valueList [ bank::bonds ] of banks ]
      if banks::classifyBy = "Bonds Target"           [ set valueList [ bank::targetBonds ] of banks ]
      if banks::classifyBy = "Cash"                   [ set valueList [ bank::cash  ] of banks ]
      if banks::classifyBy = "Cash Target"            [ set valueList [ bank::targetCash ] of banks ]
      if banks::classifyBy = "Financial Expense"      [ set valueList [ bank::last::financialExpense ] of banks ]
      if banks::classifyBy = "Financial Income"       [ set valueList [ bank::last::financialIncome ] of banks ]
      if banks::classifyBy = "Labor Costs"            [ set valueList [ bank::last::laborCosts ] of banks ]
      if banks::classifyBy = "Loans"                  [ set valueList [ bank::loans ] of banks ]
      if banks::classifyBy = "Loans Target"           [ set valueList [ bank::targetLoans ] of banks ]
      if banks::classifyBy = "Non-current Assets"     [ set valueList [ bank::nonCurrentAssets ] of banks ]
      if banks::classifyBy = "Operating Profit"       [ set valueList [ bank::last::operatingProfit ] of banks ]
      if banks::classifyBy = "Profit Before Tax"      [ set valueList [ bank::last::profitBeforeTax ] of banks ]
      if banks::classifyBy = "Result"                 [ set valueList [ bank::last::result ] of banks ]
      if banks::classifyBy = "Risk"                   [ set valueList [ bank::risk ] of banks ]
      if banks::classifyBy = "Risk Target"            [ set valueList [ bank::targetRisk ] of banks ]
      if banks::classifyBy = "Salary"                 [ set valueList [ bank::salary ] of banks ]
      if banks::classifyBy = "Stocks"                 [ set valueList [ bank::stocks ] of banks ]
      if banks::classifyBy = "Stock Profitability"    [ set valueList [ bank::stockProfitability ] of banks ]
      if banks::classifyBy = "Stocks On Sale"         [ set valueList [ bank::stocksOnSale ] of banks ]
      if banks::classifyBy = "Target Debt Ratio"      [ set valueList [ bank::targetDebtRatio ] of banks ]
      if banks::classifyBy = "Vacancies"              [ set valueList [ bank::vacancies ] of banks ]
      if banks::classifyBy = "Workers"                [ set valueList [ bank::workers ] of banks ]

      let minValue min valueList
      let maxValue max valueList
      let valueRange maxValue - minValue
      set maxValue maxValue + 0.1 ; Ensure top values appear in the histogram

      ask banks
      [
        let value 0

        if banks::classifyBy = "Age"                    [ set value bank::age  ]
        if banks::classifyBy = "Bonds"                  [ set value bank::bonds ]
        if banks::classifyBy = "Bonds Target"           [ set value bank::targetBonds ]
        if banks::classifyBy = "Cash"                   [ set value bank::cash ]
        if banks::classifyBy = "Cash Target"            [ set value bank::targetCash ]
        if banks::classifyBy = "Financial Expense"      [ set value bank::last::financialExpense ]
        if banks::classifyBy = "Financial Income"       [ set value bank::last::financialIncome ]
        if banks::classifyBy = "Labor Costs"            [ set value bank::last::laborCosts ]
        if banks::classifyBy = "Loans"                  [ set value bank::loans ]
        if banks::classifyBy = "Loans Target"           [ set value bank::targetLoans ]
        if banks::classifyBy = "Non-current Assets"     [ set value bank::nonCurrentAssets ]
        if banks::classifyBy = "Operating Profit"       [ set value bank::last::operatingProfit ]
        if banks::classifyBy = "Profit Before Tax"      [ set value bank::last::profitBeforeTax ]
        if banks::classifyBy = "Result"                 [ set value bank::last::result ]
        if banks::classifyBy = "Risk"                   [ set value bank::risk ]
        if banks::classifyBy = "Risk Target"            [ set value bank::targetRisk ]
        if banks::classifyBy = "Salary"                 [ set value bank::salary ]
        if banks::classifyBy = "Stocks"                 [ set value bank::stocks ]
        if banks::classifyBy = "Stock Profitability"    [ set value bank::stockProfitability ]
        if banks::classifyBy = "Stocks On Sale"         [ set value bank::stocksOnSale ]
        if banks::classifyBy = "Target Debt Ratio"      [ set value bank::targetDebtRatio ]
        if banks::classifyBy = "Vacancies"              [ set value bank::vacancies ]
        if banks::classifyBy = "Workers"                [ set value bank::workers ]

        ; Normalize the value and set it in the 0..255 range to color the agent
        ifelse valueRange != 0
        [
          set value ( ( value - minValue ) / valueRange )
        ]
        [
          set value value - minValue
        ]

        let iValue floor( value * 255 )

        set color rgb ( 255 - iValue) iValue 0
      ]

      set-plot-x-range world::round2(minValue) world::round2(maxValue)
      set-plot-y-range 0 count banks
      set-histogram-num-bars 10

      histogram valueList

    ]
  ]
end 



;;;;;;;;;;;;;;;;;:;;;;;:;:;;;:::;:;:;:;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; SELECTION MANAGEMENT
;

to selection::Initialize
  ; Selection management initialization
  set selection::selectedPatch nobody
  set selection::selectedAgent nobody
  set selection::currentPatch  nobody
  set selection::interactionRadius 0

  set selection::currentShowConsumptions selection::showConsumptions
  set selection::currentShowJobs selection::showJobs
  set selection::currentShowInvestments selection::showInvestments
  set selection::currentShowBonds selection::showBonds
  set selection::currentShowLoans selection::showLoans

  set selection::colors::selected white
  set selection::colors::mouseOver gray
  set selection::colors::selectedRadious rgb 97 103 116 ; dark gray
  set selection::colors::notSelected  black
end 


; Handles when the user clicks on a patch to select or deselect it. It also highlights the patch where the mouse is over.

to selection::MouseSelect [ updateOnTicks ]

  ; Display where the mouse is
  selection::MouseOver

  ; Shows or hides the agent relationships when the user clicks on the switch
  selection::UpdateRelationshipsView

  ; Select a patch if the user clicks on it
  ifelse mouse-down?
  [
    let previousSelectedPatch selection::selectedPatch

    while [mouse-down?]
    [
      selection::MouseOver
    ]

    if selection::currentPatch != nobody
    [
      ifelse selection::currentPatch != selection::selectedPatch
      [ ; select the current patch
        set selection::selectedPatch selection::currentPatch
        set selection::selectedAgent one-of turtles-on selection::selectedPatch
      ]
      [ ; Selecting the currently selected patch -> deselect
        set selection::selectedPatch nobody
        set selection::selectedAgent nobody
      ]
    ]

    selection::SelectPatch previousSelectedPatch
  ]
  [ ; Update the selection at least every month
    if updateOnTicks and ticks mod world::ticksPerMonth = world::ticksPerMonth - 1
    [
      selection::SelectPatch nobody
    ]
  ]
end 


; Shows the patch where the mouse is currently over in gray and updates selection::currentPatch global variable

to selection::MouseOver

  let previousCurrentPatch selection::currentPatch

  ifelse mouse-inside?
  [
    set selection::currentPatch patch mouse-xcor mouse-ycor
  ]
  [
    set selection::currentPatch nobody
  ]

  ; check if the previousCurrentPatch is in the interaction area of the currently selected patch
  let isInRadious false
  if selection::interactionRadius > 0 and previousCurrentPatch != nobody and selection::selectedPatch != nobody
  [
    ask previousCurrentPatch [ set isInRadious distance selection::selectedPatch <= selection::interactionRadius ]
  ]

  if previousCurrentPatch != nobody and previousCurrentPatch != selection::currentPatch and previousCurrentPatch != selection::selectedPatch
  [
    ask previousCurrentPatch [ set pcolor ifelse-value (isInRadious) [selection::colors::selectedRadious] [ selection::colors::notSelected ] ]
  ]
  if selection::currentPatch != nobody and selection::currentPatch != selection::selectedPatch
  [
    ask selection::currentPatch [ set pcolor selection::colors::mouseOver ]
  ]
end 


; Given a patch shows information about itself and the agents inside to the output control

to selection::SelectPatch [ previousSelectedPatch ]

  clear-output
  ask Consumptions with [ not hidden? ] [ set hidden? true ]
  ask Jobs with [ not hidden? ] [ set hidden? true ]
  ask Investments with [ not hidden? ] [ set hidden? true ]
  ask Bonds with [ not hidden? ] [ set hidden? true ]
  ask Loans with [ not hidden? ] [ set hidden? true ]


  ; Paint the previously selected patch back to its original value and erase its interaction area
  if previousSelectedPatch != nobody and previousSelectedPatch != selection::selectedPatch
  [
    ask previousSelectedPatch
    [
      set pcolor ifelse-value (previousSelectedPatch != selection::currentPatch) [ selection::colors::notSelected ] [ selection::colors::mouseOver ]
    ]
    if selection::interactionRadius > 0
    [
      ask patches with [ distance previousSelectedPatch <= selection::interactionRadius and self != previousSelectedPatch ]
      [
        set pcolor ifelse-value (self != selection::currentPatch) [ selection::colors::notSelected ] [ selection::colors::mouseOver ]
      ]
    ]
  ]
  ; Paint the selected patch as selected
  if selection::selectedPatch != nobody
  [
    ask selection::selectedPatch [ set pcolor selection::colors::selected ]
  ]


  if selection::selectedPatch != nobody
  [
    output-print ( word "Patch " ( [pxcor] of selection::selectedPatch ) " " [pycor] of selection::selectedPatch )

    set selection::interactionRadius 0


    if selection::selectedAgent != nobody
    [
      ; Show information about the person in this patch
      if is-person? selection::selectedAgent
      [
        ask selection::selectedAgent
        [
          person::ShowInfo
        ]

        set selection::interactionRadius world::interactionRadius
      ]

      ; Show information about the company in this patch
      if is-company? selection::selectedAgent
      [
        ask selection::selectedAgent
        [
          company::ShowInfo
        ]
        set selection::interactionRadius world::interactionRadius
      ]

      ; Show information about the company in this patch
      if is-govern? selection::selectedAgent
      [
        ask selection::selectedAgent
        [
          govern::ShowInfo
        ]
        set selection::interactionRadius world::governInteractionRadius
      ]

      ; Show information about the company in this patch
      if is-bank? selection::selectedAgent
      [
        ask selection::selectedAgent
        [
          bank::ShowInfo
        ]
        set selection::interactionRadius world::interactionRadius
      ]

      ; Show information about the company in this patch
      if is-centralBank? selection::selectedAgent
      [
        ask selection::selectedAgent
        [
          centralBank::ShowInfo
        ]
        set selection::interactionRadius 0
      ]


      ; Paint the distance at which the agent can interact
      if selection::interactionRadius > 0
      [
        ask patches with [ distance selection::selectedPatch <= selection::interactionRadius and self != selection::selectedPatch ] [ set pcolor selection::colors::selectedRadious ]
      ]

      ; Show its relationships
      ask selection::selectedAgent
      [
        if selection::currentShowConsumptions
        [
          ask my-Consumptions
          [
            set hidden? false
          ]
        ]
        if selection::currentShowJobs
        [
          ask my-Jobs
          [
            set hidden? false
          ]
        ]
        if selection::currentShowInvestments
        [
          ask my-Investments
          [
            set hidden? false
          ]
        ]
        if selection::currentShowBonds
        [
          ask my-Bonds
          [
            set hidden? false
          ]
        ]
        if selection::currentShowLoans
        [
          ask my-Loans
          [
            set hidden? false
          ]
        ]
      ]
    ]
  ]
end 


; Checks if a switch has changed and displays the relationships accordingly

to selection::UpdateRelationshipsView

  if selection::currentShowConsumptions != selection::ShowConsumptions
  [
    ifelse selection::ShowConsumptions
    [ ; show consumptions for the selected patch (if any)
      if selection::selectedAgent != nobody
      [
        ask selection::selectedAgent
        [
          ask my-Consumptions
          [
            set hidden? false
          ]
        ]
      ]
    ]
    [ ; Hide all the consumptions
      ask Consumptions with [ not hidden? ] [ set hidden? true ]
    ]

    set selection::currentShowConsumptions selection::ShowConsumptions
  ]


  if selection::currentShowJobs != selection::ShowJobs
  [
    ifelse selection::ShowJobs
    [ ; show jobs for the selected patch (if any)

      if selection::selectedAgent != nobody
      [
        ask selection::selectedAgent
        [
          ask my-Jobs
          [
            set hidden? false
          ]
        ]
      ]
    ]
    [ ; Hide all the jobs
      ask Jobs with [ not hidden? ] [ set hidden? true ]
    ]

    set selection::currentShowJobs selection::ShowJobs
  ]

  if selection::currentShowInvestments != selection::ShowInvestments
  [
    ifelse selection::ShowInvestments
    [ ; show investements for the selected patch (if any)

      if selection::selectedAgent != nobody
      [
        ask selection::selectedAgent
        [
          ask my-Investments
          [
            set hidden? false
          ]
        ]
      ]
    ]
    [ ; Hide all the investements
      ask Investments with [ not hidden? ] [ set hidden? true ]
    ]

    set selection::currentShowInvestments selection::ShowInvestments
  ]

  if selection::currentShowBonds != selection::showBonds
  [
    ifelse selection::showBonds
    [ ; show investements for the selected patch (if any)

      if selection::selectedAgent != nobody
      [
        ask selection::selectedAgent
        [
          ask my-Bonds
          [
            set hidden? false
          ]
        ]
      ]
    ]
    [ ; Hide all the bonds
      ask Bonds with [ not hidden? ] [ set hidden? true ]
    ]

    set selection::currentShowBonds selection::showBonds
  ]

  if selection::currentShowLoans != selection::showLoans
  [
    ifelse selection::showLoans
    [ ; show investements for the selected patch (if any)

      if selection::selectedAgent != nobody
      [
        ask selection::selectedAgent
        [
          ask my-Loans
          [
            set hidden? false
          ]
        ]
      ]
    ]
    [ ; Hide all the loans
      ask Loans with [ not hidden? ] [ set hidden? true ]
    ]

    set selection::currentShowLoans selection::showLoans
  ]
end 

There is only one version of this model, created 5 months ago by Ander G.

Attached files

File Type Description Last updated
DIMMBA.pdf pdf Project document(Spanish): Iterative developement of an agent based macroeconomic model. 5 months ago, by Ander G Download
Macroeconomy.png preview Preview for 'Macroeconomy' 5 months ago, by Ander G Download

This model does not have any ancestors.

This model does not have any descendants.