eu.crisis_economics.abm.markets.clearing.heterogeneous.ClearingGiltsBondsAndCommercialLoansMarket.java Source code

Java tutorial

Introduction

Here is the source code for eu.crisis_economics.abm.markets.clearing.heterogeneous.ClearingGiltsBondsAndCommercialLoansMarket.java

Source

/*
 * This file is part of CRISIS, an economics simulator.
 * 
 * Copyright (C) 2015 Ross Richardson
 * Copyright (C) 2015 John Kieran Phillips
 *
 * CRISIS 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, either version 3 of the License, or
 * (at your option) any later version.
 *
 * CRISIS 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 CRISIS.  If not, see <http://www.gnu.org/licenses/>.
 */
package eu.crisis_economics.abm.markets.clearing.heterogeneous;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.collections15.map.HashedMap;

import com.google.common.collect.TreeMultiset;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;

import eu.crisis_economics.abm.contracts.loans.Borrower;
import eu.crisis_economics.abm.contracts.loans.Lender;
import eu.crisis_economics.abm.contracts.loans.LenderInsufficientFundsException;
import eu.crisis_economics.abm.contracts.loans.LoanFactory;
import eu.crisis_economics.abm.firm.LoanStrategyFirm;
import eu.crisis_economics.abm.government.Government;
import eu.crisis_economics.abm.markets.clearing.ClearingHouse;
import eu.crisis_economics.abm.markets.clearing.PeerToPeerSaleAlgorithm;
import eu.crisis_economics.abm.markets.clearing.UpfrontInjectionLoanDistributionAlgorithm;
import eu.crisis_economics.abm.markets.clearing.PeerToPeerSaleAlgorithm.SaleExecutionDelegate;
import eu.crisis_economics.abm.simulation.NamedEventOrderings;
import eu.crisis_economics.abm.simulation.ScheduleIntervals;
import eu.crisis_economics.abm.simulation.Simulation;
import eu.crisis_economics.utilities.Pair;

/** A network {@link ClearingMarket} involving:<br><br>
  * 
  *   {@code (a)}
  *       pure heterogeneous bonds, with interest rate set according to
  *       each borrower/lender pair.<br>
  *   {@code (b)}
  *       multiple grades of homogeneous commercial loan resources, with
  *       interest rate set according to the risk assessment of the 
  *       supplier.<br><br>
  * 
  * In this implementation, commercial loan risk grades are consist of
  * three bins ("Low Risk", "Medium Risk" and "High Risk"). Commercial loan
  * consumers are binned into risk categories according to their equity as
  * compared to all other consumers. The lowest equity consumer is deemed
  * to have the highest commerical loan risk profile.
  * 
  * @author phillips
  */
public final class ClearingGiltsBondsAndCommercialLoansMarket extends AbstractClearingMarket
        implements ClearingLoanMarket {

    private final Map<String, Boolean> bankIsParticipating, firmIsParticipating, fundIsParticipating;

    private List<String> commercialLoanRiskGrades;

    private final Map<String, ClearingInstrument> instruments;
    private final Map<ClearingInstrument, Double> lastTradeVolumes;
    private final Map<ClearingInstrument, Double> lastTradeWeightedReturnRates;

    private final Map<String, List<ClearingMarketParticipant>> commericalLoanClientRiskBuckets;

    @Inject
    public ClearingGiltsBondsAndCommercialLoansMarket(@Assisted final String marketName,
            @Assisted final ClearingHouse clearingHouse) {
        super(marketName, clearingHouse);
        this.bankIsParticipating = new HashMap<String, Boolean>();
        this.firmIsParticipating = new HashMap<String, Boolean>();
        this.fundIsParticipating = new HashMap<String, Boolean>();
        this.commercialLoanRiskGrades = Arrays.asList("Low Risk", "Medium Risk", "High Risk");
        this.instruments = new HashMap<String, ClearingInstrument>();
        this.lastTradeVolumes = new HashMap<ClearingInstrument, Double>();
        this.lastTradeWeightedReturnRates = new HashMap<ClearingInstrument, Double>();
        this.commericalLoanClientRiskBuckets = new HashedMap<String, List<ClearingMarketParticipant>>();

        for (final String riskGrade : commercialLoanRiskGrades)
            addInstrument(riskGrade + " Commercial Loan", instruments);
        addInstrument("Gilt", instruments);
        addInstrument("Bank Bond", instruments);
        for (final ClearingInstrument instrument : instruments.values()) {
            lastTradeVolumes.put(instrument, 0.);
            lastTradeWeightedReturnRates.put(instrument, 0.);
        }
    }

    /**
      * Implementation detail. Add a {@link ClearingInstrument} to this market.
      * 
      * @param instrumentName
      *        The name of the instrument to add.
      * @param store
      *        The {@link Map} in which to store the resulting {@link ClearingInstrument}.
      */
    private void addInstrument(final String instrumentName, final Map<String, ClearingInstrument> store) {
        store.put(instrumentName, new ClearingInstrument(getMarketName(), instrumentName));
    }

    /**
      * Assign commerical loan consumers to risk grades according
      * to their equity. In this implementation, the number of 
      * consumer assigned to each bucket is approximately equal.
      */
    private void assignCommericalLoanClientsToRiskBuckets() {
        final int numRiskBuckets = commercialLoanRiskGrades.size();
        final List<ClearingMarketParticipant> firms = getAllPossibleCommericalLoanConsumers();

        class SortedEquityID implements Comparable<SortedEquityID> {
            private double equity;
            private ClearingMarketParticipant firm;

            SortedEquityID(final double equity, final ClearingMarketParticipant firm) {
                this.equity = equity;
                this.firm = firm;
            }

            @Override
            public int compareTo(SortedEquityID other) {
                return Double.compare(this.equity, other.equity);
            }
        }

        final TreeMultiset<SortedEquityID> firmEquities = TreeMultiset.create();
        for (final ClearingMarketParticipant participant : firms) {
            final LoanStrategyFirm firm = (LoanStrategyFirm) participant; // Contracted by ClearingHouse
            firmEquities.add(new SortedEquityID(firm.getEquity(), participant));
        }
        final int numFirms = firms.size(),
                firmsPerRiskBucket = (int) Math.floor(numFirms / (double) numRiskBuckets);
        int firmCounter = 0, riskGradeIndex = 0;
        String riskGrade = commercialLoanRiskGrades.get(0);
        for (final SortedEquityID record : firmEquities) {
            commericalLoanClientRiskBuckets.get(riskGrade).add(record.firm);
            ++firmCounter;
            if (firmCounter == firmsPerRiskBucket && !(riskGradeIndex == numRiskBuckets - 1)) {
                firmCounter = 0;
                ++riskGradeIndex;
                riskGrade = commercialLoanRiskGrades.get(riskGradeIndex);
            }
        }
        return;
    }

    /**
      * Process the Commerical Loan/Bond market and commit to contracts.
      */
    @Override
    public void process() {
        final ResourceExchangeAggregator bondTradeAggregator = new SimpleResourceExchangeAggregator();
        final Map<String, ResourceExchangeAggregator> gradedCommercianLoanAggregators = new HashedMap<String, ResourceExchangeAggregator>();
        final ResourceExchangeAggregator giltTradeAggregator = new SimpleResourceExchangeAggregator();

        // Build network
        MixedClearingNetwork.Builder networkBuilder = new MixedClearingNetwork.Builder();

        // Add risk grades for commerical loan consumers.
        for (final String riskGrade : commercialLoanRiskGrades)
            commericalLoanClientRiskBuckets.put(riskGrade, new ArrayList<ClearingMarketParticipant>());

        for (final String riskGrade : commericalLoanClientRiskBuckets.keySet()) {
            gradedCommercianLoanAggregators.put(riskGrade, new SimpleResourceExchangeAggregator());
        }

        bankIsParticipating.clear();
        firmIsParticipating.clear();
        fundIsParticipating.clear();

        // Assign Firms to risk buckets
        assignCommericalLoanClientsToRiskBuckets();

        // Install Banks, Firms and Funds
        int numSuppliers = addBondSuppliersToNetwork(networkBuilder),
                numConsumers = addCommercialLoanConsumersToNetwork(networkBuilder),
                numIntermediaries = addLoanRelayersToNetwork(networkBuilder);

        if (numSuppliers == 0 || numIntermediaries == 0 || numConsumers == 0) {
            System.out.printf("---------------------------------------------\n"
                    + "Commercial Loan/Bond Network Cleared [Time: %g]\n" + " No trade pathways in this session.\n"
                    + " Bond Suppliers: %d\n" + " Intermediaries: %d\n" + " Commercial Loan Consumers: %d\n"
                    + "---------------------------------------------\n", Simulation.getTime(), numSuppliers,
                    numIntermediaries, numConsumers);
            return;
        }

        final boolean doIncludeGiltSubnetwork = tryAddGovernmentNodeToNetwork(networkBuilder);

        // Connect Bond trade routes for banks and funds.
        addFundBondEdgesToNetwork(networkBuilder, bondTradeAggregator);

        // Create Commercial loan trade routes for firm and banks.
        addCommercialLoanEdgesToNetwork(networkBuilder, gradedCommercianLoanAggregators);

        // If applicable, create gilt/government bond trade routes for government.
        if (doIncludeGiltSubnetwork)
            addGovernmentBondSubnetworkEdges(networkBuilder, giltTradeAggregator);

        // Build the network and apply a clearing algorithm.
        final MixedClearingNetwork network = networkBuilder.build();
        final MixedClearingNetworkAlgorithm clearingAlgorithm = new AdaptiveMarchHeterogeneousClearingAlgorithm(60,
                1.e-8, new OrderOrIterationsStoppingCondition(10, 30, network));

        network.applyClearingAlgorithm(clearingAlgorithm);

        network.createContracts();

        // Commit bond trades
        commitBankBondContracts(bondTradeAggregator);

        // Commit commercial loan trades
        for (final Entry<String, ResourceExchangeAggregator> record : gradedCommercianLoanAggregators.entrySet()) {
            commitCommercialLoanContracts(record.getValue(), record.getKey());
        }

        // If applicable, commit government gilt contracts
        if (doIncludeGiltSubnetwork)
            commitGovernmentBondContracts(giltTradeAggregator);

        commericalLoanClientRiskBuckets.clear();
    }

    private void commitCommercialLoanContracts(final ResourceExchangeAggregator aggregator,
            final String riskGrade) {
        ResourceDistributionAlgorithm<Borrower, Lender> assignmentAlgorithm = new UpfrontInjectionLoanDistributionAlgorithm();
        @SuppressWarnings("unchecked")
        final Pair<Double, Double> summary = assignmentAlgorithm
                .distributeResources((Map<String, Borrower>) (Object) // Contracted by ClearingHouse
        aggregator.getKeyedConsumers(), (Map<String, Lender>) (Object) // Contracted by ClearingHouse
        aggregator.getKeyedSuppliers(), aggregator.getUnmodifiableKeyedResourceExchanges());

        double lastTradeWeightedLoanReturnRate = 0, lastTotalDesiredLoanTrade = 0.;

        lastTradeWeightedLoanReturnRate += summary.getFirst() * summary.getSecond();
        lastTotalDesiredLoanTrade += summary.getFirst();
        lastTradeWeightedLoanReturnRate = (lastTotalDesiredLoanTrade == 0. ? 0.
                : lastTradeWeightedLoanReturnRate / lastTotalDesiredLoanTrade);

        lastTradeVolumes.put(instruments.get(riskGrade + " Commercial Loan"), lastTotalDesiredLoanTrade);
        lastTradeWeightedReturnRates.put(instruments.get(riskGrade + " Commercial Loan"),
                lastTradeWeightedLoanReturnRate);

        { // Commercial Loan trade results
            double commericalLoanTotalDemand = aggregator.getTotalConsumerDemand(),
                    commericalLoanTotalSupply = aggregator.getTotalSupplierSupply();
            System.out.printf(
                    "---------------------------------------------\n"
                            + "Commerical Loan Market Cleared [Time: %g]\n" + " Grade: %s\n"
                            + " Demand:                             %8.3g\n"
                            + " Supply:                             %8.3g\n"
                            + " Abs. Diff.:                         %8.3g\n"
                            + " Rate:                               %8.3g\n"
                            + " Created:                            %8.3g\n"
                            + "---------------------------------------------\n",
                    Simulation.getTime(), riskGrade, commericalLoanTotalDemand, commericalLoanTotalSupply,
                    Math.abs(commericalLoanTotalDemand - commericalLoanTotalSupply), summary.getSecond(),
                    summary.getFirst());
            System.out.flush();
        }
    }

    private void commitBankBondContracts(final ResourceExchangeAggregator aggregator) {
        PeerToPeerSaleAlgorithm.SaleExecutionDelegate<Borrower, Lender> saleExecutationDelegate = new SaleExecutionDelegate<Borrower, Lender>() {
            @Override
            public double processSale(final Borrower consumer, final Lender supplier,
                    final Pair<Double, Double> resourceExchange) {
                try {
                    LoanFactory.createCouponBond(consumer, supplier, resourceExchange.getFirst(),
                            resourceExchange.getSecond(), 1, NamedEventOrderings.BOND_PAYMENTS,
                            ScheduleIntervals.ONE_DAY);
                    return resourceExchange.getFirst();
                } catch (final LenderInsufficientFundsException e) {
                    final String warningMsg = "Bond Clearing Market: bond supplier " + supplier.getUniqueName()
                            + "has insufficient funds to transfer a bond principal of value "
                            + resourceExchange.getFirst() + ".";
                    System.err.println(warningMsg); // Supplier insufficient liquidity.
                }
                return 0.;
            }
        };
        PeerToPeerSaleAlgorithm<Borrower, Lender> bondExchangeAlgorithm = new PeerToPeerSaleAlgorithm<Borrower, Lender>(
                saleExecutationDelegate);

        @SuppressWarnings("unchecked")
        final Pair<Double, Double> summary = bondExchangeAlgorithm
                .distributeResources((Map<String, Borrower>) (Object) // Contracted by ClearingHouse
        aggregator.getKeyedConsumers(), (Map<String, Lender>) (Object) // Contracted by ClearingHouse
        aggregator.getKeyedSuppliers(), aggregator.getUnmodifiableKeyedResourceExchanges());

        double lastTradeWeightedBondReturnRate = 0, lastTotalDesiredBondTrade = 0.;

        lastTradeWeightedBondReturnRate += summary.getFirst() * summary.getSecond();
        lastTotalDesiredBondTrade += summary.getFirst();
        lastTradeWeightedBondReturnRate = (lastTotalDesiredBondTrade == 0. ? 0.
                : lastTradeWeightedBondReturnRate / lastTotalDesiredBondTrade);

        lastTradeVolumes.put(instruments.get("Bank Bond"), lastTotalDesiredBondTrade);
        lastTradeWeightedReturnRates.put(instruments.get("Bank Bond"), lastTradeWeightedBondReturnRate);

        { // Bond Trade Results
            double bondTotalDemand = aggregator.getTotalConsumerDemand(),
                    bondTotalSupply = aggregator.getTotalSupplierSupply();
            System.out.printf(
                    "---------------------------------------------\n" + "Bond Market Cleared [Time: %g]\n"
                            + " Demand:                             %8.3g\n"
                            + " Supply:                             %8.3g\n"
                            + " Abs. Diff.:                         %8.3g\n"
                            + " Rate:                               %8.3g\n"
                            + " Created:                            %8.3g\n"
                            + "---------------------------------------------\n",
                    Simulation.getTime(), bondTotalDemand, bondTotalSupply,
                    Math.abs(bondTotalDemand - bondTotalSupply), summary.getSecond(), summary.getFirst());
            System.out.flush();
        }
    }

    /**
      * Create gilt/government bond contracts.
      */
    private void commitGovernmentBondContracts(final ResourceExchangeAggregator aggregator) {
        PeerToPeerSaleAlgorithm.SaleExecutionDelegate<Borrower, Lender> saleExecutationDelegate = new SaleExecutionDelegate<Borrower, Lender>() {
            @Override
            public double processSale(final Borrower consumer, final Lender supplier,
                    final Pair<Double, Double> resourceExchange) {
                try {
                    LoanFactory.createCouponBond(consumer, supplier, resourceExchange.getFirst(),
                            resourceExchange.getSecond(), 1, NamedEventOrderings.GILT_PAYMENTS,
                            ScheduleIntervals.ONE_DAY);
                    return resourceExchange.getFirst();
                } catch (final LenderInsufficientFundsException e) {
                    final String warningMsg = "Bond Clearing Market: bond supplier " + supplier.getUniqueName()
                            + "has insufficient funds to transfer a bond principal of value "
                            + resourceExchange.getFirst() + ".";
                    System.err.println(warningMsg); // Supplier insufficient liquidity.
                }
                return 0.;
            }
        };
        PeerToPeerSaleAlgorithm<Borrower, Lender> bondExchangeAlgorithm = new PeerToPeerSaleAlgorithm<Borrower, Lender>(
                saleExecutationDelegate);

        @SuppressWarnings("unchecked")
        final Pair<Double, Double> summary = bondExchangeAlgorithm
                .distributeResources((Map<String, Borrower>) (Object) // Contracted by ClearingHouse
        aggregator.getKeyedConsumers(), (Map<String, Lender>) (Object) // Contracted by ClearingHouse
        aggregator.getKeyedSuppliers(), aggregator.getUnmodifiableKeyedResourceExchanges());

        double lastTradeWeightedGiltReturnRate = 0, lastTotalDesiredGiltTrade = 0.;

        lastTradeWeightedGiltReturnRate += summary.getFirst() * summary.getSecond();
        lastTotalDesiredGiltTrade += summary.getFirst();
        lastTradeWeightedGiltReturnRate = (lastTotalDesiredGiltTrade == 0. ? 0.
                : lastTradeWeightedGiltReturnRate / lastTotalDesiredGiltTrade);

        lastTradeVolumes.put(instruments.get("Gilt"), lastTotalDesiredGiltTrade);
        lastTradeWeightedReturnRates.put(instruments.get("Gilt"), lastTradeWeightedGiltReturnRate);

        { // Bond Trade Results
            double bondTotalDemand = aggregator.getTotalConsumerDemand(),
                    bondTotalSupply = aggregator.getTotalSupplierSupply();
            System.out.printf(
                    "---------------------------------------------\n" + "Gilt Market Cleared [Time: %g]\n"
                            + " Demand:                             %8.3g\n"
                            + " Supply:                             %8.3g\n"
                            + " Abs. Diff.:                         %8.3g\n"
                            + " Rate:                               %8.3g\n"
                            + " Created:                            %8.3g\n"
                            + "---------------------------------------------\n",
                    Simulation.getTime(), bondTotalDemand, bondTotalSupply,
                    Math.abs(bondTotalDemand - bondTotalSupply), summary.getSecond(), summary.getFirst());
            System.out.flush();
        }
    }

    /**
      * Get an unmodifiable list of all possible commerical loan consumers.
      */
    private List<ClearingMarketParticipant> getAllPossibleCommericalLoanConsumers() {
        return Collections.unmodifiableList(
                new ArrayList<ClearingMarketParticipant>(super.getClearingHouse().getFirms().values()));
    }

    /**
      * Get an unmodifiable list of all possible commerical loan lenders.
      */
    private List<ClearingMarketParticipant> getAllPossibleCommercialLoanLenders() {
        return Collections.unmodifiableList(
                new ArrayList<ClearingMarketParticipant>(super.getClearingHouse().getBanks().values()));
    }

    /**
      * Generate a map of commerical loan instruments keyed by risk grade.
      */
    private Map<String, ClearingInstrument> getCommericalLoanInstruments() {
        final Map<String, ClearingInstrument> commericalLoanInstruments = new HashMap<String, ClearingInstrument>();
        for (final String riskGrade : commericalLoanClientRiskBuckets.keySet())
            commericalLoanInstruments.put(riskGrade, instruments.get(riskGrade + " Commercial Loan"));
        return commericalLoanInstruments;
    }

    /**
      * Generate a list of gilt resources.
      */
    private List<ClearingInstrument> getGiltResources() {
        List<ClearingInstrument> giltResources = new ArrayList<ClearingInstrument>();
        giltResources.add(new ClearingInstrument(getMarketName(), "Gilt"));
        return giltResources;
    }

    /**
      * Get an unmodifiable list of all possible bond lenders.
      */
    private List<ClearingMarketParticipant> getAllPossibleBondLenders() {
        return Collections.unmodifiableList(
                new ArrayList<ClearingMarketParticipant>(super.getClearingHouse().getFunds().values()));
    }

    /**
      * Generate a list of bond resources (typed by fund suppliers)
      */
    private List<ClearingInstrument> getBondResources() {
        List<ClearingInstrument> bondResources = new ArrayList<ClearingInstrument>();
        bondResources.add(new ClearingInstrument(getMarketName(), "Bank Bond"));
        return bondResources;
    }

    /**
      * Add commercial loan consumers to the clearing network. In this
      * implementation, commercial loan consumers are only allowed to
      * participate in one submarket corresponding to their risk grade.
      */
    private int addCommercialLoanConsumersToNetwork(final MixedClearingNetwork.Builder network) {
        final Map<String, ClearingInstrument> commericalLoanResources = getCommericalLoanInstruments();
        int numberOfParticipants = 0;
        for (final String riskGrade : commericalLoanClientRiskBuckets.keySet()) {
            final ArrayList<ClearingInstrument> resources = new ArrayList<ClearingInstrument>();
            resources.add(commericalLoanResources.get(riskGrade));
            for (final ClearingMarketParticipant firm : commericalLoanClientRiskBuckets.get(riskGrade)) {
                final MarketResponseFunction responseFunction = firm
                        .getMarketResponseFunction(getSummaryInformation());
                if (responseFunction == null) {
                    firmIsParticipating.put(firm.getUniqueName(), false);
                    continue;
                }
                firmIsParticipating.put(firm.getUniqueName(), true);
                network.addNetworkNode(firm, responseFunction, firm.getUniqueName());
                ++numberOfParticipants;
            }
        }
        return numberOfParticipants;
    }

    /**
      * Add intermediaries (bond liability/commercial loan asset holders)
      * to the clearing network.
      */
    private int addLoanRelayersToNetwork(final MixedClearingNetwork.Builder network) {
        final List<ClearingMarketParticipant> knownBanks = getAllPossibleCommercialLoanLenders();
        final List<ClearingInstrument> probeResourceList = new ArrayList<ClearingInstrument>();
        probeResourceList.add(new ClearingInstrument(getMarketName(), "Bank Bond"));
        probeResourceList.add(new ClearingInstrument(getMarketName(), "Commercial Loan"));
        int numberOfParticipants = 0;
        for (int i = 0; i < knownBanks.size(); ++i) {
            final ClearingMarketParticipant bank = knownBanks.get(i);
            MarketResponseFunction responseFunction = bank.getMarketResponseFunction(getSummaryInformation());
            if (responseFunction == null) {
                bankIsParticipating.put(bank.getUniqueName(), false);
                continue;
            }
            bankIsParticipating.put(bank.getUniqueName(), true);
            network.addNetworkNode(bank, responseFunction, bank.getUniqueName());
            ++numberOfParticipants;
        }
        return numberOfParticipants;
    }

    /**
      * Add bond suppliers to the network.
      */
    private int addBondSuppliersToNetwork(final MixedClearingNetwork.Builder network) {
        final List<ClearingMarketParticipant> knownFunds = getAllPossibleBondLenders();
        final ClearingInstrument probeResource = new ClearingInstrument(getMarketName(), "Bank Bond");
        final List<ClearingInstrument> probeResourceList = new ArrayList<ClearingInstrument>();
        probeResourceList.add(probeResource);
        int numberOfParticipants = 0;
        for (int i = 0; i < knownFunds.size(); ++i) {
            final ClearingMarketParticipant fund = knownFunds.get(i);
            MarketResponseFunction responseFunction = fund.getMarketResponseFunction(getSummaryInformation());
            if (responseFunction == null) {
                fundIsParticipating.put(fund.getUniqueName(), false);
                continue;
            }
            fundIsParticipating.put(fund.getUniqueName(), true);
            network.addNetworkNode(fund, responseFunction, fund.getUniqueName());
            ++numberOfParticipants;
        }
        return numberOfParticipants;
    }

    /**
      * Try to add a government node (trading in gilts) to the network.
      * @return
      *      true if and only if a government agent exists, and it
      *      intends to participate in a government bond/gilt submarket.
      */
    private boolean tryAddGovernmentNodeToNetwork(MixedClearingNetwork.Builder network) {
        if (!Simulation.getRunningModel().hasGovernment())
            return false;
        final Government government = Simulation.getRunningModel().getGovernment();
        final MarketResponseFunction marketResponseFunction = government
                .getMarketResponseFunction(getSummaryInformation());
        if (marketResponseFunction == null)
            return false;
        network.addNetworkNode(government, marketResponseFunction, government.getUniqueName());
        return true;
    }

    /**
      * Add MutualFund Bond resources to the network.
      */
    private void addFundBondEdgesToNetwork(final MixedClearingNetwork.Builder network,
            final ResourceExchangeDelegate bondTradeDelegate) {
        final List<ClearingInstrument> bondResources = getBondResources();
        final List<ClearingMarketParticipant> knownBanks = getAllPossibleCommercialLoanLenders(),
                knownFunds = getAllPossibleBondLenders();

        // Add bonds to the network.
        {
            network.addHyperEdge("Bank Bond");
            final ClearingInstrument resource = bondResources.get(0);
            for (int i = 0; i < knownBanks.size(); ++i) {
                final ClearingMarketParticipant bank = knownBanks.get(i);
                if (!bankIsParticipating.get(bank.getUniqueName()))
                    continue;
                for (int j = 0; j < knownFunds.size(); ++j) {
                    final ClearingMarketParticipant fund = knownFunds.get(j);
                    if (!fundIsParticipating.get(fund.getUniqueName()))
                        continue;
                    network.addToHyperEdge(bank.getUniqueName(), fund.getUniqueName(), bondTradeDelegate,
                            "Bank Bond", resource);
                }
            }
        }

    }

    /**
      * Add Commercial Loan resources to the network.
      */
    private void addCommercialLoanEdgesToNetwork(MixedClearingNetwork.Builder network,
            Map<String, ResourceExchangeAggregator> commericalLoanTradeDelegates) {
        final Map<String, ClearingInstrument> commericalLoanResources = getCommericalLoanInstruments();
        final List<ClearingMarketParticipant> knownBanks = getAllPossibleCommercialLoanLenders();

        // Add commerical loans to the network.
        for (final String riskGrade : commericalLoanClientRiskBuckets.keySet()) {
            { // Are any firms in this risk bucket participating? If not, omit the bucket.
                boolean riskBucketHasFirmParticipant = false;
                for (final ClearingMarketParticipant firm : commericalLoanClientRiskBuckets.get(riskGrade)) {
                    if (!firmIsParticipating.get(firm.getUniqueName()))
                        continue;
                    else {
                        riskBucketHasFirmParticipant = true;
                        break;
                    }
                }
                if (!riskBucketHasFirmParticipant)
                    continue;
            }
            network.addHyperEdge(riskGrade);
            final ResourceExchangeDelegate delegate = commericalLoanTradeDelegates.get(riskGrade);
            final ClearingInstrument resource = commericalLoanResources.get(riskGrade);
            for (int i = 0; i < knownBanks.size(); ++i) {
                final ClearingMarketParticipant bank = knownBanks.get(i);
                if (!bankIsParticipating.get(bank.getUniqueName()))
                    continue;
                for (final ClearingMarketParticipant firm : commericalLoanClientRiskBuckets.get(riskGrade)) {
                    if (!firmIsParticipating.get(firm.getUniqueName()))
                        continue;
                    network.addToHyperEdge(firm.getUniqueName(), bank.getUniqueName(), delegate, riskGrade,
                            resource);
                }
            }
        }
    }

    /**
      * Add gilts/government bond trade edges to the network.
      */
    private void addGovernmentBondSubnetworkEdges(MixedClearingNetwork.Builder network,
            ResourceExchangeDelegate giltTradeDelegate) {
        final Government government = Simulation.getRunningModel().getGovernment();
        final List<ClearingMarketParticipant> knownBanks = getAllPossibleCommercialLoanLenders(),
                knownFunds = getAllPossibleBondLenders();
        final ClearingInstrument resource = getGiltResources().get(0);
        network.addHyperEdge("Gilt");

        // Add banks to the gilt subnetwork
        for (int i = 0; i < knownBanks.size(); ++i) {
            final ClearingMarketParticipant bank = knownBanks.get(i);
            if (!bankIsParticipating.get(bank.getUniqueName()))
                continue;
            network.addToHyperEdge(government.getUniqueName(), bank.getUniqueName(), giltTradeDelegate, "Gilt",
                    resource);
        }

        // Add funds to the gilt subnetwork
        for (int i = 0; i < knownFunds.size(); ++i) {
            final ClearingMarketParticipant fund = knownFunds.get(i);
            if (!fundIsParticipating.get(fund.getUniqueName()))
                continue;
            network.addToHyperEdge(government.getUniqueName(), fund.getUniqueName(), giltTradeDelegate, "Gilt",
                    resource);
        }
    }

    @Override
    public ClearingMarketInformation getSummaryInformation() {
        final ClearingMarketInformation result = new ClearingMarketInformation(this);
        for (final ClearingInstrument instrument : instruments.values())
            result.add(instrument, lastTradeWeightedReturnRates.get(instrument), lastTradeVolumes.get(instrument));
        return result;
    }

    @Override
    public Set<ClearingInstrument> getInstruments() {
        final Set<ClearingInstrument> result = new HashSet<ClearingInstrument>();
        result.addAll(instruments.values());
        return result;
    }
}