diff --git a/.gitignore b/.gitignore index c6e0307..92f8dcf 100644 --- a/.gitignore +++ b/.gitignore @@ -51,4 +51,3 @@ .Trashes ehthumbs.db Thumbs.db -__init__.py diff --git a/.gitmodules b/.gitmodules index 4f9317a..c1beacf 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "abm_template"] path = abm_template - url = git://github.com/cogeorg/abm_template/ \ No newline at end of file + url = git://github.com/cogeorg/abm_template/ diff --git a/README.md b/README.md index 3c7b4e3..c5864d2 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,27 @@ This is the new repository for black_rhino and takes over from http://sourceforge.net/projects/oxblackrhino/?source=directory as of 2015-11-07. - +Minimal rhino branch aims at a complete overhaul in terms of code for an agent-based model, it is implemented from the very basics to provide greater clarity and quality. + +INSTALL +git clone https://github.com/cogeorg/black_rhino +once you have the repo, you need to clone abm_template as well +git submodule update --init --recursive + +FOLDER STRUCTURE + ++---.git -git structure files ++---abm_template -abstract base classes (submodule from https://github.com/cogeorg/abm_template) ++---agents -config files for agents +| +---banks +| +---firms +| \---households ++---environments -config files for environment ++---log -log files ++---networkx -networkx package for network capabilities ++---src -source code files ++---tests -redundant structure for tests +\---tools -specific tools + +INSTRUCTIONS -- to be updated with the new code Introduction @@ -81,4 +103,4 @@ black_rhino has a separate program called ./br-make_tests.py that is used to exe References -Georg, Co-Pierre, „The Effect of the Interbank Network Structure on Contagion and Common Shocks“, Deutsche Bundesbank Working Paper Series 2, 12-2011, (2011) \ No newline at end of file +Georg, Co-Pierre, „The Effect of the Interbank Network Structure on Contagion and Common Shocks“, Deutsche Bundesbank Working Paper Series 2, 12-2011, (2011) diff --git a/agents/banks/bank_for_tests.xml b/agents/banks/bank_for_tests.xml new file mode 100644 index 0000000..bf6faf2 --- /dev/null +++ b/agents/banks/bank_for_tests.xml @@ -0,0 +1,2 @@ + + diff --git a/agents/central_bank/central_bank_for_tests.xml b/agents/central_bank/central_bank_for_tests.xml new file mode 100644 index 0000000..449647e --- /dev/null +++ b/agents/central_bank/central_bank_for_tests.xml @@ -0,0 +1,3 @@ + + + diff --git a/agents/firms/firm_for_tests.xml b/agents/firms/firm_for_tests.xml new file mode 100644 index 0000000..c047065 --- /dev/null +++ b/agents/firms/firm_for_tests.xml @@ -0,0 +1,3 @@ + + + diff --git a/agents/households/household_for_tests.xml b/agents/households/household_for_tests.xml new file mode 100644 index 0000000..7d7fd64 --- /dev/null +++ b/agents/households/household_for_tests.xml @@ -0,0 +1,4 @@ + + + + diff --git a/black_rhino.py b/black_rhino.py index f516378..8faa00c 100644 --- a/black_rhino.py +++ b/black_rhino.py @@ -3,6 +3,8 @@ # -*- coding: utf-8 -*- """ +This is a minimal example. + black_rhino is a multi-agent simulator for financial network analysis Copyright (C) 2012 Co-Pierre Georg (co-pierre.georg@keble.ox.ac.uk) @@ -28,22 +30,18 @@ # # ------------------------------------------------------------------------- if __name__ == '__main__': - import pdb # python debugger, for debugging purposes only import sys - # sys.path.append('src/') import logging - import networkx as nx from src.environment import Environment from src.runner import Runner - from src.measurement import Measurement - args = ['./black_rhino.py', "src/environments/", "test3", "src/log/", "src/measurements/"] + args = ['./black_rhino.py', "tests/environments/", "test_all_methods", "tests/log/"] # args = sys.argv - if len(args) != 5: - print "Usage: ./black_rhino environment_directory/ environment_identifier log_directory/ measurement_directory/" + if len(args) != 4: + print "Usage: ./black_rhino environment_directory/ environment_identifier log_directory/" sys.exit() @@ -53,36 +51,27 @@ environment_directory = str(args[1]) identifier = str(args[2]) log_directory = str(args[3]) - measurement_directory = str(args[4]) # Configure logging parameters so we get output while the program runs - logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', filename=log_directory + identifier + ".log", level=logging.INFO) + logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', + filename=log_directory + identifier + ".log", level=logging.INFO) logging.info('START logging for run: %s', environment_directory + identifier + ".xml") environment = Environment(environment_directory, identifier) - # environment.initialize(environment_directory, identifier) - runner = Runner() - measurement = Measurement() + runner = Runner(environment) # # UPDATE STEP # - for i in range(environment.static_parameters["numSimulations"]): + for i in range(int(environment.num_simulations)): logging.info(' STARTED with run %s', str(i)) environment.initialize(environment_directory, identifier) - # check that environment file has been read correctly - # environment.write_environment_file(identifier) runner.initialize(environment) - measurement.initialize() # clear the previous measurement - # do the run - runner.do_run(measurement, "info") - # do the histograms, i.e. add the current measurement to the histogram - measurement.do_histograms() + runner.do_run(environment) logging.info(' DONE') # # MEASUREMENT AND LOGGING # - measurement.write_histograms(measurement_directory, environment) logging.info('FINISHED logging for run: %s \n', environment_directory + identifier + ".xml") diff --git a/black_rhino_tests.py b/black_rhino_tests.py new file mode 100644 index 0000000..0c7a954 --- /dev/null +++ b/black_rhino_tests.py @@ -0,0 +1,269 @@ +#!/usr/bin/env python +# [SublimeLinter pep8-max-line-length:150] +# -*- coding: utf-8 -*- + +""" +This is a minimal example. + +black_rhino is a multi-agent simulator for financial network analysis +Copyright (C) 2012 Co-Pierre Georg (co-pierre.georg@keble.ox.ac.uk) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, version 3 of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see + +The development of this software has been supported by the ERA-Net +on Complexity through the grant RESINEE. +""" + +# ------------------------------------------------------------------------- +# +# MAIN +# +# ------------------------------------------------------------------------- +if __name__ == '__main__': + import pdb # python debugger, for debugging purposes only + + import sys + # sys.path.append('src/') + import logging + + from tests.tests_bank import TestsBank + from tests.tests_central_bank import TestsCentralBank + from tests.tests_environment import TestsEnvironment + from tests.tests_firm import TestsFirm + from tests.tests_helper import TestsHelper + from tests.tests_household import TestsHousehold + from tests.tests_market import TestsMarket + from tests.tests_measurement import TestsMeasurement + from tests.tests_runner import TestsRunner + from tests.tests_shock import TestsShock + from tests.tests_transaction import TestsTransaction + from tests.tests_updater import TestsUpdater + + test_bank = TestsBank() + test_central_bank = TestsCentralBank() + test_environment = TestsEnvironment() + test_firm = TestsFirm() + test_helper = TestsHelper() + test_household = TestsHousehold() + test_market = TestsMarket() + test_measurement = TestsMeasurement() + test_runner = TestsRunner() + test_shock = TestsShock() + test_transaction = TestsTransaction() + test_updater = TestsUpdater() + + # Tests for Bank + test_bank.bank__get_identifier(["tests/environments/", "test_all_methods", "tests/log/"]) + test_bank.bank__set_identifier(["tests/environments/", "test_all_methods", "tests/log/"]) + test_bank.bank__get_parameters(["tests/environments/", "test_all_methods", "tests/log/"]) + test_bank.bank__set_parameters(["tests/environments/", "test_all_methods", "tests/log/"]) + test_bank.bank__get_state_variables(["tests/environments/", "test_all_methods", "tests/log/"]) + test_bank.bank__set_state_variables(["tests/environments/", "test_all_methods", "tests/log/"]) + test_bank.bank__str(["tests/environments/", "test_all_methods", "tests/log/"]) + test_bank.bank__get_parameters_from_file(["tests/environments/", "test_all_methods", "tests/log/"]) + test_bank.bank__check_consistency(["tests/environments/", "test_all_methods", "tests/log/"]) + test_bank.bank__get_account(["tests/environments/", "test_all_methods", "tests/log/"]) + test_bank.bank__get_account_num_transactions(["tests/environments/", "test_all_methods", "tests/log/"]) + test_bank.bank__add_transaction(["tests/environments/", "test_all_methods", "tests/log/"]) + test_bank.bank__clear_accounts(["tests/environments/", "test_all_methods", "tests/log/"]) + test_bank.bank__purge_accounts(["tests/environments/", "test_all_methods", "tests/log/"]) + test_bank.bank__initialize_standard_bank(["tests/environments/", "test_all_methods", "tests/log/"]) + test_bank.bank__get_transactions_from_file(["tests/environments/", "test_all_methods", "tests/log/"]) + test_bank.bank__getattr(["tests/environments/", "test_all_methods", "tests/log/"]) + + # Tests for CentralBank + test_central_bank.central_bank__get_identifier(["tests/environments/", "test_all_methods", "tests/log/"]) + test_central_bank.central_bank__set_identifier(["tests/environments/", "test_all_methods", "tests/log/"]) + test_central_bank.central_bank__get_parameters(["tests/environments/", "test_all_methods", "tests/log/"]) + test_central_bank.central_bank__set_parameters(["tests/environments/", "test_all_methods", "tests/log/"]) + test_central_bank.central_bank__get_state_variables(["tests/environments/", "test_all_methods", "tests/log/"]) + test_central_bank.central_bank__set_state_variables(["tests/environments/", "test_all_methods", "tests/log/"]) + test_central_bank.central_bank__str(["tests/environments/", "test_all_methods", "tests/log/"]) + test_central_bank.central_bank__get_parameters_from_file(["tests/environments/", "test_all_methods", "tests/log/"]) + test_central_bank.central_bank__check_consistency(["tests/environments/", "test_all_methods", "tests/log/"]) + test_central_bank.central_bank__get_account(["tests/environments/", "test_all_methods", "tests/log/"]) + test_central_bank.central_bank__get_account_num_transactions(["tests/environments/", "test_all_methods", "tests/log/"]) + test_central_bank.central_bank__add_transaction(["tests/environments/", "test_all_methods", "tests/log/"]) + test_central_bank.central_bank__clear_accounts(["tests/environments/", "test_all_methods", "tests/log/"]) + test_central_bank.central_bank__purge_accounts(["tests/environments/", "test_all_methods", "tests/log/"]) + test_central_bank.central_bank__get_transactions_from_file(["tests/environments/", "test_all_methods", "tests/log/"]) + test_central_bank.central_bank__getattr(["tests/environments/", "test_all_methods", "tests/log/"]) + + # Tests for Firm + test_firm.firm__get_identifier(["tests/environments/", "test_all_methods", "tests/log/"]) + test_firm.firm__set_identifier(["tests/environments/", "test_all_methods", "tests/log/"]) + test_firm.firm__get_parameters(["tests/environments/", "test_all_methods", "tests/log/"]) + test_firm.firm__set_parameters(["tests/environments/", "test_all_methods", "tests/log/"]) + test_firm.firm__get_state_variables(["tests/environments/", "test_all_methods", "tests/log/"]) + test_firm.firm__set_state_variables(["tests/environments/", "test_all_methods", "tests/log/"]) + test_firm.firm__str(["tests/environments/", "test_all_methods", "tests/log/"]) + test_firm.firm__get_parameters_from_file(["tests/environments/", "test_all_methods", "tests/log/"]) + test_firm.firm__get_account(["tests/environments/", "test_all_methods", "tests/log/"]) + test_firm.firm__get_account_num_transactions(["tests/environments/", "test_all_methods", "tests/log/"]) + test_firm.firm__add_transaction(["tests/environments/", "test_all_methods", "tests/log/"]) + test_firm.firm__clear_accounts(["tests/environments/", "test_all_methods", "tests/log/"]) + test_firm.firm__purge_accounts(["tests/environments/", "test_all_methods", "tests/log/"]) + test_firm.firm__initialize_standard_firm(["tests/environments/", "test_all_methods", "tests/log/"]) + test_firm.firm__get_transactions_from_file(["tests/environments/", "test_all_methods", "tests/log/"]) + test_firm.firm__getattr(["tests/environments/", "test_all_methods", "tests/log/"]) + + # Tests for Household + test_household.household__get_identifier(["tests/environments/", "test_all_methods", "tests/log/"]) + test_household.household__set_identifier(["tests/environments/", "test_all_methods", "tests/log/"]) + test_household.household__get_parameters(["tests/environments/", "test_all_methods", "tests/log/"]) + test_household.household__set_parameters(["tests/environments/", "test_all_methods", "tests/log/"]) + test_household.household__get_state_variables(["tests/environments/", "test_all_methods", "tests/log/"]) + test_household.household__set_state_variables(["tests/environments/", "test_all_methods", "tests/log/"]) + test_household.household__str(["tests/environments/", "test_all_methods", "tests/log/"]) + test_household.household__get_parameters_from_file(["tests/environments/", "test_all_methods", "tests/log/"]) + test_household.household__get_account(["tests/environments/", "test_all_methods", "tests/log/"]) + test_household.household__get_account_num_transactions(["tests/environments/", "test_all_methods", "tests/log/"]) + test_household.household__add_transaction(["tests/environments/", "test_all_methods", "tests/log/"]) + test_household.household__clear_accounts(["tests/environments/", "test_all_methods", "tests/log/"]) + test_household.household__purge_accounts(["tests/environments/", "test_all_methods", "tests/log/"]) + test_household.household__initialize_standard_household(["tests/environments/", "test_all_methods", "tests/log/"]) + test_household.household__get_transactions_from_file(["tests/environments/", "test_all_methods", "tests/log/"]) + test_household.household__getattr(["tests/environments/", "test_all_methods", "tests/log/"]) + + # Tests for Environment + test_environment.environment__add_static_parameter(["tests/environments/", "test_all_methods", "tests/log/"]) + test_environment.environment__add_variable_parameter(["tests/environments/", "test_all_methods", "tests/log/"]) + test_environment.environment__get_identifier(["tests/environments/", "test_all_methods", "tests/log/"]) + test_environment.environment__set_identifier(["tests/environments/", "test_all_methods", "tests/log/"]) + test_environment.environment__get_static_parameters(["tests/environments/", "test_all_methods", "tests/log/"]) + test_environment.environment__set_static_parameters(["tests/environments/", "test_all_methods", "tests/log/"]) + test_environment.environment__get_variable_parameters(["tests/environments/", "test_all_methods", "tests/log/"]) + test_environment.environment__set_variable_parameters(["tests/environments/", "test_all_methods", "tests/log/"]) + test_environment.environment__get_assets(["tests/environments/", "test_all_methods", "tests/log/"]) + test_environment.environment__set_assets(["tests/environments/", "test_all_methods", "tests/log/"]) + test_environment.environment__get_shocks(["tests/environments/", "test_all_methods", "tests/log/"]) + test_environment.environment__set_shocks(["tests/environments/", "test_all_methods", "tests/log/"]) + test_environment.environment__add_shock(["tests/environments/", "test_all_methods", "tests/log/"]) + test_environment.environment__str(["tests/environments/", "test_all_methods", "tests/log/"]) + test_environment.environment__print_parameters(["tests/environments/", "test_all_methods", "tests/log/"]) + test_environment.environment__write_environment_file(["tests/environments/", "test_all_methods", "tests/log/"]) + test_environment.environment__read_xml_config_file(["tests/environments/", "test_all_methods", "tests/log/"]) + test_environment.environment__initialize(["tests/environments/", "test_all_methods", "tests/log/"]) + test_environment.environment__initialize_banks_from_files(["tests/environments/", "test_all_methods", "tests/log/"]) + test_environment.environment__initialize_firms_from_files(["tests/environments/", "test_all_methods", "tests/log/"]) + test_environment.environment__initialize_households_from_files(["tests/environments/", "test_all_methods", "tests/log/"]) + test_environment.environment__initialize_central_bank_from_files(["tests/environments/", "test_all_methods", "tests/log/"]) + test_environment.environment__get_agent_by_id(["tests/environments/", "test_all_methods", "tests/log/"]) + test_environment.environment__read_transactions_for_banks(["tests/environments/", "test_all_methods", "tests/log/"]) + test_environment.environment__read_transactions_for_firms(["tests/environments/", "test_all_methods", "tests/log/"]) + test_environment.environment__read_transactions_for_households(["tests/environments/", "test_all_methods", "tests/log/"]) + test_environment.environment__read_transactions_for_central_bank(["tests/environments/", "test_all_methods", "tests/log/"]) + test_environment.environment__check_global_transaction_balance(["tests/environments/", "test_all_methods", "tests/log/"]) + test_environment.environment__check_agent_homogeneity(["tests/environments/", "test_all_methods", "tests/log/"]) + test_environment.environment__update_asset_returns(["tests/environments/", "test_all_methods", "tests/log/"]) + + # Tests for Transaction + test_transaction.transaction__init(["tests/environments/", "test_all_methods", "tests/log/"]) + test_transaction.transaction__del(["tests/environments/", "test_all_methods", "tests/log/"]) + test_transaction.transaction__get_type_(["tests/environments/", "test_all_methods", "tests/log/"]) + test_transaction.transaction__set_type_(["tests/environments/", "test_all_methods", "tests/log/"]) + test_transaction.transaction__get_asset(["tests/environments/", "test_all_methods", "tests/log/"]) + test_transaction.transaction__set_asset(["tests/environments/", "test_all_methods", "tests/log/"]) + test_transaction.transaction__get_from_(["tests/environments/", "test_all_methods", "tests/log/"]) + test_transaction.transaction__set_from_(["tests/environments/", "test_all_methods", "tests/log/"]) + test_transaction.transaction__get_to(["tests/environments/", "test_all_methods", "tests/log/"]) + test_transaction.transaction__set_to(["tests/environments/", "test_all_methods", "tests/log/"]) + test_transaction.transaction__get_amount(["tests/environments/", "test_all_methods", "tests/log/"]) + test_transaction.transaction__set_amount(["tests/environments/", "test_all_methods", "tests/log/"]) + test_transaction.transaction__get_interest(["tests/environments/", "test_all_methods", "tests/log/"]) + test_transaction.transaction__set_interest(["tests/environments/", "test_all_methods", "tests/log/"]) + test_transaction.transaction__get_maturity(["tests/environments/", "test_all_methods", "tests/log/"]) + test_transaction.transaction__set_maturity(["tests/environments/", "test_all_methods", "tests/log/"]) + test_transaction.transaction__get_time_of_default(["tests/environments/", "test_all_methods", "tests/log/"]) + test_transaction.transaction__set_time_of_default(["tests/environments/", "test_all_methods", "tests/log/"]) + test_transaction.transaction__this_transaction(["tests/environments/", "test_all_methods", "tests/log/"]) + test_transaction.transaction__add_transaction(["tests/environments/", "test_all_methods", "tests/log/"]) + test_transaction.transaction__remove_transaction(["tests/environments/", "test_all_methods", "tests/log/"]) + test_transaction.transaction__print_transaction(["tests/environments/", "test_all_methods", "tests/log/"]) + test_transaction.transaction__str(["tests/environments/", "test_all_methods", "tests/log/"]) + test_transaction.transaction__write_transaction(["tests/environments/", "test_all_methods", "tests/log/"]) + test_transaction.transaction__clear_accounts(["tests/environments/", "test_all_methods", "tests/log/"]) + test_transaction.transaction__purge_accounts(["tests/environments/", "test_all_methods", "tests/log/"]) + + # Tests for Helper + test_helper.helper__initialize_standard_bank(["tests/environments/", "test_all_methods", "tests/log/"]) + test_helper.helper__initialize_standard_firm(["tests/environments/", "test_all_methods", "tests/log/"]) + test_helper.helper__initialize_standard_household(["tests/environments/", "test_all_methods", "tests/log/"]) + test_helper.helper__cobb_douglas(["tests/environments/", "test_all_methods", "tests/log/"]) + test_helper.helper__leontief(["tests/environments/", "test_all_methods", "tests/log/"]) + test_helper.helper__ces(["tests/environments/", "test_all_methods", "tests/log/"]) + test_helper.helper__translog(["tests/environments/", "test_all_methods", "tests/log/"]) + + # Tests for Market + test_market.market__get_identifier(["tests/environments/", "test_all_methods", "tests/log/"]) + test_market.market__set_identifier(["tests/environments/", "test_all_methods", "tests/log/"]) + test_market.market__get_tolerance(["tests/environments/", "test_all_methods", "tests/log/"]) + test_market.market__set_tolerance(["tests/environments/", "test_all_methods", "tests/log/"]) + test_market.market__get_resolution(["tests/environments/", "test_all_methods", "tests/log/"]) + test_market.market__set_resolution(["tests/environments/", "test_all_methods", "tests/log/"]) + test_market.market__get_amplification(["tests/environments/", "test_all_methods", "tests/log/"]) + test_market.market__set_amplification(["tests/environments/", "test_all_methods", "tests/log/"]) + test_market.market__tatonnement(["tests/environments/", "test_all_methods", "tests/log/"]) + test_market.market__rationing(["tests/environments/", "test_all_methods", "tests/log/"]) + test_market.market__rationing_proportional(["tests/environments/", "test_all_methods", "tests/log/"]) + test_market.market__rationing_abstract(["tests/environments/", "test_all_methods", "tests/log/"]) + + # Tests for Measurement + test_measurement.measurement__get_identifier(["tests/environments/", "test_all_methods", "tests/log/"]) + test_measurement.measurement__set_identifier(["tests/environments/", "test_all_methods", "tests/log/"]) + test_measurement.measurement__get_config(["tests/environments/", "test_all_methods", "tests/log/"]) + test_measurement.measurement__set_config(["tests/environments/", "test_all_methods", "tests/log/"]) + test_measurement.measurement__get_environment(["tests/environments/", "test_all_methods", "tests/log/"]) + test_measurement.measurement__set_environment(["tests/environments/", "test_all_methods", "tests/log/"]) + test_measurement.measurement__get_runner(["tests/environments/", "test_all_methods", "tests/log/"]) + test_measurement.measurement__set_runner(["tests/environments/", "test_all_methods", "tests/log/"]) + test_measurement.measurement__get_filename(["tests/environments/", "test_all_methods", "tests/log/"]) + test_measurement.measurement__set_filename(["tests/environments/", "test_all_methods", "tests/log/"]) + test_measurement.measurement__get_file(["tests/environments/", "test_all_methods", "tests/log/"]) + test_measurement.measurement__set_file(["tests/environments/", "test_all_methods", "tests/log/"]) + test_measurement.measurement__get_csv_writer(["tests/environments/", "test_all_methods", "tests/log/"]) + test_measurement.measurement__set_csv_writer(["tests/environments/", "test_all_methods", "tests/log/"]) + test_measurement.measurement__init(["tests/environments/", "test_all_methods", "tests/log/"]) + test_measurement.measurement__open_file(["tests/environments/", "test_all_methods", "tests/log/"]) + test_measurement.measurement__write_to_file(["tests/environments/", "test_all_methods", "tests/log/"]) + test_measurement.measurement__close_file(["tests/environments/", "test_all_methods", "tests/log/"]) + test_measurement.measurement__read_xml_config_file(["tests/environments/", "test_all_methods", "tests/log/"]) + + # Tests for Runner << TINA TO WRITE + test_runner.runner__init__(["tests/environments/", "test_all_methods", "tests/log/"]) + test_runner.runner__get_identifier(["tests/environments/", "test_all_methods", "tests/log/"]) + test_runner.runner__set_identifier(["tests/environments/", "test_all_methods", "tests/log/"]) + test_runner.runner__get_num_sweeps(["tests/environments/", "test_all_methods", "tests/log/"]) + test_runner.runner__set_num_sweeps(["tests/environments/", "test_all_methods", "tests/log/"]) + test_runner.runner__do_run(["tests/environments/", "test_all_methods", "tests/log/"]) + + # Tests for Shock + test_shock.shock__do_shock(["tests/environments/", "test_all_methods", "tests/log/"]) + + # Tests for Updater + test_updater.updater__get_identifier(["tests/environments/", "test_all_methods", "tests/log/"]) + test_updater.updater__set_identifier(["tests/environments/", "test_all_methods", "tests/log/"]) + test_updater.updater__get_model_parameters(["tests/environments/", "test_all_methods", "tests/log/"]) + test_updater.updater__set_model_parameters(["tests/environments/", "test_all_methods", "tests/log/"]) + test_updater.updater__get_interactions(["tests/environments/", "test_all_methods", "tests/log/"]) + test_updater.updater__set_interactions(["tests/environments/", "test_all_methods", "tests/log/"]) + test_updater.updater__str(["tests/environments/", "test_all_methods", "tests/log/"]) + test_updater.updater__init(["tests/environments/", "test_all_methods", "tests/log/"]) + test_updater.updater__accrue_interests(["tests/environments/", "test_all_methods", "tests/log/"]) + test_updater.updater__endow_labour(["tests/environments/", "test_all_methods", "tests/log/"]) + test_updater.updater__sell_labour(["tests/environments/", "test_all_methods", "tests/log/"]) + test_updater.updater__consume_rationed(["tests/environments/", "test_all_methods", "tests/log/"]) + test_updater.updater__net_loans_deposits(["tests/environments/", "test_all_methods", "tests/log/"]) + test_updater.updater__remove_perishable(["tests/environments/", "test_all_methods", "tests/log/"]) + test_updater.updater__capitalise(["tests/environments/", "test_all_methods", "tests/log/"]) + test_updater.updater__do_update(["tests/environments/", "test_all_methods", "tests/log/"]) diff --git a/br-generate_banks.py b/br-generate_banks.py deleted file mode 100644 index 88d94fa..0000000 --- a/br-generate_banks.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python -# [SublimeLinter pep8-max-line-length:300] -# -*- coding: utf-8 -*- - -""" -black_rhino is a multi-agent simulator for financial network analysis -Copyright (C) 2012 Co-Pierre Georg (co-pierre.georg@keble.ox.ac.uk) - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, version 3 of the License. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -""" - - -# ------------------------------------------------------------------------- -# MAIN -# ------------------------------------------------------------------------- -if __name__ == '__main__': - import sys - sys.path.append('src/') - - if (len(sys.argv) != 3): - print "Usage: ./generate_banks.py numBanks directory" - sys.exit() - - numBanks = int(sys.argv[1]) - - for i in range(numBanks): - fileName = sys.argv[2] + "bank-" - # the following code ensures leading zeros so filenames will be in the right order - # for python to read in. Also, bank names are sorted properly in activeBanks of madfimas - # this code is ugly, but works... - if (numBanks <= 9): - fileName += "%01d" % (i,) - if (numBanks > 9): - fileName += "%02d" % (i,) - if (numBanks > 99): - fileName += "%03d" % (i,) - if (numBanks > 999): - fileName += "%04d" % (i,) - fileName += ".xml" - outFile = open(fileName, 'w') - - text = "\n" - text = text + " \n" - text = text + " \n" - text = text + " \n" - text = text + " \n" - text = text + " \n" - text = text + " \n" - text = text + " \n" - text = text + "\n" - outFile.write(text) - outFile.close() diff --git a/br-generate_networks.py b/br-generate_networks.py deleted file mode 100644 index a003c30..0000000 --- a/br-generate_networks.py +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env python -# [SublimeLinter pep8-max-line-length:300] -# -*- coding: utf-8 -*- - -""" -black_rhino is a multi-agent simulator for financial network analysis -Copyright (C) 2012 Co-Pierre Georg (co-pierre.georg@keble.ox.ac.uk) - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, version 3 of the License. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -""" - -# ------------------------------------------------------------------------- -# MAIN -# ------------------------------------------------------------------------- -if __name__ == '__main__': - import sys - sys.path.append('src/') - import networkx as nx - - if (len(sys.argv) != 5): - print "Usage: ./generate_networks.py numNodes [random/ba] networkParameter1 networkParameter2" - sys.exit() - - # check which network type was given - if (sys.argv[2] == "random"): - # generate a random network - G = nx.DiGraph() - G = nx.gnp_random_graph(int(sys.argv[1]), float(sys.argv[3]), directed=True) - fileName = "network-" + str(sys.argv[2]) + "-" + str(sys.argv[1]) + "-" + str(sys.argv[3]) - nx.write_gexf(G, fileName + ".gexf") - if (sys.argv[2] == "ba"): - # generate a random network - G = nx.DiGraph() - G = nx.barabasi_albert_graph(int(sys.argv[1]), int(sys.argv[3])) - fileName = "network-" + str(sys.argv[2]) + "-" + str(sys.argv[1]) + "-" + str(sys.argv[3]) - nx.write_gexf(G, fileName + ".gexf") diff --git a/environments/test_all_methods.xml b/environments/test_all_methods.xml new file mode 100644 index 0000000..d9d129d --- /dev/null +++ b/environments/test_all_methods.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/examples/noclearing/abm_template/README b/examples/noclearing/abm_template/README new file mode 100644 index 0000000..403d18d --- /dev/null +++ b/examples/noclearing/abm_template/README @@ -0,0 +1,14 @@ +This is a template for writing financial agent-based models in python. It uses abstract base classes and is implemented in sample_model.py. +The implementation is very basic and shows to implement the abstract base classes, the implementation is of a simple game with parameter +theta, two agents, two choices of action (1,0) and a payout matrix: + 1 0 +1 [ (1-theta,1-theta) (-theta,0) ] +0 [ (0,-theta) (0,0) ] + +There are simple equilibria: +Theta is known to both agents, there is common knowledge about theta. +If theta < 0, both agents find attack the dominant action: unique Nash equilibrium: {1, 1} +If theta > 1, both agents find refrain the dominant action: unique Nash equilibrium: {0, 0} +If theta is between [0, 1] both equilibria can be sustained as pure strategy Nash equilibria. + +co-pierre.georg@uct.ac.za, 2015 diff --git a/examples/noclearing/abm_template/calc_goodness.py b/examples/noclearing/abm_template/calc_goodness.py new file mode 100644 index 0000000..5d8c828 --- /dev/null +++ b/examples/noclearing/abm_template/calc_goodness.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python2.7 +# -*- coding: utf-8 -*- + +""" +Robustness check for Agent-Based Models +(conceivably other models as well) +across the whole of the multidimensional +parameter space. + +Author: Pawel Fiedor (pawel@fiedor.eu) + Co-Pierre Georg (cogeorg@gmail.com) + +Version: 0.2 + +Date of last update: 19-11-2015 (Cape Town) + +""" +if __name__ == '__main__': + import sys + from src.goodness import Goodness + + args = sys.argv + + if len(args) != 2: + print "Usage: ./calc_goodness config_file_path.xml" + sys.exit() + + goodness = Goodness() + goodness.do_run(args[1]) diff --git a/examples/noclearing/abm_template/configs/goodness_config_sample.xml b/examples/noclearing/abm_template/configs/goodness_config_sample.xml new file mode 100644 index 0000000..5fcc6d0 --- /dev/null +++ b/examples/noclearing/abm_template/configs/goodness_config_sample.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/examples/noclearing/abm_template/configs/sample_config_file.xml b/examples/noclearing/abm_template/configs/sample_config_file.xml new file mode 100644 index 0000000..f351857 --- /dev/null +++ b/examples/noclearing/abm_template/configs/sample_config_file.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/examples/noclearing/abm_template/configs/sample_runner_config.xml b/examples/noclearing/abm_template/configs/sample_runner_config.xml new file mode 100644 index 0000000..ca469a2 --- /dev/null +++ b/examples/noclearing/abm_template/configs/sample_runner_config.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/examples/noclearing/abm_template/output_test_1.csv b/examples/noclearing/abm_template/output_test_1.csv new file mode 100644 index 0000000..c74a5a6 --- /dev/null +++ b/examples/noclearing/abm_template/output_test_1.csv @@ -0,0 +1,5 @@ +1;1; +1;1; +1;1; +1;1; +1;1; diff --git a/src/bank.py b/src/bank.py index 7f0f49d..97a0343 100644 --- a/src/bank.py +++ b/src/bank.py @@ -1,10 +1,11 @@ #!/usr/bin/env python -# [SublimeLinter pep8-max-line-length:300] +# [SublimeLinter pep8-max-line-length:150] # -*- coding: utf-8 -*- """ black_rhino is a multi-agent simulator for financial network analysis -Copyright (C) 2012 Co-Pierre Georg (co-pierre.georg@keble.ox.ac.uk) +Copyright (C) 2016 Co-Pierre Georg (co-pierre.georg@keble.ox.ac.uk) +Pawel Fiedor (pawel@fiedor.eu) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,7 +21,6 @@ """ import logging -import math from abm_template.src.baseagent import BaseAgent # ============================================================================ @@ -36,69 +36,48 @@ class Bank(BaseAgent): # VARIABLES # # - identifier = "" - parameters = {} - state_variables = {} - accounts = [] # all accounts of a bank - - parameters["V"] = 0.0 # planned optimal portfolio volume of the bank - parameters["lamb"] = 0.0 # planned optimal portfolio structure of the bank - parameters["rb"] = 0.0 # refinancing costs of the bank -> tbd in environment - parameters["rd"] = 0.0 # interest rate on deposits - parameters["r"] = 0.0 # required reserve rate -> tbd in environment - parameters["pReal"] = 0.0 # probability of credit success - parameters["rhoReal"] = 0.0 # interest charged on risky investment - parameters["pFinancial"] = 0.0 # probability of interbank loan success - parameters["rhoFinancial"] = 0.0 # variance of interbank loan success - parameters["xi"] = 0.0 # scaling parameter in CRRA - parameters["theta"] = 0.0 # risk aversion parameter of bank - parameters["gamma"] = 0.0 # fraction of interbank loans in overall volume - parameters["assetNumber"] = 0 # number of assets available to bank - parameters["numBanks"] = 0 # number of banks in the economy - parameters["Q"] = 0.0 # the current liquidity position of the bank - parameters["Ip"] = 0.0 # the planned optimal investment - parameters["Ep"] = 0.0 # the planned excess reserves - parameters["Lp"] = 0.0 # the planned interbank loans; L > 0: excess supply of interbank liquidity - # keep track whether a bank is active or not. This variable is necessary since it is not good - # to remove inactive banks from banks[] while looping through them... - parameters["active"] = 0 - # filled during initialize_transactions() and equal to the size of an initial transaction - # keeping track will ensure that later transactions are not larger than average - parameters["averageTransactionSize"] = 0.0 + identifier = "" # identifier of the specific bank + parameters = {} # parameters of the specific bank + state_variables = {} # state variables of the specific bank + accounts = [] # all accounts of a bank (filled with transactions) -# -# -# CODE -# -# + # + # + # CODE + # + # + # ------------------------------------------------------------------------- + # functions for setting/changing id, parameters, and state variables + # these either return or set specific value to the above variables + # with the exception of append (2 last ones) which append the dictionaries + # which contain parameters or state variables + # ------------------------------------------------------------------------- def get_identifier(self): return self.identifier - def set_identifier(self, _value): - """ - Class variables: identifier - Local variables: _identifier - """ - super(Bank, self).set_identifier(_value) + + def set_identifier(self, value): + super(Bank, self).set_identifier(value) def get_parameters(self): return self.parameters - def set_parameters(self, _value): - """ - Class variables: parameters - Local variables: _params - """ - super(Bank, self).set_parameters(_value) + + def set_parameters(self, value): + super(Bank, self).set_parameters(value) def get_state_variables(self): return self.state_variables - def set_state_variables(self, _value): - """ - Class variables: state_variables - Local variables: _variables - """ - super(Bank, self).set_state_variables(_value) + + def set_state_variables(self, value): + super(Bank, self).set_state_variables(value) + + def append_parameters(self, value): + super(Bank, self).append_parameters(value) + + def append_state_variables(self, value): + super(Bank, self).append_state_variables(value) + # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- # functions needed to make Bank() hashable @@ -117,8 +96,19 @@ def __hash__(self): # __init__ # ------------------------------------------------------------------------- def __init__(self): - self.accounts = [] # clear transactions when bank is initialized - self.parameters["Q"] = 0.0 # bank liquidity is reset + self.identifier = "" # identifier of the specific bank + self.parameters = {} # parameters of the specific bank + self.state_variables = {} # state variables of the specific bank + self.accounts = [] # all accounts of a bank (filled with transactions) + # DO NOT EVER ASSIGN PARAMETERS BY HAND AS DONE BELOW IN PRODUCTION CODE + # ALWAYS READ THE PARAMETERS FROM CONFIG FILES + # OR USE THE FUNCTIONS FOR SETTING / CHANGING VARIABLES + # CONVERSELY, IF YOU WANT TO READ THE VALUE, DON'T USE THE FULL NAMES + # INSTEAD USE __getattr__ POWER TO CHANGE THE COMMAND FROM + # instance.static_parameters["xyz"] TO instance.xyz - THE LATTER IS PREFERRED + self.parameters["interest_rate_loans"] = 0.0 # interest rate on loans + self.parameters["interest_rate_deposits"] = 0.0 # interest rate on deposits + self.parameters["active"] = 0 # this is a control parameter checking whether bank is active # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- @@ -130,713 +120,113 @@ def __del__(self): # ------------------------------------------------------------------------- # __str__ + # returns a string describing the bank and its properties + # based on the implementation in the abstract class BaseAgent + # but adds the type of agent (bank) and lists all transactions # ------------------------------------------------------------------------- def __str__(self): - text = "\n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - for transaction in self.accounts: - text += transaction.write_transaction() - text += " \n" - text += " \n" - text += " \n" - text += "\n" - - return text + bank_string = super(Bank, self).__str__() + bank_string = bank_string.replace("\n", "\n \n", 1) + text = "\n" + text = text + " " + return bank_string.replace("\n ", text, 1) # ------------------------------------------------------------------------ # ------------------------------------------------------------------------- # get_parameters_from_file - # ------------------------------------------------------------------------- - def get_parameters_from_file(self, bankFilename, environment, numBanks, time): - from xml.etree import ElementTree - - try: - xmlText = open(bankFilename).read() - element = ElementTree.XML(xmlText) - self.identifier = element.attrib['identifier'] - - self.parameters["rb"] = environment.static_parameters["rb"] - self.parameters["r"] = environment.static_parameters["r"] - self.parameters["rd"] = environment.static_parameters["rd"] - self.parameters["assetNumber"] = environment.static_parameters["assetNumber"] - self.parameters["numBanks"] = numBanks - - # loop over all entries in the xml file - for subelement in element: - if (subelement.attrib['type'] == 'changing'): - name = subelement.attrib['name'] - value = subelement.attrib['value'] - validFrom = subelement.attrib['validity'].rsplit("-")[0] - validTo = subelement.attrib['validity'].rsplit("-")[1] - if (int(time) >= int(validFrom)) and (int(time) <= int(validTo)): # we have a valid parameterset - if (name == 'xiBank'): - self.parameters["xi"] = float(value) - if (name == 'thetaBank'): - self.parameters["theta"] = float(value) - if (name == 'pReal'): # success probability of real assets TODO change stupid name - self.parameters["pReal"] = float(value) - if (name == 'rhoReal'): # return of real assets TODO change stupid name - self.parameters["rhoReal"] = float(value) - if (name == 'pFinancial'): # success probability of financial assets - self.parameters["pFinancial"] = float(value) - if (name == 'rhoFinancial'): # return of financial assets - self.parameters["rhoFinancial"] = float(value) - if (name == 'gammaBank'): - self.parameters["gamma"] = float(value) - - # and finally, calculate optimal investment - self.calculate_optimal_investment_volume(environment) - self.initialize_transactions(environment) - except: - logging.error(" ERROR: %s could not be parsed", bankFilename) - # ------------------------------------------------------------------------ - - # ------------------------------------------------------------------------ - # apply_sifi_surcharge - # ------------------------------------------------------------------------ - def apply_sifi_surcharge(self, sifiSurchargeFactor): - old_D = self.get_account("D") - old_BC = self.get_account("BC") - new_BC = sifiSurchargeFactor*old_BC - new_D = old_D - (new_BC - old_BC) - - # str(sifiSurchargeFactor) + " || " + str(old_D) + " -> " + str(new_D) + " | " + str(old_BC) + " -> " + str(new_BC) - - for transaction in self.accounts: - if transaction.transactionType == "D": # reduce central bank liabilities - transaction.transactionValue = new_D - if transaction.transactionType == "BC": # and increase banking capital - transaction.transactionValue = new_BC + # reads the specified config file given the environment + # and sets parameters to the ones found in the config file + # the config file should be an xml file that looks like the below: + # + # + # + # ------------------------------------------------------------------------- + def get_parameters_from_file(self, bank_filename, environment): + super(Bank, self).get_parameters_from_file(bank_filename, environment) # ------------------------------------------------------------------------ -# -# ROUTINES CALLED IN UPDATER -# - - # ------------------------------------------------------------------------- - # update_maturity - # ------------------------------------------------------------------------- - def update_maturity(self): - for transaction in self.accounts: - if (int(transaction.transactionMaturity) > 0): # reduce maturity if duration longer than 0 - transaction.transactionMaturity = int(transaction.transactionMaturity) - 1 - if ((transaction.transactionType == "I") and (int(transaction.transactionTimeOfDefault) > 0)): # only investments have a time of default - transaction.transactionTimeOfDefault = int(transaction.transactionTimeOfDefault) - 1 - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # update_risk_aversion - # this method checks if there was a default in the previous period - # ------------------------------------------------------------------------- - def update_risk_aversion(self, environment, time): - lastInsolvency = [0, -2] # dummy if we did not have any insolvency yet - # -2 since we check time-1 later and in t=0 would run into problems - - # decrease risk aversion every time there was no default in the previous period - # increase risk aversion if there was a default - # find the number of total insolvencies - numInsolvencies = 0 - for entry in environment.static_parameters["insolvencyHistory"]: - numInsolvencies += entry[0] - - if numInsolvencies > 0: - lastInsolvency = environment.static_parameters["insolvencyHistory"].pop() - - # check if we had an insolvency in the last or current period - if lastInsolvency[1] == (time - 1) or lastInsolvency[1] == time: - # if so, increase risk aversion - self.parameters["theta"] = round(self.parameters["theta"]*(1.0 + environment.static_parameters["riskAversionAmplificationFactor"]), 3) # we round to be on the safe side with float numbers - else: # if not, decrease risk aversion - self.parameters["theta"] = round(self.parameters["theta"]*(1.0 - environment.static_parameters["riskAversionDiscountFactor"]), 3) # we round to be on the safe side with float numbers - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # get_interest - # ------------------------------------------------------------------------- - def get_interest(self, type): - from random import Random - - random = Random() - volume = 0.0 - sign = 1.0 # will be negative if interest has to be paid by the bank - - for transaction in self.accounts: - if (transaction.transactionType == type): - if (transaction.transactionType == "I"): # check if we have an investment - if (transaction.transactionTimeOfDefault == 0): # check if the investment defaulted - transaction.transactionInterest = 0.0 # if the loan defaulted, it will pay no interest - # - # now we check what happens if a loan defaults - # - self.reduce_banking_capital(transaction.transactionValue) # reduce the banking capital if the loan defaults - # - # - # - if (transaction.transactionTo == self.identifier): - volume = volume - float(transaction.transactionValue)*float(transaction.transactionInterest) - else: - volume = volume + float(transaction.transactionValue)*float(transaction.transactionInterest) - - return volume - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # liquidate_due_transactions - # ------------------------------------------------------------------------- - def liquidate_due_transactions(self, type): - volume = 0.0 - residual = [] - - for transaction in self.accounts: - if ((transaction.transactionType == type) and (int(transaction.transactionMaturity) == 0)): - volume = volume + float(transaction.transactionValue) - else: # if transaction is not matching, copy it to the residual list... - residual.append(transaction) - self.accounts = residual # ...and then make the residual list the accounts - - return round(volume, 4) # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # def get_new_deposits(scaleFactor) - # This method returns the NET flow of deposits, not the absolute value - # ------------------------------------------------------------------------- - def get_new_deposits(self, scaleFactor): - from random import Random - random = Random() - oldValue = 0.0 - newValue = 0.0 - returnValue = 0.0 - - for transaction in self.accounts: - if (transaction.transactionType == "D"): - oldValue = transaction.transactionValue # the old value - newValue = max(round((1.0 - scaleFactor + 2.0*scaleFactor*random.random())*oldValue, 4), 0.0) # make sure we have only positive deposit levels - transaction.transactionValue = newValue - - returnValue = round(newValue - oldValue, 4) # convention is that outflows are negative, this is the bank's viewpoint - - return returnValue - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # transfer_required_deposits - # ------------------------------------------------------------------------- - def transfer_required_deposits(self): - from src.transaction import Transaction - - transaction = Transaction() - value = round(float(self.parameters["r"]*self.get_account("D")), 4) - transaction.this_transaction("rD", self.identifier, -3, value, self.parameters["rb"], 0, -1) - self.accounts.append(transaction) - - return -1.0*value - # ------------------------------------------------------------------------- - + # check_consistency + # checks whether the assets and liabilities have the same total value + # the types of transactions that make up assets and liabilities is + # controlled by the lists below # ------------------------------------------------------------------------- - # reduce_banking_capital - # ------------------------------------------------------------------------- - def reduce_banking_capital(self, value): - for transaction in self.accounts: - if (transaction.transactionType == "BC"): - transaction.transactionValue = max(0.0, transaction.transactionValue - value) - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # check_solvency - # ------------------------------------------------------------------------- - def check_solvency(self, environment, debug, time): - # variables needed to check solvency - required_capital_ratio = environment.static_parameters["requiredCapitalRatio"] - BC = self.get_account("BC") - I = self.get_account("I") - - # here, the assumption is that banking capital has to account for 8% - # of the risk weighted assets and that all risky assets have a risk-weight - # of 1.0 - if ((self.parameters["active"] > -1) and (I > 0.0)): - if (round(float(BC/I), 4) < required_capital_ratio): - self.parameters["active"] = -1 - # store in state.numberInsolvencies when we had an insolvency - environment.addInsolvencyToHistory(time) - if (debug == "info" or debug == "debug"): - try: - capital_adequacy = BC/I - except: - capital_adequacy = 0.0 - logging.info(" time: %s: ", time, self.identifier, capital_adequacy, required_capital_ratio) - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # check_liquidity - # ------------------------------------------------------------------------- - def check_liquidity(self): - # since illiquidity is different from insolvency, we need an additional - # liquidity check. if a bank is short on liquitiy after going to the interbank - # market and the central bank, it has to sell off some of its assets in a possible fire-sale - if (self.parameters["active"] > -1): - if (self.parameters["Q"] < 0.0): - self.parameters["active"] = -1 - # string = "Bank " + self.identifier + " is illiquid" + str(self.Q) - # print helper.highlight(string, "red") - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # calculate_liquidity_demand - # - # This function calculates the liquidity demand that has to be satisfied on either - # the interbank market or by the central bank - # Note: for Lp > 0 the bank has excess liquidity supply - # ------------------------------------------------------------------------- - def calculate_liquidity_demand(self): - # first, calculate the optimal investments - self.parameters["Ip"] = round(float(self.parameters["gamma"]*(self.parameters["lamb"])*self.parameters["V"]), 4) - self.parameters["Ep"] = round(float(self.parameters["gamma"]*(1.0-self.parameters["lamb"])*self.parameters["V"]), 4) - # print str(self.Ip) + " " + str(self.Ep) + " " + str(self.get_account("I")) + " " + str(self.get_account("E")) - # the liquidity demand is given by Q - ((Ip-I) + (Ep-E)) - self.parameters["Lp"] = self.parameters["Q"] - ((self.parameters["Ip"] - self.get_account("I")) + (self.parameters["Ep"] - self.get_account("E"))) - # print str(self.identifier) + " " + str(self.Lp) - self.parameters["Q"] = 0.0 # reduce Q, since all liquidity demand is now in the planned interbank liquidity - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # get_centralliquidity(self, state) - # ------------------------------------------------------------------------- - def get_central_bank_liquidity(self, environment): - interest = environment.static_parameters["rb"] - maturity = 0 - timeOfDefault = -1 - - # banks can obtain liquidity from the central bank if they have adequate collateral - if (self.parameters["Lp"] < 0.0): - # check how much central bank liquidity is available - maxValue = environment.static_parameters["collateralQuality"] * self.get_account("I") - # calculate the actual transferred value - value = min(maxValue, abs(self.parameters["Lp"])) - # update the accounts to keep track of the loan - self.add_transaction("LC", -3, self.identifier, value, interest, maturity, timeOfDefault) - # and update the liquidity demand - self.parameters["Lp"] = self.parameters["Lp"] + value - # ------------------------------------------------------------------------ - - # ------------------------------------------------------------------------- - # liquidate_assets() - # ------------------------------------------------------------------------- - def liquidate_assets(self, initial_assets, current_assets, environment, debug, time): - # variables needed to calculate discounts - liquidation_discount_factor = environment.static_parameters["liquidationDiscountFactor"] - required_capital_ratio = environment.static_parameters["requiredCapitalRatio"] - liquidated_assets = 0.0 # the amount of assets already liquidated by the bank in the current period - - if (self.parameters["Lp"] < 0.0): # we have a shortfall in liquidity - current_I = self.get_account("I") # store the current amount of investments - current_E = self.get_account("E") # store the current amount of excess reserves - - # when banks do not have liquidity, they will cut back their planned investment - if (self.parameters["Ip"] > current_I): - self.parameters["Ip"] -= current_I - self.parameters["Lp"] += (self.parameters["Ip"] - current_I) - if (self.parameters["Ep"] > current_E): - self.parameters["Ep"] -= current_E - self.parameters["Lp"] += (self.parameters["Ep"] - current_E) - - # if cutting back planned investment is not enough, the bank will have to sell assets - if (self.parameters["Lp"] < 0.0): - # the price for assets is determined by the total share of liquidated assets, including the planned liquidation volume - liquidation_volume = min(-1.0*self.parameters["Lp"], current_I) - # the asset price will be low for high values of the liquidation_discount_factor and large amounts of liquidated assets - liquidation_price = round(float(math.exp(-liquidation_discount_factor*((initial_assets - current_assets + liquidation_volume)/(initial_assets)))), 4) - # now loop over all investments and liquidate as many as necessary - for transaction in self.accounts: - if (transaction.transactionType == "I") and (liquidated_assets < liquidation_volume) and (transaction.transactionValue > 0.0): - liquidated_assets += transaction.transactionValue - self.parameters["Lp"] += liquidation_price*transaction.transactionValue # we get liquidity for our assets - self.reduce_banking_capital((1.0 - liquidation_price) * transaction.transactionValue) # but suffer a loss from costly liquidation - transaction.transactionValue = 0.0 - # check if we are still alive - self.check_solvency(environment, debug, time) # note that this has to be done before purge_accounts (afterwards, I=0) - - # once we are done, purge the assets that have been sold from the accounts - self.purge_accounts() - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # transfer_investments - # ------------------------------------------------------------------------- - def transfer_investments(self, environment): - from random import Random - from src.transaction import Transaction - - random = Random() - - currentVolume = 0.0 - optimalVolume = 0.0 - plannedVolume = 0.0 - availableVolume = 0.0 - transactionVolume = 0.0 - transaction = Transaction() - - # calculate the optimal investment volume and compare to current volume - self.calculate_optimal_investment_volume(environment) - optimalVolume = round(float(self.parameters["gamma"]*self.parameters["lamb"]*self.parameters["V"]), 4) - currentVolume = round(self.get_account("I"), 4) - # add new transactions of average size - plannedVolume = currentVolume + optimalVolume - availableVolume = self.parameters["lamb"]*self.parameters["Q"] # we can only spend a fraction of the available Q - transactionVolume = min(plannedVolume, availableVolume) - - while ((transactionVolume >= self.parameters["averageTransactionSize"]) and (self.parameters["averageTransactionSize"] > 0.0)): - - transactionVolume = round(transactionVolume - self.parameters["averageTransactionSize"], 5) # reduce remaining transactionVolume - self.parameters["Q"] = self.parameters["Q"] - self.parameters["averageTransactionSize"] # reduce available liquidity - - # account for different maturities of investments - maturity = int(round(random.random()*environment.static_parameters["firmLoanMaturity"], 1)) # this is done very roughly and implies loans are up to 3 years - - # and determine whether the loan will default - if (random.random() >= environment.static_parameters["successProbabilityFirms"]): - # the loan defaults: determine timeOfDefault - timeOfDefault = int(round(random.random()*maturity)) - else: - timeOfDefault = -1 - - # and add transaction to the stack - transaction = Transaction() - transaction.this_transaction("I", self.identifier, -2, self.parameters["averageTransactionSize"], self.parameters["rhoReal"], maturity, timeOfDefault) - self.accounts.append(transaction) - del transaction - - transactionVolume = round(transactionVolume, 5) - # finally, add the remaining transaction to the stack if the transactionVolume was positive in the first place - if (transactionVolume > 0.0): - self.parameters["Q"] = round(self.parameters["Q"] - transactionVolume, 4) - - # account for different maturities of investments - maturity = int(round(random.random()*environment.static_parameters["firmLoanMaturity"], 1)) # this is done very roughly and implies loans are up to 3 years - - # and determine whether the loan will default - if (random.random() >= environment.static_parameters["successProbabilityFirms"]): - # the loan defaults: determine timeOfDefault - timeOfDefault = int(round(random.random()*maturity)) - else: - timeOfDefault = -1 - - transaction = Transaction() - transaction.this_transaction("I", self.identifier, -2, transactionVolume, self.parameters["rhoReal"], maturity, timeOfDefault) - self.accounts.append(transaction) - del transaction - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # transfer_excess_reserves - # ------------------------------------------------------------------------- - def transfer_excess_reserves(self): - from src.transaction import Transaction - availableVolume = self.parameters["Q"] - plannedVolume = self.parameters["gamma"]*(1.0-self.parameters["lamb"])*self.parameters["V"] - transactionVolume = round(min(plannedVolume, availableVolume), 4) - self.parameters["Q"] = round(self.parameters["Q"] - transactionVolume, 4) - if (self.parameters["Q"] < 0.0): - logging.info("ERROR: Q negative in transfer_excess_reserves") - transaction = Transaction() - transaction.this_transaction("E", self.identifier, -3, transactionVolume, self.parameters["rb"], 0, -1) - self.accounts.append(transaction) - del transaction - # ------------------------------------------------------------------------- - -# -# HELPER ROUTINES -# - - # ------------------------------------------------------------------------- - # calculate_optimal_investment_volume - # ------------------------------------------------------------------------- - def calculate_optimal_investment_volume(self, environment): # TODO this is not a good name, better would be calculate_optimal_portfolio - import math - - # TODO this is where we could update the p and rho for investments in the real economy - # TODO in a more elaborate model, financial assets can be treated as another set of risky assets - # learning can be done by checking the *actual* number of defaulted loans in the economy - # we can also include memory effects when agents "forget" past defaults - # for the interbank assets, life is even more interesting when agents now about recent defaults - mu = self.parameters["pReal"]*self.parameters["rhoReal"] - (1.0 - self.parameters["pReal"]) - sigma2 = self.parameters["pReal"]*(self.parameters["rhoReal"] - mu)*(self.parameters["rhoReal"] - mu) + (1-self.parameters["pReal"])*((-1-mu)*(-1-mu)) - - if (sigma2 > 0.0): # this test ensures there are no floating errors from division by zero - # TODO here we have to impose regulatory measures on the portfolio structure in a VaR sense - self.parameters["lamb"] = max(0.0, min((mu/(self.parameters["theta"]*sigma2)), 1.0)) # lamb is the fraction of risky assets in the portfolio - self.parameters["V"] = math.pow(self.parameters["xi"]*(1.0/self.parameters["rb"])*pow((1.0+self.parameters["lamb"]*mu-0.5*self.parameters["lamb"]*self.parameters["lamb"]*sigma2), (1.0-self.parameters["theta"])), (1.0/self.parameters["theta"])) - # print [mu, sigma2, self.theta, mu/(self.theta*sigma2), self.lamb, self.V] - - # now apply the VaR on the portfolio volume - leverage = self.get_account("BC")/self.parameters["V"] - if (environment.static_parameters["leverageRatio"] > leverage) and (leverage > 0.0): - # logging.info(" leverageRatio binding") # TODO also log how much it is binding - self.parameters["V"] = (1.0/environment.static_parameters["leverageRatio"])*self.get_account("BC") - else: - print "WARNING: sigma2 <= 0.0; mu,sigma2=", mu, sigma2 - print self.parameters["pReal"], self.parameters["rhoReal"] - self.parameters["lamb"] = 0.0 - self.parameters["V"] = 0.0 - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # initialize_transactions - # ------------------------------------------------------------------------- - def initialize_transactions(self, environment): - from src.transaction import Transaction - from random import Random - random = Random() - - value = 0.0 - - # first, calculate number of transactions for investments - numTransactions = int(round(self.parameters["assetNumber"] / self.parameters["numBanks"])) - if (numTransactions == 0): # we want some error message if there are two few assets in the economy - logging.info(" ERROR: number of assets in the economy has to be at least half the number of banks") - # now, calculate value of each transaction and note that the *product* of all individual transactions - # is supposed to have precision 4. Hence, each individual transaction should have precision 5 - - value = round(float(self.parameters["gamma"]*self.parameters["lamb"]*self.parameters["V"] / numTransactions), 5) - # finally, put them on the transaction stack - for i in range(numTransactions): - transaction = Transaction() - # - # account for different maturities - # - maturity = int(round(random.random()*environment.static_parameters["firmLoanMaturity"], 1)) # maturity is between 0 and firmLoanMaturity - # and determine whether the loan will default - if (random.random() >= environment.static_parameters["successProbabilityFirms"]): # TODO this is superfluous, we could get rid of this doubling - # the loan defaults: determine timeOfDefault - timeOfDefault = int(round(random.random()*maturity)) - else: - timeOfDefault = -1 - # then, generate the transaction, append it to the accounts, and delete it from memory - transaction.this_transaction("I", self.identifier, -2, value, self.parameters["rhoReal"], maturity, timeOfDefault) - self.accounts.append(transaction) - del transaction - # store averageTransactionSize - self.parameters["averageTransactionSize"] = value - - # then, calculate excess reserves - value = round(float(self.parameters["gamma"]*(1.0-self.parameters["lamb"])*self.parameters["V"]), 4) - transaction = Transaction() - transaction.this_transaction("E", self.identifier, -3, value, self.parameters["rb"], 0, -1) - self.accounts.append(transaction) - del transaction - - # on the liabilities side, banks are endowed with banking capital - # (see comments in get_initial_banking_capital() for further details) - value = round(float(self.get_initial_banking_capital(environment.static_parameters["requiredCapitalRatio"])), 4) - transaction = Transaction() - transaction.this_transaction("BC", self.identifier, self.identifier, value, 0.0, 0, -1) - self.accounts.append(transaction) - del transaction - - # now, transfer deposits from households to banks - value = round(float(self.parameters["gamma"]*self.parameters["V"]-self.get_account("BC")), 4) - transaction = Transaction() - transaction.this_transaction("D", -1, self.identifier, value, self.parameters["rd"], 0, -1) - self.accounts.append(transaction) - del transaction - - # as well as required deposits to the central bank - value = round(float(self.parameters["r"]*self.get_account("D")), 4) - transaction = Transaction() - transaction.this_transaction("rD", self.identifier, -3, value, self.parameters["rb"], 0, -1) - self.accounts.append(transaction) - del transaction - - # finally, determine central bank loans - value = round(float(self.get_account("I") + self.get_account("E") + self.get_account("rD") - self.get_account("D") - self.get_account("BC")), 4) - transaction = Transaction() - transaction.this_transaction("LC", self.identifier, -3, value, self.parameters["rb"], 0, -1) - self.accounts.append(transaction) - del transaction - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # get_initial_banking_capital - # ------------------------------------------------------------------------- - def get_initial_banking_capital(self, required_capital_ratio): - value = 0.0 - # the assumption here is that banks' actual capital is larger than their regulatory - # capital. for a motivation see Elizalde and Repullo (2007), IJCB - # however, we do not care about the details (i.e. how much actual capital exceeds - # regulatory capital) and just assume a fixed value for all banks - value = 1.25*required_capital_ratio*self.get_account("I") - - return value + def check_consistency(self): + assets = ["loans", "cash"] + liabilities = ["deposits"] + return super(Bank, self).check_consistency(assets, liabilities) # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- # get_account + # returns the value of all transactions of a given type # ------------------------------------------------------------------------- - def get_account(self, type): - volume = 0.0 - - for transaction in self.accounts: - if (transaction.transactionType == type): - volume = volume + float(transaction.transactionValue) - - return volume + def get_account(self, type_): + return super(Bank, self).get_account(type_) # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- # get_account_num_transactions + # returns the number of transactions of a given type # ------------------------------------------------------------------------- - def get_account_num_transactions(self, type): # returns the number of transactions in a given account - num_transactions = 0.0 - - for transaction in self.accounts: - if (transaction.transactionType == type): - num_transactions += 1 - - return num_transactions + def get_account_num_transactions(self, type_): + return super(Bank, self).get_account_num_transactions(type_) # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- # add_transaction - # ------------------------------------------------------------------------- - def add_transaction(self, type, fromID, toID, value, interest, maturity, timeOfDefault): + # adds a transaction to the accounts + # the transaction is endowed with the following information: + # type_ - type of transactions, e.g. "deposit" + # assets - type of asset, used for investment types + # from_id - agent being the originator of the transaction + # to_id - agent being the recipient of the transaction + # amount - amount of the transaction + # interest - interest rate paid to the originator each time step + # maturity - time (in steps) to maturity + # time_of_default - control variable checking for defaulted transactions + # ------------------------------------------------------------------------- + def add_transaction(self, type_, asset, from_id, to_id, amount, interest, maturity, time_of_default, environment): from src.transaction import Transaction transaction = Transaction() - transaction.this_transaction(type, fromID, toID, value, interest, maturity, timeOfDefault) - self.accounts.append(transaction) - del transaction + transaction.this_transaction(type_, asset, from_id, to_id, amount, interest, maturity, time_of_default) + transaction.add_transaction(environment) # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- # clear_accounts + # removes all transactions from bank's accounts + # only for testing, the one in transaction should be used in production # ------------------------------------------------------------------------- def clear_accounts(self): - self.accounts = [] + super(Bank, self).clear_accounts() # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- # purge_accounts + # removes worthless transactions from bank's accounts # ------------------------------------------------------------------------- - def purge_accounts(self): - new_accounts = [] - - for transaction in self.accounts: - if transaction.transactionValue > 0.0: - new_accounts.append(transaction) - - self.accounts = new_accounts + def purge_accounts(self, environment): + super(Bank, self).purge_accounts(environment) # ------------------------------------------------------------------------- -# -# ROUTINES THAT MAKE DEBUGGING EASIER -# # ------------------------------------------------------------------------- - # change_deposits(self, change) + # get_transactions_from_file + # reads transactions from the config file to the bank's accounts # ------------------------------------------------------------------------- - def change_deposits(self, change): - old_value = 0.0 - new_value = 0.0 - - for transaction in self.accounts: - if (transaction.transactionType == "D"): - old_value = transaction.transactionValue # the old value - new_value = old_value + change - transaction.transactionValue = new_value - - return (old_value - new_value) + def get_transactions_from_file(self, filename, environment): + super(Bank, self).get_transactions_from_file(filename, environment) # ------------------------------------------------------------------------- + # __getattr__ + # if the attribute isn't found by Python we tell Python + # to look for it first in parameters and then in state variables + # which allows for directly fetching parameters from the Bank + # i.e. bank.active instead of a bit more bulky + # bank.parameters["active"] + # makes sure we don't have it in both containers, which + # would be bad practice [provides additional checks] # ------------------------------------------------------------------------- - # initialize_standard_bank - # - # this routine initializes a bank with a standard balance sheet, - # which can be used to make the tests more handy - # ------------------------------------------------------------------------- - def initialize_standard_bank(self): - from src.transaction import Transaction - - self.parameters["identifier"] = "0" # identifier - self.parameters["V"] = 250.0 # planned optimal portfolio volume of the bank - self.parameters["lamb"] = 0.5 # planned optimal portfolio structure of the bank - self.parameters["rb"] = 0.02 # refinancing costs of the bank -> tbd in environment - self.parameters["rd"] = 0.01 # interest rate on deposits - self.parameters["r"] = 0.05 # required reserve rate -> tbd in environment - self.parameters["pReal"] = 0.9 # probability of credit success - self.parameters["rhoReal"] = 0.03 # interest charged on risky investment - self.parameters["xi"] = 1.0 # scaling parameter in CRRA - self.parameters["theta"] = 1.5 # risk aversion parameter of bank - self.parameters["gamma"] = 0.8 # fraction of interbank loans in overall volume - - self.parameters["Q"] = 0.0 # the current liquidity position of the bank - # set 0.0 as standard, but after calculate_liquidity_demand we will return the indicated values - self.parameters["Ip"] = 0.0 # the planned optimal investment; (gamma * lamb * V) = (0.8 * 0.5 * 250.0) = 100.0 - self.parameters["Ep"] = 0.0 # the planned excess reserves; (gamma * (1-lamb) * V) = (0.8 * 0.5 * 250.0) = 100.0 - self.parameters["Lp"] = 0.0 # the planned interbank loans; Q - ((Ip-I) + (Ep-E)) = 90.0 - - self.parameters["assetNumber"] = 6.0 # number of assets available to bank - self.parameters["numBanks"] = 3.0 # number of banks in the economy - - # first, calculate number of transactions for investments - numTransactions = int(round(self.parameters["assetNumber"] / self.parameters["numBanks"])) - - # - # ASSETS - # - - # finally, put them on the transaction stack - for i in range(numTransactions): - transaction = Transaction() - value = 100.0 - maturity = 50.0 - timeOfDefault = -1 - transaction.this_transaction("I", self.identifier, -2, value, self.parameters["rhoReal"], maturity, timeOfDefault) - self.accounts.append(transaction) - del transaction - - # excess reserves - value = 90.0 - transaction = Transaction() - transaction.this_transaction("E", self.identifier, -3, value, self.parameters["rb"], 0, -1) - self.accounts.append(transaction) - del transaction - - # required deposits to the central bank - value = 10.0 - transaction = Transaction() - transaction.this_transaction("rD", self.identifier, -3, value, self.parameters["rb"], 0, -1) - self.accounts.append(transaction) - del transaction - - # - # LIABILITIES - # - - # banking capital - value = 40.0 - transaction = Transaction() - transaction.this_transaction("BC", self.identifier, self.identifier, value, 0.0, 0, -1) - self.accounts.append(transaction) - del transaction - - # deposits - value = 250.0 - transaction = Transaction() - transaction.this_transaction("D", -1, self.identifier, value, self.parameters["rd"], 0, -1) - self.accounts.append(transaction) - del transaction - - # central bank loans - value = 10.0 - transaction = Transaction() - transaction.this_transaction("LC", -3, self.identifier, value, self.parameters["rb"], 0, -1) - self.accounts.append(transaction) - del transaction - + def __getattr__(self, attr): + return super(Bank, self).__getattr__(attr) # ------------------------------------------------------------------------- diff --git a/src/banks/3bank-0.xml b/src/banks/3bank-0.xml deleted file mode 100644 index f4d49b3..0000000 --- a/src/banks/3bank-0.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/src/banks/3bank-1.xml b/src/banks/3bank-1.xml deleted file mode 100644 index f5b439b..0000000 --- a/src/banks/3bank-1.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/src/banks/3bank-2.xml b/src/banks/3bank-2.xml deleted file mode 100644 index 094c777..0000000 --- a/src/banks/3bank-2.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/src/environment.py b/src/environment.py index f79c236..24ddf9c 100644 --- a/src/environment.py +++ b/src/environment.py @@ -1,10 +1,11 @@ #!/usr/bin/env python -# [SublimeLinter pep8-max-line-length:300] +# [SublimeLinter pep8-max-line-length:150] # -*- coding: utf-8 -*- """ black_rhino is a multi-agent simulator for financial network analysis -Copyright (C) 2012 Co-Pierre Georg (co-pierre.georg@keble.ox.ac.uk) +Copyright (C) 2016 Co-Pierre Georg (co-pierre.georg@keble.ox.ac.uk) +Pawel Fiedor (pawel@fiedor.eu) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,7 +22,6 @@ import logging import os -import networkx as nx from abm_template.src.baseconfig import BaseConfig @@ -31,520 +31,593 @@ # # ------------------------------------------------------------------------- class Environment(BaseConfig): - # from state import State - # from parameters import Parameters - from src.network import Network - + # # # VARIABLES # - identifier = "" - - # parameters = Parameters() - # state = State() - banks = [] - network = Network("") - # assets = [] - - static_parameters = {} - static_parameters["numSimulations"] = 0 - static_parameters["numSweeps"] = 0 - static_parameters["numBanks"] = 0 - static_parameters["bankDirectory"] = "" - # parameters for the networks - static_parameters["graphType"] = "" - static_parameters["graphParameter1"] = 0.0 - static_parameters["graphParameter2"] = 0.0 - static_parameters["contractsNetworkFile"] = "" - # the array of parameters that changes during the simulation - # parameters = [] - variable_parameters = {} - - # state _ variables # - # VARIABLES + identifier = "" # identifier of the environment + + banks = [] # a list containing all banks (instances of class Bank) + households = [] # a list containing all households (instances of class Household) + firms = [] # a list containing all firms (instances of class Firm) + central_bank = [] # to be consistent there's a list of central banks, but will consist of just one instance of class CentralBank + agents = [] + + assets = {} # dictionary of assets: "name": ["expected return", "return volatility", "current returns"] + shocks = [] # list of shocks: [sweep_from, sweep_to, kind_of_shock] + + static_parameters = {} # a dictionary containing all static parameters (with a fixed value) + variable_parameters = {} # a dictionary containing all variable parameters (with a range of possible values) + # DO NOT EVER ASSIGN PARAMETERS BY HAND AS DONE BELOW IN PRODUCTION CODE + # ALWAYS READ THE PARAMETERS FROM CONFIG FILES + # OR USE THE FUNCTIONS FOR SETTING / CHANGING VARIABLES + # CONVERSELY, IF YOU WANT TO READ THE VALUE, DON'T USE THE FULL NAMES + # INSTEAD USE __getattr__ POWER TO CHANGE THE COMMAND FROM + # instance.static_parameters["xyz"] TO instance.xyz - THE LATTER IS PREFERRED + static_parameters["num_simulations"] = 0 # number of simulations to be performed + static_parameters["num_sweeps"] = 0 # numbers of runs in a single simulation + + static_parameters["num_banks"] = 0 # number of banks in a simulation + static_parameters["num_firms"] = 0 # number of firms in a simulation + static_parameters["num_households"] = 0 # number of households in a simulation + + static_parameters["bank_directory"] = "" # directory containing bank config files + static_parameters["firm_directory"] = "" # directory containing firm config files + static_parameters["household_directory"] = "" # directory containing household config files + static_parameters["central_bank_directory"] = "" # directory containing central bank config file + + static_parameters["max_leverage_ratio"] = "" # max allowed leverage ratio of the banks (policy bound) + + # + # + # CODE + # # - # parameters determining the cash flow of banks - static_parameters["rb"] = 0.0 # interbank interest rate - static_parameters["rd"] = 0.0 # interest rate on deposits - # parameters for the central bank - static_parameters["collateralQuality"] = 0.0 # the fraction of a bank's portfolio that the central bank accepts as collateral - # firm parameters - static_parameters["successProbabilityFirms"] = 0.0 # probability of successful credit - static_parameters["positiveReturnFirms"] = 0.0 # return for a successful credit - static_parameters["firmLoanMaturity"] = 0.0 # maturity of loans to firms - # household parameters - static_parameters["scaleFactorHouseholds"] = 0.0 # scaling factor for deposit fluctuations - # bank parameters - static_parameters["dividendLevel"] = 0.0 # dividend level as paid out by banks - static_parameters["pBank"] = 0.0 # bank's assumed credit success probability - static_parameters["rhoBank"] = 0.0 # expected return of banks - static_parameters["pFinancial"] = 0.0 # bank's assumed credit success probability - static_parameters["rhoFinancial"] = 0.0 # expected return of banks - static_parameters["thetaBank"] = 0.0 # bank's risk aversion parameter - static_parameters["xiBank"] = 0.0 # scaling factor for CRRA - static_parameters["gammaBank"] = 0.0 # fraction of interbank lending in overall balance sheet - static_parameters["assetNumber"] = 0 # number of assets in the economy - static_parameters["interbankLoanMaturity"] = 0.0 # the maturity of interbank loans - # simulation specific parameters - static_parameters["shockType"] = 0 # type of shock that hits the system in the current state - static_parameters["liquidationDiscountFactor"] = 0.0 # the discount factor delta in exp(-delta x) when liquidating assets - static_parameters["riskAversionDiscountFactor"] = 0.0 # the risk aversion discount when there was no default in the previous period - static_parameters["riskAversionAmplificationFactor"] = 0.0 # the risk aversion amplification when there *was* a default in the previous or current period - # regulation specific parameters - static_parameters["r"] = 0.0 # minimum required deposit rate - static_parameters["sifiSurchargeFactor"] = 1.0 # the surcharge on banking capital that SIFIs have to hold - static_parameters["liquidityCoverageRatio"] = 0.0 # the fraction of assets that must have a high liquidation value - static_parameters["netStableFundingRatio"] = 0.0 # the fraction of deposits that must have low volatility - static_parameters["leverageRatio"] = 0.0 # the minimal ratio of banking capital to total assets - static_parameters["requiredCapitalRatio"] = 0.08 # the required capital ratio for banks - - # bookkeeping parameters - static_parameters["insolvencyHistory"] = [] # [num, time] the number of bank insolvencies and when they occured # ------------------------------------------------------------------------- - # print_parameters(self) + # functions for setting/changing id, parameters, and state variables + # these either return or set specific value to the above variables + # with the exception of add (2 first ones) which append the dictionaries + # which contain static parameters or variable parameters # ------------------------------------------------------------------------- - def print_parameters(self): - print "identifier: " + self.identifier - print "numSweeps: " + str(self.static_parameters["numSweeps"]) - print "numSimulations: " + str(self.static_parameters["numSimulations"]) - print "numBanks: " + str(self.static_parameters["numBanks"]) - print "graphType: " + str(self.static_parameters["graphType"]) - for key in self.variable_parameters: - print str(key) + " ; " + str(self.variable_parameters[key]['value']) + " ; " + str(self.variable_parameters[key]['validity'][0]) + "-" + str(self.variable_parameters[key]['validity'][1]) + # ------------------------------------------------------------------------- + # add_static_parameter(self, type, value) + # add a given parameter to the stack of static parameters + # ------------------------------------------------------------------------- + def add_static_parameter(self, name, value): + super(Environment, self).add_static_parameter(name, value) # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- - # add_parameter(self, type, value, validFrom, validTo) + # add_variable_parameter(self, type, range_from, range_to) + # adds a given parameter to the stack of variable parameters # ------------------------------------------------------------------------- - def add_parameter(self, name, value, validFrom, validTo): - # add the parameter to the stack of parameters - self.variable_parameters[name] = {'value': value, 'validity': [validFrom, validTo]} + def add_variable_parameter(self, name, range_from, range_to): + super(Environment, self).add_variable_parameter(name, range_from, range_to) # ------------------------------------------------------------------------- + # ------------------------------------------------------------------------- + # __init__(self, environment_directory, identifier) + # initializes the bank given the directory containing the config + # files and the identifier (name of the config file) + # ------------------------------------------------------------------------- def __init__(self, environment_directory, identifier): self.initialize(environment_directory, identifier) + # ------------------------------------------------------------------------- + # ------------------------------------------------------------------------- + # get_identifier(self) + # returns the identifier of the environment + # ------------------------------------------------------------------------- def get_identifier(self): return self.identifier + # ------------------------------------------------------------------------- - def set_identifier(self, _value): - """ - Class variables: identifier - Local variables: _identifier - """ - super(Environment, self).set_identifier(_value) + # ------------------------------------------------------------------------- + # set_identifier(self, value) + # changes the environment to the supplied value + # ------------------------------------------------------------------------- + def set_identifier(self, value): + super(Environment, self).set_identifier(value) + # ------------------------------------------------------------------------- + # ------------------------------------------------------------------------- + # get_static_parameters(self) + # returns static parameters of the environment + # ------------------------------------------------------------------------- def get_static_parameters(self): return self.static_parameters + # ------------------------------------------------------------------------- - def set_static_parameters(self, _value): - """ - Class variables: static_parameters - Local variables: _params - """ - super(Environment, self).set_static_parameters(_value) + # ------------------------------------------------------------------------- + # set_static_parameters(self, value) + # changes static parameters to the supplied value + # ------------------------------------------------------------------------- + def set_static_parameters(self, value): + super(Environment, self).set_static_parameters(value) + # ------------------------------------------------------------------------- + # ------------------------------------------------------------------------- + # get_variable_parameters(self) + # returns variable parameters of the environment + # ------------------------------------------------------------------------- def get_variable_parameters(self): return self.variable_parameters + # ------------------------------------------------------------------------- - def set_variable_parameters(self, _value): - """ - Class variables: variable_parameters - Local variables: _params - """ - super(Environment, self).set_variable_parameters(_value) + # ------------------------------------------------------------------------- + # set_variable_parameters(self, value): + # changes variable parameters to the supplied value + # ------------------------------------------------------------------------- + def set_variable_parameters(self, value): + super(Environment, self).set_variable_parameters(value) + # ------------------------------------------------------------------------- + # ------------------------------------------------------------------------- + # get_assets(self) + # returns assets of the environment + # ------------------------------------------------------------------------- + def get_assets(self): + return self.assets + # ------------------------------------------------------------------------- + + # ------------------------------------------------------------------------- + # set_assets(self, value): + # changes assets to the supplied value + # ------------------------------------------------------------------------- + def set_assets(self, value): + super(Environment, self).set_assets(value) + # ------------------------------------------------------------------------- + + # ------------------------------------------------------------------------- + # get_shocks(self) + # returns shocks of the environment + # ------------------------------------------------------------------------- + def get_shocks(self): + return self.shocks + # ------------------------------------------------------------------------- + + # ------------------------------------------------------------------------- + # set_shocks(self, value): + # changes shocks to the supplied value + # ------------------------------------------------------------------------- + def set_shocks(self, value): + super(Environment, self).set_shocks(value) + # ------------------------------------------------------------------------- + + # ------------------------------------------------------------------------- + # add_shock(self, shock): + # adds shock to the list of shocks + # ------------------------------------------------------------------------- + def add_shock(self, shock): + super(Environment, self).add_shock(shock) + # ------------------------------------------------------------------------- + + # ------------------------------------------------------------------------- + # agents_generator(self): + # generator yielding all agents + # ------------------------------------------------------------------------- + def agents_generator(self): + # self.agents = [self.banks, self.firms, self.households] + return super(Environment, self).agents_generator() + # ------------------------------------------------------------------------- + + # ------------------------------------------------------------------------- + # get_agent_by_id + # returns an agent based on the id + # ------------------------------------------------------------------------- + def get_agent_by_id(self, ident): + # self.agents = [self.banks, self.firms, self.households] + return super(Environment, self).get_agent_by_id(ident) + # ------------------------------------------------------------------------- + + # ------------------------------------------------------------------------- + # __getattr__ + # if the attribute isn't found by Python we tell Python + # to look for it first in static and then in variable parameters + # which allows for directly fetching parameters from the Environment + # i.e. environment.num_banks instead of a bit more bulky + # environment.static_parameters["num_banks"] + # makes sure we don't have it in both containers, which + # would be bad practice [provides additional checks] + # ------------------------------------------------------------------------- + def __getattr__(self, attr): + return super(Environment, self).__getattr__(attr) + # ------------------------------------------------------------------------- + + # ------------------------------------------------------------------------- + # Functions for printing and writing + # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- # __str__ + # returns the environment as an xml like config file # ------------------------------------------------------------------------- def __str__(self): - text = "\n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - # find the number of total insolvencies - numberInsolvencies = 0 - for entry in self.static_parameters["insolvencyHistory"]: - numberInsolvencies += entry[0] - text += " \n" - text += "\n" - - return text + environment_string = super(Environment, self).__str__() + # abstract class uses config, we use environment, so we amend the string + environment_string = environment_string.replace("", "", 1) + return environment_string # ------------------------------------------------------------------------ - def read_xml_config_file(self, _config_file_name): - """ - Class variables: identifier, static_parameters, variable_parameters - Local variables: xmlText, config_file_name, element, subelement, name, value, format_correct, range_from, range_to - """ - self.read_environment_file(_config_file_name) - # super(Environment, self).read_xml_config_file(_config_file_name) + # ------------------------------------------------------------------------- + # print_parameters(self) + # prints the parameters within the environment (static + variable) + # ------------------------------------------------------------------------- + def print_parameters(self): + super(Environment, self).print_parameters() + # ------------------------------------------------------------------------- - # - # METHODS - # + # ------------------------------------------------------------------------- + # write_environment_file(file_name) + # writes the environment as an xml config file to an .xml file + # with the given file_name to the current directory + # ------------------------------------------------------------------------- + def write_environment_file(self, file_name): + super(Environment, self).write_environment_file(file_name) + # ------------------------------------------------------------------------- + + # ------------------------------------------------------------------------- + # Functions for reading config files and initializing + # ------------------------------------------------------------------------- + # ------------------------------------------------------------------------- + # read_xml_config_file(self, config_file_name) + # reads an xml file with config and sets identifier, static and variable + # parameters to whatever is in the config file + # ------------------------------------------------------------------------- + def read_xml_config_file(self, config_file_name): + super(Environment, self).read_xml_config_file(config_file_name) + # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- - # initialize + # initialize(self, environment_directory, identifier) + # initializes the environment, initializing all the variables + # reading the config file from supplied environment_directory and + # identifier, and initializes all agents from the directories + # supplied in the main config file # ------------------------------------------------------------------------- def initialize(self, environment_directory, identifier): self.identifier = identifier self.static_parameters = {} - self.static_parameters["numSimulations"] = 0 - self.static_parameters["numSweeps"] = 0 - self.static_parameters["numBanks"] = 0 - self.static_parameters["bankDirectory"] = "" - # parameters for the networks - self.static_parameters["graphType"] = "" - self.static_parameters["graphParameter1"] = 0.0 - self.static_parameters["graphParameter2"] = 0.0 - self.static_parameters["contractsNetworkFile"] = "" - # the array of parameters that changes during the simulation - # parameters = [] + self.static_parameters["num_simulations"] = 0 + self.static_parameters["num_sweeps"] = 0 + self.static_parameters["num_banks"] = 0 + self.static_parameters["num_firms"] = 0 + self.static_parameters["num_households"] = 0 + self.static_parameters["bank_directory"] = "" + self.static_parameters["firm_directory"] = "" + self.static_parameters["household_directory"] = "" + self.static_parameters["central_bank_directory"] = "" + self.static_parameters["max_leverage_ratio"] = "" self.variable_parameters = {} - # state _ variables - # - # VARIABLES - # - # parameters determining the cash flow of banks - self.static_parameters["rb"] = 0.0 # interbank interest rate - self.static_parameters["rd"] = 0.0 # interest rate on deposits - # parameters for the central bank - self.static_parameters["collateralQuality"] = 0.0 # the fraction of a bank's portfolio that the central bank accepts as collateral - # firm parameters - self.static_parameters["successProbabilityFirms"] = 0.0 # probability of successful credit - self.static_parameters["positiveReturnFirms"] = 0.0 # return for a successful credit - self.static_parameters["firmLoanMaturity"] = 0.0 # maturity of loans to firms - # household parameters - self.static_parameters["scaleFactorHouseholds"] = 0.0 # scaling factor for deposit fluctuations - # bank parameters - self.static_parameters["dividendLevel"] = 0.0 # dividend level as paid out by banks - self.static_parameters["pBank"] = 0.0 # bank's assumed credit success probability - self.static_parameters["rhoBank"] = 0.0 # expected return of banks - self.static_parameters["pFinancial"] = 0.0 # bank's assumed credit success probability - self.static_parameters["rhoFinancial"] = 0.0 # expected return of banks - self.static_parameters["thetaBank"] = 0.0 # bank's risk aversion parameter - self.static_parameters["xiBank"] = 0.0 # scaling factor for CRRA - self.static_parameters["gammaBank"] = 0.0 # fraction of interbank lending in overall balance sheet - self.static_parameters["assetNumber"] = 0 # number of assets in the economy - self.static_parameters["interbankLoanMaturity"] = 0.0 # the maturity of interbank loans - # simulation specific parameters - self.static_parameters["shockType"] = 0 # type of shock that hits the system in the current state - self.static_parameters["liquidationDiscountFactor"] = 0.0 # the discount factor delta in exp(-delta x) when liquidating assets - self.static_parameters["riskAversionDiscountFactor"] = 0.0 # the risk aversion discount when there was no default in the previous period - self.static_parameters["riskAversionAmplificationFactor"] = 0.0 # the risk aversion amplification when there *was* a default in the previous or current period - # regulation specific parameters - self.static_parameters["r"] = 0.0 # minimum required deposit rate - self.static_parameters["sifiSurchargeFactor"] = 1.0 # the surcharge on banking capital that SIFIs have to hold - self.static_parameters["liquidityCoverageRatio"] = 0.0 # the fraction of assets that must have a high liquidation value - self.static_parameters["netStableFundingRatio"] = 0.0 # the fraction of deposits that must have low volatility - self.static_parameters["leverageRatio"] = 0.0 # the minimal ratio of banking capital to total assets - self.static_parameters["requiredCapitalRatio"] = 0.08 # the required capital ratio for banks - - # bookkeeping parameters - self.static_parameters["insolvencyHistory"] = [] # [num, time] the number of bank insolvencies and when they occured # first, read in the environment file environment_filename = environment_directory + identifier + ".xml" - self.read_environment_file(environment_filename) + self.read_xml_config_file(environment_filename) logging.info(" environment file read: %s", environment_filename) # then read in all the banks - if (self.static_parameters["bankDirectory"] != ""): - if (self.static_parameters["bankDirectory"] != "none"): # none is used for tests only - self.initialize_banks_from_files(self.static_parameters["bankDirectory"], self.get_state(0), 0) - logging.info(" banks read from directory: %s", self.static_parameters["bankDirectory"]) + if (self.bank_directory != ""): + if (self.bank_directory != "none"): # none is used for tests only + self.initialize_banks_from_files(self.bank_directory) + logging.info(" banks read from directory: %s", self.bank_directory) + else: + logging.error("ERROR: no bank_directory given in %s\n", environment_filename) + + # then read in all the firms + if (self.firm_directory != ""): + if (self.firm_directory != "none"): # none is used for tests only + self.initialize_firms_from_files(self.firm_directory) + logging.info(" firms read from directory: %s", self.firm_directory) + else: + logging.error("ERROR: no firm_directory given in %s\n", environment_filename) + + # then read in all the households + if (self.household_directory != ""): + if (self.household_directory != "none"): # none is used for tests only + self.initialize_households_from_files(self.household_directory) + logging.info(" households read from directory: %s", self.household_directory) + else: + logging.error("ERROR: no household_directory given in %s\n", environment_filename) + + # then read in the central bank + if (self.central_bank_directory != ""): + if (self.bank_directory != "none"): # none is used for tests only + self.initialize_central_bank_from_files(self.central_bank_directory) + logging.info(" central bank read from directory: %s", self.central_bank_directory) + else: + logging.error("ERROR: no central_bank_directory given in %s\n", environment_filename) + + # add agents to the list of all agents + self.agents = [self.banks, self.firms, self.households, self.central_bank] + + # then, initialize transactions from the config files for banks + if (self.bank_directory != ""): + if (self.bank_directory != "none"): # none is used for tests only + self.read_transactions_for_banks(self.bank_directory) + logging.info(" banks' transactions read from directory: %s", self.bank_directory) else: - logging.error("ERROR: no bankDirectory given in %s\n", environment_filename) + logging.error("ERROR: no bank_directory given in %s\n", environment_filename) - self.initial_assets = 0.0 # the initial assets are needed to determine the fire-sale price in bank.liquidate_assets - for bank in self.banks: - self.initial_assets += bank.get_account("I") + # then, initialize transactions from the config files for firms + if (self.firm_directory != ""): + if (self.firm_directory != "none"): # none is used for tests only + self.read_transactions_for_firms(self.firm_directory) + logging.info(" firms' transactions read from directory: %s", self.firm_directory) + else: + logging.error("ERROR: no firm_directory given in %s\n", environment_filename) + + # then, initialize transactions from the config files for households + if (self.household_directory != ""): + if (self.household_directory != "none"): # none is used for tests only + self.read_transactions_for_households(self.household_directory) + logging.info(" households read from directory: %s", self.household_directory) + else: + logging.error("ERROR: no household_directory given in %s\n", environment_filename) + + # then, initialize transactions from the config files for central bank + if (self.central_bank_directory != ""): + if (self.central_bank_directory != "none"): # none is used for tests only + self.read_transactions_for_central_bank(self.central_bank_directory) + logging.info(" central bank's transactions read from directory: %s", self.central_bank_directory) + else: + logging.error("ERROR: no central_bank_directory given in %s\n", environment_filename) + + # ------------------------------------------------------------------------- - # finally, create the network - # note: this has to be done after creating the banks, as they are - # passed to the network as node objects - self.network.identifier = self.identifier - self.network.initialize_networks(self) + # ------------------------------------------------------------------------- + # initialize_banks_from_files(self, bank_directory) + # banks have to be initialized for each simulation as a number of + # banks might become inactive in the previous simulation + # this reads all config files in the provided directory and + # initializes banks with the contents of these configs + # ------------------------------------------------------------------------- + def initialize_banks_from_files(self, bank_directory): + from src.bank import Bank + # this routine is called more than once, so we have to reset the list of banks each time + while len(self.banks) > 0: + self.banks.pop() + # we list all the files in the specified directory + listing = os.listdir(bank_directory) + # and check if the number of files is in line with the parameters + if (len(listing) != self.num_banks): + logging.error(" ERROR: number of configuration files in %s (=%s) does not match num_banks (=%s)", + bank_directory, str(len(listing)), str(self.num_banks)) + # we read the files sequentially + for infile in listing: + bank = Bank() + bank.get_parameters_from_file(bank_directory + infile, self) + # and read parameters to the banks, only to add them to the environment + self.banks.append(bank) + # ------------------------------------------------------------------------- - # when there is a SIFI surcharge, implement it now on the banking capital - self.apply_sifi_surcharge() + # ------------------------------------------------------------------------- + # initialize_firms_from_files + # banks have to be initialized for each simulation as a number of + # banks might become inactive in the previous simulation + # this reads all config files in the provided directory and + # initializes firms with the contents of these configs + # ------------------------------------------------------------------------- + def initialize_firms_from_files(self, firm_directory): + from src.firm import Firm + # this routine is called more than once, so we have to reset the list of firms each time + while len(self.firms) > 0: + self.firms.pop() + # we list all the files in the specified directory + listing = os.listdir(firm_directory) + # and check if the number of files is in line with the parameters + if (len(listing) != self.num_firms): + logging.error(" ERROR: number of configuration files in %s (=%s) does not match num_firms (=%s)", + firm_directory, str(len(listing)), str(self.num_firms)) + # we read the files sequentially + for infile in listing: + firm = Firm() + firm.get_parameters_from_file(firm_directory + infile, self) + # and read parameters to the firms, only to add them to the environment + self.firms.append(firm) + # ------------------------------------------------------------------------- + + # ------------------------------------------------------------------------- + # initialize_households_from_files + # households have to be initialized for each simulation as a number of + # households might become inactive in the previous simulation + # this reads all config files in the provided directory and + # initializes households with the contents of these configs + # ------------------------------------------------------------------------- + def initialize_households_from_files(self, household_directory): + from src.household import Household + # this routine is called more than once, so we have to reset the list of households each time + while len(self.households) > 0: + self.households.pop() + # we list all the files in the specified directory + listing = os.listdir(household_directory) + # and check if the number of files is in line with the parameters + if (len(listing) != self.num_households): + logging.error(" ERROR: number of configuration files in %s (=%s) does not match num_households (=%s)", + household_directory, str(len(listing)), str(self.num_households)) + # we read the files sequentially + for infile in listing: + household = Household() + household.get_parameters_from_file(household_directory + infile, self) + # and read parameters to the firms, only to add them to the environment + self.households.append(household) + # ------------------------------------------------------------------------- + + # ------------------------------------------------------------------------- + # initialize_central_bank_from_files + # central bank has to be initialized for each simulation + # this reads all config files in the provided directory and + # initializes central bank with the contents of the config + # ------------------------------------------------------------------------- + def initialize_central_bank_from_files(self, central_bank_directory): + from src.central_bank import CentralBank + # this routine is called more than once, so we have to reset the list of households each time + while len(self.central_bank) > 0: + self.central_bank.pop() + # we list all the files in the specified directory + listing = os.listdir(central_bank_directory) + # and check if the number of files is in line with the parameters + if (len(listing) != 1): + logging.error(" ERROR: number of configuration files in %s (=%s) does not match one central bank", + central_bank_directory, str(len(listing))) + # we read the files sequentially + for infile in listing: + cb = CentralBank() + cb.get_parameters_from_file(central_bank_directory + infile, self) + # and read parameters to the firms, only to add them to the environment + self.central_bank.append(cb) # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- - # read_environment_file + # read_transactions_from_files(self, bank_directory) + # reads transactions for banks from the config files # ------------------------------------------------------------------------- - def read_environment_file(self, environmentFilename): + def read_transactions_for_banks(self, bank_directory): from xml.etree import ElementTree - xmlText = open(environmentFilename).read() - - element = ElementTree.XML(xmlText) - self.identifier = element.attrib['title'] - - # self.parameters.identifier = self.identifier - - # loop over all entries in the xml file - for subelement in element: - # the first set of parameters will be valid for the whole simulation - if (subelement.attrib['type'] == 'numSweeps'): - self.static_parameters["numSweeps"] = int(subelement.attrib['value']) - if (subelement.attrib['type'] == 'numSimulations'): - self.static_parameters["numSimulations"] = int(subelement.attrib['value']) - if (subelement.attrib['type'] == 'numBanks'): - self.static_parameters["numBanks"] = int(subelement.attrib['value']) - if (subelement.attrib['type'] == 'bankDirectory'): - self.static_parameters["bankDirectory"] = str(subelement.attrib['value']) - if (subelement.attrib['type'] == 'graphType'): - self.static_parameters["graphType"] = str(subelement.attrib['value']) - if (subelement.attrib['type'] == 'graphParameter1'): - self.static_parameters["graphParameter1"] = float(subelement.attrib['value']) - if (subelement.attrib['type'] == 'graphParameter2'): - self.static_parameters["graphParameter2"] = float(subelement.attrib['value']) - if (subelement.attrib['type'] == 'contractsNetworkFile'): - self.static_parameters["contractsNetworkFile"] = str(subelement.attrib['value']) - # now also read in the parameters that can change during the simulation - if (subelement.attrib['type'] == 'changing'): - name = subelement.attrib['name'] - value = float(subelement.attrib['value']) - validFrom = subelement.attrib['validity'].rsplit("-")[0] - validTo = subelement.attrib['validity'].rsplit("-")[1] - self.add_parameter(name, value, validFrom, validTo) + # we list all the files in the specified directory + listing = os.listdir(bank_directory) + # and check if the number of files is in line with the parameters + if (len(listing) != self.num_banks): + logging.error(" ERROR: number of configuration files in %s (=%s) does not match num_banks (=%s)", + bank_directory, str(len(listing)), str(self.num_banks)) + # we read the files sequentially) + for infile in listing: + # we open the file and find the identifier of the config + xmlText = open(bank_directory + infile).read() + element = ElementTree.XML(xmlText) + identifier = element.attrib['identifier'] + # and we find the bank with this identifier + bank = self.get_agent_by_id(identifier) + # then we read the transactions from the config to the appropriate bank + bank.get_transactions_from_file(bank_directory + infile, self) # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- - # write_environment_file(file_name) + # read_transactions_for_firms + # reads transactions for firms from the config files + # ------------------------------------------------------------------------- + def read_transactions_for_firms(self, firm_directory): + from xml.etree import ElementTree + # we list all the files in the specified directory + listing = os.listdir(firm_directory) + # and check if the number of files is in line with the parameters + if (len(listing) != self.num_firms): + logging.error(" ERROR: number of configuration files in %s (=%s) does not match num_firms (=%s)", + firm_directory, str(len(listing)), str(self.num_firms)) + # we read the files sequentially + for infile in listing: + # we open the file and find the identifier of the config + xmlText = open(firm_directory + infile).read() + element = ElementTree.XML(xmlText) + identifier = element.attrib['identifier'] + # and we find the firm with this identifier + firm = self.get_agent_by_id(identifier) + # then we read the transactions from the config to the appropriate firm + firm.get_transactions_from_file(firm_directory + infile, self) # ------------------------------------------------------------------------- - def write_environment_file(self, file_name): - out_file = open(file_name + "-check.xml", 'w') - text = "\n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" + # ------------------------------------------------------------------------- + # read_transactions_for_households + # reads transactions for households from the config files + # ------------------------------------------------------------------------- + def read_transactions_for_households(self, household_directory): + from xml.etree import ElementTree + # we list all the files in the specified directory + listing = os.listdir(household_directory) + # and check if the number of files is in line with the parameters + if (len(listing) != self.num_households): + logging.error(" ERROR: number of configuration files in %s (=%s) does not match num_households (=%s)", + household_directory, str(len(listing)), str(self.num_households)) + # we read the files sequentially + for infile in listing: + # we open the file and find the identifier of the config + xmlText = open(household_directory + infile).read() + element = ElementTree.XML(xmlText) + identifier = element.attrib['identifier'] + # and we find the firm with this identifier + household = self.get_agent_by_id(identifier) + # then we read the transactions from the config to the appropriate firm + household.get_transactions_from_file(household_directory + infile, self) + # ------------------------------------------------------------------------- - for entry in self.variable_parameters: - text += " \n" + # ------------------------------------------------------------------------- + # read_transactions_for_central_bank(self, central_bank_directory) + # reads transactions for central bank from the config file + # ------------------------------------------------------------------------- + def read_transactions_for_central_bank(self, central_bank_directory): + from xml.etree import ElementTree + # we list all the files in the specified directory + listing = os.listdir(central_bank_directory) + # and check if the number of files is in line with the parameters + if (len(listing) != 1): + logging.error(" ERROR: number of configuration files in %s (=%s) does not match one central bank", + central_bank_directory, str(len(listing))) + # we read the files sequentially + for infile in listing: + # we open the file and find the identifier of the config + xmlText = open(central_bank_directory + infile).read() + element = ElementTree.XML(xmlText) + identifier = element.attrib['identifier'] + # and we find the bank with this identifier + cb = self.get_agent_by_id(identifier) + # then we read the transactions from the config to the appropriate bank + cb.get_transactions_from_file(central_bank_directory + infile, self) + # ------------------------------------------------------------------------- - text += "\n" + # ------------------------------------------------------------------------- + # check_global_transaction_balance + # checks if transaction type (ie 'deposits') balances out globally + # I suppose this should presumably be used in the Updater after every + # step to ensure consistency, along with appropriately using the + # check_consistency function, and appropriately synchronising the update + # itself + # ------------------------------------------------------------------------- + def check_global_transaction_balance(self, type_): + super(Environment, self).check_global_transaction_balance(type_) + # ------------------------------------------------------------------------- - out_file.write(text) - out_file.close() + # ------------------------------------------------------------------------- + # accrue_interests() + # This method accrues interest on all transaction + # making sure we don't double count the transactions that are + # on the books of multiple agents, interest is specified within the + # transaction itself + # ------------------------------------------------------------------------- + def accrue_interests(self): + super(Environment, self).accrue_interests() # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- - # initialize_banks_from_files - # banks have to be initialized for each simulation as a number of banks might become inactive - # in the previous simulation + # new_transaction() + # ------------------------------------------------------------------------- + def new_transaction(self, type_, asset, from_, to, amount, interest, maturity, time_of_default): + from src.transaction import Transaction + transaction = Transaction() + transaction.this_transaction(type_, asset, from_, to, amount, interest, maturity, time_of_default) + transaction.add_transaction(self) # ------------------------------------------------------------------------- - def initialize_banks_from_files(self, bankDirectory, state, time): - from src.bank import Bank - # this routine is called more than once, so we have to reset the list of banks each time - self.banks = [] - listing = os.listdir(bankDirectory) - if (len(listing) != self.static_parameters["numBanks"]): - logging.error(" ERROR: number of configuration files in %s (=%s) does not match numBanks (=%s)", bankDirectory, str(len(listing)), str(self.static_parameters["numBanks"])) + # ------------------------------------------------------------------------- + # check_agent_homogeneity(type_) + # ------------------------------------------------------------------------- + def check_agent_homogeneity(self, type_): + for agent_one in eval("self."+str(type_)): + for agent_two in eval("self."+str(type_)): + if agent_one != agent_two: + for key in agent_one.parameters: + if agent_one.parameters[key] != agent_two.parameters[key]: + return False + for key in agent_one.state_variables: + if agent_one.state_variables[key] != agent_two.state_variables[key]: + return False + return True + # ------------------------------------------------------------------------- - for infile in listing: - bank = Bank() - bank.get_parameters_from_file(bankDirectory + infile, self.get_state(0), self.static_parameters["numBanks"], time) - self.banks.append(bank) - bank.__del__() # TODO not sure if this is really safe, but it is better than doing nothing about all those created instances... - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # get_state - # ------------------------------------------------------------------------- - def get_state(self, time): # TODO bring parameters in same order as in environment file and in state.__str__() - # for each time t in the simulation return the actual set of parameters - for parameter in self.variable_parameters: - validFrom = int(self.variable_parameters[parameter]['validity'][0]) - validTo = int(self.variable_parameters[parameter]['validity'][1]) - if (int(time) >= int(validFrom)) and (int(time) <= int(validTo)): # we have a valid parameterset - if parameter == 'rb': - self.static_parameters["rb"] = float(self.variable_parameters[parameter]['value']) - if parameter == 'rd': - self.static_parameters["rd"] = float(self.variable_parameters[parameter]['value']) - if parameter == 'r': - self.static_parameters["r"] = float(self.variable_parameters[parameter]['value']) - if parameter == 'collateralQuality': - self.static_parameters["collateralQuality"] = float(self.variable_parameters[parameter]['value']) - if parameter == 'successProbabilityFirms': - self.static_parameters["successProbabilityFirms"] = float(self.variable_parameters[parameter]['value']) - if parameter == 'positiveReturnFirms': - self.static_parameters["positiveReturnFirms"] = float(self.variable_parameters[parameter]['value']) - if parameter == 'scaleFactorHouseholds': - self.static_parameters["scaleFactorHouseholds"] = float(self.variable_parameters[parameter]['value']) - if parameter == 'dividendLevel': - self.static_parameters["dividendLevel"] = float(self.variable_parameters[parameter]['value']) - if parameter == 'pFinancial': - self.static_parameters["pFinancial"] = float(self.variable_parameters[parameter]['value']) - if parameter == 'rhoFinancial': - self.static_parameters["rhoFinancial"] = float(self.variable_parameters[parameter]['value']) - if parameter == 'pReal': - self.static_parameters["pReal"] = float(self.variable_parameters[parameter]['value']) - if parameter == 'rhoReal': - self.static_parameters["rhoReal"] = float(self.variable_parameters[parameter]['value']) - if parameter == 'xiBank': - self.static_parameters["xiBank"] = float(self.variable_parameters[parameter]['value']) - if parameter == 'thetaBank': - self.static_parameters["thetaBank"] = float(self.variable_parameters[parameter]['value']) - if parameter == 'rhoBank': - self.static_parameters["rhoBank"] = float(self.variable_parameters[parameter]['value']) - if parameter == 'shockType': - self.static_parameters["shockType"] = int(self.variable_parameters[parameter]['value']) - if parameter == 'gammaBank': - self.static_parameters["gammaBank"] = float(self.variable_parameters[parameter]['value']) - if parameter == 'assetNumber': - self.static_parameters["assetNumber"] = float(self.variable_parameters[parameter]['value']) - if parameter == 'liquidationDiscountFactor': - self.static_parameters["liquidationDiscountFactor"] = float(self.variable_parameters[parameter]['value']) - if parameter == 'riskAversionDiscountFactor': - self.static_parameters["riskAversionDiscountFactor"] = float(self.variable_parameters[parameter]['value']) - if parameter == 'riskAversionAmplificationFactor': - self.static_parameters["riskAversionAmplificationFactor"] = float(self.variable_parameters[parameter]['value']) - if parameter == 'interbankLoanMaturity': - self.static_parameters["interbankLoanMaturity"] = float(self.variable_parameters[parameter]['value']) - if parameter == 'firmLoanMaturity': - self.static_parameters["firmLoanMaturity"] = float(self.variable_parameters[parameter]['value']) - if parameter == 'sifiSurchargeFactor': - self.static_parameters["sifiSurchargeFactor"] = float(self.variable_parameters[parameter]['value']) - if parameter == 'requiredCapitalRatio': - self.static_parameters["requiredCapitalRatio"] = float(self.variable_parameters[parameter]['value']) - if parameter == 'liquidityCoverageRatio': - self.static_parameters["liquidityCoverageRatio"] = float(self.variable_parameters[parameter]['value']) - if parameter == 'netStableFundingRatio': - self.static_parameters["netStableFundingRatio"] = float(self.variable_parameters[parameter]['value']) - if parameter == 'leverageRatio': - self.static_parameters["leverageRatio"] = float(self.variable_parameters[parameter]['value']) - - # - # at this point we have all the variables from the parameters[] list - # now we need to update them to incorporate past defaults to calculate - # new return and volatility for real and financial assets - # self.update_state(time) - - return self - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # apply_sifi_surcharge - # ------------------------------------------------------------------------- - def apply_sifi_surcharge(self): - degree_sum = 0 - for bank in self.network.contracts: - degree_sum += float(nx.degree(self.network.contracts)[bank]) - average_degree = float(degree_sum / len(self.network.contracts.nodes())) - - for bank in self.network.contracts: - # the sifi surcharge is the product of the sifiSurchargeFactor and the connectedness as measured - # by degree/average_degree - # the maximum ensures that no bank has to hold less than 1.0 times their banking capital - sifiSurcharge = max(self.get_state(0).static_parameters["sifiSurchargeFactor"]*(float(nx.degree(self.network.contracts)[bank]) / average_degree), 1.0) - bank.apply_sifi_surcharge(sifiSurcharge) - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # print_state - # ------------------------------------------------------------------------- - def print_state(self): - print "rb: " + str(self.static_parameters["rb"]) - print "rd: " + str(self.static_parameters["rd"]) - print "r: " + str(self.static_parameters["r"]) - print "sifiSurchargeFactor: " + str(self.static_parameters["sifiSurchargeFactor"]) - print "successProbabilityFirms: " + str(self.static_parameters["successProbabilityFirms"]) - print "positiveReturnFirms: " + str(self.static_parameters["positiveReturnFirms"]) - print "scaleFactorHouseholds: " + str(self.static_parameters["scaleFactorHouseholds"]) - print "dividendLevel: " + str(self.static_parameters["dividendLevel"]) - print "shockType: " + str(self.static_parameters["shockType"]) - print "pBank: " + str(self.static_parameters["pBank"]) - print "xiBank: " + str(self.static_parameters["xiBank"]) - print "thetaBank: " + str(self.static_parameters["thetaBank"]) - print "rhoBank: " + str(self.static_parameters["rhoBank"]) - print "gammaBank: " + str(self.static_parameters["gammaBank"]) - print "assetNumber: " + str(self.static_parameters["assetNumber"]) - print "liquidationDiscountFactor: " + str(self.static_parameters["liquidationDiscountFactor"]) - print "interbankLoanMaturity: " + str(self.static_parameters["interbankLoanMaturity"]) - print "firmLoanMaturity: " + str(self.static_parameters["firmLoanMaturity"]) - print "requiredCapitalRatio: " + str(self.static_parameters["requiredCapitalRatio"]) - print "liquidityCoverageRatio: " + str(self.static_parameters["liquidityCoverageRatio"]) - print "netStableFundingRatio: " + str(self.static_parameters["netStableFundingRatio"]) - print "leverageRatio: " + str(self.static_parameters["leverageRatio"]) - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # addInsolvencyToHistory(time) - # ------------------------------------------------------------------------- - def addInsolvencyToHistory(self, time): - lastInsolvency = [0, -1] # if we have no insolvency yet - for insolvency in self.static_parameters["insolvencyHistory"]: # loop over the insolvencyHistory - if insolvency[1] == time: # to see if we have an insolvency in this time period - lastInsolvency = insolvency # if we do, update lastInsolvency - - if lastInsolvency[1] > -1: # see if we found an insolvency in this time step - lastInsolvency[0] += 1 # add one to the number of insolvencies - else: # there has not been an insolvency yet, so add one - self.static_parameters["insolvencyHistory"].append([1, time]) - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # update_state(time) - # this method calculates the new expected returns for real and - # financial assets - # ------------------------------------------------------------------------- - def update_state(self, time): - # - # real assets have an expected return as given in the environment file - # - pReal = self.static_parameters["pBank"] # TODO change pBank to pReal everywhere in code - rhoReal = self.static_parameters["rhoBank"] - # TODO change rhoBank to rhoReal everywhere in code - # TODO: now one could make the process for real assets a bit more interesting - self.static_parameters["pBank"] = pReal - self.static_parameters["rhoBank"] = rhoReal - - # financial assets start with some initial expected return and mean - # then they are updated when the simulation proceeds and become - # more volatile when more banks go into insolvency - pFinancial = self.static_parameters["pFinancial"] - rhoFinancial = self.static_parameters["rhoFinancial"] - # TODO now we could make something interesting - self.static_parameters["pFinancial"] = pFinancial - self.static_parameters["rhoFinancial"] = rhoFinancial + # ------------------------------------------------------------------------- + # update_asset_returns() + # ------------------------------------------------------------------------- + def update_asset_returns(self): + super(Environment, self).update_asset_returns() + # ------------------------------------------------------------------------- diff --git a/src/environments/test3.xml b/src/environments/test3.xml deleted file mode 100644 index ae38e4a..0000000 --- a/src/environments/test3.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/log/test3.log b/src/log/test3.log deleted file mode 100644 index 4cf50b5..0000000 --- a/src/log/test3.log +++ /dev/null @@ -1,66 +0,0 @@ -11/13/2015 12:52:38 START logging for run: src/environments/test3.xml -11/13/2015 12:52:38 environment file read: src/environments/test3.xml -11/13/2015 12:52:38 banks read from directory: C:\\black_rhino\\src\\banks\\ -11/13/2015 12:52:38 ERROR: no .gexf contractsNetworkFile found: networks/full-3_banks -11/13/2015 12:52:38 ERROR: check your test3.xml file. -11/13/2015 12:53:30 START logging for run: src/environments/test3.xml -11/13/2015 12:53:30 environment file read: src/environments/test3.xml -11/13/2015 12:53:30 banks read from directory: C:\\black_rhino\\src\\banks\\ -11/13/2015 12:53:30 read network of contracts: src/networks/full-3_banks -11/13/2015 12:53:30 created the network of contracts with 3 nodes and 6 links -11/13/2015 12:53:30 created the network of exposures with 3 nodes -11/13/2015 12:53:30 measurement started... -11/13/2015 13:08:32 START logging for run: src/environments/test3.xml -11/13/2015 13:08:32 environment file read: src/environments/test3.xml -11/13/2015 13:08:32 banks read from directory: C:\\black_rhino\\src\\banks\\ -11/13/2015 13:08:32 read network of contracts: src/networks/full-3_banks -11/13/2015 13:08:32 created the network of contracts with 3 nodes and 6 links -11/13/2015 13:08:32 created the network of exposures with 3 nodes -11/13/2015 13:08:32 measurement started... -11/13/2015 13:08:32 STARTED with run 0 -11/13/2015 13:08:32 environment file read: src/environments/test3.xml -11/13/2015 13:08:32 banks read from directory: C:\\black_rhino\\src\\banks\\ -11/13/2015 13:08:32 read network of contracts: src/networks/full-3_banks -11/13/2015 13:08:32 created the network of contracts with 3 nodes and 6 links -11/13/2015 13:08:32 created the network of exposures with 3 nodes -11/13/2015 13:08:32 time: 0: -11/13/2015 13:08:32 DONE -11/13/2015 13:08:32 ....measurement finished -11/13/2015 13:08:32 FINISHED logging for run: src/environments/test3.xml - -11/13/2015 13:09:06 START logging for run: src/environments/test3.xml -11/13/2015 13:09:06 environment file read: src/environments/test3.xml -11/13/2015 13:09:06 banks read from directory: C:\\black_rhino\\src\\banks\\ -11/13/2015 13:09:06 read network of contracts: src/networks/full-3_banks -11/13/2015 13:09:06 created the network of contracts with 3 nodes and 6 links -11/13/2015 13:09:06 created the network of exposures with 3 nodes -11/13/2015 13:09:06 measurement started... -11/13/2015 13:09:06 STARTED with run 0 -11/13/2015 13:09:06 environment file read: src/environments/test3.xml -11/13/2015 13:09:06 banks read from directory: C:\\black_rhino\\src\\banks\\ -11/13/2015 13:09:06 read network of contracts: src/networks/full-3_banks -11/13/2015 13:09:06 created the network of contracts with 3 nodes and 6 links -11/13/2015 13:09:06 created the network of exposures with 3 nodes -11/13/2015 13:09:06 time: 0: -11/13/2015 13:09:06 DONE -11/13/2015 13:09:06 ....measurement finished -11/13/2015 13:09:06 FINISHED logging for run: src/environments/test3.xml - -11/13/2015 14:28:37 START logging for run: src/environments/test3.xml -11/13/2015 14:28:37 environment file read: src/environments/test3.xml -11/13/2015 14:28:37 banks read from directory: C:\\black_rhino\\src\\banks\\ -11/13/2015 14:28:37 read network of contracts: src/networks/full-3_banks -11/13/2015 14:28:37 created the network of contracts with 3 nodes and 6 links -11/13/2015 14:28:37 created the network of exposures with 3 nodes -11/13/2015 14:28:37 measurement started... -11/13/2015 14:28:37 STARTED with run 0 -11/13/2015 14:28:37 environment file read: src/environments/test3.xml -11/13/2015 14:28:37 banks read from directory: C:\\black_rhino\\src\\banks\\ -11/13/2015 14:28:37 read network of contracts: src/networks/full-3_banks -11/13/2015 14:28:37 created the network of contracts with 3 nodes and 6 links -11/13/2015 14:28:37 created the network of exposures with 3 nodes -11/13/2015 14:28:37 time: 0: -11/13/2015 14:28:37 DONE -11/13/2015 14:28:37 ....measurement finished -11/13/2015 14:28:37 FINISHED logging for run: src/environments/test3.xml - diff --git a/src/measurement.py b/src/measurement.py index c4ef3cb..315bc64 100644 --- a/src/measurement.py +++ b/src/measurement.py @@ -1,10 +1,11 @@ #!/usr/bin/env python -# [SublimeLinter pep8-max-line-length:300] +# [SublimeLinter pep8-max-line-length:150] # -*- coding: utf-8 -*- """ black_rhino is a multi-agent simulator for financial network analysis -Copyright (C) 2012 Co-Pierre Georg (co-pierre.georg@keble.ox.ac.uk) +Copyright (C) 2016 Co-Pierre Georg (co-pierre.georg@keble.ox.ac.uk) +Pawel Fiedor (pawel@fiedor.eu) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,6 +21,7 @@ """ import logging +from abm_template.src.basemeasurement import BaseMeasurement # ============================================================================ # @@ -28,111 +30,151 @@ # ============================================================================ -class Measurement(object): +class Measurement(BaseMeasurement): # # VARIABLES # - activeBanks = [] - I = [] - D = [] - L = [] - LC = [] - - histoActiveBanks = [] - histoI = [] - histoD = [] - histoL = [] - histoLC = [] + + # identifier for usual purposes + identifier = "" + # Now we set up a config for the measurements + # see notes on the xml config file in the method below + config = {} + # environment for access + environment = type('', (), {})() + # filename for the output csv + # runner for access + runner = type('', (), {})() + filename = "" + # and the file we're writing to + file = None + # plus the csv writer + csv_writer = None # # METHODS # + + def get_identifier(self): + return self.identifier + + def set_identifier(self, identifier): + super(Measurement, self).set_identifier(identifier) + + def get_config(self): + return self.config + + def set_config(self, config): + super(Measurement, self).set_config(config) + + def get_environment(self): + return self.environment + + def set_environment(self, environment): + super(Measurement, self).set_environment(environment) + + def get_runner(self): + return self.runner + + def set_runner(self, runner): + super(Measurement, self).set_runner(runner) + + def get_filename(self): + return self.filename + + def set_filename(self, filename): + super(Measurement, self).set_filename(filename) + + def get_file(self): + return self.file + + def set_file(self, file): + super(Measurement, self).set_file(file) + + def get_csv_writer(self): + return self.csv_writer + + def set_csv_writer(self, csv_writer): + super(Measurement, self).set_csv_writer(csv_writer) + # ------------------------------------------------------------------------- - # + # __init__(self, environment, runner) + # Initialises the Measurements object and reads the config # ------------------------------------------------------------------------- - def __init__(self): - logging.info(" measurement started...") + def __init__(self, environment, runner): + super(Measurement, self).__init__(environment, runner) # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- - # def initialize + # open_file(self) + # Opens the file and writes the headers # ------------------------------------------------------------------------- - def initialize(self): - self.activeBanks = [] - self.I = [] - self.D = [] - self.L = [] - self.LC = [] + def open_file(self): + super(Measurement, self).open_file() # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- - # def do_measurement() + # write_to_file(self) + # Writes a row of values for to store the state of the system + # at the time of calling this method # ------------------------------------------------------------------------- - def do_measurement(self, banks): - sumActiveBanks = 0 - sumI = 0.0 - sumD = 0.0 - sumL = 0.0 - sumLC = 0.0 - - for bank in banks: - # first, check if the bank is active - if (bank.parameters["active"] >= 0.0): - sumActiveBanks = sumActiveBanks + 1 - # then, get the different balance sheet items - sumI = sumI + bank.get_account("I") - sumD = sumD + bank.get_account("D") - sumL = sumL + bank.get_account("L") - sumLC = sumLC + bank.get_account("LC") - - self.activeBanks.append(sumActiveBanks) - self.I.append(sumI) - self.D.append(sumD) - self.L.append(sumL) - self.LC.append(sumLC) + def write_to_file(self): + super(Measurement, self).write_to_file() # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- - # def do_histograms() + # close_file(self, filename) + # Closes the file so we don't have issues with the disk and the file # ------------------------------------------------------------------------- - def do_histograms(self): - self.histoActiveBanks.append(self.activeBanks) - self.histoI.append(self.I) - self.histoD.append(self.D) - self.histoL.append(self.L) - self.histoLC.append(self.LC) + def close_file(self): + super(Measurement, self).close_file() # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- - # def write_histograms() + # read_xml_config_file(self, config_file_name) + # Read the xml config file specifying the config file + # which is a list of lists + # We need to specify the filename + # We also need to specify each output: + # - type: 'output' + # - column: integer specifying which column will be used for this + # - header: string written as header in the csv file in the column + # - value: string or number, identifier for the wrapper function + # specifying what the wrapper function returns + # Thus: + # {column_number: [header, output, wrapper_id],...:[...]] + # [int: [string, string, string],...:[...]] + # + # Now we pass this on to the Measurement class through an xml file + # which should look like this + # + # + # + # + # + # + # # ------------------------------------------------------------------------- - def write_histograms(self, baselineDirectory, environment): - # first, construct the file name for the parameter set - baseFileName = baselineDirectory + environment.identifier - - # then, write the different histograms - fileName = baseFileName + "-histoActiveBanks.dat" - self.write_histogram(self.histoActiveBanks, fileName) - fileName = baseFileName + "-histoI.dat" - self.write_histogram(self.histoI, fileName) - fileName = baseFileName + "-histoD.dat" - self.write_histogram(self.histoD, fileName) - fileName = baseFileName + "-histoL.dat" - self.write_histogram(self.histoL, fileName) - fileName = baseFileName + "-histoLC.dat" - self.write_histogram(self.histoLC, fileName) - - logging.info(" ....measurement finished") + def read_xml_config_file(self, config_file_name): + super(Measurement, self).read_xml_config_file(config_file_name) # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- - # def write_histogram() + # wrapper(self, id) + # Wrapper for functions returning the desired values to be written # ------------------------------------------------------------------------- - def write_histogram(self, histogram, fileName): - file = open(fileName, "w") - for line in histogram: - for entry in line: - file.write(str(round(float(entry), 4)) + " ") - file.write("\n") - file.close() + def wrapper(self, ident): + if ident == "current_step": + return self.runner.current_step+1 + + if ident == "household_deposits": + #return self.environment.households[0].get_account("deposits") + wealth = 0.0 + for household in self.environment.households: + for tranx in household.accounts: + if tranx.type_ == "deposits" and tranx.from_ == household: + wealth = wealth + tranx.amount + if tranx.type_ == "loans" and tranx.to == household: + wealth = wealth - tranx.amount + return wealth # ------------------------------------------------------------------------- diff --git a/src/measurements/test3-histoActiveBanks.dat b/src/measurements/test3-histoActiveBanks.dat deleted file mode 100644 index 579967c..0000000 --- a/src/measurements/test3-histoActiveBanks.dat +++ /dev/null @@ -1 +0,0 @@ -0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 diff --git a/src/measurements/test3-histoD.dat b/src/measurements/test3-histoD.dat deleted file mode 100644 index 579967c..0000000 --- a/src/measurements/test3-histoD.dat +++ /dev/null @@ -1 +0,0 @@ -0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 diff --git a/src/measurements/test3-histoI.dat b/src/measurements/test3-histoI.dat deleted file mode 100644 index 579967c..0000000 --- a/src/measurements/test3-histoI.dat +++ /dev/null @@ -1 +0,0 @@ -0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 diff --git a/src/measurements/test3-histoL.dat b/src/measurements/test3-histoL.dat deleted file mode 100644 index 579967c..0000000 --- a/src/measurements/test3-histoL.dat +++ /dev/null @@ -1 +0,0 @@ -0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 diff --git a/src/measurements/test3-histoLC.dat b/src/measurements/test3-histoLC.dat deleted file mode 100644 index 579967c..0000000 --- a/src/measurements/test3-histoLC.dat +++ /dev/null @@ -1 +0,0 @@ -0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 diff --git a/src/network.py b/src/network.py deleted file mode 100644 index 202c47d..0000000 --- a/src/network.py +++ /dev/null @@ -1,269 +0,0 @@ -#!/usr/bin/env python -# [SublimeLinter pep8-max-line-length:300] -# -*- coding: utf-8 -*- - -""" -black_rhino is a multi-agent simulator for financial network analysis -Copyright (C) 2012 Co-Pierre Georg (co-pierre.georg@keble.ox.ac.uk) - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, version 3 of the License. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -""" - -import networkx as nx -import logging - -# ------------------------------------------------------------------------- -# -# class Network -# -# ------------------------------------------------------------------------- - - -class Network(object): - # - # VARIABLES - # - identifier = "" - contracts = nx.DiGraph() - exposures = nx.DiGraph() - - # - # METHODS - # - # ------------------------------------------------------------------------- - # __init__ - # ------------------------------------------------------------------------- - def __init__(self, identifier): - self.identifier = identifier - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # initialize_networks - # ------------------------------------------------------------------------- - def initialize_networks(self, environment): - # TODO: I thought this line should just be contracts = nx.DiGraph() - # but without the self., no interbank trades happen in the simulations. - # beats me, why... - self.contracts = nx.DiGraph() - - # - # read in the network structure - # - if (environment.static_parameters["graphType"] == "list"): - # networks that are read from files - try: - # the network of contracts is directed, as the willingness to lend is not necessarily mutual - contracts = nx.read_weighted_edgelist(str(environment.static_parameters["contractsNetworkFile"]) + ".list").to_directed() - logging.info(" read network of contracts: %s", environment.static_parameters["contractsNetworkFile"]) - except: - logging.error(" ERROR: no .list contractsNetworkFile found: %s", environment.static_parameters["contractsNetworkFile"]) - logging.error(" ERROR: check your %s.xml file.", environment.static_parameters["identifier"]) - - if (environment.static_parameters["graphType"] == "gexf"): - # networks that are read from files - try: - # the network of contracts is directed, as the willingness to lend is not neccessarily mutual - contracts = nx.read_gexf(str(environment.static_parameters["contractsNetworkFile"]) + ".gexf").to_directed() - logging.info(" read network of contracts: %s", environment.static_parameters["contractsNetworkFile"]) - except: - logging.error(" ERROR: no .gexf contractsNetworkFile found: %s", environment.static_parameters["contractsNetworkFile"]) - logging.error(" ERROR: check your %s.xml file.", environment.identifier) - - # first ensure that all nodes are in self.contracts and self.exposures - for bank in environment.banks: - self.contracts.add_node(bank) - self.exposures.add_node(bank) - - # create the network of contracts by looping over all edges in contracts - # find the node with the appropriate identifier in self.contracts and add the - # edge to the network - for u, v, edata in contracts.edges(data=True): - try: - link_weight = edata['weight'] - except: - link_weight = 1.0 - for node in self.contracts: - if (node.identifier == str(u)): - from_node = node - if (node.identifier == str(v)): - to_node = node - try: # if the edge cannot be added, the from or to node is missing and something funny is going on - self.contracts.add_edge(from_node, to_node, weight=link_weight) - except: # if that happens, debug which node is the problem - try: - _from = from_node.identifier - except: - _from = "nan" - try: - _to = to_node.identifier - except: - _to = "nan" - # and do some logging - logging.error(" ERROR: add_edge failed, from_node=%s, to_node=%s", str(_from), str(_to)) - - # after we are done, give a short message to the log - logging.info(" created the network of contracts with %s nodes and %s links", str(len(self.contracts.nodes())), str(len(self.contracts.edges()))) - logging.info(" created the network of exposures with %s nodes", str(len(self.exposures.nodes()))) - # ------------------------------------------------------------------------- - - -# -# INTERBANK ROUTINES -# - - # ------------------------------------------------------------------------- - # do_interbank_trades - # ------------------------------------------------------------------------- - def do_interbank_trades(self, environment): - from random import shuffle - activeBanks = [] - neighbors = [] - - # we are doing interbank trades, so the interest rate is fixed, as is the timeOfDefault and maturity - interest = environment.static_parameters["rb"] - maturity = environment.static_parameters["interbankLoanMaturity"] - timeOfDefault = -1 - - # we want to loop randomly over all nodes in order to avoid effects originating in the ordering of banks - banks = self.contracts.nodes() # shuffledle cannot deal with the return from a function - shuffle(banks) - for bank in banks: # loop over all banks in the list of shuffled banks - # print str(bank.identifier) + " " + str(bank.Lp) - neighbors = self.contracts.neighbors(bank) # we also want to loop over the neighbors in a random ordering - shuffle(neighbors) - for neighbor in neighbors: - # now check if we have a match - if (bank.parameters["Lp"] * neighbor.parameters["Lp"] < 0.0) and (bank.parameters["active"] > -1) and (neighbor.parameters["active"] > -1): - # this harmless line implies that there is rationing in the model - value = min(abs(bank.parameters["Lp"]), abs(neighbor.parameters["Lp"])) - - if bank.parameters["Lp"] > 0.0: # bank has excess liquidity - # add transactions - bank.add_transaction("L", int(bank.identifier), int(neighbor.identifier), value, interest, maturity, timeOfDefault) - neighbor.add_transaction("L", int(bank.identifier), int(neighbor.identifier), value, interest, maturity, timeOfDefault) - # update network of exposures - self.update_network_of_exposures(bank, neighbor, value) - # and change Lp accordingly - bank.parameters["Lp"] -= value - neighbor.parameters["Lp"] += value - if neighbor.parameters["Lp"] > 0.0: # neighbor has excess liquidity - # add transactions - bank.add_transaction("L", int(neighbor.identifier), int(bank.identifier), value, interest, maturity, timeOfDefault) - neighbor.add_transaction("L", int(neighbor.identifier), int(bank.identifier), value, interest, maturity, timeOfDefault) - # update network of exposures - self.update_network_of_exposures(neighbor, bank, value) - # and change Lp accordingly - neighbor.parameters["Lp"] -= value - bank.parameters["Lp"] += value - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # update_network_of_exposures - # ------------------------------------------------------------------------- - def update_network_of_exposures(self, nodeFrom, nodeTo, weight): - # add_weighted_edges_from overwrites the currently set weight - # hence, the weight has to be recalculated before setting it - current_weight = 0.0 - try: - current_weight = self.exposures[nodeFrom][nodeTo]['weight'] - except: - current_weight = 0.0 - - new_weight = current_weight + weight - self.exposures.add_weighted_edges_from([(nodeFrom, nodeTo, float(new_weight))]) - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # remove_inactive_bank() - # ------------------------------------------------------------------------- - def remove_inactive_bank(self, bank, time): - try: - # this removes the bank from the network of contracts and makes it impossible to trade with - # in the next update step - self.contracts.remove_node(bank) - except: # if the bank has been removed in the current update step, remove_node(bank) throws an exception - pass - - try: - self.remove_defaulted_loans(bank, time) - except: # if the bank has been removed in the current update step, remove_node(bank) throws an exception - pass - - try: - self.exposures.remove_node(bank) - except: - pass - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # def remove_defaulted_loans(bank): - # ------------------------------------------------------------------------- - def remove_defaulted_loans(self, bank, time): - try: - num_neighbors = len(self.exposures[bank]) - except: - num_neighbors = 0 - - if num_neighbors > 0: - logging.info(" time: %s: contagion originating from bank %s to %s neighbors", time, bank.identifier, num_neighbors) - - # if remove_node does not throw an exception, also remove the bank from the network of exposures - for neighbor in self.exposures[bank]: - loss = self.exposures[bank][neighbor]['weight'] - neighbor.reduce_banking_capital(loss) - neighbor.check_solvency("info") - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # liquidate_due_transactions - # ------------------------------------------------------------------------- - def liquidate_due_transactions(self, bank): - # the cheap and easy way to remove all links to a given node - # is to remove the node itself. - # TODO: this method does not work for interbank loans of longer maturities - try: - self.exposures.remove_node(bank) - except: - pass - # ------------------------------------------------------------------------- - -# -# HELPER ROUTINES -# - # ------------------------------------------------------------------------- - # __str()__ - # ------------------------------------------------------------------------- - def __str__(self): - text = "\n" - for node in self.contracts.nodes(): - text += " \n" - for edge in self.contracts.edges(): - text += " \n" - text += "\n" - - text += "\n" - for node in self.exposures.nodes(): - text += " \n" - for fromID, toID, edata in self.exposures.edges(data=True): - text += " \n" - text += "\n" - - return text - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # write_network_of_exposures - # ------------------------------------------------------------------------- - def write_network_of_exposures(self, time): - nx.write_edgelist(self.exposures.to_directed(), "exposures-" + self.identifier + "-" + str(time) + ".list") - # ------------------------------------------------------------------------- diff --git a/src/networks/full-3_banks.gexf b/src/networks/full-3_banks.gexf deleted file mode 100644 index fd819fc..0000000 --- a/src/networks/full-3_banks.gexf +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/src/runner.py b/src/runner.py index c331f97..abaa49c 100644 --- a/src/runner.py +++ b/src/runner.py @@ -1,10 +1,11 @@ #!/usr/bin/env python -# [SublimeLinter pep8-max-line-length:300] +# [SublimeLinter pep8-max-line-length:150] # -*- coding: utf-8 -*- """ black_rhino is a multi-agent simulator for financial network analysis -Copyright (C) 2012 Co-Pierre Georg (co-pierre.georg@keble.ox.ac.uk) +Copyright (C) 2016 Co-Pierre Georg (co-pierre.georg@keble.ox.ac.uk) +Pawel Fiedor (pawel@fiedor.eu) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,8 +21,9 @@ """ from src.updater import Updater -from src.shock import Shock from abm_template.src.baserunner import BaseRunner +from src.measurement import Measurement +from src.shock import Shock # ------------------------------------------------------------------------- # @@ -31,68 +33,128 @@ class Runner(BaseRunner): - # from environment import Environment - + # # # VARIABLES # + # identifier = "" - num_simulations = 0 + num_sweeps = 0 + current_step = 0 + # # # METHODS # + # + # ------------------------------------------------------------------------- # __init__ # ------------------------------------------------------------------------- - def __init__(self): - pass + def __init__(self, environment): + self.initialize(environment) # ------------------------------------------------------------------------- + # ------------------------------------------------------------------------- + # get_identifier + # ------------------------------------------------------------------------- def get_identifier(self): return self.identifier + # ------------------------------------------------------------------------- + + # ------------------------------------------------------------------------- + # set_identifier + # ------------------------------------------------------------------------- def set_identifier(self, _value): - """ - Class variables: identifier - Local variables: _identifier - """ super(Runner, self).set_identifier(_value) + # ------------------------------------------------------------------------- + + # ------------------------------------------------------------------------- + # get_num_sweeps + # ------------------------------------------------------------------------- + def get_num_sweeps(self): + return self.num_sweeps + # ------------------------------------------------------------------------- - def get_num_simulations(self): - return self.num_simulations - def set_num_simulations(self, _value): - """ - Class variables: num_simulations - Local variables: _num_simulations - """ - super(Runner, self).set_num_simulaitons(_value) + # ------------------------------------------------------------------------- + # set_num_sweeps + # ------------------------------------------------------------------------- + def set_num_sweeps(self, _value): + super(Runner, self).set_num_sweeps(_value) + # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- # initialize() # ------------------------------------------------------------------------- def initialize(self, environment): self.identifier = environment.identifier - self.num_simulations = environment.static_parameters["numSweeps"] - self.environment = environment - self.updater = Updater(self.environment) - self.shocker = Shock() + self.num_sweeps = int(environment.num_sweeps) + self.updater = Updater(environment) # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- # do_run # ------------------------------------------------------------------------- - def do_run(self, measurement, debug): + def do_run(self, environment): # loop over all time steps and do the updating - for i in range(self.num_simulations): + # We initialise the measurement class for writing outputs to csv + # measurement = Measurement("Measurement", environment, self, {1: ["Step", "static", "self.runner.current_step"], + # 2: ["Deposits", "dynamic", "self.environment.households[0].get_account", ["deposits"]]}, "TestMeasurement.csv") + measurement = Measurement(environment, self) + # And open the output file + measurement.open_file() + # We start the shock class as well + shock_class = Shock() + # For each update step + for i in range(self.num_sweeps): + # Do the shock: + # First we check if the shock occurs at the current sweep + # Then we run the shock procedure at the start of the update + for shock in environment.shocks: + if int(shock[0]) <= i+1 and int(shock[1]) >= i+1: + shock_class.do_shock(environment, i, shock[2], "start") # the update step - self.updater.do_update(self.environment, i, debug) - - # check if there is a shock at the current time step - if (int(self.environment.get_state(i).static_parameters["shockType"]) != 0): - self.shocker.do_shock(self.environment, int(i)) - self.environment.get_state(i).static_parameters["shockType"] = 0 - - # do the measurement - measurement.do_measurement(self.environment.banks) + # append current step, this is mostly for measurements + self.current_step = i + # do the actual update + self.updater.do_update(environment, i) + # write the state of the system + measurement.write_to_file() + # Do the shock (revert the shock if necessary): + # First we check if the shock occurs at the current sweep + # Then we run the shock procedure at the end of the update + for shock in environment.shocks: + if int(shock[0]) <= i+1 and int(shock[1]) >= i+1: + shock_class.do_shock(environment, i, shock[2], "end") + # HELPER, to be removed in production + # for firm in environment.households: + # print(firm) + # print(environment.households[0]) + # print(environment.firms[0]) + # capital = 0.0 + # for tranx in environment.firms[0].accounts: + # if tranx.type_ == "capital" and tranx.from_ == environment.firms[0]: + # capital = capital + tranx.amount + # if tranx.type_ == "capital" and tranx.to == environment.firms[0]: + # capital = capital - tranx.amount + # print(environment.firms[0].get_account("deposits")+capital-environment.firms[0].get_account("loans")) + # capital = 0.0 + # for tranx in environment.firms[1].accounts: + # if tranx.type_ == "capital" and tranx.from_ == environment.firms[1]: + # capital = capital + tranx.amount + # if tranx.type_ == "capital" and tranx.to == environment.firms[1]: + # capital = capital - tranx.amount + # print(environment.firms[1].get_account("deposits")+capital-environment.firms[1].get_account("loans")) + # capital = 0.0 + # for tranx in environment.firms[2].accounts: + # if tranx.type_ == "capital" and tranx.from_ == environment.firms[2]: + # capital = capital + tranx.amount + # if tranx.type_ == "capital" and tranx.to == environment.firms[2]: + # capital = capital - tranx.amount + # print(environment.firms[2].get_account("deposits")+capital-environment.firms[2].get_account("loans")) + print(environment.banks[0]) + # print(environment.firms[0]) + # Close the output file at the end of the simulation + measurement.close_file() # ------------------------------------------------------------------------ diff --git a/src/shock.py b/src/shock.py index 8f4a76b..3b3f78d 100644 --- a/src/shock.py +++ b/src/shock.py @@ -1,10 +1,11 @@ #!/usr/bin/env python -# [SublimeLinter pep8-max-line-length:300] +# [SublimeLinter pep8-max-line-length:150] # -*- coding: utf-8 -*- """ black_rhino is a multi-agent simulator for financial network analysis -Copyright (C) 2012 Co-Pierre Georg (co-pierre.georg@keble.ox.ac.uk) +Copyright (C) 2016 Co-Pierre Georg (co-pierre.georg@keble.ox.ac.uk) +Pawel Fiedor (pawel@fiedor.eu) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,54 +20,94 @@ along with this program. If not, see . """ +from abm_template.src.baseshock import BaseShock +import random import logging -""" - class Shock -""" +# ------------------------------------------------------------------------- +# class Shock +# ------------------------------------------------------------------------- -class Shock(object): +class Shock(BaseShock): + # # # VARIABLES # + # + # # # METHODS # - def do_shock(self, environment, time): - largest_exposure = 0.0 - largest_bank = environment.banks[0] - - shock_type = int(environment.get_state(time).shockType) - logging.info(" shock of type %s executed at time %s", shock_type, time) - if shock_type == 1: - # select the largest banks in terms of interbank exposures - largest_bank = self.find_largest_bank(environment) - # now send the largest bank into default - largest_bank.reduce_banking_capital(10.0) - largest_bank.check_solvency("info", time) - - if shock_type == 2: - # select the largest banks in terms of interbank exposures - largest_bank = self.find_largest_bank(environment) - # now send the largest bank into default - largest_bank.parameters["Lp"] = -10.0 - # ------------------------------------------------------------------------- + # # ------------------------------------------------------------------------- - # find_largest_bank() - # this routine finds the largest bank in terms of interbank exposure + # do_shock(environment, time, step) + # This is the main wrapper function for the shocks + # Here we specify what shocks are doing to the environment at the + # beginning and the end (step) of the affected sweeps + # Shocks are distinguished by the shock_type saved in the environment's + # variables, these are strings for our purposes. # ------------------------------------------------------------------------- - def find_largest_bank(self, environment): - largest_exposure = 0.0 - largest_bank = environment.banks[0] - for bank in environment.network.exposures.nodes(): - exposure = 0.0 - for neighbor in environment.network.exposures[bank]: - exposure += environment.network.exposures[bank][neighbor]['weight'] - if (exposure > largest_exposure): # we have a new largest bank - largest_exposure = exposure - largest_bank = bank - return largest_bank + def do_shock(self, environment, time, shock_type, step): + # Send a logging message so we know it happened + logging.info(" shock of type %s executed at time %s", shock_type, time) + # Then we check the shock type + # This shock changes the endwoment of labour of all + # households temporarily + if shock_type == "labour": + # And run the shock for the beginning of the step + # This is usually changing the environment to the + # state of emergency + if step == "start": + for household in environment.households: + household.labour = 12 + # And run the things at the end of the step + # This is usually for reverting to the original state + if step == "end": + for household in environment.households: + household.labour = 24 + # This shock changes the propensity to save of all households + # temporarily, making them save more duing the shock (consume less) + if shock_type == "savings": + if step == "start": + for household in environment.households: + household.propensity_to_save = 0.6 + if step == "end": + for household in environment.households: + household.propensity_to_save = 0.4 + # This shock changes the total factor productivity parameter + # in the C-D production function, temporarily making production + # much less efficient, simulating malfunctions in the equipment, + # mismanagement of labour and capital, or some external crisis + if shock_type == "productivity": + if step == "start": + for firm in environment.firms: + firm.total_factor_productivity = 0.5 + if step == "end": + for firm in environment.firms: + firm.total_factor_productivity = 1.8 + # This shock changes the elasticities within the C-D production + # function, simulating a shift in the production technology + if shock_type == "elasticity": + if step == "start": + for firm in environment.firms: + firm.labour_elasticity = 0.7 + firm.capital_elasticity = 0.3 + if step == "end": + for firm in environment.firms: + firm.labour_elasticity = 0.3 + firm.capital_elasticity = 0.7 + # This shock changes the interest rates charged on loans and + # deposits, simulating a banking shift + if shock_type == "interests": + if step == "start": + for bank in environment.banks: + bank.interest_rate_loans = 0.07 + bank.interest_rate_deposits = 0.03 + if step == "end": + for bank in environment.banks: + bank.interest_rate_loans = 0.0 + bank.interest_rate_deposits = 0.0 # ------------------------------------------------------------------------- diff --git a/src/tests.py b/src/tests.py deleted file mode 100644 index f19df53..0000000 --- a/src/tests.py +++ /dev/null @@ -1,1873 +0,0 @@ -#!/usr/bin/env python -# [SublimeLinter pep8-max-line-length:300] -# -*- coding: utf-8 -*- - -""" -black_rhino is a multi-agent simulator for financial network analysis -Copyright (C) 2012 Co-Pierre Georg (co-pierre.georg@keble.ox.ac.uk) - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, version 3 of the License. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -""" - -import logging - -# ------------------------------------------------------------------------- -# class Tests -# ------------------------------------------------------------------------- - - -class Tests(object): - # - # VARIABLES - # - - # - # METHODS - # - - # ------------------------------------------------------------------------- - # __init__ - # ------------------------------------------------------------------------- - def __init__(self): - pass - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # print_info(text) - # ------------------------------------------------------------------------- - def print_info(self, text): - print '##############################################################################\n' - print text - print '##############################################################################\n' - # ------------------------------------------------------------------------- - -# ------------------------------------------------------------------------- -# TESTS FOR BANK.PY -# ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # bank__get_parameters_from_file - # ------------------------------------------------------------------------- - - def bank__get_parameters_from_file(self, args): - import os - from src.bank import Bank - from src.environment import Environment # needed for the bankDirectory - - text = "This test checks bank.get_parameters_from_file \n" - text += " XXX \n" - text += " XXX tricky one, havent tried yet\n" - text += " XXX \n" - self.print_info(text) - # - # INITIALIZATION - # - environment_directory = str(args[1]) - identifier = str(args[2]) - log_directory = str(args[3]) - - # Configure logging parameters so we get output while the program runs - logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', filename=log_directory + identifier + ".log", level=logging.INFO) - logging.info('START logging for test bank__get_parameters_from_file in run: %s', environment_directory + identifier + ".xml") - - # Construct bank filename - environment = Environment(environment_directory, identifier) - # environment.initialize(environment_directory, identifier) - # get the bankDirectory from the environment - bankDirectory = environment.static_parameters["bankDirectory"] - # and loop over all banks in the directory - listing = os.listdir(bankDirectory) - bankFilename = bankDirectory + listing[0] - - # generate the bank - bank = Bank() - bank.initialize_standard_bank() - - # - # TEST CODE - # - - # tricky one, havent tried yet - - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # bank__apply_sifi_surcharge - # ------------------------------------------------------------------------- - - def bank__apply_sifi_surcharge(self, args): - import os - from src.bank import Bank - from src.environment import Environment # needed for the bankDirectory - - text = "This test checks bank.apply_sifi_surcharge \n" - text += " XXX \n" - text += " XXX tricky one, havent tried yet\n" - text += " XXX \n" - self.print_info(text) - - # - # INITIALIZATION - # - environment_directory = str(args[1]) - identifier = str(args[2]) - log_directory = str(args[3]) - - # Configure logging parameters so we get output while the program runs - logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', filename=log_directory + identifier + ".log", level=logging.INFO) - logging.info('START logging for test bank__apply_sifi_surcharge in run: %s', environment_directory + identifier + ".xml") - - # Construct bank filename - environment = Environment(environment_directory, identifier) - # environment.initialize(environment_directory, identifier) - # get the bankDirectory from the environment - bankDirectory = environment.static_parameters["bankDirectory"] - # and loop over all banks in the directory - listing = os.listdir(bankDirectory) - bankFilename = bankDirectory + listing[0] - - # generate the bank - bank = Bank() - bank.initialize_standard_bank() - - # - # TEST CODE - # - - # tricky one, havent tried yet - - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # bank__update_maturity - # ------------------------------------------------------------------------- - def bank__update_maturity(self, args): - import os - from src.bank import Bank - from src.environment import Environment # needed for the bankDirectory - - text = "This test checks bank.update_maturity() \n" - text += " It is successfull if the maturity of all transactions is reduced by one \n" - text += " when the bank is printed the second time. \n" - text += " Note that for investments also the time of default has to be reduced by one.\n" - self.print_info(text) - - # - # INITIALIZATION - # - environment_directory = str(args[1]) - identifier = str(args[2]) - log_directory = str(args[3]) - - # Configure logging parameters so we get output while the program runs - logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', filename=log_directory + identifier + ".log", level=logging.INFO) - logging.info('START logging for test bank__update_maturity in run: %s', environment_directory + identifier + ".xml") - - # Construct bank filename - environment = Environment(environment_directory, identifier) - # environment.initialize(environment_directory, identifier) - # get the bankDirectory from the environment - bankDirectory = environment.static_parameters["bankDirectory"] - # and loop over all banks in the directory - listing = os.listdir(bankDirectory) - bankFilename = bankDirectory + listing[0] - - # generate the bank - bank = Bank() - bank.initialize_standard_bank() - - # - # TEST CODE - # - print bank - bank.update_maturity() - print bank - - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # bank__update_risk_aversion - # ------------------------------------------------------------------------- - def bank__update_risk_aversion(self, args): - from src.environment import Environment - from src.updater import Updater - - # - # INITIALIZATION - # - environment_directory = str(args[1]) - identifier = str(args[2]) - log_directory = str(args[3]) - - # Configure logging parameters so we get output while the program runs - logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', filename=log_directory + identifier + ".log", level=logging.INFO) - logging.info('START logging for test bank__update_risk_aversion in run: %s', environment_directory + identifier + ".xml") - - # - # TEST CODE - # - environment = Environment(environment_directory, identifier) - # environment.initialize(environment_directory, identifier) - # create a test environment with standardised banks - - # first test: a bank in t=0 defaults, check that risk aversion in t=1 increases - environment.banks[0].reduce_banking_capital(10.0) - environment.banks[0].check_solvency(environment.get_state(0), "info", 0) - print environment.get_state(0) - environment.banks[1].update_risk_aversion(environment.get_state(1), 1) - print environment.banks[1] - # second test: check that risk aversion in t=2 decreases - environment.banks[1].update_risk_aversion(environment.get_state(2), 2) - print environment.banks[1] - environment.banks[1].update_risk_aversion(environment.get_state(3), 3) - print environment.banks[1] - - # - # MEASUREMENT AND LOGGING - # - logging.info('FINISHED logging for test bank__update_risk_aversion in run: %s \n', environment_directory + identifier + ".xml") - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # bank__get_interest - # ------------------------------------------------------------------------- - def bank__get_interest(self, args): - import os - from src.bank import Bank - from src.environment import Environment # needed for the bankDirectory - - text = "This test checks bank.get_interest() \n" - text += " It is successfull if calculated interest equals the\n" - text += " difference for interest on assets and interest on liabilities. \n" - text += " Note that BC will not recieve any interest.\n" - self.print_info(text) - - # - # INITIALIZATION - # - environment_directory = str(args[1]) - identifier = str(args[2]) - log_directory = str(args[3]) - - # Configure logging parameters so we get output while the program runs - logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', filename=log_directory + identifier + ".log", level=logging.INFO) - logging.info('START logging for test bank__get_interest in run: %s', environment_directory + identifier + ".xml") - - # Construct bank filename - environment = Environment(environment_directory, identifier) - # environment.initialize(environment_directory, identifier) - # get the bankDirectory from the environment - bankDirectory = environment.static_parameters["bankDirectory"] - # and loop over all banks in the directory - listing = os.listdir(bankDirectory) - bankFilename = bankDirectory + listing[0] - - # generate the bank - bank = Bank() - bank.initialize_standard_bank() - - # - # TEST CODE - # - interestCalculated = 0.0 - interestAssets = 0.0 - interestLiabilities = 0.0 - print "Bank:" - print bank - - print "Transactions:" - for transaction in bank.accounts: - print transaction.transactionType, transaction.transactionValue, transaction.transactionInterest - if (transaction.transactionType == "I" or transaction.transactionType == "E" or transaction.transactionType == "rD"): # we have an asset - interestAssets += transaction.transactionValue*transaction.transactionInterest - else: # we have a liability - interestLiabilities -= transaction.transactionValue*transaction.transactionInterest - - for type in ["I", "E", "D", "rD", "LC", "L", "BC"]: - interestCalculated += bank.get_interest(type) - - print "Interest: " + str(interestCalculated) + " = " + str(interestAssets) + " + " + str(interestLiabilities) - # print transaction.transactionType, transaction.transactionValue, transaction.transactionInterest - - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # bank__liquidate_due_transactions - # ------------------------------------------------------------------------- - def bank__liquidate_due_transactions(self, args): - import os - from src.bank import Bank - from src.environment import Environment # needed for the bankDirectory - - text = "This test checks bank.liquidate_due_transactions() \n" - text += " It is successful if the maturity of the investments is 0 and \n" - text += " if the two investment the retrun their respective vaule \n" - text += " (which should be 200 in total for the standard bank). \n" - self.print_info(text) - - # - # INITIALIZATION - # - environment_directory = str(args[1]) - identifier = str(args[2]) - log_directory = str(args[3]) - - # Configure logging parameters so we get output while the program runs - logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', filename=log_directory + identifier + ".log", level=logging.INFO) - logging.info('START logging for test bank__liquidate_due_transactions in run: %s', environment_directory + identifier + ".xml") - - # Construct bank filename - environment = Environment(environment_directory, identifier) - # environment.initialize(environment_directory, identifier) - # get the bankDirectory from the environment - bankDirectory = environment.static_parameters["bankDirectory"] - # and loop over all banks in the directory - listing = os.listdir(bankDirectory) - bankFilename = bankDirectory + listing[0] - - # generate the bank - bank = Bank() - bank.initialize_standard_bank() - - # - # TEST CODE - # - for transaction in bank.accounts: - if (transaction.transactionType == "I"): - transaction.transactionMaturity = 0.0 # first lower maturity to 0 - print bank # and check maturity - - VolumeCalculated = 0.0 - print VolumeCalculated - - for type in ["I"]: # now apply the liquidate function and compare voulmes - VolumeCalculated += bank.liquidate_due_transactions(type) - print VolumeCalculated - - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # bank__get_new_deposits - # ------------------------------------------------------------------------- - def bank__get_new_deposits(self, args): - import os - from src.bank import Bank - from src.environment import Environment # needed for the bankDirectory - - text = "This test checks bank.get_new_deposits \n" - text += " It returns the change of the deposits of the bank (250). \n" - text += " With a scaleFactor of 0.02 this change should fluctuate \n" - text += " randomly between +5 and -5. \n" - self.print_info(text) - - # - # INITIALIZATION - # - environment_directory = str(args[1]) - identifier = str(args[2]) - log_directory = str(args[3]) - - # Configure logging parameters so we get output while the program runs - logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', filename=log_directory + identifier + ".log", level=logging.INFO) - logging.info('START logging for test bank__get_new_deposits in run: %s', environment_directory + identifier + ".xml") - - # Construct bank filename - environment = Environment(environment_directory, identifier) - # environment.initialize(environment_directory, identifier) - # get the bankDirectory from the environment - bankDirectory = environment.static_parameters["bankDirectory"] - # and loop over all banks in the directory - listing = os.listdir(bankDirectory) - bankFilename = bankDirectory + listing[0] - - # generate the bank - bank = Bank() - bank.initialize_standard_bank() - - # - # TEST CODE - # - DepositsChange = 0.0 - scaleFactor = 0.02 - - for transaction in bank.accounts: # print Deposits - if (transaction.transactionType == "D"): - print transaction.transactionType, transaction.transactionValue - - for type in ["D"]: # now apply the get_new_deposits function and print return value - DepositsChange += bank.get_new_deposits(scaleFactor) - print DepositsChange - - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # bank__transfer_required_deposits - # ------------------------------------------------------------------------- - def bank__transfer_required_deposits(self, args): - import os - from src.bank import Bank - from src.environment import Environment # needed for the bankDirectory - - text = "This test checks bank.transfer_required_deposits \n" - text += " first we delete the required deposits of the standard bank, \n" - text += " afterwards we calculate the new rD using the respective function. \n" - text += " for r=0.05 the output should be -12.5 \n" - self.print_info(text) - - # - # INITIALIZATION - # - environment_directory = str(args[1]) - identifier = str(args[2]) - log_directory = str(args[3]) - - # Configure logging parameters so we get output while the program runs - logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', filename=log_directory + identifier + ".log", level=logging.INFO) - logging.info('START logging for test bank__transfer_required_deposits in run: %s', environment_directory + identifier + ".xml") - - # Construct bank filename - environment = Environment(environment_directory, identifier) - # environment.initialize(environment_directory, identifier) - # get the bankDirectory from the environment - bankDirectory = environment.static_parameters["bankDirectory"] - # and loop over all banks in the directory - listing = os.listdir(bankDirectory) - bankFilename = bankDirectory + listing[0] - - # generate the bank - bank = Bank() - bank.initialize_standard_bank() - - # - # TEST CODE - # - for transaction in bank.accounts: - if (transaction.transactionType == "rD"): - transaction.transactionValue = 0.0 # first elimnate the rD of standard bank - print bank # and check bank - - ReqDep = 0.0 - print ReqDep - - for type in ["rD"]: # now apply the transfer_required_deposits and print new rD value - ReqDep += bank.transfer_required_deposits() - print ReqDep - - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # bank__reduce_banking_capital - # ------------------------------------------------------------------------- - def bank__reduce_banking_capital(self, args): - import os - from src.bank import Bank - from src.environment import Environment # needed for the bankDirectory - - text = "This test checks bank.reduce_banking_capital \n" - text += " It returns the reduced banking capital. \n" - text += " Suppose the banking capital of our standard bank is \n" - text += " reduced by 5, so the new BC will be 40 - 5 = 35 \n" - self.print_info(text) - - # - # INITIALIZATION - # - environment_directory = str(args[1]) - identifier = str(args[2]) - log_directory = str(args[3]) - - # Configure logging parameters so we get output while the program runs - logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', filename=log_directory + identifier + ".log", level=logging.INFO) - logging.info('START logging for test bank__reduce_banking_capital in run: %s', environment_directory + identifier + ".xml") - - # Construct bank filename - environment = Environment(environment_directory, identifier) - # environment.initialize(environment_directory, identifier) - # get the bankDirectory from the environment - bankDirectory = environment.static_parameters["bankDirectory"] - # and loop over all banks in the directory - listing = os.listdir(bankDirectory) - bankFilename = bankDirectory + listing[0] - - # generate the bank - bank = Bank() - bank.initialize_standard_bank() - - # - # TEST CODE - # - value = 5.0 - - for transaction in bank.accounts: - if (transaction.transactionType == "BC"): - print transaction.transactionValue - bank.reduce_banking_capital(value) - print transaction.transactionValue - - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # bank__check_solvency - # ------------------------------------------------------------------------- - def bank__check_solvency(self, args): - import os - from src.bank import Bank - from src.environment import Environment # needed for the bankDirectory - # from state import State - - text = "This test checks bank.check_solvency \n" - text += " Within this test the required_capital_ratio is set extremely \n" - text += " high so that our standard bank will fail to meet its \n" - text += " capital requirement. Subsequently 'active' will be set to -1 \n" - self.print_info(text) - - # - # INITIALIZATION - # - environment_directory = str(args[1]) - identifier = str(args[2]) - log_directory = str(args[3]) - - # Configure logging parameters so we get output while the program runs - logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', filename=log_directory + identifier + ".log", level=logging.INFO) - logging.info('START logging for test bank__check_solvency in run: %s', environment_directory + identifier + ".xml") - - # Construct bank filename - environment = Environment(environment_directory, identifier) - # environment.initialize(environment_directory, identifier) - # get the bankDirectory from the environment - bankDirectory = environment.static_parameters["bankDirectory"] - # and loop over all banks in the directory - listing = os.listdir(bankDirectory) - bankFilename = bankDirectory + listing[0] - - # generate the bank - bank = Bank() - bank.initialize_standard_bank() - - # - # TEST CODE - # - - # for the standard Bank BC/ I = 40 / 200 = 0.2 - # in order to make sure that the bank will not meet their requirements - # we set requiredCapitalRatio = 0.9 - # state = State() - environment.static_parameters["requiredCapitalRatio"] = 0.9 - required_capital_ratio = environment.static_parameters["requiredCapitalRatio"] - print environment.print_state() - - # for bank in bankDirectory: - bank.check_solvency(environment, "info", 0) - print bank - - # and check if "active" is now -1 - - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # bank__check_liquidity - # ------------------------------------------------------------------------- - def bank__check_liquidity(self, args): - import os - from src.bank import Bank - from src.environment import Environment # needed for the bankDirectory - - text = "This test checks bank.check_liquidity \n" - text += " Within this test Q is set < 0.0 so that \n" - text += " our standard bank will become illiquid. \n" - text += " Subsequently 'active' will be set to -1 \n" - self.print_info(text) - - # - # INITIALIZATION - # - environment_directory = str(args[1]) - identifier = str(args[2]) - log_directory = str(args[3]) - - # Configure logging parameters so we get output while the program runs - logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', filename=log_directory + identifier + ".log", level=logging.INFO) - logging.info('START logging for test bank__check_liquidity in run: %s', environment_directory + identifier + ".xml") - - # Construct bank filename - environment = Environment(environment_directory, identifier) - # environment.initialize(environment_directory, identifier) - # get the bankDirectory from the environment - bankDirectory = environment.static_parameters["bankDirectory"] - # and loop over all banks in the directory - listing = os.listdir(bankDirectory) - bankFilename = bankDirectory + listing[0] - - # generate the bank - bank = Bank() - bank.initialize_standard_bank() - - # - # TEST CODE - # - - # nom we just assume that - bank.parameters["Q"] = -1.0 - - # for bank in bankDirectory: - bank.check_liquidity() - - print bank - # and check if "active" is now -1 - - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # bank__calculate_liquidity_demand - # ------------------------------------------------------------------------- - def bank__calculate_liquidity_demand(self, args): - import os - from src.bank import Bank - from src.environment import Environment # needed for the bankDirectory - - text = "This test checks bank.calculate_liquidity_demand \n" - text += " It will calculate the liquidity demand for the standard bank according to its \n" - text += " parameters and the formulas used in bank.calculate_liquidity_demand \n" - text += " As a reminder the parameters for the standard bank are: \n" - text += "\n" - text += " gamma = 0.8 \n" - text += " lamb = 0.5 \n" - text += " V = 250 \n" - text += " Q = 0.0 \n" - text += "\n" - text += " The formulas for bank.calculate_liquidity_demand are: \n" - text += "\n" - text += " Ip = gamma * lamb * V = 0.8 * 0,5 * 250.0 = 100.0 \n" - text += " Ep = gamma * (1.0 - lamb) * V = 0.8 * 0,5 * 250.0 = 100.0 \n" - text += " Lp = Q - ((Ip-I) + (Ep-E)) = 0.0 - ((100-200) + (90-100)) \n" - text += "\n" - text += " The result should be Lp = 90.0 \n" - self.print_info(text) - - # - # INITIALIZATION - # - environment_directory = str(args[1]) - identifier = str(args[2]) - log_directory = str(args[3]) - - # Configure logging parameters so we get output while the program runs - logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', filename=log_directory + identifier + ".log", level=logging.INFO) - logging.info('START logging for test bank__calculate_liquidity_demand in run: %s', environment_directory + identifier + ".xml") - - # Construct bank filename - environment = Environment(environment_directory, identifier) - # environment.initialize(environment_directory, identifier) - # get the bankDirectory from the environment - bankDirectory = environment.static_parameters["bankDirectory"] - # and loop over all banks in the directory - listing = os.listdir(bankDirectory) - bankFilename = bankDirectory + listing[0] - - # generate the bank - bank = Bank() - bank.initialize_standard_bank() - - # - # TEST CODE - # - bank.calculate_liquidity_demand() - print "Ip= " + str(bank.parameters["Ip"]) + "; Ep= " + str(bank.parameters["Ep"]) + "; I= " + str(bank.get_account("I")) + "; E= " + str(bank.get_account("E")) - print "Lp= " + str(bank.parameters["Lp"]) - - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # bank__get_central_bank_liquidity - # ------------------------------------------------------------------------- - def bank__get_central_bank_liquidity(self, args): - import os - from src.bank import Bank - from src.environment import Environment # needed for the bankDirectory - # from state import State - - text = "This test checks bank.get_central_bank_liquidity \n" - text += " First we have to set Lp < 0.0 in order to set the bank in a position of \n" - text += " liquidity shortage (Lp = -50.0). Now suppose that the CB regards only 0.8 of the \n" - text += " bank's assets as safe (collateralQuality = 0.8). For I = 200.0 the bank should get \n" - text += " enough liquidity (up to 160.0). As a result LC should increase to LC = 60.0 and \n" - text += " Lp should be 0 again. \n" - self.print_info(text) - - # - # INITIALIZATION - # - environment_directory = str(args[1]) - identifier = str(args[2]) - log_directory = str(args[3]) - - # Configure logging parameters so we get output while the program runs - logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', filename=log_directory + identifier + ".log", level=logging.INFO) - logging.info('START logging for test bank__get_central_bank_liquidity in run: %s', environment_directory + identifier + ".xml") - - # Construct bank filename - environment = Environment(environment_directory, identifier) - # environment.initialize(environment_directory, identifier) - # get the bankDirectory from the environment - bankDirectory = environment.static_parameters["bankDirectory"] - # and loop over all banks in the directory - listing = os.listdir(bankDirectory) - bankFilename = bankDirectory + listing[0] - - # generate the bank - bank = Bank() - bank.initialize_standard_bank() - - # - # TEST CODE - # - # state = State() - environment.static_parameters["collateralQuality"] = 0.8 # make sure that the lokal values are used - environment.static_parameters["rb"] = 0.02 - - # now suppose we are in a liquidity shortage of Lp = -50.0 - bank.parameters["Lp"] = -50.0 - - bank.get_central_bank_liquidity(environment) - print bank - print "Lp= " + str(bank.parameters["Lp"]) - - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # bank__liquidate_assets - # ------------------------------------------------------------------------- - def bank__liquidate_assets(self, args): - import os - from src.bank import Bank - from src.environment import Environment # needed for the bankDirectory - # from state import State - - text = "This test checks bank.liquidate_assets \n" - text += " First we have to set Lp < 0.0 in order to set the bank in a position of \n" - text += " liquidity shortage (Lp = -10.0). Suppose that the liquidity discount factor will be 0.05 \n" - text += " and for Ip we have to assume e.g. 250 in order to trigger the loop in the code. \n" - text += " The requiredCapitalRatio is = 0.08 for the standard bank \n" - text += " \n" - text += " Under the current circumstances (bug?) this will lead to Lp = -160.0 in the first loop \n" - text += " and liquidation price of 0.9608. Both assets have to be sold now and the \n" - text += " BC will be decreased from 40.0 to 32.16. Lp will now be 32. 16 \n" - self.print_info(text) - - # - # INITIALIZATION - # - environment_directory = str(args[1]) - identifier = str(args[2]) - log_directory = str(args[3]) - - # Configure logging parameters so we get output while the program runs - logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', filename=log_directory + identifier + ".log", level=logging.INFO) - logging.info('START logging for test bank__liquidate_assets in run: %s', environment_directory + identifier + ".xml") - - # Construct bank filename - environment = Environment(environment_directory, identifier) - # environment.initialize(environment_directory, identifier) - # get the bankDirectory from the environment - bankDirectory = environment.static_parameters["bankDirectory"] - # and loop over all banks in the directory - listing = os.listdir(bankDirectory) - bankFilename = bankDirectory + listing[0] - - # generate the bank - bank = Bank() - bank.initialize_standard_bank() - - # - # TEST CODE - # - # state = State() - environment.static_parameters["liquidationDiscountFactor"] = 0.05 - - bank.parameters["Lp"] = - 10.0 # let's assume for argument sake that banks are short in liquidity Lp = -10.0 - bank.parameters["Ip"] = 250.0 # but want to increase their planned investment Ip = 250.0 - # the requiredCapitalRatio is = 0.08 - - bank.liquidate_assets(200.0, 200.0, environment, "info", 0) - print bank - - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # bank__transfer_investments - # ------------------------------------------------------------------------- - def bank__transfer_investments(self, args): - import os - from src.bank import Bank - from src.environment import Environment # needed for the bankDirectory - # from state import State - - text = "This test checks bank.transfer_investments \n" - text += " Note that you should first look at bank.calculate_optimal_investment_volume to better) \n" - text += " understand this. If we assume pReal = 0.98 than we will will receive lamb = 0.3014 and V = 13.5635 \n" - text += " Moreover, for our standard bank the avaiable liquditiy Q should be roughly 105 (reserves + interest gains) \n" - text += " The requiredCapitalRatio is = 0.08 for the standard bank \n" - self.print_info(text) - - # - # INITIALIZATION - # - environment_directory = str(args[1]) - identifier = str(args[2]) - log_directory = str(args[3]) - - # Configure logging parameters so we get output while the program runs - logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', filename=log_directory + identifier + ".log", level=logging.INFO) - logging.info('START logging for test bank__transfer_investments in run: %s', environment_directory + identifier + ".xml") - - # Construct bank filename - environment = Environment(environment_directory, identifier) - # environment.initialize(environment_directory, identifier) - # get the bankDirectory from the environment - bankDirectory = environment.static_parameters["bankDirectory"] - # and loop over all banks in the directory - listing = os.listdir(bankDirectory) - bankFilename = bankDirectory + listing[0] - - # generate the bank - bank = Bank() - bank.initialize_standard_bank() - - # - # TEST CODE - # - # state = State() - bank.parameters["pReal"] = 0.98 # we have to assign this value for pReal so that we recieve - # lamb = 0.3014 and V = 13.5635 - # see also bank__calculate_optimal_investment_volume for details - bank.parameters["Q"] = 105.0 # Q should be roughly 105 (reserves + interest gains) - bank.parameters["averageTransactionSize"] = 200.0 # For our standard Bank the averageTransactionSize is 200 - - bank.transfer_investments(environment) - - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # bank__transfer_excess_reserves - # ------------------------------------------------------------------------- - def bank__transfer_excess_reserves(self, args): - import os - from src.bank import Bank - from src.environment import Environment # needed for the bankDirectory - - text = "This test checks bank.transfer_excess_reserves \n" - text += " It is successful if the excess reserves of our standard bank are increase. \n" - text += " Under the assumption that our banks faces a liquidity surplus (available volume) \n" - text += " of Q = +100.0 the balance sheet of our bank should get an additional transaction-position \n" - text += " of E with a value of 100.0 \n" - self.print_info(text) - - # - # INITIALIZATION - # - environment_directory = str(args[1]) - identifier = str(args[2]) - log_directory = str(args[3]) - - # Configure logging parameters so we get output while the program runs - logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', filename=log_directory + identifier + ".log", level=logging.INFO) - logging.info('START logging for test bank__transfer_excess_reserves in run: %s', environment_directory + identifier + ".xml") - - # Construct bank filename - environment = Environment(environment_directory, identifier) - # environment.initialize(environment_directory, identifier) - # get the bankDirectory from the environment - bankDirectory = environment.static_parameters["bankDirectory"] - # and loop over all banks in the directory - listing = os.listdir(bankDirectory) - bankFilename = bankDirectory + listing[0] - - # generate the bank - bank = Bank() - bank.initialize_standard_bank() - - # - # TEST CODE - # - bank.parameters["Q"] = 100.0 # assume a available volume of Q = 100 (in order to trigger the mehthode) - - bank.transfer_excess_reserves() # call the mehtode - - print bank # check result - - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # bank__calculate_optimal_investment_volume - # ------------------------------------------------------------------------- - def bank__calculate_optimal_investment_volume(self, args): - import os - from src.bank import Bank - from src.environment import Environment # needed for the bankDirectory - # from state import State - - text = "This test checks bank.calculate_optimal_investment_volume \n" - text += " First of all we have to change pReal in order to ensure that mu will be >0 so that \n" - text += " lamb will also be >0 and <1 . Now for our standard bank mu will now be 0.0197, sigma2= 0.0105, \n" - text += " lamb = 0.3014 and V = 13.5635. As we have no leverage ratio for our standard Bank the \n" - text += " result should be V = 13.5635 \n" - self.print_info(text) - - # - # INITIALIZATION - # - environment_directory = str(args[1]) - identifier = str(args[2]) - log_directory = str(args[3]) - - # Configure logging parameters so we get output while the program runs - logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', filename=log_directory + identifier + ".log", level=logging.INFO) - logging.info('START logging for test bank__calculate_optimal_investment_volume in run: %s', environment_directory + identifier + ".xml") - - # Construct bank filename - environment = Environment(environment_directory, identifier) - # environment.initialize(environment_directory, identifier) - # get the bankDirectory from the environment - bankDirectory = environment.static_parameters["bankDirectory"] - # and loop over all banks in the directory - listing = os.listdir(bankDirectory) - bankFilename = bankDirectory + listing[0] - - # generate the bank - bank = Bank() - bank.initialize_standard_bank() - - # - # TEST CODE - # - # state = State() - bank.parameters["pReal"] = 0.98 # we have to assign this value for pReal so that we recieve - # lamb = 0.3014 and V = 13.5635 - bank.calculate_optimal_investment_volume(environment) - print "optimal investment volume = " + str(bank.parameters["V"]) - - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # bank__initialize_transactions - # ------------------------------------------------------------------------- - def bank__initialize_transactions(self, args): - import os - from src.bank import Bank - from src.environment import Environment # needed for the bankDirectory - # from state import State - - text = "This test checks bank.initialize_transactions \n" - text += " This method is supposed to create the transactions of banks in BlackRihno. \n" - text += " The simplest way to test it is to initialize the standard bank, then to call the \n" - text += " method and check whether transactions have been added to our standard bank. \n" - text += " \n" - self.print_info(text) - - # - # INITIALIZATION - # - environment_directory = str(args[1]) - identifier = str(args[2]) - log_directory = str(args[3]) - - # Configure logging parameters so we get output while the program runs - logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', filename=log_directory + identifier + ".log", level=logging.INFO) - logging.info('START logging for test bank__initialize_transactions in run: %s', environment_directory + identifier + ".xml") - - # Construct bank filename - environment = Environment(environment_directory, identifier) - # environment.initialize(environment_directory, identifier) - # get the bankDirectory from the environment - bankDirectory = environment.static_parameters["bankDirectory"] - # and loop over all banks in the directory - listing = os.listdir(bankDirectory) - bankFilename = bankDirectory + listing[0] - - # generate the bank - bank = Bank() - bank.initialize_standard_bank() - - # - # TEST CODE - # - # print bank - # state = State() - environment.static_parameters["successProbabilityFirms"] = 0.5 # we assign this values in order to check if the - environment.static_parameters["firmLoanMaturity"] = 100.0 # random function works - - print bank - bank.initialize_transactions(environment) - print bank - - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # bank__get_initial_banking_capital - # ------------------------------------------------------------------------- - def bank__get_initial_banking_capital(self, args): - import os - from src.bank import Bank - from src.environment import Environment # needed for the bankDirectory - # from state import State - - text = "This test checks bank.get_initial_banking_capital \n" - text += " This method is supposed to create a return value which will be used as a. \n" - text += " initial banking capital value. As the required capital ratio is 0.9 and \n" - text += " our Investments are 200 the result should be 225.0 \n" - text += " \n" - self.print_info(text) - - # - # INITIALIZATION - # - environment_directory = str(args[1]) - identifier = str(args[2]) - log_directory = str(args[3]) - - # Configure logging parameters so we get output while the program runs - logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', filename=log_directory + identifier + ".log", level=logging.INFO) - logging.info('START logging for test bank__get_initial_banking_capital in run: %s', environment_directory + identifier + ".xml") - - # Construct bank filename - environment = Environment(environment_directory, identifier) - # environment.initialize(environment_directory, identifier) - # get the bankDirectory from the environment - bankDirectory = environment.static_parameters["bankDirectory"] - # and loop over all banks in the directory - listing = os.listdir(bankDirectory) - bankFilename = bankDirectory + listing[0] - - # generate the bank - bank = Bank() - bank.initialize_standard_bank() - - # - # TEST CODE - # - # state = State() - environment.static_parameters["requiredCapitalRatio"] = 0.9 - required_capital_ratio = environment.static_parameters["requiredCapitalRatio"] - - initial_banking_capital = 0.0 - print initial_banking_capital - - for type in ["I"]: # now apply the initial_banking_capital and print the new value - initial_banking_capital += bank.get_initial_banking_capital(required_capital_ratio) - print initial_banking_capital - - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # bank__get_account - # ------------------------------------------------------------------------- - def bank__get_account(self, args): - import os - from src.bank import Bank - from src.environment import Environment # needed for the bankDirectory - - text = "This test checks bank.get_account \n" - text += " The purpose of this method is to establish an account for our bank which contains \n" - text += " all kinds of assets and liabilities. The method simply adds all kinds of assets \n" - text += " and stores them in one volume. As our Banks holds 300.0 assets (2* I = 100, E = 90, D = 250) \n" - text += " and 300 liabilites the total volume of our account should be 600.0 \n" - self.print_info(text) - - # - # INITIALIZATION - # - environment_directory = str(args[1]) - identifier = str(args[2]) - log_directory = str(args[3]) - - # Configure logging parameters so we get output while the program runs - logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', filename=log_directory + identifier + ".log", level=logging.INFO) - logging.info('START logging for test bank__get_account in run: %s', environment_directory + identifier + ".xml") - - # Construct bank filename - environment = Environment(environment_directory, identifier) - # environment.initialize(environment_directory, identifier) - # get the bankDirectory from the environment - bankDirectory = environment.static_parameters["bankDirectory"] - # and loop over all banks in the directory - listing = os.listdir(bankDirectory) - bankFilename = bankDirectory + listing[0] - - # generate the bank - bank = Bank() - bank.initialize_standard_bank() - - # - # TEST CODE - # - - account = 0.0 # counting all types in account together - print bank # and checking how much is the total - # volume of the account - for type in ["I", "E", "rD", "BC", "D", "LC", "L"]: - if type == "I": - account += bank.get_account(type) - print "I = " + str(account) - if type == "E": - account += bank.get_account(type) - print "I+E = " + str(account) - if type == "rD": - account += bank.get_account(type) - print "I+E+rD = " + str(account) - if type == "BC": - account += bank.get_account(type) - print "I+E+rD+BC = " + str(account) - if type == "D": - account += bank.get_account(type) - print "I+E+rD+BC+D = " + str(account) - if type == "LC": - account += bank.get_account(type) - print "I+E+rD+BC+D+LC = " + str(account) - if type == "L": - account += bank.get_account(type) - print "I+E+rD+BC+D+LC+L = " + str(account) - - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # bank__get_account_num_transactions - # ------------------------------------------------------------------------- - def bank__get_account_num_transactions(self, args): - import os - from src.bank import Bank - from src.environment import Environment # needed for the bankDirectory - - text = "This test checks bank.get_account_num_transactions \n" - text += " The purpose of this method is to count the numbers of transaction for \n" - text += " accounts banks hold. Our standard bank has 7 transactions by default. \n" - text += " (2* I + E + rD + BC + D + LC). As long as our bank does not have e.g. \n" - text += " an L the number of transactions should be 7.0 \n" - self.print_info(text) - - # - # INITIALIZATION - # - environment_directory = str(args[1]) - identifier = str(args[2]) - log_directory = str(args[3]) - - # Configure logging parameters so we get output while the program runs - logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', filename=log_directory + identifier + ".log", level=logging.INFO) - logging.info('START logging for test bank__get_account_num_transactions in run: %s', environment_directory + identifier + ".xml") - - # Construct bank filename - environment = Environment(environment_directory, identifier) - # environment.initialize(environment_directory, identifier) - # get the bankDirectory from the environment - bankDirectory = environment.static_parameters["bankDirectory"] - # and loop over all banks in the directory - listing = os.listdir(bankDirectory) - bankFilename = bankDirectory + listing[0] - - # generate the bank - bank = Bank() - bank.initialize_standard_bank() - - # - # TEST CODE - # - - num_transactions = 0.0 # counting all types in account together - # and checking if the number of transaction - # is increasing by one - for type in ["I", "E", "rD", "BC", "D", "LC", "L"]: - if type == "I": - num_transactions += bank.get_account_num_transactions(type) - print "I = " + str(num_transactions) - if type == "E": - num_transactions += bank.get_account_num_transactions(type) - print "I+E = " + str(num_transactions) - if type == "rD": - num_transactions += bank.get_account_num_transactions(type) - print "I+E+rD = " + str(num_transactions) - if type == "BC": - num_transactions += bank.get_account_num_transactions(type) - print "I+E+rD+BC = " + str(num_transactions) - if type == "D": - num_transactions += bank.get_account_num_transactions(type) - print "I+E+rD+BC+D = " + str(num_transactions) - if type == "LC": - num_transactions += bank.get_account_num_transactions(type) - print "I+E+rD+BC+D+LC = " + str(num_transactions) - if type == "L": - num_transactions += bank.get_account_num_transactions(type) - print "I+E+rD+BC+D+LC+L = " + str(num_transactions) - - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # bank__add_transaction - # ------------------------------------------------------------------------- - def bank__add_transaction(self, args): - import os - from src.bank import Bank - from src.environment import Environment # needed for the bankDirectory - - text = "This test checks bank.add_transaction \n" - text += " The most simple way to test this function is to assign an new \n" - text += " transaction to our bank. Therefore, lets just assign the following \n" - text += " transaction and check whether it has been added: \n" - text += ' (type = "D", fromID = -1, toID = bank.identifier, value = 10, \n' - text += " interest = 0.09, maturity = 0, timeOfDefault = -1) \n" - self.print_info(text) - - # - # INITIALIZATION - # - environment_directory = str(args[1]) - identifier = str(args[2]) - log_directory = str(args[3]) - - # Configure logging parameters so we get output while the program runs - logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', filename=log_directory + identifier + ".log", level=logging.INFO) - logging.info('START logging for test bank__add_transaction in run: %s', environment_directory + identifier + ".xml") - - # Construct bank filename - environment = Environment(environment_directory, identifier) - # environment.initialize(environment_directory, identifier) - # get the bankDirectory from the environment - bankDirectory = environment.static_parameters["bankDirectory"] - # and loop over all banks in the directory - listing = os.listdir(bankDirectory) - bankFilename = bankDirectory + listing[0] - - # generate the bank - bank = Bank() - bank.initialize_standard_bank() - - # - # TEST CODE - # - bank.add_transaction("D", -1, bank.identifier, 10, 0.09, 0, -1) - print bank - - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # bank__purge_accounts - # ------------------------------------------------------------------------- - def bank__purge_accounts(self, args): - import os - from src.bank import Bank - from src.environment import Environment # needed for the bankDirectory - - text = "This test checks bank.purge_accounts \n" - text += " Checking if after the purge_accounts the total amount \n" - text += " of transactions in the bank stays the same. \n" - self.print_info(text) - - # - # INITIALIZATION - # - environment_directory = str(args[1]) - identifier = str(args[2]) - log_directory = str(args[3]) - - # Configure logging parameters so we get output while the program runs - logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', filename=log_directory + identifier + ".log", level=logging.INFO) - logging.info('START logging for test bank__purge_accounts in run: %s', environment_directory + identifier + ".xml") - - # Construct bank filename - environment = Environment(environment_directory, identifier) - # environment.initialize(environment_directory, identifier) - # get the bankDirectory from the environment - bankDirectory = environment.static_parameters["bankDirectory"] - # and loop over all banks in the directory - listing = os.listdir(bankDirectory) - bankFilename = bankDirectory + listing[0] - - # generate the bank - bank = Bank() - bank.initialize_standard_bank() - - # - # TEST CODE - # - account = 0.0 - tranx = 0 - - for transaction in bank.accounts: - account = account + transaction.transactionValue - tranx = tranx + 1 - - print tranx - print account - - bank.add_transaction("LC", -1, bank.identifier, 0.0, 0.09, 0, -1) - - account = 0.0 - tranx = 0 - - for transaction in bank.accounts: - account = account + transaction.transactionValue - tranx = tranx + 1 - - print tranx - print account - - bank.purge_accounts() - - account = 0.0 - tranx = 0 - - for transaction in bank.accounts: - account = account + transaction.transactionValue - tranx = tranx + 1 - - print tranx - print account - - # for transaction in bank.accounts: - # if transaction.transactionType == "I": - # print account - # bank.purge_accounts() - # account += bank.accounts - # print account - - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # bank__change_deposits - # ------------------------------------------------------------------------- - - def bank__change_deposits(self, args): - import os - from src.bank import Bank - from src.environment import Environment # needed for the bankDirectory - - text = "This test checks bank.change_deposits \n" - text += " suppose that the change in depostits = 10.0 \n" - text += " then the new deposits should be 260.0 \n" - self.print_info(text) - - # - # INITIALIZATION - # - environment_directory = str(args[1]) - identifier = str(args[2]) - log_directory = str(args[3]) - - # Configure logging parameters so we get output while the program runs - logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', filename=log_directory + identifier + ".log", level=logging.INFO) - logging.info('START logging for test bank__change_deposits in run: %s', environment_directory + identifier + ".xml") - - # Construct bank filename - environment = Environment(environment_directory, identifier) - # environment.initialize(environment_directory, identifier) - # get the bankDirectory from the environment - bankDirectory = environment.static_parameters["bankDirectory"] - # and loop over all banks in the directory - listing = os.listdir(bankDirectory) - bankFilename = bankDirectory + listing[0] - - # generate the bank - bank = Bank() - bank.initialize_standard_bank() - - # - # TEST CODE - # - - change = 10.0 - - for transaction in bank.accounts: - if (transaction.transactionType == "D"): - print "Old Deposits = " + str(transaction.transactionValue) - bank.change_deposits(change) - print "New Deposits = " + str(transaction.transactionValue) - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # bank__initialize_standard_bank - # ------------------------------------------------------------------------- - - def bank__initialize_standard_bank(self, args): - import os - from src.bank import Bank - from src.environment import Environment # needed for the bankDirectory - - text = "This test checks bank.initialize_standard_bank() \n" - text += " It is successfull if a standart Bank with 2 asstets (I = 100),\n" - text += " E = 90, D = 250 and pReal = 0,9 etc. has been created.\n" - text += " See 'initialize_standard_bank' in bank.py for details.\n" - self.print_info(text) - - # - # INITIALIZATION - # - environment_directory = str(args[1]) - identifier = str(args[2]) - log_directory = str(args[3]) - - # Configure logging parameters so we get output while the program runs - logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', filename=log_directory + identifier + ".log", level=logging.INFO) - logging.info('START logging for test bank__initialize_standard_bank in run: %s', environment_directory + identifier + ".xml") - - # Construct bank filename - environment = Environment(environment_directory, identifier) - # environment.initialize(environment_directory, identifier) - # get the bankDirectory from the environment - bankDirectory = environment.static_parameters["bankDirectory"] - # and loop over all banks in the directory - listing = os.listdir(bankDirectory) - bankFilename = bankDirectory + listing[0] - - # - # TEST CODE - # - - # generate the bank - bank = Bank() - bank.initialize_standard_bank() - print bank - # ------------------------------------------------------------------------- - -# ------------------------------------------------------------------------- -# TESTS FOR ENVIRONMENT.PY -# ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # environment__initialize - # ------------------------------------------------------------------------- - - def environment__initialize(self, args): - import os - from src.bank import Bank - from src.environment import Environment - - text = "This test checks environment.initialize \n" - text += " It is successfull if a standart Bank with 2 asstets (I = 100),\n" - text += " E = 90, D = 250 and pReal = 0,9 etc. has been created.\n" - text += " See 'initialize_standard_bank' in bank.py for details.\n" - self.print_info(text) - - # - # INITIALIZATION - # - environment_directory = str(args[1]) - identifier = str(args[2]) - log_directory = str(args[3]) - - # Configure logging parameters so we get output while the program runs - logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', filename=log_directory + identifier + ".log", level=logging.INFO) - logging.info('START logging for test environment__initialize in run: %s', environment_directory + identifier + ".xml") - - # Construct environment - environment = Environment(environment_directory, identifier) - - # - # TEST CODE - # - # environment.initialize(environment_directory, identifier) - bankDirectory = environment.static_parameters["bankDirectory"] - - bank = Bank() - account = 0.0 - - for type in ["I"]: - if type == "I": - account += bank.get_account(type) - print "I = " + str(account) - - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # environment__read_environment_file - # ------------------------------------------------------------------------- - - def environment__read_environment_file(self, args): - text = "This test checks environment.read_environment_file \n" - text += " This is a function from the standard Python library \n" - text += " which does not really need to be tested therefore \n" - self.print_info(text) - - # ------------------------------------------------------------------------- - -# ------------------------------------------------------------------------- -# TESTS FOR NETWORK.PY -# ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # network__do_interbank_trades - # ------------------------------------------------------------------------- - def network__do_interbank_trades(self, args): - from src.environment import Environment - from src.updater import Updater - - # - # INITIALIZATION - # - environment_directory = str(args[1]) - identifier = str(args[2]) - log_directory = str(args[3]) - - # Configure logging parameters so we get output while the program runs - logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', filename=log_directory + identifier + ".log", level=logging.INFO) - logging.info('START logging for test network__do_interbank_trades in run: %s', environment_directory + identifier + ".xml") - - # - # TEST CODE - # - environment = Environment(environment_directory, identifier) - # create a test environment with standardised banks - - # print environment.banks[0] - # print environment.banks[1] - # print environment.banks[2] - print environment.network - environment.banks[0].parameters["Lp"] = 2.0 - environment.banks[1].parameters["Lp"] = -1.0 - environment.banks[2].parameters["Lp"] = -1.0 - environment.network.do_interbank_trades(environment.get_state(0)) - print environment.network - environment.banks[0].parameters["Lp"] = 2.3 - environment.banks[1].parameters["Lp"] = -1.1 - environment.banks[2].parameters["Lp"] = -1.2 - environment.network.do_interbank_trades(environment.get_state(0)) - print environment.network - - # print environment.banks[0] - # print environment.banks[1] - # print environment.banks[2] - - # - # MEASUREMENT AND LOGGING - # - logging.info('FINISHED logging for test updater__remove_inactive_bank in run: %s \n', environment_directory + identifier + ".xml") - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # network__remove_inactive_bank - # ------------------------------------------------------------------------- - def network__remove_inactive_bank(self, args): - from src.environment import Environment - from src.updater import Updater - - # - # INITIALIZATION - # - environment_directory = str(args[1]) - identifier = str(args[2]) - log_directory = str(args[3]) - - # Configure logging parameters so we get output while the program runs - logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', filename=log_directory + identifier + ".log", level=logging.INFO) - logging.info('START logging for test updater__remove_inactive_banks in run: %s', environment_directory + identifier + ".xml") - - # - # TEST CODE - # - environment = Environment(environment_directory, identifier) - # environment.initialize(environment_directory, identifier) - # create a test environment with standardised banks - - # print environment.banks[0] - # print environment.banks[1] - # print environment.banks[2] - environment.banks[0].parameters["Lp"] = 2.0 - environment.banks[1].parameters["Lp"] = -1.0 - environment.banks[2].parameters["Lp"] = -1.0 - environment.network.do_interbank_trades(environment.get_state(0)) - print environment.network - - updater = Updater(environment) - - # - # execute the update code - # - environment.banks[0].reduce_banking_capital(2.0) - environment.banks[0].check_solvency(environment, 'info', 0) - environment.network.remove_inactive_bank(environment.banks[0], 0) - - # print environment.banks[0] - # print environment.banks[1] - # print environment.banks[2] - print environment.network - - # - # MEASUREMENT AND LOGGING - # - logging.info('FINISHED logging for test updater__remove_inactive_bank in run: %s \n', environment_directory + identifier + ".xml") - # ------------------------------------------------------------------------- - -# ------------------------------------------------------------------------- -# TESTS FOR UPDATER.PY -# ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # test_updater - # ------------------------------------------------------------------------- - def updater__updater(self, args): - from src.environment import Environment - from src.updater import Updater - - # - # INITIALIZATION - # - environment_directory = str(args[1]) - identifier = str(args[2]) - log_directory = str(args[3]) - - # Configure logging parameters so we get output while the program runs - logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', filename=log_directory + identifier + ".log", level=logging.INFO) - logging.info('START logging for test updater__updater2 in run: %s', environment_directory + identifier + ".xml") - - # - # TEST CODE - # - environment = Environment(environment_directory, identifier) - # create a test environment with standardised banks - environment.banks[0].change_deposits(1.0) - environment.banks[1].change_deposits(-1.0) - - updater = Updater(environment) - - # - # execute the update code - # - updater.do_update(environment, 0, "info") - - # - # MEASUREMENT AND LOGGING - # - logging.info('FINISHED logging for test updater__updater2 in run: %s \n', environment_directory + identifier + ".xml") - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # test_updater1 - # ------------------------------------------------------------------------- - def updater__updater1(self, args): - from src.environment import Environment - from src.updater import Updater - - # - # INITIALIZATION - # - environment_directory = str(args[1]) - identifier = str(args[2]) - log_directory = str(args[3]) - - # Configure logging parameters so we get output while the program runs - logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', filename=log_directory + identifier + ".log", level=logging.INFO) - logging.info('START logging for test updater__updater1 in run: %s', environment_directory + identifier + ".xml") - - # - # TEST CODE - # - environment = Environment(environment_directory, identifier) - # environment.initialize(environment_directory, identifier) - # create a test environment with standardised banks - - print environment.banks[0] - print environment.banks[1] - print environment.banks[2] - - updater = Updater(environment) - - # - # execute the update code - # - updater.do_update_phase1(environment, environment.network.contracts.nodes(), 0, "info") - - print environment.banks[0] - print environment.banks[1] - print environment.banks[2] - - # - # MEASUREMENT AND LOGGING - # - logging.info('FINISHED logging for test updater__updater1 in run: %s \n', environment_directory + identifier + ".xml") - # ------------------------------------------------------------------------- - -# ------------------------------------------------------------------------- -# UNKNOWN TESTS -# ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # test_fire_sales - # ------------------------------------------------------------------------- - def test_fire_sales(self, args): # TODO not consistent with other test names - import logging - import networkx as nx - - from src.environment import Environment - from src.runner import Runner - from src.measurement import Measurement - - # - # INITIALIZATION - # - environment_directory = str(args[1]) - identifier = str(args[2]) - log_directory = str(args[3]) - measurement_directory = str(args[4]) - - # Configure logging parameters so we get output while the program runs - logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', filename=log_directory + identifier + ".log", level=logging.INFO) - logging.info('START logging for run: %s', environment_directory + identifier + ".xml") - - environment = Environment(environment_directory, identifier) - # environment.initialize(environment_directory, identifier) - runner = Runner() - measurement = Measurement() - - # - # UPDATE STEP - # - for i in range(environment.static_parameters["numSimulations"]): - environment.initialize(environment_directory, identifier) - runner.initialize(environment) - measurement.initialize() # clear the previous measurement - - # do the run - runner.do_run(measurement, "info") - - # do the histograms, i.e. add the current measurement to the histogram - measurement.do_histograms() - logging.info('') - - # - # MEASUREMENT AND LOGGING - # - measurement.write_histograms(measurement_directory, environment) - logging.info('FINISHED logging for run: %s \n', environment_directory + identifier + ".xml") - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # test_state - # ------------------------------------------------------------------------- - def test_state(self, args): - from src.environment import Environment - from src.updater import Updater - - # - # INITIALIZATION - # - environment_directory = str(args[1]) - identifier = str(args[2]) - log_directory = str(args[3]) - - # Configure logging parameters so we get output while the program runs - logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', filename=log_directory + identifier + ".log", level=logging.INFO) - logging.info('START logging for test updater__remove_inactive_banks in run: %s', environment_directory + identifier + ".xml") - - # - # TEST CODE - # - environment = Environment(environment_directory, identifier) - # environment.initialize(environment_directory, identifier) - # create a test environment with standardised banks - - # - print environment.get_state(0) - environment.banks[0].reduce_banking_capital(10.0) - environment.banks[0].check_solvency(environment.get_state(0), "info", 0) - print environment.get_state(1) - - # - # MEASUREMENT AND LOGGING - # - logging.info('FINISHED logging for test updater__remove_inactive_bank in run: %s \n', environment_directory + identifier + ".xml") - # ------------------------------------------------------------------------ - - # ------------------------------------------------------------------------- - # liquidate_assets - # ------------------------------------------------------------------------- - def updater__liquidate_assets(self, args): - from src.environment import Environment - from src.updater import Updater - - # - # INITIALIZATION - # - environment_directory = str(args[1]) - identifier = str(args[2]) - log_directory = str(args[3]) - - # Configure logging parameters so we get output while the program runs - logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', filename=log_directory + identifier + ".log", level=logging.INFO) - logging.info('START logging for test updater__liquidate_assets in run: %s', environment_directory + identifier + ".xml") - - # - # TEST CODE - # - environment = Environment(environment_directory, identifier) - # create a test environment with standardised banks - - print environment.banks[0] - # print environment.banks[1] - # print environment.banks[2] - - updater = Updater(environment) - environment.banks[1].parameters["active"] = -1 - environment.banks[2].parameters["active"] = -1 - # - # execute the update code - # - updater.do_update_phase1(environment, 0, "debug") - updater.do_update_phase2(environment, 0, "info") - - print environment.banks[0] - # print environment.banks[1] - # print environment.banks[2] - - # - # MEASUREMENT AND LOGGING - # - logging.info('FINISHED logging for test updater__liquidate_assets in run: %s \n', environment_directory + identifier + ".xml") - # ------------------------------------------------------------------------- - - # ------------------------------------------------------------------------- - # bank__test1 - # ------------------------------------------------------------------------- - def bank__test1(self, args): - from src.environment import Environment - - text = "This test checks the environment.initializer \n" - text += " It is successfull if a bank has been generate and \n" - text += " if a network of 3 banks with 3 nodes and 6 edges \n" - text += " has been established.\n" - self.print_info(text) - - # - # INITIALIZATION - # - environment_directory = str(args[1]) - identifier = str(args[2]) - log_directory = str(args[3]) - - # Configure logging parameters so we get output while the program runs - logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %H:%M:%S', filename=log_directory + identifier + ".log", level=logging.INFO) - logging.info('START logging for test bank__test1 in run: %s', environment_directory + identifier + ".xml") - - # - # TEST CODE - # - environment = Environment(environment_directory, identifier) - # environment.initialize(environment_directory, identifier) - - # for bank in environment.banks: - print environment.banks[2] - print environment.network - - # - # MEASUREMENT AND LOGGING - # - logging.info('FINISHED logging for test bank__test1 in run: %s \n', environment_directory + identifier + ".xml") - # ------------------------------------------------------------------------- diff --git a/src/transaction.py b/src/transaction.py index 2af1543..8f4102f 100644 --- a/src/transaction.py +++ b/src/transaction.py @@ -1,10 +1,11 @@ #!/usr/bin/env python -# [SublimeLinter pep8-max-line-length:300] +# [SublimeLinter pep8-max-line-length:150] # -*- coding: utf-8 -*- """ black_rhino is a multi-agent simulator for financial network analysis -Copyright (C) 2012 Co-Pierre Georg (co-pierre.georg@keble.ox.ac.uk) +Copyright (C) 2016 Co-Pierre Georg (co-pierre.georg@keble.ox.ac.uk) +Pawel Fiedor (pawel@fiedor.eu) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,6 +20,8 @@ along with this program. If not, see . """ +from abm_template.src.basetransaction import BaseTransaction + # ------------------------------------------------------------------------- # # class Transaction @@ -26,84 +29,187 @@ # ------------------------------------------------------------------------- -class Transaction(object): +class Transaction(BaseTransaction): + + # # # VARIABLES # - transactionType = "" - # transactionAsset = None - transactionFrom = 0 - transactionTo = 0 - transactionValue = 0.0 - transactionInterest = 0.0 - transactionMaturity = 0 + # + + identifier = None # unique identifier of the transaction, may be useful for iterators + type_ = "" # type of transactions, e.g. "deposit" + asset = "" # type of asset, used for investment types + from_ = 0.0 # agent being the originator of the transaction + to = 0.0 # agent being the recipient of the transaction + amount = 0.0 # amount of the transaction + interest = 0.0 # interest rate paid to the originator each time step + maturity = 0 # time (in steps) to maturity # this is used only for loans I, and will be > 0 for defaulting loans. with each update step, it is reduced by 1 # if timeOfDefault == 0: loan defaults - transactionTimeOfDefault = -1 + time_of_default = -1 # control variable checking for defaulted transactions + # # # METHODS # + # + # ------------------------------------------------------------------------- # __init__ + # Generate a unique identifier of the transaction + # This may be useful for looping over various agent's accounts # ------------------------------------------------------------------------- def __init__(self): - pass + self.identifier = None # unique identifier of the transaction, may be useful for iterators + self.type_ = "" # type of transactions, e.g. "deposit" + self.asset = "" # type of asset, used for investment types + self.from_ = 0.0 # agent being the originator of the transaction + self.to = 0.0 # agent being the recipient of the transaction + self.amount = 0.0 # amount of the transaction + self.interest = 0.0 # interest rate paid to the originator each time step + self.maturity = 0 # time (in steps) to maturity + # this is used only for loans I, and will be > 0 for defaulting loans. with each update step, it is reduced by 1 + # if timeOfDefault == 0: loan defaults + self.time_of_default = -1 # control variable checking for defaulted transactions + super(Transaction, self).__init__() # ------------------------------------------------------------------------ # ------------------------------------------------------------------------- - # this_transaction(transactionType, - # transactionFrom, - # transactionTo, - # transactionValue, - # transactionInterest, - # transactionMaturity, - # transactionTimeOfDefault) - # ------------------------------------------------------------------------- - def this_transaction(self, transactionType, transactionFrom, transactionTo, transactionValue, transactionInterest, transactionMaturity, transactionTimeOfDefault): - self.transactionType = transactionType - # if transactionType == "I": - # self.transactionAsset = transactionAsset - # the convention used is that values are positive - if transactionValue >= 0: - self.transactionFrom = transactionFrom - self.transactionTo = transactionTo - else: # negative values reverse direction and delete sign - self.transactionFrom = transactionTo - self.transactionTo = transactionFrom - transactionValue = abs(transactionValue) - self.transactionValue = transactionValue - self.transactionInterest = transactionInterest - self.transactionMaturity = transactionMaturity - self.transactionTimeOfDefault = transactionTimeOfDefault + # __del__() + # removes the transaction from appropriate accounts and deletes the instance + # if transaction hasn't been properly added there is no need to change accounts + # DO NOT USE IN PRODUCTION, this is a failsafe + # use remove_transaction() to take transaction off the books + # ------------------------------------------------------------------------- + def __del__(self): + super(Transaction, self).__del__() + # ------------------------------------------------------------------------- + + # ------------------------------------------------------------------------- + # functions for setting/changing variables + # these either return or set specific value to the above variables + # ------------------------------------------------------------------------- + def get_type_(self): + return self.type_ + + def set_type_(self, type_): + super(Transaction, self).set_type_(type_) + + def get_asset(self): + return self.asset + + def set_asset(self, asset): + super(Transaction, self).set_asset(asset) + + def get_from_(self): + return self.from_ + + def set_from_(self, from_): + super(Transaction, self).set_from_(from_) + + def get_to(self): + return self.to + + def set_to(self, to): + super(Transaction, self).set_to(to) + + def get_amount(self): + return self.amount + + def set_amount(self, amount): + super(Transaction, self).set_amount(amount) + + def get_interest(self): + return self.interest + + def set_interest(self, interest): + super(Transaction, self).set_interest(interest) + + def get_maturity(self): + return self.maturity + + def set_maturity(self, maturity): + super(Transaction, self).set_maturity(maturity) + + def get_time_of_default(self): + return self.time_of_default + + def set_time_of_default(self, time_of_default): + super(Transaction, self).set_time_of_default(time_of_default) + # ------------------------------------------------------------------------- + + # ------------------------------------------------------------------------- + # this_transaction(type_, + # asset, + # from_, + # to, + # amount, + # interest, + # maturity, + # time_of_default) + # sets the variables of the transaction to the given amounts + # ------------------------------------------------------------------------- + def this_transaction(self, type_, asset, from_, to, amount, interest, maturity, time_of_default): + super(Transaction, self).this_transaction(type_, asset, from_, to, amount, interest, maturity, time_of_default) + # ------------------------------------------------------------------------- + + # ------------------------------------------------------------------------- + # add_transaction + # adds the transaction to appropriate agents' accounts + # TODO: we need to make sure we don't do it twice when we iterate over + # transactions in the accounts of agents (this may be tricky) + # ------------------------------------------------------------------------- + def add_transaction(self, environment): + super(Transaction, self).add_transaction(environment) + # ------------------------------------------------------------------------- + + # ------------------------------------------------------------------------- + # remove_transaction + # removes the transaction from appropriate agents' accounts + # ------------------------------------------------------------------------- + def remove_transaction(self): + super(Transaction, self).remove_transaction() # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- # print_transaction() + # prints the transaction and its properties # ------------------------------------------------------------------------- def print_transaction(self): - print " " - print " " - print " " - print " " - print " " - print " " - print " " - print " " + super(Transaction, self).print_transaction() + # ------------------------------------------------------------------------- + + # ------------------------------------------------------------------------- + # __str__() + # prints the transaction and its properties + # ------------------------------------------------------------------------- + def __str__(self): + return super(Transaction, self).write_transaction() # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- # write_transaction() + # returns a string with the transaction and its properties # ------------------------------------------------------------------------- def write_transaction(self): - text = " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - text += " \n" - - return text + return super(Transaction, self).write_transaction() + # ------------------------------------------------------------------------- + + # ------------------------------------------------------------------------- + # clear_accounts() + # deletes all transactions of a given agent + # this should be used very sparingly, as this does not account + # for the economics of the process + # ------------------------------------------------------------------------- + def clear_accounts(self, agent): + super(Transaction, self).clear_accounts(agent) + # ------------------------------------------------------------------------- + + # ------------------------------------------------------------------------- + # purge_accounts() + # removes all transactions of all agents with amount of zero + # ------------------------------------------------------------------------- + def purge_accounts(self, environment): + super(Transaction, self).purge_accounts(environment) # ------------------------------------------------------------------------- diff --git a/src/updater.py b/src/updater.py index 2384d83..425c00d 100644 --- a/src/updater.py +++ b/src/updater.py @@ -1,10 +1,11 @@ #!/usr/bin/env python -# [SublimeLinter pep8-max-line-length:300] +# [SublimeLinter pep8-max-line-length:150] # -*- coding: utf-8 -*- """ black_rhino is a multi-agent simulator for financial network analysis -Copyright (C) 2012 Co-Pierre Georg (co-pierre.georg@keble.ox.ac.uk) +Copyright (C) 2016 Co-Pierre Georg (co-pierre.georg@keble.ox.ac.uk) +Pawel Fiedor (pawel@fiedor.eu) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,6 +21,9 @@ """ from abm_template.src.basemodel import BaseModel +import random +import logging +from src.transaction import Transaction # ------------------------------------------------------------------------- # class Updater @@ -27,79 +31,41 @@ class Updater(BaseModel): - # from environment import Environment + # + # + # VARIABLES + # + # identifier = "" model_parameters = {} - agents = [] interactions = None # - # VARIABLES - # - # # METHODS # + # def get_identifier(self): return self.identifier def set_identifier(self, _value): - """ - Class variables: identifier - Local variables: _identifier - """ super(Updater, self).set_identifier(_value) def get_model_parameters(self): return self.model_parameters def set_model_parameters(self, _value): - """ - Class variables: model_parameters - Local variables: _params - """ super(Updater, self).set_model_parameters(_value) - def get_agents(self): - return self.agents - - def set_agents(self, _value): - """ - Class variables: agents - Local variables: _agents - """ - super(Updater, self).set_agents(_value) - def get_interactions(self): return self.interactions def set_interactions(self, _value): - """ - Class variables: interactions - Local variables: _interactions - """ super(Updater, self).set_interactions(_value) - def get_agent_by_id(self, _id): - """ - Class variables: - Local variables: _id - """ - super(Updater, self).get_agent_by_id(_id) - - def check_agent_homogeneity(self): - super(Updater, self).check_agent_homogeneity() - - def initialize_agents(self): - super(Updater, self).initialize_agents() - def __str__(self): - """ - Class variables: identifier, model_parameters, agents, interactions - Local variables: ret_str, entry, value, agent - """ return super(Updater, self).__str__() # ------------------------------------------------------------------------- @@ -107,135 +73,815 @@ def __str__(self): # ------------------------------------------------------------------------- def __init__(self, environment): self.environment = environment - self.model_parameters = environment.static_parameters # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- # do_update # ------------------------------------------------------------------------- - def do_update(self, environment, time, debug): - network = environment.network - # state = environment.get_state(time) - - active_banks = self.find_active_banks(environment, network, time) - self.do_update_phase1(environment, active_banks, time, debug) + def do_update(self, environment, time): + # As a first step, we accrue all interest over the transactions + # Thus, important to notice to keep 0 as interest by default + # Unless transaction should carry interest + # DON'T DO INTERESTS SO FAR, DO ONCE THE REST WORKS + self.find_interbank_liquidity(environment, time) + self.accrue_interests(environment, time) + # Then agents get their labour endowment for the step (e.g. work hours to spend) + # For now we don't need to keep track of labour left as there is no queue + # self.endow_labour(environment, time) + # The households sell labour to firms + self.sell_labour(environment, time) + # The firms sell goods to households + self.consume_rationed(environment, time) + # We net deposits and loans + self.net_loans_deposits(environment, time) + # We remove the perishable transactions + self.remove_perishable(environment, time) + # And add capital to balance the books + self.capitalise(environment, time) + # Investing of the banks + # self.invest(environment, time) + self.invest_interbank(environment, time) + # Purging accounts at every step just in case + transaction = Transaction() + transaction.purge_accounts(environment) + # ------------------------------------------------------------------------- - active_banks = self.find_active_banks(environment, network, time) - self.do_update_phase2(environment, active_banks, time, debug) - network.do_interbank_trades(environment) + # ------------------------------------------------------------------------- + # find_interbank_liquidity(environment, time) + # This method accrues interest on all transaction + # making sure we don't double count the transactions that are + # on the books of multiple agents, interest is specified within the + # transaction itself + # ------------------------------------------------------------------------- + def find_interbank_liquidity(self, environment, time): + environment.update_asset_returns() + for bank in environment.banks: + bank.state_variables["interbank_liquidity"] = 0.0 + for asset_key in environment.assets: + # And find investment transactions + for tranx in bank.accounts: + if tranx.type_ == "investment": + # For the specific asset + if tranx.asset == asset_key: + # And amend its value by the current returns + bank.interbank_liquidity = bank.interbank_liquidity + tranx.amount * environment.assets[asset_key][2] + for tranx in bank.accounts: + if tranx.type_ == "ib_loans": + if tranx.from_ == bank: + bank.interbank_liquidity = bank.interbank_liquidity + tranx.amount * (1 + bank.interbank_interest_rate) + if tranx.to == bank: + bank.interbank_liquidity = bank.interbank_liquidity - tranx.amount * (1 + bank.interbank_interest_rate) + for tranx in bank.accounts: + if tranx.type_ == "cb_loans": + if tranx.to == bank: + bank.interbank_liquidity = bank.interbank_liquidity - tranx.amount * environment.central_bank[0].interest_rate_cb_loans + # REMOVE CENTRAL BANK LOANS AND INTERBANK LOANS + to_delete = [] + for bank in environment.banks: + for tranx in bank.accounts: + if tranx.type_ == "ib_loans" or tranx.type_ == "cb_loans": + to_delete.append(tranx) + for tranx in to_delete: + tranx.remove_transaction() + logging.info(" found interbank liquidity on step: %s", time) + # Keep on the log with the number of step, for debugging mostly + # ------------------------------------------------------------------------- - active_banks = self.find_active_banks(environment, network, time) - self.do_update_phase3(environment, active_banks, time, debug) + # ------------------------------------------------------------------------- + # accrue_interests(environment, time) + # This method accrues interest on all transaction + # making sure we don't double count the transactions that are + # on the books of multiple agents, interest is specified within the + # transaction itself + # ------------------------------------------------------------------------- + def accrue_interests(self, environment, time): + # First, add interests to all the transactions + # The function in environment makes sure we don't double count interests + environment.accrue_interests() + # And returns for assets + # First, we update the current step returns + environment.update_asset_returns() + # Then update the books of each bank + for bank in environment.banks: + # We go asset by asset + for asset_key in environment.assets: + # And find investment transactions + for tranx in bank.accounts: + if tranx.type_ == "investment": + # For the specific asset + if tranx.asset == asset_key: + # And amend its value by the current returns + tranx.amount = tranx.amount * (1 + environment.assets[asset_key][2]) + # Then, banks give their revenue as dividends to households + # which own the banks in the model + # for now we have them given out through ownership weights + total_ownership = 0.0 + for household in environment.households: + total_ownership = total_ownership + household.ownership_of_banks + # We calculate dividends for every bank + for bank in environment.banks: + # Find the excess of assets over liabilities + excess = 0.0 + # To do that we go through all transactions + # and add or subtract them appropriately + for tranx in bank.accounts: + if tranx.type_ == "loans" and tranx.from_ == bank: + excess = excess + tranx.amount + if tranx.type_ == "deposits" and tranx.to == bank: + excess = excess - tranx.amount + # If there is shortfall we throw an error for now + # since it is assumed that in the moden banks should have balanced + # books and interests on assets are higher, but in principle + # we may add some other behaviour here if necessary + # IMPLEMENTATION NOTE: while we want full accuracy in the model for + # now, the floating error around 0 means we need to round before checking + if round(excess, 2) < 0.0: + raise LookupError("Bank lost money on interests.") + # If there is excess of loans over deposits + # We distribute the excess as + # IMPLEMENTATION NOTE: same as above + if round(excess, 2) > 0.0: + # TODO: bank_ownership parameter for households to weigh this + # For now, we add these proportionately to the households + # Deposit these to households appropriately + for household in environment.households: + amount = excess * (household.ownership_of_banks / total_ownership) + environment.new_transaction("deposits", "", household.identifier, bank.identifier, + amount, bank.interest_rate_deposits, 0, -1) + logging.info(" interest accrued on step: %s", time) + # Keep on the log with the number of step, for debugging mostly # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- - # do_update_phase1 + # endow_labour + # This function makes sure that all households have the appropriate + # labour endowment for every step, in line with the parameters + # ------------------------------------------------------------------------- + def endow_labour(self, environment, time): + # We make sure household get their labour endowment per step + # labour is a parameter, doesn't change in the simulation + # sweep_labour is a state variable and can be depleted within the sweep + for household in environment.households: + household.sweep_labour = household.labour + logging.info(" labour endowed on step: %s", time) + # Keep on the log with the number of step, for debugging mostly # ------------------------------------------------------------------------- - def do_update_phase1(self, environment, active_banks, time, debug): - # state = environment.get_state(time) - network = environment.network - self.agents = environment.banks + # ------------------------------------------------------------------------- + # sell_labour(environment, time) + # This function allows the households to sell their labour to firms + # For now we assume that firms want to buy all the labour they can get + # And that they need to use cash for this purpose, they can't take loans + # And firms keep cash, they do not keep deposits, these will be updated + # in later time + # ------------------------------------------------------------------------- + def sell_labour(self, environment, time): + # First we find the market equilibrium price + # Important to note that this currently does + # not depend on the wealth of the buyers + # That is their demand may be higher than + # what they can actually buy, which may be ok + # We set the values necessary for tatonnement + # The list of sellers and their supply functions + sellers = [] + for agent in environment.households: + sellers.append([agent, agent.supply_of_labour_solow]) + # And the list of buyers and their demand functions + buyers = [] + for agent in environment.firms: + buyers.append([agent, agent.demand_for_labour_solow]) + # We may start the search for price at some specific point + # Here we pass 0, which means it'll start looking at a + # random point between 0 and 10 + starting_price = 10.0 + # We initialize the price + price = 0.0 + # Import market clearing class + from market import Market + # Put the appropriate settings, i.e. desired identifier + market = Market("market") + # And we find the market price of labour + # given supply and demand of the agents + # and tolerance of error, resolution of search + # and amplification factor for exponential search + price = market.tatonnement(sellers, buyers, starting_price, 0.001, 0.01, 1.1) + environment.variable_parameters["price_of_labour"] = price + # now we use rationing to find the actual transactions between agents + for_rationing = [] + for household in environment.households: + for_rationing.append([household, household.supply_of_labour_solow(price)]) + for firm in environment.firms: + for_rationing.append([firm, -firm.demand_for_labour_solow(price)]) + # And we find the rationing, ie the amounts + # of goods sold between pairs of agents + rationed = market.rationing_proportional(for_rationing) # - # loop over all banks and do update step + # A (from) L (to) + # bank loan deposit + # household deposit labour + # firm labour loan # - for bank in active_banks: - # first, update all maturities - bank.update_maturity() - - # then, update the risk-aversion parameter of banks to incorporate information contagion - bank.update_risk_aversion(environment.get_state(time), time) - - # re-calculate the (constraint) optimal portfolio decision of the bank - - # initialize bank liquidity - bank.parameters["Q"] = 0.0 - bank.parameters["Q"] = bank.parameters["Q"] + bank.get_interest("D") - bank.parameters["Q"] = bank.parameters["Q"] + bank.get_interest("rD") - bank.parameters["Q"] = bank.parameters["Q"] + bank.get_interest("E") - bank.parameters["Q"] = bank.parameters["Q"] + bank.get_interest("I") # here a loss on the banking capital might occur - bank.parameters["Q"] = bank.parameters["Q"] + bank.get_interest("L") - bank.parameters["Q"] = bank.parameters["Q"] + bank.get_interest("LC") - - # first, get all required reserves - bank.parameters["Q"] = bank.parameters["Q"] + bank.liquidate_due_transactions("rD") - # then, get all excess reserves - bank.parameters["Q"] = bank.parameters["Q"] + bank.liquidate_due_transactions("E") - # then, get payments from firms (interest, due loans) - bank.parameters["Q"] = bank.parameters["Q"] + bank.liquidate_due_transactions("I") - # now, settle interbank claims - # remove the claim from the network of exposures - network.liquidate_due_transactions(bank) - # and liquidate it - bank.parameters["Q"] = bank.parameters["Q"] + bank.liquidate_due_transactions("L") - # and central bank claims - bank.parameters["Q"] = bank.parameters["Q"] + bank.liquidate_due_transactions("LC") - # transfer required reserves to the central bank - bank.parameters["Q"] = bank.parameters["Q"] + bank.transfer_required_deposits() - - # check the solvency, the bank might be insolvent due to a loss on the risky assets - bank.check_solvency(environment, debug, time) + for ration in rationed: + # The labour is an asset (production factor) for the firm + # and a liability (promise to work) for the household + environment.new_transaction("labour", "", ration[1].identifier, ration[0].identifier, + ration[2], 0, 0, -1) + random_bank = random.choice(environment.banks) + # Deposit is a liability of the bank + # and an asset of the household + environment.new_transaction("deposits", "", ration[0].identifier, random_bank.identifier, + ration[2]*price, random_bank.interest_rate_deposits, 0, -1) + # Loan is an asset of the bank + # and a liability of the firm + environment.new_transaction("loans", "", random_bank.identifier, ration[1].identifier, + ration[2]*price, random_bank.interest_rate_loans, 0, -1) + # We print the action of selling to the screen + print("%s sold %d units of labour at a price %f to %s at time %d.") % (ration[0].identifier, + ration[2], price, ration[1].identifier, time) + logging.info(" labour sold to firms on step: %s", time) + # Keep on the log with the number of step, for debugging mostly # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- - # do_update_phase2 + # consume_rationed(environment, time) + # This function lets households buy the goods the firms have produced + # so they can satisfy their needs through consumption + # This consumption depends on the propensity to save of the households + # The matching of buyers and sellers on the market is random # ------------------------------------------------------------------------- - def do_update_phase2(self, environment, active_banks, time, debug): - for bank in active_banks: - # next, determine new deposit level - new_deposit_level = bank.get_new_deposits(self.environment.get_state(time).static_parameters["scaleFactorHouseholds"]) - bank.parameters["Q"] = bank.parameters["Q"] + new_deposit_level - # and calculate the liquidity demand - bank.calculate_liquidity_demand() + def consume_rationed(self, environment, time): + # We want the consumption to be done in random pairs + # We use rationing from market clearing class to do that + # Price is static for this example, otherwise we can't use rationing + # and need some other market clearing + price = 10.0 + environment.variable_parameters["price_of_goods"] = price + # We need a list of agents and their demand or supply + # Supply is denoted with positive float, demand with negative float + for_rationing = [] + # Firms give us their supply, we assume that since the goods are + # perishable their supply is all they have in stock + from src.helper import Helper + helper = Helper() + for firm in environment.firms: + # Firms produce based on their capital, for generality + # we use their net capital, as in their capital stock + # minus the capital owned of other agents + capital = 0.0 + for tranx in firm.accounts: + # This is own capital stock + if tranx.type_ == "capital" and tranx.from_ == firm: + capital = capital + tranx.amount + # And here is the ownership of other agents' stock + if tranx.type_ == "capital" and tranx.to == firm: + capital = capital - tranx.amount + # We find the amount produced through the Cobb-Douglas function + amount = helper.cobb_douglas(firm.get_account("labour"), capital, + firm.total_factor_productivity, firm.labour_elasticity, firm.capital_elasticity)*price + # And assume firm wants to sell whole production given the perishable nature of the goods + for_rationing.append([firm, amount]) + # Households give use their demand, we assume that they want to + # consume the part of their wealth (cash and deposits) that they + # do not want to save (determined through propensity to save) + # We denote demand in units of the goods, so we divide the cash + # households want to spend by price to get the demand + for household in environment.households: + demand = 0.0 + wealth = 0.0 + # For generality we calculate net wealth for this, that is the + # amount of deposits they carry minus the amount of loans + for tranx in household.accounts: + if tranx.type_ == "deposits" and tranx.from_ == household: + wealth = wealth + tranx.amount + if tranx.type_ == "loans" and tranx.to == household: + wealth = wealth - tranx.amount + # Then the demand is determined by the agent's propensity to save + # and the wealth calculated above + demand = -((wealth * (1 - household.propensity_to_save)) / price) + for_rationing.append([household, demand]) + # We import the market clearing class + from market import Market + # Put the appropriate settings, i.e. + # tolerance of error, resolution of search + # and amplification for exponential search + # This does not matter for rationing + # But in principle we need to initialize + # with these values + market = Market("market") + # And we find the rationing, ie the amounts + # of goods sold between pairs of agents + # TESTING THE ABSTRACT RATIONING + # The matching function means that all pairs will have the same priority + + def matching_agents_basic(agent_one, agent_two): + # return 1.0 + return random.random() + + # The below function means that all pairs are allowed + + def allow_match_basic(agent_one, agent_two): + return True + + # We find the actual trades + # rationed = market.rationing_abstract(for_rationing, matching_agents_basic, allow_match_basic) + rationed = market.rationing_proportional(for_rationing) + # Then we go through the rationing + # and move the goods and cash appropriately + for ration in rationed: + # + # A (from) L (to) + # bank loan deposit + # household goods loan + # firm deposit goods + # + # TODO: in the new version this may be irrelevant + environment.new_transaction("goods", "", ration[1].identifier, ration[0].identifier, + ration[2], 0, 0, -1) + # The below makes sure the allocations of loans are correct + # That is the banks don't allow overdraft for buying + # consumption goods by the households + to_finance = ration[2]*price + itrange = list(range(0, len(environment.banks))) + # And randomise this list for the purposes of iterating randomly + random.shuffle(itrange) + # And we iterate over the agents randomly by proxy of iterating + # through their places on the list [agents] + for i in itrange: + current_bank = self.environment.banks[i] + # We find how much in deposits the household has + deposits_available = 0.0 + for tranx in ration[1].accounts: + if tranx.type_ == "deposits" and tranx.to == current_bank: + deposits_available = deposits_available + tranx.amount + # This should be irrelevant, but for completeness: + if tranx.type_ == "loans" and tranx.from_ == current_bank: + deposits_available = deposits_available - tranx.amount + # We find the amount of deposits the household can spend for this particular bank + current_amount = min(to_finance, deposits_available) + # And add the appropriate transactions + environment.new_transaction("deposits", "", ration[0].identifier, current_bank.identifier, + current_amount, current_bank.interest_rate_deposits, 0, -1) + environment.new_transaction("loans", "", current_bank.identifier, ration[1].identifier, + current_amount, current_bank.interest_rate_loans, 0, -1) + to_finance = to_finance - current_amount + # Below is the old code for legacy comparison, to be deleted later + ''' + random_bank = random.choice(environment.banks) + environment.new_transaction("deposits", "", ration[0].identifier, random_bank.identifier, + ration[2]*price, random_bank.interest_rate_deposits, 0, -1) + environment.new_transaction("loans", "", random_bank.identifier, ration[1].identifier, + ration[2]*price, random_bank.interest_rate_loans, 0, -1) + ''' + # We print the action of selling to the screen + print("%s sold %d units of goods at a price %f to %s at time %d.") % (ration[0].identifier, + ration[2], price, ration[1].identifier, time) + logging.info(" goods consumed on step: %s", time) + # Keep on the log with the number of step, for debugging mostly # ------------------------------------------------------------------------- # ------------------------------------------------------------------------- - # do_update_phase3 + # net_loans_deposits(environment, time) + # This function makes deposits of all the remaining cash of the households + # It is important to notice that this ultimately depends on the propensity + # to save parameter, but indirectly, since it influences how much in goods + # the agents buy from firms prior to this step, thus allowing this step + # to be easier and move all cash to deposits in the banks + # ------------------------------------------------------------------------- + def net_loans_deposits(self, environment, time): + # We do it from the bank's perspective + for bank in environment.banks: + # And first go through the firms + for firm in environment.firms: + # Finding what their balance of deposits (+) and loans (-) is + balance = 0.0 + # And mark all the loan and deposits we account for to be deleted + to_delete = [] + # We go through the firm's accounts + for tranx in firm.accounts: + # And find deposits from the firm to the bank + if tranx.type_ == "deposits": + if tranx.to == bank: + # If we find one we append the balance + balance = balance + tranx.amount + # And mark the transaction for deletion + to_delete.append(tranx) + # And find loans from the bank to the firm + if tranx.type_ == "loans": + if tranx.from_ == bank: + # If we find one we append the balance + balance = balance - tranx.amount + # And mark the transaction for deletio + to_delete.append(tranx) + # Then we delete all market transactions + for tranx in to_delete: + tranx.remove_transaction() + # And add the netted transaction to the firm's and bank's books + if balance > 0.0: + # If the balance is positive it's a deposit + environment.new_transaction("deposits", "", firm.identifier, bank.identifier, + balance, bank.interest_rate_deposits, 0, -1) + elif balance < 0.0: + # If the balance is negative it's a loan + environment.new_transaction("loans", "", bank.identifier, firm.identifier, + abs(balance), bank.interest_rate_loans, 0, -1) + # We do it from the bank's perspective + for bank in environment.banks: + # And first go through the households + for household in environment.households: + # Finding what their balance of deposits (+) and loans (-) is + balance = 0.0 + # And mark all the loan and deposits we account for to be deleted + to_delete = [] + # We go through the household's accounts + for tranx in household.accounts: + # And find deposits from the household to the bank + if tranx.type_ == "deposits": + if tranx.to == bank: + # If we find one we append the balance + balance = balance + tranx.amount + # And mark the transaction for deletion + to_delete.append(tranx) + # And find loans from the bank to the household + if tranx.type_ == "loans": + if tranx.from_ == bank: + # If we find one we append the balance + balance = balance - tranx.amount + # And mark the transaction for deletion + to_delete.append(tranx) + # Then we delete all market transactions + for tranx in to_delete: + tranx.remove_transaction() + # And add the netted transaction to the household's and bank's books + if balance > 0.0: + # If the balance is positive it's a deposit + environment.new_transaction("deposits", "", household.identifier, bank.identifier, + balance, bank.interest_rate_deposits, 0, -1) + elif balance < 0.0: + # If the balance is negative it's a loan + environment.new_transaction("loans", "", bank.identifier, household.identifier, + abs(balance), bank.interest_rate_loans, 0, -1) + logging.info(" deposits and loans netted on step: %s", time) + # Keep on the log with the number of step, for debugging mostly # ------------------------------------------------------------------------- - def do_update_phase3(self, environment, active_banks, time, debug): - current_assets = 0.0 # the volume of all assets in the market, required to determine the price-drop of assets in a fire sale - for bank in active_banks: # find the current amount of assets in the market - current_assets += bank.get_account("I") - - # state = environment.get_state(time) - network = environment.network - self.agents = environment.banks - - # - # then do the update code - # - for bank in active_banks: - # transfer to/from standing facilitiesget_parameters_from_file - bank.get_central_bank_liquidity(environment.get_state(time)) - - # check for the bank's liquidity - bank.liquidate_assets(environment.initial_assets, current_assets, environment.get_state(time), debug, time) - # transfer investments to firms - bank.parameters["Q"] = bank.parameters["Lp"] # transfer available liquidity to Q - bank.parameters["Lp"] = 0.0 - bank.transfer_investments(environment.get_state(time)) # state is needed to determine the probability that a loan defaults - bank.transfer_excess_reserves() + # ------------------------------------------------------------------------- + # remove_perishable(environment, time) + # This function removes the perishable transactions in the system + # which need to be removed from the books before the end of the step + # all methods that use these temporary transactions need to be invoked + # before this method, currently it removes labour and goods + # ------------------------------------------------------------------------- + def remove_perishable(self, environment, time): + # First, remove labour, goods from firms + for firm in environment.firms: + # We create a list of things to be removed + # since removing things from a list + # in a loop over this list is not a good idea + to_delete = [] + # Then go through transactions + for tranx in firm.accounts: + # Append the things to delete + # if it's a labour + if tranx.type_ == "labour": + to_delete.append(tranx) + # or goods transaction + if tranx.type_ == "goods": + to_delete.append(tranx) + # And once we have them all we + # go through the things to delete + # and remove them from the books of agents + for tranx in to_delete: + tranx.remove_transaction() + + # Then, remove labour, goods from households + for household in environment.households: + # We create a list of things to be removed + # since removing things from a list + # in a loop over this list is not a good idea + to_delete = [] + # Then go through transactions + for tranx in firm.accounts: + # Append the things to delete + # if it's a labour + if tranx.type_ == "labour": + to_delete.append(tranx) + # or goods transaction + if tranx.type_ == "goods": + to_delete.append(tranx) + # And once we have them all we + # go through the things to delete + # and remove them from the books of agents + for tranx in to_delete: + tranx.remove_transaction() + + # If necessary, another line for banks will be added here + + logging.info(" perishables removed on step: %s", time) + # Keep on the log with the number of step, for debugging mostly + # ------------------------------------------------------------------------- - if (debug == "debug"): - network.write_network_of_exposures(time) + # ------------------------------------------------------------------------- + # capitalise(environment, time) + # This function makes capital transactions which represent final + # ownership of firms by the household which is done through the bank + # deposits and bank loans, these are in principle not necessary for the + # model to work, as we can assume the loans are the capital, and produce off + # that assumption, but this ensures the books are balanced for all agents + # ------------------------------------------------------------------------- + def capitalise(self, environment, time): + # We will ration the remaining excess deposits + # and loan as capital ownership transfers + # to balance books, if books don't need to be + # balanced the same would work strictly on deposits + # and loans with no capital explicitly + + # First resolve capital shortfall for firms + # ie when firm needs to sell existing capital instead of getting new owners + for firm in environment.firms: + # We calculate how much capital the firm has + capital = 0.0 + for tranx in firm.accounts: + if tranx.type_ == "capital": + if tranx.from_ == firm: + capital = capital + tranx.amount + if tranx.to == firm: + capital = capital - tranx.amount + # Then find the firm's supply of capital given current books + supply = -capital - firm.get_account("deposits") + firm.get_account("loans") + # If there is a shortfall of capital supply + if supply < 0.0: + # We go through the books + for tranx in firm.accounts: + # And find capital transactions + if tranx.type_ == "capital" and tranx.from_ == firm: + # Then we sell the appropriate amount to cover the shortfall + # TODO: we may want the sellout to be proportional or at least + # going through books at random, though in the current model it shouldn't matter + to_remove = min(-supply, tranx.amount) + tranx.amount = tranx.amount - to_remove + supply = supply + to_remove + + # First, we create the list that will be used for rationing + # method from Market class, containing agents and their + # excess supply or demand + for_rationing = [] + + # First we find household's demand for buying capital of the firms + for household in environment.households: + # We calculate the demand as the amount of wealth (deposits-loans) minus previously owned capital + # We calculate capital by hand in case there is some reverse ownership + deposits = 0.0 + loans = 0.0 + capital = 0.0 + for tranx in household.accounts: + if tranx.type_ == "deposits": + if tranx.from_ == household: + deposits = deposits + tranx.amount + if tranx.type_ == "loans": + if tranx.to == household: + loans = loans + tranx.amount + if tranx.type_ == "capital": + if tranx.to == household: + capital = capital + tranx.amount + if tranx.from_ == household: + capital = capital - tranx.amount + # demand = household.get_account("deposits") - household.get_account("loans") - household.get_account("capital") + demand = deposits - loans - capital + # And we add the household together with its demand to the list + for_rationing.append([household, -demand]) + + for firm in environment.firms: + # Supply of the firms is the opposite of the demand of the household + # that is the loans minus issued capital claims minus deposits + # We calculate capital by hand in case there is some reverse ownership + capital = 0.0 + for tranx in firm.accounts: + if tranx.type_ == "capital": + if tranx.from_ == firm: + capital = capital + tranx.amount + if tranx.to == firm: + capital = capital - tranx.amount + supply = -capital - firm.get_account("deposits") + firm.get_account("loans") + # supply = -firm.get_account("capital") - firm.get_account("deposits") + firm.get_account("loans") + # And we add the firm together with its supply to the list + for_rationing.append([firm, supply]) + + # We initialise the market clearing class + from market import Market + market = Market("market") + + # The below functions means that the pairing will be linear + + def matching_agents_basic(agent_one, agent_two): + return random.random() + + # The below function means that all pairs are allowed + + def allow_match_basic(agent_one, agent_two): + if agent_one in environment.firms and agent_two in environment.firms: + return False + else: + return True + + # We find the pairs of capital ownership transfers + # We move the capital proportionately with respect to demand + rationed = market.rationing_proportional(for_rationing) + # rationed = market.rationing_abstract(for_rationing, matching_agents_basic, allow_match_basic) + + # We add these to the books + for ration in rationed: + environment.new_transaction("capital", "", ration[0].identifier, ration[1].identifier, + ration[2], 0, 0, -1) + # And print it to the screen for easy greping + print("%s sold %f worth of capital to %s at time %d.") % (ration[0].identifier, + ration[2], ration[1].identifier, time) + + # And net the capital transactions, so we don't accumulate + # them over the course of the transaction + # Again, we create a proxy list for deleting transactions + # as deleting them from a list upon which we are looping is bad + to_delete = [] + + # We go through the firms + for firm in environment.firms: + # And then pair them with households + for household in environment.households: + # We will look for the capital balance of the pair + balance = 0.0 + # So we need to look through all their books + for tranx in household.accounts: + # Find the capital transactions + if tranx.type_ == "capital": + # And if they are ownership of the firm's equity + # We add them to the balance + # And mark for deletion + if tranx.from_ == firm: + balance = balance + tranx.amount + to_delete.append(tranx) + # If they are the other way around for some reason + # we would subtract them and mark for deletion + elif tranx.to == firm: + balance = balance - tranx.amount + to_delete.append(tranx) + # We create a new transactions from the balance + # depending on what the value of the balance is + if balance > 0.0: + environment.new_transaction("capital", "", firm.identifier, household.identifier, + balance, 0, 0, -1) + elif balance < 0.0: + environment.new_transaction("capital", "", household.identifier, firm.identifier, + balance, 0, 0, -1) + # And at the end, we remove all the transactions that we marked before + for tranx in to_delete: + tranx.remove_transaction() + + logging.info(" capitalised on step: %s", time) + # Keep on the log with the number of step, for debugging mostly # ------------------------------------------------------------------------- + # ------------------------------------------------------------------------- + # invest(environment, time) + # This function checks the optimal portfolio volume for banks and their + # allocations, then looks at the current ones and makes appropriate + # investment decisions + # ------------------------------------------------------------------------- + def invest(self, environment, time): + # We do this for every bank + for bank in environment.banks: + # Every bank finds out what leverage they want to use by using their + # own leverage from config file unless it's prohibited by the policy + target_leverage = 0.0 + target_leverage = min(bank.target_leverage, environment.max_leverage_ratio) + # Then from the loans banks have and the leverage ration above + # We find how much the banks want to be investing + investment_volume = 0.0 + investment_volume = bank.get_account("loans")*(target_leverage-1) + + # Then we add or remove central bank loans + # We assume that since there is only one central bank we can keep those + # in one transaction for now, if we fiddle with maturities this may change + # If we don't yet have a central bank loan we create one + if bank.get_account_num_transactions("cb_loans") == 0: + environment.new_transaction("cb_loans", "", environment.central_bank[0].identifier, bank.identifier, + investment_volume, environment.central_bank[0].interest_rate_cb_loans, 0, -1) + # If we have a prior central bank loan we just adjust the amount + else: + for tranx in bank.accounts: + if tranx.type_ == "cb_loans": + tranx.amount = investment_volume + + # Then we add or remove investments + # We do it for every investment class, for now we have 1/n portfolio + for asset_key in environment.assets: + # Thus each asset is invested in linearly, 1/n times the total inv volume + one_investment_volume = investment_volume / len(environment.assets) + # We find if there are already transactions + # We will want one transaction of one type of investment also, as above + number_of_asset_transactions = 0 + # We go through the books + for tranx in bank.accounts: + if tranx.asset == asset_key: + # And find the transactions that match + number_of_asset_transactions = number_of_asset_transactions + 1 + # If the bank doesn't yet have an investment we create one + if number_of_asset_transactions == 0: + environment.new_transaction("investment", asset_key, bank.identifier, bank.identifier, + one_investment_volume, 0, 0, -1) + # If they do have one we just amend the amount + elif number_of_asset_transactions == 1: + for tranx in bank.accounts: + if tranx.type_ == "investment": + if tranx.asset == asset_key: + tranx.amount = one_investment_volume + # If there are multiple investments in one asset we raise an error + else: + raise LookupError("More than one transaction of the same asset.") + # ------------------------------------------------------------------------- -# -# HELPER ROUTINES -# # ------------------------------------------------------------------------- - # find_active_banks() + # invest_interbank(environment, time) + # This function checks the optimal portfolio volume for banks and their + # allocations, then looks at the current ones and makes appropriate + # investment decisions # ------------------------------------------------------------------------- - def find_active_banks(self, environment, network, time): - active_banks = [] + def invest_interbank(self, environment, time): + # TARGET VOLUME OF INVESTMENTS + # We do this for every bank + for bank in environment.banks: + # Every bank finds out what leverage they want to use by using their + # own leverage from config file unless it's prohibited by the policy + target_leverage = 0.0 + target_leverage = min(bank.target_leverage, environment.max_leverage_ratio) + # Then from the loans banks have and the leverage ration above + # We find how much the banks want to be investing + bank.state_variables["investment_volume"] = 0.0 + bank.investment_volume = bank.get_account("loans")*(target_leverage-1) + + # INTERBANK LENDING + from market import Market + # Put the appropriate settings, i.e. desired identifier + market = Market("market") + for_rationing = [] + for bank in environment.banks: + value = bank.interbank_liquidity - (bank.investment_volume - bank.get_account("loans")) + for_rationing.append([bank, value]) + rationed = market.rationing_proportional(for_rationing) + + for ration in rationed: + environment.new_transaction("ib_loans", "", ration[0].identifier, ration[1].identifier, + ration[2], 0, 0, -1) + # And print it to the screen for easy greping + print("%s lent %f worth of interbank loans to %s at time %d.") % (ration[0].identifier, + ration[2], ration[1].identifier, time) for bank in environment.banks: - if bank.parameters["active"] > -1: - active_banks.append(bank) + # Then we add or remove central bank loans + # We assume that since there is only one central bank we can keep those + # in one transaction for now, if we fiddle with maturities this may change + # If we don't yet have a central bank loan we create one + cb_volume = bank.investment_volume + for tranx in bank.accounts: + if tranx.type_ == "ib_loans": + if tranx.from_ == bank: + cb_volume = cb_volume - tranx.amount + elif tranx.to == bank: + cb_volume = cb_volume + tranx.amount + if bank.get_account_num_transactions("cb_loans") == 0: + environment.new_transaction("cb_loans", "", environment.central_bank[0].identifier, bank.identifier, + cb_volume, environment.central_bank[0].interest_rate_cb_loans, 0, -1) + # If we have a prior central bank loan we just adjust the amount + elif bank.get_account_num_transactions("cb_loans") == 1: + for tranx in bank.accounts: + if tranx.type_ == "cb_loans": + tranx.amount = cb_volume else: - network.remove_inactive_bank(bank, time) + raise LookupError("Too many central bank loans present on bank's books.") - return active_banks + for bank in environment.banks: + # Then we add or remove investments + # We do it for every investment class, for now we have 1/n portfolio + for asset_key in environment.assets: + # Thus each asset is invested in linearly, 1/n times the total inv volume + one_investment_volume = bank.investment_volume / len(environment.assets) + # We find if there are already transactions + # We will want one transaction of one type of investment also, as above + number_of_asset_transactions = 0 + # We go through the books + for tranx in bank.accounts: + if tranx.asset == asset_key: + # And find the transactions that match + number_of_asset_transactions = number_of_asset_transactions + 1 + # If the bank doesn't yet have an investment we create one + if number_of_asset_transactions == 0: + environment.new_transaction("investment", asset_key, bank.identifier, bank.identifier, + one_investment_volume, 0, 0, -1) + # If they do have one we just amend the amount + elif number_of_asset_transactions == 1: + for tranx in bank.accounts: + if tranx.type_ == "investment": + if tranx.asset == asset_key: + tranx.amount = one_investment_volume + # If there are multiple investments in one asset we raise an error + else: + raise LookupError("More than one transaction of the same asset.") # -------------------------------------------------------------------------