eu.crisis_economics.abm.ratings.NaiveMertonDistanceToDefaultAlgorithm.java Source code

Java tutorial

Introduction

Here is the source code for eu.crisis_economics.abm.ratings.NaiveMertonDistanceToDefaultAlgorithm.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.ratings;

import java.util.Map.Entry;

import org.apache.commons.math3.distribution.NormalDistribution;

import com.google.common.base.Preconditions;

import eu.crisis_economics.abm.Agent;
import eu.crisis_economics.abm.agent.SimpleAbstactAgentOperation;
import eu.crisis_economics.abm.algorithms.statistics.DiscreteTimeSeries;
import eu.crisis_economics.abm.algorithms.statistics.StdDevOfTimeSeriesStatistic;
import eu.crisis_economics.abm.contracts.Contract;
import eu.crisis_economics.abm.contracts.stocks.UniqueStockExchange;
import eu.crisis_economics.abm.simulation.Simulation;
import eu.crisis_economics.utilities.Pair;

/**
  * Compute the naive Merton 'Distance to Default' measure for an agent.
  * When applied to an agent, this measurement assumes the following:
  *   (a) that the agent is a stock releaser whose stocks are
  *       traded on the exchange, and
  *   (b) that the rating agency processing this algorithm also
  *       tracks the equity of the agent in a measurement called
  *       "Equity".
  * @author phillips
  */
public final class NaiveMertonDistanceToDefaultAlgorithm extends SimpleAbstactAgentOperation<Double>
        implements AgentTrackingMeasurement {

    private double forecastHorizon;

    public NaiveMertonDistanceToDefaultAlgorithm(final double forecastHorizon) {
        Preconditions.checkArgument(forecastHorizon > 0.);
        this.forecastHorizon = forecastHorizon;
    }

    /**
      * Compute the (Naive) Merton Distance-to-Default measure for an agent
      * in a specified forecast timeframe T.
      * 
      * @param debtFaceValue (F)
      *        The face value of the agent debt.
      * @param equityVolatility
      *        The volatility of agent equity.
      * @param equity (E)
      *        The current agent equity.
      * @param expectedAssetReturn
      *        The asset return of the agent during the last forecast window.
      * @param forecastHorizon (T)
      *        The period over which to forecast agent default.
      * @return
      *        A Pair<Double, Double> in the format:
      *        Pair<Naive Merton Distance-to-Default, Naive Merton
      *        Probability-of-Default>, in the period of the forecast timeframe.
      * 
      * It is not permissible for: both the debt face value (F) and equity (E)
      * aguments to be simultaneously zero; for the debt face value (F) to be
      * negative; or for the forecase horizon (T) to be zero or negative. If the 
      * debt face value is zero and equity is nonzero, then the distance to 
      * default is taken to be +Infinity.
      */
    static Pair<Double, Double> compute(final double debtFaceValue, final double equityVolatility,
            final double equity, final double expectedAssetReturn, final double forecastHorizon) {
        Preconditions.checkArgument(equity != 0. || debtFaceValue > 0.);
        Preconditions.checkArgument(forecastHorizon > 0.);
        Preconditions.checkArgument(debtFaceValue >= 0.);
        final double debtVolatility = .05 + .25 * equityVolatility,
                overallValueVolatility = equityVolatility * equity / (equity + debtFaceValue)
                        + debtVolatility * debtFaceValue / (equity + debtFaceValue);
        double distanceToDefault = Math.log((equity + debtFaceValue) / debtFaceValue)
                + (expectedAssetReturn - .5 * overallValueVolatility * overallValueVolatility) * forecastHorizon;
        distanceToDefault /= Math.sqrt(forecastHorizon) * overallValueVolatility;
        NormalDistribution normalDist = new NormalDistribution();
        final double defaultProbability = normalDist.cumulativeProbability(-distanceToDefault);
        return Pair.create(distanceToDefault, defaultProbability);
    }

    @Override
    public Double operateOn(final Agent agent) {
        double debtFaceValue = 0., equity = agent.getEquity(), equityVolatility = getEquityVolatility(agent),
                expectedAssetReturn = getExpectedAssetReturn(agent), forecastHorizon = this.forecastHorizon;
        for (final Contract contract : agent.getLiabilities())
            debtFaceValue += contract.getFaceValue();
        final double pDef = compute(debtFaceValue, equityVolatility, equity, expectedAssetReturn, forecastHorizon)
                .getSecond();
        return pDef;
    }

    /**
      * Form a fundamentalist estimate of asset returns for the specified
      * agent. The asset return of the agent is estimated using fundamentalist
      * stock market returns in the last year (simulation time). For this 
      * reason, the agent must be stock releaser whose stocks are traded on
      * the stock exchange.
      * 
      * If stock market price per share/dividend per share information is not
      * available, or if this method is unable to estimate the stock market
      * return for the specified agent, the value 1.0 is returned.
      */
    private double getExpectedAssetReturn(final Agent agent) {
        if (!UniqueStockExchange.Instance.hasStock(agent.getUniqueName()))
            throw new IllegalArgumentException(
                    "NaiveMertonDistanceToDefaultAlgorithm: agent must be a stock releaser, however the"
                            + "agent is not listed on the stock exhange.");
        final double timeOfInterest = Math.max(Simulation.getTime() - forecastHorizon, 0.);
        final DiscreteTimeSeries pricePerShareTimeSeries = RatingAgency.Instance.getTimeSeries("Price Per Share",
                agent),
                dividendPerShareTimeSeries = RatingAgency.Instance.getTimeSeries("Dividend Per Share", agent);
        if (pricePerShareTimeSeries.isEmpty() || dividendPerShareTimeSeries.isEmpty())
            return 1.0; // No information about asset returns.
        final Entry<Double, Double> ppsRecord = pricePerShareTimeSeries.ceilingEntry(timeOfInterest),
                dpsRecord = dividendPerShareTimeSeries.ceilingEntry(timeOfInterest);
        if (ppsRecord.getValue() == 0.)
            return 1.0; // Stock market crashed.
        return dpsRecord.getValue() / ppsRecord.getValue(); // Fundamentalist return estimate.
    }

    /**
      * Get the standard deviation of agent equity in the last simulated
      * year. If records do not reach as far back as one year, all available
      * records are used.
      * @return
      */
    private double getEquityVolatility(final Agent agent) {
        final DiscreteTimeSeries series = RatingAgency.Instance.getTimeSeries("Equity", agent);
        if (series == null)
            return 0.; // No historical equity data.
        final double latestTimeOfInterest = Math.max(Simulation.getTime() - forecastHorizon, 0.);
        DiscreteTimeSeries subseries = series.tailMap(latestTimeOfInterest, true);
        return (new StdDevOfTimeSeriesStatistic()).measureStatistic(subseries);
    }

    /**
      * Get the forecast horizon for this distance to default measurement.
      */
    public double getForecastHorizon() {
        return forecastHorizon;
    }

    /**
      * Set the forecast horizon for this distance to default measurement.
      * The forecast horizon must be strictly positive.
      */
    public void setForecastHorizon(double value) {
        Preconditions.checkArgument(value > 0.);
        this.forecastHorizon = value;
    }

    /**
      * Returns a brief description of this object. The exact details of the
      * string are subject to change, and should not be regarded as fixed.
      */
    @Override
    public String toString() {
        return "NaiveMertonDistanceToDefaultAlgorithm, forecast horizon: " + forecastHorizon + ".";
    }
}