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>
|
<div id="chartTooltip" class="chart-tooltip"></div>
|
||||||
<script>
|
<script>
|
||||||
let importedFunctions = {};
|
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) {
|
function showTab(tab) {
|
||||||
//$('.navbar>a').removeClass('activenav');
|
//$('.navbar>a').removeClass('activenav');
|
||||||
$('.navbar').find('.activenav').removeClass('activenav');
|
$('.navbar').find('.activenav').removeClass('activenav');
|
||||||
@ -185,11 +161,11 @@
|
|||||||
redrawAllSharesCharts();
|
redrawAllSharesCharts();
|
||||||
break;*/
|
break;*/
|
||||||
case 'displayAsPercent':
|
case 'displayAsPercent':
|
||||||
importedFunctions.redrawAllSharesCharts();
|
redrawAllSharesCharts();
|
||||||
break;
|
break;
|
||||||
case 'excludeNoHoldings':
|
case 'excludeNoHoldings':
|
||||||
importedFunctions.createSharesTable();
|
createSharesTable();
|
||||||
importedFunctions.redrawAllSharesCharts();
|
redrawAllSharesCharts();
|
||||||
break;
|
break;
|
||||||
/*case 'showPreviousHoldings':
|
/*case 'showPreviousHoldings':
|
||||||
redrawAllSharesCharts();
|
redrawAllSharesCharts();
|
||||||
@ -206,24 +182,12 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<script type="module">
|
<script type="module">
|
||||||
import {
|
import { getInstruments, refreshHistoryChart, showModalDialog_AddInstrument, showModalDialog_AddHolding, showModalDialog_FullScreenChart, showModalDialog_SoldHolding } from "./scripts/SharePrices.js";
|
||||||
createSharesTable,
|
|
||||||
getInstruments,
|
|
||||||
redrawAllSharesCharts,
|
|
||||||
refreshHistoryChart,
|
|
||||||
showModalDialog_AddInstrument,
|
|
||||||
showModalDialog_AddHolding,
|
|
||||||
showModalDialog_FullScreenChart,
|
|
||||||
showModalDialog_SoldHolding
|
|
||||||
} from "./scripts/SharePrices.js";
|
|
||||||
importedFunctions = {
|
importedFunctions = {
|
||||||
createSharesTable: createSharesTable,
|
|
||||||
getInstruments: getInstruments,
|
|
||||||
redrawAllSharesCharts: redrawAllSharesCharts,
|
|
||||||
refreshHistoryChart: refreshHistoryChart,
|
|
||||||
showModalDialog_AddInstrument: showModalDialog_AddInstrument,
|
showModalDialog_AddInstrument: showModalDialog_AddInstrument,
|
||||||
showModalDialog_AddHolding: showModalDialog_AddHolding,
|
showModalDialog_AddHolding: showModalDialog_AddHolding,
|
||||||
showModalDialog_FullScreenChart: showModalDialog_FullScreenChart,
|
showModalDialog_FullScreenChart: showModalDialog_FullScreenChart,
|
||||||
|
refreshHistoryChart: refreshHistoryChart,
|
||||||
showModalDialog_SoldHolding: showModalDialog_SoldHolding
|
showModalDialog_SoldHolding: showModalDialog_SoldHolding
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,8 +20,6 @@
|
|||||||
<IISExpressAnonymousAuthentication />
|
<IISExpressAnonymousAuthentication />
|
||||||
<IISExpressWindowsAuthentication />
|
<IISExpressWindowsAuthentication />
|
||||||
<IISExpressUseClassicPipelineMode />
|
<IISExpressUseClassicPipelineMode />
|
||||||
<Use64BitIISExpress />
|
|
||||||
<UseGlobalApplicationHostFile />
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
@ -261,7 +259,6 @@
|
|||||||
<Content Include="scripts\js.cookie-2.2.1.min.js" />
|
<Content Include="scripts\js.cookie-2.2.1.min.js" />
|
||||||
<Content Include="scripts\SharePrices.js" />
|
<Content Include="scripts\SharePrices.js" />
|
||||||
<Content Include="scripts\SharePrices_Charts.js" />
|
<Content Include="scripts\SharePrices_Charts.js" />
|
||||||
<Content Include="scripts\SharePrices_Holdings.js" />
|
|
||||||
<Content Include="scripts\tablesorter\dragtable.mod.css" />
|
<Content Include="scripts\tablesorter\dragtable.mod.css" />
|
||||||
<Content Include="scripts\tablesorter\filter.formatter.css" />
|
<Content Include="scripts\tablesorter\filter.formatter.css" />
|
||||||
<Content Include="scripts\tablesorter\highlights.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">
|
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<NameOfLastUsedPublishProfile>WinSrv1</NameOfLastUsedPublishProfile>
|
<NameOfLastUsedPublishProfile>WinSrv1</NameOfLastUsedPublishProfile>
|
||||||
<LastActiveSolutionConfig>Debug|Any CPU</LastActiveSolutionConfig>
|
|
||||||
<UseIISExpress>false</UseIISExpress>
|
|
||||||
<Use64BitIISExpress />
|
|
||||||
<IISExpressSSLPort />
|
|
||||||
<IISExpressAnonymousAuthentication />
|
|
||||||
<IISExpressWindowsAuthentication />
|
|
||||||
<IISExpressUseClassicPipelineMode />
|
|
||||||
<UseGlobalApplicationHostFile />
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ProjectExtensions>
|
<ProjectExtensions>
|
||||||
<VisualStudio>
|
<VisualStudio>
|
||||||
|
Binary file not shown.
@ -1,35 +1,21 @@
|
|||||||
// @ts-check
|
// @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,
|
import {createLongChart, createMidChart, createShortChart, createSingleDayChart, createChart_Flot,
|
||||||
createChart, createFullScreenChart, createFullScreenChart_Flot, createFullScreenComparisonChart,
|
createChart, createFullScreenChart, createFullScreenChart_Flot, createFullScreenComparisonChart,
|
||||||
createFullScreenComparisonChart_Flot
|
createFullScreenComparisonChart_Flot} from "./SharePrices_Charts.js";
|
||||||
} from "./SharePrices_Charts.js";
|
|
||||||
import {createHoldingsTable} from "./SharePrices_Holdings.js";
|
|
||||||
|
|
||||||
'use strict';
|
'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 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 currencyFormatter = new Intl.NumberFormat('en-GB', { style: 'currency', currency: 'GBP' });
|
||||||
var instrumentSearchResults = [];
|
var instrumentSearchResults = [];
|
||||||
var tablesUpdateTimings = { lastUpdate: 0, timeBetweenUpdates: 10000, timer: 0, updateNeeded: true };
|
var tablesUpdateTimings = { lastUpdate: 0, timeBetweenUpdates: 10000, timer: 0, updateNeeded: true };
|
||||||
@ -104,13 +90,13 @@ var Instruments = {
|
|||||||
},
|
},
|
||||||
GetLastFetchedDaily: function () {
|
GetLastFetchedDaily: function () {
|
||||||
//if (!this.Data[0].lastFetchedDaily) { this.Data[0].lastFetchedDaily = this.Data[0].MaxDailyDate };
|
//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 lastFetchedDate = this.Data[0].lastFetchedDaily;
|
||||||
let lastFetchedIndex = 0;
|
let lastFetchedIndex = 0;
|
||||||
for (let i = 1; i < this.Data.length; i++) {
|
for (let i = 1; i < this.Data.length; i++) {
|
||||||
if (this.Data[i].Symbol != 'GBP' && this.Data[i].Symbol != 'GBp') {
|
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 = 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) {
|
if (lastFetchedDate > this.Data[i].lastFetchedDaily) {
|
||||||
lastFetchedDate = this.Data[i].lastFetchedDaily;
|
lastFetchedDate = this.Data[i].lastFetchedDaily;
|
||||||
lastFetchedIndex = i;
|
lastFetchedIndex = i;
|
||||||
@ -122,13 +108,13 @@ var Instruments = {
|
|||||||
},
|
},
|
||||||
GetLastFetchedIntraday: function () {
|
GetLastFetchedIntraday: function () {
|
||||||
//if (!this.Data[0].lastFetchedIntraday) { this.Data[0].lastFetchedIntraday = this.Data[0].MaxIntradayDate };
|
//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 lastFetchedDate = this.Data[0].lastFetchedIntraday;
|
||||||
let lastFetchedIndex = 0;
|
let lastFetchedIndex = 0;
|
||||||
for (let i = 1; i < this.Data.length; i++) {
|
for (let i = 1; i < this.Data.length; i++) {
|
||||||
if (this.Data[i].Symbol != 'GBP' && this.Data[i].Symbol != 'GBp') {
|
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 = 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) {
|
if (lastFetchedDate > this.Data[i].lastFetchedIntraday) {
|
||||||
lastFetchedDate = this.Data[i].lastFetchedIntraday;
|
lastFetchedDate = this.Data[i].lastFetchedIntraday;
|
||||||
lastFetchedIndex = i;
|
lastFetchedIndex = i;
|
||||||
@ -236,6 +222,7 @@ var Currencies = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var previousDay, previousClose, previousWeek, previousWeekClose;
|
||||||
|
|
||||||
Date.prototype.yyyymmddhhmmss = function () {
|
Date.prototype.yyyymmddhhmmss = function () {
|
||||||
var yyyy = this.getFullYear().toString();
|
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) {
|
/*function getProfitOrLoss(BasePrice, CurrentPrice, ShowPercent) {
|
||||||
//let priceMovement = (CurrentPrice - BasePrice).toFixed(2);
|
//let priceMovement = (CurrentPrice - BasePrice).toFixed(2);
|
||||||
let priceMovement = (CurrentPrice - BasePrice).autoDP();
|
let priceMovement = (CurrentPrice - BasePrice).autoDP();
|
||||||
@ -305,12 +364,35 @@ Number.prototype.autoScale = function () {
|
|||||||
if (percentMovement[0] == '-') percentMovement = percentMovement.substring(1);
|
if (percentMovement[0] == '-') percentMovement = percentMovement.substring(1);
|
||||||
return '<span class="' + (CurrentPrice < BasePrice ? 'loss' : 'profit') + '">' + (CurrentPrice < BasePrice ? '' : '+') + priceMovement + (ShowPercent ? ' (' + percentMovement + ')' : '') + '</span>';
|
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() {
|
export function getInstruments() {
|
||||||
try {
|
try {
|
||||||
//logInfo('getInstruments()');
|
//logInfo('getInstruments()');
|
||||||
vars.initialPageTitle = document.title;
|
initialPageTitle = document.title;
|
||||||
if (vars.fetchTiming.fetchTimer != 0) { clearTimeout(vars.fetchTiming.fetchTimer) };
|
if (fetchTimer != 0) { clearTimeout(fetchTimer) };
|
||||||
indexMarquee.init($('#divIndexMarquee'), ' ');
|
indexMarquee.init($('#divIndexMarquee'), ' ');
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: "POST",
|
type: "POST",
|
||||||
@ -354,7 +436,7 @@ export function getInstruments() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export function createSharesTable() {
|
function createSharesTable() {
|
||||||
try {
|
try {
|
||||||
let excludeNoHoldings = $('#excludeNoHoldings').prop('checked');
|
let excludeNoHoldings = $('#excludeNoHoldings').prop('checked');
|
||||||
//logInfo('createSharesTable()');
|
//logInfo('createSharesTable()');
|
||||||
@ -567,45 +649,27 @@ function getNewRemoteData() {
|
|||||||
|
|
||||||
//logInfo(new Date().yyyymmddhhmmss() + ": getNewRemoteData()")
|
//logInfo(new Date().yyyymmddhhmmss() + ": getNewRemoteData()")
|
||||||
|
|
||||||
let nothingToFetch = false;
|
|
||||||
let lastFetchedDailyInstrument = Instruments.GetLastFetchedDaily();
|
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());
|
//logInfo(new Date().yyyymmddhhmmss() + ": Last fetched daily: " + lastFetchedDailyInstrument.Symbol + ", Last fetched date: " + new Date(lastFetchedDailyInstrument.lastFetchedDaily).yyyymmddhhmmss());
|
||||||
getYahooDailyData(lastFetchedDailyInstrument);
|
getYahooDailyData(lastFetchedDailyInstrument);
|
||||||
} else {
|
} else {
|
||||||
let lastFetchedIntradayInstrument = Instruments.GetLastFetchedIntraday();
|
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());
|
//logInfo(new Date().yyyymmddhhmmss() + ": Last fetched intraday: " + lastFetchedIntradayInstrument.Symbol + ", Last fetched date: " + new Date(lastFetchedIntradayInstrument.lastFetchedIntraday).yyyymmddhhmmss());
|
||||||
getYahooIntradayData(lastFetchedIntradayInstrument);
|
getYahooIntradayData(lastFetchedIntradayInstrument);
|
||||||
} else {
|
} else {
|
||||||
let lastFetchedSingleDayInstrument = Instruments.GetLastFetchedSingleDay();
|
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());
|
//logInfo(new Date().yyyymmddhhmmss() + ": Last fetched single day: " + lastFetchedSingleDayInstrument.Symbol + ", Last fetched date: " + new Date(lastFetchedSingleDayInstrument.lastFetchedSingleDay).yyyymmddhhmmss());
|
||||||
getYahooSingleDayData(lastFetchedSingleDayInstrument);
|
getYahooSingleDayData(lastFetchedSingleDayInstrument);
|
||||||
} else {
|
} //else {
|
||||||
//fetchInterval = 3000;
|
//fetchInterval = 3000;
|
||||||
//fetchTimer = setTimeout(getNewRemoteData, fetchInterval);
|
//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) {
|
} catch (err) {
|
||||||
vars.fetchTiming.fetchTimer = setTimeout(getNewRemoteData, 15000);
|
|
||||||
logError({ MSG: "Error in getNewRemoteData", err: err });
|
logError({ MSG: "Error in getNewRemoteData", err: err });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -669,21 +733,17 @@ function getAllLocalIntradayData() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
//fetchTimer = setTimeout(getNewRemoteData, 500);
|
//fetchTimer = setTimeout(getNewRemoteData, 500);
|
||||||
//vars.fetchTiming.fetchTimer = setInterval(getNewRemoteData, 1000);
|
//fetchTimer = setInterval(getNewRemoteData, 1250);
|
||||||
vars.fetchTiming.fetchTimer = setTimeout(getNewRemoteData, 200);
|
fetchTimer = setInterval(getNewRemoteData, 1000);
|
||||||
},
|
},
|
||||||
failure: function (response) {
|
failure: function (response) { console.error("getYTDData error:"); console.info(response); }
|
||||||
vars.fetchTiming.fetchTimer = setTimeout(getNewRemoteData, 5000);
|
|
||||||
console.error("getYTDData error:");
|
|
||||||
console.info(response);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logError({ MSG: "Error in getAllLocalIntradayData", err: err });
|
logError({ MSG: "Error in getAllLocalIntradayData", err: err });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function redrawAllSharesCharts() {
|
function redrawAllSharesCharts() {
|
||||||
let excludeNoHoldings = $('#excludeNoHoldings').prop('checked');
|
let excludeNoHoldings = $('#excludeNoHoldings').prop('checked');
|
||||||
for (let n = 0; n < Instruments.Data.length; n++) {
|
for (let n = 0; n < Instruments.Data.length; n++) {
|
||||||
let i = Instruments.Data[n];
|
let i = Instruments.Data[n];
|
||||||
@ -710,7 +770,6 @@ function fixDecimals(val, place) {
|
|||||||
function getYahooDailyData(Instrument) {
|
function getYahooDailyData(Instrument) {
|
||||||
//console.info('getYahooDailyData: ' + Instrument.Symbol);
|
//console.info('getYahooDailyData: ' + Instrument.Symbol);
|
||||||
Instrument.lastFetchedDaily = new Date().getTime();
|
Instrument.lastFetchedDaily = new Date().getTime();
|
||||||
vars.fetchTiming.lastFetchStartTime = new Date().getTime();
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: "POST",
|
type: "POST",
|
||||||
contentType: "application/json",
|
contentType: "application/json",
|
||||||
@ -718,7 +777,6 @@ function getYahooDailyData(Instrument) {
|
|||||||
dataType: "json",
|
dataType: "json",
|
||||||
data: JSON.stringify({ Symbol: Instrument.Symbol, MinDailyDT: (Instrument.DailyData.length ? Instrument.DailyData[0].DT: new Date().getTime()) }),
|
data: JSON.stringify({ Symbol: Instrument.Symbol, MinDailyDT: (Instrument.DailyData.length ? Instrument.DailyData[0].DT: new Date().getTime()) }),
|
||||||
success: function (response) {
|
success: function (response) {
|
||||||
vars.fetchTiming.lastFetchDuration = (new Date().getTime()) - vars.fetchTiming.lastFetchStartTime;
|
|
||||||
let newData = [];
|
let newData = [];
|
||||||
if (response.chart && response.chart.result) {
|
if (response.chart && response.chart.result) {
|
||||||
let dataSeries = response.chart.result[0];
|
let dataSeries = response.chart.result[0];
|
||||||
@ -759,8 +817,7 @@ function getYahooDailyData(Instrument) {
|
|||||||
//console.info("getYahooDailyData: " + Instrument.Symbol + ', Last data point: ' + d);
|
//console.info("getYahooDailyData: " + Instrument.Symbol + ', Last data point: ' + d);
|
||||||
|
|
||||||
if (newData.length > 0) {
|
if (newData.length > 0) {
|
||||||
vars.fetchTiming.lastSuccessfulFetch = new Date();
|
lastSuccessfulFetch = new Date();
|
||||||
vars.fetchTiming.lastSuccessfulFetchDuration = (new Date()) - vars.fetchTiming.lastSuccessfulFetchStartTime;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
submitNewDailyData(Instrument, newData);
|
submitNewDailyData(Instrument, newData);
|
||||||
@ -771,18 +828,13 @@ function getYahooDailyData(Instrument) {
|
|||||||
logWarning({ MSG: "No data received for symbol: " + Instrument.Symbol, response: response })
|
logWarning({ MSG: "No data received for symbol: " + Instrument.Symbol, response: response })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
failure: function (response) {
|
failure: function (response) { console.error("getYahooDailyData error:"); console.info(response); }
|
||||||
vars.fetchTiming.lastFetchDuration = (new Date().getTime()) - vars.fetchTiming.lastFetchStartTime;
|
|
||||||
console.error("getYahooDailyData error:");
|
|
||||||
console.info(response);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function getYahooIntradayData(Instrument) {
|
function getYahooIntradayData(Instrument) {
|
||||||
//console.info('getYahooIntradayData: ' + Instrument.Symbol);
|
//console.info('getYahooIntradayData: ' + Instrument.Symbol);
|
||||||
Instrument.lastFetchedIntraday = new Date().getTime();
|
Instrument.lastFetchedIntraday = new Date().getTime();
|
||||||
let minIntradayDT = Instrument.IntradayData.length > 0 ? Instrument.IntradayData[0].DT : new Date().getTime();
|
let minIntradayDT = Instrument.IntradayData.length > 0 ? Instrument.IntradayData[0].DT : new Date().getTime();
|
||||||
vars.fetchTiming.lastFetchStartTime = new Date().getTime();
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: "POST",
|
type: "POST",
|
||||||
contentType: "application/json",
|
contentType: "application/json",
|
||||||
@ -790,7 +842,6 @@ function getYahooIntradayData(Instrument) {
|
|||||||
dataType: "json",
|
dataType: "json",
|
||||||
data: JSON.stringify({ Symbol: Instrument.Symbol, MinIntradayDT: minIntradayDT }),
|
data: JSON.stringify({ Symbol: Instrument.Symbol, MinIntradayDT: minIntradayDT }),
|
||||||
success: function (response) {
|
success: function (response) {
|
||||||
vars.fetchTiming.lastFetchDuration = (new Date().getTime()) - vars.fetchTiming.lastFetchStartTime;
|
|
||||||
let newData = [];
|
let newData = [];
|
||||||
if (response.chart && response.chart.result) {
|
if (response.chart && response.chart.result) {
|
||||||
let dataSeries = response.chart.result[0];
|
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);
|
//console.warn("Didn't submit " + nonSubmittedEntries.toString() + " entries (" + newData.length.toString() + " OK) with null values for " + Instrument.Symbol);
|
||||||
}
|
}
|
||||||
if (newData.length) {
|
if (newData.length) {
|
||||||
vars.fetchTiming.lastSuccessfulFetch = new Date();
|
lastSuccessfulFetch = new Date();
|
||||||
submitNewIntradayData(Instrument, newData);
|
submitNewIntradayData(Instrument, newData);
|
||||||
createMidChart(Instrument);
|
createMidChart(Instrument);
|
||||||
createShortChart(Instrument);
|
createShortChart(Instrument);
|
||||||
@ -838,17 +889,12 @@ function getYahooIntradayData(Instrument) {
|
|||||||
logWarning({ MSG: "No data received for symbol: " + Instrument.Symbol, response: response });
|
logWarning({ MSG: "No data received for symbol: " + Instrument.Symbol, response: response });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
failure: function (response) {
|
failure: function (response) { console.error("getYahooIntradayData error:"); console.info(response); }
|
||||||
vars.fetchTiming.lastFetchDuration = (new Date().getTime()) - vars.fetchTiming.lastFetchStartTime;
|
|
||||||
console.error("getYahooIntradayData error:");
|
|
||||||
console.info(response);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function getYahooSingleDayData(Instrument) {
|
function getYahooSingleDayData(Instrument) {
|
||||||
//console.info('getYahooSingleDayData: ' + Instrument.Symbol);
|
//console.info('getYahooSingleDayData: ' + Instrument.Symbol);
|
||||||
Instrument.lastFetchedSingleDay = new Date().getTime();
|
Instrument.lastFetchedSingleDay = new Date().getTime();
|
||||||
vars.fetchTiming.lastFetchStartTime = new Date().getTime();
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: "POST",
|
type: "POST",
|
||||||
contentType: "application/json",
|
contentType: "application/json",
|
||||||
@ -856,7 +902,6 @@ function getYahooSingleDayData(Instrument) {
|
|||||||
dataType: "json",
|
dataType: "json",
|
||||||
data: JSON.stringify({ Symbol: Instrument.Symbol }),
|
data: JSON.stringify({ Symbol: Instrument.Symbol }),
|
||||||
success: function (response) {
|
success: function (response) {
|
||||||
vars.fetchTiming.lastFetchDuration = (new Date().getTime()) - vars.fetchTiming.lastFetchStartTime;
|
|
||||||
let newData = [];
|
let newData = [];
|
||||||
if (response.chart && response.chart.result) {
|
if (response.chart && response.chart.result) {
|
||||||
let dataSeries = response.chart.result[0];
|
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);
|
//console.warn("Ignored " + ignoredEntries.toString() + " entries (" + newData.length.toString() + " OK) with null values for " + Instrument.Symbol);
|
||||||
}
|
}
|
||||||
if (newData.length > 0) {
|
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) {
|
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 });
|
logWarning({ MSG: "No data received for symbol: " + Instrument.Symbol, response: response });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
failure: function (response) {
|
failure: function (response) { console.error("getYahooSingleDayData error:"); console.info(response); }
|
||||||
vars.fetchTiming.lastFetchDuration = (new Date().getTime()) - vars.fetchTiming.lastFetchStartTime;
|
|
||||||
console.error("getYahooSingleDayData error:");
|
|
||||||
console.info(response);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function getLseSingleDayData(Instrument) {
|
function getLseSingleDayData(Instrument) {
|
||||||
@ -927,7 +968,6 @@ function getLseSingleDayData(Instrument) {
|
|||||||
Instrument.lastFetchedSingleDay = new Date().getTime();
|
Instrument.lastFetchedSingleDay = new Date().getTime();
|
||||||
let fromDate = new Date().setHours(0, 0, 0);
|
let fromDate = new Date().setHours(0, 0, 0);
|
||||||
let toDate = new Date().getTime();
|
let toDate = new Date().getTime();
|
||||||
vars.fetchTiming.lastFetchStartTime = new Date().getTime();
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: "POST",
|
type: "POST",
|
||||||
contentType: "application/json",
|
contentType: "application/json",
|
||||||
@ -935,15 +975,10 @@ function getLseSingleDayData(Instrument) {
|
|||||||
dataType: "json",
|
dataType: "json",
|
||||||
data: JSON.stringify({ Symbol: Instrument.Symbol, FromDate: fromDate, ToDate: toDate }),
|
data: JSON.stringify({ Symbol: Instrument.Symbol, FromDate: fromDate, ToDate: toDate }),
|
||||||
success: function (response) {
|
success: function (response) {
|
||||||
vars.fetchTiming.lastFetchDuration = (new Date().getTime()) - vars.fetchTiming.lastFetchStartTime;
|
|
||||||
console.info("getLseSingleDayData: " + JSON.stringify(response));
|
console.info("getLseSingleDayData: " + JSON.stringify(response));
|
||||||
|
|
||||||
},
|
},
|
||||||
failure: function (response) {
|
failure: function (response) { console.error("getLseSingleDayData error:"); console.info(response); }
|
||||||
vars.fetchTiming.lastFetchDuration = (new Date().getTime()) - vars.fetchTiming.lastFetchStartTime;
|
|
||||||
console.error("getLseSingleDayData error:");
|
|
||||||
console.info(response);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1053,7 +1088,7 @@ function updateTables() {
|
|||||||
if (tablesUpdateTimings.updateNeeded && tablesUpdateTimings.lastUpdate + tablesUpdateTimings.timeBetweenUpdates < new Date().getTime()) {
|
if (tablesUpdateTimings.updateNeeded && tablesUpdateTimings.lastUpdate + tablesUpdateTimings.timeBetweenUpdates < new Date().getTime()) {
|
||||||
tablesUpdateTimings.lastUpdate = new Date().getTime();
|
tablesUpdateTimings.lastUpdate = new Date().getTime();
|
||||||
|
|
||||||
createHoldingsTable(Instruments, vars);
|
createHoldingsTable();
|
||||||
createAccountsTables();
|
createAccountsTables();
|
||||||
createAnalysisTable();
|
createAnalysisTable();
|
||||||
createIndexMarquee();
|
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 createAccountsTables() {
|
||||||
function getAccounts() {
|
function getAccounts() {
|
||||||
let accounts = [];
|
let accounts = [];
|
||||||
|
@ -6,55 +6,6 @@
|
|||||||
oneWeek: 7 * 24 * 60 * 60 * 1000,
|
oneWeek: 7 * 24 * 60 * 60 * 1000,
|
||||||
oneMonth: 31 * 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) {
|
export function getProfitOrLoss(BasePrice, CurrentPrice, ShowPercent) {
|
||||||
//let priceMovement = (CurrentPrice - BasePrice).toFixed(2);
|
//let priceMovement = (CurrentPrice - BasePrice).toFixed(2);
|
||||||
let priceMovement = (CurrentPrice - BasePrice).autoDP();
|
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