-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 7fbe629
Showing
5 changed files
with
321 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
// Worker initialisation | ||
const myWorker = new Worker("./assets/js/worker.js"); | ||
|
||
function updatePlot(data){ | ||
|
||
var xPts = data[0], yPts = data[1], zPts = data[2] | ||
|
||
var labels = ["HL", "AJB", "BARC", "FID", "MIN"] | ||
var colors = ["rgb(0,0,255)", "rgb(255,180,0)", "rgb(0,255,0)", "rgb(255,0,0)", "rgb(200,200,200)"] | ||
|
||
var traces = [] | ||
for (let [i, label] of labels.entries()){ | ||
var trace = { | ||
x: xPts, | ||
y: yPts, | ||
z: zPts[label], | ||
type: 'surface', | ||
name: labels[i], | ||
showscale: false, | ||
colorscale: [[0,colors[i]],[1,colors[i]]], | ||
showlegend:true, | ||
} | ||
traces.push(trace) | ||
} | ||
|
||
var layout = { | ||
title: 'Stocks & Shares ISA Providers', | ||
scene:{ | ||
xaxis: {title: "Fund Value"}, | ||
yaxis: {title: "Share Value"}, | ||
zaxis: {title: "Total Annual Cost"}, | ||
camera: { | ||
// center: {x: 0, y: 0, z: 0 }, | ||
eye: {x: 1.8, y: 1.2, z: 1.2 }, | ||
up: {x: 0, y: 0, z: 0.3 } | ||
}, | ||
}, | ||
autosize: false, | ||
width: 1200, | ||
height: 840, | ||
// margin: { | ||
// l: 65, | ||
// r: 50, | ||
// b: 65, | ||
// t: 90, | ||
// }, | ||
} | ||
|
||
Plotly.newPlot('plot', traces, layout); | ||
} | ||
|
||
// Process received data here | ||
myWorker.onmessage = function(e) { | ||
var result = e.data | ||
// Process/display data here | ||
updatePlot(result) | ||
loading.style.display = "none" | ||
} | ||
|
||
const loading = document.querySelector('#sim-loading') | ||
const numFunds = document.querySelector('#num-funds') | ||
const numStocks = document.querySelector('#num-stocks') | ||
const simulate = document.querySelector('#simulate') | ||
|
||
simulate.onclick = function() { | ||
myWorker.postMessage({fundRange: 40e3, fundStep: 2e2, stockRange: 40e3, stockStep: 2e2, fundTrans: parseInt(numFunds.value), stockTrans: parseInt(numStocks.value)}) | ||
loading.style.display = "inline-block" | ||
} | ||
|
||
document.addEventListener("DOMContentLoaded", function(event) { | ||
simulate.click() | ||
}) |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
//References: | ||
// https://github.com/nicolaspanel/numjs/blob/master/src/ndarray.js | ||
// https://github.com/nicolaspanel/numjs | ||
// https://github.com/scijs/ndarray-ops | ||
|
||
|
||
// Stocks and Shares ISA Providers | ||
/* | ||
> a.slice([null,null,-1]) // same as a[::-1] | ||
array([ 4, 3, 2, 1, 0]) | ||
*/ | ||
|
||
var INF = Number.MAX_SAFE_INTEGER; | ||
var gt0 = x => x > 0 ? 1: 0; | ||
var gte0 = x => x >= 0 ? 1: 0; | ||
var lt0 = x => x < 0 ? 1: 0; | ||
var lte0 = x => x <= 0 ? 1: 0; | ||
var nanfilter = x => Math.abs(x) > INF/2 ? 0: x; | ||
|
||
//TEST: nj.subtract([2,3,4], [4,3,2]).apply(gte0) | ||
|
||
nj.NdArray.prototype.apply = function (func, copy) { | ||
if (arguments.length === 2) { copy = true; } | ||
var arr = copy ? this.clone() : this; | ||
arr.selection.data = arr.selection.data.map(x=>func(x)) | ||
return arr; | ||
}; | ||
|
||
|
||
nj.NdArray.prototype.nansum = function (copy) { | ||
if (arguments.length === 1) { copy = true; } | ||
var arr = copy ? this.clone() : this; | ||
return arr.apply(nanfilter).sum(); | ||
} | ||
//nj.NdArray.prototype.nansum = nj.NdArray.prototype.apply(nanfilter).sum() // Simpler form if it works (needs to be corrected) | ||
|
||
//TEST: nj.subtract([2,3,4], [3,1,Number.MAX_SAFE_INTEGER]).nansum() | ||
|
||
/* | ||
nj.NdArray.prototype.geq = function (value, copy) { | ||
if (arguments.length === 1) { copy = true; } | ||
var arr = copy ? this.clone() : this; | ||
ops.geq(arr.selection, value); | ||
return arr; | ||
}; | ||
*/ | ||
|
||
function tier_sum(tiers, charges, value){ | ||
tiers = nj.array(tiers) | ||
charges = nj.array(charges) | ||
var ends = tiers.slice(1) | ||
var starts = tiers.slice([0, -1]) | ||
|
||
var a = ends.subtract(value).apply(gte0) | ||
var b = starts.subtract(value).apply(lt0) | ||
var c = starts.subtract(value).negative() | ||
var A = (a.multiply(b)).multiply(c) | ||
var B = (ends.subtract(value).apply(lt0)).multiply(ends.subtract(starts)) | ||
return charges.multiply( A.add(B) ).nansum() | ||
} | ||
|
||
|
||
function HL(F, S, Nf, Ns){ | ||
var V = F + S | ||
var tiers = [0, 250e3, 500e3, 1000e3, 2000e3, INF] | ||
var charges = nj.array([0.45, 0.25, 0.25, 0.1, 0]).divide(100) | ||
|
||
var share_trans_tiers = [0, 10, 20, INF] | ||
var fund_trans_cost = 0 | ||
var share_trans_cost = [11.95, 8.95, 5.95] | ||
|
||
var holding_cost = tier_sum(tiers, charges, V) | ||
var trans_cost = ( Nf*fund_trans_cost + tier_sum(share_trans_tiers, share_trans_cost, Ns) ) * 12 | ||
|
||
return holding_cost + trans_cost | ||
} | ||
|
||
|
||
function AJB(F, S, Nf, Ns){ | ||
var V = F + S | ||
var tiers = [0, 250e3, 500e3, 1000e3, 2000e3, INF] | ||
var fund_charges = nj.array([0.25, 0.1, 0, 0, 0]).divide(100) | ||
var share_charges = nj.array([0.25, 0, 0, 0, 0]).divide(100) | ||
|
||
var share_trans_tiers = [0, 10, 20, INF] | ||
var fund_trans_cost = 1.5 | ||
var share_trans_cost = [9.95, 4.95, 4.95] | ||
|
||
var holding_cost = tier_sum(tiers, fund_charges, F) + Math.max(tier_sum(tiers, share_charges, S), 3.5*12) | ||
var trans_cost = ( Nf*fund_trans_cost + tier_sum(share_trans_tiers, share_trans_cost, Ns) ) * 12 | ||
|
||
return holding_cost + trans_cost | ||
} | ||
|
||
|
||
function BARC(F, S, Nf, Ns){ | ||
var V = F + S | ||
//tiers = [0, 250e3, 500e3, 1000e3, 2000e3, np.inf] | ||
//charges = np.array([0.2, 0.2, 0.2, 0.2, 0.2])/100 | ||
var fund_charge = 0.2/100 | ||
var share_charge = 0.1/100 | ||
|
||
//share_trans_tiers = [0, 10, 20, np.inf] | ||
var fund_trans_cost = 3 | ||
//share_trans_cost = [6, 6, 6] | ||
var share_trans_cost = 6 | ||
|
||
var holding_cost = Math.max(Math.min(fund_charge * F + share_charge * S, 125*12), 4*12) | ||
//trans_cost = ( Nf*fund_trans_cost + tier_sum(share_trans_tiers, share_trans_cost, Ns) ) * 12 | ||
var trans_cost = (Nf*fund_trans_cost + Ns*share_trans_cost)*12 | ||
|
||
return holding_cost + trans_cost | ||
} | ||
|
||
|
||
// Verified using https://www.fidelity.co.uk/fees-calculator/ | ||
function FID(F, S, Nf, Ns){ | ||
|
||
var tiers = nj.array([250e3, 500e3, 1000e3]) | ||
var charges = [0.35 / 100, 0.2 / 100, 0.2 / 100] //nj.array([0.35, 0.2, 0.2]).divide(100).tolist() | ||
|
||
// Fidelity charges the rate on the amount for the tier you are in rather than on the amount over the previous tier | ||
// For ETIs (Exchange-traded instrs eg. stocks, ETFs), the fee is capped at 45 annually | ||
// But for Mutual Funds, this is capped at 2000 annually | ||
var holding_cost = Math.min(F*charges[tiers.subtract(F).apply(lte0).sum()], 2000) + Math.min(S*charges[tiers.subtract(S).apply(lte0).sum()], 45) | ||
|
||
var instr_trans_cost = 10 | ||
//var trans_cost = ((Nf + Ns)*instr_trans_cost)*12 | ||
var trans_cost = (Ns*instr_trans_cost)*12 | ||
return holding_cost + trans_cost | ||
} | ||
|
||
|
||
function MIN(f, s, Nf, Ns){ | ||
var providers = [HL, AJB, BARC, FID] | ||
var arr = [] | ||
for (let [i, provider] of providers.entries()){ | ||
arr.push(provider(f, s, Nf, Ns)) | ||
} | ||
return Math.min(...arr) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
// Import external scripts here | ||
importScripts("./numjs.min.js", "./providers.js"); | ||
|
||
self.onmessage = function(e) { | ||
var t0 = performance.now(); | ||
|
||
var data = e.data; | ||
|
||
var xlim = data.fundRange; | ||
var xstep = data.fundStep; | ||
var ylim = data.stockRange; | ||
var ystep = data.stockStep; | ||
var Nf = data.fundTrans; | ||
var Ns = data.stockTrans; | ||
|
||
// PROCESS DATA HERE | ||
var labels = ["HL", "AJB", "BARC", "FID", "MIN"] | ||
var providers = [HL, AJB, BARC, FID, MIN] | ||
|
||
// Create template for provider value matrix | ||
var templateZ = {} | ||
for (var provider of labels) { | ||
templateZ[provider] = []; | ||
} | ||
|
||
var zPts = JSON.parse(JSON.stringify(templateZ)); | ||
var xPts = []; | ||
var yPts = []; | ||
|
||
for(x=0; x <= xlim; x += xstep) { | ||
let zTemp = JSON.parse(JSON.stringify(templateZ)); | ||
let yTemp = []; | ||
let xTemp = []; | ||
for (y=0; y <= ylim; y += ystep) { | ||
xTemp.push(x); | ||
yTemp.push(y); | ||
for (let [i, provider] of providers.entries()){ | ||
zTemp[labels[i]].push(provider(x, y, Nf, Ns)); | ||
} | ||
} | ||
xPts.push(xTemp); | ||
yPts.push(yTemp); | ||
for (let [i, provider] of providers.entries()){ | ||
zPts[labels[i]].push(zTemp[labels[i]]); | ||
} | ||
} | ||
|
||
|
||
var result = [xPts, yPts, zPts]; | ||
|
||
var t1 = performance.now(); | ||
console.log("Processing took " + (t1 - t0) + " milliseconds."); | ||
self.postMessage(result); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
<html> | ||
<head> | ||
<title>Stocks & Shares ISA Providers</title> | ||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> | ||
<meta name="description" content="Stocks Shares ISA Providers"> | ||
<meta name="keywords" content="stocks, equities, comparison, analysis, calculator, engineering"> | ||
<meta name="author" content="Abhishek Shenoy"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous"> | ||
</head> | ||
<body> | ||
<div id="main"> | ||
<nav id="menu" style="padding:10px;"> | ||
<div class="container"> | ||
<div class="row"> | ||
<div class="col-sm-4"> | ||
<div class="input-group mb-3"> | ||
<div class="input-group-prepend"> | ||
<span class="input-group-text" style="font-size: 14px;">Num Monthly Fund Trades</span> | ||
</div> | ||
<input type="text" id="num-funds" class="form-control" type="number" value="0"/> | ||
</div> | ||
</div> | ||
<div class="col-sm-4"> | ||
<div class="input-group mb-3"> | ||
<div class="input-group-prepend"> | ||
<span class="input-group-text" style="font-size: 14px;">Num Monthly Stock Trades</span> | ||
</div> | ||
<input type="text" id="num-stocks" class="form-control" type="number" value="0"/> | ||
</div> | ||
</div> | ||
<div class="col-sm-4"> | ||
<button id="simulate" class="btn btn-primary"> | ||
<span id="sim-available">Update</span> | ||
<span id="sim-loading"class="spinner-grow spinner-grow-sm" role="status" aria-hidden="true" style="display: none;"></span> | ||
</button> | ||
</div> | ||
</div> | ||
</div> | ||
</nav> | ||
<div class="container"> | ||
<div class="row"> | ||
<div id="plot"></div> | ||
</div> | ||
</div> | ||
</div> | ||
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script> | ||
<!-- <script src="https://cdn.jsdelivr.net/gh/nicolaspanel/[email protected]/dist/numjs.min.js"></script> --> | ||
<script src="./assets/js/app.js"></script> | ||
</body> | ||
</html> |