diff --git a/Websites/SharePrices/.vs/SharePrices/v17/.suo b/Websites/SharePrices/.vs/SharePrices/v17/.suo
index b6ea760..029dbbf 100644
Binary files a/Websites/SharePrices/.vs/SharePrices/v17/.suo and b/Websites/SharePrices/.vs/SharePrices/v17/.suo differ
diff --git a/Websites/SharePrices/SharePrices.v12.suo b/Websites/SharePrices/SharePrices.v12.suo
index cc492c6..6d2302e 100644
Binary files a/Websites/SharePrices/SharePrices.v12.suo and b/Websites/SharePrices/SharePrices.v12.suo differ
diff --git a/Websites/SharePrices/SharePrices/CreateDBSchema.sql b/Websites/SharePrices/SharePrices/CreateDBSchema.sql
index 0c9fa2d..7ebfc46 100644
--- a/Websites/SharePrices/SharePrices/CreateDBSchema.sql
+++ b/Websites/SharePrices/SharePrices/CreateDBSchema.sql
@@ -168,6 +168,65 @@ GO
GRANT EXECUTE ON TYPE::PriceDataType TO WebApp_Role
GO
+CREATE FUNCTION dbo.fn_GetExchangeRate(@Currency VARCHAR(3), @DT DATETIME)
+RETURNS REAL
+AS
+BEGIN
+ DECLARE @Rate REAL
+ IF @Currency = 'GBp' COLLATE Latin1_General_CS_AS
+ BEGIN
+ SET @Rate = 100
+ END
+ ELSE
+ BEGIN
+ IF @Currency = 'GBP' COLLATE Latin1_General_CS_AS
+ BEGIN
+ SET @Rate = 1
+ END
+ ELSE
+ BEGIN
+ DECLARE @InstrumentID INT
+ SELECT @InstrumentID = InstrumentID FROM Instrument WHERE FullName = 'GBP/' + @Currency
+ IF @InstrumentID IS NULL
+ BEGIN
+ --Currency pair Instrument not found - return zero to indicate error
+ SET @Rate = 0
+ END
+ ELSE
+ BEGIN
+ --Is there a rate for this exact datetime?
+ SELECT @Rate = ClosePrice FROM InstrumentHistory_Intraday WHERE InstrumentID = @InstrumentID AND HistoryDT = @DT
+ IF @Rate IS NULL
+ BEGIN
+ --No exect match
+ DECLARE @StartDT DATETIME, @EndDT DATETIME, @StartRate REAL, @EndRate REAL
+ --@StartID INT, @EndID INT,
+ SELECT TOP 1 @StartDT = HistoryDT, @StartRate = ClosePrice FROM InstrumentHistory_Intraday WHERE InstrumentID = @InstrumentID AND HistoryDT < @DT ORDER BY HistoryDT DESC
+ SELECT TOP 1 @EndDT = HistoryDT, @EndRate = OpenPrice FROM InstrumentHistory_Intraday WHERE InstrumentID = @InstrumentID AND HistoryDT > @DT ORDER BY HistoryDT
+ IF @StartDT IS NOT NULL AND @EndDT IS NOT NULL
+ BEGIN
+ --Work out how far between the 2 dates our DT is, and calculate rate accordingly
+ DECLARE @TotalSeconds BIGINT
+ DECLARE @Percent REAL
+ SELECT @TotalSeconds = DATEDIFF(second, @StartDT, @EndDT)
+ SET @Percent = CONVERT(REAL, DATEDIFF(second, @StartDT, @DT)) / @TotalSeconds * 100
+ SET @Rate = @StartRate + ((@EndRate - @StartRate) / 100 * @Percent)
+ END
+ ELSE
+ BEGIN
+ --We don't have BOTH a start AND end price
+ --Return the non-NULL rate if we have one, zero otherwise
+ SET @Rate = COALESCE(@StartRate, @EndRate, 0)
+ END
+ END
+ END
+ END
+ END
+
+ RETURN @Rate
+END
+GO
+
--CREATE PROCEDURE usp_InsertInstrument (@ExchangeShortName varchar(10), @Symbol varchar(7), @FullName varchar(100))
CREATE PROCEDURE usp_InsertInstrument (@Symbol varchar(8), @FullName varchar(100))
AS
@@ -629,7 +688,8 @@ BEGIN
h.PurchaseDate,
h.NoUnits,
h.PurchasePricePerUnit,
- h.ActualExchangeRate,
+ ISNULL(h.ActualExchangeRate, dbo.fn_GetExchangeRate(i.Currency, h.PurchaseDate)) as [PurchaseExchangeRate],
+ h.NoUnits * h.PurchasePricePerUnit / ISNULL(h.ActualExchangeRate, dbo.fn_GetExchangeRate(i.Currency, h.PurchaseDate)) as [BookCostGBP],
h.SoldDate
FROM
Holding h
diff --git a/Websites/SharePrices/SharePrices/scripts/SharePrices.js b/Websites/SharePrices/SharePrices/scripts/SharePrices.js
index a1bad47..a4ab09a 100644
--- a/Websites/SharePrices/SharePrices/scripts/SharePrices.js
+++ b/Websites/SharePrices/SharePrices/scripts/SharePrices.js
@@ -1,4 +1,5 @@
-'use strict';
+// @ts-check
+'use strict';
var timespans = { oneSecond: 1000, oneMinute: 60 * 1000, oneHour: 60 * 60 * 1000, oneDay: 24 * 60 * 60 * 1000, oneWeek: 7 * 24 * 60 * 60 * 1000, oneMonth: 31 * 24 * 60 * 60 * 1000 };
var fetchIntervalDaily = 12 * timespans.oneHour;
var fetchIntervalIntraday = 4 * timespans.oneHour;
@@ -507,6 +508,7 @@ function createSharesTableRow(instrument) {
if (Instrument.Holdings.length > 0) {
let totalUnits = 0;
let totalPrice = 0;
+ let totalPriceGBP = 0;
let noRows = 0;
let t = '
Account | Buy Date | Price | Units | Total |
';
for (let i = 0; i < Instrument.Holdings.length; i++) {
@@ -515,6 +517,7 @@ function createSharesTableRow(instrument) {
noRows++;
totalUnits += h.NoUnits;
totalPrice += (h.NoUnits * h.PurchasePricePerUnit);
+ totalPriceGBP += h.BookCostGBP;
t += '×' + h.AccountName + ' | ' +
'' + new Date(h.PurchaseDate).yyyymmdd() + ' | ' +
'' + formatAmount(h.PurchasePricePerUnit, '', 2) + ' | ' +
@@ -537,12 +540,15 @@ function createSharesTableRow(instrument) {
if (totalUnits > 0) {
Instrument.TotalUnitsHeld = totalUnits;
Instrument.TotalHoldingsCost = totalPrice;
+ Instrument.TotalHoldingsCostGBP = totalPriceGBP;
+ //Instrument.BreakevenPrice = totalPrice / totalUnits;
Instrument.BreakevenPrice = totalPrice / totalUnits;
t += '
| ' +
'Total | ' +
'' + formatAmount(Instrument.BreakevenPrice, '', 2) + ' | ' +
//'' + formatAmount(Instrument.TotalUnitsHeld, '', 0) + ' | ' +
'' + Instrument.TotalUnitsHeld.autoScale() + ' | ' +
+ //'' + formatAmount(Instrument.TotalHoldingsCost, '', 0) + ' | ' +
'' + formatAmount(Instrument.TotalHoldingsCost, '', 0) + ' | ' +
'
';
}
@@ -551,6 +557,7 @@ function createSharesTableRow(instrument) {
} else {
delete Instrument.TotalUnitsHeld;
delete Instrument.TotalHoldingsCost;
+ delete Instrument.TotalHoldingsCostGBP;
delete Instrument.BreakevenPrice;
return '';
}
@@ -579,6 +586,42 @@ function createSharesTableRow(instrument) {
//' | '
};
+function getCurrentValueContent(Instrument) {
+ let currentValue = '';
+ if (Instrument.CurrentPrice) {
+ if (Instrument.TotalUnitsHeld > 0) {
+ currentValue = '';
+ //currentValue += 'Price (' + Instrument.Currency + '): ' + (Instrument.BreakevenPrice < 0.1 ? Instrument.BreakevenPrice.toPrecision(3) : Instrument.BreakevenPrice.toFixed(2)) + ' -> ' + (Instrument.CurrentPrice < 0.1 ? Instrument.CurrentPrice.toPrecision(3) : Instrument.CurrentPrice.toFixed(2)) + ': ' + getProfitOrLoss(Instrument.BreakevenPrice, Instrument.CurrentPrice) + '
';
+ currentValue += 'Price (' + Instrument.Currency + '): ' + (Instrument.BreakevenPrice < 0.1 ? Instrument.BreakevenPrice.autoScale() : Instrument.BreakevenPrice.toFixed(2)) + ' -> ' + (Instrument.CurrentPrice < 0.1 ? Instrument.CurrentPrice.autoScale() : Instrument.CurrentPrice.toFixed(2)) + ': ' + getProfitOrLoss(Instrument.BreakevenPrice, Instrument.CurrentPrice) + '
';
+ //currentValue += 'Value: ' + (Instrument.TotalHoldingsCost).toFixed(2) + ' -> ' + (Instrument.TotalUnitsHeld * Instrument.CurrentPrice).toFixed(2) + ': ' + getProfitOrLoss(Instrument.TotalHoldingsCost, Instrument.TotalUnitsHeld * Instrument.CurrentPrice);
+ currentValue += 'Book Cost (GBP): ' + (Instrument.TotalHoldingsCostGBP).toFixed(2);
+ let exRate = Instruments.GetExchangeRate(Instrument.Currency, 'GBP');
+ if (exRate) {
+ if (Instrument.Currency != 'GBp') {
+ currentValue += '
GBP/' + Instrument.Currency + ' = ' + exRate.toFixed(4);
+ }
+ currentValue += '';
+ //currentValue += 'Current value:
' + currencyFormatter.format(Instrument.TotalUnitsHeld * Instrument.CurrentPrice / exRate) + ': ' + getProfitOrLoss(Instrument.TotalHoldingsCostGBP / exRate, Instrument.TotalUnitsHeld * Instrument.CurrentPrice / exRate, 1);
+ currentValue += 'Current value:
' + currencyFormatter.format(Instrument.TotalUnitsHeld * Instrument.CurrentPrice / exRate) + ': ' + getProfitOrLoss(Instrument.TotalHoldingsCostGBP, Instrument.TotalUnitsHeld * Instrument.CurrentPrice / exRate, 1);
+ currentValue += '
';
+ } else {
+ currentValue += '
' + Instrument.Currency;
+ }
+ } else {
+ //INDEX, CURRENCY, EQUITY
+ if (Instrument.InstrumentType == 'CURRENCY') {
+ currentValue = 'Exchange Rate: ' + Instrument.CurrentPrice.toFixed(4);
+ } else {
+ if (Instrument.InstrumentType == 'CRYPTOCURRENCY' && Math.abs(Instrument.CurrentPrice) < 0.001) {
+ currentValue = 'Price (' + Instrument.Currency + '): ' + Instrument.CurrentPrice.toExponential();
+ } else {
+ currentValue = 'Price (' + Instrument.Currency + '): ' + Instrument.CurrentPrice.toFixed(2);
+ }
+ }
+ }
+ }
+ return currentValue;
+}
function moveInstrumentUp(symbol) {
let i = Instruments.indexOfSymbol(symbol);
if (i > 0) {
@@ -2341,40 +2384,6 @@ function getYahooIntradayData(Instrument) {
failure: function (response) { console.error("getYahooIntradayData error:"); console.info(response); }
});
}
-function getCurrentValueContent(Instrument) {
- let currentValue = '';
- if (Instrument.CurrentPrice) {
- if (Instrument.TotalUnitsHeld > 0) {
- currentValue = '';
- //currentValue += 'Price (' + Instrument.Currency + '): ' + (Instrument.BreakevenPrice < 0.1 ? Instrument.BreakevenPrice.toPrecision(3) : Instrument.BreakevenPrice.toFixed(2)) + ' -> ' + (Instrument.CurrentPrice < 0.1 ? Instrument.CurrentPrice.toPrecision(3) : Instrument.CurrentPrice.toFixed(2)) + ': ' + getProfitOrLoss(Instrument.BreakevenPrice, Instrument.CurrentPrice) + '
';
- currentValue += 'Price (' + Instrument.Currency + '): ' + (Instrument.BreakevenPrice < 0.1 ? Instrument.BreakevenPrice.autoScale() : Instrument.BreakevenPrice.toFixed(2)) + ' -> ' + (Instrument.CurrentPrice < 0.1 ? Instrument.CurrentPrice.autoScale() : Instrument.CurrentPrice.toFixed(2)) + ': ' + getProfitOrLoss(Instrument.BreakevenPrice, Instrument.CurrentPrice) + '
';
- currentValue += 'Value: ' + (Instrument.TotalHoldingsCost).toFixed(2) + ' -> ' + (Instrument.TotalUnitsHeld * Instrument.CurrentPrice).toFixed(2) + ': ' + getProfitOrLoss(Instrument.TotalHoldingsCost, Instrument.TotalUnitsHeld * Instrument.CurrentPrice);
- let exRate = Instruments.GetExchangeRate(Instrument.Currency, 'GBP');
- if (exRate) {
- if (Instrument.Currency != 'GBp') {
- currentValue += '
GBP/' + Instrument.Currency + ' = ' + exRate.toFixed(4);
- }
- currentValue += '';
- currentValue += 'Current value:
' + currencyFormatter.format(Instrument.TotalUnitsHeld * Instrument.CurrentPrice / exRate) + ': ' + getProfitOrLoss(Instrument.TotalHoldingsCost / exRate, Instrument.TotalUnitsHeld * Instrument.CurrentPrice / exRate, 1);
- currentValue += '
';
- } else {
- currentValue += '
' + Instrument.Currency;
- }
- } else {
- //INDEX, CURRENCY, EQUITY
- if (Instrument.InstrumentType == 'CURRENCY') {
- currentValue = 'Exchange Rate: ' + Instrument.CurrentPrice.toFixed(4);
- } else {
- if (Instrument.InstrumentType == 'CRYPTOCURRENCY' && Math.abs(Instrument.CurrentPrice) < 0.001) {
- currentValue = 'Price (' + Instrument.Currency + '): ' + Instrument.CurrentPrice.toExponential();
- } else {
- currentValue = 'Price (' + Instrument.Currency + '): ' + Instrument.CurrentPrice.toFixed(2);
- }
- }
- }
- }
- return currentValue;
-}
function getYahooSingleDayData(Instrument) {
//console.info('getYahooSingleDayData: ' + Instrument.Symbol);
Instrument.lastFetchedSingleDay = new Date().getTime();
@@ -2966,8 +2975,10 @@ function createHoldingsTable() {
if (h.SoldDate == null) {
aggUnits += h.NoUnits;
aggBookCost += h.NoUnits * h.PurchasePricePerUnit;
- aggBookCostGBP += h.NoUnits * h.PurchasePricePerUnit / exchangeRate;
- bookCostPerAccount.addCategoryAmount(h.AccountName, (h.NoUnits * h.PurchasePricePerUnit) / exchangeRate);
+ //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; }
@@ -2990,7 +3001,8 @@ function createHoldingsTable() {
aggCurrentValue = aggUnits * (i.Currency == 'GBp' ? i.CurrentPrice / 100 : i.CurrentPrice);
if (exchangeRate) {
aggCurrentValueGBP = (aggUnits * i.CurrentPrice) / exchangeRate;
- aggGainGBP = ((i.CurrentPrice - aggPurchasePricePerUnit) * aggUnits) / exchangeRate;
+ //aggGainGBP = ((i.CurrentPrice - aggPurchasePricePerUnit) * aggUnits) / exchangeRate;
+ aggGainGBP = aggCurrentValueGBP - aggBookCostGBP;
aggSingleDayGainPercent = ((i.CurrentPrice / i.SingleDayPreviousClose) - 1) * 100;
aggSingleDayProfitOrLoss = aggSingleDayGainGBP < 0 ? 'loss' : 'profit';
}
@@ -3013,7 +3025,8 @@ function createHoldingsTable() {
bookCostGBP: aggBookCostGBP,
currentValue: aggCurrentValue,
gain: (i.Currency == 'GBp' ? (i.CurrentPrice - aggPurchasePricePerUnit) / 100 : (i.CurrentPrice - aggPurchasePricePerUnit)) * aggUnits,
- gainPercent: (i.CurrentPrice - aggPurchasePricePerUnit) / aggPurchasePricePerUnit * 100,
+ //gainPercent: (i.CurrentPrice - aggPurchasePricePerUnit) / aggPurchasePricePerUnit * 100,
+ gainPercent: aggGainGBP / aggBookCostGBP * 100,
currentValueGBP: aggCurrentValueGBP,
gainGBP: aggGainGBP,
singleDayGainGBP: aggSingleDayGainGBP,
@@ -3087,10 +3100,12 @@ function createHoldingsTable() {
if (agg.noUnits > 0) {
r.push(agg);
- holdingCurrencies.addHoldingCurrency(i.Currency, agg.noUnits * agg.purchasePricePerUnit);
+ //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, 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;
@@ -3107,7 +3122,7 @@ function createHoldingsTable() {
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,
@@ -3120,7 +3135,8 @@ function createHoldingsTable() {
if (exchangeRate) {
let holdingCurrentValueGBP = (h.NoUnits * i.CurrentPrice) / exchangeRate;
- let holdingGainGBP = ((i.CurrentPrice - h.PurchasePricePerUnit) * h.NoUnits) / 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;
@@ -3145,7 +3161,8 @@ function createHoldingsTable() {
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: (i.CurrentPrice - h.PurchasePricePerUnit) / h.PurchasePricePerUnit * 100,
+ gainPercent: holdingGainGBP / h.BookCostGBP * 100,
currentValueGBP: holdingCurrentValueGBP,
gainGBP: holdingGainGBP,
singleDayGainGBP: singleDayGainGBP,
@@ -3163,7 +3180,8 @@ function createHoldingsTable() {
currentValuePerAccount.addCategoryAmount(h.AccountName, holdingCurrentValueGBP);
bookCostPerAccount.addCategoryAmount(h.AccountName, holdingBookCost / exchangeRate);
- totalBookCostGBP += holdingBookCost / exchangeRate;
+ //totalBookCostGBP += holdingBookCost / exchangeRate;
+ totalBookCostGBP += holdingBookCostGBP;
totalValueGBP += holdingCurrentValueGBP;
totalGainGBP += holdingGainGBP;
todaysGainGBP += (marketIsOpen == 0 ? 0 : singleDayGainGBP);
@@ -3401,19 +3419,21 @@ function createAccountsTables() {
noUnits: holding.NoUnits,
cost: holding.NoUnits * holding.PurchasePricePerUnit
}
- if (exchangeRate) {
- h.costGBP = h.cost / exchangeRate;
- a.totalCostGBP += h.costGBP;
- }
+ //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;
- a.totalValueGBP += h.currentValueGBP;
+ //h.gainGBP = h.gain / exchangeRate;
+ h.gainGBP = h.currentValueGBP - h.costGBP;
a.totalGainGBP += h.gainGBP;
+ a.totalValueGBP += h.currentValueGBP;
}
}
a.holdings.push(h);
@@ -3487,6 +3507,7 @@ function createAccountsTables() {
let h = a.holdings[hx];
accountAltRow = !accountAltRow;
let holdingProfitOrLoss = h.gain < 0 ? 'loss' : 'profit';
+ let holdingGBPProfitOrLoss = h.gainGBP < 0 ? 'loss' : 'profit';
accountsTables += '' +
'' + h.instrumentName + ' | ' +
'' + h.symbol + ' | ' +
@@ -3499,8 +3520,9 @@ function createAccountsTables() {
'' + (h.currency == 'GBp' ? formatAmount(h.currentValue / 100, 'GBP', 2) : formatAmount(h.currentValue, h.currency, 2)) + ' | ' +
'' + formatAmount(h.currentValueGBP, 'GBP', 2) + ' | ' +
'' + formatAmount(h.gain, h.currency, 2) + ' | ' +
- '' + formatAmount(h.gainGBP, 'GBP', 2) + ' | ' +
- '' + ((h.gain / h.cost) * 100).toFixed(1) + '% | ' +
+ '' + formatAmount(h.gainGBP, 'GBP', 2) + ' | ' +
+ //'' + ((h.gain / h.cost) * 100).toFixed(1) + '% | ' +
+ '' + ((h.gainGBP / h.costGBP) * 100).toFixed(1) + '% | ' +
'
';
}
accountsTables += '
';