| /* |
| * 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; |
| } |
| } |