Compare commits
No commits in common. "42cffe2faeef28130cf20eeb5338bec4699df6b0" and "882d7218995d47bfc9a44c16692973cdd2021eab" have entirely different histories.
42cffe2fae
...
882d721899
Binary file not shown.
File diff suppressed because one or more lines are too long
@ -141,30 +141,6 @@
|
||||
<div id="chartTooltip" class="chart-tooltip"></div>
|
||||
<script>
|
||||
let importedFunctions = {};
|
||||
function logInfo(msg) {
|
||||
console.info(msg);
|
||||
if (typeof msg == 'object') {
|
||||
$("#logDiv").css("font-color", "white").text(JSON.stringify(msg));
|
||||
} else {
|
||||
$("#logDiv").css("font-color", "white").text(msg);
|
||||
}
|
||||
}
|
||||
function logWarning(msg) {
|
||||
console.warn(msg);
|
||||
if (typeof msg == 'object') {
|
||||
$("#logDiv").css("font-color", "orange").text(JSON.stringify(msg));
|
||||
} else {
|
||||
$("#logDiv").css("font-color", "orange").text(msg);
|
||||
}
|
||||
}
|
||||
function logError(msg) {
|
||||
console.error(msg);
|
||||
if (typeof msg == 'object') {
|
||||
$("#logDiv").css("font-color", "red").text(JSON.stringify(msg));
|
||||
} else {
|
||||
$("#logDiv").css("font-color", "red").text(msg);
|
||||
}
|
||||
}
|
||||
function showTab(tab) {
|
||||
//$('.navbar>a').removeClass('activenav');
|
||||
$('.navbar').find('.activenav').removeClass('activenav');
|
||||
@ -185,11 +161,11 @@
|
||||
redrawAllSharesCharts();
|
||||
break;*/
|
||||
case 'displayAsPercent':
|
||||
importedFunctions.redrawAllSharesCharts();
|
||||
redrawAllSharesCharts();
|
||||
break;
|
||||
case 'excludeNoHoldings':
|
||||
importedFunctions.createSharesTable();
|
||||
importedFunctions.redrawAllSharesCharts();
|
||||
createSharesTable();
|
||||
redrawAllSharesCharts();
|
||||
break;
|
||||
/*case 'showPreviousHoldings':
|
||||
redrawAllSharesCharts();
|
||||
@ -206,24 +182,12 @@
|
||||
}
|
||||
</script>
|
||||
<script type="module">
|
||||
import {
|
||||
createSharesTable,
|
||||
getInstruments,
|
||||
redrawAllSharesCharts,
|
||||
refreshHistoryChart,
|
||||
showModalDialog_AddInstrument,
|
||||
showModalDialog_AddHolding,
|
||||
showModalDialog_FullScreenChart,
|
||||
showModalDialog_SoldHolding
|
||||
} from "./scripts/SharePrices.js";
|
||||
import { getInstruments, refreshHistoryChart, showModalDialog_AddInstrument, showModalDialog_AddHolding, showModalDialog_FullScreenChart, showModalDialog_SoldHolding } from "./scripts/SharePrices.js";
|
||||
importedFunctions = {
|
||||
createSharesTable: createSharesTable,
|
||||
getInstruments: getInstruments,
|
||||
redrawAllSharesCharts: redrawAllSharesCharts,
|
||||
refreshHistoryChart: refreshHistoryChart,
|
||||
showModalDialog_AddInstrument: showModalDialog_AddInstrument,
|
||||
showModalDialog_AddHolding: showModalDialog_AddHolding,
|
||||
showModalDialog_FullScreenChart: showModalDialog_FullScreenChart,
|
||||
refreshHistoryChart: refreshHistoryChart,
|
||||
showModalDialog_SoldHolding: showModalDialog_SoldHolding
|
||||
}
|
||||
|
||||
|
@ -20,8 +20,6 @@
|
||||
<IISExpressAnonymousAuthentication />
|
||||
<IISExpressWindowsAuthentication />
|
||||
<IISExpressUseClassicPipelineMode />
|
||||
<Use64BitIISExpress />
|
||||
<UseGlobalApplicationHostFile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
@ -261,7 +259,6 @@
|
||||
<Content Include="scripts\js.cookie-2.2.1.min.js" />
|
||||
<Content Include="scripts\SharePrices.js" />
|
||||
<Content Include="scripts\SharePrices_Charts.js" />
|
||||
<Content Include="scripts\SharePrices_Holdings.js" />
|
||||
<Content Include="scripts\tablesorter\dragtable.mod.css" />
|
||||
<Content Include="scripts\tablesorter\filter.formatter.css" />
|
||||
<Content Include="scripts\tablesorter\highlights.css" />
|
||||
|
@ -1,15 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<NameOfLastUsedPublishProfile>WinSrv1</NameOfLastUsedPublishProfile>
|
||||
<LastActiveSolutionConfig>Debug|Any CPU</LastActiveSolutionConfig>
|
||||
<UseIISExpress>false</UseIISExpress>
|
||||
<Use64BitIISExpress />
|
||||
<IISExpressSSLPort />
|
||||
<IISExpressAnonymousAuthentication />
|
||||
<IISExpressWindowsAuthentication />
|
||||
<IISExpressUseClassicPipelineMode />
|
||||
<UseGlobalApplicationHostFile />
|
||||
</PropertyGroup>
|
||||
<ProjectExtensions>
|
||||
<VisualStudio>
|
||||
|
Binary file not shown.
@ -1,35 +1,21 @@
|
||||
// @ts-check
|
||||
|
||||
import {formatAmount, generateAxisBreaks, getProfitOrLoss, timespans} from "./SharePrices_Common.js";
|
||||
import {generateAxisBreaks, getProfitOrLoss, timespans} from "./SharePrices_Common.js";
|
||||
import {createLongChart, createMidChart, createShortChart, createSingleDayChart, createChart_Flot,
|
||||
createChart, createFullScreenChart, createFullScreenChart_Flot, createFullScreenComparisonChart,
|
||||
createFullScreenComparisonChart_Flot
|
||||
} from "./SharePrices_Charts.js";
|
||||
import {createHoldingsTable} from "./SharePrices_Holdings.js";
|
||||
createFullScreenComparisonChart_Flot} from "./SharePrices_Charts.js";
|
||||
|
||||
|
||||
'use strict';
|
||||
|
||||
var vars = {
|
||||
fetchTiming: {
|
||||
fetchIntervalDaily: 12 * timespans.oneHour,
|
||||
fetchIntervalIntraday: 4 * timespans.oneHour,
|
||||
fetchIntervalSingleDay: 2 * timespans.oneMinute,
|
||||
lastSuccessfulFetch: new Date(0),
|
||||
lastFetchStartTime: new Date(0),
|
||||
lastFetchDuration: 0,
|
||||
fetchTimer: 0,
|
||||
displayTimings: false
|
||||
},
|
||||
lastTotalHoldingsValue: 0, //The last total holdings value successfully set in the database
|
||||
initialPageTitle: '',
|
||||
previousDay: 0,
|
||||
previousClose: 0,
|
||||
previousWeek: 0,
|
||||
previousWeekClose:0
|
||||
}
|
||||
|
||||
//var timespans = { oneSecond: 1000, oneMinute: 60 * 1000, oneHour: 60 * 60 * 1000, oneDay: 24 * 60 * 60 * 1000, oneWeek: 7 * 24 * 60 * 60 * 1000, oneMonth: 31 * 24 * 60 * 60 * 1000 };
|
||||
//var lastSuccessfulFetch;
|
||||
var fetchIntervalDaily = 12 * timespans.oneHour;
|
||||
var fetchIntervalIntraday = 4 * timespans.oneHour;
|
||||
var fetchIntervalSingleDay = 2 * timespans.oneMinute;
|
||||
var initialPageTitle;
|
||||
var fetchTimer = 0;
|
||||
var lastSuccessfulFetch;
|
||||
var lastTotalHoldingsValue = 0; //The last total holdings value successfully set in the database
|
||||
var currencyFormatter = new Intl.NumberFormat('en-GB', { style: 'currency', currency: 'GBP' });
|
||||
var instrumentSearchResults = [];
|
||||
var tablesUpdateTimings = { lastUpdate: 0, timeBetweenUpdates: 10000, timer: 0, updateNeeded: true };
|
||||
@ -104,13 +90,13 @@ var Instruments = {
|
||||
},
|
||||
GetLastFetchedDaily: function () {
|
||||
//if (!this.Data[0].lastFetchedDaily) { this.Data[0].lastFetchedDaily = this.Data[0].MaxDailyDate };
|
||||
if (!this.Data[0].lastFetchedDaily) { this.Data[0].lastFetchedDaily = new Date().getTime() - vars.fetchTiming.fetchIntervalDaily + (4 * timespans.oneMinute) };
|
||||
if (!this.Data[0].lastFetchedDaily) { this.Data[0].lastFetchedDaily = new Date().getTime() - fetchIntervalDaily + (4 * timespans.oneMinute) };
|
||||
let lastFetchedDate = this.Data[0].lastFetchedDaily;
|
||||
let lastFetchedIndex = 0;
|
||||
for (let i = 1; i < this.Data.length; i++) {
|
||||
if (this.Data[i].Symbol != 'GBP' && this.Data[i].Symbol != 'GBp') {
|
||||
//if (!this.Data[i].lastFetchedDaily) { this.Data[i].lastFetchedDaily = this.Data[i].MaxDailyDate };
|
||||
if (!this.Data[i].lastFetchedDaily) { this.Data[i].lastFetchedDaily = new Date().getTime() - vars.fetchTiming.fetchIntervalDaily + (4 * timespans.oneMinute) };
|
||||
if (!this.Data[i].lastFetchedDaily) { this.Data[i].lastFetchedDaily = new Date().getTime() - fetchIntervalDaily + (4 * timespans.oneMinute) };
|
||||
if (lastFetchedDate > this.Data[i].lastFetchedDaily) {
|
||||
lastFetchedDate = this.Data[i].lastFetchedDaily;
|
||||
lastFetchedIndex = i;
|
||||
@ -122,13 +108,13 @@ var Instruments = {
|
||||
},
|
||||
GetLastFetchedIntraday: function () {
|
||||
//if (!this.Data[0].lastFetchedIntraday) { this.Data[0].lastFetchedIntraday = this.Data[0].MaxIntradayDate };
|
||||
if (!this.Data[0].lastFetchedIntraday) { this.Data[0].lastFetchedIntraday = new Date().getTime() - vars.fetchTiming.fetchIntervalIntraday + (2 * timespans.oneMinute) };
|
||||
if (!this.Data[0].lastFetchedIntraday) { this.Data[0].lastFetchedIntraday = new Date().getTime() - fetchIntervalIntraday + (2 * timespans.oneMinute) };
|
||||
let lastFetchedDate = this.Data[0].lastFetchedIntraday;
|
||||
let lastFetchedIndex = 0;
|
||||
for (let i = 1; i < this.Data.length; i++) {
|
||||
if (this.Data[i].Symbol != 'GBP' && this.Data[i].Symbol != 'GBp') {
|
||||
//if (!this.Data[i].lastFetchedIntraday) { this.Data[i].lastFetchedIntraday = this.Data[i].MaxIntradayDate };
|
||||
if (!this.Data[i].lastFetchedIntraday) { this.Data[i].lastFetchedIntraday = new Date().getTime() - vars.fetchTiming.fetchIntervalIntraday + (2 * timespans.oneMinute) };
|
||||
if (!this.Data[i].lastFetchedIntraday) { this.Data[i].lastFetchedIntraday = new Date().getTime() - fetchIntervalIntraday + (2 * timespans.oneMinute) };
|
||||
if (lastFetchedDate > this.Data[i].lastFetchedIntraday) {
|
||||
lastFetchedDate = this.Data[i].lastFetchedIntraday;
|
||||
lastFetchedIndex = i;
|
||||
@ -236,6 +222,7 @@ var Currencies = {
|
||||
}
|
||||
}
|
||||
}
|
||||
var previousDay, previousClose, previousWeek, previousWeekClose;
|
||||
|
||||
Date.prototype.yyyymmddhhmmss = function () {
|
||||
var yyyy = this.getFullYear().toString();
|
||||
@ -298,6 +285,78 @@ Number.prototype.autoScale = function () {
|
||||
}
|
||||
};
|
||||
|
||||
function logInfo(msg) {
|
||||
console.info(msg);
|
||||
if (typeof msg == 'object') {
|
||||
$("#logDiv").css("font-color", "white").text(JSON.stringify(msg));
|
||||
} else {
|
||||
$("#logDiv").css("font-color", "white").text(msg);
|
||||
}
|
||||
}
|
||||
function logWarning(msg) {
|
||||
console.warn(msg);
|
||||
if (typeof msg == 'object') {
|
||||
$("#logDiv").css("font-color", "orange").text(JSON.stringify(msg));
|
||||
} else {
|
||||
$("#logDiv").css("font-color", "orange").text(msg);
|
||||
}
|
||||
}
|
||||
function logError(msg) {
|
||||
console.error(msg);
|
||||
if (typeof msg == 'object') {
|
||||
$("#logDiv").css("font-color", "red").text(JSON.stringify(msg));
|
||||
} else {
|
||||
$("#logDiv").css("font-color", "red").text(msg);
|
||||
}
|
||||
}
|
||||
function formatAmount(amount, currency, decimals) {
|
||||
if (amount) {
|
||||
let isNegative = (amount < 0);
|
||||
let s = Math.abs(amount).toFixed(decimals);
|
||||
if (Math.abs(amount) < 0.01) {
|
||||
s = Math.abs(amount).toPrecision(2);
|
||||
}
|
||||
let pos = s.indexOf('.');
|
||||
let wholeNumber = (pos >= 0) ? s.substring(0, pos) : s;
|
||||
let decimal = (pos >= 0) ? s.substring(pos) : '';
|
||||
s = '';
|
||||
while (wholeNumber.length > 0) {
|
||||
//if (s.length > 0 && wholeNumber!='-') s = ',' + s; //Don't add a comma if the only thing left to add to the string is the minus sign
|
||||
if (s.length > 0) s = ',' + s;
|
||||
if (wholeNumber.length >= 3) {
|
||||
s = wholeNumber.substring(wholeNumber.length - 3) + s;
|
||||
wholeNumber = wholeNumber.substring(0, wholeNumber.length - 3);
|
||||
} else {
|
||||
s = wholeNumber + s;
|
||||
wholeNumber = '';
|
||||
}
|
||||
}
|
||||
s = s + decimal;
|
||||
let sign = isNegative ? '-' : '';
|
||||
switch (currency) {
|
||||
case 'GBP':
|
||||
s = sign + '£' + s;
|
||||
break;
|
||||
case 'GBp':
|
||||
s = sign + s + 'p';
|
||||
break;
|
||||
case 'USD':
|
||||
s = sign + 'U$' + s;
|
||||
break;
|
||||
case 'AUD':
|
||||
s = sign + 'A$' + s;
|
||||
break;
|
||||
case 'EUR':
|
||||
s = sign + '€' + s;
|
||||
break;
|
||||
default:
|
||||
s = sign + currency + s;
|
||||
}
|
||||
return s;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
/*function getProfitOrLoss(BasePrice, CurrentPrice, ShowPercent) {
|
||||
//let priceMovement = (CurrentPrice - BasePrice).toFixed(2);
|
||||
let priceMovement = (CurrentPrice - BasePrice).autoDP();
|
||||
@ -305,12 +364,35 @@ Number.prototype.autoScale = function () {
|
||||
if (percentMovement[0] == '-') percentMovement = percentMovement.substring(1);
|
||||
return '<span class="' + (CurrentPrice < BasePrice ? 'loss' : 'profit') + '">' + (CurrentPrice < BasePrice ? '' : '+') + priceMovement + (ShowPercent ? ' (' + percentMovement + ')' : '') + '</span>';
|
||||
}*/
|
||||
function createCategoryAggregator() {
|
||||
let aggregator = {
|
||||
data: [],
|
||||
indexOfCategory: function (categoryName) { return this.data.map(function (item) { return item.categoryName; }).indexOf(categoryName); },
|
||||
getCategory: function (categoryName) { return this.data[this.indexOfCategory(categoryName)]; },
|
||||
addCategoryAmount: function (categoryName, amount) {
|
||||
let c = this.getCategory(categoryName);
|
||||
if (c) {
|
||||
c.total += amount;
|
||||
} else {
|
||||
this.data.push({ categoryName: categoryName, total: amount });
|
||||
}
|
||||
},
|
||||
getChartData: function () {
|
||||
let d = []
|
||||
for (let i = 0; i < this.data.length; i++) {
|
||||
d.push({ label: this.data[i].categoryName, data: this.data[i].total });
|
||||
}
|
||||
return d;
|
||||
}
|
||||
};
|
||||
return aggregator;
|
||||
}
|
||||
|
||||
export function getInstruments() {
|
||||
try {
|
||||
//logInfo('getInstruments()');
|
||||
vars.initialPageTitle = document.title;
|
||||
if (vars.fetchTiming.fetchTimer != 0) { clearTimeout(vars.fetchTiming.fetchTimer) };
|
||||
initialPageTitle = document.title;
|
||||
if (fetchTimer != 0) { clearTimeout(fetchTimer) };
|
||||
indexMarquee.init($('#divIndexMarquee'), ' ');
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
@ -354,7 +436,7 @@ export function getInstruments() {
|
||||
}
|
||||
};
|
||||
|
||||
export function createSharesTable() {
|
||||
function createSharesTable() {
|
||||
try {
|
||||
let excludeNoHoldings = $('#excludeNoHoldings').prop('checked');
|
||||
//logInfo('createSharesTable()');
|
||||
@ -567,45 +649,27 @@ function getNewRemoteData() {
|
||||
|
||||
//logInfo(new Date().yyyymmddhhmmss() + ": getNewRemoteData()")
|
||||
|
||||
let nothingToFetch = false;
|
||||
let lastFetchedDailyInstrument = Instruments.GetLastFetchedDaily();
|
||||
if (lastFetchedDailyInstrument.lastFetchedDaily < (new Date().getTime() - vars.fetchTiming.fetchIntervalDaily)) {
|
||||
if (lastFetchedDailyInstrument.lastFetchedDaily < (new Date().getTime() - fetchIntervalDaily)) {
|
||||
//logInfo(new Date().yyyymmddhhmmss() + ": Last fetched daily: " + lastFetchedDailyInstrument.Symbol + ", Last fetched date: " + new Date(lastFetchedDailyInstrument.lastFetchedDaily).yyyymmddhhmmss());
|
||||
getYahooDailyData(lastFetchedDailyInstrument);
|
||||
} else {
|
||||
let lastFetchedIntradayInstrument = Instruments.GetLastFetchedIntraday();
|
||||
if (lastFetchedIntradayInstrument.lastFetchedIntraday < (new Date().getTime() - vars.fetchTiming.fetchIntervalIntraday)) {
|
||||
if (lastFetchedIntradayInstrument.lastFetchedIntraday < (new Date().getTime() - fetchIntervalIntraday)) {
|
||||
//logInfo(new Date().yyyymmddhhmmss() + ": Last fetched intraday: " + lastFetchedIntradayInstrument.Symbol + ", Last fetched date: " + new Date(lastFetchedIntradayInstrument.lastFetchedIntraday).yyyymmddhhmmss());
|
||||
getYahooIntradayData(lastFetchedIntradayInstrument);
|
||||
} else {
|
||||
let lastFetchedSingleDayInstrument = Instruments.GetLastFetchedSingleDay();
|
||||
if (lastFetchedSingleDayInstrument.lastFetchedSingleDay < (new Date().getTime() - vars.fetchTiming.fetchIntervalSingleDay)) {
|
||||
if (lastFetchedSingleDayInstrument.lastFetchedSingleDay < (new Date().getTime() - fetchIntervalSingleDay)) {
|
||||
//logInfo(new Date().yyyymmddhhmmss() + ": Last fetched single day: " + lastFetchedSingleDayInstrument.Symbol + ", Last fetched date: " + new Date(lastFetchedSingleDayInstrument.lastFetchedSingleDay).yyyymmddhhmmss());
|
||||
getYahooSingleDayData(lastFetchedSingleDayInstrument);
|
||||
} else {
|
||||
} //else {
|
||||
//fetchInterval = 3000;
|
||||
//fetchTimer = setTimeout(getNewRemoteData, fetchInterval);
|
||||
nothingToFetch = true;
|
||||
}
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
var nextFetchDelay = 5000;
|
||||
if (!nothingToFetch) {
|
||||
nextFetchDelay = vars.fetchTiming.lastFetchDuration + 100;
|
||||
}
|
||||
if (vars.fetchTiming.displayTimings) {
|
||||
console.info({
|
||||
msg: 'Fetch timing info',
|
||||
currentTime: (new Date()).yyyymmddhhmmss(),
|
||||
nothingToFetch: nothingToFetch,
|
||||
lastFetchDuration: vars.fetchTiming.lastFetchDuration,
|
||||
nextFetchDelay: nextFetchDelay
|
||||
});
|
||||
}
|
||||
vars.fetchTiming.fetchTimer = setTimeout(getNewRemoteData, nextFetchDelay);
|
||||
} catch (err) {
|
||||
vars.fetchTiming.fetchTimer = setTimeout(getNewRemoteData, 15000);
|
||||
logError({ MSG: "Error in getNewRemoteData", err: err });
|
||||
}
|
||||
}
|
||||
@ -669,21 +733,17 @@ function getAllLocalIntradayData() {
|
||||
}
|
||||
}
|
||||
//fetchTimer = setTimeout(getNewRemoteData, 500);
|
||||
//vars.fetchTiming.fetchTimer = setInterval(getNewRemoteData, 1000);
|
||||
vars.fetchTiming.fetchTimer = setTimeout(getNewRemoteData, 200);
|
||||
//fetchTimer = setInterval(getNewRemoteData, 1250);
|
||||
fetchTimer = setInterval(getNewRemoteData, 1000);
|
||||
},
|
||||
failure: function (response) {
|
||||
vars.fetchTiming.fetchTimer = setTimeout(getNewRemoteData, 5000);
|
||||
console.error("getYTDData error:");
|
||||
console.info(response);
|
||||
}
|
||||
failure: function (response) { console.error("getYTDData error:"); console.info(response); }
|
||||
});
|
||||
} catch (err) {
|
||||
logError({ MSG: "Error in getAllLocalIntradayData", err: err });
|
||||
}
|
||||
}
|
||||
|
||||
export function redrawAllSharesCharts() {
|
||||
function redrawAllSharesCharts() {
|
||||
let excludeNoHoldings = $('#excludeNoHoldings').prop('checked');
|
||||
for (let n = 0; n < Instruments.Data.length; n++) {
|
||||
let i = Instruments.Data[n];
|
||||
@ -710,7 +770,6 @@ function fixDecimals(val, place) {
|
||||
function getYahooDailyData(Instrument) {
|
||||
//console.info('getYahooDailyData: ' + Instrument.Symbol);
|
||||
Instrument.lastFetchedDaily = new Date().getTime();
|
||||
vars.fetchTiming.lastFetchStartTime = new Date().getTime();
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
contentType: "application/json",
|
||||
@ -718,7 +777,6 @@ function getYahooDailyData(Instrument) {
|
||||
dataType: "json",
|
||||
data: JSON.stringify({ Symbol: Instrument.Symbol, MinDailyDT: (Instrument.DailyData.length ? Instrument.DailyData[0].DT: new Date().getTime()) }),
|
||||
success: function (response) {
|
||||
vars.fetchTiming.lastFetchDuration = (new Date().getTime()) - vars.fetchTiming.lastFetchStartTime;
|
||||
let newData = [];
|
||||
if (response.chart && response.chart.result) {
|
||||
let dataSeries = response.chart.result[0];
|
||||
@ -759,8 +817,7 @@ function getYahooDailyData(Instrument) {
|
||||
//console.info("getYahooDailyData: " + Instrument.Symbol + ', Last data point: ' + d);
|
||||
|
||||
if (newData.length > 0) {
|
||||
vars.fetchTiming.lastSuccessfulFetch = new Date();
|
||||
vars.fetchTiming.lastSuccessfulFetchDuration = (new Date()) - vars.fetchTiming.lastSuccessfulFetchStartTime;
|
||||
lastSuccessfulFetch = new Date();
|
||||
}
|
||||
|
||||
submitNewDailyData(Instrument, newData);
|
||||
@ -771,18 +828,13 @@ function getYahooDailyData(Instrument) {
|
||||
logWarning({ MSG: "No data received for symbol: " + Instrument.Symbol, response: response })
|
||||
}
|
||||
},
|
||||
failure: function (response) {
|
||||
vars.fetchTiming.lastFetchDuration = (new Date().getTime()) - vars.fetchTiming.lastFetchStartTime;
|
||||
console.error("getYahooDailyData error:");
|
||||
console.info(response);
|
||||
}
|
||||
failure: function (response) { console.error("getYahooDailyData error:"); console.info(response); }
|
||||
});
|
||||
}
|
||||
function getYahooIntradayData(Instrument) {
|
||||
//console.info('getYahooIntradayData: ' + Instrument.Symbol);
|
||||
Instrument.lastFetchedIntraday = new Date().getTime();
|
||||
let minIntradayDT = Instrument.IntradayData.length > 0 ? Instrument.IntradayData[0].DT : new Date().getTime();
|
||||
vars.fetchTiming.lastFetchStartTime = new Date().getTime();
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
contentType: "application/json",
|
||||
@ -790,7 +842,6 @@ function getYahooIntradayData(Instrument) {
|
||||
dataType: "json",
|
||||
data: JSON.stringify({ Symbol: Instrument.Symbol, MinIntradayDT: minIntradayDT }),
|
||||
success: function (response) {
|
||||
vars.fetchTiming.lastFetchDuration = (new Date().getTime()) - vars.fetchTiming.lastFetchStartTime;
|
||||
let newData = [];
|
||||
if (response.chart && response.chart.result) {
|
||||
let dataSeries = response.chart.result[0];
|
||||
@ -823,7 +874,7 @@ function getYahooIntradayData(Instrument) {
|
||||
//console.warn("Didn't submit " + nonSubmittedEntries.toString() + " entries (" + newData.length.toString() + " OK) with null values for " + Instrument.Symbol);
|
||||
}
|
||||
if (newData.length) {
|
||||
vars.fetchTiming.lastSuccessfulFetch = new Date();
|
||||
lastSuccessfulFetch = new Date();
|
||||
submitNewIntradayData(Instrument, newData);
|
||||
createMidChart(Instrument);
|
||||
createShortChart(Instrument);
|
||||
@ -838,17 +889,12 @@ function getYahooIntradayData(Instrument) {
|
||||
logWarning({ MSG: "No data received for symbol: " + Instrument.Symbol, response: response });
|
||||
}
|
||||
},
|
||||
failure: function (response) {
|
||||
vars.fetchTiming.lastFetchDuration = (new Date().getTime()) - vars.fetchTiming.lastFetchStartTime;
|
||||
console.error("getYahooIntradayData error:");
|
||||
console.info(response);
|
||||
}
|
||||
failure: function (response) { console.error("getYahooIntradayData error:"); console.info(response); }
|
||||
});
|
||||
}
|
||||
function getYahooSingleDayData(Instrument) {
|
||||
//console.info('getYahooSingleDayData: ' + Instrument.Symbol);
|
||||
Instrument.lastFetchedSingleDay = new Date().getTime();
|
||||
vars.fetchTiming.lastFetchStartTime = new Date().getTime();
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
contentType: "application/json",
|
||||
@ -856,7 +902,6 @@ function getYahooSingleDayData(Instrument) {
|
||||
dataType: "json",
|
||||
data: JSON.stringify({ Symbol: Instrument.Symbol }),
|
||||
success: function (response) {
|
||||
vars.fetchTiming.lastFetchDuration = (new Date().getTime()) - vars.fetchTiming.lastFetchStartTime;
|
||||
let newData = [];
|
||||
if (response.chart && response.chart.result) {
|
||||
let dataSeries = response.chart.result[0];
|
||||
@ -885,7 +930,7 @@ function getYahooSingleDayData(Instrument) {
|
||||
//console.warn("Ignored " + ignoredEntries.toString() + " entries (" + newData.length.toString() + " OK) with null values for " + Instrument.Symbol);
|
||||
}
|
||||
if (newData.length > 0) {
|
||||
vars.fetchTiming.lastSuccessfulFetch = new Date();
|
||||
lastSuccessfulFetch = new Date();
|
||||
}
|
||||
|
||||
if (!Instrument.SingleDayData || Instrument.SingleDayData[0].DT != newData[0].DT || newData.length > Instrument.SingleDayData.length) {
|
||||
@ -915,11 +960,7 @@ function getYahooSingleDayData(Instrument) {
|
||||
logWarning({ MSG: "No data received for symbol: " + Instrument.Symbol, response: response });
|
||||
}
|
||||
},
|
||||
failure: function (response) {
|
||||
vars.fetchTiming.lastFetchDuration = (new Date().getTime()) - vars.fetchTiming.lastFetchStartTime;
|
||||
console.error("getYahooSingleDayData error:");
|
||||
console.info(response);
|
||||
}
|
||||
failure: function (response) { console.error("getYahooSingleDayData error:"); console.info(response); }
|
||||
});
|
||||
}
|
||||
function getLseSingleDayData(Instrument) {
|
||||
@ -927,7 +968,6 @@ function getLseSingleDayData(Instrument) {
|
||||
Instrument.lastFetchedSingleDay = new Date().getTime();
|
||||
let fromDate = new Date().setHours(0, 0, 0);
|
||||
let toDate = new Date().getTime();
|
||||
vars.fetchTiming.lastFetchStartTime = new Date().getTime();
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
contentType: "application/json",
|
||||
@ -935,15 +975,10 @@ function getLseSingleDayData(Instrument) {
|
||||
dataType: "json",
|
||||
data: JSON.stringify({ Symbol: Instrument.Symbol, FromDate: fromDate, ToDate: toDate }),
|
||||
success: function (response) {
|
||||
vars.fetchTiming.lastFetchDuration = (new Date().getTime()) - vars.fetchTiming.lastFetchStartTime;
|
||||
console.info("getLseSingleDayData: " + JSON.stringify(response));
|
||||
|
||||
},
|
||||
failure: function (response) {
|
||||
vars.fetchTiming.lastFetchDuration = (new Date().getTime()) - vars.fetchTiming.lastFetchStartTime;
|
||||
console.error("getLseSingleDayData error:");
|
||||
console.info(response);
|
||||
}
|
||||
failure: function (response) { console.error("getLseSingleDayData error:"); console.info(response); }
|
||||
});
|
||||
}
|
||||
|
||||
@ -1053,7 +1088,7 @@ function updateTables() {
|
||||
if (tablesUpdateTimings.updateNeeded && tablesUpdateTimings.lastUpdate + tablesUpdateTimings.timeBetweenUpdates < new Date().getTime()) {
|
||||
tablesUpdateTimings.lastUpdate = new Date().getTime();
|
||||
|
||||
createHoldingsTable(Instruments, vars);
|
||||
createHoldingsTable();
|
||||
createAccountsTables();
|
||||
createAnalysisTable();
|
||||
createIndexMarquee();
|
||||
@ -1061,6 +1096,570 @@ function updateTables() {
|
||||
}
|
||||
}
|
||||
|
||||
function createHoldingsTable() {
|
||||
function getHoldings() {
|
||||
function aggregateHoldings(i) {
|
||||
let aggUnits = 0;
|
||||
let aggBookCost = 0;
|
||||
let aggBookCostGBP = 0;
|
||||
let minPurchaseData = new Date().getTime();
|
||||
let maxPurchaseDate = 0;
|
||||
let marketIsOpen = Instruments.MarketIsOpen(i);
|
||||
let exchangeRate = Instruments.GetExchangeRate(i.Currency, 'GBP');
|
||||
let accountNames = [];
|
||||
let aggSingleDayGainGBP = 0;
|
||||
|
||||
for (let hn = 0; hn < i.Holdings.length; hn++) {
|
||||
let h = i.Holdings[hn];
|
||||
|
||||
if (h.SoldDate == null && h.NoUnits != 0) {
|
||||
aggUnits += h.NoUnits;
|
||||
aggBookCost += h.NoUnits * h.PurchasePricePerUnit;
|
||||
//aggBookCostGBP += h.NoUnits * h.PurchasePricePerUnit / exchangeRate;
|
||||
aggBookCostGBP += h.BookCostGBP;
|
||||
//bookCostPerAccount.addCategoryAmount(h.AccountName, (h.NoUnits * h.PurchasePricePerUnit) / exchangeRate);
|
||||
bookCostPerAccount.addCategoryAmount(h.AccountName, h.BookCostGBP);
|
||||
|
||||
if (h.PurchaseDate < minPurchaseData) { minPurchaseData = h.PurchaseDate; }
|
||||
if (h.PurchaseDate > maxPurchaseDate) { maxPurchaseDate = h.PurchaseDate; }
|
||||
if (!accountNames.includes(h.AccountName)) { accountNames.push(h.AccountName); }
|
||||
|
||||
if (i.CurrentPrice && exchangeRate) {
|
||||
aggSingleDayGainGBP += ((i.CurrentPrice - (h.PurchaseDate > i.SingleDayStartDate ? h.PurchasePricePerUnit : i.SingleDayPreviousClose)) * h.NoUnits) / exchangeRate;
|
||||
currentValuePerAccount.addCategoryAmount(h.AccountName, (h.NoUnits * i.CurrentPrice) / exchangeRate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let aggPurchasePricePerUnit = aggBookCost / aggUnits;
|
||||
let aggCurrentValue = 0,
|
||||
aggCurrentValueGBP = 0,
|
||||
aggGainGBP = 0,
|
||||
aggSingleDayGainPercent = 0,
|
||||
aggSingleDayProfitOrLoss = 0;
|
||||
if (i.CurrentPrice) {
|
||||
aggCurrentValue = aggUnits * (i.Currency == 'GBp' ? i.CurrentPrice / 100 : i.CurrentPrice);
|
||||
if (exchangeRate) {
|
||||
aggCurrentValueGBP = (aggUnits * i.CurrentPrice) / exchangeRate;
|
||||
//aggGainGBP = ((i.CurrentPrice - aggPurchasePricePerUnit) * aggUnits) / exchangeRate;
|
||||
aggGainGBP = aggCurrentValueGBP - aggBookCostGBP;
|
||||
aggSingleDayGainPercent = ((i.CurrentPrice / i.SingleDayPreviousClose) - 1) * 100;
|
||||
aggSingleDayProfitOrLoss = aggSingleDayGainGBP < 0 ? 'loss' : 'profit';
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
displayOrder: i.DisplayOrder,
|
||||
holdingNumber: 0,
|
||||
instrumentType: i.InstrumentType,
|
||||
instrumentName: i.InstrumentName,
|
||||
displayName: i.DisplayName,
|
||||
symbol: i.Symbol,
|
||||
currency: i.Currency,
|
||||
accountName: accountNames.length > 1 ? 'Multiple (' + accountNames.length + ')' : accountNames[0],
|
||||
accountNames: accountNames.length > 1 ? accountNames.join(', ') : '',
|
||||
purchaseDateMin: minPurchaseData,
|
||||
purchaseDateMax: maxPurchaseDate,
|
||||
noUnits: aggUnits,
|
||||
purchasePricePerUnit: aggPurchasePricePerUnit,
|
||||
bookCost: aggBookCost,
|
||||
bookCostGBP: aggBookCostGBP,
|
||||
currentValue: aggCurrentValue,
|
||||
gain: (i.Currency == 'GBp' ? (i.CurrentPrice - aggPurchasePricePerUnit) / 100 : (i.CurrentPrice - aggPurchasePricePerUnit)) * aggUnits,
|
||||
//gainPercent: (i.CurrentPrice - aggPurchasePricePerUnit) / aggPurchasePricePerUnit * 100,
|
||||
gainPercent: aggGainGBP / aggBookCostGBP * 100,
|
||||
currentValueGBP: aggCurrentValueGBP,
|
||||
gainGBP: aggGainGBP,
|
||||
singleDayGainGBP: aggSingleDayGainGBP,
|
||||
singleDayGainPercent: aggSingleDayGainPercent,
|
||||
singleDayProfitOrLoss: aggSingleDayProfitOrLoss,
|
||||
currentPrice: i.CurrentPrice,
|
||||
currentExchangeRate: exchangeRate,
|
||||
marketIsOpen: marketIsOpen
|
||||
};
|
||||
}
|
||||
|
||||
//Create a structure to summarise total holdings in each currency, to be used in the pie chart
|
||||
let holdingCurrencies = {
|
||||
data: [],
|
||||
indexOfHoldingCurrency: function (symbol) { return this.data.map(function (item) { return item.symbol; }).indexOf(symbol); },
|
||||
getHoldingCurrency: function (symbol) { return this.data[this.indexOfHoldingCurrency(symbol)]; },
|
||||
addHoldingCurrency: function (symbol, amount) {
|
||||
let c = this.getHoldingCurrency(symbol);
|
||||
if (c) {
|
||||
c.total += amount;
|
||||
} else {
|
||||
this.data.push({ symbol: symbol, total: amount });
|
||||
}
|
||||
},
|
||||
getChartData: function () {
|
||||
let d = []
|
||||
for (let i = 0; i < this.data.length; i++) {
|
||||
//d.push({ label: this.data[i].symbol, data: this.data[i].total / Instruments.GetExchangeRate(this.data[i].symbol, 'GBP') });
|
||||
d.push({ label: this.data[i].symbol, data: this.data[i].total });
|
||||
}
|
||||
return d;
|
||||
}
|
||||
};
|
||||
let holdingCurrenciesCurrentValue = {
|
||||
data: [],
|
||||
indexOfHoldingCurrency: function (symbol) { return this.data.map(function (item) { return item.symbol; }).indexOf(symbol); },
|
||||
getHoldingCurrency: function (symbol) { return this.data[this.indexOfHoldingCurrency(symbol)]; },
|
||||
addHoldingCurrency: function (symbol, amount) {
|
||||
let c = this.getHoldingCurrency(symbol);
|
||||
if (c) {
|
||||
c.total += amount;
|
||||
} else {
|
||||
this.data.push({ symbol: symbol, total: amount });
|
||||
}
|
||||
},
|
||||
getChartData: function () {
|
||||
let d = []
|
||||
for (let i = 0; i < this.data.length; i++) {
|
||||
d.push({ label: this.data[i].symbol, data: this.data[i].total / Instruments.GetExchangeRate(this.data[i].symbol, 'GBP') });
|
||||
}
|
||||
return d;
|
||||
}
|
||||
};
|
||||
let bookCostPerAccount = createCategoryAggregator();
|
||||
let currentValuePerAccount = createCategoryAggregator();
|
||||
let totalHoldingsValueGBP = 0;
|
||||
let totalBookCostGBP = 0;
|
||||
let totalValueGBP = 0;
|
||||
let totalGainGBP = 0;
|
||||
let todaysGainGBP = 0;
|
||||
let maxCurrentValueGBP = 0;
|
||||
let groupHoldings = $('#groupBySymbol').prop('checked');
|
||||
let r = [];
|
||||
let w = []; //Watchlist (instruments with no current holdings)
|
||||
|
||||
for (let n = 0; n < Instruments.Data.length; n++) {
|
||||
let i = Instruments.Data[n];
|
||||
//if (i.InstrumentType == 'EQUITY') {
|
||||
if (i.InstrumentType == 'EQUITY' || i.InstrumentType == 'ETF' || i.InstrumentType == 'CRYPTOCURRENCY' || i.InstrumentType == 'CURRENCY') {
|
||||
//if (i.CurrentPrice) {
|
||||
if (groupHoldings) {
|
||||
let agg = aggregateHoldings(i);
|
||||
if (agg.noUnits > 0) {
|
||||
r.push(agg);
|
||||
|
||||
//holdingCurrencies.addHoldingCurrency(i.Currency, agg.noUnits * agg.purchasePricePerUnit);
|
||||
//holdingCurrencies.addHoldingCurrency(i.Currency.toUpperCase(), agg.noUnits * agg.purchasePricePerUnit / (i.Currency == "GBp" ? 100 : 1));
|
||||
holdingCurrencies.addHoldingCurrency(i.Currency.toUpperCase(), agg.bookCostGBP);
|
||||
if (i.CurrentPrice) {
|
||||
if (agg.currentValueGBP > maxCurrentValueGBP) { maxCurrentValueGBP = agg.currentValueGBP };
|
||||
//holdingCurrenciesCurrentValue.addHoldingCurrency(i.Currency, agg.noUnits * i.CurrentPrice);
|
||||
holdingCurrenciesCurrentValue.addHoldingCurrency(i.Currency.toUpperCase(), agg.noUnits * i.CurrentPrice / (i.Currency == "GBp" ? 100 : 1));
|
||||
if (agg.currentExchangeRate) {
|
||||
totalBookCostGBP += agg.bookCostGBP;
|
||||
totalValueGBP += agg.currentValueGBP;
|
||||
totalGainGBP += agg.gainGBP;
|
||||
todaysGainGBP += (agg.marketIsOpen == 0 ? 0 : agg.singleDayGainGBP);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//No current holdings for this instrument - add it to the watchlist
|
||||
if (i.WatchlistID) {
|
||||
w.push(i);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let marketIsOpen = Instruments.MarketIsOpen(i);
|
||||
let exchangeRate = Instruments.GetExchangeRate(i.Currency, 'GBP');
|
||||
|
||||
let noCurrentHoldings = 0;
|
||||
for (let hn = 0; hn < i.Holdings.length; hn++) {
|
||||
let h = i.Holdings[hn];
|
||||
if (h.SoldDate == null) {
|
||||
noCurrentHoldings++;
|
||||
let holdingBookCost = h.NoUnits * h.PurchasePricePerUnit;
|
||||
|
||||
let currentHoldingValue = h.NoUnits * (i.Currency == 'GBp' ? i.CurrentPrice / 100 : i.CurrentPrice);
|
||||
|
||||
let holdingCurrentValueGBP = 0,
|
||||
holdingGainGBP = 0,
|
||||
singleDayGainGBP = 0,
|
||||
singleDayGainPercent = 0,
|
||||
singleDayProfitOrLoss = 0;
|
||||
if (i.CurrentPrice) {
|
||||
let currentHoldingValue = h.NoUnits * (i.Currency == 'GBp' ? i.CurrentPrice / 100 : i.CurrentPrice);
|
||||
|
||||
if (exchangeRate) {
|
||||
let holdingCurrentValueGBP = (h.NoUnits * i.CurrentPrice) / exchangeRate;
|
||||
//let holdingGainGBP = ((i.CurrentPrice - h.PurchasePricePerUnit) * h.NoUnits) / exchangeRate;
|
||||
let holdingGainGBP = holdingCurrentValueGBP - h.BookCostGBP;
|
||||
|
||||
let singleDayGainGBP = ((i.CurrentPrice - (h.PurchaseDate > i.SingleDayStartDate ? h.PurchasePricePerUnit : i.SingleDayPreviousClose)) * h.NoUnits) / exchangeRate;
|
||||
let singleDayGainPercent = ((i.CurrentPrice / i.SingleDayPreviousClose) - 1) * 100;
|
||||
let singleDayProfitOrLoss = singleDayGainGBP < 0 ? 'loss' : 'profit';
|
||||
|
||||
if (holdingCurrentValueGBP > maxCurrentValueGBP) { maxCurrentValueGBP = holdingCurrentValueGBP };
|
||||
}
|
||||
}
|
||||
|
||||
r.push({
|
||||
displayOrder: i.DisplayOrder,
|
||||
holdingNumber: hn,
|
||||
instrumentType: i.InstrumentType,
|
||||
instrumentName: i.InstrumentName,
|
||||
displayName: i.DisplayName,
|
||||
symbol: i.Symbol,
|
||||
currency: i.Currency,
|
||||
accountName: h.AccountName,
|
||||
purchaseDateMin: h.PurchaseDate,
|
||||
purchaseDateMax: h.PurchaseDate,
|
||||
noUnits: h.NoUnits,
|
||||
purchasePricePerUnit: h.PurchasePricePerUnit,
|
||||
bookCost: holdingBookCost,
|
||||
currentValue: currentHoldingValue,
|
||||
gain: (i.Currency == 'GBp' ? (i.CurrentPrice - h.PurchasePricePerUnit) / 100 : (i.CurrentPrice - h.PurchasePricePerUnit)) * h.NoUnits,
|
||||
//gainPercent: (i.CurrentPrice - h.PurchasePricePerUnit) / h.PurchasePricePerUnit * 100,
|
||||
gainPercent: holdingGainGBP / h.BookCostGBP * 100,
|
||||
currentValueGBP: holdingCurrentValueGBP,
|
||||
gainGBP: holdingGainGBP,
|
||||
singleDayGainGBP: singleDayGainGBP,
|
||||
singleDayGainPercent: singleDayGainPercent,
|
||||
singleDayProfitOrLoss: singleDayProfitOrLoss,
|
||||
currentPrice: i.CurrentPrice,
|
||||
currentExchangeRate: exchangeRate,
|
||||
marketIsOpen: marketIsOpen
|
||||
});
|
||||
|
||||
holdingCurrencies.addHoldingCurrency(i.Currency, h.NoUnits * h.PurchasePricePerUnit);
|
||||
if (i.CurrentPrice) {
|
||||
holdingCurrenciesCurrentValue.addHoldingCurrency(i.Currency, h.NoUnits * i.CurrentPrice);
|
||||
if (exchangeRate) {
|
||||
currentValuePerAccount.addCategoryAmount(h.AccountName, holdingCurrentValueGBP);
|
||||
bookCostPerAccount.addCategoryAmount(h.AccountName, holdingBookCost / exchangeRate);
|
||||
|
||||
//totalBookCostGBP += holdingBookCost / exchangeRate;
|
||||
totalBookCostGBP += h.BookCostGBP;
|
||||
totalValueGBP += holdingCurrentValueGBP;
|
||||
totalGainGBP += holdingGainGBP;
|
||||
todaysGainGBP += (marketIsOpen == 0 ? 0 : singleDayGainGBP);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (noCurrentHoldings == 0) {
|
||||
//No current holdings for this instrument - add it to the watchlist
|
||||
if (i.WatchlistID) {
|
||||
w.push(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//} //if (i.CurrentPrice)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
totalBookCostGBP: totalBookCostGBP,
|
||||
totalValueGBP: totalValueGBP,
|
||||
totalGainGBP: totalGainGBP,
|
||||
todaysGainGBP: todaysGainGBP,
|
||||
maxCurrentValueGBP: maxCurrentValueGBP,
|
||||
holdings: r,
|
||||
watchlist: w,
|
||||
holdingCurrencies: holdingCurrencies,
|
||||
holdingCurrenciesCurrentValue: holdingCurrenciesCurrentValue,
|
||||
bookCostPerAccount: bookCostPerAccount,
|
||||
currentValuePerAccount: currentValuePerAccount
|
||||
};
|
||||
}
|
||||
function createTable() {
|
||||
let t = $("#tblMainHoldings");
|
||||
if (t.length) {
|
||||
return;
|
||||
} else {
|
||||
let tbl = '<table id="tblMainHoldings" class="mainHoldings"><thead>' +
|
||||
'<tr><th>Type</th>' +
|
||||
'<th>Name</th>' +
|
||||
'<th>Symbol</th>' +
|
||||
'<th>Ccy</th>' +
|
||||
'<th>Account</th>' +
|
||||
'<th>Buy Date</th>' +
|
||||
'<th>No Units</th>' +
|
||||
'<th>Buy<br>Price</th>' +
|
||||
'<th>Current<br>Price</th>' +
|
||||
'<th>Book<br>Cost</th>' +
|
||||
'<th>Current<br>Value</th>' +
|
||||
'<th>Gain</th>' +
|
||||
'<th>Gain £</th>' +
|
||||
'<th>Gain %</th>' +
|
||||
'<th>Current<br>Value £</th>' +
|
||||
'<th>Allocation</th>' +
|
||||
'<th>Today's<br>Gain £</th>' +
|
||||
'<th>Today's<br>Gain %</th>' +
|
||||
'</tr></thead><tbody id="tbMainHoldings"></tbody><tfoot id="tfMainHoldings"><tr><td colspan="18"> </td></tr></tfoot></table>';
|
||||
|
||||
//$('#holdingsDiv').html(tbl);
|
||||
$('#holdingsTableDiv').html(tbl);
|
||||
|
||||
$("#tblMainHoldings").tablesorter({ ignoreCase: false });
|
||||
$("#tblMainHoldings").trigger("sorton", Cookies.get('sortHoldings'));
|
||||
$('#tblMainHoldings').on('sortEnd', function (event) {
|
||||
// Prints the current sort order to the console
|
||||
if (event.target.config.sortList.length > 3) {
|
||||
console.info({ "Setting sortHoldings Cookie (array too long?)": event.target.config.sortList });
|
||||
}
|
||||
Cookies.set('sortHoldings', event.target.config.sortList, { 'sameSite': 'strict' });
|
||||
});
|
||||
}
|
||||
}
|
||||
function createWatchlistTable() {
|
||||
let t = $("#tblWatchlist");
|
||||
if (t.length) {
|
||||
return;
|
||||
} else {
|
||||
let tbl = '<table id="tblWatchlist" class="watchlist"><thead>' +
|
||||
'<tr><th>Type</th>' +
|
||||
'<th>Name</th>' +
|
||||
'<th>Symbol</th>' +
|
||||
'<th>Ccy</th>' +
|
||||
'<th>Current Price</th>' +
|
||||
'<th>Today's Gain</th>' +
|
||||
'<th>Today's Gain %</th>' +
|
||||
'<th>Since Last Sold<th>' +
|
||||
'</tr></thead><tbody id="tbWatchlist"></tbody></table>';
|
||||
|
||||
$('#watchlistTableDiv').html(tbl);
|
||||
|
||||
$("#tblWatchlist").tablesorter({ ignoreCase: false });
|
||||
$("#tblWatchlist").trigger("sorton", Cookies.get('sortWatchlist'));
|
||||
$('#tblWatchlist').on('sortEnd', function (event) {
|
||||
// Prints the current sort order to the console
|
||||
Cookies.set('sorWatchlist', event.target.config.sortList, { 'sameSite': 'strict' });
|
||||
});
|
||||
}
|
||||
}
|
||||
function addTableRow(rowID, content, tbodyID, highlightUpdates) {
|
||||
let r = $("#" + rowID);
|
||||
let newText = $(content).text(); //$(content).text();
|
||||
if (r.length) {
|
||||
let currentContent = r.text();
|
||||
if (currentContent != newText) { //Only update row if the content has changed
|
||||
$("#" + rowID).replaceWith(content);
|
||||
if (highlightUpdates) {
|
||||
$("#" + rowID).addClass("highlighted");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$("#" + tbodyID).append(content); //.addClass("highlighted");
|
||||
//$("#" + rowID).addClass("highlighted");
|
||||
}
|
||||
}
|
||||
function getPercentCell(backgroundColor, cellWidth, valuePercent, label) {
|
||||
let labelText = label ? label : (valuePercent.toFixed(1) + '%');
|
||||
let barWidth = ((valuePercent / 100) * cellWidth);
|
||||
return '<td>' +
|
||||
'<div class="pcBackground" style="background-color: ' + backgroundColor + '; width:' + barWidth.toFixed(0) + 'px;"> </div>' +
|
||||
'<div class="pcLabel" style="width:' + cellWidth + 'px">' + labelText + '</div>' +
|
||||
'</td>';
|
||||
}
|
||||
function profitOrLoss(value) {
|
||||
return value >= 0 ? "profit" : "loss";
|
||||
}
|
||||
|
||||
let t = getHoldings();
|
||||
let totalBookCostGBP = t.totalBookCostGBP;
|
||||
let totalValueGBP = t.totalValueGBP;
|
||||
let totalGainGBP = t.totalGainGBP;
|
||||
let todaysGainGBP = t.todaysGainGBP;
|
||||
let maxCurrentValueGBP = t.maxCurrentValueGBP;
|
||||
let holdings = t.holdings;
|
||||
let watchlist = t.watchlist;
|
||||
let holdingCurrencies = t.holdingCurrencies;
|
||||
let holdingCurrenciesCurrentValue = t.holdingCurrenciesCurrentValue;
|
||||
let bookCostPerAccount = t.bookCostPerAccount;
|
||||
let currentValuePerAccount = t.currentValuePerAccount;
|
||||
|
||||
let maxAllocationPercent = (maxCurrentValueGBP / totalValueGBP) * 100;
|
||||
let allocationScaleFactor = 100 / maxAllocationPercent;
|
||||
|
||||
let altRow = 1;
|
||||
|
||||
createTable();
|
||||
for (let n = 0; n < holdings.length; n++) {
|
||||
try {
|
||||
altRow = !altRow;
|
||||
|
||||
let h = holdings[n];
|
||||
let type = h.instrumentType == 'CURRENCY' ? '$' : h.instrumentType.substring(0, 1);
|
||||
//let profitOrLoss = h.currentPrice < h.purchasePricePerUnit ? 'loss' : 'profit'; //let profitOrLoss = i.CurrentPrice < h.PurchasePricePerUnit ? 'loss' : 'profit';
|
||||
let openOrClosed = h.marketIsOpen == 1 ? 'open' : 'closed';
|
||||
let allocationPercent = h.currentValueGBP / totalValueGBP * 100;
|
||||
let scaledAllocation = allocationScaleFactor * allocationPercent;
|
||||
|
||||
let rowID = "holdingRow_" + h.displayOrder + "_" + h.holdingNumber;
|
||||
let row = '<tr' + (altRow ? ' class="altShareRow"' : '') + ' id="' + rowID + '">' +
|
||||
'<td class="' + openOrClosed + '">' + type + '</td>' +
|
||||
'<td class="' + openOrClosed + '">' + (h.displayName != '' ? h.displayName : h.instrumentName) + '</td>' +
|
||||
'<td><a target="_blank" href="https://uk.finance.yahoo.com/quote/' + h.symbol + '">' + h.symbol + '</a>' + '</td>' +
|
||||
'<td class="' + openOrClosed + '">' + h.currency + '</td>' +
|
||||
'<td class="' + openOrClosed + '" title="' + (h.accountNames ? h.accountNames : '') + '">' + h.accountName + '</td>' +
|
||||
'<td class="' + openOrClosed + '"' + (h.purchaseDateMin == h.purchaseDateMax ? ('>' + new Date(h.purchaseDateMin).yyyymmdd()) : (' title="' + new Date(h.purchaseDateMax).yyyymmdd() + ' - ' + new Date(h.purchaseDateMin).yyyymmdd() + '">' + new Date(h.purchaseDateMin).yyyymmdd()) + '+') + '</td>' +
|
||||
//'<td class="num ' + openOrClosed + '">' + formatAmount(h.noUnits, '', 0) + '</td>' +
|
||||
'<td class="num ' + openOrClosed + '">' + h.noUnits.autoScale() + '</td>' +
|
||||
'<td class="num ' + openOrClosed + '">' + formatAmount(h.purchasePricePerUnit, h.currency, 2) + '</td>' + //Buy Price
|
||||
'<td class="num ' + openOrClosed + '">' + (h.currentPrice ? formatAmount(h.currentPrice, h.currency, 2) : '') + '</td>' + //Current Price
|
||||
'<td class="num ' + openOrClosed + '">' + formatAmount(h.currency == 'GBp' ? h.bookCost / 100 : h.bookCost, h.currency == 'GBp' ? 'GBP' : h.currency, 0) + '</td>' + //Book Cost
|
||||
'<td class="num ' + openOrClosed + '">' + (h.currentExchangeRate ? formatAmount(h.currentValue, h.currency == 'GBp' ? 'GBP' : h.currency, 0) : '') + '</td>' + //Current Value
|
||||
'<td class="num ' + profitOrLoss(h.currentPrice - h.purchasePricePerUnit) + '">' + (h.currentPrice ? formatAmount(h.gain, h.currency == 'GBp' ? 'GBP' : h.currency, 0) : '') + '</td>' + //Gain
|
||||
'<td class="num ' + profitOrLoss(h.gainGBP) + '">' + (h.currentExchangeRate ? formatAmount(h.gainGBP, 'GBP', 0) : '') + '</td>' + //Gain £
|
||||
'<td class="num ' + profitOrLoss(h.gainGBP) + '">' + (h.currentExchangeRate ? h.gainPercent.toFixed(1) + '%' : '') + '</td>' + //Gain %
|
||||
'<td class="num ' + openOrClosed + '">' + (h.currentExchangeRate ? formatAmount(h.currentValueGBP, 'GBP', 0) : '') + '</td>' + //Current Value £
|
||||
(h.currentExchangeRate ? getPercentCell('#3f2d95', 70, scaledAllocation, allocationPercent.toFixed(1) + '%') : '') + //Allocation
|
||||
(h.currentExchangeRate ? (h.marketIsOpen == 0 ? '<td/>' : '<td class="num ' + h.singleDayProfitOrLoss + '">' + formatAmount(h.singleDayGainGBP, 'GBP', 0) + '</td>') : '') + //Today's Gain £
|
||||
(h.singleDayGainPercent ? (h.marketIsOpen == 0 ? '<td/>' : '<td class="num ' + h.singleDayProfitOrLoss + '">' + h.singleDayGainPercent.toFixed(1) + '%</td>') : '<td/>') + //Today's Gain %
|
||||
'</tr>';
|
||||
addTableRow(rowID, row, "tbMainHoldings", true);
|
||||
}
|
||||
catch (err) {
|
||||
logError("Error adding " + holdings[n].symbol + " to main holdings table: " + err.message);
|
||||
}
|
||||
}
|
||||
|
||||
//Add the totals line at the bottom of the table
|
||||
//let profitOrLoss = totalValueGBP < totalBookCostGBP ? 'loss' : 'profit';
|
||||
let todaysProfitOrLoss = todaysGainGBP < 0 ? 'loss' : 'profit';
|
||||
let lSF = '';
|
||||
if (lastSuccessfulFetch) {
|
||||
let lastFetchAge = ((new Date()) - lastSuccessfulFetch);
|
||||
let staleFetch = ((new Date()) - lastSuccessfulFetch) > (timespans.oneMinute * 3);
|
||||
lSF = (lastSuccessfulFetch ? ' - Last Successful Fetch: ' + (staleFetch ? '<span class="loss">' : '') + lastSuccessfulFetch.yyyymmddhhmmss() + (staleFetch ? '</span>' : '') : '');
|
||||
}
|
||||
let row = '<tr id="tfHoldingsTotals"><td></td><td id="holdingsLastUpdated" colspan="8">Updated: ' + new Date().yyyymmddhhmmss() + lSF + '</td>' +
|
||||
'<td class="num">' + formatAmount(totalBookCostGBP, 'GBP', 0) + '</td>' + //Book Cost
|
||||
'<td></td><td></td>' +
|
||||
'<td class="num ' + profitOrLoss(totalValueGBP - totalBookCostGBP) + '">' + formatAmount(totalGainGBP, 'GBP', 0) + '</td>' + //Gain GBP
|
||||
'<td class="num ' + profitOrLoss(totalValueGBP - totalBookCostGBP) + '">' + formatAmount((((totalValueGBP / totalBookCostGBP) - 1) * 100), '', 1) + '%</td>' + //Gain %
|
||||
'<td class="num">' + formatAmount(totalValueGBP, 'GBP', 0) + '</td>' + //Current Value
|
||||
'<td> </td>' + //Allocation
|
||||
'<td class="num ' + todaysProfitOrLoss + '">' + formatAmount(todaysGainGBP, 'GBP', 0) + '</td>' + //Today's Gain GBP
|
||||
//'<td class="num ' + todaysProfitOrLoss + '">' + formatAmount(((todaysGainGBP / totalBookCostGBP) * 100), '', 1) + '%</td>' + //Today's Gain %
|
||||
'<td class="num ' + todaysProfitOrLoss + '">' + formatAmount(((todaysGainGBP / (totalValueGBP-todaysGainGBP)) * 100), '', 1) + '%</td>' + //Today's Gain %
|
||||
'</tr>';
|
||||
addTableRow("tfHoldingsTotals", row, "tfMainHoldings", false);
|
||||
|
||||
//Create the watchlist table
|
||||
createWatchlistTable();
|
||||
altRow = 1;
|
||||
for (let n = 0; n < watchlist.length; n++) {
|
||||
altRow = !altRow;
|
||||
let wi = watchlist[n];
|
||||
try {
|
||||
let type = wi.InstrumentType == 'CURRENCY' ? '$' : wi.InstrumentType.substring(0, 1);
|
||||
let openOrClosed = Instruments.MarketIsOpen(wi) == 1 ? 'open' : 'closed';
|
||||
let profitOrLoss = wi.CurrentPrice < wi.SingleDayPreviousClose ? 'loss' : 'profit';
|
||||
let rowID = "watchlistRow_" + wi.DisplayOrder;
|
||||
|
||||
let sinceLastSold = '<td></td>';
|
||||
if (wi.LastSoldPrice) {
|
||||
let delta = (wi.CurrentPrice - wi.LastSoldPrice) / wi.LastSoldPrice * 100;
|
||||
let tooltip = new Date(wi.LastSoldDate).yyyymmdd() + ' @ ' + wi.LastSoldPrice.toFixed(2);
|
||||
sinceLastSold = '<td title="' + tooltip + '" class="num ' + (delta < 0 ? 'loss' : 'profit') + '">' + (delta).toFixed(1) + '%</td>';
|
||||
}
|
||||
|
||||
//Type, Name, Symbol, Ccy, Current Price, Today's Gain, Today's Gain %
|
||||
let row = '<tr' + (altRow ? ' class="altShareRow"' : '') + ' id="' + rowID + '">' +
|
||||
'<td class="' + openOrClosed + '">' + type + '</td>' +
|
||||
'<td class="' + openOrClosed + '">' + wi.InstrumentName + '</td>' +
|
||||
'<td><a target="_blank" href="https://uk.finance.yahoo.com/quote/' + wi.Symbol + '">' + wi.Symbol + '</a>' + '</td>' +
|
||||
'<td class="' + openOrClosed + '">' + wi.Currency + '</td>' +
|
||||
'<td class="num ' + openOrClosed + '">' + (wi.CurrentPrice ? formatAmount(wi.CurrentPrice, wi.Currency, 2) : '') + '</td>' + //Current Price
|
||||
(Instruments.marketIsOpen == 0 ? '<td/>' : '<td class="num ' + profitOrLoss + '">' + (wi.CurrentPrice ? formatAmount(wi.CurrentPrice - wi.SingleDayPreviousClose, wi.Currency, 2) : '') + '</td>') + //Today's Gain
|
||||
(Instruments.marketIsOpen == 0 ? '<td/>' : '<td class="num ' + profitOrLoss + '">' + (wi.CurrentPrice ? ((wi.CurrentPrice - wi.SingleDayPreviousClose) / wi.SingleDayPreviousClose * 100).toFixed(1) : '') + '</td>') + //Today's Gain %
|
||||
//'<td>' + (new Date(wi.LastSoldDate)).yyyymmdd() + '</td>' +
|
||||
sinceLastSold +
|
||||
'</tr>';
|
||||
addTableRow(rowID, row, "tbWatchlist", true);
|
||||
}
|
||||
catch (err) {
|
||||
logError("Error adding " + wi.symbol + " to watchlist table: " + err.message);
|
||||
}
|
||||
}
|
||||
|
||||
//Flash the last updated cell
|
||||
$("#holdingsLastUpdated").addClass("highlighted");
|
||||
//Remove the highlight from all rows/cells
|
||||
$("#tblMainHoldings").find(".highlighted").removeClass("highlighted", 1200);
|
||||
$("#tblWatchlist").find(".highlighted").removeClass("highlighted", 1200);
|
||||
|
||||
//Tell tablesorter that the table has been updated
|
||||
var resort = true;
|
||||
$("#tblMainHoldings").trigger("update", [resort]);
|
||||
|
||||
//Update the daily holdings value table in the DB
|
||||
if (totalValueGBP != lastTotalHoldingsValue) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
contentType: "application/json",
|
||||
url: "SharePrices.aspx/SetTotalHoldingsValue",
|
||||
dataType: "json",
|
||||
data: JSON.stringify({ TotalValue: totalValueGBP }),
|
||||
success: function (response) {
|
||||
previousDay = response.PreviousDay;
|
||||
previousClose = response.PreviousClose;
|
||||
previousWeek = response.PreviousWeekDate;
|
||||
previousWeekClose = response.PreviousWeekClose;
|
||||
|
||||
//Update the total holdings span in the site banner
|
||||
let formattedAmount = formatAmount(totalValueGBP, "GBP", 0);
|
||||
let holdingsHTML = '<span style="font-size: small;">Total holdings: </span>' + '<span style="font-size: large">' + formattedAmount + '</span> ';
|
||||
holdingsHTML += '<span style="font-size: small;"><span class="' + (previousClose < totalValueGBP ? 'profit">+' : 'loss">') + formatAmount(totalValueGBP - previousClose, "GBP", 0) + '</span> (d)';
|
||||
holdingsHTML += ' / <span class="' + (previousWeekClose < totalValueGBP ? 'profit">+' : 'loss">') + formatAmount(totalValueGBP - previousWeekClose, "GBP", 0) + '</span> (w)</span>';
|
||||
$("#spnTotalHoldings").html(holdingsHTML);
|
||||
//Set the page title
|
||||
document.title = formattedAmount + ' - ' + initialPageTitle;
|
||||
|
||||
lastTotalHoldingsValue = totalValueGBP;
|
||||
},
|
||||
failure: function (response) { console.error("SetTotalHoldingsValue error: " + JSON.stringify(response)); }
|
||||
});
|
||||
}
|
||||
|
||||
//Create / update the currency allocation pie chart
|
||||
function labelFormatter(label, series) {
|
||||
return "<div class='pieLabel'>" + label + "<br/>" + Math.round(series.percent) + "%<br>" + formatAmount(series.data[0][1], 'GBP', 0) + "</div>";
|
||||
}
|
||||
let ops = {
|
||||
series: {
|
||||
downsample: { threshold: 0 },
|
||||
pie: { show: true, radius: 1, label: { show: true, radius: 0.7, formatter: labelFormatter } }
|
||||
},
|
||||
legend: { show: false },
|
||||
grid: { hoverable: true },
|
||||
tooltip: {
|
||||
show: true,
|
||||
content: "%p.0%, %s, n=%n", // show percentages, rounding to 2 decimal places
|
||||
shifts: { x: 20, y: 0 },
|
||||
defaultTheme: false
|
||||
}
|
||||
};
|
||||
let cd = holdingCurrencies.getChartData();
|
||||
if (cd.length) {
|
||||
$.plot('#divHoldingCurrenciesChart', cd, ops);
|
||||
}
|
||||
|
||||
cd = holdingCurrenciesCurrentValue.getChartData();
|
||||
if (cd.length) {
|
||||
$.plot('#divHoldingCurrenciesChartCurrentValue', cd, ops);
|
||||
}
|
||||
|
||||
function labelFormatter2(label, series) {
|
||||
return "<div class='pieLabel'>" + label + "<br/>" + formatAmount(series.data[0][1], 'GBP', 0) + "</div>";
|
||||
}
|
||||
ops.series.pie.label.formatter = labelFormatter2;
|
||||
cd = bookCostPerAccount.getChartData();
|
||||
if (cd.length) {
|
||||
$.plot('#divAccountBookCost', cd, ops);
|
||||
}
|
||||
|
||||
cd = currentValuePerAccount.getChartData();
|
||||
if (cd.length) {
|
||||
$.plot('#divAccountValue', cd, ops);
|
||||
}
|
||||
}
|
||||
function createAccountsTables() {
|
||||
function getAccounts() {
|
||||
let accounts = [];
|
||||
|
@ -6,55 +6,6 @@
|
||||
oneWeek: 7 * 24 * 60 * 60 * 1000,
|
||||
oneMonth: 31 * 24 * 60 * 60 * 1000 };
|
||||
|
||||
export function formatAmount(amount, currency, decimals) {
|
||||
if (amount) {
|
||||
let isNegative = (amount < 0);
|
||||
let s = Math.abs(amount).toFixed(decimals);
|
||||
if (Math.abs(amount) < 0.01) {
|
||||
s = Math.abs(amount).toPrecision(2);
|
||||
}
|
||||
let pos = s.indexOf('.');
|
||||
let wholeNumber = (pos >= 0) ? s.substring(0, pos) : s;
|
||||
let decimal = (pos >= 0) ? s.substring(pos) : '';
|
||||
s = '';
|
||||
while (wholeNumber.length > 0) {
|
||||
//if (s.length > 0 && wholeNumber!='-') s = ',' + s; //Don't add a comma if the only thing left to add to the string is the minus sign
|
||||
if (s.length > 0) s = ',' + s;
|
||||
if (wholeNumber.length >= 3) {
|
||||
s = wholeNumber.substring(wholeNumber.length - 3) + s;
|
||||
wholeNumber = wholeNumber.substring(0, wholeNumber.length - 3);
|
||||
} else {
|
||||
s = wholeNumber + s;
|
||||
wholeNumber = '';
|
||||
}
|
||||
}
|
||||
s = s + decimal;
|
||||
let sign = isNegative ? '-' : '';
|
||||
switch (currency) {
|
||||
case 'GBP':
|
||||
s = sign + '£' + s;
|
||||
break;
|
||||
case 'GBp':
|
||||
s = sign + s + 'p';
|
||||
break;
|
||||
case 'USD':
|
||||
s = sign + 'U$' + s;
|
||||
break;
|
||||
case 'AUD':
|
||||
s = sign + 'A$' + s;
|
||||
break;
|
||||
case 'EUR':
|
||||
s = sign + '€' + s;
|
||||
break;
|
||||
default:
|
||||
s = sign + currency + s;
|
||||
}
|
||||
return s;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
export function getProfitOrLoss(BasePrice, CurrentPrice, ShowPercent) {
|
||||
//let priceMovement = (CurrentPrice - BasePrice).toFixed(2);
|
||||
let priceMovement = (CurrentPrice - BasePrice).autoDP();
|
||||
|
@ -1,591 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
import { formatAmount } from "./SharePrices_Common.js";
|
||||
|
||||
export function createHoldingsTable(Instruments, vars) {
|
||||
function getHoldings() {
|
||||
function aggregateHoldings(i) {
|
||||
let aggUnits = 0;
|
||||
let aggBookCost = 0;
|
||||
let aggBookCostGBP = 0;
|
||||
let minPurchaseData = new Date().getTime();
|
||||
let maxPurchaseDate = 0;
|
||||
let marketIsOpen = Instruments.MarketIsOpen(i);
|
||||
let exchangeRate = Instruments.GetExchangeRate(i.Currency, 'GBP');
|
||||
let accountNames = [];
|
||||
let aggSingleDayGainGBP = 0;
|
||||
|
||||
for (let hn = 0; hn < i.Holdings.length; hn++) {
|
||||
let h = i.Holdings[hn];
|
||||
|
||||
if (h.SoldDate == null && h.NoUnits != 0) {
|
||||
aggUnits += h.NoUnits;
|
||||
aggBookCost += h.NoUnits * h.PurchasePricePerUnit;
|
||||
//aggBookCostGBP += h.NoUnits * h.PurchasePricePerUnit / exchangeRate;
|
||||
aggBookCostGBP += h.BookCostGBP;
|
||||
//bookCostPerAccount.addCategoryAmount(h.AccountName, (h.NoUnits * h.PurchasePricePerUnit) / exchangeRate);
|
||||
bookCostPerAccount.addCategoryAmount(h.AccountName, h.BookCostGBP);
|
||||
|
||||
if (h.PurchaseDate < minPurchaseData) { minPurchaseData = h.PurchaseDate; }
|
||||
if (h.PurchaseDate > maxPurchaseDate) { maxPurchaseDate = h.PurchaseDate; }
|
||||
if (!accountNames.includes(h.AccountName)) { accountNames.push(h.AccountName); }
|
||||
|
||||
if (i.CurrentPrice && exchangeRate) {
|
||||
aggSingleDayGainGBP += ((i.CurrentPrice - (h.PurchaseDate > i.SingleDayStartDate ? h.PurchasePricePerUnit : i.SingleDayPreviousClose)) * h.NoUnits) / exchangeRate;
|
||||
currentValuePerAccount.addCategoryAmount(h.AccountName, (h.NoUnits * i.CurrentPrice) / exchangeRate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let aggPurchasePricePerUnit = aggBookCost / aggUnits;
|
||||
let aggCurrentValue = 0,
|
||||
aggCurrentValueGBP = 0,
|
||||
aggGainGBP = 0,
|
||||
aggSingleDayGainPercent = 0,
|
||||
aggSingleDayProfitOrLoss = 0;
|
||||
if (i.CurrentPrice) {
|
||||
aggCurrentValue = aggUnits * (i.Currency == 'GBp' ? i.CurrentPrice / 100 : i.CurrentPrice);
|
||||
if (exchangeRate) {
|
||||
aggCurrentValueGBP = (aggUnits * i.CurrentPrice) / exchangeRate;
|
||||
//aggGainGBP = ((i.CurrentPrice - aggPurchasePricePerUnit) * aggUnits) / exchangeRate;
|
||||
aggGainGBP = aggCurrentValueGBP - aggBookCostGBP;
|
||||
aggSingleDayGainPercent = ((i.CurrentPrice / i.SingleDayPreviousClose) - 1) * 100;
|
||||
aggSingleDayProfitOrLoss = aggSingleDayGainGBP < 0 ? 'loss' : 'profit';
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
displayOrder: i.DisplayOrder,
|
||||
holdingNumber: 0,
|
||||
instrumentType: i.InstrumentType,
|
||||
instrumentName: i.InstrumentName,
|
||||
displayName: i.DisplayName,
|
||||
symbol: i.Symbol,
|
||||
currency: i.Currency,
|
||||
accountName: accountNames.length > 1 ? 'Multiple (' + accountNames.length + ')' : accountNames[0],
|
||||
accountNames: accountNames.length > 1 ? accountNames.join(', ') : '',
|
||||
purchaseDateMin: minPurchaseData,
|
||||
purchaseDateMax: maxPurchaseDate,
|
||||
noUnits: aggUnits,
|
||||
purchasePricePerUnit: aggPurchasePricePerUnit,
|
||||
bookCost: aggBookCost,
|
||||
bookCostGBP: aggBookCostGBP,
|
||||
currentValue: aggCurrentValue,
|
||||
gain: (i.Currency == 'GBp' ? (i.CurrentPrice - aggPurchasePricePerUnit) / 100 : (i.CurrentPrice - aggPurchasePricePerUnit)) * aggUnits,
|
||||
//gainPercent: (i.CurrentPrice - aggPurchasePricePerUnit) / aggPurchasePricePerUnit * 100,
|
||||
gainPercent: aggGainGBP / aggBookCostGBP * 100,
|
||||
currentValueGBP: aggCurrentValueGBP,
|
||||
gainGBP: aggGainGBP,
|
||||
singleDayGainGBP: aggSingleDayGainGBP,
|
||||
singleDayGainPercent: aggSingleDayGainPercent,
|
||||
singleDayProfitOrLoss: aggSingleDayProfitOrLoss,
|
||||
currentPrice: i.CurrentPrice,
|
||||
currentExchangeRate: exchangeRate,
|
||||
marketIsOpen: marketIsOpen
|
||||
};
|
||||
}
|
||||
|
||||
//Create a structure to summarise total holdings in each currency, to be used in the pie chart
|
||||
let holdingCurrencies = {
|
||||
data: [],
|
||||
indexOfHoldingCurrency: function (symbol) { return this.data.map(function (item) { return item.symbol; }).indexOf(symbol); },
|
||||
getHoldingCurrency: function (symbol) { return this.data[this.indexOfHoldingCurrency(symbol)]; },
|
||||
addHoldingCurrency: function (symbol, amount) {
|
||||
let c = this.getHoldingCurrency(symbol);
|
||||
if (c) {
|
||||
c.total += amount;
|
||||
} else {
|
||||
this.data.push({ symbol: symbol, total: amount });
|
||||
}
|
||||
},
|
||||
getChartData: function () {
|
||||
let d = []
|
||||
for (let i = 0; i < this.data.length; i++) {
|
||||
//d.push({ label: this.data[i].symbol, data: this.data[i].total / Instruments.GetExchangeRate(this.data[i].symbol, 'GBP') });
|
||||
d.push({ label: this.data[i].symbol, data: this.data[i].total });
|
||||
}
|
||||
return d;
|
||||
}
|
||||
};
|
||||
let holdingCurrenciesCurrentValue = {
|
||||
data: [],
|
||||
indexOfHoldingCurrency: function (symbol) { return this.data.map(function (item) { return item.symbol; }).indexOf(symbol); },
|
||||
getHoldingCurrency: function (symbol) { return this.data[this.indexOfHoldingCurrency(symbol)]; },
|
||||
addHoldingCurrency: function (symbol, amount) {
|
||||
let c = this.getHoldingCurrency(symbol);
|
||||
if (c) {
|
||||
c.total += amount;
|
||||
} else {
|
||||
this.data.push({ symbol: symbol, total: amount });
|
||||
}
|
||||
},
|
||||
getChartData: function () {
|
||||
let d = []
|
||||
for (let i = 0; i < this.data.length; i++) {
|
||||
d.push({ label: this.data[i].symbol, data: this.data[i].total / Instruments.GetExchangeRate(this.data[i].symbol, 'GBP') });
|
||||
}
|
||||
return d;
|
||||
}
|
||||
};
|
||||
let bookCostPerAccount = createCategoryAggregator();
|
||||
let currentValuePerAccount = createCategoryAggregator();
|
||||
let totalHoldingsValueGBP = 0;
|
||||
let totalBookCostGBP = 0;
|
||||
let totalValueGBP = 0;
|
||||
let totalGainGBP = 0;
|
||||
let todaysGainGBP = 0;
|
||||
let maxCurrentValueGBP = 0;
|
||||
let groupHoldings = $('#groupBySymbol').prop('checked');
|
||||
let r = [];
|
||||
let w = []; //Watchlist (instruments with no current holdings)
|
||||
|
||||
for (let n = 0; n < Instruments.Data.length; n++) {
|
||||
let i = Instruments.Data[n];
|
||||
//if (i.InstrumentType == 'EQUITY') {
|
||||
if (i.InstrumentType == 'EQUITY' || i.InstrumentType == 'ETF' || i.InstrumentType == 'CRYPTOCURRENCY' || i.InstrumentType == 'CURRENCY') {
|
||||
//if (i.CurrentPrice) {
|
||||
if (groupHoldings) {
|
||||
let agg = aggregateHoldings(i);
|
||||
if (agg.noUnits > 0) {
|
||||
r.push(agg);
|
||||
|
||||
//holdingCurrencies.addHoldingCurrency(i.Currency, agg.noUnits * agg.purchasePricePerUnit);
|
||||
//holdingCurrencies.addHoldingCurrency(i.Currency.toUpperCase(), agg.noUnits * agg.purchasePricePerUnit / (i.Currency == "GBp" ? 100 : 1));
|
||||
holdingCurrencies.addHoldingCurrency(i.Currency.toUpperCase(), agg.bookCostGBP);
|
||||
if (i.CurrentPrice) {
|
||||
if (agg.currentValueGBP > maxCurrentValueGBP) { maxCurrentValueGBP = agg.currentValueGBP };
|
||||
//holdingCurrenciesCurrentValue.addHoldingCurrency(i.Currency, agg.noUnits * i.CurrentPrice);
|
||||
holdingCurrenciesCurrentValue.addHoldingCurrency(i.Currency.toUpperCase(), agg.noUnits * i.CurrentPrice / (i.Currency == "GBp" ? 100 : 1));
|
||||
if (agg.currentExchangeRate) {
|
||||
totalBookCostGBP += agg.bookCostGBP;
|
||||
totalValueGBP += agg.currentValueGBP;
|
||||
totalGainGBP += agg.gainGBP;
|
||||
todaysGainGBP += (agg.marketIsOpen == 0 ? 0 : agg.singleDayGainGBP);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//No current holdings for this instrument - add it to the watchlist
|
||||
if (i.WatchlistID) {
|
||||
w.push(i);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let marketIsOpen = Instruments.MarketIsOpen(i);
|
||||
let exchangeRate = Instruments.GetExchangeRate(i.Currency, 'GBP');
|
||||
|
||||
let noCurrentHoldings = 0;
|
||||
for (let hn = 0; hn < i.Holdings.length; hn++) {
|
||||
let h = i.Holdings[hn];
|
||||
if (h.SoldDate == null) {
|
||||
noCurrentHoldings++;
|
||||
let holdingBookCost = h.NoUnits * h.PurchasePricePerUnit;
|
||||
|
||||
let currentHoldingValue = h.NoUnits * (i.Currency == 'GBp' ? i.CurrentPrice / 100 : i.CurrentPrice);
|
||||
|
||||
let holdingCurrentValueGBP = 0,
|
||||
holdingGainGBP = 0,
|
||||
singleDayGainGBP = 0,
|
||||
singleDayGainPercent = 0,
|
||||
singleDayProfitOrLoss = 0;
|
||||
if (i.CurrentPrice) {
|
||||
let currentHoldingValue = h.NoUnits * (i.Currency == 'GBp' ? i.CurrentPrice / 100 : i.CurrentPrice);
|
||||
|
||||
if (exchangeRate) {
|
||||
let holdingCurrentValueGBP = (h.NoUnits * i.CurrentPrice) / exchangeRate;
|
||||
//let holdingGainGBP = ((i.CurrentPrice - h.PurchasePricePerUnit) * h.NoUnits) / exchangeRate;
|
||||
let holdingGainGBP = holdingCurrentValueGBP - h.BookCostGBP;
|
||||
|
||||
let singleDayGainGBP = ((i.CurrentPrice - (h.PurchaseDate > i.SingleDayStartDate ? h.PurchasePricePerUnit : i.SingleDayPreviousClose)) * h.NoUnits) / exchangeRate;
|
||||
let singleDayGainPercent = ((i.CurrentPrice / i.SingleDayPreviousClose) - 1) * 100;
|
||||
let singleDayProfitOrLoss = singleDayGainGBP < 0 ? 'loss' : 'profit';
|
||||
|
||||
if (holdingCurrentValueGBP > maxCurrentValueGBP) { maxCurrentValueGBP = holdingCurrentValueGBP };
|
||||
}
|
||||
}
|
||||
|
||||
r.push({
|
||||
displayOrder: i.DisplayOrder,
|
||||
holdingNumber: hn,
|
||||
instrumentType: i.InstrumentType,
|
||||
instrumentName: i.InstrumentName,
|
||||
displayName: i.DisplayName,
|
||||
symbol: i.Symbol,
|
||||
currency: i.Currency,
|
||||
accountName: h.AccountName,
|
||||
purchaseDateMin: h.PurchaseDate,
|
||||
purchaseDateMax: h.PurchaseDate,
|
||||
noUnits: h.NoUnits,
|
||||
purchasePricePerUnit: h.PurchasePricePerUnit,
|
||||
bookCost: holdingBookCost,
|
||||
currentValue: currentHoldingValue,
|
||||
gain: (i.Currency == 'GBp' ? (i.CurrentPrice - h.PurchasePricePerUnit) / 100 : (i.CurrentPrice - h.PurchasePricePerUnit)) * h.NoUnits,
|
||||
//gainPercent: (i.CurrentPrice - h.PurchasePricePerUnit) / h.PurchasePricePerUnit * 100,
|
||||
gainPercent: holdingGainGBP / h.BookCostGBP * 100,
|
||||
currentValueGBP: holdingCurrentValueGBP,
|
||||
gainGBP: holdingGainGBP,
|
||||
singleDayGainGBP: singleDayGainGBP,
|
||||
singleDayGainPercent: singleDayGainPercent,
|
||||
singleDayProfitOrLoss: singleDayProfitOrLoss,
|
||||
currentPrice: i.CurrentPrice,
|
||||
currentExchangeRate: exchangeRate,
|
||||
marketIsOpen: marketIsOpen
|
||||
});
|
||||
|
||||
holdingCurrencies.addHoldingCurrency(i.Currency, h.NoUnits * h.PurchasePricePerUnit);
|
||||
if (i.CurrentPrice) {
|
||||
holdingCurrenciesCurrentValue.addHoldingCurrency(i.Currency, h.NoUnits * i.CurrentPrice);
|
||||
if (exchangeRate) {
|
||||
currentValuePerAccount.addCategoryAmount(h.AccountName, holdingCurrentValueGBP);
|
||||
bookCostPerAccount.addCategoryAmount(h.AccountName, holdingBookCost / exchangeRate);
|
||||
|
||||
//totalBookCostGBP += holdingBookCost / exchangeRate;
|
||||
totalBookCostGBP += h.BookCostGBP;
|
||||
totalValueGBP += holdingCurrentValueGBP;
|
||||
totalGainGBP += holdingGainGBP;
|
||||
todaysGainGBP += (marketIsOpen == 0 ? 0 : singleDayGainGBP);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (noCurrentHoldings == 0) {
|
||||
//No current holdings for this instrument - add it to the watchlist
|
||||
if (i.WatchlistID) {
|
||||
w.push(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//} //if (i.CurrentPrice)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
totalBookCostGBP: totalBookCostGBP,
|
||||
totalValueGBP: totalValueGBP,
|
||||
totalGainGBP: totalGainGBP,
|
||||
todaysGainGBP: todaysGainGBP,
|
||||
maxCurrentValueGBP: maxCurrentValueGBP,
|
||||
holdings: r,
|
||||
watchlist: w,
|
||||
holdingCurrencies: holdingCurrencies,
|
||||
holdingCurrenciesCurrentValue: holdingCurrenciesCurrentValue,
|
||||
bookCostPerAccount: bookCostPerAccount,
|
||||
currentValuePerAccount: currentValuePerAccount
|
||||
};
|
||||
}
|
||||
function createTable() {
|
||||
let t = $("#tblMainHoldings");
|
||||
if (t.length) {
|
||||
return;
|
||||
} else {
|
||||
let tbl = '<table id="tblMainHoldings" class="mainHoldings"><thead>' +
|
||||
'<tr><th>Type</th>' +
|
||||
'<th>Name</th>' +
|
||||
'<th>Symbol</th>' +
|
||||
'<th>Ccy</th>' +
|
||||
'<th>Account</th>' +
|
||||
'<th>Buy Date</th>' +
|
||||
'<th>No Units</th>' +
|
||||
'<th>Buy<br>Price</th>' +
|
||||
'<th>Current<br>Price</th>' +
|
||||
'<th>Book<br>Cost</th>' +
|
||||
'<th>Current<br>Value</th>' +
|
||||
'<th>Gain</th>' +
|
||||
'<th>Gain £</th>' +
|
||||
'<th>Gain %</th>' +
|
||||
'<th>Current<br>Value £</th>' +
|
||||
'<th>Allocation</th>' +
|
||||
'<th>Today's<br>Gain £</th>' +
|
||||
'<th>Today's<br>Gain %</th>' +
|
||||
'</tr></thead><tbody id="tbMainHoldings"></tbody><tfoot id="tfMainHoldings"><tr><td colspan="18"> </td></tr></tfoot></table>';
|
||||
|
||||
//$('#holdingsDiv').html(tbl);
|
||||
$('#holdingsTableDiv').html(tbl);
|
||||
|
||||
$("#tblMainHoldings").tablesorter({ ignoreCase: false });
|
||||
$("#tblMainHoldings").trigger("sorton", Cookies.get('sortHoldings'));
|
||||
$('#tblMainHoldings').on('sortEnd', function (event) {
|
||||
// Prints the current sort order to the console
|
||||
if (event.target.config.sortList.length > 3) {
|
||||
console.info({ "Setting sortHoldings Cookie (array too long?)": event.target.config.sortList });
|
||||
}
|
||||
Cookies.set('sortHoldings', event.target.config.sortList, { 'sameSite': 'strict' });
|
||||
});
|
||||
}
|
||||
}
|
||||
function createWatchlistTable() {
|
||||
let t = $("#tblWatchlist");
|
||||
if (t.length) {
|
||||
return;
|
||||
} else {
|
||||
let tbl = '<table id="tblWatchlist" class="watchlist"><thead>' +
|
||||
'<tr><th>Type</th>' +
|
||||
'<th>Name</th>' +
|
||||
'<th>Symbol</th>' +
|
||||
'<th>Ccy</th>' +
|
||||
'<th>Current Price</th>' +
|
||||
'<th>Today's Gain</th>' +
|
||||
'<th>Today's Gain %</th>' +
|
||||
'<th>Since Last Sold<th>' +
|
||||
'</tr></thead><tbody id="tbWatchlist"></tbody></table>';
|
||||
|
||||
$('#watchlistTableDiv').html(tbl);
|
||||
|
||||
$("#tblWatchlist").tablesorter({ ignoreCase: false });
|
||||
$("#tblWatchlist").trigger("sorton", Cookies.get('sortWatchlist'));
|
||||
$('#tblWatchlist').on('sortEnd', function (event) {
|
||||
// Prints the current sort order to the console
|
||||
Cookies.set('sorWatchlist', event.target.config.sortList, { 'sameSite': 'strict' });
|
||||
});
|
||||
}
|
||||
}
|
||||
function addTableRow(rowID, content, tbodyID, highlightUpdates) {
|
||||
let r = $("#" + rowID);
|
||||
let newText = $(content).text(); //$(content).text();
|
||||
if (r.length) {
|
||||
let currentContent = r.text();
|
||||
if (currentContent != newText) { //Only update row if the content has changed
|
||||
$("#" + rowID).replaceWith(content);
|
||||
if (highlightUpdates) {
|
||||
$("#" + rowID).addClass("highlighted");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$("#" + tbodyID).append(content); //.addClass("highlighted");
|
||||
//$("#" + rowID).addClass("highlighted");
|
||||
}
|
||||
}
|
||||
function getPercentCell(backgroundColor, cellWidth, valuePercent, label) {
|
||||
let labelText = label ? label : (valuePercent.toFixed(1) + '%');
|
||||
let barWidth = ((valuePercent / 100) * cellWidth);
|
||||
return '<td>' +
|
||||
'<div class="pcBackground" style="background-color: ' + backgroundColor + '; width:' + barWidth.toFixed(0) + 'px;"> </div>' +
|
||||
'<div class="pcLabel" style="width:' + cellWidth + 'px">' + labelText + '</div>' +
|
||||
'</td>';
|
||||
}
|
||||
function profitOrLoss(value) {
|
||||
return value >= 0 ? "profit" : "loss";
|
||||
}
|
||||
|
||||
let t = getHoldings();
|
||||
let totalBookCostGBP = t.totalBookCostGBP;
|
||||
let totalValueGBP = t.totalValueGBP;
|
||||
let totalGainGBP = t.totalGainGBP;
|
||||
let todaysGainGBP = t.todaysGainGBP;
|
||||
let maxCurrentValueGBP = t.maxCurrentValueGBP;
|
||||
let holdings = t.holdings;
|
||||
let watchlist = t.watchlist;
|
||||
let holdingCurrencies = t.holdingCurrencies;
|
||||
let holdingCurrenciesCurrentValue = t.holdingCurrenciesCurrentValue;
|
||||
let bookCostPerAccount = t.bookCostPerAccount;
|
||||
let currentValuePerAccount = t.currentValuePerAccount;
|
||||
|
||||
let maxAllocationPercent = (maxCurrentValueGBP / totalValueGBP) * 100;
|
||||
let allocationScaleFactor = 100 / maxAllocationPercent;
|
||||
|
||||
let altRow = 1;
|
||||
|
||||
createTable();
|
||||
for (let n = 0; n < holdings.length; n++) {
|
||||
try {
|
||||
altRow = !altRow;
|
||||
|
||||
let h = holdings[n];
|
||||
let type = h.instrumentType == 'CURRENCY' ? '$' : h.instrumentType.substring(0, 1);
|
||||
//let profitOrLoss = h.currentPrice < h.purchasePricePerUnit ? 'loss' : 'profit'; //let profitOrLoss = i.CurrentPrice < h.PurchasePricePerUnit ? 'loss' : 'profit';
|
||||
let openOrClosed = h.marketIsOpen == 1 ? 'open' : 'closed';
|
||||
let allocationPercent = h.currentValueGBP / totalValueGBP * 100;
|
||||
let scaledAllocation = allocationScaleFactor * allocationPercent;
|
||||
|
||||
let rowID = "holdingRow_" + h.displayOrder + "_" + h.holdingNumber;
|
||||
let row = '<tr' + (altRow ? ' class="altShareRow"' : '') + ' id="' + rowID + '">' +
|
||||
'<td class="' + openOrClosed + '">' + type + '</td>' +
|
||||
'<td class="' + openOrClosed + '">' + (h.displayName != '' ? h.displayName : h.instrumentName) + '</td>' +
|
||||
'<td><a target="_blank" href="https://uk.finance.yahoo.com/quote/' + h.symbol + '">' + h.symbol + '</a>' + '</td>' +
|
||||
'<td class="' + openOrClosed + '">' + h.currency + '</td>' +
|
||||
'<td class="' + openOrClosed + '" title="' + (h.accountNames ? h.accountNames : '') + '">' + h.accountName + '</td>' +
|
||||
'<td class="' + openOrClosed + '"' + (h.purchaseDateMin == h.purchaseDateMax ? ('>' + new Date(h.purchaseDateMin).yyyymmdd()) : (' title="' + new Date(h.purchaseDateMax).yyyymmdd() + ' - ' + new Date(h.purchaseDateMin).yyyymmdd() + '">' + new Date(h.purchaseDateMin).yyyymmdd()) + '+') + '</td>' +
|
||||
//'<td class="num ' + openOrClosed + '">' + formatAmount(h.noUnits, '', 0) + '</td>' +
|
||||
'<td class="num ' + openOrClosed + '">' + h.noUnits.autoScale() + '</td>' +
|
||||
'<td class="num ' + openOrClosed + '">' + formatAmount(h.purchasePricePerUnit, h.currency, 2) + '</td>' + //Buy Price
|
||||
'<td class="num ' + openOrClosed + '">' + (h.currentPrice ? formatAmount(h.currentPrice, h.currency, 2) : '') + '</td>' + //Current Price
|
||||
'<td class="num ' + openOrClosed + '">' + formatAmount(h.currency == 'GBp' ? h.bookCost / 100 : h.bookCost, h.currency == 'GBp' ? 'GBP' : h.currency, 0) + '</td>' + //Book Cost
|
||||
'<td class="num ' + openOrClosed + '">' + (h.currentExchangeRate ? formatAmount(h.currentValue, h.currency == 'GBp' ? 'GBP' : h.currency, 0) : '') + '</td>' + //Current Value
|
||||
'<td class="num ' + profitOrLoss(h.currentPrice - h.purchasePricePerUnit) + '">' + (h.currentPrice ? formatAmount(h.gain, h.currency == 'GBp' ? 'GBP' : h.currency, 0) : '') + '</td>' + //Gain
|
||||
'<td class="num ' + profitOrLoss(h.gainGBP) + '">' + (h.currentExchangeRate ? formatAmount(h.gainGBP, 'GBP', 0) : '') + '</td>' + //Gain £
|
||||
'<td class="num ' + profitOrLoss(h.gainGBP) + '">' + (h.currentExchangeRate ? h.gainPercent.toFixed(1) + '%' : '') + '</td>' + //Gain %
|
||||
'<td class="num ' + openOrClosed + '">' + (h.currentExchangeRate ? formatAmount(h.currentValueGBP, 'GBP', 0) : '') + '</td>' + //Current Value £
|
||||
(h.currentExchangeRate ? getPercentCell('#3f2d95', 70, scaledAllocation, allocationPercent.toFixed(1) + '%') : '') + //Allocation
|
||||
(h.currentExchangeRate ? (h.marketIsOpen == 0 ? '<td/>' : '<td class="num ' + h.singleDayProfitOrLoss + '">' + formatAmount(h.singleDayGainGBP, 'GBP', 0) + '</td>') : '') + //Today's Gain £
|
||||
(h.singleDayGainPercent ? (h.marketIsOpen == 0 ? '<td/>' : '<td class="num ' + h.singleDayProfitOrLoss + '">' + h.singleDayGainPercent.toFixed(1) + '%</td>') : '<td/>') + //Today's Gain %
|
||||
'</tr>';
|
||||
addTableRow(rowID, row, "tbMainHoldings", true);
|
||||
}
|
||||
catch (err) {
|
||||
logError("Error adding " + holdings[n].symbol + " to main holdings table: " + err.message);
|
||||
}
|
||||
}
|
||||
|
||||
//Add the totals line at the bottom of the table
|
||||
//let profitOrLoss = totalValueGBP < totalBookCostGBP ? 'loss' : 'profit';
|
||||
let todaysProfitOrLoss = todaysGainGBP < 0 ? 'loss' : 'profit';
|
||||
let lSF = '';
|
||||
if (vars.fetchTiming.lastSuccessfulFetch) {
|
||||
let lastFetchAge = ((new Date()) - vars.fetchTiming.lastSuccessfulFetch);
|
||||
let staleFetch = ((new Date()) - vars.fetchTiming.lastSuccessfulFetch) > (vars.fetchTiming.fetchIntervalSingleDay * 1.5);
|
||||
lSF = (vars.fetchTiming.lastSuccessfulFetch ? ' - Last Successful Fetch: ' + (staleFetch ? '<span class="loss">' : '') + vars.fetchTiming.lastSuccessfulFetch.yyyymmddhhmmss() + (staleFetch ? '</span>' : '') : '');
|
||||
}
|
||||
let row = '<tr id="tfHoldingsTotals"><td></td><td id="holdingsLastUpdated" colspan="8">Updated: ' + new Date().yyyymmddhhmmss() + lSF + '</td>' +
|
||||
'<td class="num">' + formatAmount(totalBookCostGBP, 'GBP', 0) + '</td>' + //Book Cost
|
||||
'<td></td><td></td>' +
|
||||
'<td class="num ' + profitOrLoss(totalValueGBP - totalBookCostGBP) + '">' + formatAmount(totalGainGBP, 'GBP', 0) + '</td>' + //Gain GBP
|
||||
'<td class="num ' + profitOrLoss(totalValueGBP - totalBookCostGBP) + '">' + formatAmount((((totalValueGBP / totalBookCostGBP) - 1) * 100), '', 1) + '%</td>' + //Gain %
|
||||
'<td class="num">' + formatAmount(totalValueGBP, 'GBP', 0) + '</td>' + //Current Value
|
||||
'<td> </td>' + //Allocation
|
||||
'<td class="num ' + todaysProfitOrLoss + '">' + formatAmount(todaysGainGBP, 'GBP', 0) + '</td>' + //Today's Gain GBP
|
||||
//'<td class="num ' + todaysProfitOrLoss + '">' + formatAmount(((todaysGainGBP / totalBookCostGBP) * 100), '', 1) + '%</td>' + //Today's Gain %
|
||||
'<td class="num ' + todaysProfitOrLoss + '">' + formatAmount(((todaysGainGBP / (totalValueGBP - todaysGainGBP)) * 100), '', 1) + '%</td>' + //Today's Gain %
|
||||
'</tr>';
|
||||
addTableRow("tfHoldingsTotals", row, "tfMainHoldings", false);
|
||||
|
||||
//Create the watchlist table
|
||||
createWatchlistTable();
|
||||
altRow = 1;
|
||||
for (let n = 0; n < watchlist.length; n++) {
|
||||
altRow = !altRow;
|
||||
let wi = watchlist[n];
|
||||
try {
|
||||
let type = wi.InstrumentType == 'CURRENCY' ? '$' : wi.InstrumentType.substring(0, 1);
|
||||
let openOrClosed = Instruments.MarketIsOpen(wi) == 1 ? 'open' : 'closed';
|
||||
let profitOrLoss = wi.CurrentPrice < wi.SingleDayPreviousClose ? 'loss' : 'profit';
|
||||
let rowID = "watchlistRow_" + wi.DisplayOrder;
|
||||
|
||||
let sinceLastSold = '<td></td>';
|
||||
if (wi.LastSoldPrice) {
|
||||
let delta = (wi.CurrentPrice - wi.LastSoldPrice) / wi.LastSoldPrice * 100;
|
||||
let tooltip = new Date(wi.LastSoldDate).yyyymmdd() + ' @ ' + wi.LastSoldPrice.toFixed(2);
|
||||
sinceLastSold = '<td title="' + tooltip + '" class="num ' + (delta < 0 ? 'loss' : 'profit') + '">' + (delta).toFixed(1) + '%</td>';
|
||||
}
|
||||
|
||||
//Type, Name, Symbol, Ccy, Current Price, Today's Gain, Today's Gain %
|
||||
let row = '<tr' + (altRow ? ' class="altShareRow"' : '') + ' id="' + rowID + '">' +
|
||||
'<td class="' + openOrClosed + '">' + type + '</td>' +
|
||||
'<td class="' + openOrClosed + '">' + wi.InstrumentName + '</td>' +
|
||||
'<td><a target="_blank" href="https://uk.finance.yahoo.com/quote/' + wi.Symbol + '">' + wi.Symbol + '</a>' + '</td>' +
|
||||
'<td class="' + openOrClosed + '">' + wi.Currency + '</td>' +
|
||||
'<td class="num ' + openOrClosed + '">' + (wi.CurrentPrice ? formatAmount(wi.CurrentPrice, wi.Currency, 2) : '') + '</td>' + //Current Price
|
||||
(Instruments.marketIsOpen == 0 ? '<td/>' : '<td class="num ' + profitOrLoss + '">' + (wi.CurrentPrice ? formatAmount(wi.CurrentPrice - wi.SingleDayPreviousClose, wi.Currency, 2) : '') + '</td>') + //Today's Gain
|
||||
(Instruments.marketIsOpen == 0 ? '<td/>' : '<td class="num ' + profitOrLoss + '">' + (wi.CurrentPrice ? ((wi.CurrentPrice - wi.SingleDayPreviousClose) / wi.SingleDayPreviousClose * 100).toFixed(1) : '') + '</td>') + //Today's Gain %
|
||||
//'<td>' + (new Date(wi.LastSoldDate)).yyyymmdd() + '</td>' +
|
||||
sinceLastSold +
|
||||
'</tr>';
|
||||
addTableRow(rowID, row, "tbWatchlist", true);
|
||||
}
|
||||
catch (err) {
|
||||
logError("Error adding " + wi.symbol + " to watchlist table: " + err.message);
|
||||
}
|
||||
}
|
||||
|
||||
//Flash the last updated cell
|
||||
$("#holdingsLastUpdated").addClass("highlighted");
|
||||
//Remove the highlight from all rows/cells
|
||||
$("#tblMainHoldings").find(".highlighted").removeClass("highlighted", 1200);
|
||||
$("#tblWatchlist").find(".highlighted").removeClass("highlighted", 1200);
|
||||
|
||||
//Tell tablesorter that the table has been updated
|
||||
var resort = true;
|
||||
$("#tblMainHoldings").trigger("update", [resort]);
|
||||
|
||||
//Update the daily holdings value table in the DB
|
||||
if (totalValueGBP != vars.lastTotalHoldingsValue) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
contentType: "application/json",
|
||||
url: "SharePrices.aspx/SetTotalHoldingsValue",
|
||||
dataType: "json",
|
||||
data: JSON.stringify({ TotalValue: totalValueGBP }),
|
||||
success: function (response) {
|
||||
vars.previousDay = response.PreviousDay;
|
||||
vars.previousClose = response.PreviousClose;
|
||||
vars.previousWeek = response.PreviousWeekDate;
|
||||
vars.previousWeekClose = response.PreviousWeekClose;
|
||||
|
||||
//Update the total holdings span in the site banner
|
||||
let formattedAmount = formatAmount(totalValueGBP, "GBP", 0);
|
||||
let holdingsHTML = '<span style="font-size: small;">Total holdings: </span>' + '<span style="font-size: large">' + formattedAmount + '</span> ';
|
||||
holdingsHTML += '<span style="font-size: small;"><span class="' + (vars.previousClose < totalValueGBP ? 'profit">+' : 'loss">') + formatAmount(totalValueGBP - vars.previousClose, "GBP", 0) + '</span> (d)';
|
||||
holdingsHTML += ' / <span class="' + (vars.previousWeekClose < totalValueGBP ? 'profit">+' : 'loss">') + formatAmount(totalValueGBP - vars.previousWeekClose, "GBP", 0) + '</span> (w)</span>';
|
||||
$("#spnTotalHoldings").html(holdingsHTML);
|
||||
//Set the page title
|
||||
document.title = formattedAmount + ' - ' + vars.initialPageTitle;
|
||||
|
||||
vars.lastTotalHoldingsValue = totalValueGBP;
|
||||
},
|
||||
failure: function (response) { console.error("SetTotalHoldingsValue error: " + JSON.stringify(response)); }
|
||||
});
|
||||
}
|
||||
|
||||
//Create / update the currency allocation pie chart
|
||||
function labelFormatter(label, series) {
|
||||
return "<div class='pieLabel'>" + label + "<br/>" + Math.round(series.percent) + "%<br>" + formatAmount(series.data[0][1], 'GBP', 0) + "</div>";
|
||||
}
|
||||
let ops = {
|
||||
series: {
|
||||
downsample: { threshold: 0 },
|
||||
pie: { show: true, radius: 1, label: { show: true, radius: 0.7, formatter: labelFormatter } }
|
||||
},
|
||||
legend: { show: false },
|
||||
grid: { hoverable: true },
|
||||
tooltip: {
|
||||
show: true,
|
||||
content: "%p.0%, %s, n=%n", // show percentages, rounding to 2 decimal places
|
||||
shifts: { x: 20, y: 0 },
|
||||
defaultTheme: false
|
||||
}
|
||||
};
|
||||
let cd = holdingCurrencies.getChartData();
|
||||
if (cd.length) {
|
||||
$.plot('#divHoldingCurrenciesChart', cd, ops);
|
||||
}
|
||||
|
||||
cd = holdingCurrenciesCurrentValue.getChartData();
|
||||
if (cd.length) {
|
||||
$.plot('#divHoldingCurrenciesChartCurrentValue', cd, ops);
|
||||
}
|
||||
|
||||
function labelFormatter2(label, series) {
|
||||
return "<div class='pieLabel'>" + label + "<br/>" + formatAmount(series.data[0][1], 'GBP', 0) + "</div>";
|
||||
}
|
||||
ops.series.pie.label.formatter = labelFormatter2;
|
||||
cd = bookCostPerAccount.getChartData();
|
||||
if (cd.length) {
|
||||
$.plot('#divAccountBookCost', cd, ops);
|
||||
}
|
||||
|
||||
cd = currentValuePerAccount.getChartData();
|
||||
if (cd.length) {
|
||||
$.plot('#divAccountValue', cd, ops);
|
||||
}
|
||||
}
|
||||
function createCategoryAggregator() {
|
||||
let aggregator = {
|
||||
data: [],
|
||||
indexOfCategory: function (categoryName) { return this.data.map(function (item) { return item.categoryName; }).indexOf(categoryName); },
|
||||
getCategory: function (categoryName) { return this.data[this.indexOfCategory(categoryName)]; },
|
||||
addCategoryAmount: function (categoryName, amount) {
|
||||
let c = this.getCategory(categoryName);
|
||||
if (c) {
|
||||
c.total += amount;
|
||||
} else {
|
||||
this.data.push({ categoryName: categoryName, total: amount });
|
||||
}
|
||||
},
|
||||
getChartData: function () {
|
||||
let d = []
|
||||
for (let i = 0; i < this.data.length; i++) {
|
||||
d.push({ label: this.data[i].categoryName, data: this.data[i].total });
|
||||
}
|
||||
return d;
|
||||
}
|
||||
};
|
||||
return aggregator;
|
||||
}
|
Loading…
Reference in New Issue
Block a user