Instrument | ' + - 'YTD | ' + - '1 Month | ' + - '1 Week | ' + + 'All | ' + + '3 Month | ' + + '2 Week | ' + '1 Day | ' + instrument.DisplayName + ' ' + //' | ' + instrument.InstrumentName + ' ' + '' + instrument.Symbol + ' ' + //'+' + @@ -816,19 +840,29 @@ function createLongChart(Instrument) { } function createMidChart(Instrument) { let rowNo = Instrument.DisplayOrder; + /* let monthStartDate = new Date().setHours(0, 0, 0) - timespans.oneMonth; let monthEndDate = new Date().getTime(); - //if (Instrument.MidChart) { try { Instrument.MidChart.destroy(); } catch (ex) { } Instrument.MidChart = createChart(Instrument, monthStartDate, monthEndDate, 'divMidChart' + rowNo.toString(), 'divMidSummary' + rowNo.toString()); + */ + let quarterStartDate = new Date().setHours(0, 0, 0) - (timespans.oneMonth * 3); + let quarterEndDate = new Date().getTime(); + try { Instrument.MidChart.destroy(); } catch (ex) { } + Instrument.MidChart = createChart(Instrument, quarterStartDate, quarterEndDate, 'divMidChart' + rowNo.toString(), 'divMidSummary' + rowNo.toString()); } function createShortChart(Instrument) { let rowNo = Instrument.DisplayOrder; + /* let weekStartDate = new Date().setHours(0, 0, 0) - timespans.oneWeek; let weekEndDate = new Date().getTime(); - //if (Instrument.ShortChart) { try { Instrument.ShortChart.destroy(); } catch (ex) { } Instrument.ShortChart = createChart(Instrument, weekStartDate, weekEndDate, 'divShortChart' + rowNo.toString(), 'divShortSummary' + rowNo.toString()); + */ + let fortnightStartDate = new Date().setHours(0, 0, 0) - (timespans.oneWeek * 2); + let fortnightEndDate = new Date().getTime(); + try { Instrument.ShortChart.destroy(); } catch (ex) { } + Instrument.ShortChart = createChart(Instrument, fortnightStartDate, fortnightEndDate, 'divShortChart' + rowNo.toString(), 'divShortSummary' + rowNo.toString()); } function createSingleDayChart(Instrument) { let rowNo = Instrument.DisplayOrder; @@ -862,7 +896,7 @@ function createChart_Flot(Instrument, startDate, endDate, chartContainerID, summ } } else { //Get all in-date-range intraday data - let foundDates = { min: new Date().getTime, max: 0 } + let foundDates = { min: new Date().getTime(), max: 0 } for (let i = 0; i < Instrument.IntradayData.length; i++) { let quote = Instrument.IntradayData[i]; if (quote.DT >= dateFrom && quote.DT <= dateTo) { @@ -874,13 +908,19 @@ function createChart_Flot(Instrument, startDate, endDate, chartContainerID, summ //Back-fill with daily data if the intraday data doesn't cover the whole date range let minFoundDate = new Date(foundDates.min).setHours(0, 0, 0); if (minFoundDate > dateFrom) { - for (let i = 0; i < Instrument.DailyData.length; i++) { + /*for (let i = 0; i < Instrument.DailyData.length; i++) { let quote = Instrument.DailyData[i]; if (quote.DT >= dateFrom && quote.DT < minFoundDate) { result.push(quote); } else { break; } + }*/ + for (let i = Instrument.DailyData.length - 1; i > 0; i--) { + let quote = Instrument.DailyData[i]; + if (quote.DT >= dateFrom && quote.DT < minFoundDate) { + result.unshift(quote); + } } } } @@ -1030,7 +1070,7 @@ function createChart_Flot(Instrument, startDate, endDate, chartContainerID, summ //Add the breakeven price line //if ($('#showBreakevenLine').prop('checked')) { - if (Instrument.ShowBreakevenLine | 0 == 1) { + if ((Instrument.ShowBreakevenLine | 0) == 1) { if (priceType == 'percent') { series.unshift({ data: [[startDate, ((Instrument.BreakevenPrice / basePrice) - 1) * 100], [endDate, ((Instrument.BreakevenPrice / basePrice) - 1) * 100]], color: '#24BCC7', shadowSize: 0, lines: { lineWidth: 1 } }); } else { @@ -1173,7 +1213,7 @@ function createChart(Instrument, startDate, endDate, chartContainerID, summaryCo } } else { //Get all in-date-range intraday data - let foundDates = { min: new Date().getTime, max: 0 } + let foundDates = { min: new Date().getTime(), max: 0 } for (let i = 0; i < Instrument.IntradayData.length; i++) { let quote = Instrument.IntradayData[i]; if (quote.DT >= dateFrom && quote.DT <= dateTo) { @@ -1185,13 +1225,19 @@ function createChart(Instrument, startDate, endDate, chartContainerID, summaryCo //Back-fill with daily data if the intraday data doesn't cover the whole date range let minFoundDate = new Date(foundDates.min).setHours(0, 0, 0); if (minFoundDate > dateFrom) { - for (let i = 0; i < Instrument.DailyData.length; i++) { + /*for (let i = 0; i < Instrument.DailyData.length; i++) { let quote = Instrument.DailyData[i]; if (quote.DT >= dateFrom && quote.DT < minFoundDate) { result.push(quote); } else { break; } + }*/ + for (let i = Instrument.DailyData.length - 1; i > 0; i--) { + let quote = Instrument.DailyData[i]; + if (quote.DT >= dateFrom && quote.DT < minFoundDate) { + result.unshift(quote); + } } } } @@ -1340,7 +1386,7 @@ function createChart(Instrument, startDate, endDate, chartContainerID, summaryCo let series = [{ name: 'Volume', type: 'column', yAxis: 1, color: "#304e57", borderColor: "#304e57", data: volumeData }]; //Add the breakeven price line - if (Instrument.ShowBreakevenLine | 0 == 1) { + if ((Instrument.ShowBreakevenLine | 0) == 1) { if (priceType == 'percent') { series.unshift({ name: 'Breakeven', type: 'line', color: '#24BCC7', lineWidth: 1, marker: { enabled: false }, data: [[startDate, ((Instrument.BreakevenPrice / basePrice) - 1) * 100], [endDate, ((Instrument.BreakevenPrice / basePrice) - 1) * 100]] /* shadowSize: 0 */ }); } else { @@ -1448,7 +1494,7 @@ function createChart(Instrument, startDate, endDate, chartContainerID, summaryCo //Create the summary table let summaryTable = '
' +
+ ' | ';
+ }
+ 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 = '' + labelText + ' ' +
+ '' + type + ' | ' +
+ '' + h.instrumentName + ' | ' +
+ '' + h.symbol + '' + ' | ' +
+ '' + h.currency + ' | ' +
+ '' + h.accountName + ' | ' +
+ '' + new Date(h.purchaseDateMin).yyyymmdd()) : (' title="' + new Date(h.purchaseDateMax).yyyymmdd() + ' - ' + new Date(h.purchaseDateMin).yyyymmdd() + '">' + new Date(h.purchaseDateMin).yyyymmdd()) + '+') + ' | ' +
+ //'' + formatAmount(h.noUnits, '', 0) + ' | ' +
+ '' + h.noUnits.autoScale() + ' | ' +
+ '' + formatAmount(h.purchasePricePerUnit, h.currency, 2) + ' | ' + //Buy Price
+ '' + (h.currentPrice ? formatAmount(h.currentPrice, h.currency, 2) : '') + ' | ' + //Current Price
+ '' + formatAmount(h.currency == 'GBp' ? h.bookCost / 100 : h.bookCost, h.currency == 'GBp' ? 'GBP' : h.currency, 0) + ' | ' + //Book Cost
+ '' + (h.currentExchangeRate ? formatAmount(h.currentValue, h.currency == 'GBp' ? 'GBP' : h.currency, 0) : '') + ' | ' + //Current Value
+ '' + (h.currentPrice ? formatAmount(h.gain, h.currency == 'GBp' ? 'GBP' : h.currency, 0) : '') + ' | ' + //Gain
+ '' + (h.currentExchangeRate ? formatAmount(h.gainGBP, 'GBP', 0) : '') + ' | ' + //Gain £
+ '' + (h.currentExchangeRate ? h.gainPercent.toFixed(1) + '%' : '') + ' | ' + //Gain %
+ '' + (h.currentExchangeRate ? formatAmount(h.currentValueGBP, 'GBP', 0) : '') + ' | ' + //Current Value £
+ (h.currentExchangeRate ? getPercentCell('#3f2d95', 70, scaledAllocation, allocationPercent.toFixed(1) + '%') : '') + //Allocation
+ (h.currentExchangeRate ? (h.marketIsOpen == 0 ? '' : ' | ' + formatAmount(h.singleDayGainGBP, 'GBP', 0) + ' | ') : '') + //Today's Gain £
+ (h.singleDayGainPercent ? (h.marketIsOpen == 0 ? '' : ' | ' + h.singleDayGainPercent.toFixed(1) + '% | ') : '') + //Today's Gain %
+ ' | Updated: ' + new Date().yyyymmddhhmmss() + lSF + ' | ' +
+ '' + formatAmount(totalBookCostGBP, 'GBP', 0) + ' | ' + //Book Cost
+ '' +
+ ' | ' + formatAmount(totalGainGBP, 'GBP', 0) + ' | ' + //Gain GBP
+ '' + formatAmount((((totalValueGBP / totalBookCostGBP) - 1) * 100), '', 1) + '% | ' + //Gain %
+ '' + formatAmount(totalValueGBP, 'GBP', 0) + ' | ' + //Current Value
+ ' | ' + //Allocation
+ '' + formatAmount(todaysGainGBP, 'GBP', 0) + ' | ' + //Today's Gain GBP
+ '' + formatAmount(((todaysGainGBP / totalBookCostGBP) * 100), '', 1) + '% | ' + //Today's Gain %
+ '';
+ if (wi.LastSoldPrice) {
+ let delta = (wi.CurrentPrice - wi.LastSoldPrice) / wi.LastSoldPrice * 100;
+ let tooltip = new Date(wi.LastSoldDate).yyyymmdd() + ' @ ' + wi.LastSoldPrice.toFixed(2);
+ sinceLastSold = ' | ' + (delta).toFixed(1) + '% | ';
+ }
+
+ //Type, Name, Symbol, Ccy, Current Price, Today's Gain, Today's Gain %
+ let row = '' + type + ' | ' +
+ '' + wi.InstrumentName + ' | ' +
+ '' + wi.Symbol + '' + ' | ' +
+ '' + wi.Currency + ' | ' +
+ '' + (wi.CurrentPrice ? formatAmount(wi.CurrentPrice, wi.Currency, 2) : '') + ' | ' + //Current Price
+ (Instruments.marketIsOpen == 0 ? '' : ' | ' + (wi.CurrentPrice ? formatAmount(wi.CurrentPrice - wi.SingleDayPreviousClose, wi.Currency, 2) : '') + ' | ') + //Today's Gain
+ (Instruments.marketIsOpen == 0 ? '' : ' | ' + (wi.CurrentPrice ? ((wi.CurrentPrice - wi.SingleDayPreviousClose) / wi.SingleDayPreviousClose * 100).toFixed(1) : '') + ' | ') + //Today's Gain %
+ //'' + (new Date(wi.LastSoldDate)).yyyymmdd() + ' | ' +
+ sinceLastSold +
+ '
" + label + " ";
+ }
+ 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 "" + Math.round(series.percent) + "% " + formatAmount(series.data[0][1], 'GBP', 0) + " " + label + " ";
+ }
+ ops.series.pie.label.formatter = labelFormatter2;
+ cd = bookCostPerAccount.getChartData();
+ if (cd.length) {
+ $.plot('#divAccountBookCost', cd, ops);
+ }
+
+ cd = currentValuePerAccount.getChartData();
+ if (cd.length) {
+ $.plot('#divAccountValue', cd, ops);
+ }
+}
+function createAccountsTables() {
+ function getAccounts() {
+ let accounts = [];
+ function indexOfAccount(accountName) { return accounts.map(function (item) { return item.accountName; }).indexOf(accountName); };
+ function getAccount(accountName) { return accounts[indexOfAccount(accountName)]; };
+ function addAccountHolding(instrument, exchangeRate, holding) {
+ let a = getAccount(holding.AccountName);
+ if (!a) {
+ a = { accountName: holding.AccountName, holdings: [], totalCostGBP: 0, totalValueGBP: 0, totalGainGBP: 0 };
+ accounts.push(a);
+ }
+ let h = {
+ instrumentName: instrument.InstrumentName,
+ symbol: instrument.Symbol,
+ currency: instrument.Currency,
+ purchaseDate: holding.PurchaseDate,
+ purchasePricePerUnit: holding.PurchasePricePerUnit,
+ noUnits: holding.NoUnits,
+ cost: holding.NoUnits * holding.PurchasePricePerUnit
+ }
+ //if (exchangeRate) {
+ //h.costGBP = h.cost / exchangeRate;
+ h.costGBP = holding.BookCostGBP;
+ a.totalCostGBP += holding.BookCostGBP;
+ //}
+ if (instrument.CurrentPrice) {
+ h.currentPrice = instrument.CurrentPrice;
+ h.currentValue = holding.NoUnits * instrument.CurrentPrice;
+ h.gain = h.currentValue - h.cost;
+ if (exchangeRate) {
+ h.currentValueGBP = h.currentValue / exchangeRate;
+ //h.gainGBP = h.gain / exchangeRate;
+ h.gainGBP = h.currentValueGBP - h.costGBP;
+ a.totalGainGBP += h.gainGBP;
+ a.totalValueGBP += h.currentValueGBP;
+ }
+ }
+ a.holdings.push(h);
+ }
+
+ for (let ix = 0; ix < Instruments.Data.length; ix++) {
+ let i = Instruments.Data[ix];
+ let exchangegRate = Instruments.GetExchangeRate(i.Currency, 'GBP');
+ for (let hx = 0; hx < i.Holdings.length; hx++) {
+ let h = i.Holdings[hx];
+ if (h.SoldDate == null) {
+ addAccountHolding(i, exchangegRate, h);
+ }
+ }
+ }
+
+ return accounts;
+ }
+
+ let accounts = getAccounts();
+
+ //let eDT = new Date();
+ //console.info('createAccountsTable after getAccounts: ' + (eDT.getTime() - sDT.getTime()) + ' (' + sDT.yyyymmddhhmmss() + ' -> ' + eDT.yyyymmddhhmmss() + ')');
+
+ //Sort the accounts array by accountName
+ accounts.sort(function (a, b) {
+ if (a.accountName < b.accountName) return -1;
+ if (a.accountName > b.accountName) return 1;
+ return 0;
+ });
+
+ //eDT = new Date();
+ //console.info('createAccountsTable after array sort: ' + (eDT.getTime() - sDT.getTime()) + ' (' + sDT.yyyymmddhhmmss() + ' -> ' + eDT.yyyymmddhhmmss() + ')');
+
+ //let summaryTable = "" + formatAmount(series.data[0][1], 'GBP', 0) + "
" + a.accountName + " | " +
+ "" + a.holdings.length + " | " +
+ "" + formatAmount(a.totalCostGBP || 0, 'GBP', 2) + " | " +
+ "" + formatAmount(accountGainsGBP, 'GBP', 2) + " | " +
+ "" + formatAmount(accountLossesGBP, 'GBP', 2) + " | " +
+ "" + formatAmount(a.totalValueGBP || 0, 'GBP', 2) + " | " +
+ "" + formatAmount(a.totalGainGBP || 0, 'GBP', 2) + " | " +
+ "' + accountsTables); +} +function createAnalysisTable() { + function getHighestPriceBeforeDate(Instrument, maxDT) { + if (Instrument.DailyData) { + let r = { Price: 0, DT: 0 }; + let dt = 0; + for (let i = 0; i < Instrument.DailyData.length && dt < maxDT; i++) { + let price = Instrument.DailyData[i]; + dt = price.DT; + if (dt <= maxDT && price.high > r.Price) { + r.Price = price.high; + r.DT = dt; + } + } + return r; + } else { + return { Price: NaN, DT: NaN }; + } + } + function getHighestPriceBetweenDates(Instrument, minDT, maxDT) { + if (Instrument.DailyData) { + let r = { Price: 0, DT: 0 }; + let dt = 0; + for (let i = 0; i < Instrument.DailyData.length && dt < maxDT; i++) { + let price = Instrument.DailyData[i]; + dt = price.DT; + if (dt >= minDT && dt <= maxDT && price.high > r.Price) { + r.Price = price.high; + r.DT = dt; + } + } + return r; + } else { + return { Price: NaN, DT: NaN }; + } + } + function getLowestPriceAfterDate(Instrument, minDT) { + if (Instrument.DailyData) { + let r = { Price: 0, DT: 0 }; + let dt = 0; + let i = 0; + let DD = Instrument.DailyData; + while (dt < minDT && i < DD.length - 1) { + i++; + dt = DD[i].DT; + } + if (i < DD.length) { + r.Price = DD[i].low; + r.DT = DD[i].DT; + + while (i < DD.length - 1) { + i++; + if (DD[i].low < r.Price) { + r.Price = DD[i].low; + r.DT = DD[i].DT; + } + } + } + return r; + } else { + return { Price: NaN, DT: NaN }; + } + } + function getEquities() { + function getHoldingsValueGBP(Instrument) { + let noUnits = 0; + if (Instrument.CurrentPrice) { + let c = Instruments.GetCurrentHoldings(Instrument); + for (let x = 0; x < c.length; x++) { + noUnits += c[x].NoUnits; + } + let exchangeRate = Instruments.GetExchangeRate(Instrument.Currency, 'GBP'); + return noUnits * Instrument.CurrentPrice / exchangeRate; + } else { + return 0; + } + } + + let totalHoldingsValueGBP = 0; + let r = []; + for (let n = 0; n < Instruments.Data.length; n++) { + let i = Instruments.Data[n]; + try { + if (i.InstrumentType == 'EQUITY') { + let prePandemicHigh = getHighestPriceBetweenDates(i, new Date('2020-01-01'), pandemicCrashStartDate); + let postPandemicLow = getLowestPriceAfterDate(i, pandemicCrashStartDate); + let dilutedTarget = prePandemicHigh.Price * i.PostPandemicDilution; + let currentHoldingsValueGBP = getHoldingsValueGBP(i); + let remainingDilutedPotential = ((dilutedTarget - i.CurrentPrice) / i.CurrentPrice) * 100; + totalHoldingsValueGBP += currentHoldingsValueGBP; + + r.push({ + prePandemicHigh: prePandemicHigh, + postPandemicLow: postPandemicLow, + displayOrder: i.DisplayOrder, + instrumentName: i.InstrumentName, + symbol: i.Symbol, + currency: i.Currency, + currentPrice: i.CurrentPrice, + fallPercent: ((postPandemicLow.Price / prePandemicHigh.Price) - 1) * 100, + postPandemicDilution: i.PostPandemicDilution * 100, + dilutedTarget: dilutedTarget, + currentDiscount: ((i.CurrentPrice / prePandemicHigh.Price) - 1) * 100, + potentialDilutedRebound: (((prePandemicHigh.Price * i.PostPandemicDilution) / postPandemicLow.Price) - 1) * 100, + retracementPercent: ((i.CurrentPrice - postPandemicLow.Price) / (prePandemicHigh.Price - postPandemicLow.Price)) * 100, + remainingPotential: ((prePandemicHigh.Price - i.CurrentPrice) / i.CurrentPrice) * 100, + remainingDilutedPotential: remainingDilutedPotential, + currentHoldingsValueGBP: currentHoldingsValueGBP, + possibleRemainingProfit: currentHoldingsValueGBP * remainingDilutedPotential / 100 + }); + } + } + catch (err) { + console.error("Error analysing " + i.Symbol + ": " + err.message); + } + } + return { totalHoldingsValueGBP: totalHoldingsValueGBP, equities: r }; + } + function createTable() { + let t = $("#tblAnalysis"); + if (t.length) { + return t; + } else { + let tbl = '
' + e.displayOrder + ' | ' +
+ '' + e.instrumentName + ' | ' +
+ /*'' + e.symbol + '' + ' | ' +*/
+ /*'' + e.currency + ' | ' +*/
+ '' + formatAmount(e.prePandemicHigh.Price, e.currency, 2) + ' | ' + //Pre-pandemic peak
+ '' + formatAmount(e.postPandemicLow.Price, e.currency, 2) + ' | ' + //Bottom
+ '' + e.fallPercent.toFixed(2) + '% | ' + //% Fall
+ '' + formatAmount(e.currentPrice, e.currency, 2) + ' | ' + //Current Price
+ '' + e.postPandemicDilution + '% | ' + //Diluted Value
+ '' + formatAmount(e.dilutedTarget, e.currency, 2) + ' | ' + //Diluted target
+ '' + e.currentDiscount.toFixed(2) + '% | ' + //Current Discount
+ '' + e.potentialDilutedRebound.toFixed(0) + '% | ' + //Potential Diluted Rebound
+ '' + e.retracementPercent.toFixed(1) + '% | ' + //Retracement %
+ '' + e.remainingPotential.toFixed(1) + '% | ' + //Remaining Potential
+ '' + e.remainingDilutedPotential.toFixed(1) + '% | ' + //Remaining Diluted Potential
+ '' + (e.currentHoldingsValueGBP > 0 ? formatAmount(e.currentHoldingsValueGBP, 'GBP', 2) : '') + ' | ' + //Current Holding
+ '' + (e.currentHoldingsValueGBP > 0 ? ((e.currentHoldingsValueGBP / totalHoldingsValueGBP) * 100).toFixed(2) + '%' : '') + ' | ' + //Current Allocation %
+ '' + (e.possibleRemainingProfit > 0 ? formatAmount(e.possibleRemainingProfit, 'GBP', 2) : '') + ' | ' + //Possible Remaining Profit
+ '' + i.CurrentPrice + ' ' + percentChange.toFixed(1) + '%'); + } else { + t.push('' + i.DisplayName + ' ' + i.CurrentPrice + ' +' + percentChange.toFixed(1) + '%'); + } + } else { + t.push('' + i.DisplayName + ' ' + i.CurrentPrice + ''); + } + } + } + if (t.length > 0) { + //t.push(new Date().yyyymmddhhmmss()); + let newContent = "
Search text: ' + + ''; + $("#modalContentDiv").html(content); + + $("#modalDialog .close").click(function () { modal.style.display = "none"; }); + + window.onclick = function (event) { // When the user clicks anywhere outside of the modal, close it + if (event.target == modal) { + modal.style.display = "none"; + } + } + + //$("#modalDialog .ok-button").click(function () { modal.style.display = "none"; }); + + //Display the dialog + modal.style.display = "block"; +} +function searchForYahooInstrument(searchString) { + if (searchString.length > 1) { + $.ajax({ + type: "POST", + contentType: "application/json", + url: "SharePrices.aspx/SearchYahooFinanceShares", + dataType: "json", + data: JSON.stringify({ SearchString: searchString }), + success: function (response) { + let results = ''; + if (response.quotes) { + instrumentSearchResults = response.quotes; + results = '
Add Instrument ';
+ }
+ $("#addInstrumentSearchResults").html(results);
+ },
+ failure: function (response) { console.error("searchForYahooInstrument error: " + JSON.stringify(response)); }
+ });
+ }
+}
+function selectSearchResult(index) {
+ for (let x = 0; x < instrumentSearchResults.length; x++) {
+ if (x == index) {
+ $("#searchResult" + x.toString()).addClass('selectedSearchResult');
+ } else {
+ $("#searchResult" + x.toString()).removeClass('selectedSearchResult');
+ }
+ }
+ $("#addInstrumentOKButton").removeClass("ok-button-disabled").addClass("ok-button").unbind().click(function () { $("#modalDialog").css("display", "none"); addInstrument(index) });
+}
+function addInstrument(index) {
+ let i = instrumentSearchResults[index];
+ console.info("Add instrument: " + JSON.stringify(i));
+ $.ajax({
+ type: "POST",
+ contentType: "application/json",
+ url: "SharePrices.aspx/AddInstrument",
+ dataType: "json",
+ data: JSON.stringify({ Symbol: i.symbol, FullName: (i.longname ? i.longname : i.shortname) }),
+ success: function (response) {
+ if (response.Symbol) {
+ Instruments.Data.push(response);
+ let i = Instruments.Data[Instruments.Data.length - 1];
+ i.Holdings = [];
+ i.DailyData = [];
+ i.IntradayData = [];
+ let lastTR = $('#tblInstruments>tbody>tr').last();
+ let cls = (lastTR.hasClass('shareRow')) ? 'altShareRow' : 'shareRow';
+ let tr = '
' + + ' OK ';
+ $("#modalContentDiv").html(content);
+ $("#newHoldingDate").datepicker({ dateFormat: 'yy-mm-dd', onSelect: function () { ValidateNewHolding(symbol) }});
+ $("#modalDialog .close").click(function () { modal.style.display = "none"; });
+ window.onclick = function (event) { // When the user clicks anywhere outside of the modal, close it
+ if (event.target == modal) {
+ modal.style.display = "none";
+ }
+ }
+
+ $("#newHoldingPrice").bind('input', function () {
+ $("#newHoldingOKButton").removeClass("ok-button").addClass("ok-button-disabled").unbind();
+ try {
+ let noUnits = parseFloat($("#newHoldingNoUnits").val());
+ let price = parseFloat($("#newHoldingPrice").val());
+ $("#newHoldingTotalCost").val((noUnits * price).toFixed(2));
+ ValidateNewHolding(symbol);
+ } catch (err) { }
+ });
+ $("#newHoldingNoUnits").bind('input', function () {
+ $("#newHoldingOKButton").removeClass("ok-button").addClass("ok-button-disabled").unbind();
+ try {
+ let noUnits = parseFloat($("#newHoldingNoUnits").val());
+ let price = parseFloat($("#newHoldingPrice").val());
+ $("#newHoldingTotalCost").val((noUnits * price).toFixed(2));
+ ValidateNewHolding(symbol);
+ } catch (err) { }
+ });
+ $("#newHoldingTotalCost").bind('input', function () {
+ $("#newHoldingOKButton").removeClass("ok-button").addClass("ok-button-disabled").unbind();
+ try {
+ let noUnits = parseFloat($("#newHoldingNoUnits").val());
+ let totalCost = parseFloat($("#newHoldingTotalCost").val());
+ //$("#newHoldingPrice").val((totalCost / noUnits).toFixed(4));
+ $("#newHoldingPrice").val((totalCost / noUnits));
+ ValidateNewHolding(symbol);
+ } catch (err) { }
+ });
+ $("#newHoldingDate").bind('input', function () {
+ ValidateNewHolding(symbol);
+ });
+ $("#addHoldingTimeHour").bind('input', function () {
+ ValidateNewHolding(symbol);
+ });
+ $("#addHoldingTimeMinute").bind('input', function () {
+ ValidateNewHolding(symbol);
+ });
+ $("#addHoldingAccountName").bind('input', function () {
+ ValidateNewHolding(symbol);
+ });
+
+ //Display the dialog
+ modal.style.display = "block";
+
+ return false; //Prevent click event propogating to parent
+}
+function ValidateNewHolding(symbol) {
+ $("#newHoldingOKButton").removeClass("ok-button").addClass("ok-button-disabled").unbind();
+ try {
+ let accountName = $("#addHoldingAccountName").val();
+ let noUnits = parseFloat($("#newHoldingNoUnits").val());
+ let price = parseFloat($("#newHoldingPrice").val());
+ let purchaseDate = $("#newHoldingDate").val();
+ let purchaseHour = $("#addHoldingTimeHour").val();
+ let purchaseMinute = $("#addHoldingTimeMinute").val();
+
+ if (accountName != '' && accountName != 'Select account...' && noUnits > 0 && price > 0 && purchaseDate != '' && purchaseHour != '' && purchaseHour != 'Hour' && purchaseMinute != '' && purchaseMinute != 'Min') {
+ let purchaseDT = new Date(purchaseDate + ' ' + purchaseHour + ':' + purchaseMinute);
+ if (purchaseDT <= new Date()) {
+ $("#newHoldingOKButton").removeClass("ok-button-disabled").addClass("ok-button").unbind().bind('click', function () { $("#modalDialog").css("display", "none"); AddHolding(accountName, symbol, noUnits, price, purchaseDT) });
+ }
+ }
+ } catch (err) { }
+}
+function AddHolding (accountName, symbol, noUnits, purchasePricePerUnit, purchaseDate) {
+ console.info("AddHolding: " + accountName + ", " + symbol + ", " + noUnits.toString() + ", " + purchasePricePerUnit.toFixed(4) + ", " + new Date(purchaseDate).yyyymmdd());
+
+ $.ajax({
+ type: "POST",
+ contentType: "application/json",
+ url: "SharePrices.aspx/AddHolding",
+ dataType: "json",
+ data: JSON.stringify({ AccountName: accountName, Symbol: symbol, NoUnits: noUnits, PurchasePricePerUnit: purchasePricePerUnit, PurchaseDate: new Date(purchaseDate).getTime() }),
+ success: function (response) {
+ if (response.HoldingID) {
+ let i = Instruments.GetSymbol(symbol);
+ if (!i.Holdings) i.Holdings = [];
+ i.Holdings.push(response);
+ let tr = $("#shareRow" + i.DisplayOrder.toString());
+ tr.html(createSharesTableRow(i));
+ createLongChart(i);
+ createMidChart(i);
+ createShortChart(i);
+ createSingleDayChart(i);
+ } else {
+ console.error("addInstrument response error: " + JSON.stringify(response));
+ }
+ },
+ failure: function (response) { console.error("addInstrument AJAX error: " + JSON.stringify(response)); }
+ });
+}
+
+function showModalDialog_SoldHolding(holdingID) {
+ function GetTimeDropdowns() {
+ let dd = '';
+ dd += '';
+ return dd;
+ }
+ function GetCurrencyDropdown() {
+ let dd = '';
+ return dd;
+ }
+
+ let modal = document.getElementById("modalDialog");
+ $("#modalTitle").html("Sold Holding");
+
+ let o = Instruments.GetHolding(holdingID);
+ let h = o.holding;
+ let i = o.instrument;
+ let content = 'Symbol: ' + i.Symbol +
+ 'Name: ' + i.InstrumentName + + ' Account: ' + h.AccountName + '' + + ' Purchase Date: ' + new Date(h.PurchaseDate).yyyymmddhhmmss() + + ' Purchase Price: ' + formatAmount(h.PurchasePricePerUnit, i.Currency, 2) + + ' No Units: ' + formatAmount(h.NoUnits, '', 0) + + ' Cost: ' + formatAmount(h.NoUnits * h.PurchasePricePerUnit, i.Currency, 2) + + ' Mark holding as sold?' + + ' Sold date: ' + GetTimeDropdowns() + + ' Currency: ' + GetCurrencyDropdown() + ' Proceeds: ' + + ' OK'; + $("#modalContentDiv").html(content); + + $("#soldHoldingDate").datepicker({ dateFormat: 'yy-mm-dd', onSelect: function () { ValidateSoldHolding(h) } }); + $("#soldHoldingDate").bind('input', function () { ValidateSoldHolding(h); }); + $("#soldHoldingTimeHour").bind('input', function () { ValidateSoldHolding(h); }); + $("#soldHoldingTimeMinute").bind('input', function () { ValidateSoldHolding(h); }); + $("#soldHoldingCurrency").bind('input', function () { ValidateSoldHolding(h); }); + $("#soldHoldingProceeds").bind('input', function () { ValidateSoldHolding(h); }); + + $("#modalDialog .close").click(function () { modal.style.display = "none"; }); + + window.onclick = function (event) { // When the user clicks anywhere outside of the modal, close it + if (event.target == modal) { + modal.style.display = "none"; + } + } + + //$("#modalDialog .ok-button").click(function () { modal.style.display = "none"; deleteHolding(holdingID); }); + + //Display the dialog + modal.style.display = "block"; +} +function ValidateSoldHolding(holding) { + $("#soldHoldingOKButton").removeClass("ok-button").addClass("ok-button-disabled").unbind(); + try { + let holdingID = holding.HoldingID; + let purchaseDT = holding.PurchaseDate; + let soldDate = $("#soldHoldingDate").val(); + let soldHour = $("#soldHoldingTimeHour").val(); + let soldMinute = $("#soldHoldingTimeMinute").val(); + let accountName = $("#soldHoldingsAccountName").val(); + let soldCurrency = $("#soldHoldingCurrency").val(); + let soldProceeds = parseFloat($("#soldHoldingProceeds").val()); + + if (soldDate != '' && soldHour != '' && soldHour != 'Hour' && soldMinute != '' && soldMinute != 'Min' && soldCurrency != 'Currency' && soldProceeds > 0) { + let soldDT = new Date(soldDate + ' ' + soldHour + ':' + soldMinute).getTime(); + if (soldDT <= new Date().getTime() && soldDT > purchaseDT) { + $("#soldHoldingOKButton").removeClass("ok-button-disabled").addClass("ok-button").unbind().bind('click', function () { $("#modalDialog").css("display", "none"); soldHolding(holdingID, soldDT, accountName, soldCurrency, soldProceeds) }); + } + } + } catch (err) { } +} +function soldHolding(holdingID, soldDate, accountName, soldCurrency, soldProceeds) { + //console.info("Sold holding: " + holdingID.toString()); + + let o = Instruments.GetHolding(holdingID); + let h = o.holding; + let i = o.instrument; + + h.SoldDate = soldDate; + + $.ajax({ + type: "POST", + contentType: "application/json", + url: "SharePrices.aspx/SoldHolding", + dataType: "json", + data: JSON.stringify({ HoldingID: holdingID, SoldDate: soldDate, SoldCurrency: soldCurrency, SoldProceeds: soldProceeds }), + success: function (response) { + $("#shareRow" + i.DisplayOrder).html(createSharesTableRow(i)); + createLongChart(i); + createMidChart(i); + createShortChart(i); + createSingleDayChart(i); + }, + failure: function (response) { console.error("SoldHolding error: " + JSON.stringify(response)); } + }); + + let balance = soldProceeds; + let curr = Instruments.GetAccountCurrencyHolding(accountName, Instruments.GetSymbol(soldCurrency)); + if (curr) { + balance += curr.NoUnits; + } + + $.ajax({ + type: "POST", + contentType: "application/json", + url: "SharePrices.aspx/SetAccountCashValue", + dataType: "json", + data: JSON.stringify({ AccountName: accountName, Currency: soldCurrency, Value: balance }), + success: function (response) { + console.info("SetAccountCashValue success: " + JSON.stringify(response)); + + let c = Instruments.GetSymbol(response.Symbol); + + let ch = Instruments.GetAccountCurrencyHolding(response.AccountName, c); + if (ch) { + ch.NoUnits = response.NoUnits; + } else { + c.Holdings.push(response); + } + }, + failure: function (response) { console.error("SetAccountCashValue error: " + JSON.stringify(response)); } + }); +} + +function showModalDialog_FullScreenChart(Symbol) { + let i = Instruments.GetSymbol(Symbol); + + let modal = document.getElementById("modalChart"); + + $("#modalChartTitle").html("Detailed Chart: " + i.InstrumentName + " (" + i.Symbol + ")"); + + $("#modalChart .close").click(function () { modal.style.display = "none"; }); + $('#spnCompareInstruments').click(function () { compareInstruments(i) }); + + window.onclick = function (event) { // When the user clicks anywhere outside of the modal, close it + if (event.target == modal) { + modal.style.display = "none"; + } + } + + //$("#modalDialog .ok-button").click(function () { modal.style.display = "none"; }); + + $(document).on('keypress', function (event) { console.info("Keypress: " + event.keyCode.toString()); if (event.keyCode == 27 /*Escape key*/) modal.style.display = "none"; }); + + //Display the dialog + modal.style.display = "block"; + + let chartDiv = $("#divFullScreenChart"); + let height = chartDiv.parent().height(); + let width = chartDiv.parent().width(); + chartDiv.height(height).width(width); + + let startDate = i.DailyData[0].DT; + let endDate = new Date().getTime(); + createFullScreenChart(i, startDate, endDate); + //createFullScreenComparisonChart(i, Instruments.GetSymbol('BTC-USD'), startDate, endDate); +} +function compareInstruments(Instrument1) { + function GetAccountsDropdown() { + let dd = ''; + return dd; + } + + let modal = document.getElementById("modalDialog"); + $("#modalTitle").html("Compare Instruments"); + + let content = ' Select instrument to compare against ' + GetAccountsDropdown() + ' ' + + ' OK ';
+ $("#modalContentDiv").html(content);
+ $("#modalDialog .close").click(function () { modal.style.display = "none"; });
+ window.onclick = function (event) { // When the user clicks anywhere outside of the modal, close it
+ if (event.target == modal) {
+ modal.style.display = "none";
+ }
+ }
+
+ $("#selectInstrument2").bind('input', function () {
+ $("#compareInstrumentsOKButton").removeClass("ok-button").addClass("ok-button-disabled").unbind();
+ let instrument2Symbol = $("#selectInstrument2").val();
+ if (instrument2Symbol != '' && instrument2Symbol != 'Select 2nd instrument...') {
+ $("#compareInstrumentsOKButton").removeClass("ok-button-disabled").addClass("ok-button").bind('click', function () {
+ $("#modalDialog").css("display", "none");
+ let Instrument2 = Instruments.GetSymbol(instrument2Symbol);
+ let startDate = Instrument1.DailyData[0].DT > Instrument2.DailyData[0].DT ? Instrument1.DailyData[0].DT : Instrument2.DailyData[0].DT;
+ let endDate = new Date().getTime();
+ createFullScreenComparisonChart(Instrument1, Instrument2, startDate, endDate);
+ });
+ }
+ });
+
+ //Display the dialog
+ modal.style.display = "block";
+}
+
+function refreshHistoryChart() {
+ let chartContainer = $('#histDiv');
+
+ $.ajax({
+ type: "POST",
+ contentType: "application/json",
+ url: "SharePrices.aspx/GetTotalHoldingsHistory",
+ dataType: "json",
+ success: function (response) {
+ let seriesData = [];
+
+ for (let i = 0; i < response.length; i++) {
+ let d = response[i];
+ seriesData.push([d.HistoryDate, d.Open, d.High, d.Low, d.Close]);
+ }
+
+ chartContainer.html(response);
+
+ /*
+ Highcharts.stockChart('histDiv', {
+ title: {
+ text: 'Total Holdings History'
+ },
+
+ series: [{
+ type: 'ohlc',
+ name: 'Total Holdings',
+ data: seriesData,
+ lineWidth: 4
+ }],
+
+ plotOptions: {
+ ohlc: {
+ color: 'red',
+ upColor: 'green'
+ }
+ }
+
+ });
+ */
+
+ Highcharts.stockChart('histDiv', {
+ chart: {
+ backgroundColor: 'rgba(0,0,0,0)'/*,
+ spacingTop: 5,
+ spacingBottom: 0,
+ spacingLeft: 2,
+ spacingRight: 0*/
+ },
+
+ title: {
+ text: 'Total Holdings History'
+ },
+
+ series: [{
+ type: 'candlestick',
+ name: 'Total Holdings',
+ color: '#ff4141',
+ lineColor: '#ff4141',
+ upColor: '#07b200',
+ upLineColor: '#07b200',
+ data: seriesData/*,
+ lineWidth: 4*/
+ }],
+
+ plotOptions: {
+ ohlc: {
+ color: '#ff4141',
+ upColor: '#07b200'
+ }
+ }
+
+ });
+
+ },
+ failure: function (response) { console.error("GetTotalHoldingsHistory error: " + response.d) }
+ });
+
+}
+
+/*function createIndexMarquee() {
+ let t = [];
+ for (var ix = 0; ix < Instruments.Data.length; ix++) {
+ let i = Instruments.Data[ix];
+ //if (i.InstrumentType == 'INDEX' || i.InstrumentType == 'FUTURE') {
+ if (i.ShowInMarquee == 'A') {
+ let openOrClosed = Instruments.MarketIsOpen(i) == 1 ? 'open' : 'closed';
+ if (i.SingleDayPreviousClose) {
+ let percentChange = ((i.CurrentPrice / i.SingleDayPreviousClose) - 1) * 100;
+ if (percentChange < 0) {
+ t.push('' + i.DisplayName + ': ' + i.CurrentPrice + ' ' + percentChange.toFixed(1) + '%');
+ } else {
+ t.push('' + i.DisplayName + ': ' + i.CurrentPrice + ' +' + percentChange.toFixed(1) + '%');
+ }
+ } else {
+ t.push('' + i.DisplayName + ': ' + i.CurrentPrice + '');
+ }
+ }
+ }
+ if (t.length > 0) {
+ t.push(new Date().yyyymmddhhmmss());
+ let newContent = t.join(" ");
+ indexMarquee.setContent(newContent);
+ }
+}*/
+
+/*
function zzz_createHoldingsTable() {
function getHoldings() {
function aggregateHoldings(i) {
@@ -2602,9 +4215,8 @@ function zzz_createHoldingsTable() {
let aggCurrentValueGBP = (aggUnits * i.CurrentPrice) / exchangeRate;
let aggGainGBP = ((i.CurrentPrice - aggPurchasePricePerUnit) * aggUnits) / exchangeRate;
- /*
- let aggSingleDayGainGBP = ((i.CurrentPrice - i.SingleDayPreviousClose) * aggUnits) / exchangeRate;
- */
+ //let aggSingleDayGainGBP = ((i.CurrentPrice - i.SingleDayPreviousClose) * aggUnits) / exchangeRate;
+
let aggSingleDayGainPercent = ((i.CurrentPrice / i.SingleDayPreviousClose) - 1) * 100;
//let aggSingleDayProfitOrLoss = i.CurrentPrice < i.SingleDayPreviousClose ? 'loss' : 'profit';
let aggSingleDayProfitOrLoss = aggSingleDayGainGBP < 0 ? 'loss' : 'profit';
@@ -2896,7 +4508,7 @@ function zzz_createHoldingsTable() {
//Add the totals line at the bottom of the table
let profitOrLoss = totalValueGBP < totalBookCostGBP ? 'loss' : 'profit';
let todaysProfitOrLoss = todaysGainGBP < 0 ? 'loss' : 'profit';
- let row = 'Updated: ' + new Date().yyyymmddhhmmss() + ' | ' +
+ let row = ' | Updated: ' + new Date().yyyymmddhhmmss() + (lastSuccessfulFetch ? ' - Last Successful Fetch: ' + lastSuccessfulFetch.yyyymmddhhmmss() : '') + ' | ' +
' | ' + formatAmount(totalBookCostGBP, 'GBP', 2) + ' | ' +
'' +
' | ' + formatAmount(totalGainGBP, 'GBP', 2) + ' | ' +
@@ -2956,1249 +4568,4 @@ function zzz_createHoldingsTable() {
$.plot('#divAccountValue', cd, ops);
}
}
-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) {
- 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,
- 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') });
- }
- 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 = [];
-
- for (let n = 0; n < Instruments.Data.length; n++) {
- let i = Instruments.Data[n];
- //if (i.InstrumentType == 'EQUITY') {
- if (i.InstrumentType == 'EQUITY' || i.InstrumentType == 'CRYPTOCURRENCY') {
- //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));
- 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 {
- let marketIsOpen = Instruments.MarketIsOpen(i);
- let exchangeRate = Instruments.GetExchangeRate(i.Currency, 'GBP');
-
- for (let hn = 0; hn < i.Holdings.length; hn++) {
- let h = i.Holdings[hn];
- if (h.SoldDate == null) {
- 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,
- 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 += holdingBookCostGBP;
- totalValueGBP += holdingCurrentValueGBP;
- totalGainGBP += holdingGainGBP;
- todaysGainGBP += (marketIsOpen == 0 ? 0 : singleDayGainGBP);
- }
- }
- }
- }
- }
- //} //if (i.CurrentPrice)
- }
- }
-
- return {
- totalBookCostGBP: totalBookCostGBP,
- totalValueGBP: totalValueGBP,
- totalGainGBP: totalGainGBP,
- todaysGainGBP: todaysGainGBP,
- maxCurrentValueGBP: maxCurrentValueGBP,
- holdings: r,
- holdingCurrencies: holdingCurrencies,
- holdingCurrenciesCurrentValue: holdingCurrenciesCurrentValue,
- bookCostPerAccount: bookCostPerAccount,
- currentValuePerAccount: currentValuePerAccount
- };
- }
- function createTable() {
- let t = $("#tblMainHoldings");
- if (t.length) {
- return;
- } else {
- let tbl = '
' +
- ' | ';
- }
-
- 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 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 = '' + labelText + ' ' +
- '' + type + ' | ' +
- '' + h.instrumentName + ' | ' +
- '' + h.symbol + '' + ' | ' +
- '' + h.currency + ' | ' +
- '' + h.accountName + ' | ' +
- '' + new Date(h.purchaseDateMin).yyyymmdd()) : (' title="' + new Date(h.purchaseDateMax).yyyymmdd() + ' - ' + new Date(h.purchaseDateMin).yyyymmdd() + '">' + new Date(h.purchaseDateMin).yyyymmdd()) + '+') + ' | ' +
- //'' + formatAmount(h.noUnits, '', 0) + ' | ' +
- '' + h.noUnits.autoScale() + ' | ' +
- '' + formatAmount(h.purchasePricePerUnit, h.currency, 2) + ' | ' + //Buy Price
- '' + (h.currentPrice ? formatAmount(h.currentPrice, h.currency, 2) : '') + ' | ' + //Current Price
- '' + formatAmount(h.currency == 'GBp' ? h.bookCost / 100 : h.bookCost, h.currency == 'GBp' ? 'GBP' : h.currency, 0) + ' | ' + //Book Cost
- '' + (h.currentExchangeRate ? formatAmount(h.currentValue, h.currency == 'GBp' ? 'GBP' : h.currency, 0) : '') + ' | ' + //Current Value
- '' + (h.currentPrice ? formatAmount(h.gain, h.currency == 'GBp' ? 'GBP' : h.currency, 0) : '') + ' | ' + //Gain
- '' + (h.currentExchangeRate ? formatAmount(h.gainGBP, 'GBP', 0) : '') + ' | ' + //Gain £
- '' + (h.currentExchangeRate ? h.gainPercent.toFixed(1) + '%' : '') + ' | ' + //Gain %
- '' + (h.currentExchangeRate ? formatAmount(h.currentValueGBP, 'GBP', 0) : '') + ' | ' + //Current Value £
- (h.currentExchangeRate ? getPercentCell('#3f2d95', 70, scaledAllocation, allocationPercent.toFixed(1) + '%') : '') + //Allocation
- (h.currentExchangeRate ? (h.marketIsOpen == 0 ? '' : ' | ' + formatAmount(h.singleDayGainGBP, 'GBP', 0) + ' | ') : '') + //Today's Gain £
- (h.singleDayGainPercent ? (h.marketIsOpen == 0 ? '' : ' | ' + h.singleDayGainPercent.toFixed(1) + '% | ') : '') + //Today's Gain %
- ' | Updated: ' + new Date().yyyymmddhhmmss() + ' | ' +
- ' | ' + formatAmount(totalBookCostGBP, 'GBP', 0) + ' | ' + //Book Cost
- '' +
- ' | ' + formatAmount(totalGainGBP, 'GBP', 0) + ' | ' + //Gain GBP
- '' + formatAmount((((totalValueGBP / totalBookCostGBP) - 1) * 100), '', 1) + '% | ' + //Gain %
- '' + formatAmount(totalValueGBP, 'GBP', 0) + ' | ' + //Current Value
- ' | ' + //Allocation
- '' + formatAmount(todaysGainGBP, 'GBP', 0) + ' | ' + //Today's Gain GBP
- '' + formatAmount(((todaysGainGBP / totalBookCostGBP) * 100), '', 1) + '% | ' + //Today's Gain %
- '" + label + " ";
- }
- 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 "" + Math.round(series.percent) + "% " + formatAmount(series.data[0][1], 'GBP', 0) + " " + label + " ";
- }
- ops.series.pie.label.formatter = labelFormatter2;
- cd = bookCostPerAccount.getChartData();
- if (cd.length) {
- $.plot('#divAccountBookCost', cd, ops);
- }
-
- cd = currentValuePerAccount.getChartData();
- if (cd.length) {
- $.plot('#divAccountValue', cd, ops);
- }
-}
-function createAccountsTables() {
- function getAccounts() {
- let accounts = [];
- function indexOfAccount(accountName) { return accounts.map(function (item) { return item.accountName; }).indexOf(accountName); };
- function getAccount(accountName) { return accounts[indexOfAccount(accountName)]; };
- function addAccountHolding(instrument, exchangeRate, holding) {
- let a = getAccount(holding.AccountName);
- if (!a) {
- a = { accountName: holding.AccountName, holdings: [], totalCostGBP: 0, totalValueGBP: 0, totalGainGBP: 0 };
- accounts.push(a);
- }
- let h = {
- instrumentName: instrument.InstrumentName,
- symbol: instrument.Symbol,
- currency: instrument.Currency,
- purchaseDate: holding.PurchaseDate,
- purchasePricePerUnit: holding.PurchasePricePerUnit,
- noUnits: holding.NoUnits,
- cost: holding.NoUnits * holding.PurchasePricePerUnit
- }
- //if (exchangeRate) {
- //h.costGBP = h.cost / exchangeRate;
- h.costGBP = holding.BookCostGBP;
- a.totalCostGBP += holding.BookCostGBP;
- //}
- if (instrument.CurrentPrice) {
- h.currentPrice = instrument.CurrentPrice;
- h.currentValue = holding.NoUnits * instrument.CurrentPrice;
- h.gain = h.currentValue - h.cost;
- if (exchangeRate) {
- h.currentValueGBP = h.currentValue / exchangeRate;
- //h.gainGBP = h.gain / exchangeRate;
- h.gainGBP = h.currentValueGBP - h.costGBP;
- a.totalGainGBP += h.gainGBP;
- a.totalValueGBP += h.currentValueGBP;
- }
- }
- a.holdings.push(h);
- }
-
- for (let ix = 0; ix < Instruments.Data.length; ix++) {
- let i = Instruments.Data[ix];
- let exchangegRate = Instruments.GetExchangeRate(i.Currency, 'GBP');
- for (let hx = 0; hx < i.Holdings.length; hx++) {
- let h = i.Holdings[hx];
- if (h.SoldDate == null) {
- addAccountHolding(i, exchangegRate, h);
- }
- }
- }
-
- return accounts;
- }
-
- let accounts = getAccounts();
-
- //let eDT = new Date();
- //console.info('createAccountsTable after getAccounts: ' + (eDT.getTime() - sDT.getTime()) + ' (' + sDT.yyyymmddhhmmss() + ' -> ' + eDT.yyyymmddhhmmss() + ')');
-
- //Sort the accounts array by accountName
- accounts.sort(function (a, b) {
- if (a.accountName < b.accountName) return -1;
- if (a.accountName > b.accountName) return 1;
- return 0;
- });
-
- //eDT = new Date();
- //console.info('createAccountsTable after array sort: ' + (eDT.getTime() - sDT.getTime()) + ' (' + sDT.yyyymmddhhmmss() + ' -> ' + eDT.yyyymmddhhmmss() + ')');
-
- let summaryTable = "" + formatAmount(series.data[0][1], 'GBP', 0) + "
' + accountsTables); -} -function createAnalysisTable() { - function getHighestPriceBeforeDate(Instrument, maxDT) { - if (Instrument.DailyData) { - let r = { Price: 0, DT: 0 }; - let dt = 0; - for (let i = 0; i < Instrument.DailyData.length && dt < maxDT; i++) { - let price = Instrument.DailyData[i]; - dt = price.DT; - if (dt <= maxDT && price.high > r.Price) { - r.Price = price.high; - r.DT = dt; - } - } - return r; - } else { - return { Price: NaN, DT: NaN }; - } - } - function getHighestPriceBetweenDates(Instrument, minDT, maxDT) { - if (Instrument.DailyData) { - let r = { Price: 0, DT: 0 }; - let dt = 0; - for (let i = 0; i < Instrument.DailyData.length && dt < maxDT; i++) { - let price = Instrument.DailyData[i]; - dt = price.DT; - if (dt >= minDT && dt <= maxDT && price.high > r.Price) { - r.Price = price.high; - r.DT = dt; - } - } - return r; - } else { - return { Price: NaN, DT: NaN }; - } - } - function getLowestPriceAfterDate(Instrument, minDT) { - if (Instrument.DailyData) { - let r = { Price: 0, DT: 0 }; - let dt = 0; - let i = 0; - let DD = Instrument.DailyData; - while (dt < minDT && i < DD.length - 1) { - i++; - dt = DD[i].DT; - } - if (i < DD.length) { - r.Price = DD[i].low; - r.DT = DD[i].DT; - - while (i < DD.length - 1) { - i++; - if (DD[i].low < r.Price) { - r.Price = DD[i].low; - r.DT = DD[i].DT; - } - } - } - return r; - } else { - return { Price: NaN, DT: NaN }; - } - } - function getEquities() { - function getHoldingsValueGBP(Instrument) { - let noUnits = 0; - if (Instrument.CurrentPrice) { - let c = Instruments.GetCurrentHoldings(Instrument); - for (let x = 0; x < c.length; x++) { - noUnits += c[x].NoUnits; - } - let exchangeRate = Instruments.GetExchangeRate(Instrument.Currency, 'GBP'); - return noUnits * Instrument.CurrentPrice / exchangeRate; - } else { - return 0; - } - } - - let totalHoldingsValueGBP = 0; - let r = []; - for (let n = 0; n < Instruments.Data.length; n++) { - let i = Instruments.Data[n]; - try { - if (i.InstrumentType == 'EQUITY') { - let prePandemicHigh = getHighestPriceBetweenDates(i, new Date('2020-01-01'), pandemicCrashStartDate); - let postPandemicLow = getLowestPriceAfterDate(i, pandemicCrashStartDate); - let dilutedTarget = prePandemicHigh.Price * i.PostPandemicDilution; - let currentHoldingsValueGBP = getHoldingsValueGBP(i); - let remainingDilutedPotential = ((dilutedTarget - i.CurrentPrice) / i.CurrentPrice) * 100; - totalHoldingsValueGBP += currentHoldingsValueGBP; - - r.push({ - prePandemicHigh: prePandemicHigh, - postPandemicLow: postPandemicLow, - displayOrder: i.DisplayOrder, - instrumentName: i.InstrumentName, - symbol: i.Symbol, - currency: i.Currency, - currentPrice: i.CurrentPrice, - fallPercent: ((postPandemicLow.Price / prePandemicHigh.Price) - 1) * 100, - postPandemicDilution: i.PostPandemicDilution * 100, - dilutedTarget: dilutedTarget, - currentDiscount: ((i.CurrentPrice / prePandemicHigh.Price) - 1) * 100, - potentialDilutedRebound: (((prePandemicHigh.Price * i.PostPandemicDilution) / postPandemicLow.Price) - 1) * 100, - retracementPercent: ((i.CurrentPrice - postPandemicLow.Price) / (prePandemicHigh.Price - postPandemicLow.Price)) * 100, - remainingPotential: ((prePandemicHigh.Price - i.CurrentPrice) / i.CurrentPrice) * 100, - remainingDilutedPotential: remainingDilutedPotential, - currentHoldingsValueGBP: currentHoldingsValueGBP, - possibleRemainingProfit: currentHoldingsValueGBP * remainingDilutedPotential / 100 - }); - } - } - catch (err) { - console.error("Error analysing " + i.Symbol + ": " + err.message); - } - } - return { totalHoldingsValueGBP: totalHoldingsValueGBP, equities: r }; - } - function createTable() { - let t = $("#tblAnalysis"); - if (t.length) { - return t; - } else { - let tbl = '
' + e.displayOrder + ' | ' +
- '' + e.instrumentName + ' | ' +
- /*'' + e.symbol + '' + ' | ' +*/
- /*'' + e.currency + ' | ' +*/
- '' + formatAmount(e.prePandemicHigh.Price, e.currency, 2) + ' | ' + //Pre-pandemic peak
- '' + formatAmount(e.postPandemicLow.Price, e.currency, 2) + ' | ' + //Bottom
- '' + e.fallPercent.toFixed(2) + '% | ' + //% Fall
- '' + formatAmount(e.currentPrice, e.currency, 2) + ' | ' + //Current Price
- '' + e.postPandemicDilution + '% | ' + //Diluted Value
- '' + formatAmount(e.dilutedTarget, e.currency, 2) + ' | ' + //Diluted target
- '' + e.currentDiscount.toFixed(2) + '% | ' + //Current Discount
- '' + e.potentialDilutedRebound.toFixed(0) + '% | ' + //Potential Diluted Rebound
- '' + e.retracementPercent.toFixed(1) + '% | ' + //Retracement %
- '' + e.remainingPotential.toFixed(1) + '% | ' + //Remaining Potential
- '' + e.remainingDilutedPotential.toFixed(1) + '% | ' + //Remaining Diluted Potential
- '' + (e.currentHoldingsValueGBP > 0 ? formatAmount(e.currentHoldingsValueGBP, 'GBP', 2) : '') + ' | ' + //Current Holding
- '' + (e.currentHoldingsValueGBP > 0 ? ((e.currentHoldingsValueGBP / totalHoldingsValueGBP) * 100).toFixed(2) + '%' : '') + ' | ' + //Current Allocation %
- '' + (e.possibleRemainingProfit > 0 ? formatAmount(e.possibleRemainingProfit, 'GBP', 2) : '') + ' | ' + //Possible Remaining Profit
- '' + i.CurrentPrice + ' ' + percentChange.toFixed(1) + '%'); - } else { - t.push('' + i.DisplayName + ' ' + i.CurrentPrice + ' +' + percentChange.toFixed(1) + '%'); - } - } else { - t.push('' + i.DisplayName + ' ' + i.CurrentPrice + ''); - } - } - } - if (t.length > 0) { - //t.push(new Date().yyyymmddhhmmss()); - let newContent = "
Name: ' + i.InstrumentName + - ' Account: ' + h.AccountName + - ' Purchase Date: ' + new Date(h.PurchaseDate).yyyymmddhhmmss() + - ' Purchase Price: ' + formatAmount(h.PurchasePricePerUnit, i.Currency, 2) + - ' No Units: ' + formatAmount(h.NoUnits, '', 0) + - ' Cost: ' + formatAmount(h.NoUnits * h.PurchasePricePerUnit, i.Currency, 2) + - ' Mark holding as sold?' + - ' Sold date: ' + GetTimeDropdowns() + - ' OK'; - $("#modalContentDiv").html(content); - - $("#soldHoldingDate").datepicker({ dateFormat: 'yy-mm-dd', onSelect: function () { ValidateSoldHolding(h) } }); - $("#soldHoldingDate").bind('input', function () { - ValidateSoldHolding(h); - }); - $("#soldHoldingTimeHour").bind('input', function () { - ValidateSoldHolding(h); - }); - $("#soldHoldingTimeMinute").bind('input', function () { - ValidateSoldHolding(h); - }); - - $("#modalDialog .close").click(function () { modal.style.display = "none"; }); - - window.onclick = function (event) { // When the user clicks anywhere outside of the modal, close it - if (event.target == modal) { - modal.style.display = "none"; - } - } - - //$("#modalDialog .ok-button").click(function () { modal.style.display = "none"; deleteHolding(holdingID); }); - - //Display the dialog - modal.style.display = "block"; -} -function ValidateSoldHolding(holding) { - $("#soldHoldingOKButton").removeClass("ok-button").addClass("ok-button-disabled").unbind(); - try { - let holdingID = holding.HoldingID; - let purchaseDT = holding.PurchaseDate; - let soldDate = $("#soldHoldingDate").val(); - let soldHour = $("#soldHoldingTimeHour").val(); - let soldMinute = $("#soldHoldingTimeMinute").val(); - - if (soldDate != '' && soldHour != '' && soldHour != 'Hour' && soldMinute != '' && soldMinute != 'Min') { - let soldDT = new Date(soldDate + ' ' + soldHour + ':' + soldMinute).getTime(); - if (soldDT <= new Date().getTime() && soldDT > purchaseDT) { - $("#soldHoldingOKButton").removeClass("ok-button-disabled").addClass("ok-button").unbind().bind('click', function () { $("#modalDialog").css("display", "none"); soldHolding(holdingID, soldDT) }); - } - } - } catch (err) { } -} -function soldHolding(holdingID, soldDate) { - //console.info("Sold holding: " + holdingID.toString()); - - let o = Instruments.GetHolding(holdingID); - let h = o.holding; - let i = o.instrument; - - h.SoldDate = soldDate; - - $.ajax({ - type: "POST", - contentType: "application/json", - url: "SharePrices.aspx/SoldHolding", - dataType: "json", - data: JSON.stringify({ HoldingID: holdingID, SoldDate: soldDate }), - success: function (response) { - $("#shareRow" + i.DisplayOrder).html(createSharesTableRow(i)); - createLongChart(i); - createMidChart(i); - createShortChart(i); - createSingleDayChart(i); - }, - failure: function (response) { console.error("SoldHolding error: " + JSON.stringify(response)); } - }); -} - -function showModalDialog_AddInstrument() { - let modal = document.getElementById("modalDialog"); - - $("#modalTitle").html("Add Instrument"); - - let content = ' Search text: ' + - ''; - $("#modalContentDiv").html(content); - - $("#modalDialog .close").click(function () { modal.style.display = "none"; }); - - window.onclick = function (event) { // When the user clicks anywhere outside of the modal, close it - if (event.target == modal) { - modal.style.display = "none"; - } - } - - //$("#modalDialog .ok-button").click(function () { modal.style.display = "none"; }); - - //Display the dialog - modal.style.display = "block"; -} -function searchForYahooInstrument(searchString) { - if (searchString.length > 1) { - $.ajax({ - type: "POST", - contentType: "application/json", - url: "SharePrices.aspx/SearchYahooFinanceShares", - dataType: "json", - data: JSON.stringify({ SearchString: searchString }), - success: function (response) { - let results = ''; - if (response.quotes) { - instrumentSearchResults = response.quotes; - results = '
Add Instrument ';
- }
- $("#addInstrumentSearchResults").html(results);
- },
- failure: function (response) { console.error("searchForYahooInstrument error: " + JSON.stringify(response)); }
- });
- }
-}
-function selectSearchResult(index) {
- for (let x = 0; x < instrumentSearchResults.length; x++) {
- if (x == index) {
- $("#searchResult" + x.toString()).addClass('selectedSearchResult');
- } else {
- $("#searchResult" + x.toString()).removeClass('selectedSearchResult');
- }
- }
- $("#addInstrumentOKButton").removeClass("ok-button-disabled").addClass("ok-button").unbind().click(function () { $("#modalDialog").css("display", "none"); addInstrument(index) });
-}
-function addInstrument(index) {
- let i = instrumentSearchResults[index];
- console.info("Add instrument: " + JSON.stringify(i));
- $.ajax({
- type: "POST",
- contentType: "application/json",
- url: "SharePrices.aspx/AddInstrument",
- dataType: "json",
- data: JSON.stringify({ Symbol: i.symbol, FullName: (i.longname ? i.longname : i.shortname) }),
- success: function (response) {
- if (response.Symbol) {
- Instruments.Data.push(response);
- let i = Instruments.Data[Instruments.Data.length - 1];
- i.Holdings = [];
- i.DailyData = [];
- i.IntradayData = [];
- let lastTR = $('#tblInstruments>tbody>tr').last();
- let cls = (lastTR.hasClass('shareRow')) ? 'altShareRow' : 'shareRow';
- let tr = '
' + - ' OK ';
- $("#modalContentDiv").html(content);
- $("#newHoldingDate").datepicker({ dateFormat: 'yy-mm-dd', onSelect: function () { ValidateNewHolding(symbol) }});
- $("#modalDialog .close").click(function () { modal.style.display = "none"; });
- window.onclick = function (event) { // When the user clicks anywhere outside of the modal, close it
- if (event.target == modal) {
- modal.style.display = "none";
- }
- }
-
- $("#newHoldingPrice").bind('input', function () {
- $("#newHoldingOKButton").removeClass("ok-button").addClass("ok-button-disabled").unbind();
- try {
- let noUnits = parseFloat($("#newHoldingNoUnits").val());
- let price = parseFloat($("#newHoldingPrice").val());
- $("#newHoldingTotalCost").val((noUnits * price).toFixed(2));
- ValidateNewHolding(symbol);
- } catch (err) { }
- });
- $("#newHoldingNoUnits").bind('input', function () {
- $("#newHoldingOKButton").removeClass("ok-button").addClass("ok-button-disabled").unbind();
- try {
- let noUnits = parseFloat($("#newHoldingNoUnits").val());
- let price = parseFloat($("#newHoldingPrice").val());
- $("#newHoldingTotalCost").val((noUnits * price).toFixed(2));
- ValidateNewHolding(symbol);
- } catch (err) { }
- });
- $("#newHoldingTotalCost").bind('input', function () {
- $("#newHoldingOKButton").removeClass("ok-button").addClass("ok-button-disabled").unbind();
- try {
- let noUnits = parseFloat($("#newHoldingNoUnits").val());
- let totalCost = parseFloat($("#newHoldingTotalCost").val());
- //$("#newHoldingPrice").val((totalCost / noUnits).toFixed(4));
- $("#newHoldingPrice").val((totalCost / noUnits));
- ValidateNewHolding(symbol);
- } catch (err) { }
- });
- $("#newHoldingDate").bind('input', function () {
- ValidateNewHolding(symbol);
- });
- $("#addHoldingTimeHour").bind('input', function () {
- ValidateNewHolding(symbol);
- });
- $("#addHoldingTimeMinute").bind('input', function () {
- ValidateNewHolding(symbol);
- });
- $("#addHoldingAccountName").bind('input', function () {
- ValidateNewHolding(symbol);
- });
-
- //Display the dialog
- modal.style.display = "block";
-
- return false; //Prevent click event propogating to parent
-}
-function ValidateNewHolding(symbol) {
- $("#newHoldingOKButton").removeClass("ok-button").addClass("ok-button-disabled").unbind();
- try {
- let accountName = $("#addHoldingAccountName").val();
- let noUnits = parseFloat($("#newHoldingNoUnits").val());
- let price = parseFloat($("#newHoldingPrice").val());
- let purchaseDate = $("#newHoldingDate").val();
- let purchaseHour = $("#addHoldingTimeHour").val();
- let purchaseMinute = $("#addHoldingTimeMinute").val();
-
- if (accountName != '' && accountName != 'Select account...' && noUnits > 0 && price > 0 && purchaseDate != '' && purchaseHour != '' && purchaseHour != 'Hour' && purchaseMinute != '' && purchaseMinute != 'Min') {
- let purchaseDT = new Date(purchaseDate + ' ' + purchaseHour + ':' + purchaseMinute);
- if (purchaseDT <= new Date()) {
- $("#newHoldingOKButton").removeClass("ok-button-disabled").addClass("ok-button").unbind().bind('click', function () { $("#modalDialog").css("display", "none"); AddHolding(accountName, symbol, noUnits, price, purchaseDT) });
- }
- }
- } catch (err) { }
-}
-function AddHolding (accountName, symbol, noUnits, purchasePricePerUnit, purchaseDate) {
- console.info("AddHolding: " + accountName + ", " + symbol + ", " + noUnits.toString() + ", " + purchasePricePerUnit.toFixed(4) + ", " + new Date(purchaseDate).yyyymmdd());
-
- $.ajax({
- type: "POST",
- contentType: "application/json",
- url: "SharePrices.aspx/AddHolding",
- dataType: "json",
- data: JSON.stringify({ AccountName: accountName, Symbol: symbol, NoUnits: noUnits, PurchasePricePerUnit: purchasePricePerUnit, PurchaseDate: new Date(purchaseDate).getTime() }),
- success: function (response) {
- if (response.HoldingID) {
- let i = Instruments.GetSymbol(symbol);
- if (!i.Holdings) i.Holdings = [];
- i.Holdings.push(response);
- let tr = $("#shareRow" + i.DisplayOrder.toString());
- tr.html(createSharesTableRow(i));
- createLongChart(i);
- createMidChart(i);
- createShortChart(i);
- createSingleDayChart(i);
- } else {
- console.error("addInstrument response error: " + JSON.stringify(response));
- }
- },
- failure: function (response) { console.error("addInstrument AJAX error: " + JSON.stringify(response)); }
- });
-}
-
-function showModalDialog_FullScreenChart(Symbol) {
- let i = Instruments.GetSymbol(Symbol);
-
- let modal = document.getElementById("modalChart");
-
- $("#modalChartTitle").html("Detailed Chart: " + i.InstrumentName + " (" + i.Symbol + ")");
-
- $("#modalChart .close").click(function () { modal.style.display = "none"; });
- $('#spnCompareInstruments').click(function () { compareInstruments(i) });
-
- window.onclick = function (event) { // When the user clicks anywhere outside of the modal, close it
- if (event.target == modal) {
- modal.style.display = "none";
- }
- }
-
- //$("#modalDialog .ok-button").click(function () { modal.style.display = "none"; });
-
- $(document).on('keypress', function (event) { if (event.keyCode == 27 /*Escape key*/) modal.style.display = "none"; });
-
- //Display the dialog
- modal.style.display = "block";
-
- let chartDiv = $("#divFullScreenChart");
- let height = chartDiv.parent().height();
- let width = chartDiv.parent().width();
- chartDiv.height(height).width(width);
-
- let startDate = i.DailyData[0].DT;
- let endDate = new Date().getTime();
- createFullScreenChart(i, startDate, endDate);
- //createFullScreenComparisonChart(i, Instruments.GetSymbol('BTC-USD'), startDate, endDate);
-}
-function compareInstruments(Instrument1) {
- function GetAccountsDropdown() {
- let dd = '';
- return dd;
- }
-
- let modal = document.getElementById("modalDialog");
- $("#modalTitle").html("Compare Instruments");
-
- let content = 'Select instrument to compare against ' + GetAccountsDropdown() + ' ' + - ' OK ';
- $("#modalContentDiv").html(content);
- $("#modalDialog .close").click(function () { modal.style.display = "none"; });
- window.onclick = function (event) { // When the user clicks anywhere outside of the modal, close it
- if (event.target == modal) {
- modal.style.display = "none";
- }
- }
-
- $("#selectInstrument2").bind('input', function () {
- $("#compareInstrumentsOKButton").removeClass("ok-button").addClass("ok-button-disabled").unbind();
- let instrument2Symbol = $("#selectInstrument2").val();
- if (instrument2Symbol != '' && instrument2Symbol != 'Select 2nd instrument...') {
- $("#compareInstrumentsOKButton").removeClass("ok-button-disabled").addClass("ok-button").bind('click', function () {
- $("#modalDialog").css("display", "none");
- let Instrument2 = Instruments.GetSymbol(instrument2Symbol);
- let startDate = Instrument1.DailyData[0].DT > Instrument2.DailyData[0].DT ? Instrument1.DailyData[0].DT : Instrument2.DailyData[0].DT;
- let endDate = new Date().getTime();
- createFullScreenComparisonChart(Instrument1, Instrument2, startDate, endDate);
- });
- }
- });
-
- //Display the dialog
- modal.style.display = "block";
-}
+*/
\ No newline at end of file
|
---|