Split out Holdings tab code into SharePrices_Holdings.js, added variable fetch timing
This commit is contained in:
parent
882d721899
commit
5f53d2c336
Binary file not shown.
@ -141,6 +141,30 @@
|
||||
<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');
|
||||
@ -161,11 +185,11 @@
|
||||
redrawAllSharesCharts();
|
||||
break;*/
|
||||
case 'displayAsPercent':
|
||||
redrawAllSharesCharts();
|
||||
importedFunctions.redrawAllSharesCharts();
|
||||
break;
|
||||
case 'excludeNoHoldings':
|
||||
createSharesTable();
|
||||
redrawAllSharesCharts();
|
||||
importedFunctions.createSharesTable();
|
||||
importedFunctions.redrawAllSharesCharts();
|
||||
break;
|
||||
/*case 'showPreviousHoldings':
|
||||
redrawAllSharesCharts();
|
||||
@ -182,12 +206,24 @@
|
||||
}
|
||||
</script>
|
||||
<script type="module">
|
||||
import { getInstruments, refreshHistoryChart, showModalDialog_AddInstrument, showModalDialog_AddHolding, showModalDialog_FullScreenChart, showModalDialog_SoldHolding } from "./scripts/SharePrices.js";
|
||||
import {
|
||||
createSharesTable,
|
||||
getInstruments,
|
||||
redrawAllSharesCharts,
|
||||
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,6 +20,8 @@
|
||||
<IISExpressAnonymousAuthentication />
|
||||
<IISExpressWindowsAuthentication />
|
||||
<IISExpressUseClassicPipelineMode />
|
||||
<Use64BitIISExpress />
|
||||
<UseGlobalApplicationHostFile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
@ -259,6 +261,7 @@
|
||||
<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" />
|
||||
|
Binary file not shown.
@ -1,21 +1,35 @@
|
||||
// @ts-check
|
||||
|
||||
import {generateAxisBreaks, getProfitOrLoss, timespans} from "./SharePrices_Common.js";
|
||||
import {formatAmount, 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";
|
||||
|
||||
createFullScreenComparisonChart_Flot
|
||||
} from "./SharePrices_Charts.js";
|
||||
import {createHoldingsTable} from "./SharePrices_Holdings.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 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 lastSuccessfulFetch;
|
||||
var currencyFormatter = new Intl.NumberFormat('en-GB', { style: 'currency', currency: 'GBP' });
|
||||
var instrumentSearchResults = [];
|
||||
var tablesUpdateTimings = { lastUpdate: 0, timeBetweenUpdates: 10000, timer: 0, updateNeeded: true };
|
||||
@ -90,13 +104,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() - fetchIntervalDaily + (4 * timespans.oneMinute) };
|
||||
if (!this.Data[0].lastFetchedDaily) { this.Data[0].lastFetchedDaily = new Date().getTime() - vars.fetchTiming.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() - fetchIntervalDaily + (4 * timespans.oneMinute) };
|
||||
if (!this.Data[i].lastFetchedDaily) { this.Data[i].lastFetchedDaily = new Date().getTime() - vars.fetchTiming.fetchIntervalDaily + (4 * timespans.oneMinute) };
|
||||
if (lastFetchedDate > this.Data[i].lastFetchedDaily) {
|
||||
lastFetchedDate = this.Data[i].lastFetchedDaily;
|
||||
lastFetchedIndex = i;
|
||||
@ -108,13 +122,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() - fetchIntervalIntraday + (2 * timespans.oneMinute) };
|
||||
if (!this.Data[0].lastFetchedIntraday) { this.Data[0].lastFetchedIntraday = new Date().getTime() - vars.fetchTiming.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() - fetchIntervalIntraday + (2 * timespans.oneMinute) };
|
||||
if (!this.Data[i].lastFetchedIntraday) { this.Data[i].lastFetchedIntraday = new Date().getTime() - vars.fetchTiming.fetchIntervalIntraday + (2 * timespans.oneMinute) };
|
||||
if (lastFetchedDate > this.Data[i].lastFetchedIntraday) {
|
||||
lastFetchedDate = this.Data[i].lastFetchedIntraday;
|
||||
lastFetchedIndex = i;
|
||||
@ -222,7 +236,6 @@ var Currencies = {
|
||||
}
|
||||
}
|
||||
}
|
||||
var previousDay, previousClose, previousWeek, previousWeekClose;
|
||||
|
||||
Date.prototype.yyyymmddhhmmss = function () {
|
||||
var yyyy = this.getFullYear().toString();
|
||||
@ -285,78 +298,6 @@ 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();
|
||||
@ -364,35 +305,12 @@ function formatAmount(amount, currency, decimals) {
|
||||
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()');
|
||||
initialPageTitle = document.title;
|
||||
if (fetchTimer != 0) { clearTimeout(fetchTimer) };
|
||||
vars.initialPageTitle = document.title;
|
||||
if (vars.fetchTiming.fetchTimer != 0) { clearTimeout(vars.fetchTiming.fetchTimer) };
|
||||
indexMarquee.init($('#divIndexMarquee'), ' ');
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
@ -436,7 +354,7 @@ export function getInstruments() {
|
||||
}
|
||||
};
|
||||
|
||||
function createSharesTable() {
|
||||
export function createSharesTable() {
|
||||
try {
|
||||
let excludeNoHoldings = $('#excludeNoHoldings').prop('checked');
|
||||
//logInfo('createSharesTable()');
|
||||
@ -649,27 +567,45 @@ function getNewRemoteData() {
|
||||
|
||||
//logInfo(new Date().yyyymmddhhmmss() + ": getNewRemoteData()")
|
||||
|
||||
let nothingToFetch = false;
|
||||
let lastFetchedDailyInstrument = Instruments.GetLastFetchedDaily();
|
||||
if (lastFetchedDailyInstrument.lastFetchedDaily < (new Date().getTime() - fetchIntervalDaily)) {
|
||||
if (lastFetchedDailyInstrument.lastFetchedDaily < (new Date().getTime() - vars.fetchTiming.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() - fetchIntervalIntraday)) {
|
||||
if (lastFetchedIntradayInstrument.lastFetchedIntraday < (new Date().getTime() - vars.fetchTiming.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() - fetchIntervalSingleDay)) {
|
||||
if (lastFetchedSingleDayInstrument.lastFetchedSingleDay < (new Date().getTime() - vars.fetchTiming.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 });
|
||||
}
|
||||
}
|
||||
@ -733,17 +669,21 @@ function getAllLocalIntradayData() {
|
||||
}
|
||||
}
|
||||
//fetchTimer = setTimeout(getNewRemoteData, 500);
|
||||
//fetchTimer = setInterval(getNewRemoteData, 1250);
|
||||
fetchTimer = setInterval(getNewRemoteData, 1000);
|
||||
//vars.fetchTiming.fetchTimer = setInterval(getNewRemoteData, 1000);
|
||||
vars.fetchTiming.fetchTimer = setTimeout(getNewRemoteData, 200);
|
||||
},
|
||||
failure: function (response) { console.error("getYTDData error:"); console.info(response); }
|
||||
failure: function (response) {
|
||||
vars.fetchTiming.fetchTimer = setTimeout(getNewRemoteData, 5000);
|
||||
console.error("getYTDData error:");
|
||||
console.info(response);
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
logError({ MSG: "Error in getAllLocalIntradayData", err: err });
|
||||
}
|
||||
}
|
||||
|
||||
function redrawAllSharesCharts() {
|
||||
export function redrawAllSharesCharts() {
|
||||
let excludeNoHoldings = $('#excludeNoHoldings').prop('checked');
|
||||
for (let n = 0; n < Instruments.Data.length; n++) {
|
||||
let i = Instruments.Data[n];
|
||||
@ -770,6 +710,7 @@ 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",
|
||||
@ -777,6 +718,7 @@ 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];
|
||||
@ -817,7 +759,8 @@ function getYahooDailyData(Instrument) {
|
||||
//console.info("getYahooDailyData: " + Instrument.Symbol + ', Last data point: ' + d);
|
||||
|
||||
if (newData.length > 0) {
|
||||
lastSuccessfulFetch = new Date();
|
||||
vars.fetchTiming.lastSuccessfulFetch = new Date();
|
||||
vars.fetchTiming.lastSuccessfulFetchDuration = (new Date()) - vars.fetchTiming.lastSuccessfulFetchStartTime;
|
||||
}
|
||||
|
||||
submitNewDailyData(Instrument, newData);
|
||||
@ -828,13 +771,18 @@ function getYahooDailyData(Instrument) {
|
||||
logWarning({ MSG: "No data received for symbol: " + Instrument.Symbol, response: response })
|
||||
}
|
||||
},
|
||||
failure: function (response) { console.error("getYahooDailyData error:"); console.info(response); }
|
||||
failure: function (response) {
|
||||
vars.fetchTiming.lastFetchDuration = (new Date().getTime()) - vars.fetchTiming.lastFetchStartTime;
|
||||
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",
|
||||
@ -842,6 +790,7 @@ 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];
|
||||
@ -874,7 +823,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) {
|
||||
lastSuccessfulFetch = new Date();
|
||||
vars.fetchTiming.lastSuccessfulFetch = new Date();
|
||||
submitNewIntradayData(Instrument, newData);
|
||||
createMidChart(Instrument);
|
||||
createShortChart(Instrument);
|
||||
@ -889,12 +838,17 @@ function getYahooIntradayData(Instrument) {
|
||||
logWarning({ MSG: "No data received for symbol: " + Instrument.Symbol, response: response });
|
||||
}
|
||||
},
|
||||
failure: function (response) { console.error("getYahooIntradayData error:"); console.info(response); }
|
||||
failure: function (response) {
|
||||
vars.fetchTiming.lastFetchDuration = (new Date().getTime()) - vars.fetchTiming.lastFetchStartTime;
|
||||
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",
|
||||
@ -902,6 +856,7 @@ 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];
|
||||
@ -930,7 +885,7 @@ function getYahooSingleDayData(Instrument) {
|
||||
//console.warn("Ignored " + ignoredEntries.toString() + " entries (" + newData.length.toString() + " OK) with null values for " + Instrument.Symbol);
|
||||
}
|
||||
if (newData.length > 0) {
|
||||
lastSuccessfulFetch = new Date();
|
||||
vars.fetchTiming.lastSuccessfulFetch = new Date();
|
||||
}
|
||||
|
||||
if (!Instrument.SingleDayData || Instrument.SingleDayData[0].DT != newData[0].DT || newData.length > Instrument.SingleDayData.length) {
|
||||
@ -960,7 +915,11 @@ function getYahooSingleDayData(Instrument) {
|
||||
logWarning({ MSG: "No data received for symbol: " + Instrument.Symbol, response: response });
|
||||
}
|
||||
},
|
||||
failure: function (response) { console.error("getYahooSingleDayData error:"); console.info(response); }
|
||||
failure: function (response) {
|
||||
vars.fetchTiming.lastFetchDuration = (new Date().getTime()) - vars.fetchTiming.lastFetchStartTime;
|
||||
console.error("getYahooSingleDayData error:");
|
||||
console.info(response);
|
||||
}
|
||||
});
|
||||
}
|
||||
function getLseSingleDayData(Instrument) {
|
||||
@ -968,6 +927,7 @@ 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",
|
||||
@ -975,10 +935,15 @@ 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) { console.error("getLseSingleDayData error:"); console.info(response); }
|
||||
failure: function (response) {
|
||||
vars.fetchTiming.lastFetchDuration = (new Date().getTime()) - vars.fetchTiming.lastFetchStartTime;
|
||||
console.error("getLseSingleDayData error:");
|
||||
console.info(response);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -1088,7 +1053,7 @@ function updateTables() {
|
||||
if (tablesUpdateTimings.updateNeeded && tablesUpdateTimings.lastUpdate + tablesUpdateTimings.timeBetweenUpdates < new Date().getTime()) {
|
||||
tablesUpdateTimings.lastUpdate = new Date().getTime();
|
||||
|
||||
createHoldingsTable();
|
||||
createHoldingsTable(Instruments, vars);
|
||||
createAccountsTables();
|
||||
createAnalysisTable();
|
||||
createIndexMarquee();
|
||||
@ -1096,570 +1061,6 @@ 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,6 +6,55 @@
|
||||
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();
|
||||
|
591
Websites/SharePrices/SharePrices/scripts/SharePrices_Holdings.js
Normal file
591
Websites/SharePrices/SharePrices/scripts/SharePrices_Holdings.js
Normal file
@ -0,0 +1,591 @@
|
||||
'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