168 lines
5.1 KiB
JavaScript
168 lines
5.1 KiB
JavaScript
var optionResults;
|
|
const MAX_PLOT_ELEMENTS = 100000;
|
|
|
|
$(document).ready(function () {
|
|
"use strict";
|
|
|
|
$("#option-terms").on("submit", function (e) {
|
|
//
|
|
e.preventDefault();
|
|
sendRequest();
|
|
});
|
|
});
|
|
|
|
function serializeForm(id) {
|
|
"use strict";
|
|
//
|
|
return $(id).serializeArray()
|
|
.reduce(function (a, x) {
|
|
a[x.name] = x.value;
|
|
return a;
|
|
}, {});
|
|
}
|
|
|
|
function sendRequest() {
|
|
"use strict";
|
|
//
|
|
var inputData = serializeForm("#option-terms");
|
|
|
|
// Get the right data-types
|
|
inputData.ExpiryDate = new Date(inputData.ExpiryDate).toJSON();
|
|
inputData.OptType = parseInt(inputData.OptType);
|
|
inputData.Rfr = parseFloat(inputData.Rfr);
|
|
inputData.Sims = parseInt(inputData.Sims);
|
|
inputData.Spot = parseFloat(inputData.Spot);
|
|
inputData.Strike = parseFloat(inputData.Strike);
|
|
inputData.ValueDate = new Date(inputData.ValueDate).toJSON();
|
|
inputData.Vol = parseFloat(inputData.impliedVol);
|
|
|
|
// Send request
|
|
var startTime;
|
|
$.ajax({
|
|
type: "POST",
|
|
url: "http://localhost:8080",
|
|
data: JSON.stringify(inputData),
|
|
beforeSend: function () {
|
|
startTime = Date.now();
|
|
},
|
|
success: function (e) {
|
|
optionResults = e;
|
|
updateTable();
|
|
updateGraph();
|
|
var requestTime = Date.now() - startTime;
|
|
console.log("Request time(s):", requestTime / 1000);
|
|
}
|
|
});
|
|
}
|
|
|
|
function updateTable() {
|
|
"use strict";
|
|
// TODO
|
|
$("#results-table > tbody > tr").each(function (idx, el) {
|
|
$($(el).children()[1])[0].innerText =
|
|
optionResults.ClosedForm[$($(el).children()[0]).text()].toFixed(4);
|
|
$($(el).children()[2])[0].innerText =
|
|
optionResults.MonteCarlo[$($(el).children()[0]).text()].toFixed(4);
|
|
});
|
|
}
|
|
|
|
function updateGraph() {
|
|
"use strict";
|
|
// TODO
|
|
var strippedData = optionResults.MonteCarlo.Levels.map(function (i) {
|
|
return Number(i.toFixed(0));
|
|
});
|
|
strippedData = strippedData.slice(0, MAX_PLOT_ELEMENTS);
|
|
|
|
// Count the elements
|
|
var counts = {};
|
|
var num;
|
|
for (var i=0; i<strippedData.length; i++) {
|
|
num = strippedData[i];
|
|
counts[num] = counts[num] ? counts[num] + 1 : 1;
|
|
}
|
|
|
|
var summaryData = [];
|
|
Object.keys(counts).forEach(function (key) {
|
|
summaryData.push({"key": key, "value": counts[key]});
|
|
});
|
|
|
|
var margin = {top: 10, right: 40, bottom: 20, left: 40};
|
|
var width = 500 - margin.left - margin.right;
|
|
var height = 300 - margin.top - margin.bottom;
|
|
|
|
$("#right-upper").empty();
|
|
var svg = d3.select("#right-upper").append("svg")
|
|
.attr("width", width + margin.left + margin.right)
|
|
.attr("height", height + margin.top + margin.bottom)
|
|
.append("g")
|
|
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
|
|
|
var x = d3.scaleLinear()
|
|
.range([0, width])
|
|
.domain([0, Math.max.apply(null, strippedData)]).nice();
|
|
|
|
var maxY0 = Object.values(counts).sort((prev, next) => next - prev)[0];
|
|
var y0 = d3.scaleLinear()
|
|
.range([height, 0])
|
|
.domain([0, maxY0]).nice();
|
|
|
|
var tte = (new Date(optionResults.MonteCarlo.ExpiryDate) - new Date(optionResults.MonteCarlo.ValueDate)) / (1000 * 60 * 60 * 24 * 365.25);
|
|
var scale = optionResults.MonteCarlo.Spot * Math.exp(optionResults.MonteCarlo.Rfr * tte);
|
|
var shape = Math.sqrt(Math.exp(Math.pow(optionResults.MonteCarlo.Vol, 2)) * (Math.exp(Math.pow(optionResults.MonteCarlo.Vol, 2)) - 1)) * Math.sqrt(tte);
|
|
|
|
var maxY1 = logNormal(Math.exp(0 - Math.pow(shape, 2)), shape) / scale;
|
|
var y1 = d3.scaleLinear()
|
|
.range([height, 0])
|
|
.domain([0, maxY1]).nice();
|
|
|
|
var xAxis = d3.axisBottom().scale(x);
|
|
|
|
var yAxisLeft = d3.axisLeft().scale(y0);
|
|
|
|
var yAxisRight = d3.axisRight().scale(y1);
|
|
|
|
var logNormalLine = d3.line()
|
|
.x(function (d) { return x(d); })
|
|
.y(function (d) { return y1(logNormal(d / scale, shape) / scale); })
|
|
.curve(d3.curveBasis);
|
|
|
|
svg.selectAll(".bin")
|
|
.data(summaryData)
|
|
.enter().append("line")
|
|
.attr("class", "bin")
|
|
.attr("x1", function (d) { return x(d.key); })
|
|
.attr("x2", function (d) { return x(d.key); })
|
|
.attr("y1", height)
|
|
.attr("y2", function (d) { return y0(d.value); });
|
|
|
|
svg.append("path")
|
|
.attr("stroke", "blue")
|
|
.attr("stroke-width", "3")
|
|
.attr("fill", "none")
|
|
.attr("d", logNormalLine(d3.range(x.domain()[0], x.domain()[1], 1)));
|
|
|
|
svg.append("g")
|
|
.attr("class", "x axis")
|
|
.attr("transform", "translate(0," + height + ")")
|
|
.call(xAxis);
|
|
|
|
svg.append("g")
|
|
.attr("class", "y axis")
|
|
.call(yAxisLeft);
|
|
|
|
svg.append("g")
|
|
.attr("class", "y axis")
|
|
.style("fill", "blue")
|
|
.attr("transform", "translate(" + width + ", 0)")
|
|
.call(yAxisRight);
|
|
}
|
|
|
|
function logNormal(x, shape) {
|
|
"use strict";
|
|
var y = 1 / (shape * x * Math.sqrt(2 * Math.PI)) * Math.exp(-0.5 * Math.pow((Math.log(x) / shape), 2));
|
|
y = isFinite(y) ? y : 0;
|
|
return y;
|
|
}
|
|
|