/*	20070412c-montecarlostockprices.js		3-2-2009		JavaScript
	Copyright (C)2008-09 Steven Whitney.
	Initially published by http://25yearsofprogramming.com.

	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License (GPL)
	Version 3 as published by the Free Software Foundation.
	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 General Public License for more details.
	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

JavaScript code specific to the Monte Carlo simulated stock price time series calculator.

*/
//----------------------------------------------------------------------------------------------
// Reverses the order of the values in the output box. 
function ReverseOutputs()
{
var t = document.getElementById("StockPricesOut").value.replace(/[^0-9.]+/gi," ").split(/\s+/).reverse();
var u = "";
var i;
for(i = 0 ; i < t.length ; i++)
	u += (t[i] + "\n");
document.getElementById("StockPricesOut").value = u;
}
//----------------------------------------------------------------------------------------------
function ActualVolatility(Input)
{
var lognormal = new Array();	// transformed to natural log of px/(prev px)
var prev = Number.NaN;			// remains NaN until the first valid data point is extracted
var i, x;
for(i = 0 ; i < Input.length ; i++)
{
	x = parseFloat(Input[i]);
	if(!isNaN(x))
	{
		if(!isNaN(prev))	// price change calculation is only valid after the first data point
		{
			lognormal.push(Math.log(x / prev));	
		}
		prev = x;
	}
}
var stats = DescriptiveStatistics(lognormal);
return stats["StdDevEst"] * Math.sqrt(TradingDaysIn(365.25));
}
//----------------------------------------------------------------------------------------------
function CalcMonteCarloPriceSeries()
{
var rawonly = false;
var useslevy = false;
var calcmethod = "";
if(document.getElementById("DArithmetic").checked == true)	calcmethod = "Arithmetic";
if(document.getElementById("DLog").checked == true)			calcmethod = "Log";
if(document.getElementById("DRaw").checked == true)		{	calcmethod = "Raw"; rawonly = true; }
if(document.getElementById("DGaussian").checked == true)	calcmethod += "Normal";
if(document.getElementById("DLevy").checked == true)	{	calcmethod += "Levy"; useslevy = true; }
if(document.getElementById("DUniform").checked == true)		calcmethod += "Uniform";

var voladj = parseFloat(document.getElementById("VolAdjIn").value.replace(/[^0-9.]+/gi," "));
var alpha = parseFloat(document.getElementById("AlphaIn").value.replace(/[^0-9.]+/gi," "));
var yesterday = parseFloat(document.getElementById("StartPriceIn").value.replace(/[^0-9.]+/gi," "));
var volatility = parseFloat(document.getElementById("VolatilityIn").value.replace(/[^0-9.]+/gi," "));
var numdays = parseFloat(document.getElementById("NumDaysIn").value.replace(/[^0-9.]+/gi," "));
var u = "";
var today = Number.NaN;
var a = new Array();
var i, msg;

if(isNaN(volatility) || isNaN(numdays) || (!rawonly && isNaN(yesterday)) ||
	(useslevy && (isNaN(voladj) || isNaN(alpha) || (alpha <= 0) || (alpha > 2))))
{
	alert("Error: One or more input values is invalid.");
}
else
{
	// The program expects volatility to be entered as a percent. If it doesn't seem to be a %, get user confirmation.
	if(volatility < 1.00)	
	{
		msg = "The program interprets volatility as a percentage.\n" + 
				volatility + "% is extremely low.\nClick...\nOK to change it to " + 
				(100 * volatility) + "%, or\nCancel to leave it as " + volatility + "%.";
		if(confirm(msg))
		{
			volatility *= 100;
			document.getElementById("VolatilityIn").value = volatility;
		}
	}
	volatility /= 100;
	if(!rawonly)						// if we're doing a price time series,
		volatility *= Math.sqrt(1/252);	// convert annualized volatility to daily
	for(i = 0 ; i < numdays ; i++)
	{
		switch(calcmethod)
		{
			case "ArithmeticNormal":	 // produces actual volatility in about the correct range
				today = yesterday * (1 + NormalRandom(0, volatility));	// makes realistic looking charts
				break;

			case "ArithmeticLevy":
				today = yesterday * (1 + GSLRandomLevy(volatility / voladj, alpha));
				break;

			case "ArithmeticUniform":
				// The factor of 1.733421 was determined through testing. It is approximately sqrt(3); could be a coincidence.
				// It brings the calculated volatility of the set into the correct range, but I don't know why.
				var j = Math.random() * 1.733421 * volatility;
				if(Math.floor(Math.random() * 2) == 0)
					j = -j;
				today = yesterday * (1 + j);
				break;

			case "LogNormal":	// the original calculation method, used for a long time
				today = yesterday * Math.exp(NormalRandom(0, volatility));
				break;

			case "LogLevy":
				today = yesterday * Math.exp(GSLRandomLevy(volatility / voladj, alpha));
				break;
											
			case "LogUniform":
				var j = Math.random() * 1.733421 * volatility;
				if(Math.floor(Math.random() * 2) == 0)
					j = -j;
				today = yesterday * Math.exp(j);
				break;
											
			case "RawNormal":
				today = NormalRandom(0, volatility);
				break;

			case "RawLevy":
				today = GSLRandomLevy(volatility / voladj, alpha);
				break;
				
			case "RawUniform":	// (-1 to 1)
				var j = Math.random() * volatility;
				if(Math.floor(Math.random() * 2) == 0)
					j = -j;
				today = j;
				break;

			default:
				alert("Error 1: Unexpected calculation method. Please report that you found a bug.");
				break;
		}
		if(rawonly)
		{
			a.push(today);
			u += (today + "\n");
		}
		else
		{
			a.push(today.toFixed(2));
			u += (today.toFixed(2) + "\n");
			yesterday = today;
		}
	}
	document.getElementById("StockPricesOut").value = u;
	// calculate and report the actual annualized volatility of the output
	document.getElementById("VolatilityReport").innerHTML = (100 * ActualVolatility(a)).toFixed(8) + "%";
}
}
//----------------------------------------------------------------------------------------------

