blob: 4b05329595c7128331645b22c86f80d86951eb9a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ignite.examples.computegrid.montecarlo;
import java.util.Arrays;
import java.util.Random;
/**
* This class abstracts out the calculation of risk for a credit portfolio.
*/
@SuppressWarnings({"FloatingPointEquality"})
public class CreditRiskManager {
/**
* Default randomizer with normal distribution.
* Note that since every JVM on the cluster will have its own random
* generator (independently initialized) the Monte-Carlo simulation
* will be slightly skewed when performed on the ignite cluster due to skewed
* normal distribution of the sub-jobs comparing to execution on the
* local node only with single random generator. Real-life applications
* may want to provide its own implementation of distributed random
* generator.
*/
private static Random rndGen = new Random();
/**
* Calculates credit risk for a given credit portfolio. This calculation uses
* Monte-Carlo Simulation to produce risk value.
*
* @param portfolio Credit portfolio.
* @param horizon Forecast horizon (in days).
* @param num Number of Monte-Carlo iterations.
* @param percentile Cutoff level.
* @return Credit risk value, i.e. the minimal amount that creditor has to
* have available to cover possible defaults.
*/
public double calculateCreditRiskMonteCarlo(Credit[] portfolio, int horizon, int num, double percentile) {
System.out.println(">>> Calculating credit risk for portfolio [size=" + portfolio.length + ", horizon=" +
horizon + ", percentile=" + percentile + ", iterations=" + num + "] <<<");
long start = System.currentTimeMillis();
double[] losses = calculateLosses(portfolio, horizon, num);
Arrays.sort(losses);
double[] lossProbs = new double[losses.length];
// Count variational numbers.
// Every next one either has the same value or previous one plus probability of loss.
for (int i = 0; i < losses.length; i++)
if (i == 0)
// First time it's just a probability of first value.
lossProbs[i] = getLossProbability(losses, 0);
else if (losses[i] != losses[i - 1])
// Probability of this loss plus previous one.
lossProbs[i] = getLossProbability(losses, i) + lossProbs[i - 1];
else
// The same loss the same probability.
lossProbs[i] = lossProbs[i - 1];
// Count percentile.
double crdRisk = 0;
for (int i = 0; i < lossProbs.length; i++)
if (lossProbs[i] > percentile) {
crdRisk = losses[i - 1];
break;
}
System.out.println(">>> Finished calculating portfolio risk [risk=" + crdRisk +
", time=" + (System.currentTimeMillis() - start) + "ms]");
return crdRisk;
}
/**
* Calculates losses for the given credit portfolio using Monte-Carlo Simulation.
* Simulates probability of default only.
*
* @param portfolio Credit portfolio.
* @param horizon Forecast horizon.
* @param num Number of Monte-Carlo iterations.
* @return Losses array simulated by Monte Carlo method.
*/
private double[] calculateLosses(Credit[] portfolio, int horizon, int num) {
double[] losses = new double[num];
// Count losses using Monte-Carlo method. We generate random probability of default,
// if it exceeds certain credit default value we count losses - otherwise count income.
for (int i = 0; i < num; i++)
for (Credit crd : portfolio) {
int remDays = Math.min(crd.getRemainingTerm(), horizon);
if (rndGen.nextDouble() >= 1 - crd.getDefaultProbability(remDays))
// (1 + 'r' * min(H, W) / 365) * S.
// Where W is a horizon, H is a remaining crediting term, 'r' is an annual credit rate,
// S is a remaining credit amount.
losses[i] += (1 + crd.getAnnualRate() * Math.min(horizon, crd.getRemainingTerm()) / 365)
* crd.getRemainingAmount();
else
// - 'r' * min(H,W) / 365 * S
// Where W is a horizon, H is a remaining crediting term, 'r' is a annual credit rate,
// S is a remaining credit amount.
losses[i] -= crd.getAnnualRate() * Math.min(horizon, crd.getRemainingTerm()) / 365 *
crd.getRemainingAmount();
}
return losses;
}
/**
* Calculates probability of certain loss in array of losses.
*
* @param losses Array of losses.
* @param i Index of certain loss in array.
* @return Probability of loss with given index.
*/
private double getLossProbability(double[] losses, int i) {
double cnt = 0;
double loss = losses[i];
for (double tmp : losses)
if (loss == tmp)
cnt++;
return cnt / losses.length;
}
}