com.gordoni.opal.ScenarioSet.java Source code

Java tutorial

Introduction

Here is the source code for com.gordoni.opal.ScenarioSet.java

Source

/*
 * AACalc - Asset Allocation Calculator
 * Copyright (C) 2009, 2011-2015 Gordon Irlam
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.gordoni.opal;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.InputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.ProcessBuilder.Redirect;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.commons.math3.distribution.ChiSquaredDistribution;
import org.apache.commons.math3.distribution.LogNormalDistribution;
import org.apache.commons.math3.random.JDKRandomGenerator;

public class ScenarioSet {
    public ExecutorService executor;

    private Config config;
    public int max_years = -1;
    public String cwd;

    public VitalStats generate_stats;
    public VitalStats validate_stats;
    public AnnuityStats generate_annuity_stats;
    public AnnuityStats validate_annuity_stats;

    private static DecimalFormat f1f = new DecimalFormat("0.0");
    private static DecimalFormat f2f = new DecimalFormat("0.00");
    private static DecimalFormat f3f = new DecimalFormat("0.000");

    public void subprocess(String cmd, String prefix, String... args) throws IOException, InterruptedException {
        String real_cwd = System.getProperty("user.dir");
        List<String> arguments = new ArrayList<String>(Arrays.asList(args));
        arguments.add(0, real_cwd + "/" + cmd);
        ProcessBuilder pb = new ProcessBuilder(arguments);
        Map<String, String> env = pb.environment();
        env.put("OPAL_FILE_PREFIX", cwd + "/" + prefix);
        pb.redirectError(Redirect.INHERIT);
        Process p = pb.start();

        InputStream stdout = p.getInputStream();
        byte buf[] = new byte[8192];
        while (stdout.read(buf) != -1) {
        }
        p.waitFor();
        assert (p.exitValue() == 0);
    }

    private void dump_error(Scenario scenario, Scenario[] error_scenario, String signif, double significance)
            throws IOException {
        assert (error_scenario[0].start_p.length == 1); // No annuities.
        PrintWriter out = new PrintWriter(
                new FileWriter(new File(cwd + "/" + config.prefix + "-error-" + signif + ".csv")));
        for (int i = 0; i < scenario.map.map.length; i++) {
            double age_period = i + config.start_age * config.generate_time_periods;
            for (int step = 0; step < config.gnuplot_steps + 1; step++) {
                double age = age_period / config.generate_time_periods;
                double curr_pf = (config.gnuplot_steps - step) * scenario.tp_max / config.gnuplot_steps;
                double[] p = new double[1];
                p[scenario.tp_index] = curr_pf;

                MapElement me_s = scenario.map.lookup_interpolate(p, i);

                double[][] aa = new double[error_scenario.length][];
                double[] consume = new double[error_scenario.length];
                for (int e = 0; e < error_scenario.length; e++) {
                    MapElement me = error_scenario[e].map.lookup_interpolate(p, i);
                    aa[e] = me.aa;
                    consume[e] = me.consume;
                }
                aa = Utils.zipDoubleArrayArray(aa);

                for (int a = 0; a < scenario.normal_assets; a++) {
                    Arrays.sort(aa[a]);
                }
                Arrays.sort(consume);

                int low = (int) ((1 - significance) / 2 * (error_scenario.length - 1));
                int high = low + (int) (Math.round(significance * (error_scenario.length - 1)));

                out.print(f2f.format(age));
                out.print("," + f2f.format(curr_pf));
                out.print("," + f2f.format(me_s.consume));
                out.print("," + f2f.format(consume[low]));
                out.print("," + f2f.format(consume[high]));
                out.print(","); // Reserve space for ria and nia.
                out.print(",");
                out.print(",");
                out.print(",");
                out.print(",");
                out.print(",");
                for (int a = 0; a < scenario.normal_assets; a++) {
                    out.print("," + f3f.format(me_s.aa[a]));
                    out.print("," + f3f.format(aa[a][low]));
                    out.print("," + f3f.format(aa[a][high]));
                }
                out.print("\n");
            }
            out.print("\n");
        }
        out.close();
    }

    public ScenarioSet(Config config, HistReturns hist, String cwd, Map<String, Object> params,
            String param_filename) throws ExecutionException, IOException, InterruptedException {
        this.config = config;
        this.cwd = cwd;

        // Override default scenario based on scenario file.
        boolean param_filename_is_default = (param_filename == null);
        if (param_filename_is_default)
            param_filename = config.prefix + "-scenario.txt";

        File f = new File(cwd + '/' + param_filename);
        if (!f.exists()) {
            if (!param_filename_is_default)
                throw new FileNotFoundException(param_filename);
        } else {
            BufferedReader reader = new BufferedReader(new FileReader(f));
            StringBuilder stringBuilder = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                stringBuilder.append(line);
                stringBuilder.append(System.getProperty("line.separator"));
            }
            Map<String, Object> fParams = new HashMap<String, Object>();
            config.load_params(fParams, stringBuilder.toString());
            config.applyParams(fParams);
        }

        // Override default scenario based on command line arguments.
        if (params != null)
            config.applyParams(params);

        generate_stats = new VitalStats(this, config, hist, config.generate_time_periods);
        generate_stats.compute_stats(config.generate_life_table, 1); // Compute here so we can access death.length.
        validate_stats = new VitalStats(this, config, hist, config.validate_time_periods);
        validate_stats.compute_stats(config.validate_life_table, 1);
        generate_annuity_stats = new AnnuityStats(this, config, hist, generate_stats, config.generate_time_periods,
                config.annuity_table);
        validate_annuity_stats = new AnnuityStats(this, config, hist, validate_stats, config.validate_time_periods,
                config.annuity_table);

        System.out.println("Parameters:");
        config.dumpParams();
        System.out.println();

        boolean do_compare = !config.skip_compare;

        Scenario compare_scenario = null;
        if (do_compare) {
            assert (config.tax_rate_div == null);
            assert (!config.ef.equals("none"));
            List<String> asset_classes = new ArrayList<String>(Arrays.asList("stocks", "bonds"));
            compare_scenario = new Scenario(this, config, hist, false, !config.skip_validate, asset_classes,
                    asset_classes, config.ret_equity_premium, 1, 1, 1, null, null);
        }

        Scenario scenario = new Scenario(this, config, hist, config.compute_risk_premium, !config.skip_validate,
                config.asset_classes, config.asset_class_names, config.ret_equity_premium, 1, 1, 1,
                config.start_ria, config.start_nia);
        scenario.report_returns();

        if (config.compute_risk_premium)
            return;

        Scenario[] error_scenario = new Scenario[config.error_count];
        if (config.error_count > 0) {
            List<String> asset_classes = new ArrayList<String>(Arrays.asList("stocks", "bonds"));
            Scenario risk_premium_scenario = new Scenario(this, config, hist, true, false, asset_classes,
                    asset_classes, config.ret_equity_premium, 1, 1, 1, config.start_ria, config.start_nia);
            List<double[]> rp_returns = Utils.zipDoubleArray(risk_premium_scenario.returns_generate.original_data);
            double n = rp_returns.get(0).length;
            double sample_erp_am = Utils.mean(rp_returns.get(0));
            double sample_erp_sd = Utils.standard_deviation(rp_returns.get(0));

            JDKRandomGenerator random = new JDKRandomGenerator();
            random.setSeed(0);
            ChiSquaredDistribution chi_squared = new ChiSquaredDistribution(random, n - 1);
            LogNormalDistribution gamma_distribution = null;
            if (config.gamma_vol > 0)
                gamma_distribution = new LogNormalDistribution(random, 0, config.gamma_vol);
            LogNormalDistribution q_distribution = null;
            if (config.q_vol > 0)
                q_distribution = new LogNormalDistribution(random, 0, config.q_vol);
            for (int i = 0; i < error_scenario.length; i++) {
                double erp;
                double equity_vol_adjust;
                if (config.equity_premium_vol) {
                    double population_erp_am = sample_erp_am;
                    equity_vol_adjust = Math.sqrt((n - 1) / chi_squared.sample());
                    double population_erp_sd = sample_erp_sd * equity_vol_adjust;
                    double erp_am = population_erp_am;
                    double erp_sd = population_erp_sd / Math.sqrt(n);
                    erp = erp_am + erp_sd * random.nextGaussian();
                } else {
                    erp = sample_erp_am;
                    equity_vol_adjust = 1;
                }
                double gamma_adjust = ((config.gamma_vol > 0) ? gamma_distribution.sample() : 1);
                double q_adjust = ((config.q_vol > 0) ? q_distribution.sample() : 1);
                error_scenario[i] = new Scenario(this, config, hist, false, false, config.asset_classes,
                        config.asset_class_names, erp, equity_vol_adjust, gamma_adjust, q_adjust, config.start_ria,
                        config.start_nia);
            }
        }

        if (do_compare)
            compare_scenario.run_mvo("compare"); // Helps determine max_stocks based on risk tolerance.
        scenario.run_mvo("scenario");
        for (int i = 0; i < error_scenario.length; i++)
            error_scenario[i].run_mvo("error");

        executor = Executors.newFixedThreadPool(config.workers);
        try {
            if (do_compare)
                compare_scenario.run_compare();

            scenario.run_main();

            if (error_scenario.length > 0) {
                long start = System.currentTimeMillis();

                for (int i = 0; i < error_scenario.length; i++) {
                    if (config.trace || config.trace_error)
                        System.out.println("error scenario " + (i + 1));
                    generate_stats.compute_stats(config.generate_life_table, error_scenario[i].q_adjust);
                    error_scenario[i].run_error();
                }
                generate_stats.compute_stats(config.generate_life_table, 1); // Reset to default.

                dump_error(scenario, error_scenario, "0.68", 0.68);
                dump_error(scenario, error_scenario, "0.95", 0.95);

                double elapsed = (System.currentTimeMillis() - start) / 1000.0;
                System.out.println("Error done: " + f1f.format(elapsed) + " seconds");
                System.out.println();
            }
        } catch (Exception | AssertionError e) {
            executor.shutdownNow();
            throw e;
        }
        executor.shutdown();
    }
}